Project

General

Profile

CR #501 » CR-055_共享池(共用額度)與預借現金企業子額度之語意凍結與計算式.md

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

 

CR-055 共享池(共用額度)/預借現金/企業子額度——語意凍結與計算式

項目 內容
文件編號 CR-055
版本 1.3
狀態 口徑已凍結;P4-1/P4-2/P4-3 全數落地並真機驗證(§8/§10/§11);§6 維度已由 BEAM PMO 確認(自然人帳戶級/公司戶歸戶級/不混算);身分證 360 見 CR-056
觸發 BEAM PMO 2026-10-17 回覆 CR-054 §9 Q-A~Q-E
需求依據 RFP FR.md §2.4(1)「歸戶、持卡人、卡片調額/查詢」;CR-052 C-1(ii)/C-4;CR-053 維度模型
影響模組 limit-service(檢核與查詢)、authorization-service(授權呼叫)、商品參數(product-service)
關聯 CR-052/CR-053/CR-054

1. 口徑原文(BEAM PMO 2026-10-17,逐字保留要點)

「共享池的觀念是『總額度的另一面說法-共用額度』。如果某自然人/公司戶在某銀行的
總額度是 5 萬,這個人在這家銀行有三張卡,一張額度 5 萬、一張 2 萬、另一張 3 萬,
他的共用額度仍然是 5 萬,而不是 5+2+3=10 萬!

對卡片級而言,若當初的額度 2 萬,超刷的部分就走共享池,但共享池的上限就是 5 萬,
就算其他兩張都沒刷,這一張兩萬額度的最多也只能刷到 5 萬!

還有,共享池是個參數,2 萬那張卡若不能走共享池,那他的上限就是當初核定的 2 萬!」

「A-3:會!(一般消費走卡片額度、預借現金走共享池)——因為共享池的觀念不在消費類型,
而是該卡能否享有共享池的權利
!」

「B:共享池是個參數,只有能夠適用共享池的商品,才會在他可以消費的類別裡面『消費』。
其卡片級的『可用』仍是他原本的額度。」

「C:預借現金是總額度的某個百分比所產生的可預借現金額度。不是獨立額度——
總額度 5 萬、評等 80% → 4 萬是預借現金上限,總額度仍為 5 萬,不是 5+4=9 萬!」

「D:企業子額度是集團型總公司的總額度,各分公司、部門、分公司總經理、高階主管、
採購單位可以有個別的子額度。D-2:當然要(子額度佔用扣減上層)。」

「E:同意(60 秒容忍、300 秒告警)。」


2. 精確語意模型(本文件為權威)

2.1 三個不同的「額度」概念(★ 不可混用)

概念 定義 現行承載
總額度(shared pool 之上限) 該客戶在本行之共用上限;不是各卡額度之和 account_limits 之 ACCOUNT 維度(permanent + temporary + shadow)
卡片名目額度 核卡時核給該卡的金額;僅為該卡自身可用之上限(不享共享池時) account_limits 之 CARD 維度(V11)
共享池權利 該卡是否可動用總額度(參數,商品級) ★ 本 CR 新增:account_limits.shared_pool_eligible(卡片維度快照)

★★ 關鍵:共享池不是額外池,而是「允許該卡用到總額度」的權利。
因此 總額度 ≠ Σ卡片額度,且「享共享池」的卡其上限不會變成「卡額度 + 總額度」。

2.2 可刷上限(授權檢核用,風控底線)

若 該卡享共享池(shared_pool_eligible = true):
      可刷上限 = 總額度 − 帳戶已用 − 帳戶全部未釋放佔用        ← 即「帳戶可用」
否則:
      可刷上限 = 卡片名目額度 − 該卡未釋放佔用                 ← 卡額度硬上限

★ 兩者皆下限 0。授權時檢核「本次金額 ≤ 可刷上限」。

2.3 卡片級「可用」的呈現(查詢用)

BEAM PMO 明示:「其卡片級的『可用』仍是他原本的額度」——故查詢須分開呈現:

欄位 語意
availableAmount 卡片自身可用=卡片名目額度 − 該卡佔用(不扣帳戶已用)← 這是「原本的額度」
sharedPoolEligible 是否享共享池(參數快照)
sharedPoolAvailableAmount 可動用總額度之剩餘=帳戶可用(僅 eligible=true 時有值;否則 null)

★ 一個數字不足以表達:availableAmount 是「這張卡自己的錢」,
sharedPoolAvailableAmount 是「這張卡還能向共用池借多少」。兩者不可相加。

2.4 BEAM PMO 之 5 萬例子——逐情境驗算

設定:總額度 5 萬;三張卡名目 5/2/3 萬;帳戶已用 0、佔用 0。

# 情境 可刷上限(正解) 現行系統 判定
① 2 萬卡(享共享池)首次刷卡 5 萬(總額度,不是 2+5=7) 5 萬(看帳戶可用) ✅ 一致
② 同上,其他兩卡已刷 4 萬 1 萬(5−4) 1 萬 ✅ 一致
③ 2 萬卡(不享共享池)刷卡 2 萬(卡額度硬上限) 5 萬 ❌ 超放 3 萬
④ 同上,該卡已佔用 1.5 萬 0.5 萬 5 萬 ❌ 超放

★★ 情境 ③/④ 即本 CR 要修的真實缺陷:現行 hold() 只檢核帳戶可用、
完全不看卡片名目額度(CARD 維度僅供查詢)。→ 不享共享池的卡會用到總額度。

2.5 預借現金(P4-2)

預借現金額度上限 = 總額度 × ratio          (ratio 由商品/信用評等參數決定,例 80%)
預借現金可用     = min(預借上限 − 已預借金額, 帳戶可用)

★ 不是獨立額度:總額度 5 萬、ratio 80% → 預借上限 4 萬,總額度仍 5 萬(非 9 萬)。
★ 與一般消費共用同一總額度(故 已預借 亦計入帳戶已用/佔用)。

2.6 企業子額度(P4-3)

  • 上層:集團總公司總額度(CORP_SUB 之上層,對應 subject_type=CORP_SUB 之根)。
  • 下層:分公司/部門/分公司總經理/高階主管/採購單位——各為一個 CORP_SUB 子主體。
  • 子額度佔用扣減上層(BEAM PMO 明示「當然要」)→ 上層可用須扣除所有下層佔用之和。
  • ⚠️ 需要組織樹(誰是誰的上層)——現行無此資料結構 → P4-3 需另立資料模型。

3. 與現行模型之落差(實查)

# 落差 現況 影響
G-1 授權不看卡片額度 hold() 僅檢核帳戶可用;CARD 維度只供查詢(#A-285) ★★ 不享共享池的卡會超放(§2.4 情境③/④)
G-2 無「共享池權利」欄位 無 無法區分「享/不享」
G-3 查詢未分開呈現兩種可用 availableAmount 為「卡額度 − 帳戶已用 − 卡佔用」(保守下界) 與 BEAM PMO 語意不符(他要「卡額度 − 卡佔用」)
G-4 無預借現金額度 無資料模型與參數 P4-2
G-5 無企業組織樹 CORP_SUB 僅值域預留 P4-3
G-6 「總額度」維度待確認 現行 ACCOUNT 維度;BEAM PMO 稱「自然人在本行總額度」 見 §6 待確認

4. 參數設計(BEAM PMO:商品(產品級)參數)

參數鍵(namespace=product) 值域 說明
limit.shared-pool-eligible true/false 該商品之卡是否可動用總額度;缺值 fail-closed(不臆造)
limit.cash-advance-ratio 0–100(%) 預借現金額度佔總額度之比例(P4-2)

★ 參數 → 卡片快照(留痕原則):設定卡片額度時,由呼叫端依商品參數帶入
sharedPoolEligible 並寫入 CARD 維度列;事後改參數不改變已設定之卡片語意。
⚠️ limit-service 無 card→product 對映(CR-052 C-5)→ 無法自行查商品參數,
故由呼叫端(BFF/card-service,知道 product_code)帶入。


5. 分批

批次 內容 狀態
P4-1 共享池權利欄位+授權卡片上限檢核+查詢分開呈現 ✅ 本批落地(V13)
P4-2 預借現金:cash-advance-ratio 參數+額度計算+查詢輸出(C-4) ✅ 已落地(V15/V16;§10)。★ 授權端尚未送 CASH_ADVANCE(§10.5)
P4-3 企業子額度:組織樹資料模型+上層扣減 ✅ 已落地(V17;§11)。★ 覆核流程與卡片綁定未接(§11.5)

6. 維度確認(BEAM PMO 2026-10-17 已裁定,本節為權威)

客戶型態 總額度維度 本批實作
自然人 帳戶級(同一人在本行該帳戶之總額度;不論幾張主卡/附卡) ✅ 已相符(ACCOUNT 維度)
公司戶 歸戶級(集團總公司之總額度;分公司/部門/主管/採購單位為子額度) ⏳ P4-3(需組織樹)

6.1 ★★ 個人與公司額度不混算(BEAM PMO 2026-10-17 例子)

張三在 A 銀行個人帳戶總額度 5 萬;Z 公司在 A 銀行公司戶總額度 100 萬,
張三持 Z 的差旅卡 10 萬、採購卡 30 萬。
張三在 A 銀行的帳戶總額度仍是 5 萬——個人與公司各自獨立。

落地含義:

  • 個人卡之共享池上限=個人帳戶可用(不因持有公司卡而提高)。
  • 公司卡之共享池上限=公司戶(歸戶)可用。
  • 同一自然人跨二者時,兩邊額度不得相加、不得互相扣減。

6.2 但「以人為視角」要查得到(CR-056)

BEAM PMO 同時要求:由身分證字號查詢時,要能帶出張三在 Z 公司的兩張卡。

★ 這與「額度不混算」不衝突:額度分開算,但查詢視角要能跨帳戶彙整。
→ 已開 CR-056。盤點揭露硬缺口:parties 綁 account_id(同一人跨帳戶為不同 party 記錄)、
身分證僅有加密欄與遮罩欄(遮罩不可作比較鍵)→ 無 id_no_hash 則跨帳戶 360 查詢不可行。
第一步(GET /api/v1/cards?accountId|partyId)已落地;id_no_hash 屬資安決策,待 BEAM PMO 裁定(CR-056 §4)。

6.3 採購卡之「限資產類消費」

BEAM PMO 例中採購卡「只能限制在資產類的消費」=用途限制(MCC 白名單)。
現行可由 special_limits.scope_ref 承載(CR-035 §2.1),但該表為帳戶層;
若客戶要求卡片層用途限制,需另立資料模型(本 CR 不動,記錄待辦)。


8. P4-1 執行結果(2026-10-17,#A-289)

項目 落地 真機證據
共享池權利欄位 V13:account_limits.shared_pool_eligible(DEFAULT 1 向後相容) 真 Oracle 實查欄位存在
調額單留痕 V14:limit_adjustments.shared_pool_eligible(可空;僅 CARD 維度) 同上
授權卡片上限檢核 hold():不享共享池 → 檢查「卡額度 − 卡佔用」;並與帳戶可用取較嚴者 ★ 卡額度 200,000、帳戶 10,000,000:刷 150,000 通過;再刷 100,000 拒絕(CC-LIM-1001,訊息含「不享共享池…卡可用=50000」)
查詢分開呈現 availableAmount=卡額度−卡佔用;sharedPoolEligible;sharedPoolAvailableAmount(僅 eligible 有值) 不享共享池:available=50,000、pool=null;享共享池:available=100,000、pool=9,650,000(=10,000,000−200,000−150,000)
參數 → 快照 調額請求帶 sharedPoolEligible,核准/生效時落地 CARD 列 設定 false 後查詢回 false

★ 修正之真實缺陷(本批最大價值):原 hold() 完全不看卡片名目額度——不享共享池的卡
可一路刷到總額度(例:總額度 5 萬、卡額度 2 萬 → 可刷 5 萬=超放 3 萬)。現已擋下。

★ 實作期發現之第二個缺陷(已修,勿重犯):初版於申請時即建立 CARD 列(為寫入權利),
但若該申請待覆核,卡片會出現「名目額度 0」之列 → hold() 以「卡可用 0」拒絕該卡所有交易。
→ 已改為「權利記在調額單、核准/直接生效時才建列」;並以既有測試
(cardAdjustmentOverThresholdRequiresApprovalAndKeepsDimension 斷言「待覆核期間不得先建列」)鎖住。

★ 測試:limit-service 202 測 0 失敗(新增 4 例:不享共享池被拒/享共享池可動用總額度/
無 CARD 列向後相容/卡額度與帳戶可用取較嚴);契約 112 端點/0 錯誤、verify_contract_dto 不符 0、
validate_sql 違規 0、三掃描器 0、Mermaid 0。

★ 仍未做(誠實揭露):

  • P4-2 預借現金:需「已預借」來源與 cash-advance-ratio 參數(比率基數為總額度)。
  • P4-3 企業子額度:需組織樹資料模型(現行無)。
  • §6 之待確認:「總額度」維度為帳戶級或歸戶/當事人級——本批採帳戶級(保守:不會超放,只會少給共享額度)。

10. P4-2 預借現金(2026-10-17,#A-296)

BEAM PMO 指示「P4-2 預借現金…請執行」。語意完全依 §2.5 凍結值實作,未另行解釋:

預借現金額度上限 = 總額度 × ratio / 100          (向下取整)
預借現金可用     = min(預借上限 − 已預借金額, 有效可用)

10.1 落地

項目 內容
V15 account_limits.cash_advance_ratio NUMBER(3)(可空、CHECK 0–100)+limit_adjustments.cash_advance_ratio(同,供調額單留痕)
V16 擴充 limit_holds.hold_type 值域加入 CASH_ADVANCE(見 §10.3 之真實缺口)
領域 AccountLimit.cashAdvanceLimit(today)=totalLimit × ratio / 100;cashAdvanceConfigured();比例 0–100 於記憶體建構亦檢核(不靠 DDL 唯一防線)
查詢 GET /instruments/{id}/limits 增 cashAdvanceRatio/cashAdvanceLimitAmount/cashAdvanceUsedAmount/cashAdvanceAvailableAmount
授權 hold(holdType='CASH_ADVANCE') 先檢預借子上限,再走既有「帳戶可用」檢核(兩者皆須通過)
參數 → 快照 調額請求帶 cashAdvanceRatio,於核准/直接生效時落地 CARD 列(比照 V14,申請時不建列)

10.2 三個刻意的語意決定

決定 理由
null ≠ 0 null=未設定 → 預借一律拒絕(fail-closed,不臆造 80% 這種「舉例值」);0=明確禁止(上限 0,訊息走「額度不足」)。兩者必須可區分,否則「還沒設」與「不准用」會混為一談
「有效可用」取較嚴者 享共享池 → 帳戶可用;不享共享池 → 卡片自身可用。若一律用帳戶可用,不享共享池的卡就能用預借繞過卡片硬上限(=§3 G-1 的缺陷以另一種形式復活)
預借佔用進同一張 limit_holds CR-052 Q-C C-3 建議「共用,以 hold_type 區分」——避免兩套佔用表導致總額度被重複使用

10.3 ★★ 實作期攔下的真實缺口(值域未涵蓋)

V1 基線之 ck_holds_hold_type 值域為 ('GENERAL','SPECIAL','INSTALLMENT'),
沒有 CASH_ADVANCE → 真機測試第一次執行即 ORA-02290(違反檢查約束)。
這不是測試寫錯,是語意與 DDL 不一致:§2.5 要求「已預借」可被識別,而唯一可識別的位置就是 hold_type。

修法:V16 動態定位該系統命名約束(SYS_C… 名稱隨環境不同,不可寫死)後卸除,
再以具名 ck_holds_hold_type 重建。不改成「無 CHECK」——值域即行為契約,
放寬為任意字串會讓打錯字靜默變成新型別。

10.4 真機驗證(Testcontainers 真 Oracle +真 Flyway)

情境 結果
未設定比例 → 預借 拒絕,訊息「預借現金未開放(該卡未設定預借現金比例,fail-closed)」;查詢四欄皆 null
卡額度 200,000、ratio 80% 上限 160,000(不是 360,000);預借 100,000 後:已預借 100,000、可用 60,000
★ 預借佔用是否計入帳戶佔用 是:帳戶 holdAmount=100,000、availableAmount=900,000(與一般消費共用總額度)
帳戶可用 1,000,000 但預借上限 160,000 → 預借 170,000 拒絕(錯誤訊息帶上限 160000,供客服判讀)
ratio 0 上限 0、可用 0;預借 1,000 → 拒絕(走「額度不足」而非「未開放」)
不享共享池(卡 200,000/ratio 20%) 上限 40,000;卡可用 100,000 → 可用取 40,000;預借 50,000 → 拒絕(卡額度充足仍被預借上限擋下)
不享共享池且卡可用 30,000(< 上限 40,000) 可用取 30,000(較嚴者)
調額單帶 cashAdvanceRatio=60 生效後 CARD 列 ratio=60、上限 120,000(200,000×60%)

★ 測試:CashAdvanceLimitIntegrationTest 7 例全通過(真 Oracle、零替身)。

10.5 誠實揭露

項 說明
CASH_ADVANCE 之判定靠呼叫端 本服務不判斷「這筆是不是預借現金」——holdType 由授權端(authorization-service)依交易類型帶入。若上游帶錯,檢核即失效。目前授權端尚未帶此值(見下)
授權端尚未送 holdType=CASH_ADVANCE authorization-service 之授權流程目前一律送 GENERAL(交易類型 → 預借現金之對映屬其職責)。→ 本批完成的是限額能力,尚未在授權路徑生效。列為後續(需 CR 補 authorization-service 之交易類型對映)
預借比率來源 由呼叫端依商品參數 limit.cash-advance-ratio 帶入(本服務無 card→product 對映,CR-052 C-5);商品參數本身尚未建立(product-service 之參數清單未含此鍵)

11. P4-3 企業子額度(2026-10-17,#A-296)

BEAM PMO 指示「P4-3 企業子額度(需組織樹)…請執行」。語意依 §2.6 凍結值:

上層可用 = 上層總額度 − 上層已用 − 上層自身佔用 − **所有下層佔用之和**(下限 0)

11.1 資料模型(V17)

表/欄 內容
corp_units id/unit_no(唯一)/unit_name/parent_unit_id(根為 NULL)/root_unit_id(反正規化)/depth/settlement_account_id/status
limit_holds.corp_unit_id 佔用歸屬企業單位(可空);與 instrument_id 並存不互斥
額度列 account_limits 之 subject_type='CORP_SUB'、subject_id = corp_units.id

★ 為何要獨立一張表,不在 account_limits 上硬加 parent_id:
① 組織樹是主體結構、額度是掛在結構上的數字,生命週期不同(單位可先建、額度後核);
② 同一單位未來可能有多幣別額度列(account_limits 唯一鍵含 currency),
樹只能有一份,否則多幣別會出現「多棵不一致的樹」。

★ settlement_account_id(額度歸屬帳戶)為何必要:account_limits.account_id 為 NOT NULL,
CORP_SUB 列亦須有歸屬帳戶。企業額度在實務上歸屬企業戶結算帳戶 → 由呼叫端於建立根單位時提供,
子單位繼承;不由系統臆造(CR-053 D-4 同類原則)。

★ 單位不得實體刪除:有歷史額度與佔用 → 僅能以 status='INACTIVE' 停用(本版尚未提供停用端點)。

11.2 端點

端點 說明
POST /api/v1/corp-units 建根/子單位(unitNo 冪等;已存在回既有、不覆寫)
GET /api/v1/corp-units/{unitId}/limit 查額度:同時回「自身佔用」與「下層佔用」——只回一個總數會讓客服無法解釋「上層為何可用變少」
PUT /api/v1/corp-units/{unitId}/limit 設定總額度(風控核定端點)
POST /api/v1/corp-units/{unitId}/holds 建立佔用(檢核走上層鏈)

11.3 三個容易做錯的點(本批刻意處理)

陷阱 後果 本批做法
只檢核自身可用 N 個子單位各自把子額度花光 → 上層被超放 N 倍 檢核走上層鏈(自身→父→…→根),任一層不足即拒絕
把「自身佔用」算進「下層之和」 同一筆佔用被扣兩次 → 可用被低估一倍 兩個查詢分開(corpUnitHolds vs descendantCorpHolds),下層不含自身
遞迴方向寫反 CONNECT BY 回空集合且不報錯 → 靜默低估下層佔用 以真機整合測試鎖住(三層樹,根=750,000)

11.4 真機驗證(Testcontainers 真 Oracle +真遞迴 SQL)

三層樹:根 1,000,000 → 分公司 300,000 → 部門 100,000

動作 驗證
分公司佔用 200,000、部門佔用 50,000 分公司:自身 200,000、下層 50,000、可用 50,000
根查詢 自身 0、下層 250,000、可用 750,000(遞迴兩層都算到)
葉節點(部門) 下層 0、可用 50,000
根 100,000/分公司 80,000/部門 50,000;分公司花光 80,000 後部門再借 30,000 拒絕,訊息指出不足之層=B-003(分公司)——否則維運不知往上要調誰
子單位未帶額度 列存在但 0 元 → 佔用拒絕(不當 0 元放行、也不當無限大)
重複 unitNo 建立 回既有、名稱與額度皆不覆寫

★ 測試:CorpLimitIntegrationTest 6 例全通過。

11.5 誠實揭露

項 說明
企業額度之「申請→覆核」未接 PUT /corp-units/{id}/limit 是核定端點(風控用),非客戶申請路徑。LimitAdjustmentService 目前僅由 instrumentId 判斷 CARD 維度,尚未支援 CORP_SUB 之覆核流程
不發射 limit.hold.created.v1 該事件消費端(授權投影)語意為卡片授權佔用,payload 無企業單位維度;照發會讓投影出現「無載體的佔用」→ 污染卡片級可用額度。故企業額度佔用不進事件流,以本服務即時查詢為權威
停用端點未做 status='INACTIVE' 之值域已在 DDL,但無 API;停用後既有佔用如何處置(釋放?保留?)需客戶口徑
未與卡片綁定 企業卡消費目前不會自動產生 corp_unit_id 之佔用(需 authorization-service 帶企業單位識別);本批完成的是額度模型與檢核能力

12. 授權路徑生效(2026-10-18,#A-297)

BEAM PMO 指示「授權路徑還沒生效 ← 請完成」。§10.5/§11.5 揭露的兩個缺口,本批補上。

12.1 缺口一:授權端一律送 GENERAL(P4-2 的限額能力從未觸發)

項目 內容
問題 LimitPort.hold(...) 不帶型態,RemoteLimitPort 固定用 HoldRequest.general → hold_type 永遠 GENERAL → CASH_ADVANCE 的子上限檢核一次都沒跑過
修法 LimitPort.hold 增 holdType(與 instrumentId);AuthorizeUseCase 以 txType 對映:CASH_ADVANCE→CASH_ADVANCE、INSTALLMENT→INSTALLMENT、其餘(含未知)→GENERAL
★ 未知一律 GENERAL 之意義 不是放行:金額檢核(卡片上限/帳戶可用/預借上限)與型態無關,一律執行;型態只決定「要不要多一道子上限」
測試 AuthorizeUseCaseTest 新增 2 例(CASH_ADVANCE→CASH_ADVANCE;PURCHASE→GENERAL),並斷言 instrumentId 確實傳遞

12.2 ★ 順帶修掉一個更嚴重的缺口:instrumentId 從未傳給額度服務

LimitPort.hold(...) 原本連 instrumentId 都沒有 → limit_holds.instrument_id 永遠為空。
這正是 CR-054 §3.3 量到「34/34 筆佔用無載體」的真正原因之一(不只是「歷史資料」)——
新交易也不會帶。後果:卡片級已用/可用只能取下界,holdAttribution 永遠 PARTIAL。

本批一併補上:授權帶 card.instrumentId() → 真機實查佔用列 INSTRUMENT_ID 有值。

12.3 缺口二:企業卡消費不帶企業單位(P4-3 在授權上等於沒生效)

項目 內容
設計選擇 由 limit-service 自行解析(V19 corp_unit_instruments),不要求授權服務帶 corpUnitId
理由 授權服務沒有(也不該有)企業組織資料;若要求它帶,就得在授權服務複製組織樹或每次多打一次同步查詢。放在額度域:授權只要照常帶 instrumentId → 授權服務零改動、零額外往返
端點 POST /api/v1/corp-units/{unitId}/instruments(一卡一單位;已綁其他單位回 CC-LIM-1003,不靜默改綁)
佔用行為 hold() 解析所屬單位 → 先跑上層鏈檢核(任一層不足即拒絕)→ 佔用列同時寫 instrument_id 與 corp_unit_id

12.4 真機驗證(#A-297;真 Oracle + 真 Kafka + 真 Keycloak)

步驟 結果
發卡(8082) cardId=0dbe10fb…、411111******0335
Kafka cc.card.card.issued → limit-service 自動建立 CARD 權利列(PRODUCT_DEFAULT、金額 0、shared_pool_eligible=1)
查額度 cardLimitConfigured=false、permanentAmount=0、sharedPoolAvailableAmount=1,000,000
佔用 30,000(未綁企業單位) 成功,DB corp_unit_id=NULL、instrument_id 有值
建企業單位(額度 200,000)+綁定該卡 綁定成功
佔用 100,000(授權只帶 instrumentId) 成功,DB corp_unit_id=0A838A7E…(★ 自動歸屬)
企業額度查詢 自身佔用 100,000、可用 100,000(200,000 − 100,000)

★ 測試資料已清理(corp_units 0、limit_holds 回 34、account_limits 回 18)。

12.5 仍未做(誠實揭露)

項 說明
Stand-in 上送仍不帶 instrumentId 本地讀模型(CC_AUTH.LIMIT_HOLDS)無該欄;自行回填等於臆造 → 該類佔用在權威端仍無載體歸屬(如實揭露)
商品參數 limit.cash-advance-ratio 未建 故真機之預借檢核仍需呼叫端明確帶比例;limit.shared-pool-eligible 亦未建(走系統預設,見 CR-057 §9.3)
企業卡「改綁」 目前禁止(回 CC-LIM-1003);改綁流程(含既有佔用如何處置)需客戶口徑
企業額度「申請→覆核」 仍未接 LimitAdjustmentService(PUT /corp-units/{id}/limit 為核定端點)

13. 商品參數落地與消費端接線(2026-10-18,#A-298)

BEAM PMO「依照您的建議推進」→ 把 §4 設計的參數真的建起來並接上消費端。

13.1 建了什麼、刻意不建什麼

參數 處置 理由
product/limit.shared-pool-eligible 建(product-service V3,值 true,效期 2026-01-01 起) 與 V13 之 shared_pool_eligible DEFAULT 1 完全一致 → 建了不改變任何行為,只是把「系統隱含預設」升級為顯式、可稽核、可覆寫的參數(原本只寫在 DDL 的 DEFAULT 裡,沒人看得到)
product/limit.cash-advance-ratio 刻意不建 這是授信政策(預借現金額度佔總額度幾成)。§2.5 的 80% 只是舉例 → 填任何數字都是臆造政策。留空=未設定=預借不可用(fail-closed),待客戶核定
product/limit.approval-threshold-minor 不種 未設時 limit-service 以組態預設 500,000 fail-closed 降級(CR-045 §7.14 A4),已有明確預設 → 再種一份會產生「兩個真相」

13.2 消費端接線(★ 沒有這一步,參數建了也不會生效)

參數 消費端 行為
shared-pool-eligible CardIssuedProjectionService(#A-297 已接) 發卡事件 → CARD 權利列之 shared_pool_eligible
cash-advance-ratio LimitAdjustmentService.resolveCashAdvanceRatio(本批新增) 卡片級調額未帶比例時自動取參;優先序:呼叫端明確帶入 → 商品參數 → 未設定(null,預借不可用)。值域外(非數字/超出 0–100)視同未設定並記 WARN,不得靜默截斷

13.3 ★★ 真機攔下的真實缺陷(本批最大價值)

真機驗證時發現:調額核准後 cardLimitConfigured 仍是 false(金額已寫入 200,000)。

項目 內容
根因 applyAdjustedAmount 只更新金額,沒把 limit_source 由 PRODUCT_DEFAULT 翻成 MANUAL
後果 limitConfigured() 恆為 false → hold() 永遠不套卡片上限 → 已核定額度的卡仍可刷到帳戶總額度
嚴重度 等於 #A-286 修掉的「不享共享池之卡超放」缺陷,在「先發卡、後核定」的卡片上靜默復活
修法 三個 UPDATE(永久/臨時/彈性)一律加 limit_source = 'MANUAL'——套用調額即代表人工核定
回歸測試 CashAdvanceLimitIntegrationTest.核定後須翻為人工核定:權利列 → 核准 → 斷言 cardLimitConfigured=true

★ 教訓:「金額寫進去了」≠「語意對了」。同一張表上並存「金額」與「金額的來源」時,
只更新金額會留下自相矛盾的列(有 200,000 卻說「未核定」),而這種矛盾不會報錯。

13.4 真機驗證(真 Oracle+真 Kafka+真 product-service)

步驟 結果
參數改 limit.shared-pool-eligible=false 權威值已變
投遞發卡事件(真 Kafka) CARD 權利列:PRODUCT_DEFAULT|金額 0|shared_pool_eligible=**0**(★ 參數被實際消費)
參數 limit.cash-advance-ratio=80 已建立
卡片級調額(不帶比例) CARD 列 cash_advance_ratio=80、上限 160,000(200,000×80%)→ ★ 參數被實際消費

腳本:ops/local/e2e/limit_param_loop_e2e.sh。

13.5 ★ 待裁定:業務日之語意(本批未擅自改)

真機驗證時撞到一個跨服務的真實問題:

limit-service 之 LocalDate.now(clock) 取的是 UTC 日期。台北時間 00:00–08:00 之間,
UTC 日期仍是「昨天」→ 取參時會拿到昨天的版本(本次驗證因此一開始取到舊值 true)。

選項 說明
A(建議) 額度域之業務日統一改為 Asia/Taipei(Clock.system(ZoneId.of("Asia/Taipei")))——銀行業務日不應隨 UTC 跳動
B 維持 UTC,並要求參數生效日一律提前一天設定(治標,且易忘)

★ 未擅自改:Clock 影響本服務所有日期語意(臨時額度起訖、覆核門檻版本、預借比例版本),
屬跨功能影響 → 需 BEAM PMO 裁定後另批處理。

13.6 工具教訓(省下後人半小時)

坑 事實
Flyway 的歷史表在 Oracle 是引號小寫識別名 cc_product."flyway_schema_history",欄名亦為 "installed_rank"/"success"——用大寫會 ORA-00904
COMMENT ON TABLE 不可用相鄰字串字面值 Oracle 不自動串接 → ORA-03046(本批首版即踩,V3 因此失敗一次;已改為說明寫在註解)
Kafka CLI 不在 PATH 容器內為 /opt/kafka/bin/kafka-console-producer.sh
bash 變數後接中文全角字元 $VAR( 會被吃進變數名 → unbound variable;一律 ${VAR}(第二次踩)

9. 修訂紀錄

版本 日期 內容
1.5 2026-10-18 商品參數落地+消費端接線(#A-298):product-service V3 建 limit.shared-pool-eligible=true(與 V13 預設一致,行為中性);刻意不建 cash-advance-ratio(授信政策待客戶核定)。新增 LimitAdjustmentService.resolveCashAdvanceRatio(未帶比例時自動取參)。★★ 真機攔下缺陷:調額核准未把 limit_source 翻為 MANUAL → cardLimitConfigured 恆 false → 卡片上限永不檢核(超放缺陷靜默復活);已修+回歸測試。★ 另揭露業務日走 UTC 之跨服務問題(待裁定,未擅自改)
1.4 2026-10-18 授權路徑生效(#A-297):① LimitPort.hold 增 holdType+instrumentId,AuthorizeUseCase 以 txType 對映型態(CASH_ADVANCE→CASH_ADVANCE,未知→GENERAL);② ★ 順帶修掉「instrumentId 從未傳給額度服務」(limit_holds.instrument_id 永遠為空之真因);③ V19 corp_unit_instruments + POST /corp-units/{id}/instruments,hold() 自動解析企業單位並跑上層鏈檢核。真機:發卡→權利列→未核定仍可佔用→綁定後佔用自動歸屬企業單位
1.3 2026-10-17 P4-2/P4-3 落地(#A-296):預借現金(V15 比例欄+V16 值域擴充——真機攔下 ORA-02290 之真實缺口;上限=總額度×ratio、可用=min(上限−已預借, 有效可用);null≠0;7 例真機);企業子額度(V17 組織樹+limit_holds.corp_unit_id;上層可用扣減所有下層佔用;檢核走上層鏈並指出不足之層;6 例真機)。★ 揭露:授權端尚未送 CASH_ADVANCE、企業額度覆核流程未接、企業卡未綁 corp_unit_id
1.2 2026-10-17 §6 維度確認(BEAM PMO):自然人=帳戶級、公司戶=歸戶級;個人與公司額度不混算(附張三/Z 公司例);併記錄採購卡用途限制與 CR-056 身分證 360 之缺口
1.1 2026-10-17 P4-1 落地(#A-289):V13 共享池權利欄+V14 調額單留痕;授權卡片上限檢核(修正「不享共享池之卡超放」);查詢分開呈現(卡可用 vs 共享池剩餘);真機驗證四情境。★ 併揭露實作期第二缺陷(申請時建列會使待覆核卡片被鎖)與其修正
1.0 2026-10-17 開立並凍結 BEAM PMO 口徑:共享池=總額度之共用權利(非額外池);三概念分離(總額度/卡片名目額度/共享池權利);可刷上限與查詢呈現之精確公式;5 萬例子逐情境驗算並揭露現行「不享共享池之卡會超放」(G-1);預借現金為總額度百分比(非獨立額度);企業子額度扣減上層且需組織樹。P4-1 落地、P4-2/P4-3 分批
    (1-1/1)