專案

一般

配置概況

討論 #1461

是由 陳國瑋 於 27 天 前更新

> **REST 更新週期確認(#1424 DEC-032)**:30 日充電用量與「今日已完成充電量」沿用 5 分鐘更新,但必須與所有其他 REST-owned sections 共用同一個 Overview query/timer;頁面首載、每 5 分鐘及手動刷新各只送出一個 Overview request,不建立 Usage 專用 timer 或 API polling。此規則不形成兩個 WebSocket live blocks 的 fallback。 

 ## 討論目的 

 確認 Dashboard mockup 既有「30 日充電用量」區塊的期間、指標與資料語意。本票只定義唯讀圖表與摘要資訊,不改變交易、電量或充電作業邏輯。 

 ## Source of Truth 

 Redmine attachment #1110 a17-operations-dashboard.html 的既有設計包含: 

 - 標題「30 日充電用量」。 
 - 圖表指標切換:kWh/Sessions。 
 - 三項摘要:有效充電量、充電 Sessions、時間使用率。 
 - Mockup 中的數字均為展示假資料,正式功能必須由 Dashboard API 回傳。 

 原 mockup 沒有 7 日、90 日或自訂日期範圍選擇。 

 ## USG-DEC-001:V1 固定最近 30 個案場日曆日 

 **狀態:已確認(2026-09-03,Ken)** 

 ### 使用者會看到什麼 

 - V1 固定顯示最近 30 個案場日曆日,包含使用者開啟 Dashboard 的今天。 
 - 日期邊界依該 building 的 timezone 判定,不使用瀏覽器時區,也不硬編碼 A17。 
 - 今天尚未結束,因此今天的圖表數值屬於進行中資料;畫面不得暗示今天已完成結算。 
 - 保留 mockup 的 kWh/Sessions 指標切換;切換指標不改變 30 日日期範圍。 
 - V1 不提供 7 日、90 日或自訂日期範圍 selector。 

 ### 日期例子 

 若案場時區日期為 2026-09-03,30 個日期 bucket 為 2026-08-05 至 2026-09-03,共 30 個日曆日;其中 2026-09-03 是進行中的今天。 

 ### Backend 要開發的內容 

 - 依指定 buildingId 及該案場 timezone 產生固定 30 日查詢範圍。 
 - Dashboard read model 回傳 rangeStartDate、rangeEndDate、timezone,以及圖表使用的每日 kWh/Sessions series。 
 - 30 日範圍的日期計算由 Backend 統一負責,Frontend 不自行以 browser timezone 重算。 
 - 本區塊透過 REST 取得;不新增 Usage WebSocket event。 
 - 查詢只讀取、彙整既有資料,不回寫 Transaction、MeterValue、Billing 或其他業務資料。 

 ### Frontend 要開發的內容 

 - 標題維持「30 日充電用量」,不新增日期範圍 selector。 
 - 依 Backend 回傳的 rangeStartDate/rangeEndDate/timezone 顯示同一段 30 日資料。 
 - kWh/Sessions 切換只切換圖表指標,不觸發 7/90 日或自訂範圍查詢。 
 - 今天的 bucket 必須能被辨識為進行中資料。 
 - Loading、Empty、Partial、Error 與缺少日期資料的呈現方式另行確認。 

 ## USG-DEC-002:有效充電量只採已完成交易的最終電量 

 **狀態:已確認(2026-09-03,Ken)** 

 ### 指標定義 

 有效充電量使用下列唯讀聚合語意: 

     validEnergyKWh = SUM(energy_consumed) / 1000 

 納入條件: 

 - Transaction status 必須是 COMPLETED。 
 - energy_consumed 必須非 null 且大於或等於 0。 
 - energy_consumed 單位為 Wh,API 聚合後轉成 kWh。 
 - 0 Wh 的 COMPLETED 交易可納入資料集合,但對有效充電量的加總貢獻為 0;不得只因為 0 Wh 就自行改成 ERROR。 

 不納入有效充電量: 

 - ACTIVE:尚未形成最終 energy_consumed,完成後才納入。 
 - CANCELLED、ERROR。 
 - energy_consumed 為 null 或負值的 COMPLETED 交易。 

 ### Partial 語意 

 - 如果查詢範圍內存在 energy_consumed 為 null 或負值的 COMPLETED 交易,Backend 將其排除於加總,並回傳 excludedEnergyTransactionCount。 
 - 上述資料品質缺漏會使 Usage 區塊標示 PARTIAL;不得靜默忽略,也不得把缺值當成 0。 
 - ACTIVE、CANCELLED、ERROR 因本來就不符合本指標定義,不計入 excludedEnergyTransactionCount。 

 ### Backend 要開發的內容 

 - 使用 e_transactions 已保存的 status 與 energy_consumed 進行 read-only aggregation。 
 - Overview DTO 回傳 validEnergyKWh、excludedEnergyTransactionCount 及 section/source status。 
 - 不從 raw MeterValue 為 ACTIVE 或缺值交易另外估算電量。 
 - 不呼叫帳務重算、不修改 Transaction,也不建立第二套 energyConsumed 寫入公式。 
 - 每日 kWh series 使用相同的 Completed/energy_consumed eligibility;跨日交易歸屬哪一個日期另案確認。 

 ### Frontend 要開發的內容 

 - 「有效充電量」顯示 Backend 回傳的 validEnergyKWh,不自行從每日 bucket 重算。 
 - 提供簡短說明:「僅包含已完成交易;進行中交易完成後納入」。 
 - section status 為 PARTIAL 時顯示部分完成交易電量缺漏,不能仍標示為完整資料。 
 - Frontend 不把 null、缺漏或排除筆數轉為 0。 

 ### A17 驗證結果 

 以 2026-08-05 至 2026-09-03 的 A17 唯讀資料套用此 eligibility: 

 - 78 筆 COMPLETED 中,77 筆正電量、1 筆 0 Wh。 
 - null 與負值均為 0 筆,因此 excludedEnergyTransactionCount = 0。 
 - validEnergyKWh = 2,171.915 kWh。 
 - 另有 1 筆 ACTIVE,不在本指標內,待交易完成後才納入。 

 此數字只是當下 baseline,不是所有案場的固定驗收值。 

 ## USG-DEC-003:每日資料依交易開始日歸屬 

 **狀態:已確認(2026-09-03,Ken)** 

 ### 日期歸屬規則 

 - 符合 USG-DEC-002 的整筆 COMPLETED 交易,依 start_timestamp 在該 building timezone 對應的日曆日期歸入一個 daily bucket。 
 - 同一交易的全部有效 energy_consumed 都歸在開始日期;即使 stop_timestamp 落在隔日或更晚,也不拆分至其他日期。 
 - kWh 與 Sessions 圖表共用相同的開始日 bucket 規則;Sessions 的 eligibility 仍另案確認。 
 - 30 日查詢範圍依交易開始日判定,採 rangeStartDate 00:00(含)至 rangeEndDate 次日 00:00(不含)的半開區間,邊界以 building timezone 為準。 
 - 一筆在範圍內開始、目前仍 ACTIVE 的交易不先估算;交易完成後,於下一次 REST refresh 納入原本的開始日 bucket,因此先前日期的 bucket 可能被補上最終數值。 

 ### 使用者會看到什麼 

 - 圖表 tooltip/說明文字標示「用量依交易開始日歸屬」。 
 - 跨日交易不會在圖表被切成兩筆 Session,也不會把單筆 energyConsumed 拆到兩個日期。 
 - 此圖表代表「依 Session 開始日整理的充電使用趨勢」,不是逐日電網 MeterValue 的精確日切報表。 

 ### Backend 要開發的內容 

 - 以 building timezone 將 start_timestamp 轉為 date bucket,不使用 browser timezone,也不硬編碼 A17。 
 - 每日 kWh series 對符合 USG-DEC-002 的交易加總整筆 energy_consumed。 
 - REST response 維持固定 30 個日期 bucket;跨日交易不執行 MeterValue day-splitting。 
 - 不修改 startTimestamp、stopTimestamp、energyConsumed 或任何交易/帳務資料。 

 ### Frontend 要開發的內容 

 - 直接使用 Backend 回傳的 bucket date、kWh 與 Sessions,不自行重新分組。 
 - kWh/Sessions 切換時日期軸完全一致。 
 - 在 tooltip 或區塊說明中顯示開始日歸屬語意,避免被誤解為逐時電表報表。 

 ### A17 影響說明 

 A17 基線中 78 筆 COMPLETED 有 10 筆跨日。依本決策,這 10 筆的整筆電量都歸到各自開始日;A17 數字只用於驗證行為,不成為其他案場的固定比例或規則。 

 ## USG-DEC-004:充電 Sessions 只計算已完成且有正電量的交易 

 **狀態:已確認(2026-09-03,Ken)** 

 ### 指標定義 

     validSessionCount = COUNT(Transaction) 

 納入條件: 

 - Transaction status = COMPLETED。 
 - energy_consumed > 0。 

 不納入條件: 

 - COMPLETED 但 energy_consumed = 0:代表沒有實際產生充電量,不計入充電 Sessions;此情況本身不自動視為 ERROR 或 PARTIAL。 
 - ACTIVE:交易完成且取得最終正電量後才納入。 
 - CANCELLED、ERROR。 
 - energy_consumed 為 null 或負值的 COMPLETED:不納入,並依 USG-DEC-002 回傳 excludedEnergyTransactionCount、將 Usage 區塊標示 PARTIAL。 

 ### 日期歸屬 

 - 符合條件的 Session 依 USG-DEC-003 歸入 building timezone 的交易開始日 bucket。 
 - kWh 與 Sessions 使用相同的 30 日日期軸及 startTimestamp 歸屬方式。 
 - 本決策只定義哪些交易算一個 Session,不改變交易生命週期或狀態。 

 ### Backend 要開發的內容 

 - Overview DTO 回傳 validSessionCount,並在每個 daily bucket 回傳對應 sessionCount。 
 - validSessionCount 與 daily sessionCount 必須使用同一套 COMPLETED + positive energy eligibility。 
 - 不沿用目前未區分 status/energy 的 transactions.size() 作為 Dashboard Sessions。 
 - 不為 0 Wh、ACTIVE、CANCELLED 或 ERROR 交易虛構有效 Session。 
 - 不修改 Transaction,也不觸發 Stop、重算或補償流程。 

 ### Frontend 要開發的內容 

 - 摘要「充電 Sessions」顯示 Backend 回傳的 validSessionCount。 
 - Sessions 圖表模式使用 Backend daily sessionCount,不自行從其他欄位推算。 
 - Tooltip/說明文字顯示「僅計算已完成且有充電量的交易」。 
 - 0 筆必須顯示 0;資料來源失敗或 null 則依 Empty/Partial/Error 規則呈現,不得混為 0。 

 ### A17 驗證結果 

 以 2026-08-05 至 2026-09-03 的 A17 唯讀資料套用此規則: 

 - 77 筆 COMPLETED 且 energy_consumed > 0,因此 validSessionCount = 77。 
 - 1 筆 COMPLETED 為 0 Wh,不計入。 
 - 1 筆 ACTIVE 尚未完成,不計入。 
 - 此數字只作為當下 baseline,不是其他案場的固定驗收值。 

 ## USG-DEC-005:V1 移除時間使用率 

 **狀態:已確認(2026-09-03,Ken)** 

 ### 決策原因 

 - 原 mockup 的「時間使用率」原意是:有效 Session duration 合計占全案場 Connector 理論可使用時數的比例。 
 - 此比例不是即時負載、設備健康度或實際有功輸出時間,單獨顯示時容易被誤解。 
 - 住宅、公用/私人設備比例與使用時段不同,缺乏共同 benchmark 時,低或高百分比無法直接判斷好壞。 
 - 使用目前 Connector 數當分母會忽略期間內新增/停用設備;建立完整歷史分母又會使 V1 過度複雜。 
 - Dashboard V1 以簡單、可理解的唯讀資訊為主,因此不實作這項指標。 

 ### Frontend 影響 

 - 從「30 日充電用量」摘要中移除「時間使用率」label、數值、tooltip 與 loading/error placeholder。 
 - 不用其他百分比直接沿用「時間使用率」名稱。 
 - 第三個摘要位置依 USG-DEC-006 顯示「平均每次充電量」。 
 - 區塊仍保留已確認的有效充電量、充電 Sessions 與 kWh/Sessions 圖表切換。 

 ### Backend 影響 

 - Dashboard overview 不需要回傳 timeUtilizationPercent、availableConnectorHours 或 utilization denominator。 
 - 不查詢歷史 Connector 啟用區間,也不新增設備 availability history。 
 - 既有 durationSeconds 欄位與其他帳務/交易用途不受影響。 

 ### V1 明確不包含 

 - 時間使用率 threshold、紅黃綠燈或告警。 
 - 跨案場利用率排名。 
 - 為了此指標新增 Connector 啟停歷史資料表。 
 - 任何會改變設備、交易或排程的操作。 

 ## USG-DEC-006:以平均每次充電量取代時間使用率 

 **狀態:已確認(2026-09-03,Ken)** 

 ### 使用者會看到什麼 

 「30 日充電用量」右側三項摘要改為: 

 1. 有效充電量。 
 2. 充電 Sessions。 
 3. 平均每次充電量。 

 不再顯示時間使用率。 

 ### 指標定義 

     averageEnergyPerSessionKWh = validEnergyKWh / validSessionCount 

 - validEnergyKWh 使用 USG-DEC-002 的有效充電量。 
 - validSessionCount 使用 USG-DEC-004 的有效 Sessions。 
 - 兩者使用相同的固定 30 日範圍及 USG-DEC-003 開始日歸屬。 
 - API 值以 kWh/次表示;畫面顯示至小數點後 2 位。 
 - 若 validSessionCount = 0,Backend 回傳 null,Frontend 顯示「—」與「尚無有效充電 Session」,不得顯示 0 kWh/次或執行除以零。 
 - 若有效電量或 Sessions 為 PARTIAL/ERROR,平均值不得標示為完整;依 section status 顯示 PARTIAL/ERROR。 

 ### Backend 要開發的內容 

 - 在同一個 Usage read model 由 validEnergyKWh 與 validSessionCount 計算 averageEnergyPerSessionKWh。 
 - Overview DTO 回傳 averageEnergyPerSessionKWh;Frontend 不自行相除。 
 - 使用 decimal 計算及明確 rounding,不使用 binary floating-point 造成顯示誤差。 
 - 不新增資料表、額外歷史查詢、WebSocket event 或業務資料 mutation。 

 ### Frontend 要開發的內容 

 - 將原 mockup 的「時間使用率」摘要替換為「平均每次充電量」。 
 - 顯示格式為小數點後 2 位及單位 kWh/次。 
 - Tooltip/輔助說明為「有效充電量 ÷ 充電 Sessions」。 
 - 依 Backend 回傳結果處理正常、null、PARTIAL 與 ERROR,不自行計算。 

 ### A17 Baseline 

 - validEnergyKWh = 2,171.915 kWh。 
 - validSessionCount = 77。 
 - averageEnergyPerSessionKWh = 28.21 kWh/次(顯示值)。 

 此數字是 A17 當下 baseline,不是其他案場固定值。 

 ### Working Mock 更新 

 本機 working mock documents/dashboard-mockup/a17-operations-dashboard.html 已更新: 

 - 副標改為「Sessions、有效電量與平均單次用量」。 
 - 第三項摘要改為「平均每次充電量」。 
 - Mock 仍使用假資料:2,213.6 kWh ÷ 136 Sessions = 16.28 kWh/次。 
 - 原 Redmine attachment #1110 保留為初始設計基準;全部需求鎖定後再上傳最終 HTML/PDF,避免中間版本混淆。 

 ## USG-DEC-007:單日零使用量與資料未知必須分開 

 **狀態:已確認(2026-09-03,Ken)** 

 ### Backend 回傳規則 

 - Usage REST response 必須依 USG-DEC-001 的 rangeStartDate 至 rangeEndDate,按日期升冪固定回傳恰好 30 個 daily buckets。 
 - 每個 bucket 至少包含 date、validEnergyKWh、sessionCount 與 bucketStatus。 
 - 查詢成功且當日沒有符合 USG-DEC-002/004 的有效交易時: 
   - validEnergyKWh = 0。 
   - sessionCount = 0。 
   - bucketStatus = COMPLETE。 
 - 當日資料來源查詢失敗、資料完整性無法確認,或 Backend 明確判定該日不可用時: 
   - 無法確認的 metric 回傳 null。 
   - bucketStatus = ERROR 或 PARTIAL。 
   - 不得以 0 代替未知。 
 - 如果只有部分日期為 null/ERROR/PARTIAL,Usage section status 必須是 PARTIAL,其他正常日期仍照常回傳。 
 - Backend 不得省略沒有 Session 的日期;response 少於 30 個 bucket 屬 contract/source 問題,Frontend 不得自行補成 0。 

 ### Frontend 呈現規則 

 - 0 kWh/0 Sessions 是有效的「當日沒有使用」資料,圖表保留該日期並顯示零高度;tooltip 顯示 0。 
 - null 是「資料無法取得」,圖表以缺口或不同於 0 的 unavailable 樣式呈現;tooltip 顯示「資料無法取得」。 
 - 日期軸固定保留完整 30 日,不因零值或 null 壓縮、刪除日期。 
 - 任一日期未知時顯示 Usage Partial 提示,但正常日期仍可閱讀。 
 - Frontend 不因 response 缺少某日期就自行建立 0 bucket;應視為資料契約異常。 

 ### Mock/Fixture 需求 

 - 目前 working mock 保持正常資料情境,不強制改成 Partial 畫面。 
 - 最終交付前須補一組 Usage fixture,能同時展示: 
   1. 查詢成功但 0 kWh/0 Sessions 的日期。 
   2. null/資料無法取得的日期。 
   3. 區塊層級 PARTIAL 提示。 
 - 假資料中的 0 與 null 必須使用不同視覺語意,供 FE review。 

 ### 不影響範圍 

 - 本決策只定義 Dashboard read model 與視覺呈現。 
 - 不修改 Transaction、MeterValue 或任何充電/帳務資料。 
 - 不因某天沒有使用量而建立告警或觸發作業。 

 ## USG-DEC-008:30 日全部為零時顯示中性 Empty State 

 **狀態:已確認(2026-09-03,Ken)** 

 ### 狀態語意 

 當 Backend 成功完成 30 日查詢,而且 30 個 daily buckets 均沒有符合 USG-DEC-002/004 的有效交易時: 

 - sourceStatus = COMPLETE,代表資料來源與查詢正常。 
 - section dataState = EMPTY,代表成功查詢但沒有使用紀錄。 
 - 30 個 daily buckets 仍完整回傳,每日 validEnergyKWh = 0、sessionCount = 0。 
 - validEnergyKWh = 0。 
 - validSessionCount = 0。 
 - averageEnergyPerSessionKWh = null,避免除以零。 
 - EMPTY 不等於 ERROR、PARTIAL 或 STALE。 

 ### Frontend 呈現 

 區塊保留標題與正常容器,顯示中性文案: 

     最近 30 日尚無有效充電紀錄 
     完成充電後,使用趨勢將顯示於此 

 摘要顯示: 

 - 有效充電量:0 kWh。 
 - 充電 Sessions:0。 
 - 平均每次充電量:—。 

 其他規則: 

 - 使用中性色彩,不使用紅色/黃色警告。 
 - 不顯示「資料載入失敗」。 
 - 不產生 Needs Attention 項目。 
 - 保留 Dashboard 的一般重新整理功能,不新增特殊操作。 

 ### Backend 要開發的內容 

 - 明確區分成功但無紀錄的 EMPTY 與查詢失敗的 ERROR。 
 - 不因查不到有效 Session 就回 null、ERROR 或 HTTP 500。 
 - 不建立假交易、假 bucket 或其他業務資料。 

 ### Mock/Fixture 需求 

 - 最終 working mock/FE fixtures 增加 Usage Empty 情境,顯示上述文案與 0/0/—。 
 - 正常資料 mock 仍保持可檢視,不以 Empty 畫面取代。 
 - Empty fixture 不得使用 error icon、warning color 或 Needs Attention badge。 

 ## USG-DEC-009:初次 Error 與後續 Refresh Error 分開呈現 

 **狀態:已確認(2026-09-03,Ken)** 

 ### 情境 A:初次載入失敗,沒有可用舊資料 

 Backend/response 語意: 

 - Usage source/section 回 ERROR;metric 與 series 為 null 或不提供可誤認為有效的數值。 
 - Overview 其他區塊若可正常取得,整體 response 仍回可呈現的 PARTIAL,而不是讓 Usage 單一來源拖垮整個 Dashboard。 
 - 不以空陣列或 30 個零 bucket 冒充成功查詢。 

 Frontend 顯示: 

     30 日充電用量暫時無法取得 
     請稍後重新整理 

 - 有效充電量、充電 Sessions、平均每次充電量均顯示「—」。 
 - 圖表顯示 Error placeholder,不顯示零值圖。 
 - 提供區塊或 Dashboard 的「重新整理」操作。 
 - Error message 必須可由螢幕閱讀器讀取,不能只使用紅色表示。 

 ### 情境 B:曾成功載入,後續 Refresh 失敗 

 - Frontend 保留該 building 最後一次成功的 Usage snapshot,不清空、不改成 0。 
 - 區塊明確顯示: 

     資料更新失敗,目前顯示上次成功資料 
     最後成功更新:{lastSuccessfulAt} 

 - 保存的資料不得標示為最新;Header/section 同步呈現 refresh error。 
 - Frontend 使用先前 Backend 回傳的 updatedAt/freshness deadline 或 effective cutoff metadata 判斷顯示狀態,不自行硬編碼 stale 秒數。 
 - 超過 Backend 定義的 freshness deadline 後顯示 STALE;未超過前仍須顯示「更新失敗」,不能假裝 refresh 成功。 
 - 下一次定時或手動 REST refresh 成功後,用新 snapshot 取代舊資料並移除錯誤/Stale 提示。 
 - 切換 building 時不得沿用前一個 building 的 Usage snapshot。 

 ### Backend 要開發的內容 

 - Overview aggregation 必須隔離 Usage 子查詢失敗,回傳可診斷的 source status/error code/updatedAt,不回敏感 exception detail。 
 - Backend 不需要為此建立 Dashboard 專用歷史快取;瀏覽器可依既有 query cache 保留同一 building 的最後成功 snapshot。 
 - Freshness policy 沿用 #1424 已確認的 Backend 集中管理規則。 
 - 不因 Retry 執行 Transaction/MeterValue/Billing 重算或其他 mutation。 

 ### Frontend 要開發的內容 

 - 明確處理 initial error、refresh error with cached data、STALE 及 recovery。 
 - Cache key 必須包含 buildingId;切換案場時清除/隔離舊案場 Usage。 
 - Retry 只重新取得 read-only overview,不觸發 WebSocket 重建以外的不相關作業。 
 - Production error 不得 fallback 到 mock fixture。 

 ### Mock/Fixture 需求 

 最終 working mock/FE fixtures 必須另外包含: 

 1. Initial Error:無舊資料,摘要為「—」、圖表 Error placeholder、重新整理。 
 2. Refresh Error:保留上次圖表與摘要,顯示 lastSuccessfulAt 與更新失敗提示。 
 3. Stale:保留資料但明確標示 STALE。 
 4. Recovery:下一次成功後恢復正常。 

 正常資料 mock 不被錯誤 fixture 取代。 

 ## V1 不開發 

 - 7 日、90 日或自訂日期範圍。 
 - Usage history export。 
 - Dashboard 內的交易或 MeterValue 明細表。 
 - Usage WebSocket publisher。 
 - 任何電量、Session 或時間使用率的寫入、修正或重算操作。 

 ## Acceptance Criteria 

 - [ ] 每次 response 的 rangeStartDate 至 rangeEndDate 依案場 timezone 恰好涵蓋 30 個日曆日,且固定回傳 30 個升冪 daily buckets。 
 - [ ] rangeEndDate 是案場當天,且該日可明確辨識為進行中。 
 - [ ] kWh 與 Sessions 使用相同 30 日範圍及 Backend 回傳的開始日 bucket;跨日交易不拆分。 
 - [ ] UI 不出現 7 日、90 日或自訂日期範圍控制。 
 - [ ] 切換 kWh/Sessions 不影響 Dashboard WebSocket 連線。 
 - [ ] 有效充電量只加總符合 USG-DEC-002 的 COMPLETED energy_consumed,正確由 Wh 轉成 kWh,並依 USG-DEC-003 歸入開始日。 
 - [ ] validSessionCount 與每日 sessionCount 只計算 COMPLETED 且 energy_consumed > 0,並使用同一開始日 bucket。 
 - [ ] 異常 Completed 的排除筆數可見且區塊標示 PARTIAL;ACTIVE 不進行 MeterValue 估算。 
 - [ ] UI 與 API 均不包含時間使用率、available Connector hours 或其 threshold。 
 - [ ] averageEnergyPerSessionKWh 由 Backend 以 validEnergyKWh ÷ validSessionCount 計算,顯示至小數點後 2 位。 
 - [ ] validSessionCount = 0 時平均值為 null/「—」,不得除以零或顯示成 0 kWh/次。 
 - [ ] 成功查詢的無使用日期顯示 0;未知日期顯示 null/圖表缺口,且 section 標示 PARTIAL。 
 - [ ] 30 日全部為零時回 COMPLETE + EMPTY,UI 顯示 0 kWh/0 Sessions/—,不顯示錯誤或告警。 
 - [ ] Initial Error 不顯示 0;Refresh Error 保留同案場最後成功 snapshot 並顯示 lastSuccessfulAt,超過 Backend freshness deadline 後標示 STALE。 
 - [ ] Usage 子查詢失敗不影響其他正常 Dashboard 區塊,Production 不 fallback 到 fixture。 
 - [ ] 所有資料皆為唯讀彙整,不產生任何業務狀態變更。 

 ## 已驗證的程式碼與 A17 基線(非需求決策) 

 ### 現有程式碼 

 - e_transactions 已有 status、start_timestamp、stop_timestamp、meter_start、meter_stop、energy_consumed 與 duration_seconds。 
 - TransactionService 在交易完成時以 meterStop - meterStart 寫入 energyConsumed,單位為 Wh,並將狀態改為 COMPLETED。 
 - 現有 Transaction status 包含 ACTIVE、COMPLETED、CANCELLED、ERROR。 
 - MeterValueTariffCalculationService 另有較完整的 MeterValue 分段與帳務邊界處理;是否需要把該複雜度帶入 Dashboard Usage,尚未決定。 

 ### A17 Read-only Baseline 

 查詢時間:2026-09-03;案場日曆範圍:2026-08-05 至 2026-09-03。 

 - Building:BLD000000014;4 個 Charge Points、12 個 Connectors。 
 - 最近 30 日共有 78 筆 COMPLETED、1 筆 ACTIVE。 
 - 78 筆 COMPLETED 均有 meterStart、meterStop 與 energyConsumed。 
 - 77 筆 energyConsumed > 0、1 筆等於 0、0 筆 null、0 筆負值。 
 - 唯一的 0 Wh COMPLETED 交易持續 15 秒,StopReason 為 EVDisconnected;77 筆正電量交易最短 69 秒。此差異只作為 Sessions 定義的討論依據。 
 - 77 筆有效 Sessions 的 durationSeconds 合計 1,124,353 秒(312.320 小時)。 
 - 其中 38 筆 durationSeconds 與 stopTimestamp - startTimestamp 相差 -1 秒,全部都是秒級精度截斷,沒有小時級偏移;以 timestamp 計算則為 312.331 小時。 
 - A17 目前有 12 個 Connector,全部隸屬 enabled Charge Point,且最近 30 日沒有新增 Connector。 
 - 若以目前 12 個 Connector × 30 日 × 24 小時為分母,A17 時間使用率 baseline 為 3.6148%;mockup 的 4.08% 仍是展示假資料。 

 - 78 筆的 energyConsumed 均與 meterStop - meterStart 一致。 
 - COMPLETED 正電量合計為 2,171.915 kWh。 
 - 其中 10 筆 COMPLETED 跨越案場日曆日,表示後續仍須明確決定每日 bucket 依交易開始日、完成日或 MeterValue 實際日切分。 

 以上數字只用於驗證方案與資料品質,不是所有案場的固定規則,也不是正式驗收期待值。 

 ## 待確認事項 

 - [x] 「有效充電量」只加總 COMPLETED 且 energy_consumed 非 null、非負的交易;異常 Completed 排除並標示 Partial(USG-DEC-002)。 
 - [x] kWh 與 Sessions 的每日 bucket 均依 building timezone 的交易開始日歸屬;跨日不拆分(USG-DEC-003)。 
 - [x] 「充電 Sessions」只計算 COMPLETED 且 energy_consumed > 0 的交易;依開始日歸屬(USG-DEC-004)。 
 - [x] V1 移除「時間使用率」,不定義分子、分母、threshold 或 Backend 欄位(USG-DEC-005)。 
 - [x] 以「平均每次充電量」取代時間使用率,由 Backend 以有效電量 ÷ 有效 Sessions 計算(USG-DEC-006)。 
 - [x] 查詢成功且無有效 Session 的日期回 0;資料未知回 null;固定回傳 30 buckets,部分未知時 section 為 PARTIAL(USG-DEC-007)。 
 - [x] 整段 30 日成功但全為 0 時,sourceStatus = COMPLETE、dataState = EMPTY,顯示中性 0/0/— 與無紀錄文案(USG-DEC-008)。 
 - [x] 初次失敗顯示 Error/—;後續 refresh 失敗保留同 building 上次成功資料、顯示 lastSuccessfulAt,並依 Backend freshness 轉為 STALE(USG-DEC-009)。 

 ## 需求管理狀態 

 - Parent:#1422 
 - Requirement status:**本區塊需求已確認,待其他 Dashboard 區塊完成後整併** 
 - Confirmed decision:USG-DEC-001、USG-DEC-002、USG-DEC-003、USG-DEC-004、USG-DEC-005、USG-DEC-006、USG-DEC-007、USG-DEC-008、USG-DEC-009 
 - Final specification:所有 Dashboard 區塊確認後再整併至 #1422 v1.0 文件 
 - Implementation tickets:需求鎖定後才建立 FE/BE/QA child issues 

返回