Prechádzať zdrojové kódy

新增钉钉机器人对接

tsurumure 10 mesiacov pred
rodič
commit
a7cd3aeccf

+ 1 - 0
src/main/java/com/backendsys/exception/GlobalExceptionHandler.java

@@ -95,6 +95,7 @@ public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {
     }
 
     // -- 自定义异常输出结构 (警告) ----------------------------------------------------
+    // https://oapi.dingtalk.com/robot/send?access_token=6c677738c71a91ad096499784aa6bd0bb19ed99c2be99d31552ae3bb8fa7d5d5
     private static void printWarnException(Exception e) {
         printRequestInfo();
         log.warn(e.getClass().getName() + ": " + e.getMessage());

+ 15 - 0
src/main/java/com/backendsys/modules/log/controller/LogStreamController.java

@@ -4,6 +4,8 @@ import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.StrUtil;
 import com.backendsys.exception.CustomException;
 import com.backendsys.modules.common.config.security.annotations.Anonymous;
+import com.backendsys.modules.common.utils.Result;
+import com.backendsys.modules.log.utils.DingTalkPushUtil;
 import com.backendsys.modules.sse.emitter.SseEmitterManager;
 import com.backendsys.modules.sse.utils.SseEmitterUTF8;
 import com.backendsys.modules.sse.utils.SseUtil;
@@ -44,6 +46,19 @@ public class LogStreamController {
     @Value("${log-stream.sign}")
     private String signValue;
 
+    @Autowired
+    private DingTalkPushUtil dingTalkPushUtil;
+
+    @Anonymous
+    @GetMapping("/api/test/sendDingMsg")
+    public Result sendDingMsg() {
+//        DingTalkPushUtil.testSendTextMsg();
+//        DingTalkPushUtil.testSendLinkMsg();
+        dingTalkPushUtil.testSendLinkMsg();
+        return Result.success();
+    }
+
+
     /**
      * [SSE] 消息监听
      */

+ 199 - 0
src/main/java/com/backendsys/modules/log/utils/DingTalkPushUtil.java

@@ -0,0 +1,199 @@
+package com.backendsys.modules.log.utils;
+
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.StandardEnvironment;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 钉钉群机器人
+ * https://open.dingtalk.com/document/orgapp/robot-application-overview
+ * https://blog.csdn.net/weixin_44996457/article/details/128565224
+ */
+@Slf4j
+@Component
+public class DingTalkPushUtil {
+
+    @Value("${ding-talk.enable}")
+    private Boolean isEnable;
+    @Value("${ding-talk.secret}")
+    private String secret;
+    @Value("${ding-talk.webhook}")
+    private String webhook;
+
+//    private static final String secret = "SECf936313d718270626986fea3f1a6bfe2640f3cb653f45aa12ad68e7ddfd8c945";
+//    private static final String webhook = "https://oapi.dingtalk.com/robot/send?access_token=6c677738c71a91ad096499784aa6bd0bb19ed99c2be99d31552ae3bb8fa7d5d5";
+
+    /**
+     * 文本消息
+     */
+    public void testSendTextMsg() {
+        try {
+            sendTextMsg("我就是我, 是不一样的烟火", Lists.newArrayList(), Lists.newLinkedList(), false);
+        } catch (Exception e) {
+            System.out.println("钉钉群消息发送异常,异常原因:");
+            System.out.println(e.getMessage());
+        }
+    }
+
+    /**
+     * link类型
+     */
+    public void testSendLinkMsg() {
+        try {
+            sendLinkMsg("时代的火车向前开",
+            "这个即将发布的新版本,创始人xx称它为红树林。而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是红树林",
+            "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI",
+            "");
+        } catch (Exception e) {
+            System.out.println("钉钉群消息发送异常,异常原因:");
+            System.out.println(e.getMessage());
+        }
+    }
+
+
+
+
+    /**
+     * 群里面发送消息
+     *
+     * @param content    消息内容
+     * @param isAtAll    是否@所有人
+     * @param mobileList 被@人的手机号
+     * @param userIdList 被@人的用户userid
+     * @throws Exception
+     */
+    public void sendTextMsg(String content, List<String> mobileList, List<String> userIdList, boolean isAtAll) throws Exception {
+        if (!isEnable) return;
+        String dingUrl = getDingUrl();
+        // 组装请求内容
+        String reqStr = buildReqTextStr(content, isAtAll, mobileList, userIdList);
+        // 推送消息(http请求)
+        String result = HttpUtil.post(dingUrl, reqStr);
+        handleErrorCode(result);
+        System.out.println();
+        System.out.println("钉钉请求发送成功,返回结果:");
+        System.out.println(result);
+    }
+
+    /**
+     * 群里面发送消息
+     *
+     * @param title      消息标题
+     * @param messageUrl 点击消息跳转的URL
+     * @param picUrl     图片URL
+     * @param text       消息内容
+     * @throws Exception
+     */
+    public void sendLinkMsg(String title, String text, String messageUrl, String picUrl) throws Exception {
+        if (!isEnable) return;
+        String dingUrl = getDingUrl();
+        // 组装请求内容
+        String reqStr = buildReqLinkStr(title, text, messageUrl, picUrl);
+        // 推送消息(http请求)
+        String result = HttpUtil.post(dingUrl, reqStr);
+        handleErrorCode(result);
+        System.out.println("钉钉请求发送成功,返回结果:");
+        System.out.println(result);
+    }
+
+    /**
+     * 组装请求报文-link类型
+     *
+     * @param title      消息标题
+     * @param text       消息内容
+     * @param messageUrl 点击消息跳转的URL
+     * @param picUrl     图片URL
+     * @return
+     */
+    private String buildReqLinkStr(String title, String text, String messageUrl, String picUrl) {
+        Map<String, String> linkMap = Maps.newHashMap();
+
+        linkMap.put("text", text);
+        linkMap.put("title", title);
+        linkMap.put("picUrl", picUrl);
+        linkMap.put("messageUrl", messageUrl);
+
+        Map<String, Object> reqMap = Maps.newHashMap();
+        reqMap.put("msgtype", "link");
+        reqMap.put("link", linkMap);
+
+        return JSONObject.toJSONString(reqMap);
+    }
+
+    /**
+     * 获取请求url
+     * @return
+     */
+    private String getDingUrl() throws Exception {
+        // 获取系统时间戳
+        Long timestamp = System.currentTimeMillis();
+        // 拼接
+        String stringToSign = timestamp + "\n" + secret;
+        // 使用HmacSHA256算法计算签名
+        Mac mac = Mac.getInstance("HmacSHA256");
+        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
+        byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
+        // 进行Base64 encode 得到最后的sign,可以拼接进url里
+        String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");//Base64.encodeBase64(signData)
+        // 钉钉机器人地址(配置机器人的webhook),为了让每次请求不同,避免钉钉拦截,加上时间戳
+        String dingUrl = webhook + "&timestamp=" + timestamp + "&sign=" + sign;
+        return dingUrl;
+    }
+
+    /**
+     * 组装请求报文-text类型
+     *
+     * @param content    消息内容
+     * @param isAtAll    是否@所有人
+     * @param mobileList 被@人的手机号
+     * @param atUserIds  被@人的用户userid
+     * @return
+     */
+    private String buildReqTextStr(String content, boolean isAtAll, List<String> mobileList, List<String> atUserIds) {
+        Map<String, String> contentMap = Maps.newHashMap();
+        contentMap.put("content", content);
+
+        Map<String, Object> atMap = Maps.newHashMap();
+        atMap.put("isAtAll", isAtAll);
+        atMap.put("atMobiles", mobileList);
+        atMap.put("atUserIds", atUserIds);
+
+        Map<String, Object> reqMap = Maps.newHashMap();
+        reqMap.put("msgtype", "text");
+        reqMap.put("text", contentMap);
+        reqMap.put("at", atMap);
+
+        return JSONObject.toJSONString(reqMap);
+    }
+
+    /**
+     * errcode处理
+     * @param resultStr
+     */
+    private void handleErrorCode(String resultStr) {
+        if (StringUtils.isEmpty(resultStr)) {
+            throw new RuntimeException("返回结果为空");
+        }
+        JSONObject jsonObject = JSONObject.parseObject(resultStr);
+        if (310000 == jsonObject.getLong("errcode")) {
+            throw new RuntimeException("keywords not in content");
+        }
+    }
+
+}

+ 6 - 1
src/main/resources/application-local.yml

@@ -12,11 +12,16 @@ HTTP_BASE_STATIC: http://127.0.0.1:48080
 HTTP_ACTUATOR_URI: https://jsonplaceholder.typicode.com
 
 log-stream:
-  sign: 97e3ef8e-a8b9-46f3-b63a-f0504154efb7
   enable: true
+  sign: 97e3ef8e-a8b9-46f3-b63a-f0504154efb7
   charset: UTF-8 # GBK #
   exec: 'powershell -Command "Get-Content -Path D:\CodeJava\QuickLaunchSpring\BackendSys\logs\backendsys.log -Wait"'
 
+ding-talk:
+  enable: true
+  secret: SECf936313d718270626986fea3f1a6bfe2640f3cb653f45aa12ad68e7ddfd8c945
+  webhook: 'https://oapi.dingtalk.com/robot/send?access_token=6c677738c71a91ad096499784aa6bd0bb19ed99c2be99d31552ae3bb8fa7d5d5'
+
 spring:
   config:
     name: application-local

+ 5 - 1
src/main/resources/application-prod.yml

@@ -12,11 +12,15 @@ HTTP_BASE_STATIC: http://ai.api.daoguyujia.com
 HTTP_ACTUATOR_URI: http://43.128.1.201:48080
 
 log-stream:
-  sign: 97e3ef8e-a8b9-46f3-b63a-f0504154efb7
   enable: true
+  sign: 97e3ef8e-a8b9-46f3-b63a-f0504154efb7
   charset: UTF-8
   exec: 'tail -f /logs/backendsys.log'
 
+ding-talk:
+  secret: SECf936313d718270626986fea3f1a6bfe2640f3cb653f45aa12ad68e7ddfd8c945
+  webhook: 'https://oapi.dingtalk.com/robot/send?access_token=6c677738c71a91ad096499784aa6bd0bb19ed99c2be99d31552ae3bb8fa7d5d5'
+
 spring:
   config:
     name: application-prod

+ 0 - 2
src/main/resources/logback.xml

@@ -50,6 +50,4 @@
         <appender-ref ref="STDOUT"/>
     </root>
 
-
-
 </configuration>