103 lines
8.3 KiB
Markdown
103 lines
8.3 KiB
Markdown
# Integration 本地领星 WMS 查询
|
||
|
||
本入口用于在现有 Java 服务内验证 WMS 只读调用。仅返回单页出库摘要,不启动月度采集、库存全量同步、定时任务或数据库写入。业务方案见[领星一件代发数据统计一期方案](../../docs/自营业务/领星一件代发数据统计一期方案.md)。
|
||
|
||
后续开发交接见[接口联调手册](../../docs/自营业务/领星WMS接口联调手册.md):已另行用只读 HTTP 验证仓库发现、一件代发详情和按仓产品库存全分页。此处现有 Java 接口仍只有出库摘要;库存内部接口、分仓任务和持久化尚待开发,不能以外部直连成功推定 Java 功能已经具备。
|
||
|
||
## 凭据与开关
|
||
|
||
从 WMS 管理后台的“个人中心 → API 信息 → 自研系统”获取 `appKey` 和 `appSecret`,填写本目录 Git 忽略的 `.env.local`,格式参考 [.env.example](.env.example)。这两个值分别加载为 `LINGXING_OMS_APP_KEY` 和 `LINGXING_OMS_APP_SECRET` 进程环境变量,不提交真实值。Spring 本身不会自动读取 `.env.local`,应在启动 Java 的同一终端加载。该凭据属于 OMP 服务商内部系统对接,不使用 OMS 货主或 WMS 智能设备协议。
|
||
|
||
正式应用配置默认 `LINGXING_WMS_ENABLED=false`。下面的本地命令显式启用适配器,为该本地实例生成内部调用令牌。不要复用共享环境令牌。
|
||
|
||
测试部署通过 `deploy/docker-compose.yml` 将 `LINGXING_OMS_APP_KEY`、`LINGXING_OMS_APP_SECRET` 传入 Integration 容器。现有 Jenkins 下游部署任务读取 `firstleg-test-env` Secret File;可将这两项加入该文件,或在实际执行部署的下游服务任务中以凭据绑定注入同名环境变量。只在上游总控任务设置变量不会自动传给下游任务。变量命名采用用户指定的 `OMS`,接口协议仍为当前 WMS 自研系统(OMP);真实凭据是否有该接口权限仍待联调核验。不要将密钥作为普通构建参数或打印完整 Compose 配置。
|
||
|
||
2026-10-12 用户确认唯一账号、所有源时间按北京时间、数量按 SKU 件数。Lastleg 应用配置使用 `source-account=lingxing-primary`(内部稳定标识,不是 appKey)、`source-query-timezone=Asia/Shanghai`、`quantity-unit=PCS`,分仓配置的源时区默认 `Asia/Shanghai`;数量仍保留十进制精度。仓库编码须在获得真实仓库目录后配置到 `baiyue.self-operated-analytics.warehouses`,不预填虚构仓库。`SELFOP_EXECUTION_ENABLED` 和 `SELFOP_SALES_PUBLICATION_ENABLED` 均默认关闭。
|
||
|
||
## 本地启动
|
||
|
||
先在后端仓库根目录执行构建,包含相关模块的编译与测试:
|
||
|
||
```powershell
|
||
mvn -pl integration-service -am package
|
||
```
|
||
|
||
然后在同一目录的 PowerShell 中执行。只加载明确允许的两个名称,不执行环境文件内的代码:
|
||
|
||
```powershell
|
||
$credentialNames = @('LINGXING_OMS_APP_KEY', 'LINGXING_OMS_APP_SECRET')
|
||
foreach ($line in [IO.File]::ReadAllLines((Join-Path (Get-Location) 'integration-service/.env.local'))) {
|
||
if ($line -match '^\s*(LINGXING_OMS_APP_KEY|LINGXING_OMS_APP_SECRET)\s*=(.*)$') {
|
||
$credentialName = $matches[1]
|
||
$credentialValue = $matches[2].Trim()
|
||
if ($credentialValue.Length -ge 2 -and
|
||
(($credentialValue.StartsWith('"') -and $credentialValue.EndsWith('"')) -or
|
||
($credentialValue.StartsWith("'") -and $credentialValue.EndsWith("'")))) {
|
||
$credentialValue = $credentialValue.Substring(1, $credentialValue.Length - 2)
|
||
}
|
||
[Environment]::SetEnvironmentVariable($credentialName, $credentialValue, 'Process')
|
||
}
|
||
}
|
||
foreach ($credentialName in $credentialNames) {
|
||
if ([string]::IsNullOrWhiteSpace([Environment]::GetEnvironmentVariable($credentialName, 'Process'))) {
|
||
throw "Missing credential: $credentialName"
|
||
}
|
||
}
|
||
$env:INTERNAL_SERVICE_TOKEN = [guid]::NewGuid().ToString()
|
||
$localInternalToken = $env:INTERNAL_SERVICE_TOKEN
|
||
$env:INTEGRATION_LOCAL_PORT = '19210'
|
||
$buildJar = (Resolve-Path 'integration-service/target/integration-service-1.0.0-SNAPSHOT.jar').Path
|
||
$localJava = Start-Process -FilePath (Get-Command java).Source -WindowStyle Hidden -PassThru `
|
||
-ArgumentList @('-jar', ('"' + $buildJar + '"'),
|
||
'--spring.config.location=classpath:/application-lingxing-local.yml',
|
||
'--baiyue.integration.lingxing-wms.enabled=true') `
|
||
-RedirectStandardOutput 'integration-service/target/lingxing-local.stdout.log' `
|
||
-RedirectStandardError 'integration-service/target/lingxing-local.stderr.log'
|
||
```
|
||
|
||
该配置仅监听 `127.0.0.1`,关闭 Nacos Config、Discovery、服务注册及 Spring 定时任务,不导入共享环境配置。若端口占用,选择其他本地端口并同步替换后续 URL,不停止未知进程。确认 `http://127.0.0.1:19210/actuator/health` 返回 `UP` 后再查询。
|
||
|
||
## 读取一条出库单
|
||
|
||
在保留上述变量的同一 PowerShell 中执行一次:
|
||
|
||
```powershell
|
||
$businessZone = [TimeZoneInfo]::FindSystemTimeZoneById('China Standard Time')
|
||
$queryEnd = [TimeZoneInfo]::ConvertTimeFromUtc([DateTime]::UtcNow, $businessZone)
|
||
$query = @{
|
||
startTime = $queryEnd.AddMonths(-1).ToString('yyyy-MM-ddTHH:mm:ss')
|
||
endTime = $queryEnd.ToString('yyyy-MM-ddTHH:mm:ss')
|
||
current = 1
|
||
size = 1
|
||
} | ConvertTo-Json -Compress
|
||
$result = Invoke-RestMethod -Method Post `
|
||
-Uri 'http://127.0.0.1:19210/internal/integrations/lingxing/wms/outbound-orders/query' `
|
||
-Headers @{'X-Internal-Service-Token' = $localInternalToken} `
|
||
-ContentType 'application/json' -Body $query
|
||
$result | ConvertTo-Json -Depth 6
|
||
```
|
||
|
||
返回摘要只包含出库单号、来源单号、货主编码、仓库编码、状态、创建时间和出库时间,不返回收件人或地址。业务 `code="200"` 且 `records` 包含一条才表示已读到样本;空页表示调用成功但未取得样本;`B0001`/`S0503` 表示明确失败,保留 `errorCode`、来源数值错误码及请求标识排查,不能当作零销量。
|
||
|
||
当前调用 OMP `POST /openapi/v2/order/delivery/page`,固定 `timeType=outboundTime`、`status=100` 已出库,窗口最多向前一个自然月。该本地保护不代表供应商历史上限;单页样本也不代表整月采集完成。订单详情、全量库存、增量任务和统计落库仍按业务方案后续实现。
|
||
|
||
测试完毕,在同一终端停止本次启动的进程,并清理当前终端注入的变量:
|
||
|
||
```powershell
|
||
if (-not $localJava.HasExited) { Stop-Process -Id $localJava.Id }
|
||
foreach ($credentialName in @('LINGXING_OMS_APP_KEY', 'LINGXING_OMS_APP_SECRET', 'INTERNAL_SERVICE_TOKEN')) {
|
||
[Environment]::SetEnvironmentVariable($credentialName, $null, 'Process')
|
||
}
|
||
$localInternalToken = $null
|
||
$credentialValue = $null
|
||
```
|
||
|
||
## 接口与验证
|
||
|
||
- 2026-10-12 已通过本地 Java 实例调用 OMP WMS 出库分页,按最近 1 个月、`current=1`、`size=1` 成功返回 1 条已出库摘要;无内部令牌请求被拒绝。验证后本地进程已停止。该结果仅证明当前只读链路可用,不代表全量采集或库存同步完成。
|
||
- 实测成功响应为数值 `code=200`,但缺少官方示例及 Schema 中的 `success`。客户端以成功码和有效分页结构共同判定;`success` 出现时仍须为布尔 `true`。此差异已加入回归测试,失败响应不转换为空页。
|
||
- 内部前缀为 `POST /internal/integrations/lingxing/wms`,提供 `/outbound-orders/query`、`/outbound-orders/locate`、`/outbound-orders/detail`、`/warehouses/query`、`/product-inventory/query`;仅接受 `X-Internal-Service-Token`,不经公网 Gateway。各请求只返回一页或一单,多页与业务版本归 Lastleg 编排。定位不传时间/状态;产品库存必须指定仓库、保留全部品质和零库存。源时间原值与 BigDecimal 数量未知值保留,不推断时区或单位。
|
||
- [OpenAPI](src/main/resources/openapi/lingxing-wms-v1.yaml)定义输入、分页摘要和失败结构。
|
||
- 从后端根执行 `mvn -pl integration-service -am test` 与 `node --test scripts/gateway-route-contract.test.mjs`。
|
||
- 本地模拟 HTTP 单测验证签名、请求体、敏感信息隔离和错误处理;不能代替真实领星样本验证。官方入口为[服务商内部系统对接](https://apidoc-omp.xlwms.com/)与[一件代发出库单 WMS 分页查询](https://apidoc-omp.xlwms.com/reference/post_openapi-v2-order-delivery-page-1)。
|