CR #203 » CR-002_分期契約值域與DDL不一致.md
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(拒絕)二態,看似較完整,
但查證後不應作為持久狀態之權威:
-
BRD 流程無此狀態:
BRD-001 §5.6之分期流程為
分期申請 → 資格與額度檢核 → {否:拒絕-額度不足 / 是:總約簽署 → 首期入帳}。
其中「拒絕」為同步交易拒絕,非先落PENDING再轉REJECTED;
流程中不存在「已申請、待核准」之持久狀態。 -
ER/DDL 無此狀態:
SDD-001 §3.9之INSTALLMENT_PLANS.status無PENDING/REJECTED,
且DEFAULT 'ACTIVE'——計畫一建立即為ACTIVE。 -
實作無此狀態:
InstallmentService.create直接建立ACTIVE,略過核准流程
(此為既有已知事項,見SCS-009 §10.5#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.9ER/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-0051.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 解除 |