Files
docs/二期/二期前端联调报告-2026-08-04.md
T

567 lines
23 KiB
Markdown
Raw Normal View History

2026-08-04 12:23:47 +08:00
# 二期入库测量、计费与入库前端联调报告
> 版本日期:2026-08-04
> 后端仓库:`D:\baiyue-firstleg`
> 代码基线:`test-01` 分支,提交 `25d2a13`
> 使用对象:平台后台前端、商户端前端、第三方测量系统联调人员
## 一、联调结论
二期后端主流程、平台测量、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. 当前托盘管理没有独立启停和状态筛选接口;本期页面只做查询、新增、编辑、删除。
因此当前结论是:**代码编译及 312 项单元/契约测试全部通过,可以进入前后端测试环境联调;真实数据库迁移和在线 HTTP 业务验收尚未执行。**