Skip to content

Commit

Permalink
feat: 편지와 답장 통합 / 우체국에 보관된 편지 메일로 분류
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondanythings committed Dec 26, 2023
1 parent e51e403 commit b77d7e8
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 4 deletions.
8 changes: 7 additions & 1 deletion src/answers/dtos/answer.response.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsBoolean, IsDate, IsOptional, IsString, IsUUID } from 'class-validator';
import {
IsBoolean,
IsDate,
IsOptional,
IsString,
IsUUID,
} from 'class-validator';
import { Answer } from '../../entities/answer.entity';

export class AnswerDetailDto {
Expand Down
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { LetterModule } from './letters/letter.module';
import { AppController } from './app.controller';
import { AnswerModule } from './answers/answer.module';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
import { MailsModule } from './mails/mails.module';
@Module({
imports: [
PrometheusModule.register(),
Expand All @@ -37,6 +38,7 @@ import { PrometheusModule } from '@willsoto/nestjs-prometheus';
CommonModule,
LetterModule,
AnswerModule,
MailsModule,
],
controllers: [AppController],
providers: [],
Expand Down
2 changes: 1 addition & 1 deletion src/entities/letter.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export class Letter extends BaseEntity {
@Column({
name: 'sender_id',
comment: '편지 쓰는 사용자 아이디',
nullable: false,
nullable: true,
})
senderId: string;

Expand Down
7 changes: 6 additions & 1 deletion src/letters/dtos/letter.request.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
import { IsOptional, IsString, IsUUID } from 'class-validator';
import { Letter } from '../../entities/letter.entity';

export class CreateLetterDto {
Expand All @@ -21,6 +21,11 @@ export class CreateLetterDto {
@ApiProperty({ description: '고양이 이름', default: 'umu' })
@IsString()
catName: string;

@ApiProperty({ description: '답장하기 일 경우, 답장하는 메일의 아이디' })
@IsOptional()
@IsUUID('all')
replyMailId: string;
}

export function toEntity(
Expand Down
2 changes: 2 additions & 0 deletions src/letters/letter.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { Module } from '@nestjs/common';
import { LetterService } from './letter.service';
import { LetterController } from './letter.controller';
import { LetterRepository } from './letter.repository';
import { MailsModule } from 'src/mails/mails.module';

@Module({
imports: [MailsModule],
providers: [LetterService, LetterRepository],
controllers: [LetterController],
})
Expand Down
12 changes: 11 additions & 1 deletion src/letters/letter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import { createResponse } from 'src/utils/response.utils';
import { LetterDetailDto } from './dtos/letter.response.dto';
import { Response } from 'src/common/interface';
import { CreateLetterDto, toEntity } from './dtos/letter.request.dto';
import { MailsService } from 'src/mails/mails.service';

@Injectable()
export class LetterService {
constructor(private readonly lettersRepository: LetterRepository) {}
constructor(
private readonly lettersRepository: LetterRepository,
private readonly mailsService: MailsService,
) {}

/**
* 편지를 생성한다.
Expand All @@ -25,6 +29,12 @@ export class LetterService {
): Promise<Response<LetterDetailDto>> {
const letter = toEntity(userId, createLetterDto);
const newLetter = await this.lettersRepository.createLetter(letter);
if (createLetterDto.replyMailId) {
await this.mailsService.updateReplyMail({
mailId: createLetterDto.replyMailId,
replyLetterId: newLetter.id,
});
}
return createResponse(new LetterDetailDto(newLetter));
}

Expand Down
24 changes: 24 additions & 0 deletions src/mails/dtos/mails.request.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsUUID } from 'class-validator';

export class SaveMailRequestDTO {
@ApiProperty({ description: '저장할 편지 아이디' })
@IsUUID('all')
letterId: string;
}

export class ReadMailRequestDTO {
@ApiProperty({ description: '읽음처리할 메일 아이디' })
@IsUUID('all')
mailId: string;
}

export class UpdateMailRequetDTO {
@ApiProperty({ description: '메일 아이디' })
@IsUUID('all')
mailId: string;

@ApiProperty({ description: '답장으로 저장할 편지 아이디' })
@IsUUID('all')
replyLetterId: string;
}
53 changes: 53 additions & 0 deletions src/mails/dtos/mails.response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ApiProperty } from '@nestjs/swagger';
import { Mail } from '../entities/mail.entity';
import { IsBoolean, IsDate, IsString, IsUUID } from 'class-validator';

export class LetterFromMailResponseDTO {
@ApiProperty()
@IsUUID('all')
id: string;

@ApiProperty()
@IsDate()
createdAt: Date;

@ApiProperty()
@IsDate()
updatedAt: Date;

@ApiProperty()
@IsString()
senderId: string;

@ApiProperty()
@IsString()
senderNickname: string;

@ApiProperty()
@IsString()
receiverNickname: string;

@ApiProperty()
@IsString()
content: string;

@ApiProperty()
@IsString()
catName: string;

@ApiProperty()
@IsBoolean()
isRespond: boolean;

constructor(letter: Mail['letter'] & { replyLetterId: string | null }) {
this.id = letter.id;
this.createdAt = letter.createdAt;
this.updatedAt = letter.updatedAt;
this.senderId = letter.senderId;
this.senderNickname = letter.senderNickname;
this.receiverNickname = letter.receiverNickname;
this.content = letter.content;
this.catName = letter.catName;
this.isRespond = !!letter.replyLetterId;
}
}
51 changes: 51 additions & 0 deletions src/mails/entities/mail.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { BaseEntity } from 'src/common/entities/base.entity';
import { Letter } from 'src/entities/letter.entity';
import { User } from 'src/entities/user.entity';
import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from 'typeorm';

@Entity({ name: 'mails' })
export class Mail extends BaseEntity {
@Column({
type: 'uuid',
comment: '메일을 보유한 사용자 아이디',
name: 'user_id',
})
userId: string;

@ManyToOne(() => User, (user) => user.id, {
createForeignKeyConstraints: false,
})
@JoinColumn({ name: 'user_id', referencedColumnName: 'id' })
user: User;

@Column({ type: 'uuid', comment: '메일의 편지 아이디', name: 'letter_id' })
letterId: string;

@ManyToOne(() => Letter, (Letter) => Letter.id, {
createForeignKeyConstraints: false,
})
@JoinColumn({ name: 'letter_id', referencedColumnName: 'id' })
letter: Letter;

@Column({
type: 'uuid',
comment: '답장하기를 통해 보낸 편지 아이디',
name: 'received_letter_id',
nullable: true,
})
replyLetterId: string;

@OneToOne(() => Letter, (Letter) => Letter.id, {
createForeignKeyConstraints: false,
nullable: true,
})
@JoinColumn({ name: 'received_letter_id', referencedColumnName: 'id' })
replyLetter: Letter;

@Column({
name: 'is_read',
comment: '편지 확인 여부',
default: false,
})
isRead: boolean;
}
60 changes: 60 additions & 0 deletions src/mails/mails.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { AccessGuard } from 'src/auth/guards/acess.guard';
import { MailsService } from './mails.service';
import { Body, Controller, Get, Post, Put, UseGuards } from '@nestjs/common';
import { AuthUser } from 'src/auth/decorators/auth-user.decorator';
import {
ApiBearerAuth,
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { LetterFromMailResponseDTO } from './dtos/mails.response.dto';
import {
ReadMailRequestDTO,
SaveMailRequestDTO,
} from './dtos/mails.request.dto';

@ApiTags('Mails API')
@Controller('mails')
@ApiBearerAuth()
@UseGuards(AccessGuard)
export class MailsController {
constructor(private readonly mailsService: MailsService) {}

@Get()
@ApiOkResponse({
type: [LetterFromMailResponseDTO],
})
async getMyMails(@AuthUser() { id }) {
return await this.mailsService.getMyMails(id);
}

@ApiOperation({
summary: '편지 보관하기',
description:
'편지 보관하기 기능 수행 시 호출. 내가 받은 편지(Mail)에 저장된다',
})
@Put()
async saveMails(
@AuthUser() { id },
@Body() saveMailRequestDTO: SaveMailRequestDTO,
) {
return await this.mailsService.saveMails(id, saveMailRequestDTO);
}

@ApiOperation({
summary: '메일 읽기',
description: '우체국에 저장된 메일을 읽음처리 한다',
})
@Post('read')
@ApiCreatedResponse({
type: Boolean,
})
async readMails(
@AuthUser() { id },
@Body() readMailRequestDTO: ReadMailRequestDTO,
) {
return await this.mailsService.readMail(id, readMailRequestDTO);
}
}
10 changes: 10 additions & 0 deletions src/mails/mails.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { MailsController } from './mails.controller';
import { MailsService } from './mails.service';

@Module({
controllers: [MailsController],
providers: [MailsService],
exports: [MailsService],
})
export class MailsModule {}
87 changes: 87 additions & 0 deletions src/mails/mails.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { Mail } from './entities/mail.entity';
import { LetterFromMailResponseDTO } from './dtos/mails.response.dto';
import {
ReadMailRequestDTO,
SaveMailRequestDTO,
UpdateMailRequetDTO,
} from './dtos/mails.request.dto';
import { Letter } from 'src/entities/letter.entity';

@Injectable()
export class MailsService {
constructor(@InjectDataSource() private readonly dataSource: DataSource) {}

async getMyMails(userId: string) {
const myMails = await this.dataSource.getRepository(Mail).find({
where: {
userId,
},
relations: {
letter: true,
},
});
return myMails.map(
(mail) =>
new LetterFromMailResponseDTO({
...mail.letter,
replyLetterId: mail.replyLetterId,
}),
);
}

async saveMails(id: string, saveMailRequestDTO: SaveMailRequestDTO) {
const repository = this.dataSource.getRepository(Mail);
const newMails = repository.create({
userId: id,
letterId: saveMailRequestDTO.letterId,
});

await repository.save(newMails);

return newMails;
}

async readMail(id: string, readMailRequestDTO: ReadMailRequestDTO) {
const repository = this.dataSource.getRepository(Mail);
const currentMail = await repository.findOne({
where: {
userId: id,
id: readMailRequestDTO.mailId,
},
});

if (!currentMail) {
throw new BadRequestException();
}

currentMail.isRead = true;
await repository.save(currentMail);

return true;
}

async updateReplyMail(updateMailRequetDTO: UpdateMailRequetDTO) {
const repository = this.dataSource.getRepository(Mail);
const currentMail = await repository.findOne({
where: {
id: updateMailRequetDTO.mailId,
},
});

const existLetter = await this.dataSource
.createQueryBuilder()
.from(Letter, 'lt')
.where('id = :letterId', {
letterId: updateMailRequetDTO.replyLetterId,
});
if (existLetter) {
currentMail.replyLetterId = updateMailRequetDTO.replyLetterId;
await repository.save(currentMail);
return true;
}
return false;
}
}

0 comments on commit b77d7e8

Please sign in to comment.