From 4a65138fd7cbf8fa9370fe7cc1e5f77d301f1c06 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Tue, 4 Aug 2026 21:07:43 +0800 Subject: [PATCH] docs(tasks): define T-205 attempt events --- docs/tasks/T-205.md | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 docs/tasks/T-205.md diff --git a/docs/tasks/T-205.md b/docs/tasks/T-205.md new file mode 100644 index 0000000..4a0c8fd --- /dev/null +++ b/docs/tasks/T-205.md @@ -0,0 +1,80 @@ +--- +id: T-205 +title: purchase attempt、事件与执行摘要 API +phase: 2 +deps: [T-103, T-204, T-301, T-302] +status: TODO +created: 2026-08-04 +vikunja_task_id: 35 +context_ref: 8ec740d +work_branch: task/t-205-attempt-events +needs_device: false +needs_human_review: false +write_paths: + - docs/tasks/T-205.md + - admin/migrations/00006_attempt_events.sql + - admin/internal/migrations/migrations_test.go + - admin/internal/attempts/** + - admin/internal/taskclaim/** + - admin/internal/server/** + - admin/cmd/server/** + - admin/README.md + - docs/api.md + - docs/04-architecture.md +--- + + +## 问题 / 背景 + +T-302 负责创建并租赁唯一 purchase attempt,T-103 提供已取证的规格选择与闸门一能力。后续单趟执行需要把每个安全步骤以最小结构化事件追加到采购服务,并在围栏前失败时形成不可覆盖的执行摘要。T-205 只记录既有 claim/attempt 的事实和安全失败;不创建 attempt、不释放授权、不建立提交围栏、不点击或付款。 + +## 关联需求与交互 + +- 功能:F-006、F-007。 +- 用户故事:US-003、US-004、US-005。 +- 依赖:T-103、T-204、T-301、T-302。 +- 后续消费者:T-206 状态视图、T-303 HttpResultSink、T-305 围栏前 dry-run、T-208 提交围栏。 +- API:`POST /api/v1/tasks/{id}/events`、`POST /api/v1/purchase-attempts/{aid}/fail`。 + +## 方案 + +1. 新增 `00006_attempt_events.sql`,保存 append-only attempt events、批次幂等请求和必要执行摘要;事件以 attempt 内单调序号、事件 id/批次 id、固定 step/outcome/reason、服务端接收时间及允许的最小字段组成。数据库约束禁止更新/覆盖旧序号、跨 task/attempt 或围栏后继续写。 +2. 复用 T-302 的统一 claim validator:每个请求都绑定当前设备 Bearer、session、task、attempt、generation、claim token 和未过期租约;认证在 body 前,业务事务在线性化位置再次验证设备 ACTIVE、claim/authorization/attempt 归属与状态。旧 generation/token、跨设备/会话、租约或授权过期均不能写事件。 +3. 事件 API 只接受有界批次和严格 JSON。每个事件的 step/outcome/reason 来自版本化白名单,不接收自由页面文本、XML、地址、手机号、路径、token、选择器、坐标或动作脚本。金额为规范正十进制字符串,数量为正整数;未知字段、额外 JSON、序号缺口/倒退/重复异载荷全部拒绝。 +4. 同一 batch id 同载荷在响应丢失/服务重启后重放原结果;同键异载荷冲突。一个事务内校验整批连续性、步骤状态机和 summary 兼容性后全批追加,任一事件非法则零写入。 +5. 第一个明确的执行开始事件只允许把 attempt `CLAIMED→ORDERING`、task `CLAIMED→ORDERING`;不得隐式续租。后续事件只在 ORDERING 中追加。规格、两次规格面板价格、数量读回、确认页金额仅由对应固定成功 step 写入 summary,不能由客户端直接覆盖已有摘要。 +6. 围栏前 fail API 只接受固定 reason code 和已记录的最后安全步骤,幂等地把当前 attempt 置 FAILED、task 置 NEEDS_MANUAL,并写终止事件/finished_at。authorization 和 claim 保持占用,等待 T-207 人工检查后显式关闭;失败请求不自动回 DRAFT、不释放、不转领。 +7. 已失败、已围栏、存在 order_submission、claim/授权/租约不再有效时拒绝新事件或新的失败结果。围栏相关状态只能由 T-208 创建,T-205 不读取或返回 `click_permitted`。 +8. 原始截图继续走 T-204/T-306;事件只引用服务端已有的受控 evidence asset id(如需要),不得上传文件或枚举本地证据目录。完整 XML 永不上传。 + +## 验收要点 + +- Migration 覆盖升级/重开、外键/唯一/append-only 约束、既有事实与安全降级保护。 +- 覆盖严格 HTTP/JSON、body 前认证、claim/session/generation/token/lease/authorization 校验和跨设备拒绝。 +- 覆盖批次原子性、序号连续、并发追加、相同 key 重放、异载荷冲突、服务重启恢复。 +- 覆盖步骤状态机及 summary 单向写入;闸门金额保持十进制字符串,冲突值不得覆盖。 +- 覆盖 `CLAIMED→ORDERING` 唯一转换、固定失败到 NEEDS_MANUAL、重复失败幂等、失败后无事件、授权/claim 不释放。 +- 静态检查确认没有 selector/coordinate/free script、claim 创建/续租、围栏、提交订单点击、付款或自动重试能力。 +- `go test ./...`、`go test -race ./...`、`go vet ./...`、`go build ./...`、完整 init、上下文校验与 diff-check 全部通过。 + +## 执行记录 + +(暂无) + + +## 边界 + +- T-205 只向 T-302 已创建且由当前设备/会话持有的 attempt 追加固定结构事件和摘要;不得创建 + purchase attempt、领取任务、续租、签发或释放授权。所有权、generation、claim token、租约、 + authorization 与状态任一不匹配时零业务写入。 +- 事件必须 append-only、attempt 内严格连续且批次全有或全无;重放只能返回原结果。任何旧序号、 + 缺口、异载荷、跨 attempt、跨设备或并发冲突不得覆盖、跳过或重排既有事实。 +- 只接收版本化白名单中的 step/outcome/reason 及该步骤必需的规范金额、正整数数量和受控引用。 + 不接收自由页面文本、完整 XML、地址、手机号、本机路径、token、动作脚本、选择器或坐标。 +- 实际规格、闸门一/二单价、数量读回和确认页金额只能由对应固定成功步骤首次写入执行摘要;后续 + 冲突值必须拒绝,不能最后写入者覆盖。金额始终是十进制字符串,不用浮点数。 +- 围栏前失败只把 attempt 置 `FAILED`、task 置 `NEEDS_MANUAL` 并留下终止事实;authorization 与 + claim 保持占用,等待 T-207 人工检查。不得自动回 `DRAFT`、释放、重新授权、转领或重试页面动作。 +- 不实现或引用 submission fence、`click_permitted`、订单结果、提交订单点击。不得编写支付、免密 + 支付、先用后付或任何扣款控件代码;第一趟试选/围栏前路径不得可达下单函数。 +- 不扩展 T-204 的截图 kind,不上传 XML/manifest/目录,不接触真机页面判据或 client 代码。