報表產生開發計畫
狀態: 實作中(REP-P0 Web Prototype slice 已完成;REP-01 完成,已確認採用 PDFBox+AppFuse 自有排版層;REP-02 已實作,模型以實驗性 API 提供;REP-03 PDF renderer 已實作並通過程式碼審閱;REP-04 Figure 與圖表已實作並通過程式碼審閱;REP-05 ReportArtifact 與交付已實作並通過程式碼審閱;REP-06 ranged read 已實作並通過程式碼審閱;REP-07 大型 PDF 按需預覽已實作並通過程式碼審閱;REP-08 選頁 subset 與瀏覽器列印已實作並通過程式碼審閱;REP-09 進行中:實驗性 API 已發布於 appfuse-server 4.0.0-alpha.21,尚待實際 consuming reports、跨瀏覽器與 benchmark 驗證) 依據: ADR-029: Headless 報表引擎與中介文件模型
更新日期: 2026-10-01
目標與成功條件
建立 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
結果:見 REP-01 報表 renderer spike。已確認採用 PDFBox 3.0.x 加上 AppFuse 自有排版層;三份 corpus 已在 macOS 與 Linux headless(arm64、amd64)重現,輸出除
/ID外位元組一致。
工作內容
- 把三類代表性報表轉成固定測試資料與預期 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 核心契約
設計:見 REP-02 ReportDocument 核心契約設計,2026-09-29 通過設計審閱。 實作:2026-09-29 通過程式碼審閱(2 輪);
io.leandev.appfuse.report、report.node以@ExperimentalApi提供, 完成條件的測試已實作(架構、建構與讀取兩階段驗證、列來源生命週期、樣式合併、三份 corpus、64MB heap 記憶體測試)。
工作內容
- 建立
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 基線
設計:見 REP-03 PDF renderer 基線契約設計,2026-09-29 通過設計審閱(3 輪); 狀態:已實作並通過程式碼審閱(3 輪,2026-09-29)。三份 corpus 與 golden、
-Xmx128m記憶體 gate、Linux arm64/amd64 跨平台驗證皆通過;REP-01 spike 已移除(FU-100)。
工作內容
- 實作測量、換行、分頁與逐頁輸出 pipeline。
- 支援中文與 fallback font、字型嵌入、段落、表格跨頁、重複表頭、圖片、頁首頁尾與頁碼。
- 實作 group、aggregate 與基本 keep-together 行為。
- 產生 PDF metadata、頁數與 diagnostics;確保資源在成功、失敗與取消時都會關閉。
完成條件
- 三份 corpus 的頁數、文字內容與 visual golden 都通過。
- 大型明細基準不以「完整文件節點+完整 PDF bytes 各保留一份」為必要條件;峰值記憶體有 benchmark 與 regression gate。
- 不支援或被降級的 layout request 會產生 diagnostic,而不是靜默忽略。
REP-04 — Figure 與統計圖表
設計:見 REP-04 Figure 與圖表契約設計,2026-09-29 通過設計審閱(3 輪); 狀態:已實作並通過程式碼審閱(3 輪,2026-09-30)。受限 SVG 子集與安全政策(FU-105)、點陣 Figure、長條圖與折線圖 adapter; corpus 統計報表改用實際圖表,macOS 與 Linux arm64/amd64 輸出除
/ID外位元組相同。 已決定自寫受限 SVG 子集(不採用 Batik+pdfbox-graphics2d),圖表 adapter 提供長條圖與折線圖。
工作內容
- 建立 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
設計:見 REP-05 ReportArtifact 與交付契約設計,2026-09-30 通過設計審閱(3 輪); 已實作並通過程式碼審閱(2 輪,2026-09-30);裁決與實作偏離見契約設計的「程式碼審閱裁決」與「實作註記」。 已決定:非同步 seam 只到值型別與保存 helper、tenant/tenantless 以框架測試與指南示範、renderer 提供頁數進度回呼。
工作內容
- 定義
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
設計:見 REP-06 Ranged read 契約設計,2026-09-30 通過設計審閱(3 輪); 已實作並通過程式碼審閱(2 輪,2026-09-30);裁決與實作偏離見契約設計的「程式碼審閱裁決」與「實作註記」。 已決定:
FileStorage以 default 方法宣告能力、不支援時忽略 Range 回完整內容、mock client+容器兩層 contract test、 CORS 由框架提供常數與文件(參考伺服器併入 FU-103)。
工作內容
- 在 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 按需預覽
設計:見 REP-07 大型 PDF 按需預覽設計,2026-09-30 通過設計審閱(3 輪); 已實作並通過程式碼審閱(2 輪,2026-09-30);裁決與實作註記見設計文檔的「程式碼審閱裁決」與「實作註記」。 已決定:經
HttpClient的 PDF.js range transport、搜尋漸進且可取消、順手修正列印、測試資料於 Playwright 動態產生。
工作內容
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 與瀏覽器列印
設計:見 REP-08 選頁 subset 與瀏覽器列印設計,2026-09-30 通過設計審閱(3 輪); 已實作並通過程式碼審閱(2 輪,2026-09-30);裁決與實作註記見設計文檔的「程式碼審閱裁決」與「實作註記」。 已決定:subset 每次請求即時產生並串流(不保存)、選頁以 GET query
pages=1-3,5傳遞、框架提供前端 adapter 工廠、 保守的保留規則(移除導覽結構、保留外觀,並以原頁碼設定頁碼標籤)。
工作內容
- 前端支援全部、目前頁、連續範圍與明確頁碼集合;先正規化、排序、去重與驗證。
- 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;固定版面另由 固定表單能力 承接,不納入 flow 模型。
- 瀏覽器 silent print、預設印表機選擇或強制原生 page range。
- 應用專屬 report job Entity、排程政策、保存期限與權限規則。
- 在需求尚未出現前承諾完整 JasperReports 等價功能。
後續產品規格如何接軌
本計畫是框架工程清單,不是終端使用者 Epic。採用端可先以 RPT 報表規格 描述欄位、資料來源、篩選、排序、群組、圖表、版面、資料截止時間與驗收例,獨立完成範本與合成資料驗證,不以 US 為前置條件。 產品操作入口、權限、job/下載/重印流程則由引用 RPT 的 US/SBE 承接;既有業務 Epic/US 可引用同一份 RPT,避免重複描述欄位與版面。 產品依賴已發布的框架版本,不依賴本文件的內部 REP 工作包;固定資料樣張不能充作實際 consuming report。
固定表單與 RPT 支援的發布順序(2026-10-01 審閱結論)
第 1~4 步已於 2026-10-01 完成,第 5 步待各下游執行(見下方「交付紀錄」)。為避免主線出現新版 ReportJobWorker 搭配舊框架 pin 的編譯空窗,按下列順序交付:
- 精確分組框架、參考實作與方法論變更;先從可建置基線建立隔離 release worktree,只帶框架 API、測試及必要文件,保留舊版參考實作。
- 確认乾淨工作樹及實際版號,依框架 publish 流程執行完整測試、不可變 alpha 發布、文件站部署及線上版本驗證。候選版號不等於已發布版號。
- 框架發布成功後,將參考實作新碼、兩個 server 的實際發布版號 pin 與 FEATURE_CHANGELOG Requires 放在同一 commit;以 registry 相依重驗,不使用 useLocalFramework,再提交。
- 切參考實作 CalVer;方法論另作可審閱提交並發布,同步下游 adapters。方法論同步不會自動更新應用程式碼。
- 下游升級框架、採用 report feature 更新,再依各自 RPT 實作資料 adapter,驗證真實資料、權限、實紙套印與代收掃讀。
交付紀錄(2026-10-01)
| 步驟 | 結果 |
|---|---|
| 1–2 框架發布 | appfuse-server 4.0.0-alpha.21 以隔離 release worktree 完整建置(1534 tests 通過)後發布至 releases repo;文件站 /actuator/info 與 /server/changelog 線上驗證通過 |
| 3 參考實作 | 兩個 server 的固定表單接線、4.0.0-alpha.21 pin 與 FEATURE_CHANGELOG Requires 同一 commit 落地;以 registry 相依重驗通過(2026-10-01 強制重跑:tenant 833、tenantless 555 tests;先前記載的 845/567 誤計入過時的測試結果檔) |
| 4 切版與方法論 | 參考實作切版 app-v2026.1.22;方法論 publish 至 8 個下游,fleet 對齊無在地漂移 |
| 5 下游採用 | 未開始:各下游自行 /upgrade-appfuse-server 4.0.0-alpha.21 並依 RPT 實作 adapter |
審閱通過不取代 REP-09 的實際 consuming reports 條件;RPT-001 仍為獨立範本試行,歷史本機接受不當作目前帳本證據。