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..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, @@ -11,11 +12,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 +37,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/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 2078fce..3f0499c 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,45 @@ 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, + }); + + 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( + promiseList.map((promise) => { + return promise(); + }), + ); + return result; + } + async getRandomReviewByPart(): Promise { const parts: Part[] = Object.values(Part); return (await Promise.all(