Files
ewoooc/docs/refactor/openclaw_bot_routes_split_plan.md
ogt fcac03379d [V10.4-A] 加強 commit-quality Hook + P9 文件歸檔
新增 Edit/Write/MultiEdit 事件攔截(原僅攔截 git commit Bash 指令),
補齊 getenv fallback 模式偵測,防止 hardcoded Token 透過工具直寫入檔案。

- .claude/hooks/commit-quality.js: 改寫為 PreToolUse JSON 格式,覆蓋 Edit/Write/MultiEdit
- .claude/settings.json: 新增 Edit|Write|MultiEdit|Bash matcher 註冊
- .claude/hooks/__test__/commit-quality.test.sh: 4 case 自動化測試
- docs/guides/DISK_EXPANSION_GUIDE.md: 磁碟擴充 SOP 歸檔
- docs/p9_completion_report_*.md: P9-1 + P9-2 Sprint 完成報告
- docs/refactor/callback_prefix_proposal.md: 308 按鈕回呼前綴分析(Method C)
- docs/refactor/openclaw_bot_routes_split_plan.md: 5999 行神檔拆分計畫

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 01:42:40 +08:00

23 KiB
Raw Permalink Blame History

routes/openclaw_bot_routes.py 拆分地圖

產出時間2026-04-24 現況5999 行單檔God File僅被 app.py:683 一處 import 目的:為 H1 重構提供設計藍圖,本文件不改任何 .py 交付對象refactor-specialist / critic / planner


0. 全景快照

指標 數字
總行數 5999
外部 importer 1app.py:683 from routes.openclaw_bot_routes import openclaw_bot_bp
Blueprint routes 4/bot/telegram/webhook/bot/internal/cmd/bot/telegram/set_webhook/bot/telegram/webhook_info
頂層函數 ~90 個
模組級全域 (mutable) 5_GOALS_input_pending_excel_pending_seen_update_ids/_order_rate_tracker_scheduler_sched_lock_fh_MPL_FONT_SETUP_DONE
第三方 import requests, flask, sqlalchemy, matplotlib延遲, openpyxl延遲, apscheduler延遲, pandas延遲
跨專案服務 services.mcp_context_serviceservices.openclaw_learning_serviceservices.pchome_crawlerservices.ppt_generatordatabase.managerservices.logger_manager

1. 全檔分區(按行號順序)

# 行號 區塊 主要符號 對外依賴 內部共享狀態
A 1-72 imports、Blueprint、env 常數 GEMINI_*BOT_TOKENALLOWED_GROUPNVIDIA_*openclaw_bot_bp 3rd-party + 4 個 services
B 74-138 安全/限流/去重基礎設施 _is_authorized_check_rate_limit_seen_update_ids_seen_update_order_rate_trackerTRIGGER_KEYWORDSALLOWED_USERS os、time 持有 rate + seen 狀態
C 140-251 Telegram API wrapper _tg_strip_markdownsend_messageanswer_callbacksend_typingsend_photosend_document requests 無(純函數)
D 253-262 Mutable 全域 dict 宣告 _GOALS_scheduler_input_pending_excel_pending 跨整個檔案共用(核心耦合點)
E 264-295 中文字型搜尋 _CHINESE_FONT_PATHS_FONT_DL_URL_get_chinese_font os、requests
F 299-611 Excel 日報產生器(非 PDF名稱誤導 generate_daily_pdf openpyxl延遲、tempfile 依賴 query_*TAIPEI_TZ
G 613-1127 DB Query 層 #1複雜型 query_category_salesquery_category_monthlyquery_comparisonquery_daily_historyquery_restock_forecastquery_category_detailquery_promo_comparisonquery_anomaliesquery_growth_dataquery_vendor_bcg_dataget_goal_status DatabaseManager、SQLAlchemy text _GOALSget_goal_status
H 1129-1381 Matplotlib 圖表 _MPL_FONT_SETUP_DONE_setup_mpl_chinesegen_trend_chartgen_products_chart matplotlib延遲_get_chinese_font _MPL_FONT_SETUP_DONE
I 1384-1479 策略分析 analyze_product_strategy_analyze_strategy_range query_top_products、MCPRouter/query_mcp 讀 DB + MCP
J 1481-1960 訊息格式化 #1 fmt_categoryfmt_comparisonfmt_goal_status_short_idfmt_restock_forecastfmt_category_detailfmt_promo_comparisontrack_competitor_price_changesfmt_monthlyfmt_strategy 純字串處理
K 1962-2723 AI 簡報分析 + PPT 指令分派 _clean_ai_text_ppt_ai_analysis_generate_ppt_cmd requestsNIMservices.ppt_generator(延遲) 呼叫大量 query_*_ppt_ai_analysis_generate_ppt_cmd 內部用 10 次
L 2724-2910 Excel 匯入 _EXCEL_REQUIRED_COLS_EXCEL_OPTIONAL_COLS_validate_excel_format_fmt_excel_validation_report_download_telegram_file_handle_excel_import pandas延遲 _excel_pending
M 2912-3321 排程任務6 個 cron job send_morning_reportsend_evening_reportsend_weekly_reportcheck_anomaliessend_competitor_reportsend_daily_excel_sched_lock_fh query_*generate_daily_pdfgen_trend_chartsend_message/photo/documentpchome_* 產生告警後去重寫 DB
N 3323-3388 Scheduler 啟動/指令註冊 start_schedulerregister_commands apscheduler延遲、fcntl _scheduler
O 3392-3649 Inline Keyboard / Submenu / Await Prompt _BACKmain_menu_keyboard_submenu_*10 個)、_SUBMENUS_AWAIT_PROMPTSsales_quick_kb latest_date_GOALS
P 3651-3895 DB Query 層 #2基礎型 _dbnormalize_datelatest_datequery_salesquery_top_productsquery_top_vendorsquery_weekly_trendquery_trend_rangequery_monthly_summaryquery_date_rangequery_available_monthsquery_top_products_range DatabaseManager、SQLAlchemy text
Q 3897-3979 日期/意圖解析 resolve_dateresolve_query_intent
R 3981-4325 訊息格式化 #2 MEDALSfmt_sales_esc_pchome_linkPCHOME_URLfmt_productsfmt_vendorsfmt_trendfmt_trend_summarygen_aggregated_chart matplotlib延遲
S 4327-4345 Help 關鍵字判斷 _HELP_KEYWORDS_is_help_question
T 4347-4510 Gemini Function Calling 工具定義 _FC_TOOLS_execute_tool query_*、MCP 家族、retrieve_knowledge
U 4512-4698 openclaw_answerAI 入口) openclaw_answer requestsGemini/NVIDIA_FC_TOOLS_execute_toolbuild_rag_contextstore_conversation
V 4700-5587 handle_cmd 單體28 個 cmd 分支) handle_cmd 幾乎所有上游函數 _GOALS、讀 _excel_pending
W 5589-5943 telegram_webhook354 行) route /bot/telegram/webhook _is_authorized_check_rate_limit_SUBMENUS_AWAIT_PROMPTS_input_pending_excel_pending_GOALShandle_cmdopenclaw_answer、Gemini Vision API _seen_update_*_input_pending_GOALS
X 5946-5970 internal_cmd 端點 route /bot/internal/cmd handle_cmdthreaded
Y 5973-5999 管理端點 + blueprint hook set_webhookwebhook_info_on_register _tgregister_commandsstart_scheduler

2. 內部呼叫熱度(依賴分析)

2.1 熱點函數(拆檔風險高,被 5+ 處呼叫)

函數 呼叫次數 所在區塊 拆檔策略
send_message 105 C 保留成 shared utility最早抽出services/openclaw_tg.py
handle_cmd 97含內部遞迴 V 最後才拆(中央調度器)
query_top_products 17 P 抽到 services/openclaw_queries.py
query_weekly_trend 11 P 同上
query_sales 11 P 同上
_ppt_ai_analysis 10 K _generate_ppt_cmd 一起抽(services/openclaw_ppt.py
query_monthly_summary 7 P 同上
answer_callback 3, send_photo 6, send_typing 5 C 與 send_message 一起抽
query_top_vendors 6, analyze_product_strategy 6 P/I 同上
get_goal_status 5 G _GOALS 全域,若 query 抽出需注入或 goal state 隨之抽

2.2 低耦合函數(抽離成本低)

  • Gemini Vision 圖片辨識 (webhook 5694-5752, 58 行) — 目前 inline 在 telegram_webhook,自成函數且完全獨立,最先可抽
  • gen_trend_chart / gen_products_chart / gen_aggregated_chart — 純 matplotlib依賴 _setup_mpl_chinese + DB query 結果(非直接)。
  • resolve_date / resolve_query_intent — 無副作用、僅字串處理。
  • _validate_excel_format / _fmt_excel_validation_report / _download_telegram_file — Excel 匯入子系統L 區),只被 _handle_excel_import 用。

2.3 Dead / 1-呼叫點函數(可就地內聯)

  • _strip_markdownC 區)— 僅 send_message 內部降級時使用。
  • 所有 6 個排程任務(send_*_reportcheck_anomaliessend_daily_excel)各自僅被 start_scheduler.add_job 呼叫 1 次。

2.4 全域狀態耦合圖

   ┌─────────────┐
   │   _GOALS    │──read──► get_goal_status (G), _submenu_goals (O), generate_daily_pdf (F)
   │  (dict)     │──write─► handle_cmd (V: goal), telegram_webhook (W: await.goal_*)
   └─────────────┘
   ┌──────────────────┐
   │ _input_pending   │──read/write──► telegram_webhook (W)
   │  (chat_id→state) │   only consumer; await flow 狀態機
   └──────────────────┘
   ┌──────────────────┐
   │ _excel_pending   │──write──► _handle_excel_import (L)
   │                  │──read───► handle_cmd (V: import_confirm/cancel)
   └──────────────────┘
   ┌──────────────────┐
   │ _seen_update_*   │──read/write──► telegram_webhook (W)
   │  (去重)          │
   └──────────────────┘
   ┌──────────────────┐
   │ _rate_tracker    │──read/write──► _check_rate_limit (B) via telegram_webhook (W)
   └──────────────────┘
   ┌──────────────────┐
   │ _MPL_FONT_SETUP  │ → _setup_mpl_chinese (H), module-local init flag
   └──────────────────┘
   ┌─────────────────────┐
   │ _scheduler/_lock_fh │ → start_scheduler (N) only
   └─────────────────────┘

關鍵發現_input_pending_seen_update_* 的讀寫皆集中在 telegram_webhookW 區),只要 webhook 區塊抽出,狀態即可隨之遷移_GOALS 則跨 4 個區塊讀寫,需要以 service 或 singleton 封裝。

2.5 依賴層級ASCII抽象層次由低到高

┌──────────────────────────────────────────────────────────────────────┐
│ Layer 0  常數 + envA 區)— 任何層皆可 import                        │
├──────────────────────────────────────────────────────────────────────┤
│ Layer 1  純 util:  Telegram API (C)、中文字型 (E)、safety (B 部分)    │
├──────────────────────────────────────────────────────────────────────┤
│ Layer 2  Query (G, P) + Intent (Q) + 全域 state (D)                   │
├──────────────────────────────────────────────────────────────────────┤
│ Layer 3  Chart (H) + Strategy (I) + Format (J, R) — 消費 Layer 2      │
├──────────────────────────────────────────────────────────────────────┤
│ Layer 4  Excel 匯入 (L) + Excel 匯出 (F)  — 消費 Layer 1/2            │
├──────────────────────────────────────────────────────────────────────┤
│ Layer 5  AI: FC 工具 (T, U) + PPT 分析/分派 (K)                       │
├──────────────────────────────────────────────────────────────────────┤
│ Layer 6  Keyboard/Menu (O) + Schedule Job (M) — 消費 L4 + L1          │
├──────────────────────────────────────────────────────────────────────┤
│ Layer 7  Command dispatcher (V: handle_cmd)                           │
├──────────────────────────────────────────────────────────────────────┤
│ Layer 8  Webhook + Blueprint route (W, X, Y) + Scheduler boot (N)     │
└──────────────────────────────────────────────────────────────────────┘

重要違規:目前 F 區 generate_daily_pdf 同時依賴 Layer 2query和 Layer 4openpyxl已是跨層。抽分時建議將其放 Layer 4 並注入 query callback。


3. 外部依賴掃描

3.1 誰 import 本檔

$ grep -rn "from routes.openclaw_bot_routes import\|from routes import openclaw_bot_routes" --include="*.py"
app.py:683:    from routes.openclaw_bot_routes import openclaw_bot_bp

只有一處,且只暴露 openclaw_bot_bp。這對重構極為友善 — 拆分後只要保留 openclaw_bot_bp blueprint 可從新主模組 re-exportapp.py 完全不動。

3.2 本檔 import 的外部模組

模組 用途 風險
database.manager.DatabaseManager DB 連線 被 query 層大量使用
services.logger_manager.SystemLogger log 全檔共用 sys_log
services.mcp_context_service 外部情報 9 個函數 import
services.openclaw_learning_service RAG/學習 try-exceptoptional
services.pchome_crawler 比價 try-exceptoptional
services.ppt_generator(延遲) PPT _generate_ppt_cmd 內部 import

4. 拆分提案(修正版)

原 critic 6 檔方案大方向正確,但經實際依賴分析需要兩處修正:

  1. Excel 匯入L 區) 被遺漏,應獨立為 services/openclaw_excel.pyExcel 匯入 + Excel 匯出 = 一檔)。
  2. Gemini Function CallingT+U 區) 依賴太多 query_*,且是 webhook 訊息分支第二層入口,應獨立為 services/openclaw_nlu.py(非 commands
  3. services/openclaw_menus.py 目前 265 行偏小但高度自成一體,可採納。
  4. 排程任務M+N 區465 行)應獨立為 services/openclaw_scheduler.py(原提案未涵蓋)。

4.1 修正後拆分目標10 檔)

新檔 來源區塊 預估行數 職責 Export interface
routes/openclaw_webhook.py A + W + X + Y + N(boot) ~500 Blueprint 4 個 route、webhook 分派、scheduler 啟動 hook openclaw_bot_bp(只此一個 public
services/openclaw_tg.py C + B + E ~220 Telegram API wrapper、授權、限流、去重、字型 send_messageanswer_callbacksend_typingsend_photosend_document_tgis_authorizedcheck_rate_limitSeenCacheget_chinese_font
services/openclaw_queries.py G + P + Q + I + 部分 D(_GOALS facade) ~1200 所有 DB query + 日期解析 + 策略分析 + goal state 所有 query_*resolve_dateresolve_query_intentanalyze_product_strategyget_goal_statusGoalState(封裝 _GOALS
services/openclaw_charts.py H + R 中 gen_aggregated_chart ~320 matplotlib 圖表 gen_trend_chartgen_products_chartgen_aggregated_chartsetup_mpl_chinese
services/openclaw_excel.py F + L ~500 Excel 匯出(日報)+ Excel 匯入(驗證/handler generate_daily_excel_reporthandle_excel_importExcelPendingStore
services/openclaw_format.py J + R 其餘 ~630 純訊息格式化 所有 fmt_*MEDALS_pchome_link_esc_short_id
services/openclaw_ppt.py K ~560 PPT AI 分析 + 9 種 PPT 生成分派 generate_ppt_cmd_ppt_ai_analysisprivate
services/openclaw_nlu.py S + T + U ~360 Gemini Function Calling 入口 openclaw_answer_FC_TOOLSprivate
services/openclaw_scheduler.py M + N(core) ~470 6 個排程 job + start_scheduler start_schedulerregister_commands
services/openclaw_commands.py V + O ~1050 handle_cmd 主分派 + 所有 submenu/inline keyboard handle_cmdmain_menu_keyboardSUBMENUSAWAIT_PROMPTSInputPendingStore

合計 ~5810 行(扣除重複 import、header docstring 合理vs 原 5999 行。


5. 風險與順序

5.1 拆分順序(由低風險 → 高風險7 階段)

Phase 新檔 風險 前置條件 驗證
A1 services/openclaw_tg.py 🟢 無(純 wrapper無狀態跨區除 _seen_update_* 留在 webhook Webhook 回覆 / 按鈕 / 圖片 / 文件 4 路徑各 1 次煙霧測
A2 services/openclaw_charts.py 🟢 A1send_photo import /chart、早報 PNG 附圖
A3 services/openclaw_format.py 🟢 無副作用 隨 A4 回歸
A4 services/openclaw_queries.py 🟡 _GOALS 需以 GoalState 封裝 13 個 query 都跑過;/sales/top/goal/promo/restock/compare
A5 services/openclaw_excel.py 🟡 A4用 query /report 下載 + 群組拖 xlsx 匯入
A6 services/openclaw_scheduler.py 🟡 A1-A5 停用 4/5 個 job留 1 個做 dry-run;觀察 08:00 競品日報 3 日連續成功
B1 services/openclaw_ppt.py 🟠 A3 + A4 9 種 PPT 全跑一遍
B2 services/openclaw_nlu.py 🟠 A4 openclaw_answer 20 句對話、FC 3 個工具各觸發
B3 services/openclaw_commands.py 🔴 最高 全部 28 個 cmd 分支全測Excel 匯入+目標設定+await 狀態機
B4 routes/openclaw_webhook.py 🟠 B3 保留 blueprint 同名,app.py 零變更

安全網:每 Phase 一個 commit不做 feature flag — 風險控制靠小 diff + 可還原),每階段部署後要跑 smoke見 5.2)。

5.2 Regression 測試重點

[每 Phase 共同]
1. GET  /bot/telegram/webhook_info         → 200 且 url 正確
2. POST /bot/internal/cmd  {cmd:sales}     → 200群組收到業績訊息
3. Telegram 群組發 「今日業績」             → openclaw_answer 回覆且 ≤10s
4. Telegram 群組按「📊業績查詢 → 今日」     → 看到業績 + quick_kb
5. Telegram 群組發 /goal 150000            → _GOALS 寫入、下次 /goal 顯示 150000

[Phase A5 額外]
6. 拖一個 .xlsx 進群組                     → 驗證報告出現、按 ✅ 匯入 OK
7. /report 2026/04/20                      → 收到 Excel 附件

[Phase A6 額外]
8. 手動 _scheduler.get_jobs() 在 shell 中驗證 6 個 job 都在

[Phase B1 額外]
9. cmd:ppt:daily / weekly / monthly / strategy / competitor / promo / growth / vendor / bcg 各跑一次

[Phase B4 最終]
10. 所有 schedule job 接 5 天觀察8:00/8:30/8:45/9:00/12:00/15:00/18:00/21:00 + 週一 9:00

5.3 工時估算refactor-specialist 參考)

Phase 人時 備註
A1 TG wrapper 3h 機械搬運
A2 Charts 2h
A3 Format 2h
A4 Queries + GoalState 6h _GOALS 封裝需設計、跨 4 個呼叫點
A5 Excel 4h _excel_pending 遷移
A6 Scheduler 4h 監控期不算
B1 PPT 5h _ppt_ai_analysis prompt 內容多
B2 NLU 4h _FC_TOOLS schema 保持不動
B3 Commands + Menus 10h 28 個分支、handle_cmd 內部互相呼叫(遞迴)
B4 Webhook thin 5h _seen_update_* + _input_pending 遷移、Gemini Vision 抽函數
小計 45h 不含 smoke 測試與部署監控時段
含監控 60h~2 週 Sprint

5.4 不可忽略的坑

  1. generate_daily_pdf 其實是 ExcelF 區),真 PDF 只是函數名。重構時改名 generate_daily_excel_report 為佳;注意 send_daily_excel 排程會呼叫它。
  2. handle_cmd 會遞迴呼叫自己webhook await flow → handle_cmd('sales', ...)、圖片 Vision → handle_cmd('competitor', ...))—拆到 commands.py 時要注意不要同時在 webhook.py 也留副本。
  3. _ppt_ai_analysis_generate_ppt_cmd 被呼叫 10 次兩者必須同檔openclaw_ppt.py不可分離。
  4. _seen_update_* 雙結構去重ADR-類修補)deque + set 互為見證,拆檔時兩個必須綁定,不能只搬一個。
  5. Blueprint record_once hook 觸發 start_scheduler5995-5998—scheduler 遷移後記得 hook 仍要呼叫新位置。
  6. import calendar as _calfrom datetime import date as _date 散落在 handle_cmd 內部4723-4724—搬遷時要一起帶走否則 UnboundLocalError。
  7. Gemini Vision 圖片分支5694-5752, 58 行)目前 inline 在 telegram_webhook 裡,抽成 function 後既減負擔又可單測 — 建議 Phase A1 的時候一起做。
  8. _on_register blueprint hookY 區)只有 4 行但是整個排程啟動的唯一觸發點 — webhook.py 必須保留此 hook 對應新的 start_scheduler import。

6. 最終交付檢核單(給接手 refactor-specialist

  • app.py:683 可以不變(from routes.openclaw_bot_routes import openclaw_bot_bp 仍有效:可在 routes/openclaw_bot_routes.py 保留 compat shim from routes.openclaw_webhook import openclaw_bot_bp 1 行)
  • 4 個 Blueprint route 行為 1:1 相同webhook、internal_cmd、set_webhook、webhook_info
  • 6 個排程任務於 start_scheduler 裡仍以相同 cron 表達式註冊
  • 28 個 handle_cmd 分支全部保留,分支字串(中/英 alias不變
  • Global state 遷移清單:_GOALS_input_pending_excel_pending_seen_update_*_rate_tracker_scheduler_sched_lock_fh_MPL_FONT_SETUP_DONE 各有確定的新家module-level 或封裝成物件)
  • 跨檔 import 循環檢查:openclaw_commandsopenclaw_queries 不得互相 importcommands 單向 depend on queries
  • 監控 3 日生產驗證後才視為完成

本地圖基於 routes/openclaw_bot_routes.py 5999 行完整掃描產出。若實際 LoC 後續變動,需重新跑 grep 熱度統計(本文件 2.1 節)再修訂拆分邊界。