diff --git a/package.json b/package.json index 8b93d40..d6f6814 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "aws-sdk": "^2.1623.0", "axios": "^1.7.2", "bcrypt": "^5.1.1", + "builder-pattern": "^2.2.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "ioredis": "^5.4.1", diff --git a/src/auth/decorator/auth.decorator.ts b/src/auth/decorator/auth.decorator.ts index b0c8947..8a5bc97 100644 --- a/src/auth/decorator/auth.decorator.ts +++ b/src/auth/decorator/auth.decorator.ts @@ -5,19 +5,19 @@ import { ForbiddenException, HttpCode, HttpStatus, - UseGuards -} from "@nestjs/common"; -import { Customer } from "../../schemas/customer.entity"; -import { ApiBearerAuth, ApiUnauthorizedResponse } from "@nestjs/swagger"; -import { AuthGuard } from "@nestjs/passport"; -import { Business } from "../../schemas/business.entity"; -import { Driver } from "../../schemas/drivers.entity"; + UseGuards, +} from '@nestjs/common'; +import { Customer } from '../../schemas/customer.entity'; +import { ApiBearerAuth, ApiUnauthorizedResponse } from '@nestjs/swagger'; +import { AuthGuard } from '@nestjs/passport'; +import { Business } from '../../schemas/business.entity'; +import { Driver } from '../../schemas/drivers.entity'; export const CurrentCustomer = createParamDecorator( (data: unknown, context: ExecutionContext) => { const req: { user?: Customer } = context.switchToHttp().getRequest(); - if (!req.user.customerId) { + if (!req.user?.customerId) { throw new ForbiddenException('해당 계정은 고객 계정이 아닙니다.'); } return req.user; @@ -28,7 +28,7 @@ export const CurrentBusiness = createParamDecorator( (data: unknown, context: ExecutionContext) => { const req: { user?: Business } = context.switchToHttp().getRequest(); - if (!req.user.businessId) { + if (!req.user?.businessId) { throw new ForbiddenException('해당 계정은 업체 계정이 아닙니다.'); } return req.user; @@ -39,7 +39,7 @@ export const CurrentDriver = createParamDecorator( (data: unknown, context: ExecutionContext) => { const req: { user?: Driver } = context.switchToHttp().getRequest(); - if (!req.user.driverId) { + if (!req.user?.driverId) { throw new ForbiddenException('해당 계정은 기사 계정이 아닙니다.'); } return req.user; diff --git a/src/pet/application/pet.checklist.service.spec.ts b/src/pet/application/pet.checklist.service.spec.ts new file mode 100644 index 0000000..80b20bc --- /dev/null +++ b/src/pet/application/pet.checklist.service.spec.ts @@ -0,0 +1,24 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { PetService } from './pet.service'; + +describe('PetChecklistService', () => { + let service: PetService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [PetService], + }).compile(); + + service = module.get(PetService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + it('should find checklist', () => { + service.findCheckList(null, null).then((checklist) => { + expect(checklist).toBeDefined(); + }); + }); +}); diff --git a/src/pet/application/pet.service.ts b/src/pet/application/pet.service.ts index a57d32a..870cd62 100644 --- a/src/pet/application/pet.service.ts +++ b/src/pet/application/pet.service.ts @@ -1,17 +1,43 @@ -import { ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Repository } from "typeorm"; -import { Pet } from "../../schemas/pets.entity"; -import { Breed } from "../../schemas/breed.entity"; -import { Customer } from "../../schemas/customer.entity"; -import { PetDto } from "../presentation/pet.dto"; +import { + ForbiddenException, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; +import { Pet } from '../../schemas/pets.entity'; +import { Breed } from '../../schemas/breed.entity'; +import { Customer } from '../../schemas/customer.entity'; +import { + PetChecklistAnswerDto, + PetChecklistChoiceDto, + PetChecklistDto, + PetDto, +} from '../presentation/pet.dto'; +import { + ChecklistType, + PetChecklist, + PetChecklistCategory, +} from '../../schemas/pet-checklist.entity'; +import { PetChecklistChoice } from '../../schemas/pet-checklist-chocie.entity'; +import { PetChecklistAnswer } from '../../schemas/pet-checklist-answer.entity'; +import { PetChecklistChoiceAnswer } from '../../schemas/pet-checklist-chocie-answer.entity'; +import { Builder } from 'builder-pattern'; +import { CustomerService } from '../../customer/application/customer.service'; +import { BadRequestException } from '@nestjs/common/exceptions'; @Injectable() export class PetService { constructor( + private customerService: CustomerService, @InjectRepository(Pet) private petRepository: Repository, - + @InjectRepository(PetChecklist) + private petChecklistRepository: Repository, + @InjectRepository(PetChecklistAnswer) + private petChecklistAnswerRepository: Repository, + @InjectRepository(PetChecklistChoiceAnswer) + private petChecklistChoiceAnswerRepository: Repository, @InjectRepository(Breed) private breedRepository: Repository, ) {} @@ -34,6 +60,12 @@ export class PetService { return await this.petRepository.save(newPet); } + async findAll(customer: Customer): Promise { + return await this.petRepository.find({ + where: { customer: { customerId: customer.customerId } }, + relations: ['breed'], + }); + } async findOne(id: number, customer: Customer): Promise { const pet = await this.petRepository.findOneOrFail({ where: { petId: id }, @@ -55,20 +87,18 @@ export class PetService { const pet = await this.findOne(id, customer); if (dto.breedId && dto.breedId !== pet.breed.breedId) { - const breed = await this.breedRepository.findOneOrFail({ + pet.breed = await this.breedRepository.findOneOrFail({ where: { breedId: dto.breedId }, }); - - pet.breed = breed; } - pet.name = dto.name ?? pet.name; - pet.birthdate = dto.birthdate ?? pet.birthdate; - pet.weight = dto.weight ?? pet.weight; + pet.petName = dto.petName ?? pet.petName; + pet.petBirthdate = dto.petBirthdate ?? pet.petBirthdate; + pet.petWeight = dto.petWeight ?? pet.petWeight; pet.neuteredYn = dto.neuteredYn ?? pet.neuteredYn; pet.personality = dto.personality ?? pet.personality; pet.vaccinationStatus = dto.vaccinationStatus ?? pet.vaccinationStatus; - pet.gender = dto.gender ?? pet.gender; + pet.petGender = dto.petGender ?? pet.petGender; return await this.petRepository.save(pet); } @@ -78,4 +108,129 @@ export class PetService { await this.petRepository.softDelete(pet.petId); } + + async findCheckList( + category: PetChecklistCategory, + type: ChecklistType, + petId: number, + customer: Customer, + ): Promise { + let query = this.petChecklistRepository + .createQueryBuilder('PC') + .leftJoinAndMapMany( + 'PC.petChecklistChoices', + PetChecklistChoice, + 'PCC', + 'PC.pet_checklist_id = PCC.pet_checklist_id', + ); + + if (petId) { + await this.findOne(petId, customer); + + query = query + .leftJoinAndMapOne( + 'PCC.petChecklistChoiceAnswers', + PetChecklistChoiceAnswer, + 'PCCA', + `PCC.pet_checklist_id = PCCA.pet_checklist_id + AND PCC.pet_checklist_choice_id = PCCA.pet_checklist_choice_id + AND PCCA.pet_id = :petId`, + { petId }, + ) + .leftJoinAndMapMany( + 'PC.petChecklistAnswers', + PetChecklistAnswer, + 'PCA', + 'PC.pet_checklist_id = PCA.pet_checklist_id AND PCA.pet_id = :petId', + { petId }, + ); + } + + type && query.andWhere('PC.pet_checklist_type = :type', { type }); + category && + query.andWhere('PC.pet_checklist_category = :category', { category }); + + query.orderBy('PC.pet_checklist_id'); + + const data = await query.getMany(); + return data.map((checklist) => { + const dto = Builder() + .petChecklistId(checklist.petChecklistId) + .petChecklistType(checklist.petChecklistType) + .petChecklistCategory(checklist.petChecklistCategory) + .petChecklistContent(checklist.petChecklistContent) + .petChecklistChoices( + checklist?.petChecklistChoices?.map((choice) => { + return Builder() + .petChecklistChoiceId(choice.petChecklistChoiceId) + .petChecklistChoiceContent(choice.petChecklistChoiceContent) + .checked(!!choice?.petChecklistChoiceAnswers) + .build(); + }), + ) + .petChecklistAnswer( + checklist.petChecklistAnswers + ? checklist?.petChecklistAnswers[0]?.petChecklistAnswer + : null, + ) + .build(); + + if (dto.petChecklistType === ChecklistType.ANSWER) { + dto.petChecklistChoices = null; + } else { + dto.petChecklistAnswer = null; + } + + return dto; + }); + } + + async answerChecklist( + petId: number, + dto: PetChecklistAnswerDto[], + customer: Customer, + ) { + const pet = await this.findOne(petId, customer); + + const checklist = await this.petChecklistRepository.find({ + where: { + petChecklistId: In(dto.map((v) => v.petChecklistId)), + }, + }); + + for (const v of checklist) { + const answer = dto.find((d) => d.petChecklistId === v.petChecklistId); + + if (v.petChecklistType === ChecklistType.ANSWER) { + if (!answer.petChecklistAnswer) { + throw new BadRequestException('답변을 적어주세요'); + } + + await this.petChecklistAnswerRepository.save({ + pet, + petChecklistId: v.petChecklistId, + petChecklistAnswer: answer.petChecklistAnswer, + }); + } else { + if (!answer.petChecklistChoiceId || answer.checked == undefined) { + throw new BadRequestException('선택지를 선택해주세요'); + } + + if (answer.checked) { + await this.petChecklistChoiceAnswerRepository.save({ + petId, + petChecklistId: v.petChecklistId, + petChecklistChoiceId: answer.petChecklistChoiceId, + }); + return; + } + + await this.petChecklistChoiceAnswerRepository.delete({ + petId, + petChecklistId: v.petChecklistId, + petChecklistChoiceId: answer.petChecklistChoiceId, + }); + } + } + } } diff --git a/src/pet/pet.module.ts b/src/pet/pet.module.ts index f54d220..869dd1c 100644 --- a/src/pet/pet.module.ts +++ b/src/pet/pet.module.ts @@ -4,9 +4,22 @@ import { PetService } from './application/pet.service'; import { PetController } from './presentation/pet.controller'; import { Pet } from 'src/schemas/pets.entity'; import { Breed } from 'src/schemas/breed.entity'; +import { PetChecklist } from '../schemas/pet-checklist.entity'; +import { CustomerModule } from '../customer/customer.module'; +import { PetChecklistChoiceAnswer } from '../schemas/pet-checklist-chocie-answer.entity'; +import { PetChecklistAnswer } from '../schemas/pet-checklist-answer.entity'; @Module({ - imports: [TypeOrmModule.forFeature([Pet, Breed])], + imports: [ + TypeOrmModule.forFeature([ + Pet, + Breed, + PetChecklist, + PetChecklistAnswer, + PetChecklistChoiceAnswer, + ]), + CustomerModule, + ], controllers: [PetController], providers: [PetService], exports: [PetService], diff --git a/src/pet/presentation/pet.controller.ts b/src/pet/presentation/pet.controller.ts index c3c2293..a492cf4 100644 --- a/src/pet/presentation/pet.controller.ts +++ b/src/pet/presentation/pet.controller.ts @@ -1,25 +1,82 @@ -import { PetService } from "../application/pet.service"; -import { Body, Controller, Delete, Get, Param, Post, Put } from "@nestjs/common"; -import { PetDto } from "./pet.dto"; -import { Pet } from "../../schemas/pets.entity"; -import { ApiOkResponse, ApiOperation, ApiTags } from "@nestjs/swagger"; -import { GroupValidation } from "src/common/validation/validation.decorator"; -import { CrudGroup } from "src/common/validation/validation.data"; -import { Customer } from "src/schemas/customer.entity"; -import { CurrentCustomer } from "src/auth/decorator/auth.decorator"; +import { PetService } from '../application/pet.service'; +import { + Body, + Controller, + Delete, + Get, + Param, + Post, + Put, + Query, +} from '@nestjs/common'; +import { PetChecklistAnswerDto, PetChecklistDto, PetDto } from './pet.dto'; +import { Pet } from '../../schemas/pets.entity'; +import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { GroupValidation } from 'src/common/validation/validation.decorator'; +import { CrudGroup } from 'src/common/validation/validation.data'; +import { Customer } from 'src/schemas/customer.entity'; +// eslint-disable-next-line prettier/prettier +import { Auth, CurrentCustomer } from "src/auth/decorator/auth.decorator"; +import { + ChecklistType, + PetChecklistCategory, +} from '../../schemas/pet-checklist.entity'; @ApiTags('반려동물 관련 API') @Controller('/v1/pet') export class PetController { constructor(private readonly petService: PetService) {} + @Get('/checklist') + @Auth() + async getChecklist( + @Query('category') category: PetChecklistCategory, + @Query('type') type: ChecklistType, + @CurrentCustomer() customer: Customer, + ): Promise { + return await this.petService.findCheckList(category, type, null, customer); + } + + @Get('/:petId/checklist') + @Auth() + async getPetChecklist( + @Param('petId') petId: number, + @Query('category') category: PetChecklistCategory, + @Query('type') type: ChecklistType, + @CurrentCustomer() customer: Customer, + ): Promise { + return await this.petService.findCheckList(category, type, petId, customer); + } + + @ApiOperation({ + summary: '반려동물 체크리스트 답변', + description: '반려동물 체크리스트 답변을 등록합니다.', + }) + @ApiOkResponse({ + description: '반려동물 체크리스트 답변 성공', + }) + @ApiBody({ type: [PetChecklistAnswerDto] }) + @Post('/:petId/checklist/answer') + @GroupValidation([CrudGroup.create]) + @Auth() + async answerChecklist( + @Param('petId') petId: number, + @Body() dto: PetChecklistAnswerDto[], + @CurrentCustomer() customer: Customer, + ): Promise { + return await this.petService + .answerChecklist(petId, dto, customer) + .then(() => dto); + } + @ApiOperation({ summary: '반려동물 정보 생성', description: '새로운 반려동물 정보를 생성합니다.', }) @ApiOkResponse({ type: PetDto, description: '반려동물 정보 생성 성공' }) - @Post() @GroupValidation([CrudGroup.create]) + @Post() + @Auth() async create( @Body() dto: PetDto, @CurrentCustomer() customer: Customer, @@ -27,6 +84,17 @@ export class PetController { return await this.petService.create(dto, customer); } + @ApiOperation({ + summary: '반려동물 정보 조회', + description: '고객의 모든 반려동물 정보를 조회합니다.', + }) + @Auth() + @ApiOkResponse({ type: PetDto, description: '반려동물 정보 조회 성공' }) + @Get('/my') + async getAll(@CurrentCustomer() customer: Customer): Promise { + return await this.petService.findAll(customer); + } + @ApiOperation({ summary: '반려동물 정보 조회', description: '반려동물 ID를 통해 반려동물 정보를 조회합니다.', diff --git a/src/pet/presentation/pet.dto.ts b/src/pet/presentation/pet.dto.ts index f4edff6..dc1177b 100644 --- a/src/pet/presentation/pet.dto.ts +++ b/src/pet/presentation/pet.dto.ts @@ -1,15 +1,21 @@ import { + IsBoolean, + IsDate, + IsEnum, IsNotEmpty, + IsNumber, IsOptional, - IsEnum, - Length, - IsDate, - IsBoolean, IsUrl, - IsNumber, + Length, + ValidateIf, } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { Gender } from '../../schemas/pets.entity'; +import { + ChecklistType, + PetChecklistCategory, +} from '../../schemas/pet-checklist.entity'; +import { CrudGroup } from '../../common/validation/validation.data'; export class PetDto { @ApiProperty({ @@ -24,53 +30,72 @@ export class PetDto { description: '반려동물 이름', required: true, }) - @IsNotEmpty() - @Length(1, 30) - public name: string; + @IsNotEmpty({ + groups: [CrudGroup.create], + }) + @Length(1, 30, { + groups: [CrudGroup.create], + }) + public petName: string; @ApiProperty({ description: '반려동물의 성별', required: true, }) - @IsNotEmpty() - @IsEnum(Gender) - public gender: Gender; + @IsNotEmpty({ + groups: [CrudGroup.create], + }) + @IsEnum(Gender, { + groups: [CrudGroup.create], + }) + public petGender: Gender; @ApiProperty({ description: '반려동물의 생년월일', required: true, }) - @IsNotEmpty() - @IsDate() - public birthdate: Date; + @IsNotEmpty({ + groups: [CrudGroup.create], + }) + @IsDate({ + groups: [CrudGroup.create], + }) + public petBirthdate: Date; @ApiProperty({ description: '반려동물의 체중', required: true, }) - @IsNotEmpty() - public weight: string; + @IsNotEmpty({ + groups: [CrudGroup.create], + }) + public petWeight: number; @ApiProperty({ description: '중성화 여부', required: true, }) - @IsNotEmpty() - @IsBoolean() + @IsNotEmpty({ + groups: [CrudGroup.create], + }) + @IsBoolean({ + groups: [CrudGroup.create], + }) public neuteredYn: boolean; @ApiProperty({ - description: '반려동물의 성격', + description: '반려동물의 특성', required: true, }) - @IsNotEmpty() public personality: string; @ApiProperty({ description: '예방 접종 상태', required: true, }) - @IsNotEmpty() + @IsNotEmpty({ + groups: [CrudGroup.create], + }) public vaccinationStatus: string; @ApiProperty({ @@ -85,7 +110,123 @@ export class PetDto { description: '견종 ID', required: true, }) - @IsNotEmpty() + @IsNotEmpty({ + groups: [CrudGroup.create], + }) @IsNumber() public breedId: number; } + +export class PetChecklistDto { + @ApiProperty({ + description: '반려동물 체크리스트 ID', + required: false, + readOnly: true, + }) + @IsOptional() + @IsNumber() + @IsNotEmpty({ + groups: [CrudGroup.create], + }) + petChecklistId?: number; + + @ApiProperty({ + description: `체크리스트의 타입입니다. type은 총 choice와 answer가 있습니다. + choice는 선택지가 있는 체크리스트이며, answer는 답변이 있는 체크리스트입니다`, + required: false, + readOnly: true, + }) + petChecklistType: ChecklistType; + + @ApiProperty({ + description: '체크리스트 카테고리입니다.', + required: true, + }) + petChecklistCategory: PetChecklistCategory; + + @ApiProperty({ + description: '체크리스트 내용입니다.', + required: false, + }) + petChecklistContent: string; + + @ApiProperty({ + description: '체크리스트 선택지입니다.', + required: false, + }) + petChecklistChoices: PetChecklistChoiceDto[]; + + @ApiProperty({ + description: '체크리스트 답변입니다.', + required: false, + }) + petChecklistAnswer: string; +} + +export class PetChecklistAnswerDto { + @ApiProperty({ + description: '반려동물 체크리스트 ID', + required: true, + }) + @IsNotEmpty() + petChecklistId: number; + + @ApiProperty({ + description: + '반려동물 체크리스트 선택지 ID\n' + + '선택지가 없는 체크리스트의 경우 nullable합니다.', + required: true, + }) + @ValidateIf((o) => o.petChecklistAnswer === null) + @IsNumber() + @IsOptional() + @IsNotEmpty() + petChecklistChoiceId: number; + + @ApiProperty({ + description: + '반려동물 체크리스트 선택유무' + + '선택지가 없는 체크리스트의 경우 nullable합니다.', + required: true, + }) + @ValidateIf((o) => o.petChecklistAnswer === null) + @IsBoolean() + @IsOptional() + @IsNotEmpty() + checked: boolean; + + @ApiProperty({ + description: + '반려동물 체크리스트 답변입니다.\n' + + '선택지가 있는 체크리스트의 경우 nullable합니다.', + required: false, + }) + @IsNotEmpty() + @IsOptional() + @ValidateIf((o) => o.petChecklistChoiceId === null) + petChecklistAnswer: string; +} + +export class PetChecklistChoiceDto { + @ApiProperty({ + description: '체크리스트 선택지 ID', + required: false, + readOnly: true, + }) + @IsOptional() + petChecklistChoiceId?: number; + + @ApiProperty({ + description: '체크리스트 선택지 내용입니다.', + required: true, + }) + @IsNotEmpty() + petChecklistChoiceContent: string; + + @ApiProperty({ + readOnly: true, + required: false, + description: '해당 선택지가 선택되었는지에 대한 유무입니다.', + }) + checked: boolean = false; +} diff --git a/src/schemas/breed.entity.ts b/src/schemas/breed.entity.ts index f736770..c0adf7f 100644 --- a/src/schemas/breed.entity.ts +++ b/src/schemas/breed.entity.ts @@ -1,9 +1,9 @@ import { Entity, PrimaryColumn, Column, OneToMany } from 'typeorm'; import { Pet } from './pets.entity'; -import { HasTsid } from '../common/entity/parent.entity'; +import { HasUuid } from '../common/entity/parent.entity'; @Entity({ name: 'breed' }) -export class Breed extends HasTsid { +export class Breed extends HasUuid { @PrimaryColumn() public breedId: number; diff --git a/src/schemas/customer-chat-room.entity.ts b/src/schemas/customer-chat-room.entity.ts index 8ffd8e6..91ed355 100644 --- a/src/schemas/customer-chat-room.entity.ts +++ b/src/schemas/customer-chat-room.entity.ts @@ -11,10 +11,10 @@ export class CustomerChatRoom { chatRoomId: number; @CreateDateColumn({ type: 'timestamp' }) - created_at: Date; + createdAt: Date; @Column({ type: 'timestamp', nullable: true }) - deleted_at: Date; + deletedAt: Date; @ManyToOne(() => Customer, (customer) => customer.chatRooms) @JoinColumn({ name: 'customer_id' }) diff --git a/src/schemas/pet-checklist-answer.entity.ts b/src/schemas/pet-checklist-answer.entity.ts new file mode 100644 index 0000000..7589031 --- /dev/null +++ b/src/schemas/pet-checklist-answer.entity.ts @@ -0,0 +1,26 @@ +import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm"; +import { PetChecklist } from "./pet-checklist.entity"; +import { Pet } from "./pets.entity"; + +@Entity('pet_checklist_answers') +export class PetChecklistAnswer { + @PrimaryColumn() + petId: number; + + @PrimaryColumn() + petChecklistId: number; + + @ManyToOne(() => Pet, (pet) => pet.petChecklistAnswer) + @JoinColumn({ name: 'pet_id' }) + pet: Pet; + + @ManyToOne( + () => PetChecklist, + (petChecklist) => petChecklist.petChecklistAnswers, + ) + @JoinColumn({ name: 'pet_checklist_id' }) + petChecklist: PetChecklist; + + @Column('text') + petChecklistAnswer: string; +} diff --git a/src/schemas/pet-checklist-chocie-answer.entity.ts b/src/schemas/pet-checklist-chocie-answer.entity.ts new file mode 100644 index 0000000..2bdf191 --- /dev/null +++ b/src/schemas/pet-checklist-chocie-answer.entity.ts @@ -0,0 +1,21 @@ +import { Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm"; +import { PetChecklistChoice } from "./pet-checklist-chocie.entity"; + +@Entity('pet_checklist_choices_answers') +export class PetChecklistChoiceAnswer { + @PrimaryColumn() + petId: number; + + @PrimaryColumn() + petChecklistId: number; + + @PrimaryColumn() + petChecklistChoiceId: number; + + @ManyToOne( + () => PetChecklistChoice, + (petChecklistChoice) => petChecklistChoice.petChecklistChoiceAnswers, + ) + @JoinColumn({ name: 'pet_checklist_choice_id' }) + petChecklistChoice: PetChecklistChoice; +} diff --git a/src/schemas/pet-checklist-chocie.entity.ts b/src/schemas/pet-checklist-chocie.entity.ts new file mode 100644 index 0000000..0175d05 --- /dev/null +++ b/src/schemas/pet-checklist-chocie.entity.ts @@ -0,0 +1,25 @@ +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { PetChecklist } from "./pet-checklist.entity"; +import { PetChecklistChoiceAnswer } from "./pet-checklist-chocie-answer.entity"; + +@Entity('pet_checklist_choices') +export class PetChecklistChoice { + @PrimaryGeneratedColumn() + petChecklistChoiceId: number; + + @ManyToOne( + () => PetChecklist, + (petChecklist) => petChecklist.petChecklistChoices, + ) + @JoinColumn({ name: 'pet_checklist_id' }) + petChecklist: PetChecklist; + + @Column('text') + petChecklistChoiceContent: string; + + @OneToMany( + () => PetChecklistChoiceAnswer, + (petChecklistChoiceAnswer) => petChecklistChoiceAnswer.petChecklistChoice, + ) + petChecklistChoiceAnswers: PetChecklistChoiceAnswer[]; +} diff --git a/src/schemas/pet-checklist.entity.ts b/src/schemas/pet-checklist.entity.ts new file mode 100644 index 0000000..c0d0d70 --- /dev/null +++ b/src/schemas/pet-checklist.entity.ts @@ -0,0 +1,48 @@ +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; +import { PetChecklistChoice } from './pet-checklist-chocie.entity'; +import { PetChecklistAnswer } from './pet-checklist-answer.entity'; + +export enum PetChecklistCategory { + HEALTH = 'health', + FOOD = 'food', + GROOMING = 'grooming', + PERSONALITY = 'personality', + OTHER = 'other', +} + +export enum ChecklistType { + CHOICE = 'choice', + ANSWER = 'answer', +} +@Entity('pet_checklist') +export class PetChecklist { + @PrimaryGeneratedColumn() + petChecklistId: number; + + @Column({ + type: 'enum', + enum: ChecklistType, + }) + petChecklistType: ChecklistType; + + @Column({ + type: 'enum', + enum: PetChecklistCategory, + }) + petChecklistCategory: PetChecklistCategory; + + @Column('text') + petChecklistContent: string; + + @OneToMany( + () => PetChecklistChoice, + (petCheckListChoice) => petCheckListChoice.petChecklist, + ) + petChecklistChoices: PetChecklistChoice[]; + + @OneToMany( + () => PetChecklistAnswer, + (petChecklistAnswer) => petChecklistAnswer.petChecklist, + ) + petChecklistAnswers: PetChecklistAnswer[]; +} diff --git a/src/schemas/pets.entity.ts b/src/schemas/pets.entity.ts index cbf87c7..95790cc 100644 --- a/src/schemas/pets.entity.ts +++ b/src/schemas/pets.entity.ts @@ -7,13 +7,14 @@ import { ManyToOne, OneToMany, PrimaryColumn, - UpdateDateColumn -} from "typeorm"; -import { Appointment } from "./appointments.entity"; -import { Breed } from "./breed.entity"; -import { Customer } from "./customer.entity"; -import { Review } from "./reviews.entity"; -import { HasUuid } from "../common/entity/parent.entity"; + UpdateDateColumn, +} from 'typeorm'; +import { Appointment } from './appointments.entity'; +import { Breed } from './breed.entity'; +import { Customer } from './customer.entity'; +import { Review } from './reviews.entity'; +import { HasUuid } from '../common/entity/parent.entity'; +import { PetChecklistAnswer } from './pet-checklist-answer.entity'; export enum Gender { MALE = 'MALE', @@ -26,20 +27,24 @@ export class Pet extends HasUuid { public petId: number; @Column() - public name: string; + public petName: string; @Column({ type: 'enum', enum: Gender, default: Gender.MALE, }) - public gender: string; + public petGender: string; - @Column({ type: 'date' }) - public birthdate: Date; + @Column({ + type: 'date', + }) + public petBirthdate: Date; - @Column() - public weight: string; + @Column({ + type: 'double precision', + }) + public petWeight: number; @Column() public neuteredYn: boolean; @@ -72,4 +77,11 @@ export class Pet extends HasUuid { @ManyToOne(() => Breed, (breed) => breed.pets) @JoinColumn({ name: 'breed_id' }) public breed: Breed; + + @ManyToOne( + () => PetChecklistAnswer, + (petChecklistAnswer) => petChecklistAnswer.pet, + ) + @JoinColumn({ name: 'pet_id' }) + petChecklistAnswer: PetChecklistAnswer; } diff --git a/yarn.lock b/yarn.lock index a08b88e..6676599 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3254,6 +3254,11 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +builder-pattern@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/builder-pattern/-/builder-pattern-2.2.0.tgz#f1e56f889282486fb5b3852947e91cd09cfe7b79" + integrity sha512-cES3qdeBzA4QyJi7rV/l/kAhIFX6AKo3vK66ZPXLNpjcQWCS8sjLKscly8imlfW2YPTo/hquMRMnaWpZ80Kj+g== + busboy@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"