洗车Api
    洗车Api
    • 洗车流程图
    • 接口安全
    • 账号接口
      • 账户余额
        GET
    • 城市地区接口
      • 全国所有城市
        GET
      • 获取城市包含的行政区县
        GET
    • 商品接口
      • 商品列表
        GET
      • 商品详情
        GET
      • 商品类目
        GET
    • 订单接口
      • 获取H5预约链接
        GET
      • 取消订单
        POST
      • 汽车服务下单
        POST
      • 订单详情
        GET
    • 门店接口
      • 门店列表
        GET
      • 获取门店详情
        GET
    • 数据模型
      • CreateWashCarOrderRequest
      • PageResponse«商品信息»
      • PageResponse«店铺信息»
      • Region
      • WashCarRefundOrderRequest
      • 商品信息
      • 套餐信息
      • 带商品信息的门店详情
      • 核销码信息
      • 渠道数据
      • 类目信息
      • 行政区划
      • 订单核销信息
      • 店铺信息
      • 订单信息

    接口安全

    接口概述#

    环境地址配置#

    测试环境#

    接口地址: https://dev.washcar-api.fxzb.vip
    用途: 开发测试和集成调试

    生产环境#

    接口地址: https://washcar-api.fxzb.vip
    用途: 正式业务环境

    HTTP协议配置要求#

    基础协议配置#

    传输协议: HTTPS (强制要求,确保数据传输安全)
    请求方法: POST (所有API接口统一使用POST方法)
    字符编码: UTF-8

    请求头配置#

    所有API请求必须包含以下HTTP请求头:

    服务端口和路径信息#

    默认服务端口: 8028
    API路径前缀: 根据具体接口而定
    健康检查: 服务提供标准的健康检查接口
    API文档: 集成Swagger文档,可通过 /swagger-ui.html 访问

    连接配置建议#

    连接超时: 建议设置为30秒
    读取超时: 建议设置为60秒
    重试机制: 建议实现指数退避重试策略
    连接池: 建议使用连接池提高性能

    安全规范#

    签名验证机制#

    必需的签名参数#

    所有需要签名验证的API请求必须包含以下四个签名参数,这些参数通过URL查询参数传递:

    1. appId (渠道标识)#

    参数名: appId
    类型: String
    必需: 是
    作用: 唯一标识第三方渠道,用于渠道身份验证和权限控制
    格式要求:
    由系统分配的唯一标识符
    通常为数字字符串,如 "10000"
    长度一般为4-10位
    获取方式: 通过渠道申请流程获得
    示例: appId=10000

    2. timestamp (时间戳)#

    参数名: timestamp
    类型: String
    必需: 是
    作用: 防止重放攻击,确保请求的时效性
    格式要求:
    Unix时间戳,精确到毫秒
    13位数字字符串
    服务端会验证时间戳的有效性(通常允许5分钟误差)
    生成方式: 当前时间的毫秒级时间戳
    示例: timestamp=1709545184000

    3. nonce (随机字符串)#

    参数名: nonce
    类型: String
    必需: 是
    作用: 防止重放攻击,确保每次请求的唯一性
    格式要求:
    随机生成的字符串
    建议长度8-32位
    可包含字母和数字
    同一时间戳下应保证唯一性
    生成方式: 使用安全的随机数生成器
    示例: nonce=Hs94gj28ka12

    4. sign (签名值)#

    参数名: sign
    类型: String
    必需: 是
    作用: 验证请求参数的完整性和真实性
    格式要求:
    32位小写十六进制字符串
    通过MD5算法生成
    基于所有业务参数和签名参数(除sign本身)计算
    生成方式: 按照签名算法计算得出
    示例: sign=2dsoeQW4ZEyA883lbexZUA==

    参数传递方式#

    签名参数必须作为URL查询参数传递,不能放在请求体中:

    签名验证流程#

    1.
    参数提取: 服务端从URL查询参数中提取appId、timestamp、nonce、sign
    2.
    渠道验证: 根据appId查询渠道信息,验证渠道状态和权限
    3.
    时效验证: 检查timestamp是否在允许的时间范围内
    4.
    签名计算: 使用相同算法重新计算签名值
    5.
    签名比对: 将计算结果与传入的sign参数进行比较
    6.
    验证结果: 签名一致则验证通过,否则返回签名验证失败错误

    签名生成算法#

    JSON参数扁平化规则#

    在进行签名计算之前,需要将JSON格式的请求参数转换为扁平的键值对结构。扁平化遵循以下规则:

    1. 嵌套对象处理(使用点号连接)#

    嵌套的JSON对象使用点号(.)连接父键和子键:
    原始JSON:
    {
        "user": {
            "name": "张三",
            "profile": {
                "age": 25,
                "city": "北京"
            }
        }
    }
    扁平化结果:
    user.name=张三
    user.profile.age=25
    user.profile.city=北京

    2. 数组元素处理(使用索引标记)#

    数组元素使用方括号包围的索引进行标记,索引从0开始:
    原始JSON:
    {
        "tags": ["洗车", "保养", "美容"],
        "services": [
            {"id": "S001", "name": "标准洗车"},
            {"id": "S002", "name": "精致洗车"}
        ]
    }
    扁平化结果:
    tags[0]=洗车
    tags[1]=保养
    tags[2]=美容
    services[0].id=S001
    services[0].name=标准洗车
    services[1].id=S002
    services[1].name=精致洗车

    3. 复杂嵌套结构处理#

    对于包含多层嵌套对象和数组的复杂结构,按照上述规则递归处理:
    原始JSON:
    {
        "order": {
            "id": "ORD001",
            "items": [
                {
                    "goods": {
                        "id": "G001",
                        "name": "标准洗车套餐",
                        "options": ["内饰清洁", "轮胎护理"]
                    },
                    "quantity": 1
                },
                {
                    "goods": {
                        "id": "G002", 
                        "name": "高级美容套餐",
                        "options": ["打蜡", "镀膜"]
                    },
                    "quantity": 2
                }
            ],
            "customer": {
                "mobile": "13800138000",
                "address": {
                    "province": "北京市",
                    "city": "北京市",
                    "district": "朝阳区"
                }
            }
        }
    }
    扁平化结果:
    order.id=ORD001
    order.items[0].goods.id=G001
    order.items[0].goods.name=标准洗车套餐
    order.items[0].goods.options[0]=内饰清洁
    order.items[0].goods.options[1]=轮胎护理
    order.items[0].quantity=1
    order.items[1].goods.id=G002
    order.items[1].goods.name=高级美容套餐
    order.items[1].goods.options[0]=打蜡
    order.items[1].goods.options[1]=镀膜
    order.items[1].quantity=2
    order.customer.mobile=13800138000
    order.customer.address.province=北京市
    order.customer.address.city=北京市
    order.customer.address.district=朝阳区

    4. 特殊值处理#

    null值: 不参与签名计算,直接过滤掉
    空字符串: 不参与签名计算,直接过滤掉
    空对象{}: 不参与签名计算,直接过滤掉
    空数组[]: 不参与签名计算,直接过滤掉
    布尔值: 转换为字符串 "true" 或 "false"
    数字: 转换为字符串形式

    5. 扁平化算法实现示例#

    基于项目中的实际实现,扁平化算法的核心逻辑如下:

    参数排序和字符串拼接#

    1. 参数排序规则(ASCII字典序)#

    将扁平化后的所有参数(包括业务参数和签名参数,但不包括sign本身)按照参数名进行ASCII字典序排序:
    排序依据: 参数名的ASCII码值
    排序方向: 从小到大(升序)
    大小写敏感: 参数名区分大小写
    排序范围: 包括所有非空参数
    ASCII字典序排序示例:
    原始参数: timestamp, nonce, appId, mobile, goodsId
    排序后: appId, goodsId, mobile, nonce, timestamp
    特殊字符排序:
    数字 < 大写字母 < 小写字母 < 特殊符号
    0-9 < A-Z < a-z < [, ., 等

    2. 键值对拼接格式#

    按照排序后的参数顺序,使用以下格式进行拼接:
    拼接格式: key1=value1&key2=value2&key3=value3
    拼接规则:
    键值对之间使用等号(=)连接
    多个键值对之间使用与号(&)连接
    参数名和参数值都不进行URL编码
    保持原始的字符串格式
    示例:
    appId=10000&channelOrderNo=CH202403041234567&cityCode=110100&goodsId=G001&goodsItemId=GI001&mobile=13800138000&nonce=Hs94gj28ka12&timestamp=1709545184000

    3. 空值参数过滤规则#

    在进行参数排序和拼接之前,需要过滤掉以下类型的参数:
    过滤条件:
    null值: 参数值为null的参数
    空字符串: 参数值为""的参数
    空白字符串: 仅包含空格、制表符等空白字符的参数
    sign参数: 签名参数本身不参与签名计算
    过滤逻辑:

    4. 完整的字符串拼接算法#

    步骤1: 参数收集和扁平化
    步骤2: 参数扁平化和排序
    步骤3: 字符串拼接
    步骤4: 添加密钥

    MD5计算实现#

    1. UTF-8字符编码要求#

    MD5计算必须使用UTF-8字符编码,确保中文字符和特殊字符的正确处理:
    编码要求:
    字符集: UTF-8
    编码方式: 无BOM(Byte Order Mark)
    适用范围: 整个待签名字符串
    重要性: 不同编码会产生不同的MD5值,必须保持一致
    编码示例:

    2. MD5哈希计算的具体步骤#

    步骤1: 字符串编码转换
    步骤2: MD5算法计算
    步骤3: 字节数组转十六进制字符串

    3. 签名结果格式要求#

    格式规范:
    长度: 固定32位字符
    字符集: 小写十六进制字符(0-9, a-f)
    前导零: 必须补齐,不能省略
    大小写: 统一使用小写字母
    正确格式示例:
    2dsoeqw4zeya883lbexzua12  ✓ (32位小写十六进制)
    错误格式示例:
    2DSOEQW4ZEYA883LBEXZUA12  ✗ (大写字母)
    2dsoeqw4zeya883lbexzua    ✗ (长度不足32位)
    2dsoeQW4ZEyA883lbexZUA==  ✗ (包含大写字母和特殊字符)

    4. 完整的MD5计算实现#

    基于项目实际实现的完整MD5计算方法:

    5. 签名计算完整流程#

    完整示例:
    调试输出:
    项目中的签名计算会输出详细的调试信息:
    这有助于开发者调试签名计算过程中的问题。

    完整签名生成示例#

    示例场景#

    假设要调用订单创建接口,接口地址为 https://washcar-api.fxzb.vip/api/order/create

    签名参数#

    appId=10000
    nonce=Hs94gj28ka12
    timestamp=1709545184000

    业务参数(请求体)#

    {
        "channelOrderNo": "CH202403041234567",
        "mobile": "13800138000", 
        "goodsId": "G001",
        "goodsItemId": "GI001",
        "cityCode": "110100"
    }

    渠道密钥#

    secret=HKKA4sj81FakwFk9

    签名计算步骤#

    第一步:参数收集和扁平化
    收集所有参数(URL参数 + 请求体参数),并移除sign参数:
    appId=10000
    nonce=Hs94gj28ka12  
    timestamp=1709545184000
    channelOrderNo=CH202403041234567
    mobile=13800138000
    goodsId=G001
    goodsItemId=GI001
    cityCode=110100
    第二步:参数排序
    按照参数名ASCII字典序排序:
    appId=10000
    channelOrderNo=CH202403041234567
    cityCode=110100
    goodsId=G001
    goodsItemId=GI001
    mobile=13800138000
    nonce=Hs94gj28ka12
    timestamp=1709545184000
    第三步:字符串拼接
    按照 key=value&key=value 格式拼接:
    stringA = "appId=10000&channelOrderNo=CH202403041234567&cityCode=110100&goodsId=G001&goodsItemId=GI001&mobile=13800138000&nonce=Hs94gj28ka12&timestamp=1709545184000"
    第四步:添加密钥
    在字符串末尾直接拼接渠道密钥:
    stringSign = stringA + secret
    stringSign = "appId=10000&channelOrderNo=CH202403041234567&cityCode=110100&goodsId=G001&goodsItemId=GI001&mobile=13800138000&nonce=Hs94gj28ka12&timestamp=1709545184000HKKA4sj81FakwFk9"
    第五步:MD5计算
    使用UTF-8编码对拼接字符串进行MD5计算:

    最终请求#

    完整的API请求:

    复杂参数示例#

    对于包含嵌套对象和数组的复杂参数:
    原始请求体:
    {
        "order": {
            "items": [
                {"id": "item1", "quantity": 2},
                {"id": "item2", "quantity": 1}
            ],
            "customer": {
                "name": "张三",
                "contact": {
                    "mobile": "13800138000",
                    "email": "zhangsan@example.com"
                }
            }
        }
    }
    扁平化后的参数:
    appId=10000
    nonce=Hs94gj28ka12
    order.customer.contact.email=zhangsan@example.com
    order.customer.contact.mobile=13800138000
    order.customer.name=张三
    order.items[0].id=item1
    order.items[0].quantity=2
    order.items[1].id=item2
    order.items[1].quantity=1
    timestamp=1709545184000
    拼接字符串:
    stringA = "appId=10000&nonce=Hs94gj28ka12&order.customer.contact.email=zhangsan@example.com&order.customer.contact.mobile=13800138000&order.customer.name=张三&order.items[0].id=item1&order.items[0].quantity=2&order.items[1].id=item2&order.items[1].quantity=1&timestamp=1709545184000"

    重要注意事项#

    1.
    参数完整性: 所有非空参数都必须参与签名计算
    2.
    编码一致性: 必须使用UTF-8编码进行MD5计算
    3.
    排序准确性: 严格按照ASCII字典序排序
    4.
    密钥安全性: 渠道密钥必须妥善保管,不能泄露
    5.
    时效性验证: 服务端会验证timestamp的有效性
    6.
    调试支持: 可通过日志查看详细的签名计算过程

    渠道认证管理#

    渠道申请和配置指南#

    渠道申请流程#

    1. 申请渠道接入#

    申请条件:
    具备合法的企业资质或个人开发者身份
    有明确的业务需求和使用场景
    同意遵守API使用规范和服务条款
    申请方式:
    联系方式: 通过官方客服或商务渠道提交申请
    申请材料:
    企业营业执照或个人身份证明
    业务需求说明书
    技术对接联系人信息
    预期调用量和业务规模说明
    审核流程:
    1.
    资质审核: 验证申请方的合法资质和业务真实性
    2.
    需求评估: 评估业务需求的合理性和技术可行性
    3.
    风险评估: 评估潜在的安全风险和业务风险
    4.
    审核结果: 通常在3-5个工作日内给出审核结果

    2. 获取appId和secret密钥#

    appId分配:
    格式: 数字字符串,通常为4-10位
    唯一性: 每个渠道分配唯一的appId标识
    示例: 10000, 10001, wx123456789
    用途: 用于标识渠道身份,在所有API请求中必须携带
    secret密钥分配:
    格式: 随机生成的字符串,包含字母和数字
    长度: 通常为16-32位字符
    示例: HKKA4sj81FakwFk9, Abc123Def456Ghi789
    用途: 用于签名计算,确保请求的安全性和完整性
    安全要求:
    必须妥善保管,不得泄露给第三方
    建议定期更换(每6-12个月)
    不得在客户端代码中硬编码
    密钥获取方式:
    初始分配: 审核通过后,通过安全渠道(如加密邮件)提供
    密钥重置: 如需重置密钥,需要通过身份验证后重新分配
    备用密钥: 支持配置备用密钥,便于密钥轮换时的平滑切换

    3. 渠道配置信息#

    基础配置:
    {
        "appId": "10000",
        "secret": "HKKA4sj81FakwFk9",
        "channelName": "示例渠道",
        "status": "active",
        "createTime": "2024-03-01T10:00:00Z",
        "expireTime": "2025-03-01T10:00:00Z"
    }
    配置参数说明:
    appId: 渠道标识符,用于API调用时的身份识别
    secret: 签名密钥,用于生成请求签名
    channelName: 渠道名称,便于管理和识别
    status: 渠道状态,影响API访问权限
    createTime: 渠道创建时间
    expireTime: 渠道过期时间,过期后需要续期

    渠道状态管理#

    1. 渠道状态类型#

    active(激活状态):
    描述: 渠道正常激活,可以正常调用所有授权的API接口
    API访问: 完全开放,所有接口均可正常访问
    业务影响: 无影响,业务正常运行
    状态转换: 可转换为suspended或disabled状态
    suspended(暂停状态):
    描述: 渠道临时暂停,通常用于维护或临时限制
    API访问: 部分限制,只能访问基础查询接口
    业务影响: 新订单创建等核心业务功能暂停
    状态转换: 可恢复为active状态或转为disabled状态
    常见原因:
    系统维护期间
    账户余额不足
    临时业务调整
    disabled(禁用状态):
    描述: 渠道被禁用,无法访问任何API接口
    API访问: 完全禁止,所有请求都会被拒绝
    业务影响: 所有业务功能停止
    状态转换: 需要重新审核才能恢复
    常见原因:
    违反服务条款
    安全风险
    合同到期
    主动申请停用
    expired(过期状态):
    描述: 渠道授权已过期,需要续期
    API访问: 禁止访问,返回授权过期错误
    业务影响: 所有功能停止,需要续期后恢复
    状态转换: 续期后可恢复为active状态

    2. 渠道状态对API访问的影响#

    状态检查机制:
    不同状态的API响应:
    active状态:
    {
        "code": "200",
        "message": "success",
        "data": {
            // 正常的业务数据
        }
    }
    suspended状态:
    {
        "code": "403",
        "message": "渠道已暂停,请联系管理员",
        "detail": "Channel is suspended"
    }
    disabled状态:
    {
        "code": "403", 
        "message": "该渠道已被禁用",
        "detail": "Channel is disabled"
    }
    expired状态:
    {
        "code": "401",
        "message": "渠道授权已过期,请续期",
        "detail": "Channel authorization expired"
    }

    3. 状态变更通知#

    通知机制:
    邮件通知: 状态变更时自动发送邮件通知
    API回调: 支持配置回调URL,状态变更时主动通知
    管理后台: 可在管理后台实时查看状态变更历史
    通知内容:
    {
        "appId": "10000",
        "oldStatus": "active",
        "newStatus": "suspended", 
        "changeTime": "2024-03-15T14:30:00Z",
        "reason": "账户余额不足",
        "operator": "system"
    }

    渠道参数传递方式#

    1. URL查询参数传递(推荐方式)#

    传递格式:
    优势:
    标准化: 符合HTTP标准,易于理解和实现
    缓存友好: 便于HTTP缓存和代理服务器处理
    调试方便: 参数在URL中可见,便于调试和日志记录
    框架支持: 所有HTTP客户端和服务端框架都支持
    注意事项:
    URL长度限制:大部分服务器支持的URL长度为2048字符
    参数可见性:查询参数在日志中可见,但sign参数已经过加密处理
    编码处理:特殊字符需要进行URL编码

    2. 请求头传递(备选方式)#

    传递格式:
    使用场景:
    URL长度受限的环境
    需要隐藏签名参数的场景
    特殊的网络环境或代理要求
    实现注意:
    需要服务端额外支持请求头解析
    客户端需要正确设置自定义请求头
    某些网络环境可能过滤自定义请求头

    3. 混合传递方式#

    场景说明:
    在某些特殊情况下,可以采用混合方式传递参数:
    适用场景:
    部分参数有特殊传递要求
    兼容不同版本的客户端实现
    特定的安全策略要求

    4. 参数传递最佳实践#

    推荐做法:
    1.
    统一使用URL查询参数: 保持一致性,便于维护
    2.
    参数顺序: 建议按照appId、timestamp、nonce、sign的顺序排列
    3.
    编码处理: 对特殊字符进行正确的URL编码
    4.
    参数验证: 客户端发送前进行参数格式验证
    错误处理:
    客户端实现示例:

    渠道配置验证#

    1. 配置有效性检查#

    基础验证:
    appId格式: 验证appId是否符合规定格式
    密钥有效性: 验证secret是否正确且未过期
    权限范围: 验证渠道是否有权限访问特定接口
    调用频率: 验证是否超出调用频率限制
    验证接口:
    验证响应:
    {
        "code": "200",
        "message": "验证成功",
        "data": {
            "appId": "10000",
            "status": "active",
            "permissions": ["order.create", "order.query", "goods.list"],
            "rateLimit": {
                "maxRequestsPerMinute": 1000,
                "currentUsage": 45
            }
        }
    }

    2. 配置问题排查#

    常见配置问题:
    问题1: appId不存在
    {
        "code": "404",
        "message": "未查询到渠道,请稍后再试",
        "solution": "检查appId是否正确,或联系管理员确认渠道状态"
    }
    问题2: 渠道被禁用
    {
        "code": "403",
        "message": "该渠道已被禁用", 
        "solution": "联系管理员了解禁用原因并申请恢复"
    }
    问题3: 权限不足
    {
        "code": "403",
        "message": "渠道接口已过期",
        "solution": "检查ChannelApi配置或申请接口权限续期"
    }
    排查步骤:
    1.
    验证appId: 确认appId是否正确且存在
    2.
    检查状态: 确认渠道状态是否为active
    3.
    验证权限: 确认是否有访问特定接口的权限
    4.
    检查配置: 验证ChannelApi配置是否正确
    5.
    联系支持: 如问题持续,联系技术支持

    密钥使用和权限验证#

    密钥在签名中的使用方式#

    1. 密钥获取优先级#

    系统采用分层的密钥管理机制,按照以下优先级获取签名密钥:
    优先级顺序:
    1.
    ChannelApi密钥 (最高优先级)
    2.
    Channel基础密钥 (默认密钥)
    密钥选择逻辑:

    2. ChannelApi配置的优先级规则#

    ChannelApi配置说明:
    用途: 为特定API类型提供专用的密钥和权限配置
    优势: 支持细粒度的权限控制和密钥隔离
    适用场景:
    不同业务线需要独立的密钥管理
    需要对特定API进行单独的权限控制
    密钥轮换时的平滑过渡
    配置结构:
    优先级规则详解:
    情况1: 仅有Channel配置
    情况2: 同时存在Channel和ChannelApi配置
    情况3: ChannelApi配置存在但状态异常

    3. 密钥在签名计算中的使用#

    签名字符串构建:
    密钥拼接方式:
    位置: 密钥直接拼接在参数字符串的末尾
    分隔符: 无分隔符,直接拼接
    格式: 参数字符串 + 密钥
    示例:
    参数字符串: "appId=10000&mobile=13800138000&nonce=abc123&timestamp=1709545184000"
    密钥: "HKKA4sj81FakwFk9"
    最终字符串: "appId=10000&mobile=13800138000&nonce=abc123&timestamp=1709545184000HKKA4sj81FakwFk9"

    4. 密钥安全使用规范#

    密钥存储安全:
    服务端存储: 密钥在服务端数据库中加密存储
    客户端存储:
    不得在前端JavaScript代码中硬编码
    服务端应用中应使用环境变量或配置文件
    配置文件应设置适当的文件权限
    密钥传输安全:
    HTTPS传输: 所有包含密钥的通信必须使用HTTPS
    日志安全: 密钥不得出现在日志文件中
    调试信息: 调试时应对密钥进行脱敏处理
    密钥轮换策略:
    定期轮换: 建议每6-12个月更换一次密钥
    应急轮换: 发现密钥泄露时立即更换
    平滑过渡: 使用ChannelApi配置实现新旧密钥的平滑切换

    渠道验证失败的排查方法#

    1. 常见验证失败场景#

    场景1: 渠道不存在
    排查方法:
    1.
    验证appId: 确认传入的appId是否正确
    2.
    检查数据库: 直接查询数据库确认渠道是否存在
    3.
    清理缓存: 清理ChannelService中的缓存数据
    4.
    检查网络: 确认与后端服务的网络连接正常
    场景2: 渠道被禁用
    排查方法:
    1.
    检查渠道状态: 确认渠道的isNull和isDeleted字段
    2.
    查看禁用原因: 联系管理员了解禁用的具体原因
    3.
    申请恢复: 根据禁用原因采取相应的恢复措施
    4.
    业务调整: 如无法恢复,考虑申请新的渠道
    场景3: ChannelApi权限过期
    排查方法:
    1.
    检查ChannelApi状态: 确认apiStatus字段的值
    2.
    查看过期时间: 检查ChannelApi的expireTime字段
    3.
    申请续期: 联系管理员申请ChannelApi权限续期
    4.
    降级使用: 临时使用Channel基础密钥(如果允许)
    场景4: 签名验证失败

    2. 签名验证失败排查步骤#

    步骤1: 参数检查
    步骤2: 密钥验证
    步骤3: 参数收集验证
    步骤4: 签名计算对比

    3. 调试工具和方法#

    日志调试:
    调试信息包含:
    签名字符串: 完整的待签名字符串(包含密钥)
    签名结果: 最终的MD5签名值
    参数详情: 扁平化后的所有参数
    客户端调试建议:
    常用调试工具:
    Postman: 用于API请求测试和参数验证
    在线MD5工具: 验证MD5计算结果
    JSON格式化工具: 检查JSON参数格式
    URL编码工具: 检查参数编码问题

    4. 问题解决方案#

    解决方案1: 参数问题
    解决方案2: 时间戳问题
    解决方案3: 编码问题
    解决方案4: 缓存问题

    权限验证机制#

    1. 多层权限验证#

    验证层级:
    1.
    渠道基础验证: 验证渠道是否存在且状态正常
    2.
    ChannelApi权限验证: 验证特定API的访问权限
    3.
    接口级权限验证: 验证对特定接口的访问权限
    4.
    业务级权限验证: 验证业务操作的权限
    验证流程:

    2. 权限配置管理#

    权限配置结构:
    权限检查实现:

    3. 权限异常处理#

    权限不足异常:
    异常响应格式:
    {
        "code": "403",
        "message": "权限不足",
        "detail": "渠道 10000 缺少权限: order.create",
        "requiredPermission": "order.create",
        "availablePermissions": ["order.query", "goods.list"]
    }

    错误处理指南#

    标准错误响应格式#

    统一响应结构#

    所有API接口都采用统一的响应格式,包括成功和错误响应。基于项目中的ApiResponse类,标准响应结构如下:
    {
        "code": 200,
        "message": "success",
        "data": {
            // 业务数据(成功时返回)
        }
    }

    响应字段说明#

    字段名类型必需说明
    codeint是响应状态码,200表示成功,其他值表示错误
    messagestring是响应消息,成功时为"success",错误时为具体错误信息
    dataobject否业务数据,仅在成功时返回,错误时可能为null

    错误响应分类#

    基于项目中的异常处理机制,错误响应按照以下类型进行分类:

    1. 签名验证错误 (SignException)#

    错误码: 101
    触发场景: 请求签名验证失败
    异常类: cn.xsoft.washcar.exception.SignException
    响应格式:
    {
        "code": 101,
        "message": "签名不匹配",
        "data": null
    }
    常见原因:
    签名计算错误
    参数缺失或顺序错误
    密钥不正确
    字符编码问题

    2. 参数验证错误 (ArgumentsException)#

    错误码: 301
    触发场景: 请求参数格式错误或缺失
    异常类: cn.xsoft.exceptions.ArgumentsException
    响应格式:
    {
        "code": 301,
        "message": "参数(fieldName)错误:具体错误信息",
        "data": null
    }
    示例:
    {
        "code": 301,
        "message": "参数(appId)错误:渠道appId不存在",
        "data": null
    }
    常见参数错误:
    appId: 渠道标识不存在或格式错误
    timestamp: 时间戳格式错误或超出有效范围
    nonce: 随机字符串缺失
    sign: 签名参数缺失
    cityCode: 城市编码格式错误
    goodsId: 商品ID不存在或已下架
    orderNo: 订单号不存在

    3. 方法参数验证错误 (MethodArgumentNotValidException)#

    错误码: 301
    触发场景: Spring Validation注解验证失败
    异常类: org.springframework.web.bind.MethodArgumentNotValidException
    响应格式:
    {
        "code": 301,
        "message": "参数(fieldName)错误:验证失败信息",
        "data": null
    }
    示例:
    {
        "code": 301,
        "message": "参数(mobile)错误:手机号格式不正确",
        "data": null
    }

    4. 非法参数错误 (IllegalArgumentException)#

    错误码: 300
    触发场景: 参数值不符合业务规则
    异常类: java.lang.IllegalArgumentException
    响应格式:
    {
        "code": 300,
        "message": "参数错误 详细:具体错误描述",
        "data": null
    }

    5. 业务逻辑错误 (ServiceException)#

    错误码: 503
    触发场景: 业务逻辑处理失败
    异常类: cn.xsoft.exceptions.ServiceException
    响应格式:
    {
        "code": 503,
        "message": "接口异常 详细:具体业务错误信息",
        "data": null
    }
    常见业务错误:
    订单不存在或状态异常
    商品不存在或已下架
    核销码无效
    预约链接获取失败
    城市编码格式错误

    6. 系统运行时错误 (RuntimeException)#

    错误码: 501
    触发场景: 系统内部运行时异常
    异常类: java.lang.RuntimeException
    响应格式:
    {
        "code": 501,
        "message": "内部异常 详细:系统错误描述",
        "data": null
    }

    7. Thrift服务错误 (TException)#

    错误码: 502 或 具体业务错误码
    触发场景: 后端Thrift服务调用异常
    异常类: org.apache.thrift.TException
    响应格式:
    {
        "code": 502,
        "message": "未处理异常 详细:Thrift服务错误信息",
        "data": null
    }
    特殊情况 - 如果是已知的ThriftServiceException:
    {
        "code": 具体业务错误码,
        "message": "具体业务错误信息",
        "data": null
    }

    错误码规范#

    错误码分类#

    错误码范围错误类型说明
    200成功请求处理成功
    101签名错误签名验证失败
    300-399参数错误请求参数相关错误
    500-599系统错误服务端内部错误

    具体错误码定义#

    错误码错误类型说明
    200SUCCESS请求成功
    101SIGN_ERROR签名验证失败
    300ILLEGAL_ARGUMENT非法参数
    301INVALID_PARAMETER参数验证失败
    501RUNTIME_ERROR运行时异常
    502THRIFT_ERRORThrift服务异常
    503SERVICE_ERROR业务逻辑异常

    错误信息格式规范#

    参数错误信息格式#

    格式: 参数({参数名})错误:{具体错误描述}
    示例:
    参数(appId)错误:渠道appId不存在
    参数(goodsId)错误:商品不存在或已下架
    参数(cityCode)错误:城市编码有误
    参数(orderNo)错误:订单不存在

    业务错误信息格式#

    格式: 接口异常 详细:{具体业务错误描述}
    示例:
    接口异常 详细:未查询到对应核销码
    接口异常 详细:订单不存在或手机号为空
    接口异常 详细:获取预约链接失败,请稍候重试

    系统错误信息格式#

    格式: 内部异常 详细:{系统错误描述}
    示例:
    内部异常 详细:数据库连接超时
    内部异常 详细:JSON解析失败

    HTTP状态码说明#

    所有API响应都使用HTTP 200状态码,具体的业务状态通过响应体中的code字段来表示。这种设计的优势:
    1.
    统一处理: 客户端可以统一处理所有响应
    2.
    业务清晰: 业务状态码与HTTP状态码分离
    3.
    调试友好: 所有响应都能正常返回,便于调试
    HTTP状态码使用:
    200 OK: 所有正常响应(包括业务错误)
    500 Internal Server Error: 仅在Thrift服务异常时使用

    错误响应示例#

    成功响应示例#

    {
        "code": 200,
        "message": "success",
        "data": {
            "orderId": "ORD202403041234567",
            "orderStatus": "CREATED",
            "createTime": "2024-03-04T12:34:56Z"
        }
    }

    签名验证失败#

    {
        "code": 101,
        "message": "签名不匹配",
        "data": null
    }

    渠道不存在#

    {
        "code": 301,
        "message": "参数(channelId)错误:未查询到渠道,请稍后再试",
        "data": null
    }

    渠道被禁用#

    {
        "code": 301,
        "message": "参数(channelId)错误:该渠道已被禁用",
        "data": null
    }

    渠道接口过期#

    {
        "code": 301,
        "message": "参数(channelApiAuth)错误:渠道接口已过期",
        "data": null
    }

    商品不存在#

    {
        "code": 301,
        "message": "参数(goodsId)错误:商品不存在或已下架",
        "data": null
    }

    订单创建失败#

    {
        "code": 503,
        "message": "接口异常 详细:商品库存不足,无法创建订单",
        "data": null
    }

    系统内部错误#

    {
        "code": 501,
        "message": "内部异常 详细:数据库连接超时",
        "data": null
    }

    常见错误类型和解决方案#

    签名验证失败错误处理#

    错误类型1: 签名不匹配#

    错误响应:
    {
        "code": 101,
        "message": "签名不匹配",
        "data": null
    }
    可能原因:
    1.
    参数收集不完整: 缺少URL参数或请求体参数
    2.
    参数排序错误: 未按ASCII字典序排序
    3.
    密钥错误: 使用了错误的渠道密钥
    4.
    字符编码问题: 未使用UTF-8编码进行MD5计算
    5.
    参数扁平化错误: JSON嵌套结构处理不正确
    6.
    时间戳格式错误: 时间戳格式不符合要求
    排查步骤:
    步骤1: 验证必需参数
    确认以下参数是否存在且格式正确:
    appId: 渠道标识,数字字符串
    timestamp: 13位毫秒级时间戳
    nonce: 随机字符串,8-32位
    sign: 32位小写十六进制字符串
    步骤2: 验证参数收集
    步骤3: 验证参数排序
    步骤4: 验证字符串拼接
    步骤5: 验证MD5计算
    解决方案:
    1.
    参数完整性检查:
    2.
    正确的排序和拼接:
    3.
    正确的MD5计算:

    错误类型2: 时间戳超出有效范围#

    错误响应:
    {
        "code": 301,
        "message": "参数(timestamp)错误:请求时间戳超出有效范围",
        "data": null
    }
    可能原因:
    客户端时间与服务器时间差异过大
    时间戳格式错误(秒级vs毫秒级)
    网络延迟导致请求超时
    解决方案:

    渠道验证失败错误处理#

    错误类型1: 渠道不存在#

    错误响应:
    {
        "code": 301,
        "message": "参数(channelId)错误:未查询到渠道,请稍后再试",
        "data": null
    }
    可能原因:
    appId参数错误或不存在
    渠道数据未正确同步
    缓存问题
    排查步骤:
    1.
    验证appId格式:
    2.
    测试渠道查询接口:
    解决方案:
    确认appId是否正确
    联系管理员检查渠道配置
    清理缓存重新获取渠道信息

    错误类型2: 渠道被禁用#

    错误响应:
    {
        "code": 301,
        "message": "参数(channelId)错误:该渠道已被禁用",
        "data": null
    }
    可能原因:
    渠道状态被设置为禁用
    渠道违反服务条款
    合同到期或账户异常
    解决方案:
    1.
    联系管理员: 了解禁用原因和恢复条件
    2.
    检查合同状态: 确认服务合同是否有效
    3.
    申请恢复: 根据禁用原因提交恢复申请

    错误类型3: 渠道接口过期#

    错误响应:
    {
        "code": 301,
        "message": "参数(channelApiAuth)错误:渠道接口已过期",
        "data": null
    }
    可能原因:
    ChannelApi配置过期
    API权限到期
    特定接口类型权限被撤销
    解决方案:
    1.
    检查ChannelApi配置:
    2.
    申请权限续期: 联系管理员续期API权限
    3.
    降级使用: 临时使用Channel基础权限(如果允许)

    参数格式错误处理#

    错误类型1: 必需参数缺失#

    错误响应:
    {
        "code": 301,
        "message": "参数(mobile)错误:手机号不能为空",
        "data": null
    }
    常见缺失参数:
    mobile: 用户手机号
    goodsId: 商品ID
    cityCode: 城市编码
    channelOrderNo: 渠道订单号
    解决方案:

    错误类型2: 参数格式错误#

    错误响应:
    {
        "code": 301,
        "message": "参数(mobile)错误:手机号格式不正确",
        "data": null
    }
    常见格式错误:
    手机号格式不正确
    城市编码格式错误
    订单号格式不符合规范
    解决方案:

    错误类型3: 业务规则验证失败#

    错误响应:
    {
        "code": 301,
        "message": "参数(goodsId)错误:商品不存在或已下架",
        "data": null
    }
    常见业务验证错误:
    商品不存在或已下架
    套餐ID不存在或已删除
    城市编码不在服务范围内
    订单号已存在
    解决方案:
    1.
    商品状态检查:
    2.
    城市服务范围检查:

    常见问题排查步骤#

    问题排查工具#

    1. 签名计算验证工具
    2. 网络请求调试
    3. 参数编码检查

    系统性排查流程#

    第一步: 基础连通性检查
    第二步: 渠道配置验证
    第三步: 参数格式验证
    第四步: 签名计算验证
    第五步: 完整请求测试

    错误日志分析#

    服务端日志关键信息:
    [签名]
    签名字符串:appId=10000&channelOrderNo=CH202403041234567&cityCode=110100&goodsId=G001&goodsItemId=GI001&mobile=13800138000&nonce=Hs94gj28ka12&timestamp=1709545184000HKKA4sj81FakwFk9
    签名结果:a1b2c3d4e5f6789012345678901234ab
    客户端调试输出:
    === 签名计算调试 ===
    1. 原始参数: {...}
    2. 排序后的键: [...]
    3. 参数字符串: ...
    4. 签名字符串: ...
    5. 计算签名: ...
    通过对比服务端和客户端的签名计算过程,可以快速定位签名不匹配的具体原因。
    修改于 2026-01-01 21:01:55
    上一页
    洗车流程图
    下一页
    账户余额
    Built with