@startuml 고객참여플로우 !theme mono title 고객 참여 플로우 - 외부 시퀀스 다이어그램 actor "고객" as Customer participant "Frontend\n(고객용)" as CustomerFE participant "API Gateway" as Gateway participant "Participation\nService" as PartService participant "Kafka\n(Event Topics)" as Kafka database "Participation\nDB" as PartDB participant "Analytics\nService" as Analytics actor "사장님" as Owner participant "Frontend\n(사장님용)" as OwnerFE == UFR-PART-010: 이벤트 참여 == Customer -> CustomerFE: 이벤트 참여 화면 접근\n(우리동네TV/SNS/링고비즈) activate CustomerFE CustomerFE -> Customer: 참여 정보 입력 폼 표시\n(이름, 전화번호, 참여경로) Customer -> CustomerFE: 참여 정보 입력 및\n참여 버튼 클릭 CustomerFE -> CustomerFE: 클라이언트 유효성 검증\n(이름 2자 이상, 전화번호 형식) CustomerFE -> Gateway: POST /api/v1/participations\n{이름, 전화번호, 참여경로, 개인정보동의} activate Gateway Gateway -> PartService: POST /participations/register\n{이름, 전화번호, 참여경로, 개인정보동의} activate PartService PartService -> PartDB: SELECT * FROM participants\nWHERE phone_number = ? AND event_id = ? activate PartDB PartDB --> PartService: 중복 참여 여부 반환 deactivate PartDB alt 중복 참여인 경우 PartService --> Gateway: 409 Conflict\n{message: "이미 참여하신 이벤트입니다"} Gateway --> CustomerFE: 409 Conflict CustomerFE -> Customer: 중복 참여 오류 메시지 표시 deactivate PartService deactivate Gateway deactivate CustomerFE else 신규 참여인 경우 PartService -> PartService: 응모 번호 생성\n(UUID 또는 시퀀스 기반) PartService -> PartDB: INSERT INTO participants\n(name, phone_number, entry_path,\napplication_number, participated_at) activate PartDB PartDB --> PartService: 저장 완료 deactivate PartDB PartService -> Kafka: Publish Event\n"ParticipantRegistered"\n{participantId, eventId,\nentryPath, timestamp} activate Kafka note right of Kafka Topic: participant-events Event: ParticipantRegistered Data: { participantId: UUID, eventId: UUID, entryPath: string, timestamp: datetime } end note Kafka --> Analytics: Subscribe Event\n"ParticipantRegistered" activate Analytics Analytics -> Analytics: 참여자 데이터 집계\n- 채널별 참여자 수\n- 시간대별 참여 추이\n- 실시간 통계 업데이트 deactivate Analytics deactivate Kafka PartService --> Gateway: 201 Created\n{응모번호, 당첨발표일, 참여완료메시지} deactivate PartService Gateway --> CustomerFE: 201 Created deactivate Gateway CustomerFE -> Customer: 참여 완료 화면 표시\n- 응모번호\n- 당첨 발표일\n- "참여해주셔서 감사합니다" deactivate CustomerFE end == UFR-PART-030: 당첨자 추첨 == Owner -> OwnerFE: 이벤트 상세 화면에서\n"당첨자 추첨" 버튼 클릭 activate OwnerFE OwnerFE -> Owner: 추첨 확인 다이얼로그 표시\n"당첨자를 추첨하시겠습니까?" Owner -> OwnerFE: 확인 버튼 클릭 OwnerFE -> Gateway: POST /api/v1/events/{eventId}/draw-winners\n{당첨인원, 매장방문가산점옵션} activate Gateway Gateway -> PartService: POST /events/{eventId}/draw-winners\n{winnerCount, visitBonus} activate PartService PartService -> PartDB: SELECT * FROM participants\nWHERE event_id = ? AND is_winner = false activate PartDB PartDB --> PartService: 전체 참여자 목록 반환 deactivate PartDB PartService -> PartService: 당첨자 추첨 알고리즘 실행\n1. 난수 생성 (Crypto.randomBytes)\n2. 매장방문 가산점 적용 (옵션)\n3. Fisher-Yates Shuffle\n4. 당첨인원만큼 선정 PartService -> PartDB: UPDATE participants\nSET is_winner = true, won_at = NOW()\nWHERE participant_id IN (당첨자IDs) activate PartDB PartDB --> PartService: 업데이트 완료 deactivate PartDB PartService -> PartDB: INSERT INTO draw_logs\n(event_id, draw_method, winner_count,\nalgorithm, drawn_at) activate PartDB note right of PartDB 추첨 로그 저장: - 추첨 일시 - 추첨 방법 - 알고리즘 버전 - 가산점 적용 여부 end note PartDB --> PartService: 로그 저장 완료 deactivate PartDB PartService --> Gateway: 200 OK\n{당첨자목록, 추첨로그ID} deactivate PartService Gateway --> OwnerFE: 200 OK deactivate Gateway OwnerFE -> Owner: 당첨자 목록 화면 표시\n- 당첨자 정보 (이름, 전화번호, 응모번호)\n- "재추첨" 버튼\n- 추첨 완료 메시지 deactivate OwnerFE @enduml