merge: T-204 routed task evidence details

This commit is contained in:
QiuSW
2026-08-04 19:43:20 +08:00
28 changed files with 2997 additions and 26 deletions
+34
View File
@@ -246,6 +246,27 @@ CREATE TABLE order_submissions (
UNIQUE (authorization_id),
UNIQUE (attempt_id)
);
-- INTERNAL_RAW 原始截图;原文件名和客户端路径不进入数据库
CREATE TABLE evidence_assets (
id TEXT PRIMARY KEY,
upload_key TEXT NOT NULL,
task_id TEXT NOT NULL,
attempt_id TEXT NOT NULL,
kind TEXT NOT NULL, -- T-204 仅 SKU_PANEL_GATE_1
privacy_tier TEXT NOT NULL, -- 仅 INTERNAL_RAW
sha256 TEXT NOT NULL, -- 64 位小写十六进制
byte_size INTEGER NOT NULL,
content_type TEXT NOT NULL, -- 仅 image/png
width_px INTEGER NOT NULL,
height_px INTEGER NOT NULL,
storage_key TEXT NOT NULL, -- 由 SHA-256 唯一派生
uploaded_by_device_id TEXT NOT NULL,
captured_at TEXT NOT NULL,
created_at TEXT NOT NULL,
UNIQUE (uploaded_by_device_id, upload_key),
FOREIGN KEY (task_id, attempt_id) REFERENCES purchase_attempts(task_id, id)
);
```
MVP 不再用 `spec_trials` 作为审批记录,也不存在 `authorized_unit_price`。实际读价属于
@@ -306,6 +327,19 @@ DRAFT / PENDING / NEEDS_MANUAL ─管理员取消(围栏前)→ CANCELED
截图上传器只能接收调用方显式指定的截图,不能枚举证据目录或顺带上传 XML/manifest。证据响应
使用 `Cache-Control: no-store`,不能暴露为免登录静态目录。
内部截图存储采用以下固定边界:
- 单个 PNG 最大 10 MiB、单边最大 8192 px、总像素最大 16,777,216;同时验证 multipart MIME、
PNG 魔数、完整解码、字节数、尺寸和调用方声明的 SHA-256。
- 上传 handler 必须先通过设备认证,再解析 Content-Type 或读取 body。T-301 前生产认证器固定拒绝,
不创建临时 token,也不把管理员 session 当设备身份。
- 文件写入显式配置的私有证据根目录:同目录随机临时文件 → 流式 hash → 校验 → `fsync` → 原子
rename 到 SHA-256 内容地址 → 最后事务写数据库。数据库永远不指向半文件或缺失文件。
- SQLite 与文件系统不能组成跨资源事务;极端故障最多留下不可达孤儿文件。不得为清理孤儿而删除
可能被其他资产记录并发复用的内容文件,自动保留/删除策略留给部署任务。
- SHA-256 只用于物理内容寻址,不是业务资产唯一键;不同合法证据可以引用相同内容。同设备主体与
`upload_key` 同载荷重放原资产,任一规范字段变化即冲突。
## 六、关键技术难点
| 难点 | 风险 | 应对 |
+25 -4
View File
@@ -53,6 +53,14 @@
| `POST` | `/tasks/{id}/mark-paid` | 人工确认已付款并完成核对 |
| `GET` | `/evidence/{asset_id}` | 登录后读取内部截图;`Cache-Control: no-store` |
`GET /tasks/{id}` 的完整页与列表抽屉共享同一服务端数据模型和详情模板。列表只可用同源请求携带
`X-CMBuyer-View: drawer` 获取 HTML fragment;其他非空 view、跨站 fragment 请求或不接受
`text/html` 的 fragment 请求均拒绝。直接导航同一 URL 始终返回完整页。
`GET /evidence/{asset_id}` 不经静态目录:未登录先返回 `401`,不查询和泄露资产是否存在;登录后
缺失或畸形 id 返回空 `404`。成功只返回存储的 PNG,包含 `Content-Length`、固定安全文件名、
`Cache-Control: no-store` 与 `X-Content-Type-Options: nosniff`,不返回原文件名或服务端路径。
### `POST /tasks`
核心字段:
@@ -190,17 +198,30 @@
```json
{
"attempt_id": "018f-attempt",
"upload_key": "43c9f507-7473-4fa6-8d71-8786c34c6301",
"attempt_id": "33c9f507-7473-4fa6-8d71-8786c34c6301",
"kind": "SKU_PANEL_GATE_1",
"privacy_tier": "INTERNAL_RAW",
"sha256": "64-lowercase-hex",
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"captured_at": "2026-08-04T09:01:00Z"
}
```
- 允许规格面板和确认页截图保留页面已显示的地址/手机号;不要求遮罩或裁剪。
- 不接受 XML、目录、manifest、本机绝对路径、外部支付页截图或支付凭据。
- MIME、尺寸、字节数和 SHA-256 必须校验;资产只经管理员鉴权端点读取。
- T-204 只开放 `kind=SKU_PANEL_GATE_1`;后续 kind 必须由对应真机证据任务收紧扩展。
- `privacy_tier` 只能是 `INTERNAL_RAW`;时间必须是以 `Z` 结尾的 UTC RFC 3339。
- URL 中的 task id、`upload_key` 与 `attempt_id` 都必须是规范的小写 UUIDv4;`sha256` 必须是
恰好 64 位小写十六进制字符。
- 恰好一个带 `Content-Type: image/png` 的显式文件;除上述六个元数据字段外,未知或重复字段均拒绝。
- 单文件最多 10 MiB、单边最多 8192 px、总像素最多 16,777,216;服务端校验 PNG 魔数、完整解码、
字节数、尺寸与调用方声明的 64 位小写 SHA-256。
- `attempt_id` 必须由数据库复合外键证明属于 URL 中的 task。认证必须先于 Content-Type 解析和请求体读取。
- 同一设备主体和 `upload_key` 的同载荷重放返回原资产;任务、attempt、截图或元数据变化返回 `409`。
- 首次成功返回 `201`,幂等重放返回 `200`。响应只含资产 id、关联 id、kind/tier、hash、字节数、
MIME、宽高和采集时间,不含设备 token、原文件名或存储路径。
- T-301 接入真实设备 Bearer 身份前,生产 `DeviceAuthenticator` 固定拒绝全部上传;不得使用管理员
session、临时 token 或共享密钥代替设备身份。
### `POST /api/v1/purchase-attempts/{aid}/submission-fence`
@@ -310,6 +331,6 @@ T-103 只实现隔离的 `SkuSelectionFlow`:前四项加安全退出。它的
## 四、实现前仍需定值
- 授权有效期、领取租约时长、心跳/轮询间隔和连续失败停止阈值;
- 截图大小上限和内部保留期限;
- 内部截图保留期限;截图大小上限已固定为 10 MiB / 8192 px 单边 / 16,777,216 像素;
- 可配置单任务数量与最高总价系统上限;
- 首次真实提交真机任务的人工授权和待付款订单处置步骤。
+11 -1
View File
@@ -47,7 +47,9 @@
| 创建时间 | 本地时区显示,数据按 UTC 保存 |
没有操作列。双击非控件区域或键盘 Enter 打开 `/tasks/{id}` 路由化详情抽屉;新 tab 直接访问同 URL
则显示完整详情页。关闭抽屉或浏览器返回恢复筛选、滚动和触发行焦点。
则显示完整详情页。标题下方同时提供可见“查看详情”按钮,双击不是唯一入口。商品外链、checkbox、
输入和按钮本身不触发行双击。抽屉成功加载后才把 URL 推进 `/tasks/{id}`;关闭、Esc 或浏览器返回
恢复筛选、滚动和触发行焦点,浏览器前进重新打开同一详情且不重复写 history。
### 批量开始采购
@@ -91,6 +93,14 @@
详情中不出现 `WAITING_CONFIRMATION`、“确认机器选对了吗”、“签发第二趟授权”或“重新试选”。
完整页与抽屉执行同一 `task-detail-content` 模板和只读查询。直接导航返回完整 SSR 文档;列表 JS 对
同一 URL 发出同源 `X-CMBuyer-View: drawer` 请求,只取得 HTML fragment。加载失败时抽屉提供重试和
“在完整页打开”,不会把失败请求伪装成已打开详情。
T-204 只显示数据库中当前实际存在的任务、授权、attempt、submission 和内部截图,缺少事实就显示
明确空态;它不创建 attempt/event,不计算闸门,也不提供重置、调和、标记付款或任何设备动作。
截图以服务端记录的宽高预留布局并延迟加载,alt 只描述证据种类和采集时间,不转录截图中的地址或手机号。
## 四、采购工具界面结构
应用名:**采购工具**。顶部固定 tab: