From 468e9fa508fd5a86bc53d306ee4a311739e785f9 Mon Sep 17 00:00:00 2001 From: YeongWoooo Date: Fri, 29 Sep 2023 03:06:10 +0900 Subject: [PATCH] =?UTF-8?q?[#56]=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C/=EC=88=98=EC=A0=95=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post-v1-delete-post-param.dto.ts | 12 +++ .../post-v1-update-post-body.dto.ts | 37 +++++++++ .../post-v1-update-post-param.dto.ts | 12 +++ .../post-v1-update-post-response.dto.ts | 10 +++ server/src/post/v1/post-v1.controller.ts | 47 +++++++++++ server/src/post/v1/post-v1.service.ts | 81 +++++++++++++++++++ 6 files changed, 199 insertions(+) create mode 100644 server/src/post/v1/dto/delete-meeting-post/post-v1-delete-post-param.dto.ts create mode 100644 server/src/post/v1/dto/update-meeting-post/post-v1-update-post-body.dto.ts create mode 100644 server/src/post/v1/dto/update-meeting-post/post-v1-update-post-param.dto.ts create mode 100644 server/src/post/v1/dto/update-meeting-post/post-v1-update-post-response.dto.ts diff --git a/server/src/post/v1/dto/delete-meeting-post/post-v1-delete-post-param.dto.ts b/server/src/post/v1/dto/delete-meeting-post/post-v1-delete-post-param.dto.ts new file mode 100644 index 00000000..8fb3fb7b --- /dev/null +++ b/server/src/post/v1/dto/delete-meeting-post/post-v1-delete-post-param.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsNumber } from 'class-validator'; + +export class PostV1DeletePostParamDto { + @ApiProperty({ + description: '게시글 ID', + example: 1, + }) + @IsNotEmpty() + @IsNumber() + postId: number; +} diff --git a/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-body.dto.ts b/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-body.dto.ts new file mode 100644 index 00000000..00105462 --- /dev/null +++ b/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-body.dto.ts @@ -0,0 +1,37 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNotEmpty } from 'class-validator'; + +/** + * 게시물 수정 body Dto + * @author @rdd9223 + */ +export class PostV1UpdatePostBodyDto { + @ApiProperty({ + example: '알고보면 쓸데있는 개발 프로세스', + description: '모임 제목', + required: true, + }) + @IsNotEmpty() + @IsString() + title: string; + + @ApiProperty({ + example: + 'https://makers-web-img.s3.ap-northeast-2.amazonaws.com/meeting/2023/04/12/7bd87736-b557-4b26-a0d5-9b09f1f1d7df', + description: '게시글 이미지 리스트', + required: true, + isArray: true, + }) + @IsNotEmpty() + @IsString({ each: true }) + images: string[]; + + @ApiProperty({ + example: 'api 가 터졌다고? 깃이 터졌다고?', + description: '게시글 내용', + required: true, + }) + @IsNotEmpty() + @IsString() + contents: string; +} diff --git a/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-param.dto.ts b/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-param.dto.ts new file mode 100644 index 00000000..b6e56589 --- /dev/null +++ b/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-param.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsNumber } from 'class-validator'; + +export class PostV1UpdatePostParamDto { + @ApiProperty({ + description: '게시글 ID', + example: 1, + }) + @IsNotEmpty() + @IsNumber() + postId: number; +} diff --git a/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-response.dto.ts b/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-response.dto.ts new file mode 100644 index 00000000..3b582222 --- /dev/null +++ b/server/src/post/v1/dto/update-meeting-post/post-v1-update-post-response.dto.ts @@ -0,0 +1,10 @@ +import { PickType } from '@nestjs/swagger'; +import { Post } from 'src/entity/post/post.entity'; + +export class PostV1UpdatePostResponseDto extends PickType(Post, [ + 'id', + 'title', + 'contents', + 'updatedDate', + 'images', +]) {} diff --git a/server/src/post/v1/post-v1.controller.ts b/server/src/post/v1/post-v1.controller.ts index 59f8d3d5..8c1b6158 100644 --- a/server/src/post/v1/post-v1.controller.ts +++ b/server/src/post/v1/post-v1.controller.ts @@ -1,11 +1,13 @@ import { Body, Controller, + Delete, Get, HttpStatus, Param, ParseIntPipe, Post, + Put, Query, UseGuards, } from '@nestjs/common'; @@ -33,6 +35,10 @@ import { PostV1ReportPostResponseDto } from './dto/report-post/post-v1-report-po import { PostV1SwitchPostLikeParamDto } from './dto/switch-post-like/post-v1-switch-post-like-param.dto'; import { PostV1SwitchPostLikeResponseDto } from './dto/switch-post-like/post-v1-switch-post-like-response.dto'; import { ApiOkResponseCommon } from 'src/common/decorator/api-ok-response-common.decorator'; +import { PostV1DeletePostParamDto } from './dto/delete-meeting-post/post-v1-delete-post-param.dto'; +import { PostV1UpdatePostResponseDto } from './dto/update-meeting-post/post-v1-update-post-response.dto'; +import { PostV1UpdatePostParamDto } from './dto/update-meeting-post/post-v1-update-post-param.dto'; +import { PostV1UpdatePostBodyDto } from './dto/update-meeting-post/post-v1-update-post-body.dto'; @ApiTags('게시글') @Controller('post/v1') @@ -144,4 +150,45 @@ export class PostV1Controller { ): Promise { return this.postV1Service.reportPost({ param, user }); } + + @ApiOperation({ + summary: '모임 게시글 수정', + }) + @ApiOkResponseCommon(PostV1UpdatePostResponseDto) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: + '"게시글이 없습니다." or "권한이 없습니다." or "이미지는 최대 10개까지만 업로드 가능합니다."', + schema: { $ref: getSchemaPath(BaseExceptionDto) }, + }) + @ApiBearerAuth() + @UseGuards(AuthGuard('jwt')) + @Put('/:postId') + async updatePost( + @Param() param: PostV1UpdatePostParamDto, + @Body() body: PostV1UpdatePostBodyDto, + @GetUser() user: User, + ): Promise { + return this.postV1Service.updatePost({ + postId: param.postId, + body, + userId: user.id, + }); + } + + @ApiOperation({ + summary: '모임 게시글 삭제', + }) + @ApiBearerAuth() + @UseGuards(AuthGuard('jwt')) + @Delete('/:postId') + async deletePost( + @Param() param: PostV1DeletePostParamDto, + @GetUser() user: User, + ): Promise { + return this.postV1Service.deletePost({ + userId: user.id, + postId: param.postId, + }); + } } diff --git a/server/src/post/v1/post-v1.service.ts b/server/src/post/v1/post-v1.service.ts index 3bc870df..f872bc70 100644 --- a/server/src/post/v1/post-v1.service.ts +++ b/server/src/post/v1/post-v1.service.ts @@ -24,6 +24,7 @@ import { PostV1ReportPostParamDto } from './dto/report-post/post-v1-report-post- import { PostV1ReportPostResponseDto } from './dto/report-post/post-v1-report-post-response.dto'; import { PostV1GetPostCountQueryDto } from './dto/get-meeting-post-count/post-v1-get-post-count-query.dto'; import { PostV1GetPostCountResponseDto } from './dto/get-meeting-post-count/post-v1-get-post-count-response.dto'; +import { PostV1UpdatePostBodyDto } from './dto/update-meeting-post/post-v1-update-post-body.dto'; @Injectable() export class PostV1Service { @@ -314,4 +315,84 @@ export class PostV1Service { postCount, }; } + + /** + * 모임 게시글 수정 + */ + async updatePost({ + userId, + postId, + body, + }: { + userId: number; + postId: number; + body: PostV1UpdatePostBodyDto; + }) { + const post = await this.postRepository.findOne({ + where: { id: postId }, + }); + console.log(post); + const isValidPost = post !== null; + if (!isValidPost) { + throw new BadRequestException('게시글이 없습니다.'); + } + + const isPostOwner = post.userId === userId; + if (!isPostOwner) { + throw new ForbiddenException('권한이 없습니다.'); + } + + const isImageCountInvalid = body.images.length > 10; + if (isImageCountInvalid) { + throw new BadRequestException( + '이미지는 최대 10개까지만 업로드 가능합니다.', + ); + } + + await this.postRepository.update( + { id: postId }, + { + title: body.title, + contents: body.contents, + images: body.images, + }, + ); + + const updatedPost = await this.postRepository.findOne({ + where: { id: postId }, + }); + + return { + id: updatedPost.id, + title: updatedPost.title, + contents: updatedPost.contents, + updatedDate: updatedPost.updatedDate, + images: updatedPost.images, + }; + } + + /** + * 모임 게시글 삭제 + */ + async deletePost({ + userId, + postId, + }: { + userId: number; + postId: number; + }): Promise { + const post = await this.postRepository.findOne({ + where: { id: postId }, + }); + + if (post === null) { + throw new BadRequestException('게시글이 없습니다.'); + } + + if (post.userId !== userId) { + throw new ForbiddenException('권한이 없습니다.'); + } + + await this.postRepository.delete({ id: postId }); + } }