diff --git a/vehicle-data-platform/apps/api/internal/platform/service.go b/vehicle-data-platform/apps/api/internal/platform/service.go index 4b40bf4b..9e00e4e1 100644 --- a/vehicle-data-platform/apps/api/internal/platform/service.go +++ b/vehicle-data-platform/apps/api/internal/platform/service.go @@ -110,7 +110,7 @@ var qualityAlertRuleTemplates = []QualityAlertRule{ Level: "P1", Owner: "协议解析", Trigger: "核心字段缺失导致定位、里程或统计不可用", - Notify: "通知解析负责人核对字段映射和 RAW 样本", + Notify: "通知解析负责人核对字段映射和原始记录样本", SLA: "当日闭环", }, } @@ -130,7 +130,7 @@ var qualityNotificationPolicies = []QualityNotificationPolicy{ Channel: "站内告警 / 每日汇总邮件", Condition: "字段缺失、链路间断、容量风险", EscalationMinutes: 120, - AcceptanceCriteria: "核心字段恢复解析,影响车辆可通过 RAW 与历史证据复核", + AcceptanceCriteria: "核心字段恢复解析,影响车辆可通过原始记录与历史证据复核", }, { Name: "P2 趋势关注", @@ -207,8 +207,8 @@ func buildSourceReadinessPlan(summary VehicleServiceSummary, health OpsHealth, q Severity: "ok", Status: "生产可用", Evidence: sourceReadinessEvidence(stat, missing, health), - Action: "保持实时、轨迹、RAW 和统计链路巡检。", - Acceptance: "该来源有在线车辆、无消息积压,车辆服务可回查实时、轨迹和 RAW 证据。", + Action: "保持实时、轨迹、原始记录和统计链路巡检。", + Acceptance: "该来源有在线车辆、无消息积压,车辆服务可回查实时、轨迹和原始记录。", VehiclesHash: buildHash("vehicles", "", protocol, map[string]string{"missingProtocol": protocol}), RealtimeHash: buildHash("realtime", "", protocol, map[string]string{"online": "online"}), HistoryHash: buildHash("history-query", "", protocol, map[string]string{"tab": "raw", "includeFields": "true"}), @@ -218,7 +218,7 @@ func buildSourceReadinessPlan(summary VehicleServiceSummary, health OpsHealth, q row.Severity = "error" row.Status = "未形成来源证据" row.Action = "确认平台账号、端口监听、订阅配置和协议接入服务是否启动。" - row.Acceptance = "该协议至少出现绑定车辆和实时 RAW 证据。" + row.Acceptance = "该来源至少出现绑定车辆和实时原始记录。" } else if stat.Online <= 0 { row.Severity = "error" row.Status = "来源全离线" @@ -1334,7 +1334,7 @@ func qualityNotificationText(row QualityPriorityIssue) string { "详情:" + firstNonEmpty(row.Detail, "-"), "实时定位:" + row.RealtimeHash, "轨迹证据:" + row.HistoryHash, - "RAW证据:" + row.RawHash, + "原始记录:" + row.RawHash, "车辆服务:" + row.VehicleHash, }, "\n") } diff --git a/vehicle-data-platform/apps/web/src/pages/NotificationRules.tsx b/vehicle-data-platform/apps/web/src/pages/NotificationRules.tsx index 818f8074..79abfb2d 100644 --- a/vehicle-data-platform/apps/web/src/pages/NotificationRules.tsx +++ b/vehicle-data-platform/apps/web/src/pages/NotificationRules.tsx @@ -176,7 +176,7 @@ function slaEscalationReport(rows: SlaEscalationRow[], release?: string) { ` 问题:${qualityIssueLabel(row.issue.issueType)} / ${row.issue.protocol}`, ` 建议动作:${row.issue.actionLabel}`, ` 车辆服务:${window.location.origin}${window.location.pathname}${row.issue.vehicleHash}`, - ` RAW证据:${window.location.origin}${window.location.pathname}${row.issue.rawHash}` + ` 原始记录:${window.location.origin}${window.location.pathname}${row.issue.rawHash}` ].join('\n')) : ['暂无待升级告警']) ].join('\n'); } @@ -239,7 +239,7 @@ function notificationExecutionMatrixReport(rows: NotificationExecutionRow[], rel ` 证据:${row.evidenceText}`, ` 动作:${row.issue.actionLabel} - ${row.issue.actionDetail || '-'}`, ` 车辆服务:${window.location.origin}${window.location.pathname}${row.issue.vehicleHash}`, - ` RAW证据:${window.location.origin}${window.location.pathname}${row.issue.rawHash}` + ` 原始记录:${window.location.origin}${window.location.pathname}${row.issue.rawHash}` ].join('\n')) : ['暂无待执行通知']) ].join('\n'); } @@ -313,7 +313,7 @@ function notificationCoverageReport({ '', '管理动作:', '1. P0 命中先确认责任方、渠道和升级窗口是否明确', - '2. 证据不完整的车辆先补齐车辆服务、实时、轨迹、RAW 链接', + '2. 证据不完整的车辆先补齐车辆服务、实时、轨迹和原始记录链接', '3. 超时或即将升级的告警必须同步业务责任人', '4. 链路异常时优先恢复采集、MySQL、TDEngine、Redis、Kafka 等前置能力' ].join('\n'); @@ -364,7 +364,7 @@ function priorityIssueDigest(plan: QualityNotificationPlan | null, release?: str ` 车辆服务:${window.location.origin}${window.location.pathname}${issue.vehicleHash}`, ` 实时定位:${window.location.origin}${window.location.pathname}${issue.realtimeHash}`, ` 轨迹证据:${window.location.origin}${window.location.pathname}${issue.historyHash}`, - ` RAW证据:${window.location.origin}${window.location.pathname}${issue.rawHash}` + ` 原始记录:${window.location.origin}${window.location.pathname}${issue.rawHash}` ].join('\n')) ].join('\n'); } @@ -398,12 +398,12 @@ function alertEscalationChecklist(plan: QualityNotificationPlan | null, release? `车辆服务:${window.location.origin}${window.location.pathname}${nextIssue.vehicleHash}`, `实时证据:${window.location.origin}${window.location.pathname}${nextIssue.realtimeHash}`, `轨迹证据:${window.location.origin}${window.location.pathname}${nextIssue.historyHash}`, - `RAW证据:${window.location.origin}${window.location.pathname}${nextIssue.rawHash}` + `原始记录:${window.location.origin}${window.location.pathname}${nextIssue.rawHash}` ].join('\n') : '暂无待处置告警', '', '值班动作:', '1. 先处理 P0,无来源/VIN 缺失/存储不可写优先于普通字段缺失', - '2. 复制单条通知给责任团队,并同步车辆服务、实时、轨迹、RAW 证据', + '2. 复制单条通知给责任团队,并同步车辆服务、实时、轨迹和原始记录', '3. 超过升级窗口未恢复时,按主通知策略升级到业务责任人', '4. 恢复后检查验收口径,并在告警事件页确认不再命中' ].join('\n'); @@ -435,7 +435,7 @@ function alertNotificationDrillPackage(plan: QualityNotificationPlan | null, hea '', '演练步骤:', '1. 在告警事件页确认 P0/P1 命中数量与本页一致', - '2. 对第一条待通知告警打开车辆服务、实时、轨迹、RAW 四类证据', + '2. 对第一条待通知告警打开车辆服务、实时、轨迹、原始记录四类证据', '3. 复制单条告警通知给责任团队,记录发送时间', '4. 超过升级窗口仍未恢复时,按策略升级到业务责任人', '5. 恢复后按验收口径复核,并确认告警不再命中', @@ -449,7 +449,7 @@ function alertNotificationDrillPackage(plan: QualityNotificationPlan | null, hea `车辆服务:${window.location.origin}${window.location.pathname}${nextIssue.vehicleHash}`, `实时证据:${window.location.origin}${window.location.pathname}${nextIssue.realtimeHash}`, `轨迹证据:${window.location.origin}${window.location.pathname}${nextIssue.historyHash}`, - `RAW证据:${window.location.origin}${window.location.pathname}${nextIssue.rawHash}` + `原始记录:${window.location.origin}${window.location.pathname}${nextIssue.rawHash}` ].join('\n') : '暂无待通知样例' ].join('\n'); } @@ -565,7 +565,7 @@ export function NotificationRules() { step: '02', title: '补齐证据', value: `${evidenceCompleteCount}/${priorityIssues.length || 0} 完整`, - detail: '通知前必须带上车辆服务、实时监控、轨迹回放和RAW证据链接。', + detail: '通知前必须带上车辆服务、实时监控、轨迹回放和原始记录链接。', action: '证据矩阵', color: evidenceCompleteCount === priorityIssues.length ? 'green' as const : 'orange' as const, onClick: () => nextPriorityIssue?.rawHash ? navigateHash(nextPriorityIssue.rawHash) : undefined @@ -766,7 +766,7 @@ export function NotificationRules() { - + ) : ( @@ -801,7 +801,7 @@ export function NotificationRules() { render: (_: unknown, row: NotificationExecutionRow) => ( - + ) @@ -846,7 +846,7 @@ export function NotificationRules() { render: (_: unknown, row: SlaEscalationRow) => ( - + ) @@ -924,7 +924,7 @@ export function NotificationRules() { - + ) diff --git a/vehicle-data-platform/apps/web/src/pages/Quality.tsx b/vehicle-data-platform/apps/web/src/pages/Quality.tsx index db0ab3b2..f8779958 100644 --- a/vehicle-data-platform/apps/web/src/pages/Quality.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Quality.tsx @@ -67,7 +67,7 @@ const alertRuleTemplates = [ level: 'P1', owner: '协议解析', trigger: '核心字段缺失导致定位、里程或统计不可用', - notify: '通知解析负责人核对字段映射和 RAW 样本', + notify: '通知解析负责人核对字段映射和原始记录样本', sla: '当日闭环' } ]; @@ -87,7 +87,7 @@ const notificationPolicies = [ channel: '站内告警 / 每日汇总邮件', condition: '字段缺失、链路间断、容量风险', escalationMinutes: 120, - acceptanceCriteria: '核心字段恢复解析,影响车辆可通过 RAW 与历史证据复核' + acceptanceCriteria: '核心字段恢复解析,影响车辆可通过原始记录与历史证据复核' }, { name: 'P2 趋势关注', @@ -187,7 +187,7 @@ function qualityActionRecommendation(row: QualityIssueRow) { return { label: '核对解析字段', color: 'orange' as const, - detail: '字段缺失会影响统计和展示,优先核对解析映射和原始 RAW。' + detail: '字段缺失会影响统计和展示,优先核对解析映射和原始记录。' }; } return { @@ -317,7 +317,7 @@ function priorityIssueNotificationText(row: PriorityIssueRow) { `详情:${row.detail || '-'}`, `实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`, `轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`, - `RAW证据:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, + `原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`, `告警筛选:${qualityShareURL()}` ].join('\n'); @@ -329,10 +329,10 @@ function alertRuleTemplate(issueType: string) { function acceptanceCriteria(row: PriorityIssueRow) { if (row.issueType === 'VIN_MISSING') { - return '车辆能解析到 VIN,实时/历史/RAW 证据可通过同一车辆服务查询'; + return '车辆能解析到 VIN,实时、历史和原始记录可通过同一车辆服务查询'; } if (row.issueType === 'NO_SOURCE') { - return '至少一个生产来源恢复在线,车辆实时状态、历史轨迹和 RAW 证据可查询'; + return '至少一个生产来源恢复在线,车辆实时状态、历史轨迹和原始记录可查询'; } if (row.issueType === 'LINK_GAP') { return '来源恢复连续上报,实时在线窗口和历史轨迹无异常断点'; @@ -369,12 +369,12 @@ function priorityIssueTicketText(row: PriorityIssueRow) { `处置说明:${row.actionDetail || '-'}`, '处置步骤:', '1. 打开车辆服务核对当前身份解析结果', - '2. 核对实时定位、轨迹证据和 RAW 证据', + '2. 核对实时定位、轨迹证据和原始记录', '3. 完成后确认车辆服务状态恢复,问题不再命中当前告警', `验收标准:${acceptanceCriteria(row)}`, `实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`, `轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`, - `RAW证据:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, + `原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`, `告警筛选:${qualityShareURL()}` ].join('\n'); @@ -402,7 +402,7 @@ function priorityIssueEvidencePackageText(row: PriorityIssueRow) { `验收标准:${acceptanceCriteria(row)}`, `实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`, `轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`, - `RAW证据:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, + `原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, `里程复核:${appURL(buildAppHash({ page: 'mileage', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`, `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`, `告警筛选:${qualityShareURL()}` @@ -580,7 +580,7 @@ function notificationHandoffText({ ` 详情:${row.detail || '-'}`, ` 实时:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`, ` 轨迹:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`, - ` RAW:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, + ` 原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, ` 车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}` ].join('\n'); }) @@ -593,7 +593,7 @@ function notificationHandoffText({ `优先级:P0 ${p0Rows.length.toLocaleString()} 条 / P1 ${p1Rows.length.toLocaleString()} 条`, `活跃规则:${activeRules.length.toLocaleString()} 类${activeRules.length > 0 ? `(${activeRules.map((row) => `${row.level} ${row.title || qualityIssueLabel(row.issueType)} ${row.count}`).join(';')})` : ''}`, primaryPolicy ? `主通知策略:${[primaryPolicy.name, primaryPolicy.target, primaryPolicy.channel, formatEscalationMinutes(primaryPolicy.escalationMinutes)].filter(Boolean).join(' / ')}` : '主通知策略:-', - primaryPolicy?.acceptanceCriteria ? `统一验收:${primaryPolicy.acceptanceCriteria}` : '统一验收:车辆服务状态恢复,实时、轨迹、RAW 证据可查询', + primaryPolicy?.acceptanceCriteria ? `统一验收:${primaryPolicy.acceptanceCriteria}` : '统一验收:车辆服务状态恢复,实时、轨迹和原始记录可查询', '', '待通知车辆:', ...issueLines, @@ -708,7 +708,7 @@ function alertCustomerDecisionText({ '客户处置路径:', '1. 先确认影响车辆和 P0/P1 数量。', '2. P0 立即通知责任团队,超过 SLA 自动升级。', - '3. 每辆焦点车都要打开实时、轨迹、RAW、里程证据闭环。', + '3. 每辆焦点车都要打开实时、轨迹、原始记录、里程证据闭环。', '4. 恢复后确认告警不再命中,并保留交接包。', '', `告警事件:${qualityShareURL()}`, @@ -1031,7 +1031,7 @@ export function Quality({ }, { label: '证据闭环', - value: '实时/轨迹/RAW/里程', + value: '实时/轨迹/原始/里程', detail: '所有通知都必须回到车辆证据,不只停留在来源或链路指标。', color: 'blue' as const, action: '交接包', @@ -1067,7 +1067,7 @@ export function Quality({ {issueVehicleCount.toLocaleString()} 辆车受影响 - 告警中心面向客户处置:先判断影响车辆和优先级,再通知责任团队,并用实时、轨迹、RAW、里程证据闭环。 + 告警中心面向客户处置:先判断影响车辆和优先级,再通知责任团队,并用实时、轨迹、原始记录、里程证据闭环。 @@ -1147,7 +1147,7 @@ export function Quality({ {focusIssue?.vehicleLabel || '暂无优先车辆'} {focusIssue - ? `${focusIssue.actionLabel}:${focusIssue.actionDetail || focusIssue.detail || '请结合实时、轨迹、RAW 和里程证据闭环。'}` + ? `${focusIssue.actionLabel}:${focusIssue.actionDetail || focusIssue.detail || '请结合实时、轨迹、原始记录和里程证据闭环。'}` : '当前没有进入优先队列的车辆,保持实时监控并定期复核告警规则。'} @@ -1190,9 +1190,9 @@ export function Quality({ }, { label: '原始证据', - value: focusIssue ? '查RAW' : '待命', + value: focusIssue ? '查原始' : '待命', detail: '保留原始帧和解析字段,给平台、客户和研发确认责任边界。', - action: 'RAW证据', + action: '原始记录', color: focusIssue ? 'blue' as const : 'grey' as const, disabled: !focusIssue || !focusLookup?.key || !onOpenRaw, onClick: () => focusIssue && openIssueRaw(focusIssue) @@ -1357,7 +1357,7 @@ export function Quality({ - + @@ -1566,7 +1566,7 @@ export function Quality({ - + diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx index d638e911..db11ec67 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -166,8 +166,8 @@ test('exposes AMap operations shortcuts when map key is configured', async () => severity: 'ok', status: '生产可用', evidence: '在线 174/424,缺失车辆 609,Kafka Lag 42', - action: '保持实时、轨迹、RAW 和统计链路巡检。', - acceptance: '车辆服务可回查实时、轨迹和 RAW 证据。', + action: '保持实时、轨迹、原始记录和统计链路巡检。', + acceptance: '车辆服务可回查实时、轨迹和原始记录。', vehiclesHash: '#/vehicles?protocol=JT808&missingProtocol=JT808', realtimeHash: '#/realtime?protocol=JT808&online=online', historyHash: '#/history-query?protocol=JT808&tab=raw&includeFields=true', @@ -362,8 +362,8 @@ test('shows global alert pressure from notification plan in topbar', async () => severity: 'ok', status: '生产可用', evidence: '在线 11/50,缺失车辆 983,Kafka Lag 0', - action: '保持实时、轨迹、RAW 和统计链路巡检。', - acceptance: '车辆服务可回查实时、轨迹和 RAW 证据。', + action: '保持实时、轨迹、原始记录和统计链路巡检。', + acceptance: '车辆服务可回查实时、轨迹和原始记录。', vehiclesHash: '#/vehicles?protocol=YUTONG_MQTT&missingProtocol=YUTONG_MQTT', realtimeHash: '#/realtime?protocol=YUTONG_MQTT&online=online', historyHash: '#/history-query?protocol=YUTONG_MQTT&tab=raw&includeFields=true', @@ -667,8 +667,8 @@ test('dashboard exposes vehicle data center capability matrix', async () => { severity: 'ok', status: '生产可用', evidence: '在线 11/50,缺失车辆 983,Kafka Lag 0', - action: '保持实时、轨迹、RAW 和统计链路巡检。', - acceptance: '车辆服务可回查实时、轨迹和 RAW 证据。', + action: '保持实时、轨迹、原始记录和统计链路巡检。', + acceptance: '车辆服务可回查实时、轨迹和原始记录。', vehiclesHash: '#/vehicles?protocol=YUTONG_MQTT&missingProtocol=YUTONG_MQTT', realtimeHash: '#/realtime?protocol=YUTONG_MQTT&online=online', historyHash: '#/history-query?protocol=YUTONG_MQTT&tab=raw&includeFields=true', @@ -3660,7 +3660,7 @@ test('shows alert rule and notification policy workspace on quality page', async expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响车辆:3 辆;问题记录:5 条')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('客户处置路径:')); expect(await screen.findByText('告警事件与通知中心')).toBeInTheDocument(); - expect(screen.getByText('客户处置:先判断影响车辆和优先级,再通知责任团队,并用实时、轨迹、RAW、里程证据闭环。', { exact: false })).toBeInTheDocument(); + expect(screen.getByText('客户处置:先判断影响车辆和优先级,再通知责任团队,并用实时、轨迹、原始记录、里程证据闭环。', { exact: false })).toBeInTheDocument(); expect(screen.getByText('链路状态')).toBeInTheDocument(); expect(screen.getAllByText('优先队列').length).toBeGreaterThanOrEqual(1); expect(await screen.findByText('告警触发规则')).toBeInTheDocument(); @@ -4064,14 +4064,14 @@ test('renders notification rules as a standalone operations page', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 无来源规则 / 平台接入 / 命中 3 / SLA 30 分钟确认')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作:确认平台转发 - 核对平台转发配置')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=VIN-RULES-001')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据:http://localhost:3000/#/history-query?keyword=VIN-RULES-001&tab=raw')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=VIN-RULES-001&tab=raw')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('超过升级窗口未恢复时')); fireEvent.click(screen.getByRole('button', { name: '复制通知演练包' })); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警通知演练包】')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本:platform-rules-test')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('规则规模:P0 1 类,P1 1 类,待通知 1 条')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('P0策略:P0 实时中断 / 接入运维 + 业务责任人 / 邮件 / 企业微信 / 30 分钟升级')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 对第一条待通知告警打开车辆服务、实时、轨迹、RAW 四类证据')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 对第一条待通知告警打开车辆服务、实时、轨迹、原始记录四类证据')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知文本:【P0 告警通知】通知规则页待通知')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据:http://localhost:3000/#/history?keyword=VIN-RULES-001')); fireEvent.click(screen.getByRole('button', { name: '复制通知执行矩阵' })); @@ -4357,7 +4357,7 @@ test('copies notification text from quality priority queue', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情:手机号未映射 VIN,需要维护 binding')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位:http://localhost:3000/#/realtime?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据:http://localhost:3000/#/history?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据:http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警筛选:http://localhost:3000/#/alert-events')); }); @@ -4447,9 +4447,9 @@ test('copies dispatch ticket from quality priority queue', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作:维护身份绑定')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('处置步骤:')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 打开车辆服务核对当前身份解析结果')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 核对实时定位、轨迹证据和 RAW 证据')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 核对实时定位、轨迹证据和原始记录')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('3. 完成后确认车辆服务状态恢复,问题不再命中当前告警')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准:车辆能解析到 VIN,实时/历史/RAW 证据可通过同一车辆服务查询')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准:车辆能解析到 VIN,实时、历史和原始记录可通过同一车辆服务查询')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E5%B7%A5%E5%8D%951&protocol=JT808')); }); @@ -4553,7 +4553,7 @@ test('copies priority queue notification digest on quality page', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('统一验收:来源恢复并持续 10 分钟,车辆服务可查到实时与历史证据')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 粤A汇总1 / VIN-DIGEST-001')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时:http://localhost:3000/#/realtime?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW:http://localhost:3000/#/history-query?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. [P1] 粤A汇总2 / 13307795426')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知规则:http://localhost:3000/#/notification-rules')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运维质量:http://localhost:3000/#/ops-quality')); @@ -4667,7 +4667,7 @@ test('copies notification text from regular quality issue row', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准:缺失字段恢复解析,统计、定位或里程依赖的核心字段可查询')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位:http://localhost:3000/#/realtime?keyword=VIN-ROW-NOTIFY&protocol=GB32960')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放:http://localhost:3000/#/history?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据:http://localhost:3000/#/history-query?keyword=VIN-ROW-NOTIFY&protocol=GB32960')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=VIN-ROW-NOTIFY&protocol=GB32960')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('includeFields=true')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程复核:http://localhost:3000/#/mileage?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=VIN-ROW-NOTIFY&protocol=GB32960')); @@ -4913,7 +4913,7 @@ test('opens same-day raw evidence from quality priority queue', async () => { issueType: 'LINK_GAP', severity: 'error', lastSeen: '2026-07-03 20:12:10', - detail: 'JT808 来源间断,需要核对 RAW' + detail: 'JT808 来源间断,需要核对原始记录' }], total: 1, limit: 20, @@ -4947,7 +4947,8 @@ test('opens same-day raw evidence from quality priority queue', async () => { render(); expect(await screen.findByText('处置优先队列')).toBeInTheDocument(); - fireEvent.click(screen.getByRole('button', { name: 'RAW证据' })); + const rawEvidenceButtons = screen.getAllByRole('button', { name: '原始记录' }); + fireEvent.click(rawEvidenceButtons[rawEvidenceButtons.length - 1]); await waitFor(() => { expect(window.location.hash.startsWith('#/history-query?')).toBe(true); @@ -5304,7 +5305,7 @@ test('opens same-day raw evidence from quality issue row', async () => { render(); expect(await screen.findByText('VIN-QUALITY-RAW')).toBeInTheDocument(); - fireEvent.click(screen.getByRole('button', { name: '核对 RAW' })); + fireEvent.click(screen.getByRole('button', { name: '核对原始' })); await waitFor(() => { expect(window.location.hash.startsWith('#/history-query?')).toBe(true); @@ -10276,7 +10277,7 @@ test('opens realtime status from quality issue row with source evidence', async render(); expect(await screen.findByText('13307795425')).toBeInTheDocument(); - fireEvent.click(screen.getByRole('button', { name: '实时状态' })); + fireEvent.click(screen.getAllByRole('button', { name: '实时状态' })[0]); await waitFor(() => { expect(window.location.hash).toBe('#/realtime?keyword=%E7%B2%A4AG18312&protocol=JT808');