import { businessRouteById } from './business-routes.js'; export type LeaderTaskSummaryMilestone = 'final' | 'needs_review' | 'resolved'; export type LeaderTaskSummaryStatus = 'completed' | 'failed' | 'cancelled' | 'needs_review'; export interface LeaderTaskSummaryInput { taskId: string; status: string; businessRouteId: string | null; assigneeUsername: string; createdAt: string | Date; successReceipt?: Record | null; hadNeedsReview?: boolean; } export interface LeaderTaskSummaryProjection { milestone: LeaderTaskSummaryMilestone; deliveryStatus: LeaderTaskSummaryStatus; statusLabel: '已完成' | '未完成' | '已取消' | '需人工核验'; businessLabel: string; resultText: string; messageText: string; } const COMPLETED_STATUSES = new Set(['completed', 'dry_run']); const FAILED_STATUSES = new Set([ 'failed', 'parse_failed', 'parse_blocked', 'agent_parse_blocked', 'blocked', 'operation_blocked' ]); const NEEDS_REVIEW_STATUSES = new Set([ 'reconciliation_pending', 'saved_unverified', 'execution_uncertain', 'uncertain' ]); function singleLine(value: unknown, maxLength: number): string { return String(value ?? '') .normalize('NFKC') .replace(/[\u0000-\u001f\u007f]+/gu, ' ') .replace(/\s+/gu, ' ') .trim() .slice(0, maxLength); } function identifierValues(...values: unknown[]): string[] { const output: string[] = []; const visit = (value: unknown): void => { if (Array.isArray(value)) { value.forEach(visit); return; } if (typeof value !== 'string' && typeof value !== 'number') return; const candidate = singleLine(value, 80); if (!candidate || !/^(?=.*\d)[A-Za-z0-9._/()-]{4,80}$/u.test(candidate)) return; if (/^1[3-9]\d{9}$/u.test(candidate) || /^\d{15,18}[xX]?$/u.test(candidate)) return; if (!output.includes(candidate)) output.push(candidate); }; values.forEach(visit); return output.slice(0, 10); } function completedResult(status: string, receipt: Record): string { if (status === 'dry_run') return '业务方案已生成,尚未正式提交。'; const groupNumbers = identifierValues(receipt.group_numbers, receipt.group_number); const orderNumbers = identifierValues(receipt.order_numbers, receipt.order_number); const details = [ groupNumbers.length ? `团号:${groupNumbers.join('、')}` : '', orderNumbers.length ? `订单号:${orderNumbers.join('、')}` : '' ].filter(Boolean); return details.length ? `业务处理已完成。${details.join(';')}`.slice(0, 300) : '业务处理已完成,结果已记录。'; } export function formatShanghaiTimestamp(value: string | Date): string { const date = value instanceof Date ? value : new Date(value); if (Number.isNaN(date.getTime())) return '时间未记录'; const parts = new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h23' }).formatToParts(date); const part = (type: Intl.DateTimeFormatPartTypes) => ( parts.find((item) => item.type === type)?.value || '' ); return `${part('year')}-${part('month')}-${part('day')} ${part('hour')}:${part('minute')}:${part('second')}`; } export function buildLeaderTaskSummary( input: LeaderTaskSummaryInput ): LeaderTaskSummaryProjection | null { const status = singleLine(input.status, 80).toLowerCase(); let deliveryStatus: LeaderTaskSummaryStatus; let statusLabel: LeaderTaskSummaryProjection['statusLabel']; let resultText: string; if (COMPLETED_STATUSES.has(status)) { deliveryStatus = 'completed'; statusLabel = '已完成'; resultText = completedResult(status, input.successReceipt || {}); } else if (FAILED_STATUSES.has(status)) { deliveryStatus = 'failed'; statusLabel = '未完成'; resultText = '本次工作未完成,请在平台查看业务结果。'; } else if (status === 'cancelled') { deliveryStatus = 'cancelled'; statusLabel = '已取消'; resultText = '这项工作已取消。'; } else if (NEEDS_REVIEW_STATUSES.has(status)) { deliveryStatus = 'needs_review'; statusLabel = '需人工核验'; resultText = '当前结果需要人工核验,请勿重复提交。'; } else { return null; } const milestone: LeaderTaskSummaryMilestone = deliveryStatus === 'needs_review' ? 'needs_review' : input.hadNeedsReview ? 'resolved' : 'final'; const route = businessRouteById(input.businessRouteId); const businessLabel = singleLine(route?.directive || '其他任务', 120) || '其他任务'; const assignee = singleLine(input.assigneeUsername, 160) || '未知员工'; const taskId = singleLine(input.taskId, 200) || '任务编号未记录'; const header = milestone === 'resolved' ? '【员工任务摘要·结果更新】' : '【员工任务摘要】'; return { milestone, deliveryStatus, statusLabel, businessLabel, resultText: resultText.slice(0, 300), messageText: [ header, `员工:${assignee}`, `业务:${businessLabel}`, `状态:${statusLabel}`, `任务:${taskId}`, `提交:${formatShanghaiTimestamp(input.createdAt)}`, `结果:${resultText.slice(0, 300)}` ].join('\n') }; } export function isLeaderTaskSummaryStatus(value: unknown): boolean { const status = singleLine(value, 80).toLowerCase(); return COMPLETED_STATUSES.has(status) || FAILED_STATUSES.has(status) || NEEDS_REVIEW_STATUSES.has(status) || status === 'cancelled'; }