fix: reconcile shared-channel receipts and protocol logs

This commit is contained in:
hectorzhao
2026-07-24 12:52:06 +08:00
parent 3997349c21
commit ca12f14b00
14 changed files with 1110 additions and 43 deletions
+27 -7
View File
@@ -160,10 +160,10 @@ export function AdminSystemLogsPage() {
}
const directionLabels: Record<ProtocolInteractionLogItem['direction'], string> = {
client_to_platform: '客户 → 平台',
platform_to_channel: '平台 → 通道',
channel_to_platform: '通道 → 平台',
platform_to_client: '平台 → 客户',
client_to_platform: '企业应用 → 平台',
platform_to_channel: '平台 → 供应商通道',
channel_to_platform: '供应商通道 → 平台',
platform_to_client: '平台 → 企业应用',
};
const protocolStatusTone: Record<ProtocolInteractionLogItem['status'], 'info' | 'success' | 'warning' | 'danger'> = {
@@ -174,6 +174,26 @@ const protocolStatusTone: Record<ProtocolInteractionLogItem['status'], 'info' |
failed: 'danger',
};
const protocolEventLabels: Record<string, string> = {
submit: 'CMPP_SUBMIT',
submit_resp: 'CMPP_SUBMIT_RESP',
deliver_receipt: 'CMPP_DELIVER(状态报告)',
deliver_uplink: 'CMPP_DELIVER(上行)',
deliver_resp: 'CMPP_DELIVER_RESP',
connect: 'CMPP_CONNECT',
send_request: 'HTTP发送请求',
receipt_webhook: 'HTTP回执Webhook',
uplink_webhook: 'HTTP上行Webhook',
};
function protocolStatusLabel(record: ProtocolInteractionLogItem) {
if (record.status === 'failed') return '失败';
if (record.status === 'retrying') return '重试中';
if (record.status === 'accepted') return '已受理';
if (record.status === 'received') return '已收到(历史)';
return record.direction === 'platform_to_channel' || record.direction === 'platform_to_client' ? '已发送' : '已接收并处理';
}
function ProtocolInteractionPanel({ active }: { active: boolean }) {
const [inputs, setInputs] = useState({ keyword: '', protocol: 'all', direction: 'all', eventType: 'all', status: 'all', range: 'today' });
const [filters, setFilters] = useState(inputs);
@@ -210,10 +230,10 @@ function ProtocolInteractionPanel({ active }: { active: boolean }) {
{ key: 'createdAt', title: '时间', width: '170px', render: (record) => <span className="muted">{formatDateTime(record.createdAt)}</span> },
{ key: 'protocol', title: '协议', width: '90px', render: (record) => <Tag tone={record.protocol === 'cmpp' ? 'info' : 'success'}>{record.protocol.toUpperCase()}</Tag> },
{ key: 'direction', title: '方向', width: '150px', render: (record) => directionLabels[record.direction] },
{ key: 'eventType', title: '事件', width: '150px', render: (record) => <strong>{record.eventType}</strong> },
{ key: 'eventType', title: '协议报文', width: '190px', render: (record) => <strong>{protocolEventLabels[record.eventType] ?? record.eventType}</strong> },
{ key: 'messageId', title: '消息标识', width: '220px', render: (record) => <div className="protocol-log-identifiers"><span>{record.messageId || '-'}</span><small>{record.gatewayMessageId || record.requestId || ''}</small></div> },
{ key: 'target', title: '对象', width: '160px', render: (record) => <div className="protocol-log-identifiers"><span>{record.phoneMasked || record.account || '-'}</span><small>{record.channelId || record.applicationId || ''}</small></div> },
{ key: 'status', title: '结果', width: '130px', render: (record) => <div className="protocol-log-result"><Tag tone={protocolStatusTone[record.status]}>{record.status}</Tag><small>{record.resultCode || ''}</small></div> },
{ key: 'status', title: '处理结果', width: '150px', render: (record) => <div className="protocol-log-result"><Tag tone={protocolStatusTone[record.status]}>{protocolStatusLabel(record)}</Tag><small>{record.resultCode || ''}</small></div> },
{ key: 'durationMs', title: '耗时', width: '90px', render: (record) => record.durationMs == null ? '-' : `${record.durationMs} ms` },
{ key: 'detail', title: '详情', width: '90px', render: (record) => <Button onClick={() => setDetail(record)} size="sm" variant="ghost"></Button> },
], []);
@@ -232,7 +252,7 @@ function ProtocolInteractionPanel({ active }: { active: boolean }) {
return (
<div className="page-stack protocol-log-panel">
<div className="protocol-log-hint"> CMPP </div>
<div className="protocol-log-hint"> CMPP </div>
<div className="system-log-filters protocol-log-filters">
<Input onChange={(event) => setInputs((value) => ({ ...value, keyword: event.target.value }))} placeholder="消息ID、请求ID、账号、脱敏手机号或结果码" prefix={<Search size={16} />} value={inputs.keyword} />
<Select onChange={(event) => setInputs((value) => ({ ...value, protocol: event.target.value }))} options={[{ label: '全部协议', value: 'all' }, { label: 'CMPP', value: 'cmpp' }, { label: 'HTTP', value: 'http' }]} value={inputs.protocol} />