[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>
This commit is contained in:
282
docs/refactor/openclaw_bot_routes_split_plan.md
Normal file
282
docs/refactor/openclaw_bot_routes_split_plan.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# routes/openclaw_bot_routes.py 拆分地圖
|
||||
|
||||
> **產出時間**:2026-04-24
|
||||
> **現況**:5999 行單檔(God File),僅被 `app.py:683` 一處 import
|
||||
> **目的**:為 H1 重構提供設計藍圖,**本文件不改任何 .py**
|
||||
> **交付對象**:refactor-specialist / critic / planner
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景快照
|
||||
|
||||
| 指標 | 數字 |
|
||||
|------|------|
|
||||
| 總行數 | 5999 |
|
||||
| 外部 importer | 1(`app.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_service`、`services.openclaw_learning_service`、`services.pchome_crawler`、`services.ppt_generator`、`database.manager`、`services.logger_manager` |
|
||||
|
||||
---
|
||||
|
||||
## 1. 全檔分區(按行號順序)
|
||||
|
||||
| # | 行號 | 區塊 | 主要符號 | 對外依賴 | 內部共享狀態 |
|
||||
|---|-----|------|---------|---------|-------------|
|
||||
| A | 1-72 | imports、Blueprint、env 常數 | `GEMINI_*`、`BOT_TOKEN`、`ALLOWED_GROUP`、`NVIDIA_*`、`openclaw_bot_bp` | 3rd-party + 4 個 services | — |
|
||||
| B | 74-138 | **安全/限流/去重基礎設施** | `_is_authorized`、`_check_rate_limit`、`_seen_update_ids`、`_seen_update_order`、`_rate_tracker`、`TRIGGER_KEYWORDS`、`ALLOWED_USERS` | os、time | 持有 rate + seen 狀態 |
|
||||
| C | 140-251 | **Telegram API wrapper** | `_tg`、`_strip_markdown`、`send_message`、`answer_callback`、`send_typing`、`send_photo`、`send_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_sales`、`query_category_monthly`、`query_comparison`、`query_daily_history`、`query_restock_forecast`、`query_category_detail`、`query_promo_comparison`、`query_anomalies`、`query_growth_data`、`query_vendor_bcg_data`、`get_goal_status` | DatabaseManager、SQLAlchemy text | 讀 `_GOALS`(`get_goal_status`) |
|
||||
| H | 1129-1381 | **Matplotlib 圖表** | `_MPL_FONT_SETUP_DONE`、`_setup_mpl_chinese`、`gen_trend_chart`、`gen_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_category`、`fmt_comparison`、`fmt_goal_status`、`_short_id`、`fmt_restock_forecast`、`fmt_category_detail`、`fmt_promo_comparison`、`track_competitor_price_changes`、`fmt_monthly`、`fmt_strategy` | — | 純字串處理 |
|
||||
| K | 1962-2723 | **AI 簡報分析 + PPT 指令分派** | `_clean_ai_text`、`_ppt_ai_analysis`、`_generate_ppt_cmd` | requests(NIM)、`services.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_report`、`send_evening_report`、`send_weekly_report`、`check_anomalies`、`send_competitor_report`、`send_daily_excel`、`_sched_lock_fh` | `query_*`、`generate_daily_pdf`、`gen_trend_chart`、`send_message/photo/document`、`pchome_*` | 產生告警後去重寫 DB |
|
||||
| N | 3323-3388 | **Scheduler 啟動/指令註冊** | `start_scheduler`、`register_commands` | apscheduler(延遲)、fcntl | 寫 `_scheduler` |
|
||||
| O | 3392-3649 | **Inline Keyboard / Submenu / Await Prompt** | `_BACK`、`main_menu_keyboard`、`_submenu_*`(10 個)、`_SUBMENUS`、`_AWAIT_PROMPTS`、`sales_quick_kb` | `latest_date`、`_GOALS` | — |
|
||||
| P | 3651-3895 | **DB Query 層 #2(基礎型)** | `_db`、`normalize_date`、`latest_date`、`query_sales`、`query_top_products`、`query_top_vendors`、`query_weekly_trend`、`query_trend_range`、`query_monthly_summary`、`query_date_range`、`query_available_months`、`query_top_products_range` | DatabaseManager、SQLAlchemy text | — |
|
||||
| Q | 3897-3979 | **日期/意圖解析** | `resolve_date`、`resolve_query_intent` | — | — |
|
||||
| R | 3981-4325 | **訊息格式化 #2** | `MEDALS`、`fmt_sales`、`_esc`、`_pchome_link`、`PCHOME_URL`、`fmt_products`、`fmt_vendors`、`fmt_trend`、`fmt_trend_summary`、`gen_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_answer(AI 入口)** | `openclaw_answer` | requests(Gemini/NVIDIA)、`_FC_TOOLS`、`_execute_tool`、`build_rag_context`、`store_conversation` | — |
|
||||
| V | 4700-5587 | **handle_cmd 單體(28 個 cmd 分支)** | `handle_cmd` | **幾乎所有上游函數** | 寫 `_GOALS`、讀 `_excel_pending` |
|
||||
| W | 5589-5943 | **telegram_webhook(354 行)** | route `/bot/telegram/webhook` | `_is_authorized`、`_check_rate_limit`、`_SUBMENUS`、`_AWAIT_PROMPTS`、`_input_pending`、`_excel_pending`、`_GOALS`、`handle_cmd`、`openclaw_answer`、Gemini Vision API | 寫 `_seen_update_*`、`_input_pending`、`_GOALS` |
|
||||
| X | 5946-5970 | internal_cmd 端點 | route `/bot/internal/cmd` | `handle_cmd`(threaded) | — |
|
||||
| Y | 5973-5999 | 管理端點 + blueprint hook | `set_webhook`、`webhook_info`、`_on_register` | `_tg`、`register_commands`、`start_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_markdown`(C 區)— 僅 `send_message` 內部降級時使用。
|
||||
- 所有 6 個排程任務(`send_*_report`、`check_anomalies`、`send_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_webhook`(W 區),**只要 webhook 區塊抽出,狀態即可隨之遷移**。`_GOALS` 則跨 4 個區塊讀寫,需要以 service 或 singleton 封裝。
|
||||
|
||||
### 2.5 依賴層級(ASCII,抽象層次由低到高)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ Layer 0 常數 + env(A 區)— 任何層皆可 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 2(query)和 Layer 4(openpyxl),已是跨層。抽分時建議將其放 Layer 4 並注入 query callback。
|
||||
|
||||
---
|
||||
|
||||
## 3. 外部依賴掃描
|
||||
|
||||
### 3.1 誰 import 本檔
|
||||
|
||||
```bash
|
||||
$ 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-export,`app.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-except,optional |
|
||||
| `services.pchome_crawler` | 比價 | try-except,optional |
|
||||
| `services.ppt_generator`(延遲) | PPT | 在 `_generate_ppt_cmd` 內部 import |
|
||||
|
||||
---
|
||||
|
||||
## 4. 拆分提案(修正版)
|
||||
|
||||
原 critic 6 檔方案大方向正確,但**經實際依賴分析**需要兩處修正:
|
||||
|
||||
1. **Excel 匯入(L 區)** 被遺漏,應獨立為 `services/openclaw_excel.py`(Excel 匯入 + Excel 匯出 = 一檔)。
|
||||
2. **Gemini Function Calling(T+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_message`、`answer_callback`、`send_typing`、`send_photo`、`send_document`、`_tg`、`is_authorized`、`check_rate_limit`、`SeenCache`、`get_chinese_font` |
|
||||
| `services/openclaw_queries.py` | G + P + Q + I + 部分 D(`_GOALS` facade) | ~1200 | 所有 DB query + 日期解析 + 策略分析 + goal state | 所有 `query_*`、`resolve_date`、`resolve_query_intent`、`analyze_product_strategy`、`get_goal_status`、`GoalState`(封裝 `_GOALS`) |
|
||||
| `services/openclaw_charts.py` | H + R 中 `gen_aggregated_chart` | ~320 | matplotlib 圖表 | `gen_trend_chart`、`gen_products_chart`、`gen_aggregated_chart`、`setup_mpl_chinese` |
|
||||
| `services/openclaw_excel.py` | F + L | ~500 | Excel 匯出(日報)+ Excel 匯入(驗證/handler) | `generate_daily_excel_report`、`handle_excel_import`、`ExcelPendingStore` |
|
||||
| `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_analysis`(private) |
|
||||
| `services/openclaw_nlu.py` | S + T + U | ~360 | Gemini Function Calling 入口 | `openclaw_answer`、`_FC_TOOLS`(private) |
|
||||
| `services/openclaw_scheduler.py` | M + N(core) | ~470 | 6 個排程 job + start_scheduler | `start_scheduler`、`register_commands` |
|
||||
| `services/openclaw_commands.py` | V + O | ~1050 | `handle_cmd` 主分派 + 所有 submenu/inline keyboard | `handle_cmd`、`main_menu_keyboard`、`SUBMENUS`、`AWAIT_PROMPTS`、`InputPendingStore` |
|
||||
|
||||
合計 ~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` | 🟢 低 | A1(`send_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` 其實是 Excel**(F 區),真 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_scheduler`**(5995-5998)—scheduler 遷移後記得 hook 仍要呼叫新位置。
|
||||
6. **`import calendar as _cal`、`from 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 hook**(Y 區)只有 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_commands` ↔ `openclaw_queries` 不得互相 import(commands 單向 depend on queries)
|
||||
- [ ] 監控 3 日生產驗證後才視為完成
|
||||
|
||||
---
|
||||
|
||||
*本地圖基於 routes/openclaw_bot_routes.py 5999 行完整掃描產出。若實際 LoC 後續變動,需重新跑 grep 熱度統計(本文件 2.1 節)再修訂拆分邊界。*
|
||||
Reference in New Issue
Block a user