動作
Bug #1395
已結束[Branch Bug] 刪除 Charge Group 前未檢查仍被 Connector 使用
開始日期:
2026-07-29
完成日期:
預估工時:
概述
問題描述¶
目前刪除 Charge Group 時,API 應先確認是否仍有 Connector 的 chargeGroup 指向該 Group。若直接刪除仍被使用的 Group,可能留下失效關聯,並使私人樁的手動排隊、離峰排程與容量計算出現不一致。
本議題由 #1382 driverState/離峰充電需求釐清過程發現,但屬於獨立的 Charge Group 資料完整性問題,不納入 #1382 範圍。
預期行為¶
- 收到刪除 Charge Group request 時,先查詢是否仍有任何 Connector 屬於該 Group。
- 若仍有 Connector:
- 拒絕刪除,Group 資料保持不變。
- 回傳明確的 domain error,例如
CHARGE_GROUP_IN_USE。 - 回覆訊息應讓管理者知道必須先移除或重新指派 Connector;可附帶使用中的 Connector 數量。
- 若沒有 Connector 使用該 Group,才允許刪除。
- 不得透過 cascade、設為 null 或其他隱含方式,讓使用中的 Connector 靜默失去 Charge Group。
- API 驗證與刪除應具備一致性,避免檢查後到刪除前被新增 Connector 關聯的競態。
驗收條件¶
- Given Charge Group 仍被至少一支 Connector 使用,When 呼叫刪除 API,Then 刪除失敗並回
CHARGE_GROUP_IN_USE,Group 與 Connector 關聯均維持原狀。 - Given Charge Group 沒有任何 Connector 使用,When 呼叫刪除 API,Then 刪除成功。
- 補上 service/API regression tests,涵蓋「使用中拒絕」與「未使用可刪除」。
- 更新 EMS E2E catalog,新增或更新 Charge Group 刪除資料完整性案例。
非本議題範圍¶
- #1382 的
driverState、LIFF、離峰 switch 與 WebSocket 行為。 - Connector 更換或移除 Charge Group 時的離峰模式處理規則;如需調整另行確認。
是由 陳國瑋 於 約 2 個月 前更新
#1395 後端完成/FE 實作交接¶
後端已合併至 private:b634c30(功能 commit:953126c)。
API contract¶
1. 刪除前查詢引用數¶
GET /api/charge-group/connector-count/{chargeGroupId}
成功回應:
{
"chargeGroupId": "CG001",
"connectorCount": 2
}
2. 刪除 Charge Group¶
DELETE /api/charge-group/delete/{chargeGroupId}
- 未被使用:
200 OK,無 response body。 - Group 不存在:既有
404 Not Found。 - 仍被 Connector 使用:
409 Conflict,body 為:
{
"errorCode": "CHARGE_GROUP_IN_USE",
"chargeGroupId": "CG001",
"connectorCount": 2,
"message": "此充電群組仍被 2 支 Connector 使用,請先移除或重新指派 Connector"
}
Ryan 的 Branch Admin FE 需求¶
- 使用者點擊刪除前,先呼叫 connector-count API。
-
connectorCount > 0:不要呼叫 DELETE;顯示不可刪除提示與引用數,請使用者先移除或重新指派 Connector。 -
connectorCount = 0:依既有 UX 顯示確認框,確認後才呼叫 DELETE;成功才更新/重取列表。 - 預查 API 失敗時採 fail-safe:不可繼續刪除,顯示錯誤。
- 即使預查為 0,DELETE 仍可能因並行 Connector 指派而回 409;此時顯示後端
message與connectorCount、保持列表資料、不自動重試,也不得誤顯示刪除成功。 - FE 請以
errorCode === "CHARGE_GROUP_IN_USE"判斷,不要比對 message 字串。
驗收:UI-022(P1 Planned)。後端已在 Expo isolated DB clone 完成整合測試(3/3)與單元測試(5/5),clone 已清除。
是由 鍾正剛 於 約 2 個月 前更新
【FE 開發完成】#1395 Branch Admin 充電群組刪除
branch: fix/redmine-1395-charge-group-delete-in-use(base: private)
一、實作內容
- 點擊刪除先呼叫
GET /api/charge-group/connector-count/{id}(需求 1)。 -
connectorCount > 0:不送 DELETE,直接以警告對話框顯示引用數與後續處理方式(需求 2)。 -
connectorCount = 0:維持既有 SweetAlert 確認框,確認後才送 DELETE,成功才 invalidate 列表(需求 3)。 - 預查失敗(含回應格式非預期)一律 fail-safe 中止刪除,不再當成 0 放行(需求 4)。原本的實作是 fail-open,這次修掉。
- 409 以
errorCode === "CHARGE_GROUP_IN_USE"判斷,顯示後端message與connectorCount,不 invalidate 列表、不重試、不顯示成功(需求 5、6)。
二、順帶修掉的既有缺陷
- 409 會同時跳 SweetAlert 與 toast 兩則提示,且 SweetAlert 只顯示「409 Conflict」。原因是 crudHooksFactory 的 useDelete 已內建 onError toast,呼叫端再掛的 per-call onError 不會取代它,兩者都會執行;而 commonAlertError 讀的是 err.response.data.message,但 middleware 掛上的 err.response 是 Response 物件、沒有 .data,所以 fallback 到 "409 Conflict"。現在每條失敗路徑只會出現一則提示:業務阻擋走對話框,其餘錯誤走 toast。
- 刪除改用專屬的 useDeleteChargeGroup,不走 crudHooksFactory。原因是 factory 的 onError 會先呼叫 getErrorMessage 讀掉 response body,而 body stream 只能讀一次,呼叫端就再也拿不到 errorCode,無法滿足需求 6。
- DELETE 改為檢查 openapi-fetch 回傳的 error。errorMiddleware 在 401 走 signOutToLogin 後是 return response 而非 throw,原本會誤報刪除成功。
三、後續事項(給後端)
-
GET /api/charge-group/connector-count/{id}目前在 OpenAPI 沒有宣告 response schema,產生的型別是{[key: string]: unknown},FE 只能用as any取connectorCount。若方便,麻煩補上 response DTO,FE 再跑一次 openapi:refresh 就能拿到型別。 - 已知但這次未處理:crudHooksFactory / error-handler 會讀掉 response body,任何要判斷結構化 domain error 的功能都得像這次一樣繞過 factory。要根治得改共用層(讓 middleware 先把 body 解析好掛在 error 上),影響全站所有 request,不在本票範圍,建議另開票。
四、E2E catalog
UI-022 由 Planned 轉 Active,並補上「預查失敗須 fail-safe」與「任一失敗路徑只能出現一則提示」兩條驗收點。validator PASS。
動作