Skip to content

Commit

Permalink
Merge pull request #368 from boostcampwm2023/BE/story/modify-coordina…
Browse files Browse the repository at this point in the history
…te-coordinates

[BE] ✨ : coordinate 형식 변경에 따른 v2 api 구현
  • Loading branch information
twoo1999 authored Feb 10, 2024
2 parents 6420b3e + a646fc7 commit ff317ea
Show file tree
Hide file tree
Showing 11 changed files with 898 additions and 248 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Music Spot Project

on:
push:
branches: ["BE/release"]
branches: ["main"]
jobs:
build:
runs-on: ubuntu-latest
Expand Down
38 changes: 38 additions & 0 deletions BE/musicspot/src/common/decorator/coordinate.v2.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
registerDecorator,
ValidationOptions,
ValidationArguments,
} from 'class-validator';
import { isLinestring, isPointString } from '../util/coordinate.v2.util';

export function IsCoordinateV2(validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
name: 'isCoordinateV2',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(receiveValue: string, args: ValidationArguments) {
return isPointString(receiveValue);
},
},
});
};
}

export function IsCoordinatesV2(validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
name: 'isCoordinatesV2',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(receiveValue: string, args: ValidationArguments) {
return isLinestring(receiveValue);
},
},
});
};
}
61 changes: 61 additions & 0 deletions BE/musicspot/src/common/util/coordinate.v2.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
export const isPointString = (pointString: string): boolean => {
const regex = /^\d+.\d+\s\d+.\d+$/;
if (!pointString.match(regex)) {
return false;
}
if (!isCorrectCoordinateRange(pointString)) {
return false;
}
return true;
};

export const isLinestring = (lineString: string): boolean => {
const regex: RegExp =
/^\d+.\d+\s\d+.\d+,(\d+.\d+\s\d+.\d+,)*\d+.\d+\s\d+.\d+$/;
if (!lineString.match(regex)) {
return false;
}

const points = lineString.split(',');
for (let i = 0; i < points.length; i++) {
if (!isCorrectCoordinateRange(points[i])) {
return false;
}
}
return true;
};

export const isCorrectCoordinateRange = (pointString: string): boolean => {
const [lat, lon] = pointString.split(' ').map((str) => Number(str));
if (lat > 90 || lat < -90) {
return false;
}
if (lon > 180 || lon < -180) {
return false;
}

return true;
};

export const parseCoordinateFromDtoToGeoV2 = (coordinate: string): string => {
// coordinate = 1 2
return `POINT(${coordinate})`;
};

export const parseCoordinateFromGeoToDtoV2 = (coordinate: string): string => {
// coordinate = 'POINT(1 2)'

const pointLen = 'POINT('.length;
return coordinate.slice(pointLen, -1);
};

export const parseCoordinatesFromDtoToGeoV2 = (coordinates: string): string => {
// coordinates = 1 2,3 4
return `LINESTRING(${coordinates})`;
};

export const parseCoordinatesFromGeoToDtoV2 = (coordinates: string): string => {
// coordinates = 'LINESTRING(1 2,3 4)'
const pointLen = 'LINESTRING('.length;
return coordinates.slice(pointLen, -1);
};
106 changes: 103 additions & 3 deletions BE/musicspot/src/journey/controller/journey.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ import { DeleteJourneyReqDTO } from '../dto/journeyDelete.dto';

import { Journey } from '../entities/journey.entity';
import { LastJourneyResDTO } from '../dto/journeyLast.dto';
import {
StartJourneyReqDTOV2,
StartJourneyResDTOV2,
} from '../dto/v2/startJourney.v2.dto';
import {
EndJourneyReqDTOV2,
EndJourneyResDTOV2,
} from '../dto/v2/endJourney.v2.dto';

@Controller('journey')
@ApiTags('journey 관련 API')
Expand All @@ -50,18 +58,25 @@ export class JourneyController {
description: '생성된 여정 데이터를 반환',
type: StartJourneyResDTO,
})
@Post('start')
@Post('/start')
async create(@Body() startJourneyDTO: StartJourneyReqDTO) {
return await this.journeyService.insertJourneyData(startJourneyDTO);
}

@Version('2')
@ApiOperation({
summary: '여정 시작 API',
summary: '여정 시작 API(V2)',
description: '여정 기록을 시작합니다.',
})
@ApiCreatedResponse({
description: '생성된 여정 데이터를 반환',
type: StartJourneyResDTO,
type: StartJourneyResDTOV2,
})
@Post('start')
async createV2(@Body() startJourneyDTO: StartJourneyReqDTOV2) {
return await this.journeyService.insertJourneyDataV2(startJourneyDTO);
}

@ApiOperation({
summary: '여정 종료 API',
description: '여정을 종료합니다.',
Expand All @@ -75,6 +90,20 @@ export class JourneyController {
return await this.journeyService.end(endJourneyReqDTO);
}

@Version('2')
@ApiOperation({
summary: '여정 종료 API(V2)',
description: '여정을 종료합니다.',
})
@ApiCreatedResponse({
description: '여정 종료 정보 반환',
type: EndJourneyResDTOV2,
})
@Post('end')
async endV2(@Body() endJourneyReqDTO: EndJourneyReqDTOV2) {
return await this.journeyService.endV2(endJourneyReqDTO);
}

@ApiOperation({
summary: '여정 좌표 기록API',
description: '여정의 좌표를 기록합니다.',
Expand All @@ -90,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: '해당 범위 내의 여정들을 반환합니다.',
Expand Down Expand Up @@ -138,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: '진행 중인 여정이 있었는 지 확인',
Expand All @@ -151,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를 통해 여정을 조회',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class SpotDTO {
@ApiProperty({ description: '여정 ID', example: '65649c91380cafcab8869ed2' })
readonly journeyId: string;

@ApiProperty({ description: 'spot 위치', example: [37.555913, 126.972313] })
@ApiProperty({ description: 'spot 위치', example: '37.555913 126.972313' })
readonly coordinate: number[];

@ApiProperty({ description: '기록 시간', example: '2023-11-22T12:00:00Z' })
Expand Down
88 changes: 88 additions & 0 deletions BE/musicspot/src/journey/dto/v2/endJourney.v2.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { ApiProperty } from '@nestjs/swagger';
import {
IsString,
IsDateString,
IsNumber,
IsObject,
} from 'class-validator';
import { IsCoordinatesV2 } from '../../../common/decorator/coordinate.v2.decorator';

export class EndJourneyReqDTOV2 {
@ApiProperty({
example: 20,
description: '여정 id',
required: true,
})
@IsNumber()
readonly journeyId: number;

@ApiProperty({
example: '37.555946 126.972384,37.555946 126.972384',
description: '위치 좌표',
required: true,
})
@IsCoordinatesV2({
message:
'잘못된 coordinates 형식입니다. Ex) 37.555946 126.972384,37.555946 126.972384',
})
readonly coordinates: string;

@ApiProperty({
example: '2023-11-22T12:00:00Z',
description: '종료 timestamp',
required: true,
})
@IsDateString()
readonly endTimestamp: string;

@ApiProperty({
example: '여정 제목',
description: '여정 제목',
required: true,
})
@IsString()
readonly title: string;

@IsObject()
@ApiProperty({
description: '노래 정보',
required: true,
})
readonly song: object;
}

export class EndJourneyResDTOV2 {
@ApiProperty({
example: 20,
description: '여정 id',
required: true,
})
readonly journeyId: number;

@ApiProperty({
example: '37.555946 126.972384,37.555946 126.972384',
description: '위치 좌표',
required: true,
})
readonly coordinates: string;

@ApiProperty({
example: '2023-11-22T15:30:00.000+09:00',
description: '여정 종료 시간',
required: true,
})
readonly endTimestamp: string;

@ApiProperty({
example: 2,
description: '기록된 coordinate 수',
required: true,
})
readonly numberOfCoordinates: number;

@ApiProperty({
description: '노래 정보',
required: true,
})
readonly song: object;
}
Loading

0 comments on commit ff317ea

Please sign in to comment.