Files
track-LICA/backend/scripts/fix_product_status.py
duxingchen 3286a11bc7 chore: fork from IRIS track 供 LICA 部门独立运行
- 复制来源: /home/yueli/track @ 192c8ee (feature/ai-audit-update)
- 组织隔离目标: LICA
- 端口规划: 前端 8030 / 后端 8031 / 数据库 8032
- 已排除 deploy.sh、deploy_full.sh、docker-compose.prod.yml(IRIS 生产发布脚本)
- 已排除工作区未提交改动,取干净的 192c8ee 状态
2026-09-21 15:56:52 +08:00

85 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""一次性数据清洗 — 修复 Product.status 与 overall_status 脱节的历史存量
背景
----
app/core/lifecycle.py 记录了历史缺陷:早期 receive_task / transfer_task 只改
overall_status、不改 status,导致部分设备的 status 残留为 'pending' / 'OUTBOUND'
等旧值。
现状(重要)
----------
所有 overall_status 的写入点**都已补上 sync_product_status()**:
task_service.py:387 / :691 / :1007、product_service.py:512、
webhooks.py:84 / :256、product_finalize_service.py:107
因此新数据不会再脱节,本脚本只处理历史存量。
影响面
------
前端列表筛选走的是 macro_status(由 overall_status 实时派生,见
product_service._resolve_macro_status),所以这批脏数据**基本不影响页面展示**。
但 _mark_after_sales_if_reactivated 等逻辑会读 product.status 判断"是否出库回流",
脏值可能引发误判 —— 故仍需清洗。
用法
----
python -m scripts.fix_product_status # 干跑:只打印待修复清单
python -m scripts.fix_product_status --apply # 确认后真正写库
"""
from __future__ import annotations
import argparse
import asyncio
from sqlalchemy import select
from app.core.database import AsyncSessionLocal
from app.core.lifecycle import overall_to_product_status
from app.models.product import Product
async def main(apply: bool) -> None:
async with AsyncSessionLocal() as db:
products = (
await db.execute(select(Product).order_by(Product.serial_number))
).scalars().all()
fixes: list[tuple[Product, str, str]] = []
for p in products:
expected = overall_to_product_status(p.overall_status)
current = (p.status or "").strip()
# 大小写不敏感比较:历史数据里存在小写 'pending'
if current.upper() == expected:
continue
fixes.append((p, current or "(空)", expected))
print(f"扫描 {len(products)} 台设备,需修复 {len(fixes)} 台\n")
if fixes:
header = f"{'序列号':<18}{'overall_status':<16}{'当前 status':<14}→ 目标 status"
print(header)
print("-" * len(header) * 2)
for p, cur, exp in fixes:
overall = p.overall_status or "(NULL)"
phase = p.lifecycle_phase or "-"
print(f"{p.serial_number:<18}{overall:<16}{cur:<14}→ {exp} [{phase}]")
else:
print("所有设备的 status 均已与 overall_status 对齐。")
if not apply:
print("\n[干跑] 未写库。确认清单无误后,加 --apply 执行。")
return
if not fixes:
return
for p, _cur, exp in fixes:
p.status = exp
await db.commit()
print(f"\n[已提交] {len(fixes)} 台设备的 status 已对齐到 overall_status。")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="对齐 Product.status 与 overall_status 的历史脏数据")
parser.add_argument("--apply", action="store_true", help="真正写库(默认仅干跑)")
asyncio.run(main(parser.parse_args().apply))