spring boot 整合钉钉机器人发送消息通知

5 篇文章 0 订阅
4 篇文章 0 订阅

钉钉消息通知

主要用于系统预警、资源预警、重要消息通知,随时随地可以掌握重要信息

一、通知效果

1.文本通知

在这里插入图片描述

在这里插入图片描述

2.带链接的通知

在这里插入图片描述

在这里插入图片描述

3.makrdown格式 通知

在这里插入图片描述

在这里插入图片描述

4.ActionCard 通知

在这里插入图片描述

在这里插入图片描述

5.Feedcard 消息通知

在这里插入图片描述

在这里插入图片描述

二、使用方法

1.新建一个消息通知群,用于消息通知

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
复制这个加签的字符串,后面会用到

在这里插入图片描述
记住这个webhook,反而也会用到

2. 新建 spring boot 工程

在这里插入图片描述

2.1 POM 文件

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
    </dependencies>

2.2 配置文件 application.yml

#集成钉钉服务
ding:
  robot:
    # webhook
    webhook: ### webhook ###
    # 加签
    key: ### 加签 ###

2.3 消息模型 SendRequestParam

package com.billow.ding;

import lombok.Data;

import java.io.Serializable;
import java.util.List;

@Data
public class SendRequestParam implements Serializable
{
    private SendRequestParam.Actioncard actionCard;
    private SendRequestParam.At at;
    private SendRequestParam.Feedcard feedCard;
    private SendRequestParam.Link link;
    private SendRequestParam.Markdown markdown;
    private SendRequestParam.Text text;
    private String msgtype;

    @Data
    public static class Text implements Serializable
    {
        private static final long serialVersionUID = 2112411828946494293L;

        private String content;
    }

    @Data
    public static class Link implements Serializable
    {
        private static final long serialVersionUID = 7833398226941254374L;
        private String messageUrl;
        private String picUrl;
        private String text;
        private String title;
    }

    @Data
    public static class Markdown implements Serializable
    {
        private static final long serialVersionUID = 4697553615692276546L;
        private String text;
        private String title;
    }

    @Data
    public static class At implements Serializable
    {
        private static final long serialVersionUID = 6328897894299426499L;
        private List<String> atMobiles;
        private List<String> atUserIds;
        private Boolean isAtAll;
    }


    @Data
    public static class Actioncard implements Serializable
    {
        private static final long serialVersionUID = 1553944892187227581L;
        private String btnOrientation;
        private List<SendRequestParam.Btns> btns;
        private String hideAvatar;
        private String singleTitle;
        private String singleURL;
        private String text;
        private String title;
    }

    @Data
    public static class Feedcard implements Serializable
    {
        private static final long serialVersionUID = 6578287939968676945L;
        private List<SendRequestParam.Links> links;
    }

    @Data
    public static class Btns implements Serializable
    {
        private static final long serialVersionUID = 4435422339746837645L;
        private String actionURL;
        private String title;
    }

    @Data
    public static class Links implements Serializable
    {
        private static final long serialVersionUID = 7496318843524412877L;
        private String messageURL;
        private String picURL;
        private String title;
    }
}

2.4 发送消息服务 SendDingService

package com.billow.ding;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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;

@Slf4j
@Service
public class SendDingService
{
    @Value("${ding.robot.webhook}")
    private String robotWebhook;

    @Value("${ding.robot.key}")
    private String robotKey;

    @Autowired
    private RestTemplate restTemplate;

    private final static String MSG_TYPE_TEXT = "text";
    private final static String MSG_TYPE_LINK = "link";
    private final static String MSG_TYPE_MARKDOWN = "markdown";
    private final static String MSG_ACTION_CARD = "actionCard";
    private final static String MSG_FEED_CARD = "feedCard";

    private ThreadLocal<SendRequestParam> paramLocal = new ThreadLocal<>();

    /**
     * 发送文本消息
     *
     * @param text
     * @return OapiRobotSendRequest
     * @author 千面
     * @date 2021/11/25 21:21
     */
    public SendDingService sendText(SendRequestParam.Text text)
    {
        SendRequestParam request = new SendRequestParam();
        request.setText(text);
        request.setMsgtype(MSG_TYPE_TEXT);
        paramLocal.set(request);
        return this;
    }

    /**
     * 发送带连接的消息
     *
     * @param link
     * @return OapiRobotSendRequest
     * @author 千面
     * @date 2021/11/25 21:21
     */
    public SendDingService sendLink(SendRequestParam.Link link)
    {
        SendRequestParam request = new SendRequestParam();
        request.setMsgtype(MSG_TYPE_LINK);
        request.setLink(link);
        paramLocal.set(request);
        return this;
    }

    /**
     * 发送markdown消息
     *
     * @param markdown
     * @return OapiRobotSendRequest
     * @author 千面
     * @date 2021/11/25 21:21
     */
    public SendDingService sendMarkdown(SendRequestParam.Markdown markdown)
    {
        SendRequestParam request = new SendRequestParam();
        request.setMsgtype(MSG_TYPE_MARKDOWN);
        request.setMarkdown(markdown);
        paramLocal.set(request);
        return this;
    }

    public String sendActionCard(SendRequestParam.Actioncard actioncard)
    {
        SendRequestParam request = new SendRequestParam();
        request.setMsgtype(MSG_ACTION_CARD);
        request.setActionCard(actioncard);
        paramLocal.set(request);
        return this.send();
    }


    public String sendFeedcard(SendRequestParam.Feedcard feedcard)
    {
        SendRequestParam request = new SendRequestParam();
        request.setMsgtype(MSG_FEED_CARD);
        request.setFeedCard(feedcard);
        paramLocal.set(request);
        return this.send();
    }

    /**
     * 推送钉钉机器人消息
     *
     * @return
     */
    public String send()
    {
        return this.send(null);
    }

    /**
     * 推送钉钉机器人消息
     *
     * @return
     */
    public String send(SendRequestParam.At at)
    {
        String json = null;
        String dingUrl = null;
        try
        {
            dingUrl = this.getDingUrl();
            //组装请求内容
            SendRequestParam param = paramLocal.get();
            param.setAt(at);
            json = JSON.toJSONString(param);
            return sendRequest(dingUrl, json);
        }
        catch (Exception e)
        {
            log.error("盯盯消息发送错误,接口地址:[{}],请求参数:[{}]", dingUrl, json, e);
        }
        finally
        {
            paramLocal.remove();
        }
        return null;
    }

    /**
     * 获取 钉钉机器人地址
     *
     * @param
     * @return String
     * @author 千面
     * @date 2021/11/26 13:36
     */
    private String getDingUrl()
    {
        long timestamp = System.currentTimeMillis();
        String sign = HmacSha256Util.dingHmacSHA256(System.currentTimeMillis(), robotKey);
        // 钉钉机器人地址(配置机器人的 webhook) https://oapi.dingtalk.com/robot/send?access_token=XXXXXX&timestamp=XXX&sign=XXX
        return String.format(robotWebhook + "&timestamp=%d&sign=%s", timestamp, sign);
    }

    private String sendRequest(String url, String params)
    {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> httpEntity = new HttpEntity<>(params, headers);
        ResponseEntity<String> entity = restTemplate.postForEntity(url, httpEntity, String.class);
        String body = entity.getBody();
        log.info("sendRequest()>>>[{}]", body);
        return body;
    }
}

2.5 加签算法 HmacSha256Util

package com.billow.ding;

import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;

@Slf4j
public class HmacSha256Util
{
    /**
     * 钉钉自定义机器人安全设置
     * 把timestamp+"\n"+密钥当做签名字符串,使用HmacSHA256算法计算签名,然后进行Base64 encode,最后再把签名参数再进行urlEncode,得到最终的签名(需要使用UTF-8字符集)
     *
     * @param secret
     * @return
     */
    /**
     * 钉钉自定义机器人安全设置
     *
     * @param timestamp
     * @param secret
     * @return String
     * @author 千面
     * @date 2021/11/26 14:05
     */
    public static String dingHmacSHA256(long timestamp, String secret)
    {
        try
        {
            String stringToSign = timestamp + "\n" + secret;
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
            String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
            return sign;
        }
        catch (Exception e)
        {
            log.error("dingHmacSHA256加密失败", e);
        }
        return null;
    }
}

2.6 测试 TestApi

package com.billow;

import com.billow.ding.SendDingService;
import com.billow.ding.SendRequestParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class TestApi
{

    @Autowired
    private SendDingService sendDingService;

    @GetMapping("/sendText")
    public String sendText()
    {
        SendRequestParam.Text text = new SendRequestParam.Text();
        text.setContent("我就是我, @XXX 是不一样的烟火");

//        // at 指定人
//        at.setAtMobiles(Arrays.asList("185XXXXXXXX"));
//        // at 所有人
//        at.setIsAtAll(true);
//        return sendDingService.sendText(text).send(at);

        return sendDingService.sendText(text).send();
    }

    @GetMapping("/sendLink")
    public String sendLink()
    {
        SendRequestParam.Link link = new SendRequestParam.Link();
        link.setText("这个即将发布的新版本,创始人xx称它为红树林。而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是红树林");
        link.setTitle("时代的火车向前开");
        link.setPicUrl("https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png");
        link.setMessageUrl("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");
        return sendDingService.sendLink(link).send();
    }

    @GetMapping("/sendMarkdown")
    public String sendMarkdown()
    {
        SendRequestParam.Markdown markdown = new SendRequestParam.Markdown();
        markdown.setTitle("这个即将发布的新版本,创始人xx称它为红树林。而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是红树林");
        markdown.setText("#### 杭州天气 @150XXXXXXXX \n > 9度,西北风1级,空气良89,相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n");
        return sendDingService.sendMarkdown(markdown).send();
    }

    @GetMapping("/sendActionCard")
    public String sendActionCard()
    {
        SendRequestParam.Actioncard actioncard = new SendRequestParam.Actioncard();
        actioncard.setTitle("乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身");
        actioncard.setText("![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png) \n" +
                " ### 乔布斯 20 年前想打造的苹果咖啡厅 \n" +
                " Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划");
//        actioncard.setSingleTitle("阅读全文");
//        actioncard.setSingleURL("https://www.dingtalk.com/");
        // 0:按钮竖直排列 1:按钮横向排列
        actioncard.setBtnOrientation("1");
        // 添加按钮
        List<SendRequestParam.Btns> btns = new ArrayList<>();
        SendRequestParam.Btns btn1 = new SendRequestParam.Btns();
        btn1.setTitle("查看");
        btn1.setActionURL("");
        btns.add(btn1);
        SendRequestParam.Btns btn2 = new SendRequestParam.Btns();
        btn2.setTitle("取消");
        btns.add(btn2);
        btn1.setActionURL("");

        actioncard.setBtns(btns);
        return sendDingService.sendActionCard(actioncard);
    }

    @GetMapping("/sendFeedcard")
    public String sendFeedcard()
    {
        SendRequestParam.Feedcard feedcard = new SendRequestParam.Feedcard();

        List<SendRequestParam.Links> links = new ArrayList<>();
        SendRequestParam.Links links1 = new SendRequestParam.Links();
        links1.setTitle("时代的火车向前开1");
        links1.setMessageURL("https://www.dingtalk.com/");
        links1.setPicURL("https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png");
        links.add(links1);

        SendRequestParam.Links links2 = new SendRequestParam.Links();
        links2.setTitle("时代的火车向前开2");
        links2.setMessageURL("https://www.dingtalk.com/");
        links2.setPicURL("https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png");
        links.add(links2);

        feedcard.setLinks(links);

        return sendDingService.sendFeedcard(feedcard);
    }
}

盯盯官方文档

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值