fix: 领取任务后立即刷新列表 (#107)

This commit is contained in:
chengma
2026-08-10 17:11:59 +08:00
parent 8931162106
commit 39686d7b3b
8 changed files with 125 additions and 15 deletions
+7 -6
View File
@@ -21,7 +21,7 @@
- 图标用 `FluentIcon`,**不得用表情符号**。
- 页面 `objectName` 固定为 `pddTaskPage`,不要改。
注意:“开始自动获取”和“重新执行”会真的操作手机。重新执行当前只允许
注意:“自动获取”和“重新执行”会真的操作手机。重新执行当前只允许
采集任务,采购任务必须在事件层拦截,不能从本入口下单。
"""
@@ -387,10 +387,10 @@ class PDDTaskPage(QWidget):
def _build_command_area(self) -> None:
self.autoFetchButton = PrimaryPushButton(
FIF.PLAY, "开始自动获取", self
FIF.PLAY, "自动获取", self
)
self.autoFetchButton.setMinimumWidth(132)
self.autoFetchButton.setAccessibleName("开始自动获取 PDD 任务")
self.autoFetchButton.setAccessibleName("自动获取 PDD 任务")
self.taskTypeLabel = CaptionLabel("任务类型", self)
self.taskTypeCombo = ComboBox(self)
@@ -410,8 +410,8 @@ class PDDTaskPage(QWidget):
self.keywordInput = SearchLineEdit(self)
self.keywordInput.setPlaceholderText("任务编号、商品编号或商品标题")
self.keywordInput.setClearButtonEnabled(True)
self.keywordInput.setMinimumWidth(190)
self.keywordInput.setMaximumWidth(480)
self.keywordInput.setMinimumWidth(150)
self.keywordInput.setMaximumWidth(240)
self.keywordInput.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.keywordInput.setAccessibleName("任务关键词")
@@ -626,7 +626,7 @@ class PDDTaskPage(QWidget):
"""更新自动获取按钮;state 为 stopped/starting/running/stopping。"""
states = {
"stopped": ("开始自动获取", FIF.PLAY, True),
"stopped": ("自动获取", FIF.PLAY, True),
"starting": ("正在启动", FIF.SYNC, False),
"running": ("停止自动获取", FIF.PAUSE, True),
"stopping": ("正在停止", FIF.PAUSE, False),
@@ -635,6 +635,7 @@ class PDDTaskPage(QWidget):
self.autoFetchButton.setText(text)
self.autoFetchButton.setIcon(icon)
self.autoFetchButton.setEnabled(enabled)
self.autoFetchButton.setAccessibleName(f"{text} PDD 任务")
def set_engine_status(self, text: str) -> None:
"""更新页面底部的任务引擎状态说明。"""
+1
View File
@@ -214,6 +214,7 @@ class ClaimTaskWorker(QObject):
purchase_reconcile_factory=self._purchase_reconcile_factory,
cancelled=lambda: self._cancelled,
device_connection_checker=self._device_connection_checker,
task_saved=self.taskSaved.emit,
)
result = dispatcher.execute_one()
if not self._cancelled or result.kind == "cancelled":
+6
View File
@@ -138,6 +138,7 @@ class TaskDispatcher:
purchase_reconcile_factory: Optional[PurchaseReconcileFactory] = None,
cancelled: Callable[[], bool] = lambda: False,
device_connection_checker: Optional[Callable[[str], None]] = None,
task_saved: Callable[[str], None] = lambda _task_id: None,
) -> None:
self._gateway = gateway
self._repository = repository
@@ -151,6 +152,7 @@ class TaskDispatcher:
self._purchase_mode = purchase_mode
self._reconcile_factory = purchase_reconcile_factory
self._cancelled = cancelled
self._task_saved = task_saved
self._device_connection_checker = (
device_connection_checker
or AndroidDeviceService().require_connected
@@ -251,10 +253,12 @@ class TaskDispatcher:
)
if remote is None:
return TaskDispatchOutcome("no_task", "暂无可领取的任务")
saved_now = False
try:
self._repository.add_claimed_task(
admin_task_to_new_claimed_task(remote)
)
saved_now = True
except DuplicateTaskError:
pass
except Exception as exc:
@@ -266,6 +270,8 @@ class TaskDispatcher:
raise RuntimeError(
f"任务 {remote.task_id} 已领取,但未能保存到本地"
)
if saved_now:
self._task_saved(remote.task_id)
if task.task_type is TaskType.COLLECT:
service = CollectTaskService(
+19
View File
@@ -80,6 +80,25 @@ class TaskTableModelCheckTests(unittest.TestCase):
self.assertEqual(page.checkedCountLabel.text(), "已选 0 条")
page.deleteLater()
def test_auto_fetch_text_and_keyword_width(self):
page = PDDTaskPage()
self.assertEqual(page.autoFetchButton.text(), "自动获取")
self.assertEqual(
page.autoFetchButton.accessibleName(), "自动获取 PDD 任务"
)
self.assertEqual(page.keywordInput.minimumWidth(), 150)
self.assertEqual(page.keywordInput.maximumWidth(), 240)
page.set_auto_fetch_state("running")
self.assertEqual(page.autoFetchButton.text(), "停止自动获取")
self.assertEqual(
page.autoFetchButton.accessibleName(), "停止自动获取 PDD 任务"
)
page.set_auto_fetch_state("stopped")
self.assertEqual(page.autoFetchButton.text(), "自动获取")
page.deleteLater()
if __name__ == "__main__":
unittest.main()
+55 -4
View File
@@ -182,6 +182,20 @@ class FakeCollector:
return FakeCollectResult()
class BlockingCollector:
"""让测试能在新任务落库后、采集结束前检查界面。"""
def __init__(self, started, release):
self.started = started
self.release = release
def collect(self, _task):
self.started.set()
if not self.release.wait(timeout=3):
raise RuntimeError("测试没有及时结束阻塞采集")
return FakeCollectResult()
def fake_collect_factory(*_args):
return FakeCollector()
@@ -1040,6 +1054,43 @@ class PDDTaskPageEventTest(unittest.TestCase):
events.shutdown()
page.deleteLater()
def test_new_claim_refreshes_table_before_collection_finishes(self):
gateway = RecordingClaimGateway(collect_admin_task("COL-EARLY"))
collect_started = threading.Event()
release_collect = threading.Event()
page = PDDTaskPage()
events = PDDTaskPageEvent(
page,
self.repository,
claim_gateway=gateway,
settings_repository=self._saved_settings(),
collect_service_factory=(
lambda *_args: BlockingCollector(
collect_started, release_collect
)
),
)
page.autoFetchRequested.emit()
try:
self.assertTrue(wait_until(self.app, collect_started.is_set))
self.assertTrue(events._claim_busy)
self.assertTrue(
wait_until(
self.app,
lambda: page.taskModel.data_row_count() == 1,
)
)
self.assertEqual(
page.taskModel.row_at(0).remote_task_id, "COL-EARLY"
)
finally:
release_collect.set()
self.assertTrue(wait_until(self.app, lambda: not events._claim_busy))
events.shutdown()
page.deleteLater()
def test_ready_runtime_claims_and_dispatches_purchase_in_worker(self):
gateway = RecordingClaimGateway(purchase_admin_task())
page = PDDTaskPage()
@@ -1091,7 +1142,7 @@ class PDDTaskPageEventTest(unittest.TestCase):
page.autoFetchRequested.emit()
self.assertFalse(events._next_cycle_timer.isActive())
self.assertEqual(page.autoFetchButton.text(), "开始自动获取")
self.assertEqual(page.autoFetchButton.text(), "自动获取")
events.shutdown()
page.deleteLater()
@@ -1113,7 +1164,7 @@ class PDDTaskPageEventTest(unittest.TestCase):
self.assertFalse(page.autoFetchButton.isEnabled())
self.assertTrue(wait_until(self.app, lambda: not events._claim_busy))
self.assertEqual(len(gateway.calls), 1)
self.assertEqual(page.autoFetchButton.text(), "开始自动获取")
self.assertEqual(page.autoFetchButton.text(), "自动获取")
events.shutdown()
page.deleteLater()
@@ -1142,7 +1193,7 @@ class PDDTaskPageEventTest(unittest.TestCase):
page.autoFetchRequested.emit()
self.assertTrue(wait_until(self.app, lambda: not events._claim_busy))
self.assertEqual(page.autoFetchButton.text(), "开始自动获取")
self.assertEqual(page.autoFetchButton.text(), "自动获取")
events.shutdown()
page.deleteLater()
@@ -1307,7 +1358,7 @@ class PDDTaskPageEventTest(unittest.TestCase):
self.assertEqual(gateway.calls, [])
self.assertIn("设置页", page.statusLabel.text())
self.assertFalse(events._auto_fetch_running)
self.assertEqual(page.autoFetchButton.text(), "开始自动获取")
self.assertEqual(page.autoFetchButton.text(), "自动获取")
events.shutdown()
page.deleteLater()
+32
View File
@@ -133,6 +133,7 @@ class TaskDispatcherTest(unittest.TestCase):
live_ready=False,
purchase_mode="dry_run",
device_checker=lambda _serial: None,
task_saved=lambda _task_id: None,
):
purchase_factory = None
if purchase_ready:
@@ -159,6 +160,7 @@ class TaskDispatcherTest(unittest.TestCase):
),
purchase_mode=purchase_mode,
device_connection_checker=device_checker,
task_saved=task_saved,
)
def test_capability_only_includes_purchase_when_adapter_is_ready(self):
@@ -249,6 +251,36 @@ class TaskDispatcherTest(unittest.TestCase):
)
self.assertIn(("purchase", "stopped"), self.calls)
def test_new_task_notifies_after_save_and_before_execution(self):
task = AdminTask(
"COL-SAVED",
TaskType.COLLECT,
1,
0,
{
"goods_url": "https://example.test/COL-SAVED",
"goods_id": "COL-GOODS",
},
)
self.gateway.enqueue_task(task, self.client.client_id)
notifications = []
def record_saved(task_id):
detail = self.repository.get_task(task_id)
notifications.append(
(task_id, detail.status if detail else None, list(self.calls))
)
outcome = self._dispatcher(
purchase_ready=False, task_saved=record_saved
).execute_one()
self.assertEqual(outcome.kind, "succeeded")
self.assertEqual(
notifications,
[("COL-SAVED", TaskStatus.CLAIMED, [])],
)
def test_purchase_is_not_claimed_when_runtime_is_not_ready(self):
self.gateway.enqueue_task(purchase_task(), self.client.client_id)