diff --git a/back/src/common/interceptors/sample.intercepotr.ts b/back/src/common/interceptors/sample.intercepotr.ts deleted file mode 100644 index 70073dd..0000000 --- a/back/src/common/interceptors/sample.intercepotr.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - Injectable, - NestInterceptor, - ExecutionContext, - CallHandler -} from '@nestjs/common'; -import { Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; - -@Injectable() -export class SampleInterceptor implements NestInterceptor { - intercept(context: ExecutionContext, next: CallHandler): Observable { - console.log('Before...'); - - // 여기에 원하는 로직 추가 가능 - - const now = Date.now(); - return next - .handle() - .pipe(tap(() => console.log(`After... ${Date.now() - now}ms`))); - } -} diff --git a/back/src/modules/message/dto/update-message-location.dto.ts b/back/src/modules/message/dto/update-message-location.dto.ts index 67545ec..e0f5998 100644 --- a/back/src/modules/message/dto/update-message-location.dto.ts +++ b/back/src/modules/message/dto/update-message-location.dto.ts @@ -2,6 +2,11 @@ import { IsNotEmpty, IsNumber, Min, Max } from '@nestjs/class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class UpdateMessageLocationDto { + @IsNumber() + @IsNotEmpty() + @ApiProperty({ type: Number, description: '메세지 ID' }) + readonly message_id: number; + @IsNumber() @Min(1) @Max(72) diff --git a/back/src/modules/message/dto/update-message-locations.dto.ts b/back/src/modules/message/dto/update-message-locations.dto.ts new file mode 100644 index 0000000..7a36286 --- /dev/null +++ b/back/src/modules/message/dto/update-message-locations.dto.ts @@ -0,0 +1,15 @@ +import { ValidateNested } from '@nestjs/class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { UpdateMessageLocationDto } from './update-message-location.dto'; +import { Expose, Type } from 'class-transformer'; + +export class UpdateMessageLocationsDto { + @Expose() + @Type(() => UpdateMessageLocationDto) + @ValidateNested({ each: true }) + @ApiProperty({ + type: [UpdateMessageLocationDto], + description: '업데이트 할 메세지 위치 리스트' + }) + readonly location_list: UpdateMessageLocationDto[]; +} diff --git a/back/src/modules/message/entity/message.entity.ts b/back/src/modules/message/entity/message.entity.ts index e7cbfd1..87c2671 100644 --- a/back/src/modules/message/entity/message.entity.ts +++ b/back/src/modules/message/entity/message.entity.ts @@ -38,8 +38,8 @@ export class MessageEntity { @Column({ length: 16 }) sender: string; - @Column({ default: false }) - is_deleted: boolean; + @CreateDateColumn({ default: `${process.env.DATE_DEFAULT}` }) + is_deleted: Date; @Column() location: number; @@ -50,7 +50,7 @@ export class MessageEntity { @Column() confidence: number; - @CreateDateColumn({ default: null }) + @CreateDateColumn({ nullable: true, default: null }) opened: Date | null; @CreateDateColumn() diff --git a/back/src/modules/message/message.controller.ts b/back/src/modules/message/message.controller.ts index a077d25..3c23dac 100644 --- a/back/src/modules/message/message.controller.ts +++ b/back/src/modules/message/message.controller.ts @@ -28,9 +28,9 @@ import { import { ResCreateMessageDto } from './dto/response/res-create-message.dto'; import { MessageDto } from './dto/message.dto'; import { JWTGuard } from 'src/common/guards/jwt.guard'; -import { UpdateMessageLocationDto } from './dto/update-message-location.dto'; import { JWTRequest } from 'src/common/interface/request.interface'; import { ClovaService } from './clova.service'; +import { UpdateMessageLocationsDto } from './dto/update-message-locations.dto'; @ApiTags('Message API') @Controller('message') export class MessageController { @@ -149,14 +149,14 @@ export class MessageController { @UseGuards(JWTGuard) @ApiCookieAuth('access_token') - @Put('/:message_id/location') + @Put('/location') @ApiOperation({ summary: '메세지 위치 변경', description: '메시지의 위치를 변경합니다.' }) @ApiResponse({ status: 200, - type: MessageDto + type: UpdateMessageLocationsDto }) @ApiBadRequestResponse({ description: '잘못된 요청입니다.' @@ -171,12 +171,8 @@ export class MessageController { description: '목표 위치가 비어있지 않습니다.' }) async updateMessageLocation( - @Param('message_id') message_id: number, - @Body() updateMessageLocationDto: UpdateMessageLocationDto - ): Promise { - return await this.messageService.updateMessageLocation( - message_id, - updateMessageLocationDto - ); + @Body() updateDtos: UpdateMessageLocationsDto + ): Promise { + return await this.messageService.updateMessageLocations(updateDtos); } } diff --git a/back/src/modules/message/message.module.ts b/back/src/modules/message/message.module.ts index 73df928..7b447bb 100644 --- a/back/src/modules/message/message.module.ts +++ b/back/src/modules/message/message.module.ts @@ -7,9 +7,15 @@ import { JWTGuard } from '../../common/guards/jwt.guard'; import { ClovaService } from './clova.service'; import { LetterEntity } from './entity/letter.entity'; import { SnowballEntity } from '../snowball/entity/snowball.entity'; +import { DecorationPrefixEntity } from '../snowball/entity/decoration-prefix.entity'; @Module({ imports: [ - TypeOrmModule.forFeature([MessageEntity, SnowballEntity, LetterEntity]) + TypeOrmModule.forFeature([ + MessageEntity, + SnowballEntity, + LetterEntity, + DecorationPrefixEntity + ]) ], controllers: [MessageController], providers: [MessageService, ClovaService, JWTGuard], diff --git a/back/src/modules/message/message.service.ts b/back/src/modules/message/message.service.ts index a35fd28..da9b8c0 100644 --- a/back/src/modules/message/message.service.ts +++ b/back/src/modules/message/message.service.ts @@ -7,7 +7,7 @@ import { NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { DataSource, Repository } from 'typeorm'; import { ReqCreateMessageDto } from './dto/request/req-create-message.dto'; import { MessageEntity } from './entity/message.entity'; import { ResCreateMessageDto } from './dto/response/res-create-message.dto'; @@ -17,6 +17,8 @@ import { plainToInstance, instanceToPlain } from 'class-transformer'; import { LetterEntity } from './entity/letter.entity'; import { ResClovaSentiment } from './clova.service'; import { SnowballEntity } from '../snowball/entity/snowball.entity'; +import { DecorationPrefixEntity } from '../snowball/entity/decoration-prefix.entity'; +import { UpdateMessageLocationsDto } from './dto/update-message-locations.dto'; @Injectable() export class MessageService { @@ -26,7 +28,10 @@ export class MessageService { @InjectRepository(SnowballEntity) private readonly snowballRepository: Repository, @InjectRepository(LetterEntity) - private readonly letterRepository: Repository + private readonly letterRepository: Repository, + @InjectRepository(DecorationPrefixEntity) + private readonly decorationPrefixRepository: Repository, + private readonly dataSource: DataSource ) {} async createMessage( createMessageDto: ReqCreateMessageDto, @@ -34,6 +39,7 @@ export class MessageService { snowball_id: number ): Promise { await this.isInsertAllowed(snowball_id); + await this.doesDecoIdExist(createMessageDto.decoration_id); await this.doesLetterIdExist(createMessageDto.letter_id); const user_id = await this.findUserId(snowball_id); @@ -42,10 +48,8 @@ export class MessageService { user_id: user_id, snowball_id: snowball_id, location: location, - opened: null, ...resClovaSentiment, ...createMessageDto - // is_deleted랑 created는 자동으로 설정 }); try { await this.messageRepository.save(messageEntity, { @@ -69,13 +73,25 @@ export class MessageService { async isInsertAllowed(snowball_id: number): Promise { const messageCount = await this.messageRepository.count({ - where: { snowball_id: snowball_id, is_deleted: false } + where: { + snowball_id: snowball_id, + is_deleted: new Date(`${process.env.DATE_DEFAULT}`) + } }); if (messageCount >= 30) { throw new ConflictException('메세지 갯수가 30개를 초과했습니다'); } } + async doesDecoIdExist(decoration_id: number): Promise { + const decoration = await this.decorationPrefixRepository.findOne({ + where: { id: decoration_id, active: true } + }); + if (!decoration) { + throw new NotFoundException('존재하지 않는 decoration id입니다'); + } + } + async doesLetterIdExist(letter_id: number): Promise { const letter = await this.letterRepository.findOne({ where: { id: letter_id, active: true } @@ -86,36 +102,35 @@ export class MessageService { } async deleteMessage(user_id: number, message_id: number): Promise { - try { - const message = await this.messageRepository.findOne({ - where: { id: message_id }, - select: ['user_id', 'is_deleted'] - }); - if (message.user_id !== user_id) { - throw new ForbiddenException( - `${message_id} 메시지는 해당 유저의 메시지가 아닙니다.` - ); - } - if (!message) { - throw new NotFoundException( - `${message_id} 메시지를 찾을 수 없었습니다.` - ); - } - if (message.is_deleted) { - throw new GoneException(`${message_id}는 이미 삭제된 메시지입니다.`); - } - await this.messageRepository.save( - { id: message_id, is_deleted: true }, - { reload: false } + const message = await this.messageRepository.findOne({ + where: { id: message_id }, + select: ['user_id', 'is_deleted'] + }); + if (message.user_id !== user_id) { + throw new ForbiddenException( + `${message_id} 메시지는 해당 유저의 메시지가 아닙니다.` ); - } catch (err) { - throw new BadRequestException('서버 오류'); } + if (!message) { + throw new NotFoundException(`${message_id} 메시지를 찾을 수 없었습니다.`); + } + console.log(message.is_deleted.getTime()); + + if (message.is_deleted.getTime() !== Number(process.env.TIME_DEFAULT)) { + throw new GoneException(`${message_id}는 이미 삭제된 메시지입니다.`); + } + await this.messageRepository.save( + { id: message_id, is_deleted: new Date() }, + { reload: false } + ); } async getAllMessages(user_id: number): Promise { const messageEntities = await this.messageRepository.find({ - where: { user_id: user_id, is_deleted: false } + where: { + user_id: user_id, + is_deleted: new Date(`${process.env.DATE_DEFAULT}`) + } }); if (!messageEntities) { throw new NotFoundException(`User with id ${user_id} not found`); @@ -130,7 +145,10 @@ export class MessageService { async openMessage(message_id: number): Promise { const messageEntity = await this.messageRepository.findOne({ - where: { id: message_id, is_deleted: false } + where: { + id: message_id, + is_deleted: new Date(`${process.env.DATE_DEFAULT}`) + } }); if (!messageEntity) { throw new NotFoundException( @@ -143,7 +161,7 @@ export class MessageService { ); } await this.messageRepository.save( - { id: message_id, opened: Date() }, + { id: message_id, opened: new Date() }, { reload: false } ); const messageDto = plainToInstance( @@ -154,30 +172,59 @@ export class MessageService { return messageDto; } + async updateMessageLocations( + updateDtos: UpdateMessageLocationsDto + ): Promise { + const result: UpdateMessageLocationDto[] = []; + console.log(updateDtos.location_list); + for (const updateDto of updateDtos.location_list) { + const success = await this.updateMessageLocation(updateDto); + if (success) result.push(updateDto); + } + const instance = { location_list: result }; + return plainToInstance(UpdateMessageLocationsDto, { + instance + }); + } + async updateMessageLocation( - message_id: number, - updateMessageLocationDto: UpdateMessageLocationDto - ): Promise { - const { location } = updateMessageLocationDto; - const updateResult = await this.messageRepository - .createQueryBuilder() - .update(MessageEntity) - .set({ - location - }) - .where('id = :id', { id: message_id, is_deleted: false }) - .execute(); - if (!updateResult.affected) { - throw new NotFoundException('업데이트를 실패했습니다'); - } else if (updateResult.affected > 1) { - throw new BadRequestException('데이터 중복 오류'); + updateDto: UpdateMessageLocationDto + ): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction('READ COMMITTED'); + + const { message_id, location } = updateDto; + + try { + await queryRunner.manager + .createQueryBuilder() + .update(MessageEntity) + .set({ + location + }) + .where('id = :id', { + id: message_id, + is_deleted: new Date(`${process.env.DATE_DEFAULT}`) + }) + .execute(); + await queryRunner.commitTransaction(); + } catch (err) { + await queryRunner.rollbackTransaction(); + console.log(err.sqlMessage); + return false; + } finally { + await queryRunner.release(); } - return updateMessageLocationDto; + return true; } async getMessageCount(user_id: number): Promise { return this.messageRepository.count({ - where: { user_id: user_id, is_deleted: false } + where: { + user_id: user_id, + is_deleted: new Date(`${process.env.DATE_DEFAULT}`) + } }); } @@ -186,7 +233,10 @@ export class MessageService { groups: string ): Promise { const messageEntities = await this.messageRepository.find({ - where: { snowball_id: snowball_id, is_deleted: false } + where: { + snowball_id: snowball_id, + is_deleted: new Date(`${process.env.DATE_DEFAULT}`) + } }); const messageDtos = messageEntities.map(entity => @@ -212,12 +262,14 @@ export class MessageService { } async findLocation(snowball_id: number): Promise { - const findLocations = this.messageRepository - .createQueryBuilder('message') + const locations = await this.messageRepository + .createQueryBuilder() .select('location') .where('snowball_id = :snowball_id', { snowball_id }) - .andWhere('is_deleted = false'); - const locations = await findLocations.getRawMany(); + .andWhere('is_deleted =:is_deleted', { + is_deleted: `${process.env.DATE_DEFAULT}` + }) + .getRawMany(); let firstEmptyLocation: number | null = null; for (let i = 1; i <= 30; i++) {