From ca4752cdab4c5b6584432862587099b8cf148a44 Mon Sep 17 00:00:00 2001 From: Jeonghwan Park Date: Sun, 14 Jul 2024 00:28:40 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix=20:=20workflow=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20=EA=B0=81=EA=B0=81=20=EB=8B=A4?= =?UTF-8?q?=EB=A5=B8=20runner=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=A0=88=EC=9D=B4=EB=B8=94=20=EC=A7=80?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 1d99aca..7db8cdb 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -49,7 +49,7 @@ jobs: deploy_main: needs: build if: github.ref == 'refs/heads/main' - runs-on: self-hosted + runs-on: [self-hosted, release] steps: - name: setup .env run: | @@ -73,7 +73,7 @@ jobs: deploy_dev: needs: build if: github.ref == 'refs/heads/develop' - runs-on: self-hosted + runs-on: [self-hosted, dev] steps: - name: setup .env run: | From 1981ff160d7ce811d7dfb0b42e19998852158de9 Mon Sep 17 00:00:00 2001 From: heewoong-ahn Date: Thu, 7 Nov 2024 20:31:45 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feature:=20getPlacePreviewInfoByIds=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 +- .../dtos/paginated-places-list-res.dto.ts | 21 ++++++++++++++++ src/place/repositories/place.repository.ts | 21 ++++++++++++++++ src/place/services/place.service.ts | 24 +++++++++++++++++++ src/trip/trip.controller.ts | 2 +- 5 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index b99a746..d50c81f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -64,7 +64,7 @@ import { FolderComplexModule } from './folder-complex/folder-complex.module'; TripModule, FolderModule, CollectionModule, - CrawlingModule, + //CrawlingModule, CollectionComplexModule, PlaceComplexModule, FolderComplexModule, diff --git a/src/place/dtos/paginated-places-list-res.dto.ts b/src/place/dtos/paginated-places-list-res.dto.ts index b59f3a6..ed301d5 100644 --- a/src/place/dtos/paginated-places-list-res.dto.ts +++ b/src/place/dtos/paginated-places-list-res.dto.ts @@ -8,6 +8,10 @@ import { CursorPaginationMeta, } from 'src/common/utils/cursor-pagination.util'; import { IeumCategory } from 'src/common/enums/ieum-category.enum'; +import { + createNormalList, + NormalListMeta, +} from 'src/common/utils/normal-list.util'; export class PlaceInfoDto { @ApiProperty() @@ -57,3 +61,20 @@ export class PlacesListResDto { this.items = items; } } + +export class PlacesListNoPaginationResDto { + @ApiProperty() + meta: NormalListMeta; //GET /collections에서 정의한 listMeta 재활용 + @ApiProperty({ type: [PlaceInfoDto] }) + items: PlaceInfoDto[]; + + constructor(rawData: RawPlaceInfo[]) { + const { meta, items } = createNormalList( + rawData, + (item) => new PlaceInfoDto(item), + ); + + this.meta = meta; + this.items = items; + } +} diff --git a/src/place/repositories/place.repository.ts b/src/place/repositories/place.repository.ts index 2339fe8..06cb29d 100644 --- a/src/place/repositories/place.repository.ts +++ b/src/place/repositories/place.repository.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Place } from 'src/place/entities/place.entity'; import { DataSource, Repository } from 'typeorm'; import { KakaoLocalSearchRes } from 'src/common/interfaces/kakao-local-search-res.interface'; +import { RawPlaceInfo } from 'src/common/interfaces/raw-place-info.interface'; @Injectable() export class PlaceRepository extends Repository { @@ -47,6 +48,26 @@ export class PlaceRepository extends Repository { return placePreviewInfo; } + async getPlacePreviewInfoByIds(placeIds: number[]): Promise { + const placePreviewInfoList = await this.createQueryBuilder('place') + .leftJoinAndSelect('place.placeTags', 'placeTag') + .leftJoinAndSelect('placeTag.tag', 'tag') + .leftJoinAndSelect('place.placeImages', 'placeImage') + .select([ + 'place.id AS id', + 'place.name AS name', + 'place.address AS address', + 'place.primary_category AS primary_category ', + 'ARRAY_AGG(placeImage.url ORDER BY placeImage.id DESC) AS "image_urls"', + ]) + .where('place.id IN (:...placeIds)', { placeIds }) + .groupBy('place.id') + .orderBy('place.id', 'DESC') + .getRawMany(); + + return placePreviewInfoList; + } + async getPlacesByPlaceName(placeName: string): Promise { const places = await this.createQueryBuilder('place') .select(['place.id', 'place.name']) diff --git a/src/place/services/place.service.ts b/src/place/services/place.service.ts index 4b0334d..0e695e9 100644 --- a/src/place/services/place.service.ts +++ b/src/place/services/place.service.ts @@ -22,6 +22,7 @@ import { addressSimplifier } from 'src/common/utils/address-simplifier.util'; import { GooglePlacesApiPlaceDetailsRes } from 'src/common/interfaces/google-places-api.interface'; import { KakaoCategoryMappingService } from './kakao-category-mapping.service'; import { IeumCategory } from 'src/common/enums/ieum-category.enum'; +import { PlacesListNoPaginationResDto } from '../dtos/paginated-places-list-res.dto'; @Injectable() export class PlaceService { @@ -197,6 +198,29 @@ export class PlaceService { return new PlacePreviewResDto(place, ieumCategory); } + async getPlacePreviewInfoByIds( + placeIds: number[], + ): Promise { + //빈 배열 처리. + if (placeIds.length == 0) { + return new PlacesListNoPaginationResDto([]); + } + + const rawPlacesInfoList = + await this.placeRepository.getPlacePreviewInfoByIds(placeIds); + + await Promise.all( + rawPlacesInfoList.map(async (placeInfo) => { + placeInfo.ieumCategory = await this.getIeumCategoryByKakaoCategory( + placeInfo.primary_category, + ); + return placeInfo; // 이 반환값은 실제로 사용되지 않지만, 명시적으로 표현 + }), + ); + + return new PlacesListNoPaginationResDto(rawPlacesInfoList); + } + async getPlacesByPlaceName(placeName: string): Promise { return await this.placeRepository.getPlacesByPlaceName(placeName); } diff --git a/src/trip/trip.controller.ts b/src/trip/trip.controller.ts index d1dd600..a004b19 100644 --- a/src/trip/trip.controller.ts +++ b/src/trip/trip.controller.ts @@ -1,4 +1,4 @@ import { Controller } from '@nestjs/common'; -@Controller('trip') +@Controller('trips') export class TripController {} From 27ee0bce3282c0160b50f2df43a5eed80cb7616b Mon Sep 17 00:00:00 2001 From: heewoong-ahn Date: Thu, 7 Nov 2024 21:03:20 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feature:=20=EC=9E=A5=EC=86=8C=20=EC=B6=94?= =?UTF-8?q?=EC=B2=9C,=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?=EA=BB=8D=EB=8D=B0=EA=B8=B0=20=EB=B0=8F=20req=20dto=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/place/dtos/rec-places-list-req.dto.ts | 19 +++++++++++++++++++ src/place/dtos/rec-schedule-req.dto.ts | 14 ++++++++++++++ src/place/services/place.service.ts | 9 +++++++++ 3 files changed, 42 insertions(+) create mode 100644 src/place/dtos/rec-places-list-req.dto.ts create mode 100644 src/place/dtos/rec-schedule-req.dto.ts diff --git a/src/place/dtos/rec-places-list-req.dto.ts b/src/place/dtos/rec-places-list-req.dto.ts new file mode 100644 index 0000000..521ea29 --- /dev/null +++ b/src/place/dtos/rec-places-list-req.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform, Type } from 'class-transformer'; +import { IsInt } from 'class-validator'; + +export class RecommendedPlacesListReqDto { + @ApiProperty() + @Type(() => Number) + @IsInt() + userId: number; + + @ApiProperty() + @Transform(({ value }) => (typeof value === 'number' ? [value] : value)) + placeIds: number[]; + + @ApiProperty() + @Type(() => Number) + @IsInt() + numPlaceRec: number; +} diff --git a/src/place/dtos/rec-schedule-req.dto.ts b/src/place/dtos/rec-schedule-req.dto.ts new file mode 100644 index 0000000..389844c --- /dev/null +++ b/src/place/dtos/rec-schedule-req.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform, Type } from 'class-transformer'; +import { IsInt } from 'class-validator'; + +export class RecommendedScheduleReqDto { + @ApiProperty() + @Transform(({ value }) => (typeof value === 'number' ? [value] : value)) + placeIds: number[]; + + @ApiProperty() + @Type(() => Number) + @IsInt() + numCluster: number; +} diff --git a/src/place/services/place.service.ts b/src/place/services/place.service.ts index 0e695e9..b56f0c4 100644 --- a/src/place/services/place.service.ts +++ b/src/place/services/place.service.ts @@ -23,6 +23,7 @@ import { GooglePlacesApiPlaceDetailsRes } from 'src/common/interfaces/google-pla import { KakaoCategoryMappingService } from './kakao-category-mapping.service'; import { IeumCategory } from 'src/common/enums/ieum-category.enum'; import { PlacesListNoPaginationResDto } from '../dtos/paginated-places-list-res.dto'; +import { RecommendedPlacesListReqDto } from '../dtos/rec-places-list-req.dto'; @Injectable() export class PlaceService { @@ -92,6 +93,14 @@ export class PlaceService { return placePhoto.data; } + + // --------- 외부 API 호출: AI 서버 --------- + async getRecommendedPlacesIds( + recommendedPlacesListReqDto: RecommendedPlacesListReqDto, + ) {} + + async getRecommendedSchedule() {} + // --------- 주요 메서드 --------- @Transactional() async createPlaceDetailByGooglePlacesApi(placeId: number) {