feat: complete drainage review and admin search workflows
This commit is contained in:
@@ -64,6 +64,8 @@ function createPrismaMock() {
|
||||
tenant: { id: 'tenant-1', name: '租户A', code: 'TENANT-A' },
|
||||
application: { id: 'app-1', name: '应用A' },
|
||||
materials: [],
|
||||
drainageItems: [],
|
||||
reportTasks: [],
|
||||
}]),
|
||||
findUnique: jest.fn().mockResolvedValue({ id: 'sig-1', tenantId: 'tenant-1', auditStatus: 'pending' }),
|
||||
create: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'sig-new', tenantId: 'tenant-1', ...data })),
|
||||
@@ -74,11 +76,20 @@ function createPrismaMock() {
|
||||
},
|
||||
drainageReportMaterial: {
|
||||
upsert: jest.fn().mockResolvedValue({ id: 'drainage-report-value-1' }),
|
||||
create: jest.fn().mockResolvedValue({ id: 'drainage-report-value-1' }),
|
||||
deleteMany: jest.fn().mockResolvedValue({ count: 0 }),
|
||||
},
|
||||
smsDrainageInfo: {
|
||||
findMany: jest.fn().mockResolvedValue([]),
|
||||
findUnique: jest.fn().mockResolvedValue(null),
|
||||
create: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'drainage-1', createdAt: new Date(), updatedAt: new Date(), submittedAt: new Date(), ...data })),
|
||||
update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'drainage-1', tenantId: 'tenant-1', signatureId: 'sig-1', applicationId: 'app-1', siteName: '官网', url: 'https://example.com', createdAt: new Date(), updatedAt: new Date(), submittedAt: new Date(), ...data })),
|
||||
},
|
||||
channelSignatureReportTask: {
|
||||
findFirst: jest.fn().mockResolvedValue(null),
|
||||
findMany: jest.fn().mockResolvedValue([]),
|
||||
create: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'drainage-task-1', ...data })),
|
||||
update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ id: 'drainage-task-1', ...data })),
|
||||
deleteMany: jest.fn().mockResolvedValue({ count: 0 }),
|
||||
},
|
||||
channelSignatureReportRecord: {
|
||||
@@ -188,7 +199,7 @@ describe('SmsConfigService', () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
|
||||
await expect(service.listApplications({ includeConnections: true })).resolves.toEqual([
|
||||
await expect(service.listApplications({ includeConnections: true, enterpriseKeyword: '租户', applicationKeyword: '应用', status: 'active' })).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: 'app-1',
|
||||
cmppStatus: 'connected',
|
||||
@@ -199,6 +210,7 @@ describe('SmsConfigService', () => {
|
||||
}),
|
||||
]);
|
||||
expect(prisma.smsApplication.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: expect.objectContaining({ status: 'active', tenant: { name: { contains: '租户' } }, name: { contains: '应用' } }),
|
||||
include: { tenant: true, ipAllowlist: true },
|
||||
}));
|
||||
expect(prisma.smsApplication.findMany.mock.calls[0][0]).not.toHaveProperty('take');
|
||||
@@ -427,7 +439,7 @@ describe('SmsConfigService', () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
|
||||
await expect(service.listSignatures({ keyword: '签名A' })).resolves.toEqual([
|
||||
await expect(service.listSignatures({ enterpriseKeyword: '租户', applicationKeyword: '应用', signatureKeyword: '签名', drainageKeyword: '官网' })).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: 'sig-1',
|
||||
tenant: expect.objectContaining({ name: '租户A' }),
|
||||
@@ -437,9 +449,18 @@ describe('SmsConfigService', () => {
|
||||
expect(prisma.smsSignature.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: expect.objectContaining({
|
||||
auditStatus: { not: 'deleted' },
|
||||
OR: expect.any(Array),
|
||||
tenant: { name: { contains: '租户' } },
|
||||
application: { name: { contains: '应用' } },
|
||||
name: { contains: '签名' },
|
||||
drainageItems: expect.objectContaining({ some: expect.objectContaining({ OR: expect.any(Array) }) }),
|
||||
}),
|
||||
include: { materials: true, tenant: true, application: true, reportTasks: { include: { channel: true } } },
|
||||
include: {
|
||||
materials: true,
|
||||
tenant: true,
|
||||
application: true,
|
||||
drainageItems: { where: { auditStatus: { not: 'deleted' } }, orderBy: { updatedAt: 'desc' } },
|
||||
reportTasks: { include: { channel: true, drainageInfo: true } },
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -473,7 +494,7 @@ describe('SmsConfigService', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('validates and persists dynamic signature and drainage report values by channel', async () => {
|
||||
it('validates and persists dynamic signature report values by channel without bypassing drainage audit', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
prisma.smsSignature.findUnique.mockResolvedValue({ id: 'sig-1', tenantId: 'tenant-1', applicationId: 'app-1', auditStatus: 'draft' });
|
||||
prisma.smsSignature.update.mockImplementation(({ data }) => Promise.resolve({ id: 'sig-1', tenantId: 'tenant-1', applicationId: 'app-1', ...data }));
|
||||
@@ -498,25 +519,48 @@ describe('SmsConfigService', () => {
|
||||
applicationId: 'app-1',
|
||||
drainageInfo: {
|
||||
signatureReportValues: { license: { fileObjectId: 'file-1', fileName: 'license.pdf' } },
|
||||
links: [{ id: 'drain-1', reportValues: { site_owner: '企业A' } }],
|
||||
links: [{ id: 'legacy-drain-1', reportValues: { site_owner: '企业A' } }],
|
||||
},
|
||||
});
|
||||
|
||||
expect(prisma.signatureReportMaterial.upsert).toHaveBeenCalledWith(expect.objectContaining({
|
||||
create: expect.objectContaining({ signatureId: 'sig-1', channelId: 'channel-1', fieldCode: 'license', fileObjectId: 'file-1' }),
|
||||
}));
|
||||
expect(prisma.drainageReportMaterial.upsert).toHaveBeenCalledWith(expect.objectContaining({
|
||||
create: expect.objectContaining({ signatureId: 'sig-1', drainageItemId: 'drain-1', channelId: 'channel-1', fieldCode: 'site_owner', fieldValue: '企业A' }),
|
||||
}));
|
||||
expect(prisma.drainageReportMaterial.deleteMany).toHaveBeenCalledWith({
|
||||
where: { signatureId: 'sig-1', drainageItemId: { notIn: ['drain-1'] } },
|
||||
});
|
||||
expect(prisma.channelSignatureReportTask.create).toHaveBeenCalledWith({
|
||||
data: expect.objectContaining({ signatureId: 'sig-1', channelId: 'channel-1', reportType: 'drainage', drainageItemId: 'drain-1', status: 'pending' }),
|
||||
});
|
||||
expect(prisma.channelSignatureReportRecord.create).toHaveBeenCalledWith({
|
||||
data: expect.objectContaining({ taskId: 'drainage-task-1', action: 'create', statusAfter: 'pending' }),
|
||||
});
|
||||
expect(prisma.drainageReportMaterial.create).not.toHaveBeenCalled();
|
||||
expect(prisma.channelSignatureReportTask.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates client drainage info as pending without generating channel report tasks', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
prisma.smsSignature.findUnique.mockResolvedValue({ id: 'sig-1', tenantId: 'tenant-1', applicationId: 'app-1', auditStatus: 'approved' });
|
||||
prisma.channelRouteRule.findMany.mockResolvedValue([]);
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
|
||||
await expect(service.createDrainageInfo('sig-1', { siteName: '官网', url: 'https://example.com', reportValues: {} }, {}, 'tenant-1'))
|
||||
.resolves.toEqual(expect.objectContaining({ id: 'drainage-1', auditStatus: 'pending' }));
|
||||
|
||||
expect(prisma.auditRecord.create).toHaveBeenCalledWith({ data: expect.objectContaining({ targetType: 'sms_drainage_info', action: 'submit', statusAfter: 'pending' }) });
|
||||
expect(prisma.channelSignatureReportTask.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates real drainage materials and channel tasks after operations approval', async () => {
|
||||
const prisma = createPrismaMock();
|
||||
const pendingItem = { id: 'drainage-1', tenantId: 'tenant-1', signatureId: 'sig-1', applicationId: 'app-1', siteName: '官网', url: 'https://example.com', reportValues: { site_owner: '企业A' }, auditStatus: 'pending' };
|
||||
const approvedItem = { ...pendingItem, auditStatus: 'approved', signature: { id: 'sig-1', applicationId: 'app-1' } };
|
||||
prisma.smsDrainageInfo.findUnique.mockResolvedValueOnce(pendingItem).mockResolvedValueOnce(approvedItem);
|
||||
prisma.smsDrainageInfo.update.mockResolvedValue({ ...approvedItem, tenant: {}, application: {} });
|
||||
prisma.channelRouteRule.findMany.mockResolvedValue([{
|
||||
id: 'route-1', priority: 10,
|
||||
group: { id: 'group-1', name: '默认通道组', items: [{ channel: { id: 'channel-1', code: 'CH-1', name: '通道一', reportFields: [{ status: 'active', required: true, reportType: 'drainage', drainageField: { id: 'field-2', code: 'site_owner', name: '网站主体', fieldType: 'string', description: null, status: 'active' } }] } }] },
|
||||
}] as never);
|
||||
prisma.$transaction.mockImplementation((callback) => callback(prisma));
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
|
||||
await service.approveDrainageInfo('drainage-1', {});
|
||||
|
||||
expect(prisma.drainageReportMaterial.create).toHaveBeenCalledWith({ data: expect.objectContaining({ drainageItemId: 'drainage-1', channelId: 'channel-1', fieldCode: 'site_owner', fieldValue: '企业A' }) });
|
||||
expect(prisma.channelSignatureReportTask.create).toHaveBeenCalledWith({ data: expect.objectContaining({ reportType: 'drainage', drainageItemId: 'drainage-1', status: 'pending' }) });
|
||||
expect(prisma.channelSignatureReportRecord.create).toHaveBeenCalledWith({ data: expect.objectContaining({ action: 'audit_approved_create', statusAfter: 'pending' }) });
|
||||
});
|
||||
|
||||
it('creates admin signatures with an approved initial audit status', async () => {
|
||||
@@ -565,7 +609,7 @@ describe('SmsConfigService', () => {
|
||||
const prisma = createPrismaMock();
|
||||
const service = new SmsConfigService(prisma as never);
|
||||
|
||||
await expect(service.listTemplates({ keyword: '模板A' })).resolves.toEqual([
|
||||
await expect(service.listTemplates({ enterpriseKeyword: '租户', applicationKeyword: '应用', nameKeyword: '模板', contentKeyword: '验证码' })).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: 'tpl-1',
|
||||
tenant: expect.objectContaining({ name: '租户A' }),
|
||||
@@ -576,7 +620,10 @@ describe('SmsConfigService', () => {
|
||||
expect(prisma.smsTemplate.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: expect.objectContaining({
|
||||
auditStatus: { not: 'deleted' },
|
||||
OR: expect.any(Array),
|
||||
tenant: { name: { contains: '租户' } },
|
||||
application: { name: { contains: '应用' } },
|
||||
name: { contains: '模板' },
|
||||
content: { contains: '验证码' },
|
||||
}),
|
||||
include: { variables: true, application: true, tenant: true, signature: true },
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user