在日常研发中,尤其是做 任务调度、异常监控、系统告警 时,我们经常需要快速通知到相关人员。
钉钉机器人就是一个非常轻量、好用、免费的方案。

今天分享一个 完整可用的钉钉机器人工具类,包含:

  • 文本消息(Text)

  • Markdown 消息

  • 链接消息(Link)

  • 图片消息(Image)

  • 自动签名(HmacSHA256)

  • 完整日志可追踪

支持直接在 Java 工程中使用(SpringBoot)。


一、钉钉机器人原理说明

钉钉自定义机器人有两种安全模式:

  1. Signature(加签模式)

  2. IP 限制模式

本文使用操作最安全通用的 加签方式
每次发送消息都需要三要素:

  • webhook 地址(accessToken)

  • secret(加签密钥)

  • 时间戳 + secret 用 HmacSHA256 生成 sign

二、工具类完整代码

下面是可直接复制使用的工具类,支持四种消息类型。

1.添加依赖项到工程的pom.xml文件

 <dependencies>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>alibaba-dingtalk-service-sdk</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.11</version>
        </dependency>
    </dependencies>

2.yml 配置中加入:

ding:
  secret: 你的secret
  url: https://oapi.dingtalk.com/robot/send?access_token=xxxx

3.DingTalkUtil.java


import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

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;

/**
 * 钉钉机器人工具类
 * 支持以下消息类型:
 * 1. 文本消息(Text)
 * 2. Markdown 消息
 * 3. 链接消息(Link)
 * 4. 图片消息(Image)
 * @author xiaomai
 */
public class DingTalkUtil {
    private static final Logger log = LoggerFactory.getLogger(DingTalkUtil.class);

    /**
     * 钉钉机器人配置常量
     */
    @Value("${ding.secret}")
    private static final String SECRET = "SECxxxxxxxx";
    @Value("${ding.url}")
    private static final String DINGTALK_BASE_URL = "https://oapi.dingtalk.com/robot/send?access_token=d77xxxxxxx";

    //==================== 公共发送方法 ====================//

    /**
     * 发送文本消息
     *
     * @param content   消息内容
     * @param isAtAll   是否通知所有人
     * @param mobiles   指定通知的手机号列表,可为空
     */
    public static void sendText(String content, boolean isAtAll, List<String> mobiles) {
        sendMessage(buildTextMsg(content, isAtAll, mobiles));
    }

    /**
     * 发送 Markdown 消息
     *
     * @param title            消息标题
     * @param markdownContent  Markdown 内容
     * @param isAtAll          是否通知所有人
     * @param mobiles          指定通知的手机号列表,可为空
     */
    public static void sendMarkdown(String title, String markdownContent, boolean isAtAll, List<String> mobiles) {
        sendMessage(buildMarkdownMsg(title, markdownContent, isAtAll, mobiles));
    }

    /**
     * 发送链接消息
     *
     * @param title       消息标题
     * @param text        消息内容
     * @param messageUrl  点击跳转链接
     * @param picUrl      图片链接,可为空
     */
    public static void sendLink(String title, String text, String messageUrl, String picUrl) {
        sendMessage(buildLinkMsg(title, text, messageUrl, picUrl));
    }

    /**
     * 发送图片消息
     *
     * @param base64 图片 Base64 内容
     * @param md5    图片 MD5 校验值
     */
    public static void sendImage(String base64, String md5) {
        sendMessage(buildImageMsg(base64, md5));
    }

    //==================== 核心发送方法 ====================//

    /**
     * 核心方法:发送钉钉消息
     *
     * @param reqJson 已组装好的请求 JSON 字符串
     */
    private static void sendMessage(String reqJson) {
        try {
            // 获取时间戳
            long timestamp = System.currentTimeMillis();
            // 按钉钉加签规则生成签名
            String stringToSign = timestamp + "\n" + SECRET;
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
            String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");

            // 构建完整请求 URL
            String url = DINGTALK_BASE_URL + "&timestamp=" + timestamp + "&sign=" + sign;

            // 发送 POST 请求
            String result = HttpUtil.post(url, reqJson);
            log.info("DingTalk push result: {}", result);
        } catch (Exception e) {
            log.error("DingTalk message send failed", e);
        }
    }

    //==================== 构建消息体 ====================//

    /**
     * 构建文本消息 JSON
     */
    private static String buildTextMsg(String content, boolean isAtAll, List<String> mobiles) {
        Map<String, String> textMap = Maps.newHashMap();
        textMap.put("content", content);

        Map<String, Object> atMap = Maps.newHashMap();
        atMap.put("isAtAll", isAtAll);
        atMap.put("atMobiles", mobiles != null ? mobiles : Lists.newArrayList());

        Map<String, Object> reqMap = Maps.newHashMap();
        reqMap.put("msgtype", "text");
        reqMap.put("text", textMap);
        reqMap.put("at", atMap);

        return JSON.toJSONString(reqMap);
    }

    /**
     * 构建 Markdown 消息 JSON
     */
    private static String buildMarkdownMsg(String title, String markdownContent, boolean isAtAll, List<String> mobiles) {
        Map<String, String> markdownMap = Maps.newHashMap();
        markdownMap.put("title", title);
        markdownMap.put("text", markdownContent);

        Map<String, Object> atMap = Maps.newHashMap();
        atMap.put("isAtAll", isAtAll);
        atMap.put("atMobiles", mobiles != null ? mobiles : Lists.newArrayList());

        Map<String, Object> reqMap = Maps.newHashMap();
        reqMap.put("msgtype", "markdown");
        reqMap.put("markdown", markdownMap);
        reqMap.put("at", atMap);

        return JSON.toJSONString(reqMap);
    }

    /**
     * 构建链接消息 JSON
     */
    private static String buildLinkMsg(String title, String text, String messageUrl, String picUrl) {
        Map<String, String> linkMap = Maps.newHashMap();
        linkMap.put("title", title);
        linkMap.put("text", text);
        linkMap.put("messageUrl", messageUrl);
        linkMap.put("picUrl", picUrl != null ? picUrl : "");

        Map<String, Object> reqMap = Maps.newHashMap();
        reqMap.put("msgtype", "link");
        reqMap.put("link", linkMap);

        return JSON.toJSONString(reqMap);
    }

    /**
     * 构建图片消息 JSON
     */
    private static String buildImageMsg(String base64, String md5) {
        Map<String, String> imageMap = Maps.newHashMap();
        imageMap.put("base64", base64);
        imageMap.put("md5", md5);

        Map<String, Object> reqMap = Maps.newHashMap();
        reqMap.put("msgtype", "image");
        reqMap.put("image", imageMap);

        return JSON.toJSONString(reqMap);
    }

    public static void main(String[] args) {
        // 发送文本消息
        sendText("测试Text消息 ✅", false, Lists.newArrayList());

        // 发送 Markdown 消息
        sendMarkdown("Markdown标题", "### 测试Markdown消息\n内容示例 ✅", false, Lists.newArrayList());

        // 发送链接消息
        sendLink("Link标题", "链接消息内容", "https://www.dingtalk.com", null);

        // 图片消息示例(
        // sendImage("base64内容", "md5值");
    }
}

三、钉钉申请操作步骤

    • 登录钉钉客户端,选择需要添加机器人的群聊会话。

    • 进入群聊会话,单击右上角群设置标识。

    • 在群管理栏,单击机器人 > 添加机器人,选择自定义机器人。

    • 单击添加,配置机器人信息
    • 配置项

      说明

      机器人头像

      单击编辑标识,上传机器人头像。

      机器人名字

      添加机器人名称

      安全设置

      安全设置类型:

      • 自定义关键词

      • 加签

      • IP 地址(段)

      安全设置详情参考 自定义机器人安全设置

      (可选)是否开启 Outgoing 机制

      通过 @ 群机器人,将消息发送到指定外部服务,还可以将外部服务的响应结果返回到群聊会话。

      机器人接收消息类型和数据格式,详情参考 机器人接收消息

      配置完成后,勾选《自定义机器人服务及免责条款》,并单击完成。
      支持的消息类型

      四、支持消息类型

      类型

      是否支持 @人

      说明

      Text文本类型

      Text 消息 @ 人图例:

      Link链接消息

      Link 消息图例:

      Markdown 类型

      Markdown 消息 @ 人图例:

      整体跳转 ActionCard 类型

      整体跳转 ActionCard 消息 @ 人图例:

      独立跳转 ActionCard 类型

      独立跳转 ActionCard 消息 @ 人图例:

      FeedCard 类型

      FeedCard 消息图例:

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐