Skip to content

Commit

Permalink
Merge pull request #367 from boostcampwm2023/BE/task/modify-coordinat…
Browse files Browse the repository at this point in the history
…e-spot

[BE] ✨ : coordinate 형식 변경에 따른 spot dto 및 비지니스 로직 수정
  • Loading branch information
twoo1999 authored Feb 10, 2024
2 parents 4ed5ef2 + 2751a06 commit a646fc7
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 84 deletions.
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
2 changes: 1 addition & 1 deletion BE/musicspot/src/journey/service/journey.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export class JourneyService {
}

return {
journey: this.parseJourneyFromEntityToDtoV2(lastJourneyData),
journey: lastJourneyData,
isRecording: true,
};
}
Expand Down
19 changes: 19 additions & 0 deletions BE/musicspot/src/spot/controller/spot.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import {
UploadedFile,
Get,
Query,
Version,
} from '@nestjs/common';
import { ApiCreatedResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { RecordSpotReqDTO } from '../dto/recordSpot.dto';
import { SpotService } from '../service/spot.service';
import { FileInterceptor } from '@nestjs/platform-express';
import { Spot } from '../schema/spot.schema';
import { SpotDTO } from 'src/journey/dto/journeyCheck/journeyCheck.dto';
import {RecordSpotReqDTOV2} from "../dto/v2/recordSpot.v2.dto";
@Controller('spot')
@ApiTags('spot 관련 API')
export class SpotController {
Expand All @@ -35,6 +37,23 @@ export class SpotController {
return await this.spotService.create(file, recordSpotDTO);
}

@Version('2')
@ApiOperation({
summary: 'spot 기록 API',
description: 'spot을 기록합니다.',
})
@ApiCreatedResponse({
description: 'spot 생성 데이터 반환',
type: SpotDTO,
})
@UseInterceptors(FileInterceptor('image'))
@Post('')
async createV2(
@UploadedFile() file: Express.Multer.File,
@Body() recordSpotDTO,
) {
return await this.spotService.createV2(file, recordSpotDTO);
}
@ApiOperation({
summary: 'spot 조회 API',
description: 'spotId로 스팟 이미지를 조회합니다.',
Expand Down
63 changes: 63 additions & 0 deletions BE/musicspot/src/spot/dto/v2/recordSpot.v2.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsDateString, IsNumber } from 'class-validator';
import { IsCoordinateV2 } from '../../../common/decorator/coordinate.v2.decorator';

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

@ApiProperty({
example: '37.555946 126.972384',
description: '위치 좌표',
required: true,
})
@IsCoordinateV2({
message:
'위치 좌표는 2개의 숫자와 각각의 범위를 만족해야합니다.(-90~90 , -180~180)',
})
readonly coordinate: string;

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

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

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

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

@ApiProperty({
example:
'https://music-spot-storage.kr.object.ncloudstorage.com/path/name?AWSAccessKeyId=key&Expires=1701850233&Signature=signature',
description: 'presigned url',
required: true,
})
readonly photoUrl: string;
}
219 changes: 137 additions & 82 deletions BE/musicspot/src/spot/service/spot.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@nestjs/common';
import {Spot} from '../entities/spot.entity'
import { Spot } from '../entities/spot.entity';
import {
SpotNotFoundException,
SpotRecordFail,
Expand All @@ -10,99 +10,154 @@ import {
makePresignedUrl,
} from '../../common/s3/objectStorage';

import { JourneyNotFoundException, coordinateNotCorrectException } from 'src/filters/journey.exception';
import { is1DArray, parseCoordinateFromDtoToGeo } from 'src/common/util/coordinate.util';
import {
JourneyNotFoundException,
coordinateNotCorrectException,
} from 'src/filters/journey.exception';
import {
is1DArray,
parseCoordinateFromDtoToGeo, parseCoordinateFromGeoToDto,
} from 'src/common/util/coordinate.util';
import { SpotRepository } from '../repository/spot.repository';
import { RecordSpotResDTO } from '../dto/recordSpot.dto';
import { JourneyRepository } from 'src/journey/repository/journey.repository';
import {
RecordSpotReqDTOV2,
RecordSpotResDTOV2,
} from '../dto/v2/recordSpot.v2.dto';
import {
isPointString,
parseCoordinateFromDtoToGeoV2, parseCoordinateFromGeoToDtoV2,
} from '../../common/util/coordinate.v2.util';

@Injectable()
export class SpotService {
constructor(
private spotRepository: SpotRepository, private journeyRepository: JourneyRepository) {}


async uploadPhotoToStorage(journeyId, file) {
try{
const key = `${journeyId}/${Date.now()}`;
const result = await S3.putObject({
Bucket: bucketName,
Key: key,
Body: file.buffer,
}).promise();

return key;
} catch(err){
throw new SpotRecordFail();
}
}
private spotRepository: SpotRepository,
private journeyRepository: JourneyRepository,
) {}

async uploadPhotoToStorage(journeyId, file) {
try {
const key = `${journeyId}/${Date.now()}`;
const result = await S3.putObject({
Bucket: bucketName,
Key: key,
Body: file.buffer,
}).promise();


async insertToSpot(spotData){
const point = `POINT(${parseCoordinateFromDtoToGeo(spotData.coordinate)})`;
const data = {...spotData, journeyId :Number(spotData.journeyId), coordinate: point }

return await this.spotRepository.save(data);
return key;
} catch (err) {
throw new SpotRecordFail();
}

async updateCoordinatesToJourney(journeyId, coordinate){
const parsedCoordinate = parseCoordinateFromDtoToGeo(coordinate);
const originalJourney = await this.journeyRepository.findOne({where:{journeyId}})
const lineStringLen = 'LINESTRING('.length;

if(!originalJourney){
throw new JourneyNotFoundException();
}
originalJourney.coordinates = `LINESTRING(${originalJourney.coordinates.slice(lineStringLen, -1)}, ${parsedCoordinate})`


return await this.journeyRepository.save(originalJourney);
}

async insertToSpotV2(spotData) {
const data = {
...spotData,
journeyId: Number(spotData.journeyId),
};

return await this.spotRepository.save(data);
}
async insertToSpot(spotData) {
const point = `POINT(${parseCoordinateFromDtoToGeo(spotData.coordinate)})`;
const data = {
...spotData,
journeyId: Number(spotData.journeyId),
coordinate: point,
};

return await this.spotRepository.save(data);
}

async updateCoordinatesToJourney(journeyId, coordinate) {
const parsedCoordinate = parseCoordinateFromDtoToGeo(coordinate);
const originalJourney = await this.journeyRepository.findOne({
where: { journeyId },
});
const lineStringLen = 'LINESTRING('.length;

if (!originalJourney) {
throw new JourneyNotFoundException();
}

async create(file, recordSpotDto){
let parsedCoordinate;
try {
parsedCoordinate = JSON.parse(recordSpotDto.coordinate);
} catch (err) {
throw new coordinateNotCorrectException();
}
if (!is1DArray(parsedCoordinate)) {
throw new coordinateNotCorrectException();
}

const photoKey = await this.uploadPhotoToStorage(
recordSpotDto.journeyId,
file,
);
const presignedUrl = makePresignedUrl(photoKey);

const createdSpotData = await this.insertToSpot({
...recordSpotDto,
photoKey,
coordinate: parsedCoordinate
});
const updatedJourneyData = await this.updateCoordinatesToJourney(recordSpotDto.journeyId, parsedCoordinate)

const returnData:RecordSpotResDTO = {
journeyId : createdSpotData.journeyId,
coordinate : parsedCoordinate,
timestamp : createdSpotData.timestamp,
photoUrl : presignedUrl

}

return returnData
originalJourney.coordinates = `LINESTRING(${originalJourney.coordinates.slice(
lineStringLen,
-1,
)}, ${parsedCoordinate})`;

return await this.journeyRepository.save(originalJourney);
}

async createV2(file, recordSpotDto) {
const { coordinate } = recordSpotDto;
if (!isPointString(coordinate)) {
throw new coordinateNotCorrectException();
}

async getSpotImage(spotId: number) {
const spot = await this.spotRepository.findOne({where: {spotId}});
if (!spot) {
throw new SpotNotFoundException();
}

return spot.photoKey;

const photoKey: string = await this.uploadPhotoToStorage(
recordSpotDto.journeyId,
file,
);
const presignedUrl: string = makePresignedUrl(photoKey);
const createdSpotData = await this.insertToSpotV2({
...recordSpotDto,
photoKey,
coordinate: parseCoordinateFromDtoToGeoV2(coordinate),
});

const returnData: RecordSpotResDTOV2 = {
journeyId: createdSpotData.journeyId,
coordinate: parseCoordinateFromGeoToDtoV2(createdSpotData.coordinate),
timestamp: createdSpotData.timestamp,
photoUrl: presignedUrl,
};

return returnData;
}
async create(file, recordSpotDto) {
let parsedCoordinate;
try {
parsedCoordinate = JSON.parse(recordSpotDto.coordinate);
} catch (err) {
throw new coordinateNotCorrectException();
}
}
if (!is1DArray(parsedCoordinate)) {
throw new coordinateNotCorrectException();
}

const photoKey = await this.uploadPhotoToStorage(
recordSpotDto.journeyId,
file,
);
const presignedUrl = makePresignedUrl(photoKey);

const createdSpotData = await this.insertToSpot({
...recordSpotDto,
photoKey,
coordinate: parsedCoordinate,
});
const updatedJourneyData = await this.updateCoordinatesToJourney(
recordSpotDto.journeyId,
parsedCoordinate,
);

const returnData: RecordSpotResDTO = {
journeyId: createdSpotData.journeyId,
coordinate: parsedCoordinate,
timestamp: createdSpotData.timestamp,
photoUrl: presignedUrl,
};

return returnData;
}

async getSpotImage(spotId: number) {
const spot = await this.spotRepository.findOne({ where: { spotId } });
if (!spot) {
throw new SpotNotFoundException();
}

return spot.photoKey;
}
}

0 comments on commit a646fc7

Please sign in to comment.