CR-013 兌換現金價值(CASH_VALUE)由折算率計算並納入契約回應
| 項目 |
內容 |
| 文件編號 |
CR-013 |
| 版本 |
1.0(BEAM PMO 2026-09-22 核定執行——「當然點頭,請執行」;已執行並真機實測通過,見 §5) |
| 狀態 |
✅ 已核定/已執行:cash_value = pointsUsed × POINTS_TIERS.REDEEM_RATE(每 1 點可折抵之現金(元)),以分四捨五入(HALF_UP);由後端依權威參數計算,契約 RedeemRequest 不收現金價值、RedemptionView 補回 cashValue
|
| 提出日期 |
2026-09-22 |
| 提出者 |
WorkBuddy AI(開發團隊) |
| 關聯 |
SCS-010 §10 #11(本 CR 之緣起)、CR-012 §8.4(判定不併案、另案處理)、SDD-001 §3.8(REDEMPTIONS.cash_value 欄已存在)、SDD-005 §9(POINTS_TIERS) |
| 嚴重度 |
🟡 中(執行前:現金價值恆 0,兌換之「點數換多少錢」無法稽核;執行後:單據自證) |
1. 緣起
SCS-010 §10 #11:REDEMPTIONS.CASH_VALUE 恆為 0——契約 RedeemRequest/
RedemptionView 皆無現金價值欄位,折算率(POINTS_TIERS.REDEEM_RATE)無來源可驗證,
待 CR 決定是否由契約帶入。CR-012 §8.4 評估後判定不併案(改動面無交集),另案處理——即本 CR。
2. 裁定內容(BEAM PMO 2026-09-22 授權)
| # |
裁定 |
理由 |
| 1 |
REDEEM_RATE 語意定案:每 1 點可折抵之現金金額(元) |
種子值 CLASSIC 0.010000/PLATINUM 0.012500(每點 0.01/0.0125 元)之自然解讀;NUMBER(9,6) 容納此精度 |
| 2 |
cash_value(分)= pointsUsed × REDEEM_RATE × 100,四捨五入至整數分(HALF_UP) |
最小單位為分(SDD-001 §1.3 金額通則);HALF_UP 為對帳展示之一般慣例。⚠️ 若日後核對客戶規範明定進位方向(如捨去),再修約 |
| 3 |
由後端計算,RedeemRequest 不收現金價值 |
財務金額由客戶端宣稱即是漏洞——折算率之權威來源是 POINTS_TIERS,不是呼叫端 |
| 4 |
RedemptionView 補回 cashValue |
單據自證(同 pointsBefore/after 之設計哲學) |
| 5 |
折算率缺漏 → 以 0 計、兌換照常(fail-soft) |
兌換是客戶既有權益,參數缺漏不得擋兌換;現金價值記 0 屬保守方向(不會多給),與 earn_multiplier 缺漏給 1 倍(對客戶有利)同一哲學、方向各自正確;倉儲層記 WARN 供維運察覺 |
3. 執行清單
| 層 |
變更 |
| Port |
PointsRepository 新增 findRedeemRate(tierCode)(javadoc 載明語意與 fail-soft 設計) |
| 基礎設施 |
JdbcPointsRepository.findRedeemRate——比照 findEarnMultiplier(查無生效層級 → 0 + WARN) |
| 應用 |
RedemptionService.redeem:以帳戶自身之 tier 查折算率 → 計算 cashValue → 傳入 RedemptionOrder.create(原寫死 0L 處) |
| 檢視 |
RedemptionView 補 cashValue(PointsViewAssembler 同步);RedeemRequest 刻意不加欄位(裁定 3) |
| 契約 |
gen_openapi.py:RedemptionView 補 cashValue(描述含語意、公式與進位規則)。checksum 比對:17 份僅 rewards-service.yaml 變動;驗證 0 錯誤(72 端點)
|
| 測試 |
RedemptionServiceTest 11 → 14(折算率計算 3125/進位 HALF_UP 154/缺漏記 0 兌換照常);FakePointsRepository 增 setRedeemRate。單測合計 69 → 72 例 0 失敗
|
4. 契約示例
// RedemptionView(201 回應之 data)
{
"redemptionNo": "RD2026090019",
"redemptionType": "GOODS",
"settlementMethod": "POINTS_ONLY",
"pointsUsed": 2500,
"cashValue": 3125, // 2500 點 × 0.0125 元/點(PLATINUM)= 31.25 元 = 3125 分
"pointsBefore": 7485,
"pointsAfter": 4985
}
// RedeemRequest:無 cashValue 欄位——客戶端不得宣稱現金價值(裁定 3)
5. 真機 e2e(8091/FREEPDB1,2026-09-22)
| # |
情境 |
結果 |
| 1 |
0013(PLATINUM,折算率 0.0125)兌換 2,500 點 |
201;回應 cashValue=3125;DB RD2026090019 CASH_VALUE=3125 ✅ |
| 2 |
同帳戶兌換 123 點(進位案例:123 × 0.0125 = 1.5375 元 = 153.75 分) |
201;cashValue=154(HALF_UP);DB RD2026090020 CASH_VALUE=154 ✅ |
帳務自洽:0013 可兌換 7,485 → 4,985(兩筆合計扣 2,623 點)。
6. 未竟與後續
-
WebUI 顯示現金價值:
RedemptionView 已回傳,畫面下次強化時一併顯示(屆時同步 SDD-007 §5.4.7)——本次刻意不動 UI,維持「規格、實物、文件三者同步」之最小變更。
-
進位方向之客戶規範核對:HALF_UP 為本案裁定;若客戶規範明定「捨去」等方向,修約即可(單點改
RoundingMode)。
-
merchantShare(商家分攤)仍固定 0——屬另一功能缺口,不在本 CR。
7. 修訂記錄
| 版本 |
日期 |
修訂內容 |
| 1.0 |
2026-09-22 |
立案+BEAM PMO 核定+執行完畢(一次完成):§10 #11 之 CASH_VALUE 由折算率計算並納入契約回應;語意/公式/進位/fail-soft 五項裁定;真機 e2e 2 情境+DB 複核通過;單測 69 → 72 例 0 失敗 |