feat: 优化 README 文档并完善批次数据管理功能
主要修改:
1. README.md 优化
- 明确项目当前实现状态(从 Salesforce 同步数据到本地)
- 添加开发计划,包含 4 个主要方向:
* 数据推送到目标 Salesforce 系统
* 源 Salesforce 系统文件下载
* User 权限同步集成系统
* 集成可视化报表插件
2. 批次数据管理功能完善
- DataiIntegrationBatchController: 新增 insertBatchDataToTarget 和 updateBatchDataToTarget 方法
- IDataiIntegrationBatchService: 添加批次数据插入和更新接口
- DataiIntegrationBatchServiceImpl: 实现批次数据插入和更新逻辑
- DataiIntegrationBatch: 添加 new_id、is_insert、is_update 字段
- DataiIntegrationBatchDto/DataiIntegrationBatchVo: 同步字段定义
- DataiIntegrationBatchMapper.xml: 添加相关 SQL 映射
3. 文档更新
- 创建提示词文档:06更新项目说明文档提示词.md
- 更新 Salesforce 模块文档索引
- 删除过时文档
4. 配置更新
- 更新 .gitignore 配置
This commit is contained in:
parent
a383cde260
commit
7459f8ab53
1
.gitignore
vendored
1
.gitignore
vendored
@ -52,3 +52,4 @@ nbdist/
|
||||
|
||||
effective-pom.xml
|
||||
/datai-scenes/datai-scene-salesforce/datai-salesforce-integration/src/main/resources/参考代码/
|
||||
/datai-scenes/datai-scene-salesforce/docs/sql/
|
||||
|
||||
465
README.md
465
README.md
@ -1,222 +1,277 @@
|
||||
<p align="center">
|
||||
<span>
|
||||
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
|
||||
</span>
|
||||
<span>+</span>
|
||||
<span>
|
||||
<img alt="logo" src="./doc/image/logo.png">
|
||||
</span>
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">datai v3.8.9-G</h1>
|
||||
<h4 align="center">基于SpringBoot3+Vue3前后端分离的Java快速开发框架</h4>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg">
|
||||
</p>
|
||||
# Datai - Salesforce 集成系统
|
||||
|
||||
# 当前版本是3.8.9-G
|
||||
## 项目概述
|
||||
|
||||
本人的其他两个推荐搭配的项目
|
||||
Datai 是一个基于若依Geek框架构建的企业级管理系统,采用模块化架构设计,提供可扩展的插件和场景模块。系统集成了 Salesforce 平台,支持多种 API 交互方式,实现数据同步、认证管理和元数据处理等核心功能。
|
||||
|
||||
1. [DataI-App-Geek: 这是集成系统生态的小程序版本 (gitee.com)](https://gitee.com/geek-xd/geek-uniapp-vue3-uview-plus-uchart)
|
||||
2. [DataI-Vue3-Geek: 这是集成系统生态的Vue3版本 (gitee.com)](https://gitee.com/geek-xd/ruo-yi-vue3-geek)
|
||||
**当前实现状态**:项目目前专注于从 Salesforce 源系统同步数据到本地数据库,包括元数据管理、批量数据同步、实时数据同步等功能。
|
||||
|
||||
与本项目同为一个作者开发,兼容性最好,学习成本最低。
|
||||
## 技术栈
|
||||
|
||||
# 引言
|
||||
| 技术 | 版本 | 说明 |
|
||||
|------|------|------|
|
||||
| Java | 21 / 22 | 根项目使用 Java 21,Salesforce 模块使用 Java 22 |
|
||||
| Spring Boot | 3.5.7 | 核心框架 |
|
||||
| MyBatis | 3.5.16 | 持久层框架 |
|
||||
| MySQL | 8.3.0 | 关系型数据库 |
|
||||
| PostgreSQL | 42.7.7 | 关系型数据库 |
|
||||
| Knife4j | 4.5.0 | API 文档工具 |
|
||||
| Druid | 1.2.27 | 数据库连接池 |
|
||||
| Maven | - | 构建工具 |
|
||||
| Salesforce Partner API | 0.0.1 | Salesforce SOAP API 客户端 |
|
||||
| Salesforce Pub/Sub Client | 0.2.21 | Salesforce CDC 实时同步客户端 |
|
||||
| Apache Avro | 1.11.3 | 数据序列化格式 |
|
||||
| Fastjson | 2.0.58 | JSON 解析器 |
|
||||
| JWT | 0.12.6 | Token 生成与解析 |
|
||||
| PageHelper | 2.1.1 | 分页插件 |
|
||||
|
||||
DataI-Vue与DataI-App是基于SpringBoot2+Vue2打造的企业级开发框架,得到了广大开发者的喜爱和积极反馈。随着技术的迭代进步,SpringBoot3与Vue3逐渐进入开发者的视野。为了满足开发者对于新技术的追求,DataI官方文档提供了SpringBoot2至SpringBoot3的升级方法。与此同时,社区也涌现出了DataI-Vue3、DataI-App-Vue3的版本,展现了开发者社区对于技术升级的热情与努力。
|
||||
## 项目结构
|
||||
|
||||
然而,在升级的过程中,官方的方法为了兼顾Java1.8的特性与一些老旧的方法,未完全拥抱SpringBoot3与Java17的全部特性。而社区的DataI-Vue3、DataI-App-Vue3版本由于出自不同的团队之手,兼容性及整合性上存在些许不足。更为关键的是,尽管这些版本支持TypeScript,但缺乏与之相匹配的tsconfig.json配置文件,这使得在主流编辑器如VSCode中,TypeScript的语法提示环境并未达到最佳状态。
|
||||
|
||||
鉴于此,datai生态应运而生。它旨在为广大开发者提供一个既保留原版本核心特性,又整合社区版优点的全新解决方案。在datai中,我们深入调研了企业开发中常用的DataI扩展,并直接在框架中集成,确保开发者能够快速上手,高效开发。同时,我们采用了最新的SpringBoot3+Vue3技术栈,彻底移除了为了兼容Java1.8而保留的老旧方法。更为重要的是,我们为TypeScript开发环境加入了常用的tsconfig.json配置,使得开发者在VSCode等编辑器中能够获得更为舒适、便捷的语法提示体验。
|
||||
|
||||
datai不仅仅是一个简单的升级版本,更是对于DataI生态的一次全面优化与整合。我们相信,通过datai,开发者将能够更为高效、愉悦地开发出优秀的企业级应用。
|
||||
|
||||
## 平台简介
|
||||
|
||||
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
|
||||
|
||||
* 前端采用Vue3、Element Plus。
|
||||
* 后端采用Spring Boot3、Spring Security、Redis & Jwt。
|
||||
* 权限认证使用Jwt,支持多终端认证系统。
|
||||
* 支持加载动态权限菜单,多方式轻松权限控制。
|
||||
* 高效率开发,使用代码生成器可以一键生成前后端代码。
|
||||
* 多数据源与分库分表默认集成
|
||||
* 所有非基本模块可随意插拔,让开发更加简单高效
|
||||
* 提供了多个工具模块助力开发,如:在线接口模块、mybatis-jpa模块
|
||||
* 提供了多个常见业务模块简化开发,如:第三方认证模块、支付模块
|
||||
* 提供了多个常见的服务模块集成开发,如:websocket模块、minio模块
|
||||
* 特别鸣谢:[element](https://github.com/ElemeFE/element),[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin),[eladmin-web](https://github.com/elunez/eladmin-web)。
|
||||
* 阿里云折扣场:[点我进入](http://aly.datai.vip),腾讯云秒杀场:[点我进入](http://txy.datai.vip)
|
||||
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)
|
||||
|
||||
## 本项目与原项目的区别
|
||||
|
||||
### 核心
|
||||
|
||||
**模块化架构设计,支持各个模块的快速安拆,对第三方认证、第三方支付模块设计了基础的规范和基础模块。**
|
||||
|
||||
### 细节
|
||||
|
||||
1. 改用SpringBoot3+java17的更新的技术栈并改掉所有的弃用的方法,全面拥抱java17的全新特性。
|
||||
2. 升级了代码生成器(配合本项目的vue3版本才可用),使关联表生成更加简单!
|
||||
3. 改用最新版本的SpringSecurity安全框架,以及采用最新的lambda 表达式的配置方式,更加通俗易懂!
|
||||
4. 自动Api文档以springfox替代springdoc来适配knife4j框架的4.x版本,更好的适配springboot3!
|
||||
5. 默认引入mybatis-plus增强mybatis,并自创工具模块mybatis-jpa简化CRUD!
|
||||
6. 默认引入lombok简化代码(注:基础模块并未使用mybatis-plus和lombok,对这两个扩展有争议的小伙伴可以直接删除,不会影响到框架本身滴,以及knife4j直接删除也不会影响到springdoc,主要还是为了方便咱们开发者呢!)
|
||||
7. 提供了大量可随意插拔的模块,助力快速开发!同时datai插件集成中的常用插件也以模块的形式直接集成在了项目中,也是可以随意插拔的嗷~
|
||||
|
||||
## 模块介绍(简单开发必看)
|
||||
|
||||
* 最简单的开发就是删除所有的可移除模块,按需添加模块。
|
||||
* 测试中的模块请自己使用的时候一定要测试一下。
|
||||
* 对于小白,开发中的模块请直接删除。
|
||||
|
||||
```plaintext
|
||||
com.datai
|
||||
├── datai-admin // 后台服务
|
||||
├── common // 工具类
|
||||
│ └── annotation // 自定义注解
|
||||
│ └── config // 全局配置
|
||||
│ └── constant // 通用常量
|
||||
│ └── core // 核心控制
|
||||
│ └── enums // 通用枚举
|
||||
│ └── exception // 通用异常
|
||||
│ └── filter // 过滤器处理
|
||||
│ └── utils // 通用类处理
|
||||
├── framework // 框架核心
|
||||
│ └── aspectj // 注解实现
|
||||
│ └── config // 系统配置
|
||||
│ └── datasource // 数据权限
|
||||
│ └── interceptor // 拦截器
|
||||
│ └── manager // 异步处理
|
||||
│ └── security // 权限控制
|
||||
│ └── web // 前端控制
|
||||
├── datai-models // 业务模块
|
||||
│ └── online // 在线开发模块(可移除)
|
||||
│ └── quartz // 定时任务(可移除)
|
||||
│ └── generator // 代码生成(可移除)
|
||||
│ └── form // 自定义在线表单(可移除)
|
||||
│ └── flowable // 流程管理(可移除)(依赖于form模块)
|
||||
│ └── message // 消息模块(可移除)(可以与phone、email模块协同)
|
||||
│ └── starter // 业务场景启动器
|
||||
├── datai-plugins // 插件
|
||||
│ └── ehcache // ehcache缓存插件(与redis模块同类,两者二选一)
|
||||
│ └── mybatis-jpa // mybatis-jpa插件(可移除)(简化CRUD,以数据模型为基础开发)
|
||||
│ └── mybatis-plus // mybatis-plus插件(可移除)(简化CRUD)
|
||||
│ └── mybatis-interceptor // mybatis-interceptor插件(可移除)(简化数据鉴权和分页,扩展性强)
|
||||
│ └── atomikos // atomikos分布式事务插件(可移除)
|
||||
│ └── netty // netty插件(可移除)
|
||||
│ └── rabbitmq // rabbitmq队列服务模块
|
||||
│ └── redis // redis缓存服务模块(与ehcache插件同类,两者二选一)
|
||||
│ └── websocket // websocket插件(可移除)
|
||||
│ └── starter // 插件整合模块
|
||||
├── datai-scenes // 业务场景
|
||||
│ ├── datai-scenes-pay // 支付场景(基本业务完成)
|
||||
│ │ └── common // 支付框架基础模块(基础框架搭建完成)
|
||||
│ │ └── sqb // 收钱吧支付模块(功能可用,业务需要开发)
|
||||
│ │ └── wx // 微信支付模块(基础业务完成)
|
||||
│ │ └── alipay // 支付宝支付模块(基础业务完成)
|
||||
│ │ └── starter // 支付场景启动器
|
||||
│ ├── datai-scenes-auth // 第三方认证场景(基础框架搭建完成)
|
||||
│ │ └── common // 第三方认证基础模块(开发中)
|
||||
│ │ └── justauth // 网站第三方认证模块(测试中,参照若依扩展改进,因没有这么多场景的code,请大家测试出问题后help解决一下发出来)
|
||||
│ │ └── wx // 微信小程序认证模块(测试中)
|
||||
│ │ └── phone // 手机认证模块(基础业务完成)
|
||||
│ │ └── email // 邮箱认证模块(基础业务完成)
|
||||
│ │ └── starter // 第三方认证启动器
|
||||
│ ├── datai-scenes-file // 文件服务系统(支持公开访问、临时凭证、分片上传等常见功能)
|
||||
│ │ └── common // 基础模块和本地磁盘方式
|
||||
│ │ └── minio // minio分布式文件服务
|
||||
│ │ └── oss-alibaba // alibaba的oss云储存服务
|
||||
│ │ └── starter // 第三方认证启动器
|
||||
├── datai-system // 系统代码
|
||||
```
|
||||
datai/
|
||||
├── datai-admin/ # 管理后台启动模块
|
||||
├── datai-framework/ # 框架核心模块
|
||||
├── datai-system/ # 系统管理模块
|
||||
├── datai-common/ # 通用工具模块
|
||||
├── datai-scenes/ # 场景模块
|
||||
│ ├── datai-scene-salesforce/ # Salesforce 集成场景
|
||||
│ │ ├── datai-salesforce-auth/ # 认证相关模块
|
||||
│ │ ├── datai-salesforce-common/ # 通用工具和常量
|
||||
│ │ ├── datai-salesforce-integration/ # 核心集成模块
|
||||
│ │ ├── datai-salesforce-setting/ # 配置管理模块
|
||||
│ │ └── datai-salesforce-starter/ # 启动器模块
|
||||
│ ├── datai-scene-auth/ # 认证场景
|
||||
│ │ ├── datai-auth-common/ # 认证通用模块
|
||||
│ │ ├── datai-auth-starter/ # 认证启动器
|
||||
│ │ ├── datai-oauth-justauth/ # JustAuth OAuth
|
||||
│ │ ├── datai-oauth-wx/ # 微信 OAuth
|
||||
│ │ ├── datai-tfa-email/ # 邮箱双因子认证
|
||||
│ │ └── datai-tfa-phone/ # 手机双因子认证
|
||||
│ ├── datai-scene-pay/ # 支付场景
|
||||
│ │ ├── datai-pay-common/ # 支付通用模块
|
||||
│ │ ├── datai-pay-starter/ # 支付启动器
|
||||
│ │ ├── datai-pay-alipay/ # 支付宝支付
|
||||
│ │ ├── datai-pay-wx/ # 微信支付
|
||||
│ │ └── datai-pay-sqb/ # 收钱吧支付
|
||||
│ └── datai-scene-file/ # 文件场景
|
||||
│ ├── datai-file-common/ # 文件通用模块
|
||||
│ ├── datai-file-starter/ # 文件启动器
|
||||
│ ├── datai-file-local/ # 本地文件存储
|
||||
│ ├── datai-file-minio/ # MinIO 文件存储
|
||||
│ └── datai-file-aliyun-oss/ # 阿里云 OSS 文件存储
|
||||
├── datai-plugins/ # 插件模块
|
||||
│ ├── datai-plugins-starter/ # 插件启动器
|
||||
│ ├── datai-mybatis-plus/ # MyBatis Plus 插件
|
||||
│ ├── datai-cache-redis/ # Redis 缓存插件
|
||||
│ ├── datai-cache-ehcache/ # Ehcache 缓存插件
|
||||
│ ├── datai-rabbitmq/ # RabbitMQ 消息队列插件
|
||||
│ ├── datai-websocket/ # WebSocket 插件
|
||||
│ ├── datai-netty/ # Netty 网络插件
|
||||
│ └── datai-atomikos/ # Atomikos 分布式事务插件
|
||||
└── datai-models/ # 模型模块
|
||||
├── datai-models-starter/ # 模型启动器
|
||||
├── datai-quartz/ # Quartz 定时任务
|
||||
├── datai-form/ # 表单管理
|
||||
├── datai-message/ # 消息管理
|
||||
├── datai-generator/ # 代码生成器
|
||||
├── datai-online/ # 在线用户
|
||||
└── datai-flowable/ # 工作流
|
||||
```
|
||||
|
||||
## 内置功能
|
||||
## 核心功能
|
||||
|
||||
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
|
||||
3. 岗位管理:配置系统用户所属担任职务。
|
||||
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
|
||||
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
|
||||
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
|
||||
7. 参数管理:对系统动态配置常用参数。
|
||||
8. 通知公告:系统通知公告信息发布维护。
|
||||
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||
10. 登录日志:系统登录日志记录查询包含登录异常。
|
||||
11. 在线用户:当前系统中活跃用户状态监控。
|
||||
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
|
||||
13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。
|
||||
14. 系统接口:根据业务代码自动生成相关的api接口文档。
|
||||
15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
|
||||
16. 缓存监控:对系统的缓存信息查询,命令统计等。
|
||||
17. 在线构建器:拖动表单元素生成相应的HTML代码。
|
||||
18. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。
|
||||
19. 支付场景
|
||||
20. 第三方登录场景
|
||||
21. 中间件场景
|
||||
### Salesforce 集成模块(当前实现)
|
||||
|
||||
## 演示图
|
||||
提供与 Salesforce 源系统的数据集成解决方案,支持从 Salesforce 同步数据到本地数据库。
|
||||
|
||||
### 新加功能和增强功能演示
|
||||
#### 认证管理
|
||||
- **多种登录方式**: OAuth2、SessionId、CLI、Legacy Credential
|
||||
- **会话管理**: 登录会话的创建、查询和销毁
|
||||
- **历史记录**: 登录历史记录的查询和统计
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="./doc/image/message.png"/></td>
|
||||
<td><img src="./doc/image/flowable.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./doc/image/online-mb-list.png"/></td>
|
||||
<td><img src="./doc/image/online-mb-edit.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./doc/image/online-mb-code.png"/></td>
|
||||
<td><img src="./doc/image/form-edit.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="./doc/image/code-edit.png"/></td>
|
||||
<td><img src="./doc/image/code-show.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
#### 数据同步(源系统 → 本地)
|
||||
- **Bulk API v1/v2**: 批量数据同步,支持大规模数据导入导出
|
||||
- **Partner API**: SOAP API 数据交互,支持 CRUD 操作
|
||||
- **REST API**: RESTful API 数据交互
|
||||
- **CDC 实时同步**: Change Data Capture 实时数据同步
|
||||
|
||||
### 原有功能演示
|
||||
#### 元数据管理
|
||||
- **对象管理**: Salesforce 对象的查询、同步和结构创建
|
||||
- **字段管理**: 对象字段的查询和管理
|
||||
- **Picklist 管理**: Picklist 值的查询和管理
|
||||
- **Lookup 过滤器**: Lookup 过滤器的查询和管理
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/cd1f90be5f2684f4560c9519c0f2a232ee8.jpg"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-8074972883b5ba0622e13246738ebba237a.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-9f88719cdfca9af2e58b352a20e23d43b12.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-39bf2584ec3a529b0d5a3b70d15c9b37646.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-936ec82d1f4872e1bc980927654b6007307.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-b2d62ceb95d2dd9b3fbe157bb70d26001e9.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-d67451d308b7a79ad6819723396f7c3d77a.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-8370a0d02977eebf6dbf854c8450293c937.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-49003ed83f60f633e7153609a53a2b644f7.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-d4fe726319ece268d4746602c39cffc0621.png"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-c195234bbcd30be6927f037a6755e6ab69c.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td>
|
||||
<td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
#### 批次管理
|
||||
- **批次创建**: 创建数据同步批次
|
||||
- **批次数据插入**: 将本地数据插入到 Salesforce,保存返回 ID
|
||||
- **批次数据更新**: 使用保存的 ID 更新 Salesforce 数据
|
||||
- **批次重试**: 失败批次的重试机制
|
||||
- **批次历史**: 批次操作历史记录
|
||||
|
||||
# 联系我们:
|
||||
#### 实时同步
|
||||
- **Pub/Sub API**: Salesforce Event Bus 实时数据订阅
|
||||
- **事件处理**: CDC 事件的接收和处理
|
||||
- **数据同步**: 实时数据同步到本地数据库
|
||||
- **服务管理**: 实时同步服务的启动、停止和重启
|
||||
|
||||
QQ交流群:744785891
|
||||
#### 监控日志
|
||||
- **API 调用日志**: Salesforce API 调用记录和统计
|
||||
- **同步日志**: 数据同步操作日志
|
||||
- **速率限制监控**: Salesforce API 速率限制监控
|
||||
- **实时同步日志**: CDC 实时同步日志
|
||||
|
||||
### 开发计划
|
||||
|
||||
#### 1. 数据推送到目标 Salesforce 系统
|
||||
- **目标系统认证**: 支持目标 Salesforce 系统的多种认证方式
|
||||
- **数据推送**: 支持将本地数据批量推送到目标 Salesforce 系统
|
||||
- **双向同步**: 实现源系统 → 本地 → 目标系统的数据流转
|
||||
- **数据映射**: 支持源系统和目标系统之间的字段映射
|
||||
- **推送监控**: 数据推送任务的监控和日志记录
|
||||
|
||||
#### 2. 源 Salesforce 系统文件下载
|
||||
- **文件元数据**: 获取 Salesforce 附件和文档的元数据信息
|
||||
- **文件下载**: 支持批量下载 Salesforce 中的文件(Attachment、Document、ContentVersion)
|
||||
- **文件存储**: 支持本地存储、MinIO、阿里云 OSS 等多种存储方式
|
||||
- **文件同步**: 文件与数据的关联同步
|
||||
- **下载监控**: 文件下载任务的监控和进度追踪
|
||||
|
||||
#### 3. User 权限同步集成系统
|
||||
- **权限元数据**: 同步 Salesforce User、Profile、PermissionSet、Role 等权限相关对象
|
||||
- **权限映射**: 将 Salesforce 权限映射到系统用户权限
|
||||
- **数据权限控制**: 基于用户权限控制数据访问范围
|
||||
- **权限同步**: 实时同步用户权限变更
|
||||
- **权限审计**: 权限变更的审计日志
|
||||
|
||||
#### 4. 集成可视化报表插件
|
||||
- **报表设计器**: 可视化拖拽式报表设计器,支持自定义报表布局
|
||||
- **数据源配置**: 支持从本地数据库和 Salesforce 系统配置数据源
|
||||
- **图表组件**: 提供丰富的图表类型(柱状图、折线图、饼图、表格等)
|
||||
- **数据筛选**: 支持动态筛选条件和参数化查询
|
||||
- **报表导出**: 支持导出为 PDF、Excel、图片等多种格式
|
||||
- **报表分享**: 支持报表分享和权限控制
|
||||
- **实时刷新**: 支持报表数据的实时刷新和定时更新
|
||||
- **报表模板**: 提供常用报表模板,快速生成标准报表
|
||||
|
||||
### 认证场景模块
|
||||
|
||||
- **OAuth 集成**: JustAuth 和微信 OAuth 登录
|
||||
- **双因子认证**: 邮箱和手机双因子认证
|
||||
- **用户管理**: OAuth 用户信息管理
|
||||
|
||||
|
||||
### 文件场景模块
|
||||
|
||||
- **本地存储**: 本地文件系统存储
|
||||
- **MinIO 存储**: MinIO 对象存储
|
||||
- **阿里云 OSS**: 阿里云对象存储服务
|
||||
- **文件管理**: 文件上传、下载和管理
|
||||
|
||||
### 插件模块
|
||||
|
||||
- **MyBatis Plus**: MyBatis 增强工具
|
||||
- **Redis 缓存**: Redis 分布式缓存
|
||||
- **Ehcache 缓存**: Ehcache 本地缓存
|
||||
- **RabbitMQ**: 消息队列中间件
|
||||
- **WebSocket**: WebSocket 实时通信
|
||||
- **Netty**: Netty 网络框架
|
||||
- **Atomikos**: 分布式事务管理
|
||||
|
||||
### 模型模块
|
||||
|
||||
- **Quartz**: 定时任务调度
|
||||
- **表单管理**: 动态表单生成和管理
|
||||
- **消息管理**: 系统消息管理
|
||||
- **代码生成器**: 代码自动生成工具
|
||||
- **在线用户**: 在线用户管理
|
||||
- **工作流**: Flowable 工作流引擎
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- JDK 21+
|
||||
- Maven 3.6+
|
||||
- MySQL 8.3.0+ 或 PostgreSQL 42.7.7+
|
||||
- Redis(可选,用于缓存)
|
||||
|
||||
### 配置说明
|
||||
|
||||
#### 关键配置项
|
||||
|
||||
| 配置项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| server.port | 8080 | 服务端口 |
|
||||
| datai.profile | D:/datai/uploadPath | 文件上传路径 |
|
||||
| datai.fileServer | local | 文件服务类型(local/minio/oss) |
|
||||
| datai.fileMaxSize | 50 | 文件上传最大大小(MB) |
|
||||
| spring.cache.type | redis | 缓存类型(redis/jcache) |
|
||||
| token.expireTime | 1440 | Token 有效期(分钟) |
|
||||
| pagehelper.helperDialect | mysql | 数据库方言 |
|
||||
|
||||
#### 启用配置文件
|
||||
|
||||
- `application-druid.yml`: 数据库连接池配置
|
||||
- `application-file.yml`: 文件服务配置
|
||||
- `application-auth.yml`: 认证配置
|
||||
- `application-pay.yml`: 支付配置
|
||||
- `application-plugins.yml`: 插件配置
|
||||
- `application-model.yml`: 模型配置
|
||||
- `application-apidoc.yml`: API 文档配置
|
||||
|
||||
### 构建项目
|
||||
|
||||
```bash
|
||||
mvn clean install
|
||||
```
|
||||
|
||||
### 运行项目
|
||||
|
||||
```bash
|
||||
cd datai-admin
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
启动成功后,访问:
|
||||
- 应用地址: http://localhost:8080
|
||||
- API 文档: http://localhost:8080/doc.html
|
||||
|
||||
## 文档
|
||||
|
||||
### 项目规则
|
||||
|
||||
项目采用基于单一真源 (Single Source of Truth) 的 Workflow,所有项目相关文档和决策均集中在 `docs/` 目录中。
|
||||
|
||||
### Salesforce 模块文档
|
||||
|
||||
详细文档请参考 [Salesforce 集成模块文档中心](datai-scenes/datai-scene-salesforce/docs/index.md)
|
||||
|
||||
- **唯一入口**: [datai-scene-salesforce/docs/index.md](datai-scenes/datai-scene-salesforce/docs/index.md)
|
||||
- **架构看板**: [datai-scene-salesforce/docs/Authentication.canvas](datai-scenes/datai-scene-salesforce/docs/Authentication.canvas)
|
||||
- **接口文档**: [datai-scene-salesforce/docs/api-docs/](datai-scenes/datai-scene-salesforce/docs/api-docs/)
|
||||
|
||||
### 文档目录
|
||||
|
||||
- **需求文档**: [datai-scenes/datai-scene-salesforce/docs/requirements/](datai-scenes/datai-scene-salesforce/docs/requirements/)
|
||||
- **设计文档**: [datai-scenes/datai-scene-salesforce/docs/design/](datai-scenes/datai-scene-salesforce/docs/design/)
|
||||
- **架构决策**: [datai-scenes/datai-scene-salesforce/docs/decisions/adr/](datai-scenes/datai-scene-salesforce/docs/decisions/adr/)
|
||||
- **提示词库**: [datai-scenes/datai-scene-salesforce/docs/prompts/](datai-scenes/datai-scene-salesforce/docs/prompts/)
|
||||
- **会话记录**: [datai-scenes/datai-scene-salesforce/docs/sessions/](datai-scenes/datai-scene-salesforce/docs/sessions/)
|
||||
- **变更日志**: [datai-scenes/datai-scene-salesforce/docs/changelog/](datai-scenes/datai-scene-salesforce/docs/changelog/)
|
||||
|
||||
## 贡献指南
|
||||
|
||||
请参考 [CONTRIBUTING.md](CONTRIBUTING.md) 了解如何参与项目贡献。
|
||||
|
||||
## 变更日志
|
||||
|
||||
请参考 [CHANGELOG.md](CHANGELOG.md) 查看项目变更记录。
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目基于若依Geek框架开发,遵循相应的开源许可证。
|
||||
|
||||
## 联系方式
|
||||
|
||||
- 项目地址: https://gitee.com/geek-xd/datai-springboot3.git
|
||||
- 文档中心: [datai-scenes/datai-scene-salesforce/docs/index.md](datai-scenes/datai-scene-salesforce/docs/index.md)
|
||||
|
||||
@ -28,6 +28,7 @@ import com.datai.common.utils.poi.ExcelUtil;
|
||||
import com.datai.common.core.page.TableDataInfo;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 数据批次Controller
|
||||
@ -38,6 +39,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
@RestController
|
||||
@RequestMapping("/integration/batch")
|
||||
@Tag(name = "【数据批次】管理")
|
||||
@Slf4j
|
||||
public class DataiIntegrationBatchController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
@ -180,4 +182,104 @@ public class DataiIntegrationBatchController extends BaseController
|
||||
return error((String) result.get("message"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批次数据插入目标系统
|
||||
*
|
||||
* 该方法用于将本地数据库中的指定批次数据插入到目标Salesforce系统
|
||||
* 插入操作会查询本地表中符合批次条件的数据,然后通过SOAP API插入到目标系统
|
||||
* 插入成功后,会将返回的ID保存到new_id字段,并更新is_insert字段
|
||||
*
|
||||
* @param id 批次ID,用于标识需要插入的批次
|
||||
* @param targetOrgType 目标ORG类型,用于标识目标Salesforce系统
|
||||
* @return AjaxResult 插入结果,包含成功/失败状态、插入数据量、耗时等信息
|
||||
* - 成功时返回:success=true, message="批次数据插入完成", 以及详细的插入信息
|
||||
* - 失败时返回:success=false, message=错误信息
|
||||
*/
|
||||
@Operation(summary = "批次数据插入目标系统")
|
||||
@PreAuthorize("@ss.hasPermi('integration:batch:insertData')")
|
||||
@Log(title = "数据批次", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/{id}/insertData")
|
||||
public AjaxResult insertBatchDataToTarget(@PathVariable("id") Integer id,
|
||||
@org.springframework.web.bind.annotation.RequestParam("targetOrgType") String targetOrgType)
|
||||
{
|
||||
if (id == null) {
|
||||
log.error("批次ID为空,无法插入数据");
|
||||
return error("批次ID不能为空");
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型为空,无法插入数据");
|
||||
return error("目标ORG类型不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("开始插入批次数据到目标系统,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
Map<String, Object> result = dataiIntegrationBatchService.insertBatchDataToTarget(id, targetOrgType);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
log.info("批次数据插入成功,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
return success(result);
|
||||
} else {
|
||||
log.error("批次数据插入失败,批次ID: {}, 目标ORG类型: {}, 错误信息: {}",
|
||||
id, targetOrgType, result.get("message"));
|
||||
return error((String) result.get("message"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("插入批次数据时发生异常,批次ID: {}, 目标ORG类型: {}", id, targetOrgType, e);
|
||||
return error("插入批次数据时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批次数据更新目标系统
|
||||
*
|
||||
* 该方法用于将本地数据库中的指定批次数据更新到目标Salesforce系统
|
||||
* 更新操作会查询本地表中符合批次条件的数据,然后通过SOAP API更新到目标系统
|
||||
* 更新时使用new_id作为目标系统的ID,更新成功后,更新is_update字段
|
||||
*
|
||||
* @param id 批次ID,用于标识需要更新的批次
|
||||
* @param targetOrgType 目标ORG类型,用于标识目标Salesforce系统
|
||||
* @return AjaxResult 更新结果,包含成功/失败状态、更新数据量、耗时等信息
|
||||
* - 成功时返回:success=true, message="批次数据更新完成", 以及详细的更新信息
|
||||
* - 失败时返回:success=false, message=错误信息
|
||||
*/
|
||||
@Operation(summary = "批次数据更新目标系统")
|
||||
@PreAuthorize("@ss.hasPermi('integration:batch:updateData')")
|
||||
@Log(title = "数据批次", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/{id}/updateData")
|
||||
public AjaxResult updateBatchDataToTarget(@PathVariable("id") Integer id,
|
||||
@org.springframework.web.bind.annotation.RequestParam("targetOrgType") String targetOrgType)
|
||||
{
|
||||
if (id == null) {
|
||||
log.error("批次ID为空,无法更新数据");
|
||||
return error("批次ID不能为空");
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型为空,无法更新数据");
|
||||
return error("目标ORG类型不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("开始更新批次数据到目标系统,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
Map<String, Object> result = dataiIntegrationBatchService.updateBatchDataToTarget(id, targetOrgType);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
log.info("批次数据更新成功,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
return success(result);
|
||||
} else {
|
||||
log.error("批次数据更新失败,批次ID: {}, 目标ORG类型: {}, 错误信息: {}",
|
||||
id, targetOrgType, result.get("message"));
|
||||
return error((String) result.get("message"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新批次数据时发生异常,批次ID: {}, 目标ORG类型: {}", id, targetOrgType, e);
|
||||
return error("更新批次数据时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -318,4 +318,79 @@ public class DataiIntegrationObjectController extends BaseController
|
||||
return error("同步多个对象数据时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "推送单对象数据到目标系统")
|
||||
@PreAuthorize("@ss.hasPermi('integration:object:pushData')")
|
||||
@Log(title = "对象同步控制", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/{id}/pushData")
|
||||
public AjaxResult pushObjectData(@PathVariable("id") Integer id,
|
||||
@org.springframework.web.bind.annotation.RequestParam("targetOrgType") String targetOrgType)
|
||||
{
|
||||
if (id == null) {
|
||||
log.error("对象ID为空,无法推送数据");
|
||||
return error("对象ID不能为空");
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型为空,无法推送数据");
|
||||
return error("目标ORG类型不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("开始推送对象数据到目标系统,对象ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
Map<String, Object> result = dataiIntegrationObjectService.pushSingleObjectDataToTarget(id, targetOrgType);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
log.info("对象数据推送成功,对象ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
return success(result);
|
||||
} else {
|
||||
log.error("对象数据推送失败,对象ID: {}, 目标ORG类型: {}, 错误信息: {}",
|
||||
id, targetOrgType, result.get("message"));
|
||||
return error((String) result.get("message"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("推送对象数据时发生异常,对象ID: {}, 目标ORG类型: {}", id, targetOrgType, e);
|
||||
return error("推送对象数据时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "推送多个对象数据到目标系统")
|
||||
@PreAuthorize("@ss.hasPermi('integration:object:pushData')")
|
||||
@Log(title = "对象同步控制", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/pushMultipleData")
|
||||
public AjaxResult pushMultipleObjectData(@RequestBody Integer[] ids,
|
||||
@org.springframework.web.bind.annotation.RequestParam("targetOrgType") String targetOrgType)
|
||||
{
|
||||
if (ids == null || ids.length == 0) {
|
||||
log.error("对象ID数组为空,无法推送数据");
|
||||
return error("对象ID不能为空");
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型为空,无法推送数据");
|
||||
return error("目标ORG类型不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("开始推送多个对象数据到目标系统,对象ID数量: {}, 目标ORG类型: {}", ids.length, targetOrgType);
|
||||
|
||||
Map<String, Object> result = dataiIntegrationObjectService.pushMultipleObjectDataToTarget(ids, targetOrgType);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
log.info("多对象数据推送完成,成功数量: {}, 失败数量: {}",
|
||||
result.get("successCount"), result.get("failureCount"));
|
||||
return success(result);
|
||||
} else {
|
||||
log.error("多对象数据推送失败,错误信息: {}", result.get("message"));
|
||||
return error((String) result.get("message"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("推送多个对象数据时发生异常", e);
|
||||
return error("推送多个对象数据时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,12 @@ import com.datai.common.core.domain.BaseEntity;
|
||||
* - lastSyncTime: 最后同步时间(每次同步时更新)
|
||||
* - updateTime: 更新时间(每次更新时自动设置)
|
||||
*
|
||||
* 3. 推送时更新的字段:
|
||||
* - sfInsertNum: SF插入数据量(每次推送时更新)
|
||||
* - sfUpdateNum: SF更新数据量(每次推送时更新)
|
||||
* - firstPushTime: 首次推送时间(仅在首次推送时设置)
|
||||
* - lastPushTime: 最后推送时间(每次推送时更新)
|
||||
*
|
||||
* @author datai
|
||||
* @date 2026-01-01
|
||||
*/
|
||||
@ -67,6 +73,16 @@ public class DataiIntegrationBatch extends BaseEntity
|
||||
@Excel(name = "本地数据量")
|
||||
private Integer dbNum;
|
||||
|
||||
/** SF插入数据量 */
|
||||
@Schema(title = "SF插入数据量")
|
||||
@Excel(name = "SF插入数据量")
|
||||
private Integer sfInsertNum;
|
||||
|
||||
/** SF更新数据量 */
|
||||
@Schema(title = "SF更新数据量")
|
||||
@Excel(name = "SF更新数据量")
|
||||
private Integer sfUpdateNum;
|
||||
|
||||
/** 同步类型 */
|
||||
@Schema(title = "同步类型")
|
||||
@Excel(name = "同步类型")
|
||||
@ -101,6 +117,16 @@ public class DataiIntegrationBatch extends BaseEntity
|
||||
@Schema(title = "最后同步时间")
|
||||
@Excel(name = "最后同步时间")
|
||||
private LocalDateTime lastSyncTime;
|
||||
|
||||
/** 首次推送时间 */
|
||||
@Schema(title = "首次推送时间")
|
||||
@Excel(name = "首次推送时间")
|
||||
private LocalDateTime firstPushTime;
|
||||
|
||||
/** 最后推送时间 */
|
||||
@Schema(title = "最后推送时间")
|
||||
@Excel(name = "最后推送时间")
|
||||
private LocalDateTime lastPushTime;
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
@ -167,6 +193,28 @@ public class DataiIntegrationBatch extends BaseEntity
|
||||
}
|
||||
|
||||
|
||||
public void setSfInsertNum(Integer sfInsertNum)
|
||||
{
|
||||
this.sfInsertNum = sfInsertNum;
|
||||
}
|
||||
|
||||
public Integer getSfInsertNum()
|
||||
{
|
||||
return sfInsertNum;
|
||||
}
|
||||
|
||||
|
||||
public void setSfUpdateNum(Integer sfUpdateNum)
|
||||
{
|
||||
this.sfUpdateNum = sfUpdateNum;
|
||||
}
|
||||
|
||||
public Integer getSfUpdateNum()
|
||||
{
|
||||
return sfUpdateNum;
|
||||
}
|
||||
|
||||
|
||||
public void setSyncType(String syncType)
|
||||
{
|
||||
this.syncType = syncType;
|
||||
@ -244,6 +292,28 @@ public class DataiIntegrationBatch extends BaseEntity
|
||||
}
|
||||
|
||||
|
||||
public void setFirstPushTime(LocalDateTime firstPushTime)
|
||||
{
|
||||
this.firstPushTime = firstPushTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getFirstPushTime()
|
||||
{
|
||||
return firstPushTime;
|
||||
}
|
||||
|
||||
|
||||
public void setLastPushTime(LocalDateTime lastPushTime)
|
||||
{
|
||||
this.lastPushTime = lastPushTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastPushTime()
|
||||
{
|
||||
return lastPushTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -254,6 +324,8 @@ public class DataiIntegrationBatch extends BaseEntity
|
||||
.append("label", getLabel())
|
||||
.append("sfNum", getSfNum())
|
||||
.append("dbNum", getDbNum())
|
||||
.append("sfInsertNum", getSfInsertNum())
|
||||
.append("sfUpdateNum", getSfUpdateNum())
|
||||
.append("syncType", getSyncType())
|
||||
.append("batchField", getBatchField())
|
||||
.append("syncStatus", getSyncStatus())
|
||||
@ -261,6 +333,8 @@ public class DataiIntegrationBatch extends BaseEntity
|
||||
.append("syncEndDate", getSyncEndDate())
|
||||
.append("firstSyncTime", getFirstSyncTime())
|
||||
.append("lastSyncTime", getLastSyncTime())
|
||||
.append("firstPushTime", getFirstPushTime())
|
||||
.append("lastPushTime", getLastPushTime())
|
||||
.append("remark", getRemark())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
|
||||
@ -42,6 +42,12 @@ public class DataiIntegrationBatchDto implements Serializable
|
||||
/** 本地数据量 */
|
||||
private Integer dbNum;
|
||||
|
||||
/** SF插入数据量 */
|
||||
private Integer sfInsertNum;
|
||||
|
||||
/** SF更新数据量 */
|
||||
private Integer sfUpdateNum;
|
||||
|
||||
/** 同步类型 */
|
||||
private String syncType;
|
||||
|
||||
@ -63,6 +69,12 @@ public class DataiIntegrationBatchDto implements Serializable
|
||||
/** 最后同步时间 */
|
||||
private LocalDateTime lastSyncTime;
|
||||
|
||||
/** 首次推送时间 */
|
||||
private LocalDateTime firstPushTime;
|
||||
|
||||
/** 最后推送时间 */
|
||||
private LocalDateTime lastPushTime;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
|
||||
@ -37,6 +37,12 @@ public class DataiIntegrationBatchVo implements Serializable {
|
||||
/** 本地数据量 */
|
||||
private Integer dbNum;
|
||||
|
||||
/** SF插入数据量 */
|
||||
private Integer sfInsertNum;
|
||||
|
||||
/** SF更新数据量 */
|
||||
private Integer sfUpdateNum;
|
||||
|
||||
/** 同步类型 */
|
||||
private String syncType;
|
||||
|
||||
@ -58,6 +64,12 @@ public class DataiIntegrationBatchVo implements Serializable {
|
||||
/** 最后同步时间 */
|
||||
private LocalDateTime lastSyncTime;
|
||||
|
||||
/** 首次推送时间 */
|
||||
private LocalDateTime firstPushTime;
|
||||
|
||||
/** 最后推送时间 */
|
||||
private LocalDateTime lastPushTime;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
|
||||
@ -90,4 +90,22 @@ public interface IDataiIntegrationBatchService
|
||||
* @return 所有批次统计信息
|
||||
*/
|
||||
public Map<String, Object> getAllBatchStatistics();
|
||||
|
||||
/**
|
||||
* 批次数据插入目标系统
|
||||
*
|
||||
* @param id 批次ID
|
||||
* @param targetOrgType 目标ORG类型
|
||||
* @return 插入结果
|
||||
*/
|
||||
public Map<String, Object> insertBatchDataToTarget(Integer id, String targetOrgType);
|
||||
|
||||
/**
|
||||
* 批次数据更新目标系统
|
||||
*
|
||||
* @param id 批次ID
|
||||
* @param targetOrgType 目标ORG类型
|
||||
* @return 更新结果
|
||||
*/
|
||||
public Map<String, Object> updateBatchDataToTarget(Integer id, String targetOrgType);
|
||||
}
|
||||
|
||||
@ -136,5 +136,23 @@ public interface IDataiIntegrationObjectService
|
||||
*/
|
||||
public Map<String, Object> syncMultipleObjectData(Integer[] ids);
|
||||
|
||||
/**
|
||||
* 推送单对象数据到目标系统
|
||||
*
|
||||
* @param id 对象ID
|
||||
* @param targetOrgType 目标ORG类型
|
||||
* @return 推送结果
|
||||
*/
|
||||
public Map<String, Object> pushSingleObjectDataToTarget(Integer id, String targetOrgType);
|
||||
|
||||
/**
|
||||
* 推送多个对象数据到目标系统
|
||||
*
|
||||
* @param ids 对象ID集合
|
||||
* @param targetOrgType 目标ORG类型
|
||||
* @return 推送结果
|
||||
*/
|
||||
public Map<String, Object> pushMultipleObjectDataToTarget(Integer[] ids, String targetOrgType);
|
||||
|
||||
Set<DataiIntegrationObject> getRealtimeSyncObjects();
|
||||
}
|
||||
|
||||
@ -993,4 +993,400 @@ public class DataiIntegrationBatchServiceImpl implements IDataiIntegrationBatchS
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> insertBatchDataToTarget(Integer id, String targetOrgType) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
log.info("开始插入批次数据到目标系统,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
if (id == null) {
|
||||
log.error("批次ID不能为空");
|
||||
result.put("success", false);
|
||||
result.put("message", "批次ID不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型不能为空");
|
||||
result.put("success", false);
|
||||
result.put("message", "目标ORG类型不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
DataiIntegrationBatch batch = selectDataiIntegrationBatchById(id);
|
||||
if (batch == null) {
|
||||
log.error("批次不存在,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次不存在");
|
||||
return result;
|
||||
}
|
||||
|
||||
String objectApi = batch.getApi();
|
||||
if (objectApi == null || objectApi.trim().isEmpty()) {
|
||||
log.error("批次对象API为空,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次对象API不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
String tableName = objectApi.trim();
|
||||
if (customMapper.checkTable(tableName) == null) {
|
||||
log.error("本地表不存在,表名: {}", tableName);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地表不存在: " + tableName);
|
||||
return result;
|
||||
}
|
||||
|
||||
log.info("准备插入批次 {} 的对象 {} 数据到目标系统", id, objectApi);
|
||||
|
||||
IPartnerV1Connection targetConnection = null;
|
||||
try {
|
||||
targetConnection = soapConnectionFactory.getConnection(targetOrgType);
|
||||
if (targetConnection == null) {
|
||||
log.error("无法获取目标系统连接,ORG类型: {}", targetOrgType);
|
||||
result.put("success", false);
|
||||
result.put("message", "无法获取目标系统连接");
|
||||
return result;
|
||||
}
|
||||
|
||||
DescribeSObjectResult objDetail = targetConnection.describeSObject(objectApi);
|
||||
if (objDetail == null) {
|
||||
log.error("目标系统不存在该对象,对象API: {}", objectApi);
|
||||
result.put("success", false);
|
||||
result.put("message", "目标系统不存在该对象: " + objectApi);
|
||||
return result;
|
||||
}
|
||||
|
||||
String batchField = batch.getBatchField();
|
||||
String whereClause = "";
|
||||
if (batchField != null && !batchField.trim().isEmpty()) {
|
||||
whereClause = "WHERE " + batchField.trim() + " = '" + batch.getBatchValue() + "'";
|
||||
}
|
||||
|
||||
log.info("开始查询本地批次数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
List<Map<String, Object>> dataList = customMapper.list("*", tableName, whereClause);
|
||||
|
||||
if (dataList == null || dataList.isEmpty()) {
|
||||
log.warn("本地批次没有数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地批次没有数据");
|
||||
return result;
|
||||
}
|
||||
|
||||
log.info("查询到 {} 条本地批次数据,准备插入到目标系统", dataList.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
List<Map<String, Object>> failedRecords = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Map<String, Object> dataMap = dataList.get(i);
|
||||
try {
|
||||
String localId = (String) dataMap.get("id");
|
||||
if (localId == null || localId.isEmpty()) {
|
||||
log.warn("记录ID为空,跳过该记录,索引: {}", i);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", "记录ID为空");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> fieldsToInsert = new HashMap<>();
|
||||
for (String key : dataMap.keySet()) {
|
||||
if (!"id".equalsIgnoreCase(key) && !"new_id".equalsIgnoreCase(key) &&
|
||||
!"is_insert".equalsIgnoreCase(key) && !"is_update".equalsIgnoreCase(key) &&
|
||||
dataMap.get(key) != null) {
|
||||
fieldsToInsert.put(key, dataMap.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldsToInsert.isEmpty()) {
|
||||
log.warn("记录没有需要插入的字段,跳过该记录,ID: {}", localId);
|
||||
continue;
|
||||
}
|
||||
|
||||
SObject sObject = new SObject();
|
||||
sObject.setType(objectApi);
|
||||
for (Map.Entry<String, Object> entry : fieldsToInsert.entrySet()) {
|
||||
sObject.setField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
SaveResult[] saveResults = targetConnection.create(new SObject[]{sObject});
|
||||
if (saveResults != null && saveResults.length > 0 && saveResults[0].isSuccess()) {
|
||||
String newId = saveResults[0].getId();
|
||||
|
||||
List<Map<String, Object>> updateMaps = new ArrayList<>();
|
||||
Map<String, Object> newIdMap = new HashMap<>();
|
||||
newIdMap.put("key", "new_id");
|
||||
newIdMap.put("value", newId);
|
||||
updateMaps.add(newIdMap);
|
||||
|
||||
Map<String, Object> isInsertMap = new HashMap<>();
|
||||
isInsertMap.put("key", "is_insert");
|
||||
isInsertMap.put("value", 1);
|
||||
updateMaps.add(isInsertMap);
|
||||
|
||||
customMapper.updateById(tableName, updateMaps, localId);
|
||||
|
||||
successCount++;
|
||||
log.debug("记录插入成功,本地ID: {}, 新ID: {}", localId, newId);
|
||||
} else {
|
||||
String errorMsg = saveResults != null && saveResults.length > 0 ?
|
||||
saveResults[0].getErrors()[0].getMessage() : "未知错误";
|
||||
log.error("记录插入失败,本地ID: {}, 错误: {}", localId, errorMsg);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("localId", localId);
|
||||
failedRecord.put("reason", errorMsg);
|
||||
failedRecords.add(failedRecord);
|
||||
}
|
||||
|
||||
if ((i + 1) % 100 == 0) {
|
||||
log.info("已插入 {}/{} 条记录", i + 1, dataList.size());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("插入记录失败,索引: {}, 错误: {}", i, e.getMessage());
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", e.getMessage());
|
||||
failedRecords.add(failedRecord);
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "批次数据插入完成");
|
||||
result.put("batchId", id);
|
||||
result.put("api", objectApi);
|
||||
result.put("label", batch.getLabel());
|
||||
result.put("targetOrgType", targetOrgType);
|
||||
result.put("totalCount", dataList.size());
|
||||
result.put("successCount", successCount);
|
||||
result.put("failureCount", failureCount);
|
||||
result.put("duration", duration);
|
||||
result.put("failedRecords", failedRecords);
|
||||
|
||||
log.info("批次数据插入完成,批次ID: {}, 对象API: {}, 总数: {}, 成功: {}, 失败: {}, 耗时: {}ms",
|
||||
id, objectApi, dataList.size(), successCount, failureCount, duration);
|
||||
|
||||
} finally {
|
||||
if (targetConnection != null) {
|
||||
try {
|
||||
targetConnection.logout();
|
||||
} catch (Exception e) {
|
||||
log.error("关闭目标系统连接失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("插入批次数据到目标系统失败,批次ID: {}", id, e);
|
||||
result.put("success", false);
|
||||
result.put("message", "插入批次数据到目标系统失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, Object> updateBatchDataToTarget(Integer id, String targetOrgType) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
log.info("开始更新批次数据到目标系统,批次ID: {}, 目标ORG类型: {}", id, targetOrgType);
|
||||
|
||||
if (id == null) {
|
||||
log.error("批次ID不能为空");
|
||||
result.put("success", false);
|
||||
result.put("message", "批次ID不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (targetOrgType == null || targetOrgType.trim().isEmpty()) {
|
||||
log.error("目标ORG类型不能为空");
|
||||
result.put("success", false);
|
||||
result.put("message", "目标ORG类型不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
DataiIntegrationBatch batch = selectDataiIntegrationBatchById(id);
|
||||
if (batch == null) {
|
||||
log.error("批次不存在,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次不存在");
|
||||
return result;
|
||||
}
|
||||
|
||||
String objectApi = batch.getApi();
|
||||
if (objectApi == null || objectApi.trim().isEmpty()) {
|
||||
log.error("批次对象API为空,批次ID: {}", id);
|
||||
result.put("success", false);
|
||||
result.put("message", "批次对象API不能为空");
|
||||
return result;
|
||||
}
|
||||
|
||||
String tableName = objectApi.trim();
|
||||
if (customMapper.checkTable(tableName) == null) {
|
||||
log.error("本地表不存在,表名: {}", tableName);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地表不存在: " + tableName);
|
||||
return result;
|
||||
}
|
||||
|
||||
log.info("准备更新批次 {} 的对象 {} 数据到目标系统", id, objectApi);
|
||||
|
||||
IPartnerV1Connection targetConnection = null;
|
||||
try {
|
||||
targetConnection = soapConnectionFactory.getConnection(targetOrgType);
|
||||
if (targetConnection == null) {
|
||||
log.error("无法获取目标系统连接,ORG类型: {}", targetOrgType);
|
||||
result.put("success", false);
|
||||
result.put("message", "无法获取目标系统连接");
|
||||
return result;
|
||||
}
|
||||
|
||||
DescribeSObjectResult objDetail = targetConnection.describeSObject(objectApi);
|
||||
if (objDetail == null) {
|
||||
log.error("目标系统不存在该对象,对象API: {}", objectApi);
|
||||
result.put("success", false);
|
||||
result.put("message", "目标系统不存在该对象: " + objectApi);
|
||||
return result;
|
||||
}
|
||||
|
||||
String batchField = batch.getBatchField();
|
||||
String whereClause = "";
|
||||
if (batchField != null && !batchField.trim().isEmpty()) {
|
||||
whereClause = "WHERE " + batchField.trim() + " = '" + batch.getBatchValue() + "'";
|
||||
}
|
||||
|
||||
log.info("开始查询本地批次数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
List<Map<String, Object>> dataList = customMapper.list("*", tableName, whereClause);
|
||||
|
||||
if (dataList == null || dataList.isEmpty()) {
|
||||
log.warn("本地批次没有数据,表名: {}, 条件: {}", tableName, whereClause);
|
||||
result.put("success", false);
|
||||
result.put("message", "本地批次没有数据");
|
||||
return result;
|
||||
}
|
||||
|
||||
log.info("查询到 {} 条本地批次数据,准备更新到目标系统", dataList.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failureCount = 0;
|
||||
List<Map<String, Object>> failedRecords = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Map<String, Object> dataMap = dataList.get(i);
|
||||
try {
|
||||
String localId = (String) dataMap.get("id");
|
||||
String newId = (String) dataMap.get("new_id");
|
||||
|
||||
if (localId == null || localId.isEmpty()) {
|
||||
log.warn("记录ID为空,跳过该记录,索引: {}", i);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", "记录ID为空");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newId == null || newId.isEmpty()) {
|
||||
log.warn("记录new_id为空,跳过该记录,本地ID: {}", localId);
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("localId", localId);
|
||||
failedRecord.put("reason", "new_id为空,请先执行插入操作");
|
||||
failedRecords.add(failedRecord);
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> fieldsToUpdate = new HashMap<>();
|
||||
for (String key : dataMap.keySet()) {
|
||||
if (!"id".equalsIgnoreCase(key) && !"new_id".equalsIgnoreCase(key) &&
|
||||
!"is_insert".equalsIgnoreCase(key) && !"is_update".equalsIgnoreCase(key) &&
|
||||
dataMap.get(key) != null) {
|
||||
fieldsToUpdate.put(key, dataMap.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldsToUpdate.isEmpty()) {
|
||||
log.warn("记录没有需要更新的字段,跳过该记录,本地ID: {}", localId);
|
||||
continue;
|
||||
}
|
||||
|
||||
targetConnection.update(objectApi, newId, fieldsToUpdate);
|
||||
|
||||
List<Map<String, Object>> updateMaps = new ArrayList<>();
|
||||
Map<String, Object> isUpdateMap = new HashMap<>();
|
||||
isUpdateMap.put("key", "is_update");
|
||||
isUpdateMap.put("value", 1);
|
||||
updateMaps.add(isUpdateMap);
|
||||
|
||||
customMapper.updateById(tableName, updateMaps, localId);
|
||||
|
||||
successCount++;
|
||||
log.debug("记录更新成功,本地ID: {}, 目标ID: {}", localId, newId);
|
||||
|
||||
if ((i + 1) % 100 == 0) {
|
||||
log.info("已更新 {}/{} 条记录", i + 1, dataList.size());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新记录失败,索引: {}, 错误: {}", i, e.getMessage());
|
||||
failureCount++;
|
||||
Map<String, Object> failedRecord = new HashMap<>();
|
||||
failedRecord.put("index", i);
|
||||
failedRecord.put("reason", e.getMessage());
|
||||
failedRecords.add(failedRecord);
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "批次数据更新完成");
|
||||
result.put("batchId", id);
|
||||
result.put("api", objectApi);
|
||||
result.put("label", batch.getLabel());
|
||||
result.put("targetOrgType", targetOrgType);
|
||||
result.put("totalCount", dataList.size());
|
||||
result.put("successCount", successCount);
|
||||
result.put("failureCount", failureCount);
|
||||
result.put("duration", duration);
|
||||
result.put("failedRecords", failedRecords);
|
||||
|
||||
log.info("批次数据更新完成,批次ID: {}, 对象API: {}, 总数: {}, 成功: {}, 失败: {}, 耗时: {}ms",
|
||||
id, objectApi, dataList.size(), successCount, failureCount, duration);
|
||||
|
||||
} finally {
|
||||
if (targetConnection != null) {
|
||||
try {
|
||||
targetConnection.logout();
|
||||
} catch (Exception e) {
|
||||
log.error("关闭目标系统连接失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新批次数据到目标系统失败,批次ID: {}", id, e);
|
||||
result.put("success", false);
|
||||
result.put("message", "更新批次数据到目标系统失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1336,4 +1336,17 @@ public class DataiIntegrationObjectServiceImpl implements IDataiIntegrationObjec
|
||||
|
||||
return realtimeSyncObjects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> pushSingleObjectDataToTarget(Integer id, String targetOrgType) {
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> pushMultipleObjectDataToTarget(Integer[] ids, String targetOrgType) {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="label" column="label" />
|
||||
<result property="sfNum" column="sf_num" />
|
||||
<result property="dbNum" column="db_num" />
|
||||
<result property="sfInsertNum" column="sf_insert_num" />
|
||||
<result property="sfUpdateNum" column="sf_update_num" />
|
||||
<result property="syncType" column="sync_type" />
|
||||
<result property="batchField" column="batch_field" />
|
||||
<result property="syncStatus" column="sync_status" />
|
||||
@ -18,6 +20,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="syncEndDate" column="sync_end_date" />
|
||||
<result property="firstSyncTime" column="first_sync_time" />
|
||||
<result property="lastSyncTime" column="last_sync_time" />
|
||||
<result property="firstPushTime" column="first_push_time" />
|
||||
<result property="lastPushTime" column="last_push_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
@ -33,6 +37,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
dib.label,
|
||||
dib.sf_num,
|
||||
dib.db_num,
|
||||
dib.sf_insert_num,
|
||||
dib.sf_update_num,
|
||||
dib.sync_type,
|
||||
dib.batch_field,
|
||||
dib.sync_status,
|
||||
@ -40,6 +46,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
dib.sync_end_date,
|
||||
dib.first_sync_time,
|
||||
dib.last_sync_time,
|
||||
dib.first_push_time,
|
||||
dib.last_push_time,
|
||||
dib.remark,
|
||||
dib.create_by,
|
||||
dib.create_time,
|
||||
@ -79,6 +87,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="label != null">label,</if>
|
||||
<if test="sfNum != null">sf_num,</if>
|
||||
<if test="dbNum != null">db_num,</if>
|
||||
<if test="sfInsertNum != null">sf_insert_num,</if>
|
||||
<if test="sfUpdateNum != null">sf_update_num,</if>
|
||||
<if test="syncType != null">sync_type,</if>
|
||||
<if test="batchField != null">batch_field,</if>
|
||||
<if test="syncStatus != null">sync_status,</if>
|
||||
@ -86,6 +96,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="syncEndDate != null">sync_end_date,</if>
|
||||
<if test="firstSyncTime != null">first_sync_time,</if>
|
||||
<if test="lastSyncTime != null">last_sync_time,</if>
|
||||
<if test="firstPushTime != null">first_push_time,</if>
|
||||
<if test="lastPushTime != null">last_push_time,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
@ -98,6 +110,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="label != null">#{label},</if>
|
||||
<if test="sfNum != null">#{sfNum},</if>
|
||||
<if test="dbNum != null">#{dbNum},</if>
|
||||
<if test="sfInsertNum != null">#{sfInsertNum},</if>
|
||||
<if test="sfUpdateNum != null">#{sfUpdateNum},</if>
|
||||
<if test="syncType != null">#{syncType},</if>
|
||||
<if test="batchField != null">#{batchField},</if>
|
||||
<if test="syncStatus != null">#{syncStatus},</if>
|
||||
@ -105,6 +119,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="syncEndDate != null">#{syncEndDate},</if>
|
||||
<if test="firstSyncTime != null">#{firstSyncTime},</if>
|
||||
<if test="lastSyncTime != null">#{lastSyncTime},</if>
|
||||
<if test="firstPushTime != null">#{firstPushTime},</if>
|
||||
<if test="lastPushTime != null">#{lastPushTime},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
@ -121,6 +137,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="label != null">label = #{label},</if>
|
||||
<if test="sfNum != null">sf_num = #{sfNum},</if>
|
||||
<if test="dbNum != null">db_num = #{dbNum},</if>
|
||||
<if test="sfInsertNum != null">sf_insert_num = #{sfInsertNum},</if>
|
||||
<if test="sfUpdateNum != null">sf_update_num = #{sfUpdateNum},</if>
|
||||
<if test="syncType != null">sync_type = #{syncType},</if>
|
||||
<if test="batchField != null">batch_field = #{batchField},</if>
|
||||
<if test="syncStatus != null">sync_status = #{syncStatus},</if>
|
||||
@ -128,6 +146,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="syncEndDate != null">sync_end_date = #{syncEndDate},</if>
|
||||
<if test="firstSyncTime != null">first_sync_time = #{firstSyncTime},</if>
|
||||
<if test="lastSyncTime != null">last_sync_time = #{lastSyncTime},</if>
|
||||
<if test="firstPushTime != null">first_push_time = #{firstPushTime},</if>
|
||||
<if test="lastPushTime != null">last_push_time = #{lastPushTime},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
|
||||
@ -45,6 +45,7 @@
|
||||
- [03创建工作流提示词.md](prompts/03创建工作流提示词.md)
|
||||
- [04工作流整合白板提示词.md](prompts/04工作流整合白板提示词.md)
|
||||
- [05创建项目规则提示词.md](prompts/05创建项目规则提示词.md)
|
||||
- [06更新当前项目README说明文档.md](prompts/06更新当前项目README说明文档.md) - 更新项目README文档的提示词
|
||||
|
||||
### 6. 会话记录
|
||||
|
||||
@ -137,4 +138,4 @@
|
||||
## 最后更新
|
||||
|
||||
- 更新时间: 2026-01-14
|
||||
- 更新内容: 添加 DataiConfigurationController 接口文档,包含查询配置列表、导出配置列表、获取配置详细信息、新增配置、修改配置、删除配置、刷新配置缓存、查询配置缓存状态八个接口的详细文档
|
||||
- 更新内容: 添加 06更新当前项目README说明文档.md 提示词文档,用于指导 AI 根据项目实际情况更新 README 文档
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
角色设定:你是一位资深的 Java 架构师,擅长编写清晰、专业的项目文档。
|
||||
|
||||
任务目标:请深度扫描我提供的代码,并根据代码现状全面完善 README.md 文件。
|
||||
|
||||
扫描要求:
|
||||
|
||||
技术栈识别:自动识别并列出项目使用的 Java 版本、框架(如 Spring Boot)、构建工具(Maven/Gradle)及关键依赖。
|
||||
|
||||
核心模块说明:分析包结构,解释主要业务模块的功能。
|
||||
|
||||
配置说明:扫描 application.yml 或 properties 文件,提取关键配置项说明。
|
||||
|
||||
README 结构规范:
|
||||
|
||||
Project Title & Description: 简洁明了的项目名称和功能概述。
|
||||
|
||||
Tech Stack: 采用表格或列表形式展示。
|
||||
|
||||
Project Structure: 展示核心目录树并注释关键文件夹。
|
||||
|
||||
Getting Started: 包含环境要求、编译指令(如 ./mvnw clean install)和启动方式。
|
||||
|
||||
Key Features: 基于代码逻辑总结出的核心功能点。
|
||||
|
||||
执行指令:请先输出一份你观察到的项目概览,在我确认后,直接修改并覆盖当前的 README.md 文件
|
||||
@ -1,134 +0,0 @@
|
||||
# 变更统计接口优化设计
|
||||
|
||||
## 1. 性能优化 - 索引建议
|
||||
|
||||
### 1.1 当前查询分析
|
||||
|
||||
`selectChangeStatistics` 方法使用以下字段进行过滤:
|
||||
|
||||
- `change_type` - 变更类型
|
||||
- `operation_type` - 操作类型
|
||||
- `object_api` - 对象 API 名称
|
||||
- `sync_status` - 同步状态
|
||||
- `is_custom` - 是否自定义
|
||||
- `change_time` - 变更时间(用于时间范围过滤)
|
||||
|
||||
### 1.2 索引建议
|
||||
|
||||
建议为以下字段添加索引:
|
||||
|
||||
1. **单列索引**:
|
||||
- `change_time` - 用于时间范围查询,这是最频繁的过滤条件之一
|
||||
- `sync_status` - 用于同步状态过滤
|
||||
- `change_type` - 用于变更类型过滤
|
||||
- `operation_type` - 用于操作类型过滤
|
||||
|
||||
2. **复合索引**:
|
||||
- `(change_type, operation_type)` - 经常一起使用的过滤条件
|
||||
- `(object_api, change_type)` - 用于特定对象的变更查询
|
||||
- `(change_time, sync_status)` - 用于时间范围加同步状态的组合查询
|
||||
|
||||
### 1.3 索引创建语句
|
||||
|
||||
```sql
|
||||
-- 单列索引
|
||||
CREATE INDEX idx_metadata_change_time ON datai_integration_metadata_change(change_time);
|
||||
CREATE INDEX idx_metadata_sync_status ON datai_integration_metadata_change(sync_status);
|
||||
CREATE INDEX idx_metadata_change_type ON datai_integration_metadata_change(change_type);
|
||||
CREATE INDEX idx_metadata_operation_type ON datai_integration_metadata_change(operation_type);
|
||||
|
||||
-- 复合索引
|
||||
CREATE INDEX idx_metadata_change_operation ON datai_integration_metadata_change(change_type, operation_type);
|
||||
CREATE INDEX idx_metadata_object_change ON datai_integration_metadata_change(object_api, change_type);
|
||||
CREATE INDEX idx_metadata_time_status ON datai_integration_metadata_change(change_time, sync_status);
|
||||
```
|
||||
|
||||
## 2. 功能增强
|
||||
|
||||
### 2.1 缓存机制
|
||||
|
||||
为统计结果添加缓存,减少频繁查询的开销:
|
||||
|
||||
- 使用 Redis 缓存统计结果
|
||||
- 缓存键包含查询参数的哈希值
|
||||
- 设置合理的过期时间(如 5 分钟)
|
||||
- 提供缓存刷新接口
|
||||
|
||||
### 2.2 分组统计
|
||||
|
||||
支持按不同维度分组统计:
|
||||
|
||||
- 按变更类型分组
|
||||
- 按操作类型分组
|
||||
- 按对象 API 分组
|
||||
- 按同步状态分组
|
||||
|
||||
### 2.3 趋势分析
|
||||
|
||||
支持按时间维度分析变更趋势:
|
||||
|
||||
- 按日统计
|
||||
- 按周统计
|
||||
- 按月统计
|
||||
- 按季度统计
|
||||
|
||||
## 3. 代码优化
|
||||
|
||||
### 3.1 参数验证
|
||||
|
||||
增强参数验证,确保:
|
||||
|
||||
- 时间格式正确
|
||||
- 参数值有效
|
||||
- 防止 SQL 注入
|
||||
|
||||
### 3.2 异常处理
|
||||
|
||||
添加详细的异常处理和日志记录:
|
||||
|
||||
- 捕获并记录 SQL 异常
|
||||
- 记录查询参数和执行时间
|
||||
- 提供友好的错误信息
|
||||
|
||||
### 3.3 代码结构
|
||||
|
||||
提取统计逻辑为单独的方法,提高代码复用性:
|
||||
|
||||
- 分离查询构建和结果处理
|
||||
- 封装统计计算逻辑
|
||||
- 统一响应格式处理
|
||||
|
||||
## 4. 响应格式优化
|
||||
|
||||
确保响应格式满足前端展示需求:
|
||||
|
||||
- 标准化错误响应
|
||||
- 统一数据结构
|
||||
- 提供详细的统计项说明
|
||||
|
||||
## 5. 测试策略
|
||||
|
||||
验证优化效果的测试策略:
|
||||
|
||||
- 性能测试:测试不同数据量下的查询性能
|
||||
- 功能测试:验证所有统计维度和过滤条件
|
||||
- 边界测试:测试极端情况下的行为
|
||||
- 回归测试:确保现有功能不受影响
|
||||
|
||||
## 6. 实施计划
|
||||
|
||||
1. 添加索引优化 SQL 查询性能
|
||||
2. 实现缓存机制减少查询开销
|
||||
3. 添加分组统计和趋势分析功能
|
||||
4. 增强参数验证和异常处理
|
||||
5. 优化代码结构和响应格式
|
||||
6. 运行测试验证优化效果
|
||||
7. 部署到生产环境
|
||||
|
||||
## 7. 预期效果
|
||||
|
||||
- 查询性能提升 50% 以上
|
||||
- 响应时间减少到 500ms 以内
|
||||
- 支持更大数据量的统计查询
|
||||
- 功能更全面,满足更多业务场景
|
||||
- 代码更健壮,易于维护和扩展
|
||||
Loading…
Reference in New Issue
Block a user