常见问题
本文档汇总了使用 Lovrabet Java OpenSDK 过程中的常见问题和解决方案。
目录
认证与授权
Q1: 提示 "Token 无效" 或 "签名验证失败"
可能原因:
- AccessKey 不正确或已过期
- 系统时间与服务器时间相差过大(超过 10 分钟)
- 网络请求被代理或中间件修改
解决方案:
// 1. 验证 AccessKey 是否正确
System.out.println("AccessKey 前 10 位: " + accessKey.substring(0, 10));
// 2. 检查系统时间
System.out.println("当前时间戳: " + System.currentTimeMillis());
System.out.println("当前时间: " + new Date());
// 3. 确认请求参数正确
System.out.println("AppCode: " + request.getAppCode());
System.out.println("ModelCode: " + request.getModelCode());
如果时间不准确:
# Linux/Mac 同步系统时间
sudo ntpdate -u time.apple.com
# 或使用 NTP 服务
sudo systemctl start ntpd
Q2: 提示 "AccessKey 不存在或已失效"
解决方案:
- 登录 Lovrabet 云兔平台
- 进入应用管理,检查以下内容:
- OpenAPI 功能是否已开通
- AccessKey 是否已生成
- AccessKey 是否在有效期内
- 如果 AccessKey 过期或丢失,重新生成:
- 进入应用设置
- 找到 OpenAPI 配置
- 点击"重新生成 AccessKey"
- 复制新的 AccessKey 并更新配置
注意: 重新生成 AccessKey 后,旧的 AccessKey 立即失效,需要更新所有使用该 AccessKey 的应用。
Q3: 提示 "应用编码不存在"
解决方案:
- 检查
appCode是否正确:
// appCode 格式通常为: app-xxxxxxxx
String appCode = "app-c2dd52a2"; // 确保格式正确
// 检查是否有多余的空格或换行符
appCode = appCode.trim();
- 在 Lovrabet 云兔平台确认应用编码:
- 进入应用管理
- 查看应用详情
- 复制正确的应用编码
Q4: 多个应用如何管理 AccessKey?
推荐方案:
方式 1: 环境变量 + 配置文件
# application.yml
lovrabet:
app1:
access-key: ${APP1_ACCESS_KEY}
app-code: app-xxx1
app2:
access-key: ${APP2_ACCESS_KEY}
app-code: app-xxx2
方式 2: 使用密钥管理服务
@Configuration
public class LovrabetMultiAppConfig {
@Bean("app1Client")
public LovrabetSDKClient app1Client(SecretManager secretManager) {
String accessKey = secretManager.getSecret("lovrabet-app1-key");
return new LovrabetSDKClient(accessKey, baseUrl);
}
@Bean("app2Client")
public LovrabetSDKClient app2Client(SecretManager secretManager) {
String accessKey = secretManager.getSecret("lovrabet-app2-key");
return new LovrabetSDKClient(accessKey, baseUrl);
}
}
参数与数据
Q5: 创建数据时提示 "必填字段缺失"
解决方案:
- 登录 Lovrabet 云兔平台,查看数据集的字段定义
- 确认哪些字段是必填的
- 确保所有必填字段都已提供值:
Map<String, Object> paramMap = new HashMap<>();
// ✅ 正确:提供必填字段的值
paramMap.put("TEXT_1", "客户名称");
paramMap.put("NUMBER_3", 100);
// ❌ 错误:不要传递 null 值
// paramMap.put("TEXT_2", null);
// 对于非必填的空值字段,直接不放入 paramMap
Q6: 字段名不确定,如何查看数据集的字段定义?
方法 1: 在 Lovrabet 云兔平台查看
- 进入应用管理
- 选择对应的数据集
- 查看字段列表,获取字段编码(如
TEXT_1,NUMBER_3)
方法 2: 通过浏览器开发者工具
- 在 Lovrabet 云兔平台打开数据集
- 打开浏览器开发者工具(F12)
- 切换到 Network 标签
- 创建或编辑一条数据
- 查看请求参数,了解字段名和数据格式
字段命名规则:
TEXT_1,TEXT_2- 文本字段NUMBER_1,NUMBER_2- 数字字段SELECT_1,SELECT_2- 选择字段DATETIME_1,DATETIME_2- 日期时间字段BOOLEAN_1,BOOLEAN_2- 布尔字段
Q7: 如何处理日期时间字段?
方案 1: 使用时间戳(推荐)
// 当前时间
paramMap.put("DATETIME_1", System.currentTimeMillis());
// 指定日期
Calendar calendar = Calendar.getInstance();
calendar.set(2025, Calendar.JANUARY, 12, 10, 30, 0);
paramMap.put("DATETIME_1", calendar.getTimeInMillis());
// 从 Date 对象
Date date = new Date();
paramMap.put("DATETIME_1", date.getTime());
方案 2: 使用字符串格式
// 格式: yyyy-MM-dd HH:mm:ss
paramMap.put("DATETIME_1", "2025-01-12 10:30:00");
读取日期时间:
// API 返回的是时间戳(毫秒)
Number timestampMs = (Number) data.get("DATETIME_1");
if (timestampMs != null) {
Date date = new Date(timestampMs.longValue());
System.out.println("创建时间: " + date);
}
Q8: 更新数据时,是否需要传递所有字段?
不需要,只需要传递要更新的字段:
// ✅ 推荐:只传递要更新的字段
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("TEXT_1", "新的客户名称"); // 只更新客户名称
LovrabetRequest request = new LovrabetRequest();
request.setId(513L);
request.setParamMap(paramMap);
sdkClient.update(request);
注意: ID 字段会被 SDK 自动添加到 paramMap 中,无需手动添加。
Q9: 如何处理选择字段(下拉框)?
单选字段:
// 选择字段的值可能是字符串或数字,取决于数据集定义
paramMap.put("SELECT_4", "china-north"); // 字符串类型
paramMap.put("SELECT_7", 463); // 数字类型
多选字段:
// 多选字段使用 List
List<String> regions = Arrays.asList("north", "south", "east");
paramMap.put("MULTI_SELECT_1", regions);
查询选择字段的可选值:
在 Lovrabet 云兔平台的数据集字段定义中查看选项列表。
Q10: 分页查询返回数据为空,但数据集确实有数据
可能原因:
- 页码超出实际数据范围
- 查询条件过滤掉了所有数据
- 数据权限限制
排查步骤:
// 1. 先查询第一页
paramMap.put("currentPage", 1);
paramMap.put("pageSize", 10);
LovrabetResult<?> result = sdkClient.getList(request);
if (result.isSuccess()) {
List<?> dataList = (List<?>) result.getData();
System.out.println("第一页数据量: " + dataList.size());
if (dataList.isEmpty()) {
// 2. 检查查询条件
System.out.println("查询参数: " + paramMap);
// 3. 尝试不带任何查询条件
Map<String, Object> simpleParams = new HashMap<>();
simpleParams.put("currentPage", 1);
simpleParams.put("pageSize", 10);
request.setParamMap(simpleParams);
LovrabetResult<?> result2 = sdkClient.getList(request);
// ...
}
}
性能问题
Q11: 查询速度很慢,如何优化?
优化策略:
1. 减少单次查询数据量
// ❌ 不推荐:pageSize 过大
paramMap.put("pageSize", 1000);
// ✅ 推荐:使用合理的 pageSize
paramMap.put("pageSize", 50); // 20-100 之间
2. 使用本地缓存
// 对于不常变化的数据,使用缓存
private final Cache<String, Map<String, Object>> cache =
Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
public Map<String, Object> getData(String id) {
return cache.get(id, key -> fetchFromApi(key));
}
3. 并发查询
// 使用线程池并发查询多个数据
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Future<Map<String, Object>>> futures = new ArrayList<>();
for (String id : idList) {
Future<Map<String, Object>> future = executor.submit(() -> {
return queryData(id);
});
futures.add(future);
}
// 收集结果
for (Future<Map<String, Object>> future : futures) {
Map<String, Object> data = future.get();
// 处理数据
}
executor.shutdown();
Q12: 大量数据创建时效率低,如何提高?
优化方案:
1. 使用线程池并发创建
ExecutorService executor = Executors.newFixedThreadPool(10); // 控制并发数
List<CompletableFuture<Void>> futures = dataList.stream()
.map(data -> CompletableFuture.runAsync(() -> {
try {
LovrabetRequest request = buildRequest(data);
sdkClient.create(request);
} catch (Exception e) {
logger.error("创建失败", e);
}
}, executor))
.collect(Collectors.toList());
// 等待所有任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();
2. 批量处理 + 错误重试
public void batchCreateWithRetry(List<Map<String, Object>> dataList) {
int batchSize = 100;
for (int i = 0; i < dataList.size(); i += batchSize) {
int end = Math.min(i + batchSize, dataList.size());
List<Map<String, Object>> batch = dataList.subList(i, end);
// 批量创建
batchCreate(batch);
// 短暂休息,避免过载
Thread.sleep(100);
}
}
Q13: 内存占用过高
可能原因:
- 一次性加载过多数据
- 没有及时释放资源
解决方案:
1. 流式处理大量数据
// ❌ 不推荐:一次性加载所有数据
List<Map<String, Object>> allData = getAllData(); // 可能有 10 万条
// ✅ 推荐:分页流式处理
public void processAllData(Consumer<Map<String, Object>> processor) {
int currentPage = 1;
int pageSize = 100;
boolean hasMore = true;
while (hasMore) {
List<Map<String, Object>> pageData = getPageData(currentPage, pageSize);
if (pageData.isEmpty()) {
hasMore = false;
} else {
// 处理当前页数据
pageData.forEach(processor);
// 释放引用,帮助 GC
pageData.clear();
if (pageData.size() < pageSize) {
hasMore = false;
} else {
currentPage++;
}
}
}
}
2. 及时清理缓存
// 定期清理缓存
@Scheduled(fixedRate = 3600000) // 每小时清理一次
public void cleanupCache() {
cache.cleanUp();
logger.info("缓存清理完成");
}
网络问题
Q14: 提示 "Connection timeout" 或 "Read timeout"
可能原因:
- 网络不稳定
- 服务器响应慢
- 请求数据量过大
解决方案:
1. 检查网络连通性
# 测试网络连接
ping runtime.lovrabet.com
# 测试 HTTPS 连接
curl -I https://runtime.lovrabet.com/openapi
2. 实现重试机制
// 使用重试工具(参考最佳实践文档)
LovrabetResult<?> result = RetryHelper.executeWithRetry(
() -> sdkClient.getOne(request),
3, // 最多重试 3 次
1000L // 初始延迟 1 秒
);
3. 减少单次请求的数据量
// 如果查询数据量过大导致超时
paramMap.put("pageSize", 20); // 减小 pageSize
Q15: 在公司内网无法访问 Lovrabet API
可能原因: 公司防火墙或代理限制
解决方案:
1. 配置代理
// 设置系统代理
System.setProperty("https.proxyHost", "proxy.company.com");
System.setProperty("https.proxyPort", "8080");
// 如果代理需要认证
System.setProperty("https.proxyUser", "username");
System.setProperty("https.proxyPassword", "password");
2. 联系网络管理员
需要将以下域名加入白名单:
runtime.lovrabet.com- 端口:
443(HTTPS)
Q16: 偶尔出现 "SocketException: Connection reset"
原因: 网络抖动或连接被重置
解决方案:
实现自动重试机制(参考 最佳实践 - 错误处理):
LovrabetResult<?> result = RetryHelper.executeWithRetry(
() -> sdkClient.getList(request),
3, // 最多重试 3 次
2000L // 初始延迟 2 秒
);
数据一致性
Q17: 更新数据后查询不到最新值
可能原因:
- 数据库主从同步延迟
- 本地缓存未更新
解决方案:
1. 等待短暂时间后查询
// 更新数据
sdkClient.update(updateRequest);
// 短暂等待(通常 100-200ms 足够)
Thread.sleep(100);
// 查询数据
LovrabetResult<?> result = sdkClient.getOne(queryRequest);
2. 使用更新返回的数据
// 更新后直接使用本地数据,不重新查询
Map<String, Object> localData = updateRequest.getParamMap();
// 使用 localData 而不是重新查询
3. 清除本地缓存
// 更新后清除缓存
sdkClient.update(request);
cache.invalidate(id); // 清除该 ID 的缓存
Q18: 并发更新同一条数据,如何避免冲突?
方案 1: 乐观锁(推荐)
public boolean updateWithVersion(Long id, Map<String, Object> newData) {
// 1. 查询当前数据和版本号
Map<String, Object> current = getData(id);
Integer currentVersion = (Integer) current.get("version");
// 2. 更新时携带版本号
newData.put("version", currentVersion + 1);
LovrabetRequest request = new LovrabetRequest();
request.setId(id);
request.setParamMap(newData);
LovrabetResult<String> result = sdkClient.update(request);
// 3. 检查更新结果
if (!result.isSuccess()) {
// 版本冲突,可能需要重试
logger.warn("版本冲突,当前版本: {}", currentVersion);
return false;
}
return true;
}
方案 2: 分布式锁
public void updateWithLock(Long id, Map<String, Object> newData) {
String lockKey = "lovrabet:lock:" + id;
// 获取分布式锁
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试获取锁,最多等待 10 秒,锁超时时间 30 秒
if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
try {
// 执行更新操作
sdkClient.update(buildRequest(id, newData));
} finally {
lock.unlock();
}
} else {
logger.warn("获取锁超时: {}", id);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
开发调试
Q19: 如何查看 SDK 发送的 HTTP 请求详情?
方法 1: 启用日志
# application.yml 或 logback.xml
logging:
level:
com.lovrabet.runtime.opensdk: DEBUG
方法 2: 使用抓包工具
- Windows: Fiddler
- Mac: Charles Proxy
- 跨平台: Wireshark
配置代理:
System.setProperty("https.proxyHost", "localhost");
System.setProperty("https.proxyPort", "8888"); // Charles 默认端口
Q20: 如何在本地开发环境测试 SDK?
方案 1: 使用测试数据集
在 Lovrabet 云兔平台创建专门的测试数据集,避免污染生产数据。
# application-dev.yml
lovrabet:
app-code: app-dev-xxx
model-code: test-dataset-xxx
方案 2: Mock SDK 客户端
@TestConfiguration
public class TestConfig {
@Bean
@Primary // 覆盖正常的 Bean
public LovrabetSDKClient mockSdkClient() {
LovrabetSDKClient mockClient = Mockito.mock(LovrabetSDKClient.class);
// 设置 Mock 行为
LovrabetResult<String> mockResult = new LovrabetResult<>();
mockResult.setSuccess(true);
mockResult.setData("mock-id-12345");
Mockito.when(mockClient.create(Mockito.any()))
.thenReturn(mockResult);
return mockClient;
}
}
Q21: 如何排查 "数据创建成功但查询不到" 的问题?
排查步骤:
// 1. 创建数据并记录返回的 ID
LovrabetResult<String> createResult = sdkClient.create(request);
if (createResult.isSuccess()) {
String newId = createResult.getData();
logger.info("创建成功,新 ID: {}", newId);
// 2. 立即查询确认
Thread.sleep(200); // 等待 200ms
LovrabetRequest queryRequest = new LovrabetRequest();
queryRequest.setAppCode(appCode);
queryRequest.setModelCode(modelCode);
queryRequest.setId(Long.parseLong(newId));
LovrabetResult<?> queryResult = sdkClient.getOne(queryRequest);
if (queryResult.isSuccess()) {
Map<String, Object> data = (Map<String, Object>) queryResult.getData();
logger.info("查询成功: {}", data);
} else {
logger.error("查询失败: {}", queryResult.getResultMsg());
// 3. 检查 appCode 和 modelCode 是否一致
logger.error("创建时 appCode: {}, modelCode: {}",
request.getAppCode(), request.getModelCode());
logger.error("查询时 appCode: {}, modelCode: {}",
queryRequest.getAppCode(), queryRequest.getModelCode());
}
}
Q22: SDK 版本如何升级?
Maven 项目:
<dependency>
<groupId>com.lovrabet.runtime</groupId>
<artifactId>lovrabet-runtime-opensdk</artifactId>
<version>1.1.0-SNAPSHOT</version> <!-- 更新版本号 -->
</dependency>
然后执行:
mvn clean install
Gradle 项目:
implementation 'com.lovrabet.runtime:lovrabet-runtime-opensdk:1.1.0-SNAPSHOT'
然后执行:
./gradlew clean build
升级注意事项:
- 查看官方文档了解变更内容
- 在测试环境验证后再升级生产环境
- 注意是否有不兼容的变更
获取帮助
如果以上问题没有解决您的疑问,请通过以下方式获取帮助:
技术支持
- 📧 邮箱: service@lovrabet.com
- 🌐 官方网站: https://lovrabet.com
- 📚 文档中心: https://open.lovrabet.com
反馈问题时,请提供以下信息:
- SDK 版本:
lovrabet-runtime-opensdk的版本号 - JDK 版本: 使用的 Java 版本
- 错误信息: 完整的错误堆栈(注意脱敏敏感信息)
- 复现步骤: 如何触发该问题
- 代码片段: 相关的代码示例(脱敏后)
示例邮件:
主题: [OpenSDK] 创建数据时提示必填字段缺失
您好,
我在使用 Lovrabet Java OpenSDK 时遇到问题:
1. SDK 版本: lovrabet-runtime-opensdk 1.0.0-SNAPSHOT
2. JDK 版本: Java 11
3. 错误信息:
resultCode: PARAM_ERROR
resultMsg: 必填字段 TEXT_1 缺失
4. 代码片段:
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("TEXT_1", "测试数据");
// ...
5. 已尝试的解决方法:
- 确认字段名正确
- 确认值不为 null
请问这个问题如何解决?
谢谢!
下一步
查阅完常见问题后,您可以按以下顺序回顾学习内容:
-
📖 快速开始 5 分钟完成第一个 CRUD 程序
-
📚 核心概念 深入理解 SDK 工作原理
-
📋 API 参考 查阅完整的接口文档
-
💡 业务示例 学习 5 个真实场景的实现
-
🚀 最佳实践 掌握生产环境优化技巧
-
❓ 常见问题 ← 当前位置 解决使用中的疑问
最后更新: 2025-10-12