# Backend Admin 營運 Dashboard 需求說明書

> 文件版本：v0.4 Draft  
> 文件日期：2026-09-16  
> 追蹤議題：Redmine #1422  
> 適用範圍：所有 EMS Branch 案場；A17 僅為第一個資料基準與展示案例  
> 文件狀態：需求討論快照，尚未進入 v1.0 需求鎖定與開發  
> 配套 Mock：`backend-admin-dashboard-mock-v0.4.html`

本文件是目前已確認需求的完整快照，供 Product、FE、Backend、QA 與維運共同 review。Mock 內所有數字均為 UI 假資料，不代表 A17 或其他案場的即時營運結果。

---

## 1. 文件管理

### 1.1 版號規則

| 版號 | 用途 | 是否可直接進入開發 |
|---|---|---|
| v0.x Draft | 需求討論與 review 快照；每次保留 PDF、Markdown、HTML Mock 三份同版附件 | 否 |
| v1.0 | 所有必要需求確認後的開發基準 | 是，仍須依 Redmine SOP 通過規劃關卡 |
| v1.x | v1.0 後的需求釐清或核准變更 | 依該版變更內容判定 |

每一個 review checkpoint 都必須產出相同版號、相同日期的 PDF、Markdown 與 HTML Mock。新版本一律新增至 Redmine #1422，不覆寫、不刪除舊附件。

### 1.2 版本紀錄

| 版號 | 日期 | 狀態 | 內容 |
|---|---|---|---|
| v0.2 Draft | 2026-07-25 | 已封存於 #1422 | 舊版 PDF #1108、Markdown #1109、HTML #1110 |
| v0.3 Draft | 2026-09-07 | 已封存於 #1422 | 納入當時已確認的跨案場、資料來源、WebSocket、KPI、Connector、負載、用量與帳務決策 |
| v0.4 Draft | 2026-09-16 | 本次 review 快照 | 修正 v0.3 一致性稽核 PKG-01～10；確認 Drawer 定義、「摘要分類」displayBucket、Full List search 最小契約與 canonical fixture |

### 1.3 本版定位

- 本版是「顯示資訊」的 Dashboard，不新增控制充電、重送命令、補帳、修改排程或調整案場設定的操作。
- 本版保留必要的即時資訊，但不追求監控中心等級的秒級正確性、斷線容錯或專用告警系統。
- 所有案場共用同一套產品規則。A17 的 Connector 數量、群組數量與歷史資料只能作為驗證尺度，不可寫死成跨案場規則。

### 1.4 v0.4 主要修訂

- 需留意事項使用現行固定名稱；結算區顯示資料年份與帳單／請款發票入口。
- 30 日用量 Mock 使用完整 30 個日期桶，圖表、合計、平均值與今日 KPI 由同一組假資料推導，並明列 X、Y 軸。
- Connector 預覽卡分層顯示 CP OCPP connection、Connector runtime 與 Queue，另顯示 CP ID、功率、資料時間與 freshness。
- 點預覽卡先開啟單支詳情 Drawer；「查看全部」開啟 Full List Drawer。兩者都不是新頁面，也不執行作業邏輯。
- Full List 篩選名稱改為「摘要分類」，直接使用 displayBucket；首頁五項摘要維持純資訊、不可點擊。
- 首頁 summary、Top-8、abnormalTotal 與完整清單由同一份 canonical fixture 推導；Tablet Full List Drawer 於 1239 px 以下使用全螢幕。

## 2. 背景、目標與成功條件

### 2.1 背景

Backend Admin 現有 Dashboard 為空白頁。EMS 已有 Charge Point、Connector、Transaction、MeterValue、Queue、Settlement 與 Invoice 等資料，但缺少一個讓案場管理人員快速掌握營運狀況的首頁。

### 2.2 目標

使用者進入 Dashboard 後，應能在不執行任何控制動作的前提下快速回答：

1. 案場目前是否有明顯異常或資料不可靠？
2. Charge Point 與 Connector 目前大致處於什麼狀態？
3. 現在充電負載多少，各群組容量與可用 slots 如何？
4. 最近 30 日的充電量與使用次數如何？
5. 最近一個帳期的結算流程進行到哪裡？
6. 上一個完整曆月的電費是否已完整結算、金額多少？

### 2.3 V1 成功條件

- 有 `dashboard` function 權限的使用者可正常開啟頁面。
- 每個 Branch 在 V1 僅有一個 enabled Building 時，可由 Backend 自動解析案場，不要求 FE 傳 `buildingId`。
- 一般資料可於初次載入、每 5 分鐘及手動重新整理時更新。
- Connector 即時狀態與案場即時負載可由同一條 Dashboard WebSocket 更新。
- WebSocket 無法連線時，使用者看得到明確提示、最後更新時間與最後一份資料；其他 REST 區塊仍可使用。
- 1、2、12、30 個 Connector 都能在同一 UI 結構內合理顯示，頁面不因數量改變而破版。
- 390、768、1024、1440 px 寬度不產生整頁水平捲動。
- 缺資料、部分資料、錯誤與真正的零值不會被混為一談。

## 3. 使用者、權限與案場範圍

### 3.1 權限

Dashboard 沿用既有 SS3A function/API mapping，功能代碼為 `dashboard`。

- 預期可被授予 Dashboard 的既有角色：`administrator`、`power_user`、`association`、`dealer`。
- `adm_user`、`member`、`hq`、`engineer` 不因角色名稱而自動取得 Dashboard 權限。
- Backend API 仍須做授權檢查；FE 隱藏選單不是安全邊界。
- 未授權時回傳或呈現 403，不顯示其他案場資料。

角色與 function 的關係應透過既有權限資料維護，不在 Dashboard 程式碼中以角色名稱硬編碼授權判斷。

### 3.2 Building 解析

V1 規則：一個 Branch 必須恰好有一個 enabled Building。

| 狀況 | 行為 |
|---|---|
| 恰好 1 個 enabled Building | Backend 自動解析並回傳 Dashboard 資料 |
| 0 個 enabled Building | 回傳案場設定錯誤，Dashboard 不載入資料 |
| 超過 1 個 enabled Building | 回傳案場設定錯誤，Dashboard 不自行猜測 |

FE 不傳 `buildingId`，也不在 V1 提供 Building 下拉選單。未來若 Branch 支援多 Building，應另開需求擴充契約。

## 4. 範圍

### 4.1 In Scope

- 案場頁首、資料更新狀態與手動重新整理。
- 需留意事項。
- 六個營運 KPI。
- 案場即時負載與三個時間範圍圖表。
- 群組容量與 slots。
- Connector 即時狀態摘要、重點卡片與完整清單 drawer。
- 最近 30 日充電用量。
- 最近一個既有結算帳期的四步驟帳務流程。
- REST API、Dashboard WebSocket、授權、資料品質、錯誤狀態與基本驗收。

### 4.2 Out of Scope

- 從 Dashboard 控制充電、重啟設備、改變 Connector 狀態或手動重送 OCPP command。
- 從 Dashboard 修改 Queue、priority、slot、排程或離峰設定。
- 從 Dashboard 執行補帳、重算、付款、開立發票或修正帳務。
- 新增 Operational Event SLA、處理時限、逾時升級或 on-call 規則。
- 為 Dashboard 新增專用 snapshot table、cache、排程器、監控平台、告警與 fallback polling。
- 自 Dashboard 推導「離線充電」、「待對帳」或其他會改變既有業務狀態的結論。
- 以 A17 的數量、時間或設備特性作為所有案場固定設定。

## 5. 頁面資訊架構

頁面由上到下固定為：

1. 案場名稱、即時連線狀態、一般資料更新時間與手動重新整理。
2. 需留意事項。
3. 六個 KPI。
4. 案場即時負載。
5. 群組容量與 slots。
6. Connector 即時狀態。
7. 30 日充電用量。
8. 帳務結算流程。

區塊可以因 Empty、Partial、Error 或權限狀態改變內容，但不可任意改變順序。Dashboard 是資訊總覽；只有明確定義的卡片或文字連結可導向既有頁面，不在首頁加入操作按鈕。

## 6. 資料取得架構

### 6.1 簡化原則

Dashboard 只使用兩條資料通道：一支 Overview REST API 與一條 Dashboard WebSocket。

| 區塊 | 初始資料 | 後續更新 | WebSocket 斷線時 |
|---|---|---|---|
| 需留意事項 | Overview REST | 每 5 分鐘或手動 refresh | 不受影響 |
| 六個 KPI | Overview REST | 每 5 分鐘或手動 refresh | 不受影響 |
| 案場即時負載 | REST 提供三段完整歷史序列；WebSocket 提供目前值與 bucket 增量 | WebSocket | 顯示斷線提示，保留最後資料，不做 fallback |
| 群組容量與 slots | Overview REST | 每 5 分鐘或手動 refresh | 不受影響 |
| Connector 即時狀態 | Overview REST 初始摘要與重點資料；WebSocket 更新 | WebSocket | 顯示斷線提示，保留最後資料，不做 fallback |
| 30 日充電用量 | Overview REST | 每 5 分鐘或手動 refresh | 不受影響 |
| 帳務結算流程 | Overview REST | 每 5 分鐘或手動 refresh | 不受影響 |

### 6.2 REST 生命週期

- 初次進頁呼叫一次 `GET /api/dashboard/overview`。
- 頁面存活期間每 5 分鐘呼叫一次。
- 使用者按下重新整理按鈕時立即呼叫一次。
- 同一時間只允許一個 Overview request；重複點擊不得製造平行請求。
- 手動重新整理只重新取得 REST 資料，不另建第二條 WebSocket。
- REST 某一來源失敗時，Backend 優先以該 section 的 `dataStatus` 隔離，不應讓整份 response 全部失效；授權或 Building 設定錯誤除外。

### 6.3 WebSocket 生命週期

- 頁面載入時建立一條 Dashboard WebSocket。
- 僅處理三種 message：
  - `DASHBOARD_LIVE_SNAPSHOT`
  - `CONNECTOR_LIVE_BLOCK_UPDATED`
  - `LIVE_LOAD_UPDATED`
- WebSocket 連線失敗或中斷時：
  - Connector 與即時負載區塊顯示「即時連線已中斷」類提示。
  - 保留最後一次成功資料與其時間，不清成 0。
  - 不改用 REST 輪詢、不做自訂 exponential backoff、不做 sequence gap recovery。
  - 不在背景自動重連；使用者重新載入整頁時才重新嘗試連線。
- WebSocket 斷線不影響其他 REST 區塊的顯示與五分鐘更新。

## 7. 共用資料契約

### 7.1 Overview REST

建議 endpoint：`GET /api/dashboard/overview`

回應至少包含：

```text
site                 案場識別、名稱、時區
generatedAt          Backend 產生 response 的時間
attention            需留意事項
kpis                  六個 KPI
liveLoad              三段完整圖表資料與目前值
chargeGroups          群組容量與 slots
connectorLive         Connector 五狀態摘要與固定 Top 8
usage30Days           30 日用量與摘要
billingPipeline       最近既有帳期的四步驟狀態
```

每一個主要 section 應具有：

```text
dataStatus            COMPLETE | PARTIAL | ERROR
updatedAt             此來源最後成功更新時間
message               Partial 或 Error 的可顯示原因；Complete 可為 null
```

`0` 表示已確認數值為零；`null` 表示無法確認、無資料或不可計算。FE 不得把 `null` 格式化成 0。

### 7.2 時間與時區

- 所有「今日」、「上月」、「最近 30 日」、「最近 7 日」以該 Building 的時區為準。
- API 時間點使用含 offset 的 ISO 8601。
- Backend 回傳 Building 時區；FE 負責依該時區顯示，不以瀏覽器所在時區重新歸日。
- 日期區間採明確的半開區間 `[start, end)`，避免月末與午夜重複計算。

### 7.3 資料新鮮度

- Backend 依資料來源套用全系統預設 freshness cutoff。
- V1 不提供每案場、每 Building 的 cutoff override，也不新增 Dashboard 設定畫面。
- 每個 section 回傳自己的 `updatedAt` 與 `dataStatus`；FE 不自行用一個全頁時間推定所有來源都新鮮。

## 8. 詳細功能需求

### 8.1 頁首與更新狀態

- 顯示案場名稱，不把 A17 寫死在產品文案。
- 顯示一般資料最後更新時間與「每 5 分鐘更新」。
- 顯示 WebSocket 連線狀態：正常或中斷。
- 手動重新整理按鈕要有 loading、disabled 與可讀的 accessible name。
- refresh 成功時更新成功區塊；部分失敗時保留仍有效資料並顯示 section 訊息。

### 8.2 需留意事項

區塊名稱固定為「需留意事項」，不用 `Needs Attention` 作為使用者可見標題。

只允許以下五類，順序固定：

| 順序 | code | 中文名稱 | affectedCount 意義 | 導向 |
|---|---|---|---|---|
| 1 | `CHARGE_POINT_OFFLINE` | Charge Point 離線 | 符合離線條件的 Charge Point 數 | `/cp/list` |
| 2 | `CONNECTOR_FAULTED` | Connector 故障 | 符合故障條件的 Connector 數 | `/connector/list` |
| 3 | `LIVE_LOAD_DATA_UNRELIABLE` | 即時負載資料不可靠 | 目前受影響的資料來源或設備數；依 Backend 契約定義 | `#load` |
| 4 | `QUEUE_BLOCKED` | 排隊受阻 | 目前 `BLOCKED` 的 distinct Connector 數 | `#connectors` |
| 5 | `SETTLEMENT_INCOMPLETE_OR_ERROR` | 帳務結算未完成或異常 | 目前帳期未完成或錯誤的結算單元數 | `#billing` |

顯示規則：

- 只顯示 `affectedCount > 0` 的類別，全部顯示，不做 Top N、drawer 或獨立頁。
- `categoryCount` 是非零類別數，不是受影響設備總數。
- 不跨類別做 root-cause 去重；同一設備可同時影響不同類別。
- `COMPLETE` 且 `categoryCount=0` 顯示中性的「目前沒有需留意事項」。
- `PARTIAL` 顯示已確認卡片，並提示內容可能不完整。
- `ERROR` 時 `categoryCount=null`，顯示暫時無法取得，不顯示 0。
- 這些卡片只提供導向，不提供修復、acknowledge 或變更狀態的功能。

### 8.3 六個 KPI

固定順序如下：

| 順序 | KPI | 定義 | 顯示 | 導向 |
|---|---|---|---|---|
| 1 | Charge Point 連線 | persisted OCPP connection state；分母為 enabled Charge Point | online / enabled total，並分列 offline、unknown、disabled | `/cp/list` |
| 2 | Connector 可服務 | enabled CP 之 Connector，且 parent CP 為 ONLINE、runtime status 在可服務 allowlist | serviceable / enabled connector total | `/connector/list` |
| 3 | 充電進行中 | canonical `ACTIVE` transaction；每個 Connector 最多計 1 個 | `{N} Sessions`，輔助文案「依未結束的充電 Session 計算」 | `/connector/list` |
| 4 | 等待供電 | Queue 狀態為 `ELIGIBLE` 的 distinct Connector | `{N} Connectors`；`NOT_PLUGGED` 分開顯示且互斥，`BLOCKED` 歸需留意事項 | `/connector/list` |
| 5 | 今日已完成充電量 | 與 30 日用量的今日 bucket 使用完全相同規則 | kWh | `#usage` |
| 6 | 上月電費 | Building 時區上一個完整曆月的 settlement `cost_predict` 總和 | 完整時顯示金額；不完整顯示 `—` 與完成數 | `#billing` |

補充規則：

- `充電進行中` 是「未結束 Session 數」，不是實際有功功率；即使 CP offline 或 disabled，只要 canonical transaction 仍為 ACTIVE 仍計入。
- KPI 不推論 offline charging、不新增 reconciliation flag，也不重複顯示 `currentPowerKw`。
- 上月電費包含該月已付款與待付款的 settlement，但只有所有應計單元都完整時才顯示總額。
- 六張卡皆為 REST 資料，不使用 WebSocket 更新。
- 六張卡皆為原生可聚焦連結；整張卡的可點擊範圍與 focus ring 必須清楚。
- 版面：寬度大於等於 1240 px 為 6×1；768–1239 px 為 3×2；320–767 px 為 2×3。不隱藏、不輪播。

### 8.4 案場即時負載

此區塊只顯示案場「充電功率」，不顯示或推導案場契約容量、案場使用率、剩餘容量。群組契約容量只在群組區塊顯示。

固定提供三個 tab：

| tab | X 軸 | bucket | Y 軸 |
|---|---|---|---|
| 即時 | 最近 60 分鐘，由舊到新 | 1 分鐘 | 該分鐘的時間加權平均充電功率，單位 kW |
| 今日 | Building 當地日 00:00 至現在 | 15 分鐘 | 該 15 分鐘的時間加權平均充電功率，單位 kW |
| 7 日 | 含今日在內的 7 個 Building 曆日 | 1 小時 | 該小時的時間加權平均充電功率，單位 kW |

圖表規則：

- X 軸名稱為「時間」，Y 軸名稱為「充電功率（kW）」。
- REST 一次回傳三個 tab 的完整 series；切換 tab 只讀取頁面 cache，不重打 API，也不改變 WebSocket 訂閱。
- tab 選擇在五分鐘 refresh 後保留，整頁重載後回到「即時」。
- WebSocket 更新 `currentPowerKw`、OPEN bucket 與跨界後的 FINALIZED bucket。
- `bucketState` 為 `OPEN | FINALIZED`，和 `dataStatus` 分開。
- 新鮮且已確認沒有充電時可回 0。
- 若有充電但必要功率資料超過 freshness cutoff，該 bucket 回 `null` 且 section 為 `PARTIAL`。
- 不做內插、不顯示猜測值、不以 coverage threshold 補值，也不提供「部分總功率」。
- 只有「目前」資料品質問題進入需留意事項；歷史缺口留在圖表中以 gap 呈現。

### 8.5 群組容量與 Slots

每個 Charge Group 顯示：

- 群組名稱。
- 群組目前充電功率 kW。
- 群組自己的 `contractCapacity` kW。
- 群組可使用與已使用 slots。
- 必要時顯示該群組資料的 Partial/Error 狀態。

不同群組的 `contractCapacity` 不可直接加總成案場契約容量。Dashboard 不新增群組設定或調整 slots 的操作。

### 8.6 Connector 即時狀態

#### 8.6.1 五個互斥狀態桶

所有 Connector 必須恰好歸入一個狀態桶，順序固定：

| code | 中文名稱 | 核心規則 |
|---|---|---|
| `NEEDS_ATTENTION` | 需留意 | `isAbnormal=true`、faulted、unknown 或 Backend 無法安全歸類的狀態 |
| `CHARGING` | 充電進行中 | 依既有 runtime 狀態與交易事實判定 |
| `WAITING` | 準備／排隊 | 已插槍準備或 Queue waiting 類狀態 |
| `AVAILABLE` | 可用 | parent CP 與 Connector 均符合可服務條件 |
| `OTHER` | 其他狀態 | 已知但不屬前四類；`NOT_PLUGGED` 歸此類 |

五個 count 加總必須等於 `total`，`NEEDS_ATTENTION` count 必須等於 `abnormalTotal`。當 `total > 0` 時五個摘要都顯示，包括 count 0；當 `total = 0` 時改顯示空狀態。

五個狀態摘要是純資訊，不可點擊、不可聚焦、不可當篩選器。使用者可點 Connector 卡片開啟單支詳情 Drawer，或按「查看全部」開啟 Full List Drawer。首頁摘要不會開啟 Drawer，也不會預先帶入篩選。

#### 8.6.2 不同 Connector 數量的彈性設計

Backend 固定回傳最多 8 筆 priority items，加上 `total` 與 `abnormalTotal`。FE 依 viewport 顯示同一份 payload 的前幾筆：

| viewport | 首頁最多顯示 |
|---|---|
| Desktop | 8 |
| Tablet | 6 |
| Mobile | 4 |

- 案場只有 1 或 2 個 Connector：只顯示實際卡片，不補空白 placeholder、不把卡片無限制拉寬。
- 案場有 30 個以上 Connector：首頁仍只顯示上述重點數量，標題與按鈕顯示總數，完整清單在 drawer 內分頁。
- 重點項目由 Backend 固定 priority sort；先看 `isAbnormal`／`severity`，再以穩定欄位做 deterministic tie-breaker。V1 不提供使用者排序。
- 每個 item 回傳 `displayBucket`、`isAbnormal`、`severity`、`priorityReason`；FE 不自行重算分類、異常或優先順序。
- 卡片至少分層顯示所屬 Charge Point 的 OCPP connection、Connector runtime 與 Queue status／reason，另顯示 Connector ID、Charge Point ID、位置／群組、nullable currentPowerKw、updatedAt 與 freshness。未知功率顯示「—」，不是 0。

#### 8.6.3 完整清單 Drawer

Drawer 是覆蓋在目前 Dashboard 上、由右側滑出的暫時面板，不是另一個頁面。桌面保留部分 Dashboard 作為背景脈絡；平板／手機可使用全螢幕 Sheet。關閉後回到原入口與原 Dashboard 位置。

| 入口 | 開啟內容 | 資料方式 |
|---|---|---|
| 點 Connector 預覽卡 | 單支 Connector 詳情 Drawer | 按需取得既有單支 detail；開啟才使用既有單支 socket，關閉釋放 |
| 點「查看全部」 | Full List Drawer | REST search、篩選、固定 priority sorting 與 server pagination |

- 固定 server-side pagination，每頁 20 筆。
- 只有上一頁、下一頁；總數小於等於 20 時隱藏 pager。
- 搜尋或篩選條件改變時回到第 1 頁。
- drawer 的搜尋、篩選與當前頁只保留在本次 Dashboard page session；關閉再開保留，整頁重載或離開頁面即清除。
- 不寫入 URL、local storage 或 Backend preference。
- 不提供 user-controlled sorting；現有 `/connector/list` 頁的排序不受影響。

Full List 的篩選固定為「摘要分類」與 Charge Group。「摘要分類」直接使用首頁同一套 `displayBucket`，不是 Connector runtime status 或 Queue status 的混合 selector：

| UI 選項 | Request `displayBucket` |
|---|---|
| 全部 | 不傳或 `null` |
| 需留意 | `NEEDS_ATTENTION` |
| 充電進行中 | `CHARGING` |
| 準備／排隊 | `WAITING` |
| 可用 | `AVAILABLE` |
| 其他狀態 | `OTHER` |

Full List search request 最少包含：

| 欄位 | 規則 |
|---|---|
| `page` | 沿用既有 API page convention；UI 頁次從 1 開始 |
| `size` | 固定 20 |
| `keyword` | 搜尋 Connector ID、Charge Point ID、車位；未填不過濾 |
| `displayBucket` | 可省略；值限上述五個 code |
| `chargeGroupId` | 可省略；只限制目前案場可見群組 |
| `sortMode` | 固定 `DASHBOARD_PRIORITY`，不是使用者控制欄位 |
| `buildingId` | FE 不傳；Backend 使用與 Overview 相同的目前案場 resolver |

每筆 response item 至少包含 `connectorId`、`chargePointId`、`location`、`floor`、`parkingSpaceNo`、`chargeGroupId`、`chargeGroupName`、`displayBucket`、`isAbnormal`、`severity`、`priorityReason`、`ocppConnectionStatus`、`runtimeStatus`、nullable `queueStatus`／`queueReason`、nullable `currentPowerKw`、`updatedAt` 與 `freshness`。Page 外層沿用既有 Spring Page metadata。

### 8.7 30 日充電用量

- 範圍固定為含今日在內的 30 個 Building 曆日，不提供日期選擇器。
- 只計算 completed transaction。
- `energy_consumed` 單位由 Wh 轉為 kWh；`null` 或負值排除並將 section 標為 `PARTIAL`。
- Sessions 只計 completed 且 `energy_consumed > 0` 的 transaction。
- 整筆 transaction 依 `start_timestamp` 所屬 Building 日期歸日，不做跨日拆分。
- 平均每次充電量 = 有效 kWh / 有效 Sessions；Sessions 為 0 時回 `null`。
- API 固定回 30 個 buckets。已確認無資料的日期為 0；未知或無法計算為 `null`。
- X 軸為 30 個 Building 日期，由最舊到今天；Y 軸依切換顯示「有效充電量（kWh）」或「有效充電 Sessions（次）」。畫面必須直接標示軸意義與單位，不可只靠 tooltip 推測。
- 30 日皆為已確認 0 時，回 `COMPLETE` 並顯示 empty 說明，不判為錯誤。
- 首次載入錯誤且無 cache 時顯示 Error；refresh 失敗但已有成功資料時保留 cache、標示 stale 與失敗時間。
- 已移除「時間使用率」，以「平均每次充電量」取代。
- 本區與「今日已完成充電量」只能使用 transaction 的 `status`、`energy_consumed`、`start_timestamp`。不讀 raw MeterValue、不呼叫帳務計費服務、不依 Tariff boundary 拆分。

### 8.8 帳務結算流程

此區塊保留 Mock 既有的四步驟流程，不新增「本月帳務摘要」或「本月預估電費」。

顯示 Backend 可取得的最新一個既有 settlement period；即使該帳期仍 pending 或 error，也顯示該帳期。完全沒有 settlement period 時顯示 empty。

固定四列：

1. Connector 結算。
2. 門牌結算。
3. 帳務覆核。
4. 發票結算。

四列都是純資訊，不可點擊。區塊底部提供兩個既有頁面連結：

- 帳單：`/bill/list`
- 發票：`/invoice/list`

帳務流程是 REST-only、read-only。上一個完整曆月的電費金額只出現在第六個 KPI，並依該 KPI 的完整性規則顯示。

## 9. FE 開發需求

### 9.1 頁面與元件

FE 至少需完成：

- Dashboard route 與 navigation entry，套用既有 `dashboard` function 可見性。
- Overview REST query、五分鐘 timer、manual refresh 與 request 去重。
- 單一 Dashboard WebSocket connection 與三種 message reducer。
- Header、Attention、KPI grid、Live Load chart、Group cards、Connector summary/cards/drawer、Usage chart、Billing pipeline。
- Loading、Empty、Partial、Error、Stale、403 與 Building config error 畫面。
- 所有既定 deep links、native links、hash focus 與 drawer keyboard interaction。

### 9.2 FE 不得自行推導的內容

以下必須由 Backend 回傳，FE 只負責呈現：

- Charge Point online/offline/unknown 統計。
- Connector 的五桶歸類、`isAbnormal`、`severity`、`priorityReason` 與固定 priority sort。
- ACTIVE Session、Queue ELIGIBLE/NOT_PLUGGED/BLOCKED 計數。
- 需留意事項類別與 affectedCount。
- Usage bucket、平均每次充電量、上月電費完整性。
- Live Load 的時間加權 bucket、bucket state 與 data quality。

### 9.3 FE 狀態管理

- REST cache 與 WebSocket live state 分開管理。
- WebSocket message 只能更新 Connector live 與 Live Load，不得改寫 REST-only KPI。
- WebSocket 斷線保留最後資料；畫面明確標示資料時間與斷線，不把舊資料偽裝成即時。
- section refresh error 不應清除其他 section。
- tab、drawer 與 pagination 的 page-session 行為依第 8 節執行。

### 9.4 圖表與格式

- 圖表必須顯示 X、Y 軸名稱、單位、tooltip 時間與數值。
- `null` bucket 以 gap 表示；不得連線或補 0。
- 金額、kW、kWh、小數位與千分位由共用 formatter 統一。
- 顏色不能是唯一狀態線索；同時提供文字、icon 或 pattern。

## 10. Backend 開發需求

### 10.1 API 與授權

Backend 至少需完成：

- `GET /api/dashboard/overview` controller、application service 與 response DTO。
- Dashboard WebSocket endpoint 或既有 endpoint 內的 Dashboard subscription contract。
- `dashboard` function/API mapping 與授權測試。
- 單一 enabled Building 解析及 0/多筆的明確 domain error。
- OpenAPI schema 與每個 DTO 欄位的中文說明。

### 10.2 Query 與聚合

Backend 負責：

- 依既有 Charge Point connection state 統計 online/offline/unknown/disabled。
- 依 parent CP 與 Connector runtime allowlist 統計可服務數。
- 以 canonical transaction 計算 ACTIVE Sessions。
- 以現有 Queue 資料計算 ELIGIBLE、NOT_PLUGGED、BLOCKED distinct Connectors。
- 產生 Attention allowlist、count 與 deep-link code。
- 產生三段 Live Load time-weighted series 與資料品質。
- 查詢 Charge Group 自身 capacity、current power 與 slots。
- 依 transaction 建立 30 日 Usage buckets。
- 查詢最近既有 settlement period 的四步驟摘要。
- 計算上一完整曆月 settlement 完整度與 `cost_predict` 總額。
- 回傳 Connector 五桶摘要、Top 8 與 drawer 分頁查詢。
- 擴充既有 Connector search：keyword 涵蓋 Connector ID、Charge Point ID、車位；新增可選 `displayBucket`、`chargeGroupId` 與固定 `DASHBOARD_PRIORITY` mode。先對完整案場集合 filter／sort，再固定每頁 20 筆 paginate。
- Dashboard Full List 不重用既有原始 `status` 建立 runtime／Queue 混合選項；既有管理頁的原始 status 與 sorting 維持相容。
- Connector search response 補足第 8.6.3 節的識別、群組、displayBucket、priority、原始狀態、功率與資料時間欄位，並完成 OpenAPI／contract tests。

### 10.3 WebSocket Publisher

只需發布：

- 初次訂閱的 `DASHBOARD_LIVE_SNAPSHOT`。
- Connector 相關事實變動後的 `CONNECTOR_LIVE_BLOCK_UPDATED`。
- current power 或 bucket 變動後的 `LIVE_LOAD_UPDATED`。

不為 Dashboard 建立 `NETWORK_HEALTH_UPDATED`、`CONNECTOR_OVERVIEW_UPDATED` 或 KPI 專用訊息。Publisher 不負責自動修復設備狀態。

### 10.4 資料庫原則

- V1 先使用既有 operational tables 與既有索引可支援的 read query。
- 不預先建立 Dashboard snapshot table、materialized cache 或專用 scheduler。
- 不為 A17 寫死 Building ID、Connector 數量、Group 數量或時間門檻。
- 查詢必須限制 Building 與日期範圍，避免跨案場讀取與 unbounded scan。
- 若實測顯示瓶頸，再以 EXPLAIN 與 endpoint baseline 決定是否補索引或調整 query。

## 11. UI 狀態與錯誤處理

| 狀態 | 定義 | 畫面行為 |
|---|---|---|
| Loading | 尚無首次資料，request 進行中 | skeleton 或 loading，不顯示假數字 |
| Empty | request 成功且已確認沒有資料 | 中性說明；必要計數顯示 0 |
| Partial | 只有部分來源可確認 | 顯示可確認資料與「可能不完整」訊息 |
| Error | 該 section 無可用資料 | 顯示無法取得與 retry/refresh 提示，不顯示 0 |
| Stale | 曾成功，但後續 refresh 或 WebSocket 失敗 | 保留最後資料、標示最後成功時間與 stale |
| 403 | 使用者無 `dashboard` 權限 | 不載入營運資料，顯示無權限 |
| Building config error | enabled Building 為 0 或多筆 | 不猜測、不載入，提示管理員修正設定 |

任一 section 的資料錯誤，不應自動讓其他來源正確的 section 變成 Error。只有 auth、Branch/Building scope 無法解析等全頁前置條件才阻止整頁載入。

## 12. Responsive 與 Accessibility

- 測試寬度至少包含 390、768、1024、1440 px。
- 不得產生整頁水平捲動；表格或 drawer 若需要可在元件內處理。
- Full List Drawer 在 1240 px 以上由右側滑出並保留部分 Dashboard；1239 px 以下使用全螢幕 Sheet。RWD 只改版面，不改搜尋、摘要分類、群組、欄位或分頁語意。
- 鍵盤可到達所有實際可互動元素，focus ring 清楚。
- 五個 Connector 狀態摘要與帳務四列不可被放入 tab order。
- Dialog/drawer 需有正確 role、可讀標題、focus trap、Esc 關閉與關閉後 focus restoration。
- loading、斷線、error、partial 更新需有適當 live region，避免只靠視覺變色。
- 文字與背景對比、非文字元件與 focus indicator 以 WCAG 2.2 AA 基本要求為準。
- 動畫尊重 `prefers-reduced-motion`。

## 13. Performance、可靠性與安全性

### 13.1 Performance

本功能不設定硬性的 `P95 < 1.5 秒`產品 SLA。開發完成後必須做可重現 baseline：

- 對 A17 與一個代表較大量資料的尺度執行 query EXPLAIN。
- 測量 Overview endpoint 的 P50、P95。
- 記錄環境、資料列數與分布、warm-up、concurrency、sample size、計時範圍。
- 檢查 N+1、unbounded query、重複聚合與不必要 payload。
- 只有量測證明一般使用受影響時才做額外 cache、索引或聚合設計。

### 13.2 Reliability

- REST 五分鐘更新失敗時，保留已成功資料並標示 stale。
- WebSocket 失敗只影響兩個 live 區塊；沒有 REST fallback、自訂重連或複雜 backoff。
- 不以 Mock fixture 作為 production fallback。
- response 與 message 必須能辨識 `null`、0、Partial、Error 與時間。

### 13.3 Security與Privacy

- API 與 WebSocket 都必須做既有 session/JWT 授權與 Branch scope 驗證。
- 不接受 client 傳入任意 Building ID 來跨案場查詢。
- error message 不暴露 SQL、內部 host、token 或個資。
- Dashboard 只回營運摘要所需欄位，不擴大暴露住戶或付款個資。

## 14. 驗收條件

### 14.1 共用驗收

- [ ] 有權限且 Building 設定正確時，初次載入可見所有八個主要區段。
- [ ] 無權限回 403；0 或多 enabled Building 顯示設定錯誤。
- [ ] 五分鐘 refresh 與手動 refresh 不建立重複 request 或重複 socket。
- [ ] 一個 section Error 不清除其他成功 section。
- [ ] 0、null、Partial、Error、Stale 的畫面與文案可分辨。
- [ ] 所有日期依 Building 時區計算。

### 14.2 WebSocket 驗收

- [ ] 一條 socket 可更新 Connector live 與 Live Load。
- [ ] 只接受三種既定 Dashboard message。
- [ ] socket 中斷後兩區塊顯示提示、最後更新時間與最後資料。
- [ ] socket 中斷不啟動 REST fallback、不自動重連、不影響 REST-only 區塊。
- [ ] 重新載入整頁會重新嘗試建立 socket。

### 14.3 Connector 驗收

- [ ] 五個 count 相加等於 total，需留意 count 等於 abnormalTotal。
- [ ] total 大於 0 時五個摘要都在，且純資訊、不可點擊。
- [ ] 1、2 個 Connector 不補假卡片；30 個 Connector 不一次塞滿首頁。
- [ ] Desktop/Tablet/Mobile 最多顯示 8/6/4 筆。
- [ ] 預覽卡分層顯示 CP OCPP connection、Connector runtime、Queue、CP ID、nullable 功率與資料時間；點卡片開啟可由 Esc 關閉且回復焦點的單支詳情 Drawer。
- [ ] Full List Drawer 在 Desktop 為右側面板、Tablet/Mobile 為全螢幕；每頁 20，filter/search 回第 1 頁，page-session state 規則正確。
- [ ] 「摘要分類」固定六個 UI 選項並直接對應 `displayBucket`；首頁五摘要不可點、不帶入條件，既有管理頁 status 無回歸。
- [ ] 首頁 summary、abnormalTotal、Top-8 與 Full List 由同一完整集合推導；30／100 筆 fixture 不互相矛盾。
- [ ] Backend priority sort 穩定，同一資料重查順序一致。

### 14.4 圖表與數值驗收

- [ ] Live Load 三個 tab 的範圍、bucket、X/Y 軸與單位符合規格。
- [ ] `null` bucket 顯示 gap，不補 0 或內插。
- [ ] Usage 固定 30 buckets，依 start date 歸日，跨日不拆分。
- [ ] 今日已完成充電量與 Usage 今日 bucket 完全一致。
- [ ] 上月電費只有 settlement 全部完整才顯示總額；不完整時為 `—`。
- [ ] 帳務區只有既有四步驟，沒有本月預估電費或虛構摘要。

### 14.5 Responsive與A11y驗收

- [ ] 390、768、1024、1440 px 無整頁水平捲動。
- [ ] Keyboard-only 可操作 refresh、KPI links、Connector cards、drawer 與 footer links。
- [ ] 純資訊元素不誤設為 button/link，不進 tab order。
- [ ] 狀態不只依顏色傳達，screen reader 可得知 loading/error/disconnected 更新。

## 15. 測試與發布要求

### 15.1 測試範圍

進入實作後，Backend 至少涵蓋：授權、Building 解析、各 KPI/section 聚合、時區邊界、0/null/Partial/Error、Connector 分桶守恆、Usage 歸日、上月電費完整性、WebSocket message contract。

FE 至少涵蓋：初載、refresh、socket update/disconnect、responsive 數量、drawer state、deep link、chart gap、keyboard 與錯誤狀態。

E2E Catalog 分類為 **Add**，但目前仍在需求階段，尚不建立測試結果或宣告 Pass。正式實作時需新增 P0–P3 優先級與 execution trigger，並依專案 catalog SOP 驗證。

### 15.2 First Rollout

- A17 作為第一個 rollout 與資料合理性驗證案場，不代表程式限定 A17。
- 上線後執行基本 smoke test：頁面權限、REST 初載、socket 更新、主要 deep links 與錯誤狀態。
- 下一個工作日使用既有 logs、工具與 DB load 做一次檢查。
- 不要求連續 24 小時監看，也不為本功能新增專用 dashboard metrics、alerts 或 on-call 流程。

## 16. A17 驗證基準

以下是規格整理時的 A17 觀察基準，只用於檢查 query 與 UI 是否能處理真實尺度；不得作為固定 fixture 或跨案場 acceptance value。

| 項目 | A17 基準 |
|---|---|
| Building | `BLD000000014` |
| Charge Points | 4 |
| Connectors | 12 |
| Charge Groups | B1、B2、B3，共 3 組 |
| 各 Group contract capacity | 各 99 kW；不得加總推導成案場容量 |
| 觀察區間 | 2026-08-05 至 2026-09-03 |
| Transactions | completed 78、active 1 |
| completed 正用電 | 77 筆；另 1 筆為 0 Wh，無 null/negative |
| 有效總電量 | 2,171.915 kWh |
| 有效 Sessions | 77 |
| 平均每次充電量 | 28.21 kWh/session |
| 跨日 Transactions | 10 筆，Dashboard Usage 全部依開始日歸屬 |

## 17. 工作拆分與 Redmine 追蹤

| Issue | 範圍 |
|---|---|
| #1422 | Parent：全案場 Dashboard 需求、文件版本、整體驗收與發佈 |
| #1423 | Connector UI/UX、不同數量、狀態摘要、卡片與 drawer |
| #1424 | Overview REST、Dashboard WebSocket、資料生命週期與效能原則 |
| #1460 | 帳務結算流程與上月電費資料 |
| #1461 | 30 日 Usage、今日用量與平均每次充電量 |
| #1462 | Live Load、圖表 buckets、群組 capacity/slots |
| #1463 | 需留意事項 allowlist、狀態與 deep links |
| #1486 | 六個 KPI 的計算、顯示與導向 |

所有 issue 在需求確認期間維持 New / 0%。只有需求 review 完成並由 Ken 明確同意進入開發後，才建立 v1.0、進入 planning gate 與實作。

## 18. Definition of Ready（進入開發前）

- [ ] Product 確認 v0.4 內容或後續 v0.x 修訂。
- [ ] FE 確認 layout、responsive、interaction、states 與 accessibility 可實作。
- [ ] Backend 確認現有資料可支援定義，OpenAPI/WS contract 無重大未解問題。
- [ ] QA 確認 acceptance criteria 可測。
- [ ] 所有未決項目已在 #1422 或 subtask 明確定案。
- [ ] 產出並附加同版 PDF、Markdown、HTML Mock。
- [ ] Ken 明確同意需求鎖定後，才建立 v1.0。

---

本文件到 v0.4 為止仍是 review Draft。任何在本文件中沒有明確定義的業務狀態、控制流程、告警 SLA、fallback 或自動修復行為，都不屬於本 Dashboard V1，不能由實作者自行補想。
