報表產生開發計畫
狀態: 實作中(REP-P0 Web Prototype slice 已完成) 依據: ADR-029: Headless 報表引擎與中介文件模型
更新日期: 2026-08-06
目標與成功條件
建立 developer/AI-first 的 headless 報表管線,使應用可以用 Java 建立 ReportDocument、在
server 直接產生 PDF,並讓 appfuse-web 對大型 PDF 做 range-based 預覽、選頁與瀏覽器列印。
第一個可用版本完成時,必須能用同一組公開契約產生至少三類代表性報表:
- 多頁明細表:群組、彙總、重複表頭與頁碼。
- 含中文長文、圖片與跨頁區塊的版面報表。
- 含長條圖或折線圖的統計報表。
固定座標證書、申請書及政府 PDF 表單不屬於本計畫的 MVP;另案建立 fixed-layout PDF capability。
現況與缺口
| 範圍 | 現況 | 缺口 |
|---|---|---|
| Server 文件能力 | document 以 XWPF handle 操作 DOCX | 沒有 renderer-neutral report IR 與直接 PDF renderer |
| Server 檔案下載 | FileResponseBuilder 有 HTTP Range 行為 | FileStorage 缺少原生 ranged-read 契約,部分 backend 可能仍讀取/略過前綴 |
| Web PDF 預覽 | PdfViewer 使用 PDF.js,並延遲繪製鄰近頁面 | authenticated URL 先經 axios 下載完整 Blob,無法利用 PDF.js range loading;頁面資源回收無界 |
| Web 列印 | 以新視窗開啟 PDF | 尚無可靠的 ready handshake、subset PDF 流程與可測試列印契約 |
| 圖表 | 應用可各自輸出圖片 | 沒有 Figure/向量交換邊界與 server-side chart adapter |
交付架構
domain/query data
│
▼
ReportTemplate<T> ──► ReportDocument (flow IR)
│
┌─────────┴─────────┐
▼ ▼
Chart adapter PDF renderer
ChartSpec→Figure │
▼
ReportArtifact/PDF
│
storage + authorized URL
│
┌────────────────────┴────────────────────┐
▼ ▼
PDF.js range preview subset PDF → print()
工作包與依賴
| ID | 工作包 | 依賴 | 主要產物 |
|---|---|---|---|
| REP-P0 | Web Prototype contract slice | ADR-029 | ReportViewer、lifecycle state、page selection、mockable adapter |
| REP-01 | 代表性報表 corpus 與 renderer spike | ADR-029 | 測試資料、版面基準、backend 選型證據 |
| REP-02 | ReportDocument 核心契約 | REP-01 | immutable IR、builder、style/measure、validation |
| REP-03 | PDF renderer 基線 | REP-02 | 文字、表格、圖片、分頁、頁首頁尾、diagnostics |
| REP-04 | Figure 與統計圖表 | REP-02、REP-03 | Figure、SVG policy、至少一個 chart adapter |
| REP-05 | ReportArtifact 與 server delivery | REP-03 | 產物契約、同步/非同步 seam、授權下載整合 |
| REP-06 | 真正的 ranged read | REP-05 | FileStorage range SPI、backend contract tests、CORS headers |
| REP-07 | 大型 PDF 按需預覽 | REP-06 | URL-based PDF.js 載入、page LRU、取消與錯誤處理 |
| REP-08 | 選頁 subset 與瀏覽器列印 | REP-05、REP-07 | page selection、subset service、print lifecycle |
| REP-09 | hardening 與發布 | REP-03~08 | conformance、benchmark、安全、指南、changelog |
REP-P0 可在 server 尚未實作時先供下游 Prototype 使用;它固定的是 artifact delivery 與使用者
操作接縫,不是 server ReportDocument。REP-01~03 是 server 報表核心的最小縱切;REP-04 可在
核心穩定後平行發展。REP-05~08 組成實際交付與整合體驗,不應反向污染 ReportDocument。
REP-P0 — Web Prototype contract slice
工作內容
- 在
appfuse-web提供ReportViewer,以高階報表語意組合既有PdfViewer。 - 以 discriminated state 表達 generating、ready、failed 與 expired;application 仍擁有 job 與 artifact lifecycle。
- 提供全部、目前頁、連續範圍與指定頁碼的 physical-page selection;送入 adapter 前完成驗證、 排序與去重。
- 定義
ReportViewerAdapter;Prototype 使用 mock adapter,整合期再換成 subset PDF/print adapter。 - 元件不認識
ReportDocument、renderer vendor、API route 或 persistence Entity。
完成條件
- Storybook 可操作 ready 報表及四種選頁模式,並可查看 generating/failed/expired 狀態。
- component tests 驗證完整 lifecycle、四種選頁、callback identity、權威頁數、adapter failure/成功及 cancellation/unmount abort。
- 公開 export、README 與 changelog 完整;TypeScript、ESLint、Vitest 與 framework build 通過。
- REP-07/REP-08 整合時沿用此契約,不要求 Prototype 改寫畫面流程。
REP-01 — 報表 corpus 與 renderer spike
工作內容
- 把三類代表性報表轉成固定測試資料與預期 PDF 頁面。
- 針對候選 PDF backend 驗證中文字型、fallback、文字 shaping、表格跨頁、SVG 與逐頁輸出。
- 記錄 license、維護狀態、Java 25 相容性、模組大小與公開 API 隔離方式。
- 建立 page raster diff、文字擷取、頁數與 metadata 的測試 harness。
完成條件
- 至少一個 backend 能在 CI 環境重現三份 corpus 的基線。
- 選型結果記錄為 ADR-029 的 implementation note 或另立技術選型 ADR。
- 所有基線字型具明確授權,CI 與正式環境可重現。
REP-02 — ReportDocument 核心契約
工作內容
- 建立
ReportTemplate<T>、ReportDocument、ReportContext、style、measure 與 validation。 - 定義 section、paragraph、table、image、figure、group、aggregate、header/footer、page break 與 keep-together/page-break hint。
- 為大型重複資料定義 single-use row source 與 batch/close contract;文件結構 immutable,runtime data source 的 ownership 另行明定。
- 區分 hard constraint、soft hint 與 diagnostic;拒絕互相矛盾或無界的模型。
- 先提供 Java builder;序列化 schema 只做可行性評估,不承諾 v1 wire compatibility。
完成條件
- 公開 API architecture test 證明沒有 PDF/chart vendor 型別洩漏。
- builder 可建立三份 corpus 的完整文件樹,且有 snapshot/structural tests。
- 文件結構與 style immutable,重用 token 不會因 renderer 修改而產生共享狀態;row source 不能被無意重複消費或遺漏關閉。
REP-03 — PDF renderer 基線
工作內容
- 實作測量、換行、分頁與逐頁輸出 pipeline。
- 支援中文與 fallback font、字型嵌入、段落、表格跨頁、重複表頭、圖片、頁首頁尾與頁碼。
- 實作 group、aggregate 與基本 keep-together 行為。
- 產生 PDF metadata、頁數與 diagnostics;確保資源在成功、失敗與取消時都會關閉。
完成條件
- 三份 corpus 的頁數、文字內容與 visual golden 都通過。
- 大型明細基準不以「完整文件節點+完整 PDF bytes 各保留一份」為必要條件;峰值記憶體有 benchmark 與 regression gate。
- 不支援或被降級的 layout request 會產生 diagnostic,而不是靜默忽略。
REP-04 — Figure 與統計圖表
工作內容
- 建立 Figure 的尺寸、替代文字、overflow 與 flow-layout 契約。
- 在 Figure 內提供局部座標系;禁止一般 report node 指定絕對頁面座標。
- 定義受限 SVG 輸入與 raster fallback,禁止 script、外部 URL、file URL 與未受控 resolver。
- 提供至少 bar 或 line chart 的
ChartSpec -> Figureadapter,涵蓋座標軸、圖例、標籤與 無資料狀態。
完成條件
- 同一 Figure 可放在段落間、table cell 與跨頁前後,且不破壞 flow pagination。
- 圖表的文字、顏色、單位與 accessibility alt text 可由應用控制。
- 惡意 SVG、過大 viewBox、極端 path 數量與外部資源都有拒絕或限制測試。
REP-05 — ReportArtifact 與 server delivery
工作內容
- 定義
ReportArtifact的 MIME type、filename、size/page count、metadata 與 stream/resource ownership。 - 提供同步產生能力;為 application-owned 非同步 job、進度、取消、重試與 retention 定義 seam。
- 與
FileStorage/FileResponseBuilder整合,但不在框架建立 report job Entity 或固定 API route。 - 定義原始 PDF 與 subset PDF 的 authorization、ETag 與稽核責任。
完成條件
- 應用可選擇直接串流短報表,或把大型報表保存後再提供下載。
- renderer failure 不留下未關閉 stream 或誤認為成功的 artifact。
- tenant/tenantless 參考實作都能由 application adapter 接線,框架無 tenant 假設。
REP-06 — 真正的 ranged read
工作內容
- 在 storage capability 增加 offset/length 或等價的 ranged-read 契約。
- Local、Database、S3/MinIO、Azure Blob 與 SFTP 分別實作或明確宣告降級策略。
FileResponseBuilder正確處理Range、If-Range、If-None-Match、單一合法 range、206與416;回應暴露Accept-Ranges、Content-Range、Content-Length與ETag。- 驗證前端跨來源設定可以讀取 PDF.js 所需 headers。
完成條件
- backend contract test 證明 ranged request 不必讀取完整物件或從 byte 0 線性略過至 offset。
- 合法、超界、格式錯誤與條件式 range 都有整合測試。
- 不支援 efficient range 的 backend 會被 capability/diagnostic 清楚識別。
REP-07 — 大型 PDF 按需預覽
工作內容
PdfViewer以受權 URL 和 PDF.js HTTP 設定載入,不先透過 axios 取得完整 Blob。- 支援 bearer header 或同源 cookie;處理 token 失效、取消、重試與文件替換。
- 只渲染 viewport 鄰近頁面;以 LRU/上限回收 canvas、page proxy、render task 與 object URL。
- 在不載入所有 page object 的前提下取得頁數與漸進式頁面尺寸。
- 顯示 loading、下載進度(可取得時)、錯誤與 fallback download action。
完成條件
- 第一頁可在完整 PDF 下載前顯示,network test 可觀察到
206與分段請求。 - 連續瀏覽大量頁面後,DOM/canvas/page cache 維持設定上限。
- zoom、跳頁、搜尋或快速捲動不留下未取消的 render task,且可安全卸載 viewer。
REP-08 — 選頁 subset 與瀏覽器列印
工作內容
- 前端支援全部、目前頁、連續範圍與明確頁碼集合;先正規化、排序、去重與驗證。
- server 從已授權的原始 artifact 擷取頁面,產生短效 subset PDF;定義 outline、link、form、 metadata 與頁碼語意的保留/捨棄規則。
- 用受控 iframe/新視窗載入可列印 PDF,收到 ready signal 後呼叫
print();避免在需要取得 window handle 時使用使回傳值為null的開窗設定。 - 明確顯示「系統選的是輸出文件頁面」;原報表頁碼與 PDF physical page 不同時要可辨識。
完成條件
- subset 的 physical page 順序與選擇完全一致,無權限或過期 artifact 無法擷取。
- Playwright 可用 print stub 驗證只觸發一次、觸發前文件已就緒;native dialog 本身不做自動化斷言。
- popup 被封鎖、載入失敗、使用者取消列印與 subset 過期都有可理解的回復路徑。
- 文檔清楚聲明 Web 無法預填原生列印對話框的 page range,也不提供 silent print。
REP-09 — hardening 與發布
測試矩陣
- IR validation、builder 與 renderer unit tests。
- corpus PDF 的 page/text/metadata assertions 與 raster golden diff。
- 字型缺失、超長不可斷文字、表格列高超頁、損壞圖片與 SVG 攻擊測試。
- storage backend range contract tests 與 server integration tests。
- Chromium、Firefox、WebKit 的預覽生命週期與列印觸發測試。
- 大頁數、大表格、高解析圖片與複雜圖表的時間、記憶體與輸出大小 benchmark。
發布條件
- 先發布 experimental API;在至少兩個實際 consuming reports 驗證後再承諾 stable API。
- 補齊 server 使用指南、web
PdfViewer指南、Javadoc/API docs、範例與 changelog。 - 所有 optional renderer/chart dependencies 都是隔離相依,未使用報表能力的應用不必載入。
- 記錄已知限制、相容性政策、字型部署方式與大型報表容量規劃方法。
明確不在本計畫內
- GUI/WYSIWYG report designer 與 power-user 自訂 expression。
- 以 HTML/CSS、JRXML、DOCX 或 raw PDF drawing commands 作為 canonical authoring language。
- 固定格式 PDF 套版與 AcroForm 填寫的 authoring API。
- 瀏覽器 silent print、預設印表機選擇或強制原生 page range。
- 應用專屬 report job Entity、排程政策、保存期限與權限規則。
- 在需求尚未出現前承諾完整 JasperReports 等價功能。
後續產品規格如何接軌
本計畫是框架工程清單,不是終端使用者 Epic。採用端要交付「財務報表」「庫存統計」等功能時, 仍應各自建立或補強產品 Epic/US/SBE,描述角色、欄位、篩選、排序、群組、圖表、權限、資料 截止時間與驗收例。產品 US 依賴一個已發布的 report capability 版本,而不是依賴本文件內部的 REP 工作包。
目前花店規格庫已有財務報表需求時,應在其既有 Epic/US 上補充業務例,不另建一個「開發 報表引擎」的假使用者故事。