1
0
forked from erp-dev/erp
Files
erpnew/stateflow/management/commands/repair_business_object_bindings.py

125 lines
4.5 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.
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
from django.db import transaction
from printing import models as printing_models
from stateflow import services as stateflow_services
class Command(BaseCommand):
help = (
"修复 printing 相关模型PrintingJob/PlateOrder所关联的 BusinessObject 的 "
"content_type/object_id/name 绑定(用于历史数据回填与一致性修复)。"
)
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='只输出统计,不写入数据库',
)
parser.add_argument(
'--limit',
type=int,
default=None,
help='最多处理多少条记录(用于分批回填)',
)
parser.add_argument(
'--only',
choices=['printingjob', 'plateorder', 'all'],
default='all',
help='仅修复指定类型(默认 all',
)
def handle(self, *args, **options):
dry_run: bool = options['dry_run']
limit: int | None = options['limit']
only: str = options['only']
# 提前确保 ContentType 可用(也用于输出校验信息)
ct_job = ContentType.objects.get_for_model(printing_models.PrintingJob)
ct_po = ContentType.objects.get_for_model(printing_models.PlateOrder)
self.stdout.write(f"dry_run={dry_run} only={only} limit={limit}")
self.stdout.write(f"ContentType: PrintingJob={ct_job.id}, PlateOrder={ct_po.id}")
stats = {
'printingjob_total': 0,
'printingjob_updated': 0,
'plateorder_total': 0,
'plateorder_updated': 0,
}
def _iter_qs(qs):
if limit:
return qs.order_by('id')[:limit]
return qs.order_by('id')
# 用事务包住避免部分更新dry-run 仍使用事务,最后标记回滚)
with transaction.atomic():
if only in ('printingjob', 'all'):
qs = (
printing_models.PrintingJob.objects
.select_related('business_object')
.filter(business_object__isnull=False)
)
for job in _iter_qs(qs):
stats['printingjob_total'] += 1
bo = job.business_object
should_fix = (
bo.content_type_id != ct_job.id
or bo.object_id != job.id
or not (bo.name or '').strip()
)
if not should_fix:
continue
if dry_run:
stats['printingjob_updated'] += 1
continue
if stateflow_services.ensure_business_object_bound_to_instance(
bo,
job,
default_name=f"PrintingJob-{job.id}",
):
stats['printingjob_updated'] += 1
if only in ('plateorder', 'all'):
qs = (
printing_models.PlateOrder.objects
.select_related('business_object')
.filter(business_object__isnull=False)
)
for po in _iter_qs(qs):
stats['plateorder_total'] += 1
bo = po.business_object
should_fix = (
bo.content_type_id != ct_po.id
or bo.object_id != po.id
or not (bo.name or '').strip()
)
if not should_fix:
continue
if dry_run:
stats['plateorder_updated'] += 1
continue
if stateflow_services.ensure_business_object_bound_to_instance(
bo,
po,
default_name=f"PlateOrder-{po.id}",
):
stats['plateorder_updated'] += 1
if dry_run:
# dry-run显式回滚即便未来误加了写逻辑也不会落库
transaction.set_rollback(True)
self.stdout.write(self.style.SUCCESS("Done."))
for k in sorted(stats.keys()):
self.stdout.write(f"{k}={stats[k]}")