docs: import wiki at afc651f75a3a

ila
2026-08-07 16:36:55 +08:00
parent da904da701
commit 798da820bf
+110
@@ -0,0 +1,110 @@
<!-- docs-wiki-sync:docs/tasks/T-245.md@afc651f75a3abc2676bb13aa8a80f6aa6a25a72e -->
> 同步来源:[`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。