diff --git a/README.md b/README.md index 678dc10f4..30d2fbeb3 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -weixin-tools +weixin-java-tools =========== 微信java开发工具,本项目提供了两个主要特性:微信消息路由器、微信Java API ## 微信消息路由器 -你可以使用``WxMessageRouter``来对微信推送过来的消息、事件进行路由,交给特定的``WxMessageHandler``处理。 +你可以使用``WxMessageRouter``来对微信推送过来的消息、事件进行路由,交给的``WxMessageHandler``处理。 使用方法: @@ -13,32 +13,269 @@ weixin-tools WxMessageRouter router = new WxMessageRouter(); router .rule() - .msgType("MSG_TYPE").event("EVENT").eventKey("EVENT_KEY").content("CONTENT") - .interceptor(interceptor, ...).handler(handler, ...) + .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); ``` 1. 开发人员需实现自己的``WxMessageHandler``和``WxMessageInterceptor`` 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理 -2. 默认情况下消息只会被处理一次,除非使用 ``WxMessageRouter#Rule#next()`` -3. 规则的结束必须用``WxMessageRouter#Rule#end()``或者``WxMessageRouter#Rule#next()``,否则不会生效 +2. 默认情况下消息只会被处理一次,除非使用 ``Rule.next()`` +3. 规则的结束必须用``Rule.end()``或者``Rule.next()``,否则不会生效 +4. 具体使用可以看源代码中的``WxMessageRouterTest``单元测试,或者查看Javadoc ## 微信Java API -使用``WxService``可以调用微信API。目前实现了以下功能,其余功能以后陆续补充: +使用``WxService``可以调用微信API。目前实现的功能有: -1. 发送客服消息 -1. 创建自定义菜单 -1. 删除自定义菜单 -1. 查询自定义菜单 -1. 刷新access_token +### 构造WxService + +``WxService``依赖于``WxConfigStorage``,``WxConfigStorage``是微信客户端配置所存储的地方,本工具提供了默认的``WxInMemoryConfigStorage``,这是一个存储在内存中的实现,生产环境中需要开发人员提供自己的实现。 + +```java +WxConfigStorage config = new WxInMemoryConfigStorage(); +config.setAppId(...); +config.setSecret(...); +config.setToken(...); +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 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(); +// 设置消息的内容等信息 +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 diff --git a/src/main/java/chanjarster/weixin/api/WxService.java b/src/main/java/chanjarster/weixin/api/WxService.java index ac67c8796..c6d22ed87 100644 --- a/src/main/java/chanjarster/weixin/api/WxService.java +++ b/src/main/java/chanjarster/weixin/api/WxService.java @@ -210,7 +210,7 @@ public interface WxService { * @param openid 微信用户的openid * @throws WxErrorException */ - public long groupQueryUserGroup(String openid) throws WxErrorException; + public long userGetGroup(String openid) throws WxErrorException; /** *
@@ -296,7 +296,7 @@ public interface WxService {
 
   /**
    * 
-   * 换取二维码图片
+   * 换取二维码图片文件,jpg格式
    * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=生成带参数的二维码
    * 
* @param ticket 二维码ticket diff --git a/src/main/java/chanjarster/weixin/api/WxServiceImpl.java b/src/main/java/chanjarster/weixin/api/WxServiceImpl.java index 89f103858..ba4c4fe2e 100644 --- a/src/main/java/chanjarster/weixin/api/WxServiceImpl.java +++ b/src/main/java/chanjarster/weixin/api/WxServiceImpl.java @@ -223,7 +223,7 @@ public class WxServiceImpl implements WxService { return WxGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("groups"), new TypeToken>(){}.getType()); } - public long groupQueryUserGroup(String openid) throws WxErrorException { + public long userGetGroup(String openid) throws WxErrorException { String url = "https://api.weixin.qq.com/cgi-bin/groups/getid"; JsonObject o = new JsonObject(); o.addProperty("openid", openid); diff --git a/src/test/java/chanjarster/weixin/api/WxGroupAPITest.java b/src/test/java/chanjarster/weixin/api/WxGroupAPITest.java index d3ab5699a..aa4a3da2a 100644 --- a/src/test/java/chanjarster/weixin/api/WxGroupAPITest.java +++ b/src/test/java/chanjarster/weixin/api/WxGroupAPITest.java @@ -6,7 +6,6 @@ import org.testng.Assert; import org.testng.annotations.Guice; import org.testng.annotations.Test; -import chanjarster.weixin.api.ApiTestModule.WxXmlConfigStorage; import chanjarster.weixin.bean.WxGroup; import chanjarster.weixin.exception.WxErrorException; @@ -42,22 +41,10 @@ public class WxGroupAPITest { } } - @Test(dependsOnMethods="testGroupCreate") - public void testGroupQueryUserGroup() throws WxErrorException { - WxXmlConfigStorage configStorage = (WxXmlConfigStorage) wxService.wxConfigStorage; - long groupid = wxService.groupQueryUserGroup(configStorage.getOpenId()); - Assert.assertTrue(groupid != -1l); - } - @Test(dependsOnMethods={"testGroupGet", "testGroupCreate"}) public void getGroupUpdate() throws WxErrorException { group.setName("分组改名"); wxService.groupUpdate(group); } - @Test(dependsOnMethods={"testGroupGet", "testGroupCreate"}) - public void getGroupMoveUser() throws WxErrorException { - WxXmlConfigStorage configStorage = (WxXmlConfigStorage) wxService.wxConfigStorage; - wxService.userUpdateGroup(configStorage.getOpenId(), group.getId()); - } } diff --git a/src/test/java/chanjarster/weixin/api/WxMediaAPITest.java b/src/test/java/chanjarster/weixin/api/WxMediaAPITest.java index b10ce5860..cfb200b27 100644 --- a/src/test/java/chanjarster/weixin/api/WxMediaAPITest.java +++ b/src/test/java/chanjarster/weixin/api/WxMediaAPITest.java @@ -1,5 +1,6 @@ package chanjarster.weixin.api; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -57,7 +58,8 @@ public class WxMediaAPITest { @Test(dependsOnMethods = { "testUploadMedia" }, dataProvider="downloadMedia") public void testDownloadMedia(String media_id) throws WxErrorException { - wxService.mediaDownload(media_id); + File file = wxService.mediaDownload(media_id); + Assert.assertNotNull(file); } @DataProvider diff --git a/src/test/java/chanjarster/weixin/api/WxShortUrlAPITest.java b/src/test/java/chanjarster/weixin/api/WxShortUrlAPITest.java index d957ffcc4..d4cd31d7e 100644 --- a/src/test/java/chanjarster/weixin/api/WxShortUrlAPITest.java +++ b/src/test/java/chanjarster/weixin/api/WxShortUrlAPITest.java @@ -9,7 +9,7 @@ import chanjarster.weixin.exception.WxErrorException; import com.google.inject.Inject; /** - * 测试用户相关的接口 + * 测试短连接 * * @author chanjarster */ diff --git a/src/test/java/chanjarster/weixin/api/WxUserAPITest.java b/src/test/java/chanjarster/weixin/api/WxUserAPITest.java index 1381f158e..64a0e0308 100644 --- a/src/test/java/chanjarster/weixin/api/WxUserAPITest.java +++ b/src/test/java/chanjarster/weixin/api/WxUserAPITest.java @@ -8,7 +8,6 @@ import chanjarster.weixin.api.ApiTestModule.WxXmlConfigStorage; import chanjarster.weixin.bean.result.WxUser; import chanjarster.weixin.bean.result.WxUserList; import chanjarster.weixin.exception.WxErrorException; -import chanjarster.weixin.util.json.WxGsonBuilder; import com.google.inject.Inject; @@ -17,7 +16,7 @@ import com.google.inject.Inject; * @author chanjarster * */ -@Test(groups = "userAPI", dependsOnGroups = { "baseAPI" }) +@Test(groups = "userAPI", dependsOnGroups = { "baseAPI", "groupAPI" }) @Guice(modules = ApiTestModule.class) public class WxUserAPITest { @@ -33,7 +32,6 @@ public class WxUserAPITest { WxXmlConfigStorage configProvider = (WxXmlConfigStorage) wxService.wxConfigStorage; WxUser user = wxService.userInfo(configProvider.getOpenId(), null); Assert.assertNotNull(user); - System.out.println(WxGsonBuilder.INSTANCE.create().toJson(user)); } public void testUserList() throws WxErrorException { @@ -44,4 +42,15 @@ public class WxUserAPITest { Assert.assertFalse(wxUserList.getOpenids().size() == -1); } + public void testGroupQueryUserGroup() throws WxErrorException { + WxXmlConfigStorage configStorage = (WxXmlConfigStorage) wxService.wxConfigStorage; + long groupid = wxService.userGetGroup(configStorage.getOpenId()); + Assert.assertTrue(groupid != -1l); + } + + public void getGroupMoveUser() throws WxErrorException { + WxXmlConfigStorage configStorage = (WxXmlConfigStorage) wxService.wxConfigStorage; + wxService.userUpdateGroup(configStorage.getOpenId(), wxService.groupGet().get(3).getId()); + } + } diff --git a/src/test/resources/testng.xml b/src/test/resources/testng.xml index d5b967a2d..2f3bddf2c 100644 --- a/src/test/resources/testng.xml +++ b/src/test/resources/testng.xml @@ -6,13 +6,13 @@ - + - +