feat: 安全领取并分派采购任务 (#72)
This commit is contained in:
+48
-61
@@ -34,7 +34,6 @@ from qfluentwidgets import InfoBar, InfoBarPosition, MessageBox
|
||||
|
||||
from .admin_gateway import (
|
||||
AdminGatewayError,
|
||||
AdminTask,
|
||||
ClientInfo,
|
||||
AdminGateway,
|
||||
)
|
||||
@@ -42,16 +41,20 @@ from .collect_task_service import CollectServiceFactory, CollectTaskService
|
||||
from .current_client_service import CurrentClientService
|
||||
from .http_admin_gateway import DEFAULT_ADMIN_BASE_URL, HttpAdminGateway
|
||||
from .pdd_ui import PDDTaskPage, TaskRow
|
||||
from .purchase_task_service import PurchaseAdapterFactory
|
||||
from .selected_android_device_service import SelectedAndroidDeviceService
|
||||
from .settings_repository import SettingsRepository
|
||||
from .task_models import (
|
||||
NewClaimedTask,
|
||||
TaskFilters,
|
||||
TaskStatus,
|
||||
TaskSummary,
|
||||
TaskType,
|
||||
)
|
||||
from .task_repository import CollectRerunError, TaskRepository
|
||||
from .task_dispatcher import (
|
||||
TaskDispatcher,
|
||||
admin_task_to_new_claimed_task,
|
||||
)
|
||||
from .task_detail_view import TaskDetailWindow
|
||||
|
||||
|
||||
@@ -88,42 +91,8 @@ TASK_STATUS_TEXT = {
|
||||
}
|
||||
|
||||
|
||||
def admin_task_to_new_claimed_task(task: AdminTask) -> NewClaimedTask:
|
||||
"""把 Admin 任务显式映射为本地任务,避免字段名自动展开出错。"""
|
||||
|
||||
if task.task_type is not TaskType.COLLECT:
|
||||
raise ValueError(f"任务 {task.task_id} 不是采集任务")
|
||||
|
||||
payload = dict(task.payload)
|
||||
goods_url = payload.get("goods_url")
|
||||
goods_id = payload.get("goods_id")
|
||||
if not isinstance(goods_url, str) or not goods_url.strip():
|
||||
raise ValueError("payload.goods_url 不能为空")
|
||||
if goods_id is not None and not isinstance(goods_id, str):
|
||||
raise ValueError("payload.goods_id 必须是文本")
|
||||
|
||||
original_task = {
|
||||
"id": task.task_id,
|
||||
"type": task.task_type.value,
|
||||
"version": task.version,
|
||||
"priority": task.priority,
|
||||
"payload": payload,
|
||||
"created_at": task.created_at,
|
||||
"updated_at": task.updated_at,
|
||||
}
|
||||
return NewClaimedTask(
|
||||
remote_task_id=task.task_id,
|
||||
task_type=task.task_type,
|
||||
goods_url=goods_url,
|
||||
goods_id=goods_id.strip() if isinstance(goods_id, str) else None,
|
||||
priority=task.priority,
|
||||
version=task.version,
|
||||
admin_payload=original_task,
|
||||
)
|
||||
|
||||
|
||||
class ClaimTaskWorker(QObject):
|
||||
"""在后台补交或执行至多一条采集任务。"""
|
||||
"""在后台补交、领取或执行至多一条任务。"""
|
||||
|
||||
noTask = pyqtSignal()
|
||||
taskSaved = pyqtSignal(str)
|
||||
@@ -141,6 +110,7 @@ class ClaimTaskWorker(QObject):
|
||||
client_service: CurrentClientService,
|
||||
android_device_service: SelectedAndroidDeviceService,
|
||||
collect_service_factory: Optional[CollectServiceFactory] = None,
|
||||
purchase_adapter_factory: Optional[PurchaseAdapterFactory] = None,
|
||||
selected_task_id: str = "",
|
||||
) -> None:
|
||||
super().__init__()
|
||||
@@ -150,6 +120,7 @@ class ClaimTaskWorker(QObject):
|
||||
self._android_device_service = android_device_service
|
||||
self._cancelled = False
|
||||
self._collect_service_factory = collect_service_factory
|
||||
self._purchase_adapter_factory = purchase_adapter_factory
|
||||
self._selected_task_id = selected_task_id
|
||||
|
||||
def cancel(self) -> None:
|
||||
@@ -168,22 +139,31 @@ class ClaimTaskWorker(QObject):
|
||||
return
|
||||
android_serial = self._android_device_service.load()
|
||||
|
||||
service = CollectTaskService(
|
||||
self._gateway,
|
||||
self._task_repository,
|
||||
ClientInfo(
|
||||
client_settings.client_id,
|
||||
client_settings.client_name,
|
||||
),
|
||||
android_serial or "",
|
||||
cancelled=lambda: self._cancelled,
|
||||
collect_service_factory=self._collect_service_factory,
|
||||
)
|
||||
result = (
|
||||
service.execute_selected(self._selected_task_id)
|
||||
if self._selected_task_id
|
||||
else service.execute_one()
|
||||
client = ClientInfo(
|
||||
client_settings.client_id,
|
||||
client_settings.client_name,
|
||||
)
|
||||
if self._selected_task_id:
|
||||
service = CollectTaskService(
|
||||
self._gateway,
|
||||
self._task_repository,
|
||||
client,
|
||||
android_serial or "",
|
||||
cancelled=lambda: self._cancelled,
|
||||
collect_service_factory=self._collect_service_factory,
|
||||
)
|
||||
result = service.execute_selected(self._selected_task_id)
|
||||
else:
|
||||
dispatcher = TaskDispatcher(
|
||||
self._gateway,
|
||||
self._task_repository,
|
||||
client,
|
||||
android_serial or "",
|
||||
collect_service_factory=self._collect_service_factory,
|
||||
purchase_adapter_factory=self._purchase_adapter_factory,
|
||||
cancelled=lambda: self._cancelled,
|
||||
)
|
||||
result = dispatcher.execute_one()
|
||||
if not self._cancelled or result.kind == "cancelled":
|
||||
self.outcome.emit(result.kind, result.message, result.task_id)
|
||||
except AdminGatewayError as exc:
|
||||
@@ -198,7 +178,7 @@ class ClaimTaskWorker(QObject):
|
||||
self.failed.emit(message)
|
||||
except Exception as exc:
|
||||
if not self._cancelled:
|
||||
self.failed.emit(f"执行采集任务失败:{exc}")
|
||||
self.failed.emit(f"执行任务失败:{exc}")
|
||||
finally:
|
||||
self.completed.emit()
|
||||
|
||||
@@ -214,6 +194,7 @@ class PDDTaskPageEvent(QObject):
|
||||
claim_gateway: Optional[AdminGateway] = None,
|
||||
settings_repository: Optional[SettingsRepository] = None,
|
||||
collect_service_factory: Optional[CollectServiceFactory] = None,
|
||||
purchase_adapter_factory: Optional[PurchaseAdapterFactory] = None,
|
||||
next_task_delay_ms: int = 500,
|
||||
no_task_delay_ms: int = 5_000,
|
||||
retry_delays_ms: tuple[int, ...] = (5_000, 10_000, 20_000, 30_000),
|
||||
@@ -233,6 +214,7 @@ class PDDTaskPageEvent(QObject):
|
||||
self._retry_count = 0
|
||||
self._detail_windows: Dict[str, TaskDetailWindow] = {}
|
||||
self._collect_service_factory = collect_service_factory
|
||||
self._purchase_adapter_factory = purchase_adapter_factory
|
||||
if next_task_delay_ms < 0 or no_task_delay_ms <= 0:
|
||||
raise ValueError("自动获取等待时间配置无效")
|
||||
if not retry_delays_ms or any(value <= 0 for value in retry_delays_ms):
|
||||
@@ -476,15 +458,16 @@ class PDDTaskPageEvent(QObject):
|
||||
self._claim_busy = True
|
||||
self._cycle_next_delay_ms = None
|
||||
self._page.set_auto_fetch_state("running")
|
||||
self._page.set_engine_status("自动获取:运行中 · 正在处理一条采集任务…")
|
||||
self._page.set_engine_status("自动获取:运行中 · 正在处理一条任务…")
|
||||
|
||||
thread = QThread(self)
|
||||
worker = ClaimTaskWorker(
|
||||
self._claim_gateway,
|
||||
self._repository,
|
||||
self._client_service,
|
||||
self._selected_android_device_service,
|
||||
self._collect_service_factory,
|
||||
gateway=self._claim_gateway,
|
||||
task_repository=self._repository,
|
||||
client_service=self._client_service,
|
||||
android_device_service=self._selected_android_device_service,
|
||||
collect_service_factory=self._collect_service_factory,
|
||||
purchase_adapter_factory=self._purchase_adapter_factory,
|
||||
)
|
||||
worker.moveToThread(thread)
|
||||
thread.started.connect(worker.run)
|
||||
@@ -630,7 +613,7 @@ class PDDTaskPageEvent(QObject):
|
||||
self._show_retry_paused(content)
|
||||
self._stop_after_current(content)
|
||||
elif kind in {"manual_review", "failed"}:
|
||||
self._show_claim_error("采集任务需要处理", message)
|
||||
self._show_claim_error("任务需要处理", message)
|
||||
self._stop_after_current(message)
|
||||
elif kind == "cancelled":
|
||||
self._stop_after_current(message or "自动获取已停止")
|
||||
@@ -648,7 +631,11 @@ class PDDTaskPageEvent(QObject):
|
||||
task = self._repository.get_task(task_id)
|
||||
except Exception:
|
||||
return False
|
||||
return task is not None and task.status is TaskStatus.RETRY_WAIT
|
||||
return (
|
||||
task is not None
|
||||
and task.task_type is TaskType.COLLECT
|
||||
and task.status is TaskStatus.RETRY_WAIT
|
||||
)
|
||||
|
||||
def _show_retry_paused(self, content: str) -> None:
|
||||
"""用持久警告说明任务不会自行倒计时重试。"""
|
||||
|
||||
Reference in New Issue
Block a user