2026-08-04 21:36:52 +08:00
|
|
|
|
---
|
|
|
|
|
|
id: T-304
|
|
|
|
|
|
title: 定时轮询、会话边界与采购工具主界面
|
|
|
|
|
|
phase: 3
|
|
|
|
|
|
deps: [T-303, T-006]
|
|
|
|
|
|
status: TODO
|
|
|
|
|
|
created: 2026-08-04
|
|
|
|
|
|
vikunja_task_id: 37
|
|
|
|
|
|
context_ref: 526af1e
|
|
|
|
|
|
work_branch: task/t-304-polling-session-ui
|
|
|
|
|
|
needs_device: false
|
|
|
|
|
|
needs_human_review: true
|
|
|
|
|
|
write_paths:
|
|
|
|
|
|
- docs/tasks/T-304.md
|
|
|
|
|
|
- client/src/cmbuyer_client/app.py
|
|
|
|
|
|
- client/src/cmbuyer_client/polling/**
|
|
|
|
|
|
- client/src/cmbuyer_client/ui/**
|
|
|
|
|
|
- client/tests/test_app.py
|
|
|
|
|
|
- client/tests/polling/**
|
|
|
|
|
|
- client/tests/ui/**
|
|
|
|
|
|
- docs/api.md
|
|
|
|
|
|
- docs/06-tasks.md
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-04 23:20:28 +08:00
|
|
|
|
<!-- BEGIN VIKUNJA EXPORT id=37 synced=2026-08-04T15:18:59Z sha256=70cde8bfacd40b2c2a7132dc0f0390689682403c7934587efb25348b40796826 -->
|
2026-08-04 21:36:52 +08:00
|
|
|
|
## 问题 / 背景
|
|
|
|
|
|
|
|
|
|
|
|
T-006 已确认采购工具双 Tab 原型,T-303 将提供唯一的 HTTP、本地恢复、DPAPI 与单实例底座。T-304 把原型转换为可测试的原生 PySide6 主界面和定时轮询协调器,但在真实单趟执行器接入前不得领取真实任务,避免形成无人消费的 active claim。
|
|
|
|
|
|
|
|
|
|
|
|
## 关联需求与交互
|
|
|
|
|
|
|
|
|
|
|
|
- 功能:F-005、F-008、F-012、F-013。
|
|
|
|
|
|
- 用户故事:US-003、US-004、US-007。
|
|
|
|
|
|
- 依赖:T-303、T-006;T-302/T-301/T-203/T-204 由 T-303 传递满足。
|
|
|
|
|
|
- 实现已确认的“采购工具”固定双 Tab:默认“采购执行”,另一个为“配置”。
|
|
|
|
|
|
- 不接入真机选择、事件、围栏、提交或付款;T-305 才注入受控单趟执行器。
|
|
|
|
|
|
|
|
|
|
|
|
## 方案
|
|
|
|
|
|
|
|
|
|
|
|
1. 使用 `QMainWindow + QTabWidget` 建立不可关闭的固定 Tab,使用稳定 page id 而非显示索引;Tab 切换保留内部状态、选择、滚动和焦点。
|
|
|
|
|
|
2. 采购执行页顶部显示连接、轮询、当前任务与连续失败状态,开始/停止复用同一 `QAction`。独立运行且未注入 execution consumer 时开始按钮禁用,并显示“单趟执行能力尚未接入,不能领取真实任务”。
|
|
|
|
|
|
3. 主体使用 `QSplitter`。左侧 `QStackedWidget` 在“当前任务”和“采购记录详情”间切换;当前任务左侧文字、右侧图片约 2:1,下方为滚动日志。右侧使用 `QTableView + QAbstractTableModel` 显示按时间倒序的标题和状态,不放假记录。
|
|
|
|
|
|
4. 记录表单击只选择;双击非控件区域、Enter、显式“查看所选记录”和上下文命令复用同一 QAction。详情态选择另一行立即更新;Esc 仅从详情返回当前任务,不停止轮询、不关闭程序、不改变服务端状态,并恢复记录选择、滚动和焦点。
|
2026-08-04 23:20:28 +08:00
|
|
|
|
5. 配置页使用 `QScrollArea + QFormLayout` 显式保存。service URL 为只读精确 `http://127.0.0.1:8080`;token 使用密码框且永不回填,只显示“已保存/未保存”布尔状态。首次保存且 `has_stored_device_token=false` 时 token 必填;已有凭据时,规范化后的空输入表示保留现有 DPAPI 密文,非空输入才通过 T-303 原子 profile API 替换。成功后清空输入框;失败保留全部输入并聚焦首个错误。MVP 不提供清除/删除凭据命令,空值绝不能覆盖或删除既有凭据。
|
|
|
|
|
|
6. 配置承载 device id、ADB 路径、serial/transport、轮询间隔默认 15 秒且范围 5–300、连续失败阈值默认 3 且范围 1–10、HTTP 超时默认 10 秒且范围 1–120、真机步骤超时默认 45 秒且范围 5–300。只执行必填、格式、范围以及本地路径形态/存在性校验,不执行路径所指程序;pending/active claim 时冻结 service/device 身份。
|
|
|
|
|
|
7. 配置页不提供“检查连接/测试连接”按钮、QAction、worker 或隐藏探测。保存和本地校验不得发送 HTTP health、`claim-next`、`renew`、`evidence`,也不得调用 ADB、uiautomator2 或拼多多。稳定内联提示为“服务身份将在首次真实领取时验证;设备与 App 状态由后续已取证执行能力验证”。本地校验通过不等于服务、设备或 App 已就绪。
|
|
|
|
|
|
8. 轮询协调器状态固定为 STOPPED、STARTING、BLOCKED、RECOVERING、WAITING、CLAIMING、ACTIVE、RECOVERY_REQUIRED。启动必须先取得 T-303 单实例 guard 并加载 recovery snapshot;重启不自动开始,active claim 只显示待安全恢复。
|
|
|
|
|
|
9. 停止只设置 `accept_new_claims=false`。WAITING 取消下一计时器;CLAIMING 等待有界结果并先落库;返回 claim 后进入 RECOVERY_REQUIRED。不得中断已发请求、清 pending/active、release/abandon、生成新 key/session 或调用真机返回动作。
|
|
|
|
|
|
10. `QTimer` 只调度下一轮;阻塞 I/O 放长期 worker QObject/QThread,worker 不接触 Widget。claim 业务结果必须先交 T-303 持久化,不能因 UI generation 过期而丢弃;迟到的纯视图结果按稳定 request id 丢弃。
|
|
|
|
|
|
11. 连续失败只统计 T-303 标记为可用同幂等键安全重放的网络、超时、5xx 或截断;同 pending request 按轮询间隔重放,达到阈值立即停止。401、协议错误、409、DPAPI/SQLite 错误立即转持久 Banner,不进入普通重试。
|
2026-08-04 21:36:52 +08:00
|
|
|
|
|
|
|
|
|
|
## 验收要点
|
|
|
|
|
|
|
|
|
|
|
|
- 纯状态机覆盖开始、EMPTY、等待、停止、请求中停止、claim 落库、无重叠请求、同键恢复、失败阈值、401/403/409/本地错误和 active 阻止新领取。
|
2026-08-04 23:20:28 +08:00
|
|
|
|
- QtTest 覆盖默认 Tab、固定 Tab 状态、按钮禁用原因、记录倒序、单击/双击/Enter/按钮、详情切换、Esc 层级与焦点恢复。
|
|
|
|
|
|
- 配置测试覆盖:首次无凭据时空 token 拒绝且零 T-303 写入;已有凭据时空 token 原样保留既有 DPAPI 密文;非空 token 只替换一次且成功后清空输入;失败保留输入并聚焦首个错误;界面无 token 回读和清除/删除命令,日志与异常均无 token。
|
|
|
|
|
|
- 配置页不存在连接检查命令;保存、本地校验以及 consumer 未注入时点击禁用的开始入口均产生零 HTTP health、零 `claim-next`/`renew`/`evidence`、零 ADB/uiautomator2/PDD,并显示延后验证提示。静态检查不得出现网络或 ADB 探测调用链。
|
2026-08-04 21:36:52 +08:00
|
|
|
|
- compact/medium/wide 不重建模型或丢选择;长文案、100%–200% DPI、浅色/深色/高对比与纯键盘行为可人工复核。
|
|
|
|
|
|
- 应用独立启动、切换 Tab、Esc、查看记录与关闭窗口均产生零 claim、零 release、零 ADB;代码不导入 PDD 点击、数量、确认页、围栏、提交或付款能力。
|
|
|
|
|
|
- client 全量 unittest、compileall、wheel metadata、根目录完整 init、Vikunja 导出、上下文校验与 diff-check 通过。
|
|
|
|
|
|
|
|
|
|
|
|
## 执行记录
|
|
|
|
|
|
|
|
|
|
|
|
### 2026-08-04T13:35:53Z · ila
|
|
|
|
|
|
|
|
|
|
|
|
2026-08-04 已按确认原型与 Windows UI/UX 规范落成任务:采用固定双 Tab、稳定 ID 的主从工作区、非模态持久错误、Esc 只退出记录详情、显式配置保存,以及停止仅阻止下一次领取的会话边界。独立运行时未注入单趟执行器,开始轮询必须禁用,确保零真实 claim、零 ADB;依赖 T-303、T-006 完成后再开工。
|
|
|
|
|
|
<!-- END VIKUNJA EXPORT -->
|
|
|
|
|
|
|
|
|
|
|
|
## 边界
|
|
|
|
|
|
|
|
|
|
|
|
- 本任务只能消费 T-303 暴露的 profile/session/recovery API,不得另建 SQLite、DPAPI、mutex、
|
|
|
|
|
|
HTTP task source 或第二套 active-claim 状态源。配置或恢复状态损坏必须失败闭合,不能用界面默认值
|
|
|
|
|
|
覆盖持久业务事实。
|
|
|
|
|
|
- 独立应用在未注入单趟 execution consumer 时必须禁用“开始轮询”,且启动、切换 Tab、Esc、查看记录、
|
|
|
|
|
|
保存配置和关闭窗口都必须产生零 claim、零 ADB。不能为了演示界面领取真实任务,也不能填充会被当作
|
|
|
|
|
|
真实状态的假记录。
|
|
|
|
|
|
- 停止轮询只阻止下一次新领取。不得取消已发出的 claim、清除 pending/active、release/abandon、
|
|
|
|
|
|
生成新 session 或幂等键、自动转领,也不得触发真机 Back、退出页面或其他设备动作;返回的 claim
|
|
|
|
|
|
必须先由 T-303 原子落库再更新 UI。
|
|
|
|
|
|
- 重启不自动开始轮询,不自动恢复任何真机点击。pending claim 只能用原 session/request 恢复;active
|
|
|
|
|
|
claim 只显示“待安全恢复”并阻止新领取,租约过期或 409 交 T-207 人工处理。
|
2026-08-04 23:20:28 +08:00
|
|
|
|
- 配置页只允许必填、格式、范围以及本地路径形态/存在性校验,不得提供或隐藏任何网络/ADB“连接检查”;
|
|
|
|
|
|
保存、校验都不得调用 HTTP health、`claim-next`、`renew`、`evidence`,也不得执行 ADB、uiautomator2
|
|
|
|
|
|
或拼多多探测。界面必须明确提示“服务身份将在首次真实领取时验证;设备与 App 状态由后续已取证执行
|
|
|
|
|
|
能力验证”,本地校验通过不能显示服务、设备或 App 已就绪。
|
|
|
|
|
|
- 服务地址只读为精确 `http://127.0.0.1:8080`。首次保存且没有既有 DPAPI 凭据时 token 必填;已有
|
|
|
|
|
|
凭据时空输入表示原样保留现有密文,非空输入才通过 T-303 的原子 profile API 替换,绝不能用空值
|
|
|
|
|
|
覆盖或删除。保存成功后清空 token 输入框且永不回填;保存失败保留输入并聚焦首个错误。MVP 不提供
|
|
|
|
|
|
清除/删除凭据命令,token 不得进入日志或异常文本。
|
2026-08-04 21:36:52 +08:00
|
|
|
|
- Qt worker 不得直接访问 Widget,UI 回调不得丢弃迟到的 claim 业务结果。连续失败只允许用 T-303
|
|
|
|
|
|
已持久化的同一幂等键重放安全网络操作;`retryable` 绝不推导为页面点击、证据替换、下单或提交可重试。
|
|
|
|
|
|
- 记录表的双击仅是非破坏性详情入口,并必须有 Enter 和显式按钮等价路径。Esc 只在详情态返回当前任务,
|
|
|
|
|
|
不停止轮询、不关闭窗口、不释放 claim、不改变服务端状态;返回后按稳定记录 ID 恢复选择、滚动和焦点。
|
|
|
|
|
|
- 不导入或实现 PDD 页面判据、规格选择、数量、确认页、证据上传、事件、失败上报、提交围栏或结果接口;
|
|
|
|
|
|
不编写或引用点击“提交订单”的代码,不编写支付、免密支付、先用后付或任何扣款控件代码。
|