# 用户同步服务重构说明 ## 重构概述 将用户同步功能重构为**数据查询**和**数据同步**两个独立的服务,与部门同步保持一致的架构风格,实现职责分离和灵活性提升。 --- ## 架构变化 ### 重构前 ``` ┌─────────────────────────────────────────┐ │ UserSyncServiceImpl │ │ │ │ ┌─────────────────────────────────┐ │ │ │ syncOaUsers() │ │ │ │ ├─ 查询 SQL Server │ │ │ │ ├─ 查询 MySQL 部门 │ │ │ │ └─ 写入 MySQL 用户 │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘ ``` **问题**: - ❌ 数据查询和同步逻辑耦合 - ❌ 无法使用外部数据源 - ❌ 难以扩展其他数据来源 ### 重构后 ``` ┌──────────────────────────────────────────────────────────┐ │ UserSyncDataServiceImpl (新增) │ │ 职责:从 SQL Server 查询用户数据 │ │ │ │ ├─ getOaUsers() │ │ └─ 返回 List │ └──────────────────────────────────────────────────────────┘ │ v ┌──────────────────────────────────────────────────────────┐ │ UserSyncServiceImpl (重构) │ │ 职责:接收数据并写入 MySQL │ │ │ │ ├─ syncOaUsers() │ │ │ └─ 调用 UserSyncDataService 获取数据 │ │ │ │ │ └─ syncOaUsers(List) │ │ └─ 接收外部传入的数据进行同步 │ └──────────────────────────────────────────────────────────┘ ``` --- ## 核心组件 ### 1. IUserSyncDataService(新增) **文件路径**: `ruoyi-system/src/main/java/com/ruoyi/system/service/IUserSyncDataService.java` **职责**: 定义从 SQL Server 查询用户数据的接口 ```java public interface IUserSyncDataService { /** * 从 SQL Server 查询 OA 用户数据 * * @return OA 用户列表 */ List getOaUsers(); } ``` --- ### 2. UserSyncDataServiceImpl(新增) **文件路径**: `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 getOaUsers() { log.info("开始从 SQL Server 查询 OA 用户数据..."); // 自动切换到 SQL Server 数据源 List oaUsers = userSyncMapper.selectOaUsers(); log.info("从 SQL Server 查询到 {} 条 OA 用户数据", oaUsers.size()); return oaUsers; } } ``` **数据流**: ``` UserSyncDataServiceImpl ↓ UserSyncMapper (@DataSource(SQLSERVER)) ↓ SQL Server 数据库 (OA_User) ↓ 返回 List ``` --- ### 3. IUserSyncService(重构) **文件路径**: `ruoyi-system/src/main/java/com/ruoyi/system/service/IUserSyncService.java` **变化**: 新增重载方法,支持外部数据源 ```java public interface IUserSyncService { /** * 同步OA用户数据(从 SQL Server 查询并同步到 MySQL) */ AjaxResult syncOaUsers(); /** * 同步OA用户数据(使用外部传入的数据源) * * @param oaUsers 外部传入的OA用户数据列表 */ AjaxResult syncOaUsers(List oaUsers); } ``` --- ### 4. UserSyncServiceImpl(重构) **文件路径**: `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java` **变化**: 1. 移除对 `UserSyncMapper` 的直接依赖 2. 注入 `IUserSyncDataService` 获取数据 3. 新增 `syncOaUsers(List)` 方法 **重构后的代码**: ```java @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 oaUsers = userSyncDataService.getOaUsers(); if (oaUsers == null || oaUsers.isEmpty()) { return AjaxResult.warn("未获取到需要同步的用户数据"); } // 调用重载方法执行同步 return syncOaUsers(oaUsers); } // 方法 2: 外部数据源 + 同步(新增) @Override @Transactional public AjaxResult syncOaUsers(List oaUsers) { if (oaUsers == null || oaUsers.isEmpty()) { return AjaxResult.warn("传入的用户数据为空"); } log.info("开始同步 {} 条OA用户数据到 MySQL 数据库...", oaUsers.size()); // 同步逻辑(只涉及 MySQL 操作) // 1. 查询 MySQL 部门信息 // 2. 创建或更新用户 // ... } } ``` --- ### 5. DepartmentSyncController(更新) **文件路径**: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/DepartmentSyncController.java` **变化**: 新增用户同步的外部数据源接口 ```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 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 oaUsers) { return userSyncService.syncOaUsers(oaUsers); } } ``` --- ## 使用场景 ### 场景 1: 常规同步(从 SQL Server 查询) ```bash # 同步用户数据 POST http://localhost:8080/system/dept/sync/user ``` **执行流程**: 1. Controller 调用 `userSyncService.syncOaUsers()` 2. Service 调用 `userSyncDataService.getOaUsers()` 3. 自动切换到 SQL Server 查询数据 4. 将数据写入 MySQL --- ### 场景 2: 使用外部数据源 ```bash 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` 2. Controller 调用 `userSyncService.syncOaUsers(oaUsers)` 3. Service 查询 MySQL 部门信息 4. Service 创建或更新用户到 MySQL --- ### 场景 3: 批量同步部门和用户 ```java @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 deptData = departmentSyncDataService.getBranchDepartments(); // 2. 同步部门到 MySQL departmentSyncService.syncBranchDepartments(deptData); // 3. 从 SQL Server 查询用户数据 List userData = userSyncDataService.getOaUsers(); // 4. 同步用户到 MySQL(确保部门已同步) userSyncService.syncOaUsers(userData); } } ``` --- ### 场景 4: 数据预处理后同步 ```java @Service public class PreprocessSyncService { @Autowired private IUserSyncDataService userSyncDataService; @Autowired private IUserSyncService userSyncService; public void syncWithPreprocess() { // 1. 从 SQL Server 查询数据 List rawData = userSyncDataService.getOaUsers(); // 2. 数据预处理 List 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 | 手动数据导入、数据预处理 ⭐ | --- ## 定时任务更新 定时任务可以继续使用原有方式,无需修改: ```java @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` - 定时任务不变 --- ## 优势总结 ### 1. 与部门同步保持一致 ✅ - 相同的架构风格 - 相同的职责分离模式 - 相同的扩展方式 ### 2. 职责分离 ✅ - **UserSyncDataService**: 只负责查询数据 - **UserSyncService**: 只负责同步数据 ### 3. 灵活性提升 ✅ - 支持从 SQL Server 查询 - 支持外部数据源 - 支持数据预处理 - 支持多源数据整合 ### 4. 易于扩展 ✅ ```java // 扩展:支持其他数据源 public interface IUserSyncDataService { List getOaUsers(); // SQL Server List getOaUsersFromLDAP(); // LDAP List getOaUsersFromAPI(); // HTTP API } ``` ### 5. 易于测试 ✅ ```java @Test public void testSyncWithMockData() { // 创建测试数据 List testData = createTestUserData(); // 测试同步逻辑 AjaxResult result = userSyncService.syncOaUsers(testData); // 验证结果 assertEquals(200, result.get("code")); } ``` --- ## 总结 通过本次重构,用户同步功能与部门同步功能保持了一致的架构风格: ✅ **职责分离**: 数据查询和同步逻辑解耦 ✅ **灵活性提升**: 支持多种数据来源 ✅ **易于扩展**: 可轻松添加新的数据源 ✅ **向后兼容**: 原有代码无需修改 ✅ **易于测试**: 可使用 Mock 数据进行单元测试 ✅ **架构一致**: 与部门同步保持相同模式 --- ## 相关文档 - [部门同步服务重构说明.md](部门同步服务重构说明.md) - [OA数据同步多数据源架构说明.md](OA数据同步多数据源架构说明.md) - [多数据源切换问题修复说明.md](多数据源切换问题修复说明.md) - [SQL Server数据源查询优化说明.md](SQL Server数据源查询优化说明.md)