1. 实体类
package com.robot.message;
/**
* 群机器人的消息类型.
*/
public class GroupRobotMsgType {
/**
* 文本消息.
*/
public static final String TEXT = "text";
/**
* 图片消息.
*/
public static final String IMAGE = "image";
/**
* markdown消息.
*/
public static final String MARKDOWN = "markdown";
/**
* 图文消息(点击跳转到外链).
*/
public static final String NEWS = "news";
}
//-------------------------------------------------------------------
package com.robot.message;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 图文类型
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class NewArticle implements Serializable {
private static final long serialVersionUID = 4087852055781140659L;
/**
* 标题,不超过128个字节,超过会自动截断
*/
private String title;
/**
* 描述,不超过512个字节,超过会自动截断
*/
private String description;
/**
* 点击后跳转的链接。
*/
private String url;
/**
* 图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图1068*455,小图150*150。
*/
private String picUrl;
/**
* 按钮文字,仅在图文数为1条时才生效。 默认为“阅读全文”, 不超过4个文字,超过自动截断。该设置只在企业微信上生效,微工作台(原企业号)上不生效。
*/
private String btnText;
}
//-------------------------------------------------------------------
package com.robot.message;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 微信群机器人消息
*
* @author xiaomingzhang
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class WxCpGroupRobotMessage {
/**
* 消息类型
*/
private String msgType;
/**
* 文本内容,最长不超过2048个字节,markdown内容,最长不超过4096个字节,必须是utf8编码
* 必填
*/
private String content;
/**
* userid的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userid,可以使用mentioned_mobile_list
*/
private List<String> mentionedList;
/**
* 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人
*/
private List<String> mentionedMobileList;
/**
* 图片内容的base64编码
*/
private String base64;
/**
* 图片内容(base64编码前)的md5值
*/
private String md5;
/**
* 图文消息,一个图文消息支持1到8条图文
*/
private List<NewArticle> articles;
public String toJson() {
JSONObject messageJson = new JSONObject().set("msgtype", this.getMsgType());
switch (this.getMsgType()) {
case GroupRobotMsgType.TEXT: {
JSONObject text = new JSONObject();
JSONArray uidJsonArray = new JSONArray();
JSONArray mobileJsonArray = new JSONArray();
text.set("content", this.getContent());
if (this.getMentionedList() != null) {
for (String item : this.getMentionedList()) {
uidJsonArray.add(item);
}
}
if (this.getMentionedMobileList() != null) {
for (String item : this.getMentionedMobileList()) {
mobileJsonArray.add(item);
}
}
text.set("mentioned_list", uidJsonArray);
text.set("mentioned_mobile_list", mobileJsonArray);
messageJson.set("text", text);
break;
}
case GroupRobotMsgType.MARKDOWN: {
JSONObject text = new JSONObject().set("content", this.getContent());
messageJson.set("markdown", text);
break;
}
case GroupRobotMsgType.IMAGE: {
JSONObject text = new JSONObject()
.set("base64", this.getBase64())
.set("md5", this.getMd5());
messageJson.set("image", text);
break;
}
case GroupRobotMsgType.NEWS: {
JSONObject text = new JSONObject();
JSONArray array = new JSONArray();
for (NewArticle article : this.getArticles()) {
JSONObject articleJson = new JSONObject()
.set("title", article.getTitle())
.set("description", article.getDescription())
.set("url", article.getUrl())
.set("picurl", article.getPicUrl());
array.add(articleJson);
}
text.set("articles", array);
messageJson.set("news", text);
break;
}
default:
}
return messageJson.toString();
}
}
2. 接口实现
package com.robot.service;
import com.robot.message.NewArticle;
import java.util.List;
/**
* 微信群机器人消息发送api<br/>
*
* @author xiaomingzhang
* @see <a href="https://work.weixin.qq.com/help?doc_id=13376">文档地址</a>
* @see <a href="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=">调用地址</a>
*/
public interface WxCpGroupRobotService {
/**
* 发送text类型的消息
*
* @param content 文本内容,最长不超过2048个字节,必须是utf8编码
* @param mentionedList userId的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userId,可以使用mentioned_mobile_list
* @param mobileList 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人
*/
void sendText(String content, List<String> mentionedList, List<String> mobileList);
/**
* 发送markdown类型的消息
*
* @param content markdown内容,最长不超过4096个字节,必须是utf8编码
*/
void sendMarkdown(String content);
/**
* 发送image类型的消息
*
* @param base64 图片内容的base64编码
* @param md5 图片内容(base64编码前)的md5值
*/
void sendImage(String base64, String md5);
/**
* 发送news类型的消息
*
* @param articleList 图文消息,支持1到8条图文
*/
void sendNews(List<NewArticle> articleList);
/**
* 发送text类型的消息
*
* @param webhookUrl webhook地址
* @param content 文本内容,最长不超过2048个字节,必须是utf8编码
* @param mentionedList userId的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userId,可以使用mentioned_mobile_list
* @param mobileList 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人
*/
void sendText(String webhookUrl, String content, List<String> mentionedList, List<String> mobileList);
/**
* 发送markdown类型的消息
*
* @param webhookUrl webhook地址
* @param content markdown内容,最长不超过4096个字节,必须是utf8编码
*/
void sendMarkdown(String webhookUrl, String content);
/**
* 发送image类型的消息
*
* @param webhookUrl webhook地址
* @param base64 图片内容的base64编码
* @param md5 图片内容(base64编码前)的md5值
*/
void sendImage(String webhookUrl, String base64, String md5);
/**
* 发送news类型的消息
*
* @param webhookUrl webhook地址
* @param articleList 图文消息,支持1到8条图文
*/
void sendNews(String webhookUrl, List<NewArticle> articleList);
}
//------------------------------------------------------------------
package com.robot.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.robot.WxErrorException;
import com.robot.message.GroupRobotMsgType;
import com.robot.message.NewArticle;
import com.robot.message.WxCpGroupRobotMessage;
import com.robot.service.WxCpGroupRobotService;
import lombok.SneakyThrows;
import java.util.List;
/**
* 企业微信群机器人消息发送API 实现
*
* @author xiaomingzhang
*/
public class WxCpGroupRobotServiceImpl implements WxCpGroupRobotService {
public static final String CP_BASE_URL = "https://qyapi.weixin.qq.com";
public static final String WEBHOOK_SEND = "/cgi-bin/webhook/send?key=";
public final String webhookKey;
@SneakyThrows
public WxCpGroupRobotServiceImpl(String webhookKey) {
if (StrUtil.isBlank(webhookKey)) {
throw new WxErrorException("无效的企业微信群机器人KEY");
}
this.webhookKey = webhookKey;
}
private String getWebhookUrl() {
return CP_BASE_URL + WEBHOOK_SEND + webhookKey;
}
@Override
public void sendText(String content, List<String> mentionedList, List<String> mobileList) {
this.sendText(this.getWebhookUrl(), content, mentionedList, mobileList);
}
@Override
public void sendMarkdown(String content) {
this.sendMarkdown(this.getWebhookUrl(), content);
}
@Override
public void sendImage(String base64, String md5) {
this.sendImage(this.getWebhookUrl(), base64, md5);
}
@Override
public void sendNews(List<NewArticle> articleList) {
this.sendNews(this.getWebhookUrl(), articleList);
}
@Override
public void sendText(String webhookUrl, String content, List<String> mentionedList, List<String> mobileList) {
HttpUtil.post(webhookUrl, new WxCpGroupRobotMessage()
.setMsgType(GroupRobotMsgType.TEXT)
.setContent(content)
.setMentionedList(mentionedList)
.setMentionedMobileList(mobileList)
.toJson());
}
@Override
public void sendMarkdown(String webhookUrl, String content) {
HttpUtil.post(webhookUrl, new WxCpGroupRobotMessage()
.setMsgType(GroupRobotMsgType.MARKDOWN)
.setContent(content)
.toJson());
}
@Override
public void sendImage(String webhookUrl, String base64, String md5) {
HttpUtil.post(this.getWebhookUrl(), new WxCpGroupRobotMessage()
.setMsgType(GroupRobotMsgType.IMAGE)
.setBase64(base64)
.setMd5(md5).toJson());
}
@Override
public void sendNews(String webhookUrl, List<NewArticle> articleList) {
HttpUtil.post(this.getWebhookUrl(), new WxCpGroupRobotMessage()
.setMsgType(GroupRobotMsgType.NEWS)
.setArticles(articleList).toJson());
}
}
3. 异常类
package com.robot.exception;
/**
* WxErrorException
*/
public class WxErrorException extends Exception {
// ~ Constructors
// ===================================================================================================
/**
* Constructs a <code>WxErrorException</code> with the specified message.
*
* @param msg the detail message
*/
public WxErrorException(String msg) {
super(msg);
}
/**
* Constructs a <code>WxErrorException</code> with the specified message and
* root cause.
*
* @param msg the detail message
* @param t root cause
*/
public WxErrorException(String msg, Throwable t) {
super(msg, t);
}
}