feat(product): 实现产品需求管理模块(后端 + 前端)

后端:Product Module(CRUD)、Requirement Module(CRUD + 状态机流转)、PrismaService
前端:产品列表页、产品详情页(含需求池 Tab)、Zustand Store、API 封装

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Script Generator
2026-06-08 11:06:16 +08:00
parent 8de1f93fd3
commit e261c3d0b8
27 changed files with 6993 additions and 3 deletions

View File

@@ -0,0 +1,11 @@
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
export class CreateProductDto {
@IsString()
@IsNotEmpty()
name!: string;
@IsString()
@IsOptional()
description?: string;
}

View File

@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateProductDto } from './create-product.dto';
export class UpdateProductDto extends PartialType(CreateProductDto) {}

View File

@@ -0,0 +1,34 @@
import { Controller, Get, Post, Patch, Delete, Param, Body } from '@nestjs/common';
import { ProductService } from './product.service';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
@Controller('products')
export class ProductController {
constructor(private readonly productService: ProductService) {}
@Post()
create(@Body() dto: CreateProductDto) {
return this.productService.create(dto);
}
@Get()
findAll() {
return this.productService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.productService.findOne(id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() dto: UpdateProductDto) {
return this.productService.update(id, dto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.productService.remove(id);
}
}

View File

@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { ProductController } from './product.controller';
import { ProductService } from './product.service';
@Module({
controllers: [ProductController],
providers: [ProductService],
exports: [ProductService],
})
export class ProductModule {}

View File

@@ -0,0 +1,50 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
@Injectable()
export class ProductService {
constructor(private prisma: PrismaService) {}
create(dto: CreateProductDto) {
return this.prisma.product.create({ data: dto });
}
findAll() {
return this.prisma.product.findMany({
orderBy: { createdAt: 'desc' },
include: {
_count: { select: { requirements: true, projects: true } },
},
});
}
async findOne(id: string) {
const product = await this.prisma.product.findUnique({
where: { id },
include: {
requirements: { orderBy: { createdAt: 'desc' } },
versions: { orderBy: { createdAt: 'desc' } },
_count: { select: { projects: true } },
},
});
if (!product) throw new NotFoundException('产品不存在');
return product;
}
async update(id: string, dto: UpdateProductDto) {
await this.ensureExists(id);
return this.prisma.product.update({ where: { id }, data: dto });
}
async remove(id: string) {
await this.ensureExists(id);
return this.prisma.product.delete({ where: { id } });
}
private async ensureExists(id: string) {
const exists = await this.prisma.product.findUnique({ where: { id } });
if (!exists) throw new NotFoundException('产品不存在');
}
}