Some checks are pending
CD Pipeline / deploy (push) Waiting to run
技術債清零 (2026-04-19): - migrations/010: ai_insights 補 decay_exempt/avg_quality/status/ai_model/feedback 欄位 - migrations/011: embedding_retry_queue 持久化表 (ADR-009) - migrations/012: backup_log 備份記錄表 - services/openclaw_learning_service: 記憶體 Queue → DB retry queue,時間衰減 RAG - services/nemoton_dispatcher_service: 三個 tool 強制雙寫 ai_insights (_sink_insight_to_km) - services/import_service: Excel 前置欄位防禦(商品名稱類 + 業績金額類) - services/ollama_service: generate_embedding 新增 EMBEDDING_HOST env,embedding 永遠走 192.168.0.111 - SYSTEM_VERSION: V9.4 → V10.3 DB 備份機制: - scripts/pg_backup.sh: host-level pg_dump 備份腳本,cron 每日 02:00,保留 7 天,Telegram 通知 - services/db_backup_service.py: Python 備份 service,寫入 backup_log - scheduler: run_db_backup_task (02:00) + run_backup_monitor_task (每 6h AI Agent 監控) - Dockerfile: 加入 postgresql-client 文件: - CLAUDE.md: 環境架構依 ADR-008 實地重寫,含完整 SSH/Docker 部署 SOP - PROJECT_CONSTITUTION.md: 內容已整合入 CLAUDE.md,刪除重複檔案 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
139 lines
4.6 KiB
Python
139 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
MOMO Pro System - 獨立排程服務
|
||
此腳本用於 K8s scheduler 部署,獨立運行排程任務
|
||
避免 Gunicorn 多 worker 重複執行的問題
|
||
"""
|
||
import os
|
||
import sys
|
||
import time
|
||
import logging
|
||
import schedule
|
||
from datetime import datetime
|
||
|
||
# 設定日誌
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s [%(levelname)s] %(message)s',
|
||
handlers=[
|
||
logging.StreamHandler(sys.stdout)
|
||
]
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 確保能夠導入專案模組
|
||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
sys.path.insert(0, BASE_DIR)
|
||
|
||
# 設定環境變數(如果未設定)
|
||
if not os.environ.get('DATABASE_URL'):
|
||
os.environ['DATABASE_URL'] = 'sqlite:///data/momo_database.db'
|
||
|
||
def main():
|
||
"""主函數:初始化並運行排程"""
|
||
logger.info("=" * 60)
|
||
logger.info("🚀 MOMO Pro Scheduler 啟動中...")
|
||
logger.info("=" * 60)
|
||
|
||
# 導入排程任務
|
||
try:
|
||
from scheduler import (
|
||
run_momo_task,
|
||
run_edm_task,
|
||
run_festival_task,
|
||
run_auto_import_task,
|
||
run_whitepage_check,
|
||
run_competitor_price_feeder_task,
|
||
run_icaim_analysis_task,
|
||
run_weekly_strategy_task,
|
||
run_db_backup_task,
|
||
run_backup_monitor_task,
|
||
)
|
||
logger.info("✅ 排程任務模組載入成功")
|
||
except ImportError as e:
|
||
logger.error(f"❌ 無法載入排程模組: {e}")
|
||
sys.exit(1)
|
||
|
||
# 檢查是否停用自動匯入
|
||
disable_auto_import = os.environ.get('DISABLE_AUTO_IMPORT', 'false').lower() == 'true'
|
||
|
||
# 設定排程
|
||
schedule.every(1).hours.do(run_momo_task)
|
||
logger.info("📅 已設定:每小時執行主站爬蟲任務")
|
||
|
||
schedule.every(1).hours.do(run_edm_task)
|
||
logger.info("📅 已設定:每小時執行 EDM 爬蟲任務")
|
||
|
||
schedule.every(1).hours.do(run_festival_task)
|
||
logger.info("📅 已設定:每 6 小時執行購物節爬蟲任務")
|
||
|
||
if not disable_auto_import:
|
||
schedule.every(30).minutes.do(run_auto_import_task)
|
||
logger.info("📅 已設定:每 30 分鐘執行 Google Drive 自動匯入任務")
|
||
else:
|
||
logger.info("⚠️ 自動匯入已停用 (DISABLE_AUTO_IMPORT=true)")
|
||
|
||
schedule.every(30).minutes.do(run_whitepage_check)
|
||
logger.info("📅 已設定:每 30 分鐘執行網頁白頁監控任務")
|
||
|
||
schedule.every(4).hours.do(run_competitor_price_feeder_task)
|
||
logger.info("📅 已設定:每 4 小時執行 PChome 競品價格抓取任務")
|
||
|
||
schedule.every(6).hours.do(run_icaim_analysis_task)
|
||
logger.info("📅 已設定:每 6 小時執行 ICAIM 競價情報分析(Hermes→NemoTron→Telegram)")
|
||
|
||
schedule.every().monday.at("07:00").do(run_weekly_strategy_task)
|
||
logger.info("📅 已設定:每週一 07:00 執行 Gemini 策略師週報任務")
|
||
|
||
schedule.every().day.at("02:00").do(run_db_backup_task)
|
||
logger.info("📅 已設定:每日 02:00 執行 PostgreSQL 資料庫備份")
|
||
|
||
schedule.every(6).hours.do(run_backup_monitor_task)
|
||
logger.info("📅 已設定:每 6 小時執行備份健康監控(AI Agent 跟進)")
|
||
|
||
logger.info("=" * 60)
|
||
logger.info("✅ 排程器已啟動,等待任務執行...")
|
||
logger.info("=" * 60)
|
||
|
||
# 啟動時立即執行一次自動匯入(如果未停用)
|
||
if not disable_auto_import:
|
||
logger.info("🔄 啟動時執行一次自動匯入...")
|
||
try:
|
||
run_auto_import_task()
|
||
except Exception as e:
|
||
logger.error(f"❌ 啟動時自動匯入失敗: {e}")
|
||
|
||
# 啟動時立即執行一次爬蟲任務
|
||
logger.info("🔄 啟動時執行一次 MOMO 商品爬蟲...")
|
||
try:
|
||
run_momo_task()
|
||
except Exception as e:
|
||
logger.error(f"❌ 啟動時 MOMO 爬蟲失敗: {e}")
|
||
|
||
logger.info("🔄 啟動時執行一次 EDM 活動爬蟲...")
|
||
try:
|
||
run_edm_task()
|
||
except Exception as e:
|
||
logger.error(f"❌ 啟動時 EDM 爬蟲失敗: {e}")
|
||
|
||
logger.info("🔄 啟動時執行一次購物節爬蟲...")
|
||
try:
|
||
run_festival_task()
|
||
except Exception as e:
|
||
logger.error(f"❌ 啟動時購物節爬蟲失敗: {e}")
|
||
|
||
# 運行排程循環
|
||
while True:
|
||
try:
|
||
schedule.run_pending()
|
||
time.sleep(1)
|
||
except KeyboardInterrupt:
|
||
logger.info("🛑 收到中斷信號,排程器停止")
|
||
break
|
||
except Exception as e:
|
||
logger.error(f"❌ 排程執行錯誤: {e}")
|
||
time.sleep(5) # 發生錯誤時等待 5 秒後繼續
|
||
|
||
if __name__ == "__main__":
|
||
main()
|