이벤트의 참여자 목록을 조회합니다.
- *
- * getParticipants(
- @PathVariable("eventId") String eventId,
- @RequestParam(value = "page", defaultValue = "0") int page,
- @RequestParam(value = "size", defaultValue = "20") int size,
- @RequestParam(value = "storeVisited", required = false) Boolean storeVisited,
- @RequestParam(value = "isWinner", required = false) Boolean isWinner) {
-
- log.info("GET /events/{}/participants - page: {}, size: {}, storeVisited: {}, isWinner: {}",
- eventId, page, size, storeVisited, isWinner);
-
- // 페이지 크기 제한 (최대 100)
- int validatedSize = Math.min(size, 100);
-
- Pageable pageable = PageRequest.of(page, validatedSize);
-
- // 참여 경로는 API 스펙에 없으므로 null로 전달
- ParticipantListResponse response = participationService.getParticipantList(
- eventId, null, isWinner, pageable);
-
- log.info("Participant list fetched successfully - eventId: {}, totalElements: {}",
- eventId, response.getTotalElements());
-
- return ResponseEntity.ok(response);
- }
-
- /**
- * 참여자 검색
- *
- * 이벤트의 참여자를 이름 또는 전화번호로 검색합니다.
- *
- * 기능:
- *
- * - 이름 또는 전화번호로 검색 (부분 일치)
- * - 페이징 지원 (기본: page=0, size=20)
- * - 전화번호 마스킹 처리 (개인정보 보호)
- *
- *
- * Response:
- *
- * - 200 OK: 검색 성공
- * - 404 Not Found: 이벤트를 찾을 수 없음
- *
- *
- * @param eventId 이벤트 ID (Path Variable)
- * @param keyword 검색 키워드 (이름 또는 전화번호)
- * @param page 페이지 번호 (0부터 시작, 기본값: 0)
- * @param size 페이지 크기 (기본값: 20, 최대: 100)
- * @return 검색된 참여자 목록 (페이징 정보 포함)
- */
- @GetMapping("/participants/search")
- public ResponseEntity searchParticipants(
- @PathVariable("eventId") String eventId,
- @RequestParam("keyword") String keyword,
- @RequestParam(value = "page", defaultValue = "0") int page,
- @RequestParam(value = "size", defaultValue = "20") int size) {
-
- log.info("GET /events/{}/participants/search - keyword: {}, page: {}, size: {}",
- eventId, keyword, page, size);
-
- // 페이지 크기 제한 (최대 100)
- int validatedSize = Math.min(size, 100);
-
- Pageable pageable = PageRequest.of(page, validatedSize);
-
- ParticipantListResponse response = participationService.searchParticipants(
- eventId, keyword, pageable);
-
- log.info("Participants searched successfully - eventId: {}, keyword: {}, totalElements: {}",
- eventId, keyword, response.getTotalElements());
-
- return ResponseEntity.ok(response);
- }
-}
diff --git a/participation-service/src/main/java/com/kt/event/participation/presentation/controller/WinnerController.java b/participation-service/src/main/java/com/kt/event/participation/presentation/controller/WinnerController.java
deleted file mode 100644
index 6de791c..0000000
--- a/participation-service/src/main/java/com/kt/event/participation/presentation/controller/WinnerController.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package com.kt.event.participation.presentation.controller;
-
-import com.kt.event.participation.application.dto.WinnerDrawRequest;
-import com.kt.event.participation.application.dto.WinnerDrawResponse;
-import com.kt.event.participation.application.dto.WinnerDto;
-import com.kt.event.participation.application.service.WinnerDrawService;
-import jakarta.validation.Valid;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 당첨자 추첨 및 관리 컨트롤러
- * - 당첨자 추첨 실행
- * - 당첨자 목록 조회
- *
- * RESTful API 설계 원칙:
- * - Base Path: /events/{eventId}
- * - HTTP Method 사용: POST (추첨), GET (조회)
- * - HTTP Status Code: 200 (성공), 400 (잘못된 요청), 404 (찾을 수 없음), 409 (중복 추첨)
- * - Request Validation: @Valid 사용하여 요청 검증
- * - Error Handling: GlobalExceptionHandler에서 처리
- *
- * @author Digital Garage Team
- * @since 2025-10-23
- */
-@Slf4j
-@RestController
-@RequestMapping("/events/{eventId}")
-@RequiredArgsConstructor
-public class WinnerController {
-
- private final WinnerDrawService winnerDrawService;
-
- /**
- * 당첨자 추첨
- *
- * 이벤트 당첨자를 추첨합니다.
- *
- * 비즈니스 로직:
- *
- * - 중복 추첨 검증 (이벤트별 1회만 가능)
- * - 참여자 수 검증 (당첨자 수보다 많아야 함)
- * - Fisher-Yates Shuffle 알고리즘 사용
- * - 매장 방문 보너스 가중치 적용 (선택)
- * - 추첨 로그 저장 (감사 추적)
- *
- *
- * Response:
- *
- * - 200 OK: 추첨 성공
- * - 400 Bad Request: 유효하지 않은 요청 (당첨자 수가 참여자 수보다 많음)
- * - 404 Not Found: 이벤트를 찾을 수 없음
- * - 409 Conflict: 이미 추첨 완료
- *
- *
- * @param eventId 이벤트 ID (Path Variable)
- * @param request 추첨 요청 정보 (당첨자 수, 매장 방문 보너스 적용 여부)
- * @return 추첨 결과 (당첨자 목록, 추첨 일시, 추첨 로그 ID 등)
- */
- @PostMapping("/draw-winners")
- public ResponseEntity drawWinners(
- @PathVariable("eventId") String eventId,
- @Valid @RequestBody WinnerDrawRequest request) {
-
- log.info("POST /events/{}/draw-winners - winnerCount: {}, visitBonusApplied: {}",
- eventId, request.getWinnerCount(), request.getVisitBonusApplied());
-
- WinnerDrawResponse response = winnerDrawService.drawWinners(eventId, request);
-
- log.info("Winners drawn successfully - eventId: {}, drawLogId: {}, winnerCount: {}",
- eventId, response.getDrawLogId(), response.getWinnerCount());
-
- return ResponseEntity.ok(response);
- }
-
- /**
- * 당첨자 목록 조회
- *
- * 이벤트의 당첨자 목록을 조회합니다.
- *
- * 기능:
- *
- * - 당첨 순위별 정렬 (당첨 일시 내림차순)
- * - 전화번호 마스킹 처리 (개인정보 보호)
- * - 응모 번호 포함
- *
- *
- * Response:
- *
- * - 200 OK: 조회 성공
- * - 404 Not Found: 이벤트를 찾을 수 없음 또는 당첨자가 없음
- *
- *
- * @param eventId 이벤트 ID (Path Variable)
- * @return 당첨자 목록 (당첨 순위, 이름, 마스킹된 전화번호, 당첨 일시 등)
- */
- @GetMapping("/winners")
- public ResponseEntity> getWinners(
- @PathVariable("eventId") String eventId) {
-
- log.info("GET /events/{}/winners", eventId);
-
- List winners = winnerDrawService.getWinners(eventId);
-
- log.info("Winners fetched successfully - eventId: {}, count: {}",
- eventId, winners.size());
-
- return ResponseEntity.ok(winners);
- }
-}
diff --git a/participation-service/src/main/resources/application.yml b/participation-service/src/main/resources/application.yml
deleted file mode 100644
index 7fc673d..0000000
--- a/participation-service/src/main/resources/application.yml
+++ /dev/null
@@ -1,88 +0,0 @@
-spring:
- application:
- name: participation-service
-
- datasource:
- url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:participation_db}?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
- username: ${DB_USER:root}
- password: ${DB_PASSWORD:password}
- driver-class-name: com.mysql.cj.jdbc.Driver
- hikari:
- maximum-pool-size: ${DB_POOL_SIZE:10}
- minimum-idle: ${DB_MIN_IDLE:5}
- connection-timeout: ${DB_CONN_TIMEOUT:30000}
- idle-timeout: ${DB_IDLE_TIMEOUT:600000}
- max-lifetime: ${DB_MAX_LIFETIME:1800000}
-
- jpa:
- hibernate:
- ddl-auto: ${JPA_DDL_AUTO:validate}
- show-sql: ${JPA_SHOW_SQL:false}
- properties:
- hibernate:
- format_sql: true
- use_sql_comments: true
- dialect: org.hibernate.dialect.MySQL8Dialect
-
- # Redis Configuration
- data:
- redis:
- host: ${REDIS_HOST:localhost}
- port: ${REDIS_PORT:6379}
- password: ${REDIS_PASSWORD:}
- timeout: ${REDIS_TIMEOUT:3000}
- lettuce:
- pool:
- max-active: ${REDIS_POOL_MAX_ACTIVE:8}
- max-idle: ${REDIS_POOL_MAX_IDLE:8}
- min-idle: ${REDIS_POOL_MIN_IDLE:2}
- max-wait: ${REDIS_POOL_MAX_WAIT:3000}
-
- # Kafka Configuration
- kafka:
- bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:localhost:9092}
- producer:
- key-serializer: org.apache.kafka.common.serialization.StringSerializer
- value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
- acks: ${KAFKA_PRODUCER_ACKS:all}
- retries: ${KAFKA_PRODUCER_RETRIES:3}
- properties:
- max.in.flight.requests.per.connection: 1
- enable.idempotence: true
- # Topic Names
- topics:
- participant-registered: participant-events
-
-server:
- port: ${SERVER_PORT:8084}
- servlet:
- context-path: /
- error:
- include-message: always
- include-binding-errors: always
-
-# Logging
-logging:
- level:
- root: ${LOG_LEVEL_ROOT:INFO}
- com.kt.event.participation: ${LOG_LEVEL_APP:DEBUG}
- org.springframework.data.redis: ${LOG_LEVEL_REDIS:INFO}
- org.springframework.kafka: ${LOG_LEVEL_KAFKA:INFO}
- pattern:
- console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
- file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
- file:
- name: ${LOG_FILE_PATH:./logs}/participation-service.log
- max-size: ${LOG_FILE_MAX_SIZE:10MB}
- max-history: ${LOG_FILE_MAX_HISTORY:30}
-
-# Application-specific Configuration
-app:
- cache:
- duplicate-check-ttl: ${CACHE_DUPLICATE_TTL:604800} # 7 days in seconds
- participant-list-ttl: ${CACHE_PARTICIPANT_TTL:600} # 10 minutes in seconds
- lottery:
- algorithm: FISHER_YATES_SHUFFLE
- visit-bonus-weight: ${LOTTERY_VISIT_BONUS:2.0} # 매장 방문 고객 가중치
- security:
- phone-mask-pattern: "***-****-***" # 전화번호 마스킹 패턴