SpringBoot整合钉钉消息推送(四十四)

news2024/11/26 18:38:16

从头开始,并不意味着失败,相反,正是拥抱成功的第一步,即使还会继续失败

上一章简单介绍了 SpringBoot 发送邮件(四十三), 如果没有看过,请观看上一章

钉钉消息推送, 官方文档: https://open.dingtalk.com/document/group/group-robot

开发钉钉消息推送,我们可以使用 spring-boot-dingtalk-robot-starter 第三方依赖进行快速开发.

对应的官方文档是: https://gitee.com/snowheart/dingtalk-robot

一. 钉钉群安装机器人

点击进入一个群, 选择智能群助手

image-20230516194624287

选择 添加机器人:

image-20230516194640905

点击 右边的设置

image-20230516194657049

选择 自定义

image-20230516194709770

点击添加:

image-20230516194728786

image-20230516194741074

完成之后,会生成一个相应 的 Webhook

https://oapi.dingtalk.com/robot/send?access_token=817edae9c6xxxxxxxxxxxxxxx

image-20230516194801120

二. 钉钉消息推送开发

二.一 pom.xml 添加依赖


<dependency>
    <groupId>cn.snowheart</groupId>
    <artifactId>spring-boot-dingtalk-robot-starter</artifactId>
    <version>1.0.2.RELEASE</version>
</dependency>

二.二 添加配置

application.yml 上添加配置, 只添加一个对应的 webhook 值即可。 webhook 是 群创建机器人的标识

dingtalk:
  robot:
    webhook: https://oapi.dingtalk.com/robot/send?access_token=817edae9c662dc38bb5fcb4231f74b9e1eb9f33d17f1e12b25903980f82b7c9d

二.三 功能测试

先注入客户端

@Autowired
@Qualifier("dingTalkRobotClient")
private DingTalkRobotClient client;

二.三.一 普通文本通知

@Test
 public void testSendTextMessage() throws InterruptedException {
     DingTalkResponse response = null;
 
     response = client.sendTextMessage(new TextMessage("业务报警:构建 TextMessage对象发布!"));
     Assert.assertEquals(response.getErrcode().longValue(), 0L);
     log.info(response.toString());
     Thread.sleep(3000);
 
     response = client.sendTextMessage("业务报警:构建普通字符串发布!");
     Assert.assertEquals(response.getErrcode().longValue(), 0L);
     log.info(response.toString());
     Thread.sleep(3000);
 
     response = client.sendTextMessage("业务报警:通知指定人!", new String[]{"15734078927"});
     Assert.assertEquals(response.getErrcode().longValue(), 0L);
     log.info(response.toString());
     Thread.sleep(3000);
 
     response = client.sendTextMessage("业务报警:通知群内所有人!", true);
     Assert.assertEquals(response.getErrcode().longValue(), 0L);
     log.info(response.toString());
     Thread.sleep(3000);
 }

image-20230516195031545

二.三.二 超链接文本

@Test
public void testSendLinkMessage() throws InterruptedException {
    DingTalkResponse response = null;
 
    //链接,构建 LinkMessage 对象
    response = client.sendLinkMessage(new LinkMessage("业务报警:AAA",
            "BBBBBBB",
            "CCCCC",
            "DDDD"));
    Assert.assertEquals(response.getErrcode().longValue(), 0L);
    log.info(response.toString());
    Thread.sleep(3000);
}

二.三.三 markdown 文本推送

private static final String markDownDemoText = "业务报警:标题" +
           "# 一级标题\n" +
           "## 二级标题\n" +
           "### 三级标题\n" +
           "#### 四级标题\n" +
           "##### 五级标题\n" +
           "###### 六级标题\n" ;  
 
@Test
   public void testSendMarkdownMessage() throws InterruptedException {
       DingTalkResponse response = null;
       // 构建 markdown 对象用法
       response = client.sendMarkdownMessage(new MarkdownMessage("业务报警:钉钉markdown消息支持的语法",
               markDownDemoText));
       Assert.assertEquals(response.getErrcode().longValue(), 0L);
       log.info(response.toString());
       Thread.sleep(3000);
 
       // 不构建对象
       response = client.sendMarkdownMessage("业务报警:钉钉markdown消息支持的语法",
               markDownDemoText);
       Assert.assertEquals(response.getErrcode().longValue(), 0L);
       log.info(response.toString());
       Thread.sleep(3000);
 
       // 发送给指定人
       response = client.sendMarkdownMessage("业务报警:钉钉markdown消息支持的语法",
               markDownDemoText, new String[]{"15734078927"});
       Assert.assertEquals(response.getErrcode().longValue(), 0L);
       log.info(response.toString());
       Thread.sleep(3000);
       //发送给全体人
       response = client.sendMarkdownMessage("业务报警:钉钉markdown消息支持的语法",
               markDownDemoText, true);
       Assert.assertEquals(response.getErrcode().longValue(), 0L);
       log.info(response.toString());
       Thread.sleep(3000);
   }

image-20230516195138291

二.三.四 ActionCard 业务推送

@Test
    public void testSendActionCardMessage() throws InterruptedException {
        DingTalkResponse response = null;
 
        response = client.sendActionCardMessage(new ActionCardMessage("业务报警:This is title", "![screenshot](@lADOpwk3K80C0M0FoA)\n" +
                "**Apple Store** 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划"));
        Assert.assertEquals(response.getErrcode().longValue(), 0L);
        log.info(response.toString());
        Thread.sleep(3000);
 
        response = client.sendActionCardMessage("业务报警:This is title", "![screenshot](@lADOpwk3K80C0M0FoA)\n" +
                "**Apple Store** 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划");
        Assert.assertEquals(response.getErrcode().longValue(), 0L);
        log.info(response.toString());
        Thread.sleep(3000);
 
        response = client.sendActionCardMessage("业务报警:This is title", "![screenshot](@lADOpwk3K80C0M0FoA)\n" +
                        "**Apple Store** 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划",
                HideAvatarType.HIDE);
        Assert.assertEquals(response.getErrcode().longValue(), 0L);
        log.info(response.toString());
        Thread.sleep(3000);
 
        response = client.sendActionCardMessage("业务报警:This is title", "![screenshot](@lADOpwk3K80C0M0FoA)\n" +
                        "**Apple Store** 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划",
                ActionCardButton.defaultReadButton("https://www.dingtalk.com"));
        Assert.assertEquals(response.getErrcode().longValue(), 0L);
        log.info(response.toString());
        Thread.sleep(3000);
 
        response = client.sendActionCardMessage("业务报警:This is title", "![screenshot](@lADOpwk3K80C0M0FoA)\n" +
                        "**Apple Store** 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划",
                HideAvatarType.HIDE,
                ActionCardButton.defaultReadButton("https://www.dingtalk.com"));
        Assert.assertEquals(response.getErrcode().longValue(), 0L);
        log.info(response.toString());
        Thread.sleep(3000);
 
    }

image-20230516195211514

二.三.五 FeedCard 消息推送

@Test
public void testSendFeedCardMessage() throws InterruptedException {
    DingTalkResponse response = null;
 
    ArrayList<FeedCardMessageItem> items = new ArrayList<>();
    items.add(new FeedCardMessageItem("业务报警:成为架构师的路上,看这一篇文章就足够了,因为……",
            "http://mp.weixin.qq.com/s/CPUaB60pue0Zf3fUL9xqvw",
            "https://mmbiz.qpic.cn/mmbiz_jpg/YriaiaJPb26VMtfgPvTsM9amH5hf3pmTbf40ia6OLE845icrDb0vt4AsMnTyva5mMMpwwxnkVR5UjCEI8ADvSic1qWQ/640"));
 
    items.add(new FeedCardMessageItem("业务报警:想成为一名Web开发者?你应该学习Node.js而不是PHP",
            "http://mp.weixin.qq.com/s/x8dm9e7gwLXSEzxE6sQYow",
            "https://mmbiz.qpic.cn/mmbiz_jpg/YriaiaJPb26VND0Q0hSBOoyVkr9cXQrFjWI7hOzax1IxIibqanXYD4W8nyeYX5iaicjgiaqia7ly94iawOsGwehbKGwGsA/640"));
 
    response = client.sendFeedCardMessage(new FeedCardMessage(items));
    Assert.assertEquals(response.getErrcode().longValue(), 0L);
    log.info(response.toString());
    Thread.sleep(3000);
 
    response = client.sendFeedCardMessage(items);
    Assert.assertEquals(response.getErrcode().longValue(), 0L);
    log.info(response.toString());
    Thread.sleep(3000);
}

image-20230516195240452

三. 钉钉业务消息Service 封装

也采用模板的方式进行处理

三.一 接口

public interface DingService {
	/**
	 * 发送普通的文本消息
	 *  @param toArr   发送人, 之间用 ,号分隔
	 * @param content 发送的内容, 普通文本内容
	 */
	boolean sendSimpleText(String[] toArr, String content);

	/**
	 * 发送邮件 velocity 模板邮件
	 * @param title   主题
	 * @param toArr                发送人
	 * @param dataMap              发送模板邮件填充数据
	 * @param templateName 模板名称
	 */
	boolean sendVelocityText(String title, String[] toArr, Map<String, Object> dataMap, String templateName);
}

三.二 接口实现

@Service
@Slf4j
public class DingServiceImpl implements DingService {
	@Autowired
	@Qualifier("dingTalkRobotClient")
	private DingTalkRobotClient client;

	@Value("${dingtalk.robot.webhook}")
	private String webhook;

	private VelocityEngine velocityEngine;
	

	
	@PostConstruct
	public void initVelocityEngine() {
		velocityEngine = new VelocityEngine();
		Properties p = new Properties();
		velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
		velocityEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
		// 配置资源
		// velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, "/usr/local/templates/");
		velocityEngine.init(p);
	}
	
	@Override
	public boolean sendSimpleText(String[] toArr, String content) {
		//发送信息
		try {
			TextMessage textMessage = new TextMessage(content,toArr);
			DingTalkResponse response = client.sendMessageByURL(webhook, textMessage);
			return response.getErrcode() == 0? true :false;
		} catch (Exception e) {
			log.error("componentAndSendReqeust simple mail error", e);
			return false;
		}
	}

	@Override
    public boolean sendVelocityText(String title, String[] toArr, Map<String, Object> dataMap, String templateName) {
		//发送信息
		try {
			String velocityMailText = getVelocityMailText(dataMap,templateName);
			MarkdownMessage markdownMessage = new MarkdownMessage(title,velocityMailText,toArr);
			DingTalkResponse response = client.sendMessageByURL(webhook, markdownMessage);
			return response.getErrcode() == 0? true :false;
		} catch (Exception e) {
			log.error("componentAndSendReqeust simple mail error", e);
			return false;
		}
    }

    private String getVelocityMailText(Map<String, Object> dataMap, String templateName) {
        VelocityContext velocityContext = new VelocityContext(dataMap);
        StringWriter writer = new StringWriter();
        velocityEngine.mergeTemplate(templateName, "UTF-8", velocityContext, writer);
        return writer.toString();
    }
}

三.三 服务测试

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ServiceTest {

    @Resource
    private DingService dingService;

    @Test
    public void simpleEmailTest() {
        String[] toArr = new String[]{"15734078927"};
        dingService.sendSimpleText(toArr, "钉钉发送消息");
        log.info(">>>钉钉发送消息成功");
    }

    @Test
    public void velocityTest() {
        String[] toArr = new String[]{"15734078927"};
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("line",System.lineSeparator());
        dataMap.put("title","你叫什么名称");
        dataMap.put("content","我叫岳泽霖,是一个快乐的程序员");
        dingService.sendVelocityText("md消息",toArr, dataMap,"interface_tenwhy.vm");
        log.info(">>>发送测试钉钉成功");
    }
}

本章节的代码放置在 github 上:


https://github.com/yuejianli/springboot/tree/develop/SpringBoot_Dingding


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/533836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.结构化的数学思想

前言 今天在复习概率论的公理化过程中&#xff0c;我发现它的公理其实也是人为定义的&#xff0c;为什么我会这么想呢&#xff1f;这是因为我曾听过严伯均在为什么诺贝尔奖没有数学讲曾说过数学是一门无法证伪的学科&#xff0c;甚至不能算是科学&#xff0c;而诺贝尔设置这个…

超详细的Linux环境下使用git上传代码教程(gitee版)

git是一个版本控制器&#xff0c;我们使用它上传我们以前写过的代码给他进行托管&#xff0c;更为方便以后找到&#xff0c;同时也方便我们找到我们每次更改了什么。 创建仓库 创建完成后界面&#xff1a; 接下来复制我们创建的仓库的地址&#xff1a; 使用 git 命令行 安装…

BGP路由策略,IPV6

下一跳不变 从EBGP来的路由,传给|BGP S居时,下一跳不变 解决方案: 水平分割 ∷:AS内防环 从|BGP来的路由,不会传给IBGP邻居 1全互联 2路由反射器 3联盟 BGP选路原则: 当BGP 由表存在多条相同路由,会产生多个转发路径,BGP 会根据这些路由的属性,选择一条最优…

使用 C 语言验证非均匀概率的离散事件在样本数量足够大时,符合正态分布曲线(通过生成一个PPM格式的图像)

我想写本文的原因是看到著名数学科普账号 3Blue1Brown 发布的【官方双语】但是什么是中心极限定理&#xff1f;中提到&#xff1a;不论这个离散型事件的各种情况概率是不是平均的&#xff0c;当数量一定大时&#xff0c;还是会符合正态分布曲线。我就想自己试试看是不是这种情况…

深入篇【C++】类与对象:const成员与Static成员

深入篇【C】类与对象&#xff1a;const成员与Static成员 ⏰<const成员>&#x1f553;1.权限&#x1f550;2.规则&#x1f552;3.思考&#xff1a; ⏰<Static成员>&#x1f551;1.概念&#x1f557;2.特性&#x1f555;3.思考&#xff1a; ⏰<const成员> &am…

从零开始 Spring Boot 29:类型转换

从零开始 Spring Boot 29&#xff1a;类型转换 图源&#xff1a;简书 (jianshu.com) PropertyEditor Spring使用PropertyEditor进行String和具体类型之间的转换&#xff1a; public interface PropertyEditor {void setValue(Object value);Object getValue();String getAsT…

第五章 面向对象-7.hashCode()和toString()

hashCode()和toString() hashCode() hashCoed 的特性&#xff1a; &#xff08;1&#xff09;HashCode的存在主要是用于查找的快捷性&#xff0c;如Hashtable&#xff0c;HashMap等&#xff0c;HashCode经常用于确定对象的存储地址&#xff1b; &#xff08;2&#xff09;如果…

华为OD机试真题 Java 实现【统一限载货物数最小值】【2023Q1 200分】

一、题目描述 火车站附近的货物中转站负责将到站货物运往仓库&#xff0c;小明在中转站负责调度 2K 辆中转车(K辆干货中转车&#xff0c;K 辆湿货中转车)货物由不同供货商从各地发来&#xff0c;各地的货物是依次进站&#xff0c;然后小明按照卸货顺序依次装货到中转车&#x…

智能床垫市场调研分析报告

文章目录 一、简介&#xff08;1&#xff09;电动床&#xff08;2&#xff09;气垫床 二、使用人群三、睡姿四、实用性 一、简介 &#xff08;1&#xff09;电动床 电动床之下又分成了分体、连体和床头分体。分体电动床是指床垫与床底座分开的电动床&#xff1b;连体的则是床垫…

数据结构-外部排序-(多路归并排序、败者树、置换选择排序、最佳归并树)

目录 一、外部归并排序 二、败者树 三、置换选择排序 四、最佳归并树 一、外部归并排序 16个块&#xff0c;先每个块读入内存进行排序在输出回来&#xff0c;进行16次读和16次写 两两归并&#xff0c;第一趟如下 在两两归并 时间分析 外部排序时间开销读写外存时间内存排序时…

C语言基础知识:函数的声明和使用

目录 函数的声明 1.定义顺序 2.函数的声明 3.函数的声明格式 多源文件开发 1.为什么要有多个源文件 2.将sum函数写到其他源文件中 3.在main函数中调用sum函数 4.编译所有的源文件 5.链接所有的目标文件 #include 1.#include的作用 2.#include可以使用绝对路径 3.#…

Linux免交互操作

免交互操作 Here DocumentExpect工具 Here Document Here Document概述 使用I/O重定向的方式将命令列表提供给交互式程序或命令&#xff0c;比如 ftp 、cat 或 read 命令。Here Document 是标准输入的一种替代品&#xff0c;可以帮助脚本开发人员不必使用临时文件来构建输入信息…

docker搭建Elasticsearch集群

这里写目录标题 1.拉取es镜像2.配置配置文件3.启动容器4.启动过程中遇到的问题5.查看容器启动情况 1.拉取es镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.17.0版本根据自己需求进行拉取&#xff0c;我这边选择的是7.17.0&#xff0c;不同版本配置可能稍有…

ANR原理篇 - Input超时机制

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、事件分发流程1.1 事件分发流程概览1.2 InputDispatcher 三、ANR触发流程超时重…

ANR原理篇 - service/broadcast/provider超时机制

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、Service超时机制1.1 埋炸弹1.1.1 AS.realStartServiceLocked1.1.2 AS.bumpSer…

三大基础排序算法——我欲修仙(功法篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️我欲修仙】 学习名言&#xff1a;莫等闲、白了少年头&#xff0c;空悲切。——岳飞 系列文章目录 第一章 ❤️ 学习前的必知知识 第二章 ❤️ 二分查找 文章目录 系列文章目录前言&#x1f697;&…

Netty实战(三)

Netty的组件和设计 一、Channel、EventLoop 和 ChannelFuture1.1 Channel 接口1.2 EventLoop 接口1.3 ChannelFuture 接口 二、ChannelHandler 和 ChannelPipeline2.1 ChannelHandler 接口2.2 ChannelPipeline 接口2.3 编码器和解码器2.4 抽象类 SimpleChannelInboundHandler 三…

suricata中DPDK收发包源码分析2

《suricata中DPDK收发包源码分析1》中分析了整体的DPDK收发包框架代码&#xff0c;今天我们继续来深入了解一下一些细节方面的问题。 目录 Q1&#xff1a;收发包线程模式在代码中是怎样确定的&#xff1f; Q2: DPDK库的初始化rte_eal_init在哪里调用的&#xff1f; Q3: 对网…

Linux中LV Status的状态为NOT available

今天下午有现场反馈备份磁盘找不到了&#xff0c;使用lvm方式的。提供了todesk帮忙看下&#xff0c; 首先使用 blkid查看&#xff0c;确实看不到备份磁盘的UUID&#xff0c;使用lvdisplay查看状态&#xff0c;状态不对了 [rootdb1 ~]# lvdisplay --- Logical volume --- …

.Vue3项目初始化

文章目录 1.Vue3项目初始化1.1 创建vue项目1.2 vue 初始化1.3 git 项目管理1.4 配置iconfig.json1.5 element 按需引入1.6 element 主题色的定制1.7 axios的基础配置1.8 router路由的配置 1.Vue3项目初始化 1.1 创建vue项目 npm init vuelatest1.2 vue 初始化 npm install1.…