|
| 1 | +import { type User, type SettlementStatus, type Participant, type Room, type Location } from "@/types/mongo"; |
| 2 | + |
| 3 | +/** |
| 4 | + * 쿼리를 통해 얻은 Room Document를 populate할 설정값을 정의합니다. |
| 5 | + * @constant {{path: string, select: string, populate?: {path: string, select: string}}[]} |
| 6 | + */ |
| 7 | +export const roomPopulateOption = [ |
| 8 | + { path: "from", select: "_id koName enName" }, |
| 9 | + { path: "to", select: "_id koName enName" }, |
| 10 | + { |
| 11 | + path: "part", |
| 12 | + select: "-_id user settlementStatus readAt", |
| 13 | + populate: { path: "user", select: "_id id name nickname profileImageUrl" }, |
| 14 | + }, |
| 15 | +]; |
| 16 | + |
| 17 | +interface PopulatedParticipant extends Pick<Participant, "settlementStatus" | "readAt"> { |
| 18 | + user: Pick<User, "_id" | "id" | "name" | "nickname" | "profileImageUrl">; |
| 19 | +} |
| 20 | + |
| 21 | +export interface PopulatedRoom extends Omit<Room, "from" | "to" | "part"> { |
| 22 | + from: Pick<Location, "_id" | "koName" | "enName">; |
| 23 | + to: Pick<Location, "_id" | "koName" | "enName">; |
| 24 | + part?: PopulatedParticipant[]; |
| 25 | +} |
| 26 | + |
| 27 | +export interface FormattedRoom extends Omit<PopulatedRoom, "part" | "settlementTotal"> { |
| 28 | + part?: { |
| 29 | + _id: string; |
| 30 | + name: string; |
| 31 | + nickname: string; |
| 32 | + profileImageUrl: string; |
| 33 | + isSettlement?: SettlementStatus; |
| 34 | + readAt: Date; |
| 35 | + }[]; |
| 36 | + settlementTotal?: number; |
| 37 | + isOver?: boolean; |
| 38 | + isDeparted: boolean; |
| 39 | +}; |
| 40 | + |
| 41 | +/** |
| 42 | + * Room Object가 주어졌을 때 room의 part array의 각 요소를 API 명세에서와 같이 {userId: String, ... , isSettlement: String}으로 가공합니다. |
| 43 | + * 또한, 방이 현재 출발했는지 유무인 isDeparted 속성을 추가합니다. |
| 44 | + * @param {PopulatedRoom} roomObject - 정산 정보를 가공할 room Object로, Mongoose Document가 아닌 순수 Javascript Object여야 합니다. |
| 45 | + * @param {Object} options - 추가 파라미터로, 기본값은 {}입니다. |
| 46 | + * @param {Boolean} options.includeSettlement - 반환 결과에 정산 정보를 포함할 지 여부로, 기본값은 true입니다. |
| 47 | + * @param {Date} options.timestamp - 방의 출발 여부(isDeparted)를 판단하는 기준이 되는 시각입니다. |
| 48 | + * @param {Boolean} options.isOver - 방의 완료 여부(isOver)로, 기본값은 false입니다. includeSettlement가 false인 경우 roomDocument의 isOver 속성은 undefined로 설정됩니다. |
| 49 | + * @return {FormattedRoom} 정산 여부가 위와 같이 가공되고 isDeparted 속성이 추가된 Room Object가 반환됩니다. |
| 50 | + */ |
| 51 | +export const formatSettlement = ( |
| 52 | + roomObject: PopulatedRoom, |
| 53 | + { includeSettlement = true, isOver = false, timestamp = Date.now() } = {} |
| 54 | +): FormattedRoom => { |
| 55 | + return { |
| 56 | + ...roomObject, |
| 57 | + part: roomObject.part?.map((participantSubDocument) => { |
| 58 | + const { _id, name, nickname, profileImageUrl } = |
| 59 | + participantSubDocument.user; |
| 60 | + const { settlementStatus, readAt } = participantSubDocument; |
| 61 | + return { |
| 62 | + _id, |
| 63 | + name, |
| 64 | + nickname, |
| 65 | + profileImageUrl, |
| 66 | + isSettlement: includeSettlement ? settlementStatus : undefined, |
| 67 | + readAt: readAt ?? roomObject.madeat, |
| 68 | + }; |
| 69 | + }), |
| 70 | + settlementTotal: includeSettlement ? roomObject.settlementTotal : undefined, |
| 71 | + isOver: includeSettlement ? isOver : undefined, |
| 72 | + isDeparted: new Date(roomObject.time) < new Date(timestamp), |
| 73 | + }; |
| 74 | +} |
| 75 | + |
| 76 | +/** |
| 77 | + * roomPopulateOption을 사용해 populate된 Room Object와 사용자의 id(userId)가 주어졌을 때, 해당 사용자의 정산 상태를 반환합니다. |
| 78 | + * @param {PopulatedRoom} roomObject - roomPopulateOption을 사용해 populate된 변환한 Room Object입니다. |
| 79 | + * @param {String} userId - 방 완료 상태를 확인하려는 사용자의 id(user.id)입니다. |
| 80 | + * @return {Boolean | undefined} 사용자의 해당 방에 대한 완료 여부(true | false)를 반환합니다. 사용자가 참여중인 방이 아닐 경우 undefined를 반환합니다. |
| 81 | + **/ |
| 82 | +export const getIsOver = (roomObject: PopulatedRoom, userId: string) => { |
| 83 | + // room document의 part subdoocument에서 사용자 id와 일치하는 정산 정보를 찾습니다. |
| 84 | + const participantSubDocuments = roomObject.part?.filter((part) => { |
| 85 | + return part.user.id === userId; |
| 86 | + }); |
| 87 | + |
| 88 | + // 방에 참여중이지 않은 사용자의 경우, undefined을 반환합니다. |
| 89 | + if (!participantSubDocuments || participantSubDocuments.length === 0) return undefined; |
| 90 | + |
| 91 | + // 방에 참여중인 사용자의 경우, 정산 상태가 완료된 것인지("paid"거나 "sent"인지)를 반환합니다. |
| 92 | + return ["paid", "sent"].includes(participantSubDocuments[0].settlementStatus); |
| 93 | +}; |
0 commit comments