|
|
@@ -12,304 +12,43 @@ weixin-java-tools |
|
|
|
5. 微信服务端繁忙自动重试的功能 |
|
|
|
6. 提供微信错误信息的异常处理机制 |
|
|
|
|
|
|
|
## 微信消息路由器 |
|
|
|
# Quickstart |
|
|
|
|
|
|
|
你可以使用``WxMessageRouter``来对微信推送过来的消息、事件进行路由,交给的``WxMessageHandler``处理。 |
|
|
|
## 打包 |
|
|
|
|
|
|
|
使用方法: |
|
|
|
|
|
|
|
```java |
|
|
|
WxMessageRouter router = new WxMessageRouter(); |
|
|
|
router |
|
|
|
.rule() |
|
|
|
.msgType("MSG_TYPE") |
|
|
|
.event("EVENT") |
|
|
|
.eventKey("EVENT_KEY") |
|
|
|
.content("CONTENT") |
|
|
|
.interceptor(interceptor).handler(handler) |
|
|
|
.end() |
|
|
|
.rule() |
|
|
|
.msgType("MSG_TYPE") |
|
|
|
.handler(handler) |
|
|
|
.end() |
|
|
|
; |
|
|
|
|
|
|
|
// 将WxXmlMessage交给消息路由器 |
|
|
|
WxXmlMessage message = WxXmlMessage.fromXml(xml); |
|
|
|
router.route(message); |
|
|
|
如果你可以跳过单元测试直接打包: |
|
|
|
```bash |
|
|
|
mvn clean install -Dmaven.test.skip=true |
|
|
|
``` |
|
|
|
|
|
|
|
1. 开发人员需实现自己的``WxMessageHandler``和``WxMessageInterceptor`` |
|
|
|
1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理 |
|
|
|
2. 默认情况下消息只会被处理一次,除非使用 ``Rule.next()`` |
|
|
|
3. 规则的结束必须用``Rule.end()``或者``Rule.next()``,否则不会生效 |
|
|
|
4. 具体使用可以看源代码中的``WxMessageRouterTest``单元测试,或者查看Javadoc |
|
|
|
|
|
|
|
### 同步回复 |
|
|
|
|
|
|
|
``WxMessageRouter``默认使用异步的方式处理消息,如果要使用同步的方式处理消息,那么可以这样: |
|
|
|
|
|
|
|
```java |
|
|
|
router |
|
|
|
.rule() |
|
|
|
.async(false) |
|
|
|
.handler(handler) |
|
|
|
.end() |
|
|
|
; |
|
|
|
如果你需要执行本项目的单元测试代码,那么你需要将 ``src/test/resources/test-config.sample.xml`` 改成 ``test-config.xml`` ,并设置appId, secret, accessToken(可选), openId,然后执行: |
|
|
|
|
|
|
|
WxXmlMessage message = WxXmlMessage.fromXml(xml); |
|
|
|
// 获得同步的返回结果 |
|
|
|
WxXmlMessage res = router.route(message); |
|
|
|
String xml = res.toXml(); |
|
|
|
// ... 将xml写入HttpServletResponse |
|
|
|
```bash |
|
|
|
mvn clean install |
|
|
|
``` |
|
|
|
|
|
|
|
在你的maven项目中添加: |
|
|
|
```xml |
|
|
|
<dependency> |
|
|
|
<groupId>chanjarster.weixin</groupId> |
|
|
|
<artifactId>weixin-java-tools</artifactId> |
|
|
|
<version>1.0.0-SNAPSHOT</version> |
|
|
|
</dependency> |
|
|
|
``` |
|
|
|
|
|
|
|
## 微信Java API |
|
|
|
|
|
|
|
使用``WxService``可以调用微信API。目前已实现除“微信小店”以外的所有功能。 |
|
|
|
|
|
|
|
### 构造WxService |
|
|
|
|
|
|
|
``WxService``依赖于``WxConfigStorage``,``WxConfigStorage``是微信客户端配置所存储的地方,本工具提供了默认基于内存的实现:``WxInMemoryConfigStorage``。您可能在实际使用时需要提供自己的实现,比如在集群环境下存储到数据库中。 |
|
|
|
|
|
|
|
|
|
|
|
## Hello World |
|
|
|
```java |
|
|
|
WxConfigStorage config = new WxInMemoryConfigStorage(); |
|
|
|
config.setAppId(...); |
|
|
|
config.setSecret(...); |
|
|
|
config.setToken(...); |
|
|
|
config.setAppId(...); // 设置微信公众号的appid |
|
|
|
config.setSecret(...); // 设置微信公众号的app secret |
|
|
|
config.setToken(...); // 设置微信公众号的token |
|
|
|
|
|
|
|
WxServiceImpl wxService = new WxServiceImpl(); |
|
|
|
wxService.setWxConfigStorage(config); |
|
|
|
``` |
|
|
|
|
|
|
|
### 验证消息 |
|
|
|
|
|
|
|
```java |
|
|
|
if(!wxService.checkSignature(timestamp, nonce, signature)) { |
|
|
|
// 验证失败 |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
### 刷新access_token |
|
|
|
|
|
|
|
```java |
|
|
|
wxService.accessTokenRefresh(); |
|
|
|
``` |
|
|
|
|
|
|
|
刷新后的accessToken存在WxConfigStorage中。 |
|
|
|
|
|
|
|
### 多媒体文件 |
|
|
|
|
|
|
|
#### 上传多媒体文件 |
|
|
|
```java |
|
|
|
InputStream inputStream = ...; |
|
|
|
File file = ...; |
|
|
|
WxMediaUploadResult res = wxService.mediaUpload(mediaType, fileType, inputStream); |
|
|
|
// 或者 |
|
|
|
res = wxService.mediaUpload(mediaType, file); |
|
|
|
res.getType(); |
|
|
|
res.getCreated_at(); |
|
|
|
res.getMedia_id(); |
|
|
|
res.getThumb_media_id(); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 下载多媒体文件 |
|
|
|
```java |
|
|
|
// 获得一个在系统临时目录的文件 |
|
|
|
File file = wxService.mediaDownload(media_id); |
|
|
|
``` |
|
|
|
|
|
|
|
### 分组管理 |
|
|
|
|
|
|
|
#### 创建分组 |
|
|
|
```java |
|
|
|
WxGroup res = wxService.groupCreate("测试分组1"); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 获得分组列表 |
|
|
|
```java |
|
|
|
List<WxGroup> groupList = wxService.groupGet(); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 更新分组名 |
|
|
|
```java |
|
|
|
WxGroup g = new WxGroup(); |
|
|
|
g.setId(...); |
|
|
|
g.setName(...); |
|
|
|
wxService.groupUpdate(group); |
|
|
|
``` |
|
|
|
|
|
|
|
### 用户管理 |
|
|
|
|
|
|
|
#### 更新用户备注名 |
|
|
|
```java |
|
|
|
wxService.userUpdateRemark(openid, "测试备注名"); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 获得用户信息 |
|
|
|
```java |
|
|
|
String lang = "zh_CN"; //语言 |
|
|
|
WxUser user = wxService.userInfo(openid, lang); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 获得用户列表 |
|
|
|
```java |
|
|
|
WxUserList wxUserList = wxService.userList(next_openid); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 查询用户所在分组 |
|
|
|
```java |
|
|
|
long groupid = wxService.userGetGroup(openid); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 将用户移到分组 |
|
|
|
```java |
|
|
|
wxService.userUpdateGroup(openid, to_groupid); |
|
|
|
``` |
|
|
|
|
|
|
|
### 发送客服消息 |
|
|
|
|
|
|
|
```java |
|
|
|
WxCustomMessage message = new WxCustomMessage(); |
|
|
|
// 设置消息的内容等信息 |
|
|
|
// 用户的openid在下面地址获得 |
|
|
|
// https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type=用户管理&form=获取关注者列表接口%20/user/get |
|
|
|
String openid = ...; |
|
|
|
WxCustomMessage message = WxCustomMessage.TEXT().toUser(openid).content("Hello World").build(); |
|
|
|
wxService.customMessageSend(message); |
|
|
|
``` |
|
|
|
|
|
|
|
### 群发消息 |
|
|
|
|
|
|
|
下面用用户列表群发(``WxMassOpenIdsMessage``)做例子,如果要使用分组群发,则使用``WxMassGroupMessage``即可。 |
|
|
|
|
|
|
|
#### 文本消息 |
|
|
|
```java |
|
|
|
WxMassOpenIdsMessage massMessage = new WxMassOpenIdsMessage(); |
|
|
|
massMessage.setMsgtype(WxConsts.MASS_MSG_TEXT); |
|
|
|
massMessage.setContent("消息内容"); |
|
|
|
massMessage.getTouser().add(openid); |
|
|
|
|
|
|
|
WxMassSendResult massResult = wxService.massOpenIdsMessageSend(massMessage); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 视频消息 |
|
|
|
```java |
|
|
|
WxMediaUploadResult uploadMediaRes = wxService.mediaUpload(WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, inputStream); |
|
|
|
|
|
|
|
// 把视频变成可被群发的媒体 |
|
|
|
WxMassVideo video = new WxMassVideo(); |
|
|
|
video.setTitle("测试标题"); |
|
|
|
video.setDescription("测试描述"); |
|
|
|
video.setMedia_id(uploadMediaRes.getMedia_id()); |
|
|
|
WxMassUploadResult uploadResult = wxService.massVideoUpload(video); |
|
|
|
|
|
|
|
WxMassOpenIdsMessage massMessage = new WxMassOpenIdsMessage(); |
|
|
|
massMessage.setMsgtype(WxConsts.MASS_MSG_VIDEO); |
|
|
|
massMessage.setMedia_id(uploadResult.getMedia_id()); |
|
|
|
massMessage.getTouser().add(openid); |
|
|
|
|
|
|
|
WxMassSendResult massResult = wxService.massOpenIdsMessageSend(massMessage); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 图片消息 |
|
|
|
```java |
|
|
|
WxMediaUploadResult uploadMediaRes = wxService.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, inputStream); |
|
|
|
|
|
|
|
WxMassOpenIdsMessage massMessage = new WxMassOpenIdsMessage(); |
|
|
|
massMessage.setMsgtype(WxConsts.MASS_MSG_IMAGE); |
|
|
|
massMessage.setMedia_id(uploadMediaRes.getMedia_id()); |
|
|
|
massMessage.getTouser().add(openid); |
|
|
|
|
|
|
|
WxMassSendResult massResult = wxService.massOpenIdsMessageSend(massMessage); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 语音消息 |
|
|
|
```java |
|
|
|
WxMediaUploadResult uploadMediaRes = wxService.mediaUpload(WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, inputStream); |
|
|
|
|
|
|
|
WxMassOpenIdsMessage massMessage = new WxMassOpenIdsMessage(); |
|
|
|
massMessage.setMsgtype(WxConsts.MASS_MSG_VOICE); |
|
|
|
massMessage.setMedia_id(uploadMediaRes.getMedia_id()); |
|
|
|
massMessage.getTouser().add(openid); |
|
|
|
|
|
|
|
WxMassSendResult massResult = wxService.massOpenIdsMessageSend(massMessage); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 图文消息 |
|
|
|
```java |
|
|
|
// 先上传图文消息里需要的图片 |
|
|
|
WxMediaUploadResult uploadMediaRes = wxService.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, inputStream); |
|
|
|
|
|
|
|
WxMassNews news = new WxMassNews(); |
|
|
|
WxMassNewsArticle article1 = new WxMassNewsArticle(); |
|
|
|
article1.setTitle("标题1"); |
|
|
|
article1.setContent("内容1"); |
|
|
|
article1.setThumb_media_id(uploadMediaRes.getMedia_id()); |
|
|
|
news.addArticle(article1); |
|
|
|
|
|
|
|
WxMassNewsArticle article2 = new WxMassNewsArticle(); |
|
|
|
article2.setTitle("标题2"); |
|
|
|
article2.setContent("内容2"); |
|
|
|
article2.setThumb_media_id(uploadMediaRes.getMedia_id()); |
|
|
|
article2.setShow_cover_pic(true); |
|
|
|
article2.setAuthor("作者2"); |
|
|
|
article2.setContent_source_url("www.baidu.com"); |
|
|
|
article2.setDigest("摘要2"); |
|
|
|
news.addArticle(article2); |
|
|
|
|
|
|
|
WxMassUploadResult massUploadResult = wxService.massNewsUpload(news); |
|
|
|
|
|
|
|
WxMassOpenIdsMessage massMessage = new WxMassOpenIdsMessage(); |
|
|
|
massMessage.setMsgtype(WxConsts.MASS_MSG_NEWS); |
|
|
|
massMessage.setMedia_id(uploadResult.getMedia_id()); |
|
|
|
massMessage.getTouser().add(openid); |
|
|
|
|
|
|
|
WxMassSendResult massResult = wxService.massOpenIdsMessageSend(massMessage); |
|
|
|
``` |
|
|
|
|
|
|
|
### 自定义菜单 |
|
|
|
|
|
|
|
#### 创建自定义菜单 |
|
|
|
```java |
|
|
|
WxMenu wxMenu = new WxMenu(); |
|
|
|
// 设置菜单 |
|
|
|
wxService.menuCreate(wxMenu); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 删除自定义菜单 |
|
|
|
```java |
|
|
|
wxService.menuDelete(); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 获得自定义菜单 |
|
|
|
```java |
|
|
|
WxMenu wxMenu = wxService.menuGet() |
|
|
|
``` |
|
|
|
|
|
|
|
### 二维码 |
|
|
|
|
|
|
|
#### 获得二维码ticket |
|
|
|
```java |
|
|
|
// 临时ticket |
|
|
|
WxQrCodeTicket ticket = wxService.qrCodeCreateTmpTicket(scene, expire_seconds); |
|
|
|
// 永久ticket |
|
|
|
WxQrCodeTicket ticket = wxService.qrCodeCreateLastTicket(scene); |
|
|
|
``` |
|
|
|
|
|
|
|
#### 换取二维码图片 |
|
|
|
```java |
|
|
|
WxQrCodeTicket ticket = ...; |
|
|
|
// 获得一个在系统临时目录下的文件,是jpg格式的 |
|
|
|
File file = wxService.qrCodePicture(ticket); |
|
|
|
``` |
|
|
|
|
|
|
|
### 短链接 |
|
|
|
|
|
|
|
```java |
|
|
|
String shortUrl = wxService.shortUrl("www.baidu.com"); |
|
|
|
``` |
|
|
|
|
|
|
|
## 如何执行单元测试 |
|
|
|
将 ``src/test/resources/test-config.sample.xml`` 改成 ``test-config.xml`` 设置appId, secret, accessToken(可选), openId |
|
|
|
|
|
|
|
```bash |
|
|
|
mvn clean test |
|
|
|
``` |