Prechádzať zdrojové kódy

Merge branch 'master' of http://47.100.37.243:10191/wutt/manHourHousekeeper

QuYueTing 1 týždeň pred
rodič
commit
1eba94112d
15 zmenil súbory, kde vykonal 489 pridanie a 22 odobranie
  1. 42 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/MiniBindUserController.java
  2. 37 21
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WechatCallbackController.java
  3. 4 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Company.java
  4. 66 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/MiniBindUser.java
  5. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/WechatQrcodeScan.java
  6. 16 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/MiniBindUserMapper.java
  7. 16 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/MiniBindUserService.java
  8. 20 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/MiniBindUserServiceImpl.java
  9. 16 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WechatApiService.java
  10. 19 0
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/MiniBindUserMapper.xml
  11. 52 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/DingTalkController.java
  12. 14 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/DingTalkConfig.java
  13. 12 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/DingTalkUserInfo.java
  14. 19 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/AuthService.java
  15. 153 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingTalkService.java

+ 42 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/MiniBindUserController.java

@@ -0,0 +1,42 @@
+package com.management.platform.controller;
+
+
+import com.management.platform.entity.MiniBindUser;
+import com.management.platform.service.MiniBindUserService;
+import com.management.platform.util.HttpRespMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-05-09
+ */
+@RestController
+@Slf4j
+@RequestMapping("/mini-bind-user")
+public class MiniBindUserController {
+
+    @Resource
+    private MiniBindUserService miniBindUserService;
+
+    @RequestMapping("/save")
+    private HttpRespMsg saveMiniBindUser(MiniBindUser miniBindUser) {
+        HttpRespMsg msg = new HttpRespMsg();
+        boolean save = miniBindUserService.save(miniBindUser);
+        if(save) {
+            return msg;
+        }else {
+            msg.setError("保存失败");
+            return msg;
+        }
+    }
+
+}
+

+ 37 - 21
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WechatCallbackController.java

@@ -1,13 +1,11 @@
 package com.management.platform.controller;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.management.platform.entity.Custom;
-import com.management.platform.entity.User;
-import com.management.platform.entity.WechatQrcodeScan;
-import com.management.platform.entity.WechatUserFollow;
+import com.management.platform.entity.*;
 import com.management.platform.mapper.WechatQrcodeScanMapper;
 import com.management.platform.mapper.WechatUserFollowMapper;
 import com.management.platform.service.CustomService;
+import com.management.platform.service.MiniBindUserService;
 import com.management.platform.service.UserService;
 import com.management.platform.service.impl.WechatApiService;
 import lombok.extern.slf4j.Slf4j;
@@ -26,6 +24,7 @@ import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 
 @RestController
@@ -51,6 +50,9 @@ public class WechatCallbackController {
     @Resource
     private CustomService customService;
 
+    @Resource
+    private MiniBindUserService miniBindUserService;
+
     // 微信配置验证接口(GET请求)
     @GetMapping("/callback")
     public String validate(@RequestParam("signature") String signature,
@@ -90,9 +92,9 @@ public class WechatCallbackController {
             
             String event = root.elementText("Event");
             String openId = root.elementText("FromUserName");
-            String eventKey = root.elementText("EventKey");
+            String eventKey = StringUtils.isEmpty(root.elementText("EventKey")) ? "" : root.elementText("EventKey");
             log.info("场景值ID==>"+(StringUtils.isEmpty(eventKey)?"空":eventKey));
-            String ticket = root.elementText("Ticket");
+            String ticket = StringUtils.isEmpty(root.elementText("Ticket"))?"":root.elementText("Ticket");
             log.info("ticket==>"+(StringUtils.isEmpty(ticket)?"空":ticket));
             String createTime = root.elementText("CreateTime");
             
@@ -118,13 +120,16 @@ public class WechatCallbackController {
     private void handleSubscribeOrScan(String event, String openId, String eventKey, 
                                      String ticket, String createTime, 
                                      HttpServletRequest request) {
-        // 提取销售人员ID
-        String salesmanId = eventKey.startsWith("qrscene_") ? 
-            eventKey.substring(8) : eventKey;
-        
+        String salesmanId="";
+        if (StringUtils.isNotEmpty(eventKey)&&StringUtils.isNotEmpty(ticket)) {
+            // 提取销售人员ID
+            salesmanId = eventKey.startsWith("qrscene_") ?
+                    eventKey.substring(8) : eventKey;
+        }
+
         // 获取用户IP
         String ipAddress = getClientIp(request);
-        
+
         // 获取用户信息
         Map<String, Object> userInfo = wechatApiService.getUserInfo(openId,salesmanId);//用户的openId,员工的userId
         
@@ -145,6 +150,7 @@ public class WechatCallbackController {
             scanRecord.setProvince((String) userInfo.get("province"));
             scanRecord.setCountry((String) userInfo.get("country"));
             scanRecord.setAvatarUrl((String) userInfo.get("headimgurl"));
+            scanRecord.setUnionid((String) userInfo.get("unionid"));
         }
         
         scanMapper.insert(scanRecord);
@@ -161,17 +167,27 @@ public class WechatCallbackController {
                 followRecord.setSalesmanId(salesmanId);
                 followMapper.insert(followRecord);
 
-                Custom serviceOne = customService.getOne(new QueryWrapper<Custom>().eq("plate4", openId));
-                if(serviceOne==null) {
-                    Custom custom = new Custom();
-                    custom.setCustomName(openId);//用户的openId
-                    custom.setIsDelete(0);
-                    custom.setInchargerId(salesmanId);
-                    if (StringUtils.isNotEmpty(salesmanId)) {
-                        User user = userService.getById(salesmanId);
-                        custom.setCompanyId(user != null ? user.getCompanyId() : null);
+                List<MiniBindUser> miniBindUserList = miniBindUserService.list(new QueryWrapper<MiniBindUser>()
+                        .eq("unionid", scanRecord.getUnionid())
+                        .orderByDesc("create_time")
+                        .last("limit 1"));
+
+                if (!miniBindUserList.isEmpty()){
+                    int count = customService.count(new QueryWrapper<Custom>().eq("custom_name", openId));
+                    if(count==0) {
+                        Custom custom = new Custom();
+                        custom.setCustomName(openId);//用户的openId
+                        custom.setIsDelete(0);
+                        custom.setInchargerId(miniBindUserList.get(0).getUserId());
+                        if (StringUtils.isNotEmpty(miniBindUserList.get(0).getUserId())) {
+                            User user = userService.getById(miniBindUserList.get(0).getUserId());
+                            custom.setCompanyId(user != null ? user.getCompanyId() : null);
+                            customService.save(custom);
+                            log.info("新增客户成功");
+                        }
+                    }else {
+                        log.info("已存在custom_name为"+openId+"的客户");
                     }
-                    customService.save(custom);
                 }
                 log.info("用户新关注成功");
             } else {

+ 4 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Company.java

@@ -4,9 +4,11 @@ import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
 import java.time.LocalDateTime;
@@ -47,6 +49,8 @@ public class Company extends Model<Company> {
     /**
      * 会员到期时间
      */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
     @TableField("expiration_date")
     private LocalDateTime expirationDate;
 

+ 66 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/MiniBindUser.java

@@ -0,0 +1,66 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-05-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class MiniBindUser extends Model<MiniBindUser> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 小程序的openId
+     */
+    @TableField("mini_open_id")
+    private String miniOpenId;
+
+    /**
+     * 微信平台的unionid
+     */
+    @TableField("unionid")
+    private String unionid;
+
+    /**
+     * 销售人员userId
+     */
+    @TableField("user_id")
+    private String userId;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 3 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/WechatQrcodeScan.java

@@ -119,6 +119,9 @@ public class WechatQrcodeScan extends Model<WechatQrcodeScan> {
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime createTime;
 
+    @TableField(exist = false)
+    private String unionid;
+
 
     @Override
     protected Serializable pkVal() {

+ 16 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/MiniBindUserMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.MiniBindUser;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-05-09
+ */
+public interface MiniBindUserMapper extends BaseMapper<MiniBindUser> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/MiniBindUserService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.MiniBindUser;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-05-09
+ */
+public interface MiniBindUserService extends IService<MiniBindUser> {
+
+}

+ 20 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/MiniBindUserServiceImpl.java

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.MiniBindUser;
+import com.management.platform.mapper.MiniBindUserMapper;
+import com.management.platform.service.MiniBindUserService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-05-09
+ */
+@Service
+public class MiniBindUserServiceImpl extends ServiceImpl<MiniBindUserMapper, MiniBindUser> implements MiniBindUserService {
+
+}

+ 16 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WechatApiService.java

@@ -8,6 +8,7 @@ import com.management.platform.service.CompanyService;
 import com.management.platform.service.UserService;
 import com.management.platform.service.WechatAccountService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
@@ -33,7 +34,12 @@ public class WechatApiService {
     private final ObjectMapper objectMapper = new ObjectMapper();
     
     public Map<String, Object> getUserInfo(String openId,String userId) {
-        String accessToken = getAccessToken(userId);
+        String accessToken=null;
+        if (StringUtils.isNotEmpty(userId)){
+            accessToken= getAccessToken(userId);
+        }else {
+            accessToken= getAccessToken();
+        }
         if (accessToken == null) {
             return null;
         }
@@ -68,4 +74,13 @@ public class WechatApiService {
             return wechatAccountService.getAccessToken(user.getCompanyId(), wechatAccount.getAppId());
         }
     }
+
+    private String getAccessToken() {
+        WechatAccount wechatAccount = wechatAccountService.list().get(0);
+        if (wechatAccount==null){
+            log.info("该公司没有配置公众号相关的参数");
+            return null;
+        }
+        return wechatAccountService.getAccessToken(wechatAccount.getCompanyId(), wechatAccount.getAppId());
+    }
 }

+ 19 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/MiniBindUserMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.MiniBindUserMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.MiniBindUser">
+        <id column="id" property="id" />
+        <result column="mini_open_id" property="miniOpenId" />
+        <result column="unionid" property="unionid" />
+        <result column="user_id" property="userId" />
+        <result column="create_time" property="createTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, mini_open_id, unionid, user_id, create_time
+    </sql>
+
+</mapper>

+ 52 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/DingTalkController.java

@@ -0,0 +1,52 @@
+package com.management.platform.controller;
+
+import com.management.platform.entity.DingTalkConfig;
+
+import com.management.platform.entity.DingTalkUserInfo;
+import com.management.platform.service.impl.AuthService;
+import com.management.platform.service.impl.DingTalkService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+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 javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@RestController
+@RequestMapping("/dingtalk")
+public class DingTalkController {
+    
+    private final DingTalkConfig dingTalkConfig;
+    private final DingTalkService dingTalkService;
+    private final AuthService authService;
+
+    @Autowired
+    public DingTalkController(DingTalkConfig dingTalkConfig, 
+                           DingTalkService dingTalkService,
+                           AuthService authService) {
+        this.dingTalkConfig = dingTalkConfig;
+        this.dingTalkService = dingTalkService;
+        this.authService = authService;
+    }
+
+    /**
+     * 钉钉回调接口
+     */
+    @GetMapping("/callback")
+    public ResponseEntity<?> callback(@RequestParam("code") String code,
+                                      @RequestParam("state") String state,
+                                      HttpServletResponse response) throws IOException {
+        // 1. 用code换取用户信息
+        DingTalkUserInfo userInfo = dingTalkService.getUserInfoByCode(code);
+        
+        // 2. 业务系统登录逻辑
+        String token = "";
+        
+        // 3. 重定向到前端并携带token
+        response.sendRedirect(dingTalkConfig.getRedirectUri() + "?token=" + token);
+        return ResponseEntity.ok().build();
+    }
+}

+ 14 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/DingTalkConfig.java

@@ -0,0 +1,14 @@
+package com.management.platform.entity;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "dingtalk")
+public class DingTalkConfig {
+    private String appId;
+    private String appSecret;
+    private String redirectUri;
+}

+ 12 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/DingTalkUserInfo.java

@@ -0,0 +1,12 @@
+package com.management.platform.entity;
+
+import lombok.Data;
+
+@Data
+public class DingTalkUserInfo {
+    private String unionId;    // 用户唯一标识
+    private String userId;     // 用户ID
+    private String name;       // 用户姓名
+    private String avatar;     // 头像URL
+    private String mobile;     // 手机号
+}

+ 19 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/AuthService.java

@@ -0,0 +1,19 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.DingTalkUserInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AuthService {
+    
+
+    /**
+     * 钉钉登录处理
+     */
+    public String dingTalkLogin(DingTalkUserInfo dingTalkUserInfo) {
+        return "";
+    }
+    
+
+}

+ 153 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingTalkService.java

@@ -0,0 +1,153 @@
+package com.management.platform.service.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.management.platform.entity.DingTalkConfig;
+import com.management.platform.entity.DingTalkUserInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class DingTalkService {
+    
+    private final DingTalkConfig dingTalkConfig;
+    private final RestTemplate restTemplate;
+    
+    private static final String GET_USER_INFO_URL = 
+        "https://oapi.dingtalk.com/sns/getuserinfo_bycode";
+    private static final String GET_USER_DETAIL_URL = 
+        "https://oapi.dingtalk.com/topapi/v2/user/getuserinfo";
+
+    @Autowired
+    public DingTalkService(DingTalkConfig dingTalkConfig, RestTemplate restTemplate) {
+        this.dingTalkConfig = dingTalkConfig;
+        this.restTemplate = restTemplate;
+    }
+
+    /**
+     * 使用临时授权码获取用户信息
+     */
+    public DingTalkUserInfo getUserInfoByCode(String code) {
+        // 1. 构造请求参数
+        String timestamp = String.valueOf(System.currentTimeMillis());
+        String signature = generateSignature(timestamp);
+        
+        // 2. 构造请求体
+        Map<String, String> requestBody = new HashMap<>();
+        requestBody.put("tmp_auth_code", code);
+        
+        // 3. 构造请求URL
+        String url = String.format("%s?accessKey=%s&timestamp=%s&signature=%s",
+                GET_USER_INFO_URL, dingTalkConfig.getAppId(), timestamp, signature);
+        
+        // 4. 发送请求
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                url, 
+                new HttpEntity<>(requestBody, buildHeaders()),
+                String.class);
+        
+        // 5. 解析响应
+        JsonNode jsonNode = parseResponse(response.getBody());
+        JsonNode userInfo = jsonNode.get("user_info");
+        
+        // 6. 获取用户详细信息
+        String unionId = userInfo.get("unionid").asText();
+        return getUserDetail(unionId);
+    }
+    
+    /**
+     * 获取用户详细信息
+     */
+    private DingTalkUserInfo getUserDetail(String unionId) {
+        // 1. 获取access_token
+        String accessToken = getAccessToken();
+        
+        // 2. 构造请求体
+        Map<String, Object> requestBody = new HashMap<>();
+        requestBody.put("unionid", unionId);
+        
+        // 3. 发送请求
+        ResponseEntity<String> response = restTemplate.postForEntity(
+                GET_USER_DETAIL_URL + "?access_token=" + accessToken,
+                new HttpEntity<>(requestBody, buildHeaders()),
+                String.class);
+        
+        // 4. 解析响应
+        JsonNode jsonNode = parseResponse(response.getBody());
+        JsonNode result = jsonNode.get("result");
+        
+        DingTalkUserInfo userInfo = new DingTalkUserInfo();
+        userInfo.setUnionId(unionId);
+        userInfo.setUserId(result.get("userid").asText());
+        userInfo.setName(result.get("name").asText());
+        userInfo.setAvatar(result.get("avatar").asText());
+        userInfo.setMobile(result.get("mobile").asText());
+        
+        return userInfo;
+    }
+    
+    /**
+     * 获取access_token
+     */
+    private String getAccessToken() {
+        String url = String.format(
+                "https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s",
+                dingTalkConfig.getAppId(), dingTalkConfig.getAppSecret());
+        
+        JsonNode jsonNode = parseResponse(restTemplate.getForObject(url, String.class));
+        return jsonNode.get("access_token").asText();
+    }
+    
+    /**
+     * 生成签名
+     */
+    private String generateSignature(String timestamp) {
+        try {
+            String stringToSign = timestamp + "\n" + dingTalkConfig.getAppSecret();
+            Mac mac = Mac.getInstance("HmacSHA256");
+            mac.init(new SecretKeySpec(dingTalkConfig.getAppSecret().getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
+            byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
+            return URLEncoder.encode(Base64.getEncoder().encodeToString(signData), "UTF-8");
+        } catch (Exception e) {
+            throw new RuntimeException("生成签名失败", e);
+        }
+    }
+    
+    /**
+     * 构建请求头
+     */
+    private HttpHeaders buildHeaders() {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return headers;
+    }
+    
+    /**
+     * 解析响应
+     */
+    private JsonNode parseResponse(String response) {
+        try {
+            JsonNode jsonNode = new ObjectMapper().readTree(response);
+            if (jsonNode.has("errcode") && jsonNode.get("errcode").asInt() != 0) {
+                throw new RuntimeException("钉钉接口错误: " + jsonNode.get("errmsg").asText());
+            }
+            return jsonNode;
+        } catch (IOException e) {
+            throw new RuntimeException("解析响应失败", e);
+        }
+    }
+}