feat: 安全移除本地任务记录 (#108)

This commit is contained in:
chengma
2026-08-10 17:23:24 +08:00
parent 9d227b431a
commit b2721071c9
10 changed files with 619 additions and 30 deletions
+158 -5
View File
@@ -59,7 +59,11 @@ from .task_models import (
TaskSummary,
TaskType,
)
from .task_repository import CollectRerunError, TaskRepository
from .task_repository import (
CollectRerunError,
TaskRemovalError,
TaskRepository,
)
from .task_dispatcher import (
TaskDispatcher,
admin_task_to_new_claimed_task,
@@ -322,6 +326,36 @@ class ResultResubmitWorker(QObject):
self.completed.emit()
class TaskRemoveWorker(QObject):
"""在后台原子校验并软移除勾选任务,不访问任何界面控件。"""
succeeded = pyqtSignal(object)
failed = pyqtSignal(str)
completed = pyqtSignal()
def __init__(
self,
repository: TaskRepository,
task_ids: tuple[str, ...],
) -> None:
super().__init__()
self._repository = repository
self._task_ids = task_ids
@pyqtSlot()
def run(self) -> None:
try:
self._repository.remove_tasks_from_list(self._task_ids)
except TaskRemovalError as exc:
self.failed.emit(str(exc))
except Exception:
self.failed.emit("删除本地任务失败,请检查数据库后重试。")
else:
self.succeeded.emit(self._task_ids)
finally:
self.completed.emit()
class PDDTaskPageEvent(QObject):
"""把 PDD 页面只读操作连接到本地任务 Repository。"""
@@ -359,6 +393,9 @@ class PDDTaskPageEvent(QObject):
self._resubmit_busy = False
self._resubmit_thread: Optional[QThread] = None
self._resubmit_worker: Optional[ResultResubmitWorker] = None
self._remove_busy = False
self._remove_thread: Optional[QThread] = None
self._remove_worker: Optional[TaskRemoveWorker] = None
self._stop_requested = False
self._cycle_next_delay_ms: Optional[int] = None
self._stop_status = "自动获取:已停止 · 当前没有执行中的任务"
@@ -417,6 +454,7 @@ class PDDTaskPageEvent(QObject):
page.rerunRequested.connect(self.request_rerun)
page.rerunCancelRequested.connect(self.request_cancel_rerun)
page.resubmitRequested.connect(self.request_resubmit)
page.removeRequested.connect(self.request_remove)
page.autoFetchRequested.connect(self._request_claim_task)
page.detailRequested.connect(self.show_task_detail)
page.taskModel.loadMoreRequested.connect(self._load_page)
@@ -452,7 +490,12 @@ class PDDTaskPageEvent(QObject):
if self._closing or not task_id:
return
if self._auto_fetch_running or self._claim_busy or self._resubmit_busy:
if (
self._auto_fetch_running
or self._claim_busy
or self._resubmit_busy
or self._remove_busy
):
self._show_rerun_warning(
"暂时不能重新执行",
"自动获取或其他采集正在运行,请停止并等待当前任务结束。",
@@ -517,7 +560,12 @@ class PDDTaskPageEvent(QObject):
)
if self._closing or not stable_ids:
return
if self._auto_fetch_running or self._claim_busy or self._resubmit_busy:
if (
self._auto_fetch_running
or self._claim_busy
or self._resubmit_busy
or self._remove_busy
):
self._show_rerun_warning(
"暂时不能重新上报",
"自动获取、重新执行或另一批上报正在运行,请等待当前操作结束。",
@@ -545,6 +593,98 @@ class PDDTaskPageEvent(QObject):
self._start_resubmit_worker(stable_ids)
@pyqtSlot(object)
def request_remove(self, task_ids) -> None:
"""确认后在后台从普通列表软移除勾选的本地任务。"""
stable_ids = tuple(
dict.fromkeys(
str(value).strip()
for value in task_ids
if value is not None and str(value).strip()
)
)
if self._closing or not stable_ids:
return
if (
self._auto_fetch_running
or self._claim_busy
or self._resubmit_busy
or self._remove_busy
):
self._show_rerun_warning(
"暂时不能删除",
"自动获取、重新执行、重新上报或另一批删除正在运行,"
"请等待当前操作结束。",
)
return
count = len(stable_ids)
dialog = MessageBox(
f"从列表删除 {count} 条任务?",
"只会从普通任务列表中隐藏这些本地记录,不会删除 Admin 任务;"
"执行记录和上报历史仍会永久保留。\n\n"
"只有已结束、没有待上报数据且没有进入不可逆阶段的任务才允许删除。",
self._page.window(),
)
dialog.yesButton.setText(f"删除 {count} 条")
dialog.cancelButton.setText("暂不删除")
dialog.cancelButton.setFocus()
if not dialog.exec():
return
self._start_remove_worker(stable_ids)
def _start_remove_worker(self, task_ids: tuple[str, ...]) -> None:
"""启动本地任务软删除工作线程。"""
self._remove_busy = True
self._page.set_remove_running(True)
self._page.set_engine_status(
f"正在检查并删除 {len(task_ids)} 条本地任务…"
)
thread = QThread(self)
worker = TaskRemoveWorker(self._repository, task_ids)
worker.moveToThread(thread)
thread.started.connect(worker.run)
worker.succeeded.connect(self._on_remove_succeeded)
worker.failed.connect(self._on_remove_failed)
worker.completed.connect(thread.quit)
worker.completed.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)
thread.finished.connect(self._on_remove_thread_finished)
self._remove_thread = thread
self._remove_worker = worker
thread.start()
@pyqtSlot(object)
def _on_remove_succeeded(self, task_ids) -> None:
if self._closing:
return
stable_ids = tuple(task_ids)
self._page.taskModel.uncheck_tasks(stable_ids)
self._page.set_engine_status(
f"已从列表删除 {len(stable_ids)} 条本地任务;"
"执行记录和上报历史仍会永久保留。"
)
self._reload()
@pyqtSlot(str)
def _on_remove_failed(self, message: str) -> None:
if self._closing:
return
self._page.set_engine_status(f"删除未完成:{message}")
self._show_claim_error("不能删除所选任务", message)
@pyqtSlot()
def _on_remove_thread_finished(self) -> None:
self._remove_worker = None
self._remove_thread = None
self._remove_busy = False
if not self._closing:
self._page.set_remove_running(False)
def _start_resubmit_worker(self, task_ids: tuple[str, ...]) -> None:
"""启动只处理指定任务 Outbox 的工作线程。"""
@@ -774,10 +914,10 @@ class PDDTaskPageEvent(QObject):
if self._closing:
return
if self._resubmit_busy:
if self._resubmit_busy or self._remove_busy:
self._show_rerun_warning(
"暂时不能获取任务",
"任务结果正在重新上报,请等待当前操作结束。",
"本地任务正在重新上报或删除,请等待当前操作结束。",
)
return
if self._auto_fetch_running:
@@ -1283,6 +1423,19 @@ class PDDTaskPageEvent(QObject):
resubmit_thread.quit()
resubmit_thread.wait(60_000)
remove_worker = self._remove_worker
remove_thread = self._remove_thread
if remove_worker is not None:
try:
remove_worker.succeeded.disconnect(self._on_remove_succeeded)
remove_worker.failed.disconnect(self._on_remove_failed)
except (TypeError, RuntimeError):
pass
if remove_thread is not None and remove_thread.isRunning():
# 删除只包含一个很短的 SQLite 事务,不中途取消,避免部分更新。
remove_thread.quit()
remove_thread.wait(60_000)
def summary_to_row(summary: TaskSummary) -> TaskRow:
"""把领域摘要转换成只供表格显示的轻量行。"""