From 8ac573030c8fc795d9a30afb739f44393f8b63eb Mon Sep 17 00:00:00 2001
From: cuilei <ray_tsu1@163.com>
Date: 星期一, 18 八月 2025 18:03:07 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 src/main/java/org/jeecg/modules/feishu/service/FeishuUserService.java |  635 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 635 insertions(+), 0 deletions(-)

diff --git a/src/main/java/org/jeecg/modules/feishu/service/FeishuUserService.java b/src/main/java/org/jeecg/modules/feishu/service/FeishuUserService.java
new file mode 100644
index 0000000..92d6b09
--- /dev/null
+++ b/src/main/java/org/jeecg/modules/feishu/service/FeishuUserService.java
@@ -0,0 +1,635 @@
+package org.jeecg.modules.feishu.service;
+
+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.andon.dto.AndonButtonDTO;
+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.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Service
+@Slf4j
+public class FeishuUserService {
+    // 绫讳腑瀹氫箟妯℃澘甯搁噺锛堜究浜庣淮鎶わ級
+    private static final String FIRST_LEVEL_TEMPLATE = "銆愬伐鑹哄畨鐏�慭n%s 路 %s 鍙戣捣浜嗗伐鑹哄畨鐏紝璇�%d鍒嗛挓鍐呭湪PDA涓婅繘琛屽搷搴斿鐞嗭紒锛侊紒";
+    private static final String SECOND_LEVEL_TEMPLATE = "銆愬伐鑹哄畨鐏�慭n%s 路 %s 鍙戣捣鐨勫伐鑹哄畨鐏紝%s鏈湪%d鍒嗛挓鍐呭搷搴旓紝璇峰敖蹇潱淇冭繘琛屽搷搴斿鐞嗭紒锛侊紒";
+    @Resource
+    private ISysUserService sysUserService;
+
+    @Resource
+    private RestTemplate restTemplate;
+
+    @Value("${feishu.appId:}")
+    private String appId;
+
+    @Value("${feishu.appSecret:}")
+    private String appSecret;
+
+    @Value("${feishu.url:}")
+    private String feishuUrl;
+
+    // 瀹氭椂浠诲姟绾跨▼姹狅紙鏀寔澶氱骇鍝嶅簲璋冨害锛�
+    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
+    private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
+
+    /**
+     * 鍚屾椋炰功閮ㄩ棬鐢ㄦ埛鍒扮郴缁熺敤鎴疯〃
+     */
+    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());
+        }
+    }
+
+    public 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;
+        }
+    }
+
+    /**
+     * 鍙戦�佸畨鐏�氱煡娑堟伅锛堟敮鎸佷竴銆佷簩銆佷笁绾у搷搴旓級
+     * 娴佺▼锛�
+     * 1. APP鐐瑰嚮鍙戦�佷竴绾у搷搴旓紙鍒濈骇鍝嶅簲锛�
+     * 2. 涓�绾у搷搴旀椂闀垮悗妫�鏌ョ姸鎬侊紝鏈鐞嗗垯鍙戦�佷簩绾�
+     * 3. 浜岀骇鍝嶅簲鏃堕暱鍚庢鏌ョ姸鎬侊紝鏈鐞嗗垯鍙戦�佷笁绾�
+     */
+    public boolean sendAndonNotification(String accessToken, AndonButtonDTO andonOrde) {
+        try {
+            String currentTime = sdf.format(new Date());
+            log.info("銆恵}銆戣Е鍙戜竴绾у搷搴旓紙鍒濈骇鍝嶅簲锛夛紝瀹夌伅ID: {}", currentTime, andonOrde.getId());
+
+            // 1. 鍙傛暟楠岃瘉
+            if (!validateNotificationParams(andonOrde)) {
+                return false;
+            }
+            if (accessToken == null || accessToken.isEmpty()) {
+                log.error("璁块棶浠ょ墝涓虹┖锛屾棤娉曞彂閫佸畨鐏�氱煡");
+                return false;
+            }
+
+            // 2. 鍙戦�佷竴绾у搷搴旈�氱煡锛堝嵆鏃跺彂閫侊級
+            boolean firstLevelResult = sendLevelNotification(accessToken, andonOrde, 1);
+
+            // 3. 璋冨害浜岀骇鍝嶅簲閫氱煡锛堝欢杩熸椂闂�=涓�绾у搷搴旀椂闀匡級
+            if (firstLevelResult) {
+                int secondDelay = andonOrde.getUpgradeResponseDuration();
+                String secondTime = sdf.format(new Date(System.currentTimeMillis() + secondDelay * 60 * 1000L));
+                log.info("銆恵}銆戜竴绾у搷搴旇皟搴︿簩绾ч�氱煡锛屽欢杩焮}鍒嗛挓锛岄璁″彂閫佹椂闂�: {}",
+                        currentTime, secondDelay, secondTime);
+                scheduleNextLevelNotification(accessToken, andonOrde, 1, secondDelay);
+            } else {
+                log.warn("涓�绾ч�氱煡鍙戦�佸け璐ワ紝缁堟鍚庣画閫氱煡娴佺▼锛屽畨鐏疘D: {}", andonOrde.getId());
+            }
+
+            return firstLevelResult;
+        } catch (Exception e) {
+            log.error("澶勭悊瀹夌伅閫氱煡寮傚父锛屽畨鐏疘D: {}", andonOrde != null ? andonOrde.getId() : "鏈煡", e);
+            return false;
+        }
+    }
+
+    /**
+     * 鍙戦�佹寚瀹氱骇鍒殑閫氱煡
+     * @param level 1-涓�绾э紝2-浜岀骇锛�3-涓夌骇
+     */
+    private boolean sendLevelNotification(String accessToken, AndonButtonDTO andonOrde, int level) {
+        try {
+            String levelDesc = getLevelDesc(level);
+            String openId = getResponderOpenId(andonOrde, level);
+            String currentTime = sdf.format(new Date());
+            log.info("銆恵}銆戝紑濮嬪彂閫亄}绾у畨鐏�氱煡锛屾帴鏀朵汉openId: {}, 瀹夌伅ID: {}",
+                    currentTime, levelDesc, openId, andonOrde.getId());
+
+            // 鏋勫缓璇锋眰URL
+            String url = feishuUrl + "open-apis/im/v1/messages?receive_id_type=open_id";
+            log.info("{}绾ч�氱煡璇锋眰URL: {}", levelDesc, url);
+
+            // 璁剧疆璇锋眰澶�
+            HttpHeaders headers = new HttpHeaders();
+            String authHeader = "Bearer " + accessToken;
+            headers.add("Authorization", authHeader);
+            headers.add("Content-Type", "application/json; charset=utf-8");
+            log.info("璁剧疆{}绾ч�氱煡璇锋眰澶�: Authorization = {}", levelDesc,
+                    authHeader.substring(0, Math.min(30, authHeader.length())) + "...");
+
+            // 鏋勫缓娑堟伅鍐呭
+            JSONObject content = new JSONObject();
+            String notificationContent = buildNotificationContent(andonOrde, level);
+            content.put("text", notificationContent);
+            log.debug("銆恵}銆憑}绾ч�氱煡鍐呭: {}", currentTime, levelDesc, notificationContent);
+
+            // 鏋勫缓璇锋眰浣�
+            JSONObject requestBody = new JSONObject();
+            requestBody.put("receive_id", openId);
+            requestBody.put("msg_type", "text");
+            requestBody.put("content", content.toJSONString());
+            requestBody.put("uuid", "andon_" + andonOrde.getId() + "_level_" + level + "_" + System.currentTimeMillis());
+
+            // 鍙戦�佽姹�
+            HttpEntity<String> requestEntity = new HttpEntity<>(requestBody.toJSONString(), headers);
+            ResponseEntity<String> response = restTemplate.exchange(
+                    url, HttpMethod.POST, requestEntity, String.class
+            );
+
+            // 瑙f瀽鍝嶅簲
+            JSONObject result = JSONObject.parseObject(response.getBody());
+            log.debug("{}绾ч�氱煡API鍝嶅簲: {}", levelDesc, result != null ? result.toJSONString() : "null");
+
+            if (result != null && result.getInteger("code") == 0) {
+                JSONObject data = result.getJSONObject("data");
+                if (data != null) {
+                    log.info("銆恵}銆憑}绾у畨鐏�氱煡鍙戦�佹垚鍔燂紝娑堟伅ID: {}, 瀹夌伅ID: {}",
+                            currentTime, levelDesc, data.getString("message_id"), andonOrde.getId());
+                    return true;
+                } else {
+                    log.warn("{}绾ч�氱煡鍝嶅簲data涓虹┖锛屽畨鐏疘D: {}", levelDesc, andonOrde.getId());
+                    return false;
+                }
+            } else {
+                log.error("鍙戦�亄}绾у畨鐏�氱煡澶辫触锛屽畨鐏疘D: {}", levelDesc, andonOrde.getId());
+                if (result != null) {
+                    log.error("閿欒鐮�: {}, 閿欒淇℃伅: {}", result.getInteger("code"), result.getString("msg"));
+                }
+                return false;
+            }
+
+        } catch (Exception e) {
+            log.error("鍙戦�亄}绾у畨鐏�氱煡寮傚父锛屽畨鐏疘D: {}", getLevelDesc(level), andonOrde.getId(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 璋冨害涓嬩竴绾ч�氱煡锛�1鈫�2鈫�3锛�
+     * @param currentLevel 褰撳墠绾у埆
+     * @param delayMinutes 寤惰繜鍒嗛挓鏁帮紙鐢卞綋鍓嶇骇鍒搷搴旀椂闀垮喅瀹氾級
+     */
+    private void scheduleNextLevelNotification(String accessToken, AndonButtonDTO andonOrde, int currentLevel, int delayMinutes) {
+        int nextLevel = currentLevel + 1;
+        if (nextLevel > 3) {
+            log.info("宸插埌杈炬渶楂樼骇鍒�氱煡锛屾棤闇�缁х画璋冨害锛屽畨鐏疘D: {}", andonOrde.getId());
+            return;
+        }
+
+        String currentLevelDesc = getLevelDesc(currentLevel);
+        String nextLevelDesc = getLevelDesc(nextLevel);
+        String scheduleTime = sdf.format(new Date());
+        String executeTime = sdf.format(new Date(System.currentTimeMillis() + delayMinutes * 60 * 1000L));
+
+        log.info("銆恵}銆戣皟搴}閫氱煡锛屽欢杩焮}鍒嗛挓锛堝綋鍓嶇骇鍒珄}鏃堕暱锛夛紝棰勮鎵ц鏃堕棿: {}",
+                scheduleTime, nextLevelDesc, delayMinutes, currentLevelDesc, executeTime);
+
+        scheduler.schedule(() -> {
+            try {
+                String currentExecuteTime = sdf.format(new Date());
+                log.info("銆恵}銆戝紑濮嬫墽琛寋}閫氱煡妫�鏌ワ紝瀹夌伅ID: {}", currentExecuteTime, nextLevelDesc, andonOrde.getId());
+
+                // 妫�鏌ヨ鍗曠姸鎬侊細3琛ㄧず宸插搷搴旓紝鏃犻渶鍙戦�佷笅涓�绾�
+                if (andonOrde.getOrderStatus() != null && "3".equals(andonOrde.getOrderStatus())) {
+                    log.info("銆恵}銆戣鍗曞凡鍝嶅簲锛坰tatus=3锛夛紝鍙栨秷{}閫氱煡鍙戦�侊紝瀹夌伅ID: {}",
+                            currentExecuteTime, nextLevelDesc, andonOrde.getId());
+                    return;
+                }
+
+                // 妫�鏌ヤ笅涓�绾у搷搴斾汉鏄惁鏈夋晥
+                if (!hasValidResponder(andonOrde, nextLevel)) {
+                    log.warn("銆恵}銆憑}鍝嶅簲浜轰俊鎭棤鏁堬紝鍙栨秷閫氱煡鍙戦�侊紝瀹夌伅ID: {}",
+                            currentExecuteTime, nextLevelDesc, andonOrde.getId());
+                    return;
+                }
+
+                // 鍙戦�佷笅涓�绾ч�氱煡
+                boolean nextLevelResult = sendLevelNotification(accessToken, andonOrde, nextLevel);
+                if (nextLevelResult && nextLevel < 3) {
+
+                    int nextDelay = getResponseDuration(andonOrde, nextLevel);
+                    String nextExecuteTime = sdf.format(new Date(System.currentTimeMillis() + nextDelay * 60 * 1000L));
+                    log.info("銆恵}銆憑}閫氱煡璋冨害涓夌骇閫氱煡锛屽欢杩焮}鍒嗛挓锛岄璁″彂閫佹椂闂�: {}",
+                            currentExecuteTime, nextLevelDesc, nextDelay, nextExecuteTime);
+                    scheduleNextLevelNotification(accessToken, andonOrde, nextLevel, nextDelay);
+                } else if (!nextLevelResult) {
+                    log.warn("銆恵}銆憑}閫氱煡鍙戦�佸け璐ワ紝缁堟鍚庣画璋冨害锛屽畨鐏疘D: {}",
+                            currentExecuteTime, nextLevelDesc, andonOrde.getId());
+                }
+
+            } catch (Exception e) {
+                log.error("鎵ц{}璋冨害浠诲姟寮傚父锛屽畨鐏疘D: {}", nextLevelDesc, andonOrde.getId(), e);
+            }
+        }, delayMinutes, TimeUnit.MINUTES);
+    }
+    /**
+     * 鏋勫缓閫氱煡鍐呭锛堜紭鍖栧垎闅旂锛屾彁鍗囧彲璇绘�э級
+     */
+    private String buildNotificationContent(AndonButtonDTO andonOrde, int level) {
+        // 鑾峰彇宸ュ巶鍚嶇О鍜屼骇绾垮悕绉帮紝鍋氱┖鍊间繚鎶�
+        String parentFactoryName = andonOrde.getParentFactoryName() != null ? andonOrde.getParentFactoryName() : "鏈煡宸ュ巶";
+        String factoryName = andonOrde.getFactoryName() != null ? andonOrde.getFactoryName() : "鏈煡浜х嚎";
+        String content;
+
+        if (level == 1) {
+            // 涓�绾фā鏉匡細浣跨敤"路"鍒嗛殧宸ュ巶鍜屼骇绾�
+            int firstDuration = andonOrde.getUpgradeResponseDuration() != null ? andonOrde.getUpgradeResponseDuration() : 0;
+            content = String.format(FIRST_LEVEL_TEMPLATE, parentFactoryName, factoryName, firstDuration);
+        } else {
+            // 浜岀骇妯℃澘锛氫繚鎸佷竴鑷寸殑鍒嗛殧椋庢牸
+            String prevResponder = getResponderName(andonOrde, level - 1);
+            int prevDuration = getResponseDuration(andonOrde, level - 1);
+
+            prevResponder = oConvertUtils.isEmpty(prevResponder) ? "鏈煡浜哄憳" : prevResponder;
+            prevDuration = Math.max(prevDuration, 0);
+
+            content = String.format(SECOND_LEVEL_TEMPLATE, parentFactoryName, factoryName, prevResponder, prevDuration);
+        }
+
+        log.debug("鏋勫缓{}绾ч�氱煡鍐呭: {}", level, content);
+        return content;
+    }
+//    /**
+//     * 鏋勫缓閫氱煡鍐呭锛堟寜绾у埆浣跨敤涓嶅悓妯℃澘锛�
+//     */
+//    private String buildNotificationContent(AndonButtonDTO andonOrde, int level) {
+//        String factoryName = andonOrde.getFactoryName() != null ? andonOrde.getFactoryName() : "鏈煡浜х嚎";
+//
+//        if (level == 1) {
+//            // 涓�绾фā鏉匡細銆愬伐鑹哄畨鐏�慩XX浜х嚎鍙戣捣浜嗗伐鑹哄畨鐏紝璇穢x鍒嗛挓鍐呭湪PDA涓婅繘琛屽搷搴斿鐞嗭紒锛侊紒
+//            return String.format("銆愬伐鑹哄畨鐏�慭n%s浜х嚎鍙戣捣浜嗗伐鑹哄畨鐏紝璇�%d鍒嗛挓鍐呭湪PDA涓婅繘琛屽搷搴斿鐞嗭紒锛侊紒",
+//                    factoryName,
+//                    andonOrde.getUpgradeResponseDuration());
+//        } else {
+//            // 浜�/涓夌骇妯℃澘锛氥�愬伐鑹哄畨鐏�慩XX锛堜骇绾匡級鍙戣捣鐨勫伐鑹哄畨鐏紝xxx锛堜笂涓�绾у搷搴斾汉锛夋湭鍦▁x鍒嗛挓鍐呭搷搴旓紝璇峰敖蹇潱淇冭繘琛屽搷搴斿鐞嗭紒锛侊紒
+//            String prevLevelDesc = getLevelDesc(level - 1);
+//            String prevResponder = getResponderName(andonOrde, level - 1);
+//            int prevDuration = getResponseDuration(andonOrde, level - 1);
+//
+//            return String.format("銆愬伐鑹哄畨鐏�慭n%s锛堜骇绾匡級鍙戣捣鐨勫伐鑹哄畨鐏紝%s锛�%s鍝嶅簲浜猴級鏈湪%d鍒嗛挓鍐呭搷搴旓紝璇峰敖蹇潱淇冭繘琛屽搷搴斿鐞嗭紒锛侊紒",
+//                    factoryName,
+//                    prevResponder,
+//                    prevLevelDesc,
+//                    prevDuration);
+//        }
+//    }
+
+    /**
+     * 楠岃瘉閫氱煡鐩稿叧鍙傛暟
+     */
+    private boolean validateNotificationParams(AndonButtonDTO andonOrde) {
+        if (andonOrde == null) {
+            log.error("瀹夌伅璁㈠崟淇℃伅涓虹┖");
+            return false;
+        }
+        if (andonOrde.getId() == null) {
+            log.error("瀹夌伅ID涓虹┖");
+            return false;
+        }
+        // 涓�绾у搷搴旀牎楠岋紙蹇呴�夛級
+        if (andonOrde.getResponderOpenId() == null || andonOrde.getResponderOpenId().isEmpty()
+                || andonOrde.getUpgradeResponseDuration() == null || andonOrde.getUpgradeResponseDuration() <= 0) {
+            log.error("涓�绾у搷搴斾汉淇℃伅鏃犳晥锛坥penId鎴栨椂闀跨己澶憋級锛屽畨鐏疘D: {}", andonOrde.getId());
+            return false;
+        }
+        // 浜岀骇鍝嶅簲鏍¢獙锛堣嫢瀛樺湪鍒欏繀閫夊畬鏁达級
+        if (andonOrde.getSecondResponderOpenId() != null && !andonOrde.getSecondResponderOpenId().isEmpty()) {
+            if (andonOrde.getSecondUpgradeResponseDuration() == null || andonOrde.getSecondUpgradeResponseDuration() <= 0) {
+                log.error("浜岀骇鍝嶅簲浜哄瓨鍦ㄤ絾鏃堕暱鏃犳晥锛屽畨鐏疘D: {}", andonOrde.getId());
+                return false;
+            }
+        }
+        // 涓夌骇鍝嶅簲鏍¢獙锛堣嫢瀛樺湪鍒欏繀閫夊畬鏁达級
+        if (andonOrde.getThirdResponderOpenId() != null && !andonOrde.getThirdResponderOpenId().isEmpty()) {
+            if (andonOrde.getSecondUpgradeResponseDuration() == null || andonOrde.getSecondUpgradeResponseDuration() <= 0) {
+                log.error("涓夌骇鍝嶅簲浜哄瓨鍦ㄤ絾浜岀骇鏃堕暱鏃犳晥锛堜笁绾т緷璧栦簩绾ф椂闀匡級锛屽畨鐏疘D: {}", andonOrde.getId());
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // 宸ュ叿鏂规硶锛氳幏鍙栫骇鍒弿杩�
+    private String getLevelDesc(int level) {
+        switch (level) {
+            case 1: return "涓�绾�";
+            case 2: return "浜岀骇";
+            case 3: return "涓夌骇";
+            default: return "鏈煡绾у埆";
+        }
+    }
+
+    // 宸ュ叿鏂规硶锛氳幏鍙栧搷搴斾汉openId
+    private String getResponderOpenId(AndonButtonDTO andonOrde, int level) {
+        switch (level) {
+            case 1: return andonOrde.getResponderOpenId();
+            case 2: return andonOrde.getSecondResponderOpenId();
+            case 3: return andonOrde.getThirdResponderOpenId();
+            default: return null;
+        }
+    }
+
+    // 宸ュ叿鏂规硶锛氳幏鍙栧搷搴斾汉鍚嶇О
+    private String getResponderName(AndonButtonDTO andonOrde, int level) {
+        String name = null;
+        switch (level) {
+            case 1:
+                name = andonOrde.getResponder();
+                break;
+            case 2:
+                name = andonOrde.getSecondResponder();
+                break;
+            case 3:
+                name = andonOrde.getThirdResponder();
+                break;
+            default:
+                name = null;
+        }
+        return name != null ? name : "鏈煡鍝嶅簲浜�";
+    }
+
+    // 宸ュ叿鏂规硶锛氳幏鍙栧搷搴旀椂闀匡紙涓�绾�=浜岀骇寤惰繜锛屼簩绾�=涓夌骇寤惰繜锛�
+    private int getResponseDuration(AndonButtonDTO andonOrde, int level) {
+        int duration = 0;
+        switch (level) {
+            case 1:
+                duration = andonOrde.getUpgradeResponseDuration() != null ? andonOrde.getUpgradeResponseDuration() : 0;
+                break;
+            case 2:
+                duration = andonOrde.getSecondUpgradeResponseDuration() != null ? andonOrde.getSecondUpgradeResponseDuration() : 0;
+                break;
+            case 3:
+                duration = 0; // 涓夌骇鏄渶鍚庝竴绾э紝鏃犻渶寤惰繜
+                break;
+            default:
+                duration = 0;
+        }
+        return duration;
+    }
+
+    // 宸ュ叿鏂规硶锛氬垽鏂寚瀹氱骇鍒搷搴斾汉鏄惁鏈夋晥
+    private boolean hasValidResponder(AndonButtonDTO andonOrde, int level) {
+        String openId = getResponderOpenId(andonOrde, level);
+        return openId != null && !openId.isEmpty();
+    }
+
+    /**
+     * 鑾峰彇閮ㄩ棬鐢ㄦ埛鍒楄〃锛堢洿灞炲憳宸ワ級
+     */
+    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())) + "...");
+
+            String url = feishuUrl + "open-apis/contact/v3/users/find_by_department";
+            String pageToken = null;
+            int pageNumber = 1;
+
+            do {
+                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 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())) + "...");
+
+                HttpEntity<String> requestEntity = new HttpEntity<>(headers);
+                ResponseEntity<String> response = restTemplate.exchange(
+                        urlBuilder.toString(), HttpMethod.GET, requestEntity, String.class
+                );
+
+                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) {
+                        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瀽鐢ㄦ埛鏁版嵁
+     */
+    @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();
+            user.setOpenId((String) userMap.getOrDefault("open_id", ""));
+
+            String name = (String) userMap.getOrDefault("name", "");
+            if (name.contains(" ")) {
+                String[] parts = name.split(" ");
+                if (parts.length == 2) {
+                    user.setUsername(parts[0].trim());
+                    user.setRealname(parts[1].trim());
+                    user.setWorkNo(parts[0].trim());
+                } 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);
+            }
+
+            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);
+
+            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");
+            user.setOpenId((String) userMap.getOrDefault("open_id", ""));
+            userList.add(user);
+        }
+
+        return userList;
+    }
+
+    /**
+     * 鍚屾鍗曚釜椋炰功鐢ㄦ埛鍒扮郴缁�
+     */
+    private boolean syncFeishuUserToSystem(FeishuUser feishuUser) {
+        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