diff --git a/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java b/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java index 664ff07..58c2dba 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java +++ b/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java @@ -48,6 +48,12 @@ public class SendAccountConstant { public static final String DING_DING_ROBOT_ACCOUNT_KEY = "dingDingRobotAccount"; public static final String DING_DING_ROBOT_PREFIX = "ding_ding_robot_"; + /** + * 企业微信群机器人 账号 + */ + public static final String ENTERPRISE_WECHAT_ROBOT_ACCOUNT_KEY = "enterpriseWechatAccountRobot"; + public static final String ENTERPRISE_WECHAT_ROBOT_PREFIX = "enterprise_wechat_robot_"; + /** * 企业微信 应用消息 账号 */ diff --git a/austin-common/src/main/java/com/java3y/austin/common/dto/account/EnterpriseWeChatRobotAccount.java b/austin-common/src/main/java/com/java3y/austin/common/dto/account/EnterpriseWeChatRobotAccount.java new file mode 100644 index 0000000..b4da99a --- /dev/null +++ b/austin-common/src/main/java/com/java3y/austin/common/dto/account/EnterpriseWeChatRobotAccount.java @@ -0,0 +1,24 @@ +package com.java3y.austin.common.dto.account; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 企业微信 机器人 账号信息 + * + * @author 3y + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EnterpriseWeChatRobotAccount { + /** + * 自定义群机器人中的 webhook + */ + private String webhook; + +} diff --git a/austin-common/src/main/java/com/java3y/austin/common/dto/model/EnterpriseWeChatContentModel.java b/austin-common/src/main/java/com/java3y/austin/common/dto/model/EnterpriseWeChatContentModel.java index a78cc86..8c1f2c9 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/dto/model/EnterpriseWeChatContentModel.java +++ b/austin-common/src/main/java/com/java3y/austin/common/dto/model/EnterpriseWeChatContentModel.java @@ -60,7 +60,7 @@ public class EnterpriseWeChatContentModel extends ContentModel { /** * 图文消息(mpnews) - * [{"title":"Title","thumb_media_id":"MEDIA_ID","author":"Author","content_source_url":"URL","content":"Content","digest":"Digest description"}] + * [{"title":"Title","thumb_media_id":"MEDIA_ID","author":"Author","content_source_url":"URL","content":"Content","digest":"Digest description"}] */ private String mpNewsArticle; @@ -74,12 +74,8 @@ public class EnterpriseWeChatContentModel extends ContentModel { private String contentItems; - /** * 其他消息类型: https://developer.work.weixin.qq.com/document/path/90372#%E6%96%87%E6%9C%AC%E6%B6%88%E6%81%AF */ - - - } diff --git a/austin-common/src/main/java/com/java3y/austin/common/dto/model/EnterpriseWeChatRobotContentModel.java b/austin-common/src/main/java/com/java3y/austin/common/dto/model/EnterpriseWeChatRobotContentModel.java new file mode 100644 index 0000000..926c3ab --- /dev/null +++ b/austin-common/src/main/java/com/java3y/austin/common/dto/model/EnterpriseWeChatRobotContentModel.java @@ -0,0 +1,44 @@ +package com.java3y.austin.common.dto.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author 3y + * 企业微信群 机器人 + *

+ * https://developer.work.weixin.qq.com/document/path/91770#%E6%96%87%E6%9C%AC%E7%B1%BB%E5%9E%8B + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EnterpriseWeChatRobotContentModel extends ContentModel { + + /** + * 发送类型 + */ + private String sendType; + + /** + * 发送内容 + */ + private String content; + + /** + * 媒体Id + */ + private String mediaId; + + /** + * 图文消息:[{"title":"中秋节礼品领取","description":"今年中秋节公司有豪礼相送","url":"www.qq.com","picurl":"http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png"}] + */ + private String articles; + + /** + * 图片路径 + */ + private String imagePath; +} diff --git a/austin-common/src/main/java/com/java3y/austin/common/enums/ChannelType.java b/austin-common/src/main/java/com/java3y/austin/common/enums/ChannelType.java index ea8b736..114eed4 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/enums/ChannelType.java +++ b/austin-common/src/main/java/com/java3y/austin/common/enums/ChannelType.java @@ -26,6 +26,7 @@ public enum ChannelType { ENTERPRISE_WE_CHAT(70, "EnterpriseWeChat(企业微信)", EnterpriseWeChatContentModel.class, "enterprise_we_chat"), DING_DING_ROBOT(80, "dingDingRobot(钉钉机器人)", DingDingRobotContentModel.class, "ding_ding_robot"), DING_DING_WORK_NOTICE(90, "dingDingWorkNotice(钉钉工作通知)", DingDingWorkContentModel.class, "ding_ding_work_notice"), + ENTERPRISE_WE_CHAT_ROBOT(100, "EnterpriseWeChat(企业微信机器人)", EnterpriseWeChatRobotContentModel.class, "enterprise_we_chat_robot"), ; /** diff --git a/austin-common/src/main/java/com/java3y/austin/common/enums/SendMessageType.java b/austin-common/src/main/java/com/java3y/austin/common/enums/SendMessageType.java index 47c939f..2dfeb1a 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/enums/SendMessageType.java +++ b/austin-common/src/main/java/com/java3y/austin/common/enums/SendMessageType.java @@ -14,20 +14,20 @@ import lombok.ToString; @AllArgsConstructor public enum SendMessageType { - TEXT("10", "文本", "text", "text"), - VOICE("20", "语音", null, "voice"), - VIDEO("30", "视频", null, null), - NEWS("40", "图文", "feedCard", null), - TEXT_CARD("50", "文本卡片", null, null), - FILE("60", "文件", null, "file"), - MINI_PROGRAM_NOTICE("70", "小程序通知", null, null), - MARKDOWN("80", "markdown", "markdown", "markdown"), - TEMPLATE_CARD("90", "模板卡片", null, null), - IMAGE("100", "图片", null, "image"), - LINK("110", "链接消息", "link", "link"), - ACTION_CARD("120", "跳转卡片消息", "actionCard", "action_card"), - OA("130", "OA消息", null, "oa"), - MP_NEWS("140", "图文消息(mpnews)", null, null), + TEXT("10", "文本", "text", "text","text"), + VOICE("20", "语音", null, "voice",null), + VIDEO("30", "视频", null, null,null), + NEWS("40", "图文", "feedCard", null,"news"), + TEXT_CARD("50", "文本卡片", null, null,null), + FILE("60", "文件", null, "file","file"), + MINI_PROGRAM_NOTICE("70", "小程序通知", null, null,null), + MARKDOWN("80", "markdown", "markdown", "markdown","markdown"), + TEMPLATE_CARD("90", "模板卡片", null, null,"template_card"), + IMAGE("100", "图片", null, "image","image"), + LINK("110", "链接消息", "link", "link",null), + ACTION_CARD("120", "跳转卡片消息", "actionCard", "action_card",null), + OA("130", "OA消息", null, "oa",null), + MP_NEWS("140", "图文消息(mpnews)", null, null,null), ; @@ -44,6 +44,11 @@ public enum SendMessageType { */ private String dingDingWorkType; + /** + * 企业微信机器人的类型值 + */ + private String enterpriseWeChatRobotType; + /** * 通过code获取钉钉机器人的Type值 @@ -75,5 +80,19 @@ public enum SendMessageType { return null; } + /** + * 通过code获取企业微信机器人的Type值 + * + * @param code + * @return + */ + public static String getEnterpriseWeChatRobotTypeByCode(String code) { + for (SendMessageType value : SendMessageType.values()) { + if (value.getCode().equals(code)) { + return value.getEnterpriseWeChatRobotType(); + } + } + return null; + } } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/robot/EnterpriseWeChatRobotParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/robot/EnterpriseWeChatRobotParam.java new file mode 100644 index 0000000..d2084eb --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/robot/EnterpriseWeChatRobotParam.java @@ -0,0 +1,437 @@ +package com.java3y.austin.handler.domain.wechat.robot; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 钉钉自定义机器人 入参 + * + * https://open.dingtalk.com/document/group/custom-robot-access + * + * @author 3y + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EnterpriseWeChatRobotParam { + + /** + * msgtype + */ + @JSONField(name = "msgtype") + private String msgType; + /** + * text + */ + @JSONField(name = "text") + private TextDTO text; + /** + * markdown + */ + @JSONField(name = "markdown") + private MarkdownDTO markdown; + + /** + * markdown + */ + @JSONField(name = "image") + private ImageDTO image; + /** + * news + */ + @JSONField(name = "news") + private NewsDTO news; + /** + * file + */ + @JSONField(name = "file") + private FileDTO file; + /** + * templateCard + */ + @JSONField(name = "template_card") + private TemplateCardDTO templateCard; + + /** + * TextDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class TextDTO { + /** + * content + */ + @JSONField(name = "content") + private String content; + /** + * mentionedList + */ + @JSONField(name = "mentioned_list") + private List mentionedList; + /** + * mentionedMobileList + */ + @JSONField(name = "mentioned_mobile_list") + private List mentionedMobileList; + } + + /** + * MarkdownDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class MarkdownDTO { + /** + * content + */ + @JSONField(name = "content") + private String content; + } + /** + * ImageDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class ImageDTO { + /** + * base64 + */ + @JSONField(name = "base64") + private String base64; + + @JSONField(name = "md5") + private String md5; + } + + /** + * NewsDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class NewsDTO { + /** + * articles + */ + @JSONField(name = "articles") + private List articles; + + /** + * ArticlesDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class ArticlesDTO { + /** + * title + */ + @JSONField(name = "title") + private String title; + /** + * description + */ + @JSONField(name = "description") + private String description; + /** + * url + */ + @JSONField(name = "url") + private String url; + /** + * picurl + */ + @JSONField(name = "picurl") + private String picurl; + } + } + + /** + * FileDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class FileDTO { + /** + * mediaId + */ + @JSONField(name = "media_id") + private String mediaId; + } + + /** + * TemplateCardDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class TemplateCardDTO { + /** + * cardType + */ + @JSONField(name = "card_type") + private String cardType; + /** + * source + */ + @JSONField(name = "source") + private SourceDTO source; + /** + * mainTitle + */ + @JSONField(name = "main_title") + private MainTitleDTO mainTitle; + /** + * emphasisContent + */ + @JSONField(name = "emphasis_content") + private EmphasisContentDTO emphasisContent; + /** + * quoteArea + */ + @JSONField(name = "quote_area") + private QuoteAreaDTO quoteArea; + /** + * subTitleText + */ + @JSONField(name = "sub_title_text") + private String subTitleText; + /** + * horizontalContentList + */ + @JSONField(name = "horizontal_content_list") + private List horizontalContentList; + /** + * jumpList + */ + @JSONField(name = "jump_list") + private List jumpList; + /** + * cardAction + */ + @JSONField(name = "card_action") + private CardActionDTO cardAction; + + /** + * SourceDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class SourceDTO { + /** + * iconUrl + */ + @JSONField(name = "icon_url") + private String iconUrl; + /** + * desc + */ + @JSONField(name = "desc") + private String desc; + /** + * descColor + */ + @JSONField(name = "desc_color") + private Integer descColor; + } + + /** + * MainTitleDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class MainTitleDTO { + /** + * title + */ + @JSONField(name = "title") + private String title; + /** + * desc + */ + @JSONField(name = "desc") + private String desc; + } + + /** + * EmphasisContentDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class EmphasisContentDTO { + /** + * title + */ + @JSONField(name = "title") + private String title; + /** + * desc + */ + @JSONField(name = "desc") + private String desc; + } + + /** + * QuoteAreaDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class QuoteAreaDTO { + /** + * type + */ + @JSONField(name = "type") + private Integer type; + /** + * url + */ + @JSONField(name = "url") + private String url; + /** + * appid + */ + @JSONField(name = "appid") + private String appid; + /** + * pagepath + */ + @JSONField(name = "pagepath") + private String pagepath; + /** + * title + */ + @JSONField(name = "title") + private String title; + /** + * quoteText + */ + @JSONField(name = "quote_text") + private String quoteText; + } + + /** + * CardActionDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class CardActionDTO { + /** + * type + */ + @JSONField(name = "type") + private Integer type; + /** + * url + */ + @JSONField(name = "url") + private String url; + /** + * appid + */ + @JSONField(name = "appid") + private String appid; + /** + * pagepath + */ + @JSONField(name = "pagepath") + private String pagepath; + } + + /** + * HorizontalContentListDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class HorizontalContentListDTO { + /** + * keyname + */ + @JSONField(name = "keyname") + private String keyname; + /** + * value + */ + @JSONField(name = "value") + private String value; + /** + * type + */ + @JSONField(name = "type") + private Integer type; + /** + * url + */ + @JSONField(name = "url") + private String url; + /** + * mediaId + */ + @JSONField(name = "media_id") + private String mediaId; + } + + /** + * JumpListDTO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class JumpListDTO { + /** + * type + */ + @JSONField(name = "type") + private Integer type; + /** + * url + */ + @JSONField(name = "url") + private String url; + /** + * title + */ + @JSONField(name = "title") + private String title; + /** + * appid + */ + @JSONField(name = "appid") + private String appid; + /** + * pagepath + */ + @JSONField(name = "pagepath") + private String pagepath; + } + } +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EnterpriseWeChatRobotHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EnterpriseWeChatRobotHandler.java new file mode 100644 index 0000000..f2ddae4 --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EnterpriseWeChatRobotHandler.java @@ -0,0 +1,106 @@ +package com.java3y.austin.handler.handler.impl; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.file.FileReader; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.crypto.digest.MD5; +import cn.hutool.http.ContentType; +import cn.hutool.http.Header; +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.base.Throwables; +import com.java3y.austin.common.constant.SendAccountConstant; +import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.common.dto.account.EnterpriseWeChatRobotAccount; +import com.java3y.austin.common.dto.model.EnterpriseWeChatRobotContentModel; +import com.java3y.austin.common.enums.ChannelType; +import com.java3y.austin.common.enums.SendMessageType; +import com.java3y.austin.handler.domain.wechat.robot.EnterpriseWeChatRobotParam; +import com.java3y.austin.handler.handler.BaseHandler; +import com.java3y.austin.handler.handler.Handler; +import com.java3y.austin.support.domain.MessageTemplate; +import com.java3y.austin.support.utils.AccountUtils; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; + +/** + * 企业微信群机器人 消息处理器 + * + * @author 3y + */ +@Slf4j +@Service +public class EnterpriseWeChatRobotHandler extends BaseHandler implements Handler { + + @Autowired + private AccountUtils accountUtils; + + public EnterpriseWeChatRobotHandler() { + channelCode = ChannelType.ENTERPRISE_WE_CHAT_ROBOT.getCode(); + } + + @Override + public boolean handler(TaskInfo taskInfo) { + try { + EnterpriseWeChatRobotAccount account = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.ENTERPRISE_WECHAT_ROBOT_ACCOUNT_KEY, SendAccountConstant.ENTERPRISE_WECHAT_ROBOT_PREFIX, EnterpriseWeChatRobotAccount.class); + EnterpriseWeChatRobotParam enterpriseWeChatRobotParam = assembleParam(taskInfo); + String result = HttpRequest.post(account.getWebhook()).header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue()) + .body(JSON.toJSONString(enterpriseWeChatRobotParam)) + .timeout(2000) + .execute().body(); + JSONObject jsonObject = JSON.parseObject(result); + if (jsonObject.getInteger("errcode") != 0) { + return true; + } + log.error("EnterpriseWeChatRobotHandler#handler fail! result:{},params:{}", JSON.toJSONString(jsonObject), JSON.toJSONString(taskInfo)); + } catch (Exception e) { + log.error("EnterpriseWeChatRobotHandler#handler fail!e:{},params:{}", Throwables.getStackTraceAsString(e), JSON.toJSONString(taskInfo)); + } + return false; + } + + private EnterpriseWeChatRobotParam assembleParam(TaskInfo taskInfo) { + EnterpriseWeChatRobotContentModel contentModel = (EnterpriseWeChatRobotContentModel) taskInfo.getContentModel(); + EnterpriseWeChatRobotParam param = EnterpriseWeChatRobotParam.builder() + .msgType(SendMessageType.getEnterpriseWeChatRobotTypeByCode(contentModel.getSendType())).build(); + + if (SendMessageType.TEXT.getCode().equals(contentModel.getSendType())) { + param.setText(EnterpriseWeChatRobotParam.TextDTO.builder().content(contentModel.getContent()).build()); + } + if (SendMessageType.MARKDOWN.getCode().equals(contentModel.getSendType())) { + param.setMarkdown(EnterpriseWeChatRobotParam.MarkdownDTO.builder().content(contentModel.getContent()).build()); + } + if (SendMessageType.IMAGE.getCode().equals(contentModel.getSendType())) { + FileReader fileReader = new FileReader(contentModel.getImagePath()); + byte[] bytes = fileReader.readBytes(); + param.setImage(EnterpriseWeChatRobotParam.ImageDTO.builder().base64(Base64.encode(bytes)) + .md5(DigestUtil.md5Hex(bytes)).build()); + } + if (SendMessageType.FILE.getCode().equals(contentModel.getSendType())) { + param.setFile(EnterpriseWeChatRobotParam.FileDTO.builder().mediaId(contentModel.getMediaId()).build()); + } + if (SendMessageType.NEWS.getCode().equals(contentModel.getSendType())) { + List articlesDTOS = JSON.parseArray(contentModel.getArticles(), EnterpriseWeChatRobotParam.NewsDTO.ArticlesDTO.class); + param.setNews(EnterpriseWeChatRobotParam.NewsDTO.builder().articles(articlesDTOS).build()); + } + if (SendMessageType.TEMPLATE_CARD.getCode().equals(contentModel.getSendType())) { + // + } + return param; + } + + @Override + public void recall(MessageTemplate messageTemplate) { + + } +} + diff --git a/austin-support/src/main/java/com/java3y/austin/support/pending/AbstractLazyPending.java b/austin-support/src/main/java/com/java3y/austin/support/pending/AbstractLazyPending.java index f3cc326..6e5f313 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/pending/AbstractLazyPending.java +++ b/austin-support/src/main/java/com/java3y/austin/support/pending/AbstractLazyPending.java @@ -68,6 +68,7 @@ public abstract class AbstractLazyPending { // 判断是否停止当前线程 if (stop && CollUtil.isEmpty(tasks)) { + executorService.shutdown(); break; } } catch (Exception e) { @@ -75,7 +76,7 @@ public abstract class AbstractLazyPending { } } }); - executorService.shutdown(); + } /**