import { AlertTriangle, Info, MessageSquare } from 'lucide-react'; import type { SmsMessageRecord, SmsMessageSegmentAudit } from '@/api/adminApi'; import { Button, CarrierTag, Modal, Tag } from '@/components/ui'; import { DrainageContent } from './SmsRecordList'; import { buildRouteRows, getReceiptNotice, getRecordStatus, getRecordStatusLabel, getTime, statusToneMap, } from './smsRecordModel'; type SendDetailModalProps = { record: SmsMessageRecord; segmentAudits: SmsMessageSegmentAudit[]; segmentLoading: boolean; onClose: () => void; }; export function SendDetailModal({ record, segmentAudits, segmentLoading, onClose }: SendDetailModalProps) { const visibleWordDecisions = record.channelWordDecisions?.filter( (decision) => decision.snapshot.hits.length > 0 || Boolean(decision.snapshot.reason), ); const routeRows = buildRouteRows(record, segmentAudits); const channelGroupNames = Array.from(new Set(routeRows.map((route) => route.channelGroup).filter(Boolean))); const orderedSegmentAudits = [...segmentAudits].sort((left, right) => { const timeDiff = new Date(left.createdAt).getTime() - new Date(right.createdAt).getTime(); return timeDiff || left.segmentIndex - right.segmentIndex || left.id.localeCompare(right.id); }); const sentAccessNumber = `${record.channel?.srcId ?? ''}${record.applicationExtension ?? ''}`; const displayStatus = getRecordStatus(record); const receiptNotice = getReceiptNotice(record); return ( 关闭 } onClose={onClose} open size="xl" title={

发送详情

{record.messageId}

} >
{visibleWordDecisions?.length ? (

通道筛选原因

{visibleWordDecisions.map((decision) => (

{getTime(decision.decidedAt)} · {decision.snapshot.reason || '已排除命中通道,按剩余候选选路'}

{decision.snapshot.hits.map((hit) => (

通道 {hit.channelName || hit.channelId}:命中 {hit.count} 个词, {hit.samples.map((sample) => `“${sample.word}”`).join('、')} {hit.count > hit.samples.length ? '(仅展示部分)' : ''}

))}
))}
) : null}

引流发送资格

{record.drainageGate ? ( <>

{record.drainageGate.reason || (record.drainageGate.targets.length ? '引流信息资格检查通过;发送仍受通道及其他规则限制' : '未检测到引流信息')}

{record.drainageGate.targets.map((target, index) => (

{target.text} · {target.materialIds.length ? '已匹配审核通过的资料' : '未匹配有效资料'}

))} ) : (

{segmentLoading ? '正在加载引流资格校验结果…' : '未执行引流资格校验'}

)}
最终状态 {getRecordStatusLabel(record)}
提交状态 {record.submitStatus ?? '-'}
回执状态 {record.receiptStatus ?? '-'}
最终回执时间 {getTime(record.deliveredAt)}
引流信息 {record.hasDrainageContent === true ? '含引流' : record.hasDrainageContent === false ? '不含引流' : '未检测'}
提交时间 {getTime(record.queuedAt)}
发送号码 {record.phoneNumber || '-'}
号码归属 {record.province ?? '-'} / {record.carrier ? : '-'}
通道组 {channelGroupNames.join(' / ') || '-'}
收到的接入号 {record.clientSrcId || '-'}
发送的接入号 {sentAccessNumber || '-'}
{receiptNotice ? (
{receiptNotice}
) : null}

短信内容

{record.originalContent != null ? ( <>

原始短信内容

{record.originalContent}

) : null} {record.originalContent != null ? record.submitRecords ?.filter((submit) => submit.sentContent != null) .map((submit, index) => (

第 {index + 1} 次提交通道内容 · {submit.channel?.name ?? submit.channelId}

{submit.sentContent}

)) : null}

通道发送与回执

{routeRows.map((route, index) => (
{index + 1}
{route.channel}
发送时间
{getTime(route.sentAt)}
回执时间
{getTime(route.receiptAt)}
回执码
{route.receiptCode ?? '-'}
提交状态
{route.submitStatus ?? '-'}
))}

状态信息

消息编号 {record.messageId}
发送状态 {getRecordStatusLabel(record)}
提交状态 {record.submitStatus ?? '-'}
回执状态 {record.receiptStatus ?? '-'}
{['submit_failed', 'failed', 'rejected'].includes(displayStatus) ? (
失败原因 {record.errorMessage ?? record.errorCode ?? '未返回明确失败原因'}
) : null}

分片补偿审计

{segmentLoading ? (
加载中...
) : segmentAudits.length === 0 ? (
暂无分片审计
) : (
{orderedSegmentAudits.map((segment) => (
分片 {segment.segmentIndex}/{segment.segmentTotal}
{segment.submitStatus} {segment.receiptStatus ? ( {segment.receiptStatus} ) : null}
通道
{segment.channel?.name ?? segment.channelId ?? '-'}
Sequence
{segment.sequenceId ?? '-'}
提交 ID
{segment.submitId}
网关 MsgId
{segment.gatewayMessageId ?? '-'}
补偿方式
{segment.compensationType ?? '-'}
审计时间
{getTime(segment.createdAt)}
错误信息
{segment.errorMessage ?? segment.errorCode ?? '-'}
))}
)}
); }