diff --git a/README.md b/README.md
index 88ba5f7c..47393ea1 100644
--- a/README.md
+++ b/README.md
@@ -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
+```
diff --git a/pom.xml b/pom.xml
index 9297530d..b3655797 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4.0.0
chanjarster.weixin
- weixin-java
+ weixin-toolset
1.0.0-SNAPSHOT
WeiXin Java Toolset
https://github.com/chanjarster/weixin-java
diff --git a/src/main/java/chanjarster/weixin/api/WxConsts.java b/src/main/java/chanjarster/weixin/api/WxConsts.java
index a6e77b57..8d7d8d8f 100644
--- a/src/main/java/chanjarster/weixin/api/WxConsts.java
+++ b/src/main/java/chanjarster/weixin/api/WxConsts.java
@@ -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";
+
-
}
diff --git a/src/main/java/chanjarster/weixin/api/WxMessageHandler.java b/src/main/java/chanjarster/weixin/api/WxMessageHandler.java
index 9f985e2c..7dcb7a77 100644
--- a/src/main/java/chanjarster/weixin/api/WxMessageHandler.java
+++ b/src/main/java/chanjarster/weixin/api/WxMessageHandler.java
@@ -5,7 +5,7 @@ import java.util.Map;
import chanjarster.weixin.bean.WxXmlMessage;
/**
- * 处理微信推送消息的处理器
+ * 处理微信推送消息的处理器接口
* @author chanjarster
*
*/
diff --git a/src/main/java/chanjarster/weixin/api/WxMessageRouter.java b/src/main/java/chanjarster/weixin/api/WxMessageRouter.java
index c256a228..a7b8309f 100644
--- a/src/main/java/chanjarster/weixin/api/WxMessageRouter.java
+++ b/src/main/java/chanjarster/weixin/api/WxMessageRouter.java
@@ -5,13 +5,17 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import chanjarster.weixin.api.WxMessageRouterTest.WxEchoMessageHandler;
import chanjarster.weixin.bean.WxXmlMessage;
/**
*
* 微信消息路由器,通过代码化的配置,把来自微信的消息交给handler处理
*
+ * 说明:
+ * 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
+ * 2. 默认情况下消息只会被处理一次,除非使用 {@link Rule#next()}
+ * 3. 规则的结束必须用{@link Rule#end()}或者{@link Rule#next()},否则不会生效
+ *
* 使用方法:
* WxMessageRouter router = new WxMessageRouter();
* router
@@ -27,9 +31,6 @@ import chanjarster.weixin.bean.WxXmlMessage;
* // 将WxXmlMessage交给消息路由器
* router.route(message);
*
- * 说明:
- * 1. 配置路由规则时要按照从细到粗的原则
- * 2. 默认情况下消息只会被处理一次,除非使用 {@link Rule#reEnter()}
*
* @author qianjia
*
@@ -52,9 +53,11 @@ public class WxMessageRouter {
*/
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;
+ }
}
}
}
@@ -122,16 +125,16 @@ public class WxMessageRouter {
}
/**
- * 将消息交给后面的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
@@ -147,10 +150,20 @@ public class WxMessageRouter {
}
/**
- * 添加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) {
@@ -162,7 +175,7 @@ public class WxMessageRouter {
}
/**
- * 规则结束
+ * 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则
* @return
*/
public WxMessageRouter end() {
@@ -170,6 +183,15 @@ public class WxMessageRouter {
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()))
@@ -188,11 +210,6 @@ public class WxMessageRouter {
* @return true 代表继续执行别的router,false 代表停止执行别的router
*/
protected boolean service(WxXmlMessage wxMessage) {
- // 如果不匹配本规则,那么接着执行后面的Rule
- if (!test(wxMessage)) {
- return true;
- }
-
Map context = new HashMap();
// 如果拦截器不通过
for (WxMessageInterceptor interceptor : this.interceptors) {
diff --git a/src/main/java/chanjarster/weixin/api/WxServiceImpl.java b/src/main/java/chanjarster/weixin/api/WxServiceImpl.java
index aa319c89..1c203ef4 100644
--- a/src/main/java/chanjarster/weixin/api/WxServiceImpl.java
+++ b/src/main/java/chanjarster/weixin/api/WxServiceImpl.java
@@ -20,7 +20,7 @@ import chanjarster.weixin.bean.WxCustomMessage;
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 {
@@ -133,7 +133,7 @@ public class WxServiceImpl implements WxService {
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;
@@ -141,7 +141,7 @@ public class WxServiceImpl implements WxService {
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);
diff --git a/src/main/java/chanjarster/weixin/util/Utf8StringResponseHandler.java b/src/main/java/chanjarster/weixin/util/Utf8ResponseHandler.java
similarity index 90%
rename from src/main/java/chanjarster/weixin/util/Utf8StringResponseHandler.java
rename to src/main/java/chanjarster/weixin/util/Utf8ResponseHandler.java
index 1b8f0145..9884425d 100644
--- a/src/main/java/chanjarster/weixin/util/Utf8StringResponseHandler.java
+++ b/src/main/java/chanjarster/weixin/util/Utf8ResponseHandler.java
@@ -15,9 +15,9 @@ import org.apache.http.util.EntityUtils;
* @author chanjarster
*
*/
-public class Utf8StringResponseHandler implements ResponseHandler {
+public class Utf8ResponseHandler implements ResponseHandler {
- public static final ResponseHandler INSTANCE = new Utf8StringResponseHandler();
+ public static final ResponseHandler INSTANCE = new Utf8ResponseHandler();
public String handleResponse(final HttpResponse response) throws HttpResponseException, IOException {
final StatusLine statusLine = response.getStatusLine();
diff --git a/src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java b/src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java
index 03e83fd4..76b7368b 100644
--- a/src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java
+++ b/src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java
@@ -3,30 +3,97 @@ package chanjarster.weixin.api;
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 {
diff --git a/src/test/java/chanjarster/weixin/api/WxServiceTest.java b/src/test/java/chanjarster/weixin/api/WxServiceTest.java
index 4306970b..58650bee 100644
--- a/src/test/java/chanjarster/weixin/api/WxServiceTest.java
+++ b/src/test/java/chanjarster/weixin/api/WxServiceTest.java
@@ -43,18 +43,18 @@ public class WxServiceTest {
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下面\nHello World");
+ message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World");
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);
}
@@ -64,7 +64,7 @@ public class WxServiceTest {
Assert.assertNotNull(wxService.getMenu());
}
- @Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu" }, enabled = false)
+ @Test(dependsOnMethods = { "testRefreshAccessToken", "testGetMenu" })
public void testDeleteMenu() throws WxErrorException {
wxService.deleteMenu();
}