@@ -20,6 +20,7 @@ from .task_models import (
|
||||
StartedTaskRun,
|
||||
TaskDetail,
|
||||
TaskFilters,
|
||||
TaskRerunPlan,
|
||||
TaskStatus,
|
||||
TaskSummary,
|
||||
TaskType,
|
||||
@@ -39,6 +40,10 @@ class CollectRerunError(ValueError):
|
||||
"""当前任务不满足重新采集条件。"""
|
||||
|
||||
|
||||
class PurchaseRerunError(ValueError):
|
||||
"""当前任务不满足安全重新采购条件。"""
|
||||
|
||||
|
||||
class TaskRemovalError(ValueError):
|
||||
"""勾选任务不满足从普通列表移除的安全条件。"""
|
||||
|
||||
@@ -472,6 +477,42 @@ class TaskRepository:
|
||||
connection.close()
|
||||
return self._to_detail(row)
|
||||
|
||||
def plan_rerun_batch(
|
||||
self, remote_task_ids: Iterable[str], target_type: TaskType
|
||||
) -> TaskRerunPlan:
|
||||
"""批量预检并分类:可执行、类型不符、被安全条件阻止。"""
|
||||
|
||||
stable_ids = tuple(dict.fromkeys(str(value) for value in remote_task_ids if value))
|
||||
eligible: List[str] = []
|
||||
filtered: List[str] = []
|
||||
blocked: List[Tuple[str, str]] = []
|
||||
connection = open_database(self._db_path)
|
||||
try:
|
||||
for task_id in stable_ids:
|
||||
row = connection.execute(
|
||||
"SELECT * FROM pdd_tasks WHERE remote_task_id = ?", (task_id,)
|
||||
).fetchone()
|
||||
if row is None:
|
||||
blocked.append((task_id, "任务不存在"))
|
||||
continue
|
||||
if row["task_type"] != target_type.value:
|
||||
filtered.append(task_id)
|
||||
continue
|
||||
try:
|
||||
if target_type is TaskType.COLLECT:
|
||||
self._check_collect_rerun(connection, row)
|
||||
else:
|
||||
self._check_purchase_rerun(connection, row)
|
||||
except (CollectRerunError, PurchaseRerunError) as exc:
|
||||
blocked.append((task_id, str(exc)))
|
||||
else:
|
||||
eligible.append(task_id)
|
||||
finally:
|
||||
connection.close()
|
||||
return TaskRerunPlan(
|
||||
target_type, len(stable_ids), tuple(eligible), tuple(filtered), tuple(blocked)
|
||||
)
|
||||
|
||||
def prepare_collect_rerun(self, remote_task_id: str) -> TaskDetail:
|
||||
"""事务内把已结束的采集任务恢复为待执行,保留旧结果。"""
|
||||
|
||||
@@ -508,6 +549,7 @@ class TaskRepository:
|
||||
if row["task_type"] != TaskType.COLLECT.value:
|
||||
raise CollectRerunError("采购任务不能重新执行,以免重复下单")
|
||||
allowed_statuses = {
|
||||
TaskStatus.CLAIMED.value,
|
||||
TaskStatus.RETRY_WAIT.value,
|
||||
TaskStatus.SUCCEEDED.value,
|
||||
TaskStatus.FAILED.value,
|
||||
@@ -547,6 +589,77 @@ class TaskRepository:
|
||||
"任务仍有未发送的结果,请先勾选任务点击“重新上报”"
|
||||
)
|
||||
|
||||
def validate_purchase_rerun(self, remote_task_id: str) -> TaskDetail:
|
||||
"""校验采购任务能否重新采购;历史上已下单的任务永久拒绝。"""
|
||||
|
||||
connection = open_database(self._db_path)
|
||||
try:
|
||||
row = connection.execute(
|
||||
"SELECT * FROM pdd_tasks WHERE remote_task_id = ?", (remote_task_id,)
|
||||
).fetchone()
|
||||
if row is None:
|
||||
raise PurchaseRerunError(f"任务 {remote_task_id} 不存在")
|
||||
self._check_purchase_rerun(connection, row)
|
||||
finally:
|
||||
connection.close()
|
||||
return self._to_detail(row)
|
||||
|
||||
def prepare_purchase_rerun(self, remote_task_id: str) -> TaskDetail:
|
||||
"""事务内恢复安全采购任务;不会清除任何历史执行记录。"""
|
||||
|
||||
now = utc_now_iso()
|
||||
connection = open_database(self._db_path)
|
||||
try:
|
||||
with connection:
|
||||
row = connection.execute(
|
||||
"SELECT * FROM pdd_tasks WHERE remote_task_id = ?", (remote_task_id,)
|
||||
).fetchone()
|
||||
if row is None:
|
||||
raise PurchaseRerunError(f"任务 {remote_task_id} 不存在")
|
||||
self._check_purchase_rerun(connection, row)
|
||||
connection.execute(
|
||||
"UPDATE pdd_tasks SET status = 'claimed',"
|
||||
" current_step = 'purchase_rerun_requested', finished_at = NULL,"
|
||||
" last_error_code = NULL, last_error_message = NULL, updated_at = ?"
|
||||
" WHERE id = ?",
|
||||
(now, row["id"]),
|
||||
)
|
||||
finally:
|
||||
connection.close()
|
||||
task = self.get_task(remote_task_id)
|
||||
assert task is not None
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def _check_purchase_rerun(
|
||||
connection: sqlite3.Connection, row: sqlite3.Row
|
||||
) -> None:
|
||||
if row["task_type"] != TaskType.PURCHASE.value:
|
||||
raise PurchaseRerunError("采集任务不能重新采购")
|
||||
irreversible = connection.execute(
|
||||
"SELECT 1 FROM task_runs WHERE task_id = ?"
|
||||
" AND irreversible_action_at IS NOT NULL LIMIT 1",
|
||||
(row["id"],),
|
||||
).fetchone()
|
||||
if irreversible is not None:
|
||||
raise PurchaseRerunError("任务历史上已进入下单阶段,只准核对订单,绝不重新下单")
|
||||
allowed = {
|
||||
TaskStatus.CLAIMED.value,
|
||||
TaskStatus.RETRY_WAIT.value,
|
||||
TaskStatus.SUCCEEDED.value,
|
||||
TaskStatus.FAILED.value,
|
||||
TaskStatus.CANCELLED.value,
|
||||
TaskStatus.MANUAL_REVIEW.value,
|
||||
}
|
||||
if row["status"] not in allowed:
|
||||
raise PurchaseRerunError(f"任务当前状态 {row['status']} 不能重新采购")
|
||||
unsent = connection.execute(
|
||||
"SELECT 1 FROM outbox_events WHERE task_id = ? AND status != 'sent' LIMIT 1",
|
||||
(row["id"],),
|
||||
).fetchone()
|
||||
if unsent is not None:
|
||||
raise PurchaseRerunError("任务仍有未发送数据,请先点击“重新上报”")
|
||||
|
||||
def start_collect_run(
|
||||
self, remote_task_id: str, device_address: str
|
||||
) -> StartedTaskRun:
|
||||
|
||||
Reference in New Issue
Block a user