「緯書」八字命盤演算功能修復技術報告

## 1. 問題背景

在將「緯書」博客的八字命盤工具（WASM 版）重構爲統一的“帛書”視覺風格並部署至 Cloudflare Workers 後，用戶反饋前端頁面點擊“推算命盤”後出現報錯：`演算失敗: 無法加載 WASM 文件`。同時，控制檯打印出魔數匹配錯誤：`expected magic word 00 61 73 m, found cf ff ff 7f`。

該問題導致核心的八字排盤功能完全失效。本報告詳細記錄了該問題的排查過程、根本原因分析以及最終的修復方案。

---

## 2. 故障診斷與原因分析

經過對前端加載邏輯、網絡請求抓包以及 WebAssembly 實例化過程的深入排查，發現該故障並非單一原因引起，而是由三個獨立的技術問題疊加導致的。

### 2.1 壓縮格式衝突與 CDN 攔截（核心問題）

**現象：**
前端嘗試通過 `fetch` 獲取 `ganzhi.wasm.gz`（18MB）文件，並使用 `DecompressionStream('gzip')` 進行解壓。然而，解壓後傳遞給 `WebAssembly.instantiate` 的字節流前 8 字節爲 `cf ff ff 7f`，而非標準的 WASM 魔數 `00 61 73 6d`。

**分析：**
`cf ff ff 7f` 是 Brotli 壓縮算法的文件頭標識。進一步抓包發現，儘管前端請求的是 `.gz` 文件，但由於瀏覽器在請求時自動攜帶了 `Accept-Encoding: gzip, deflate, br` 請求頭，Cloudflare CDN 邊緣節點檢測到客戶端支持 Brotli，便**主動將原始的 Gzip 文件再次使用 Brotli 進行了壓縮**，並返回了 `Content-Encoding: br`。

這就導致前端 `DecompressionStream('gzip')` 接收到的是 Brotli 格式的數據，解壓失敗，進而導致 WebAssembly 引擎拋出魔數不匹配的致命錯誤。

### 2.2 JSON 數據解析路徑異常

**現象：**
在解決 WASM 加載問題後，前端控制檯顯示計算已完成，但頁面上的四柱、五行、大運等 UI 元素依然顯示爲佔位符“—”。

**分析：**
原始的 JS 膠水代碼在處理 WASM 返回的 JSON 字符串時，直接訪問了 `data.pillars` 等頂層屬性。然而，最新編譯的 WASM 模塊返回的數據結構被包裹在了一個 `result` 對象中，實際結構爲 `{"result": {"pillars": {...}}, "success": true}`。由於路徑錯誤，前端渲染函數接收到了 `undefined`，導致 UI 無法更新。

### 2.3 字段格式不兼容

**現象：**
四柱的干支數據未能正確填入對應的 DOM 節點。

**分析：**
舊版渲染邏輯期望 WASM 返回分離的天干和地支字段（如 `yearStem: "庚", yearBranch: "午"`）。但實際返回的數據是將干支合併的單字符串（如 `year: "庚午"`）。這導致前端解構賦值失敗。

---

## 3. 修復方案與實施

針對上述三個問題，我們採取了“服務端解壓 + 前端適配”的綜合修復策略。

### 3.1 邊緣計算層（Cloudflare Workers）重構

爲了徹底避開瀏覽器與 CDN 之間的自動壓縮內容協商（Content Negotiation）衝突，我們放棄了在前端使用 `DecompressionStream` 解壓的方案，轉而利用 Cloudflare Workers 的邊緣計算能力。

**具體實現：**
我們編寫了一個攔截腳本 `worker.js`，當檢測到對 `/dist/ganzhi.wasm` 的請求時，Worker 會在服務端讀取靜態資產中的 `ganzhi.wasm.gz` 文件，**在邊緣節點完成 Gzip 解壓**，並強制設置 `Content-Type: application/wasm`，然後直接將純淨的原始 WASM 字節流返回給前端。

這一方案不僅解決了雙重壓縮的衝突，還利用了邊緣節點的算力，減輕了客戶端瀏覽器的解壓負擔。

### 3.2 前端渲染邏輯修復

針對 JSON 結構和字段格式的變化，我們對 `index.html` 中的 `renderResult` 函數進行了重寫。

| 修復項 | 舊邏輯 | 新邏輯 |
| :--- | :--- | :--- |
| **數據層級** | `const p = data.pillars;` | `const data = raw.result ? raw.result : raw; const p = data.pillars;` |
| **四柱拆分** | `[p.yearStem, p.yearBranch]` | `const sb = p.year; $(sId).textContent = sb[0]; $(bId).textContent = sb.slice(1);` |
| **五行渲染** | 依賴特定中文字符鍵值 | 增加中英文字典映射，支持動態生成帶有相應 CSS 顏色類的 HTML 標籤 |

### 3.3 視覺風格統一

在修復功能的同時，我們將八字命盤的 UI 樣式與「緯書」博客主站進行了深度統一。包括引入白紙底色（`#FAF7F2`）、硃砂紅強調色（`#C0392B`）以及墨跡黑文字（`#1A1A1A`），並在頂部添加了統一的返回導航欄，確保了整體品牌視覺的連貫性。

---

## 4. 總結

本次修復成功解決了 Cloudflare CDN 自動壓縮機制與 WebAssembly 加載之間的衝突問題。通過將解壓邏輯前置到 Workers 邊緣節點，並重構前端數據解析路徑，八字命盤工具現已恢復穩定運行。

該案例也爲後續在 Serverless 架構中部署大型 WASM 文件提供了寶貴的經驗：**在處理非標準二進制資產時，應警惕 CDN 默認的透明壓縮行爲，必要時需通過自定義 Worker 腳本進行顯式幹預。**