[16928] Shopee AGV 与集波项目文档更新
This commit is contained in:
+31
-10
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## 1. 目标
|
## 1. 目标
|
||||||
|
|
||||||
实现 Shopee AGV 出库分拣:工作站登录 AGV 分拣模式后,系统把已释放的 AGV 区域拣货任务分配到分拣墙,按工作站 AGV 并发量下发周转箱搬运任务;周转箱到站后,操作员按批量或按单首选项扫描分拣,通过电子标签或手工确认完成格口任务,支持绑箱、满箱、封箱、缺料重分配和回库调度。
|
实现 Shopee AGV 出库分拣:工作站登录 AGV 分拣模式后,系统把已释放的 AGV 区域拣货任务分配到分拣墙,按工作站 AGV 并发量下发周转箱搬运任务;周转箱到站后,操作员按批量或按单首选项扫描分拣,通过电子标签或手工确认完成格口任务,支持绑箱、满箱、缺料重分配、三段式完成回传和回库调度。
|
||||||
|
|
||||||
## 2. 总体模块拆分
|
## 2. 总体模块拆分
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| P0 | 数据模型与配置 | `agv_preference`、`wcs_sorting_wall` AGV 分拣字段、工作站 AGV 并发量、菜单/字典配置 | 部分完成:`agv_preference` 建表/domain/table JSON/bill JSON 已落地 |
|
| P0 | 数据模型与配置 | `agv_preference`、`wcs_sorting_wall` AGV 分拣字段、工作站 AGV 并发量、菜单/字典配置 | 部分完成:`agv_preference` 建表/domain/table JSON/bill JSON 已落地 |
|
||||||
| P1 | 工作站 AGV 分拣入口 | 登录工作站、开始/退出/暂停/继续、出库单类型选择 | 部分完成:已存在 `AgvSortingService` 统一服务骨架 |
|
| P1 | 工作站 AGV 分拣入口 | 登录工作站、开始/退出/暂停/继续、出库单类型选择 | 部分完成:已存在 `AgvSortingService` 统一服务骨架 |
|
||||||
| P1 | 分拣墙箱绑定 | 绑箱、满箱、封箱、缺料按钮 | 待实现 |
|
| P1 | 分拣墙箱绑定 | 绑箱、满箱、缺料按钮;删除独立封箱按钮 | 待实现 |
|
||||||
| P1 | 任务分配计划任务 | 拣货任务分配到分拣墙,绑定上游波次号和分拣墙 | 待实现 |
|
| P1 | 任务分配计划任务 | 拣货任务分配到分拣墙,绑定上游波次号和分拣墙 | 待实现 |
|
||||||
| P1 | AGV 下发计划任务 | 控制工作站未完成 AGV 数量,生成/下发 `wcs_dispatch_task` | 待实现 |
|
| P1 | AGV 下发计划任务 | 控制工作站未完成 AGV 数量,生成/下发 `wcs_dispatch_task` | 待实现 |
|
||||||
| P1 | 扫箱分拣服务 | 扫周转箱、按首选项展示批量/按单分拣信息 | 待实现 |
|
| P1 | 扫箱分拣服务 | 扫周转箱、按首选项展示批量/按单分拣信息 | 待实现 |
|
||||||
@@ -110,6 +110,23 @@ docx 多次提到任务头自定义字段:
|
|||||||
|
|
||||||
建议先确认任务头“自定义1-4”在当前数据库实际列名,再决定字段落点。
|
建议先确认任务头“自定义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. 后端服务计划
|
||||||
|
|
||||||
### 4.1 AGV 分拣工作站服务(部分完成)
|
### 4.1 AGV 分拣工作站服务(部分完成)
|
||||||
@@ -137,10 +154,11 @@ docx 多次提到任务头自定义字段:
|
|||||||
|
|
||||||
接口:
|
接口:
|
||||||
- `bindBox(session, warehouseCode, sortingWallCode, lpn)`:校验分拣墙存在、`lpn` 为空、调用 WMS 周转箱检查接口、回写 `wcs_sorting_wall.lpn`。
|
- `bindBox(session, warehouseCode, sortingWallCode, lpn)`:校验分拣墙存在、`lpn` 为空、调用 WMS 周转箱检查接口、回写 `wcs_sorting_wall.lpn`。
|
||||||
- `replaceFullBox(session, warehouseCode, sortingWallCode, oldLpn, newLpn)`:满箱换箱;docx 此处描述仍写“检查可用并更新 LPN”,需确认是否要先封箱旧箱。
|
- `replaceFullBox(session, warehouseCode, sortingWallCode, oldLpn, newLpn)`:满箱换箱并更新 LPN,不调用独立封箱接口。
|
||||||
- `sealBox(session, warehouseCode, sortingWallCode)`:要求 `syStatus=6`,清空 `taskId/lpn`,`syStatus=0`,下发灭灯。
|
|
||||||
- `shortage(session, warehouseCode, sortingWallCode)`:二次确认后触发缺料重分配、缺料盘点单、继续显示下一货品或回库。
|
- `shortage(session, warehouseCode, sortingWallCode)`:二次确认后触发缺料重分配、缺料盘点单、继续显示下一货品或回库。
|
||||||
|
|
||||||
|
删除 `sealBox/sealDevice` 独立封箱入口。完成拣货改为参考接口 3.1.9 的 `confirm -> get -> uploadStatus` 三段式回传;仅在 `uploadStatus` 成功后释放格口/设备并下发灭灯,释放结果写入 `shopee_wave.deviceReleaseSts`,失败时保留原绑定等待重试。
|
||||||
|
|
||||||
依赖待确认:
|
依赖待确认:
|
||||||
- 上游 WMS 周转箱检查接口。
|
- 上游 WMS 周转箱检查接口。
|
||||||
- 电子标签灭灯接口。
|
- 电子标签灭灯接口。
|
||||||
@@ -306,8 +324,8 @@ docx 多次提到任务头自定义字段:
|
|||||||
- 暂停/继续:切换 `acceptStatus`。
|
- 暂停/继续:切换 `acceptStatus`。
|
||||||
- 绑箱:扫货架号 + 周转箱号。
|
- 绑箱:扫货架号 + 周转箱号。
|
||||||
- 满箱:扫货架号 + 周转箱号。
|
- 满箱:扫货架号 + 周转箱号。
|
||||||
- 封箱:扫货架号。
|
|
||||||
- 缺料:二次确认后触发缺料流程。
|
- 缺料:二次确认后触发缺料流程。
|
||||||
|
- 不显示封箱按钮,不提供封箱弹框或扫描货架号操作。
|
||||||
|
|
||||||
分拣区:
|
分拣区:
|
||||||
- 左侧:当前周转箱格口示意,支持 1/2/4/6/8 格,当前格口放大蓝色显示。
|
- 左侧:当前周转箱格口示意,支持 1/2/4/6/8 格,当前格口放大蓝色显示。
|
||||||
@@ -325,7 +343,7 @@ docx 多次提到任务头自定义字段:
|
|||||||
待实现:
|
待实现:
|
||||||
- 页面真实数据绑定。
|
- 页面真实数据绑定。
|
||||||
- AGV 首选项弹窗/选择逻辑。
|
- AGV 首选项弹窗/选择逻辑。
|
||||||
- 开始/退出/暂停/绑箱/满箱/封箱/任务确认按钮接口联动。
|
- 开始/退出/暂停/绑箱/满箱/任务确认按钮接口联动;删除封箱按钮逻辑。
|
||||||
|
|
||||||
## 6. 接口清单
|
## 6. 接口清单
|
||||||
|
|
||||||
@@ -334,6 +352,9 @@ docx 多次提到任务头自定义字段:
|
|||||||
| 周转箱检查 | WES -> 上游 WMS | 待接口 |
|
| 周转箱检查 | WES -> 上游 WMS | 待接口 |
|
||||||
| AGV 任务创建 | WES -> WCS/AGV | 可复用现有 `AgvTaskCreateWcsCmd`,需补出库分拣参数 |
|
| AGV 任务创建 | WES -> WCS/AGV | 可复用现有 `AgvTaskCreateWcsCmd`,需补出库分拣参数 |
|
||||||
| AGV 任务完成回调 | WCS/AGV -> WES | 现有回调需确认是否覆盖 |
|
| 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`,回库任务需确认 |
|
| AGV 任务取消/回库 | WES -> WCS/AGV | 可复用 `AgvTaskCancelWcsCmd`,回库任务需确认 |
|
||||||
| 电子标签亮红灯 | WES -> WCS | 待接口 |
|
| 电子标签亮红灯 | WES -> WCS | 待接口 |
|
||||||
| 电子标签灭灯 | WES -> WCS | 待接口 |
|
| 电子标签灭灯 | WES -> WCS | 待接口 |
|
||||||
@@ -372,11 +393,11 @@ docx 多次提到任务头自定义字段:
|
|||||||
- 已完成:`AgvPreferenceService` 通用 CRUD 服务。
|
- 已完成:`AgvPreferenceService` 通用 CRUD 服务。
|
||||||
- 待实现:用户权限过滤和默认首选项选择业务逻辑。
|
- 待实现:用户权限过滤和默认首选项选择业务逻辑。
|
||||||
|
|
||||||
### Task 4:分拣墙绑箱/满箱/封箱/缺料
|
### Task 4:分拣墙绑箱/满箱/缺料
|
||||||
|
|
||||||
- 新增 `AgvSortingWallBoxService`。
|
- 新增 `AgvSortingWallBoxService`。
|
||||||
- 实现货架校验、LPN 校验、周转箱检查接口占位。
|
- 实现货架校验、LPN 校验、周转箱检查接口占位。
|
||||||
- 实现封箱清空和灭灯接口占位。
|
- 删除封箱按钮及独立封箱接口,格口清理和灭灯由三段式完成回传成功状态触发。
|
||||||
- 实现缺料入口:先记录待重分配和待盘点,等接口确认后补全。
|
- 实现缺料入口:先记录待重分配和待盘点,等接口确认后补全。
|
||||||
|
|
||||||
### Task 5:波次任务分配到分拣墙
|
### Task 5:波次任务分配到分拣墙
|
||||||
@@ -436,7 +457,7 @@ docx 多次提到任务头自定义字段:
|
|||||||
- AGV 分拣首选项页面。
|
- AGV 分拣首选项页面。
|
||||||
- 已完成:AGV 分拣工作台静态页面和自适应样式。
|
- 已完成:AGV 分拣工作台静态页面和自适应样式。
|
||||||
- 工作站登录/开始/暂停/退出操作。
|
- 工作站登录/开始/暂停/退出操作。
|
||||||
- 绑箱/满箱/封箱/缺料弹框。
|
- 绑箱/满箱/缺料弹框;不提供封箱按钮和弹框。
|
||||||
- 分拣动态图和电子标签/手工确认交互。
|
- 分拣动态图和电子标签/手工确认交互。
|
||||||
- 按单模式增加交付阶段按钮和 PTL 分播口状态展示。
|
- 按单模式增加交付阶段按钮和 PTL 分播口状态展示。
|
||||||
|
|
||||||
@@ -465,7 +486,7 @@ docx 多次提到任务头自定义字段:
|
|||||||
| Task 1 | 数据模型与配置落地 | 2 - 3 | 1 | 0 - 1 | 3 - 5 | 包含迁移、domain/service、表单配置和常量 |
|
| Task 1 | 数据模型与配置落地 | 2 - 3 | 1 | 0 - 1 | 3 - 5 | 包含迁移、domain/service、表单配置和常量 |
|
||||||
| Task 2 | 工作站 AGV 分拣入口 | 1 - 2 | 1 | 0 - 1 | 2 - 4 | currentJobMode、acceptStatus、系统日志 |
|
| 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 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 5 | 波次任务分配到分拣墙 | 3 - 4 | 0 | 1 | 4 - 5 | 包含 Redis 锁、任务头/分拣墙回写 |
|
||||||
| Task 6 | AGV 任务下发 | 2 - 3 | 0 | 1 - 2 | 3 - 5 | 复用现有 dispatch task,需确认 AGV 参数 |
|
| 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 扫描 |
|
| Task 7 | 扫箱与分拣展示 | 3 - 5 | 2 - 3 | 1 - 2 | 6 - 10 | 批量/按单查询差异、SKU/UID 扫描 |
|
||||||
|
|||||||
Binary file not shown.
@@ -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 转换和数据库核验结果。
|
||||||
@@ -65,6 +65,18 @@ operator_skills
|
|||||||
| inventoryThreshold | 库存阈值-出库分拣使用 | int | | Y | 0 |
|
| inventoryThreshold | 库存阈值-出库分拣使用 | int | | Y | 0 |
|
||||||
| 其他base预留字段等 | | | | | |
|
| 其他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
|
## 实现状态 / TODO
|
||||||
|
|
||||||
### 第一阶段后端闭环已落地
|
### 第一阶段后端闭环已落地
|
||||||
@@ -75,9 +87,9 @@ operator_skills
|
|||||||
- `exit`
|
- `exit`
|
||||||
- `assignShopeeWaveToAcceptedWorkStations`
|
- `assignShopeeWaveToAcceptedWorkStations`
|
||||||
- 已复用现有 `WcsWorkStationService.updateAcceptStatus` 做暂停/继续接单。
|
- 已复用现有 `WcsWorkStationService.updateAcceptStatus` 做暂停/继续接单。
|
||||||
- 已复用现有 `WaveRuleMatchService.findBestMatchedRuleByWorkStationAndOutboundType` 做分拣墙空闲格口的 Shopee 集波分配。
|
- 自动集波通过 `WaveRuleMatchService.matchByStation` 完成分拣墙空闲格口的 Shopee 集波分配。
|
||||||
- 已在绑箱/满箱后端校验格口必须已有 `shopeeWaveCode`,避免未分配波次的格口绑定箱号。
|
- 已在绑箱/满箱后端校验格口必须已有 `shopeeWaveCode`,避免未分配波次的格口绑定箱号。
|
||||||
- 已在封箱后清理现有真实字段:`containerCode`、`shopeeWaveCode`、`currentWaveRule`、`useStatus`。
|
- 工作站登录及 AGV 分拣页面不提供独立封箱按钮;完成拣货统一进入参考接口 3.1.9 的三段式回传流程。
|
||||||
|
|
||||||
### 第一阶段的最小承载策略
|
### 第一阶段的最小承载策略
|
||||||
|
|
||||||
@@ -94,7 +106,7 @@ operator_skills
|
|||||||
- 换箱场景仅补了基础后端限制:
|
- 换箱场景仅补了基础后端限制:
|
||||||
- `ticketType=5` 的 MTO 任务拒绝换箱
|
- `ticketType=5` 的 MTO 任务拒绝换箱
|
||||||
- Flow Pick 换波次逻辑待确认是否复用现有集波服务,否则需后续补充
|
- Flow Pick 换波次逻辑待确认是否复用现有集波服务,否则需后续补充
|
||||||
- 封箱后的电子标签灭灯仍保留 TODO,待确认现场 PTL 服务接口后接入。
|
- 三段式回传成功后的格口释放和电子标签灭灯仍保留 TODO,待确认现场 PTL 服务接口后接入。
|
||||||
- 任务确认阶段的 `targetContainer` 承载字段本轮未新增。
|
- 任务确认阶段的 `targetContainer` 承载字段本轮未新增。
|
||||||
- 如后续确认现有任务明细已有合适字段,可优先复用;否则再补迁移。
|
- 如后续确认现有任务明细已有合适字段,可优先复用;否则再补迁移。
|
||||||
|
|
||||||
@@ -178,15 +190,15 @@ A点击开始按钮,弹框选择出库单类型。弹框显示的内容为出
|
|||||||
|
|
||||||
- 补充:出库单ticketType=5(即MTO下发任务类型)的不允许换箱,如果点换箱时出库单头ticketType=5则提示:MTO非需求池类型不允许换箱
|
- 补充:出库单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进行再跑对应波次分配库存,如果分配成功则按集波逻辑进行更新波次号、槽口号等,前端提示分配成功,继续进行下面的作业,如果分配失败则前端提示无可用库存已确认短拣)。
|
点击按钮,弹框进行二次确认:是否确定短拣并进行重新分配,点击确定更新出库单明细短拣数量shortPickQty,则拿当前周转箱货品+批次的订单需要重新分配(根据短缺数量shortPickQty进行再跑对应波次分配库存,如果分配成功则按集波逻辑进行更新波次号、槽口号等,前端提示分配成功,继续进行下面的作业,如果分配失败则前端提示无可用库存已确认短拣)。
|
||||||
|
|
||||||
@@ -266,4 +278,4 @@ H、订单分拣完成
|
|||||||
|
|
||||||
任务确认的时候,判断wcs_sorting_wall_cell.shopeeWave分拣墙格口的任务是否完成,若整个任务分拣完成,则下发当前格口电子标签绿灯,useStatus更新‘PENDING_OUT’待离。
|
任务确认的时候,判断wcs_sorting_wall_cell.shopeeWave分拣墙格口的任务是否完成,若整个任务分拣完成,则下发当前格口电子标签绿灯,useStatus更新‘PENDING_OUT’待离。
|
||||||
|
|
||||||
UI分拣界面需要监测到电子标签拍灯接口,若报文返回的分拣墙货位wcs_sorting_wall_cell表的useStatus=‘PENDING_OUT’待离,则为封箱,将当前格口shopeeWave,containerCode清空,useStatus更新为‘IDLE’空闲
|
UI 不再通过封箱按钮释放格口。任务完成后等待 3.1.9 三段式回传结果;`uploadStatus` 成功后处理 `deviceReleaseSts`,释放成功时清空当前格口 `shopeeWaveCode`、`containerCode`,将 `useStatus` 更新为 `IDLE` 并写 `deviceReleaseSts=1`;释放失败时保留现场绑定并写 `deviceReleaseSts=-1`,供释放任务重试。
|
||||||
|
|||||||
Binary file not shown.
@@ -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`
|
> 对应当前 `WaveRuleMatchService.groovy`,不包含已删除的历史方法或未落地占位流程。
|
||||||
>
|
|
||||||
> 说明:本文是 AI 可直接读取的纯文本流程图,不使用图片。
|
|
||||||
> 口径说明:`WES-WMS` 指当前项目实现侧,`Shopee` 指需要调用或接收交互的外部接口方。
|
|
||||||
> 目标:把当前代码的入口、分流、外部接口调用、回滚和状态回写,整理成可检索的文本流程图。
|
|
||||||
|
|
||||||
---
|
## 1. 总入口
|
||||||
|
|
||||||
## 1. 读取方式
|
|
||||||
|
|
||||||
建议按下面顺序理解本文:
|
|
||||||
|
|
||||||
1. 先看 `2. 主流程总图`
|
|
||||||
2. 再看 `3. 分支流程`
|
|
||||||
3. 最后看 `4. 方法映射表`
|
|
||||||
|
|
||||||
如果只想快速定位代码路径,直接看 `4. 方法映射表`。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. 主流程总图
|
|
||||||
|
|
||||||
### 2.1 计划集波工作站入口
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
assignShopeeWaveToAcceptedWorkStations(session, warehouseCode[, workStationCode])
|
assignShopeeWaveToAcceptedWorkStations(session, warehouseCode, workStationCode)
|
||||||
├─ 查询候选工作站
|
│
|
||||||
│ ├─ currentJobMode 为拣选模式
|
├─ 查询启用且可接单的拣选工作站
|
||||||
│ ├─ acceptStatus = OPEN
|
├─ 按空格口数降序、工作站 ID 升序
|
||||||
│ └─ status = ENABLE
|
└─ for each workstation
|
||||||
├─ 统计各工作站的可用空格口
|
└─ matchByStation(session, workstation)
|
||||||
│ ├─ 分播墙 status = ENABLE
|
├─ 校验 status / warehouseCode / acceptStatus
|
||||||
│ ├─ 格口 status = ENABLE
|
├─ 获取 ruleLockKey(warehouseCode)
|
||||||
│ └─ 格口 shopeeWaveCode 为空
|
├─ ORDER_PICK
|
||||||
├─ 多工作站排序
|
│ └─ assignSingles
|
||||||
│ ├─ 可用空格口数量降序
|
└─ BATCH_PICK
|
||||||
│ └─ 数量相同时工作站 ID 升序
|
├─ 操作员技能与工作站 waveRule 取交集
|
||||||
└─ 按排序结果逐个调用统一集波匹配流程
|
├─ fillShortage
|
||||||
├─ 无启用分播墙 → 跳过当前工作站
|
└─ 无补单结果时按 ticketType 分流
|
||||||
└─ 单个工作站失败 → 记录日志并继续后续工作站
|
├─ 1/5 → assignTaskTicket
|
||||||
|
├─ 2/3/4 → assignOrder
|
||||||
|
└─ 6 → assignRt
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.2 单工作站集波入口
|
## 2. 快速拣选
|
||||||
|
|
||||||
```text
|
```text
|
||||||
自动集波工作站扫描
|
assignSingles(warehouseCode, workstation)
|
||||||
└─ matchByStation(session, workstation)
|
├─ findSingleEmptyCells
|
||||||
├─ [1] 从 workstation 读取 id、warehouseCode、code、shipmentType
|
├─ findSingleShipments
|
||||||
│
|
└─ for each shipment
|
||||||
├─ [2] 工作站状态校验
|
├─ findShipmentExt
|
||||||
│ ├─ status != ENABLE → MSG_WRM_0005
|
├─ 波次号来源
|
||||||
│ └─ warehouseCode 为空 → MSG_WRM_0006
|
│ ├─ 已有 shopeeWaveCode → 复用
|
||||||
│
|
│ ├─ RT → shipmentCode
|
||||||
├─ [3] 接单状态校验
|
│ └─ 其他 → acquireWave
|
||||||
│ ├─ acceptStatus != OPEN → MSG_WRM_0015
|
├─ RT → backfillRtShopeeWaveCode
|
||||||
│ └─ 通过 → 继续
|
├─ 已有外部波次号 → createInternalShopeeWave
|
||||||
│
|
├─ occupySingleCell
|
||||||
├─ [4] 仓库维度 Redis 锁
|
├─ createWave
|
||||||
│ ├─ ruleLockKey(warehouseCode)
|
├─ syncShopeePickingTaskAndFilterAcceptedShipments(ticketType)
|
||||||
│ ├─ tryLock 失败 → MSG_WRM_0018
|
│ └─ RT 跳过外部调用
|
||||||
│ └─ 获取成功 → 继续
|
├─ updateLocalShopeeWaveBinding(ruleCode='')
|
||||||
│
|
└─ addToWave
|
||||||
├─ [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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## 3. 任务单
|
||||||
|
|
||||||
## 3. 分支流程
|
|
||||||
|
|
||||||
### 3.1 一波一单分流
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
assignSingleShipmentWaves(warehouseCode, shipmentType, workstationId, rules)
|
assignTaskTicket(warehouseCode, workstation, rule, ticketType)
|
||||||
├─ for each rule
|
│ ticketType = SALES_TASK / MTO_TASK
|
||||||
│ ├─ findCells(session, workstationId)
|
│
|
||||||
│ ├─ matchEmptyCells(cells)
|
├─ findSingleEmptyCells(workstation.id, rule)
|
||||||
│ ├─ findSingleShipmentWaveShipments(session, warehouseCode, shipmentType, rule, emptyCells.size())
|
├─ findTaskTicketShipments
|
||||||
│ ├─ if 未命中 → 继续下一个 rule
|
└─ for each shipment
|
||||||
│ └─ if 命中
|
├─ 读取 shipment_header_ext1.shopeeWaveCode
|
||||||
│ ├─ 取当前空闲格口的首个 cell
|
├─ createInternalShopeeWave
|
||||||
│ ├─ occupySingleShipmentSortingWallCell
|
├─ occupySingleCell
|
||||||
│ ├─ 若不是 RT 且 shipment_header_ext1.shopeeWave 为空
|
├─ createWave
|
||||||
│ │ ├─ acquireUnusedShopeeWave
|
├─ syncShopeePickingTaskAndFilterAcceptedShipments(ticketType)
|
||||||
│ │ ├─ RT 或已有 shopeeWaveCode 时创建 shopee_wave
|
├─ updateLocalShopeeWaveBinding
|
||||||
│ │ └─ 传 ticketType 同步 picking task(仅 RT 跳过外部调用)后直接本地回写
|
└─ addToWave
|
||||||
│ ├─ createInternalWave
|
|
||||||
│ ├─ addShipmentsToInternalWave
|
|
||||||
│ └─ 记录成功日志
|
|
||||||
└─ 返回 success 或 error
|
|
||||||
```
|
```
|
||||||
|
|
||||||
关键点:
|
## 4. 普通订单
|
||||||
|
|
||||||
- `RT` 不申请 Shopee 外部波次号,使用 `shipmentCode` 作为本地 `shopeeWaveCode`。
|
|
||||||
- `backfillRtShopeeWaveCode` 仅由 RT 分支调用,方法内部不再重复判断 `ticketType`。
|
|
||||||
- 非 `RT` 的一波一单,若本地没有 `shopeeWave`,会先取号再绑定。
|
|
||||||
- 一波一单是“1 单 -> 1 格口 -> 1 内部波次”的结构。
|
|
||||||
|
|
||||||
### 3.2 补缺口分流
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
fillShortage(warehouseCode, workstation) → shipmentType 直接读取 workstation.shipmentType
|
assignOrder(warehouseCode, workstation, rule, ticketType)
|
||||||
├─ findShortage(workstationId) → 仅选择 shopee_wave.status=200 的关联格口
|
│ ticketType = SALES_ORDER / RTS / MTO_ORDER
|
||||||
├─ for each cell
|
│
|
||||||
│ ├─ 如果 cell 未绑定 shopeeWaveCode → 跳过
|
├─ findUnusedCells(workstation.id, rule)
|
||||||
│ ├─ calcNeedQty
|
└─ 首个可用格口
|
||||||
│ ├─ needQty <= 0 → 跳过
|
├─ findAvailableShipments
|
||||||
│ ├─ needQty > 0 → 加入需补货格口明细
|
├─ hasEmptyCellMinShipments
|
||||||
│ ├─ resolveBoundShipmentGroup
|
├─ acquireWave
|
||||||
│ ├─ findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
|
└─ assignAcquiredWaveToCell
|
||||||
│ ├─ assignToCell
|
└─ assignToCell
|
||||||
│ └─ 记录补单成功或失败日志
|
|
||||||
├─ MSG_WRM_0244 → 记录需补货格口数量及 cellCode|shopeeWaveCode|currentWaveRule|ticketType|needQty
|
|
||||||
└─ 返回
|
|
||||||
```
|
```
|
||||||
|
|
||||||
关键点:
|
## 5. RT
|
||||||
|
|
||||||
- 候选资格只判断当前工作站格口关联的 `shopee_wave.status=200`。
|
|
||||||
- 不按分播墙状态、格口状态、ticketType、任务完成时间或 `waitingTime` 过滤候选。
|
|
||||||
- 补单执行时从已绑定订单解析 `groupKey`,保持同一 Shopee 波次订单特征一致。
|
|
||||||
- 补缺口不重新取新 Shopee 波次号。
|
|
||||||
|
|
||||||
### 3.3 空格口首次分配
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
assignEmptyCells(warehouseCode, shipmentType, workstationId, rules)
|
assignRt(warehouseCode, workstation, rule)
|
||||||
├─ for each rule
|
├─ findSingleEmptyCells(workstation.id, rule)
|
||||||
│ ├─ findCells(session, workstationId)
|
├─ findRtTicketShipments
|
||||||
│ ├─ assignCellNumbers(warehouseCode, shipmentType, workstationId, rule, cells)
|
└─ for each shipment
|
||||||
│ ├─ 若本轮分配成功 → break
|
├─ shipmentCode 作为本地 Shopee 波次号
|
||||||
│ └─ 否则继续下一个 rule
|
├─ backfillRtShopeeWaveCode
|
||||||
└─ 返回 success
|
├─ occupySingleCell
|
||||||
|
├─ createWave
|
||||||
|
├─ updateLocalShopeeWaveBinding
|
||||||
|
└─ addToWave
|
||||||
```
|
```
|
||||||
|
|
||||||
`assignCellNumbers` 的核心逻辑:
|
RT 不申请接口号段,不调用 Shopee picking task 外部接口。
|
||||||
|
|
||||||
|
## 6. 缺口补单
|
||||||
|
|
||||||
```text
|
```text
|
||||||
assignCellNumbers(...)
|
fillShortage(warehouseCode, workstation)
|
||||||
├─ for each cell
|
├─ findShortage(workstation.id)
|
||||||
│ ├─ 读取 cell.shopeeWaveCode
|
│ └─ 只选择关联 shopee_wave.status=200 的格口
|
||||||
│ ├─ 如果 cell 没有 shopeeWaveCode
|
└─ for each cell
|
||||||
│ │ ├─ findAvailableShipments(session, warehouseCode, rule, ..., workstation)
|
├─ 按 currentWaveRule 读取启用规则
|
||||||
│ │ ├─ acquireUnusedShopeeWave
|
├─ calcNeedQty
|
||||||
│ │ └─ 得到新 shopeeWaveCode
|
├─ 记录需补单格口处理日志
|
||||||
│ ├─ 如果 cell 已有 shopeeWaveCode
|
├─ resolveBoundShipmentGroup
|
||||||
│ │ ├─ calcNeedQty
|
├─ findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
|
||||||
│ │ ├─ needQty <= 0 → 跳过
|
└─ assignToCell
|
||||||
│ │ ├─ resolveBoundShipmentGroup
|
|
||||||
│ │ └─ findAvailableShipments(..., shopeeWaveCode, groupKey)
|
|
||||||
│ └─ assignToCell
|
|
||||||
└─ 返回 success / error
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## 7. 单格口分配
|
||||||
|
|
||||||
## 4. 关键子流程
|
|
||||||
|
|
||||||
### 4.1 assignToCell
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
assignToCell(warehouseCode, rule, cell, shipmentIds, shopeeWaveCode, ticketType, workstation)
|
assignToCell(...)
|
||||||
├─ pickCellShipmentIds
|
├─ 获取 cellLockKey(cell.id)
|
||||||
├─ ensureCellReadyForWave
|
├─ pickCellShipments
|
||||||
│ ├─ 已有同 wave 同 rule 的 USED 格口 → 直接复用
|
├─ occupyCell
|
||||||
│ └─ 否则走 occupySortingWallCell
|
├─ createWave
|
||||||
|
├─ Shopee 同步
|
||||||
|
│ ├─ 新格口 → syncShopeePickingTaskAndFilterAcceptedShipments
|
||||||
|
│ └─ 同波次补单 → syncShopeeAppendOrderAndFilterAcceptedShipments
|
||||||
├─ bindBatchShipmentWave
|
├─ bindBatchShipmentWave
|
||||||
|
│ ├─ waveBindLockKey
|
||||||
│ ├─ isWaveAvailable
|
│ ├─ isWaveAvailable
|
||||||
│ └─ updateLocalShopeeWaveBinding
|
│ └─ updateLocalShopeeWaveBinding
|
||||||
├─ createInternalWave
|
├─ addToWave
|
||||||
├─ addShipmentsToInternalWave
|
├─ 达到 maxShipments → status=FULL
|
||||||
│ ├─ shipmentHeaderService.addMultipleToWave
|
└─ finally unlock
|
||||||
│ └─ waveSvc.run
|
|
||||||
└─ 清空当前候选池,返回 success
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.2 分类型 Shopee 波次绑定
|
## 8. Shopee 同步
|
||||||
|
|
||||||
```text
|
```text
|
||||||
updateLocalShopeeWaveBinding(...) [快速拣选直接调用]
|
syncShopeePickingTaskAndFilterAcceptedShipments(..., ticketType)
|
||||||
|
├─ RT → 返回 acceptedIds,不调用外部接口
|
||||||
updateLocalShopeeWaveBinding(...) [Sales/MTO 任务单直接调用]
|
└─ 其他类型
|
||||||
|
├─ sendShopeePickingTaskRequest
|
||||||
bindBatchShipmentWave(...) [普通批量订单]
|
├─ updateShopeeWaveStartPickUploadStatus
|
||||||
├─ 校验参数和调用方传入的 ticketType
|
├─ resolveKickedShipmentIds
|
||||||
├─ waveBindLockKey
|
├─ markKickOut
|
||||||
├─ 获取 Shopee 波次绑定锁
|
└─ writePickingTaskSyncWaveCode
|
||||||
├─ isWaveAvailable
|
|
||||||
│ └─ 方法内直接查询并确认 shopee_wave 状态可用
|
|
||||||
├─ 若成功
|
|
||||||
│ └─ updateLocalShopeeWaveBinding
|
|
||||||
│ └─ 统一回写格口、内部 wave 和 shopee_wave
|
|
||||||
└─ finally 解锁
|
|
||||||
```
|
```
|
||||||
|
|
||||||
重要说明:
|
|
||||||
|
|
||||||
- 快速拣选和 Sales/MTO 任务单直接调用本地回写方法;普通批量订单使用独立绑定方法。
|
|
||||||
- 普通批量订单由调用方传入当前分支已确定的 `ticketType`。
|
|
||||||
- 真实的 3.1.6/3.4.6 绑定接口在工作站绑箱时同步调用。
|
|
||||||
|
|
||||||
### 4.3 createInternalWave / addShipmentsToInternalWave
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
createInternalWave(session, warehouseCode, rule)
|
syncShopeeAppendOrderAndFilterAcceptedShipments(...)
|
||||||
├─ resolveMasterCode
|
├─ sendShopeeAppendOrderRequest
|
||||||
├─ waveSvc.createInternalWave
|
├─ resolveKickedShipmentIds
|
||||||
└─ 返回 waveId
|
├─ markKickOut
|
||||||
|
└─ 返回 acceptedIds
|
||||||
addShipmentsToInternalWave(shipmentIds, waveId)
|
|
||||||
├─ shipmentHeaderService.addMultipleToWave
|
|
||||||
├─ waveSvc.run
|
|
||||||
└─ run 失败时 cancelInternalWave
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.4 回滚与释放
|
## 9. 波次号与本地绑定
|
||||||
|
|
||||||
```text
|
```text
|
||||||
回滚点
|
acquireWave(warehouseCode, ticketType)
|
||||||
├─ 格口抢占失败
|
├─ waveAcquireLockKey(ticketType)
|
||||||
│ └─ 直接返回 false / error,继续尝试下一个格口或规则
|
└─ shopee_wave
|
||||||
├─ Shopee 波次绑定失败
|
├─ ticketType 匹配
|
||||||
│ └─ markKickOutWave
|
├─ sourceType=INTERFACE
|
||||||
├─ 内部波次创建失败
|
├─ waveDate >= now - 10 hours
|
||||||
│ └─ releaseSingleShipmentSortingWallCell / 返回 error
|
├─ status=UNUSED
|
||||||
├─ 内部波次运行失败
|
└─ order by rand() limit 1
|
||||||
│ └─ cancelInternalWave
|
|
||||||
└─ finally
|
|
||||||
└─ unlock
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
```text
|
||||||
|
createInternalShopeeWave(shopeeWaveCode, ticketType)
|
||||||
|
├─ code + ticketType 已存在 → success
|
||||||
|
└─ 不存在 → 创建 INTERNAL / USING 记录
|
||||||
|
```
|
||||||
|
|
||||||
## 5. 方法映射表
|
```text
|
||||||
|
updateLocalShopeeWaveBinding(...)
|
||||||
|
├─ resolveShipmentTicketType
|
||||||
|
├─ sortingWallCellLockKey(cell.id)
|
||||||
|
├─ 更新 wcs_sorting_wall_cell
|
||||||
|
├─ 更新内部 wave
|
||||||
|
└─ 更新 shopee_wave.status
|
||||||
|
```
|
||||||
|
|
||||||
| 方法 | 作用 | 在流程图中的位置 |
|
## 10. 失败处理
|
||||||
|---|---|---|
|
|
||||||
| `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 |
|
|
||||||
|
|
||||||
---
|
```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 |
|
| `assignShopeeWaveToAcceptedWorkStations` | 工作站扫描与调度 |
|
||||||
| 3.1.6 | `requestSalesBindPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales bind_picking_task |
|
| `matchByStation` | 单工作站统一入口 |
|
||||||
| 3.1.7 | `requestSalesSyncPickingDetail` | 当前项目/WES-WMS -> Shopee 外部接口 | sales sync_picking_detail |
|
| `assignSingles` | 快速拣选 |
|
||||||
| 3.1.8 | `requestSalesChangePickingDevice` | 当前项目/WES-WMS -> Shopee 外部接口 | sales change_picking_device |
|
| `assignTaskTicket` | SALES_TASK / MTO_TASK |
|
||||||
| 3.1.9 | `requestSalesCompletePickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales complete_picking_task |
|
| `assignOrder` | SALES_ORDER / RTS / MTO_ORDER |
|
||||||
| 3.1.10 | `requestSalesSearchPickingProcessGuide` | 当前项目/WES-WMS -> Shopee 外部接口 | sales search_picking_process_guide |
|
| `assignRt` | RT 专用分流 |
|
||||||
| 3.2.5 | `requestSalesCacheTaskNumber` | 当前项目/WES-WMS -> Shopee 外部接口 | sales cache_task_number |
|
| `fillShortage` | USING 波次格口补单 |
|
||||||
| 3.2.7 | `requestSalesCreatePickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales create_picking_task |
|
| `assignToCell` | 普通订单单格口完整分配 |
|
||||||
| 3.2.8 | `requestSalesAppendOrder` | 当前项目/WES-WMS -> Shopee 外部接口 | sales append_order |
|
| `assignAcquiredWaveToCell` | 使用已取得号段进入单格口分配 |
|
||||||
| 3.2.9 | `requestSalesChangeDevice` | 当前项目/WES-WMS -> Shopee 外部接口 | sales change_device |
|
| `createWave` | 创建内部 wave |
|
||||||
| 3.2.10 | `requestSalesVendorCompleteTask` | 当前项目/WES-WMS -> Shopee 外部接口 | sales vendor_complete_task |
|
| `addToWave` | 加单并运行内部 wave |
|
||||||
| 3.3.7 | `requestRtsmtoBindPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | rtsmto bind_picking_task |
|
| `updateLocalShopeeWaveBinding` | 回写格口、内部 wave、shopee_wave |
|
||||||
| 3.3.9 | `requestRtsmtoSyncPickingDetail` | 当前项目/WES-WMS -> Shopee 外部接口 | rtsmto sync_picking_detail |
|
| `bindBatchShipmentWave` | 普通订单波次校验与本地绑定 |
|
||||||
| 3.3.10 | `requestRtsmtoCompleteTask` | 当前项目/WES-WMS -> Shopee 外部接口 | rtsmto complete_task |
|
| `acquireWave` | 从接口号段池选择 UNUSED 号段 |
|
||||||
| 3.3.11 | `requestRtsmtoChangePickingDevice` | 当前项目/WES-WMS -> Shopee 外部接口 | rtsmto change_picking_device |
|
| `createInternalShopeeWave` | 补建单据已有波次号记录 |
|
||||||
| 3.4.5 | `requestMtoStartPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | mto start_picking_task |
|
| `backfillRtShopeeWaveCode` | RT 本地波次记录维护 |
|
||||||
| 3.4.6 | `requestMtoBindPickingTask` | 当前项目/WES-WMS -> Shopee 外部接口 | mto bind_picking_task |
|
| `releaseSingleCell` | 释放无未完成任务的格口 |
|
||||||
| 3.4.7 | `requestMtoSyncPickingDetail` | 当前项目/WES-WMS -> Shopee 外部接口 | mto sync_picking_detail |
|
| `cancelWave` | 取消失败内部 wave |
|
||||||
| 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 绑定 + 内部波次运行”的串联中枢,不是纯规则查询服务。
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user