Skip to content

Commit 72cdb9d

Browse files
committed
feature: Leaderboard 도메인 파일 세팅 및 타입 추가
1 parent 5f14e86 commit 72cdb9d

File tree

7 files changed

+300
-0
lines changed

7 files changed

+300
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { LeaderboardService } from '@/services/leaderboard.service';
2+
import { NextFunction, RequestHandler, Request, Response } from 'express';
3+
4+
export class LeaderboardController {
5+
constructor(private leaderboardService: LeaderboardService) {}
6+
7+
getLeaderboard: RequestHandler = async (req: Request, res: Response, next: NextFunction) => {
8+
try {
9+
res.status(200).json({ message: 'ok' });
10+
} catch (error) {
11+
next(error);
12+
}
13+
};
14+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Pool } from 'pg';
2+
3+
export class LeaderboardRepository {
4+
constructor(private pool: Pool) {}
5+
}

src/routes/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import express, { Router } from 'express';
22
import UserRouter from './user.router';
33
import PostRouter from './post.router';
44
import NotiRouter from './noti.router';
5+
import LeaderboardRouter from './leaderboard.router';
56

67
const router: Router = express.Router();
78

@@ -12,4 +13,5 @@ router.use('/ping', (req, res) => {
1213
router.use('/', UserRouter);
1314
router.use('/', PostRouter);
1415
router.use('/', NotiRouter);
16+
router.use('/', LeaderboardRouter);
1517
export default router;

src/routes/leaderboard.router.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import express, { Router } from 'express';
2+
import { LeaderboardRepository } from '@/repositories/leaderboard.repository';
3+
import pool from '@/configs/db.config';
4+
import { LeaderboardService } from '@/services/leaderboard.service';
5+
import { LeaderboardController } from '@/controllers/leaderboard.controller';
6+
import { validateRequestDto } from '@/middlewares/validation.middleware';
7+
import { GetLeaderboardQueryDto } from '@/types/dto/requests/getLeaderboardQuery.type';
8+
9+
const router: Router = express.Router();
10+
11+
const leaderboardRepository = new LeaderboardRepository(pool);
12+
const leaderboardService = new LeaderboardService(leaderboardRepository);
13+
const leaderboardController = new LeaderboardController(leaderboardService);
14+
15+
/**
16+
* @swagger
17+
* /leaderboard:
18+
* get:
19+
* summary: 리더보드 조회
20+
* tags:
21+
* - Leaderboard
22+
* parameters:
23+
* - in: query
24+
* name: type
25+
* schema:
26+
* $ref: '#/components/schemas/GetLeaderboardQueryDto/properties/type'
27+
* - in: query
28+
* name: sort
29+
* schema:
30+
* $ref: '#/components/schemas/GetLeaderboardQueryDto/properties/sort'
31+
* - in: query
32+
* name: dateRange
33+
* schema:
34+
* $ref: '#/components/schemas/GetLeaderboardQueryDto/properties/dateRange'
35+
* - in: query
36+
* name: limit
37+
* schema:
38+
* $ref: '#/components/schemas/GetLeaderboardQueryDto/properties/limit'
39+
* responses:
40+
* '200':
41+
* description: 리더보드 조회 성공
42+
* content:
43+
* application/json:
44+
* schema:
45+
* $ref: '#/components/schemas/LeaderboardResponseDto'
46+
* '500':
47+
* description: 서버 오류 / 데이터 베이스 조회 오류
48+
*/
49+
router.get('/leaderboard', validateRequestDto(GetLeaderboardQueryDto, 'query'), leaderboardController.getLeaderboard);
50+
51+
export default router;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { LeaderboardRepository } from '@/repositories/leaderboard.repository';
2+
3+
export class LeaderboardService {
4+
constructor(private leaderboardRepo: LeaderboardRepository) {}
5+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { Transform } from 'class-transformer';
2+
import { IsNumber, IsOptional, IsString, Max, Min } from 'class-validator';
3+
4+
/**
5+
* @swagger
6+
* components:
7+
* schemas:
8+
* LeaderboardType:
9+
* type: string
10+
* description: 리더보드 조회 타입
11+
* nullable: true
12+
* enum: ['user', 'post']
13+
* default: 'user'
14+
*/
15+
export type LeaderboardType = 'user' | 'post';
16+
17+
/**
18+
* @swagger
19+
* components:
20+
* schemas:
21+
* LeaderboardSortType:
22+
* type: string
23+
* description: 리더보드 정렬 기준
24+
* nullable: true
25+
* enum: ['viewCount', 'likeCount', 'postCount']
26+
* default: 'viewCount'
27+
*/
28+
export type LeaderboardSortType = 'viewCount' | 'likeCount' | 'postCount';
29+
30+
export interface GetLeaderboardQuery {
31+
type?: LeaderboardType;
32+
sort?: LeaderboardSortType;
33+
dateRange?: number;
34+
limit?: number;
35+
}
36+
37+
/**
38+
* @swagger
39+
* components:
40+
* schemas:
41+
* GetLeaderboardQueryDto:
42+
* type: object
43+
* properties:
44+
* type:
45+
* $ref: '#/components/schemas/LeaderboardType'
46+
* description: 리더보드 조회 타입
47+
* nullable: true
48+
* default: 'user'
49+
* sort:
50+
* $ref: '#/components/schemas/LeaderboardSortType'
51+
* description: 리더보드 정렬 기준
52+
* nullable: true
53+
* default: 'viewCount'
54+
* dateRange:
55+
* type: number
56+
* description: 리더보드 조회 기간 (일수)
57+
* nullable: true
58+
* default: 30
59+
* minimum: 1
60+
* maximum: 30
61+
* limit:
62+
* type: number
63+
* description: 리더보드 조회 제한 수
64+
* nullable: true
65+
* default: 10
66+
* minimum: 1
67+
* maximum: 30
68+
*/
69+
export class GetLeaderboardQueryDto {
70+
@IsOptional()
71+
@IsString()
72+
type?: LeaderboardType;
73+
74+
@IsOptional()
75+
@IsString()
76+
sort?: LeaderboardSortType;
77+
78+
@IsOptional()
79+
@IsNumber()
80+
@Transform(({ value }) => Number(value))
81+
@Min(1)
82+
@Max(30)
83+
dateRange?: number;
84+
85+
@IsOptional()
86+
@IsNumber()
87+
@Transform(({ value }) => Number(value))
88+
@Min(1)
89+
@Max(30)
90+
limit?: number;
91+
92+
constructor(type?: LeaderboardType, sort?: LeaderboardSortType, dateRange?: number, limit?: number) {
93+
this.type = type;
94+
this.sort = sort;
95+
this.dateRange = dateRange;
96+
this.limit = limit;
97+
}
98+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { BaseResponseDto } from '@/types/dto/responses/baseResponse.type';
2+
3+
/**
4+
* @swagger
5+
* components:
6+
* schemas:
7+
* LeaderboardUserType:
8+
* type: object
9+
* properties:
10+
* id:
11+
* type: integer
12+
* description: 사용자 ID
13+
* email:
14+
* type: string
15+
* description: 사용자 이메일
16+
* totalViews:
17+
* type: integer
18+
* description: 누적 조회수
19+
* totalLikes:
20+
* type: integer
21+
* description: 누적 좋아요 수
22+
* totalPosts:
23+
* type: integer
24+
* description: 누적 게시물 수
25+
* viewDiff:
26+
* type: integer
27+
* description: 구간 조회수 상승값
28+
* likeDiff:
29+
* type: integer
30+
* description: 구간 좋아요 수 상승값
31+
* postDiff:
32+
* type: integer
33+
* description: 구간 게시물 수 상승값
34+
*/
35+
export interface LeaderboardUserType {
36+
id: number;
37+
email: string;
38+
totalViews: number;
39+
totalLikes: number;
40+
totalPosts: number;
41+
viewDiff: number;
42+
likeDiff: number;
43+
postDiff: number;
44+
}
45+
46+
/**
47+
* @swagger
48+
* components:
49+
* schemas:
50+
* LeaderboardPostType:
51+
* type: object
52+
* properties:
53+
* id:
54+
* type: integer
55+
* description: 게시물 ID
56+
* title:
57+
* type: string
58+
* description: 게시물 제목
59+
* slug:
60+
* type: string
61+
* description: 게시물 url slug 값
62+
* totalViews:
63+
* type: integer
64+
* description: 누적 조회수
65+
* totalLikes:
66+
* type: integer
67+
* description: 누적 좋아요 수
68+
* viewDiff:
69+
* type: integer
70+
* description: 구간 조회수 상승값
71+
* likeDiff:
72+
* type: integer
73+
* description: 구간 좋아요 수 상승값
74+
* releasedAt:
75+
* type: string
76+
* format: date-time
77+
* description: 게시물 업로드 일시
78+
*/
79+
export interface LeaderboardPostType {
80+
id: number;
81+
title: string;
82+
slug: string;
83+
totalViews: number;
84+
totalLikes: number;
85+
viewDiff: number;
86+
likeDiff: number;
87+
releasedAt: string;
88+
}
89+
90+
/**
91+
* @swagger
92+
* components:
93+
* schemas:
94+
* LeaderboardResponseData:
95+
* type: object
96+
* properties:
97+
* posts:
98+
* type: array
99+
* nullable: true
100+
* items:
101+
* $ref: '#/components/schemas/LeaderboardPostType'
102+
* users:
103+
* type: array
104+
* nullable: true
105+
* items:
106+
* $ref: '#/components/schemas/LeaderboardUserType'
107+
*/
108+
export interface LeaderboardResponseData {
109+
users: LeaderboardUserType[] | null;
110+
posts: LeaderboardPostType[] | null;
111+
}
112+
113+
/**
114+
* @swagger
115+
* components:
116+
* schemas:
117+
* LeaderboardResponseDto:
118+
* allOf:
119+
* - $ref: '#/components/schemas/BaseResponseDto'
120+
* - type: object
121+
* properties:
122+
* data:
123+
* $ref: '#/components/schemas/LeaderboardResponseData'
124+
*/
125+
export class LeaderboardResponseDto extends BaseResponseDto<LeaderboardResponseData> {}

0 commit comments

Comments
 (0)