diff --git a/src/contents/contents.service.ts b/src/contents/contents.service.ts index 1178d2f..e135773 100644 --- a/src/contents/contents.service.ts +++ b/src/contents/contents.service.ts @@ -470,6 +470,19 @@ export class CategoryService { } currentParentId = parentCategory?.parentId; } + } else { + /** + * TODO: 유료 플랜 사용자이면 카테고리 개수 제한 없도록 추가 구성해야함. + */ + // if parentId is null, it means that category is root category + // root categories can't be more than 10 in one user + const isOverCategoryLimit = + await this.categoryRepository.isOverCategoryLimit(user); + if (isOverCategoryLimit) { + throw new ConflictException( + "Root categories can't be more than 10 in one user", + ); + } } // check if category exists in user's categories(check if category name is duplicated in same level too) diff --git a/src/contents/repository/category.repository.ts b/src/contents/repository/category.repository.ts index 5259c30..2f956a8 100644 --- a/src/contents/repository/category.repository.ts +++ b/src/contents/repository/category.repository.ts @@ -85,4 +85,19 @@ export class CategoryRepository extends Repository { return category; } + + /** + * 대 카테고리는 유저 당 10개까지만 생성 가능 + * 해당 유저의 대 카테고리 개수를 확인하고, 10개 이상이면 true 반환 + * @param user.id + * @returns boolean + */ + async isOverCategoryLimit(user: User): Promise { + const categoryCount = await this.createQueryBuilder('category') + .where('category.userId = :id', { id: user.id }) + .andWhere('category.parentId IS NULL') + .getCount(); + + return categoryCount >= 10; + } } diff --git a/src/users/entities/paid-plan.entity.ts b/src/users/entities/paid-plan.entity.ts new file mode 100644 index 0000000..edef111 --- /dev/null +++ b/src/users/entities/paid-plan.entity.ts @@ -0,0 +1,37 @@ +import { Column, Entity, OneToMany } from 'typeorm'; +import { IsNumber, IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { CoreEntity } from '../../common/entities/core.entity'; +import { User } from './user.entity'; + +@Entity() +export class PaidPlan extends CoreEntity { + @ApiProperty({ example: 'ultimate', description: 'plan name' }) + @Column({ unique: true }) + @IsString() + name!: string; + + @ApiProperty({ example: 3000, description: 'plan price' }) + @Column() + @IsNumber() + price!: number; + + @ApiProperty({ + example: 30, + description: 'The period (in days) of the paid plan.', + }) + @Column() + @IsString() + duration_days!: number; + + @ApiProperty({ example: 'ultimate plan', description: 'plan description' }) + @Column() + @IsString() + description!: string; + + @ApiProperty({ description: 'Users in use', type: [User] }) + @OneToMany((type) => User, (user) => user.paidPlan, { + nullable: true, + }) + users?: User[]; +} diff --git a/src/users/entities/user.entity.ts b/src/users/entities/user.entity.ts index 687e966..20d3847 100644 --- a/src/users/entities/user.entity.ts +++ b/src/users/entities/user.entity.ts @@ -1,5 +1,12 @@ import { InternalServerErrorException } from '@nestjs/common'; -import { BeforeInsert, BeforeUpdate, Column, Entity, OneToMany } from 'typeorm'; +import { + BeforeInsert, + BeforeUpdate, + Column, + Entity, + ManyToOne, + OneToMany, +} from 'typeorm'; import * as bcrypt from 'bcrypt'; import { IsBoolean, IsEmail, IsEnum, IsString, Matches } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; @@ -7,6 +14,7 @@ import { Content } from '../../contents/entities/content.entity'; import { Category } from '../../contents/entities/category.entity'; import { Collection } from '../../collections/entities/collection.entity'; import { CoreEntity } from '../../common/entities/core.entity'; +import { PaidPlan } from './paid-plan.entity'; export enum UserRole { Client = 'Client', @@ -77,6 +85,16 @@ export class User extends CoreEntity { }) collections?: Collection[]; + @ApiProperty({ + description: 'User Plan', + type: PaidPlan, + required: false, + }) + @ManyToOne((type) => PaidPlan, (paidPlan) => paidPlan.users, { + nullable: true, + }) + paidPlan?: PaidPlan; + @BeforeInsert() @BeforeUpdate() async hashPassword(): Promise {