Quellcode durchsuchen

Merge branch 'feature/sync-ewechat-user'

wuwenyi vor 4 Tagen
Ursprung
Commit
3c36d1a73b

+ 4 - 6
src/main/java/com/qqflow/engine/domain/ewechat/controller/WecomContactController.java

@@ -7,12 +7,10 @@ import com.qqflow.engine.domain.ewechat.service.WecomContactSyncService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import java.util.Map;
 
 @Tag(name = "企微通讯录")
 @RestController
@@ -23,13 +21,13 @@ public class WecomContactController {
     private final WecomContactSyncService syncService;
 
     @GetMapping("/departments")
-    @Operation(summary = "获取子部门列表(懒加载同步)")
+    @Operation(summary = "获取子部门列表")
     public Result<List<EnterpriseWechatDepartment>> departments(@RequestParam(defaultValue = "0") Long parentId) {
         return Result.ok(syncService.listSubDepartments(parentId));
     }
 
     @GetMapping("/members")
-    @Operation(summary = "获取部门成员列表(懒加载同步)")
+    @Operation(summary = "获取部门成员列表")
     public Result<List<EnterpriseWechatMember>> members(@RequestParam Long departmentId) {
         return Result.ok(syncService.listDepartmentMembers(departmentId));
     }

+ 30 - 0
src/main/java/com/qqflow/engine/domain/ewechat/controller/WecomOpenApiController.java

@@ -0,0 +1,30 @@
+package com.qqflow.engine.domain.ewechat.controller;
+
+import com.qqflow.engine.common.Result;
+import com.qqflow.engine.domain.ewechat.service.WecomContactSyncService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ * 内部手动调用接口,通过 /open-api 绕过鉴权
+ */
+@Tag(name = "内部开放接口")
+@RestController
+@RequestMapping("/open-api/wecom")
+@RequiredArgsConstructor
+public class WecomOpenApiController {
+
+    private final WecomContactSyncService syncService;
+
+    @PostMapping("/contact/sync")
+    @Operation(summary = "手动触发企微通讯录全量同步(部门 + 成员)")
+    public Result<Map<String, Object>> syncContact() {
+        return Result.ok(syncService.syncAll());
+    }
+}

+ 101 - 3
src/main/java/com/qqflow/engine/domain/ewechat/service/WecomContactSyncService.java

@@ -11,11 +11,11 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.time.LocalDateTime;
+import java.util.*;
 
 /**
- * 企微通讯录同步服务(懒加载:先查本地DB,没有再调企微接口同步后返回
+ * 企微通讯录同步服务(懒加载 + 全量同步
  */
 @Slf4j
 @Service
@@ -58,4 +58,102 @@ public class WecomContactSyncService {
         return members;
     }
 
+    // ==================== 全量同步 ====================
+
+    /**
+     * 全量同步:部门 + 成员
+     * @return 同步结果摘要
+     */
+    public Map<String, Object> syncAll() {
+        String accessToken = wechatMsgService.getCorpAccessToken();
+        if (accessToken == null) {
+            throw new RuntimeException("无法获取企微 access_token,请检查授权配置");
+        }
+
+        Map<String, Object> result = new LinkedHashMap<>();
+        Map<String, Integer> deptResult = this.syncAllDepartments(accessToken);
+        result.put("departments", deptResult);
+
+        Map<String, Integer> memberResult = this.syncAllMembers(accessToken);
+        result.put("members", memberResult);
+
+        return result;
+    }
+
+    /**
+     * 全量同步部门:从企微拉取完整部门树,upsert 到本地表
+     */
+    private Map<String, Integer> syncAllDepartments(String accessToken) {
+        var rsp = enterpriseWechatApi.listDepartment(accessToken, null);
+        if (rsp == null || rsp.getDepartment() == null || rsp.getDepartment().isEmpty()) {
+            log.warn("[通讯录同步] 企微返回的部门列表为空");
+            return Map.of("total", 0, "inserted", 0, "updated", 0);
+        }
+
+        int inserted = 0, updated = 0;
+        for (var d : rsp.getDepartment()) {
+            EnterpriseWechatDepartment dept = departmentMapper.selectById(d.getId());
+            if (dept == null) {
+                dept = new EnterpriseWechatDepartment();
+                dept.setId(d.getId());
+                dept.setCreatedTime(LocalDateTime.now());
+                inserted++;
+            } else {
+                updated++;
+            }
+            dept.setName(d.getName());
+            dept.setParentId(d.getParentid() != null ? d.getParentid() : 0L);
+            this.departmentMapper.insertOrUpdate(dept);
+        }
+
+        log.info("[通讯录同步] 部门同步完成 | 总数:{} 新增:{} 更新:{}", rsp.getDepartment().size(), inserted, updated);
+        return Map.of("total", rsp.getDepartment().size(), "inserted", inserted, "updated", updated);
+    }
+
+    /**
+     * 全量同步成员:遍历本地已同步的所有部门,从企微拉取各部门成员并 upsert
+     */
+    private Map<String, Integer> syncAllMembers(String accessToken) {
+        List<EnterpriseWechatDepartment> allDepts = departmentMapper.selectList(
+                new LambdaQueryWrapper<EnterpriseWechatDepartment>());
+        if (allDepts.isEmpty()) {
+            log.warn("[通讯录同步] 本地无部门数据,请先同步部门");
+            return Map.of("departments", 0, "total", 0, "inserted", 0, "updated", 0);
+        }
+
+        int totalInserted = 0, totalUpdated = 0, totalMembers = 0;
+        for (var dept : allDepts) {
+            var rsp = enterpriseWechatApi.listDepartmentUsers(accessToken, dept.getId());
+            if (rsp == null || rsp.getUserlist() == null) continue;
+
+            for (var u : rsp.getUserlist()) {
+                totalMembers++;
+                // 按 userid + departmentId 查重(一个用户可能在多个部门)
+                var existing = memberMapper.selectList(
+                        new LambdaQueryWrapper<EnterpriseWechatMember>()
+                                .eq(EnterpriseWechatMember::getUserid, u.getUserid())
+                                .eq(EnterpriseWechatMember::getDepartmentId, dept.getId()));
+                EnterpriseWechatMember wm;
+                if (existing != null && !existing.isEmpty()) {
+                    wm = existing.get(0);
+                    totalUpdated++;
+                } else {
+                    wm = new EnterpriseWechatMember();
+                    totalInserted++;
+                }
+                wm.setUserid(u.getUserid());
+                wm.setName(u.getName() != null ? u.getName() : u.getUserid());
+                wm.setDepartmentId(dept.getId());
+                wm.setOpenUserid(u.getOpenUserid());
+                wm.setUpdatedTime(LocalDateTime.now());
+                this.memberMapper.insertOrUpdate(wm);
+            }
+        }
+
+        log.info("[通讯录同步] 成员同步完成 | 部门数:{} 成员总数:{} 新增:{} 更新:{}",
+                allDepts.size(), totalMembers, totalInserted, totalUpdated);
+        return Map.of("departments", allDepts.size(), "total", totalMembers,
+                "inserted", totalInserted, "updated", totalUpdated);
+    }
+
 }