feat(apply): block abnormal product statuses

This commit is contained in:
chengma
2026-07-18 17:10:12 +08:00
parent 3a92119187
commit d64e3c7404
9 changed files with 499 additions and 51 deletions
+96 -21
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
from ... import product_status
from ..models import ApplyTaskTableModel
from ..widgets import *
from ..workers import ApplyWorker as _RealApplyWorker, WriteBackWorker as _RealWriteBackWorker
@@ -244,21 +245,23 @@ class ApplyTab(QWidget):
if self.apply_thread is not None:
self._set_status("更新正在进行...")
return
tasks = [
task for task in self.model.tasks
if self._is_update_candidate_task(task)
]
if not tasks:
base_candidates = self._update_candidates()
if not base_candidates:
self._set_status("当前筛选结果没有可更新任务")
return
update_cfg = self._shopee_update_config()
update_mode = self._current_update_mode()
content_plan = self._partition_update_content_tasks(tasks, update_mode)
executable_tasks = content_plan["executable"]
update_plan = product_status.build_apply_plan(base_candidates, update_mode)
executable_tasks = update_plan["executable"]
if not executable_tasks:
content_error = self._update_content_error(content_plan, update_mode)
QMessageBox.warning(self, "更新内容未生成", content_error)
self._set_status(content_error.replace("\n", " "))
preflight_error = self._update_preflight_error(update_plan, update_mode)
title = (
"商品状态不允许更新"
if update_plan["status_scope_excluded"]
else "更新内容未生成"
)
QMessageBox.warning(self, title, preflight_error)
self._set_status(preflight_error.replace("\n", " "))
return
dry_run = bool(dry_run)
answer = QMessageBox.question(
@@ -267,7 +270,7 @@ class ApplyTab(QWidget):
self._confirmation_message(
executable_tasks,
dry_run=dry_run,
content_plan=content_plan,
content_plan=update_plan,
),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
@@ -275,6 +278,18 @@ class ApplyTab(QWidget):
if answer != QMessageBox.Yes:
self._set_status("已取消检查本轮更新" if dry_run else "已取消开始更新")
return
current_plan = product_status.build_apply_plan(
self._update_candidates(),
update_mode,
)
if current_plan["fingerprint"] != update_plan["fingerprint"]:
self._set_status("当前任务数据已变化,请重新开始更新")
return
executable_tasks = current_plan["executable"]
if not executable_tasks:
preflight_error = self._update_preflight_error(current_plan, update_mode)
self._set_status(preflight_error.replace("\n", " "))
return
batch_size = max(1, int(update_cfg.get("max_items_per_run", 1) or 1))
worker = ApplyWorker(
executable_tasks,
@@ -288,6 +303,10 @@ class ApplyTab(QWidget):
),
batch_size=batch_size,
diagnostic_log_dir=diagnostics.DEFAULT_LOG_DIR,
product_status_counts=current_plan["status_counts"],
status_scope_excluded=current_plan["status_scope_excluded"],
content_scope_excluded=current_plan["content_scope_excluded"],
apply_plan_fingerprint=current_plan["fingerprint"],
)
worker.progress.connect(self._on_apply_progress)
worker.row_updated.connect(self._on_apply_row_updated)
@@ -304,9 +323,9 @@ class ApplyTab(QWidget):
executable_tasks,
dry_run=dry_run,
batch_size=batch_size,
content_plan=content_plan,
content_plan=current_plan,
)
skip_status = self._content_skip_status(content_plan)
skip_status = self._content_skip_status(current_plan)
if dry_run:
self._set_status(f"开始检查本轮更新:{len(executable_tasks)} 条{skip_status}")
else:
@@ -407,6 +426,13 @@ class ApplyTab(QWidget):
and getattr(task, "status", None) in {"success", "pending", "failed"}
)
def _update_candidates(self):
return [
task
for task in self.model.tasks
if self._is_update_candidate_task(task)
]
def _populate_batch_filter(self, batches, selected_batch):
had_previous_items = self.batch_filter.count() > 0
self.batch_filter.blockSignals(True)
@@ -617,6 +643,22 @@ class ApplyTab(QWidget):
"missing_cover": missing_cover,
}
def _update_preflight_error(self, update_plan, update_mode):
if update_plan["executable"]:
return None
lines = []
status_text = self._status_skip_text(update_plan)
if status_text:
lines.append("当前筛选结果没有状态正常且可更新的商品。")
lines.append(status_text)
lines.append("请先回到①导入采集重新确认商品状态。")
content_error = self._update_content_error(update_plan, update_mode)
if content_error:
if lines:
lines.append("")
lines.append(content_error)
return "\n".join(lines) or "当前筛选结果没有可更新任务。"
def _update_content_error(self, tasks_or_plan, update_mode):
if isinstance(tasks_or_plan, dict):
content_plan = tasks_or_plan
@@ -643,12 +685,13 @@ class ApplyTab(QWidget):
def _content_skip_text(self, content_plan):
if not content_plan:
return ""
status_text = self._status_skip_text(content_plan)
missing_title = content_plan.get("missing_title", [])
missing_cover = content_plan.get("missing_cover", [])
skipped_count = len({id(task) for task in missing_title + missing_cover})
if not skipped_count:
return ""
lines = [f"本轮将跳过 {skipped_count} 条缺少所选更新内容的记录:"]
lines = [status_text] if status_text else []
if skipped_count:
lines.append(f"本轮将跳过 {skipped_count} 条缺少所选更新内容的记录:")
if missing_title:
lines.append(
f"缺少新标题:{len(missing_title)} 条(示例商品ID:{self._sample_item_ids(missing_title)})"
@@ -662,16 +705,43 @@ class ApplyTab(QWidget):
def _content_skip_status(self, content_plan):
if not content_plan:
return ""
status_skipped = int(content_plan.get("status_scope_excluded", 0) or 0)
missing_title = content_plan.get("missing_title", [])
missing_cover = content_plan.get("missing_cover", [])
skipped_count = len({id(task) for task in missing_title + missing_cover})
if not skipped_count:
if not skipped_count and not status_skipped:
return ""
return (
f",跳过 {skipped_count} 条(缺标题 {len(missing_title)} / "
f"缺封面 {len(missing_cover)})"
parts = []
if status_skipped:
parts.append(f"状态异常 {status_skipped}")
if skipped_count:
parts.append(
f"缺标题 {len(missing_title)} / 缺封面 {len(missing_cover)}"
)
return ",跳过 {total} 条({details})".format(
total=status_skipped + skipped_count,
details=";".join(parts),
)
def _status_skip_text(self, update_plan):
if not update_plan:
return ""
rows = [
("未上架", update_plan.get("unlisted", [])),
("审核中", update_plan.get("reviewing", [])),
("状态未知", update_plan.get("unknown", [])),
]
skipped = sum(len(tasks) for _label, tasks in rows)
if not skipped:
return ""
lines = [f"本轮将排除 {skipped} 条商品状态异常记录:"]
for label, tasks in rows:
if tasks:
lines.append(
f"{label}:{len(tasks)} 条(示例商品ID:{self._sample_item_ids(tasks)})"
)
return "\n".join(lines)
def _sample_item_ids(self, tasks):
values = [
str(getattr(task, "item_id", "") or "").strip()
@@ -784,8 +854,13 @@ class ApplyTab(QWidget):
)
skip_status = self._content_skip_status(content_plan)
if skip_status:
excluded_label = (
"预检排除记录"
if content_plan and content_plan.get("status_scope_excluded")
else "缺失记录"
)
self._append_run_log(
f"本轮预检:可执行 {len(tasks)} 条{skip_status};缺失记录未进入本轮执行。"
f"本轮预检:可执行 {len(tasks)} 条{skip_status};{excluded_label}未进入本轮执行。"
)
def _load_latest_run_log(self):