|
@@ -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×tamp=%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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|