fix: 优化重新采集取消交互 (#82)

This commit is contained in:
chengma
2026-08-10 09:08:54 +08:00
parent 8cc8574e8a
commit 39f4a8ee4f
6 changed files with 307 additions and 16 deletions
+25 -3
View File
@@ -126,12 +126,20 @@ class CollectTaskService:
def _execute_task(self, remote_task_id: str) -> CollectTaskOutcome:
"""执行一条已经保存在本地且处于待执行状态的采集任务。"""
if self._cancelled():
return CollectTaskOutcome("cancelled", "本次采集已取消", remote_task_id)
started = self._repository.start_collect_run(
remote_task_id, self._device_address
)
if self._cancelled():
event = self._repository.save_collect_failure(
remote_task_id,
started.attempt_id,
TaskStatus.CANCELLED,
"PDD_CANCELLED",
"采集任务已安全取消",
False,
{},
)
return self._submit_cancelled(event, remote_task_id)
collector = self._factory(
self._device_address, self._client.client_id, self._cancelled
)
@@ -152,8 +160,22 @@ class CollectTaskService:
retryable,
exc.diagnostics,
)
if exc.code == "PDD_CANCELLED":
return self._submit_cancelled(event, remote_task_id)
return self._submit(event)
def _submit_cancelled(
self, event: OutboxEventRecord, remote_task_id: str
) -> CollectTaskOutcome:
"""提交取消结果;Admin 接收后对界面返回明确的已取消。"""
outcome = self._submit(event)
if outcome.kind == "failed":
return CollectTaskOutcome(
"cancelled", f"任务 {remote_task_id} 已安全取消", remote_task_id
)
return outcome
def _submit(self, event: OutboxEventRecord) -> CollectTaskOutcome:
task_id = self._repository.outbox_task_id(event.id)
self._repository.mark_outbox_sending(event.id)
+32 -5
View File
@@ -277,12 +277,14 @@ class PDDTaskPage(QWidget):
searchRequested = pyqtSignal(dict)
refreshRequested = pyqtSignal()
rerunRequested = pyqtSignal(str)
rerunCancelRequested = pyqtSignal()
detailRequested = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("pddTaskPage")
self._rerun_state = "idle"
self.taskModel = TaskTableModel(self)
self._build_command_area()
self._build_content_area()
@@ -508,22 +510,47 @@ class PDDTaskPage(QWidget):
return task.remote_task_id if task else ""
def set_rerun_running(self, running: bool) -> None:
"""重新采集期间锁定两个会操作手机的入口。"""
"""设置重新采集状态;运行时保留可见的停止入口。"""
self.rerunButton.setText("正在重新采集" if running else "重新执行")
self.autoFetchButton.setEnabled(not running)
self._rerun_state = "running" if running else "idle"
self._update_rerun_button()
def set_rerun_cancelling(self) -> None:
"""显示取消请求已发出,防止用户重复点击。"""
self._rerun_state = "cancelling"
self._update_rerun_button()
def _request_rerun(self) -> None:
if self._rerun_state == "running":
self.rerunCancelRequested.emit()
return
if self._rerun_state != "idle":
return
task_id = self.current_task_id()
if task_id:
self.rerunRequested.emit(task_id)
def _update_rerun_button(self) -> None:
running = self.rerunButton.text() == "正在重新采集"
if self._rerun_state == "running":
self.rerunButton.setText("停止重新采集")
self.rerunButton.setAccessibleName("停止当前重新采集")
self.rerunButton.setEnabled(True)
self.autoFetchButton.setEnabled(False)
return
if self._rerun_state == "cancelling":
self.rerunButton.setText("正在停止…")
self.rerunButton.setAccessibleName("正在停止重新采集")
self.rerunButton.setEnabled(False)
self.autoFetchButton.setEnabled(False)
return
self.rerunButton.setText("重新执行")
self.rerunButton.setAccessibleName("重新执行当前选中的采集任务")
self.autoFetchButton.setEnabled(True)
has_selection = self.taskTable.selectionModel().hasSelection()
self.rerunButton.setEnabled(
has_selection and bool(self.current_task_id()) and not running
has_selection and bool(self.current_task_id())
)
def _apply_filters(self) -> None:
+92 -6
View File
@@ -30,7 +30,7 @@ from PyQt5.QtCore import (
pyqtSignal,
pyqtSlot,
)
from qfluentwidgets import InfoBar, InfoBarPosition, MessageBox
from qfluentwidgets import InfoBar, InfoBarPosition, MessageBox, PushButton
from .admin_gateway import (
AdminGatewayError,
@@ -134,7 +134,7 @@ class ClaimTaskWorker(QObject):
@pyqtSlot()
def run(self) -> None:
try:
if self._cancelled:
if self._cancelled and not self._selected_task_id:
return
client_settings = self._client_service.load()
if not client_settings.client_id:
@@ -212,6 +212,8 @@ class PDDTaskPageEvent(QObject):
self._claim_busy = False
self._claim_thread: Optional[QThread] = None
self._claim_worker: Optional[ClaimTaskWorker] = None
self._rerun_cancel_requested = False
self._rerun_feedback: Optional[InfoBar] = None
self._auto_fetch_running = False
self._stop_requested = False
self._cycle_next_delay_ms: Optional[int] = None
@@ -261,6 +263,7 @@ class PDDTaskPageEvent(QObject):
page.searchRequested.connect(self.search_tasks)
page.refreshRequested.connect(self.refresh_tasks)
page.rerunRequested.connect(self.request_rerun)
page.rerunCancelRequested.connect(self.request_cancel_rerun)
page.autoFetchRequested.connect(self._request_claim_task)
page.detailRequested.connect(self.show_task_detail)
page.taskModel.loadMoreRequested.connect(self._load_page)
@@ -345,7 +348,7 @@ class PDDTaskPageEvent(QObject):
self._page.window(),
)
dialog.yesButton.setText("重新采集")
dialog.cancelButton.setText("取消")
dialog.cancelButton.setText("暂不重新采集")
dialog.cancelButton.setFocus()
if not dialog.exec():
return
@@ -367,6 +370,8 @@ class PDDTaskPageEvent(QObject):
"""启动只处理指定任务的工作线程。"""
assert self._claim_gateway is not None
self._close_rerun_feedback()
self._rerun_cancel_requested = False
self._claim_busy = True
self._page.set_rerun_running(True)
self._page.set_engine_status(f"正在重新采集任务 {task_id}…")
@@ -394,32 +399,67 @@ class PDDTaskPageEvent(QObject):
self._claim_worker = worker
thread.start()
@pyqtSlot()
def request_cancel_rerun(self) -> None:
"""请求当前重新采集在下一个安全点停止。"""
worker = self._claim_worker
if (
self._closing
or self._rerun_cancel_requested
or not self._claim_busy
or worker is None
):
return
self._rerun_cancel_requested = True
self._page.set_rerun_cancelling()
self._page.set_engine_status(
"正在停止重新采集…正在等待手机当前操作结束"
)
# uiautomator2/ADB 的单次调用无法安全强制中断。
# Worker 会在调用返回后通过 cancelled 回调在安全点停止。
worker.cancel()
@pyqtSlot(str, str, str)
def _on_rerun_outcome(self, kind: str, message: str, _task_id: str) -> None:
if self._closing:
return
self._reload()
if self._rerun_cancel_requested:
self._page.set_engine_status(
"停止请求已处理,请查看任务最新状态"
)
return
self._page.set_engine_status(message)
if kind != "succeeded":
self._show_claim_error("重新采集需要处理", message)
self._show_rerun_error("重新采集需要处理", message)
@pyqtSlot(str)
def _on_rerun_failed(self, message: str) -> None:
if self._closing:
return
if self._rerun_cancel_requested:
return
self._page.set_engine_status(message)
self._show_claim_error("重新采集失败", message)
self._show_rerun_error("重新采集失败", message)
@pyqtSlot()
def _on_rerun_thread_finished(self) -> None:
cancel_requested = self._rerun_cancel_requested
self._claim_worker = None
self._claim_thread = None
self._claim_busy = False
self._rerun_cancel_requested = False
if not self._closing:
self._page.set_rerun_running(False)
if cancel_requested:
self._reload()
self._page.set_engine_status(
"停止请求已处理,请查看任务最新状态"
)
def _show_rerun_warning(self, title: str, content: str) -> None:
InfoBar.warning(
bar = InfoBar.warning(
title=title,
content=content,
isClosable=True,
@@ -427,6 +467,51 @@ class PDDTaskPageEvent(QObject):
position=InfoBarPosition.TOP_RIGHT,
parent=self._page,
)
self._replace_rerun_feedback(bar)
def _show_rerun_error(self, title: str, content: str) -> None:
"""显示一条可明确关闭的重新采集错误。"""
bar = InfoBar.error(
title=title,
content=content,
isClosable=True,
duration=-1,
position=InfoBarPosition.TOP_RIGHT,
parent=self._page,
)
self._replace_rerun_feedback(bar)
def _replace_rerun_feedback(self, bar: InfoBar) -> None:
"""用新提示替换旧提示,避免连续失败后堆叠。"""
self._close_rerun_feedback()
self._rerun_feedback = bar
close_button = PushButton("关闭提示", bar)
close_button.setAccessibleName("关闭重新采集提示")
close_button.clicked.connect(
lambda _checked=False, current=bar: self._close_rerun_feedback(current)
)
bar.addWidget(close_button)
bar.destroyed.connect(
lambda _object=None, current=bar: self._forget_rerun_feedback(current)
)
def _close_rerun_feedback(self, expected: Optional[InfoBar] = None) -> None:
"""关闭当前重新采集提示;旧提示不得关闭新提示。"""
bar = self._rerun_feedback
if bar is None or (expected is not None and bar is not expected):
return
self._rerun_feedback = None
try:
bar.close()
except RuntimeError:
pass
def _forget_rerun_feedback(self, bar: InfoBar) -> None:
if self._rerun_feedback is bar:
self._rerun_feedback = None
@pyqtSlot()
def _request_claim_task(self) -> None:
@@ -763,6 +848,7 @@ class PDDTaskPageEvent(QObject):
self._auto_fetch_running = False
self._stop_requested = True
self._next_cycle_timer.stop()
self._close_rerun_feedback()
for window in list(self._detail_windows.values()):
window.close()