企业微信、飞书、钉钉机器人消息发送工具类

1、实例化WebClient对象

其实你也可以使用RestTemplate,我这里主要是用到了webflux框架,所以需要实例化客户端请求对象

java 复制代码
@Bean
public WebClient webClient(){
    HttpClient httpClient = getHttpClient();
    return WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(httpClient)).build();
}

private HttpClient getHttpClient() {
    ConnectionProvider provider = ConnectionProvider.builder("你爱咋咋的,一般用你项目名即可")
            .maxConnections(500)
            .maxIdleTime(Duration.ofSeconds(10))
            .maxLifeTime(Duration.ofSeconds(20))
            .pendingAcquireTimeout(Duration.ofSeconds(30))
            .pendingAcquireTimer((r, d) -> {
                Timeout t = wheel.newTimeout(timeout -> r.run(), d.toMillis(), TimeUnit.MILLISECONDS);
                return () -> t.cancel();
            })
            .fifo()
            .build();

    HttpClient httpClient = HttpClient.create(provider);
    return httpClient;
}

2、发送及有效性测试工具类

java 复制代码
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import static com.paratera.console.notice.utils.Constants.*;

/**
 * 机器人发送工具类(微信,飞书,钉钉)
 *
 * @author huxiang
 */
@Component
@Slf4j
public class RobotUtil {

    @Autowired
    private WebClient webClient;

    /**
     * 机器人发送消息(markdown格式)
     *
     * @param robotUrl   机器人地址
     * @param type       类型:1微信,2飞书,3钉钉
     * @param context    markdown文本内容
     * @param signSecret 签名校验(飞书,钉钉使用,可为空,具体要根据用户是否启用签名)
     */
    public void sendMsg(String robotUrl, Integer type, String context, String signSecret) {
        JSONObject msgObj = JSONUtil.createObj();
        JSONObject mdObj = JSONUtil.createObj();
        switch (type) {
            case ROBOT_TYPE_WX:
                msgObj.set("msgtype", "markdown");
                mdObj.set("content", context);
                msgObj.set("markdown", mdObj);
                break;
            case ROBOT_TYPE_FS:
                if (StringUtils.isNotEmpty(signSecret)) {
                    msgObj = getFsSignObj(signSecret);
                }
                msgObj.set("msg_type", "interactive");
                mdObj.set("tag", "lark_md");
                mdObj.set("content", context);
                JSONArray elements = JSONUtil.createArray();
                JSONObject wrapObj = JSONUtil.createObj();
                wrapObj.set("tag", "div");
                wrapObj.set("text", mdObj);
                elements.put(wrapObj);
                JSONObject cardObj = JSONUtil.createObj();
                cardObj.set("elements", elements);
                msgObj.set("card", cardObj);
                break;
            case ROBOT_TYPE_DD:
                if (StringUtils.isNotEmpty(signSecret)) {
                    robotUrl = getDdRobotURL(robotUrl, signSecret);
                }
                msgObj.set("msgtype", "markdown");
                mdObj.set("title", "通知");
                mdObj.set("text", context);
                msgObj.set("markdown", mdObj);
                break;
        }
        webClient.post()
                .uri(robotUrl)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(msgObj))
                .retrieve().bodyToMono(String.class).subscribe(
                        result -> {
                            log.info("机器人通知发送结果:" + result);
                        }
                );
    }

    /**
     * 机器人有效性测试 0表示成功,其他表示失败
     *
     * @param robotUrl   机器人地址
     * @param type       类型:1微信,2飞书,3钉钉
     * @param signSecret 签名校验(飞书,钉钉使用,可为空,具体要根据用户是否启用签名)
     * @return
     */
    public Integer sendTestMsg(String robotUrl, Integer type, String signSecret) {
        JSONObject msgObj = JSONUtil.createObj();
        JSONObject mdObj = JSONUtil.createObj();
        ObjectMapper mapper = new ObjectMapper();
        switch (type) {
            case ROBOT_TYPE_WX:
                msgObj.set("msgtype", "markdown");
                mdObj.set("content", "机器人有效性测试!");
                msgObj.set("markdown", mdObj);
                break;
            case ROBOT_TYPE_FS:
                if (StringUtils.isNotEmpty(signSecret)) {
                    msgObj = getFsSignObj(signSecret);
                }
                msgObj.set("msg_type", "interactive");
                mdObj.set("tag", "lark_md");
                mdObj.set("content", "机器人有效性测试!");
                JSONArray elements = JSONUtil.createArray();
                JSONObject wrapObj = JSONUtil.createObj();
                wrapObj.set("tag", "div");
                wrapObj.set("text", mdObj);
                elements.put(wrapObj);
                JSONObject cardObj = JSONUtil.createObj();
                cardObj.set("elements", elements);
                msgObj.set("card", cardObj);
                break;
            case ROBOT_TYPE_DD:
                if (StringUtils.isNotEmpty(signSecret)) {
                    robotUrl = getDdRobotURL(robotUrl, signSecret);
                }
                msgObj.set("msgtype", "markdown");
                mdObj.set("title", "通知");
                mdObj.set("text", "机器人有效性测试!");
                msgObj.set("markdown", mdObj);
                break;

        }
        Mono<String> mono = webClient.post()
                .uri(robotUrl)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(msgObj))
                .retrieve().bodyToMono(String.class);
        String result = mono.block();
        try {
            Map res = mapper.readValue(result, Map.class);
            return (Integer) (res.containsKey("errcode") ? res.get("errcode") : res.get("code"));
        } catch (JsonProcessingException e) {
            log.error("类型转换异常-RobotUtil.sendTestMsg:" + e);
        }
        return -1;
    }

    /**
     * 飞书获取带签名的消息体
     *
     * @return
     */
    public JSONObject getFsSignObj(String signSecret) {
        JSONObject signObj = JSONUtil.createObj();
        Long timestamp = DateUtil.currentSeconds();
        String sign = null;
        try {
            //把timestamp+"\n"+密钥当做签名字符串
            String stringToSign = timestamp + "\n" + signSecret;
            //使用HmacSHA256算法计算签名
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] signData = mac.doFinal(new byte[]{});
            sign = Base64.encodeBase64String(signData);
        } catch (Exception e) {
            log.error("飞书获取签名失败:" + e);
        }
        if (StringUtils.isEmpty(sign)) {
            return null;
        }
        signObj.set("timestamp", timestamp);
        signObj.set("sign", sign);
        return signObj;
    }


    /**
     * 钉钉获取带签名的机器人推送URL
     *
     * @return
     */

    public String getDdRobotURL(String robotUrl, String signSecret) {
        Long timestamp = System.currentTimeMillis();
        String stringToSign = timestamp + "\n" + signSecret;
        String sign = null;
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(signSecret.getBytes("UTF-8"), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
            sign = URLEncoder.encode(Base64.encodeBase64String(signData), "UTF-8");
        } catch (Exception e) {
            log.error("钉钉获取签名失败:" + e);
        }
        if (StringUtils.isEmpty(sign)) {
            return null;
        }
        return robotUrl + "&timestamp=" + timestamp + "&sign=" + sign;
    }
}
相关推荐
风若飞10 分钟前
Spring Ai 入门遇到问题1
java·后端·spring
Nuanyt33 分钟前
LeetCode Hot 100 链表专题(五)· 2 题(Java)
java·算法·leetcode·链表
黑马程序员毕设1 小时前
基于 Java 的餐饮点餐系统设计与应用报告
java·开发语言
SL_staff1 小时前
从文档工具到知识中枢:基于JVS数字底座构建企业级统一内容管理平台的技术实践
java·开源·全栈
SL_staff1 小时前
从‘连得上’到‘管得好’:JVS-IOT双引擎驱动设备运维闭环
java·物联网·产品
金融Tech趋势派3 小时前
企业微信客户管理工具哪个好?按企业规模、行业与合规要求的三层判断标准
人工智能·企业微信
硅谷秋水3 小时前
AgenticRoboRL:将“编程智能体”后训练引入智能体化机器人领域
人工智能·机器学习·语言模型·机器人
敢敢のwings4 小时前
SimpleMemVLA 深度解读:把原生视频上下文变成机器人的记忆
数码相机·机器人·音视频
w_zero_one4 小时前
递归算法(一)
java·开发语言·算法
SL_staff4 小时前
私有化低代码不是减配版开发:JVS双模式建模如何重建IT交付信任
java·低代码·spring cloud