2026-07-29 14:43:54 +08:00
|
|
|
|
---
|
|
|
|
|
|
id: T-239
|
|
|
|
|
|
title: 冻结货运商品规格、原始价格与图片引用契约
|
|
|
|
|
|
phase: 2
|
|
|
|
|
|
deps:
|
|
|
|
|
|
- T-238
|
2026-07-29 14:54:58 +08:00
|
|
|
|
status: DONE
|
2026-07-29 14:43:54 +08:00
|
|
|
|
created: 2026-07-29
|
|
|
|
|
|
context_ref: 01be7e7
|
|
|
|
|
|
work_branch: null
|
|
|
|
|
|
write_paths:
|
|
|
|
|
|
- docs/tasks/T-239.md
|
|
|
|
|
|
- docs/api.md
|
|
|
|
|
|
- docs/current-state.md
|
|
|
|
|
|
- backend-api/migrations/00015_freight_item_metadata.sql
|
|
|
|
|
|
- backend-api/internal/domain/freight.go
|
|
|
|
|
|
- backend-api/internal/platform/shunyunbao/normalizer.go
|
|
|
|
|
|
- backend-api/internal/platform/shunyunbao/*_test.go
|
|
|
|
|
|
- backend-api/internal/usecase/freight_*
|
|
|
|
|
|
- backend-api/internal/repository/sqlite/freight_*
|
|
|
|
|
|
- backend-api/internal/transport/httpapi/freight_handlers.go
|
|
|
|
|
|
- backend-api/internal/transport/webui/**
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 问题 / 背景
|
|
|
|
|
|
|
|
|
|
|
|
顺运宝货运详情已能导入商品标题、数量和 `productThumb` 引用,但采购使用的 SKU 仍优先取
|
|
|
|
|
|
ERP `sku/variationSku`,与本项目需要按颜色、尺码硬匹配的 `productSpec` 不一致;ERP
|
|
|
|
|
|
`productPrice` 也没有进入本地货运契约。图片引用当前按任意标量接收,后续无法安全地用于固定
|
|
|
|
|
|
图片 endpoint。
|
|
|
|
|
|
|
|
|
|
|
|
本机真实响应的结构化检查确认:`productSpec` 是采购所需规格,`productPrice` 当前样例为
|
|
|
|
|
|
整数,`productThumb` 是正整数且不同于货运单 ID 和商品明细 ID。真实响应包含业务及个人
|
|
|
|
|
|
数据,继续保持未跟踪且不得复制到 fixture、日志或提交。
|
|
|
|
|
|
|
|
|
|
|
|
## 关联需求与交互
|
|
|
|
|
|
|
|
|
|
|
|
- 货运商品的规格和采购 SKU 都取 ERP `productSpec`。
|
|
|
|
|
|
- 保存 ERP `productPrice`,默认币种为 TWD,供货运详情和后续采购复核使用。
|
|
|
|
|
|
- 保存 ERP `productThumb` 的规范化正整数引用,供后续后端拼接固定图片 URL。
|
|
|
|
|
|
- 已生成采购任务保持不可变;新建采购需求使用新同步版本中的规格 SKU。
|
|
|
|
|
|
|
|
|
|
|
|
## 方案
|
|
|
|
|
|
|
|
|
|
|
|
1. normalizer 将 `productSpec` 同时写入 `product_spec` 和 `sku`;字段为空时保留空值,由既有
|
|
|
|
|
|
采购前置校验阻塞,不静默回退 `sku/variationSku`。
|
|
|
|
|
|
2. 严格解析 `productPrice` 的整数、小数或数字字符串,不接受负数、指数、NaN、超过两位
|
|
|
|
|
|
小数或越界金额;未知值保存 `NULL`,不能误写为零。
|
|
|
|
|
|
3. SQLite 使用 `original_unit_price_minor INTEGER NULL` 保存最小货币单位,并增加
|
|
|
|
|
|
`original_currency TEXT NOT NULL DEFAULT 'TWD'`。金额和币种进入商品 canonical hash,变化
|
|
|
|
|
|
时递增 revision。
|
|
|
|
|
|
4. `productThumb` 缺失时为 `NULL`;存在时必须是正整数并规范化为十进制字符串。禁止接受
|
|
|
|
|
|
完整 URL、负数、小数或任意文本。
|
|
|
|
|
|
5. API 和 Web 适配层返回原始单价与币种,但本任务不下载图片、不展示外部 URL。
|
|
|
|
|
|
|
|
|
|
|
|
## 验收要点
|
|
|
|
|
|
|
2026-07-29 14:54:58 +08:00
|
|
|
|
- [x] `productSpec` 同时成为货运商品的规格和采购 SKU,缺失时不使用其他 ERP SKU 猜测。
|
|
|
|
|
|
- [x] `productPrice` 的合法整数、小数和数字字符串精确转换为 TWD 最小单位。
|
|
|
|
|
|
- [x] 缺失价格为 `NULL`;负数、指数、超过两位小数和越界值拒绝为 ERP 协议错误。
|
|
|
|
|
|
- [x] `productThumb` 仅接受可规范化的正整数引用,且不与货运单 ID 混用。
|
|
|
|
|
|
- [x] 金额、币种和新 SKU 进入 canonical hash,并通过 SQLite 往返和 revision 测试。
|
|
|
|
|
|
- [x] API/Web 适配层能读取新增字段;既有采购快照和任务不可变语义不回归。
|
|
|
|
|
|
- [x] 标准 Go 测试、race、vet 和三个入口构建通过。
|
2026-07-29 14:43:54 +08:00
|
|
|
|
|
|
|
|
|
|
## 边界
|
|
|
|
|
|
|
|
|
|
|
|
- 本任务不请求 `/api/p/file`,不保存图片文件,也不修改页面视觉布局。
|
|
|
|
|
|
- 不计算货运总价、税费、运费或采购预算;`productPrice` 仅标记为 ERP 原始单价。
|
|
|
|
|
|
- 不提交真实 ERP 响应、账号、Cookie、Token、订单、收件人或商品业务数据。
|
|
|
|
|
|
|
|
|
|
|
|
## 执行记录
|
|
|
|
|
|
|
|
|
|
|
|
- 2026-07-29:创建任务。确认图片 URL 的查询参数必须来自商品 `productThumb`,不能使用
|
|
|
|
|
|
货运单或商品明细 ID;金额使用定点最小单位,图片网络获取拆分到后续任务。
|
2026-07-29 14:54:58 +08:00
|
|
|
|
- 2026-07-29:增加 migration 15 和货运领域/API/Web 字段;normalizer 以 `productSpec`
|
|
|
|
|
|
同时生成规格和采购 SKU,严格解析 TWD 原始单价及正整数图片引用。金额进入商品与货运单
|
|
|
|
|
|
canonical hash,SQLite 往返、revision、采购不可变快照和脱敏 API 回归通过。标准 Go
|
|
|
|
|
|
测试、race、vet 及三个入口构建均通过;真实 ERP JSON 保持未跟踪。
|