feat: complete signature and drainage reporting workflows

This commit is contained in:
hectorzhao
2026-07-13 17:40:37 +08:00
parent 567e4da0c9
commit 551b99cbcd
21 changed files with 452 additions and 71 deletions
+19 -7
View File
@@ -98,11 +98,13 @@ export interface CreateReportTaskDto {
tenantId: string;
signatureId: string;
channelId: string;
reportType?: 'signature' | 'drainage';
drainageItemId?: string;
createdById?: string;
}
export interface ChangeReportTaskStatusesDto {
items: Array<{ signatureId: string; channelId: string; status: string }>;
items: Array<{ signatureId: string; channelId: string; status: string; reportType?: 'signature' | 'drainage'; drainageItemId?: string }>;
reason?: string;
operatorId?: string;
}
@@ -973,11 +975,15 @@ export class ChannelsService implements OnModuleInit, OnModuleDestroy {
}
async createReportTask(data: CreateReportTaskDto) {
const reportType = data.reportType ?? 'signature';
if (reportType === 'drainage' && !data.drainageItemId) throw new BadRequestException('drainageItemId is required');
const task = await this.prisma.channelSignatureReportTask.create({
data: {
tenantId: data.tenantId,
signatureId: data.signatureId,
channelId: data.channelId,
reportType,
drainageItemId: reportType === 'drainage' ? data.drainageItemId : undefined,
createdById: data.createdById,
status: 'pending',
},
@@ -993,20 +999,24 @@ export class ChannelsService implements OnModuleInit, OnModuleDestroy {
if (!allowed.has(item.status)) throw new BadRequestException('unsupported report task status');
}
return this.prisma.$transaction(async (tx) => {
const signatureIds = [...new Set(data.items.map((item) => item.signatureId))];
const signatureIds = [...new Set(data.items.filter((item) => (item.reportType ?? 'signature') === 'signature').map((item) => item.signatureId))];
const drainageResults: Array<{ signatureId: string; reportType: 'drainage'; drainageItemId: string; channelId: string; status: string }> = [];
for (const item of data.items) {
const reportType = item.reportType ?? 'signature';
if (reportType === 'drainage' && !item.drainageItemId) throw new BadRequestException('drainageItemId is required');
const signature = await tx.smsSignature.findUnique({ where: { id: item.signatureId } });
const channel = await tx.smsChannel.findUnique({ where: { id: item.channelId } });
if (!signature || !channel) throw new NotFoundException('Signature or channel not found');
const existing = await tx.channelSignatureReportTask.findFirst({ where: { signatureId: item.signatureId, channelId: item.channelId } });
const existing = await tx.channelSignatureReportTask.findFirst({ where: { signatureId: item.signatureId, channelId: item.channelId, reportType, drainageItemId: reportType === 'drainage' ? item.drainageItemId : null } });
const task = existing
? await tx.channelSignatureReportTask.update({ where: { id: existing.id }, data: { status: item.status, reason: data.reason } })
: await tx.channelSignatureReportTask.create({ data: { tenantId: signature.tenantId, signatureId: item.signatureId, channelId: item.channelId, status: item.status, reason: data.reason, createdById: data.operatorId } });
: await tx.channelSignatureReportTask.create({ data: { tenantId: signature.tenantId, signatureId: item.signatureId, channelId: item.channelId, reportType, drainageItemId: reportType === 'drainage' ? item.drainageItemId : undefined, status: item.status, reason: data.reason, createdById: data.operatorId } });
await tx.channelSignatureReportRecord.create({ data: { taskId: task.id, channelId: item.channelId, action: 'manual_status_change', statusBefore: existing?.status, statusAfter: item.status, reason: data.reason, operatorId: data.operatorId } });
if (reportType === 'drainage') drainageResults.push({ signatureId: item.signatureId, reportType, drainageItemId: item.drainageItemId!, channelId: item.channelId, status: item.status });
}
const summaries = [];
for (const signatureId of signatureIds) summaries.push(await this.recomputeSignatureReportSummary(tx, signatureId));
return summaries;
return [...summaries, ...drainageResults];
});
}
@@ -1018,7 +1028,7 @@ export class ChannelsService implements OnModuleInit, OnModuleDestroy {
include: { group: { include: { items: { include: { channel: true } } } } },
}) : [];
const configuredChannels = routes.flatMap((route) => route.group.items.map((item) => item.channel)).filter((channel) => channel.status !== 'deleted');
const tasks = await tx.channelSignatureReportTask.findMany({ where: { signatureId }, include: { channel: true } });
const tasks = await tx.channelSignatureReportTask.findMany({ where: { signatureId, reportType: 'signature' }, include: { channel: true } });
const channels = configuredChannels.length ? configuredChannels : tasks.map((task) => task.channel);
const uniqueChannels = [...new Map(channels.map((channel) => [channel.id, channel])).values()];
const taskByChannel = new Map(tasks.map((task) => [task.channelId, task]));
@@ -1067,7 +1077,9 @@ export class ChannelsService implements OnModuleInit, OnModuleDestroy {
},
});
await this.updateReportTaskStatus(taskId, task.channelId, task.status, statusAfter, 'receipt_import', data.reason);
await this.recomputeSignatureReportSummary(this.prisma as unknown as Prisma.TransactionClient, task.signatureId);
if ((task.reportType ?? 'signature') === 'signature') {
await this.recomputeSignatureReportSummary(this.prisma as unknown as Prisma.TransactionClient, task.signatureId);
}
return imported;
}