#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 商品圖片 URL 批次更新工具 功能:將所有商品的圖片 URL 更新為 MOMO CDN 標準格式 格式:https://m.momoshop.com.tw/moscdn/goods/{i_code}_m.webp """ from database.manager import DatabaseManager from database.models import Product from logger_manager import SystemLogger from datetime import datetime log = SystemLogger("ImageURLUpdater").get_logger() def get_image_path(i_code): """ 將 i_code 轉換為圖片路徑 規則:從右往左取 3位/3位/剩餘,第一部分補0到4位 例如:12092813 → 813/092/12 → 0012/092/813 """ i_code_str = str(i_code) # 從右往左切分 part3 = i_code_str[-3:] if len(i_code_str) >= 3 else i_code_str.zfill(3) # 最後3位 part2 = i_code_str[-6:-3] if len(i_code_str) > 3 else '000' # 中間3位 part1 = i_code_str[:-6] if len(i_code_str) > 6 else '0' # 前面剩餘 part1 = part1.zfill(4) # 第一部分補0到4位 part2 = part2.zfill(3) # 第二部分補0到3位 (如果需要) return f'{part1}/{part2}/{part3}' def update_all_product_images(): """批次更新所有商品的圖片 URL""" db = DatabaseManager() session = db.get_session() try: log.info("=" * 80) log.info("🖼️ 開始批次更新商品圖片 URL") log.info("=" * 80) # 查詢所有商品 all_products = session.query(Product).all() total_count = len(all_products) log.info(f"📊 總商品數: {total_count}") # 統計變數 updated_count = 0 no_change_count = 0 error_count = 0 log.info("\n🔄 開始處理商品...") print() # 空行,讓輸出更清晰 for idx, product in enumerate(all_products, 1): try: # 構造標準圖片 URL (使用新格式) # https://img.momoshop.com.tw/goodsimg/0012/092/813/12092813_OL_m.webp path = get_image_path(product.i_code) new_image_url = f"https://img.momoshop.com.tw/goodsimg/{path}/{product.i_code}_OL_m.webp" # 判斷是否需要更新 needs_update = False if product.image_url is None: # 情況 1: 沒有圖片 URL needs_update = True reason = "無圖片 URL" elif product.image_url != new_image_url: # 情況 2: 圖片 URL 格式不正確 needs_update = True reason = "格式需更新" if needs_update: old_url = product.image_url if product.image_url else "無" product.image_url = new_image_url product.updated_at = datetime.now() updated_count += 1 if updated_count % 100 == 0: log.info(f" ✅ 已更新 {updated_count}/{total_count} 件商品...") # 詳細記錄(每 500 件顯示一次詳情) if updated_count % 500 == 1 or updated_count <= 5: log.debug(f" [{idx}/{total_count}] {reason}") log.debug(f" 商品: {product.name[:40]}...") log.debug(f" i_code: {product.i_code}") log.debug(f" 舊 URL: {old_url[:60]}..." if len(str(old_url)) > 60 else f" 舊 URL: {old_url}") log.debug(f" 新 URL: {new_image_url}") else: no_change_count += 1 except Exception as e: error_count += 1 log.error(f" ❌ 處理失敗 | i_code: {product.i_code} | 商品: {product.name[:30]} | Error: {e}") # 提交變更 log.info("\n💾 提交資料庫變更...") session.commit() # 輸出統計結果 log.info("\n" + "=" * 80) log.info("📊 處理結果統計") log.info("=" * 80) log.info(f" 總商品數: {total_count}") log.info(f" ✅ 已更新: {updated_count} ({updated_count/total_count*100:.1f}%)") log.info(f" ⏭️ 無需更新: {no_change_count} ({no_change_count/total_count*100:.1f}%)") log.info(f" ❌ 處理失敗: {error_count}") log.info("=" * 80) if updated_count > 0: log.info(f"✅ 成功更新 {updated_count} 件商品的圖片 URL") else: log.info("✅ 所有商品圖片 URL 已是最新格式,無需更新") return { 'total': total_count, 'updated': updated_count, 'no_change': no_change_count, 'error': error_count } except Exception as e: session.rollback() log.error(f"❌ 批次更新失敗: {e}") raise finally: session.close() if __name__ == "__main__": try: result = update_all_product_images() # 驗證結果 print("\n" + "=" * 80) print("🔍 驗證更新結果...") print("=" * 80) db = DatabaseManager() session = db.get_session() try: # 檢查還有多少商品沒有圖片 URL products_without_image = session.query(Product).filter(Product.image_url.is_(None)).count() # 檢查使用標準格式的商品數 standard_pattern = "https://m.momoshop.com.tw/moscdn/goods/%" products_with_standard_url = session.query(Product).filter( Product.image_url.like(standard_pattern) ).count() total_products = session.query(Product).count() print(f"無圖片 URL 商品: {products_without_image} ({products_without_image/total_products*100:.1f}%)") print(f"標準格式商品: {products_with_standard_url} ({products_with_standard_url/total_products*100:.1f}%)") print("=" * 80) if products_without_image == 0: print("✅ 驗證成功:所有商品都有圖片 URL") else: print(f"⚠️ 仍有 {products_without_image} 件商品沒有圖片 URL") finally: session.close() except KeyboardInterrupt: print("\n⚠️ 使用者中斷執行") except Exception as e: print(f"\n❌ 執行失敗: {e}") raise