目录

  • 背景
    • 需求目标
    • 企业微信群聊机器人
      • 微信官方文档
      • 简单介绍一下
  • 名词解释
  • 具体实现
    • 类清单及功能说明
    • MessageSendService
    • EnterpriceWechatRobotMessageSendService
    • EnterpriseWeChatRobotMessageSendServiceImpl
    • EnterpriseRobotMessageDO
  • 测试Demo(使用方式)
    • 服务Bean构建
    • 测试用例

背景

需求目标

本次要做的东西需要满足如下要求

  • 方便快捷地生成对应类型的消息实体(建造者模式)
  • 提供一套Service实现,可以让各个模块快速调用并发送消息,并能够提供如下的使用模式
    • 构建服务时指定发送机器人,发送时无需关注,仅调用服务
    • 消息实体内自带Robot Key,服务根据Robot Key发送到指定机器人

OK,先简单介绍一下企业微信群聊机器人

企业微信群聊机器人


最近发现企业微信的robot特别好用,可以用较为简便的方式推送消息,甚至可以将机器人加入不同的群聊,灵活推送各类消息。完成一些企业微信服务号完成不了的工作

微信官方文档

《如何配置群聊机器人》

简单介绍一下

直接在群聊上点击右键即可添加“群聊机器人”

点击已添加的机器人可看到webhook地址

通过使用说明,可以推送不同种类的消息到群聊内

名词解释

  • 企业微信群聊机器人:本次需求的目标调用对象,可以在企业微信群聊中发送指定格式消息,下称Robot
  • Robot Key:企业微信机器人WebHook接口的Key参数。如https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=8606ac1f-dc01-423s3-9d74-121343ef539的Robot Key为8606ac1f-dc01-423s3-9d74-121343ef539

具体实现

类清单及功能说明

  • MessageSendService

    • 基类服务接口,定义发送服务
    • 使用泛型定义发送的消息实体,继承接口可定义具体定义实体类
  • EnterpriceWechatRobotMessageSendService
    • 企业微信Robot消息发送服务
    • 继承MessageSendService,定义消息实体类
  • EnterpriseWeChatRobotMessageSendServiceImpl
    • EnterpriceWechatRobotMessageSendService的实现类
  • EnterpriseRobotMessageDO
    • 消息实体类,用于构建Robot消息
  • ResultDTO
    • 返回结果的实体,可自行定义,不做具体赘述

MessageSendService

  • 基类服务接口,定义发送服务
  • 使用泛型定义发送的消息实体,继承接口可定义具体定义实体类
  • 使用泛型的目的:定义MessageSend系列服务,可扩展其他的消息发送方式
package demo.service.wechat.inter;import demo.common.ResultDTO;/*** MessageSendService* 企业微信:微信消息推送接口** @author John Chen* @since 2019/12/12*/
public interface MessageSendService<T> {/*** 推送消息** @param msg 消息体* @return 返回推送结果*/ResultDTO<String> sendMassage(T msg);
}

EnterpriceWechatRobotMessageSendService

  • 企业微信Robot消息发送服务
  • 继承MessageSendService,定义消息实体类
  • 没有定义新的接口方法,沿用MessageSendService内的方法作为唯一实现方法
package demo.service.wechat.inter;import demo.model.wechat.EnterpriseRobotMessageDO;/*** EnterpriceWechatRobotMessageSendService* 企业微信机器人消息发送接口** @author John Chen* @since 2019/12/13*/
public interface EnterpriceWechatRobotMessageSendService extends MessageSendService<EnterpriseRobotMessageDO> {}

EnterpriseWeChatRobotMessageSendServiceImpl

  • EnterpriceWechatRobotMessageSendService的实现类
  • 说明一下其中几个自有类,可以在实际使用中删除
    • SkynetUtils:封装了公司内部日志系统日志记录功能的实现类(记录日志)
    • EnumSkynetLogModule:日志记录相关
    • EnumSkynetCategoryWechatMessageSend:日志记录相关
    • IllegalInputVariableException:自定义Exception,可以在实际使用时变更为Exception
    • OkHttp3Utils:OKHttp3封装的http服务类,用于发起http请求,可使用HTTPClient等框架代替。具体实现可参看我的另一篇原创文章《(Java)高性能Http框架:OKHttp3的工具类OkHttp3Utils实现(可使用Http代理)》
package demo.service.wechat.impl;import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import demo.common.ResultDTO;
import demo.common.enumlibrary.skynet.EnumSkynetLogModule;
import demo.common.enumlibrary.skynet.category.EnumSkynetCategoryWechatMessageSend;
import demo.common.myexception.IllegalInputVariableException;
import demo.common.utls.OkHttp3Utils;
import demo.common.utls.SkynetUtils;
import demo.model.wechat.EnterpriseRobotMessageDO;
import demo.service.wechat.inter.EnterpriceWechatRobotMessageSendService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;/*** EnterpriseWeChatRobotMessageSendServiceImpl* 企业微信机器人消息推送接口** @author John Chen* @since 2019/12/12*/
@Slf4j
public class EnterpriseWeChatRobotMessageSendServiceImpl implements EnterpriceWechatRobotMessageSendService {private final static String MODULE = EnumSkynetLogModule.WECHAT_MESSAGE_SEND.getName();private final static String CATEGORY = EnumSkynetCategoryWechatMessageSend.ENTERPRISE_ROBOT.getName();/*** 企业微信robot推送地址。在构建的时候传进来*/private final String defaultPushUrl;/*** 企业微信robot推送任务名称,无实际逻辑用途,仅用于记录日志*/private final String jobName;private final Gson gson;/*** 企业微信机器人推送地址*/private final String wxEnterpriseRobotPushUrl;private static final String ERR_CODE_KEY = "errcode";private static final int ERR_CODE_SUCCESS_VALUE = 0;private static final String ERR_MSG_KEY = "errmsg";/*** OkHttp3实例*/private OkHttp3Utils okHttp3Utils = new OkHttp3Utils();/*** 构造方法** @param defaultPushUrl           默认消息推送地址* @param jobName                  名称* @param gson                     gson* @param wxEnterpriseRobotPushUrl 企业微信消息推送地址,参考地址:https://qyapi.weixin.qq.com/cgi-bin/webhook/send*/public EnterpriseWeChatRobotMessageSendServiceImpl(String defaultPushUrl, String jobName, Gson gson, String wxEnterpriseRobotPushUrl) {this.defaultPushUrl = defaultPushUrl;this.jobName = jobName;this.gson = gson;this.wxEnterpriseRobotPushUrl = wxEnterpriseRobotPushUrl;}/*** 构造方法** @param defaultPushUrl 默认消息推送地址* @param jobName        名称* @param gson           gson*/public EnterpriseWeChatRobotMessageSendServiceImpl(String defaultPushUrl, String jobName, Gson gson) {this.defaultPushUrl = defaultPushUrl;this.jobName = jobName;this.gson = gson;this.wxEnterpriseRobotPushUrl = null;}/*** 推送消息** @param msg 消息体* @return 返回推送结果*/@Overridepublic ResultDTO<String> sendMassage(EnterpriseRobotMessageDO msg) {try {String msgStr = gson.toJson(msg);log.info("收到企业微信推送请求,job:{};开始推送消息:{}", jobName, msgStr);String pushUrl = getPushUrl(msg);log.debug("推送地址:{}", pushUrl);//推送消息String resultStr = okHttp3Utils.post(pushUrl, msgStr);//记录结果并返回log.info("推送结果:{}", resultStr);JSONObject resultJson = JSONObject.parseObject(resultStr);boolean success = resultJson.getInteger(ERR_CODE_KEY) == ERR_CODE_SUCCESS_VALUE;String resMsg = resultJson.getString(ERR_MSG_KEY);if (!success) {SkynetUtils.printError(String.format("消息推送失败:%s", resMsg), MODULE, CATEGORY, "消息推送失败", jobName, "", null);return new ResultDTO<>(success, "发送消息成功", resultJson.getString(ERR_CODE_KEY), resMsg);}return new ResultDTO<>(success, "发送消息成功", "200", resMsg);} catch (Exception e) {String errMsg = String.format("消息推送异常:%s", e.getMessage());SkynetUtils.printError(errMsg, MODULE, CATEGORY, "消息推送异常", jobName, "", e);return new ResultDTO<>(false, "消息发送异常", "999", errMsg);}}/*** 获取推送的url地址(带key参数)** @param msg 入参* @return 返回地址;如果无法组成地址,则返回null*/private String getPushUrl(EnterpriseRobotMessageDO msg) {String key = msg.getRobotKey();if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(wxEnterpriseRobotPushUrl)) {final String urlKey = "key";return wxEnterpriseRobotPushUrl + "?" + urlKey + "=" + key;} else if (!StringUtils.isEmpty(defaultPushUrl)) {return defaultPushUrl;} else {throw new IllegalInputVariableException("入参中key为空或企业微信机器人推送地址为空,且任务未配置默认推送地址,无法组合出推送地址,请检查代码!");}}
}

EnterpriseRobotMessageDO

  • 消息实体类,用于构建Robot消息
  • 使用建造者模式,在具体使用过程时根据不同需求选择构建不同的Builder
package demo.model.wechat;import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;/*** EnterpriseRobotMessageDO* 企业微信机器人消息推送实体** @author John Chen* @since 2019/12/12*/
@ToString
public class EnterpriseRobotMessageDO {public final static String MSG_TYPE_TEXT = "text";public final static String MSG_TYPE_MARKDOWN = "markdown";public final static String MSG_TYPE_IMAGE = "image";public final static String MSG_TYPE_NEWS = "news";/*** 推送robot key(不填则由实现决定如何处理)* 优先使用pushKey*/@Getterprivate String robotKey;/*** 消息类型枚举*/private String msgtype;//region 不同消息类型用到的不同字段。每次仅需要实例化1个即可/*** type=text时需要去构建的实体*/private TextType text;/*** type=markdown时需要去构建的实体*/private MarkdownType markdown;/*** type=image时需要去构建的实体*/private ImageType image;/*** type=news时需要去构建的实体*/private NewsType news;//endregion/*** 构建一个Text类型消息实体Builder** @param content 消息内容* @return 返回builder*/public static TextBuilder textBuilder(String content) {return new TextBuilder(content);}/*** 构建一个Markdown类型消息实体** @param content 消息内容(Markdown格式)* @return 返回builder*/public static MarkdownBuilder markdownBuilder(String content) {return new MarkdownBuilder(content);}/*** 构建一个Image类型消息实体** @param base64 图片内容的base64编码;无需增加类似data:image/png;base64,的头。这一点要注意,因为在线转换工具大多会带上这个前缀* @param md5    图片内容(base64编码前)的md5值;* @return 返回builder*/public static ImageBuilder imageBuilder(String base64, String md5) {return new ImageBuilder(base64, md5);}/*** 构建一个news类型消息实体** @param title       标题,不超过128个字节,超过会自动截断* @param url         点击后跳转的链接。* @param description 描述,不超过512个字节,超过会自动截断 非必填* @param picUrl      图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图 1068*455,小图150*150。 非必填* @return 返回builder*/public static NewsBuilder newsBuilder(String title, String url, String description, String picUrl) {return new NewsBuilder(title, url, description, picUrl);}public static NewsBuilder newsBuilder(String title, String url, String description) {return new NewsBuilder(title, url, description, null);}public static NewsBuilder newsBuilder(String title, String url) {return new NewsBuilder(title, url, null, null);}//region 消息实体类@AllArgsConstructorprivate static class TextType {/*** 文本内容,最长不超过2048个字节,必须是utf8编码*/private String content;/*** userid的列表,提醒群中的指定成员(@某个成员),@all表示提醒所有人,如果开发者获取不到userid,可以使用mentioned_mobile_list*/private List<String> mentioned_list;/*** 手机号列表,提醒手机号对应的群成员(@某个成员),@all表示提醒所有人*/private List<String> mentioned_mobile_list;}@AllArgsConstructorprivate static class MarkdownType {/*** markdown内容,最长不超过4096个字节,必须是utf8编码*/private String content;}@AllArgsConstructorprivate static class ImageType {/*** 图片内容的base64编码*/private String base64;/*** 图片内容(base64编码前)的md5值*/private String md5;}@AllArgsConstructorprivate static class NewsType {/*** 图文消息,一个图文消息支持1到8条图文*/private List<Article> articles;/*** 图文消息实体*/@AllArgsConstructorprivate static class Article {/*** 标题,不超过128个字节,超过会自动截断*/private String title;/*** 描述,不超过512个字节,超过会自动截断* 非必填*/private String description;/*** 点击后跳转的链接。*/private String url;/*** 图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图 1068*455,小图150*150。* 非必填*/private String picurl;}}//endregion//region 各类构造方法,用于构建不同的消息类型实体private EnterpriseRobotMessageDO(NewsType news) {this.msgtype = MSG_TYPE_NEWS;this.news = news;}private EnterpriseRobotMessageDO(NewsType news, String robotKey) {this.msgtype = MSG_TYPE_NEWS;this.news = news;this.robotKey = robotKey;}private EnterpriseRobotMessageDO(ImageType image) {this.msgtype = MSG_TYPE_IMAGE;this.image = image;}private EnterpriseRobotMessageDO(ImageType image, String robotKey) {this.msgtype = MSG_TYPE_IMAGE;this.image = image;this.robotKey = robotKey;}private EnterpriseRobotMessageDO(MarkdownType markdown) {this.msgtype = MSG_TYPE_MARKDOWN;this.markdown = markdown;}private EnterpriseRobotMessageDO(MarkdownType markdown, String robotKey) {this.msgtype = MSG_TYPE_MARKDOWN;this.markdown = markdown;this.robotKey = robotKey;}private EnterpriseRobotMessageDO(TextType text) {this.msgtype = MSG_TYPE_TEXT;this.text = text;}private EnterpriseRobotMessageDO(TextType text, String robotKey) {this.msgtype = MSG_TYPE_TEXT;this.text = text;this.robotKey = robotKey;}//endregion//region 不同消息类型的Builder/*** Text类型消息Builder*/public static class TextBuilder {/*** 当需要@all时候需要填入mentioned_list或mentioned_mobile_list中的*/private static final String AT_ALL = "@all";private String content;private List<String> mentionedList;private List<String> mentionedMobileList;/*** 构造方法,消息体必填** @param content 消息体*/private TextBuilder(String content) {this.content = content;}/*** 添加userId,用于在消息中@某人** @param mentioned 企业微信userId* @return 返回建造者本身*/public TextBuilder addUserIdForAt(String... mentioned) {if (mentioned != null && mentioned.length > 0) {if (mentionedList == null) {mentionedList = new ArrayList<>();}mentionedList.addAll(Arrays.asList(mentioned));}return this;}/*** 添加手机号,用于添加某人* 当无法获取到userId的时候,则可以添加手机号(需要是企业微信绑定的)** @param mobiles 企业微信userId* @return 返回建造者本身*/public TextBuilder addMobileForAt(String... mobiles) {if (mobiles != null && mobiles.length > 0) {if (mentionedMobileList == null) {mentionedMobileList = new ArrayList<>();}mentionedMobileList.addAll(Arrays.asList(mobiles));}return this;}public TextBuilder atAll() {addMobileForAt(AT_ALL);return this;}public EnterpriseRobotMessageDO build() {return new EnterpriseRobotMessageDO(new TextType(content, mentionedList, mentionedMobileList));}public EnterpriseRobotMessageDO build(String robotKey) {return new EnterpriseRobotMessageDO(new TextType(content, mentionedList, mentionedMobileList), robotKey);}}/*** Markdown类型消息Builder*/@AllArgsConstructor(access = AccessLevel.PRIVATE)public static class MarkdownBuilder {/*** markdown内容,最长不超过4096个字节,必须是utf8编码*/private String content;public EnterpriseRobotMessageDO build() {return new EnterpriseRobotMessageDO(new MarkdownType(content));}public EnterpriseRobotMessageDO build(String robotKey) {return new EnterpriseRobotMessageDO(new MarkdownType(content), robotKey);}}/*** Image类型消息Builder*/@AllArgsConstructor(access = AccessLevel.PRIVATE)public static class ImageBuilder {/*** 图片内容的base64编码*/private String base64;/*** 图片内容(base64编码前)的md5值*/private String md5;public EnterpriseRobotMessageDO build() {return new EnterpriseRobotMessageDO(new ImageType(base64, md5));}public EnterpriseRobotMessageDO build(String robotKey) {return new EnterpriseRobotMessageDO(new ImageType(base64, md5), robotKey);}}/*** News类型消息Builder*/public static class NewsBuilder {/*** 图文消息,一个图文消息支持1到8条图文*/private List<NewsType.Article> articles;/*** 构造方法** @param title       标题* @param url         跳转地址* @param description 描述(非必填)* @param picUrl      图片地址(非必填)*/private NewsBuilder(String title, String url, String description, String picUrl) {this.articles = new ArrayList<>(Collections.singletonList(new NewsType.Article(title, description, url, picUrl)));}/*** 新增一个图文** @param title       标题* @param url         跳转地址* @param description 描述(可为空)* @param picUrl      图片地址* @return 返回builder*/public NewsBuilder addArticles(String title, String url, String description, String picUrl) {articles.add(new NewsType.Article(title, description, url, picUrl));return this;}/*** 新增一个图文** @param title       标题* @param url         跳转地址* @param description 描述(可为空)* @return 返回builder*/public NewsBuilder addArticles(String title, String url, String description) {return addArticles(title, url, description, null);}/*** 新增一个图文** @param title 标题* @param url   跳转地址* @return 返回builder*/public NewsBuilder addArticles(String title, String url) {return addArticles(title, url, null, null);}public EnterpriseRobotMessageDO build() {return new EnterpriseRobotMessageDO(new NewsType(articles));}public EnterpriseRobotMessageDO build(String robotKey) {return new EnterpriseRobotMessageDO(new NewsType(articles), robotKey);}}//endregion
}

测试Demo(使用方式)

这里通过几个测试Demo来说明一下使用方法

服务Bean构建

  • 下面的案例使用了Spring框架先构建了一个Bean
  • 也可以不通过Spring框架,直接new一个实现
package demo.nelsen.config;import com.google.gson.Gson;
import demo.common.enumlibrary.tccomponent.EnumAirtravelOpsMonitorDataNelsenKeys;
import demo.service.wechat.impl.EnterpriseWeChatRobotMessageSendServiceImpl;
import demo.service.wechat.inter.EnterpriceWechatRobotMessageSendService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** WechatSendMessageConfig* 企业微信消息推送Bean** @author John Chen* @since 2019/12/13*/
@Configuration
public class WechatSendMessageConfig {/*** 企业微信机器人公共推送服务* 注意:使用这个Bean的时候,在发送时需要带上对应的机器人Key,否则会导致发送到默认机器人上** @param gson gson* @return 返回bean* @throws Exception 获取统一配置时可能出现的错误*/@Beanpublic EnterpriceWechatRobotMessageSendService publicWxRobotSendService(Gson gson) throws Exception {return new EnterpriseWeChatRobotMessageSendServiceImpl("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=8606ac1f-dc01-423s3-9d74-121343ef539", "企业微信Robot公共推送服务", gson, "https://qyapi.weixin.qq.com/cgi-bin/webhook/send");}
}

测试用例

下面的测试用例分别构建了4中不同的消息类型进行推送

  • 如果不想使用Spring框架,可以直接new一个EnterpriceWechatRobotMessageSendService
package demo.nelsen.tests;import demo.common.utls.EncodeUtils;
import demo.model.wechat.EnterpriseRobotMessageDO;
import demo.service.wechat.inter.EnterpriceWechatRobotMessageSendService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.io.IOException;/*** WechatSendMessageConfig* 微信发送消息测试用例集** @author John Chen* @since 2019/12/13*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class WechatSendMessageTests {@Autowiredprivate EnterpriceWechatRobotMessageSendService publicWxRobotSendService;@Testpublic void tiantiUploadSendTest() {/*文本消息类型*/EnterpriseRobotMessageDO messageTextDO = EnterpriseRobotMessageDO.textBuilder("nelsen测试用例中-上传动态-测试用例-test消息").addMobileForAt("13800000000").build();assert tianTiUploadRobotSendService.sendMassage(messageTextDO).getSuccess();/*markdown类型消息*/EnterpriseRobotMessageDO messageMarkdownDo = EnterpriseRobotMessageDO.markdownBuilder("# 消息发送测试\n" +">小鲜肉<font color=\"info\">最牛</font>\n" +">消息来自<font color=\"comment\">上传动态-Markdown类型消息</font>").build();assert tianTiUploadRobotSendService.sendMassage(messageMarkdownDo).getSuccess();/*图片类型*/String imageB = "data:image/gif;base64,R0lGODlhdQAmAKIAAOYyL+rU4llg6Jmd8e92dCky4eEGAv///yH5BAAAAAAALAAAAAB1ACYAAAP/eLrc/jC2IEoZMATJu/9gyFVWIUyksIls677LUJbrEcxWDe98f+CWk4I0w/iOSNANKJQBC8mo9LEEDp8F3XR7rOIU2Cx3jHwKsUKyWqRhEEvGN3xN91BoCq8l9tTW/244Rk4mOkBGgIl8VjF+d4V5A5KKf3IWiCCEOZRraGxPnGqeIZpzoVyjDBptDpYmp1yumI9BWq5QUQS6fn+lm3lYmLdSBsW8xcjJBgAABLwfAMhXRQt6ODWuJ8rb3AYK0d3h4OHIBC7jC0TCYb/ZB+Th3/Dc4/PmLOgSvkwK+xjMAAEmCwhQnrc8rFSxGkiwnoFnEnTp0mdqlJw0DgIggwgu/08xABM+KkhGJdm9JJqMhGkyQIAATA3HxVwATuO8jyQfECiXJyYzZwzyOSilcqWEmzkP1ES6LGmDcStshjspNJAgMOwwNmBajGZXGyJVhU22quxOaR7hrahqQ52DC1jh/nj1AG0Eux3BLgu59x3SkxHO9mVLhJfcirUc2IWA96vUbvf+QkNbldCgIGCKHi6h1e/Bu1+VOiYHYAVTwBHyCfWyocoZzZovbf3ok1njzx/IltUg+GG13twMfta0gdDruLH3MOD6uaPDcI8hCAZp4/k24elmtOacGXlcPAuYe/VmnVt0ncioTyeYU+ibQRWOHyCRXEz40BDyivYWYBzQAIbTbeBUNTIZRF1QlNlVimGdfffLSPg9oJ9+vVV4H23smWSgFqrZZc0AWiTWACMXcjAhfgAqgxpXKwDXDXbZsSPjVcth6NNtZj3nzCpM1dDfPDACM+OQ9l2I1HiPlfNceentMoGLygQp5Eq3aKfYaUgOdBIB9RyYSEsmvASiFgG0RMRLEK2RAAA7";EnterpriseRobotMessageDO messageImageDo = EnterpriseRobotMessageDO.imageBuilder(imageB, EncodeUtils.md5bytes(EncodeUtils.decodeBase64(imageB))).build();assert tianTiUploadRobotSendService.sendMassage(messageImageDo).getSuccess();/*图文消息类型*/EnterpriseRobotMessageDO messageNewsDO = EnterpriseRobotMessageDO.newsBuilder("上传动态-news类型消息-跳转", "https://www.baidu.com/", "上传动态-news类型消息-测试用例1", "http://img1.imgtn.bdimg.com/it/u=3334640638,1744228669&fm=26&gp=0.jpg").addArticles("上传动态-news类型消息-跳转TCSchedule", "https://www.baidu.com/", "上传动态-news类型消息-测试用例2", "http://img1.imgtn.bdimg.com/it/u=3334640638,1744228669&fm=26&gp=0.jpg").build();assert tianTiUploadRobotSendService.sendMassage(messageNewsDO).getSuccess();}
}

企业微信Robot(群聊机器人)消息推送Java服务相关推荐

  1. 持续集成之群聊机器人消息推送:钉钉 vs 企业微信

    企业微信和钉钉都有面向群的消息推送机器人,两者的使用非常相近,也有一些细节上的特性的区别,这篇文章将结合前面的使用示例进行总结. 使用方式 不同点 企业微信:先创建群,然后在群中添加机器人,可添加多个 ...

  2. 持续集成之消息推送:钉钉与企业微信的群聊机器人通用示例

    使用钉钉和企业微信的群聊机器人可以进行消息推送,这篇文章使用一个简单的脚本来说明其使用方式. 推送消息的方法 钉钉或者企业微信的群机器人都可提供消息推送的功能,相关的机器人的添加和消息推送的方法可以参 ...

  3. 企业微信通过群聊机器人用springboot发送信息

    前言 学习了一下,如何通过企业微信的群聊机器人发送信息,没想到比想象中的简单,那么这次就来讲讲如何进行通过群聊机器人发送信息吧 步骤 第一步,在自己的企业进行创建一个群聊 然后,在自己的群聊里,添加机 ...

  4. 企业微信接入群聊机器人详细步骤

    目录 一. 创建群机器人 二.机器人配置 三.机器人信息推送 四.线上使用 五.推送效果 一. 创建群机器人 先选择一个企业微信群 右键添加机器人 完善机器人的头像.名称即可 二.机器人配置 查看生成 ...

  5. 友盟消息推送java服务端

    基本概念 appkey:应用唯一标识 app_master_secret:服务器Key. device_token: 友盟后台对设备的唯一标识.注意, Android的device_token是44位 ...

  6. 企业微信SDK接口API调用-触发推送企业微信微信好友

    企业微信SDK接口API调用-触发推送企业微信微信好友 /**      * 触发企业微信推送微信好友列表      * @author wechat:happybabby110      * @bl ...

  7. 接入微信小程序客服消息推送

    微信小程序客服消息推送接入 这两天弄一个客服的消息推送,这里必须吐槽一下,按我以往接微信的东西的感觉这块估计也要踩坑的,而且众所周知微信的文档很坑的也乱.(吐槽一下) 小程序的配置设置: URL(服务 ...

  8. 运用python实现企业微信群机器人消息推送

    使用场景:将BI报表精准推送入(群),精准触达用户 目的:提高管理层对数据的感知度 工具:python+企业微信 步骤: 1.创建企业微信群机器人,提取Webhook地址(群机器人地址) 2.编写代码 ...

  9. springboot集成钉钉_Java(SpringBoot)实现钉钉机器人消息推送

    零.前言 上一次做消息推送,是微信公众号的定时消息通知. 由于自己当时的水平不够,加上企鹅家的开发文档普遍不太友好,导致根本看不懂文档在写什么,不得不去看第三方博客来学习公众号的开发. 这次就不一样了 ...

  10. 微信公众号开发(消息推送)

    文章目录 微信公众号开发 运行效果 微信公众号简介 注册微信公众号 注册测试公众号 搭建微信本地调试环境 微信公众号接入(校验签名) 给指定用户推送消息 网页授权获取用户openid 给指定用户发送模 ...

最新文章

  1. Linux内核实现原子性操作cmpxchg指令的理解
  2. 删了手机里的一个html文件,手机太卡,哪些内容可以毫不犹豫的删除?
  3. qt mysql显示文件名字_【实例】Qt获取文件属性
  4. face recognition[翻译][深度人脸识别:综述]
  5. 大学计算机基础实验指导试题,(大学计算机基础实验指导)模拟试题(二)参考答案...
  6. [error]Cannot create __weak reference in file using manual refer XCode7.3
  7. java序列化和反序列化_Java恶意序列化背后的历史和动机
  8. SpringCloud工作笔记079---SpringBoot中使用CXF集成SpringWebServices_来创建wsdl_WebServices_服务端_以及客户端
  9. tomcat 无法关闭 :8005端口未启动
  10. oracle sql group_con,SQL:Group Functions,GROUP BY,HAVING
  11. contact form 7如何设置placeholder让提示文字显示在输入框中
  12. 21. Django进阶:内建用户系统
  13. 线性(欧拉)筛法筛素数表
  14. 【Scratch】青少年蓝桥杯_每日一题_12.01_角色装扮
  15. session fixation漏洞简述
  16. Echarts南丁格尔图.
  17. JAVA学习笔记——集合
  18. 那些不再追逐互联网的年轻人们,正遇见下一个「互联网」
  19. IT业界新闻资讯网站推荐
  20. SSRF(Server-side Request Forgery)

热门文章

  1. 成都蚂蚁金服java_成都蚂蚁金服暑期实习Java后台开发面经(已收到OC)
  2. 关于 用git clone 命令时报错RPC failed; curl 56 Recv failure....’ 的解决办法
  3. r语言 rep(c(1 3) 4),不倒翁-R语言入门系列4-rep函数的用法
  4. 史上最全最实用网站来袭
  5. android与后台交互,Android客户端与服务端交互
  6. C语言实现 输入两个正整数m和n,求其最大公约数和最小公倍数【学习笔记】
  7. 【LeetCode】图解 904. 水果成篮
  8. 残差的正态性检验——概率图和QQ-plot图
  9. 启动PE系统找不到计算机硬盘,华硕电脑进PE系统找不到硬盘怎么办?
  10. python中获取中位数的两种方法