Files
docs/入库测量与计费/入库测量与计费前端联调报告-2026-08-04.md

628 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 入库测量、计费与入库前端联调报告
> 版本日期:2026-08-04
> 后端仓库:`D:\baiyue-firstleg`
> 代码基线:`test-01` 分支,提交 `25d2a13`
> 使用对象:平台后台前端、商户端前端、第三方测量系统联调人员
> 术语与现行规则校正:当前产品只有“平台—客户”两级。客户业务中的 `tenantId` / `tenant_id` 表示客户 ID 兼容字段;“资源租户”历史术语表示公共主数据技术归属。平台公共仓库、快递公司、托盘和库位现归属 `tenant_id=0`,客户往来单位、订单、文件和费用归属客户 `tenant_id>0`;报告中的 `10001` 是已失效的历史联调样本,不得继续作为公共资源归属。
## 一、联调结论
入库测量与计费后端主流程、平台测量、OCR 重量、客服审核、FBA 计费、费用发布、入库完成、库位和托盘维护、客户只读查询、第三方测量回传接口均已形成代码闭环。
最终正常业务流程是:
```text
订单生成入库单
→ 第三方或平台录入长宽高、箱规和件数
→ 上传称重图片并执行 OCR
→ 人工修正并确认 OCR 重量
→ 保存测量草稿
→ 提交测量
→ 系统按托盘容量拆分托盘并立即占用库位
→ 客服审核测量数据
→ 审核通过后自动按 FBA 规则计费
→ 自动发布费用给客户
→ 自动完成入库
→ 客户只读查看费用和入库结果
```
联调时必须遵守以下口径:
- 客户不确认费用,前端不得设计或调用“客户确认费用”操作。
- 测量提交后必须由客服审核;第三方回传测量数据不能直接审核、计费或完成入库。
- 提交测量时已经完成托盘拆分和库位占用。客服驳回只退回测量修改,已占库位不释放。
- 客服审核通过后,后端自动完成计费、内部确认、费用发布和入库,不需要前端依次点击计费、确认、发送。
- 已发布费用可以调整;调整会生成新费用版本,但不会回退 `INBOUNDED` 入库状态。
- 测量审核失败默认不允许异常放行。Nacos 配置 `firstleg.workflow.allow-rejected-measurement=false`,仅开启配置且有专门权限时才能放行。
- 当前只实现 FBA 目的仓计费,不包含应收应付、对账、核销、发票、利润、PDA 和完整 WMS。
## 二、服务依赖、入口与文档
### 2.1 前端统一入口
本地默认网关:
```text
http://127.0.0.1:9000
```
聚合 Swagger:
```text
http://127.0.0.1:9000/webjars/swagger-ui/index.html
```
Swagger 分组:
- `firstleg-platform`:平台入库、测量、审核、计费及客户只读接口。
- `firstleg-measurement-openapi`:第三方测量回传接口。
- `warehouse-platform`:库位、托盘等仓储基础资料。
- `file-platform`:测量图片上传、识别和读取。
- `integration`:OCR 内部能力,前端一般不直接调用。
### 2.2 联调相关服务及默认端口
| 顺序 | 服务 | 默认端口 | 用途 |
|---:|---|---:|---|
| 1 | Nacos、MySQL、Redis | 环境配置 | 注册配置、数据和会话 |
| 2 | `iam-service` | 9201 | 用户、角色、按钮权限 |
| 3 | `auth-service` | 9100 | 登录、Token 刷新和退出 |
| 4 | `warehouse-service` | 9205 | 仓库、库位、托盘及占用 |
| 5 | `integration-service` | 9210 | OCR 识别能力 |
| 6 | `file-service` | 9206 | 图片上传、持久化和 OCR 聚合 |
| 7 | `firstleg-service` | 9202 | 入库、测量、审核和计费主流程 |
| 8 | `gateway` | 9000 | 前端统一入口和聚合文档 |
若登录或订单前置流程依赖其他订单与客户服务,应在 Firstleg 前启动。前端业务请求统一走网关,不要将各服务端口写死到页面。
## 三、全局请求与响应约定
### 3.1 平台和商户请求头
```http
Authorization: Bearer <accessToken>
Content-Type: application/json
```
平台代目标企业操作的接口还必须传代表目标客户的 `tenantId` 查询参数。后端同时校验登录应用类型、操作者数据范围、目标客户、仓库和资源归属;不能只依靠前端隐藏按钮实现隔离。
所有有副作用且要求幂等的接口必须传:
```http
Idempotency-Key: <每次业务操作唯一且重试保持不变的字符串>
```
网络超时重试时复用原键;用户重新发起一笔新操作时生成新键。
### 3.2 统一响应
```json
{
"code": "00000",
"message": "成功",
"data": {},
"requestId": "...",
"traceId": "...",
"timestamp": "2026-08-04T02:38:31Z"
}
```
- 前端必须以业务 `code` 判断成功,不能只判断 HTTP 200。
- 分页数据结构为 `data.records`、`data.page`、`data.size`、`data.total`、`data.pages`。
- Long 类型 ID 已统一序列化为 JSON 字符串,前端必须按字符串保存和传递,不要转换成 JavaScript `Number`。
- 401 统一跳转登录或触发刷新 Token;403 显示无权限;409 通常表示重复、状态冲突、乐观锁过期或资源已被占用,页面应刷新详情后再操作。
- 提交按钮在请求完成前禁用,配合 `Idempotency-Key` 防止重复提交。
## 四、状态、页签和按钮规则
### 4.1 入库流程状态 `processStatus`
| 状态 | 中文含义 | 页面主要操作 |
|---|---|---|
| `WAIT_MEASURE` | 待测量 | 进入测量、录入草稿 |
| `MEASURING` | 测量中 | 编辑草稿、OCR、人工修正、提交 |
| `MEASURE_PENDING` | 待客服审核 | 客服审核通过或驳回 |
| `MEASURE_REJECTED` | 测量驳回 | 修改并重新提交;特殊情况下异常放行 |
| `WAIT_PRICING` | 待计费 | 正常流程由后台自动推进 |
| `FEE_CONFIRMING` | 费用处理中 | 正常流程由后台自动推进 |
| `INBOUNDING` | 入库处理中 | 正常流程由后台自动推进 |
| `INBOUNDED` | 已入库 | 查看费用、库位、文件;有权限时调整费用 |
| `VOIDED` | 已作废 | 只读 |
页面不要根据中文文案判断状态,必须使用状态码。
### 4.2 列表页签 `tab`
| 页签参数 | 后端包含状态 |
|---|---|
| `WAIT_MEASURE` | `WAIT_MEASURE`、`MEASURING`、`MEASURE_REJECTED` |
| `WAIT_REVIEW` | `MEASURE_PENDING` |
| `WAIT_FEE_SEND` | `WAIT_PRICING`、`FEE_CONFIRMING`、`INBOUNDING` |
| `INBOUNDED` | `INBOUNDED` |
### 4.3 主流程状态变化
| 业务动作 | 结果状态 | 库位事实 |
|---|---|---|
| 保存测量草稿 | `MEASURING` | 未占用 |
| 提交测量 | `MEASURE_PENDING` | 已拆托并确认 `OCCUPIED` |
| 客服驳回 | `MEASURE_REJECTED` | 保持占用 |
| 客服通过 | 自动经过计费和入库中间态 | 保持占用 |
| 自动闭环完成 | `INBOUNDED`,费用 `SENT` | 保持占用 |
| 调整已发布费用 | 仍为 `INBOUNDED`;新费用 `SENT`,旧费用 `SUPERSEDED` | 保持占用 |
## 五、平台入库单列表与详情
| 方法 | 路径 | 权限 |
|---|---|---|
| GET | `/api/platform/inbounds` | `platform:firstleg:inbound:list` |
| GET | `/api/platform/inbounds/{inboundId}` | `platform:firstleg:inbound:view` |
共同参数:`tenantId`。列表支持:
```text
inboundCode, orderNo, processStatus, orderStatus, measureReviewStatus,
feeStatus, feeReviewStatus, tab, expectedArrivalFrom, expectedArrivalTo,
customerId, customerName, companyId, warehouseId, deliveryWarehouseId,
destinationCode, holdFlag, page, size
```
列表已覆盖前端主要展示字段:入仓码、订单号、订单/流程/测量审核/费用状态、客户、公司、仓库、交货仓、FBA 目的仓、扣货、箱数、实重、净重、实测体积、材积重、计费重量、计费体积、托盘数、`locationCodes`、测量和入库完成时间、快递公司和快递单号、配载、集装箱、柜号、船名、航次、开船和到港时间、创建和更新时间。
详情在此基础上还返回:`publicRemark`、`palletLocations`、`files`、`internalNotes`、`statusHistory`。
## 六、平台测量与 OCR
接口前缀:
```text
/api/platform/v1/first-leg/inbounds/{inboundId}
```
| 方法 | 子路径 | 权限 | 说明 |
|---|---|---|---|
| GET | `/measurement` | `platform:firstleg:inbound:measure:view` | 获取测量、明细和 OCR 结果 |
| PUT | `/measurement/draft` | `platform:firstleg:inbound:measure:edit` | 保存或更新草稿 |
| POST | `/measurement/submit` | `platform:firstleg:inbound:measure:submit` | 提交并拆托、占库位 |
| POST | `/weight-results` | `platform:firstleg:inbound:weight:edit` | 新增 OCR 结果 |
| PUT | `/weight-results/{resultId}` | `platform:firstleg:inbound:weight:edit` | 人工修正重量 |
| POST | `/weight-results/{resultId}/confirm` | `platform:firstleg:inbound:weight:confirm` | 确认最终重量 |
以上接口均带 `tenantId`;写接口按 Swagger 要求携带 `Idempotency-Key`。
### 6.1 保存测量草稿
```json
{
"measurementId": "已有草稿时传,首次可为空",
"version": 0,
"originalDocumentNo": "原单据号",
"originalGrossWeightKg": 100.000,
"originalVolumeCbm": 2.5000,
"actualGoodsName": "实际品名",
"items": [
{
"itemNo": "ITEM-001",
"boxSpecification": "60*40*50cm",
"packageCount": 10,
"lengthCm": 60.000,
"widthCm": 40.000,
"heightCm": 50.000,
"unitWeightKg": 12.500,
"intelligentImageFileId": "图片文件ID",
"overLimitWarning": "",
"outerLengthCm": 61.000,
"outerWidthCm": 41.000,
"outerHeightCm": 51.000,
"remark": ""
}
]
}
```
注意:汇总体积、材积重等计算字段由后端计算,前端不要传入或自行覆盖。`items` 至少一条,尺寸和单件重量必须大于 0,`boxSpecification` 必填。
提交测量请求:
```json
{
"measurementId": "190000000000000001",
"version": 1
}
```
`version` 是乐观锁版本。每次保存、修正或确认成功后都应使用响应中的新版本;409 后重新拉取详情,不能用旧版本强行重试。
### 6.2 OCR 重量
图片接口:
| 方法 | 路径 | 权限 |
|---|---|---|
| POST | `/api/platform/v1/files/measurements/{tenantId}/{inboundId}` | `platform:firstleg:inbound:weight:edit` |
| POST | `/api/platform/v1/files/measurements/{tenantId}/{inboundId}/{fileId}/recognize` | `platform:firstleg:inbound:weight:edit` |
| GET | `/api/platform/v1/files/measurements/{tenantId}/{inboundId}/{fileId}` | `platform:firstleg:inbound:measure:view` |
支持 JPG、PNG、BMP,单文件最大 5 MB。建议交互顺序:上传图片 → 调用识别 → 展示 OCR 原值、单位、置信度 → 用户按需修正 → 确认重量。
人工修正请求:
```json
{
"version": 0,
"adjustedWeightValue": 12.500,
"adjustedWeightUnit": "KG",
"adjustmentReason": "OCR 小数点识别错误"
}
```
确认请求:
```json
{
"version": 1
}
```
测量详情会返回 OCR 原文、原重量和单位、置信度、人工修正值和原因、最终 KG、识别状态、修正/确认人员与时间、版本。多张秤图的最终重量由业务服务汇总,前端只负责展示和人工确认,不自行写汇总重量。
## 七、客服测量审核
| 方法 | 路径 | 权限 |
|---|---|---|
| POST | `/api/platform/v1/inbounds/{inboundId}/measurements/approve` | `firstleg:inbound:measurement-review` |
| POST | `/api/platform/v1/inbounds/{inboundId}/measurements/reject` | `firstleg:inbound:measurement-review` |
| POST | `/api/platform/v1/inbounds/{inboundId}/measurements/exception-release` | `firstleg:inbound:exception-release` |
| GET | `/api/platform/v1/inbounds/{inboundId}/measurement-review-history` | `firstleg:inbound:measurement-review-history` |
写接口必须传 `tenantId` 和 `Idempotency-Key`。通过/驳回请求:
```json
{
"measurementId": "190000000000000001",
"expectedVersion": 2,
"reason": "审核意见,通过时可为空,驳回时按业务填写"
}
```
异常放行请求的 `reason` 必填:
```json
{
"measurementId": "190000000000000001",
"expectedVersion": 3,
"reason": "负责人审批后的异常放行原因"
}
```
审核通过请求成功即表示后端自动闭环已执行完成或可幂等续跑。前端应重新查询入库详情和最新费用,不要再自动串行调用 `/calculate`、`/confirm`、`/send`。
## 八、FBA 计费与费用调整
接口前缀:
```text
/api/platform/v1/inbounds/{inboundId}/fees
```
| 方法 | 子路径 | 权限 | 前端用途 |
|---|---|---|---|
| GET | `/latest` | `firstleg:fba-pricing:view` | 查询最新费用快照 |
| PUT | `/published` | `firstleg:fba-pricing:adjust` | 调整已发布费用 |
| POST | `/calculate` | `firstleg:fba-pricing:calculate` | 异常恢复/人工补偿,不是正常主流程按钮 |
| POST | `/confirm` | `firstleg:fba-pricing:confirm` | 异常恢复/人工补偿 |
| POST | `/send` | `firstleg:inbound:send-fee` | 异常恢复/人工补偿 |
调整已发布费用:
```json
{
"subtotalAmount": 1000.00,
"additionalAmount": 50.00,
"reason": "经审批调整附加费"
}
```
计费规则:
- 包税、按 KG:`计费数量 = max(实重, 实测 CBM × 换算系数)`。
- 不包税、按 CBM:`计费数量 = max(实测 CBM, 实重 ÷ 换算系数)`。
- 换算系数、精度、舍入方式、币种和价格均来自客户规则及船司费率版本,前端不能写死。
FBA 基础资料:
```text
GET /api/platform/v1/fba-pricing/customer-rules
GET /api/platform/v1/fba-pricing/customer-rules/{id}
POST /api/platform/v1/fba-pricing/customer-rules/versions
DELETE /api/platform/v1/fba-pricing/customer-rules/{id}
GET /api/platform/v1/fba-pricing/carrier-rates
GET /api/platform/v1/fba-pricing/carrier-rates/{id}
POST /api/platform/v1/fba-pricing/carrier-rates/versions
DELETE /api/platform/v1/fba-pricing/carrier-rates/{id}
```
规则采用版本化新增,历史订单费用使用当时快照,不应被新版本覆盖。
## 九、库位管理
| 方法 | 路径 | 权限 |
|---|---|---|
| GET | `/api/platform/v1/locations` | `platform:location:list` |
| GET | `/api/platform/v1/locations/{id}` | `platform:location:list` |
| GET | `/api/platform/v1/locations/available` | `platform:location:list` |
| POST | `/api/platform/v1/locations` | `platform:location:create` |
| PUT | `/api/platform/v1/locations/{id}` | `platform:location:edit` |
| PATCH | `/api/platform/v1/locations/{id}/status` | `platform:location:status` |
| DELETE | `/api/platform/v1/locations/{id}` | `platform:location:delete` |
分页条件:`tenantId`、`warehouseId`(必填)、`zoneCode`、`locationCode`、`locationType`、`status`、`page`、`size`。
库位类型:`SHELF`、`PALLET`、`FLOOR`、`FLOW_RACK`。查询状态:`AVAILABLE`、`RESERVED`、`OCCUPIED`、`DISABLED`。
新增/编辑请求:
```json
{
"warehouseId": "190000000000000002",
"zoneCode": "A",
"zoneName": "A库区",
"locationCode": "A-001",
"locationName": "A区001库位",
"locationType": "PALLET",
"maxWeightKg": 1000.000,
"status": "AVAILABLE",
"remark": "",
"version": 0
}
```
启停仅允许 `AVAILABLE` 或 `DISABLED`,请求为:
```json
{
"status": "DISABLED",
"version": 1
}
```
活动预占或已占用库位不能停用;删除要求库位已停用、无活动预占/占用,并在查询参数传 `warehouseId`、`version`。无普通空闲库位时,系统可使用本租户、本仓库的共享溢出位 `A0`,前端只展示后端结果。
## 十、托盘管理
| 方法 | 路径 | 权限 |
|---|---|---|
| GET | `/api/platform/v1/pallets` | `platform:pallet:list` |
| GET | `/api/platform/v1/pallets/{id}` | `platform:pallet:list` |
| POST | `/api/platform/v1/pallets` | `platform:pallet:create` |
| PUT | `/api/platform/v1/pallets/{id}` | `platform:pallet:edit` |
| DELETE | `/api/platform/v1/pallets/{id}` | `platform:pallet:delete` |
分页条件:`tenantId`、`warehouseId`、`palletCode`、`palletType`、`page`、`size`。新增/编辑请求:
```json
{
"warehouseId": "190000000000000002",
"palletCode": "TP-001",
"palletType": "木托盘",
"capacityCbm": 2.000000,
"remark": "标准托盘"
}
```
当前平台托盘 DTO 只返回主数据,不暴露内部 `AVAILABLE / RESERVED / OCCUPIED` 状态;也没有单独的托盘启停或状态筛选 API。页面当前只实现查询、新增、编辑、删除。已预占或占用托盘删除会被后端拒绝。这是当前明确的接口边界,不要自行猜测状态路径。
## 十一、商户端只读接口
```http
GET /api/merchant/v1/first-leg/orders/{orderId}/inbound-billing
```
权限:`merchant:order:view`。
返回 `inbound` 和 `latestFee`。仅允许商户身份查询本租户订单,且只有客服审核通过并已发布的费用可见。商户端只展示:入库结果、测量汇总、托盘/库位结果和最新费用,不提供费用确认、测量审核、费用修改操作。
## 十二、第三方测量开放接口
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/openapi/v1/inbound-measurements` | 一次提交一条箱规明细和一张对应图片 |
| GET | `/openapi/v1/inbound-measurements/events/{eventNo}` | 查询事件处理结果 |
请求头:
```http
X-App-Key: <调用方应用键>
X-Timestamp: <时间戳>
X-Nonce: <随机串>
X-Signature: <签名>
Idempotency-Key: <幂等键>
```
POST 使用 `multipart/form-data`,包含:
```text
data:原始 UTF-8 JSON 文本
image:该条箱规唯一对应的 JPG/PNG/BMP 图片,最大 5 MB
```
`data` 示例:
```json
{
"eventNo": "M202608010001",
"externalDetailNo": "BOX-20260801-0001",
"warehouseCode": "CN-YW-01",
"inboundCode": "RK202608010001",
"inboundDetailId": "190000000000000001",
"deviceCode": "DEVICE-01",
"measuredAt": "2026-08-01T10:30:00+08:00",
"boxSpecification": "60*40*50cm",
"packageCount": 10,
"lengthCm": 60.000,
"widthCm": 40.000,
"heightCm": 50.000,
"unitWeightKg": 12.500,
"remark": ""
}
```
绑定和签名规则:
- 后端先按 `inboundCode` 匹配入库单,再使用 `inboundDetailId` 通过 Order 内部接口校验该货物明细确实属于入库单关联订单;第三方不能自由创建来源不明的明细。
- 一次请求只生成一个测量批次、一条测量明细和一个文件中心图片引用。
- `externalDetailNo` 标识第三方物理测量明细;同一订单明细和相同第三方明细号不能重复绑定。
- `eventNo` 在同一 `appKey` 下唯一;相同事件号或幂等键重试时,测量 JSON 和图片内容必须完全一致。
- 签名 body 为:`data原文 + LF + image文件内容SHA-256小写十六进制`;再按既有六行 HMAC 规则计算 `X-Signature`。
- `measuredAt` 必须带时区;长宽高大于 0;`packageCount` 为 1 至 1,000,000;`boxSpecification` 必填。
- 图片由 File 服务验证真实格式并持久化;尺寸测量图片不触发重量 OCR。测量落库失败时会调用文件中心补偿清理。
- 接口只接收测量数据,不推进客服审核、计费、库位或入库状态。
成功响应的 `data` 返回:`eventNo`、`inboundCode`、`inboundDetailId`、`measurementId`、`measurementItemId`、`imageFileId`、`accepted`、`status`、`receivedAt`。所有 ID 按字符串处理。
## 十三、推荐前端联调顺序
1. 验证平台登录、刷新 Token、退出、过期会话、401 和 403。
2. 给测试角色同步入库测量与计费权限,确认按钮显示与后端权限码一致。
3. 初始化启用仓库、至少一个托盘、足量可用库位、客户计费规则和船司 FBA 费率。
4. 创建或准备一条已进入待测量状态的真实入库单。
5. 调用入库列表和详情,核对 Long ID 均按字符串处理。
6. 上传秤图、调用 OCR、人工修正和确认重量。
7. 保存测量草稿,刷新页面后核对草稿和版本回显。
8. 提交测量,验证状态变为 `MEASURE_PENDING`,托盘和库位结果已生成。
9. 先走一次驳回 → 修改 → 重新提交,验证旧页面版本提交被拒绝。
10. 客服审核通过,随后重新查询详情和 `/fees/latest`,验证自动变为 `INBOUNDED / SENT`。
11. 商户账号查询 `inbound-billing`,验证只读数据、跨客户 403/404 和未发布费用不可见。
12. 调整一次已发布费用,验证新版本可见、旧版本被替代且入库状态不回退。
13. 验证重复点击使用同一幂等键不会重复计费、重复托盘或重复占位。
14. 验证无权限按钮、直接构造越权请求、空态、加载态、异常提示和移动端展示。
## 十四、测试数据准备清单
- 平台管理员账号:具备入库查看、测量、OCR、审核、计费查看/调整、库位和托盘权限。
- 客服账号:具备测量审核权限,但不授予异常放行权限,用于验证按钮隔离。
- 商户账号:绑定独立租户,只具备本企业订单查看权限。
- 两个不同租户及各自仓库,用于跨企业越权验证。
- 一个启用仓库、至少 2 个容量明确的托盘、至少 2 个 `AVAILABLE` 库位。
- 一套生效中的客户 FBA 计费规则和匹配的船司费率,币种、渠道、目的仓、有效期完整。
- 一条待测量入库单、一条已发布费用入库单。
- JPG/PNG/BMP 各一张有效秤图,以及超 5 MB、伪造扩展名和无法识别图片各一张。
- 第三方测量应用的 AppKey、签名密钥、绑定租户和仓库编码。
## 十五、本次回归测试结果
2026-08-04 11:42(Asia/Shanghai)在最终代码上重新执行:
```powershell
D:\apache-maven-3.8.8\bin\mvn.cmd clean test -pl order,firstleg,warehouse,file,integration-service,gateway,iam -am
```
结果:13 个 Reactor 模块全部 `SUCCESS`,`BUILD SUCCESS`,耗时约 1 分 5 秒。
| 模块 | 测试数 | Failures | Errors | Skipped |
|---|---:|---:|---:|---:|
| common-core | 3 | 0 | 0 | 0 |
| common-web | 10 | 0 | 0 | 0 |
| common-security | 6 | 0 | 0 | 0 |
| common-openapi | 1 | 0 | 0 | 0 |
| common-audit | 0 | 0 | 0 | 0 |
| gateway | 10 | 0 | 0 | 0 |
| iam | 79 | 0 | 0 | 0 |
| order | 30 | 0 | 0 | 0 |
| firstleg | 74 | 0 | 0 | 0 |
| warehouse | 68 | 0 | 0 | 0 |
| file | 23 | 0 | 0 | 0 |
| integration-service | 8 | 0 | 0 | 0 |
| **合计** | **312** | **0** | **0** | **0** |
重点自动化覆盖:
- 统一响应、异常映射、Long ID 字符串序列化和权限认证。
- 测量草稿、OCR 修正/确认、提交及乐观锁。
- 测量提交时托盘拆分、库位预占/确认、失败回滚、幂等重试。
- 客服驳回、通过后自动计费/发布/入库、中断续跑。
- FBA 计费规则选择、已发布费用调整和状态不回退。
- 客户只见本客户已发布费用,平台/商户身份及跨客户隔离。
- 第三方签名、防重放、事件幂等、仓库范围及不越权推进状态。
- 第三方一条箱规对应一条订单货物明细和一张图片、重复绑定拦截、图片补偿清理。
- 库位/托盘主数据约束、A0 共享溢出及活动占用唯一性契约。
- 图片上传类型、大小、真实图片校验、下载三字段隔离和 OCR 聚合。
- Gateway 路由以及 OpenAPI、Application、Nacos YAML 解析。
测试日志中的模拟数据库不可用、文件写入失败等异常堆栈,是异常映射测试主动构造的场景;对应测试均通过,不是构建失败。
## 十六、尚需在线环境验证与当前遗留
本次回归没有启动服务、没有占用 `9000/9100/920x` 默认端口,也没有连接或修改 MySQL。以下内容必须在测试环境由后端与前端共同验证后,才能判定上线验收通过:
1. 入库测量与计费 SQL 在真实 MySQL 的建表/迁移执行,以及历史数据与唯一约束冲突检查。
2. Gateway、Nacos、Redis、MySQL、服务间令牌和各微服务的真实 HTTP 联通。
3. OCR 引擎对现场秤图的识别准确率、超时和失败重试。
4. 并发提交、并发抢托盘/库位、A0 多托盘共享在真实数据库事务下的压力验证。
5. 浏览器端完整登录、刷新 Token、权限按钮、403、跨客户拒绝、空态、加载态和重复提交体验。
6. 当前托盘管理没有独立启停和状态筛选接口;页面只做查询、新增、编辑、删除。
上述内容是本报告初版时的阶段结论。2026-08-04 已继续完成真实数据库迁移和隔离端口在线 HTTP 回归,最终结论以第十七章为准。
## 十七、最终在线回归结果(任务 019fc6f9)
### 17.1 验收结论
2026-08-04 已连接本地测试 MySQL,并使用独立 Nacos 分组和隔离端口完成真实 API 全链路回归。下单、订单审核生成入库单、测量草稿、图片绑定、称重图片、OCR 失败人工修正、重量确认、托盘库位占用、测量审核、FBA 自动计费、费用发送和入库完成全部通过。
最终状态:
| 项目 | 结果 |
|---|---|
| 订单 ID | `2084549604719718402` |
| 入库单 ID | `2084549769916538881` |
| 入仓码 | `RK-2084549604719718402` |
| 测量批次 | `2084556854154715137` |
| 入库状态 | `INBOUNDED` |
| 测量审核状态 | `APPROVED` |
| 费用状态 | `SENT` |
| 费用单号 | `FBAF-2084549769916538881-1` |
| 实重 / 体积 | `20kg` / `0.24m³` |
| 体积重 / 计费量 | `40.08kg` / `40.08kg` |
| 最终费用 | `440.68 CNY` |
| 托盘 | `2084561214309851137` |
| 库位 | `2084561214452457473` |
| 库位预占单 | `LR2084562525306675202` |
| 分配状态 | `CONFIRMED` |
### 17.2 环境与自动化结果
- Nacos 隔离组:`BAIYUE_PHASE2_E2E_20260804`。
- 隔离端口:`19100`、`19201`、`19202`、`19204`、`19205`、`19206`、`19207`,未占用默认 `9000/9100/920x` 端口。
- 7 个隔离服务健康检查均为 `UP`。
- Firstleg 自动化测试 77 项通过。
- Warehouse 自动化测试 68 项通过。
- 公共仓储技术归属专项(历史测试名含“资源租户”)3 项通过。
### 17.3 前端需要采用的最终契约
- 入库详情已经返回 `version`,测量审核、驳回等乐观锁请求必须使用服务端最新版本。
- Long 类型 ID 继续按字符串处理。
- 客服审核通过后由后端自动完成计费、费用发送和入库,前端成功后刷新详情与最新费用,不串行调用补偿接口。
- 平台操作公共收货仓时,订单客户归属与公共主数据技术归属由后端分别校验,前端不自行改写技术归属字段。
- OCR 未配置时会返回明确失败状态,页面必须允许人工修正识别重量并确认,不能把 OCR 失败等同于整条业务失败。
### 17.4 本轮修复
1. 补齐 `fl_inbound_weight_result.created_by`、`updated_by`,修复称重结果登记 SQL 失败。
2. 增加历史兼容配置 `firstleg.inbound.warehouse-resource-tenant-id`,支持商户订单使用公共主数据技术归属下的仓库、托盘和库位;2026-08-04 本轮曾使用 `10001` 验证,但该样本已失效,现行公共资源归属必须为平台技术作用域 `0`。
3. 仓储确认结果回传并保留 `reservationNo`,避免分配快照写入空预占单号。
4. 入库详情 `InboundOrderSummary` 增加 `version`。
5. `fl_inbound_pallet_allocation` 增加 `warehouse_resource_tenant_id`,保留历史资源归属快照。
### 17.5 当前边界
- 本轮已验证 OCR 不可用时的人工修正降级闭环;未验证真实 OCR 供应商对现场秤图的准确率、时延和失败重试。
- 浏览器端视觉、移动端适配和真实用户操作体验仍由前端按本报告接口完成联调验收。
- 数据库唯一增量入口:`D:\baiyue-firstleg\deploy\sql\头程数据库统一增量-MySQL8.sql`。后续变更只追加到该文件,历史日期脚本不再作为部署入口。
最终结论:**入库测量与计费核心正常主链路已经完成真实 MySQL、真实 HTTP 和自动化回归,后端达到前端联调条件;真实 OCR 供应商识别和浏览器端体验属于后续环境验收项。**