fix(db): 補全 metadata model import 與 realtime sales ORM
ADR-017 Phase 3f-0
This commit is contained in:
114
docs/adr/ADR-017-modularization-cleanup-roadmap.md
Normal file
114
docs/adr/ADR-017-modularization-cleanup-roadmap.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# ADR-017: 模組化收尾路線圖(Phase 3f)
|
||||
|
||||
- **狀態**: Accepted
|
||||
- **日期**: 2026-04-29
|
||||
- **觸發**: 12-Agent 全景盤點(debugger / refactor-specialist / critic / db-expert / Explore)
|
||||
- **相關 ADR**: ADR-008(188 拓撲)、ADR-011(資源隔離)、ADR-016(cache fingerprint)
|
||||
- **相關 Memory**: feedback_flask_blueprint_shadow、project_phase3e_refactor_progress、project_phase3f_cleanup_roadmap、feedback_db_metadata_import
|
||||
|
||||
## 背景
|
||||
|
||||
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 孤兒 |
|
||||
|
||||
## 決策
|
||||
|
||||
執行 **Phase 3f 五階段收尾**,總工期估 12-15 小時(不含驗證),每階段獨立 commit、每階段 critic 審查、每階段先 SSH 驗證 production。
|
||||
|
||||
### Phase 3f-0:DB metadata 救急(30 分鐘,最高優先)
|
||||
1. `database/manager.py` 補完 import:
|
||||
- `permission_models`(Permission, UserPermission)
|
||||
- `vendor_models` 補 VendorList / VendorEmail / EmailSendLog
|
||||
- `ai_models` 顯式 import 4 個 AI history/template class
|
||||
- `autoheal_models` 顯式 import 7 個 AIOps class,移除 `ai_models.py` re-export shim
|
||||
2. 處置 `realtime_sales_monthly` 孤兒表:
|
||||
- 選項 A:建 `database/realtime_sales_models.py`(推薦,補 ORM 一致性)
|
||||
- 選項 B:移除 `app.py:693` 的 import,metrics 不依賴此 model
|
||||
3. PostgreSQL 與 SQLite 初始化都執行全域 `Base.metadata.create_all()`;PostgreSQL 路徑以 process-local guard + advisory lock 保護,避免一般流量重複碰 DDL
|
||||
4. `app.py` 啟動加 metadata self-check,缺表直接 `SystemExit`
|
||||
5. `docker/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 小時)
|
||||
1. Dockerfile + docker-compose.yml gunicorn 加 `--preload`,只作為 COW 記憶體優化,不作為一致性保證
|
||||
2. 新建 `services/cache_manager.py` 套用 ADR-016 fingerprint 模式
|
||||
3. 移除三處重複 `_SALES_*_CACHE` 定義(app.py:82, routes/sales_routes.py:32, services/cache_service.py:16)→ 統一 import `services.cache_manager`
|
||||
4. `clear_cache` endpoint 從「N-POST 廣播」改「DB fingerprint pull」(已在 daily_sales bp 完成,擴及 sales)
|
||||
|
||||
### Phase 3f-3:穩定性補強(1-2 小時)
|
||||
1. 先在 `services/event_router.py` 補同步安全 facade(如 `notify_failure()` / `dispatch_sync()`),因現有 `EventRouter` 只有 async `dispatch()`,scheduler 直接呼叫會掉告警
|
||||
2. scheduler.py 7 處裸 `except: pass`(line 243, 254, 582, 587, 653, 890, 1222)改為 `except Exception as e: logger.exception(...)`,P1/P2 經 EventRouter 同步 facade 強制告警
|
||||
3. docker-compose.yml 刪 7 條死路徑 mount(vendor_routes.py / vendor_stockout_*.html)
|
||||
4. routes/vendor_routes.py:28-30 template_folder 改用絕對路徑,移除靠 Flask fallback 的脆弱依賴
|
||||
|
||||
### Phase 3f-4:模板統一(2-3 小時)
|
||||
1. 補 2 個 TemplateNotFound:`trends.html`(trend_routes.py:33)、`login_history.html`(user_routes.py:40)→ 找回或停用對應 endpoint
|
||||
2. 刪 `web/templates/sales_analysis.html`(0-byte 空檔)
|
||||
3. 刪 3 個雙寫死檔:`web/templates/brand_assets.html`、`web/templates/growth_analysis.html`、根目錄 `logs.html`
|
||||
4. 根目錄 11 個 *.html 全搬 `templates/`,確認 docker-compose 舊 mount 已清理後,再移除 `app.py:185-188` ChoiceLoader 的 BASE_DIR fallback
|
||||
5. `web/templates/` 僅留 `vendor_stockout/` 子目錄(vendor_bp 自帶 template_folder)
|
||||
|
||||
### Phase 3f-5:死碼清除(30 分鐘)
|
||||
1. 確認並刪除 3 個孤兒 service:
|
||||
- `services/elephant_alpha_decision_router.py`
|
||||
- `services/telegram_ai_integration.py`
|
||||
- `services/watcher_agent.py`
|
||||
2. `.env.example` 補齊 15+ 個程式碼實際讀但 example 缺的變數(AIDER_*, ELEPHANT_ALPHA_*, HEAL_SSH_*, NVIDIA_API_KEY 等)
|
||||
|
||||
## 風險與回滾
|
||||
|
||||
每階段獨立 commit,CI/CD 失敗自動回滾(依賴 ADR-014 防線)。Phase 3f-1 為高風險區,必須:
|
||||
1. 先在本機 `python app.py` 啟動驗證 url_map 無重複
|
||||
2. critic 審 diff 過關
|
||||
3. 部署後 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 全清
|
||||
@@ -37,6 +37,8 @@
|
||||
| [013](ADR-013-aiops-autoheal.md) | AIOps 自動修復閉環架構(七步閉環 + SSH Jump Executor) | Accepted | 2026-04-19 |
|
||||
| [014](ADR-014-ppt-report-system-v2.md) | PPT 簡報系統 V2 — 原生圖表 + 9 種報告類型 | Accepted | 2026-04-20 |
|
||||
| [015](ADR-015-telegram-bot-menu-restoration.md) | Telegram Bot 完整菜單系統恢復 | Accepted | 2026-04-20 |
|
||||
| [016](ADR-016-daily-sales-cache-fingerprint.md) | daily_sales cache fingerprint(gunicorn 多 worker 一致性) | Accepted | 2026-04-29 |
|
||||
| [017](ADR-017-modularization-cleanup-roadmap.md) | 模組化收尾路線圖(Phase 3f) | Accepted | 2026-04-29 |
|
||||
|
||||
## 規範
|
||||
|
||||
|
||||
Reference in New Issue
Block a user