Project

General

Profile

CR #201 » SCS-017_報表服務程式設計規格.md

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

 

SCS-017 報表服務(report-service)程式設計規格

項目 內容
文件編號 SCS-017
版本 1.6(GOV-007 §4:§6.2 之下載「作業未就緒」錯誤碼定案——由暫用之 CC-PLT-3001(非法狀態轉換)改為另立之 CC-PLT-3003(409 資源尚未就緒),語意區隔;錯誤碼 123 → 124。前版 1.5(HANDOVER #A-109:新增 §6.2 報表檔案下載端點(代理端點)——BEAM PMO 裁定採方案 B;§8.1.4 定案)
狀態 已核定(規格先行)
對應需求 SYS-1(Report Generation Tool)、ACQ-4.5(商店報送與報表)、ISS-2.11(信用卡報送與報表)、DEB-3.4(DEBIT 卡報送與報表)
服務埠號 待骨架產生時依 CSP-006 §4 分配(自 8081 起編)
套件根 com.beam.cardcore.rpt
權威 schema CC_RPT(尚未建表;報表以 rpt Postgres 讀模型為主)
關聯文件 SDD-006 §7、BRD-002、GOV-005

1. 對應需求

FR 編號 需求名稱 來源
SYS-1 Report Generation Tool 能力 BRD-002
ACQ-4.5 商店報送與報表(收單) BRD-002
ISS-2.11 信用卡報送與報表 BRD-002
DEB-3.4 DEBIT 卡報送與報表 BRD-002

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

圖種 文件 數量(實測) 本服務相關
ER-Diagram SDD-001_領域模型與ER-Diagram 16 個 erDiagram ⚠️ 本服務 schema 尚未建表(SDD-001 §3 僅涵蓋 14 個 bounded context;報表主體位於 rpt Postgres 讀模型)
DFD SDD-002_DFD與流程分解 10 個 flowchart 報表產生流
Sequence SDD-003_Sequence-Diagrams 15 個 sequenceDiagram 報表作業時序
狀態機 SDD-005_狀態機設計 10 個 stateDiagram ⚠️ 報表作業狀態待確認
API Contract SDD-006 + contracts/openapi/report-service.yaml 17 份 本服務 2 支端點

2. 服務職責與邊界

承作:

  • 報表定義(ReportDefinition:欄位、篩選、排程、格式)
  • 報表作業(ReportJob:非同步產生、狀態、輸出)
  • 報送(監管/商店報送,含 ACQ-4.5/ISS-2.11/DEB-3.4)

不做(屬他服務):

  • 交易處理 → 各領域服務
  • 即時查詢 → 各領域服務 API

3. 領域模型

  • 聚合(依 services.yaml):ReportDefinition(報表定義)、ReportJob(報表作業)
  • 值物件:ReportCode、ReportStatus、OutputFormat(PDF/CSV/EXCEL)
  • 狀態(設計意圖):ReportJob.status ∈ {QUEUED, RUNNING, COMPLETED, FAILED}(待 SDD-005 正式化)

4. 程式結構

套件/類別 職責 對應需求
domain/model/ReportDefinition ReportJob 聚合與值物件 SYS-1/ACQ-4.5/ISS-2.11/DEB-3.4
domain/port/ReportRepository 持久化埠(待建表) —
application/ReportService 定義/作業/報送 SYS-1
infrastructure/jdbc/*RepositoryImpl JDBC 實作(待建表) —
interfaces/rest/ReportController REST 端點 SYS-1

5. 資料模型

CR-017 裁定 D(BEAM PMO 2026-09-22):C-7 範圍含 Postgres 讀模型。本節為設計權威,
實體交付見 §5.4(V2 遷移 + db/migration-postgres/V1)。

5.1 架構:作業指標(Oracle)+ 讀模型(Postgres)

存放 內容 理由
Oracle CC_RPT REPORT_DEFINITIONS(報表定義)/REPORT_JOBS(作業指標:狀態、筆數、輸出參照) 作業狀態為權威事務資料(契約直接對應);與 CSP-006 §2.4「Oracle 為權威 OLTP」一致
Postgres rpt 讀模型表(報表主體) CQRS 讀側(SCS-017 §7):報表查詢不佔用權威 OLTP

5.2 讀模型(Postgres rpt,實測 schema 已存在、0 表)

表 欄位 說明
rpt.daily_auth_summary business_date DATE/channel/tx_type/decision/tx_count BIGINT/total_amount NUMERIC(19,0)/currency CHAR(3)/refreshed_at TIMESTAMPTZ 每日授權彙總(DAILY_AUTH_SUMMARY);主鍵=(日 × 通路 × 交易型別 × 決策),冪等 upsert

⚠️ CQRS 讀側首例:實測 evt/proj/rpt 三 schema 由 ops/local/init/postgres/01-init.sql
建立,惟迄今無任何服務使用(grep jdbc:postgresql 全庫 0 命中)——本服務為首個接線者,
其 DataSource/Flyway 配置方式即為後續讀側之慣例(見 §5.4)。

5.3 投影(Oracle → Postgres)

SCS-017 §7 原載「讀模型與寫側最終一致」,惟同步機制未實作(GOV-006 4.8 W-2 Kafka 消費端 ⬜)。
本期採 批次投影(排程自 Oracle 權威彙總後 upsert 讀模型)為 stand-in——
不阻塞、可實測、可重跑,且與 #A-79 之排程慣例一致;待 Kafka 消費端落地後可改為事件驅動。

5.4 交付

項 位置
Oracle 作業指標 V2__report_definitions_and_jobs.sql(+report_job_no_seq+內建定義 DAILY_AUTH_SUMMARY)
Postgres 讀模型 db/migration-postgres/V1__rpt_read_model.sql(獨立 Flyway 實例,schemas("rpt"))
輸出 ${cardcore.report.output-dir}(預設 ops/local/logs/reports);downloadUrl 為 file://
⚠️ 誠實揭露 物件儲存「基礎設施已就緒、程式尚未接線」(2026-10-01 更新,HANDOVER #A-106):本地物件儲存已建置並實測(Garage v2.4.1,S3 API 埠 3900、bucket cardcore-artifacts,見 SAD-006 §3.2)——惟本服務尚未接線,downloadUrl 仍為 file://。⚠️ 併更正:原載「實測 compose 無 MinIO」仍成立,但結論已變——不是「無替代方案」,而是 MinIO 開源版已封存(官方回 410)、改用 Garage。XLSX/PDF 需額外函式庫,未支援(請求即回 FAILED 並載明原因,非靜默)

6. API 設計(來自 contracts/openapi/report-service.yaml)

端點 方法 說明 對應 FR
/api/v1/report-jobs POST 報表作業提交 SYS-1/ACQ-4.5/ISS-2.11/DEB-3.4
/api/v1/report-jobs/{jobId} GET 報表作業查詢 SYS-1
/api/v1/report-jobs GET 報表作業清單(最近 N 筆;2026-09-30 補建,見 §6.1) SYS-1/ACQ-4.5/ISS-2.11/DEB-3.4
/api/v1/report-jobs/{jobId}/download GET 報表檔案下載(代理端點;2026-10-01 補建,見 §6.2) SYS-1/ISS-2.11

錯誤碼:404 找不到作業/定義、400 參數錯誤、409 重複作業。

6.2 報表檔案下載端點(代理端點;2026-10-01 補建,HANDOVER #A-109)

緣由:W-5 階段二接線後,DOWNLOAD_URL 存的是物件鍵(§8.1.4),
但如何下載尚未實作。BEAM PMO 2026-10-01 裁定採方案 B(代理端點)——
理由:本案為銀行核心系統,報表可能含敏感資料;presigned URL 會把授權交給物件儲存
(URL 一旦外流,有效期內人人可載),與全案「一律經服務認證」之架構(SAD-005 §3)不一致。

GET /api/v1/report-jobs/{jobId}/download(回應檔案位元組,200)

項目 規格
回應 非 ApiResponse 封套——直接回檔案內容(application/octet-stream)
Content-Disposition attachment; filename="<jobNo>.<ext>"(供瀏覽器另存)
Content-Type 依格式(CSV → text/csv; charset=utf-8;JSON → application/json; charset=utf-8)
認證 受本服務認證與授權保護(與其他端點一致)——此即方案 B 之核心價值

行為要求:

  1. 唯讀——僅取回,不觸發產生。
  2. ⚠️ 作業須為 COMPLETED:QUEUED/RUNNING 無檔案可下載 → 409 CC-PLT-3003(資源尚未就緒)(不得回空檔假裝成功)。
  3. ⚠️ FAILED 亦無檔案 → 同樣 409 CC-PLT-3003(並於訊息載明作業失敗原因)。
  4. 作業不存在 → 404 CC-PLT-4040。
  5. ⚠️ 物件不存在(鍵在但物件儲存查無) → 404——如實回報,不回空檔:
    這是「DB 說有、儲存說沒有」的不一致,須讓人看見(比照 CSP-001 §8 B2 不靜默)。
  6. 不快取。

✅ 409 錯誤碼已定案(BEAM PMO 2026-10-01 裁定;GOV-007 §4):
原暫用 CC-PLT-3001(非法狀態轉換)——因 SDD-006 §3.4 之既有 4 個 409 平台碼
(CC-PLT-2001/2002/3001/3002)皆為「狀態機轉換」語意。
惟本情境「資源尚未就緒」(QUEUED/RUNNING/FAILED)與「狀態機非法轉換」語意不同 →
已於 SDD-006 §3.4 另立 CC-PLT-3003(409 資源尚未就緒) 並回填登錄表
(錯誤碼 123 → 124);本端點改用之。

⚠️ DOWNLOAD_URL 仍存物件鍵(不變)——下載由此端點動態取回,
故資料庫欄位不必隨 URL 效期變動,且換物件儲存後端不影響下載路徑。

⚠️ 未竟:(i) 無範圍請求(Range)支援——大檔續傳俟需求確認;
(ii) 無下載稽核留痕(誰下載了什麼)——報表可能含敏感資料,建議另立 CR 補稽核。

6.1 報表作業清單端點(2026-09-30 補建,HANDOVER #A-104)

緣由:SDD-007 §5.4.26 之桶 C「批次作業監控」(/system/batch)——
bff-web 依 SCS-020 §9.9 聚合四服務之批次執行紀錄,需各服務提供「列出批次」端點。
⚠️ 實測揭露:/api/v1/report-jobs 原僅有 POST(送交)——GET 實測回 404。

GET /api/v1/report-jobs?limit=N(回應 ReportJobSummary[],200)

參數 型別 說明
limit integer(可選) 回傳筆數上限。預設 20,上限 100

回應欄位(ReportJobSummary):

欄位 說明
jobId 作業識別碼(UUID)
jobNo 作業編號(RPTJ + 年月 + 連號,可稽核)
reportCode 報表代碼
format 輸出格式(CSV/JSON/…)
status QUEUED/RUNNING/COMPLETED/FAILED
rowCount 產出列數(未完成時為 0)
downloadUrl 輸出參照(本期為 file://<絕對路徑>,見 §10)
errorDetail 失敗原因(非 FAILED 時為 null)
startedAt/finishedAt 開始/完成時刻(未開始/未完成者為 null)

⚠️ 為何不沿用既有 ReportJobView:該 DTO 為受理/單筆查詢回應之形狀,
未含 jobNo/reportCode/format——清單需可辨識「這是哪一支報表」與時間錨點
(SCS-020 §9.9.4 #3)。故另立清單專用 DTO,不動既有已驗證之契約。

行為要求:

  1. 唯讀——僅查詢,不觸發產生(觸發為 POST /report-jobs)。
  2. 依 startedAt 遞減(最近送交者在前;startedAt 為 null 之 QUEUED 置後)。
  3. 無資料回 200 + 空陣列——非 404。
  4. 不快取(SCS-020 §9.9.4 #4)。
  5. ⚠️ 與 POST /report-jobs 同路徑、不同方法——須明確 @GetMapping/@PostMapping。

錯誤碼:400 參數錯誤(limit 超出 1..100)。無 404 情境。

未竟:

  • 不支援分頁游標(僅取最近 N 筆);不含 parameters 原文(清單僅需辨識與狀態;
    參數原文仍由單筆查詢取得)。

7. 關鍵邏輯

  • 非同步產生:ReportJob 入隊,common-batch 批次產出,避免阻塞。
  • 讀模型:報表資料源自 rpt Postgres(CQRS 讀側),與寫側(CC_*)最終一致。
  • 報送:監管/商店報送依定義排程,輸出於物件儲存。

CR-017(BEAM PMO 2026-09-22 裁定 D)後定案之實作要點

  1. 非同步(實作方式):受理僅入隊(QUEUED)即回 202;實際產生由排程取件
    (ReportJobScheduler)→ RUNNING → COMPLETED/FAILED——狀態由流程推進。
  2. 讀模型投影(stand-in):ReportProjectionScheduler 以追趕視窗「N 日前 ~ 昨日」
    自 Oracle 權威彙總後 upsert 至 rpt.daily_auth_summary(冪等、可回補)。
    ⚠️ 正規機制為事件驅動(W-2 Kafka 消費端 ⬜);本地以跨 schema 授權暫代(見 §10)。
  3. 產生器:DAILY_AUTH_SUMMARY → 讀讀模型 → 輸出 CSV/JSON(金額為分);
    XLSX/PDF 未支援 → 作業 FAILED 並載明原因(不產空檔、不假裝成功)。
  4. 冪等:受理以冪等鍵派生主鍵(UUID v3,前綴 rpt:job:)——同鍵重送回同一作業。
  5. 失敗可稽核:FAILED 必載 errorDetail(領域層強制,未載即拒)。

8. 外部依賴與降級

依賴 失敗處理(Stand-in)
rpt Postgres 讀模型、common-batch 報表為非同步;來源延遲僅影響報表即時性,不影響交易

8.1 物件儲存(輸出媒體)——2026-10-01 補建(HANDOVER #A-107)

緣由:W-5 之本地替代方案已建置並實測(Garage v2.4.1,SAD-006 §3.2),
本節為程式接線之規格——規格先行,先立本節再動程式。

8.1.1 契約:S3 API(不綁特定實作)

本服務只依賴 S3 API(endpoint + region + 靜態憑證),不依賴任何 Garage 專屬特性。
故本地後端(Garage)與正式環境(客戶之企業物件儲存)可互換,程式不動。

組態 值(本地)
endpoint http://localhost:3900
region cardcore(由 config/garage.toml 之 s3_region 決定,非 AWS 固定值)
bucket cardcore-artifacts
憑證 GARAGE_ACCESS_KEY/GARAGE_SECRET_KEY(環境變數,不寫入組態檔)
物件鍵前綴 reports/<jobNo>.<ext>(以 jobNo 為鍵——可稽核、可追溯至作業)

8.1.2 分層:埠(application)+ 實作(infrastructure)

角色 類別 說明
埠 application/ObjectStoragePort 本輪只有 put(key, bytes, contentType)
實作 infrastructure/storage/S3ObjectStorageAdapter(+S3StorageConfig) AWS SDK v2 for Java;沿用 AccountOverviewPort 之慣例

⚠️ get/delete 本輪「不」納入埠(規格與實作一致):本輪範圍為「上傳 + 存鍵」,
取回/刪除俟「下載呈現方式」(§8.1.4)定案後再補——
依「不預先加未使用之介面方法」之原則,避免死碼。

⚠️ S3StorageConfig 之 fail-closed:憑證刻意不給預設值——缺任一項即啟動期失敗
(IllegalStateException),不靜默略過、不退回本地檔案(§8.1.5)。
endpoint/region/bucket 則給本地預設值(比照 POSTGRES_URL 之慣例)。
⚠️ path-style 預設 true——本地 localhost 必須用路徑式尋址
(虛擬主機式會把 bucket 名當前綴,cardcore-artifacts.localhost:3900 無法解析);
正式環境若需虛擬主機式,改組態即可,程式不動。

⚠️ 為何要埠:與 KeyProvider/ServiceClientHttpClient 同一思路——
換後端(Garage → 客戶企業儲存)、換 SDK、或改為測試替身時,應用層不動;
且應用層得以手寫 fake 測試(無須起 S3)。

8.1.3 輸出流程(RUNNING → COMPLETED)

① 產生內容(CSV/JSON,記憶體)
② put(key, bytes, contentType)  → 上傳物件儲存
③ complete(rowCount, key, now)  → 將「物件鍵」寫入 REPORT_JOBS.DOWNLOAD_URL

⚠️ 順序不可顛倒:先上傳成功、才標 COMPLETED。
若先標完成再上傳,上傳失敗將留下「已完成但沒有檔案」的作業——
那是假成功,比失敗更難查(比照 #C-42「零產出必須可區分」之教訓)。

8.1.4 ⚠️ downloadUrl 之語意(關鍵決策——本節之核心)

REPORT_JOBS.DOWNLOAD_URL 是寫入資料庫的欄位,故:

⚠️ 不得存入 presigned URL——presigned URL 有時效,
存進 DB 後一旦過期即失效,而作業紀錄不會再更新(COMPLETED 為終態)。
這會產生「資料庫裡有一條永遠打不開的連結」的缺陷。

故:DOWNLOAD_URL 存「物件鍵」(如 reports/RPTJ20261001000001.csv),
不存 URL——物件鍵永久有效,且下載呈現方式(見下)可日後更換而不動資料。

下載之呈現方式(✅ 2026-10-01 BEAM PMO 裁定採 B):

方案 做法 優點 缺點
A. 動態 presigned URL 查詢時由 ObjectStoragePort 產生短效 URL 改動最小 URL 會過期(前端顯示的字串有時效);授權交給物件儲存——URL 一旦外流,有效期內人人可下載
B. 代理下載端點(✅ 採用) 新增 GET /api/v1/report-jobs/{jobId}/download,由服務向物件儲存取回後回傳 受本服務認證與授權保護(與其他端點一致);URL 永久有效;不洩漏物件儲存憑證 需新增端點(契約+前端+測試);檔案流量經服務

裁定理由:本案為銀行核心系統,報表可能含敏感資料;把授權交給物件儲存的 presigned URL,
與全案「一律經 gateway 認證」之架構不一致(SAD-005 §3)→ 採 B。
端點規格見 §6.2。

8.1.5 降級政策

情境 行為
物件儲存不可用(連線失敗/逾時) 該作業標 FAILED 並載明原因——不得退回寫本地檔案(否則「已上傳」與「寫本機」兩種狀態混雜,日後無法辨識)
未組態 cardcore.storage.*(endpoint 空白) 啟動期即失敗(fail-closed),不靜默略過——比照 CR-010 之出站憑證政策

⚠️ 不再有 file:// fallback:接線後移除本地檔案路徑,避免「兩種輸出媒體並存」。
此為刻意的破壞性變更——SCS-017 §10.1 之 stand-in 記載同時廢止。

8.1.6 依賴(已查證 + 已 spike 驗證)

項目 值
SDK software.amazon.awssdk:s3:2.55.9(Maven Central 實查 :s3 與 :bom 皆存在;版本鎖於 gradle/libs.versions.toml)
傳遞相依 :s3 之 sync 客戶端預設即 url-connection-client(JDK 內建)→ 不需 Apache HttpClient/Netty,與 Spring 之 HTTP 疊層無衝突
驗證 ✅ 編譯 + 執行期皆通過(version-compat-verification skill 步驟 4/5)——見下

8.1.7 ⚠️ ★ S3 相容性之兩個地雷(實測踩到並修除,必讀)

這兩項編譯期不會報錯、對 AWS S3 也無感,但在 S3 相容實作(Garage/MinIO 等)
一律失敗——是典型的「靜默相容性地雷」。

地雷 ①:SDK 2.30 起預設「盡量加校驗」+ aws-chunked 串流簽章

項目 內容
症狀 上傳回 400:Bad request: Invalid payload signature (Service: S3, Status Code: 400)
根因 SDK 2.30 起 requestChecksumCalculation 預設為 WHEN_SUPPORTED,會附加 x-amz-checksum-* trailer;搭配 aws-chunked 串流簽章,S3 相容實作不支援
修法 S3Configuration.chunkedEncodingEnabled(false) + client 層 requestChecksumCalculation(WHEN_REQUIRED)/responseChecksumValidation(WHEN_REQUIRED)——即 SDK 2.30 之前的預設值,對 AWS S3 亦完全有效

地雷 ②:校驗設定「只能在一處」

項目 內容
症狀 啟動期失敗:IllegalStateException: Checksum behavior has been configured on both S3Configuration and the client/global level. Please limit checksum behavior configuration to one location.
根因 於 S3Configuration 亦設 checksumValidationEnabled,與 client 層之校驗設定衝突(SDK 明文禁止)
修法 校驗只在 client 層設;S3Configuration 只留尋址方式(pathStyleAccessEnabled)與 chunkedEncodingEnabled

8.1.8 ★ 真機驗證(2026-10-01,HANDOVER #A-107)

驗證項 結果
服務啟動 ✅ S3StorageConfig 記錄「物件儲存(S3 API)已建立:endpoint=http://localhost:3900 region=cardcore pathStyle=true」
送出作業(POST /report-jobs,CSV) ✅ 202、QUEUED
排程取件 ✅ COMPLETED、rowCount=7
DOWNLOAD_URL 之值 ✅ reports/RPTJ202610000006.csv(物件鍵,非 file://)
物件確實進 Garage ✅ 以 S3 客戶端取回:ContentType=text/csv; charset=utf-8、385 bytes、內容為真實讀模型資料(表頭 + 7 列)
負樣本:本機不留檔 ✅ 近 10 分鐘內無新增本地報表檔——證實未退回 file://(§8.1.5)
失敗路徑(修正前) ✅ 亦已驗證:上傳失敗時作業標 FAILED 並載明原因(Invalid payload signature),無假成功、無本地 fallback

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

  • 報表定義/作業/查詢(SYS-1)
  • 商店/信用卡/DEBIT 報送(ACQ-4.5/ISS-2.11/DEB-3.4)、格式與排程

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

  • CC_RPT 尚未建表 → 已建(V2__report_definitions_and_jobs.sql;SDD-001 §3.16 已補 DDL 章節)。
  • 報表作業狀態機待 SDD-005 正式化(本期狀態轉換由領域層守衛,未建模為正式狀態機)。
  • report-service 骨架與埠號尚未產生(規格先行) → 已產生,埠號 8098,已啟動實測。

CR-017 執行後新增之限制(2026-09-22)

  1. 物件儲存已接線,惟「下載呈現」尚未提供(2026-10-01,HANDOVER #A-107):
    輸出已改走物件儲存(S3 API)(本地 Garage v2.4.1,見 SAD-006 §3.2),
    DOWNLOAD_URL 存物件鍵(非 URL,理由見 §8.1.4);
    ⚠️ 已移除本地檔案(file://)fallback(§8.1.5)。
    仍待:下載呈現方式(方案 A 動態 presigned/方案 B 代理端點,§8.1.4)待 BEAM PMO 裁定——
    故目前前端只顯示物件鍵文字,尚無法直接下載。
    正式環境為客戶之企業物件儲存(契約為 S3 API,換後端只需改組態、程式不動)。
  2. 投影之來源以跨 schema 讀取暫代(GRANT SELECT ON CC_AUTH.AUTHORIZATIONS TO CC_RPT,
    ops/local/init/oracle/12-dev-report-projection-grant.sql)——本地 stand-in:
    正規為事件驅動(W-2 ⬜);經 API 亦不可行(authorization-service 之 allowed-callers
    依 SAD-002 §5 為空集合)。正式環境不得如此(破壞服務邊界)。
  3. 僅一支報表:DAILY_AUTH_SUMMARY;其餘報表(監管/商店報送)未實作。
  4. 僅兩種格式:CSV/JSON;XLSX/PDF 未支援(需額外函式庫)。
  5. 無排程報送:REPORT_DEFINITIONS 之排程欄位(Cron)未納入本期(契約無排程端點)。
  6. 無預設區間策略:參數未提供時取讀模型全部資料(避免產出看似正確卻空無一列之報表)。
  7. REPORT_DEFINITIONS 無管理端點:定義由遷移內建;契約未定義 CRUD。
  8. common-batch 未使用:SCS-017 §7 原載「common-batch 批次產出」,
    本期以服務內排程實作(common-batch 尚未落地)。

11. 修訂記錄

版本 日期 修訂者 說明
1.6 2026-10-01 WorkBuddy AI §6.2 之下載「作業未就緒」錯誤碼定案(GOV-007 §4;BEAM PMO 裁定)——由暫用之 CC-PLT-3001(非法狀態轉換)改為另立之 CC-PLT-3003(409 資源尚未就緒)。緣由:「資源尚未就緒」(QUEUED/RUNNING/FAILED)與「狀態機非法轉換」語意不同;已於 SDD-006 §3.4 補登 CC-PLT-3003(錯誤碼 123 → 124)並重生 ErrorCodes.java(--force);ReportJobService.download 與 ReportJobServiceTest 同步。★ 解除 v1.5 所載之「語意落差」
1.5 2026-10-01 WorkBuddy AI 新增 §6.2「報表檔案下載端點(代理端點)」(HANDOVER #A-109)——BEAM PMO 裁定採方案 B(代理端點),§8.1.4 由「待裁定」改為定案。規格先行:立 §6.2 → 改 gen_openapi.py(新增 BINARY_RESP 哨兵以產生非封套之二進位回應)→ 契約 86 端點、0 錯誤 → 實作(ObjectStoragePort.get + S3ObjectStorageAdapter.get + ReportJobService.download + controller)→ 前端 downloadReportFile + 下載鈕 → 真機驗證。★ 設計要點:(i) 回應為檔案位元組(application/octet-stream + Content-Disposition),全案唯一非封套回應;(ii) 追蹤碼改以 X-Trace-Id 回應標頭回傳(回應體已非封套,否則此路徑追蹤斷掉);(iii) 三種「沒有檔案」分開講:作業不存在→404、作業未完成/失敗→409、物件查無→404 如實回報不一致(不回空檔)。★ 併載錯誤碼語意落差之誠實揭露:409 之既有平台碼皆為「狀態機轉換」語意,本情境以最接近之 CC-PLT-3001 表達,不為單一端點擴充登錄表;若此類情境增多建議另立 CC-PLT-3003。真機驗證:QUEUED→409 CC-PLT-3001;完成後→200、Content-Disposition 正確、下載內容與物件儲存內之原始檔逐位元組一致(SHA-256 相同);作業不存在→404、非 UUID→400
1.4 2026-10-01 WorkBuddy AI 新增 §8.1「物件儲存(輸出媒體)」——程式接線(HANDOVER #A-107)。規格先行:立 §8.1 → 加依賴(awssdk:s3:2.55.9,Maven Central 實查)→ 實作埠與 S3 實作 → 真機驗證。設計要點:埠(application/ObjectStoragePort)+ 實作(infrastructure/.../S3ObjectStorageAdapter)——應用層只認物件鍵,換後端程式不動;只依賴 S3 API。★ downloadUrl 語意定案:DOWNLOAD_URL 存物件鍵(非 URL)——presigned URL 會過期,存進 DB 即成死連結(§8.1.4)。⚠️ 移除 file:// fallback(避免兩種輸出媒體並存,§8.1.5);憑證 fail-closed(缺則啟動期失敗)。★ 併載 S3 相容性兩個地雷(實測踩到並修除):① SDK 2.30 起預設校驗+aws-chunked → S3 相容實作回 400 Invalid payload signature(修:關閉 chunked + 校驗改 WHEN_REQUIRED);② 校驗只能在一處設,兩處並設會啟動期失敗。真機驗證:作業 COMPLETED、DOWNLOAD_URL=reports/RPTJ202610000006.csv、物件確實可自 Garage 取回(385 bytes)、本機未留檔
1.3 2026-10-01 WorkBuddy AI 物件儲存狀態更正(HANDOVER #A-106):§5.4/§10.1 原載「物件儲存未部署」→ 更正為「基礎設施已就緒、程式尚未接線」。本地物件儲存已建置並實測(Garage v2.4.1,S3 API 埠 3900、region cardcore、bucket cardcore-artifacts,見 SAD-006 §3.2);惟本服務尚未接線,downloadUrl 仍為 file://。★ 併載選型更正:原「無 MinIO」之觀察仍成立,但結論已變——MinIO 開源版已封存不再維護(官方下載站 410 Gone),故改採 Garage。接線所需 Java SDK 已查證 awssdk:s3:2.55.9(未做 spike,不得視為已驗證)
1.2 2026-09-30 WorkBuddy AI 新增 §6.1「報表作業清單端點」(GET /api/v1/report-jobs?limit=N)——解鎖 SDD-007 §5.4.26 桶 C「批次作業監控」(SCS-020 §9.9)。⚠️ 併載實測揭露:該路徑原僅有 POST、GET 回 404。新增 ReportJobSummary(含 jobNo/reportCode/format/時間錨點)——不沿用既有 ReportJobView(受理/單筆查詢形狀),不動既有已驗證之契約。limit 預設 20/上限 100;無資料回空陣列非 404
1.0 — 開發團隊 初版(規格先行)
1.1 2026-09-22 WorkBuddy AI CR-017 裁定 D 執行完成(含 Postgres 讀模型)——①**§5 改寫為設計權威(Oracle 作業指標/Postgres 讀模型之分工、rpt.daily_auth_summary 定義、批次投影 stand-in、交付清單);②§7** 新增實作要點(非同步語意、投影追趕視窗、產生器支援矩陣、冪等、失敗可稽核);③**§10** 移除兩項已解除限制、新增 8 項(物件儲存未部署/跨 schema stand-in/僅一支報表/僅兩種格式/無排程報送/無預設區間/無定義管理端點/common-batch 未使用);④補建本修訂記錄區(原文件欠缺)。單測 16 例 0 失敗;真機 e2e 六項
(49-49/67)