專案

一般

配置概況

討論 #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

返回