# SCS-026　行銷活動（Campaign）模型程式設計規格

| 項目 | 內容 |
|---|---|
| 文件編號 | SCS-026 |
| 版本 | **3.9**（★ **`CR-049` 真機缺陷修正**：① 回沖業務日——`reversed.v1` payload 增可選 `postingDate`（**不升版**）＋`refundCommandJson` 帶 `postingDate`＋rewards 缺值 **fail-closed 略過**（不以 `occurredAt` 反推，`§7.5①`）；② 回沖欄位層級一致化——消費端一律「`recognition` 優先、`payload` 次之」，回沖判定以 **`txType=REFUND` 為權威**（原僅讀 `payload.refund` → 恆 `false` → **把退款當入點**）；③ 線格式——producer `value-serializer` 必須 `StringSerializer`（`JsonSerializer` 二次編碼 → `eventType=null` 靜默略過），已納入架構關卡 **A9**。★ **真機 E2E 32/32 通過**（載體無關／比率＋上限截斷／回沖）。前版 **3.8**（★ **§7.16 排除項參數化實作完成**：基準僅 `PURCHASE`（與原行為一致）＋`exclusion_json` 之 `includeTxTypes`／`excludedMccs` 覆寫；★ BEAM PMO 裁定「未宣告 `clawback_rule` → 不回沖」已凍結。前版 **3.7**（★ **§7.15 疊加 D9 與優先序實作完成**：`campaigns.priority`（`V7`；越小越優先，預設 100）＋`NON_STACKABLE` 擇一（取 priority 最小並排除其他）／全 `STACKABLE` 全套用；契約加 `priority`。前版 **3.6**（★ **§7.14 退款／沖正回沖實作完成**：`posting.transaction.reversed.v1` 消費 → 以 `reversalOf` 反查原指令（`JSON_VALUE`）→ `CLAW_BACK` 才回沖（未宣告＝不回沖，保守不扣客戶）→ 回沖指令（`refund:true`）＋ cap 用量退還（`GREATEST`）→ rewards 既有退款分支；`ledger` 活動歸屬寫入）。前版 **3.5**（★ **R-3b 定案並實作（BEAM PMO 裁定採 (a)）**：`PointsAccountView.tierRank`（序之權威在 rewards，`QUALIFICATION_SPEND` 升冪；停用不佔序；查無 null）＋條件鍵改 **`minLevelRank`**（數值序——★ 更貼近 BRD §11「Level 2 起 3%」原文）＋`TierLookupPort.TierInfo`；campaign `RateResolverTest` 7 例＋rewards 真機 2 例（序／停用排除）全綠）。前版 **3.4**（★ **§7.13 條件式比率凍結定案並實作**：R-1② `campaign_plans.benefit_rate_json` 承載方案權益率／R-2 生日權威＝客戶主檔（外部依賴，已盤點本地無）／R-3 rewards 查詢 API＋`SAD-002 §5` ACL 加 campaign／R-4 授權側新增結構化 `pay_path`（**不接受 UNKNOWN**）／R-5 `rateReason` 稽核留痕；條件 schema 四鍵 AND）。前版 **3.3**（★ **§7.12 新增「條件式比率待凍結清單 R-1～R-5」**——供 BEAM PMO 逐條裁定；每條含 BRD 依據／**實查現況**（R-1 表已存在／R-2 生日來源未備／R-3 Level 在 rewards 且無 ACL／R-4 授權側無結構化 wallet／R-5 稽核留痕）／待裁定項／凍結後落點。裁定前有條件檔維持 fail-closed）。前版 **3.2**（★ **§7.11 活動回饋率接線＋精確上限截斷（真機通過）**：無條件基準檔判定（BRD 例 1 之 0.5%）→ `applicableRate` 隨指令下發 → rewards 依比率計點；**有條件檔 fail-closed**（語意未凍結）；精確截斷＝「已用量＋推定價值>上限」；Oracle JSON 欄比較陷阱（Java 端判定）。campaign 46 例／rewards 159 例全綠）。前版 **3.1**（★ **`CR-047` cap 定案並實作（真機通過）**：定義／用量兩表分工＋通用 scope/period 維度＋回饋價值（分）記帳＋點值商品參數（兩側皆參數）＋事前截斷／事後回填；`PER_STATEMENT`／`PER_HOUSEHOLD_MEMBER` fail-closed；§3 #17 補 `PER_HOUSEHOLD_MEMBER`。campaign 43 例／rewards 158 例全綠）。前版 **3.0**（★ **§7.9 cap 設計提案＋錄取查詢端點＋AC 11 載體無關性驗證**：① cap 兩落差實查（`campaign_caps` 無 household 維度／`cap_value_minor` vs 點數換算）——**待 BEAM PMO 裁定後實作**，不臆造；② 新端點 `GET /campaigns/{code}/enrollments/{householdId}`（含 `accruedAmountMinor`，契約重生 **106 endpoints**）；③ 載體無關性服務內判定力測試（同戶實體＋虛擬跨載體累計合併→一次性指令）。campaign 全測 **40 例** BUILD SUCCESSFUL）。前版 **2.9**（`CR-046`：新增 **§7.5 活動認列輸入契約**（設計提案，待 BEAM PMO 核定）——認列來源唯一化、欄位權威映射、**歸戶語意更正**（household_id ≡ accountId，權威 `CC_PARTY.ACCOUNTS`）、payPath 權責邊界（認定器屬 campaign、認定規則待 BEAM PMO 核定）、AC 11 驗收前置條件）。前版 **2.3**（★ 依 **`BRD-005`**（客戶提供之台灣信用卡行銷活動 BRD＋Business Spec，v1.3，2026-10-16）全面改寫；前版 1.0 僅基於 BEAM PMO 口述六項政策。★ **2026-10-16 BEAM PMO 核定**） |
| 狀態 | **★ 已核定（BEAM PMO 2026-10-16）**——據以執行 `CR-046`（`campaign-service` 新建＋BR-16 載體抽象同批上線） |
| 對應需求 | **`BRD-005` BR-01～BR-16（16 條）**、`CR-045` D3（行銷活動軸治理） |
| 權威來源 | **`BRD-005`**（客戶業務單位開立之需求，白紙黑字，**取代**口述政策成為權威語意） |
| 關聯文件 | `BRD-005`、`CR-045`、`SCS-005`（產品）、`SCS-006`（費率／年費）、`SCS-010`（紅利點數）、`SCS-013`（清算）、`SCS-023`（商品組合）、`SDD-001 §3`、`SDD-005`、`SDD-006` |

> **本版差異（2.0）**：★ **從「六項口述政策」升級為「BRD 16 條需求驅動」**。
> 前版 1.0 只涵蓋 BEAM PMO 2026-10-15 口述六項（年費/兌換/滿額禮/疊加/回沖/預算）；
> BRD 揭示的真實範圍遠大於此——**九大維度、18+ 參數化要素、10 個新實體、
> 9 個信貸結構差異、12 條驗收標準、10 個驗算範例**。本版依 BRD 重新界定
> **參數化邊界、實體模型、服務權責分工與驗收基準**。
>
> **⚠️ 核心設計命題（BRD §11）**：「每檔活動由**參數化要素**組成，**禁止以程式碼寫死**」。
> 本規格的成敗判準只有一句：**「上架新檔只需填參數，不需改程式」**（BRD 驗收標準 1）。

---

## 1. 對應需求（BRD-005 全 16 條）

| BR | 名稱 | 優先 | cardcore 承接服務（權責） |
|---|---|---|---|
| BR-01 | 活動規則模型 | P0 | **`campaign-service`（新建）** |
| BR-02 | 交易認列與排除引擎 | P0 | `campaign-service`（規則）＋ `posting-service`（事件供給） |
| BR-03 | 回饋計算與上限截斷 | P0 | `campaign-service`（**規則判定**：門檻／資格／疊加／上限之「算不算」——依 `CR-045` D3 釐清）＋ `rewards-service`（點數計算與帳本） |
| BR-04 | 點數帳本（累積/效期/兌換/回沖） | P0 | **`rewards-service`（權威，`SCS-010`）** |
| BR-05 | 正附卡歸戶與合併 | P0 | `card-service`（歸戶主檔）＋ `campaign-service`（規則參數） |
| BR-06 | 錄取（登錄）管理 | P1 | **`campaign-service`**（名額原子控管＋波次） |
| BR-07 | 新戶首刷禮/滿額禮 | P1 | `campaign-service`（門檻）＋ `rewards-service`（發放） |
| BR-08 | 年費減免引擎 | P1 | **`pricing-service`（權威，`SCS-006`）** |
| BR-09 | 額度事件（調額邀約） | P2 | `limit-service` + `card-service` |
| BR-10 | 卡種/卡組織規則包 | P1 | `product-service`（`SCS-005`/`SCS-023`） |
| BR-11 | 報表與成本稽核 | P1 | `report-service`（`SCS-017`） |
| BR-12 | 方案與任務（Plan/Quest）引擎 | P1 | **`campaign-service`** |
| BR-13 | 支付路徑認定（Pay Path Classifier） | P0 | **`posting-service` 或 `campaign-service`（認定器，規則表驅動）** |
| BR-14 | 通路插件掛載（Channel Adapter Registry） | P0 | **`product-service` 或獨立 `channel-service`** |
| BR-15 | 信貸促銷引擎（利率/費用型回饋） | P1 | `pricing-service`（利率）＋ `campaign-service`（活動） |
| BR-16 | 載體抽象（實體卡/虛擬卡號/Token） | P0 | **`card-service`（INSTRUMENT 主檔）**＋全服務交易鍵升級 |

> ⭐ **權責原則（沿用 `CR-045` D3 治理）**：**計算權威留原服務**（rewards 管點數帳本、pricing 管年費與利率、limit 管額度）；
> `campaign-service` **只做「活動定義、資格、疊加、上限、歸戶單位、錄取、指令下發」**，
> **不接管計算、不持有點數餘額、不定義利率**。這是「最大參數化」不變成「巨型泛型表」的關鍵紀律。

---

## 2. 服務職責與邊界

### 2.1 `campaign-service` 承作（活動引擎）
- 活動主檔與**版本化**（同 `product-service` 參數版控模式：`effective_from`/`effective_to`）
- **BR-01 全要素**之儲存、驗證與版本鏈
- 資格判定（卡別/卡種/客群/新戶定義/正附卡/歸戶單位）
- 疊加互斥（`stackable`＋`exclusion_group`＋`priority`）
- 上限截斷（每卡/每戶/每期/每月/每年/每歸戶）
- 錄取管理（自動/登錄、波次、名額原子控管、規則型拒絕碼）
- 方案（Plan）與任務（Quest）引擎
- **指令下發**：呼叫 rewards（發點）、pricing（年費/利率）、limit（調額）、statement（帳單列示）

### 2.2 不做（屬他服務，計算權威）
| 不做 | 屬誰 | 依據 |
|---|---|---|
| 點數累積/效期/兌換/回沖/負點 | `rewards-service` | BR-04、`SCS-010` |
| 年費判定與折抵 | `pricing-service` | BR-08、`SCS-006` |
| 利率減碼與試算 | `pricing-service` | BR-15、`SCS-006` |
| 額度佔用/臨調 | `limit-service` | BR-09、`SCS-002` |
| 交易入帳、分錄、請款 | `posting-service` | BRD §3.2 反列 |
| 卡別/卡種/卡組織主檔 | `product-service` | BR-10、`SCS-005` |
| 歸戶／正附卡主檔 | `card-service` | BR-05、`SCS-003` |
| 帳單產出與列示 | `statement-service` | BR-02 消費到回饋時序 |

---

## 3. BR-01 參數化要素全表（★ 本規格之主契約）

> **鐵則**：下表每一欄**都是資料**，不是程式分支。新增活動 = 新增一列，**不改程式、不需發版**。

| # | 要素 | BRD 出處 | 型別 | 說明與取值 |
|---|---|---|---|---|
| 1 | `campaign_code` | §11 | string PK | 活動代碼（唯一） |
| 2 | `activity_period` | §11 | 區間 | 起迄日（含） |
| 3 | `apply_period` | §11（信貸 D1） | 區間? | **申請期間**（信貸側，與撥款截止分離） |
| 4 | `disburse_deadline` | §11（D1） | date? | **撥款截止日**（常跨年） |
| 5 | `recognition_basis` | §11 | enum | `TXN_DATE`｜`POST_DATE`｜`APPLY_AND_DISBURSE`（雙錨點） |
| 6 | `eligibility` | §11 | JSON | 卡別/新戶/等級/正附卡；信貸側「全新＝往來未達 6 個月（卡/貸/房貸合計）」 |
| 7 | `enroll_mode` | §11 | enum | `AUTO`｜`REGISTER` |
| 8 | `enroll_windows` | §11 | JSON[] | 波次（起迄＋名額）；JCB 型 1/11/21 三波 |
| 9 | `enroll_quota` | §11 | int? | 名額；**原子控管**，禁超發 |
| 10 | `attribution` | §11（D3） | JSON? | 進件歸因：廣宣連結/專屬代碼/專員欄位/第三方專頁 |
| 11 | `channel_condition` | §11 | JSON[] | 指定通路清單（MCC 或 `channel_id`） |
| 12 | `threshold` | §11 | JSON | 單筆/累計金額、次數；多檔門檻（SOGO 型） |
| 13 | `threshold_scope` | §11/例5 | enum | `PER_TXN`｜`ACCUM`｜`PER_STORE_PER_DAY`（每館每日擇一） |
| 14 | `reward_form` | §11 | enum | `CASH`｜`POINTS`｜`MILEAGE`｜`GIFT`｜`LOTTERY`｜`RATE_CUT`｜`FEE_WAIVE` |
| 15 | `rate_schedule` | §11 | JSON[] | 條件式比率：依方案/生日月/Level/支付路徑/期間分層 |
| 16 | `cap` | §11 | JSON[] | 上限組：每卡/每戶/每期/每月/每年/每歸戶 **各自可配** |
| 17 | `cap_unit` | §11/例1 | enum | `PER_CARD`｜`PER_HOUSEHOLD`｜`PER_STATEMENT`｜`PER_MONTH`｜`PER_YEAR`｜**`PER_HOUSEHOLD_MEMBER`**（★ `CR-047 R5` 更正：DDL `V1` 原即六值，本表原漏列） |
| 18 | `exclusions` | §11/§14.3 | JSON[] | 排除項清單；**可按活動覆寫**（加值事件可翻轉為資格來源） |
| 19 | `pay_path_limit` | §11/§6B | enum[] | `PHYSICAL`｜`INTL_3PAY`｜`BANK_OWN`｜`THIRDPARTY`｜不限 |
| 20 | `stacking` | §11/§6C | JSON | `stackable`＋`exclusion_group`＋`priority`＋疊加模式（併行/擇優/互斥） |
| 21 | `payout_schedule` | §11 | string | 當期/次期/N 工作天（AMEX 型 5–150 天） |
| 22 | `household_unit` | §11/§14.1 | enum | `PRIMARY`（歸正卡）｜`CARD`（單卡獨立，JCB 型） |
| 23 | `cost_share` | §11（D4） | JSON[] | 成本承擔方＋比例（銀行/通路/卡組織） |
| 24 | `external_dependency` | §11/§14.4 | JSON[] | 外部事件依賴（票證加值檔/支付歸屬檔/商店代號檔） |
| 25 | `clawback_rule` | §11（例9） | enum | 提前清償回沖：`CLAW_BACK`｜`NO_CLAWBACK` |
| 26 | `quest_rule` | §11（BR-12） | JSON? | 任務進度規則（子任務、集章、抽獎隊伍） |
| 27 | `plan_ref` | §11（BR-12） | FK? | 綁定方案（CUBE 型權益方案） |
| 28 | `draw_pool` | §11（D8） | JSON? | 抽獎獎池（券別×名額×中獎上限） |
| 29 | `status` | §3.3 | enum | 活動生命週期狀態 |
| 30 | `rule_version` | §5 | int | 規則版本（樂觀鎖） |

> ★ **「最大參數化」的判準**：上表 30 欄**全部可經由 API/UI 設定**；
> 任一欄需要改程式才能變，即為參數化缺口，須回報為缺陷（BRD 驗收標準 1）。

---

## 4. 領域模型（設計提案）

### 4.1 聚合根

| 聚合 | 職責 | 不變式 |
|---|---|---|
| **`Campaign`** | 活動定義＋規則版本鏈 | `effective_to >= effective_from`；`stacking` 必填（**無預設值**，BRD D9）；`cap` 至少一筆；`clawback_rule` 於信貸型必填 |
| **`Enrollment`** | 錄取紀錄（BR-06） | 名額不得超發（原子遞減）；同戶同波次唯一；拒絕碼必落 log |
| **`Plan`** | 權益方案（BR-12） | `benefit_rate`＋`level_req`；`valid_from/to` 不可重疊 |
| **`Quest`** | 任務（BR-12） | 進度單調；`prize` 與 `Campaign.cap` 連動 |

### 4.2 值物件與列舉
`RecognitionBasis`、`RewardForm`（7 值）、`CapUnit`（6 值）、`PayPath`（5 值）、
`StackingMode`（`STACKABLE`/`NON_STACKABLE`）、`HouseholdUnit`（`PRIMARY`/`CARD`）、
`ReversalPolicy`（點數里程回沖 vs 贈品打呆帳）、`ClawbackRule`、`CampaignStatus`。

### 4.3 狀態機

```mermaid
stateDiagram-v2
    [*] --> DRAFT
    DRAFT --> ACTIVE: approve
    ACTIVE --> PAUSED: pause
    PAUSED --> ACTIVE: resume
    ACTIVE --> CLOSED: close
    PAUSED --> CLOSED: close
    CLOSED --> [*]
```

### 4.4 **錄取狀態機（BR-06，BRD §12.2 原圖照錄）**

```mermaid
stateDiagram-v2
    [*] --> 未登錄
    未登錄 --> 已登錄 : 錄取成功
    未登錄 --> 錄取額滿 : 名額已滿
    已登錄 --> 資格符合 : 達成消費門檻
    已登錄 --> 已失效 : 逾期或停卡
    資格符合 --> 回饋處理中 : 結帳日計算
    回饋處理中 --> 回饋完成 : 發放入帳
    回饋處理中 --> 部分回沖 : 退款
    回饋完成 --> [*]
    部分回沖 --> 回饋完成 : 差額補正
    已失效 --> [*]
    錄取額滿 --> [*]
```

> ⚠️ **併發硬規格**：名額以原子計數控管（DB 條件更新或 Redis 原子遞減）；
> **禁超發**（BRD 驗收標準 7：100 併發搶 10 名額，成交恰 10）。
> 拒絕碼須落 log 供客訴追溯（US8）。

---

## 5. 資料模型（設計提案，DDL 待補 Flyway）

| 表 | 關鍵欄位 |
|---|---|
| `CC_CPG.CAMPAIGNS` | `campaign_code`(PK)、`activity_from/to`、`apply_from/to`、`disburse_deadline`、`recognition_basis`、`enroll_mode`、`reward_form`、`stacking`(JSON)、`household_unit`、`clawback_rule`、`status`、`rule_version` |
| `CC_CPG.CAMPAIGN_RULES` | `campaign_code`(FK)、`rule_version`、`rule_json`（§3 全 30 欄之**版本快照**） |
| `CC_CPG.ENROLLMENTS` | `campaign_code`、`wave_no`、`household_id`、`instrument_id`、`status`、`enroll_ts`、`quota_seq`、`attribution_code`、`source_channel` |
| `CC_CPG.CAMPAIGN_CAPS` | `campaign_code`、`cap_unit`、`cap_value`、`consumed`（**原子遞減/遞增**） |
| `CC_CPG.PLANS` | `campaign_code`、`plan_code`、`benefit_rate`(JSON)、`level_req`、`valid_from/to` |
| `CC_CPG.QUESTS` | `quest_id`、`campaign_code`、`rule_json`、`progress_json`、`prize` |
| `CC_CPG.PAY_PATHS` | `auth_code`、`path_type`、`wallet_id`、`classified_by`(規則表版本) |
| `CC_CPG.CHANNELS` | `channel_id`、`adapter_type`、`path_type`、`status`（**BR-14 插件掛載**） |
| `CC_CPG.REWARD_CMDS` | `cmd_id`、`campaign_code`、`target_service`、`payload`、`status`（指令下發＋outbox） |

> **載體抽象（BR-16）**：交易最小鍵由 `card_no` 升級為 **`instrument_id`**（實體卡／虛擬卡號／Token 同構）。
> 既有 `CRD_CARD` 等以**視圖對接**，不要求重寫——這是侵入性最高的一項，須單獨評估（見 §8）。

---

## 6. 計算範例即驗收基準（BRD §15 十例）

| 例 | 場景 | 關鍵參數 | 預期 |
|---|---|---|---|
| 1 | 一銀星璨 0.5%＋指定 1%、每戶每期上限 500 | `rate_schedule`×2、`cap=[PER_HOUSEHOLD,PER_STATEMENT,500]` | 300 元刷卡金 |
| 2 | 紅利 20 元 1 點、當期門檻 3,000、效期 2 年 | `rate_schedule`、`threshold`、`exclusions` | 160 點入正卡帳；2 年後清零 |
| 3 | JCB 悠遊加值 10%、月上限 50 | `external_dependency=autoload`、`cap=[PER_CARD,PER_MONTH,50]` | 截斷 50 元 |
| 4 | AMEX 週五滿 2,000 送 100、月上限 200 | `threshold`、`cap` | 截斷 200 元 |
| 5 | SOGO 滿額禮「每館每卡每日擇一」 | `threshold_scope=PER_STORE_PER_DAY`、多檔門檻 | 90,000→1,500；他館 6,000→另計 100 |
| 6 | 富邦分期滿額贈 200,000→10,000、限量 3 | `enroll_quota=3`、`enroll_mode=REGISTER` | 前 3 名得；第 4 名無 |
| 7 | CUBE 樂饗購 3.3% 支付路徑分級 | `pay_path_limit`、`rate_schedule` | 實體 330 點／LINE Pay 30 點／分期 30 點 |
| 8 | 華航鼎尊條件式累哩（生日月 x3） | `rate_schedule` 分層、期間 | 同額差 3 倍；**條件可追溯還原** |
| 9 | eLOAN 首期 0%＋第 2 期減碼＋提前清償 | `reward_form=RATE_CUT`、`clawback_rule` | 依 `clawback_rule` 回沖或 0 元 |
| 10 | Whatever Pay 新通路插件 | `CC_CPG.CHANNELS` 新增一列＋adapter | 不改引擎即納入指定通路 |

> ⭐ **這十例就是我們的自動化驗收案例**（TRR），逐例可在真 Oracle 上以參數建立並驗算。

---

## 7. 關鍵邏輯

### 7.1 活動套用流程（BRD §11 原流程）
```
1 交易認列（recognition_basis）→ 2 排除項檢查（可按活動覆寫）
→ 3 錄取/資格 → 3b 支付路徑認定（BR-13，規則表驅動）
→ 4 門檻判定（單筆/累計/每館每日）→ 5 回饋計算（條件式比率）
→ 6 上限截斷 → 7 入帳/發放 → 8 退款回沖（可生負點）
```

### 7.2 疊加規則（BRD D9：**必填、無預設**）
`stacking ∈ {並行, 擇優, 互斥}` ＋ `exclusion_group` ＋ `priority`。
並行＝雙重回饋；擇優＝取優先序高者；互斥＝同群僅一筆生效。

### 7.3 信貸側九差異（D1–D9）之落地
雙日期（`apply_period`+`disburse_deadline`）、雙錨點（`recognition_basis=APPLY_AND_DISBURSE`）、
進件歸因、利率/費用型回饋、全新客戶定義（跨產品線往來月數）、條件交換
（綁約換手續費、MyData 換開辦費）、動用排他（期間動用筆數檢核）、抽獎（`draw_pool`）、
併用宣告（疊加必填）。

### 7.4 fail-closed
取規則失敗（連線/4xx/5xx）→ **不套用該活動、不臆造放行**，記 WARN 並落拒絕碼（沿用 §7.14 A4 模式）。

### 7.5 活動認列輸入契約（`CR-046` B4；**★ BEAM PMO 2026-10-16 已裁定，見各項標註**）

> ⚠️ 消費端程式於本節②③裁定落定後開工（`GOV-003`）。

**① 認列來源（唯一）**：`posting.transaction.posted.v1`（`SDD-006 §4.2`；入帳為交易之權威落點）。
**不**另訂閱授權事件作認列依據（授權可能不出現請款；認列以 `recognition_basis` 錨定入帳日）。

| 契約欄位 | 事件欄位 | 權威來源 | 語意 |
|---|---|---|---|
| `instrumentId` | `payload.instrumentId` | `CC_CARD.INSTRUMENTS`（載體主檔） | AC 11 之載體最小識別；null＝升級前歷史交易（不參與同構對照） |
| `merchantId` | `payload.merchantId` | 入帳請求（可追溯至授權） | 通路／特店歸因；null 容忍 |
| `mcc` | `payload.mcc` | **授權紀錄**（`CC_AUTH.AUTHORIZATIONS.mcc`） | 指定消費類別判定；null 容忍 |
| `accountId` | `payload.accountId` | `CC_PARTY.ACCOUNTS` | **即歸戶主鍵**（見 ②） |
| `amount`／`currency` | `payload.amount`／`currency` | 入帳交易 | 門檻累計基礎（最小單位：分） |
| `txType`／`postingDate` | `payload.txType`／`postingDate` | 入帳交易 | `REVERSAL`／沖正不計正面累計；認列日以 `postingDate` 為準（不用 `occurredAt` 反推業務日）。★ **本期可認列類別＝僅 `PURCHASE`**（唯一無爭議之消費類別）——分期／退款回沖／其他類別之認列與排除規則（BRD §6B／排除項參數）尚未實作，不臆造；其餘類別略過並記錄（fail-closed） |

**② 歸戶語意（★ BEAM PMO 已裁定：自然人成立；法人經查證同構）**：`ENROLLMENTS.household_id` ＝ **歸戶主鍵 ＝ `accountId`**。
權威：`SCS-004` 歸戶模型——本系統之「歸戶」即 `CC_PARTY.ACCOUNTS`（`Account` 聚合根：歸戶聚合根；
`/api/v1/accounts` 即歸戶 REST；正附卡為同戶之 `parties[]`，共用 `cif_no`）。

> **★ 法人公司戶（BEAM PMO 2026-10-16 提問，已實查）**：`CC_PARTY.ACCOUNTS.account_type` 之 CHECK
> 為 `IN ('PERSONAL','CORPORATE')`（V1 baseline 即有；`AccountType` 枚舉一致）；`BRD-005 §3`
> 持卡人分類亦明列「正卡／附卡／公司戶」→ **法人公司戶與自然人同構**：公司戶本身即一個歸戶
> （一個 `accountId`，`account_type='CORPORATE'`），員工附卡為該歸戶之 `parties[]`。
> 故 `household_id ≡ accountId` 對 **PERSONAL 與 CORPORATE 皆成立**，不需要第二套歸戶鍵。
> 公司戶之活動差異（例：`BRD-005` AMEX 企業卡除外）屬**活動排除參數**（`exclusions`），
> 不是歸戶模型問題。BEAM PMO 已確認自然人部分成立；法人部分依本查證同構適用。
>
> （更正：`#A-253` 曾載「householdId 不得以 accountId 代替」——該判斷係當時未查 party 模型之過度保守，
> 經實查 `SCS-004`／`JdbcAccountRepository` 後更正。`HouseholdUnit` 枚舉仍保留，表達錄取門檻之歸戶單位類型。）

**③ `payPath`（BR-13）：★ BEAM PMO 已同意規則表對照方向；尚有一個輸入缺口**
- 認定器屬 **campaign-service**（規則表 `campaign_pay_paths` 驅動，`auth_code` → `path_type`；fail-closed）。
- **值域有 BRD 依據**：`PHYSICAL`／`INTL_3PAY`／`BANK_OWN`／`THIRDPARTY` 四類即 BRD AC 8
  「實體／三 Pay／自家／第三方四路徑分級」與 BRD §6B 分級事實（DDL CHECK 同值域）。
- **★ 已定位之輸入缺口（實查授權側）**：分類需「wallet 識別」（哪一家三 Pay／第三方條碼），
  惟授權側現僅有自由格式 `emvData` JSON（`V10` 接線之 `entry_mode`／`emv_data`），**無結構化
  wallet 欄位** → 種子規則可依 BRD §6B 立（實體卡＝`PHYSICAL`；`entryMode` 為錢包類且 wallet 屬
  Apple／Google／Samsung Pay＝`INTL_3PAY`；本行自家 Wallet＝`BANK_OWN`；LINE Pay／街口等＝
  `THIRDPARTY`），**wallet 識別之權威欄位須由 authorization 契約補強**（另立增量），補強前
  認定器僅能對 `PHYSICAL`（非錢包 entryMode）作可靠分類，其餘落 `UNKNOWN`——fail-closed，不猜。
- **不在 posting 事件上加 payPath**（分類是活動規則職責，非帳務權威）。

**④ 消費者技術約束（承 `SAD-004 §5.4` 慣例）**：
分區鍵＝`aggregateId`；冪等＝`eventId` 去重（`consumed_events` 型投影）；亂序＝`sequenceNo` 略過；
舊事件缺欄（`instrumentId`／`merchantId`／`mcc` 為 null）**不得**阻斷消費——僅活動參與判定之欄位缺失時落「資料不足」拒絕碼，不臆造。

**⑤ AC 11（B4）驗收前置條件**（全部滿足方可執行真機驗收）：
1. 本節②③裁定已落定（★ 2026-10-16 達成：自然人確認＋法人同構查證；payPath 方向同意）；
2. campaign 消費者＋錄取寫入（`ENROLLMENTS`）＋回饋指令（`REWARD_CMDS`）實作完成且真 Oracle 測試通過（★ 已達成，`§7.6`）；
3. 至少一個活動以真 Oracle 參數建立（BRD §15 之例）；
4. 同戶以實體卡／虛擬卡兩載體產生同額交易，回饋結果逐分一致（Token 路徑依 BRD AC 11 範圍）；
5. wallet 識別欄位之授權側契約補強（③缺口）——僅影響 payPath 維度之活動，不阻擋 AC 11 之載體同構驗證。

### 7.6 指令派發與 rewards 消費（★ 實作完成，真機通過）

**① `REWARD_CMDS` 即指令 outbox**：本表自帶派發狀態機（`PENDING → SENT`，`V1__baseline.sql`），
**不**重複經 `outbox` 表轉運（避免雙表狀態同步）。
`TransactionRecognitionService` 於錄取之**同交易**寫入 `REWARD_CMDS(PENDING)`；
`RewardCommandRelay`（`@Scheduled`，批次 100、間隔 2s，退避由排程週期決定——`CSP-003 §7 E5`）
掃 `PENDING` → 發布 → 成功即標 `SENT`。

**② 事件契約**：主題 `cc.campaign.reward.commanded`；事件 `campaign.reward.commanded.v1`；
分區鍵＝`commandId`（同指令有序）；信封依 `SAD-004 §5.1`；
`payload = {commandId, campaignCode, recognition:{...§7.5① 之事實...}}`。
**`SENT` ＝ 已派發至事件流，非「rewards 已入點」**（消費確認屬 rewards 職責）。

**③ rewards 消費端**（`CampaignRewardCommandListener`，`SCS-010 §7.4`）：
- **計算權威在 rewards**：`points = floor(amount/100) × 帳戶層級 earn_multiplier`（`SCS-010 §7`）——
  指令僅載事實，不含計算結果（`CR-045` D3）。
- **冪等**：`accrue` 以 `POINTS_LEDGER.IDEMPOTENCY_KEY = campcmd:<commandId>` 判定——
  relay 之**至少一次**投遞重放安全；同指令重放回前次結果。
- **到期日錨點**：以 `postingDate`（認列日）起算 `CUR` 效期，不用 `occurredAt`。
- ⚠️ **活動加成比率（BR-01 `rate_schedule`）尚未實作**——本期入點為**標準層級倍率**；
  活動加成不得臆造，加成規則實作前不得宣稱「活動回饋已完整」。

**④ 錄取語意**：認列驅動之錄取寫 `status='REGISTERED'`、`wave_no=0`（本期無波次管理）；
**`QUALIFIED` 及其後狀態須待門檻判定實作**——不得由本鏈路逕行宣告合格。

### 7.7 名額原子控管與登錄 API（`CR-046` AC 7；BRD BR-06「禁超發」）

**① 原子性設計**（`V1__baseline.sql` 之 DDL 註解即本節依據——「以條件更新原子遞減」）：
- `campaign_quotas` 之 `CHECK (quota_used <= quota_total)` 為**最後防線**；
- 應用層採 **`SELECT … FOR UPDATE`（行鎖）＋條件更新**兩段式：讀 `quota_used`（鎖列）→
  檢查 `< quota_total` → `UPDATE SET quota_used = quota_used + 1 WHERE quota_used = <舊值>`；
- `quota_seq` ＝ 更新前之 `quota_used + 1`（第 N 個名額）；波次窗口（`window_from/to` 非空時）
  須涵蓋登錄日；
- **併發安全**：行鎖內無窗口；CHECK 為 DB 層最終防線（應用層破防也擋得住）。

**② 登錄 API**：`POST /api/v1/campaigns/{campaignCode}/enrollments`（僅 `REGISTER` 模式）：
1. 活動不存在 → `CC-CAM-4040`；非 `ACTIVE` 或非 `REGISTER` → `CC-CAM-3001`（AUTO 活動不接受登錄）；
2. **冪等**：同 `(campaign_code, wave_no, household_id)` 已有錄取 → **回前次結果**（`uq_enroll` 自然冪等）；
3. 額滿 → `CC-CAM-1004`（422；語意來自 `SDD-006 §3.4`，BR-06）；
4. 取號成功 → 錄取寫入（`REGISTERED`＋`quota_seq`）；
   併發同戶撞 `uq_enroll` → **退回名額**（`quota_used - 1`）並回前次結果（補償，不得吃掉名額）。

**③ 仍缺（下一增量）**：門檻判定（`REGISTERED → QUALIFIED`）、上限截斷（BR-03）、排除項、疊加計算、
活動加成比率（`rate_schedule`）——本節僅解 AC 7 之「禁超發」與登錄入口。

### 7.15 疊加規則（D9）與活動優先序 ★ 實作完成

> 依據：`BRD-005` D9（`stackingMode` 必填、無預設值——`CC-CAM-1002`）。

**① 優先序參數**：`campaigns.priority NUMBER(4) DEFAULT 100 NOT NULL`（`V7`）——**數值越小越優先**；
未指定為 `100`。實查原表**無此欄**（多個不可疊加活動同時命中時無從擇一）。

**② 判定（`TransactionRecognitionService`）**：
1. 取「期間內且 `ACTIVE`」之活動；
2. 其中若有 **`NON_STACKABLE`** → **擇一**：取 `priority` **最小**者，
   **並排除其他所有活動**（含 `STACKABLE`——`NON_STACKABLE` ＝不得與任何活動併用）；
3. 全為 `STACKABLE` → **全部套用**（疊加）。

**③ 契約**：`CreateCampaignRequest.priority`（可選）／`CampaignView.priority`。

### 7.16 排除項（參數化）★ 實作完成

> 依據：`BRD-005 §11`「排除項：預借現金、年費、稅、學費、小額支付平台、全聯、分期特店、退款…」；
> 「排除項清單**可按活動覆寫**（含加值事件翻轉）」。

**① 基準（未覆寫時）**：**僅 `PURCHASE` 納入**——其餘 `txType`（`CASH_ADVANCE`／`FEE`／`INTEREST`／
`PAYMENT`／`REFUND`／`REVERSAL`／`ADJUSTMENT`／`CHARGEBACK`／`REWARD_REDEEM`／`INSTALLMENT`）皆排除。
此與 `§7.5①` 之原行為**一致**（原即只認 PURCHASE），故無行為變更風險。

**② 活動覆寫（`campaign_exclusions.exclusion_json`，本節定案 schema）**：

| 鍵 | 語意 | 例 |
|---|---|---|
| `includeTxTypes` | **納入**之額外交易類別（覆寫基準；BRD「可按活動覆寫／翻轉」） | `["INSTALLMENT"]` |
| `excludedMccs` | **排除**之 MCC（活動自訂；代碼權威在收單側，由活動定義者填入） | `["5942","8211"]` |

- 判定：`txType ∈ {PURCHASE} ∪ includeTxTypes` **且** `mcc ∉ excludedMccs` → 認列；否則略過（記 INFO）。
- **mcc 缺席**（舊事件／非授權來源）：僅在活動宣告 `excludedMccs` 時無法判定 → **fail-closed 略過**
  （不臆造）；未宣告 `excludedMccs` 則不受影響。
- ⚠️ **本節 schema 為工程定案**（`BRD-005` 未給 `exclusion_json` 欄名）；如與客戶既有參數契約不符，
  以 `CR` 調整。

### 7.14 退款／沖正之回沖（clawback）★ 實作完成

> 依據：`BRD-005 §11`（排除項含「退款」）、例 9（利率型必填 `clawbackRule`）、`SCS-026 §3` 參數 #18。
> 來源事件：**`posting.transaction.reversed.v1`**（payload：`postingId`（沖正交易）／**`reversalOf`（原 postingId）**／
> `amount`／`reason`／**`postingDate`＊**（★ `CR-049`：沖正交易之入帳日＝**業務日錨點**））
> ——生產端 `PostingEventFactory.reversedEnvelope`。

**⓪ 業務日（★ `CR-049` 真機揭露後補強）**：原 `reversed.v1` payload **恰 4 欄、無任何業務日**，
消費端只能拼 `null + "T00:00:00Z"` → 解析失敗 → **回沖在真機從未成功**（單元測試因手工餵入正確 JSON 而全綠）。
修正：生產端增**可選** `postingDate`（不升版）＋消費端**缺值即 fail-closed 略過**（不拼字串、不以 `occurredAt` 反推）。

**① 判定與動作**：
1. **反查原指令**：以 `reversalOf` 對 `campaign_reward_cmds` 之**結構化欄位 `posting_id`**（`V8`）反查
   → 得原活動／歸戶／載體／原金額；**查無 → 略過**（該筆未經認列，無可回沖）。
   ★ 實作決定：`V8__reward_cmd_lookup_columns.sql` 加 `posting_id`／`household_id`／`amount_minor`
   ＋索引——**不依賴 `JSON_VALUE`**（JSON 函式之相容性與可索引性皆不理想；銀行稽核要求可查、可索引）。
2. **回沖條件（`clawback_rule`）**：
   - `CLAW_BACK` → **發回沖指令**（`command_json` 帶 `"refund":true` ＋ `originalPostingId` ＋ `postingDate`）；
   - `NO_CLAWBACK` → **不回沖**（活動明示不追回）；
   - ★ **未宣告（null）→ 不回沖**（`BRD-005` 僅要求利率型必填；其餘型別未宣告時
     **保守不扣客戶點數**）——★ **BEAM PMO 2026-10-17 裁定：維持「不回沖」**（已凍結）。
3. **上限用量退還**：回沖時同步退還 cap 用量（`consumed_minor = GREATEST(consumed_minor - X, 0)`；
   `CHECK consumed_minor >= 0` 為防線），業務日採**沖正交易之入帳日**（`CR-049`）。
4. **冪等**：`consumed_events` 以 `eventId` 去重（同一沖正事件僅回沖一次）。

**② rewards 側**：回沖判定以 **`txType=REFUND` 為權威**（`refund` 旗標為輔）→
`AccrualRequest.isRefund=true` → 既有退款分支（`ADJUST` ＋負值、自 `CUR` 桶扣回）——**復用既有權威路徑**，不新增計算。

> ★ **`CR-049` 欄位層級一致性**：relay 一律把 `command_json` 包進 **`payload.recognition`**；
> 故消費端對 `refund`／`originalPostingId`／`postingDate` 一律「**`recognition` 優先、`payload` 次之**」
> （與 `txType`／`accountId`／`instrumentId` 同一慣例）。原實作僅讀 `payload` 層 → `refund` 恆 `false`
> → **把退款當一般入點**（方向錯誤），為真機 E2E 揭露之實質缺陷。

**③ 部分退款**：`reversed.v1` 之 `amount` 為沖正金額；本期**以原指令之金額為回沖基準**
（部分退款之按比例回沖待 `SCS-010` 定義，見 §8）。

### 7.13 條件式比率**凍結定案**（R-1～R-5，BEAM PMO 2026-10-17 裁定）★ 實作完成

> BEAM PMO 逐條裁定：**R-1②** 由 `campaign_plans.benefit_rate_json` 直接承載方案層權益率；
> **R-2** 生日權威＝**客戶主檔**；**R-3** 由 **rewards 提供查詢 API**（需改 `SAD-002 §5` ACL）；
> **R-4** 授權側**新增結構化欄位**，**不接受**以 `UNKNOWN` 交差；**R-5** 指令加 `rateReason`。

**① 條件 schema（本節定案，`campaign_rates.condition_json`）**——單層物件，**多鍵為 AND**：

| 鍵 | 語意 | 值域 | 來源（實查） |
|---|---|---|---|
| `planCode` | 方案 | `campaign_plans.plan_code` | ★ **R-1②**：方案層權益率改由 `campaign_plans.benefit_rate_json` 承載（schema：`{"rate": <number>}`）；`condition_json.planCode` 僅用於**選檔** |
| `birthMonth` | 生日月 | `1`–`12` | **客戶主檔**（外部；本地 `CC_PARTY.ACCOUNTS`／`PARTIES` **無生日欄**——實查） |
| **`minLevelRank`** | 最低層級**序**（★ R-3b 定案：BEAM PMO 裁定採 (a)） | 整數（1 起算；呼應 BRD §11「Level 2 起 3%」） | **rewards API** `PointsAccountView.tierRank`（序之權威在 rewards，以 `QUALIFICATION_SPEND` 升冪）——`SAD-002 §5` ACL 已加 `cc-campaign-service` |
| `payPath` | 支付路徑 | `PHYSICAL`｜`INTL_3PAY`｜`BANK_OWN`｜`THIRDPARTY`（`BRD-005 §6B／AC 8`） | ★ **R-4**：授權側新增結構化 `pay_path` 欄位（`V13`）→ posting 事件帶出 |

**② 判定順序**（`§7.11①` 擴充）：
1. 有條件檔逐檔比對：全鍵滿足者為候選；**取候選中最高 rate**；
2. 無條件基準檔（`condition_json={}`）為**底線**；
3. 方案層權益率（`campaign_plans.benefit_rate_json.rate`，`planCode` 命中時）優先於條件檔；
4. 三類皆無 → 不帶 `applicableRate`（rewards 走標準層級倍率）。

**③ `rateReason`（R-5，稽核留痕）**：`command_json.rateReason`（可選）——
格式 `BASE`｜`PLAN:<planCode>`｜`LEVEL>=<tier>`｜`BIRTH:<m>`｜`PAYPATH:<v>`，多條件以 `+` 連接
（支援 `BRD-005 §12`「條件式比率覆核：實際達成條件與筆數」之月報）。

**④ 缺值處理（R-4 之「不接受 UNKNOWN」）**：
- **UNKNOWN ＝ 猜測為未知類別** → **禁止**（不臆造）；
- 授權側欄位補強後，若仍缺值（如舊事件、未帶欄）→ **該條件視為不滿足**（記 WARN，
  **不套用**該條件檔）——即「不判定」而非「猜成 UNKNOWN」。
- ★ 與 R-4 裁定不衝突：裁定要求的是**補強來源使其可得**，本節即完成授權側結構化欄位補強。

**④b ★ R-3b 定案（BEAM PMO 2026-10-17 裁定採 (a)）**：`CC_RW.POINTS_TIERS` **無序號欄**（實查）→
層級**序之權威歸 rewards**：`PointsAccountView` 加 **`tierRank`**（以 `QUALIFICATION_SPEND`
**升冪**取序，1 起算；停用層級不佔序；查無 → `null`）。campaign **不需知道序之規則**，
只消費序號——條件鍵為 **`minLevelRank`（數值序）**。
★ 此設計**更貼近 BRD 原文**：`BRD-005 §11` 即寫「**Level 2 起 3%**」——Level 就是序。
⚠️ 序缺席（無點數帳戶／無層級參數）→ 條件**不判定**（不臆造序）。

**⑤ 外部依賴（R-2）**：生日月權威＝**客戶主檔**（本平台**無** customer-service，實查 `spec/services.yaml`）
→ 介面待外部提供；提供前 `birthMonth` 條件檔**不套用**（記 WARN）。
⚠️ 此為**外部依賴**（非自我設限）——已盤點本地 `party` 無生日欄。

### 7.12 條件式比率之待凍結清單（R-1～R-5）——★ **已於 2026-10-17 由 BEAM PMO 逐條裁定，定案見 §7.13**

> **用途**：`§7.11①` 之「有條件檔」目前 **fail-closed**（不套用）。解除 fail-closed 之前，
> 需逐條凍結下列維度之**欄名、值域、來源服務**。本清單每條含「BRD 依據／實查現況／待裁定」，
> **裁決後直接可實作**（不需再查）。
>
> BRD 依據原文（`BRD-005 §11`）：「**條件式比率（本版）**：比率依**方案／生日月／Level／支付路徑**
> 分層——生日月國外 x3、Level 2 起 3%」；另「**支付路徑限定（本版）**：實體／國際三Pay／
> 自家支付／第三方條碼——CUBE 指定消費限實體與三Pay」。

| # | 維度 | BRD 依據 | 值域來源（**實查現況**） | 需 BEAM PMO 裁定 | 凍結後落點 |
|---|---|---|---|---|---|
| **R-1** | **方案（plan）** | §11 條件式比率 | **表已存在**：`CC_CPG.CAMPAIGN_PLANS`（`plan_code`／`benefit_rate_json`／`level_req`／`valid_from-to`） | ① 條件式比率之「方案」＝對映 `campaign_plans.plan_code`？② 或改由 `campaign_plans.benefit_rate_json` 直接承載方案層權益率（與 `campaign_rates` 之分工）？ | `condition_json.planCode` 匹配 `campaign_plans`（同活動內） |
| **R-2** | **生日月** | §11（生日月國外 x3） | **來源未備**：`CC_PARTY.ACCOUNTS` **無生日欄**（實查 `birth` 無命中） | 生日資料權威在何處？（客戶主檔／`party` 擴充／外部）——**決定後需同步契約** | 需新增來源；`condition_json.birthMonth` 比對「帳戶生日月」 |
| **R-3** | **Level** | §11（Level 2 起 3%）；BRD §312「CUBE Level 進階制」 | **有表但跨服務**：`CC_RW.POINTS_ACCOUNTS.tier`／`POINTS_TIERS.tier_code`；⚠️ `campaign → rewards` **無同步 ACL**（`SAD-002 §5`） | ① Level 權威＝rewards 之 `tier`？② 傳遞方式：**事件投影**（rewards 發布 tier 變更 → campaign 本地投影）或 **`rewards` 提供查詢 API**（需改 `SAD-002 §5` ACL）？ | 投影表或 API；`condition_json.minLevel` 比對 |
| **R-4** | **支付路徑（payPath）** | §11 支付路徑限定（實體／三Pay／自家支付／第三方條碼） | **來源未備**（`#A-257` 實查）：授權側無結構化 wallet 欄位（僅自由格式 `emvData`） | ① 授權側契約補強（新增結構化欄位）之範圍？② 補強前非實體路徑落 `UNKNOWN`（fail-closed）是否可接受？ | `condition_json.payPath ∈ {PHYSICAL, THREEPAY, OWN_PAY, THIRD_BARCODE}` |
| **R-5** | **適用率留痕（稽核）** | §12 條件式比率覆核「對外宣稱最高 XX% 之**實際達成條件與筆數**」 | 現行：指令已帶 `applicableRate`（`§7.11②`）但**未記「為何適用該率」** | 是否需於指令加 `rateReason`（命中哪一檔／哪個條件）以支援月報覆核？ | `command_json.rateReason`（可選） |

> **裁定方式**：逐條回覆（例：`R-1 採①；R-3 採事件投影；R-5 需要`）即可。
> **未裁定前之行為**：有條件檔一律 **fail-closed**（記 WARN、不套用、不臆造匹配）——
> 即「不會算錯」，但「有條件加碼」暫不生效。
> ★ 注意：`campaign_plans` 與 `campaign_rates` 之分工（R-1②）牽動 `§3 #15/#16` 之參數模型，
> 建議與 `CR-045`（參數極大化）一併定案。

### 7.11 活動回饋率接線與精確上限截斷（★ 實作完成，真機通過）

> 語意依 **BRD 例 1 算式**（權威）：**回饋價值 ＝ 消費金額 × rate**（20,000 × 0.5% = 100）；
> `campaign_rates.rate` 即該比率（`SCS-026 §3 #15` 條件式比率）。

**① 適用比率判定**（campaign 職責——`CR-045` D3「規則判定」）：
- **無條件基準檔**（`condition_json` 為空物件 `{}`／null）＝**唯一可判定者**——BRD 例 1 之
  「一般消費 0.5%」即此檔；取**最高** rate。
- **有條件檔**（依方案／生日月／Level／支付路徑分層）：逐欄語意**尚未凍結** →
  **fail-closed 不套用**（記 WARN），**不臆造**條件匹配。
- ⚠️ 實作要點：`condition_json` 為 Oracle **JSON 型別**——以 `getString` 讀取
  （`getObject` 拋 `ORA-18722`），且**不在 SQL 內與字串比較**（語意不穩）→ 於 Java 端判定空物件。

**② 比率隨指令下發**：`command_json.applicableRate`（可選）；rewards 有值時**取代**標準層級倍率
（`SCS-010 §7.4`），無值走既有倍率。

**③ 精確上限截斷**（`CR-047 §3.1` 補完）：
`已用量 ≥ 上限` **或** `已用量 ＋ 本筆推定價值 > 上限` → 停發；
推定價值＝`amount × rate`（無比率時為 0 → 退化為「已達上限即停發」，保守不臆造）。
★ 對照 BRD 例 1：上限 1,000 元、已用 995 元、本筆 20,000 元 × 0.5% = 100 元 → 超限即停。

**④ 仍缺**：有條件比率之逐欄語意（待 BRD §11 條件式比率逐條驗證）／排除項／疊加（D9）／
REFUND 回沖（`reversed.v1` 帶 `reversalOf`，需規格先行）／`POINTS_LEDGER.campaign_code` 寫入。

### 7.8 門檻判定與指令下發語意（★ 實作完成，真機通過）

> ★ **權威矛盾釐清（依 preflight 紀律，不可靜默）**：`SCS-026 §2` 之 BR-03 表列
> 「campaign-service（計算）＋ rewards-service（點數帳本）」與 `CR-045` D3（BEAM PMO 2026-10-15 採納：
> 「Campaign **不接管計算**；rewards 保留紅利累點與回沖規則權威」**相衝突**。
> **以 D3 為準**：campaign 判定**資格與規則**（門檻／錄取／疊加／上限之「算不算」），
> rewards 執行**點數計算與帳本**（「給多少」）。`SCS-026 §2` 已同步更正。

**① 判定流程**（`TransactionRecognitionService`，認列同交易）：
1. 查活動之 `campaign_thresholds` 列：
   - **無門檻列**（即時回饋型）→ 維持現行：每筆認列下發指令；
   - **有門檻列**（滿額型）→ 累計判定，**`QUALIFIED` 才下發指令（一次性）**。
2. 門檻 scope（`campaign_thresholds.threshold_scope`）：
   - `PER_TXN`：單筆 `amount_minor ≥ min_amount_minor` 即達標；
   - `ACCUM`：`campaign_enrollments.accrued_amount_minor` 原子累計（`V5` 新欄，`+=` 單語句），
     累計 ≥ `min_amount_minor` 即達標（正附卡合併＝household 語意，`§7.5②`）；
   - `PER_STORE_PER_DAY`：**本期不實作**（fail-closed）——「每館」之店別識別與 merchantId 之
     等價性未經業務確認；含此 scope 之活動記 WARN 並**不下發指令**。
3. **升格原子性**：`UPDATE … SET status='QUALIFIED' WHERE id=? AND status='REGISTERED'`——
   恰更新 1 列者**才**下發指令（不同 postingId 分區之併發升格，僅一筆成功→指令恰一次）。
4. 多檔門檻（SOGO 型多檔）：任一列達標即升格；**檔位對應之差異化禮內容屬回饋計算**（下一增量），
   本期指令為一次性、不區分檔位（誠實揭露）。

**② 事實欄**：`V5__enrollment_accrued.sql` 為 `campaign_enrollments` 加
`accrued_amount_minor NUMBER(18) DEFAULT 0 NOT NULL`（活動期內認列累計；稽核友善，
QUALIFIED 後仍持續累計）。不修改 V1 baseline。

### 7.9 上限截斷（cap）——★ **已依 `CR-047` 定案並實作（2026-10-17）**

> BEAM PMO 2026-10-17 裁定：**開立 `CR-047` 並回訪既有架構**（參數極大化，不用寫死／workaround）；
> **「點」與「元」兩側皆為參數**（現防呆 1 點＝1 元）。
> 定案設計見 **`CR-047 §2 架構回訪 R1–R6`／§3**；實作摘要：
>
> - **定義／用量兩表分工**：`campaign_caps`（定義）＋ **`campaign_cap_usages`（用量；`V6`）**——
>   通用 `scope_key`＋`period_key` 維度（**新增單位不需改表**）；
> - **單位**：一律以**回饋價值（分）** 記帳；點值以商品參數 **`rewards/point-value-minor`**
>   換算（**兩側皆參數**；預設 1＝既有防呆）；
> - **事前截斷**：下發指令前檢查「已用量 ≥ 上限即停發」（保守；精確之「已用量＋本筆推定價值」
>   待 `rate_schedule` 實作後補）；
> - **事後記帳**：`rewards.points.accrued.v1` 回流（payload 帶 `campaignCode`／`valueMinor`／
>   `instrumentId`／`postingDate`；`CR-047 §3.3`）→ 原子遞增用量；
> - **本期支援**：`PER_CARD`／`PER_HOUSEHOLD`／`PER_MONTH`／`PER_YEAR`；
>   **`PER_STATEMENT`／`PER_HOUSEHOLD_MEMBER` fail-closed**（來源未備，記 WARN 不臆造）。
>
> 真機判定力：未達上限可發／回填達上限後停發／`PER_MONTH` 跨月各自獨立／未支援單位不發。

#### 原始提案紀錄（保留供追溯）

> ★ 實查 `campaign_caps` DDL 發現**兩個真實落差**，未裁定前不實作（fail-closed，不臆造換算）：

| # | 落差 | 內容 | 提案 |
|---|---|---|---|
| ① | **結構落差** | `campaign_caps` 無 `household_id` 維度——`PER_HOUSEHOLD`（每歸戶上限，BRD 例 1）需 **per-household** 之 `consumed` 追蹤，表僅有**活動級**單一 `consumed_minor` | `V6` 遷移：`ALTER TABLE campaign_caps ADD household_id RAW(16)`（可空；活動級 cap 為 NULL、每戶 cap 為 NOT NULL）＋唯一約束 `(campaign_code, cap_unit, household_id)`；`consumed_minor` 之**原子條件遞增**（`WHERE consumed_minor + ? <= cap_value_minor`）與名額取號同型 |
| ② | **單位落差** | `cap_value_minor`（**分**）vs rewards 回流之 **points**（點）——回饋價值換算（例 1「上限 500 元刷卡金」＝上限是**回饋價值**，而 `points.accrued` 帶的是點數）屬**客戶業務規則** | 兩案待裁定：**(a)** cap 以**點數**累計（`cap_value_minor` 語意改「回饋單位」，最簡）；**(b)** 以**消費金額**累計（門檻同源，`accrued_amount_minor` 可復用——但「回饋上限」≠「消費上限」，語意不符 BRD 例 1/3）。**建議 (a)**：BRD 例 1/3 之上限皆為回饋價值，需 BEAM PMO 提供點數↔元換算（或確認以點數為 cap 單位） |

**③ 回流路徑（已備）**：rewards 已發布 `rewards.points.accrued.v1`（`SCS-010 §7.1`，含 `accountId`／`points`）——campaign 新增消費者即可回流；**卡在①②之裁定**，非技術阻塞。

**④ AC 11 載體無關性（契約即證明）**：rewards 之 `AccrualRequest` **無載體欄位**（入點以
`accountId` 為識別）→ 入點**天然載體無關**；`instrumentId` 僅稽核用（campaign 指令事實）。
服務內判定力測試已驗：同戶實體卡＋虛擬卡各一筆認列 → **跨載體累計合併**、達標一次性指令。
⚠️ 跨服務真 Kafka 端到端仍待跨 JVM 驗證（同 `#A-241` 判例：本機沙箱攔截代理限制）。

---

## 8. 已知限制（誠實揭露）

- ★ **本版規格已部分落地，但不是完整活動引擎**：`campaign-service` 現已具備 Campaign 定義（DDD／JDBC／Outbox／5 端點）**＋交易認列消費端**（`PostedTransactionListener`／`TransactionRecognitionService`／`consumed_events`冪等／`ENROLLMENTS` 錄取／`REWARD_CMDS` 指令下發，真 Oracle 測試通過）。**仍缺**：門檻判定／上限截斷／排除項檢核／疊加計算／錄取名額原子控管（BRD AC 7）／登錄 API（REGISTER 模式）／rewards 側指令消費。不得把「認列已通」寫成「回饋已確定」。
- **B4／AC 11 尚不可執行**：消費端鏈路（認列→錄取→指令）已通真機，但 AC 11 尚需 rewards 側消費 `REWARD_CMDS` 之實作與三載體同額對照資料；現行 `REWARD_CMDS` 為 **PENDING 佇列狀態**（指令已下發、無人收——屬 §7.5① 之計畫內過渡，rewards 消費端為下一增量）。
- **交易事件資料缺口已定位**：`posting.transaction.posted.v1` 現已可帶 `instrumentId`／`merchantId`／`mcc` 三個可選欄位（皆有權威來源：載體主檔／入帳請求／授權紀錄）。仍缺：`payPath`（BR-13 無核定輸出契約）、`householdId`（不得以 accountId 靜默代替）——二者屬活動認列契約範圍；不得為了讓活動規則「看起來可算」而臆造。
- **不可臆造 AC 11 行為**：本版沒有自行新增「交易事件→活動認列→回饋發放」的 payload、事件消費者、回饋計算公式或活動政策；這些須先補 `SCS`／契約，並確認與 `rewards-service` 權威及 BRD 之 recognition／排除／上限語意一致。
- **BR-16 載體抽象侵入性最高**：交易鍵由 `card_no` 升為 `instrument_id`，牽動全服務 DTO/DDL/事件 payload；已完成 card／authorization／posting／statement／party 等現有鏈路接線，但 B4 同構回饋仍是獨立未完成增量。
- **BR-13 支付路徑認定器**需與 `posting-service`／卡組織歸屬檔介接規格先行（BRD 附錄 B 行動 6 未結）。
- **BR-14 通路插件**的 Adapter 介面需與 IT 共擬（BRD 附錄 B 行動 9 未結）。
- **BRD 遺留假設仍待客戶**：A1 銀聯檔期清單、A6 本行信貸產品全表、A7 利率減碼會計科目、
  A4/A5 逐卡官網條款覆核。**這些屬客戶輸入，非我方阻塞**。
- ⚠️ **前版 1.0 之「六項口述政策」已由 BRD 取代**；兩者不一致處以 **BRD 為準**，`CR-045 §8.2` 僅保留為歷史決策記錄。

---

## 9. 測試案例（對應 TRP-001，設計提案）

| # | 案例 | 對應 |
|---|---|---|
| 1 | 例 1–10 逐例以參數建立並驗算 | BRD §15 |
| 2 | 100 併發搶 10 名額，成交恰 10、無超發 | AC 7 |
| 3 | 同卡同額四支付路徑分級 | AC 8 |
| 4 | Level/方案切換次月生效＋慶生月任務進度 | AC 9 |
| 5 | 新通路經 CHANNEL 主檔掛載後納入既有活動 | AC 10 |
| 6 | 實體卡號 vs 虛擬卡號同構回饋一致（待交易認列／回饋發放契約先立） | AC 11；本版尚不可執行，不得以 DDL 代替驗收 |
| 7 | 信貸首期 0%＋提前清償依 clawback 回沖 | AC 12 |
| 8 | 疊加三模式（並行/擇優/互斥） | D9 |
| 9 | 排除項可按活動覆寫（加值事件翻轉） | AC 5 |
| 10 | 正附卡合併上限 vs JCB 獨立登錄雙模式 | AC 4 |

---

## 10. 修訂記錄

| 版本 | 說明 |
|---|---|
| 3.9 | **2026-10-17：★ `CR-049` 真機缺陷修正（跨服務線格式與回沖業務日）**——① `posting.transaction.reversed.v1` 增可選 `postingDate`（沖正交易之入帳日＝業務日錨點；**不升版**）；② `ReversalService` 之 `refundCommandJson` 帶 `postingDate`、未帶時以當日為錨點並記 WARN；③ rewards `CampaignRewardCommandListener` 對 `refund`／`originalPostingId`／`postingDate` 改「**`recognition` 優先、`payload` 次之**」、回沖判定以 `txType=REFUND` 為權威、**缺 `postingDate` fail-closed**（原拼 `"nullT00:00:00Z"` → 解析失敗）；④ 6 個服務＋產生器之 `value-serializer` 改 `StringSerializer`，新增架構關卡 **A9**。★ **真機 E2E 32/32**（`ops/local/e2e/campaign_chain_e2e.sh`）：載體無關 123＋123、比率 5% → 6 點、上限 `used=600 estimated=617 cap=1000` 精確截斷、回沖 `ADJUST -123` 淨零。 |
| 3.8 | **2026-10-17：★ §7.16 排除項參數化實作完成**——① 基準＝僅 `PURCHASE`（**與原行為一致**，無行為變更）；② 活動覆寫 `exclusion_json`：`includeTxTypes`（納入額外類別，BRD「可按活動覆寫／翻轉」）／`excludedMccs`（活動自訂 mcc 排除；mcc 缺席時 fail-closed）；③ ★ BEAM PMO 裁定「未宣告 `clawback_rule` → 不回沖」**已凍結**（原待覆核項結案）。判定力：真機 3 例（預設只認 PURCHASE／覆寫納入 INSTALLMENT／mcc 排除）。 |
| 3.7 | **2026-10-17：★ §7.15 疊加 D9 與優先序實作完成**——① `V7__campaign_priority.sql`（`priority`；★ 實查原表無此欄——多個 `NON_STACKABLE` 命中時無從擇一）；② `Campaign` 聚合＋repo＋DTO（**相容多載**，既有呼叫不動）＋契約；③ `TransactionRecognitionService`：命中 `NON_STACKABLE` → **取 priority 最小者並排除其他所有活動**；全 `STACKABLE` → 全部套用。判定力：**5 例真機**（NON_STACKABLE 擇一／priority 較大者不套用／可疊加者被排除／全 STACKABLE 全套用）。 |
| 3.6 | **2026-10-17：★ §7.14 退款／沖正回沖實作完成**——① `ReversalListener`（`cc.posting.transaction.reversed`）＋`ReversalService`：`eventId` 冪等 → 以 `reversalOf` 對 `JSON_VALUE(command_json,'$.postingId')` 反查原指令 → 依 `clawback_rule` 判定（`CLAW_BACK` 回沖／`NO_CLAWBACK` 與**未宣告**不回沖）→ 發回沖指令（`refund:true`）＋ cap 用量退還（`GREATEST(consumed - X, 0)`）；② rewards：listener 讀 `refund` → `AccrualRequest.isRefund=true`（**復用既有退款分支**：`ADJUST`＋負值、自 `CUR` 扣回）；③ `POINTS_LEDGER.campaign_code` **寫入**（活動歸屬稽核，欄位已備）。★ 未宣告 `clawback_rule` 之預設（不回沖）**列待 BEAM PMO 覆核**；部分退款按比例回沖待 `SCS-010` 定義。 |
| 3.5 | **2026-10-17：★ R-3b 定案並實作（BEAM PMO 裁定採 (a)「rewards API 回傳 tier 序」）**——① rewards：`PointsRepository.findTierRank`（`ROW_NUMBER() OVER (ORDER BY QUALIFICATION_SPEND ASC, TIER_CODE ASC)`；僅 `ACTIVE`；查無 → null）＋`PointsAccountView.tierRank`＋assembler／`PointsAccountService` 傳遞；② campaign：`TierLookupPort.TierInfo(tier, rank)`＋`RewardsTierClient` 解析＋`RateResolver` 條件鍵 **`minLevelRank`**（數值序比較；序缺席 → 不判定）；③ 契約同步（`PointsAccountView.tierRank`）。★ **設計洞察**：`minLevelRank`（序）**更貼近 BRD §11 原文**「Level 2 起 3%」——Level 即序。判定力：campaign `RateResolverTest` **7 例**（基準檔／方案優先／序達標／序未達標／序缺席不判定／payPath 三態／多條件 AND／多檔取最高／無檔 NONE＋verify）；rewards 真機 **2 例**（序 1-2-3／停用不佔序）。契約 106 endpoints／0 不符；SQL 44 檔 0 違規；掃描器 0 命中（★ T3 攔下純函式測試缺 verify → 補互動斷言）。 |
| 3.4 | **2026-10-17：★ §7.13 條件式比率凍結定案並實作**——BEAM PMO 逐條裁定：**R-1②**（方案權益率由 `campaign_plans.benefit_rate_json` 承載，schema `{"rate":n}`；BRD §646 之 PLAN 實體欄位與之一致）、**R-2**（生日權威＝客戶主檔；★ 實查本平台無 customer-service 且 `party` 無生日欄 → **外部依賴**，介面待提供）、**R-3**（rewards 提供查詢 API；`SAD-002 §5` ACL 新增 `cc-campaign-service`）、**R-4**（授權側新增結構化 `pay_path` `V13`；★ BEAM PMO 明確**不接受**以 `UNKNOWN` 交差——缺值時改為「條件不滿足」（不判定，非猜測））、**R-5**（`rateReason` 稽核留痕，支援 BRD §12 覆核）。條件 schema 四鍵（`planCode`／`birthMonth`／`minLevel`／`payPath`）**AND** 語意、取最高 rate。 |
| 3.3 | **2026-10-17：★ §7.12 條件式比率待凍結清單（R-1～R-5）**——應 BEAM PMO「需要我逐條凍結的列在哪裡？」而產出：將散在 `BRD-005 §11`／本規格 §3 #15 之條件維度整理為**可逐條勾選**之清單，每條附 **BRD 依據／實查現況／待裁定項／凍結後落點**。★ 實查新發現：`campaign_plans` 表**已存在**（含 `benefit_rate_json`／`level_req`）——「方案」維度可用，惟與 `campaign_rates` 之分工需一併定案（R-1②）。 |
| 3.2 | **2026-10-17：★ §7.11 活動回饋率接線＋精確上限截斷（真機通過）**——① 語意依 **BRD 例 1 算式**（回饋價值＝金額 × rate）——`rate` 為**回饋率**（非倍率）；② 僅採**無條件基準檔**（`condition_json={}`），有條件檔 **fail-closed**（語意未凍結）；③ `applicableRate` 隨指令下發 → rewards 有值時取代標準倍率；④ **精確截斷**：`used ≥ cap` 或 `used + amount×rate > cap` → 停發（`CR-047 §3.1` 補完）；⑤ ★ 實作陷阱：`condition_json` 為 Oracle JSON 型別——`getString` 讀取（`getObject`→`ORA-18722`）且**不在 SQL 內字串比較**（改 Java 端判定）。campaign **46 例**／rewards **159 例** 全綠；SQL 30 檔 0 違規；掃描器 0 命中；契約 106 endpoints／0 不符。**仍缺**：有條件比率逐欄語意／排除項／疊加／REFUND 回沖／ledger 歸屬寫入。 |
| 3.1 | **2026-10-17：★ `CR-047` cap 定案並實作（真機通過）**——依 BEAM PMO 裁定（開 CR＋回訪架構；點與元兩側皆參數）。**架構回訪**：`campaign_caps` 僅活動級單一 consumed，無法承載 BRD §11「六單位各自可配＋可組合」→ **定義／用量兩表分工**（`campaign_caps` 定義／**`campaign_cap_usages`** 用量；`V6`）；**通用 `scope_key`＋`period_key`**（新增單位不改表）。**換算**：一律以回饋價值（分）記帳；點值＝商品參數 `rewards/point-value-minor`（預設 1＝既有防呆）——**「點」為資料、「元」為參數**。**實作**：`CapGuardService`（事前截斷／事後回填）、`JdbcCapUsageRepository`（原子遞增）、`PointAccruedListener`（`cc.rewards.points.accrued` 回流）、`TransactionRecognitionService` 下發前經守衛。**rewards 側**：`AccrualRequest` 加 `campaignCode`／`instrumentId`／`postingDate`（可選）、`accrued` payload 加 4 可選欄位（`CR-047 §3.3`，不升版）、`ProductPointValueHttpClient`（取點值）。**判定力**：未達上限可發／回填達上限停發／`PER_MONTH` 跨月獨立／未支援單位 fail-closed。campaign **43 例**、rewards **158 例** 全綠；SQL 30 檔 0 違規；掃描器 0 命中；契約 106 endpoints／0 不符。 |
| 3.0 | **2026-10-17：★ §7.9 cap 設計提案＋錄取查詢端點＋AC 11 載體無關性（真機通過）**——① **cap 兩落差實查**：`campaign_caps` 無 `household_id` 維度（PER_HOUSEHOLD 之 per-household consumed 無處放）、`cap_value_minor`（分）vs rewards 回流點數之換算未定——**待 BEAM PMO 裁定後實作**（提案：V6 加欄＋兩案換算，建議以點數為 cap 單位），不臆造；② 新端點 `GET /api/v1/campaigns/{code}/enrollments/{householdId}`（回 status／quotaSeq／accruedAmountMinor——AC 11 對照之可觀測性），契約重生 **106 endpoints／0 errors**；③ **AC 11 載體無關性服務內判定力測試**：同戶實體卡＋虛擬卡各一筆認列→跨載體累計合併（16,000 達標）→一次性指令（載體僅稽核；`AccrualRequest` 無載體欄位＝契約面載體無關）；⚠️ 跨服務真 Kafka 端到端仍待跨 JVM 驗證（#A-241 判例）。campaign 全測 **40 例** BUILD SUCCESSFUL；SQL 28 檔 0 違規；掃描器 0 命中；契約↔DTO 0 不符。 |
| 2.9 | **2026-10-17：★ §7.8 門檻判定與指令下發完成（真機通過）**——① 無門檻活動＝即時型（每筆認列下發指令，現行語意保留）；② 有門檻活動＝滿額型：`PER_TXN`（單筆達標）／`ACCUM`（`V5__enrollment_accrued.sql` 新欄 `accrued_amount_minor` 原子 `+=` 累計）→ 達標 **原子升格 `QUALIFIED`**（`UPDATE … WHERE status='REGISTERED'`，併發下恰一筆成功→**指令恰一次**）；`PER_STORE_PER_DAY` fail-closed 略過（店別識別未經業務確認）；③ **`REGISTER`＋已登錄者之認列接通**（未登錄略過——登錄 API 與認列鏈閉合）；④ ★ **權威矛盾釐清**：`SCS-026 §2` BR-03（campaign 計算）與 `CR-045` D3（Campaign 不接管計算）相衝突——**依 D3 為準**：campaign 規則判定（「算不算」）、rewards 點數計算與帳本（「給多少」），§2 已更正。campaign 全測 **38 例** BUILD SUCCESSFUL（判定力：ACCUM 三段式 12000→21000→26000 之無指令／一次性下發／不重發；PER_TXN 立即升格；未達不下發；fail-closed；REGISTER 已登錄才認列）。SQL 合併 28 檔 0 違規；掃描器 0 命中；契約↔DTO 0 不符。 |
| 2.8 | **2026-10-17：★ §7.7 名額原子控管與登錄 API 實作完成（真機通過）**——`EnrollmentService`＋`POST /api/v1/campaigns/{code}/enrollments`（僅 REGISTER 模式）：① 原子取號＝行鎖（SELECT FOR UPDATE）＋條件更新（雙重保險）＋DB CHECK 防線；② 額滿 `CC-CAM-1004`；③ 冪等：同 `(campaign, wave, household)` 回前次結果；④ 併發撞 `uq_enroll` → 退回名額補償；⑤ AUTO／非 ACTIVE → `CC-CAM-3001`；⑥ 波次窗口檢核。新檔：`EnrollmentRepository`／`JdbcEnrollmentRepository`／`EnrollmentService`／`EnrollmentController`／DTO／`CampaignTimeConfig`。契約重生 **105 endpoints／0 errors**。campaign 全測 **33 例** BUILD SUCCESSFUL（真 Oracle 判定力：取號連號／禁超發／冪等／AUTO 拒絕／404／窗口外）。AC 7「禁超發」解鎖。 |
| 2.7 | **2026-10-17：★ §7.6 指令派發與 rewards 消費完成（真機通過）**——campaign `RewardCommandRelay`（`REWARD_CMDS` 即指令 outbox：PENDING→發布→SENT；SENT＝已派發非已入點）；事件 `campaign.reward.commanded.v1`（主題 `cc.campaign.reward.commanded`，分區鍵 commandId）；rewards `CampaignRewardCommandListener` → `AccrualService.accrue`（冪等鍵 `campcmd:<commandId>`；到期日以 postingDate 錨點；⚠️ 活動加成 rate_schedule 未實作——本期標準層級倍率，不臆造加成）。§7.5① 收斂：本期認列類別＝僅 `PURCHASE`。§7.5① 表同步 `SAD-004 §5.2`（posting 消費者加 campaign；新列 campaign.reward.commanded）。campaign 全測 **27 例**、rewards 全測 **158 例** BUILD SUCCESSFUL；SQL 合併 27 檔 0 違規；掃描器 0 命中（faketest T5 攔下無斷言測試，已補 assertDoesNotThrow）。**仍缺**：門檻／上限／排除項／疊加／名額控管／登錄 API／活動加成比率。 |
| 2.6 | **2026-10-16：★ §7.5 消費端實作完成（真機通過）**——`PostedTransactionListener`（`cc.posting.transaction.posted`）→ `TransactionRecognitionService`（`eventId` 冪等＋`sequenceNo` 亂序略過＋沖正略過＋REGISTER 略過）→ `ENROLLMENTS` 錄取（`household_id ≡ accountId`；`uq_enroll` 冪等重放）→ `REWARD_CMDS` 指令下發（**僅認列事實、PENDING**；計算屬 rewards 權威）。遷移：`V3__consumed_events.sql`、`V4__nullable_instrument.sql`（舊事件 instrumentId null 不阻斷，§7.5④）。campaign 全測 **25 例全綠**（真 Oracle 判定力測試：冪等／亂序／沖正／REGISTER／舊事件相容／期間外／未知型別）。**仍缺**：門檻判定／上限截斷／排除項／疊加／名額控管（AC 7）／登錄 API／rewards 消費端——不得宣稱回饋已確定。 |
| 2.5 | **2026-10-16：★ BEAM PMO 裁定落定**——② 歸戶語意：自然人經 BEAM PMO 確認；**法人公司戶經實查同構**（`account_type IN ('PERSONAL','CORPORATE')`，公司戶即一歸戶、員工附卡為其 parties[]；活動差異走 exclusions 參數）；③ payPath：規則表對照方向經 BEAM PMO 同意；**實查定位新缺口**——授權側無結構化 wallet 欄位，種子規則依 BRD §6B/AC 8 可立，wallet 識別權威待授權側契約補強（補強前非實體路徑落 UNKNOWN，fail-closed）。 |
| 2.4 | **2026-10-16：`CR-046` 新增 §7.5 活動認列輸入契約（設計提案，待 BEAM PMO 核定）**——① 認列來源唯一化（posted.v1）；② 欄位權威映射；③ **歸戶語意更正**：household_id ≡ accountId（權威 `CC_PARTY.ACCOUNTS`／`SCS-004`；更正 `#A-253` 之過度保守判斷）；④ payPath 權責：認定器屬 campaign、認定規則待 BEAM PMO 核定、不在 posting 事件加欄；⑤ AC 11 驗收前置條件五項。核定前不寫 campaign 消費端。 |
| 2.3 | **2026-10-16：`CR-046` AC 11 資料鏈補強**——`posted.v1` 已可帶 `instrumentId`／`merchantId`／`mcc`（皆權威來源）；campaign 消費者、payPath 輸出契約、household 歸戶解析仍未立，AC 11 繼續維持「待契約」。 |
| 2.2 | **2026-10-16：`CR-046` AC 11 資料平面盤點**——`posting.transaction.posted.v1` 已帶可選 `instrumentId`；惟 posting 交易未持久化 `mcc`、`PostingRequest.merchantId` 未進入聚合，故 `mcc`／`merchantId` 不能臆造加入活動認列；AC 11 仍待契約與來源治理。 |
| 2.1 | **2026-10-16：`CR-046` B4／AC 11 動工前盤點更正**——Campaign 定義之 Java 主幹已存在，但 `campaign_enrollments`／`campaign_reward_cmds` 僅為 DDL；尚無交易事件消費、錄取服務、回饋計算與 rewards 發放鏈路。故 AC 11 改列「待立交易認列／回饋契約」，不以有表或有 `instrument_id` 欄位宣稱完成 |
| 2.0 | **2026-10-16：依 `BRD-005`（客戶提供，v1.3）全面改寫**——① 對應需求由「六項口述政策」擴為 **BR-01～16 全 16 條**；② BR-01 參數化要素由前版 6 項擴為 **30 欄完整契約**（新增申請/撥款雙日期、認列錨點、進件歸因、條件式比率、cap 六單位、支付路徑限定、成本分攤、外部事件依賴、clawback、任務、抽獎等）；③ 新增 ENROLLMENT／PLAN／QUEST／PAY_PATH／CHANNEL／REWARD_CMD 實體；④ 錄取狀態機照 BRD §12.2 錄入；⑤ §6 十個驗算範例列為自動化驗收基準；⑥ §8 明列 BRD 遺留假設屬客戶輸入、BR-16 載體抽象須單獨分期 |
| 1.0 | 2026-10-15：初版（僅基於 BEAM PMO 口述六項政策之草案） |
