feat: complete signature and drainage reporting workflows
This commit is contained in:
@@ -44,6 +44,10 @@ export interface CreateSmsSignatureDto {
|
||||
drainageInfo?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface CreateSmsSignatureOptions {
|
||||
initialAuditStatus?: string;
|
||||
}
|
||||
|
||||
export type UpdateSmsSignatureDto = Partial<Omit<CreateSmsSignatureDto, 'tenantId'>> & {
|
||||
auditStatus?: string;
|
||||
};
|
||||
@@ -589,19 +593,44 @@ export class SmsConfigService {
|
||||
const applicationIds = signatures.map((signature) => signature.applicationId).filter((id): id is string => Boolean(id));
|
||||
const routes = applicationIds.length ? await this.prisma.channelRouteRule.findMany({
|
||||
where: { applicationId: { in: applicationIds }, status: 'active' },
|
||||
include: { group: { include: { items: { include: { channel: true } } } } },
|
||||
include: { group: { include: { items: { include: { channel: { include: { reportFields: true } } } } } } },
|
||||
}) : [];
|
||||
return signatures.map((signature) => ({
|
||||
...signature,
|
||||
reportTargets: (() => {
|
||||
const channels = routes.filter((route) => route.applicationId === signature.applicationId && route.group).flatMap((route) => route.group!.items.map((item) => item.channel)).filter((channel) => channel.status !== 'deleted');
|
||||
const taskByChannel = new Map((signature.reportTasks ?? []).map((task) => [task.channelId, task]));
|
||||
const taskByChannel = new Map((signature.reportTasks ?? []).filter((task) => task.reportType === 'signature').map((task) => [task.channelId, task]));
|
||||
return [...new Map(channels.map((channel) => [channel.id, channel])).values()].map((channel) => ({ channel, channelId: channel.id, status: taskByChannel.get(channel.id)?.status ?? 'pending', taskId: taskByChannel.get(channel.id)?.id }));
|
||||
})(),
|
||||
drainageReportTargets: Object.fromEntries((Array.isArray((signature.drainageInfo as Record<string, unknown> | null)?.links) ? (signature.drainageInfo as Record<string, unknown>).links as Array<Record<string, unknown>> : []).map((link) => {
|
||||
const drainageItemId = String(link.id ?? '');
|
||||
const channels = routes
|
||||
.filter((route) => route.applicationId === signature.applicationId && route.group)
|
||||
.flatMap((route) => route.group!.items.map((item) => item.channel))
|
||||
.filter((channel) => channel.status !== 'deleted' && channel.reportFields.some((field) => field.status === 'active' && ['drainage', 'both'].includes(field.reportType)));
|
||||
const taskByChannel = new Map((signature.reportTasks ?? []).filter((task) => task.reportType === 'drainage' && task.drainageItemId === drainageItemId).map((task) => [task.channelId, task]));
|
||||
return [drainageItemId, [...new Map(channels.map((channel) => [channel.id, channel])).values()].map((channel) => ({ channel, channelId: channel.id, status: taskByChannel.get(channel.id)?.status ?? 'pending', taskId: taskByChannel.get(channel.id)?.id }))];
|
||||
})),
|
||||
drainageCarrierReportSummary: Object.fromEntries((Array.isArray((signature.drainageInfo as Record<string, unknown> | null)?.links) ? (signature.drainageInfo as Record<string, unknown>).links as Array<Record<string, unknown>> : []).map((link) => {
|
||||
const drainageItemId = String(link.id ?? '');
|
||||
const channels = routes
|
||||
.filter((route) => route.applicationId === signature.applicationId && route.group)
|
||||
.flatMap((route) => route.group!.items.map((item) => item.channel))
|
||||
.filter((channel) => channel.status !== 'deleted' && channel.reportFields.some((field) => field.status === 'active' && ['drainage', 'both'].includes(field.reportType)));
|
||||
const targets = [...new Map(channels.map((channel) => [channel.id, channel])).values()];
|
||||
const taskByChannel = new Map((signature.reportTasks ?? []).filter((task) => task.reportType === 'drainage' && task.drainageItemId === drainageItemId).map((task) => [task.channelId, task]));
|
||||
return [drainageItemId, Object.fromEntries(['mobile', 'unicom', 'telecom'].map((carrier) => {
|
||||
const carrierTargets = targets.filter((channel) => channel.carrier === carrier || channel.carrier === 'all');
|
||||
const statuses = carrierTargets.map((channel) => taskByChannel.get(channel.id)?.status ?? 'pending');
|
||||
const approved = statuses.filter((status) => status === 'approved').length;
|
||||
const status = !statuses.length ? 'not_applicable' : approved === statuses.length ? 'approved' : statuses.some((item) => ['failed', 'rejected'].includes(item)) ? 'failed' : statuses.some((item) => ['reporting', 'exporting'].includes(item)) || approved ? 'reporting' : statuses.some((item) => item === 'waiting_material') ? 'waiting_material' : 'pending';
|
||||
return [carrier, { status, approved, total: statuses.length }];
|
||||
}))];
|
||||
})),
|
||||
carrierReportSummary: Object.fromEntries(['mobile', 'unicom', 'telecom'].map((carrier) => {
|
||||
const configured = routes.filter((route) => route.applicationId === signature.applicationId && route.group).flatMap((route) => route.group!.items.map((item) => item.channel)).filter((channel) => channel.status !== 'deleted' && (channel.carrier === carrier || channel.carrier === 'all'));
|
||||
const targets = [...new Map(configured.map((channel) => [channel.id, channel])).values()];
|
||||
const taskByChannel = new Map((signature.reportTasks ?? []).map((task) => [task.channelId, task]));
|
||||
const taskByChannel = new Map((signature.reportTasks ?? []).filter((task) => task.reportType === 'signature').map((task) => [task.channelId, task]));
|
||||
const statuses = targets.map((channel) => taskByChannel.get(channel.id)?.status ?? 'pending');
|
||||
const approved = statuses.filter((status) => status === 'approved').length;
|
||||
const status = !targets.length ? 'not_applicable' : approved === targets.length ? 'approved' : statuses.some((item) => ['failed', 'rejected'].includes(item)) ? 'failed' : statuses.some((item) => ['reporting', 'exporting'].includes(item)) || approved ? 'reporting' : statuses.some((item) => item === 'waiting_material') ? 'waiting_material' : 'pending';
|
||||
@@ -610,7 +639,7 @@ export class SmsConfigService {
|
||||
}));
|
||||
}
|
||||
|
||||
async createSignature(data: CreateSmsSignatureDto) {
|
||||
async createSignature(data: CreateSmsSignatureDto, options: CreateSmsSignatureOptions = {}) {
|
||||
await this.validateSignatureReportValues(data.applicationId, data.drainageInfo);
|
||||
const drainageInfo = await this.withReportRequirementSnapshot(data.applicationId, data.drainageInfo);
|
||||
const signature = await this.prisma.smsSignature.create({
|
||||
@@ -619,10 +648,21 @@ export class SmsConfigService {
|
||||
applicationId: data.applicationId,
|
||||
name: data.name,
|
||||
purpose: data.purpose,
|
||||
auditStatus: options.initialAuditStatus,
|
||||
drainageInfo: drainageInfo as Prisma.InputJsonValue | undefined,
|
||||
},
|
||||
});
|
||||
await this.syncSignatureReportValues(signature.id, data.applicationId, drainageInfo);
|
||||
if (options.initialAuditStatus) {
|
||||
await this.createAuditRecord({
|
||||
tenantId: signature.tenantId,
|
||||
targetType: 'sms_signature',
|
||||
targetId: signature.id,
|
||||
action: 'admin_create_approved',
|
||||
statusAfter: options.initialAuditStatus,
|
||||
reason: '运营端新建签名自动审核通过',
|
||||
});
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
@@ -684,6 +724,13 @@ export class SmsConfigService {
|
||||
...(drainageItemIds.length > 0 ? { drainageItemId: { notIn: drainageItemIds } } : {}),
|
||||
},
|
||||
});
|
||||
await this.prisma.channelSignatureReportTask.deleteMany({
|
||||
where: {
|
||||
signatureId,
|
||||
reportType: 'drainage',
|
||||
...(drainageItemIds.length > 0 ? { drainageItemId: { notIn: drainageItemIds } } : {}),
|
||||
},
|
||||
});
|
||||
for (const field of fields.filter((item) => item.reportTypes.some((type) => type === 'signature' || type === 'both'))) {
|
||||
const value = reportValueParts(signatureValues[field.code]);
|
||||
for (const channel of field.channels) {
|
||||
@@ -694,11 +741,29 @@ export class SmsConfigService {
|
||||
});
|
||||
}
|
||||
}
|
||||
const drainageFields = fields.filter((item) => item.reportTypes.some((type) => type === 'drainage' || type === 'both'));
|
||||
const drainageChannels = new Map(drainageFields.flatMap((item) => item.channels).map((channel) => [channel.id, channel]));
|
||||
const signatureOwner = links.length > 0
|
||||
? await this.prisma.smsSignature.findUnique({ where: { id: signatureId }, select: { tenantId: true } })
|
||||
: null;
|
||||
for (const link of links) {
|
||||
const drainageItemId = String(link.id ?? '');
|
||||
const values = isRecord(link.reportValues) ? link.reportValues : {};
|
||||
if (!drainageItemId) continue;
|
||||
for (const field of fields.filter((item) => item.reportTypes.some((type) => type === 'drainage' || type === 'both'))) {
|
||||
for (const channel of drainageChannels.values()) {
|
||||
const existingTask = await this.prisma.channelSignatureReportTask.findFirst({
|
||||
where: { signatureId, channelId: channel.id, reportType: 'drainage', drainageItemId },
|
||||
});
|
||||
if (!existingTask && signatureOwner) {
|
||||
const task = await this.prisma.channelSignatureReportTask.create({
|
||||
data: { tenantId: signatureOwner.tenantId, signatureId, channelId: channel.id, reportType: 'drainage', drainageItemId, status: 'pending' },
|
||||
});
|
||||
await this.prisma.channelSignatureReportRecord.create({
|
||||
data: { taskId: task.id, channelId: channel.id, action: 'create', statusAfter: 'pending' },
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const field of drainageFields) {
|
||||
const value = reportValueParts(values[field.code]);
|
||||
for (const channel of field.channels) {
|
||||
await this.prisma.drainageReportMaterial.upsert({
|
||||
|
||||
Reference in New Issue
Block a user