第5章:返回规范

5.1 统一返回结构

所有 API 返回均采用统一的 JSON 格式,包含以下字段:

字段名类型必填说明
successBoolean请求是否成功,true 表示成功,false 表示失败
errorCodeInteger错误码。当 success=true 时,该字段为 null;当 success=false 时,该字段有值(详见错误码说明错误码索引
errorMsgString错误信息。当 success=true 时,该字段为 null;当 success=false 时,该字段有值
resultObject/Array实际返回数据内容,不同接口结构不同
paginationObject分页信息,仅在分页查询接口中返回

成功响应示例:

{
  "success": true,
  "errorCode": null,
  "errorMsg": null,
  "result": {
    "orderNo": "OUT202411010001",
    "status": "CREATED"
  }
}

失败响应示例:

{
  "success": false,
  "errorCode": 401,
  "errorMsg": "用户认证失败",
  "result": null
}

5.2 成功返回示例

5.2.1 无数据返回(操作成功)

{
  "success": true,
  "errorCode": null,
  "errorMsg": null,
  "result": null
}

说明:

  • success = true 表示业务处理成功
  • errorCode = nullerrorMsg = null 表示请求成功,无错误信息
  • result = null 表示无返回数据(常见于创建、更新、删除等操作)

5.2.2 单个对象返回

{
  "success": true,
  "errorCode": null,
  "errorMsg": null,
  "result": {
    "skuCode": "SKU123456",
    "skuName": "iPhone 15 Case",
    "createdTime": "2025-11-07 10:00:00"
  }
}

说明:

  • success = true 表示业务处理成功
  • errorCode = nullerrorMsg = null 表示请求成功,无错误信息
  • result 字段返回接口的实际业务数据对象

5.2.3 数组返回

{
  "success": true,
  "errorCode": null,
  "errorMsg": null,
  "result": [
    {
      "skuCode": "SKU123456",
      "skuName": "iPhone 15 Case"
    },
    {
      "skuCode": "SKU123457",
      "skuName": "iPhone 15 Pro Case"
    }
  ]
}

说明:

  • success = true 表示业务处理成功
  • errorCode = nullerrorMsg = null 表示请求成功,无错误信息
  • result 字段返回数组类型的数据

5.3 失败返回示例

5.3.1 参数校验失败

{
  "success": false,
  "errorCode": 1000,
  "errorMsg": "无效的参数",
  "result": null
}

5.3.2 业务逻辑错误

{
  "success": false,
  "errorCode": 2003,
  "errorMsg": "当前的数据不支持此操作",
  "result": null
}

5.3.3 系统错误

{
  "success": false,
  "errorCode": 500,
  "errorMsg": "服务器错误",
  "result": null
}

说明:

  • success = false 表示请求处理失败
  • errorCode 为错误码,用于区分不同的错误类型
  • errorMsg 为错误信息描述
  • result = null 表示请求失败,无返回数据

5.4 分页返回结构

当接口支持分页返回时,会在响应中包含 pagination 字段,result 字段为数组类型。

分页字段说明

字段名类型说明
currentInteger当前页码,从 1 开始
pageSizeInteger每页条数
totalLong数据总条数

分页返回示例

{
  "success": true,
  "errorCode": null,
  "errorMsg": null,
  "result": [
    {
      "orderNo": "OUT202411010001",
      "status": "CREATED"
    },
    {
      "orderNo": "OUT202411010002",
      "status": "SHIPPED"
    }
  ],
  "pagination": {
    "current": 1,
    "pageSize": 20,
    "total": 125
  }
}

说明:

  • result 为当前页的数据列表
  • pagination.current 表示当前页码
  • pagination.pageSize 表示每页条数
  • pagination.total 表示数据总条数

5.5 字段命名规范

1. 统一采用驼峰命名(如:orderNo, createTime)

2. 所有日期时间字段统一使用:

  • 格式:MM/dd/yyyy HH:mm:ss
  • 时区:America/Los_Angeles(洛杉矶时区)

3. 金额类字段统一以 USD 为单位,保留两位小数

5.6 响应状态判断

客户端应根据 success 字段判断请求是否成功:

  • success = true:请求成功,可以读取 result 字段获取业务数据
  • success = false:请求失败,应读取 errorCodeerrorMsg 字段获取错误信息

5.7 错误码说明

本章节定义 WMS 对外 API 的系统级错误码,用于标识框架、认证或系统相关问题。


5.7.1 系统级错误码

说明:以下为当前已定义的系统级错误码,错误码列表将持续补充完善。

错误码HTTP Code错误描述可能原因解决建议
200200请求处理成功--
401401用户认证失败Token 无效或过期重新获取 Token
403403权限不足无访问权限联系管理员确认权限
405405验证码校验失败验证码错误重新输入验证码
500200服务器错误后端服务异常稍后重试或联系技术支持
1000200无效的参数必填参数为空或格式不符检查请求参数是否完整、格式正确
1006200不支持的请求参数请求参数格式错误检查请求参数格式
1007200不支持的请求类型HTTP 方法错误检查请求方法(GET/POST/PUT/DELETE)
1012200第三方初始化失败第三方服务初始化异常联系技术支持
2003200当前的数据不支持此操作数据状态不允许当前操作检查数据状态

注意:错误码列表将持续更新,如遇到未列出的错误码,请查看 errorMsg 字段获取详细错误信息,或联系技术支持。


5.7.2 错误响应示例

示例1:参数校验失败

{
  "success": false,
  "errorCode": 1000,
  "errorMsg": "无效的参数",
  "result": null
}

示例2:认证失败

{
  "success": false,
  "errorCode": 401,
  "errorMsg": "用户认证失败",
  "result": null
}

示例3:数据状态错误

{
  "success": false,
  "errorCode": 2003,
  "errorMsg": "当前的数据不支持此操作",
  "result": null
}

示例4:系统错误

{
  "success": false,
  "errorCode": 500,
  "errorMsg": "服务器错误",
  "result": null
}

5.7.3 错误处理最佳实践

5.7.3.1 错误分类处理

重要说明:只有 500 错误码可以重试,其他所有错误码都不可以重试。

500 服务器错误(可以重试)

错误码:500

处理策略

  • 可以重试:服务器临时错误,可以适当重试
  • 指数退避:实现指数退避重试策略
  • 限制重试次数:最多重试 3 次
  • 记录日志:记录重试日志,便于排查问题

重试策略

  • 第1次重试:等待 1 秒
  • 第2次重试:等待 2 秒
  • 第3次重试:等待 4 秒

示例处理代码(Java)

import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.TimeUnit;

public class ApiClient {
    
    private static final int MAX_RETRIES = 3;
    private RestTemplate restTemplate;
    
    /**
     * 发送请求,仅对 500 错误进行重试
     */
    public Map<String, Object> sendRequestWithRetry(String url, Object request) {
        int retryCount = 0;
        
        while (retryCount <= MAX_RETRIES) {
            try {
                ResponseEntity<Map> response = restTemplate.postForEntity(
                    url, request, Map.class);
                
                Map<String, Object> body = response.getBody();
                
                // 如果成功,直接返回
                if (body != null && Boolean.TRUE.equals(body.get("success"))) {
                    return body;
                }
                
                // 只有 500 错误码可以重试
                if (body != null && body.get("errorCode") != null && (Integer) body.get("errorCode") == 500) {
                    if (retryCount < MAX_RETRIES) {
                        // 指数退避:1秒、2秒、4秒
                        long waitTime = (long) Math.pow(2, retryCount);
                        Thread.sleep(waitTime * 1000);
                        retryCount++;
                        continue;
                    }
                }
                
                // 其他错误码或重试次数已用完,直接返回
                return body;
                
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("请求被中断", e);
            } catch (Exception e) {
                // 网络异常等,如果是最后一次重试,抛出异常
                if (retryCount >= MAX_RETRIES) {
                    throw new RuntimeException("请求失败,已重试 " + MAX_RETRIES + " 次", e);
                }
                // 指数退避后重试
                try {
                    long waitTime = (long) Math.pow(2, retryCount);
                    Thread.sleep(waitTime * 1000);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("请求被中断", ie);
                }
                retryCount++;
            }
        }
        
        throw new RuntimeException("请求失败,已重试 " + MAX_RETRIES + " 次");
    }
}

其他错误码(不可重试)

错误码范围:除 500 外的所有错误码(包括 400、401、403、404、429、1000、1006、1007、1012、2003 等)

处理策略

  • 不可重试:这些错误通常是参数问题、权限问题、业务逻辑问题或限流问题,重试不会解决问题
  • 检查请求参数:仔细检查请求参数是否符合要求
  • 检查认证信息:对于 401 错误,确认 Token 是否有效
  • 检查权限:对于 403 错误,确认账号是否有访问权限
  • 降低请求频率:对于 429 错误,降低请求频率,实现客户端限流
  • 修正后重新请求:修正问题后重新发送请求(不是自动重试)

常见错误码

  • 400 / 1000 - 无效的参数:检查请求参数是否完整、格式正确
  • 401 - 用户认证失败:重新获取 Token
  • 403 - 权限不足:联系管理员确认权限
  • 404 - 资源不存在:检查请求的资源路径是否正确
  • 429 - 请求频率过高:降低请求频率,实现客户端限流
  • 1006 - 不支持的请求参数:检查请求参数格式
  • 1007 - 不支持的请求类型:检查请求方法(GET/POST/PUT/DELETE)
  • 1012 - 第三方初始化失败:联系技术支持
  • 2003 - 当前的数据不支持此操作:检查数据状态

示例处理代码(Java)

public void handleError(Map<String, Object> response) {
    if (response == null || Boolean.TRUE.equals(response.get("success"))) {
        return;
    }
    
    Integer errorCode = (Integer) response.get("errorCode");
    String errorMsg = (String) response.get("errorMsg");
    
    // 只有 500 错误可以重试,其他错误都不可以重试
    if (errorCode != null && errorCode == 500) {
        // 500 错误会在重试逻辑中处理
        return;
    }
    
    // 其他错误码,根据错误类型进行处理,但不自动重试
    switch (errorCode) {
        case 401:
            // Token 过期,重新获取 Token(不是重试原请求)
            refreshToken();
            break;
        case 403:
            // 权限不足,记录日志并通知管理员
            log.error("权限不足: {}", errorMsg);
            notifyAdmin("权限不足,请联系管理员");
            break;
        case 429:
            // 限流错误,降低请求频率
            log.warn("请求频率过高: {}", errorMsg);
            reduceRequestRate();
            break;
        case 1000:
        case 1006:
        case 1007:
            // 参数错误,检查并修正参数(不是重试)
            log.error("参数错误 [{}]: {}", errorCode, errorMsg);
            validateAndFixParameters();
            break;
        default:
            // 其他错误,记录日志
            log.error("请求失败 [{}]: {}", errorCode, errorMsg);
            break;
    }
    
    // 注意:这里不进行自动重试,需要人工介入或修正问题后重新请求
}

5.7.3.2 业务错误处理

批量操作部分失败

场景:批量创建商品、批量创建出库订单等

处理策略

1. 检查返回结果,查看每个条目的处理状态

2. 对于失败的条目,根据错误信息修正后重新提交

3. 建议分批提交,每批不超过 100 条

4. 实现重试机制,对失败的条目进行重试

示例响应

{
  "success": true,
  "errorCode": null,
  "errorMsg": null,
  "result": {
    "successResultList": [
      {
        "orderNo": "OUT202411010001",
        "referenceNo": "ORDER-001",
        "success": true,
        "errorCode": null,
        "errorMsg": null
      }
    ],
    "failedResultList": [
      {
        "orderNo": null,
        "referenceNo": "ORDER-002",
        "success": false,
        "errorCode": 1000,
        "errorMsg": "SKU不存在"
      }
    ]
  }
}

处理代码(Java)

import java.util.ArrayList;
import java.util.List;

public class BatchOperationHandler {
    
    /**
     * 处理批量操作结果,提取失败的条目
     */
    public void handleBatchResult(Map<String, Object> response, 
                                   List<Map<String, Object>> originalRequest) {
        List<Map<String, Object>> successList = (List<Map<String, Object>>) response.get("successResultList");
        List<Map<String, Object>> failedList = (List<Map<String, Object>>) response.get("failedResultList");
        
        // 处理成功的条目
        if (successList != null && !successList.isEmpty()) {
            for (Map<String, Object> successItem : successList) {
                // success 字段为 true,errorCode 和 errorMsg 为 null
                if (Boolean.TRUE.equals(successItem.get("success"))) {
                // 保存成功的订单信息
                    saveSuccessOrder((String) successItem.get("orderNo"), (String) successItem.get("referenceNo"));
                }
            }
        }
        
        // 处理失败的条目
        if (failedList != null && !failedList.isEmpty()) {
            List<FailedItem> failedItems = new ArrayList<>();
            
            for (Map<String, Object> failedItem : failedList) {
                // success 字段为 false,errorCode 和 errorMsg 有值
                if (Boolean.FALSE.equals(failedItem.get("success"))) {
                FailedItem item = new FailedItem();
                    item.setReferenceNo((String) failedItem.get("referenceNo"));
                    item.setErrorCode((Integer) failedItem.get("errorCode"));
                    item.setErrorMsg((String) failedItem.get("errorMsg"));
                failedItems.add(item);
                }
            }
            
            // 记录失败信息,后续人工处理或修正后重新提交
            logFailedItems(failedItems);
            
            // 注意:这里不进行自动重试,需要根据错误信息修正后重新提交
            // 例如:如果是参数错误,修正参数后重新调用接口
        }
    }
    
    private void saveSuccessOrder(String orderNo, String referenceNo) {
        // 保存成功的订单信息
    }
    
    private void logFailedItems(List<FailedItem> failedItems) {
        // 记录失败信息,便于后续处理
        for (FailedItem item : failedItems) {
            log.error("订单创建失败 - referenceNo: {}, errorCode: {}, errorMsg: {}", 
                item.getReferenceNo(), item.getErrorCode(), item.getErrorMsg());
        }
    }
    
    // 内部类:失败的条目信息
    private static class FailedItem {
        private String referenceNo;
        private Integer errorCode;
        private String errorMsg;
        
        // Getters and Setters
        public String getReferenceNo() { return referenceNo; }
        public void setReferenceNo(String referenceNo) { this.referenceNo = referenceNo; }
        public Integer getErrorCode() { return errorCode; }
        public void setErrorCode(Integer errorCode) { this.errorCode = errorCode; }
        public String getErrorMsg() { return errorMsg; }
        public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; }
    }
}

5.7.3.3 错误日志记录

建议记录的信息

1. 请求信息

  • 请求 URL
  • 请求方法
  • 请求参数(脱敏处理)
  • 请求时间

2. 响应信息

  • 响应状态码
  • 错误码(errorCode)
  • 错误信息(errorMsg)
  • 响应时间

3. 环境信息

  • 使用的环境(测试/生产)
  • API Key(脱敏处理)

示例日志格式

[2025-11-13 10:00:00] ERROR
Request: POST /onixport/api/wms/product/create
Status: 400
ErrorCode: 1000
ErrorMsg: 无效的参数
RequestId: abc123
Environment: Production

5.7.3.4 错误监控与告警

建议实现

1. 错误率监控:监控接口错误率,超过阈值时告警

2. 错误分类统计:统计不同错误码的出现频率

3. 响应时间监控:监控接口响应时间,异常时告警

4. 限流监控:监控 429 错误,及时调整请求频率


5.7.4 调试建议

1. 检查请求参数:确保必填参数完整、格式正确

2. 查看错误码:根据 errorCode 定位错误类型

3. 查看错误信息errorMsg 字段提供详细的错误描述

4. 检查认证信息:对于 401 错误,检查 Token 是否有效

5. 检查权限:对于 403 错误,确认账号是否有访问权限

6. 重试机制:对于 5xx 错误,建议加入指数退避重试策略

7. 联系支持:如问题持续存在,可联系技术支持并提供请求详情