16 Commits

Author SHA1 Message Date
cf5f10af64 Add season-end settlement and tierPoints reset
- config/season 문서(id/startDate/endDate)로 시즌 경계 정의 — 문서가 없으면 시즌제 비활성(기존 동작 유지)
- endDate 다음 날 dailyArchive에서 maybeSettleSeason 1회 실행: 랭킹 대상 전원의 최종 성적(tierPoints·티어·동점 동일 rank)을 users/{uid}/seasonHistory/{seasonId}에 아카이브 후 tierPoints 0 리셋·rankSnapshot 제거
- 유저별 아카이브+리셋을 같은 배치로 묶고 settledAt 마커는 전원 완료 후 기록 — 부분 실패 재실행 시 기정산 유저 점수를 collectionGroup으로 되읽어 순위 보존(멱등)
- 스트릭은 시즌과 무관하게 유지(이월 이득은 streakBonus 상한이 제한)
- seasonHistory 본인 읽기 전용 rules와 seasonId collectionGroup 인덱스 추가, 정산 시나리오 테스트 신규 작성
2026-07-23 14:46:22 +09:00
ffafca1338 Unify prediction ranking on baseball tier codes
- 티어 코드를 bronze~diamond에서 야구 테마 BW/PR/ST/AS/MVP로 교체 (임계값 0/100/300/700/1500 유지), 클라이언트 동기화 주석 추가
- 누적 예측 수 기반 레벨 시스템 제거 — levels.ts 삭제, /stats 응답의 currentLevel·progress 필드 제거
- 스코어보드 top/me 엔트리에 tierPoints 파생 tier 코드 포함, 배포 이전 생성 캐시는 응답 시점에 tier 보강
- 주간 마스터 티켓 잔재 주석과 문서 표의 tickets 항목 정리, 폐기된 레벨·티켓 언급 주석 정돈
- statsService 테스트를 새 티어 코드·필드 구성으로 갱신
2026-07-23 14:13:19 +09:00
d5aca61f4f Add admin user search and list APIs
- GET /admin/users/search: 닉네임 접두어(Firestore orderBy+startAt/endAt) + 이메일 정확 일치(Auth 조회) + uid 정확 일치를 한 입력으로 통합 검색, uid 기준 중복 제거
- GET /admin/users/list: 가입일 내림차순 + 문서 id 커서 페이지네이션 (주문 목록과 동일 패턴)
- AdminUserDto 추가: 날짜는 UTC ISO 변환, 옵션 필드는 키 생략, streak/티어 등 lazy 보정 필드는 기존 원칙대로 제외, active 미백필 문서는 활성으로 간주
- 비활성(탈퇴) 유저도 검색 대상에 포함해 어드민이 상태를 확인할 수 있게 함
- 에뮬레이터 회귀 테스트 5건 추가 (접두어 검색·uid 일치·비활성 표시·빈 q 400·커서 페이지네이션)
2026-07-21 15:25:37 +09:00
c964572a9c Replace point ledger with wallet engine and rework reward earning
- 지갑 문서(users/{uid}/wallet/current)와 문서 ID=멱등키 원장(pointLedger/{txId})으로 포인트 엔진 교체 — 기존 balanceAfter 최신 row 조회 방식 폐기
- 모든 포인트 변경은 pointService.applyPointChangesTx 단일 경로로 처리, available+reserved == totalEarned-totalSpent 불변식을 매 커밋 검증
- 출석 리워드 개편: 일일 20P, 연속 5일 +50P(사이클당 1회), 10일 단위 +100P — attendance/state 문서에 스트릭 상태 저장, 주간·월간 보너스 폐기
- 승부예측 일일 리워드 정산 신설: 전체 참여 50P + 성공 100P + 퍼펙트 50P, judgeDay 이후 voteHistory.rewardSettledAt 플래그와 원장 멱등키로 배치 재실행에도 중복 지급 차단
- 관리자 포인트 지급·회수(adminPointService)와 수동 재정산 디버그 라우트(/debug/settle-reward) 추가
- 소비처 없던 티켓 시스템(dailyAllKill·weeklyMaster)과 위클리마스터 판정 흐름 전체 제거 — StatsResponse.tickets 필드 삭제로 클라 응답 스키마 변경
2026-07-16 14:25:42 +09:00
4d1a46e587 Implement soft-delete for user accounts and add account purge job
- 회원 탈퇴 시 즉시 삭제 대신 `active: false` 및 `deactivatedAt`을 기록하여 30일간 데이터를 유예하는 소프트 삭제 방식을 도입했습니다.
- 경기 취소 시 투표를 삭제하는 대신 `cancelled: true`로 마킹하여 참여 흔적을 보존하고, 통계 집계에서만 제외하도록 로직을 변경했습니다.
- 유예 기간이 지난 비활성 계정을 영구적으로 파기하는 `accountPurge` 스케줄러를 추가했습니다.
2026-07-02 11:29:40 +09:00
f0b01a2dc0 Field-mask the scoreboard self-user read (R5)
getScoreboard loaded the full user doc just to use 5 fields. Add
getUserForScoreboard using getAll with a fieldMask so only displayName,
photoUrl, tierPoints, favoriteTeamCode, and rankSnapshot are fetched,
cutting per-request bandwidth. The short-lived me-rank cache (meRankCache,
30s) already covers the rank-count side of R5.
2026-05-28 17:34:16 +09:00
38a2f78647 Fold rank snapshot into the daily judgment transaction (W4)
dailyArchive wrote users/{uid} twice per cron run: once for rankSnapshot
and once for the judgment. Extract the read-only computeRankSnapshot, then
pass the pre-judgment snapshot into judgeDay so applyDailyJudgmentTx merges
it into the same patch as the judgment write. The snapshot is computed from
current (pre-judgment) tierPoints before the transaction, preserving the
"rank BEFORE judgment" semantics; on the idempotent guard-skip path no
write occurs (and stale re-snapshotting is avoided). snapshotRankForUser is
kept as a thin wrapper for its existing callers/tests.
2026-05-28 17:29:14 +09:00
1a68d41e4f Implement lazy streak correction and enhance game result synchronization.
- 사용자의 마지막 참여일 이후 실제 경기가 있었음에도 참여하지 않은 경우를 감지하여 스트릭을 0으로 초기화하는 지연 보정(lazy correction) 로직을 도입했습니다.
- KBO 휴장일(월요일, 전체 우천 취소 등)을 자동으로 식별하여 실제 경기가 열린 날에만 결석 판정이 내려지도록 `hasMissedGameDayBetween` 검증 기능을 구현했습니다.
- 실시간 라이브 데이터를 기반으로 전일 경기 결과를 확정하는 기능을 추가하여 월간 일정 갱신 과정에서 발생할 수 있는 데이터 누락을 보완했습니다.
- 일일 아카이브 및 경기 동기화 로직을 수동으로 트리거할 수 있는 디버그 엔드포인트를 추가하고, 통계 데이터에 산출 기준일(forDate)을 포함하여 캐시 무효화 정밀도를 개선했습니다.
2026-05-06 11:14:28 +09:00
b25e78dde8 Implement scoreboard system with rank snapshots and delta tracking.
- 전체 및 팀별 스코어보드 기능을 구현하고, 순위 변동(delta)을 시각화하기 위한 스냅샷 시스템을 도입했습니다.
- `dailyArchive` 과정에서 사용자의 현재 순위를 `rankSnapshot`으로 기록하여, 다음 판정 시점의 순위 변화량을 정확히 계산합니다.
- 상위권 데이터는 RTDB에 사전 계산(precompute)하여 저장함으로써 대규모 조회 요청에 대응하고, `MemCache`를 통해 API 응답 성능을 최적화했습니다.
- Firestore 복합 색인과 Count Aggregation을 활용하여 동점자 처리가 포함된 랭킹 및 상위 퍼센타일 산출 로직을 구현했습니다.
- 관련 서비스 레이어, 저장소 함수, API 핸들러 및 단위 테스트 코드를 추가했습니다.
2026-04-23 09:19:04 +09:00
392ecfbd0b Implement game result judgment and automated reward system.
- `dailyArchive` 과정에 `judgeDay`를 통합하여 경기 결과에 따른 일간 판정, 스트릭 업데이트, 티어 포인트 가산을 자동화했습니다.
- 주간 모든 경기를 성공적으로 예측했을 때 '주간 마스터 티켓'을 지급하는 `judgeWeekIfNeeded` 로직을 구현했습니다.
- Firestore 트랜잭션을 사용하여 판정 결과 반영의 원자성을 확보하고, 중복 처리를 방지하는 멱등성 가드를 적용했습니다.
- `statsService`가 실시간 계산 대신 사용자 문서에 저장된 스트릭과 티어 정보를 활용하도록 고도화하고 관련 유틸리티와 테스트 코드를 추가했습니다.
2026-04-22 11:08:35 +09:00
a59ccf3314 Implement user profile updates and synthesize KBO game IDs.
- 사용자 프로필 수정을 위한 `PATCH` 핸들러와 `updateMe` 서비스를 구현하여 닉네임, 응원팀, 지식 수준 변경 기능을 추가했습니다.
- HTML 정규식 파싱 오류로 인해 `null`이 반환되던 `gameId` 문제를 해결하기 위해 일정 메타데이터 기반의 ID 생성 로직을 도입했습니다.
- `updateUser`가 `FieldValue`를 수용하도록 개선하여 응원팀 설정 해제(필드 삭제)가 가능하도록 수정했습니다.
- 닉네임 중복 확인 및 변경 규칙 검증을 포함한 유저 서비스 테스트 케이스를 작성하고 `TODO.md` 이슈를 해결했습니다.
2026-04-18 15:53:29 +09:00
c7e4ed8c22 Standardize game status values and make favorite team optional.
- `GameStatus` 타입을 `scheduled`, `live`, `completed`, `cancelled`로 변경하여 상태 관리의 일관성을 높였습니다.
- 사용자 프로필의 `favoriteTeamCode`를 선택 사항으로 변경하고, null 또는 undefined 입력에 대한 처리를 추가했습니다.
- KBO 데이터 병합 과정에 상세 로그를 도입하여 실시간 데이터 매칭 및 외부 API 호출 실패 상황에 대한 가시성을 확보했습니다.
- 상태 값 변경에 따라 영향받는 서비스 로직과 테스트 코드를 일괄 업데이트했습니다.
2026-04-14 23:08:45 +09:00
697e0ff11a Strengthen nickname uniqueness validation with Firestore fallback.
- RTDB 예약 시스템에만 의존하던 닉네임 중복 검증에 Firestore 조회를 통한 fallback 로직을 추가했습니다.
- `consumeReservation`을 검증(`verify`)과 삭제(`delete`) 단계로 분리하여 유저 생성 과정의 안정성을 높였습니다.
- 이미 가입이 완료되어 RTDB 예약 기록이 삭제된 닉네임에 대해서도 중복 가입을 차단하도록 개선했습니다.
- 확정된 유저의 닉네임 중복 요청 시 409 NICKNAME_TAKEN 에러를 반환하는 테스트 케이스를 추가했습니다.
2026-04-14 13:26:43 +09:00
dfd21ba4c2 Implement nickname reservation system and add user delete functionality.
- 닉네임 선점 및 검증을 위한 예약 시스템을 구현하고 관련 로직을 추가했습니다.
- 유저 데이터 및 하위 컬렉션을 완전히 삭제하는 회원 탈퇴 기능을 구현했습니다.
- 토큰의 프로필 사진 변경 시 유저 정보를 자동으로 동기화하도록 개선했습니다.
- RTDB를 활용하여 닉네임 예약 상태를 관리하고, 회원 가입 시 이를 검증하도록 변경했습니다.
2026-04-14 13:16:50 +09:00
f98fa84da8 feat: user endpoint update
기존 유저 관련 엔드포인트를 /user로 전부 통합해서 관리할 수 있도록 수정 및 테스트 코드 추가
2026-04-13 13:21:04 +09:00
7ff609022f init 2026-04-13 09:18:34 +09:00