Project

General

Profile

CR #203 » CR-002_分期契約值域與DDL不一致.md

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

 

CR-002 分期服務契約值域與 DDL 不一致

項目 內容
文件編號 CR-002
版本 1.1
狀態 已核定並執行完成(2026-09-19,BEAM PMO 裁定;含 SDD-005 1.2 更正)
提出日期 2026-09-19
提出者 開發團隊(分期域 WebUI 端到端實測時發現)
影響文件 SDD-001 §3.9(ER/DDL)、SDD-005 §6(狀態機)、SDD-006 §3.4(錯誤碼)、SCS-009
影響服務 installment-service(8089)、WebUI /installments
優先級 高(P1)——契約列舉值全部不可用,照契約開發之呼叫端無法建立分期

1. 摘要

installment-service 之 OpenAPI 契約有 3 處值域與 ER/實體 DDL/實作不一致。
其中 installmentType 為阻斷性缺陷:契約列舉之 6 個值無一可用,
實測一律回 400 CC-INS-4000。亦即依契約開發之呼叫端 100% 無法建立分期。

已於 2026-09-19 經 BEAM PMO 裁定處理方向並執行修正(見 §4、§5)。


2. 現況與實測證據

以下皆為 2026-09-19 於本地 Oracle 26ai(FREEPDB1)+ 服務 8089 實測之結果,非推測。

2.1 實測:契約列舉值全部被拒

POST /api/v1/installments   (X-Channel: WEB、X-Idempotency-Key: 每次不同)

installmentType=SINGLE      → HTTP 400  CC-INS-4000  未知分期類型:SINGLE
installmentType=RECURRING   → HTTP 400  CC-INS-4000  未知分期類型:RECURRING
installmentType=QRCODE      → HTTP 400  CC-INS-4000  未知分期類型:QRCODE
installmentType=MERCHANT    → HTTP 201
installmentType=BANK        → HTTP 201

→ 契約所載之 6 個值全部不可用;可用值僅 DDL 之 4 值。

2.2 三方(四方)來源對照

不一致項 1:CreateInstallmentRequest.installmentType

來源 分類軸 值域
BRD-001 §5.6(需求源頭) 業務型態 單筆消費分期/約定分期/帳單分期
契約(修正前) 業務型態 SINGLE/RECURRING/STATEMENT/ACQUIRER/NCCC/QRCODE
SDD-001 §3.9 敘述 來源通路 商家分期(MERCHANT)/銀行分期(BANK)
SDD-001 §3.9 DDL + 實體 DDL + 實作列舉 來源通路 MERCHANT/BANK/REVOLVING/BILL

⚠️ 這不是單純的「值域寫錯」,而是兩個不同分類軸被塞進同一欄:
「單筆/約定/帳單」講的是業務型態(怎麼發起);
「特店/銀行/循環/帳單」講的是來源通路(誰提供)。
兩者在真實卡系統都需要,但目前只有一個欄位。

不一致項 2:InstallmentView.status

來源 值域
契約(修正前) PENDING/ACTIVE/SETTLED/EARLY_SETTLED/DEFAULTED/REJECTED/CANCELLED
SDD-005 §6 狀態機(8 條轉換) 同上 7 值
SDD-001 §3.9 DDL ACTIVE/COMPLETED/PRE_CLOSED/DEFAULTED/CANCELLED
實體 DDL(V1__baseline.sql) 同上 5 值
實作列舉 PlanStatus 同上 5 值

不一致項 3:InstallmentPeriod.status

來源 值域
契約(修正前) 未定義列舉,僅範例誤植為 PENDING
SDD-001 §3.9 DDL + 實體 DDL + 實作列舉 DUE/PAID/OVERDUE/WAIVED/REVERSED(預設 DUE)

3. 衝突性質分析

三項並非同一類問題,處理方式亦不同:

# 項目 性質 嚴重度
1 installmentType 建模缺陷——兩個正交分類軸共用一欄 阻斷(P1):契約值 100% 不可用
2 InstallmentView.status 值域不一致——狀態機值域無法落 ER/DDL 高:契約宣告不可達之狀態
3 InstallmentPeriod.status 契約缺漏——欄位未定義列舉且範例錯誤 中:契約未表達約束

3.1 為何第 2 項不以 SDD-005 §6 為權威

SDD-005 §6 之狀態機含 PENDING(申請)與 REJECTED(拒絕)二態,看似較完整,
但查證後不應作為持久狀態之權威:

  1. BRD 流程無此狀態:BRD-001 §5.6 之分期流程為
    分期申請 → 資格與額度檢核 → {否:拒絕-額度不足 / 是:總約簽署 → 首期入帳}。
    其中「拒絕」為同步交易拒絕,非先落 PENDING 再轉 REJECTED;
    流程中不存在「已申請、待核准」之持久狀態。
  2. ER/DDL 無此狀態:SDD-001 §3.9 之 INSTALLMENT_PLANS.status 無 PENDING/REJECTED,
    且 DEFAULT 'ACTIVE'——計畫一建立即為 ACTIVE。
  3. 實作無此狀態:InstallmentService.create 直接建立 ACTIVE,略過核准流程
    (此為既有已知事項,見 SCS-009 §10.5 #4)。
  4. view 只能回傳存得下的值:InstallmentView.status 是已持久狀態之檢視,
    宣告一個資料庫存不下的值,只會讓呼叫端寫出永不觸發的分支。

→ 綜合:4 個來源(BRD+ER/DDL+實體 DDL+實作)不支持 PENDING/REJECTED,
僅 SDD-005 §6 一家支持
。故以 ER/DDL 為權威。

名稱對照(語意相同、命名不同):
SETTLED ↔ COMPLETED(按期攤還完畢);EARLY_SETTLED ↔ PRE_CLOSED(提前結清)。


4. 決策(2026-09-19 BEAM PMO 裁定)

# 項目 裁定 理由
1 installmentType 契約先對齊 DDL(MERCHANT/BANK/REVOLVING/BILL) 契約是介面,介面必須可用;建模層面的雙軸拆分(見 §6)另案處理,不在本 CR 動 DDL
2 InstallmentView.status 以 ER/DDL 為權威,契約對齊 5 值 見 §3.1:4 源 vs 1 源,且 BRD 流程無持久化申請狀態
3 InstallmentPeriod.status 契約補列舉並修正範例 屬契約缺漏,無設計爭議

5. 已執行之修正

5.1 契約(經產生器,未手改 YAML)

修改 tools/gen_openapi.py 之 installment-service 區塊後重新產生
(不可手改 contracts/openapi/*.yaml,會被覆蓋):

# 修正
1 installmentType enum → ["MERCHANT","BANK","REVOLVING","BILL"],並加註說明原值域不可用
2 InstallmentView.status enum → ["ACTIVE","COMPLETED","PRE_CLOSED","DEFAULTED","CANCELLED"],並註明與 SDD-005 §6 之差異
3 InstallmentPeriod.status 新增 enum ["DUE","PAID","OVERDUE","WAIVED","REVERSED"]
4 服務描述由「單筆、約定、帳單、自行收單、NCCC、QR Code 分期」改為「特店分期、銀行分期、循環分期、帳單分期」(對齊實際值域)
5 修正範例:installmentType SINGLE→MERCHANT;期數 status PENDING→DUE;totalFee 6000→600(120000×0.005);feeAmount 500→50(600÷12)
6 補欄位說明:金額單位為最小貨幣單位(分);agreementSigned 未帶回 422 CC-INS-1002;remainingPrincipal 末期必為 0 之不變式

產生器驗證結果:17 服務 / 70 端點 / 0 錯誤(✅ OpenAPI 驗證:0 錯誤)。

5.2 文件

文件 修正
SCS-009 §10.1 更新為「契約已於 CR-002 對齊 DDL」;保留值域三方不一致之歷史敘述供追溯
SCS-009 §10.5 #15 產品維護端點說明不變;補註契約已對齊
SCS-009 §11 修訂記錄新增 1.2
frontend/src/api/installments.ts 移除「契約列舉不同」之過時註解(現已一致)

5.3 前端

前端於開發時即已依 DDL 值域實作(INSTALLMENT_TYPE_LABEL/PLAN_STATUS_LABEL/
PERIOD_STATUS_LABEL),本次無需改動程式邏輯,僅更新過時註解。


6. 未納入本次修正(另案追蹤)

6.1 ✅【已完成】分期類型雙軸拆分 → CR-008

2026-09-20 更新:本項所指「另提之 CR」即 CR-008(已由 BEAM PMO 核定為 v1.1,
並於同日完成程式/DDL/契約/種子/前端與真實 Oracle 端到端實測)。
處置與原建議略有差異——installment_type 改名為 channel_type
(而非「保留為來源通路」),理由見 CR-008 §7.1:
「欄名與語意不符」正是本 CR 阻斷性缺陷之成因,保留舊名等於保留成因。
詳細值域、NULL 政策與 ACQUIRER/NCCC/QRCODE 之歸屬見 CR-008。

installmentType 一欄同時被要求承載兩個正交維度:

維度 值域 來源
來源通路 MERCHANT/BANK/REVOLVING/BILL SDD-001 §3.9、實體 DDL
業務型態 單筆/約定/帳單/收單/NCCC/QR Code BRD-001 §5.6、原契約

建議:於 INSTALLMENT_PLANS 與 INSTALLMENT_PRODUCTS 新增
business_type VARCHAR2(16),值域對齊 BRD-001 §5.6;
installment_type 保留為來源通路。此為建模層變更,須另提 CR 並經 SA 核定。

✅ 已完成(CR-008,2026-09-20):business_type 已新增(允許 NULL,
語意為「當初未記錄」);通路軸改名為 channel_type 並擴充為 7 值。
SCS-009 §10.2 與 §10.7 #16 已由「限制揭露」改為「已解除」。

現行權宜(已解除):business_type 之資訊暫無欄位承載,報表/篩選若需此維度,
須自 original_transaction_id 或 product_code 間接推導。

⚠️ 殘留:business_type 允許 NULL,報表與篩選必須處理空值,
不得以 COALESCE 默認成某一型態(CR-008 §7 ③)。

6.2 ✅【已完成】SDD-005 §6 狀態機之更正

SDD-005 §6 為已核定文件,其值域與 ER/DDL 不一致(見 §2.2 項 2)。
本 CR 先使契約對齊 ER/DDL;SDD-005 §6 本身已於 2026-09-19 經 BEAM PMO 核定後更正,
SDD-005 升版為 1.2。

已完成之更正內容:

# 更正 說明
1 移除 PENDING(申請) DDL CHECK 無此值(DEFAULT 'ACTIVE'),無法持久化;BRD-001 §5.6 無持久化之申請狀態
2 移除 REJECTED(拒絕) 同上;BRD 之「拒絕-額度不足」為同步交易拒絕
3 SETTLED → COMPLETED 語意相同,以 DDL 為權威統一術語
4 EARLY_SETTLED → PRE_CLOSED 同上
5 轉換 #4 來源 PENDING → ACTIVE Saga 補償發生於首期入帳階段
6 轉換數 8 → 6 連動更正 §12.2 矩陣:分期 8/11 → 6/9;合計 82/105 → 80/103
7 新增通用規則 SM-08 「狀態值域以可持久化之 DDL CHECK 為權威」——與 1.1 版 §11 Saga RUNNING→STARTED 同一原則;並明訂「新增狀態須先改 ER/DDL 再改本文件」

保留事項(已寫入 SDD-005 1.2 §6):

  • COMPLETED 之產生者(每期扣款/攤還排程)尚未實作
  • ACTIVE → CANCELLED(Saga 補償)尚未實作
  • 若日後業務確需「申請—核准」流程,須另提 CR 於 SDD-001 §3.9 ER/DDL
    補入狀態並附遷移腳本,不得僅修改文件——否則將重現「狀態機有、資料庫存不下」之同一缺陷

連動更新:SDD-007 升版 1.1(§5.4.8 值域說明)、SCS-009 升版 1.3(§10.1 改為「已全部對齊」)、
契約 InstallmentView.status 描述、InstallmentView.java javadoc。

6.3 【提案】InstallmentView 欄位不足

INSTALLMENT_PLANS 有 principal_outstanding/paid_periods/remaining_periods/
currency/fee_rate/start_date/first_due_date 等欄位,
但 InstallmentView 未暴露,前端無法顯示「剩餘本金總額/已繳期數」。

建議:視 WebUI 需求於契約補欄位(屬功能增補,非不一致,優先級較低)。


7. 影響範圍與回改清單

# 項目 狀態
1 tools/gen_openapi.py 分期區塊 ✅ 已改
2 contracts/openapi/installment-service.yaml ✅ 已重生(產生器產物)
3 frontend/src/api/installments.ts 註解 ✅ 已更新
4 SCS-009 §10/§11 ✅ 已更新(1.3:§10.1 改為「值域已全部對齊」)
5 SDD-005 §6 更正與升版 ✅ 已完成(1.2):移除 PENDING/REJECTED、更名 SETTLED/EARLY_SETTLED、新增 SM-08、§12.2 連動更正
6 SDD-007 §5.4.8 值域說明 ✅ 已更新(1.1)
7 SDD-001 §3.9 新增 business_type(並將 installment_type 改名為 channel_type) ✅ 已完成(CR-008,2026-09-20):SDD-001 升 1.5、V3__installment_channel_business_type.sql 已遷移並經真實 Oracle 實測
8 installment_products 維護端點 ⏳ 契約未定義,維持揭露(SCS-009 §10.7 #15)

8. 殘留風險

# 風險 說明
1 SDD-005 §6 未更正 ✅ 已解除(1.2 完成)。值域四方(BRD/ER-DDL/狀態機/契約)已全部對齊;殘留僅「COMPLETED 無產生者」與「ACTIVE→CANCELLED 未實作」兩項,已於 SDD-005 1.2 §6 明列為保留事項
2 business_type 無欄位承載 ✅ 已解除(CR-008):欄位已新增並完成端到端實測。殘留風險:允許 NULL,報表與篩選必須處理空值,不得以 COALESCE 默認成某一型態(見 §6.1)
3 既有資料 修正僅涉及契約與文件,未動任何已落庫資料;DDL 未變更,故無資料轉換風險

9. 建議

  • 本 CR 之契約修正已完成並驗證,可立即解除呼叫端之阻斷
  • §6.2(SDD-005 §6 更正)建議於本 Sprint 內核定 → ✅ 已完成(SDD-005 1.2)
  • §6.1(雙軸拆分)建議於下一 Sprint 之建模檢視一併處理 → ✅ 已完成(CR-008,2026-09-20)。
    BRD §5.6 之業務型態維度已可儲存;殘留要求為「空值須被正確處理」

10. 修訂記錄

版本 日期 說明
1.0 2026-09-19 初版:分期域 WebUI 實測時發現契約列舉值 100% 不可用,提出 3 項不一致之分析與修正;契約修正已執行並通過產生器驗證
1.1 2026-09-19 BEAM PMO 核定 §6.2 後補完:SDD-005 升版 1.2(§6 移除 PENDING/REJECTED、更名 SETTLED/EARLY_SETTLED、新增 SM-08、§12.2 連動更正)、SDD-007 升版 1.1、SCS-009 升版 1.3(§10.1 改為「已全部對齊」);連動更新契約描述與 InstallmentView javadoc;§8 風險 #1 解除
    (1-1/1)