feat(v2.2): 建立分区关系表和AppData迁移预演
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import { IsString, IsNotEmpty, IsOptional, IsInt, Min, Max } from 'class-validator';
|
||||
|
||||
export class CreateRequirementDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
code?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title!: string;
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { RequirementService } from './requirement.service';
|
||||
|
||||
describe('RequirementService with V2.2 composite requirement key', () => {
|
||||
const makeService = () => {
|
||||
const prisma = {
|
||||
requirement: {
|
||||
create: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
findFirst: jest.fn(),
|
||||
findMany: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
prisma,
|
||||
service: new RequirementService(prisma as any),
|
||||
};
|
||||
};
|
||||
|
||||
it('creates requirements with a partition-key-scoped business code', async () => {
|
||||
const { prisma, service } = makeService();
|
||||
prisma.requirement.create.mockResolvedValue({ id: 'req-1', productId: 'product-1', code: 'REQ-001' });
|
||||
|
||||
await service.create('product-1', {
|
||||
code: 'REQ-001',
|
||||
title: 'Payment',
|
||||
creatorId: 'user-1',
|
||||
});
|
||||
|
||||
expect(prisma.requirement.create).toHaveBeenCalledWith({
|
||||
data: expect.objectContaining({
|
||||
productId: 'product-1',
|
||||
code: 'REQ-001',
|
||||
title: 'Payment',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('updates requirements by id plus product id', async () => {
|
||||
const { prisma, service } = makeService();
|
||||
prisma.requirement.findFirst.mockResolvedValue({
|
||||
id: 'req-1',
|
||||
productId: 'product-1',
|
||||
status: 'draft',
|
||||
});
|
||||
prisma.requirement.update.mockResolvedValue({ id: 'req-1', productId: 'product-1' });
|
||||
|
||||
await service.update('product-1', 'req-1', { title: 'Payment v2' });
|
||||
|
||||
expect(prisma.requirement.update).toHaveBeenCalledWith({
|
||||
where: { id_productId: { id: 'req-1', productId: 'product-1' } },
|
||||
data: { title: 'Payment v2' },
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes requirements by id plus product id', async () => {
|
||||
const { prisma, service } = makeService();
|
||||
prisma.requirement.findFirst.mockResolvedValue({
|
||||
id: 'req-1',
|
||||
productId: 'product-1',
|
||||
status: 'draft',
|
||||
});
|
||||
prisma.requirement.delete.mockResolvedValue({ id: 'req-1', productId: 'product-1' });
|
||||
|
||||
await service.remove('product-1', 'req-1');
|
||||
|
||||
expect(prisma.requirement.delete).toHaveBeenCalledWith({
|
||||
where: { id_productId: { id: 'req-1', productId: 'product-1' } },
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,13 @@ const VALID_TRANSITIONS: Record<string, string[]> = {
|
||||
[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) {}
|
||||
@@ -20,6 +27,7 @@ export class RequirementService {
|
||||
return this.prisma.requirement.create({
|
||||
data: {
|
||||
productId,
|
||||
code: dto.code?.trim() || createFallbackRequirementCode(),
|
||||
title: dto.title,
|
||||
description: dto.description || '',
|
||||
priority: dto.priority ?? 0,
|
||||
@@ -51,8 +59,9 @@ export class RequirementService {
|
||||
async update(productId: string, id: string, dto: UpdateRequirementDto) {
|
||||
await this.findOne(productId, id);
|
||||
return this.prisma.requirement.update({
|
||||
where: { id },
|
||||
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 }),
|
||||
@@ -69,13 +78,13 @@ export class RequirementService {
|
||||
);
|
||||
}
|
||||
return this.prisma.requirement.update({
|
||||
where: { id },
|
||||
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 } });
|
||||
return this.prisma.requirement.delete({ where: { id_productId: { id, productId } } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user