Project

General

Profile

CR #214 » CR-013_兌換現金價值由折算率計算並納入契約回應.md

內容更新:內部稱謂去識別化(2026-10-18) - Redmine Admin, 10/18/2026 05:32 AM

 

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. 未竟與後續

  1. WebUI 顯示現金價值:RedemptionView 已回傳,畫面下次強化時一併顯示(屆時同步 SDD-007 §5.4.7)——本次刻意不動 UI,維持「規格、實物、文件三者同步」之最小變更。
  2. 進位方向之客戶規範核對:HALF_UP 為本案裁定;若客戶規範明定「捨去」等方向,修約即可(單點改 RoundingMode)。
  3. merchantShare(商家分攤)仍固定 0——屬另一功能缺口,不在本 CR。

7. 修訂記錄

版本 日期 修訂內容
1.0 2026-09-22 立案+BEAM PMO 核定+執行完畢(一次完成):§10 #11 之 CASH_VALUE 由折算率計算並納入契約回應;語意/公式/進位/fail-soft 五項裁定;真機 e2e 2 情境+DB 複核通過;單測 69 → 72 例 0 失敗
    (1-1/1)