Some checks failed
CD Pipeline / deploy (push) Failing after 59s
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml) - 部署模式: rsync Python 檔案至 188 → docker restart (volume mount) - Dockerfile/requirements 變動時自動重建 Docker image - 部署通知: Telegram (開始/成功/失敗) - 健康檢查: https://mo.wooo.work/health (最多 5 次重試) - 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
3.0 KiB
Python
88 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
獨立排程器服務
|
||
負責定時執行商品看板和活動看板的爬蟲任務
|
||
"""
|
||
import os
|
||
import sys
|
||
import time
|
||
import logging
|
||
import schedule
|
||
from datetime import datetime, timedelta, timezone
|
||
|
||
# 確保在正確的目錄運行
|
||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
os.chdir(BASE_DIR)
|
||
sys.path.insert(0, BASE_DIR)
|
||
|
||
# 設定台北時區
|
||
TAIPEI_TZ = timezone(timedelta(hours=8))
|
||
|
||
# 設定日誌
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s [%(levelname)s] %(message)s',
|
||
handlers=[
|
||
logging.FileHandler(os.path.join(BASE_DIR, "logs/scheduler.log"), encoding="utf-8"),
|
||
logging.StreamHandler()
|
||
]
|
||
)
|
||
logger = logging.getLogger("SchedulerService")
|
||
|
||
# 導入爬蟲任務
|
||
from scheduler import run_momo_task, run_edm_task, run_festival_task, run_auto_import_task
|
||
|
||
def safe_task_wrapper(task_func, task_name):
|
||
"""安全的任務包裝器,捕獲異常避免排程中斷"""
|
||
def wrapper():
|
||
try:
|
||
timestamp = datetime.now(TAIPEI_TZ).strftime('%Y-%m-%d %H:%M:%S')
|
||
logger.info(f"⏰ [{timestamp}] 開始執行任務: {task_name}")
|
||
task_func()
|
||
logger.info(f"✅ [{timestamp}] 任務完成: {task_name}")
|
||
except Exception as e:
|
||
logger.error(f"❌ 任務執行失敗: {task_name} | Error: {e}", exc_info=True)
|
||
return wrapper
|
||
|
||
def main():
|
||
"""主程序"""
|
||
logger.info("=" * 60)
|
||
logger.info("🚀 WOOO TECH 排程器服務啟動")
|
||
logger.info("=" * 60)
|
||
|
||
# 設定排程任務
|
||
# V-Opt 2026-01-14: 商品看板和 EDM 需每小時執行以監控價格變化
|
||
# 商品看板(主站爬蟲)- 每 1 小時執行
|
||
schedule.every(1).hours.do(safe_task_wrapper(run_momo_task, "商品看板爬蟲"))
|
||
logger.info("📅 已設定:商品看板爬蟲 - 每 1 小時執行一次")
|
||
|
||
# EDM 限時搶購看板 - 每 1 小時執行
|
||
schedule.every(1).hours.do(safe_task_wrapper(run_edm_task, "EDM 限時搶購爬蟲"))
|
||
logger.info("📅 已設定:EDM 限時搶購爬蟲 - 每 1 小時執行一次")
|
||
|
||
# 購物節活動看板 - 每 6 小時執行
|
||
schedule.every(6).hours.do(safe_task_wrapper(run_festival_task, "購物節活動爬蟲"))
|
||
logger.info("📅 已設定:購物節活動爬蟲 - 每 6 小時執行一次")
|
||
|
||
# Google Drive 自動匯入 - 每 30 分鐘執行
|
||
schedule.every(30).minutes.do(safe_task_wrapper(run_auto_import_task, "Google Drive 自動匯入"))
|
||
logger.info("📅 已設定:Google Drive 自動匯入 - 每 30 分鐘執行一次")
|
||
|
||
logger.info("=" * 60)
|
||
logger.info("✅ 所有排程任務已設定完成,開始監聽...")
|
||
logger.info("=" * 60)
|
||
|
||
# 主循環
|
||
try:
|
||
while True:
|
||
schedule.run_pending()
|
||
time.sleep(1)
|
||
except KeyboardInterrupt:
|
||
logger.info("🔌 收到中斷信號,排程器服務正在關閉...")
|
||
except Exception as e:
|
||
logger.error(f"🚨 排程器服務異常: {e}", exc_info=True)
|
||
sys.exit(1)
|
||
|
||
if __name__ == "__main__":
|
||
main()
|