From f1afbb442e03f51266a0e68e2c1b02cbe45dd8b6 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Fri, 9 Feb 2024 23:17:10 +0900 Subject: [PATCH 1/2] =?UTF-8?q?:recycle:=20(V2)=20coordinates=20geo->dto?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (가 포함되어 parse되던 문제 해결 --- BE/musicspot/src/common/util/coordinate.v2.util.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BE/musicspot/src/common/util/coordinate.v2.util.ts b/BE/musicspot/src/common/util/coordinate.v2.util.ts index 3734a74..3ef3cc3 100644 --- a/BE/musicspot/src/common/util/coordinate.v2.util.ts +++ b/BE/musicspot/src/common/util/coordinate.v2.util.ts @@ -12,7 +12,6 @@ export const isPointString = (pointString: string): boolean => { export const isLinestring = (lineString: string): boolean => { const regex: RegExp = /^\d+.\d+\s\d+.\d+,(\d+.\d+\s\d+.\d+,)*\d+.\d+\s\d+.\d+$/; - console.log(':ASD'); if (!lineString.match(regex)) { return false; } @@ -57,6 +56,6 @@ export const parseCoordinatesFromDtoToGeoV2 = (coordinates: string): string => { export const parseCoordinatesFromGeoToDtoV2 = (coordinates: string): string => { // coordinates = 'LINESTRING(1 2,3 4)' - const pointLen = 'LINESTRING'.length; + const pointLen = 'LINESTRING('.length; return coordinates.slice(pointLen, -1); }; From c13c9f2a6cf1edca2860af1d6dc9293e46b4ced8 Mon Sep 17 00:00:00 2001 From: twoo1999 Date: Fri, 9 Feb 2024 23:19:01 +0900 Subject: [PATCH 2/2] =?UTF-8?q?:sparkles:=20(V2)=20coordinate=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=EC=97=90=20=ED=8F=AC=ED=95=A8=EB=90=98=EB=8A=94=20jou?= =?UTF-8?q?rney=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=A4=EB=8A=94=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit coordinate 형식 수정에 따른 get 로직 수정 --- .../journey/controller/journey.controller.ts | 71 ++++++++++ .../src/journey/service/journey.service.ts | 121 ++++++++++++++++-- 2 files changed, 181 insertions(+), 11 deletions(-) diff --git a/BE/musicspot/src/journey/controller/journey.controller.ts b/BE/musicspot/src/journey/controller/journey.controller.ts index e069871..fcb88bf 100644 --- a/BE/musicspot/src/journey/controller/journey.controller.ts +++ b/BE/musicspot/src/journey/controller/journey.controller.ts @@ -119,6 +119,50 @@ export class JourneyController { return returnData; } + @Version('2') + @ApiOperation({ + summary: '여정 조회 API', + description: '해당 범위 내의 여정들을 반환합니다.', + }) + @ApiQuery({ + name: 'userId', + description: '유저 ID', + required: true, + example: 'yourUserId', + }) + @ApiQuery({ + name: 'minCoordinate', + description: '최소 좌표', + required: true, + example: '37.5 127.0', + }) + @ApiQuery({ + name: 'maxCoordinate', + description: '최대 좌표', + required: true, + example: '38.0 128.0', + }) + @ApiCreatedResponse({ + description: '범위에 있는 여정의 기록들을 반환', + type: CheckJourneyResDTO, + }) + @Get() + @UsePipes(ValidationPipe) + async getJourneyByCoordinate( + @Query('userId') userId: UUID, + @Query('minCoordinate') minCoordinate: string, + @Query('maxCoordinate') maxCoordinate: string, + ) { + console.log('min:', minCoordinate, 'max:', maxCoordinate); + const checkJourneyDTO = { + userId, + minCoordinate, + maxCoordinate, + }; + return await this.journeyService.getJourneyByCoordinationRangeV2( + checkJourneyDTO, + ); + } @ApiOperation({ summary: '여정 조회 API', description: '해당 범위 내의 여정들을 반환합니다.', @@ -167,6 +211,19 @@ export class JourneyController { ); } + @Version('2') + @ApiOperation({ + summary: '최근 여정 조회 API', + description: '진행 중인 여정이 있었는 지 확인', + }) + @ApiCreatedResponse({ + description: '사용자가 진행중이었던 여정 정보', + type: LastJourneyResDTO, + }) + @Get('last') + async loadLastDataV2(@Body('userId') userId) { + return await this.journeyService.getLastJourneyByUserIdV2(userId); + } @ApiOperation({ summary: '최근 여정 조회 API', description: '진행 중인 여정이 있었는 지 확인', @@ -180,6 +237,20 @@ export class JourneyController { return await this.journeyService.getLastJourneyByUserId(userId); } + @Version('2') + @ApiOperation({ + summary: '여정 조회 API', + description: 'journey id를 통해 여정을 조회', + }) + @ApiCreatedResponse({ + description: 'journey id에 해당하는 여정을 반환', + type: [Journey], + }) + @Get(':journeyId') + async getJourneyByIdV2(@Param('journeyId') journeyId: string) { + return await this.journeyService.getJourneyByIdV2(journeyId); + } + @ApiOperation({ summary: '여정 조회 API', description: 'journey id를 통해 여정을 조회', diff --git a/BE/musicspot/src/journey/service/journey.service.ts b/BE/musicspot/src/journey/service/journey.service.ts index f46bd65..b0911da 100644 --- a/BE/musicspot/src/journey/service/journey.service.ts +++ b/BE/musicspot/src/journey/service/journey.service.ts @@ -32,6 +32,12 @@ import { StartJourneyResDTOV2, } from '../dto/v2/startJourney.v2.dto'; import { EndJourneyReqDTOV2 } from '../dto/v2/endJourney.v2.dto'; +import { + isPointString, + parseCoordinateFromGeoToDtoV2, + parseCoordinatesFromGeoToDtoV2, +} from '../../common/util/coordinate.v2.util'; +import { UserNotFoundException } from '../../filters/user.exception'; @Injectable() export class JourneyService { @@ -117,7 +123,6 @@ export class JourneyService { } async endV2(endJourneyDTO: EndJourneyReqDTOV2) { const { coordinates, journeyId, song } = endJourneyDTO; - const coordinatesLen: number = coordinates.split(',').length; const originalData = await this.journeyRepository.findOne({ where: { journeyId }, }); @@ -135,14 +140,12 @@ export class JourneyService { const returnedDate = await this.journeyRepository.save(newJourneyData); - const parsedCoordinates = parseCoordinatesFromGeoToDto( + const parsedCoordinates = parseCoordinatesFromGeoToDtoV2( returnedDate.coordinates, ); - const returnData: EndJourneyResDTO = { + const returnData = { journeyId: returnedDate.journeyId, - coordinates: parsedCoordinates.slice( - parsedCoordinates.length - coordinatesLen, - ), + coordinates: parsedCoordinates, endTimestamp: returnedDate.endTimestamp, numberOfCoordinates: parsedCoordinates.length, song: JSON.parse(returnedDate.song), @@ -178,6 +181,43 @@ export class JourneyService { return { coordinates: updatedCoordinate.slice(len - coordinateLen) }; } + async getJourneyByCoordinationRangeV2(checkJourneyDTO) { + const { userId, minCoordinate, maxCoordinate } = checkJourneyDTO; + if (!(await this.userRepository.findOne({ where: { userId } }))) { + throw new UserNotFoundException(); + } + + if (!(isPointString(minCoordinate) && isPointString(maxCoordinate))) { + throw new coordinateNotCorrectException(); + } + + const [xMinCoordinate, yMinCoordinate] = minCoordinate + .split(' ') + .map((str) => Number(str)); + const [xMaxCoordinate, yMaxCoordinate] = maxCoordinate + .split(' ') + .map((str) => Number(str)); + console.log(xMinCoordinate, yMinCoordinate, xMaxCoordinate, yMaxCoordinate); + const coordinatesRange = { + xMinCoordinate, + yMinCoordinate, + xMaxCoordinate, + yMaxCoordinate, + }; + const returnedData = await this.journeyRepository.manager + .createQueryBuilder(Journey, 'journey') + .leftJoinAndSelect('journey.spots', 'spot') + .where( + `st_within(coordinates, ST_PolygonFromText('POLYGON((:xMinCoordinate :yMinCoordinate, :xMaxCoordinate :yMinCoordinate, :xMaxCoordinate :yMaxCoordinate, :xMinCoordinate :yMaxCoordinate, :xMinCoordinate :yMinCoordinate))'))`, + coordinatesRange, + ) + .where('userId = :userId', { userId }) + .getMany(); + console.log(returnedData); + return returnedData.map((data) => { + return this.parseJourneyFromEntityToDtoV2(data); + }); + } async getJourneyByCoordinationRange(checkJourneyDTO) { let { userId, minCoordinate, maxCoordinate } = checkJourneyDTO; if (!(Array.isArray(minCoordinate) && Array.isArray(maxCoordinate))) { @@ -210,7 +250,32 @@ export class JourneyService { return this.parseJourneyFromEntityToDto(data); }); } + async getLastJourneyByUserIdV2(userId) { + const journeys = await this.journeyRepository.manager + .createQueryBuilder(Journey, 'journey') + .where({ userId }) + .leftJoinAndSelect('journey.spots', 'spot') + .getMany(); + + if (!journeys) { + return { + journey: null, + isRecording: false, + }; + } + + const journeyLen = journeys.length; + const lastJourneyData = journeys[journeyLen - 1]; + + if (lastJourneyData.title) { + return { journey: null, isRecording: false }; + } + return { + journey: this.parseJourneyFromEntityToDtoV2(lastJourneyData), + isRecording: true, + }; + } async getLastJourneyByUserId(userId) { const journeys = await this.journeyRepository.manager .createQueryBuilder(Journey, 'journey') @@ -237,7 +302,14 @@ export class JourneyService { isRecording: true, }; } - + async getJourneyByIdV2(journeyId) { + const returnedData = await this.journeyRepository.manager + .createQueryBuilder(Journey, 'journey') + .where({ journeyId }) + .leftJoinAndSelect('journey.spots', 'spot') + .getOne(); + return this.parseJourneyFromEntityToDtoV2(returnedData); + } async getJourneyById(journeyId) { const returnedData = await this.journeyRepository.manager .createQueryBuilder(Journey, 'journey') @@ -246,7 +318,34 @@ export class JourneyService { .getOne(); return this.parseJourneyFromEntityToDto(returnedData); } - + parseJourneyFromEntityToDtoV2(journey) { + const { + journeyId, + coordinates, + startTimestamp, + endTimestamp, + song, + title, + spots, + } = journey; + return { + journeyId, + coordinates: parseCoordinatesFromGeoToDtoV2(coordinates), + title, + journeyMetadata: { + startTimestamp, + endTimestamp, + }, + song: JSON.parse(song), + spots: spots.map((spot) => { + return { + ...spot, + coordinate: parseCoordinateFromGeoToDtoV2(spot.coordinate), + photoUrl: makePresignedUrl(spot.photoKey), + }; + }), + }; + } parseJourneyFromEntityToDto(journey) { const { journeyId, @@ -262,11 +361,11 @@ export class JourneyService { coordinates: parseCoordinatesFromGeoToDto(coordinates), title, journeyMetadata: { - startTimestamp: journey.startTimestamp, - endTimestamp: journey.endTimestamp, + startTimestamp, + endTimestamp, }, song: JSON.parse(song), - spots: journey.spots.map((spot) => { + spots: spots.map((spot) => { return { ...spot, coordinate: parseCoordinateFromGeoToDto(spot.coordinate),