diff --git a/T-245.-.md b/T-245.-.md new file mode 100644 index 0000000..da3cc14 --- /dev/null +++ b/T-245.-.md @@ -0,0 +1,110 @@ + +> 同步来源:[`docs/tasks/T-245.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/tasks/T-245.md) · commit `afc651f75a3a` + +--- +id: T-245 +title: 创建采购需求时采用 ERP 商品图 +phase: 2 +deps: + - T-243 + - T-244 +status: DONE +created: 2026-07-29 +context_ref: 9195019 +work_branch: null +write_paths: + - docs/tasks/T-245.md + - docs/current-state.md + - backend-api/migrations/00017_procurement_reference_sources.sql + - backend-api/internal/domain/** + - backend-api/internal/usecase/** + - backend-api/internal/repository/sqlite/** + - backend-api/internal/transport/httpapi/** + - backend-api/internal/transport/webui/** + - backend-api/cmd/api/** +--- + +## 需求 / 背景 + +货运详情已经展示并缓存 ERP 商品图片,但采购人员确认“仍需采购”后,采购需求固定进入 +`NEEDS_IMAGE`,还要重新选择并上传同一张图片。ERP 图片与当前货运商品直接关联,通常是 +图片搜索最准确、操作成本最低的默认参考图。 + +现有货运缓存图和采购参考资产属于不同生命周期:`freight_item_images` 会随重新同步、图片 +变化或货运删除而替换/清理;采购任务则通过 `assets / TASK_REFERENCE` 保存不可变参考图。 +因此不能让采购任务直接共享货运图片的 `storage_key`。 + +## 修订契约 + +- ADMIN 显式确认创建采购需求时,若当前商品资料完整且对应缓存图为 `READY`,系统自动将 + 本地缓存 JPEG 复制并重新校验为独立 `TASK_REFERENCE` asset,需求直接进入 `READY`。 +- 自动采用图片只读取 `CMROUBAO_ASSET_DIR` 中已验证的本地缓存,不在创建需求请求中访问 + ERP,也不依赖 ERP 会话、OCR 或网络。 +- ERP 图片不存在、`MISSING` 或 `FAILED` 时正常创建 `NEEDS_IMAGE` 需求,继续提供人工上传。 +- 标题、SKU、数量或来源取消造成资料阻塞时保持 `BLOCKED`,不创建参考资产。 +- 同商品 revision 的重复创建保持幂等;既有 `NEEDS_IMAGE` 且来源未变化时,如果 ERP 图片 + 后来变为 `READY`,重复操作可原子升级为 `READY`。 +- 自动图片必须在事务提交时仍与当前商品、图片引用、SHA-256 和 storage key 一致;并发 + 来源变化或图片替换时回滚,复制文件通过补偿清理,不留下 asset 或半绑定需求。 +- 自动采用后的参考图记录来源 `ERP_FREIGHT_IMAGE`、原图片数字引用和 SHA-256;人工上传 + 记录 `MANUAL_UPLOAD`。 +- 采购任务生成前允许人工上传替换参考图。替换自动 ERP 参考图时,事务删除旧自动 asset, + 提交后清理其独立文件;任务已生成后继续禁止替换。 + +## 方案 + +1. v17 增加 `procurement_reference_sources`,以采购需求 ID 为主键保存来源、ERP 图片引用、 + 原图片 SHA-256 和绑定时间;现有已绑定参考图回填为 `MANUAL_UPLOAD`。Down 在存在来源 + 记录时 fail-closed,避免静默丢失审计。 +2. 为 `ProcurementService` 增加可选本地参考图 store;生产组合传入现有 asset store, + 未配置的纯仓储测试保持原 `NEEDS_IMAGE` 行为。 +3. 创建需求时先构造来源快照和阻塞状态。非阻塞时读取当前 `READY` 图片元数据并打开本地 + 文件,经现有 `Put` 再次执行完整 JPEG 解码、尺寸/像素、20 MB 和 SHA-256 校验,生成 + 独立 storage key 与 asset 候选。 +4. SQLite immediate 事务统一校验商品 revision/hash、缓存图引用/hash/key,插入 asset、 + 采购需求和 ERP 来源记录;对既有 `NEEDS_IMAGE` 执行相同原子升级。事务未使用复制文件 + 时,由用例幂等删除。 +5. 人工绑定事务允许 `NEEDS_IMAGE -> READY` 或替换尚未生成任务的 `READY` 引用,来源记录 + 统一改为 `MANUAL_UPLOAD`。只删除无上传幂等记录的旧 ERP 自动 asset,文件在提交后清理。 +6. Admin API 返回 `reference_origin`;SSR 显示“已使用 ERP 商品图”或“已上传参考图”, + `READY` 状态同时提供“生成采购任务”和“更换参考图”。 +7. 任务仍只携带现有 `image_asset_id`,Roubao 领取、下载和图片搜索协议不变。 + +## 验收要点 + +- [x] 当前商品有 `READY` ERP 图时,创建需求返回 `READY`、独立 asset 和 + `ERP_FREIGHT_IMAGE` 来源。 +- [x] 自动 asset 使用不同 storage key;货运缓存图替换不会破坏采购参考图。 +- [x] 无可用 ERP 图时返回 `NEEDS_IMAGE`,人工上传路径不回归。 +- [x] 资料阻塞时不复制文件、不创建 asset,状态保持 `BLOCKED`。 +- [x] 重复创建不产生重复需求/asset;既有 `NEEDS_IMAGE` 可在图片恢复后升级。 +- [x] 商品或图片并发变化时无采购 asset、来源记录或孤立复制文件。 +- [x] `READY` 需求可人工替换;旧 ERP 自动 asset/文件被清理,任务创建后禁止替换。 +- [x] API/SSR 展示参考图来源,生成的采购任务和 Roubao 图片契约不变化。 +- [x] 已自动绑定参考图的货运单继续受 T-243 删除保护。 +- [x] v17 上下迁移、标准 Go 测试、race、vet 和三个入口构建通过。 + +## 边界 + +- 不在采购需求创建时请求 ERP,不把 ERP URL 暴露给 Admin 或 Roubao。 +- 不直接共享货运图片 storage key,不引入跨表隐式文件引用。 +- 不自动生成采购任务;采购人员仍需明确点击“生成采购任务”。 +- 不在任务生成后替换参考图,不改写已有任务快照。 +- 不自动为既有 `NEEDS_IMAGE` 批量回填;由详情操作显式触发升级。 +- 不增加通用 asset 回收站或全局引用计数;人工上传产生的历史未绑定 asset 继续沿用现状。 +- 不记录图片正文、ERP Cookie/Token 或真实订单、商品、店铺和图片 ID。 + +## 执行记录 + +- 2026-07-29:审计确认当前 `CreateRequest` 对非阻塞商品固定写入 `NEEDS_IMAGE`;ERP + 缓存图只有 `freight_item_images` 记录,而采购任务要求 `assets` 外键。确定采用独立 + 文件/asset 快照,不共享可变货运 storage key;Roubao 协议无需变化。 +- 2026-07-29:v17 已增加参考图来源记录;生产组合启用本地 ERP 缓存图复制。SQLite + 事务复核商品 revision/hash 和图片引用/hash/key 后,原子写 asset、需求及来源;重复 + 创建可升级既有 `NEEDS_IMAGE`,未使用或失败的复制文件由用例补偿清理。 +- 2026-07-29:人工绑定已支持任务生成前替换自动参考图,提交后清理旧自动 asset/文件; + API 返回 `reference_origin`,货运详情显示来源并提供“更换参考图”。任务仍使用既有 + `image_asset_id`,未改变 Roubao 协议。 +- 2026-07-29:`go test ./...`、`go test -race ./...`、`go vet ./...` 通过; + `go build ./cmd/api`、`go build ./cmd/authctl`、`go build ./cmd/migrate` 通过。 + 测试仅使用运行时生成图片和脱敏 ID,未访问真实 ERP。