# CR-003　狀態機值域與 DDL 不一致（§5／§7／§9／§10）

| 項目 | 內容 |
|---|---|
| 文件編號 | CR-003 |
| 版本 | 1.0 |
| 狀態 | **已核定並執行完成**（2026-09-19，BEAM PMO 裁定） |
| 提出日期 | 2026-09-19 |
| 提出者 | 開發團隊（`SDD-005` 1.2 版 §12.3 值域稽核時發現） |
| 影響文件 | SDD-001 §3.7／§3.8／§3.10、SDD-005 §1／§5／§7／§9／§10／§12／§13、SDD-007、SCS-008／010／011／012 |
| 影響服務 | `payment-service`（8088）、`dispute-service`、`statement-service`（8092）、`rewards-service`（8091） |
| 優先級 | **高（P1）**——`SDD-005 §1 SM-08` 明訂值域以 DDL 為權威；不一致將導致**寫入即 `ORA-02290`** 或**文件與實物永久脫節** |

---

## 1. 摘要

`SDD-005` 1.2 版之 **§12.3 值域稽核**（以 `all_cons_columns` ＋
`all_constraints.search_condition_vc` **實測** 10 組狀態機對應之 DDL CHECK 約束）
發現 **4 組不一致**（稽核當時：6 組對齊、4 組待處理）。

本 CR 逐項分析性質、提請核定，並於核定後執行修正。**4 項均已於 2026-09-19 完成**，
`SDD-005` 升版至 **1.3**，稽核結果由「6 對齊／4 待處理」轉為 **「14 組全數對齊」**。

---

## 2. 四項不一致與性質分析

### 2.1 A｜§5 繳款：`UNMATCHED` 不可持久化

| 本文件（1.2 版） | 實體 DDL（實測） |
|---|---|
| `RECEIVED`／`ALLOCATED`／`POSTED`／`REVERSED`／`FAILED`／**`UNMATCHED`** | `status IN ('RECEIVED','ALLOCATED','POSTED','REVERSED','FAILED')`——**無 `UNMATCHED`** |

**性質**：**值域不足**（非混用維度）。`UNMATCHED` 於實體中以
`PAYMENTS.UNMATCHED_FLAG` **旗標**表達，與 `status` 為正交維度。

**二途**：①移除該狀態、改以旗標語意；②**於 DDL 增列該值**。

**關鍵事實**：本節之 `UNMATCHED` 為**作業狀態**——轉換 6「`ALLOCATED` 逾三日未收銷帳檔
→ `UNMATCHED`（**重新佔額**，POC-11 情境三）」、轉換 7「收到銷帳檔 → `POSTED`」、
轉換 8「確認未入帳 → `REVERSED`」。此語意**與旗標不同**，不應為遷就 DDL 而捨棄。

### 2.2 B｜§7 爭議：值域幾乎完全不同（**最嚴重**）

| 本文件（1.2 版） | 實體 DDL（實測） |
|---|---|
| `OPENED`／`EVIDENCE_COLLECTING`／`UNDER_REVIEW`／`RESOLVED_ISSUER`／`RESOLVED_ACQUIRER`／`ARBITRATION`／`CLOSED` | `status IN ('OPEN','EVIDENCE_PENDING','REPRESENTED','WON','LOST','CLOSED')` |

**僅 `CLOSED` 一值交集。**

**性質**：**不同分類軸被繪入單一狀態機**——本文件採「**處理階段 + 責任歸屬**」，
DDL 採「**階段 + 勝敗結果**」。與 `CR-002` 之分期類型問題**性質完全相同**。

**關鍵事實**：實體 DDL **已有三個欄位**分別承載三個維度（實測值域）：

| 欄位 | 值域 | 維度 |
|---|---|---|
| `DISPUTE_CASES.stage` | `INQUIRY`／`FIRST_PRESENTMENT`／`SECOND_PRESENTMENT`／`PRE_ARB`／`ARBITRATION`／`CLOSED` | 卡組織流程階段 |
| `DISPUTE_CASES.status` | `OPEN`／`EVIDENCE_PENDING`／`REPRESENTED`／`WON`／`LOST`／`CLOSED` | 案件處理與結果 |
| `DISPUTE_CASES.liability` | `ISSUER`／`ACQUIRER`／`MERCHANT`／`PENDING` | 責任歸屬（**分類屬性，非生命週期**） |

亦即**實體模型正確、文件繪錯**——`dispute-service` 目前僅有骨架（無領域模型），
故無實作債。`SCS-012 §10` 亦已載明「`DISPUTE_CASES.status`／`STAGE` 轉換**強烈建議**
建模為 `SDD-005` 正式狀態機」。

### 2.3 C｜§9 帳單：混用兩個實體且有缺值

| 本文件（1.2 版） | 實體 DDL（實測） |
|---|---|
| `GENERATING`／`GENERATED`／`FAILED`／`SENT`／`PARTIALLY_PAID`／`PAID`／`OVERDUE` | `STATEMENTS.status`：`GENERATED`／`SENT`／`PAID`／`PARTIALLY_PAID`／`OVERDUE`／**`DISPUTED`**／**`CLOSED`**<br>`STATEMENT_RUNS.status`：`GENERATING`／`GENERATED`／`FAILED` |

**性質**：**兩個實體被繪入單一狀態機**——`GENERATING`／`FAILED` 屬**批次執行**
（`statement_runs`，於 V2 遷移補建），其餘屬**帳單本體**（`statements`）。
另 DDL 之 `DISPUTED`／`CLOSED` **未出現**於本節（**缺值**）。

**關鍵事實**：`STATEMENT_RUNS` 係因契約要求「批次狀態與 Checkpoint」而於
`V2__statement_runs_and_no_seq.sql` 補建，`SDD-001 §3.10` 之 ER/DDL 為 V1 基線快照，
**未含此表**（文件債，一併於本次加註交付說明）。

### 2.4 D｜§10 點數：混用三個維度

| 本文件（1.2 版） | 實體 DDL（實測） |
|---|---|
| `CUR`／`AV1`／`AV2`／`CLOSED` | `POINTS_EXPIRY_BUCKETS.BUCKET_STAGE`：`CUR`／`AV1`／`AV2`（**桶階段**）<br>`POINTS_EXPIRY_BUCKETS.STATUS`：`ACTIVE`／`EXPIRED`／`REDEEMED`（**桶狀態**）<br>`POINTS_ACCOUNTS.STATUS`：`ACTIVE`／`FROZEN`／`CLOSED`（**帳戶狀態**） |

**性質**：**三個維度被繪入單一狀態機**。`CUR`／`AV1`／`AV2` 為**桶階段**、
`CLOSED` 為**帳戶**狀態。

**關鍵事實**：
- 實作（`rewards-service`）**已正確對應至不同欄位**，故為**文件債，非程式債**。
- `SCS-010 §10.2` 已揭露 **2 條不可達轉換**：轉換 3（`CUR` 到期且餘額 **< 0**——
  受 `BALANCE >= 0` 之 CHECK 所限）、轉換 5（`AV1` 到期且餘額 **= 0**——
  受桶 `POINTS > 0` 之 CHECK 所限）。
- `SCS-010 §10.6` 列有**規格空白**：「`FROZEN` 帳戶之入點行為待 CR 確認」，
  其待辦 ① 即為「於 `SDD-005` 新增**帳戶營運狀態機**」。

---

## 3. 決策（2026-09-19 BEAM PMO 裁定）

| 項 | 問題 | **裁定** |
|---|---|---|
| A §5 | 移除 `UNMATCHED`，或 DDL 增列？ | **DDL 增列** |
| B §7 | 擇一（統一用勝敗結果），或拆兩欄？ | **拆兩個**（階段／結果） |
| C §9 | 是否同意拆分狀態機？ | **拆分狀態機** |
| D §10 | 是否同意拆分狀態機？ | **拆分狀態機** |

---

## 4. 已執行之修正

### 4.1 A｜§5 繳款（DDL 增列）

| # | 動作 | 產出 |
|---|---|---|
| 1 | 設計權威 `SDD-001 §3.7` 之 `status` CHECK **增列 `UNMATCHED`** | 文件 |
| 2 | 新增遷移 `V4__payments_unmatched_status.sql`（V1 已凍結，不得修改） | 實體 |
| 3 | `PaymentStatus` 增列 `UNMATCHED`，並釐清與 `UNMATCHED_FLAG` 之正交關係 | 程式 |
| 4 | 契約 `PaymentView.status` 枚舉增列 `UNMATCHED`（改 `gen_openapi.py` 後重生） | 契約 |
| 5 | `SDD-005 §5` 補值域說明、正交關係表與**保留事項** | 文件 |

**實測（本地 Oracle 26ai `FREEPDB1`，非推測）**：

```
寫入 status='UNMATCHED'  → 成功，SELECT 回 UNMATCHED
寫入 status='BOGUS_VALUE' → ORA-02290: check constraint (CC_PAYMENT.CK_PAYMENTS_STATUS) violated
```

約束名由自動生成之 `SYS_C0010868` 重建為**具名** `CK_PAYMENTS_STATUS`（V4 以資料字典
動態尋得，不依賴自動名稱），使後續值域變更得以穩定引用。**V4 冪等**，可重跑。

**⚠️ 保留事項**：
- `UNMATCHED` 之**產生者尚未實作**（「逾三日未收銷帳檔」之批次不存在），
  故尚無程式路徑可達；惟**持久化能力已具備**（本次更正之目的）。
- 轉換 8（`UNMATCHED` → `REVERSED`）**尚未實作**：`PaymentStatus.reversible()`
  目前僅放行 `POSTED`。日後實作該批次時**須一併放行**。

### 4.2 B｜§7 爭議（拆分為兩個狀態機）

`SDD-005 §7` 改寫為：

| 小節 | 狀態機 | 對應欄位 | 轉換數 |
|---|---|---|---:|
| §7.1 | 爭議階段機 | `stage` | 10 |
| §7.2 | 爭議案件狀態機 | `status` | 11 |
| §7.3 | `liability`——**正交屬性**，非狀態機 | `liability` | — |

**DDL 無須變更**（三欄皆已存在）。轉換依 `BRD-001 BF-09`（C1～E3）逐節對應。

### 4.3 C｜§9 帳單（拆分為兩個狀態機）

| 小節 | 狀態機 | 對應欄位 | 轉換數 |
|---|---|---|---:|
| §9.1 | 帳單本體 | `statements.status`（7 值，**補入 `DISPUTED`／`CLOSED`**） | 14 |
| §9.2 | 批次執行 | `statement_runs.status`（3 值） | 4 |

轉換依 `BRD-001 BF-05`（A1～C4）逐節對應。
另於 `SDD-001 §3.10` 加註 **V2 交付說明**（`statement_runs`／`statement_cycle_accounts`／
`statement_no_seq` 未列於 V1 基線快照之原因與值域）。

### 4.4 D｜§10 點數（依維度拆分為三個狀態機）

| 小節 | 狀態機 | 對應欄位 | 轉換數 |
|---|---|---|---:|
| §10.1 | 桶階段流轉 | `bucket_stage` | 5 |
| §10.2 | 桶狀態機 | `points_expiry_buckets.status` | 5 |
| §10.3 | 帳戶營運狀態機 | `points_accounts.status` | 6 |

**一併處置二項既有債**：
1. **移除不可達轉換**——原轉換 3「`CUR` 到期且餘額 < 0 → `CUR`」，
   因 `BALANCE >= 0` 之 CHECK 使負餘額不可能存在（實測），已刪除。
   （`SCS-010 §10.2` 原已揭露，本次一併更正。）
2. **封存 `FROZEN` 入點決策**——`§10.3` 新增「各狀態之操作權限」表
   （`ACTIVE` 可入點可兌換；`FROZEN` 可入點、不可兌換 `422 CC-RWD-1003`；
   `CLOSED` 皆不可 `409 CC-RWD-3002`），**封存現行實作**（`SCS-010 §10.6` 之規格空白）。
   此即該節待辦之第 ① 項。

另於 `SDD-001 §3.8` 加註 **V2 交付說明**（`reward_rules` 表、
`bucket_stage` 欄、`idempotency_key` 欄、`redemption_no_seq`）。

### 4.5 連動更正

| 項 | 內容 |
|---|---|
| `SDD-005 §1` | 新增通用規則 **SM-09**「一組狀態機只表達一個欄位」——由本次 4 項更正歸納而得 |
| `SDD-005 §12.2` | 組數 10 → **14**；合計 80／103 → **108／139** |
| `SDD-005 §12.3` | 改為 **14 組全數對齊**，並載明 A～D 之更正歷程 |
| `SDD-005 §13` | 新增 1.3 修訂記錄 |
| `SDD-005` 檔頭 | 版本 1.2 → **1.3** |

**轉換數重算**：

| 狀態機 | 1.2 版 | 1.3 版 |
|---|---:|---:|
| 爭議 | 9 | 10 + 11 = **21** |
| 帳單 | 11 | 14 + 4 = **18** |
| 點數 | 7 | 5 + 5 + 6 = **16** |
| **合計** | **80** | **108** |
| **必測案例數** | **103** | **139** |

---

## 5. 驗證結果

| 項目 | 結果 |
|---|---|
| `V4` 遷移套用 | PL/SQL 成功；約束重建為 `CK_PAYMENTS_STATUS` |
| 值域正向實測 | `UNMATCHED` 可寫入並持久化 ✅ |
| 值域負向實測 | 非法值觸發 `ORA-02290`（證明約束生效，非僅名稱存在）✅ |
| `gen_openapi.py` 重生 | 17 服務 / 70 端點 / **0 錯誤** |
| `validate_mermaid.py docs` | 115 區塊 / **0 錯誤**（1.2 版為 111；+4 為新拆分之狀態圖） |
| 狀態機組數 | 10 → **14**；值域稽核 **14/14 對齊** |

---

## 6. 未納入本次修正（另案追蹤）

| # | 項目 | 說明 |
|---|---|---|
| 1 | `UNMATCHED` 之產生者（逾三日未收銷帳檔之批次） | 未實作；`SDD-005 §5` 已列保留事項 |
| 2 | `PaymentStatus.reversible()` 未放行 `UNMATCHED` | 同上；實作該批次時須一併處理 |
| 3 | `SDD-001 §3` 之 V2+ 遷移落差（跨服務） | ⚠️ **本列原記載不實，2026-09-21 更正**：原稱「本次僅加註 §3.7／§3.8／§3.10 三處」並舉 `reversal_reason` 為例——實測 **`reversal_reason` 於 `SDD-001` 全文出現 0 次**，§3.7 **並未**回寫（§3.8／§3.10 確已完成）。<br>→ ✅ **已於 `HANDOVER #C-18` 全數補齊**（`SDD-001` 升 **1.5**）：以腳本比對 V2/V3 遷移與文件，補上 8 項——§3.1 `pty_idempotency`、§3.2 `card_idempotency`、§3.4 `account_limits`／`limit_adjustments`、§3.5 `card_status_caches`／`account_limit_caches`／`authorizations.hold_id`、**§3.7 `reversal_reason`（ER 圖與 DDL 皆同步）**。詳 `SDD-001 §10`（1.5） |
| 4 | §7.3 `WON`／`LOST` 與 `liability` 之精確對應 | 須依卡組織規則確認；已列入 `SCS-012 §10` |
| 5 | 平台層 500-vs-400 缺口 | **另立 `CR-004`** |

---

## 7. 殘留風險

| # | 風險 | 等級 | 對策 |
|---|---|---|---|
| 1 | 重跑 `gen_flyway_baseline.py --force` 會覆寫 V1，使 Flyway checksum 失配 | 中 | `SDD-001 §3.7` 與 `V4` 檔頭均已載明；若確需重建須 `flyway repair` 或 `docker compose down -v` |
| 2 | 契約與 DDL 現已一致，但 `V4` 尚未於其他環境套用 | 中 | 服務啟動時 Flyway 會自動套用；`V4` 冪等 |
| 3 | `SDD-001 §3` 之 V2+ 落差可能再次造成「文件與實物脫節」 | 中 | 建議後續 CR 建立「遷移檔新增欄位／表 → 回寫 `SDD-001 §3`」之強制步驟 |

---

## 8. 修訂記錄

| 版本 | 日期 | 修訂者 | 說明 |
|---|---|---|---|
| 1.1 | 2026-09-21 | WorkBuddy AI | **更正 §6 #3 之不實記載**：原稱「本次僅加註 §3.7／§3.8／§3.10 三處」，實測 `SDD-001` 全文不含 `reversal_reason`（出現 0 次）→ §3.7 **並未**回寫。該落差已於 `HANDOVER #C-18` 全數補齊（`SDD-001` 升 **1.5**，共 8 項），本列同步更正，以免後人據「已完成」之記載而再度略過。⚠️ 盤點方法亦已修正：不得以「指定章節是否含該名稱」判缺（`lim.limit_holds` 記載於 §3.4 而非 §3.5，按 §3.5 查會誤判） |
| 1.0 | 2026-09-19 | 開發團隊 | 初版。依 BEAM PMO 裁定執行 §5／§7／§9／§10 四項值域更正，`SDD-005` 升版 1.3 |
