springboot接入企业微信群机器人消息推送

企业微信群机器人消息推送文档地址

链接: 企业微信群机器人消息推送配置说明

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);
    }


}
相关推荐
向阳逐梦4 小时前
机器人描述文件xacro(urdf扩展)
机器人
_院长大人_5 小时前
SpringBoot + 百度内容安全实战:自定义注解 + AOP 实现统一内容审核(支持文本 / 图片 / 视频 + 白名单 + 动态开关)
spring boot·安全·音视频
yzq-38415 小时前
Websocket两台服务器之间的通信
spring boot·websocket·网络协议
摇滚侠6 小时前
Spring Boot3零基础教程,SpringSecurity 测试,笔记81
spring boot·笔记·后端
摇滚侠7 小时前
Spring Boot3零基础教程,定制 Health 健康端点,笔记83
spring boot·笔记·spring
赤壁淘沙8 小时前
开源底盘+机械臂机器人:Lekiwi驱动链路分析
机器人
No0d1es10 小时前
青少年机器人技术(六级)等级考试试卷-实操题(2025年9月)
青少年编程·机器人·电子学会·六级·实际操作
一 乐10 小时前
商城推荐系统|基于SprinBoot+vue的商城推荐系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·商城推荐系统
后端小张11 小时前
【JAVA 进阶】Mybatis-Plus 实战使用与最佳实践
java·spring boot·spring·spring cloud·tomcat·mybatis·mybatis plus