專案

一般

配置概況

動作

Feature #1400

進行中

[Branch後台][BE] 充電樁狀態改由 WebSocket 推送,並補齊維運欄位與操作 API

是由 鍾正剛3 天 前加入. 於 約 14 小時 前更新.

狀態:
In Progress
優先權:
High
被分派者:
開始日期:
2026-07-31
完成日期:
完成百分比:

0%

預估工時:

概述

背景

Branch Admin「連接器 → 編輯 → 充電樁狀態」分頁(react/branch/app/(dashboard)/connector/components/connector-status.tsx)目前全靠 REST 輪詢取得狀態:

  • GET /api/connector/status/{connectorId}AVAILABLEPREPARINGCHARGING 時每 2 秒輪詢一次。
  • 另外為了判斷是否可遠端操作,再打 ChargePoint API 取得 ocppConnectionStatus,離線時每 10 秒輪詢。
  • 送出 start/stop 後靠 setTimeout(3000) 硬等 3 秒才 refetch,用猜的方式等待後端狀態落地。

問題:

  1. 後端資料實際上只在 OCPP StatusNotificationMeterValues 進來時才更新(MeterValueServiceTransactionServiceChargingOrchestratorChargePointOfflineService 都會發 connector event)。2 秒輪詢絕大多數拿到同一份資料,是純浪費,卻又不保證比推送快。
  2. 同一份 Connector 狀態,LIFF(#1399)已改為由 WebSocket 推送的 ConnectorForHqDto 取得,後台仍是輪詢。同一個真實狀態有兩條取得路徑、兩種時序,出事時無法對齊時間軸。
  3. 一個畫面同時掛兩條不同節奏的輪詢,statusocppConnectionStatus 可能短暫互相矛盾,已經需要在 FE 使用 prevOnlineRef 補償。

注意:本案不是要後台照抄 LIFF 的顯示或操作方式。 LIFF 的 driverStateactions[] 是車主語意(OFF_PEAK_WAITING、「取消排隊」),授權綁住戶關聯與 transaction owner,管理員不適用也不需要。

本案只要求資料取得管道與來源一致:同一份 Connector 狀態、同一個推送時機,後台使用自己的維運語意呈現與操作。

目標

  • 提供後台專用的 Connector 狀態 WebSocket 推送,讓 FE 可移除輪詢與 3 秒硬等。
  • 後台需要的維運欄位由同一份 payload 一次給齊,不再為了單一欄位另打一支 API。
  • 後台充電操作維持現行 operator 授權模型,但補上可追蹤性與重送防護。

A-1. 新增後台專用 Connector WebSocket endpoint(ems_branch)

  • ems_branch 目前只有 OCPP 使用的 /ws/ocpp/*,沒有供 UI 使用的 WebSocket,需新增。

  • 不可沿用 HQ 的 /ws/connector/{buildingId}/{connectorId}:該 endpoint 的 LineIdHandshakeInterceptorxLineId 查詢 findByLineIdAndBuildingId 驗證,後台管理員沒有 LINE 身分,也不是該 building 住戶,握手必定被拒。

  • 握手驗證改用 Branch 既有的 ss3a 身分(JWT),並套用該管理員對此 Connector/Building 的既有權限判斷。授權失敗時的 close code 與原因請明確定義,供 FE 分辨「無權限」與「連線失敗」。

  • Branch 本身就是 connector event 的 publisher,直接由 Branch 推送即可,不需要繞 RabbitMQ 再回來

  • 推送時機請與現有 ConnectorEventPublisher 的觸發點一致,包括:

    • 狀態變更
    • MeterValues
    • 交易起訖
    • 離線偵測
    • remote operation lifecycle
  • 連線建立時需提供一份 initial snapshot,FE 不再另打 detail REST。

A-2. Payload 欄位需求

Base 沿用 ConnectorForHqDtoGET /api/connector/status/{connectorId} 現在回傳的就是它),後台已使用:

idstatusstatusLabelstartTimecurrentPowercurrentVoltagecurrentCurrentenergyUsagetotalEnergyremainTimecostPredictcurrentTariff

需要補進同一份 payload 的維運欄位:

欄位 用途 目前如何取得
ocppConnectionStatus(id + label) 判斷是否可遠端操作、離線時停用按鈕 另打 ChargePoint API 並自行輪詢
chargePointId 關聯導頁與 engineer tools 由 FE 從 ConnectorDto 帶入
lastHeartbeatAt 判斷離線多久,區分剛斷線與長期失聯
statusChangedAt 判斷狀態停滯多久,例如卡在 PREPARING
errorCodevendorErrorCodevendorId FAULTED 時的實際故障原因 無,後台目前只顯示「故障/請聯繫技術支援」
currentTransactionId 對照充電記錄、engineer tools 追查
currentIdTag/使用者識別 知道現在是誰在充電,公樁停止前確認
activeOperation(#1398 已有) 有未終結的 RemoteStart/RemoteStop 時,避免管理員重複送指令

driverState(車主語意)在後台不使用;payload 帶著無妨,但後台不得使用它決定顯示或操作。

A-3. 操作面 API

後台維持現行:

  • POST /api/tx/charge/start
  • POST /api/tx/charge/stop

以上 API 以 ss3a 管理員身分帶入 userId不改用 POST /api/connector/commands

後者的 authorizeConnector() 會檢查住戶關聯,公樁停止時還要求操作者是 transaction IdTag owner,管理員一律會被 CONNECTOR_FORBIDDEN 擋下。

但需要補齊以下項目:

  1. 回傳 operationId

    operationId 必須與 #1398e_remote_transaction_log.id 一致,讓後台操作能與 LIFF、Branch、OCPP log 串起同一條時間軸。目前後台送出的指令在稽核上是斷點。

  2. 明確的成功/失敗語意

    現行回應是 isSuccess + message,FE 只能將訊息原樣顯示給使用者。

    請提供穩定的 errorCode,例如:

    • 充電樁離線
    • 已有未終結 operation
    • 狀態不允許

    讓 FE 能依錯誤類型分流,而不是比對訊息字串。

  3. operation 結果透過 A-1 的 WebSocket 推送

    以 WebSocket 推送 operation 結果,取代 FE 目前的 setTimeout(3000) 硬等。

  4. 已有未終結 operation 時明確拒絕

    activeOperation 不為 null 時,不得讓管理員再次送出 RemoteStart/RemoteStop。

    #1398 §15.2 已規定同一 Connector 不得存在重疊的未完成 operation,後台入口也必須遵守。

不在本次範圍

  • Connector 列表頁的狀態顯示,仍走既有查詢;本案先處理單一 Connector 詳細狀態。
  • Engineer tools(#1380)既有的 OCPP/Modbus 指令面板。
  • LIFF 端行為(#1397/#1398/#1399)。

驗收重點

  • 後台可透過新 WebSocket endpoint,以管理員身分連線並取得 initial snapshot;非授權身分(含 LIFF lineId)連線時會被拒絕,且 close code 定義明確。
  • Payload 一次帶齊 A-2 的全部欄位,statusocppConnectionStatus 來自同一份資料。
  • 推送時機涵蓋狀態變更、MeterValues、交易起訖、離線偵測與 remote operation lifecycle。
  • FAULTED 時,payload 帶出實際的 errorCodevendorErrorCode
  • start/stop 回傳 operationId 與穩定的 errorCode;已有未終結 operation 時,操作會被拒絕。
  • 後台送出的充電操作,可使用 operationId 在 Branch log 追蹤到對應的 RemoteStart/RemoteStop。

對應 FE ticket

FE 實作見 #1401


相關的議題清單 1 (1 進行中0 已結束)

阻擋 EMS - Feature #1401: [Branch後台][FE] 充電樁狀態改吃 WebSocket,並修正充電指令自動重送New鍾正剛2026-07-31

動作

是由 鍾正剛3 天 前更新

是由 鍾正剛3 天 前更新

  • 阻擋 Feature #1401: [Branch後台][FE] 充電樁狀態改吃 WebSocket,並修正充電指令自動重送 已新增

是由 鍾正剛3 天 前更新

  • 被分派者 設定為 陳國瑋

是由 陳國瑋約 23 小時 前更新

是由 陳國瑋約 15 小時 前更新

  • 狀態New 變更為 In Progress

需求確認與實作計畫已核准,開始 Backend 實作。

是由 陳國瑋約 15 小時 前更新

Backend 實作已完成,等待測試計畫核准。

已完成:

  • 一次性 30 秒 connector-bound WebSocket ticket REST API
  • Branch Admin Connector WebSocket initial snapshot、shared snapshot 與 initiating-admin operation result
  • RabbitMQ 與 Admin WebSocket transport 隔離;無 viewer 時不查完整 snapshot
  • /api/tx/charge/start|stop 改採 JWT identity,補齊 200/202/403/409 structured OpenAPI schema
  • Admin Start 使用自己的 BranchUser/IdTag;私樁 queue、公樁 reserved slot、離峰 ON guard 沿用 #1398
  • Admin Stop 可停止其他 user 的 ACTIVE transaction;無 ACTIVE transaction 不送 ChangeAvailability
  • SS3A migration 與 E2E catalog 已補齊
  • #1401 已更新完整 FE contract

目前驗證:

  • mvn -DskipTests package:PASS
  • 3 個隔離 unit method:PASS
  • E2E catalog validator:PASS(151 cases)

是由 陳國瑋約 14 小時 前更新

#1400 Backend 已 commit 並部署至 ken 測試環境。

Commit:

  • 5e0b58c feat(#1400): 新增管理後台充電樁 WebSocket 控制

ken 部署:

  • Branch container: ems-branch-api
  • 目標: 192.168.127.3:/opt/ems/ems_branch/api
  • 備份: /opt/ems/backups/20260802-230118-redmine-1400
  • 使用 conf/ken 建置,JAR SHA-256: 1ff3ede95476634fbf066f55a85124337894c14dd8a41917ced5edf0aa04199f
  • container 重建後狀態: Up
  • /api-docs: HTTP 200
  • OpenAPI 已確認包含 /api/connector/admin-websocket-ticket、/api/tx/charge/start、/api/tx/charge/stop

Database:

  • 已在 ken ems_branch DB 套用 documents/sql/redmine-1400-branch-admin-connector-websocket.sql
  • A1218 已建立並綁定 connector_mgmt_view/edit
  • A1401/A1402 已移除 hq_function,改綁定 connector_mgmt_edit

測試與限制:

  • 核准的隔離 unit test: 36/36 PASS
  • E2E catalog validator: PASS(151 cases)
  • 尚未執行人工 E2E;需待 FE #1401 完成後再進行完整 Branch Admin WebSocket E2E 驗證
  • Issue 維持 In Progress,尚未結案。
動作

匯出至 Atom PDF