Files
cmbuyer/docs/tasks/T-203.md
T

77 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-203
title: 表格查询与批量开始采购授权
phase: 2
deps: [T-202, T-209]
status: DOING
created: 2026-08-04
vikunja_task_id: 30
context_ref: 1f20271
work_branch: task/t-203-start-purchases
needs_device: false
needs_human_review: false
write_paths:
- docs/tasks/T-203.md
- admin/internal/auth/**
- admin/internal/tasks/**
- admin/internal/server/**
- admin/internal/transport/webui/**
- admin/internal/config/**
- admin/cmd/server/**
- admin/README.md
---
<!-- BEGIN VIKUNJA EXPORT id=30 synced=2026-08-04T09:16:48Z sha256=6c62ba368a760e96b8feb06f3f3ced8a2a40d10f7754a99471f7429c3862a80c -->
## 问题 / 背景
T-202 已完成手工 DRAFT 建单;T-209 将生产 schema/领域状态机迁移到单趟模型。项目所有者明确:管理员点击“开始采购(只创建待付款订单)”本身就是授权,不再增加试选后确认。T-203 负责采购服务查询和批量授权事务,使设备后续只能领取显式授权的 PENDING 任务。
## 关联需求与交互
F-004、F-008、F-018;US-003、US-005;IX-005;GET /tasks、POST /tasks/start-purchases;依赖 T-202、T-209。
## 方案
1. GET /tasks 支持 keyword、status、created_from、created_to;日期按 Asia/Shanghai 转为 UTC 半开区间,非法筛选返回可访问字段错误;默认全部状态并按 created_at DESC,rowid DESC。
2. 页面只让 DRAFT 行可勾选;表格上方显示选中数量、最高总额字符串合计、“系统不会付款”和唯一主按钮“开始采购(只创建待付款订单)”,不加逐行操作或重复确认弹窗。JS 只用同源静态文件,金额以分/BigInt 累计,不用浮点。
3. POST 使用服务端生成并渲染的 UUID v4 start_key,接收非空去重任务 id + expected_task_version;批量上限 100。created_by 只取已认证管理员,不接受请求字段。
4. 在一个有界 SQLite 写事务内先按 start_key 检查重放,再按 task_id 稳定排序读取并复核全部任务:存在、DRAFT、版本相等、锁定字段完整、数量/总额上限满足显式配置。任一失败整批不变。
5. 新请求为整批使用同一 created_at/expires_at;逐条创建 ACTIVE 一次性授权,锁定新 task_version、goods_id、颜色、尺码、数量和 total_price_cap;条件更新每条 DRAFT/version 为 PENDING/version+1,任一 RowsAffected != 1 则全批回滚。
6. 相同 start_key + 相同规范集合(输入顺序无关)返回原 authorization ids/版本/有效期,不再次改任务;同 key 子集、超集、不同版本或残缺集合返回 409。网络结果不明时前端冻结原 key/载荷,只允许原样重放。
7. 显式配置并启动时校验授权 TTL、最大任务数量、最大总额;金额只用规范十进制字符串。T-207 才关闭过期授权,T-203 只写 expires_at。
## 验收要点
- 覆盖单条/100条成功,任务版本仅加一次、快照逐字段相等、管理员来自服务端、UTC 过期时间正确。
- 任一缺失/非 DRAFT/版本冲突/字段不完整/数量或金额超限/中途 SQL 失败均整批零修改。
- 同 key 同集合、倒序集合、并发重放返回同一结果;同 key 不同集合冲突;不同 key 并发抢同一版本仅一个成功。
- 未登录、CSRF、空/重复/畸形/超大请求、配置边界和数据库故障 fail closed,不泄露内部错误。
- UI 只有 DRAFT 可选;筛选、全选当前可见项、选择反馈、网络不明重放、焦点/aria-live/横向滚动与不付款文案有测试;无同义确认弹窗。
- go test ./...、go test -race ./...、go vet ./...、go build ./...、node --check 静态 JS、完整 init.ps1、上下文校验和 diff-check 通过。
## 执行记录
### 2026-08-04T09:16:39Z · ila
2026-08-04 开始 T-203:依赖 T-209 已完成并合入 main。主 agent 已完成开工前只读审计,冻结 v2 schema 启动校验、单进程 writeGate、start_key 规范集合重放、Asia/Shanghai 到 UTC 半开区间、julianday 查询及 big.Int 分金额边界;本地状态转 DOING,分支 task/t-203-start-purchases。
<!-- END VIKUNJA EXPORT -->
## 边界
- 本任务只实现管理员任务查询与批量“开始采购”授权;不实现任务详情/截图、设备 Bearer 身份、领取/
租约、purchase attempt API、提交围栏、结果调和、真机自动化、创建订单点击或付款。
- “开始采购(只创建待付款订单)”按钮本身就是明确授权,不得再增加同义确认弹窗,也不得把它拆回
试选后确认。按钮附近必须持续显示系统不付款;页面不得提供逐行“开始采购”操作列。
- 只有 `DRAFT` 行可以勾选;批量事务必须全有或全无。任一任务缺失、状态/版本变化、锁定字段非法、
配置超限、授权插入失败或条件更新未命中,都不得留下部分授权或部分 `PENDING`。
- 幂等重放必须先于 DRAFT 状态检查:相同 `start_key` 和相同规范任务集合只返回原结果,任务版本不得
再增加;同 key 的子集、超集、不同 expected version 或残缺授权集合一律冲突。网络结果不明只能
原样重放同一个 key 和载荷,不能生成新 key。
- 授权只锁定任务的新版本、goods_id、颜色、尺码、数量和 `total_price_cap`;不得写入观察单价或
`authorized_unit_price`。金额校验、配置比较和浏览器合计均使用十进制字符串/整数分,不用浮点。
- `created_by` 只能来自已认证管理员会话;POST 必须验证 CSRF。设备凭据不能调用本接口,本任务也不
新增设备接口。内部数据库错误不得回显 SQL、路径、配置值或凭据。
- 过期授权的关闭/重置属于 T-207;本任务只创建 `expires_at`。授权一旦进入 `FENCED`,本任务没有
释放、取消、重新授权或重试入口。
- 本任务不实现、不引用通用真机点击、`submit_order_once()` 或任何支付、免密支付、先用后付能力。