Skip to content

Commit ad46e06

Browse files
authored
Merge pull request #55 from oodd-team/OD-41
게시물 상세조회 차단 유저 like, comment 제외
2 parents 17618d6 + 52f99dc commit ad46e06

File tree

3 files changed

+117
-56
lines changed

3 files changed

+117
-56
lines changed

src/post/dto/post.response.ts

+104-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { ApiProperty, OmitType } from '@nestjs/swagger';
22
import { Type } from 'class-transformer';
3+
import dayjs from 'dayjs';
4+
import { PostClothing } from 'src/common/entities/post-clothing.entity';
5+
import { PostImage } from 'src/common/entities/post-image.entity';
6+
import { Post } from 'src/common/entities/post.entity';
7+
import { User } from 'src/common/entities/user.entity';
38

49
class PostImageDto {
510
@ApiProperty({
@@ -13,6 +18,11 @@ class PostImageDto {
1318
description: '이미지의 순서 번호',
1419
})
1520
orderNum: number;
21+
22+
constructor(postImage: PostImage) {
23+
this.imageUrl = postImage.url;
24+
this.orderNum = postImage.orderNum;
25+
}
1626
}
1727

1828
class UserDto {
@@ -33,6 +43,12 @@ class UserDto {
3343
description: '사용자의 프로필 사진 URL',
3444
})
3545
profilePictureUrl: string;
46+
47+
constructor(user: User) {
48+
this.userId = user.id;
49+
this.nickname = user.nickname;
50+
this.profilePictureUrl = user.profilePictureUrl;
51+
}
3652
}
3753

3854
class PostClothingDto {
@@ -59,6 +75,14 @@ class PostClothingDto {
5975
description: '옷 상품 링크입니다.',
6076
})
6177
url: string;
78+
79+
constructor(postClothing: PostClothing) {
80+
this.imageUrl = postClothing.clothing.imageUrl;
81+
this.brandName = postClothing.clothing.brandName;
82+
this.modelName = postClothing.clothing.modelName;
83+
this.modelNumber = postClothing.clothing.modelNumber;
84+
this.url = postClothing.clothing.url;
85+
}
6286
}
6387

6488
export class PostResponse {
@@ -106,11 +130,69 @@ export class PostResponse {
106130
description: '대표 게시물 여부입니다.',
107131
})
108132
isRepresentative: boolean;
133+
134+
constructor(post: Post) {
135+
this.userId = post.user.id;
136+
this.postId = post.id;
137+
this.content = post.content;
138+
this.isRepresentative = post.isRepresentative;
139+
this.postImages =
140+
post.postImages?.map((image) => new PostImageDto(image)) || [];
141+
this.postStyletags =
142+
post.postStyletags?.map((postStyleTag) => postStyleTag.styletag.tag) ||
143+
[];
144+
this.postClothings =
145+
post.postClothings?.map((clothing) => new PostClothingDto(clothing)) ||
146+
[];
147+
}
109148
}
149+
export class PostDetailResponse {
150+
@ApiProperty({
151+
example: 1,
152+
description: '게시물 번호입니다.',
153+
})
154+
postId: number;
155+
156+
@ApiProperty({
157+
type: UserDto,
158+
description: '게시물 작성자 정보입니다.',
159+
})
160+
@Type(() => UserDto)
161+
user: UserDto;
162+
163+
@ApiProperty({
164+
example: '게시물 내용',
165+
description: '게시물 내용입니다. 최대 100자까지 입력할 수 있습니다.',
166+
})
167+
content: string;
110168

111-
class PostDetailDto extends OmitType(PostResponse, ['userId']) {}
169+
@ApiProperty({
170+
type: [PostImageDto],
171+
description: '게시물에 포함된 이미지 목록입니다.',
172+
})
173+
postImages?: PostImageDto[];
174+
175+
@ApiProperty({
176+
type: [String],
177+
example: ['classic', 'basic'],
178+
description:
179+
'게시글에 포함된 스타일 태그 목록입니다. 스타일 태그에 저장된 태그만 입력 가능합니다.',
180+
})
181+
postStyletags?: string[];
182+
183+
@ApiProperty({
184+
type: [PostClothingDto],
185+
description: '게시물에 포함된 옷 정보 리스트입니다.',
186+
})
187+
@Type(() => PostClothingDto)
188+
postClothings?: PostClothingDto[];
189+
190+
@ApiProperty({
191+
example: false,
192+
description: '대표 게시물 여부입니다.',
193+
})
194+
isRepresentative: boolean;
112195

113-
export class PostDetailResponse extends PostDetailDto {
114196
@ApiProperty({
115197
example: '2024-10-11T09:00:00.000Z',
116198
description: '생성 시각',
@@ -123,13 +205,6 @@ export class PostDetailResponse extends PostDetailDto {
123205
})
124206
updatedAt: string;
125207

126-
@ApiProperty({
127-
type: UserDto,
128-
description: '게시물 작성자 정보입니다.',
129-
})
130-
@Type(() => UserDto)
131-
user: UserDto;
132-
133208
@ApiProperty({
134209
example: 10,
135210
description: '게시글에 달린 댓글 수입니다.',
@@ -147,4 +222,24 @@ export class PostDetailResponse extends PostDetailDto {
147222
description: '현재 사용자가 게시물에 좋아요를 눌렀는지 여부',
148223
})
149224
isPostLike: boolean;
225+
constructor(post: Post, currentUserId: number) {
226+
this.postId = post.id;
227+
this.content = post.content;
228+
this.isRepresentative = post.isRepresentative;
229+
this.postImages =
230+
post.postImages?.map((image) => new PostImageDto(image)) || [];
231+
this.postStyletags =
232+
post.postStyletags?.map((postStyleTag) => postStyleTag.styletag.tag) ||
233+
[];
234+
this.postClothings =
235+
post.postClothings?.map((clothing) => new PostClothingDto(clothing)) ||
236+
[];
237+
(this.createdAt = dayjs(post.createdAt).format('YYYY-MM-DDTHH:mm:ssZ')),
238+
(this.updatedAt = dayjs(post.updatedAt).format('YYYY-MM-DDTHH:mm:ssZ')),
239+
(this.user = new UserDto(post.user));
240+
this.postCommentsCount = post.postComments?.length || 0;
241+
this.postLikesCount = post.postLikes?.length || 0;
242+
this.isPostLike =
243+
post.postLikes?.some((like) => like.user.id === currentUserId) || false;
244+
}
150245
}

src/post/post.controller.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,12 @@ export class PostController {
110110
@Req() req: Request,
111111
): Promise<BaseResponse<PostDetailResponse>> {
112112
const post = await this.postService.getPost(postId, req.user.id);
113+
if (!post) {
114+
throw DataNotFoundException('해당 게시글을 찾을 수 없습니다.');
115+
}
113116

114-
return new BaseResponse(true, '게시글 조회 성공', post);
117+
const postDetailResponse = new PostDetailResponse(post, req.user.id);
118+
return new BaseResponse(true, '게시글 조회 성공', postDetailResponse);
115119
}
116120

117121
@Post()

src/post/post.service.ts

+8-46
Original file line numberDiff line numberDiff line change
@@ -390,11 +390,10 @@ export class PostService {
390390
}
391391

392392
// 게시글 상세 조회
393-
async getPost(
394-
postId: number,
395-
currentUserId: number,
396-
): Promise<PostDetailResponse> {
397-
const post = await this.postRepository
393+
async getPost(postId: number, currentUserId: number): Promise<Post | null> {
394+
const blockedUserIds =
395+
await this.userBlockService.getBlockedUserIdsByRequesterId(currentUserId);
396+
return await this.postRepository
398397
.createQueryBuilder('post')
399398
.leftJoinAndSelect(
400399
'post.postImages',
@@ -406,18 +405,20 @@ export class PostService {
406405
.leftJoinAndSelect(
407406
'post.postLikes',
408407
'postLike',
409-
'postLike.status = :likeStatus',
408+
'postLike.status = :likeStatus AND postLike.user.id NOT IN (:...blockedUserIds)',
410409
{
411410
likeStatus: 'activated',
411+
blockedUserIds: blockedUserIds.length > 0 ? blockedUserIds : [-1], // 차단된 사용자가 없으면 무효화된 조건 (-1)
412412
},
413413
)
414414
.leftJoinAndSelect('postLike.user', 'postLikeUser')
415415
.leftJoinAndSelect(
416416
'post.postComments',
417417
'postComment',
418-
'postComment.status = :commentStatus',
418+
'postComment.status = :commentStatus AND postComment.user.id NOT IN (:...blockedUserIds)',
419419
{
420420
commentStatus: 'activated',
421+
blockedUserIds: blockedUserIds.length > 0 ? blockedUserIds : [-1],
421422
},
422423
)
423424
.leftJoinAndSelect('postComment.user', 'postCommentUser')
@@ -438,45 +439,6 @@ export class PostService {
438439
.where('post.id = :postId', { postId })
439440
.andWhere('post.status = :postStatus', { postStatus: 'activated' })
440441
.getOne();
441-
442-
if (!post) {
443-
throw DataNotFoundException('해당 게시글을 찾을 수 없습니다.');
444-
}
445-
446-
return this.returnPostDetail(post, currentUserId);
447-
}
448-
449-
private returnPostDetail(
450-
post: Post,
451-
currentUserId: number,
452-
): PostDetailResponse {
453-
return {
454-
postId: post.id,
455-
user: {
456-
userId: post.user.id,
457-
nickname: post.user.nickname,
458-
profilePictureUrl: post.user.profilePictureUrl,
459-
},
460-
content: post.content,
461-
isRepresentative: post.isRepresentative,
462-
postStyletags: post.postStyletags?.map((tag) => tag.styletag.tag),
463-
postImages: post.postImages.map((image) => ({
464-
imageUrl: image.url,
465-
orderNum: image.orderNum,
466-
})),
467-
postClothings: post.postClothings.map((postClothing) => ({
468-
imageUrl: postClothing.clothing.imageUrl,
469-
brandName: postClothing.clothing.brandName,
470-
modelName: postClothing.clothing.modelName,
471-
modelNumber: postClothing.clothing.modelNumber,
472-
url: postClothing.clothing.url,
473-
})),
474-
postLikesCount: post.postLikes.length,
475-
postCommentsCount: post.postComments.length,
476-
isPostLike: this.checkIsPostLiked(post, currentUserId),
477-
createdAt: dayjs(post.createdAt).format('YYYY-MM-DDTHH:mm:ssZ'),
478-
updatedAt: dayjs(post.updatedAt).format('YYYY-MM-DDTHH:mm:ssZ'),
479-
};
480442
}
481443

482444
// 대표 게시글 설정

0 commit comments

Comments
 (0)