WecomContactSyncService.java 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package com.qqflow.engine.domain.ewechat.service;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.qqflow.engine.domain.ewechat.api.EnterpriseWechatApi;
  4. import com.qqflow.engine.domain.ewechat.mapper.EnterpriseWechatDepartmentMapper;
  5. import com.qqflow.engine.domain.ewechat.mapper.EnterpriseWechatMemberMapper;
  6. import com.qqflow.engine.domain.ewechat.model.enterprise.rsp.EnterpriseWechatUserListRsp;
  7. import com.qqflow.engine.domain.ewechat.po.EnterpriseWechatDepartment;
  8. import com.qqflow.engine.domain.ewechat.po.EnterpriseWechatMember;
  9. import lombok.RequiredArgsConstructor;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.springframework.stereotype.Service;
  12. import java.time.LocalDateTime;
  13. import java.util.*;
  14. /**
  15. * 企微通讯录同步服务(懒加载 + 全量同步)
  16. */
  17. @Slf4j
  18. @Service
  19. @RequiredArgsConstructor
  20. public class WecomContactSyncService {
  21. private final EnterpriseWechatDepartmentMapper departmentMapper;
  22. private final EnterpriseWechatMemberMapper memberMapper;
  23. private final EnterpriseWechatApi enterpriseWechatApi;
  24. private final EnterpriseWechatMsgService wechatMsgService;
  25. /** 获取子部门列表(从本地DB查询) */
  26. public List<EnterpriseWechatDepartment> listSubDepartments(Long parentId) {
  27. return departmentMapper.selectList(
  28. new LambdaQueryWrapper<EnterpriseWechatDepartment>().eq(EnterpriseWechatDepartment::getParentId, parentId));
  29. }
  30. /** 获取部门成员(懒加载) */
  31. public List<EnterpriseWechatMember> listDepartmentMembers(Long departmentId) {
  32. List<EnterpriseWechatMember> cached = memberMapper.selectList(
  33. new LambdaQueryWrapper<EnterpriseWechatMember>().eq(EnterpriseWechatMember::getDepartmentId, departmentId));
  34. if (!cached.isEmpty()) return cached;
  35. String accessToken = wechatMsgService.getCorpAccessToken();
  36. if (accessToken == null) return List.of();
  37. EnterpriseWechatUserListRsp rsp = enterpriseWechatApi.listDepartmentUsers(accessToken, departmentId);
  38. if (rsp == null || rsp.getUserlist() == null) return List.of();
  39. List<EnterpriseWechatMember> members = new ArrayList<>();
  40. for (var u : rsp.getUserlist()) {
  41. EnterpriseWechatMember wm = new EnterpriseWechatMember();
  42. wm.setUserid(u.getUserid());
  43. wm.setName(u.getName() != null ? u.getName() : u.getUserid());
  44. wm.setDepartmentId(departmentId);
  45. wm.setOpenUserid(u.getOpenUserid());
  46. memberMapper.insert(wm);
  47. members.add(wm);
  48. }
  49. return members;
  50. }
  51. // ==================== 全量同步 ====================
  52. /**
  53. * 全量同步:部门 + 成员
  54. * @return 同步结果摘要
  55. */
  56. public Map<String, Object> syncAll() {
  57. String accessToken = wechatMsgService.getCorpAccessToken();
  58. if (accessToken == null) {
  59. throw new RuntimeException("无法获取企微 access_token,请检查授权配置");
  60. }
  61. Map<String, Object> result = new LinkedHashMap<>();
  62. Map<String, Integer> deptResult = this.syncAllDepartments(accessToken);
  63. result.put("departments", deptResult);
  64. Map<String, Integer> memberResult = this.syncAllMembers(accessToken);
  65. result.put("members", memberResult);
  66. return result;
  67. }
  68. /**
  69. * 全量同步部门:从企微拉取完整部门树,upsert 到本地表
  70. */
  71. private Map<String, Integer> syncAllDepartments(String accessToken) {
  72. var rsp = enterpriseWechatApi.listDepartment(accessToken, null);
  73. if (rsp == null || rsp.getDepartment() == null || rsp.getDepartment().isEmpty()) {
  74. log.warn("[通讯录同步] 企微返回的部门列表为空");
  75. return Map.of("total", 0, "inserted", 0, "updated", 0);
  76. }
  77. int inserted = 0, updated = 0;
  78. for (var d : rsp.getDepartment()) {
  79. EnterpriseWechatDepartment dept = departmentMapper.selectById(d.getId());
  80. if (dept == null) {
  81. dept = new EnterpriseWechatDepartment();
  82. dept.setId(d.getId());
  83. dept.setCreatedTime(LocalDateTime.now());
  84. inserted++;
  85. } else {
  86. updated++;
  87. }
  88. dept.setName(d.getName());
  89. dept.setParentId(d.getParentid() != null ? d.getParentid() : 0L);
  90. this.departmentMapper.insertOrUpdate(dept);
  91. }
  92. log.info("[通讯录同步] 部门同步完成 | 总数:{} 新增:{} 更新:{}", rsp.getDepartment().size(), inserted, updated);
  93. return Map.of("total", rsp.getDepartment().size(), "inserted", inserted, "updated", updated);
  94. }
  95. /**
  96. * 全量同步成员:遍历本地已同步的所有部门,从企微拉取各部门成员并 upsert
  97. */
  98. private Map<String, Integer> syncAllMembers(String accessToken) {
  99. List<EnterpriseWechatDepartment> allDepts = departmentMapper.selectList(
  100. new LambdaQueryWrapper<EnterpriseWechatDepartment>());
  101. if (allDepts.isEmpty()) {
  102. log.warn("[通讯录同步] 本地无部门数据,请先同步部门");
  103. return Map.of("departments", 0, "total", 0, "inserted", 0, "updated", 0);
  104. }
  105. int totalInserted = 0, totalUpdated = 0, totalMembers = 0;
  106. for (var dept : allDepts) {
  107. var rsp = enterpriseWechatApi.listDepartmentUsers(accessToken, dept.getId());
  108. if (rsp == null || rsp.getUserlist() == null) continue;
  109. for (var u : rsp.getUserlist()) {
  110. totalMembers++;
  111. // 按 userid + departmentId 查重(一个用户可能在多个部门)
  112. var existing = memberMapper.selectList(
  113. new LambdaQueryWrapper<EnterpriseWechatMember>()
  114. .eq(EnterpriseWechatMember::getUserid, u.getUserid())
  115. .eq(EnterpriseWechatMember::getDepartmentId, dept.getId()));
  116. EnterpriseWechatMember wm;
  117. if (existing != null && !existing.isEmpty()) {
  118. wm = existing.get(0);
  119. totalUpdated++;
  120. } else {
  121. wm = new EnterpriseWechatMember();
  122. totalInserted++;
  123. }
  124. wm.setUserid(u.getUserid());
  125. wm.setName(u.getName() != null ? u.getName() : u.getUserid());
  126. wm.setDepartmentId(dept.getId());
  127. wm.setOpenUserid(u.getOpenUserid());
  128. wm.setUpdatedTime(LocalDateTime.now());
  129. this.memberMapper.insertOrUpdate(wm);
  130. }
  131. }
  132. log.info("[通讯录同步] 成员同步完成 | 部门数:{} 成员总数:{} 新增:{} 更新:{}",
  133. allDepts.size(), totalMembers, totalInserted, totalUpdated);
  134. return Map.of("departments", allDepts.size(), "total", totalMembers,
  135. "inserted", totalInserted, "updated", totalUpdated);
  136. }
  137. }