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

@@ -0,0 +1,136 @@
from collections.abc import Callable
from django.db.models import Q
from printing.models import PrintingJob
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 run_external_product_image_backfill(
*,
fetch_image: Callable[[str], dict],
upload_image: Callable[[object, dict], None],
merchant_id: int | None = None,
product_id: int | None = None,
limit: int | None = None,
dry_run: bool = False,
on_success: Callable[[str], None] | None = None,
on_error: Callable[[str], None] | None = None,
on_info: Callable[[str], None] | None = None,
) -> dict:
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")
)
if merchant_id:
queryset = queryset.filter(product__merchant_id=merchant_id)
if product_id:
queryset = queryset.filter(product_id=product_id)
if limit is not None:
limit = int(limit)
if limit <= 0:
raise ValueError("limit must be greater than 0")
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
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 = 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:
if on_info:
on_info(
"[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_image(request_name)
upload_image(product, image_payload)
except Exception as exc:
failed_count += 1
if on_error:
on_error(
"补图失败: "
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} "
f"error={exc}"
)
else:
success_count += 1
if on_success:
on_success(
"补图成功: "
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}"
)
if limit is not None and selected_count >= limit:
break
payload = {
"scanned": scanned_count,
"selected": selected_count,
"success": success_count,
"failed": failed_count,
"skipped_with_image": skipped_with_image_count,
"skipped_without_request_name": skipped_without_request_name_count,
"skipped_duplicate_product": skipped_duplicate_product_count,
"merchant_id": merchant_id,
"product_id": product_id,
"limit": limit,
"dry_run": dry_run,
}
if dry_run and on_info:
on_info("dry-run 完成")
return payload