From 6be88526699cd8b3d2167140ac1a546af8df9518 Mon Sep 17 00:00:00 2001 From: hardy <2545433047@qq.com> Date: Fri, 16 Oct 2026 16:54:18 +0800 Subject: [PATCH 01/11] =?UTF-8?q?feat(TAP-13052):=20=C2=A74.1=20navigation?= =?UTF-8?q?,=20alarm=20events=20and=20notification=20records?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Main menu: Operations > Alarm Events, Messages & Notifications > Notification Records / Settings, System > Operation Audit; old /notification/* routes redirect, NotifyLayout removed - Bell popover: unread in-app alarm notifications (99+), license reminder for admins, open-to-read, mark-all-read with scope confirmation - Alarm events: stats cards, URL-synced filters, occurrence count, object type, delivery result, detail drawer, close hint - Notification records: aggregated by alarm event, All/Unread/Failed tabs, filters, batch/recipient delivery details, single/batch read & unread - Notification setting: in-app toggles retired, link to alarm settings - zh-CN / zh-TW / en texts Co-Authored-By: Claude Opus 5.5 --- apps/daas/src/i18n/langs/en.js | 115 ++++ apps/daas/src/i18n/langs/zh-CN.js | 111 ++++ apps/daas/src/i18n/langs/zh-TW.js | 111 ++++ apps/daas/src/layouts/NotifyLayout.vue | 81 --- apps/daas/src/router/menu.ts | 18 + apps/daas/src/router/routes.ts | 42 +- .../views/notification/AlarmEventDrawer.vue | 213 ++++++ .../src/views/notification/AlarmEvents.vue | 382 +++++++++++ .../views/notification/DeliverySummaryTag.vue | 52 ++ .../notification/NotificationPopover.vue | 628 +++++------------- .../notification/NotificationRecordDrawer.vue | 245 +++++++ .../notification/NotificationRecords.vue | 359 ++++++++++ .../src/views/setting/NotificationSetting.vue | 26 +- packages/api/src/core/alarm.ts | 96 +++ packages/api/src/core/notification.ts | 89 +++ 15 files changed, 2000 insertions(+), 568 deletions(-) delete mode 100644 apps/daas/src/layouts/NotifyLayout.vue create mode 100644 apps/daas/src/views/notification/AlarmEventDrawer.vue create mode 100644 apps/daas/src/views/notification/AlarmEvents.vue create mode 100644 apps/daas/src/views/notification/DeliverySummaryTag.vue create mode 100644 apps/daas/src/views/notification/NotificationRecordDrawer.vue create mode 100644 apps/daas/src/views/notification/NotificationRecords.vue diff --git a/apps/daas/src/i18n/langs/en.js b/apps/daas/src/i18n/langs/en.js index 8279070d0a..a1a8e71a4c 100644 --- a/apps/daas/src/i18n/langs/en.js +++ b/apps/daas/src/i18n/langs/en.js @@ -2738,4 +2738,119 @@ export default { license_expired_today: 'License expires today', license_expired_days: 'License has expired {val} days', license_expire_days: 'License expires in {val} days', + // TAP-13052 告警事件 / 通知记录 / 操作审计 + page_title_ops_center: 'Operations', + page_title_message_center: 'Messages & Notifications', + page_title_alarm_events: 'Alarm Events', + page_title_notification_records: 'Notification Records', + page_title_operation_audit: 'Operation Audit', + notification_modular_alarm: 'alarm event', + notification_operation_unread: 'marked as unread', + notification_operation_close: 'closed', + alarm_event_stats_alarming: 'Alarming', + alarm_event_stats_severe: 'Critical/Emergency', + alarm_event_stats_today_new: 'New Today', + alarm_event_stats_today_recovered: 'Recovered Today', + alarm_event_object_type: 'Object Type', + alarm_object_type_TASK: 'Task', + alarm_object_type_NODE: 'Node', + alarm_object_type_CONNECTION: 'Connection', + alarm_object_type_AGENT: 'Agent', + alarm_object_type_INSPECT: 'Validation Task', + alarm_object_type_API_SERVER: 'API Server', + alarm_event_notified: 'Notified', + alarm_event_notified_NONE: 'Not notified', + alarm_event_notified_NOTIFIED: 'Notified', + alarm_event_notified_PARTIAL_FAILED: 'Partially failed', + alarm_event_notified_FAILED: 'Failed', + alarm_event_agent: 'Agent', + alarm_event_keyword_placeholder: 'Task/object name or ID', + alarm_event_occurrence_count: 'Occurrences', + alarm_event_notification_result: 'Notification Result', + alarm_event_go_task: 'Go to Task', + alarm_event_close_hint: + 'Closing only stops tracking this alarm; it does not mean the problem has recovered', + alarm_event_close_confirm: + 'Closing stops tracking the selected alarms. It does not mean the problem has recovered, and it will be recorded in the operation audit. Close them?', + alarm_event_detail: 'Alarm Details', + alarm_event_overview: 'Overview', + alarm_event_id: 'Alarm ID', + alarm_event_type: 'Alarm Type', + alarm_event_duration: 'Duration', + alarm_event_minutes: '{0} min', + alarm_event_hours: '{0} h', + alarm_event_recovery_time: 'Recovered At', + alarm_event_close_time: 'Closed At', + alarm_event_current_value: 'Current Value', + alarm_event_threshold: 'Threshold', + alarm_event_related_object: 'Related Object', + alarm_event_object_deleted: 'Object deleted', + alarm_event_task: 'Task', + alarm_event_node: 'Node', + alarm_event_open_monitor: 'Open Task Monitor', + alarm_event_open_setting: 'Open Alarm Settings', + alarm_event_notification_overview: 'Notification Overview', + alarm_event_latest_notify: 'Last notified', + alarm_event_view_notifications: 'View notification records', + delivery_channel_system: 'In-app', + delivery_channel_email: 'Email', + delivery_channel_sms: 'SMS', + delivery_channel_wechat: 'WeChat', + delivery_channel_webhook: 'Webhook', + delivery_status_success: 'All succeeded', + delivery_status_partial_failed: 'Partially failed', + delivery_status_failed: 'Failed', + delivery_status_skipped: 'Skipped', + delivery_status_none: 'No delivery record', + delivery_recipient_success: 'Succeeded', + delivery_recipient_failed: 'Failed', + delivery_recipient_skipped: 'Skipped', + delivery_skip_no_receivers: 'No receivers configured', + delivery_skip_blacklist: 'Blacklisted', + delivery_skip_rate_limit: 'Sending limit exceeded', + notification_record_tab_all: 'All', + notification_record_tab_unread: 'Unread', + notification_record_tab_failed: 'Failed', + notification_record_notify_time: 'Notified At', + notification_record_channel: 'Channel', + notification_record_delivery_status: 'Delivery Status', + notification_record_read_state: 'Read Status', + notification_record_read: 'Read', + notification_record_unread: 'Unread', + notification_record_recipient_placeholder: 'Recipient (email/phone)', + notification_record_keyword_placeholder: 'Task/object name or ID', + notification_record_content: 'Content', + notification_record_legacy: 'Historical record without delivery details', + notification_record_delivery: 'Delivery', + notification_record_latest_notify: 'Last Notified', + notification_record_mark_read: 'Mark as read', + notification_record_mark_unread: 'Mark as unread', + notification_record_mark_all_read: 'Mark all as read', + notification_record_confirm_read: + '{0} notifications under the current filter will be marked as read. Continue?', + notification_record_confirm_unread: + '{0} selected notifications will be marked as unread. Continue?', + notification_record_truncated: + '(Up to 1000 per operation; repeat for the rest)', + notification_record_nothing_to_mark: 'Nothing to update', + notification_record_detail: 'Notification Details', + notification_record_read_at: 'Read at', + notification_record_basic: 'Basic Information', + notification_record_view_alarm: 'View Alarm Event', + notification_record_deliveries: 'Deliveries', + notification_record_no_delivery: + 'No delivery details (historical record or no external channel)', + notification_record_occurrence_snapshot: '{0} occurrences when sent', + notification_record_recipient: 'Recipient', + notification_record_status: 'Status', + notification_record_sent_at: 'Sent At', + notification_record_reason: 'Failure/skip reason', + notification_record_smtp_hint: + 'Email "succeeded" means the mail server accepted it, not that the recipient opened it. Recipients of the same send share one result; addresses explicitly rejected by the server are marked failed separately.', + notification_bell_title: 'Unread ({0})', + notification_platform_license: + 'License expires in {0} days, please renew in time', + notification_view_all_records: 'View all notification records', + notification_setting_retired_tip: + 'In-app system notifications are retired. Task errors, validation errors and Agent status are now notified by alarm rules; configure channels and recipients in Alarm Settings. Existing email notifications below still apply.', } diff --git a/apps/daas/src/i18n/langs/zh-CN.js b/apps/daas/src/i18n/langs/zh-CN.js index 37cb73a3d7..81b8f5805f 100644 --- a/apps/daas/src/i18n/langs/zh-CN.js +++ b/apps/daas/src/i18n/langs/zh-CN.js @@ -2594,4 +2594,115 @@ export default { license_expired_today: 'License 今日到期', license_expired_days: 'License 已过期 {val} 天', license_expire_days: 'License 还有 {val} 天到期', + // TAP-13052 告警事件 / 通知记录 / 操作审计 + page_title_ops_center: '运维中心', + page_title_message_center: '消息与通知', + page_title_alarm_events: '告警事件', + page_title_notification_records: '通知记录', + page_title_operation_audit: '操作审计', + notification_modular_alarm: '告警事件', + notification_operation_unread: '标记为未读', + notification_operation_close: '关闭了', + alarm_event_stats_alarming: '告警中', + alarm_event_stats_severe: '严重/紧急', + alarm_event_stats_today_new: '今日新增', + alarm_event_stats_today_recovered: '今日已恢复', + alarm_event_object_type: '对象类型', + alarm_object_type_TASK: '任务', + alarm_object_type_NODE: '节点', + alarm_object_type_CONNECTION: '连接', + alarm_object_type_AGENT: '引擎', + alarm_object_type_INSPECT: '校验任务', + alarm_object_type_API_SERVER: 'API Server', + alarm_event_notified: '是否已通知', + alarm_event_notified_NONE: '未通知', + alarm_event_notified_NOTIFIED: '已通知', + alarm_event_notified_PARTIAL_FAILED: '部分失败', + alarm_event_notified_FAILED: '全部失败', + alarm_event_agent: '所属 Agent', + alarm_event_keyword_placeholder: '任务/对象名称或 ID', + alarm_event_occurrence_count: '发生次数', + alarm_event_notification_result: '通知结果', + alarm_event_go_task: '前往任务', + alarm_event_close_hint: '关闭只表示结束对该告警的跟踪,不代表问题已恢复', + alarm_event_close_confirm: + '关闭后将结束对所选告警的跟踪,不代表问题已恢复,且会记录到操作审计。确定关闭吗?', + alarm_event_detail: '告警详情', + alarm_event_overview: '告警概览', + alarm_event_id: '告警 ID', + alarm_event_type: '告警类型', + alarm_event_duration: '持续时长', + alarm_event_minutes: '{0} 分钟', + alarm_event_hours: '{0} 小时', + alarm_event_recovery_time: '恢复时间', + alarm_event_close_time: '关闭时间', + alarm_event_current_value: '当前值', + alarm_event_threshold: '阈值', + alarm_event_related_object: '关联对象', + alarm_event_object_deleted: '对象已删除', + alarm_event_task: '任务', + alarm_event_node: '节点', + alarm_event_open_monitor: '打开任务监控', + alarm_event_open_setting: '打开告警设置', + alarm_event_notification_overview: '通知概览', + alarm_event_latest_notify: '最近通知时间', + alarm_event_view_notifications: '查看全部通知记录', + delivery_channel_system: '站内', + delivery_channel_email: '邮件', + delivery_channel_sms: '短信', + delivery_channel_wechat: '微信', + delivery_channel_webhook: 'Webhook', + delivery_status_success: '全部成功', + delivery_status_partial_failed: '部分失败', + delivery_status_failed: '全部失败', + delivery_status_skipped: '已跳过', + delivery_status_none: '无投递记录', + delivery_recipient_success: '成功', + delivery_recipient_failed: '失败', + delivery_recipient_skipped: '跳过', + delivery_skip_no_receivers: '未配置接收人', + delivery_skip_blacklist: '在黑名单中', + delivery_skip_rate_limit: '超出发送限额', + notification_record_tab_all: '全部', + notification_record_tab_unread: '未读', + notification_record_tab_failed: '发送失败', + notification_record_notify_time: '通知时间', + notification_record_channel: '通知渠道', + notification_record_delivery_status: '投递状态', + notification_record_read_state: '阅读状态', + notification_record_read: '已读', + notification_record_unread: '未读', + notification_record_recipient_placeholder: '收件人(邮箱/手机号)', + notification_record_keyword_placeholder: '任务/对象名称或 ID', + notification_record_content: '通知内容', + notification_record_legacy: '历史记录,无投递明细', + notification_record_delivery: '投递结果', + notification_record_latest_notify: '最近通知时间', + notification_record_mark_read: '标为已读', + notification_record_mark_unread: '标为未读', + notification_record_mark_all_read: '全部标为已读', + notification_record_confirm_read: + '将把当前筛选条件下的 {0} 条通知标为已读,确定吗?', + notification_record_confirm_unread: '将把选中的 {0} 条通知标为未读,确定吗?', + notification_record_truncated: '(单次最多处理 1000 条,其余请再次操作)', + notification_record_nothing_to_mark: '没有需要处理的通知', + notification_record_detail: '通知详情', + notification_record_read_at: '读取时间', + notification_record_basic: '基本信息', + notification_record_view_alarm: '查看告警事件', + notification_record_deliveries: '投递明细', + notification_record_no_delivery: + '该通知没有投递明细(历史记录或未配置外部渠道)', + notification_record_occurrence_snapshot: '发送时已发生 {0} 次', + notification_record_recipient: '接收人', + notification_record_status: '状态', + notification_record_sent_at: '发送时间', + notification_record_reason: '失败/跳过原因', + notification_record_smtp_hint: + '邮件“成功”表示邮件服务器已接收,不代表收件人已打开;同一次发送的收件人共用同一结果,服务器明确拒收的地址会单独标记失败。', + notification_bell_title: '未读通知({0})', + notification_platform_license: 'License 还有 {0} 天到期,请及时续期', + notification_view_all_records: '查看全部通知记录', + notification_setting_retired_tip: + '站内系统通知已下线:任务异常、校验异常、Agent 状态等提醒统一由告警规则产生,请在告警设置中配置渠道与接收人;下方已配置的邮件提醒仍然生效。', } diff --git a/apps/daas/src/i18n/langs/zh-TW.js b/apps/daas/src/i18n/langs/zh-TW.js index 5e39cbcac2..7b4c3dc342 100644 --- a/apps/daas/src/i18n/langs/zh-TW.js +++ b/apps/daas/src/i18n/langs/zh-TW.js @@ -2578,4 +2578,115 @@ export default { license_expired_today: 'License 今日到期', license_expired_days: 'License 已過期 {val} 天', license_expire_days: 'License 還有 {val} 天到期', + // TAP-13052 告警事件 / 通知记录 / 操作审计 + page_title_ops_center: '運維中心', + page_title_message_center: '訊息與通知', + page_title_alarm_events: '告警事件', + page_title_notification_records: '通知記錄', + page_title_operation_audit: '操作審計', + notification_modular_alarm: '告警事件', + notification_operation_unread: '標記為未讀', + notification_operation_close: '關閉了', + alarm_event_stats_alarming: '告警中', + alarm_event_stats_severe: '嚴重/緊急', + alarm_event_stats_today_new: '今日新增', + alarm_event_stats_today_recovered: '今日已恢復', + alarm_event_object_type: '對象類型', + alarm_object_type_TASK: '任務', + alarm_object_type_NODE: '節點', + alarm_object_type_CONNECTION: '連接', + alarm_object_type_AGENT: '引擎', + alarm_object_type_INSPECT: '校驗任務', + alarm_object_type_API_SERVER: 'API Server', + alarm_event_notified: '是否已通知', + alarm_event_notified_NONE: '未通知', + alarm_event_notified_NOTIFIED: '已通知', + alarm_event_notified_PARTIAL_FAILED: '部分失敗', + alarm_event_notified_FAILED: '全部失敗', + alarm_event_agent: '所屬 Agent', + alarm_event_keyword_placeholder: '任務/對象名稱或 ID', + alarm_event_occurrence_count: '發生次數', + alarm_event_notification_result: '通知結果', + alarm_event_go_task: '前往任務', + alarm_event_close_hint: '關閉只表示結束對該告警的追蹤,不代表問題已恢復', + alarm_event_close_confirm: + '關閉後將結束對所選告警的追蹤,不代表問題已恢復,且會記錄到操作審計。確定關閉嗎?', + alarm_event_detail: '告警詳情', + alarm_event_overview: '告警概覽', + alarm_event_id: '告警 ID', + alarm_event_type: '告警類型', + alarm_event_duration: '持續時長', + alarm_event_minutes: '{0} 分鐘', + alarm_event_hours: '{0} 小時', + alarm_event_recovery_time: '恢復時間', + alarm_event_close_time: '關閉時間', + alarm_event_current_value: '當前值', + alarm_event_threshold: '閾值', + alarm_event_related_object: '關聯對象', + alarm_event_object_deleted: '對象已刪除', + alarm_event_task: '任務', + alarm_event_node: '節點', + alarm_event_open_monitor: '打開任務監控', + alarm_event_open_setting: '打開告警設置', + alarm_event_notification_overview: '通知概覽', + alarm_event_latest_notify: '最近通知時間', + alarm_event_view_notifications: '查看全部通知記錄', + delivery_channel_system: '站內', + delivery_channel_email: '郵件', + delivery_channel_sms: '簡訊', + delivery_channel_wechat: '微信', + delivery_channel_webhook: 'Webhook', + delivery_status_success: '全部成功', + delivery_status_partial_failed: '部分失敗', + delivery_status_failed: '全部失敗', + delivery_status_skipped: '已跳過', + delivery_status_none: '無投遞記錄', + delivery_recipient_success: '成功', + delivery_recipient_failed: '失敗', + delivery_recipient_skipped: '跳過', + delivery_skip_no_receivers: '未配置接收人', + delivery_skip_blacklist: '在黑名單中', + delivery_skip_rate_limit: '超出發送限額', + notification_record_tab_all: '全部', + notification_record_tab_unread: '未讀', + notification_record_tab_failed: '發送失敗', + notification_record_notify_time: '通知時間', + notification_record_channel: '通知渠道', + notification_record_delivery_status: '投遞狀態', + notification_record_read_state: '閱讀狀態', + notification_record_read: '已讀', + notification_record_unread: '未讀', + notification_record_recipient_placeholder: '收件人(郵箱/手機號)', + notification_record_keyword_placeholder: '任務/對象名稱或 ID', + notification_record_content: '通知內容', + notification_record_legacy: '歷史記錄,無投遞明細', + notification_record_delivery: '投遞結果', + notification_record_latest_notify: '最近通知時間', + notification_record_mark_read: '標為已讀', + notification_record_mark_unread: '標為未讀', + notification_record_mark_all_read: '全部標為已讀', + notification_record_confirm_read: + '將把當前篩選條件下的 {0} 條通知標為已讀,確定嗎?', + notification_record_confirm_unread: '將把選中的 {0} 條通知標為未讀,確定嗎?', + notification_record_truncated: '(單次最多處理 1000 條,其餘請再次操作)', + notification_record_nothing_to_mark: '沒有需要處理的通知', + notification_record_detail: '通知詳情', + notification_record_read_at: '讀取時間', + notification_record_basic: '基本資訊', + notification_record_view_alarm: '查看告警事件', + notification_record_deliveries: '投遞明細', + notification_record_no_delivery: + '該通知沒有投遞明細(歷史記錄或未配置外部渠道)', + notification_record_occurrence_snapshot: '發送時已發生 {0} 次', + notification_record_recipient: '接收人', + notification_record_status: '狀態', + notification_record_sent_at: '發送時間', + notification_record_reason: '失敗/跳過原因', + notification_record_smtp_hint: + '郵件「成功」表示郵件伺服器已接收,不代表收件人已開啟;同一次發送的收件人共用同一結果,伺服器明確拒收的地址會單獨標記失敗。', + notification_bell_title: '未讀通知({0})', + notification_platform_license: 'License 還有 {0} 天到期,請及時續期', + notification_view_all_records: '查看全部通知記錄', + notification_setting_retired_tip: + '站內系統通知已下線:任務異常、校驗異常、Agent 狀態等提醒統一由告警規則產生,請在告警設置中配置渠道與接收人;下方已配置的郵件提醒仍然生效。', } diff --git a/apps/daas/src/layouts/NotifyLayout.vue b/apps/daas/src/layouts/NotifyLayout.vue deleted file mode 100644 index f635f66897..0000000000 --- a/apps/daas/src/layouts/NotifyLayout.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - diff --git a/apps/daas/src/router/menu.ts b/apps/daas/src/router/menu.ts index 69ca6b8932..2517c594f4 100644 --- a/apps/daas/src/router/menu.ts +++ b/apps/daas/src/router/menu.ts @@ -56,6 +56,23 @@ export const MENU = [ parent: 'dataVerification', }, + { + name: 'opsCenter', + label: 'page_title_ops_center', + icon: 'warning', + group: 'bottom', + children: [{ name: 'alarmEvents' }], + }, + { + name: 'messageCenter', + label: 'page_title_message_center', + icon: 'notice-system-notice', + group: 'bottom', + children: [ + { name: 'notificationRecords' }, + { name: 'notificationSetting' }, + ], + }, { name: 'advancedFeatures', label: 'page_title_advanced_features', @@ -125,6 +142,7 @@ export const MENU = [ hidden: isCommunity, }, { name: 'externalStorage', code: 'v2_external-storage_menu' }, + { name: 'operationAudit' }, ], }, ] diff --git a/apps/daas/src/router/routes.ts b/apps/daas/src/router/routes.ts index 06a4ad726a..0b3a205d75 100644 --- a/apps/daas/src/router/routes.ts +++ b/apps/daas/src/router/routes.ts @@ -1,5 +1,4 @@ import Layout from '@/layouts/Layout.vue' -import NotifyLayout from '@/layouts/NotifyLayout.vue' import SettingLayout from '@/layouts/SettingLayout.vue' const FunctionForm = () => import('@/views/function/Form.vue') @@ -60,8 +59,6 @@ const ExternalStorageList = () => import('@tap/business/src/views/external-storage/List.vue') // 告警通知 -const AlarmNotification = () => - import('@tap/business/src/views/setting/AlarmNotification.vue') // 数据加密 const DataEncryptionList = () => @@ -848,6 +845,25 @@ export const routes = [ hideTitle: true, }, }, + /* ---------- 告警事件 / 通知记录 / 操作审计(TAP-13052 §4.1) ----------*/ + { + path: '/alarm-events', + name: 'alarmEvents', + component: () => import('@/views/notification/AlarmEvents.vue'), + meta: { title: 'page_title_alarm_events', hideTitle: true }, + }, + { + path: '/notification-records', + name: 'notificationRecords', + component: () => import('@/views/notification/NotificationRecords.vue'), + meta: { title: 'page_title_notification_records', hideTitle: true }, + }, + { + path: '/operation-audit', + name: 'operationAudit', + component: () => import('@/views/notification/UserNotification.vue'), + meta: { title: 'page_title_operation_audit', hideTitle: true }, + }, /* ---------- 用户管理 ----------*/ { path: '/user', @@ -969,39 +985,31 @@ export const routes = [ }, ], }, - /* ---------- 通知模块 ----------*/ + /* ---------- 通知模块(旧入口,重定向至 §4.1 新入口) ----------*/ { path: '/notification', name: 'notification', - redirect: '/notification/systemNotification', - component: NotifyLayout, - meta: { - title: 'page_title_back_menu', - }, + redirect: { name: 'notificationRecords' }, children: [ { path: 'systemNotification', name: 'systemNotification', - component: () => import('@/views/notification/SystemNotification.vue'), - meta: { title: 'notify_system_notice' }, + redirect: { name: 'notificationRecords' }, }, { path: 'userNotification', name: 'userNotification', - component: () => import('@/views/notification/UserNotification.vue'), - meta: { title: 'daas_notification_center_yonghucaozuo' }, + redirect: { name: 'operationAudit' }, }, { path: 'alarmNotification', name: 'alarmNotification', - component: AlarmNotification, - meta: { title: 'daas_notification_alarmnotification_gaojingtongzhi' }, + redirect: { name: 'notificationRecords' }, }, { path: 'systemAlarm', name: 'systemAlarm', - component: () => import('@/views/notification/SystemAlarm.vue'), - meta: { title: 'daas_notification_center_xitonggaojing' }, + redirect: { name: 'alarmEvents' }, }, ], }, diff --git a/apps/daas/src/views/notification/AlarmEventDrawer.vue b/apps/daas/src/views/notification/AlarmEventDrawer.vue new file mode 100644 index 0000000000..8feb501af5 --- /dev/null +++ b/apps/daas/src/views/notification/AlarmEventDrawer.vue @@ -0,0 +1,213 @@ + + + diff --git a/apps/daas/src/views/notification/AlarmEvents.vue b/apps/daas/src/views/notification/AlarmEvents.vue new file mode 100644 index 0000000000..686d14b1ba --- /dev/null +++ b/apps/daas/src/views/notification/AlarmEvents.vue @@ -0,0 +1,382 @@ + + + + + diff --git a/apps/daas/src/views/notification/DeliverySummaryTag.vue b/apps/daas/src/views/notification/DeliverySummaryTag.vue new file mode 100644 index 0000000000..dd24371f97 --- /dev/null +++ b/apps/daas/src/views/notification/DeliverySummaryTag.vue @@ -0,0 +1,52 @@ + + + diff --git a/apps/daas/src/views/notification/NotificationPopover.vue b/apps/daas/src/views/notification/NotificationPopover.vue index 708eded63d..f7ca9faff8 100644 --- a/apps/daas/src/views/notification/NotificationPopover.vue +++ b/apps/daas/src/views/notification/NotificationPopover.vue @@ -1,230 +1,106 @@ - - - diff --git a/apps/daas/src/views/notification/NotificationRecordDrawer.vue b/apps/daas/src/views/notification/NotificationRecordDrawer.vue new file mode 100644 index 0000000000..57e3b7a4f9 --- /dev/null +++ b/apps/daas/src/views/notification/NotificationRecordDrawer.vue @@ -0,0 +1,245 @@ + + + diff --git a/apps/daas/src/views/notification/NotificationRecords.vue b/apps/daas/src/views/notification/NotificationRecords.vue new file mode 100644 index 0000000000..fcfc5762a7 --- /dev/null +++ b/apps/daas/src/views/notification/NotificationRecords.vue @@ -0,0 +1,359 @@ + + + + + diff --git a/apps/daas/src/views/setting/NotificationSetting.vue b/apps/daas/src/views/setting/NotificationSetting.vue index 629073735b..4adff733de 100644 --- a/apps/daas/src/views/setting/NotificationSetting.vue +++ b/apps/daas/src/views/setting/NotificationSetting.vue @@ -70,6 +70,20 @@ export default { type="info" show-icon /> + + {{ $t('alarm_event_open_setting') }} +
  • {{ notificationMAP[item.label] }} - {{ - $t('notify_system_notice') - }} +
    {{ @@ -162,9 +174,7 @@ export default {
    • {{ notificationMAP[item.label] }} - {{ - $t('notify_system_notice') - }} +
    @@ -177,9 +187,7 @@ export default {
    • {{ notificationMAP[item.label] }} - {{ - $t('notify_system_notice') - }} +
    diff --git a/packages/api/src/core/alarm.ts b/packages/api/src/core/alarm.ts index 432006495e..7fcd4a8d4d 100644 --- a/packages/api/src/core/alarm.ts +++ b/packages/api/src/core/alarm.ts @@ -229,3 +229,99 @@ export function fetchAlarmReceiverPreview(taskId: string) { { params: { taskId } }, ) } + +/* ---------- 告警事件(TAP-13052) ---------- */ + +export type AlarmObjectType = + | 'TASK' + | 'NODE' + | 'CONNECTION' + | 'AGENT' + | 'INSPECT' + | 'API_SERVER' + +export interface DeliveryChannelSummary { + total: number + success: number + failed: number + skipped: number +} + +export interface DeliverySummary { + status: 'NONE' | 'SUCCESS' | 'PARTIAL_FAILED' | 'FAILED' | 'SKIPPED' + total: number + success: number + failed: number + skipped: number + channels: Notify[] + byChannel: Partial> + latestDeliveryTime?: string +} + +export interface AlarmEvent { + id: string + level: string + status: 'ING' | 'RECOVER' | 'CLOESE' + metric: string + summary: string + objectType: AlarmObjectType + objectId?: string + objectName?: string + objectDeleted: boolean + taskId?: string + taskName?: string + syncType?: string + nodeId?: string + nodeName?: string + agentId?: string + connectionId?: string + inspectId?: string + serverId?: string + apiId?: string + currentValue?: number + threshold?: number + firstOccurrenceTime?: string + lastOccurrenceTime?: string + occurrenceCount?: number + recoveryTime?: string + closeTime?: string + closeBy?: string + lastNotifyTime?: string + deliverySummary?: DeliverySummary +} + +export interface AlarmEventStats { + alarming: number + severe: number + todayNew: number + todayRecovered: number +} + +/** 数组参数以逗号拼接,服务端按列表绑定 */ +export function joinListParams(params: Record = {}) { + const result: Record = {} + Object.entries(params).forEach(([key, value]) => { + if (value === undefined || value === null || value === '') return + if (Array.isArray(value)) { + if (value.length) result[key] = value.join(',') + return + } + result[key] = value + }) + return result +} + +export function listAlarmEvents(params: Record) { + return requestClient.get<{ total: number; items: AlarmEvent[] }>( + '/api/alarms', + { params: joinListParams(params) }, + ) +} + +export function getAlarmEvent(id: string) { + return requestClient.get(`/api/alarms/${id}`) +} + +export function getAlarmEventStats() { + return requestClient.get('/api/alarms/stats') +} diff --git a/packages/api/src/core/notification.ts b/packages/api/src/core/notification.ts index 3fd07df85a..f1924991ff 100644 --- a/packages/api/src/core/notification.ts +++ b/packages/api/src/core/notification.ts @@ -1,4 +1,5 @@ import { isPlainObj } from '@tap/shared' +import { joinListParams, type DeliverySummary } from './alarm' import { requestClient, type CountFetchResult, @@ -84,3 +85,91 @@ export function updateNotification(where: any, attributes: any) { attributes, ) } + +/* ---------- 通知记录(TAP-13052) ---------- */ + +const RECORD_URL = '/api/notifications' + +export interface NotificationRecord { + id: string + alarmEventId?: string + legacy: boolean + messageIds: string[] + title: string + level: string + metric?: string + alarmStatus?: string + objectType?: string + taskId?: string + taskName?: string + firstOccurrenceTime?: string + lastOccurrenceTime?: string + occurrenceCount?: number + notificationCount: number + unread: boolean + latestNotifyTime?: string + deliverySummary?: DeliverySummary +} + +export interface NotificationRecipient { + channel: string + address?: string + status: 'SUCCESS' | 'FAILED' | 'SKIPPED' + sentAt?: string + failureMessage?: string + skipReason?: string +} + +export interface NotificationBatch { + batchId: string + time?: string + occurrenceCountSnapshot?: number + recipients: NotificationRecipient[] +} + +export interface NotificationRecordDetail extends NotificationRecord { + content: string + readAt?: string + batches: NotificationBatch[] +} + +export function listNotificationRecords(params: Record) { + return requestClient.get<{ total: number; items: NotificationRecord[] }>( + RECORD_URL, + { params: joinListParams(params) }, + ) +} + +export function getNotificationRecord(id: string) { + return requestClient.get( + `${RECORD_URL}/${encodeURIComponent(id)}`, + ) +} + +export function getUnreadNotificationCount() { + return requestClient.get(`${RECORD_URL}/unread-count`) +} + +export function previewNotificationReadState( + params: Record, + read: boolean, +) { + return requestClient.get<{ + count: number + ids: string[] + truncated: boolean + }>(`${RECORD_URL}/read-state/preview`, { + params: { ...joinListParams(params), read }, + }) +} + +export function updateNotificationReadState(ids: string[], read: boolean) { + return requestClient.patch(`${RECORD_URL}/read-state`, { ids, read }) +} + +export function updateOneNotificationReadState(id: string, read: boolean) { + return requestClient.patch( + `${RECORD_URL}/${encodeURIComponent(id)}/read-state`, + { read }, + ) +} From 9850d26b309aea95da78b8130540df661aab13c0 Mon Sep 17 00:00:00 2001 From: hardy <2545433047@qq.com> Date: Tue, 20 Oct 2026 09:16:24 +0800 Subject: [PATCH 02/11] fix(TAP-13052): show cleared alarm events and rerun archive in notification views Co-Authored-By: Claude Opus 5.5 --- apps/daas/src/i18n/langs/en.js | 3 +++ apps/daas/src/i18n/langs/zh-CN.js | 2 ++ apps/daas/src/i18n/langs/zh-TW.js | 2 ++ .../src/views/notification/AlarmEventDrawer.vue | 9 +++++++-- .../views/notification/NotificationRecordDrawer.vue | 13 +++++++++++-- .../src/views/notification/NotificationRecords.vue | 3 +++ packages/api/src/core/alarm.ts | 2 ++ packages/api/src/core/notification.ts | 2 ++ 8 files changed, 32 insertions(+), 4 deletions(-) diff --git a/apps/daas/src/i18n/langs/en.js b/apps/daas/src/i18n/langs/en.js index a1a8e71a4c..2d1bb01579 100644 --- a/apps/daas/src/i18n/langs/en.js +++ b/apps/daas/src/i18n/langs/en.js @@ -2821,6 +2821,9 @@ export default { notification_record_keyword_placeholder: 'Task/object name or ID', notification_record_content: 'Content', notification_record_legacy: 'Historical record without delivery details', + notification_record_alarm_missing: + 'Alarm event was cleared when the task was rerun', + alarm_event_archived: 'Archived when the task was rerun', notification_record_delivery: 'Delivery', notification_record_latest_notify: 'Last Notified', notification_record_mark_read: 'Mark as read', diff --git a/apps/daas/src/i18n/langs/zh-CN.js b/apps/daas/src/i18n/langs/zh-CN.js index 81b8f5805f..5d4c56a054 100644 --- a/apps/daas/src/i18n/langs/zh-CN.js +++ b/apps/daas/src/i18n/langs/zh-CN.js @@ -2676,6 +2676,8 @@ export default { notification_record_keyword_placeholder: '任务/对象名称或 ID', notification_record_content: '通知内容', notification_record_legacy: '历史记录,无投递明细', + notification_record_alarm_missing: '告警事件已在任务重新运行时清除', + alarm_event_archived: '任务重新运行时归档', notification_record_delivery: '投递结果', notification_record_latest_notify: '最近通知时间', notification_record_mark_read: '标为已读', diff --git a/apps/daas/src/i18n/langs/zh-TW.js b/apps/daas/src/i18n/langs/zh-TW.js index 7b4c3dc342..37aaa23cab 100644 --- a/apps/daas/src/i18n/langs/zh-TW.js +++ b/apps/daas/src/i18n/langs/zh-TW.js @@ -2660,6 +2660,8 @@ export default { notification_record_keyword_placeholder: '任務/對象名稱或 ID', notification_record_content: '通知內容', notification_record_legacy: '歷史記錄,無投遞明細', + notification_record_alarm_missing: '告警事件已在任務重新運行時清除', + alarm_event_archived: '任務重新運行時歸檔', notification_record_delivery: '投遞結果', notification_record_latest_notify: '最近通知時間', notification_record_mark_read: '標為已讀', diff --git a/apps/daas/src/views/notification/AlarmEventDrawer.vue b/apps/daas/src/views/notification/AlarmEventDrawer.vue index 8feb501af5..b2bfe1d099 100644 --- a/apps/daas/src/views/notification/AlarmEventDrawer.vue +++ b/apps/daas/src/views/notification/AlarmEventDrawer.vue @@ -1,5 +1,6 @@ @@ -117,12 +121,7 @@ onMounted(() => { @show="loadRecords" >