import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'; import { PrismaService } from '../../prisma/prisma.service'; import { RequirementStatus } from '@ftb/shared'; import { CreateRequirementDto } from './dto/create-requirement.dto'; import { UpdateRequirementDto } from './dto/update-requirement.dto'; const VALID_TRANSITIONS: Record = { [RequirementStatus.DRAFT]: [RequirementStatus.REVIEWING], [RequirementStatus.REVIEWING]: [RequirementStatus.APPROVED, RequirementStatus.REJECTED], [RequirementStatus.APPROVED]: [RequirementStatus.DELIVERED], [RequirementStatus.REJECTED]: [RequirementStatus.DRAFT], [RequirementStatus.DELIVERED]: [], }; function createFallbackRequirementCode() { return `REQ-${Date.now().toString(36).toUpperCase()}-${Math.random() .toString(36) .slice(2, 6) .toUpperCase()}`; } @Injectable() export class RequirementService { constructor(private prisma: PrismaService) {} create(productId: string, dto: CreateRequirementDto) { return this.prisma.requirement.create({ data: { productId, code: dto.code?.trim() || createFallbackRequirementCode(), title: dto.title, description: dto.description || '', priority: dto.priority ?? 0, creatorId: dto.creatorId, }, }); } findAll(productId: string, status?: string) { return this.prisma.requirement.findMany({ where: { productId, ...(status ? { status } : {}), }, orderBy: { createdAt: 'desc' }, include: { creator: { select: { id: true, name: true } } }, }); } async findOne(productId: string, id: string) { const req = await this.prisma.requirement.findFirst({ where: { id, productId }, include: { creator: { select: { id: true, name: true } } }, }); if (!req) throw new NotFoundException('需求不存在'); return req; } async update(productId: string, id: string, dto: UpdateRequirementDto) { await this.findOne(productId, id); return this.prisma.requirement.update({ where: { id_productId: { id, productId } }, data: { ...(dto.code !== undefined && { code: dto.code }), ...(dto.title !== undefined && { title: dto.title }), ...(dto.description !== undefined && { description: dto.description }), ...(dto.priority !== undefined && { priority: dto.priority }), }, }); } async updateStatus(productId: string, id: string, newStatus: string) { const req = await this.findOne(productId, id); const allowed = VALID_TRANSITIONS[req.status] || []; if (!allowed.includes(newStatus)) { throw new BadRequestException( `无法从 "${req.status}" 转换到 "${newStatus}"`, ); } return this.prisma.requirement.update({ where: { id_productId: { id, productId } }, data: { status: newStatus }, }); } async remove(productId: string, id: string) { await this.findOne(productId, id); return this.prisma.requirement.delete({ where: { id_productId: { id, productId } } }); } }