Skip to content

Commit

Permalink
Merge pull request #180 from boostcampwm2023/feat/178-stellar-route-api
Browse files Browse the repository at this point in the history
[Feat] 별자리선 Entity 생성 및 API 구현
  • Loading branch information
mingxoxo authored Nov 30, 2023
2 parents 073163d + 735dc2b commit 8aba6ce
Show file tree
Hide file tree
Showing 9 changed files with 319 additions and 39 deletions.
2 changes: 2 additions & 0 deletions BE/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { UsersRepository } from "./auth/users.repository";
import { typeORMTestConfig } from "./configs/typeorm.test.config";
import { RedisModule } from "@liaoliaots/nestjs-redis";
import { StatModule } from './stat/stat.module';
import { LinesModule } from './lines/lines.module';

@Module({
imports: [
Expand All @@ -29,6 +30,7 @@ import { StatModule } from './stat/stat.module';
IntroduceModule,
ShapesModule,
StatModule,
LinesModule,
],
providers: [ShapesRepository, UsersRepository],
})
Expand Down
42 changes: 3 additions & 39 deletions BE/src/diaries/diaries.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import { ReadDiaryDto, ReadDiaryResponseDto } from "./dto/diaries.read.dto";
import { PrivateDiaryGuard } from "src/auth/guard/auth.diary-guard";
import { GetUser } from "src/auth/get-user.decorator";
import { User } from "src/auth/users.entity";
import { Tag } from "src/tags/tags.entity";
import { JwtAuthGuard } from "src/auth/guard/auth.jwt-guard";
import { getReadResponseDtoFormat } from "src/utils/data-transform";

@Controller("diaries")
@UseGuards(JwtAuthGuard)
Expand All @@ -48,7 +48,7 @@ export class DiariesController {
const readDiaryDto: ReadDiaryDto = { uuid };
const diary = await this.diariesService.readDiary(readDiaryDto);

return this.getReadResponseDtoFormat(diary);
return getReadResponseDtoFormat(diary);
}

@Get()
Expand All @@ -57,7 +57,7 @@ export class DiariesController {
): Promise<ReadDiaryResponseDto[]> {
const diaryList = await this.diariesService.readDiariesByUser(user);
const readDiaryResponseDtoList = diaryList.map((diary) =>
this.getReadResponseDtoFormat(diary),
getReadResponseDtoFormat(diary),
);

return readDiaryResponseDtoList;
Expand All @@ -82,40 +82,4 @@ export class DiariesController {
await this.diariesService.deleteDiary(deleteDiaryDto);
return;
}

getTagNames(tags: Tag[]): string[] {
const tagNames = tags.map((tag) => tag.name);
return tagNames;
}

getCoordinate(point: string): { x: number; y: number; z: number } {
const [x, y, z] = point.split(",");
return {
x: parseFloat(x),
y: parseFloat(y),
z: parseFloat(z),
};
}

getReadResponseDtoFormat(diary: Diary): ReadDiaryResponseDto {
const tagNames = this.getTagNames(diary.tags);
const coordinate = this.getCoordinate(diary.point);

return {
coordinate,
uuid: diary.uuid,
userId: diary.user.userId,
title: diary.title,
content: diary.content,
date: diary.date,
tags: tagNames,
emotion: {
positive: diary.positiveRatio,
neutral: diary.neutralRatio,
negative: diary.negativeRatio,
sentiment: diary.sentiment,
},
shapeUuid: diary.shape.uuid,
};
}
}
17 changes: 17 additions & 0 deletions BE/src/lines/dto/lines.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IsNotEmpty, IsUUID } from "class-validator";

export class CreateLineDto {
@IsUUID("4", { message: "별자리선 uuid1 값이 uuid 양식이어야 합니다." })
@IsNotEmpty({ message: "별자리선 uuid1는 비어있지 않아야 합니다." })
uuid1: string;

@IsUUID("4", { message: "별자리선 uuid2 값이 uuid 양식이어야 합니다." })
@IsNotEmpty({ message: "별자리선 uuid2는 비어있지 않아야 합니다." })
uuid2: string;
}

export class ReadLineDto {
id: number;
first: { uuid: string; coordinate: { x: number; y: number; z: number } };
second: { uuid: string; coordinate: { x: number; y: number; z: number } };
}
55 changes: 55 additions & 0 deletions BE/src/lines/lines.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
Body,
Controller,
Delete,
Get,
HttpCode,
Param,
ParseIntPipe,
Post,
UseGuards,
} from "@nestjs/common";
import { JwtAuthGuard } from "src/auth/guard/auth.jwt-guard";
import { LinesService } from "./lines.service";
import { CreateLineDto, ReadLineDto } from "./dto/lines.dto";
import { GetUser } from "src/auth/get-user.decorator";
import { User } from "src/auth/users.entity";
import { Line } from "./lines.entity";
import { getReadLineDtoFormat } from "src/utils/data-transform";

@Controller("lines")
@UseGuards(JwtAuthGuard)
export class LinesController {
constructor(private linesService: LinesService) {}

@Post()
@HttpCode(201)
async createLine(
@Body() createLineDto: CreateLineDto,
@GetUser() user: User,
): Promise<{ id: number }> {
const line: Line = await this.linesService.createLine(createLineDto, user);
return { id: line.id };
}

@Get()
async getLinesByUser(@GetUser() user: User): Promise<ReadLineDto[]> {
const lines: Line[] = await this.linesService.getLinesByUser(user);

const readLineDtoList: ReadLineDto[] = lines.map((line) =>
getReadLineDtoFormat(line),
);

return readLineDtoList;
}

@Delete("/:id")
@HttpCode(204)
async deleteLine(
@Param("id", ParseIntPipe) id: number,
@GetUser() user: User,
): Promise<void> {
await this.linesService.deleteLine(id, user);
return;
}
}
41 changes: 41 additions & 0 deletions BE/src/lines/lines.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { User } from "src/auth/users.entity";
import { Diary } from "src/diaries/diaries.entity";
import {
BaseEntity,
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
ManyToOne,
PrimaryGeneratedColumn,
} from "typeorm";

@Entity({ name: "constellation" })
export class Line extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;

@ManyToOne(() => Diary, (diary) => diary.id, {
nullable: false,
eager: true,
})
firstDiary: Diary;

@ManyToOne(() => Diary, (diary) => diary.id, {
nullable: false,
eager: true,
})
secondDiary: Diary;

@ManyToOne(() => User, (user) => user.id, {
nullable: false,
eager: true,
})
user: User;

@CreateDateColumn({ type: "datetime" })
createdDate: Date;

@DeleteDateColumn({ type: "datetime" })
deletedDate: Date;
}
15 changes: 15 additions & 0 deletions BE/src/lines/lines.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Module } from "@nestjs/common";
import { LinesController } from "./lines.controller";
import { LinesService } from "./lines.service";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Line } from "./lines.entity";
import { DiariesModule } from "src/diaries/diaries.module";
import { DiariesRepository } from "src/diaries/diaries.repository";
import { LinesRepository } from "./lines.repository";

@Module({
imports: [TypeOrmModule.forFeature([Line]), DiariesModule],
controllers: [LinesController],
providers: [LinesService, LinesRepository, DiariesRepository],
})
export class LinesModule {}
42 changes: 42 additions & 0 deletions BE/src/lines/lines.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Diary } from "src/diaries/diaries.entity";
import { Line } from "./lines.entity";
import { User } from "src/auth/users.entity";
import { NotFoundException } from "@nestjs/common";

export class LinesRepository {
async createLine(
firstDiary: Diary,
secondDiary: Diary,
user: User,
): Promise<Line> {
const newLine = Line.create({
firstDiary,
secondDiary,
user,
});
await newLine.save();

return newLine;
}

async fetchLinesByUser(user: User): Promise<Line[]> {
const linesList = await Line.find({
where: { user: { id: user.id } },
});
return linesList;
}

async deleteLine(id: number): Promise<void> {
const line = await this.getLineById(id);
await Line.softRemove(line);
}

async getLineById(id: number): Promise<Line> {
const found = await Line.findOne({ where: { id } });
if (!found) {
throw new NotFoundException("존재하지 않는 별자리선입니다.");
}

return found;
}
}
83 changes: 83 additions & 0 deletions BE/src/lines/lines.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
BadRequestException,
Injectable,
NotFoundException,
} from "@nestjs/common";
import { CreateLineDto } from "./dto/lines.dto";
import { User } from "src/auth/users.entity";
import { Line } from "./lines.entity";
import { LinesRepository } from "./lines.repository";
import { DiariesRepository } from "src/diaries/diaries.repository";
import { Diary } from "src/diaries/diaries.entity";

@Injectable()
export class LinesService {
constructor(
private linesRepository: LinesRepository,
private diariesRepository: DiariesRepository,
) {}

async createLine(createLineDto: CreateLineDto, user: User): Promise<Line> {
const { uuid1, uuid2 } = createLineDto;

if (uuid1 === uuid2) {
throw new BadRequestException(
"동일한 일기에 별자리선을 생성할 수 없습니다.",
);
}

const firstDiary = await this.diariesRepository.getDiaryByUuid(uuid1);
const secondDiary = await this.diariesRepository.getDiaryByUuid(uuid2);

const isDuplicate = await this.isDuplicateLine(
firstDiary,
secondDiary,
user,
);
if (isDuplicate) {
throw new BadRequestException("이미 존재하는 별자리선입니다.");
}

if (firstDiary.user.id !== user.id || secondDiary.user.id !== user.id) {
throw new NotFoundException("존재하지 않는 일기입니다.");
}

return this.linesRepository.createLine(firstDiary, secondDiary, user);
}

async getLinesByUser(user: User): Promise<Line[]> {
return this.linesRepository.fetchLinesByUser(user);
}

async deleteLine(id: number, user: User): Promise<void> {
const line = await this.linesRepository.getLineById(id);
if (line.user.id !== user.id) {
throw new NotFoundException("존재하지 않는 별자리선입니다.");
}

await this.linesRepository.deleteLine(id);
}

private async isDuplicateLine(
firstDiary: Diary,
secondDiary: Diary,
user: User,
): Promise<boolean> {
const existingLine = await Line.findOne({
where: [
{
firstDiary: { id: firstDiary.id },
secondDiary: { id: secondDiary.id },
user: { id: user.id },
},
{
firstDiary: { id: secondDiary.id },
secondDiary: { id: firstDiary.id },
user: { id: user.id },
},
],
});

return !!existingLine;
}
}
Loading

0 comments on commit 8aba6ce

Please sign in to comment.