钉钉消息通知
主要用于系统预警、资源预警、重要消息通知,随时随地可以掌握重要信息
一、通知效果
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×tamp=XXX&sign=XXX
return String.format(robotWebhook + "×tamp=%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);
}
}