CR #201 » SDD-005_狀態機設計.md
SDD-005 狀態機設計
| 項目 | 內容 |
|---|---|
| 文件編號 | SDD-005 |
| 版本 |
1.9(CR-022 裁定後:新增 §3.1 授權決策狀態機——RFP FR 1.1(授權/發卡授權)第 4 項要求「支援人工授權、強制授權、人工取消授權」(廠商已回覆「滿足」);其中人工取消=沖正(§3 轉換 5)已完成,本節補 REFERRAL(轉人工)之中間狀態與其核決轉換(SM-D1~D5),為 AuthDecision.REFERRAL 之唯一產生來源)。前版 1.8(CR-020 F4:新增 §2.4「block_code 與 status 之衍生關係」——status 由 block_code 單向推導(完整 7 事件映射表),由狀態機統一維護,消除「生命週期轉換僅改 block_code、快發快綁兩欄皆改」之不一致;併載明與 SM-09 之關係及未來若需獨立時之處理方式) |
| 狀態 | 已核定 |
| 撰寫者 | SD |
| 審查者 | SA / BA / QA |
| 對應需求 | ISS-2.2、ISS-2.3、ISS-2.6、ISS-2.7、ISS-2.8;BRD-001 BF-05、BF-09、BF-10、BF-12 |
本版差異(1.3):依
CR-003處置 1.2 版 §12.3 值域稽核所列之 4 項不一致
(A~D),使全部狀態機之值域與可持久化之 DDL CHECK 一致(14 組全數對齊):
①**§5 繳款**——UNMATCHED改採 DDL 增列途徑(SDD-001 §3.7+
V4__payments_unmatched_status.sql),並釐清其與UNMATCHED_FLAG為正交維度;
②**§7 爭議**——由單一狀態機拆分為兩個(階段/案件),liability明列為正交屬性;
③**§9 帳單**——由單一狀態機拆分為兩個(帳單本體/批次執行),並補DISPUTED/CLOSED;
④**§10 點數**——由單一狀態機依維度拆分為三個(桶階段/桶狀態/帳戶),
一併移除不可達轉換、封存FROZEN帳戶之入點決策。
連動更正 §13.2 全轉換矩陣(組數 10 → 14,合計 80/103 → 108/139)與
§13.3(改為全數對齊並載明更正歷程)。詳見 §13。⚠️ 章節編號遞移提示(引用本文件時必看):上段之
§12.2/§12.3為 1.3 版當時之編號
(指全轉換矩陣/值域稽核),其後歷經兩次遞移——1.4 版(新增 §12 申請案件)
→§13.2/§13.3;1.7 版(新增 §13 通知)→§14.2/§14.3。
同理「狀態機實作規範」:1.3 版為§12→ 1.4 起§13→ 1.7 起 §14;
「變更記錄」→ §15。⚠️ 但現行
§12是「卡片申請案件狀態機」(1.4 版新增),其§12.1/§12.2/§12.3
分別為值域對應/轉換表/非法轉換——與上段之舊編號無關。
→ 引用時一律看「語意」而非「號碼」:引「值域稽核」=§14.3;引「申請案件轉換表」=§12.2。已同步更正之現行文件:
CSP-007(§13→§14)、SDD-006(同上)、SCS-024(§13.2/§13.3→§14.2/§14.3)。
歷史紀錄保留原編號不動:CR-003(§12.2/§12.3/§13)與HANDOVER §一~§九——該二者本即
「寫作當時之快照」,不隨章節遞移改寫。
1. 通用規則
| 規則 | 說明 |
|---|---|
| SM-01 | 所有狀態轉換須經狀態機檢核,不得跳轉 |
| SM-02 | 非法轉換回 CC-*-3001(HTTP 409) |
| SM-03 | 重複執行同一轉換(目標狀態已達成)→ 回 200(冪等) |
| SM-04 | 並發轉換以「狀態條件更新」實作(WHERE status = :expected) |
| SM-05 | 終態(標記 ◆)不得再轉換 |
| SM-06 | 每次轉換須記錄 from_status、to_status、operator、occurred_at
|
| SM-07 | 狀態機定義可參數化(狀態與轉換存於參數表),新增狀態不需改碼 |
| SM-08 |
狀態值域以「可持久化之 DDL CHECK 約束」為權威。本文件不得書寫 DDL 不存在之狀態——程式一旦寫入即觸發 ORA-02290。新增狀態須先改 ER/DDL 並附遷移腳本,再改本文件(1.2 版新增;與 1.1 版 §11 Saga RUNNING→STARTED 同一原則) |
| SM-09 |
一組狀態機只表達一個欄位。若值分屬不同欄位(不同實體或不同維度),即屬混用維度,須拆分為多個狀態機,不得以單一圖表達。判準:①值域與 DDL 幾乎無交集 → 不同分類軸;②值分屬不同表 → 不同實體;③同一表之不同欄位 → 不同維度。非生命週期之分類屬性(如 liability、unmatched_flag)應明列為正交屬性,不繪為狀態機(1.3 版新增;由 §7/§9/§10 之更正歸納而得) |
2. 卡片狀態機(Block Code)
stateDiagram-v2
[*] --> INACTIVE: 製卡完成待開卡
INACTIVE --> ACTIVE: 開卡成功
INACTIVE --> CLOSED: 未開卡註銷
ACTIVE --> LOCKED: 風控鎖卡
LOCKED --> ACTIVE: 解鎖
ACTIVE --> LOST: 掛失
LOST --> REISSUED: 補發新卡
ACTIVE --> REISSUED: 毀損換卡
ACTIVE --> REISSUED: 到期換卡
REISSUED --> ACTIVE: 新卡開卡
ACTIVE --> EXPIRED: 到期未續
EXPIRED --> CLOSED: 逾期末使用註銷
ACTIVE --> CLOSED: 客戶註銷
LOCKED --> CLOSED: 強制註銷
REISSUED --> CLOSED: 新卡註銷
CLOSED --> [*]
2.1 轉換表
| # | 來源 | 事件 | 目標 | 前置條件 | 副作用 | 允許角色 |
|---|---|---|---|---|---|---|
| 1 | INACTIVE |
開卡成功 | ACTIVE |
三要素驗證通過 | 寫入開卡時間、發 card.card.status-changed.v1
|
系統 |
| 2 | INACTIVE |
未開卡註銷 | CLOSED |
— | 釋放卡號、通知 | CSR_SUP |
| 3 | ACTIVE |
風控鎖卡 | LOCKED |
具風控權限 | 記錄原因、授權即時拒絕 | RISK |
| 4 | LOCKED |
解鎖 | ACTIVE |
具風控權限 | 記錄解除人 | RISK |
| 5 | ACTIVE |
掛失 | LOST |
客戶申請或風控判定 | 拒絕所有新交易、觸發補發流程 | CSR |
| 6 | LOST |
補發 | REISSUED |
已產生新卡號 | 舊卡註銷、權益移轉 | CSR_SUP |
| 7 | ACTIVE |
毀損換卡 | REISSUED |
已產生新卡號 | 同上 | CSR |
| 8 | ACTIVE |
到期換卡 | REISSUED |
到期日到達 | 同上 | 系統 |
| 9 | REISSUED |
新卡開卡 | ACTIVE |
新卡三要素驗證通過 | 新卡啟用 | 系統 |
| 10 | ACTIVE |
到期未續 | EXPIRED |
到期日 + 寬限期 | 停止交易 | 系統 |
| 11 | EXPIRED |
逾期末使用 | CLOSED |
逾期限 | 帳戶結清 | 系統 |
| 12 | ACTIVE |
客戶註銷 | CLOSED |
帳戶已結清 | 釋放權益、通知 | CSR_SUP |
| 13 | LOCKED |
強制註銷 | CLOSED |
主管核可 | 帳戶結清 | RISK |
| 14 | REISSUED |
新卡註銷 | CLOSED |
帳戶已結清 | — | CSR_SUP |
2.2 非法轉換(須被拒絕)
| 嘗試 | 結果 |
|---|---|
CLOSED → ACTIVE
|
❌ CC-CARD-3001(終態) |
LOST → ACTIVE
|
❌ 須經 REISSUED 換卡 |
INACTIVE → LOCKED
|
❌ 未開卡不得鎖卡 |
EXPIRED → ACTIVE
|
❌ 須重新申請 |
2.3 授權即時性要求
| 狀態 | 授權行為 |
|---|---|
ACTIVE |
正常檢核 |
LOCKED / LOST
|
即時拒絕(不得等快取過期) |
REISSUED(舊卡) |
拒絕 |
EXPIRED / CLOSED
|
拒絕 |
INACTIVE |
拒絕 |
狀態變更須於 1 秒內同步至授權服務(
BR-04-04)。
2.4 ★ block_code 與 status 之衍生關係(2026-09-23 新增,依 CR-020 F4 之裁定)
緣由(
CR-020F4,真機實測):原生命週期轉換(POST /cards/{cardId}/lifecycle-transitions)
僅更新block_code,而快發快綁(POST …/instant-issuance)兩欄皆更新
→ 同一業務事件(開卡)因路徑不同而結果不同,實測產生block_code=ACTIVE、
status=UNACTIVATED之組合(測試前 0 列、測試後 2 列)。裁定(BEAM PMO 2026-09-23):採完整映射——
status由block_code單向推導,
由狀態機統一維護,消除「兩條寫入路徑不一致」。
block_code(生命週期) |
status(啟用/帳務) |
說明 |
|---|---|---|
INACTIVE |
UNACTIVATED |
製卡完成、待開卡 |
ACTIVE |
ACTIVE |
已開卡可用 |
LOCKED |
SUSPENDED |
風控鎖卡 → 停用 |
LOST |
SUSPENDED |
掛失 → 停用 |
REISSUED |
UNACTIVATED |
新卡(補發/換卡)待開卡 |
EXPIRED |
SUSPENDED |
到期未續 → 停用 |
CLOSED |
CLOSED |
終態 |
⚠️ 三條鐵律(不可違反,違者即為漂移):
-
status為衍生欄位,非獨立維度——任何寫入路徑皆須由block_code推導,
不得由呼叫端指定、不得單獨更新。(此即CR-020F4 之根因。)
實作上映射表內建於CardBlockCode狀態列舉,以switch運算式之窮盡性檢查
保證:新增區塊碼而未定義對應status時編譯即失敗。 -
與 SM-09 之關係:SM-09 要求「一組狀態機只表達一個欄位」。本節之
status既為
block_code之衍生,不另繪狀態機圖——其轉換全由 §2 之圖決定,故未違反 SM-09。
⚠️ 若日後status需獨立於block_code變化(例:「卡片ACTIVE但帳務SUSPENDED」
——欠款停用而卡片仍可交易),則兩欄即為真正之兩個維度,屆時須新增獨立狀態機
並廢除本映射表。不可同時維持映射與獨立更新——那必然漂移。 -
保留兩欄(而非廢除
status)之理由:status已出現於對外契約
(CardStatusView.status)與既有 DDL(V1__baseline.sql),廢除屬破壞性變更。
故本期保留欄位、消除其獨立性,使其不可能與block_code不一致——
此為「既相容又一致」之取捨,不是承認兩欄各有語意。
3. 授權佔用狀態機(Limit Hold)
⚠️ 本節已於
CR-044 ③(BEAM PMO 2026-10-15 裁定)修訂:裁定「以limit-service為權威、
讀模型鏡射」。★ 原狀態機將PARTIALLY_RELEASED列為部分請款後之可達中繼態,
惟權威端之釋放 SQL 為「僅全額才轉RELEASED」——部分請款不改變狀態。
→ ★ 本節依權威行為重寫:PARTIALLY_RELEASED改為「DDL 值域內、本期權威不可達」之值。
stateDiagram-v2
[*] --> HELD: 授權核准並佔用
HELD --> RELEASED: 全額請款(含先前已部分釋放之差額)
HELD --> RELEASED: 授權取消或沖正
HELD --> EXPIRED: 超過有效期限未請款
RELEASED --> [*]
EXPIRED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 授權核准 | HELD |
佔用可用額度 |
| 2 | HELD |
部分請款 | HELD(狀態不變) |
★ CR-044 ③:原訂目標為 PARTIALLY_RELEASED,惟權威(limit-service)之 CASE 僅全額轉 RELEASED,部分請款只累計 released_amount、不改狀態;BEAM PMO 2026-10-15 裁定「以 limit 為權威、讀模型鏡射」→ 讀模型亦維持 HELD
|
| 3 | PARTIALLY_RELEASED |
請款完成 | RELEASED |
⬜ 本期不可達(#2 不再產生 PARTIALLY_RELEASED)——「全額釋放」由 #2 直接收斂至 #4 之語意 |
| 4 | HELD |
全額請款 | RELEASED |
全額釋放 |
| 5 | HELD |
取消/沖正 | RELEASED |
全額釋放 |
| 6 | HELD |
逾期未請款 | EXPIRED |
釋放額度(依參數之有效天數) |
| 7 | PARTIALLY_RELEASED |
逾期 | EXPIRED |
⬜ 本期不可達(HELD 逾期由 #6 處理) |
規則:RELEASED 與 EXPIRED 為終態;釋放操作具冪等性(已釋放則略過)。
3.0.1 權威來源與讀取端注意事項(CR-044 ③)
| # | 規則 |
|---|---|
| R1 |
權威:佔用狀態之權威為 limit-service(CC_LIM.LIMIT_HOLDS)。authorization-service 之 CC_AUTH.LIMIT_HOLDS 為讀模型投影,其 status 必須鏡射權威(SCS-001 §7.6/§7.15)——★ 兩端對同一事實不得有兩種陳述
|
| R2 |
部分請款不改變狀態:部分釋放之事實由 released_amount 累計表達;status 僅於「全額釋放」轉 RELEASED
|
| R3 |
PARTIALLY_RELEASED 保留於 DDL/契約值域,但本期不可達:SDD-001 §3 之 CHECK 與契約 enum 刻意不放寬(放寬值域屬破壞性變更,且無需求支撐);惟權威不寫入之 → 為不可達值。前端仍須保留其顯示分支(防舊資料) |
| R4 | ⚠️ 讀取端之過濾條件必須保留 PARTIALLY_RELEASED:availableLimit 之 SUM 原為 status IN ('HELD','PARTIALLY_RELEASED')(SCS-001 §7.6 曾因低估佔用而修正)。★ 即使本期不再新寫 PARTIALLY_RELEASED,歷史資料仍可能存在;省略該值將低估佔用、高估可用額度 → 超額風險。★ 故「權威不再寫入」≠「讀取時可省略」 |
3.1 授權決策狀態機(Authorization Decision)——2026-09-24 新增(CR-022 裁定後)
緣由:RFP
業務功能需求(授權)S/N 1.1(授權/發卡授權)第 4 項明文要求
「支援人工授權、強制授權、人工取消授權」(廠商回覆欄已填「滿足」)。
其中「人工取消授權」=沖正(§3 轉換 5)已完成;「人工授權/強制授權」須有
轉人工(REFERRAL) 之中間狀態方能成立。
⚠️ 查證:AuthDecision.REFERRAL原存在於 enum 但全服務零產生處——
本節為其唯一產生來源(SCS-001 §7.8.1)。
stateDiagram-v2
[*] --> APPROVED: 檢核全數通過
[*] --> DECLINED: 硬性拒絕(卡況/黑名單)
[*] --> REFERRAL: 命中人工複核門檻
[*] --> STANDIN: 額度服務不可用(降級代佔)
REFERRAL --> APPROVED: 人工授權(APPROVE)
REFERRAL --> DECLINED: 人工婉拒(DECLINE)
APPROVED --> [*]
DECLINED --> [*]
STANDIN --> [*]
| # | 來源 | 事件 | 目標 | 副作用 |
|---|---|---|---|---|
| 1 | [*] |
檢核全數通過 | APPROVED |
佔額(hold) |
| 2 | [*] |
硬性拒絕 | DECLINED |
不佔額 |
| 3 | [*] |
命中人工複核門檻 | REFERRAL |
仍佔額(交易已發生,先保留額度) |
| 4 | [*] |
額度服務不可用 | STANDIN |
本地代佔、standin_flag=1、待上送 |
| 5 | REFERRAL |
APPROVE(人工授權) |
APPROVED |
保留原佔額 |
| 6 | REFERRAL |
DECLINE(人工婉拒) |
DECLINED |
釋放原佔額(LimitPort.release,§3 轉換 5 同一路徑) |
規則:
| 規則 | 說明 |
|---|---|
| SM-D1 |
僅 REFERRAL 可被核決——對 APPROVED/DECLINED/STANDIN 核決回 CC-AUTH-3001(409) |
| SM-D2 |
核決為一次性——override_at 非空後再核決回 CC-AUTH-3002(409,終態) |
| SM-D3 |
不修改原檢核明細——auth_check_results 為當時事實;核決寫入 auth_overrides(新增紀錄,與 §4 轉換 4「產生反向交易、不修改原紀錄」同一原則) |
| SM-D4 |
force=true 僅限軟性拒絕——不得用於硬性拒絕(卡片 LOCKED/CLOSED、黑名單),避免繞過風控
|
| SM-D5 |
門檻參數化且預設關閉——AUTH.MANUAL_REVIEW 未設時不轉人工(fail-safe:不因缺參數把正常交易卡住) |
⚠️ 與
CR-022(B) 案之區別:本狀態機不允許對已DECLINED之件強制核准
——那會繞過檢核(人工可強制放行任何拒絕件),業務風險過高,故 (B) 案不予採納。
4. 帳務交易狀態機
stateDiagram-v2
[*] --> PENDING: 交易受理
PENDING --> POSTED: 入帳成功
PENDING --> FAILED: 入帳失敗
POSTED --> REVERSED: 沖正
FAILED --> PENDING: 異常處理後重新入帳
REVERSED --> [*]
FAILED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 交易受理 | PENDING |
寫入待處理 |
| 2 | PENDING |
入帳成功 | POSTED |
更新餘額、發事件 |
| 3 | PENDING |
入帳失敗 | FAILED |
記錄原因,可查詢 |
| 4 | POSTED |
沖正 | REVERSED |
產生反向交易(不修改原紀錄) |
| 5 | FAILED |
異常處理 | PENDING |
調整後重新入帳 |
規則
| 規則 | 說明 |
|---|---|
POSTED 不得回到 PENDING
|
已入帳不得重複入帳 |
REVERSED 為終態 |
沖正後不得再沖正(須以新交易處理) |
| 原紀錄不可變 |
REVERSED 僅標記,不刪除、不修改金額 |
5. 繳款狀態機
stateDiagram-v2
[*] --> RECEIVED: 繳款受理
RECEIVED --> ALLOCATED: 分配完成
ALLOCATED --> POSTED: 入帳並恢復額度
POSTED --> REVERSED: 繳款沖正
RECEIVED --> FAILED: 檢核失敗
POSTED --> UNMATCHED: 逾三日未收銷帳檔(重新佔額)
UNMATCHED --> POSTED: 收到銷帳檔後入帳
UNMATCHED --> REVERSED: 確認未入帳後沖正
REVERSED --> [*]
FAILED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 受理 | RECEIVED |
寫入繳款紀錄 |
| 2 | RECEIVED |
分配 | ALLOCATED |
依沖銷順序分配 |
| 3 | ALLOCATED |
入帳 | POSTED |
即時恢復額度 |
| 4 | POSTED |
沖正 | REVERSED |
反向處理並重新佔額 |
| 5 | RECEIVED |
檢核失敗 | FAILED |
記錄原因 |
| 6 | POSTED |
逾三日未銷帳 | UNMATCHED |
重新佔額(POC-11 情境三) |
| 7 | UNMATCHED |
收到銷帳檔 | POSTED |
補入帳 |
| 8 | UNMATCHED |
確認未入帳 | REVERSED |
沖正 |
值域:RECEIVED/ALLOCATED/POSTED/REVERSED/FAILED/UNMATCHED
——對齊 SDD-001 §3.7 ER 與 CC_PAYMENT.PAYMENTS.status 之 CHECK 約束(CK_PAYMENTS_STATUS)。
⚠️ 1.3 版更正說明(
UNMATCHED改採 DDL 增列途徑)1.2 版之稽核曾判定本節
UNMATCHED不可持久化(PAYMENTS.status之 CHECK 無此值),
並提出二途:①移除該狀態、改以旗標語意;②於 DDL 增列該值。
經核定採 ②DDL 增列——理由:本節之UNMATCHED為作業狀態
(已分配、逾三日未收銷帳檔),與旗標語意不同,不應為遷就 DDL 而捨棄。
項 內容 設計權威 SDD-001 §3.7——status之 CHECK 增列UNMATCHED實體交付 V4__payments_unmatched_status.sql(V1 基線已凍結,不得修改)契約 PaymentView.status枚舉增列UNMATCHED(改gen_openapi.py後重生)實作 PaymentStatus增列UNMATCHED實測 寫入 UNMATCHED成功;非法值觸發ORA-02290(CK_PAYMENTS_STATUS)——約束確為生效,非僅名稱存在
UNMATCHED與UNMATCHED_FLAG之關係(正交,非取代):
欄位 語意 判定時機 UNMATCHED_FLAG本次受理未取得沖抵來源(資料品質訊號) 受理時 status = 'UNMATCHED'已分配、逾三日未收銷帳檔(作業狀態) 批次 二者可同時成立;
UNMATCHED_FLAG不移除。保留事項(不得視為已實作):
轉換 #6(ALLOCATED→UNMATCHED)之產生者尚未實作
→ ✅ 已實作(C-19,2026-09-22):PaymentUnmatchedService+PaymentUnmatchedScheduler
(逾三日批次)、Payment#markUnmatched()(含重新佔額)。真機實測(走真實受理流程):
POSTED(LIMIT_RESTORED=1)→ 逾期 →UNMATCHED(LIMIT_RESTORED=0)、VERSION2→3;
未逾時者不動(負樣本)。詳SCS-008 §8.2。轉換 #8(UNMATCHED→REVERSED)尚未實作
→ ✅ 已放行(C-19,2026-09-22):PaymentStatus#reversible()現回true於
POSTED與UNMATCHED,消除「狀態機允許、程式拒絕」之缺陷;
真機實測對UNMATCHED繳款呼叫沖正端點 → 200REVERSED(SCS-008 §8.2)。⚠️ 轉換 6 來源狀態之更正(BEAM PMO 2026-09-22 裁定採方案 B)
本節原記來源為
ALLOCATED,惟實作C-19時發現三方不相容:
① 現行受理流程不停留ALLOCATED(PaymentService#accept一次完成
RECEIVED → ALLOCATED → POSTED、僅落地POSTED),故該狀態不可達;
②BRD-004POC-11 情境 11-3 之「繳款當下額度恢復 → 三日後回復原值(重新佔額)」
隱含額度已先恢復,而額度於post()才標LIMIT_RESTORED = true;
③SCS-008 §10 #5原載「受理即標」與實物不符。
故來源狀態更正為POSTED(額度已恢復者方有可撤銷之恢復),
並同步修訂本節轉換 6 與SCS-008。此更正使 POC-11 11-3 之語意自洽、批次對
現行常態狀態立即可達。
6. 分期計畫狀態機
stateDiagram-v2
[*] --> ACTIVE: 建立分期(佔用額度、產生各期明細)
ACTIVE --> COMPLETED: 按期攤還完畢
ACTIVE --> PRE_CLOSED: 提前結清
ACTIVE --> DEFAULTED: 違約加速到期
ACTIVE --> CANCELLED: Saga 補償取消
DEFAULTED --> COMPLETED: 清償完畢
COMPLETED --> [*]
PRE_CLOSED --> [*]
CANCELLED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 建立分期 | ACTIVE |
佔用額度、產生各期明細(建立即生效) |
| 2 | ACTIVE |
按期攤還完畢 | COMPLETED |
最終期入帳 |
| 3 | ACTIVE |
提前結清 | PRE_CLOSED |
剩餘本金一次清償、免收後續手續費 |
| 4 | ACTIVE |
違約 | DEFAULTED |
計收違約金、加速到期 |
| 5 | ACTIVE |
Saga 補償 | CANCELLED |
首期入帳失敗時取消 |
| 6 | DEFAULTED |
清償 | COMPLETED |
— |
值域:ACTIVE/COMPLETED/PRE_CLOSED/DEFAULTED/CANCELLED
——對齊 SDD-001 §3.9 ER 與 CC_INST.INSTALLMENT_PLANS.status 之 CHECK 約束。
⚠️ 1.2 版更正說明(原 1.0/1.1 版之
PENDING/REJECTED未採納)1.0/1.1 版之本節另含
PENDING(申請,未佔用)與REJECTED(拒絕),
以及SETTLED/EARLY_SETTLED二名稱。經查證後更正,依據如下:
項 原值 更正為 依據 申請狀態 PENDING移除(建立即 ACTIVE)① CC_INST.INSTALLMENT_PLANS.status之 CHECK 無PENDING,DEFAULT 'ACTIVE'——無法持久化;②BRD-001 §5.6之流程為「申請 → 資格與額度檢核 → {拒絕-額度不足 / 總約簽署 → 首期入帳}」,其中拒絕為同步交易拒絕,流程中不存在「已申請、待核准」之持久狀態;③ 實作InstallmentService.create直接建立ACTIVE拒絕狀態 REJECTED移除 同①(CHECK 無此值);額度不足之拒絕為同步回應( BRD-001 §5.6C3「拒絕-額度不足」),非持久狀態攤還完畢 SETTLEDCOMPLETEDDDL CHECK 之值域;語意相同,以 DDL 為權威統一術語 提前結清 EARLY_SETTLEDPRE_CLOSED同上 原則:與 1.1 版更正 §11 Saga
RUNNING→STARTED同一原則——
狀態值域以可持久化之 DDL 為權威;書寫 DDL 不存在之狀態,程式一旦寫入即觸發
ORA-02290(違反 CHECK 約束)。保留事項(不得視為已實作):
- 本節僅定義狀態與合法轉換。
COMPLETED之產生者(每期扣款/攤還排程)尚未實作,
故目前無程式路徑可達;詳見SCS-009 §10.5#7。- 轉換 #5(
ACTIVE→CANCELLED,Saga 補償)尚未實作。- 若日後業務確需「申請—核准」流程(即恢復
PENDING/REJECTED),
須另提 CR 於SDD-001 §3.9ER/DDL 補入該二狀態並附遷移腳本,
不得僅修改本文件——否則將重現「狀態機有、資料庫存不下」之同一缺陷。
7. 爭議案件狀態機(兩個正交狀態機)
1.3 版更正:本節原為單一狀態機,將兩個正交分類軸繪入同一圖——
「卡組織流程階段」與「案件處理/結果」,且與SDD-001 §3.13之
DISPUTE_CASES.stage/status二欄值域幾乎無交集(僅CLOSED一值相同)。
經核定拆分為兩個狀態機,分別對應實體之兩個欄位;
第三個欄位liability(責任歸屬)為正交屬性,非狀態機,見 §7.3。
7.1 爭議階段機(DISPUTE_CASES.stage)——卡組織流程階段
stateDiagram-v2
[*] --> INQUIRY: 受理並向收單行提出調單請求
INQUIRY --> FIRST_PRESENTMENT: 收單行第一次再提示
FIRST_PRESENTMENT --> SECOND_PRESENTMENT: 發卡行不服,第二次再提示
SECOND_PRESENTMENT --> PRE_ARB: 進入仲裁前置協商
PRE_ARB --> ARBITRATION: 協商不成,提交卡組織仲裁
INQUIRY --> CLOSED: 階段終止
FIRST_PRESENTMENT --> CLOSED: 階段終止
SECOND_PRESENTMENT --> CLOSED: 階段終止
PRE_ARB --> CLOSED: 階段終止
ARBITRATION --> CLOSED: 裁決確定
CLOSED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 受理並調單 | INQUIRY |
客服建檔後向收單行提出調單請求(BRD-001 BF-09 C1) |
| 2 | INQUIRY |
第一次再提示 | FIRST_PRESENTMENT |
收單行提交請款證明 |
| 3 | FIRST_PRESENTMENT |
第二次再提示 | SECOND_PRESENTMENT |
發卡行不接受第一次提示 |
| 4 | SECOND_PRESENTMENT |
仲裁前置 | PRE_ARB |
依卡組織規則進入強制協商 |
| 5 | PRE_ARB |
提交仲裁 | ARBITRATION |
協商不成 |
| 6 | INQUIRY |
階段終止 | CLOSED |
撤案,或收單行不再提示 |
| 7 | FIRST_PRESENTMENT |
階段終止 | CLOSED |
同上 |
| 8 | SECOND_PRESENTMENT |
階段終止 | CLOSED |
同上 |
| 9 | PRE_ARB |
階段終止 | CLOSED |
同上 |
| 10 | ARBITRATION |
裁決確定 | CLOSED |
卡組織裁決書確定 |
值域:INQUIRY/FIRST_PRESENTMENT/SECOND_PRESENTMENT/PRE_ARB/ARBITRATION/CLOSED
——對齊 SDD-001 §3.13 ER 與 CC_DIS.DISPUTE_CASES.stage 之 CHECK 約束(實測值)。
7.2 爭議案件狀態機(DISPUTE_CASES.status)——案件處理與結果
stateDiagram-v2
[*] --> OPEN: 客服案件建檔
OPEN --> EVIDENCE_PENDING: 證據不完整,待補件
EVIDENCE_PENDING --> REPRESENTED: 補件完成並送件
OPEN --> REPRESENTED: 證據完整,逕行送件
REPRESENTED --> WON: 發卡行勝
REPRESENTED --> LOST: 商戶勝
WON --> CLOSED: 帳務沖銷完成並結案
LOST --> CLOSED: 爭議駁回並結案
OPEN --> CLOSED: 撤案
EVIDENCE_PENDING --> CLOSED: 撤案
REPRESENTED --> CLOSED: 撤案
CLOSED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 受理 | OPEN |
客服案件建檔、記錄卡組織時限 |
| 2 | OPEN |
證據不完整 | EVIDENCE_PENDING |
BRD-001 BF-09 C2「否」→ 補充資料要件後重新送件 |
| 3 | EVIDENCE_PENDING |
補件完成 | REPRESENTED |
C3 → C1 送件 |
| 4 | OPEN |
證據完整 | REPRESENTED |
C2「是」→ 已向收單行提出 |
| 5 | REPRESENTED |
發卡行勝 | WON |
含「逾時未回覆逕行裁決」(C5)與仲裁「持卡人勝」(D5) |
| 6 | REPRESENTED |
商戶勝 | LOST |
含仲裁「商戶勝」(D5)→ 爭議駁回(D7) |
| 7 | WON |
帳務沖銷完成 | CLOSED |
沖銷、額度恢復、結案通知(E1~E3) |
| 8 | LOST |
結案 | CLOSED |
同上 |
| 9 | OPEN |
撤案 | CLOSED |
持卡人撤回 |
| 10 | EVIDENCE_PENDING |
撤案 | CLOSED |
同上 |
| 11 | REPRESENTED |
撤案 | CLOSED |
同上 |
值域:OPEN/EVIDENCE_PENDING/REPRESENTED/WON/LOST/CLOSED
——對齊 SDD-001 §3.13 ER 與 CC_DIS.DISPUTE_CASES.status 之 CHECK 約束(實測值)。
規則:CLOSED 為終態;爭議期間之計息依卡組織規則暫停(BR-09-03)。
7.3 正交屬性:liability(責任歸屬,非狀態機)
DISPUTE_CASES.liability 為分類屬性而非生命週期狀態,故不繪為狀態機:
| 值 | 語意 | 對應 BRD-001 BF-09 |
|---|---|---|
PENDING |
尚未定案(預設) | 案件進行中 |
ISSUER |
發卡行承擔損失 | D1「發卡行責任」→ D2 |
ACQUIRER |
收單行退款 | D1「收單行責任」→ D3 |
MERCHANT |
特約商店承擔 | D1 之商戶側歸屬 |
liability之寫入時機為 §7.2 轉換 5/6 定案之後;WON/LOST與liability
之精確對應(孰為ACQUIRER、孰為MERCHANT)須於實作時依卡組織規則確認,
已列入SCS-012 §10。
保留事項(不得視為已實作):
-
dispute-service目前僅有骨架(無領域模型),本節二狀態機之轉換全部尚未實作。 - §7.1 之
PRE_ARB/ARBITRATION涉及卡組織仲裁程序與時限控管,
BRD-001BF-09 僅至「提交卡組織仲裁」;各階段之組織時限參數尚未取得,
實作前須補(BR-09-01要求受卡組織時限控管)。
8. 額度調整狀態機
stateDiagram-v2
[*] --> DRAFT: 建立申請
DRAFT --> PENDING_APPROVAL: 送出(超過權限上限)
DRAFT --> EFFECTIVE: 生效(未超過權限上限)
PENDING_APPROVAL --> EFFECTIVE: 覆核通過
PENDING_APPROVAL --> REJECTED: 覆核駁回
EFFECTIVE --> EXPIRED: 臨時額度到期
EFFECTIVE --> REVOKED: 撤銷
EXPIRED --> [*]
REVOKED --> [*]
REJECTED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 建立 | DRAFT |
未生效 |
| 2 | DRAFT |
送出(超限) | PENDING_APPROVAL |
待覆核 |
| 3 | DRAFT |
送出(未超限) | EFFECTIVE |
直接生效 |
| 4 | PENDING_APPROVAL |
覆核通過 | EFFECTIVE |
生效並同步授權 |
| 5 | PENDING_APPROVAL |
覆核駁回 | REJECTED |
— |
| 6 | EFFECTIVE |
臨時額度到期 | EXPIRED |
自動調降 |
| 7 | EFFECTIVE |
撤銷 | REVOKED |
需主管核可 |
9. 帳單狀態機(兩個正交狀態機)
1.3 版更正:本節原為單一狀態機,將兩個實體繪入同一圖——
帳單本體(stmt.statements)與批次執行(stmt.statement_runs)。
其中GENERATING/FAILED屬批次執行,其餘屬帳單本體;
且原圖缺DISPUTED/CLOSED二值。經核定拆分為兩個狀態機。
9.1 帳單本體狀態機(stmt.statements.status)
stateDiagram-v2
[*] --> GENERATED: 出帳批次產製完成
GENERATED --> SENT: 帳款通知送達客戶
SENT --> PAID: 全額繳款
SENT --> PARTIALLY_PAID: 部分繳款
SENT --> OVERDUE: 逾繳款期限未達最低應繳
SENT --> DISPUTED: 帳款爭議成立
PARTIALLY_PAID --> PAID: 補足繳款
PARTIALLY_PAID --> OVERDUE: 逾繳款期限
OVERDUE --> PAID: 逾期後清償
DISPUTED --> PAID: 爭議處理後清償
DISPUTED --> OVERDUE: 爭議駁回且未清償
DISPUTED --> CLOSED: 爭議調整完成結案
PAID --> CLOSED: 結案歸檔
OVERDUE --> CLOSED: 轉催收後結案
CLOSED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 出帳批次完成 | GENERATED |
歸戶合併、計算應繳、凍結明細(BRD-001 BF-05 A1~A4) |
| 2 | GENERATED |
帳款通知送達 | SENT |
電子/行動/紙本(A5~A6) |
| 3 | SENT |
全額繳款 | PAID |
— |
| 4 | SENT |
部分繳款 | PARTIALLY_PAID |
未達全額 |
| 5 | SENT |
逾繳款期限 | OVERDUE |
未達最低應繳(B6「否」→ C2 延滯標記) |
| 6 | SENT |
帳款爭議 | DISPUTED |
轉 dispute-service(BF-09) |
| 7 | PARTIALLY_PAID |
補足繳款 | PAID |
— |
| 8 | PARTIALLY_PAID |
逾繳款期限 | OVERDUE |
— |
| 9 | OVERDUE |
逾期後清償 | PAID |
— |
| 10 | DISPUTED |
爭議處理後清償 | PAID |
— |
| 11 | DISPUTED |
爭議駁回 | OVERDUE |
爭議不成立且仍未清償 |
| 12 | DISPUTED |
爭議調整完成 | CLOSED |
帳務調整後結案 |
| 13 | PAID |
結案歸檔 | CLOSED |
— |
| 14 | OVERDUE |
轉催收後結案 | CLOSED |
C3 催收流程結束 |
值域:GENERATED/SENT/PAID/PARTIALLY_PAID/OVERDUE/DISPUTED/CLOSED
——對齊 SDD-001 §3.10 ER 與 CC_STMT.STATEMENTS.status 之 CHECK 約束(實測值)。
9.2 批次執行狀態機(stmt.statement_runs.status)
stateDiagram-v2
[*] --> GENERATING: 出帳批次啟動
GENERATING --> GENERATED: 產製完成
GENERATING --> FAILED: 產製異常
FAILED --> GENERATING: 自 Checkpoint 續跑
GENERATED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 批次啟動 | GENERATING |
建立本次執行紀錄 |
| 2 | GENERATING |
產製完成 | GENERATED |
寫入 finished_at、statement_count
|
| 3 | GENERATING |
產製異常 | FAILED |
寫入 error_detail,保留 checkpoint
|
| 4 | FAILED |
續跑 | GENERATING |
自 checkpoint 續跑,不從頭重跑 |
值域:GENERATING/GENERATED/FAILED
——對齊 CC_STMT.STATEMENT_RUNS.status 之 CHECK 約束(實測值)。
與
SDD-001 §3.10之關係:statement_runs與statement_cycle_accounts
係因契約要求「批次狀態與 Checkpoint」而於 V2 遷移補建
(V2__statement_runs_and_no_seq.sql),SDD-001 §3.10之 ER/DDL 為
V1 基線快照,未含此二表——已於該節加註變更交付說明。
10. 點數狀態機(三個正交狀態機)
1.3 版更正:本節原為單一狀態機,將三個維度繪入同一圖——
① 桶階段(bucket_stage:CUR/AV1/AV2)
② 桶狀態(points_expiry_buckets.status)
③ 帳戶營運狀態(points_accounts.status)。
經核定依維度拆分為三個狀態機。
原圖之CLOSED實屬帳戶狀態,已移至 §10.3。
10.1 桶階段流轉(points_expiry_buckets.bucket_stage)
stateDiagram-v2
[*] --> CUR: 首筆回饋入帳
CUR --> AV1: CUR 到期且餘額為正
AV1 --> AV2: AV1 到期且餘額為正
AV1 --> [*]: AV1 到期且無餘額可搬
AV2 --> [*]: AV2 到期並清零
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 首筆回饋 | CUR |
效期 = 入帳日 + 25 個月 |
| 2 | CUR |
到期且餘額 > 0 | AV1 |
餘額搬至 AV1(效期 +3 個月),CUR 桶重設效期 |
| 3 | AV1 |
到期且餘額 > 0 | AV2 |
餘額搬至 AV2(效期 +1 個月),AV1 桶清零 |
| 4 | AV1 |
到期且無餘額 | (結束) | 該階段桶已用盡,不建新桶 |
| 5 | AV2 |
到期 | (結束) | 餘額清零、移除效期 |
值域:CUR/AV1/AV2——對齊 POINTS_EXPIRY_BUCKETS.bucket_stage 之 CHECK(實測)。
依據:BRD-001 BR-07-03(CUR 當期/AV1 年度/AV2 活動三類分別管理)、POC-02 情境 2-11。
⚠️ 1.3 版更正說明(原 1.2 版之轉換 3 已移除)
原圖含轉換 3「
CUR到期且餘額 < 0 →CUR(餘額不移轉,僅重設效期)」。
經實測,CC_RW.POINTS_ACCOUNTS.BALANCE有balance >= 0之 CHECK 約束,
負餘額不可能存在,故該轉換不可達——已移除。
(此項原已由SCS-010 §10.2揭露為「不可達轉換」,本次一併更正。)
10.2 桶狀態機(points_expiry_buckets.status)
stateDiagram-v2
[*] --> ACTIVE: 桶建立
ACTIVE --> EXPIRED: 到期未用盡
ACTIVE --> REDEEMED: 兌換扣抵用盡
EXPIRED --> [*]
REDEEMED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 桶建立 | ACTIVE |
隨 §10.1 之階段產生,points > 0
|
| 2 | ACTIVE |
到期未用盡 | EXPIRED |
沒收(FORFEIT) |
| 3 | ACTIVE |
兌換扣抵用盡 | REDEEMED |
先到期先用(POC 2-12) |
| 4 | EXPIRED |
— | (終態) | — |
| 5 | REDEEMED |
— | (終態) | — |
值域:ACTIVE/EXPIRED/REDEEMED
——對齊 POINTS_EXPIRY_BUCKETS.status 之 CHECK(實測)。
10.3 帳戶營運狀態機(points_accounts.status)
stateDiagram-v2
[*] --> ACTIVE: 帳戶開立
ACTIVE --> FROZEN: 風控凍結
FROZEN --> ACTIVE: 解除凍結
ACTIVE --> CLOSED: 歸戶結清
FROZEN --> CLOSED: 凍結後結清
CLOSED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 開立 | ACTIVE |
首筆回饋或歸戶建立時開立 |
| 2 | ACTIVE |
風控凍結 | FROZEN |
停卡/爭議等風險事由 |
| 3 | FROZEN |
解除凍結 | ACTIVE |
風險事由消滅 |
| 4 | ACTIVE |
歸戶結清 | CLOSED |
帳戶註銷 |
| 5 | FROZEN |
結清 | CLOSED |
— |
| 6 | CLOSED |
— | (終態) | — |
值域:ACTIVE/FROZEN/CLOSED
——對齊 POINTS_ACCOUNTS.status 之 CHECK(實測)。
各狀態之操作權限(封存現行實作,見 SCS-010 §10.6):
| 狀態 | 入點 | 兌換 |
|---|---|---|
ACTIVE |
✅ | ✅ |
FROZEN |
✅ | ❌ 422 CC-RWD-1003
|
CLOSED |
❌ 409 CC-RWD-3002
|
❌ |
凍結僅限制負債側(兌換);消費仍會發生,點數屬客戶既有權益。
此為規格空白之封存決策(原SCS-010 §10.6列為「待 CR 確認」)——
本次補入本節即為該待辦之第 ① 項(「於SDD-005新增帳戶營運狀態機」)。
若日後改為「凍結應全面停止累積」,須另提 CR,並同步改AccrualService
與SDD-006 §3.4之錯誤碼。
11. Saga 狀態機
stateDiagram-v2
[*] --> STARTED: 啟動
STARTED --> COMPLETED: 全部步驟成功
STARTED --> COMPENSATING: 某步驟失敗
COMPENSATING --> COMPENSATED: 補償全部成功
COMPENSATING --> FAILED: 補償失敗
FAILED --> COMPENSATING: 人工介入後重試補償
COMPLETED --> [*]
COMPENSATED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 啟動 | STARTED |
持久化 Saga 狀態 |
| 2 | STARTED |
全部成功 | COMPLETED |
— |
| 3 | STARTED |
步驟失敗 | COMPENSATING |
逆序執行補償 |
| 4 | COMPENSATING |
補償成功 | COMPENSATED |
回覆失敗結果 |
| 5 | COMPENSATING |
補償失敗 | FAILED |
告警並可人工介入(CC-PLT-5002) |
| 6 | FAILED |
重試補償 | COMPENSATING |
人工觸發 |
術語對齊(2026-09-18):本節原將初始狀態書為
RUNNING,但
ops/local/init/oracle/01-init.sh之ck_saga_status約束實際枚舉為
('STARTED','COMPENSATING','COMPLETED','COMPENSATED','FAILED')——
持久化契約無RUNNING。以 DDL 為權威,本節正式更名為STARTED;
程式端SagaState列舉亦以此五值為準,避免寫入時觸發ORA-02290。終態:
COMPLETED、COMPENSATED(FAILED可經人工重試回COMPENSATING,非終態)。
12. 卡片申請案件狀態機(CC_CARD.CARD_APPLICATIONS.status)
1.4 版新增(2026-09-20,BEAM PMO 核定「進件系統缺口」)。
對應BRD-001 BF-01(受理進件 → 身分驗證與徵信 → 核決 → 製卡/即時給號 → 開卡)
與BRD-001 BF-10(Applied → UnderReview → Approved → Manufactured → Active)。
程式設計規格見SCS-024。
12.1 值域與 BRD 對應(以 DDL CHECK 為權威)
| 本機值域 | BRD BF-10 | 說明 |
|---|---|---|
RECEIVED |
Applied |
受理(通路 WEB/MOBILE/API/BRANCH/MERCHANT) |
REVIEWING |
UnderReview |
送徵信/徵審中 |
SUPPLEMENT |
—(BRD 未列) | 補件;補件期限由商品參數控制 |
APPROVED |
Approved |
核准,產生核准序號 approval_no
|
REJECTED |
Rejected |
婉拒 |
終態:APPROVED、REJECTED。
⚠️
Manufactured不在本狀態機:BRD BF-10 之Manufactured(製卡完成)
實為卡片狀態機 §2 之INACTIVE——§2 之入邊即為「[*] --> INACTIVE: 製卡完成待開卡」。
故本機以APPROVED為終態,其後由 §2 接手;兩機之交界點為「製卡完成、卡片建立」。
如此設計無需新增 DDL 值域(CARD_APPLICATIONS.status之 CHECK 五值已足)。
12.2 轉換表
| # | 起始 | 事件 | 結果 | 觸發者 | 附帶效果 |
|---|---|---|---|---|---|
| 1 | — | 受理 | RECEIVED |
通路/分行 | 產生 apply_no
|
| 2 | RECEIVED |
送徵信 | REVIEWING |
系統/徵審 | 呼叫徵信(§12.4) |
| 3 | REVIEWING |
要求補件 | SUPPLEMENT |
徵審 | 記補件原因與期限 |
| 4 | SUPPLEMENT |
補件完成 | REVIEWING |
客戶/徵審 | 重送徵信 |
| 5 | REVIEWING |
核准 | APPROVED |
徵審 | 產生 approval_no(§12.5) |
| 6 | REVIEWING |
婉拒 | REJECTED |
徵審 | 記婉拒原因 |
| 7 | SUPPLEMENT |
補件逾期 | REJECTED |
批次 | 逾 supplement.days 未補 |
| 8 | RECEIVED |
逕予婉拒 | REJECTED |
徵審 | 資格明顯不符,免徵信 |
12.3 非法轉換(須被拒絕,CC-CARD-3001)
| 起始 | 事件 | 為何非法 |
|---|---|---|
RECEIVED |
核准 | 未經徵信不得核准(fail-closed,見 §12.4) |
APPROVED |
任何 | 終態 |
REJECTED |
任何 | 終態 |
REVIEWING |
回 RECEIVED
|
不可逆向退回受理 |
CC-CARD-3002(終態不可再轉換) 用於APPROVED/REJECTED;
RECEIVED → APPROVED與REVIEWING → RECEIVED屬「非法轉換」,用CC-CARD-3001。
12.4 徵信(外部系統)與 fail-closed
徵信為外部系統(BRD-001 D-01 徵審系統),以 port/adapter 介接(§14.1)。
| 徵信結果 | 處置 |
|---|---|
APPROVE |
自動 → APPROVED
|
DECLINE |
自動 → REJECTED
|
REFER |
轉人工核決(CREDIT 角色),案件維持 REVIEWING
|
SUPPLEMENT |
→ SUPPLEMENT
|
| 呼叫失敗/逾時/未設定 adapter | ⚠️ 轉 REFER、維持 REVIEWING,記 WARN
|
為何是 fail-closed:徵信不可用時若靜默放行即為「靜默不做」型缺陷
(與 Flyway 未顯式引入 starter、認證預設 fail-open 同類,見SCS-020 §9.2、
SCS-022 §7.4)。故不得自動核准、亦不得自動婉拒——一律轉人工。
12.5 核准後之銜接(製卡/快發)
| 路徑 | 條件 | 處置 |
|---|---|---|
| 快發 |
fast_issue_flag=1 且已勾選快發條款(BR-01-01) |
核准後即時給號(POST /api/v1/cards),卡片建立為 INACTIVE
|
| 一般 | 其他 | 核准後寫入 Transactional Outbox 事件 card.application.approved,由批次送製卡檔 |
快發之三要素驗證(身分證字號+生日+核准序號)見
BR-01-04,失敗為CC-CARD-1002。
13. 通知狀態機(notif.notifications.status)——1.7 版新增
來源:
C-9(notification-service)實作(CR-017裁定 F/G)。
CC_NOTIF於 V1 基線完全無表,本輪以V2__notification_templates_and_notifications.sql
建表;值域以契約NotificationView.status為權威(QUEUED/SENT/FAILED)。
設計權威見SCS-016 §3/§7、SDD-001 §3.18。
stateDiagram-v2
[*] --> QUEUED: 受理(建立通知)
QUEUED --> SENT: 遞送成功
QUEUED --> QUEUED: 遞送失敗且未達重試上限(retry_count+1)
QUEUED --> FAILED: 遞送失敗且已達重試上限
SENT --> [*]
FAILED --> [*]
| # | 來源 | 事件 | 目標 | 說明 |
|---|---|---|---|---|
| 1 | — | 受理 | QUEUED |
受理期已驗證樣板存在/通道交集非空/變數齊備(fail-fast,SCS-016 §7.3) |
| 2 | QUEUED |
遞送成功 | SENT |
寫入 sent_at;終態 ◆
|
| 3 | QUEUED |
遞送失敗(未達上限) | QUEUED |
自轉換:retry_count + 1、寫入 fail_reason,待下一輪取件重試 |
| 4 | QUEUED |
遞送失敗(達上限) | FAILED |
終態 ◆;fail_reason 必載(領域層強制,未載即拒) |
值域:QUEUED/SENT/FAILED
——對齊 CC_NOTIF.NOTIFICATIONS.STATUS 之 CHECK 約束(實測值)。
⚠️ 為何「重試」不是一個狀態:
SCS-016 §3原載設計意圖含RETRYING,惟契約未定義該值
(NotificationView.status僅 3 值)。若於 DDL 增列RETRYING,則契約無處回傳,
應用層必須把它映射回QUEUED——即一個無對應語意之死狀態(假綠)。
故以轉換 3(QUEUED → QUEUED自轉換 +retry_count遞增)表達重試,不新增狀態。⚠️ 轉換 3 與 SM-03(冪等)之區別:SM-03 指「目標狀態已達成 → 回 200(無狀態變更)」;
轉換 3 之來源與目標同為QUEUED,但非冪等重複——其為有意義之重試計數
(retry_count遞增、fail_reason更新)。判準為有無狀態變更:SM-03 無,轉換 3 有。⚠️ 終態不可轉換(SM-05):
SENT/FAILED再嘗試轉換 →CC-NOT-3002(409)。
逾時重送同一冪等鍵不產生新通知(SM-03:回傳前次結果)——此為受理層之冪等,
與本節之狀態轉換無關。⚠️ 本期實作邊界(誠實揭露):轉換 2/3/4 之觸發者為本地 Log 通道 stand-in
(SCS-016 §8)——真實閘道未接(鐵則所稱真阻塞之一)。其「失敗」判定為
通道內容長度上限(真實閘道限制之近似值),目的在使轉換 3/4 可達且可測。
14. 狀態機實作規範
| 規範 | 說明 |
|---|---|
| 定義方式 | 狀態與轉換存於參數表(product.parameters,namespace state_machine.<entity>) |
| 檢核時機 | 領域層於執行轉換前檢核;資料庫以狀態條件更新為第二道防線 |
| 值域權威(SM-08) |
狀態值域一律以「可持久化之 DDL CHECK 約束」為權威(見 §1)。本文件不得書寫 DDL 不存在之狀態——程式一旦寫入即觸發 ORA-02290。新增狀態須先改 ER/DDL 並附遷移腳本,再改本文件 |
| 錯誤碼 | 非法轉換 CC-<SVC>-3001;終態轉換 CC-<SVC>-3002
|
| 冪等 | 目標狀態已達成 → 回 200,不重複執行副作用 |
| 稽核 | 每次轉換寫入 audit_trail(含前後狀態) |
| 事件 | 每次轉換發布 <context>.<aggregate>.status-changed.v1
|
| 測試 | 每個狀態機須有「全轉換矩陣測試」:合法轉換全通過、非法轉換全拒絕 |
14.1 狀態機參數化範例
{
"namespace": "state_machine.card",
"param_key": "transitions",
"param_value": [
{ "from": "INACTIVE", "event": "ACTIVATE", "to": "ACTIVE",
"guard": "three_factor_verified", "roles": ["SYSTEM"] },
{ "from": "ACTIVE", "event": "LOCK", "to": "LOCKED",
"guard": "risk_authorized", "roles": ["RISK"] },
{ "from": "LOST", "event": "REISSUE", "to": "REISSUED",
"guard": "new_card_no_generated", "roles": ["CSR_SUP"] }
]
}
14.2 全轉換矩陣測試要求
| 狀態機 | 轉換數 | 必測案例數 |
|---|---|---|
| 卡片 | 14 | 14(合法)+ 4(非法)= 18 |
| 佔用 | 7 | 7 + 3 = 10 |
| 交易 | 5 | 5 + 2 = 7 |
| 繳款 | 8 | 8 + 2 = 10 |
| 分期 | 6 | 6 + 3 = 9 |
| 爭議階段(§7.1) | 10 | 10 + 2 = 12 |
| 爭議案件(§7.2) | 11 | 11 + 2 = 13 |
| 額度調整 | 7 | 7 + 2 = 9 |
| 帳單本體(§9.1) | 14 | 14 + 2 = 16 |
| 帳單批次(§9.2) | 4 | 4 + 2 = 6 |
| 點數桶階段(§10.1) | 5 | 5 + 2 = 7 |
| 點數桶狀態(§10.2) | 5 | 5 + 2 = 7 |
| 點數帳戶(§10.3) | 6 | 6 + 2 = 8 |
| Saga | 6 | 6 + 1 = 7 |
| 申請案件(§12,1.4 新增) | 8 | 8 + 4 = 12 |
| 通知(§13,1.7 新增) | 4 | 4 + 2 = 6 |
| 合計 | 120 | 157 |
狀態機測試為
CSP-005 §3之必要覆蓋項:分支覆蓋率須達 100%。⚠️ 1.7 版:新增 §13 通知狀態機(4 轉換,含 1 條重試自轉換),組數 15 → 16,
合計 116/151 → 120/157。⚠️ 分期之轉換數於 1.2 版由 8 修正為 6(移除
PENDING/REJECTED相關 2 條,
見 §6 更正說明);合計由 82/105 更正為 80/103。⚠️ 1.4 版:新增 §12 申請案件狀態機(8 轉換),組數 14 → 15,
合計 108/139 → 116/151。⚠️ 1.3 版:§7/§9/§10 拆分為多個狀態機,故組數由 10 增為 14,
合計由 80/103 更正為 108/139:
§7 由 9 → 10 + 11 = 21;§9 由 11 → 14 + 4 = 18;
§10 由 7 → 5 + 5 + 6 = 16(原轉換 3 因不可達而移除,見 §10.1 說明)。
14.3 值域稽核(1.2 版新增;1.3 版全數對齊;1.4 版含 §12 申請案件;1.7 版含 §13 通知)
本節為對本文件全部 16 組狀態機之值域合規稽核——目的在於檢驗 SM-08
(狀態值域以可持久化之 DDL 為權威)是否被遵守。稽核方法:以 all_cons_columns +
all_constraints.search_condition_vc 實測各 schema 之 CHECK 約束,與本文件逐值比對。
| § | 狀態機 | 對應 DDL 欄位 | 結果 |
|---|---|---|---|
| 2 | 卡片(Block Code) | CC_CARD.CARDS.BLOCK_CODE |
✅ 對齊(7 值全同) |
| 3 | 授權佔用 | CC_LIM.LIMIT_HOLDS.STATUS |
✅ 對齊(4 值全同) |
| 4 | 帳務交易 | CC_POSTING.TRANSACTIONS.STATUS |
✅ 對齊(4 值全同) |
| 5 | 繳款 | CC_PAYMENT.PAYMENTS.STATUS |
✅ 對齊(6 值全同,1.3 版 DDL 增列 UNMATCHED) |
| 6 | 分期計畫 | CC_INST.INSTALLMENT_PLANS.STATUS |
✅ 對齊(1.2 版修正) |
| 7.1 | 爭議階段 | CC_DIS.DISPUTE_CASES.STAGE |
✅ 對齊(6 值全同,1.3 版拆分) |
| 7.2 | 爭議案件 | CC_DIS.DISPUTE_CASES.STATUS |
✅ 對齊(6 值全同,1.3 版拆分) |
| 7.3 | 爭議責任歸屬(正交屬性) | CC_DIS.DISPUTE_CASES.LIABILITY |
✅ 對齊(4 值全同) |
| 8 | 額度調整 | CC_LIM.LIMIT_ADJUSTMENTS.STATUS |
✅ 對齊(6 值全同) |
| 9.1 | 帳單本體 | CC_STMT.STATEMENTS.STATUS |
✅ 對齊(7 值全同,1.3 版拆分並補 DISPUTED/CLOSED) |
| 9.2 | 帳單批次 | CC_STMT.STATEMENT_RUNS.STATUS |
✅ 對齊(3 值全同,1.3 版拆分) |
| 10.1 | 點數桶階段 | CC_RW.POINTS_EXPIRY_BUCKETS.BUCKET_STAGE |
✅ 對齊(3 值全同,1.3 版拆分) |
| 10.2 | 點數桶狀態 | CC_RW.POINTS_EXPIRY_BUCKETS.STATUS |
✅ 對齊(3 值全同,1.3 版拆分) |
| 10.3 | 點數帳戶 | CC_RW.POINTS_ACCOUNTS.STATUS |
✅ 對齊(3 值全同,1.3 版拆分) |
| 11 | Saga | CC_PLT.SAGA_INSTANCES.STATUS |
✅ 對齊(5 值全同) |
| 12 | 申請案件 | CC_CARD.CARD_APPLICATIONS.STATUS |
✅ 對齊(5 值全同,1.4 版新增;2026-09-20 以 all_constraints.search_condition_vc 實測) |
| 13 | 通知 | CC_NOTIF.NOTIFICATIONS.STATUS |
✅ 對齊(3 值全同,1.7 版新增;2026-09-22 以 all_constraints.search_condition_vc 實測) |
16 組全數對齊、0 組待處理。 1.2 版稽核所列之 4 項不一致(A~D)
已於 1.3 版全數更正,逐項對照如下。
更正歷程(1.2 版 A~D → 1.3 版處置)
| # | 1.2 版之發現 | 1.3 版處置 | 交付 |
|---|---|---|---|
| A | §5 繳款 UNMATCHED 不可持久化 |
採 DDL 增列途徑:SDD-001 §3.7 增列該值 |
V4__payments_unmatched_status.sql;契約與 PaymentStatus 同步 |
| B | §7 爭議值域幾乎完全不同(僅 CLOSED 交集) |
拆分為兩個狀態機(階段/案件),liability 明列為正交屬性 |
本文件 §7;DDL 已有對應三欄,無須改 DDL |
| C | §9 帳單混用兩實體、缺 DISPUTED/CLOSED
|
拆分為兩個狀態機(帳單本體/批次執行),並補入缺值 | 本文件 §9;SDD-001 §3.10 加註 V2 交付說明 |
| D | §10 點數混用三個維度 | 依維度拆分為三個狀態機(桶階段/桶狀態/帳戶) | 本文件 §10;一併移除不可達轉換、封存 FROZEN 入點決策 |
1.4 版新增第 15 組(§12 申請案件):值域直接取
CARD_APPLICATIONS.STATUS既有 CHECK 五值,
未新增值域、未改 DDL——BRD BF-10 之Manufactured歸屬 §2 卡片狀態機之INACTIVE。A~D 均涉及設計決策,業經逐案核定後執行(核定紀錄見
CR-003)。
本次更正之共同依據為 SM-08:值域以可持久化之 DDL CHECK 為權威;
值分屬不同欄位者即屬混用維度,應拆分狀態機,不得以單一圖表達。
15. 變更記錄
| 版本 | 說明 |
|---|---|
| 1.7 | 2026-09-22|WorkBuddy AI|新增 §13 通知狀態機(notif.notifications.status)——C-9(notification-service,CR-017 裁定 F/G)落地後補正式化。3 狀態/4 轉換(含 1 條重試自轉換 QUEUED → QUEUED);值域以契約 NotificationView.status 為權威(QUEUED/SENT/FAILED)。⚠️ 兩項設計決策:①**「重試」不設獨立狀態**——SCS-016 §3 原載 RETRYING 而契約未定義,若增列則無處回傳、須映射回 QUEUED,即成無對應語意之死狀態(假綠);故以自轉換+retry_count 表達,並於節內載明其與 SM-03(冪等)之區別(判準:有無狀態變更)。②本期觸發者為本地 Log 通道 stand-in(真實閘道真阻塞),其失敗判定為通道內容長度上限,目的在使轉換 3/4 可達且可測。原 §13/§14 依序遞移為 §14/§15(CR-003 所載 §13.2/§13.3 即為本版之 §14.2/§14.3,內容不變);連動更新 §14.2(組數 15 → 16,合計 116/151 → 120/157)與 §14.3(16 組全數對齊,新增第 13 列) |
| 1.6 | 2026-09-22|WorkBuddy AI|BEAM PMO 裁定採方案 B:§5 轉換 6 之來源狀態由 ALLOCATED 更正為 POSTED(含 mermaid 與轉換表)——理由:現行 accept() 不停留 ALLOCATED(不可達),且「重新佔額」須以「額度已先恢復」為前提,而額度於 post() 才標;此更正使 BRD-004 POC-11 情境 11-3 之語意自洽。真機重跑(真實受理流程):POSTED(LIMIT_RESTORED=1)→ 逾期 → UNMATCHED(LIMIT_RESTORED=0)、VERSION 2→3;負樣本未逾時者不動。併揭露:SCS-008 §10 #5「受理即標」與實物不符,已於該文件更正 |
| 1.5 | 2026-09-22|WorkBuddy AI|C-19:§5 繳款之兩項「保留事項」解除——①轉換 6(ALLOCATED → UNMATCHED)之產生者已實作(PaymentUnmatchedService/Scheduler,逾三日批次;含 markUnmatched() 之重新佔額),真機實測逾時轉換與未逾時負樣本;②轉換 8(UNMATCHED → REVERSED)之 reversible() 已放行,實測沖正回 200。⚠️ 新增待裁定事項:轉換 6 之來源狀態可達性存疑——現行受理流程不停留 ALLOCATED(accept() 一次完成至 POSTED),且 BRD-004 POC-11 情境 11-3 之「重新佔額」隱含額度已先恢復,與本節轉換 6 之來源狀態不相容;須 BEAM PMO 裁定(裁定為 POSTED 時本節須隨之修訂)。併更正 1.4 列之懸空引用:原引 HANDOVER #C-31,該編號現屬他項(pricing TX_TYPE),實應為 #A-43
|
| 1.9 | 2026-09-24|開發團隊|新增 §3.1 授權決策狀態機(CR-022 裁定後)——依據 RFP FR 1.1 第 4 項「支援人工授權、強制授權、人工取消授權」(廠商回覆欄已填「滿足」,未實作即未履約)。新增 REFERRAL→APPROVED/DECLINED 兩條核決轉換,並訂 SM-D1~D5(僅 REFERRAL 可核決/核決一次性/不修改原檢核明細/force 僅限軟性拒絕/門檻參數化預設關閉)。⚠️ 併載明不採 CR-022 (B) 案(不得對 DECLINED 強制核准,以免繞過風控)。 |
| 1.0 | 初版:通用規則 SM-01~SM-07 與 11 組狀態機(卡片、佔用、交易、繳款、分期、爭議、額度調整、帳單、點數帳戶、Saga) |
| 1.1 | 2026-09-18|SA/Tech Lead|①**§11 Saga 初始狀態更名 RUNNING → STARTED——ops/local/init/oracle/01-init.sh 之 ck_saga_status 約束實際枚舉 ('STARTED','COMPENSATING','COMPLETED','COMPENSATED','FAILED'),持久化契約無 RUNNING;原書寫會使程式寫入時觸發 ORA-02290。以 DDL 為權威統一術語,並補註終態定義(COMPLETED/COMPENSATED 為終態,FAILED 可人工重試)。②§11 轉換 #5 補註錯誤碼 CC-PLT-5002。③新增本變更記錄區**(原文件欠缺,無法追溯修訂)。④§12 之非法/終態轉換碼 CC-<SVC>-3001/3002 於平台層具體化為 CC-PLT-3001/CC-PLT-3002,已依 CSP-003 §2 程序登錄於 SDD-006 §3.4
|
| 1.3 | 2026-09-19|開發團隊|依 CR-003 處置 §12.3 稽核所列之 4 項不一致(A~D),全數更正——①A §5 繳款:UNMATCHED 經核定採 DDL 增列途徑(原二選一:移除該狀態/DDL 增列)。理由:本節 UNMATCHED 為作業狀態(已分配、逾三日未收銷帳檔),與旗標語意不同,不應為遷就 DDL 而捨棄。交付:SDD-001 §3.7 之 CHECK 增列該值、V4__payments_unmatched_status.sql(V1 基線已凍結)、契約 PaymentView.status 與 PaymentStatus 同步;實測寫入成功、非法值觸發 ORA-02290。並釐清 UNMATCHED 與 UNMATCHED_FLAG 為正交維度(受理時/批次判定,可同時成立)。②B §7 爭議:原單一狀態機將「卡組織階段」與「案件處理/結果」二正交軸繪於一圖,與 DISPUTE_CASES.stage/status 值域幾乎無交集(僅 CLOSED)。經核定拆分為二(§7.1 階段機/§7.2 案件機),並將 liability 明列為正交屬性(§7.3)。DDL 已有對應三欄,無須改 DDL。③C §9 帳單:原單一狀態機混用兩個實體(STATEMENTS/STATEMENT_RUNS)且缺 DISPUTED/CLOSED。經核定拆分為二(§9.1 帳單本體 7 值/§9.2 批次執行 3 值)並補入缺值。④D §10 點數:原單一狀態機混用三個維度(桶階段/桶狀態/帳戶狀態)。經核定依維度拆分為三(§10.1/§10.2/§10.3);一併移除不可達轉換(原轉換 3「CUR 到期且餘額 < 0」——受 balance >= 0 之 CHECK 所限,SCS-010 §10.2 原已揭露)並封存 FROZEN 帳戶之入點決策(原 SCS-010 §10.6 之規格空白,本次即其待辦 ①)。⑤連動更正 §12.2:組數 10 → 14,合計 80/103 → 108/139。⑥**§12.3 改為全數對齊**(14 組)並載明更正歷程。⑦SDD-001 §3.7 加註變更交付說明(V1 凍結、不得 --force 重生);§3.10 加註 STATEMENT_RUNS/STATEMENT_CYCLE_ACCOUNTS 係 V2 交付 |
| 1.4 | 2026-09-20 |
| 1.2 | 2026-09-19|開發團隊|依 CR-002 更正 §6 分期計畫狀態機——①移除 PENDING(申請)與 REJECTED(拒絕):CC_INST.INSTALLMENT_PLANS.status 之 CHECK 約束無此二值(DEFAULT 'ACTIVE',無法持久化),且 BRD-001 §5.6 之流程中「拒絕-額度不足」為同步交易拒絕,不存在「已申請、待核准」之持久狀態;實作 create 亦直接建立 ACTIVE。②**SETTLED → COMPLETED、EARLY_SETTLED → PRE_CLOSED:語意相同,以 DDL 為權威統一術語。③轉換 #4 之來源由 PENDING 更正為 ACTIVE(Saga 補償發生於首期入帳階段)。④轉換數 8 → 6。⑤新增通用規則 SM-08「狀態值域以可持久化之 DDL CHECK 為權威」——與 1.1 版同一原則,並明訂「新增狀態須先改 ER/DDL 再改本文件」。⑥連動更正 §12.2** 全轉換矩陣:分期 8/11 → 6/9,合計 82/105 → 80/103。⑦§6 新增更正說明區,載明未採納之原設計與保留事項(COMPLETED 無產生者、轉換 #5 未實作;日後若需核准流程須另提 CR 補 ER/DDL,不得僅改本文件)。⑧新增 §12.3 值域稽核——對本文件全部 10 組狀態機逐一比對實體 DDL CHECK 約束(實測):6 組對齊、4 組待處理(§5 繳款 UNMATCHED、§7 爭議值域幾乎完全不同、§9 帳單混用兩實體、§10 點數混用三維度),各項均載明待決方向;稽核發現之 4 項未自行更正,因涉及設計決策(值軸歸屬、分類軸選擇),須逐案核定 |