Skip to content

Commit

Permalink
Merge pull request #125 from BumgeunSong/feat/sort-streak
Browse files Browse the repository at this point in the history
정렬 로직을 연속일자 기준으로 하고 연속일자가 같으면 글쓴 개수로 한다.
  • Loading branch information
BumgeunSong authored Jan 4, 2025
2 parents d162eae + 27148ea commit f91b4f8
Showing 1 changed file with 97 additions and 76 deletions.
173 changes: 97 additions & 76 deletions functions/src/writingHistory/getWritingStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ import { onRequest } from "firebase-functions/v2/https";
import admin from "../admin";
import { isWorkingDay } from "../notifications/isWorkingDay";

interface UserData {
id: string;
nickname: string | null;
realName: string | null;
profilePhotoURL: string | null;
bio: string | null;
}

interface WritingStatsWithMeta extends WritingStats {
streak: number;
totalContributions: number;
}

interface WritingStats {
user: {
id: string;
nickname: string | null;
realname: string | null;
profilePhotoURL: string | null;
bio: string | null;
}
user: UserData;
contributions: Contribution[];
badges: WritingBadge[];
}
Expand All @@ -29,10 +36,67 @@ const calculateTotalContributions = (contributions: Contribution[]): number => {
return contributions.reduce((sum: number, value: Contribution) => sum + (value.contentLength !== null ? 1 : 0), 0);
};

// 사용자 데이터 변환 (순수 함수)
const createUserProfile = (id: string, data: FirebaseFirestore.DocumentData): UserData => ({
id,
nickname: data.nickname ?? null,
realName: data.realName ?? null,
profilePhotoURL: data.profilePhotoURL ?? null,
bio: data.bio ?? null
});

// 단일 사용자의 WritingStats 생성 (순수 함수)
const createUserWritingStats = (
userData: UserData,
histories: FirebaseFirestore.QueryDocumentSnapshot[],
workingDays: string[]
): WritingStatsWithMeta | null => {
if (histories.length === 0) return null;

const contributions = createContributions(workingDays, histories);
const streak = calculateRecentStreak(workingDays, histories);
const totalContributions = calculateTotalContributions(contributions);
const badges = createBadges(streak);

return {
user: userData,
contributions,
badges,
streak,
totalContributions
};
};

// WritingStats 정렬 (순수 함수)
const sortWritingStats = (stats: WritingStatsWithMeta[]): WritingStats[] => {
return stats
.sort((a, b) => {
if (b.streak !== a.streak) {
return b.streak - a.streak;
}
return b.totalContributions - a.totalContributions;
})
.map(({ streak, totalContributions, ...stat }) => stat);
};

// 데이터베이스에서 사용자 기록 조회 (비순수 함수)
const fetchUserWritingHistory = async (
userDoc: FirebaseFirestore.QueryDocumentSnapshot
): Promise<{
userData: UserData;
histories: FirebaseFirestore.QueryDocumentSnapshot[];
}> => {
const histories = await userDoc.ref.collection('writingHistories').get();
const userData = createUserProfile(userDoc.id, userDoc.data());

return {
userData,
histories: histories.docs
};
};

export const getWritingStats = onRequest(
{
cors: true
},
{ cors: true },
async (req, res) => {
if (req.method !== 'GET') {
res.status(405).json({
Expand All @@ -44,61 +108,25 @@ export const getWritingStats = onRequest(

try {
const db = admin.firestore();

// 1. 최근 20 영업일 계산
const workingDays = getRecentWorkingDays(20);

// 2. 모든 사용자 조회
// 1. 사용자 데이터 조회
const usersSnapshot = await db.collection('users').get();

// 3. 각 사용자의 WritingStats 생성
const writingStats = await Promise.all(
usersSnapshot.docs.map(async userDoc => {
// 사용자의 writingHistories 조회
const historiesSnapshot = await userDoc
.ref
.collection('writingHistories')
.get();

// writingHistory가 없는 사용자 제외
if (historiesSnapshot.empty) {
return null;
}

// 각 날짜별 컨텐츠 길이 매핑
const contributions = createContributions(
workingDays,
historiesSnapshot.docs
);

const badges = createBadges(workingDays, historiesSnapshot.docs);

// WritingStats 생성
const userData = userDoc.data();
return {
user: {
id: userDoc.id,
nickname: userData.nickname,
realname: userData.realName,
profilePhotoURL: userData.profilePhotoURL,
bio: userData.bio
},
contributions,
badges: badges,
// 정렬을 위한 총 기여도 추가
totalContributions: calculateTotalContributions(contributions)
};
})
// 2. 각 사용자의 기록 조회
const usersData = await Promise.all(
usersSnapshot.docs.map(fetchUserWritingHistory)
);
// 4. null 값 필터링 (writingHistory가 없는 사용자 제외)
const filteredStats: WritingStats[] = writingStats
.filter((stat): stat is Exclude<typeof stat, null> =>
stat !== null

// 3. WritingStats 생성
const writingStatsWithMeta = usersData
.map(({ userData, histories }) =>
createUserWritingStats(userData, histories, workingDays)
)
// 총 기여도 기준으로 내림차순 정렬
.sort((a, b) => (b?.totalContributions ?? 0) - (a?.totalContributions ?? 0))
// totalContributions 필드 제거
.map(({ totalContributions, ...stat }) => stat);
.filter((stats): stats is WritingStatsWithMeta => stats !== null);

// 4. 정렬 및 최종 데이터 형식 변환
const filteredStats = sortWritingStats(writingStatsWithMeta);

res.status(200).json({
status: 'success',
Expand Down Expand Up @@ -160,23 +188,6 @@ function createContributions(
return contributions;
}

function createBadges(
workingDays: string[],
histories: admin.firestore.QueryDocumentSnapshot[]
): WritingBadge[] {
const recentStreak = calculateRecentStreak(workingDays, histories);
if (recentStreak < 2) {
return [];
}

return [
{
name: `연속 ${recentStreak}일차`,
emoji: '🔥'
}
];
}

function calculateRecentStreak(
workingDays: string[],
histories: admin.firestore.QueryDocumentSnapshot[]
Expand All @@ -198,4 +209,14 @@ function calculateRecentStreak(
}

return streak;
}

// 배지 생성 (순수 함수)
function createBadges(streak: number): WritingBadge[] {
if (streak < 2) return [];

return [{
name: `연속 ${streak}일차`,
emoji: '🔥'
}];
}

0 comments on commit f91b4f8

Please sign in to comment.