forked from erp-dev/erp
137 lines
4.3 KiB
Python
137 lines
4.3 KiB
Python
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
|