前言
最近在阅读的时候,看到一篇用企业微信机器人实现定时提醒功能的文章,顿时有了兴趣,于是乎自己动手实操一遍,期间也遇到了一些问题,最后也是顺利地完成了一个简单的SDK的开发,结合自己的经验,写了这样一篇 SDK 开发文章。
什么是SDK?
SDK(Software Development Kit)即 软件开发工具包 ,就是帮助我们开发出软件的工具集合,除了代码之外,一般还要搭配文档、示例等。
一般 SDK 都是需要 引入 到项目中使用的。比如学 Java 的朋友最早接触的 JDK,就是用来开发 Java 软件的工具包,使用时需要编写 类似 import java.util.* 的语法来引入。此外,大部分的 SDK,都是需要通过人工或项目管理工具,将其文件下载到指定路径才能引入。
使用SDK的好处?
比如公司有许多系统都需要实现文件上传的功能
显然我们不需要给每个系统都去开发文件上传,而是只要有一个团队编写一套通用的文件上传SDK,然后让需要实现同样功能的系统去引入即可。可以大大提高开发效率。

因此,编写SDK也就是写一个功能、造一个轮子。好的轮子可以减少一些项目在相同功能上的差异,也可以省去每个系统都去开发同样功能的时间。
上手开发
学习开发第三方应用时,一定要先完整阅读官方文档,比如企业微信群机器人配置文档。
官方网址:
developer.work.weixin.qq.com/document/pa...

设计SDK结构
在查阅企微机器人文档后,了解到企业微信机器人支持发送多种类型的消息,包括文本、 Markdown 、图片、图文、文件、语音和模块卡片等,文档中对每一种类型的请求参数和字段含义都做了详尽的解释。

由于每种消息最终都是要转换成 JSON 格式作为 HTTP 请求的参数的,所以我们可以设计一个基础的消息类(Message)来存放公共参数,然后定义各种不同的具体消息类来集成它(比如文本消息 TextMessage、Markdown 消息 MarkdownMessage 等)。
为了简化开发者使用 SDK 来发送消息,定义统一的 MessageSender 类,在类中提供发送消息的方法(比如发送文本消息 sendText),可以接受 Message 并发送到企业微信服务器。
最终,客户端只需调用统一的消息发送方法即可。SDK 的整体结构如下图所示:

开发SDK
做好设计之后,接下来就可以开始开发 SDK 了。
步骤如下:
- 获取 webhook
- 创建 SDK 项目
- 编写代码
- SDK 打包
- 调用 SDK
1. 获取webhook
首先,必须在企业微信群聊中创建一个企业微信机器人,并获取机器人的 webhook。
webhook 是一个 url 地址,用于接受我们开发者自己服务器的请求,从而控制企业微信机器人。后续所有的开发过程,都需要通过 webhook 才可以实现。
复制并保存好这个 Webhook 地址,注意不要泄露该地址!
2、创建SDK项目
SDK 通常是一个很干净的项目,此处我们使用 Maven 来构建一个空的项目,并在 pom.xml 文件中配置项目信息。
需要特别注意的是,既然我们正在创建一个 SDK,这意味着它将被更多的开发者使用。因此,在配置 groupId 和 artifactId 时,我们应当遵循以下规范:
- groupId:它是项目组织或项目开发者的唯一标识符,其实际对应的是 main 目录下的 Java 目录结构。
- artifactId:它是项目的唯一标识符,对应的是项目名称,即项目的根目录名称。通常,它应当为纯小写,并且多个词之间使用中划线(-)隔开。
- version:它指定了项目的当前版本。其中,SNAPSHOT 表示该项目仍在开发中,是一个不稳定的版本。
xml
<groupId>com.xhl</groupId>
<artifactId>wxsdk</artifactId>
<version>1.0-SNAPSHOT</version>
所以此处我们把项目只作为 Spring Boot 的 starter,需要在 pom.xml 文件中引入依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
为了让我们的项目更加易用,我们还要能做到让开发者通过配置文件来传入配置(比如 webhook),而不是通过硬编码重复配置各种信息。
所以此处我们把项目只作为 Spring Boot 的 starter,需要在 pom.xml 文件中引入依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>3.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-configuration-processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>3.1.2</version>
</dependency>
最后,我们还需要添加一个配置,配置项<skip>true<skip>
表示跳过执行该插件的默认行为:
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.4.RELEASE</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
这样,一个 SDK 项目的初始依赖就配置好了。
3、编写配置类
现在我们就可以按照之前设计的结构开发了。
首先,我们要写一个配置类,用来接受开发者在配置文件中写入的 webhook。
同时,我们可以在配置类中,将需要被调用的 MessageSender 对象 Bean 自动注入到 IOC 容器中,不用让开发者自己 new 对象了。
示例代码:
java
@Configuration
@ConfigurationProperties(prefix = "wechatwork-bot")
@ComponentScan
@Data
public class WebhookConfig {
private String webhook;
@Bean
public RtxRobotMessageSender rtxRobotMessageSender(){
return new RtxRobotMessageSender(webhook);
}
}
接下来,为了让 Spring Boot 项目在启动时能自动识别并应用配置类,需要把配置类写入到 resources/META-INF/spring.factories 文件中,示例代码如下:
properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xhl.rtxrobot.config.WebhookConfig
4、编写消息类
接下来,我们要按照官方文档的请求参数把几种类型的消息对象编写好。
由于每个消息类都有一个固定的字段 msgtype,所以我们定义一个基类 Message,方便后续将不同类型的消息传入统一的方法:
java
public class Message {
/**
* 消息类型
*/
String msgtype;
}
接下来编写具体的消息类,比如纯文本类型消息 TextMessage,示例代码如下:
java
@Data
public class TextMessage extends Message implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 消息内容
*/
private String content;
/**
* 被提及者userId列表
*/
private List<String> mentionedList;
/**
* 被提及者电话号码列表
*/
private List<String> mentionedMobileList;
/**
* 提及全体
*/
private boolean mentionAll = false;
public TextMessage(String content, List<String> mentionedList, List<String> mentionedMobileList, boolean mentionAll) {
this.content = content;
this.mentionedList = mentionedList;
this.mentionedMobileList = mentionedMobileList;
this.mentionAll = mentionAll;
if (mentionAll) {
if (CollUtil.isNotEmpty(mentionedList) || CollUtil.isNotEmpty(mentionedMobileList)) {
if (CollUtil.isNotEmpty(mentionedList)) {
this.mentionedList.add("@all");
} else {
this.mentionedList = CollUtil.newArrayList("@all");
}
} else {
this.mentionedList = CollUtil.newArrayList("@all");
}
}
}
public JSONObject toJson() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("msgtype", "text");
JSONObject textObject = new JSONObject();
textObject.put("content", content);
textObject.put("mentioned_list", mentionedList);
textObject.put("mentioned_mobile_list", mentionedMobileList);
jsonObject.put("text", textObject);
jsonObject.put("mentionAll",mentionAll);
return jsonObject;
}
}
上面的代码中,有个代码优化小细节,官方文档是使用 "@all" 字符串来表示 @全体成员的,但 "@all" 是一个魔法值,为了简化调用,我们将其封装为 mentionAll 布尔类型字段,并且在构造函数中自动转换为实际请求需要的字段。
- 构造方法:
TextMessage(String content, List<String> mentionedList, List<String> mentionedMobileList, Boolean mentionAll)
:带有多个参数的构造方法,用于设置所有成员变量的值。如果 mentionAll 为 true,还会将 @all 添加到 mentionedList 中。TextMessage(String content)
:带有单个参数的构造方法,用于设置 content 成员变量的值,并将其他成员变量设置为默认值。
这里记得打上@Data注解,我就是忘记打上注解结果JSON序列化失败了。
toJson()方法是按照官方文档的格式,在初始化对象的时候别忘记使用这个方法转成json格式与官网要求的对应。
5、编写消息发送类
接下来,我们将编写一个消息发送类。在这个类中,定义了用于发送各种类型消息的方法,并且所有的方法都会依赖调用底层的 send 方法。send 方法的作用是通过向企微机器人的 webhook 地址发送请求,从而驱动企微机器人发送消息。
以下是示例代码,有很多编码细节:
java
@Data
public class RtxRobotMessageSender {
private final String webhook;
public WebhookConfig webhookConfig;
public RtxRobotMessageSender(String webhook) {
this.webhook = webhook;
}
/**
* 支持自定义消息发送
*/
public void sendMessage(Message message) throws Exception {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
send(textMessage.toJson());
}
// } else if (message instanceof MarkdownMessage) {
// MarkdownMessage markdownMessage = (MarkdownMessage) message;
// send(markdownMessage);
// }
else {
throw new RuntimeException("Unsupported message type");
}
}
/**
* 发送文本(简化调用)
*/
public void sendText(String content) throws Exception {
sendText(content, null, null, true);
}
/**
* 严格调用
* @param content
* @param mentionedList
* @param mentionedMobileList
* @param mentionAll
* @throws Exception
*/
public void sendText(String content, List<String> mentionedList, List<String> mentionedMobileList,boolean mentionAll) throws Exception {
TextMessage textMessage = new TextMessage(content, mentionedList, mentionedMobileList, mentionAll);
JSONObject jsonMessage = textMessage.toJson();
send(jsonMessage);
}
/**
* 发送消息的公共依赖底层代码
*/
private void send(JSONObject message) throws Exception {
String webhook = this.webhook;
Object messageJsonObject = JSONUtil.toJsonPrettyStr(message);
log.info("发送消息:{}", messageJsonObject);
// 未传入配置,降级为从配置文件中寻找
if (StrUtil.isBlank(this.webhook)) {
try {
webhook = webhookConfig.getWebhook();
} catch (Exception e) {
log.error("没有找到配置项中的webhook,请检查:1.是否在application.yml中填写webhook 2.是否在spring环境下运行");
throw new RuntimeException(e);
}
}
// 创建 OkHttpClient 实例
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"),(String) messageJsonObject);
// 构建请求对象
Request request = new Request.Builder().url(webhook).post(body).build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
log.info("消息发送成功");
} else {
log.error("消息发送失败,响应码:{}", response.code());
throw new Exception("消息发送失败,响应码:" + response.code());
}
} catch (IOException e) {
log.error("发送消息时发生错误:" + e);
throw new Exception("发送消息时发生错误", e);
}
}
}
6、SDK打包
接下来就可以对 SDK 进行打包,然后本地使用或者上传到远程仓库了。
SDK 的打包非常简单,通过 Maven 的 install 命令即可,SDK 的 jar 包就会被导入到你的本地仓库中。
在打包前建议先执行 clean 来清理垃圾文件。

7、调用SDK
最后我们来调用自己写的 SDK,首先将你的 SDK 作为依赖引入到项目中,比如我们的接水提醒应用。
引入代码如下:
xml
<dependency>
<groupId>com.xhl</groupId>
<artifactId>wxrobot</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
然后将之前复制的 webhook 写入到 Spring Boot 的配置文件中
yaml
wechatwork-bot:
webhook: 你的webhook地址
随后你就可以用依赖注入的方式得到一个消息发送者对象了:
java
@Resource
public RtxRobotMessageSender rtxRobotMessageSender;
使用定时任务提醒喝水
别忘了打上@EnableScheduling注解
java
@Component
public class WaterReminderTask {
@Resource
public RtxRobotMessageSender rtxRobotMessageSender;
@Scheduled(cron = "0 0 9,15,22 * * ?") // 每天早上9点、下午3点和晚上9点触发任务
public void remindToDrinkWater() throws Exception {
String message = "小火龙提醒你:记得喝水啦!"; // 提示喝水的消息内容
// 调用消息发送方法发送消息
sendReminderMessage(message);
}
private void sendReminderMessage(String message) throws Exception {
// 调用消息发送的方法,具体实现根据您的需求进行修改
rtxRobotMessageSender.sendText(message);
}
}