Java 对接企业微信(文本消息推送)

Java 对接企业微信(文本消息推送)

  • 背景
  • 版本
  • 代码
    • POM
    • 配置实体
    • 工具类
    • 发送消息
    • 测试
    • 配置文件
    • 配置文件中的参数来源
    • secret
    • corpid
    • agentid
  • 执行
    • 异常
    • 原因
  • 文档

背景

公司的项目,通知信息打算接入企业微信通知。提前做下实验。

版本

JDK 21
SpringBoot 3.1.5
weixin-java-cp 4.5.0
hutool-all 5.8.23
jedis 5.1.0

说明:暂时只是一个springboot小demo,后续根据项目会有组件和结构的修改。

代码

代码比较简单,配置好参数直接调用即可。
这里全部贴到这里,后面会讲解每一步所需要的参数来源及怎么配置。

POM

    <properties>
        <java.version>21</java.version>
        <snakeyaml.version>2.2</snakeyaml.version>
        <jakarta.validation-api.version>3.0.2</jakarta.validation-api.version>
        <hibernate-validator.version>8.0.1.Final</hibernate-validator.version>
        <fastjson.version>2.0.43</fastjson.version>
        <weixin-java-cp.version>4.5.0</weixin-java-cp.version>
        <hutool-all.version>5.8.23</hutool-all.version>
        <jedis.version>5.1.0</jedis.version>
    </properties>
<dependencies>
        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool-all.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>${jedis.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </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>
        <!-- https://mvnrepository.com/artifact/com.github.binarywang/weixin-java-cp -->
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-cp</artifactId>
            <version>${weixin-java-cp.version}</version>
        </dependency>

        <dependency>
            <groupId>jakarta.validation</groupId>
            <artifactId>jakarta.validation-api</artifactId>
            <version>${jakarta.validation-api.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>${hibernate-validator.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
    </dependencies>

配置实体

@Data
@Component
@ConfigurationProperties(value = "wechat2")
public class WechatConfig {
    /**
     * 要发送消息的应用的两个字段
     */
    @Value("${wechat2.agentid}")
    private Integer agentId;
    /**
     *
     */
    @Value("${wechat2.secret}")
    private String secret;
    /**
     * 企业id
     */
    @Value("${wechat2.corpid}")
    private String corpId;
    /**
     * 用于访问redis的密码,没设置就不需要这个字段
     */
    @Value("${wechat2.redis-pwd}")
    private String redisPwd;
}

工具类

@Slf4j
@Component
public class WechatConfigUtil {
    @Resource
    private WechatConfig wechatConfig;

    /**
     * 配置企业微信服务
     *
     * @return -
     */
    public WxCpService wxCpService() {
        WxCpService wxCpService = new WxCpServiceImpl();
        WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();
        config.setAgentId(wechatConfig.getAgentId());
        config.setCorpSecret(wechatConfig.getSecret());
        config.setCorpId(wechatConfig.getCorpId());
        resetTokenAndJsApi(wxCpService, config);
        return wxCpService;
    }

    /**
     * 重置token
     *
     * @param wxCpService       -
     * @param wxCpDefaultConfig -
     */
    public void resetTokenAndJsApi(WxCpService wxCpService, WxCpDefaultConfigImpl wxCpDefaultConfig) {
        JedisPool jedisPool = new JedisPool();
        Jedis jedis = jedisPool.getResource();
        if (StringUtils.isNotBlank(wechatConfig.getRedisPwd())) {
            jedis.auth(wechatConfig.getRedisPwd());
        }
        wxCpService.setWxCpConfigStorage(wxCpDefaultConfig);
        String wxAccessToken = "wx" + wechatConfig.getAgentId();
        // 根据应用id获取对应token
        String json = jedis.get(wxAccessToken);
        if (!StringUtils.isEmpty(json)) {
            wxCpDefaultConfig = JSON.parseObject(json, WxCpDefaultConfigImpl.class);
        }
        // token到期
        if (wxCpDefaultConfig.isAccessTokenExpired()) {
            try {
                String accessToken;
                accessToken = wxCpService.getAccessToken(false);
                wxCpDefaultConfig.setAccessToken(accessToken);
            } catch (WxErrorException e) {
                log.error(e.getMessage());
            }
        }
        // 缓存token
        jedis.set(wxAccessToken, JSON.toJSONString(wxCpDefaultConfig));
        jedis.close();
    }
}

发送消息

@Slf4j
@AllArgsConstructor
@NoArgsConstructor
@Component
public class SendWxCpMsg {
    @Autowired
    private WechatConfigUtil wechatConfigUtil;

    public void sendToWxCp(String username) {
        // 微信消息对象
        WxCpMessageServiceImpl wxCpMessageService = new WxCpMessageServiceImpl(wechatConfigUtil.wxCpService());
        WxCpMessage wxCpMessage = new WxCpMessage();
        wxCpMessage.setSafe("0");
        // 设置消息形式,这里设置为卡片消息
        wxCpMessage.setMsgType("textcard");

        var time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
        // 设置发送用户
        wxCpMessage.setToUser(username);
        // 发送的标题
        wxCpMessage.setTitle("通知标题-TEST");
        // 发送内容
        wxCpMessage.setDescription("祝新年快乐,幸福安康!"
                + "\n" + "当前时间为:"
                + time + "\n"
                + "点击下方领取祝福");
        // 设置跳转url
        wxCpMessage.setUrl("https://developer.work.weixin.qq.com/community/question");
        wxCpMessage.setBtnTxt("前往");
        try {
            // 发送消息
            WxCpMessageSendResult send = wxCpMessageService.send(wxCpMessage);
            log.info("send result:{}", send);
        } catch (WxErrorException e) {
            log.error("发送信息接口调用出错: {}", e.getMessage());
        }
    }
}

测试

@RestController
public class MsgSendController {
    @Resource
    private SendWxCpMsg sendWxCpMsg;

    @GetMapping("/send")
    private void send() {
        sendWxCpMsg.sendToWxCp("Xxxxxx.Xxx");
    }
}

配置文件

wechat2:
  corpid: xxxxxxx1
  agentid: 1000002
  secret: xxxxxxx2
  redis-pwd:

配置文件中的参数来源

secret

在这里插入图片描述

corpid

在这里插入图片描述

agentid

在这里插入图片描述

执行

到这里,基本的配置已经OK,可以跑下代码。

异常

会报如下异常:
在这里插入图片描述

【错误信息】:错误代码:60020, 错误信息:访问ip不在白名单之中;请确认访问ip是否在服务商白名单IP列表,微信原始报文:{"errcode":60020,"errmsg":"not allow to access from your ip, hint: [170201673442683], from ip: xxx.7xx.xxx.10, more info at https://open.work.weixin.qq.com/devtool/query?e=60020"}

原因

没有配置可信域名
在这里插入图片描述
这里如何配置?
见官网:设置可信域名时,提示“检查域名所有权不通过”

注意:它这里必须要有域名可以公网访问,必须要企业微信能调通,不然不会通过的。

OK,也就是到这里,就终止了实验。
后面公司申请流程通过之后,拿到公司域名再来实验,bye。

文档

  • 个人申请企业微信,进入工作台地址:企业微信工作台
  • 有不清楚的术语可以参考企业微信开发者中心:企业微信开发者中心
  • 可参考 https://blog.csdn.net/weixin_50495215/article/details/132903103?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-132903103-blog-122540462.235%5Ev39%5Epc_relevant_3m_sort_dl_base4&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-132903103-blog-122540462.235%5Ev39%5Epc_relevant_3m_sort_dl_base4&utm_relevant_index=5

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

故障排查方法与技巧

判断网络是否稳定&#xff0c;最重要的两个命令 ping 10.28.0.23 -t -l 1000 -t &#xff1a;无限循环ping -l&#xff1a;指定数据包大小 内网环境< 1ms,是较好的网络&#xff0c;如果跳到100多&#xff0c;说明网络不稳定 telnet ip地址空格端口号 表示不通 数据库…

老师发成绩单攻略:5种方法让群发成绩变得更高效

作为老师&#xff0c;发布成绩单是一项重要的任务。为了更高效地完成这项任务&#xff0c;本文将介绍5种方法&#xff0c;帮助老师群发成绩单更加高效。 一、提前规划&#xff0c;做好准备 在发布成绩单之前&#xff0c;老师需要提前规划好发布的时间、方式、接收对象等&#…

路径总和(递归)

112. 路径总和 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &a…

34、卷积实战 - 手写一个基础卷积算法

前面基本上把卷积这一算法的原理和公式介绍完了,如果还有不懂的,可以多翻几遍前面的章节内容,深入理解一下。 本节加一个实战,大家可以手动来实现一个卷积算法,本文中以 python 代码为例,C++ 的代码可以查看本节后面的链接。 说到卷积实现,其实就是自己手写一个卷积算…

js基础:简介、变量与数据类型、流程循环控制语句、数组及其api

JS基础&#xff1a;简介、变量与数据类型、流程循环控制语句、数组及其api 一、简介 1、js概述 tip&#xff1a;JavaScript是什么&#xff1f; 有什么作用&#xff1f; JavaScript&#xff08;简称JS&#xff09;是一种轻量级的、解释性的编程语言&#xff0c;主要用于在网页…

SpringCloud微服务(简略笔记二)

Docker 概念 docker和虚拟机的差异 * docker是一个系统进程&#xff1b;虚拟机是在操作系统中的操作系统 * docker体积小&#xff0c;启动速度&#xff0c;性能好&#xff0c;虚拟机体积大&#xff0c;启动速度慢&#xff0c;性能一般 镜像和容器 镜像&#xff08;image&…

多模态统计图表综述:图表分类,图表理解,图表生成,图表大一统模型

Overview 多模态统计图表综述一、图表分类1.1 Survey1.2 常见分类数据集&#xff1a;1.3 常见图表类型 二、图表理解2.1 VQA2..1.1 DVQA CVPR20182.1.2 PlotQA 20192.1.3 ChartQA 2022 2.2 Summary2.2.1 Chart-to-text ACL 2022 三、图表生成四、图表大一统模型4.1 UniChart 20…

stm32 使用18B20 测试温度

用18b20 测试温度是非常常用的&#xff0c;不过18B20的调试不是这么容易的&#xff0c;有些内容网上很多的&#xff0c;不再重复说了&#xff0c;我先把波形说一下&#xff0c;再说程序部分&#xff1a; 整个都温度数据的顺序是&#xff1a; 1.700uS的低电平复位并测试18B20的…

【matlab进阶学习-6】 读取log数据data.txt文件,并做处理,导出报告/表格/图表

原始文件 原始文件格式txt&#xff0c;每一行对应一个数据&#xff0c;数据之间由逗号分割开 对应意思 时刻&#xff0c;电压&#xff0c;电流&#xff0c;功率&#xff0c;容量&#xff0c;&#xff0c;电流&#xff0c;功率&#xff0c;&#xff0c;RTC时间&#xff0c;状态…

什么是电商价格监控

电商价格监控是一种系统爬取数据后的实现动作&#xff0c;比起含义&#xff0c;其实更应该关注为什么要做电商价格监控&#xff0c;电商价格监控的执行前提是品牌要治理渠道&#xff0c;需要将电商平台的低价链接打击干净&#xff0c;那就需要先对低价链接进行确认、筛选&#…

YOLOv8改进 | 2023主干篇 | 利用轻量化卷积优化PP-HGNetV2改进主干(全网独家创新)

一、本文介绍 Hello&#xff0c;大家好&#xff0c;上一篇博客我们讲了利用HGNetV2去替换YOLOv8的主干&#xff0c;经过结构的研究我们可以发现在HGNetV2的网络中有大量的卷积存在&#xff0c;所以我们可以用一种更加轻量化的卷积去优化HGNetV2从而达到更加轻量化的效果&#…

AspNetCore 中使用 Knife4jUI 更加友好的Swagger界面

&#x1f680;介绍 aspnetcore.knife4j是一个基于.NET Core平台的Swagger UI库&#xff0c;它提供了API文档的生成和管理功能。这个库的前身是swagger-bootstrap-ui&#xff0c;在Java项目中广泛使用&#xff0c;由于其优秀的界面和易用性被许多开发者所推崇。现在&#xff0c…

探索Nginx的奥秘--从代理到负载均衡的艺术实践

文章目录 &#x1f33a;Nginx的引入&#x1f33a;&#x1f33a;深刻理解正向代理与反向代理&#x1f33a;&#x1f339;Reverse proxy&#x1f339;&#x1f339;正向代理与反向代理的区别&#x1f339;&#x1f339;反向代理为什么叫反向代理&#x1f339;&#x1f339;负载均…

MySQL和Redis有什么区别?

目录 一、什么是MySQL 二、什么是Redis 三、MySQL和Redis的区别 一、什么是MySQL MySQL是一种开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它是最流行的数据库之一。MySQL以其高性能、可靠性和易用性而闻名&#xff0c;广泛应用于各种Web应用程序…

27系列DGUS智能屏发布:可实时播放高清模拟信号摄像头视频

针对高清晰度的模拟信号摄像头视频画面的显示需求&#xff0c;迪文特推出27系列DGUS智能屏。该系列智能屏可适配常见的AHD摄像头、CVBS摄像头&#xff0c;支持单路1080P高清显示、两路720P同屏显示&#xff08;同一类型摄像头&#xff09;。用户通过DGUS简单开发即可实现摄像头…

【二分查找】【区间合并】LeetCode2589:完成所有任务的最少时间

作者推荐 【动态规划】【广度优先】LeetCode2258:逃离火灾 本文涉及的基础知识点 二分查找算法合集 有序向量的二分查找&#xff0c;向量只会在尾部增加删除。 题目 你有一台电脑&#xff0c;它可以 同时 运行无数个任务。给你一个二维整数数组 tasks &#xff0c;其中 ta…

万界星空科技MES系统中的生产调度流程

MES系统生产调度的目标是达到作业有序、协调、可控和高效的运行效果&#xff0c;作业计划的快速生成以及面向生产扰动事件的快速响应处理是生产调度系统的核心和关键。 为了顺利生成作业计划&#xff0c;需要为调度系统提供完整的产品和工艺信息&#xff0c;MES系统生成作业计…

Flutter打包iOS苹果IPA应用有哪些优势?如何实现?

Hello各位小伙伴们各位开发者们好&#xff0c;我是咕噜铁蛋&#xff01;&#xff0c;经常和移动应用开发相关的话题打交道的伙伴们都知道。在开发移动应用时&#xff0c;选择合适的打包方式对于应用的发布和分发至关重要。在今天这篇文章中&#xff0c;我将和大家聊聊Flutter打…

【小沐学Python】Python实现语音识别(Whisper)

文章目录 1、简介1.1 whisper简介1.2 whisper模型 2、安装2.1 whisper2.2 pytorch2.3 ffmpeg 3、测试3.1 命令测试3.2 代码测试&#xff1a;识别声音文件3.3 代码测试&#xff1a;实时录音识别 4、工具4.1 WhisperDesktop4.2 Buzz4.3 Whisper-WebUI 结语 1、简介 https://gith…

基于QTreeWidget实现多级组织结构

基于QTreeWidget实现多级组织结构以及带Checkbox的选择树 采用基于QWidgetMingw实现的原生的消息气泡 通过QTreeWidget控件实现的多级组织结构树。 基于QTreeWidget实现多级组织结构代码已上传到【https://gitee.com/duyanjun/bubbleChat.git】 目录 基于QTreeWidget实现多级组…