将用户同步功能重构为**数据查询**和**数据同步**两个独立的服务,与部门同步保持一致的架构风格,实现职责分离和灵活性提升。
┌─────────────────────────────────────────┐
│ UserSyncServiceImpl │
│ │
│ ┌─────────────────────────────────┐ │
│ │ syncOaUsers() │ │
│ │ ├─ 查询 SQL Server │ │
│ │ ├─ 查询 MySQL 部门 │ │
│ │ └─ 写入 MySQL 用户 │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
问题:
- ❌ 数据查询和同步逻辑耦合
- ❌ 无法使用外部数据源
- ❌ 难以扩展其他数据来源
┌──────────────────────────────────────────────────────────┐
│ UserSyncDataServiceImpl (新增) │
│ 职责:从 SQL Server 查询用户数据 │
│ │
│ ├─ getOaUsers() │
│ └─ 返回 List<UserSyncDTO> │
└──────────────────────────────────────────────────────────┘
│
v
┌──────────────────────────────────────────────────────────┐
│ UserSyncServiceImpl (重构) │
│ 职责:接收数据并写入 MySQL │
│ │
│ ├─ syncOaUsers() │
│ │ └─ 调用 UserSyncDataService 获取数据 │
│ │ │
│ └─ syncOaUsers(List<UserSyncDTO>) │
│ └─ 接收外部传入的数据进行同步 │
└──────────────────────────────────────────────────────────┘
文件路径: ruoyi-system/src/main/java/com/ruoyi/system/service/IUserSyncDataService.java
职责: 定义从 SQL Server 查询用户数据的接口
public interface IUserSyncDataService {
/**
* 从 SQL Server 查询 OA 用户数据
*
* @return OA 用户列表
*/
List<UserSyncDTO> getOaUsers();
}
文件路径: ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncDataServiceImpl.java
职责: 专门负责从 SQL Server 查询用户数据
关键特性:
```java
@Service
public class UserSyncDataServiceImpl implements IUserSyncDataService {
@Autowired
private UserSyncMapper userSyncMapper; // 带 @DataSource 注解
@Override
public List<UserSyncDTO> getOaUsers() {
log.info("开始从 SQL Server 查询 OA 用户数据...");
// 自动切换到 SQL Server 数据源
List<UserSyncDTO> oaUsers = userSyncMapper.selectOaUsers();
log.info("从 SQL Server 查询到 {} 条 OA 用户数据", oaUsers.size());
return oaUsers;
}
}
```
数据流: UserSyncDataServiceImpl ↓ UserSyncMapper (@DataSource(SQLSERVER)) ↓ SQL Server 数据库 (OA_User) ↓ 返回 List<UserSyncDTO>
文件路径: ruoyi-system/src/main/java/com/ruoyi/system/service/IUserSyncService.java
变化: 新增重载方法,支持外部数据源
public interface IUserSyncService {
/**
* 同步OA用户数据(从 SQL Server 查询并同步到 MySQL)
*/
AjaxResult syncOaUsers();
/**
* 同步OA用户数据(使用外部传入的数据源)
*
* @param oaUsers 外部传入的OA用户数据列表
*/
AjaxResult syncOaUsers(List<UserSyncDTO> oaUsers);
}
文件路径: ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java
变化:
1. 移除对 UserSyncMapper 的直接依赖
2. 注入 IUserSyncDataService 获取数据
3. 新增 syncOaUsers(List<UserSyncDTO>) 方法
重构后的代码:
@Service
public class UserSyncServiceImpl implements IUserSyncService {
@Autowired
private IUserSyncDataService userSyncDataService; // 数据查询服务
@Autowired
private SysUserMapper sysUserMapper; // MySQL 用户操作
@Autowired
private SysDeptMapper sysDeptMapper; // MySQL 部门查询
// 方法 1: 内部查询 + 同步
@Override
@Transactional
public AjaxResult syncOaUsers() {
// 调用数据查询服务获取 SQL Server 数据
List<UserSyncDTO> oaUsers = userSyncDataService.getOaUsers();
if (oaUsers == null || oaUsers.isEmpty()) {
return AjaxResult.warn("未获取到需要同步的用户数据");
}
// 调用重载方法执行同步
return syncOaUsers(oaUsers);
}
// 方法 2: 外部数据源 + 同步(新增)
@Override
@Transactional
public AjaxResult syncOaUsers(List<UserSyncDTO> oaUsers) {
if (oaUsers == null || oaUsers.isEmpty()) {
return AjaxResult.warn("传入的用户数据为空");
}
log.info("开始同步 {} 条OA用户数据到 MySQL 数据库...", oaUsers.size());
// 同步逻辑(只涉及 MySQL 操作)
// 1. 查询 MySQL 部门信息
// 2. 创建或更新用户
// ...
}
}
文件路径: ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/DepartmentSyncController.java
变化: 新增用户同步的外部数据源接口
@RestController
@RequestMapping("/system/dept/sync")
public class DepartmentSyncController {
@Autowired
private IDepartmentSyncService departmentSyncService;
@Autowired
private IUserSyncService userSyncService;
/**
* 接口 1: 同步部门数据(从 SQL Server 查询并同步)
*/
@PostMapping("/branch")
public AjaxResult syncBranchDepartments() {
return departmentSyncService.syncBranchDepartments();
}
/**
* 接口 2: 同步部门数据(使用外部传入的数据源)
*/
@PostMapping("/branch/data")
public AjaxResult syncBranchDepartmentsWithData(@RequestBody List<DepartmentSyncDTO> branchDepts) {
return departmentSyncService.syncBranchDepartments(branchDepts);
}
/**
* 接口 3: 同步用户数据(从 SQL Server 查询并同步)
*/
@PostMapping("/user")
public AjaxResult syncOaUsers() {
return userSyncService.syncOaUsers();
}
/**
* 接口 4: 同步用户数据(使用外部传入的数据源)⭐ 新增
*/
@PostMapping("/user/data")
public AjaxResult syncOaUsersWithData(@RequestBody List<UserSyncDTO> oaUsers) {
return userSyncService.syncOaUsers(oaUsers);
}
}
# 同步用户数据
POST http://localhost:8080/system/dept/sync/user
执行流程:
1. Controller 调用 userSyncService.syncOaUsers()
2. Service 调用 userSyncDataService.getOaUsers()
3. 自动切换到 SQL Server 查询数据
4. 将数据写入 MySQL
POST http://localhost:8080/system/dept/sync/user/data
Content-Type: application/json
[
{
"oaUserId": 1001,
"userName": "zhangsan",
"nickName": "张三",
"departmentId": 2001,
"sex": "0",
"email": "zhangsan@example.com",
"phonenumber": "13800138000"
},
{
"oaUserId": 1002,
"userName": "lisi",
"nickName": "李四",
"departmentId": 2002,
"sex": "1",
"email": "lisi@example.com",
"phonenumber": "13900139000"
}
]
执行流程:
1. Controller 接收 JSON 数据并转换为 List<UserSyncDTO>
2. Controller 调用 userSyncService.syncOaUsers(oaUsers)
3. Service 查询 MySQL 部门信息
4. Service 创建或更新用户到 MySQL
@Service
public class BatchSyncService {
@Autowired
private IDepartmentSyncDataService departmentSyncDataService;
@Autowired
private IUserSyncDataService userSyncDataService;
@Autowired
private IDepartmentSyncService departmentSyncService;
@Autowired
private IUserSyncService userSyncService;
public void batchSyncAll() {
// 1. 从 SQL Server 查询部门数据
List<DepartmentSyncDTO> deptData = departmentSyncDataService.getBranchDepartments();
// 2. 同步部门到 MySQL
departmentSyncService.syncBranchDepartments(deptData);
// 3. 从 SQL Server 查询用户数据
List<UserSyncDTO> userData = userSyncDataService.getOaUsers();
// 4. 同步用户到 MySQL(确保部门已同步)
userSyncService.syncOaUsers(userData);
}
}
@Service
public class PreprocessSyncService {
@Autowired
private IUserSyncDataService userSyncDataService;
@Autowired
private IUserSyncService userSyncService;
public void syncWithPreprocess() {
// 1. 从 SQL Server 查询数据
List<UserSyncDTO> rawData = userSyncDataService.getOaUsers();
// 2. 数据预处理
List<UserSyncDTO> processedData = rawData.stream()
.filter(user -> StringUtils.isNotEmpty(user.getEmail())) // 只同步有邮箱的用户
.peek(user -> {
// 统一邮箱域名
if (!user.getEmail().contains("@company.com")) {
user.setEmail(user.getUserName() + "@company.com");
}
})
.collect(Collectors.toList());
// 3. 同步处理后的数据
userSyncService.syncOaUsers(processedData);
}
}
| 接口 | 路径 | 数据来源 | 适用场景 |
|---|---|---|---|
| 部门同步 1 | POST /system/dept/sync/branch |
内部查询 SQL Server | 常规定时同步 |
| 部门同步 2 | POST /system/dept/sync/branch/data |
外部传入 JSON | 手动数据导入、数据预处理 |
| 用户同步 1 | POST /system/dept/sync/user |
内部查询 SQL Server | 常规定时同步 |
| 用户同步 2 | POST /system/dept/sync/user/data |
外部传入 JSON | 手动数据导入、数据预处理 ⭐ |
定时任务可以继续使用原有方式,无需修改:
@Component("oaSyncTask")
public class OaSyncTask {
@Autowired
private IDepartmentSyncService departmentSyncService;
@Autowired
private IUserSyncService userSyncService;
public void syncOaData() {
// 第一步:同步部门
AjaxResult deptResult = departmentSyncService.syncBranchDepartments();
if (deptResult.get("code").equals(200)) {
// 第二步:同步用户(只有部门同步成功才执行)
AjaxResult userResult = userSyncService.syncOaUsers();
log.info("用户同步结果: {}", userResult.get("msg"));
}
}
}
IUserSyncDataService.java - 用户数据查询服务接口UserSyncDataServiceImpl.java - 用户数据查询服务实现用户同步服务重构说明.md - 本文档IUserSyncService.java - 新增重载方法UserSyncServiceImpl.java - 重构实现DepartmentSyncController.java - 新增 /user/data 接口UserSyncMapper.java - Mapper 接口不变UserSyncMapper.xml - SQL 映射不变OaSyncTask.java - 定时任务不变// 扩展:支持其他数据源
public interface IUserSyncDataService {
List<UserSyncDTO> getOaUsers(); // SQL Server
List<UserSyncDTO> getOaUsersFromLDAP(); // LDAP
List<UserSyncDTO> getOaUsersFromAPI(); // HTTP API
}
@Test
public void testSyncWithMockData() {
// 创建测试数据
List<UserSyncDTO> testData = createTestUserData();
// 测试同步逻辑
AjaxResult result = userSyncService.syncOaUsers(testData);
// 验证结果
assertEquals(200, result.get("code"));
}
通过本次重构,用户同步功能与部门同步功能保持了一致的架构风格:
✅ 职责分离: 数据查询和同步逻辑解耦
✅ 灵活性提升: 支持多种数据来源
✅ 易于扩展: 可轻松添加新的数据源
✅ 向后兼容: 原有代码无需修改
✅ 易于测试: 可使用 Mock 数据进行单元测试
✅ 架构一致: 与部门同步保持相同模式