Project

General

Profile

CR #228 » CR-027_事件流W-2推廣盤點與排序建議.md

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

 

CR-027 事件流(W-2)推廣盤點與排序建議

項目 內容
文件編號 CR-027
版本 1.8
提出日 2026-10-01
提出者 WorkBuddy AI(開發團隊)
緣起 BEAM PMO 指示「① 其餘 15 個事件的生產/消費(比照本模式逐條打通)」。前 3 條已打通(#A-117~#A-121)後,動工前查證發現 8 項文件互相矛盾/缺口——盲目照做會做出語意錯誤的事件流,故先盤點並請求裁定。1.1:批次 A-1(再發現 2 項)。1.2:批次 A-2/A-3(再發現 2 項)——批次 A 全數完成。1.3:#1~#4(hold 群)已化解(CR-028);#13 新增。1.4:批次 B 完成(#A-128);#14 新增。1.5:批次 A-4 完成(#A-133~#A-138)——payload 已定義之 9 項事件全數打通;#15 新增(主題命名不一致);併更正 #A-131 之根因。1.6:批次 C 動工——CR-029 新建(消費端盤點與讀模型設計建議);C-1/C-2 完成;#16 新增(事件 vs 同步呼叫之重複)。1.7:§5 十二項全數裁定並執行完畢(CR-030)。1.8:批次 C 剩餘項——C-5 首項完成(party 卡片狀態投影,架構上唯一解;CR-031);C-7 裁定暫緩(實測 9 服務皆無參數快取)
影響範圍 SAD-002 §3、SAD-003 §4、SAD-004 §5.2/§5.3/§5.4、SDD-006 §4.2;GOV-006 §4.8
狀態 批次 A ✅、批次 B ✅、批次 A-4 ✅、批次 C 🟡;★ §5 之 #5~#16(十二項)已全數裁定(CR-030)並執行完畢;★ 批次 C 之 C-1/C-2/C-5 首項已完成(#A-141/#A-143)、C-7 裁定暫緩(CR-031 §3);餘 12 項事件之 payload 未定義(需先立契約)、C-5 其餘 8 條目仍待立讀模型規格

1. 現況(已完成)

# 事件 生產端 消費端 狀態
1 card.card.status-changed.v1 card-service ✅ authorization-service ✅ ✅ 全鏈路(#A-117+#A-118)
2 limit.limit.adjusted.v1 limit-service ✅ authorization-service ✅ ✅ 全鏈路(#A-119)
3 authorization.authorization.approved.v1 authorization-service ✅ notification-service ✅(#A-125);posting/limit ⬜ ✅ 全鏈路(至少一消費端)(#A-121+#A-125)
4 limit.hold.created.v1(不在 SAD-004 §5.2) limit-service ✅ authorization-service ✅(#A-128) ✅ 全鏈路(#A-122+#A-128)
5 limit.hold.released.v1 limit-service ✅ authorization-service ✅(#A-128) ✅ 全鏈路(#A-122+#A-128)
6 payment.payment.posted.v1 payment-service ✅ limit-service ✅ ✅ 全鏈路(#A-124;CR-027 批次 A-1)
7 card.card.issued.v1(不在 SAD-004 §5.2) card-service ✅ (無) 🟡 僅生產端(#A-126)
8 card.token.bound.v1(不在 SAD-004 §5.2) card-service ✅ (無) 🟡 僅生產端(#A-126)

🔧 補充:limit.hold.created.v1(第 4 列)亦已於 #A-128 完成消費端(auth 本地 limit_holds 投影)→ 全鏈路。


2. 兩份「權威清單」不一致(本 CR 之核心)

盤點發現專案內有兩份事件清單,且不一致:

來源 性質 事件數
SAD-002 §3(各服務之「事件(發布)/(訂閱)」) 服務 × 事件矩陣(含訂閱關係) 約 30
SAD-004 §5.2(事件清單(主要)) 主要事件表(含主要消費者) 16

⚠️ 兩份皆自稱權威(SAD-004 為「事件治理」章、SAD-002 為「微服務劃分」章),
惟內容不同:SAD-002 有 party.*/card.card.issued/card.token.bound/posting.balance.changed
等,SAD-004 §5.2 皆無;反之 SAD-004 §5.2 有 dispute.case.opened.v1,SAD-002 §3.9~3.17 則以摘要形式列出。
本 CR 不擅自擇一(見 §5 待裁定 #5)。


3. 生產端就緒度(可線性推進,風險低)

服務 應發布事件 生產端掛點(程式) 契約 payload 就緒
authorization …approved.v1 ✅/…declined.v1 AuthorizeUseCase(已接 outbox) SDD-006 §4.2 ✅ 🟢 易
limit limit.hold.created.v1/limit.hold.released.v1/limit.special-limit.set.v1 LimitHoldService(已接 outbox) ✅ 🟢 易
card card.card.issued.v1/card.token.bound.v1 CardQueryService(已接 outbox) ✅ 🟢 易
party party.account.created.v1/party.block-code.changed.v1/party.flag.applied.v1 需接 outbox(尚無) ✅ 🟡 中
product product.parameter.changed.v1/product.product.published.v1 需接 outbox ✅ 🟡 中
posting posting.transaction.posted.v1/…reversed.v1/posting.balance.changed.v1 需接 outbox ✅ 🟡 中
payment payment.payment.posted.v1/…reversed.v1 需接 outbox ✅ 🟡 中
statement statement.statement.issued.v1 需接 outbox ✅ 🟡 中
installment installment.plan.created.v1/…period.settled.v1 需接 outbox ✅ 🟡 中
rewards rewards.points.accrued.v1/…redeemed.v1 需接 outbox ✅ 🟡 中
clearing clearing.batch.matched.v1 需接 outbox ✅ 🟡 中
dispute dispute.case.opened.v1/…closed.v1 需接 outbox ✅ 🟡 中
merchant merchant.settlement.submitted.v1 需接 outbox ✅ 🟡 中

結論:生產端 13 服務皆可做——掛點明確、SDD-006 §4.2 之 payload 已定義、模式已驗證 3 次。
成本主要在每個服務要各建一次 outbox 表 + relay(V?_outbox.sql + 6 個類別),屬複製型工作。


4. 消費端就緒度(真正的難點)

判準:消費端是否已有「該被餵養的表」。沒有的話,貿然建表=無中生有(CSP-001 §8 B2/假綠風險)。

事件 消費者(SAD-004 §5.2) 消費端表 就緒
authorization…approved.v1 posting/limit/notification posting:無「待請款」表;limit:account_limits.used_amount(有欄位);notification:notifications(有表) 🟡 部分可做
authorization.hold.created.v1 limit limit 之 limit_holds 是權威,不應由 auth 覆寫 ⛔ 語意矛盾(§5 #1)
limit.hold.created.v1 authorization(SAD-002) auth 之 limit_holds(本地投影,無餵養者) 🟡 有表,但 payload 缺 authorizationId(§5 #4)
limit.hold.released.v1 authorization 同上 🟡 同上
product.parameter.changed.v1 全體 無參數快取表 ⛔ 需新表+17 服務
posting.transaction.posted.v1 statement/rewards/clearing/limit limit:account_limits.used_amount;其餘多為權威表自身 🟡 部分
posting.transaction.reversed.v1 statement/rewards 同上 🟡 部分
payment.payment.posted.v1 limit/posting/notification limit:account_limits.used_amount(恢復額度,語意明確) 🟢 可做
statement.statement.issued.v1 payment/notification payment:無帳單表 ⛔ 需新表
installment.plan.created.v1 limit/posting limit:分期佔用(無對應表) ⛔ 需新表
rewards.points.accrued.v1 notification notifications(有表) 🟡 但發送通道為真阻塞
rewards.points.redeemed.v1 posting/notification posting:無點數兌換入帳表 ⛔
clearing.batch.matched.v1 posting 無對應表 ⛔
dispute.case.opened.v1 posting/notification 無對應表 ⛔

結論:消費端僅 2~3 條可立即做;其餘需先立讀模型規格(=先決定「這張表是什麼」),
否則就是為打通而造表——正是客戶硬性要求禁止的「塞框架/mock data」精神。


5. ★ 待裁定(十六項)——動工前必須先定

✅ 本節 12 項已全數裁定(2026-10-01,CR-030)

BEAM PMO 2026-10-01 指示:「以 Best Practice(Payment Industry 20 年金融科技/架構師)直接將這些問題解決」。
→ 已立 CR-030《事件流治理:CR-027 §5 十二項待裁定之架構裁定》,以 4 條業界通則(P1~P4)
為方法論,逐項裁定並載明「理由/業界依據/影響面/可逆性」。本節下表保留為決策歷程。

# 爭點 裁定 執行狀態
5 事件清單權威 (c) SAD-004 §5.2 為唯一權威;SAD-002 §3 降為指標 ✅ 文件
6 q_* 語意 廢除 7 個自投影;改立「跨服務投影清單」 ✅ 文件
7 declined.v1 不納入事件流;改以 metrics 涵蓋 ✅ 文件 + 程式
8 REFERRAL 缺漏 ★ approved.v1 語意=「已佔用額度」;補 decision;發射條件改 holdsLimit() ✅ 程式(真缺陷)
9 UNMATCHED/REVERSED ★ 補發射 + limit 重新佔用(超額風險之真缺陷) ✅ 程式(真缺陷)
10 posted.v1 缺 currency 確認為可選(已執行)+ 提升為通則(凡涉金額必帶) ✅ 文件(追認)
11 approved.v1 擴充 2 欄 追認(cardNoMasked 為遮罩,符合 PCI) ✅ 文件(追認)
12 sequenceNo 交錯 明訂不變式「同聚合之所有事件型別共用同一序號來源」;修正 card ✅ 文件 + 程式
13 card.status-changed payload 追認以實作為準 ✅ 文件(追認)
14 limit.hold.created 補 currency 追認 ✅ 文件(追認)
15 主題命名不一致 (a) 對齊文件慣例;修正 product/clearing/dispute/posting ✅ 文件 + 程式
16 事件 vs 同步呼叫 (b)「同步為主、事件為輔」;事件不重做同步已做之寫入 ✅ 文件 + 連帶收斂批次 C 範圍

另 4 項留待外部輸入(CR-030 §4.2,不得由開發臆造):業務文案(通知模板)/
卡組織規範(拒付時限)/聯絡資訊端點/累點規則。

以下為決策歷程原文(保留供追溯):

✅ #1~#4(hold 事件群)已化解(2026-10-01,CR-028)——無需 BEAM PMO 裁定:
經取三項客觀證據(authorization.hold.* 全庫程式碼零引用/其 payload 與 limit.hold.created.v1
逐欄相同/hold 之權威在 CC_LIM.LIMIT_HOLDS),確認為「同一事實兩個生產者」:
(#1) 廢除 authorization.hold.*,hold 事件一律由 limit 發布;
(#2/#3) authorization-service 同時訂閱 limit.hold.created.v1 與 limit.hold.released.v1;
(#4) limit.hold.created.v1 補 authorizationId 為可選欄位(不升版)。
程式碼零影響;已執行 6 份文件之同步修訂(詳 CR-028 §4)。
→ 批次 B 之規格障礙已解除(藍圖見 CR-028 §6)。

下列原表保留作為決策歷程(爭點與選項),現況以本 blockquote 為準。

# 爭點 衝突內容 選項 建議
1 hold 事件之方向與權威 SAD-002 §3.1 說 authorization 發布 authorization.hold.created.v1/.released.v1;SAD-002 §3.2 又說 limit 發布 limit.hold.created.v1/.released.v1。而 SAD-002 §3.2/SDD-001 明訂 hold 之權威在 CC_LIM.limit_holds (a) 廢 authorization.hold.*(只留 limit.hold.*)
(b) 保留,但定義為「本地代佔(stand-in)」事件
(c) 兩者並存,各自語意於 SAD-004 補明
(a)——權威只有一個;authorization.hold.* 實為命令(「請你佔用」),不該是事件。若採 (b) 須補 payload 與消費者
2 authorization 訂閱清單 SAD-002 §3.1 訂閱 limit.hold.created.v1;SAD-004 §5.2 無此事件(只有 limit.hold.released.v1) (a) 以 SAD-002 為準(補進 SAD-004)
(b) 以 SAD-004 為準(修 SAD-002)
(a)——auth 需要 hold 的建立才知本地投影該有幾列;只給 released 會「釋放一筆不存在的佔用」
3 limit.hold.released.v1 之消費者 SAD-004 §5.2 消費者=authorization;SAD-002 §3.1 之 auth 訂閱清單未含 同上 補進 SAD-002(與 #2 同批處理)
4 limit.hold.created.v1 payload 缺 authorizationId SDD-006 §4.2 為 holdId, accountId, holdAmount, holdType——無 authorizationId,auth 無法把 hold 對應回自己的授權(limit_holds.authorization_id) (a) 補 authorizationId(新增必填 → 依 SAD-004 §5.3 須升 v2 + 雙軌 ≥1 release)
(b) 改為選填(不升版)
(c) auth 端以 holdId 反查(需另存對照,不建議)
(b)——authorizationId 為可選(stand-in 路徑無對應授權時可空),依 SAD-004 §5.3「新增可選欄位不升版」
5 事件清單以哪份為權威 SAD-002 §3(~30 事件)vs SAD-004 §5.2(16 事件)不一致 (a) SAD-002 為權威(服務矩陣),SAD-004 §5.2 降為「主要事件節錄」
(b) SAD-004 §5.2 為權威,逐服務回寫 SAD-002
(c) 合併為單一清單(放 SAD-004,SAD-002 僅留指標)
(c)——「單一真實來源」是本案鐵律;兩份並存必然再漂移
6 SAD-003 §4.1 之 7 個 q_* 讀模型語意 多數與該服務自身權威表同名同義(q_transaction_history vs posting.transactions、q_authorization_history vs authorization.authorizations、q_available_limit vs limit.account_limits…) (a) 視為「服務內自投影」(寫模型→事件→讀模型)
(b) 視為「跨服務投影」(SAD-003 表頭之「服務」欄為存放地)
(c) 廢除本表
(b) 或 (c)——若是自投影,實作價值低且與權威表混淆(本專案已有 card_status_caches 之「本地投影」慣例,語意清楚);建議明確改寫為跨服務投影,否則廢除並以「各服務之 CQRS 讀模型清單」取代
7 authorization.authorization.declined.v1 是否納入 SDD-006 §4.2 有定義、SDD-003 時序圖有寫、SAD-002 §3.1 有列;惟 SAD-004 §5.2「主要事件」未列 (a) 納入(拒絕亦需下游得知?)
(b) 不納入(無消費者)
(b)——目前找不到消費者(拒絕不佔額、不產生待請款)。若為稽核用途,應走 audit_trail 而非事件流。但若客戶要求「授權拒絕率即時看板」則需 (a)
8 REFERRAL(轉人工)事件缺漏(#A-121 實測發現,已記 SCS-001 §10 #8) REFERRAL 已佔用額度,卻不發射任何事件(AuthDecision.isApproved() 不含 REFERRAL)→ 下游無從得知該筆佔用 (a) 另立 authorization.authorization.referred.v1
(b) 讓 approved.v1 涵蓋 REFERRAL(以 standinFlag/新增 decision 欄位區分)
(b)——轉人工件確實佔了額度,語意上屬「已核准佔用、待人工覆核」;另立事件會讓下游要多接一條而語意重疊。建議 payload 補 decision(可選),讓下游可分辨
9 UNMATCHED/REVERSED 之事件未實作(#A-124 實作 A-1 時發現,已記 SCS-008 §10 #11) SCS-008 §8.2 轉換 6 會把 LIMIT_RESTORED 設回 false(撤銷額度恢復=重新佔額)、§7 沖正亦然;惟 §7.1 只發射 posted.v1 → 「繳款後又轉未匹配/沖正」時,額度不會被重新佔用 (a) 補 payment.payment.unmatched.v1(SDD-006 §4.2 已定義;惟不在 SAD-004 §5.2)+ payment.payment.reversed.v1(SAD-002 §3.7 有列)
(b) 讓 posted.v1 之 payload 帶 limitRestored,消費者據以「恢復/撤銷」
(c) 暫不處理,記為已知限制
(a)——兩個語意不同(「繳款入帳」vs「重新佔額」),用同一事件帶旗標會讓消費者承擔狀態機責任。⚠️ 惟 (a) 需先解決 #5(清單權威),否則又是不在清單內的事件
10 payment.payment.posted.v1 payload 缺 currency(#A-124 實作 A-1 時發現,已記 SCS-008 §10 #12) SDD-006 §4.2 之欄位無 currency,惟 payments.currency 存在;消費端(limit-service)之額度主檔鍵為 (account_id, currency) → 無法精確定位 (a) 補 currency 為必填(升 v2,雙軌 ≥1 release)
(b) 補為可選(不升版)
(c) 不改契約,消費端以「該帳戶恰一列才更新」之保守規則處理
(b)——依 SAD-004 §5.3「新增可選欄位不升版」;消費端已以 (c) 之保守規則實作(多幣別時告警不更新,不臆測)。
★ 2026-10-01 更新(CR-029 批次 C-1/C-2):已依 (b) 執行——payment.posted.v1 及 statement.statement.issued.v1 皆補可選 currency(不升版;SCS-008 §7.2/SCS-011 §7.2)。理由:notification-service 之既有種子模板 PAYMENT_RECEIVED/STATEMENT_ISSUED 需要該欄,否則無法建立通知(CC-NOT-1003)。請追認(比照 #11/#14 之「依建議執行 + 登記待裁定」)
11 authorization.authorization.approved.v1 payload 已擴充 2 個可選欄位(#A-125 實作 A-2 時主動擴充,已記 SCS-001 §7.14/SCS-016 §10 #10) AUTH_APPROVED 樣板需要 cardLast4/merchant,惟原 payload 未帶 cardNoMasked/merchantId → 通知無法建立(變數不足) (a) 接受本次擴充(可選欄位,依 SAD-004 §5.3 不升版)
(b) 改為必填並升 v2 雙軌
(c) 撤回,改由消費端容忍缺欄(以空值渲染)
(a)——兩欄皆非敏感(cardNoMasked 為遮罩,先例見 card.card.status-changed.v1;merchantId 為商戶代號)。⚠️ 惟須留意:擴充前之舊事件將因變數不足而略過(不會補出通知)——重放歷史事件時需知悉
12 card.card.issued.v1/token.bound.v1 之 sequenceNo 與 status-changed 不同源(#A-126 實作 A-3 時發現,已記 SCS-003 §10 #19) 前者用同 JVM 內遞增之時間戳基底序號,後者用卡片版本;惟二者 aggregateId 同為 cardId → 同一聚合之不同事件型別可能序號交錯 (a) 統一為卡片版本推導(cardId 層級)
(b) 改為聚合層級持久化序號(每個 aggregateId 一計數器)
(c) 明訂「序號僅在 (aggregateId, eventType) 內有意義」,消費者據此判準
(b) 為正解(SAD-004 §5.4 要求「同一聚合內依 sequenceNo 遞增」);(c) 為立即可行之權宜——惟須同步修 SAD-004 §5.4,避免與現行文字衝突
13 SDD-006 §4.2 之 card.card.status-changed.v1 payload 與實作不符(CR-028 執行時發現) 契約表載 cardId, fromBlockCode, toBlockCode, reason(狀態變更之差量);惟實作(SCS-003 §7.10,#A-118 真機驗證)發射者為變更後之完整狀態(cardNoHash, cardId, accountId, cardNoMasked, status, blockCode, expiryDate) (a) 依實作更正契約表(已於 CR-028 執行)
(b) 改實作以符合原契約(破壞性:已發布之事件形狀改變,且 SCS-003 §7.10/消費端皆須改)
(a)——實作已真機驗證並有消費端(card_status_caches);原契約表為該事件之唯一錯誤記載。惟依 CSP-003 §2 精神,改契約須經覆核 → 列此待 BEAM PMO 確認
14 limit.hold.created.v1 payload 缺 currency(批次 B 實作時發現) CC_AUTH.LIMIT_HOLDS.currency 為 NOT NULL(availableLimit 之查詢亦以幣別過濾),惟事件原 payload 未帶 currency → 消費端無法寫入本地投影 (a) 補 currency 為可選(已於批次 B 執行;依 SAD-004 §5.3 不升版)
(b) 補為必填(升 v2 雙軌)
(c) 消費端一律回查 account_limit_caches
(a)——limit-service 本來就有該值(hold() 之入參),且已實作回查 fallback(事件未帶時回查,恰一列才用)。⚠️ 與 #10(posted.v1 缺 currency)同型——建議一併裁定
15 ★ Kafka 主題命名不一致(本批實測發現,2026-10-01) OutboxPublisher.topicFor 為 cc.<aggregateType>.<eventType>;SAD-004 §5.4 之示例與 SCS-002 §7.5/SCS-003 §7.11 之既有記載皆為**「事件全名去版本後、再移除一層與聚合同名之首段」(card.card.issued.v1 → card.issued → 主題 cc.card.card.issued)。惟後期 4 個服務寫入完整名** → 主題多一層重複:product.parameter.changed → cc.product.parameter.changed;clearing.batch.matched → cc.clearing.batch.matched;dispute.case.opened → cc.dispute.case.opened;posting.transaction.posted → cc.posting.transaction.posted (a) 對齊文件慣例(4 服務各改 1 個常數 → 主題變為 cc.product.parameter.changed/cc.clearing.batch.matched/cc.dispute.case.opened/cc.posting.transaction.posted)
(b) 改文件以承認「完整名」為準(則 card/limit/auth 三服務需改,且主題將出現三層如 cc.card.card.card.issued)
(a)——①現階段零消費者(批次 C 未動工),變更成本=每服務 1 行 + 重驗;②文件(SAD-004 §5.4 示例 + 兩份 SCS 既有記載)為多數且較早之慣例;③**(b) 會使主題退化為三層**(cc.card.card.card.issued),明顯非設計意圖。★ 本批之 statement/rewards/merchant/notification 已依 (a) 實作(cc.statement.statement.issued/cc.rewards.points.accrued/cc.merchant.settlement.submitted/cc.notification.sent)→ 目前新舊不一致,建議裁定後一次對齊

★ 附註(#A-131 根因更正,2026-10-01):#A-131 原記「clearing 之端到端受阻於服務間 ACL」,
經複查更正——本機 ops/local/sso.env 為 CARDCORE_SECURITY_ENABLED=true(SSO 開啟),
故未帶權杖即回 401 CC-PLT-4010(未認證);ACL 之拒絕碼為 403 CC-PLT-4030,二者不同。
以合法使用者權杖(demo.csr/client cardcore-services,azp 非 cc-* → 不受服務間 ACL 管制)
重跑即 201、outbox 落列、主題建立。→ 非阻塞,clearing 已於本批完成驗證。

| 16 | ★ 事件 vs 同步呼叫之重複(CR-029 批次 C 盤點發現,2026-10-01) | 三條消費邊所描述的行為已由既有同步呼叫完成:① authorization.approved.v1 → limit(佔用額度)vs AuthorizeUseCase 同步呼叫 limit 佔用;② payment.payment.posted.v1 → posting(繳款入帳)vs payment 同步呼叫 posting 入帳;③ posting.transaction.posted.v1 → statement(帳務事件)vs statement 結帳時同步查詢 posting。兩者並存必然重複套用(重複佔用/重複入帳/兩套真相) | (a) 事件為唯一路徑——移除同步呼叫(授權需即時佔用,不建議)
(b) 同步為主、事件為輔——事件僅供投影/通知,不重做同步已做之寫入;SAD-004 §5.2 之「用途」欄須改寫
(c) 逐案裁定 | (b)——① 授權/繳款之即時性使同步不可去;② 事件驅動之價值在跨服務投影與通知,重做寫入無增益且引入重複風險;③ (b) 下批次 C 始可大規模推進(C-1/C-2 即採此)。此為批次 C 之最大前提,請優先裁定(詳 CR-029 §6/§8.1) |


6. 建議排序(依「可驗證性」而非「事件編號」)

批次 A|無爭議、可立即做(不依賴 §5 任何裁定)

順序 內容 消費者就緒 真機驗證方式
A-1 payment.payment.posted.v1(payment 生產)→ limit-service 消費:恢復可用額度 → ✅ 已完成(#A-124,2026-10-01) 🟢 有 ✅ 繳款入帳 → account_limits.used_amount 500,000 → 300,000;冪等/亂序/溢繳下限皆通過(見 SCS-002 §7.7)
A-2 authorization…approved.v1 之消費端:notification-service 建立通知 → ✅ 已完成(#A-125,2026-10-01) 🟢 有 ✅ 授權核准 → 通知 NOTIF202610000003 建立(AUTH_APPROVED、SENT、4 通道);冪等/亂序皆通過(見 SCS-016 §7.9)
A-3 生產端補齊(card.card.issued/token.bound——已接 outbox 之服務,補事件即可) → ✅ 已完成(#A-126,2026-10-01) — ✅ 發卡 → cc.card.card.issued;綁 Token → cc.card.card.token.bound;payload 與 SDD-006 §4.2 逐欄一致(見 SCS-003 §7.11/§7.12)

批次 A 三項全數完成(A-1 #A-124/A-2 #A-125/A-3 #A-126,2026-10-01)。
實作中共發現 4 項契約缺口(§5 #9~#12),已一併登錄。
下一步:批次 B(hold 群)動工前必須先裁定 §5 #1~#4。

批次 B|✅ 已完成(2026-10-01,#A-128)

limit.hold.created.v1/limit.hold.released.v1 → auth 本地 limit_holds 投影
(V2 已載明之第三個「無餵養者」讀模型)。
真機:正常路徑 → 本地 8 → 9(id=權威 holdId);stand-in 去重負樣本 → 本地仍 1 列。
★ 併修正 JdbcLimitPort.availableLimit 之超額風險缺陷。

批次 A-4|生產端補齊(續)——BEAM PMO 2026-10-01 指示「先補批次 A 剩下的生產端」

範圍:批次 A-3 只補了「已接 outbox 之服務」(card/limit)。本批補其餘服務之生產端。

掃描結果(2026-10-01 實測):

類 服務 outbox 表/埠/工廠
已接(4) authorization/card/limit/payment ✅/✅/✅
未接(13) clearing/dispute/installment/merchant/migration/notification/party/posting/pricing/product/report/rewards/statement —/—/—

待補事件清單(依 SAD-002 §3):

# 服務 事件 payload(SDD-006 §4.2) 備註
1 product product.parameter.changed.v1 ✅ 已定義(namespace/paramKey/effectiveFrom) ✅ 已完成(#A-130)——真機驗證通過;消費端(全體服務)未實作
2 product product.product.published.v1 ⬜ 缺 需先立 payload
3 party party.account.created.v1/party.block-code.changed.v1/party.flag.applied.v1 ⬜ 缺(3 者皆缺) 需先立 payload
4 posting posting.transaction.posted.v1 ✅ 已定義(含 subAccountEntries[]) ✅ 已完成(#A-134)——真機驗證通過(入帳 → cc.posting.transaction.posted)
5 posting posting.transaction.reversed.v1 ✅ 已定義 ✅ 已完成(#A-134)——真機驗證通過(沖正 → cc.posting.transaction.reversed)
6 posting posting.balance.changed.v1 ⬜ 缺 需先立 payload
7 payment payment.payment.reversed.v1 ⬜ 缺 payment 已接 outbox,僅需補事件
8 limit limit.special-limit.set.v1 ⬜ 缺 ⚠️ 無生產端程式點——SCS-002 §10 #2 載「SPECIAL 調整類型未支援」→ 需先實作該功能
9 statement statement.statement.issued.v1 ✅ 已定義 ✅ 已完成(#A-135)——真機驗證通過(出帳 → cc.statement.statement.issued;冪等重跑不重複發射)
10 installment installment.plan.created.v1 ✅ 已定義 掛點:建立分期
11 installment installment.period.settled.v1 ⬜ 缺 需先立 payload
12 rewards rewards.points.accrued.v1/rewards.points.redeemed.v1 ✅ 已定義(2 者) ✅ 已完成(#A-136)——真機驗證通過(入點/兌換 → 兩主題);併負樣本:退款(ADJUST+負值)不發射、冪等重播不重複發射
13 clearing clearing.batch.matched.v1 ✅ 已定義 ✅ 已完成(#A-131 實作 + 本批補驗)——真機驗證通過(cc.clearing.batch.matched)。⚠️ #A-131 原記「受 ACL 阻擋」之根因已更正:實為本機 SSO 開啟(CARDCORE_SECURITY_ENABLED=true)→ 無權杖即 401 CC-PLT-4010(非 ACL 之 403)。見 §5 #15 之附註
14 dispute dispute.case.opened.v1 ✅ 已定義 ✅ 已完成(#A-133)——真機驗證通過(cc.dispute.case.opened)。★ 實作時由單測攔下一項語意缺陷:disputeType 原誤取 DISPUTE_TYPE(程序類型,恆為 INQUIRY),應取 DISPUTE_REASON(爭議原因)
15 dispute dispute.case.closed.v1 ✅ 已定義 ⛔ 不發射:payload 含 resolution/adjustAmount,惟 Dispute 聚合無該二欄位且無結案路徑可達 → 不臆造(見 SCS-012 §10 #8)
16 merchant merchant.settlement.submitted.v1 ✅ 已定義 ✅ 已完成(#A-137)——真機驗證通過(cc.merchant.settlement.submitted);併負樣本:冪等重播不發射、淨額為負之 EXCEPTION 仍發射
17 merchant merchant.merchant.registered.v1 ⬜ 缺 需先立 payload
18 notification notification.sent.v1 ✅ 已定義 ✅ 已完成(#A-138)——真機驗證通過(cc.notification.sent);併負樣本:QUEUED/FAILED/受理階段皆不發射
19 report report.job.completed.v1 ⬜ 缺 需先立 payload
20 migration migration.job.completed.v1 ⬜ 缺 需先立 payload
21 pricing pricing.interest.calculated.v1 ⬜ 缺 需先立 payload

批次 A-4 進度小結(2026-10-01):payload 已定義者(8 項)全數完成——
posting×2(#A-134)/statement(#A-135)/rewards×2(#A-136)/clearing(#A-131+補驗)/
dispute(#A-133)/merchant(#A-137)/notification(#A-138);
另加 product(#A-130)= 9 項事件/7 服務。
餘 12 項皆為「payload 未定義」(需先立契約)+ dispute.case.closed.v1(待裁定)。

每個服務之工作量(複製型):V?_outbox.sql + OutboxPort + <X>EventFactory +
JdbcOutboxRepository + {KafkaEventPublisher, OutboxRelayConfig, OutboxRelayScheduler} +
掛點(同交易寫 outbox)+ @EnableScheduling + application.yml 值序列化改 StringSerializer + 單測 + 真機。

⚠️ 順序建議:先做 payload 已定義者(免立規格);limit.special-limit.set.v1 最後做
——它缺的不只是事件,是整個 SPECIAL 調整功能(SCS-002 §10 #2),屬功能開發而非事件推廣。

⚠️ 與 §7「不建議一次補完生產端」之張力(誠實揭露):§7 之顧慮是「有人發、沒人收」。
BEAM PMO 2026-10-01 裁定「先補生產端,再批次 C」——本 CR 依此執行,但每一批仍須真機驗證
(outbox 落列 + relay 發布 + 主題建立),並在規格如實標記「目前無消費者」,避免誤認為已全鏈路。

批次 C|消費端 → ★ 已立計劃(CR-029)

2026-10-01 更新:本批之盤點與分批計畫已獨立為 CR-029
(《事件流(W-2)批次 C:消費端盤點與讀模型設計建議》)——原因:本批之障礙不是技術,
而是**「目標讀模型/行為尚未被規格定義」**,需逐條判定而非照單全做。

CR-029 之結論(摘要):

  • SAD-004 §5.2 之 22 條消費邊 + SAD-002 §3 獨有 3 條,逐條判定「語意來源/目標模型/payload/同步冗餘」。
  • SAD-003 §4.1 之 7 個 q_* 全部是「自投影」(來源事件與存放服務同一)→ 不在批次 C 之範圍(屬 §5 #6)。
  • 僅 2 條可立即動工(C-1/C-2:payment.posted/statement.issued → notification)
    ——其語意來源為既有種子模板(PAYMENT_RECEIVED/STATEMENT_ISSUED),非臆造。
  • ★ 揭露架構性問題:3 條消費邊所描述的行為已由既有同步呼叫完成
    (auth→limit 佔用/payment→posting 入帳/statement→posting 查詢)→ 並存即重複套用,
    須先裁定「事件是唯一路徑,抑或同步為主、事件僅供投影/通知」。
  • 其餘分為 C-3(payload 缺口)/C-4(冗餘同步路徑)/C-5(語意未定義)/
    C-6(無模板)/C-7(範圍大)/C-8(待生產端)/C-9(清單權威未定)。

已完成(#A-141):C-1/C-2——payment.payment.posted.v1 → notification(PAYMENT_RECEIVED)、
statement.statement.issued.v1 → notification(STATEMENT_ISSUED);真機驗證通過。
★ 前置契約變更:兩事件 payload 原缺 currency 而模板需要 → 依 SAD-004 §5.3 補為可選(不升版)。

已完成(#A-143):C-5 之首項——party 之卡片狀態投影(card.card.status-changed.v1 → CC_PARTY.card_status_caches;
讀取端點 GET /api/v1/accounts/{accountId}/cards)。★ 架構上之唯一解:card-service 之 ACL 只允許 cc-authorization-service
→ party 無法同步取得卡片狀態 → 事件投影是唯一途徑。真機驗證 8 項通過(含兩負樣本)。
詳 CR-031(v1.1)/SCS-004 §7.5。⚠️ C-5 其餘條目仍待逐條立讀模型規格。

裁定暫緩(#A-143):C-7(參數快取)——實測 9 個可呼叫 product 之服務全部沒有參數快取,
且參數化自載「已登記為後續項」→ 無快取可失效 = 建監聽器即假綠。設計已立(CR-031 §3),
參數化一落地即 drop-in。★ 併更正 SAD-004 §5.2:該事件之消費者「全體」實為「可呼叫 product 之 9 服務」(ACL 所限)。

原載之範圍(保留供對照):posting.transaction.posted.v1(statement/rewards/limit 之跨服務投影)、
statement.statement.issued.v1(payment)、installment.plan.created.v1(limit)、
clearing.batch.matched.v1、dispute.case.opened.v1、rewards.points.*、product.parameter.changed.v1。

批次 D|暫緩

notification.sent.v1(發送通道為真阻塞,GOV-006 §3.D);report.job.completed.v1/migration.job.completed.v1(無消費者)。


7. 明確不建議做的事(避免假綠)

  1. 不要為了「打通」而新建沒有語意來源的表——例如為 dispute.case.opened.v1 在 posting 造一張表。先有規格、才有表(GOV-003 條件 9)。
  2. 不要實作 SAD-003 §4.1 的 q_* 自投影(除非 #6 裁定為跨服務投影)——會與權威表並存成兩套真相。
  3. 不要一次補完 13 個生產端再回頭做消費端——那會產出 13 個「有人發、沒人收」的事件(Kafka 主題堆積、無人驗證)。建議生產+消費成對交付(如批次 A-1)。
  4. 不要動 REFERRAL 的行為(§5 #8)——待裁定。

8. 請求裁定事項(給 BEAM PMO)

  1. §5 #1~#4(hold 事件群):請擇一;建議 #1(a)+#2(a)+#3(併)+#4(b)。
  2. §5 #5(事件清單權威):建議 (c) 合併為單一清單。
  3. §5 #6(q_* 語意):建議改寫為跨服務投影,否則廢除。
  4. §5 #7(declined.v1):建議 (b) 不納入(除非客戶要求拒絕率即時看板)。
  5. §5 #8(REFERRAL):建議 (b)。
  6. 是否同意「先做批次 A-1(payment.payment.posted.v1 生產+消費)」——該條不依賴任何裁定。
  7. §5 #15(Kafka 主題命名不一致):建議 (a) 對齊文件慣例——4 服務各改 1 個常數
    (product/clearing/dispute/posting),主題由「多一層重複」改為文件所示之形狀;
    現階段零消費者,成本極低。⚠️ 本批已依 (a) 實作 4 個新服務,故新舊目前不一致。
  8. ★ §5 #16(事件 vs 同步呼叫之重複)—— 請【優先】裁定:建議 (b)「同步為主、事件為輔」——事件僅供投影/通知,不重做同步已做之寫入;SAD-004 §5.2 之「用途」欄須改寫以排除「重做寫入」之語意。理由:① 授權/繳款之即時性使同步不可去;② 事件驅動之價值在跨服務投影與通知,重做寫入無增益且引入重複套用風險;③ (b) 下批次 C 始可大規模推進(C-1/C-2 即採此)。詳 CR-029 §6。
  9. §5 #5(清單權威)與 #6(q_* 語意)—— 請一併裁定:二者直接決定批次 C 之範圍(#5 決定「哪些消費邊存在」;#6 決定「7 個自投影是否要做」)。詳 CR-029 §3/§8.5。
  10. §5 #10 之追認:CR-029 已依建議 (b) 為 payment.posted.v1 及 statement.issued.v1 補可選 currency(不升版),請追認或指示改為必填/升版。
  11. CR-029 §5 C-6(無模板之通知):rewards.points.*/dispute.case.opened 之通知是否要做?若要做,模板(業務文案)由誰提供?(不應由開發自行編寫)

9. 修訂記錄

版本 說明
1.0 初版:W-2 事件流推廣盤點(生產端 13 服務就緒度、消費端就緒度、8 項文件矛盾、四批次排序建議)
1.1 依 BEAM PMO 同意執行 批次 A-1(payment.payment.posted.v1 生產+消費,#A-124)——①§1 現況更新(6 條已處理:3 全鏈路 + 3 僅生產端);②§6 批次 A-1 標記完成(真機:used_amount 500,000 → 300,000;冪等/亂序/溢繳下限皆通過);③實作中再發現 2 項契約缺口 → 新增 §5 #9(UNMATCHED/REVERSED 事件未實作,額度不會被重新佔用)、§5 #10(posted.v1 缺 currency,消費端無法精確定位額度主檔);④狀態改為「批次 A-1 完成;10 項待裁定」
1.2 依 BEAM PMO 指示續做 A-2/A-3(#A-125/#A-126)——①§1 現況更新(8 條已處理:4 全鏈路/含一消費端 + 4 僅生產端);②§6 批次 A 全數標記完成(A-2 真機:授權核准 → 通知建立、AUTH_APPROVED、4 通道、冪等/亂序通過;A-3 真機:發卡/綁 Token → 兩主題自動建立、payload 逐欄一致);③再發現 2 項 → §5 #11(approved.v1 主動擴充 2 個可選欄位 cardNoMasked/merchantId;舊事件將因變數不足而略過)、§5 #12(card.issued/token.bound 之 sequenceNo 與 status-changed 不同源,同聚合可能交錯);④狀態改為「批次 A 完成;12 項待裁定」
1.3 依 BEAM PMO 指示「hold 事件群四項矛盾請一一化解」→ 立 CR-028(先取三項客觀證據,再逐項定案)——①**§5 #1~#4 標記為「已化解」(無需 BEAM PMO 裁定):廢除 authorization.hold.*、auth 訂閱 limit.hold.* 兩者、limit.hold.created.v1 補 authorizationId(可選);②§6 批次 B 之規格障礙標記為「已解除」(藍圖見 CR-028 §6);③新增 §5 #13**——SDD-006 §4.2 之 card.card.status-changed.v1 payload 與實作不符(已依實作更正,列待覆核);④標題由「八項」更正為「十二項」(原標題漏隨 #9~#12 更新);⑤狀態改為「#5~#13(九項)待裁定」
1.4 依 BEAM PMO 指示「批次 B 仍需嚴謹的執行,請動工吧」(#A-128)——①§1 現況更新(8 條已處理:5 全鏈路 + 3 僅生產端);②§6 批次 B 標記完成;③新增 §5 #14——limit.hold.created.v1 payload 缺 currency(CC_AUTH.LIMIT_HOLDS.currency 為 NOT NULL;已補為可選,與 #10 同型);④★ 併載「動工前查證攔下之關鍵風險」:stand-in 後送會建立權威 hold → 若以 holdId 盲目插入將重複扣減可用額度 → 定案以 authorizationId 去重(真機以負樣本驗證);⑤狀態改為「批次 A/B 完成;#5~#14(十項)待裁定」
1.5 依 BEAM PMO 指示「繼續批次 A-4,從 payload 已定義的服務下手(posting/statement/rewards/clearing/dispute/merchant/notification),每個都要比照本次:規格先行 → 實作 → 真機驗證」(#A-133~#A-138)——①**§6 批次 A-4 表全面更新**:payload 已定義之 9 項事件全數完成(posting×2/statement/rewards×2/clearing/dispute/merchant/notification);併刪除原重複之第 14 列(dispute 原誤列兩次);②新增 §5 #15——★ Kafka 主題命名不一致(4 服務寫入「完整名」致主題多一層重複,與 SAD-004 §5.4 示例及 SCS-002/SCS-003 既有記載不符);本批新服務已依文件慣例實作 → 新舊不一致,待裁定對齊方向;③★ 附註更正 #A-131 之根因——原記「受服務間 ACL 阻擋」,實為本機 SSO 開啟(CARDCORE_SECURITY_ENABLED=true)→ 401 CC-PLT-4010(非 ACL 之 403);以合法使用者權杖重跑即通,clearing 已補驗完成;④**§8 新增第 7 項請求裁定**(#15);⑤實作中由單測攔下 1 項語意缺陷(dispute 之 disputeType 誤取程序類型軸);⑥狀態改為「批次 A/B/A-4(payload 已定義者)完成;#5~#15(十一項)待裁定」
1.6 依 BEAM PMO 指示「繼續批次 C(消費端),一樣擬定計劃、規格先行、任務追蹤清單缺一不可」(#A-141)——①新增 §6 批次 C 之計劃摘要並指向新建之 CR-029(消費端盤點與讀模型設計建議);②**§5 新增 #16**——★ 事件 vs 同步呼叫之重複(3 條消費邊所描述的行為已由既有同步呼叫完成,並存即重複套用);③**§5 #10 標記「已依 (b) 執行」(payment.posted.v1 + statement.issued.v1 補可選** currency)並請求追認;④**§8 新增第 8~11 項請求裁定**(#16 優先/#5+#6/#10 追認/無模板之通知);⑤狀態改為「批次 C 🟡(計劃已立;C-1/C-2 完成);#5~#16(十二項)待裁定」
1.7 依 BEAM PMO 指示「以 Best Practice 直接將這些問題解決」(#A-142)——★ §5 之 12 項全數裁定,改由架構師依業界實務決定(不再逐項回問):立 CR-030(含 4 條通則 P1~P4 作為方法論)。★ 其中 2 項為實證之真缺陷:#8(REFERRAL 已佔額度卻不發射事件——AuthorizeUseCase 之 willApprove 證實佔用、發射條件卻以 isApproved() 排除)、#9(UNMATCHED/沖正撤銷額度恢復卻未告知 limit → 可用額度永久高估)。★ #16 連帶收斂 CR-029 批次 C 之範圍(4 條改判「不做」)。★ #5 併補登 card.issued/token.bound/merchant.settlement.submitted/notification.sent 至權威清單。§5 上方新增裁定總表(12 項 × 裁定 × 執行狀態);另列 4 項留待外部輸入(業務文案/卡組織規範/聯絡資訊/累點規則)
1.8 依 BEAM PMO 指示「批次 C 剩下的(party 卡片狀態投影、各服務參數快取)都需先立讀模型規格,我可以直接依 CR-030 的通則自行設計並推進」(#A-143)——①**§6 批次 C 標記 C-5 首項完成**:party 之卡片狀態投影(card.card.status-changed.v1 → CC_PARTY.card_status_caches;端點 GET /api/v1/accounts/{accountId}/cards)——★ 架構上之唯一解(card-service 之 ACL 只允許 cc-authorization-service → party 無法同步取得卡片狀態);真機驗證 8 項通過(含兩負樣本:重複 eventId/較舊 sequenceNo)。②**C-7(參數快取)標記「裁定暫緩」**——實測 9 個可呼叫 product 之服務全部沒有參數快取,且參數化自載「已登記為後續項」→ 無快取可失效 = 建監聽器即假綠;設計已立(CR-031 §3),參數化落地即 drop-in。詳 CR-031(v1.1)。③★ 併更正 SAD-004 §5.2:product.parameter.changed.v1 之消費者原載「全體」,惟 ACL 只允許 9 個服務呼叫 product → 更正為「可呼叫 product 之 9 服務」
    (1-1/1)