專案

一般

配置概況

Feature #1422

是由 陳國瑋 於 26 天 前更新

## 現行 Connector Status Summary 互動(UI-DEC-017/DEC-038) 

 - 五個狀態摘要項目只呈現分類名稱與數量,不可點擊、不進入鍵盤 Tab 順序,也不開 Drawer、套 filter、導頁或送出 request。 
 - 單支 detail 仍由 Connector preview card 開啟;完整清單仍由「查看全部」開啟。Backend 不新增 bucket-to-filter mapping、click API、URL 欄位或 WebSocket event。細節見 #1423 UI-DEC-017/#1424 DEC-038。 

 ## 現行 Connector Status Summary 零值呈現(UI-DEC-016/DEC-037) 

 - Backend 的 REST/WebSocket contract 固定回五個 bucket(允許 count = 0)。`total > 0` 時 Frontend 固定顯示五項,不因即時數字更新增減或重排;`total = 0` 時隱藏 summary 並顯示 Connector Empty State。 
 - 0 是有效數值,不得當成 Loading、Error、Unknown 或 null;本規則只控制唯讀呈現,不新增傳輸或作業邏輯。細節見 #1423 UI-DEC-016/#1424 DEC-037。 

 ## 現行 Connector Status Summary Mapping(UI-DEC-015/DEC-036) 

 - 固定五類與順序:`NEEDS_ATTENTION`「需留意」→ `CHARGING`「充電進行中」→ `WAITING`「準備/排隊」→ `AVAILABLE`「可用」→ `OTHER`「其他狀態」;Backend 由上而下命中第一類,Frontend 使用固定中文 label。 
 - Unknown/資料不足歸 NEEDS_ATTENTION,NOT_PLUGGED 歸 OTHER;`NEEDS_ATTENTION.count = abnormalTotal`。細部條件與唯讀邊界見 #1423 UI-DEC-015。 

 ## 現行 Connector Status Summary 計數(UI-DEC-014/DEC-035) 

 - Backend 依同一份 authoritative snapshot,讓每個 Connector 恰好歸入一個唯讀 `displayBucket`;所有 bucket count 無重複、無遺漏且加總等於 `total`。Frontend 不從 Top-8 preview 重算。 
 - Card/Drawer 仍分層顯示 OCPP connection、Connector runtime 與 Queue 原始狀態;bucket codes/labels/mapping/precedence 已由 #1423 UI-DEC-015 確認。 

 ## 現行 Connector Preview Payload(UI-DEC-013/DEC-034) 

 - Backend 對完整 Connector 集合完成排序與異常分類後,REST bootstrap 與 `CONNECTOR_LIVE_BLOCK_UPDATED` 固定回傳同一份最多 Top-8 `items`,以及 `total`、`abnormalTotal`;每筆帶 Backend authoritative `isAbnormal`、`severity`、`priorityReason`。 
 - Frontend 不傳 viewport、不重排或重判異常,只依 breakpoint 顯示前 8/6/4 筆,並用 Backend totals 與 visible slice 計算純顯示用隱藏總數/隱藏異常數。完整清單仍由 Drawer 呼叫 REST search API。 

 ## 現行「充電進行中」KPI 顯示(KPI-DEC-010) 

 - 第三張 KPI 只顯示 `{activeSessionCount} Sessions`,小字為「依未結束的充電 Session 計算」。 
 - KPI 不重複顯示 `currentPowerKw`;即時功率只留在 WebSocket-owned「案場即時負載」區塊。 

 ## 現行 V1 Freshness 設定(DEC-033) 

 - Stale cutoff 由 Backend 依資料來源設定全系統預設值,所有案場共用;Frontend 不硬編碼秒數,A17 只作 default 驗證。 
 - V1 不提供 per-building override,也不新增設定資料表、管理 API/UI、設定權限或 audit trail;特殊案場需求未來另開 Feature。 

 ## 現行 V1 REST 更新週期(DEC-032) 

 **狀態:已確認(2026-09-04,Ken)** 

 - 所有非即時區塊共用同一個更新週期:頁面首次載入、Dashboard 開啟期間每 5 分鐘一次,以及使用者手動刷新。 
 - 每個更新週期只呼叫一次 `GET /api/dashboard/overview`,整包取得 KPI、需留意事項、群組容量/Slots、30 日用量、今日已完成充電量、結算流程、上月電費與 Live Load 歷史 series;不得為不同區塊建立 5/15/60 分鐘等多套 timer 或各自發 request。 
 - Frontend 以同一份 response `generatedAt` 更新 REST-owned sections,並沿用各區塊既有的 COMPLETE/PARTIAL/ERROR、null/0、lastSuccessfulAt 與同 building cache 顯示規則。 
 - 「Connector 即時狀態」與「案場即時負載」仍由 DEC-031 的單一 WebSocket 負責即時更新。Overview 首載可提供兩區初始畫面;WebSocket 接手後,後續 5 分鐘/手動 REST refresh 不得覆寫兩區的 live state,也不得在 socket 斷線時冒充備援更新。 
 - Header 必須把「即時連線狀態」與「一般資料最後更新」分開呈現。手動刷新只更新一般 REST 資料,不重建 WebSocket;重新載入整頁才重新嘗試 WebSocket。 
 - 本決策取代下方及相關子票中帳務 15 分鐘、群組低頻刷新、區塊各自 timer 等較早描述;不改變任何資料公式、充電/Queue/帳務作業邏輯或 Dashboard 唯讀邊界。 

 ## 現行 V1 資料取得方式(DEC-031) 

 > 本節取代本票下方較早關於 Dashboard 全區 WebSocket、REST polling fallback、capped exponential backoff、sequence/gap recovery、固定 5 秒 coalescing及 2 秒 propagation SLO 的傳輸描述;資料公式、權限、所有案場共用及唯讀規則不變。 

 - `GET /api/dashboard/overview` 負責整張 Dashboard 的首次 snapshot 與所有非即時資料。 
 - 另建立一條案場層級 Dashboard WebSocket,只更新「Connector 即時狀態」與「案場即時負載」。 
 - Connector 即時狀態採整個區塊 replacement,payload 為 Backend 排序/分類後的 Top-8 + `total`/`abnormalTotal`,FE 依 breakpoint 顯示 8/6/4;案場即時負載只更新目前功率與 open buckets,完整歷史 series 仍由 REST 取得。 
 - 六張 KPI、需留意事項、群組容量/Slots、30 日用量、今日已完成充電量、結算流程與上月電費不訂閱 Dashboard WebSocket。 
 - WebSocket 失敗或中斷時,Header 與兩個即時區塊顯示提示;保留最後成功資料及時間並標示已停止更新,沒有資料時顯示 `—`。 
 - V1 不做 REST polling fallback、custom backoff、jitter、背景自動重連、sequence/gap recovery或專用 Retry workflow;重新載入頁面才重新連線。 
 - WebSocket 斷線不影響其他 REST 區塊。 
 - Backend 需開發最小事件集合:`DASHBOARD_LIVE_SNAPSHOT`、`CONNECTOR_LIVE_BLOCK_UPDATED`、`LIVE_LOAD_UPDATED`。 
 - Frontend 需提供 connected、首次連線失敗與連線後中斷三種 fixture;驗證兩區提示、最後資料保留、最後更新時間,以及未啟動 REST fallback。 
 - 非即時 REST 區塊的更新方式已由 DEC-032 確認為頁面首載、共用 5 分鐘 Overview cycle 與手動刷新;詳細 supersession 與驗收條件見 #1424。 

 ## 背景 

 Branch Admin 的 `/dashboard` 目前是空白頁。既有 `DashboardStatsCard` 只有硬編碼假資料且未掛載;現有 `GET /api/connector/dashboard` 只能提供 Connector 總數、Available、Charging、Faulted、公用與私人數量,尚不足以支援案場營運判斷。 

 本需求建立所有案場共用的 Backend Admin 營運 Dashboard;A17 僅為第一個驗證與導入案場。每個 EMS Branch 的 V1 自動顯示該 Branch 唯一一個 enabled Building;API、WebSocket、UI、freshness policy 與資料模型不得硬編碼 A17,且資料仍須以 Backend 解析的 `buildingId` 明確隔離。 

 > Mockup 內所有數字均為假資料。A17 production baseline 必須在實作前以 read-only query 驗證,不得將 mock 數字作為驗收基準。 

 ## 多案場適用原則 

 - Dashboard 功能適用所有案場,A17 僅作為首個 rollout/baseline 驗證站點。 
 - V1 的每個 EMS Branch 必須且只能有一個 enabled Building;Dashboard 自動使用該 Building,不提供案場 selector 或跨案場彙總。 
 - Frontend 呼叫 Overview/ticket 及建立 WebSocket 時不傳 `buildingId`;Backend 解析唯一 enabled Building。Overview response 與 WebSocket event 仍明確帶 `buildingId`,供 scope 驗證與資料隔離。 
 - enabled Building 為零筆或多筆時,Dashboard 必須顯示設定錯誤;Backend 不可任選第一筆、跨 Building 加總或回傳全零資料。 
 - Freshness/stale cutoff 由 Backend 依資料來源設定全系統預設值,不由 FE 或 A17-specific code 決定;V1 不提供 per-building override 或設定介面。 
 - A17 Phase 0 的數據只用於驗證通用方案的公式、效能及預設值,不能成為其他案場的固定規則。 

 ## 產品目標 

 讓 Site Admin、Operations、Finance 與 Engineer 在 10 秒內回答: 

 - 案場現在是否正常? 
 - 哪些 Charge Point 離線、哪些 Connector 故障? 
 - 目前有多少充電工作階段、全案場即時充電功率是多少? 
 - 輪充/離峰隊列是否已有明確 BLOCKED 狀態? 
 - 今日使用量與最近一期結算流程是否有待處理項目? 
 - Dashboard 資料是否仍然新鮮、可被信任? 

 Dashboard V1 以唯讀總覽與 deep link 為主,不在首頁直接執行 Remote Start/Stop、Reset、Modbus 或 Engineer command。 

 ## 完整需求文件 

 建立 Issue 後將附上: 

 - `a17-ems-dashboard-requirements.pdf`:17 頁完整需求說明書 
 - Repository source:`documents/dashboard-mockup/a17-dashboard-requirements.md` 
 - UI mockup:`documents/dashboard-mockup/a17-operations-dashboard.html` 

 ## V1 Scope 

 - 「需留意事項」唯讀異常摘要與 deep link;只納入既有明確狀態(ATT-DEC-001,詳見 #1463) 
 - Charge Point OCPP connection 與 runtime 摘要 
 - Connector 完整狀態統計與即時狀態 grid 
 - Active sessions、案場即時總功率與負載趨勢(保留「即時/今日/7 日」三種檢視;軸線定義詳見 #1462) 
 - 各 Charge Group 的目前功率、群組自身 contract capacity、occupied/reserved/available slots 
 - Queue waiting/blocked/最久等待時間摘要(唯讀資訊) 
 - 今日用電與近 30 日 kWh/sessions 趨勢 
 - 結算流程區塊沿用 attachment #1110 四步驟面板;假數字改接正式 API,資料期間由 Backend 依最近結算資料回傳(BIL-DEC-001/002,詳見 #1460) 
 - Loading、Empty、Error、Stale、Partial、403 等 UI states 
 - Desktop/Tablet/Mobile responsive 
 - SS3A 權限沿用既有 `dashboard` function 與 `administrator`、`power_user`、`association`、`dealer` 四個 role;不新增 Dashboard role/function(#1424 DEC-030) 

 ## V1 Scope Boundary 

 - Dashboard 是唯讀營運資訊頁,只查詢、彙整及呈現既有系統狀態。 
 - V1 的案場名稱是 Backend-resolved 唯讀識別;不新增 site switcher、多案場權限矩陣或跨案場 aggregate。 
 - V1 不以 SUM(group.contractCapacity) 推算案場契約容量、利用率或剩餘容量;案場即時總功率與各群組自身容量分開呈現(LOAD-DEC-001,詳見 #1462)。 
 - Queue 顯示既有 source、status、priority、statusReason、waitingSince/waiting duration;`BLOCKED` 沿用既有狀態呈現。 
 - V1 不新增 Queue SLA、等待逾時判定、門檻 override、Queue 設定頁或 Rotation scheduler-health 規則。 
 - Connector summary 使用固定五項互斥 `displayBucket` 且分項合計等於 total;`total > 0` 時包含零值的五項全部顯示,`total = 0` 時改顯示 Empty State。五個摘要項目為不可點擊的純資訊;detail 與完整清單分別由 State。Connector preview card/「查看全部」負責。Connector preview 固定 Cards,使用單一 Top-8 payload,Desktop/Tablet/Mobile 顯示 8/6/4 張。完整清單 Drawer 採單一 responsive list;不提供 bucket click API、viewport-specific viewport-specific API、Grid/Table toggle 或顯示偏好保存。 
 - Dashboard 開發不得改變 queue lifecycle、priority、slot allocation、rotation dispatch、OCPP command 或任何充電作業邏輯。 
 - 「需留意事項」只彙整既有明確狀態;V1 不新增 Queue mismatch、waiting timeout/SLA、scheduler-health、容量告警、派工或自動修復規則(詳見 #1463)。 
 - 若未來確有 SLA/scheduler monitoring 需求,須另案確認;本次不先建立實作 ticket。 

 ## 開發工作拆分 

 ### Backend / API 

 - [ ] 新增 typed `GET /api/dashboard/overview`;request 不含 `buildingId`,response 回傳 Backend-resolved `buildingId`、案場名稱與 timezone 
 - [ ] 建立 `DashboardController`、`DashboardService`、「需留意事項」與各區塊 DTO 
 - [ ] 建立 current-building scope resolver,驗證 enabled Building 恰好一筆,並讓全部 Dashboard query 共用該 scope;零筆/多筆回明確 configuration error,不得沿用 `findDefault()` 的任選第一筆行為 
 - [ ] 聚合 CP connection/runtime、Connector 狀態、active transaction 
 - [ ] 依 #1423 UI-DEC-006/007/013~016 建立單一 Connector preview read model:對完整集合排序/分類後回最多 Top-8、`total`、`abnormalTotal` 與 item `displayBucket`/`isAbnormal`/`severity`/`priorityReason`;依 NEEDS_ATTENTION → CHARGING → WAITING → AVAILABLE → OTHER precedence,以同一 snapshot 產生互斥 bucket counts,保證每個 Connector 恰好一類、總和等於 total 且 NEEDS_ATTENTION count = abnormalTotal。REST bootstrap 與 `CONNECTOR_LIVE_BLOCK_UPDATED` 固定回五個 bucket(包含 count = 0)並共用 service/contract,不接受 viewport/device/requestedLimit,也不透過 WS 傳完整 inventory 
 - [ ] 依 #1486 KPI-DEC-002 聚合 Backend-resolved current building 的 enabled CP:persisted OCPP ONLINE 為分子、enabled total 為分母,另回 OFFLINE/UNKNOWN/disabled;不得以 lastHeartbeat 另建 Dashboard cutoff,狀態 transition 透過 NETWORK_HEALTH_UPDATED replacement 更新 
 - [ ] 依 #1486 KPI-DEC-003 聚合 Connector 可服務數:分母為 enabled CP 下的 Connector,分子同時要求 parent CP ONLINE 與固定 runtime status allowlist;FAULTED/UNAVAILABLE/unknown/parent CP 非 ONLINE 排除,並由 CONNECTOR_OVERVIEW_UPDATED replacement 更新 
 - [ ] 依 #1486 KPI-DEC-004/010 聚合「充電進行中」:每個 Connector 解析最多一筆 canonical ACTIVE transaction,忽略較舊孤兒 ACTIVE;隨共用 Overview REST 更新,不回傳 KPI 次要功率,也不新增交易 timeout 或修正 
 - [ ] 依 #1486 KPI-DEC-005 聚合「等待供電」:主數字只計 Queue ELIGIBLE 的 distinct Connector;NOT_PLUGGED 另列且排除已屬 ELIGIBLE 的 Connector,BLOCKED 留在需留意事項,其餘 Queue 狀態排除;由 CONNECTOR_OVERVIEW_UPDATED replacement 更新,且不新增 Queue timeout、SLA 或狀態轉換 
 - [ ] 建立案場 currentPowerKw 的 latest power query、W/kW normalization 與 stale cutoff;不得從群組容量推算案場契約容量/利用率/餘裕 
 - [ ] 依 #1462 LOAD-DEC-003 建立 Live Load 歷史 read model:即時 60 分鐘/1 分鐘 bucket、今日 00:00 至現在/15 分鐘 bucket、含今天 7 個案場日曆日/1 小時 bucket;以案場 timezone 計算 time-weighted averagePowerKw,不可直接加總或平均 raw MeterValue rows 
 - [ ] 依 #1462 LOAD-DEC-004 由 REST 提供完整 Live Load series;LIVE_LOAD_UPDATED 只推 currentPowerKw、三種 open buckets 與跨界 finalized bucket,不重送完整歷史 
 - [ ] 依 #1462 LOAD-DEC-005 分離 bucketState(OPEN/FINALIZED)與 dataStatus(COMPLETE/PARTIAL/ERROR);fresh 非充電為 0,正在充電但 Power 超過 cutoff 仍未知時整個 bucket 回 null + PARTIAL,不插值或回部分合計 
 - [ ] 依既有 SlotCalculator 語意逐組產生 Charge Group capacity summary;群組 contract capacity 不得加總成案場契約容量 KPI 
 - [ ] 統計既有 queue waiting/blocked/statusReason/waiting duration;不得新增 Queue SLA、timeout 或狀態轉換規則 
 - [ ] 依 #1463 ATT-DEC-001 彙整 CP OFFLINE、Connector FAULTED、Queue BLOCKED、明確 source STALE/PARTIAL/ERROR 與 #1460 結算未完成/異常;不得新增 mismatch 或推測性判斷;Live Load 只依 current STALE/PARTIAL/ERROR 建立一個 category,歷史 bucket gap 不納入 
 - [ ] 依 #1463 ATT-DEC-003 回傳非零 categoryCount 與各類 distinct affectedCount;不加總成設備數,不做跨類 root-cause 去重 
 - [ ] 依 #1463 ATT-DEC-004 以固定 allowlist 順序回傳全部非零 categories;不依 severity/count/時間動態排序 
 - [ ] 依 #1461 USG-DEC-001~009 建立固定最近 30 個案場日曆日(含今天)的 kWh/Sessions usage trend;有效充電量只讀取符合條件的 COMPLETED energy_consumed,Sessions 只計算 COMPLETED 且正電量交易,兩者依 building timezone 的交易開始日歸屬且跨日不拆分;Backend 回傳平均每次充電量與固定 30 daily buckets,成功無使用回 0、資料未知回 null/PARTIAL,整段全零回 COMPLETE + EMPTY,子查詢失敗回 source error metadata;不提供 7/90 日、自訂範圍或時間使用率 
 - [ ] 依 #1486 KPI-DEC-006 讓「今日已完成充電量」直接重用 #1461 `date = rangeEndDate` 的 daily bucket;不新增 active-energy/MeterValue 暫估 query、第二套今日聚合欄位或 Usage/Energy WebSocket 
 - [ ] 依 #1460 BIL-DEC-001/002 建立四步驟結算流程的 read-only aggregation/DTO,並回傳 periodYear/periodMonth/timezone;不得執行帳務 mutation 或改變既有月結排程 
 - [ ] 依 #1486 KPI-DEC-001 建立「上月電費」read model:以 building timezone 取上一完整日曆月、加總該案場 `e_bill_settlement.cost_predict`;paid/pending 均納入,全部 calculation COMPLETE 才回 amount,否則回 null 與完成筆數;不顯示部分合計、不退回前期、不新增帳務 WebSocket 
 - [ ] 依 #1486 KPI-DEC-007/#1424 DEC-028,讓各 KPI source 回傳 value(可為 null)、COMPLETE/PARTIAL/ERROR、freshness、updatedAt 及必要的 lastSuccessfulAt;子查詢失敗不得回 0,且只影響相依 Card,REST/WS 語意一致 
 - [ ] KPI-DEC-008 為純 Frontend 排版責任;Backend 不新增 viewport-specific API 欄位、回傳順序或 WebSocket event 分支,同一份 snapshot 支援所有寬度 
 - [ ] KPI-DEC-009 不新增 deep-link URL、target、keyboard 或 ARIA API 欄位;Card 點擊不觸發 Backend mutation,固定目的地與 accessible copy 由 Frontend 維護 
 - [ ] 沿用既有 SS3A `dashboard` function,新增 Overview/ticket API mapping;以冪等 migration 確認 `administrator`、`power_user`、`association`、`dealer` 的既有 role-function mapping,不新增 role/function、不在 Java hardcode role name,並在部署重啟後驗證授權快取已載入 
 - [ ] 補 OpenAPI `@Schema`、method/DTO 欄位註解與測試 
 - [ ] 在 A17 資料量下完成 EXPLAIN 與 P95 驗證 

 ### Frontend 

 - [ ] 重新實作 `react/branch/app/(dashboard)/dashboard/page.tsx` 
 - [ ] Dashboard menu/route 依 Backend 回傳的既有 `dashboard` function 顯示;未授權狀態不得先載入或短暫顯示 Dashboard 資料,且不可只靠 Frontend 隱藏作為授權 
 - [ ] OpenAPI refresh 與 React Query overview hook 
 - [ ] Header、「需留意事項」、KPI、LiveLoad、ChargeGroup、ConnectorGrid;LiveLoad 只顯示案場即時總功率/趨勢/freshness,不顯示推算的案場契約容量、利用率或剩餘容量 
 - [ ] Header/Sidebar 顯示 Overview 回傳的唯讀案場名稱,不提供 selector;Overview request、ticket request 與 WebSocket URL 不送 `buildingId` 
 - [ ] Backend 回報 enabled Building 零筆/多筆時顯示整頁「案場設定異常」,隱藏所有營運數字且不得 fallback 到 0 或 fixture 
 - [ ] Charge Point KPI 顯示「online / enabled 台啟用」,OFFLINE/UNKNOWN 與 disabled 另列文字;整卡固定導向 `/cp/list`,connection 與 runtime 不混用 
 - [ ] Connector KPI 顯示「serviceable / total 個」及不可服務/其中故障數;不得將可服務誤標為 AVAILABLE,也不得由 bounded preview 重算;整卡固定導向 `/connector/list` 
 - [ ] 第三張 KPI 名稱固定為「充電進行中」,顯示 Backend activeSessionCount;不得由 raw CHARGING/功率/preview 推導;小字顯示「依未結束的充電 Session 計算」,不顯示次要功率,整卡導向 `/connector/list` 
 - [ ] 第四張 KPI 名稱固定為「等待供電」,顯示 Backend waitingForPowerCount 與「另有 N 個未插槍(不計入)」;不得將 notPluggedCount 加回主數字或由 preview/等待時間重算,整卡導向 `/connector/list` 
 - [ ] 「需留意事項」頁首顯示 N 類,各 card 顯示自己的 affectedCount 與單位;FE 不加總或跨類別去重 
 - [ ] 「需留意事項」顯示全部非零 cards,Desktop auto-fit、Tablet 雙欄、Mobile 單欄;不做 Top N/Drawer/展開/分頁 
 - [ ] LiveLoad 明確顯示 X 軸時間、Y 軸「充電功率(kW)」、各模式 bucket 與「區間平均功率」;bucketState 的進行中語意與 dataStatus 的 COMPLETE/PARTIAL/ERROR 分開呈現,0 與 null 不得混淆 
 - [ ] 依 #1462 LOAD-DEC-007,Overview 一次回傳即時/今日/7 日三組 series;預設即時,切換只讀取既有 cache、不發 request 或重建 WebSocket;五分鐘/手動刷新保留選擇,所選範圍 Empty/Error 時不自動跳頁籤 
 - [ ] LiveLoad 以 buildingId + rangeCode + bucketStartAt 套用 WebSocket replacement;不自行積分/平均,sequence gap 或重連時以 REST snapshot 恢復 
 - [ ] Connector summary 依固定順序與中文 label 顯示 NEEDS_ATTENTION「需留意」、CHARGING「充電進行中」、WAITING「準備/排隊」、AVAILABLE「可用」、OTHER「其他狀態」,直接使用 Backend counts、不從 preview 重算;`total > 0` 時五項皆顯示(包含 count = 0),`total = 0` 時隱藏 summary 並顯示 Connector Empty State,且 0 不得當成缺值。五個 summary 項目使用非互動內容語意,不實作 link/button/tabindex/click/filter/navigation;單支 detail 與完整清單只由 preview card/「查看全部」開啟。Card/Drawer 不得當成缺值。Card/Drawer 仍分層顯示 OCPP/runtime/Queue。Connector preview 固定 Cards並保持 Backend 順序;Desktop/Tablet/Mobile 只顯示 Top-8 payload 的前 8/6/4 筆,以 `total`/`abnormalTotal` 與 visible `isAbnormal` 計算隱藏數量;不傳 viewport 或重判異常。完整清單 Drawer 使用 REST search 的單一 responsive list(不實作 Grid/Table toggle) 
 - [ ] Connector detail drawer 與既有管理頁/Engineer Tools deep link 
 - [ ] 依 #1461 實作固定 30 日 UsageTrend(kWh/Sessions、平均每次充電量、0/null、Empty、Partial、Initial Error、Refresh Error with cached data、Stale,依案場 timezone且不提供 range selector),以及 attachment #1110 四步驟結算流程面板;月份只格式化 Backend 回傳期間,四列維持純資訊,footer 提供「查看帳單」與「查看請款/發票」既有 route 導覽 
 - [ ] 第五張 KPI 名稱固定為「今日已完成充電量」,顯示 Usage today bucket 的 validEnergyKWh 與「已完成 N 次充電」;不顯示昨日同時段比較、不估算 ACTIVE,整卡以 keyboard-accessible anchor 導向同頁 `#usage` 
 - [ ] 依 #1486 顯示「上月電費」KPI:完整時顯示應收總額;未全數完成時顯示 `—`、月份及完成筆數,整卡可用鍵盤導向同頁 `#billing`,不得自行推算月份、顯示部分合計或 forecast 
 - [ ] 依 KPI-DEC-007 實作 KPI Loading skeleton、有效 0、PARTIAL 有值/無值、Initial Error、Refresh Error with same-building cache、STALE 與 recovery;null 不轉 0、狀態不只靠顏色、失敗 source 不清空其他 KPI 
 - [ ] Healthy/Critical/Partial/Stale/Empty/Large fixtures,並涵蓋 KPI loading、zero、initial error、refresh error with cache 與跨 building cache isolation 
 - [ ] 依 #1486 KPI-DEC-008 實作六張 KPI responsive grid:`>=1240px` 6×1、`768~1239px` 3×2、`320~767px` 2×3;固定 DOM/視覺順序,不隱藏、不使用 carousel 或水平捲動,長文案完整換行;驗證 390/768/1024/1440px 與臨界值 
 - [ ] 依 #1486 KPI-DEC-009 將六張 KPI 實作為單一原生 link:固定導向 `/cp/list`、`/connector/list`、`#usage`、`#billing`;整卡支援 pointer/touch/Tab/Enter、同頁 anchor 後移動 focus、狀態同步 accessible name、支援 reduced motion,且不逐筆 live announce WebSocket 數字 
 - [ ] Production API error 不得 fallback 到 fixture 

 ### Database / Performance 

 - [ ] 取得 A17 table row count、狀態分布、MeterValue 24h volume 與 query plan 
 - [ ] 優先採 read-only aggregate,不先建立 Dashboard snapshot table 
 - [ ] 只有在 A17 EXPLAIN 證明必要時,才提出普通 index 
 - [ ] 不新增 UNIQUE/FOREIGN KEY/CHECK/cascade constraint 

 ### QA / Release 

 - [ ] Backend repository/service/security/contract tests 
 - [ ] 權限測試覆蓋 `administrator`、`power_user`、`association`、`dealer` 可讀取 Overview/取得 ticket,以及只有 `adm_user`、`member`、`hq`、`engineer` 時為 403/WebSocket 拒絕;多 role member 只要具任一允許 role 即可存取 
 - [ ] FE fixture、RWD、keyboard、unknown enum、null/0/large value tests;Connector 覆蓋 0/1/2/8/9/30/100 筆與 390/768/1024/1440px,驗證同一 Top-8 payload 的 8/6/4 截取及隱藏數量;驗證五類固定 precedence、每個 Connector 恰好一個 displayBucket、bucket counts 總和等於 total、NEEDS_ATTENTION count = abnormalTotal、Unknown/NOT_PLUGGED mapping 正確;另驗證 total > 0 時五類包含零值皆可見、total = 0 時只顯示 Empty State、0/null/Loading/Error 不混淆,以及五個 summary 項目不可點擊、不進 Tab 順序、不觸發 Drawer/filter/navigation/request State,以及 0/null/Loading/Error 不混淆 
 - [ ] Backend/FE contract tests:唯一 enabled Building 正常載入;零筆與多筆均顯示 configuration error;request 不含 buildingId;WS event buildingId 不符時拒絕套用 
 - [ ] A17 staging E2E:offline、faulted、active transaction、stale meter、blocked queue、billing、partial data、403 
 - [ ] 上線採 controlled release,觀察 24 小時 error rate、partial rate、query latency 

 ## 核心狀態規則 

 1. `ChargePoint.ocppConnectionStatus`、Charge Point runtime、Connector runtime、rotation queue 必須分層顯示,不得合併成單一在線/離線。 
 2. Heartbeat/BootNotification 只能證明連線存活,不能把 FAULTED/CHARGING 覆寫為 AVAILABLE。 
 3. Connector runtime primary owner 是 StatusNotification。 
 4. `currentTransactionId` 或 ACTIVE transaction 存在時,離線告警需標記可能仍在離線充電/待 reconciliation。 
 5. 即時數值不存在時回 `null`,不可用 0 表示未知。 
 6. Status code 供 FE 邏輯使用;中文 label 僅供顯示,未知 code 不得 fallback 成 AVAILABLE。 

 ## Acceptance Criteria 

 - [ ] 使用者 10 秒內可從「需留意事項」指出離線、故障、既有 Queue BLOCKED 或結算流程異常 
 - [ ] 所有「需留意事項」摘要與關鍵數字都有可用 deep link;摘要本身不執行 mutation 
 - [ ] 「需留意事項」頁首 categoryCount 等於非零 categories 數量;各類 affectedCount 於類別內 distinct,跨類別不推導共同根因 
 - [ ] 需留意 categories 依固定 allowlist 順序全部顯示;1~5 類在 390/768/1024/1440 px responsive 換行且無水平捲軸 
 - [ ] Attention aggregate 為 COMPLETE + categoryCount 0 + empty categories 時保留精簡正常狀態;PARTIAL/ERROR 不得顯示為沒有需留意事項 
 - [ ] Attention PARTIAL 顯示已確認 categories 與數量可能不完整;ERROR 回 null count 並顯示不可取得,兩者皆不新增作業行為 
 - [ ] 五類 Attention card 依 ATT-DEC-007 固定導向既有 route/section;支援 keyboard focus,且不新增任意 URL、篩選 API 或 mutation 
 - [ ] Overview API 在 A17 資料量下 P95 < 1.5 秒 
 - [ ] Header 顯示 Backend-resolved 案場名稱與 `generatedAt`;各資料源有 `updatedAt` 與 freshness;UI 不提供案場 selector 
 - [ ] Overview/ticket request 與 WebSocket URL 不含 Frontend-supplied `buildingId`;Overview response、ticket binding 與 event envelope 使用相同 Backend-resolved `buildingId` 
 - [ ] enabled Building 恰好一筆才載入 Dashboard;零筆或多筆顯示設定錯誤並隱藏營運數字,不得任選、跨案場加總或將未知顯示為 0 
 - [ ] Charge Point 連線 KPI 只統計 Backend-resolved current building 的 enabled CP,ONLINE/OFFLINE/UNKNOWN 合計等於分母;disabled 不算離線,Dashboard 不用 Heartbeat 時間重新判定連線 
 - [ ] Connector 可服務 KPI 的 parent CP/status allowlist、unknown 排除與 enabled CP scope 符合 KPI-DEC-003;serviceable + nonServiceable 等於 total,REST/WS 使用相同 Backend 結果 
 - [ ] 「充電進行中」每 Connector 最多計一筆 canonical ACTIVE transaction;舊孤兒 ACTIVE、Queue 與未建立 StartTransaction 的 operation 不計,offline/disabled CP 下尚未結束 Session 不隱藏 
 - [ ] 「等待供電」只計 Queue ELIGIBLE 的 distinct Connector;多筆 Queue row 不重複,NOT_PLUGGED 另列且與主數字互斥,BLOCKED 只由需留意事項呈現,REST/WS aggregate 一致 
 - [ ] 「今日已完成充電量」與完成次數等於 Usage rangeEndDate daily bucket;只含依開始日歸屬的有效 COMPLETED 交易,ACTIVE 不估算,狀態與 REST refresh 完全沿用 #1461 
 - [ ] 六張 KPI 在 Loading/COMPLETE 0/PARTIAL/Initial Error/Refresh Error/STALE 下符合 KPI-DEC-007;null 不冒充 0、快取不跨 building,且 source failure 只影響相依 Card 
 - [ ] 六張 KPI 依 KPI-DEC-008 在 1440px 為 6×1、768/1024px 為 3×2、390px 為 2×3;固定順序且完整顯示,長內容可換行,320px 以上不產生水平捲軸 
 - [ ] 六張 KPI 依 KPI-DEC-009 各為一個原生 link 與單一 Tab stop;目的地、Enter、focus indicator、同頁 focus transfer、accessible name、reduced motion 與非 live WebSocket 更新均符合規格,所有資料狀態仍可導覽 
 - [ ] 任一子查詢失敗時,其餘區塊仍顯示並標記 PARTIAL 
 - [ ] Stale 資料不可被顯示為正常,也不可納入需要即時性的總功率 
 - [ ] API 與 UI 不得以 Charge Group contract capacity 加總值作為案場契約容量、利用率或剩餘容量 
 - [ ] Live Load 三種檢視的 range/bucket 符合 LOAD-DEC-003,7 日為含今天的 7 個案場日曆日而非 rolling 168 hours 
 - [ ] Y 軸數值為重建案場功率時間線後的 time-weighted averagePowerKw;不得直接對不同步且頻率不同的 raw MeterValue rows 加總或算術平均 
 - [ ] WebSocket 一般更新不重送完整 series;bucket 跨界可正確 finalized/建立新 open bucket,重連或 gap 後由 REST authoritative snapshot 恢復 
 - [ ] Live Load 完整零負載為 0 + COMPLETE;任一超過 cutoff 的 unknown 充電功率為 null + PARTIAL,圖表顯示缺口且不插值/跨線 
 - [ ] current Live Load STALE/PARTIAL/ERROR 只在「需留意事項」產生一個 category;歷史 gap 與 OPEN + COMPLETE 不產生摘要 
 - [ ] 「上月電費」以 building timezone 的上一完整日曆月及 `e_bill_settlement.cost_predict` 計算;paid/pending 均納入,任一帳單未 COMPLETE 時 amount 為 null 並顯示完成筆數,不顯示部分合計或退回前期 
 - [ ] `administrator`、`power_user`、`association`、`dealer` 經既有 `dashboard` function 可進入頁面、讀取 Overview 並取得 ticket;四者資料內容與唯讀能力一致 
 - [ ] 僅有 `adm_user`、`member`、`hq` 或 `engineer` role 而未取得 `dashboard` function 時,直接呼叫 Overview/ticket API 得到 403,且沒有有效 ticket 不得建立 Dashboard WebSocket 
 - [ ] Frontend menu/route visibility 與 Backend function 一致,但不得以隱藏 menu 取代 API/ticket 授權;Dashboard 權限不附帶任何 Start/Stop、Reset、Queue、帳務修改或 Engineer command 能力 
 - [ ] 狀態不可只靠顏色;keyboard focus、status message 與文字對比符合 WCAG 2.2 AA 基本要求 
 - [ ] 390/768/1024/1440 px 不出現頁面級水平捲軸 
 - [ ] Production build 不包含 API error → fixture fallback 

 ## A17 Phase 0 Gate 

 實作前必須用 read-only query 確認: 

 - CP ONLINE/OFFLINE/UNKNOWN 與 runtime 分布 
 - Connector status/area/type 分布 
 - Active transaction 數與最長持續時間 
 - 各 Charge Group contract/allocated/reserved/queue 
 - MeterValue 24 小時筆數、峰值與 latest lag 
 - 近 30 日 transaction/kWh/日分布 
 - 最近結算資料期間的 Billing/Invoice 狀態分布 
 - 各 aggregate query 的 EXPLAIN 

 ## E2E Test Impact 

 **Classification:Add** 

 此需求新增 user-visible Dashboard、typed API、SS3A permission 與多來源營運聚合,需在實作階段新增長期 E2E Catalog cases: 

 - P0 / Standard、Major、A17 release:Dashboard Overview/ticket/WebSocket 未授權拒絕、PARTIAL、stale safety 
 - P1 / Standard、Major、A17 release:四個允許 role 的正向存取、多 role member、OCPP offline、Connector faulted、active transaction、queue blocked、billing block、responsive,以及唯一/零筆/多筆 enabled Building scope 
 - P2 / Major、A17 release:large data、unknown enum、performance baseline 

 實作 ticket 的階段 2 必須依 `maintain-ems-e2e-catalog` skill 確認正式 Case ID 與 release pack。 

 ## Definition of Done 

 - [ ] Product 確認 KPI 字典、範圍與待決策事項 
 - [ ] A17 baseline 完成且未推翻公式/效能方案 
 - [ ] Backend API、OpenAPI、SS3A mapping 與測試完成 
 - [ ] FE 各區塊、states、RWD、accessibility、deep link 完成 
 - [ ] A17 staging E2E 與 P95 通過 
 - [ ] E2E Catalog 與 `ai/02-backend-services.md`、`ai/03-database.md`、`ai/04-frontend.md` 更新 
 - [ ] 發布後完成 24 小時監看 

 ## 待決策 

 - V1 已確認採每個 EMS Branch 自動使用唯一 enabled Building;Frontend 不傳或切換 `buildingId`,Overview response/ticket binding/WS event 仍帶 Backend-resolved `buildingId`;零筆或多筆顯示設定錯誤(#1424 DEC-029) 
 - V1 不顯示或回傳由 Charge Group contract capacity 加總推導的案場契約容量、利用率與剩餘容量;只保留案場即時總功率及各群組自身容量(LOAD-DEC-001,已確認;詳見 #1462) 
 - Live Load 保留「即時/今日/7 日」三種檢視;X 軸分別為最近 60 分鐘/今日/含今天 7 個案場日曆日,bucket 為 1 分鐘/15 分鐘/1 小時,Y 軸為 time-weighted averagePowerKw(LOAD-DEC-002/003,已確認;詳見 #1462) 
 - Live Load 完整 series 由 REST 提供;WebSocket 只更新 currentPowerKw、三種 open buckets 與跨界 finalized bucket,FE 採 replacement(LOAD-DEC-004,已確認;詳見 #1462) 
 - Live Load 以 bucketState 區分 OPEN/FINALIZED、dataStatus 區分 COMPLETE/PARTIAL/ERROR;unknown 充電功率回 null + PARTIAL,不插值、部分合計或 coverage threshold(LOAD-DEC-005,已確認;詳見 #1462) 
 - Live Load 預設「即時」,三組 series 隨同一 Overview 一次取得;切換不發 API/重建 WebSocket,刷新保留選擇,Empty/Error 留在所選頁籤(LOAD-DEC-007,已確認;詳見 #1462) 
 - Queue waiting 僅顯示持續時間,V1 不設定 warning/timeout 門檻(已確認) 
 - 30 日充電用量固定最近 30 個案場日曆日(含今天),保留 kWh/Sessions 切換,不提供 7/90 日或自訂範圍(USG-DEC-001,已確認;詳見 #1461) 
 - 有效充電量只加總 COMPLETED 的既有最終 energy_consumed;ACTIVE 不估算,異常 Completed 排除並標示 PARTIAL(USG-DEC-002,已確認) 
 - Usage 每日 kWh/Sessions 依 building timezone 的交易開始日歸屬,跨日不拆 MeterValue(USG-DEC-003,已確認) 
 - 充電 Sessions 只計算 COMPLETED 且 energy_consumed > 0 的交易;0 Wh 與 ACTIVE 不計入(USG-DEC-004,已確認) 
 - V1 移除時間使用率及其 Backend denominator/threshold(USG-DEC-005,已確認) 
 - 第三項摘要改為平均每次充電量,由 Backend 以 validEnergyKWh ÷ validSessionCount 計算;0 Sessions 回 null(USG-DEC-006,已確認) 
 - Usage 固定回傳 30 個日期 bucket;成功無使用為 0,資料未知為 null,部分未知時區塊為 PARTIAL(USG-DEC-007,已確認) 
 - 30 日全為零時 sourceStatus = COMPLETE、dataState = EMPTY,顯示 0/0/— 與中性無紀錄文案,不建立告警(USG-DEC-008,已確認) 
 - Usage 初次失敗顯示 Error;後續 refresh 失敗保留同 building 最後成功資料並顯示 lastSuccessfulAt,依 Backend freshness 轉為 STALE(USG-DEC-009,已確認) 
 - 首頁帳務區塊沿用 attachment #1110 四步驟「結算流程」;不改成 Billing Summary(BIL-DEC-001,已確認) 
 - 結算流程期間由 Backend 依最近結算資料回傳;Frontend 不依日曆月份推算(BIL-DEC-002,已確認) 
 - 結算四列維持純資訊;footer 分別導向既有 /bill/list 與 /invoice/list,不新增頁面或 drill-down API(BIL-DEC-003,已確認) 
 - 「需留意事項」ATT-DEC-001~007 已確認:使用既有明確狀態、current Live Load 品質規則、category/affected count、固定順序與 responsive、Empty/Partial/Error State,以及五類固定 deep link;本區塊待全 Dashboard 鎖版時整併(詳見 #1463) 
 - 「即時關鍵指標」KPI-DEC-001~010 已完成需求確認:六張 KPI 的資料與狀態、6×1/3×2/2×3 responsive layout、單一原生 link/固定 deep link,以及「充電進行中」不重複顯示即時功率均已定案(詳見 #1486) 
 - Dashboard 權限已確認沿用既有 `dashboard` function,授權 `administrator`、`power_user`、`association`、`dealer`;`adm_user`、`member`、`hq`、`engineer` 不自動取得(#1424 DEC-030) 
 - 非即時區塊已確認使用頁面首載、共用 5 分鐘 Overview cycle 與手動刷新;不再採 60 秒 fallback 或各區塊獨立週期(DEC-031/032,已確認) 
 - Freshness 已確認只使用 Backend per-source global defaults;V1 撤回 per-building override 及其設定儲存/API/UI/權限/audit(DEC-033,已確認) 
 - Connector preview 已確認採 Backend 單一 Top-8 payload + `total`/`abnormalTotal`/item abnormal metadata;FE 只依 breakpoint 顯示 8/6/4 筆並計算純顯示隱藏數量,完整清單走 REST Drawer(UI-DEC-013/DEC-034,已確認) 
 - Connector Status summary 已確認使用互斥 `displayBucket`,每個 Connector 恰好一類且分項合計等於 total;固定 NEEDS_ATTENTION/CHARGING/WAITING/AVAILABLE/OTHER 五類及 precedence,Unknown 歸需留意、NOT_PLUGGED 歸其他。非空案場固定顯示包含零值的五類,空案場顯示 Empty State;五個摘要項目為不可點擊的純資訊,detail/完整清單由既有 preview card/「查看全部」負責;原始狀態仍分層顯示(UI-DEC-014~017、DEC-035~038,已確認) State;原始狀態仍分層顯示(UI-DEC-014~016、DEC-035~037,已確認)

返回