9.1 KiB
9.1 KiB
id, title, phase, deps, status, created, context_ref, work_branch, write_paths
| id | title | phase | deps | status | created | context_ref | work_branch | write_paths | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| T-203 | 实现任务创建 API 与管理 Web | 2 |
|
DONE | 2026-07-26 | 2b265c9 |
main |
|
问题 / 背景
T-201 只有健康检查、SQLite 和 migration 骨架,T-202 只确认了页面信息结构。当前还
不能从管理端安全上传参考图、创建 PENDING 任务、查看真实列表/详情或取消待领取
任务,因此 T-205 的原子领取没有可消费的数据。
审计还发现正式合约存在四个会阻断后续 App 接入的问题:
- 现有 Android
ProbeTask强制标题和 SKU 非空,但任务创建 API/数据表遗漏 SKU。 - 已确认 Web 使用“最高总预算”,部分 Android 原型误写成每件预算。
- API/原型允许 JPEG/PNG/WebP,但 Android 需求提取当前只接受 JPEG。
- 管理会话属于 T-204,T-203 不能伪造登录或永久开放匿名管理接口。
关联需求与交互
- 功能:F-001、F-002,以及 F-007 的最小可见错误。
- 用户故事:US-001、US-002。
- 交互:IX-002、IX-003;登录 IX-001 留给 T-204。
- 页面:
/tasks、/tasks/new、/tasks/{id}。 - API:资产上传、任务创建/列表/详情、待领取任务取消。
已定合约
- 标题和 SKU 必填;描述可空。标题最多 120 个 Unicode 字符且不超过 2048 个 UTF-8 字节,SKU 不超过 512 个 UTF-8 字节,描述不超过 8192 个 UTF-8 字节。
max_budget表示当前任务全部数量的最高商品总预算,币种 CNY,不含无法确认的 运费或优惠;数据库用整数分保存,API 固定输出两位小数字符串。- 参考图上传接受可解码 JPEG/PNG/WebP,原始请求最多 20 MiB、最长边最多 10000 px、总像素最多 25 MP;后端统一缩放到最长边不超过 2048 px、白底合成并 编码为匿名 JPEG。响应的媒体类型、大小和 SHA-256 都指向规范化结果。
- SKU、数量和总预算属于不可变原始事实,任何派生结果都不能覆盖。
- 列表增加
q,匹配任务 ID、来源引用、标题或 SKU;分页固定按created_at DESC, id DESC,cursor 同时包含两个字段。 - T-203 只提前实现事务化
PENDING -> CANCELED;其他状态取消返回冲突,完整取消 请求和状态机属于 T-205。 - T-204 前,管理 Web/API 业务路由只接受 loopback 来源;Web 写操作还必须携带
SameSite Cookie 与表单字段匹配的随机双提交 CSRF token,API 创建/上传必须使用
JSON 或 multipart 与自定义幂等头。
/login不提供假实现,局域网部署仍被明确 阻塞。 - 创建、上传均按调用主体、操作和
Idempotency-Key幂等;同 key 同请求返回原 资源,同 key 不同请求返回409。
方案
- 增加 assets、purchase_tasks、task_events 和 idempotency_records migration, 包含外键、CHECK、唯一约束和 rollback。
- 建立 domain/usecase/repository/sqlite 分层;handler 和模板不得直接执行 SQL。
- 建立受控文件存储与图片规范化器,随机相对路径、同目录临时文件原子改名,失败时 清理;数据库和响应永不暴露绝对路径。
- 实现 JSON/multipart API 和稳定错误映射,保持无默认 CORS、request ID 与 recovery 安全约束。
- 用
html/template、embed、原生 CSS/JS 实现列表、新建和详情;真实数据为空时 显示空状态,不把原型假数据写入数据库。 - Web 表单无 JS 也可完成,使用 PRG
303;JS 只负责图片预览、防重复、离开提醒 和取消确认。
验收要点
- migration 可 up/down/up,约束和外键有效,启动前 migration 门禁明确。
- JPEG/PNG/WebP 被真实解码并规范化为 JPEG;伪 MIME、损坏、超限和像素炸弹拒绝。
- 合法任务创建为唯一
PENDING,原始 title/SKU/quantity/budget/image 不可变。 - 上传和创建幂等;同 key 不同请求冲突,并发重试不产生重复资源。
- 列表筛选、搜索、稳定 cursor 和详情返回真实数据,404 不泄露内部信息。
- 只有
PENDING可立即取消;终态或不支持状态返回稳定冲突。 - Web 覆盖空列表、创建字段错误、图片错误、成功跳转、详情和取消二次确认。
- 非 loopback 业务请求、缺失/错误 CSRF、错误 Content-Type 被拒绝。
- HTML 自动转义,日志/响应不含 Cookie、文件绝对路径、私有样本或请求正文。
- Go test/race/vet/gofmt、迁移 smoke、真实 HTTP/Web smoke 和 Playwright 通过。
边界
- 不实现管理登录、Cookie 会话、用户表/密码哈希或设备鉴权;属于 T-204。
- 不实现 claim、租约、执行状态机、候选、执行证据或 App HTTP TaskSource。
- 不开放非 loopback 管理使用;T-204 完成前不能部署到局域网供其他用户访问。
- 不实现对象存储、ORM、SPA、WebSocket、推送或自动下单。
- 不提交运行时数据库、上传文件、密钥或真实订单/店铺/商品数据。
执行记录
2026-07-26:任务开始
- 基于提交
2b265c9开始,工作区干净。 - codebase-memory MCP 本轮未暴露 graph 工具,按项目规则回退到
rg和定点文件读取。 - 已审计 T-201 现有 config/database/migration/httpapi/cmd 骨架、T-202 原型以及 requirements/API/architecture/IX,先固定上述跨层合约再编码。
2026-07-26:合约和实现
- 修正正式需求和原型中的 SKU、最高总预算、图片格式与验收编号;总预算统一指当前 任务全部数量的商品预算,模型不得覆盖 SKU、数量或预算。
- 新增
00002_tasks_and_assets.sql,建立 assets、purchase_tasks、task_events 和 idempotency_records;字段长度、UTF-8 字节、外键、状态、金额和唯一性均有数据库 约束。 - 建立 domain/usecase/repository/sqlite 分层,上传和创建事务化幂等,列表按
created_at DESC, id DESC稳定分页,待领取取消使用状态与 version 条件更新。 - 文件层真实解码 JPEG/PNG/WebP,限制 20 MiB、10000 px 和 25 MP,白底缩放至最长 边 2048 px 后以质量 90 编码为随机相对键 JPEG;绝对路径不进入数据库外部响应。
- Gin API 实现上传/读取资产、创建/列表/详情/取消任务;业务路由在 T-204 前只接受 loopback,错误使用稳定 code、request ID 和 no-store 响应。
- 管理 Web 使用嵌入模板/CSS/JS,实现真实空列表、搜索筛选、创建、图片预览与复用、 详情参考图和取消确认;使用双提交 CSRF、PRG 303、HTML 自动转义和固定 CSP。
- API 启动前检查全部 migration;存在 pending 版本时拒绝启动并明确提示执行
migrate up,服务进程不自动修改数据库。
2026-07-26:自动验证
GOTOOLCHAIN=local go test -count=1 ./...:99 个测试通过;覆盖领域校验、金额、 图片限制、路径防护、迁移约束、事务幂等、并发、分页、HTTP 生命周期、CSRF 和 模板安全。go test -race -count=1 ./...、go vet ./...、gofmt -l cmd internal migrations和 API/migration Windows 构建全部通过。- 真实 migration CLI 完成
status -> up -> up -> down -> API gate -> up:首次applied=2、重复为 0,回滚 v2 后 API 以退出码 1 拒绝旧库,恢复后 v1/v2 均applied。 httptest使用临时真实 SQLite/asset 目录跑通:上传及幂等重放、创建及重放、SKU 搜索、详情、JPEG 内容读取、取消和重复取消;非 loopback 和缺失幂等键被拒绝。- Playwright 连接真实 Gin/SQLite 进程完成创建任务和详情读取;1440x900、
390x844、360x800 均无横向溢出,可见操作控件最小 44 px,规范化参考图
naturalWidth=64、naturalHeight=48且成功加载,只有同源 HTML/CSS/JS/资产请求, 新页面 console 为 0 error、0 warning;取消对话框默认聚焦“保留任务”。 - 根
init.ps1通过:Androidtest assembleDebug共 76 个 Gradle task 成功, 现有 166 次测试保持通过;随后 Go 测试、vet、格式检查和两个入口构建再次通过。
2026-07-26:隐私与收尾
- 测试只使用代码生成的脱敏小图、标题和 SKU;未读取、复制或提交本地蝦皮订单、 店铺名、真实商品文字或图片。
- Playwright、数据库、资产和 exe smoke 产物只保存在被忽略的
.local/、bin/或var/,未纳入 Git;业务响应和管理页面不显示客户端文件名或服务端存储路径。 - T-203 验收全部满足,状态改为
DONE;T-204 可开始实现正式管理会话和角色门禁。