# 虫虫出海二期需求优化版 V2:测量、计费、托盘入库闭环 > 形成日期:2026-08-01 > 文档状态:待业务审核 > 依据:二期原型、现有二期需求优化稿及 2026-08-01 新增流程说明 > 本文只定义二期范围和业务契约,不代表功能已经开发完成。 > 技术实现状态更新(2026-08-04):任务 `019fc6f9-8dcf-74e1-bc68-d42f15d7802a` 已完成核心正常主链路开发,并通过本地测试 MySQL 和隔离端口真实 API 回归。产品/业务待确认项仍以第九章和《二期 V2 需求确认清单》为准。 ## 一、结论 二期主流程应从原来的“人工测量—测量审核—接收审核—人工计费—填写一个库位”调整为: ```text 一期头程订单审核通过 → 生成入库单和唯一入仓码 → 第三方测量设备按入仓码回传长宽高 → 上传称重图片并由 OCR 识别重量 → 人工校正 OCR 结果并汇总实际重量 → 测量数据审核 → 按客户规则、船司和 FBA 目的仓自动试算 → 人工确认费用 → 点击“发送费用给客户” → 拆分托盘并分配库位 → 占用库位、入库成功、费用对客户可见 → 异步通知客户 ``` “发送费用给客户”是二期业务完成节点,也是入库成功节点。原方案中的独立“接收审核”不再作为入库前置节点;如页面需要保留,可改名为“入库确认”,其核心动作就是确认费用并完成托盘、库位占用。 二期不新增微服务: - 入库单、测量数据、审核、计费试算和费用快照放在头程服务。 - 托盘基础资料、库位及库位占用放在仓库服务。 - 测量图、称重图继续使用文件服务。 - 客户通知通过现有事件桥接服务发送,不在入库事务中同步调用外部消息渠道。 - 权限继续复用 IAM,所有后台按钮做到租户、企业、仓库数据范围校验。 ## 二、核心业务规则 ### 2.1 测量审核失败是否允许入库 保留 Nacos 动态开关,默认关闭: ```yaml firstleg: inbound: allow-rejected-measurement: false ``` 建议配置归属: - Data ID:`firstleg-${spring.profiles.active}.yaml` - Group:`BAIYUE` - 默认值:代码和 Nacos 均为 `false`,避免 Nacos 配置缺失时意外放行。 开关语义: | 配置值 | 规则 | |---|---| | `false` | 只有测量审核通过,才能确认费用、发送费用和入库;审核失败必须修正后重新提交。 | | `true` | 测量审核失败的入库单允许由有“异常放行”权限的人员继续;必须填写放行原因。 | 即使开关开启,也不能让所有失败单自动入库。后端还必须同时校验按钮权限 `firstleg:inbound:exception-release`,记录操作人、原因、审核失败原因、开关当时值和时间。作废单、缺少费用快照或没有可用库位的单据始终不得放行。 ### 2.2 第三方长宽高测量 第三方设备只通过入仓码绑定入库单,不接受第三方直接传内部数据库主键。一个入仓码可回传多条箱规测量明细,每条回传必须带第三方事件号以保证幂等。 建议开放接口: ```http POST /openapi/v1/inbound-measurements ``` 请求头: ```text X-App-Key: 分配给测量服务商的应用标识 X-Timestamp: Unix 毫秒时间戳 X-Nonce: 随机串 X-Signature: HMAC-SHA256 签名 Idempotency-Key: 第三方事件号 ``` 请求示例: ```json { "eventNo": "M202608010001", "warehouseCode": "CN-YW-01", "inboundCode": "RK202608010001", "deviceCode": "DEVICE-01", "measuredAt": "2026-08-01T10:30:00+08:00", "items": [ { "itemNo": "001", "lengthCm": 50.20, "widthCm": 40.10, "heightCm": 30.00, "quantity": 5, "sourceImageUrl": null } ] } ``` 统一响应示例: ```json { "code": "0", "message": "成功", "data": { "eventNo": "M202608010001", "inboundCode": "RK202608010001", "accepted": true }, "requestId": "...", "timestamp": 1785541800000 } ``` 接口校验: - 入仓码必须存在,且属于请求中的仓库和租户绑定关系。 - 入库单必须处于待测量或测量被驳回状态;已入库、已作废、费用已发送的数据不得覆盖。 - 长宽高必须大于 0,单位统一为厘米;数量必须为正整数。 - 同一个 `appKey + eventNo` 重复请求返回第一次结果,不重复新增明细。 - 第三方原始报文、签名主体、处理结果和请求时间单独留档,业务表保存标准化后的数据。 - 回调接口只接收测量结果,不直接审核、计费、占用库位或改变入库状态。 建议同时提供查询接口,供设备确认处理结果: ```http GET /openapi/v1/inbound-measurements/events/{eventNo} ``` ### 2.3 OCR 重量识别与人工校正 重量数据来自称重图片,而不是长宽高设备。前端先把图片上传文件中心,再把 `fileId` 提交给头程服务识别,避免业务服务接收和长期保存大文件。 建议接口: ```http POST /api/platform/inbounds/{inboundId}/weight-images/recognize PUT /api/platform/inbounds/{inboundId}/weight-results/{resultId} POST /api/platform/inbounds/{inboundId}/weight-results/confirm ``` 识别请求只传文件标识: ```json { "fileIds": ["file-001", "file-002"] } ``` 每张图片形成一条结果,至少保存:图片、OCR 原始文本、OCR 原始重量、置信度、修正后重量、修正人、修正原因、确认状态。总实际重量由所有“已确认且有效”的修正后重量累加,禁止前端直接传一个不可追溯的总重量。 规则: - OCR 结果允许人工修改,但不得覆盖原始识别值。 - 无法识别、置信度过低或单位不明时标记为“待人工确认”。 - 支持公斤和磅,服务端统一换算成 KG,原单位和原值必须保留。 - 同一图片不得重复计重;替换、删除图片必须留下操作记录。 - 测量提交审核前,所有有效图片必须已确认,且总重量大于 0。 ### 2.4 FBA 计费范围 二期只支持 FBA 目的仓计费,不做完整财务、账单、收付款、核销和利润模块。 计费匹配顺序: 1. 根据租户、客户和生效时间选择客户计费规则版本。 2. 根据起运仓、渠道/计费类型、船司、FBA 目的仓和生效时间匹配船司价格。 3. 计算实测体积、实重和折算量。 4. 按客户规则确定 KG 或 CBM 计费口径,并取实际量与折算量中的较大值。 5. 生成费用试算明细,人工确认后形成不可变费用快照。 包税、按 KG 计费: ```text 体积重(kg) = 实测体积(CBM) × 167 计费重量(kg) = max(实际重量(kg), 体积重(kg)) 基础费用 = 计费重量 × KG 单价 ``` 不包税、按 CBM 计费: ```text 重量折算体积(CBM) = 实际重量(kg) ÷ 363 计费体积(CBM) = max(实测体积(CBM), 重量折算体积(CBM)) 基础费用 = 计费体积 × CBM 单价 ``` 其中 `167`、`363` 只是当前业务给出的默认示例,必须配置在客户规则版本中,不得硬编码。金额还可叠加船司价格表中明确配置的附加费;二期不支持阶梯价、组合优惠和复杂表达式。 费用单必须保存以下快照,避免基础资料后续修改影响历史订单: - 客户规则 ID、版本、计费类型、换算系数和舍入规则。 - 船司价格 ID、版本、船司、起运仓、FBA 目的仓、有效期和币种。 - 实测体积、实际重量、折算量、最终计费量、单价、附加费和总金额。 - 试算人、确认人、确认时间,以及是否异常放行。 无法唯一匹配客户规则或船司价格时,不允许发送费用和入库;返回明确错误,交由运营补充配置。 ### 2.5 发送费用与入库成功 后台按钮:`发送费用给客户`。建议权限码:`firstleg:inbound:send-fee`。 点击前必须满足: - 入库单未作废、未入库、费用未发送。 - 长宽高测量数据完整,重量图片均已确认。 - 测量审核通过;或异常放行开关开启、操作人有权限且填写原因。 - 费用已按当前有效规则重新试算并由操作人确认。 - 已选择足够数量的可用托盘和库位。 成功后一次业务操作应产生以下结果: 1. 锁定费用快照,不再允许原地覆盖。 2. 生成托盘分配明细。 3. 在仓库服务预占并确认库位占用。 4. 入库单状态变为 `INBOUNDED`,记录入库人和入库时间。 5. 费用状态变为 `SENT`,客户侧可只读查看。 6. 写入“费用已发送”领域事件,由事件桥接服务异步通知客户。 客户消息发送失败不能回滚已经完成的实物入库和库位占用。事件通过 Outbox 重试,并在后台显示“通知失败/待重试”;禁止因用户重复点击产生重复费用、重复托盘或重复占位。 ### 2.6 托盘拆分与库位占用 托盘数量按总体积和选定托盘可用容积向上取整: ```text 托盘数量 = ceil(入库单实际总体积 ÷ 单托盘可用容积) ``` 示例:总体积 `20 CBM`,托盘可用容积 `2 CBM`,需要 `10` 个托盘;默认一个托盘绑定一个库位,因此需要占用 `10` 个库位。若总体积为 `20.1 CBM`,则需要 `11` 个托盘,最后一个托盘保存剩余分配体积。 注意:业务含义应是“10 个托盘分别绑定 10 个库位”,不是“一个托盘同时绑定 10 个库位”。如果实际仓库允许同一库位存多个托盘,后续再增加库位容量模型;二期默认一托一位,以避免占用口径不清。 拆分规则: - 托盘容积取托盘基础资料快照,历史分配不随托盘资料修改。 - 托盘编号由仓库服务生成,禁止前端自行拼接。 - 后端校验库位属于当前仓库、启用且未占用。 - 多人同时选同一库位时,以数据库唯一约束或乐观锁保证只有一个请求成功。 - 已入库后的换位、释放和重新上架不属于本次主流程;如确有需要,应作为 P1 的库位调整功能单独验收。 ## 三、状态模型 建议把“流程状态”“审核状态”“费用状态”“通知状态”分开保存,避免一个状态字段组合爆炸。 ### 3.1 入库单流程状态 | 状态 | 说明 | |---|---| | `WAIT_MEASURE` | 等待第三方长宽高和称重图片。 | | `MEASURING` | 已接收部分测量或 OCR 数据,尚未确认完整。 | | `MEASURE_PENDING` | 测量已提交,等待审核。 | | `MEASURE_REJECTED` | 审核失败,等待修正;默认不能继续入库。 | | `WAIT_PRICING` | 测量审核通过,等待计费试算。 | | `FEE_CONFIRMING` | 已试算,等待人工确认并发送。 | | `INBOUNDING` | 正在预占托盘和库位,防止重复操作。 | | `INBOUNDED` | 费用已确认并发送,托盘和库位已占用,入库成功。 | | `VOIDED` | 已作废。 | ### 3.2 子状态 - 测量审核:`PENDING / APPROVED / REJECTED`。 - 费用:`UNPRICED / CALCULATED / CONFIRMED / SENT / VOIDED`。 - 客户通知:`NOT_CREATED / PENDING / SUCCESS / FAILED`。 ## 四、服务与数据边界 | 服务 | 二期职责 | 主要新增数据 | |---|---|---| | 头程服务 | 入库单、第三方测量接入、OCR 结果业务管理、测量审核、计费匹配、费用快照、入库编排 | 入库单、测量批次/明细、OCR 重量结果、审核记录、客户规则、船司价格、费用快照、托盘分配引用 | | 仓库服务 | 托盘资料、库位资料、可用库位查询、预占/确认/释放 | 托盘、库位、库位占用、预占记录 | | 文件服务 | 称重图片、测量图片和附件 | 文件元数据和对象存储引用 | | 事件桥接服务 | 客户通知的可靠投递 | Outbox 消费记录、通知结果 | | IAM | 租户、企业、仓库范围和按钮权限 | 新增二期权限资源,不新增业务数据 | 头程与仓库分库,不能依赖跨库事务。采用“仓库库位预占 → 头程确认入库 → 仓库确认占用”的幂等 Saga;补偿只释放仍处于预占状态的库位,已经完成实物入库的记录不得被通知失败反向释放。 ## 五、建议新增的核心接口 ### 5.1 平台后台接口 ```text GET /api/platform/inbounds GET /api/platform/inbounds/{id} POST /api/platform/inbounds/{id}/measurements/submit POST /api/platform/inbounds/{id}/measurements/approve POST /api/platform/inbounds/{id}/measurements/reject POST /api/platform/inbounds/{id}/weight-images/recognize PUT /api/platform/inbounds/{id}/weight-results/{resultId} POST /api/platform/inbounds/{id}/weight-results/confirm POST /api/platform/inbounds/{id}/fees/calculate POST /api/platform/inbounds/{id}/fees/confirm POST /api/platform/inbounds/{id}/send-fee-and-inbound GET /api/platform/inbounds/{id}/pallet-allocations GET /api/platform/inbounds/{id}/operation-logs ``` ### 5.2 仓库内部接口 ```text GET /internal/warehouses/{warehouseId}/locations/available POST /internal/location-reservations POST /internal/location-reservations/{reservationNo}/confirm POST /internal/location-reservations/{reservationNo}/release ``` 所有写接口必须支持幂等键;所有查询由后端基于登录上下文注入租户和企业范围,禁止相信前端传入的 `tenantId`。 ## 六、页面范围 ### P0 页面 - 入库单列表和详情。 - 入库单测量:第三方数据展示、称重图片、OCR 原值/修正值和总重量。 - 测量数据审核:客户下单、第三方长宽高、OCR 重量三组信息和驳回原因。 - 费用试算与确认:客户规则、船司价格、FBA 目的仓、计费过程和费用快照预览。 - 托盘/库位分配:托盘数量、每托盘体积、候选库位、占用结果。 - 客户订单详情:只读显示已发送费用和入库结果。 - 客户计费规则、船司价格表、托盘管理和库位管理。 ### P1 页面/功能 - 通知失败人工重试。 - 入库后库位调整。 - 测量视频。 - 单票导出。 ## 七、二期开发任务重排 | ID | 模块 | 交付内容 | 优先级 | |---|---|---|---| | P2-01 | 契约、状态与权限 | 状态机、错误码、按钮权限、租户/企业/仓库数据范围 | P0 | | P2-02 | 入库单 | 列表、详情、入仓码和操作历史 | P0 | | P2-03 | 第三方测量 | 签名、幂等、长宽高回传、原始报文留档和查询 | P0 | | P2-04 | OCR 称重 | 图片关联、OCR、人工校正、单位换算、确认和重量汇总 | P0 | | P2-05 | 测量审核 | 原单/设备/OCR 对比、通过、驳回和异常放行开关 | P0 | | P2-06 | 客户计费规则 | KG/CBM 类型、换算系数、舍入、生效期和版本 | P0 | | P2-07 | 船司 FBA 价格 | 起运仓、船司、FBA 目的仓、单价、币种、附加费和有效期 | P0 | | P2-08 | 自动试算与费用快照 | 规则匹配、取大计算、人工确认、费用发送和历史快照 | P0 | | P2-09 | 托盘与库位 | 托盘资料、向上拆分、库位可用查询、预占和确认占用 | P0 | | P2-10 | 入库编排 | 发送费用、入库成功、跨服务 Saga、幂等和失败补偿 | P0 | | P2-11 | 客户通知与展示 | Outbox 通知、失败重试、客户侧费用和入库结果只读展示 | P0 | | P2-12 | 文件与审计 | 称重/测量图片鉴权、第三方调用日志和全操作审计 | P0 | | P2-13 | 回归与发布 | 正常、驳回、放行、并发占位、重复提交、越权和异常恢复 | P0 | ## 八、验收主流程 ### 8.1 正常流程 1. 已审核的一期订单生成唯一入仓码。 2. 第三方用入仓码回传多条长宽高,同一事件重复回传不产生重复数据。 3. 操作员上传多张称重图片,OCR 识别后可逐条修正,系统正确累加 KG。 4. 审核员通过测量,系统按客户规则、船司、FBA 目的仓匹配唯一有效价格。 5. 系统按 KG 或 CBM 口径取大计算,页面展示完整计算过程。 6. 操作员确认后点击发送费用,系统生成正确托盘数并占用相同数量的可用库位。 7. 入库单变为已入库,客户能看到已发送费用;通知异步成功。 ### 8.2 必须覆盖的异常流程 - 默认开关关闭时,测量审核失败不能计费发送和入库。 - 开关开启但无异常放行权限,仍不能入库;有权限且填写原因时才可放行。 - 无客户规则、无船司价格、价格过期或匹配多条时禁止发送费用。 - OCR 图片重复、单位不明、未确认或总重为零时禁止提交审核。 - 库位数量不足、被并发抢占或已停用时,入库失败且不能留下半完成费用/占用。 - 重复点击发送费用只产生一份费用快照、一组托盘和一次入库结果。 - 客户通知失败时入库状态不回滚,系统自动重试并可查失败原因。 - 跨租户、跨企业、跨仓库查询和操作全部返回 403,并记录安全审计。 ## 九、仍需业务最终确认的事项 1. 包税是否固定采用 `CBM × 167` 转 KG,不包税是否固定采用 `KG ÷ 363` 转 CBM;不同客户能否配置不同系数。 2. “根据客户规则然后根据船司计算”是只生成客户应收费用,还是同时保存船司成本。本文默认二期只向客户发送应收费用,船司价格作为计价基础,不进入应付和利润闭环。 3. 船司价格唯一匹配所需维度是否就是:起运仓、渠道/类型、船司、FBA 目的仓和有效期。 4. 托盘是否始终一托一库位。本文按一托一位设计;若一个库位可放多个托盘,需要补充库位容积、承重和剩余容量规则。 5. 托盘分配是只按体积平均拆分,还是还要拆到具体 SKU/箱数。本文二期只分配体积和箱数汇总,不做完整 SKU 库存。 6. 点击发送费用前是否还需要第二人复核。本文默认测量审核完成后由有发送权限的操作员确认,不再增加独立接收审核。 7. OCR 服务商、图片格式、单图大小、识别时限和失败重试次数需要在技术接入前确定。 ## 十、本次相对上一版的变更 - 第三方长宽高测量从“不纳入”改为二期 P0,并定义按入仓码接入的开放接口。 - OCR 称重从“不纳入”改为二期 P0,支持多图累加和人工修正。 - 人工简单计费改为客户规则加船司 FBA 目的仓自动试算。 - 新增 KG/CBM 换算取大和费用规则/价格快照。 - 新增 Nacos 审核失败异常放行开关,默认关闭。 - 原“接收审核通过即入库”改为“发送费用给客户即入库成功”。 - 原“只填一个库位”改为按托盘容积向上拆分,并一托一位占用库位。 - 托盘管理从“不纳入”改为二期必要基础能力,但不扩展为完整 WMS。 ## 十一、实现与验收状态(2026-08-04) ### 11.1 已实现并通过真实 API 验证 - 订单审核后生成入库单和唯一入仓码。 - 平台测量草稿、箱规明细、尺寸证据图片绑定。 - 称重图片上传、OCR 失败状态、人工修正、重量确认和多重量汇总。 - 测量提交、托盘拆分、库位预占及确认占用。 - 客服测量审核通过后自动计费、发送费用并完成入库。 - FBA 目的仓计费按实重和体积重取大,本轮 `20kg` 与 `40.08kg` 取 `40.08kg`。 - 入库详情、费用快照、托盘和库位分配结果查询。 - 公共收货仓资源租户与商户业务租户分离。 ### 11.2 最终验证数据 | 项目 | 结果 | |---|---| | 入库单 | `2084549769916538881` | | 入仓码 | `RK-2084549604719718402` | | 测量批次 | `2084556854154715137` | | 最终状态 | `INBOUNDED / APPROVED / SENT` | | 计费 | `40.08kg × 11 CNY/kg = 440.68 CNY` | | 托盘 / 库位 | `2084561214309851137` / `2084561214452457473` | | 预占单 | `LR2084562525306675202`,状态 `CONFIRMED` | ### 11.3 已落地配置与数据快照 公共仓资源租户配置: ```yaml firstleg: inbound: warehouse-resource-tenant-id: ${WAREHOUSE_RESOURCE_TENANT_ID:0} ``` 默认值 `0` 保持租户独立仓逻辑;本地联调使用 `10001`。托盘分配快照已保存 `warehouse_resource_tenant_id` 和 `reservation_no`,避免配置变化后无法追溯历史资源归属。 ### 11.4 尚未关闭的环境验收项 - 接入真实 OCR 供应商凭证后,以现场秤图验证识别准确率、超时和重试。 - 前端浏览器完成登录、权限按钮、403、跨租户、重复提交、空态和移动端体验验收。 - C1-C8 产品结论仍需产品/业务负责人正式填写,技术实现通过不等于代替产品确认。