diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index 14d64fd..f5f422c 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -10,8 +10,9 @@ import { ShapesRepository } from "./shapes/shapes.repository"; 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'; +import { StatModule } from "./stat/stat.module"; +import { LinesModule } from "./lines/lines.module"; +import { PurchaseModule } from "./purchase/purchase.module"; @Module({ imports: [ @@ -31,6 +32,7 @@ import { LinesModule } from './lines/lines.module'; ShapesModule, StatModule, LinesModule, + PurchaseModule, ], providers: [ShapesRepository, UsersRepository], }) diff --git a/BE/src/auth/users.entity.ts b/BE/src/auth/users.entity.ts index a24c755..e81aa53 100644 --- a/BE/src/auth/users.entity.ts +++ b/BE/src/auth/users.entity.ts @@ -12,6 +12,7 @@ import { import { premiumStatus, providerEnum } from "src/utils/enum"; import { Diary } from "../diaries/diaries.entity"; import { Shape } from "src/shapes/shapes.entity"; +import { Purchase } from "src/purchase/purchase.entity"; @Entity() @Unique(["userId"]) @@ -54,4 +55,7 @@ export class User extends BaseEntity { @OneToMany(() => Shape, (shape) => shape.user) shapes: Diary[]; + + @OneToMany(() => Purchase, (purchase) => purchase.user) + purchases: Purchase[]; } diff --git a/BE/src/purchase/dto/purchase.design.dto.ts b/BE/src/purchase/dto/purchase.design.dto.ts new file mode 100644 index 0000000..3fa9909 --- /dev/null +++ b/BE/src/purchase/dto/purchase.design.dto.ts @@ -0,0 +1,9 @@ +import { IsNotEmpty } from "class-validator"; + +export class PurchaseDesignDto { + @IsNotEmpty({ message: "구매 항목 종류는 비어있지 않아야 합니다." }) + domain: string; + + @IsNotEmpty({ message: "디자인은 비어있지 않아야 합니다." }) + design: string; +} diff --git a/BE/src/purchase/purchase.controller.ts b/BE/src/purchase/purchase.controller.ts new file mode 100644 index 0000000..ddeaab7 --- /dev/null +++ b/BE/src/purchase/purchase.controller.ts @@ -0,0 +1,21 @@ +import { Controller, Post, UseGuards, Body, HttpCode } from "@nestjs/common"; +import { JwtAuthGuard } from "src/auth/guard/auth.jwt-guard"; +import { PurchaseService } from "./purchase.service"; +import { PurchaseDesignDto } from "./dto/purchase.design.dto"; +import { GetUser } from "src/auth/get-user.decorator"; +import { User } from "src/auth/users.entity"; + +@Controller("purchase") +@UseGuards(JwtAuthGuard) +export class PurchaseController { + constructor(private purchaseService: PurchaseService) {} + + @Post("/design") + @HttpCode(204) + async purchaseDesign( + @GetUser() user: User, + @Body() purchaseDesignDto: PurchaseDesignDto, + ): Promise { + await this.purchaseService.purchaseDesign(user, purchaseDesignDto); + } +} diff --git a/BE/src/purchase/purchase.entity.ts b/BE/src/purchase/purchase.entity.ts new file mode 100644 index 0000000..84602e2 --- /dev/null +++ b/BE/src/purchase/purchase.entity.ts @@ -0,0 +1,33 @@ +import { User } from "src/auth/users.entity"; +import { designEnum, domainEnum } from "src/utils/enum"; +import { + Entity, + PrimaryGeneratedColumn, + BaseEntity, + Column, + ManyToOne, +} from "typeorm"; + +@Entity() +export class Purchase extends BaseEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ + type: "enum", + enum: domainEnum, + }) + domain: domainEnum; + + @Column({ + type: "enum", + enum: designEnum, + }) + design: designEnum; + + @ManyToOne(() => User, (user) => user.userId, { + nullable: false, + eager: true, + }) + user: User; +} diff --git a/BE/src/purchase/purchase.module.ts b/BE/src/purchase/purchase.module.ts new file mode 100644 index 0000000..2b088cc --- /dev/null +++ b/BE/src/purchase/purchase.module.ts @@ -0,0 +1,14 @@ +import { Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { Purchase } from "./purchase.entity"; +import { PurchaseController } from "./purchase.controller"; +import { PurchaseService } from "./purchase.service"; +import { PurchaseRepository } from "./purchase.repository"; + +@Module({ + imports: [TypeOrmModule.forFeature([Purchase])], + controllers: [PurchaseController], + providers: [PurchaseService, PurchaseRepository], + exports: [PurchaseRepository], +}) +export class PurchaseModule {} diff --git a/BE/src/purchase/purchase.repository.ts b/BE/src/purchase/purchase.repository.ts new file mode 100644 index 0000000..ef6aae4 --- /dev/null +++ b/BE/src/purchase/purchase.repository.ts @@ -0,0 +1,20 @@ +import { Purchase } from "./purchase.entity"; +import { PurchaseDesignDto } from "./dto/purchase.design.dto"; +import { User } from "src/auth/users.entity"; +import { designEnum, domainEnum } from "src/utils/enum"; + +export class PurchaseRepository { + async purchaseDesign( + user: User, + domain: domainEnum, + design: designEnum, + ): Promise { + const purchase = await Purchase.create(); + + purchase.domain = domain; + purchase.design = design; + purchase.user = user; + + purchase.save(); + } +} diff --git a/BE/src/purchase/purchase.service.ts b/BE/src/purchase/purchase.service.ts new file mode 100644 index 0000000..983b5ed --- /dev/null +++ b/BE/src/purchase/purchase.service.ts @@ -0,0 +1,42 @@ +import { Injectable, BadRequestException } from "@nestjs/common"; +import { PurchaseDesignDto } from "./dto/purchase.design.dto"; +import { User } from "src/auth/users.entity"; +import { PurchaseRepository } from "./purchase.repository"; +import { Purchase } from "./purchase.entity"; +import { designEnum, domainEnum } from "src/utils/enum"; + +@Injectable() +export class PurchaseService { + constructor(private purchaseRepository: PurchaseRepository) {} + + async purchaseDesign( + user: User, + purchaseDesignDto: PurchaseDesignDto, + ): Promise { + const domain = domainEnum[purchaseDesignDto.domain]; + const design = designEnum[purchaseDesignDto.design]; + + if (user.credit < 500) { + throw new BadRequestException( + `보유한 별가루가 부족합니다. 현재 ${user.credit} 별가루`, + ); + } + + if ( + Purchase.findOne({ + where: { + user: { userId: user.userId }, + domain: domain, + design: design, + }, + }) + ) { + throw new BadRequestException(`이미 구매한 디자인입니다.`); + } + + user.credit -= 500; + user.save(); + + await this.purchaseRepository.purchaseDesign(user, domain, design); + } +} diff --git a/BE/src/utils/enum.ts b/BE/src/utils/enum.ts index a9db9d3..c9b49ab 100644 --- a/BE/src/utils/enum.ts +++ b/BE/src/utils/enum.ts @@ -15,3 +15,13 @@ export enum providerEnum { NAVER = "naver", KAKAO = "kakao", } + +export enum domainEnum { + GROUND = "ground", + SKY = "sky", +} + +export enum designEnum { + GROUND_GREEN = "#254117", + GROUND_MOCHA = "#493D26", +}