feat: add carrier-aware signature retirement alerts

This commit is contained in:
hectorzhao
2026-08-10 20:54:05 +08:00
parent 232d1c22a3
commit 55aa054005
52 changed files with 3074 additions and 152 deletions
+15 -6
View File
@@ -11,6 +11,7 @@ import { SmsReportValidationService } from './report-validation.service';
import { SmsAuditService } from './audit.service';
import { shanghaiDateRange } from '../common/shanghai-date-range';
import { summarizeReportStatuses } from '../common/report-status';
import { normalizeChannelCarriers } from '../channels/channels.helpers';
/** R3 domain service. Kept framework-agnostic and composed behind SmsConfigService. */
export class SmsSignatureService {
@@ -84,8 +85,14 @@ export class SmsSignatureService {
drainageInfo: { ...legacyPayload, links: drainageLinks },
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 ?? []).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 }));
const tasks = (signature.reportTasks ?? []).filter((task) => task.reportType === 'signature');
return [...new Map(channels.map((channel) => [channel.id, channel])).values()].flatMap((channel) => (
normalizeChannelCarriers(channel.carriers, channel.carrier).map((carrier) => {
const task = tasks.find((candidate) => candidate.channelId === channel.id && candidate.carrier === carrier)
?? tasks.find((candidate) => candidate.channelId === channel.id && candidate.carrier === null && candidate.approvalScope === 'legacy_channel');
return { channel, channelId: channel.id, carrier, status: task?.status ?? 'pending', taskId: task?.id, approvedAt: task?.approvedAt, approvalScope: task?.approvalScope ?? 'carrier_specific' };
})
));
})(),
drainageReportTargets: Object.fromEntries(signature.drainageItems.map((drainageItem) => {
const drainageItemId = drainageItem.id;
@@ -108,16 +115,18 @@ export class SmsSignatureService {
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 carrierTargets = targets.filter((channel) => normalizeChannelCarriers(channel.carriers, channel.carrier).includes(carrier));
const statuses = carrierTargets.flatMap((channel) => taskByChannel.get(channel.id)?.status ? [taskByChannel.get(channel.id)!.status] : []);
return [carrier, summarizeReportStatuses(statuses)];
}))];
})),
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 configured = routes.filter((route) => route.applicationId === signature.applicationId && route.group).flatMap((route) => route.group!.items.map((item) => item.channel)).filter((channel) => channel.status !== 'deleted' && normalizeChannelCarriers(channel.carriers, channel.carrier).includes(carrier));
const targets = [...new Map(configured.map((channel) => [channel.id, channel])).values()];
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 signatureTasks = (signature.reportTasks ?? []).filter((task) => task.reportType === 'signature');
const statuses = targets.map((channel) => signatureTasks.find((task) => task.channelId === channel.id && task.carrier === carrier)?.status
?? signatureTasks.find((task) => task.channelId === channel.id && task.carrier === null && task.approvalScope === 'legacy_channel')?.status
?? 'pending');
return [carrier, summarizeReportStatuses(statuses)];
})),
};