Jelajahi Sumber

1、同步员工用户业务逻辑调整
2、增加补全业务逻辑机制
3、调整openid的获取

yys 2 bulan lalu
induk
melakukan
5fb5c56dd8

+ 40 - 2
fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java

@@ -17,6 +17,7 @@ import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.user.UserPasswordNotMatchException;
 import com.fs.common.exception.user.UserPasswordNotMatchException;
 import com.fs.common.utils.MessageUtils;
 import com.fs.common.utils.MessageUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
+import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.company.mapper.CompanyUserMapper;
@@ -26,6 +27,7 @@ import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.mapper.FastGptRoleMapper;
 import com.fs.fastGpt.mapper.FastGptRoleMapper;
 import com.fs.framework.manager.AsyncManager;
 import com.fs.framework.manager.AsyncManager;
 import com.fs.framework.manager.factory.AsyncFactory;
 import com.fs.framework.manager.factory.AsyncFactory;
+import com.fs.framework.web.service.TokenService;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwCompanyMapper;
 import com.fs.qw.mapper.QwCompanyMapper;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwExternalContactMapper;
@@ -82,7 +84,8 @@ public class QwUserController extends BaseController {
 
 
     @Autowired
     @Autowired
     private IQwDeptService qwDeptService;
     private IQwDeptService qwDeptService;
-
+    @Autowired
+    private TokenService tokenService;
     @Autowired
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
     private QwExternalContactMapper qwExternalContactMapper;
 
 
@@ -649,7 +652,8 @@ public class QwUserController extends BaseController {
     @PostMapping("sync/{corpId}")
     @PostMapping("sync/{corpId}")
     public R sync(@PathVariable String corpId)
     public R sync(@PathVariable String corpId)
     {
     {
-        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(getLoginUser().getUser().getCompanyId());
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(loginUser.getUser().getCompanyId());
         Long tenantId = SecurityUtils.getTenantId();
         Long tenantId = SecurityUtils.getTenantId();
         for (String string : strings) {
         for (String string : strings) {
             if (string.equals(corpId)){
             if (string.equals(corpId)){
@@ -693,6 +697,40 @@ public class QwUserController extends BaseController {
         }
         }
         return R.ok();
         return R.ok();
     }
     }
+    /**
+     * 同步企微用户
+     */
+    @RepeatSubmit
+    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
+    @Log(title = "企微用户", businessType = BusinessType.INSERT)
+    @PostMapping("/syncUser/{corpId}")
+    public R syncUser(@PathVariable String corpId)
+    {
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(getLoginUser().getUser().getCompanyId());
+        for (String string : strings) {
+            if (string.equals(corpId)){
+                // 远程调用 fs-qw-api 同步企微用户
+                String syncUserUrl = OpenQwConfig.taskApi + "/app/common/syncQwUserAsync?corpId=" + corpId;
+                try {
+                    HttpResponse response = HttpRequest.post(syncUserUrl)
+                            .timeout(apiTimeout * 1000)
+                            .execute();
+                    if (response.getStatus() != 200) {
+                        log.error("同步企微用户失败,HTTP状态码: {}", response.getStatus());
+                        return R.error("同步企微用户失败,服务返回状态码: " + response.getStatus());
+                    }
+                } catch (Exception e) {
+                    log.error("同步企微用户异常, url={}", syncUserUrl, e);
+                    if (e.getCause() instanceof SocketTimeoutException) {
+                        return R.error("同步企微用户超时,请稍后重试");
+                    }
+                    return R.error("同步企微用户失败: " + e.getMessage());
+                }
+
+            }
+        }
+        return R.ok();
+    }
     @RepeatSubmit
     @RepeatSubmit
     @PreAuthorize("@ss.hasPermi('qw:user:sync')")
     @PreAuthorize("@ss.hasPermi('qw:user:sync')")
     @Log(title = "同步企微用户名称", businessType = BusinessType.INSERT)
     @Log(title = "同步企微用户名称", businessType = BusinessType.INSERT)

+ 35 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java

@@ -935,4 +935,39 @@ public class QwUserController extends BaseController
         List<QwUser> list = qwUserService.selectQwUserList(qwUser);
         List<QwUser> list = qwUserService.selectQwUserList(qwUser);
         return getDataTable(list);
         return getDataTable(list);
     }
     }
+
+    /**
+     * 同步企微用户
+     */
+    @RepeatSubmit
+    @Log(title = "企微用户", businessType = BusinessType.INSERT)
+    @PostMapping("/syncUser/{corpId}")
+    public R syncUser(@PathVariable String corpId)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(loginUser.getCompany().getCompanyId());
+        for (String string : strings) {
+            if (string.equals(corpId)){
+                // 远程调用 fs-qw-api 同步企微用户
+                String syncUserUrl = OpenQwConfig.taskApi + "/app/common/syncQwUserAsync?corpId=" + corpId;
+                try {
+                    HttpResponse response = HttpRequest.post(syncUserUrl)
+                            .timeout(apiTimeout * 1000)
+                            .execute();
+                    if (response.getStatus() != 200) {
+                        log.error("同步企微用户失败,HTTP状态码: {}", response.getStatus());
+                        return R.error("同步企微用户失败,服务返回状态码: " + response.getStatus());
+                    }
+                } catch (Exception e) {
+                    log.error("同步企微用户异常, url={}", syncUserUrl, e);
+                    if (e.getCause() instanceof SocketTimeoutException) {
+                        return R.error("同步企微用户超时,请稍后重试");
+                    }
+                    return R.error("同步企微用户失败: " + e.getMessage());
+                }
+
+            }
+        }
+        return R.ok();
+    }
 }
 }

+ 146 - 74
fs-qw-api/src/main/java/com/fs/app/service/impl/OpenQwApiServiceImpl.java

@@ -127,97 +127,155 @@ public class OpenQwApiServiceImpl implements OpenQwApiService {
      */
      */
     private void executeSync(Long tenantId, String corpId) {
     private void executeSync(Long tenantId, String corpId) {
         tenantDataSourceUtil.executeWithResult(tenantId, () -> {
         tenantDataSourceUtil.executeWithResult(tenantId, () -> {
+            long startTime = System.currentTimeMillis();
+            log.info("========== 开始同步用户数据 ==========");
+            log.info("租户ID: {}, 企业ID: {}", tenantId, corpId);
+
             try {
             try {
-                // 1. 获取部门列表
-                QwDeptResult userList = qwApiService.getDepartmentList(corpId);
-                List<Department> deptUser = userList.getDepartment();
-                log.info("同步部门数量:{}", deptUser.size());
+                // ========== 1. 获取部门列表 ==========
+                log.info("步骤1: 获取部门列表");
+                QwDeptResult deptResult = qwApiService.getDepartmentList(corpId);
+                List<Department> departmentList = deptResult.getDepartment();
+
+                if (departmentList == null || departmentList.isEmpty()) {
+                    log.warn("未获取到任何部门,同步结束");
+                    return R.ok("未获取到部门列表");
+                }
+                log.info("获取到部门数量: {}", departmentList.size());
 
 
-                // 2. 获取企业信息和token
+                // ========== 2. 获取企业信息和access_token ==========
+                log.info("步骤2: 获取企业信息和access_token");
                 QwCompany qwCompany = iQwCompanyService.selectQwCompanyByCorpId(corpId);
                 QwCompany qwCompany = iQwCompanyService.selectQwCompanyByCorpId(corpId);
+                if (qwCompany == null) {
+                    log.error("未找到企业信息, corpId: {}", corpId);
+                    return R.error("未找到企业信息");
+                }
                 String accessToken = qwApiService.getToken(corpId, qwCompany.getPermanentCode());
                 String accessToken = qwApiService.getToken(corpId, qwCompany.getPermanentCode());
+                log.info("获取access_token成功");
+
+                // ========== 3. 批量获取所有部门的用户(去重) ==========
+                log.info("步骤3: 遍历所有部门获取用户列表");
+                Map<String, DeptUserResult> userMap = new ConcurrentHashMap<>();
+                int totalDeptUsers = 0;
+                int emptyDeptCount = 0;
+
+                for (Department department : departmentList) {
+                    try {
+                        log.debug("正在获取部门 [{}] {} 的用户列表", department.getId(), department.getName());
+                        UserResult userResult = qwApiService.getUserSimpleList(corpId, accessToken, department.getId());
+                        List<DeptUserResult> deptUsers = userResult.getUserlist();
+
+                        if (deptUsers == null || deptUsers.isEmpty()) {
+                            log.debug("部门 [{}] {} 没有用户", department.getId(), department.getName());
+                            emptyDeptCount++;
+                            continue;
+                        }
+
+                        // 使用putIfAbsent实现去重(保留第一次出现的用户信息)
+                        for (DeptUserResult user : deptUsers) {
+                            userMap.putIfAbsent(user.getUserid(), user);
+                        }
+                        totalDeptUsers += deptUsers.size();
+
+                    } catch (Exception e) {
+                        log.error("获取部门 [{}] {} 的用户列表失败", department.getId(), department.getName(), e);
+                    }
+                }
+
+                log.info("部门统计: 总部门数={}, 空部门数={}, 原始用户总数={}, 去重后用户数={}",
+                        departmentList.size(), emptyDeptCount, totalDeptUsers, userMap.size());
 
 
-                // 3. 批量获取所有部门用户
-                Map<String, DeptUserResult> userMap = deptUser.parallelStream()
-                        .flatMap(department -> {
-                            try {
-                                log.info("正在获取部门 {} 的用户列表", department.getId());
-                                UserResult userResult = qwApiService.getUserSimpleList(corpId, accessToken, department.getId());
-                                List<DeptUserResult> deptUserResults = userResult.getUserlist();
-                                if (deptUserResults == null || deptUserResults.isEmpty()) {
-                                    log.warn("部门 {} 没有用户", department.getId());
-                                    return Stream.empty();
-                                }
-                                return deptUserResults.stream();
-                            } catch (Exception e) {
-                                log.error("获取部门 {} 用户失败", department.getId(), e);
-                                return Stream.empty();
-                            }
-                        })
-                        .collect(Collectors.toMap(
-                                DeptUserResult::getUserid,
-                                Function.identity(),
-                                (existing, replacement) -> existing
-                        ));
-
-                log.info("去重后用户总数:{}", userMap.size());
                 if (userMap.isEmpty()) {
                 if (userMap.isEmpty()) {
                     log.info("无用户需要同步");
                     log.info("无用户需要同步");
                     return R.ok("无用户需要同步");
                     return R.ok("无用户需要同步");
                 }
                 }
 
 
-                // 4. 批量查询现有用户
+                // ========== 4. 查询数据库中已存在的用户 ==========
+                log.info("步骤4: 查询数据库中已存在的用户");
                 List<String> userIds = new ArrayList<>(userMap.keySet());
                 List<String> userIds = new ArrayList<>(userMap.keySet());
                 List<QwUser> existingUsers = qwUserMapper.selectQwUsersByCorpIdAndUserIds(corpId, userIds);
                 List<QwUser> existingUsers = qwUserMapper.selectQwUsersByCorpIdAndUserIds(corpId, userIds);
                 Map<String, QwUser> existingUserMap = existingUsers.stream()
                 Map<String, QwUser> existingUserMap = existingUsers.stream()
                         .collect(Collectors.toMap(QwUser::getQwOpenUserId, Function.identity()));
                         .collect(Collectors.toMap(QwUser::getQwOpenUserId, Function.identity()));
 
 
-                // 5. 批量获取用户详细信息
+                log.info("数据库已存在用户数: {}, 新增用户数: {}",
+                        existingUserMap.size(), userIds.size() - existingUserMap.size());
+
+                // ========== 5. 批量获取用户详细信息并构建处理对象 ==========
+                log.info("步骤5: 批量处理用户详情(每批100个)");
                 List<QwUser> usersToProcess = new ArrayList<>();
                 List<QwUser> usersToProcess = new ArrayList<>();
                 List<List<String>> batches = Lists.partition(userIds, 100);
                 List<List<String>> batches = Lists.partition(userIds, 100);
 
 
-                for (List<String> batch : batches) {
-                    List<QwUser> batchUsers = batch.parallelStream()
-                            .map(userId -> {
-                                try {
-                                    DeptUserResult user = userMap.get(userId);
-                                    QwUser existingQwUser = existingUserMap.get(userId);
+                int successCount = 0;
+                int errorCount = 0;
+
+                for (int batchIndex = 0; batchIndex < batches.size(); batchIndex++) {
+                    List<String> batch = batches.get(batchIndex);
+                    log.info("处理第 {}/{} 批,本批用户数: {}", batchIndex + 1, batches.size(), batch.size());
+
+                    List<QwUser> batchUsers = new ArrayList<>();
+                    for (String userId : batch) {
+                        try {
+                            DeptUserResult apiUser = userMap.get(userId);
+                            if (apiUser == null) {
+                                log.warn("用户 {} 在API结果中不存在,跳过", userId);
+                                errorCount++;
+                                continue;
+                            }
 
 
-                                    // 判断是否需要更新
-                                    if (existingQwUser != null && !needUpdateUserInfo(existingQwUser)) {
-                                        return null;
-                                    }
+                            QwUser existingQwUser = existingUserMap.get(userId);
+                            boolean isNewUser = (existingQwUser == null);
 
 
-                                    // 转换openid
-                                    QwOpenidByUserParams param = new QwOpenidByUserParams();
-                                    param.setUserid(userId);
-                                    QwOpenidResult qwOpenidResult = qwApiService.useridToOpenid(param, corpId);
-
-                                    QwUser qwUser = new QwUser();
-                                    qwUser.setDepartment(user.getDepartment().toString());
-                                    qwUser.setQwUserName(user.getName());
-                                    qwUser.setCorpId(corpId);
-                                    qwUser.setOpenid(qwOpenidResult.getOpenid());
-                                    qwUser.setQwOpenUserId(userId);
-
-                                    if (existingQwUser != null) {
-                                        qwUser.setId(existingQwUser.getId());
-                                        qwUser.setIsDel(0);
-                                    }
+                            // 调用API转换openid
+                            QwOpenidByUserParams params = new QwOpenidByUserParams();
+                            params.setUserid(userId);
+                            QwOpenidResult openidResult = qwApiService.useridToOpenid(params, corpId);
 
 
-                                    return qwUser;
-                                } catch (Exception e) {
-                                    log.error("处理用户失败,userId: {}", userId, e);
-                                    return null;
-                                }
-                            })
-                            .filter(Objects::nonNull)
-                            .collect(Collectors.toList());
+                            // 构建QwUser对象
+                            QwUser qwUser = new QwUser();
+
+                            // 设置部门(取第一个部门)
+                            List<Integer> departmentList_ = apiUser.getDepartment();
+                            if (departmentList_ != null && !departmentList_.isEmpty()) {
+                                qwUser.setDepartment(String.valueOf(departmentList_.get(0)));
+                            } else {
+                                qwUser.setDepartment("");
+                            }
+
+                            qwUser.setQwUserName(apiUser.getName());
+                            qwUser.setCorpId(corpId);
+                            qwUser.setOpenid(openidResult.getOpenid());
+                            qwUser.setQwOpenUserId(userId);
+
+                            // 设置id(存在则设置,不存在则为null)
+                            if (existingQwUser != null) {
+                                qwUser.setId(existingQwUser.getId());
+                            } else {
+                                qwUser.setId(null);
+                            }
+
+                            batchUsers.add(qwUser);
+                            successCount++;
+
+                        } catch (Exception e) {
+                            log.error("处理用户失败,userId: {}", userId, e);
+                            errorCount++;
+                        }
+                    }
 
 
                     usersToProcess.addAll(batchUsers);
                     usersToProcess.addAll(batchUsers);
+                    log.info("第 {} 批处理完成: 成功={}, 失败={}", batchIndex + 1, batchUsers.size(), batch.size() - batchUsers.size());
+                }
+
+                log.info("用户处理统计: 成功={}, 失败={}", successCount, errorCount);
+                log.info("最终待处理用户数: {}", usersToProcess.size());
+
+                if (usersToProcess.isEmpty()) {
+                    log.info("没有需要新增或更新的用户");
+                    return R.ok(String.format("同步完成: 失败%d个", errorCount));
                 }
                 }
 
 
-                // 6. 批量数据库操作
+                // ========== 6. 批量数据库操作 ==========
+                log.info("步骤6: 执行数据库批量操作");
                 List<QwUser> toInsert = usersToProcess.stream()
                 List<QwUser> toInsert = usersToProcess.stream()
                         .filter(u -> u.getId() == null)
                         .filter(u -> u.getId() == null)
                         .collect(Collectors.toList());
                         .collect(Collectors.toList());
@@ -225,21 +283,35 @@ public class OpenQwApiServiceImpl implements OpenQwApiService {
                         .filter(u -> u.getId() != null)
                         .filter(u -> u.getId() != null)
                         .collect(Collectors.toList());
                         .collect(Collectors.toList());
 
 
-                int successCount = 0;
+                log.info("数据库操作: 待新增{}条, 待更新{}条", toInsert.size(), toUpdate.size());
+
+                int insertSuccess = 0;
+                int updateSuccess = 0;
+
                 if (!toInsert.isEmpty()) {
                 if (!toInsert.isEmpty()) {
-                    successCount += qwUserMapper.batchInsertQwUser(toInsert);
-                    log.info("批量新增用户:{}", toInsert.size());
+                    insertSuccess = qwUserMapper.batchUpdateQwUser(toInsert);
+                    log.info("批量新增用户成功: {}条", insertSuccess);
                 }
                 }
+
                 if (!toUpdate.isEmpty()) {
                 if (!toUpdate.isEmpty()) {
-                    successCount += qwUserMapper.batchUpdateQwUser(toUpdate);
-                    log.info("批量更新用户:{}", toUpdate.size());
+                    updateSuccess = qwUserMapper.batchUpdateQwUser(toUpdate);
+                    log.info("批量更新用户成功: {}条", updateSuccess);
                 }
                 }
 
 
-                log.info("同步完成,成功:{},失败:{}", successCount, usersToProcess.size() - successCount);
-                return R.ok(String.format("同步完成,成功%d个,失败%d个", successCount, usersToProcess.size() - successCount));
+                long endTime = System.currentTimeMillis();
+                String resultMsg = String.format(
+                        "同步完成!总耗时: %d ms | 新增: %d/%d | 更新: %d/%d | 失败: %d",
+                        (endTime - startTime), insertSuccess, toInsert.size(),
+                        updateSuccess, toUpdate.size(), errorCount
+                );
+
+                log.info(resultMsg);
+                log.info("========== 用户同步结束 ==========");
+
+                return R.ok(resultMsg);
 
 
             } catch (Exception e) {
             } catch (Exception e) {
-                log.error("同步用户过程异常", e);
+                log.error("同步用户过程发生异常", e);
                 return R.error("同步失败:" + e.getMessage());
                 return R.error("同步失败:" + e.getMessage());
             }
             }
         });
         });

+ 58 - 0
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -2,9 +2,11 @@ package com.fs.app.controller;
 
 
 
 
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.fs.app.task.qwTask;
 import com.fs.app.task.qwTask;
 import com.fs.app.taskService.*;
 import com.fs.app.taskService.*;
+import com.fs.common.config.RedisTenantContext;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCache;
@@ -16,6 +18,7 @@ import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.service.*;
 import com.fs.course.service.*;
 import com.fs.course.vo.FsUserCourseVideoQVO;
 import com.fs.course.vo.FsUserCourseVideoQVO;
+import com.fs.framework.datasource.TenantDataSourceManager;
 import com.fs.his.domain.FsUser;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsInquiryOrderService;
 import com.fs.his.service.IFsInquiryOrderService;
 import com.fs.his.utils.qrcode.QRCodeUtils;
 import com.fs.his.utils.qrcode.QRCodeUtils;
@@ -34,11 +37,14 @@ import com.fs.sop.mapper.SopUserLogsMapper;
 import com.fs.sop.service.*;
 import com.fs.sop.service.*;
 import com.fs.sop.vo.QwSopLogsDoSendListTVO;
 import com.fs.sop.vo.QwSopLogsDoSendListTVO;
 import com.fs.store.service.IFsUserCourseCountService;
 import com.fs.store.service.IFsUserCourseCountService;
+import com.fs.tenant.domain.TenantInfo;
+import com.fs.tenant.service.TenantInfoService;
 import com.fs.wxwork.dto.WxWorkGetQrCodeDTO;
 import com.fs.wxwork.dto.WxWorkGetQrCodeDTO;
 import com.fs.wxwork.service.WxWorkService;
 import com.fs.wxwork.service.WxWorkService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -153,7 +159,59 @@ public class CommonController {
     IQwExternalContactService externalContactService;
     IQwExternalContactService externalContactService;
     @Autowired
     @Autowired
     WxWorkService wxWorkService;
     WxWorkService wxWorkService;
+    @Autowired
+    private IQwUserService qwUserService;
+    @Autowired
+    private IQwDeptService qwDeptService;
+    @Autowired
+    private TenantInfoService tenantInfoService;
+
+    @Autowired
+    private IQwCompanyService qwCompanyService;
+    @Autowired
+    private TenantDataSourceManager tenantDataSourceManager;
+
+    @RequestMapping("/syncQwUserAsync")
+    public void syncQwUserAsync(String corpId) {
+        QwCompany qwCompany = qwCompanyService.selectQwCompanyByCorpId(corpId);
+        if (ObjectUtil.isEmpty(corpId)) {
+            return; // 跳过无效租户,继续下一个
+        }
+
+        TenantInfo tenantInfo = null;
+        try {
+            tenantInfo = tenantInfoService.getById(qwCompany.getTenantId());
+            if (ObjectUtil.isEmpty(tenantInfo)) {
+                log.warn("租户信息不存在,tenantId={}", qwCompany.getTenantId());
+                return;
+            }
 
 
+            // 切换到租户数据源
+            tenantDataSourceManager.switchTenant(tenantInfo);
+            // 切换Redis租户上下文
+            RedisTenantContext.setTenantId(tenantInfo.getId());
+
+            log.info("开始同步企微用户,租户={}, corpId={}", tenantInfo.getId(), qwCompany.getCorpId());
+
+            // 执行同步操作
+            qwUserService.syncQwUser(qwCompany.getCorpId());
+
+            log.info("同步完成,租户={}", tenantInfo.getId());
+
+        } catch (Exception e) {
+            log.error("同步企微员工和部门失败,租户={}, corpId={}",
+                    qwCompany.getTenantId(), qwCompany.getCorpId(), e);
+        } finally {
+            // 清理租户上下文(数据源和Redis)
+            try {
+                tenantDataSourceManager.clear(); // 假设有此方法,请根据实际API调整
+            } catch (Exception ignored) {}
+
+            try {
+                RedisTenantContext.clear(); // 或 RedisTenantContext.removeTenantId()
+            } catch (Exception ignored) {}
+        }
+    }
     /**
     /**
      *
      *
      */
      */

+ 83 - 0
fs-qw-task/src/main/java/com/fs/app/task/QwUserAsyncTask.java

@@ -0,0 +1,83 @@
+package com.fs.app.task;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.fs.common.config.RedisTenantContext;
+import com.fs.framework.datasource.TenantDataSourceManager;
+import com.fs.qw.domain.QwCompany;
+import com.fs.qw.service.IQwCompanyService;
+import com.fs.qw.service.IQwDeptService;
+import com.fs.qw.service.IQwUserService;
+import com.fs.tenant.domain.TenantInfo;
+import com.fs.tenant.service.TenantInfoService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+@Slf4j
+@Component
+public class QwUserAsyncTask {
+
+    @Autowired
+    private IQwUserService qwUserService;
+    @Autowired
+    private IQwDeptService qwDeptService;
+    @Autowired
+    private TenantInfoService tenantInfoService;
+
+    @Autowired
+    private IQwCompanyService qwCompanyService;
+    @Autowired
+    private TenantDataSourceManager tenantDataSourceManager;
+
+    @Scheduled(cron = "0 0 0/3 * * ?")
+    public void syncQwUserAsync() {
+        List<QwCompany> qwCompanies = qwCompanyService.selectQwCompanyList(new QwCompany());
+        if (CollectionUtils.isEmpty(qwCompanies)) {
+            return;
+        }
+
+        for (QwCompany qwCompany : qwCompanies) {
+            if (ObjectUtil.isEmpty(qwCompany.getTenantId())) {
+                continue; // 跳过无效租户,继续下一个
+            }
+
+            TenantInfo tenantInfo = null;
+            try {
+                tenantInfo = tenantInfoService.getById(qwCompany.getTenantId());
+                if (ObjectUtil.isEmpty(tenantInfo)) {
+                    log.warn("租户信息不存在,tenantId={}", qwCompany.getTenantId());
+                    continue;
+                }
+
+                // 切换到租户数据源
+                tenantDataSourceManager.switchTenant(tenantInfo);
+                // 切换Redis租户上下文
+                RedisTenantContext.setTenantId(tenantInfo.getId());
+
+                log.info("开始同步企微用户,租户={}, corpId={}", tenantInfo.getId(), qwCompany.getCorpId());
+
+                // 执行同步操作
+                qwUserService.syncQwUser(qwCompany.getCorpId());
+//                qwDeptService.insertOrUpdateQwDept(qwCompany.getCorpId());
+
+                log.info("同步完成,租户={}", tenantInfo.getId());
+
+            } catch (Exception e) {
+                log.error("同步企微员工和部门失败,租户={}, corpId={}",
+                        qwCompany.getTenantId(), qwCompany.getCorpId(), e);
+            } finally {
+                // 清理租户上下文(数据源和Redis)
+                try {
+                    tenantDataSourceManager.clear(); // 假设有此方法,请根据实际API调整
+                } catch (Exception ignored) {}
+
+                try {
+                    RedisTenantContext.clear(); // 或 RedisTenantContext.removeTenantId()
+                } catch (Exception ignored) {}
+            }
+        }
+    }
+}

+ 5 - 7
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -942,21 +942,19 @@ public class QwUserServiceImpl implements IQwUserService
         log.info("同步用户数量:{}", deptUser.size());
         log.info("同步用户数量:{}", deptUser.size());
         QwCompany qwCompany = iQwCompanyService.selectQwCompanyByCorpId(corpId);
         QwCompany qwCompany = iQwCompanyService.selectQwCompanyByCorpId(corpId);
         for (DeptUser user : deptUser) {
         for (DeptUser user : deptUser) {
-            QwUser qw=qwUserMapper.selectQwUserByCorpIdAndUserId(corpId,user.getUserid());
-            String serverQwUserName = qwApiService.getServerQwUserName(corpId, qwCompany.getOpenSecret(), user.getUserid(),qwCompany.getPermanentCode());
-            log.info("同步用户名称:{}", serverQwUserName);
+            QwUser qw=qwUserMapper.selectQwUserByCorpIdAndUserId(corpId,qwApiService.getOpenUserid(qwApiService.getToken(corpId,qwCompany.getPermanentCode()),user.getUserid(),corpId));
+//            String serverQwUserName = qwApiService.getServerQwUserName(corpId, qwCompany.getOpenSecret(), user.getUserid(),qwCompany.getPermanentCode());
+//            log.info("同步用户名称:{}", serverQwUserName);
             QwUser qwUser = new QwUser();
             QwUser qwUser = new QwUser();
             qwUser.setQwUserId(user.getUserid());
             qwUser.setQwUserId(user.getUserid());
             qwUser.setDepartment(user.getDepartment().toString());
             qwUser.setDepartment(user.getDepartment().toString());
-            qwUser.setQwUserName(serverQwUserName);
+//            qwUser.setQwUserName(serverQwUserName);
             qwUser.setCorpId(corpId);
             qwUser.setCorpId(corpId);
             QwOpenidByUserParams param=new QwOpenidByUserParams();
             QwOpenidByUserParams param=new QwOpenidByUserParams();
             param.setUserid(user.getUserid());
             param.setUserid(user.getUserid());
             QwOpenidResult qwOpenidResult = qwApiService.useridToOpenid(param, corpId);
             QwOpenidResult qwOpenidResult = qwApiService.useridToOpenid(param, corpId);
             qwUser.setOpenid(qwOpenidResult.getOpenid());
             qwUser.setOpenid(qwOpenidResult.getOpenid());
-             if (qw==null){
-                 qwUserMapper.insertQwUser(qwUser);
-             }else {
+             if (qw!=null){
                  qwUser.setId(qw.getId());
                  qwUser.setId(qw.getId());
                  qwUser.setIsDel(0);
                  qwUser.setIsDel(0);
                  qwUserMapper.updateQwUser(qwUser);
                  qwUserMapper.updateQwUser(qwUser);

+ 1 - 0
fs-service/src/main/java/com/fs/qwApi/config/OpenQwConfig.java

@@ -3,4 +3,5 @@ package com.fs.qwApi.config;
 public interface OpenQwConfig {
 public interface OpenQwConfig {
     String baseApi ="http://saasqwapi.ylrzcloud.com/open/qwapi";
     String baseApi ="http://saasqwapi.ylrzcloud.com/open/qwapi";
     String api ="http://saasqwapi.ylrzcloud.com";
     String api ="http://saasqwapi.ylrzcloud.com";
+    String taskApi ="101.245.104.238:7006";
 }
 }

+ 1 - 1
fs-service/src/main/java/com/fs/qwApi/service/impl/QwApiServiceImpl.java

@@ -1590,7 +1590,7 @@ public class QwApiServiceImpl implements QwApiService {
 
 
         QwCompany qwCompany = iQwCompanyService.selectQwCompanyByCorpId(corpId);
         QwCompany qwCompany = iQwCompanyService.selectQwCompanyByCorpId(corpId);
 
 
-        String bookSecret = qwCompany.getServerBookSecret();
+        String bookSecret = qwCompany.getPermanentCode();
 
 
         HttpClient httpClient = HttpClients.createDefault();
         HttpClient httpClient = HttpClients.createDefault();
         try {
         try {

+ 17 - 27
fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

@@ -368,33 +368,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </insert>
     </insert>
 
 
     <!-- 批量更新 - 使用 CASE WHEN 方式,更高效 -->
     <!-- 批量更新 - 使用 CASE WHEN 方式,更高效 -->
-    <update id="batchUpdateQwUser">
-        UPDATE qw_user
-        SET
-        department = CASE id
-        <foreach collection="list" item="user">
-            WHEN #{user.id} THEN #{user.department}
-        </foreach>
-        END,
-        qw_user_name = CASE id
-        <foreach collection="list" item="user">
-            WHEN #{user.id} THEN #{user.qwUserName}
-        </foreach>
-        END,
-        openid = CASE id
-        <foreach collection="list" item="user">
-            WHEN #{user.id} THEN #{user.openid}
-        </foreach>
-        END,
-        is_del = CASE id
-        <foreach collection="list" item="user">
-            WHEN #{user.id} THEN #{user.isDel}
+    <insert id="batchUpdateQwUser">
+        INSERT INTO qw_user (
+        id, corp_id, qw_open_user_id, qw_user_name,
+        department, openid, is_del, create_time, update_time
+        ) VALUES
+        <foreach collection="list" item="user" separator=",">
+            (
+            #{user.id}, #{user.corpId}, #{user.qwOpenUserId}, #{user.qwUserName},
+            #{user.department}, #{user.openid}, #{user.isDel},
+            NOW(), NOW()
+            )
         </foreach>
         </foreach>
-        END,
+        ON DUPLICATE KEY UPDATE
+        qw_user_name = VALUES(qw_user_name),
+        department = VALUES(department),
+        openid = VALUES(openid),
+        is_del = VALUES(is_del),
         update_time = NOW()
         update_time = NOW()
-        WHERE id IN
-        <foreach collection="list" item="user" open="(" close=")" separator=",">
-            #{user.id}
-        </foreach>
-    </update>
+    </insert>
 </mapper>
 </mapper>