CR #206 » CR-005_定價服務契約缺口.md
CR-005 定價服務(pricing-service)契約缺口
| 項目 | 內容 |
|---|---|
| 編號 | CR-005 |
| 版本 | 1.2(2026-09-19 核定並執行完畢;2026-09-22 §8 #1 結案——TX_TYPE 增設) |
| 狀態 | ✅ 已核定/已執行/已實測 |
| 提出日期 | 2026-09-19 |
| 核定日期 | 2026-09-19(BEAM PMO:四項全數授權我方設計) |
| 提出者 | WorkBuddy AI |
| 影響文件 |
SCS-006(升 1.1)、contracts/openapi/pricing-service.yaml(由 tools/gen_openapi.py 產生)、SDD-006 §3.4、ops/local/init/oracle/11-dev-pricing-seed.sql
|
| 關聯 |
HANDOVER §3.C #C-3(pricing-service 為★關鍵路徑) |
核定結論(BEAM PMO 2026-09-19 原意)
fees端點——以契約為主(GET查詢費用設定),除非與其他地方衝突。interest-inquiries參數——由我方決定。- ADB 輸入——由我方設計。
- 範例數字——由我方定奪。
並指示「把 REST 端點做完」。四項之定奪見 §6,實作與實測見 §7。
1. 背景
pricing-service 為關鍵路徑——statement-service 之「最低應繳參數化」與
posting-service 之 recalculateInterest 皆等待它(見 SCS-011 §10 #4、
SCS-007 §10)。動工前盤點契約與規格,發現4 處缺口:
其中 2 處屬規格與契約直接矛盾,若自行認定將造成「文件與實物不一致」。
依 GOV-003 節奏第 3 條「有缺口先補 SPEC,不邊寫邊猜」,先提本 CR。
2. 缺口明細
#1 GET /api/v1/pricing/fees —— 方法與語意矛盾 🔴
| 來源 | 記載 |
|---|---|
SCS-006 §6 |
/api/v1/pricing/fees POST「費用計算」 |
contracts/openapi/pricing-service.yaml(第 171 行) |
/api/v1/pricing/fees GET「查詢費用設定」 |
兩者方法不同、語意亦不同(計算 vs 查詢)。且契約之 FeeView
(第 466 行)欄位為 feeCode/feeName/amount/currency,
無任何輸入欄位——無法表達「對哪個帳戶/哪筆交易計費」。
若依
SCS-006實作為 POST 計算,則與現行契約不符;
若依契約實作為 GET 查詢,則SCS-006 §6記載錯誤。
#2 GET /api/v1/pricing/interest-inquiries —— 無查詢參數 🔴
契約第 96–170 行宣告此端點,但 parameters 僅有 X-Trace-Id,
無任何查詢參數(無 accountId/periodStart/periodEnd)。
回應卻是完整的 InterestCalcView(含 interestAmount/details[])。
→ 呼叫方無從指定要查什麼,端點無法實作。
#3 ADB 計息缺「日終餘額序列」輸入 🔴
-
SCS-006 §7:「ADB(平均日餘額)為預設,需傳入日終餘額序列與計息天數」 -
InterestCalcRequest(契約第 361–389 行)欄位僅:
accountId、cardNo、periodStart、periodEnd、
principalAmount(nullable,無預設)
→ 沒有可傳入日終餘額序列的欄位。ADB 無輸入即無法計算。
#4 契約範例數字不自洽 🟡
InterestCalcView 之 example(契約第 419–435 行):
principalAmount=52345、days=30、appliedRate=0.185、newInterest=850。
實算:
| 計息基礎 | 算式 | 結果 |
|---|---|---|
| 年利率/365 天 | 52345 × 0.185 × 30 ÷ 365 |
795.9 |
| 年利率/360 天 | 52345 × 0.185 × 30 ÷ 360 |
807.4 |
兩者皆非 850。範例與任一常見慣例都不吻合,
不能作為演算法之驗收依據。
3. 建議方案(待 BEAM PMO 裁定)
#1 fees 端點
| 選項 | 說明 | 影響 |
|---|---|---|
| A(建議) | 二者都要:POST /fees 計算 + GET /fees 查規則設定 |
契約新增 POST;SCS-006 §6 補 GET。語意最完整 |
| B | 僅 POST /fees(計算),改契約 |
查不到費用規則設定,WebUI 無從顯示 |
| C | 僅 GET /fees(查詢),改 SCS-006 §6
|
費用計算無端點,ISS-2.5 無法驗收 |
#2 interest-inquiries 參數
建議補 accountId(UUID,必填)、periodStart、periodEnd(選填,
預設最近一期)。查無資料時回 CC-PRC-1002(計息期間重疊)或 404。
#3 ADB 輸入
| 選項 | 說明 |
|---|---|
| A(建議) |
InterestCalcRequest 新增 dailyBalances[](日期+餘額)。由呼叫端(posting-service)提供 |
| B | 由 pricing-service 反查 posting-service 之日終餘額——引入服務間依賴,與 SCS-006 §8「計算為純函式」衝突 |
| C | 本期僅支援 FLAT(不計 ADB),ADB 待輸入來源確定後再開 |
#4 範例數字
建議以實測結果回填,並於契約註明計息基礎(365 或 360 天)。
4. 不受缺口影響、可立即施作之範圍
以下與上述缺口無關,可先行實作並實測:
| 項目 | 依據 |
|---|---|
PricingRule 聚合(20 欄位,含 RULE_TYPE/CALC_METHOD/STATUS 值域) |
SCS-006 §5;實測 Oracle CC_PRC.PRICING_RULES 20 欄全數吻合
|
PricingRuleRepository 埠 + JDBC 實作 |
SCS-006 §4 |
適用規則解析(依 RULE_TYPE/PRODUCT_CODE/ORG/TIER/EFFECTIVE_FROM-TO) |
SCS-006 §7「試算須帶 AS_OF」 |
PRICING_RULES 種子資料 |
實測現況 0 筆——無種子則任何查詢皆空 |
錯誤碼 CC-PRC-1001/1002/3001/3002
|
SDD-006 §3.4;ErrorCodes.java 8 個 PRC 常數已備
|
5. 誠實揭露(提出時)
- 本 CR 提出時僅盤點缺口,未變更任何契約或規格檔案。
- 契約為產生器產物(
tools/gen_openapi.py),不可手改 yaml;
核定後須改產生器再重跑(同CR-002之處理方式)。 -
pricing-service提出時僅有骨架(1 個PricingServiceApplication+ 4 個LayeringTest),
尚無任何領域程式。
6. 核定結果與定奪(2026-09-19)
#1 fees 端點 —— 以契約為準:GET 查詢費用設定
- 依 BEAM PMO 指示「以契約為主,除非您發現與其他地方衝突」。
- 經查無其他衝突:
SDD-006 §3.4之CC-PRC-*錯誤碼與SCS-006 §8
皆未假定fees為寫入操作。 - 故
SCS-006 §6表格之「POST費用計算」須隨之更正(已於 1.1 版修正),
使文件與契約一致。 - 本服務不做費用計算,僅回傳設定(金額取自
FEE_AMOUNT)。
#2 interest-inquiries 參數 —— 定奪如下
| 參數 | 必填 | 說明 |
|---|---|---|
accountId |
✅ | 帳戶識別碼 |
periodStart |
— | 省略時為 periodEnd 前 30 日 |
periodEnd |
— | 省略時為系統日 |
principalAmount |
— | 本服務不保存帳戶餘額,須由呼叫方提供;省略時本金視為 0 |
cardNo |
— | 省略時回應之 cardNo 為空 |
語意定奪:
SCS-006 §6表格載明本端點為「利率查詢」,
故定位為唯讀試算——不寫入、不計違約金、不拆分預借現金;
與POST /interest-calculations(正式試算,需冪等鍵)之差異即為「安全/唯讀」。
#3 ADB 輸入 —— 定奪如下
InterestCalcRequest 新增 6 個選填欄位,每一項皆對應規格或契約描述中
原本無從滿足之要求:
| 欄位 | 需求來源 |
|---|---|
dailyBalances |
SCS-006 §7「ADB 需傳入日終餘額序列」 |
calcMethod |
SCS-006 §6「利息試算(指定計算方法/區間)」 |
dayCountBasis |
範例數字不自洽(#4),須明定天數基礎 |
overduePeriods |
契約描述「計循環利息與違約金(至多三期)」 |
cashAdvanceAmount |
契約描述「明細分 GENERAL/CASH_ADVANCE」 |
priorCycleAdb |
TWO_CYCLE 需上期平均日餘額 |
productCode |
規則選擇條件(PRICING_RULES.PRODUCT_CODE) |
四種計息方法之定義(原契約未定義,此為定案):
| 方法 | 公式 | 備註 |
|---|---|---|
ADB |
Σ(日終餘額) × 日利率 | 預設 |
PREVIOUS_BALANCE |
期初餘額 × 日利率 × 天數 | |
TWO_CYCLE |
(本期 ADB + 上期 ADB) ÷ 2 × 日利率 × 天數 |
缺 priorCycleAdb 時回 CC-PLT-4003,不靜默代以他法 |
FLAT |
ADB × 年利率 ÷ 12 | 固定月息,不依天數攤提 |
利率權威定奪:以
ANNUAL_RATE(年利率) 為準,
日利率 =ANNUAL_RATE ÷ 基礎天數。
理由:RATE(日利率)與ANNUAL_RATE係兩個獨立欄位,若以RATE為準,
dayCountBasis參數將完全失去作用;且年利率方為對帳單/合約上
可被人類核對之數值。
#4 範例數字 —— 定奪如下
原範例 interestAmount: 850 算不出來(BEAM PMO:「真是天才,我還真不知問題出在哪個環節」)。
實算:
52345 × 0.185 × 30 ÷ 365 = 795.93… → 796
52345 × 0.185 × 30 ÷ 360 = 806.99… → 807
52345 × 0.185 × 30 ÷ 330 = 880.0 → 880 ← 皆非 850
根因判定:850 無任何合理天數基礎可對應,屬範例撰寫時的臆值,
非公式或欄位定義錯誤。故:
- 契約範例更正為 796(
ACT_365),並補principalAmount: 52345
(原範例為null,與 796 之結果自相矛盾)。 - 明定 天數採半開區間
[periodStart, periodEnd):
2026-08-16 → 2026-09-15= 30 天,與範例一致;
且與「日終餘額序列每日一筆」之筆數定義吻合。 -
dayCountBasis預設ACT_365。
7. 實作結果與實測證據(2026-09-19)
7.1 交付項目
| 檔案 | 說明 |
|---|---|
application/dto/InterestCalcRequest |
輸入 DTO(含 RequestBuilder)+ DailyBalance
|
application/dto/InterestCalcView |
輸出 DTO + Detail + CalcBasis/DayCountBasis
|
application/dto/FeeView |
費用檢視 + 中文顯示名對照 |
application/InterestCalculationService |
試算/查詢與計算核心(純函式) |
application/FeeService |
費用設定查詢 |
interfaces/rest/PricingController |
3 個 REST 端點 |
domain/port/InMemoryPricingRuleRepository(測試) |
埠之完整假實作(非 mock 回值) |
11-dev-pricing-seed.sql |
補 TIER 維度(一般消費/預借現金利率之區分) |
7.2 自動化測試
pricing-service 45 測試 0 失敗 0 跳過
(新增 InterestCalculationServiceTest 23、FeeServiceTest 5)。
7.3 實機端到端實測(真實 Oracle CC_PRC,非快取)
| # | 情境 | 實測結果 |
|---|---|---|
| ① |
POST interest-calculations(本金 52345,30 天) |
interestAmount=796、appliedRate=0.185、calcBasis=CONSTANT_PRINCIPAL、dayCountBasis=ACT_365 ✅ |
| ② | GET fees |
5 筆生效規則;停用+已過期之 PRC-INT-GENERAL-LEGACY 正確排除 ✅ |
| ③ | GET interest-inquiries |
796,與 POST 一致 ✅ |
| ④ | 違約金+預借現金拆分 | 利息 960(796+164)、lateFeeCount=3(由 5 期夾擠)、lateFeeAmount=90000、CASH_ADVANCE 用 0.2 ✅ |
7.4 錯誤路徑實測(驗證 CR-004 回歸守門)
| 情境 | 回應 |
|---|---|
缺 X-Channel
|
400 CC-PLT-4001 |
未知 calcMethod
|
400 CC-PLT-4003 |
TWO_CYCLE 缺 priorCycleAdb
|
400 CC-PLT-4003 |
| 非法 JSON | 400 CC-PLT-4002 |
| 不存在路徑 | 404 CC-PLT-4040 |
缺 accountId
|
400 CC-PLT-4001 |
未知 ruleType
|
400 CC-PLT-4003 |
7 項皆為 4xx,無一落入 500;服務日誌 ERROR 筆數=0 ✅
8. 施行後新揭露之限制(誠實揭露)
-
TIER欄位被用作計息子型(GENERAL/CASH_ADVANCE)
✅ 已解除(2026-09-22,C-31):PRICING_RULES已增設TX_TYPE欄位
(V2__pricing_rules_tx_type.sql;可為 null,僅INTEREST規則具值;
既有列依TIER回填),規則選取改以TX_TYPE篩選,TIER回歸本義(客層級距)。
單測 53 例 0 失敗(含 2 防回歸);真機 e2e:清空TX_TYPE→CC-PRC-1001、
還原 → 20% 恢復。詳見SCS-006 1.2 §7.5、SDD-001 1.7 §3.11。 -
冪等鍵目前無從實作:
POST interest-calculations為純函式、不寫入,
故契約要求之X-Idempotency-Key僅接收而無副作用可重放。 -
CC-PRC-1001(422「無適用利率」)被延伸用於「查無違約金規則」——
語意略有擴張(違約金非利率)。未新增錯誤碼以避免更動SDD-006 §3.4;
若日後認為應獨立成碼,另提 CR。 -
未套用
GRACE_PERIOD_DAYS:免息期之判定須有繳款狀態,本服務無該資料。 -
crossPeriodInterest恆為 0、accumulatedInterest= 本期新增利息——
本服務不保存計息狀態。 -
FeeView.currency恆為TWD:PRICING_RULES無幣別欄位。 -
FeeView.feeName由RULE_TYPE對照產生(無名稱欄位)。
9. 修訂記錄
| 版本 | 日期 | 說明 |
|---|---|---|
| 1.0 | 2026-09-19 | 提出,盤點 4 處缺口,待核定 |
| 1.1 | 2026-09-19 | BEAM PMO 核定(四項全數授權我方設計);新增 §6 核定結果與定奪、§7 實作結果與實測證據、§8 施行後新揭露之限制;SCS-006 升 1.1 |
| 1.2 | 2026-09-22 |
§8 #1 結案(C-31):PRICING_RULES.tx_type 增設(V2__pricing_rules_tx_type.sql),TIER 挪用解除;SCS-006 升 1.2、SDD-001 升 1.7 |