diff --git a/datai-common/src/main/java/com/datai/common/core/controller/BaseController.java b/datai-common/src/main/java/com/datai/common/core/controller/BaseController.java index 91da91c9..2cc4fc6c 100644 --- a/datai-common/src/main/java/com/datai/common/core/controller/BaseController.java +++ b/datai-common/src/main/java/com/datai/common/core/controller/BaseController.java @@ -89,6 +89,19 @@ public class BaseController return rspData; } + /** + * 响应请求分页数据 + */ + protected TableDataInfo getDataTableByPage(List list,long total) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(total); + return rspData; + } + /** * 返回成功 */ diff --git a/datai-models/datai-generator/src/main/resources/vm/java/controller.java.vm b/datai-models/datai-generator/src/main/resources/vm/java/controller.java.vm index afdddea5..51e6399b 100644 --- a/datai-models/datai-generator/src/main/resources/vm/java/controller.java.vm +++ b/datai-models/datai-generator/src/main/resources/vm/java/controller.java.vm @@ -55,7 +55,7 @@ public class ${ClassName}Controller extends BaseController startPage(); List<${ClassName}> list = ${className}Service.select${ClassName}List(${ClassName}Dto.toObj(${className}Dto)); List<${ClassName}Vo> voList = list.stream().map(${ClassName}Vo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTable(voList,PageUtils.getTotal(list)); } #elseif($table.tree) public AjaxResult list(${ClassName}Dto ${className}Dto) diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/controller/DataiSfLoginHistoryController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/controller/DataiSfLoginHistoryController.java index 470709d7..67bb8786 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/controller/DataiSfLoginHistoryController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/controller/DataiSfLoginHistoryController.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.stream.Collectors; import com.datai.auth.model.vo.DataiSfLoginHistoryVo; +import com.datai.common.utils.PageUtils; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; @@ -52,7 +53,7 @@ public class DataiSfLoginHistoryController extends BaseController startPage(); List list = dataiSfLoginHistoryService.selectDataiSfLoginHistoryList(DataiSfLoginHistoryDto.toObj(dataiSfLoginHistoryDto)); List voList = list.stream().map(DataiSfLoginHistoryVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/controller/DataiSfLoginSessionController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/controller/DataiSfLoginSessionController.java index 16b757d9..9acc16f0 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/controller/DataiSfLoginSessionController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/controller/DataiSfLoginSessionController.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.stream.Collectors; import com.datai.auth.model.vo.DataiSfLoginSessionVo; +import com.datai.common.utils.PageUtils; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; @@ -52,7 +53,7 @@ public class DataiSfLoginSessionController extends BaseController startPage(); List list = dataiSfLoginSessionService.selectDataiSfLoginSessionList(DataiSfLoginSessionDto.toObj(dataiSfLoginSessionDto)); List voList = list.stream().map(DataiSfLoginSessionVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/model/domain/SalesforceLoginResult.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/model/domain/SalesforceLoginResult.java index 926eaa0a..3e499000 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/model/domain/SalesforceLoginResult.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/model/domain/SalesforceLoginResult.java @@ -20,7 +20,8 @@ public class SalesforceLoginResult implements Serializable { private String refreshToken; // 仅在 OAuth 流程中存在 private String tokenType = "Bearer"; private long expiresIn; // 过期时间(秒) - private long loginTimestamp = System.currentTimeMillis(); // 记录登录时间点 + private long loginTimestamp; // 记录登录时间点 + private long expirationTimestamp; // 记录过期时间点(登录时间 + 过期时长) /** 环境信息 */ private String instanceUrl; // API 访问基础地址 @@ -49,9 +50,10 @@ public class SalesforceLoginResult implements Serializable { * 提前 5 分钟判断为过期,以预留网络传输时间 */ public boolean isSessionExpired() { - if (expiresIn <= 0) return false; // 如果永不过期 - long bufferMillis = 300 * 1000; // 5分钟缓冲 - return System.currentTimeMillis() > (loginTimestamp + (expiresIn * 1000) - bufferMillis); + if (expiresIn <= 0) return false; + if (expirationTimestamp <= 0) return false; + long bufferMillis = 300 * 1000; + return System.currentTimeMillis() > (expirationTimestamp - bufferMillis); } // --- 增强的 Getter 和 Setter --- @@ -110,6 +112,12 @@ public class SalesforceLoginResult implements Serializable { public String getTimeZone() { return timeZone; } public void setTimeZone(String timeZone) { this.timeZone = timeZone; } + public long getLoginTimestamp() { return loginTimestamp; } + public void setLoginTimestamp(long loginTimestamp) { this.loginTimestamp = loginTimestamp; } + + public long getExpirationTimestamp() { return expirationTimestamp; } + public void setExpirationTimestamp(long expirationTimestamp) { this.expirationTimestamp = expirationTimestamp; } + @Override public String toString() { return "SalesforceLoginResult{" + diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/LegacyCredentialLoginStrategy.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/LegacyCredentialLoginStrategy.java index bda3f0b0..3f8ee8d4 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/LegacyCredentialLoginStrategy.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/LegacyCredentialLoginStrategy.java @@ -550,6 +550,12 @@ public class LegacyCredentialLoginStrategy implements LoginStrategy { // 4. 后置处理:设置默认过期时间和 Token 类型 fillDefaultExpiration(result); + + // 5. 设置登录时间戳和过期时间点 + long currentTimestamp = System.currentTimeMillis(); + result.setLoginTimestamp(currentTimestamp); + result.setExpirationTimestamp(currentTimestamp + (result.getExpiresIn() * 1000)); + return result; } @@ -617,7 +623,7 @@ public class LegacyCredentialLoginStrategy implements LoginStrategy { result.setTokenType("Bearer"); } - long expiresIn = 7200L; // 默认值 + long expiresIn = 6900L; // 默认值 if (result.getExpiresIn() == 0) { try { @@ -627,7 +633,7 @@ public class LegacyCredentialLoginStrategy implements LoginStrategy { .map(c -> CacheUtils.get(salesforceConfigCacheManager.getEnvironmentCacheKey(), "salesforce.session.timeout", String.class)) .filter(StringUtils::isNumeric) // 确保是数字 .map(Long::parseLong) - .orElse(7200L); + .orElse(6900L); } catch (Exception e) { logger.warn("读取Salesforce会话超时配置失败,使用默认值: 7200", e); } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/OAuth2LoginStrategy.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/OAuth2LoginStrategy.java index 61461936..70bf9530 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/OAuth2LoginStrategy.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/OAuth2LoginStrategy.java @@ -340,6 +340,11 @@ public class OAuth2LoginStrategy implements LoginStrategy { result.setRefreshToken(jsonResponse.get("refresh_token").asText()); } + // 设置登录时间戳和过期时间点 + long currentTimestamp = System.currentTimeMillis(); + result.setLoginTimestamp(currentTimestamp); + result.setExpirationTimestamp(currentTimestamp + (result.getExpiresIn() * 1000)); + return result; } @@ -377,6 +382,11 @@ public class OAuth2LoginStrategy implements LoginStrategy { result.setOrganizationId(jsonResponse.get("organization_id").asText()); } + // 设置登录时间戳和过期时间点 + long currentTimestamp = System.currentTimeMillis(); + result.setLoginTimestamp(currentTimestamp); + result.setExpirationTimestamp(currentTimestamp + (result.getExpiresIn() * 1000)); + return result; } @@ -461,6 +471,11 @@ public class OAuth2LoginStrategy implements LoginStrategy { STATE_CODE_VERIFIER_MAP.remove(state); STATE_TIMESTAMP_MAP.remove(state); + // 设置登录时间戳和过期时间点 + long currentTimestamp = System.currentTimeMillis(); + result.setLoginTimestamp(currentTimestamp); + result.setExpirationTimestamp(currentTimestamp + (result.getExpiresIn() * 1000)); + return result; } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/SalesforceCliLoginStrategy.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/SalesforceCliLoginStrategy.java index e34bc89c..10c89cdd 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/SalesforceCliLoginStrategy.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/SalesforceCliLoginStrategy.java @@ -56,6 +56,11 @@ public class SalesforceCliLoginStrategy implements LoginStrategy { // 3. 解析CLI输出,获取Session信息 result = parseCliOutput(output); result.setSuccess(true); + + // 4. 设置登录时间戳和过期时间点 + long currentTimestamp = System.currentTimeMillis(); + result.setLoginTimestamp(currentTimestamp); + result.setExpirationTimestamp(currentTimestamp + (result.getExpiresIn() * 1000)); } catch (Exception e) { logger.error("Salesforce CLI登录失败: {}", e.getMessage(), e); result.setSuccess(false); diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/SessionIdLoginStrategy.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/SessionIdLoginStrategy.java index a5f57f56..972e1a85 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/SessionIdLoginStrategy.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/strategy/impl/SessionIdLoginStrategy.java @@ -80,6 +80,11 @@ public class SessionIdLoginStrategy implements LoginStrategy { result.setTokenType("Bearer"); result.setExpiresIn(getSessionTimeout(config)); + // 设置登录时间戳和过期时间点 + long currentTimestamp = System.currentTimeMillis(); + result.setLoginTimestamp(currentTimestamp); + result.setExpirationTimestamp(currentTimestamp + (result.getExpiresIn() * 1000)); + logger.info("Session ID登录成功,用户ID: {}", userInfo.get("user_id")); } catch (Exception e) { diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationApiCallLogController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationApiCallLogController.java index 503c4e7f..f6acd95e 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationApiCallLogController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationApiCallLogController.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.vo.DataiIntegrationApiCallLogVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -56,7 +57,7 @@ public class DataiIntegrationApiCallLogController extends BaseController List voList = list.stream() .map(DataiIntegrationApiCallLogVo::objToVo) .collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationBatchController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationBatchController.java index be726203..5ec76cb4 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationBatchController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationBatchController.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.vo.DataiIntegrationBatchVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -56,7 +57,7 @@ public class DataiIntegrationBatchController extends BaseController List voList = list.stream() .map(DataiIntegrationBatchVo::objToVo) .collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationBatchHistoryController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationBatchHistoryController.java index e2c0f25c..2eecf140 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationBatchHistoryController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationBatchHistoryController.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.HashMap; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.vo.DataiIntegrationBatchHistoryVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -57,7 +58,7 @@ public class DataiIntegrationBatchHistoryController extends BaseController List voList = list.stream() .map(DataiIntegrationBatchHistoryVo::objToVo) .collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationFieldController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationFieldController.java index 6e1e58f1..f19e1009 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationFieldController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationFieldController.java @@ -3,6 +3,7 @@ package com.datai.integration.controller; import java.util.List; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.vo.DataiIntegrationFieldVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -55,7 +56,7 @@ public class DataiIntegrationFieldController extends BaseController List voList = list.stream() .map(DataiIntegrationFieldVo::objToVo) .collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationFilterLookupController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationFilterLookupController.java index 67311456..e75a8e81 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationFilterLookupController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationFilterLookupController.java @@ -3,6 +3,7 @@ package com.datai.integration.controller; import java.util.List; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.vo.DataiIntegrationFilterLookupVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -53,7 +54,7 @@ public class DataiIntegrationFilterLookupController extends BaseController DataiIntegrationFilterLookup dataiIntegrationFilterLookup = DataiIntegrationFilterLookupDto.toObj(dataiIntegrationFilterLookupDto); List list = dataiIntegrationFilterLookupService.selectDataiIntegrationFilterLookupList(dataiIntegrationFilterLookup); List voList = list.stream().map(DataiIntegrationFilterLookupVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationMetadataChangeController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationMetadataChangeController.java index f8b0542d..d99cd6fa 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationMetadataChangeController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationMetadataChangeController.java @@ -3,6 +3,7 @@ package com.datai.integration.controller; import java.util.*; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.domain.DataiIntegrationMetadataChange; import com.datai.integration.model.vo.DataiIntegrationMetadataChangeVo; import jakarta.servlet.http.HttpServletResponse; @@ -55,7 +56,7 @@ public class DataiIntegrationMetadataChangeController extends BaseController DataiIntegrationMetadataChange dataiIntegrationMetadataChange = DataiIntegrationMetadataChangeDto.toObj(dataiIntegrationMetadataChangeDto); List list = dataiIntegrationMetadataChangeService.selectDataiIntegrationMetadataChangeList(dataiIntegrationMetadataChange); List voList = list.stream().map(DataiIntegrationMetadataChangeVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** @@ -70,7 +71,7 @@ public class DataiIntegrationMetadataChangeController extends BaseController DataiIntegrationMetadataChange dataiIntegrationMetadataChange = DataiIntegrationMetadataChangeDto.toObj(dataiIntegrationMetadataChangeDto); List list = dataiIntegrationMetadataChangeService.selectUnsyncedMetadataChangeList(dataiIntegrationMetadataChange); List voList = list.stream().map(DataiIntegrationMetadataChangeVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** @@ -233,12 +234,21 @@ public class DataiIntegrationMetadataChangeController extends BaseController /** * 全对象元数据变更拉取 * 从Salesforce拉取所有对象的元数据变更信息并记录到元数据变更表中 + * 该方法会: + * 1. 连接到Salesforce获取所有对象的元数据 + * 2. 比较现有数据库中的对象元数据 + * 3. 记录对象级别的变更(新增、修改、删除) + * 4. 记录字段级别的变更(新增、修改、删除) + * 5. 检测并记录已从Salesforce中删除的对象 + * * 表的变更新增需要满足以下任一条件: * - isQueryable (可查询) * - isCreateable (可创建) * - isUpdateable (可更新) * - isDeletable (可删除) * 字段的变更新增无限制 + * + * @return 包含拉取结果的AjaxResult,包含对象变更数量和字段变更数量 */ @Operation(summary = "全对象元数据变更拉取") @PreAuthorize("@ss.hasPermi('integration:change:pullAll')") @@ -247,9 +257,11 @@ public class DataiIntegrationMetadataChangeController extends BaseController public AjaxResult pullAllMetadataChanges() { try { + // 调用服务层方法执行全对象元数据变更拉取 Map result = dataiIntegrationMetadataChangeService.pullAllMetadataChanges(); return success(result); } catch (Exception e) { + // 记录拉取过程中发生的异常 log.error("全对象元数据变更拉取时发生异常", e); return error("全对象元数据变更拉取失败: " + e.getMessage()); } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationObjectController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationObjectController.java index fe285f76..ba051d3f 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationObjectController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationObjectController.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.vo.DataiIntegrationObjectVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -54,7 +55,7 @@ public class DataiIntegrationObjectController extends BaseController DataiIntegrationObject dataiIntegrationObject = DataiIntegrationObjectDto.toObj(dataiIntegrationObjectDto); List list = dataiIntegrationObjectService.selectDataiIntegrationObjectList(dataiIntegrationObject); List voList = list.stream().map(DataiIntegrationObjectVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationPicklistController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationPicklistController.java index 41b1674a..ba0909d7 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationPicklistController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationPicklistController.java @@ -3,6 +3,7 @@ package com.datai.integration.controller; import java.util.List; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.vo.DataiIntegrationPicklistVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -53,7 +54,7 @@ public class DataiIntegrationPicklistController extends BaseController DataiIntegrationPicklist dataiIntegrationPicklist = DataiIntegrationPicklistDto.toObj(dataiIntegrationPicklistDto); List list = dataiIntegrationPicklistService.selectDataiIntegrationPicklistList(dataiIntegrationPicklist); List voList = list.stream().map(DataiIntegrationPicklistVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationRateLimitController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationRateLimitController.java index 3c4301c0..e0a1280f 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationRateLimitController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/controller/DataiIntegrationRateLimitController.java @@ -3,6 +3,7 @@ package com.datai.integration.controller; import java.util.List; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.integration.model.vo.DataiIntegrationRateLimitVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -53,7 +54,7 @@ public class DataiIntegrationRateLimitController extends BaseController DataiIntegrationRateLimit dataiIntegrationRateLimit = DataiIntegrationRateLimitDto.toObj(dataiIntegrationRateLimitDto); List list = dataiIntegrationRateLimitService.selectDataiIntegrationRateLimitList(dataiIntegrationRateLimit); List voList = list.stream().map(DataiIntegrationRateLimitVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/core/SessionManager.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/core/SessionManager.java index d3c6bc6a..f675fbb4 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/core/SessionManager.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/core/SessionManager.java @@ -1,5 +1,8 @@ package com.datai.integration.core; +import com.datai.auth.model.domain.DataiSfLoginHistory; +import com.datai.auth.service.IDataiSfLoginHistoryService; +import com.datai.common.core.domain.AjaxResult; import com.datai.salesforce.common.constant.SalesforceConfigConstants; import com.datai.auth.model.domain.SalesforceLoginResult; import com.datai.auth.service.ISalesforceLoginService; @@ -21,6 +24,9 @@ public class SessionManager { @Autowired private ISalesforceLoginService salesforceLoginService; + @Autowired + private IDataiSfLoginHistoryService dataiSfLoginHistoryService; + /** * 获取当前Salesforce会话信息 * 如果会话无效,则自动尝试重新登录 @@ -34,20 +40,13 @@ public class SessionManager { try { SalesforceLoginResult loginResult = CacheUtils.get(SalesforceConfigConstants.CACHE_NAME, SalesforceConfigConstants.CURRENT_RESULT, SalesforceLoginResult.class); - if (loginResult != null && loginResult.isSuccess() && !loginResult.isSessionExpired()) { + if (isLoginResultValid(loginResult)) { log.info("获取会话信息成功,访问令牌前缀: {}", loginResult.getSessionId() != null ? loginResult.getSessionId().substring(0, Math.min(10, loginResult.getSessionId().length())) : "null"); return loginResult; } - if (loginResult == null) { - log.warn("会话信息为null,尝试自动重新登录"); - } else if (!loginResult.isSuccess()) { - log.warn("登录状态为失败,尝试自动重新登录,错误信息: {}", loginResult.getErrorMessage()); - } else if (loginResult.isSessionExpired()) { - log.warn("Session已过期,尝试自动重新登录"); - } - + logSessionStatus(loginResult); return autoLogin(); } catch (Exception e) { @@ -66,7 +65,15 @@ public class SessionManager { log.info("开始执行自动重新登录"); try { - SalesforceLoginResult result = salesforceLoginService.autoLogin(null); + DataiSfLoginHistory latestLoginHistory = dataiSfLoginHistoryService.selectLatestSuccessLoginHistory(); + + if (latestLoginHistory == null) { + throw new RuntimeException("未找到登录历史记录" ); + } + + Long historyId = latestLoginHistory.getId(); + + SalesforceLoginResult result = salesforceLoginService.autoLogin(historyId); if (result == null || !result.isSuccess()) { String errorMsg = result != null ? result.getErrorMessage() : "登录结果为空"; @@ -126,36 +133,39 @@ public class SessionManager { log.info("检查当前Salesforce会话信息是否有效"); try { - SalesforceLoginResult loginResult = CacheUtils.get(SalesforceConfigConstants.CACHE_NAME, SalesforceConfigConstants.CURRENT_RESULT, SalesforceLoginResult.class); - - if (loginResult == null) { - log.warn("会话信息为null,会话无效"); - return false; - } - - if (!loginResult.isSuccess()) { - log.warn("登录状态为失败,会话无效"); - return false; - } - - if (loginResult.getSessionId() == null || loginResult.getSessionId().trim().isEmpty()) { - log.warn("Session ID为空,会话无效"); - return false; - } - - if (loginResult.isSessionExpired()) { - log.warn("Session已过期,会话无效"); - return false; - } - - log.info("会话信息有效,Session ID前缀: {}", - loginResult.getSessionId() != null ? - loginResult.getSessionId().substring(0, Math.min(10, loginResult.getSessionId().length())) : "null"); - return true; + SalesforceLoginResult loginResult = getCurrentLoginResult(); + return isLoginResultValid(loginResult); } catch (Exception e) { log.error("检查会话信息有效性时发生异常: {}", e.getMessage(), e); return false; } } + + /** + * 判断登录结果是否有效 + * + * @param loginResult 登录结果 + * @return 如果有效返回true,否则返回false + */ + private boolean isLoginResultValid(SalesforceLoginResult loginResult) { + return loginResult != null + && loginResult.isSuccess() + && !loginResult.isSessionExpired(); + } + + /** + * 记录会话状态日志 + * + * @param loginResult 登录结果 + */ + private void logSessionStatus(SalesforceLoginResult loginResult) { + if (loginResult == null) { + log.warn("会话信息为null,尝试自动重新登录"); + } else if (!loginResult.isSuccess()) { + log.warn("登录状态为失败,尝试自动重新登录,错误信息: {}", loginResult.getErrorMessage()); + } else if (loginResult.isSessionExpired()) { + log.warn("Session已过期,尝试自动重新登录"); + } + } } \ No newline at end of file diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/factory/AbstractConnectionFactory.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/factory/AbstractConnectionFactory.java index 2df0b308..5de5ef73 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/factory/AbstractConnectionFactory.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/factory/AbstractConnectionFactory.java @@ -30,10 +30,18 @@ public abstract class AbstractConnectionFactory implements ISalesforceConnect String configKey = getConfigKey(); T connection = connectionCache.get(configKey); + + if (connection != null && !sessionManager.isSessionValid()) { + log.warn("检测到Session已过期,清除缓存的{}连接", getConnectionType()); + connectionCache.remove(configKey); + connection = null; + } + if (connection == null) { lock.lock(); try { connection = connectionCache.get(configKey); + if (connection == null) { connection = createConnection(); connectionCache.put(configKey, connection); diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/service/impl/DataiIntegrationMetadataChangeServiceImpl.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/service/impl/DataiIntegrationMetadataChangeServiceImpl.java index 14e78702..df8a1277 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/service/impl/DataiIntegrationMetadataChangeServiceImpl.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/service/impl/DataiIntegrationMetadataChangeServiceImpl.java @@ -692,17 +692,21 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat @Override public Map pullAllMetadataChanges() { + // 初始化返回结果 Map result = new HashMap<>(); try { log.info("开始执行全对象元数据变更拉取"); + // 建立与Salesforce的连接,使用重试机制确保连接成功 PartnerConnection connection = retryOperation(() -> soapConnectionFactory.getConnection(), 3, 1000); log.info("成功获取Salesforce SOAP连接"); + // 获取Salesforce中所有对象的全局描述信息 DescribeGlobalResult globalDescribe = connection.describeGlobal(); DescribeGlobalSObjectResult[] sObjects = globalDescribe.getSobjects(); + // 检查是否获取到任何对象 if (sObjects == null || sObjects.length == 0) { log.warn("未获取到任何Salesforce对象"); result.put("success", true); @@ -714,68 +718,93 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat log.info("从Salesforce获取到 {} 个对象", sObjects.length); + // 用于存储已同步的对象API名称,以便后续检测删除的对象 Set syncedObjectApis = new HashSet<>(); - int objectChangeCount = 0; - int fieldChangeCount = 0; + int objectChangeCount = 0; // 记录对象变更数量 + int fieldChangeCount = 0; // 记录字段变更数量 + // 遍历所有Salesforce对象 for (DescribeGlobalSObjectResult sObject : sObjects) { try { String objectApi = sObject.getName(); + // 检查对象是否需要同步(满足查询、创建、更新或删除任一条件) if (shouldSyncObject(sObject)) { + // 获取对象的详细描述信息 DescribeSObjectResult objDetail = connection.describeSObject(objectApi); + // 查询数据库中是否已存在该对象 DataiIntegrationObject queryObject = new DataiIntegrationObject(); queryObject.setApi(objectApi); List existingObjects = dataiIntegrationObjectService.selectDataiIntegrationObjectList(queryObject); + // 构建当前对象的元数据信息 DataiIntegrationObject newObject = buildObjectMetadata(objDetail); - boolean hasFieldChange = false; + boolean hasFieldChange = false; // 标记该对象是否有字段变更 + // 判断对象是否为新增 if (existingObjects.isEmpty()) { + // 新增对象到数据库 dataiIntegrationObjectService.insertDataiIntegrationObject(newObject); + // 记录对象新增变更 recordObjectChange(newObject, null, "INSERT"); objectChangeCount++; log.info("新增对象并记录变更: {}", objectApi); + // 重新查询以获取新插入对象的ID existingObjects = dataiIntegrationObjectService.selectDataiIntegrationObjectList(queryObject); if (!existingObjects.isEmpty()) { newObject.setId(existingObjects.get(0).getId()); } - } else { - DataiIntegrationObject existingObject = existingObjects.get(0); - newObject.setId(existingObject.getId()); - List changedFields = compareObjects(existingObject, newObject); - if (!changedFields.isEmpty()) { - recordObjectChange(newObject, existingObject, "UPDATE"); - objectChangeCount++; - log.debug("记录对象更新: {} - 变更: {}", objectApi, String.join(", ", changedFields)); - } + // 将对象API添加到已同步集合中 + syncedObjectApis.add(objectApi); + // 对象新增后直接跳过字段比较 + continue; } + // 对象已存在,比较新旧对象的差异 + DataiIntegrationObject existingObject = existingObjects.get(0); + newObject.setId(existingObject.getId()); + List changedFields = compareObjects(existingObject, newObject); + if (!changedFields.isEmpty()) { + // 记录对象更新变更 + recordObjectChange(newObject, existingObject, "UPDATE"); + objectChangeCount++; + log.debug("记录对象更新: {} - 变更: {}", objectApi, String.join(", ", changedFields)); + } + + // 将对象API添加到已同步集合中 syncedObjectApis.add(objectApi); + // 查询数据库中该对象的现有字段 DataiIntegrationField queryField = new DataiIntegrationField(); queryField.setApi(objectApi); List existingFields = dataiIntegrationFieldService.selectDataiIntegrationFieldList(queryField); + // 将现有字段转换为Map,便于快速查找 Map existingFieldMap = new HashMap<>(); for (DataiIntegrationField existingField : existingFields) { existingFieldMap.put(existingField.getField(), existingField); } + // 遍历Salesforce中的所有字段 for (Field field : objDetail.getFields()) { + // 构建当前字段的元数据信息 DataiIntegrationField newField = buildFieldMetadata(objectApi, field); + // 检查字段是否已存在 DataiIntegrationField existingField = existingFieldMap.get(field.getName()); if (existingField == null) { + // 字段为新增 recordFieldChange(objectApi, newObject.getLabel(), newField.getField(), newField.getLabel(), null, "INSERT", "新增字段", newObject.getIsCustom()); fieldChangeCount++; hasFieldChange = true; log.debug("记录字段新增: {}.{}", objectApi, field.getName()); } else { + // 字段已存在,比较新旧字段的差异 List changedFieldProps = compareFields(existingField, newField); if (!changedFieldProps.isEmpty()) { + // 记录字段更新变更 recordFieldChange(objectApi, newObject.getLabel(), newField.getField(), newField.getLabel(), "字段属性变更: " + String.join(", ", changedFieldProps), "UPDATE", "字段属性更新", newObject.getIsCustom()); @@ -787,6 +816,7 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat } } + // 检查是否有已删除的字段(在数据库中存在但在Salesforce中不存在) for (DataiIntegrationField existingField : existingFields) { boolean fieldExists = false; for (Field field : objDetail.getFields()) { @@ -796,6 +826,7 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat } } if (!fieldExists) { + // 记录字段删除变更 recordFieldChange(objectApi, newObject.getLabel(), existingField.getField(), existingField.getLabel(), "字段已从Salesforce中删除", "DELETE", "字段删除", newObject.getIsCustom()); @@ -805,23 +836,27 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat } } + // 如果该对象有字段变更,禁用其增量更新状态 if (hasFieldChange && newObject.getId() != null) { DataiIntegrationObject updateObject = new DataiIntegrationObject(); updateObject.setId(newObject.getId()); - updateObject.setIsIncremental(false); + updateObject.setIsIncremental(false); // 禁用增量更新 dataiIntegrationObjectService.updateDataiIntegrationObject(updateObject); log.info("检测到字段变更,已禁用对象 {} 的增量更新状态", objectApi); } } } catch (Exception e) { + // 记录处理单个对象时的错误,但继续处理其他对象 log.error("处理对象 {} 时出错: {}", sObject.getName(), e.getMessage(), e); } } + // 检查并记录已从Salesforce中删除的对象 checkDeletedObjectsForMetadata(syncedObjectApis); log.info("全对象元数据变更拉取完成,对象变更: {} 个,字段变更: {} 个", objectChangeCount, fieldChangeCount); + // 设置返回结果 result.put("success", true); result.put("objectChangeCount", objectChangeCount); result.put("fieldChangeCount", fieldChangeCount); @@ -829,6 +864,7 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat return result; } catch (Exception e) { + // 记录整个拉取过程中的异常 log.error("全对象元数据变更拉取时发生异常", e); result.put("success", false); result.put("message", "全对象元数据变更拉取失败: " + e.getMessage()); @@ -836,114 +872,218 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat } } + /** + * 判断对象是否需要同步 + * 只有满足以下任一条件的对象才需要同步: + * - 可查询 (isQueryable) + * - 可创建 (isCreateable) + * - 可更新 (isUpdateable) + * - 可删除 (isDeletable) + * + * @param sObject Salesforce对象描述信息 + * @return 如果对象需要同步返回true,否则返回false + */ private boolean shouldSyncObject(DescribeGlobalSObjectResult sObject) { return sObject.isQueryable() || sObject.isCreateable() || sObject.isUpdateable() || sObject.isDeletable(); } + /** + * 根据Salesforce对象描述信息构建对象元数据实体 + * 将Salesforce的DescribeSObjectResult转换为DataiIntegrationObject实体 + * + * @param objDetail Salesforce对象的详细描述信息 + * @return 构建的DataiIntegrationObject实体,如果构建失败返回null + */ private DataiIntegrationObject buildObjectMetadata(DescribeSObjectResult objDetail) { try { + // 创建对象元数据实体 DataiIntegrationObject object = new DataiIntegrationObject(); + // 设置对象API名称 object.setApi(objDetail.getName()); + // 设置对象标签名称 object.setLabel(objDetail.getLabel()); + // 设置对象复数标签名称 object.setLabelPlural(objDetail.getLabelPlural()); + // 设置对象键前缀 object.setKeyPrefix(objDetail.getKeyPrefix()); + // 设置是否为自定义对象 object.setIsCustom(objDetail.isCustom()); + // 设置是否为自定义设置对象 object.setIsCustomSetting(objDetail.isCustomSetting()); + // 设置是否可查询 object.setIsQueryable(objDetail.isQueryable()); + // 设置是否可创建 object.setIsCreateable(objDetail.isCreateable()); + // 设置是否可更新 object.setIsUpdateable(objDetail.isUpdateable()); + // 设置是否可删除 object.setIsDeletable(objDetail.isDeletable()); + // 设置是否可复制 object.setIsReplicateable(objDetail.isReplicateable()); + // 设置是否可检索 object.setIsRetrieveable(objDetail.isRetrieveable()); + // 设置是否可搜索 object.setIsSearchable(objDetail.isSearchable()); - object.setIsWork(true); - object.setIsIncremental(true); + // 设置是否工作状态(启用) + object.setIsWork(false); + // 设置是否启用增量更新 + object.setIsIncremental(false); + // 设置最后同步时间 object.setLastSyncDate(LocalDateTime.now()); return object; } catch (Exception e) { + // 记录构建对象元数据时的错误 log.error("构建对象 {} 元数据时出错: {}", objDetail.getName(), e.getMessage(), e); return null; } } + /** + * 根据Salesforce字段描述信息构建字段元数据实体 + * 将Salesforce的Field对象转换为DataiIntegrationField实体 + * + * @param objectApi 所属对象的API名称 + * @param field Salesforce字段描述信息 + * @return 构建的DataiIntegrationField实体 + */ private DataiIntegrationField buildFieldMetadata(String objectApi, Field field) { + // 创建字段元数据实体 DataiIntegrationField fieldEntity = new DataiIntegrationField(); + // 设置所属对象API名称 fieldEntity.setApi(objectApi); + // 设置字段名称 fieldEntity.setField(field.getName()); + // 设置字段标签 fieldEntity.setLabel(field.getLabel()); + // 设置是否可创建 fieldEntity.setIsCreateable(field.isCreateable()); + // 设置是否可为空 fieldEntity.setIsNillable(field.isNillable()); + // 设置是否可更新 fieldEntity.setIsUpdateable(field.isUpdateable()); + // 设置是否在创建时有默认值 fieldEntity.setIsDefaultedOnCreate(field.isDefaultedOnCreate()); + // 设置是否唯一 fieldEntity.setIsUnique(field.isUnique()); + // 设置是否可过滤 fieldEntity.setIsFilterable(field.isFilterable()); + // 设置是否可排序 fieldEntity.setIsSortable(field.isSortable()); + // 设置是否可聚合 fieldEntity.setIsAggregatable(field.isAggregatable()); + // 设置是否可分组 fieldEntity.setIsGroupable(field.isGroupable()); + // 设置是否为多态外键 fieldEntity.setIsPolymorphicForeignKey(field.isPolymorphicForeignKey()); + // 设置多态外键类型字段 fieldEntity.setPolymorphicForeignField(field.getName() + "_type"); + // 设置是否为外部ID fieldEntity.setIsExternalId(field.isExternalId()); + // 设置是否为自定义字段 fieldEntity.setIsCustom(field.isCustom()); + // 设置是否为计算字段 fieldEntity.setIsCalculated(field.isCalculated()); + // 设置是否为自动编号字段 fieldEntity.setIsAutoNumber(field.isAutoNumber()); + // 设置是否区分大小写 fieldEntity.setIsCaseSensitive(field.isCaseSensitive()); + // 设置是否为加密字段 fieldEntity.setIsEncrypted(field.isEncrypted()); + // 设置是否为HTML格式化字段 fieldEntity.setIsHtmlFormatted(field.isHtmlFormatted()); + // 设置是否为ID查找字段 fieldEntity.setIsIdLookup(field.isIdLookup()); + // 设置是否为权限字段 fieldEntity.setIsPermissionable(field.isPermissionable()); + // 设置是否为受限选择列表 fieldEntity.setIsRestrictedPicklist(field.isRestrictedPicklist()); + // 设置是否为受限删除 fieldEntity.setIsRestrictedDelete(field.isRestrictedDelete()); + // 设置写入是否需要主记录读取权限 fieldEntity.setIsWriteRequiresMasterRead(field.isWriteRequiresMasterRead()); + // 设置字段数据类型 fieldEntity.setFieldDataType(field.getType() != null ? field.getType().toString() : null); + // 设置字段长度 fieldEntity.setFieldLength(field.getLength()); + // 设置字段精度 fieldEntity.setFieldPrecision(field.getPrecision()); + // 设置字段小数位数 fieldEntity.setFieldScale(field.getScale()); + // 设置字段字节长度 fieldEntity.setFieldByteLength(field.getByteLength()); + // 设置默认值公式 fieldEntity.setDefaultValue(field.getDefaultValueFormula()); + // 设置计算公式 fieldEntity.setCalculatedFormula(field.getCalculatedFormula()); + // 设置内联帮助文本 fieldEntity.setInlineHelpText(field.getInlineHelpText()); + // 设置关系名称 fieldEntity.setRelationshipName(field.getRelationshipName()); + // 设置关系顺序 fieldEntity.setRelationshipOrder(field.getRelationshipOrder()); + // 设置引用目标字段 fieldEntity.setReferenceTargetField(field.getReferenceTo() != null && field.getReferenceTo().length > 0 ? field.getReferenceTo()[0] : null); + // 如果字段有引用目标,则设置引用目标 if (field.getReferenceTo() != null && field.getReferenceTo().length > 0) { + // 设置引用目标(多个引用目标用逗号分隔) fieldEntity.setReferenceTo(String.join(",", field.getReferenceTo())); + // 设置第一个引用目标字段 fieldEntity.setReferenceTargetField(field.getReferenceTo()[0]); } return fieldEntity; } + /** + * 比较两个对象元数据的差异 + * 检查新旧对象元数据之间的不同属性,并返回有变化的属性列表 + * + * @param oldObject 旧的对象元数据 + * @param newObject 新的对象元数据 + * @return 有变化的属性名称列表 + */ private List compareObjects(DataiIntegrationObject oldObject, DataiIntegrationObject newObject) { + // 初始化变更字段列表 List changedFields = new ArrayList<>(); + // 比较标签名称 if (!Objects.equals(oldObject.getLabel(), newObject.getLabel())) { changedFields.add("label"); } + // 比较复数标签名称 if (!Objects.equals(oldObject.getLabelPlural(), newObject.getLabelPlural())) { changedFields.add("labelPlural"); } + // 比较键前缀 if (!Objects.equals(oldObject.getKeyPrefix(), newObject.getKeyPrefix())) { changedFields.add("keyPrefix"); } + // 比较是否可查询状态 if (!Objects.equals(oldObject.getIsQueryable(), newObject.getIsQueryable())) { changedFields.add("isQueryable"); } + // 比较是否可创建状态 if (!Objects.equals(oldObject.getIsCreateable(), newObject.getIsCreateable())) { changedFields.add("isCreateable"); } + // 比较是否可更新状态 if (!Objects.equals(oldObject.getIsUpdateable(), newObject.getIsUpdateable())) { changedFields.add("isUpdateable"); } + // 比较是否可删除状态 if (!Objects.equals(oldObject.getIsDeletable(), newObject.getIsDeletable())) { changedFields.add("isDeletable"); } + // 比较是否可复制状态 if (!Objects.equals(oldObject.getIsReplicateable(), newObject.getIsReplicateable())) { changedFields.add("isReplicateable"); } + // 比较是否可检索状态 if (!Objects.equals(oldObject.getIsRetrieveable(), newObject.getIsRetrieveable())) { changedFields.add("isRetrieveable"); } + // 比较是否可搜索状态 if (!Objects.equals(oldObject.getIsSearchable(), newObject.getIsSearchable())) { changedFields.add("isSearchable"); } @@ -951,105 +1091,147 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat return changedFields; } + /** + * 比较两个字段元数据的差异 + * 检查新旧字段元数据之间的不同属性,并返回有变化的属性列表 + * + * @param oldField 旧的字段元数据 + * @param newField 新的字段元数据 + * @return 有变化的属性名称列表 + */ private List compareFields(DataiIntegrationField oldField, DataiIntegrationField newField) { + // 初始化变更字段列表 List changedFields = new ArrayList<>(); + // 比较标签名称 if (!Objects.equals(oldField.getLabel(), newField.getLabel())) { changedFields.add("label"); } + // 比较是否可创建状态 if (!Objects.equals(oldField.getIsCreateable(), newField.getIsCreateable())) { changedFields.add("isCreateable"); } + // 比较是否可为空状态 if (!Objects.equals(oldField.getIsNillable(), newField.getIsNillable())) { changedFields.add("isNillable"); } + // 比较是否可更新状态 if (!Objects.equals(oldField.getIsUpdateable(), newField.getIsUpdateable())) { changedFields.add("isUpdateable"); } + // 比较是否在创建时有默认值状态 if (!Objects.equals(oldField.getIsDefaultedOnCreate(), newField.getIsDefaultedOnCreate())) { changedFields.add("isDefaultedOnCreate"); } + // 比较是否唯一状态 if (!Objects.equals(oldField.getIsUnique(), newField.getIsUnique())) { changedFields.add("isUnique"); } + // 比较是否可过滤状态 if (!Objects.equals(oldField.getIsFilterable(), newField.getIsFilterable())) { changedFields.add("isFilterable"); } + // 比较是否可排序状态 if (!Objects.equals(oldField.getIsSortable(), newField.getIsSortable())) { changedFields.add("isSortable"); } + // 比较是否可聚合状态 if (!Objects.equals(oldField.getIsAggregatable(), newField.getIsAggregatable())) { changedFields.add("isAggregatable"); } + // 比较是否可分组状态 if (!Objects.equals(oldField.getIsGroupable(), newField.getIsGroupable())) { changedFields.add("isGroupable"); } + // 比较是否为多态外键状态 if (!Objects.equals(oldField.getIsPolymorphicForeignKey(), newField.getIsPolymorphicForeignKey())) { changedFields.add("isPolymorphicForeignKey"); } + // 比较是否为外部ID状态 if (!Objects.equals(oldField.getIsExternalId(), newField.getIsExternalId())) { changedFields.add("isExternalId"); } + // 比较是否为自定义字段状态 if (!Objects.equals(oldField.getIsCustom(), newField.getIsCustom())) { changedFields.add("isCustom"); } + // 比较是否为计算字段状态 if (!Objects.equals(oldField.getIsCalculated(), newField.getIsCalculated())) { changedFields.add("isCalculated"); } + // 比较是否为自动编号字段状态 if (!Objects.equals(oldField.getIsAutoNumber(), newField.getIsAutoNumber())) { changedFields.add("isAutoNumber"); } + // 比较是否区分大小写状态 if (!Objects.equals(oldField.getIsCaseSensitive(), newField.getIsCaseSensitive())) { changedFields.add("isCaseSensitive"); } + // 比较是否为加密字段状态 if (!Objects.equals(oldField.getIsEncrypted(), newField.getIsEncrypted())) { changedFields.add("isEncrypted"); } + // 比较是否为HTML格式化字段状态 if (!Objects.equals(oldField.getIsHtmlFormatted(), newField.getIsHtmlFormatted())) { changedFields.add("isHtmlFormatted"); } + // 比较是否为ID查找字段状态 if (!Objects.equals(oldField.getIsIdLookup(), newField.getIsIdLookup())) { changedFields.add("isIdLookup"); } + // 比较是否为权限字段状态 if (!Objects.equals(oldField.getIsPermissionable(), newField.getIsPermissionable())) { changedFields.add("isPermissionable"); } + // 比较是否为受限选择列表状态 if (!Objects.equals(oldField.getIsRestrictedPicklist(), newField.getIsRestrictedPicklist())) { changedFields.add("isRestrictedPicklist"); } + // 比较是否为受限删除状态 if (!Objects.equals(oldField.getIsRestrictedDelete(), newField.getIsRestrictedDelete())) { changedFields.add("isRestrictedDelete"); } + // 比较写入是否需要主记录读取权限状态 if (!Objects.equals(oldField.getIsWriteRequiresMasterRead(), newField.getIsWriteRequiresMasterRead())) { changedFields.add("isWriteRequiresMasterRead"); } + // 比较字段数据类型 if (!Objects.equals(oldField.getFieldDataType(), newField.getFieldDataType())) { changedFields.add("fieldDataType"); } + // 比较字段长度 if (!Objects.equals(oldField.getFieldLength(), newField.getFieldLength())) { changedFields.add("fieldLength"); } + // 比较字段精度 if (!Objects.equals(oldField.getFieldPrecision(), newField.getFieldPrecision())) { changedFields.add("fieldPrecision"); } + // 比较字段小数位数 if (!Objects.equals(oldField.getFieldScale(), newField.getFieldScale())) { changedFields.add("fieldScale"); } + // 比较字段字节长度 if (!Objects.equals(oldField.getFieldByteLength(), newField.getFieldByteLength())) { changedFields.add("fieldByteLength"); } + // 比较默认值公式 if (!Objects.equals(oldField.getDefaultValue(), newField.getDefaultValue())) { changedFields.add("defaultValue"); } + // 比较计算公式 if (!Objects.equals(oldField.getCalculatedFormula(), newField.getCalculatedFormula())) { changedFields.add("calculatedFormula"); } + // 比较内联帮助文本 if (!Objects.equals(oldField.getInlineHelpText(), newField.getInlineHelpText())) { changedFields.add("inlineHelpText"); } + // 比较关系名称 if (!Objects.equals(oldField.getRelationshipName(), newField.getRelationshipName())) { changedFields.add("relationshipName"); } + // 比较引用目标 if (!Objects.equals(oldField.getReferenceTo(), newField.getReferenceTo())) { changedFields.add("referenceTo"); } @@ -1057,102 +1239,189 @@ public class DataiIntegrationMetadataChangeServiceImpl implements IDataiIntegrat return changedFields; } + /** + * 记录对象元数据变更 + * 将对象级别的变更(新增、修改、删除)记录到元数据变更表中 + * + * @param newObject 新的对象元数据 + * @param oldObject 旧的对象元数据(新增操作时为null) + * @param operationType 操作类型(INSERT、UPDATE、DELETE) + */ private void recordObjectChange(DataiIntegrationObject newObject, DataiIntegrationObject oldObject, String operationType) { try { + // 创建元数据变更记录实体 DataiIntegrationMetadataChange metadataChange = new DataiIntegrationMetadataChange(); + // 设置变更类型为对象 metadataChange.setChangeType("OBJECT"); + // 设置操作类型(INSERT、UPDATE、DELETE) metadataChange.setOperationType(operationType); + // 设置对象API名称 metadataChange.setObjectApi(newObject.getApi()); + // 设置对象标签名称 metadataChange.setObjectLabel(newObject.getLabel()); + // 设置变更时间 metadataChange.setChangeTime(LocalDateTime.now()); + // 设置同步状态为未同步 metadataChange.setSyncStatus(false); + // 设置是否为自定义对象 metadataChange.setIsCustom(newObject.getIsCustom()); + // 如果是更新操作(oldObject不为null),则比较新旧对象的差异 if (oldObject != null) { List changedFields = compareObjects(oldObject, newObject); if (!changedFields.isEmpty()) { + // 如果有具体变更字段,则记录详细变更信息 metadataChange.setChangeReason("对象属性变更: " + String.join(", ", changedFields)); } else { + // 如果没有具体变更字段,则记录通用更新信息 metadataChange.setChangeReason("对象属性更新"); } } else { + // 如果是新增操作,则记录为新增对象 metadataChange.setChangeReason("新增对象"); } + // 设置变更用户为系统 metadataChange.setChangeUser("SYSTEM"); + // 插入元数据变更记录 insertDataiIntegrationMetadataChange(metadataChange); log.debug("记录对象变更成功: {} - {}", newObject.getApi(), operationType); } catch (Exception e) { + // 记录记录对象变更时的错误 log.error("记录对象变更失败: {} - {}", newObject.getApi(), e.getMessage(), e); } } + /** + * 记录字段元数据变更 + * 将字段级别的变更(新增、修改、删除)记录到元数据变更表中 + * + * @param objectApi 所属对象的API名称 + * @param objectLabel 所属对象的标签名称 + * @param fieldApi 字段API名称 + * @param fieldLabel 字段标签名称 + * @param changeReason 变更原因(可为null,使用defaultReason) + * @param operationType 操作类型(INSERT、UPDATE、DELETE) + * @param defaultReason 默认变更原因 + * @param isCustom 是否为自定义对象 + */ private void recordFieldChange(String objectApi, String objectLabel, String fieldApi, String fieldLabel, String changeReason, String operationType, String defaultReason, Boolean isCustom) { try { + // 创建元数据变更记录实体 DataiIntegrationMetadataChange metadataChange = new DataiIntegrationMetadataChange(); + // 设置变更类型为字段 metadataChange.setChangeType("FIELD"); + // 设置操作类型(INSERT、UPDATE、DELETE) metadataChange.setOperationType(operationType); + // 设置对象API名称 metadataChange.setObjectApi(objectApi); + // 设置对象标签名称 metadataChange.setObjectLabel(objectLabel); + // 设置字段API名称 metadataChange.setFieldApi(fieldApi); + // 设置字段标签名称 metadataChange.setFieldLabel(fieldLabel); + // 设置变更时间 metadataChange.setChangeTime(LocalDateTime.now()); + // 设置同步状态为未同步 metadataChange.setSyncStatus(false); + // 设置是否为自定义对象 metadataChange.setIsCustom(isCustom); + // 设置变更原因,如果changeReason不为null则使用它,否则使用默认原因 metadataChange.setChangeReason(changeReason != null ? changeReason : defaultReason); + // 设置变更用户为系统 metadataChange.setChangeUser("SYSTEM"); + // 插入元数据变更记录 insertDataiIntegrationMetadataChange(metadataChange); log.debug("记录字段变更成功: {}.{} - {}", objectApi, fieldApi, operationType); } catch (Exception e) { + // 记录记录字段变更时的错误 log.error("记录字段变更失败: {}.{} - {}", objectApi, fieldApi, e.getMessage(), e); } } + /** + * 检查并记录已从Salesforce中删除的对象 + * 通过比较数据库中存储的对象与从Salesforce获取的对象列表, + * 识别出已从Salesforce中删除的对象并记录相应的删除变更 + * + * @param syncedObjectApis 从Salesforce获取到的已同步对象API名称集合 + */ private void checkDeletedObjectsForMetadata(Set syncedObjectApis) { try { + // 查询数据库中所有对象 DataiIntegrationObject queryObject = new DataiIntegrationObject(); - queryObject.setIsWork(true); List allObjects = dataiIntegrationObjectService.selectDataiIntegrationObjectList(queryObject); + // 遍历数据库中的所有对象,检查哪些对象已不在Salesforce中 for (DataiIntegrationObject object : allObjects) { if (!syncedObjectApis.contains(object.getApi())) { + // 创建元数据变更记录,标记为删除操作 DataiIntegrationMetadataChange metadataChange = new DataiIntegrationMetadataChange(); + // 设置变更类型为对象 metadataChange.setChangeType("OBJECT"); + // 设置操作类型为删除 metadataChange.setOperationType("DELETE"); + // 设置对象API名称 metadataChange.setObjectApi(object.getApi()); + // 设置对象标签名称 metadataChange.setObjectLabel(object.getLabel()); + // 设置变更时间 metadataChange.setChangeTime(LocalDateTime.now()); + // 设置同步状态为未同步 metadataChange.setSyncStatus(false); + // 设置是否为自定义对象 metadataChange.setIsCustom(object.getIsCustom()); + // 设置变更原因 metadataChange.setChangeReason("对象已从Salesforce中删除"); + // 设置变更用户为系统 metadataChange.setChangeUser("SYSTEM"); + // 插入元数据变更记录 insertDataiIntegrationMetadataChange(metadataChange); log.warn("检测到对象已删除: {}", object.getApi()); } } } catch (Exception e) { + // 记录检查已删除对象时的错误 log.error("检查已删除对象时出错: {}", e.getMessage(), e); } } + /** + * 重试操作执行 + * 对指定的操作执行重试机制,在操作失败时进行重试,直到达到最大重试次数 + * + * @param 操作返回值类型 + * @param operation 需要执行的操作(Supplier函数式接口) + * @param maxRetries 最大重试次数 + * @param delayMs 每次重试前的延迟时间(毫秒) + * @return 操作执行结果 + */ private T retryOperation(java.util.function.Supplier operation, int maxRetries, long delayMs) { + // 初始化重试计数器 int retries = 0; + // 无限循环直到操作成功或达到最大重试次数 while (true) { try { + // 执行操作并返回结果 return operation.get(); } catch (Exception e) { + // 增加重试计数 retries++; if (retries >= maxRetries) { + // 如果达到最大重试次数,记录错误并抛出异常 log.error("操作失败,已达到最大重试次数: {}", maxRetries, e); throw e; } try { + // 记录重试日志并延迟指定时间 log.warn("操作失败,将在 {} 毫秒后重试,当前重试次数: {}", delayMs, retries); Thread.sleep(delayMs); } catch (InterruptedException ie) { + // 如果线程在延迟期间被中断,恢复中断状态并抛出运行时异常 Thread.currentThread().interrupt(); log.error("重试延迟时线程被中断", ie); throw new RuntimeException(ie); diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/task/SalesforceSyncTask.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/task/SalesforceSyncTask.java index d31bd73e..2c46074f 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/task/SalesforceSyncTask.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/java/com/datai/integration/task/SalesforceSyncTask.java @@ -1,7 +1,9 @@ package com.datai.integration.task; import com.alibaba.fastjson2.JSON; +import com.datai.integration.model.domain.DataiIntegrationMetadataChange; import com.datai.integration.model.param.DataiSyncParam; +import com.datai.integration.service.IDataiIntegrationMetadataChangeService; import com.datai.salesforce.common.utils.ApiListUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -15,19 +17,9 @@ public class SalesforceSyncTask { @Autowired private ISalesforceDataPullService salesforceDataPullService; - - public void syncAllData(String paramStr) { - DataiSyncParam dataiSyncParam = new DataiSyncParam(); - - if (StringUtils.isNotBlank(paramStr)) { - dataiSyncParam = JSON.parseObject(paramStr, DataiSyncParam.class); - } - - List strings = ApiListUtils.splitApiList(dataiSyncParam.getApi()); - - salesforceDataPullService.syncObjectsData(strings); - } + @Autowired + private IDataiIntegrationMetadataChangeService dataiIntegrationMetadataChangeService; public void syncByObjectStructures(String paramStr) { @@ -41,4 +33,8 @@ public class SalesforceSyncTask { salesforceDataPullService.syncObjectStructures(strings); } + + public void pullAllMetadataChanges() { + dataiIntegrationMetadataChangeService.pullAllMetadataChanges(); + } } \ No newline at end of file diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/resources/doc/全对象元数据变更拉取接口文档.md b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/resources/doc/全对象元数据变更拉取接口文档.md new file mode 100644 index 00000000..5d6c1d79 --- /dev/null +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/resources/doc/全对象元数据变更拉取接口文档.md @@ -0,0 +1,245 @@ +# 全对象元数据变更拉取接口文档 + +## 1. 接口概述 + +### 1.1 接口名称 +全对象元数据变更拉取 + +### 1.2 接口描述 +从Salesforce拉取所有对象的元数据变更信息并记录到元数据变更表中。该接口会自动检测对象和字段的新增、修改、删除操作,并将变更记录到数据库中。 + +### 1.3 接口地址 +``` +POST /integration/change/pullAll +``` + +### 1.4 请求方式 +POST + +### 1.5 权限要求 +需要 `integration:change:pullAll` 权限 + +### 1.6 日志记录 +操作类型:OTHER(其他) +日志标题:全对象元数据变更拉取 + +## 2. 请求参数 + +### 2.1 请求头 +``` +Content-Type: application/json +Authorization: Bearer {token} +``` + +### 2.2 请求体 +该接口无需请求体参数 + +### 2.3 请求示例 +```bash +curl -X POST "http://localhost:8080/integration/change/pullAll" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer {your_token}" +``` + +## 3. 响应格式 + +### 3.1 响应结构 +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "success": true, + "objectChangeCount": 5, + "fieldChangeCount": 23, + "message": "全对象元数据变更拉取完成,对象变更: 5 个,字段变更: 23 个" + } +} +``` + +### 3.2 响应字段说明 + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| code | Integer | 响应状态码,200表示成功 | +| msg | String | 响应消息 | +| data | Object | 响应数据对象 | +| data.success | Boolean | 操作是否成功 | +| data.objectChangeCount | Integer | 对象变更数量 | +| data.fieldChangeCount | Integer | 字段变更数量 | +| data.message | String | 详细消息说明 | + +### 3.3 成功响应示例 +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "success": true, + "objectChangeCount": 3, + "fieldChangeCount": 15, + "message": "全对象元数据变更拉取完成,对象变更: 3 个,字段变更: 15 个" + } +} +``` + +### 3.4 失败响应示例 +```json +{ + "code": 500, + "msg": "全对象元数据变更拉取失败: 无法连接到Salesforce", + "data": null +} +``` + +## 4. 业务逻辑说明 + +### 4.1 处理流程 +1. **建立连接**:使用重试机制(最多3次,每次间隔1秒)建立与Salesforce的SOAP连接 +2. **获取对象列表**:从Salesforce获取所有对象的全局描述信息 +3. **对象过滤**:只同步满足以下任一条件的对象: + - isQueryable(可查询) + - isCreateable(可创建) + - isUpdateable(可更新) + - isDeletable(可删除) +4. **对象变更检测**: + - 新增对象:插入数据库并记录变更 + - 修改对象:比较差异并记录变更 + - 删除对象:检测并记录已删除的对象 +5. **字段变更检测**: + - 新增字段:记录字段新增变更 + - 修改字段:比较字段属性差异并记录变更 + - 删除字段:检测并记录已删除的字段 +6. **增量更新处理**:如果检测到字段变更,自动禁用该对象的增量更新状态 +7. **统计结果**:返回对象变更数量和字段变更数量 + +### 4.2 对象同步条件 +只有满足以下任一条件的对象才会被同步: +- 可查询(isQueryable = true) +- 可创建(isCreateable = true) +- 可更新(isUpdateable = true) +- 可删除(isDeletable = true) + +### 4.3 字段同步条件 +所有字段都会被同步,无特殊限制 + +### 4.4 变更记录规则 +- **对象变更**:记录到元数据变更表,变更类型为 "OBJECT" +- **字段变更**:记录到元数据变更表,变更类型为 "FIELD" +- **操作类型**:INSERT(新增)、UPDATE(修改)、DELETE(删除) +- **同步状态**:默认为 false(未同步) + +## 5. 错误处理 + +### 5.1 常见错误码 + +| 错误码 | 说明 | 处理建议 | +|--------|------|----------| +| 401 | 未授权 | 检查token是否有效 | +| 403 | 权限不足 | 确认用户是否具有 `integration:change:pullAll` 权限 | +| 500 | 服务器内部错误 | 查看服务器日志,检查Salesforce连接配置 | + +### 5.2 常见错误场景 + +#### 5.2.1 Salesforce连接失败 +``` +错误信息:全对象元数据变更拉取失败: 无法连接到Salesforce +原因:SOAP连接配置错误或网络问题 +解决方案:检查SOAPConnectionFactory配置和网络连接 +``` + +#### 5.2.2 权限不足 +``` +错误信息:您没有权限执行此操作 +原因:用户缺少 `integration:change:pullAll` 权限 +解决方案:联系管理员分配相应权限 +``` + +#### 5.2.3 未获取到对象 +``` +错误信息:未获取到任何Salesforce对象 +原因:Salesforce中无可访问对象或API权限不足 +解决方案:检查Salesforce API权限配置 +``` + +## 6. 使用注意事项 + +### 6.1 性能考虑 +- 该接口会遍历Salesforce中的所有对象和字段,可能需要较长时间 +- 建议在业务低峰期调用 +- 避免频繁调用,建议按需触发 + +### 6.2 数据一致性 +- 接口执行过程中会记录详细的变更日志 +- 如果执行失败,已记录的变更不会回滚 +- 建议在执行前备份重要数据 + +### 6.3 并发控制 +- 不建议同时调用多个拉取接口 +- 确保前一次拉取完成后再执行下一次拉取 + +### 6.4 监控建议 +- 监控接口执行时间 +- 监控变更数量,异常增长可能需要人工介入 +- 定期检查同步状态,及时处理未同步的变更 + +## 7. 相关接口 + +### 7.1 查询元数据变更列表 +``` +GET /integration/change/list +``` + +### 7.2 查询未同步的元数据变更 +``` +GET /integration/change/unsynced +``` + +### 7.3 同步元数据变更到本地数据库 +``` +POST /integration/change/{id}/sync +``` + +### 7.4 批量同步元数据变更 +``` +POST /integration/change/syncBatch +``` + +### 7.5 获取变更统计信息 +``` +GET /integration/change/statistics +``` + +## 8. 附录 + +### 8.1 元数据变更表结构 + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| id | Long | 主键ID | +| changeType | String | 变更类型(OBJECT/FIELD) | +| operationType | String | 操作类型(INSERT/UPDATE/DELETE) | +| objectApi | String | 对象API名称 | +| objectLabel | String | 对象标签名称 | +| fieldApi | String | 字段API名称(字段变更时) | +| fieldLabel | String | 字段标签名称(字段变更时) | +| changeReason | String | 变更原因 | +| changeTime | LocalDateTime | 变更时间 | +| syncStatus | Boolean | 同步状态(false-未同步,true-已同步) | +| isCustom | Boolean | 是否为自定义对象 | +| changeUser | String | 变更用户 | + +### 8.2 版本历史 + +| 版本 | 日期 | 说明 | +|------|------|------| +| 1.0 | 2025-12-27 | 初始版本 | + +### 8.3 联系方式 +如有问题,请联系技术支持团队。 + +--- + +**文档生成时间**:2025-12-27 +**最后更新时间**:2025-12-27 +**文档版本**:1.0 diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigAuditLogController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigAuditLogController.java index 965931e9..89a63996 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigAuditLogController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigAuditLogController.java @@ -3,6 +3,7 @@ package com.datai.setting.controller; import java.util.List; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.setting.model.dto.DataiConfigAuditLogDto; import com.datai.setting.model.vo.DataiConfigAuditLogVo; import com.datai.setting.utils.AuditLogObjectNameResolver; @@ -63,7 +64,7 @@ public class DataiConfigAuditLogController extends BaseController return vo; }).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigEnvironmentController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigEnvironmentController.java index 25f11678..988fa7a6 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigEnvironmentController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigEnvironmentController.java @@ -3,6 +3,7 @@ package com.datai.setting.controller; import java.util.List; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.setting.model.vo.DataiConfigEnvironmentVo; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; @@ -55,7 +56,7 @@ public class DataiConfigEnvironmentController extends BaseController DataiConfigEnvironment dataiConfigEnvironment = DataiConfigEnvironmentDto.toObj(dataiConfigEnvironmentDto); List list = dataiConfigEnvironmentService.selectDataiConfigEnvironmentList(dataiConfigEnvironment); List voList = list.stream().map(DataiConfigEnvironmentVo::objToVo).collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigSnapshotController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigSnapshotController.java index 70b1762f..8732efce 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigSnapshotController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigSnapshotController.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.setting.model.dto.DataiConfigSnapshotDto; import com.datai.setting.model.vo.DataiConfigSnapshotVo; import com.datai.setting.service.IDataiConfigEnvironmentService; @@ -80,7 +81,7 @@ public class DataiConfigSnapshotController extends BaseController }) .collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /** diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigurationController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigurationController.java index 68290597..d6c40988 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigurationController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigurationController.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import com.datai.common.utils.PageUtils; import com.datai.setting.model.vo.DataiConfigurationVo; import com.datai.setting.service.IDataiConfigEnvironmentService; import jakarta.servlet.http.HttpServletResponse; @@ -80,7 +81,7 @@ public class DataiConfigurationController extends BaseController }) .collect(Collectors.toList()); - return getDataTable(voList); + return getDataTableByPage(voList,PageUtils.getTotal(list)); } /**