Boloop 閱讀示意圖(AI 生成):合法長小數不應被任意精度限制Reject。

2026 年 10 月 21 日,Boloop 的 GA4 概覽可以顯示,詳細報表卻被標成不可用。最直觀的猜測是 Google 請求失敗,但只讀檢查給出了另一組事實:同一批七個報表查询,全都返回 HTTP 200。

問題發生在響應回来了之後。本站的小數解析器給小數部分設了最多九位的限制,剛好Reject了 Google 返回的合法指標值。對外展示的「服務商不可用」,實際包住了一個本站解析錯誤。

不可用是結果分類还不是根因

後台需要在統計就读失敗時保持可用,所以外部來源查询有降級處理:無法得到可信數據時,顯示 unavailable,而不是偽造一組零。

這個行為本身有必要。麻煩在於,網絡失敗、響應結構異常和本地解析失敗,最終都可能落入同一類顯示狀態。如果排查只停留在界面文案,就會把「這張卡片拿不到可用數據」誤讀為「Google 沒有正常響應」。

當時先在正式後台復現明細不可用,再單獨檢查對應查询。概覽與明細也不是同一次結果,概覽正常不能替明細證明成功,明細失敗也不應把已有概覽一併改成零。

兩個真實值找到了校驗邊界

返回的參與率和平均會話時長分別是:

engagementRate: 0.2857142857142857
averageSessionDuration: 39.193496142857143

它們的小數部分都超過九位。舊校驗在數值轉換前就Reject了字符串,拋出 Unexpected GA4 engagement rate,隨後外層將整份明細降級為不可用。

Google Data API 用字符串承載指標的 value,具體類型由指標頭說明。收到字符串後做校驗是合理的,但小數位數限制需要有業務或接口依據,不能把界面準備顯示幾位小數直接當作輸入格式約束。Google MetricValue 文件

參與率在界面顯示為 28.6%,也不意味著上游只能返回一位、兩位或九位小數。顯示精度應放在展示環節處理。

只移除錯誤限制保留真正的邊界

修復沒有改成「任何值都先轉數字」。例如 JavaScript 的 Number('') 和 Number(' ') 都會得到零,直接轉換會把缺失或髒數據變成看似有效的指標。

這次只去掉小數部分最多九位的限制。輸入仍必須符合本站接受的非負十進制形式,轉換後必須是有限數,並遵守數值上限;整數指標繼續用自己的整數校驗。響應大小、超時、權限和匯總一致性檢查也保留。

共享解析器當時有兩個調用方,正好對應參與率與平均會話時長。後台和日報都通過共享報表方法就读明細,因此應修在這一層,而不是分別給兩處顯示補一個特殊處理。

把失敗值放進真實調用鏈的測試

原來的正常數據沒有碰到這個邊界。回歸時,將上面兩個原始失敗值放進已有的完整明細測試數據,讓自定義日期查询經過實際報表解析過程。

這個測試在舊解析器下失敗,修復後通過。它不僅斷言正則能匹配,還斷言解析後的參與率與平均時長等於預期數值,避免只證明了一個脫離調用方的小函數。

還補了相反方向的檢查:空串、純空格、負數、NaN、Infinity、帶尾隨文字的數值,以及超過本站上限的值,都仍應讓來源返回不可用。修復一個過嚴條件,不意味著其餘校驗都需要放鬆。

當次統計回歸共 114 項通過,覆蓋報表、日報、權限、日期範圍及匯總不一致等路徑。

上線後分別確認界面和下一封郵件

部署後,正式後台刷新出了參與率、平均時長、趨勢和各類明細。一次成功查询的每日會話和浏覽量之和,與匯總的 7 次會話、15 次浏覽一致,說明結果還通過了已有的一致性檢查。

檢查期間也曾出現另一次不可用。因此結論只應落在已證實的解析缺陷:這個合法長小數不再被本站错误Reject,不能據此承諾所有上游请求永遠成功。這次沒有增加盲目重試,也沒有通過延長超時掩蓋解析問題。

郵件與後台共用明細邏輯,但网页恢復並等於已經收到新郵件。部署當天保留下一次正常投遞作為郵件驗收點,沒有為驗證文案或數值重複發送日報。10 月 22 日的正常日報中,明細與新時間文案才取得實際收信證據。

以後再遇到「请求成功,頁面卻不可用」,我會按三個邊界追查:HTTP 是否成功,響應是否符合接口結構,應用是否正確接受了合法值。先找出第一處Reject,才知道該重試網絡、修正數據,還是刪除一條沒有依據的限制。