diff --git a/design/backend/sequence/outer/고객참여플로우.puml b/design/backend/sequence/outer/고객참여플로우.puml index 3b5bc89..a942530 100644 --- a/design/backend/sequence/outer/고객참여플로우.puml +++ b/design/backend/sequence/outer/고객참여플로우.puml @@ -79,6 +79,40 @@ else 신규 참여인 경우 deactivate CustomerFE end +== UFR-PART-020: 참여자 목록 조회 == + +Owner -> OwnerFE: 이벤트 상세 화면에서\n"참여자 목록" 탭 클릭 +activate OwnerFE + +OwnerFE -> Gateway: GET /api/v1/events/{eventId}/participants\n?page=1&size=20 +activate Gateway + +Gateway -> PartService: GET /events/{eventId}/participants\n?page=1&size=20 +activate PartService + +PartService -> PartDB: 참여자 목록 조회\n(이벤트ID, 페이지네이션)\nORDER BY 참여일시 DESC +activate PartDB +PartDB --> PartService: 참여자 목록 반환\n(이름, 전화번호, 참여경로,\n응모번호, 참여일시)\n+ 총 참여자 수 +deactivate PartDB + +PartService --> Gateway: 200 OK\n{participants[], totalCount, page, size} +deactivate PartService + +Gateway --> OwnerFE: 200 OK +deactivate Gateway + +OwnerFE -> Owner: 참여자 목록 화면 표시\n- 참여자 정보 테이블\n- 페이지네이션\n- 총 참여자 수\n- CSV 다운로드 버튼 +deactivate OwnerFE + +note right of Owner + 참여자 정보: + - 이름 (마스킹: 김**) + - 전화번호 (마스킹: 010-****-1234) + - 참여경로 (우리동네TV, Instagram 등) + - 응모번호 + - 참여일시 +end note + == UFR-PART-030: 당첨자 추첨 == Owner -> OwnerFE: 이벤트 상세 화면에서\n"당첨자 추첨" 버튼 클릭 @@ -124,7 +158,7 @@ deactivate PartService Gateway --> OwnerFE: 200 OK deactivate Gateway -OwnerFE -> Owner: 당첨자 목록 화면 표시\n- 당첨자 정보 (이름, 전화번호, 응모번호)\n- "재추첨" 버튼\n- 추첨 완료 메시지 +OwnerFE -> Owner: 당첨자 목록 화면 표시\n- 당첨자 정보 (이름, 전화번호, 응모번호)\n- 추첨 완료 메시지 deactivate OwnerFE @enduml diff --git a/design/backend/sequence/outer/사용자인증플로우.puml b/design/backend/sequence/outer/사용자인증플로우.puml index a1d0223..e5d624b 100644 --- a/design/backend/sequence/outer/사용자인증플로우.puml +++ b/design/backend/sequence/outer/사용자인증플로우.puml @@ -27,16 +27,27 @@ activate UserService UserService -> UserService: 서버 측 유효성 검증\n(이름 2자 이상, 전화번호 형식 등) -UserService -> UserDB: 전화번호로 사용자 조회\n(중복 가입 확인) +UserService -> UserDB: 이메일로 사용자 조회\n(중복 가입 확인) activate UserDB UserDB --> UserService: 기존 사용자 확인 결과 deactivate UserDB -alt 중복 사용자 존재 - UserService --> Gateway: 400 Bad Request\n(이미 등록된 전화번호) +alt 이메일 중복 존재 + UserService --> Gateway: 400 Bad Request\n(이미 등록된 이메일) Gateway --> Frontend: 400 Bad Request - Frontend --> User: "이미 가입된 전화번호입니다" -else 신규 사용자 + Frontend --> User: "이미 가입된 이메일입니다" +else 이메일 신규 + + UserService -> UserDB: 전화번호로 사용자 조회\n(중복 가입 확인) + activate UserDB + UserDB --> UserService: 기존 사용자 확인 결과 + deactivate UserDB + + alt 전화번호 중복 존재 + UserService --> Gateway: 400 Bad Request\n(이미 등록된 전화번호) + Gateway --> Frontend: 400 Bad Request + Frontend --> User: "이미 가입된 전화번호입니다" + else 신규 사용자 UserService -> UserService: 비밀번호 해싱\n(bcrypt, Cost Factor 10) @@ -68,6 +79,7 @@ else 신규 사용자 Frontend -> Gateway: 대시보드 화면으로 이동 deactivate Frontend + end end == UFR-USER-020: 로그인 플로우 == @@ -75,19 +87,19 @@ end User -> Frontend: 로그인 화면 접근 activate Frontend -User -> Frontend: 전화번호, 비밀번호 입력 +User -> Frontend: 이메일, 비밀번호 입력 -Frontend -> Frontend: 클라이언트 측 유효성 검증\n(필수 필드 확인) +Frontend -> Frontend: 클라이언트 측 유효성 검증\n(필수 필드 확인, 이메일 형식) -Frontend -> Gateway: POST /api/users/login\n(전화번호, 비밀번호) +Frontend -> Gateway: POST /api/users/login\n(이메일, 비밀번호) activate Gateway Gateway -> Gateway: Request 검증 -Gateway -> UserService: POST /api/users/login\n(전화번호, 비밀번호) +Gateway -> UserService: POST /api/users/login\n(이메일, 비밀번호) activate UserService -UserService -> UserDB: 전화번호로 사용자 조회\n(로그인 인증용) +UserService -> UserDB: 이메일로 사용자 조회\n(로그인 인증용) activate UserDB UserDB --> UserService: 사용자 정보\n(user_id, password_hash, role) deactivate UserDB @@ -95,7 +107,7 @@ deactivate UserDB alt 사용자 없음 UserService --> Gateway: 401 Unauthorized\n(인증 실패) Gateway --> Frontend: 401 Unauthorized - Frontend --> User: "전화번호 또는 비밀번호를\n확인해주세요" + Frontend --> User: "이메일 또는 비밀번호를\n확인해주세요" else 사용자 존재 UserService -> UserService: 비밀번호 검증\n(bcrypt compare) @@ -103,7 +115,7 @@ else 사용자 존재 alt 비밀번호 불일치 UserService --> Gateway: 401 Unauthorized\n(인증 실패) Gateway --> Frontend: 401 Unauthorized - Frontend --> User: "전화번호 또는 비밀번호를\n확인해주세요" + Frontend --> User: "이메일 또는 비밀번호를\n확인해주세요" else 비밀번호 일치 UserService -> UserService: JWT 토큰 생성\n(user_id, role=OWNER,\nexp=7일) diff --git a/design/backend/sequence/outer/성과분석플로우.puml b/design/backend/sequence/outer/성과분석플로우.puml index 40811d3..8db66c6 100644 --- a/design/backend/sequence/outer/성과분석플로우.puml +++ b/design/backend/sequence/outer/성과분석플로우.puml @@ -7,7 +7,7 @@ actor "소상공인" as User participant "Frontend" as FE participant "API Gateway" as GW participant "Analytics Service" as Analytics -participant "Redis Cache\n(TTL 5분)" as Redis +participant "Redis Cache\n(TTL 1시간)" as Redis participant "Analytics DB" as AnalyticsDB participant "Kafka\n(Event Topics)" as Kafka @@ -37,7 +37,7 @@ deactivate Redis note right of Analytics **Cache-Aside 패턴** - - TTL: 5분 + - TTL: 1시간 - 예상 크기: 5KB - 히트율 목표: 95% - 응답 시간: 0.5초 @@ -105,7 +105,7 @@ note right of Analytics **목업 데이터 활용** - 초기에는 목업 데이터로 시작 - 점진적으로 실제 배치 작업 구현 - - 배치 주기: 10분마다 수집 + - 배치 주기: 5분마다 수집 end note ||| @@ -123,7 +123,7 @@ end note ||| == 2.4. Redis 캐싱 및 응답 == -Analytics -> Redis: 대시보드 데이터 캐시 저장\n(캐시키: analytics:dashboard:{eventId},\n값: 통합 데이터, TTL: 5분) +Analytics -> Redis: 대시보드 데이터 캐시 저장\n(캐시키: analytics:dashboard:{eventId},\n값: 통합 데이터, TTL: 1시간) activate Redis Redis --> Analytics: OK deactivate Redis diff --git a/design/backend/sequence/outer/이벤트생성플로우.puml b/design/backend/sequence/outer/이벤트생성플로우.puml index 7007f7f..fda5856 100644 --- a/design/backend/sequence/outer/이벤트생성플로우.puml +++ b/design/backend/sequence/outer/이벤트생성플로우.puml @@ -21,18 +21,20 @@ participant "배포 채널 APIs" as ChannelApis == 1. 이벤트 목적 선택 (UFR-EVENT-020) == User -> FE: 이벤트 목적 선택 -FE -> Gateway: POST /events/purposes\n{목적, userId} -Gateway -> Event: 이벤트 목적 저장 요청 -Event -> UserSvc: GET /api/users/{userId}/store\n회원 및 매장정보 조회 +FE -> UserSvc: GET /api/users/{userId}/store\n회원 및 매장정보 조회 activate UserSvc UserSvc -> UserDB: 사용자 및 매장 정보 조회 activate UserDB UserDB --> UserSvc: 사용자, 매장 정보 반환 deactivate UserDB -UserSvc --> Event: 200 OK\n{userId, storeName, industry, address} +UserSvc --> FE: 200 OK\n{userId, storeName, industry, address} deactivate UserSvc -Event -> EventDB: 이벤트 목적 정보 저장\n(목적, 매장정보 저장) -EventDB --> Event: 저장 완료 +FE -> Gateway: POST /events/purposes\n{목적, userId, storeName, industry, address} +Gateway -> Event: 이벤트 목적 저장 요청 +Event -> Redis: 이벤트 목적 정보 저장\nKey: draft:event:{eventDraftId}\n(목적, 매장정보 저장)\nTTL: 24시간 +activate Redis +Redis --> Event: 저장 완료 +deactivate Redis Event --> Gateway: 저장 완료\n{eventDraftId} Gateway --> FE: 200 OK FE --> User: AI 추천 화면으로 이동 @@ -49,9 +51,7 @@ FE --> User: "AI가 분석 중입니다..." (로딩) note over AI: Kafka Consumer\nai 이벤트 생성 topic 구독 Kafka --> AI: Consume Job Message\n{jobId, eventDraftId, ...} -AI -> EventDB: 과거 이벤트 데이터 조회\n(업종, 지역 기반 통계 조회) -EventDB --> AI: 이벤트 통계 데이터 -AI -> AIApi: 트렌드 분석 및 이벤트 추천 요청\n{목적, 업종, 지역, 과거데이터, 매장정보}\n[Circuit Breaker, Timeout: 5분] +AI -> AIApi: 트렌드 분석 및 이벤트 추천 요청\n{목적, 업종, 지역, 매장정보}\n[Circuit Breaker, Timeout: 5분] AIApi --> AI: 3가지 추천안 + 트렌드 요약\n(예: "여름철 시원한 음료 선호도 증가") AI -> Redis: AI 추천 결과 저장\nKey: ai:event:{eventDraftId}\n(3가지 추천안, 트렌드 요약)\nTTL: 24시간 Redis --> AI: 저장 완료 @@ -80,18 +80,20 @@ end User -> FE: 추천안 선택\n(제목/경품 커스텀) FE -> Gateway: PUT /events/drafts/{eventDraftId}\n{선택한 추천안, 커스텀 정보} Gateway -> Event: 선택 저장 -Event -> EventDB: 선택한 추천안 저장\n(이벤트 초안 업데이트) -EventDB --> Event: 업데이트 완료 +Event -> Redis: 선택한 추천안 저장\nKey: draft:event:{eventDraftId}\n(이벤트 초안 업데이트)\nTTL: 24시간 +activate Redis +Redis --> Event: 업데이트 완료 +deactivate Redis Event --> Gateway: 200 OK Gateway --> FE: 저장 완료 FE --> User: 콘텐츠 생성 화면으로 이동 == 3. SNS 이미지 생성 - 비동기 처리 (UFR-CONT-010) == User -> FE: 이미지 생성 요청 -FE -> Gateway: POST /api/events/{eventDraftId}/content-generation -Gateway -> Event: 이미지 생성 요청 -Event -> Event: Job 생성\n{jobId, eventDraftId, status: PENDING} -Event --> Gateway: Job 생성 완료\n{jobId, status: PENDING} +FE -> Gateway: POST /api/content/images/{eventDraftId}/generate +Gateway -> Content: 이미지 생성 요청 +Content -> Content: Job 생성\n{jobId, eventDraftId, status: PENDING} +Content --> Gateway: Job 생성 완료\n{jobId, status: PENDING} Gateway --> FE: 202 Accepted\n{jobId} FE --> User: "이미지 생성 중..." (로딩) @@ -124,17 +126,17 @@ note over Content, Redis: 이미지 URL은 Redis에 저장\n- 최종 승인 시 group Polling으로 상태 확인 loop 상태 확인 (최대 30초) - FE -> Gateway: GET /jobs/{jobId}/status - Gateway -> Event: Job 상태 조회 - Event -> Redis: Job 상태 조회\n(jobId로 상태 및 이미지 URL 조회) - Redis --> Event: {status, imageUrls} + FE -> Gateway: GET /api/content/jobs/{jobId}/status + Gateway -> Content: Job 상태 조회 + Content -> Redis: Job 상태 조회\n(jobId로 상태 및 이미지 URL 조회) + Redis --> Content: {status, imageUrls} alt Job 완료 - Event --> Gateway: 200 OK\n{status: COMPLETED, imageUrls} + Content --> Gateway: 200 OK\n{status: COMPLETED, imageUrls} Gateway --> FE: 이미지 URL 반환 FE --> User: 3가지 스타일 카드 표시 else Job 진행중 - Event --> Gateway: 200 OK\n{status: PENDING/PROCESSING} + Content --> Gateway: 200 OK\n{status: PENDING/PROCESSING} Gateway --> FE: 진행중 상태 note over FE: 2초 후 재요청 end @@ -144,8 +146,10 @@ end User -> FE: 스타일 선택 및 편집 FE -> Gateway: PUT /events/drafts/{eventDraftId}/content\n{선택한 이미지, 편집내용} Gateway -> Event: 콘텐츠 선택 저장 -Event -> EventDB: 선택한 콘텐츠 저장\n(이벤트 초안 업데이트) -EventDB --> Event: 업데이트 완료 +Event -> Redis: 선택한 콘텐츠 저장\nKey: draft:event:{eventDraftId}\n(이벤트 초안 업데이트)\nTTL: 24시간 +activate Redis +Redis --> Event: 업데이트 완료 +deactivate Redis Event --> Gateway: 200 OK Gateway --> FE: 저장 완료 FE --> User: 배포 채널 선택 화면으로 이동 @@ -157,6 +161,11 @@ Gateway -> Event: 최종 승인 및 배포 처리 note over Event: Redis 데이터를 Event DB에 영구 저장 +Event -> Redis: 이벤트 초안 조회\nKey: draft:event:{eventDraftId} +activate Redis +Redis --> Event: 이벤트 초안 데이터\n(목적, 매장정보, 추천안, 콘텐츠) +deactivate Redis + Event -> Redis: AI 추천 결과 조회\nKey: ai:event:{eventDraftId} activate Redis Redis --> Event: AI 추천 결과 @@ -167,7 +176,7 @@ activate Redis Redis --> Event: 이미지 URL 목록 deactivate Redis -Event -> EventDB: 이벤트 정보 영구 저장\n(AI 추천, 이미지 URL, 배포 채널 포함) +Event -> EventDB: 이벤트 정보 영구 저장\n(목적, 매장정보, AI 추천, 이미지 URL, 배포 채널 포함) EventDB --> Event: 저장 완료 Event -> EventDB: 이벤트 상태 변경\n(DRAFT → APPROVED로 업데이트)