專案

一般

配置概況

Bug #1383

是由 陳國瑋11 天 前更新

## 現象 
 2026-07-23 下午 4 點左右(16:02:05、16:02:06、16:02:08、16:02:13、16:02:16、16:02:42、16:13:46、16:16:36-37、16:17:27),production /opt/hm/api/logs/api.log 出現多次同一模式的例外,皆由使用者點擊 richmenu「Menu-3」(postback tid=richmenu:50263_3, block=b304cae8-06e1-4f09-8daf-3439ff1fd1e9)觸發。 

 ## 根因(已修正:問題不在 SS3A Framework,而在 OmniChat 轉發格式) 根因(Source-backed,已比對 ss3a-line-6.9.5-SNAPSHOT-sources.jar 原始碼) 
 SS3A Framework 的 `CheckUserStatusEventHandler.canHandle()` 原始設計是正確的:它預期收到的 該 richmenu 按鈕送出的 postback data 是 LINE 官方原生的 `key=value` query string 格式,這本來就是 LINE Messaging API 對 postback data 的標準處理方式。 

 真正的問題在於資料流架構:書心診所的 LINE 官方帳號目前只掛了 **OmniChat(第三方客服/CRM 系統)** 的 webhook URL(LINE 一個官方帳號只能設定一個 webhook URL)。實際資料流是: 

 **JSON 格式**: 
 ``` 
 LINE Platform --(webhook)--> OmniChat --(forward)--> hm (/callback) {"action":"bot","block":"b304cae8-06e1-4f09-8daf-3439ff1fd1e9","title":"Menu-3","tid":"richmenu:50263_3"} 
 ``` 

 OmniChat 在此扮演 gateway 角色:先接收 LINE 平台原生事件,再轉發給 hm 處理。問題出在**這次轉發過程中,OmniChat 把 postback.data 的格式從原本的 
 但 SS3A Framework(com.sylksoft.line.bot)的 `CheckUserStatusEventHandler`(@Order(1),postback 攔截器鏈中第一個被檢查者)預期的是 `key=value` query string 轉換成了 JSON**: 格式。其 `canHandle()` 方法(CheckUserStatusEventHandler.java:39)沒有做 null check: 

 ```java 
 ``` Map<String, String> postBackData = convertToQueryStringToHashMap(pbe.postback().data()); 
 {"action":"bot","block":"b304cae8-06e1-4f09-8daf-3439ff1fd1e9","title":"Menu-3","tid":"richmenu:50263_3"} return null != postBackData && postBackData.get("pbe").equals("checkUserStatus"); 
 ``` 
 導致 hm 收到的資料不符合 `CheckUserStatusEventHandler.canHandle()`(CheckUserStatusEventHandler.java:39)預期的格式,`postBackData.get("pbe")` 

 因為解析出的 map 沒有 `"pbe"` 這個 key,`.get("pbe")` 回傳 null,呼叫 `.equals()` 觸發 直接丟出 NullPointerException: 
 ``` 
 java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "java.util.Map.get(Object)" is null 
	 at com.sylksoft.line.bot.handler.user.CheckUserStatusEventHandler.canHandle(CheckUserStatusEventHandler.java:36) 
	 at com.sylksoft.line.bot.LineEventDispatcher.handlePostBackEvent(LineEventDispatcher.java:217) 
 ``` 

 此 NPE 被 LineEventDispatcher 的 catch-all 接住,改發送制式訊息「LINE發生錯誤,請連絡客服:service@sylksoft.com,謝謝」,但這則回覆本身也因為 reply token 已失效而送不出去(詳見 #1384),導致使用者點擊按鈕後完全沒有收到任何回應。 已失效而送不出去(詳見另一張票:Invalid reply token 系統性問題),導致使用者點擊按鈕後完全沒有收到任何回應。 

 **結論:** ## 影響範圍 
 所有點擊「Menu-3」這個 richmenu 按鈕的使用者,皆會遇到無回應的狀況。 

 ## 備註 
 - ❌ 不是 根因程式碼位於 SS3A Framework Framework(com.sylksoft.line.bot),非 hm 專案自己的程式碼,需請框架維護方協助修正 canHandle()bug——canHandle() 對「LINE 平台原生 postback data」的處理方式是對的,不應更動。 null check。 
 - ✅ 真正原因是 OmniChat 轉發 另一個可行的暫時解法:重新設計「Menu-3」richmenu 按鈕的 postback event 時,把格式從 data,改用 canHandle() 預期的 query string 改成了 JSON。 

 ## 待釐清 / 下一步 
 1. 需與 OmniChat 確認:轉發 postback event 時是否有設定可保留原始 query string 格式,或此為 OmniChat 固定行為、無法調整。 
 2. 若 OmniChat 無法調整格式,hm 端需要自行相容兩種格式(query string 與 JSON),在進入各 handler 的 canHandle() 之前先正規化 postback data——這是 hm/OmniChat 整合層的責任,不應要求 SS3A framework 改動其對原生事件的假設。 
 3. 需確認除了「Menu-3」外,是否還有其他 richmenu 按鈕的 postback 也會經過 OmniChat 轉發而受到相同影響。 

 ## 影響範圍 
 所有點擊「Menu-3」這個 richmenu 按鈕的使用者,皆會遇到無回應的狀況。 格式(例如 `pbe=checkUserStatus&...`)。

返回