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>
126 lines
4.4 KiB
Python
126 lines
4.4 KiB
Python
# cSpell:ignore momo
|
||
"""
|
||
更新所有監控商品的圖片
|
||
使用 i_code 從商品詳情頁直接獲取圖片 URL
|
||
"""
|
||
import os
|
||
import sys
|
||
import time
|
||
from sqlalchemy import create_engine
|
||
from sqlalchemy.orm import sessionmaker
|
||
|
||
# 設定路徑
|
||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
sys.path.insert(0, BASE_DIR)
|
||
|
||
from database.models import Product
|
||
from utils.image_url_builder import get_product_image_url
|
||
|
||
# 資料庫路徑
|
||
DB_PATH = os.path.join(BASE_DIR, 'data', 'momo_database.db')
|
||
|
||
def update_all_images(batch_size: int = 100, delay: float = 0.5, start_from: int = 0):
|
||
"""
|
||
更新所有監控商品的圖片
|
||
|
||
Args:
|
||
batch_size: 每批處理的商品數量
|
||
delay: 每個請求之間的延遲(秒)
|
||
start_from: 從第幾個商品開始處理(用於斷點續傳)
|
||
"""
|
||
print(f"🔄 開始更新所有監控商品的圖片(從第 {start_from + 1} 個開始)...\n")
|
||
|
||
if not os.path.exists(DB_PATH):
|
||
print(f"❌ 資料庫檔案不存在: {DB_PATH}")
|
||
return
|
||
|
||
try:
|
||
engine = create_engine(f"sqlite:///{DB_PATH}")
|
||
Session = sessionmaker(bind=engine)
|
||
session = Session()
|
||
|
||
# 查詢所有商品的總數
|
||
total_count = session.query(Product).count()
|
||
print(f"📊 資料庫中共有 {total_count} 個監控商品")
|
||
print(f"📦 本次將處理 {min(batch_size, total_count - start_from)} 個商品\n")
|
||
|
||
# 查詢指定範圍的商品
|
||
products = session.query(Product).offset(start_from).limit(batch_size).all()
|
||
|
||
if not products:
|
||
print("✅ 沒有需要處理的商品!")
|
||
return
|
||
|
||
success_count = 0
|
||
fail_count = 0
|
||
skip_count = 0
|
||
|
||
for idx, product in enumerate(products, 1):
|
||
global_idx = start_from + idx
|
||
print(f"[{global_idx}/{total_count}] 處理: [{product.i_code}] {product.name[:50]}...")
|
||
|
||
try:
|
||
# 從商品詳情頁獲取圖片 URL
|
||
image_url = get_product_image_url(product.i_code)
|
||
|
||
if image_url:
|
||
# 檢查圖片是否有變化
|
||
if product.image_url != image_url:
|
||
product.image_url = image_url
|
||
session.commit()
|
||
print(f" ✅ 更新: {image_url}")
|
||
success_count += 1
|
||
else:
|
||
print(f" ⏭️ 相同: 圖片已是最新")
|
||
skip_count += 1
|
||
else:
|
||
print(f" ⚠️ 無法獲取圖片(可能已下架或不存在)")
|
||
fail_count += 1
|
||
|
||
# 延遲,避免請求過快
|
||
time.sleep(delay)
|
||
|
||
except Exception as e:
|
||
print(f" ❌ 錯誤: {e}")
|
||
fail_count += 1
|
||
session.rollback()
|
||
|
||
session.close()
|
||
|
||
print("\n" + "=" * 60)
|
||
print("📊 本批更新結果")
|
||
print("=" * 60)
|
||
total = len(products)
|
||
print(f"✅ 成功更新: {success_count}/{total}")
|
||
print(f"⏭️ 跳過(相同): {skip_count}/{total}")
|
||
print(f"❌ 失敗: {fail_count}/{total}")
|
||
|
||
# 顯示進度
|
||
completed = start_from + len(products)
|
||
progress = completed / total_count * 100
|
||
remaining = total_count - completed
|
||
|
||
print(f"\n📈 總進度: {completed}/{total_count} ({progress:.1f}%)")
|
||
if remaining > 0:
|
||
print(f"⏳ 剩餘: {remaining} 個商品")
|
||
print(f"\n💡 提示: 繼續執行下一批,使用參數 --start-from {completed}")
|
||
else:
|
||
print(f"\n🎉 所有商品已處理完成!")
|
||
|
||
except Exception as e:
|
||
print(f"❌ 批次更新失敗: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
if __name__ == "__main__":
|
||
import argparse
|
||
|
||
parser = argparse.ArgumentParser(description='更新所有監控商品的圖片')
|
||
parser.add_argument('--batch-size', type=int, default=100, help='每批處理的商品數量(預設: 100)')
|
||
parser.add_argument('--delay', type=float, default=0.5, help='每個請求之間的延遲秒數(預設: 0.5)')
|
||
parser.add_argument('--start-from', type=int, default=0, help='從第幾個商品開始處理(預設: 0)')
|
||
|
||
args = parser.parse_args()
|
||
|
||
update_all_images(batch_size=args.batch_size, delay=args.delay, start_from=args.start_from)
|