feat: 安全继续失败采购核单 (#262)
This commit is contained in:
+119
-11
@@ -123,6 +123,12 @@ ERROR_FEEDBACK_BUTTON_MIN_HEIGHT = 40
|
||||
PURCHASE_RECONCILE_DELAY_MS = 5_000
|
||||
|
||||
|
||||
def _reject_reconcile_only_purchase_adapter(*_args):
|
||||
"""继续核单分支绝不允许创建采购 Adapter。"""
|
||||
|
||||
raise RuntimeError("只读核单禁止创建采购执行器")
|
||||
|
||||
|
||||
class ClaimTaskWorker(QObject):
|
||||
"""在固定后台线程中串行补交、领取或执行至多一条任务。"""
|
||||
|
||||
@@ -319,6 +325,14 @@ class ClaimTaskWorker(QObject):
|
||||
collect_service_factory=self._collect_service_factory,
|
||||
)
|
||||
result = service.execute_selected(task_id)
|
||||
elif task_id in command.reconcile_task_ids:
|
||||
result = self._execute_purchase_reconcile_retry(
|
||||
task_id,
|
||||
client,
|
||||
android_serial,
|
||||
current=index + 1,
|
||||
total=len(command.task_ids),
|
||||
)
|
||||
else:
|
||||
result = self._execute_purchase_rerun(
|
||||
task_id,
|
||||
@@ -329,6 +343,14 @@ class ClaimTaskWorker(QObject):
|
||||
)
|
||||
except (CollectRerunError, PurchaseRerunError, ValueError) as exc:
|
||||
self.outcome.emit("skipped", str(exc), task_id)
|
||||
if command.target_type is TaskType.PURCHASE:
|
||||
for remaining_id in command.task_ids[index + 1 :]:
|
||||
self.outcome.emit(
|
||||
"skipped",
|
||||
f"前一采购任务未形成成功闭环,任务 {remaining_id} 未开始",
|
||||
remaining_id,
|
||||
)
|
||||
break
|
||||
continue
|
||||
self.outcome.emit(result.kind, result.message, result.task_id or task_id)
|
||||
if command.target_type is TaskType.PURCHASE and result.kind != "succeeded":
|
||||
@@ -340,6 +362,48 @@ class ClaimTaskWorker(QObject):
|
||||
)
|
||||
break
|
||||
|
||||
def _execute_purchase_reconcile_retry(
|
||||
self,
|
||||
task_id: str,
|
||||
client: ClientInfo,
|
||||
android_serial: str,
|
||||
current: int = 1,
|
||||
total: int = 1,
|
||||
):
|
||||
"""恢复原不可逆运行并只读核单,绝不创建采购运行。"""
|
||||
|
||||
if self._purchase_reconcile_factory is None:
|
||||
raise ValueError("采购核单执行器未就绪;绝不重新下单")
|
||||
self._task_repository.prepare_purchase_reconcile_retry(task_id)
|
||||
self.taskStarted.emit(
|
||||
task_id,
|
||||
TaskType.PURCHASE.value,
|
||||
current,
|
||||
total,
|
||||
)
|
||||
result = PurchaseReconcileService(
|
||||
self._task_repository,
|
||||
android_serial,
|
||||
self._purchase_reconcile_factory,
|
||||
cancelled=lambda: False,
|
||||
).execute_selected(task_id)
|
||||
if result.kind != "result_pending":
|
||||
return result
|
||||
event = self._task_repository.outbox_for_resubmit(task_id)
|
||||
if event is None:
|
||||
return type(result)(
|
||||
"manual_review", "核单结果未写入待上报队列", task_id
|
||||
)
|
||||
submitter = PurchaseTaskService(
|
||||
self._gateway,
|
||||
self._task_repository,
|
||||
client,
|
||||
android_serial,
|
||||
_reject_reconcile_only_purchase_adapter,
|
||||
cancelled=lambda: False,
|
||||
)
|
||||
return submitter.submit_saved_event(event)
|
||||
|
||||
def _execute_purchase_rerun(
|
||||
self,
|
||||
task_id: str,
|
||||
@@ -768,30 +832,59 @@ class PDDTaskPageEvent(QObject):
|
||||
return
|
||||
|
||||
action = "采集" if target_type is TaskType.COLLECT else "采购"
|
||||
if not plan.eligible_task_ids:
|
||||
processable_task_ids = (
|
||||
plan.reconcile_task_ids + plan.eligible_task_ids
|
||||
)
|
||||
if not processable_task_ids:
|
||||
self._show_rerun_warning(
|
||||
f"没有可重新{action}的任务",
|
||||
f"已选 {plan.selected_count} 条,过滤 {len(plan.filtered_task_ids)} 条其他类型,"
|
||||
f"安全条件阻止 {len(plan.blocked)} 条。",
|
||||
)
|
||||
return
|
||||
reconcile_count = len(plan.reconcile_task_ids)
|
||||
purchase_count = len(plan.eligible_task_ids)
|
||||
if target_type is TaskType.PURCHASE:
|
||||
title = f"确认处理 {len(processable_task_ids)} 条采购任务?"
|
||||
detail = (
|
||||
f"已选 {plan.selected_count} 条;可重新采购 {purchase_count} 条;"
|
||||
f"只继续核单 {reconcile_count} 条;"
|
||||
f"过滤其他类型 {len(plan.filtered_task_ids)} 条;"
|
||||
f"安全条件阻止 {len(plan.blocked)} 条。\n\n"
|
||||
"已提交订单的任务只读取待付款订单,绝不重新下单;"
|
||||
"只读核单优先执行,结果不确定时立即停止剩余任务。"
|
||||
)
|
||||
confirm_text = (
|
||||
"继续核单"
|
||||
if reconcile_count and not purchase_count
|
||||
else "继续处理"
|
||||
)
|
||||
else:
|
||||
title = f"确认重新{action} {len(plan.eligible_task_ids)} 条任务?"
|
||||
detail = (
|
||||
f"已选 {plan.selected_count} 条;可执行 {len(plan.eligible_task_ids)} 条;"
|
||||
f"过滤其他类型 {len(plan.filtered_task_ids)} 条;"
|
||||
f"安全条件阻止 {len(plan.blocked)} 条。\n\n"
|
||||
"新结果会覆盖 Client 和 Admin 当前采集数据,旧执行记录仍保留。"
|
||||
)
|
||||
confirm_text = f"重新{action}"
|
||||
dialog = MessageBox(
|
||||
f"确认重新{action} {len(plan.eligible_task_ids)} 条任务?",
|
||||
f"已选 {plan.selected_count} 条;可执行 {len(plan.eligible_task_ids)} 条;"
|
||||
f"过滤其他类型 {len(plan.filtered_task_ids)} 条;安全条件阻止 {len(plan.blocked)} 条。\n\n"
|
||||
+ ("采购任务会逐条执行;核单不确定时立即停止剩余任务,绝不重新下单。"
|
||||
if target_type is TaskType.PURCHASE else
|
||||
"新结果会覆盖 Client 和 Admin 当前采集数据,旧执行记录仍保留。"),
|
||||
title,
|
||||
detail,
|
||||
self._page.window(),
|
||||
)
|
||||
dialog.yesButton.setText(f"重新{action}")
|
||||
dialog.yesButton.setText(confirm_text)
|
||||
dialog.cancelButton.setText("取消")
|
||||
dialog.cancelButton.setFocus()
|
||||
if not dialog.exec():
|
||||
return
|
||||
|
||||
self._start_rerun_worker(
|
||||
TaskRerunBatch(target_type, plan.eligible_task_ids)
|
||||
TaskRerunBatch(
|
||||
target_type,
|
||||
processable_task_ids,
|
||||
plan.reconcile_task_ids,
|
||||
)
|
||||
)
|
||||
|
||||
@pyqtSlot(object)
|
||||
@@ -1000,8 +1093,7 @@ class PDDTaskPageEvent(QObject):
|
||||
self._claim_busy = True
|
||||
self._page.set_rerun_running(True)
|
||||
self._page.set_engine_status(
|
||||
f"已加入队列 {len(command.task_ids)} 条,正在检查 Android 设备,等待开始重新"
|
||||
f"{'采集' if command.target_type is TaskType.COLLECT else '采购'}…"
|
||||
self._rerun_queue_status(command)
|
||||
)
|
||||
self._reload()
|
||||
|
||||
@@ -1010,6 +1102,22 @@ class PDDTaskPageEvent(QObject):
|
||||
worker.prepare_run()
|
||||
self._claimRunRequested.emit(command)
|
||||
|
||||
@staticmethod
|
||||
def _rerun_queue_status(command: TaskRerunBatch) -> str:
|
||||
total = len(command.task_ids)
|
||||
if command.target_type is TaskType.COLLECT:
|
||||
action = "重新采集"
|
||||
elif len(command.reconcile_task_ids) == total:
|
||||
action = "继续核单"
|
||||
elif command.reconcile_task_ids:
|
||||
action = "处理采购任务"
|
||||
else:
|
||||
action = "重新采购"
|
||||
return (
|
||||
f"已加入队列 {total} 条,正在检查 Android 设备,"
|
||||
f"等待开始{action}…"
|
||||
)
|
||||
|
||||
@pyqtSlot()
|
||||
def request_cancel_rerun(self) -> None:
|
||||
"""请求当前重新采集在下一个安全点停止。"""
|
||||
|
||||
Reference in New Issue
Block a user