Files
ttx-project-doc/superpowers/plans/2026-06-04-shopee-outbound-api-wes-mapping.md

56 KiB
Raw Permalink Blame History

Shopee Outbound API WES Mapping Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Implement Shopee Automation outbound interfaces plus protocol sections 4.1-5.8, with edi-shopee as the external Shopee-facing entry and WES ShopeeInboundApiHandler as the internal business handler.

Architecture: Shopee calls C:\work\gitlab\shopee\edi-shopee; edi-shopee owns Shopee URL paths, JWT validation/generation, protocol response shape, EDI logging, and forwarding to WES. WES business handling stays in wes-loghub/wms-api/src/main/groovy/com/ittx/wms/api/service/platform/ShopeeInboundApiHandler.groovy, reached by edi-shopee through the existing WES sync API with api=shopee....

Tech Stack: Groovy, Spring Boot, CBT ResponseMessage, WES SyncApiController/ApiService platform dispatch, MySQL/Flyway, edi-shopee HTTP/JWT integration.


Scope

This plan covers the outbound module, inventory/rack-transfer module, and basic-data module interfaces present in this protocol file, based on:

  • 出库单接口文档/Shopee_Automation_Vendor_接入协议手册_出库模块映射WES字段_V1.2.md
  • wes-loghub/wms-api/src/main/groovy/com/ittx/wms/api/service/platform/ShopeeInboundApiHandler.groovy
  • C:\work\gitlab\shopee\edi-shopee

Do not implement inbound module interfaces in this plan. Include all concrete interfaces from 3.1-3.4, 4.1-4.7, and 5.1-5.8.

Current State

  • SyncApiController already receives standard platform calls and delegates through ApiService; this remains the internal WES target called by edi-shopee.
  • ApiService already delegates ess.* and shopee.* APIs to PlatformInboundApiService.
  • ShopeeInboundApiHandler exists but has no outbound cases implemented.
  • Confirmed: 4.2-4.5 Vendor-to-WES inventory/rack-transfer interfaces all enter WES through ShopeeInboundApiHandler; the handler then reuses existing wms-transfer and wms-inventory services.
  • Confirmed: WES-to-Vendor basic-data interfaces in 5.2-5.8 are driven by scheduled jobs using a three-step .confirm -> .get -> .uploadStatus flow. Protocol 5.5 remains Vendor-to-WES and is not part of that scheduled outbound flow.
  • Confirmed: Shopee extension fields are persisted through the dictionary mechanism only. Do not add WES entity columns for Shopee-only fields; use different dictionary group types for different usage locations.
  • Confirmed: All 50 concrete interfaces in this plan must be implemented in this delivery. New Shopee-specific implementation classes must live under a shopee subpackage; keep only the existing platform entry handler in service/platform.
  • ShipmentHeader already contains key Shopee fields: cutOffTime, purchaseTime, orderTime, shopeeWave, waveRule, kickOutWave, ticketType, pickType, orderStructure, groupKey.
  • ShopeeWaveService has low-water scheduling and persistence, but the actual upstream wave-number request is still a placeholder.
  • WaveRuleMatchService has Shopee wave binding placeholders and local binding/rollback logic.
  • wms-transfer already contains transfer order services; use those existing transfer entities/services for rack-transfer data extraction before adding any new persistence.
  • edi-shopee already owns outbound HTTP/token patterns and is the Shopee-facing entry for both inbound calls from Shopee and outbound calls to Shopee.

File Structure

wes-loghub

  • Modify: wms-api/src/main/groovy/com/ittx/wms/api/config/domain/PlatformApiConstants.groovy
    • Add Shopee outbound API names.
  • Modify: wms-api/src/main/groovy/com/ittx/wms/api/service/platform/ShopeeInboundApiHandler.groovy
    • Keep as the existing platform dispatch entry; add switch cases for all 50 concrete APIs and delegate to services in the shopee subpackage.
  • Create: wms-api/src/main/groovy/com/ittx/wms/api/domain/platform/shopee/ShopeeOutboundApiCmds.groovy
    • Store request DTOs for all 50 Shopee protocol APIs.
  • Create: wms-api/src/main/groovy/com/ittx/wms/api/domain/platform/shopee/ShopeeOutboundApiResults.groovy
    • Store response DTOs when a list result is required.
  • Create: wms-api/src/main/groovy/com/ittx/wms/api/service/platform/shopee/ShopeeOutboundShipmentMappingService.groovy
    • Convert Shopee request fields into existing WES entities and dictionaries.
  • Modify: wms-wave/src/main/groovy/com/ittx/wms/wave/service/hairo/ShopeeWaveService.groovy
    • Replace requestShopeeWaveNumbers placeholder with a call to edi-shopee.
  • Modify: wms-wave/src/main/groovy/com/ittx/wms/wave/service/hairo/WaveRuleMatchService.groovy
    • Replace Shopee wave bind placeholder with a call to edi-shopee and parse failed shipment IDs.
  • Do not modify WES transfer-order business hooks for 4.1 in this phase.
    • Only implement the 4.1, 4.6, and 4.7 interface surfaces and edi-shopee forwarding paths; the real移库业务触发点 remains explicitly out of scope.
  • Create only if no existing transfer service method is suitable: wms-api/src/main/groovy/com/ittx/wms/api/service/platform/shopee/ShopeeRackTransferMappingService.groovy
    • Define 4.1, 4.6, and 4.7 request contracts and response normalization only; do not read TransferOrderHeader/detail data until the business trigger is confirmed later.
  • Modify: src/main/resources/application.yml and profile-specific config only if no existing system-parameter path is available.
    • Add internal edi-shopee endpoint only if it cannot be represented by existing API/customer config.

edi-shopee

  • Create or modify: src/main/groovy/com/ittx/edi/erp/service/ShopeeJwtService.groovy
    • Generate and verify HS256 JWT per Shopee protocol.
  • Create or modify: src/main/groovy/com/ittx/edi/erp/service/ShopeeAutomationVendorService.groovy
    • Validate Shopee inbound requests, forward to WES, build Shopee outbound URLs, set headers, call Shopee Automation Vendor APIs, normalize responses.
  • Create or modify: src/main/groovy/com/ittx/edi/erp/controller/ShopeeAutomationController.groovy
    • External endpoints called by Shopee plus internal endpoints consumed by wes-loghub for wave-number request and wave binding.
  • Modify: src/main/resources/application-zzw.yml.
    • Read Shopee JWT/account/host settings from customerMap.shopee; do not introduce a second config path in application-dev.yml or application-k8s.yml for this phase.

Interface Inventory

Protocol alignment from 出库单接口文档/Shopee_Automation_Vendor_接入协议手册_出库模块映射WES字段_V1.2.md:

  • Outbound module title count: 45 entries under 3.1 to 3.4.
  • Outbound module URL count: 35 concrete, unique /api/v2/automation/.../outbound/... APIs.
  • Reused/deprecated/explanation count: 10 title entries.
  • Inventory/rack-transfer URL count: 7 concrete APIs under 4.1 to 4.7.
  • Basic-data URL count: 8 concrete APIs under 5.1 to 5.8.
  • Total implemented protocol API inventory in this plan: 50 concrete APIs.

Concrete Outbound APIs

No. Section Direction Shopee/edi-shopee path WES api constant Implementation note
1 3.1.1 WMS -> Vendor /api/v2/automation/tovendor/outbound/salesorder/create_sub_picking_task shopee.outbound.salesorder.createSubPickingTask WES 下发销售 SubPickingTask
2 3.1.2 WMS -> Vendor /api/v2/automation/tovendor/outbound/salesorder/cancel_sub_picking_task shopee.outbound.salesorder.cancelSubPickingTask WES 取消销售 SubPickingTask
3 3.1.3 WMS -> Vendor /api/v2/automation/tovendor/outbound/salesorder/update_task_flag shopee.outbound.salesorder.updateTaskFlag 更新 urgent flag
4 3.1.5 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/start_picking_task shopee.outbound.salesorder.startPickingTask Vendor 开始拣货任务
5 3.1.6 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/bind_picking_task shopee.outbound.salesorder.bindPickingTask Vendor 实绑拣货任务
6 3.1.7 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/sync_picking_detail shopee.outbound.salesorder.syncPickingDetail 增量拣货明细
7 3.1.8 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/change_picking_device shopee.outbound.salesorder.changePickingDevice SubPickingTask 换箱,不产生新任务
8 3.1.9 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/complete_picking_task shopee.outbound.salesorder.completePickingTask 完成拣货任务
9 3.1.10 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/search_picking_process_guide shopee.outbound.salesorder.searchPickingProcessGuide 查询流程指引
10 3.1.11 WMS -> Vendor /api/v2/automation/tovendor/outbound/salesorder/query_picking_detail shopee.outbound.salesorder.queryPickingDetail 查询 Vendor 拣货任务列表
11 3.2.1 WMS -> Vendor /api/v2/automation/tovendor/outbound/salesorder/create_order shopee.outbound.salesorder.createOrder WMS 给 Vendor 下销售订单
12 3.2.2 WMS -> Vendor /api/v2/automation/tovendor/outbound/salesorder/cancel_order shopee.outbound.salesorder.cancelOrderToVendor WMS 向 Vendor 撤回订单,协议标题为“捞单”
13 3.2.3 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/cancel_order shopee.outbound.salesorder.vendorCancelOrder Vendor 向 WMS 撤单
14 3.2.4 WMS -> Vendor /api/v2/automation/tovendor/outbound/salesorder/lock_unlock_order shopee.outbound.salesorder.lockUnlockOrder WMS 下发锁单/解锁
15 3.2.5 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/cache_task_number shopee.outbound.salesorder.cacheTaskNumber 获取号段缓存
16 3.2.7 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/create_picking_task shopee.outbound.salesorder.createPickingTask 创建 picking task
17 3.2.8 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/append_order shopee.outbound.salesorder.appendOrder 追加订单明细
18 3.2.9 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/change_device shopee.outbound.salesorder.changeDevice Flow Pick 换箱,产生新任务
19 3.2.10 Vendor -> WMS /api/v2/automation/toshopee/outbound/salesorder/vendor_complete_task shopee.outbound.salesorder.vendorCompleteTask Vendor 完成 task
20 3.2.11 WMS -> Vendor /api/v2/automation/tovendor/outbound/salesorder/search_lock_order_list shopee.outbound.salesorder.searchLockOrderList 查询冻结状态订单列表
21 3.3.1 WMS -> Vendor /api/v2/automation/tovendor/outbound/rtsmto/create_require shopee.outbound.rtsmto.createRequire RTS/MTO 需求下发
22 3.3.2 WMS -> Vendor /api/v2/automation/tovendor/outbound/rtsmto/cancel_require shopee.outbound.rtsmto.cancelRequire RTS/MTO 需求取消
23 3.3.3 Vendor -> WMS /api/v2/automation/toshopee/outbound/rtsmto/sync_occupied_require shopee.outbound.rtsmto.syncOccupiedRequire 同步占用信息
24 3.3.6 Vendor -> WMS /api/v2/automation/toshopee/outbound/rtsmto/create_picking_task shopee.outbound.rtsmto.createPickingTask RTS/MTO 创建 picking task
25 3.3.7 Vendor -> WMS /api/v2/automation/toshopee/outbound/rtsmto/bind_picking_task shopee.outbound.rtsmto.bindPickingTask RTS/MTO 实绑任务
26 3.3.8 Vendor -> WMS /api/v2/automation/toshopee/outbound/rtsmto/append_order shopee.outbound.rtsmto.appendOrder RTS/MTO 追加
27 3.3.9 Vendor -> WMS /api/v2/automation/toshopee/outbound/rtsmto/sync_picking_detail shopee.outbound.rtsmto.syncPickingDetail RTS/MTO 增量拣货明细
28 3.3.10 Vendor -> WMS /api/v2/automation/toshopee/outbound/rtsmto/complete_task shopee.outbound.rtsmto.completeTask RTS/MTO 完成 task
29 3.3.11 Vendor -> WMS /api/v2/automation/toshopee/outbound/rtsmto/change_picking_device shopee.outbound.rtsmto.changePickingDevice RTS/MTO 换箱;协议原文 URL 有多余空格,实施时按无空格路径
30 3.4.1 WMS -> Vendor /api/v2/automation/tovendor/outbound/mto/create_picking_task shopee.outbound.mto.createPickingTask MTO 拣货任务下发
31 3.4.2 WMS -> Vendor /api/v2/automation/tovendor/outbound/mto/cancel_picking_task shopee.outbound.mto.cancelPickingTask MTO 取消拣货任务
32 3.4.5 Vendor -> WMS /api/v2/automation/toshopee/outbound/mto/start_picking_task shopee.outbound.mto.startPickingTask MTO 开始拣货
33 3.4.6 Vendor -> WMS /api/v2/automation/toshopee/outbound/mto/bind_picking_task shopee.outbound.mto.bindPickingTask MTO 真实绑定
34 3.4.7 Vendor -> WMS /api/v2/automation/toshopee/outbound/mto/sync_picking_detail shopee.outbound.mto.syncPickingDetail MTO 增量拣货明细
35 3.4.8 Vendor -> WMS /api/v2/automation/toshopee/outbound/mto/complete_picking_task shopee.outbound.mto.completePickingTask MTO 完成拣货任务

Concrete Inventory and Basic APIs

No. Section Direction Shopee/edi-shopee path WES api constant Implementation note
36 4.1 WMS -> Vendor /api/v2/automation/tovendor/inventory/racktransfer/create_rt_order shopee.inventory.racktransfer.createRtOrder WES 给 Vendor 同步移库占用库存
37 4.2 Vendor -> WMS /api/v2/automation/toshopee/inventory/racktransfer/occupy_device shopee.inventory.racktransfer.occupyDevice Vendor 占用或释放 PickingDevice
38 4.3 Vendor -> WMS /api/v2/automation/toshopee/inventory/racktransfer/submit_picking_sku_list shopee.inventory.racktransfer.submitPickingSkuList Vendor 整箱同步移库拣货明细
39 4.4 Vendor -> WMS /api/v2/automation/toshopee/inventory/racktransfer/sync_picking_sku_list shopee.inventory.racktransfer.syncPickingSkuList Vendor 定时回传 WMS 创建移库单拣货明细
40 4.5 Vendor -> WMS /api/v2/automation/toshopee/inventory/racktransfer/handle_diff_stock shopee.inventory.racktransfer.handleDiffStock Vendor 盘点差异调减同步给 WMS
41 4.6 WMS -> Vendor /api/v2/automation/tovendor/inventory/racktransfer/query_picking_list shopee.inventory.racktransfer.queryPickingList WES 查询 Vendor 移库拣货明细用于对账
42 4.7 WMS -> Vendor /api/v2/automation/tovendor/inventory/racktransfer/update_rt_order shopee.inventory.racktransfer.updateRtOrder WES 更新移库单优先级或取消状态
43 5.1 Vendor -> WMS /api/v2/automation/toshopee/basic/user/query_user_exist shopee.basic.user.queryUserExist Vendor 查询 WES 用户是否存在
44 5.2 WMS -> Vendor /api/v2/automation/tovendor/basic/inventory/batch_status_update shopee.basic.inventory.batchStatusUpdate WES 同步批次状态给 Vendor
45 5.3 WMS -> Vendor /api/v2/automation/tovendor/basic/inventory/query_sku_batch_inventory shopee.basic.inventory.querySkuBatchInventory WES 查询 Vendor 批次库存信息
46 5.4 WMS -> Vendor /api/v2/automation/tovendor/basic/inventory/sync_reconciliation_result shopee.basic.inventory.syncReconciliationResult WES 同步库存对账结果给 Vendor
47 5.5 Vendor -> WMS /api/v2/automation/toshopee/basic/sku/query_sku_detail shopee.basic.sku.querySkuDetail Vendor 查询 WES SKU 信息
48 5.6 WMS -> Vendor /api/v2/automation/tovendor/basic/sku/sync_sku_info shopee.basic.sku.syncSkuInfo WES 同步 SKU 信息给 Vendor
49 5.7 WMS -> Vendor /api/v2/automation/tovendor/basic/robot/query_agv_status shopee.basic.robot.queryAgvStatus WES 查询 Vendor AGV 设备信息
50 5.8 WMS -> Vendor /api/v2/automation/tovendor/basic/inventory/query_vendor_unit_list shopee.basic.inventory.queryVendorUnitList WES 查询 Vendor Unit 信息

Reused or Deprecated Title Entries

Section Title Handling
3.1.4 Vendor绑定/解绑device(空占)【复用basic接口】废弃 不新增出库接口
3.2.6 Vendor绑定解绑设备【复用basic接口】废弃 不新增出库接口
3.2.12 复用接口列表 说明项,不新增接口
3.3.4 Vendor绑定/解绑device【废弃】 不新增出库接口
3.3.5 Vendor向wms获取号段缓存【复用】 复用 3.2.5
3.3.12 查询Vendor拣货任务列表【复用】 复用 3.1.11
3.3.13 更新需求的urgent_flag【复用】 复用 3.1.3
3.4.3 WMS向Vendor更新task的urgent flag【复用3.1.3】 复用 3.1.3
3.4.4 Vendor绑定/解绑device【复用basic接口】废弃 不新增出库接口
3.4.9 查询Vendor拣货任务列表【复用】 复用 3.1.11

Interface Names

Add constants for all 50 concrete APIs above. All 50 must be implemented in this delivery; the constant inventory must stay aligned to the table and to Task 1 Step 1.

Two cancel_order protocol paths exist with different directions, so they use separate WES API identifiers: cancelOrderToVendor for tovendor and vendorCancelOrder for toshopee.

Use these names as WES internal API identifiers. Shopee does not call WES directly; Shopee calls edi-shopee, and edi-shopee forwards to WES SyncApiController with /api/wms/api/sync/in?api=<constant>&format=json.


Task 1: Add Shopee Constants and DTOs

Files:

  • Modify: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\config\domain\PlatformApiConstants.groovy

  • Create: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\domain\platform\shopee\ShopeeOutboundApiCmds.groovy

  • Create: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\domain\platform\shopee\ShopeeOutboundApiResults.groovy

  • Step 1: Add constants

Add the full 50-interface constant set under PlatformApiConstants.ShopeeInterfaceType. Do not leave any of these 50 constants as ApiMessages.MSG_INTF_0005 in the final implementation:

static final String SALES_CREATE_SUB_PICKING_TASK = 'shopee.outbound.salesorder.createSubPickingTask'
static final String SALES_CANCEL_SUB_PICKING_TASK = 'shopee.outbound.salesorder.cancelSubPickingTask'
static final String SALES_UPDATE_TASK_FLAG = 'shopee.outbound.salesorder.updateTaskFlag'
static final String SALES_ORDER_CREATE = 'shopee.outbound.salesorder.createOrder'
static final String SALES_CANCEL_ORDER_TO_VENDOR = 'shopee.outbound.salesorder.cancelOrderToVendor'
static final String SALES_SEARCH_LOCK_ORDER_LIST = 'shopee.outbound.salesorder.searchLockOrderList'
static final String SALES_VENDOR_CANCEL_ORDER = 'shopee.outbound.salesorder.vendorCancelOrder'
static final String SALES_LOCK_UNLOCK_ORDER = 'shopee.outbound.salesorder.lockUnlockOrder'
static final String SALES_CACHE_TASK_NUMBER = 'shopee.outbound.salesorder.cacheTaskNumber'
static final String SALES_START_PICKING_TASK = 'shopee.outbound.salesorder.startPickingTask'
static final String SALES_BIND_PICKING_TASK = 'shopee.outbound.salesorder.bindPickingTask'
static final String SALES_SYNC_PICKING_DETAIL = 'shopee.outbound.salesorder.syncPickingDetail'
static final String SALES_CHANGE_PICKING_DEVICE = 'shopee.outbound.salesorder.changePickingDevice'
static final String SALES_COMPLETE_PICKING_TASK = 'shopee.outbound.salesorder.completePickingTask'
static final String SALES_SEARCH_PICKING_PROCESS_GUIDE = 'shopee.outbound.salesorder.searchPickingProcessGuide'
static final String SALES_QUERY_PICKING_DETAIL = 'shopee.outbound.salesorder.queryPickingDetail'
static final String SALES_CREATE_PICKING_TASK = 'shopee.outbound.salesorder.createPickingTask'
static final String SALES_APPEND_ORDER = 'shopee.outbound.salesorder.appendOrder'
static final String SALES_CHANGE_DEVICE = 'shopee.outbound.salesorder.changeDevice'
static final String SALES_VENDOR_COMPLETE_TASK = 'shopee.outbound.salesorder.vendorCompleteTask'
static final String RTSMTO_CREATE_REQUIRE = 'shopee.outbound.rtsmto.createRequire'
static final String RTSMTO_CANCEL_REQUIRE = 'shopee.outbound.rtsmto.cancelRequire'
static final String RTSMTO_SYNC_OCCUPIED_REQUIRE = 'shopee.outbound.rtsmto.syncOccupiedRequire'
static final String RTSMTO_CREATE_PICKING_TASK = 'shopee.outbound.rtsmto.createPickingTask'
static final String RTSMTO_BIND_PICKING_TASK = 'shopee.outbound.rtsmto.bindPickingTask'
static final String RTSMTO_APPEND_ORDER = 'shopee.outbound.rtsmto.appendOrder'
static final String RTSMTO_SYNC_PICKING_DETAIL = 'shopee.outbound.rtsmto.syncPickingDetail'
static final String RTSMTO_COMPLETE_TASK = 'shopee.outbound.rtsmto.completeTask'
static final String RTSMTO_CHANGE_PICKING_DEVICE = 'shopee.outbound.rtsmto.changePickingDevice'
static final String MTO_CREATE_PICKING_TASK = 'shopee.outbound.mto.createPickingTask'
static final String MTO_CANCEL_PICKING_TASK = 'shopee.outbound.mto.cancelPickingTask'
static final String MTO_START_PICKING_TASK = 'shopee.outbound.mto.startPickingTask'
static final String MTO_BIND_PICKING_TASK = 'shopee.outbound.mto.bindPickingTask'
static final String MTO_SYNC_PICKING_DETAIL = 'shopee.outbound.mto.syncPickingDetail'
static final String MTO_COMPLETE_PICKING_TASK = 'shopee.outbound.mto.completePickingTask'
static final String RACK_TRANSFER_CREATE_RT_ORDER = 'shopee.inventory.racktransfer.createRtOrder'
static final String RACK_TRANSFER_OCCUPY_DEVICE = 'shopee.inventory.racktransfer.occupyDevice'
static final String RACK_TRANSFER_SUBMIT_PICKING_SKU_LIST = 'shopee.inventory.racktransfer.submitPickingSkuList'
static final String RACK_TRANSFER_SYNC_PICKING_SKU_LIST = 'shopee.inventory.racktransfer.syncPickingSkuList'
static final String RACK_TRANSFER_HANDLE_DIFF_STOCK = 'shopee.inventory.racktransfer.handleDiffStock'
static final String RACK_TRANSFER_QUERY_PICKING_LIST = 'shopee.inventory.racktransfer.queryPickingList'
static final String RACK_TRANSFER_UPDATE_RT_ORDER = 'shopee.inventory.racktransfer.updateRtOrder'
static final String BASIC_USER_QUERY_USER_EXIST = 'shopee.basic.user.queryUserExist'
static final String BASIC_INVENTORY_BATCH_STATUS_UPDATE = 'shopee.basic.inventory.batchStatusUpdate'
static final String BASIC_INVENTORY_QUERY_SKU_BATCH_INVENTORY = 'shopee.basic.inventory.querySkuBatchInventory'
static final String BASIC_INVENTORY_SYNC_RECONCILIATION_RESULT = 'shopee.basic.inventory.syncReconciliationResult'
static final String BASIC_SKU_QUERY_SKU_DETAIL = 'shopee.basic.sku.querySkuDetail'
static final String BASIC_SKU_SYNC_SKU_INFO = 'shopee.basic.sku.syncSkuInfo'
static final String BASIC_ROBOT_QUERY_AGV_STATUS = 'shopee.basic.robot.queryAgvStatus'
static final String BASIC_INVENTORY_QUERY_VENDOR_UNIT_LIST = 'shopee.basic.inventory.queryVendorUnitList'
  • Step 2: Add DTO shells

Create DTOs with only fields present in the protocol tables and mapped to WES fields. Use Groovy classes, no Lombok, no new validation framework.

Minimum classes:

package com.ittx.wms.api.domain.platform.shopee

import java.io.Serializable

class ShopeeCreateSubPickingTaskCmd implements Serializable {
    String whs_id
    String sub_pickup_id
    Integer urgent_flag
    Long cut_off_time
    Long ship_by_date
    Long purchase_time
    Long order_time
    Long ctime
    Integer can_group_picking
    String group_key
    List<ShopeeSkuInfoCmd> sku_info_list = []
    List<ShopeeSingleAttrCmd> single_attr_list = []
    List<ShopeeMultiAttrCmd> multi_attr_list = []
    Integer ticket_type
}

class ShopeeSkuInfoCmd implements Serializable {
    String sku_id
    BigDecimal qty
    Integer block_type
    Integer quality
}

class ShopeeSingleAttrCmd implements Serializable {
    String attr_key
    String attr_value_id
    String attr_value_type
    String attr_value_name
}

class ShopeeMultiAttrCmd implements Serializable {
    String attr_key
    List<ShopeeAttrCmd> attr_value_list = []
}

class ShopeeAttrCmd implements Serializable {
    String attr_value_id
    String attr_value_type
    String attr_value_name
}

class ShopeeCancelSubPickingTaskCmd implements Serializable {
    String whs_id
    String sub_pickup_id
}

class ShopeeCreateRtOrderCmd implements Serializable {
    String whs_id
    String rt_order_id
    List<ShopeeRtAttrCmd> attr_list = []
    Long ctime
    String operator
    Integer ticket_type
    List<ShopeeRtSkuInfoCmd> sku_info_list = []
}

class ShopeeRtAttrCmd implements Serializable {
    String attr_key
    String attr_value_id
    String attr_value_name
}

class ShopeeRtSkuInfoCmd implements Serializable {
    String sku_id
    Integer block_type
    BigDecimal qty
    Integer sku_quality
}

class ShopeeUpdateTaskFlagCmd implements Serializable {
    String whs_id
    Integer biz_type
    Integer ticket_type
    List<String> ticket_number_list = []
    Integer urgent_flag
}
  • Step 3: Compile-check DTO syntax

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-api:compileGroovy --no-problems-report

Expected: Groovy compile succeeds, or dependency resolution fails due local Nexus credentials. If Nexus fails, record the exact failure.

Task 2: Wire ShopeeInboundApiHandler

Files:

  • Modify: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\service\platform\ShopeeInboundApiHandler.groovy

  • Create: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\service\platform\shopee\ShopeeOutboundShipmentMappingService.groovy

  • Step 1: Inject the mapping service

Add:

@Autowired
ShopeeOutboundShipmentMappingService shopeeOutboundShipmentMappingSvc
  • Step 2: Add switch cases

Use the constants from Task 1. Add switch cases for all 50 concrete APIs and delegate to focused services under com.ittx.wms.api.service.platform.shopee.

case PlatformApiConstants.ShopeeInterfaceType.SALES_CREATE_SUB_PICKING_TASK:
    return createSubPickingTask(session, data)
case PlatformApiConstants.ShopeeInterfaceType.SALES_CANCEL_SUB_PICKING_TASK:
    return cancelSubPickingTask(session, data)
case PlatformApiConstants.ShopeeInterfaceType.SALES_UPDATE_TASK_FLAG:
    return updateTaskFlag(session, data)
case PlatformApiConstants.ShopeeInterfaceType.RACK_TRANSFER_OCCUPY_DEVICE:
    return shopeeRackTransferSvc.occupyDevice(session, data)
case PlatformApiConstants.ShopeeInterfaceType.BASIC_SKU_QUERY_SKU_DETAIL:
    return shopeeBasicDataSvc.querySkuDetail(session, data)

Final implementation must not return ApiMessages.MSG_INTF_0005 for any of the 50 concrete APIs in the inventory.

  • Step 3: Add parse/validate methods

Implement each method with the same pattern as EssInboundApiHandler: parse, validate required fields, set CURRENT_WAREHOUSE, call service.

Example:

private ResponseMessage createSubPickingTask(TtxSession session, String data) {
    ShopeeCreateSubPickingTaskCmd cmd
    try {
        cmd = convertValue(data, ShopeeCreateSubPickingTaskCmd.class)
    } catch (Throwable ignored) {
        return ResponseMessageFactory.error(ApiMessages.MSG_INTF_0003)
    }
    ResponseMessage validateRsp = validateCreateSubPickingTask(cmd)
    if (validateRsp.hasError()) return validateRsp
    session.params[WmsConstants.CURRENT_WAREHOUSE] = cmd.whs_id
    return shopeeOutboundShipmentMappingSvc.createSubPickingTask(session, cmd)
}
  • Step 4: Compile

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-api:compileGroovy --no-problems-report

Expected: Compile succeeds or reports only external dependency credential issues.

Task 3: Implement Sales SubPickingTask Mapping

Files:

  • Modify: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\service\platform\shopee\ShopeeOutboundShipmentMappingService.groovy

  • Reuse: ShipmentHeaderService, ShipmentDetailService, existing config/dictionary service if available

  • Step 1: Implement createSubPickingTask

Map fields:

Shopee field WES field
whs_id shipment_header.warehouseCode
sub_pickup_id shipment_header.erpOrderCode and shipment_header.shopeeWave
urgent_flag shipment_header.priority
cut_off_time shipment_header.cutOffTime
ship_by_date shipment_header.scheduledShipDate
purchase_time shipment_header.purchaseTime
order_time shipment_header.orderTime
ctime dictionary only if business needs query/display; otherwise request log only
can_group_picking dictionary only
group_key shipment_header.groupKey
ticket_type shipment_header.ticketType; shipmentType defaults to XSCK
max_order_size attr dictionary only
channel_id attr dictionary only
fulfillment_chain_id attr dictionary only

Use WES entity fields only for existing core business facts already present in the domain. All Shopee-only extension attributes go through the dictionary mechanism, not new columns.

  • Step 2: Implement idempotency

Use warehouseCode + erpOrderCode:

select id from shipment_header where warehouseCode = ? and erpOrderCode = ? and processType = 'NORMAL'

If found, update mutable Shopee fields and replace details only if the task is still in pool. If task has started, return a non-retryable parameter/business error.

  • Step 3: Create details

For each sku_info_list row:

Shopee field WES field
sku_id shipment_detail.itemCode
qty shipment_detail.requestQty
block_type shipment_detail.shelfLifeSts
quality shipment_detail.inventorySts

Validate SKU existence by reusing existing item/detail service behavior. Do not hand-roll item master queries if a service already validates item code.

  • Step 4: Map all Shopee extension attributes to dictionary

For single_attr_list, multi_attr_list, and Shopee-only scalar fields, write values to the existing field dictionary mechanism. Different usage locations must use different dictionary group types so downstream logic can distinguish meaning.

Minimum group type split:

Usage location Dictionary group type
Outbound sales/sub-picking task header attrs SHOPEE_OUTBOUND_TASK_HEADER
Outbound order attrs SHOPEE_OUTBOUND_ORDER_HEADER
RTS/MTO demand attrs SHOPEE_RTSMTO_DEMAND_HEADER
MTO picking task attrs SHOPEE_MTO_TASK_HEADER
Inventory/rack-transfer attrs SHOPEE_RACK_TRANSFER_HEADER
Basic-data sync attrs SHOPEE_BASIC_DATA

If no compatible dictionary table/service exists in the current branch, add a separate dictionary task before implementation; do not add Shopee-only fields to WES domain entities:

rg -n "shopee_field_dictionary|FieldDictionary|field_dictionary" C:\work\gitlab\shopee\wes-loghub

If absent, add a later task before implementation; do not create it inside this task ad hoc.

  • Step 5: Compile

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-api:compileGroovy :wms-shipping:compileGroovy --no-problems-report

Task 4: Implement Cancel and Urgent Flag Update

Files:

  • Modify: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\service\platform\shopee\ShopeeOutboundShipmentMappingService.groovy

  • Step 1: Implement cancel

Input: ShopeeCancelSubPickingTaskCmd.

Behavior:

  • Find by warehouseCode = whs_id, erpOrderCode = sub_pickup_id.

  • If not found, return non-retryable business error.

  • If already picked/closed, return non-retryable business error.

  • Otherwise reuse existing cancellation/status-flow service. If no exact cancel service applies, mark processType/status via the same base cancel path used by manual shipment cancel, not a raw SQL-only status mutation.

  • Ensure wave matching excludes canceled shipments.

  • Step 2: Implement urgent flag update

Input: ShopeeUpdateTaskFlagCmd.

Behavior:

  • Batch update shipment_header.priority by erpOrderCode in ticket_number_list.
  • Return success_list, fail_list, and not_exist_list.
  • Do not fail the entire request because one order is missing.

Return shape:

[
    success_list: successCodes,
    fail_list: failCodes,
    not_exist_list: notExistCodes
]
  • Step 3: Compile

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-api:compileGroovy :wms-shipping:compileGroovy --no-problems-report

Task 5: Implement edi-shopee JWT Service

Files:

  • Create: C:\work\gitlab\shopee\edi-shopee\src\main\groovy\com\ittx\edi\erp\service\ShopeeJwtService.groovy

  • Modify: C:\work\gitlab\shopee\edi-shopee\src\main\resources\application-zzw.yml

  • Step 1: Add JWT generation

Implement HS256 with Base64 URL encoding:

String generateToken(String account, String secret, Long timestampSeconds = System.currentTimeMillis().intdiv(1000L)) {
    Map header = [alg: 'HS256', typ: 'JWT']
    Map payload = [timestamp: timestampSeconds, account: account]
    String signingInput = base64UrlJson(header) + '.' + base64UrlJson(payload)
    String signature = hmacSha256Base64Url(signingInput, secret)
    return signingInput + '.' + signature
}
  • Step 2: Add token validation

Validation must:

  • Split token into 3 segments.

  • Verify signature using configured secret.

  • Parse payload timestamp.

  • Reject if absolute clock drift is greater than 300 seconds.

  • Reject if account does not match configured account.

  • Step 3: Read config from customerMap.shopee

JWT and Shopee host configuration live in application-zzw.yml under customerMap.shopee:

customerMap:
  shopee:
    account: xxx
    secret: xxx
    env: uat
    cid: cn
    connectTimeoutMillis: 3000
    readTimeoutMillis: 10000

ShopeeJwtService and ShopeeAutomationVendorService must read account, secret, host environment, country id, and timeout values from customerMap.shopee. Do not hard-code secrets in code and do not add duplicate Shopee JWT config under another profile file in this phase.

  • Step 4: Compile edi-shopee

Run:

cd C:\work\gitlab\shopee\edi-shopee
.\gradlew compileGroovy

Task 6: Implement edi-shopee Shopee Automation Gateway

Files:

  • Create: C:\work\gitlab\shopee\edi-shopee\src\main\groovy\com\ittx\edi\erp\service\ShopeeAutomationVendorService.groovy

  • Create: C:\work\gitlab\shopee\edi-shopee\src\main\groovy\com\ittx\edi\erp\controller\ShopeeAutomationController.groovy

  • Step 1: Add Shopee inbound controller methods

Expose Shopee-facing POST endpoints in edi-shopee, not in WES:

@PostMapping('/api/v2/automation/toshopee/outbound/salesorder/create_sub_picking_task')
Object createSubPickingTask(@RequestHeader('Authorization') String authorization,
                            @RequestBody Map body) {
    return shopeeAutomationVendorSvc.forwardShopeeInbound(
            authorization,
            'shopee.outbound.salesorder.createSubPickingTask',
            body)
}

Add the same pattern for implemented Shopee inbound interfaces:

  • /api/v2/automation/toshopee/outbound/salesorder/cancel_sub_picking_task

  • /api/v2/automation/toshopee/outbound/salesorder/update_task_flag

  • sales order and demand pool paths when their WES handlers are implemented.

  • Step 2: Validate JWT before forwarding to WES

For every Shopee inbound request:

  • Read Authorization.
  • Validate JWT using ShopeeJwtService.
  • Reject invalid account, signature, or timestamp over 5 minutes.
  • Return Shopee response shape directly:
[retcode: -10000000, message: 'auth failed', data: [:]]
  • Step 3: Forward valid Shopee inbound requests to WES

Call WES existing sync API from edi-shopee:

POST {wesBaseUrl}/api/wms/api/sync/in?api={apiName}&format=json
Content-Type: application/json
Body: original Shopee body

Convert WES ResponseMessage back to Shopee response shape:

[retcode: 0, message: 'success', data: rsp.data ?: [:]]

On WES business error, do not implement final Shopee retcode mapping yet. Return a generic Shopee failure shape, keep the WES message sanitized, and log the original WES error for later mapping design. Do not expose WES stack traces.

  • Step 4: Add outbound call method

Implement:

ResponseMessage postToShopee(String module, String subModule, String interfaceName, Map body)

Build URL:

https://wms-automation.ssc.{env}.shopee.{cid}/api/v2/automation/{direction}/{module}/{sub_module}/{interface_name}

For production, omit {env} according to the protocol.

  • Step 5: Add headers for edi-shopee outbound calls to Shopee

Headers:

[
    'Content-Type': 'application/json',
    'Authorization': shopeeJwtSvc.generateToken(account, secret)
]
  • Step 6: Normalize response from Shopee outbound calls

Shopee response:

{"retcode":0,"message":"success","data":{}}

Convert to CBT:

  • retcode == 0 -> ResponseMessageFactory.success(data)

  • otherwise -> ResponseMessageFactory.error(message) and include retcode in data or parameters

  • Step 7: Add internal endpoints for WES-to-Shopee calls

Internal controller methods:

  • POST /internal/shopee/automation/waveNumbers
  • POST /internal/shopee/automation/bindWave
  • POST /internal/shopee/automation/cancelOrder
  • POST /internal/shopee/automation/flowPickChangeBox
  • POST /internal/shopee/automation/rackTransfer/createRtOrder

Keep these endpoints internal. They are for wes-loghub to call edi-shopee; Shopee-facing inbound endpoints are the /api/v2/automation/toshopee/... methods above, while WES-to-Vendor calls use tovendor paths built by edi-shopee.

  • Step 8: Compile

Run:

cd C:\work\gitlab\shopee\edi-shopee
.\gradlew compileGroovy

Task 7: Replace WES ShopeeWaveService Placeholder

Files:

  • Modify: C:\work\gitlab\shopee\wes-loghub\wms-wave\src\main\groovy\com\ittx\wms\wave\service\hairo\ShopeeWaveService.groovy

  • Step 1: Add an internal edi-shopee client method

Prefer a small private method in ShopeeWaveService or a focused bean if an existing HTTP utility is available.

Request:

[
    warehouseCode: warehouseCode,
    waveType: waveType,
    needQty: needQty
]

Response data should be a list of wave codes:

[
    [code: 'SW202606040001', waveType: '1'],
    [code: 'SW202606040002', waveType: '1']
]
  • Step 2: Implement requestShopeeWaveNumbers

Replace the placeholder log-only method with:

  • Call edi-shopee internal endpoint.

  • Convert returned codes to ShopeeWave.

  • Return empty list on call failure and log a process history error.

  • Do not generate fake local wave numbers.

  • Step 3: Verify low-water scheduler

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-wave:compileGroovy --no-problems-report

Task 8: Replace WES Wave Binding Placeholder

Files:

  • Modify: C:\work\gitlab\shopee\wes-loghub\wms-wave\src\main\groovy\com\ittx\wms\wave\service\hairo\WaveRuleMatchService.groovy

  • Step 1: Implement requestShopeeWaveBind via edi-shopee

Request:

[
    warehouseCode: warehouseCode,
    shopeeWaveCode: shopeeWaveCode,
    shipmentIds: shipmentIds
]

Response:

[
    success_list: [1L, 2L],
    fail_list: [3L]
]
  • Step 2: Parse partial failure

Current code treats bind failure as whole batch failure. Change only if Shopee returns per-order failure. If partial failure exists:

  • Mark failed shipments with kickOutWave.
  • Continue binding successful shipment IDs.
  • Return success if at least one shipment bound.

If Shopee returns whole request failure, keep existing whole-batch behavior.

  • Step 3: Keep lock and rollback semantics

Do not remove:

  • buildShopeeWaveBindLockKey
  • isShopeeWaveCodeAvailable
  • rollbackShipmentShopeeWave
  • rollbackWaveShipments

These protect local WES consistency around external calls.

  • Step 4: Compile

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-wave:compileGroovy --no-problems-report

Task 9A: Implement Inventory and Basic Interface Surfaces 4.1-5.8

Files:

  • Do not modify: C:\work\gitlab\shopee\wes-loghub\wms-transfer\src\main\groovy\com\ittx\wms\transfer\service\TransferOrderHeaderService.groovy

  • Do not modify: C:\work\gitlab\shopee\wes-loghub\wms-transfer\src\main\groovy\com\ittx\wms\transfer\service\TransferOrderDetailService.groovy

  • Create only if needed: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\service\platform\shopee\ShopeeRackTransferMappingService.groovy

  • Step 1: Keep 4.1 business trigger out of scope

Do not search for or wire the WES移库单创建、定位、锁定、占用完成事件 in this phase.

Implementation target for 4.1 is limited to:

  • edi-shopee internal endpoint: POST /internal/shopee/automation/rackTransfer/createRtOrder

  • Shopee outbound call: POST /api/v2/automation/tovendor/inventory/racktransfer/create_rt_order

  • Request validation, JWT header generation, response normalization, logging, and retry classification.

  • Step 2: Define the 4.1 request contract without wiring WES transfer data

Endpoint request shape accepted by edi-shopee:

[
    whs_id: request.whs_id,
    rt_order_id: request.rt_order_id,
    attr_list: [
        [attr_key: 'urgent_flag', attr_value_id: '1', attr_value_name: '1'],
        [attr_key: 'oos_order_qty', attr_value_id: '0', attr_value_name: '0']
    ].findAll { it.attr_value_id != null },
    ctime: request.ctime,
    operator: request.operator,
    ticket_type: 6,
    sku_info_list: request.sku_info_list.collect {
        [
            sku_id: it.sku_id,
            block_type: it.block_type,
            qty: it.qty,
            sku_quality: it.sku_quality
        ]
    }
]

Keep ticket_type = 6 fixed for Rack Transfer. The later WES business trigger will be responsible for building this body from real transfer entities.

  • Step 3: Add 4.2-4.5 Vendor-to-WES rack-transfer handlers

Expose these Shopee-facing edi-shopee endpoints and forward them to WES sync API with the WES api name below. WES dispatch must be:

edi-shopee -> /api/wms/api/sync/in?api=<WES api>&format=json -> PlatformInboundApiService -> ShopeeInboundApiHandler
Section Path WES api
4.2 /api/v2/automation/toshopee/inventory/racktransfer/occupy_device shopee.inventory.racktransfer.occupyDevice
4.3 /api/v2/automation/toshopee/inventory/racktransfer/submit_picking_sku_list shopee.inventory.racktransfer.submitPickingSkuList
4.4 /api/v2/automation/toshopee/inventory/racktransfer/sync_picking_sku_list shopee.inventory.racktransfer.syncPickingSkuList
4.5 /api/v2/automation/toshopee/inventory/racktransfer/handle_diff_stock shopee.inventory.racktransfer.handleDiffStock

Inside ShopeeInboundApiHandler, route to existing wms-transfer / wms-inventory services first. Do not add a separate WES controller for these four endpoints. Add new service methods only when existing transfer/inventory APIs cannot express the protocol action.

  • Step 4: Add 4.6-4.7 WMS-to-Vendor rack-transfer interface endpoints only

Use internal edi-shopee endpoints for:

Section Internal endpoint Shopee path
4.6 /internal/shopee/automation/rackTransfer/queryPickingList /api/v2/automation/tovendor/inventory/racktransfer/query_picking_list
4.7 /internal/shopee/automation/rackTransfer/updateRtOrder /api/v2/automation/tovendor/inventory/racktransfer/update_rt_order

Do not bind these endpoints to WES对账任务、移库取消、优先级更新、人工操作 or any other business trigger in this phase.

Keep the 4.1 internal endpoint:

Use the same internal client pattern as wave-number and wave-bind calls:

POST {ediShopeeBaseUrl}/internal/shopee/automation/rackTransfer/createRtOrder

edi-shopee then calls Shopee:

POST /api/v2/automation/tovendor/inventory/racktransfer/create_rt_order

Failure handling:

  • Preserve Shopee retcode and message in the EDI call log.

  • Do not finalize retry/non-retry mapping in this phase.

  • Use existing transport failure handling for timeout/network errors only.

  • Step 5: Add 5.1 and 5.5 Vendor-to-WES basic handlers

Expose Shopee-facing edi-shopee endpoints and forward to WES:

Section Path WES api
5.1 /api/v2/automation/toshopee/basic/user/query_user_exist shopee.basic.user.queryUserExist
5.5 /api/v2/automation/toshopee/basic/sku/query_sku_detail shopee.basic.sku.querySkuDetail

Reuse existing WES user and SKU services. Do not create duplicate user/SKU lookup SQL if a service already exists.

  • Step 6: Add 5.2-5.4 and 5.6-5.8 scheduled WMS-to-Vendor basic calls

Confirmed trigger source: scheduled jobs. Each WES-to-Vendor basic-data sync uses this three-step internal flow:

<basic api>.confirm -> <basic api>.get -> <basic api>.uploadStatus

Use this pattern for the WES-to-Vendor basic-data interfaces. 5.5 remains a Vendor-to-WES query handled in Step 5.

Section Step Internal endpoint Shopee path
5.2 .confirm /internal/shopee/automation/basic/inventory/batchStatusUpdate.confirm /api/v2/automation/tovendor/basic/inventory/batch_status_update
5.2 .get /internal/shopee/automation/basic/inventory/batchStatusUpdate.get /api/v2/automation/tovendor/basic/inventory/batch_status_update
5.2 .uploadStatus /internal/shopee/automation/basic/inventory/batchStatusUpdate.uploadStatus /api/v2/automation/tovendor/basic/inventory/batch_status_update
5.3 .confirm /internal/shopee/automation/basic/inventory/querySkuBatchInventory.confirm /api/v2/automation/tovendor/basic/inventory/query_sku_batch_inventory
5.3 .get /internal/shopee/automation/basic/inventory/querySkuBatchInventory.get /api/v2/automation/tovendor/basic/inventory/query_sku_batch_inventory
5.3 .uploadStatus /internal/shopee/automation/basic/inventory/querySkuBatchInventory.uploadStatus /api/v2/automation/tovendor/basic/inventory/query_sku_batch_inventory
5.4 .confirm /internal/shopee/automation/basic/inventory/syncReconciliationResult.confirm /api/v2/automation/tovendor/basic/inventory/sync_reconciliation_result
5.4 .get /internal/shopee/automation/basic/inventory/syncReconciliationResult.get /api/v2/automation/tovendor/basic/inventory/sync_reconciliation_result
5.4 .uploadStatus /internal/shopee/automation/basic/inventory/syncReconciliationResult.uploadStatus /api/v2/automation/tovendor/basic/inventory/sync_reconciliation_result
5.6 .confirm /internal/shopee/automation/basic/sku/syncSkuInfo.confirm /api/v2/automation/tovendor/basic/sku/sync_sku_info
5.6 .get /internal/shopee/automation/basic/sku/syncSkuInfo.get /api/v2/automation/tovendor/basic/sku/sync_sku_info
5.6 .uploadStatus /internal/shopee/automation/basic/sku/syncSkuInfo.uploadStatus /api/v2/automation/tovendor/basic/sku/sync_sku_info
5.7 .confirm /internal/shopee/automation/basic/robot/queryAgvStatus.confirm /api/v2/automation/tovendor/basic/robot/query_agv_status
5.7 .get /internal/shopee/automation/basic/robot/queryAgvStatus.get /api/v2/automation/tovendor/basic/robot/query_agv_status
5.7 .uploadStatus /internal/shopee/automation/basic/robot/queryAgvStatus.uploadStatus /api/v2/automation/tovendor/basic/robot/query_agv_status
5.8 .confirm /internal/shopee/automation/basic/inventory/queryVendorUnitList.confirm /api/v2/automation/tovendor/basic/inventory/query_vendor_unit_list
5.8 .get /internal/shopee/automation/basic/inventory/queryVendorUnitList.get /api/v2/automation/tovendor/basic/inventory/query_vendor_unit_list
5.8 .uploadStatus /internal/shopee/automation/basic/inventory/queryVendorUnitList.uploadStatus /api/v2/automation/tovendor/basic/inventory/query_vendor_unit_list

The previous one-call internal endpoint names are retained only as conceptual API names, not as the scheduled-job contract:

Section Internal endpoint Shopee path
5.2 /internal/shopee/automation/basic/inventory/batchStatusUpdate /api/v2/automation/tovendor/basic/inventory/batch_status_update
5.3 /internal/shopee/automation/basic/inventory/querySkuBatchInventory /api/v2/automation/tovendor/basic/inventory/query_sku_batch_inventory
5.4 /internal/shopee/automation/basic/inventory/syncReconciliationResult /api/v2/automation/tovendor/basic/inventory/sync_reconciliation_result
5.6 /internal/shopee/automation/basic/sku/syncSkuInfo /api/v2/automation/tovendor/basic/sku/sync_sku_info
5.7 /internal/shopee/automation/basic/robot/queryAgvStatus /api/v2/automation/tovendor/basic/robot/query_agv_status
5.8 /internal/shopee/automation/basic/inventory/queryVendorUnitList /api/v2/automation/tovendor/basic/inventory/query_vendor_unit_list

These calls stay in edi-shopee. Scheduled jobs call .confirm, .get, then .uploadStatus; do not bind them to ad hoc WES business events in this phase.

  • Step 7: Compile transfer, API, inventory, and edi-shopee modules

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-transfer:compileGroovy :wms-api:compileGroovy --no-problems-report
cd C:\work\gitlab\shopee\edi-shopee
.\gradlew compileGroovy

Task 9: Add Sales Order and Demand Pool Interfaces

Files:

  • Modify DTO file from Task 1.

  • Modify ShopeeInboundApiHandler.groovy.

  • Modify C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\service\platform\shopee\ShopeeOutboundShipmentMappingService.groovy.

  • Step 1: Implement sales order create/pull/revoke/lock

Use the same handler pattern as Tasks 2-4.

Minimum mappings:

  • Sales order create maps to shipment_header and shipment_detail, with ticketType = 2.

  • Pull order queries eligible WES orders by Shopee identifiers and current state.

  • Revoke order cancels or marks excluded, using existing shipment cancel/status services.

  • Lock/unlock writes existing lock fields if present; otherwise reuse standard lock service.

  • Step 2: Implement RTS/MTO demand create/cancel/sync occupy

Minimum mappings:

  • RTS demand -> shipmentType = RTS, ticketType = 3.

  • MTO demand -> shipmentType = MTO, ticketType = 4.

  • MTO task -> ticketType = 5.

  • RT -> ticketType = 6, does not use Shopee wave number.

  • Step 3: Guard one-wave-one-order types

Ensure WaveRuleMatchService one-wave-one-order logic keeps:

  • ticketType = 1

  • ticketType = 5

  • ticketType = 6

  • Step 4: Compile

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-api:compileGroovy :wms-wave:compileGroovy --no-problems-report

Task 10: Error Code and Response Contract Placeholder

Files:

  • Modify: ShopeeInboundApiHandler.groovy

  • Modify: C:\work\gitlab\shopee\wes-loghub\wms-api\src\main\groovy\com\ittx\wms\api\service\platform\shopee\ShopeeOutboundShipmentMappingService.groovy

  • Step 1: Keep retcode mapping as a placeholder

WES standard ResponseMessage is acceptable inside current sync API. Final mapping from WES errors to Shopee protocol retcode is not decided yet and must not be implemented in this phase.

Boundary adapter behavior for now:

[
    retcode: rsp.hasError() ? -1 : 0,
    message: rsp.hasError() ? sanitize(rsp.message) : 'success',
    data: rsp.data ?: [:]
]

Keep original WES error code/message in logs for later retcode mapping. Do not change ResponseMessage globally.

  • Step 2: Defer retry mapping

Do not create a retry table or classify business retcode values yet.

Only transport-level failures such as timeout, connection refused, and upstream unavailable use existing retry behavior. Business retcode retry rules stay pending until the final error-code mapping is confirmed.

Task 11: Verification and Documentation

Files:

  • Modify: C:\work\gitlab\shopee\docs\统一汇总.md

  • Modify or create: focused API implementation note under C:\work\gitlab\shopee\docs\

  • Step 1: Run WES compile

Run:

cd C:\work\gitlab\shopee\wes-loghub
.\gradlew :wms-api:compileGroovy :wms-wave:compileGroovy :wms-shipping:compileGroovy --no-problems-report
  • Step 2: Run edi-shopee compile

Run:

cd C:\work\gitlab\shopee\edi-shopee
.\gradlew compileGroovy
  • Step 3: Manual Shopee-facing smoke tests

Use one JSON body per implemented interface through edi-shopee, which then forwards to WES:

Invoke-RestMethod `
  -Method Post `
  -Uri 'http://localhost:9210/api/v2/automation/toshopee/outbound/salesorder/create_sub_picking_task' `
  -ContentType 'application/json' `
  -Headers @{ Authorization = '<valid-shopee-jwt>' } `
  -Body '<json body from protocol>'

Expected:

  • Response is Shopee shape with retcode = 0.

  • shipment_header has Shopee fields.

  • shipment_detail rows exist.

  • Duplicate request is idempotent.

  • Step 4: Manual 4.1-5.8 smoke tests

Trigger or post representative edi-shopee endpoints from each added module.

4.1 WMS-to-Vendor rack-transfer body:

Invoke-RestMethod `
  -Method Post `
  -Uri 'http://localhost:9210/internal/shopee/automation/rackTransfer/createRtOrder' `
  -ContentType 'application/json' `
  -Body '{
    "whs_id":"SGL",
    "rt_order_id":"RTSGL00020250102001",
    "attr_list":[{"attr_key":"urgent_flag","attr_value_id":"1","attr_value_name":"1"}],
    "ctime":1780000000,
    "operator":"xxx@shopee.com",
    "ticket_type":6,
    "sku_info_list":[{"sku_id":"18377383_9999","block_type":0,"qty":2,"sku_quality":0}]
  }'

Expected:

  • edi-shopee calls /api/v2/automation/tovendor/inventory/racktransfer/create_rt_order.
  • JWT header is present.
  • Shopee success maps to CBT success.
  • Retryable -10013099 remains retryable.

Vendor-to-WES samples:

Invoke-RestMethod `
  -Method Post `
  -Uri 'http://localhost:9210/api/v2/automation/toshopee/inventory/racktransfer/occupy_device' `
  -ContentType 'application/json' `
  -Headers @{ Authorization = '<valid-shopee-jwt>' } `
  -Body '<4.2 json body from protocol>'

Invoke-RestMethod `
  -Method Post `
  -Uri 'http://localhost:9210/api/v2/automation/toshopee/basic/user/query_user_exist' `
  -ContentType 'application/json' `
  -Headers @{ Authorization = '<valid-shopee-jwt>' } `
  -Body '<5.1 json body from protocol>'

WMS-to-Vendor basic-data samples:

Invoke-RestMethod `
  -Method Post `
  -Uri 'http://localhost:9210/internal/shopee/automation/basic/sku/syncSkuInfo.confirm' `
  -ContentType 'application/json' `
  -Body '<5.6 confirm json body>'

Invoke-RestMethod `
  -Method Post `
  -Uri 'http://localhost:9210/internal/shopee/automation/basic/sku/syncSkuInfo.get' `
  -ContentType 'application/json' `
  -Body '<5.6 get json body from protocol>'

Invoke-RestMethod `
  -Method Post `
  -Uri 'http://localhost:9210/internal/shopee/automation/basic/sku/syncSkuInfo.uploadStatus' `
  -ContentType 'application/json' `
  -Body '<5.6 uploadStatus json body>'
  • Step 5: Manual internal WES smoke tests

Use the WES sync API only as an internal verification target:

Invoke-RestMethod `
  -Method Post `
  -Uri 'http://localhost:9001/api/wms/api/sync/in?api=shopee.outbound.salesorder.createSubPickingTask&format=json' `
  -ContentType 'application/json' `
  -Body '<json body from protocol>'
  • Step 6: Manual wave tests

Check:

  • Low-water scheduler calls edi-shopee and saves shopee_wave.

  • Wave binding calls edi-shopee.

  • Failed bind marks kickOutWave.

  • Successful bind writes shipment_header.shopeeWave.

  • Step 7: Update docs

Update:

  • docs/统一汇总.md: move implemented items from "待实现" to "已落地".
  • Add a concise implementation note linking ShopeeInboundApiHandler, ShopeeWaveService, WaveRuleMatchService, and edi-shopee JWT service.

Self-Review

  • Spec coverage: Covers edi-shopee as Shopee-facing entry, WES field mapping, wave number request, wave binding, all concrete interfaces from 3.1-3.4, 4.1-4.7, and 5.1-5.8, JWT ownership in edi-shopee, and verification.
  • Scope control: Excludes inbound module interfaces; includes outbound, inventory/rack-transfer, and basic-data interfaces from this protocol file; Shopee extension fields do not add WES entity columns and are persisted through dictionary group types.
  • Type consistency: DTO names and handler method names match task references.
  • Confirmed entry contract: Shopee 调 edi-shopeeedi-shopee 再转发到 WES /api/wms/api/sync/in?api=shopee...WES 不直接作为 Shopee 外部入口。