- `GameStatus` 타입을 `scheduled`, `live`, `completed`, `cancelled`로 변경하여 상태 관리의 일관성을 높였습니다. - 사용자 프로필의 `favoriteTeamCode`를 선택 사항으로 변경하고, null 또는 undefined 입력에 대한 처리를 추가했습니다. - KBO 데이터 병합 과정에 상세 로그를 도입하여 실시간 데이터 매칭 및 외부 API 호출 실패 상황에 대한 가시성을 확보했습니다. - 상태 값 변경에 따라 영향받는 서비스 로직과 테스트 코드를 일괄 업데이트했습니다.
80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
import { FieldValue } from "firebase-admin/firestore";
|
|
import { firestore } from "../firebase";
|
|
import type {
|
|
KnowledgeLevel,
|
|
Provider,
|
|
TeamCode,
|
|
User,
|
|
} from "../types/panit";
|
|
|
|
const COLLECTION = "users";
|
|
|
|
export interface RegisterInput {
|
|
displayName: string;
|
|
email: string;
|
|
photoUrl?: string;
|
|
provider: Provider;
|
|
favoriteTeamCode?: TeamCode;
|
|
knowledgeLevel: KnowledgeLevel;
|
|
}
|
|
|
|
/**
|
|
* 특정 유저 문서를 조회한다.
|
|
*
|
|
* @param uid - Firebase Auth UID
|
|
* @returns 유저 문서. 존재하지 않으면 `null`.
|
|
*/
|
|
export async function getUser(uid: string): Promise<User | null> {
|
|
const snap = await firestore.collection(COLLECTION).doc(uid).get();
|
|
return snap.exists ? (snap.data() as User) : null;
|
|
}
|
|
|
|
/**
|
|
* displayName으로 유저의 uid를 조회한다. 없으면 `null`.
|
|
* 닉네임 유니크성 검증용 fallback.
|
|
*/
|
|
export async function findUidByDisplayName(
|
|
displayName: string
|
|
): Promise<string | null> {
|
|
const snap = await firestore
|
|
.collection(COLLECTION)
|
|
.where("displayName", "==", displayName)
|
|
.limit(1)
|
|
.get();
|
|
return snap.empty ? null : snap.docs[0].id;
|
|
}
|
|
|
|
/**
|
|
* 신규 유저 문서를 생성한다. `createdAt`은 서버 타임스탬프로 기록되며,
|
|
* 같은 UID가 있으면 전체 덮어쓴다(`merge: false`).
|
|
*/
|
|
export async function createUser(uid: string, input: RegisterInput): Promise<void> {
|
|
const doc: Record<string, unknown> = {
|
|
displayName: input.displayName,
|
|
email: input.email,
|
|
provider: input.provider,
|
|
knowledgeLevel: input.knowledgeLevel,
|
|
createdAt: FieldValue.serverTimestamp(),
|
|
};
|
|
if (input.photoUrl) doc.photoUrl = input.photoUrl;
|
|
if (input.favoriteTeamCode) doc.favoriteTeamCode = input.favoriteTeamCode;
|
|
await firestore.collection(COLLECTION).doc(uid).set(doc, { merge: false });
|
|
}
|
|
|
|
/**
|
|
* 유저 문서의 일부 필드를 병합 업데이트한다.
|
|
*/
|
|
export async function updateUser(
|
|
uid: string,
|
|
patch: Partial<Pick<User, "displayName" | "photoUrl" | "favoriteTeamCode" | "knowledgeLevel">>
|
|
): Promise<void> {
|
|
await firestore.collection(COLLECTION).doc(uid).set(patch, { merge: true });
|
|
}
|
|
|
|
/**
|
|
* 유저 문서 및 하위 컬렉션(voteHistory 등)을 모두 삭제한다.
|
|
*/
|
|
export async function deleteUser(uid: string): Promise<void> {
|
|
await firestore.recursiveDelete(firestore.collection(COLLECTION).doc(uid));
|
|
}
|