企业微信群机器人消息推送文档地址
链接: 企业微信群机器人消息推送配置说明
java
import java.io.File;
/**
* 企业微信机器人消息推送
* @author zhou
*/
public interface WxWebHookHttpClient {
/**
* 消息推送
* @param robotId 机器人key
* @param body 消息参数
* @return
*/
WebhookResponse send(String robotId, WebhookBody body);
/**
* 消息推送
* @param robotId 机器人key
* @param jsonParam 消息参数JSON格式
* @return
*/
WebhookResponse send(String robotId, String jsonParam);
/**
* 上传文件
* 使用multipart/form-data POST上传文件或语音, 文件标识名为"media"
*
* 所有类型的文件大小均要求大于5个字节
* 普通文件(file):文件大小不超过20M
* 语音(voice):文件大小不超过2M,播放长度不超过60s,仅支持AMR格式
*
* @param robotId 机器人key
* @param type 文件类型,分别有语音(voice)和普通文件(file)
* @param file
* @return
*/
WebhookUploadMediaResponse uploadFile(String robotId, String type, File file);
}
java
public abstract class WebhookBody {
private final String msgtype;
protected WebhookBody(String msgtype) {
this.msgtype = msgtype;
}
public String getMsgtype() {
return msgtype;
}
@Override
public String toString() {
return "WebhookBody(msgtype=" + this.getMsgtype() + ")";
}
}
java
/**
* 文件类型消息体
*/
public class WebhookFileBody extends WebhookBody {
private final WebhookFile file;
private WebhookFileBody(WebhookFile file) {
super("file");
this.file = file;
}
public WebhookFile getFile() {
return file;
}
@Override
public String toString() {
return "WebhookFileBody{" +
"file=" + file +
'}';
}
/**
* 创建文件消息
* @param mediaId 文件id,通过文件上传接口获取
*/
public static WebhookFileBody from(String mediaId) {
if (mediaId == null || mediaId.trim().isEmpty()) {
throw new IllegalArgumentException("mediaId不能为空");
}
return new WebhookFileBody(new WebhookFile(mediaId));
}
/**
* 文件消息内容
*/
public static class WebhookFile {
/**
* 文件id,通过下文的文件上传接口获取
*/
private String media_id;
public WebhookFile(String media_id) {
this.media_id = media_id;
}
public String getMedia_id() {
return media_id;
}
public void setMedia_id(String media_id) {
this.media_id = media_id;
}
@Override
public String toString() {
return "WebhookFile{" +
"media_id='" + media_id + '\'' +
'}';
}
}
}
java
/**
* 图片类型消息体
*/
public class WebhookImageBody extends WebhookBody {
private final WebhookImage image;
private WebhookImageBody(WebhookImage image) {
super("image");
this.image = image;
}
public WebhookImage getImage() {
return image;
}
@Override
public String toString() {
return "WebhookImageBody{" +
"image=" + image +
'}';
}
/**
* 创建图片消息
* @param base64 图片内容的base64编码
* @param md5 图片内容(base64编码前)的md5值
*/
public static WebhookImageBody from(String base64, String md5) {
return new WebhookImageBody(new WebhookImage(base64, md5));
}
/**
* 图片消息内容
*/
public static class WebhookImage {
/**
* 图片内容的base64编码
*/
private String base64;
/**
* 图片内容(base64编码前)的md5值
*/
private String md5;
public WebhookImage(String base64, String md5) {
this.base64 = base64;
this.md5 = md5;
}
public String getBase64() {
return base64;
}
public void setBase64(String base64) {
this.base64 = base64;
}
public String getMd5() {
return md5;
}
public void setMd5(String md5) {
this.md5 = md5;
}
@Override
public String toString() {
return "WebhookImage{" +
"base64='" + base64 + '\'' +
", md5='" + md5 + '\'' +
'}';
}
}
}
java
public class WebhookMarkdownBody extends WebhookBody {
private final WebhookMarkdown markdown;
private WebhookMarkdownBody(WebhookMarkdown markdown) {
super("markdown");
this.markdown = markdown;
}
public WebhookMarkdown getMarkdown() {
return markdown;
}
@Override
public String toString() {
return "WebhookMarkdownBody{" +
"markdown=" + markdown +
'}';
}
/**
* 构造markdown消息
* @param content markdown内容,最长不超过4096个字节,必须是utf8编码
*/
public static WebhookMarkdownBody from(String content) {
return new WebhookMarkdownBody(new WebhookMarkdown(content));
}
/**
* Markdown消息内容
*/
public static class WebhookMarkdown {
/**
* markdown内容,最长不超过4096个字节,必须是utf8编码
* 支持标准markdown语法,同时支持部分HTML标签
* 支持的颜色标签:<font color="warning">、<font color="comment">、<font color="info">等
*/
private String content;
public WebhookMarkdown(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "WebhookMarkdown{" +
"content='" + content + '\'' +
'}';
}
}
}
java
/**
* 图文类型消息体
*/
public class WebhookNewsBody extends WebhookBody {
/**
* 图文消息,一个图文消息支持1到8条图文
*/
private final WebhookNews news;
private WebhookNewsBody(WebhookNews news) {
super("news");
this.news = news;
}
public WebhookNews getNews() {
return news;
}
@Override
public String toString() {
return "WebhookNewsBody{" +
"news=" + news +
'}';
}
/**
* 创建图文消息
* @param articles 图文列表(1-8条)
*/
public static WebhookNewsBody from(List<Article> articles) {
return new WebhookNewsBody(new WebhookNews(articles));
}
/**
* 图文消息内容
*/
public static class WebhookNews {
private List<Article> articles;
public WebhookNews(List<Article> articles) {
this.articles = articles;
}
public List<Article> getArticles() {
return articles;
}
public void setArticles(List<Article> articles) {
this.articles = articles;
}
@Override
public String toString() {
return "WebhookNews{" +
"articles=" + articles +
'}';
}
}
/**
* 单篇图文
*/
public static class Article {
/**
* 标题,不超过128个字节
*/
private String title;
/**
* 描述,不超过512个字节(可选)
*/
private String description;
/**
* 点击后跳转的链接
*/
private String url;
/**
* 图片链接(可选)
* 支持JPG/PNG格式,推荐尺寸:大图1068 * 455,小图150 * 150
*/
private String picurl;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPicurl() {
return picurl;
}
public void setPicurl(String picurl) {
this.picurl = picurl;
}
@Override
public String toString() {
return "Article{" +
"title='" + title + '\'' +
", description='" + description + '\'' +
", url='" + url + '\'' +
", picurl='" + picurl + '\'' +
'}';
}
}
}
java
public class WebhookResponse {
/**
* 错误码
*/
private Integer errcode;
/**
* 错误提示
*/
private String errmsg;
/**
* 成功响应
*/
public boolean isSuccessful() {
return errcode != null && errcode == 0;
}
/**
* 错误响应
*/
public boolean isError() {
return errcode != null && errcode != 0;
}
public Integer getErrcode() {
return errcode;
}
public void setErrcode(Integer errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
@Override
public String toString() {
return "WebhookResponse{" +
"errcode=" + errcode +
", errmsg='" + errmsg + '\'' +
'}';
}
}
java
public class WebhookTemplateCardNewsNoticeBody extends WebhookBody {
private final TemplateCard template_card;
private WebhookTemplateCardNewsNoticeBody(TemplateCard templateCard) {
super("template_card");
this.template_card = templateCard;
}
public TemplateCard getTemplate_card() {
return template_card;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
WebhookTemplateCardNewsNoticeBody that = (WebhookTemplateCardNewsNoticeBody) o;
return Objects.equals(template_card, that.template_card);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), template_card);
}
@Override
public String toString() {
return "WebhookTemplateCardNewsNoticeBody(super=" + super.toString() +
", template_card=" + template_card + ")";
}
/**
* 创建图文展示模版卡片消息
*
* @param mainTitle 主标题
* @param cardImage 卡片图片
* @param cardAction 卡片点击动作
* @return 消息体
*/
public static WebhookTemplateCardNewsNoticeBody create(MainTitle mainTitle, CardImage cardImage, CardAction cardAction) {
return builder()
.cardType("news_notice")
.mainTitle(mainTitle)
.cardImage(cardImage)
.cardAction(cardAction)
.build();
}
/**
* 创建构建器
*/
public static TemplateCardBuilder builder() {
return new TemplateCardBuilder();
}
/**
* 模版卡片构建器
*/
public static class TemplateCardBuilder {
private String cardType = "news_notice";
private Source source;
private MainTitle mainTitle;
private CardImage cardImage;
private ImageTextArea imageTextArea;
private QuoteArea quoteArea;
private List<VerticalContent> verticalContentList;
private List<HorizontalContent> horizontalContentList;
private List<JumpItem> jumpList;
private CardAction cardAction;
public TemplateCardBuilder cardType(String cardType) {
this.cardType = cardType;
return this;
}
public TemplateCardBuilder source(Source source) {
this.source = source;
return this;
}
public TemplateCardBuilder mainTitle(MainTitle mainTitle) {
this.mainTitle = mainTitle;
return this;
}
public TemplateCardBuilder cardImage(CardImage cardImage) {
this.cardImage = cardImage;
return this;
}
public TemplateCardBuilder imageTextArea(ImageTextArea imageTextArea) {
this.imageTextArea = imageTextArea;
return this;
}
public TemplateCardBuilder quoteArea(QuoteArea quoteArea) {
this.quoteArea = quoteArea;
return this;
}
public TemplateCardBuilder verticalContentList(List<VerticalContent> verticalContentList) {
this.verticalContentList = verticalContentList;
return this;
}
public TemplateCardBuilder horizontalContentList(List<HorizontalContent> horizontalContentList) {
this.horizontalContentList = horizontalContentList;
return this;
}
public TemplateCardBuilder jumpList(List<JumpItem> jumpList) {
this.jumpList = jumpList;
return this;
}
public TemplateCardBuilder cardAction(CardAction cardAction) {
this.cardAction = cardAction;
return this;
}
/**
* 构建模版卡片消息
*/
public WebhookTemplateCardNewsNoticeBody build() {
// 校验必填条件
if (mainTitle == null) {
throw new IllegalArgumentException("main_title为必填项");
}
if (cardImage == null) {
throw new IllegalArgumentException("card_image为必填项");
}
if (cardAction == null) {
throw new IllegalArgumentException("news_notice模版卡片中card_action为必填项");
}
if (imageTextArea != null && imageTextArea.getImage_url() == null) {
throw new IllegalArgumentException("image_text_area中image_url为必填项");
}
TemplateCard templateCard = new TemplateCard(
cardType, source, mainTitle, cardImage, imageTextArea, quoteArea,
verticalContentList, horizontalContentList, jumpList, cardAction
);
return new WebhookTemplateCardNewsNoticeBody(templateCard);
}
}
/**
* 模版卡片
*/
public static class TemplateCard {
/**
* 模版卡片的模版类型,图文展示模版卡片的类型为news_notice
*/
private final String card_type;
/**
* 卡片来源样式信息,不需要来源样式可不填写
*/
private final Source source;
/**
* 模版卡片的主要内容,包括一级标题和标题辅助信息
*/
private final MainTitle main_title;
/**
* 图片样式
*/
private final CardImage card_image;
/**
* 左图右文样式
*/
private final ImageTextArea image_text_area;
/**
* 引用文献样式,建议不与关键数据共用
*/
private final QuoteArea quote_area;
/**
* 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4
*/
private final List<VerticalContent> vertical_content_list;
/**
* 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
*/
private final List<HorizontalContent> horizontal_content_list;
/**
* 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3
*/
private final List<JumpItem> jump_list;
/**
* 整体卡片的点击跳转事件,news_notice模版卡片中该字段为必填项
*/
private final CardAction card_action;
public TemplateCard(String card_type, Source source, MainTitle main_title, CardImage card_image,
ImageTextArea image_text_area, QuoteArea quote_area,
List<VerticalContent> vertical_content_list,
List<HorizontalContent> horizontal_content_list,
List<JumpItem> jump_list, CardAction card_action) {
this.card_type = card_type;
this.source = source;
this.main_title = main_title;
this.card_image = card_image;
this.image_text_area = image_text_area;
this.quote_area = quote_area;
this.vertical_content_list = vertical_content_list;
this.horizontal_content_list = horizontal_content_list;
this.jump_list = jump_list;
this.card_action = card_action;
}
// Getters
public String getCard_type() { return card_type; }
public Source getSource() { return source; }
public MainTitle getMain_title() { return main_title; }
public CardImage getCard_image() { return card_image; }
public ImageTextArea getImage_text_area() { return image_text_area; }
public QuoteArea getQuote_area() { return quote_area; }
public List<VerticalContent> getVertical_content_list() { return vertical_content_list; }
public List<HorizontalContent> getHorizontal_content_list() { return horizontal_content_list; }
public List<JumpItem> getJump_list() { return jump_list; }
public CardAction getCard_action() { return card_action; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TemplateCard that = (TemplateCard) o;
return Objects.equals(card_type, that.card_type) &&
Objects.equals(source, that.source) &&
Objects.equals(main_title, that.main_title) &&
Objects.equals(card_image, that.card_image) &&
Objects.equals(image_text_area, that.image_text_area) &&
Objects.equals(quote_area, that.quote_area) &&
Objects.equals(vertical_content_list, that.vertical_content_list) &&
Objects.equals(horizontal_content_list, that.horizontal_content_list) &&
Objects.equals(jump_list, that.jump_list) &&
Objects.equals(card_action, that.card_action);
}
@Override
public int hashCode() {
return Objects.hash(card_type, source, main_title, card_image, image_text_area,
quote_area, vertical_content_list, horizontal_content_list,
jump_list, card_action);
}
@Override
public String toString() {
return "TemplateCard{" +
"card_type='" + card_type + '\'' +
", source=" + source +
", main_title=" + main_title +
", card_image=" + card_image +
", image_text_area=" + image_text_area +
", quote_area=" + quote_area +
", vertical_content_list=" + vertical_content_list +
", horizontal_content_list=" + horizontal_content_list +
", jump_list=" + jump_list +
", card_action=" + card_action +
'}';
}
}
/**
* 卡片来源样式信息
*/
public static class Source {
/**
* 来源图片的url
*/
private final String icon_url;
/**
* 来源图片的描述,建议不超过13个字
*/
private final String desc;
/**
* 来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色
*/
private final Integer desc_color;
private Source(String icon_url, String desc, Integer desc_color) {
this.icon_url = icon_url;
this.desc = desc;
this.desc_color = desc_color;
}
// Getters
public String getIcon_url() { return icon_url; }
public String getDesc() { return desc; }
public Integer getDesc_color() { return desc_color; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Source source = (Source) o;
return Objects.equals(icon_url, source.icon_url) &&
Objects.equals(desc, source.desc) &&
Objects.equals(desc_color, source.desc_color);
}
@Override
public int hashCode() {
return Objects.hash(icon_url, desc, desc_color);
}
@Override
public String toString() {
return "Source{" +
"icon_url='" + icon_url + '\'' +
", desc='" + desc + '\'' +
", desc_color=" + desc_color +
'}';
}
// Builder
public static class Builder {
private String icon_url;
private String desc;
private Integer desc_color = 0;
public Builder icon_url(String icon_url) {
this.icon_url = icon_url;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public Builder desc_color(Integer desc_color) {
this.desc_color = desc_color;
return this;
}
public Source build() {
return new Source(icon_url, desc, desc_color);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 模版卡片的主要内容
*/
public static class MainTitle {
/**
* 一级标题,建议不超过26个字
*/
private final String title;
/**
* 标题辅助信息,建议不超过30个字
*/
private final String desc;
private MainTitle(String title, String desc) {
this.title = title;
this.desc = desc;
}
// Getters
public String getTitle() { return title; }
public String getDesc() { return desc; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MainTitle mainTitle = (MainTitle) o;
return Objects.equals(title, mainTitle.title) &&
Objects.equals(desc, mainTitle.desc);
}
@Override
public int hashCode() {
return Objects.hash(title, desc);
}
@Override
public String toString() {
return "MainTitle{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
'}';
}
// Builder
public static class Builder {
private String title;
private String desc;
public Builder title(String title) {
this.title = title;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public MainTitle build() {
return new MainTitle(title, desc);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 图片样式
*/
public static class CardImage {
/**
* 图片的url
*/
private final String url;
/**
* 图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3
*/
private final Float aspect_ratio;
private CardImage(String url, Float aspect_ratio) {
this.url = url;
this.aspect_ratio = aspect_ratio;
}
// Getters
public String getUrl() { return url; }
public Float getAspect_ratio() { return aspect_ratio; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CardImage cardImage = (CardImage) o;
return Objects.equals(url, cardImage.url) &&
Objects.equals(aspect_ratio, cardImage.aspect_ratio);
}
@Override
public int hashCode() {
return Objects.hash(url, aspect_ratio);
}
@Override
public String toString() {
return "CardImage{" +
"url='" + url + '\'' +
", aspect_ratio=" + aspect_ratio +
'}';
}
// Builder
public static class Builder {
private String url;
private Float aspect_ratio = 1.3f;
public Builder url(String url) {
this.url = url;
return this;
}
public Builder aspect_ratio(Float aspect_ratio) {
this.aspect_ratio = aspect_ratio;
return this;
}
public CardImage build() {
return new CardImage(url, aspect_ratio);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 左图右文样式
*/
public static class ImageTextArea {
/**
* 左图右文样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序
*/
private final Integer type;
/**
* 点击跳转的url,image_text_area.type是1时必填
*/
private final String url;
/**
* 点击跳转的小程序的appid,必须是与当前应用关联的小程序,image_text_area.type是2时必填
*/
private final String appid;
/**
* 点击跳转的小程序的pagepath,image_text_area.type是2时选填
*/
private final String pagepath;
/**
* 左图右文样式的标题
*/
private final String title;
/**
* 左图右文样式的描述
*/
private final String desc;
/**
* 左图右文样式的图片url
*/
private final String image_url;
private ImageTextArea(Integer type, String url, String appid, String pagepath,
String title, String desc, String image_url) {
this.type = type;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
this.title = title;
this.desc = desc;
this.image_url = image_url;
}
// Getters
public Integer getType() { return type; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
public String getTitle() { return title; }
public String getDesc() { return desc; }
public String getImage_url() { return image_url; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ImageTextArea that = (ImageTextArea) o;
return Objects.equals(type, that.type) &&
Objects.equals(url, that.url) &&
Objects.equals(appid, that.appid) &&
Objects.equals(pagepath, that.pagepath) &&
Objects.equals(title, that.title) &&
Objects.equals(desc, that.desc) &&
Objects.equals(image_url, that.image_url);
}
@Override
public int hashCode() {
return Objects.hash(type, url, appid, pagepath, title, desc, image_url);
}
@Override
public String toString() {
return "ImageTextArea{" +
"type=" + type +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
", title='" + title + '\'' +
", desc='" + desc + '\'' +
", image_url='" + image_url + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String url;
private String appid;
private String pagepath;
private String title;
private String desc;
private String image_url;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public Builder image_url(String image_url) {
this.image_url = image_url;
return this;
}
public ImageTextArea build() {
return new ImageTextArea(type, url, appid, pagepath, title, desc, image_url);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 引用文献样式
*/
public static class QuoteArea {
/**
* 引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序
*/
private final Integer type;
/**
* 点击跳转的url,quote_area.type是1时必填
*/
private final String url;
/**
* 点击跳转的小程序的appid,quote_area.type是2时必填
*/
private final String appid;
/**
* 点击跳转的小程序的pagepath,quote_area.type是2时选填
*/
private final String pagepath;
/**
* 引用文献样式的标题
*/
private final String title;
/**
* 引用文献样式的引用文案
*/
private final String quote_text;
private QuoteArea(Integer type, String url, String appid, String pagepath,
String title, String quote_text) {
this.type = type;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
this.title = title;
this.quote_text = quote_text;
}
// Getters
public Integer getType() { return type; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
public String getTitle() { return title; }
public String getQuote_text() { return quote_text; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
QuoteArea quoteArea = (QuoteArea) o;
return Objects.equals(type, quoteArea.type) &&
Objects.equals(url, quoteArea.url) &&
Objects.equals(appid, quoteArea.appid) &&
Objects.equals(pagepath, quoteArea.pagepath) &&
Objects.equals(title, quoteArea.title) &&
Objects.equals(quote_text, quoteArea.quote_text);
}
@Override
public int hashCode() {
return Objects.hash(type, url, appid, pagepath, title, quote_text);
}
@Override
public String toString() {
return "QuoteArea{" +
"type=" + type +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
", title='" + title + '\'' +
", quote_text='" + quote_text + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String url;
private String appid;
private String pagepath;
private String title;
private String quote_text;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder quote_text(String quote_text) {
this.quote_text = quote_text;
return this;
}
public QuoteArea build() {
return new QuoteArea(type, url, appid, pagepath, title, quote_text);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 垂直内容
*/
public static class VerticalContent {
/**
* 卡片二级标题,建议不超过26个字
*/
private final String title;
/**
* 二级普通文本,建议不超过112个字
*/
private final String desc;
private VerticalContent(String title, String desc) {
this.title = title;
this.desc = desc;
}
// Getters
public String getTitle() { return title; }
public String getDesc() { return desc; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VerticalContent that = (VerticalContent) o;
return Objects.equals(title, that.title) &&
Objects.equals(desc, that.desc);
}
@Override
public int hashCode() {
return Objects.hash(title, desc);
}
@Override
public String toString() {
return "VerticalContent{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
'}';
}
// Builder
public static class Builder {
private String title;
private String desc;
public Builder title(String title) {
this.title = title;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public VerticalContent build() {
return new VerticalContent(title, desc);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 水平内容列表项
*/
public static class HorizontalContent {
/**
* 模版卡片的二级标题信息内容支持的类型,1是url,2是文件附件,3 代表点击跳转成员详情
*/
private final Integer type;
/**
* 二级标题,建议不超过5个字
*/
private final String keyname;
/**
* 二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过26个字
*/
private final String value;
/**
* 链接跳转的url,horizontal_content_list.type是1时必填
*/
private final String url;
/**
* 附件的media_id,horizontal_content_list.type是2时必填
*/
private final String media_id;
/**
* 成员详情的userid,horizontal_content_list.type是3时必填
*/
private final String userid;
private HorizontalContent(Integer type, String keyname, String value,
String url, String media_id, String userid) {
this.type = type;
this.keyname = keyname;
this.value = value;
this.url = url;
this.media_id = media_id;
this.userid = userid;
}
// Getters
public Integer getType() { return type; }
public String getKeyname() { return keyname; }
public String getValue() { return value; }
public String getUrl() { return url; }
public String getMedia_id() { return media_id; }
public String getUserid() { return userid; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HorizontalContent that = (HorizontalContent) o;
return Objects.equals(type, that.type) &&
Objects.equals(keyname, that.keyname) &&
Objects.equals(value, that.value) &&
Objects.equals(url, that.url) &&
Objects.equals(media_id, that.media_id) &&
Objects.equals(userid, that.userid);
}
@Override
public int hashCode() {
return Objects.hash(type, keyname, value, url, media_id, userid);
}
@Override
public String toString() {
return "HorizontalContent{" +
"type=" + type +
", keyname='" + keyname + '\'' +
", value='" + value + '\'' +
", url='" + url + '\'' +
", media_id='" + media_id + '\'' +
", userid='" + userid + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String keyname;
private String value;
private String url;
private String media_id;
private String userid;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder keyname(String keyname) {
this.keyname = keyname;
return this;
}
public Builder value(String value) {
this.value = value;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder media_id(String media_id) {
this.media_id = media_id;
return this;
}
public Builder userid(String userid) {
this.userid = userid;
return this;
}
public HorizontalContent build() {
return new HorizontalContent(type, keyname, value, url, media_id, userid);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 跳转指引项
*/
public static class JumpItem {
/**
* 跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序
*/
private final Integer type;
/**
* 跳转链接样式的文案内容,建议不超过13个字
*/
private final String title;
/**
* 跳转链接的url,jump_list.type是1时必填
*/
private final String url;
/**
* 跳转链接的小程序的appid,jump_list.type是2时必填
*/
private final String appid;
/**
* 跳转链接的小程序的pagepath,jump_list.type是2时选填
*/
private final String pagepath;
private JumpItem(Integer type, String title, String url, String appid, String pagepath) {
this.type = type;
this.title = title;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
}
// Getters
public Integer getType() { return type; }
public String getTitle() { return title; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JumpItem jumpItem = (JumpItem) o;
return Objects.equals(type, jumpItem.type) &&
Objects.equals(title, jumpItem.title) &&
Objects.equals(url, jumpItem.url) &&
Objects.equals(appid, jumpItem.appid) &&
Objects.equals(pagepath, jumpItem.pagepath);
}
@Override
public int hashCode() {
return Objects.hash(type, title, url, appid, pagepath);
}
@Override
public String toString() {
return "JumpItem{" +
"type=" + type +
", title='" + title + '\'' +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String title;
private String url;
private String appid;
private String pagepath;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public JumpItem build() {
return new JumpItem(type, title, url, appid, pagepath);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 卡片点击动作
*/
public static class CardAction {
/**
* 卡片跳转类型,1 代表跳转url,2 代表打开小程序。news_notice模版卡片中该字段取值范围为[1,2]
*/
private final Integer type;
/**
* 跳转事件的url,card_action.type是1时必填
*/
private final String url;
/**
* 跳转事件的小程序的appid,card_action.type是2时必填
*/
private final String appid;
/**
* 跳转事件的小程序的pagepath,card_action.type是2时选填
*/
private final String pagepath;
private CardAction(Integer type, String url, String appid, String pagepath) {
this.type = type;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
}
// Getters
public Integer getType() { return type; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CardAction that = (CardAction) o;
return Objects.equals(type, that.type) &&
Objects.equals(url, that.url) &&
Objects.equals(appid, that.appid) &&
Objects.equals(pagepath, that.pagepath);
}
@Override
public int hashCode() {
return Objects.hash(type, url, appid, pagepath);
}
@Override
public String toString() {
return "CardAction{" +
"type=" + type +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type;
private String url;
private String appid;
private String pagepath;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public CardAction build() {
return new CardAction(type, url, appid, pagepath);
}
}
public static Builder builder() {
return new Builder();
}
}
public static void main(String[] args) {
// 创建简单的图文展示模版卡片
WebhookTemplateCardNewsNoticeBody.MainTitle mainTitle = WebhookTemplateCardNewsNoticeBody.MainTitle.builder()
.title("欢迎使用企业微信")
.desc("您的好友正在邀请您加入企业微信")
.build();
WebhookTemplateCardNewsNoticeBody.CardImage cardImage = WebhookTemplateCardNewsNoticeBody.CardImage.builder()
.url("https://wework.qpic.cn/wwpic/354393_4zpkKXd7SrGMvfg_1629280616/0")
.aspect_ratio(2.25f)
.build();
WebhookTemplateCardNewsNoticeBody.CardAction cardAction = WebhookTemplateCardNewsNoticeBody.CardAction.builder()
.type(1)
.url("https://work.weixin.qq.com/?from=openApi")
.build();
WebhookTemplateCardNewsNoticeBody simpleCard = WebhookTemplateCardNewsNoticeBody.create(mainTitle, cardImage, cardAction);
// 创建复杂的图文展示模版卡片
WebhookTemplateCardNewsNoticeBody.Source source = WebhookTemplateCardNewsNoticeBody.Source.builder()
.icon_url("https://wework.qpic.cn/wwpic/252813_jOfDHtcISzuodLa_1629280209/0")
.desc("企业微信")
.desc_color(0)
.build();
WebhookTemplateCardNewsNoticeBody.ImageTextArea imageTextArea = WebhookTemplateCardNewsNoticeBody.ImageTextArea.builder()
.type(1)
.url("https://work.weixin.qq.com")
.title("欢迎使用企业微信")
.desc("您的好友正在邀请您加入企业微信")
.image_url("https://wework.qpic.cn/wwpic/354393_4zpkKXd7SrGMvfg_1629280616/0")
.build();
WebhookTemplateCardNewsNoticeBody.QuoteArea quoteArea = WebhookTemplateCardNewsNoticeBody.QuoteArea.builder()
.type(1)
.url("https://work.weixin.qq.com/?from=openApi")
.title("引用文本标题")
.quote_text("Jack:企业微信真的很好用~\nBalian:超级好的一款软件!")
.build();
List<VerticalContent> verticalContents = Arrays.asList(
WebhookTemplateCardNewsNoticeBody.VerticalContent.builder()
.title("惊喜红包等你来拿")
.desc("下载企业微信还能抢红包!")
.build()
);
List<HorizontalContent> horizontalContents = Arrays.asList(
WebhookTemplateCardNewsNoticeBody.HorizontalContent.builder()
.keyname("邀请人")
.value("张三")
.build(),
WebhookTemplateCardNewsNoticeBody.HorizontalContent.builder()
.type(1)
.keyname("企微官网")
.value("点击访问")
.url("https://work.weixin.qq.com/?from=openApi")
.build(),
WebhookTemplateCardNewsNoticeBody.HorizontalContent.builder()
.type(2)
.keyname("企微下载")
.value("企业微信.apk")
.media_id("38Kz4vLCb49gfGvfOPw_y2f4nrik8lA_1xqNFmIzhtgkZVBoLoEYDQ7WopEKGnCTG")
.build()
);
List<JumpItem> jumpList = Arrays.asList(
WebhookTemplateCardNewsNoticeBody.JumpItem.builder()
.type(1)
.title("企业微信官网")
.url("https://work.weixin.qq.com/?from=openApi")
.build(),
WebhookTemplateCardNewsNoticeBody.JumpItem.builder()
.type(2)
.title("跳转小程序")
.appid("APPID")
.pagepath("PAGEPATH")
.build()
);
WebhookTemplateCardNewsNoticeBody complexCard = WebhookTemplateCardNewsNoticeBody.builder()
.source(source)
.mainTitle(mainTitle)
.cardImage(cardImage)
.imageTextArea(imageTextArea)
.quoteArea(quoteArea)
.verticalContentList(verticalContents)
.horizontalContentList(horizontalContents)
.jumpList(jumpList)
.cardAction(cardAction)
.build();
System.out.println(complexCard);
}
}
java
public class WebhookTemplateCardNewsNoticeBody extends WebhookBody {
private final TemplateCard template_card;
private WebhookTemplateCardNewsNoticeBody(TemplateCard templateCard) {
super("template_card");
this.template_card = templateCard;
}
public TemplateCard getTemplate_card() {
return template_card;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
WebhookTemplateCardNewsNoticeBody that = (WebhookTemplateCardNewsNoticeBody) o;
return Objects.equals(template_card, that.template_card);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), template_card);
}
@Override
public String toString() {
return "WebhookTemplateCardNewsNoticeBody(super=" + super.toString() +
", template_card=" + template_card + ")";
}
/**
* 创建图文展示模版卡片消息
*
* @param mainTitle 主标题
* @param cardImage 卡片图片
* @param cardAction 卡片点击动作
* @return 消息体
*/
public static WebhookTemplateCardNewsNoticeBody create(MainTitle mainTitle, CardImage cardImage, CardAction cardAction) {
return builder()
.cardType("news_notice")
.mainTitle(mainTitle)
.cardImage(cardImage)
.cardAction(cardAction)
.build();
}
/**
* 创建构建器
*/
public static TemplateCardBuilder builder() {
return new TemplateCardBuilder();
}
/**
* 模版卡片构建器
*/
public static class TemplateCardBuilder {
private String cardType = "news_notice";
private Source source;
private MainTitle mainTitle;
private CardImage cardImage;
private ImageTextArea imageTextArea;
private QuoteArea quoteArea;
private List<VerticalContent> verticalContentList;
private List<HorizontalContent> horizontalContentList;
private List<JumpItem> jumpList;
private CardAction cardAction;
public TemplateCardBuilder cardType(String cardType) {
this.cardType = cardType;
return this;
}
public TemplateCardBuilder source(Source source) {
this.source = source;
return this;
}
public TemplateCardBuilder mainTitle(MainTitle mainTitle) {
this.mainTitle = mainTitle;
return this;
}
public TemplateCardBuilder cardImage(CardImage cardImage) {
this.cardImage = cardImage;
return this;
}
public TemplateCardBuilder imageTextArea(ImageTextArea imageTextArea) {
this.imageTextArea = imageTextArea;
return this;
}
public TemplateCardBuilder quoteArea(QuoteArea quoteArea) {
this.quoteArea = quoteArea;
return this;
}
public TemplateCardBuilder verticalContentList(List<VerticalContent> verticalContentList) {
this.verticalContentList = verticalContentList;
return this;
}
public TemplateCardBuilder horizontalContentList(List<HorizontalContent> horizontalContentList) {
this.horizontalContentList = horizontalContentList;
return this;
}
public TemplateCardBuilder jumpList(List<JumpItem> jumpList) {
this.jumpList = jumpList;
return this;
}
public TemplateCardBuilder cardAction(CardAction cardAction) {
this.cardAction = cardAction;
return this;
}
/**
* 构建模版卡片消息
*/
public WebhookTemplateCardNewsNoticeBody build() {
// 校验必填条件
if (mainTitle == null) {
throw new IllegalArgumentException("main_title为必填项");
}
if (cardImage == null) {
throw new IllegalArgumentException("card_image为必填项");
}
if (cardAction == null) {
throw new IllegalArgumentException("news_notice模版卡片中card_action为必填项");
}
if (imageTextArea != null && imageTextArea.getImage_url() == null) {
throw new IllegalArgumentException("image_text_area中image_url为必填项");
}
TemplateCard templateCard = new TemplateCard(
cardType, source, mainTitle, cardImage, imageTextArea, quoteArea,
verticalContentList, horizontalContentList, jumpList, cardAction
);
return new WebhookTemplateCardNewsNoticeBody(templateCard);
}
}
/**
* 模版卡片
*/
public static class TemplateCard {
/**
* 模版卡片的模版类型,图文展示模版卡片的类型为news_notice
*/
private final String card_type;
/**
* 卡片来源样式信息,不需要来源样式可不填写
*/
private final Source source;
/**
* 模版卡片的主要内容,包括一级标题和标题辅助信息
*/
private final MainTitle main_title;
/**
* 图片样式
*/
private final CardImage card_image;
/**
* 左图右文样式
*/
private final ImageTextArea image_text_area;
/**
* 引用文献样式,建议不与关键数据共用
*/
private final QuoteArea quote_area;
/**
* 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4
*/
private final List<VerticalContent> vertical_content_list;
/**
* 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
*/
private final List<HorizontalContent> horizontal_content_list;
/**
* 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3
*/
private final List<JumpItem> jump_list;
/**
* 整体卡片的点击跳转事件,news_notice模版卡片中该字段为必填项
*/
private final CardAction card_action;
public TemplateCard(String card_type, Source source, MainTitle main_title, CardImage card_image,
ImageTextArea image_text_area, QuoteArea quote_area,
List<VerticalContent> vertical_content_list,
List<HorizontalContent> horizontal_content_list,
List<JumpItem> jump_list, CardAction card_action) {
this.card_type = card_type;
this.source = source;
this.main_title = main_title;
this.card_image = card_image;
this.image_text_area = image_text_area;
this.quote_area = quote_area;
this.vertical_content_list = vertical_content_list;
this.horizontal_content_list = horizontal_content_list;
this.jump_list = jump_list;
this.card_action = card_action;
}
// Getters
public String getCard_type() { return card_type; }
public Source getSource() { return source; }
public MainTitle getMain_title() { return main_title; }
public CardImage getCard_image() { return card_image; }
public ImageTextArea getImage_text_area() { return image_text_area; }
public QuoteArea getQuote_area() { return quote_area; }
public List<VerticalContent> getVertical_content_list() { return vertical_content_list; }
public List<HorizontalContent> getHorizontal_content_list() { return horizontal_content_list; }
public List<JumpItem> getJump_list() { return jump_list; }
public CardAction getCard_action() { return card_action; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TemplateCard that = (TemplateCard) o;
return Objects.equals(card_type, that.card_type) &&
Objects.equals(source, that.source) &&
Objects.equals(main_title, that.main_title) &&
Objects.equals(card_image, that.card_image) &&
Objects.equals(image_text_area, that.image_text_area) &&
Objects.equals(quote_area, that.quote_area) &&
Objects.equals(vertical_content_list, that.vertical_content_list) &&
Objects.equals(horizontal_content_list, that.horizontal_content_list) &&
Objects.equals(jump_list, that.jump_list) &&
Objects.equals(card_action, that.card_action);
}
@Override
public int hashCode() {
return Objects.hash(card_type, source, main_title, card_image, image_text_area,
quote_area, vertical_content_list, horizontal_content_list,
jump_list, card_action);
}
@Override
public String toString() {
return "TemplateCard{" +
"card_type='" + card_type + '\'' +
", source=" + source +
", main_title=" + main_title +
", card_image=" + card_image +
", image_text_area=" + image_text_area +
", quote_area=" + quote_area +
", vertical_content_list=" + vertical_content_list +
", horizontal_content_list=" + horizontal_content_list +
", jump_list=" + jump_list +
", card_action=" + card_action +
'}';
}
}
/**
* 卡片来源样式信息
*/
public static class Source {
/**
* 来源图片的url
*/
private final String icon_url;
/**
* 来源图片的描述,建议不超过13个字
*/
private final String desc;
/**
* 来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色
*/
private final Integer desc_color;
private Source(String icon_url, String desc, Integer desc_color) {
this.icon_url = icon_url;
this.desc = desc;
this.desc_color = desc_color;
}
// Getters
public String getIcon_url() { return icon_url; }
public String getDesc() { return desc; }
public Integer getDesc_color() { return desc_color; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Source source = (Source) o;
return Objects.equals(icon_url, source.icon_url) &&
Objects.equals(desc, source.desc) &&
Objects.equals(desc_color, source.desc_color);
}
@Override
public int hashCode() {
return Objects.hash(icon_url, desc, desc_color);
}
@Override
public String toString() {
return "Source{" +
"icon_url='" + icon_url + '\'' +
", desc='" + desc + '\'' +
", desc_color=" + desc_color +
'}';
}
// Builder
public static class Builder {
private String icon_url;
private String desc;
private Integer desc_color = 0;
public Builder icon_url(String icon_url) {
this.icon_url = icon_url;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public Builder desc_color(Integer desc_color) {
this.desc_color = desc_color;
return this;
}
public Source build() {
return new Source(icon_url, desc, desc_color);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 模版卡片的主要内容
*/
public static class MainTitle {
/**
* 一级标题,建议不超过26个字
*/
private final String title;
/**
* 标题辅助信息,建议不超过30个字
*/
private final String desc;
private MainTitle(String title, String desc) {
this.title = title;
this.desc = desc;
}
// Getters
public String getTitle() { return title; }
public String getDesc() { return desc; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MainTitle mainTitle = (MainTitle) o;
return Objects.equals(title, mainTitle.title) &&
Objects.equals(desc, mainTitle.desc);
}
@Override
public int hashCode() {
return Objects.hash(title, desc);
}
@Override
public String toString() {
return "MainTitle{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
'}';
}
// Builder
public static class Builder {
private String title;
private String desc;
public Builder title(String title) {
this.title = title;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public MainTitle build() {
return new MainTitle(title, desc);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 图片样式
*/
public static class CardImage {
/**
* 图片的url
*/
private final String url;
/**
* 图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3
*/
private final Float aspect_ratio;
private CardImage(String url, Float aspect_ratio) {
this.url = url;
this.aspect_ratio = aspect_ratio;
}
// Getters
public String getUrl() { return url; }
public Float getAspect_ratio() { return aspect_ratio; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CardImage cardImage = (CardImage) o;
return Objects.equals(url, cardImage.url) &&
Objects.equals(aspect_ratio, cardImage.aspect_ratio);
}
@Override
public int hashCode() {
return Objects.hash(url, aspect_ratio);
}
@Override
public String toString() {
return "CardImage{" +
"url='" + url + '\'' +
", aspect_ratio=" + aspect_ratio +
'}';
}
// Builder
public static class Builder {
private String url;
private Float aspect_ratio = 1.3f;
public Builder url(String url) {
this.url = url;
return this;
}
public Builder aspect_ratio(Float aspect_ratio) {
this.aspect_ratio = aspect_ratio;
return this;
}
public CardImage build() {
return new CardImage(url, aspect_ratio);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 左图右文样式
*/
public static class ImageTextArea {
/**
* 左图右文样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序
*/
private final Integer type;
/**
* 点击跳转的url,image_text_area.type是1时必填
*/
private final String url;
/**
* 点击跳转的小程序的appid,必须是与当前应用关联的小程序,image_text_area.type是2时必填
*/
private final String appid;
/**
* 点击跳转的小程序的pagepath,image_text_area.type是2时选填
*/
private final String pagepath;
/**
* 左图右文样式的标题
*/
private final String title;
/**
* 左图右文样式的描述
*/
private final String desc;
/**
* 左图右文样式的图片url
*/
private final String image_url;
private ImageTextArea(Integer type, String url, String appid, String pagepath,
String title, String desc, String image_url) {
this.type = type;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
this.title = title;
this.desc = desc;
this.image_url = image_url;
}
// Getters
public Integer getType() { return type; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
public String getTitle() { return title; }
public String getDesc() { return desc; }
public String getImage_url() { return image_url; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ImageTextArea that = (ImageTextArea) o;
return Objects.equals(type, that.type) &&
Objects.equals(url, that.url) &&
Objects.equals(appid, that.appid) &&
Objects.equals(pagepath, that.pagepath) &&
Objects.equals(title, that.title) &&
Objects.equals(desc, that.desc) &&
Objects.equals(image_url, that.image_url);
}
@Override
public int hashCode() {
return Objects.hash(type, url, appid, pagepath, title, desc, image_url);
}
@Override
public String toString() {
return "ImageTextArea{" +
"type=" + type +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
", title='" + title + '\'' +
", desc='" + desc + '\'' +
", image_url='" + image_url + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String url;
private String appid;
private String pagepath;
private String title;
private String desc;
private String image_url;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public Builder image_url(String image_url) {
this.image_url = image_url;
return this;
}
public ImageTextArea build() {
return new ImageTextArea(type, url, appid, pagepath, title, desc, image_url);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 引用文献样式
*/
public static class QuoteArea {
/**
* 引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序
*/
private final Integer type;
/**
* 点击跳转的url,quote_area.type是1时必填
*/
private final String url;
/**
* 点击跳转的小程序的appid,quote_area.type是2时必填
*/
private final String appid;
/**
* 点击跳转的小程序的pagepath,quote_area.type是2时选填
*/
private final String pagepath;
/**
* 引用文献样式的标题
*/
private final String title;
/**
* 引用文献样式的引用文案
*/
private final String quote_text;
private QuoteArea(Integer type, String url, String appid, String pagepath,
String title, String quote_text) {
this.type = type;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
this.title = title;
this.quote_text = quote_text;
}
// Getters
public Integer getType() { return type; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
public String getTitle() { return title; }
public String getQuote_text() { return quote_text; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
QuoteArea quoteArea = (QuoteArea) o;
return Objects.equals(type, quoteArea.type) &&
Objects.equals(url, quoteArea.url) &&
Objects.equals(appid, quoteArea.appid) &&
Objects.equals(pagepath, quoteArea.pagepath) &&
Objects.equals(title, quoteArea.title) &&
Objects.equals(quote_text, quoteArea.quote_text);
}
@Override
public int hashCode() {
return Objects.hash(type, url, appid, pagepath, title, quote_text);
}
@Override
public String toString() {
return "QuoteArea{" +
"type=" + type +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
", title='" + title + '\'' +
", quote_text='" + quote_text + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String url;
private String appid;
private String pagepath;
private String title;
private String quote_text;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder quote_text(String quote_text) {
this.quote_text = quote_text;
return this;
}
public QuoteArea build() {
return new QuoteArea(type, url, appid, pagepath, title, quote_text);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 垂直内容
*/
public static class VerticalContent {
/**
* 卡片二级标题,建议不超过26个字
*/
private final String title;
/**
* 二级普通文本,建议不超过112个字
*/
private final String desc;
private VerticalContent(String title, String desc) {
this.title = title;
this.desc = desc;
}
// Getters
public String getTitle() { return title; }
public String getDesc() { return desc; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VerticalContent that = (VerticalContent) o;
return Objects.equals(title, that.title) &&
Objects.equals(desc, that.desc);
}
@Override
public int hashCode() {
return Objects.hash(title, desc);
}
@Override
public String toString() {
return "VerticalContent{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
'}';
}
// Builder
public static class Builder {
private String title;
private String desc;
public Builder title(String title) {
this.title = title;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public VerticalContent build() {
return new VerticalContent(title, desc);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 水平内容列表项
*/
public static class HorizontalContent {
/**
* 模版卡片的二级标题信息内容支持的类型,1是url,2是文件附件,3 代表点击跳转成员详情
*/
private final Integer type;
/**
* 二级标题,建议不超过5个字
*/
private final String keyname;
/**
* 二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过26个字
*/
private final String value;
/**
* 链接跳转的url,horizontal_content_list.type是1时必填
*/
private final String url;
/**
* 附件的media_id,horizontal_content_list.type是2时必填
*/
private final String media_id;
/**
* 成员详情的userid,horizontal_content_list.type是3时必填
*/
private final String userid;
private HorizontalContent(Integer type, String keyname, String value,
String url, String media_id, String userid) {
this.type = type;
this.keyname = keyname;
this.value = value;
this.url = url;
this.media_id = media_id;
this.userid = userid;
}
// Getters
public Integer getType() { return type; }
public String getKeyname() { return keyname; }
public String getValue() { return value; }
public String getUrl() { return url; }
public String getMedia_id() { return media_id; }
public String getUserid() { return userid; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HorizontalContent that = (HorizontalContent) o;
return Objects.equals(type, that.type) &&
Objects.equals(keyname, that.keyname) &&
Objects.equals(value, that.value) &&
Objects.equals(url, that.url) &&
Objects.equals(media_id, that.media_id) &&
Objects.equals(userid, that.userid);
}
@Override
public int hashCode() {
return Objects.hash(type, keyname, value, url, media_id, userid);
}
@Override
public String toString() {
return "HorizontalContent{" +
"type=" + type +
", keyname='" + keyname + '\'' +
", value='" + value + '\'' +
", url='" + url + '\'' +
", media_id='" + media_id + '\'' +
", userid='" + userid + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String keyname;
private String value;
private String url;
private String media_id;
private String userid;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder keyname(String keyname) {
this.keyname = keyname;
return this;
}
public Builder value(String value) {
this.value = value;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder media_id(String media_id) {
this.media_id = media_id;
return this;
}
public Builder userid(String userid) {
this.userid = userid;
return this;
}
public HorizontalContent build() {
return new HorizontalContent(type, keyname, value, url, media_id, userid);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 跳转指引项
*/
public static class JumpItem {
/**
* 跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序
*/
private final Integer type;
/**
* 跳转链接样式的文案内容,建议不超过13个字
*/
private final String title;
/**
* 跳转链接的url,jump_list.type是1时必填
*/
private final String url;
/**
* 跳转链接的小程序的appid,jump_list.type是2时必填
*/
private final String appid;
/**
* 跳转链接的小程序的pagepath,jump_list.type是2时选填
*/
private final String pagepath;
private JumpItem(Integer type, String title, String url, String appid, String pagepath) {
this.type = type;
this.title = title;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
}
// Getters
public Integer getType() { return type; }
public String getTitle() { return title; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JumpItem jumpItem = (JumpItem) o;
return Objects.equals(type, jumpItem.type) &&
Objects.equals(title, jumpItem.title) &&
Objects.equals(url, jumpItem.url) &&
Objects.equals(appid, jumpItem.appid) &&
Objects.equals(pagepath, jumpItem.pagepath);
}
@Override
public int hashCode() {
return Objects.hash(type, title, url, appid, pagepath);
}
@Override
public String toString() {
return "JumpItem{" +
"type=" + type +
", title='" + title + '\'' +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String title;
private String url;
private String appid;
private String pagepath;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public JumpItem build() {
return new JumpItem(type, title, url, appid, pagepath);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 卡片点击动作
*/
public static class CardAction {
/**
* 卡片跳转类型,1 代表跳转url,2 代表打开小程序。news_notice模版卡片中该字段取值范围为[1,2]
*/
private final Integer type;
/**
* 跳转事件的url,card_action.type是1时必填
*/
private final String url;
/**
* 跳转事件的小程序的appid,card_action.type是2时必填
*/
private final String appid;
/**
* 跳转事件的小程序的pagepath,card_action.type是2时选填
*/
private final String pagepath;
private CardAction(Integer type, String url, String appid, String pagepath) {
this.type = type;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
}
// Getters
public Integer getType() { return type; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CardAction that = (CardAction) o;
return Objects.equals(type, that.type) &&
Objects.equals(url, that.url) &&
Objects.equals(appid, that.appid) &&
Objects.equals(pagepath, that.pagepath);
}
@Override
public int hashCode() {
return Objects.hash(type, url, appid, pagepath);
}
@Override
public String toString() {
return "CardAction{" +
"type=" + type +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type;
private String url;
private String appid;
private String pagepath;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public CardAction build() {
return new CardAction(type, url, appid, pagepath);
}
}
public static Builder builder() {
return new Builder();
}
}
public static void main(String[] args) {
// 创建简单的图文展示模版卡片
WebhookTemplateCardNewsNoticeBody.MainTitle mainTitle = WebhookTemplateCardNewsNoticeBody.MainTitle.builder()
.title("欢迎使用企业微信")
.desc("您的好友正在邀请您加入企业微信")
.build();
WebhookTemplateCardNewsNoticeBody.CardImage cardImage = WebhookTemplateCardNewsNoticeBody.CardImage.builder()
.url("https://wework.qpic.cn/wwpic/354393_4zpkKXd7SrGMvfg_1629280616/0")
.aspect_ratio(2.25f)
.build();
WebhookTemplateCardNewsNoticeBody.CardAction cardAction = WebhookTemplateCardNewsNoticeBody.CardAction.builder()
.type(1)
.url("https://work.weixin.qq.com/?from=openApi")
.build();
WebhookTemplateCardNewsNoticeBody simpleCard = WebhookTemplateCardNewsNoticeBody.create(mainTitle, cardImage, cardAction);
// 创建复杂的图文展示模版卡片
WebhookTemplateCardNewsNoticeBody.Source source = WebhookTemplateCardNewsNoticeBody.Source.builder()
.icon_url("https://wework.qpic.cn/wwpic/252813_jOfDHtcISzuodLa_1629280209/0")
.desc("企业微信")
.desc_color(0)
.build();
WebhookTemplateCardNewsNoticeBody.ImageTextArea imageTextArea = WebhookTemplateCardNewsNoticeBody.ImageTextArea.builder()
.type(1)
.url("https://work.weixin.qq.com")
.title("欢迎使用企业微信")
.desc("您的好友正在邀请您加入企业微信")
.image_url("https://wework.qpic.cn/wwpic/354393_4zpkKXd7SrGMvfg_1629280616/0")
.build();
WebhookTemplateCardNewsNoticeBody.QuoteArea quoteArea = WebhookTemplateCardNewsNoticeBody.QuoteArea.builder()
.type(1)
.url("https://work.weixin.qq.com/?from=openApi")
.title("引用文本标题")
.quote_text("Jack:企业微信真的很好用~\nBalian:超级好的一款软件!")
.build();
List<VerticalContent> verticalContents = Arrays.asList(
WebhookTemplateCardNewsNoticeBody.VerticalContent.builder()
.title("惊喜红包等你来拿")
.desc("下载企业微信还能抢红包!")
.build()
);
List<HorizontalContent> horizontalContents = Arrays.asList(
WebhookTemplateCardNewsNoticeBody.HorizontalContent.builder()
.keyname("邀请人")
.value("张三")
.build(),
WebhookTemplateCardNewsNoticeBody.HorizontalContent.builder()
.type(1)
.keyname("企微官网")
.value("点击访问")
.url("https://work.weixin.qq.com/?from=openApi")
.build(),
WebhookTemplateCardNewsNoticeBody.HorizontalContent.builder()
.type(2)
.keyname("企微下载")
.value("企业微信.apk")
.media_id("38Kz4vLCb49gfGvfOPw_y2f4nrik8lA_1xqNFmIzhtgkZVBoLoEYDQ7WopEKGnCTG")
.build()
);
List<JumpItem> jumpList = Arrays.asList(
WebhookTemplateCardNewsNoticeBody.JumpItem.builder()
.type(1)
.title("企业微信官网")
.url("https://work.weixin.qq.com/?from=openApi")
.build(),
WebhookTemplateCardNewsNoticeBody.JumpItem.builder()
.type(2)
.title("跳转小程序")
.appid("APPID")
.pagepath("PAGEPATH")
.build()
);
WebhookTemplateCardNewsNoticeBody complexCard = WebhookTemplateCardNewsNoticeBody.builder()
.source(source)
.mainTitle(mainTitle)
.cardImage(cardImage)
.imageTextArea(imageTextArea)
.quoteArea(quoteArea)
.verticalContentList(verticalContents)
.horizontalContentList(horizontalContents)
.jumpList(jumpList)
.cardAction(cardAction)
.build();
System.out.println(complexCard);
}
}
java
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 文本通知模版卡片消息体
*/
public class WebhookTemplateCardTextNoticeBody extends WebhookBody {
private final TemplateCard template_card;
private WebhookTemplateCardTextNoticeBody(TemplateCard templateCard) {
super("template_card");
this.template_card = templateCard;
}
public TemplateCard getTemplate_card() {
return template_card;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
WebhookTemplateCardTextNoticeBody that = (WebhookTemplateCardTextNoticeBody) o;
return Objects.equals(template_card, that.template_card);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), template_card);
}
@Override
public String toString() {
return "WebhookTemplateCardTextNoticeBody(super=" + super.toString() +
", template_card=" + template_card + ")";
}
/**
* 创建文本通知模版卡片消息
* @param mainTitle 主标题
* @param cardAction 卡片点击动作
* @return 消息体
*/
public static WebhookTemplateCardTextNoticeBody create(MainTitle mainTitle, CardAction cardAction) {
return builder()
.cardType("text_notice")
.mainTitle(mainTitle)
.cardAction(cardAction)
.build();
}
/**
* 创建构建器
*/
public static TemplateCardBuilder builder() {
return new TemplateCardBuilder();
}
/**
* 模版卡片构建器
*/
public static class TemplateCardBuilder {
private String cardType = "text_notice";
private Source source;
private MainTitle mainTitle;
private EmphasisContent emphasisContent;
private QuoteArea quoteArea;
private String subTitleText;
private List<HorizontalContent> horizontalContentList;
private List<JumpItem> jumpList;
private CardAction cardAction;
public TemplateCardBuilder cardType(String cardType) {
this.cardType = cardType;
return this;
}
public TemplateCardBuilder source(Source source) {
this.source = source;
return this;
}
public TemplateCardBuilder mainTitle(MainTitle mainTitle) {
this.mainTitle = mainTitle;
return this;
}
public TemplateCardBuilder emphasisContent(EmphasisContent emphasisContent) {
this.emphasisContent = emphasisContent;
return this;
}
public TemplateCardBuilder quoteArea(QuoteArea quoteArea) {
this.quoteArea = quoteArea;
return this;
}
public TemplateCardBuilder subTitleText(String subTitleText) {
this.subTitleText = subTitleText;
return this;
}
public TemplateCardBuilder horizontalContentList(List<HorizontalContent> horizontalContentList) {
this.horizontalContentList = horizontalContentList;
return this;
}
public TemplateCardBuilder jumpList(List<JumpItem> jumpList) {
this.jumpList = jumpList;
return this;
}
public TemplateCardBuilder cardAction(CardAction cardAction) {
this.cardAction = cardAction;
return this;
}
/**
* 构建模版卡片消息
* 校验:main_title.title和sub_title_text必须有一项填写
*/
public WebhookTemplateCardTextNoticeBody build() {
// 校验必填条件
if (mainTitle == null || (mainTitle.getTitle() == null && subTitleText == null)) {
throw new IllegalArgumentException("main_title.title和sub_title_text必须有一项填写");
}
if (cardAction == null) {
throw new IllegalArgumentException("text_notice模版卡片中card_action为必填项");
}
TemplateCard templateCard = new TemplateCard(
cardType, source, mainTitle, emphasisContent, quoteArea,
subTitleText, horizontalContentList, jumpList, cardAction
);
return new WebhookTemplateCardTextNoticeBody(templateCard);
}
}
/**
* 模版卡片
*/
public static class TemplateCard {
/**
* 模版卡片的模版类型,文本通知模版卡片的类型为text_notice
*/
private final String card_type;
/**
* 卡片来源样式信息,不需要来源样式可不填写
*/
private final Source source;
/**
* 模版卡片的主要内容,包括一级标题和标题辅助信息
*/
private final MainTitle main_title;
/**
* 关键数据样式
*/
private final EmphasisContent emphasis_content;
/**
* 引用文献样式,建议不与关键数据共用
*/
private final QuoteArea quote_area;
/**
* 二级普通文本,建议不超过112个字。模版卡片主要内容的一级标题main_title.title和二级普通文本sub_title_text必须有一项填写
*/
private final String sub_title_text;
/**
* 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6
*/
private final List<HorizontalContent> horizontal_content_list;
/**
* 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3
*/
private final List<JumpItem> jump_list;
/**
* 整体卡片的点击跳转事件,text_notice模版卡片中该字段为必填项
*/
private final CardAction card_action;
public TemplateCard(String card_type, Source source, MainTitle main_title,
EmphasisContent emphasis_content, QuoteArea quote_area,
String sub_title_text, List<HorizontalContent> horizontal_content_list,
List<JumpItem> jump_list, CardAction card_action) {
this.card_type = card_type;
this.source = source;
this.main_title = main_title;
this.emphasis_content = emphasis_content;
this.quote_area = quote_area;
this.sub_title_text = sub_title_text;
this.horizontal_content_list = horizontal_content_list;
this.jump_list = jump_list;
this.card_action = card_action;
}
// Getters
public String getCard_type() { return card_type; }
public Source getSource() { return source; }
public MainTitle getMain_title() { return main_title; }
public EmphasisContent getEmphasis_content() { return emphasis_content; }
public QuoteArea getQuote_area() { return quote_area; }
public String getSub_title_text() { return sub_title_text; }
public List<HorizontalContent> getHorizontal_content_list() { return horizontal_content_list; }
public List<JumpItem> getJump_list() { return jump_list; }
public CardAction getCard_action() { return card_action; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TemplateCard that = (TemplateCard) o;
return Objects.equals(card_type, that.card_type) &&
Objects.equals(source, that.source) &&
Objects.equals(main_title, that.main_title) &&
Objects.equals(emphasis_content, that.emphasis_content) &&
Objects.equals(quote_area, that.quote_area) &&
Objects.equals(sub_title_text, that.sub_title_text) &&
Objects.equals(horizontal_content_list, that.horizontal_content_list) &&
Objects.equals(jump_list, that.jump_list) &&
Objects.equals(card_action, that.card_action);
}
@Override
public int hashCode() {
return Objects.hash(card_type, source, main_title, emphasis_content, quote_area,
sub_title_text, horizontal_content_list, jump_list, card_action);
}
@Override
public String toString() {
return "TemplateCard{" +
"card_type='" + card_type + '\'' +
", source=" + source +
", main_title=" + main_title +
", emphasis_content=" + emphasis_content +
", quote_area=" + quote_area +
", sub_title_text='" + sub_title_text + '\'' +
", horizontal_content_list=" + horizontal_content_list +
", jump_list=" + jump_list +
", card_action=" + card_action +
'}';
}
}
/**
* 卡片来源样式信息
*/
public static class Source {
/**
* 来源图片的url
*/
private final String icon_url;
/**
* 来源图片的描述,建议不超过13个字
*/
private final String desc;
/**
* 来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色
*/
private final Integer desc_color;
private Source(String icon_url, String desc, Integer desc_color) {
this.icon_url = icon_url;
this.desc = desc;
this.desc_color = desc_color;
}
// Getters
public String getIcon_url() { return icon_url; }
public String getDesc() { return desc; }
public Integer getDesc_color() { return desc_color; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Source source = (Source) o;
return Objects.equals(icon_url, source.icon_url) &&
Objects.equals(desc, source.desc) &&
Objects.equals(desc_color, source.desc_color);
}
@Override
public int hashCode() {
return Objects.hash(icon_url, desc, desc_color);
}
@Override
public String toString() {
return "Source{" +
"icon_url='" + icon_url + '\'' +
", desc='" + desc + '\'' +
", desc_color=" + desc_color +
'}';
}
// Builder
public static class Builder {
private String icon_url;
private String desc;
private Integer desc_color = 0;
public Builder icon_url(String icon_url) {
this.icon_url = icon_url;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public Builder desc_color(Integer desc_color) {
this.desc_color = desc_color;
return this;
}
public Source build() {
return new Source(icon_url, desc, desc_color);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 模版卡片的主要内容
*/
public static class MainTitle {
/**
* 一级标题,建议不超过26个字。模版卡片主要内容的一级标题main_title.title和二级普通文本sub_title_text必须有一项填写
*/
private final String title;
/**
* 标题辅助信息,建议不超过30个字
*/
private final String desc;
private MainTitle(String title, String desc) {
this.title = title;
this.desc = desc;
}
// Getters
public String getTitle() { return title; }
public String getDesc() { return desc; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MainTitle mainTitle = (MainTitle) o;
return Objects.equals(title, mainTitle.title) &&
Objects.equals(desc, mainTitle.desc);
}
@Override
public int hashCode() {
return Objects.hash(title, desc);
}
@Override
public String toString() {
return "MainTitle{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
'}';
}
// Builder
public static class Builder {
private String title;
private String desc;
public Builder title(String title) {
this.title = title;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public MainTitle build() {
return new MainTitle(title, desc);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 关键数据样式
*/
public static class EmphasisContent {
/**
* 关键数据样式的数据内容,建议不超过10个字
*/
private final String title;
/**
* 关键数据样式的数据描述内容,建议不超过15个字
*/
private final String desc;
private EmphasisContent(String title, String desc) {
this.title = title;
this.desc = desc;
}
// Getters
public String getTitle() { return title; }
public String getDesc() { return desc; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmphasisContent that = (EmphasisContent) o;
return Objects.equals(title, that.title) &&
Objects.equals(desc, that.desc);
}
@Override
public int hashCode() {
return Objects.hash(title, desc);
}
@Override
public String toString() {
return "EmphasisContent{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
'}';
}
// Builder
public static class Builder {
private String title;
private String desc;
public Builder title(String title) {
this.title = title;
return this;
}
public Builder desc(String desc) {
this.desc = desc;
return this;
}
public EmphasisContent build() {
return new EmphasisContent(title, desc);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 引用文献样式
*/
public static class QuoteArea {
/**
* 引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序
*/
private final Integer type;
/**
* 点击跳转的url,quote_area.type是1时必填
*/
private final String url;
/**
* 点击跳转的小程序的appid,quote_area.type是2时必填
*/
private final String appid;
/**
* 点击跳转的小程序的pagepath,quote_area.type是2时选填
*/
private final String pagepath;
/**
* 引用文献样式的标题
*/
private final String title;
/**
* 引用文献样式的引用文案
*/
private final String quote_text;
private QuoteArea(Integer type, String url, String appid, String pagepath,
String title, String quote_text) {
this.type = type;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
this.title = title;
this.quote_text = quote_text;
}
// Getters
public Integer getType() { return type; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
public String getTitle() { return title; }
public String getQuote_text() { return quote_text; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
QuoteArea quoteArea = (QuoteArea) o;
return Objects.equals(type, quoteArea.type) &&
Objects.equals(url, quoteArea.url) &&
Objects.equals(appid, quoteArea.appid) &&
Objects.equals(pagepath, quoteArea.pagepath) &&
Objects.equals(title, quoteArea.title) &&
Objects.equals(quote_text, quoteArea.quote_text);
}
@Override
public int hashCode() {
return Objects.hash(type, url, appid, pagepath, title, quote_text);
}
@Override
public String toString() {
return "QuoteArea{" +
"type=" + type +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
", title='" + title + '\'' +
", quote_text='" + quote_text + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String url;
private String appid;
private String pagepath;
private String title;
private String quote_text;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder quote_text(String quote_text) {
this.quote_text = quote_text;
return this;
}
public QuoteArea build() {
return new QuoteArea(type, url, appid, pagepath, title, quote_text);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 水平内容列表项
*/
public static class HorizontalContent {
/**
* 模版卡片的二级标题信息内容支持的类型,1是url,2是文件附件,3 代表点击跳转成员详情
*/
private final Integer type;
/**
* 二级标题,建议不超过5个字
*/
private final String keyname;
/**
* 二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过26个字
*/
private final String value;
/**
* 链接跳转的url,horizontal_content_list.type是1时必填
*/
private final String url;
/**
* 附件的media_id,horizontal_content_list.type是2时必填
*/
private final String media_id;
/**
* 成员详情的userid,horizontal_content_list.type是3时必填
*/
private final String userid;
private HorizontalContent(Integer type, String keyname, String value,
String url, String media_id, String userid) {
this.type = type;
this.keyname = keyname;
this.value = value;
this.url = url;
this.media_id = media_id;
this.userid = userid;
}
// Getters
public Integer getType() { return type; }
public String getKeyname() { return keyname; }
public String getValue() { return value; }
public String getUrl() { return url; }
public String getMedia_id() { return media_id; }
public String getUserid() { return userid; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HorizontalContent that = (HorizontalContent) o;
return Objects.equals(type, that.type) &&
Objects.equals(keyname, that.keyname) &&
Objects.equals(value, that.value) &&
Objects.equals(url, that.url) &&
Objects.equals(media_id, that.media_id) &&
Objects.equals(userid, that.userid);
}
@Override
public int hashCode() {
return Objects.hash(type, keyname, value, url, media_id, userid);
}
@Override
public String toString() {
return "HorizontalContent{" +
"type=" + type +
", keyname='" + keyname + '\'' +
", value='" + value + '\'' +
", url='" + url + '\'' +
", media_id='" + media_id + '\'' +
", userid='" + userid + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String keyname;
private String value;
private String url;
private String media_id;
private String userid;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder keyname(String keyname) {
this.keyname = keyname;
return this;
}
public Builder value(String value) {
this.value = value;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder media_id(String media_id) {
this.media_id = media_id;
return this;
}
public Builder userid(String userid) {
this.userid = userid;
return this;
}
public HorizontalContent build() {
return new HorizontalContent(type, keyname, value, url, media_id, userid);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 跳转指引项
*/
public static class JumpItem {
/**
* 跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序
*/
private final Integer type;
/**
* 跳转链接样式的文案内容,建议不超过13个字
*/
private final String title;
/**
* 跳转链接的url,jump_list.type是1时必填
*/
private final String url;
/**
* 跳转链接的小程序的appid,jump_list.type是2时必填
*/
private final String appid;
/**
* 跳转链接的小程序的pagepath,jump_list.type是2时选填
*/
private final String pagepath;
private JumpItem(Integer type, String title, String url, String appid, String pagepath) {
this.type = type;
this.title = title;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
}
// Getters
public Integer getType() { return type; }
public String getTitle() { return title; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JumpItem jumpItem = (JumpItem) o;
return Objects.equals(type, jumpItem.type) &&
Objects.equals(title, jumpItem.title) &&
Objects.equals(url, jumpItem.url) &&
Objects.equals(appid, jumpItem.appid) &&
Objects.equals(pagepath, jumpItem.pagepath);
}
@Override
public int hashCode() {
return Objects.hash(type, title, url, appid, pagepath);
}
@Override
public String toString() {
return "JumpItem{" +
"type=" + type +
", title='" + title + '\'' +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type = 0;
private String title;
private String url;
private String appid;
private String pagepath;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder title(String title) {
this.title = title;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public JumpItem build() {
return new JumpItem(type, title, url, appid, pagepath);
}
}
public static Builder builder() {
return new Builder();
}
}
/**
* 卡片点击动作
*/
public static class CardAction {
/**
* 卡片跳转类型,1 代表跳转url,2 代表打开小程序。text_notice模版卡片中该字段取值范围为[1,2]
*/
private final Integer type;
/**
* 跳转事件的url,card_action.type是1时必填
*/
private final String url;
/**
* 跳转事件的小程序的appid,card_action.type是2时必填
*/
private final String appid;
/**
* 跳转事件的小程序的pagepath,card_action.type是2时选填
*/
private final String pagepath;
private CardAction(Integer type, String url, String appid, String pagepath) {
this.type = type;
this.url = url;
this.appid = appid;
this.pagepath = pagepath;
}
// Getters
public Integer getType() { return type; }
public String getUrl() { return url; }
public String getAppid() { return appid; }
public String getPagepath() { return pagepath; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CardAction that = (CardAction) o;
return Objects.equals(type, that.type) &&
Objects.equals(url, that.url) &&
Objects.equals(appid, that.appid) &&
Objects.equals(pagepath, that.pagepath);
}
@Override
public int hashCode() {
return Objects.hash(type, url, appid, pagepath);
}
@Override
public String toString() {
return "CardAction{" +
"type=" + type +
", url='" + url + '\'' +
", appid='" + appid + '\'' +
", pagepath='" + pagepath + '\'' +
'}';
}
// Builder
public static class Builder {
private Integer type;
private String url;
private String appid;
private String pagepath;
public Builder type(Integer type) {
this.type = type;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder appid(String appid) {
this.appid = appid;
return this;
}
public Builder pagepath(String pagepath) {
this.pagepath = pagepath;
return this;
}
public CardAction build() {
return new CardAction(type, url, appid, pagepath);
}
}
public static Builder builder() {
return new Builder();
}
}
// 创建复杂的文本通知模
public static void main(String[] args) {
// 创建简单的文本通知模版卡片
WebhookTemplateCardTextNoticeBody.MainTitle mainTitle = WebhookTemplateCardTextNoticeBody.MainTitle.builder()
.title("欢迎使用企业微信")
.desc("您的好友正在邀请您加入企业微信")
.build();
WebhookTemplateCardTextNoticeBody.CardAction cardAction = WebhookTemplateCardTextNoticeBody.CardAction.builder()
.type(1)
.url("https://work.weixin.qq.com/?from=openApi")
.build();
WebhookTemplateCardTextNoticeBody simpleCard = WebhookTemplateCardTextNoticeBody.create(mainTitle, cardAction);
// 创建复杂的文本通知模版卡片
WebhookTemplateCardTextNoticeBody.Source source = WebhookTemplateCardTextNoticeBody.Source.builder()
.icon_url("https://wework.qpic.cn/wwpic/252813_jOfDHtcISzuodLa_1629280209/0")
.desc("企业微信")
.desc_color(0)
.build();
WebhookTemplateCardTextNoticeBody.EmphasisContent emphasisContent = WebhookTemplateCardTextNoticeBody.EmphasisContent.builder()
.title("100")
.desc("数据含义")
.build();
WebhookTemplateCardTextNoticeBody.QuoteArea quoteArea = WebhookTemplateCardTextNoticeBody.QuoteArea.builder()
.type(1)
.url("https://work.weixin.qq.com/?from=openApi")
.title("引用文本标题")
.quote_text("Jack:企业微信真的很好用~\nBalian:超级好的一款软件!")
.build();
List<HorizontalContent> horizontalContents = Arrays.asList(
WebhookTemplateCardTextNoticeBody.HorizontalContent.builder()
.keyname("邀请人")
.value("张三")
.build(),
WebhookTemplateCardTextNoticeBody.HorizontalContent.builder()
.type(1)
.keyname("企微官网")
.value("点击访问")
.url("https://work.weixin.qq.com/?from=openApi")
.build(),
WebhookTemplateCardTextNoticeBody.HorizontalContent.builder()
.type(2)
.keyname("企微下载")
.value("企业微信.apk")
.media_id("38Kz4vLCb49gfGvfOPw_y2f4nrik8lA_1xqNFmIzhtgkZVBoLoEYDQ7WopEKGnCTG")
.build()
);
// WebhookUploadMediaResponse{errcode=0, errmsg='ok', type='file', media_id='38Kz4vLCb49gfGvfOPw_y2f4nrik8lA_1xqNFmIzhtgkZVBoLoEYDQ7WopEKGnCTG', created_at='1761102339'}
List<JumpItem> jumpList = Arrays.asList(
WebhookTemplateCardTextNoticeBody.JumpItem.builder()
.type(1)
.title("企业微信官网")
.url("https://work.weixin.qq.com/?from=openApi")
.build()
// WebhookTemplateCardTextNoticeBody.JumpItem.builder()
// .type(2)
// .title("跳转小程序")
// .appid("APPID")
// .pagepath("PAGEPATH")
// .build()
);
WebhookTemplateCardTextNoticeBody complexCard = WebhookTemplateCardTextNoticeBody.builder()
.source(source)
.mainTitle(mainTitle)
.emphasisContent(emphasisContent)
.quoteArea(quoteArea)
.subTitleText("下载企业微信还能抢红包!")
.horizontalContentList(horizontalContents)
.jumpList(jumpList)
.cardAction(cardAction)
.build();
System.out.println(complexCard);
}
}
java
/**
* 文本类型
*/
public class WebhookTextBody extends WebhookBody {
private final WebhookText text;
WebhookTextBody(WebhookText text) {
super("text");
this.text = text;
}
public WebhookText getText() {
return text;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
WebhookTextBody that = (WebhookTextBody) o;
return Objects.equals(text, that.text);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), text);
}
@Override
public String toString() {
return "WebhookTextBody(super=" + super.toString() + ", text=" + text + ")";
}
/**
* 传入文本
* @param content 文本内容
* @return
*/
public static WebhookTextBody from(String content) {
return from(content, null);
}
/**
* 传入文本
* @param content 文本内容
* @param mentionedMobileList 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人
* @return
*/
public static WebhookTextBody from(String content, List<String> mentionedMobileList) {
return from(content, null, mentionedMobileList);
}
/**
* 传入文本
* @param content 文本内容
* @param mentionedList userid的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userid,可以使用mentioned_mobile_list
* @param mentionedMobileList 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人
* @return
*/
public static WebhookTextBody from(String content, List<String> mentionedList, List<String> mentionedMobileList) {
WebhookText webhookText = new WebhookText(content);
webhookText.setMentioned_list(mentionedList);
webhookText.setMentioned_mobile_list(mentionedMobileList);
return new WebhookTextBody(webhookText);
}
/**
* 文本类型
*/
public static class WebhookText {
/**
* 文本内容,最长不超过2048个字节,必须是utf8编码
*/
private final String content;
/**
* userid的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userid,可以使用mentioned_mobile_list
*/
private List<String> mentioned_list;
/**
* 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人
*/
private List<String> mentioned_mobile_list;
public WebhookText(String content) {
this.content = content;
}
// Getters
public String getContent() {
return content;
}
public List<String> getMentioned_list() {
return mentioned_list;
}
public List<String> getMentioned_mobile_list() {
return mentioned_mobile_list;
}
// Setters
public void setMentioned_list(List<String> mentioned_list) {
this.mentioned_list = mentioned_list;
}
public void setMentioned_mobile_list(List<String> mentioned_mobile_list) {
this.mentioned_mobile_list = mentioned_mobile_list;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WebhookText that = (WebhookText) o;
return Objects.equals(content, that.content) &&
Objects.equals(mentioned_list, that.mentioned_list) &&
Objects.equals(mentioned_mobile_list, that.mentioned_mobile_list);
}
@Override
public int hashCode() {
return Objects.hash(content, mentioned_list, mentioned_mobile_list);
}
@Override
public String toString() {
return "WebhookText{" +
"content='" + content + '\'' +
", mentioned_list=" + mentioned_list +
", mentioned_mobile_list=" + mentioned_mobile_list +
'}';
}
}
}
java
public class WebhookUploadMediaResponse extends WebhookResponse{
private Integer errcode;
private String errmsg;
private String type;
private String media_id;
private String created_at;
public WebhookUploadMediaResponse(){}
public WebhookUploadMediaResponse(Integer errcode, String errmsg, String type, String media_id, String created_at) {
this.errcode = errcode;
this.errmsg = errmsg;
this.type = type;
this.media_id = media_id;
this.created_at = created_at;
}
public String getMedia_id() {
return media_id;
}
public void setMedia_id(String media_id) {
this.media_id = media_id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCreated_at() {
return created_at;
}
public void setCreated_at(String created_at) {
this.created_at = created_at;
}
@Override
public String toString() {
return "WebhookUploadMediaResponse{" +
"errcode=" + errcode +
", errmsg='" + errmsg + '\'' +
", type='" + type + '\'' +
", media_id='" + media_id + '\'' +
", created_at='" + created_at + '\'' +
'}';
}
}
java
/**
* 语音类型消息体
*/
public class WebhookVoiceBody extends WebhookBody {
private final WebhookVoice voice;
private WebhookVoiceBody(WebhookVoice voice) {
super("voice");
this.voice = voice;
}
public WebhookVoice getVoice() {
return voice;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
WebhookVoiceBody that = (WebhookVoiceBody) o;
return Objects.equals(voice, that.voice);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), voice);
}
@Override
public String toString() {
return "WebhookVoiceBody(super=" + super.toString() +
", voice=" + voice + ")";
}
/**
* 创建语音消息
* @param mediaId 语音文件id,通过文件上传接口获取
*/
public static WebhookVoiceBody from(String mediaId) {
if (mediaId == null || mediaId.trim().isEmpty()) {
throw new IllegalArgumentException("mediaId不能为空");
}
return new WebhookVoiceBody(new WebhookVoice(mediaId));
}
/**
* 语音消息内容
*/
public static class WebhookVoice {
/**
* 语音文件id,通过下文的文件上传接口获取
*/
private final String media_id;
public WebhookVoice(String media_id) {
this.media_id = media_id;
}
public String getMedia_id() {
return media_id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WebhookVoice that = (WebhookVoice) o;
return Objects.equals(media_id, that.media_id);
}
@Override
public int hashCode() {
return Objects.hash(media_id);
}
@Override
public String toString() {
return "WebhookVoice(media_id=" + media_id + ")";
}
}
}
上面定义了接口
下面是具体的实现类
xml
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
<!-- httpclient http客户端-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- https://www.xuxueli.com/xxl-tool/# -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-tool</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<!-- 推荐使用 2.10.1(截至2025年稳定版本) -->
<version>2.10.1</version>
</dependency>
</dependencies>
java
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class RestClient {
private RestClient() {
}
private final static Logger log = LoggerFactory.getLogger(RestClient.class);
/**
* 建立连接时间
*/
private static final int CONNECTION_TIME = 3000;
/**
* 发送请求时间
*/
private static final int CONNECTION_REQUEST_TIME = 30000;
/**
* 读取数据时间
*/
private static final int SOCKET_TIME = 180000;
/**
* 重试次数
*/
private static final int RETRY = 3;
/**
* 连接池最大连接数
*/
private static final int MAX_TOTAL = 100;
/**
* 最大路由
*/
private static final int MAX_PER_ROUTE = 5;
// 它是线程安全的,所有的线程都可以使用它一起发送http请求
private static final CloseableHttpClient httpClient;
public static CloseableHttpClient getHttpClient() {
return httpClient;
}
static {
log.info("初始化HttpClientTest~~~开始");
httpClient = getConnection();
log.info("初始化HttpClientTest~~~结束");
}
static class miTM implements TrustManager, X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
//don't check
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
//don't check
}
}
private static SSLConnectionSocketFactory trustAllHttpsCertificates() {
SSLConnectionSocketFactory socketFactory = null;
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager tm = new miTM();
trustAllCerts[0] = tm;
SSLContext sc = null;
try {
sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, null);
socketFactory = new SSLConnectionSocketFactory(sc, NoopHostnameVerifier.INSTANCE);
// HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return socketFactory;
}
/**
* 忽略证书验证的CloseableHttpClient对象,适配Http/Https
*
* @return CloseableHttpClient
*/
public static CloseableHttpClient getConnection() {
CloseableHttpClient closeableHttpClient = null;
try {
closeableHttpClient = HttpClients.custom()
// 设置超时时间
.setDefaultRequestConfig(getRequestConfig())
// 设置连接池
.setConnectionManager(getPoolingManager())
// 设置重试策略,框架层面重试,可以在业务层面重试
//.setRetryHandler(getRetryHandler())
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
// 连接池清理过期连接
.evictExpiredConnections()
// 连接池释放空闲连接
.evictIdleConnections(60, TimeUnit.SECONDS)
.build();
} catch (KeyManagementException e) {
log.error("KeyManagement error", e);
} catch (NoSuchAlgorithmException e) {
log.error("No Such Algorithm error", e);
}
return closeableHttpClient;
}
/**
* HTTP Post:Form表单方式
* Content-type:application/x-www-form-urlencoded; charset=UTF-8
*
* @param url 请求的url地址
* @param headerMap 请求头的参数
* @param params 请求的参数
*/
public static String postToJson(String url, String params, Map<String, String> headerMap) {
// 创建Post请求
HttpPost httpPost = new HttpPost(url);
// post请求是将参数放在请求体里面传过去的;这里将entity放入post请求体中
httpPost.setEntity(new StringEntity(params, StandardCharsets.UTF_8));
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
try (CloseableHttpResponse response = getHttpClient().execute(httpPost)) {
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
return EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
}
return null;
} catch (IOException e) {
log.error( "request failure! error {}", e.getMessage());
throw new RuntimeException("request failure! error " + e.getMessage());
}
}
public static String getStringFromStream(final InputStreamReader isr) throws Exception{
final BufferedReader br = new BufferedReader(isr);
final StringBuilder sb = new StringBuilder();
String tmp;
while((tmp = br.readLine())!=null){
sb.append(tmp);
sb.append("\r\n");
}
br.close();
isr.close();
return sb.toString();
}
public static byte[] postToJsonToByte(String url, String params, Map<String, String> headerMap) {
// 创建Post请求
HttpPost httpPost = new HttpPost(url);
// post请求是将参数放在请求体里面传过去的;这里将entity放入post请求体中
httpPost.setEntity(new StringEntity(params, StandardCharsets.UTF_8));
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
try (CloseableHttpResponse response = getHttpClient().execute(httpPost)) {
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
return EntityUtils.toByteArray(responseEntity);
}
return null;
} catch (IOException e) {
log.error( "request failure! error {}", e.getMessage());
throw new RuntimeException("request failure! error " + e.getMessage());
}
}
/**
* 发送 multipart/form-data 请求
* @param url 请求地址
* @param params 请求参数
* @param headerMap 请求头
* @param file 文件
* @return
*/
public static String postToFormData(String url, Map<String, String> params, Map<String, String> headerMap,File file) {
HttpPost httpPost = new HttpPost(url);
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
String filesKey = "file";
// (多个文件的话,使用同一个key就行,后端用数组或集合进行接收即可)
// 文件名其实是放在请求头的Content-Disposition里面进行传输的,如其值为form-data; name="files"; filename="头像.jpg"
multipartEntityBuilder.addBinaryBody(filesKey, file, ContentType.DEFAULT_BINARY, encode(file.getName()));
// 其它参数(注:自定义contentType,设置UTF-8是为了防止服务端拿到的参数出现乱码)
ContentType contentType = ContentType.create("text/plain", StandardCharsets.UTF_8);
for (Map.Entry<String, String> entry : params.entrySet()) {
multipartEntityBuilder.addTextBody(entry.getKey(), entry.getValue(), contentType);
}
HttpEntity httpEntity = multipartEntityBuilder.build();
httpPost.setEntity(httpEntity);
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
try (CloseableHttpResponse response = getHttpClient().execute(httpPost)) {
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
return EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
}
return null;
} catch (IOException e) {
throw new RuntimeException("request failure! error " + e.getMessage());
}
}
/**
* 发送 multipart/form-data 请求
* @param url 请求地址
* @param params 请求参数
* @param headerMap 请求头
* @return
*/
public static String postToFormData(String url, Map<String, String> params, Map<String, String> headerMap) {
HttpPost httpPost = new HttpPost(url);
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
// 其它参数(注:自定义contentType,设置UTF-8是为了防止服务端拿到的参数出现乱码)
ContentType contentType = ContentType.create("text/plain", StandardCharsets.UTF_8);
for (Map.Entry<String, String> entry : params.entrySet()) {
multipartEntityBuilder.addTextBody(entry.getKey(), entry.getValue(), contentType);
}
HttpEntity httpEntity = multipartEntityBuilder.build();
httpPost.setEntity(httpEntity);
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
try (CloseableHttpResponse response = getHttpClient().execute(httpPost)) {
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null) {
return EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
}
return null;
} catch (IOException e) {
throw new RuntimeException("request failure! error " + e.getMessage());
}
}
public static String encode(String str) {
String encode = null;
try {
encode = URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return encode;
}
/**
* Http连接池配置
*
* @return HttpClientConnectionManager
*/
public static HttpClientConnectionManager getPoolingManager() throws NoSuchAlgorithmException, KeyManagementException {
Registry<ConnectionSocketFactory> socketFactoryRegistry =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", trustAllHttpsCertificates()).build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
// 设置整个连接池最大连接数
connectionManager.setMaxTotal(MAX_TOTAL);
// 最大路由
connectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
return connectionManager;
}
/**
* 封装RequestConfig
*
* @return RequestConfig
*/
public static RequestConfig getRequestConfig() {
return RequestConfig.custom().
setConnectTimeout(getConnectionTime()).
setConnectionRequestTimeout(getConnectionRequestTime())
.setSocketTimeout(getSocketTime()).build();
}
/**
* HttpClient的重试策略
*
* @return HttpRequestRetryHandler
*/
public static HttpRequestRetryHandler getRetryHandler() {
HttpRequestRetryHandler retryHandler = (exception, executionCount, context) -> {
// 重试三次
if (executionCount >= RETRY) {
return false;
}
if (exception instanceof InterruptedIOException) {
// 超时
return false;
}
if (exception instanceof UnknownHostException) {
// 目标服务器不可达
return false;
}
if (exception instanceof SSLException) {
// SSL握手异常
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
if (idempotent) {
// 如果请求是幂等的,就再次尝试
return true;
}
return false;
};
return retryHandler;
}
public static Integer getConnectionTime() {
return CONNECTION_TIME;
}
public static Integer getConnectionRequestTime() {
return CONNECTION_REQUEST_TIME;
}
public static Integer getSocketTime() {
return SOCKET_TIME;
}
/**
* 校验url,支持扩展
*
* @param url
*/
private static void checkUrl(String url) {
if (StringUtils.isBlank(url)) {
log.error("rest client request url not null");
throw new RuntimeException("rest client request url not null");
}
}
/**
* 关闭流资源
*
* @param closeable
*/
private static void close(Closeable closeable) {
try {
closeable.close();
} catch (IOException e) {
log.error("close error", e);
}
}
}
java
import com.fjdci.sdk.wechat.webhook.api.dto.WebhookBody;
import com.fjdci.sdk.wechat.webhook.api.dto.WebhookResponse;
import com.fjdci.sdk.wechat.webhook.api.dto.WebhookUploadMediaResponse;
import com.fjdci.sdk.wechat.webhook.api.service.WxWebHookHttpClient;
import com.xxl.tool.gson.GsonTool;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
public class defaultWxWebHookHttpClient implements WxWebHookHttpClient {
private String webhookurl;
public defaultWxWebHookHttpClient(String webhookurl) {
this.webhookurl = webhookurl;
}
/**
* 消息推送
* @param robotId 机器人key
* @param body 请求参数体
* @return
*/
@Override
public WebhookResponse send(String robotId, WebhookBody body) {
String sendUrl = webhookurl + "/send?key=" + robotId;
String params = GsonTool.toJson(body);
Map<String, String> headerMap = new HashMap<>();
String resJson = RestClient.postToJson(sendUrl, params, headerMap);
return GsonTool.fromJson(resJson, WebhookResponse.class);
}
/**
* 消息推送
* @param robotId 机器人key
* @param jsonParam 请求参数JSON
* @return
*/
@Override
public WebhookResponse send(String robotId, String jsonParam) {
String sendUrl = webhookurl + "/send?key=" + robotId;
Map<String, String> headerMap = new HashMap<>();
String resJson = RestClient.postToJson(sendUrl, jsonParam, headerMap);
return GsonTool.fromJson(resJson, WebhookResponse.class);
}
/**
* 上传文件
* 使用multipart/form-data POST上传文件或语音, 文件标识名为"media"
*
* 所有类型的文件大小均要求大于5个字节
* 普通文件(file):文件大小不超过20M
* 语音(voice):文件大小不超过2M,播放长度不超过60s,仅支持AMR格式
*
* @param robotId 机器人key
* @param type 文件类型,分别有语音(voice)和普通文件(file)
* @param file
* @return
*/
@Override
public WebhookUploadMediaResponse uploadFile(String robotId, String type, File file) {
// 上传文件
String urlString = webhookurl + "/upload_media?key=" + robotId + "&type=file";
HashMap<String, String> params = new HashMap<>();
// 调用接口凭证, 消息推送webhookurl中的key参数
params.put("key", robotId);
// 文件类型,分别有语音(voice)和普通文件(file)
params.put("type", type);
HashMap<String, String> headerMap = new HashMap<>();
String resJson = RestClient.postToFormData(urlString, params, headerMap, file);
// {"errcode":0,"errmsg":"ok","type":"file","media_id":"31mP38KDISpVm8FHlYHkYAoUmcIPTjSGjZU8aBF0LHwN5lk1Vev4h6ADufssKKib4urz3MbMrTVZ8e-ulXeqvCg","created_at":"1742282248"}
return GsonTool.fromJson(resJson, WebhookUploadMediaResponse.class);
}
}