通知紀錄(Project Alert Notifications)¶
功能群:專案管理|上層見 專案總覽頁(本頁由總覽頁動作列「通知紀錄」進入)
事實基準:2026-10-14 從 FE
src/views/project/ProjectAlertNotificationsView.vue、BEapi/task_alert/、app/task_alert/service/、infra/task_alert/、infra/readmodel/detection/scan_alert_context_query.py/support_end_query.py、migration2026-10-12-fr137-*/2026-10-13-fr137-*實查掃出
變更紀錄¶
| 日期 | FR | 變更 |
|---|---|---|
| 2026-10-14 | FR-137 | 新頁:專案內的條件通知寄送紀錄——SBOM 掃描新出現 CRITICAL/HIGH 的額外通知、資訊系統版本支援期剩 90/30/7 天的提醒,每封一列 |
1. 功能描述¶
通知紀錄是專案層的唯讀紀錄頁:系統依條件自動寄出的通知信,每寄一位收件人就在這裡留一列,讓專案成員查得到「寄了沒、寄給誰、為什麼寄、寄失敗的原因」。目前有兩種來源:
- SBOM 門檻通知:週期任務(或手動執行)發起的第三方元件弱點掃描完成後,與同一筆任務上一次成功的掃描比對,有新出現的 CRITICAL/HIGH 弱點就寄給任務設定的「高風險額外通知對象」(設定見任務配置 §5「任務週期」)。
- 支援期提醒:專案 SSP 資產清冊納入的資訊系統,若某版本的支援期結束日剩 90/30/7 天,寄給該 CRA 專案的管理者(版本與支援期在資訊系統盤點維護)。
本頁只看、不寄、不補寄——寄送失敗的不會自動重寄。
主要使用者:專案任一參與者(manager/auditor/reviewer/viewer 都看得到)。
角色速覽
| 角色 | 一句話 |
|---|---|
| 專案參與者 | 任一角色都可看本專案的通知紀錄 |
| 非參與者 | 看不到(回 404 GRC_404001) |
1.1 功能總覽(本頁全部功能)¶
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 返回專案總覽 | 回本專案總覽頁 | Header 左側箭頭 | — |
| 2 | 三張計數卡 | 本專案通知(全部)/已寄出/寄送失敗;失敗數 > 0 時字轉紅 | Header 下 | §6.2 |
| 3 | 寄送失敗提示 | 有失敗時顯示「有 N 封寄送失敗……請管理員到郵件設定啟用;失敗的不會自動補寄」 | 計數卡下(條件顯示) | §12 |
| 4 | 篩選:結果 | 全部/已寄出/寄送失敗 | 篩選列 | §6.2 |
| 5 | 篩選:期間 | 近 90 天(預設)/全部 | 篩選列 | §6.2 |
| 6 | 通知清單 | 分頁(25/50/100),新到舊;五欄:時間/掃描/新出現 high 以上/收件人/結果 | 主表 | §5 |
| 7 | 點列跳轉 | SBOM 列 → 該次掃描結果頁;支援期列 → 資訊系統盤點 | 主表整列 | UC-PAN-01 |
UC(§2)展開一條:查看通知並追到來源。兩種通知怎麼產生屬背景行為,見 §11。
2. Use Case¶
角色速覽(同 §1)。
UC-PAN-01 查看通知並追到來源¶
| 項目 | 內容 |
|---|---|
| 角色 | 專案參與者 |
| 前置條件 | 專案存在、使用者是本專案參與者;系統至少寄過一封本專案的條件通知 |
| 產出 / 後置條件 | 唯讀,不改任何資料 |
- 專案總覽頁動作列按「通知紀錄」→ 進本頁,預設顯示近 90 天、全部結果。
- 看計數卡;有寄送失敗時,提示條說明原因(多半是郵件伺服器未啟用)。
- 切換「結果」「期間」篩選,清單回第 1 頁重查。
- 點 SBOM 列 → 開 掃描結果頁(
source-scan-result,帶該次scan_uid);點支援期列 → 跳 資訊系統盤點。
3. 權限矩陣¶
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 進頁 | 路由登記為內頁(母頁 project-auditor-overview),需 grc.project.read |
@require_license("project")+@require_capability("grc.project.read") |
api/task_alert/routes/task_alert_notification_route.py |
| 讀清單 | 總覽頁的入口鈕不分角色都顯示 | assert_project_participant:任一參與者放行,非參與者 404 GRC_404001 |
app/task_alert/service/task_alert_notification_app_service.py::list_notifications |
守門分兩層:route 層驗「你有沒有讀專案的能力點」(主體域),app service 層驗「你是不是這個專案的人」(資源域)。
4. 狀態機與前置條件¶
本頁無狀態機。每列的「結果」三態:
| 結果 | 意思 | 何時寫入 |
|---|---|---|
pending(寄送中) |
已搶下紀錄列、信還沒寄完 | 寄信前先插列(唯一鍵擋重複) |
sent(已寄出) |
郵件服務回成功,sent_at 記時間 |
寄成功後 |
failed(寄送失敗) |
例外或郵件服務回「未寄出」;error 記原因 |
寄失敗後;不自動補寄 |
| 條件 | 效果 | 出處 |
|---|---|---|
| 專案 uid 查不到 | 404 GRC_404001 |
list_notifications |
| 非本專案參與者 | 404 GRC_404001(不洩漏專案存在) |
common/authz/project.py::assert_project_participant |
5. UI 設計¶
| 區 | 內容 |
|---|---|
| Header | 返回鈕+「通知紀錄 — 專案名」 |
| 說明句 | 「SBOM 掃描比上一次多出 CRITICAL/HIGH 弱點時寄給任務設定的額外通知對象;資訊系統版本支援期剩 90/30/7 天時寄給 CRA 專案管理者。每封都記在這裡。」 |
| 計數卡 ×3 | 全部/已寄出/寄送失敗(計數隨「期間」篩選,不隨「結果」篩選) |
| 篩選列 | 結果(SelectButton)、期間(SelectButton) |
| 主表 | 見下表;空狀態「目前沒有通知紀錄」 |
主表欄位:
| 欄 | SBOM 門檻通知列 | 支援期提醒列 |
|---|---|---|
| 時間 | sent_at(未寄成功則 created_at),YYYY-MM-DD HH:mm |
同左 |
| 掃描 | 第一行任務名;第二行「源碼包檔名 · 首次掃描」或「源碼包檔名 · vs 上一次」 | 第一行「支援期提醒 系統名 版本」;第二行「剩 N 天 · 支援期結束日」 |
| 新出現 high 以上 | CRITICAL n(紅)、HIGH n(橘)tag,數字為 0 的不顯示 |
「支援期」tag |
| 收件人 | 顯示名(無則信箱)+第二行信箱 | 同左 |
| 結果 | 三態 tag;失敗的 hover 顯示錯誤原因 | 同左 |
📸 截圖待補(亮色模式):含 SBOM 與支援期兩種列、有一列失敗的畫面
alert-notifications.png。
6. API 規格¶
Envelope:成功 {"status": true, "data": …}、失敗 {"error_code": "…", "msg": "…"}。
6.1 總清單¶
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 清單 | POST /project/{project_uid}/alert-notifications |
分頁取本專案通知紀錄+計數 | §6.2 |
| 專案名 | GET /grc/project/{project_uid} |
Header 顯示專案名(失敗不擋頁) | GAI-SD-02 |
6.2 [POST] /project/{project_uid}/alert-notifications¶
Request(TaskAlertNotificationListRequestSchema):
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| pager.page / pager.page_size | int | 否 | 預設 1/25 |
| filters.result | string|null | 否 | pending/sent/failed;null=全部 |
| filters.period | string|null | 否 | 90d=近 90 天;all 或 null=全部 |
Response data(TaskAlertNotificationListResponseSchema):{items[], paging, summary}。
items[]:
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | string | 紀錄 uid |
| object_type | string | scan_alert(SBOM 門檻)/support_end(支援期) |
| object_uid / stage | string|null | 支援期列:資訊系統版本 uid、階段 d90/d30/d7 |
| system_name / version / support_end_date | string|date|null | 支援期列:系統名、版本、支援期結束日(後端依 object_uid 補) |
| scan_uid / vs_scan_uid | string|null | SBOM 列:這次掃描、比對的上一次(首掃為 null) |
| job_name / source_filename | string|null | SBOM 列:任務名、源碼包檔名(後端依 scan_uid 補) |
| new_critical_count / new_high_count | int | 新出現的 CRITICAL/HIGH 數 |
| recipient_email / recipient_label | string | 收件信箱、顯示名 |
| result / error | string | 三態與失敗原因 |
| created_at / sent_at | datetime | 建立時間、寄出時間 |
summary:{total, sent, failed}——只套「期間」條件,不套「結果」條件。
排序:created_at DESC, id DESC。出處:api/task_alert/serializers/task_alert_notification.py、app/task_alert/service/task_alert_notification_app_service.py、infra/task_alert/repository/task_alert_notification_repo_impl.py::page。
7. 前端檔案地圖(compliance-manager-fe/)¶
| 檔案 | 角色 |
|---|---|
src/views/project/ProjectAlertNotificationsView.vue |
本頁(計數卡、篩選、清單、點列跳轉) |
src/views/project/ProjectAuditorOverview.vue |
動作列「通知紀錄」入口鈕 |
src/config/router/index.js |
route project-alert-notifications,path /project/projects/:id/alert-notifications |
src/config/api/api.js |
PROJECT_ALERT_NOTIFICATIONS |
src/config/locales/i18n/*/alert-notifications.json |
文案 |
8. 後端檔案地圖¶
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 清單 | api/task_alert/routes/task_alert_notification_route.py |
TaskAlertNotificationAppService.list_notifications |
TaskAlertNotificationDomainService → task_alert_notification_repo_impl;標籤補值 infra/readmodel/detection/scan_alert_context_query.py::scan_labels、support_end_query.py::labels |
| SBOM 門檻通知(寫入) | — | app/task_alert/service/scan_alert_app_service.py::handle,收件人解析 scan_alert_recipients.py |
jedi-source-scan 的 on_scan_completed 回呼(core/plugins/source_scan.py);新出現判定用套件 jedi_source_scan.plugin.scan_alert_diff.new_findings |
| 支援期提醒(寫入) | — | app/task_alert/service/support_end_reminder_service.py::run_once |
core/scheduler.py 的 task_recurrence_tick(每 5 分鐘);查詢 infra/readmodel/detection/support_end_query.py |
DI:di_containers/task_alert/task_alert_containers.py。
9. DB¶
9.1 資料表總清單¶
| 表 | 讀寫 | 說明 |
|---|---|---|
| compliance.task_alert_notifications | 讀(本頁)/寫(背景) | 一列一位收件人 |
| compliance.source_scans | 讀 | SBOM 列補任務名、源碼包名 |
| compliance.information_system_versions/information_systems | 讀 | 支援期列補系統名、版本、結束日 |
9.3 compliance.task_alert_notifications¶
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | varchar(36) | 對外識別 |
| tenant_id / org_unit_id | int | 租戶隔離(RLS FORCE) |
| project_id | bigint | → compliance.projects(CASCADE) |
| object_type | varchar(32) | scan_alert/support_end |
| job_execution_id | bigint|null | soft-ref → 發起掃描的任務 |
| scan_uid / vs_scan_uid | varchar(36)|null | soft-ref → 這次/比對的上一次掃描 |
| object_uid / stage | varchar|null | 支援期:版本 uid、d90/d30/d7 |
| new_critical_count / new_high_count | int | 新出現數 |
| recipient_email / recipient_label | varchar | 收件人 |
| result / error / sent_at | varchar/text/timestamptz | 三態、原因、寄出時間 |
唯一索引 uq_task_alert_notifications_object_recipient (object_type, COALESCE(scan_uid, object_uid), COALESCE(stage,''), recipient_email)——同一次掃描、同一支援期階段對同一信箱只會有一列,重跑不重寄。
10. 頁面邏輯與資料對應¶
進頁同時打專案明細與清單;篩選改變 → 回第 1 頁重查;換頁 → 帶新 page/page_size 重查。點列依 object_type 分流跳轉。專案名取不到只記 console,不擋清單。
11. 背景行為與外部依賴¶
| 類型 | 內容 |
|---|---|
| SBOM 門檻通知 | 任務發起的 SBOM 掃描完成(status='done')且任務有設額外通知對象時觸發:找同一筆任務上一次成功的掃描當基準(沒有就是首掃,全部弱點都算新出現),只取 CRITICAL/HIGH 的新出現弱點,有才寄。收件人=任務設定的專案參與人員(已退出專案的略過)+系統責任角色在 living SSP 上對應的人員信箱;同信箱只寄一次。信件語系繁中,列前 5 筆弱點 |
| 支援期提醒 | 掛在每 5 分鐘的 task_recurrence_tick 之後跑。以台北時區的「今天」算剩餘天數,剛好等於 90/30/7 天的版本才算;找出 SSP 資產清冊納入該系統、框架為 CRA、未結束的專案,寄給專案 manager。一天跑很多次,靠唯一索引每階段只寄一次 |
| 寄送 | 先插 pending 列搶位,搶不到=已寄過、跳過;郵件服務回 False(SMTP 未啟用)記 failed 不記成已寄;一位寄失敗不擋其他收件人 |
| Socket | 無;本頁進頁與篩選時打 API |
| 系統參數 | 郵件設定(系統設定 / 郵件設定);信中連結用 SYSTEM_URL |
12. 邊界情況與已知坑¶
- 寄失敗不補寄:
failed列不會被任何排程重試;修好郵件設定後,同一次掃描或同一支援期階段也不會再寄(唯一索引已佔位)。 - 計數卡不跟「結果」篩選:計數只套期間;選「寄送失敗」時清單只剩失敗列,但計數卡仍顯示三種數字,這是刻意的(卡片是總覽)。
- 同一批老弱點不重寄:比對基準是「同一筆任務上一次成功的掃描」,上次就有的弱點不算新出現;中間有失敗的掃描不算基準。
- 支援期提醒剛好落在 90/30/7 天才寄:當天系統沒在跑(停機超過一天)就錯過那一階段,不補寄。
- 非參與者回 404 不是 403:沿用專案讀取守門慣例,不洩漏專案是否存在。
13. 開發與驗證¶
- 跑起來:起 BE(api+worker,SBOM 掃描在 worker 跑)+FE;帳號見
.env/部署文件 - 導航:專案總覽頁 → 動作列「通知紀錄」
- 前置資料:一筆綁「第三方元件弱點掃描」的檢測工具任務,週期設定加一位額外通知對象 → 手動執行兩次(第一次全部算新出現);支援期:資訊系統版本的結束日設成今天+30 天,且該系統在 CRA 專案的 SSP 資產清冊內,等下一個 5 分鐘
- 相關:任務配置「任務週期」、我的任務 §11.4、FR-137