討論 #1424
是由 陳國瑋 於 15 天 前更新
> **閱讀基準:** [主需求 #1422](https://redmine.sylksoft.com/issues/1422) 與 [HTML Mock v0.4/附件 #1121](https://redmine.sylksoft.com/attachments/1121)。以下沿用主票區塊編號,依畫面由上到下、由左到右說明。Mock v0.3/附件 #1117](https://redmine.sylksoft.com/attachments/1117)。以下沿用主票區塊編號,依畫面由上到下、由左到右說明。Mock 數字皆為假資料;功能適用所有案場,A17 不是固定規則。
## 本票要完成什麼:讓整張 Dashboard 用簡單、一致的方式取得資料
本票負責**案場範圍、權限、REST/WebSocket 分工、資料狀態與前後端契約**。畫面/公式分別由 #1423、#1460、#1461、#1462、#1463、#1486 定義,不在本票建立第二套算法。
既有 GET /api/connector/dashboard 只有部分Connector統計;管理頁search與單支detail socket可重用,但不能直接當成整張Dashboard的資料來源。需要新的typed Overview與最小Dashboard socket。
**先記住三件事:**
1. 一次Overview取得一般資料,首載/每5分鐘/手動刷新。
2. 一條Dashboard WebSocket只更新「06 Connector即時狀態」與「04 案場即時負載」。
3. Socket連不到就提示並保留資料;不另建REST備援、自動重連或gap recovery。
## 1. 先依畫面順序看資料通道
「REST-owned」表示由Overview負責更新;「WS-owned live state」表示socket接手後只由socket負責更新,兩者不能互相覆寫。(DEC-031/032)
| 主票位置/畫面 | 首次資料 | 後續更新 | Dashboard socket斷線時 |
|---|---|---|---|
| 01 案場名稱/一般資料時間 | Overview | 共用5分鐘/手動Overview | 照常更新一般資料與其品質 |
| 02 需留意事項 | Overview | 同上,REST-only | 照常;不改成其他備援模式 |
| 03 六張KPI | Overview | 同上,全部REST | 照常;不訂閱KPI WS事件 |
| 04 目前功率/open buckets | Overview bootstrap | LIVE_LOAD_UPDATED replacement | 保留最後值/時間、標停止更新;無資料— |
| 04 即時/今日/7日完整歷史 | 一次Overview給三組 | 同一Overview更新歷史;WS另負責open/跨界finalized | 一般歷史可刷新,但不得冒充即時recovery |
| 05 群組容量/Slots/Queue摘要 | Overview | 共用5分鐘/手動Overview | 照常,非WS |
| 06 五項摘要/Top-8預覽 | Overview bootstrap | CONNECTOR_LIVE_BLOCK_UPDATED整區replacement | 保留最後值/時間、標停止更新;無資料— |
| 07 30日用量、03-5今日已完成量 | 同一Overview/Usage read model | 共用5分鐘/手動Overview | 照常,非Usage WS |
| 08 結算流程、03-6上月電費 | Overview | 共用5分鐘/手動Overview | 照常,非Billing WS |
按需打開的介面另外處理,不混入首頁更新:
| 介面 | 資料通道 |
|---|---|
| 06「查看全部」Full List Drawer | 開啟、搜尋、「摘要分類」/Charge Group 篩選、換頁時 POST /api/connector/search,REST server pagination |
| 單支Connector detail | 既有detail API;打開時才使用既有單Connector socket,關閉釋放 |
「一條Dashboard socket」指首頁本體,不是禁止按需使用既有單支detail flow;也不能因此替每張preview card建立socket。
## 2. 整體資料流程與scope
```text
登入使用者
↓
既有 SS3A dashboard function 授權
↓
Backend 解析本 Branch 唯一 enabled Building
├─ 不是恰好1筆 → 設定異常,不載入營運數字
└─ 恰好1筆
├─ GET /api/dashboard/overview
│ ├─ 一般區塊+三組歷史
│ └─ 兩個live blocks初始畫面
└─ 短效、一次性Dashboard ticket
↓
raw WebSocket
├─ DASHBOARD_LIVE_SNAPSHOT
├─ CONNECTOR_LIVE_BLOCK_UPDATED
└─ LIVE_LOAD_UPDATED
```
### 2.1 案場由Backend決定(DEC-029)
- 每個EMS Branch恰好一筆enabled Building;零筆/多筆回明確configuration error,FE顯示整頁「案場設定異常」並隱藏營運資料。
- 不任選第一筆、不跨Building加總、不顯示假0;不沿用任選預設案場的查詢方式。
- Overview/ticket request、WebSocket URL不傳FE指定buildingId;Drawer的Dashboard read mode也依相同resolver。
- Overview response、ticket binding、event envelope明確帶相同Backend-resolved buildingId;FE只套用相符資料,cache按buildingId隔離。
- Header/Sidebar顯示Backend案場識別,timezone供日期與時間呈現;不提供site selector。
- A17只用來驗證,不能出現在固定數字/公式/cutoff/query分支。
### 2.2 權限沿用dashboard function(DEC-030)
| 條件 | 應有結果 |
|---|---|
| administrator/power_user/association/dealer,經既有dashboard function授權 | 相同唯讀Dashboard、Overview與ticket能力 |
| 只有adm_user/member/hq/engineer,未取得dashboard function | Overview/ticket為403;無有效ticket不能建立Dashboard socket |
| 多role使用者已有允許權限 | 依既有function機制授權,不因同時擁有其他role拒絕 |
不新增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契約:一個週期、一份一般資料
新增typed GET /api/dashboard/overview,回傳案場識別、generatedAt、各source的品質/時間及各section。(DEC-002保留部分/DEC-032)
| 時機 | Request |
|---|---|
| 頁面首次載入 | 一次Overview |
| Dashboard保持掛載期間每300秒 | 每輪一次Overview,不是每卡各一次 |
| 使用者手動刷新 | 一次Overview,不動socket |
| 卸載Dashboard | 釋放timer與socket資源 |
同一成功response的REST-owned sections共用generatedAt;需要各別品質判斷的來源另外回dataStatus、freshness、updatedAt及必要lastSuccessfulAt。
### 3.1 接手前後不得覆寫錯的資料
```text
首載REST bootstrap
↓
兩個live blocks可以先顯示初始資料
↓
收到DASHBOARD_LIVE_SNAPSHOT → socket接手
↓
之後Overview刷新:
一般區塊/歷史 → 可更新
Connector live/目前功率與open buckets → 不覆寫
```
只有整頁reload才重取ticket、重新嘗試socket。手動刷新不重建連線;socket中斷也不解除live ownership讓REST冒充備援。
### 3.2 REST失敗不代表全頁歸零
單一source失敗只影響相依section,其他成功部分繼續顯示,Overview標PARTIAL或可診斷source status。初次無成功資料顯示Error/—;後續失敗可依各區契約保留**同building**成功cache,附更新失敗與lastSuccessfulAt,不假裝最新。
不能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欄位 | 用途 |
|---|---|
| schemaVersion | FE判斷是否支援此契約版本 |
| type | 下表三種訊息之一 |
| buildingId | 案場隔離,不接受不符scope資料 |
| occurredAt | 事件資料時間 |
| data | 對應type的typed區塊payload |
不傳raw DB row、JWT、ticket、currentIdTag或不必要個資;前端不對不支援schema或錯scope猜測套用。
### 4.2 訊息內容
| type | 什麼時候/送什麼 | FE如何套用 |
|---|---|---|
| DASHBOARD_LIVE_SNAPSHOT | 建連後,兩個live blocks完整目前狀態 | 接手目前live state,不等於重送整個Dashboard/全部歷史 |
| CONNECTOR_LIVE_BLOCK_UPDATED | Connector五項summary+totals+Backend排序Top-8 | 整個區塊replacement,FE不自己算分類/排名 |
| LIVE_LOAD_UPDATED | currentPowerKw、LIVE/TODAY/SEVEN_DAYS三種open bucket;跨界可帶剛結束finalized | 目前值與bucket各自取代,按buildingId+rangeCode+bucketStartAt合併 |
不用細碎delta讓FE重算total、bucket、priority或平均功率。Backend可以內部debounce/coalescing保護資源,但不承諾固定5秒更新或P95 2秒SLO,也不為這個要求先建專用架構。
### 4.3 連不到/中斷時就明示
| 情況 | 頁首與兩個live blocks |
|---|---|
| 首次連線失敗 | 明示無法連線;已有初始資料可保留並標未即時更新,沒有資料則—與「即時資料暫時無法取得」。 |
| 已連線後中斷 | 明示連線中斷,保留最後成功資料/時間並標「已停止更新」。 |
| 其他REST區塊 | 仍依共用Overview正常更新,不因socket中斷停止。 |
**不做:** REST polling fallback、定時更新模式切換、自訂capped exponential backoff、jitter、背景自動重連、visibility reconnect、sequence/gap recovery、專用Retry。使用者整頁reload才再嘗試。
Connection state與source freshness獨立:連著不代表資料FRESH,斷線也不改寫Backend已回傳的來源品質。
### 4.4 部署前提(DEC-009)
V1為**單一active ems_branch instance**。未來多instance/HA的session fan-out與授權一致性另案設計,不在本功能預做。
## 5. 品質欄位:0、未知、失敗、過期分開
| 狀態 | 契約與畫面 |
|---|---|
| COMPLETE且數值0 | 成功確認的0,不是缺值 |
| null | 未知,不轉0 |
| PARTIAL且Backend有安全值 | 可顯示,明示不完整;FE不自行加出部分合計 |
| PARTIAL無安全值 | null/—,例如不完整的案場功率、未全數完成的上月金額 |
| ERROR | 文字說明來源無法取得,不冒充正常 |
| STALE | 文字標過期與資料時間,不使用正常成功樣式 |
(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契約
Backend從完整案場Connector集合分類、排序,再回固定五個bucket、total、abnormalTotal與最多8筆items。(DEC-034~038,詳細#1423)
| 不變條件 | 說明 |
|---|---|
| 五類順序 | NEEDS_ATTENTION、CHARGING、WAITING、AVAILABLE、OTHER |
| 分類互斥 | 每Connector恰好一類,count總和=total;NEEDS_ATTENTION.count=abnormalTotal |
| 原始事實 | OCPP connection、runtime、Queue分層保留,不被displayBucket取代 |
| Unknown/NOT_PLUGGED | Unknown不當AVAILABLE/OTHER;NOT_PLUGGED依#1423既定mapping歸OTHER |
| 非空/空 | 非空固定五項含0;空時FE顯示Empty,Backend仍五個0 |
| 摘要互動 | 純資訊不可點擊,不進Tab、不發request |
| FE預覽 | Desktop≥1240顯示8、Tablet640~1239顯示6、Mobile<640顯示4;不傳viewport/requestedLimit |
| Item metadata | displayBucket、isAbnormal、severity、priorityReason;FE不重判 |
| Footer | FE只依totals與visible slice算隱藏總數/異常數,不從Top-8推完整統計 |
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
```text
完整集合(Backend限制目前案場)
↓ 全部條件filter
↓ 共用DASHBOARD_PRIORITY sort
↓ 固定size=20 paginate
↓ Spring Page回應
```
沿用POST /api/connector/search、SearchConnectorDto的page/size與Spring Page metadata;補 keyword 所需的 Charge Point ID/車位、`displayBucket`、Charge Group、OpenAPI及contract tests。不得先分頁才filter/sort。
Dashboard Full List 的分類欄位命名為「摘要分類」,request 使用 `displayBucket`;未傳為全部,值限 NEEDS_ATTENTION/CHARGING/WAITING/AVAILABLE/OTHER。它直接重用Summary分類,不把 Connector runtime status 與 Queue status 混成另一組選項。既有管理頁的原始 `status` 條件維持相容。(DEC-043)
Drawer固定sortMode=DASHBOARD_PRIORITY,不提供使用者sidx/sord控制;/connector/list未用此mode時保留原欄位排序。(DEC-039~041)
Request 最小欄位為 page、固定 size=20、keyword、可選 displayBucket、可選 chargeGroupId、固定 sortMode;不得傳 buildingId。Response 每筆至少回 connectorId、chargePointId、location/floor/parkingSpaceNo、chargeGroup、displayBucket、isAbnormal/severity/priorityReason、OCPP connection/runtime/Queue 分層狀態、nullable currentPowerKw、updatedAt/freshness;外層沿用 Spring Page metadata。完整欄位表以 #1423 第6.3節為準。
只提供前後頁、頁次/總數;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. 其他區塊:只對接既有決策,不另建算法
依主票畫面順序:
| 區塊 | 本票須遵守的關鍵規則 | 完整公式 |
|---|---|---|
| 02 Attention | 明確既有狀態、非零類別數、類內distinct、固定順序/路由;只看current負載品質,無推論或新告警 | #1463 ATT-DEC-001~008 |
| 03 KPI | enabled CP persisted連線;Connector可服務allowlist;canonical ACTIVE;ELIGIBLE distinct/未插槍互斥;today Usage重用;上月全COMPLETE金額。全部REST、不重複功率 | #1486 KPI-DEC-001~011 |
| 04 Live Load | 60分鐘/1分、今天/15分、含今天7日/1小時;時間加權kW;OPEN與品質分離,未知功率null+PARTIAL;無插值/跨缺口線 | #1462 LOAD-DEC-001~007 |
| 05 Group/Queue | 群組自身容量/Slots,不加總成案場契約容量;只顯示既有source、status、priority、reason、waitingSince/duration | #1462、#1423 UI-DEC-010/011 |
| 07 Usage | 固定30日、COMPLETED最終Wh/開始日、正電量次數、Backend平均;不碰raw MeterValue/費率分段,不顯示時間使用率 | #1461 USG-DEC-001~010 |
| 08 結算流程 | 最近已存在資料期間、四列純資訊、Footer既有帳單/請款頁;不等同上月電費的固定上一日曆月 | #1460 BIL-DEC-001~003 |
## 8. 工程分工與交付
| 負責方 | 交付內容 |
|---|---|
| Backend/REST | current-building resolver、DashboardController/Service、typed Overview/各section DTO與source isolation、共用聚合。 |
| Backend/WS | ticket、handler、session registry、三種typed message,短效一次性授權與single-instance前提。 |
| Backend/Connector | 共用summary/Top-8 ranking;既有search補 displayBucket/keyword/Charge Group、最小response欄位、DASHBOARD_PRIORITY與20筆分頁,管理頁原始status/排序相容。 |
| Backend/Load | current/三range歷史query與時間加權、open/finalized replacement,品質依#1462。 |
| Backend/權限與文件 | dashboard API mapping、OpenAPI @Schema、method與DTO欄位註解、security/contract tests。 |
| FE | /dashboard頁面、單一Overview hook/300秒timer與socket hook、REST/WS state分離、unmount釋放、全部畫面與states、RWD/keyboard/來源時間。 |
| QA/效能 | 權限/scope/typed contract、socket failure無備援、source隔離、各子票數據與RWD、baseline與首站驗證。 |
不改OCPP、Queue/Rotation、Slot allocation、charging lifecycle、帳務計算或排程,不新增告警引擎、Dashboard inventory或業務mutation。
## 9. 效能與驗收
### 9.1 先量測,不先加架構(DEC-042)
A17或代表性等量資料執行aggregate query EXPLAIN,量測Overview endpoint P50/P95,記錄環境、主要table row count/分布、暖機、併發、sample size、量測起訖,使結果可重現。
避免N+1、無界查詢、重複聚合;只有量測證明影響正常使用,才提出query/普通index優化。**不把P95<1.5秒當V1 SLA,不先建snapshot table、專用cache、效能排程或新constraint**。跨案場SLA若日後需要另定資料量/負載模型。
### 9.2 驗收清單
- [ ] 唯一Building正常、零/多筆設定錯誤;request不含FE案場選擇、response/ticket/event一致,cache/event不跨scope。
- [ ] 四允許role與多role正向、未授權403、無有效ticket拒絕WS;無credential/個資洩漏或mutation。
- [ ] 首載/每300秒/手動每輪一個Overview,無各section timer;同response generatedAt一致、source失敗隔離。
- [ ] 初次Error不假0/fixture;refresh只保留同案場資料與時間,依各區品質規則顯示。
- [ ] socket接手後,timer/手動Overview不覆寫live;斷線時也不形成fallback。
- [ ] Dashboard本體一條raw socket、三typed訊息只更新兩區,full list走REST、detail按需釋放。
- [ ] 首次失敗/後續中斷都有提示,最後資料/時間保留、無資料—;無backoff、jitter、自動/visibility重連、sequence/gap recovery、專用Retry。
- [ ] 其他REST區塊在socket中斷後照常;整頁reload才新建連線。
- [ ] 五bucket互斥、Top-8與8/6/4、priority穩定、summary不可點,0/1/2/8/9/30/100與各斷點正確。
- [ ] search完整集合依 keyword/displayBucket/Charge Group filter→sort→20分頁、30/100為2/5頁、0~20無換頁;六個「摘要分類」選項、最小response契約與page memory正確,管理頁原始status/sorting無回歸。
- [ ] Load、Attention、KPI、Usage、Billing公式與來源狀態符合第7節子票;無額外range、時間使用率或預估費用。
- [ ] 320px以上無頁面水平捲軸;390/768/1024/1440可讀;文字狀態/focus/對比符合WCAG 2.2 AA基本要求。
- [ ] EXPLAIN與P50/P95條件完整,無固定秒數SLA。
- [ ] production無API/WS error→fixture fallback。
功能實作E2E impact為Add,依主票與各子票新增/更新UI、REST/WS failure、權限、RWD與大量資料case,按priority/trigger納release pack;本次不產生PASS/FAIL或宣稱已跑測試。
## 10. 決策追溯:現行與已撤回分開
| 決策 | 現行有效內容/本文位置 |
|---|---|
| DEC-002 | 保留Overview bootstrap;第3節;fallback部分撤回 |
| DEC-006 | Backend管理source cutoff原則;第5節,設定範圍依033 |
| DEC-008 | raw WS;第4節,sequence/gap recovery撤回 |
| DEC-009~030 | 未被取代的部署/只讀/公式/顯示/權限保留;非兩區WS方案由031取代 |
| DEC-029/030 | 唯一enabled Building與dashboard function;第2節 |
| DEC-031/032 | 兩區WS、無備援;共用5分鐘Overview;第1、3~4節 |
| DEC-033 | 全系統per-source defaults、無案場override;第5節 |
| DEC-034~041 | Connector summary/preview/Drawer記憶體/固定分頁排序;第6節 |
| DEC-042 | 可重現效能baseline而非1.5秒SLA;第9節 |
| DEC-043 | Full List「摘要分類」使用 displayBucket;request/response 最小契約;第1、6、8~9節 |
| 已撤回提案 | 取代規則 |
|---|---|
| DEC-001 FE選building | DEC-029 Backend唯一案場 |
| DEC-003 capped exponential backoff/60秒fallback | DEC-031 無備援與自動重連 |
| DEC-004 固定5秒coalescing產品契約 | DEC-031 只容許內部資源保護,不承諾秒數 |
| DEC-005 Operational Event P95 2秒SLO | DEC-031 不列V1驗收承諾 |
| DEC-007 per-building cutoff override | DEC-033 全系統來源預設 |
| DEC-014 reconnect/gap recovery部分 | DEC-031撤回;REST歷史+WS區間ownership保留 |
| DEC-021/023~028要求Attention/KPI走WS部分 | DEC-031/032改共用REST;各公式/呈現仍保留 |
歷史討論仍在原票J5641及更早journals,本次不刪除。現行架構已整理至DEC-043,完成全Dashboard一致性review後才整併v1.0;不以本次文字整理視為已進入實作。
## 需求管理與本次編輯
- 本票仍是已確認需求的追蹤票,不代表已完成開發或通過測試;指派、狀態、進度、附件與父子關係均維持原狀。
- v0.4 Draft 文件套件已依 全 Dashboard 完成一致性 review 後,才依 #1422 DOC-DEC-001 發布並保留 v0.2/v0.3;目前閱讀基準為 PDF #1119、Markdown #1120、HTML #1121。此版仍是 review Draft,尚非 v1.0。 發布同版號 PDF/Markdown/HTML 套件;現有 v0.3 Draft 不覆寫,本次不發布新版附件。
- 2026-09-14:僅重整 Description、說明與排版,將已確認決策併入對應畫面/工程工作;決策編號保留供追溯。E2E impact:No catalog change(沒有修改產品行為、公式或 API 契約);功能實作時仍須遵循主票與本票驗收。
- 2026-09-16:依 Ken 確認新增 DEC-043;Full List Drawer 使用 displayBucket 作為「摘要分類」,明列 request/response 最小契約並保留管理頁原始 status 相容性。本輪只更新需求,不變更 WebSocket、作業邏輯、Issue 狀態/進度或附件。
返回