96 lines
9.0 KiB
Markdown
96 lines
9.0 KiB
Markdown
---
|
||
id: T-306
|
||
title: 规格面板原始截图与可靠证据上传
|
||
phase: 3
|
||
deps: [T-104, T-303]
|
||
status: TODO
|
||
created: 2026-08-04
|
||
vikunja_task_id: 38
|
||
context_ref: 3a0a41d
|
||
work_branch: task/t-306-evidence-upload
|
||
needs_device: false
|
||
needs_human_review: false
|
||
write_paths:
|
||
- docs/tasks/T-306.md
|
||
- client/src/cmbuyer_client/evidence/**
|
||
- client/tests/evidence/**
|
||
---
|
||
|
||
<!-- BEGIN VIKUNJA EXPORT id=38 synced=2026-08-04T15:31:58Z sha256=9d3d86a2c34aab67a4ada76f3735ee8c79eddb89b1112b697f43b108a629af57 -->
|
||
## 问题 / 背景
|
||
|
||
T-104 将在规格选择、闸门一读价、原始截图和严格安全退出全部成功后返回显式 PNG 路径与实际 UTC 截图时刻;T-303 提供受设备认证的 EvidenceSink 与可崩溃恢复的唯一上传槽。T-306 只把调用方显式给出的这张 Gate1 规格面板原始截图可靠上传为内部审计证据,不连接 UI/轮询、不扩展页面能力或服务端证据类型。
|
||
|
||
服务端当前只以设备主体与 upload_key 保证幂等,并没有数据库级 `(attempt_id, kind)` 唯一约束。因此“一个 attempt 只发布一张 Gate1”必须由 T-303 本地唯一槽失败闭合保证;T-306 不能在本地状态丢失或损坏后生成新 key、扫描目录或寻找替代资产。
|
||
|
||
## 关联需求与交互
|
||
|
||
- 功能:F-007、F-013。
|
||
- 用户故事:US-004、US-005。
|
||
- 依赖:T-104、T-303;T-103 由 T-104 传递,T-204/T-302 由 T-303 传递。
|
||
- 后续消费者:T-305 围栏前 dry-run。
|
||
- 本任务可与 T-304、T-205 并行,不修改 app/polling/ui、remote/localstate、PDD 或 admin。
|
||
|
||
## 方案
|
||
|
||
1. 唯一公开接口固定为 `publish_gate1(active_claim, *, screenshot_path: Path, captured_at: datetime) -> AssetRef`。调用方不能传入 kind、privacy tier、task id 或 attempt id;接口不接受目录、manifest 或任意文件选择结果。
|
||
2. Publisher 内部固定 `kind=SKU_PANEL_GATE_1`、`privacy_tier=INTERNAL_RAW`。task/attempt/device/session/generation 归属只取 T-303 的完整 active_claim,不拆散、不重建,也不允许调用方参数覆盖。
|
||
3. `screenshot_path` 与 UTC-aware `captured_at` 只来自 T-104 成功结果。T-104 只有在规格选择、闸门一读价、原始截图和严格安全退出全部成功后才返回;post-exit 商品详情截图不得冒充 Gate1。
|
||
4. Publisher 只做窄参数适配并对 T-303 EvidenceSink 调用一次,不直接实现或调用 HTTP、SQLite、UUID、DPAPI、localstate,也不导入 PDD、ADB、uiautomator2。每次 publish 调用最多一次 sink;异常或结果不明不得内部循环重试。
|
||
5. `lstat`、常规文件与 PNG 判定、文件 identity/大小/SHA-256、固定安全 multipart 文件名、首次 HTTP 前持久化以及 `(attempt_id, evidence_kind)` 唯一 prepare-or-resume 槽全部由 T-303 负责。T-306 不另建缓存、数据库、key 或恢复状态。
|
||
6. 本地上传槽缺失、损坏或无法与完整 active_claim 核对时必须零 HTTP 失败闭合。不得生成新 upload key、枚举目录、读取 manifest、查询相似资产、改用另一张截图或重新截图;结果不明只能经 T-303 以原 key、原字节和原元数据恢复。
|
||
7. T-303 对 201 首次成功和 200 稳定重放严格解析 AssetRef;Publisher 再核对 AssetRef 的 task/attempt 与 active_claim 一致,kind/tier 与内部固定值一致。fake sink 返回错误 task、attempt、kind 或 tier 时必须拒绝,不能把不一致引用交给后续事件。
|
||
8. 成功 AssetRef 继续保存在原槽;重复调用只返回同一结果,不能清 pending/success 后生成新 key。claim 后续关闭时,只允许原 key 对既有成功资产的稳定重放,不允许创建新槽。
|
||
9. 原始 PNG 字节保持不变,可以包含页面已显示的地址和手机号;不遮罩、不裁剪、不重编码、不 OCR,也不把页面内容解析为字段、日志或异常。完整 XML、manifest、本机路径、原文件名、外部支付页和支付凭据永不上传。
|
||
10. multipart 只使用 T-204 已冻结的字段,不增加 claim token/session/path,不扩展 SKU_PANEL_GATE_2、确认页或其他 kind;不新增页面判据、PDD 动作、事件、围栏、结果或提交能力。
|
||
|
||
## 验收要点
|
||
|
||
- 静态签名精确为 `publish_gate1(active_claim, *, screenshot_path: Path, captured_at: datetime) -> AssetRef`;kind/tier/task/attempt 不能成为调用方参数,内部只固定 Gate1/InternalRaw。
|
||
- T-104 成功产物只调用一次 sink;安全退出失败、无 active claim、claim 归属不一致、UTC 时间非法、文件缺失或变化均零 HTTP,且 Publisher 不做内部重试。
|
||
- 首次传输前 T-303 唯一槽已持久化;结果不明和重启后 key/bytes/metadata 原样重放,重复成功返回同一 AssetRef,关闭 claim 后只能重放既有成功资产。
|
||
- 本地槽缺失、损坏或不可验证时零 HTTP,证明不会生成新 key、扫描目录、读取 manifest、寻找替代文件或重新截图。
|
||
- fake sink 分别返回错误 task、attempt、kind、tier 的 AssetRef 时 Publisher 全部拒绝;正确 AssetRef 原样返回。
|
||
- 用包含地址/手机号的合成有效 PNG 证明传给 sink 的原始字节完全不变;日志和异常不含页面正文、token、本机路径或原文件名。
|
||
- 拒绝目录、XML、manifest、JPEG、post-exit 截图和未批准 kind;静态检查没有目录枚举、图像变换/OCR、HTTP/SQLite/UUID、uiautomator2、PDD/ADB、数量、确认页、围栏、提交订单或付款能力。
|
||
- client 全量 unittest、compileall、wheel metadata、完整 init、Vikunja 导出、上下文校验与 diff-check 通过。
|
||
|
||
## 执行记录
|
||
|
||
### 2026-08-04T13:47:38Z · ila
|
||
|
||
2026-08-04 预研定值:T-306 收窄为 T-104 gate1 单张原始 PNG 到 T-303 唯一持久上传槽的可靠适配,因此可与 T-304/T-205 并行;不扩展服务端 kind、不另建 SQLite、不连接真机或 UI。
|
||
<!-- END VIKUNJA EXPORT -->
|
||
|
||
## 边界
|
||
|
||
- 唯一发布接口固定为
|
||
`publish_gate1(active_claim, *, screenshot_path: Path, captured_at: datetime) -> AssetRef`。调用方不得
|
||
传入 kind、privacy tier、task id 或 attempt id;`kind=SKU_PANEL_GATE_1` 与
|
||
`privacy_tier=INTERNAL_RAW` 只能由 Publisher 内部固定,task/attempt/device/session/generation 归属
|
||
只能取自 T-303 的完整 `active_claim`,不得拆散、重建或由其他参数覆盖。
|
||
- `screenshot_path` 与 UTC-aware `captured_at` 只能来自 T-104 在规格选择、闸门一读价、规格面板原始
|
||
截图和严格安全退出全部成功后返回的显式结果。不得枚举目录、读取 manifest、使用文件选择器,也不得
|
||
把 post-exit 商品详情截图或其他文件冒充规格面板证据。
|
||
- 只发布一个 `kind=SKU_PANEL_GATE_1`、`privacy_tier=INTERNAL_RAW` 的原始 PNG;task/attempt 只来自
|
||
当前 active claim。不得增加 gate2/确认页 kind,不得修改 admin schema/API,也不得自行向 multipart
|
||
增加 claim token、session、路径、原文件名或其他字段。
|
||
- Publisher 每次调用只适配一次 T-303 `EvidenceSink`,不得直接实现或调用 HTTP、SQLite、UUID、DPAPI
|
||
或本地状态细节,也不得导入 PDD/ADB。`lstat`、常规 PNG 判定、文件 identity/大小/SHA-256、首次发送
|
||
前原子持久化和 `(attempt_id, evidence_kind)` 唯一恢复槽全部由 T-303 负责。
|
||
- 服务端当前只保证设备主体与 `upload_key` 幂等,不保证数据库级 `(attempt_id, kind)` 唯一。因此本地
|
||
上传槽缺失、损坏或无法验证时必须在零 HTTP 下失败闭合;绝不能生成新 key、扫描目录、读取 manifest、
|
||
查询相似资产或重新截图来“恢复”。结果不明只能经 T-303 以原 key、原字节、原元数据恢复。
|
||
- T-303 返回的 AssetRef 必须再次匹配当前 active claim 的 task/attempt 以及固定 kind/tier;fake sink
|
||
返回错 task、attempt、kind 或 tier 时 Publisher 必须拒绝,不能把不一致引用交给后续事件。成功
|
||
AssetRef 继续保存在原槽,重复调用只能返回同一结果,不能清槽后生成第二个 key。
|
||
- 每次调用最多一次 HTTP,不在内部循环重试。文件缺失或 identity/大小/hash 变化时必须零 HTTP 并
|
||
转人工;不得重新截图、替换文件或在目录中寻找“相似”截图。
|
||
- 停止轮询、Esc、关闭窗口和进程退出不取消在途上传、不清 pending、不删除原图、不 release/abandon
|
||
claim,也不触发任何 ADB/PDD 动作。重启只可恢复同一 HTTP 请求,不自动恢复设备页面操作。
|
||
- 原始 PNG 字节必须保持不变,可以包含页面已显示的地址和手机号;不得遮罩、裁剪、重编码、OCR,
|
||
也不得把地址或手机号解析为业务字段、日志或异常文本。完整 XML、manifest、本机路径、页面全文、
|
||
外部支付页和支付凭据不上传。
|
||
- 不导入或实现 uiautomator2、PDD 页面判据、通用 click、规格/数量/确认页动作、事件、失败上报、
|
||
提交围栏或结果接口;不编写或引用点击“提交订单”的代码,不编写支付或任何扣款能力。
|