From 1346f9426c9c305c07b7be7de851addf96e0a142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B0=AC=EA=B8=B0?= Date: Wed, 6 Mar 2024 23:58:44 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=ED=99=9C=EB=8F=99=ED=9B=84?= =?UTF-8?q?=EA=B8=B0=20=EB=84=A3=EB=8A=94=20API=20=EC=9E=91=EC=97=85=20Set?= =?UTF-8?q?up?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/review/review.swagger.ts | 15 ++++++ src/reviews/controllers/reviews.controller.ts | 14 +++++- src/reviews/dtos/reviews-request.dto.ts | 50 ++++++++++++++++++- src/reviews/services/reviews.service.ts | 29 ++++++++++- 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/docs/review/review.swagger.ts b/docs/review/review.swagger.ts index 5813c96..ab4b05b 100644 --- a/docs/review/review.swagger.ts +++ b/docs/review/review.swagger.ts @@ -1,5 +1,6 @@ import { applyDecorators } from '@nestjs/common'; import { + ApiBody, ApiCreatedResponse, ApiExtraModels, ApiOkResponse, @@ -9,6 +10,8 @@ import { } from '@nestjs/swagger'; import { PaginateResponseDto } from '../../src/utils/paginate-response.dto'; import { ReviewsResponseDto } from '../../src/reviews/dtos/reviews-response.dto'; +import { CreateSopticleDto } from '../../src/sopticle/dtos/create-sopticle.dto'; +import { PutReviewsRequestDto } from '../../src/reviews/dtos/reviews-request.dto'; export function GetReviewsDocs() { return applyDecorators( @@ -34,6 +37,18 @@ export function GetReviewsDocs() { ); } +export function PutReviewsDocs() { + return applyDecorators( + ApiOperation({ + summary: '여러 활동 후기 넣기', + }), + ApiBody({ + type: [PutReviewsRequestDto], + }), + ApiCreatedResponse({ type: [ReviewsResponseDto] }), + ); +} + export function GetRandomReviewByPart() { return applyDecorators( ApiOperation({ diff --git a/src/reviews/controllers/reviews.controller.ts b/src/reviews/controllers/reviews.controller.ts index 298038e..009ea34 100644 --- a/src/reviews/controllers/reviews.controller.ts +++ b/src/reviews/controllers/reviews.controller.ts @@ -11,11 +11,15 @@ import { ReviewsService } from '../services/reviews.service'; import { GetRandomReviewByPart, GetReviewsDocs, + PutReviewsDocs, ReviewEntityMigration, } from '../../../docs/review/review.swagger'; import { PaginateResponseDto } from '../../utils/paginate-response.dto'; import { ReviewsResponseDto } from '../dtos/reviews-response.dto'; -import { ReviewsRequestDto } from '../dtos/reviews-request.dto'; +import { + PutReviewsRequestDto, + ReviewsRequestDto, +} from '../dtos/reviews-request.dto'; import { ApiTags } from '@nestjs/swagger'; @UsePipes(new ValidationPipe({ transform: true })) @@ -32,6 +36,14 @@ export class ReviewsController { return await this.reviewsService.getReviews(reviewsRequestDto); } + @Post() + @PutReviewsDocs() + async putReviews( + @Body() putReviewsDto: [PutReviewsRequestDto], + ): Promise { + return await this.reviewsService.putReviews(putReviewsDto); + } + @Get('/random') @GetRandomReviewByPart() async GetRandomReviewByPart(): Promise { diff --git a/src/reviews/dtos/reviews-request.dto.ts b/src/reviews/dtos/reviews-request.dto.ts index dcfedc0..c88cda1 100644 --- a/src/reviews/dtos/reviews-request.dto.ts +++ b/src/reviews/dtos/reviews-request.dto.ts @@ -1,8 +1,9 @@ import { PageRequest } from '../../utils/paginate-request.dto'; import { ApiProperty } from '@nestjs/swagger'; import { Part } from '../../common/type'; -import { IsNumber, IsOptional } from 'class-validator'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; import { Transform } from 'class-transformer'; +import { string } from 'joi'; export class ReviewsRequestDto extends PageRequest { @ApiProperty({ @@ -24,3 +25,50 @@ export class ReviewsRequestDto extends PageRequest { @IsOptional() readonly generation: number | null; } + +export class PutReviewsRequestDto { + @ApiProperty({ + required: true, + description: '활동후기 링크', + type: String, + }) + readonly url: string; + + @ApiProperty({ + type: String, + enum: Part, + required: true, + }) + readonly part: Part; + + @ApiProperty({ + required: true, + description: + '활동기수로 필터링 합니다, 값을 넣지 않을 경우 전체 조회합니다.', + type: Number, + }) + @Transform(({ value }) => parseInt(value)) + @IsNumber() + readonly generation: number; + + @ApiProperty({ + required: true, + description: '작성자', + type: String, + }) + readonly author: string; + + @ApiProperty({ + required: true, + description: '활동 종류', + type: String, + }) + readonly subject: string; + + @ApiProperty({ + required: true, + description: '활동후기 플랫폼', + type: String, + }) + readonly platform: string; +} diff --git a/src/reviews/services/reviews.service.ts b/src/reviews/services/reviews.service.ts index 2078fce..1a04cae 100644 --- a/src/reviews/services/reviews.service.ts +++ b/src/reviews/services/reviews.service.ts @@ -3,7 +3,10 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Review } from 'src/reviews/entities/reviews.entity'; import { Repository } from 'typeorm'; import { ReviewsResponseDto } from '../dtos/reviews-response.dto'; -import { ReviewsRequestDto } from '../dtos/reviews-request.dto'; +import { + PutReviewsRequestDto, + ReviewsRequestDto, +} from '../dtos/reviews-request.dto'; import { PaginateResponseDto } from '../../utils/paginate-response.dto'; import { Part } from '../../common/type'; import { ScraperService } from '../../scraper/scraper.service'; @@ -54,6 +57,30 @@ export class ReviewsService { ); } + async putReviews(dto: PutReviewsRequestDto[]): Promise { + const promiseList: any[] = []; + const result: Review[]; + for (const review of dto) { + promiseList.push(async () => { + const scrapResult = await this.scrapperService.scrap({ + articleUrl: review, + }); + const reviewEntity: Review = { + title: scrapResult.title, + }; + + + result.push(scrapResult.) + }); + } + await Promise.all( + promiseList.map((promise) => { + return promise(); + }), + ); + + } + async getRandomReviewByPart(): Promise { const parts: Part[] = Object.values(Part); return (await Promise.all( From cf17a06b4e896886170c74d1851704c8c71bbf62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B0=AC=EA=B8=B0?= Date: Thu, 7 Mar 2024 00:56:06 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=ED=99=9C=EB=8F=99=ED=9B=84?= =?UTF-8?q?=EA=B8=B0=20=EC=82=BD=EC=9E=85=20API=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/reviews/controllers/reviews.controller.ts | 1 + src/reviews/entities/reviews.entity.ts | 36 +++++++++++++++++++ src/reviews/services/reviews.service.ts | 25 ++++++++----- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/reviews/controllers/reviews.controller.ts b/src/reviews/controllers/reviews.controller.ts index 009ea34..eda9d84 100644 --- a/src/reviews/controllers/reviews.controller.ts +++ b/src/reviews/controllers/reviews.controller.ts @@ -1,4 +1,5 @@ import { + Body, Controller, Get, Post, diff --git a/src/reviews/entities/reviews.entity.ts b/src/reviews/entities/reviews.entity.ts index 90dbbd9..b9a44f0 100644 --- a/src/reviews/entities/reviews.entity.ts +++ b/src/reviews/entities/reviews.entity.ts @@ -1,5 +1,6 @@ import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; import { Part } from '../../common/type'; +import { Sopticle } from '../../sopticle/entities/sopticle.entity'; @Index('review_pk', ['id'], { unique: true }) @Entity('Review', { schema: 'public' }) @@ -36,4 +37,39 @@ export class Review { @Column('varchar', { name: 'url', nullable: false, length: 500 }) url: string; + + static from(params: { + title: string; + author: string; + part: Part; + generation: number; + subject: string; + thumbnailUrl: string; + platform: string; + url: string; + description: string; + }) { + const review = new Review(); + const { + title, + author, + generation, + part, + subject, + thumbnailUrl, + platform, + url, + description, + } = params; + review.title = title; + review.author = author; + review.generation = generation; + review.part = part; + review.subject = subject; + review.thumbnailUrl = thumbnailUrl; + review.platform = platform; + review.url = url; + review.description = description; + return review; + } } diff --git a/src/reviews/services/reviews.service.ts b/src/reviews/services/reviews.service.ts index 1a04cae..66e7d42 100644 --- a/src/reviews/services/reviews.service.ts +++ b/src/reviews/services/reviews.service.ts @@ -59,18 +59,27 @@ export class ReviewsService { async putReviews(dto: PutReviewsRequestDto[]): Promise { const promiseList: any[] = []; - const result: Review[]; + const result: Review[] = []; for (const review of dto) { promiseList.push(async () => { const scrapResult = await this.scrapperService.scrap({ - articleUrl: review, + articleUrl: review.url, }); - const reviewEntity: Review = { - title: scrapResult.title, - }; - - result.push(scrapResult.) + const reviewEntity = await this.reviewsRepository.save( + Review.from({ + title: scrapResult.title, + description: scrapResult.description, + thumbnailUrl: scrapResult.thumbnailUrl, + generation: review.generation, + url: review.url, + part: review.part, + platform: review.platform, + author: review.author, + subject: review.subject, + }), + ); + result.push(reviewEntity); }); } await Promise.all( @@ -78,7 +87,7 @@ export class ReviewsService { return promise(); }), ); - + return result; } async getRandomReviewByPart(): Promise { From cf2bc8e2aa4b7613efaa3673e1b8525b0e1fbc3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B0=AC=EA=B8=B0?= Date: Fri, 8 Mar 2024 00:05:50 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refac:=20Put=20Review=20API=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5URL=20case=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/reviews/services/reviews.service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/reviews/services/reviews.service.ts b/src/reviews/services/reviews.service.ts index 66e7d42..3f0499c 100644 --- a/src/reviews/services/reviews.service.ts +++ b/src/reviews/services/reviews.service.ts @@ -60,7 +60,13 @@ export class ReviewsService { async putReviews(dto: PutReviewsRequestDto[]): Promise { const promiseList: any[] = []; const result: Review[] = []; + const allReviews = await this.reviewsRepository.find(); + const allReviewUrls = allReviews.map((data) => { + return data.url; + }); + for (const review of dto) { + if (allReviewUrls.includes(review.url)) continue; promiseList.push(async () => { const scrapResult = await this.scrapperService.scrap({ articleUrl: review.url,