新火炬后端单体项目初始化代码
cuilei
5 天以前 c71714508fbe3ace3543423c7700d7bbcca90056
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("同步飞书用户失败,用户ID: {}", feishuUser.getUserId(), e);
                }
            }
            log.info("飞书用户同步完成,总处理: {},新增: {},更新: {}", successCount, addCount, updateCount);
        } catch (Exception e) {
            log.error("同步飞书部门用户失败,部门ID: {}", 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发送请求,确保头信息被正确传递
                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("第{}页API响应: {}", 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("第{}页has_more: {}, page_token: {}", pageNumber, hasMore, pageToken);
                        pageNumber++;
                    } else {
                        log.warn("第{}页data为空", 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;
    }
    /**
     * è§£æžç”¨æˆ·æ•°æ®
     */
    /**
     * è§£æžç”¨æˆ·æ•°æ®
     */
    @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("解析用户数据失败,items不是列表类型: {}", 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();
            // è§£æžopen_id
            user.setOpenId((String) userMap.getOrDefault("open_id", ""));
            // è§£æž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());   // å·¥å·ä¹Ÿå­˜åˆ°workNo
                } 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"); // ä½ è¦æ±‚的默认密码
            // å°†open_id存到对应的字段(根据你的用户表结构,假设用户表有open_id字段)
            user.setOpenId((String) userMap.getOrDefault("open_id", ""));
            userList.add(user);
        }
        return userList;
    }
    /**
     * åŒæ­¥å•个飞书用户到系统
     * @param feishuUser é£žä¹¦ç”¨æˆ·
     * @return true表示更新,false表示新增
     *
     * åŽæœŸå¢žåŠ ï¼š ç”¨æˆ·åŒæ­¥å¯¹æ¯”,飞书离职员工需要在本地用户表里面将该用户状态禁用
     */
    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;
        }
    }
}