[16928] Shopee AGV 与集波项目文档更新

This commit is contained in:
曾志威
2026-08-02 01:04:19 +08:00
parent 13e123e865
commit a690e5cf6e
9 changed files with 1655 additions and 1132 deletions
+31 -10
View File
@@ -4,7 +4,7 @@
## 1. 目标
实现 Shopee AGV 出库分拣:工作站登录 AGV 分拣模式后,系统把已释放的 AGV 区域拣货任务分配到分拣墙,按工作站 AGV 并发量下发周转箱搬运任务;周转箱到站后,操作员按批量或按单首选项扫描分拣,通过电子标签或手工确认完成格口任务,支持绑箱、满箱、封箱、缺料重分配和回库调度。
实现 Shopee AGV 出库分拣:工作站登录 AGV 分拣模式后,系统把已释放的 AGV 区域拣货任务分配到分拣墙,按工作站 AGV 并发量下发周转箱搬运任务;周转箱到站后,操作员按批量或按单首选项扫描分拣,通过电子标签或手工确认完成格口任务,支持绑箱、满箱、缺料重分配、三段式完成回传和回库调度。
## 2. 总体模块拆分
@@ -12,7 +12,7 @@
| --- | --- | --- | --- |
| P0 | 数据模型与配置 | `agv_preference``wcs_sorting_wall` AGV 分拣字段、工作站 AGV 并发量、菜单/字典配置 | 部分完成:`agv_preference` 建表/domain/table JSON/bill JSON 已落地 |
| P1 | 工作站 AGV 分拣入口 | 登录工作站、开始/退出/暂停/继续、出库单类型选择 | 部分完成:已存在 `AgvSortingService` 统一服务骨架 |
| P1 | 分拣墙箱绑定 | 绑箱、满箱、封箱、缺料按钮 | 待实现 |
| P1 | 分拣墙箱绑定 | 绑箱、满箱、缺料按钮;删除独立封箱按钮 | 待实现 |
| P1 | 任务分配计划任务 | 拣货任务分配到分拣墙,绑定上游波次号和分拣墙 | 待实现 |
| P1 | AGV 下发计划任务 | 控制工作站未完成 AGV 数量,生成/下发 `wcs_dispatch_task` | 待实现 |
| P1 | 扫箱分拣服务 | 扫周转箱、按首选项展示批量/按单分拣信息 | 待实现 |
@@ -110,6 +110,23 @@ docx 多次提到任务头自定义字段:
建议先确认任务头“自定义1-4”在当前数据库实际列名,再决定字段落点。
### 3.4 扩展 `shopee_wave` 格口/设备释放标记
新增字段 `deviceReleaseSts int not null default 0`,用于记录 3.1.9 回传完成后的格口/设备释放结果:
| 值 | 状态 | 处理规则 |
| ---: | --- | --- |
| `0` | 待释放 | 3.1.9 尚未成功,或成功后等待执行释放 |
| `1` | 已释放 | 格口/设备释放和灭灯均处理成功 |
| `-1` | 释放失败 | 保留现场绑定,由释放任务重试 |
`completeTaskUploadSts` 继续只表示 3.1.9 的 `confirm/get/uploadStatus` 回传状态,不与 `deviceReleaseSts` 合并。`uploadStatus` 成功后触发释放;释放结果单独写入 `deviceReleaseSts`,避免上游回传成功但现场释放失败时丢失重试依据。
计划文件:
- 修改数据库迁移,给 `shopee_wave` 增加 `deviceReleaseSts`
- 修改 `ShopeeWave.groovy``shopee_wave.json`,补充字段及状态说明。
- 三段式回传成功后的释放处理必须同时满足 `completeTaskUploadSts=SUCCESS``deviceReleaseSts in (0, -1)`,避免默认值为 `0` 的未完成波次被提前释放。
## 4. 后端服务计划
### 4.1 AGV 分拣工作站服务(部分完成)
@@ -137,10 +154,11 @@ docx 多次提到任务头自定义字段:
接口:
- `bindBox(session, warehouseCode, sortingWallCode, lpn)`:校验分拣墙存在、`lpn` 为空、调用 WMS 周转箱检查接口、回写 `wcs_sorting_wall.lpn`
- `replaceFullBox(session, warehouseCode, sortingWallCode, oldLpn, newLpn)`:满箱换箱;docx 此处描述仍写“检查可用并更新 LPN”,需确认是否要先封箱旧箱
- `sealBox(session, warehouseCode, sortingWallCode)`:要求 `syStatus=6`,清空 `taskId/lpn``syStatus=0`,下发灭灯。
- `replaceFullBox(session, warehouseCode, sortingWallCode, oldLpn, newLpn)`:满箱换箱并更新 LPN,不调用独立封箱接口
- `shortage(session, warehouseCode, sortingWallCode)`:二次确认后触发缺料重分配、缺料盘点单、继续显示下一货品或回库。
删除 `sealBox/sealDevice` 独立封箱入口。完成拣货改为参考接口 3.1.9 的 `confirm -> get -> uploadStatus` 三段式回传;仅在 `uploadStatus` 成功后释放格口/设备并下发灭灯,释放结果写入 `shopee_wave.deviceReleaseSts`,失败时保留原绑定等待重试。
依赖待确认:
- 上游 WMS 周转箱检查接口。
- 电子标签灭灯接口。
@@ -306,8 +324,8 @@ docx 多次提到任务头自定义字段:
- 暂停/继续:切换 `acceptStatus`
- 绑箱:扫货架号 + 周转箱号。
- 满箱:扫货架号 + 周转箱号。
- 封箱:扫货架号。
- 缺料:二次确认后触发缺料流程。
- 不显示封箱按钮,不提供封箱弹框或扫描货架号操作。
分拣区:
- 左侧:当前周转箱格口示意,支持 1/2/4/6/8 格,当前格口放大蓝色显示。
@@ -325,7 +343,7 @@ docx 多次提到任务头自定义字段:
待实现:
- 页面真实数据绑定。
- AGV 首选项弹窗/选择逻辑。
- 开始/退出/暂停/绑箱/满箱/封箱/任务确认按钮接口联动。
- 开始/退出/暂停/绑箱/满箱/任务确认按钮接口联动;删除封箱按钮逻辑
## 6. 接口清单
@@ -334,6 +352,9 @@ docx 多次提到任务头自定义字段:
| 周转箱检查 | WES -> 上游 WMS | 待接口 |
| AGV 任务创建 | WES -> WCS/AGV | 可复用现有 `AgvTaskCreateWcsCmd`,需补出库分拣参数 |
| AGV 任务完成回调 | WCS/AGV -> WES | 现有回调需确认是否覆盖 |
| 完成拣货 confirm | EDI -> WES | 参考 3.1.9,返回待回传任务 ID 列表并锁定批次 |
| 完成拣货 get | EDI -> WES | 参考 3.1.9,按任务 ID 返回完整拣货明细 |
| 完成拣货 uploadStatus | EDI -> WES | 参考 3.1.9,回写成功/失败;成功后触发释放并更新 `deviceReleaseSts` |
| AGV 任务取消/回库 | WES -> WCS/AGV | 可复用 `AgvTaskCancelWcsCmd`,回库任务需确认 |
| 电子标签亮红灯 | WES -> WCS | 待接口 |
| 电子标签灭灯 | WES -> WCS | 待接口 |
@@ -372,11 +393,11 @@ docx 多次提到任务头自定义字段:
- 已完成:`AgvPreferenceService` 通用 CRUD 服务。
- 待实现:用户权限过滤和默认首选项选择业务逻辑。
### Task 4:分拣墙绑箱/满箱/封箱/缺料
### Task 4:分拣墙绑箱/满箱/缺料
- 新增 `AgvSortingWallBoxService`
- 实现货架校验、LPN 校验、周转箱检查接口占位。
- 实现封箱清空和灭灯接口占位
- 删除封箱按钮及独立封箱接口,格口清理和灭灯由三段式完成回传成功状态触发
- 实现缺料入口:先记录待重分配和待盘点,等接口确认后补全。
### Task 5:波次任务分配到分拣墙
@@ -436,7 +457,7 @@ docx 多次提到任务头自定义字段:
- AGV 分拣首选项页面。
- 已完成:AGV 分拣工作台静态页面和自适应样式。
- 工作站登录/开始/暂停/退出操作。
- 绑箱/满箱/封箱/缺料弹框。
- 绑箱/满箱/缺料弹框;不提供封箱按钮和弹框。
- 分拣动态图和电子标签/手工确认交互。
- 按单模式增加交付阶段按钮和 PTL 分播口状态展示。
@@ -465,7 +486,7 @@ docx 多次提到任务头自定义字段:
| Task 1 | 数据模型与配置落地 | 2 - 3 | 1 | 0 - 1 | 3 - 5 | 包含迁移、domain/service、表单配置和常量 |
| Task 2 | 工作站 AGV 分拣入口 | 1 - 2 | 1 | 0 - 1 | 2 - 4 | currentJobMode、acceptStatus、系统日志 |
| Task 3 | AGV 分拣首选项 | 1 - 2 | 1 - 2 | 0 - 1 | 2 - 5 | CRUD、默认首选项、权限过滤 |
| Task 4 | 分拣墙绑箱/满箱/封箱/缺料入口 | 2 - 3 | 1 - 2 | 1 | 4 - 6 | 周转箱检查、灭灯接口先可占位 |
| Task 4 | 分拣墙绑箱/满箱/缺料入口 | 2 - 3 | 1 - 2 | 1 | 4 - 6 | 删除封箱入口,周转箱检查、灭灯接口先可占位 |
| Task 5 | 波次任务分配到分拣墙 | 3 - 4 | 0 | 1 | 4 - 5 | 包含 Redis 锁、任务头/分拣墙回写 |
| Task 6 | AGV 任务下发 | 2 - 3 | 0 | 1 - 2 | 3 - 5 | 复用现有 dispatch task,需确认 AGV 参数 |
| Task 7 | 扫箱与分拣展示 | 3 - 5 | 2 - 3 | 1 - 2 | 6 - 10 | 批量/按单查询差异、SKU/UID 扫描 |
@@ -0,0 +1,376 @@
# Shopee 3.2.13 部分更新销售订单接口测试用例
## 接口信息
- 接口:3.2.13 WMS -> WES 部分更新销售订单
- Shopee URL`POST /api/v2/automation/tovendor/outbound/salesorder/update_order`
- WES 本地入口:`POST http://127.0.0.1:9001/api/wms/api/sync/in`
- Content-Type`application/json`
- EDI 配置 key`update_order`
- WMS API`shopee.outbound.salesorder.updateOrder`
- XSLT`edi-shopee/src/main/resources/xslt/out/3_2_13_update_order.xslt`
- 测试日期:2026-07-28
- 测试仓库:`PHIXP`
## 测试范围
1. EDI XSLT 将 Shopee 原始字段转换为 WES 请求字段。
2. WES 必填字段校验和订单存在性校验。
3. `single_attr_list` 单选属性解析、订单扩展字段更新和属性字典同步。
4. `multi_attr_list` 多选属性解析、订单扩展字段更新和属性字典同步。
5. 相同请求重复执行时,属性字典不产生重复记录。
6. 测试数据清理和订单原值恢复。
本次请求格式以《Shopee Automation Vendor 接入协议手册出库模块映射WES字段V1.2.1》3.2.13 章节为准。协议请求使用 snake_case 字段;EDI XSLT 转换后,WES 内部请求使用 camelCase 字段。
本次未调用 Shopee 外部测试环境。测试按“协议请求 -> 本地 XSLT 转换 -> WES `9001` 内部接口 -> 数据库核验”拆分执行,因此报告同时保留协议请求和 WES 实测响应。Shopee 对外响应协议需在 EDI 对外路由可用后另行联调。
## 前置条件
1. WES 服务已在 `9001` 端口启动。
2. 数据库为 `ttx-xwms-test`,仓库为 `PHIXP`
3. 成功路径测试订单:`OBSGD0002607281457`
4. `config_detail` 已维护以下 `SHOPEE_ORDER_HEADER` 属性组:
- `service_code`
- `shop_id`
- `inner_packaging`
5. WES 内部接口使用请求头 `X-DB: ttx-xwms-test`
## 字段转换说明
| Shopee 字段 | WES 字段 | 数据库字段 |
| --- | --- | --- |
| `whs_id` | `warehouseCode` | `shipment_header.warehouseCode` |
| `order_number` | `orderNumber` | `shipment_header.erpOrderCode` |
| `single_attr_list.service_code` | `serviceCode` | `shipment_header_ext1.serviceCode` |
| `multi_attr_list.shop_id` | `shopId``shopIdStr` | `shipment_header_ext1.shopId``shopIdStr` |
| `multi_attr_list.inner_packaging` | `innerPackaging``innerPackagingType` | `shipment_header_ext1.innerPackaging``innerPackagingType` |
| `single_attr_list` | `singleAttrList` | `config_detail` 单选属性字典 |
| `multi_attr_list` | `multiAttrList` | `config_detail` 多选属性字典 |
## 3.2.13 标准请求格式
以下结构依据 V1.2.1 协议 3.2.13 章节。`whs_id``order_number` 必填,其余字段按“有传则更新,未传不更新”处理。
```json
{
"whs_id": "PHIXP",
"order_number": "OBSGD0002607281457",
"can_group_picking": 1,
"group_key": "standard_group",
"urgent_flag": 99,
"cut_off_time": 1767837541,
"ctime": 1767751167,
"ship_by_date": 1767751167,
"purchase_time": 1767751167,
"single_attr_list": [
{
"attr_key": "service_code",
"attr_value_id": "STANDARD",
"attr_value_type": "",
"attr_value_name": "STANDARD"
}
],
"multi_attr_list": [
{
"attr_key": "shop_id",
"attr_value_list": [
{
"attr_value_id": "12345678",
"attr_value_type": "",
"attr_value_name": "Electronics Store SG"
},
{
"attr_value_id": "87654321",
"attr_value_type": "",
"attr_value_name": "Fashion Store SG"
}
]
},
{
"attr_key": "inner_packaging",
"attr_value_list": [
{
"attr_value_id": "CONS-BUBBLE-S",
"attr_value_type": "3",
"attr_value_name": "Small Bubble Wrap"
},
{
"attr_value_id": "CONS-TAPE-01",
"attr_value_type": "3",
"attr_value_name": "Sealing Tape"
}
]
}
]
}
```
协议允许的单选属性包括 `service_code``channel_id``fulfillment_chain_id``order_structure``order_size``shop_group``store_id``parcel_id``lm_tracking_no``pickup_region``delivery_region``sls_tracking_no``actual_weight``lane_code``handover``zone_code_id``outer_packaging`
协议允许的多选属性包括 `shop_id``shopee_order_sn``inner_packaging`
## 测试结果汇总
| 用例 | 场景 | 预期结果 | 实测结果 |
| --- | --- | --- | --- |
| TC-01 | 空请求体 | 返回“无效的消息体” | 通过 |
| TC-02 | 订单不存在 | 按协议返回成功 | **不通过:WES 返回“出库单不存在”** |
| TC-03 | 现有订单,不传更新字段 | 返回成功且不修改业务字段 | 通过 |
| TC-04 | XSLT 转换单选和多选属性 | 生成合法 JSON,字段和值完整 | 通过 |
| TC-05 | `single_attr_list` 使用现有字典值 | 更新 `serviceCode`,字典记录保持唯一 | 通过 |
| TC-06 | `multi_attr_list` 使用现有字典值 | 更新店铺和内包装字段,字典记录保持唯一 | 通过 |
| TC-07 | 新单选、多选枚举增量同步 | 新增 1 条单选、2 条多选字典记录 | 通过 |
| TC-08 | 相同属性请求重复执行 | 接口均成功,字典不重复 | 通过 |
| TC-09 | 测试数据恢复与清理 | 订单恢复原值,临时字典记录为 0 | 通过 |
## TC-01 空请求体
协议请求:
```http
POST /api/v2/automation/tovendor/outbound/salesorder/update_order
Content-Type: application/json
{}
```
协议预期:请求无效。
WES 内部实测结果:
```json
{"code":"1","msg":"无效的消息体","notify":true,"error":true}
```
结论:必填字段校验生效。
## TC-02 订单不存在
协议请求体:
```json
{
"whs_id": "PHIXP",
"order_number": "CODEX-3-2-13-NOT-EXIST"
}
```
协议预期:订单不存在时返回成功。
WES 内部实测结果:
```json
{"code":"1","msg":"出库单不存在","notify":true,"error":true}
```
结论:按仓库和订单号查询生效,但返回行为不符合 3.2.13 协议“若订单不存在返回成功即可”的要求,记录为待修复问题。
## TC-03 现有订单无更新字段
协议请求体:
```json
{
"whs_id": "PHIXP",
"order_number": "202607280002_0"
}
```
预期及实测结果:
```json
{"code":"0","msg":"Success","notify":true,"error":false}
```
结论:订单存在时成功返回;未携带扩展字段和属性列表,不修改订单业务字段。
## TC-04 EDI XSLT 属性转换
协议 JSON 进入 EDI 后转换成供 XSLT 处理的中间 XML;本次用于验证的关键中间 XML:
```xml
<o>
<order_number>OBSGD0002607281457</order_number>
<single_attr_list>
<e>
<attr_key>service_code</attr_key>
<attr_value_id>STANDARD</attr_value_id>
<attr_value_name>STANDARD</attr_value_name>
</e>
</single_attr_list>
<multi_attr_list>
<e>
<attr_key>shop_id</attr_key>
<attr_value_list>
<e><attr_value_id>12345678</attr_value_id><attr_value_name>Electronics Store SG</attr_value_name></e>
<e><attr_value_id>87654321</attr_value_id><attr_value_name>Fashion Store SG</attr_value_name></e>
</attr_value_list>
</e>
<e>
<attr_key>inner_packaging</attr_key>
<attr_value_list>
<e><attr_value_id>CONS-BUBBLE-S</attr_value_id><attr_value_type>3</attr_value_type></e>
<e><attr_value_id>CONS-TAPE-01</attr_value_id><attr_value_type>3</attr_value_type></e>
</attr_value_list>
</e>
</multi_attr_list>
</o>
```
实测转换结果摘要:
```json
{
"serviceCode": "STANDARD",
"shopId": "[\"12345678\",\"87654321\"]",
"shopIdStr": "12345678,87654321",
"innerPackaging": "CONS-BUBBLE-S,CONS-TAPE-01",
"innerPackagingType": "3,3",
"singleAttrCount": 1,
"multiAttrCount": 2,
"shopValueCount": 2,
"innerValueCount": 2
}
```
结论:转换结果是合法 JSON,单选、多选列表及派生字段完整。
## TC-05 single_attr_list 现有字典值
协议请求关键字段:
```json
{
"whs_id": "PHIXP",
"order_number": "OBSGD0002607281457",
"single_attr_list": [
{
"attr_key": "service_code",
"attr_value_id": "STANDARD",
"attr_value_type": "",
"attr_value_name": "STANDARD"
}
]
}
```
实测结果:
- 接口返回 `code=0`
- `shipment_header_ext1.serviceCode = STANDARD`
- `config_detail``service_code / STANDARD` 数量为 `1`
- 字典说明为 `STANDARD`
## TC-06 multi_attr_list 现有字典值
协议请求关键字段:
```json
{
"whs_id": "PHIXP",
"order_number": "OBSGD0002607281457",
"multi_attr_list": [
{
"attr_key": "shop_id",
"attr_value_list": [
{"attr_value_id":"12345678","attr_value_type":"","attr_value_name":"Electronics Store SG"},
{"attr_value_id":"87654321","attr_value_type":"","attr_value_name":"Fashion Store SG"}
]
},
{
"attr_key": "inner_packaging",
"attr_value_list": [
{"attr_value_id":"CONS-BUBBLE-S","attr_value_type":"3","attr_value_name":"Small Bubble Wrap"},
{"attr_value_id":"CONS-TAPE-01","attr_value_type":"3","attr_value_name":"Sealing Tape"}
]
}
]
}
```
数据库实测结果:
```json
{
"shopId": "[\"12345678\", \"87654321\"]",
"shopIdStr": "12345678,87654321",
"innerPackaging": "CONS-BUBBLE-S,CONS-TAPE-01",
"innerPackagingType": "3,3"
}
```
字典核验:4 个多选枚举的记录数量均为 `1`,名称和 `attrValueType` 正确。
## TC-07 新枚举增量同步
测试值:
| 属性类型 | groupType | identifier | description | value1 |
| --- | --- | --- | --- | --- |
| 单选 | `service_code` | `CODEX3213_SC_20260728` | `Codex 3.2.13 single test` | `test` |
| 多选 | `shop_id` | `CODEX3213_SHOP_A` | `Codex 3.2.13 multi A` | `test` |
| 多选 | `shop_id` | `CODEX3213_SHOP_B` | `Codex 3.2.13 multi B` | `test` |
实测结果:
- 接口返回 `code=0`
- 订单 `serviceCode``shopId``shopIdStr` 更新为请求值。
- `config_detail` 新增 3 条记录,字段内容与请求一致。
## TC-08 重复请求幂等性
执行方式:连续两次发送 TC-07 的相同请求。
两次接口响应:
```json
[
{"attempt":1,"http":200,"response":{"code":"0","msg":"Success","notify":true,"error":false}},
{"attempt":2,"http":200,"response":{"code":"0","msg":"Success","notify":true,"error":false}}
]
```
数据库核验:
- `CODEX3213_SC_20260728``count = 1`
- `CODEX3213_SHOP_A``count = 1`
- `CODEX3213_SHOP_B``count = 1`
结论:重复请求不会生成重复属性字典记录。
## TC-09 数据恢复和清理
清理动作:
1. 通过 3.2.13 接口将订单字段恢复为测试前的值。
2. 先按仓库、记录类型和测试 identifier 查询临时记录 ID。
3. 仅按查询到的主键 `2444``2445``2446` 删除临时测试记录。
4. 再次查询订单字段和临时记录数量。
最终核验结果:
```json
{
"restoreResponse": {"code":"0","msg":"Success","notify":true,"error":false},
"deleted": 3,
"remaining": 0,
"order": {
"serviceCode": "STANDARD",
"shopId": "[\"12345678\", \"87654321\"]",
"shopIdStr": "12345678,87654321",
"innerPackaging": "CONS-BUBBLE-S,CONS-TAPE-01",
"innerPackagingType": "3,3"
}
}
```
结论:测试订单已恢复,临时测试字典无残留。
## 测试结论
3.2.13 接口的 XSLT 转换、单选属性、多选属性、属性字典新增、已有字典复用和重复请求去重均通过。
发现 1 项协议偏差:V1.2.1 协议要求订单不存在时返回成功,当前 WES 实现返回“出库单不存在”。该场景 TC-02 判定为不通过,需调整实现后复测。
本次未执行 Gradle 静态编译或自动化测试任务;结论来自 2026-07-28 的实际 HTTP 调用、XSLT 转换和数据库核验结果。
+22 -10
View File
@@ -65,6 +65,18 @@ operator_skills
| inventoryThreshold | 库存阈值-出库分拣使用 | int | | Y | 0 |
| 其他base预留字段等 | | | | | |
Shopee 波次释放标记
`shopee_wave` 新增 `deviceReleaseSts`,与 3.1.9 三段式回传字段 `completeTaskUploadSts` 分开维护。
| 字段名 | 含义 | 类型 | 非空 | 默认值 | 状态说明 |
| --- | --- | --- | --- | --- | --- |
| `deviceReleaseSts` | 格口/设备释放状态 | int | Y | 0 | `0` 待释放、`1` 已释放、`-1` 释放失败 |
- `completeTaskUploadSts` 仅表示 3.1.9 回传状态,不直接代表格口/设备已经释放。
- `uploadStatus` 回写 3.1.9 成功后执行格口/设备释放;释放成功写 `deviceReleaseSts=1`,失败写 `deviceReleaseSts=-1` 并进入释放重试。
- 释放任务必须同时满足 `completeTaskUploadSts=SUCCESS``deviceReleaseSts in (0, -1)`,不能只按释放标记查询。
## 实现状态 / TODO
### 第一阶段后端闭环已落地
@@ -75,9 +87,9 @@ operator_skills
- `exit`
- `assignShopeeWaveToAcceptedWorkStations`
- 已复用现有 `WcsWorkStationService.updateAcceptStatus` 做暂停/继续接单。
- 已复用现有 `WaveRuleMatchService.findBestMatchedRuleByWorkStationAndOutboundType` 分拣墙空闲格口的 Shopee 集波分配。
- 自动集波通过 `WaveRuleMatchService.matchByStation` 完成分拣墙空闲格口的 Shopee 集波分配。
- 已在绑箱/满箱后端校验格口必须已有 `shopeeWaveCode`,避免未分配波次的格口绑定箱号。
- 已在封箱后清理现有真实字段:`containerCode``shopeeWaveCode``currentWaveRule``useStatus`
- 工作站登录及 AGV 分拣页面不提供独立封箱按钮;完成拣货统一进入参考接口 3.1.9 的三段式回传流程
### 第一阶段的最小承载策略
@@ -94,7 +106,7 @@ operator_skills
- 换箱场景仅补了基础后端限制:
- `ticketType=5` 的 MTO 任务拒绝换箱
- Flow Pick 换波次逻辑待确认是否复用现有集波服务,否则需后续补充
- 封箱后的电子标签灭灯仍保留 TODO,待确认现场 PTL 服务接口后接入。
- 三段式回传成功后的格口释放和电子标签灭灯仍保留 TODO,待确认现场 PTL 服务接口后接入。
- 任务确认阶段的 `targetContainer` 承载字段本轮未新增。
- 如后续确认现有任务明细已有合适字段,可优先复用;否则再补迁移。
@@ -178,15 +190,15 @@ A点击开始按钮,弹框选择出库单类型。弹框显示的内容为出
- 补充:出库单ticketType=5(即MTO下发任务类型)的不允许换箱,如果点换箱时出库单头ticketType=5则提示:MTO非需求池类型不允许换箱
- G.封箱:点击封箱弹框显示(该波次全部拣完的情况)
- G.完成拣货回传:删除工作站登录及 AGV 分拣页面的封箱按钮、封箱弹框和扫描货架号逻辑,不再提供独立封箱接口。
- 货架号:XXXXXXX
完成拣货参考接口 3.1.9,采用 `confirm -> get -> uploadStatus` 三段式回传:
- 扫描货架号,判断必须在wcs_sorting_wall_cell.containerCode存在,若不存在,则报错
1. `confirm`:按仓库查询待回传任务,锁定本批任务并返回任务 ID 列表。
2. `get`:按任务 ID 返回本任务完整拣货明细,重试时必须保持报文一致。
3. `uploadStatus`:回写本次回传成功或失败;失败进入回传重试,成功后再处理 `deviceReleaseSts`,释放对应格口/设备并执行灭灯。
另外wcs_sorting_wall_cell的useStatus使用必须=’ USED’ 绑定状态,且当前槽口绑定的波次必须全部完成(task_detail.cellCode槽口字段,task_detail. shopeeWave波次字段),则将当前格口wcs_sorting_wall_cell.containerCod清空,useStatus更新为‘IDLE’空闲,还需下发当前格口灭灯指令。(之前下发过亮绿灯指令)
G:封箱后面增加缺料按钮
缺料按钮保留在分拣操作区:
点击按钮,弹框进行二次确认:是否确定短拣并进行重新分配,点击确定更新出库单明细短拣数量shortPickQty,则拿当前周转箱货品+批次的订单需要重新分配(根据短缺数量shortPickQty进行再跑对应波次分配库存,如果分配成功则按集波逻辑进行更新波次号、槽口号等,前端提示分配成功,继续进行下面的作业,如果分配失败则前端提示无可用库存已确认短拣)。
@@ -266,4 +278,4 @@ H、订单分拣完成
任务确认的时候,判断wcs_sorting_wall_cell.shopeeWave分拣墙格口的任务是否完成,若整个任务分拣完成,则下发当前格口电子标签绿灯,useStatus更新‘PENDING_OUT’待离。
UI分拣界面需要监测到电子标签拍灯接口,若报文返回的分拣墙货位wcs_sorting_wall_cell表的useStatus=PENDING_OUT’待离,则为封箱,将当前格口shopeeWavecontainerCode清空,useStatus更新为‘IDLE’空闲
UI 不再通过封箱按钮释放格口。任务完成后等待 3.1.9 三段式回传结果;`uploadStatus` 成功后处理 `deviceReleaseSts`,释放成功时清空当前格口 `shopeeWaveCode``containerCode`,将 `useStatus` 更新为 `IDLE` 并写 `deviceReleaseSts=1`;释放失败时保留现场绑定并写 `deviceReleaseSts=-1`,供释放任务重试。
+310
View File
@@ -0,0 +1,310 @@
# AnyTLS-go 备用服务器安装指南
## 1. 文档信息
| 项目 | 内容 |
| --- | --- |
| 目标 | 在 Linux 服务器上安装 AnyTLS-go,作为 VLESS + REALITY 主入口之外的备用入口 |
| 适用架构 | x86_64/amd64、aarch64/arm64 |
| 示例版本 | `v0.0.13` |
| 默认端口 | `8443/TCP` |
| 更新日期 | 2026-07-25 |
本文是 [VLESS + REALITY 与 AnyTLS 双协议部署方案](VLESS-Reality与AnyTLS双协议部署指南.md) 中 AnyTLS 备用入口的详细安装步骤。安装的是 [`anytls/anytls-go`](https://github.com/anytls/anytls-go) 参考服务端实现,默认独立监听 `8443/TCP`,不与监听 `443/TCP` 的 Xray 服务互相转发。执行前,将文中的端口、服务器地址和密码按实际环境填写。
> **重要限制**:官方将 AnyTLS-go 定位为协议参考实现,而不是功能完备的生产级服务端。当前 `anytls-server` 会在每次启动时生成一个短期自签名证书,不支持通过命令行加载正式证书。客户端需要允许该实现对应的不安全证书校验方式。若生产环境要求校验证书、域名和 SNI,应改用支持 AnyTLS 的 sing-box 服务端方案。
## 2. 部署参数
安装前确认以下参数:
| 参数 | 示例 | 说明 |
| --- | --- | --- |
| 服务器公网地址 | `<SERVER_IP_OR_DOMAIN>` | 客户端连接地址 |
| 服务端口 | `8443` | 必须为未占用的 TCP 端口 |
| 密码 | `<RANDOM_HEX_PASSWORD>` | 建议使用至少 32 字节随机值 |
| CPU 架构 | `amd64``arm64` | 由 `uname -m` 确认 |
同时确认云平台安全组、主机防火墙和上游网络均允许所选 TCP 端口入站。
## 3. 前置检查
登录服务器后执行:
```bash
uname -m
cat /etc/os-release
sudo ss -lntp 'sport = :8443'
```
架构对应关系:
| `uname -m` 输出 | 发布包架构 |
| --- | --- |
| `x86_64` | `amd64` |
| `aarch64``arm64` | `arm64` |
若端口检查有输出,说明 `8443` 已被占用,应先选择其他端口。
安装依赖,按服务器发行版选择一组命令:
```bash
# Debian / Ubuntu
sudo apt-get update
sudo apt-get install -y curl unzip openssl
```
```bash
# RHEL / Rocky Linux / AlmaLinux
sudo dnf install -y curl unzip openssl
```
## 4. 下载并安装
以下命令固定安装 `v0.0.13`,并使用 GitHub Release 提供的 SHA-256 摘要校验安装包:
```bash
ANYTLS_VERSION=0.0.13
case "$(uname -m)" in
x86_64)
ANYTLS_ARCH=amd64
EXPECTED_SHA256=7e80fc099ea54a71110d256dd60648c47c63c70a3c499eb1f6d7aaa4edb7016f
;;
aarch64|arm64)
ANYTLS_ARCH=arm64
EXPECTED_SHA256=88cb762c3c8eb56b46a2d8d6feab9c0858655192143fc164874229499246a956
;;
*)
echo "不支持的 CPU 架构: $(uname -m)" >&2
exit 1
;;
esac
WORK_DIR="$(mktemp -d)"
PACKAGE="anytls_${ANYTLS_VERSION}_linux_${ANYTLS_ARCH}.zip"
curl --fail --location \
--output "${WORK_DIR}/${PACKAGE}" \
"https://github.com/anytls/anytls-go/releases/download/v${ANYTLS_VERSION}/${PACKAGE}"
echo "${EXPECTED_SHA256} ${WORK_DIR}/${PACKAGE}" | sha256sum --check -
unzip "${WORK_DIR}/${PACKAGE}" -d "${WORK_DIR}/anytls"
sudo install -m 0755 "${WORK_DIR}/anytls/anytls-server" /usr/local/bin/anytls-server
/usr/local/bin/anytls-server -h
```
只有在校验结果显示 `OK` 后才继续安装。版本升级时,必须同时更新版本号和对应架构的官方 SHA-256 摘要。
## 5. 创建运行用户和配置
创建无登录权限的系统用户:
```bash
sudo groupadd --system anytls
sudo useradd --system --gid anytls --no-create-home --shell /usr/sbin/nologin anytls
sudo install -d -m 0750 -o root -g anytls /etc/anytls-go
```
生成一个 32 字节随机密码:
```bash
openssl rand -hex 32
```
记录输出结果,然后创建配置文件:
```bash
sudoedit /etc/anytls-go/anytls.env
```
写入以下内容,将占位符替换为刚生成的密码:
```ini
ANYTLS_LISTEN=0.0.0.0:8443
ANYTLS_PASSWORD=<RANDOM_HEX_PASSWORD>
```
设置配置文件权限:
```bash
sudo chown root:anytls /etc/anytls-go/anytls.env
sudo chmod 0640 /etc/anytls-go/anytls.env
```
> `anytls-server` 当前只支持通过 `-p` 参数接收密码,因此运行时密码会出现在服务进程的命令行参数中。配置文件权限只能减少静态文件泄露风险,不能消除这一实现限制。服务器应限制非必要的系统登录账号。
## 6. 创建 systemd 服务
创建服务文件:
```bash
sudoedit /etc/systemd/system/anytls-go.service
```
写入:
```ini
[Unit]
Description=AnyTLS-go Server
Documentation=https://github.com/anytls/anytls-go
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=anytls
Group=anytls
EnvironmentFile=/etc/anytls-go/anytls.env
ExecStart=/usr/local/bin/anytls-server -l ${ANYTLS_LISTEN} -p ${ANYTLS_PASSWORD}
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6
LockPersonality=true
MemoryDenyWriteExecute=true
UMask=0077
[Install]
WantedBy=multi-user.target
```
检查并启动服务:
```bash
sudo systemd-analyze verify /etc/systemd/system/anytls-go.service
sudo systemctl daemon-reload
sudo systemctl enable --now anytls-go
sudo systemctl status anytls-go --no-pager
```
## 7. 放行端口
除云平台安全组外,还需按服务器实际使用的防火墙选择一组命令。不要同时配置 UFW 和 firewalld。
UFW
```bash
sudo ufw allow 8443/tcp
sudo ufw status
```
firewalld
```bash
sudo firewall-cmd --permanent --add-port=8443/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
```
若使用了其他端口,需要同步修改防火墙规则和 `/etc/anytls-go/anytls.env`
## 8. 服务端验收
确认服务处于运行状态并监听预期端口:
```bash
sudo systemctl is-active anytls-go
sudo ss -lntp 'sport = :8443'
sudo journalctl -u anytls-go -n 50 --no-pager
```
期望日志包含类似内容:
```text
[Server] anytls-go ...
[Server] Listening TCP 0.0.0.0:8443
```
从另一台可访问服务器的设备检查公网端口:
```bash
nc -vz <SERVER_IP_OR_DOMAIN> 8443
```
端口连通只说明 TCP 链路正常,完整验收仍需通过 AnyTLS 客户端发起代理请求。
## 9. 客户端联调
使用同版本参考客户端进行基础联调:
```bash
./anytls-client \
-l 127.0.0.1:1080 \
-s <SERVER_IP_OR_DOMAIN>:8443 \
-p <RANDOM_HEX_PASSWORD>
```
另开一个终端,通过本地 SOCKS5 代理验证出口:
```bash
curl --proxy socks5h://127.0.0.1:1080 https://api.ipify.org
```
`v0.0.12` 及以上版本也支持 URI
```text
anytls://<RANDOM_HEX_PASSWORD>@<SERVER_IP_OR_DOMAIN>:8443
```
使用 sing-box、mihomo、Shadowrocket 等第三方客户端时,至少需要填写服务器地址、端口和相同密码。由于参考服务端使用临时自签名证书,还需要按客户端文档配置对应的跳过证书校验选项;不要将这一配置套用到支持正式证书的其他服务端。
## 10. 日常运维
常用命令:
```bash
sudo systemctl restart anytls-go
sudo systemctl stop anytls-go
sudo systemctl start anytls-go
sudo journalctl -u anytls-go -f
```
修改端口或密码后执行:
```bash
sudo systemctl restart anytls-go
sudo systemctl status anytls-go --no-pager
```
升级时下载目标版本对应架构的发布包,核对官方摘要,重新安装 `/usr/local/bin/anytls-server`,然后重启并重复第 8、9 节的验收步骤。
## 11. 故障排查
| 现象 | 检查项 |
| --- | --- |
| 服务启动失败 | `journalctl -u anytls-go -n 100 --no-pager`;检查环境文件格式及权限 |
| 提示端口被占用 | `ss -lntp 'sport = :8443'`;更换端口或停止冲突服务 |
| 外网无法连接 | 云安全组、主机防火墙、运营商或机房入站策略 |
| TCP 可连接但代理失败 | 客户端协议类型、端口、密码、证书校验设置是否一致 |
| 重启后客户端证书报错 | 参考服务端每次启动都会重新生成临时自签名证书 |
| 下载速度异常 | 服务器线路质量、丢包、MTU、客户端实现和 CPU 使用率 |
## 12. 卸载
确认不再使用后执行:
```bash
sudo systemctl disable --now anytls-go
sudo rm /etc/systemd/system/anytls-go.service
sudo systemctl daemon-reload
sudo rm /usr/local/bin/anytls-server
sudo rm /etc/anytls-go/anytls.env
sudo rmdir /etc/anytls-go
sudo userdel anytls
```
最后删除云安全组和主机防火墙中的 AnyTLS 端口放行规则。上述操作会删除服务配置和密码,执行前应确认没有其他服务复用这些文件或账号。
## 13. 参考资料
- [AnyTLS-go 官方仓库](https://github.com/anytls/anytls-go)
- [AnyTLS-go Releases](https://github.com/anytls/anytls-go/releases)
- [AnyTLS 协议文档](https://github.com/anytls/anytls-go/blob/main/docs/protocol.md)
- [sing-box AnyTLS 文档](https://sing-box.sagernet.org/configuration/inbound/anytls/)
@@ -0,0 +1,490 @@
# VLESS + REALITY 与 AnyTLS 双协议部署指南
## 1. 方案目标
在同一台 Linux 服务器上部署两个相互独立的代理入口:
| 优先级 | 协议 | 服务端实现 | 监听端口 | 用途 |
| --- | --- | --- | --- | --- |
| 主力 | VLESS + TCP + XTLS Vision + REALITY | Xray-core | `443/TCP` | 日常主要连接 |
| 备用 | AnyTLS | AnyTLS-go | `8443/TCP` | 主力入口不可用时切换 |
```text
客户端
|-- 主力节点 ------ TCP/443 ------> Xray: VLESS + REALITY ------> Internet
`-- 备用节点 ------ TCP/8443 ------> AnyTLS-go -----------------> Internet
```
两个服务不互相转发,也不共享端口。所谓“主力/备用”由客户端节点选择或客户端故障转移策略决定;服务器不会自动把 VLESS 连接切换到 AnyTLS。
本文示例版本和核对日期:
| 软件 | 示例版本 | 核对日期 |
| --- | --- | --- |
| Xray-core | `v26.3.27` | 2026-07-25 |
| AnyTLS-go | `v0.0.13` | 2026-07-25 |
## 2. 部署前准备
准备以下参数,文档中的尖括号内容均为待替换占位符:
| 参数 | 用途 |
| --- | --- |
| `<SERVER_IP>` | 服务器公网 IP;连接 REALITY 不要求拥有域名 |
| `<REALITY_TARGET>` | REALITY 借用 TLS 外观的目标站点域名 |
| `<VLESS_UUID>` | VLESS 用户 ID |
| `<REALITY_PRIVATE_KEY>` | 只保存在服务端的 X25519 私钥 |
| `<REALITY_PASSWORD>` | 与私钥对应、配置在客户端的公开连接参数 |
| `<REALITY_SHORT_ID>` | 最长 16 位、偶数长度的十六进制字符串 |
| `<ANYTLS_PASSWORD>` | AnyTLS 的独立随机密码 |
安全组和服务器防火墙需要放行 `443/TCP``8443/TCP`。不需要放行 UDP 端口。
## 3. 系统检查
```bash
uname -m
cat /etc/os-release
timedatectl status
sudo ss -lntp 'sport = :443 or sport = :8443'
```
要求:
- Linux 使用 systemd。
- CPU 架构为 `x86_64``aarch64``arm64`
- `443``8443` 未被其他进程监听。
- 系统时间同步正常。
安装依赖时按发行版选择一组命令:
```bash
# Debian / Ubuntu
sudo apt-get update
sudo apt-get install -y curl unzip openssl ca-certificates
```
```bash
# RHEL / Rocky Linux / AlmaLinux
sudo dnf install -y curl unzip openssl ca-certificates
```
## 4. 安装 Xray-core
以下命令固定安装 `v26.3.27`,并根据服务器架构选择 GitHub Release 文件与 SHA-256 摘要:
```bash
XRAY_VERSION=26.3.27
case "$(uname -m)" in
x86_64|amd64)
XRAY_ARCH=64
XRAY_SHA256=23cd9af937744d97776ee35ecad4972cf4b2109d1e0fe6be9930467608f7c8ae
;;
aarch64|arm64)
XRAY_ARCH=arm64-v8a
XRAY_SHA256=4d30283ae614e3057f730f67cd088a42be6fdf91f8639d82cb69e48cde80413c
;;
*)
echo "不支持的 CPU 架构: $(uname -m)" >&2
exit 1
;;
esac
XRAY_WORK_DIR="$(mktemp -d)"
XRAY_PACKAGE="Xray-linux-${XRAY_ARCH}.zip"
curl --fail --location \
--output "${XRAY_WORK_DIR}/${XRAY_PACKAGE}" \
"https://github.com/XTLS/Xray-core/releases/download/v${XRAY_VERSION}/${XRAY_PACKAGE}"
echo "${XRAY_SHA256} ${XRAY_WORK_DIR}/${XRAY_PACKAGE}" | sha256sum --check -
unzip "${XRAY_WORK_DIR}/${XRAY_PACKAGE}" -d "${XRAY_WORK_DIR}/xray"
sudo install -m 0755 "${XRAY_WORK_DIR}/xray/xray" /usr/local/bin/xray
/usr/local/bin/xray version
```
只有摘要检查显示 `OK` 后才继续。升级版本时必须同时更新版本号和相应架构的官方摘要。
创建专用账号和配置目录:
```bash
sudo groupadd --system xray
sudo useradd --system --gid xray --no-create-home --shell /usr/sbin/nologin xray
sudo install -d -m 0750 -o root -g xray /usr/local/etc/xray
```
## 5. 选择 REALITY 目标站点
REALITY 鉴权失败时,Xray 会将流量转发给 `target`。目标选择不当可能让服务器成为第三方站点的转发入口,因此不要直接照抄一个公共示例域名。
目标站点应满足:
- 从该服务器访问延迟低、连接稳定,优先选择与服务器相同 ASN 的站点。
- 支持 TLS 1.3,并能正常完成 TLS 握手。
- `serverNames` 使用目标证书 SAN 中存在的域名。
- 避免使用 Cloudflare 等公共 CDN 站点,减少被扫描后偷跑转发流量的风险。
- 不使用银行、支付、政府或其他敏感站点。
对候选站点执行:
```bash
/usr/local/bin/xray tls ping <REALITY_TARGET>:443
openssl s_client -connect <REALITY_TARGET>:443 -servername <REALITY_TARGET> </dev/null
```
确认握手、证书域名和延迟符合要求后,再固定 `<REALITY_TARGET>`。服务端 `target``serverNames`、客户端 `serverName` 必须使用同一目标域名。
## 6. 生成 VLESS 和 REALITY 参数
生成 VLESS UUID
```bash
/usr/local/bin/xray uuid
```
生成 REALITY X25519 密钥对:
```bash
/usr/local/bin/xray x25519
```
记录输出中的两项:
- `PrivateKey`:填入服务端 `<REALITY_PRIVATE_KEY>`,不得发送给客户端或他人。
- `Password`:填入客户端 `<REALITY_PASSWORD>`。这是当前 Xray 对原 `publicKey` 字段的新名称,不是登录密码。
生成 8 字节 short ID
```bash
openssl rand -hex 8
```
将 16 位十六进制输出记录为 `<REALITY_SHORT_ID>`。UUID、私钥和 short ID 不要使用本文中的占位符或公共示例值。
## 7. 配置 VLESS + REALITY 主入口
创建配置:
```bash
sudoedit /usr/local/etc/xray/config.json
```
写入以下 JSON 并替换全部占位符:
```json
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "vless-reality-in",
"listen": "0.0.0.0",
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "<VLESS_UUID>",
"flow": "xtls-rprx-vision",
"email": "primary-client"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"target": "<REALITY_TARGET>:443",
"xver": 0,
"serverNames": [
"<REALITY_TARGET>"
],
"privateKey": "<REALITY_PRIVATE_KEY>",
"shortIds": [
"<REALITY_SHORT_ID>"
]
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls",
"quic"
],
"routeOnly": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": [
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.168.0.0/16",
"198.18.0.0/15",
"224.0.0.0/4",
"::1/128",
"fc00::/7",
"fe80::/10"
],
"outboundTag": "block"
}
]
}
}
```
上述路由规则阻止代理客户端访问服务器所在的常见私网、链路本地地址和云元数据地址。若确实需要通过代理访问某个私网,应逐项评估后修改对应网段,不要直接删除整个限制。
设置权限并验证配置:
```bash
sudo chown root:xray /usr/local/etc/xray/config.json
sudo chmod 0640 /usr/local/etc/xray/config.json
sudo -u xray /usr/local/bin/xray run -test -config /usr/local/etc/xray/config.json
```
验证失败时不要启动服务,先根据输出修正占位符和 JSON 格式。
## 8. 创建 Xray systemd 服务
```bash
sudoedit /etc/systemd/system/xray.service
```
写入:
```ini
[Unit]
Description=Xray VLESS REALITY Server
Documentation=https://github.com/XTLS/Xray-core
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=xray
Group=xray
ExecStart=/usr/local/bin/xray run -config /usr/local/etc/xray/config.json
Restart=on-failure
RestartSec=5s
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6
LockPersonality=true
MemoryDenyWriteExecute=true
UMask=0077
[Install]
WantedBy=multi-user.target
```
`CAP_NET_BIND_SERVICE` 只用于让非 root 的 `xray` 用户监听 `443`,不要将服务改成 root 运行。
启动主入口:
```bash
sudo systemd-analyze verify /etc/systemd/system/xray.service
sudo systemctl daemon-reload
sudo systemctl enable --now xray
sudo systemctl status xray --no-pager
```
## 9. 部署 AnyTLS 备用入口
按照 [AnyTLS-go 服务器安装指南](AnyTLS-go服务器安装指南.md) 完成以下部分:
1. 安装 AnyTLS-go `v0.0.13`
2. 创建独立的 `anytls` 系统账号。
3. 生成与 VLESS UUID、REALITY 密钥完全无关的 AnyTLS 随机密码。
4. 保持监听地址为 `0.0.0.0:8443`
5. 启动 `anytls-go.service`
AnyTLS-go 是协议参考实现,会在启动时生成临时自签名证书,客户端必须使用与该实现相符的跳过证书校验设置。它不应占用 `443`,也不要与 Xray 共用账号、配置或密钥。
## 10. 防火墙和安全组
云平台安全组放行:
| 协议 | 端口 | 来源 |
| --- | --- | --- |
| TCP | `443` | 实际客户端来源;无法固定时为公网 |
| TCP | `8443` | 实际客户端来源;无法固定时为公网 |
主机使用 UFW 时:
```bash
sudo ufw allow 443/tcp
sudo ufw allow 8443/tcp
sudo ufw status
```
主机使用 firewalld 时:
```bash
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --add-port=8443/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
```
UFW 与 firewalld 只选择服务器实际使用的一种,不要重复配置。
## 11. 客户端参数
### 11.1 VLESS + REALITY 主节点
| 客户端字段 | 值 |
| --- | --- |
| 地址 | `<SERVER_IP>` |
| 端口 | `443` |
| 协议 | VLESS |
| UUID | `<VLESS_UUID>` |
| Flow | `xtls-rprx-vision` |
| 传输 | TCP/RAW |
| 传输安全 | REALITY |
| SNI/Server Name | `<REALITY_TARGET>` |
| Fingerprint | `chrome` |
| Public Key/Password | `<REALITY_PASSWORD>` |
| Short ID | `<REALITY_SHORT_ID>` |
| Spider X | `/` 或留空 |
常见客户端仍可能把当前 Xray 文档中的 `password` 显示为 `Public Key``pbk`,填入的都是 `xray x25519` 输出的 `Password` 值。
通用分享 URI 结构:
```text
vless://<VLESS_UUID>@<SERVER_IP>:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=<REALITY_TARGET>&fp=chrome&pbk=<REALITY_PASSWORD>&sid=<REALITY_SHORT_ID>&type=tcp#VLESS-REALITY
```
### 11.2 AnyTLS 备用节点
| 客户端字段 | 值 |
| --- | --- |
| 地址 | `<SERVER_IP>` |
| 端口 | `8443` |
| 协议 | AnyTLS |
| 密码 | `<ANYTLS_PASSWORD>` |
| 跳过证书验证 | 仅对 AnyTLS-go 参考服务端启用 |
参考 URI
```text
anytls://<ANYTLS_PASSWORD>@<SERVER_IP>:8443
```
### 11.3 主备策略
客户端中创建两个独立节点,顺序保持 VLESS + REALITY 在前、AnyTLS 在后。需要自动切换时,使用客户端自身的 `fallback` 或健康检查组;需要稳定可控时,使用手动选择组。
不要把两个协议配置成同时向同一连接转发。切换策略只发生在客户端节点层。
## 12. 联合验收
服务端检查:
```bash
sudo systemctl is-active xray
sudo systemctl is-active anytls-go
sudo ss -lntp 'sport = :443 or sport = :8443'
sudo journalctl -u xray -n 50 --no-pager
sudo journalctl -u anytls-go -n 50 --no-pager
```
预期结果:
- `xray``anytls-go` 均返回 `active`
- Xray 监听 `0.0.0.0:443`AnyTLS-go 监听 `0.0.0.0:8443`
- 日志中没有配置解析、权限或端口占用错误。
客户端按以下顺序验收:
1. 只选择 VLESS + REALITY 节点,访问出口 IP 检测网站并完成实际网页、下载测试。
2. 只选择 AnyTLS 节点,重复相同测试。
3. 在客户端关闭或临时禁用主节点,确认主备组能切换到 AnyTLS。
4. 恢复主节点,确认客户端策略按预期回到 VLESS + REALITY。
端口连通性可从外部设备辅助检查:
```bash
nc -vz <SERVER_IP> 443
nc -vz <SERVER_IP> 8443
```
TCP 端口可连接不代表协议配置正确,最终以代理请求成功为准。
## 13. 日常运维
```bash
# 查看状态
sudo systemctl status xray anytls-go --no-pager
# 查看实时日志
sudo journalctl -u xray -f
sudo journalctl -u anytls-go -f
# 重启单个入口
sudo systemctl restart xray
sudo systemctl restart anytls-go
```
修改 Xray 配置前先备份私钥和客户端参数;修改后先执行配置测试,再重启:
```bash
sudo -u xray /usr/local/bin/xray run -test -config /usr/local/etc/xray/config.json
sudo systemctl restart xray
sudo systemctl status xray --no-pager
```
升级任一服务时,只替换对应二进制并单独验收,不要同时升级两个入口。这样其中一个入口出现兼容问题时,另一个仍可用于连接。
## 14. 故障定位
| 现象 | 优先检查 |
| --- | --- |
| Xray 无法启动 | JSON 校验、配置权限、`443` 占用、systemd capability |
| REALITY 握手失败 | UUID、`flow``serverName``password/pbk`、short ID 是否完全一致 |
| REALITY 偶发超时 | 目标站点连通性、服务器时间、线路丢包、目标是否变更 TLS 配置 |
| AnyTLS 无法启动 | 环境文件权限、密码是否填写、`8443` 占用 |
| 端口都通但无法代理 | 客户端协议字段、服务日志、安全软件或上游网络限制 |
| 主节点故障后未切换 | 客户端是否使用 fallback/健康检查组及其探测 URL、间隔设置 |
| 服务器出现异常转发流量 | REALITY 目标是否为公共 CDN;重新选择同 ASN 的非 CDN 目标 |
## 15. 参考资料
- [Xray-core 官方仓库](https://github.com/XTLS/Xray-core)
- [Xray 官方 REALITY 配置文档](https://xtls.github.io/config/transports/reality.html)
- [Xray 官方 VLESS + TCP + XTLS Vision + REALITY 示例](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-Vision-REALITY)
- [Xray-core Releases](https://github.com/XTLS/Xray-core/releases)
- [AnyTLS-go 官方仓库](https://github.com/anytls/anytls-go)
- [AnyTLS-go 服务器安装指南](AnyTLS-go服务器安装指南.md)
@@ -1,316 +1,215 @@
# WaveRuleMatchService 流程图
# WaveRuleMatchService 当前流程图
> 对应实现:`C:\work\gitlab\shopee\wes-loghub\wms-wave\src\main\groovy\com\ittx\wms\wave\service\hairo\WaveRuleMatchService.groovy`
>
> 说明:本文是 AI 可直接读取的纯文本流程图,不使用图片。
> 口径说明:`WES-WMS` 指当前项目实现侧,`Shopee` 指需要调用或接收交互的外部接口方。
> 目标:把当前代码的入口、分流、外部接口调用、回滚和状态回写,整理成可检索的文本流程图。
> 对应当前 `WaveRuleMatchService.groovy`,不包含已删除的历史方法或未落地占位流程。
---
## 1. 读取方式
建议按下面顺序理解本文:
1. 先看 `2. 主流程总图`
2. 再看 `3. 分支流程`
3. 最后看 `4. 方法映射表`
如果只想快速定位代码路径,直接看 `4. 方法映射表`
---
## 2. 主流程总图
### 2.1 计划集波工作站入口
## 1. 总入口
```text
assignShopeeWaveToAcceptedWorkStations(session, warehouseCode[, workStationCode])
├─ 查询候选工作站
│ ├─ currentJobMode 为拣选模式
│ ├─ acceptStatus = OPEN
│ └─ status = ENABLE
├─ 统计各工作站的可用空格口
├─ 分播墙 status = ENABLE
├─ 格口 status = ENABLE
│ └─ 格口 shopeeWaveCode 为空
├─ 多工作站排序
可用空格口数量降序
数量相同时工作站 ID 升序
└─ 按排序结果逐个调用统一集波匹配流程
├─ 无启用分播墙 → 跳过当前工作站
└─ 单个工作站失败 → 记录日志并继续后续工作站
assignShopeeWaveToAcceptedWorkStations(session, warehouseCode, workStationCode)
├─ 查询启用且可接单的拣选工作站
├─ 按空格口数降序、工作站 ID 升序
└─ for each workstation
└─ matchByStation(session, workstation)
├─ 校验 status / warehouseCode / acceptStatus
├─ 获取 ruleLockKey(warehouseCode)
├─ ORDER_PICK
│ └─ assignSingles
BATCH_PICK
操作员技能与工作站 waveRule 取交集
├─ fillShortage
└─ 无补单结果时按 ticketType 分流
├─ 1/5 → assignTaskTicket
├─ 2/3/4 → assignOrder
└─ 6 → assignRt
```
### 2.2 单工作站集波入口
## 2. 快速拣选
```text
自动集波工作站扫描
matchByStation(session, workstation)
├─ [1] 从 workstation 读取 id、warehouseCode、code、shipmentType
├─ [2] 工作站状态校验
├─ status != ENABLE → MSG_WRM_0005
└─ warehouseCode 为空 → MSG_WRM_0006
├─ [3] 接单状态校验
├─ acceptStatus != OPEN → MSG_WRM_0015
│ └─ 通过 → 继续
├─ [4] 仓库维度 Redis 锁
├─ ruleLockKey(warehouseCode)
├─ tryLock 失败 → MSG_WRM_0018
│ └─ 获取成功 → 继续
├─ [5] ORDER_PICK → assignSingles
├─ [6] BATCH_PICK
│ ├─ 取操作员与工作站 Wave Rule 交集
│ ├─ fillShortage
│ └─ 补单未分配时,按规则 ticketType 进入独立方法
│ ├─ 1/5 → assignTaskTicket
│ ├─ 2/3/4 → assignOrder
│ └─ 6 → assignRt
├─ [7] 有任一分流完成分配 → success
└─ [8] finally
├─ 写入结束日志
└─ unlock
assignSingles(warehouseCode, workstation)
findSingleEmptyCells
├─ findSingleShipments
└─ for each shipment
├─ findShipmentExt
├─ 波次号来源
├─ 已有 shopeeWaveCode → 复用
├─ RT → shipmentCode
│ └─ 其他 → acquireWave
├─ RT → backfillRtShopeeWaveCode
├─ 已有外部波次号 → createInternalShopeeWave
├─ occupySingleCell
├─ createWave
├─ syncShopeePickingTaskAndFilterAcceptedShipments(ticketType)
└─ RT 跳过外部调用
├─ updateLocalShopeeWaveBinding(ruleCode='')
└─ addToWave
```
---
## 3. 分支流程
### 3.1 一波一单分流
## 3. 任务单
```text
assignSingleShipmentWaves(warehouseCode, shipmentType, workstationId, rules)
├─ for each rule
├─ findCells(session, workstationId)
│ ├─ matchEmptyCells(cells)
├─ findSingleShipmentWaveShipments(session, warehouseCode, shipmentType, rule, emptyCells.size())
│ ├─ if 未命中 → 继续下一个 rule
│ └─ if 命中
├─ 取当前空闲格口的首个 cell
├─ occupySingleShipmentSortingWallCell
├─ 若不是 RT 且 shipment_header_ext1.shopeeWave 为空
│ │ ├─ acquireUnusedShopeeWave
│ │ ├─ RT 或已有 shopeeWaveCode 时创建 shopee_wave
│ │ └─ 传 ticketType 同步 picking task(仅 RT 跳过外部调用)后直接本地回写
│ ├─ createInternalWave
│ ├─ addShipmentsToInternalWave
│ └─ 记录成功日志
└─ 返回 success 或 error
assignTaskTicket(warehouseCode, workstation, rule, ticketType)
│ ticketType = SALES_TASK / MTO_TASK
├─ findSingleEmptyCells(workstation.id, rule)
├─ findTaskTicketShipments
└─ for each shipment
├─ 读取 shipment_header_ext1.shopeeWaveCode
├─ createInternalShopeeWave
├─ occupySingleCell
├─ createWave
├─ syncShopeePickingTaskAndFilterAcceptedShipments(ticketType)
├─ updateLocalShopeeWaveBinding
└─ addToWave
```
关键点:
- `RT` 不申请 Shopee 外部波次号,使用 `shipmentCode` 作为本地 `shopeeWaveCode`
- `backfillRtShopeeWaveCode` 仅由 RT 分支调用,方法内部不再重复判断 `ticketType`
-`RT` 的一波一单,若本地没有 `shopeeWave`,会先取号再绑定。
- 一波一单是“1 单 -> 1 格口 -> 1 内部波次”的结构。
### 3.2 补缺口分流
## 4. 普通订单
```text
fillShortage(warehouseCode, workstation) → shipmentType 直接读取 workstation.shipmentType
├─ findShortage(workstationId) → 仅选择 shopee_wave.status=200 的关联格口
├─ for each cell
│ ├─ 如果 cell 未绑定 shopeeWaveCode → 跳过
│ ├─ calcNeedQty
├─ needQty <= 0 → 跳过
├─ needQty > 0 → 加入需补货格口明细
├─ resolveBoundShipmentGroup
findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
─ assignToCell
│ └─ 记录补单成功或失败日志
├─ MSG_WRM_0244 → 记录需补货格口数量及 cellCode|shopeeWaveCode|currentWaveRule|ticketType|needQty
└─ 返回
assignOrder(warehouseCode, workstation, rule, ticketType)
│ ticketType = SALES_ORDER / RTS / MTO_ORDER
├─ findUnusedCells(workstation.id, rule)
└─ 首个可用格口
├─ findAvailableShipments
├─ hasEmptyCellMinShipments
├─ acquireWave
assignAcquiredWaveToCell
─ assignToCell
```
关键点:
- 候选资格只判断当前工作站格口关联的 `shopee_wave.status=200`
- 不按分播墙状态、格口状态、ticketType、任务完成时间或 `waitingTime` 过滤候选。
- 补单执行时从已绑定订单解析 `groupKey`,保持同一 Shopee 波次订单特征一致。
- 补缺口不重新取新 Shopee 波次号。
### 3.3 空格口首次分配
## 5. RT
```text
assignEmptyCells(warehouseCode, shipmentType, workstationId, rules)
├─ for each rule
├─ findCells(session, workstationId)
│ ├─ assignCellNumbers(warehouseCode, shipmentType, workstationId, rule, cells)
├─ 若本轮分配成功 → break
└─ 否则继续下一个 rule
└─ 返回 success
assignRt(warehouseCode, workstation, rule)
├─ findSingleEmptyCells(workstation.id, rule)
├─ findRtTicketShipments
└─ for each shipment
├─ shipmentCode 作为本地 Shopee 波次号
├─ backfillRtShopeeWaveCode
├─ occupySingleCell
├─ createWave
├─ updateLocalShopeeWaveBinding
└─ addToWave
```
`assignCellNumbers` 的核心逻辑:
RT 不申请接口号段,不调用 Shopee picking task 外部接口。
## 6. 缺口补单
```text
assignCellNumbers(...)
├─ for each cell
读取 cell.shopeeWaveCode
│ ├─ 如果 cell 没有 shopeeWaveCode
│ │ ├─ findAvailableShipments(session, warehouseCode, rule, ..., workstation)
│ │ ├─ acquireUnusedShopeeWave
│ │ └─ 得到新 shopeeWaveCode
├─ 如果 cell 已有 shopeeWaveCode
│ │ ├─ calcNeedQty
│ │ ├─ needQty <= 0 → 跳过
│ │ ├─ resolveBoundShipmentGroup
│ │ └─ findAvailableShipments(..., shopeeWaveCode, groupKey)
│ └─ assignToCell
└─ 返回 success / error
fillShortage(warehouseCode, workstation)
├─ findShortage(workstation.id)
只选择关联 shopee_wave.status=200 的格口
└─ for each cell
├─ 按 currentWaveRule 读取启用规则
├─ calcNeedQty
├─ 记录需补单格口处理日志
├─ resolveBoundShipmentGroup
├─ findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
└─ assignToCell
```
---
## 4. 关键子流程
### 4.1 assignToCell
## 7. 单格口分配
```text
assignToCell(warehouseCode, rule, cell, shipmentIds, shopeeWaveCode, ticketType, workstation)
├─ pickCellShipmentIds
├─ ensureCellReadyForWave
│ ├─ 已有同 wave 同 rule 的 USED 格口 → 直接复用
│ └─ 否则走 occupySortingWallCell
assignToCell(...)
├─ 获取 cellLockKey(cell.id)
├─ pickCellShipments
├─ occupyCell
├─ createWave
├─ Shopee 同步
│ ├─ 新格口 → syncShopeePickingTaskAndFilterAcceptedShipments
│ └─ 同波次补单 → syncShopeeAppendOrderAndFilterAcceptedShipments
├─ bindBatchShipmentWave
│ ├─ waveBindLockKey
│ ├─ isWaveAvailable
│ └─ updateLocalShopeeWaveBinding
├─ createInternalWave
├─ addShipmentsToInternalWave
│ ├─ shipmentHeaderService.addMultipleToWave
│ └─ waveSvc.run
└─ 清空当前候选池,返回 success
├─ addToWave
├─ 达到 maxShipments → status=FULL
└─ finally unlock
```
### 4.2 分类型 Shopee 波次绑定
## 8. Shopee 同步
```text
updateLocalShopeeWaveBinding(...) [快速拣选直接调用]
updateLocalShopeeWaveBinding(...) [Sales/MTO 任务单直接调用]
bindBatchShipmentWave(...) [普通批量订单]
├─ 校验参数和调用方传入的 ticketType
├─ waveBindLockKey
├─ 获取 Shopee 波次绑定锁
├─ isWaveAvailable
│ └─ 方法内直接查询并确认 shopee_wave 状态可用
├─ 若成功
│ └─ updateLocalShopeeWaveBinding
│ └─ 统一回写格口、内部 wave 和 shopee_wave
└─ finally 解锁
syncShopeePickingTaskAndFilterAcceptedShipments(..., ticketType)
├─ RT → 返回 acceptedIds,不调用外部接口
└─ 其他类型
├─ sendShopeePickingTaskRequest
├─ updateShopeeWaveStartPickUploadStatus
├─ resolveKickedShipmentIds
├─ markKickOut
└─ writePickingTaskSyncWaveCode
```
重要说明:
- 快速拣选和 Sales/MTO 任务单直接调用本地回写方法;普通批量订单使用独立绑定方法。
- 普通批量订单由调用方传入当前分支已确定的 `ticketType`
- 真实的 3.1.6/3.4.6 绑定接口在工作站绑箱时同步调用。
### 4.3 createInternalWave / addShipmentsToInternalWave
```text
createInternalWave(session, warehouseCode, rule)
├─ resolveMasterCode
├─ waveSvc.createInternalWave
返回 waveId
addShipmentsToInternalWave(shipmentIds, waveId)
├─ shipmentHeaderService.addMultipleToWave
├─ waveSvc.run
└─ run 失败时 cancelInternalWave
syncShopeeAppendOrderAndFilterAcceptedShipments(...)
├─ sendShopeeAppendOrderRequest
├─ resolveKickedShipmentIds
markKickOut
└─ 返回 acceptedIds
```
### 4.4 回滚与释放
## 9. 波次号与本地绑定
```text
回滚点
├─ 格口抢占失败
│ └─ 直接返回 false / error,继续尝试下一个格口或规则
├─ Shopee 波次绑定失败
markKickOutWave
├─ 内部波次创建失败
releaseSingleShipmentSortingWallCell / 返回 error
├─ 内部波次运行失败
│ └─ cancelInternalWave
└─ finally
└─ unlock
acquireWave(warehouseCode, ticketType)
├─ waveAcquireLockKey(ticketType)
└─ shopee_wave
├─ ticketType 匹配
sourceType=INTERFACE
├─ waveDate >= now - 10 hours
status=UNUSED
└─ order by rand() limit 1
```
---
```text
createInternalShopeeWave(shopeeWaveCode, ticketType)
├─ code + ticketType 已存在 → success
└─ 不存在 → 创建 INTERNAL / USING 记录
```
## 5. 方法映射表
```text
updateLocalShopeeWaveBinding(...)
├─ resolveShipmentTicketType
├─ sortingWallCellLockKey(cell.id)
├─ 更新 wcs_sorting_wall_cell
├─ 更新内部 wave
└─ 更新 shopee_wave.status
```
| 方法 | 作用 | 在流程图中的位置 |
|---|---|---|
| `assignShopeeWaveToAcceptedWorkStations` | 计划集波工作站扫描、排序与调度 | 计划集波工作站入口 |
| `matchByStation` | 单工作站统一分流入口 | 主流程总图 |
| `assignTaskTicket` | Sales/MTO Task 共用分流,补建缺失的 shopee_wave | 主流程总图 [6] |
| `assignOrder` | Sales/MTO/RTS Order 共用分流 | 主流程总图 [6] |
| `assignRt` | RT 独立分流 | 主流程总图 [6] |
| `hasUserShipmentTypePermission` | 用户出库类型权限 | 主流程总图 [1] |
| `findEnabledWorkStation` | 工作站校验 | 主流程总图 [3] |
| `hasDifferentActiveShipmentType` | 同工作站出库类型隔离 | 主流程总图 [5] |
| `findEnabledRules` | 查启用规则 | 主流程总图 [7] |
| `assignSingleShipmentWaves` | 一波一单分流 | 3.1 |
| `fillShortage` | status=200 Shopee 波次补缺口分流 | 3.2 |
| `assignEmptyCells` | 空格口首次分配 | 3.3 |
| `assignCellNumbers` | 规则驱动的格口分配 | 3.3 |
| `assignToCell` | 单个格口承接一批单据 | 4.1 |
| `bindBatchShipmentWave` | 普通批量订单 Shopee 波次绑定 | 4.2 |
| `createInternalWave` | 创建内部波次 | 4.3 |
| `addShipmentsToInternalWave` | 订单入波次并运行 | 4.3 |
| `cancelInternalWave` | 运行失败回滚 | 4.4 |
## 10. 失败处理
---
```text
格口占用失败 → 返回错误
createWave 失败 → releaseSingleCell
Shopee 同步失败 → releaseSingleCell + cancelWave
Shopee 踢单 → markKickOut
本地绑定失败 → releaseSingleCell + cancelWave
addToWave 失败 → releaseSingleCell + cancelWave
```
## 6. 外部接口关系
## 11. 方法映射
| 接口编号 | `ShopeeOutboundApiService` 方法 | 方向 | Shopee 外部接口 |
|---|---|---|---|
| 3.1.5 | `requestSalesStartPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales start_picking_task |
| 3.1.6 | `requestSalesBindPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales bind_picking_task |
| 3.1.7 | `requestSalesSyncPickingDetail` | 当前项目/WES-WMS -> Shopee 外部接口 | sales sync_picking_detail |
| 3.1.8 | `requestSalesChangePickingDevice` | 当前项目/WES-WMS -> Shopee 外部接口 | sales change_picking_device |
| 3.1.9 | `requestSalesCompletePickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales complete_picking_task |
| 3.1.10 | `requestSalesSearchPickingProcessGuide` | 当前项目/WES-WMS -> Shopee 外部接口 | sales search_picking_process_guide |
| 3.2.5 | `requestSalesCacheTaskNumber` | 当前项目/WES-WMS -> Shopee 外部接口 | sales cache_task_number |
| 3.2.7 | `requestSalesCreatePickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales create_picking_task |
| 3.2.8 | `requestSalesAppendOrder` | 当前项目/WES-WMS -> Shopee 外部接口 | sales append_order |
| 3.2.9 | `requestSalesChangeDevice` | 当前项目/WES-WMS -> Shopee 外部接口 | sales change_device |
| 3.2.10 | `requestSalesVendorCompleteTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales vendor_complete_task |
| 3.3.7 | `requestRtsmtoBindPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | rtsmto bind_picking_task |
| 3.3.9 | `requestRtsmtoSyncPickingDetail` | 当前项目/WES-WMS -> Shopee 外部接口 | rtsmto sync_picking_detail |
| 3.3.10 | `requestRtsmtoCompleteTask` | 当前项目/WES-WMS -> Shopee 外部接口 | rtsmto complete_task |
| 3.3.11 | `requestRtsmtoChangePickingDevice` | 当前项目/WES-WMS -> Shopee 外部接口 | rtsmto change_picking_device |
| 3.4.5 | `requestMtoStartPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | mto start_picking_task |
| 3.4.6 | `requestMtoBindPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | mto bind_picking_task |
| 3.4.7 | `requestMtoSyncPickingDetail` | 当前项目/WES-WMS -> Shopee 外部接口 | mto sync_picking_detail |
| 3.4.8 | `requestMtoCompletePickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | mto complete_picking_task |
| `waveSvc.createInternalWave` | - | 当前项目内部 | 创建内部波次 |
| `waveSvc.run` | - | 当前项目内部 | 触发内部波次运行,异步提交 |
---
## 7. 当前实现摘要
从代码看,`WaveRuleMatchService` 的职责可以概括成:
1. 校验当前工作站和出库类型是否允许进入集波
2. 按规则优先级找单
3. 把命中的单据分到合适的格口
4. 需要时申请 Shopee 波次号并调用 Shopee 外部接口
5. 创建内部波次并触发运行
6. 失败时做格口释放、踢单标记或波次取消
如果只记一句话:
> 这个服务是“集波入口 + 格口分配 + Shopee 绑定 + 内部波次运行”的串联中枢,不是纯规则查询服务。
| 方法 | 当前职责 |
|---|---|
| `assignShopeeWaveToAcceptedWorkStations` | 工作站扫描与调度 |
| `matchByStation` | 单工作站统一入口 |
| `assignSingles` | 快速拣选 |
| `assignTaskTicket` | SALES_TASK / MTO_TASK |
| `assignOrder` | SALES_ORDER / RTS / MTO_ORDER |
| `assignRt` | RT 专用分流 |
| `fillShortage` | USING 波次格口补单 |
| `assignToCell` | 普通订单单格口完整分配 |
| `assignAcquiredWaveToCell` | 使用已取得号段进入单格口分配 |
| `createWave` | 创建内部 wave |
| `addToWave` | 加单并运行内部 wave |
| `updateLocalShopeeWaveBinding` | 回写格口、内部 wave、shopee_wave |
| `bindBatchShipmentWave` | 普通订单波次校验与本地绑定 |
| `acquireWave` | 从接口号段池选择 UNUSED 号段 |
| `createInternalShopeeWave` | 补建单据已有波次号记录 |
| `backfillRtShopeeWaveCode` | RT 本地波次记录维护 |
| `releaseSingleCell` | 释放无未完成任务的格口 |
| `cancelWave` | 取消失败内部 wave |
File diff suppressed because it is too large Load Diff