SpringBoot整合企业微信消息推送(四十五)

news2024/11/27 15:42:38

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

上一章简单介绍了 SpringBoot整合钉钉消息推送(四十四) , 如果没有看过,请观看上一章

一. 企业微信前期准备

用户需要注册一个企业微信, 并且登录. https://work.weixin.qq.com/

添加相应的员工, 注意 成员详情里面的 账号, 这是用户的唯一标识.

image-20230516203431338

查询企业 id, 点击 我的企业:

image-20230516203543980

创建应用:

image-20230516203619732

填写相应的信息, 注意 可见 范围的筛选

有两个主要的信息, AgentId 和 Secret 密钥.

image-20230516203720788

用户需要先关注该企业号, 并且开启接收消息通知才可以收到消息.

二. 企业微信发送消息

二.一 添加依赖

 <!--添加 weixin 发送-->
        <dependency>
            <groupId>com.tencentcloudapi</groupId>
            <artifactId>tencentcloud-sdk-java</artifactId>
            <!-- go to https://search.maven.org/search?q=tencentcloud-sdk-java and get the latest version. -->
            <!-- 请到https://search.maven.org/search?q=tencentcloud-sdk-java查询所有版本,最新版本如下 -->
            <version>3.1.322</version>
        </dependency>
        <dependency>
            <groupId>com.tencentcloudapi</groupId>
            <artifactId>tencentcloud-sdk-java-ocr</artifactId>
            <version>3.1.322</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-velocity</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.71</version>
        </dependency>

二.二 企业消息发送配置

# 微信发送的配置信息
weixin:
  corpId: 我的企业---> 企业Id
  coprsecret: 新创建应用的秘钥
  agentId: 1000003

二.三 微信消息封装 message.weixin 包下

image-20230516204309355

其中, message.weixin 包 下为 老蝴蝶封装好的内容信息, 只需要全部复制到项目里面即可。

就是简单的实体 和util 类.

二.三.一 AccessToken 微信通用接口凭证, 获取token信息

/**
 * @Description 微信通用接口凭证, 获取token信息
 * @Author yuejianli
 * @Date 2022/6/4 16:13
 **/
@Data
public class AccessToken {
    /**
     * 获取到的凭证
     */
    private String token;
    /**
     * 凭证有效时间,单位:秒
     */
    private int expiresIn;

}

二.三.二 BaseMessage 基本消息处理

/**
 * @Description 基本消息处理
 * @Author yuejianli
 * @Date 2022/6/4 16:15
 **/
@Data
public class BaseMessage {
    /**
     * 否 成员ID列表(消息接收者,多个接收者用‘|'分隔,最多支持1000个)。
     * 特殊情况:指定为@all,则向该企业应用的全部成员发送
     */
    private String touser;
    /**
     * 否 部门ID列表,多个接收者用‘|'分隔,最多支持100个。
     * 当touser为@all时忽略本参数
     */
    private String toparty;
    /**
     * 否 标签ID列表,多个接收者用‘|'分隔,最多支持100个。当touser为@all时忽略本参数
     */
    private String totag;
    /**
     * 是 消息类型
     */
    private String msgtype;
    /**
     * 是 企业应用的id,整型。可在应用的设置页面查看
     */
    private int agentid;
}

二.三.三 TextCardMessage TextCard 类型,支持 html 标签

/**
 * @Description TextCard 类型,支持 html 标签
 * @Author yuejianli
 * @Date 2022/6/4 16:17
 **/
@Data
public class TextCardMessage extends BaseMessage {
	/**
	 * 放置内容
	 */
	private WxTextCard textcard;
}

二.三.四 TextMessage 文本是否加密的信息

/**
 * @Description 文本是否加密的信息
 * @Author yuejianli
 * @Date 2022/6/4 16:17
 **/
@Data
public class TextMessage extends BaseMessage {
    /**
     * 文本
     */
    private WxText text;
    /**
     * 否 表示是否是保密消息,0表示否,1表示是,默认0
     */
    private int safe;
}

二.三.五 WxText 普通微信文本信息

/**
 * @Description 普通微信文本信息
 * @Author yuejianli
 * @Date 2022/6/4 16:16
 **/
@Data
public class WxText {
    /**
     * 是    消息内容,最长不超过2048个字节
     */
    private String content;
}

二.三.六 WxTextCard markdown 文本信息

/**
 * @Description markdown 文本信息
 * @Author yuejianli
 * @Date 2022/6/4 16:16
 **/
@Data
public class WxTextCard {
	/**
	 * 标题
	 */
	private String title;
	/**
	 * 描述信息
	 */
	private String description;
	/**
	 * url 信息
	 */
	private String url;
	/**
	 * 按钮展示文字
	 */
	private String btntxt;
}

二.三.七 证书信任凭证 MyX509TrustManager

/**
 * @Description 证书信任管理器(用于https请求
 * @Author yuejianli
 * @Date 2022/6/4 16:18
 **/
public class MyX509TrustManager implements X509TrustManager {
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

二.三.八 WeChatUtil 微信工具使用

/**
 * @Description 微信工具使用
 * @Author yuejianli
 * @Date 2022/6/4 16:20
 **/
@Slf4j
public class WeChatUtil {
	/**
	 * 微信的请求url 获取access_token的接口地址(GET) 限200(次/天)
	 */
	public final static String access_token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={0}&corpsecret={1}";

    /**
     * 1.发起https请求并获取结果
     *
     * @param requestUrl    请求地址
     * @param requestMethod 请求方式(GET、POST)
     * @param outputStr     提交的数据
     * @return JSONObject(通过JSONObject.get ( key)的方式获取json对象的属性值)
     */
    public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
        JSONObject jsonObject = null;
        StringBuffer buffer = new StringBuffer();
        try {
            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            URL url = new URL(requestUrl);
            HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
            httpUrlConn.setSSLSocketFactory(ssf);
            httpUrlConn.setDoOutput(true);
            httpUrlConn.setDoInput(true);
            httpUrlConn.setUseCaches(false);
            // 设置请求方式(GET/POST)
            httpUrlConn.setRequestMethod(requestMethod);
            if ("GET".equalsIgnoreCase(requestMethod)) {
                httpUrlConn.connect();
            }
            // 当有数据需要提交时
            if (null != outputStr) {
                OutputStream outputStream = httpUrlConn.getOutputStream();
                // 注意编码格式,防止中文乱码
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 将返回的输入流转换成字符串
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 释放资源
            inputStream.close();
            httpUrlConn.disconnect();
            jsonObject = JSONObject.parseObject(buffer.toString());
            return jsonObject;
        } catch (Exception e) {
            log.error("异常信息", e);
            return null;
        }
    }

    /**
     * 微信用户登录,登录成功后,返回相应的token 值
     *
     * @param appid     机构id
     * @param appsecret 密码
     * @return 微信用户登录,登录成功后,返回相应的token 值
     */
    public static AccessToken getAccessToken(String appid, String appsecret) {
		AccessToken accessToken = null;
		String requestUrl = MessageFormat.format(access_token_url, appid, appsecret);
		JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
        // 如果请求成功
        if (null != jsonObject) {
            try {
                accessToken = new AccessToken();
                accessToken.setToken(jsonObject.getString("access_token"));
                accessToken.setExpiresIn(jsonObject.getInteger("expires_in"));
            } catch (Exception e) {
                accessToken = null;
                // 获取token失败
                log.error("获取token失败 errcode:{} errmsg:{}",
                        jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
                return accessToken;
            }
        }
        return accessToken;
    }
}

二.四 微信发送消息接口和实现

二.四.一 接口

/**
 * 邮件发送的接口信息
 *
 * @author yuejianli
 * @date 2022-06-09
 */

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

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

二.四.二 接口实现

先获取 token 再进行封装数据,发送.

getWeiXinToken() 获取的 token 最好放置在 redis 缓存里面。 10分钟过期.

/**
 * 邮件发送信息
 *
 * @author yuejianli
 * @date 2022-06-09
 */
@Service
@Slf4j
public class WeiXinServiceImpl implements WeiXinService {
	private static String sendMessage_url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={0}";
	@Value("${weixin.corpId}")
	private String corpId;
	@Value("${weixin.coprsecret}")
	private String coprsecret;
	@Value("${weixin.agentId}")
	private Integer agentId;

	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 String sendSimpleText(String[] toArr, String content) {
		if (ArrayUtils.isEmpty(toArr)){
			return null;
		}
		// 1. 获取 token
		String accessToken = getWeiXinToken();
		// 2 构建普通文本对象
		Gson gson = new GsonBuilder().disableHtmlEscaping().create();
		TextMessage message = new TextMessage();
		// 1.1非必需
		// 不区分大小写
		message.setTouser(StringUtils.join(toArr,"|"));
		// 1.2必需
		message.setMsgtype("text");
		message.setAgentid(agentId);
		WxText wxText = new WxText();
		wxText.setContent(content);
		message.setText(wxText);
		String jsonMessage = gson.toJson(message);
		// 3. 发送 json 形式的获取,获取响应信息
		return messageResponse(accessToken, jsonMessage);
	}

	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();
	}

	@Override
	public String sendVelocityText(String title, String[] toArr, Map<String, Object> dataMap, String templateName) {
		if (ArrayUtils.isEmpty(toArr)){
			return null;
		}
		// 1.获取access_token:根据企业id和应用密钥获取access_token,并拼接请求url
		String accessToken = getWeiXinToken();
		// 2.获取发送对象,并转成json
		Gson gson = new GsonBuilder().disableHtmlEscaping().create();
		TextCardMessage textCardMessage = new TextCardMessage();
		// 1.1非必需
		// 不区分大小写
		textCardMessage.setTouser(StringUtils.join(toArr,"|"));
		//message.setToparty("1");
		//message.getTouser(totag);
		// 1.2必需
		textCardMessage.setMsgtype("textcard");
		textCardMessage.setAgentid(agentId);
		WxTextCard wxTextCard = new WxTextCard();
		wxTextCard.setTitle(title);
		wxTextCard.setDescription(getVelocityMailText(dataMap,templateName));
		wxTextCard.setUrl("https://www.yueshushu.top");
		textCardMessage.setTextcard(wxTextCard);
		String jsonMessage = gson.toJson(textCardMessage);
		// 3.获取请求的url
		return messageResponse(accessToken, jsonMessage);
	}

	/**
	 * 获取微信登录的token
	 * 最好放置在缓存里面,避免重复获取.
	 */
	public String getWeiXinToken() {
		return WeChatUtil.getAccessToken(corpId, coprsecret).getToken();
	}

	/**
	 * 将消息通过企业微信发送给相应的用户
	 *
	 * @param accessToken token信息
	 * @param jsonMessage 发送的消息
	 */
	public String messageResponse(String accessToken, String jsonMessage) {
		String url = MessageFormat.format(sendMessage_url, accessToken);
		// 4.调用接口,发送消息
		JSONObject jsonObject = WeChatUtil.httpRequest(url, "POST", jsonMessage);
		// 4.错误消息处理
		if (null != jsonObject) {
			if (0 != jsonObject.getInteger("errcode")) {
				log.info("消息发送失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"),
						jsonObject.getString("errmsg"));
			}
		}
		return jsonObject.toString();
	}
}

二.五 测试

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

    @Resource
    private WeiXinService weiXinService;

    @Test
    public void simpleEmailTest() {
        String[] toArr = new String[]{"YueJianLi"};
        weiXinService.sendSimpleText(toArr, "你好啊,岳泽霖");
        log.info(">>>企业微信发送消息");
    }

    @Test
    public void velocityTest() {
        String[] toArr = new String[]{"YueJianLi"};
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("line",System.lineSeparator());
        dataMap.put("title","你叫什么名字");
        dataMap.put("content","我叫岳泽霖,是一个快乐的程序员");
        weiXinService.sendVelocityText("你好啊",toArr, dataMap,"interface_tenwhy.vm");
        log.info(">>>企业微信发送消息成功");
    }
}

测试结果: 消息可以正常的接收

image-20230516205129240

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


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


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

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

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

相关文章

ANR基础 - Input系统

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、Input系统概述二、整体框架1.整体框架类图2.核心启动过程2.1 initialize2.1 I…

浅析一下PTPD

浅聊一下PTPD 文章目录 浅聊一下PTPD1.什么是PTPD2.PTPD源码浅析一下1.src文件1.arith.c2.bmc.c3.constant.h 和 datatypes.h4.display.c5.management.c6.protocol.c7.ptp_datatypes.h8.ptp_primitives.h9.ptp_timers.c10.ptpd.c11.signaling.c12.timedomain.c 2.def文件夹3.de…

ROS:gazebo创建仿真地图,turtlebot3加载仿真地图进行建图,生成yaml和pgm地图信息

一.安装turtlebot3 Ubuntu18.04 实现&#xff1a;安装turtlebot3功能包、虚拟机与机器人之间的网络配置、测试机器人Cartographer建图_Charlesffff的博客-CSDN博客 二.安装gazebo ROS18.04&#xff1a;安装gazebo&#xff0c;下载模型_gazebo下载模型_Charlesffff的博客-CSD…

Linux 设备驱动程序(二)

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核&#xff08;一&#xff09; 深入理解 Linux 内核&#xff08;二&#xff09; Linux 设备驱动程序&#xff08;一&#xff09; Linux 设备驱动程序&#xff08;二&#xff09; Linux设备驱动开发详解 文章目录 系列文章目…

大型央企集团财务经营分析框架系列(三)

01集团经营管理分析的切入点 集团经营管理分析的切入点往往是从财务分析开始。 往往在一家企业里面&#xff0c;财务方面的信息化建设是要早于其它方面的信息化建设的&#xff0c;业务标准化程度比较高&#xff0c;数据标准化程度也比较高&#xff0c;分析框架也相对成熟。 …

栈和队列的相关功能实现及其基础应用

前言&#xff1a;栈和队列是常见的数据结构&#xff0c;它们在计算机科学中被广泛应用。栈和队列都是一些元素的集合&#xff0c;它们的主要区别在于数据的组织方式和访问顺序。在栈中&#xff0c;元素的添加和删除都在同一端进行&#xff0c;称为栈顶&#xff0c;而在队列中&a…

PMP考试100个主要知识点

1.一个项目在启动阶段会进行量级估算&#xff0c;准确范围是-50至100%。2000版的量级估算准确度为&#xff1a;-25%到75%。 2.质量控制通常先于范围确认执行&#xff0c;但这两个过程可以并列进行参考 3.Cost-plus-fixed-fee(CPFF)成本加固定费用合同。成本补偿型合同包括成本加…

快速入门ChatGPT和AIGC:底层原理、热门工具、行业现状【我们能做什么】

最近大家热议的ChatGPT和AI绘画工具的底层技术原理是什么&#xff1f;是如何发展到现在的&#xff1f;有哪些应用场景、热门工具&#xff1f;AIGC产业上下游有哪些公司&#xff1f;作为普通用户&#xff0c;我们还能接触哪些应用AI技术打造的商业解决方案&#xff1f;…… 我们…

微信小程序 录音+播放组件封装

展示 长按录音 松开结束录音 点击播放 再次点击暂停 再次点击继续播放 展示效果&#xff1a; 录音功能 录音功能&#xff08;手指按下开始录音 手指松开结束录音&#xff09;&#xff1a; 使用wx原生录音功能在 component 外新建 wx.getRecorderManager() RecorderManager…

国巨 :硬件设计基础60条

硬件设计是现代科技发展中至关重要的领域之一。它涵盖了从微电子器件到复杂的系统级设计的各个方面&#xff0c;是现代电子产品的核心。在这篇文章中&#xff0c;我将介绍60个基础概念&#xff0c;这些概念是硬件设计工程师必备的知识&#xff0c;并且是设计出高质量硬件的关键…

【web】学习ajax和fetch

1/什么是ajax ajax 全名 async javascript and XML(异步JavaScript和XML) 是前后台交互的能⼒。 也就是我们客户端给服务端发送消息的⼯具&#xff0c;以及接受响应的⼯具。 在不重新加载整个网页的情况下&#xff0c;对网页的某部分进行更新。而传统的网页(不使用 Ajax)如果需…

设计师必备的5个素材库,马住

今天就告诉大家设计师都是去哪些网站找素材&#xff0c;分享五个网站&#xff0c;解决你80%的设计素材&#xff0c;建议收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/?vNTYxMjky 这是一个为新手设计师提供免费素材的设计网站&#xff0c;站内有超多平面模板、海报…

成为一名黑客需要学什么

想成为一名专业黑客&#xff0c;但不知道从哪里学起”很多人向盾叔问过这个问题&#xff0c;今天盾叔就为你介绍成为专业黑客必须学习的十个方面的知识&#xff0c;希望能为迷惘中的你指明方向。 一、基本的计算机知识 把它列为第一条&#xff0c;相信很多人肯定会觉得不以为…

Java虚拟机快速入门 | JVM引言、JVM内存结构、直接内存

目录 一&#xff1a;JVM引言 1. 什么是 JVM ? 2. 常见的 JVM 3. 学习路线 二&#xff1a;JVM内存结构 1. 程 序 计 数 器&#xff08;PC Register&#xff09; 2. 虚 拟 机 栈&#xff08;JVM Stacks&#xff09; 3. 本 地 方 法 栈&#xff08;Native Method Stacks&…

Vscode C++环境配置

多文件编译 打开设置搜索coderunner 找到Executor Map 加入-I目录名 目录名/*.cpp 调试 点击调试以后会产生tasks.json文件&#xff0c;加入链接文件和库文件

map用法以及特殊值的情况

map用法以及特殊值的情况 一、map用法的说明 map(callbackFn, thisArg); // callbackFn回调函数&#xff0c;thisArg可选 callbackFn是个回调函数&#xff0c;该回调函数的参数按照顺序为element&#xff08;当前正在处理的元素&#xff09;&#xff0c;index&#xff08;正…

WPF MaterialDesign 初学项目实战(1)首页搭建

前言 最近在学WPF&#xff0c;由于人比较烂&#xff0c;有一个星期没怎么动代码了。感觉有点堕落。现在开始记录WPF项目&#xff0c;使用MaterialDesignInXamlToolkit。 环境搭建 如果没下载MaterialDesign 的源码 github源码运行 在Nuget里面引入MaterialDesign Materia…

数字孪生技术在环境保护领域怎样应用?

近年来&#xff0c;环境保护成为全球范围内的热点话题&#xff0c;各国都在积极探索创新的解决方案。其中&#xff0c;数字孪生技术的出现为环境保护带来了全新的机遇和挑战。数字孪生技术将物理世界与数字世界相结合&#xff0c;通过精确的模拟和实时数据分析&#xff0c;为环…

华为ensp 防火墙的基础配置

拓扑图&#xff1a; [FW3-zone-isp1]set priority 12 #配置防火墙优先级 步骤一 #首先进入防火墙需要输入默认账号和密码&#xff0c;必须修改密码。 [USG6000V1] undo in en #关闭提示。 #先配置ip。 [USG6000V1]ip route-static 0.0.0.0 0.0.0.0 64.1.1.10 #配置去往外网的默…

【Redis】Redisson入门以及Redisson可重入锁的lua脚本实现

目录 一、Redisson介绍 二、Redisson的入门 1、引入依赖 2、配置客户端 3、使用锁 三、Redisson可重入锁的原理 1、原理 2、实现 3、lua脚本保证原子性 1.获取锁 2.释放锁 一、Redisson介绍 在之前的文章里我们通过redis中的setn实现了一个简单的分布式锁以及解决了…