第8章:常见问题
本文档整理了 WMS API 使用过程中的常见问题及解决方案。
8.1 认证相关
8.1.1 Q1: Token 获取失败,提示 "认证失败"
可能原因:
1. API Key 或 API Secret 错误
2. 请求头格式不正确
3. 环境不匹配(测试环境使用了生产环境的凭证)
解决方案:
1. 检查 API Key 和 API Secret 是否正确
2. 确认请求头格式:X-Api-Key 和 X-Api-Secret
3. 确认使用的环境与凭证匹配
4. 联系技术支持获取正确的凭证
8.1.2 Q2: Token 过期如何处理?
问题描述:
调用接口时返回 401 错误,提示 Token 过期。
解决方案:
1. Token 有效期为 24 小时(86400 秒)
2. 创建 Token 时,响应中会返回 expiresIn 字段(单位:秒),表示 Token 的有效期
3. 推荐方案:将 Token 缓存到 Redis 或本地缓存中
- 缓存过期时间 = expiresIn - 5分钟(例如:86400 - 300 = 86100秒)
- 当缓存过期时,自动重新获取 Token
4. 如果 Token 已过期(返回 401 错误),重新调用 Token 获取接口
示例代码逻辑(使用缓存):
// 示例代码 - 使用 Redis 缓存
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.concurrent.TimeUnit;
public class TokenService {
private static final String REDIS_KEY = "wms_api_token";
private static final int CACHE_BUFFER = 5 * 60; // 提前5分钟失效(300秒)
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private TokenApiClient tokenApiClient;
/**
* 获取 Token,优先从缓存获取,缓存不存在或过期时重新获取
*/
public String getToken() {
// 1. 尝试从缓存获取 Token
String token = redisTemplate.opsForValue().get(REDIS_KEY);
if (token != null && !token.isEmpty()) {
// 缓存中存在,直接返回
return token;
}
// 2. 缓存不存在或已过期,重新获取 Token
TokenResponse tokenResponse = tokenApiClient.getNewToken();
token = tokenResponse.getAccessToken();
Long expiresIn = tokenResponse.getExpiresIn();
// 3. 计算缓存过期时间(提前5分钟失效)
long cacheExpireTime = expiresIn - CACHE_BUFFER;
// 4. 将 Token 存入缓存,设置过期时间
redisTemplate.opsForValue().set(REDIS_KEY, token, cacheExpireTime, TimeUnit.SECONDS);
return token;
}
}
// 使用示例
@Autowired
private TokenService tokenService;
String token = tokenService.getToken();
// 使用 token 调用业务接口
8.1.3 Q3: 如何判断 Token 是否即将过期?
问题描述:
需要判断 Token 是否即将过期,以便提前刷新。
解决方案:
1. 使用缓存机制:将 Token 缓存到 Redis 或本地缓存中
2. 设置缓存过期时间:缓存过期时间 = expiresIn - 5分钟(例如:86400 - 300 = 86100秒)
3. 自动刷新:当缓存过期时,说明 Token 即将失效,自动重新获取 Token
4. 优势:无需手动计算剩余时间,利用缓存机制自动管理 Token 生命周期
示例代码(Redis 缓存):
// 示例代码
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.concurrent.TimeUnit;
public class TokenService {
private static final String REDIS_KEY = "wms_api_token";
private static final int CACHE_BUFFER = 5 * 60; // 提前5分钟失效(300秒)
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private TokenApiClient tokenApiClient;
/**
* 获取 Token,优先从缓存获取,缓存不存在或过期时重新获取
*/
public String getToken() {
// 1. 尝试从缓存获取 Token
String token = redisTemplate.opsForValue().get(REDIS_KEY);
if (token != null && !token.isEmpty()) {
// 缓存中存在,直接返回
return token;
}
// 2. 缓存不存在或已过期,重新获取 Token
TokenResponse tokenResponse = tokenApiClient.getNewToken();
token = tokenResponse.getAccessToken();
Long expiresIn = tokenResponse.getExpiresIn();
// 3. 计算缓存过期时间(提前5分钟失效)
long cacheExpireTime = expiresIn - CACHE_BUFFER;
// 4. 将 Token 存入缓存,设置过期时间
redisTemplate.opsForValue().set(REDIS_KEY, token, cacheExpireTime, TimeUnit.SECONDS);
return token;
}
}
// 使用示例
@Autowired
private TokenService tokenService;
String token = tokenService.getToken();
// 使用 token 调用业务接口
示例代码(本地缓存):
// 示例代码
import java.util.concurrent.atomic.AtomicLong;
public class TokenCacheService {
private static final int CACHE_BUFFER = 5 * 60; // 提前5分钟失效(300秒)
private String token;
private AtomicLong expireTime = new AtomicLong(0);
private final Object lock = new Object();
@Autowired
private TokenApiClient tokenApiClient;
/**
* 获取 Token,优先从本地缓存获取,缓存过期时重新获取
*/
public String getToken() {
long now = System.currentTimeMillis() / 1000; // 当前时间戳(秒)
// 1. 检查缓存是否有效
if (token != null && now < expireTime.get()) {
return token;
}
// 2. 缓存无效,重新获取 Token(加锁防止并发)
synchronized (lock) {
// 双重检查,避免重复获取
if (token != null && now < expireTime.get()) {
return token;
}
TokenResponse tokenResponse = tokenApiClient.getNewToken();
Long expiresIn = tokenResponse.getExpiresIn();
// 3. 计算缓存过期时间(提前5分钟失效)
long cacheExpireTime = now + expiresIn - CACHE_BUFFER;
// 4. 更新缓存
this.token = tokenResponse.getAccessToken();
this.expireTime.set(cacheExpireTime);
return this.token;
}
}
}
// 使用示例
@Autowired
private TokenCacheService tokenCacheService;
String token = tokenCacheService.getToken();
// 使用 token 调用业务接口
8.2 接口调用相关
8.2.1 Q4: 批量创建接口,部分成功部分失败如何处理?
问题描述:
调用批量创建接口(如创建商品、创建出库订单),返回的响应中部分成功部分失败。
解决方案:
1. 检查返回结果,查看每个条目的处理状态
2. 对于失败的条目,根据错误信息修正后重新提交
3. 建议分批提交,每批不超过 100 条
4. 实现重试机制,对失败的条目进行重试
8.2.2 Q5: 接口返回 400 错误,提示参数错误
可能原因:
1. 必填参数缺失
2. 参数格式不正确(如日期格式、枚举值)
3. 参数值超出范围(如数量为负数)
4. 参数长度超出限制
解决方案:
1. 检查请求参数是否完整
2. 确认参数格式是否符合要求(参考接口文档)
3. 检查参数值是否在有效范围内
4. 查看错误信息中的具体字段提示
常见错误:
- SKU 格式错误:仅支持大写字母、数字、下划线、横线,长度1-20
- 日期格式错误:应使用 MM/dd/yyyy 格式
- 枚举值错误:库存类型应为 1、2、3 等
8.2.3 Q6: 接口返回 500 错误,服务器内部错误
问题描述:
调用接口时返回 500 错误。
解决方案:
1. 500 错误通常是服务器端问题
2. 可以适当重试(建议实现指数退避)
3. 如果持续出现,联系技术支持
4. 检查请求参数是否异常(如数据量过大)
重试建议:
- 首次重试:等待 1 秒
- 第二次重试:等待 2 秒
- 第三次重试:等待 4 秒
- 最多重试 3 次
8.2.4 Q7: 分页查询如何获取所有数据?
问题描述:
需要获取所有数据,但接口只支持分页查询。
解决方案:
1. 从第一页开始查询(current=1)
2. 根据返回的 pagination.total 计算总页数
3. 循环调用接口,逐页获取数据
4. 直到获取完所有数据
示例逻辑:
// 示例代码
import java.util.ArrayList;
import java.util.List;
int current = 1;
List<Object> allData = new ArrayList<>();
boolean hasMore = true;
while (hasMore) {
Map<String, Object> response = queryData(current);
List<Object> resultList = (List<Object>) response.get("result");
allData.addAll(resultList);
Map<String, Object> pagination = (Map<String, Object>) response.get("pagination");
int totalPages = (int) Math.ceil((double) ((Long) pagination.get("total")) / ((Integer) pagination.get("pageSize")));
if (current >= totalPages) {
hasMore = false;
} else {
current++;
}
}
8.3 业务逻辑相关
8.3.1 Q8: 为什么出库订单更新失败,提示只能更新 Pending 或 Special 状态的订单?
问题描述:
尝试更新出库订单,但返回错误提示只能更新 Pending 或 Special 状态的订单。
解决方案:
1. 出库订单更新仅支持 Pending 和 Special 状态
2. 如果订单已进入其他状态(如 Working、Fulfiled),无法更新
3. 可以先取消订单,然后重新创建
4. 或者联系技术支持处理
订单状态流转:
Pending → Processing → Shipped → Delivered
↓
Cancelled
8.3.2 Q9: 创建入库预报时,SKU 不存在怎么办?
问题描述:
创建入库预报时,提示 SKU 不存在。
解决方案:
1. SKU 必须先在商品库中创建
2. 先调用商品创建接口创建商品
3. 然后再创建入库预报
4. 建议在创建入库预报前,先查询商品是否存在
正确流程:
1. 创建商品(POST /onixport/api/wms/product/create)
2. 创建入库预报(POST /onixport/api/wms/inbound/create)
8.3.3 Q10: 序列号格式要求是什么?
问题描述:
创建序列号预报时,序列号格式验证失败。
解决方案:
序列号格式要求:
1. 首字符:必须是大写字母(A-Z)
2. 后续字符:可以是大写字母(A-Z)、数字(0-9)、下划线(_)、横线(-)
3. 长度限制:最大 32 个字符
正确示例:
SN001234567890AUSD124254253223PROD-2024-001
错误示例:
sn001234567890(首字母必须大写)1234567890(首字符必须是字母)SN 001234567890(不能包含空格)
8.3.4 Q11: 库存查询返回的数据为什么和实际不一致?
问题描述:
查询库存时,返回的数据与实际库存不一致。
可能原因:
1. 数据同步延迟(通常不超过 5 分钟)
2. 查询条件不正确(如仓库编码、库存类型)
3. 库存被其他订单占用(frozenQty)
解决方案:
1. 等待几分钟后重新查询
2. 检查查询条件是否正确
3. 查看 frozenQty(占用库存)和 availableQty(可用库存)
4. 总库存 = 可用库存 + 占用库存
8.4 数据格式相关
8.4.1 Q12: 日期时间格式是什么?
问题描述:
接口中日期时间字段的格式要求。
解决方案:
1. 日期格式:MM/dd/yyyy(如:10/10/2025)
2. 时间格式:HH:mm:ss(如:17:00:00)
3. 日期时间格式:MM/dd/yyyy HH:mm:ss(如:10/10/2025 17:00:00)
4. 时区:所有时间均为仓库所在地时区
8.4.2 Q13: 数值精度要求是什么?
问题描述:
金额、重量等数值字段的精度要求。
解决方案:
1. 金额(declaredValue):USD,保留两位小数(如:29.99)
2. 重量(weight):Lbs(磅),支持小数(如:0.5)
3. 尺寸(dimensions):Inch(英寸),整数(如:6)
4. 数量(quantity):整数,必须大于 0
8.5 性能与限流相关
8.5.1 Q14: 接口调用频率有限制吗?
问题描述:
担心接口调用频率过高被限制。
解决方案:
1. 单个 API Key 的 QPS 限制:100 次/秒
2. 单个 IP 的 QPS 限制:200 次/秒
3. 超过限制会返回 429 错误
4. 建议实现请求队列和限流控制
建议:
- 批量操作优先使用批量接口
- 避免频繁轮询,使用合理的查询间隔
- 实现请求缓存机制
8.5.2 Q15: 接口响应时间一般是多少?
问题描述:
接口响应时间过长。
解决方案:
1. 正常响应时间:通常 < 1 秒
2. 批量操作:可能达到 3-5 秒
3. 超时设置:建议设置 30 秒超时
4. 如果超过 60 秒未响应,可以重试或联系技术支持
8.6 环境相关
8.6.1 Q16: 测试环境和生产环境的区别?
问题描述:
不清楚两个环境的区别和使用场景。
解决方案:
1. 测试环境(Sandbox):
- 用于开发调试
- 数据不影响正式业务
- 可能定期清理数据
- 性能可能较低
2. 生产环境(Production):
- 用于正式业务
- 数据真实有效
- 数据永久保存
- 生产级性能
建议:
- 开发阶段使用测试环境
- 验收通过后切换到生产环境
- 两个环境的凭证不互通
8.7 技术支持
8.7.1 Q17: 如何获取技术支持?
联系方式:
1. 技术支持邮箱:[待补充]
2. 技术支持电话:[待补充]
3. 在线支持:[待补充]
反馈内容:
- 问题描述
- 请求参数(脱敏处理)
- 错误信息
- 复现步骤
- 环境信息(测试/生产)
8.8 其他问题
8.8.1 Q18: 如何查看接口调用日志?
问题描述:
需要查看接口调用历史记录。
解决方案:
1. 目前系统不提供调用日志查询接口
2. 建议在调用方记录请求和响应日志
3. 如有需要,联系技术支持查询
8.8.2 Q19: 支持哪些编程语言?
问题描述:
是否有特定语言的 SDK。
解决方案:
1. 目前提供 RESTful API,支持所有支持 HTTP 的编程语言
2. 暂无官方 SDK,但可以基于 HTTP 客户端自行封装
3. 常见语言示例:
- Java:使用 OkHttp、HttpClient
- Python:使用 requests
- PHP:使用 cURL、Guzzle
- JavaScript:使用 axios、fetch
8.8.3 Q20: 数据安全如何保障?
问题描述:
担心数据传输和存储的安全性。
解决方案:
1. 传输安全:所有接口使用 HTTPS 加密传输
2. 认证安全:使用 Token 机制,Token 有时效性
3. 数据隔离:不同客户的数据完全隔离
4. 访问控制:基于 API Key 的访问控制
反馈与建议
如果您遇到的问题不在上述列表中,或者有改进建议,请联系技术支持团队。
我们会持续更新本文档,添加更多常见问题和解决方案。