Skip to content

Commit 9ee243f

Browse files
committed
fix: 리더보드 비정상적인 집계값 수정
1 parent 90432cc commit 9ee243f

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

src/repositories/__test__/leaderboard.repo.test.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,20 @@ describe('LeaderboardRepository', () => {
7979
await repo.getUserLeaderboard('viewCount', mockDateRange, 10);
8080

8181
expect(mockPool.query).toHaveBeenCalledWith(
82-
expect.stringContaining('WHERE date >='), // pastDateKST를 사용하는 부분 확인
82+
expect.stringContaining('WHERE date ='), // pastDateKST를 사용하는 부분 확인
8383
[expect.any(Number)], // limit
8484
);
8585
});
8686

87+
it('데이터 수집이 비정상적인 유저는 리더보드에 포함되지 않아야 한다', async () => {
88+
await repo.getUserLeaderboard('viewCount', 30, 10);
89+
90+
expect(mockPool.query).toHaveBeenCalledWith(
91+
expect.stringContaining('HAVING SUM(COALESCE(ts.today_view, 0)) != SUM(COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, 0))'),
92+
expect.anything(),
93+
);
94+
});
95+
8796
it('에러 발생 시 DBError를 던져야 한다', async () => {
8897
mockPool.query.mockRejectedValue(new Error('DB connection failed'));
8998
await expect(repo.getUserLeaderboard('viewCount', 30, 10)).rejects.toThrow(DBError);
@@ -156,11 +165,20 @@ describe('LeaderboardRepository', () => {
156165
await repo.getPostLeaderboard('viewCount', mockDateRange, 10);
157166

158167
expect(mockPool.query).toHaveBeenCalledWith(
159-
expect.stringContaining('WHERE date >='), // pastDateKST를 사용하는 부분 확인
168+
expect.stringContaining('WHERE date ='), // pastDateKST를 사용하는 부분 확인
160169
[expect.any(Number)], // limit
161170
);
162171
});
163172

173+
it('데이터 수집이 비정상적인 게시물은 리더보드에 포함되지 않아야 한다', async () => {
174+
await repo.getPostLeaderboard('viewCount', 30, 10);
175+
176+
expect(mockPool.query).toHaveBeenCalledWith(
177+
expect.stringContaining('COALESCE(ts.today_view, 0) != COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, 0)'),
178+
expect.anything()
179+
);
180+
});
181+
164182
it('에러 발생 시 DBError를 던져야 한다', async () => {
165183
mockPool.query.mockRejectedValue(new Error('DB connection failed'));
166184
await expect(repo.getPostLeaderboard('viewCount', 30, 10)).rejects.toThrow(DBError);

src/repositories/leaderboard.repository.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ export class LeaderboardRepository {
2121
COALESCE(SUM(ts.today_view), 0) AS total_views,
2222
COALESCE(SUM(ts.today_like), 0) AS total_likes,
2323
COUNT(DISTINCT CASE WHEN p.is_active = true THEN p.id END) AS total_posts,
24-
SUM(COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, COALESCE(ts.today_view, 0))) AS view_diff,
25-
SUM(COALESCE(ts.today_like, 0) - COALESCE(ss.start_like, COALESCE(ts.today_like, 0))) AS like_diff,
24+
SUM(COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, 0)) AS view_diff,
25+
SUM(COALESCE(ts.today_like, 0) - COALESCE(ss.start_like, 0)) AS like_diff,
2626
COUNT(DISTINCT CASE WHEN p.released_at >= '${pastDateKST}' AND p.is_active = true THEN p.id END) AS post_diff
2727
FROM users_user u
2828
LEFT JOIN posts_post p ON p.user_id = u.id
2929
LEFT JOIN today_stats ts ON ts.post_id = p.id
3030
LEFT JOIN start_stats ss ON ss.post_id = p.id
3131
WHERE u.username IS NOT NULL
3232
GROUP BY u.id, u.email, u.username
33+
HAVING SUM(COALESCE(ts.today_view, 0)) != SUM(COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, 0))
3334
ORDER BY ${this.SORT_COL_MAPPING[sort]} DESC, u.id
3435
LIMIT $1;
3536
`;
@@ -44,6 +45,7 @@ export class LeaderboardRepository {
4445

4546
async getPostLeaderboard(sort: PostLeaderboardSortType, dateRange: number, limit: number) {
4647
try {
48+
const pastDateKST = getKSTDateStringWithOffset(-dateRange * 24 * 60);
4749
const cteQuery = this.buildLeaderboardCteQuery(dateRange);
4850

4951
const query = `
@@ -56,13 +58,18 @@ export class LeaderboardRepository {
5658
u.username AS username,
5759
COALESCE(ts.today_view, 0) AS total_views,
5860
COALESCE(ts.today_like, 0) AS total_likes,
59-
COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, COALESCE(ts.today_view, 0)) AS view_diff,
60-
COALESCE(ts.today_like, 0) - COALESCE(ss.start_like, COALESCE(ts.today_like, 0)) AS like_diff
61+
COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, 0) AS view_diff,
62+
COALESCE(ts.today_like, 0) - COALESCE(ss.start_like, 0) AS like_diff
6163
FROM posts_post p
6264
LEFT JOIN users_user u ON u.id = p.user_id
6365
LEFT JOIN today_stats ts ON ts.post_id = p.id
6466
LEFT JOIN start_stats ss ON ss.post_id = p.id
6567
WHERE p.is_active = true
68+
AND (
69+
p.released_at >= '${pastDateKST}'
70+
OR
71+
COALESCE(ts.today_view, 0) != COALESCE(ts.today_view, 0) - COALESCE(ss.start_view, 0)
72+
)
6673
ORDER BY ${this.SORT_COL_MAPPING[sort]} DESC, p.id
6774
LIMIT $1;
6875
`;
@@ -89,17 +96,15 @@ export class LeaderboardRepository {
8996
daily_view_count AS today_view,
9097
daily_like_count AS today_like
9198
FROM posts_postdailystatistics
92-
WHERE date <= '${nowDateKST}'
93-
ORDER BY post_id, date DESC
99+
WHERE date = '${nowDateKST}'
94100
),
95101
start_stats AS (
96102
SELECT DISTINCT ON (post_id)
97103
post_id,
98104
daily_view_count AS start_view,
99105
daily_like_count AS start_like
100106
FROM posts_postdailystatistics
101-
WHERE date >= '${pastDateKST}'
102-
ORDER BY post_id, date ASC
107+
WHERE date = '${pastDateKST}'
103108
)
104109
`;
105110
}

0 commit comments

Comments
 (0)