1
0
forked from erp-dev/erp

feat: backfill external order image upgrade to beat schedule by 1 hour

This commit is contained in:
2026-03-31 18:10:24 +08:00
parent aff10b8925
commit 72840b7277
12 changed files with 1085 additions and 108 deletions

View File

@@ -1,8 +1,7 @@
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Q
from api_v1.external_product_image_backfill import run_external_product_image_backfill
from api_v1.tasks import _fetch_external_product_image, _upload_product_image
from printing.models import PrintingJob
class Command(BaseCommand):
@@ -18,114 +17,25 @@ class Command(BaseCommand):
help='仅输出将处理的产品,不实际请求外部 API 或写库',
)
@staticmethod
def _extract_request_name(job: PrintingJob) -> str:
raw = job.external_raw or {}
if isinstance(raw, dict):
request_name = str(raw.get('YanSe') or '').strip()
if request_name:
return request_name
return str(job.external_product_name or '').strip()
def handle(self, *args, **options):
queryset = (
PrintingJob.objects.filter(
printing_order__external_order_id__isnull=False,
)
.exclude(printing_order__external_order_id='')
.filter(Q(product__image__isnull=True) | Q(product__image=''))
.select_related('product', 'printing_order', 'product__merchant')
.order_by('-id')
)
merchant_id = options.get('merchant_id')
if merchant_id:
queryset = queryset.filter(product__merchant_id=merchant_id)
product_id = options.get('product_id')
if product_id:
queryset = queryset.filter(product_id=product_id)
limit = options.get('limit')
if limit is not None:
if int(limit) <= 0:
raise CommandError('--limit 必须大于 0')
limit = int(limit)
success_count = 0
failed_count = 0
skipped_with_image_count = 0
skipped_without_request_name_count = 0
skipped_duplicate_product_count = 0
selected_count = 0
scanned_count = 0
dry_run = bool(options.get('dry_run'))
seen_product_ids = set()
for job in queryset.iterator(chunk_size=1000):
scanned_count += 1
product = job.product
if product.image:
skipped_with_image_count += 1
continue
if product.id in seen_product_ids:
skipped_duplicate_product_count += 1
continue
request_name = self._extract_request_name(job)
if not request_name:
skipped_without_request_name_count += 1
continue
seen_product_ids.add(product.id)
selected_count += 1
if dry_run:
self.stdout.write(
'[DRY-RUN] '
f'job_id={job.id} '
f'product_id={product.id} '
f'merchant_id={product.merchant_id} '
f'product_name={product.name} '
f'request_name={request_name}'
)
else:
try:
image_payload = _fetch_external_product_image(request_name)
_upload_product_image(product, image_payload)
except Exception as exc:
failed_count += 1
self.stderr.write(
self.style.ERROR(
f'补图失败: job_id={job.id} product_id={product.id} merchant_id={product.merchant_id} '
f'product_name={product.name} request_name={request_name} error={exc}'
)
)
else:
success_count += 1
self.stdout.write(
self.style.SUCCESS(
f'补图成功: job_id={job.id} product_id={product.id} merchant_id={product.merchant_id} '
f'product_name={product.name} request_name={request_name}'
)
)
if limit is not None and selected_count >= limit:
break
if dry_run:
self.stdout.write(self.style.SUCCESS('dry-run 完成'))
self.stdout.write(
self.style.SUCCESS(
'完成: '
f'scanned={scanned_count} '
f'selected={selected_count} '
f'success={success_count} '
f'failed={failed_count} '
f'skipped_with_image={skipped_with_image_count} '
f'skipped_without_request_name={skipped_without_request_name_count} '
f'skipped_duplicate_product={skipped_duplicate_product_count}'
)
payload = run_external_product_image_backfill(
fetch_image=_fetch_external_product_image,
upload_image=_upload_product_image,
merchant_id=merchant_id,
product_id=product_id,
limit=limit,
dry_run=bool(options.get('dry_run')),
on_success=lambda message: self.stdout.write(self.style.SUCCESS(message)),
on_error=lambda message: self.stderr.write(self.style.ERROR(message)),
on_info=lambda message: self.stdout.write(message),
)
self.stdout.write(self.style.SUCCESS(f'完成: {payload}'))