diff --git a/doc/salesforce/integration/全对象元数据变更拉取接口文档.md b/doc/salesforce/integration/全对象元数据变更拉取接口文档.md new file mode 100644 index 0000000..5d6c1d7 --- /dev/null +++ b/doc/salesforce/integration/全对象元数据变更拉取接口文档.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/src/api/integration/change.js b/src/api/integration/change.js index ebfed87..3507385 100644 --- a/src/api/integration/change.js +++ b/src/api/integration/change.js @@ -51,3 +51,11 @@ export function delChange(id) { method: 'delete' }) } + +// 全对象元数据变更拉取 +export function pullAllChange() { + return request({ + url: '/integration/change/pullAll', + method: 'post' + }) +} diff --git a/src/views/auth/register/index.vue b/src/views/auth/register/index.vue index 593c3f1..bb698c5 100644 --- a/src/views/auth/register/index.vue +++ b/src/views/auth/register/index.vue @@ -163,38 +163,101 @@ -
- - - - {{ loginResult.instanceUrl }} + + 用户信息 + + + + + + + + + {{ loginResult.userEmail }} + + + + {{ loginResult.userId }} + + + + {{ loginResult.organizationName }} + + + + + + 环境信息 + + + + + + {{ loginResult.instanceUrl }} + + + + + + {{ loginResult.sandbox ? 'Sandbox' : 'Production' }} + + + + {{ loginResult.tokenType }} + + + + + {{ loginResult.sessionExpired ? '已过期' : '有效' }} + + + +
+ + {{ loginResult.expiresIn ? formatExpiresIn(loginResult.expiresIn) : 'Unknown' }} +
+
+ +
+ {{ loginResult.language }} + {{ loginResult.timeZone }} +
+
+
+ + + 服务端点 + + + - - Metadata Server -
@@ -222,7 +285,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'; import { RefreshRight, User, SwitchButton, DocumentCopy, Switch, - Connection, Key, Lock, Ticket, Hide, Monitor, TopRight, ArrowDown, Link + Connection, Key, Lock, Ticket, Hide, Monitor, TopRight, ArrowDown, Link, Clock } from '@element-plus/icons-vue'; const { proxy } = getCurrentInstance(); @@ -478,6 +541,22 @@ const handleLoginTypeChange = () => { } }; + const formatExpiresIn = (seconds) => { + if (!seconds) return 'Unknown'; + + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = seconds % 60; + + if (hours > 0) { + return `${hours}小时 ${minutes}分钟 ${secs}秒`; + } else if (minutes > 0) { + return `${minutes}分钟 ${secs}秒`; + } else { + return `${secs}秒`; + } + }; + const copyText = (text) => { if (navigator.clipboard) { navigator.clipboard.writeText(text).then(() => ElMessage.success('已复制 Session ID')); @@ -633,6 +712,43 @@ const handleLoginTypeChange = () => { line-height: 1.4; } +/* 用户信息样式 */ +.user-info { + display: flex; + align-items: center; + gap: 8px; +} + +.user-icon { + color: #409eff; + font-size: 16px; +} + +/* 过期信息样式 */ +.expires-info { + display: flex; + align-items: center; + gap: 6px; + color: #606266; +} + +/* 语言/时区样式 */ +.locale-info { + display: flex; + align-items: center; + gap: 8px; +} + +.timezone-text { + color: #606266; + font-size: 13px; +} + +/* Refresh Token 区域样式 */ +.refresh-token-section { + margin-top: 16px; +} + /* 响应式调整 */ @media screen and (max-width: 768px) { .main-row { diff --git a/src/views/integration/change/index.vue b/src/views/integration/change/index.vue index 09f57ed..bb94f8f 100644 --- a/src/views/integration/change/index.vue +++ b/src/views/integration/change/index.vue @@ -117,6 +117,16 @@ v-hasPermi="['integration:change:export']" >导出 + + 变更拉取 + @@ -498,12 +508,14 @@ \ No newline at end of file