動作
Bug #1392
進行中[Backend/Frontend Bug] richmenu「填寫個人基本資料/健康日誌」打不開——LIFF 取不到 userId 導致 /check/completed/profile/undefined 回 500
狀態:
New
優先權:
High
被分派者:
-
開始日期:
2026-07-27
完成日期:
預估工時:
概述
現象¶
客戶回報部分會員點擊 richmenu「填寫個人基本資料/健康日誌」後,畫面卡在「確認中,請稍候…」,既沒有進入「填寫基本資料」表單、也沒有進入「會員中心」,等於整顆按鈕打不開、無法填寫。
happy path 正常(2026-07-27 當天 654 筆成功 vs 21 筆失敗,約 3% 失敗),因此是「部分會員」踩到,符合客戶描述。
資料流¶
richmenu 目前由 OmniChat 管理(選單名稱皆帶 _260716)。此按鈕為 URI action,實際連結為 OmniChat redirect 再進 LIFF:
api.omnichat.ai/restapi/v1/redirect/line_richmenu?url=…liff.line.me/2006305147-4ZbabE3q…&id=49988_3
2006305147-4ZbabE3q = 前端 NEXT_PUBLIC_LIFF_CHECK_PROFILE,對應 web 的 /check-profile 頁。
根因(前後端皆缺防呆)¶
- 前端
react/web/src/utils/LineHelper.js的getProfile(),在「LIFF 未登入」或liff.getProfile()失敗時,會回傳空物件{}(userId = undefined)而不報錯:- 未登入分支只呼叫
liff.login()就return this.profile(此時為{}),呼叫端在重導前仍會繼續往下跑。 -
liff.getProfile()的.catch()只console.log就吞掉,profile 維持{}。
- 未登入分支只呼叫
-
react/web/src/app/check-profile/page.js未檢查就直接checkCompletedProfile(res?.userId),送出GET /api/user/check/completed/profile/undefined。 - 後端
UserServiceImpl.checkIfUserCompletedProfile()(UserServiceImpl.java:282)把字串"undefined"原封不動丟給messagingApiClient.getProfile("undefined")→ LINE 回 400The value for the 'userId' parameter is invalid→ 例外未捕捉 → 回前端 HTTP 500。 -
check-profile/page.js的.then()沒有.catch(),API 一旦 500 整個 promise reject,router.push不會執行 → 使用者永遠卡在載入畫面。
證據(Source-backed)¶
- production api.log 反覆出現:
Request: /api/user/check/completed/profile/undefined raised MessagingApiClientException: code=400 requestUrl=.../v2/bot/profile/undefined error='The value for the 'userId' parameter is invalid' - 直接打 production API 重現:
curl https://api.shuxinclinic.com/api/user/check/completed/profile/undefined→HTTP 500 {"errorType":"ExecutionException","message":"系統發生錯誤,請連絡系統管理員"} - 失敗數自 2026-07-23 起明顯暴增(每日 undefined 出現次數):7/22:4 → 7/23:80 → 7/24:96 → 7/25:60 → 7/26:20 → 7/27:36。程式碼與 api.jar 皆未變動(前端 container 已跑約 10 個月、api.jar 為 2026-01),故觸發點在外部——與 OmniChat 於 7 月接管並重發 richmenu(
_260716)時間吻合。
與 #1383 / #1384 的關係¶
三者共同上游皆為「OmniChat 於 7 月接管 richmenu(_260716)」,於 2026-07-23 同時爆發,但分屬不同流程:
- #1383 / #1384 走 postback → webhook
/callback路徑(OmniChat 把 data 轉 JSON → NPE;回錯誤訊息時 reply token 失效)。 - 本單走 URI → OmniChat redirect → LIFF → 前端 → REST API 路徑,不經過 webhook、也不用 reply token,與前兩單無程式碼交集,但同源。
推測 OmniChat redirect 多一層轉址,使部分使用者進入 LIFF 時登入 context 遺失(liff.isLoggedIn()為 false),放大了原本潛伏的前端防呆缺口。
建議修法¶
前端(治本、可自行 rebuild hm-web 止血):
-
LineHelper.getProfile():未登入時liff.login()後中止流程(勿回傳{});getProfile()失敗要向上拋錯或回null。 -
check-profile/page.js:res?.userId為空就不打 API(改為重新登入或顯示重試),並補.catch()處理 API 失敗,不要靜默卡住。
後端(多一層保護):
-
checkIfUserCompletedProfile():先擋掉id為空/"undefined",回 400(InvalidParameterException)而非 500;並 catchMessagingApiClientException避免直接噴 500。
維運面:
- 與 OmniChat 確認該 URI 按鈕能否直接指向
https://liff.line.me/2006305147-4ZbabE3q、拿掉中間 redirect 包裝,以降低失敗率。
影響範圍¶
所有點擊「填寫個人基本資料/健康日誌」按鈕、且當下 LIFF 登入 context 未建立的會員(約 3%,7/23 後升高),會卡在載入畫面無法填寫基本資料,等同無法進入自費療程前置流程。
沒有任何資料可供顯示
動作