9.5 KiB
ADR-017: 模組化收尾路線圖(Phase 3f)
- 狀態: Accepted
- 日期: 2026-04-29
- 觸發: 12 Agent 全景盤點(debugger / refactor-specialist / critic / db-expert / explorer)
- 相關 ADR: ADR-008(188 拓撲)、ADR-011(資源隔離)、ADR-016(cache fingerprint)
- 相關 Memory:
docs/memory/project_phase3f_cleanup_roadmap.md、docs/memory/feedback_db_metadata_import.md、docs/memory/history_logs.md
背景
Phase 3e(4/28-29)完成 app.py 7,386→6,590 行(-10.8%),但僅完成「搬檔」,未完成「拆乾淨」。12 Agent 盤點揭露 6 個面向的殘留問題,且發現新的 critical 風險(DB metadata import 漏洞、孤兒表)。
完成度真相(2026-04-29 盤點基線)
| 維度 | 完成度 | 真相 |
|---|---|---|
| 路由搬檔(41 條 → 28 BP) | 68% | 13 條 app.py 獨有未遷移 |
| 路由「拆乾淨」 | 0% | app.py 41 條一條未刪,28 條與 BP 同 URL 雙寫,由 first-registered-wins 決定行為 |
USE_MODULAR_ROUTES 開關 |
0% | register_blueprints() 從未被 app.py 呼叫,整套設計死碼 |
| services/ 模組化 | ~95% | 唯一乾淨;3 個孤兒 service(0 引用) |
| 模板統一 | ~50% | 三目錄並存 + 1 空檔 + 3 死檔 + 2 TemplateNotFound 風險 |
| DB schema vs Model | ~60% | manager.py import 漏 3 模組;6 張表有 SQL 無 ORM;realtime_sales_monthly 孤兒 |
實作狀態補記(2026-05-13)
上表是 2026-04-29 立案時的盤點基線;後續整改已把多個 HIGH 項目落地並加上回歸守門:
app.py已收斂為 Flask bootstrap / Blueprint registration / 啟動自檢,active@app.route為 0,並由tests/test_phase3f_cleanup_contracts.py::test_app_py_stays_blueprint_only_for_routes守住。- 舊
USE_MODULAR_ROUTES、register_blueprints()、MODULAR_ENDPOINTS、duplicate cleanup shim 已移除;routes/__init__.py僅保留 package docstring。 - DB metadata / migration 覆蓋已由
tests/test_migration_metadata_coverage.py與啟動自檢守住,v5 observability / Market Intel 等表不再只靠手動口述。 - 模板路徑已收斂為
templates/與web/templates/vendor_stockout/;根層 placeholdertemplates/list.html已刪除並有測試防回歸。 - 大檔治理仍未結案:
routes/openclaw_bot_routes.py、routes/admin_observability_routes.py、routes/sales_routes.py、scheduler.py仍超過 800 行,後續只能做 bugfix、安全修補或往外抽模組。
決策
執行 Phase 3f 五階段收尾,總工期估 12-15 小時(不含驗證),每階段獨立 commit、每階段 critic 審查、每階段先 SSH 驗證 production。
Phase 3f-0:DB metadata 救急(30 分鐘,最高優先)
database/manager.py補完 import:permission_models(Permission, UserPermission)vendor_models補 VendorList / VendorEmail / EmailSendLogai_models顯式 import 4 個 AI history/template classautoheal_models顯式 import 7 個 AIOps class,移除ai_models.pyre-export shim
- 處置
realtime_sales_monthly孤兒表:- 選項 A:建
database/realtime_sales_models.py(推薦,補 ORM 一致性) - 選項 B:移除
app.py:693的 import,metrics 不依賴此 model
- 選項 A:建
- PostgreSQL 與 SQLite 初始化都執行全域
Base.metadata.create_all();PostgreSQL 路徑以 process-local guard + advisory lock 保護,避免一般流量重複碰 DDL app.py啟動加 metadata self-check,缺表直接SystemExitdocker/postgres/init/01-init.sql的realtime_sales_monthly欄位同步 ORM,避免 fresh volume 先由 init.sql 建出窄表後 create_all 無法補欄位
驗收:Base.metadata 含全 34 個 table;create_all 在新環境零漏。
Phase 3f-1:路由雙註冊徹底解(4-6 小時,P9 切分)
策略:保留 Blueprint,刪 app.py 對應 @app.route(28 條),13 條 app.py 獨有遷至 BP。實作順序要先處理 API shadow、次要頁面與 /brand_assets,首頁 / 最後動,因為多處 url_for('index') 仍依賴 app endpoint name。
子任務(按 BP 分組獨立 commit):
| Sprint | 範圍 | 動作 |
|---|---|---|
| 3f-1-a | dashboard_bp | 刪 app.py:722 /;確認 BP 版可用 |
| 3f-1-b | edm_bp | 刪 app.py:1029, 1280 兩條 |
| 3f-1-c | export_bp | 刪 app.py:1431-1905 共 9 條;補 /api/export/excel/seasonality_detail 進 BP |
| 3f-1-d | api_bp | 刪 app.py:2219-2451 共 6 條 trigger/run |
| 3f-1-e | import_bp | 刪 app.py:2691, 2980 兩條 |
| 3f-1-f | monthly_bp | 刪 app.py:3076, 3083 兩條 |
| 3f-1-g | sales_bp | 刪 app.py:3592-5809 共 7 條;同時刪 routes/sales_routes.py 6 處 wrapper(174-348),把實作搬進 BP |
| 3f-1-h | system routes 補強 | 現有 system_bp 有 /api/system prefix;公開 URL(/health /metrics /logs /settings 等)需新建無 prefix 的 system_public_bp 或拆成 public/internal 兩個 BP,不能直接塞進現有 system_bp。/abc_analysis/detail 歸 sales_bp。 |
| 3f-1-i | 死碼清除 | 刪 routes/__init__.py:32-177 整套(register_blueprints / MODULAR_ENDPOINTS / is_endpoint_modular / cleanup_duplicate_routes)+ config.py:244-254 USE_MODULAR_ROUTES |
驗收:
app.url_map.iter_rules()任一 (rule, methods) 皆唯一- 啟動加 self-check:duplicate detect 直接 raise SystemExit
- 全 endpoint smoke test(critic 監督)
Phase 3f-2:Cache 統一(2-3 小時)
- Dockerfile + docker-compose.yml gunicorn 加
--preload,只作為 COW 記憶體優化,不作為一致性保證 - 新建
services/cache_manager.py套用 ADR-016 fingerprint 模式 - 移除三處重複
_SALES_*_CACHE定義(app.py:82, routes/sales_routes.py:32, services/cache_service.py:16)→ 統一 importservices.cache_manager clear_cacheendpoint 從「N-POST 廣播」改「DB fingerprint pull」(已在 daily_sales bp 完成,擴及 sales)
Phase 3f-3:穩定性補強(1-2 小時)
- 先在
services/event_router.py補同步安全 facade(如notify_failure()/dispatch_sync()),因現有EventRouter只有 asyncdispatch(),scheduler 直接呼叫會掉告警 - scheduler.py 7 處裸
except: pass(line 243, 254, 582, 587, 653, 890, 1222)改為except Exception as e: logger.exception(...),P1/P2 經 EventRouter 同步 facade 強制告警 - docker-compose.yml 刪 7 條死路徑 mount(vendor_routes.py / vendor_stockout_*.html)
- routes/vendor_routes.py:28-30 template_folder 改用絕對路徑,移除靠 Flask fallback 的脆弱依賴
Phase 3f-4:模板統一(2-3 小時)
- 補 2 個 TemplateNotFound:
trends.html(trend_routes.py:33)、login_history.html(user_routes.py:40)→ 找回或停用對應 endpoint - 刪
web/templates/sales_analysis.html(0-byte 空檔) - 刪 3 個雙寫死檔:
web/templates/brand_assets.html、web/templates/growth_analysis.html、根目錄logs.html - 根目錄 11 個 *.html 全搬
templates/,確認 docker-compose 舊 mount 已清理後,再移除app.py:185-188ChoiceLoader 的 BASE_DIR fallback web/templates/僅留vendor_stockout/子目錄(vendor_bp 自帶 template_folder)
Phase 3f-5:死碼清除(30 分鐘)
- 確認並刪除 3 個孤兒 service:
services/elephant_alpha_decision_router.pyservices/telegram_ai_integration.pyservices/watcher_agent.py
.env.example補齊 15+ 個程式碼實際讀但 example 缺的變數(AIDER_, ELEPHANT_ALPHA_, HEAL_SSH_*, NVIDIA_API_KEY 等)
風險與回滾
每階段獨立 commit,CI/CD 失敗自動回滾(依賴 ADR-014 防線)。Phase 3f-1 為高風險區,必須:
- 先在本機
python app.py啟動驗證 url_map 無重複 - critic 審 diff 過關
- 部署後 SSH 健康檢查
/health+ 抽測 5 條核心 endpoint
不做的事
- 不做模組化開關設計回填(USE_MODULAR_ROUTES 已死,直接刪)
- 不做 ORM 全面遷移(25 張無 SQL migration 軌跡的 table 留待 Phase 4)
- 不在 3f-0 修
docker/postgres/init/01-init.sql與 ORM 的products中文欄位 schema drift;這是 Phase 4 migration 題 - 不做 openclaw_bot_routes.py(5,543 行)拆解(留 Phase 4)
- 不撤換 NGROK Token(統帥 2026-04-29 明示先忽略)
完成定義
- app.py < 5,000 行
- url_map 零重複
- create_all 新環境零漏表
- 模板僅存
templates/+web/templates/vendor_stockout/ - gunicorn
--preload啟用、cache 跨 worker 一致 - scheduler 7 處 except 全改、docker-compose mount 全清
2026-04-30 補充決策:模組化治理守門
Phase 3f 已陸續完成 DB metadata、路由註冊、cache、scheduler、模板與 orphan cleanup 的多個收斂項目,但 line-count 盤點仍顯示 15 個 Python 檔案超過 800 行。為避免後續功能開發再次把 route / service 寫成巨檔,本 ADR 補充以下守門規則:
app.py只保留 Flask app bootstrap、Blueprint registration、啟動自檢、版本與全域設定;不得新增 route 或商業邏輯。routes/必須保持 thin controller,重複邏輯需抽到services/或utils/。- 超過 800 行的 Python 檔案列入
docs/memory/code_modularization_inventory_20260430.md,只能做安全修補、bugfix、或往外抽模組;新增功能應先拆分。 - 新增 Python 檔案超過 600 行需提出拆分理由;超過 800 行需同步更新 inventory 與測試。
tests/test_modularization_governance.py是守門測試:新巨檔沒有被盤點會失敗。
詳細操作規則以 docs/guides/modularization_governance.md 為準。