【feat】 优化元数据变更同步本地

This commit is contained in:
Kris 2026-01-05 13:58:05 +08:00
parent 7dd6d5dd44
commit a1c53bc92b
3 changed files with 376 additions and 7 deletions

View File

@ -0,0 +1,261 @@
# 批量同步元数据变更到本地数据库接口文档
## 接口概述
批量同步元数据变更到本地数据库接口用于将多个元数据变更记录批量同步到本地数据库,支持并发处理以提高同步效率。
## 接口信息
- **接口路径**: `POST /integration/change/syncBatch`
- **接口名称**: 批量同步元数据变更到本地数据库
- **权限要求**: `integration:change:syncBatch`
- **请求类型**: POST
- **Content-Type**: application/json
## 请求参数
### 路径参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | Long[] | 是 | 元数据变更ID数组支持批量传入多个ID |
### 请求示例
```bash
POST /integration/change/syncBatch/1,2,3,4,5
```
```bash
POST /integration/change/syncBatch/[1,2,3,4,5]
```
## 响应参数
### 响应结构
| 字段名 | 类型 | 说明 |
|--------|------|------|
| success | Boolean | 是否全部成功true表示所有记录同步成功 |
| message | String | 同步结果摘要信息 |
| data | Object | 详细数据对象 |
### data 对象结构
| 字段名 | 类型 | 说明 |
|--------|------|------|
| totalCount | Integer | 总记录数 |
| successCount | Integer | 成功数量 |
| failCount | Integer | 失败数量 |
| details | Array | 每条记录的同步详情列表 |
### details 数组元素结构
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | Long | 元数据变更ID |
| success | Boolean | 是否成功 |
| message | String | 结果消息 |
### 成功响应示例
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"success": true,
"message": "批量同步完成: 成功 5 条, 失败 0 条",
"data": {
"totalCount": 5,
"successCount": 5,
"failCount": 0,
"details": [
{
"id": 1,
"success": true,
"message": "对象创建成功"
},
{
"id": 2,
"success": true,
"message": "字段创建成功"
},
{
"id": 3,
"success": true,
"message": "对象更新成功"
},
{
"id": 4,
"success": true,
"message": "字段删除成功"
},
{
"id": 5,
"success": true,
"message": "对象创建成功"
}
]
}
}
}
```
### 部分失败响应示例
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"success": false,
"message": "批量同步完成: 成功 3 条, 失败 2 条",
"data": {
"totalCount": 5,
"successCount": 3,
"failCount": 2,
"details": [
{
"id": 1,
"success": true,
"message": "对象创建成功"
},
{
"id": 2,
"success": false,
"message": "同步失败: 对象不存在"
},
{
"id": 3,
"success": true,
"message": "字段创建成功"
},
{
"id": 4,
"success": false,
"message": "同步失败: 获取Salesforce连接失败"
},
{
"id": 5,
"success": true,
"message": "对象更新成功"
}
]
}
}
}
```
## 功能说明
### 主要功能
1. **批量同步**: 支持一次性同步多个元数据变更记录到本地数据库
2. **并发处理**: 使用线程池并发执行同步任务,大幅提升处理速度
3. **进度反馈**: 实时输出同步进度每10条记录输出一次
4. **异常隔离**: 单个同步任务失败不会影响其他任务的执行
5. **结果汇总**: 提供详细的同步结果统计
### 同步逻辑
该方法会根据元数据变更记录的变更类型和操作类型执行相应的同步操作:
#### 对象变更OBJECT
- **INSERT新增**:
- 从Salesforce获取对象元数据
- 创建对象记录到本地数据库
- 保存对象字段信息
- 检测对象数据量,创建对应的数据库表(普通表或分区表)
- 创建批处理任务
- **UPDATE修改**:
- 更新对象的标签信息
- 标记对象为不可用状态
- **DELETE删除**:
- 标记对象为不可用状态
- 停止增量同步
#### 字段变更FIELD
- **INSERT新增**:
- 创建字段记录到本地数据库
- 在对应的数据库表中添加字段
- **UPDATE修改**:
- 更新字段的标签信息
- 修改数据库表中的字段
- **DELETE删除**:
- 删除字段记录
- 从数据库表中删除字段
### 同步状态更新
- 同步成功:将元数据变更记录的 `syncStatus` 更新为 `true`
- 同步失败:将元数据变更记录的 `syncStatus` 更新为 `false`,并记录错误信息和重试次数
## 优化特性
### 1. 并发处理
使用 `SalesforceExecutor` 线程池并发执行同步任务,大幅提升大批量数据的处理速度。
### 2. 进度反馈
实时输出同步进度,方便监控大批量同步的执行状态:
- 每处理10条记录输出一次进度
- 显示当前处理数、总数、成功数、失败数
### 3. 异常隔离
- 单个同步任务失败不会影响其他任务的执行
- 每个任务的异常被独立捕获和记录
- 保证整体同步流程的完整性
### 4. 结果汇总
提供详细的同步结果统计:
- 总记录数
- 成功数量
- 失败数量
- 每条记录的详细同步结果
### 5. 线程安全
- 使用 `AtomicInteger` 保证计数器的线程安全
- 使用 `Collections.synchronizedList` 保证结果列表的线程安全
- 使用 `Future` 管理异步任务,确保所有任务完成后再返回结果
## 使用场景
1. **批量同步**: 需要一次性同步多个元数据变更记录时使用
2. **大批量处理**: 适合处理大量元数据变更记录(如全量同步)
3. **并发优化**: 需要提高同步效率时使用,相比单条同步可大幅提升速度
## 注意事项
1. **权限要求**: 调用该接口需要 `integration:change:syncBatch` 权限
2. **ID有效性**: 传入的ID必须是有效的元数据变更记录ID
3. **并发限制**: 线程池大小由系统配置决定,大批量同步时注意系统资源占用
4. **事务处理**: 每个同步任务独立执行,单个任务失败不影响其他任务
5. **日志监控**: 同步过程中会输出详细的日志信息,便于问题排查
6. **重试机制**: 失败的记录会增加重试次数,可通过其他接口进行重试
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 401 | 未授权,缺少访问权限 |
| 404 | 元数据变更记录不存在 |
| 500 | 服务器内部错误,同步过程中发生异常 |
## 相关接口
- [同步元数据变更到本地数据库](./同步元数据变更到本地数据库接口文档.md) - 单条同步接口
- [查询未同步的元数据变更列表](#) - 获取未同步的变更记录
- [批量更新元数据变更同步状态](#) - 批量更新同步状态

View File

@ -71,8 +71,7 @@ export function syncChange(id) {
// 批量同步元数据变更到本地数据库
export function syncBatchChange(ids) {
return request({
url: '/integration/change/syncBatch',
method: 'post',
data: ids
url: '/integration/change/syncBatch/' + ids.join(','),
method: 'post'
})
}

View File

@ -162,7 +162,6 @@
<span>{{ parseTime(scope.row.syncTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="异常信息" align="center" prop="syncErrorMessage" />
<el-table-column label="重试次数" align="center" prop="retryCount" />
<el-table-column label="重试时间" align="center" prop="lastRetryTime" width="180">
<template #default="scope">
@ -174,7 +173,13 @@
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width" width="200">
<template #default="scope">
<el-button link type="primary" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['integration:change:query']">详情</el-button>
<el-button link type="success" icon="Upload" @click="handleSync(scope.row)" v-hasPermi="['integration:change:sync']">同步</el-button>
<el-button
link
type="success"
icon="Upload"
@click="handleSync(scope.row)"
v-hasPermi="['integration:change:sync']"
v-if="scope.row.syncStatus === false || scope.row.syncStatus === 'false'">同步</el-button>
</template>
</el-table-column>
</el-table>
@ -515,6 +520,49 @@
</div>
</template>
</el-drawer>
<el-dialog title="批量同步结果" v-model="syncResultDialogVisible" width="800px" append-to-body>
<el-row :gutter="20" class="mb20">
<el-col :span="8">
<el-card shadow="hover" class="result-card success">
<div class="result-number">{{ syncResultData.totalCount }}</div>
<div class="result-label">总记录数</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="result-card success">
<div class="result-number">{{ syncResultData.successCount }}</div>
<div class="result-label">成功数量</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="result-card" :class="syncResultData.failCount > 0 ? 'error' : 'success'">
<div class="result-number">{{ syncResultData.failCount }}</div>
<div class="result-label">失败数量</div>
</el-card>
</el-col>
</el-row>
<el-divider content-position="left">同步详情</el-divider>
<el-table :data="syncResultData.details" border stripe max-height="400">
<el-table-column label="ID" align="center" prop="id" width="100" />
<el-table-column label="状态" align="center" prop="success" width="100">
<template #default="scope">
<el-tag :type="scope.row.success ? 'success' : 'danger'">
{{ scope.row.success ? '成功' : '失败' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="结果消息" align="center" prop="message" show-overflow-tooltip />
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="syncResultDialogVisible = false"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@ -535,6 +583,15 @@
const total = ref(0);
const title = ref("");
//
const syncResultDialogVisible = ref(false);
const syncResultData = ref({
totalCount: 0,
successCount: 0,
failCount: 0,
details: []
});
//
const openDetail = ref(false);
const isEditMode = ref(false); // /
@ -815,7 +872,28 @@
return syncBatchChange(ids.value).then(response => {
notification.close();
proxy.$modal.msgSuccess(response.data.message || '变更同步成功');
if (response.data && response.data.data) {
const resultData = response.data.data;
syncResultData.value = {
totalCount: resultData.totalCount || 0,
successCount: resultData.successCount || 0,
failCount: resultData.failCount || 0,
details: resultData.details || []
};
syncResultDialogVisible.value = true;
if (resultData.failCount === 0) {
proxy.$modal.msgSuccess(`批量同步完成: 成功 ${resultData.successCount}`);
} else {
proxy.$modal.msgWarning(`批量同步完成: 成功 ${resultData.successCount} 条, 失败 ${resultData.failCount}`);
}
} else {
proxy.$modal.msgSuccess(response.data.message || '变更同步成功');
}
getList();
}).catch(error => {
notification.close();
@ -855,4 +933,35 @@
}
getList();
</script>
</script>
<style scoped>
.result-card {
text-align: center;
padding: 20px;
}
.result-card.success {
border-left: 4px solid #67c23a;
}
.result-card.error {
border-left: 4px solid #f56c6c;
}
.result-number {
font-size: 32px;
font-weight: bold;
color: #303133;
margin-bottom: 10px;
}
.result-label {
font-size: 14px;
color: #909399;
}
.mb20 {
margin-bottom: 20px;
}
</style>