Skip to content

Commit c431e5b

Browse files
authored
[25.01.08 / TASK-97] Feature - get post by UUID (#14)
* feature: uuid로 post 단건 조회 * feature: uuid로 post 단건 조회 v2 * hotfix: uuid 조회, id 조회 api 분리 * hotfix: 오탈자 수정 * modify: NotFoundError 위치 수정 * feature: 헬스체크 전용 api * modify: date type 수정
1 parent 0256c24 commit c431e5b

File tree

11 files changed

+109
-15
lines changed

11 files changed

+109
-15
lines changed

src/app.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import cors from 'cors';
55
import cookieParser from 'cookie-parser';
66
import router from './routes';
77
import { errorHandlingMiddleware } from './middlewares/errorHandling.middleware';
8+
import { NotFoundError } from './exception';
89

910
dotenv.config();
1011

@@ -21,9 +22,10 @@ app.use(
2122
credentials: true,
2223
}),
2324
);
25+
2426
app.use('/api', router);
25-
app.get('/', (req, res) => {
26-
res.send('Hello, V.D.!');
27+
app.use((req) => {
28+
throw new NotFoundError(`${req.url} not found`);
2729
});
2830
app.use(errorHandlingMiddleware);
2931

src/controllers/post.controller.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class PostController {
6363
}
6464
};
6565

66-
getPost: RequestHandler = async (
66+
getPostByPostId: RequestHandler<PostParam> = async (
6767
req: Request<PostParam, object, object, GetPostQuery>,
6868
res: Response<PostResponseDto>,
6969
next: NextFunction,
@@ -72,7 +72,7 @@ export class PostController {
7272
const postId = Number(req.params.postId);
7373
const { start, end } = req.query;
7474

75-
const post = await this.postService.getPost(postId, start, end);
75+
const post = await this.postService.getPostByPostId(postId, start, end);
7676

7777
const response = new PostResponseDto(true, '단건 post 조회에 성공하였습니다.', { post }, null);
7878

@@ -82,4 +82,23 @@ export class PostController {
8282
next(error);
8383
}
8484
};
85+
86+
getPostByUUID: RequestHandler<PostParam> = async (
87+
req: Request<PostParam>,
88+
res: Response<PostResponseDto>,
89+
next: NextFunction,
90+
) => {
91+
try {
92+
const postId = req.params.postId;
93+
94+
const post = await this.postService.getPostByPostUUID(postId);
95+
96+
const response = new PostResponseDto(true, 'uuid로 post 조회에 성공하였습니다.', { post }, null);
97+
98+
res.status(200).json(response);
99+
} catch (error) {
100+
logger.error('단건 조회 실패 : ', error);
101+
next(error);
102+
}
103+
};
85104
}

src/exception/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export { DBError } from './db.exception';
33
export { TokenError, TokenExpiredError, InvalidTokenError } from './token.exception';
44
export { UnauthorizedError } from './unauthorized.exception';
55
export { BadRequestError } from './badRequest.exception';
6+
export { NotFoundError } from './notFound.exception';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { CustomError } from './custom.exception';
2+
3+
export class NotFoundError extends CustomError {
4+
constructor(message: string) {
5+
super(message, 'NOT_FOUND_ERROR', 404);
6+
}
7+
}

src/repositories/post.repository.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,29 @@ export class PostRepository {
185185
throw new DBError('단건 post 조회 중 문제가 발생했습니다.');
186186
}
187187
}
188+
189+
async findPostByPostUUID(postId: string, start: string, end: string) {
190+
try {
191+
const query = `
192+
SELECT
193+
(pds.date AT TIME ZONE 'Asia/Seoul') AT TIME ZONE 'UTC' AS date,
194+
pds.daily_view_count,
195+
pds.daily_like_count
196+
FROM posts_post p
197+
JOIN posts_postdailystatistics pds ON p.id = pds.post_id
198+
WHERE p.post_uuid = $1
199+
AND (pds.date AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date >= ($2 AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date
200+
AND (pds.date AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date <= ($3 AT TIME ZONE 'Asia/Seoul' AT TIME ZONE 'UTC')::date
201+
ORDER BY pds.date ASC
202+
`;
203+
204+
const values = [postId, start, end];
205+
206+
const result = await this.pool.query(query, values);
207+
return result.rows;
208+
} catch (error) {
209+
logger.error('Post Repo findPostByUUID error : ', error);
210+
throw new DBError('통계 데이터 조회 중 문제가 발생했습니다.');
211+
}
212+
}
188213
}

src/routes/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import PostRouter from './post.router';
55

66
const router: Router = express.Router();
77

8+
router.use('/ping', (req, res) => {
9+
res.send('pong');
10+
});
11+
812
router.use('/', UserRouter);
913
router.use('/', TrackingRouter);
1014
router.use('/', PostRouter);

src/routes/post.router.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ router.get(
2222
postController.getAllPost,
2323
);
2424
router.get('/posts-stats', authMiddleware.verify, postController.getAllPostStatistics);
25-
router.get('/post/:postId', authMiddleware.verify, postController.getPost);
25+
router.get('/post/velog/:postId', authMiddleware.verify, postController.getPostByUUID);
26+
router.get('/post/:postId', authMiddleware.verify, postController.getPostByPostId);
2627

2728
export default router;

src/services/post.service.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logger from '@/configs/logger.config';
22
import { PostRepository } from '@/repositories/post.repository';
3+
import { RawPostType } from '@/types';
34

45
export class PostService {
56
constructor(private postRepo: PostRepository) {}
@@ -52,20 +53,44 @@ export class PostService {
5253
return await this.postRepo.getTotalPostCounts(id);
5354
}
5455

55-
async getPost(postId: number, start?: string, end?: string) {
56+
async getPostByPostId(postId: number, start?: string, end?: string) {
5657
try {
5758
const posts = await this.postRepo.findPostByPostId(postId, start, end);
5859

59-
const transformedPosts = posts.map((post) => ({
60-
date: post.date,
61-
dailyViewCount: parseInt(post.daily_view_count),
62-
dailyLikeCount: parseInt(post.daily_like_count),
63-
}));
60+
const transformedPosts = this.transformPosts(posts);
61+
62+
return transformedPosts;
63+
} catch (error) {
64+
logger.error('PostService getPost error : ', error);
65+
throw error;
66+
}
67+
}
68+
69+
async getPostByPostUUID(postId: string) {
70+
try {
71+
const seoulNow = new Date(new Date().getTime() + 9 * 60 * 60 * 1000);
72+
const sevenDaysAgo = new Date(seoulNow);
73+
74+
const end = seoulNow.toISOString().split('T')[0];
75+
sevenDaysAgo.setDate(seoulNow.getDate() - 6);
76+
const start = sevenDaysAgo.toISOString().split('T')[0];
77+
78+
const posts = await this.postRepo.findPostByPostUUID(postId, start, end);
79+
80+
const transformedPosts = this.transformPosts(posts);
6481

6582
return transformedPosts;
6683
} catch (error) {
67-
logger.error('PostService getTotalPostCounts error : ', error);
84+
logger.error('PostService getPostByPostUUID error : ', error);
6885
throw error;
6986
}
7087
}
88+
89+
private transformPosts(posts: RawPostType[]) {
90+
return posts.map((post) => ({
91+
date: post.date,
92+
dailyViewCount: post.daily_view_count,
93+
dailyLikeCount: post.daily_like_count,
94+
}));
95+
}
7196
}

src/types/dto/requests/getPostQuery.type.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Type } from 'class-transformer';
22
import { IsDate, IsOptional } from 'class-validator';
33

44
export interface PostParam {
5-
postId?: string;
5+
postId: string;
66
}
77

88
export interface GetPostQuery {

src/types/dto/responses/postResponse.type.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ export class PostsResponseDto extends BaseResponseDto<PostsResponseData> {}
2121

2222
// ------ 단건 조회 ------
2323
interface GetPostType {
24-
date: string;
24+
date: Date;
2525
dailyViewCount: number;
2626
dailyLikeCount: number;
2727
}
2828

29+
export interface RawPostType {
30+
date: Date;
31+
daily_view_count: number;
32+
daily_like_count: number;
33+
}
2934
interface PostResponseData {
3035
post: GetPostType[];
3136
}

0 commit comments

Comments
 (0)