"""清理多余产品分类脚本。 将数据库中多余的分类迁移到工业品(1)或日用品(2),然后软删除多余分类。 运行方式: python -m backend.scripts.cleanup_categories """ from pathlib import Path import sys ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from sqlalchemy import select, func from backend.app.db import SessionLocal from backend.app.models.business import Product, ProductCategory # 保留的分类 ID KEEP_IDS = {1, 2} # 工业关键词:产品名包含这些词的归入工业品 INDUSTRY_KEYWORDS = ["布", "带", "胶", "板", "管", "钢", "铁", "铝", "铜", "塑", "膜", "网", "绳", "线", "丝", "棉", "纱"] def classify_product(product_name: str) -> int: """根据产品名判断归入工业品(1)还是日用品(2)""" for kw in INDUSTRY_KEYWORDS: if kw in product_name: return 1 return 2 def main(): session = SessionLocal() try: # ========== Step 1: 查询当前状态 ========== print("=" * 60) print("产品分类清理脚本") print("=" * 60) categories = session.execute( select(ProductCategory).where(ProductCategory.deleted == 0).order_by(ProductCategory.id) ).scalars().all() print(f"\n当前分类列表(共 {len(categories)} 个):") print("-" * 50) extra_categories = [] for cat in categories: product_count = session.execute( select(func.count(Product.id)).where( Product.category_id == cat.id, Product.deleted == 0, ) ).scalar() or 0 tag = "✓ 保留" if cat.id in KEEP_IDS else "✗ 待删除" print(f" ID={cat.id} {cat.category_name:<12} ({cat.category_code}) 产品数: {product_count} [{tag}]") if cat.id not in KEEP_IDS: extra_categories.append((cat, product_count)) if not extra_categories: print("\n没有需要清理的多余分类。") return # 统计需要迁移的产品 total_products_to_migrate = sum(count for _, count in extra_categories) print(f"\n需要迁移的产品总数: {total_products_to_migrate}") print(f"需要删除的分类数: {len(extra_categories)}") # 显示迁移计划 print("\n迁移计划:") print("-" * 50) for cat, count in extra_categories: products = session.execute( select(Product).where( Product.category_id == cat.id, Product.deleted == 0, ) ).scalars().all() industry_count = sum(1 for p in products if classify_product(p.product_name) == 1) daily_count = count - industry_count print(f" 分类「{cat.category_name}」({count}个产品):") print(f" → 工业品: {industry_count} 个") print(f" → 日用品: {daily_count} 个") for p in products: target = "工业品" if classify_product(p.product_name) == 1 else "日用品" print(f" - {p.product_name} ({p.specification}) → {target}") # ========== Step 2: 确认 ========== print("\n" + "=" * 60) confirm = input("确认执行清理?(输入 yes 继续): ").strip() if confirm.lower() != "yes": print("已取消。") return # ========== Step 3: 执行迁移 ========== print("\n执行迁移...") migrated = 0 for cat, count in extra_categories: products = session.execute( select(Product).where( Product.category_id == cat.id, Product.deleted == 0, ) ).scalars().all() for p in products: target_id = classify_product(p.product_name) target_name = "工业品" if target_id == 1 else "日用品" p.category_id = target_id p.category = target_name migrated += 1 print(f" 迁移: {p.product_name} → {target_name}") # ========== Step 4: 软删除多余分类 ========== print("\n删除多余分类...") deleted = 0 for cat, _ in extra_categories: cat.deleted = 1 deleted += 1 print(f" 删除: {cat.category_name}") session.commit() # ========== Step 5: 验证 ========== print("\n" + "=" * 60) print("清理完成!验证结果:") remaining = session.execute( select(ProductCategory).where(ProductCategory.deleted == 0).order_by(ProductCategory.id) ).scalars().all() print(f"剩余分类数: {len(remaining)}") for cat in remaining: product_count = session.execute( select(func.count(Product.id)).where( Product.category_id == cat.id, Product.deleted == 0, ) ).scalar() or 0 print(f" ID={cat.id} {cat.category_name} 产品数: {product_count}") print(f"\n共迁移 {migrated} 个产品,删除 {deleted} 个分类。") finally: session.close() if __name__ == "__main__": main()