Feature #1422 » a17-dashboard-requirements.md
A17 EMS Backend Admin Dashboard 需求說明書
版本:v0.2 Draft
日期:2026-07-25
適用系統:react/branch + java/ems_branch + ems_branch database
Review 對象:Product / FE / Backend / QA / DevOps
文件狀態:需求草案,待確認 A17 production baseline 與權限範圍後進入實作
重要聲明:Mockup 中所有數字皆為假資料。這份文件已依現有 source code 與 repository 知識庫校準,但本次無法連上公開的 A17 staging endpoint,因此沒有把未驗證的 A17 數值寫成正式需求。第 12 節列出實作前必須完成的 A17 baseline 查核。
1. 執行摘要
1.1 問題
Branch Admin 的 /dashboard 目前是空白頁。DashboardStatsCard 雖然存在假資料版本,但在 page 中被註解,且資料模型只涵蓋電壓、電流、功率與簡化狀態,沒有對應現行 EMS 2.0 的 OCPP 連線、Connector runtime、輪充、離峰、帳務與資料新鮮度語意。
管理者現在必須分別進入充電站、Connector、Charge Group、帳單與事件頁面,才能回答下列基本問題:
- 案場現在是否正常?
- 哪些 Charge Point 離線、哪些 Connector 故障?
- 現在有多少充電工作階段,總負載是否接近契約容量?
- 輪充/離峰隊列是否阻塞或等待過久?
- 今日使用量與本月帳務是否有待處理項目?
- 畫面上的資料是否仍然新鮮、可被信任?
1.2 目標
建立「營運決策入口」,讓值班人員在 10 秒內判斷是否需要處理,並能從摘要直接進入既有管理頁面完成後續工作。Dashboard 第一版以讀取與導覽為主,不在首頁提供高風險設備控制。
1.3 V1 成功標準
| 指標 | 驗收目標 |
|---|---|
| 首屏判讀 | 使用者 10 秒內能指出是否有離線、故障、隊列阻塞或帳務異常 |
| API 效能 |
/api/dashboard/overview 在 A17 資料量下 P95 小於 1.5 秒 |
| 資料新鮮度 | 即時區塊顯示 generatedAt 與各資料源 updatedAt;stale 不可被顯示成正常 |
| 可行動性 | 所有告警與關鍵數字都有明確 deep link |
| 穩定性 | 任一子查詢失敗時,其他區塊仍可顯示,並標記 partial data |
| 可及性 | 符合 WCAG 2.2 AA 的鍵盤、對比與 status message 基本要求 |
1.4 設計原則
- 先顯示異常,再顯示趨勢;先總覽,再逐層 drill-down。
- 顏色不是唯一狀態媒介,必須同時提供文字、icon 或形狀。
- 不把 OCPP connection、Charge Point runtime、Connector runtime、rotation queue 混成單一「在線/離線」。
- Dashboard 只呈現可解釋、可追溯、可採取行動的指標。
- 自動刷新頻率應符合資料變化速度,避免無意義的高頻 polling。
2. 現況盤點與 Gap Analysis
2.1 Source code 現況
| 項目 | 現況證據 | 結論 |
|---|---|---|
| Dashboard 頁面 |
react/branch/app/(dashboard)/dashboard/page.tsx 只有空容器,DashboardStatsCard 被註解 |
FE 需重新實作正式頁面 |
| 舊假資料元件 |
dashboard-stats-card.tsx 內建 8 台假 Charger、固定電壓/電流/功率 |
僅可參考樣式,不可直接接 production |
| Connector 統計 |
GET /api/connector/dashboard 回 total、available、charging、faulted、public、private |
可重用邏輯,但不足以支撐完整 Dashboard |
| 系統健康 |
SystemController.healthCheck() 回 service、OCPP active connections、Charge Point count |
屬系統診斷 Map,未形成正式 typed Dashboard contract |
| Charge Point 連線 |
e_charge_points.ocpp_connection_status 為 UNKNOWN / ONLINE / OFFLINE |
可作為連線健康的 primary source |
| Connector runtime |
e_connectors.status 與 last_status_change
|
可作為設備狀態與 stale 判定來源 |
| 輪充隊列 |
e_connector_priority 有 priority、charge_status、status_reason、start_charging_time |
可提供等待、阻塞與派發摘要 |
| 容量配置 |
e_charge_group 有 contract_capacity、allocated_power、reserved_slots |
可計算 slots 與容量摘要 |
| 即時量測 |
e_meter_values 有 Power / Current / Energy 類型與 timestamp |
需新增高效的 latest-value aggregation |
| 帳務統計 |
/api/bills/statistics 已能統計 total、paid、pending、overdue、energy、cost |
可重用查詢概念,需整併進 overview |
| 權限 | EMS 使用 SS3A role → function → api mapping | 新 API 必須加入 Dashboard function mapping |
2.2 已存在與需要新增
| 能力 | 可直接重用 | 需要新增/調整 |
|---|---|---|
| Connector 數量摘要 | ConnectorRepository.queryStatisticsOfConnector() |
增加 unavailable、suspended、preparing、stale 等語意 |
| OCPP 連線摘要 |
ChargePointService / persisted connection status |
建立 typed DTO,區分 active session 與 persisted online |
| 即時負載 | MeterValue entity / repository | 新增 latest power query、單位正規化、stale cutoff |
| Charge Group 容量 | ChargeGroup + SlotCalculator 規則 | 新增唯讀 summary projection,避免複製錯誤公式 |
| 隊列健康 | ConnectorPriority entity | 新增等待時間、blocked reason、最久等待項目 |
| 今日用量 | Transaction / MeterValue 可計算 | 新增按時間區間的 aggregate query |
| 帳務摘要 | Bill statistics query | 併入同一 Dashboard response;定義月份與時區 |
| 告警列 | 各資料表已有部分訊號 | 新增 severity / code / count / actionUrl 的規則引擎 |
| FE 元件 | shadcn/ui、React Query、OpenAPI client | 新頁面、hooks、圖表、states、deep links、RWD |
2.3 不應直接沿用的舊假資料
-
systemVoltage與systemCurrent目前無單一「全站總表」資料源,不應以固定 220V 或把各 Connector 電流直接相加冒充真值。 - 「充電進度」與「預估剩餘時間」不是所有 OCPP 1.6 Charge Point 都能可靠提供,V1 不列為必備指標。
-
status = idle / maintenance與現有ConnectorStatusenum 不一致,正式 UI 必須使用後端穩定 code。
3. 使用角色、場景與範圍
3.1 使用角色
| 角色 | 主要任務 | Dashboard 權限 |
|---|---|---|
| Site Admin / 管理者 | 查看全站狀況、處理設備與設定問題 | 完整唯讀總覽與 deep link |
| Operations / 值班人員 | 找出離線、故障、輪充阻塞與 stale data | 營運與告警區塊 |
| Finance / 帳務人員 | 查看本月待確認、逾期與結算狀態 | 帳務摘要與帳單 deep link |
| Engineer | 查看設備與通訊異常,再進 Engineer Tools 診斷 | 可見技術診斷 deep link,但首頁不直接下 command |
權限原則:前端隱藏不是授權。Dashboard API 必須由 SS3A function/API mapping 控制;未具 Dashboard function 的使用者不得直接呼叫。
3.2 核心使用場景
- 使用者登入後進入 Dashboard。
- 系統載入首屏 skeleton,取得單一 overview response。
- 若有 Critical / Warning,告警列優先呈現,並依 severity、影響數量、持續時間排序。
- 使用者查看 OCPP 連線、Connector 狀態、即時負載與隊列摘要。
- 點擊卡片或告警進入既有 Charge Point、Connector、Charge Group、Bill 或 Engineer Tools 頁面。
- 返回 Dashboard 時保留原本時間範圍,重新確認資料新鮮度。
3.3 V1 In Scope
- 案場單站總覽。
- OCPP connection、Charge Point、Connector、active session 統計。
- 即時總功率與近 24 小時負載序列。
- Charge Group slots、reserved、occupied、waiting、blocked 摘要。
- Connector 即時狀態卡與 detail drawer。
- 今日用電、近 30 日用量/session 趨勢。
- 本月帳務 pipeline 摘要。
- 告警列、deep link、資料新鮮度、partial-data state。
- Desktop / Tablet / Mobile responsive。
3.4 V1 Out of Scope
- 在 Dashboard 直接 Remote Start / Stop、Reset、Modbus 或 Engineer command。
- 告警 acknowledgement、owner assignment、SLA workflow。
- 跨多案場 HQ portfolio Dashboard。
- 預測性維護、AI 異常偵測、費用預測模型。
- 自建 BI 報表編輯器或讓使用者自由配置 widget。
- 取代既有 Charge Point、Connector、帳單、Engineer Tools 詳細頁。
4. 畫面資訊架構

4.1 首屏由上到下
| 區塊 | 使用者問題 | 主要行動 |
|---|---|---|
| Header | 我看的是哪個案場?資料何時更新? | 刷新、切換時間範圍 |
| Needs Attention | 現在最需要處理什麼? | 點擊進入對應清單 |
| KPI Row | 全站規模、在線、充電、等待、用量、帳務概況? | 點擊卡片 drill-down |
| Live Load | 目前功率與契約容量的距離? | 切 Live / 今日 / 7 日 |
| Group Capacity | 各群組 slot 是否不足或阻塞? | 進 Charge Group |
| Connector Grid | 哪一支 Connector 現在是什麼狀態? | 開 detail drawer |
| Usage Trend | 近 30 日 kWh 與 sessions 如何變化? | 切 metric / range |
| Billing Pipeline | 本月帳務卡在哪個階段? | 進 Billing / Invoice |
4.2 Connector detail drawer
至少顯示:
- connectorId、chargePointId、parkingSpaceNo、area、connectorType。
- OCPP connection status、Charge Point runtime、Connector runtime。
- errorCode、vendorErrorCode、info。
- currentTransactionId、開始時間、目前功率、累計用量。
- chargeGroup、queue status、priority、statusReason、waiting duration。
- lastHeartbeat、lastStatusChange、latestMeterAt。
- 「查看 Connector」、「查看 Charge Point」、「查看 Engineer Tools」deep link。
4.3 Responsive
| Breakpoint | 規格 |
|---|---|
| >= 1240 px | 固定側欄;6 張 KPI;Live Load 與 Group Capacity 雙欄 |
| 940 - 1239 px | 側欄縮合;KPI 三欄;圖表上下排列 |
| 640 - 939 px | KPI 兩欄;Connector 兩欄;表格改卡片 |
| < 640 px | KPI 兩欄或單欄;Connector 單欄;告警可水平滑動;不得出現頁面級水平捲軸 |
5. 指標字典與狀態定義
5.1 首要指標
| 指標 | 定義/公式 | Primary source | Freshness | 點擊後 |
|---|---|---|---|---|
| Charge Point 總數 | enabled CP count | e_charge_points |
60 秒 | /cp/list |
| OCPP Online | ocpp_connection_status = ONLINE |
e_charge_points |
60 秒 | CP list filtered |
| Connector 可用數 | status = AVAILABLE |
e_connectors |
60 秒 | Connector list filtered |
| 正在充電 |
status IN (CHARGING, SUSPENDED_EV, SUSPENDED_EVSE);卡片另顯 active transaction count |
e_connectors, e_transactions
|
30 秒 | Connector list |
| 等待供電 | queue status 為 eligible / not_plugged / blocked 的分項;不得只給單一合計 | e_connector_priority |
60 秒 | Charge Group |
| 即時總功率 | 各 enabled Connector 最新、未 stale 的 Power.Active.Import 加總;統一轉 kW | e_meter_values |
30 秒 | Connector grid |
| 契約容量利用率 |
currentPowerKw / sum(applicable contractCapacityKw) * 100;分母規則須避免重複計算 |
e_charge_group |
60 秒 | Charge Group |
| 今日用電量 | 台北時區今日已完成交易 + active transaction 即時增量 |
e_transactions, e_meter_values
|
5 分鐘 | Usage |
| 本月帳務待處理 | pending + overdue + invoice draft/pending 的分項 | settlement / billing tables | 15 分鐘 | Billing |
| 資料 stale 數 | lastHeartbeat / lastStatusChange / latestMeterAt 超過各自 cutoff | CP / Connector / MeterValue | 60 秒 | 告警列 |
5.2 三層狀態不可混用
| 狀態層 | 欄位/來源 | 顯示目的 |
|---|---|---|
| Connection | ChargePoint.ocppConnectionStatus |
Branch 是否觀察到 OCPP 連線存活 |
| Runtime |
ChargePoint.status, Connector.status
|
設備/槍實際運作狀態 |
| Queue |
ConnectorPriority.chargeStatus, statusReason
|
離峰/輪充是否等待、充電、阻塞或完成 |
規則:
- CP OFFLINE 不代表每支 Connector 的最後 runtime status 可被改寫成 AVAILABLE。
- Heartbeat 或 BootNotification 只能證明連線存活,不能把 FAULTED/CHARGING 覆寫為 AVAILABLE。
- Connector 的 primary runtime owner 是 StatusNotification;畫面要分開顯示「OCPP 離線」與「最後設備狀態」。
-
currentTransactionId或 ACTIVE transaction 存在時,離線告警需標記「可能仍在離線充電/待 reconciliation」。
5.3 告警 severity
| Severity | 條件範例 | UI |
|---|---|---|
| Critical | CP OFFLINE 且有 active transaction;Connector FAULTED;資料全面 stale | 紅色、置頂、需 action |
| Warning | queue BLOCKED、等待超過門檻、帳單 overdue、容量利用率 >= 85% | 黃色、依持續時間排序 |
| Info | invoice draft、部分 Connector 未配置 tariff / charge group | 藍色、可收合 |
告警物件必須有 code、severity、title、description、count、firstSeenAt、latestSeenAt、actionLabel、actionUrl。
6. Functional Requirements
| ID | Priority | Owner | Requirement / Acceptance |
|---|---|---|---|
| FR-001 | Must | BE | 提供 typed GET /api/dashboard/overview;成功回 generatedAt, dataStatus, 各區塊資料 |
| FR-002 | Must | FE |
/dashboard 顯示正式營運總覽,不再引用 component 內 hard-coded production data |
| FR-003 | Must | BE | 統計 OCPP connection 與 CP runtime,兩者使用不同欄位 |
| FR-004 | Must | BE | 統計 Connector 全部主要狀態,不只 AVAILABLE / CHARGING / FAULTED |
| FR-005 | Must | BE | 取得 latest power 並做 unit normalization;stale 資料不納入總功率 |
| FR-006 | Must | BE | 依既有 SlotCalculator 語意產生每個 Charge Group 的 total / occupied / reserved / available slots |
| FR-007 | Must | BE | 回傳 queue waiting / blocked / charging 分項與最久等待時間 |
| FR-008 | Must | BE | 回傳今日用量與近 30 日 kWh / sessions 時序 |
| FR-009 | Must | BE | 回傳本月 billing / invoice pipeline,不混用 BillStatus 與 InvoiceStatus |
| FR-010 | Must | BE | 建立 severity-first alerts,所有 alert 具 deep link |
| FR-011 | Must | FE | 所有區塊支援 loading / empty / error / stale / partial data |
| FR-012 | Must | FE | Header 顯示最後成功更新時間;手動 Refresh 不重設使用者選擇 |
| FR-013 | Must | FE | Connector card 點擊開 drawer;首頁禁止直接發送控制 command |
| FR-014 | Must | FE | status 以 code 判斷邏輯,label 僅顯示;未知值不可 fallback 成 AVAILABLE |
| FR-015 | Must | BE | Dashboard API 納入 SS3A API/function mapping,Operator identity 由 JWT |
| FR-016 | Should | BE/FE | API 子區塊失敗時回 partial status;FE 保留成功區塊並允許 retry |
| FR-017 | Should | FE | Live / 今日 / 7 日與 30 日趨勢切換不得觸發不必要的全頁刷新 |
| FR-018 | Should | FE | URL query 保存 range / metric,使 deep link 可重現畫面 |
| FR-019 | Should | BE | 每個 aggregate query 記錄 elapsed time;慢查詢可被 observability 追蹤 |
| FR-020 | Could | FE | 匯出目前 Dashboard snapshot;V1 可先不做 |
6.1 UI States 驗收
- Loading:使用區塊 skeleton,不使用全頁 blocking spinner。
- Empty:說明為何沒有資料與可採取動作,不只顯示
No data。 - Error:顯示失敗區塊與 retry;不可把錯誤顯示成 0。
- Stale:保留最後值但標示 stale 與時間,不納入需要即時性的合計。
- Partial:Header 顯示「部分資料未更新」,各區塊顯示自己的 source status。
- Unauthorized:API 回 403;FE 顯示無權限頁,不 silently hide failure。
7. API Contract 建議
7.1 Endpoint
GET /api/dashboard/overview?range=TODAY&trendDays=30
Query:
| 參數 | 值 | Default | 說明 |
|---|---|---|---|
range |
LIVE / TODAY / SEVEN_DAYS | TODAY | 負載序列範圍 |
trendDays |
7 / 30 / 90 | 30 | 用量趨勢日數 |
timezone |
IANA timezone | Asia/Taipei | V1 可固定並在 response 回傳 |
7.2 Response shape
{
"generatedAt": "2026-07-25T16:30:00+08:00",
"timezone": "Asia/Taipei",
"dataStatus": "COMPLETE",
"sourceStatuses": [
{"source": "OCPP", "status": "FRESH", "updatedAt": "..."},
{"source": "METER_VALUES", "status": "STALE", "updatedAt": "..."}
],
"alerts": [],
"networkHealth": {
"chargePointTotal": 0,
"ocppOnline": 0,
"ocppOffline": 0,
"activeWebSocketSessions": 0
},
"connectorSummary": {
"total": 0,
"available": 0,
"charging": 0,
"preparing": 0,
"suspended": 0,
"unavailable": 0,
"faulted": 0
},
"liveLoad": {
"currentPowerKw": null,
"contractCapacityKw": null,
"utilizationPercent": null,
"latestMeterAt": null,
"series": []
},
"chargeGroups": [],
"connectors": [],
"usageTrend": [],
"billingPipeline": {}
}
7.3 Contract 規則
- 金額使用 decimal string 或明確 BigDecimal schema,不使用浮點近似。
- 時間一律 ISO-8601 含 offset;response 額外回 timezone。
- 數值不存在用
null,不可用 0 表示「未知」。 -
dataStatus:COMPLETE / PARTIAL / STALE。 - source status:FRESH / STALE / ERROR / NOT_CONFIGURED。
- 狀態 code 穩定且供 FE 邏輯使用;中文 label 可由後端提供,但不得取代 code。
- 每個 DTO class、public method 與 DTO 欄位依專案標準補繁體中文用途說明與 OpenAPI
@Schema。
7.4 Error handling
| HTTP | 情況 |
|---|---|
| 200 | 完整、partial 或 stale,細節由 dataStatus 表示 |
| 400 | query parameter 不合法 |
| 401 | 未登入/token 無效 |
| 403 | 無 Dashboard function |
| 500 | overview 無法建立任何可用內容 |
| 503 | 服務層整體不可用;不可用來表示單一子資料源失敗 |
8. Backend 開發內容
8.1 建議 package
api/dashboard/
DashboardController.java
DashboardService.java
DashboardAlertService.java
dto/
DashboardOverviewDto.java
NetworkHealthDto.java
ConnectorSummaryDto.java
LiveLoadDto.java
ChargeGroupSummaryDto.java
DashboardConnectorDto.java
UsageTrendPointDto.java
BillingPipelineDto.java
DashboardAlertDto.java
Repository 可新增 projection query,但不應把所有邏輯塞進一條巨大 native SQL。建議由 DashboardService 在同一 read-only transaction 內協調 5 - 7 個有界 aggregate query,並獨立量測 elapsed time。
8.2 BE Task Breakdown
| Task | 說明 | Done Definition |
|---|---|---|
| BE-01 Contract | 建立 Controller、DTO、OpenAPI schema | schema 可由 yarn openapi:refresh 產生 |
| BE-02 Network health | CP connection / runtime aggregate | 單元測試涵蓋 ONLINE、OFFLINE、UNKNOWN |
| BE-03 Connector summary | 全狀態 aggregate + active transaction count | 狀態總和可對回 total |
| BE-04 Latest load | latest power query、unit convert、stale cutoff | W/kW 混合與 stale 測試通過 |
| BE-05 Group capacity | 使用既有 slot rule;回 waiting / blocked | reserved slot 不重複計算 |
| BE-06 Usage trend | 今日與日粒度 aggregate | Asia/Taipei 日界線測試 |
| BE-07 Billing | 整合 settlement / billing / invoice status | 不混淆兩套 enum |
| BE-08 Alerts | 規則與 deep link mapping | severity ordering deterministic |
| BE-09 Partial data | 子查詢隔離與 source status | 單一 repository exception 仍回 200 PARTIAL |
| BE-10 Security | SS3A function/API migration | 無權限 403、有權限 200 |
| BE-11 Performance | A17 EXPLAIN、P95 測試、必要 index proposal | P95 < 1.5 秒 |
| BE-12 Documentation | 更新 ai/、OpenAPI、註解 |
與實作一致 |
8.3 Database 原則
V1 原則上不新增 Dashboard snapshot table,先以 read-only aggregate 驗證 A17 資料量與查詢成本。若 EXPLAIN 證明 latest MeterValue 查詢昂貴,再提出普通 index 或彙總表方案。
候選 index 必須先用 A17 EXPLAIN 驗證:
e_meter_values(connector_id, value_type, timestamp)e_transactions(status, start_timestamp)e_connector_priority(charge_group, charge_status, priority)
依專案規則,不為 Dashboard 新增 UNIQUE / FOREIGN KEY / CHECK / cascade constraint。
8.4 快取與刷新
- FE 預設 60 秒 refetch;使用者可手動刷新。
- Live Load 若需要 30 秒,可只刷新 live 子查詢;第一版可先整包 60 秒。
- BE 可使用 15 - 30 秒短快取,但 cache key 必須包含 range / trendDays。
- 快取失敗不可回舊值而不標示;必須保留 generatedAt / source updatedAt。
9. Frontend 開發內容
9.1 建議目錄
app/(dashboard)/dashboard/
page.tsx
hooks/useDashboardOverview.ts
lib/dashboard-presentation.ts
components/
dashboard-header.tsx
attention-strip.tsx
kpi-card.tsx
live-load-chart.tsx
charge-group-capacity.tsx
connector-status-grid.tsx
connector-detail-drawer.tsx
usage-trend-chart.tsx
billing-pipeline.tsx
dashboard-section-state.tsx
9.2 FE Task Breakdown
| Task | 說明 | Done Definition |
|---|---|---|
| FE-01 Types | refresh OpenAPI,禁止手改 emsapi.ts
|
build 通過 |
| FE-02 Query hook | React Query hook、range key、refetch policy | 不重複 request |
| FE-03 Header | 案場、generatedAt、dataStatus、refresh | refresh 有狀態回饋 |
| FE-04 Alerts | severity ordering、deep link | keyboard 可操作 |
| FE-05 KPI | 共用 KpiCard,null / 0 / stale 分開 | 長 label 不破版 |
| FE-06 Charts | load / usage,tooltip、單位、文字摘要 | 空資料與單點資料正常 |
| FE-07 Group | slots、reserved、occupied、waiting | 不只用顏色 |
| FE-08 Connector | status grid + drawer + deep link | unknown status 安全顯示 |
| FE-09 Billing | pipeline 與既有 Billing route 對接 | enum label 正確 |
| FE-10 States | loading / empty / error / stale / partial / 403 | Story/fixture 可重現 |
| FE-11 RWD | 四個 breakpoint | 無頁面級 horizontal scroll |
| FE-12 Accessibility | focus、ARIA status、contrast、keyboard | 基本 axe / manual check 通過 |
9.3 Fixture 策略
- 假資料集中在
dashboard.fixtures.ts或 MSW fixture,不放在 component body。 - Fixture shape 必須完全等同 OpenAPI generated type。
- 至少準備:healthy、critical、partial、stale、empty、large-data 六組 scenario。
- Production build 不得在 API error 時自動 fallback 到 fixture。
9.4 圖表要求
- X/Y 軸必須有時間與單位。
- Tooltip 可鍵盤/觸控使用。
- 圖表旁提供最新值與趨勢文字摘要,不能只提供視覺線條。
- 不同單位不放在同一 Y 軸;若需要雙軸,必須清楚標示。
- Loading 時維持固定高度,避免 layout shift。
10. Non-Functional Requirements
10.1 Performance
- Overview response gzip 後建議小於 250 KB。
- 初次可見內容在一般內網環境 2.5 秒內呈現。
- A17 production P95 API 小於 1.5 秒;P99 小於 3 秒。
- Trend points 做 server-side downsampling;單一 series 不超過 500 points。
- 禁止 N+1 查詢 Connector latest meter values。
10.2 Reliability
- 任一子區塊失敗不可把值改成 0。
- React Query 保留最後成功資料時,必須顯示 stale / fetching。
- 手動 refresh 可取消前一個 in-flight request。
- API response 必須具 deterministic ordering,避免卡片跳動。
10.3 Security / Privacy
- 使用既有 JWT 與 SS3A function mapping。
- Dashboard 不回住戶姓名、手機、LINE ID 等個資。
- Connector drawer 只回營運必要欄位。
- 不回 raw OCPP payload、credential、token 或 Modbus connection details。
- Deep link 目的頁仍需自己的後端權限檢查。
10.4 Accessibility
- 依 WCAG 2.2 AA:狀態不可只靠顏色,文字對比、focus visible、status message 可被 assistive technology 判定。
- 圖表有文字替代摘要。
- 自動更新不搶 focus;更新完成用 non-intrusive status message。
- Touch target 建議至少 24 × 24 CSS px,主要操作建議 44 × 44。
10.5 Observability
- 記錄 overview total elapsed、每個子查詢 elapsed、dataStatus、result counts。
- 不記錄完整 response、個資或敏感資料。
- 慢查詢與 partial response 應有可搜尋的 structured log。
- 後續可增加 Dashboard API metrics:request count、P95、error rate、partial rate。
11. QA、驗收與發布計畫
11.1 Backend Test
- Repository integration:狀態 aggregate、latest meter、date boundary、billing status。
- Service unit:severity、stale cutoff、unit normalization、partial failure。
- Security:401 / 403 / 200。
- Contract:OpenAPI field、enum、nullability、ISO time。
- Performance:A17 baseline data volume 下執行 EXPLAIN 與 repeated request。
11.2 Frontend Test
- fixture scenarios:healthy / critical / partial / stale / empty / large。
- viewport:1440、1024、768、390 px。
- keyboard:alert、range toggle、connector card、drawer、deep link。
- unknown enum、超長 ID、0、null、極大值、慢回應。
- chart single point、cross-day、timezone、30 日資料。
11.3 End-to-End Acceptance
- CP 斷線後,Dashboard 在刷新週期內顯示 OFFLINE,且不把最後 Connector runtime 誤顯示為 AVAILABLE。
- Connector FAULTED 會產生 Critical alert,點擊後進入對應 Connector。
- ACTIVE transaction 期間可看到 session 與功率;MeterValue stale 後功率不納入 live total。
- Queue BLOCKED 顯示 reason 與等待時間,點擊後進 Charge Group。
- Billing pending / overdue 與 Invoice draft / pending 分開顯示。
- 任一資料源模擬 exception 時,其餘區塊仍可使用並顯示 PARTIAL。
- 未授權帳號直接呼叫 API 得到 403。
11.4 Rollout
| Phase | 內容 | Gate |
|---|---|---|
| 0 - Baseline | 執行 A17 queries、確認指標與門檻 | Product / BE 確認 |
| 1 - Contract | BE DTO + OpenAPI + fixture | FE / BE contract review |
| 2 - Backend | aggregate、alerts、security、tests | API test / P95 |
| 3 - Frontend | layout、states、RWD、accessibility | FE review |
| 4 - Staging | 使用真實 staging data E2E | QA sign-off |
| 5 - A17 | feature flag / controlled release | 24 小時觀察 |
回滾:保留舊空 Dashboard route 的 deploy rollback 能力;若 overview API 異常,FE 顯示明確 error,不導向假資料。
12. A17 Data Baseline 與待確認事項
12.1 實作前必跑 baseline
正式開發前,BE 需在 A17 以 read-only query 取得:
| Baseline | 用途 |
|---|---|
| CP 數量與 ONLINE / OFFLINE / UNKNOWN 分布 | 決定 network KPI 與 offline alert |
| Connector 各 status、area、type 分布 | 驗證卡片狀態與 grid 規模 |
| active transaction 數與最長持續時間 | 驗證 session 與離線保護 |
| 每個 Charge Group 的 contract / allocated / reserved / queue | 驗證 slots 與等待邏輯 |
| MeterValue 24 小時筆數、每分鐘峰值、latest lag | 決定 query / index / downsampling |
| 近 30 日 transaction 數、kWh、日分布 | 驗證 trend |
| 當月 billing / invoice status 分布 | 驗證 pipeline |
| Table row count 與 EXPLAIN | 確認 P95 目標可行 |
12.2 建議 read-only SQL
SELECT ocpp_connection_status, status, COUNT(*)
FROM e_charge_points
GROUP BY ocpp_connection_status, status;
SELECT status, connector_area, connector_type, COUNT(*)
FROM e_connectors
GROUP BY status, connector_area, connector_type;
SELECT charge_group, charge_status, status_reason, COUNT(*),
MIN(create_time) AS oldest_waiting_at
FROM e_connector_priority
GROUP BY charge_group, charge_status, status_reason;
SELECT value_type, unit, COUNT(*), MAX(timestamp) AS latest_at
FROM e_meter_values
WHERE timestamp >= NOW() - INTERVAL 24 HOUR
GROUP BY value_type, unit;
SELECT status, COUNT(*), SUM(energy_consumed) / 1000 AS energy_kwh
FROM e_transactions
WHERE start_timestamp >= CURDATE() - INTERVAL 30 DAY
GROUP BY status;
SELECT bill_status, COUNT(*), SUM(energy_usage), SUM(cost_predict)
FROM e_bill_settlement
WHERE year = YEAR(CURDATE()) AND month = MONTH(CURDATE())
GROUP BY bill_status;
12.3 需要 Product / Engineering 決策
| ID | 問題 | 建議預設 |
|---|---|---|
| D-01 | Dashboard 僅 A17 單站,或未來需同畫面切多社區? | V1 單站,API 保留 buildingId 擴充空間 |
| D-02 | 即時負載以 Charge Group 契約容量總和為分母是否會重複? | 先依 A17 電力拓撲確認後再定 |
| D-03 | CP OFFLINE 告警門檻是否沿用 330 秒 watchdog? | 沿用 persisted status,不在 Dashboard 另推導 |
| D-04 | Queue waiting warning 門檻? | 先以 rotation cycle × 2,baseline 後調整 |
| D-05 | 帳務資料顯示 Bill、Settlement 或 Invoice 哪一層為主? | 首頁顯示 pipeline 分層,不合併成單一數字 |
| D-06 | Dashboard function 要授權哪些既有 role? | 明確 mapping,不自動繼承 Engineer 能力 |
| D-07 | 是否需要 30 秒 live refresh? | V1 60 秒,確認負載與 DB 成本後再縮短 |
13. Definition of Done
本需求只有在以下條件全部達成時才算完成:
- Product 確認本文件 scope、KPI 字典與 decisions。
- A17 baseline 已完成,結果沒有推翻 KPI 公式或效能方案。
- Backend overview API、OpenAPI、SS3A mapping、測試完成。
- FE 完成所有區塊、states、RWD、accessibility 與 deep link。
- Mock fixture 不會在 production error 時被使用。
- A17 staging E2E 與 P95 驗證通過。
-
ai/02-backend-services.md、ai/03-database.md、ai/04-frontend.md依實作更新。 - FE / BE / QA review issue 皆有對應 acceptance evidence。
- 發布後 24 小時監看 error rate、partial rate 與 query latency。
14. 參考來源
- EMS source code:
react/branch/app/(dashboard)/dashboard/、java/ems_branch/.../ConnectorController.java、ConnectorRepository.java、SystemController.java、Charge Group / MeterValue / Billing 相關 Entity 與 Repository。 - EMS AI knowledge base:
ai/01-overview.md、ai/02-backend-services.md、ai/03-database.md、ai/04-frontend.md、ai/06-domain-glossary.md。 - Grafana Dashboard Best Practices:https://grafana.com/docs/grafana/latest/visualizations/dashboards/build-dashboards/best-practices/
- W3C WCAG 2.2 - Use of Color:https://www.w3.org/WAI/WCAG22/Understanding/use-of-color
- W3C WCAG 2.2 - Status Messages:https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html
- Open Charge Alliance OCPP 1.6 resources:https://openchargealliance.org/protocols/ocpp-protocols/ocpp-1-6/