CR #209 » CR-008_分期通路與業務型態雙軸拆分.md
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 隨之更新 |