Project

General

Profile

CR #209 » CR-008_分期通路與業務型態雙軸拆分.md

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

 

CR-008 分期通路與業務型態雙軸拆分(channel_type + business_type)

項目 內容
編號 CR-008
版本 1.1(2026-09-20 BEAM PMO 核定:A1 + NULL 允許 + 通路軸擴充為 7 值)
狀態 ✅ 已核定(CR-002 §6.1 明載:本項屬建模層變更,須另提 CR 並經核定)
提出日期 2026-09-20
核定日期 2026-09-20(BEAM PMO:①A1——DB 與 API 皆改名;②business_type 允許 NULL,報表須處理空值;③ACQUIRER/NCCC/QRCODE 納入 channel_type,通路軸擴充為 7 值)
提出者 WorkBuddy AI
影響文件 SDD-001 §3.9、SCS-009(§4/§6/§9/§10 #16)、SDD-007 §5.4.x(分期 WebUI)、contracts/openapi/installment-service.yaml(由 tools/gen_openapi.py 產生)、CR-002 §6.1
影響程式 installment-service:InstallmentType(→ChannelType)、新增 BusinessType、InstallmentPlan、JdbcInstallmentRepository、InstallmentService、CreateInstallmentRequest;種子 ops/local/init/oracle/10-dev-installment-seed.sql;前端 frontend/src/api/installments.ts、pages/InstallmentConsole.tsx
關聯 CR-002 §6.1(本 CR 即其所指「另提之 CR」)、HANDOVER §3.C #C-15
嚴重度 🟨 P2(建模缺陷)——非阻斷(通路軸已可用),但業務型態無處儲存,報表維度受限

1. 背景

CR-002 修正了 installmentType 之阻斷性缺陷(契約 6 值實測全數回 400),
處置為「契約先對齊 DDL」——使介面可用。但該欄的建模問題並未解決:

CR-002 §2.2:「這不是單純的『值域寫錯』,而是兩個不同分類軸被塞進同一欄」。
「單筆/約定/帳單」講的是業務型態(怎麼發起);
「特店/銀行/循環/帳單」講的是來源通路(誰提供)。

CR-002 §6.1 並已載明處置方向與其限制:

現行權宜:business_type 之資訊暫無欄位承載,報表/篩選若需此維度,
須自 original_transaction_id 或 product_code 間接推導。
此限制應於 SCS-009 §10 明確揭露,不得默認已支援。

BEAM PMO 2026-09-20 裁定(#C-15):一定要拆成兩個欄位(channel_type + business_type)。


2. 現況(2026-09-20 實測盤點,非推測)

2.1 business_type 在程式與 DDL 中完全不存在

全庫搜尋 business_type/businessType 僅命中文件(CR-002/HANDOVER/SCS-009 §10 #16),
程式、DDL、契約、前端皆無此欄位——證實「無處儲存」確為事實,非搜尋疏漏。

2.2 現有唯一一欄 installment_type

-- V1__baseline.sql(installment_products 與 installment_plans 皆同)
installment_type   VARCHAR2(16 CHAR) NOT NULL
                   CHECK (installment_type IN ('MERCHANT','BANK','REVOLVING','BILL')),
來源 分類軸 值域 權威性
BRD-001 §5.6(需求源頭) 業務型態 單筆消費分期/約定分期/帳單分期 ★ 需求源頭
原契約(CR-002 修正前) 業務型態 SINGLE/RECURRING/STATEMENT/ACQUIRER/NCCC/QRCODE ⚠️ 後 3 值語意見 §4.2
SDD-001 §3.9 敘述 來源通路 商家分期(MERCHANT)/銀行分期(BANK) 部分
SDD-001 §3.9 DDL + 實體 DDL + 實作列舉 來源通路 MERCHANT/BANK/REVOLVING/BILL ★ 硬約束(CHECK)

3. 衝突性質

# 性質 說明
1 兩正交軸共用一欄 業務型態(怎麼發起)與來源通路(誰提供)為正交維度,真實卡系統兩者都需要
2 其中一軸完全無處儲存 業務型態(business_type)目前無任何欄位承載
3 欄名未反映其軸 承載「通路」的欄位卻叫 installment_type(=「分期類型」),易被誤讀為業務型態——這正是 CR-002 契約值域錯誤之根源

4. 設計

4.1 雙軸定義(BEAM PMO 裁定:兩個欄位)

欄位 軸 說明 值域 權威來源
channel_type 來源通路(誰提供) 由 installment_type 改名而來,值域擴充為 7 值(既有 4 值 + 原契約之 ACQUIRER/NCCC/QRCODE) MERCHANT/BANK/REVOLVING/BILL/ACQUIRER/NCCC/QRCODE BRD-001 §5.6 + 原契約 + 既有 DDL(見 §4.2 裁定)
business_type 業務型態(怎麼發起) 新增,允許 NULL SINGLE/RECURRING/STATEMENT BRD-001 §5.6(需求源頭)

⚠️ channel_type 擴充為 7 值屬放寬既有 CHECK 約束(4 → 7)——
既有資料之 4 種值仍然合法,故不會因放寬而擋住任何既有資料。

適用表:installment_products、installment_plans(兩表皆需,前者為產品定義、後者為實際案件)。
installment_periods 不需——攤還期隸屬於計畫,維度由父層繼承。

約束:

  • channel_type:NOT NULL + CHECK 同既有值域(沿用,不改)
  • business_type:NOT NULL + CHECK (business_type IN ('SINGLE','RECURRING','STATEMENT'))
    • ⚠️ 是否允許 NULL 待定,見 §7 待裁定項 ③

4.2 ⚠️ ACQUIRER/NCCC/QRCODE 之歸屬(待裁定)

原契約 6 值中,前 3 值(SINGLE/RECURRING/STATEMENT)明確對應 BRD §5.6 之業務型態;
後 3 值語意偏「通路/交易來源」,與 channel_type 既有 4 值之關係無法由現有文件判定:

值 字面語意 疑問
ACQUIRER 收單(分期) 屬「誰提供」→ 應歸 channel_type?但既有 4 值無此項
NCCC 財金公司(跨行) 同上
QRCODE QR Code 掃碼分期 偏「交易管道」→ channel_type?或視為一種業務型態?

BEAM PMO 2026-09-20 裁定:三者納入 channel_type(「誰提供」之語意),
使通路軸由 4 值擴充為 7 值:

MERCHANT(特店)/BANK(銀行)/REVOLVING(循環)/BILL(帳單)
ACQUIRER(收單)/NCCC(財金)/QRCODE(QR Code)

business_type 維持 BRD §5.6 之 3 值(SINGLE/RECURRING/STATEMENT)——
單筆/約定/帳單,語意為「怎麼發起」,與通路軸正交。

⚠️ 誠實揭露:此歸類係依字面語意判定,非來自客戶確認之規格
(這 3 值既不見於 BRD-001 §5.6,亦不見於既有 DDL)。
值域一旦寫入即難以回頭,故仍建議於客戶規格確認時覆核;
本期以 BEAM PMO 裁定為準並記載於此,日後可追溯。


5. 影響範圍與執行順序

依「SPEC 先行 → DDL → 程式 → 契約 → 前端 → 文件」:

步驟 項目 說明
0 本 CR 核定 前置(CR-002 §6.1 要求)
1 SDD-001 §3.9 升版 權威模型先改,否則下次重產基線會再掉一次(A-42 之教訓)
2 新增 V3__installment_channel_business_type.sql ⚠️ 不改 V1 基線(SAD-003 §9;V1 已套用,改檔即 checksum 失配)
3 程式:InstallmentType → ChannelType、新增 BusinessType 領域列舉、聚合、Repository(含 SQL 欄位)、Service、DTO
4 契約:改 tools/gen_openapi.py 後 --write ⚠️ 禁手改 yaml(產生器會覆寫)
5 種子 10-dev-installment-seed.sql 補 business_type 值
6 前端 installments.ts/InstallmentConsole.tsx 標籤對照與表單
7 SCS-009/SDD-007 §5.4.x 升版、回寫 CR-002 §6.1 解除 §10 #16 之限制揭露

5.1 遷移腳本須注意(Oracle 實測事項)

  • ALTER TABLE … RENAME COLUMN installment_type TO channel_type:
    ⚠️ 須實測 CHECK 約束之搜尋條件是否隨改名自動更新;
    若不更新,須先刪約束再改名再重建(V1 已套用,不可改)。
  • 新增 business_type 時,既有資料必須回填(不可留 NULL 而無預設):
    建議先加可為 NULL 之欄 → 回填 → 再加 NOT NULL 與 CHECK;
    或直接 DEFAULT 'SINGLE'(⚠️ 默認值需業務確認,見 §7 ③)。
  • 遷移後必須真實 Oracle 實測(項目紀律:DDL 變更不得僅以單測驗證)。

6. 回溯相容性

面向 影響
DB installment_type → channel_type 為改名,既有查詢/視圖若引用舊名會失效
API 契約 ⚠️ 若同步將 installmentType 改名為 channelType,為 breaking change——既有呼叫端全數失效
程式 InstallmentType enum 改名,所有引用點須同步(已盤點,見表頭「影響程式」)

7. 裁定結果(BEAM PMO 2026-09-20)

# 項目 裁定 影響
① 是否改名 installment_type → channel_type ✅ A1:改名(DB 與 API 皆改) 徹底消除 CR-002 值域錯誤之根源(開發者依欄名字面填了業務型態值,導致契約值 100% 被拒)。API 為 breaking change,本期仍在開發階段,代價低於上線後
② API 契約是否同步改名 ✅ 同步改名 installmentType → channelType,並新增 businessType
③ business_type 是否允許 NULL ✅ 允許 NULL 既有資料(含種子、已建立之分期計畫)當初未記錄此維度;誠實表達「未記錄」勝過編一個值。報表與篩選須處理空值
④ ACQUIRER/NCCC/QRCODE 歸屬 ✅ 納入 channel_type 通路軸擴充為 7 值(放寬 CHECK,既有 4 值仍合法)。歸類依字面語意,非客戶確認規格——見 §4.2 之揭露

7.1 為何採 A1 而非 B(保留舊名)

CR-002 之阻斷性缺陷正是「欄名與語意不符」所導致:
開發者看到 installmentType(字面=「分期類型」)便填了業務型態值
(SINGLE/RECURRING/QRCODE),結果 100% 被 DDL 的通路值域拒絕。
保留舊名等於保留缺陷成因——改名是治本,加欄只是治標。


8. 執行結果(2026-09-20 完成)

依 §5 之順序全數執行完畢,並以真實 Oracle 端到端實測(非僅單測推論)。

步驟 項目 結果
1 SDD-001 §3.9 升版 ✅ 1.5(ER 圖與 DDL 兩表同步為 channel_type + business_type)
2 V3__installment_channel_business_type.sql ✅ Flyway 於 8089 啟動時套用成功(now at version v3);CHANNEL_TYPE NOT NULL、BUSINESS_TYPE 可 NULL;四個具名 CHECK 成立;舊值域約束已卸除
3 程式 ✅ InstallmentType→ChannelType(7 值)+ 新增 BusinessType(3 值);InstallmentPlan/JdbcInstallmentRepository/InstallmentService/DTO 同步
4 契約(gen_openapi.py → --write) ✅ 重產 17 服務/72 端點/0 錯誤;installmentType→channelType(7 值)、新增 businessType(nullable);⚠️ 未手改 yaml
5 種子 ✅ 補雙軸值;刻意保留 0003 為 NULL 以驗證空值路徑
6 前端 ✅ installments.ts/InstallmentConsole.tsx 同步;空值顯示「未記錄」;npm run typecheck 0 錯誤
7 SCS-009/SDD-007/回寫 CR-002 §6.1 ✅ SCS-009 1.4、SDD-007 1.5、CR-002 §6.1 標為已完成

8.1 單測與判定力

  • installment-service 單測 22 → 33 例,0 失敗
  • 新增 ChannelTypeBusinessTypeTest(8 例):通路軸 7 值、業務型態軸 3 值、
    業務型態值不得被通路軸接受(CR-002 根因回歸)、fromCodeOrNull 之 NULL/未知值分流
  • 負樣本實測:刻意把 SINGLE 加回 ChannelType、並讓 fromCodeOrNull 對未知值
    靜默回 null → 精準轉紅 3 例,其餘 30 例不受影響,證明測試有判定力

8.2 真實 Oracle 端到端實測(SCS-009 §9.3 #14–18)

# 情境 實測
14 V3 遷移 ✅ Flyway 套用成功,四個具名約束成立
15 MERCHANT + STATEMENT 建立 ✅ 201,INST2026090006 落庫 MERCHANT/STATEMENT,12 期
16 省略 businessType(QRCODE) ✅ 201,INST2026090007 落庫 QRCODE/NULL,6 期
17 查詢種子 0003(未記錄) ✅ channelType=BANK、businessType=**null**(非空字串、非預設值)
18 channelType=SINGLE(誤填業務型態值) ✅ 400 CC-INS-4000「未知分期來源通路:SINGLE」——CR-002 根因仍被正確擋下

8.3 執行補充:InstallmentView 新增 2 欄

InstallmentView 原未回傳分期分類(連舊的 installmentType 都沒有)。
為使呼叫端能讀回雙軸、並讓上述端到端實測可驗證「落庫值」而非僅查 DB,
本 CR 執行時加值新增 channelType 與 businessType 兩欄:

  • 性質:純加值(additive),既有欄位不變、不改語意,非 breaking change
  • businessType 於回應中可為 null——與 DDL 一致,語意為「當初未記錄」
  • 此項屬實作細節,不影響 §7 之四項裁定,故未另提 CR;記載於此以維持文件與實物一致

9. 修訂記錄

版本 日期 修訂者 說明
1.0 2026-09-20 WorkBuddy AI 初版。依 CR-002 §6.1 與 BEAM PMO 2026-09-20 裁定(#C-15:拆分為 channel_type + business_type)提出;現況經全庫實測盤點(business_type 確認不存在於程式/DDL/契約)
1.1 2026-09-20 BEAM PMO 核定 裁定三項:①A1——DB 與 API 皆改名(installment_type→channel_type、installmentType→channelType)②business_type 允許 NULL,報表須處理空值 ③ACQUIRER/NCCC/QRCODE 納入 channel_type,通路軸擴充為 7 值。§4.1/§4.2/§7 隨之更新
    (1-1/1)