您现在的位置是:首页 >技术杂谈 >SpringBoot整合企业微信消息推送(四十五)网站首页技术杂谈

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

两个蝴蝶飞 2024-06-17 10:26:13
简介SpringBoot整合企业微信消息推送(四十五)

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

上一章简单介绍了 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


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

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。