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()``,否则不会生效 3. 规则的结束必须用``Rule.end()``或者``Rule.next()``,否则不会生效
4. 具体使用可以看源代码中的``WxMessageRouterTest``单元测试或者查看Javadoc 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 ## 微信Java API
使用``WxService``可以调用微信API。目前已实现除“微信小店”以外的所有功能。 使用``WxService``可以调用微信API。目前已实现除“微信小店”以外的所有功能。

View File

@ -15,7 +15,8 @@ public interface WxMessageHandler {
* *
* @param wxMessage * @param wxMessage
* @param context 上下文如果handler或interceptor之间有信息要传递可以用这个 * @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.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import chanjarster.weixin.bean.WxXmlMessage; import chanjarster.weixin.bean.WxXmlMessage;
@ -37,7 +39,9 @@ import chanjarster.weixin.bean.WxXmlMessage;
*/ */
public class WxMessageRouter { 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规则 * 开始一个新的Route规则
@ -51,21 +55,52 @@ public class WxMessageRouter {
* 处理微信消息 * 处理微信消息
* @param wxMessage * @param wxMessage
*/ */
public void route(WxXmlMessage wxMessage) { public WxXmlMessage route(final WxXmlMessage wxMessage) {
for (Rule rule : rules) { final List<Rule> matchRules = new ArrayList<Rule>();
// 收集匹配的规则
for (final Rule rule : rules) {
if (rule.test(wxMessage)) { if (rule.test(wxMessage)) {
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); rule.service(wxMessage);
if(!rule.reEnter) { if (!rule.reEnter) {
break; 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 { public static class Rule {
private final WxMessageRouter routerBuilder; private final WxMessageRouter routerBuilder;
private boolean async = true;
private String msgType; private String msgType;
private String event; private String event;
@ -84,6 +119,16 @@ public class WxMessageRouter {
this.routerBuilder = routerBuilder; this.routerBuilder = routerBuilder;
} }
/**
* 设置是否异步执行默认是true
* @param async
* @return
*/
public Rule async(boolean async) {
this.async = async;
return this;
}
/** /**
* 如果msgType等于某值 * 如果msgType等于某值
* @param msgType * @param msgType
@ -209,21 +254,22 @@ public class WxMessageRouter {
* @param wxMessage * @param wxMessage
* @return true 代表继续执行别的routerfalse 代表停止执行别的router * @return true 代表继续执行别的routerfalse 代表停止执行别的router
*/ */
protected void service(WxXmlMessage wxMessage) { protected WxXmlMessage service(WxXmlMessage wxMessage) {
Map<String, Object> context = new HashMap<String, Object>(); Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过 // 如果拦截器不通过
for (WxMessageInterceptor interceptor : this.interceptors) { for (WxMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context)) { if (!interceptor.intercept(wxMessage, context)) {
return; return null;
} }
} }
// 交给handler处理 // 交给handler处理
for (WxMessageHandler interceptor : this.handlers) { WxXmlMessage res = null;
interceptor.handle(wxMessage, context); for (WxMessageHandler handler : this.handlers) {
// 返回最后handler的结果
res = handler.handle(wxMessage, context);
} }
return res;
return;
} }
} }

View File

@ -3,7 +3,6 @@ package chanjarster.weixin.api;
import java.util.Map; import java.util.Map;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -17,40 +16,78 @@ import chanjarster.weixin.bean.WxXmlMessage;
@Test @Test
public class WxMessageRouterTest { public class WxMessageRouterTest {
protected StringBuilder sb; @Test(enabled = false)
protected WxMessageRouter router; public void prepare(boolean async, StringBuffer sb, WxMessageRouter router) {
@BeforeMethod
public void prepare() {
this.sb = new StringBuilder();
this.router = new WxMessageRouter();
router router
.rule() .rule()
.async(async)
.msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1").content("CONTENT_1") .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1").content("CONTENT_1")
.handler(new WxEchoMessageHandler(sb, "COMBINE_4")) .handler(new WxEchoMessageHandler(sb, "COMBINE_4"))
.end() .end()
.rule() .rule()
.async(async)
.msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1") .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1")
.handler(new WxEchoMessageHandler(sb, "COMBINE_3")) .handler(new WxEchoMessageHandler(sb, "COMBINE_3"))
.end() .end()
.rule() .rule()
.async(async)
.msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK) .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK)
.handler(new WxEchoMessageHandler(sb, "COMBINE_2")) .handler(new WxEchoMessageHandler(sb, "COMBINE_2"))
.end() .end()
.rule().msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoMessageHandler(sb, WxConsts.XML_MSG_TEXT)).end() .rule().async(async).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().async(async).event(WxConsts.EVT_CLICK).handler(new WxEchoMessageHandler(sb, WxConsts.EVT_CLICK)).end()
.rule().eventKey("KEY_1").handler(new WxEchoMessageHandler(sb, "KEY_1")).end() .rule().async(async).eventKey("KEY_1").handler(new WxEchoMessageHandler(sb, "KEY_1")).end()
.rule().content("CONTENT_1").handler(new WxEchoMessageHandler(sb, "CONTENT_1")).end() .rule().async(async).content("CONTENT_1").handler(new WxEchoMessageHandler(sb, "CONTENT_1")).end()
.rule().handler(new WxEchoMessageHandler(sb, "ALL")).end(); .rule().async(async).handler(new WxEchoMessageHandler(sb, "ALL")).end();
; ;
} }
@Test(dataProvider="messages-1") @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); router.route(message);
Assert.assertEquals(sb.toString(), expected); 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") @DataProvider(name="messages-1")
public Object[][] messages2() { public Object[][] messages2() {
WxXmlMessage message1 = new WxXmlMessage(); WxXmlMessage message1 = new WxXmlMessage();
@ -98,17 +135,18 @@ public class WxMessageRouterTest {
public static class WxEchoMessageHandler implements WxMessageHandler { public static class WxEchoMessageHandler implements WxMessageHandler {
private StringBuilder sb; private StringBuffer sb;
private String echoStr; private String echoStr;
public WxEchoMessageHandler(StringBuilder sb, String echoStr) { public WxEchoMessageHandler(StringBuffer sb, String echoStr) {
this.sb = sb; this.sb = sb;
this.echoStr = echoStr; this.echoStr = echoStr;
} }
@Override @Override
public void handle(WxXmlMessage wxMessage, Map<String, Object> context) { public WxXmlMessage handle(WxXmlMessage wxMessage, Map<String, Object> context) {
sb.append(this.echoStr).append(','); sb.append(this.echoStr).append(',');
return null;
} }
} }