feat(platform): frame notifications as customer closure
This commit is contained in:
@@ -63,7 +63,7 @@ const navGroups = [
|
||||
description: '断链、离线、定位异常和通知升级',
|
||||
items: [
|
||||
{ itemKey: 'alert-events', text: '告警事件', icon: <IconHistogram /> },
|
||||
{ itemKey: 'notification-rules', text: '通知规则', icon: <IconBell /> }
|
||||
{ itemKey: 'notification-rules', text: '通知闭环', icon: <IconBell /> }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -773,7 +773,7 @@ export function NotificationRules() {
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="通知规则" description="集中管理告警触发条件、通知对象、升级时间和验收标准,让断链与数据质量问题可追踪、可通知、可闭环" />
|
||||
<PageHeader title="通知闭环" description="面向客户交付告警影响、通知对象、升级时限和恢复验收;规则配置只作为闭环背后的证据。" />
|
||||
<Card bordered loading={loading} className="vp-notification-customer-board" bodyStyle={{ padding: 0 }}>
|
||||
<div className="vp-notification-customer-summary">
|
||||
<Space wrap>
|
||||
|
||||
@@ -600,7 +600,7 @@ function notificationHandoffText({
|
||||
...issueLines,
|
||||
'',
|
||||
`告警事件:${qualityShareURL()}`,
|
||||
`通知规则:${appURL(buildAppHash({ page: 'notification-rules' }))}`,
|
||||
`通知闭环:${appURL(buildAppHash({ page: 'notification-rules' }))}`,
|
||||
`运维质量:${appURL(buildAppHash({ page: 'ops-quality' }))}`
|
||||
].join('\n');
|
||||
}
|
||||
@@ -654,7 +654,7 @@ function alertBusinessImpactText({
|
||||
`容量风险:${storageOk ? '存储可写' : '存储异常'};Kafka Lag ${formatLag(health?.kafkaLag)};容量发现 ${capacityCount.toLocaleString()} 项`,
|
||||
`建议动作:${severity === '高风险' ? '立即通知责任团队并按 P0/P1 队列闭环' : severity === '需关注' ? '纳入当日治理,持续观察来源覆盖和字段完整性' : '保持监控'}`,
|
||||
`告警事件:${qualityShareURL()}`,
|
||||
`通知规则:${appURL(buildAppHash({ page: 'notification-rules' }))}`,
|
||||
`通知闭环:${appURL(buildAppHash({ page: 'notification-rules' }))}`,
|
||||
`运维质量:${appURL(buildAppHash({ page: 'ops-quality' }))}`
|
||||
].join('\n');
|
||||
}
|
||||
@@ -715,7 +715,7 @@ function alertCustomerDecisionText({
|
||||
`告警事件:${qualityShareURL()}`,
|
||||
`实时监控:${appURL(buildAppHash({ page: 'realtime', keyword: focusLookup?.key || filters.keyword || '', protocol: focusIssue?.protocol || filters.protocol }))}`,
|
||||
`轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: focusLookup?.key || filters.keyword || '', protocol: focusIssue?.protocol || filters.protocol }))}`,
|
||||
`通知规则:${appURL(buildAppHash({ page: 'notification-rules' }))}`
|
||||
`通知闭环:${appURL(buildAppHash({ page: 'notification-rules' }))}`
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
@@ -869,7 +869,7 @@ export function Quality({
|
||||
value: primaryPolicy?.target || '待配置',
|
||||
detail: primaryPolicy ? `${primaryPolicy.name} / ${primaryPolicy.channel}` : '缺少通知策略,建议先配置责任团队。',
|
||||
color: primaryPolicy ? 'blue' as const : 'orange' as const,
|
||||
action: '通知规则',
|
||||
action: '通知闭环',
|
||||
onClick: () => onOpenNotificationRules?.()
|
||||
},
|
||||
{
|
||||
@@ -1226,7 +1226,7 @@ export function Quality({
|
||||
<Space wrap>
|
||||
<Button size="small" theme="solid" type="primary" onClick={copyAlertCustomerDecision}>复制告警决策</Button>
|
||||
<Button size="small" disabled={priorityRows.length === 0} onClick={copyPriorityDigest}>复制通知汇总</Button>
|
||||
{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>通知规则</Button> : null}
|
||||
{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>通知闭环</Button> : null}
|
||||
</Space>
|
||||
</div>
|
||||
<div className="vp-alert-decision-grid">
|
||||
@@ -1310,7 +1310,7 @@ export function Quality({
|
||||
<Space wrap>
|
||||
<Button size="small" theme="solid" type="primary" onClick={copyNotificationHandoff}>复制通知交接包</Button>
|
||||
<Button size="small" onClick={copyPolicyRunbook}>复制 SLA 策略</Button>
|
||||
{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>维护通知规则</Button> : null}
|
||||
{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>维护通知闭环</Button> : null}
|
||||
</Space>
|
||||
</div>
|
||||
<div className="vp-alert-sla-grid">
|
||||
@@ -1350,7 +1350,7 @@ export function Quality({
|
||||
<Space wrap>
|
||||
<Button size="small" theme="solid" type="primary" onClick={copyNotificationHandoff}>复制交接包</Button>
|
||||
<Button size="small" onClick={copyPolicyRunbook}>复制通知策略</Button>
|
||||
{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>维护通知规则</Button> : null}
|
||||
{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>维护通知闭环</Button> : null}
|
||||
</Space>
|
||||
</div>
|
||||
<div className="vp-alert-channel-grid">
|
||||
@@ -1426,7 +1426,7 @@ export function Quality({
|
||||
<Space wrap>
|
||||
<Button size="small" theme="solid" type="primary" disabled={priorityRows.length === 0} onClick={copyNotificationHandoff}>复制中心交接包</Button>
|
||||
<Button size="small" onClick={copyBusinessImpact}>复制影响摘要</Button>
|
||||
{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>管理通知规则</Button> : null}
|
||||
{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>管理通知闭环</Button> : null}
|
||||
</Space>
|
||||
</div>
|
||||
<div className="vp-alert-center-grid">
|
||||
@@ -1757,7 +1757,7 @@ export function Quality({
|
||||
/>
|
||||
</Card>
|
||||
<div className="vp-alert-ops-grid">
|
||||
<Card bordered title={<Space><span>告警触发规则</span>{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>管理通知规则</Button> : null}</Space>}>
|
||||
<Card bordered title={<Space><span>告警触发规则</span>{onOpenNotificationRules ? <Button size="small" onClick={onOpenNotificationRules}>管理通知闭环</Button> : null}</Space>}>
|
||||
<Table<AlertRuleRow>
|
||||
pagination={false}
|
||||
dataSource={rules}
|
||||
|
||||
@@ -4279,7 +4279,8 @@ test('renders notification rules as a standalone operations page', async () => {
|
||||
|
||||
render(<App />);
|
||||
|
||||
expect(await screen.findByRole('heading', { name: '通知规则' })).toBeInTheDocument();
|
||||
expect(await screen.findByRole('heading', { name: '通知闭环' })).toBeInTheDocument();
|
||||
expect(screen.getByText('面向客户交付告警影响、通知对象、升级时限和恢复验收;规则配置只作为闭环背后的证据。')).toBeInTheDocument();
|
||||
expect(screen.getByText('客户告警闭环')).toBeInTheDocument();
|
||||
expect(screen.getByText('从告警触发到通知升级,再到验收恢复')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '客户告警闭环 发现问题 告警队列' })).toBeInTheDocument();
|
||||
@@ -4845,7 +4846,7 @@ test('copies priority queue notification digest on quality page', async () => {
|
||||
expect(screen.getByText('把告警翻译成客户能理解的通知、时限和恢复标准')).toBeInTheDocument();
|
||||
expect(screen.getByText('每一条告警都先回答影响哪些车、通知谁、多久升级、恢复后如何验收;链路、协议和原始记录只作为证据附件。')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '客户通知SLA 客户影响范围 影响摘要' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '客户通知SLA 通知负责人 通知规则' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '客户通知SLA 通知负责人 通知闭环' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '客户通知SLA 升级时限 复制策略' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '客户通知SLA 恢复验收 交接包' })).toBeInTheDocument();
|
||||
expect(screen.getAllByText('接入运维 + 业务责任人').length).toBeGreaterThanOrEqual(1);
|
||||
@@ -4864,7 +4865,7 @@ test('copies priority queue notification digest on quality page', async () => {
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时:http://localhost:3000/#/realtime?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE'));
|
||||
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/#/notification-rules'));
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运维质量:http://localhost:3000/#/ops-quality'));
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '复制优先队列通知汇总' }));
|
||||
|
||||
Reference in New Issue
Block a user