Bläddra i källkod

feat: 喂养计划

IlhamTahir 11 månader sedan
förälder
incheckning
32f8aca49e

+ 22 - 0
src/pet-feeder/controller/feeding-plan.controller.ts

@@ -0,0 +1,22 @@
+import { Body, Controller, Post } from '@nestjs/common';
+import { CreateFeedingPlanRequest } from '@/pet-feeder/dto/create-feeding-plan.request';
+import { FeedingPlanService } from '@/pet-feeder/service/feeding-plan.service';
+import { FeedingPlanVo } from '@/pet-feeder/vo/feeding-plan.vo';
+import { FeedingPlanMapper } from '@/pet-feeder/mapper/feeding-plan.mapper';
+
+@Controller('feeding-plans')
+export class FeedingPlanController {
+  constructor(
+    private readonly feedingPlanService: FeedingPlanService,
+    private readonly feedingPlanMapper: FeedingPlanMapper,
+  ) {}
+
+  @Post()
+  async create(
+    @Body() createFeedingPlanRequest: CreateFeedingPlanRequest,
+  ): Promise<FeedingPlanVo> {
+    return this.feedingPlanMapper.toVo(
+      await this.feedingPlanService.create(createFeedingPlanRequest),
+    );
+  }
+}

+ 5 - 1
src/pet-feeder/controller/product.controller.ts

@@ -37,7 +37,11 @@ export class ProductController {
       await this.productService.create(createProductRequest),
     );
   }
-
+  @Get('list')
+  @ApiOkResponse({ type: Array<ProductVo> })
+  async list(): Promise<ProductVo[]> {
+    return this.productMapper.toVos(await this.productService.list());
+  }
   @Put(':id')
   @ApiOkResponse({
     type: ProductVo,

+ 29 - 0
src/pet-feeder/dto/create-feeding-plan.request.ts

@@ -0,0 +1,29 @@
+import { FeedingGoal } from '@/pet-feeder/enum/feeding-goal';
+import { IsNotEmpty, IsNumber } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class CreateFeedingPlanRequest {
+  @IsNotEmpty({
+    message: '目标体重不能为空',
+  })
+  @ApiProperty()
+  targetWeight: number;
+
+  @IsNotEmpty({
+    message: '喂养目标不能为空',
+  })
+  @ApiProperty()
+  feedingGoal: FeedingGoal;
+
+  @IsNotEmpty({
+    message: '宠物 ID 不能为空',
+  })
+  @ApiProperty()
+  petId: string;
+
+  @IsNotEmpty({
+    message: '产品 ID 不能为空',
+  })
+  @ApiProperty()
+  products: { id: string; dailyUsageWeight: number }[];
+}

+ 22 - 0
src/pet-feeder/mapper/feeding-plan-product.mapper.ts

@@ -0,0 +1,22 @@
+import { BaseMapper } from '@/core/mapper/base.mapper';
+import { FeedingPlanProduct } from '@/pet-feeder/entity/feeding-plan-product.entity';
+import { FeedingPlanProductVo } from '@/pet-feeder/vo/feeding-plan-product.vo';
+import { ProductMapper } from '@/pet-feeder/mapper/product.mapper';
+import { Injectable } from '@nestjs/common';
+
+@Injectable()
+export class FeedingPlanProductMapper extends BaseMapper<
+  FeedingPlanProduct,
+  FeedingPlanProductVo
+> {
+  constructor(private productMapper: ProductMapper) {
+    super();
+  }
+  toVo(entity: FeedingPlanProduct): FeedingPlanProductVo {
+    return {
+      ...super.toVo(entity),
+      dailyUsageWeight: entity.dailyUsageWeight,
+      product: this.productMapper.toVo(entity.product),
+    };
+  }
+}

+ 24 - 0
src/pet-feeder/mapper/feeding-plan.mapper.ts

@@ -0,0 +1,24 @@
+import { BaseMapper } from '@/core/mapper/base.mapper';
+import { FeedingPlan } from '@/pet-feeder/entity/feeding-plan.entity';
+import { FeedingPlanVo } from '@/pet-feeder/vo/feeding-plan.vo';
+import { Injectable } from '@nestjs/common';
+import { PetMapper } from '@/pet-feeder/mapper/pet.mapper';
+import { ProductMapper } from '@/pet-feeder/mapper/product.mapper';
+
+@Injectable()
+export class FeedingPlanMapper extends BaseMapper<FeedingPlan, FeedingPlanVo> {
+  constructor(
+    private petMapper: PetMapper,
+    private productMapper: ProductMapper,
+  ) {
+    super();
+  }
+  toVo(entity: FeedingPlan): FeedingPlanVo {
+    return {
+      ...super.toVo(entity),
+      targetWeight: entity.targetWeight,
+      feedingGoal: entity.feedingGoal,
+      pet: this.petMapper.toVo(entity.pet),
+    };
+  }
+}

+ 14 - 2
src/pet-feeder/pet-feeder.module.ts

@@ -10,13 +10,25 @@ import { Product } from '@/pet-feeder/entity/product.entity';
 import { ProductService } from '@/pet-feeder/service/product.service';
 import { ProductMapper } from '@/pet-feeder/mapper/product.mapper';
 import { ProductController } from '@/pet-feeder/controller/product.controller';
+import { FeedingPlanService } from '@/pet-feeder/service/feeding-plan.service';
+import { FeedingPlanMapper } from '@/pet-feeder/mapper/feeding-plan.mapper';
+import { FeedingPlanController } from '@/pet-feeder/controller/feeding-plan.controller';
+import { FeedingPlanProductMapper } from '@/pet-feeder/mapper/feeding-plan-product.mapper';
 
 @Module({
   imports: [
     TypeOrmModule.forFeature([Pet, FeedingPlan, FeedingPlanProduct, Product]),
   ],
-  controllers: [PetController, ProductController],
-  providers: [PetService, PetMapper, ProductService, ProductMapper],
+  controllers: [PetController, ProductController, FeedingPlanController],
+  providers: [
+    PetService,
+    PetMapper,
+    ProductService,
+    ProductMapper,
+    FeedingPlanService,
+    FeedingPlanMapper,
+    FeedingPlanProductMapper,
+  ],
   exports: [],
 })
 export class PetFeederModule {}

+ 48 - 0
src/pet-feeder/service/feeding-plan.service.ts

@@ -0,0 +1,48 @@
+import { Injectable } from '@nestjs/common';
+import { CreateFeedingPlanRequest } from '@/pet-feeder/dto/create-feeding-plan.request';
+import { FeedingPlan } from '@/pet-feeder/entity/feeding-plan.entity';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Repository } from 'typeorm';
+import { FeedingPlanProduct } from '@/pet-feeder/entity/feeding-plan-product.entity';
+import { PetService } from '@/pet-feeder/service/pet.service';
+import { ProductService } from '@/pet-feeder/service/product.service';
+
+@Injectable()
+export class FeedingPlanService {
+  constructor(
+    @InjectRepository(FeedingPlan)
+    private readonly feedingPlanRepository: Repository<FeedingPlan>,
+
+    @InjectRepository(FeedingPlanProduct)
+    private readonly feedingPlanProductRepository: Repository<FeedingPlanProduct>,
+
+    private readonly petService: PetService,
+
+    private readonly productService: ProductService,
+  ) {}
+
+  async create(
+    createFeedingPlanRequest: CreateFeedingPlanRequest,
+  ): Promise<FeedingPlan> {
+    const feedingPlan = this.feedingPlanRepository.create({
+      feedingGoal: createFeedingPlanRequest.feedingGoal,
+      targetWeight: createFeedingPlanRequest.targetWeight,
+    });
+    feedingPlan.pet = await this.petService.get(createFeedingPlanRequest.petId);
+
+    const feedingPlanProducts: FeedingPlanProduct[] = [];
+
+    await Promise.all(
+      createFeedingPlanRequest.products.map(async (product) => {
+        const existProduct = await this.productService.get(product.id);
+        const feedingPlanProduct = this.feedingPlanProductRepository.create({
+          product: existProduct,
+          dailyUsageWeight: product.dailyUsageWeight,
+        });
+        feedingPlanProducts.push(feedingPlanProduct);
+      }),
+    );
+    feedingPlan.feedingPlanProducts = feedingPlanProducts;
+    return this.feedingPlanRepository.save(feedingPlan);
+  }
+}

+ 10 - 0
src/pet-feeder/service/pet.service.ts

@@ -29,6 +29,16 @@ export class PetService {
     return this.petRepository.save(pet);
   }
 
+  async get(id: string): Promise<Pet> {
+    const pet = await this.petRepository.findOneBy({
+      id,
+    });
+    if (!pet) {
+      throw new Error('Pet not found');
+    }
+    return pet;
+  }
+
   async ownPetList(): Promise<Pet[]> {
     const id = RequestContext.currentContext.req.user.id;
     return this.petRepository.findBy({

+ 6 - 0
src/pet-feeder/service/product.service.ts

@@ -57,4 +57,10 @@ export class ProductService {
     const product = await this.get(id);
     await this.productRepository.remove(product);
   }
+
+  async list() {
+    return this.productRepository.find({
+      order: { createdTime: 'DESC' },
+    });
+  }
 }

+ 14 - 0
src/pet-feeder/vo/feeding-plan-product.vo.ts

@@ -0,0 +1,14 @@
+import { BaseVo } from '@/core/vo/base.vo';
+import { ApiProperty, ApiSchema } from '@nestjs/swagger';
+import { ProductVo } from '@/pet-feeder/vo/product.vo';
+
+@ApiSchema({
+  name: 'FeedingPlanProduct',
+})
+export class FeedingPlanProductVo extends BaseVo {
+  @ApiProperty()
+  dailyUsageWeight: number;
+
+  @ApiProperty()
+  product: ProductVo;
+}

+ 23 - 0
src/pet-feeder/vo/feeding-plan.vo.ts

@@ -0,0 +1,23 @@
+import { BaseVo } from '@/core/vo/base.vo';
+import { ApiProperty, ApiSchema } from '@nestjs/swagger';
+import { FeedingGoal } from '@/pet-feeder/enum/feeding-goal';
+import { PetVo } from '@/pet-feeder/vo/pet.vo';
+import { FeedingPlanProduct } from '@/pet-feeder/entity/feeding-plan-product.entity';
+import { FeedingPlanProductVo } from '@/pet-feeder/vo/feeding-plan-product.vo';
+
+@ApiSchema({
+  name: 'FeedingPlan',
+})
+export class FeedingPlanVo extends BaseVo {
+  @ApiProperty()
+  targetWeight: number;
+
+  @ApiProperty()
+  feedingGoal: FeedingGoal;
+
+  @ApiProperty()
+  pet: PetVo;
+
+  @ApiProperty()
+  feedingPlanProducts: FeedingPlanProductVo[];
+}