移植 PDF 轉換功能

了解專案分層,將原型中的 PDF 轉換 API 和前端元件移植到 Saavo。

上一篇已讓 Saavo 專案在本機執行,並完成品牌設定。這篇會將通過驗證的原型功能移植進來。

移植原型功能

修改完文案和主題後,接下來要將原型開發時實作的功能,逐步移植到新專案。整個過程比較繁瑣,前面也提過,最好由人工負責,不過大部分工作仍能借助 AI 完成。

移植前,必須先了解目前框架的核心分層,才知道新增程式碼最適合放在哪裡。

分層結構

目前專案採用傳統分層架構:

  1. DB 層。

DB 層負責直接讀寫 Cloudflare D1 資料庫,目錄位於 src\core\db。檔案大致依業務分類,每個資料夾對應一個業務模組,程式碼很單純,就是直接讀寫 D1 資料表。

多數情況不需要手動修改。實作新的業務查詢時,可以請 AI 新增對應的介面和查詢函式。

  1. Repositories 層。

Repositories(儲存庫)層負責型別轉換和資料儲存操作,內部主要呼叫 DB 層完成工作,目錄位於 src\core\repositories。程式碼結構與 DB 層大致一一對應,是 D1 資料庫介面的簡單封裝,核心責任是型別轉換。

  1. Service 層。

Service 層負責實作業務邏輯,目錄位於 src\core\services。這一層也依業務分類,在程式中對外提供業務服務介面,大部分核心業務邏輯都放在這裡。

對外介面通常位於對應目錄的 service.ts。例如 src\core\services\pdf\service.ts,就負責 PDF 轉換業務的介面實作。

  1. API 層。

API 層對外提供 HTTP API,依請求回傳對應的 JSON 資料。本身不包含太多複雜邏輯,主要負責請求身分驗證、權限檢查、參數驗證、呼叫業務邏輯、整理回傳資料,以及產生最終 HTTP 回應。

  1. Pages 層。

Pages 層負責頁面呈現,目錄位於 src\pages。這一層的程式碼最直觀,可以簡單理解為一個檔案對應一個頁面。

同樣依業務分類,負責在程式中對外提供頁面呈現服務,並依不同業務需求呼叫不同元件。

對外頁面通常位於對應目錄的 page.tsx。例如 src\pages\pdf\page.tsx,就負責 PDF 轉換業務的頁面呈現,內部會呼叫 Components 層的元件。

這一層也直接對外提供 HTTP 服務,與 API 層的主要差別是回應類型。API 層主要回傳 json 資料,Pages 層主要回傳 HTML 頁面。

  1. Components 層。

Components 層主要存放頁面元件,目錄位於 src/components。通常依頁面或個別功能分類,方便不同頁面按需求組合與重複使用。

這些元件一般由 Pages 層引入,透過 SSR 或 CSR 呈現給使用者。了解框架分層後,就能開始規劃如何逐步將原型功能移植到正式專案。

分層結構

頁面部分相對簡單。可以先將原型的 React 元件移植到 Components 層,再由首頁引入這些元件完成頁面呈現,直接請 Codex 處理即可。

真正需要仔細處理的是 PDF 轉換服務。我們要在 API 層定義對應的路由端點,支援三種 PDF 轉換方式。使用者在前端按下轉換按鈕後,前端直接呼叫這些 API,再依後端回傳資料更新頁面狀態。

API 層主要負責請求身分驗證、權限檢查、參數驗證和回應處理。實際 PDF 轉換邏輯則放在 Service 層,由 Service 層透過 Repositories 層存取 DB 層介面,完成業務邏輯。

移植後端 API

可以簡單將移植工作分為前端和後端。先保留前端,完成後端 API 的移植,也比較方便測試。請 AI 分析原型專案有哪些後端 API:

類型API功能回傳方式
快速/自訂轉換POST /api/convert產生截圖或原始 PDF串流 NDJSON
視覺化編輯POST /api/visual/sessions建立編輯工作階段串流 NDJSON
視覺化編輯POST /api/visual/sessions/:sessionId/actions執行編輯操作JSON
視覺化編輯POST /api/visual/sessions/:sessionId/generate產生原始 PDFJSON
視覺化編輯DELETE /api/visual/sessions/:sessionId關閉工作階段並結算額度JSON

可以請 AI 逐步移植這些 API。每次完成後,都要檢查責任邊界,避免邏輯混亂或功能遺漏。

我的策略是先移植 POST /api/convert,但不要直接請 AI 一次完成,而是分步執行。一次移植太多程式碼,容易產生問題,也不利於人工審查。

以下是我採用的步驟:

  1. 移植外部相依功能。我另外建立了一個獨立 Worker,負責 PDF 核心產生工作,因此先移植這部分。它本身獨立,只涉及 RPC 呼叫,沒有太多複雜邏輯。
  2. 依舊版專案的程式碼邏輯,建立 PDF 服務,包括介面定義、業務邏輯和單元測試。
  3. 建立對外 API,內部呼叫 PDF 服務完成業務邏輯。

請 AI 分步移植後,我逐一檢查了每個步驟。坦白說,產生的程式碼不算理想,但確實能用,不符合要求的部分,我又請它重做。此外,也可以請它撰寫測試用戶端,以實際情境檢查 API 是否有問題。

PDF 轉換 API 會在伺服器端開啟使用者提交的網址,因此不能只在前端檢查輸入。後端至少要限制為公開的 HTTP/HTTPS 網址,並在每次重新導向後重新檢查目標位址,拒絕存取 localhost、內部網路位址和連結本機位址。同時也要限制頁面載入時間、重新導向次數、產生檔案的大小,以及同時執行的工作數量,避免單一請求長時間占用資源。

產生的 PDF 也不能使用任何人都能猜到的固定網址。下載 API 必須檢查工作歸屬,暫存檔案則需設定清理時間。這些限制都屬於轉換服務本身,應在部署前於本機完成驗證。

移植前端元件

前端元件的移植相對簡單,因為都是 React 元件,可以直接移植到 Components 層。過程中可請 AI 自行處理,大部分不會有太大問題,主要注意樣式合併,以及是否移除不再使用的舊元件。

另外,原型中的字型依賴第三方服務,我請 AI 直接移到本機。這些不是網站 UI 使用的字型,而是產生 PDF 時可能用到的字型,畢竟不是所有 PDF 都只有英文。

也需要請 AI 處理行動裝置版面。前期原型完全沒有處理這部分,在行動裝置上的 UI 很不理想,仍需調整。

剩下的是恢復功能、調整效能和樣式。這部分相對單純,主要是仔細測試,發現問題後再請 AI 解決。

以下是移植後的畫面。這是需要耐心的工作,得慢慢調整、修正錯誤,不要期待目前的 AI 能一次解決所有問題,至少現在還做不到:

前端元件移植

這篇沒有深入說明太多實作細節,主要有兩個原因。

第一,原型開發和後續移植大多透過 AI 完成,過程經過多輪調整,很難完整記錄每個步驟。

第二,本系列的重點一直是如何使用 Saavo 範本快速開發產品。每個人想做的產品不同,業務功能的實作方式也有很大差異,很難整理出適用所有專案的開發流程,因此沒有必要在這裡展開。

當然,我也可以記錄 Webpage to PDF 從原型到實作的所有細節,但這會逐漸偏離本系列原本的方向。

相較之下,接下來的內容在 Saavo 範本中更通用,也更值得詳細介紹。

本篇檢查

完成本篇後,應能在本機使用三種轉換模式,並檢查轉換 API、下載流程和行動裝置頁面。

教學總覽 · 上一篇:建立專案與品牌設定 · 下一篇:設計方案與串接使用權益