Skip to content

Commit

Permalink
微信消息路由器,及单元测试代码
Browse files Browse the repository at this point in the history
  • Loading branch information
chanjarster committed Aug 22, 2014
1 parent 189f285 commit ed3cdfc
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 46 deletions.
49 changes: 45 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,48 @@
weixin-java
weixin-tools
===========

微信java开发工具集
微信java开发工具,本项目提供了两个主要特性:微信消息路由器、微信Java API

## 执行测试
将 ''src/test/resources/test-config.sample.xml'' 改成 ''test-config.xml'' 并设置appId, secret, 一个过期的accessToken
## 微信消息路由器

你可以使用``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()
// 另外一个匹配规则
.end()
;

// 将WxXmlMessage交给消息路由器
router.route(message);
```

说明:
1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
2. 默认情况下消息只会被处理一次,除非使用 {@link Rule#next()}
3. 规则的结束必须用{@link Rule#end()}或者{@link Rule#next()},否则不会生效

## 微信Java API

使用``WxService``可以调用微信API。目前实现了以下功能,其余功能以后陆续补充:

1. 发送客服消息
1. 创建自定义菜单
1. 删除自定义菜单
1. 查询自定义菜单
1. 刷新access_token

## 如何执行单元测试
``src/test/resources/test-config.sample.xml`` 改成 ``test-config.xml`` 设置appId, secret, accessToken(可选), openId

```bash
mvn clean test
```
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>chanjarster.weixin</groupId>
<artifactId>weixin-java</artifactId>
<artifactId>weixin-toolset</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>WeiXin Java Toolset</name>
<url>https://github.com/chanjarster/weixin-java</url>
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/chanjarster/weixin/api/WxConsts.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ public class WxConsts {
public static final String MSG_NEWS = "news";
public static final String MSG_LOCATION = "location";
public static final String MSG_LINK = "link";
public static final String MSG_EVENT = "event";

public static final String EVT_SUBSCRIBE = "subscribe";
public static final String EVT_UNSUBSCRIBE = "unsubscribe";
public static final String EVT_SCAN = "SCAN";
public static final String EVT_LOCATION = "LOCATION";
public static final String EVT_CLICK = "LOCATION";
public static final String EVT_VIEW = "VIEW";



}
2 changes: 1 addition & 1 deletion src/main/java/chanjarster/weixin/api/WxMessageHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import chanjarster.weixin.bean.WxXmlMessage;

/**
* 处理微信推送消息的处理器
* 处理微信推送消息的处理器接口
* @author chanjarster
*
*/
Expand Down
55 changes: 36 additions & 19 deletions src/main/java/chanjarster/weixin/api/WxMessageRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
import java.util.List;
import java.util.Map;

import chanjarster.weixin.api.WxMessageRouterTest.WxEchoMessageHandler;
import chanjarster.weixin.bean.WxXmlMessage;

/**
* <pre>
* 微信消息路由器,通过代码化的配置,把来自微信的消息交给handler处理
*
* 说明:
* 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
* 2. 默认情况下消息只会被处理一次,除非使用 {@link Rule#next()}
* 3. 规则的结束必须用{@link Rule#end()}或者{@link Rule#next()},否则不会生效
*
* 使用方法:
* WxMessageRouter router = new WxMessageRouter();
* router
Expand All @@ -27,9 +31,6 @@
* // 将WxXmlMessage交给消息路由器
* router.route(message);
*
* 说明:
* 1. 配置路由规则时要按照从细到粗的原则
* 2. 默认情况下消息只会被处理一次,除非使用 {@link Rule#reEnter()}
* </pre>
* @author qianjia
*
Expand All @@ -52,9 +53,11 @@ public Rule rule() {
*/
public void route(WxXmlMessage wxMessage) {
for (Rule rule : rules) {
boolean doNext = rule.service(wxMessage);
if (!doNext) {
break;
if (rule.test(wxMessage)) {
boolean reEnter = rule.service(wxMessage);
if (!reEnter) {
break;
}
}
}
}
Expand Down Expand Up @@ -122,16 +125,16 @@ public Rule content(String content) {
}

/**
* 将消息交给后面的Rule处理
* 设置微信消息拦截器
* @param interceptor
* @return
*/
public Rule reEnter() {
this.reEnter = true;
return this;
public Rule interceptor(WxMessageInterceptor interceptor) {
return interceptor(interceptor, (WxMessageInterceptor[]) null);
}

/**
* 添加interceptor
* 设置微信消息拦截器
* @param interceptor
* @param otherInterceptors
* @return
Expand All @@ -147,10 +150,20 @@ public Rule interceptor(WxMessageInterceptor interceptor, WxMessageInterceptor..
}

/**
* 添加handler
* 设置微信消息处理器
* @param handler
* @return
*/
public Rule handler(WxMessageHandler handler) {
return handler(handler, (WxMessageHandler[]) null);
}

/**
* 设置微信消息处理器
* @param handler
* @param otherHandlers
* @return
*/
public Rule handler(WxMessageHandler handler, WxMessageHandler... otherHandlers) {
this.handlers.add(handler);
if (otherHandlers != null && otherHandlers.length > 0) {
Expand All @@ -162,14 +175,23 @@ public Rule handler(WxMessageHandler handler, WxMessageHandler... otherHandlers)
}

/**
* 规则结束
* 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则
* @return
*/
public WxMessageRouter end() {
this.routerBuilder.rules.add(this);
return this.routerBuilder;
}

/**
* 规则结束,但是消息还会进入其他规则
* @return
*/
public WxMessageRouter next() {
this.reEnter = true;
return end();
}

protected boolean test(WxXmlMessage wxMessage) {
return
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
Expand All @@ -188,11 +210,6 @@ protected boolean test(WxXmlMessage wxMessage) {
* @return true 代表继续执行别的router,false 代表停止执行别的router
*/
protected boolean service(WxXmlMessage wxMessage) {
// 如果不匹配本规则,那么接着执行后面的Rule
if (!test(wxMessage)) {
return true;
}

Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过
for (WxMessageInterceptor interceptor : this.interceptors) {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/chanjarster/weixin/api/WxServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import chanjarster.weixin.bean.WxError;
import chanjarster.weixin.bean.WxMenu;
import chanjarster.weixin.exception.WxErrorException;
import chanjarster.weixin.util.Utf8StringResponseHandler;
import chanjarster.weixin.util.Utf8ResponseHandler;

public class WxServiceImpl implements WxService {

Expand Down Expand Up @@ -133,15 +133,15 @@ protected String execute(String method, String uri, String data) throws WxErrorE
httpPost.setEntity(entity);
}
CloseableHttpResponse response = httpclient.execute(httpPost);
resultContent = Utf8StringResponseHandler.INSTANCE.handleResponse(response);
resultContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
} else if ("GET".equals(method)) {
if (data != null) {
uriWithAccessToken += uriWithAccessToken.endsWith("&") ? data : '&' + data;
}
HttpGet httpGet = new HttpGet(uriWithAccessToken);
CloseableHttpResponse response = httpclient.execute(httpGet);
response.setHeader("Content-Type", ContentType.APPLICATION_JSON.toString());
resultContent = Utf8StringResponseHandler.INSTANCE.handleResponse(response);
resultContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
}

WxError error = WxError.fromJson(resultContent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
* @author chanjarster
*
*/
public class Utf8StringResponseHandler implements ResponseHandler<String> {
public class Utf8ResponseHandler implements ResponseHandler<String> {

public static final ResponseHandler<String> INSTANCE = new Utf8StringResponseHandler();
public static final ResponseHandler<String> INSTANCE = new Utf8ResponseHandler();

public String handleResponse(final HttpResponse response) throws HttpResponseException, IOException {
final StatusLine statusLine = response.getStatusLine();
Expand Down
89 changes: 78 additions & 11 deletions src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,97 @@
import java.util.Map;

import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import chanjarster.weixin.bean.WxXmlMessage;

@Test
public class WxMessageRouterTest {

public void testSimple() {
StringBuilder sb = new StringBuilder();
WxMessageRouter router = new WxMessageRouter();

protected StringBuilder sb;
protected WxMessageRouter router;

@BeforeMethod
public void prepare() {
this.sb = new StringBuilder();
this.router = new WxMessageRouter();
router
.rule()
.msgType(WxConsts.MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1").content("CONTENT_1")
.handler(new WxEchoMessageHandler(sb, "COMBINE_4"))
.end()
.rule()
.msgType(WxConsts.MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1")
.handler(new WxEchoMessageHandler(sb, "COMBINE_3"))
.end()
.rule()
.msgType(WxConsts.MSG_TEXT).event(WxConsts.EVT_CLICK)
.handler(new WxEchoMessageHandler(sb, "COMBINE_2"))
.end()
.rule().msgType(WxConsts.MSG_TEXT).handler(new WxEchoMessageHandler(sb, WxConsts.MSG_TEXT)).end()
.rule().msgType(WxConsts.MSG_IMAGE).handler(new WxEchoMessageHandler(sb, WxConsts.MSG_IMAGE)).end()
.rule().event(WxConsts.EVT_CLICK).handler(new WxEchoMessageHandler(sb, WxConsts.EVT_CLICK)).end()
.rule().eventKey("KEY_1").handler(new WxEchoMessageHandler(sb, "KEY_1")).end()
.rule().content("CONTENT_1").handler(new WxEchoMessageHandler(sb, "CONTENT_1")).end()
.rule().handler(new WxEchoMessageHandler(sb, "ALL")).end();
;

WxXmlMessage message = new WxXmlMessage();
message.setMsgType(WxConsts.MSG_TEXT);
}

@Test(dataProvider="messages-1")
public void testSimple(WxXmlMessage message, String expected) {
router.route(message);
Assert.assertEquals(sb.toString(), WxConsts.MSG_TEXT + ",");

Assert.assertEquals(sb.toString(), expected);
}

@Test()
public void test() {

}
@DataProvider(name="messages-1")
public Object[][] messages2() {
WxXmlMessage message1 = new WxXmlMessage();
message1.setMsgType(WxConsts.MSG_TEXT);

WxXmlMessage message2 = new WxXmlMessage();
message2.setEvent(WxConsts.EVT_CLICK);

WxXmlMessage message3 = new WxXmlMessage();
message3.setEventKey("KEY_1");

WxXmlMessage message4 = new WxXmlMessage();
message4.setContent("CONTENT_1");

WxXmlMessage message5 = new WxXmlMessage();
message5.setContent("BLA");

WxXmlMessage c2 = new WxXmlMessage();
c2.setMsgType(WxConsts.MSG_TEXT);
c2.setEvent(WxConsts.EVT_CLICK);

WxXmlMessage c3 = new WxXmlMessage();
c3.setMsgType(WxConsts.MSG_TEXT);
c3.setEvent(WxConsts.EVT_CLICK);
c3.setEventKey("KEY_1");

WxXmlMessage c4 = new WxXmlMessage();
c4.setMsgType(WxConsts.MSG_TEXT);
c4.setEvent(WxConsts.EVT_CLICK);
c4.setEventKey("KEY_1");
c4.setContent("CONTENT_1");

return new Object[][] {
new Object[] { message1, WxConsts.MSG_TEXT + "," },
new Object[] { message2, WxConsts.EVT_CLICK + "," },
new Object[] { message3, "KEY_1," },
new Object[] { message4, "CONTENT_1," },
new Object[] { message5, "ALL," },
new Object[] { c2, "COMBINE_2," },
new Object[] { c3, "COMBINE_3," },
new Object[] { c4, "COMBINE_4," }
};

}

public static class WxEchoMessageHandler implements WxMessageHandler {

Expand Down
8 changes: 4 additions & 4 deletions src/test/java/chanjarster/weixin/api/WxServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ public void testRefreshAccessToken() throws WxErrorException {
Assert.assertTrue(StringUtils.isNotBlank(after));
}

@Test(dependsOnMethods = "testRefreshAccessToken", enabled = false)
@Test(dependsOnMethods = "testRefreshAccessToken")
public void sendCustomMessage() throws WxErrorException {
WxXmlConfigStorage configProvider = (WxXmlConfigStorage) wxService.wxConfigProvider;
WxCustomMessage message = new WxCustomMessage();
message.setMsgtype(WxConsts.MSG_TEXT);
message.setTouser(configProvider.getOpenId());
message.setContent("欢迎使用教务系统微信公众号\n下面\n<a href=\"http://www.baidu.com\">Hello World</a>");
message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>");

wxService.sendCustomMessage(message);
}

@Test(dataProvider = "menu", enabled = true, dependsOnMethods = "testRefreshAccessToken")
@Test(dataProvider = "menu", dependsOnMethods = "testRefreshAccessToken")
public void testCreateMenu(WxMenu wxMenu) throws WxErrorException {
wxService.createMenu(wxMenu);
}
Expand All @@ -64,7 +64,7 @@ public void testGetMenu() throws WxErrorException {
Assert.assertNotNull(wxService.getMenu());
}

@Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu" }, enabled = false)
@Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu" })
public void testDeleteMenu() throws WxErrorException {
wxService.deleteMenu();
}
Expand Down

0 comments on commit ed3cdfc

Please sign in to comment.