From d50af2f2313eedfa0c8589dbaeac17be2e40abd1 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Tue, 7 Feb 2023 14:48:12 +0900 Subject: [PATCH 01/23] =?UTF-8?q?Refactor(minho/report):=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4/=ED=95=84=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0=EB=A7=81=20#194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/enquiries/enquiries.controller.ts | 2 +- .../src/reports/dto/report-filter.dto.ts | 28 ++++++++++ .../reports/interface/reports.interface.ts | 12 +---- .../src/reports/reports.controller.ts | 32 ++++++++--- main-project/src/reports/reports.service.ts | 30 +++++++---- .../repository/report-board.repository.ts | 2 +- .../repository/report-user.repository.ts | 2 +- .../reports/repository/reports.repository.ts | 43 +++++++++++---- .../get-reports.decorator.ts | 54 +++++++++++++++++++ 9 files changed, 164 insertions(+), 41 deletions(-) create mode 100644 main-project/src/reports/dto/report-filter.dto.ts create mode 100644 main-project/src/reports/swagger-decorator/get-reports.decorator.ts diff --git a/main-project/src/enquiries/enquiries.controller.ts b/main-project/src/enquiries/enquiries.controller.ts index db924ea9..d9301031 100644 --- a/main-project/src/enquiries/enquiries.controller.ts +++ b/main-project/src/enquiries/enquiries.controller.ts @@ -13,7 +13,7 @@ import { UseInterceptors, } from '@nestjs/common'; import { FilesInterceptor } from '@nestjs/platform-express'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { GetUser } from 'src/common/decorator/get-user.decorator'; import { TransactionDecorator } from 'src/common/decorator/transaction-manager.decorator'; import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; diff --git a/main-project/src/reports/dto/report-filter.dto.ts b/main-project/src/reports/dto/report-filter.dto.ts new file mode 100644 index 00000000..14ca30ff --- /dev/null +++ b/main-project/src/reports/dto/report-filter.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsNumber, IsOptional, Max, Min } from 'class-validator'; + +export class ReportFilterDto { + @ApiProperty({ + example: '23', + description: '페이지 번호', + required: false, + }) + @IsNumber() + @Min(1) + @Type(() => Number) + @IsOptional() + page: number; + + @ApiProperty({ + example: '1', + description: '유저 / 게시글 신고내역 구별 (0: 게시글, 1:유저)', + required: false, + }) + @IsNumber() + @Min(0) + @Max(1) + @Type(() => Number) + @IsOptional() + type: number; +} diff --git a/main-project/src/reports/interface/reports.interface.ts b/main-project/src/reports/interface/reports.interface.ts index eb825837..6b713c92 100644 --- a/main-project/src/reports/interface/reports.interface.ts +++ b/main-project/src/reports/interface/reports.interface.ts @@ -1,17 +1,9 @@ -import { OmitType } from '@nestjs/swagger'; -import { Reports } from '../entity/reports.entity'; - -export interface ReportDetail { - reportNo: number; - targetBoardNo?: number; - targetUserNo?: number; -} - -export interface Report { +export interface Report { no: number; title: string; description: string; userNo: number; targetBoardNo?: number; targetUserNo?: number; + imageUrls: T; } diff --git a/main-project/src/reports/reports.controller.ts b/main-project/src/reports/reports.controller.ts index bab2ca10..c84aae70 100644 --- a/main-project/src/reports/reports.controller.ts +++ b/main-project/src/reports/reports.controller.ts @@ -7,12 +7,21 @@ import { ParseIntPipe, Patch, Post, + Query, + UseGuards, + UseInterceptors, } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { TransactionDecorator } from 'src/common/decorator/transaction-manager.decorator'; +import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; +import { TransactionInterceptor } from 'src/common/interceptor/transaction-interceptor'; +import { EntityManager } from 'typeorm'; import { CreateReportDto } from './dto/create-reports.dto'; +import { ReportFilterDto } from './dto/report-filter.dto'; import { UpdateReportDto } from './dto/update-reports.dto'; import { Report } from './interface/reports.interface'; import { ReportsService } from './reports.service'; +import { ApiGetReports } from './swagger-decorator/get-reports.decorator'; @Controller('reports') @ApiTags('신고 API') @@ -20,14 +29,19 @@ export class ReportsController { constructor(private reportsService: ReportsService) {} // Get @Get() - @ApiOperation({ - summary: '신고 전체 조회 API', - description: '신고내역을 전부 조회한다.', - }) - async getAllReports(): Promise { - const response: Report[] = await this.reportsService.getAllReports(); + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) + @ApiGetReports() + async getReports( + @TransactionDecorator() manager: EntityManager, + @Query() reportFilterDto: ReportFilterDto, + ): Promise { + const reports: Report[] = await this.reportsService.getReports( + manager, + reportFilterDto, + ); - return { response }; + return { msg: '신고내역 전체/필터 조회 성공', response: { reports } }; } @Get('/:reportNo') @@ -38,7 +52,9 @@ export class ReportsController { async getReportByNo( @Param('reportNo', ParseIntPipe) reportNo: number, ): Promise { - const response: Report = await this.reportsService.getReportByNo(reportNo); + const response: Report = await this.reportsService.getReportByNo( + reportNo, + ); return { response }; } diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index 7da26804..735563e1 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -7,7 +7,7 @@ import { import { InjectRepository } from '@nestjs/typeorm'; import { Board } from 'src/boards/interface/boards.interface'; import { BoardsRepository } from 'src/boards/repository/board.repository'; -import { Connection, QueryRunner } from 'typeorm'; +import { Connection, EntityManager, QueryRunner } from 'typeorm'; import { CreateReportDto } from './dto/create-reports.dto'; import { UpdateReportDto } from './dto/update-reports.dto'; import { Report } from './interface/reports.interface'; @@ -15,6 +15,7 @@ import { ReportBoardRepository } from './repository/report-board.repository'; import { ReportRepository } from './repository/reports.repository'; import { ReportUserRepository } from './repository/report-user.repository'; import { ResultSetHeader } from 'mysql2'; +import { ReportFilterDto } from './dto/report-filter.dto'; @Injectable() export class ReportsService { @@ -34,20 +35,25 @@ export class ReportsService { private readonly connection: Connection, ) {} // 조회 관련 - async getAllReports(): Promise { - const reports: Report[] = await this.reportRepository.getAllReports(); + async getReports( + manager: EntityManager, + reportFilterDto: ReportFilterDto, + ): Promise[]> { + const reports: Report[] = await manager + .getCustomRepository(ReportRepository) + .getReports(reportFilterDto); - if (!reports) { + if (!reports.length) { throw new NotFoundException( - `신고내역 전체 조회(getAllReports): 알 수 없는 서버 에러입니다.`, + `신고내역 전체 조회(getReports): 알 수 없는 서버 에러입니다.`, ); } return reports; } - async getAllBoardReports(): Promise { - const reportedBoards: Report[] = + async getAllBoardReports(): Promise[]> { + const reportedBoards: Report[] = await this.boardReportRepository.getAllBoardReports(); if (!reportedBoards) { @@ -59,8 +65,8 @@ export class ReportsService { return reportedBoards; } - async getAllUserReports(): Promise { - const reportedUsers: Report[] = + async getAllUserReports(): Promise[]> { + const reportedUsers: Report[] = await this.userReportRepository.getAllUserReports(); if (!reportedUsers) { @@ -72,8 +78,10 @@ export class ReportsService { return reportedUsers; } - async getReportByNo(reportNo: number): Promise { - const report: Report = await this.reportRepository.getReportByNo(reportNo); + async getReportByNo(reportNo: number): Promise> { + const report: Report = await this.reportRepository.getReportByNo( + reportNo, + ); if (!report) { throw new NotFoundException( diff --git a/main-project/src/reports/repository/report-board.repository.ts b/main-project/src/reports/repository/report-board.repository.ts index 6182cb50..cf62b5ca 100644 --- a/main-project/src/reports/repository/report-board.repository.ts +++ b/main-project/src/reports/repository/report-board.repository.ts @@ -8,7 +8,7 @@ import { Report } from '../interface/reports.interface'; export class ReportBoardRepository extends Repository { //신고글 조회 관련 - async getAllBoardReports(): Promise { + async getAllBoardReports(): Promise[]> { try { const reportedBoards = this.createQueryBuilder('ReportBoards') .leftJoin('ReportBoards.reportNo', 'reports') diff --git a/main-project/src/reports/repository/report-user.repository.ts b/main-project/src/reports/repository/report-user.repository.ts index 20e9755d..8c890bf0 100644 --- a/main-project/src/reports/repository/report-user.repository.ts +++ b/main-project/src/reports/repository/report-user.repository.ts @@ -7,7 +7,7 @@ import { Report } from '../interface/reports.interface'; @EntityRepository(ReportUsers) export class ReportUserRepository extends Repository { //신고글 조회 관련 - async getAllUserReports(): Promise { + async getAllUserReports(): Promise[]> { try { const reportedusers = this.createQueryBuilder('ReportUsers') .leftJoin('ReportUsers.reportNo', 'reports') diff --git a/main-project/src/reports/repository/reports.repository.ts b/main-project/src/reports/repository/reports.repository.ts index b2a1e384..aebec070 100644 --- a/main-project/src/reports/repository/reports.repository.ts +++ b/main-project/src/reports/repository/reports.repository.ts @@ -5,9 +5,11 @@ import { EntityRepository, InsertResult, Repository, + SelectQueryBuilder, UpdateResult, } from 'typeorm'; import { CreateReportDto } from '../dto/create-reports.dto'; +import { ReportFilterDto } from '../dto/report-filter.dto'; import { UpdateReportDto } from '../dto/update-reports.dto'; import { Reports } from '../entity/reports.entity'; import { Report } from '../interface/reports.interface'; @@ -15,32 +17,55 @@ import { Report } from '../interface/reports.interface'; @EntityRepository(Reports) export class ReportRepository extends Repository { //신고글 조회 관련 - async getAllReports(): Promise { + async getReports({ + type, + page, + }: ReportFilterDto): Promise[]> { try { - const reports = this.createQueryBuilder('reports') - .leftJoin('reports.reportedBoard', 'reportedBoard') - .leftJoin('reports.reportedUser', 'reportedUser') + const query: SelectQueryBuilder = this.createQueryBuilder( + 'reports', + ) + .leftJoin('reports.reportedBoard', 'reportedBoards') + .leftJoin('reports.reportedUser', 'reportedUsers') .select([ 'reports.no AS no', 'reports.userNo AS userNo', 'reports.title AS title', 'reports.description AS description', - 'reportedBoard.targetBoardNo as tagetBoardNo', - 'reportedUser.targetUserNo as tagetUserNo', ]) .orderBy('reports.no', 'DESC') - .getRawMany(); + .groupBy('reports.no') + .limit(5); + + if (page > 1) { + query.offset((page - 1) * 5); + } + switch (type) { + case 0: + query.addSelect('reportedUsers.targetUserNo as tagetUserNo'); + break; + case 1: + query.addSelect('reportedBoards.targetBoardNo as tagetBoardNo'); + break; + default: + query.addSelect([ + 'reportedUsers.targetUserNo as tagetUserNo', + 'reportedBoards.targetBoardNo as tagetBoardNo', + ]); + } + + const reports = await query.getRawMany(); return reports; } catch (error) { throw new InternalServerErrorException( `${error} - getAllreports-repository: 알 수 없는 서버 에러입니다.`, + getReports-repository: 알 수 없는 서버 에러입니다.`, ); } } - async getReportByNo(reportNo: number): Promise { + async getReportByNo(reportNo: number): Promise> { try { const report = this.createQueryBuilder('reports') .leftJoin('reports.reportedBoard', 'reportedBoard') diff --git a/main-project/src/reports/swagger-decorator/get-reports.decorator.ts b/main-project/src/reports/swagger-decorator/get-reports.decorator.ts new file mode 100644 index 00000000..ffb26bba --- /dev/null +++ b/main-project/src/reports/swagger-decorator/get-reports.decorator.ts @@ -0,0 +1,54 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBearerAuth, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiGetReports() { + return applyDecorators( + ApiOperation({ + summary: '신고내역 전체/필터 조회', + }), + ApiBearerAuth(), + ApiOkResponse( + SwaggerApiResponse.success( + '신고내역 전체/필터 조회', + '신고내역 전체/필터 조회 성공', + { + reports: [ + { + no: 4, + userNo: 16, + title: 'test', + description: 'test description', + isDone: 0, + createdDate: '2023.01.30 16:34:05', + }, + { + no: 4, + userNo: 16, + title: 'test', + description: 'test description', + isDone: 0, + createdDate: '2023.01.30 16:34:05', + }, + ], + }, + ), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'reportsNotFound', + example: { + msg: `신고내역 전체 조회(getReports-service): 신고내역이 없습니다.`, + }, + }, + ]), + ), + ); +} From d913489f3f68e54cde8f90bc02b9dce0d2e4f2d8 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 8 Feb 2023 01:11:59 +0900 Subject: [PATCH 02/23] =?UTF-8?q?Imcomplete(minho/report):=20=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0=EB=A7=81=20=EB=AF=B8=EC=99=84=EC=84=B1=20#19?= =?UTF-8?q?4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/enquiries/enquiries.controller.ts | 2 +- .../repository/notices-board.repository.ts | 6 +- .../entity/report-board-images.entity.ts | 28 +++ .../src/reports/entity/report-board.entity.ts | 8 + .../reports/entity/report-images.entity.ts | 24 --- .../entity/report-user-image.entity.ts | 28 +++ .../src/reports/entity/report-user.entity.ts | 8 + .../src/reports/entity/reports.entity.ts | 5 +- .../src/reports/reports.controller.ts | 42 +++-- main-project/src/reports/reports.service.ts | 165 +++++------------- .../repository/report-board.repository.ts | 8 +- .../reports/repository/reports.repository.ts | 10 +- .../swagger-decorator/get-report.decorator.ts | 51 ++++++ 13 files changed, 208 insertions(+), 177 deletions(-) create mode 100644 main-project/src/reports/entity/report-board-images.entity.ts delete mode 100644 main-project/src/reports/entity/report-images.entity.ts create mode 100644 main-project/src/reports/entity/report-user-image.entity.ts create mode 100644 main-project/src/reports/swagger-decorator/get-report.decorator.ts diff --git a/main-project/src/enquiries/enquiries.controller.ts b/main-project/src/enquiries/enquiries.controller.ts index d9301031..d7f837f9 100644 --- a/main-project/src/enquiries/enquiries.controller.ts +++ b/main-project/src/enquiries/enquiries.controller.ts @@ -72,7 +72,7 @@ export class EnquiriesController { userNo, ); - return { msg: '유저별 문의사항 전체 조회', response: { eunqiries } }; + return { msg: '유저별 문의사항 전체 조회 성공', response: { eunqiries } }; } @Get('/:enquiryNo') diff --git a/main-project/src/notices/repository/notices-board.repository.ts b/main-project/src/notices/repository/notices-board.repository.ts index 55c3998f..bd25a1e5 100644 --- a/main-project/src/notices/repository/notices-board.repository.ts +++ b/main-project/src/notices/repository/notices-board.repository.ts @@ -6,15 +6,11 @@ import { NoticeBoards } from '../entity/notice-board.entity'; export class NoticeBoardsRepository extends Repository { async saveNoticeBoard(noticeNo: number, boardNo: number): Promise { try { - const { raw }: InsertResult = await this.createQueryBuilder( - 'notice_boards', - ) + await this.createQueryBuilder() .insert() .into(NoticeBoards) .values({ noticeNo, boardNo }) .execute(); - - return raw.affectedRows; } catch (error) { throw new InternalServerErrorException( `${error} 알람 생성 에러(saveNoticeBoard): 알 수 없는 서버 오류입니다.`, diff --git a/main-project/src/reports/entity/report-board-images.entity.ts b/main-project/src/reports/entity/report-board-images.entity.ts new file mode 100644 index 00000000..968a8a42 --- /dev/null +++ b/main-project/src/reports/entity/report-board-images.entity.ts @@ -0,0 +1,28 @@ +import { + BaseEntity, + Column, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { ReportBoards } from './report-board.entity'; + +@Entity('report _board_images') +export class ReportBoardImages extends BaseEntity { + @PrimaryGeneratedColumn() + no: number; + + @Column({ type: 'varchar', length: 255, nullable: false, name: 'image_url' }) + imageUrl: string; + + @ManyToOne( + (type) => ReportBoards, + (reportBoards) => reportBoards.reportBoardImage, + { + onDelete: 'CASCADE', + }, + ) + @JoinColumn({ name: 'report_board_no' }) + reportBoardNo: number; +} diff --git a/main-project/src/reports/entity/report-board.entity.ts b/main-project/src/reports/entity/report-board.entity.ts index 8396d72e..c295013a 100644 --- a/main-project/src/reports/entity/report-board.entity.ts +++ b/main-project/src/reports/entity/report-board.entity.ts @@ -4,9 +4,11 @@ import { Entity, JoinColumn, ManyToOne, + OneToMany, OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; +import { ReportBoardImages } from './report-board-images.entity'; import { Reports } from './reports.entity'; @Entity('report_boards') @@ -25,4 +27,10 @@ export class ReportBoards extends BaseEntity { }) @JoinColumn({ name: 'target_board_no' }) targetBoardNo: number; + + @OneToMany( + (type) => ReportBoardImages, + (reportBoardImages) => reportBoardImages.reportBoardNo, + ) + reportBoardImage: ReportBoardImages[]; } diff --git a/main-project/src/reports/entity/report-images.entity.ts b/main-project/src/reports/entity/report-images.entity.ts deleted file mode 100644 index 79f722fb..00000000 --- a/main-project/src/reports/entity/report-images.entity.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - BaseEntity, - Column, - Entity, - JoinColumn, - OneToOne, - PrimaryGeneratedColumn, -} from 'typeorm'; -import { Reports } from './reports.entity'; - -@Entity('report_images') -export class ReportImages extends BaseEntity { - @PrimaryGeneratedColumn() - no: number; - - @Column({ type: 'varchar', length: 255, nullable: false, name: 'img_url' }) - imgUrl: string; - - @OneToOne((type) => Reports, (reports) => reports.reportImages, { - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'report_no' }) - reportNo: number; -} diff --git a/main-project/src/reports/entity/report-user-image.entity.ts b/main-project/src/reports/entity/report-user-image.entity.ts new file mode 100644 index 00000000..d4b8db42 --- /dev/null +++ b/main-project/src/reports/entity/report-user-image.entity.ts @@ -0,0 +1,28 @@ +import { + BaseEntity, + Column, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { ReportUsers } from './report-user.entity'; + +@Entity('report _user_images') +export class ReportUserImages extends BaseEntity { + @PrimaryGeneratedColumn() + no: number; + + @Column({ type: 'varchar', length: 255, nullable: false, name: 'image_url' }) + imageUrl: string; + + @ManyToOne( + (type) => ReportUsers, + (reportUsers) => reportUsers.reportUserImage, + { + onDelete: 'CASCADE', + }, + ) + @JoinColumn({ name: 'report_user_no' }) + reportUserNo: number; +} diff --git a/main-project/src/reports/entity/report-user.entity.ts b/main-project/src/reports/entity/report-user.entity.ts index b108dd46..cdc78869 100644 --- a/main-project/src/reports/entity/report-user.entity.ts +++ b/main-project/src/reports/entity/report-user.entity.ts @@ -4,9 +4,11 @@ import { Entity, JoinColumn, ManyToOne, + OneToMany, OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; +import { ReportUserImages } from './report-user-image.entity'; import { Reports } from './reports.entity'; @Entity('report_users') @@ -25,4 +27,10 @@ export class ReportUsers extends BaseEntity { }) @JoinColumn({ name: 'target_user_no' }) targetUserNo: number; + + @OneToMany( + (type) => ReportUserImages, + (reportUserImages) => reportUserImages.reportUserNo, + ) + reportUserImage: ReportUserImages[]; } diff --git a/main-project/src/reports/entity/reports.entity.ts b/main-project/src/reports/entity/reports.entity.ts index 67a430f1..63a46052 100644 --- a/main-project/src/reports/entity/reports.entity.ts +++ b/main-project/src/reports/entity/reports.entity.ts @@ -10,7 +10,7 @@ import { OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; -import { ReportImages } from './report-images.entity'; +import { ReportBoardImages } from './report-board-images.entity'; import { ReportBoards } from './report-board.entity'; import { ReportUsers } from './report-user.entity'; @@ -40,7 +40,4 @@ export class Reports extends BaseEntity { @ManyToOne((type) => Users, (user) => user.report, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'user_no' }) userNo: number; - - @OneToOne((type) => ReportImages, (reportImages) => reportImages.reportNo) - reportImages: ReportImages; } diff --git a/main-project/src/reports/reports.controller.ts b/main-project/src/reports/reports.controller.ts index c84aae70..910c207b 100644 --- a/main-project/src/reports/reports.controller.ts +++ b/main-project/src/reports/reports.controller.ts @@ -45,83 +45,101 @@ export class ReportsController { } @Get('/:reportNo') + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) @ApiOperation({ summary: '신고 상세 조회 API', description: '신고 번호를 통해 해당 신고내역을 조회한다.', }) async getReportByNo( + @TransactionDecorator() manager: EntityManager, @Param('reportNo', ParseIntPipe) reportNo: number, ): Promise { - const response: Report = await this.reportsService.getReportByNo( + const report: Report = await this.reportsService.getReport( + manager, reportNo, ); - return { response }; + return { msg: '신고내역 상세조회 성공', response: { report } }; } // Post @Post('/boards/:boardNo') + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) @ApiOperation({ summary: '게시글 신고 생성 API', description: '입력된 정보로 게시글 신고 생성.', }) async createBoardReport( @Param('boardNo', ParseIntPipe) boardNo: number, + @TransactionDecorator() manager: EntityManager, @Body() createReportDto: CreateReportDto, ): Promise { - const response: number = await this.reportsService.createBoardReport( + await this.reportsService.createBoardReport( + manager, createReportDto, boardNo, ); - return { response }; + return { msg: '게시글 신고 성공' }; } @Post('/users/:userNo') + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) @ApiOperation({ summary: '사용자 신고 생성 API', description: '입력된 정보로 사용자 신고 생성.', }) async createUserReport( @Param('userNo', ParseIntPipe) userNo: number, + @TransactionDecorator() manager: EntityManager, @Body() createReportDto: CreateReportDto, ): Promise { - const response: number = await this.reportsService.createUserReport( + await this.reportsService.createUserReport( + manager, createReportDto, userNo, ); - return { response }; + return { msg: '유저 신고 성공' }; } // Patch Methods @Patch('/:reportNo') + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) @ApiOperation({ summary: '신고내용 수정 API', description: '입력한 정보로 신고내용을 수정한다.', }) async updateBoard( @Param('reportNo', ParseIntPipe) reportNo: number, + @TransactionDecorator() manager: EntityManager, @Body() updateReportDto: UpdateReportDto, ): Promise { - const response: string = await this.reportsService.updateReport( - reportNo, - updateReportDto, - ); + await this.reportsService.updateReport(manager, reportNo, updateReportDto); - return { response }; + return { msg: '신고내역 수정 성공' }; } // Delete Methods @Delete('/:reportNo') + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) @ApiOperation({ summary: '특정 신고내역 삭제 API', description: '신고 번호를 사용하여 해당 신고내역을 삭제한다.', }) async deleteReportByNo( @Param('reportNo', ParseIntPipe) reportNo: number, + @TransactionDecorator() manager: EntityManager, ): Promise { - const response = await this.reportsService.deleteReportByNo(reportNo); + const response = await this.reportsService.deleteReportByNo( + manager, + reportNo, + ); return { response }; } diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index 735563e1..102824ca 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -7,7 +7,7 @@ import { import { InjectRepository } from '@nestjs/typeorm'; import { Board } from 'src/boards/interface/boards.interface'; import { BoardsRepository } from 'src/boards/repository/board.repository'; -import { Connection, EntityManager, QueryRunner } from 'typeorm'; +import { EntityManager } from 'typeorm'; import { CreateReportDto } from './dto/create-reports.dto'; import { UpdateReportDto } from './dto/update-reports.dto'; import { Report } from './interface/reports.interface'; @@ -20,19 +20,9 @@ import { ReportFilterDto } from './dto/report-filter.dto'; @Injectable() export class ReportsService { constructor( - @InjectRepository(ReportRepository) - private readonly reportRepository: ReportRepository, - - @InjectRepository(BoardsRepository) private readonly boardRepository: BoardsRepository, - - @InjectRepository(ReportBoardRepository) private readonly boardReportRepository: ReportBoardRepository, - - @InjectRepository(ReportUserRepository) private readonly userReportRepository: ReportUserRepository, - - private readonly connection: Connection, ) {} // 조회 관련 async getReports( @@ -78,12 +68,15 @@ export class ReportsService { return reportedUsers; } - async getReportByNo(reportNo: number): Promise> { - const report: Report = await this.reportRepository.getReportByNo( - reportNo, - ); + async getReport( + manager: EntityManager, + reportNo: number, + ): Promise> { + const report: Report = await manager + .getCustomRepository(ReportRepository) + .getReport(reportNo); - if (!report) { + if (!report.no) { throw new NotFoundException( `${reportNo}번 신고내역 상세 조회(getReportByNo): 알 수 없는 서버 에러입니다.`, ); @@ -98,143 +91,77 @@ export class ReportsService { // 생성 관련 async createBoardReport( + manager: EntityManager, createReportDto: CreateReportDto, boardNo: number, - ): Promise { - const queryRunner: QueryRunner = this.connection.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - const board: Board = await this.boardRepository.getBoardByNo( - boardNo, - ); - if (!board.no) { - throw new BadRequestException(` + ): Promise { + const board: Board = await this.boardRepository.getBoardByNo( + boardNo, + ); + if (!board.no) { + throw new BadRequestException(` 게시글 신고 생성(createBoardReport): ${boardNo}번 게시글을 찾을 수 없습니다. `); - } - - const reportNo: number = await this.setReport( - queryRunner, - createReportDto, - ); - - const { insertId }: ResultSetHeader = await queryRunner.manager - .getCustomRepository(ReportBoardRepository) - .createBoardReport(reportNo, boardNo); - - if (!insertId) { - throw new InternalServerErrorException( - `게시글 신고 생성(createBoardReport): 게시글 신고 생성 실패.`, - ); - } - - await queryRunner.commitTransaction(); + } - return reportNo; - } catch (error) { - await queryRunner?.rollbackTransaction(); + const reportNo: number = await this.setReport(manager, createReportDto); - throw error; - } finally { - await queryRunner?.release(); - } + await manager + .getCustomRepository(ReportBoardRepository) + .createBoardReport(reportNo, boardNo); } async createUserReport( + manager: EntityManager, createReportDto: CreateReportDto, userNo: number, - ): Promise { - const queryRunner: QueryRunner = this.connection.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - // TODO: User 확인 Method 사용 부분 - - const reportNo: number = await this.setReport( - queryRunner, - createReportDto, - ); - - const { insertId }: ResultSetHeader = await queryRunner.manager - .getCustomRepository(ReportUserRepository) - .createUserReport(reportNo, userNo); - - if (!insertId) { - throw new InternalServerErrorException( - `사용자 신고 생성(createUserReport): 알 수 없는 서버 에러입니다.`, - ); - } + ): Promise { + // TODO: User 확인 Method 사용 부분 - await queryRunner.commitTransaction(); + const reportNo: number = await this.setReport(manager, createReportDto); - return reportNo; - } catch (error) { - await queryRunner?.rollbackTransaction(); + const { insertId }: ResultSetHeader = await manager + .getCustomRepository(ReportUserRepository) + .createUserReport(reportNo, userNo); - throw error; - } finally { - await queryRunner?.release(); + if (!insertId) { + throw new InternalServerErrorException( + `사용자 신고 생성(createUserReport): 알 수 없는 서버 에러입니다.`, + ); } } private async setReport( - queryRunner: QueryRunner, + manager: EntityManager, createReportDto: CreateReportDto, ): Promise { - const { insertId }: ResultSetHeader = await queryRunner.manager + const { insertId }: ResultSetHeader = await manager .getCustomRepository(ReportRepository) .createReport(createReportDto); - if (!insertId) { - throw new InternalServerErrorException( - `신고내역 생성(setReport): 알 수 없는 서버 에러입니다.`, - ); - } - return insertId; } //수정 관련 async updateReport( + manager: EntityManager, reportNo: number, updateReportDto: UpdateReportDto, - ): Promise { - const queryRunner: QueryRunner = this.connection.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - await this.getReportByNo(reportNo); - - const report: number = await queryRunner.manager - .getCustomRepository(ReportRepository) - .updateReport(reportNo, updateReportDto); - - if (!report) { - throw new InternalServerErrorException( - `신고내역 수정(updateReport): 알 수 없는 서버 에러입니다.`, - ); - } - - await queryRunner.commitTransaction(); + ): Promise { + await this.getReport(manager, reportNo); - return `${reportNo}번 신고내역이 수정되었습니다.`; - } catch (error) { - await queryRunner?.rollbackTransaction(); - - throw error; - } finally { - await queryRunner?.release(); - } + await manager + .getCustomRepository(ReportRepository) + .updateReport(reportNo, updateReportDto); } // 삭제 관련 - async deleteReportByNo(reportNo: number): Promise { - await this.getReportByNo(reportNo); - await this.reportRepository.deleteReport(reportNo); + async deleteReportByNo( + manager: EntityManager, + reportNo: number, + ): Promise { + await this.getReport(manager, reportNo); + await manager.getCustomRepository(ReportRepository).deleteReport(reportNo); return `${reportNo}번 신고내역 삭제 성공 :)`; } diff --git a/main-project/src/reports/repository/report-board.repository.ts b/main-project/src/reports/repository/report-board.repository.ts index cf62b5ca..37cecc80 100644 --- a/main-project/src/reports/repository/report-board.repository.ts +++ b/main-project/src/reports/repository/report-board.repository.ts @@ -31,17 +31,13 @@ export class ReportBoardRepository extends Repository { } // 신고글 작성 관련 - async createBoardReport( - reportNo: number, - boardNo: number, - ): Promise { + async createBoardReport(reportNo: number, boardNo: number): Promise { try { - const { raw }: InsertResult = await this.createQueryBuilder() + await this.createQueryBuilder() .insert() .into(ReportBoards) .values({ reportNo, targetBoardNo: boardNo }) .execute(); - return raw; } catch (error) { throw new InternalServerErrorException( `${error} createBoardReport-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/reports/repository/reports.repository.ts b/main-project/src/reports/repository/reports.repository.ts index aebec070..157d8b1c 100644 --- a/main-project/src/reports/repository/reports.repository.ts +++ b/main-project/src/reports/repository/reports.repository.ts @@ -65,7 +65,7 @@ export class ReportRepository extends Repository { } } - async getReportByNo(reportNo: number): Promise> { + async getReport(reportNo: number): Promise> { try { const report = this.createQueryBuilder('reports') .leftJoin('reports.reportedBoard', 'reportedBoard') @@ -84,7 +84,7 @@ export class ReportRepository extends Repository { return report; } catch (error) { throw new InternalServerErrorException( - `${error} getReportByNo-repository: 알 수 없는 서버 에러입니다.`, + `${error} getReport-repository: 알 수 없는 서버 에러입니다.`, ); } } @@ -111,15 +111,13 @@ export class ReportRepository extends Repository { async updateReport( reportNo: number, updateReportDto: UpdateReportDto, - ): Promise { + ): Promise { try { - const { affected }: UpdateResult = await this.createQueryBuilder() + await this.createQueryBuilder() .update(Reports) .set(updateReportDto) .where('no = :reportNo', { reportNo }) .execute(); - - return affected; } catch (error) { throw new InternalServerErrorException( `${error} updateReport-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/reports/swagger-decorator/get-report.decorator.ts b/main-project/src/reports/swagger-decorator/get-report.decorator.ts new file mode 100644 index 00000000..063985b0 --- /dev/null +++ b/main-project/src/reports/swagger-decorator/get-report.decorator.ts @@ -0,0 +1,51 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiGetReport() { + return applyDecorators( + ApiOperation({ + summary: '신고내역 상세 조회', + }), + ApiBearerAuth(), + ApiOkResponse( + SwaggerApiResponse.success('신고내역 조회', '신고내역 조회 성공', { + enquiries: { + no: 4, + userNo: 16, + title: 'test', + description: 'test description', + isDone: 0, + createdDate: '2023.01.30 16:34:05', + }, + }), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'reportyNotFound', + example: { + msg: `문의 상세 조회(readReport-service): 3번 신고내역이 없습니다.`, + }, + }, + ]), + ), + ApiBadRequestResponse( + SwaggerApiResponse.exception([ + { + name: 'isNotWriter', + example: { + msg: `사용자 검증(getEnquiry-service): 잘못된 사용자의 접근입니다.`, + }, + }, + ]), + ), + ); +} From dc497573750cfd5768e286e651c50d33398ae25b Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 8 Feb 2023 03:07:22 +0900 Subject: [PATCH 03/23] =?UTF-8?q?Imcomplete(minho/report):=20=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=EC=88=98=EC=A0=95,=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81=20=EB=AF=B8=EC=99=84=EC=84=B1=20#194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/enquiry.repository.ts | 4 +- .../reports/interface/reports.interface.ts | 3 +- .../src/reports/reports.controller.ts | 8 +-- main-project/src/reports/reports.service.ts | 68 +++++-------------- .../repository/report-board.repository.ts | 28 +------- .../repository/report-user.repository.ts | 35 +--------- .../reports/repository/reports.repository.ts | 27 ++++---- .../swagger-decorator/get-report.decorator.ts | 2 +- 8 files changed, 43 insertions(+), 132 deletions(-) diff --git a/main-project/src/enquiries/repository/enquiry.repository.ts b/main-project/src/enquiries/repository/enquiry.repository.ts index 45a058b6..7a86da1b 100644 --- a/main-project/src/enquiries/repository/enquiry.repository.ts +++ b/main-project/src/enquiries/repository/enquiry.repository.ts @@ -31,7 +31,7 @@ export class EnquiriesRepository extends Repository { 'enquiries.title AS title', 'enquiries.description AS description', 'enquiries.isDone AS isDone', - `DATE_FORMAT(enquiries.createdDate, '%Y.%m.%d %T') AS createdDate`, + 'DATE_FORMAT(enquiries.createdDate, "%Y.%m.%d %T") AS createdDate', 'JSON_ARRAYAGG(images.imageUrl) AS imageUrls', ]) .orderBy('no', 'DESC') @@ -78,7 +78,7 @@ export class EnquiriesRepository extends Repository { 'enquiries.title AS title', 'enquiries.description AS description', 'enquiries.isDone AS isDone', - `DATE_FORMAT(enquiries.createdDate, '%Y.%m.%d %T') AS createdDate`, + 'DATE_FORMAT(enquiries.createdDate, "%Y.%m.%d %T") AS createdDate', 'JSON_ARRAYAGG(images.imageUrl) AS imageUrls', ]) .where('enquiries.no = :enquiryNo', { enquiryNo }) diff --git a/main-project/src/reports/interface/reports.interface.ts b/main-project/src/reports/interface/reports.interface.ts index 6b713c92..6d7dc178 100644 --- a/main-project/src/reports/interface/reports.interface.ts +++ b/main-project/src/reports/interface/reports.interface.ts @@ -5,5 +5,6 @@ export interface Report { userNo: number; targetBoardNo?: number; targetUserNo?: number; - imageUrls: T; + createdDate?: Date; + imageUrls?: T; } diff --git a/main-project/src/reports/reports.controller.ts b/main-project/src/reports/reports.controller.ts index 910c207b..c67ecae5 100644 --- a/main-project/src/reports/reports.controller.ts +++ b/main-project/src/reports/reports.controller.ts @@ -21,6 +21,7 @@ import { ReportFilterDto } from './dto/report-filter.dto'; import { UpdateReportDto } from './dto/update-reports.dto'; import { Report } from './interface/reports.interface'; import { ReportsService } from './reports.service'; +import { ApiGetReport } from './swagger-decorator/get-report.decorator'; import { ApiGetReports } from './swagger-decorator/get-reports.decorator'; @Controller('reports') @@ -47,11 +48,8 @@ export class ReportsController { @Get('/:reportNo') @UseGuards(JwtAuthGuard) @UseInterceptors(TransactionInterceptor) - @ApiOperation({ - summary: '신고 상세 조회 API', - description: '신고 번호를 통해 해당 신고내역을 조회한다.', - }) - async getReportByNo( + @ApiGetReport() + async getReport( @TransactionDecorator() manager: EntityManager, @Param('reportNo', ParseIntPipe) reportNo: number, ): Promise { diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index 102824ca..39f25b4e 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -4,7 +4,6 @@ import { InternalServerErrorException, NotFoundException, } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; import { Board } from 'src/boards/interface/boards.interface'; import { BoardsRepository } from 'src/boards/repository/board.repository'; import { EntityManager } from 'typeorm'; @@ -19,11 +18,7 @@ import { ReportFilterDto } from './dto/report-filter.dto'; @Injectable() export class ReportsService { - constructor( - private readonly boardRepository: BoardsRepository, - private readonly boardReportRepository: ReportBoardRepository, - private readonly userReportRepository: ReportUserRepository, - ) {} + constructor(private readonly boardRepository: BoardsRepository) {} // 조회 관련 async getReports( manager: EntityManager, @@ -42,33 +37,22 @@ export class ReportsService { return reports; } - async getAllBoardReports(): Promise[]> { - const reportedBoards: Report[] = - await this.boardReportRepository.getAllBoardReports(); - - if (!reportedBoards) { - throw new NotFoundException( - `게시글 신고내역 전체 조회(getAllReportedusers): 알 수 없는 서버 에러입니다.`, - ); - } - - return reportedBoards; - } - - async getAllUserReports(): Promise[]> { - const reportedUsers: Report[] = - await this.userReportRepository.getAllUserReports(); + async getReport( + manager: EntityManager, + reportNo: number, + ): Promise> { + const report: Report = await this.readReport(manager, reportNo); - if (!reportedUsers) { + if (!report.no) { throw new NotFoundException( - `사용자 신고내역 전체 조회(getAllReportedusers): 알 수 없는 서버 에러입니다.`, + `신고내역 상세 조회(getReport): ${reportNo}번 신고내역이 없습니다.`, ); } - return reportedUsers; + return report; } - async getReport( + private async readReport( manager: EntityManager, reportNo: number, ): Promise> { @@ -76,16 +60,6 @@ export class ReportsService { .getCustomRepository(ReportRepository) .getReport(reportNo); - if (!report.no) { - throw new NotFoundException( - `${reportNo}번 신고내역 상세 조회(getReportByNo): 알 수 없는 서버 에러입니다.`, - ); - } - - !report.targetBoardNo - ? delete report.targetBoardNo - : delete report.targetUserNo; - return report; } @@ -95,13 +69,13 @@ export class ReportsService { createReportDto: CreateReportDto, boardNo: number, ): Promise { - const board: Board = await this.boardRepository.getBoardByNo( + const { no }: Board = await this.boardRepository.getBoardByNo( boardNo, ); - if (!board.no) { - throw new BadRequestException(` - 게시글 신고 생성(createBoardReport): ${boardNo}번 게시글을 찾을 수 없습니다. - `); + if (!no) { + throw new BadRequestException( + `게시글 신고 생성(createBoardReport-service): ${boardNo}번 게시글을 찾을 수 없습니다.`, + ); } const reportNo: number = await this.setReport(manager, createReportDto); @@ -120,15 +94,9 @@ export class ReportsService { const reportNo: number = await this.setReport(manager, createReportDto); - const { insertId }: ResultSetHeader = await manager + await manager .getCustomRepository(ReportUserRepository) .createUserReport(reportNo, userNo); - - if (!insertId) { - throw new InternalServerErrorException( - `사용자 신고 생성(createUserReport): 알 수 없는 서버 에러입니다.`, - ); - } } private async setReport( @@ -159,10 +127,8 @@ export class ReportsService { async deleteReportByNo( manager: EntityManager, reportNo: number, - ): Promise { + ): Promise { await this.getReport(manager, reportNo); await manager.getCustomRepository(ReportRepository).deleteReport(reportNo); - - return `${reportNo}번 신고내역 삭제 성공 :)`; } } diff --git a/main-project/src/reports/repository/report-board.repository.ts b/main-project/src/reports/repository/report-board.repository.ts index 37cecc80..87cfb63f 100644 --- a/main-project/src/reports/repository/report-board.repository.ts +++ b/main-project/src/reports/repository/report-board.repository.ts @@ -1,35 +1,9 @@ import { InternalServerErrorException } from '@nestjs/common'; -import { ResultSetHeader } from 'mysql2'; -import { EntityRepository, InsertResult, Repository } from 'typeorm'; +import { EntityRepository, Repository } from 'typeorm'; import { ReportBoards } from '../entity/report-board.entity'; -import { Report } from '../interface/reports.interface'; @EntityRepository(ReportBoards) export class ReportBoardRepository extends Repository { - //신고글 조회 관련 - - async getAllBoardReports(): Promise[]> { - try { - const reportedBoards = this.createQueryBuilder('ReportBoards') - .leftJoin('ReportBoards.reportNo', 'reports') - .select([ - 'reports.no AS no', - 'reports.userNo AS userNo', - 'reports.title AS title', - 'reports.description AS description', - 'ReportBoards.targetBoardNo as targetBoardNo', - ]) - .where('ReportBoards.reportNo > 0') - .getRawMany(); - - return reportedBoards; - } catch (error) { - throw new InternalServerErrorException( - `${error} getAllBoardReports-repository: 알 수 없는 서버 에러입니다.`, - ); - } - } - // 신고글 작성 관련 async createBoardReport(reportNo: number, boardNo: number): Promise { try { diff --git a/main-project/src/reports/repository/report-user.repository.ts b/main-project/src/reports/repository/report-user.repository.ts index 8c890bf0..efa06ba5 100644 --- a/main-project/src/reports/repository/report-user.repository.ts +++ b/main-project/src/reports/repository/report-user.repository.ts @@ -1,46 +1,17 @@ import { InternalServerErrorException } from '@nestjs/common'; -import { ResultSetHeader } from 'mysql2'; -import { EntityRepository, InsertResult, Repository } from 'typeorm'; +import { EntityRepository, Repository } from 'typeorm'; import { ReportUsers } from '../entity/report-user.entity'; -import { Report } from '../interface/reports.interface'; @EntityRepository(ReportUsers) export class ReportUserRepository extends Repository { - //신고글 조회 관련 - async getAllUserReports(): Promise[]> { - try { - const reportedusers = this.createQueryBuilder('ReportUsers') - .leftJoin('ReportUsers.reportNo', 'reports') - .select([ - 'reports.no AS no', - 'reports.userNo AS userNo', - 'reports.title AS title', - 'reports.description AS description', - 'ReportUsers.targetUserNo as targetUserNo', - ]) - .where('ReportUsers.reportNo > 0') - .getRawMany(); - - return reportedusers; - } catch (error) { - throw new InternalServerErrorException( - `${error} getAllReportedusers-repository: 알 수 없는 서버 에러입니다.`, - ); - } - } - // 신고글 작성 관련 - async createUserReport( - reportNo: number, - userNo: number, - ): Promise { + async createUserReport(reportNo: number, userNo: number): Promise { try { - const { raw }: InsertResult = await this.createQueryBuilder() + await this.createQueryBuilder() .insert() .into(ReportUsers) .values({ reportNo, targetUserNo: userNo }) .execute(); - return raw; } catch (error) { throw new InternalServerErrorException( `${error} createUserReport-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/reports/repository/reports.repository.ts b/main-project/src/reports/repository/reports.repository.ts index 157d8b1c..f49446d0 100644 --- a/main-project/src/reports/repository/reports.repository.ts +++ b/main-project/src/reports/repository/reports.repository.ts @@ -6,7 +6,6 @@ import { InsertResult, Repository, SelectQueryBuilder, - UpdateResult, } from 'typeorm'; import { CreateReportDto } from '../dto/create-reports.dto'; import { ReportFilterDto } from '../dto/report-filter.dto'; @@ -32,6 +31,7 @@ export class ReportRepository extends Repository { 'reports.userNo AS userNo', 'reports.title AS title', 'reports.description AS description', + 'DATE_FORMAT(reports.createdDate, "%Y.%m.%d %T") AS createdDate', ]) .orderBy('reports.no', 'DESC') .groupBy('reports.no') @@ -67,18 +67,23 @@ export class ReportRepository extends Repository { async getReport(reportNo: number): Promise> { try { - const report = this.createQueryBuilder('reports') - .leftJoin('reports.reportedBoard', 'reportedBoard') - .leftJoin('reports.reportedUser', 'reportedUser') + const report: Report = await this.createQueryBuilder('reports') + .leftJoin('reports.reportedBoard', 'reportedBoards') + .leftJoin('reports.reportedUser', 'reportedUsers') + .leftJoin('reportedBoards.reportBoardImage', 'reportBoardImages') + .leftJoin('reportedUsers.reportUserImage', 'reportUserImages') .select([ 'reports.no AS no', 'reports.userNo AS userNo', 'reports.title AS title', 'reports.description AS description', - 'reportedBoard.targetBoardNo as targetBoardNo', - 'reportedUser.targetUserNo as targetUserNo', + 'DATE_FORMAT(reports.createdDate, "%Y.%m.%d %T") AS createdDate', + `IF(reportedBoards.reportNo = ${reportNo}, reportedBoards.targetBoardNo, NULL) AS targetBoardNo`, + + // 'reportedBoards.targetBoardNo as targetBoardNo', + // 'reportedUsers.targetUserNo as targetUserNo', ]) - .where('reports.no=:reportNo', { reportNo }) + .where('reports.no = :reportNo', { reportNo }) .getRawOne(); return report; @@ -126,17 +131,13 @@ export class ReportRepository extends Repository { } // 신고 삭제 관련 - async deleteReport(reportNo: number): Promise { + async deleteReport(reportNo: number): Promise { try { - const { affected }: DeleteResult = await this.createQueryBuilder( - 'reports', - ) + await this.createQueryBuilder() .delete() .from(Reports) .where('no = :reportNo', { reportNo }) .execute(); - - return affected; } catch (error) { throw new InternalServerErrorException( `${error} deleteReport-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/reports/swagger-decorator/get-report.decorator.ts b/main-project/src/reports/swagger-decorator/get-report.decorator.ts index 063985b0..d56e9aaa 100644 --- a/main-project/src/reports/swagger-decorator/get-report.decorator.ts +++ b/main-project/src/reports/swagger-decorator/get-report.decorator.ts @@ -32,7 +32,7 @@ export function ApiGetReport() { { name: 'reportyNotFound', example: { - msg: `문의 상세 조회(readReport-service): 3번 신고내역이 없습니다.`, + msg: `신고내역 상세 조회(getReport): 3번 신고내역이 없습니다.`, }, }, ]), From 40f1ba641c6ae67af84a4ffa0cf3dd34c9571278 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:04:34 +0900 Subject: [PATCH 04/23] =?UTF-8?q?Refactor(minho/report):=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EA=B8=80=20=EC=8B=A0=EA=B3=A0=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81=20#194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/announces/announces.service.ts | 13 +- main-project/src/app.module.ts | 1 + main-project/src/boards/boards.service.ts | 11 +- .../src/enquiries/enquiries.service.ts | 30 ++-- .../repository/enquiry-image.repository.ts | 8 +- .../src/reports/dto/create-reports.dto.ts | 26 ++-- .../src/reports/dto/update-reports.dto.ts | 6 +- .../reports/interface/reports.interface.ts | 7 +- .../src/reports/reports.controller.ts | 34 ++-- main-project/src/reports/reports.module.ts | 3 +- main-project/src/reports/reports.service.ts | 147 +++++++++++++++--- .../report-board-image.repository.ts | 0 .../repository/report-board.repository.ts | 12 +- .../report-user-image.repository.ts | 22 +++ .../reports/repository/reports.repository.ts | 13 +- .../create-board-report.decorator.ts | 64 ++++++++ 16 files changed, 294 insertions(+), 103 deletions(-) create mode 100644 main-project/src/reports/repository/report-board-image.repository.ts create mode 100644 main-project/src/reports/repository/report-user-image.repository.ts create mode 100644 main-project/src/reports/swagger-decorator/create-board-report.decorator.ts diff --git a/main-project/src/announces/announces.service.ts b/main-project/src/announces/announces.service.ts index 032f0b0f..89980d1f 100644 --- a/main-project/src/announces/announces.service.ts +++ b/main-project/src/announces/announces.service.ts @@ -23,7 +23,7 @@ export class AnnouncesService { private readonly configService: ConfigService, ) {} - ADMIN_USER: number = Number(this.configService.get('ADMIN_USER')); + ADMIN_USER: number = this.configService.get('ADMIN_USER'); // 생성 관련 async createAnnounce( @@ -57,7 +57,7 @@ export class AnnouncesService { imageUrls: string[], announceNo: number, ): Promise { - const images: AnnounceImage[] = await this.convertImageArray( + const images: AnnounceImage[] = this.convertImageArray( announceNo, imageUrls, ); @@ -180,10 +180,10 @@ export class AnnouncesService { } // functions - private async convertImageArray( + private convertImageArray( announceNo: number, imageUrls: string[], - ): Promise[]> { + ): AnnounceImage[] { const images: AnnounceImage[] = imageUrls.map( (imageUrl: string) => { return { announceNo, imageUrl }; @@ -193,7 +193,10 @@ export class AnnouncesService { return images; } - private async validateAdmin(manager: EntityManager, userNo: number) { + private async validateAdmin( + manager: EntityManager, + userNo: number, + ): Promise { const { no }: Users = await manager .getCustomRepository(UsersRepository) .getUserByNo(userNo); diff --git a/main-project/src/app.module.ts b/main-project/src/app.module.ts index bde2aed2..e0ca0e5d 100644 --- a/main-project/src/app.module.ts +++ b/main-project/src/app.module.ts @@ -34,6 +34,7 @@ import * as Joi from 'joi'; EXPIRES_IN: Joi.number().required(), REFRESH_TOKEN_EXPIRATION: Joi.number().required(), TOKEN_EXPIRATION: Joi.number().required(), + ADMIN_USER: Joi.number().required(), }), }), CacheModule.register(), diff --git a/main-project/src/boards/boards.service.ts b/main-project/src/boards/boards.service.ts index 7f3a2614..a99a6c5e 100644 --- a/main-project/src/boards/boards.service.ts +++ b/main-project/src/boards/boards.service.ts @@ -402,7 +402,7 @@ export class BoardsService { manager, boardNo, ); - await this.validateHost(hostUserNo, userNo); + this.validateHost(hostUserNo, userNo); await this.removeBoard(manager, boardNo); } @@ -560,7 +560,7 @@ export class BoardsService { guests: number[], ): Promise { const type = NoticeType.GUEST_REQUEST_REJECTED; - // TODO: + const notices: SavedNotice[] = guests.map((userNo) => { return { userNo, targetUserNo, type }; }); @@ -626,10 +626,7 @@ export class BoardsService { } } - private async validateHost( - hostUserNo: number, - userNo: number, - ): Promise { + private validateHost(hostUserNo: number, userNo: number): void { if (userNo != hostUserNo) { throw new BadRequestException( `작성자 검증 (validateHost-service): 작성자와 사용자가 일치하지 않습니다.`, @@ -789,7 +786,7 @@ export class BoardsService { ): Promise { const board: Board = await this.getBoard(manager, boardNo); - await this.validateHost(board.hostUserNo, userNo); + this.validateHost(board.hostUserNo, userNo); await this.validateRecruits(manager, board, updateBoardDto); } } diff --git a/main-project/src/enquiries/enquiries.service.ts b/main-project/src/enquiries/enquiries.service.ts index 2df959ab..5b3ae48d 100644 --- a/main-project/src/enquiries/enquiries.service.ts +++ b/main-project/src/enquiries/enquiries.service.ts @@ -1,11 +1,6 @@ -import { - Injectable, - InternalServerErrorException, - NotFoundException, -} from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { BadRequestException } from '@nestjs/common/exceptions'; import { ConfigService } from '@nestjs/config'; -import { write } from 'fs'; import { ResultSetHeader } from 'mysql2'; import { AwsService } from 'src/aws/aws.service'; import { Users } from 'src/users/entity/user.entity'; @@ -29,7 +24,7 @@ export class EnquiriesService { private readonly configService: ConfigService, ) {} - ADMIN_USER: number = Number(this.configService.get('ADMIN_USER')); + ADMIN_USER: number = this.configService.get('ADMIN_USER'); // Get Methods async getEnquiries( @@ -82,7 +77,7 @@ export class EnquiriesService { ); } - await this.validateWriter(userNo, enquiry.userNo); + this.validateWriter(userNo, enquiry.userNo); return enquiry; } @@ -109,7 +104,7 @@ export class EnquiriesService { userNo, ); - await this.validateWriter(userNo, enquiry.userNo); + this.validateWriter(userNo, enquiry.userNo); const reply: Reply = await this.readReply(manager, enquiryNo); @@ -169,7 +164,7 @@ export class EnquiriesService { imageUrls: string[], enquiryNo: number, ): Promise { - const images: ImageInfo[] = await this.convertImageArray( + const images: ImageInfo[] = this.convertImageArray( imageUrls, enquiryNo, ); @@ -219,7 +214,7 @@ export class EnquiriesService { imageUrls: string[], replyNo: number, ): Promise { - const images: ImageInfo[] = await this.convertImageArray( + const images: ImageInfo[] = this.convertImageArray( imageUrls, undefined, replyNo, @@ -252,7 +247,7 @@ export class EnquiriesService { enquiryNo, userNo, ); - await this.validateWriter(enquiry.userNo, userNo); + this.validateWriter(enquiry.userNo, userNo); await this.updateEnquiry(manager, enquiryNo, updateEnquiryDto); await this.editEnquiryimages(manager, files, enquiryNo, imageUrls); @@ -340,7 +335,7 @@ export class EnquiriesService { enquiryNo, userNo, ); - await this.validateWriter(enquiry.userNo, userNo); + this.validateWriter(enquiry.userNo, userNo); if (!imageUrls.includes(null)) { await this.awsService.deleteFiles(imageUrls); @@ -404,10 +399,7 @@ export class EnquiriesService { } //function - private async validateWriter( - userNo: number, - writerNo: number, - ): Promise { + private validateWriter(userNo: number, writerNo: number): void { if (writerNo !== userNo && this.ADMIN_USER !== userNo) { throw new BadRequestException( `사용자 검증(validateWriter-service): 잘못된 사용자의 접근입니다.`, @@ -427,11 +419,11 @@ export class EnquiriesService { } } - private async convertImageArray( + private convertImageArray( imageUrls: string[], enquiryNo?: number, replyNo?: number, - ): Promise[]> { + ): ImageInfo[] { const images: ImageInfo[] = enquiryNo ? imageUrls.map((imageUrl: string) => { return { enquiryNo, imageUrl }; diff --git a/main-project/src/enquiries/repository/enquiry-image.repository.ts b/main-project/src/enquiries/repository/enquiry-image.repository.ts index ba59949e..891a7119 100644 --- a/main-project/src/enquiries/repository/enquiry-image.repository.ts +++ b/main-project/src/enquiries/repository/enquiry-image.repository.ts @@ -1,11 +1,5 @@ import { InternalServerErrorException } from '@nestjs/common'; -import { ResultSetHeader } from 'mysql2'; -import { - DeleteResult, - EntityRepository, - InsertResult, - Repository, -} from 'typeorm'; +import { EntityRepository, Repository } from 'typeorm'; import { EnquiryImages } from '../entity/enquiry-images.entity'; import { ImageInfo } from '../interface/enquiry.interface'; diff --git a/main-project/src/reports/dto/create-reports.dto.ts b/main-project/src/reports/dto/create-reports.dto.ts index 0154e466..1d421021 100644 --- a/main-project/src/reports/dto/create-reports.dto.ts +++ b/main-project/src/reports/dto/create-reports.dto.ts @@ -1,25 +1,29 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; -export class CreateReportDto { - @IsNotEmpty() - @IsNumber() - @ApiProperty({ example: 21, description: '신고주체 사용자 번호(원고)' }) - reportingUserNo: number; - // jwt 구현되면 useGaroud로 대체 예정 - - @IsNotEmpty() - @IsString() +export class CreateReportBoardDto { @ApiProperty({ example: '개발이 너무 재밌어요 어떡하죠?', description: '신고글 제목', }) + @IsString() + @IsNotEmpty() title: string; - @IsNotEmpty() - @IsString() @ApiProperty({ example: '라고 할 뻔ㅋㅋㅋㅋㅋ', description: '신고글 내용', }) + @IsString() + @IsNotEmpty() description: string; + + @ApiProperty({ + example: 65, + description: '게시글 번호', + }) + @IsNumber() + @Type(() => Number) + @IsNotEmpty() + boardNo: number; } diff --git a/main-project/src/reports/dto/update-reports.dto.ts b/main-project/src/reports/dto/update-reports.dto.ts index 6b309320..3c4dbcbe 100644 --- a/main-project/src/reports/dto/update-reports.dto.ts +++ b/main-project/src/reports/dto/update-reports.dto.ts @@ -1,6 +1,4 @@ import { OmitType } from '@nestjs/swagger'; -import { CreateReportDto } from './create-reports.dto'; +import { CreateReportBoardDto } from './create-reports.dto'; -export class UpdateReportDto extends OmitType(CreateReportDto, [ - 'reportingUserNo', -]) {} +export class UpdateReportBoardDto extends OmitType(CreateReportBoardDto, []) {} diff --git a/main-project/src/reports/interface/reports.interface.ts b/main-project/src/reports/interface/reports.interface.ts index 6d7dc178..0708a831 100644 --- a/main-project/src/reports/interface/reports.interface.ts +++ b/main-project/src/reports/interface/reports.interface.ts @@ -1,5 +1,5 @@ export interface Report { - no: number; + no?: number; title: string; description: string; userNo: number; @@ -8,3 +8,8 @@ export interface Report { createdDate?: Date; imageUrls?: T; } + +export interface ReportImage { + imageUrl: T; + announceNo?: number; +} diff --git a/main-project/src/reports/reports.controller.ts b/main-project/src/reports/reports.controller.ts index c67ecae5..ff1e1e1c 100644 --- a/main-project/src/reports/reports.controller.ts +++ b/main-project/src/reports/reports.controller.ts @@ -8,19 +8,23 @@ import { Patch, Post, Query, + UploadedFiles, UseGuards, UseInterceptors, } from '@nestjs/common'; +import { FilesInterceptor } from '@nestjs/platform-express'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { GetUser } from 'src/common/decorator/get-user.decorator'; import { TransactionDecorator } from 'src/common/decorator/transaction-manager.decorator'; import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; import { TransactionInterceptor } from 'src/common/interceptor/transaction-interceptor'; import { EntityManager } from 'typeorm'; -import { CreateReportDto } from './dto/create-reports.dto'; +import { CreateReportBoardDto } from './dto/create-reports.dto'; import { ReportFilterDto } from './dto/report-filter.dto'; -import { UpdateReportDto } from './dto/update-reports.dto'; +import { UpdateReportBoardDto } from './dto/update-reports.dto'; import { Report } from './interface/reports.interface'; import { ReportsService } from './reports.service'; +import { ApiCreateReportBoard } from './swagger-decorator/create-board-report.decorator'; import { ApiGetReport } from './swagger-decorator/get-report.decorator'; import { ApiGetReports } from './swagger-decorator/get-reports.decorator'; @@ -62,28 +66,28 @@ export class ReportsController { } // Post - @Post('/boards/:boardNo') + @Post('/boards') @UseGuards(JwtAuthGuard) @UseInterceptors(TransactionInterceptor) - @ApiOperation({ - summary: '게시글 신고 생성 API', - description: '입력된 정보로 게시글 신고 생성.', - }) + @UseInterceptors(FilesInterceptor('files', 10)) + @ApiCreateReportBoard() async createBoardReport( - @Param('boardNo', ParseIntPipe) boardNo: number, @TransactionDecorator() manager: EntityManager, - @Body() createReportDto: CreateReportDto, + @Body() createReportDto: CreateReportBoardDto, + @GetUser() userNo: number, + @UploadedFiles() files: Express.Multer.File[], ): Promise { await this.reportsService.createBoardReport( manager, createReportDto, - boardNo, + files, + userNo, ); return { msg: '게시글 신고 성공' }; } - @Post('/users/:userNo') + @Post('/users') @UseGuards(JwtAuthGuard) @UseInterceptors(TransactionInterceptor) @ApiOperation({ @@ -93,7 +97,7 @@ export class ReportsController { async createUserReport( @Param('userNo', ParseIntPipe) userNo: number, @TransactionDecorator() manager: EntityManager, - @Body() createReportDto: CreateReportDto, + @Body() createReportDto: CreateReportBoardDto, ): Promise { await this.reportsService.createUserReport( manager, @@ -115,7 +119,7 @@ export class ReportsController { async updateBoard( @Param('reportNo', ParseIntPipe) reportNo: number, @TransactionDecorator() manager: EntityManager, - @Body() updateReportDto: UpdateReportDto, + @Body() updateReportDto: UpdateReportBoardDto, ): Promise { await this.reportsService.updateReport(manager, reportNo, updateReportDto); @@ -132,11 +136,13 @@ export class ReportsController { }) async deleteReportByNo( @Param('reportNo', ParseIntPipe) reportNo: number, + @GetUser() userNo: number, @TransactionDecorator() manager: EntityManager, ): Promise { - const response = await this.reportsService.deleteReportByNo( + const response = await this.reportsService.deleteReport( manager, reportNo, + userNo, ); return { response }; diff --git a/main-project/src/reports/reports.module.ts b/main-project/src/reports/reports.module.ts index 1f5ace9d..2054de1f 100644 --- a/main-project/src/reports/reports.module.ts +++ b/main-project/src/reports/reports.module.ts @@ -7,6 +7,7 @@ import { ReportsService } from './reports.service'; import { ReportBoardRepository } from './repository/report-board.repository'; import { ReportRepository } from './repository/reports.repository'; import { ReportUserRepository } from './repository/report-user.repository'; +import { AwsService } from 'src/aws/aws.service'; @Module({ imports: [ @@ -18,7 +19,7 @@ import { ReportUserRepository } from './repository/report-user.repository'; ReportUserRepository, ]), ], - providers: [ReportsService], + providers: [ReportsService, AwsService], controllers: [ReportsController], }) export class ReportsModule {} diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index 39f25b4e..b803c0a4 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -1,24 +1,30 @@ import { BadRequestException, Injectable, - InternalServerErrorException, NotFoundException, } from '@nestjs/common'; import { Board } from 'src/boards/interface/boards.interface'; import { BoardsRepository } from 'src/boards/repository/board.repository'; import { EntityManager } from 'typeorm'; -import { CreateReportDto } from './dto/create-reports.dto'; -import { UpdateReportDto } from './dto/update-reports.dto'; -import { Report } from './interface/reports.interface'; +import { CreateReportBoardDto } from './dto/create-reports.dto'; +import { UpdateReportBoardDto } from './dto/update-reports.dto'; +import { Report, ReportImage } from './interface/reports.interface'; import { ReportBoardRepository } from './repository/report-board.repository'; import { ReportRepository } from './repository/reports.repository'; import { ReportUserRepository } from './repository/report-user.repository'; import { ResultSetHeader } from 'mysql2'; import { ReportFilterDto } from './dto/report-filter.dto'; +import { AwsService } from 'src/aws/aws.service'; +import { ConfigService } from '@nestjs/config'; +import { ReportBoardImagesRepository } from './repository/report-user-image.repository'; @Injectable() export class ReportsService { - constructor(private readonly boardRepository: BoardsRepository) {} + constructor( + private readonly boardRepository: BoardsRepository, + private readonly awsService: AwsService, + private readonly configService: ConfigService, + ) {} // 조회 관련 async getReports( manager: EntityManager, @@ -66,33 +72,64 @@ export class ReportsService { // 생성 관련 async createBoardReport( manager: EntityManager, - createReportDto: CreateReportDto, - boardNo: number, + { boardNo, ...createReportDto }: CreateReportBoardDto, + files: Express.Multer.File[], + userNo: number, ): Promise { - const { no }: Board = await this.boardRepository.getBoardByNo( + await this.validateBoard(manager, boardNo); + + const reportNo: number = await this.setReport(manager, { + ...createReportDto, + userNo, + }); + const boardReportedNo: number = await this.setBoardReport( + manager, boardNo, + reportNo, ); - if (!no) { - throw new BadRequestException( - `게시글 신고 생성(createBoardReport-service): ${boardNo}번 게시글을 찾을 수 없습니다.`, - ); - } - const reportNo: number = await this.setReport(manager, createReportDto); + if (files.length) { + const imageUrls: string[] = await this.uploadImages(files); + await this.setReportBoardImages(manager, imageUrls, boardReportedNo); + } + } - await manager + private async setBoardReport( + manager: EntityManager, + boardNo: number, + reportNo: number, + ): Promise { + const { insertId }: ResultSetHeader = await manager .getCustomRepository(ReportBoardRepository) .createBoardReport(reportNo, boardNo); + + return insertId; + } + + private async setReportBoardImages( + manager: EntityManager, + imageUrls: string[], + reportBoardNo: number, + ): Promise { + const images: ReportImage[] = this.convertReportBoardImageArray( + imageUrls, + reportBoardNo, + ); + + await manager + .getCustomRepository(ReportBoardImagesRepository) + .createBoardReportImages(images); } async createUserReport( manager: EntityManager, - createReportDto: CreateReportDto, + createReportDto: CreateReportBoardDto, userNo: number, ): Promise { - // TODO: User 확인 Method 사용 부분 - - const reportNo: number = await this.setReport(manager, createReportDto); + const reportNo: number = await this.setReport(manager, { + ...createReportDto, + userNo, + }); await manager .getCustomRepository(ReportUserRepository) @@ -101,11 +138,11 @@ export class ReportsService { private async setReport( manager: EntityManager, - createReportDto: CreateReportDto, + report: Report, ): Promise { const { insertId }: ResultSetHeader = await manager .getCustomRepository(ReportRepository) - .createReport(createReportDto); + .createReport(report); return insertId; } @@ -114,7 +151,7 @@ export class ReportsService { async updateReport( manager: EntityManager, reportNo: number, - updateReportDto: UpdateReportDto, + updateReportDto: UpdateReportBoardDto, ): Promise { await this.getReport(manager, reportNo); @@ -124,11 +161,73 @@ export class ReportsService { } // 삭제 관련 - async deleteReportByNo( + async deleteReport( + manager: EntityManager, + reportNo: number, + userNo: number, + ): Promise { + const { imageUrls, ...report }: Report = await this.getReport( + manager, + reportNo, + ); + this.validateWriter(userNo, report.userNo); + + if (!imageUrls.includes(null)) { + await this.awsService.deleteFiles(imageUrls); + } + await this.removeReport(manager, reportNo); + } + + private async removeReport( manager: EntityManager, reportNo: number, ): Promise { - await this.getReport(manager, reportNo); await manager.getCustomRepository(ReportRepository).deleteReport(reportNo); } + + // functions + private validateWriter(userNo: number, writerNo: number): void { + const ADMIN_USER = this.configService.get('ADMIN_USER'); + + if (writerNo !== userNo && ADMIN_USER !== userNo) { + throw new BadRequestException( + `사용자 검증(validateWriter-service): 잘못된 사용자의 접근입니다.`, + ); + } + } + + private convertReportBoardImageArray( + imageUrls: string[], + reportBoardNo: number, + ): ReportImage[] { + const images: ReportImage[] = imageUrls.map((imageUrl: string) => { + return { reportBoardNo, imageUrl }; + }); + + return images; + } + + private async validateBoard( + manager: EntityManager, + boardNo: number, + ): Promise { + const { no }: Board = await manager + .getCustomRepository(BoardsRepository) + .getBoardByNo(boardNo); + if (!no) { + throw new BadRequestException( + `게시글 신고 생성(validateBoard-service): ${boardNo}번 게시글을 찾을 수 없습니다.`, + ); + } + } + + // s3 + private async uploadImages(files: Express.Multer.File[]): Promise { + const imageUrls: string[] = await this.awsService.uploadImages( + files, + 'report', + ); + + return imageUrls; + } } diff --git a/main-project/src/reports/repository/report-board-image.repository.ts b/main-project/src/reports/repository/report-board-image.repository.ts new file mode 100644 index 00000000..e69de29b diff --git a/main-project/src/reports/repository/report-board.repository.ts b/main-project/src/reports/repository/report-board.repository.ts index 87cfb63f..25baca5e 100644 --- a/main-project/src/reports/repository/report-board.repository.ts +++ b/main-project/src/reports/repository/report-board.repository.ts @@ -1,17 +1,23 @@ import { InternalServerErrorException } from '@nestjs/common'; -import { EntityRepository, Repository } from 'typeorm'; +import { ResultSetHeader } from 'mysql2'; +import { EntityRepository, InsertResult, Repository } from 'typeorm'; import { ReportBoards } from '../entity/report-board.entity'; @EntityRepository(ReportBoards) export class ReportBoardRepository extends Repository { // 신고글 작성 관련 - async createBoardReport(reportNo: number, boardNo: number): Promise { + async createBoardReport( + reportNo: number, + boardNo: number, + ): Promise { try { - await this.createQueryBuilder() + const { raw }: InsertResult = await this.createQueryBuilder() .insert() .into(ReportBoards) .values({ reportNo, targetBoardNo: boardNo }) .execute(); + + return raw; } catch (error) { throw new InternalServerErrorException( `${error} createBoardReport-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/reports/repository/report-user-image.repository.ts b/main-project/src/reports/repository/report-user-image.repository.ts new file mode 100644 index 00000000..193411b4 --- /dev/null +++ b/main-project/src/reports/repository/report-user-image.repository.ts @@ -0,0 +1,22 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { EntityRepository, Repository } from 'typeorm'; +import { ReportBoardImages } from '../entity/report-board-images.entity'; +import { ReportImage } from '../interface/reports.interface'; + +@EntityRepository(ReportBoardImages) +export class ReportBoardImagesRepository extends Repository { + // 신고글 작성 관련 + async createBoardReportImages(images: ReportImage[]): Promise { + try { + await this.createQueryBuilder() + .insert() + .into(ReportBoardImages) + .values(images) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} createBoardReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } +} diff --git a/main-project/src/reports/repository/reports.repository.ts b/main-project/src/reports/repository/reports.repository.ts index f49446d0..7e10f47d 100644 --- a/main-project/src/reports/repository/reports.repository.ts +++ b/main-project/src/reports/repository/reports.repository.ts @@ -1,15 +1,14 @@ import { InternalServerErrorException } from '@nestjs/common'; import { ResultSetHeader } from 'mysql2'; import { - DeleteResult, EntityRepository, InsertResult, Repository, SelectQueryBuilder, } from 'typeorm'; -import { CreateReportDto } from '../dto/create-reports.dto'; +import { CreateReportBoardDto } from '../dto/create-reports.dto'; import { ReportFilterDto } from '../dto/report-filter.dto'; -import { UpdateReportDto } from '../dto/update-reports.dto'; +import { UpdateReportBoardDto } from '../dto/update-reports.dto'; import { Reports } from '../entity/reports.entity'; import { Report } from '../interface/reports.interface'; @@ -78,7 +77,7 @@ export class ReportRepository extends Repository { 'reports.title AS title', 'reports.description AS description', 'DATE_FORMAT(reports.createdDate, "%Y.%m.%d %T") AS createdDate', - `IF(reportedBoards.reportNo = ${reportNo}, reportedBoards.targetBoardNo, NULL) AS targetBoardNo`, + `IF(reportedBoards.reportNo = ${reportNo}, reportedBoards.targetBoardNo, NULL) `, // 'reportedBoards.targetBoardNo as targetBoardNo', // 'reportedUsers.targetUserNo as targetUserNo', @@ -96,7 +95,7 @@ export class ReportRepository extends Repository { // 신고글 작성 관련 async createReport( - createReportDto: CreateReportDto, + createReportDto: Omit, ): Promise { try { const { raw }: InsertResult = await this.createQueryBuilder() @@ -107,7 +106,7 @@ export class ReportRepository extends Repository { return raw; } catch (error) { throw new InternalServerErrorException( - `${error} createBoard-repository: 알 수 없는 서버 에러입니다.`, + `${error} createReport-repository: 알 수 없는 서버 에러입니다.`, ); } } @@ -115,7 +114,7 @@ export class ReportRepository extends Repository { //게시글 수정 관련 async updateReport( reportNo: number, - updateReportDto: UpdateReportDto, + updateReportDto: UpdateReportBoardDto, ): Promise { try { await this.createQueryBuilder() diff --git a/main-project/src/reports/swagger-decorator/create-board-report.decorator.ts b/main-project/src/reports/swagger-decorator/create-board-report.decorator.ts new file mode 100644 index 00000000..08d2636e --- /dev/null +++ b/main-project/src/reports/swagger-decorator/create-board-report.decorator.ts @@ -0,0 +1,64 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiBody, + ApiConsumes, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiCreateReportBoard() { + return applyDecorators( + ApiOperation({ + summary: '게시글 신고 생성', + }), + ApiBearerAuth(), + ApiBody({ + schema: { + type: 'object', + properties: { + title: { + type: 'string', + example: '광고성 글이네요 광고회사 사이트인 줄ㅋㅋㅋㅋ', + minLength: 2, + maxLength: 255, + nullable: false, + description: '게시글 신고 제목', + }, + description: { + type: 'string', + example: '@@@@@@@@@@@광고광고빔 빰빠마ㅃ마빠빠마빠빰', + minLength: 2, + maxLength: 255, + nullable: false, + description: '게시글 신고 내용', + }, + boardNo: { + type: 'number', + example: 54, + description: '신고할 게시글 번호', + }, + }, + }, + }), + ApiOkResponse( + SwaggerApiResponse.success( + 'Api 작동 성공 msg 반환', + '게시글 신고 생성 성공.', + ), + ), + ApiBadRequestResponse( + SwaggerApiResponse.exception([ + { + name: 'boardNotFound', + example: { + msg: `게시글 신고 생성(validateBoard-service): 3번 게시글을 찾을 수 없습니다.`, + }, + }, + ]), + ), + ); +} From e60697c00e7171f28bfb6cb4cbf31a550175f0e2 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:17:07 +0900 Subject: [PATCH 05/23] =?UTF-8?q?Refactor(minho/report):=20=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=20=EC=82=AD=EC=A0=9C=20=EB=A6=AC=ED=8C=A9=ED=84=B8?= =?UTF-8?q?=EC=9D=B4=20#194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/reports/entity/reports.entity.ts | 8 +++- .../src/reports/reports.controller.ts | 12 ++--- main-project/src/reports/reports.service.ts | 22 +++++++-- .../delete-report.decorator.ts | 47 +++++++++++++++++++ 4 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 main-project/src/reports/swagger-decorator/delete-report.decorator.ts diff --git a/main-project/src/reports/entity/reports.entity.ts b/main-project/src/reports/entity/reports.entity.ts index 63a46052..75d14329 100644 --- a/main-project/src/reports/entity/reports.entity.ts +++ b/main-project/src/reports/entity/reports.entity.ts @@ -31,10 +31,14 @@ export class Reports extends BaseEntity { @DeleteDateColumn({ name: 'deleted_date' }) deletedDate: Date; - @OneToOne((type) => ReportBoards, (reportBoards) => reportBoards.reportNo) + @OneToOne((type) => ReportBoards, (reportBoards) => reportBoards.reportNo, { + onDelete: 'CASCADE', + }) reportedBoard: number; - @OneToOne((type) => ReportUsers, (reportUser) => reportUser.reportNo) + @OneToOne((type) => ReportUsers, (reportUser) => reportUser.reportNo, { + onDelete: 'CASCADE', + }) reportedUser: number; @ManyToOne((type) => Users, (user) => user.report, { onDelete: 'CASCADE' }) diff --git a/main-project/src/reports/reports.controller.ts b/main-project/src/reports/reports.controller.ts index ff1e1e1c..8328d320 100644 --- a/main-project/src/reports/reports.controller.ts +++ b/main-project/src/reports/reports.controller.ts @@ -25,6 +25,7 @@ import { UpdateReportBoardDto } from './dto/update-reports.dto'; import { Report } from './interface/reports.interface'; import { ReportsService } from './reports.service'; import { ApiCreateReportBoard } from './swagger-decorator/create-board-report.decorator'; +import { ApiDeleteReport } from './swagger-decorator/delete-report.decorator'; import { ApiGetReport } from './swagger-decorator/get-report.decorator'; import { ApiGetReports } from './swagger-decorator/get-reports.decorator'; @@ -130,21 +131,18 @@ export class ReportsController { @Delete('/:reportNo') @UseGuards(JwtAuthGuard) @UseInterceptors(TransactionInterceptor) - @ApiOperation({ - summary: '특정 신고내역 삭제 API', - description: '신고 번호를 사용하여 해당 신고내역을 삭제한다.', - }) - async deleteReportByNo( + @ApiDeleteReport() + async deleteReport( @Param('reportNo', ParseIntPipe) reportNo: number, @GetUser() userNo: number, @TransactionDecorator() manager: EntityManager, ): Promise { - const response = await this.reportsService.deleteReport( + const report = await this.reportsService.deleteReport( manager, reportNo, userNo, ); - return { response }; + return { msg: '게시글 신고 삭제 성공' }; } } diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index b803c0a4..2852543d 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -17,6 +17,8 @@ import { ReportFilterDto } from './dto/report-filter.dto'; import { AwsService } from 'src/aws/aws.service'; import { ConfigService } from '@nestjs/config'; import { ReportBoardImagesRepository } from './repository/report-user-image.repository'; +import { Users } from 'src/users/entity/user.entity'; +import { UsersRepository } from 'src/users/repository/users.repository'; @Injectable() export class ReportsService { @@ -25,6 +27,9 @@ export class ReportsService { private readonly awsService: AwsService, private readonly configService: ConfigService, ) {} + + ADMIN_USER = this.configService.get('ADMIN_USER'); + // 조회 관련 async getReports( manager: EntityManager, @@ -170,6 +175,7 @@ export class ReportsService { manager, reportNo, ); + this.validateWriter(userNo, report.userNo); if (!imageUrls.includes(null)) { @@ -187,9 +193,7 @@ export class ReportsService { // functions private validateWriter(userNo: number, writerNo: number): void { - const ADMIN_USER = this.configService.get('ADMIN_USER'); - - if (writerNo !== userNo && ADMIN_USER !== userNo) { + if (writerNo !== userNo && this.ADMIN_USER !== userNo) { throw new BadRequestException( `사용자 검증(validateWriter-service): 잘못된 사용자의 접근입니다.`, ); @@ -221,6 +225,18 @@ export class ReportsService { } } + private async validateAdmin(manager: EntityManager, userNo: number) { + const { no }: Users = await manager + .getCustomRepository(UsersRepository) + .getUserByNo(userNo); + + if (no !== this.ADMIN_USER) { + throw new BadRequestException( + '관리자 검증(validateAdmin-service): 관리자가 아닙니다.', + ); + } + } + // s3 private async uploadImages(files: Express.Multer.File[]): Promise { const imageUrls: string[] = await this.awsService.uploadImages( diff --git a/main-project/src/reports/swagger-decorator/delete-report.decorator.ts b/main-project/src/reports/swagger-decorator/delete-report.decorator.ts new file mode 100644 index 00000000..af24f75e --- /dev/null +++ b/main-project/src/reports/swagger-decorator/delete-report.decorator.ts @@ -0,0 +1,47 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiDeleteReport() { + return applyDecorators( + ApiOperation({ + summary: '게시글 신고 삭제', + }), + ApiBearerAuth(), + ApiOkResponse( + SwaggerApiResponse.success( + 'Api 작동 성공 msg 반환', + '게시글 신고 삭제 성공', + ), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'boardNotFound', + example: { msg: `존재하지 않는 게시글 번호입니다.` }, + }, + { + name: 'boardReportNotFound', + example: { msg: `존재하지 않는 게시글 신고 번호입니다.` }, + }, + ]), + ), + ApiBadRequestResponse( + SwaggerApiResponse.exception([ + { + name: 'isNotWriter', + example: { + msg: `사용자 검증(deleteReport-service): 잘못된 사용자의 접근입니다.`, + }, + }, + ]), + ), + ); +} From 75316c671c3622f3c177ef5a3127a0604bdb1f14 Mon Sep 17 00:00:00 2001 From: JUHA <84626225+khabh@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:42:37 +0900 Subject: [PATCH 06/23] =?UTF-8?q?Refactor(juha):=20=EC=B4=88=EA=B8=B0=20?= =?UTF-8?q?=EB=A7=A4=EB=84=88=20=ED=8F=89=EC=A0=90=20=EB=B0=8F=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main-project/src/manners/entity/manners.entity.ts | 4 ++-- main-project/src/users/entity/user-profile.entity.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main-project/src/manners/entity/manners.entity.ts b/main-project/src/manners/entity/manners.entity.ts index ac2b7f74..efcf53d1 100644 --- a/main-project/src/manners/entity/manners.entity.ts +++ b/main-project/src/manners/entity/manners.entity.ts @@ -13,10 +13,10 @@ export class Manners extends BaseEntity { @PrimaryGeneratedColumn() no: number; - @Column({ name: 'grade', default: 0 }) + @Column({ name: 'grade', default: 45 }) grade: number; - @Column({ name: 'grade_count', default: 0 }) + @Column({ name: 'grade_count', default: 10 }) gradeCount: number; @OneToOne((type) => Users, (users) => users.mannerNo, { diff --git a/main-project/src/users/entity/user-profile.entity.ts b/main-project/src/users/entity/user-profile.entity.ts index f8cca20a..da5decc2 100644 --- a/main-project/src/users/entity/user-profile.entity.ts +++ b/main-project/src/users/entity/user-profile.entity.ts @@ -22,7 +22,7 @@ export class UserProfile extends BaseEntity { @JoinColumn({ name: 'user_no' }) userNo: number; - @Column({ type: 'varchar', length: 45 }) + @Column({ type: 'varchar', length: 10 }) nickname: string; @Column({ type: 'boolean', width: 1, default: false }) From 63f0350f6748be1019295285f3cac896927dce3d Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:52:52 +0900 Subject: [PATCH 07/23] =?UTF-8?q?Refactor(minho/report):=20=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=EB=82=B4=EC=97=AD=20=EC=83=81=EC=84=B8=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81=20#194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/boards/repository/board.repository.ts | 61 +++++++++---------- main-project/src/reports/reports.service.ts | 3 +- .../report-board-image.repository.ts | 22 +++++++ .../report-user-image.repository.ts | 22 ------- .../reports/repository/reports.repository.ts | 43 +++++++------ 5 files changed, 77 insertions(+), 74 deletions(-) diff --git a/main-project/src/boards/repository/board.repository.ts b/main-project/src/boards/repository/board.repository.ts index 187598cf..2ff035bc 100644 --- a/main-project/src/boards/repository/board.repository.ts +++ b/main-project/src/boards/repository/board.repository.ts @@ -41,43 +41,40 @@ export class BoardsRepository extends Repository { async getBoardByNo(no: number): Promise> { try { - const { - hostMemberNums, - hostMemberNicknames, - ...jsonBoard - }: Board = await this.createQueryBuilder('boards') - .leftJoin('boards.userNo', 'users') - .leftJoin('users.userProfileNo', 'profile') - .leftJoin('boards.hosts', 'hosts') - .leftJoin('hosts.userNo', 'hostUsers') - .leftJoin('hostUsers.userProfileNo', 'hostProfile') - .select([ - 'boards.no AS no', - 'boards.userNo AS hostUserNo', - 'profile.nickname AS hostNickname', - 'boards.title AS title', - 'boards.description AS description', - 'boards.location AS location', - 'boards.isDone AS isDone', - 'boards.recruitMale AS recruitMale', - 'boards.recruitFemale AS recruitFemale', - 'boards.isImpromptu AS isImpromptu', - `DATE_FORMAT(boards.meetingTime, '%Y.%m.%d %T') AS meetingTime`, - `DATE_FORMAT(boards.createdDate, '%Y.%m.%d %T') AS createdDate`, - 'JSON_ARRAYAGG(hosts.userNo) AS hostMemberNums', - 'JSON_ARRAYAGG(hostProfile.nickname) AS hostMemberNicknames', - ]) - .where('boards.no = :no', { no }) - .andWhere('hosts.board_no = :no', { no }) - .getRawOne(); + const { hostMemberNums, hostMemberNicknames, ...board }: Board = + await this.createQueryBuilder('boards') + .leftJoin('boards.userNo', 'users') + .leftJoin('users.userProfileNo', 'profile') + .leftJoin('boards.hosts', 'hosts') + .leftJoin('hosts.userNo', 'hostUsers') + .leftJoin('hostUsers.userProfileNo', 'hostProfile') + .select([ + 'boards.no AS no', + 'boards.userNo AS hostUserNo', + 'profile.nickname AS hostNickname', + 'boards.title AS title', + 'boards.description AS description', + 'boards.location AS location', + 'boards.isDone AS isDone', + 'boards.recruitMale AS recruitMale', + 'boards.recruitFemale AS recruitFemale', + 'boards.isImpromptu AS isImpromptu', + `DATE_FORMAT(boards.meetingTime, '%Y.%m.%d %T') AS meetingTime`, + `DATE_FORMAT(boards.createdDate, '%Y.%m.%d %T') AS createdDate`, + 'JSON_ARRAYAGG(hosts.userNo) AS hostMemberNums', + 'JSON_ARRAYAGG(hostProfile.nickname) AS hostMemberNicknames', + ]) + .where('boards.no = :no', { no }) + .andWhere('hosts.board_no = :no', { no }) + .getRawOne(); - const board: Board = { - ...jsonBoard, + const convertBoard: Board = { + ...board, hostMemberNums: JSON.parse(hostMemberNums), hostMemberNicknames: JSON.parse(hostMemberNicknames), }; - return board; + return convertBoard; } catch (error) { throw new InternalServerErrorException( `${error} getBoard-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index 2852543d..1c02c9a6 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -16,14 +16,13 @@ import { ResultSetHeader } from 'mysql2'; import { ReportFilterDto } from './dto/report-filter.dto'; import { AwsService } from 'src/aws/aws.service'; import { ConfigService } from '@nestjs/config'; -import { ReportBoardImagesRepository } from './repository/report-user-image.repository'; +import { ReportBoardImagesRepository } from './repository/report-board-image.repository'; import { Users } from 'src/users/entity/user.entity'; import { UsersRepository } from 'src/users/repository/users.repository'; @Injectable() export class ReportsService { constructor( - private readonly boardRepository: BoardsRepository, private readonly awsService: AwsService, private readonly configService: ConfigService, ) {} diff --git a/main-project/src/reports/repository/report-board-image.repository.ts b/main-project/src/reports/repository/report-board-image.repository.ts index e69de29b..193411b4 100644 --- a/main-project/src/reports/repository/report-board-image.repository.ts +++ b/main-project/src/reports/repository/report-board-image.repository.ts @@ -0,0 +1,22 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { EntityRepository, Repository } from 'typeorm'; +import { ReportBoardImages } from '../entity/report-board-images.entity'; +import { ReportImage } from '../interface/reports.interface'; + +@EntityRepository(ReportBoardImages) +export class ReportBoardImagesRepository extends Repository { + // 신고글 작성 관련 + async createBoardReportImages(images: ReportImage[]): Promise { + try { + await this.createQueryBuilder() + .insert() + .into(ReportBoardImages) + .values(images) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} createBoardReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } +} diff --git a/main-project/src/reports/repository/report-user-image.repository.ts b/main-project/src/reports/repository/report-user-image.repository.ts index 193411b4..e69de29b 100644 --- a/main-project/src/reports/repository/report-user-image.repository.ts +++ b/main-project/src/reports/repository/report-user-image.repository.ts @@ -1,22 +0,0 @@ -import { InternalServerErrorException } from '@nestjs/common'; -import { EntityRepository, Repository } from 'typeorm'; -import { ReportBoardImages } from '../entity/report-board-images.entity'; -import { ReportImage } from '../interface/reports.interface'; - -@EntityRepository(ReportBoardImages) -export class ReportBoardImagesRepository extends Repository { - // 신고글 작성 관련 - async createBoardReportImages(images: ReportImage[]): Promise { - try { - await this.createQueryBuilder() - .insert() - .into(ReportBoardImages) - .values(images) - .execute(); - } catch (error) { - throw new InternalServerErrorException( - `${error} createBoardReportImages-repository: 알 수 없는 서버 에러입니다.`, - ); - } - } -} diff --git a/main-project/src/reports/repository/reports.repository.ts b/main-project/src/reports/repository/reports.repository.ts index 7e10f47d..fbb9702d 100644 --- a/main-project/src/reports/repository/reports.repository.ts +++ b/main-project/src/reports/repository/reports.repository.ts @@ -1,5 +1,6 @@ import { InternalServerErrorException } from '@nestjs/common'; import { ResultSetHeader } from 'mysql2'; +import { report } from 'process'; import { EntityRepository, InsertResult, @@ -66,26 +67,32 @@ export class ReportRepository extends Repository { async getReport(reportNo: number): Promise> { try { - const report: Report = await this.createQueryBuilder('reports') - .leftJoin('reports.reportedBoard', 'reportedBoards') - .leftJoin('reports.reportedUser', 'reportedUsers') - .leftJoin('reportedBoards.reportBoardImage', 'reportBoardImages') - .leftJoin('reportedUsers.reportUserImage', 'reportUserImages') - .select([ - 'reports.no AS no', - 'reports.userNo AS userNo', - 'reports.title AS title', - 'reports.description AS description', - 'DATE_FORMAT(reports.createdDate, "%Y.%m.%d %T") AS createdDate', - `IF(reportedBoards.reportNo = ${reportNo}, reportedBoards.targetBoardNo, NULL) `, + const { imageUrls, ...report }: Report = + await this.createQueryBuilder('reports') + .leftJoin('reports.reportedBoard', 'reportedBoards') + .leftJoin('reports.reportedUser', 'reportedUsers') + .leftJoin('reportedBoards.reportBoardImage', 'reportBoardImages') + .leftJoin('reportedUsers.reportUserImage', 'reportUserImages') + .select([ + 'reports.no AS no', + 'reports.userNo AS userNo', + 'reports.title AS title', + 'reports.description AS description', + 'DATE_FORMAT(reports.createdDate, "%Y.%m.%d %T") AS createdDate', + 'reportedBoards.targetBoardNo AS targetBoardNo', + 'reportedUsers.targetUserNo AS targetUserNo', + `IF(reportedBoards.reportNo = ${reportNo}, JSON_ARRAYAGG(reportBoardImages.imageUrl), JSON_ARRAYAGG(reportUserImages.imageUrl)) AS imageUrls`, + ]) + .where('reports.no = :reportNo', { reportNo }) + .groupBy('reports.no') + .getRawOne(); - // 'reportedBoards.targetBoardNo as targetBoardNo', - // 'reportedUsers.targetUserNo as targetUserNo', - ]) - .where('reports.no = :reportNo', { reportNo }) - .getRawOne(); + const convertReport: Report = { + ...report, + imageUrls: JSON.parse(imageUrls), + }; - return report; + return convertReport; } catch (error) { throw new InternalServerErrorException( `${error} getReport-repository: 알 수 없는 서버 에러입니다.`, From 937b3a3d8a40e57f4f9d69ed1d0b430b81c80204 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:22:36 +0900 Subject: [PATCH 08/23] =?UTF-8?q?Refactor(minho/report):=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EC=8B=A0=EA=B3=A0=20=EC=83=9D=EC=84=B1=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B8=EC=9D=B4=20#194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...orts.dto.ts => create-report-board.dto.ts} | 0 .../src/reports/dto/create-report-user.dto.ts | 29 +++++++ ...orts.dto.ts => update-report-board.dto.ts} | 2 +- .../src/reports/dto/update-report-user.dto.ts | 0 .../src/reports/reports.controller.ts | 20 ++--- main-project/src/reports/reports.service.ts | 78 +++++++++++++++++-- .../report-board-image.repository.ts | 1 - .../report-user-image.repository.ts | 21 +++++ .../repository/report-user.repository.ts | 11 ++- .../reports/repository/reports.repository.ts | 5 +- .../create-user-report.decorator.ts | 53 +++++++++++++ 11 files changed, 196 insertions(+), 24 deletions(-) rename main-project/src/reports/dto/{create-reports.dto.ts => create-report-board.dto.ts} (100%) create mode 100644 main-project/src/reports/dto/create-report-user.dto.ts rename main-project/src/reports/dto/{update-reports.dto.ts => update-report-board.dto.ts} (65%) create mode 100644 main-project/src/reports/dto/update-report-user.dto.ts create mode 100644 main-project/src/reports/swagger-decorator/create-user-report.decorator.ts diff --git a/main-project/src/reports/dto/create-reports.dto.ts b/main-project/src/reports/dto/create-report-board.dto.ts similarity index 100% rename from main-project/src/reports/dto/create-reports.dto.ts rename to main-project/src/reports/dto/create-report-board.dto.ts diff --git a/main-project/src/reports/dto/create-report-user.dto.ts b/main-project/src/reports/dto/create-report-user.dto.ts new file mode 100644 index 00000000..b382f4ff --- /dev/null +++ b/main-project/src/reports/dto/create-report-user.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +export class CreateReportUserDto { + @ApiProperty({ + example: '개발이 너무 재밌어요 어떡하죠?', + description: '신고글 제목', + }) + @IsString() + @IsNotEmpty() + title: string; + + @ApiProperty({ + example: '라고 할 뻔ㅋㅋㅋㅋㅋ', + description: '신고글 내용', + }) + @IsString() + @IsNotEmpty() + description: string; + + @ApiProperty({ + example: 54, + description: '신고할 유저 번호', + }) + @IsNumber() + @Type(() => Number) + @IsNotEmpty() + targetUserNo: number; +} diff --git a/main-project/src/reports/dto/update-reports.dto.ts b/main-project/src/reports/dto/update-report-board.dto.ts similarity index 65% rename from main-project/src/reports/dto/update-reports.dto.ts rename to main-project/src/reports/dto/update-report-board.dto.ts index 3c4dbcbe..fba2cb91 100644 --- a/main-project/src/reports/dto/update-reports.dto.ts +++ b/main-project/src/reports/dto/update-report-board.dto.ts @@ -1,4 +1,4 @@ import { OmitType } from '@nestjs/swagger'; -import { CreateReportBoardDto } from './create-reports.dto'; +import { CreateReportBoardDto } from './create-report-board.dto'; export class UpdateReportBoardDto extends OmitType(CreateReportBoardDto, []) {} diff --git a/main-project/src/reports/dto/update-report-user.dto.ts b/main-project/src/reports/dto/update-report-user.dto.ts new file mode 100644 index 00000000..e69de29b diff --git a/main-project/src/reports/reports.controller.ts b/main-project/src/reports/reports.controller.ts index 8328d320..0f2e2d14 100644 --- a/main-project/src/reports/reports.controller.ts +++ b/main-project/src/reports/reports.controller.ts @@ -19,12 +19,14 @@ import { TransactionDecorator } from 'src/common/decorator/transaction-manager.d import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; import { TransactionInterceptor } from 'src/common/interceptor/transaction-interceptor'; import { EntityManager } from 'typeorm'; -import { CreateReportBoardDto } from './dto/create-reports.dto'; +import { CreateReportBoardDto } from './dto/create-report-board.dto'; +import { CreateReportUserDto } from './dto/create-report-user.dto'; import { ReportFilterDto } from './dto/report-filter.dto'; -import { UpdateReportBoardDto } from './dto/update-reports.dto'; +import { UpdateReportBoardDto } from './dto/update-report-board.dto'; import { Report } from './interface/reports.interface'; import { ReportsService } from './reports.service'; import { ApiCreateReportBoard } from './swagger-decorator/create-board-report.decorator'; +import { ApiCreateReportUser } from './swagger-decorator/create-user-report.decorator'; import { ApiDeleteReport } from './swagger-decorator/delete-report.decorator'; import { ApiGetReport } from './swagger-decorator/get-report.decorator'; import { ApiGetReports } from './swagger-decorator/get-reports.decorator'; @@ -91,18 +93,18 @@ export class ReportsController { @Post('/users') @UseGuards(JwtAuthGuard) @UseInterceptors(TransactionInterceptor) - @ApiOperation({ - summary: '사용자 신고 생성 API', - description: '입력된 정보로 사용자 신고 생성.', - }) + @UseInterceptors(FilesInterceptor('files', 10)) + @ApiCreateReportUser() async createUserReport( - @Param('userNo', ParseIntPipe) userNo: number, @TransactionDecorator() manager: EntityManager, - @Body() createReportDto: CreateReportBoardDto, + @Body() createReportUserDto: CreateReportUserDto, + @GetUser() userNo: number, + @UploadedFiles() files: Express.Multer.File[], ): Promise { await this.reportsService.createUserReport( manager, - createReportDto, + createReportUserDto, + files, userNo, ); diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index 1c02c9a6..8b82fe5e 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -6,8 +6,8 @@ import { import { Board } from 'src/boards/interface/boards.interface'; import { BoardsRepository } from 'src/boards/repository/board.repository'; import { EntityManager } from 'typeorm'; -import { CreateReportBoardDto } from './dto/create-reports.dto'; -import { UpdateReportBoardDto } from './dto/update-reports.dto'; +import { CreateReportBoardDto } from './dto/create-report-board.dto'; +import { UpdateReportBoardDto } from './dto/update-report-board.dto'; import { Report, ReportImage } from './interface/reports.interface'; import { ReportBoardRepository } from './repository/report-board.repository'; import { ReportRepository } from './repository/reports.repository'; @@ -19,6 +19,8 @@ import { ConfigService } from '@nestjs/config'; import { ReportBoardImagesRepository } from './repository/report-board-image.repository'; import { Users } from 'src/users/entity/user.entity'; import { UsersRepository } from 'src/users/repository/users.repository'; +import { CreateReportUserDto } from './dto/create-report-user.dto'; +import { ReportUserImagesRepository } from './repository/report-user-image.repository'; @Injectable() export class ReportsService { @@ -86,7 +88,7 @@ export class ReportsService { ...createReportDto, userNo, }); - const boardReportedNo: number = await this.setBoardReport( + const reportBoardNo: number = await this.setBoardReport( manager, boardNo, reportNo, @@ -94,7 +96,7 @@ export class ReportsService { if (files.length) { const imageUrls: string[] = await this.uploadImages(files); - await this.setReportBoardImages(manager, imageUrls, boardReportedNo); + await this.setReportBoardImages(manager, imageUrls, reportBoardNo); } } @@ -127,17 +129,53 @@ export class ReportsService { async createUserReport( manager: EntityManager, - createReportDto: CreateReportBoardDto, + { targetUserNo, ...createReportUserDto }: CreateReportUserDto, + files: Express.Multer.File[], userNo: number, ): Promise { + await this.validateUser(manager, targetUserNo); + const reportNo: number = await this.setReport(manager, { - ...createReportDto, + ...createReportUserDto, userNo, }); + const reportUserNo: number = await this.setUserReport( + manager, + targetUserNo, + reportNo, + ); - await manager + if (files.length) { + const imageUrls: string[] = await this.uploadImages(files); + await this.setReportUserImages(manager, imageUrls, reportUserNo); + } + } + + private async setUserReport( + manager: EntityManager, + userNo: number, + reportNo: number, + ): Promise { + const { insertId }: ResultSetHeader = await manager .getCustomRepository(ReportUserRepository) .createUserReport(reportNo, userNo); + + return insertId; + } + + private async setReportUserImages( + manager: EntityManager, + imageUrls: string[], + reportUserNo: number, + ): Promise { + const images: ReportImage[] = this.convertReportUserImageArray( + imageUrls, + reportUserNo, + ); + + await manager + .getCustomRepository(ReportUserImagesRepository) + .createUserReportImages(images); } private async setReport( @@ -210,6 +248,32 @@ export class ReportsService { return images; } + private convertReportUserImageArray( + imageUrls: string[], + reportUserNo: number, + ): ReportImage[] { + const images: ReportImage[] = imageUrls.map((imageUrl: string) => { + return { reportUserNo, imageUrl }; + }); + + return images; + } + + private async validateUser( + manager: EntityManager, + targetUserNo: number, + ): Promise { + const { no }: Users = await manager + .getCustomRepository(UsersRepository) + .getUserByNo(targetUserNo); + + if (!no) { + throw new BadRequestException( + '사용자 확인(validateUser-service): 존재하지 않는 사용자 입니다', + ); + } + } + private async validateBoard( manager: EntityManager, boardNo: number, diff --git a/main-project/src/reports/repository/report-board-image.repository.ts b/main-project/src/reports/repository/report-board-image.repository.ts index 193411b4..957050e0 100644 --- a/main-project/src/reports/repository/report-board-image.repository.ts +++ b/main-project/src/reports/repository/report-board-image.repository.ts @@ -5,7 +5,6 @@ import { ReportImage } from '../interface/reports.interface'; @EntityRepository(ReportBoardImages) export class ReportBoardImagesRepository extends Repository { - // 신고글 작성 관련 async createBoardReportImages(images: ReportImage[]): Promise { try { await this.createQueryBuilder() diff --git a/main-project/src/reports/repository/report-user-image.repository.ts b/main-project/src/reports/repository/report-user-image.repository.ts index e69de29b..253fe959 100644 --- a/main-project/src/reports/repository/report-user-image.repository.ts +++ b/main-project/src/reports/repository/report-user-image.repository.ts @@ -0,0 +1,21 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { EntityRepository, Repository } from 'typeorm'; +import { ReportUserImages } from '../entity/report-user-image.entity'; +import { ReportImage } from '../interface/reports.interface'; + +@EntityRepository(ReportUserImages) +export class ReportUserImagesRepository extends Repository { + async createUserReportImages(images: ReportImage[]): Promise { + try { + await this.createQueryBuilder() + .insert() + .into(ReportUserImages) + .values(images) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} createUserReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } +} diff --git a/main-project/src/reports/repository/report-user.repository.ts b/main-project/src/reports/repository/report-user.repository.ts index efa06ba5..ffbd4d25 100644 --- a/main-project/src/reports/repository/report-user.repository.ts +++ b/main-project/src/reports/repository/report-user.repository.ts @@ -1,17 +1,22 @@ import { InternalServerErrorException } from '@nestjs/common'; -import { EntityRepository, Repository } from 'typeorm'; +import { ResultSetHeader } from 'mysql2'; +import { EntityRepository, InsertResult, Repository } from 'typeorm'; import { ReportUsers } from '../entity/report-user.entity'; @EntityRepository(ReportUsers) export class ReportUserRepository extends Repository { // 신고글 작성 관련 - async createUserReport(reportNo: number, userNo: number): Promise { + async createUserReport( + reportNo: number, + userNo: number, + ): Promise { try { - await this.createQueryBuilder() + const { raw }: InsertResult = await this.createQueryBuilder() .insert() .into(ReportUsers) .values({ reportNo, targetUserNo: userNo }) .execute(); + return raw; } catch (error) { throw new InternalServerErrorException( `${error} createUserReport-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/reports/repository/reports.repository.ts b/main-project/src/reports/repository/reports.repository.ts index fbb9702d..727cf7d5 100644 --- a/main-project/src/reports/repository/reports.repository.ts +++ b/main-project/src/reports/repository/reports.repository.ts @@ -1,15 +1,14 @@ import { InternalServerErrorException } from '@nestjs/common'; import { ResultSetHeader } from 'mysql2'; -import { report } from 'process'; import { EntityRepository, InsertResult, Repository, SelectQueryBuilder, } from 'typeorm'; -import { CreateReportBoardDto } from '../dto/create-reports.dto'; +import { CreateReportBoardDto } from '../dto/create-report-board.dto'; import { ReportFilterDto } from '../dto/report-filter.dto'; -import { UpdateReportBoardDto } from '../dto/update-reports.dto'; +import { UpdateReportBoardDto } from '../dto/update-report-board.dto'; import { Reports } from '../entity/reports.entity'; import { Report } from '../interface/reports.interface'; diff --git a/main-project/src/reports/swagger-decorator/create-user-report.decorator.ts b/main-project/src/reports/swagger-decorator/create-user-report.decorator.ts new file mode 100644 index 00000000..a2b1efd9 --- /dev/null +++ b/main-project/src/reports/swagger-decorator/create-user-report.decorator.ts @@ -0,0 +1,53 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBearerAuth, + ApiBody, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiCreateReportUser() { + return applyDecorators( + ApiOperation({ + summary: '유저 신고 생성', + }), + ApiBearerAuth(), + ApiBody({ + schema: { + type: 'object', + properties: { + title: { + type: 'string', + example: '제가 고백했는데 읽씹했어요ㅠㅠㅠㅠ', + minLength: 2, + maxLength: 255, + nullable: false, + description: '유저 신고 제목', + }, + description: { + type: 'string', + example: '자다가 침대에서 굴러 떨어지게해주세요...', + minLength: 2, + maxLength: 255, + nullable: false, + description: '유저 신고 내용', + }, + targetUserNo: { + type: 'number', + example: 54, + nullable: false, + description: '신고할 유저 번호', + }, + }, + }, + }), + ApiOkResponse( + SwaggerApiResponse.success( + 'Api 작동 성공 msg 반환', + '유저 신고 생성 성공.', + ), + ), + ); +} From 0b5a20de1eb0ce8d326d48548479293c7d9167ed Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Thu, 9 Feb 2023 13:33:26 +0900 Subject: [PATCH 09/23] =?UTF-8?q?Refactor(mihno/report):=20=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=EB=82=B4=EC=97=AD=20=EC=88=98=EC=A0=95=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0=EB=A7=81=20#194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reports/dto/update-report-board.dto.ts | 4 +- .../src/reports/reports.controller.ts | 23 ++-- main-project/src/reports/reports.service.ts | 103 +++++++++++++++++- .../report-board-image.repository.ts | 14 +++ .../repository/report-board.repository.ts | 16 ++- .../report-user-image.repository.ts | 14 +++ .../repository/report-user.repository.ts | 16 ++- .../reports/repository/reports.repository.ts | 5 +- .../update-report.decorator.ts | 77 +++++++++++++ 9 files changed, 254 insertions(+), 18 deletions(-) create mode 100644 main-project/src/reports/swagger-decorator/update-report.decorator.ts diff --git a/main-project/src/reports/dto/update-report-board.dto.ts b/main-project/src/reports/dto/update-report-board.dto.ts index fba2cb91..40362c5f 100644 --- a/main-project/src/reports/dto/update-report-board.dto.ts +++ b/main-project/src/reports/dto/update-report-board.dto.ts @@ -1,4 +1,6 @@ import { OmitType } from '@nestjs/swagger'; import { CreateReportBoardDto } from './create-report-board.dto'; -export class UpdateReportBoardDto extends OmitType(CreateReportBoardDto, []) {} +export class UpdateReportDto extends OmitType(CreateReportBoardDto, [ + 'boardNo', +]) {} diff --git a/main-project/src/reports/reports.controller.ts b/main-project/src/reports/reports.controller.ts index 0f2e2d14..9edd1fc6 100644 --- a/main-project/src/reports/reports.controller.ts +++ b/main-project/src/reports/reports.controller.ts @@ -13,7 +13,7 @@ import { UseInterceptors, } from '@nestjs/common'; import { FilesInterceptor } from '@nestjs/platform-express'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { GetUser } from 'src/common/decorator/get-user.decorator'; import { TransactionDecorator } from 'src/common/decorator/transaction-manager.decorator'; import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; @@ -22,7 +22,7 @@ import { EntityManager } from 'typeorm'; import { CreateReportBoardDto } from './dto/create-report-board.dto'; import { CreateReportUserDto } from './dto/create-report-user.dto'; import { ReportFilterDto } from './dto/report-filter.dto'; -import { UpdateReportBoardDto } from './dto/update-report-board.dto'; +import { UpdateReportDto } from './dto/update-report-board.dto'; import { Report } from './interface/reports.interface'; import { ReportsService } from './reports.service'; import { ApiCreateReportBoard } from './swagger-decorator/create-board-report.decorator'; @@ -30,6 +30,7 @@ import { ApiCreateReportUser } from './swagger-decorator/create-user-report.deco import { ApiDeleteReport } from './swagger-decorator/delete-report.decorator'; import { ApiGetReport } from './swagger-decorator/get-report.decorator'; import { ApiGetReports } from './swagger-decorator/get-reports.decorator'; +import { ApiUpdateReport } from './swagger-decorator/update-report.decorator'; @Controller('reports') @ApiTags('신고 API') @@ -115,16 +116,22 @@ export class ReportsController { @Patch('/:reportNo') @UseGuards(JwtAuthGuard) @UseInterceptors(TransactionInterceptor) - @ApiOperation({ - summary: '신고내용 수정 API', - description: '입력한 정보로 신고내용을 수정한다.', - }) + @UseInterceptors(FilesInterceptor('files', 10)) + @ApiUpdateReport() async updateBoard( @Param('reportNo', ParseIntPipe) reportNo: number, @TransactionDecorator() manager: EntityManager, - @Body() updateReportDto: UpdateReportBoardDto, + @GetUser() userNo: number, + @Body() updateReportDto: UpdateReportDto, + @UploadedFiles() files: Express.Multer.File[], ): Promise { - await this.reportsService.updateReport(manager, reportNo, updateReportDto); + await this.reportsService.editReport( + manager, + reportNo, + updateReportDto, + userNo, + files, + ); return { msg: '신고내역 수정 성공' }; } diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index 8b82fe5e..0e6a3c58 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -7,7 +7,7 @@ import { Board } from 'src/boards/interface/boards.interface'; import { BoardsRepository } from 'src/boards/repository/board.repository'; import { EntityManager } from 'typeorm'; import { CreateReportBoardDto } from './dto/create-report-board.dto'; -import { UpdateReportBoardDto } from './dto/update-report-board.dto'; +import { UpdateReportDto } from './dto/update-report-board.dto'; import { Report, ReportImage } from './interface/reports.interface'; import { ReportBoardRepository } from './repository/report-board.repository'; import { ReportRepository } from './repository/reports.repository'; @@ -21,6 +21,8 @@ import { Users } from 'src/users/entity/user.entity'; import { UsersRepository } from 'src/users/repository/users.repository'; import { CreateReportUserDto } from './dto/create-report-user.dto'; import { ReportUserImagesRepository } from './repository/report-user-image.repository'; +import { ReportBoards } from './entity/report-board.entity'; +import { ReportUsers } from './entity/report-user.entity'; @Injectable() export class ReportsService { @@ -75,6 +77,28 @@ export class ReportsService { return report; } + private async readReportBoard( + manager: EntityManager, + reportNo: number, + ): Promise { + const reportBoard: ReportBoards = await manager + .getCustomRepository(ReportBoardRepository) + .getReportBoard(reportNo); + + return reportBoard; + } + + private async readReportUser( + manager: EntityManager, + reportNo: number, + ): Promise { + const reportUser: ReportUsers = await manager + .getCustomRepository(ReportUserRepository) + .getReportUser(reportNo); + + return reportUser; + } + // 생성 관련 async createBoardReport( manager: EntityManager, @@ -190,18 +214,71 @@ export class ReportsService { } //수정 관련 - async updateReport( + async editReport( manager: EntityManager, reportNo: number, - updateReportDto: UpdateReportBoardDto, + updateReportDto: UpdateReportDto, + userNo: number, + files: Express.Multer.File[], ): Promise { - await this.getReport(manager, reportNo); + const { imageUrls, ...report }: Report = await this.getReport( + manager, + reportNo, + ); + this.validateWriter(userNo, report.userNo); + + await this.updateReport(manager, reportNo, updateReportDto); + report.targetBoardNo + ? await this.editReportBoardImages(manager, files, reportNo, imageUrls) + : await this.editReportUserImages(manager, files, reportNo, imageUrls); + } + private async updateReport( + manager: EntityManager, + reportNo: number, + updateReportDto: UpdateReportDto, + ): Promise { await manager .getCustomRepository(ReportRepository) .updateReport(reportNo, updateReportDto); } + private async editReportBoardImages( + manager: EntityManager, + files: Express.Multer.File[], + reportNo: number, + imageUrls: string[], + ): Promise { + const { no }: ReportBoards = await this.readReportBoard(manager, reportNo); + + if (!imageUrls.includes(null)) { + await this.deleteReportBoardImages(manager, no); + await this.awsService.deleteFiles(imageUrls); + } + if (files.length) { + const images: string[] = await this.uploadImages(files); + await this.setReportBoardImages(manager, images, reportNo); + } + } + + private async editReportUserImages( + manager: EntityManager, + files: Express.Multer.File[], + reportNo: number, + imageUrls: string[], + ): Promise { + const { no }: ReportUsers = await this.readReportUser(manager, reportNo); + + if (!imageUrls.includes(null)) { + await this.deleteReportUserImages(manager, no); + await this.awsService.deleteFiles(imageUrls); + } + if (files.length) { + const images: string[] = await this.uploadImages(files); + await this.setReportUserImages(manager, images, reportNo); + } + } + // 삭제 관련 async deleteReport( manager: EntityManager, @@ -228,6 +305,24 @@ export class ReportsService { await manager.getCustomRepository(ReportRepository).deleteReport(reportNo); } + private async deleteReportBoardImages( + manager: EntityManager, + reportBoardNo: number, + ): Promise { + await manager + .getCustomRepository(ReportBoardImagesRepository) + .deleteBoardReportImages(reportBoardNo); + } + + private async deleteReportUserImages( + manager: EntityManager, + reportUserNo: number, + ): Promise { + await manager + .getCustomRepository(ReportUserImagesRepository) + .deleteUserReportImages(reportUserNo); + } + // functions private validateWriter(userNo: number, writerNo: number): void { if (writerNo !== userNo && this.ADMIN_USER !== userNo) { diff --git a/main-project/src/reports/repository/report-board-image.repository.ts b/main-project/src/reports/repository/report-board-image.repository.ts index 957050e0..6b0bc654 100644 --- a/main-project/src/reports/repository/report-board-image.repository.ts +++ b/main-project/src/reports/repository/report-board-image.repository.ts @@ -18,4 +18,18 @@ export class ReportBoardImagesRepository extends Repository { ); } } + + async deleteBoardReportImages(reportBoardNo: number): Promise { + try { + await this.createQueryBuilder() + .delete() + .from(ReportBoardImages) + .where('reportBoardNo = :reportBoardNo', { reportBoardNo }) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} deleteBoardReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } } diff --git a/main-project/src/reports/repository/report-board.repository.ts b/main-project/src/reports/repository/report-board.repository.ts index 25baca5e..2ab2cb09 100644 --- a/main-project/src/reports/repository/report-board.repository.ts +++ b/main-project/src/reports/repository/report-board.repository.ts @@ -5,7 +5,21 @@ import { ReportBoards } from '../entity/report-board.entity'; @EntityRepository(ReportBoards) export class ReportBoardRepository extends Repository { - // 신고글 작성 관련 + async getReportBoard(reportNo: number): Promise { + try { + const reportBoards: ReportBoards = await this.createQueryBuilder() + .select() + .where('report_no = :reportNo', { reportNo }) + .getOne(); + + return reportBoards; + } catch (error) { + throw new InternalServerErrorException( + `${error} getReportBoard-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } + async createBoardReport( reportNo: number, boardNo: number, diff --git a/main-project/src/reports/repository/report-user-image.repository.ts b/main-project/src/reports/repository/report-user-image.repository.ts index 253fe959..e06e1e68 100644 --- a/main-project/src/reports/repository/report-user-image.repository.ts +++ b/main-project/src/reports/repository/report-user-image.repository.ts @@ -18,4 +18,18 @@ export class ReportUserImagesRepository extends Repository { ); } } + + async deleteUserReportImages(reportUserNo: number): Promise { + try { + await this.createQueryBuilder() + .delete() + .from(ReportUserImages) + .where('reportUserNo = :reportUserNo', { reportUserNo }) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} deleteUserReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } } diff --git a/main-project/src/reports/repository/report-user.repository.ts b/main-project/src/reports/repository/report-user.repository.ts index ffbd4d25..0cb6b9b5 100644 --- a/main-project/src/reports/repository/report-user.repository.ts +++ b/main-project/src/reports/repository/report-user.repository.ts @@ -5,7 +5,21 @@ import { ReportUsers } from '../entity/report-user.entity'; @EntityRepository(ReportUsers) export class ReportUserRepository extends Repository { - // 신고글 작성 관련 + async getReportUser(reportNo: number): Promise { + try { + const reportUser: ReportUsers = await this.createQueryBuilder() + .select() + .where('report_no = :reportNo', { reportNo }) + .getOne(); + + return reportUser; + } catch (error) { + throw new InternalServerErrorException( + `${error} getReportUser-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } + async createUserReport( reportNo: number, userNo: number, diff --git a/main-project/src/reports/repository/reports.repository.ts b/main-project/src/reports/repository/reports.repository.ts index 727cf7d5..ac3dfe59 100644 --- a/main-project/src/reports/repository/reports.repository.ts +++ b/main-project/src/reports/repository/reports.repository.ts @@ -8,7 +8,7 @@ import { } from 'typeorm'; import { CreateReportBoardDto } from '../dto/create-report-board.dto'; import { ReportFilterDto } from '../dto/report-filter.dto'; -import { UpdateReportBoardDto } from '../dto/update-report-board.dto'; +import { UpdateReportDto } from '../dto/update-report-board.dto'; import { Reports } from '../entity/reports.entity'; import { Report } from '../interface/reports.interface'; @@ -83,7 +83,6 @@ export class ReportRepository extends Repository { `IF(reportedBoards.reportNo = ${reportNo}, JSON_ARRAYAGG(reportBoardImages.imageUrl), JSON_ARRAYAGG(reportUserImages.imageUrl)) AS imageUrls`, ]) .where('reports.no = :reportNo', { reportNo }) - .groupBy('reports.no') .getRawOne(); const convertReport: Report = { @@ -120,7 +119,7 @@ export class ReportRepository extends Repository { //게시글 수정 관련 async updateReport( reportNo: number, - updateReportDto: UpdateReportBoardDto, + updateReportDto: UpdateReportDto, ): Promise { try { await this.createQueryBuilder() diff --git a/main-project/src/reports/swagger-decorator/update-report.decorator.ts b/main-project/src/reports/swagger-decorator/update-report.decorator.ts new file mode 100644 index 00000000..475f0f39 --- /dev/null +++ b/main-project/src/reports/swagger-decorator/update-report.decorator.ts @@ -0,0 +1,77 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiBody, + ApiConsumes, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiUpdateReport() { + return applyDecorators( + ApiBearerAuth(), + ApiOperation({ + summary: '신고내역 수정', + description: '신고내역의 제목, 내용, 사진을 수정', + }), + ApiConsumes('multipart/form-data'), + ApiBody({ + schema: { + type: 'object', + properties: { + title: { + type: 'string', + example: '경찰아저씨 저 사람 좀 잡아가세요', + minLength: 2, + maxLength: 255, + nullable: false, + description: '신고내역 제목', + }, + description: { + type: 'string', + example: '제 마음을 훔쳐갔어요....!', + minLength: 2, + maxLength: 255, + nullable: false, + description: '신고내역 내용', + }, + file: { + type: 'string', + format: 'binary', + description: '신고내역 이미지 파일', + }, + }, + }, + }), + ApiOkResponse( + SwaggerApiResponse.success( + 'Api 작동 성공 msg 반환', + '신고내역 수정 성공', + ), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'reportNotFound', + example: { + msg: `신고내역 상세 조회(getReport-service): 4번 신고내역이 없습니다.`, + }, + }, + ]), + ), + ApiBadRequestResponse( + SwaggerApiResponse.exception([ + { + name: 'isNotWriter', + example: { + msg: `사용자 검증(validateWriter-service): 잘못된 사용자의 접근입니다.`, + }, + }, + ]), + ), + ); +} From 63f42624f05b11530747f5a9db529fd3da24b7bc Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Thu, 9 Feb 2023 14:14:56 +0900 Subject: [PATCH 10/23] =?UTF-8?q?Fix(minho/report):=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95=20#194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/reports/repository/report-board.repository.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main-project/src/reports/repository/report-board.repository.ts b/main-project/src/reports/repository/report-board.repository.ts index 2ab2cb09..1a0a6601 100644 --- a/main-project/src/reports/repository/report-board.repository.ts +++ b/main-project/src/reports/repository/report-board.repository.ts @@ -7,12 +7,12 @@ import { ReportBoards } from '../entity/report-board.entity'; export class ReportBoardRepository extends Repository { async getReportBoard(reportNo: number): Promise { try { - const reportBoards: ReportBoards = await this.createQueryBuilder() + const reportBoard: ReportBoards = await this.createQueryBuilder() .select() .where('report_no = :reportNo', { reportNo }) .getOne(); - return reportBoards; + return reportBoard; } catch (error) { throw new InternalServerErrorException( `${error} getReportBoard-repository: 알 수 없는 서버 에러입니다.`, From 785d22d19df5bdeff74991a1c4379c7c81b128f9 Mon Sep 17 00:00:00 2001 From: JUHA Date: Fri, 10 Feb 2023 13:56:48 +0900 Subject: [PATCH 11/23] =?UTF-8?q?Feature(juha/notice)=20:=20=EC=9D=BD?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=95=8C=EB=A6=BC=20=EC=9E=88?= =?UTF-8?q?=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20#18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/notices/notices.controller.ts | 15 +++++++++++++++ main-project/src/notices/notices.service.ts | 7 +++++++ .../notices/repository/notices.repository.ts | 15 +++++++++++++++ .../is-user-has-unread-notices.decorator.ts | 19 +++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 main-project/src/notices/swagger-decorator/is-user-has-unread-notices.decorator.ts diff --git a/main-project/src/notices/notices.controller.ts b/main-project/src/notices/notices.controller.ts index 9312d3c3..ec09d392 100644 --- a/main-project/src/notices/notices.controller.ts +++ b/main-project/src/notices/notices.controller.ts @@ -7,6 +7,7 @@ import { APIResponse } from 'src/common/interface/interface'; import { ExtractedNotice } from './interface/notice.interface'; import { NoticesService } from './notices.service'; import { ApiGetNotices } from './swagger-decorator/get-notices.decorator'; +import { ApiIsUserHasUnreadNotices } from './swagger-decorator/is-user-has-unread-notices.decorator'; import { ApiReadNotice } from './swagger-decorator/read-notice.decorator'; @Controller('notices') @@ -36,4 +37,18 @@ export class NoticesController { return { msg: '알림 읽음 처리 성공' }; } + + @ApiIsUserHasUnreadNotices() + @Get('/unread') + async isUserHasUnreadNotices( + @GetUser() userNo: number, + ): Promise { + const isUserHasUnreadNotices: boolean = + await this.noticesService.isUserHasUnreadNotices(userNo); + + return { + response: { isUserHasUnreadNotices }, + msg: '유저 읽지 않은 알림 조회', + }; + } } diff --git a/main-project/src/notices/notices.service.ts b/main-project/src/notices/notices.service.ts index 6036dd4a..f4e225ce 100644 --- a/main-project/src/notices/notices.service.ts +++ b/main-project/src/notices/notices.service.ts @@ -33,6 +33,13 @@ export class NoticesService { }); } + async isUserHasUnreadNotices(userNo: number): Promise { + const countOfUnreadNotices: number = + await this.noticeRepository.getCountOfUnreadNotices(userNo); + + return Boolean(countOfUnreadNotices); + } + private async updateNotice( noticeNo: number, updatedNotice: UpdatedNotice, diff --git a/main-project/src/notices/repository/notices.repository.ts b/main-project/src/notices/repository/notices.repository.ts index c56ba40f..3a66c345 100644 --- a/main-project/src/notices/repository/notices.repository.ts +++ b/main-project/src/notices/repository/notices.repository.ts @@ -133,4 +133,19 @@ export class NoticesRepository extends Repository { ); } } + + async getCountOfUnreadNotices(userNo: number): Promise { + try { + const countOfUnreadNotices: number = await this.createQueryBuilder() + .where('user_no = :userNo', { userNo }) + .andWhere('read_datetime IS NULL', { userNo }) + .getCount(); + + return countOfUnreadNotices; + } catch (error) { + throw new InternalServerErrorException( + `${error} 안 읽은 알림 개수 조회(getCountOfUnreadNotices): 알 수 없는 서버 오류입니다.`, + ); + } + } } diff --git a/main-project/src/notices/swagger-decorator/is-user-has-unread-notices.decorator.ts b/main-project/src/notices/swagger-decorator/is-user-has-unread-notices.decorator.ts new file mode 100644 index 00000000..a4865571 --- /dev/null +++ b/main-project/src/notices/swagger-decorator/is-user-has-unread-notices.decorator.ts @@ -0,0 +1,19 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation } from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiIsUserHasUnreadNotices() { + return applyDecorators( + ApiOperation({ + summary: '안 읽은 알림 있는지 확인', + }), + ApiOkResponse( + SwaggerApiResponse.success( + '안 읽은 알림 있는지 조회', + '유저 읽지 않은 알림 조회', + { isUserHasUnreadNotices: true }, + ), + ), + ); +} From 7d477747f0b7ab24ea9c8727f71e3eed296ceaf5 Mon Sep 17 00:00:00 2001 From: JUHA Date: Fri, 10 Feb 2023 14:27:35 +0900 Subject: [PATCH 12/23] =?UTF-8?q?Feature(juha/user)=20:=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20#220?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../delete-profile-image.decorator.ts | 34 +++++++++++++++++++ .../update-profile-image.decorator.ts | 6 +--- main-project/src/users/users.controller.ts | 20 +++++++++++ main-project/src/users/users.service.ts | 14 ++++++++ 4 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 main-project/src/users/swagger-decorator/delete-profile-image.decorator.ts diff --git a/main-project/src/users/swagger-decorator/delete-profile-image.decorator.ts b/main-project/src/users/swagger-decorator/delete-profile-image.decorator.ts new file mode 100644 index 00000000..b7e8d2d7 --- /dev/null +++ b/main-project/src/users/swagger-decorator/delete-profile-image.decorator.ts @@ -0,0 +1,34 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBearerAuth, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiDeleteProfileImage() { + return applyDecorators( + ApiBearerAuth(), + ApiOperation({ + summary: '유저 프로필 이미지 삭제', + }), + ApiOkResponse( + SwaggerApiResponse.success( + 'accessToken 반환', + '프로필 이미지가 삭제되었습니다.', + { + accessToken: '갱신된 토큰', + }, + ), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'noProfileImage', + example: { msg: '프로필 이미지가 존재하지 않는 유저입니다.' }, + }, + ]), + ), + ); +} diff --git a/main-project/src/users/swagger-decorator/update-profile-image.decorator.ts b/main-project/src/users/swagger-decorator/update-profile-image.decorator.ts index 4a37fb23..13b6e49c 100644 --- a/main-project/src/users/swagger-decorator/update-profile-image.decorator.ts +++ b/main-project/src/users/swagger-decorator/update-profile-image.decorator.ts @@ -7,8 +7,6 @@ import { ApiOkResponse, ApiOperation, } from '@nestjs/swagger'; -import { UserStatus } from 'src/common/configs/user-status.config'; - import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; export function ApiUpdateProfileImage() { @@ -32,12 +30,10 @@ export function ApiUpdateProfileImage() { }), ApiOkResponse( SwaggerApiResponse.success( - 'userNo,status,accessToken 반환', + 'accessToken 반환', '프로필 이미지가 수정되었습니다.', { user: { - userNo: 1, - status: UserStatus.CONFIRMED, accessToken: '갱신된 토큰', }, }, diff --git a/main-project/src/users/users.controller.ts b/main-project/src/users/users.controller.ts index 80cb989b..460141b7 100644 --- a/main-project/src/users/users.controller.ts +++ b/main-project/src/users/users.controller.ts @@ -30,6 +30,7 @@ import { import { ApiConfirmUser } from './swagger-decorator/confirm-user.decorator'; import { ApiCreateCertificate } from './swagger-decorator/create-certificate.decorator'; import { ApiCreateProfile } from './swagger-decorator/create-profile.decorator'; +import { ApiDeleteProfileImage } from './swagger-decorator/delete-profile-image.decorator'; import { ApiDenyUser } from './swagger-decorator/deny-user.decorator'; import { ApiGetUserCertificates } from './swagger-decorator/get-certificates.decorator'; import { ApiGetOthersProfile } from './swagger-decorator/get-others-profile.decorator'; @@ -127,6 +128,25 @@ export class UsersController { }; } + @ApiDeleteProfileImage() + @UseInterceptors(TransactionInterceptor) + @UseGuards(JwtAuthGuard) + @Delete('/profile-image') + async deleteProfileImage( + @GetUser() userNo: number, + @TransactionDecorator() manager: EntityManager, + ) { + const accessToken: string = await this.usersService.deleteProfileImage( + userNo, + manager, + ); + + return { + msg: '프로필 이미지가 삭제되었습니다.', + response: { accessToken }, + }; + } + @ApiCreateCertificate() @UseInterceptors(FileInterceptor('file')) @UseInterceptors(TransactionInterceptor) diff --git a/main-project/src/users/users.service.ts b/main-project/src/users/users.service.ts index 65825bf7..68e05cd8 100644 --- a/main-project/src/users/users.service.ts +++ b/main-project/src/users/users.service.ts @@ -123,6 +123,20 @@ export class UsersService { return await this.updateAccessToken(userNo); } + async deleteProfileImage( + userNo: number, + manager: EntityManager, + ): Promise { + const { imageUrl, profileNo }: UserImage = + await this.profileImageRepository.getProfileImage(userNo); + if (!imageUrl) { + throw new NotFoundException('프로필 이미지가 존재하지 않는 유저입니다.'); + } + await this.updateProfileImageByProfileNo(profileNo, null, manager); + + return await this.updateAccessToken(userNo); + } + async softDeleteUser(userNo: number): Promise { await this.cacheManager.del(userNo); From 9df1149b6439675c697b1ea4225281bac4ae3ac6 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Mon, 13 Feb 2023 21:41:14 +0900 Subject: [PATCH 13/23] =?UTF-8?q?Feature(minho/board):=20=EC=97=AC?= =?UTF-8?q?=EB=A6=84=20=EC=8B=A0=EC=B2=AD=EB=82=B4=EC=97=AD=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20#4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main-project/src/boards/boards.controller.ts | 23 +++++++- main-project/src/boards/boards.service.ts | 51 ++++++++++++++++- .../src/boards/dto/get-board-by-user.dto.ts | 17 ++++++ .../repository/board-guest-team.repository.ts | 45 ++++++++++++++- .../repository/board-guest.repository.ts | 2 +- .../get-guest-teams-by-board-no.decorator.ts | 56 +++++++++++++++++++ .../src/common/configs/typeorm.config.ts | 2 +- 7 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 main-project/src/boards/dto/get-board-by-user.dto.ts create mode 100644 main-project/src/boards/swagger-decorator/get-guest-teams-by-board-no.decorator.ts diff --git a/main-project/src/boards/boards.controller.ts b/main-project/src/boards/boards.controller.ts index 41b24cb8..80584e89 100644 --- a/main-project/src/boards/boards.controller.ts +++ b/main-project/src/boards/boards.controller.ts @@ -15,7 +15,7 @@ import { ApiTags } from '@nestjs/swagger'; import { BoardsService } from './boards.service'; import { CreateGuestTeamDto } from './dto/create-guest-team.dto'; import { CreateBoardDto } from './dto/create-board.dto'; -import { Board } from './interface/boards.interface'; +import { Board, GuestTeam } from './interface/boards.interface'; import { BoardFilterDto } from './dto/board-filter.dto'; import { Cron, CronExpression } from '@nestjs/schedule/dist'; import { APIResponse } from 'src/common/interface/interface'; @@ -38,6 +38,8 @@ import { GuestInviteDto } from './dto/guest-invite.dto'; import { ApiDeleteBoard } from './swagger-decorator/delete-board.decorator'; import { ApiDeleteBookmark } from './swagger-decorator/delete-bookmark.decorator'; import { ApiGetBoardsByUser } from './swagger-decorator/get- boards-by-user.decorator'; +import { GetBoardByUserDto } from './dto/get-board-by-user.dto'; +import { ApiGetGuestTemasByBoardNo } from './swagger-decorator/get-guest-teams-by-board-no.decorator'; @Controller('boards') @ApiTags('게시글 API') @@ -90,7 +92,7 @@ export class BoardsController { @UseInterceptors(TransactionInterceptor) @ApiGetBoardsByUser() async getBoardByUser( - @Param('type') type: number, + @Param('type') { type }: GetBoardByUserDto, @GetUser() userNo: number, @TransactionDecorator() manager: EntityManager, ): Promise { @@ -103,6 +105,21 @@ export class BoardsController { return { msg: '유저별 게시글 조회 성공', response: { boards } }; } + @Get('/guest-team/:boardNo') + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) + @ApiGetGuestTemasByBoardNo() + async getGuestTeamsByBoardNo( + @Param('boardNo') boardNo: number, + @GetUser() userNo: number, + @TransactionDecorator() manager: EntityManager, + ): Promise { + const guestTeams: GuestTeam[] = + await this.boardService.getGuestTeamsByBoardNo(manager, userNo, boardNo); + + return { msg: '여름 신청내역 조회 성공', response: { guestTeams } }; + } + // Post Methods @Post() @UseGuards(JwtAuthGuard) @@ -205,7 +222,7 @@ export class BoardsController { isAccepted, ); - return { msg: '게시글 수락/거절 처리 성공' }; + return { msg: '게스트 수락/거절 처리 성공' }; } // Delete Methods diff --git a/main-project/src/boards/boards.service.ts b/main-project/src/boards/boards.service.ts index a99a6c5e..57438053 100644 --- a/main-project/src/boards/boards.service.ts +++ b/main-project/src/boards/boards.service.ts @@ -134,6 +134,37 @@ export class BoardsService { return info; } + async getGuestTeamsByBoardNo( + manager: EntityManager, + userNo: number, + boardNo: number, + ): Promise[]> { + await this.getBoard(manager, boardNo); + await this.validateHost(manager, boardNo, userNo); + + const guestTeams: GuestTeam[] = + await this.readGuestTeamsByBoardNo(manager, boardNo); + + if (!guestTeams.length) { + throw new BadRequestException( + `여름 신청내역 조회(getGuestTeamsByBoardNo-service): 신청내역이 없습니다.`, + ); + } + + return guestTeams; + } + + async readGuestTeamsByBoardNo( + manager: EntityManager, + boardNo: number, + ): Promise[]> { + const guestTeams: GuestTeam[] = await manager + .getCustomRepository(BoardGuestTeamsRepository) + .getGuestTeamsByBoardNo(boardNo); + + return guestTeams; + } + private async getAllGuestsByBoardNo( manager: EntityManager, boardNo: number, @@ -402,7 +433,7 @@ export class BoardsService { manager, boardNo, ); - this.validateHost(hostUserNo, userNo); + this.validateWriter(hostUserNo, userNo); await this.removeBoard(manager, boardNo); } @@ -626,7 +657,7 @@ export class BoardsService { } } - private validateHost(hostUserNo: number, userNo: number): void { + private validateWriter(hostUserNo: number, userNo: number): void { if (userNo != hostUserNo) { throw new BadRequestException( `작성자 검증 (validateHost-service): 작성자와 사용자가 일치하지 않습니다.`, @@ -634,6 +665,20 @@ export class BoardsService { } } + private async validateHost( + manager: EntityManager, + boardNo: number, + userNo: number, + ): Promise { + const { users }: Host = await this.getHosts(manager, boardNo); + + if (!users.includes(userNo)) { + throw new BadRequestException( + '호스트 확인(validateHost-service): 해당 게시글의 호스트멤버가 아닙니다.', + ); + } + } + private async validateIsHostMember( manager: EntityManager, boardNo: number, @@ -786,7 +831,7 @@ export class BoardsService { ): Promise { const board: Board = await this.getBoard(manager, boardNo); - this.validateHost(board.hostUserNo, userNo); + this.validateWriter(board.hostUserNo, userNo); await this.validateRecruits(manager, board, updateBoardDto); } } diff --git a/main-project/src/boards/dto/get-board-by-user.dto.ts b/main-project/src/boards/dto/get-board-by-user.dto.ts new file mode 100644 index 00000000..930612d6 --- /dev/null +++ b/main-project/src/boards/dto/get-board-by-user.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; +import { ValidateGender } from 'src/common/decorator/validateGender.decorator'; + +export class GetBoardByUserDto { + @ApiProperty({ + example: 1, + description: + '게시글 조회 시 유저가 포함된 항목에 따른 조회시를 수행. 1: 작성자로 등록된 게시글, 2: 호스트멤버로 등록된 게시글, 3: 게스트로 등록된 게시글', + required: true, + }) + @IsNumber() + @Type(() => Number) + @IsOptional() + readonly type: number; +} diff --git a/main-project/src/boards/repository/board-guest-team.repository.ts b/main-project/src/boards/repository/board-guest-team.repository.ts index 3accb37d..d944b2b3 100644 --- a/main-project/src/boards/repository/board-guest-team.repository.ts +++ b/main-project/src/boards/repository/board-guest-team.repository.ts @@ -1,6 +1,11 @@ import { InternalServerErrorException } from '@nestjs/common'; import { ResultSetHeader } from 'mysql2'; -import { EntityRepository, InsertResult, Repository } from 'typeorm'; +import { + EntityRepository, + InsertResult, + Repository, + SelectQueryBuilder, +} from 'typeorm'; import { BoardGuestTeams } from '../entity/board-guest-team.entity'; import { GuestTeam } from '../interface/boards.interface'; @@ -36,6 +41,44 @@ export class BoardGuestTeamsRepository extends Repository { } } + async getGuestTeamsByBoardNo( + boardNo: number, + ): Promise[]> { + try { + const query: SelectQueryBuilder = + this.createQueryBuilder('teams') + .leftJoin('teams.boardGuest', 'guests') + .select([ + 'guests.teamNo AS teamNo', + 'teams.title AS title', + 'teams.description AS description', + 'JSON_ARRAYAGG(guests.userNo) AS guests', + ]) + .where('teams.boardNo = :boardNo', { boardNo }) + .groupBy('guests.teamNo') + .where('teams.isAccepted = TRUE'); + + const guestTeams = await query.getRawMany(); + + const convertGuestTeams: GuestTeam[] = guestTeams.map( + ({ guests, ...guetTemaInfo }) => { + const guetTeam: GuestTeam = { + ...guetTemaInfo, + guests: JSON.parse(guests), + }; + + return guetTeam; + }, + ); + + return convertGuestTeams; + } catch (error) { + throw new InternalServerErrorException( + `${error} getGuestTeamsByBoardNo-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } + // 생성 async createGuestTeam( participation: GuestTeam, diff --git a/main-project/src/boards/repository/board-guest.repository.ts b/main-project/src/boards/repository/board-guest.repository.ts index 0be0ccfe..86306e23 100644 --- a/main-project/src/boards/repository/board-guest.repository.ts +++ b/main-project/src/boards/repository/board-guest.repository.ts @@ -12,7 +12,7 @@ export class BoardGuestsRepository extends Repository { 'boardGuest', ) .leftJoin('boardGuest.teamNo', 'team') - .select('JSON_ARRAYAGG(boardGuest.userNo) AS userNo') + .select('JSON_ARRAYAGG(boardGuest.userNo) AS guests') .where('team.boardNo = :boardNo', { boardNo }) .getRawOne(); diff --git a/main-project/src/boards/swagger-decorator/get-guest-teams-by-board-no.decorator.ts b/main-project/src/boards/swagger-decorator/get-guest-teams-by-board-no.decorator.ts new file mode 100644 index 00000000..944e60e1 --- /dev/null +++ b/main-project/src/boards/swagger-decorator/get-guest-teams-by-board-no.decorator.ts @@ -0,0 +1,56 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBearerAuth, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiGetGuestTemasByBoardNo() { + return applyDecorators( + ApiOperation({ + summary: '여름 신청내역 조회', + }), + ApiBearerAuth(), + ApiOkResponse( + SwaggerApiResponse.success('해당 게시글의 여름 신청내역 전체조회', { + guestTeams: [ + { + teamNo: 4, + title: 'tessssst', + description: 'desc', + guests: [16, 14], + }, + { + teamNo: 5, + title: 'tessssst', + description: 'desc', + guests: [16, 14], + }, + ], + }), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'boardNotFound', + example: { msg: `존재하지 않는 게시글 번호입니다.` }, + }, + { + name: 'isNotHostMember', + example: { + msg: '호스트 확인(validateHost-service): 해당 게시글의 호스트멤버가 아닙니다.', + }, + }, + { + name: 'guestTeamsNotFound', + example: { + msg: '여름 신청내역 조회(getGuestTeamsByBoardNo-service): 신청내역이 없습니다.', + }, + }, + ]), + ), + ); +} diff --git a/main-project/src/common/configs/typeorm.config.ts b/main-project/src/common/configs/typeorm.config.ts index 657c8b5f..b298eb2a 100644 --- a/main-project/src/common/configs/typeorm.config.ts +++ b/main-project/src/common/configs/typeorm.config.ts @@ -10,7 +10,7 @@ export const typeOrmConfig: TypeOrmModuleAsyncOptions = { username: configService.get('DB_USERNAME'), password: configService.get('DB_PASSWORD'), database: configService.get('DB_DATABASE'), - synchronize: true, + synchronize: false, entities: [__dirname + '/../../**/entity/*.entity.{js,ts}'], logging: false, }), From 52b70f46200452aabc2a1946c4b4e919aefbc0be Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Tue, 14 Feb 2023 03:05:22 +0900 Subject: [PATCH 14/23] pull --- .../src/announces/announces.service.ts | 13 +- main-project/src/app.module.ts | 1 + main-project/src/boards/boards.service.ts | 11 +- .../src/boards/repository/board.repository.ts | 61 ++- .../src/enquiries/enquiries.controller.ts | 4 +- .../src/enquiries/enquiries.service.ts | 30 +- .../repository/enquiry-image.repository.ts | 8 +- .../repository/enquiry.repository.ts | 4 +- .../repository/notices-board.repository.ts | 6 +- ...orts.dto.ts => create-report-board.dto.ts} | 26 +- .../src/reports/dto/create-report-user.dto.ts | 29 ++ .../src/reports/dto/report-filter.dto.ts | 28 + .../reports/dto/update-report-board.dto.ts | 6 + .../src/reports/dto/update-report-user.dto.ts | 0 .../src/reports/dto/update-reports.dto.ts | 6 - .../entity/report-board-images.entity.ts | 28 + .../src/reports/entity/report-board.entity.ts | 8 + .../reports/entity/report-images.entity.ts | 24 - .../entity/report-user-image.entity.ts | 28 + .../src/reports/entity/report-user.entity.ts | 8 + .../src/reports/entity/reports.entity.ts | 13 +- .../reports/interface/reports.interface.ts | 20 +- .../src/reports/reports.controller.ts | 145 ++++-- main-project/src/reports/reports.module.ts | 3 +- main-project/src/reports/reports.service.ts | 478 ++++++++++++------ .../report-board-image.repository.ts | 35 ++ .../repository/report-board.repository.ts | 26 +- .../report-user-image.repository.ts | 35 ++ .../repository/report-user.repository.ts | 24 +- .../reports/repository/reports.repository.ts | 108 ++-- .../create-board-report.decorator.ts | 64 +++ .../create-user-report.decorator.ts | 53 ++ .../delete-report.decorator.ts | 47 ++ .../swagger-decorator/get-report.decorator.ts | 51 ++ .../get-reports.decorator.ts | 54 ++ .../update-report.decorator.ts | 77 +++ 36 files changed, 1147 insertions(+), 415 deletions(-) rename main-project/src/reports/dto/{create-reports.dto.ts => create-report-board.dto.ts} (70%) create mode 100644 main-project/src/reports/dto/create-report-user.dto.ts create mode 100644 main-project/src/reports/dto/report-filter.dto.ts create mode 100644 main-project/src/reports/dto/update-report-board.dto.ts create mode 100644 main-project/src/reports/dto/update-report-user.dto.ts delete mode 100644 main-project/src/reports/dto/update-reports.dto.ts create mode 100644 main-project/src/reports/entity/report-board-images.entity.ts delete mode 100644 main-project/src/reports/entity/report-images.entity.ts create mode 100644 main-project/src/reports/entity/report-user-image.entity.ts create mode 100644 main-project/src/reports/repository/report-board-image.repository.ts create mode 100644 main-project/src/reports/repository/report-user-image.repository.ts create mode 100644 main-project/src/reports/swagger-decorator/create-board-report.decorator.ts create mode 100644 main-project/src/reports/swagger-decorator/create-user-report.decorator.ts create mode 100644 main-project/src/reports/swagger-decorator/delete-report.decorator.ts create mode 100644 main-project/src/reports/swagger-decorator/get-report.decorator.ts create mode 100644 main-project/src/reports/swagger-decorator/get-reports.decorator.ts create mode 100644 main-project/src/reports/swagger-decorator/update-report.decorator.ts diff --git a/main-project/src/announces/announces.service.ts b/main-project/src/announces/announces.service.ts index 032f0b0f..89980d1f 100644 --- a/main-project/src/announces/announces.service.ts +++ b/main-project/src/announces/announces.service.ts @@ -23,7 +23,7 @@ export class AnnouncesService { private readonly configService: ConfigService, ) {} - ADMIN_USER: number = Number(this.configService.get('ADMIN_USER')); + ADMIN_USER: number = this.configService.get('ADMIN_USER'); // 생성 관련 async createAnnounce( @@ -57,7 +57,7 @@ export class AnnouncesService { imageUrls: string[], announceNo: number, ): Promise { - const images: AnnounceImage[] = await this.convertImageArray( + const images: AnnounceImage[] = this.convertImageArray( announceNo, imageUrls, ); @@ -180,10 +180,10 @@ export class AnnouncesService { } // functions - private async convertImageArray( + private convertImageArray( announceNo: number, imageUrls: string[], - ): Promise[]> { + ): AnnounceImage[] { const images: AnnounceImage[] = imageUrls.map( (imageUrl: string) => { return { announceNo, imageUrl }; @@ -193,7 +193,10 @@ export class AnnouncesService { return images; } - private async validateAdmin(manager: EntityManager, userNo: number) { + private async validateAdmin( + manager: EntityManager, + userNo: number, + ): Promise { const { no }: Users = await manager .getCustomRepository(UsersRepository) .getUserByNo(userNo); diff --git a/main-project/src/app.module.ts b/main-project/src/app.module.ts index bde2aed2..e0ca0e5d 100644 --- a/main-project/src/app.module.ts +++ b/main-project/src/app.module.ts @@ -34,6 +34,7 @@ import * as Joi from 'joi'; EXPIRES_IN: Joi.number().required(), REFRESH_TOKEN_EXPIRATION: Joi.number().required(), TOKEN_EXPIRATION: Joi.number().required(), + ADMIN_USER: Joi.number().required(), }), }), CacheModule.register(), diff --git a/main-project/src/boards/boards.service.ts b/main-project/src/boards/boards.service.ts index 7f3a2614..a99a6c5e 100644 --- a/main-project/src/boards/boards.service.ts +++ b/main-project/src/boards/boards.service.ts @@ -402,7 +402,7 @@ export class BoardsService { manager, boardNo, ); - await this.validateHost(hostUserNo, userNo); + this.validateHost(hostUserNo, userNo); await this.removeBoard(manager, boardNo); } @@ -560,7 +560,7 @@ export class BoardsService { guests: number[], ): Promise { const type = NoticeType.GUEST_REQUEST_REJECTED; - // TODO: + const notices: SavedNotice[] = guests.map((userNo) => { return { userNo, targetUserNo, type }; }); @@ -626,10 +626,7 @@ export class BoardsService { } } - private async validateHost( - hostUserNo: number, - userNo: number, - ): Promise { + private validateHost(hostUserNo: number, userNo: number): void { if (userNo != hostUserNo) { throw new BadRequestException( `작성자 검증 (validateHost-service): 작성자와 사용자가 일치하지 않습니다.`, @@ -789,7 +786,7 @@ export class BoardsService { ): Promise { const board: Board = await this.getBoard(manager, boardNo); - await this.validateHost(board.hostUserNo, userNo); + this.validateHost(board.hostUserNo, userNo); await this.validateRecruits(manager, board, updateBoardDto); } } diff --git a/main-project/src/boards/repository/board.repository.ts b/main-project/src/boards/repository/board.repository.ts index 187598cf..2ff035bc 100644 --- a/main-project/src/boards/repository/board.repository.ts +++ b/main-project/src/boards/repository/board.repository.ts @@ -41,43 +41,40 @@ export class BoardsRepository extends Repository { async getBoardByNo(no: number): Promise> { try { - const { - hostMemberNums, - hostMemberNicknames, - ...jsonBoard - }: Board = await this.createQueryBuilder('boards') - .leftJoin('boards.userNo', 'users') - .leftJoin('users.userProfileNo', 'profile') - .leftJoin('boards.hosts', 'hosts') - .leftJoin('hosts.userNo', 'hostUsers') - .leftJoin('hostUsers.userProfileNo', 'hostProfile') - .select([ - 'boards.no AS no', - 'boards.userNo AS hostUserNo', - 'profile.nickname AS hostNickname', - 'boards.title AS title', - 'boards.description AS description', - 'boards.location AS location', - 'boards.isDone AS isDone', - 'boards.recruitMale AS recruitMale', - 'boards.recruitFemale AS recruitFemale', - 'boards.isImpromptu AS isImpromptu', - `DATE_FORMAT(boards.meetingTime, '%Y.%m.%d %T') AS meetingTime`, - `DATE_FORMAT(boards.createdDate, '%Y.%m.%d %T') AS createdDate`, - 'JSON_ARRAYAGG(hosts.userNo) AS hostMemberNums', - 'JSON_ARRAYAGG(hostProfile.nickname) AS hostMemberNicknames', - ]) - .where('boards.no = :no', { no }) - .andWhere('hosts.board_no = :no', { no }) - .getRawOne(); + const { hostMemberNums, hostMemberNicknames, ...board }: Board = + await this.createQueryBuilder('boards') + .leftJoin('boards.userNo', 'users') + .leftJoin('users.userProfileNo', 'profile') + .leftJoin('boards.hosts', 'hosts') + .leftJoin('hosts.userNo', 'hostUsers') + .leftJoin('hostUsers.userProfileNo', 'hostProfile') + .select([ + 'boards.no AS no', + 'boards.userNo AS hostUserNo', + 'profile.nickname AS hostNickname', + 'boards.title AS title', + 'boards.description AS description', + 'boards.location AS location', + 'boards.isDone AS isDone', + 'boards.recruitMale AS recruitMale', + 'boards.recruitFemale AS recruitFemale', + 'boards.isImpromptu AS isImpromptu', + `DATE_FORMAT(boards.meetingTime, '%Y.%m.%d %T') AS meetingTime`, + `DATE_FORMAT(boards.createdDate, '%Y.%m.%d %T') AS createdDate`, + 'JSON_ARRAYAGG(hosts.userNo) AS hostMemberNums', + 'JSON_ARRAYAGG(hostProfile.nickname) AS hostMemberNicknames', + ]) + .where('boards.no = :no', { no }) + .andWhere('hosts.board_no = :no', { no }) + .getRawOne(); - const board: Board = { - ...jsonBoard, + const convertBoard: Board = { + ...board, hostMemberNums: JSON.parse(hostMemberNums), hostMemberNicknames: JSON.parse(hostMemberNicknames), }; - return board; + return convertBoard; } catch (error) { throw new InternalServerErrorException( `${error} getBoard-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/enquiries/enquiries.controller.ts b/main-project/src/enquiries/enquiries.controller.ts index db924ea9..d7f837f9 100644 --- a/main-project/src/enquiries/enquiries.controller.ts +++ b/main-project/src/enquiries/enquiries.controller.ts @@ -13,7 +13,7 @@ import { UseInterceptors, } from '@nestjs/common'; import { FilesInterceptor } from '@nestjs/platform-express'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { GetUser } from 'src/common/decorator/get-user.decorator'; import { TransactionDecorator } from 'src/common/decorator/transaction-manager.decorator'; import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; @@ -72,7 +72,7 @@ export class EnquiriesController { userNo, ); - return { msg: '유저별 문의사항 전체 조회', response: { eunqiries } }; + return { msg: '유저별 문의사항 전체 조회 성공', response: { eunqiries } }; } @Get('/:enquiryNo') diff --git a/main-project/src/enquiries/enquiries.service.ts b/main-project/src/enquiries/enquiries.service.ts index 2df959ab..5b3ae48d 100644 --- a/main-project/src/enquiries/enquiries.service.ts +++ b/main-project/src/enquiries/enquiries.service.ts @@ -1,11 +1,6 @@ -import { - Injectable, - InternalServerErrorException, - NotFoundException, -} from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { BadRequestException } from '@nestjs/common/exceptions'; import { ConfigService } from '@nestjs/config'; -import { write } from 'fs'; import { ResultSetHeader } from 'mysql2'; import { AwsService } from 'src/aws/aws.service'; import { Users } from 'src/users/entity/user.entity'; @@ -29,7 +24,7 @@ export class EnquiriesService { private readonly configService: ConfigService, ) {} - ADMIN_USER: number = Number(this.configService.get('ADMIN_USER')); + ADMIN_USER: number = this.configService.get('ADMIN_USER'); // Get Methods async getEnquiries( @@ -82,7 +77,7 @@ export class EnquiriesService { ); } - await this.validateWriter(userNo, enquiry.userNo); + this.validateWriter(userNo, enquiry.userNo); return enquiry; } @@ -109,7 +104,7 @@ export class EnquiriesService { userNo, ); - await this.validateWriter(userNo, enquiry.userNo); + this.validateWriter(userNo, enquiry.userNo); const reply: Reply = await this.readReply(manager, enquiryNo); @@ -169,7 +164,7 @@ export class EnquiriesService { imageUrls: string[], enquiryNo: number, ): Promise { - const images: ImageInfo[] = await this.convertImageArray( + const images: ImageInfo[] = this.convertImageArray( imageUrls, enquiryNo, ); @@ -219,7 +214,7 @@ export class EnquiriesService { imageUrls: string[], replyNo: number, ): Promise { - const images: ImageInfo[] = await this.convertImageArray( + const images: ImageInfo[] = this.convertImageArray( imageUrls, undefined, replyNo, @@ -252,7 +247,7 @@ export class EnquiriesService { enquiryNo, userNo, ); - await this.validateWriter(enquiry.userNo, userNo); + this.validateWriter(enquiry.userNo, userNo); await this.updateEnquiry(manager, enquiryNo, updateEnquiryDto); await this.editEnquiryimages(manager, files, enquiryNo, imageUrls); @@ -340,7 +335,7 @@ export class EnquiriesService { enquiryNo, userNo, ); - await this.validateWriter(enquiry.userNo, userNo); + this.validateWriter(enquiry.userNo, userNo); if (!imageUrls.includes(null)) { await this.awsService.deleteFiles(imageUrls); @@ -404,10 +399,7 @@ export class EnquiriesService { } //function - private async validateWriter( - userNo: number, - writerNo: number, - ): Promise { + private validateWriter(userNo: number, writerNo: number): void { if (writerNo !== userNo && this.ADMIN_USER !== userNo) { throw new BadRequestException( `사용자 검증(validateWriter-service): 잘못된 사용자의 접근입니다.`, @@ -427,11 +419,11 @@ export class EnquiriesService { } } - private async convertImageArray( + private convertImageArray( imageUrls: string[], enquiryNo?: number, replyNo?: number, - ): Promise[]> { + ): ImageInfo[] { const images: ImageInfo[] = enquiryNo ? imageUrls.map((imageUrl: string) => { return { enquiryNo, imageUrl }; diff --git a/main-project/src/enquiries/repository/enquiry-image.repository.ts b/main-project/src/enquiries/repository/enquiry-image.repository.ts index ba59949e..891a7119 100644 --- a/main-project/src/enquiries/repository/enquiry-image.repository.ts +++ b/main-project/src/enquiries/repository/enquiry-image.repository.ts @@ -1,11 +1,5 @@ import { InternalServerErrorException } from '@nestjs/common'; -import { ResultSetHeader } from 'mysql2'; -import { - DeleteResult, - EntityRepository, - InsertResult, - Repository, -} from 'typeorm'; +import { EntityRepository, Repository } from 'typeorm'; import { EnquiryImages } from '../entity/enquiry-images.entity'; import { ImageInfo } from '../interface/enquiry.interface'; diff --git a/main-project/src/enquiries/repository/enquiry.repository.ts b/main-project/src/enquiries/repository/enquiry.repository.ts index 45a058b6..7a86da1b 100644 --- a/main-project/src/enquiries/repository/enquiry.repository.ts +++ b/main-project/src/enquiries/repository/enquiry.repository.ts @@ -31,7 +31,7 @@ export class EnquiriesRepository extends Repository { 'enquiries.title AS title', 'enquiries.description AS description', 'enquiries.isDone AS isDone', - `DATE_FORMAT(enquiries.createdDate, '%Y.%m.%d %T') AS createdDate`, + 'DATE_FORMAT(enquiries.createdDate, "%Y.%m.%d %T") AS createdDate', 'JSON_ARRAYAGG(images.imageUrl) AS imageUrls', ]) .orderBy('no', 'DESC') @@ -78,7 +78,7 @@ export class EnquiriesRepository extends Repository { 'enquiries.title AS title', 'enquiries.description AS description', 'enquiries.isDone AS isDone', - `DATE_FORMAT(enquiries.createdDate, '%Y.%m.%d %T') AS createdDate`, + 'DATE_FORMAT(enquiries.createdDate, "%Y.%m.%d %T") AS createdDate', 'JSON_ARRAYAGG(images.imageUrl) AS imageUrls', ]) .where('enquiries.no = :enquiryNo', { enquiryNo }) diff --git a/main-project/src/notices/repository/notices-board.repository.ts b/main-project/src/notices/repository/notices-board.repository.ts index 55c3998f..bd25a1e5 100644 --- a/main-project/src/notices/repository/notices-board.repository.ts +++ b/main-project/src/notices/repository/notices-board.repository.ts @@ -6,15 +6,11 @@ import { NoticeBoards } from '../entity/notice-board.entity'; export class NoticeBoardsRepository extends Repository { async saveNoticeBoard(noticeNo: number, boardNo: number): Promise { try { - const { raw }: InsertResult = await this.createQueryBuilder( - 'notice_boards', - ) + await this.createQueryBuilder() .insert() .into(NoticeBoards) .values({ noticeNo, boardNo }) .execute(); - - return raw.affectedRows; } catch (error) { throw new InternalServerErrorException( `${error} 알람 생성 에러(saveNoticeBoard): 알 수 없는 서버 오류입니다.`, diff --git a/main-project/src/reports/dto/create-reports.dto.ts b/main-project/src/reports/dto/create-report-board.dto.ts similarity index 70% rename from main-project/src/reports/dto/create-reports.dto.ts rename to main-project/src/reports/dto/create-report-board.dto.ts index 0154e466..1d421021 100644 --- a/main-project/src/reports/dto/create-reports.dto.ts +++ b/main-project/src/reports/dto/create-report-board.dto.ts @@ -1,25 +1,29 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; -export class CreateReportDto { - @IsNotEmpty() - @IsNumber() - @ApiProperty({ example: 21, description: '신고주체 사용자 번호(원고)' }) - reportingUserNo: number; - // jwt 구현되면 useGaroud로 대체 예정 - - @IsNotEmpty() - @IsString() +export class CreateReportBoardDto { @ApiProperty({ example: '개발이 너무 재밌어요 어떡하죠?', description: '신고글 제목', }) + @IsString() + @IsNotEmpty() title: string; - @IsNotEmpty() - @IsString() @ApiProperty({ example: '라고 할 뻔ㅋㅋㅋㅋㅋ', description: '신고글 내용', }) + @IsString() + @IsNotEmpty() description: string; + + @ApiProperty({ + example: 65, + description: '게시글 번호', + }) + @IsNumber() + @Type(() => Number) + @IsNotEmpty() + boardNo: number; } diff --git a/main-project/src/reports/dto/create-report-user.dto.ts b/main-project/src/reports/dto/create-report-user.dto.ts new file mode 100644 index 00000000..b382f4ff --- /dev/null +++ b/main-project/src/reports/dto/create-report-user.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +export class CreateReportUserDto { + @ApiProperty({ + example: '개발이 너무 재밌어요 어떡하죠?', + description: '신고글 제목', + }) + @IsString() + @IsNotEmpty() + title: string; + + @ApiProperty({ + example: '라고 할 뻔ㅋㅋㅋㅋㅋ', + description: '신고글 내용', + }) + @IsString() + @IsNotEmpty() + description: string; + + @ApiProperty({ + example: 54, + description: '신고할 유저 번호', + }) + @IsNumber() + @Type(() => Number) + @IsNotEmpty() + targetUserNo: number; +} diff --git a/main-project/src/reports/dto/report-filter.dto.ts b/main-project/src/reports/dto/report-filter.dto.ts new file mode 100644 index 00000000..14ca30ff --- /dev/null +++ b/main-project/src/reports/dto/report-filter.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsNumber, IsOptional, Max, Min } from 'class-validator'; + +export class ReportFilterDto { + @ApiProperty({ + example: '23', + description: '페이지 번호', + required: false, + }) + @IsNumber() + @Min(1) + @Type(() => Number) + @IsOptional() + page: number; + + @ApiProperty({ + example: '1', + description: '유저 / 게시글 신고내역 구별 (0: 게시글, 1:유저)', + required: false, + }) + @IsNumber() + @Min(0) + @Max(1) + @Type(() => Number) + @IsOptional() + type: number; +} diff --git a/main-project/src/reports/dto/update-report-board.dto.ts b/main-project/src/reports/dto/update-report-board.dto.ts new file mode 100644 index 00000000..40362c5f --- /dev/null +++ b/main-project/src/reports/dto/update-report-board.dto.ts @@ -0,0 +1,6 @@ +import { OmitType } from '@nestjs/swagger'; +import { CreateReportBoardDto } from './create-report-board.dto'; + +export class UpdateReportDto extends OmitType(CreateReportBoardDto, [ + 'boardNo', +]) {} diff --git a/main-project/src/reports/dto/update-report-user.dto.ts b/main-project/src/reports/dto/update-report-user.dto.ts new file mode 100644 index 00000000..e69de29b diff --git a/main-project/src/reports/dto/update-reports.dto.ts b/main-project/src/reports/dto/update-reports.dto.ts deleted file mode 100644 index 6b309320..00000000 --- a/main-project/src/reports/dto/update-reports.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { OmitType } from '@nestjs/swagger'; -import { CreateReportDto } from './create-reports.dto'; - -export class UpdateReportDto extends OmitType(CreateReportDto, [ - 'reportingUserNo', -]) {} diff --git a/main-project/src/reports/entity/report-board-images.entity.ts b/main-project/src/reports/entity/report-board-images.entity.ts new file mode 100644 index 00000000..968a8a42 --- /dev/null +++ b/main-project/src/reports/entity/report-board-images.entity.ts @@ -0,0 +1,28 @@ +import { + BaseEntity, + Column, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { ReportBoards } from './report-board.entity'; + +@Entity('report _board_images') +export class ReportBoardImages extends BaseEntity { + @PrimaryGeneratedColumn() + no: number; + + @Column({ type: 'varchar', length: 255, nullable: false, name: 'image_url' }) + imageUrl: string; + + @ManyToOne( + (type) => ReportBoards, + (reportBoards) => reportBoards.reportBoardImage, + { + onDelete: 'CASCADE', + }, + ) + @JoinColumn({ name: 'report_board_no' }) + reportBoardNo: number; +} diff --git a/main-project/src/reports/entity/report-board.entity.ts b/main-project/src/reports/entity/report-board.entity.ts index 8396d72e..c295013a 100644 --- a/main-project/src/reports/entity/report-board.entity.ts +++ b/main-project/src/reports/entity/report-board.entity.ts @@ -4,9 +4,11 @@ import { Entity, JoinColumn, ManyToOne, + OneToMany, OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; +import { ReportBoardImages } from './report-board-images.entity'; import { Reports } from './reports.entity'; @Entity('report_boards') @@ -25,4 +27,10 @@ export class ReportBoards extends BaseEntity { }) @JoinColumn({ name: 'target_board_no' }) targetBoardNo: number; + + @OneToMany( + (type) => ReportBoardImages, + (reportBoardImages) => reportBoardImages.reportBoardNo, + ) + reportBoardImage: ReportBoardImages[]; } diff --git a/main-project/src/reports/entity/report-images.entity.ts b/main-project/src/reports/entity/report-images.entity.ts deleted file mode 100644 index 79f722fb..00000000 --- a/main-project/src/reports/entity/report-images.entity.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - BaseEntity, - Column, - Entity, - JoinColumn, - OneToOne, - PrimaryGeneratedColumn, -} from 'typeorm'; -import { Reports } from './reports.entity'; - -@Entity('report_images') -export class ReportImages extends BaseEntity { - @PrimaryGeneratedColumn() - no: number; - - @Column({ type: 'varchar', length: 255, nullable: false, name: 'img_url' }) - imgUrl: string; - - @OneToOne((type) => Reports, (reports) => reports.reportImages, { - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'report_no' }) - reportNo: number; -} diff --git a/main-project/src/reports/entity/report-user-image.entity.ts b/main-project/src/reports/entity/report-user-image.entity.ts new file mode 100644 index 00000000..d4b8db42 --- /dev/null +++ b/main-project/src/reports/entity/report-user-image.entity.ts @@ -0,0 +1,28 @@ +import { + BaseEntity, + Column, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { ReportUsers } from './report-user.entity'; + +@Entity('report _user_images') +export class ReportUserImages extends BaseEntity { + @PrimaryGeneratedColumn() + no: number; + + @Column({ type: 'varchar', length: 255, nullable: false, name: 'image_url' }) + imageUrl: string; + + @ManyToOne( + (type) => ReportUsers, + (reportUsers) => reportUsers.reportUserImage, + { + onDelete: 'CASCADE', + }, + ) + @JoinColumn({ name: 'report_user_no' }) + reportUserNo: number; +} diff --git a/main-project/src/reports/entity/report-user.entity.ts b/main-project/src/reports/entity/report-user.entity.ts index b108dd46..cdc78869 100644 --- a/main-project/src/reports/entity/report-user.entity.ts +++ b/main-project/src/reports/entity/report-user.entity.ts @@ -4,9 +4,11 @@ import { Entity, JoinColumn, ManyToOne, + OneToMany, OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; +import { ReportUserImages } from './report-user-image.entity'; import { Reports } from './reports.entity'; @Entity('report_users') @@ -25,4 +27,10 @@ export class ReportUsers extends BaseEntity { }) @JoinColumn({ name: 'target_user_no' }) targetUserNo: number; + + @OneToMany( + (type) => ReportUserImages, + (reportUserImages) => reportUserImages.reportUserNo, + ) + reportUserImage: ReportUserImages[]; } diff --git a/main-project/src/reports/entity/reports.entity.ts b/main-project/src/reports/entity/reports.entity.ts index 67a430f1..75d14329 100644 --- a/main-project/src/reports/entity/reports.entity.ts +++ b/main-project/src/reports/entity/reports.entity.ts @@ -10,7 +10,7 @@ import { OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; -import { ReportImages } from './report-images.entity'; +import { ReportBoardImages } from './report-board-images.entity'; import { ReportBoards } from './report-board.entity'; import { ReportUsers } from './report-user.entity'; @@ -31,16 +31,17 @@ export class Reports extends BaseEntity { @DeleteDateColumn({ name: 'deleted_date' }) deletedDate: Date; - @OneToOne((type) => ReportBoards, (reportBoards) => reportBoards.reportNo) + @OneToOne((type) => ReportBoards, (reportBoards) => reportBoards.reportNo, { + onDelete: 'CASCADE', + }) reportedBoard: number; - @OneToOne((type) => ReportUsers, (reportUser) => reportUser.reportNo) + @OneToOne((type) => ReportUsers, (reportUser) => reportUser.reportNo, { + onDelete: 'CASCADE', + }) reportedUser: number; @ManyToOne((type) => Users, (user) => user.report, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'user_no' }) userNo: number; - - @OneToOne((type) => ReportImages, (reportImages) => reportImages.reportNo) - reportImages: ReportImages; } diff --git a/main-project/src/reports/interface/reports.interface.ts b/main-project/src/reports/interface/reports.interface.ts index eb825837..0708a831 100644 --- a/main-project/src/reports/interface/reports.interface.ts +++ b/main-project/src/reports/interface/reports.interface.ts @@ -1,17 +1,15 @@ -import { OmitType } from '@nestjs/swagger'; -import { Reports } from '../entity/reports.entity'; - -export interface ReportDetail { - reportNo: number; - targetBoardNo?: number; - targetUserNo?: number; -} - -export interface Report { - no: number; +export interface Report { + no?: number; title: string; description: string; userNo: number; targetBoardNo?: number; targetUserNo?: number; + createdDate?: Date; + imageUrls?: T; +} + +export interface ReportImage { + imageUrl: T; + announceNo?: number; } diff --git a/main-project/src/reports/reports.controller.ts b/main-project/src/reports/reports.controller.ts index bab2ca10..9edd1fc6 100644 --- a/main-project/src/reports/reports.controller.ts +++ b/main-project/src/reports/reports.controller.ts @@ -7,12 +7,30 @@ import { ParseIntPipe, Patch, Post, + Query, + UploadedFiles, + UseGuards, + UseInterceptors, } from '@nestjs/common'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { CreateReportDto } from './dto/create-reports.dto'; -import { UpdateReportDto } from './dto/update-reports.dto'; +import { FilesInterceptor } from '@nestjs/platform-express'; +import { ApiTags } from '@nestjs/swagger'; +import { GetUser } from 'src/common/decorator/get-user.decorator'; +import { TransactionDecorator } from 'src/common/decorator/transaction-manager.decorator'; +import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; +import { TransactionInterceptor } from 'src/common/interceptor/transaction-interceptor'; +import { EntityManager } from 'typeorm'; +import { CreateReportBoardDto } from './dto/create-report-board.dto'; +import { CreateReportUserDto } from './dto/create-report-user.dto'; +import { ReportFilterDto } from './dto/report-filter.dto'; +import { UpdateReportDto } from './dto/update-report-board.dto'; import { Report } from './interface/reports.interface'; import { ReportsService } from './reports.service'; +import { ApiCreateReportBoard } from './swagger-decorator/create-board-report.decorator'; +import { ApiCreateReportUser } from './swagger-decorator/create-user-report.decorator'; +import { ApiDeleteReport } from './swagger-decorator/delete-report.decorator'; +import { ApiGetReport } from './swagger-decorator/get-report.decorator'; +import { ApiGetReports } from './swagger-decorator/get-reports.decorator'; +import { ApiUpdateReport } from './swagger-decorator/update-report.decorator'; @Controller('reports') @ApiTags('신고 API') @@ -20,93 +38,120 @@ export class ReportsController { constructor(private reportsService: ReportsService) {} // Get @Get() - @ApiOperation({ - summary: '신고 전체 조회 API', - description: '신고내역을 전부 조회한다.', - }) - async getAllReports(): Promise { - const response: Report[] = await this.reportsService.getAllReports(); + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) + @ApiGetReports() + async getReports( + @TransactionDecorator() manager: EntityManager, + @Query() reportFilterDto: ReportFilterDto, + ): Promise { + const reports: Report[] = await this.reportsService.getReports( + manager, + reportFilterDto, + ); - return { response }; + return { msg: '신고내역 전체/필터 조회 성공', response: { reports } }; } @Get('/:reportNo') - @ApiOperation({ - summary: '신고 상세 조회 API', - description: '신고 번호를 통해 해당 신고내역을 조회한다.', - }) - async getReportByNo( + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) + @ApiGetReport() + async getReport( + @TransactionDecorator() manager: EntityManager, @Param('reportNo', ParseIntPipe) reportNo: number, ): Promise { - const response: Report = await this.reportsService.getReportByNo(reportNo); + const report: Report = await this.reportsService.getReport( + manager, + reportNo, + ); - return { response }; + return { msg: '신고내역 상세조회 성공', response: { report } }; } // Post - @Post('/boards/:boardNo') - @ApiOperation({ - summary: '게시글 신고 생성 API', - description: '입력된 정보로 게시글 신고 생성.', - }) + @Post('/boards') + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) + @UseInterceptors(FilesInterceptor('files', 10)) + @ApiCreateReportBoard() async createBoardReport( - @Param('boardNo', ParseIntPipe) boardNo: number, - @Body() createReportDto: CreateReportDto, + @TransactionDecorator() manager: EntityManager, + @Body() createReportDto: CreateReportBoardDto, + @GetUser() userNo: number, + @UploadedFiles() files: Express.Multer.File[], ): Promise { - const response: number = await this.reportsService.createBoardReport( + await this.reportsService.createBoardReport( + manager, createReportDto, - boardNo, + files, + userNo, ); - return { response }; + return { msg: '게시글 신고 성공' }; } - @Post('/users/:userNo') - @ApiOperation({ - summary: '사용자 신고 생성 API', - description: '입력된 정보로 사용자 신고 생성.', - }) + @Post('/users') + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) + @UseInterceptors(FilesInterceptor('files', 10)) + @ApiCreateReportUser() async createUserReport( - @Param('userNo', ParseIntPipe) userNo: number, - @Body() createReportDto: CreateReportDto, + @TransactionDecorator() manager: EntityManager, + @Body() createReportUserDto: CreateReportUserDto, + @GetUser() userNo: number, + @UploadedFiles() files: Express.Multer.File[], ): Promise { - const response: number = await this.reportsService.createUserReport( - createReportDto, + await this.reportsService.createUserReport( + manager, + createReportUserDto, + files, userNo, ); - return { response }; + return { msg: '유저 신고 성공' }; } // Patch Methods @Patch('/:reportNo') - @ApiOperation({ - summary: '신고내용 수정 API', - description: '입력한 정보로 신고내용을 수정한다.', - }) + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) + @UseInterceptors(FilesInterceptor('files', 10)) + @ApiUpdateReport() async updateBoard( @Param('reportNo', ParseIntPipe) reportNo: number, + @TransactionDecorator() manager: EntityManager, + @GetUser() userNo: number, @Body() updateReportDto: UpdateReportDto, + @UploadedFiles() files: Express.Multer.File[], ): Promise { - const response: string = await this.reportsService.updateReport( + await this.reportsService.editReport( + manager, reportNo, updateReportDto, + userNo, + files, ); - return { response }; + return { msg: '신고내역 수정 성공' }; } // Delete Methods @Delete('/:reportNo') - @ApiOperation({ - summary: '특정 신고내역 삭제 API', - description: '신고 번호를 사용하여 해당 신고내역을 삭제한다.', - }) - async deleteReportByNo( + @UseGuards(JwtAuthGuard) + @UseInterceptors(TransactionInterceptor) + @ApiDeleteReport() + async deleteReport( @Param('reportNo', ParseIntPipe) reportNo: number, + @GetUser() userNo: number, + @TransactionDecorator() manager: EntityManager, ): Promise { - const response = await this.reportsService.deleteReportByNo(reportNo); + const report = await this.reportsService.deleteReport( + manager, + reportNo, + userNo, + ); - return { response }; + return { msg: '게시글 신고 삭제 성공' }; } } diff --git a/main-project/src/reports/reports.module.ts b/main-project/src/reports/reports.module.ts index 1f5ace9d..2054de1f 100644 --- a/main-project/src/reports/reports.module.ts +++ b/main-project/src/reports/reports.module.ts @@ -7,6 +7,7 @@ import { ReportsService } from './reports.service'; import { ReportBoardRepository } from './repository/report-board.repository'; import { ReportRepository } from './repository/reports.repository'; import { ReportUserRepository } from './repository/report-user.repository'; +import { AwsService } from 'src/aws/aws.service'; @Module({ imports: [ @@ -18,7 +19,7 @@ import { ReportUserRepository } from './repository/report-user.repository'; ReportUserRepository, ]), ], - providers: [ReportsService], + providers: [ReportsService, AwsService], controllers: [ReportsController], }) export class ReportsModule {} diff --git a/main-project/src/reports/reports.service.ts b/main-project/src/reports/reports.service.ts index 7da26804..0e6a3c58 100644 --- a/main-project/src/reports/reports.service.ts +++ b/main-project/src/reports/reports.service.ts @@ -1,233 +1,407 @@ import { BadRequestException, Injectable, - InternalServerErrorException, NotFoundException, } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; import { Board } from 'src/boards/interface/boards.interface'; import { BoardsRepository } from 'src/boards/repository/board.repository'; -import { Connection, QueryRunner } from 'typeorm'; -import { CreateReportDto } from './dto/create-reports.dto'; -import { UpdateReportDto } from './dto/update-reports.dto'; -import { Report } from './interface/reports.interface'; +import { EntityManager } from 'typeorm'; +import { CreateReportBoardDto } from './dto/create-report-board.dto'; +import { UpdateReportDto } from './dto/update-report-board.dto'; +import { Report, ReportImage } from './interface/reports.interface'; import { ReportBoardRepository } from './repository/report-board.repository'; import { ReportRepository } from './repository/reports.repository'; import { ReportUserRepository } from './repository/report-user.repository'; import { ResultSetHeader } from 'mysql2'; +import { ReportFilterDto } from './dto/report-filter.dto'; +import { AwsService } from 'src/aws/aws.service'; +import { ConfigService } from '@nestjs/config'; +import { ReportBoardImagesRepository } from './repository/report-board-image.repository'; +import { Users } from 'src/users/entity/user.entity'; +import { UsersRepository } from 'src/users/repository/users.repository'; +import { CreateReportUserDto } from './dto/create-report-user.dto'; +import { ReportUserImagesRepository } from './repository/report-user-image.repository'; +import { ReportBoards } from './entity/report-board.entity'; +import { ReportUsers } from './entity/report-user.entity'; @Injectable() export class ReportsService { constructor( - @InjectRepository(ReportRepository) - private readonly reportRepository: ReportRepository, - - @InjectRepository(BoardsRepository) - private readonly boardRepository: BoardsRepository, - - @InjectRepository(ReportBoardRepository) - private readonly boardReportRepository: ReportBoardRepository, + private readonly awsService: AwsService, + private readonly configService: ConfigService, + ) {} - @InjectRepository(ReportUserRepository) - private readonly userReportRepository: ReportUserRepository, + ADMIN_USER = this.configService.get('ADMIN_USER'); - private readonly connection: Connection, - ) {} // 조회 관련 - async getAllReports(): Promise { - const reports: Report[] = await this.reportRepository.getAllReports(); + async getReports( + manager: EntityManager, + reportFilterDto: ReportFilterDto, + ): Promise[]> { + const reports: Report[] = await manager + .getCustomRepository(ReportRepository) + .getReports(reportFilterDto); - if (!reports) { + if (!reports.length) { throw new NotFoundException( - `신고내역 전체 조회(getAllReports): 알 수 없는 서버 에러입니다.`, + `신고내역 전체 조회(getReports): 알 수 없는 서버 에러입니다.`, ); } return reports; } - async getAllBoardReports(): Promise { - const reportedBoards: Report[] = - await this.boardReportRepository.getAllBoardReports(); + async getReport( + manager: EntityManager, + reportNo: number, + ): Promise> { + const report: Report = await this.readReport(manager, reportNo); - if (!reportedBoards) { + if (!report.no) { throw new NotFoundException( - `게시글 신고내역 전체 조회(getAllReportedusers): 알 수 없는 서버 에러입니다.`, + `신고내역 상세 조회(getReport): ${reportNo}번 신고내역이 없습니다.`, ); } - return reportedBoards; + return report; } - async getAllUserReports(): Promise { - const reportedUsers: Report[] = - await this.userReportRepository.getAllUserReports(); - - if (!reportedUsers) { - throw new NotFoundException( - `사용자 신고내역 전체 조회(getAllReportedusers): 알 수 없는 서버 에러입니다.`, - ); - } + private async readReport( + manager: EntityManager, + reportNo: number, + ): Promise> { + const report: Report = await manager + .getCustomRepository(ReportRepository) + .getReport(reportNo); - return reportedUsers; + return report; } - async getReportByNo(reportNo: number): Promise { - const report: Report = await this.reportRepository.getReportByNo(reportNo); + private async readReportBoard( + manager: EntityManager, + reportNo: number, + ): Promise { + const reportBoard: ReportBoards = await manager + .getCustomRepository(ReportBoardRepository) + .getReportBoard(reportNo); - if (!report) { - throw new NotFoundException( - `${reportNo}번 신고내역 상세 조회(getReportByNo): 알 수 없는 서버 에러입니다.`, - ); - } + return reportBoard; + } - !report.targetBoardNo - ? delete report.targetBoardNo - : delete report.targetUserNo; + private async readReportUser( + manager: EntityManager, + reportNo: number, + ): Promise { + const reportUser: ReportUsers = await manager + .getCustomRepository(ReportUserRepository) + .getReportUser(reportNo); - return report; + return reportUser; } // 생성 관련 async createBoardReport( - createReportDto: CreateReportDto, + manager: EntityManager, + { boardNo, ...createReportDto }: CreateReportBoardDto, + files: Express.Multer.File[], + userNo: number, + ): Promise { + await this.validateBoard(manager, boardNo); + + const reportNo: number = await this.setReport(manager, { + ...createReportDto, + userNo, + }); + const reportBoardNo: number = await this.setBoardReport( + manager, + boardNo, + reportNo, + ); + + if (files.length) { + const imageUrls: string[] = await this.uploadImages(files); + await this.setReportBoardImages(manager, imageUrls, reportBoardNo); + } + } + + private async setBoardReport( + manager: EntityManager, boardNo: number, + reportNo: number, ): Promise { - const queryRunner: QueryRunner = this.connection.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - const board: Board = await this.boardRepository.getBoardByNo( - boardNo, - ); - if (!board.no) { - throw new BadRequestException(` - 게시글 신고 생성(createBoardReport): ${boardNo}번 게시글을 찾을 수 없습니다. - `); - } - - const reportNo: number = await this.setReport( - queryRunner, - createReportDto, - ); - - const { insertId }: ResultSetHeader = await queryRunner.manager - .getCustomRepository(ReportBoardRepository) - .createBoardReport(reportNo, boardNo); - - if (!insertId) { - throw new InternalServerErrorException( - `게시글 신고 생성(createBoardReport): 게시글 신고 생성 실패.`, - ); - } + const { insertId }: ResultSetHeader = await manager + .getCustomRepository(ReportBoardRepository) + .createBoardReport(reportNo, boardNo); - await queryRunner.commitTransaction(); + return insertId; + } - return reportNo; - } catch (error) { - await queryRunner?.rollbackTransaction(); + private async setReportBoardImages( + manager: EntityManager, + imageUrls: string[], + reportBoardNo: number, + ): Promise { + const images: ReportImage[] = this.convertReportBoardImageArray( + imageUrls, + reportBoardNo, + ); + + await manager + .getCustomRepository(ReportBoardImagesRepository) + .createBoardReportImages(images); + } - throw error; - } finally { - await queryRunner?.release(); + async createUserReport( + manager: EntityManager, + { targetUserNo, ...createReportUserDto }: CreateReportUserDto, + files: Express.Multer.File[], + userNo: number, + ): Promise { + await this.validateUser(manager, targetUserNo); + + const reportNo: number = await this.setReport(manager, { + ...createReportUserDto, + userNo, + }); + const reportUserNo: number = await this.setUserReport( + manager, + targetUserNo, + reportNo, + ); + + if (files.length) { + const imageUrls: string[] = await this.uploadImages(files); + await this.setReportUserImages(manager, imageUrls, reportUserNo); } } - async createUserReport( - createReportDto: CreateReportDto, + private async setUserReport( + manager: EntityManager, userNo: number, + reportNo: number, ): Promise { - const queryRunner: QueryRunner = this.connection.createQueryRunner(); + const { insertId }: ResultSetHeader = await manager + .getCustomRepository(ReportUserRepository) + .createUserReport(reportNo, userNo); - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - // TODO: User 확인 Method 사용 부분 + return insertId; + } - const reportNo: number = await this.setReport( - queryRunner, - createReportDto, - ); + private async setReportUserImages( + manager: EntityManager, + imageUrls: string[], + reportUserNo: number, + ): Promise { + const images: ReportImage[] = this.convertReportUserImageArray( + imageUrls, + reportUserNo, + ); + + await manager + .getCustomRepository(ReportUserImagesRepository) + .createUserReportImages(images); + } - const { insertId }: ResultSetHeader = await queryRunner.manager - .getCustomRepository(ReportUserRepository) - .createUserReport(reportNo, userNo); + private async setReport( + manager: EntityManager, + report: Report, + ): Promise { + const { insertId }: ResultSetHeader = await manager + .getCustomRepository(ReportRepository) + .createReport(report); - if (!insertId) { - throw new InternalServerErrorException( - `사용자 신고 생성(createUserReport): 알 수 없는 서버 에러입니다.`, - ); - } + return insertId; + } - await queryRunner.commitTransaction(); + //수정 관련 + async editReport( + manager: EntityManager, + reportNo: number, + updateReportDto: UpdateReportDto, + userNo: number, + files: Express.Multer.File[], + ): Promise { + const { imageUrls, ...report }: Report = await this.getReport( + manager, + reportNo, + ); + this.validateWriter(userNo, report.userNo); + + await this.updateReport(manager, reportNo, updateReportDto); + report.targetBoardNo + ? await this.editReportBoardImages(manager, files, reportNo, imageUrls) + : await this.editReportUserImages(manager, files, reportNo, imageUrls); + } + + private async updateReport( + manager: EntityManager, + reportNo: number, + updateReportDto: UpdateReportDto, + ): Promise { + await manager + .getCustomRepository(ReportRepository) + .updateReport(reportNo, updateReportDto); + } - return reportNo; - } catch (error) { - await queryRunner?.rollbackTransaction(); + private async editReportBoardImages( + manager: EntityManager, + files: Express.Multer.File[], + reportNo: number, + imageUrls: string[], + ): Promise { + const { no }: ReportBoards = await this.readReportBoard(manager, reportNo); - throw error; - } finally { - await queryRunner?.release(); + if (!imageUrls.includes(null)) { + await this.deleteReportBoardImages(manager, no); + await this.awsService.deleteFiles(imageUrls); + } + if (files.length) { + const images: string[] = await this.uploadImages(files); + await this.setReportBoardImages(manager, images, reportNo); } } - private async setReport( - queryRunner: QueryRunner, - createReportDto: CreateReportDto, - ): Promise { - const { insertId }: ResultSetHeader = await queryRunner.manager - .getCustomRepository(ReportRepository) - .createReport(createReportDto); + private async editReportUserImages( + manager: EntityManager, + files: Express.Multer.File[], + reportNo: number, + imageUrls: string[], + ): Promise { + const { no }: ReportUsers = await this.readReportUser(manager, reportNo); - if (!insertId) { - throw new InternalServerErrorException( - `신고내역 생성(setReport): 알 수 없는 서버 에러입니다.`, - ); + if (!imageUrls.includes(null)) { + await this.deleteReportUserImages(manager, no); + await this.awsService.deleteFiles(imageUrls); } + if (files.length) { + const images: string[] = await this.uploadImages(files); + await this.setReportUserImages(manager, images, reportNo); + } + } - return insertId; + // 삭제 관련 + async deleteReport( + manager: EntityManager, + reportNo: number, + userNo: number, + ): Promise { + const { imageUrls, ...report }: Report = await this.getReport( + manager, + reportNo, + ); + + this.validateWriter(userNo, report.userNo); + + if (!imageUrls.includes(null)) { + await this.awsService.deleteFiles(imageUrls); + } + await this.removeReport(manager, reportNo); } - //수정 관련 - async updateReport( + private async removeReport( + manager: EntityManager, reportNo: number, - updateReportDto: UpdateReportDto, - ): Promise { - const queryRunner: QueryRunner = this.connection.createQueryRunner(); + ): Promise { + await manager.getCustomRepository(ReportRepository).deleteReport(reportNo); + } + + private async deleteReportBoardImages( + manager: EntityManager, + reportBoardNo: number, + ): Promise { + await manager + .getCustomRepository(ReportBoardImagesRepository) + .deleteBoardReportImages(reportBoardNo); + } + + private async deleteReportUserImages( + manager: EntityManager, + reportUserNo: number, + ): Promise { + await manager + .getCustomRepository(ReportUserImagesRepository) + .deleteUserReportImages(reportUserNo); + } + + // functions + private validateWriter(userNo: number, writerNo: number): void { + if (writerNo !== userNo && this.ADMIN_USER !== userNo) { + throw new BadRequestException( + `사용자 검증(validateWriter-service): 잘못된 사용자의 접근입니다.`, + ); + } + } + + private convertReportBoardImageArray( + imageUrls: string[], + reportBoardNo: number, + ): ReportImage[] { + const images: ReportImage[] = imageUrls.map((imageUrl: string) => { + return { reportBoardNo, imageUrl }; + }); - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - await this.getReportByNo(reportNo); + return images; + } + + private convertReportUserImageArray( + imageUrls: string[], + reportUserNo: number, + ): ReportImage[] { + const images: ReportImage[] = imageUrls.map((imageUrl: string) => { + return { reportUserNo, imageUrl }; + }); - const report: number = await queryRunner.manager - .getCustomRepository(ReportRepository) - .updateReport(reportNo, updateReportDto); + return images; + } - if (!report) { - throw new InternalServerErrorException( - `신고내역 수정(updateReport): 알 수 없는 서버 에러입니다.`, - ); - } + private async validateUser( + manager: EntityManager, + targetUserNo: number, + ): Promise { + const { no }: Users = await manager + .getCustomRepository(UsersRepository) + .getUserByNo(targetUserNo); + + if (!no) { + throw new BadRequestException( + '사용자 확인(validateUser-service): 존재하지 않는 사용자 입니다', + ); + } + } - await queryRunner.commitTransaction(); + private async validateBoard( + manager: EntityManager, + boardNo: number, + ): Promise { + const { no }: Board = await manager + .getCustomRepository(BoardsRepository) + .getBoardByNo(boardNo); + if (!no) { + throw new BadRequestException( + `게시글 신고 생성(validateBoard-service): ${boardNo}번 게시글을 찾을 수 없습니다.`, + ); + } + } - return `${reportNo}번 신고내역이 수정되었습니다.`; - } catch (error) { - await queryRunner?.rollbackTransaction(); + private async validateAdmin(manager: EntityManager, userNo: number) { + const { no }: Users = await manager + .getCustomRepository(UsersRepository) + .getUserByNo(userNo); - throw error; - } finally { - await queryRunner?.release(); + if (no !== this.ADMIN_USER) { + throw new BadRequestException( + '관리자 검증(validateAdmin-service): 관리자가 아닙니다.', + ); } } - // 삭제 관련 - async deleteReportByNo(reportNo: number): Promise { - await this.getReportByNo(reportNo); - await this.reportRepository.deleteReport(reportNo); + // s3 + private async uploadImages(files: Express.Multer.File[]): Promise { + const imageUrls: string[] = await this.awsService.uploadImages( + files, + 'report', + ); - return `${reportNo}번 신고내역 삭제 성공 :)`; + return imageUrls; } } diff --git a/main-project/src/reports/repository/report-board-image.repository.ts b/main-project/src/reports/repository/report-board-image.repository.ts new file mode 100644 index 00000000..6b0bc654 --- /dev/null +++ b/main-project/src/reports/repository/report-board-image.repository.ts @@ -0,0 +1,35 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { EntityRepository, Repository } from 'typeorm'; +import { ReportBoardImages } from '../entity/report-board-images.entity'; +import { ReportImage } from '../interface/reports.interface'; + +@EntityRepository(ReportBoardImages) +export class ReportBoardImagesRepository extends Repository { + async createBoardReportImages(images: ReportImage[]): Promise { + try { + await this.createQueryBuilder() + .insert() + .into(ReportBoardImages) + .values(images) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} createBoardReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } + + async deleteBoardReportImages(reportBoardNo: number): Promise { + try { + await this.createQueryBuilder() + .delete() + .from(ReportBoardImages) + .where('reportBoardNo = :reportBoardNo', { reportBoardNo }) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} deleteBoardReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } +} diff --git a/main-project/src/reports/repository/report-board.repository.ts b/main-project/src/reports/repository/report-board.repository.ts index 6182cb50..1a0a6601 100644 --- a/main-project/src/reports/repository/report-board.repository.ts +++ b/main-project/src/reports/repository/report-board.repository.ts @@ -2,35 +2,24 @@ import { InternalServerErrorException } from '@nestjs/common'; import { ResultSetHeader } from 'mysql2'; import { EntityRepository, InsertResult, Repository } from 'typeorm'; import { ReportBoards } from '../entity/report-board.entity'; -import { Report } from '../interface/reports.interface'; @EntityRepository(ReportBoards) export class ReportBoardRepository extends Repository { - //신고글 조회 관련 - - async getAllBoardReports(): Promise { + async getReportBoard(reportNo: number): Promise { try { - const reportedBoards = this.createQueryBuilder('ReportBoards') - .leftJoin('ReportBoards.reportNo', 'reports') - .select([ - 'reports.no AS no', - 'reports.userNo AS userNo', - 'reports.title AS title', - 'reports.description AS description', - 'ReportBoards.targetBoardNo as targetBoardNo', - ]) - .where('ReportBoards.reportNo > 0') - .getRawMany(); + const reportBoard: ReportBoards = await this.createQueryBuilder() + .select() + .where('report_no = :reportNo', { reportNo }) + .getOne(); - return reportedBoards; + return reportBoard; } catch (error) { throw new InternalServerErrorException( - `${error} getAllBoardReports-repository: 알 수 없는 서버 에러입니다.`, + `${error} getReportBoard-repository: 알 수 없는 서버 에러입니다.`, ); } } - // 신고글 작성 관련 async createBoardReport( reportNo: number, boardNo: number, @@ -41,6 +30,7 @@ export class ReportBoardRepository extends Repository { .into(ReportBoards) .values({ reportNo, targetBoardNo: boardNo }) .execute(); + return raw; } catch (error) { throw new InternalServerErrorException( diff --git a/main-project/src/reports/repository/report-user-image.repository.ts b/main-project/src/reports/repository/report-user-image.repository.ts new file mode 100644 index 00000000..e06e1e68 --- /dev/null +++ b/main-project/src/reports/repository/report-user-image.repository.ts @@ -0,0 +1,35 @@ +import { InternalServerErrorException } from '@nestjs/common'; +import { EntityRepository, Repository } from 'typeorm'; +import { ReportUserImages } from '../entity/report-user-image.entity'; +import { ReportImage } from '../interface/reports.interface'; + +@EntityRepository(ReportUserImages) +export class ReportUserImagesRepository extends Repository { + async createUserReportImages(images: ReportImage[]): Promise { + try { + await this.createQueryBuilder() + .insert() + .into(ReportUserImages) + .values(images) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} createUserReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } + + async deleteUserReportImages(reportUserNo: number): Promise { + try { + await this.createQueryBuilder() + .delete() + .from(ReportUserImages) + .where('reportUserNo = :reportUserNo', { reportUserNo }) + .execute(); + } catch (error) { + throw new InternalServerErrorException( + `${error} deleteUserReportImages-repository: 알 수 없는 서버 에러입니다.`, + ); + } + } +} diff --git a/main-project/src/reports/repository/report-user.repository.ts b/main-project/src/reports/repository/report-user.repository.ts index 20e9755d..0cb6b9b5 100644 --- a/main-project/src/reports/repository/report-user.repository.ts +++ b/main-project/src/reports/repository/report-user.repository.ts @@ -2,34 +2,24 @@ import { InternalServerErrorException } from '@nestjs/common'; import { ResultSetHeader } from 'mysql2'; import { EntityRepository, InsertResult, Repository } from 'typeorm'; import { ReportUsers } from '../entity/report-user.entity'; -import { Report } from '../interface/reports.interface'; @EntityRepository(ReportUsers) export class ReportUserRepository extends Repository { - //신고글 조회 관련 - async getAllUserReports(): Promise { + async getReportUser(reportNo: number): Promise { try { - const reportedusers = this.createQueryBuilder('ReportUsers') - .leftJoin('ReportUsers.reportNo', 'reports') - .select([ - 'reports.no AS no', - 'reports.userNo AS userNo', - 'reports.title AS title', - 'reports.description AS description', - 'ReportUsers.targetUserNo as targetUserNo', - ]) - .where('ReportUsers.reportNo > 0') - .getRawMany(); + const reportUser: ReportUsers = await this.createQueryBuilder() + .select() + .where('report_no = :reportNo', { reportNo }) + .getOne(); - return reportedusers; + return reportUser; } catch (error) { throw new InternalServerErrorException( - `${error} getAllReportedusers-repository: 알 수 없는 서버 에러입니다.`, + `${error} getReportUser-repository: 알 수 없는 서버 에러입니다.`, ); } } - // 신고글 작성 관련 async createUserReport( reportNo: number, userNo: number, diff --git a/main-project/src/reports/repository/reports.repository.ts b/main-project/src/reports/repository/reports.repository.ts index b2a1e384..ac3dfe59 100644 --- a/main-project/src/reports/repository/reports.repository.ts +++ b/main-project/src/reports/repository/reports.repository.ts @@ -1,72 +1,106 @@ import { InternalServerErrorException } from '@nestjs/common'; import { ResultSetHeader } from 'mysql2'; import { - DeleteResult, EntityRepository, InsertResult, Repository, - UpdateResult, + SelectQueryBuilder, } from 'typeorm'; -import { CreateReportDto } from '../dto/create-reports.dto'; -import { UpdateReportDto } from '../dto/update-reports.dto'; +import { CreateReportBoardDto } from '../dto/create-report-board.dto'; +import { ReportFilterDto } from '../dto/report-filter.dto'; +import { UpdateReportDto } from '../dto/update-report-board.dto'; import { Reports } from '../entity/reports.entity'; import { Report } from '../interface/reports.interface'; @EntityRepository(Reports) export class ReportRepository extends Repository { //신고글 조회 관련 - async getAllReports(): Promise { + async getReports({ + type, + page, + }: ReportFilterDto): Promise[]> { try { - const reports = this.createQueryBuilder('reports') - .leftJoin('reports.reportedBoard', 'reportedBoard') - .leftJoin('reports.reportedUser', 'reportedUser') + const query: SelectQueryBuilder = this.createQueryBuilder( + 'reports', + ) + .leftJoin('reports.reportedBoard', 'reportedBoards') + .leftJoin('reports.reportedUser', 'reportedUsers') .select([ 'reports.no AS no', 'reports.userNo AS userNo', 'reports.title AS title', 'reports.description AS description', - 'reportedBoard.targetBoardNo as tagetBoardNo', - 'reportedUser.targetUserNo as tagetUserNo', + 'DATE_FORMAT(reports.createdDate, "%Y.%m.%d %T") AS createdDate', ]) .orderBy('reports.no', 'DESC') - .getRawMany(); + .groupBy('reports.no') + .limit(5); + + if (page > 1) { + query.offset((page - 1) * 5); + } + switch (type) { + case 0: + query.addSelect('reportedUsers.targetUserNo as tagetUserNo'); + break; + case 1: + query.addSelect('reportedBoards.targetBoardNo as tagetBoardNo'); + break; + default: + query.addSelect([ + 'reportedUsers.targetUserNo as tagetUserNo', + 'reportedBoards.targetBoardNo as tagetBoardNo', + ]); + } + + const reports = await query.getRawMany(); return reports; } catch (error) { throw new InternalServerErrorException( `${error} - getAllreports-repository: 알 수 없는 서버 에러입니다.`, + getReports-repository: 알 수 없는 서버 에러입니다.`, ); } } - async getReportByNo(reportNo: number): Promise { + async getReport(reportNo: number): Promise> { try { - const report = this.createQueryBuilder('reports') - .leftJoin('reports.reportedBoard', 'reportedBoard') - .leftJoin('reports.reportedUser', 'reportedUser') - .select([ - 'reports.no AS no', - 'reports.userNo AS userNo', - 'reports.title AS title', - 'reports.description AS description', - 'reportedBoard.targetBoardNo as targetBoardNo', - 'reportedUser.targetUserNo as targetUserNo', - ]) - .where('reports.no=:reportNo', { reportNo }) - .getRawOne(); + const { imageUrls, ...report }: Report = + await this.createQueryBuilder('reports') + .leftJoin('reports.reportedBoard', 'reportedBoards') + .leftJoin('reports.reportedUser', 'reportedUsers') + .leftJoin('reportedBoards.reportBoardImage', 'reportBoardImages') + .leftJoin('reportedUsers.reportUserImage', 'reportUserImages') + .select([ + 'reports.no AS no', + 'reports.userNo AS userNo', + 'reports.title AS title', + 'reports.description AS description', + 'DATE_FORMAT(reports.createdDate, "%Y.%m.%d %T") AS createdDate', + 'reportedBoards.targetBoardNo AS targetBoardNo', + 'reportedUsers.targetUserNo AS targetUserNo', + `IF(reportedBoards.reportNo = ${reportNo}, JSON_ARRAYAGG(reportBoardImages.imageUrl), JSON_ARRAYAGG(reportUserImages.imageUrl)) AS imageUrls`, + ]) + .where('reports.no = :reportNo', { reportNo }) + .getRawOne(); - return report; + const convertReport: Report = { + ...report, + imageUrls: JSON.parse(imageUrls), + }; + + return convertReport; } catch (error) { throw new InternalServerErrorException( - `${error} getReportByNo-repository: 알 수 없는 서버 에러입니다.`, + `${error} getReport-repository: 알 수 없는 서버 에러입니다.`, ); } } // 신고글 작성 관련 async createReport( - createReportDto: CreateReportDto, + createReportDto: Omit, ): Promise { try { const { raw }: InsertResult = await this.createQueryBuilder() @@ -77,7 +111,7 @@ export class ReportRepository extends Repository { return raw; } catch (error) { throw new InternalServerErrorException( - `${error} createBoard-repository: 알 수 없는 서버 에러입니다.`, + `${error} createReport-repository: 알 수 없는 서버 에러입니다.`, ); } } @@ -86,15 +120,13 @@ export class ReportRepository extends Repository { async updateReport( reportNo: number, updateReportDto: UpdateReportDto, - ): Promise { + ): Promise { try { - const { affected }: UpdateResult = await this.createQueryBuilder() + await this.createQueryBuilder() .update(Reports) .set(updateReportDto) .where('no = :reportNo', { reportNo }) .execute(); - - return affected; } catch (error) { throw new InternalServerErrorException( `${error} updateReport-repository: 알 수 없는 서버 에러입니다.`, @@ -103,17 +135,13 @@ export class ReportRepository extends Repository { } // 신고 삭제 관련 - async deleteReport(reportNo: number): Promise { + async deleteReport(reportNo: number): Promise { try { - const { affected }: DeleteResult = await this.createQueryBuilder( - 'reports', - ) + await this.createQueryBuilder() .delete() .from(Reports) .where('no = :reportNo', { reportNo }) .execute(); - - return affected; } catch (error) { throw new InternalServerErrorException( `${error} deleteReport-repository: 알 수 없는 서버 에러입니다.`, diff --git a/main-project/src/reports/swagger-decorator/create-board-report.decorator.ts b/main-project/src/reports/swagger-decorator/create-board-report.decorator.ts new file mode 100644 index 00000000..08d2636e --- /dev/null +++ b/main-project/src/reports/swagger-decorator/create-board-report.decorator.ts @@ -0,0 +1,64 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiBody, + ApiConsumes, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiCreateReportBoard() { + return applyDecorators( + ApiOperation({ + summary: '게시글 신고 생성', + }), + ApiBearerAuth(), + ApiBody({ + schema: { + type: 'object', + properties: { + title: { + type: 'string', + example: '광고성 글이네요 광고회사 사이트인 줄ㅋㅋㅋㅋ', + minLength: 2, + maxLength: 255, + nullable: false, + description: '게시글 신고 제목', + }, + description: { + type: 'string', + example: '@@@@@@@@@@@광고광고빔 빰빠마ㅃ마빠빠마빠빰', + minLength: 2, + maxLength: 255, + nullable: false, + description: '게시글 신고 내용', + }, + boardNo: { + type: 'number', + example: 54, + description: '신고할 게시글 번호', + }, + }, + }, + }), + ApiOkResponse( + SwaggerApiResponse.success( + 'Api 작동 성공 msg 반환', + '게시글 신고 생성 성공.', + ), + ), + ApiBadRequestResponse( + SwaggerApiResponse.exception([ + { + name: 'boardNotFound', + example: { + msg: `게시글 신고 생성(validateBoard-service): 3번 게시글을 찾을 수 없습니다.`, + }, + }, + ]), + ), + ); +} diff --git a/main-project/src/reports/swagger-decorator/create-user-report.decorator.ts b/main-project/src/reports/swagger-decorator/create-user-report.decorator.ts new file mode 100644 index 00000000..a2b1efd9 --- /dev/null +++ b/main-project/src/reports/swagger-decorator/create-user-report.decorator.ts @@ -0,0 +1,53 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBearerAuth, + ApiBody, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiCreateReportUser() { + return applyDecorators( + ApiOperation({ + summary: '유저 신고 생성', + }), + ApiBearerAuth(), + ApiBody({ + schema: { + type: 'object', + properties: { + title: { + type: 'string', + example: '제가 고백했는데 읽씹했어요ㅠㅠㅠㅠ', + minLength: 2, + maxLength: 255, + nullable: false, + description: '유저 신고 제목', + }, + description: { + type: 'string', + example: '자다가 침대에서 굴러 떨어지게해주세요...', + minLength: 2, + maxLength: 255, + nullable: false, + description: '유저 신고 내용', + }, + targetUserNo: { + type: 'number', + example: 54, + nullable: false, + description: '신고할 유저 번호', + }, + }, + }, + }), + ApiOkResponse( + SwaggerApiResponse.success( + 'Api 작동 성공 msg 반환', + '유저 신고 생성 성공.', + ), + ), + ); +} diff --git a/main-project/src/reports/swagger-decorator/delete-report.decorator.ts b/main-project/src/reports/swagger-decorator/delete-report.decorator.ts new file mode 100644 index 00000000..af24f75e --- /dev/null +++ b/main-project/src/reports/swagger-decorator/delete-report.decorator.ts @@ -0,0 +1,47 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiDeleteReport() { + return applyDecorators( + ApiOperation({ + summary: '게시글 신고 삭제', + }), + ApiBearerAuth(), + ApiOkResponse( + SwaggerApiResponse.success( + 'Api 작동 성공 msg 반환', + '게시글 신고 삭제 성공', + ), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'boardNotFound', + example: { msg: `존재하지 않는 게시글 번호입니다.` }, + }, + { + name: 'boardReportNotFound', + example: { msg: `존재하지 않는 게시글 신고 번호입니다.` }, + }, + ]), + ), + ApiBadRequestResponse( + SwaggerApiResponse.exception([ + { + name: 'isNotWriter', + example: { + msg: `사용자 검증(deleteReport-service): 잘못된 사용자의 접근입니다.`, + }, + }, + ]), + ), + ); +} diff --git a/main-project/src/reports/swagger-decorator/get-report.decorator.ts b/main-project/src/reports/swagger-decorator/get-report.decorator.ts new file mode 100644 index 00000000..d56e9aaa --- /dev/null +++ b/main-project/src/reports/swagger-decorator/get-report.decorator.ts @@ -0,0 +1,51 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiGetReport() { + return applyDecorators( + ApiOperation({ + summary: '신고내역 상세 조회', + }), + ApiBearerAuth(), + ApiOkResponse( + SwaggerApiResponse.success('신고내역 조회', '신고내역 조회 성공', { + enquiries: { + no: 4, + userNo: 16, + title: 'test', + description: 'test description', + isDone: 0, + createdDate: '2023.01.30 16:34:05', + }, + }), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'reportyNotFound', + example: { + msg: `신고내역 상세 조회(getReport): 3번 신고내역이 없습니다.`, + }, + }, + ]), + ), + ApiBadRequestResponse( + SwaggerApiResponse.exception([ + { + name: 'isNotWriter', + example: { + msg: `사용자 검증(getEnquiry-service): 잘못된 사용자의 접근입니다.`, + }, + }, + ]), + ), + ); +} diff --git a/main-project/src/reports/swagger-decorator/get-reports.decorator.ts b/main-project/src/reports/swagger-decorator/get-reports.decorator.ts new file mode 100644 index 00000000..ffb26bba --- /dev/null +++ b/main-project/src/reports/swagger-decorator/get-reports.decorator.ts @@ -0,0 +1,54 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBearerAuth, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiGetReports() { + return applyDecorators( + ApiOperation({ + summary: '신고내역 전체/필터 조회', + }), + ApiBearerAuth(), + ApiOkResponse( + SwaggerApiResponse.success( + '신고내역 전체/필터 조회', + '신고내역 전체/필터 조회 성공', + { + reports: [ + { + no: 4, + userNo: 16, + title: 'test', + description: 'test description', + isDone: 0, + createdDate: '2023.01.30 16:34:05', + }, + { + no: 4, + userNo: 16, + title: 'test', + description: 'test description', + isDone: 0, + createdDate: '2023.01.30 16:34:05', + }, + ], + }, + ), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'reportsNotFound', + example: { + msg: `신고내역 전체 조회(getReports-service): 신고내역이 없습니다.`, + }, + }, + ]), + ), + ); +} diff --git a/main-project/src/reports/swagger-decorator/update-report.decorator.ts b/main-project/src/reports/swagger-decorator/update-report.decorator.ts new file mode 100644 index 00000000..475f0f39 --- /dev/null +++ b/main-project/src/reports/swagger-decorator/update-report.decorator.ts @@ -0,0 +1,77 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiBody, + ApiConsumes, + ApiNotFoundResponse, + ApiOkResponse, + ApiOperation, +} from '@nestjs/swagger'; + +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiUpdateReport() { + return applyDecorators( + ApiBearerAuth(), + ApiOperation({ + summary: '신고내역 수정', + description: '신고내역의 제목, 내용, 사진을 수정', + }), + ApiConsumes('multipart/form-data'), + ApiBody({ + schema: { + type: 'object', + properties: { + title: { + type: 'string', + example: '경찰아저씨 저 사람 좀 잡아가세요', + minLength: 2, + maxLength: 255, + nullable: false, + description: '신고내역 제목', + }, + description: { + type: 'string', + example: '제 마음을 훔쳐갔어요....!', + minLength: 2, + maxLength: 255, + nullable: false, + description: '신고내역 내용', + }, + file: { + type: 'string', + format: 'binary', + description: '신고내역 이미지 파일', + }, + }, + }, + }), + ApiOkResponse( + SwaggerApiResponse.success( + 'Api 작동 성공 msg 반환', + '신고내역 수정 성공', + ), + ), + ApiNotFoundResponse( + SwaggerApiResponse.exception([ + { + name: 'reportNotFound', + example: { + msg: `신고내역 상세 조회(getReport-service): 4번 신고내역이 없습니다.`, + }, + }, + ]), + ), + ApiBadRequestResponse( + SwaggerApiResponse.exception([ + { + name: 'isNotWriter', + example: { + msg: `사용자 검증(validateWriter-service): 잘못된 사용자의 접근입니다.`, + }, + }, + ]), + ), + ); +} From 8ace2bca11d16be6fe4ec8579eadd37b4fcc6f29 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Tue, 14 Feb 2023 03:08:41 +0900 Subject: [PATCH 15/23] =?UTF-8?q?Feature(minho/nginx):=20load=20balancing?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- default.conf | 14 ++++++++++++++ docker-compose.yml | 16 +++++++++++----- dockerfile | 5 +++-- 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 default.conf diff --git a/default.conf b/default.conf new file mode 100644 index 00000000..5cde4086 --- /dev/null +++ b/default.conf @@ -0,0 +1,14 @@ +upstream app { + server nest-prod1:3000; + server nest-prod2:3002; + server nest-prod3:3003; +} + +server { + listen 80; + + location / { + proxy_pass http://app; + } + +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9a080d15..30e097c6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,24 @@ version: '3.8' services: + nginx: + container_name: nginx + image: nginx:latest + ports: + - 80:80 redis: image: redis:latest - command: redis-server --requirepass redispw --port 55001 + command: redis-server --requirepass redispw --port 6379 env_file: .env container_name: redis_prod hostname: redis_prod ports: - - 55000:55000 + - 6379:6379 + restart: always nest1: build: context: . - dockerfile: ./dockerfile + dockerfile: ./dockerfilecd env_file: .env image: ghcr.io/klaus9267/summer-auto-deploy ports: @@ -28,7 +34,7 @@ services: env_file: .env image: ghcr.io/klaus9267/summer-auto-deploy ports: - - 4000:3000 + - 3002:3000 container_name: nest-prod2 depends_on: - redis @@ -40,7 +46,7 @@ services: env_file: .env image: ghcr.io/klaus9267/summer-auto-deploy ports: - - 5000:3000 + - 3003:3000 container_name: nest-prod3 depends_on: - redis diff --git a/dockerfile b/dockerfile index 1a3a6d42..b6c0963a 100644 --- a/dockerfile +++ b/dockerfile @@ -3,8 +3,9 @@ FROM node:16.15.1 AS builder WORKDIR /home/app COPY ./main-project . +COPY default.conf /etc/nginx/conf.d/default.conf RUN npm install --force npm@8.19.2 -RUN npm run build +RUN npm run build -CMD ["npm", "run", "start:prod"] \ No newline at end of file +CMD ["npm", "run", "start:prod"] From 96259708cf88555227d620519c39b3df61c3c7c4 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:37:44 +0900 Subject: [PATCH 16/23] =?UTF-8?q?Fix(minho/swagger):=20board-swagger=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/boards/dto/guest-invite.dto.ts | 3 +- .../src/boards/dto/host-invite.dto.ts | 3 +- .../accept-guest-invite.decorator.ts | 16 ----- .../accept-host-iInvite.decorator.ts | 16 ----- .../create-board.decorator.ts | 59 ------------------- .../create-guest-team.decorator.ts | 28 --------- .../update-board.decorator.ts | 58 ------------------ .../src/common/configs/typeorm.config.ts | 2 +- 8 files changed, 5 insertions(+), 180 deletions(-) diff --git a/main-project/src/boards/dto/guest-invite.dto.ts b/main-project/src/boards/dto/guest-invite.dto.ts index 834bbd7a..36765c16 100644 --- a/main-project/src/boards/dto/guest-invite.dto.ts +++ b/main-project/src/boards/dto/guest-invite.dto.ts @@ -6,9 +6,10 @@ export class GuestInviteDto { @ApiProperty({ example: true, description: '여름 참가 시 게스트멤버로 초대받는 것에 대한 수락/거절', + required: true, }) @IsBoolean() @ToBoolean() @IsNotEmpty() - isAccepted: boolean; + readonly isAccepted: boolean; } diff --git a/main-project/src/boards/dto/host-invite.dto.ts b/main-project/src/boards/dto/host-invite.dto.ts index 88573c1b..481cae0d 100644 --- a/main-project/src/boards/dto/host-invite.dto.ts +++ b/main-project/src/boards/dto/host-invite.dto.ts @@ -6,9 +6,10 @@ export class HostInviteDto { @ApiProperty({ example: true, description: '게시글 작성 시 호스트멤버로 초대받는 것에 대한 수락/거절', + required: true, }) @IsBoolean() @ToBoolean() @IsNotEmpty() - isAccepted: boolean; + readonly isAccepted: boolean; } diff --git a/main-project/src/boards/swagger-decorator/accept-guest-invite.decorator.ts b/main-project/src/boards/swagger-decorator/accept-guest-invite.decorator.ts index 1583dbeb..c02cba39 100644 --- a/main-project/src/boards/swagger-decorator/accept-guest-invite.decorator.ts +++ b/main-project/src/boards/swagger-decorator/accept-guest-invite.decorator.ts @@ -2,8 +2,6 @@ import { applyDecorators } from '@nestjs/common'; import { ApiBadRequestResponse, ApiBearerAuth, - ApiBody, - ApiConsumes, ApiNotFoundResponse, ApiOkResponse, ApiOperation, @@ -17,20 +15,6 @@ export function ApiAcceptGuestInvite() { summary: '여름 참가 시 guestMembers 초대 수락/거절', }), ApiBearerAuth(), - ApiConsumes('multipart/form-data'), - ApiBody({ - schema: { - type: 'object', - properties: { - isAccepted: { - type: 'boolean', - example: false, - nullable: false, - description: 'guest 초대 수락/거절', - }, - }, - }, - }), ApiOkResponse( SwaggerApiResponse.success( 'Api 작동 성공 msg 반환', diff --git a/main-project/src/boards/swagger-decorator/accept-host-iInvite.decorator.ts b/main-project/src/boards/swagger-decorator/accept-host-iInvite.decorator.ts index 6636978a..daebee03 100644 --- a/main-project/src/boards/swagger-decorator/accept-host-iInvite.decorator.ts +++ b/main-project/src/boards/swagger-decorator/accept-host-iInvite.decorator.ts @@ -2,8 +2,6 @@ import { applyDecorators } from '@nestjs/common'; import { ApiBadRequestResponse, ApiBearerAuth, - ApiBody, - ApiConsumes, ApiNotFoundResponse, ApiOkResponse, ApiOperation, @@ -17,20 +15,6 @@ export function ApiAcceptHostInvite() { summary: '게시글 생성 시 hostMembers 초대 수락/거절', }), ApiBearerAuth(), - ApiConsumes('multipart/form-data'), - ApiBody({ - schema: { - type: 'object', - properties: { - isAccepted: { - type: 'boolean', - example: false, - nullable: false, - description: 'host초대 수락/거절', - }, - }, - }, - }), ApiOkResponse( SwaggerApiResponse.success( 'Api 작동 성공 msg 반환', diff --git a/main-project/src/boards/swagger-decorator/create-board.decorator.ts b/main-project/src/boards/swagger-decorator/create-board.decorator.ts index 6f59a7aa..b66e8b35 100644 --- a/main-project/src/boards/swagger-decorator/create-board.decorator.ts +++ b/main-project/src/boards/swagger-decorator/create-board.decorator.ts @@ -16,65 +16,6 @@ export function ApiCreateBoard() { summary: '게시글 생성', }), ApiBearerAuth(), - ApiBody({ - schema: { - type: 'object', - properties: { - title: { - type: 'string', - example: '산타와 함께 크리스마스를 즐길 사람 급구@@@@@@', - minLength: 2, - maxLength: 255, - nullable: false, - description: '게시글 내용', - }, - description: { - type: 'string', - example: '산타맨과 함께하는 크리스마스 파티@@@@@@@@', - minLength: 2, - maxLength: 255, - nullable: false, - description: '게시글 내용', - }, - isImpromptu: { - type: 'boolean', - example: true, - description: '즉석만남 설정 1: 즉석만남 / 0: 일반 모집', - }, - location: { - type: 'string', - example: '민들레 뜨락', - minLength: 2, - maxLength: 255, - nullable: true, - description: '예상 만남 장소', - }, - meetingTime: { - type: 'Date', - example: '2021-06-27 15:22:21', - description: '예상 만남 시간', - }, - recruitMale: { - type: 'number', - example: 233, - nullable: true, - description: '남자 모집 인원 (선택)', - }, - recruitFemale: { - type: 'number', - example: 233, - nullable: true, - description: '여자 모집 인원 (선택)', - }, - hostMembers: { - type: 'number[]', - example: [2, 5, 8], - nullable: false, - description: '호스트 측 인원들(친구)', - }, - }, - }, - }), ApiOkResponse( SwaggerApiResponse.success('Api 작동 성공 msg 반환', '게시글 생성 성공.'), ), diff --git a/main-project/src/boards/swagger-decorator/create-guest-team.decorator.ts b/main-project/src/boards/swagger-decorator/create-guest-team.decorator.ts index 57d574ee..d8375ec5 100644 --- a/main-project/src/boards/swagger-decorator/create-guest-team.decorator.ts +++ b/main-project/src/boards/swagger-decorator/create-guest-team.decorator.ts @@ -16,34 +16,6 @@ export function ApiCreateGuestTeam() { summary: '여름 참가 신청', }), ApiBearerAuth(), - ApiBody({ - schema: { - type: 'object', - properties: { - guests: { - type: 'number[]', - example: [3, 2, 98], - nullable: false, - description: - '참가 신청자를 제외한 나머지 게스트의 userNo(신청자는 jwt를 통한 userNo사용)', - }, - title: { - type: 'string', - example: '크리스마스는 혼자 였지만 이번엔 아닐거야ㅋㅋ', - minLength: 2, - maxLength: 255, - nullable: false, - description: '여름 참가글 제목', - }, - description: { - type: 'string', - example: '화끈한 사람들 다수 대기 중', - nullable: false, - description: '여름 참가글 내용', - }, - }, - }, - }), ApiOkResponse( SwaggerApiResponse.success('Api 작동 성공 msg 반환', '참가신청 성공.'), ), diff --git a/main-project/src/boards/swagger-decorator/update-board.decorator.ts b/main-project/src/boards/swagger-decorator/update-board.decorator.ts index 968631ec..1acda172 100644 --- a/main-project/src/boards/swagger-decorator/update-board.decorator.ts +++ b/main-project/src/boards/swagger-decorator/update-board.decorator.ts @@ -18,64 +18,6 @@ export function ApiUpdateBoard() { summary: '게시글 수정', description: '여름 참가 신청이 없을 시 멤버 정보도 수정 가능', }), - ApiConsumes('multipart/form-data'), - ApiBody({ - schema: { - type: 'object', - properties: { - title: { - type: 'string', - example: '산타와 함께 크리스마스를 즐길 사람 급구@@@@@@', - minLength: 2, - maxLength: 255, - nullable: false, - description: '게시글 내용', - }, - description: { - type: 'string', - example: '산타맨과 함께하는 크리스마스 파티@@@@@@@@', - minLength: 2, - maxLength: 255, - nullable: false, - description: '게시글 내용', - }, - location: { - type: 'string', - example: '민들레 뜨락', - minLength: 2, - maxLength: 255, - nullable: true, - description: '예상 만남 장소', - }, - meetimeTime: { - type: 'Date', - example: '2021-06-27 15:22:21', - description: '예상 만남 시간', - }, - recruitMale: { - type: 'number', - example: '233', - nullable: true, - description: - '남자 모집 인원 (선택) - 여름 참가 신청이 없을 시 수정 가능', - }, - recruitFeMale: { - type: 'number', - example: '233', - nullable: true, - description: - '여자 모집 인원 (선택) - 여름 참가 신청이 없을 시 수정 가능', - }, - hostMembers: { - type: 'number[]', - example: '[2, 5, 8]', - nullable: false, - description: - '호스트 측 인원들(친구) - 여름 참가 신청이 없을 시 수정 가능', - }, - }, - }, - }), ApiOkResponse( SwaggerApiResponse.success('Api 작동 성공 msg 반환', '게시글 수정 성공'), ), diff --git a/main-project/src/common/configs/typeorm.config.ts b/main-project/src/common/configs/typeorm.config.ts index 657c8b5f..b298eb2a 100644 --- a/main-project/src/common/configs/typeorm.config.ts +++ b/main-project/src/common/configs/typeorm.config.ts @@ -10,7 +10,7 @@ export const typeOrmConfig: TypeOrmModuleAsyncOptions = { username: configService.get('DB_USERNAME'), password: configService.get('DB_PASSWORD'), database: configService.get('DB_DATABASE'), - synchronize: true, + synchronize: false, entities: [__dirname + '/../../**/entity/*.entity.{js,ts}'], logging: false, }), From 4fe12e74d957f58e8133d8ae9922572d9f98bac7 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Tue, 14 Feb 2023 13:07:30 +0900 Subject: [PATCH 17/23] =?UTF-8?q?Fix(minho/swagger):=20board-swagger=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../create-board.decorator.ts | 60 ++++++++++++++++++- .../create-enquiry.decorator.ts | 2 +- .../create-reply.decorator.ts | 2 +- .../update-enquiry.decorator.ts | 2 +- .../update-reply.decorator.ts | 2 +- 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/main-project/src/boards/swagger-decorator/create-board.decorator.ts b/main-project/src/boards/swagger-decorator/create-board.decorator.ts index b66e8b35..36e255d8 100644 --- a/main-project/src/boards/swagger-decorator/create-board.decorator.ts +++ b/main-project/src/boards/swagger-decorator/create-board.decorator.ts @@ -3,7 +3,6 @@ import { ApiBadRequestResponse, ApiBearerAuth, ApiBody, - ApiConsumes, ApiOkResponse, ApiOperation, } from '@nestjs/swagger'; @@ -16,6 +15,65 @@ export function ApiCreateBoard() { summary: '게시글 생성', }), ApiBearerAuth(), + ApiBody({ + schema: { + type: 'object', + properties: { + title: { + type: 'string', + example: '산타와 함께 크리스마스를 즐길 사람 급구@@@@@@', + minLength: 2, + maxLength: 255, + nullable: false, + description: '게시글 내용', + }, + description: { + type: 'string', + example: '산타맨과 함께하는 크리스마스 파티@@@@@@@@', + minLength: 2, + maxLength: 255, + nullable: false, + description: '게시글 내용', + }, + isImpromptu: { + type: 'boolean', + example: true, + description: '즉석만남 설정 1: 즉석만남 / 0: 일반 모집', + }, + location: { + type: 'string', + example: '민들레 뜨락', + minLength: 2, + maxLength: 255, + nullable: true, + description: '예상 만남 장소', + }, + meetingTime: { + type: 'Date', + example: '2021-06-27 15:22:21', + description: '예상 만남 시간', + }, + recruitMale: { + type: 'number', + example: 2, + nullable: true, + description: '남자 모집 인원 (선택)', + }, + recruitFemale: { + type: 'number', + example: 0, + nullable: true, + description: '여자 모집 인원 (선택)', + }, + hostMembers: { + type: 'number[]', + example: [14, 15], + nullable: false, + description: '호스트 측 인원들(친구)', + }, + }, + }, + }), ApiOkResponse( SwaggerApiResponse.success('Api 작동 성공 msg 반환', '게시글 생성 성공.'), ), diff --git a/main-project/src/enquiries/swagger-decorator/create-enquiry.decorator.ts b/main-project/src/enquiries/swagger-decorator/create-enquiry.decorator.ts index 00bad663..5928319c 100644 --- a/main-project/src/enquiries/swagger-decorator/create-enquiry.decorator.ts +++ b/main-project/src/enquiries/swagger-decorator/create-enquiry.decorator.ts @@ -37,7 +37,7 @@ export function ApiCreateEnquiry() { nullable: false, description: '문의사항 내용', }, - file: { + files: { type: 'string', format: 'binary', description: '문의사항 이미지 파일', diff --git a/main-project/src/enquiries/swagger-decorator/create-reply.decorator.ts b/main-project/src/enquiries/swagger-decorator/create-reply.decorator.ts index 61897d05..7e41fade 100644 --- a/main-project/src/enquiries/swagger-decorator/create-reply.decorator.ts +++ b/main-project/src/enquiries/swagger-decorator/create-reply.decorator.ts @@ -38,7 +38,7 @@ export function ApiCreateReply() { nullable: false, description: '답변 내용', }, - file: { + files: { type: 'string', format: 'binary', description: '답변 이미지 파일', diff --git a/main-project/src/enquiries/swagger-decorator/update-enquiry.decorator.ts b/main-project/src/enquiries/swagger-decorator/update-enquiry.decorator.ts index 5e8766ec..3e13c027 100644 --- a/main-project/src/enquiries/swagger-decorator/update-enquiry.decorator.ts +++ b/main-project/src/enquiries/swagger-decorator/update-enquiry.decorator.ts @@ -39,7 +39,7 @@ export function ApiUpdateEnquiry() { nullable: false, description: '문의사항 내용', }, - file: { + files: { type: 'string', format: 'binary', description: '답변 이미지 파일', diff --git a/main-project/src/enquiries/swagger-decorator/update-reply.decorator.ts b/main-project/src/enquiries/swagger-decorator/update-reply.decorator.ts index c2b90ce1..89ea0f9d 100644 --- a/main-project/src/enquiries/swagger-decorator/update-reply.decorator.ts +++ b/main-project/src/enquiries/swagger-decorator/update-reply.decorator.ts @@ -39,7 +39,7 @@ export function ApiUpdateReply() { nullable: false, description: '답변 내용', }, - file: { + files: { type: 'string', format: 'binary', description: '답변 이미지 파일', From ed9ca9b3fe47352ae7035845b99493dc6735c059 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Tue, 14 Feb 2023 17:34:04 +0900 Subject: [PATCH 18/23] =?UTF-8?q?Fix(minho/board):=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=A0=84=EC=B2=B4/=ED=95=84=ED=84=B0=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=88=98=EC=A0=95=20#4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/boards/dto/board-filter.dto.ts | 9 ++- .../repository/board-guest.repository.ts | 2 +- .../src/boards/repository/board.repository.ts | 61 ++++++++----------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/main-project/src/boards/dto/board-filter.dto.ts b/main-project/src/boards/dto/board-filter.dto.ts index c4ca73bd..9db47ff4 100644 --- a/main-project/src/boards/dto/board-filter.dto.ts +++ b/main-project/src/boards/dto/board-filter.dto.ts @@ -1,6 +1,12 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator'; +import { + IsBoolean, + IsNumber, + IsOptional, + IsString, + Min, +} from 'class-validator'; import { ValidateGender } from 'src/common/decorator/validateGender.decorator'; import { ToBoolean } from 'src/common/decorator/validateValue.decorator'; @@ -50,6 +56,7 @@ export class BoardFilterDto { description: '번개글 :1 or true, 일반 과팅이면 작성 안해도됨', required: false, }) + @Min(1) @IsBoolean() @ToBoolean() @IsOptional() diff --git a/main-project/src/boards/repository/board-guest.repository.ts b/main-project/src/boards/repository/board-guest.repository.ts index 0be0ccfe..86306e23 100644 --- a/main-project/src/boards/repository/board-guest.repository.ts +++ b/main-project/src/boards/repository/board-guest.repository.ts @@ -12,7 +12,7 @@ export class BoardGuestsRepository extends Repository { 'boardGuest', ) .leftJoin('boardGuest.teamNo', 'team') - .select('JSON_ARRAYAGG(boardGuest.userNo) AS userNo') + .select('JSON_ARRAYAGG(boardGuest.userNo) AS guests') .where('team.boardNo = :boardNo', { boardNo }) .getRawOne(); diff --git a/main-project/src/boards/repository/board.repository.ts b/main-project/src/boards/repository/board.repository.ts index 2ff035bc..a785b419 100644 --- a/main-project/src/boards/repository/board.repository.ts +++ b/main-project/src/boards/repository/board.repository.ts @@ -82,7 +82,13 @@ export class BoardsRepository extends Repository { } } - async getBoards(filters?: BoardFilterDto): Promise[]> { + async getBoards({ + gender, + people, + page, + isDone, + isImpromptu, + }: BoardFilterDto): Promise[]> { try { const boards: SelectQueryBuilder = this.createQueryBuilder( 'boards', @@ -107,40 +113,27 @@ export class BoardsRepository extends Repository { `DATE_FORMAT(boards.createdDate, '%Y.%m.%d %T') AS createdDate`, ]) .where('boards.is_accepted = 1') - .orderBy('boards.no', 'DESC'); - - for (let idx in filters) { - switch (idx) { - case 'gender': - boards.andWhere(`boards.${filters[idx]} = :${filters[idx]}`, { - [filters[idx]]: 0, - }); - break; - - case 'people': - boards.andWhere( - 'boards.recruitMale + boards.recruitFemale = :people', - { - people: filters[idx], - }, - ); - break; - - case 'isDone': - boards.andWhere(`boards.${idx} = :${idx}`, { [idx]: filters[idx] }); - break; - - case 'isImpromptu': - boards.andWhere(`boards.${idx} = :${idx}`, { [idx]: filters[idx] }); - break; + .orderBy('boards.no', 'DESC') + .limit(10); - case 'page': - boards.limit(10).offset(filters[idx] * 10); - break; - - default: - break; - } + if (gender) { + boards.andWhere(`boards.${gender} = :gender`, { gender: 0 }); + } + if (people) { + boards.andWhere('boards.recruitMale + boards.recruitFemale = :people', { + people, + }); + } + if (isDone) { + boards.andWhere(`boards.isDone = :isDone`, { isDone }); + } + if (isImpromptu) { + boards.andWhere(`boards.isImpromptu = :isImpromptu`, { + isImpromptu, + }); + } + if (page > 1) { + boards.offset((page - 1) * 10); } return await boards.getRawMany(); From 36b2c880add5f2282002bfe2d0333abda03d2293 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 15 Feb 2023 00:37:34 +0900 Subject: [PATCH 19/23] =?UTF-8?q?Fix(minho/board):=20=EC=84=B1=EB=B3=84=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=20=EC=88=98=EC=A0=95=20#4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/common/decorator/validateGender.decorator.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/main-project/src/common/decorator/validateGender.decorator.ts b/main-project/src/common/decorator/validateGender.decorator.ts index 11084c81..6fcc3529 100644 --- a/main-project/src/common/decorator/validateGender.decorator.ts +++ b/main-project/src/common/decorator/validateGender.decorator.ts @@ -26,17 +26,11 @@ const ValidateGender = () => { }; }; -const valueToGender = (value: any) => { - if (value === null || value === undefined) { - return undefined; - } - if (typeof value === 'boolean') { - return value; - } - if (value === 0) { +const valueToGender = (value: string) => { + if (value === '0') { return 'recruitFemale'; } - if (value === 1) { + if (value === '1') { return 'recruitMale'; } throw new InternalServerErrorException(`성별을 잘못 입력하셨습니다.`); From 53193e780cdc968ad84bcdb8b02053ed1c1fdcfa Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 15 Feb 2023 01:28:58 +0900 Subject: [PATCH 20/23] =?UTF-8?q?Fix(minho/board):=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=ED=95=84=ED=84=B0/=EC=A0=84=EC=B2=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=88=98=EC=A0=95=20#4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main-project/src/boards/boards.controller.ts | 6 +++--- main-project/src/boards/boards.service.ts | 16 +++++++++----- .../src/boards/interface/boards.interface.ts | 6 ++++++ .../src/boards/repository/board.repository.ts | 21 +++++++++++-------- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/main-project/src/boards/boards.controller.ts b/main-project/src/boards/boards.controller.ts index 41b24cb8..e924573c 100644 --- a/main-project/src/boards/boards.controller.ts +++ b/main-project/src/boards/boards.controller.ts @@ -15,7 +15,7 @@ import { ApiTags } from '@nestjs/swagger'; import { BoardsService } from './boards.service'; import { CreateGuestTeamDto } from './dto/create-guest-team.dto'; import { CreateBoardDto } from './dto/create-board.dto'; -import { Board } from './interface/boards.interface'; +import { Board, BoardPagenation } from './interface/boards.interface'; import { BoardFilterDto } from './dto/board-filter.dto'; import { Cron, CronExpression } from '@nestjs/schedule/dist'; import { APIResponse } from 'src/common/interface/interface'; @@ -61,12 +61,12 @@ export class BoardsController { @TransactionDecorator() manager: EntityManager, @Query() boardFilterDto: BoardFilterDto, ): Promise { - const boards: Board[] = await this.boardService.getBoards( + const boardPagenation: BoardPagenation = await this.boardService.getBoards( manager, boardFilterDto, ); - return { msg: '게시글 필터/전체 조회 성공', response: { boards } }; + return { msg: '게시글 필터/전체 조회 성공', response: { boardPagenation } }; } @Get('/:boardNo') diff --git a/main-project/src/boards/boards.service.ts b/main-project/src/boards/boards.service.ts index a99a6c5e..e9791941 100644 --- a/main-project/src/boards/boards.service.ts +++ b/main-project/src/boards/boards.service.ts @@ -8,7 +8,13 @@ import { NoticeBoardsRepository } from 'src/notices/repository/notices-board.rep import { NoticesRepository } from 'src/notices/repository/notices.repository'; import { CreateGuestTeamDto } from './dto/create-guest-team.dto'; import { CreateBoardDto } from './dto/create-board.dto'; -import { Guest, Board, Host, GuestTeam } from './interface/boards.interface'; +import { + Guest, + Board, + Host, + GuestTeam, + BoardPagenation, +} from './interface/boards.interface'; import { BoardBookmarksRepository } from './repository/board-bookmark.repository'; import { BoardGuestsRepository as BoardGuestsRepository } from './repository/board-guest.repository'; import { BoardHostsRepository } from './repository/board-host.repository'; @@ -58,18 +64,18 @@ export class BoardsService { async getBoards( manager: EntityManager, filter: BoardFilterDto, - ): Promise[]> { - const boards: Board[] = await manager + ): Promise { + const boardPagenation: BoardPagenation = await manager .getCustomRepository(BoardsRepository) .getBoards(filter); - if (!boards.length) { + if (!boardPagenation.boards.length) { throw new NotFoundException( `게시글 전체 조회(getAllBoards-service): 조건에 맞는 게시글이 없습니다.`, ); } - return boards; + return boardPagenation; } async getBoardsByUser( diff --git a/main-project/src/boards/interface/boards.interface.ts b/main-project/src/boards/interface/boards.interface.ts index 9101ff81..30e3eb05 100644 --- a/main-project/src/boards/interface/boards.interface.ts +++ b/main-project/src/boards/interface/boards.interface.ts @@ -15,6 +15,12 @@ export class Board { createdDate?: Date; } +export class BoardPagenation { + boards: Board[]; + totalPage: number; + page: number; +} + export interface Guest { no?: number; teamNo: number; diff --git a/main-project/src/boards/repository/board.repository.ts b/main-project/src/boards/repository/board.repository.ts index a785b419..3163d478 100644 --- a/main-project/src/boards/repository/board.repository.ts +++ b/main-project/src/boards/repository/board.repository.ts @@ -15,7 +15,7 @@ import { BoardFilterDto } from '../dto/board-filter.dto'; import { CreateBoardDto } from '../dto/create-board.dto'; import { UpdateBoardDto } from '../dto/update-board.dto'; import { Boards } from '../entity/board.entity'; -import { Board } from '../interface/boards.interface'; +import { Board, BoardPagenation } from '../interface/boards.interface'; @EntityRepository(Boards) export class BoardsRepository extends Repository { @@ -88,9 +88,9 @@ export class BoardsRepository extends Repository { page, isDone, isImpromptu, - }: BoardFilterDto): Promise[]> { + }: BoardFilterDto): Promise { try { - const boards: SelectQueryBuilder = this.createQueryBuilder( + const query: SelectQueryBuilder = this.createQueryBuilder( 'boards', ) .leftJoin('boards.userNo', 'users') @@ -117,26 +117,29 @@ export class BoardsRepository extends Repository { .limit(10); if (gender) { - boards.andWhere(`boards.${gender} = :gender`, { gender: 0 }); + query.andWhere(`boards.${gender} = :gender`, { gender: 0 }); } if (people) { - boards.andWhere('boards.recruitMale + boards.recruitFemale = :people', { + query.andWhere('boards.recruitMale + boards.recruitFemale = :people', { people, }); } if (isDone) { - boards.andWhere(`boards.isDone = :isDone`, { isDone }); + query.andWhere(`boards.isDone = :isDone`, { isDone }); } if (isImpromptu) { - boards.andWhere(`boards.isImpromptu = :isImpromptu`, { + query.andWhere(`boards.isImpromptu = :isImpromptu`, { isImpromptu, }); } + const totalPage: number = Math.ceil((await query.getCount()) / 10); + if (page > 1) { - boards.offset((page - 1) * 10); + query.offset((page - 1) * 10); } + const boards: Board[] = await query.getRawMany(); - return await boards.getRawMany(); + return { boards, totalPage, page }; } catch (error) { throw new InternalServerErrorException( `${error} getBoards-repository: 알 수 없는 서버 에러입니다.`, From 2b8661081c96ab1ce200d887e863fe19b0274c71 Mon Sep 17 00:00:00 2001 From: Kim minho <90795904+klaus9267@users.noreply.github.com> Date: Wed, 15 Feb 2023 01:32:27 +0900 Subject: [PATCH 21/23] =?UTF-8?q?Fix(minho/swagger):=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=ED=95=84=ED=84=B0/=EC=A0=84=EC=B2=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20swagger=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/boards/swagger-decorator/get-boards.decorator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main-project/src/boards/swagger-decorator/get-boards.decorator.ts b/main-project/src/boards/swagger-decorator/get-boards.decorator.ts index 09dfc9c0..7af9b2fa 100644 --- a/main-project/src/boards/swagger-decorator/get-boards.decorator.ts +++ b/main-project/src/boards/swagger-decorator/get-boards.decorator.ts @@ -49,6 +49,8 @@ export function ApiGetBoards() { createdDate: '2023.01.30 16:34:05', }, ], + totalPage: 2, + page: 2, }, ), ), From 6b420397ba534672f2fd4e61b366784639bfcfe4 Mon Sep 17 00:00:00 2001 From: KimSoo Date: Wed, 15 Feb 2023 02:05:06 +0900 Subject: [PATCH 22/23] =?UTF-8?q?Feature(hyunsoo/friend):=20=EC=B9=9C?= =?UTF-8?q?=EA=B5=AC=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20#38?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/friends/friends.controller.ts | 19 ++++++++++++ main-project/src/friends/friends.service.ts | 9 ++++++ .../friends/repository/friends.repository.ts | 30 +++++++++++++++++-- .../swagger/validate-friend.decorator.ts | 18 +++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 main-project/src/friends/swagger/validate-friend.decorator.ts diff --git a/main-project/src/friends/friends.controller.ts b/main-project/src/friends/friends.controller.ts index beafcea0..8e85ba9e 100644 --- a/main-project/src/friends/friends.controller.ts +++ b/main-project/src/friends/friends.controller.ts @@ -11,6 +11,7 @@ import { UseInterceptors, } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { number } from 'joi'; import { GetUser } from 'src/common/decorator/get-user.decorator'; import { TransactionDecorator } from 'src/common/decorator/transaction-manager.decorator'; import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; @@ -26,6 +27,7 @@ import { ApiGetSentRequests } from './swagger/get-sent-requests.decorator'; import { ApiRefuseRequests } from './swagger/refuse-request.decorator'; import { ApiSearchFriends } from './swagger/search-friends.decorator'; import { ApiSendFriendRequest } from './swagger/send-friend-request.decorator'; +import { ApiValidateFriend } from './swagger/validate-friend.decorator'; @Controller('friends') @ApiTags('친구 API') @@ -148,4 +150,21 @@ export class FriendsController { response: { searchResult }, }; } + + @Get('/validate/:friendUserNo') + @ApiValidateFriend() + @UseGuards(JwtAuthGuard) + async isFriend( + @GetUser('userNo') myUserNo: number, + @Param('friendUserNo', ParseIntPipe) friendUserNo: number, + ): Promise { + const isFriend: Boolean = await this.friendsService.isFriend( + myUserNo, + friendUserNo, + ); + + return { + response: { isFriend }, + }; + } } diff --git a/main-project/src/friends/friends.service.ts b/main-project/src/friends/friends.service.ts index ddceabb4..b7e54c37 100644 --- a/main-project/src/friends/friends.service.ts +++ b/main-project/src/friends/friends.service.ts @@ -178,6 +178,15 @@ export class FriendsService { return searchResult; } + async isFriend(myUserNo: number, friendUserNo: number): Promise { + const isFriend: Friends = await this.friendsRepository.getFriend( + myUserNo, + friendUserNo, + ); + + return Boolean(isFriend); + } + private async deleteRequest(request: Friend): Promise { const deleteResult = await this.friendsRepository.deleteRequest(request); if (!deleteResult) { diff --git a/main-project/src/friends/repository/friends.repository.ts b/main-project/src/friends/repository/friends.repository.ts index 789a0191..54dd831a 100644 --- a/main-project/src/friends/repository/friends.repository.ts +++ b/main-project/src/friends/repository/friends.repository.ts @@ -4,7 +4,6 @@ import { DeleteResult, EntityRepository, InsertResult, - QueryResult, Repository, UpdateResult, } from 'typeorm'; @@ -12,7 +11,6 @@ import { Friends } from '../entity/friend.entity'; import { Friend, FriendInfo, - FriendInsertResult, FriendRequestStatus, FriendToSearch, } from '../interface/friend.interface'; @@ -231,6 +229,7 @@ export class FriendsRepository extends Repository { ); } } + async searchFriendByNickname({ userNo, nickname, @@ -275,4 +274,31 @@ export class FriendsRepository extends Repository { ); } } + + async getFriend(myUserNo, friendUserNo): Promise { + try { + const friend: Friends = await this.createQueryBuilder('friends') + .where( + `friends.receiverNo = :myUserNo AND friends.senderNo = :friendUserNo`, + { + myUserNo, + friendUserNo, + }, + ) + .orWhere( + `friends.receiverNo = :friendUserNo AND friends.senderNo = :myUserNo`, + { + friendUserNo, + myUserNo, + }, + ) + .getRawOne(); + + return friend; + } catch (error) { + throw new InternalServerErrorException( + `${error}: 친구 검색(searchFriendByNickname): 알 수 없는 서버 에러입니다. `, + ); + } + } } diff --git a/main-project/src/friends/swagger/validate-friend.decorator.ts b/main-project/src/friends/swagger/validate-friend.decorator.ts new file mode 100644 index 00000000..0cd7bcd4 --- /dev/null +++ b/main-project/src/friends/swagger/validate-friend.decorator.ts @@ -0,0 +1,18 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiBearerAuth, ApiOkResponse, ApiOperation } from '@nestjs/swagger'; +import { SwaggerApiResponse } from 'src/common/swagger/api-response.swagger'; + +export function ApiValidateFriend() { + return applyDecorators( + ApiOperation({ + summary: '친구 검증', + description: ' 파라미터로 전달한 상대 유저 번호로 친구여부 확인', + }), + ApiBearerAuth(), + ApiOkResponse( + SwaggerApiResponse.success('친구 여부 반환', undefined, { + isFriend: true, + }), + ), + ); +} From cc83b229280ceb670cf0eb3593ebd13f129f9e1b Mon Sep 17 00:00:00 2001 From: KimSoo Date: Mon, 20 Feb 2023 16:09:29 +0900 Subject: [PATCH 23/23] =?UTF-8?q?Refactor(hyunsoo/chat):=20=ED=94=84?= =?UTF-8?q?=EB=A1=A0=ED=8A=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20cors=20=EC=84=A4=EC=A0=95=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20#92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main-project/src/chats/chats.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main-project/src/chats/chats.gateway.ts b/main-project/src/chats/chats.gateway.ts index e3fa4e77..d3aa7e7d 100644 --- a/main-project/src/chats/chats.gateway.ts +++ b/main-project/src/chats/chats.gateway.ts @@ -24,7 +24,7 @@ import { EntityManager } from 'typeorm'; @WebSocketGateway(4000, { namespace: 'chat', cors: { - origin: ['http://localhost:3001'], + origin: ['http://localhost:3000', 'http://localhost:3001'], }, }) export class ChatsGateway {