討論 #1424
是由 陳國瑋 於 17 天 前更新
# Dashboard 資料取得與即時更新需求(現行有效版)
> **閱讀基準:** [主需求 #1422](https://redmine.sylksoft.com/issues/1422) 與 [HTML Mock v0.3/附件 #1117](https://redmine.sylksoft.com/attachments/1117)。以下沿用主票區塊編號,依畫面由上到下、由左到右說明。Mock 數字皆為假資料;功能適用所有案場,A17 不是固定規則。 **Revision 2026-09-05**:本 description 只保留目前可供實作與驗收的 V1 規格。先前的 capped exponential backoff、60 秒 REST polling fallback、sequence/gap recovery、固定 5 秒 coalescing、Operational Event P95 2 秒 SLO、per-building stale cutoff override,以及要求 KPI/需留意事項/群組資料走 WebSocket 的方案,均已被 DEC-031~033 取代,不得作為開發依據。完整討論歷程仍保留於 J5641 及更早的 Redmine journals。
## 本票要完成什麼:讓整張 Dashboard 用簡單、一致的方式取得資料 背景與目標
本票負責**案場範圍、權限、REST/WebSocket 分工、資料狀態與前後端契約**。畫面/公式分別由 Branch Admin `/dashboard` 目前沒有正式營運內容。既有 `GET /api/connector/dashboard` 只有 Connector 總數、Available、Charging、Faulted、公用與私人數量;既有 Connector 管理頁 search API 與單 Connector WebSocket 可部分重用,但不足以直接支援整張營運 Dashboard。
V1 建立所有 EMS Branch 共用的唯讀 Dashboard。A17 只作第一個資料、公式及效能驗證案場,任何 API、UI、WebSocket、freshness 或 query 不得硬編碼 A17。
本票只定義資料 ownership、REST/WebSocket channel、Frontend state 與跨區塊 contract;各區塊完整資料公式分別由 #1423、#1460、#1461、#1462、#1463、#1486 定義,不在本票建立第二套算法。 管理。
既有 GET /api/connector/dashboard 只有部分Connector統計;管理頁search與單支detail socket可重用,但不能直接當成整張Dashboard的資料來源。需要新的typed Overview與最小Dashboard socket。 ## V1 簡化原則
**先記住三件事:**
1. 一次Overview取得一般資料,首載/每5分鐘/手動刷新。 - Dashboard 只查詢、彙整與顯示既有狀態,不執行 Remote Start/Stop、Reset、Queue/Rotation、帳務或其他 mutation。
2. 一條Dashboard WebSocket只更新「06 Connector即時狀態」與「04 案場即時負載」。 - 只有「Connector 即時狀態」與「案場即時負載」需要 WebSocket;其他區塊使用同一個 Overview REST response。
3. Socket連不到就提示並保留資料;不另建REST備援、自動重連或gap - WebSocket 失敗時誠實提示使用者,不建立 REST fallback、custom retry、背景自動重連或 gap recovery。
- V1 優先重用既有 endpoint、權限與 domain service;不建立重複的 Dashboard inventory、告警引擎或作業狀態。
## 1. 先依畫面順序看資料通道 Overview 效能驗證(DEC-042)
「REST-owned」表示由Overview負責更新;「WS-owned live state」表示socket接手後只由socket負責更新,兩者不能互相覆寫。(DEC-031/032) **狀態:已確認(2026-09-06,Ken)**
- V1 不將 `P95 < 1.5 秒` 列為 Overview 產品 SLA;A17 只作第一個 baseline 驗證站點,規則不得硬編碼 A17。
- 實作與首站驗證必須以 A17 或具代表性的等量資料,對 Overview aggregate query 執行 EXPLAIN,並量測 endpoint P50/P95。
- Test report 必須記錄測試環境、主要資料表 row count/資料分布、暖機方式、併發數、sample size 與量測起訖邊界,使結果可重現。
- Backend 必須避免 N+1、無界查詢及不必要的重複聚合。只有量測證明效能已影響正常使用時,才提出 query 或普通 index 優化。
- V1 不為達成固定秒數預先新增 Dashboard snapshot table、專用 cache、效能排程或其他架構。跨案場固定 SLA 若日後需要,另案定義資料量級、負載模型與測試條件。
## Building Scope 與權限(DEC-029/030)
- 每個 EMS Branch 的 V1 必須且只能有一筆 enabled Building。Backend 自動解析該 Building;Frontend 的 Overview/ticket request 與 WebSocket URL 不傳 `buildingId`。
- Overview response、ticket binding 與所有 Dashboard WebSocket event 明確帶 Backend-resolved `buildingId`,Frontend 只套用 scope 相符的資料。
- enabled Building 為零筆或多筆時,Backend 回明確 configuration error;Frontend 顯示整頁「案場設定異常」,隱藏營運數字,不得任選第一筆、跨 Building 加總或顯示假 0。
- Header/Sidebar 只顯示 Backend 回傳的唯讀案場名稱與 timezone,不提供 site selector。
- V1 沿用既有 SS3A `dashboard` function,不新增 role/function。既有 `administrator`、`power_user`、`association`、`dealer` 可看同一份唯讀 Dashboard;只有 `adm_user`、`member`、`hq`、`engineer` 時不自動取得權限。
- Overview API 與 Dashboard ticket API 都必須 mapping 至 `dashboard` function;Frontend 隱藏 menu 不能取代 Backend 的 403 與 WebSocket ticket authorization。
## 現行資料通道矩陣(DEC-031/032)
| 主票位置/畫面 區塊 | 首次資料 | 後續更新 | Dashboard socket斷線時 WebSocket 失敗時 |
|---|---|---|---|
| 01 案場名稱/一般資料時間 Header 案場資訊、一般資料時間 | `GET /api/dashboard/overview` | 同一 Overview 每 5 分鐘/手動刷新 | 共用5分鐘/手動Overview 照常更新 REST 狀態 | 照常更新一般資料與其品質 |
| 02 需留意事項 六張 KPI | Overview | 同上,REST-only 同一 Overview 每 5 分鐘/手動刷新 | 照常;不改成其他備援模式 照常更新,不使用 WS fallback |
| 03 六張KPI 需留意事項 | Overview | 同上,全部REST 同一 Overview 每 5 分鐘/手動刷新 | 照常;不訂閱KPI WS事件 照常更新,不使用 WS |
| 04 目前功率/open buckets Charge Group 容量/Slots/Queue 摘要 | Overview bootstrap | LIVE_LOAD_UPDATED replacement 同一 Overview 每 5 分鐘/手動刷新 | 保留最後值/時間、標停止更新;無資料— 照常更新,不使用 WS |
| 04 即時/今日/7日完整歷史 30 日 Usage、今日已完成充電量 | 一次Overview給三組 Overview | 同一Overview更新歷史;WS另負責open/跨界finalized 同一 Overview 每 5 分鐘/手動刷新 | 一般歷史可刷新,但不得冒充即時recovery 照常更新,不使用 WS |
| 05 群組容量/Slots/Queue摘要 四步驟結算、上月電費 | Overview | 共用5分鐘/手動Overview 同一 Overview 每 5 分鐘/手動刷新 | 照常,非WS 照常更新,不使用 WS |
| 06 五項摘要/Top-8預覽 Connector 即時狀態摘要/Top-8 | Overview bootstrap | CONNECTOR_LIVE_BLOCK_UPDATED整區replacement `CONNECTOR_LIVE_BLOCK_UPDATED` replacement | 保留最後值/時間、標停止更新;無資料— 保留最後資料並標示已停止更新;無資料顯示 `—` |
| 07 30日用量、03-5今日已完成量 Connector 完整清單 | 同一Overview/Usage read model `POST /api/connector/search` | 共用5分鐘/手動Overview 開啟 Drawer、搜尋、篩選或換頁時重新查詢 | 照常,非Usage WS 不受 Dashboard socket 影響 |
| 08 結算流程、03-6上月電費 Connector detail | Overview 既有 detail API | 共用5分鐘/手動Overview Drawer 開啟時才使用既有單 Connector socket | 照常,非Billing WS 依既有 detail flow 顯示錯誤;關閉即釋放 |
按需打開的介面另外處理,不混入首頁更新:
| 介面 案場即時負載目前值/open buckets | 資料通道 Overview bootstrap | `LIVE_LOAD_UPDATED` replacement | 保留最後資料並標示已停止更新;無資料顯示 `—` |
|---|---|
| 06「查看全部」Drawer Live Load 即時/今日/7 日完整 series | 開啟、搜尋、篩選、換頁時POST /api/connector/search,REST server pagination Overview |
同一 Overview 每 5 分鐘/手動刷新;WS 只 replacement open bucket | 單支Connector detail 保留 REST series;不得冒充仍即時 | 既有detail API;打開時才使用既有單Connector socket,關閉釋放 |
「一條Dashboard socket」指首頁本體,不是禁止按需使用既有單支detail flow;也不能因此替每張preview card建立socket。
## 2. 整體資料流程與scope Overview REST Contract(DEC-002 保留部分/DEC-032)
```text - 新增 typed `GET /api/dashboard/overview`,一次回傳目前 Building 的案場識別、`generatedAt`、各 source 的狀態/時間,以及所有 Dashboard sections。
登入使用者
↓ - 更新時機只有:頁面首次載入、Dashboard 保持掛載期間每 300 秒一次、使用者手動刷新。
既有 SS3A dashboard function 授權
↓ - 每個更新週期只送一個 Overview request;KPI、Attention、Group、Usage、Billing 與歷史 series 不得各自建立 timer 或重複 request。
Backend 解析本 Branch 唯一 enabled Building
├─ 不是恰好1筆 → 設定異常,不載入營運數字
└─ 恰好1筆
├─ GET /api/dashboard/overview
│ ├─ 一般區塊+三組歷史
│ └─ 兩個live blocks初始畫面
└─ 短效、一次性Dashboard ticket
↓
raw - 同一份成功 response 的 REST-owned sections 使用相同 `generatedAt`。每個需要獨立品質判斷的 source/section 另回 `dataStatus`、`freshness`、`updatedAt` 及必要的 `lastSuccessfulAt`。
- WebSocket
├─ DASHBOARD_LIVE_SNAPSHOT
├─ CONNECTOR_LIVE_BLOCK_UPDATED
└─ LIVE_LOAD_UPDATED 尚未接手前,Overview 可顯示兩個 live blocks 的 bootstrap。收到 socket initial snapshot 後,定時或手動 Overview refresh 不得覆寫 Connector 即時狀態與案場即時負載的 live state。
``` - 手動刷新只更新 REST-owned sections,不重建 WebSocket;只有重新載入整頁才重新取得 ticket 並嘗試連線。
- REST refresh error 可保留同一 Backend-resolved Building 的最後成功資料,但必須標示更新失敗與 `lastSuccessfulAt`;首次失敗不得顯示 fixture 或假 0,也不得跨 Building 使用 cache。
### 2.1 案場由Backend決定(DEC-029) ## Dashboard WebSocket Contract(DEC-008 保留部分/DEC-009/031)
- 每個EMS Branch恰好一筆enabled Building;零筆/多筆回明確configuration error,FE顯示整頁「案場設定異常」並隱藏營運資料。 V1 沿用 raw WebSocket,不引入 STOMP。每張 Dashboard 頁面只建立一條案場層級 Dashboard socket,不為每張 Connector card 建立連線。
- 不任選第一筆、不跨Building加總、不顯示假0;不沿用任選預設案場的查詢方式。 Dashboard ticket 必須短效、一次性、綁定登入管理員與 Backend-resolved Building,且只有具 `dashboard` function 的使用者可取得。
- Overview/ticket request、WebSocket URL不傳FE指定buildingId;Drawer的Dashboard read mode也依相同resolver。 V1 只允許三種 message type:
- `DASHBOARD_LIVE_SNAPSHOT`:連線建立後提供兩個 live blocks 的完整目前狀態。
- `CONNECTOR_LIVE_BLOCK_UPDATED`:replacement 整個 Connector summary + bounded Top-8 preview。
- `LIVE_LOAD_UPDATED`:replacement `currentPowerKw` 與三種 range 的目前 open buckets;跨 bucket 邊界時可同時帶 finalized bucket。
- Overview response、ticket binding、event envelope明確帶相同Backend-resolved buildingId;FE只套用相符資料,cache按buildingId隔離。 Event envelope 至少包含 `schemaVersion`、`type`、`buildingId`、`occurredAt` 與 typed `data`;不傳 raw DB row、JWT、ticket、currentIdTag 或不必要個資。
- Header/Sidebar顯示Backend案場識別,timezone供日期與時間呈現;不提供site selector。 Message 採區塊 replacement。Frontend 不從細碎 delta 自行重算 total、bucket、priority ranking、功率平均或其他 domain state。
- A17只用來驗證,不能出現在固定數字/公式/cutoff/query分支。 V1 以單一 active `ems_branch` instance 為部署前提;若未來要多 instance/HA,須另案設計 session fan-out 與授權,不在本需求預做。
- Backend 可為資源保護做內部 debounce/coalescing,但 V1 不把固定 5 秒或 P95 2 秒列為產品驗收承諾。
### 2.2 權限沿用dashboard function(DEC-030) 連線失敗與中斷
| 條件 | 應有結果 | - 首次連線失敗,或已連線後中斷時,Header 與兩個 live blocks 顯示「即時連線中斷」。
|---|---| - 已有最後成功資料時保留資料與最後更新時間,並明確標示「已停止更新」;尚無資料時顯示 `—` 與「即時資料暫時無法取得」。不得清成 0。
| administrator/power_user/association/dealer,經既有dashboard function授權 | 相同唯讀Dashboard、Overview與ticket能力 | - V1 不做 REST polling fallback、定時更新模式、custom backoff、jitter、背景自動重連、sequence/gap recovery、頁面回到 visible 自動重連或區塊專用 Retry。
| 只有adm_user/member/hq/engineer,未取得dashboard function | Overview/ticket為403;無有效ticket不能建立Dashboard - 其他 REST-owned sections 不受 socket | 中斷影響;使用者重新載入整個頁面時,才重新取得 ticket 並嘗試建立新 socket。
| 多role使用者已有允許權限 | 依既有function機制授權,不因同時擁有其他role拒絕 | - WebSocket connection state 與 source freshness 是兩件事:連線中不代表資料一定 fresh,斷線也不能改寫 Backend 已提供的 source quality。
不新增role/function、不在Java硬編碼role判斷;補Overview/ticket API mapping,依主票以冪等migration確認role-function mapping並驗證重啟後授權快取。
FE依function控制menu/route,未授權不先載入或短暫顯示資料;隱藏menu不能代替Backend授權。既有管理頁/Engineer Tools導覽沿用目的頁權限,Dashboard不附帶Start/Stop、Reset、Queue、帳務或工程指令能力。
## 3. Overview REST契約:一個週期、一份一般資料 Freshness 與資料品質(DEC-006 保留部分/DEC-015/028/033)
新增typed GET /api/dashboard/overview,回傳案場識別、generatedAt、各source的品質/時間及各section。(DEC-002保留部分/DEC-032)
| 時機 | Request | - Stale cutoff 由 Backend 依資料來源設定全系統預設值;Connector、MeterValue、Billing 等來源可不同,但所有案場使用相同設定。Frontend 不硬編碼秒數。
|---|---| - V1 不提供 per-building override,不新增設定 table、管理 API/UI、設定權限或 audit trail;特殊案場未來另開 Feature。
| 頁面首次載入 | 一次Overview | - COMPLETE 的 0 是有效值;`null` 表示未知。Backend 子查詢失敗不得回 0,Frontend 也不得從其他卡片或 preview 猜值。
| Dashboard保持掛載期間每300秒 | 每輪一次Overview,不是每卡各一次 | - PARTIAL 有 Backend 認定安全的值時可保留並標示可能不完整;沒有安全值時回 `null`/顯示 `—`。ERROR/STALE 必須有文字狀態,不得只靠顏色。
| 使用者手動刷新 | 一次Overview,不動socket |
| 卸載Dashboard | 釋放timer與socket資源 | - 一個 source 失敗只影響相依 section;其餘成功 sections 繼續顯示。
同一成功response的REST-owned sections共用generatedAt;需要各別品質判斷的來源另外回dataStatus、freshness、updatedAt及必要lastSuccessfulAt。 ## Connector 即時狀態(DEC-034~038/#1423 UI-DEC-001~017)
### 3.1 接手前後不得覆寫錯的資料 Summary 與 Preview
```text - Backend 對目前 Building 的完整 Connector 集合使用同一份 authoritative snapshot 進行分類與排序,再回傳固定五項 summary、`total`、`abnormalTotal` 及最多 Top-8 `items`。
首載REST bootstrap
↓ - 五個互斥 `displayBucket` 固定依序為:`NEEDS_ATTENTION`「需留意」→ `CHARGING`「充電進行中」→ `WAITING`「準備/排隊」→ `AVAILABLE`「可用」→ `OTHER`「其他狀態」。每個 Connector 恰好一類,bucket counts 合計等於 `total`,`NEEDS_ATTENTION.count = abnormalTotal`。
兩個live blocks可以先顯示初始資料
↓ - Unknown/資料不足歸 NEEDS_ATTENTION;NOT_PLUGGED 歸 OTHER。Card/Drawer 仍分層顯示 OCPP connection、Connector runtime 與 Queue 原始狀態,不以 `displayBucket` 覆蓋 domain state。
收到DASHBOARD_LIVE_SNAPSHOT → socket接手
↓
之後Overview刷新:
一般區塊/歷史 → 可更新
- `total > 0` 時固定顯示包含零值的五項;`total = 0` 時隱藏 summary 並顯示 Connector live/目前功率與open buckets → 不覆寫 Empty State。0 不得當成 Loading/Error/Unknown。
``` - 五個 summary 項目是不可點擊的純資訊,不是 link/button/filter shortcut,也不進入 Tab 順序。單支 detail 由 preview card 開啟,完整清單由「查看全部」開啟。
- Backend payload 固定最多 Top-8,不接受 viewport/requestedLimit。Frontend 依 breakpoint 顯示 Desktop 8、Tablet 6、Mobile 4 筆,並以 Backend totals 計算隱藏總數與隱藏異常數;不自行重排或重判異常。
只有整頁reload才重取ticket、重新嘗試socket。手動刷新不重建連線;socket中斷也不解除live ownership讓REST冒充備援。
### 3.2 REST失敗不代表全頁歸零 固定 Priority Hierarchy
單一source失敗只影響相依section,其他成功部分繼續顯示,Overview標PARTIAL或可診斷source status。初次無成功資料顯示Error/—;後續失敗可依各區契約保留**同building**成功cache,附更新失敗與lastSuccessfulAt,不假裝最新。 Backend 對 Preview 與 Full List 共用以下順序:
不能catch後回0、不能從preview/其他卡猜值,production不能退回fixture。需留意事項aggregate ERROR依#1463顯示不可取得,不能拿舊cards冒充本次結果;不是所有section都能不分狀態套同一fallback。
## 4. WebSocket契約:三種訊息、區塊取代
V1沿用**raw WebSocket,不引入STOMP**。每個掛載的Dashboard本體只有一條案場socket。(DEC-008保留部分/009/031)
### 4.1 Ticket與envelope
Ticket短效、一次性,綁定登入管理員與Backend-resolved Building,只有具dashboard function者可取得。
| Envelope欄位 | 用途 | 1. Charge Point OFFLINE 且 Connector 有 ACTIVE transaction。
|---|---| 2. Connector FAULTED。
| schemaVersion | FE判斷是否支援此契約版本 | 3. Charge Point OFFLINE、資料 STALE、Queue BLOCKED。
| type | 下表三種訊息之一 | 4. Connector SUSPENDED_EVSE、UNAVAILABLE。
| buildingId | 案場隔離,不接受不符scope資料 | 5. Connector SUSPENDED_EV。
| occurredAt | 事件資料時間 | 6. Connector CHARGING。
| data | 對應type的typed區塊payload | 7. Connector PREPARING/Waiting。
8. Connector AVAILABLE。
不傳raw DB row、JWT、ticket、currentIdTag或不必要個資;前端不對不支援schema或錯scope猜測套用。 同一層級依異常/狀態持續時間由久至新,再以 Connector ID 作 deterministic tie-breaker。排序只用於唯讀顯示,不修改 Queue priority、Connector status 或 charging lifecycle。
### 4.2 訊息內容 ## Connector Full List Drawer(DEC-039~041/#1423 UI-DEC-018~020)
| type | 什麼時候/送什麼 | FE如何套用 | - 使用/擴充既有 `POST /api/connector/search`;完整 inventory、搜尋與分頁不透過 Dashboard WebSocket。
|---|---|---| - `SearchConnectorDto` 沿用 `page`/`size` 與 Spring Page response metadata,Dashboard 固定 `size = 20`。結果 1~20 筆隱藏換頁控制;多頁只提供上一頁/下一頁與「第 X / Y 頁・共 N 個」。
| DASHBOARD_LIVE_SNAPSHOT | 建連後,兩個live blocks完整目前狀態 | 接手目前live state,不等於重送整個Dashboard/全部歷史 | - 搜尋文字或任一篩選改變時回第 1 頁。Server 必須對完整資料集先 filter、再依固定 priority hierarchy sort、最後 paginate;不得只處理目前 20 筆。
| CONNECTOR_LIVE_BLOCK_UPDATED | Connector五項summary+totals+Backend排序Top-8 | 整個區塊replacement,FE不自己算分類/排名 | - 既有 keyword/status 等條件可重用;Charge Point ID/Charge Group 等缺少條件須補 DTO、query、OpenAPI 與 contract tests。
| LIVE_LOAD_UPDATED | currentPowerKw、LIVE/TODAY/SEVEN_DAYS三種open bucket;跨界可帶剛結束finalized | 目前值與bucket各自取代,按buildingId+rangeCode+bucketStartAt合併 | - Dashboard Drawer 不提供 sorting control。既有 search endpoint 增加 stable `sortMode = DASHBOARD_PRIORITY`;Dashboard 不接受使用者產生的 `sidx`/`sord`,既有 `/connector/list` 管理頁原欄位排序保持不變。
- Search/filter/page state 只存在目前 Dashboard page memory;close/reopen 保留,reload/navigation 重設。不寫 URL、history、localStorage、sessionStorage、Backend preference 或 WebSocket。
- V1 不提供 page-size selector、頁碼列、直接跳頁、Grid/Table toggle 或 virtualization。
不用細碎delta讓FE重算total、bucket、priority或平均功率。Backend可以內部debounce/coalescing保護資源,但不承諾固定5秒更新或P95 2秒SLO,也不為這個要求先建專用架構。 ## 其他區塊的有效資料規則
### 4.3 連不到/中斷時就明示 Live Load(DEC-012~015 保留資料公式;詳見 #1462)
| 情況 | 頁首與兩個live blocks | - 不以 Charge Group contract capacity 加總推算案場契約容量、利用率或剩餘容量;只顯示案場即時總功率及各群組自身容量。
|---|---| - 保留「即時/今日/7 日」三種 series。X 軸分別為最近 60 分鐘/1 分鐘 bucket、今日 00:00 至現在/15 分鐘 bucket、含今天 7 個案場日曆日/1 小時 bucket;Y 軸為「充電功率(kW)」的 time-weighted averagePowerKw。
| 首次連線失敗 | 明示無法連線;已有初始資料可保留並標未即時更新,沒有資料則—與「即時資料暫時無法取得」。 | - REST 回完整 series;WebSocket 只 replacement currentPowerKw 與 open/跨界 finalized buckets。Frontend 不自行積分、平均、插值或跨資料缺口連線。
| 已連線後中斷 | 明示連線中斷,保留最後成功資料/時間並標「已停止更新」。 |
| 其他REST區塊 | 仍依共用Overview正常更新,不因socket中斷停止。 | - `bucketState` 的 OPEN/FINALIZED 與 `dataStatus` 的 COMPLETE/PARTIAL/ERROR 分離;完整零負載為 0 + COMPLETE,存在未知充電功率時整個 bucket 為 `null` + PARTIAL。
**不做:** REST polling fallback、定時更新模式切換、自訂capped exponential backoff、jitter、背景自動重連、visibility reconnect、sequence/gap recovery、專用Retry。使用者整頁reload才再嘗試。
Connection state與source freshness獨立:連著不代表資料FRESH,斷線也不改寫Backend已回傳的來源品質。
### 4.4 部署前提(DEC-009) 需留意事項(DEC-016~022;詳見 #1463)
V1為**單一active ems_branch instance**。未來多instance/HA的session fan-out與授權一致性另案設計,不在本功能預做。
## 5. 品質欄位:0、未知、失敗、過期分開
| 狀態 | 契約與畫面 | - 只彙整既有明確狀態:CP OFFLINE、Connector FAULTED、current Live Load STALE/PARTIAL/ERROR、Queue BLOCKED,以及既有結算未完成/異常。不新增 mismatch、waiting SLA、scheduler-health、容量告警、派工或自動修復。
|---|---| - 頁首 `categoryCount` 是非零 category 數量;每類另回 distinct `affectedCount`,不跨 category 推導共同 root cause。
| COMPLETE且數值0 | 成功確認的0,不是缺值 | - 固定 allowlist 順序並顯示全部非零 categories;COMPLETE + 0 顯示精簡 Empty State,PARTIAL/ERROR 不得冒充正常。
| null | 未知,不轉0 |
| PARTIAL且Backend有安全值 | 可顯示,明示不完整;FE不自行加出部分合計 |
| PARTIAL無安全值 | null/—,例如不完整的案場功率、未全數完成的上月金額 |
| ERROR | 文字說明來源無法取得,不冒充正常 |
| STALE | 文字標過期與資料時間,不使用正常成功樣式 | - Deep link 由 Frontend 固定 allowlist 映射至既有 route/section,只導覽、不執行 mutation。Attention 由 Overview REST 更新,不訂閱 Dashboard WebSocket。
(DEC-006保留部分/015/028/033)
Backend依**資料來源**設定全系統stale cutoff:Connector、MeterValue、Billing可不同,但所有案場共用;FE不硬編碼秒數。V1不做per-building override、設定table/API/UI、設定權限或audit trail。
## 6. Connector:相同snapshot支援首頁與完整清單
### 6.1 Summary/Top-8契約 KPI(DEC-023~028 保留資料公式;詳見 #1486)
Backend從完整案場Connector集合分類、排序,再回固定五個bucket、total、abnormalTotal與最多8筆items。(DEC-034~038,詳細#1423)
| 不變條件 | 說明 |
|---|---|
| 五類順序 | NEEDS_ATTENTION、CHARGING、WAITING、AVAILABLE、OTHER |
| 分類互斥 | 每Connector恰好一類,count總和=total;NEEDS_ATTENTION.count=abnormalTotal |
| 原始事實 | - Charge Point 連線使用 enabled CP 的 persisted OCPP connection、runtime、Queue分層保留,不被displayBucket取代 | ONLINE/OFFLINE/UNKNOWN,不用 Heartbeat 時間另建 Dashboard cutoff。
| Unknown/NOT_PLUGGED | Unknown不當AVAILABLE/OTHER;NOT_PLUGGED依#1423既定mapping歸OTHER | - Connector 可服務數由 Backend 依 enabled CP scope、parent CP ONLINE 與固定 runtime allowlist 計算;Frontend 不從 bounded preview 重算。
| 非空/空 | 非空固定五項含0;空時FE顯示Empty,Backend仍五個0 | - 「充電進行中」以每 Connector 最多一筆 canonical ACTIVE transaction 計算;不以 raw CHARGING、Queue 或功率推導,也不重複顯示 currentPowerKw。
| 摘要互動 | 純資訊不可點擊,不進Tab、不發request | - 「等待供電」只計 Queue ELIGIBLE 的 distinct Connector;NOT_PLUGGED 另列且互斥,BLOCKED 留在需留意事項。
| FE預覽 | Desktop≥1240顯示8、Tablet640~1239顯示6、Mobile<640顯示4;不傳viewport/requestedLimit | - 「今日已完成充電量」重用 Usage 的 today daily bucket;ACTIVE 不以 MeterValue 暫估。
| Item metadata | displayBucket、isAbnormal、severity、priorityReason;FE不重判 | - 「上月電費」使用上一完整日曆月,只有資料完整時顯示總額,否則顯示 `—` 與完成筆數。
| Footer | FE只依totals與visible slice算隱藏總數/異常數,不從Top-8推完整統計 | - 六張 KPI 全部由 Overview REST 更新,不訂閱 Dashboard WebSocket;狀態呈現沿用 source-isolated COMPLETE/PARTIAL/ERROR/STALE contract。
Preview與Full List共用priority:OFFLINE+ACTIVE → FAULTED → OFFLINE/STALE/BLOCKED → SUSPENDED_EVSE/UNAVAILABLE → SUSPENDED_EV → CHARGING → PREPARING/Waiting → AVAILABLE。同層持續時間由久至新、Connector ID穩定排序,詳細多條件處理見#1423 UI-DEC-007。
排序是只讀呈現,不改Queue priority;OFFLINE+ACTIVE不產生「離線充電/待reconciliation」推論(主票及#1463/#1486較新決策)。
### 6.2 完整清單仍用既有REST search Usage、結算與 Queue(DEC-010/011;詳見 #1460/#1461)
```text - Usage 固定最近 30 個案場日曆日(含今天),保留 kWh/Sessions,平均每次充電量由 Backend 計算;不提供 7/90 日、自訂範圍或時間使用率。
完整集合(Backend限制目前案場)
↓ 全部條件filter
↓ 共用DASHBOARD_PRIORITY sort
↓ 固定size=20 paginate
↓ Spring Page回應 - 四步驟結算流程期間由 Backend 依最近結算資料回傳;四列維持純資訊,Frontend footer 導向既有帳單/請款頁。
``` - Queue 只顯示既有 source、status、priority、statusReason、waitingSince/duration;V1 不新增 SLA、timeout、override、設定頁或狀態轉換。
沿用POST /api/connector/search、SearchConnectorDto的page/size與Spring Page metadata;補Charge Point ID/Charge Group等缺少條件、OpenAPI及contract tests。不得先分頁才filter/sort。
Drawer固定sortMode=DASHBOARD_PRIORITY,不提供使用者sidx/sord控制;/connector/list未用此mode時保留原欄位排序。(DEC-039~041)
只提供前後頁、頁次/總數;0~20不顯示換頁,多頁first/last按鈕正確。改搜尋/篩選回第1頁。Search/filter/page只存目前page memory,close/reopen保留,reload/navigation重設;不存URL、history、local/sessionStorage、Backend或WS。
不提供page-size、頁碼列、直接跳頁、Grid/Table、virtualization或完整inventory WS。
## 7. 其他區塊:只對接既有決策,不另建算法 Backend 開發內容
依主票畫面順序:
| 區塊 | 本票須遵守的關鍵規則 | 完整公式 | - [ ] 建立 current-building resolver,讓 Overview、ticket、WebSocket 與所有 query 共用唯一 enabled Building scope。
|---|---|---| - [ ] 建立 typed `DashboardController`/`DashboardService`/Overview DTO,按 section 回傳 status、freshness 與時間 metadata。
| 02 Attention | 明確既有狀態、非零類別數、類內distinct、固定順序/路由;只看current負載品質,無推論或新告警 | #1463 ATT-DEC-001~008 | - [ ] 建立最小 Dashboard ticket、WebSocket handler、session registry 與三種 typed message;使用區塊 replacement,不加入 fallback/retry/gap recovery。
| 03 KPI | enabled CP persisted連線;Connector可服務allowlist;canonical ACTIVE;ELIGIBLE distinct/未插槍互斥;today Usage重用;上月全COMPLETE金額。全部REST、不重複功率 | #1486 KPI-DEC-001~011 | - [ ] 建立 Connector summary/Top-8 共用分類排序 service,供 Overview 與 `CONNECTOR_LIVE_BLOCK_UPDATED` 使用。
| 04 - [ ] 擴充既有 Connector search:Charge Point ID/Charge Group 條件、`DASHBOARD_PRIORITY` mode、固定 20 筆 pagination 及完整 Page metadata;保持既有管理頁 sorting 相容。
- [ ] 建立 Live Load | 60分鐘/1分、今天/15分、含今天7日/1小時;時間加權kW;OPEN與品質分離,未知功率null+PARTIAL;無插值/跨缺口線 | current/series read model與 `LIVE_LOAD_UPDATED` replacement,遵守 #1462 LOAD-DEC-001~007 | bucket/quality contract。
| 05 Group/Queue | 群組自身容量/Slots,不加總成案場契約容量;只顯示既有source、status、priority、reason、waitingSince/duration | #1462、#1423 UI-DEC-010/011 | - [ ] 聚合 Attention、KPI、Group、Usage、Settlement/Billing 等 REST-owned sections,遵守各 child issue 的既有公式,不新增作業邏輯。
| 07 Usage | 固定30日、COMPLETED最終Wh/開始日、正電量次數、Backend平均;不碰raw MeterValue/費率分段,不顯示時間使用率 | #1461 USG-DEC-001~010 | - [ ] 沿用既有 `dashboard` function,補 Overview/ticket API mapping、OpenAPI `@Schema`、method/DTO 註解與 security/contract tests。
| 08 結算流程 | 最近已存在資料期間、四列純資訊、Footer既有帳單/請款頁;不等同上月電費的固定上一日曆月 | #1460 BIL-DEC-001~003 | - [ ] 依 DEC-042 優先採 read-only aggregate,完成 A17 或具代表性等量資料的 EXPLAIN 與可重現 P50/P95 baseline;只有實測影響正常使用時才提出 query/普通 index 優化,不為固定秒數預先建立 Dashboard snapshot table、專用 cache、效能排程或新 constraint。
## 8. 工程分工與交付 Frontend 開發內容
| 負責方 | 交付內容 | - [ ] 重新實作 `/dashboard`,依既有 `dashboard` function 控制 menu/route;未授權時不載入或短暫顯示資料。
|---|---| - [ ] 建立單一 Overview query/300 秒 timer/手動刷新,以及一個 Dashboard socket hook;Dashboard unmount 時釋放資源。
| Backend/REST | current-building resolver、DashboardController/Service、typed Overview/各section DTO與source isolation、共用聚合。 | - [ ] 分離 REST-owned state 與兩個 WS-owned live blocks;socket 接手後,Overview refresh 不覆寫 live state。
| Backend/WS | ticket、handler、session registry、三種typed message,短效一次性授權與single-instance前提。 | - [ ] Header 分別呈現案場名稱、一般 REST 最後更新、WebSocket connection state及 source freshness;手動刷新不假裝會重連。
| Backend/Connector | 共用summary/Top-8 ranking;既有search條件、DASHBOARD_PRIORITY與20筆分頁,管理頁相容。 | - [ ] 實作 Connected、首次連線失敗、連線後中斷;中斷保留最後資料/時間並顯示已停止更新,不啟動 REST fallback。
| Backend/Load | current/三range歷史query與時間加權、open/finalized replacement,品質依#1462。 | - [ ] 實作 Connector 五項 summary、Top-8 responsive preview、detail、Full List Drawer、固定排序/分頁與 page-session state。
| Backend/權限與文件 | dashboard API mapping、OpenAPI @Schema、method與DTO欄位註解、security/contract tests。 | - [ ] 實作 KPI、Attention、Live Load、Group、Usage、Settlement 的 Loading/Empty/Partial/Error/Stale、RWD、keyboard與文字狀態。
| FE | /dashboard頁面、單一Overview hook/300秒timer與socket hook、REST/WS state分離、unmount釋放、全部畫面與states、RWD/keyboard/來源時間。 |
| QA/效能 | 權限/scope/typed contract、socket failure無備援、source隔離、各子票數據與RWD、baseline與首站驗證。 | - [ ] Production API/WebSocket error 不得 fallback 到 fixture。
不改OCPP、Queue/Rotation、Slot allocation、charging lifecycle、帳務計算或排程,不新增告警引擎、Dashboard inventory或業務mutation。
## 9. 效能與驗收 Acceptance Criteria
### 9.1 先量測,不先加架構(DEC-042) Scope、權限與資料隔離
A17或代表性等量資料執行aggregate - [ ] 同一套功能可部署至所有 EMS Branch,A17 不出現在程式判斷或固定資料中。
- [ ] 唯一 enabled Building 正常載入;零筆/多筆顯示設定異常並隱藏營運資料。
- [ ] Overview/ticket/socket 不接受 Frontend building selector;response/ticket/event 的 resolved `buildingId` 一致。
- [ ] 四個允許 role 可讀取 Overview/取得 ticket;未具 `dashboard` function 時 API 為 403、socket 無有效 ticket 時拒絕。
- [ ] Dashboard 及 deep links 不執行任何業務 mutation,也不洩漏 credential/個資。
### REST 更新
- [ ] 首載及每 300 秒最多一個 Overview request;手動刷新也只送一個,沒有各 section timer。
- [ ] 同一次 response 的 REST-owned sections 使用相同 `generatedAt`;section failure 不清空其他成功內容。
- [ ] Refresh error 只保留同 Building cache並顯示 `lastSuccessfulAt`;首次 error 不顯示 fixture/假 0。
- [ ] Socket 接手後,Overview timer與手動刷新不覆寫兩個 live blocks;socket 中斷時也不形成 REST fallback。
- [ ] Overview 效能依 DEC-042 完成 A17 或具代表性等量資料的 query EXPLAIN,量測Overview endpoint P50/P95,記錄環境、主要table row count/分布、暖機、併發、sample size、量測起訖,使結果可重現。 EXPLAIN、endpoint P50/P95 baseline 與完整測試條件紀錄;V1 不以固定 1.5 秒作產品 SLA。
避免N+1、無界查詢、重複聚合;只有量測證明影響正常使用,才提出query/普通index優化。**不把P95<1.5秒當V1 SLA,不先建snapshot table、專用cache、效能排程或新constraint**。跨案場SLA若日後需要另定資料量/負載模型。
### 9.2 驗收清單 WebSocket 簡化行為
- [ ] 唯一Building正常、零/多筆設定錯誤;request不含FE案場選擇、response/ticket/event一致,cache/event不跨scope。 Dashboard 本體只有一條案場 socket,且只更新 Connector 即時狀態與案場即時負載;完整清單及其他 sections 不走 WS。
- [ ] 四允許role與多role正向、未授權403、無有效ticket拒絕WS;無credential/個資洩漏或mutation。 三種 message 均為 typed section replacement,Frontend 不由 raw delta 重算 domain state。
- [ ] 首載/每300秒/手動每輪一個Overview,無各section timer;同response generatedAt一致、source失敗隔離。 首次連線失敗與連線後中斷都顯示明確提示;已有資料保留並標示已停止更新,無資料顯示 `—`,不清成 0。
- [ ] 初次Error不假0/fixture;refresh只保留同案場資料與時間,依各區品質規則顯示。 中斷後沒有 polling fallback、backoff/jitter、自動重連、sequence/gap recovery、visibility reconnect 或專用 Retry;整頁 reload 才重新連線。
- [ ] socket接手後,timer/手動Overview不覆寫live;斷線時也不形成fallback。 WebSocket 中斷不阻止其他 REST sections 依 5 分鐘週期更新。
### Connector
- [ ] 0/1/2/8/9/30/100 筆及 390/768/1024/1440px 均不破版;Top-8 payload 在 Desktop/Tablet/Mobile 分別顯示 8/6/4。
- [ ] Dashboard本體一條raw socket、三typed訊息只更新兩區,full list走REST、detail按需釋放。 五個 bucket 互斥、合計等於 total、NEEDS_ATTENTION 等於 abnormalTotal;非空包含零值五項,空案場顯示 Empty State。
- [ ] 首次失敗/後續中斷都有提示,最後資料/時間保留、無資料—;無backoff、jitter、自動/visibility重連、sequence/gap recovery、專用Retry。 五個 summary items 不可點擊、不進 Tab;preview card 與「查看全部」仍分別支援 detail/完整清單。
- [ ] 其他REST區塊在socket中斷後照常;整頁reload才新建連線。 Preview 與 Full List 共用固定 priority hierarchy;同層級順序穩定,搜尋/篩選後仍先排序再分頁。
- [ ] 五bucket互斥、Top-8與8/6/4、priority穩定、summary不可點,0/1/2/8/9/30/100與各斷點正確。 Drawer 固定每頁 20 筆;30/100 筆為 2/5 頁,0~20 筆隱藏換頁控制,第一/最後一頁按鈕狀態正確。
- [ ] search完整集合filter→sort→20分頁、30/100為2/5頁、0~20無換頁;page memory正確,無額外控制,管理頁sorting無回歸。 Drawer 無 sorting/page-size/direct-jump/view-toggle/virtualization;close/reopen 保留搜尋/篩選/頁碼,reload/navigation 重設。
- [ ] Load、Attention、KPI、Usage、Billing公式與來源狀態符合第7節子票;無額外range、時間使用率或預估費用。 `DASHBOARD_PRIORITY` 不改變既有 `/connector/list` 欄位排序;完整清單永遠透過 REST,不擴大 Dashboard WS payload。
### 其他區塊與品質
- [ ] Live Load 三種 range、X/Y 軸、bucket、time-weighted average、0/null/PARTIAL 規則符合 #1462;不顯示推算的案場容量/利用率。
- [ ] 320px以上無頁面水平捲軸;390/768/1024/1440可讀;文字狀態/focus/對比符合WCAG 2.2 AA基本要求。 Attention 類別、count、順序、Empty/Partial/Error及固定 deep link 符合 #1463,且不新增告警或 workflow。
- [ ] EXPLAIN與P50/P95條件完整,無固定秒數SLA。 六張 KPI 的 scope、公式、0/null/status、RWD與固定 deep link 符合 #1486;全部由 REST 更新。
- [ ] production無API/WS error→fixture 30 日 Usage 與四步驟結算符合 #1461/#1460;不加入額外 range、時間使用率或 billing WebSocket。
- [ ] 狀態不只靠顏色,keyboard focus、status message與文字對比符合 WCAG 2.2 AA 基本要求;320px 以上無頁面級水平捲軸。
- [ ] Production build 不包含 API/WebSocket error → fixture fallback。
功能實作E2E impact為Add,依主票與各子票新增/更新UI、REST/WS failure、權限、RWD與大量資料case,按priority/trigger納release pack;本次不產生PASS/FAIL或宣稱已跑測試。 ## E2E Test Impact
- Dashboard 正式實作屬 **Add**;需由實作 ticket 新增/更新 UI、REST contract、WebSocket failure、權限、RWD與 Connector large-data cases,並依 priority/trigger 納入 release pack。
- 本次 description 去矛盾沒有改變已確認產品行為,分類為 **No catalog change**;不產生 run-specific PASS/FAIL,也不宣稱已完成 E2E。
## 10. 決策追溯:現行與已撤回分開 現行決策索引
| 決策 | 現行有效內容/本文位置 | - DEC-002:保留 Overview bootstrap;其 fallback/重連部分由 DEC-031 取代。
|---|---| - DEC-006:Backend 管理 source cutoff 原則保留;override 最終以 DEC-033 為準。
| DEC-002 | 保留Overview bootstrap;第3節;fallback部分撤回 |
| DEC-006 | Backend管理source cutoff原則;第5節,設定範圍依033 |
| DEC-008 | - DEC-008:保留 raw WS;第4節,sequence/gap recovery撤回 | WebSocket;sequence/gap recovery 部分由 DEC-031 取代。
| DEC-009~030 | 未被取代的部署/只讀/公式/顯示/權限保留;非兩區WS方案由031取代 | - DEC-009~030:保留各項部署前提、唯讀邊界、資料公式、顯示及權限;其中非 Connector/Live Load sections 使用 WebSocket 的部分由 DEC-031 取代。
| DEC-029/030 | 唯一enabled Building與dashboard function;第2節 | - DEC-031:現行資料通道與斷線簡化方案。
| DEC-031/032 | 兩區WS、無備援;共用5分鐘Overview;第1、3~4節 | - DEC-032:現行單一 Overview/5 分鐘/手動刷新規則。
| DEC-033 | 全系統per-source defaults、無案場override;第5節 | - DEC-033:現行 Backend per-source global freshness defaults;V1 無 per-building override。
| DEC-034~041 | - DEC-034~041:現行 Connector summary/preview/Drawer記憶體/固定分頁排序;第6節 | preview、summary、Drawer state、固定 pagination與固定 priority sorting。
| DEC-042 | 可重現效能baseline而非1.5秒SLA;第9節 | - DEC-042:現行 Overview 效能驗證方式;採可重現 baseline,不承諾固定 1.5 秒 SLA,也不預先加入 snapshot table/cache/排程。
| 已撤回提案 | 取代規則 |
|---|---|
| ## 已取代的討論歷程索引
- DEC-001 FE選building | 的 Frontend building selection 由 DEC-029 Backend唯一案場 | 取代。
| - DEC-003 的 capped exponential backoff/60秒fallback | backoff/60 秒 REST fallback 由 DEC-031 無備援與自動重連 | 取代。
| - DEC-004 固定5秒coalescing產品契約 | 的固定 5 秒產品 coalescing contract 由 DEC-031 只容許內部資源保護,不承諾秒數 | 取代;Backend 僅可作內部資源保護,不列固定秒數驗收。
| - DEC-005 的 Operational Event P95 2秒SLO | 2 秒 SLO 由 DEC-031 不列V1驗收承諾 | 取代。
| - DEC-007 的 per-building stale cutoff override | 由 DEC-033 全系統來源預設 | 取代。
| - DEC-014 的 reconnect/gap recovery部分 | DEC-031撤回;REST歷史+WS區間ownership保留 | recovery 部分由 DEC-031 取代;REST series + WS open-bucket replacement 的資料 ownership 保留。
| DEC-021/023~028要求Attention/KPI走WS部分 | DEC-031/032改共用REST;各公式/呈現仍保留 | - DEC-021/023~028 中要求 Attention/KPI 走 WebSocket 的部分由 DEC-031 取代;其資料公式與 presentation contract 保留。
歷史討論仍在原票J5641及更早journals,本次不刪除。現行架構已整理至DEC-042,完成全Dashboard一致性review後才整併v1.0;不以本次文字整理視為已進入實作。
## 需求管理與本次編輯 需求管理狀態
- 本票仍是已確認需求的追蹤票,不代表已完成開發或通過測試;指派、狀態、進度、附件與父子關係均維持原狀。 Parent:#1422
- 全 Related:#1423(Connector)、#1460(結算)、#1461(Usage)、#1462(Live Load)、#1463(需留意事項)、#1486(KPI)
- Requirement status:**現行架構決策已整理至 DEC-042;Connector 子需求已鎖定,其他 Dashboard 完成一致性 review 後,才依 #1422 DOC-DEC-001 發布同版號 PDF/Markdown/HTML 套件;現有 v0.3 Draft 不覆寫,本次不發布新版附件。 決策待整體一致性稽核後再整併最終 v1.0 文件**
- 2026-09-14:僅重整 Description、說明與排版,將已確認決策併入對應畫面/工程工作;決策編號保留供追溯。E2E impact:No catalog change(沒有修改產品行為、公式或 API 契約);功能實作時仍須遵循主票與本票驗收。 Issue status:維持 New/0%,不代表已進入開發
- Implementation tickets:整體需求鎖定後才建立 FE/BE/QA child issues
返回