CR-017 C-6~C-9 四服務缺口總盤點與裁定建議(dispute/report/migration/notification)
| 項目 |
內容 |
| 文件編號 |
CR-017 |
| 版本 |
1.5(C-6 ✅ §9.1;C-7 ✅ §9.2;C-8 ✅ §9.3;C-9 ✅ §9.4——通道抽象先行**;四服務全數結案。SCS-016 → 1.1、SDD-001 → 1.13、SDD-005 → 1.7、SDD-006 → 122 碼)** |
| 提出日 |
2026-09-22 |
| 提出者 |
WorkBuddy AI(開發團隊) |
| 緣起 |
BEAM PMO 指示:「先一次盤點 C-6~C-9 的缺口再逐支實作」 |
| 盤點方法 |
①Oracle 實測(all_tables/all_constraints,2026-09-22)②契約解析(contracts/openapi/*.yaml 逐 schema 抽欄位與列舉)③SCS 規格比對(SCS-012/015/016/017) |
| 關聯前例 |
CR-015(merchant)、CR-016(clearing)——同為「動工前查證 → 立 CR 取裁定 → 實作」 |
1. 盤點總表(一眼看盡)
| 服務 |
埠 |
契約端點 |
DDL 現況(實測) |
缺口嚴重度 |
C-6 dispute
|
8093 |
3(開案/查詢/chargeback) |
CC_DIS:2 表已備(DISPUTE_CASES/DISPUTE_TIMELINES) |
🟡 值域嚴重脫鉤(兩處正交維度混用)+缺 3 欄 |
C-7 report
|
8098 |
2(送 job/查 job) |
CC_RPT:0 表(V1 為空) |
🔴 需新建表(2 張)+輸出媒體未定 |
C-8 migration
|
8096 |
2(送 job/查 legacy) |
CC_MIG:0 表
|
🔴 需新建表(3 張)+契約與 SCS 粒度不一致+外部資料源 |
C-9 notification
|
8097 |
2(送通知/查通知) |
CC_NOTIF:0 表
|
🔴 需新建表(2 張)+發送通道為真阻塞(通道抽象先行) |
四服務皆僅有骨架(*ServiceApplication + LayeringTest),與 C-4/C-5 起點相同。
2. C-6 dispute-service(CC_DIS 已有 2 表)
2.1 ⛔ 兩處「正交維度混用」(核心發現)
① disputeType——「爭議原因」與「程序類型」混用
| 來源 |
值域 |
語意 |
契約 OpenDisputeRequest.disputeType
|
UNAUTHORIZED/DUPLICATE/GOODS_SERVICES/OTHER(4) |
爭議原因(為何爭議) |
DDL DISPUTE_CASES.dispute_type(實測) |
CHARGEBACK/INQUIRY/RETROACTIVE/FRAUD/DUPLICATE(5) |
程序類型(CHARGEBACK/INQUIRY/RETROACTIVE)與原因(FRAUD/DUPLICATE)混用
|
交集僅 DUPLICATE——與 CR-008/CR-012/CR-015 同一類問題(兩軸塞進一欄)。
② stage——「卡組織程序階段」與「內部作業階段」混用
| 來源 |
值域 |
語意 |
契約 DisputeView.stage
|
OPENED/EVIDENCE_COLLECTING/UNDER_REVIEW/ARBITRATION/RESOLVED_ISSUER/RESOLVED_ACQUIRER/CLOSED(7) |
內部作業階段(本行怎麼處理) |
DDL DISPUTE_CASES.stage(實測) |
INQUIRY/FIRST_PRESENTMENT/SECOND_PRESENTMENT/PRE_ARB/ARBITRATION/CLOSED(6) |
卡組織程序階段(SDD-005 §7.1 已明載) |
交集僅 ARBITRATION/CLOSED——亦為兩軸(SDD-005 §7.1 之階段 vs 本行作業流程)。
2.2 其餘缺口
| # |
契約元素 |
DDL 現況 |
判定 |
| a |
ChargebackRequest.chargebackType(FIRST/SECOND/ARBITRATION) |
無對應欄(DDL 之 STAGE 有 FIRST_PRESENTMENT 等,惟語意為組織階段、非本行提交類型) |
需新增欄
|
| b |
ChargebackRequest.reason |
無對應欄(EVIDENCE_SUBMITTED 為 JSON,語意不同) |
需新增欄
|
| c |
ChargebackRequest.evidence |
EVIDENCE_SUBMITTED JSON 已有 |
可沿用(或新增 CHARGEBACK_EVIDENCE) |
| d |
DisputeView.orgDeadline/handler
|
RESPONSE_DEADLINE/ASSIGNED_TO 已有 |
僅命名對映,無缺口 |
| e |
disputeNo/amount/openedAt/closedAt/disputeId
|
皆有 |
✅ |
| f |
liability(DDL 4 值) |
契約未回傳 |
正交屬性(SDD-005 §7.3)——契約未回傳可接受,建議日後補 |
| g |
DISPUTE_TIMELINES(稽核軌跡) |
契約未提 |
✅ 內部用,不回傳 |
3. C-7 report-service(CC_RPT 0 表)
SCS-017 §5 已自載「DDL 待補」與設計意圖(非無中生有):
-
REPORT_DEFINITIONS(REPORT_CODE UNIQUE、定義 JSON、排程 Cron、格式)
-
REPORT_JOBS(REPORT_CODE、狀態、參數、輸出參照、完成時間)
| # |
契約元素 |
需落之欄 |
備註 |
| a |
ReportJobRequest.reportCode/parameters(物件)/format(CSV/XLSX/PDF/JSON) |
REPORT_JOBS.REPORT_CODE/PARAMETERS JSON/FORMAT CHECK |
format 值域由契約定(DDL 新增即對齊) |
| b |
ReportJobView.status(QUEUED/RUNNING/COMPLETED/FAILED) |
STATUS CHECK 4 值 |
同契約 |
| c |
rowCount/downloadUrl/startedAt/finishedAt
|
ROW_COUNT/DOWNLOAD_URL/起訖時間 |
DOWNLOAD_URL 指向物件儲存(本地已有 MinIO,見鐵則) |
| d |
報表產生器(各報表之查詢邏輯) |
— |
⚠️ 範圍須裁定:本期僅 job 生命週期(送件/查詢)?或含實作 1~2 支示範報表? |
| e |
報表主體之資料來源 |
— |
SCS-017 註「報表主體建議置於 rpt Postgres 讀模型(CQRS 讀側)」→ 本期是否觸及 Postgres? |
4. C-8 migration-service(CC_MIG 0 表)
SCS-015 §5 已自載設計意圖:MIGRATION_WAVES(波次)/MIGRATION_CHECKPOINTS/LEGACY_ACCOUNT_MAP。
4.1 ⛔ 契約與 SCS 之粒度不一致
| 來源 |
概念 |
值域 |
契約 MigrationJobRequest
|
Job:migrationType(ACCOUNT/CARD/BALANCE/POINTS/MERCHANT)+sourceRef+dryRun
|
— |
契約 MigrationJobView
|
status(RUNNING/COMPLETED/FAILED)、processedCount/successCount/errorCount/errorDetail
|
— |
| SCS-015 §5 |
Wave(波次 W0–W4):WAVE_CODE、波次狀態、起訖、影響範圍 |
SDD-005 待正式化(GOV-006 2.3.6 註) |
→ 「Job(一次遷移執行)」與「Wave(波次,可含多個 Job)」為不同粒度——須裁定:
(a) 以契約之 Job 為主,Wave 由 WAVE_CODE 標籤歸類(單表);
(b) 建 MIGRATION_WAVES + MIGRATION_JOBS 兩層(符合 SCS 意圖,契約加 waveCode)。
4.2 其餘缺口
| # |
項目 |
說明 |
| a |
LegacyAccountView(legacyId/accountNo/migrated/migratedAt/snapshot) |
對映至 LEGACY_ACCOUNT_MAP
|
| b |
舊系統資料源 |
sourceRef(如 LEGACY_ACCT_2026Q1)指向 Legacy ECS(外部)→ 本期可實作 dryRun 與對映查詢,實際搬遷需資料源(列限制) |
| c |
波次狀態機 |
GOV-006 2.3.6 已註「待 SDD-005 正式化」→ 本期不新增狀態機(避免虛構) |
5. C-9 notification-service(CC_NOTIF 0 表)
SCS-016 §5 已自載設計意圖:NOTIFICATION_TEMPLATES/NOTIFICATIONS。
| # |
契約元素 |
需落之欄 |
| a |
NotificationRequest.accountId/partyId/templateCode/variables*(物件)/preferredChannels(陣列)/amount
|
NOTIFICATIONS:TEMPLATE_CODE/VARIABLES JSON/PREFERRED_CHANNELS JSON/AMOUNT
|
| b |
NotificationView.status(QUEUED/SENT/FAILED)/channels/sentAt/failReason
|
STATUS CHECK 3 值/CHANNELS JSON/SENT_AT/FAIL_REASON
|
| c |
發送通道 |
🔴 真阻塞(鐵則:真阻塞僅 3 類,其一為「通知發送通道(僅發送)」)→ 本期通道抽象先行(GOV-006 2.3.7 明載):定義 NotificationChannelPort + 本地 Log 通道(stand-in),實際 SMS/Email/推播未接
|
| d |
模板管理 |
NOTIFICATION_TEMPLATES(TEMPLATE_CODE UNIQUE、通道、語言、內文、變數定義)——本期是否需要管理端點? 契約無模板端點 → 建議僅建表+內建少量種子,不做 CRUD |
6. 共通發現(跨四服務)
-
三個 schema 完全無表(
CC_RPT/CC_MIG/CC_NOTIF),且 SDD-001 §3 僅涵蓋 14 個 bounded context
→ 新建表須同步補 SDD-001 之 DDL 章節(設計權威)+以 V2 遷移交付(V1 已套用、凍結,SAD-003 §9)。
-
SCS-015/016/017 均已自載「DDL 待補」與設計意圖——新表設計有規格依據,非無中生有;
惟實際欄位與約束須以 Oracle 實測值回填(不得憑印象)。
-
四服務皆僅骨架,且契約端點數少(3/2/2/2=9 端點)——與
C-4(3 端點)/C-5(3 端點)同規模,逐支實作可行。
-
兩個「正交維度混用」(C-6 之
disputeType/stage)——與 CR-008/CR-012/CR-015 同類,
建議沿用雙欄位解法(不為相容而刪值)。
7. 建議裁定與實作順序
| # |
議題 |
建議 |
需裁定 |
| A |
C-6 disputeType 雙軸 |
新增 DISPUTE_REASON(契約 4 值:UNAUTHORIZED/DUPLICATE/GOODS_SERVICES/OTHER);DISPUTE_TYPE 保留程序類型(DDL 值域擴充為 3 值 CHARGEBACK/INQUIRY/RETROACTIVE+保留 FRAUD/DUPLICATE 供相容);契約 disputeType 對映 DISPUTE_REASON
|
✔ |
| B |
C-6 stage 雙軸 |
新增 WORKFLOW_STAGE(契約 7 值);STAGE 保留卡組織程序階段(SDD-005 §7.1,契約另以 orgStage 回傳) |
✔ |
| C |
C-6 chargeback 欄 |
新增 CHARGEBACK_TYPE(3 值)、CHARGEBACK_REASON
|
— |
| D |
C-7 範圍 |
建 REPORT_JOBS(+REPORT_DEFINITIONS);本期僅 job 生命週期(送件/查詢/狀態流轉),報表產生器與 Postgres 讀模型不做(列限制) |
✔ |
| E |
C-8 粒度 |
建議 (b):MIGRATION_WAVES + MIGRATION_JOBS 兩層(符合 SCS-015 意圖,契約加 waveCode)——惟若求最小,可採 (a) 單表 |
✔ |
| F |
C-9 通道 |
通道抽象先行(NotificationChannelPort + 本地 Log 通道 stand-in);實際發送列為真阻塞 |
— |
| G |
三 schema 之 DDL |
一律 V2 遷移 + SDD-001 新增三節(rpt/mig/notif)+ SCS-015/016/017 §5 回填實測值 |
— |
建議實作順序(依 BEAM PMO 原列序,惟註明負擔):
C-6 dispute ← 表已備、缺口明確(2 處雙軸+3 欄) 較輕
C-7 report ← 新建 2 表+job 生命週期 中等
C-8 migration ← 新建 2~3 表+對映+外部資料源 最重
C-9 notification ← 新建 2 表+通道抽象 中等
8. 裁定結果(BEAM PMO 2026-09-22)
| # |
議題 |
裁定 |
影響 |
| A |
C-6 disputeType 雙軸 |
✅ 採雙欄位
|
新增 DISPUTE_REASON(契約 4 值);DISPUTE_TYPE 保留程序類型 |
| B |
C-6 stage 雙軸 |
✅ 採雙欄位
|
新增 WORKFLOW_STAGE(契約 7 值);STAGE 保留卡組織程序階段;契約另以 orgStage 回傳 |
| C |
C-6 chargeback 欄 |
✅ 補欄 |
CHARGEBACK_TYPE(3 值)、CHARGEBACK_REASON
|
| D |
C-7 範圍 |
⚠️ 含 Postgres 讀模型(較建議更大) |
除 REPORT_JOBS/REPORT_DEFINITIONS(Oracle)外,另建 rpt Postgres 讀模型——見 §8.1 之依賴揭露 |
| E |
C-8 粒度 |
✅ 兩層:MIGRATION_WAVES + MIGRATION_JOBS
|
契約加 waveCode;符合 SCS-015 §5 意圖 |
| F |
C-9 通道 |
✅ 通道抽象先行 |
NotificationChannelPort + 本地 Log 通道(stand-in);實際發送列真阻塞 |
| G |
三 schema DDL |
✅ V2 + 補 SDD-001 三節 |
rpt/mig/notif 各補 DDL 章節,SCS-015/016/017 §5 回填實測值 |
| — |
實作順序 |
✅ 依原序 C-6 → C-7 → C-8 → C-9
|
— |
8.1 ⚠️ 裁定 D 之依賴揭露(須先講清楚,以免實作時才發現)
BEAM PMO 裁定 C-7 含 Postgres 讀模型(CQRS 讀側),此範圍較我方建議為大,其前置依賴如下:
| # |
依賴 |
現況(實測/文件) |
影響與建議 |
| 1 |
Postgres rpt schema 之讀模型設計 |
尚未定義(SDD-001 §3 未涵蓋;SCS-017 §5 僅註「報表主體建議置於 rpt 讀模型」) |
須先設計讀模型表(報表主體:授權彙總/交易明細/帳務快照等)→ 屬新設計,須再經裁定或於實作時提出
|
| 2 |
同步機制(Oracle → Postgres 投影) |
未實作;GOV-006 4.8 W-2 Kafka 消費端 ⬜ 未實作(生產端已備) |
讀模型無資料來源——三條可行路徑:(i) 批次投影(排程讀 Oracle 寫 Postgres,可本地落地);(ii) 先建 Kafka 消費端(W-2,較重);(iii) 本期僅建 schema 與投影介面,實作待 W-2。建議 (i)(不阻塞、可實測、且與 #A-79 之排程慣例一致) |
| 3 |
報表輸出媒體(downloadUrl) |
本地已有 MinIO(鐵則:物件儲存非真阻塞) |
可落地;惟 downloadUrl 之產生與檔案寫入須一併實作 |
| 4 |
報表定義(REPORT_DEFINITIONS 之內容) |
契約僅示例 DAILY_AUTH_SUMMARY
|
本期建議內建 1~2 支示範報表定義(否則 job 無事可做=空殼) |
我方建議之落地切法(供實作時採用,若與 BEAM PMO 預期不同請即指正):
① Oracle CC_RPT:REPORT_DEFINITIONS + REPORT_JOBS(作業指標,契約直接對應);
② Postgres rpt:讀模型表(報表主體)+批次投影(排程自 Oracle 權威讀入,stand-in 取代 Kafka 消費端);
③ 報表產生器:讀 Postgres 讀模型 → 輸出 CSV/JSON(XLSX/PDF 需額外函式庫,建議先 CSV/JSON,其餘列限制)。
9. 逐支實作結果
9.1 C-6 dispute-service(2026-09-22 完成)
交付
| 項 |
內容 |
| 遷移 |
V2__dispute_reason_workflow_stage_chargeback.sql——新增 DISPUTE_REASON(4 值)/WORKFLOW_STAGE(7 值)/CHARGEBACK_TYPE(3 值)/CHARGEBACK_REASON;dispute_type 之 CHECK 重建為具名(值域不變);新增 dispute_no_seq
|
| 契約 |
改 gen_openapi.py 後重生(0 錯誤/74 端點):disputeType 補述「爭議原因」語意、DisputeView 新增 orgStage(6 值)、stage 補述「本機作業階段」 |
| 程式 |
DDD 四層:Dispute 聚合(7 個值域枚舉)+1 埠+JDBC+應用服務+組裝器+3 端點 |
| 品質 |
單測 19 例 0 失敗(領域 7/應用 8/既有 4);scan_scaffold/scan_faketest 0 命中;validate_sql 0 違規 |
真機 e2e(8093,六項)
| # |
情境 |
結果 |
| ① |
開案(disputeType=UNAUTHORIZED) |
201:DSP202609000001/stage=OPENED/orgStage=INQUIRY/時限=開案 +30 日 |
| ② |
查詢 |
200,狀態一致 |
| ③ |
扣款 FIRST(帶 evidence 物件) |
201:stage=UNDER_REVIEW/orgStage=FIRST_PRESENTMENT
|
| ④ |
重複提交 FIRST |
冪等:狀態不變、REPRESENTMENT_COUNT 仍為 1(SM-03) |
| ⑤ |
跳號提交 ARBITRATION |
409 CC-DSP-3001「目前階段 FIRST_PRESENTMENT,不得提交 ARBITRATION」 |
| ⑥ |
DB 複核 |
雙軸同列各自獨立:DISPUTE_REASON=UNAUTHORIZED(原因)/DISPUTE_TYPE=INQUIRY(程序類型)/STAGE=FIRST_PRESENTMENT(組織階段)/WORKFLOW_STAGE=UNDER_REVIEW(作業階段);EVIDENCE_SUBMITTED JSON 落地;4 個具名約束值域正確 |
施行中追加(附理由)
| # |
事項 |
處理 |
理由 |
| a |
卡組織(org)之來源 |
開案預設 INTERNAL
|
契約之 OpenDisputeRequest 未帶 org,而 DDL 之 ORG 為 NOT NULL;INTERNAL(本行受理、尚未向卡組織提出)與 SDD-005 §7.1 轉換 1 語意一致。待契約擴充後改由請求帶入 |
| b |
卡組織時限(orgDeadline) |
開案 +30 日(常數) |
契約示例為 09-17→10-17(30 日);卡組織時限未參數化(無參數來源表),列為限制 |
| c |
ChargebackRequest.evidence |
型別由 String 改為 Object(契約為 object),序列化後寫入 EVIDENCE_SUBMITTED
|
原實作與契約不符(契約明載 type: object)——契約優先
|
| d |
扣款提交之冪等 |
同一類型且已達目標階段 → 回傳現況(SM-03) |
更新操作無冪等鍵欄位;以狀態判斷重複執行,避免重送變 409 |
9.2 C-7 report-service(2026-09-22 完成)——含 Postgres 讀模型
交付
| 項 |
內容 |
| Oracle 側 |
V2__report_definitions_and_jobs.sql:REPORT_DEFINITIONS(含內建 DAILY_AUTH_SUMMARY)+REPORT_JOBS(狀態 4 值/格式 4 值 CHECK)+report_job_no_seq
|
| Postgres 讀模型 |
db/migration-postgres/V1__rpt_read_model.sql:rpt.daily_auth_summary(日 × 通路 × 型別 × 決策,複合主鍵、upsert 冪等)——CQRS 讀側首例(實測 rpt schema 早已存在但全庫無服務使用) |
| 投影 |
AuthSummaryProjectionService+ReportProjectionScheduler(追趕視窗「N 日前 ~ 昨日」,可回補) |
| 產生 |
ReportJobService(入隊/查詢/取件執行)+ReportGenerator(CSV/JSON;輸出本地檔案)+ReportJobScheduler
|
| 契約 |
端點與成功碼未變(202/200);故未改 gen_openapi.py
|
| 品質 |
單測 16 例 0 失敗(領域 6/應用 6/既有 4);scan_scaffold/scan_faketest 0 命中;validate_sql 0 違規 |
真機 e2e(8098,五項)
| # |
情境 |
結果 |
| ① |
投影(Oracle CC_AUTH → Postgres rpt) |
4 列:09-18 APPROVED 2/100000、09-19 APPROVED 16/351300、09-19 DECLINED 11/403000、09-19 STANDIN 1/22200——與 sqlplus 直查權威側逐欄一致
|
| ② |
POST /report-jobs(CSV) |
202/QUEUED
|
| ③ |
排程取件後查詢 |
COMPLETED/rowCount=4/downloadUrl=file://…/RPTJ202609000001.csv
|
| ④ |
CSV 內容 |
表頭+4 列,金額為分(100000/351300/403000/22200)——與讀模型一致 |
| ⑤ |
未支援格式 XLSX |
FAILED+原因「輸出格式 XLSX 本期未支援(需額外函式庫);已支援 CSV/JSON」,無輸出參照(不產空檔、不假裝成功) |
| ⑥ |
同冪等鍵重送 |
回同一 jobId(未重複入隊) |
⚠️ 施行中追加/更正(附理由)
| # |
事項 |
處理 |
理由 |
| a |
物件儲存之更正 |
輸出改為本地檔案(${cardcore.report.output-dir},downloadUrl 為 file://) |
CR-017 §8.1 原載「MinIO 可落地」為誤——實測 docker-compose.yml 無 MinIO service(已於 SCS-017 §5.4 更正) |
| b |
跨 schema 讀取授權(ops/local/init/oracle/12-dev-report-projection-grant.sql) |
GRANT SELECT ON CC_AUTH.AUTHORIZATIONS TO CC_RPT,明標為本地 stand-in
|
正規同步為事件驅動(W-2 Kafka 消費端 ⬜);經 API 亦不可行——authorization-service 之 allowed-callers 依 SAD-002 §5 為空集合(實測)。正式環境不得如此(服務邊界),須改事件驅動或先修互動矩陣 |
| c |
雙資料源之兩大陷阱(實測踩坑,已寫入程式註解) |
①兩個 DataSource/JdbcTemplate 皆明確定義、主側標 @Primary;②一律以 DataSourceProperties 綁定(url 鍵);③Postgres 之 Flyway 不註冊 bean(由 RptReadModelMigrator 程式執行) |
①Boot 之 @ConditionalOnSingleCandidate(DataSource.class)/@ConditionalOnMissingBean 會整體退讓→ Oracle 遷移不執行、未指名之注入誤取 Postgres 模板(Oracle SQL 於 Postgres 拋 BadSqlGrammar);②DataSourceBuilder 之鍵為 jdbcUrl,與慣例 url 不符;③任何 Flyway bean 會抑制 Boot 之自動配置遷移 |
| d |
Flyway 需資料庫專屬模組 |
補 org.flywaydb:flyway-database-postgresql
|
缺之則啟動失敗「Unsupported Database: PostgreSQL 18.6」(同 Oracle 之 flyway-database-oracle) |
| e |
投影視窗 |
「N 日前 ~ 昨日」區間(非單日) |
需可回補(停機/延遲補正);投影冪等故重跑無副作用 |
| f |
報表參數未提供時 |
取讀模型全部資料 |
本期未實作預設區間策略(如「昨日」)——以免產出看似正確卻空無一列之報表 |
9.3 C-8 migration-service(2026-09-22 完成)——兩層 wave+job
交付
| 項 |
內容 |
| 遷移 |
V2__migration_waves_jobs_and_legacy_map.sql:MIGRATION_WAVES(波次 W0–W4,內建 5 筆種子)+MIGRATION_JOBS(型別 5 值/狀態 3 值,皆對齊契約)+LEGACY_ACCOUNT_MAPPINGS(舊帳對映)+migration_job_no_seq
|
| 契約 |
MigrationJobRequest 加 waveCode(W0–W4,required)、MigrationJobView 加 waveCode/dryRun;重生 0 錯誤/74 端點
|
| 程式 |
DDD 四層:MigrationJob(兩層之下層)/MigrationWave(上層)/LegacyAccount;3 埠+3 JDBC;2 應用服務+組裝器;2 端點(POST 202/GET 200)+取件排程 |
| 品質 |
單測 17 例 0 失敗;scan_scaffold/scan_faketest 0 命中;validate_sql 0 違規 |
真機 e2e(8096,六項)
| # |
情境 |
結果 |
| ① |
試跑 dryRun=true(W0) |
202 → 排程 → MIGJ202609000001 COMPLETED 2/2;PENDING 仍為 2(未變更任何列) |
| ② |
非試跑 dryRun=false(W1) |
202 → MIGJ202609000002 COMPLETED 2/2;PENDING → 0、MIGRATED → 3
|
| ③ |
舊帳查詢 GET /legacy-accounts/LEGACY_ACCT_0001
|
200:migrated=true、snapshot={"note":"seed","tier":"PLATINUM"}(JSON 解析為物件,符契約 type: object) |
| ④ |
波次不存在(W9) |
422 CC-MIG-1001「波次不存在:W9」(兩層結構之檢核) |
| ⑤ |
同冪等鍵重送 |
回同一 jobId(未重複入隊) |
| ⑥ |
DB 複核 |
MIGRATION_JOBS 2 列(W0 dry=1/W1 dry=0,皆 COMPLETED 2/2);LEGACY_ACCOUNT_MAPPINGS 3 列全 MIGRATED=1;MIGRATION_WAVES 5 筆種子 |
⚠️ 施行中追加/事故(附理由,皆已揭露)
| # |
事項 |
處理 |
理由 |
| a |
本期 job 語意 |
為「對映註冊表之處理與計數」 |
舊系統(Legacy ECS)資料源未接入 → 不搬移任何帳務資料;計數為對映處理數(SCS-015 §5.1 明載,不假造搬遷) |
| b |
SQL-04 命名違規與處置 |
V2 所建之 legacy_account_map 違反「表名須為複數」→ 更正 V2 檔為 legacy_account_mappings,並重置本機遷移狀態(drop 三表+序號、刪除 V2 之 history 列)後重套 |
⚠️ 正規紀律為「已套用之遷移不可改」(SAD-003 §9)——此次例外之唯一理由:該檔係本輪剛建立、僅本機套用(未離開本機、未進任何共享環境)。豁免登錄檔(sql_exemptions.yaml)明訂業務主檔不適用,故不能以豁免處理。若該檔已進共享環境,則應改以 V3 改名遷移處理
|
| c |
LegacyAccountView.snapshot 型別 |
以 Object(解析 JSON)承載 |
契約為 type: object;解析失敗則原樣回傳字串(不靜默丟棄) |
| d |
⚠️ 過程事故(自我揭露)
|
造種子時誤以 CREATE TABLE(含 EXCEPTION WHEN OTHERS THEN NULL)預先建了 LEGACY_ACCOUNT_MAP,致 V2 部分套用後失敗(Oracle DDL 自動提交、無交易保護),需清理殘留+刪除失敗之 history 列 |
教訓:造測試資料不得預先建立遷移將建立之物件;WHEN OTHERS THEN NULL 會吞掉真實錯誤,屬反模式 |
9.4 C-9 notification-service(2026-09-22 完成)——通道抽象先行
交付
| 項 |
內容 |
| 遷移 |
V2__notification_templates_and_notifications.sql:NOTIFICATION_TEMPLATES(唯一鍵 (TEMPLATE_CODE, CHANNEL, LANGUAGE)=多通道多語)+NOTIFICATIONS(STATUS 3 值對齊契約、RETRY_COUNT、FAIL_REASON)+notification_no_seq+內建 8 筆模板(AUTH_APPROVED × 4 通道、PAYMENT_RECEIVED × 2、STATEMENT_ISSUED × 1、AUTH_APPROVED/LINE_AOA 刻意停用以供「無可用通道」路徑示範) |
| 程式 |
DDD 四層:Notification(狀態守衛+重試)/NotificationTemplate(渲染與變數偵測)/NotificationMessage/DeliveryResult;3 埠(通知/模板/通道);3 JDBC;NotificationService;NotificationViewAssembler;2 端點(POST 201/GET 200);NotificationDispatchScheduler(取件遞送) |
|
通道抽象(裁定 F) |
NotificationChannelPort(supports+deliver)+唯一實作 LoggingNotificationChannel(本地 Log 通道 stand-in)——不呼叫任何閘道;正式環境僅需新增實作 bean,領域/應用層不動 |
| 契約 |
未改契約(2 端點、NotificationRequest/NotificationView 皆已備);新增 3 錯誤碼(SDD-006 §3.4:CC-NOT-1002/1003/4040) |
| 品質 |
單測 31 例 0 失敗(全模組 35 例);scan_scaffold/scan_mockdata/scan_faketest 各 0 命中;validate_sql(正規範圍:src/main/resources/db/migration)0 違規
|
真機 e2e(8097,十一項)
| # |
情境 |
結果 |
| ① |
受理(preferredChannels=["SMS"]) |
201:QUEUED、channels=["SMS"]
|
| ② |
同冪等鍵重送 |
回同一 notificationId(32d79c15-…),未重複落庫 |
| ③ |
未知樣板 |
422 CC-NOT-1001「通知樣板不存在或未啟用:NO_SUCH_TEMPLATE(語言 zh-TW)」 |
| ④ |
偏好與可用通道無交集(LINE_AOA 未啟用) |
422 CC-NOT-1002「已啟用 [EMAIL, LINE_BC, PUSH, SMS],與偏好 [LINE_AOA] 無交集」——未靜默改送
|
| ⑤ |
變數不足 |
422 CC-NOT-1003「通道 SMS 缺少 [cardLast4, merchant, currency]」——受理期即擋,未留下任何通知 |
| ⑥ |
非法通道值 FAX
|
400 CC-PLT-4003(參數錯誤,非 422) |
| ⑦ |
缺 X-Channel 標頭 |
400 CC-PLT-4001(含 details) |
| ⑧ |
排程遞送後查詢 |
200:SENT、sentAt=2026-09-22T11:45:38.928587Z
|
| ⑨ |
查無通知 |
404 CC-NOT-4040 |
| ⑩ |
識別碼非 UUID |
400 CC-PLT-4003 |
| ⑪ |
超長內容(SMS 上限 160 字元;實得 177) |
QUEUED →(retry 1,仍 QUEUED)→(retry 2)→ FAILED,failReason="SMS:通道 SMS 內容長度 177 字元超過上限 160 字元"
|
| ⑫ |
DB 複核 |
NOTIFICATIONS 2 列:NOTIF202609000001 SENT/retry=0/channels=["SMS"]/SENT_AT 19:45:38;NOTIF202609000002 FAILED/retry=2/fail_reason 已載;NOTIFICATION_TEMPLATES 8 列、LINE_AOA ENABLED=0
|
⚠️ 施行中追加/事故(附理由,皆已揭露)
| # |
事項 |
處理 |
理由 |
| a |
狀態值域脫鉤之更正 |
SCS-016 §3 原載 {PENDING, SENT, FAILED, RETRYING} → 更正為契約之 {QUEUED, SENT, FAILED}
|
契約為 API 曝露列舉之權威(前例:C-5 成功碼依契約更正)。PENDING ≠ QUEUED;RETRYING 契約未定義
|
| b |
重試不以獨立狀態表示 |
失敗時 RETRY_COUNT + 1 且狀態維持 QUEUED,達上限方轉 FAILED
|
若於 DDL 增列 RETRYING,契約無處回傳、須映射回 QUEUED=無對應語意之死狀態(假綠)。以自轉換表達,並於 SDD-005 §13 載明其與 SM-03(冪等)之區別(判準:有無狀態變更) |
| c |
FAILED 之可達性 |
本地 Log 通道之遞送規則含通道內容長度上限(SMS 160/PUSH 200/LINE_BC·LINE_AOA 1000/EMAIL 2000 字元) |
若 stand-in 永遠成功,則 FAILED 與重試不可達——測試只能證明「成功會成功」,屬假綠。長度上限為真實閘道之既有限制,以其為判定使失敗路徑可達且可測。⚠️ 此為本地 stand-in 之規則,非契約或 SDD 之規定 |
| d |
收件人未解析 |
PARTY_ID 僅為參照;recipientRef 以 party:<uuid> 形式記錄 |
契約 NotificationRequest 無收件人欄位(實測)、party-service 契約 4 端點皆非聯絡資訊(實測)→ 不假造收件人
|
| e |
刻意不設 PII 加密欄 |
NOTIFICATIONS 無 AesGcmFieldEncryptor 路徑 |
因 (d)——無來源之密文欄位=假綠(SCS-016 §5.4 ③) |
| f |
多語未啟用 |
LANGUAGE 固定 zh-TW
|
契約無語言欄位;模板仍以 (CODE, CHANNEL, LANGUAGE) 為鍵以備擴充 |
| g |
SCS-016 §5.3 之交付清單更正 |
原載「種子 ops/local/init/oracle/13-dev-notification-seed.sql」→ 改為「無獨立本地種子檔」
|
模板為產品內建、隨 V2 交付(NOT EXISTS 守衛、冪等);另立本地種子將造成同一資料兩處定義之漂移 |
| h |
SDD-001 §9 統計更正 |
Bounded Context 14 → 17、實體表 47 → 55
|
前版(1.12)之數字係 1.9 版之值,其後 §3.16/§3.17 新增 6 表未同步更新;本次逐項清點更正(方法已載於該節) |
⚠️ 發現之既有問題(非本輪引入,僅記錄、未逕改)
| # |
事項 |
說明 |
| i |
validate_sql 之掃描範圍模糊 |
以正規範圍(src/main/resources/db/migration)掃描 → 0 違規;惟若以 backend 全庫掃描,會命中 C-7 之 Postgres 讀模型 5 處(migration-postgres/V1__rpt_read_model.sql:SQL-04 表名非複數、SQL-05 缺 id/created_at/updated_at/version)。該檔刻意如此設計(SDD-001 §3.16:複合主鍵、無樂觀鎖),且不在 CSP-001 §5.3 豁免登錄檔之適用範圍(登錄檔明訂「僅供基礎設施表」)。建議(需 BEAM PMO 裁定方向):(a) 於 CSP-001 §5.3 明訂 migration-postgres 之處理(讀模型非 OLTP 主檔,SQL-04/SQL-05 之立法目的不適用);或 (b) 納入豁免登錄檔並放寬其適用範圍(須改登錄檔之約束條款)。本輪未逕改——此屬 C-7 之交付物與規則適用範圍之設計決策 |
10. 誠實揭露
- 本盤點之 DDL 現況為 Oracle 實測(
all_tables/all_constraints,2026-09-22);
契約欄位為逐 schema 解析(非抽樣)。
-
未實測者:
CC_RPT/CC_MIG/CC_NOTIF 之既有資料筆數為 0(無表,故必然為 0)——不涉及資料搬遷風險。
- C-6 之
DISPUTE_CASES/DISPUTE_TIMELINES 實測皆為 0 筆(2026-09-22)→
新增 NOT NULL 欄無回填風險;惟兩表之 CHECK 全為系統命名(SYS_C*,實測無具名約束)
→ 擴充值域時須比照前例動態尋得後重建為具名(CR-015/CR-016 同一手法)。
- 本 CR 僅盤點,不含實作;各服務之實作將逐支進行並各自回寫
HANDOVER/GOV-006。
-
C-9 之核心限制(真阻塞):實際發送未接——唯一通道實作為本地 Log 通道 stand-in,
訊息不離開本機;「通知發送通道(僅發送)」屬鐵則所稱 3 類真阻塞之一
(GOV-006 2.3.7)。收件人聯絡方式亦無法解析(契約無欄位、party-service 無聯絡端點)。
-
C-9 之本地 stand-in 規則已明標:通道內容長度上限(SMS 160 字元等)為近似值,
其目的在使 FAILED/重試路徑可達且可測(避免「永遠成功」之假綠);
正式環境須以真實閘道之限制校準(SCS-016 §10.6)。
-
§9.4 (i) 之掃描範圍問題已記錄但未逕改——屬 C-7 交付物與規則適用範圍之決策,待裁定。
11. 修訂記錄
| 版本 |
日期 |
修訂內容 |
| 1.0 |
2026-09-22 |
提出。依 BEAM PMO 指示「先一次盤點再逐支實作」,完成 C-6~C-9 四服務盤點:C-6 表已備但兩處正交維度混用+缺 3 欄;C-7/C-8/C-9 之 schema 完全無表(SDD-001 §3 僅涵蓋 14 context);C-8 另有契約與 SCS 粒度不一致、C-9 有發送通道真阻塞。提出 7 項裁定建議與實作順序。待 BEAM PMO 裁定 |
| 1.1 |
2026-09-22 |
BEAM PMO 裁定完成:A/B 採雙欄位、C 補 chargeback 欄、D 含 Postgres 讀模型(較建議為大)、E 採兩層 wave+job、F 通道抽象先行、G 補 SDD-001 三節+V2;實作順序依原序。新增 §8 裁定結果與 §8.1 裁定 D 之依賴揭露(Postgres 讀模型之 4 項前置依賴:schema 未定義、同步機制未實作〔W-2 Kafka 消費端 ⬜〕、MinIO 可落地、報表定義需內建),並提出落地切法建議(Oracle 作業指標 + Postgres 讀模型 + 批次投影 stand-in + CSV/JSON 輸出) |
| 1.5 |
2026-09-22 |
C-9 notification-service 實作完成並真機實測通過(見 §9.4)——C-6~C-9 四服務全數結案。依裁定 F 通道抽象先行(NotificationChannelPort + 本地 Log 通道 stand-in):V2 建 2 表+8 筆內建模板、DDD 四層、2 端點(201/200)+取件排程;新增 3 錯誤碼;單測 31 例 0 失敗;真機 e2e 十二項(含受理即驗證之三種 422、重試後 FAILED 且載明原因、DB 複核)。併揭露八項施行決策(a 值域更正/b 重試不設獨立狀態/c FAILED 可達性/d 收件人未解析/e 不設 PII 欄/f 多語未啟用/g 交付清單更正/h SDD-001 §9 統計更正)與一項既有問題(i validate_sql 掃描範圍模糊,僅記錄未逕改)。SCS-016 → 1.1、SDD-001 → 1.13(新增 §3.18)、SDD-005 → 1.7(新增 §13 通知狀態機;原 §13/§14 遞移為 §14/§15)、SDD-006 → 122 碼 |
| 1.4 |
2026-09-22 |
C-8 migration-service 實作完成並真機實測通過(見 §9.3)——依裁定 E 之兩層 WAVE+JOB:Oracle V2(3 表+5 波次種子)、契約加 waveCode、2 端點+取件排程;單測 17 例 0 失敗;真機 e2e 六項(試跑不動任何列 vs 非試跑標記、W9 → 422、冪等、DB 複核)。併揭露兩項事故:①SQL-04 命名違規之例外處置(V2 僅本機套用故直接更正;豁免登錄檔明訂業務主檔不適用);②造種子時誤預建遷移將建之物件致 V2 部分失敗(WHEN OTHERS THEN NULL 吞錯之反模式) |
| 1.3 |
2026-09-22 |
C-7 report-service 實作完成並真機實測通過(見 §9.2)——Oracle 作業指標(2 表+內建定義)+Postgres 讀模型(rpt.daily_auth_summary,CQRS 讀側首例)+批次投影(追趕視窗)+非同步作業產生(CSV/JSON)。單測 16 例 0 失敗;真機 e2e 六項(投影 4 列與權威側逐欄一致、202→COMPLETED、CSV 內容、XLSX 明確 FAILED、冪等)。併更正 §8.1 之「MinIO 可落地」為誤(實測無 MinIO),並揭露跨 schema 授權 stand-in 與雙資料源三大陷阱(皆已寫入程式註解) |
| 1.2 |
2026-09-22 |
C-6 dispute-service 實作完成並真機實測通過(見 §9.1):V2 遷移(4 新欄+具名約束+序號)、契約重生(orgStage 新增/disputeType 語意補述)、DDD 四層+3 端點;單測 19 例 0 失敗;真機 e2e 六項(含雙軸同列獨立之 DB 複核、跳號 409、重複提交冪等)。併施行中追加四項(org 預設 INTERNAL/時限 30 日未參數化/evidence 型別改 Object 對齊契約/扣款冪等以狀態判斷)。⚠️ 本版併修正章節編號(插入 §9 後原 §9/§10 順移為 §10/§11,使實體順序連號)。SCS-012 → 1.1、SDD-001 → 1.10 |