CR #201 » SCS-005_產品服務程式設計規格.md
SCS-005 產品服務(product-service)程式設計規格
| 項目 | 內容 |
|---|---|
| 文件編號 | SCS-005 |
| 版本 |
1.4(CR-045 D4:於 §7.1/§10 #A-130 新增正向參照至 SCS-002 §7.14 首案消費端規格。前版 1.3(★ 修正 paramValue 之 JSON 序列化缺陷——STRING/JSON 型別原產生非法 JSON → ORA-40441 → HTTP 500)。前版 1.2(GOV-006 4.8 W-2 推廣・CR-027 批次 A-4:新增 §7.1 參數變更事件輸出(product.parameter.changed.v1)——SAD-002 §3.5 明列、SDD-006 §4.2 已定義 payload,惟從未有人發射。★ 分區鍵=namespace/paramKey;★ 併補 @Transactional(原方法未標交易)。真機驗證通過。前版 1.1(C-2 落地:5 端點實作+版本化/熱更新語意定案+真機 e2e;2026-09-22) |
| 狀態 | 已核定(規格先行) |
| 對應需求 |
COM-6.1(參數設定)、COM-6.2(共同類/產品主檔) |
| 服務埠號 | 待骨架產生時依 CSP-006 §4 分配(自 8081 起編,參 spec/services.yaml port_start) |
| 套件根 | com.beam.cardcore.product |
| 權威 schema | CC_PRODUCT |
| 關聯文件 |
SDD-001 §6、SDD-006 §4、BRD-002、GOV-005
|
1. 對應需求
| FR 編號 | 需求名稱 | 來源 |
|---|---|---|
COM-6.1 |
參數設定(參數化,P0–P3) | BRD-002 |
COM-6.2 |
共同類/產品主檔查詢 | BRD-002 |
產品服務亦承載卡片產品主檔(
PRODUCTS)與外部組織碼對照(CHARSET_MAPPINGS),為授權、定價、分期等服務提供產品維度參數來源。
1.1 上層設計依據(本規格不重複這些圖,僅引用)
| 圖種 | 文件 | 數量(實測) | 本服務相關 |
|---|---|---|---|
| ER-Diagram | SDD-001_領域模型與ER-Diagram |
16 個 erDiagram
|
PRODUCTS/PARAMETERS/CHARSET_MAPPINGS/RULE_SETS 實體(SDD-001 §6) |
| DFD | SDD-002_DFD與流程分解 |
10 個 flowchart(L0/L1/L2) | 參數設定資料流(COM-6.1) |
| Sequence | SDD-003_Sequence-Diagrams |
15 個 sequenceDiagram
|
產品/參數查詢時序 |
| 狀態機 | SDD-005_狀態機設計 |
10 個 stateDiagram
|
⚠️ PRODUCTS.status 轉換(DRAFT→ACTIVE→SUSPENDED→RETIRED)以 CHECK 約束+應用邏輯控管,未定義為正式狀態機(見 §3、§10) |
| API Contract |
SDD-006 + contracts/openapi/product-service.yaml
|
17 份 | 本服務 3 支端點 |
2. 服務職責與邊界
承作:
- 卡片產品主檔(
PRODUCTS:產品代碼、卡名、組織 VISA/MC/…、卡等、票種) - 參數化(
PARAMETERS:以 JSON 承載各類參數,依NAMESPACE/PARAM_KEY生效區間管理) - 組織碼對照(
CHARSET_MAPPINGS:內部碼↔外部組織碼) - 規則集(
RULE_SETS:資格/費用/紅利/利息等規則,供他服務引用)
不做(屬他服務):
- 費率試算與利息計算 →
pricing-service - 授權決策 →
authorization-service - 紅利累兌 →
rewards-service
3. 領域模型
- 聚合:
Product(產品主檔)、ProductParameter(參數,含生效區間)、CharsetMapping(組織碼對照)、RuleSet(規則) - 值物件:
ProductCode、OrgCode(受限於PRODUCTS.orgCHECK)、ParamKey、RuleType - 狀態:
PRODUCTS.status ∈ {DRAFT, ACTIVE, SUSPENDED, RETIRED}(CHECK 約束,預設 DRAFT);轉換由應用層控管,未建模為SDD-005狀態機
4. 程式結構
| 套件/類別 | 職責 | 對應需求 |
|---|---|---|
domain/model/Product ProductParameter CharsetMapping RuleSet
|
聚合與值物件、不變式 | COM-6.2 |
domain/port/ProductRepository ParameterRepository RuleRepository
|
持久化埠 | — |
application/ProductQueryService |
產品主檔查詢 | COM-6.2 |
application/ParameterService |
參數設定與生效區間管理 | COM-6.1 |
application/RuleEngine |
規則評估(依 PRIORITY) |
(規則消費側,供他服務) |
infrastructure/jdbc/*RepositoryImpl |
JDBC 實作 | — |
interfaces/rest/ProductController ParameterController
|
REST 端點 |
COM-6.1/6.2
|
5. 資料模型(約束為 Oracle 實測值)
| 表 | 關鍵欄位 | 約束(實測) |
|---|---|---|
PRODUCTS |
ID RAW(16) PK;PRODUCT_CODE VARCHAR2(16) NOT NULL UNIQUE;CARD_NAME;ORG;CARD_GRADE;TICKET_TYPE;STATUS VARCHAR2(16) DEFAULT 'DRAFT'
|
org IN ('VISA','MASTERCARD','JCB','AMEX','UNIONPAY','DINERS','DISCOVER');status IN ('DRAFT','ACTIVE','SUSPENDED','RETIRED');EFFECTIVE_FROM NOT NULL、EFFECTIVE_TO 可空(生效區間) |
PARAMETERS |
ID PK;NAMESPACE VARCHAR2(40);PARAM_KEY VARCHAR2(80);PARAM_VALUE JSON NOT NULL;VALUE_TYPE;EFFECTIVE_FROM/TO DATE |
value_type IN ('STRING','NUMBER','BOOLEAN','JSON','AMOUNT','RATE');UQ_PARAM UNIQUE(NAMESPACE,PARAM_KEY,EFFECTIVE_FROM) |
CHARSET_MAPPINGS |
INTERNAL_CHAR;EXTERNAL_CODE;ORG
|
UQ_CHARSET UNIQUE(ORG,INTERNAL_CHAR) |
RULE_SETS |
RULE_CODE UNIQUE;RULE_TYPE;CONDITION JSON;ACTION JSON;PRIORITY DEFAULT 100;STATUS
|
rule_type IN ('AUTH_CHECK','REWARD','FEE','INTEREST','DEDUCT','ELIGIBILITY');status IN ('ACTIVE','INACTIVE')
|
所有表均含
CREATED_AT/UPDATED_ATTIMESTAMP(6) WITH TIME ZONE(預設SYSTIMESTAMP)、VERSIONNUMBER(10) 樂觀鎖。
6. API 設計(來自 contracts/openapi/product-service.yaml)
| 端點 | 方法 | 說明 | 對應 FR |
|---|---|---|---|
/api/v1/products |
POST | 建立產品(CreateProductRequest→201;代碼重複 CC-PRD-1001) |
COM-6.2 |
/api/v1/products/{productCode} |
GET | 單筆產品主檔(查無 CC-PLT-4040) |
COM-6.2 |
/api/v1/products/{productCode} |
PUT | 更新產品(RETIRED 終態不可更新 CC-PRD-3002) |
COM-6.2 |
/api/v1/parameters/{namespace}/{paramKey} |
GET | 查詢參數(選填 businessDate 依業務日取版本;查無 CC-PLT-4040) |
COM-6.1 |
/api/v1/parameters/{namespace}/{paramKey} |
PUT | 設定參數(熱更新;生效日重疊 CC-PRD-1002) |
COM-6.1 |
錯誤碼沿用
common-domainErrorCodes;org/valueType列舉於應用層檢核(400CC-PLT-4003,不落 DDL CHECK 變 500)。⚠️copyFrom之完整複製語意(§10)與狀態機端點屬後續增量。
6.1 參數版本鏈語意(C-2 定案)
| 操作 | 行為 |
|---|---|
PUT(新版本 [from, to?]) |
自動封閉被覆蓋之現行版本(effective_to = from - 1);開放式版本被有尾端新版本切割 → 尾端自動延續(同值補 [to+1, ∞),避免參數憑空消失) |
| 真重疊 | 較晚版本起日落入新區間內(以封閉後狀態重算)→ 400 CC-PRD-1002
|
| 查詢 |
businessDate(預設當日)取 covers(date) 之版本;查無 → 404 CC-PLT-4040
|
7. 關鍵邏輯
-
參數生效區間:
PARAMETERS以EFFECTIVE_FROM/EFFECTIVE_TO管理版本,查詢須帶AS_OF時間取當時有效版本;UQ_PARAM防止同鍵同起始日重複。 -
組織碼對照:外部系統(如 VISA/MC 檔)交換前,經
CHARSET_MAPPINGS轉內部碼,避免外部碼污染核心。 -
規則評估:
RuleEngine依PRIORITY由小到大評估RULE_SETS(條件 JSON 匹配 → 動作 JSON)。
7.1 參數變更事件輸出(product.parameter.changed.v1)——2026-10-01 新增(GOV-006 4.8 W-2 推廣・CR-027 批次 A-4)
緣由:
SAD-002 §3.5明列本服務發布product.parameter.changed.v1(用途:參數熱更新),
SDD-006 §4.2亦已定義 payload,惟從未有人發射(同SCS-002 §7.5之型)。
本節補生產端。
主題:cc.product.parameter.changed(命名規則 SAD-004 §5.4)。
| 項目 | 值 |
|---|---|
eventType |
product.parameter.changed.v1 |
Outbox aggregate_type
|
product |
Outbox event_type
|
product.parameter.changed(不含版本) |
| Kafka 分區鍵 | namespace + "/" + paramKey |
payload(v1;欄位名依 SDD-006 §4.2):namespace/paramKey/effectiveFrom。
處理:ParameterService.set 於 repository.save(version) 後、同一交易寫 outbox。
設計要點:
- ★ 分區鍵為
namespace/paramKey(而非版本列之id):本事件之聚合是「該參數」,
非某個版本——同一參數之歷次變更須落同一分區以保序(SAD-004 §5.4)。 - ★ 補上
@Transactional(2026-10-01,#A-130):原set()未標交易——惟其步驟
(封閉現行版本 +(必要時)補延續版本 + 寫新版本)本應為原子操作;
且 Outbox 寫入必須與權威寫入同交易,否則失去 Transactional Outbox 之意義(SDD-001 §3.15)。 - ⚠️ 消費者為「全體服務」(
SAD-002 §3.5)——消費端尚未實作(批次 C)。
故現階段為「有人發、沒人收」(如實揭露)。 - ★ 首案消費端規格已立:
limit-service之商品參數消費閉環見SCS-002 §7.14(CR-045D4,2026-10-15 採納立場;規格先行,程式尚未實作)。本節(生產端)不變。
真機驗證(2026-10-01;真實 Oracle + Kafka):見 §10 #6。
8. 外部依賴與降級
| 依賴 | 失敗處理(Stand-in) |
|---|---|
僅依賴自身 CC_PRODUCT 權威庫;屬讀多寫少之配置型服務 |
無強即時外部依賴;快取(Redis 8)可承接高頻查詢,失效時回查 DB,不降級功能 |
9. 測試案例(對應 TRP-001)
18 tests, 0 failures(2026-09-22,--rerun-tasks 實測)
| 測試類別 | 案例數 | 對應 |
|---|---|---|
ProductServiceTest |
6 | 建立 DRAFT/代碼重複 CC-PRD-1001/org 非法 400/查無 404/更新版本遞增 |
ParameterServiceTest |
8 | 依業務日取版本/自動封閉/真重疊 CC-PRD-1002/同生效日/查無 404/valueType 檢核/切割尾端延續 |
LayeringTest(ArchUnit) |
4 | CSP-002 §6.3 |
§9.3 真機 e2e(8085,使用者 token):產品 201→200→PUT 200(org 更新);重複代碼 CC-PRD-1001;參數版本鏈 09-21→404(生效前)、09-22→3(v1)、09-26/10-14→9(v2 切割)、10-16/11-01→3(尾端延續)——DB 與 API 一致。
10. 已知限制(誠實揭露)
-
PRODUCTS.status轉換未建模為SDD-005正式狀態機,目前以 CHECK 約束+應用層邏輯控管;若客戶要求正式狀態機,須提 CR 補SDD-005。 -
RuleEngine之規則語意範圍(哪些 FR 實際消費RULE_SETS)待各消費側服務(授權/紅利/費用)確認後凍結。 -
→ ✅ 已過時:埠 8085/product-service骨架與埠號尚未產生CC_PRODUCTschema/Flyway V1 皆已就緒,C-2已實作(1.1)。 -
copyFrom之完整複製語意(來源產品參數複製範圍)本期僅驗證來源存在,未實作複製;產品狀態機端點(DRAFT→ACTIVE)屬後續增量。 -
product.parameter.changed.v1之消費者尚未實作(§7.1,#A-130):SAD-002 §3.5載其消費者為全體服務(參數熱更新)——無任何服務訂閱 → 現階段為「有人發、沒人收」(如實揭露)。★ 首案(同步取參)消費端規格見SCS-002 §7.14(CR-045D4,規格中;程式待立 SCS 後實作)。 -
真機驗證(2026-10-01,
#A-130;真實 Oracle + Kafka):PUT /api/v1/parameters/{ns}/{key}(X-Channel/X-Idempotency-Key必填)→ HTTP 200 →outbox落列(product.parameter.changed、aggregate_id=e2e-ns/event-test、published=YES)→ Kafka 主題cc.product.parameter.changed自動建立;消費得信封,payload 與SDD-006 §4.2逐欄一致(namespace/paramKey/effectiveFrom)。併註:實作時補上@Transactional(原方法未標交易——版本鏈之「封閉現行/補延續/寫新版本」本應原子,且 Outbox 須與權威寫入同交易,SDD-001 §3.15)。
★ 缺陷修正(2026-10-01,真機驗證時發現)
paramValue之 JSON 序列化錯誤 →ORA-40441→ HTTP 500
CC_PRODUCT.parameters.param_value為 JSON 欄。原實作以paramValue.toString()直接寫入:
valueType=STRING+paramValue="v1"→ 寫出裸字串v1→ 非法 JSON →ORA-40441→ HTTP 500
(實測:PUT /api/v1/parameters/e2e-ns/topic-test回 500CC-PLT-5000)valueType=JSON+{"a":1}→Map.toString()產生{a=1}→ 非法 JSON(同型缺陷)修正:一律以 Jackson 序列化(
ParameterService.toJson)——
"v1"/100/true/{"a":1}/[1,2]皆為合法 JSON。
回歸測試:ParameterValueJsonTest6 例(含特殊字元轉義)。⚠️ 揭露:本缺陷非
CR-030之 12 項之一,係於驗證#15(主題命名)時順帶發現——
原請求以valueType=AMOUNT可繞過(整數toString()恰為合法 JSON),故長期未被察覺。
11. 修訂記錄
| 版本 | 日期 | 修訂內容 |
|---|---|---|
| 1.4 | 2026-10-15 |
CR-045 D4(首案消費閉環):於 SCS-005 §7.1/§10 #A-130 新增正向參照至 SCS-002 §7.14(首案商品參數消費端規格已立;程式尚未實作)。本服務(生產端)內容不變 |
| 1.3 | 2026-10-01 | WorkBuddy AI |
| 1.0 | 2026-09-18 | 規格先行版(埠號與類別名為規劃值) |
| 1.2 | 2026-10-01 |
GOV-006 4.8 W-2 推廣・CR-027 批次 A-4:新增 §7.1 參數變更事件輸出(product.parameter.changed.v1)——SAD-002 §3.5 明列本服務發布、SDD-006 §4.2 已定義 payload,惟從未有人發射。實作:V2__outbox.sql + OutboxPort/ParameterEventFactory/JdbcOutboxRepository/{KafkaEventPublisher,OutboxRelayConfig,OutboxRelayScheduler};ParameterService.set 同交易寫 outbox。★ 分區鍵=namespace/paramKey(聚合為「該參數」,非版本列)。★ 併補 @Transactional(原方法未標交易;版本鏈本應原子,且 Outbox 須同交易)。真機驗證通過(payload 與契約逐欄一致)。⚠️ 消費者(全體服務)尚未實作
|
| 1.1 | 2026-09-22 | 依 C-2(BEAM PMO 指示,GOV-006 排序) 實作 5 端點:產品 CRUD+參數版本化(熱更新)。版本鏈語意定案(§6.1):自動封閉被覆蓋版本/真重疊 CC-PRD-1002/開放式被切割 → 尾端自動延續(同值補 [to+1, ∞),避免參數憑空消失)。單測 18 例 0 失敗;真機 e2e(產品 201/200/PUT、參數版本鏈多日期點、DB 一致);三支掃描器 0 命中。⚠️ copyFrom 完整語意與狀態機端點屬後續 |