issue #8 添加同步回复消息的支持

This commit is contained in:
Daniel Qian 2014-08-26 21:25:59 +08:00
parent dad826b0e6
commit 706230da50
4 changed files with 136 additions and 31 deletions

View File

@ -45,6 +45,26 @@ router.route(message);
3. 规则的结束必须用``Rule.end()``或者``Rule.next()``,否则不会生效
4. 具体使用可以看源代码中的``WxMessageRouterTest``单元测试或者查看Javadoc
### 同步回复
``WxMessageRouter``默认使用异步的方式处理消息,如果要使用同步的方式处理消息,那么可以这样:
```java
router
.rule()
.async(false)
.handler(handler)
.end()
;
WxXmlMessage message = WxXmlMessage.fromXml(xml);
// 获得同步的返回结果
WxXmlMessage res = router.route(message);
String xml = res.toXml();
// ... 将xml写入HttpServletResponse
```
## 微信Java API
使用``WxService``可以调用微信API。目前已实现除“微信小店”以外的所有功能。

View File

@ -15,7 +15,8 @@ public interface WxMessageHandler {
*
* @param wxMessage
* @param context 上下文如果handler或interceptor之间有信息要传递可以用这个
* @return xml格式的消息如果在异步规则里处理的话可以返回null
*/
public void handle(WxXmlMessage wxMessage, Map<String, Object> context);
public WxXmlMessage handle(WxXmlMessage wxMessage, Map<String, Object> context);
}

View File

@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import chanjarster.weixin.bean.WxXmlMessage;
@ -37,8 +39,10 @@ import chanjarster.weixin.bean.WxXmlMessage;
*/
public class WxMessageRouter {
private List<Rule> rules = new ArrayList<Rule>();
private final List<Rule> rules = new ArrayList<Rule>();
private final ExecutorService es = Executors.newCachedThreadPool();
/**
* 开始一个新的Route规则
* @return
@ -51,21 +55,52 @@ public class WxMessageRouter {
* 处理微信消息
* @param wxMessage
*/
public void route(WxXmlMessage wxMessage) {
for (Rule rule : rules) {
public WxXmlMessage route(final WxXmlMessage wxMessage) {
final List<Rule> matchRules = new ArrayList<Rule>();
// 收集匹配的规则
for (final Rule rule : rules) {
if (rule.test(wxMessage)) {
rule.service(wxMessage);
if(!rule.reEnter) {
break;
}
matchRules.add(rule);
}
}
if (matchRules.size() == 0) {
return null;
}
if (matchRules.get(0).async) {
// 只要第一个是异步的那就异步执行
// 在另一个线程里执行
es.submit(new Runnable() {
public void run() {
for (final Rule rule : matchRules) {
rule.service(wxMessage);
if (!rule.reEnter) {
break;
}
}
}
});
return null;
}
WxXmlMessage res = null;
for (final Rule rule : matchRules) {
// 返回最后一个匹配规则的结果
res = rule.service(wxMessage);
if (!rule.reEnter) {
break;
}
}
return res;
}
public static class Rule {
private final WxMessageRouter routerBuilder;
private boolean async = true;
private String msgType;
private String event;
@ -84,6 +119,16 @@ public class WxMessageRouter {
this.routerBuilder = routerBuilder;
}
/**
* 设置是否异步执行默认是true
* @param async
* @return
*/
public Rule async(boolean async) {
this.async = async;
return this;
}
/**
* 如果msgType等于某值
* @param msgType
@ -209,21 +254,22 @@ public class WxMessageRouter {
* @param wxMessage
* @return true 代表继续执行别的routerfalse 代表停止执行别的router
*/
protected void service(WxXmlMessage wxMessage) {
protected WxXmlMessage service(WxXmlMessage wxMessage) {
Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过
for (WxMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context)) {
return;
return null;
}
}
// 交给handler处理
for (WxMessageHandler interceptor : this.handlers) {
interceptor.handle(wxMessage, context);
WxXmlMessage res = null;
for (WxMessageHandler handler : this.handlers) {
// 返回最后handler的结果
res = handler.handle(wxMessage, context);
}
return;
return res;
}
}

View File

@ -3,7 +3,6 @@ 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;
@ -17,40 +16,78 @@ import chanjarster.weixin.bean.WxXmlMessage;
@Test
public class WxMessageRouterTest {
protected StringBuilder sb;
protected WxMessageRouter router;
@BeforeMethod
public void prepare() {
this.sb = new StringBuilder();
this.router = new WxMessageRouter();
@Test(enabled = false)
public void prepare(boolean async, StringBuffer sb, WxMessageRouter router) {
router
.rule()
.async(async)
.msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1").content("CONTENT_1")
.handler(new WxEchoMessageHandler(sb, "COMBINE_4"))
.end()
.rule()
.async(async)
.msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1")
.handler(new WxEchoMessageHandler(sb, "COMBINE_3"))
.end()
.rule()
.async(async)
.msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK)
.handler(new WxEchoMessageHandler(sb, "COMBINE_2"))
.end()
.rule().msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoMessageHandler(sb, WxConsts.XML_MSG_TEXT)).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();
.rule().async(async).msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoMessageHandler(sb, WxConsts.XML_MSG_TEXT)).end()
.rule().async(async).event(WxConsts.EVT_CLICK).handler(new WxEchoMessageHandler(sb, WxConsts.EVT_CLICK)).end()
.rule().async(async).eventKey("KEY_1").handler(new WxEchoMessageHandler(sb, "KEY_1")).end()
.rule().async(async).content("CONTENT_1").handler(new WxEchoMessageHandler(sb, "CONTENT_1")).end()
.rule().async(async).handler(new WxEchoMessageHandler(sb, "ALL")).end();
;
}
@Test(dataProvider="messages-1")
public void testSimple(WxXmlMessage message, String expected) {
public void testSync(WxXmlMessage message, String expected) {
StringBuffer sb = new StringBuffer();
WxMessageRouter router = new WxMessageRouter();
prepare(false, sb, router);
router.route(message);
Assert.assertEquals(sb.toString(), expected);
}
@Test(dataProvider="messages-1")
public void testAsync(WxXmlMessage message, String expected) throws InterruptedException {
StringBuffer sb = new StringBuffer();
WxMessageRouter router = new WxMessageRouter();
prepare(true, sb, router);
router.route(message);
Thread.sleep(500l);
Assert.assertEquals(sb.toString(), expected);
}
public void testConcurrency() throws InterruptedException {
final WxMessageRouter router = new WxMessageRouter();
router.rule().handler(new WxMessageHandler() {
@Override
public WxXmlMessage handle(WxXmlMessage wxMessage, Map<String, Object> context) {
// TODO Auto-generated method stub
return null;
}
}).end();
final WxXmlMessage m = new WxXmlMessage();
Runnable r = new Runnable() {
@Override
public void run() {
router.route(m);
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
}
}
};
for (int i = 0; i < 10; i++) {
new Thread(r).start();
}
Thread.sleep(1000l * 2);
}
@DataProvider(name="messages-1")
public Object[][] messages2() {
WxXmlMessage message1 = new WxXmlMessage();
@ -98,17 +135,18 @@ public class WxMessageRouterTest {
public static class WxEchoMessageHandler implements WxMessageHandler {
private StringBuilder sb;
private StringBuffer sb;
private String echoStr;
public WxEchoMessageHandler(StringBuilder sb, String echoStr) {
public WxEchoMessageHandler(StringBuffer sb, String echoStr) {
this.sb = sb;
this.echoStr = echoStr;
}
@Override
public void handle(WxXmlMessage wxMessage, Map<String, Object> context) {
public WxXmlMessage handle(WxXmlMessage wxMessage, Map<String, Object> context) {
sb.append(this.echoStr).append(',');
return null;
}
}