phonebill/design/backend/api/product-change-service-api.yaml
2025-09-09 01:12:14 +09:00

943 lines
26 KiB
YAML

openapi: 3.0.3
info:
title: Product-Change Service API
description: |
통신요금 관리 서비스 중 상품변경 서비스 API
## 주요 기능
- 상품변경 메뉴 조회 (UFR-PROD-010)
- 상품변경 화면 데이터 조회 (UFR-PROD-020)
- 상품변경 요청 및 사전체크 (UFR-PROD-030)
- KOS 연동 상품변경 처리 (UFR-PROD-040)
## 설계 원칙
- KOS 시스템 연동 고려
- 사전체크 단계 포함
- 상태 관리 (진행중/완료/실패)
- 트랜잭션 처리 고려
- Circuit Breaker 패턴 적용
version: 1.0.0
contact:
name: Backend Development Team
email: backend@mvno.com
servers:
- url: https://api.mvno.com/v1/product-change
description: Production Server
- url: https://api-dev.mvno.com/v1/product-change
description: Development Server
tags:
- name: menu
description: 상품변경 메뉴 관련 API
- name: customer
description: 고객 정보 관련 API
- name: product
description: 상품 정보 관련 API
- name: change
description: 상품변경 처리 관련 API
- name: history
description: 상품변경 이력 관련 API
paths:
/products/menu:
get:
tags:
- menu
summary: 상품변경 메뉴 조회
description: |
상품변경 메뉴 접근 시 필요한 기본 정보를 조회합니다.
- UFR-PROD-010 구현
- 고객 회선번호 및 기본 정보 제공
- 캐시를 활용한 성능 최적화
operationId: getProductMenu
security:
- bearerAuth: []
responses:
'200':
description: 메뉴 조회 성공
content:
application/json:
schema:
$ref: '#/components/schemas/ProductMenuResponse'
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
$ref: '#/components/responses/ForbiddenError'
'500':
$ref: '#/components/responses/InternalServerError'
/products/customer/{lineNumber}:
get:
tags:
- customer
summary: 고객 정보 조회
description: |
특정 회선번호의 고객 정보와 현재 상품 정보를 조회합니다.
- UFR-PROD-020 구현
- KOS 시스템 연동
- Redis 캐시 활용 (TTL: 4시간)
operationId: getCustomerInfo
security:
- bearerAuth: []
parameters:
- name: lineNumber
in: path
required: true
description: 고객 회선번호
schema:
type: string
pattern: '^010[0-9]{8}$'
example: "01012345678"
responses:
'200':
description: 고객 정보 조회 성공
content:
application/json:
schema:
$ref: '#/components/schemas/CustomerInfoResponse'
'400':
$ref: '#/components/responses/BadRequestError'
'401':
$ref: '#/components/responses/UnauthorizedError'
'404':
description: 고객 정보를 찾을 수 없음
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
/products/available:
get:
tags:
- product
summary: 변경 가능한 상품 목록 조회
description: |
현재 판매중이고 변경 가능한 상품 목록을 조회합니다.
- UFR-PROD-020 구현
- KOS 시스템 연동
- Redis 캐시 활용 (TTL: 24시간)
operationId: getAvailableProducts
security:
- bearerAuth: []
parameters:
- name: currentProductCode
in: query
required: false
description: 현재 상품코드 (필터링용)
schema:
type: string
example: "PLAN001"
- name: operatorCode
in: query
required: false
description: 사업자 코드
schema:
type: string
example: "MVNO001"
responses:
'200':
description: 상품 목록 조회 성공
content:
application/json:
schema:
$ref: '#/components/schemas/AvailableProductsResponse'
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
/products/change/validation:
post:
tags:
- change
summary: 상품변경 사전체크
description: |
상품변경 요청 전 사전체크를 수행합니다.
- UFR-PROD-030 구현
- 판매중인 상품 확인
- 사업자 일치 확인
- 회선 사용상태 확인
operationId: validateProductChange
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProductChangeValidationRequest'
responses:
'200':
description: 사전체크 완료 (성공/실패 포함)
content:
application/json:
schema:
$ref: '#/components/schemas/ProductChangeValidationResponse'
'400':
$ref: '#/components/responses/BadRequestError'
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
/products/change:
post:
tags:
- change
summary: 상품변경 요청
description: |
실제 상품변경 처리를 요청합니다.
- UFR-PROD-040 구현
- KOS 시스템 연동
- Circuit Breaker 패턴 적용
- 비동기 이력 저장
operationId: requestProductChange
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProductChangeRequest'
responses:
'200':
description: 상품변경 처리 완료
content:
application/json:
schema:
$ref: '#/components/schemas/ProductChangeResponse'
'202':
description: 상품변경 요청 접수 (비동기 처리)
content:
application/json:
schema:
$ref: '#/components/schemas/ProductChangeAsyncResponse'
'400':
$ref: '#/components/responses/BadRequestError'
'401':
$ref: '#/components/responses/UnauthorizedError'
'409':
description: 사전체크 실패 또는 처리 불가 상태
content:
application/json:
schema:
$ref: '#/components/schemas/ProductChangeFailureResponse'
'503':
description: KOS 시스템 장애 (Circuit Breaker Open)
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
/products/change/{requestId}:
get:
tags:
- change
summary: 상품변경 결과 조회
description: |
특정 요청ID의 상품변경 처리 결과를 조회합니다.
- 비동기 처리 결과 조회
- 상태별 상세 정보 제공
operationId: getProductChangeResult
security:
- bearerAuth: []
parameters:
- name: requestId
in: path
required: true
description: 상품변경 요청 ID
schema:
type: string
format: uuid
example: "123e4567-e89b-12d3-a456-426614174000"
responses:
'200':
description: 처리 결과 조회 성공
content:
application/json:
schema:
$ref: '#/components/schemas/ProductChangeResultResponse'
'400':
$ref: '#/components/responses/BadRequestError'
'401':
$ref: '#/components/responses/UnauthorizedError'
'404':
description: 요청 정보를 찾을 수 없음
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
/products/history:
get:
tags:
- history
summary: 상품변경 이력 조회
description: |
고객의 상품변경 이력을 조회합니다.
- UFR-PROD-040 구현 (이력 관리)
- 페이징 지원
- 기간별 필터링 지원
operationId: getProductChangeHistory
security:
- bearerAuth: []
parameters:
- name: lineNumber
in: query
required: false
description: 회선번호 (미입력시 로그인 고객 기준)
schema:
type: string
pattern: '^010[0-9]{8}$'
- name: startDate
in: query
required: false
description: 조회 시작일 (YYYY-MM-DD)
schema:
type: string
format: date
example: "2024-01-01"
- name: endDate
in: query
required: false
description: 조회 종료일 (YYYY-MM-DD)
schema:
type: string
format: date
example: "2024-12-31"
- name: page
in: query
required: false
description: 페이지 번호 (1부터 시작)
schema:
type: integer
minimum: 1
default: 1
- name: size
in: query
required: false
description: 페이지 크기
schema:
type: integer
minimum: 1
maximum: 100
default: 10
responses:
'200':
description: 이력 조회 성공
content:
application/json:
schema:
$ref: '#/components/schemas/ProductChangeHistoryResponse'
'400':
$ref: '#/components/responses/BadRequestError'
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT 토큰을 Authorization 헤더에 포함
schemas:
ProductMenuResponse:
type: object
required:
- success
- data
properties:
success:
type: boolean
example: true
data:
type: object
required:
- customerId
- lineNumber
- menuItems
properties:
customerId:
type: string
description: 고객 ID
example: "CUST001"
lineNumber:
type: string
description: 고객 회선번호
example: "01012345678"
currentProduct:
$ref: '#/components/schemas/ProductInfo'
menuItems:
type: array
description: 메뉴 항목들
items:
type: object
properties:
menuId:
type: string
example: "MENU001"
menuName:
type: string
example: "상품변경"
available:
type: boolean
example: true
CustomerInfoResponse:
type: object
required:
- success
- data
properties:
success:
type: boolean
example: true
data:
$ref: '#/components/schemas/CustomerInfo'
CustomerInfo:
type: object
required:
- customerId
- lineNumber
- customerName
- currentProduct
- lineStatus
properties:
customerId:
type: string
description: 고객 ID
example: "CUST001"
lineNumber:
type: string
description: 회선번호
example: "01012345678"
customerName:
type: string
description: 고객명
example: "홍길동"
currentProduct:
$ref: '#/components/schemas/ProductInfo'
lineStatus:
type: string
description: 회선 상태
enum: [ACTIVE, SUSPENDED, TERMINATED]
example: "ACTIVE"
contractInfo:
type: object
properties:
contractDate:
type: string
format: date
description: 계약일
termEndDate:
type: string
format: date
description: 약정 만료일
earlyTerminationFee:
type: number
description: 예상 해지비용
example: 150000
AvailableProductsResponse:
type: object
required:
- success
- data
properties:
success:
type: boolean
example: true
data:
type: object
required:
- products
properties:
products:
type: array
items:
$ref: '#/components/schemas/ProductInfo'
totalCount:
type: integer
description: 전체 상품 수
example: 15
ProductInfo:
type: object
required:
- productCode
- productName
- monthlyFee
- isAvailable
properties:
productCode:
type: string
description: 상품 코드
example: "PLAN001"
productName:
type: string
description: 상품명
example: "5G 프리미엄 플랜"
monthlyFee:
type: number
description: 월 요금
example: 55000
dataAllowance:
type: string
description: 데이터 제공량
example: "100GB"
voiceAllowance:
type: string
description: 음성 제공량
example: "무제한"
smsAllowance:
type: string
description: SMS 제공량
example: "기본 무료"
isAvailable:
type: boolean
description: 변경 가능 여부
example: true
operatorCode:
type: string
description: 사업자 코드
example: "MVNO001"
ProductChangeValidationRequest:
type: object
required:
- lineNumber
- currentProductCode
- targetProductCode
properties:
lineNumber:
type: string
description: 회선번호
pattern: '^010[0-9]{8}$'
example: "01012345678"
currentProductCode:
type: string
description: 현재 상품 코드
example: "PLAN001"
targetProductCode:
type: string
description: 변경 대상 상품 코드
example: "PLAN002"
ProductChangeValidationResponse:
type: object
required:
- success
- data
properties:
success:
type: boolean
example: true
data:
type: object
required:
- validationResult
properties:
validationResult:
type: string
enum: [SUCCESS, FAILURE]
example: "SUCCESS"
validationDetails:
type: array
items:
type: object
properties:
checkType:
type: string
enum: [PRODUCT_AVAILABLE, OPERATOR_MATCH, LINE_STATUS]
example: "PRODUCT_AVAILABLE"
result:
type: string
enum: [PASS, FAIL]
example: "PASS"
message:
type: string
example: "현재 판매중인 상품입니다"
failureReason:
type: string
description: 실패 사유 (실패 시에만)
example: "회선이 정지 상태입니다"
ProductChangeRequest:
type: object
required:
- lineNumber
- currentProductCode
- targetProductCode
properties:
lineNumber:
type: string
description: 회선번호
pattern: '^010[0-9]{8}$'
example: "01012345678"
currentProductCode:
type: string
description: 현재 상품 코드
example: "PLAN001"
targetProductCode:
type: string
description: 변경 대상 상품 코드
example: "PLAN002"
requestDate:
type: string
format: date-time
description: 요청 일시
example: "2024-03-15T10:30:00Z"
changeEffectiveDate:
type: string
format: date
description: 변경 적용일 (선택)
example: "2024-03-16"
ProductChangeResponse:
type: object
required:
- success
- data
properties:
success:
type: boolean
example: true
data:
type: object
required:
- requestId
- processStatus
- resultCode
properties:
requestId:
type: string
format: uuid
description: 요청 ID
example: "123e4567-e89b-12d3-a456-426614174000"
processStatus:
type: string
enum: [COMPLETED, FAILED]
example: "COMPLETED"
resultCode:
type: string
description: 처리 결과 코드
example: "SUCCESS"
resultMessage:
type: string
description: 처리 결과 메시지
example: "상품 변경이 완료되었습니다"
changedProduct:
$ref: '#/components/schemas/ProductInfo'
processedAt:
type: string
format: date-time
description: 처리 완료 시간
example: "2024-03-15T10:35:00Z"
ProductChangeAsyncResponse:
type: object
required:
- success
- data
properties:
success:
type: boolean
example: true
data:
type: object
required:
- requestId
- processStatus
properties:
requestId:
type: string
format: uuid
description: 요청 ID
example: "123e4567-e89b-12d3-a456-426614174000"
processStatus:
type: string
enum: [PENDING, PROCESSING]
example: "PROCESSING"
estimatedCompletionTime:
type: string
format: date-time
description: 예상 완료 시간
example: "2024-03-15T10:35:00Z"
message:
type: string
example: "상품 변경이 진행되었습니다"
ProductChangeFailureResponse:
type: object
required:
- success
- error
properties:
success:
type: boolean
example: false
error:
type: object
required:
- code
- message
properties:
code:
type: string
enum: [VALIDATION_FAILED, CHANGE_DENIED, LINE_SUSPENDED]
example: "VALIDATION_FAILED"
message:
type: string
example: "상품 사전 체크에 실패하였습니다"
details:
type: string
description: 상세 실패 사유
example: "회선이 정지 상태입니다"
ProductChangeResultResponse:
type: object
required:
- success
- data
properties:
success:
type: boolean
example: true
data:
type: object
required:
- requestId
- processStatus
properties:
requestId:
type: string
format: uuid
example: "123e4567-e89b-12d3-a456-426614174000"
lineNumber:
type: string
example: "01012345678"
processStatus:
type: string
enum: [PENDING, PROCESSING, COMPLETED, FAILED]
example: "COMPLETED"
currentProductCode:
type: string
example: "PLAN001"
targetProductCode:
type: string
example: "PLAN002"
requestedAt:
type: string
format: date-time
example: "2024-03-15T10:30:00Z"
processedAt:
type: string
format: date-time
example: "2024-03-15T10:35:00Z"
resultCode:
type: string
example: "SUCCESS"
resultMessage:
type: string
example: "상품 변경이 완료되었습니다"
failureReason:
type: string
description: 실패 사유 (실패 시에만)
ProductChangeHistoryResponse:
type: object
required:
- success
- data
properties:
success:
type: boolean
example: true
data:
type: object
required:
- history
- pagination
properties:
history:
type: array
items:
$ref: '#/components/schemas/ProductChangeHistoryItem'
pagination:
$ref: '#/components/schemas/PaginationInfo'
ProductChangeHistoryItem:
type: object
required:
- requestId
- lineNumber
- processStatus
- requestedAt
properties:
requestId:
type: string
format: uuid
example: "123e4567-e89b-12d3-a456-426614174000"
lineNumber:
type: string
example: "01012345678"
processStatus:
type: string
enum: [PENDING, PROCESSING, COMPLETED, FAILED]
example: "COMPLETED"
currentProductCode:
type: string
example: "PLAN001"
currentProductName:
type: string
example: "5G 베이직 플랜"
targetProductCode:
type: string
example: "PLAN002"
targetProductName:
type: string
example: "5G 프리미엄 플랜"
requestedAt:
type: string
format: date-time
example: "2024-03-15T10:30:00Z"
processedAt:
type: string
format: date-time
example: "2024-03-15T10:35:00Z"
resultMessage:
type: string
example: "상품 변경이 완료되었습니다"
PaginationInfo:
type: object
required:
- page
- size
- totalElements
- totalPages
properties:
page:
type: integer
description: 현재 페이지 번호
example: 1
size:
type: integer
description: 페이지 크기
example: 10
totalElements:
type: integer
description: 전체 요소 수
example: 45
totalPages:
type: integer
description: 전체 페이지 수
example: 5
hasNext:
type: boolean
description: 다음 페이지 존재 여부
example: true
hasPrevious:
type: boolean
description: 이전 페이지 존재 여부
example: false
ErrorResponse:
type: object
required:
- success
- error
properties:
success:
type: boolean
example: false
error:
type: object
required:
- code
- message
properties:
code:
type: string
example: "INVALID_REQUEST"
message:
type: string
example: "요청이 올바르지 않습니다"
details:
type: string
description: 상세 오류 정보
timestamp:
type: string
format: date-time
example: "2024-03-15T10:30:00Z"
path:
type: string
description: 요청 경로
example: "/products/change"
responses:
BadRequestError:
description: 잘못된 요청
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "INVALID_REQUEST"
message: "요청 파라미터가 올바르지 않습니다"
timestamp: "2024-03-15T10:30:00Z"
UnauthorizedError:
description: 인증 실패
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "UNAUTHORIZED"
message: "인증이 필요합니다"
timestamp: "2024-03-15T10:30:00Z"
ForbiddenError:
description: 권한 없음
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "FORBIDDEN"
message: "서비스 이용 권한이 없습니다"
timestamp: "2024-03-15T10:30:00Z"
InternalServerError:
description: 서버 내부 오류
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
error:
code: "INTERNAL_SERVER_ERROR"
message: "서버 내부 오류가 발생했습니다"
timestamp: "2024-03-15T10:30:00Z"