이정수
|
305cc73860
|
feat(badge): W4 유저 배지/평판 — 리뷰어/테크니션 자동·수동 하이브리드 부여 + 평판 이벤트 감사
- badges/user_badges/reputation_events 3테이블: docs/badge-ddl.sql 권위 + db/schema.sql 동기, 멱등. badge_type/event_type CHECK + 부분유니크(ux_user_badges_active WHERE revoked_at IS NULL · ux_reputation_events_source WHERE source_ref IS NOT NULL) + FK→users
- BadgeKeys enum 단일정의처(REVIEWER 임계10/REVIEW_WRITTEN, TECHNICIAN 임계3/GAME_UPLOADED). BadgeCatalogSeeder ApplicationRunner ON CONFLICT DO NOTHING(신규 키만 시드)
- 자동부여: ReputationService.record(best-effort 훅, 리뷰작성/게임업로드 후단 try/catch) → BadgeService.evaluateAndSync(countActive>=임계 → insertIgnore 멱등). 임계초과 추가활동·동시성 1회만(ON CONFLICT 부분유니크)
- 수동 부여/회수: BadgeManageController /manage/users/{userId}/badges/{key}/(grant|revoke) — /admin/** 밖 경로 + permissionGate.has(BADGE_MANAGE) 게이트헬퍼(ADMIN 암묵+SUBADMIN 키). CSRF→401→403. revoke=revoked_at+reason, 회수후 재획득 가능
- 신규 권한키 BADGE_MANAGE(PermissionKeys 4번째) — PermissionCatalogVerifier values() 자동 시드(W1 카탈로그 3→4, 회귀 0)
- 표시: 리뷰 작성자 배지 배치 부착(listReviews userId 집합 IN 1쿼리, N+1 차단) + game-detail.jsp 칩(textContent)
- 신규 5매퍼 #{} only(${} 0). @MockBean 6
검증: 컨테이너 ./mvnw -o test 347/347 GREEN(신규 29: BadgeServiceTest 12·BadgeManageControllerTest 14·GameReviewControllerTest 회귀 3), 회귀 0. L2 격리 throwaway DB contract PASS(자동부여 멱등·회수후 재획득 부분유니크·reputation dedupe·countActive·배치 alias·CHECK/FK). W1 카탈로그 회귀 0. 실 dev DB 무접촉.
알려진 잔여(표시 deferral): 프로필 myBadges 컨트롤러 주입 + 게임카드 creator 배지 미구현(JSP 방어적 준비됨, 무렌더 — 설계 graceful-empty 정합). 배지 도메인 코어 + 리뷰작성자 표시는 완성. 두 표시면은 WebMvcController/SearchController(W3-1/3-4) 수정 필요라 별도 후속.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
|
2026-06-29 12:34:23 +09:00 |