fix: 允许等待重试任务手动重新执行 (#70)

This commit is contained in:
chengma
2026-08-09 22:47:44 +08:00
parent 5df5398f32
commit a6c573a76a
6 changed files with 151 additions and 12 deletions
+31 -1
View File
@@ -608,7 +608,7 @@ class PDDTaskPageEvent(QObject):
self._stop_after_current(content)
@pyqtSlot(str, str, str)
def _on_collect_outcome(self, kind: str, message: str, _task_id: str) -> None:
def _on_collect_outcome(self, kind: str, message: str, task_id: str) -> None:
if self._closing:
return
self._reload()
@@ -622,6 +622,13 @@ class PDDTaskPageEvent(QObject):
)
elif kind == "result_pending":
self._on_claim_retryable_failed(message)
elif kind == "failed" and self._is_retry_wait_task(task_id):
content = (
f"自动获取:已停止 · 任务 {task_id} 重试已暂停;"
"请选择该任务点击“重新执行”,或重新启动获取任务"
)
self._show_retry_paused(content)
self._stop_after_current(content)
elif kind in {"manual_review", "failed"}:
self._show_claim_error("采集任务需要处理", message)
self._stop_after_current(message)
@@ -632,6 +639,29 @@ class PDDTaskPageEvent(QObject):
self._show_claim_error("自动获取已停止", content)
self._stop_after_current(content)
def _is_retry_wait_task(self, task_id: str) -> bool:
"""判断失败结果对应的任务是否仍可在本地重试。"""
if not task_id:
return False
try:
task = self._repository.get_task(task_id)
except Exception:
return False
return task is not None and task.status is TaskStatus.RETRY_WAIT
def _show_retry_paused(self, content: str) -> None:
"""用持久警告说明任务不会自行倒计时重试。"""
InfoBar.warning(
title="重试已暂停",
content=content,
isClosable=True,
duration=-1,
position=InfoBarPosition.TOP_RIGHT,
parent=self._page,
)
def _show_claim_error(self, title: str, content: str) -> None:
"""显示不会自动消失的可恢复错误,同时保留底部状态文字。"""
+13 -9
View File
@@ -262,6 +262,7 @@ class TaskRepository:
if row["task_type"] != TaskType.COLLECT.value:
raise CollectRerunError("采购任务不能重新执行,以免重复下单")
allowed_statuses = {
TaskStatus.RETRY_WAIT.value,
TaskStatus.SUCCEEDED.value,
TaskStatus.FAILED.value,
TaskStatus.CANCELLED.value,
@@ -272,17 +273,20 @@ class TaskRepository:
TaskStatus.CLAIMED.value: "待执行",
TaskStatus.RUNNING.value: "执行中",
TaskStatus.RESULT_PENDING.value: "结果待提交",
TaskStatus.RETRY_WAIT.value: "等待重试",
}.get(row["status"], row["status"])
raise CollectRerunError(f"任务当前为“{status_name}”,不能重新采集")
unsent_count = int(
connection.execute(
"SELECT COUNT(*) FROM outbox_events"
" WHERE task_id = ? AND status != 'sent'",
(row["id"],),
).fetchone()[0]
)
if unsent_count:
unsent = connection.execute(
"SELECT status, last_error FROM outbox_events"
" WHERE task_id = ? AND status != 'sent'"
" ORDER BY id DESC LIMIT 1",
(row["id"],),
).fetchone()
if unsent is not None and unsent["status"] == OutboxStatus.FAILED.value:
reason = unsent["last_error"] or "Admin 未接收上次结果"
raise CollectRerunError(
f"任务上次结果提交失败:{reason};请先处理后再重新采集"
)
if unsent is not None:
raise CollectRerunError("任务仍有未发送的结果,请先完成提交")
def start_collect_run(
+59
View File
@@ -563,6 +563,65 @@ class PDDTaskPageEventTest(unittest.TestCase):
events.shutdown()
page.deleteLater()
def test_retry_wait_failure_shows_paused_recovery_actions(self):
self._add_task(1)
started = self.repository.start_collect_run("PDD-001", "USB-001")
event = self.repository.save_collect_failure(
"PDD-001",
started.attempt_id,
TaskStatus.RETRY_WAIT,
"DEVICE_OFFLINE",
"设备离线",
True,
)
self.repository.mark_outbox_sent(event.id)
page = PDDTaskPage()
events = PDDTaskPageEvent(
page,
self.repository,
claim_gateway=RecordingClaimGateway(None),
settings_repository=self._saved_settings(),
)
events._on_collect_outcome(
"failed",
"任务 PDD-001 采集未完成",
"PDD-001",
)
self.assertIn("重试已暂停", events._stop_status)
self.assertIn("重新执行", events._stop_status)
self.assertIn("重新启动获取任务", events._stop_status)
events.shutdown()
page.deleteLater()
def test_failed_task_does_not_show_retry_paused_status(self):
self._add_task(1)
started = self.repository.start_collect_run("PDD-001", "USB-001")
event = self.repository.save_collect_failure(
"PDD-001",
started.attempt_id,
TaskStatus.FAILED,
"GOODS_OFF_SHELF",
"商品已下架",
False,
)
self.repository.mark_outbox_sent(event.id)
page = PDDTaskPage()
events = PDDTaskPageEvent(
page,
self.repository,
claim_gateway=RecordingClaimGateway(None),
settings_repository=self._saved_settings(),
)
events._on_collect_outcome("failed", "商品已下架", "PDD-001")
self.assertEqual(events._stop_status, "商品已下架")
self.assertNotIn("重试已暂停", events._stop_status)
events.shutdown()
page.deleteLater()
def test_duplicate_task_is_normal_status(self):
task = collect_admin_task()
self.repository.add_claimed_task(admin_task_to_new_claimed_task(task))
+42
View File
@@ -267,6 +267,48 @@ class TaskRepositoryTests(unittest.TestCase):
with self.assertRaisesRegex(CollectRerunError, "未发送"):
self.repository.validate_collect_rerun("UNSENT-RERUN")
def test_retry_wait_can_rerun_only_after_failure_outbox_is_sent(self):
self.repository.add_claimed_task(self._task("RETRY-RERUN"))
first = self.repository.start_collect_run("RETRY-RERUN", "USB-001")
event = self.repository.save_collect_failure(
"RETRY-RERUN",
first.attempt_id,
TaskStatus.RETRY_WAIT,
"DEVICE_OFFLINE",
"设备离线",
True,
)
with self.assertRaisesRegex(CollectRerunError, "未发送"):
self.repository.validate_collect_rerun("RETRY-RERUN")
self.repository.mark_outbox_sent(event.id)
prepared = self.repository.prepare_collect_rerun("RETRY-RERUN")
second = self.repository.start_collect_run("RETRY-RERUN", "USB-001")
self.assertEqual(prepared.status, TaskStatus.CLAIMED)
self.assertEqual(second.attempt_no, 2)
def test_retry_wait_explains_permanently_failed_outbox(self):
self.repository.add_claimed_task(self._task("RETRY-REJECTED"))
started = self.repository.start_collect_run(
"RETRY-REJECTED", "USB-001"
)
event = self.repository.save_collect_failure(
"RETRY-REJECTED",
started.attempt_id,
TaskStatus.RETRY_WAIT,
"DEVICE_OFFLINE",
"设备离线",
True,
)
self.repository.mark_outbox_failed(event.id, "任务不存在")
with self.assertRaisesRegex(
CollectRerunError, "上次结果提交失败:任务不存在"
):
self.repository.validate_collect_rerun("RETRY-REJECTED")
def test_recovery_restores_sending_and_interrupted_running(self):
self.repository.add_claimed_task(self._task("TASK-RECOVER"))
started = self.repository.start_collect_run("TASK-RECOVER", "USB-001")