跳至主要内容

報表產生開發計畫

狀態: 實作中(REP-P0 Web Prototype slice 已完成) 依據: ADR-029: Headless 報表引擎與中介文件模型
更新日期: 2026-08-06

目標與成功條件

建立 developer/AI-first 的 headless 報表管線,使應用可以用 Java 建立 ReportDocument、在 server 直接產生 PDF,並讓 appfuse-web 對大型 PDF 做 range-based 預覽、選頁與瀏覽器列印。

第一個可用版本完成時,必須能用同一組公開契約產生至少三類代表性報表:

  1. 多頁明細表:群組、彙總、重複表頭與頁碼。
  2. 含中文長文、圖片與跨頁區塊的版面報表。
  3. 含長條圖或折線圖的統計報表。

固定座標證書、申請書及政府 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-P0Web Prototype contract sliceADR-029ReportViewer、lifecycle state、page selection、mockable adapter
REP-01代表性報表 corpus 與 renderer spikeADR-029測試資料、版面基準、backend 選型證據
REP-02ReportDocument 核心契約REP-01immutable IR、builder、style/measure、validation
REP-03PDF renderer 基線REP-02文字、表格、圖片、分頁、頁首頁尾、diagnostics
REP-04Figure 與統計圖表REP-02、REP-03Figure、SVG policy、至少一個 chart adapter
REP-05ReportArtifact 與 server deliveryREP-03產物契約、同步/非同步 seam、授權下載整合
REP-06真正的 ranged readREP-05FileStorage range SPI、backend contract tests、CORS headers
REP-07大型 PDF 按需預覽REP-06URL-based PDF.js 載入、page LRU、取消與錯誤處理
REP-08選頁 subset 與瀏覽器列印REP-05、REP-07page selection、subset service、print lifecycle
REP-09hardening 與發布REP-03~08conformance、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>ReportDocumentReportContext、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 -> Figure adapter,涵蓋座標軸、圖例、標籤與 無資料狀態。

完成條件

  • 同一 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 正確處理 RangeIf-RangeIf-None-Match、單一合法 range、206416;回應暴露 Accept-RangesContent-RangeContent-LengthETag
  • 驗證前端跨來源設定可以讀取 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 上補充業務例,不另建一個「開發 報表引擎」的假使用者故事。