diff --git a/.gradle/8.10/executionHistory/executionHistory.bin b/.gradle/8.10/executionHistory/executionHistory.bin index 3778430..e2f2a1a 100644 Binary files a/.gradle/8.10/executionHistory/executionHistory.bin and b/.gradle/8.10/executionHistory/executionHistory.bin differ diff --git a/.gradle/8.10/executionHistory/executionHistory.lock b/.gradle/8.10/executionHistory/executionHistory.lock index 778ae70..5d5f0de 100644 Binary files a/.gradle/8.10/executionHistory/executionHistory.lock and b/.gradle/8.10/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.10/fileHashes/fileHashes.bin b/.gradle/8.10/fileHashes/fileHashes.bin index 176d863..6103adf 100644 Binary files a/.gradle/8.10/fileHashes/fileHashes.bin and b/.gradle/8.10/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.10/fileHashes/fileHashes.lock b/.gradle/8.10/fileHashes/fileHashes.lock index 4577896..58f7b76 100644 Binary files a/.gradle/8.10/fileHashes/fileHashes.lock and b/.gradle/8.10/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.10/fileHashes/resourceHashesCache.bin b/.gradle/8.10/fileHashes/resourceHashesCache.bin index 4320628..639a07b 100644 Binary files a/.gradle/8.10/fileHashes/resourceHashesCache.bin and b/.gradle/8.10/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 06bcff5..cf9282e 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index 2037b03..7e81e5a 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/distribution-service/src/main/java/com/kt/distribution/config/OpenApiConfig.java b/distribution-service/src/main/java/com/kt/distribution/config/OpenApiConfig.java new file mode 100644 index 0000000..60c28ba --- /dev/null +++ b/distribution-service/src/main/java/com/kt/distribution/config/OpenApiConfig.java @@ -0,0 +1,52 @@ +package com.kt.distribution.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.servers.Server; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * OpenAPI (Swagger) Configuration + * Swagger UI 설정 및 API 문서화 + * + * @author System Architect + * @since 2025-10-24 + */ +@Configuration +public class OpenApiConfig { + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(new Info() + .title("Distribution Service API") + .description(""" + KT AI 기반 소상공인 이벤트 자동 생성 서비스의 다중 채널 배포 관리 API + + ## 주요 기능 + - 다중 채널 동시 배포 (우리동네TV, 링고비즈, 지니TV, SNS) + - 배포 상태 실시간 모니터링 + - Circuit Breaker 기반 장애 격리 + - Retry 패턴 및 Fallback 처리 + """) + .version("1.0.0") + .contact(new Contact() + .name("Digital Garage Team") + .email("support@kt-event-marketing.com"))) + .servers(List.of( + new Server() + .url("http://localhost:8085") + .description("Local Development Server"), + new Server() + .url("https://dev-api.kt-event-marketing.com/distribution/v1") + .description("Development Server"), + new Server() + .url("https://api.kt-event-marketing.com/distribution/v1") + .description("Production Server") + )); + } +} diff --git a/distribution-service/src/main/java/com/kt/distribution/controller/DistributionController.java b/distribution-service/src/main/java/com/kt/distribution/controller/DistributionController.java index d0825fb..aa0ed3e 100644 --- a/distribution-service/src/main/java/com/kt/distribution/controller/DistributionController.java +++ b/distribution-service/src/main/java/com/kt/distribution/controller/DistributionController.java @@ -4,6 +4,13 @@ import com.kt.distribution.dto.DistributionRequest; import com.kt.distribution.dto.DistributionResponse; import com.kt.distribution.dto.DistributionStatusResponse; import com.kt.distribution.service.DistributionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; @@ -11,8 +18,8 @@ import org.springframework.web.bind.annotation.*; /** * Distribution Controller - * POST /api/distribution/distribute - 다중 채널 배포 실행 - * GET /api/distribution/{eventId}/status - 배포 상태 조회 + * POST /distribution/distribute - 다중 채널 배포 실행 + * GET /distribution/{eventId}/status - 배포 상태 조회 * * @author System Architect * @since 2025-10-23 @@ -21,6 +28,7 @@ import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/distribution") @RequiredArgsConstructor +@Tag(name = "Distribution", description = "다중 채널 배포 관리 API") public class DistributionController { private final DistributionService distributionService; @@ -32,6 +40,29 @@ public class DistributionController { * @param request DistributionRequest * @return DistributionResponse */ + @Operation( + summary = "다중 채널 배포 요청", + description = """ + 이벤트 콘텐츠를 선택된 채널들에 동시 배포합니다. + + ## 처리 흐름 + 1. 배포 요청 검증 (이벤트 ID, 채널 목록, 콘텐츠 데이터) + 2. 채널별 병렬 배포 실행 (1분 이내 완료 목표) + 3. Circuit Breaker로 장애 채널 격리 + 4. 실패 시 Retry (지수 백오프: 1s, 2s, 4s) + 5. Fallback: 실패 채널 스킵 및 알림 + """ + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "배포 완료", + content = @Content(schema = @Schema(implementation = DistributionResponse.class)) + ), + @ApiResponse(responseCode = "400", description = "잘못된 요청"), + @ApiResponse(responseCode = "404", description = "이벤트를 찾을 수 없음"), + @ApiResponse(responseCode = "500", description = "서버 내부 오류") + }) @PostMapping("/distribute") public ResponseEntity distribute(@RequestBody DistributionRequest request) { log.info("Received distribution request: eventId={}, channels={}", @@ -53,8 +84,30 @@ public class DistributionController { * @param eventId 이벤트 ID * @return DistributionStatusResponse */ + @Operation( + summary = "배포 상태 조회", + description = """ + 특정 이벤트의 배포 상태를 실시간으로 조회합니다. + + ## 조회 정보 + - 전체 배포 상태 (진행중, 완료, 부분성공, 실패) + - 채널별 배포 상태 및 결과 + - 실패 채널 상세 정보 (오류 유형, 재시도 횟수) + """ + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "배포 상태 조회 성공", + content = @Content(schema = @Schema(implementation = DistributionStatusResponse.class)) + ), + @ApiResponse(responseCode = "404", description = "배포 이력을 찾을 수 없음"), + @ApiResponse(responseCode = "500", description = "서버 내부 오류") + }) @GetMapping("/{eventId}/status") - public ResponseEntity getDistributionStatus(@PathVariable String eventId) { + public ResponseEntity getDistributionStatus( + @Parameter(description = "이벤트 ID", required = true, example = "evt-12345") + @PathVariable String eventId) { log.info("Received distribution status request: eventId={}", eventId); DistributionStatusResponse response = distributionService.getDistributionStatus(eventId); diff --git a/distribution-service/src/main/resources/application.yml b/distribution-service/src/main/resources/application.yml index 40fe36a..d6c5e99 100644 --- a/distribution-service/src/main/resources/application.yml +++ b/distribution-service/src/main/resources/application.yml @@ -119,6 +119,19 @@ channel: url: ${KAKAO_API_URL:http://localhost:9006/api/kakao} timeout: 10000 +# Springdoc OpenAPI (Swagger) +springdoc: + api-docs: + path: /v3/api-docs + enabled: true + swagger-ui: + path: /swagger-ui.html + enabled: true + operations-sorter: alpha + tags-sorter: alpha + display-request-duration: true + show-actuator: true + # Logging logging: file: @@ -132,3 +145,4 @@ logging: com.kt.distribution: DEBUG org.springframework.kafka: INFO io.github.resilience4j: DEBUG + org.springframework.web: DEBUG