feat(platform): add notification rules workspace

This commit is contained in:
lingniu
2026-07-04 16:27:37 +08:00
parent 33da8ba5a4
commit c060317fb0
8 changed files with 287 additions and 9 deletions

View File

@@ -3183,6 +3183,82 @@ test('uses backend quality notification plan on quality page', async () => {
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/quality/notification-plan?limit=20'), undefined);
});
test('renders notification rules as a standalone operations page', async () => {
window.history.replaceState(null, '', '/#/notification-rules');
const writeText = vi.fn(() => Promise.resolve());
Object.defineProperty(navigator, 'clipboard', {
configurable: true,
value: { writeText }
});
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
const path = String(input);
if (path.includes('/api/ops/health')) {
return {
ok: true,
json: async () => ({
data: {
linkHealth: [],
kafkaLag: 0,
activeConnections: 1000,
capacityFindings: [],
redisOnlineKeys: 800,
tdengineWritable: true,
mysqlWritable: true,
runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-rules-test' }
},
traceId: 'trace-test',
timestamp: 1783094400000
})
} as Response;
}
if (path.includes('/api/quality/notification-plan')) {
return {
ok: true,
json: async () => ({
data: {
summary: { issueVehicleCount: 5, issueRecordCount: 8, errorCount: 2, warningCount: 6, protocols: [], issueTypes: [{ name: 'NO_SOURCE', count: 3 }] },
rules: [
{ issueType: 'NO_SOURCE', title: '无来源规则', level: 'P0', owner: '平台接入', trigger: '车辆无任何来源证据', notify: '立即通知接入运维', sla: '30 分钟确认', count: 3 },
{ issueType: 'FIELD_MISSING', title: '字段缺失规则', level: 'P1', owner: '协议解析', trigger: '核心字段缺失', notify: '进入每日治理清单', sla: '当日闭环', count: 1 }
],
policies: [
{ name: 'P0 实时中断', target: '接入运维 + 业务责任人', channel: '邮件 / 企业微信', condition: '无来源、VIN 缺失、存储不可写', escalationMinutes: 30, acceptanceCriteria: '来源恢复并持续 10 分钟' },
{ name: 'P1 数据质量', target: '协议解析 + 数据治理', channel: '每日汇总邮件', condition: '字段缺失、链路间断', escalationMinutes: 120, acceptanceCriteria: '核心字段恢复解析' }
],
priorityIssues: [],
activeRuleCount: 2,
p0RuleCount: 1
},
traceId: 'trace-test',
timestamp: 1783094400000
})
} as Response;
}
return {
ok: true,
json: async () => ({
data: { items: [], total: 0, limit: 20, offset: 0 },
traceId: 'trace-test',
timestamp: 1783094400000
})
} as Response;
});
render(<App />);
expect(await screen.findByRole('heading', { name: '通知规则' })).toBeInTheDocument();
expect(screen.getByText('无来源规则')).toBeInTheDocument();
expect(screen.getByText('车辆无任何来源证据')).toBeInTheDocument();
expect(screen.getByText('P0 实时中断')).toBeInTheDocument();
expect(screen.getByText('接入运维 + 业务责任人')).toBeInTheDocument();
expect(screen.getByText('platform-rules-test')).toBeInTheDocument();
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/quality/notification-plan?limit=50'), undefined);
fireEvent.click(screen.getByRole('button', { name: '复制通知规则Runbook' }));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【通知规则Runbook】'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('无来源规则 / P0 / 平台接入'));
});
test('copies notification text from quality priority queue', async () => {
window.history.replaceState(null, '', '/#/quality');
const writeText = vi.fn(() => Promise.resolve());