手写超级好用的钉钉机器人发送消息starter

手写超级好用的钉钉机器人发送消息starter

1.前言

  由于业务上经常需要使用钉钉发送预警消息或者是发送一些运营数据消息等,平时经常写一些copy的代码,零零散散,那个工程要用就去copy一个过来,这种不易维护,且代码丑陋,重复代码多,且封装复用性差,所以我就说来写一个简单使用的starter,可以支持配置多个钉钉群的机器人的accessToken和secret,发送消息可以根据下标来构建一个对应类型的消息,然后调用一套好用简单的api就可以我完成发送消息,代码封装性和复用性强,哪个项目要用,只要引入依赖和配置以下就可以简单快速实现发送钉钉消息。

2.实现

2.1项目结构

image-20240315134056868

  后面经过修改又加了一个DingRobotMsgFeedCardLink类

2.2核心实现分享

EnableDingRobot注解

package com.zlf.starter;

import org.springframework.context.annotation.Import;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 在启动类上加上该注解
 *
 * @author zlf
 * @date 2024/3/14
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DingRobotServiceConfiguration.class)
public @interface EnableDingRobot {

}

DingRobotServiceConfiguration类

package com.zlf.starter;


import com.zlf.config.DingRobotConfig;
import com.zlf.service.DingRobotService;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 钉钉机器人服务配置类
 *
 * @author zlf
 * @date 2024/3/14
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(DingRobotConfig.class)
public class DingRobotServiceConfiguration {

    @Bean
    public DingRobotService dingRobotService(DingRobotConfig dingRobotConfig) {
        return new DingRobotService(dingRobotConfig);
    }

}

DingRobotService类

package com.zlf.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.zlf.config.DingRobotConfig;
import com.zlf.config.RobotProperties;
import com.zlf.constants.ZlfDingRobotConstant;
import com.zlf.dto.DingRobotMsgDto;
import com.zlf.enums.DingRobotMsgTypeEnum;
import com.zlf.type.DingRobotMsg;
import com.zlf.type.DingRobotMsgActionCardButton;
import com.zlf.type.DingRobotMsgAt;
import com.zlf.type.DingRobotMsgFeedCard;
import com.zlf.type.DingRobotMsgLink;
import com.zlf.type.DingRobotMsgMarkdown;
import com.zlf.type.DingRobotMsgMoreActionCard;
import com.zlf.type.DingRobotMsgSingleActionCard;
import com.zlf.type.DingRobotMsgText;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

@Slf4j
public class DingRobotService {

    private DingRobotConfig dingRobotConfig;

    public DingRobotService(DingRobotConfig dingRobotConfig) {
        this.dingRobotConfig = dingRobotConfig;
    }

    /**
     * 根据一个map构建一个markdown的字符串
     * Map<String, Map<String, String>> map
     * Map<String,String> map
     *
     * @param map
     * @return
     */
    public String buildMarkDownContent(Map<String, Object> map) {
        DingRobotMsgDto dto = new DingRobotMsgDto(map);
        String mdContent = dto.buildMarkdownMsgContent();
        return mdContent;
    }

    public void sendMsgByIndex0(DingRobotMsg msg) {
        String msgStr = this.buildMsg1(msg);
        this.send(0, msgStr);
    }

    public void sendMsgByIndex0(DingRobotMsg msg, DingRobotMsgAt at) {
        String msgStr = this.buildMsg1(msg, at);
        this.send(0, msgStr);
    }

    public void sendMsgByIndex(int index, DingRobotMsg msg) {
        String msgStr = this.buildMsg1(msg);
        this.send(index, msgStr);
    }

    public void sendMsgByIndex(int index, DingRobotMsg msg, DingRobotMsgAt at) {
        String msgStr = this.buildMsg0(msg, at);
        this.send(index, msgStr);
    }

    public String buildMsg1(DingRobotMsg msg) {
        return this.buildMsg0(msg, null);
    }

    public String buildMsg1(DingRobotMsg msg, DingRobotMsgAt at) {
        return this.buildMsg0(msg, at);
    }

    public String buildMsg0(DingRobotMsg msg, DingRobotMsgAt at) {
        this.checkMsg(msg, at);
        HashMap<String, Object> param = new HashMap<>(3);
        DingRobotMsgTypeEnum msgTypeEnum = msg.getMsgType();
        String msgType = msgTypeEnum.getMsgType();
        param.put("msgtype", msgType);
        param.put(msgType, msg);
        //是否@所有人
        if (msg instanceof DingRobotMsgText) {
            if (Objects.isNull(at)) {
                param.put("at", new DingRobotMsgAt());
            } else {
                param.put("at", at);
            }
        } else if (msg instanceof DingRobotMsgMarkdown) {
            if (Objects.isNull(at)) {
                param.put("at", new DingRobotMsgAt());
            } else {
                param.put("at", at);
            }
        }
        return JSON.toJSONString(param);
    }

    /**
     * 检查消息参数
     *
     * @param msg
     * @param at
     */
    private void checkMsg(DingRobotMsg msg, DingRobotMsgAt at) {
        if (Objects.isNull(msg)) {
            throw new RuntimeException("DingRobotMsg参数不为空");
        }
        if (msg instanceof DingRobotMsgText) {
            DingRobotMsgText msgText = (DingRobotMsgText) msg;
            if (StringUtils.isEmpty(msgText.getContent())) {
                throw new RuntimeException("发送" + msgText.getMsgType().getMsgType() + "类型消息的content不为空");
            }
            this.doContent(msg, at);
        } else if (msg instanceof DingRobotMsgMarkdown) {
            DingRobotMsgMarkdown msgMarkdown = (DingRobotMsgMarkdown) msg;
            if (StringUtils.isEmpty(msgMarkdown.getTitle())) {
                throw new RuntimeException("发送" + msgMarkdown.getMsgType().getMsgType() + "类型消息的title不为空");
            }
            if (StringUtils.isEmpty(msgMarkdown.getText())) {
                throw new RuntimeException("发送" + msgMarkdown.getMsgType().getMsgType() + "类型消息的text不为空");
            }
            this.doContent(msg, at);
        } else if (msg instanceof DingRobotMsgLink) {
            DingRobotMsgLink msgLink = (DingRobotMsgLink) msg;
            if (StringUtils.isEmpty(msgLink.getTitle())) {
                throw new RuntimeException("发送" + msgLink.getMsgType().getMsgType() + "类型消息的title不为空");
            }
            if (StringUtils.isEmpty(msgLink.getText())) {
                throw new RuntimeException("发送" + msgLink.getMsgType().getMsgType() + "类型消息的text不为空");
            }
        } else if (msg instanceof DingRobotMsgSingleActionCard) {
            DingRobotMsgSingleActionCard singleActionCard = (DingRobotMsgSingleActionCard) msg;
            if (StringUtils.isEmpty(singleActionCard.getTitle())) {
                throw new RuntimeException("发送" + singleActionCard.getMsgType().getMsgType() + "类型消息的title不为空");
            }
            if (StringUtils.isEmpty(singleActionCard.getText())) {
                throw new RuntimeException("发送" + singleActionCard.getMsgType().getMsgType() + "类型消息的text不为空");
            }
            if (StringUtils.isEmpty(singleActionCard.getSingleTitle())) {
                throw new RuntimeException("发送" + singleActionCard.getMsgType().getMsgType() + "类型消息的singleTitle不为空");
            }
            if (StringUtils.isEmpty(singleActionCard.getSingleURL())) {
                throw new RuntimeException("发送" + singleActionCard.getMsgType().getMsgType() + "类型消息的singleURL不为空");
            }
        } else if (msg instanceof DingRobotMsgMoreActionCard) {
            DingRobotMsgMoreActionCard moreActionCard = (DingRobotMsgMoreActionCard) msg;
            if (StringUtils.isEmpty(moreActionCard.getTitle())) {
                throw new RuntimeException("发送" + moreActionCard.getMsgType().getMsgType() + "类型消息的title不为空");
            }
            if (StringUtils.isEmpty(moreActionCard.getText())) {
                throw new RuntimeException("发送" + moreActionCard.getMsgType().getMsgType() + "类型消息的text不为空");
            }
            if (CollectionUtil.isEmpty(moreActionCard.getBtns())) {
                throw new RuntimeException("发送" + moreActionCard.getMsgType().getMsgType() + "类型消息的btns不为空");
            }
            //校验btns
            List<DingRobotMsgActionCardButton> btns = moreActionCard.getBtns();
            for (DingRobotMsgActionCardButton btn : btns) {
                if (StringUtils.isEmpty(btn.getTitle())) {
                    throw new RuntimeException("发送" + moreActionCard.getMsgType().getMsgType() + "类型消息的btns中title不为空");
                }
                if (StringUtils.isEmpty(btn.getActionURL())) {
                    throw new RuntimeException("发送" + moreActionCard.getMsgType().getMsgType() + "类型消息的btns中actionURL不为空");
                }
            }
        } else if (msg instanceof DingRobotMsgFeedCard) {
            DingRobotMsgFeedCard feedCard = (DingRobotMsgFeedCard) msg;
            if (CollectionUtil.isEmpty(feedCard.getLinks())) {
                throw new RuntimeException("发送" + feedCard.getMsgType().getMsgType() + "类型消息的links不为空");
            }
            //校验links
            List<DingRobotMsgFeedCardLink> links = feedCard.getLinks();
            for (DingRobotMsgFeedCardLink link : links) {
                if (StringUtils.isEmpty(link.getTitle())) {
                    throw new RuntimeException("发送" + feedCard.getMsgType().getMsgType() + "类型消息的links中title不为空");
                }           
            }
        }
    }

    private void doContent(DingRobotMsg msg, DingRobotMsgAt at) {
        if (Objects.nonNull(at) && CollectionUtil.isNotEmpty(at.getAtUserIds())) {
            //userIds去重
            List<String> atUserIds = at.getAtUserIds();
            log.info("doContent.atUserIds:{}", JSON.toJSONString(atUserIds));
            Set<String> atUserIdsSet = new HashSet<>(atUserIds);
            List<String> atUserIdsNew = new ArrayList<>(atUserIdsSet);
            log.info("doContent.atUserIdsNew:{}", JSON.toJSONString(atUserIdsNew));
            at.setAtUserIds(atUserIdsNew);
            StringBuffer sb = new StringBuffer();
            atUserIdsNew.forEach(e -> {
                        sb.append("@" + e);
                    }
            );
            String userIdsNewStr = sb.toString();
            log.info("DingRobotService.doContent.userIdsNewStr:{}", userIdsNewStr);
            if (msg instanceof DingRobotMsgText) {
                DingRobotMsgText msgText = (DingRobotMsgText) msg;
                msgText.setContent(msgText.getContent() + userIdsNewStr);
                log.info("DingRobotService.doContent.atUserIds.text-content:{}", msgText.getContent());
            } else if (msg instanceof DingRobotMsgMarkdown) {
                DingRobotMsgMarkdown msgMarkdown = (DingRobotMsgMarkdown) msg;
                msgMarkdown.setText(msgMarkdown.getText() + userIdsNewStr);
                log.info("DingRobotService.doContent.atUserIds.markdown-text:{}", msgMarkdown.getText());
            }
        }
        if (Objects.nonNull(at) && CollectionUtil.isNotEmpty(at.getAtMobiles())) {
            List<String> atMobiles = at.getAtMobiles();
            log.info("DingRobotService.doContent.atMobiles:{}", JSON.toJSONString(atMobiles));
            //手机号去重
            Set<String> atMobilesSet = new HashSet<>(atMobiles);
            StringBuffer sb = new StringBuffer();
            atMobilesSet.forEach(e -> {
                sb.append("@" + e);
            });
            String atMobileStr = sb.toString();
            log.info("DingRobotService.doContent.atMobileStr:{}", atMobileStr);
            if (msg instanceof DingRobotMsgText) {
                DingRobotMsgText msgText = (DingRobotMsgText) msg;
                msgText.setContent(msgText.getContent() + atMobileStr);
                log.info("DingRobotService.doContent.atMobiles.text-content:{}", msgText.getContent());
            } else if (msg instanceof DingRobotMsgMarkdown) {
                DingRobotMsgMarkdown msgMarkdown = (DingRobotMsgMarkdown) msg;
                msgMarkdown.setText(msgMarkdown.getText() + atMobileStr);
                log.info("DingRobotService.doContent.atMobiles.markdown-text:{}", msgMarkdown.getText());
            }
        }
        if (Objects.nonNull(at) && (CollectionUtil.isNotEmpty(at.getAtMobiles()) || CollectionUtil.isNotEmpty(at.getAtUserIds()))) {
            at.setIsAtAll(Boolean.FALSE);
        }
    }

    private RobotProperties getRobotProperties(int index) {
        List<RobotProperties> rbs = dingRobotConfig.getRbs();
        if (CollectionUtil.isEmpty(rbs)) {
            throw new RuntimeException("根据index获取RobotProperties为空");
        }
        return rbs.get(index);
    }

    private void send(int index, String msg) {
        try {
            RobotProperties p = this.getRobotProperties(index);
            Long timestamp = System.currentTimeMillis();
            String sign = getSign(p.getSecret(), timestamp);
            if (StringUtils.isEmpty(sign)) {
                log.error("【发送钉钉群消息】sign不为空,签名异常");
            }
            String url = ZlfDingRobotConstant.DING_URL + p.getAccessToken() + "&timestamp=" + timestamp + "&sign=" + sign;
            ThreadPoolService.getInstance().execute(
                    () -> {
                        log.info("【发送钉钉群消息】请求参数:url = {}, msg = {}", url, msg);
                        String res = HttpUtil.post(url, msg);
                        log.info("【发送钉钉群消息】消息响应结果:" + res);
                    });
        } catch (Exception e) {
            log.error("【发送钉钉群消息】请求钉钉接口异常,msg = {}", e);
        }
    }

    private String getSign(String secret, Long timestamp) {
        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");
            log.info("【发送钉钉群消息】获取到签名sign = {}", sign);
            return sign;
        } catch (Exception e) {
            log.error("【发送钉钉群消息】计算签名异常,errMsg = {}", e);
            return null;
        }
    }

}

ThreadPoolService类

package com.zlf.service;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author zlf
 * @time: 2024/3/14
 */
@Slf4j
public class ThreadPoolService {

    private static final int DEFAULT_CORE_SIZE = Runtime.getRuntime().availableProcessors();
    private static final int MAX_QUEUE_SIZE = Runtime.getRuntime().availableProcessors();
    private static final int QUEUE_INIT_MAX_SIZE = 500;
    private volatile static ThreadPoolExecutor executor;

    private ThreadPoolService() {
    }

    // 获取单例的线程池对象
    public static ThreadPoolExecutor getInstance() {
        if (executor == null) {
            synchronized (ThreadPoolService.class) {
                if (executor == null) {
                    //拒绝策略
                    executor = new ThreadPoolExecutor(
                            DEFAULT_CORE_SIZE,// 核心线程数
                            MAX_QUEUE_SIZE, // 最大线程数
                            10L, // 闲置线程存活时间
                            TimeUnit.SECONDS,// 时间单位
                            new LinkedBlockingDeque<>(QUEUE_INIT_MAX_SIZE),// 线程队列
                            Executors.defaultThreadFactory(),// 线程工厂
                            (r, executor) -> {
                                try {
                                    executor.getQueue().put(r);
                                } catch (InterruptedException e) {
                                    log.error("线程处理拒绝策略失败:{}", e.getMessage());
                                }
                            }
                    );
                }
            }
        }
        return executor;
    }

    public void execute(Runnable runnable) {
        if (runnable == null) {
            return;
        }
        executor.execute(runnable);
    }

    // 从线程队列中移除对象
    public void cancel(Runnable runnable) {
        if (executor != null) {
            executor.getQueue().remove(runnable);
        }
    }

}

  消息类型相关类的封装参看钉钉官网:

https://open.dingtalk.com/document/orgapp/robot-message-types-and-data-format#title-z74-8to-i7e

  atUserIds这个参数我没有找到,因为我没有钉钉的管理员权限,需要登录钉钉后台查看,不是用户头像里面的钉钉号,可以参看钉钉官网的如下接口:

https://open.dingtalk.com/document/isvapp/query-the-list-of-department-userids

  at手机号下面是测试是可以的,这个只是一个发消息的简单的api集成,可以拓展集成钉钉官方文档的其它接口,比如如下可以集成开发一个机器人服务端,发送消息到指定的钉钉群,然后服务端可以收到用户发的消息,然后机器人回复相应的消息:

https://open.dingtalk.com/document/orgapp/robot-receive-message

  这只是一个例子,可以根据自己的需求来集成,官方提供的sdk,依赖的东西比较多,也不怎么好用,所以可以自己去造个符合自己需求和使用习惯的轮子。

3.配置使用

3.1 引入项目resources下libs中的jar包依赖如下

  右键点击ding-robot-spring-boot-start-1.0-SNAPSHOT.jar将该jar包手动导入(add as Library),复制该jar包到resources下libs,若果maven自动导入就不用右键手动导入

<dependency>
    <groupId>org.zlf</groupId>
    <artifactId>ding-robot-spring-boot-start</artifactId>
    <version>1.0-SNAPSHOT</version>
    <scope>system</scope>
    <systemPath>${pom.basedir}/src/main/resources/libs/ding-robot-spring-boot-start-1.0-SNAPSHOT.jar
     </systemPath>
</dependency>

3.2引入maven私服依赖如下

<dependency>
    <groupId>org.zlf</groupId>
    <artifactId>ding-robot-spring-boot-start</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

3.3yaml配置

zlf:
  robot:
    rbs:
      - accessToken: xxxxxxx
        secret: xxxxxxx

4.测试验证

  在项目中引入3.2的依赖和配置好3.3的配置

  然后写如下的controller

DingDingController类

package xxxx.controller;

import com.zlf.service.DingRobotService;
import com.zlf.type.DingRobotMsgActionCardButton;
import com.zlf.type.DingRobotMsgAt;
import com.zlf.type.DingRobotMsgLink;
import com.zlf.type.DingRobotMsgMarkdown;
import com.zlf.type.DingRobotMsgMoreActionCard;
import com.zlf.type.DingRobotMsgSingleActionCard;
import com.zlf.type.DingRobotMsgText;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("ding")
public class DingDingController {

    @Autowired
    private DingRobotService dingRobotService;

    @GetMapping("/sendMsg1")
    public String sendMsg1(@RequestParam(value = "msg") String msg) {
        DingRobotMsgText dingRobotMsgText = new DingRobotMsgText();
        dingRobotMsgText.setContent(msg);
        DingRobotMsgAt at = new DingRobotMsgAt();
       /* List<String> mobiles = new ArrayList<>();
        mobiles.add("xxxx");
        mobiles.add("xxxxxx");
        at.setAtMobiles(mobiles);*/
        //xxxxxx
        List<String> userIds = new ArrayList<>();
        userIds.add("xxxxx");
        userIds.add("xxxxx");
        at.setAtUserIds(userIds);
        dingRobotService.sendMsgByIndex0(dingRobotMsgText, at);
        return "ok";
    }

    @GetMapping("/sendMsg2")
    public String sendMsg2(@RequestParam(value = "msg") String msg) {
        DingRobotMsgMarkdown msgMarkdown = new DingRobotMsgMarkdown();
        msgMarkdown.setTitle("测试");
        msgMarkdown.setText(msg);
        dingRobotService.sendMsgByIndex0(msgMarkdown);
        return "ok";
    }

    @GetMapping("/sendMsg3")
    public String sendMsg() {
        DingRobotMsgMarkdown msgMarkdown = new DingRobotMsgMarkdown();
        msgMarkdown.setTitle("测试");
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("title", "=====我是标题=====");
        /*map.put("年龄", "28");
        map.put("兴趣", "打球");*/
        Map<String, String> map1 = new HashMap<>();
        map1.put("姓名", "张三");
        map1.put("年龄", "28");
        Map<String, String> map2 = new HashMap<>();
        map2.put("兴趣", "打球");
        map.put("item1", map1);
        map.put("item2", map2);
        map.put("我是张三", "我是张三");
        map.put("我是李四", "我是李四");
        String msg = dingRobotService.buildMarkDownContent(map);
        msgMarkdown.setText(msg);
        DingRobotMsgAt at = new DingRobotMsgAt();
        dingRobotService.sendMsgByIndex0(msgMarkdown,at);
        return "ok";
    }

    @GetMapping("/sendMsg4")
    public String sendMsg4() {
        DingRobotMsgLink link = new DingRobotMsgLink();
        link.setTitle("这是Link消息");
        link.setText("这是一个Link消息");
        link.setPicUrl("https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png");
        link.setMessageUrl("https://open.dingtalk.com/document/");
        dingRobotService.sendMsgByIndex0(link);
        return "ok";
    }

    @GetMapping("/sendMsg5")
    public String sendMsg5() {
        DingRobotMsgSingleActionCard singleActionCard = new DingRobotMsgSingleActionCard();
        singleActionCard.setTitle("打造一间咖啡厅");
        singleActionCard.setText("![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \\n #### 乔布斯 20 年前想打造的苹果咖啡厅 \\n\\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划");
        singleActionCard.setSingleTitle("阅读全文");
        singleActionCard.setSingleURL("https://www.dingtalk.com/");
        dingRobotService.sendMsgByIndex0(singleActionCard);
        return "ok";
    }


    @GetMapping("/sendMsg6")
    public String sendMsg6() {
        DingRobotMsgMoreActionCard moreActionCard = new DingRobotMsgMoreActionCard();
        moreActionCard.setTitle("打造一间咖啡厅");
        moreActionCard.setText("![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \\n #### 乔布斯 20 年前想打造的苹果咖啡厅 \\n\\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划");
        List<DingRobotMsgActionCardButton> btns = new ArrayList<>();
        DingRobotMsgActionCardButton bt1 = new DingRobotMsgActionCardButton();
        bt1.setTitle("内容不错");
        bt1.setActionURL("https://www.dingtalk.com/");
        DingRobotMsgActionCardButton bt2 = new DingRobotMsgActionCardButton();
        bt2.setTitle("不感兴趣");
        bt2.setActionURL("https://www.dingtalk.com/");
        btns.add(bt1);
        btns.add(bt2);
        moreActionCard.setBtns(btns);
        dingRobotService.sendMsgByIndex0(moreActionCard);
        return "ok";
    }
    
    @GetMapping("/sendMsg7")
    public String sendMsg7() {
        DingRobotMsgFeedCard feedCard = new DingRobotMsgFeedCard();
        List<DingRobotMsgFeedCardLink> links = new ArrayList<>();
        DingRobotMsgFeedCardLink link = new DingRobotMsgFeedCardLink();
        link.setTitle("时代的火车向前开1");
        link.setMessageURL("https://www.dingtalk.com/");
        link.setPicURL("https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png");

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

        links.add(link);
        links.add(link2);
        feedCard.setLinks(links);
        dingRobotService.sendMsgByIndex0(feedCard);
    }


}

4.1测试实现效果如下

  只有text和markdown消息支持at参数,at参数可选

4.1.1.发送text消息

image-20240315132015600

4.1.2.发送markdown消息

image-20240315132128750

4.1.3.发送link消息

image-20240315132229525

4.1.4.发送单actionCard消息

image-20240315132407986

4.1.5.发送多actionCard消息

image-20240315132419684

4.1.6.发送feedCard消息

在这里插入图片描述

4.1.7.发送自定义markdown消息

在这里插入图片描述

  这个就是我们业务上最常用的发消息的样子,使用一个Map<String,Object>来实现,Object可以放String和一个Map<String,String>,可以使用有序Map和无序Map,所以我就实现了一个默认的,还可以自定义一个markdown消息的text,只不过需要自己去拼接text,也比较麻烦。

5.总结

  通过以上的分享就实现了一个使用钉钉机器人发消息的starter,希望我的分享对你有所帮助和启发,请一键三连,么么哒,下期分享见!

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
当然可以!以下是一个简单的示例,展示了如何手写一个Spring Boot Starter: 首先,创建一个 Maven 项目,并添加以下依赖项: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.5.4</version> </dependency> </dependencies> ``` 接下来,创建一个自定义的自动配置类,用于配置你的 Starter: ```java @Configuration @EnableConfigurationProperties(MyStarterProperties.class) public class MyStarterAutoConfiguration { private final MyStarterProperties properties; public MyStarterAutoConfiguration(MyStarterProperties properties) { this.properties = properties; } // 在此处定义你的自动配置逻辑 @Bean public MyStarterService myStarterService() { return new MyStarterService(properties); } } ``` 然后,创建一个属性类,用于将外部配置绑定到属性上: ```java @ConfigurationProperties("my.starter") public class MyStarterProperties { private String message; // 提供 getter 和 setter } ``` 最后,创建一个自定义的服务类,该服务类将在你的 Starter 中使用: ```java public class MyStarterService { private final MyStarterProperties properties; public MyStarterService(MyStarterProperties properties) { this.properties = properties; } public void showMessage() { System.out.println(properties.getMessage()); } } ``` 现在,你的 Spring Boot Starter 已经准备就绪了!你可以将其打包并使用在其他 Spring Boot 项目中。在其他项目的 `pom.xml` 文件中,添加你的 Starter 依赖: ```xml <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>my-starter</artifactId> <version>1.0.0</version> </dependency> </dependencies> ``` 然后,在你的应用程序中使用 `MyStarterService`: ```java @SpringBootApplication public class MyApplication implements CommandLineRunner { private final MyStarterService myStarterService; public MyApplication(MyStarterService myStarterService) { this.myStarterService = myStarterService; } public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } @Override public void run(String... args) throws Exception { myStarterService.showMessage(); } } ``` 这样,你就成功地创建了一个简单的 Spring Boot Starter!当其他项目引入你的 Starter 并运行时,将会输出预定义的消息。 当然,这只是一个简单的示例,真实的 Starter 可能包含更多的配置和功能。你可以根据自己的需求进行扩展和定制。希望对你有所帮助!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值