跳到主要内容

故障排查

本文提供 Lovrabet SDK 常见问题的解决方案,帮助您快速诊断和修复问题。

🔧 常见问题诊断

问题诊断流程

在遇到问题时,请按以下步骤进行排查:

  1. 检查错误信息 - 查看控制台错误日志
  2. 验证配置 - 确认 SDK 配置正确
  3. 检查网络 - 查看网络请求状态
  4. 验证权限 - 确认 API 访问权限
  5. 查看文档 - 对照本文档检查用法

启用详细日志

// 启用详细的错误日志
const client = createClient({
options: {
onError: (error) => {
console.group("🔍 Lovrabet SDK Error Details");
console.log("📝 Message:", error.message);
console.log("🔢 Status:", error.status);
console.log("🌐 URL:", error.url);
console.log("📤 Request Data:", error.requestData);
console.log("📥 Response Data:", error.responseData);
console.log("⏱️ Timestamp:", new Date().toISOString());
console.log("🔗 Stack:", error.stack);
console.groupEnd();
},
},
});

❌ 配置相关问题

Q1: "Cannot find module '@lovrabet/sdk'"

问题描述:项目无法找到 SDK 模块

解决方案

# 确认 SDK 已安装
npm list @lovrabet/sdk

# 如果未安装,请安装
npm install @lovrabet/sdk

# 如果是 TypeScript 项目,确认类型支持
npm install --save-dev typescript

Q2: "registerModels is not a function"

问题描述:导入函数出现问题

解决方案

// ❌ 错误的导入方式
import lovrabetSdk from "@lovrabet/sdk";
lovrabetSdk.registerModels(config); // 会报错

// ✅ 正确的导入方式
import { registerModels, createClient } from "@lovrabet/sdk";
registerModels(config);

Q3: "No configuration found for apiConfigName 'default'"

问题描述:找不到默认配置

解决方案

// ❌ 问题:没有注册配置就直接使用
const client = createClient(); // 会报错

// ✅ 解决:先注册配置
registerModels({
appCode: "your-app-code",
models: {
users: { tableName: "users", datasetCode: "your-dataset-id" },
},
});
const client = createClient(); // 正常工作

Q4: AppCode 配置问题

问题描述:appCode 配置错误或缺失

解决方案

// 检查环境变量
console.log("AppCode:", process.env.REACT_APP_LOVRABET_APP_CODE);

// 确保配置正确
const config = {
appCode: "your-actual-app-code", // 不要使用 "your-app-code"
models: {
// ...
},
};

// 验证配置
if (!config.appCode) {
throw new Error("AppCode is required");
}

🔐 认证相关问题

Q5: "Authentication failed" / 401 错误

问题描述:认证失败,无法访问 API

解决方案

// 1. 检查 token 是否设置
const client = createClient();
console.log("Current token:", client.getConfig().token);

// 2. 设置 token
client.setToken("your-valid-token");

// 3. 检查 token 格式
if (token && !token.startsWith("Bearer ")) {
client.setToken(`Bearer ${token}`);
}

// 4. 检查 token 是否过期
const isTokenExpired = (token: string) => {
try {
const payload = JSON.parse(atob(token.split(".")[1]));
return payload.exp * 1000 < Date.now();
} catch {
return true;
}
};

Q6: OpenAPI 密钥认证问题

问题描述:使用 OpenAPI 密钥时认证失败

解决方案

// 检查密钥配置
const client = createClient({
accessKey: process.env.LOVRABET_ACCESS_KEY,
secretKey: process.env.LOVRABET_SECRET_KEY,
});

// 验证密钥存在
const config = client.getConfig();
console.log("Has Access Key:", !!config.accessKey);
console.log("Has Secret Key:", !!config.secretKey);

// 确保环境变量正确设置
if (!process.env.LOVRABET_ACCESS_KEY) {
console.error("Missing LOVRABET_ACCESS_KEY environment variable");
}

问题描述:在浏览器中 Cookie 认证失败

解决方案

// 检查 Cookie 设置
document.cookie.split(";").forEach((cookie) => {
console.log("Cookie:", cookie.trim());
});

// 确保域名和路径设置正确
// 检查是否在同源域下
console.log("Current origin:", window.location.origin);
console.log("API endpoint:", client.getConfig().serverUrl);

🌐 网络相关问题

Q8: "Network Error" / 连接失败

问题描述:网络请求失败

解决方案

// 1. 检查服务器地址
const client = createClient({
serverUrl: "https://api.lovrabet.com", // 确保地址正确
});

// 2. 检查网络连接
const testConnection = async () => {
try {
const response = await fetch("https://api.lovrabet.com/health");
console.log("API健康检查:", response.status);
} catch (error) {
console.error("网络连接问题:", error);
}
};

// 3. 检查 CORS 设置(前端)
// 确保服务器配置了正确的 CORS 头

Q9: 请求超时问题

问题描述:请求经常超时

解决方案

// 增加超时时间
const client = createClient({
options: {
timeout: 60000, // 60秒
},
});

// 实现请求重试
const withRetry = async <T>(
operation: () => Promise<T>,
maxRetries = 3
): Promise<T> => {
let lastError: any;

for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error: any) {
lastError = error;

if (error.code === "TIMEOUT" && attempt < maxRetries) {
console.log(`请求超时,重试 ${attempt}/${maxRetries}`);
await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
continue;
}

throw error;
}
}

throw lastError;
};

Q10: 速率限制问题

问题描述:收到 429 Too Many Requests 错误

解决方案

// 实现请求限流
class RateLimiter {
private requests: number[] = [];
private maxRequests: number;
private timeWindow: number;

constructor(maxRequests = 100, timeWindowMs = 60000) {
this.maxRequests = maxRequests;
this.timeWindow = timeWindowMs;
}

async waitForAvailability(): Promise<void> {
const now = Date.now();

// 清理过期的请求记录
this.requests = this.requests.filter(
(time) => now - time < this.timeWindow
);

if (this.requests.length >= this.maxRequests) {
const oldestRequest = Math.min(...this.requests);
const waitTime = this.timeWindow - (now - oldestRequest);

console.log(`Rate limit reached, waiting ${waitTime}ms`);
await new Promise((resolve) => setTimeout(resolve, waitTime));
return this.waitForAvailability();
}

this.requests.push(now);
}
}

const rateLimiter = new RateLimiter();

const makeRequest = async (operation: () => Promise<any>) => {
await rateLimiter.waitForAvailability();
return operation();
};

📊 数据相关问题

Q11: "Dataset not found" 错误

问题描述:数据集不存在或无权访问

解决方案

// 验证数据集ID
const verifyDataset = async (datasetCode: string) => {
try {
// 尝试获取数据集信息
const response = await client.models.yourModel.filter({ pageSize: 1 });
console.log("数据集验证成功:", response.total);
} catch (error: any) {
if (error.status === 404) {
console.error("数据集不存在:", datasetCode);
} else if (error.status === 403) {
console.error("没有访问权限:", datasetCode);
}
throw error;
}
};

// 检查配置中的数据集ID
console.log("配置的数据集ID:", client.getConfig().models);

Q12: 数据格式问题

问题描述:返回的数据格式不符合预期

解决方案

// 添加数据验证
const validateResponseData = (data: any, expectedFields: string[]) => {
if (!data || typeof data !== "object") {
throw new Error("Invalid response data format");
}

const missingFields = expectedFields.filter((field) => !(field in data));
if (missingFields.length > 0) {
console.warn("Missing fields:", missingFields);
}

return data;
};

// 使用时验证数据
const users = await client.models.users.filter();
users.tableData.forEach((user) => {
validateResponseData(user, ["id", "name", "email"]);
});

Q13: 分页数据问题

问题描述:分页参数不正确或分页数据异常

解决方案

// 验证分页参数
const validatePaginationParams = (params: any) => {
const { currentPage, pageSize } = params;

if (currentPage && (currentPage < 1 || !Number.isInteger(currentPage))) {
throw new Error("currentPage must be a positive integer");
}

if (
pageSize &&
(pageSize < 1 || pageSize > 1000 || !Number.isInteger(pageSize))
) {
throw new Error("pageSize must be between 1 and 1000");
}

return params;
};

// 安全的分页查询
const safeGetList = async (modelName: string, params: any = {}) => {
const validatedParams = validatePaginationParams(params);
return await client.models[modelName].filter(validatedParams);
};

🔍 调试工具和技巧

调试助手类

class LovrabetDebugger {
private client: any;

constructor(client: any) {
this.client = client;
}

// 测试连接
async testConnection(): Promise<boolean> {
try {
// 尝试获取少量数据测试连接
await this.client.models[Object.keys(this.client.getConfig().models)[0]].filter({ pageSize: 1 });
console.log("✅ 连接测试成功");
return true;
} catch (error: any) {
console.error("❌ 连接测试失败:", error.message);
return false;
}
}

// 检查配置
checkConfiguration(): void {
const config = this.client.getConfig();

console.group("🔧 配置检查");
console.log("AppCode:", config.appCode);
console.log("Environment:", config.env);
console.log("Server URL:", config.serverUrl);
console.log("Has Token:", !!config.token);
console.log("Has Access Key:", !!config.accessKey);
console.log("Models:", Object.keys(config.models || {}));
console.groupEnd();
}

// 测试所有模型
async testAllModels(): Promise<void> {
const config = this.client.getConfig();
const modelNames = Object.keys(config.models || {});

console.log("🧪 测试所有模型...");

for (const modelName of modelNames) {
try {
const response = await this.client.models[modelName].filter({ pageSize: 1 });
console.log(`${modelName}: ${response.total} records`);
} catch (error: any) {
console.error(`${modelName}: ${error.message}`);
}
}
}

// 性能测试
async performanceTest(modelName: string, iterations = 10): Promise<void> {
console.log(`⚡ 性能测试 ${modelName} (${iterations} 次请求)`);

const times: number[] = [];

for (let i = 0; i < iterations; i++) {
const start = Date.now();
try {
await this.client.models[modelName].filter({ pageSize: 10 });
const duration = Date.now() - start;
times.push(duration);
console.log(`请求 ${i + 1}: ${duration}ms`);
} catch (error: any) {
console.error(`请求 ${i + 1} 失败:`, error.message);
}
}

if (times.length > 0) {
const avg = times.reduce((a, b) => a + b, 0) / times.length;
const min = Math.min(...times);
const max = Math.max(...times);

console.log(`📊 性能统计:`);
console.log(`平均耗时: ${avg.toFixed(2)}ms`);
console.log(`最短耗时: ${min}ms`);
console.log(`最长耗时: ${max}ms`);
}
}

// 检查权限
async checkPermissions(modelName: string): Promise<void> {
console.log(`🔒 检查 ${modelName} 权限`);

const tests = [
{ name: "读取", action: () => this.client.models[modelName].filter({ pageSize: 1 }) },
{ name: "创建", action: () => this.client.models[modelName].create({ test: true }) },
{ name: "更新", action: () => this.client.models[modelName].update("test-id", { test: true }) },
{ name: "删除", action: () => this.client.models[modelName].delete("test-id") },
];

for (const test of tests) {
try {
await test.action();
console.log(`${test.name}: 有权限`);
} catch (error: any) {
if (error.status === 403) {
console.log(`${test.name}: 无权限`);
} else if (error.status === 404) {
console.log(`⚠️ ${test.name}: 资源不存在(可能有权限)`);
} else {
console.log(`${test.name}: ${error.message}`);
}
}
}
}
}

// 使用调试器
const debugger = new LovrabetDebugger(client);

// 运行完整诊断
const runDiagnostics = async () => {
debugger.checkConfiguration();
await debugger.testConnection();
await debugger.testAllModels();
await debugger.checkPermissions("users");
await debugger.performanceTest("users", 5);
};

浏览器开发者工具

// 在浏览器控制台中添加调试方法
(window as any).lovrabetDebug = {
client,

// 快速测试
async test() {
const debugger = new LovrabetDebugger(client);
await debugger.testConnection();
},

// 查看配置
config() {
console.table(client.getConfig());
},

// 查看最近的请求
requests: [],

// 添加请求监听
enableRequestLogging() {
const originalFetch = window.fetch;
(window as any).lovrabetDebug.requests = [];

window.fetch = function(...args) {
const start = Date.now();
const url = typeof args[0] === 'string' ? args[0] : args[0].url;

return originalFetch.apply(this, args).then(response => {
(window as any).lovrabetDebug.requests.push({
url,
status: response.status,
duration: Date.now() - start,
timestamp: new Date().toISOString(),
});

return response;
});
};

console.log("✅ 请求日志已启用,使用 lovrabetDebug.requests 查看");
},
};

console.log("🔧 调试工具已加载,使用 lovrabetDebug 访问");

📞 获取帮助

自助排查清单

在联系技术支持前,请完成以下检查:

  • 确认 SDK 版本是最新的
  • 检查控制台是否有错误信息
  • 验证 appCode 和 datasetCode 配置正确
  • 测试网络连接是否正常
  • 确认 API 认证配置正确
  • 尝试简单的测试请求

提供问题信息

当需要技术支持时,请提供以下信息:

// 收集问题信息的工具函数
const collectProblemInfo = () => {
const info = {
sdkVersion: "1.0.0", // 检查 package.json
browser: navigator.userAgent,
timestamp: new Date().toISOString(),
config: {
appCode: client.getConfig().appCode,
env: client.getConfig().env,
hasToken: !!client.getConfig().token,
hasAccessKey: !!client.getConfig().accessKey,
modelsCount: Object.keys(client.getConfig().models || {}).length,
},
error: "具体错误信息",
steps: "重现步骤",
};

console.log("问题信息:", JSON.stringify(info, null, 2));
return info;
};

联系渠道

  • 📧 技术支持邮箱 - 发送问题详情和系统信息
  • 💬 内部技术群 - 快速获得社区帮助
  • 📖 文档反馈 - 如发现文档问题,请反馈

常用检查命令

# 检查 SDK 版本
npm list @lovrabet/sdk

# 检查依赖冲突
npm ls --depth=0

# 清理缓存
npm cache clean --force
rm -rf node_modules package-lock.json
npm install

# 检查环境变量
echo $REACT_APP_LOVRABET_APP_CODE

📖 预防问题的最佳实践

  1. 使用 TypeScript - 编译时发现类型错误
  2. 错误边界 - 在 React 中使用错误边界组件
  3. 输入验证 - 验证用户输入和 API 响应
  4. 环境管理 - 区分开发和生产环境配置
  5. 监控日志 - 记录关键操作和错误信息
  6. 测试驱动 - 编写单元测试和集成测试
  7. 渐进部署 - 分阶段发布和测试功能

希望这些排查步骤能帮助您快速解决问题!如果仍有疑问,请联系技术支持团队。