From c71714508fbe3ace3543423c7700d7bbcca90056 Mon Sep 17 00:00:00 2001
From: cuilei <ray_tsu1@163.com>
Date: 星期二, 12 八月 2025 13:41:37 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 src/main/java/org/jeecg/modules/mes/job/FeishuUserService.java |  327 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 327 insertions(+), 0 deletions(-)

diff --git a/src/main/java/org/jeecg/modules/mes/job/FeishuUserService.java b/src/main/java/org/jeecg/modules/mes/job/FeishuUserService.java
new file mode 100644
index 0000000..b5b3991
--- /dev/null
+++ b/src/main/java/org/jeecg/modules/mes/job/FeishuUserService.java
@@ -0,0 +1,327 @@
+package org.jeecg.modules.mes.job;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.util.PasswordUtil;
+import org.jeecg.common.util.RestUtil;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.mes.entity.FeishuUser;
+import org.jeecg.modules.system.entity.SysUser;
+import org.jeecg.modules.system.service.ISysUserService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class FeishuUserService {
+
+    @Resource
+    private ISysUserService sysUserService;
+
+    @Resource
+    private RestTemplate restTemplate;  // 鏂板RestTemplate渚濊禆娉ㄥ叆
+
+    @Value("${feishu.appId:}")
+    private String appId;
+
+    @Value("${feishu.appSecret:}")
+    private String appSecret;
+
+    @Value("${feishu.url:}")
+    private String feishuUrl;
+
+    /**
+     * 鍚屾椋炰功閮ㄩ棬鐢ㄦ埛鍒扮郴缁熺敤鎴疯〃
+     * @param departmentId 椋炰功閮ㄩ棬ID
+     */
+    public void syncFeishuDepartmentUsers(String departmentId) {
+        try {
+            log.info("寮�濮嬪悓姝ラ涔﹂儴闂ㄧ敤鎴凤紝閮ㄩ棬ID: {}", departmentId);
+
+            // 1. 鑾峰彇椋炰功璁块棶浠ょ墝
+            String accessToken = getFeishuAccessToken();
+            if (oConvertUtils.isEmpty(accessToken)) {
+                log.error("鑾峰彇椋炰功璁块棶浠ょ墝澶辫触锛岀粓姝㈠悓姝ユ祦绋�");
+                return;  // 浠ょ墝鑾峰彇澶辫触鐩存帴杩斿洖锛岄伩鍏嶅悗缁棤鏁堟搷浣�
+            }
+
+            // 2. 鑾峰彇閮ㄩ棬涓嬬殑鐢ㄦ埛鍒楄〃
+            List<FeishuUser> feishuUsers = getDepartmentUsers(accessToken, departmentId);
+            log.info("鑾峰彇鍒伴涔﹂儴闂ㄧ敤鎴锋暟閲�: {}", feishuUsers.size());
+
+            // 3. 鍚屾鍒扮郴缁熺敤鎴疯〃
+            int successCount = 0;
+            int updateCount = 0;
+            int addCount = 0;
+
+            for (FeishuUser feishuUser : feishuUsers) {
+                try {
+                    boolean isUpdated = syncFeishuUserToSystem(feishuUser);
+                    successCount++;
+                    if (isUpdated) {
+                        updateCount++;
+                    } else {
+                        addCount++;
+                    }
+                } catch (Exception e) {
+                    log.error("鍚屾椋炰功鐢ㄦ埛澶辫触锛岀敤鎴稩D: {}", feishuUser.getUserId(), e);
+                }
+            }
+
+            log.info("椋炰功鐢ㄦ埛鍚屾瀹屾垚锛屾�诲鐞�: {}锛屾柊澧�: {}锛屾洿鏂�: {}", successCount, addCount, updateCount);
+        } catch (Exception e) {
+            log.error("鍚屾椋炰功閮ㄩ棬鐢ㄦ埛澶辫触锛岄儴闂↖D: {}", departmentId, e);
+            throw new RuntimeException("鍚屾椋炰功鐢ㄦ埛澶辫触: " + e.getMessage());
+        }
+    }
+
+    private String getFeishuAccessToken() {
+        try {
+            String url = feishuUrl + "open-apis/auth/v3/tenant_access_token/internal";
+
+            log.info("寮�濮嬭幏鍙栭涔﹁闂护鐗岋紝AppId: {}",
+                    appId != null ? appId.substring(0, Math.min(appId.length(), 10)) + "..." : "null");
+
+            JSONObject params = new JSONObject();
+            params.put("app_id", appId);
+            params.put("app_secret", appSecret);
+
+            log.debug("鑾峰彇浠ょ墝璇锋眰鍙傛暟: {}", params.toJSONString());
+
+            JSONObject result = RestUtil.post(url, params);
+
+            log.info("鑾峰彇椋炰功璁块棶浠ょ墝鍝嶅簲: {}", result != null ? result.toJSONString() : "null");
+
+            if (result != null && result.getInteger("code") == 0) {
+                String accessToken = result.getString("tenant_access_token");
+                log.info("鎴愬姛鑾峰彇椋炰功璁块棶浠ょ墝锛屼护鐗岄暱搴�: {}", accessToken != null ? accessToken.length() : 0);
+                return accessToken;
+            } else {
+                log.error("鑾峰彇椋炰功璁块棶浠ょ墝澶辫触锛屽搷搴�: {}", result != null ? result.toJSONString() : "null");
+                if (result != null) {
+                    log.error("閿欒鐮�: {}, 閿欒淇℃伅: {}", result.getInteger("code"), result.getString("msg"));
+                }
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("鑾峰彇椋炰功璁块棶浠ょ墝寮傚父", e);
+            return null;
+        }
+    }
+
+    /**
+     * 鑾峰彇閮ㄩ棬鐢ㄦ埛鍒楄〃锛堢洿灞炲憳宸ワ級
+     */
+    private List<FeishuUser> getDepartmentUsers(String accessToken, String departmentId) {
+        List<FeishuUser> userList = new ArrayList<>();
+        try {
+            log.info("寮�濮嬭幏鍙栭涔﹂儴闂ㄧ敤鎴峰垪琛紝閮ㄩ棬ID: {}", departmentId);
+
+            // 楠岃瘉璁块棶浠ょ墝
+            if (accessToken == null || accessToken.isEmpty()) {
+                log.error("璁块棶浠ょ墝涓虹┖锛屾棤娉曠户缁墽琛�");
+                return userList;
+            }
+            log.info("浣跨敤鐨勮闂护鐗屽墠缂�: Bearer {}", accessToken.substring(0, Math.min(20, accessToken.length())) + "...");
+
+            // 椋炰功API绔偣
+            String url = feishuUrl + "open-apis/contact/v3/users/find_by_department";
+
+            String pageToken = null;
+            int pageNumber = 1;
+
+            do {
+                // 鏋勫缓璇锋眰URL鍜屽弬鏁�
+                StringBuilder urlBuilder = new StringBuilder(url);
+                urlBuilder.append("?department_id=").append(departmentId);
+                urlBuilder.append("&department_id_type=open_department_id");
+                urlBuilder.append("&page_size=50");
+                urlBuilder.append("&user_id_type=open_id");
+
+                if (pageToken != null && !pageToken.isEmpty()) {
+                    urlBuilder.append("&page_token=").append(pageToken);
+                }
+
+                log.info("璇锋眰绗瑊}椤垫暟鎹紝URL: {}", pageNumber, urlBuilder.toString());
+
+                // 璁剧疆璇锋眰澶� - 鍏抽敭淇鐐癸細浣跨敤HttpHeaders纭繚澶翠俊鎭纭缃�
+                HttpHeaders headers = new HttpHeaders();
+                String authHeader = "Bearer " + accessToken;
+                headers.add("Authorization", authHeader);
+                headers.add("Content-Type", "application/json; charset=utf-8");
+                log.info("璁剧疆璇锋眰澶�: Authorization = {}", authHeader.substring(0, Math.min(30, authHeader.length())) + "...");
+
+                // 浣跨敤RestTemplate鍙戦�佽姹傦紝纭繚澶翠俊鎭姝g‘浼犻��
+                HttpEntity<String> requestEntity = new HttpEntity<>(headers);
+                ResponseEntity<String> response = restTemplate.exchange(
+                        urlBuilder.toString(),
+                        HttpMethod.GET,
+                        requestEntity,
+                        String.class
+                );
+
+                // 瑙f瀽鍝嶅簲
+                JSONObject result = JSONObject.parseObject(response.getBody());
+                log.debug("绗瑊}椤礎PI鍝嶅簲: {}", pageNumber, result != null ? result.toJSONString() : "null");
+
+                if (result != null && result.getInteger("code") == 0) {
+                    JSONObject data = result.getJSONObject("data");
+                    if (data != null) {
+                        // 瑙f瀽鐢ㄦ埛鍒楄〃
+                        Object items = data.get("items");
+                        log.info("绗瑊}椤靛師濮嬬敤鎴锋暟鎹暟閲�: {}", pageNumber, items instanceof List ? ((List<?>) items).size() : 0);
+
+                        if (items != null) {
+                            List<FeishuUser> pageUsers = parseUsers(items);
+                            userList.addAll(pageUsers);
+                            log.info("绗瑊}椤佃В鏋愬悗鐢ㄦ埛鏁伴噺: {}", pageNumber, pageUsers.size());
+                        }
+
+                        // 妫�鏌ユ槸鍚︽湁涓嬩竴椤�
+                        pageToken = data.getString("page_token");
+                        boolean hasMore = data.getBooleanValue("has_more");
+                        log.info("绗瑊}椤礹as_more: {}, page_token: {}", pageNumber, hasMore, pageToken);
+                        pageNumber++;
+                    } else {
+                        log.warn("绗瑊}椤礵ata涓虹┖", pageNumber);
+                        break;
+                    }
+                } else {
+                    log.error("鑾峰彇椋炰功閮ㄩ棬鐢ㄦ埛鍒楄〃澶辫触锛屽搷搴�: {}", result != null ? result.toJSONString() : "null");
+                    if (result != null) {
+                        log.error("閿欒鐮�: {}, 閿欒淇℃伅: {}", result.getInteger("code"), result.getString("msg"));
+                    }
+                    break;
+                }
+            } while (pageToken != null && !pageToken.isEmpty());
+
+            log.info("鑾峰彇椋炰功閮ㄩ棬鐢ㄦ埛瀹屾垚锛屾�荤敤鎴锋暟閲�: {}", userList.size());
+
+        } catch (Exception e) {
+            log.error("鑾峰彇椋炰功閮ㄩ棬鐢ㄦ埛鍒楄〃寮傚父", e);
+        }
+        return userList;
+    }
+
+    /**
+     * 瑙f瀽鐢ㄦ埛鏁版嵁
+     */
+    /**
+     * 瑙f瀽鐢ㄦ埛鏁版嵁
+     */
+    @SuppressWarnings("unchecked")
+    private List<FeishuUser> parseUsers(Object items) {
+        Logger log = LoggerFactory.getLogger(FeishuUserService.class);
+        List<FeishuUser> userList = new ArrayList<>();
+        if (!(items instanceof List)) {
+            log.warn("瑙f瀽鐢ㄦ埛鏁版嵁澶辫触锛宨tems涓嶆槸鍒楄〃绫诲瀷: {}", items);
+            return userList;
+        }
+        List<?> userItems = (List<?>) items;
+        log.info("寮�濮嬭В鏋愰涔︾敤鎴峰垪琛紝鍏眥}鏉¤褰�", userItems.size());
+        for (Object item : userItems) {
+            if (item == null || !(item instanceof Map)) {
+                log.warn("璺宠繃鏃犳晥鐢ㄦ埛鏁版嵁椤癸紝绫诲瀷涓嶅尮閰�: {}", item != null ? item.getClass() : "null");
+                continue;
+            }
+            Map<String, Object> userMap = (Map<String, Object>) item;
+            FeishuUser user = new FeishuUser();
+            // 瑙f瀽open_id
+            user.setOpenId((String) userMap.getOrDefault("open_id", ""));
+            // 瑙f瀽name骞舵媶鍒�
+            String name = (String) userMap.getOrDefault("name", "");
+            if (name.contains(" ")) {
+                String[] parts = name.split(" ");
+                if (parts.length == 2) {
+                    user.setUsername(parts[0].trim()); // 宸ュ彿閮ㄥ垎瀛樺埌username
+                    user.setRealname(parts[1].trim()); // 濮撳悕閮ㄥ垎瀛樺埌realname
+                    user.setWorkNo(parts[0].trim());   // 宸ュ彿涔熷瓨鍒皐orkNo
+                } else {
+                    // 鎷嗗垎寮傚父鏃讹紝榛樿澶勭悊
+                    user.setUsername(name);
+                    user.setRealname(name);
+                    user.setWorkNo("");
+                    log.warn("椋炰功鐢ㄦ埛name瀛楁鏍煎紡寮傚父锛屾棤娉曟媶鍒嗭紝鍘熷鍊硷細{}", name);
+                }
+            } else {
+                user.setUsername(name);
+                user.setRealname(name);
+                user.setWorkNo("");
+                log.warn("椋炰功鐢ㄦ埛name瀛楁鏃犵┖鏍煎垎闅旓紝鏃犳硶鎷嗗垎锛屽師濮嬪�硷細{}", name);
+            }
+            // 瑙f瀽鍏朵粬鍩烘湰淇℃伅
+            user.setEnName((String) userMap.getOrDefault("en_name", ""));
+            user.setDescription((String) userMap.getOrDefault("description", ""));
+            Object mobileVisibleObj = userMap.get("mobile_visible");
+            user.setMobileVisible(mobileVisibleObj instanceof Boolean ? (Boolean) mobileVisibleObj : false);
+            // 瑙f瀽澶村儚淇℃伅
+            Object avatarObj = userMap.get("avatar");
+            if (avatarObj instanceof Map) {
+                Map<String, Object> avatarMap = (Map<String, Object>) avatarObj;
+                user.setAvatar240((String) avatarMap.getOrDefault("avatar_240", ""));
+                user.setAvatar640((String) avatarMap.getOrDefault("avatar_640", ""));
+                user.setAvatar72((String) avatarMap.getOrDefault("avatar_72", ""));
+                user.setAvatarOrigin((String) avatarMap.getOrDefault("avatar_origin", ""));
+            } else {
+                log.info("鐢ㄦ埛[{}]鏈缃ご鍍忔垨澶村儚鏍煎紡寮傚父", user.getRealname());
+            }
+            // 璁剧疆榛樿鍔犲瘑瀵嗙爜锛堣繖閲屽亣璁惧瘑鐮佹槸绠�鍗曞姞瀵嗗瓨鍌紝瀹為檯寤鸿鐢ㄦ洿瀹夊叏鐨勫姞瀵嗘柟寮忥級
+            user.setPassword("123456"); // 浣犺姹傜殑榛樿瀵嗙爜
+            // 灏唎pen_id瀛樺埌瀵瑰簲鐨勫瓧娈碉紙鏍规嵁浣犵殑鐢ㄦ埛琛ㄧ粨鏋勶紝鍋囪鐢ㄦ埛琛ㄦ湁open_id瀛楁锛�
+            user.setOpenId((String) userMap.getOrDefault("open_id", ""));
+            userList.add(user);
+        }
+
+        return userList;
+    }
+
+    /**
+     * 鍚屾鍗曚釜椋炰功鐢ㄦ埛鍒扮郴缁�
+     * @param feishuUser 椋炰功鐢ㄦ埛
+     * @return true琛ㄧず鏇存柊锛宖alse琛ㄧず鏂板
+     *
+     * 鍚庢湡澧炲姞锛� 鐢ㄦ埛鍚屾瀵规瘮锛岄涔︾鑱屽憳宸ラ渶瑕佸湪鏈湴鐢ㄦ埛琛ㄩ噷闈㈠皢璇ョ敤鎴风姸鎬佺鐢�
+     */
+    private boolean syncFeishuUserToSystem(FeishuUser feishuUser) {
+        // 杩欓噷鏍规嵁鎷嗗垎鍚庣殑username锛堝伐鍙凤級鍘绘煡璇㈢郴缁熺敤鎴�
+        String username = feishuUser.getUsername();
+        SysUser existUser = sysUserService.getUserByName(username);
+        SysUser sysUser = new SysUser();
+        sysUser.setUsername(username);
+        sysUser.setStatus(1);
+        sysUser.setDelFlag(0);
+        sysUser.setAvatar(feishuUser.getAvatar640());
+        sysUser.setRealname(feishuUser.getRealname());
+        sysUser.setWorkNo(feishuUser.getWorkNo());
+        String password = "123456", salt = oConvertUtils.randomGen(8);
+        String passwordEncode = PasswordUtil.encrypt(sysUser.getUsername(), password, salt);
+        sysUser.setSalt(salt);
+        sysUser.setPassword(passwordEncode);
+        sysUser.setOpenId(feishuUser.getOpenId());
+
+        if (existUser == null) {
+
+            sysUserService.addUserWithRole(sysUser, "5");
+            log.info("鏂板鐢ㄦ埛: {}", username);
+            return false;
+        } else {
+            sysUser.setId(existUser.getId());
+            sysUserService.editUser(sysUser);
+            log.info("鏇存柊鐢ㄦ埛: {}", username);
+            return true;
+        }
+    }
+}

--
Gitblit v1.9.3