CR #502 » CR-056_身分證字號360查詢(跨帳戶跨企業之卡片視角).md
CR-056 身分證字號 360 查詢(跨帳戶/跨企業之卡片視角)
| 項目 | 內容 |
|---|---|
| 文件編號 | CR-056 |
| 版本 | 1.4 |
| 狀態 | 360 全鏈路已通並真機驗證:身分證 → party → 卡 → 額度,BFF 一次呼叫取得(§11)。真實客戶資料待提供 |
| 觸發 | BEAM PMO 2026-10-17:「由身分證字號查詢時,要能把張三在 Z 公司的兩張卡訊息帶出來,這一點很重要」 |
| 需求依據 |
CR-055 §1(自然人/公司戶額度隔離之同一情境) |
| 影響模組 |
card-service(查卡清單)、party-service(id_no_hash)、bff-web(聚合) |
| 關聯 |
CR-055(維度與隔離)/CR-052 C-5(無 card→instrument 對映) |
1. BEAM PMO 之情境(原文要點)
張三在 A 銀行有個人信用卡帳戶,總額度 5 萬(不論幾張主卡/附卡)。
Z 公司也在 A 銀行有公司戶,總額度 100 萬;張三持有 Z 的兩張公司卡:
差旅卡 10 萬、採購卡 30 萬(限資產類消費)。
張三在 A 銀行的帳戶總額度仍是 5 萬(個人與公司額度不混算)。
但由身分證字號查詢時,要能把張三在 Z 公司的兩張卡訊息帶出來。
2. 現況盤點(實查)
| # | 事實 | 影響 |
|---|---|---|
| F-1 |
cards 有 account_id/party_id/product_code
|
✅ 卡的歸屬與產品可查 |
| F-2 |
card-service 原本無「依帳戶/依持卡人查卡」端點(僅 byId/byCardNoHash) |
❌ 360 查詢無從起手 → 本 CR 已補(§3) |
| F-3 |
parties 表綁 account_id(account_id NOT NULL REFERENCES accounts(id)) |
★★ 同一自然人在不同帳戶=不同 party 記錄 |
| F-4 |
parties 之身分證僅有 id_no_enc(加密 BLOB)與 id_no_masked(遮罩) |
★★ 無可比較鍵:加密無法直接比對、遮罩不可作比較鍵(專案鐵律) |
| F-5 | 無 id_no_hash |
★★ 跨帳戶 360 查詢目前技術上不可行 |
2.1 為何「依 partyId 查卡」不足以滿足 BEAM PMO 之要求
張三(個人帳戶) → party 記錄 P1(id_no_enc 同一身分證)
張三(Z 公司帳戶)→ party 記錄 P2(**另一筆**,同身分證、不同 account_id)
↓
cards.party_id = P1 → 個人卡;cards.party_id = P2 → 公司卡
-
GET /api/v1/cards?partyId=P1→ 只得個人卡(BEAM PMO 要的兩張公司卡拿不到)。 - 要「一次帶出全部」→ 必須先由身分證字號解析出 P1 與 P2 兩個 partyId → 需
id_no_hash。
3. 已落地(第一步,#A-290)
| 項目 | 內容 |
|---|---|
| 端點 |
GET /api/v1/cards?accountId=…/?partyId=…(至少一項,皆缺 → CC-PLT-4003) |
| 回應 |
CardSummaryView[](cardId/accountId/partyId/cardNoMasked/productCode/bin/expiryDate/status/blockCode/instantIssued) |
| 守界 |
不含卡號雜湊與明碼;不含額度(權威在 limit-service) |
| 已支援 | ① 自然人情境:以 accountId 查即該帳戶所有主/附卡(額度主體=帳戶,CR-055 §6)② 已知 partyId 之公司卡查詢 |
★ 這一步不足以完成 BEAM PMO 之「身分證 → 全部卡」,但它是必要組件(有了卡清單,才能逐卡查額度)。
4. 待裁定:id_no_hash(資安決策,不擅自實作)
4.1 建議方案
| 項 | 建議 |
|---|---|
| 欄位 |
parties.id_no_hash CHAR(64)(SHA-256 hex)+索引 (id_no_hash)
|
| 計算 | 建檔/換證時以正規化後之身分證字號計算(去空白、統一大寫) |
| 是否加鹽 | ★ 建議加「系統級固定鹽」(存於 KeyProvider,不入 DB)——避免彩虹表;但不可用隨機鹽(隨機鹽無法跨記錄比對,失去查詢用途) |
| 回填 | 既有資料需解密 id_no_enc → 重算 hash(一次性 Java 回填程式,需 KeyProvider);⚠️ 不可用 SQL 直接算(DB 無金鑰) |
| 查詢端點 |
GET /api/v1/parties?idNo=…(明文入、內部 hash 比對、不回傳 hash)——⚠️ 需授權與稽核(見 §4.3) |
| 360 聚合 | BFF:parties?idNo → 得所有 partyId → cards?partyId= 逐個 → limit/instruments/{id}/limits 逐卡 |
4.2 為何不能用既有欄位替代
| 替代方案 | 為何不可行 |
|---|---|
用 id_no_masked 比對 |
★ 遮罩值不可作比較鍵(專案鐵律;且 A123****89 大量碰撞) |
解密 id_no_enc 後全表掃描比對 |
每次查詢需解密全表 → 效能與資安皆不可接受 |
用 party_no 比對 |
party_no 為每帳戶獨立編號,跨帳戶不同 |
4.3 需 BEAM PMO 裁定之三點
| # | 問題 | 建議 |
|---|---|---|
| H-1 | 是否同意新增 id_no_hash(含系統級固定鹽)? |
建議同意——這是業界標準做法,且不影響既有加密欄 |
| H-2 | 是否允許「以明文身分證查詢」之 API? | 建議允許但嚴格管制:限客服角色、每次查詢寫稽核軌跡(誰查誰、何時、結果筆數)、回應不含 hash |
| H-3 | 既有資料回填之授權(需解密) | 建議以一次性維運程式執行(本機可先做),並記錄回填筆數 |
⚠️ 未裁定前不動:不加欄、不回填、不開明文查詢 API。
理由:id_no_hash涉及個資處理政策(可比較鍵=可關聯),屬資安決策,不應由實作端自行決定。
5. 分批
| 批次 | 內容 | 依賴 |
|---|---|---|
| 第一步 | 查卡清單端點(依帳戶/持卡人) | ✅ 已完成 |
| 第二步 |
id_no_hash 欄位+建檔計算+回填 |
H-1/H-3 |
| 第三步 |
parties?idNo= 查詢端點(含稽核) |
H-2 |
| 第四步 | BFF 360 聚合(卡清單+逐卡額度+所屬企業) | 第二、三步 |
6. 順帶記錄:採購卡之「限資產類消費」
BEAM PMO 提到採購卡「只能限制在資產類的消費」——此為用途限制(MCC 白名單),
現行可由 special_limits.scope_ref(MCC 白名單,逗號分隔)承載(CR-035 §2.1)。
⚠️ 但 special_limits 為帳戶層;若客戶要求「卡片層之用途限制」,需另立資料模型
(cards 或 account_limits(CARD) 加用途限制欄)。本 CR 不動,僅記錄待辦。
8. H-1/H-2/H-3 執行結果(2026-10-17,#A-291)
BEAM PMO 2026-10-17 核准:H-1 同意(業界標準、360 視角重點)/H-2 允許但嚴格管制/H-3 一次性維運程式並記錄筆數。
8.1 H-1:id_no_hash(V5)
| 項 | 實作 |
|---|---|
| 欄位 |
parties.id_no_hash CHAR(64)+索引 ix_parties_id_no_hash
|
| 計算 |
SHA-256(系統級固定鹽 ∥ 正規化字號);正規化=去空白+轉大寫
|
| 鹽值 |
cardcore.pii.id-no-salt;缺值即啟動失敗(fail-closed,不以預設頂替) |
| 元件 |
IdNoHasher(6 例單測:大小寫/空白變體同 hash、不同鹽不同 hash、空值拒絕、缺鹽失敗) |
8.2 H-2:PII 查詢端點(POST /api/v1/party-lookups)
★ 刻意用 POST 而非 GET:身分證若置於 query string 會進入 access log/瀏覽器歷史/Referer。
| 項 | 實作/真機結果 |
|---|---|
| 權限 | 角色取自已驗證權杖 realm_access.roles;不在 cardcore.pii.lookup-allowed-roles 即拒絕 |
真機(客服 demo.csr) |
查到 1 筆(P001 王小明 A1******89) |
真機(持卡人 demo.holder) |
403 CC-PLT-4030(訊息列出角色與允許清單) |
| 查無 | 空陣列(非 404——「查無此人」與「無權限」語意不同) |
| 稽核 |
pii_access_audits 實測 2 筆:result_count=1 與 result_count=0(★ 探測行為須可見);記錄 actor/role/hash 前綴(非原文)
|
8.3 H-3:回填——實測 0 筆可回填(如實揭露)
| 項 | 結果 |
|---|---|
| 工具 |
IdNoHashBackfillRunner(--cardcore.pii.backfill-id-no-hash=true 啟用;可重跑、冪等、逐筆錯誤不中斷) |
| 實查 |
CC_PARTY.PARTIES 共 2 筆,ID_NO_ENC 全部 NULL → 候選 0 筆
|
| 結論 | 回填無資料可做——這不是程式缺陷,是資料來源缺口 |
8.4 ★★ 根本缺口:身分證字號從未進入系統
| # | 事實 | 影響 |
|---|---|---|
| N-1 | party-service 只有 GET /parties/{id} 與 POST /parties/{id}/flags——無建檔端點
|
無處接收身分證 |
| N-2 |
id_no_enc 全為 NULL(2/2) |
無密文可解密、無 hash 可算 |
| N-3 | 現有 2 筆為 seed 資料 | 不代表真實開戶流程 |
→ 360 查詢目前「技術就緒、資料為空」:端點、權限、稽核、hash 皆已備,
但查不到任何人(因為沒有任何 party 有 hash)。
需 BEAM PMO 裁定(N-Q):身分證字號從哪裡進來?
| 選項 | 說明 | 影響 |
|---|---|---|
| (a) 開戶/進件流程帶入 | 由 card-applications(進件)或帳戶開立時寫入 party |
需在該流程加「身分證」欄位並呼叫 party(目前無此路徑) |
| (b) 由外部核心匯入 | 遷移時自既有系統匯入身分證(加密+hash) | 需客戶提供匯出檔與欄位對映 |
| (c) 客服補登端點 | 新增 PUT /parties/{id}/id-no(權限+稽核) |
最快可動,但屬「事後補資料」,量大時成本高 |
★ 建議 (a)+(b) 並行:(b) 解既有資料、(a) 保新資料正確;(c) 僅作例外補救。
⚠️ 未定前不臆造:不建假資料、不以遮罩推導原文。
8.5 驗證
party-service 68 測 0 失敗(新增 7:6 例 hash + 1 例 PII 端點 400);
契約 114 端點/0 錯誤;verify_contract_dto 不符 0;validate_sql 違規 0;三掃描器 0。
9. (a)+(b) 執行結果(2026-10-17,#A-292)
BEAM PMO 裁定 (a)+(b):開戶/進件帶入 + 外部核心匯入。
9.1 補上 N-1 缺口:當事人建檔端點
盤點確認:本服務原本只有查詢與旗標、id_no_enc 從未寫入 → (a)(b) 皆無處落地。
故先補建檔能力,兩個入口共用同一條路徑(避免兩套寫入邏輯漂移):
| 端點 | POST /api/v1/parties |
|---|---|
| 請求 |
RegisterPartyRequest(accountId/partyNo/partyType/name/idNo 明文/birthDate/relationType) |
| 處理 | 明文僅用於 ①FieldEncryptor → id_no_enc ②IdNoHasher → id_no_hash ③PartyIdMasker → id_no_masked;寫入後即丟棄
|
| 防重 |
partyNo 唯一鍵(重複 → CC-PLT-4003「建檔不覆寫既有資料」)——故不需冪等鍵
|
| 稽核 |
pii_access_audits 以 PARTY_REGISTER 型別留痕(V6 擴充值域) |
| 回應 |
PartyView(僅遮罩,不含 hash 與明文) |
真機(客服 demo.csr):
- 建檔
P900→ 201,idNoMasked=A1******89 - DB 實查:
ID_NO_ENC65 bytes(密文)、ID_NO_HASH=41A3E6830E07…✅ - 360 查詢以
a123 456789(小寫+空白) → 仍查到P900(正規化生效) - 重複建檔同
partyNo→ 400「建檔不覆寫既有資料」 - 稽核 2 筆:
PARTY_REGISTER+ID_NO_LOOKUP
9.2 (b) 外部核心匯入
PartyImportRunner(--cardcore.pii.import-parties=<csv>):
| 項 | 實作 |
|---|---|
| 格式 | UTF-8 CSV(表頭):partyNo,accountId,name,partyType,idNo,birthDate,relationType
|
| 路徑 |
逐筆呼叫 PartyRegistrationService.register(與 (a) 同一條路徑) |
| 可重跑 | 已存在之 partyNo → 跳過並計數(不覆寫) |
| 失敗不中斷 | 單筆失敗記錄續行;結束輸出「總筆數/成功/跳過/失敗」=回填紀錄 |
| 明碼 | 不複製檔案、不寫 log(失敗訊息只帶 partyNo 與錯誤碼);執行後應刪檔 |
真機:1 筆 CSV → 日誌「總筆數 1、成功 1、跳過 0、失敗 0」;DB P901 有遮罩 C1******89 與 hash ✅
9.3 仍未做(誠實揭露)
| 項 | 說明 |
|---|---|
| 進件流程接線 | (a) 之「建檔端點」已備,但 card-service 之進件(card-applications)仍無 party 欄位——需在進件請求加 partyId 並由前端/流程先建 party。屬 card-service 資料模型變更,另批
|
| 既有 2 筆 seed | 仍無 hash(無密文可回填)——須由 (b) 匯入真實資料覆蓋,或客服補登 |
| 客服補登端點 | (c) 未做(BEAM PMO 選 a+b,c 僅列備案) |
9.4 驗證
party-service 68 測 0 失敗;契約 115 端點/0 錯誤;verify_contract_dto 不符 0;
validate_sql 違規 0;三掃描器 0;Mermaid 0。
10. 進件接線+360 端到端(2026-10-17,#A-293)
BEAM PMO 指示「先做完進件流程接線,再匯入真實資料把 360 跑起來」。
10.1 進件接線(V10)
| 項 | 內容 |
|---|---|
| DDL |
card_applications.party_id RAW(16)(可空——V10 前案件無申請人,不得臆造回填)+ix_card_app_party
|
| 契約 |
CardApplicationRequest.partyId(nullable) |
| 四層 | 領域建構子 overload(含 partyId)→ received(...) overload → service create(accountId, partyId, …) → controller 傳遞 |
| ★ 存在性 |
不驗證——本服務 ACL 未允許 card→party(CR-052 C-5);party_id 由呼叫端保證(開戶流程先建 party 再進件)。文件與契約皆已載明 |
真機:建 party P910(partyId=4d2abd3f…)→ 進件帶該 partyId → 201/APPLY_NO=APP2026101738705406;
DB 實查 PARTY_ID=4D2ABD3FB5BD404E8D00147310FD68E8 與 partyId 一致 ✅
10.2 ★★ 360 跨帳戶端到端(張三情境)
以 (b) 匯入 3 筆(同一身分證、3 個帳戶)模擬 BEAM PMO 之「張三/Z 公司」情境:
| partyNo | 名稱 | 類型 | 帳戶 |
|---|---|---|---|
| P920 | 張三(個人) | PRIMARY | …0001 |
| P921 | 張三(Z公司-差旅) | COMPANY | …0002 |
| P922 | 張三(Z公司-採購) | COMPANY | …0010 |
匯入日誌:★ 身分證匯入完成:總筆數 3、成功 3、跳過 0、失敗 0
★ 360 查詢(POST /api/v1/party-lookups {"idNo":"D999888777"}):
count = 3
P920 | 張三(個人) | PRIMARY | D9******77
P921 | 張三(Z公司-差旅) | COMPANY | D9******77
P922 | 張三(Z公司-採購) | COMPANY | D9******77
→ 一次查詢即帶出跨帳戶之全部記錄(含公司卡之持卡人身份)——BEAM PMO 要求之「360 視角」成立。
★ 可重跑(冪等):同檔重跑 → 總筆數 3、成功 0、跳過(已存在)3、失敗 0,DB 筆數不變。
10.3 誠實揭露
| 項 | 說明 |
|---|---|
| 示範資料非真實 | §10.2 之 3 筆為虛構示範(已清理),僅證明鏈路;客戶真實資料尚未提供 |
| 範本 | 已置 ops/local/pii-import-sample.csv(明確標示虛構)+ops/local/README-pii-import.md(操作步驟) |
| 360 目前只到「人+進件」 | 卡清單(GET /api/v1/cards?partyId=)與額度(/instruments/{id}/limits)需實際發卡後才有資料;本輪未走完整發卡流程 |
| BFF 聚合 | 未實作(需串 party→cards→limits 三服務);目前由呼叫端自行串接 |
10.4 驗證
card-service 149 測 0 失敗、party-service 68 測 0 失敗;
契約 115 端點/0 錯誤;verify_contract_dto 不符 0;validate_sql 違規 0;三掃描器 0;Mermaid 0。
11. 360 全鏈路(BFF 聚合)+真機(2026-10-17,#A-294)
BEAM PMO 指示「把 360 這條鏈路完善」。
11.1 補上「卡 → 載體」對映
CardSummaryView 加 instrumentId(由 instruments.card_id 批次取,避免 N+1)。
★ 卡若尚未註冊載體 → instrumentId=null(該卡無額度可查,非錯誤)。
11.2 BFF 360 端點(POST /api/v1/bff/customer-360)
| 項 | 內容 |
|---|---|
| 鍵 | 身分證明文置於 body(不進 URL) |
| 流程 | 身分證 → party-lookups → 各 party → cards?partyId= → 各卡 instruments/{id}/limits
|
| ★ 權限 | BFF 為 token-relay 模式(SAD-005 §3.4)→ 轉傳使用者權杖,故 PII 權限由 party-service 依使用者角色判定(非服務身分) |
| 降級 | 各層獨立:某卡查不到額度 → 該卡 limitsSection=UNAVAILABLE,不拖垮其他;parties 失敗則整體 UNAVAILABLE 並保留原錯誤碼(CC-PLT-4030 不吞成 500) |
| 資料 | 下游原樣轉出(不重命名欄位)——避免 BFF 成為第二個契約真相 |
11.3 ★★ 真機(4 服務:party 8084/card 8082/limit 8083/bff 8101)
| 步驟 | 結果 |
|---|---|
① 建 party(E111222333) |
partyId=cfe8320a… |
| ② 發卡 |
cardId=54665fa6…、411111******0098
|
| ③ 註冊載體(PHYSICAL) | instrumentId=2a136f2c… |
| ④ 設卡片額度 200,000(享共享池) | EFFECTIVE |
| ⑤ BFF 360 一次呼叫 |
idNoMasked=E1******33、partyCount=1、partiesSection=OKP930 360測試 → cardsSection=OK411111******0098、instrumentId=2a136f2c…limitsSection=OK:permanent=200,000、available=200,000、sharedPool=9,800,000
|
★ sharedPool=9,800,000 = 總額度 10,000,000 − 帳戶已用 200,000 − 佔用 0(與共享池語意一致)。
11.4 仍未做(誠實揭露)
| 項 | 說明 |
|---|---|
| 已補(見 §12) | |
| BFF 僅回原始資料 | 未做欄位裁切/重命名;前端需自行解讀下游結構 |
| 真實資料 | 仍待客戶提供(範本見 ops/local/) |
12. 發卡自動建載體(2026-10-17,#A-295)
BEAM PMO 裁定:「『發卡自動建載體』這個洞(小改動,但讓 360 對新卡自動生效)← 麻煩您補上」。
本節記錄為何這是洞、改了什麼、如何證明閉環。
12.1 問題本質:instrumentId 是額度的唯一入口
limit-service 的卡片級額度查詢是 GET /api/v1/instruments/{instrumentId}/limits
——鍵是 instrumentId,不是 cardId。而 instruments 表在本輪之前
只能靠 POST /api/v1/instruments 手動註冊才會有人寫入。
因此新卡(未手動註冊載體)在 360 的表現是:
parties ✅ → cards ✅(但 instrumentId = null)→ limits ✗(無鍵可查)
→ 「360 對新卡不生效」不是 BFF 的問題,是發卡鏈路缺一環。
這正是 #A-294 §11.4 誠實揭露的那一項。
12.2 改動
| 層 | 內容 |
|---|---|
| 應用層 |
CardIssueService 注入 InstrumentRepository;issue() 在 cardRepository.save(...) 之後、同一交易內建立 PHYSICAL 載體 |
| 語意 |
Instrument.register(UUID, Type.PHYSICAL, cardId, cardNoHash, null, null, accountId)——cardNoHash 取已寫入之卡片的雜湊,確保 card_id/card_no_hash 同源 |
| ★ 失敗語意 | 建載體失敗 → 整筆回滾(含已寫入之卡片)。理由:留一張「有卡無載體」的卡=假成功,該卡在授權與 360 都缺一環且需人工補登 |
| 不變式 |
instruments 對 (type, natural_key) 有唯一約束,且 InstrumentService.register 於寫入前先 existsByNaturalKey → 手動重複註冊得 CC-CARD-1004,不會產生第二列
|
為何不用事件驅動(非同步建載體):載體是發卡的固有部分,不是外部副作用;
非同步會產生「發卡成功、載體稍後才到」的視窗,而該視窗內 360 仍缺額度。
故採同交易(與 pool.reserve 之樂觀鎖衝突回滾語意一致)。
12.3 真機驗證(4 服務:card 8082/limit 8083/party 8084/bff 8101)
★ 關鍵:全程未呼叫 POST /api/v1/instruments。
| 步驟 | 結果 |
|---|---|
① 建 party(F111222333) |
partyId=74363cef… |
| ② 發卡(PLATINUM) |
cardId=8a03b324-80b2-4e8d-b4b1-fe788c08f25b、411111******2508
|
③ DB 實查 INSTRUMENTS
|
B8F1A461D7804E9690B877DB9B1DBA5C、INSTRUMENT_TYPE=PHYSICAL、CARD_NO_HASH=e8badf7c03… ✅ 自動出現
|
④ GET /cards?partyId=
|
instrumentId=b8f1a461-d780-4e96-90b8-77db9b1dba5c ✅ |
| ⑤ 設卡片額度 150,000 | EFFECTIVE |
| ⑥ BFF 360 一次呼叫 |
idNoMasked=F1******33、partyCount=1、P940 自動載體測試instrumentId=b8f1a461…limitsSection=OK:permanent=150,000、available=150,000、sharedPool=9,800,000
|
★ 對照 #A-294 §11.3:該次必須手動註冊載體才有 instrumentId;
本次發卡即得,360 對新卡自動生效。
清理:已刪 LIMIT_ADJUSTMENTS/ACCOUNT_LIMITS 之 CARD 列、INSTRUMENTS/CARDS、
PARTIES(P940)、PII_ACCESS_AUDITS;parties 筆數回到 2。
12.4 測試與關卡
| 項 | 結果 |
|---|---|
CardIssueServiceTest |
新增 Instruments 替身(其餘方法明確拋 UnsupportedOperationException——避免假綠)+4 項斷言:載體數 1、型別 PHYSICAL、cardId 相符、accountId 相符、cardNoHash 非空 |
card-service 全套 |
BUILD SUCCESSFUL |
verify_contract_dto |
18 服務、不符 0、需確認 0、INFO 9 |
validate_sql |
全庫 101 檔、違規 0(登錄檔警告 0) |
scan_scaffold/scan_mockdata/scan_faketest(card-service) |
各 0 |
validate_mermaid |
0 錯 |
12.5 仍未做(誠實揭露)
| 項 | 說明 |
|---|---|
card.card.issued.v1 未帶 instrumentId |
目前無消費端需要(limit-service 之 CARD 級額度列依 CR-055 於調額核准時才建立,不隨發卡建立)→ 未改事件,避免「有人發、沒人收」。若未來要讓發卡自動生成 CARD 級額度列,須同時補事件欄位與消費端,屆時另立 CR |
| 虛擬卡/換卡路徑 | 現行 CardIssueService.issue() 是唯一建立 cards 之入口(已 grep 確認 cardRepository.save 僅 1 處)→ 本改動覆蓋全部發卡;未來新增換卡/補發流程時必須同樣建載體 |
| 卡號重用 | 載體自然鍵為卡號雜湊;卡號由號碼池配置、不重用 → 無碰撞風險。若未來開放卡號重用,需先處理載體歷史 |
7. 修訂紀錄
| 版本 | 日期 | 內容 |
|---|---|---|
| 1.5 | 2026-10-17 |
發卡自動建載體(#A-295):CardIssueService.issue() 同交易建立 PHYSICAL 載體(失敗即整筆回滾,不留「有卡無載體」之假成功)。真機全程未呼叫 POST /instruments:發卡 → DB 自動出現 INSTRUMENTS(PHYSICAL+CARD_NO_HASH)→ GET /cards 帶 instrumentId → BFF 360 一次呼叫回額度(sharedPool 9,800,000)。★ 揭露:card.card.issued.v1 未帶 instrumentId(目前無消費端,依「不發無人收之事件」原則不動) |
| 1.4 | 2026-10-17 |
360 全鏈路(#A-294):CardSummaryView 加 instrumentId(批次、避免 N+1);BFF POST /api/v1/bff/customer-360(token-relay 轉傳使用者權杖、分層獨立降級、下游原樣轉出)。真機 4 服務:建人→發卡→註冊載體→設額度→一次呼叫回「人+卡+額度」(sharedPool 9,800,000)。★ 揭露:發卡未自動建載體(需手動 POST /instruments) |
| 1.3 | 2026-10-17 |
進件接線+360 端到端(#A-293):V10 card_applications.party_id(可空、不跨服務驗證);四層與契約同步。真機:建 party → 進件帶 partyId → DB 一致;匯入張三 3 帳戶 → 一次查詢回 3 筆(跨帳戶 360 成立);重跑冪等(跳過 3)。★ 揭露:示範資料非真實、360 未含卡清單與額度(需實際發卡)、BFF 聚合未做 |
| 1.2 | 2026-10-17 |
BEAM PMO 裁 (a)+(b) → 已實作(#A-292):補 N-1 缺口(POST /api/v1/parties 建檔:加密+hash+遮罩+PARTY_REGISTER 稽核、partyNo 防重);外部匯入 PartyImportRunner(CSV、逐筆走同一路徑、可重跑、筆數即回填紀錄)。真機:建檔 → DB 有密文與 hash → 360 查得到(小寫/空白亦命中)→ 重複建檔 400。★ 未做:進件流程接線(card-applications 無 party 欄位) |
| 1.1 | 2026-10-17 |
H-1~H-3 落地(#A-291):V5 加 id_no_hash+pii_access_audits;POST /api/v1/party-lookups(body 傳 PII、角色管制、含 0 筆之稽核);回填 runner(實測 0 筆——無密文)。★ 揭露根本缺口 N-1~N-3:本服務無建檔端點、id_no_enc 從未寫入 → 360 查詢「技術就緒、資料為空」,列 N-Q (a)(b)(c) 待裁 |
| 1.0 | 2026-10-17 | 開立:承載 BEAM PMO「身分證字號 360 查詢」需求。盤點揭露 F-3/F-4/F-5(parties 綁帳戶、身分證僅加密與遮罩、無 id_no_hash)→ 跨帳戶 360 目前不可行;已落地第一步(`GET /api/v1/cards?accountId |