81 lines
4.3 KiB
Markdown
81 lines
4.3 KiB
Markdown
---
|
|
id: T-244
|
|
title: 兼容 ERP 图片响应缺少媒体类型
|
|
phase: 2
|
|
deps:
|
|
- T-241
|
|
status: TODO
|
|
created: 2026-07-29
|
|
context_ref: 24eef29
|
|
work_branch: null
|
|
write_paths:
|
|
- docs/tasks/T-244.md
|
|
- docs/current-state.md
|
|
- backend-api/internal/platform/shunyunbao/image.go
|
|
- backend-api/internal/platform/shunyunbao/image_test.go
|
|
---
|
|
|
|
## Bug / 背景
|
|
|
|
完整单号同步成功后,商品图片可能显示“图片获取失败”。只读数据审计确认图片引用为规范正
|
|
整数,图片记录为 `FAILED / ERP_IMAGE_INVALID`,不是货运导入失败。
|
|
|
|
真实 `/api/p/file?id=<numeric-id>` 响应为 HTTP 200,正文是有效且很小的 JPEG,但 ERP
|
|
未返回 `Content-Type`、`Content-Length`,只通过 `Content-Disposition` 提供 `.jpg`
|
|
文件名。当前 Go 客户端无条件执行 `mime.ParseMediaType(Content-Type)`,空响应头在正文
|
|
进入本地图片存储前就被归类为无效图片。
|
|
|
|
图片缓存是货运同步后的 best-effort 阶段,所以该错误不会回滚已成功保存的货运头和商品;
|
|
页面只显示统一失败标签,实际稳定错误码保存在图片状态记录中。
|
|
|
|
## 修订契约
|
|
|
|
- HTTP 2xx 且 `Content-Type` 为 `image/jpeg`、`image/png`、`image/webp` 时沿用现有路径。
|
|
- `Content-Type` 缺失或规范化后为 `application/octet-stream` 时,只读取正文前最多
|
|
512 字节并使用 Go 标准库 `http.DetectContentType` 判定真实类型。
|
|
- 嗅探只接受 JPEG、PNG、WebP;空正文、JSON、HTML、SVG、未知二进制继续返回
|
|
`ErrFreightImageInvalid`。
|
|
- 已读取的前缀必须无损拼回返回流,后续本地存储继续执行完整解码、声明类型一致性、
|
|
尺寸/像素、20 MB、归一化 JPEG 和 SHA-256 校验。
|
|
- ERP 明确声明其他媒体类型时 fail-closed,不用文件扩展名覆盖响应头。
|
|
- 不信任或解析 `Content-Disposition` 文件名,不扩大 URL、重定向或 ERP 会话边界。
|
|
|
|
## 方案
|
|
|
|
1. 在顺运宝图片适配器中增加小型媒体类型解析函数:先规范化响应头,支持类型直接返回;
|
|
仅对缺失/通用二进制响应调用内容嗅探。
|
|
2. 使用固定 512 字节 `bufio.Reader` 执行 `Peek`;短图片允许 `io.EOF`,空正文拒绝。成功
|
|
时返回组合了 buffered reader 和原响应 body closer 的流,避免丢失或复制完整图片。
|
|
3. 继续在读取正文前拒绝已知超出 20 MB 的 `Content-Length`;未知长度由现有 asset store
|
|
的 `LimitReader(MaxInputBytes+1)` 强制限制。
|
|
4. 测试覆盖缺失媒体类型的 JPEG、通用二进制 PNG/WebP、短正文、明确 JSON、嗅探非图片、
|
|
声明图片类型、超大已知长度和前缀不丢失。
|
|
5. 修复部署后重新以相同完整订单号同步即可重试 `status != READY` 的图片;本任务不增加
|
|
独立重试按钮或后台定时重试。
|
|
|
|
## 验收要点
|
|
|
|
- [ ] 缺少 `Content-Type` 的有效 JPEG 保存为本系统归一化 JPEG,图片状态为 `READY`。
|
|
- [ ] `application/octet-stream` 的有效 PNG/WebP 可以进入既有解码与存储路径。
|
|
- [ ] 嗅探不会丢失正文前缀,完整内容只读取一次并最终关闭 ERP 响应 body。
|
|
- [ ] JSON、HTML、SVG、空正文、未知二进制和明确不支持的媒体类型仍被拒绝。
|
|
- [ ] 已知或流式超限图片仍受 20 MB 限制,不能绕过 asset store。
|
|
- [ ] 401/403、404、非 2xx、会话清理、固定 URL 和数字 ID 校验不回归。
|
|
- [ ] 货运同步仍为图片 best-effort,不因单张图片失败回滚业务数据。
|
|
- [ ] 标准 Go 测试、race、vet 和三个入口构建通过。
|
|
|
|
## 边界
|
|
|
|
- 不修改顺运宝 ERP,不上传或回写 ERP 图片。
|
|
- 不直接在 Admin 页面引用 ERP URL;图片仍保存到 `CMROUBAO_ASSET_DIR` 并从鉴权路由读取。
|
|
- 不接受 SVG、GIF、BMP、ICO、PDF 或任意未知格式。
|
|
- 不以扩展名、文件名或 `Content-Disposition` 替代正文验证。
|
|
- 不记录图片正文、Cookie、Token、真实订单、商品、店铺或图片 ID。
|
|
|
|
## 执行记录
|
|
|
|
- 2026-07-29:真实只读审计确认货运同步成功,单张图片失败码为
|
|
`ERP_IMAGE_INVALID`;受控响应探测确认 HTTP 200 正文为有效 JPEG,但
|
|
`Content-Type` 和 `Content-Length` 均缺失。根因位于 source adapter 的响应头强校验,
|
|
不是 ERP 鉴权、数字图片 ID、图片不存在、本地目录或图片体积。
|