鼎熠智控-物联网管理系统文档

搜索文档

搜索文档内容

API文档

常见问题

Q1: 签名验证失败怎么办?

排查步骤

  1. 检查时间戳

    • 确保时间戳在当前时间的 ±300 秒(5分钟)内
    • 检查服务器时间是否准确同步
    • 使用 NTP 同步系统时间
  2. 检查参数顺序

    • 确保所有参数按字母顺序排序
    • 确保排除 signature 参数
    • 参数名大小写要完全匹配
  3. 检查密钥

    • 确认 SecretKey 正确无误
    • 检查是否有前导或后缀空格
    • 确认使用的是正确环境的密钥
  4. 检查编码

    • 确保使用 UTF-8 编码
    • 检查特殊字符是否正确转义
    • 确认签名字符串构造正确

示例修复

# 问题:密钥包含空格
SECRET_KEY = " sk_abc123 "  # 错误
SECRET_KEY = "sk_abc123"    # 正确

# 问题:参数顺序错误
params = "b=2&a=1"       # 错误
params = "a=1&b=2"       # 正确(按字母顺序)

# 问题:时间戳过期
timestamp = "1234567890"  # 错误(太旧)
timestamp = str(int(time.time()))  # 正确(当前时间)

调试技巧

def debug_signature(method, path, params, secret_key):
    # 打印签名字符串
    sorted_params = "&".join(
        f"{'{'}k{'}'}={'{'}v{'}'}" for k, v in sorted(params.items())
        if k != 'signature'
    )
    sign_str = f"{'{'}method{'}'}{'{'}path{'}'}{'{'}params['timestamp']{'}'}{'{'}params['nonce']{'}'}{'{'}sorted_params{'}'}"
    print(f"签名字符串: {'{'}sign_str{'}'}")

    # 生成签名
    signature = hmac.new(
        secret_key.encode(),
        sign_str.encode(),
        hashlib.sha256
    ).hexdigest()

    print(f"生成签名: {'{'}signature{'}'}")
    return signature

Q2: 请求频率超限怎么解决?

解决方案

  1. 降低请求频率

    • 合理规划请求时间
    • 批量操作改为分批处理
    • 增加请求间隔
  2. 实现指数退避重试

    • 使用 2^n 秒间隔重试
    • 添加随机抖动避免同时重试
    • 设置最大重试次数
  3. 联系管理员调整限流配置

    • 说明业务需求
    • 申请更高的频率限制
    • 考虑使用多个 AccessKey 分散请求

重试示例

import time
import random

def api_request_with_backoff(request_func, max_retries=3):
    """使用指数退避策略重试"""
    for attempt in range(max_retries):
        try:
            return request_func()
        except APIError as e:
            if e.code == 429:  # 频率限制
                if attempt == max_retries - 1:
                    raise  # 最后一次尝试失败,抛出异常

                # 指数退避: 2^n + 随机抖动
                delay = (2 ** attempt) + random.uniform(0, 1)
                print(f"频率限制,等待 {'{'}delay:.2f{'}'} 秒后重试...")
                time.sleep(delay)
            else:
                raise  # 其他错误直接抛出

# 使用示例
result = api_request_with_backoff(lambda: client.get_card_info("CARD123"))

监控告警

建议设置以下监控指标:

  • 429 错误出现频率
  • 请求成功率
  • 平均响应时间
  • 重试次数统计

Q3: 卡板状态不允许操作怎么办?

状态说明

不同操作对卡板状态有要求:

操作 允许的状态 不允许的状态
复机 停机(4) 已激活(2)、风险停机(5)、销户(6)
停机 已激活(2) 停机(4)、销户(6)
订购套餐 已激活(2) 其他所有状态
查询信息 所有状态 无限制

状态检查

def check_card_status(card_info, operation):
    """检查卡板状态是否允许操作"""
    status = card_info.get('status')

    status_map = {'{'}
        1: "待激活",
        2: "已激活",
        3: "机卡分离",
        4: "停机",
        5: "风险停机",
        6: "销户",
        7: "库存",
        8: "其他",
        9: "可测试",
        10: "达量断网"
    {'}'}

    status_name = status_map.get(status, "未知状态")
    print(f"卡板状态: {'{'}status{'}'} ({'{'}status_name{'}'})")

    # 检查操作权限
    allowed_operations = {'{'}
        'restart': [4],           # 复机:仅停机状态
        'stop': [2],              # 停机:仅已激活状态
        'order': [2],             # 订购:仅已激活状态
        'query': list(range(1, 11))  # 查询:所有状态
    {'}'}

    if status in allowed_operations.get(operation, []):
        return True
    else:
        print(f"当前状态 ({'{'}status_name{'}'}) 不允许执行 {'{'}operation{'}'} 操作")
        return False

# 使用示例
card_info = client.get_card_info("CARD123")
if check_card_status(card_info, 'stop'):
    client.stop_card("CARD123")

处理建议

  • 待激活状态:先激活卡板
  • 停机状态:需要先复机才能使用
  • 风险停机:联系管理员人工处理
  • 销户状态:无法恢复,需重新开户

Q4: 如何调试签名问题?

调试工具

创建一个调试函数,对比客户端和服务器的签名计算过程:

def debug_signature_detailed(method, path, params, secret_key):
    """详细调试签名生成过程"""
    print("=== 签名调试信息 ===")

    # 1. 显示原始参数
    print("\n1. 原始参数:")
    for k, v in params.items():
        print(f"   {'{'}k{'}'} = {'{'}v{'}'}")

    # 2. 排除 signature 并排序
    print("\n2. 排序后参数(排除signature):")
    sorted_params = []
    for k in sorted(params.keys()):
        if k != 'signature':
            sorted_params.append(f"{'{'}k{'}'}={'{'}params[k]{'}'}")
            print(f"   {'{'}k{'}'} = {'{'}params[k]{'}'}")

    # 3. 构造参数字符串
    param_str = "&".join(sorted_params)
    print(f"\n3. 参数字符串:\n   {'{'}param_str{'}'}")

    # 4. 构造签名字符串
    sign_str = f"{'{'}method{'}'}{'{'}path{'}'}{'{'}params['timestamp']{'}'}{'{'}params['nonce']{'}'}{'{'}param_str{'}'}"
    print(f"\n4. 签名字符串:\n   {'{'}sign_str{'}'}")

    # 5. 生成签名
    signature = hmac.new(
        secret_key.encode('utf-8'),
        sign_str.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    print(f"\n5. 生成签名:\n   {'{'}signature{'}'}")

    # 6. 对比
    if 'signature' in params:
        print(f"\n6. 参数中的签名:\n   {'{'}params['signature']{'}'}")
        print(f"\n7. 签名匹配: {'{'}signature == params['signature']{'}'}")

    print("\n===================")
    return signature

在线调试

可以使用在线工具验证 HMAC-SHA256 签名:

将签名字符串和密钥输入,对比生成的签名是否一致。


Q5: 如何验证环境配置?

验证清单

在正式使用 API 前,建议验证以下项目:

  • 确认 Base URL 正确(包括协议和端口)
  • 验证网络连通性(能否访问服务器)
  • 检查 AccessKey 和 SecretKey 是否正确
  • 确认服务器时间同步(时间偏差 < 5分钟)
  • 测试心跳接口是否正常

验证脚本

#!/bin/bash

echo "=== 天城智控环境验证 ==="
echo ""

# 配置信息
BASE_URL="http://your-api-server.com"
ACCESS_KEY="ak_xxxxxxxxxx"
SECRET_KEY="sk_xxxxxxxxxx"

# 1. 网络连通性测试
echo "1. 网络连通性测试..."
if curl -I "$BASE_URL/api/v1/heartbeat" \
     --connect-timeout 10 \
     --max-time 30 \
     -s -o /dev/null -w "%{'{'}http_code{'}'}" | grep -q "200"; then
    echo "   ✅ 网络连接正常"
else
    echo "   ❌ 网络连接失败"
    exit 1
fi
echo ""

# 2. 时间同步检查
echo "2. 时间同步检查..."
LOCAL_TIME=$(date +%s)
echo "   本地时间戳: $LOCAL_TIME"
echo "   建议使用 NTP 同步: sudo ntpdate -u pool.ntp.org"
echo ""

# 3. 心跳接口测试
echo "3. 心跳接口测试..."
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen)

# 构造签名(简化版,实际需要完整实现)
PARAMS="access_key=$ACCESS_KEY&nonce=$NONCE&timestamp=$TIMESTAMP"
SIGN_STR="GET/api/v1/heartbeat${'{'}TIMESTAMP{'}'}${'{'}NONCE{'}'}${'{'}PARAMS{'}'}"

# 这里需要实现 HMAC-SHA256 签名
# SIGNATURE=$(echo -n "$SIGN_STR" | openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -d' ' -f2)

echo "   签名字符串: $SIGN_STR"
echo "   如果签名验证失败,请检查 SecretKey 是否正确"
echo ""

# 4. AccessKey 验证
echo "4. AccessKey 验证..."
if [[ $ACCESS_KEY == ak_* ]]; then
    echo "   ✅ AccessKey 格式正确"
else
    echo "   ⚠️  AccessKey 格式可能不正确(应以 ak_ 开头)"
fi
echo ""

# 5. SecretKey 验证
echo "5. SecretKey 验证..."
if [[ ${'{'}#SECRET_KEY{'}'} -ge 16 ]]; then
    echo "   ✅ SecretKey 长度符合要求"
else
    echo "   ❌ SecretKey 长度不足(应至少16字符)"
fi
echo ""

echo "=== 验证完成 ==="

Python 验证脚本

#!/usr/bin/env python3
"""环境配置验证脚本"""

import sys
import time
import socket
from urllib.parse import urlparse

def check_network(base_url):
    """检查网络连通性"""
    try:
        parsed = urlparse(base_url)
        host = parsed.hostname
        port = parsed.port or (443 if parsed.scheme == 'https' else 80)

        sock = socket.create_connection((host, port), timeout=10)
        sock.close()
        print("✅ 网络连接正常")
        return True
    except Exception as e:
        print(f"❌ 网络连接失败: {'{'}e{'}'}")
        return False

def check_time_sync():
    """检查时间同步"""
    local_time = int(time.time())
    print(f"✅ 本地时间戳: {'{'}local_time{'}'}")
    print(f"   时间: {'{'}time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(local_time)){'}'}")
    return True

def check_credentials(access_key, secret_key):
    """检查凭据格式"""
    if access_key.startswith('ak_'):
        print("✅ AccessKey 格式正确")
    else:
        print("⚠️  AccessKey 格式可能不正确")

    if len(secret_key) >= 16:
        print("✅ SecretKey 长度符合要求")
    else:
        print("❌ SecretKey 长度不足")

    return True

if __name__ == "__main__":
    BASE_URL = "http://your-api-server.com"
    ACCESS_KEY = "ak_xxxxxxxxxx"
    SECRET_KEY = "sk_xxxxxxxxxx"

    print("=== 天城智控环境验证 ===\n")

    print("1. 网络连通性测试")
    check_network(BASE_URL)
    print()

    print("2. 时间同步检查")
    check_time_sync()
    print()

    print("3. 凭据格式检查")
    check_credentials(ACCESS_KEY, SECRET_KEY)
    print()

    print("=== 验证完成 ===")

Q6: 如何处理超时问题?

超时类型

  1. 连接超时:无法建立 TCP 连接
  2. 读取超时:连接建立但响应缓慢
  3. 请求超时:整体请求时间过长

解决方案

# 设置合理的超时时间
client = WLWOpenAPIClient(
    base_url="https://api.example.com",
    access_key="ak_xxx",
    secret_key="sk_xxx",
    timeout=30  # 30秒超时
)

# 实现重试机制
def request_with_retry(func, max_retries=3):
    for attempt in range(max_retries):
        try:
            return func()
        except TimeoutError:
            if attempt == max_retries - 1:
                raise
            print(f"请求超时,重试 {'{'}attempt + 1{'}'}/{'{'}max_retries{'}'}")
            time.sleep(2 ** attempt)

Q7: 池中池卡板调用 OpenAPI 怎么处理?

处理口径

  • 流量查询:池中池卡板调用卡板信息查询时,used_flow 返回运营商上游数据中的已用流量;普通卡板返回本地统计的已用流量。
  • 欠费拦截:同一客户名下任一启用池子欠费并触发 OpenAPI 暂停后,该客户的 OpenAPI 请求会返回 Forbidden
  • 欠费判断:池子余额低于保证金时视为欠费,需要先充值或补足余额。
  • 停复机:欠费拦截通过后,池中池卡板可以正常调用停复机接口;复机不会因为没有单卡订单而被阻断。
  • 扣费:池中池余额不在接口查询或轮询时实时扣费,由后台定时任务处理昨天流量,并由月结流程结算周期差额。
  • 套餐订购:套餐订购接口创建普通卡板套餐订单;池中池日扣费和月结不依赖下游通过该接口下单。

欠费响应示例

{'{'}
  "code": "Forbidden",
  "msg": "池中池已欠费(余额低于保证金),OpenAPI 已暂停,请先充值或补足余额",
  "ok": false
{'}'}

Q8: 账单提取配置禁止 OpenAPI 后影响哪些对象?

  • 执行时点:配置勾选“禁止调用接口”后,使用该配置成功提取账单时执行。该动作与池中池余额或保证金判断无关。
  • 卡板配置:只禁止配置内直接绑定的卡板调用卡板 OpenAPI。
  • 设备配置:只禁止配置内直接绑定的设备调用设备 OpenAPI。
  • 其他对象:同一客户/代理名下未绑定到该配置的卡板、设备可正常调用,不会整个代理一起封禁。
  • 关联关系:卡板和设备按配置类型精确匹配,不通过设备及其槽位卡的绑定关系相互推导。
  • 恢复方式:取消“禁止调用接口”动作、解绑对象或删除配置后,对应对象恢复调用。

账单配置拦截与池中池欠费使用不同的错误信息:

  • 卡板:账单提取配置已禁止该卡板调用接口
  • 设备:账单提取配置已禁止该设备调用接口

这两种账单配置报错不会返回“池中池已欠费”的提示。


更多问题?

如果以上内容没有解决您的问题,请:

  1. 查看 API 响应中的详细错误信息
  2. 检查系统日志获取更多上下文
  3. 联系技术支持团队,提供:
    • 请求 ID(X-Request-ID)
    • 完整的错误信息
    • 请求时间和卡板编号
    • 重现步骤