Project

General

Profile

CR #201 » SCS-004_當事人服務程式設計規格.md

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

 

SCS-004 當事人服務(party-service)程式設計規格

項目 內容
文件編號 SCS-004
版本 1.3(CR-046 B2-6:投影讀模型接入可選 instrumentId;V4 遷移、JDBC COALESCE 舊事件相容與 CardStatusView 映射)。前版 1.2(CR-031 §4.1.1:真機驗證通過——§9 補 UT-PTY-008~012 與驗證註記;更正 §7.5 編號(v1.1 誤植為 §7.1);更正本表格式;補種子對齊說明)。前版 1.1(CR-031 §2:新增 §7.5 卡片狀態投影——跨服務讀模型;補上 SAD-002 §3.4 載明卻從未實作之訂閱)。前版 1.0
狀態 已核定(規格先行之第一個示範)
對應需求 ISS-2.1(信用卡持卡人、歸戶管理)、POC-10(市區停車)
服務埠號 8084
套件根 com.beam.cardcore.party
權威 schema CC_PARTY
關聯文件 SDD-001 §5.1、BRD-002、BRD-004 POC-10、GOV-005

1. 對應需求

FR 編號 需求名稱 來源
ISS-2.1 信用卡持卡人、歸戶管理 建議書發卡章節
POC-10 市區停車(身分參數設定 → 符合資格名單產出 → 停車 → 資格判斷) BRD-004 §3.10

1.1 上層設計依據(本規格不重複這些圖,僅引用)

圖種 文件 數量(實測) 本服務相關
ER-Diagram SDD-001_領域模型與ER-Diagram 16 個 erDiagram PARTIES/ACCOUNTS/BLOCK_CODE_HISTORY 之實體關係
DFD SDD-002_DFD與流程分解 10 個 flowchart(L0/L1/L2) 持卡人資料流(ISS-2.1)
Sequence SDD-003_Sequence-Diagrams 15 個 sequenceDiagram 歸戶/持卡人查詢時序
狀態機 SDD-005_狀態機設計 10 個 stateDiagram ⚠️ 當事人狀態機未定義(見 §3.2、§10)
API Contract SDD-006 + contracts/openapi/party-service.yaml 17 份 本服務 4 支端點

規範:SCS 為程式/服務層級,設計圖一律引用 SDD-* 系列,
不在 SCS 內重複繪製(避免兩套圖不同步)。若需新增圖,應先更新 SDD-*,再於 SCS 引用。
這也是「規格先行」與「單一真實來源」的體現。

POC-10 對本服務之要求:須能設定並查詢持卡人之身分參數(貼標 flags):

  • 每日/每月優惠停車總次數與總時數
  • 達月限次簡訊通知註記
  • 限定停車場註記

2. 服務職責與邊界

承作:

  • 持卡人(Party)基本資料與身分參數(貼標 flags)之維護與查詢
  • 歸戶(Account)基本資料查詢
  • 歸戶管制碼(block_codes)設定

不做(屬他服務):

  • 卡片核發與生命週期 → card-service
  • 授權決策 → authorization-service
  • 額度 → limit-service
  • 帳務入帳 → posting-service
  • 實際停車交易與優惠計算 → 屬收單/行銷範疇,本服務**僅提供資格參數(flags)供其判斷

3. 領域模型

3.1 聚合與值物件

型別 職責
Party 持卡人聚合(不可變)。含身分別、姓名、身分證遮罩、生日、flags、狀態
PartyType 身分別列舉(PRIMARY/SUPPLEMENTARY/COMPANY)
Account 歸戶聚合(帳號、戶別、狀態)
PartyFlags 身分參數貼標(POC-10 之優惠停車參數

設計要點:

  1. 不可變:所有轉換回傳新實例(CSP-001 §4.2)。
  2. **個資不落地明碼:身分證/手機/Email 皆以 *_enc(BLOB)儲存,對外僅提供 *_masked。

3.2 狀態機:當事人/歸戶狀態

⚠️ SDD-005 未定義當事人狀態機。本服務不自創狀態機(避免無依據設計):

  • PARTIES.STATUS/ACCOUNTS.STATUS 僅攜帶與檢核,不做轉換邏輯。
  • 若未來需要,應先於 SDD-005 增訂狀態機後再實作。此缺口已記錄於 §10。
維度 欄位 維護者 用途
當事人狀態 PARTIES.status 本服務(僅設定,無轉換邏輯) 顯示/稽核

| 歸戶狀態 | ACCOUNTS.status | 本服務(僅設定) | 顯示/稽核 |

3.3 身分參數(flags)— POC-10

FLAGS 為 JSON NOT NULL。POC-10 之參數結構(建議,須與行銷/收單確認):

{
  "parking": {
    "dailyLimitCount": 2,
    "monthlyLimitCount": 20,
    "dailyLimitHours": 4,
    "monthlyLimitHours": 60,
    "smsOnMonthlyLimit": true,
    "restrictedLots": ["LOT-A", "LOT-B"]
  }
}

設計原則:

  • flags 採 JSON 欄位存放(DDL 已定義 FLAGS JSON NOT NULL),不額外開表(避免過度設計;參數異動頻率低)

  • 領域層以 PartyFlags record 承載,由基礎設施層做 JSON ↔ record 轉換(領域層不碰 JSON 序列化框架)

3.4 管制碼(block_codes)

ACCOUNTS.BLOCK_CODE + BLOCK_CODE_HISTORY(歷程表)。

  • 管制碼設定時**須寫入歷程(稽核留痕)— 銀行案必要。同一交易內完成。

4. 程式結構

套件/類別 職責 對應需求
domain/model/Party 持卡人聚合 ISS-2.1
domain/model/PartyType 身分別列舉 ISS-2.1
domain/model/PartyFlags 身分參數貼標 POC-10
domain/model/Account 歸戶聚合 ISS-2.1
domain/port/PartyRepository 持卡人持久化埠 ISS-2.1
domain/port/AccountRepository 歸戶持久化埠 ISS-2.1
application/PartyQueryService 持卡人查詢 ISS-2.1
application/PartyFlagService 貼標設定(POC-10 參數) POC-10
application/AccountService 歸戶查詢 + 管制碼設定(含歷程) ISS-2.1
infrastructure/jdbc/JdbcPartyRepository JDBC 實作 ISS-2.1
infrastructure/jdbc/JdbcAccountRepository JDBC 實作 ISS-2.1
interfaces/rest/PartyController 持卡人/貼標端點 ISS-2.1, POC-10
interfaces/rest/AccountController 歸戶/管制碼端點 ISS-2.1
infrastructure/crypto/PartyPiiCipher 身分證/手機/Email 加解密(common-security) SAD-005 §5

5. 資料模型(實測值,2026-09-19)

表 關鍵欄位 實測約束
parties id(RAW,PK)、account_id(RAW,NOT NULL)、party_no、party_type、name、id_no_enc(BLOB)、id_no_masked(NOT NULL)、birth_date、mobile_enc(BLOB)、email_enc(BLOB)、relation_type、flags(JSON,NOT NULL)、status(NOT NULL)、created_at、updated_at、version party_type IN ('PRIMARY','SUPPLEMENTARY','COMPANY');ACCOUNT_ID/PARTY_NO/PARTY_TYPE/NAME/ID_NO_MASKED/FLAGS/STATUS/CREATED_AT/UPDATED_AT/VERSION 皆 NOT NULL(PII 明文欄 id_no_enc/mobile_enc/email_enc 可為 NULL(未收集時)。birth_date、relation_type 可為 NULL。

| accounts | id(RAW,PK)、account_no、cif_no、account_type、block_code、statement_cycle、statement_day、status、created_at、updated_at、version | (既有 V1 基線;BLOCK_CODE 可空 |

| block_code_history | id、account_id、block_code、reason、operator_id、created_at | 歷程表(稽核) |

| card_status_caches | id(RAW,PK)、card_no_hash(CHAR(256),UNIQUE)、card_id、instrument_id(RAW,可空)、account_id、card_no_masked、status、block_code、expiry_date、last_sequence_no、created_at、updated_at、version | V3 新增、V4 補載體鍵(CR-031 §2/CR-046 B2-6)。跨服務投影——由 card.card.status-changed.v1 餵養,非本服務之權威資料;舊列/舊事件可為 null |

| consumed_events | id(RAW,PK)、event_id(UNIQUE)、event_type、aggregate_id、sequence_no、created_at | V3 新增。消費冪等表——形狀與其他服務一致(SAD-004 §5.4) |

⚠️ card_no_hash 為 CHAR(256):綁定參數為 VARCHAR2 → 比對一律 TRIM(同 authorization-service 之同名表之註記)。
⚠️ 種子資料見 §9;accounts 之歸戶主檔須與 card/limit 之種子對齊(CR-031 §4.4)。


6. API 設計(依 contracts/openapi/party-service.yaml)

端點 說明 錯誤碼
GET /api/v1/parties/{partyId} 查詢持卡人(含 flags) CC-PTY-1001
POST /api/v1/parties/{partyId}/flags 貼標設定(POC-10 身分參數) CC-PTY-1001
GET /api/v1/accounts/{accountId} 查詢歸戶 CC-PTY-1001
GET /api/v1/accounts/{accountId}/cards 查詢該歸戶之卡片狀態清單(跨服務讀模型;CR-031 §2、§7.5) CC-PTY-4040(歸戶不存在)
POST /api/v1/accounts/{accountId}/block-codes 管制碼設定(寫歷程) CC-PTY-1001

回應要點:

  • 持卡人/歸戶查詢回傳僅含遮罩個資(id_no_masked),不含明文或密文。

  • 貼標設定回傳更新後之完整 flags。


7. 關鍵邏輯

7.1 個資處理(SAD-005 §5)**

  • 寫入:id_no_enc/mobile_enc/email_enc 以 AES-GCM 加密(common-security 之 AesGcmFieldEncryptor)。
  • 讀取:僅回傳遮罩欄;**明文不進日誌、不進例外訊息。

7.2 貼標設定(POC-10)

  • 採**整包覆寫(flags 整體更新)。理由:參數結構單純、避免部分更新之合併複雜度。
  • 須驗證 dailyLimitCount/monthlyLimitCount 為非負整數;flags 不得為 null(DDL NOT NULL)。

7.3 管制碼設定

  • 更新 accounts.block_code + 新增 block_code_history(同一交易)。
  • 記錄 operator_id、reason(稽核留痕)。

7.4 交易邊界**

  • 貼標更新:單表更新,交易包住 UPDATE parties。
  • 管制碼:UPDATE accounts + INSERT block_code_history 同一交易。

7.5 卡片狀態投影(跨服務讀模型)——2026-10-01 新增(CR-031 §2)

⚠️ 編號更正:本節於 SCS-004 v1.1 初稿誤植為 §7.1,與既有「§7.1 個資處理」衝突;
v1.2 更正為 §7.5(CR-031 §4.1 之引用同步更正)。

緣由:SAD-002 §3.4 明載本服務訂閱 card.card.status-changed.v1,
惟從未實作(同 V2 載明卻無餵養者之型)。本節補上。

★ 為何是投影,而非同步查詢(架構上的唯一解)

cardcore-service-acl.yml 之 card-service 只允許 cc-authorization-service 呼叫
→ 本服務(及任何非授權場景:客服/風控/報表)在架構上無法同步取得卡片狀態。
故事件投影是唯一途徑(CR-031 §2.1)。

與 CR-030 §2 #16(同步為主、事件為輔)之關係:該通則禁止「重做同步已做之寫入」。
本項不存在可重做的同步路徑(ACL 即擋死)→ 屬「做沒人做的事」→ 應做。

訂閱主題:cc.card.card.status-changed(SAD-004 §5.4 命名規則)。

項目 值
事件型別 card.card.status-changed.v1
分區鍵 cardId(信封 aggregateId)
存放地 CC_PARTY.card_status_caches
業務鍵 card_no_hash(UNIQUE;與 authorization 之同名表同一慣例)
更新語意 upsert by card_no_hash;last_sequence_no 供亂序防護
讀取端點 GET /api/v1/accounts/{accountId}/cards → CardStatusView[]

處理(複用既有消費端之三步,皆於同一交易):
① 冪等(consumed_events.event_id)→ ② 亂序(sequence_no 落後於投影列之 last_sequence_no → 略過、不覆寫較新狀態)
→ ③ upsert + 寫 consumed_events。
⚠️ 未知事件型別一律略過並記 WARN(不拋錯——否則新增事件會毒化消費者)。

讀取語意:

情境 回應
歸戶不存在 404 CC-PTY-4040(與 GET /accounts/{id} 一致)
歸戶存在但尚無卡片投影 200 + 空陣列(非 404——「尚無卡片」是合法狀態)

⚠️ 與 authorization-service 之同名表之關係(誠實揭露):二者同形狀、不同語意——
authorization 之投影供授權熱路徑(免同步查卡況,<100ms);本服務之投影供歸戶視圖。
刻意不共用:跨服務共用讀模型=跨庫耦合(違反「一服務一 schema」SAD-003 §1 DP-01)。
兩份各自投影是 CQRS 之標準取捨(以儲存換解耦)。

⚠️ 投影為唯讀且最終一致(SAD-003 §4.2):卡片狀態之權威仍在 card-service;
本服務僅提供歸戶視角之查詢,不得作為寫入來源。
⚠️ 不含 PAN:cardNoMasked 為遮罩值(先例見 card.card.status-changed.v1)——符合 PCI DSS。
BR-16 補充:投影同步保留事件之 instrumentId(交易載體最小識別),回應 CardStatusView 一併揭露;舊事件/舊列無值時回 null。instrumentId 是內部識別,不以 cardNoMasked 作比較鍵。

8. 外部依賴與降級**

依賴 用途 失敗處理
Oracle CC_PARTY 權威儲存 服務不可用(無降級 — 當事人資料無本地快取,查無即回 CC-PTY-1001。

無 Stand-in 降級:當事人資料非即時交易路徑,授權服務未直接呼叫本服務(授權僅用卡片/額度)。故不設計降級,避免不必要的複雜度。若未來授權需查當事人,再另行評估(含快取一致性)。


9. 測試案例(對應 TRP-001)**

編號 情境 預期
UT-PTY-001 查詢持卡人(存在) 200,回傳遮罩個資 + flags
UT-PTY-002 查詢持卡人(不存在) 404(CC-PTY-1001)
UT-PTY-003 貼標設定(POC-10 參數) 200,flags 更新
UT-PTY-004 貼標設定(flags 為 null) 400(CC-PTY-1001)
UT-PTY-005 查詢歸戶 200
UT-PTY-006 管制碼設定 200,accounts.block_code 更新 + 歷程新增 1 筆
UT-PTY-007 管制碼設定(無 operator/reason) 400(稽核欄位必填)
UT-PTY-008 卡片狀態投影(已知事件型別) 透傳至埠;序號與完整狀態原樣傳遞(CardStatusProjectionServiceTest)
UT-PTY-009 卡片狀態投影(未知事件型別) 略過且不拋錯、不落到埠(避免毒化消費者;SAD-004 §5.4)
UT-PTY-010 卡片狀態投影(埠回 false=重複/亂序) 如實回 false,不吞不轉
UT-PTY-011 查該歸戶之卡片(歸戶不存在) 404(CC-PTY-4040)
UT-PTY-012 查該歸戶之卡片(歸戶存在、無投影) 200、空陣列

測試資料(種子):需種入至少 1 個歸戶 + 2 個持卡人(PRIMARY/SUPPLEMENTARY),flags 含 POC-10 參數。種子檔:ops/local/init/oracle/06-dev-party-seed.sql。
⚠️ UT-PTY-011/012 需歸戶主檔與其他服務之種子對齊**——見 CR-031 §4.4(party 種子原僅有 …0010,與 card/limit 之 …0001~…0003 對不上,已補齊)。

真機驗證(2026-10-01):CR-031 §4.1.1——8 項樣本全數通過(含兩項負樣本:重複 eventId、較舊 sequenceNo)。
⚠️ 投影列由事件餵養,非種子種入;種子僅提供歸戶主檔。


10. 已知限制(誠實揭露)**

  1. **當事人/歸戶狀態機未定義(SDD-005 無)。本服務不做狀態轉換邏輯,僅設定/攜帶 status。
  2. POC-10 完整流程未實作:本服務僅提供「身分參數(flags)設定與查詢」;「符合資格名單產出」與「停車資格判斷」屬行銷/收單範疇,不在本服務。
  3. 歸戶(accounts)僅查詢與管制碼設定;未含開戶/結清等生命週期(屬其他流程)**
  4. flags 結構未與行銷/收單確認(本文件為建議結構,須跨服務確認後凍結。
  5. 監控未接(CSP-004 僅追蹤碼)**
  6. 無 Stand-in 降級(見 §8)**

11. 修訂記錄**

版本 日期 作者 說明
1.3 2026-10-16 WorkBuddy AI CR-046 B2-6(#A-249):新增 instrumentId 至 CardStatusSnapshot/card_status_caches 投影/CardStatusView;新增 V4__card_status_instrument_key.sql(nullable+索引);舊事件相容(INSERT 可空、UPDATE COALESCE 不清除既有值);契約由產生器重生。
1.2 2026-10-01 WorkBuddy AI CR-031 §4.1.1 真機驗證通過:§9 補 UT-PTY-008~012(含未知事件型別略過、亂序/重複略過、404 vs 空陣列)+ 真機驗證註記;★ 更正 §7.5 之編號(v1.1 誤植為 §7.1,與既有「§7.1 個資處理」衝突);★ 更正本表格式(v1.1 之列多一欄,與表頭不符);補 §9 種子與其他服務對齊之說明(CR-031 §4.4)
1.1 2026-10-01 WorkBuddy AI CR-031 §2(#A-143):新增卡片狀態投影(跨服務讀模型)——補上 SAD-002 §3.4 載明卻從未實作之訂閱。★ 架構上之唯一解:card-service 之 ACL 只允許 cc-authorization-service → 本服務無法同步取得卡片狀態。新增 V3__card_status_projection.sql/CardStatusProjectionPort/CardStatusProjectionService/CardStatusChangedListener/GET /api/v1/accounts/{accountId}/cards。契約端點 92 → 93(0 錯誤)。⚠️ 本版之章節編號誤植為 §7.1(見 1.2 更正)
1.0 2026-09-19 WorkBuddy AI 初版:規格先行(實作前完成)。依 GOV-005 與 SCS-000 模板撰寫;資料模型約束為 Oracle 實測值。
(47-47/67)