前言
公司开发OA流程审批需要通知用户,所以选择企微对接,我们选择的图文消息发送,当然其他的也都一样的发送格式,只是传参有变化而已。
效果展示
实战代码
1、首先登录企微管理后台,需要管理员扫码登录。
2、加入依赖。
<!-- 企业微信配置依赖 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-cp</artifactId>
<version>4.0.8.B</version>
</dependency>
3、企微发送工具类,每个方法有详细的注释。
package com.sansint.oa.utils;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.sansint.core.redis.RedisUtil;
import com.sansint.oa.config.QiWeiConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* @description: 发送企微消息
* @date 2025年04月15日 13:28
*/
@Component
@Slf4j
public class SendQiWeiMessageUtil {
@Resource
private QiWeiConfiguration qiWeiConfiguration;
@Resource
private RedisUtil redisUtil;
/***
* 入口方法
*/
public void sendMessage(String phone, String item, String itemContent, String content) {
try {
String accessToken = getAccessToken();
//根据手机号获取userId
String userId = getUserIdByPhone(phone, accessToken);
if (StringUtils.isNotBlank(userId)) {
sendCardMessageToUser(userId, item, itemContent, content, accessToken);
}
} catch (Exception e) {
log.error("发送企微消息失败", e);
}
}
/***
* 发送卡片文字给指定用户
* @param userId 用户id
* @param item 标题
* @param content 内容
* @param token token
*/
public void sendCardMessageToUser(String userId, String item, String itemContent, String content, String token) {
// 构造请求 URL
String urlStr = qiWeiConfiguration.getSendMessageUrl() + token;
HttpURLConnection connection = null;
try {
// 创建 URL 和连接
URL url = new URL(urlStr);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
// 构造消息内容
JSONObject messageData = new JSONObject();
messageData.put("touser", userId);
messageData.put("msgtype", "textcard");
messageData.put("agentid", qiWeiConfiguration.getAgentId());
JSONObject text = new JSONObject();
text.put("title", item);
// text.put("description", "<div class=\\\"normal\\\">" + itemContent + "</div><div class=\\\"highlight\\\">" + content + "</div>");
String now = LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy年MM月dd日 HH:mm");
text.put("description", "<div class=\"gray\">" + now + "</div> <div class=\"normal\">" + itemContent + "</div><div class=\"highlight\">" + content + "</div>");
text.put("url", "http://bank.sansint.com:86");
messageData.put("textcard", text);
log.info("发送文本卡片消息给指定的用户 : messageData:{}", messageData);
// 发送请求数据
OutputStream os = null;
PrintWriter out = null;
try {
os = connection.getOutputStream();
out = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
out.write(messageData.toString());
out.flush();
} finally {
if (os != null) os.close();
if (out != null) out.close();
}
// 获取响应
StringBuilder response = new StringBuilder();
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
}
// 解析响应
JSONObject jsonResponse = new JSONObject(response.toString());
log.info("发送文本卡片消息给指定的用户:解析响应:{}", jsonResponse);
// 检查响应状态
if (jsonResponse.getInt("errcode") == 0) {
log.info("发送文本卡片消息给指定的用户消息发送成功!");
} else {
log.error("发送消息失败: {}", jsonResponse.getString("errmsg"));
}
} catch (IOException e) {
log.error("网络请求失败: {}", e.getMessage(), e);
} catch (JSONException e) {
log.error("JSON 解析失败: {}", e.getMessage(), e);
} catch (Exception e) {
log.error("发生未知错误: {}", e.getMessage(), e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
/**
* 通过手机号查找 userId
* 目前手机号是在企微管理后台找到的并和用户表绑定在一起,获取是定时任务每天查询用户表存到redis中,在这直接拿的手机号
*/
public String getUserIdByPhone(String phone, String token) {
// 构造请求 URL
String urlStr = qiWeiConfiguration.getUserByPhoneUrl() + token;
log.info("企微通过手机号查找请求URL: {}", urlStr);
HttpURLConnection connection = null;
try {
// 创建 URL 和连接
URL url = new URL(urlStr);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
// 构造消息内容
JSONObject messageData = new JSONObject();
messageData.put("mobile", phone);
// 发送请求数据
try (OutputStream os = connection.getOutputStream()) {
os.write(messageData.toString().getBytes(StandardCharsets.UTF_8));
os.flush();
}
// 获取响应
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
StringBuilder response = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
// 解析响应
JSONObject jsonResponse = new JSONObject(response.toString());
log.info("企微通过手机号查找响应数据: {}", jsonResponse);
// 获取并返回用户ID
if (jsonResponse.has("userid")) {
return jsonResponse.getString("userid");
} else {
log.error("企微响应数据中未找到userid 字段: {}", jsonResponse);
return "";
}
}
} catch (IOException e) {
log.error("企微请求失败: {},URL: {},请求数据: {}", e.getMessage(), urlStr, phone, e);
} catch (Exception e) {
log.error("企微发生异常: {}", e.getMessage(), e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
return "";
}
// 获取企业微信的 access_token
public String getAccessToken() {
// 尝试从缓存中获取 token
Object qiweiToken = redisUtil.get("qiwei_token");
if (Objects.nonNull(qiweiToken)) {
log.info("Redis 中获取企微 access_token: {}", qiweiToken);
return qiweiToken.toString();
}
// 如果缓存中没有 token,则从外部 API 获取
return fetchAccessTokenFromApi();
}
/**
* 获取token
*/
private String fetchAccessTokenFromApi() {
try {
URL url = new URL(qiWeiConfiguration.getAccessTokenUrl());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
// 使用 try-with-resources 确保资源关闭
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
StringBuilder response = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
JSONObject jsonResponse = new JSONObject(response.toString());
log.info("企微调用第三方接口获取数据: {}", jsonResponse);
if (jsonResponse.getInt("errcode") != 0) {
log.error("企微获取 access_token 失败,错误代码: {}", jsonResponse.getInt("code"));
return null;
}
String accessToken = jsonResponse.getString("access_token");
log.info("企微获取 access_token 成功: {}", accessToken);
// 存入 Redis,并设置过期时间为7100秒
redisUtil.set("qiwei_token", accessToken, 7100);
log.info("已存入 Redis,过期时间为7100秒: {}", accessToken);
return accessToken;
} catch (IOException e) {
log.error("请求获取 access_token 时发生错误: {}", e.getMessage(), e);
}
} catch (Exception e) {
log.error("获取 access_token 过程中发生错误: {}", e.getMessage(), e);
}
return null;
}
/**
* 发送消息给指定的用户
*/
public void sendMessageToUser(String userId, String message, String token) {
try {
String urlStr = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + token;
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
// 构造消息内容
JSONObject messageData = new JSONObject();
messageData.put("touser", userId);
messageData.put("msgtype", "text");
messageData.put("agentid", "1000006");
JSONObject text = new JSONObject();
text.put("content", message);
messageData.put("text", text);
// 发送请求
OutputStream os = connection.getOutputStream();
os.write(messageData.toString().getBytes("UTF-8"));
os.flush();
os.close();
// 获取响应
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 解析响应
JSONObject jsonResponse = new JSONObject(response.toString());
log.info("发送消息给指定的用户:jsonResponse:{}", jsonResponse);
if (jsonResponse.getInt("errcode") == 0) {
System.out.println("Message sent successfully!");
} else {
System.out.println("Error sending message: " + jsonResponse.getString("errmsg"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4、上面的 qiWeiConfiguration是配置类,读取的配置文件的企微配置,redisUtil我们用的公司自有的工具类,可以用SpingBoot自带的redis模版使用,
/** * @version V1.0 * 企微配置 * @date 2022-03-11 11:03 */ @Data @Configuration @ConfigurationProperties(prefix = "qiwei") public class QiWeiConfiguration { private String corpId; private String corpSecret; private String accessTokenUrl; private String userByPhoneUrl; private String sendMessageUrl; private String agentId; }
5、nacos的配置
qiwei:
corpId: 管理后台企业id
corpSecret: 管理后台secrrt
accessTokenUrl: https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ww2cfd1fb2f4ea2d5d&corpsecret=sCqta5ungP66ROLC3txe0Hod3Y0M5_oSygbOkxXSgpg
userByPhoneUrl: https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=
sendMessageUrl: https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=
agentId: 自建应用id
对应企微相关的API接口链接:发送应用消息 - 文档 - 企业微信开发者中心
6、实际使用的代码,工具类中也有写到,发送需要用户ID,这个用户ID我是调用的根据手机号查询出来具体的用户ID,这个手机号是在企微管理后台和用户表绑定的,我是定时任务获取存入缓存,用的时候直接根据用户表id查询出手机号传入企微工具类直接使用。
//发送企微
// 获取用户信息
Object phone = redisUtil.get("oaPhone_" + person);
if (Objects.isNull(phone)) {
R<User> userR = userClient.userInfoById(Long.valueOf(person));
if (Objects.nonNull(userR)) {
phone = userR.getData().getPhone();
if (!Objects.isNull(phone)) {
redisUtil.set("oaPhone_" + person, phone);
}
}
}
//发送企微消息
if (Objects.nonNull(phone)) {
sendQiWeiMessageUtil.sendMessage(phone.toString(), "审批中心", "您有一条" + start.getCreator() + "发起的待办流程,请及时处理!", detail.getProcessName() + "-" + detail.getStepName());
}