21 Commits

Author SHA1 Message Date
Daniel Qian
239ef054ec prompt version to 1.1.1 2015-01-25 10:59:45 +08:00
Daniel Qian
15aff7f93c issue #88 修改createJsapiSignature方法签名,便于开发人员使用 2015-01-25 10:35:40 +08:00
Daniel Qian
1819b0e492 issue #87 WxMessageRouter、Rule提供setter方法,便于使用spring等依赖注入框架 2015-01-24 19:35:32 +08:00
Daniel Qian
97dff44b3e issue #78 删除接口支持批量操作 2015-01-24 15:27:14 +08:00
Daniel Qian
b8dd3662cd issue #79 增加批量获取用户详情的接口 2015-01-24 15:18:31 +08:00
Daniel Qian
2c218d85c2 issue #80 增加用户进入应用的callback事件 2015-01-24 15:06:01 +08:00
Daniel Qian
10f841c591 修改一下方法签名,代码洁癖 2015-01-24 15:03:11 +08:00
Daniel Qian
037c326ebf issue #81 增加获取微信服务器的ip段接口 2015-01-24 15:00:10 +08:00
Daniel Qian
b43a62c3a6 issue #82 增加邀请成员关注接口 2015-01-24 14:51:57 +08:00
Daniel Qian
b921490253 issue #86 handler, intercept方法签名添加throw WxException 2015-01-23 17:20:05 +08:00
Daniel Qian
8ec76bda59 issue #84 Wx*ConfigStorage在多线程环境下有visibility问题 2015-01-23 10:13:02 +08:00
Daniel Qian
4b8aa69cca issue #77 用matcher做新的猜数字游戏,代码更简单 2015-01-22 21:27:38 +08:00
Daniel Qian
36dad7b8d0 issue #77 添加自定义消息匹配器的支持 2015-01-22 20:37:26 +08:00
Daniel Qian
b00bf13bd7 refactor: 修改rule.service方法签名 2015-01-22 20:11:22 +08:00
Daniel Qian
ffe63e71eb 添加猜数字游戏的demo 2015-01-22 17:11:56 +08:00
Daniel Qian
3fbb260bf7 add tostring 2015-01-22 17:11:35 +08:00
Daniel Qian
bf4c825279 issue #76 2015-01-22 16:11:34 +08:00
Daniel Qian
84a3683c35 Merge branch 'master' into develop
Conflicts:
	pom.xml
	weixin-java-common/pom.xml
	weixin-java-cp/pom.xml
	weixin-java-mp/pom.xml
2015-01-22 14:50:08 +08:00
Daniel Qian
4d6321e69b modify readme 2015-01-22 14:48:18 +08:00
Daniel Qian
7c3113eec8 upgrade to 1.1.1-SNAPSHOT 2015-01-22 14:44:12 +08:00
Daniel Qian
3003f68cf4 upgrade to 1.1.0 2015-01-22 14:42:41 +08:00
36 changed files with 1473 additions and 673 deletions

View File

@@ -3,12 +3,6 @@ weixin-java-tools
微信公众号、企业号Java SDK。
从``1.0.3``开始本项目拆分成3个部分
1. weixin-java-common公共lib
2. weixin-java-cp企业号SDK
3. weixin-java-mp公众号订阅号、服务号SDK
详细文档请看 [wiki](https://github.com/chanjarster/weixin-java-tools/wiki)。
## Quick Start
@@ -19,7 +13,7 @@ weixin-java-tools
<dependency>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>1.0.7</version>
<version>1.1.1</version>
</dependency>
```
@@ -29,7 +23,7 @@ weixin-java-tools
<dependency>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-cp</artifactId>
<version>1.0.7</version>
<version>1.1.1</version>
</dependency>
```
@@ -45,12 +39,4 @@ weixin-java-tools
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>
```
## 升级指南
原``1.0.0~1.0.2``版本用户无法平滑升级到``1.0.3``。需要做的是:
1. maven引用``weixin-java-mp``
2. 将原来``WxXXX``的类,改成``WxMpXXX``
3. ``WxConsts``, ``WxError``, ``WxMediaUploadResult``, ``WxAccessToken``, ``WxMenu``, ``WxErrorException``不要改
```

View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
<packaging>pom</packaging>
<name>WeiXin Java Tools - Parent</name>
<description>微信公众号、企业号上级POM</description>
@@ -217,7 +217,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5</version>
<version>2.5.1</version>
<configuration>
<autoVersionSubmodules>true</autoVersionSubmodules>
<useReleaseProfile>false</useReleaseProfile>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
</parent>
<artifactId>weixin-java-common</artifactId>

View File

@@ -89,6 +89,7 @@ public class WxConsts {
public static final String EVT_PIC_WEIXIN = "pic_weixin";
public static final String EVT_LOCATION_SELECT = "location_select";
public static final String EVT_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH";
public static final String EVT_ENTER_AGENT = "enter_agent";
///////////////////////
// 上传多媒体文件的类型

View File

@@ -0,0 +1,19 @@
package me.chanjar.weixin.common.util;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogExceptionHandler implements WxErrorExceptionHandler {
private Logger log = LoggerFactory.getLogger(WxErrorExceptionHandler.class);
@Override
public void handle(WxErrorException e) {
log.error("Error happens", e);
}
}

View File

@@ -0,0 +1,12 @@
package me.chanjar.weixin.common.util;
import me.chanjar.weixin.common.exception.WxErrorException;
/**
* WxErrorException处理器
*/
public interface WxErrorExceptionHandler {
public void handle(WxErrorException e);
}

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
</parent>
<artifactId>weixin-java-cp</artifactId>

View File

@@ -9,21 +9,21 @@ import me.chanjar.weixin.common.bean.WxAccessToken;
*/
public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
protected String corpId;
protected String corpSecret;
protected volatile String corpId;
protected volatile String corpSecret;
protected String token;
protected String accessToken;
protected String aesKey;
protected String agentId;
protected long expiresTime;
protected volatile String token;
protected volatile String accessToken;
protected volatile String aesKey;
protected volatile String agentId;
protected volatile long expiresTime;
protected String oauth2redirectUri;
protected volatile String oauth2redirectUri;
protected String http_proxy_host;
protected int http_proxy_port;
protected String http_proxy_username;
protected String http_proxy_password;
protected volatile String http_proxy_host;
protected volatile int http_proxy_port;
protected volatile String http_proxy_username;
protected volatile String http_proxy_password;
public String getAccessToken() {
return this.accessToken;

View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSession;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
@@ -24,6 +25,6 @@ public interface WxCpMessageHandler {
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage,
Map<String, Object> context,
WxCpService wxCpService,
WxSessionManager sessionManager);
WxSessionManager sessionManager) throws WxErrorException;
}

View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
@@ -24,6 +25,6 @@ public interface WxCpMessageInterceptor {
public boolean intercept(WxCpXmlMessage wxMessage,
Map<String, Object> context,
WxCpService wxCpService,
WxSessionManager sessionManager);
WxSessionManager sessionManager) throws WxErrorException;
}

View File

@@ -0,0 +1,17 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
/**
* 消息匹配器,用在消息路由的时候
*/
public interface WxCpMessageMatcher {
/**
* 消息是否匹配某种模式
* @param message
* @return
*/
public boolean match(WxCpXmlMessage message);
}

View File

@@ -1,6 +1,11 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.session.*;
import me.chanjar.weixin.common.session.InternalSession;
import me.chanjar.weixin.common.session.InternalSessionManager;
import me.chanjar.weixin.common.session.StandardSessionManager;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.LogExceptionHandler;
import me.chanjar.weixin.common.util.WxErrorExceptionHandler;
import me.chanjar.weixin.common.util.WxMessageDuplicateChecker;
import me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
@@ -9,14 +14,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
/**
* <pre>
@@ -24,8 +26,8 @@ import java.util.regex.Pattern;
*
* 说明:
* 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
* 2. 默认情况下消息只会被处理一次,除非使用 {@link Rule#next()}
* 3. 规则的结束必须用{@link Rule#end()}或者{@link Rule#next()},否则不会生效
* 2. 默认情况下消息只会被处理一次,除非使用 {@link WxCpMessageRouterRule#next()}
* 3. 规则的结束必须用{@link WxCpMessageRouterRule#end()}或者{@link WxCpMessageRouterRule#next()},否则不会生效
*
* 使用方法:
* WxCpMessageRouter router = new WxCpMessageRouter();
@@ -52,7 +54,7 @@ public class WxCpMessageRouter {
private static final int DEFAULT_THREAD_POOL_SIZE = 100;
private final List<Rule> rules = new ArrayList<Rule>();
private final List<WxCpMessageRouterRule> rules = new ArrayList<WxCpMessageRouterRule>();
private final WxCpService wxCpService;
@@ -62,11 +64,14 @@ public class WxCpMessageRouter {
private WxSessionManager sessionManager;
private WxErrorExceptionHandler exceptionHandler;
public WxCpMessageRouter(WxCpService wxCpService) {
this.wxCpService = wxCpService;
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker();
this.sessionManager = new StandardSessionManager();
this.exceptionHandler = new LogExceptionHandler();
}
/**
@@ -94,7 +99,7 @@ public class WxCpMessageRouter {
/**
* <pre>
* 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager}
* 如果不调用该方法,默认使用 {@linke SessionManagerImpl}
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager}
* </pre>
* @param sessionManager
*/
@@ -102,12 +107,27 @@ public class WxCpMessageRouter {
this.sessionManager = sessionManager;
}
/**
* <pre>
* 设置自定义的{@link me.chanjar.weixin.common.util.WxErrorExceptionHandler}
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler}
* </pre>
* @param exceptionHandler
*/
public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
List<WxCpMessageRouterRule> getRules() {
return this.rules;
}
/**
* 开始一个新的Route规则
* @return
*/
public Rule rule() {
return new Rule(this, wxCpService, sessionManager);
public WxCpMessageRouterRule rule() {
return new WxCpMessageRouterRule(this);
}
/**
@@ -119,13 +139,13 @@ public class WxCpMessageRouter {
// 如果是重复消息,那么就不做处理
return null;
}
final List<Rule> matchRules = new ArrayList<Rule>();
final List<WxCpMessageRouterRule> matchRules = new ArrayList<WxCpMessageRouterRule>();
// 收集匹配的规则
for (final Rule rule : rules) {
for (final WxCpMessageRouterRule rule : rules) {
if (rule.test(wxMessage)) {
matchRules.add(rule);
if(!rule.reEnter) {
if(!rule.isReEnter()) {
break;
}
}
@@ -137,18 +157,18 @@ public class WxCpMessageRouter {
WxCpXmlOutMessage res = null;
final List<Future> futures = new ArrayList<Future>();
for (final Rule rule : matchRules) {
for (final WxCpMessageRouterRule rule : matchRules) {
// 返回最后一个非异步的rule的执行结果
if(rule.async) {
if(rule.isAsync()) {
futures.add(
executorService.submit(new Runnable() {
public void run() {
rule.service(wxMessage);
rule.service(wxMessage, wxCpService, sessionManager, exceptionHandler);
}
})
);
} else {
res = rule.service(wxMessage);
res = rule.service(wxMessage, wxCpService, sessionManager, exceptionHandler);
// 在同步操作结束session访问结束
log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUserName());
sessionEndAccess(wxMessage);
@@ -181,7 +201,11 @@ public class WxCpMessageRouter {
String messageId = "";
if (wxMessage.getMsgId() == null) {
messageId = wxMessage.getFromUserName() + "-" + String.valueOf(wxMessage.getCreateTime());
messageId = String.valueOf(wxMessage.getCreateTime())
+ "-" +String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId())
+ "-" + wxMessage.getFromUserName()
+ "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEvent());
;
} else {
messageId = String.valueOf(wxMessage.getMsgId());
}
@@ -206,231 +230,5 @@ public class WxCpMessageRouter {
}
public static class Rule {
private final WxCpMessageRouter routerBuilder;
private final WxCpService wxCpService;
private final WxSessionManager sessionManager;
private boolean async = true;
private String fromUser;
private String msgType;
private String event;
private String eventKey;
private String content;
private String rContent;
private boolean reEnter = false;
private Integer agentId;
private List<WxCpMessageHandler> handlers = new ArrayList<WxCpMessageHandler>();
private List<WxCpMessageInterceptor> interceptors = new ArrayList<WxCpMessageInterceptor>();
protected Rule(WxCpMessageRouter routerBuilder, WxCpService wxCpService, WxSessionManager sessionManager) {
this.routerBuilder = routerBuilder;
this.wxCpService = wxCpService;
this.sessionManager = sessionManager;
}
/**
* 设置是否异步执行默认是true
* @param async
* @return
*/
public Rule async(boolean async) {
this.async = async;
return this;
}
/**
* 如果agentId匹配
* @param agentId
* @return
*/
public Rule agentId(Integer agentId) {
this.agentId = agentId;
return this;
}
/**
* 如果msgType等于某值
* @param msgType
* @return
*/
public Rule msgType(String msgType) {
this.msgType = msgType;
return this;
}
/**
* 如果event等于某值
* @param event
* @return
*/
public Rule event(String event) {
this.event = event;
return this;
}
/**
* 如果eventKey等于某值
* @param eventKey
* @return
*/
public Rule eventKey(String eventKey) {
this.eventKey = eventKey;
return this;
}
/**
* 如果content等于某值
* @param content
* @return
*/
public Rule content(String content) {
this.content = content;
return this;
}
/**
* 如果content匹配该正则表达式
* @param regex
* @return
*/
public Rule rContent(String regex) {
this.rContent = regex;
return this;
}
/**
* 如果fromUser等于某值
* @param fromUser
* @return
*/
public Rule fromUser(String fromUser) {
this.fromUser = fromUser;
return this;
}
/**
* 设置微信消息拦截器
* @param interceptor
* @return
*/
public Rule interceptor(WxCpMessageInterceptor interceptor) {
return interceptor(interceptor, (WxCpMessageInterceptor[]) null);
}
/**
* 设置微信消息拦截器
* @param interceptor
* @param otherInterceptors
* @return
*/
public Rule interceptor(WxCpMessageInterceptor interceptor, WxCpMessageInterceptor... otherInterceptors) {
this.interceptors.add(interceptor);
if (otherInterceptors != null && otherInterceptors.length > 0) {
for (WxCpMessageInterceptor i : otherInterceptors) {
this.interceptors.add(i);
}
}
return this;
}
/**
* 设置微信消息处理器
* @param handler
* @return
*/
public Rule handler(WxCpMessageHandler handler) {
return handler(handler, (WxCpMessageHandler[]) null);
}
/**
* 设置微信消息处理器
* @param handler
* @param otherHandlers
* @return
*/
public Rule handler(WxCpMessageHandler handler, WxCpMessageHandler... otherHandlers) {
this.handlers.add(handler);
if (otherHandlers != null && otherHandlers.length > 0) {
for (WxCpMessageHandler i : otherHandlers) {
this.handlers.add(i);
}
}
return this;
}
/**
* 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则
* @return
*/
public WxCpMessageRouter end() {
this.routerBuilder.rules.add(this);
return this.routerBuilder;
}
/**
* 规则结束,但是消息还会进入其他规则
* @return
*/
public WxCpMessageRouter next() {
this.reEnter = true;
return end();
}
protected boolean test(WxCpXmlMessage wxMessage) {
return
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
&&
(this.agentId == null || this.agentId.equals(wxMessage.getAgentId()))
&&
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
&&
(this.event == null || this.event.equals(wxMessage.getEvent()))
&&
(this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey()))
&&
(this.content == null || this.content.equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim()))
&&
(this.rContent == null || Pattern.matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim()))
;
}
/**
* 处理微信推送过来的消息
* @param wxMessage
* @return true 代表继续执行别的routerfalse 代表停止执行别的router
*/
protected WxCpXmlOutMessage service(WxCpXmlMessage wxMessage) {
Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过
for (WxCpMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context, wxCpService, sessionManager)) {
return null;
}
}
// 交给handler处理
WxCpXmlOutMessage res = null;
for (WxCpMessageHandler handler : this.handlers) {
// 返回最后handler的结果
res = handler.handle(wxMessage, context, wxCpService, sessionManager);
}
return res;
}
}
}

View File

@@ -0,0 +1,335 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.WxErrorExceptionHandler;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class WxCpMessageRouterRule {
private final WxCpMessageRouter routerBuilder;
private boolean async = true;
private String fromUser;
private String msgType;
private String event;
private String eventKey;
private String content;
private String rContent;
private WxCpMessageMatcher matcher;
private boolean reEnter = false;
private Integer agentId;
private List<WxCpMessageHandler> handlers = new ArrayList<WxCpMessageHandler>();
private List<WxCpMessageInterceptor> interceptors = new ArrayList<WxCpMessageInterceptor>();
protected WxCpMessageRouterRule(WxCpMessageRouter routerBuilder) {
this.routerBuilder = routerBuilder;
}
/**
* 设置是否异步执行默认是true
*
* @param async
* @return
*/
public WxCpMessageRouterRule async(boolean async) {
this.async = async;
return this;
}
/**
* 如果agentId匹配
*
* @param agentId
* @return
*/
public WxCpMessageRouterRule agentId(Integer agentId) {
this.agentId = agentId;
return this;
}
/**
* 如果msgType等于某值
*
* @param msgType
* @return
*/
public WxCpMessageRouterRule msgType(String msgType) {
this.msgType = msgType;
return this;
}
/**
* 如果event等于某值
*
* @param event
* @return
*/
public WxCpMessageRouterRule event(String event) {
this.event = event;
return this;
}
/**
* 如果eventKey等于某值
*
* @param eventKey
* @return
*/
public WxCpMessageRouterRule eventKey(String eventKey) {
this.eventKey = eventKey;
return this;
}
/**
* 如果content等于某值
*
* @param content
* @return
*/
public WxCpMessageRouterRule content(String content) {
this.content = content;
return this;
}
/**
* 如果content匹配该正则表达式
*
* @param regex
* @return
*/
public WxCpMessageRouterRule rContent(String regex) {
this.rContent = regex;
return this;
}
/**
* 如果fromUser等于某值
*
* @param fromUser
* @return
*/
public WxCpMessageRouterRule fromUser(String fromUser) {
this.fromUser = fromUser;
return this;
}
/**
* 如果消息匹配某个matcher用在用户需要自定义更复杂的匹配规则的时候
*
* @param matcher
* @return
*/
public WxCpMessageRouterRule matcher(WxCpMessageMatcher matcher) {
this.matcher = matcher;
return this;
}
/**
* 设置微信消息拦截器
*
* @param interceptor
* @return
*/
public WxCpMessageRouterRule interceptor(WxCpMessageInterceptor interceptor) {
return interceptor(interceptor, (WxCpMessageInterceptor[]) null);
}
/**
* 设置微信消息拦截器
*
* @param interceptor
* @param otherInterceptors
* @return
*/
public WxCpMessageRouterRule interceptor(WxCpMessageInterceptor interceptor, WxCpMessageInterceptor... otherInterceptors) {
this.interceptors.add(interceptor);
if (otherInterceptors != null && otherInterceptors.length > 0) {
for (WxCpMessageInterceptor i : otherInterceptors) {
this.interceptors.add(i);
}
}
return this;
}
/**
* 设置微信消息处理器
*
* @param handler
* @return
*/
public WxCpMessageRouterRule handler(WxCpMessageHandler handler) {
return handler(handler, (WxCpMessageHandler[]) null);
}
/**
* 设置微信消息处理器
*
* @param handler
* @param otherHandlers
* @return
*/
public WxCpMessageRouterRule handler(WxCpMessageHandler handler, WxCpMessageHandler... otherHandlers) {
this.handlers.add(handler);
if (otherHandlers != null && otherHandlers.length > 0) {
for (WxCpMessageHandler i : otherHandlers) {
this.handlers.add(i);
}
}
return this;
}
/**
* 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则
*
* @return
*/
public WxCpMessageRouter end() {
this.routerBuilder.getRules().add(this);
return this.routerBuilder;
}
/**
* 规则结束,但是消息还会进入其他规则
*
* @return
*/
public WxCpMessageRouter next() {
this.reEnter = true;
return end();
}
protected boolean test(WxCpXmlMessage wxMessage) {
return
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
&&
(this.agentId == null || this.agentId.equals(wxMessage.getAgentId()))
&&
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
&&
(this.event == null || this.event.equals(wxMessage.getEvent()))
&&
(this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey()))
&&
(this.content == null || this.content
.equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim()))
&&
(this.rContent == null || Pattern
.matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim()))
&&
(this.matcher == null || this.matcher.match(wxMessage))
;
}
/**
* 处理微信推送过来的消息
*
* @param wxMessage
* @return true 代表继续执行别的routerfalse 代表停止执行别的router
*/
protected WxCpXmlOutMessage service(WxCpXmlMessage wxMessage,
WxCpService wxCpService,
WxSessionManager sessionManager,
WxErrorExceptionHandler exceptionHandler) {
try {
Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过
for (WxCpMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context, wxCpService, sessionManager)) {
return null;
}
}
// 交给handler处理
WxCpXmlOutMessage res = null;
for (WxCpMessageHandler handler : this.handlers) {
// 返回最后handler的结果
res = handler.handle(wxMessage, context, wxCpService, sessionManager);
}
return res;
} catch (WxErrorException e) {
exceptionHandler.handle(e);
}
return null;
}
public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public void setEvent(String event) {
this.event = event;
}
public void setEventKey(String eventKey) {
this.eventKey = eventKey;
}
public void setContent(String content) {
this.content = content;
}
public void setrContent(String rContent) {
this.rContent = rContent;
}
public void setMatcher(WxCpMessageMatcher matcher) {
this.matcher = matcher;
}
public void setAgentId(Integer agentId) {
this.agentId = agentId;
}
public void setHandlers(List<WxCpMessageHandler> handlers) {
this.handlers = handlers;
}
public void setInterceptors(List<WxCpMessageInterceptor> interceptors) {
this.interceptors = interceptors;
}
public boolean isAsync() {
return async;
}
public void setAsync(boolean async) {
this.async = async;
}
public boolean isReEnter() {
return reEnter;
}
public void setReEnter(boolean reEnter) {
this.reEnter = reEnter;
}
}

View File

@@ -33,7 +33,7 @@ public interface WxCpService {
* @param data 微信传输过来的数据有可能是echoStr有可能是xml消息
* @return
*/
public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data);
boolean checkSignature(String msgSignature, String timestamp, String nonce, String data);
/**
* <pre>
@@ -43,7 +43,7 @@ public interface WxCpService {
*
* @param userId
*/
public void userAuthenticated(String userId) throws WxErrorException;
void userAuthenticated(String userId) throws WxErrorException;
/**
* 获取access_token, 不强制刷新access_token
@@ -51,7 +51,7 @@ public interface WxCpService {
* @return
* @throws WxErrorException
*/
public String getAccessToken() throws WxErrorException;
String getAccessToken() throws WxErrorException;
/**
* <pre>
@@ -65,7 +65,7 @@ public interface WxCpService {
* @return
* @throws me.chanjar.weixin.common.exception.WxErrorException
*/
public String getAccessToken(boolean forceRefresh) throws WxErrorException;
String getAccessToken(boolean forceRefresh) throws WxErrorException;
/**
* <pre>
@@ -83,7 +83,7 @@ public interface WxCpService {
* @param inputStream 输入流
* @throws WxErrorException
*/
public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream)
WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream)
throws WxErrorException, IOException;
/**
@@ -92,7 +92,7 @@ public interface WxCpService {
* @throws WxErrorException
* @see #mediaUpload(String, String, InputStream)
*/
public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException;
WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException;
/**
* <pre>
@@ -105,7 +105,7 @@ public interface WxCpService {
* @throws WxErrorException
* @params media_id
*/
public File mediaDownload(String media_id) throws WxErrorException;
File mediaDownload(String media_id) throws WxErrorException;
/**
* <pre>
@@ -116,7 +116,7 @@ public interface WxCpService {
* @param message
* @throws WxErrorException
*/
public void messageSend(WxCpMessage message) throws WxErrorException;
void messageSend(WxCpMessage message) throws WxErrorException;
/**
* <pre>
@@ -127,7 +127,7 @@ public interface WxCpService {
* @param menu
* @throws WxErrorException
*/
public void menuCreate(WxMenu menu) throws WxErrorException;
void menuCreate(WxMenu menu) throws WxErrorException;
/**
* <pre>
@@ -137,7 +137,7 @@ public interface WxCpService {
*
* @throws WxErrorException
*/
public void menuDelete() throws WxErrorException;
void menuDelete() throws WxErrorException;
/**
* <pre>
@@ -148,7 +148,7 @@ public interface WxCpService {
* @return
* @throws WxErrorException
*/
public WxMenu menuGet() throws WxErrorException;
WxMenu menuGet() throws WxErrorException;
/**
* <pre>
@@ -161,7 +161,7 @@ public interface WxCpService {
* @return 部门id
* @throws WxErrorException
*/
public Integer departCreate(WxCpDepart depart) throws WxErrorException;
Integer departCreate(WxCpDepart depart) throws WxErrorException;
/**
* <pre>
@@ -172,7 +172,7 @@ public interface WxCpService {
* @return
* @throws WxErrorException
*/
public List<WxCpDepart> departGet() throws WxErrorException;
List<WxCpDepart> departGet() throws WxErrorException;
/**
* <pre>
@@ -184,7 +184,7 @@ public interface WxCpService {
* @param group 要更新的groupgroup的id,name必须设置
* @throws WxErrorException
*/
public void departUpdate(WxCpDepart group) throws WxErrorException;
void departUpdate(WxCpDepart group) throws WxErrorException;
/**
* <pre>
@@ -194,10 +194,28 @@ public interface WxCpService {
* @param departId
* @throws WxErrorException
*/
public void departDelete(Integer departId) throws WxErrorException;
void departDelete(Integer departId) throws WxErrorException;
/**
* <pre>
* 获取部门成员(详情)
*
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98.28.E8.AF.A6.E6.83.85.29
* </pre>
* @param departId 必填。部门id
* @param fetchChild 非必填。1/0是否递归获取子部门下面的成员
* @param status 非必填。0获取全部员工1获取已关注成员列表2获取禁用成员列表4获取未关注成员列表。status可叠加
* @return
* @throws WxErrorException
*/
List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException;
/**
* <pre>
* 获取部门成员
*
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98
* </pre>
*
* @param departId 必填。部门id
* @param fetchChild 非必填。1/0是否递归获取子部门下面的成员
@@ -205,7 +223,7 @@ public interface WxCpService {
* @return
* @throws WxErrorException
*/
public List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException;
List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException;
/**
* 新建用户
@@ -213,7 +231,7 @@ public interface WxCpService {
* @param user
* @throws WxErrorException
*/
public void userCreate(WxCpUser user) throws WxErrorException;
void userCreate(WxCpUser user) throws WxErrorException;
/**
* 更新用户
@@ -221,7 +239,7 @@ public interface WxCpService {
* @param user
* @throws WxErrorException
*/
public void userUpdate(WxCpUser user) throws WxErrorException;
void userUpdate(WxCpUser user) throws WxErrorException;
/**
* 删除用户
@@ -229,7 +247,18 @@ public interface WxCpService {
* @param userid
* @throws WxErrorException
*/
public void userDelete(String userid) throws WxErrorException;
void userDelete(String userid) throws WxErrorException;
/**
* <pre>
* 批量删除成员
*
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98
* </pre>
* @param userids 员工UserID列表。对应管理端的帐号
* @throws WxErrorException
*/
void userDelete(String[] userids) throws WxErrorException;
/**
* 获取用户
@@ -238,7 +267,7 @@ public interface WxCpService {
* @return
* @throws WxErrorException
*/
public WxCpUser userGet(String userid) throws WxErrorException;
WxCpUser userGet(String userid) throws WxErrorException;
/**
* 创建标签
@@ -246,7 +275,7 @@ public interface WxCpService {
* @param tagName
* @return
*/
public String tagCreate(String tagName) throws WxErrorException;
String tagCreate(String tagName) throws WxErrorException;
/**
* 更新标签
@@ -254,21 +283,21 @@ public interface WxCpService {
* @param tagId
* @param tagName
*/
public void tagUpdate(String tagId, String tagName) throws WxErrorException;
void tagUpdate(String tagId, String tagName) throws WxErrorException;
/**
* 删除标签
*
* @param tagId
*/
public void tagDelete(String tagId) throws WxErrorException;
void tagDelete(String tagId) throws WxErrorException;
/**
* 获得标签列表
*
* @return
*/
public List<WxCpTag> tagGet() throws WxErrorException;
List<WxCpTag> tagGet() throws WxErrorException;
/**
* 获取标签成员
@@ -276,7 +305,7 @@ public interface WxCpService {
* @param tagId
* @return
*/
public List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException;
List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException;
/**
* 增加标签成员
@@ -284,7 +313,7 @@ public interface WxCpService {
* @param tagId
* @param userIds
*/
public void tagAddUsers(String tagId, List<String> userIds) throws WxErrorException;
void tagAddUsers(String tagId, List<String> userIds) throws WxErrorException;
/**
* <pre>
@@ -294,7 +323,7 @@ public interface WxCpService {
* @param state
* @return code
*/
public String oauth2buildAuthorizationUrl(String state);
String oauth2buildAuthorizationUrl(String state);
/**
* <pre>
@@ -305,7 +334,7 @@ public interface WxCpService {
* @param code
* @return [userid, deviceid]
*/
public String[] oauth2getUserInfo(String code) throws WxErrorException;
String[] oauth2getUserInfo(String code) throws WxErrorException;
/**
* 移除标签成员
@@ -313,7 +342,29 @@ public interface WxCpService {
* @param tagId
* @param userIds
*/
public void tagRemoveUsers(String tagId, List<String> userIds) throws WxErrorException;
void tagRemoveUsers(String tagId, List<String> userIds) throws WxErrorException;
/**
* <pre>
* 邀请成员关注
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8
* </pre>
* @param userId 用户的userid
* @param inviteTips 推送到微信上的提示语只有认证号可以使用。当使用微信推送时该字段默认为“请关注XXX企业号”邮件邀请时该字段无效。
* @return 1:微信邀请 2.邮件邀请
* @throws WxErrorException
*/
int invite(String userId, String inviteTips) throws WxErrorException;
/**
* <pre>
* 获取微信服务器的ip段
* http://qydev.weixin.qq.com/wiki/index.php?title=回调模式#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5
* </pre>
* @return { "ip_list": ["101.226.103.*", "101.226.62.*"] }
* @throws WxErrorException
*/
String[] getCallbackIp() throws WxErrorException;
/**
* 当本Service没有实现某个API的时候可以用这个针对所有微信API中的GET请求
@@ -347,14 +398,14 @@ public interface WxCpService {
* @return
* @throws WxErrorException
*/
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;
/**
* 注入 {@link WxCpConfigStorage} 的实现
*
* @param wxConfigProvider
*/
public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider);
void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider);
/**
* <pre>

View File

@@ -78,7 +78,7 @@ public class WxCpServiceImpl implements WxCpService {
public void userAuthenticated(String userId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId;
execute(new SimpleGetRequestExecutor(), url, null);
get(url, null);
}
public String getAccessToken() throws WxErrorException {
@@ -123,23 +123,23 @@ public class WxCpServiceImpl implements WxCpService {
public void messageSend(WxCpMessage message) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send";
execute(new SimplePostRequestExecutor(), url, message.toJson());
post(url, message.toJson());
}
public void menuCreate(WxMenu menu) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" + wxCpConfigStorage.getAgentId();
execute(new SimplePostRequestExecutor(), url, menu.toJson());
post(url, menu.toJson());
}
public void menuDelete() throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + wxCpConfigStorage.getAgentId();
execute(new SimpleGetRequestExecutor(), url, null);
get(url, null);
}
public WxMenu menuGet() throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + wxCpConfigStorage.getAgentId();
try {
String resultContent = execute(new SimpleGetRequestExecutor(), url, null);
String resultContent = get(url, null);
return WxMenu.fromJson(resultContent);
} catch (WxErrorException e) {
// 46003 不存在的菜单数据
@@ -178,17 +178,17 @@ public class WxCpServiceImpl implements WxCpService {
public void departUpdate(WxCpDepart group) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update";
execute(new SimplePostRequestExecutor(), url, group.toJson());
post(url, group.toJson());
}
public void departDelete(Integer departId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId;
execute(new SimpleGetRequestExecutor(), url, null);
get(url, null);
}
public List<WxCpDepart> departGet() throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list";
String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
String responseContent = get(url, null);
/*
* 操蛋的微信API创建时返回的是 { group : { id : ..., name : ...} }
* 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] }
@@ -204,28 +204,62 @@ public class WxCpServiceImpl implements WxCpService {
@Override
public void userCreate(WxCpUser user) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create";
execute(new SimplePostRequestExecutor(), url, user.toJson());
post(url, user.toJson());
}
@Override
public void userUpdate(WxCpUser user) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update";
execute(new SimplePostRequestExecutor(), url, user.toJson());
post(url, user.toJson());
}
@Override
public void userDelete(String userid) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userid;
execute(new SimpleGetRequestExecutor(), url, null);
get(url, null);
}
@Override
public void userDelete(String[] userids) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete";
JsonObject jsonObject = new JsonObject();
JsonArray jsonArray = new JsonArray();
for (int i = 0; i < userids.length; i++) {
jsonArray.add(new JsonPrimitive(userids[i]));
}
jsonObject.add("useridlist", jsonArray);
post(url, jsonObject.toString());
}
@Override
public WxCpUser userGet(String userid) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid;
String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
String responseContent = get(url, null);
return WxCpUser.fromJson(responseContent);
}
@Override
public List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId;
String params = "";
if (fetchChild != null) {
params += "&fetch_child=" + (fetchChild ? "1" : "0");
}
if (status != null) {
params += "&status=" + status;
} else {
params += "&status=0";
}
String responseContent = get(url, params);
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
return WxCpGsonBuilder.INSTANCE.create()
.fromJson(
tmpJsonElement.getAsJsonObject().get("userlist"),
new TypeToken<List<WxCpUser>>() { }.getType()
);
}
@Override
public List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId;
@@ -239,7 +273,7 @@ public class WxCpServiceImpl implements WxCpService {
params += "&status=0";
}
String responseContent = execute(new SimpleGetRequestExecutor(), url, params);
String responseContent = get(url, params);
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
return WxCpGsonBuilder.INSTANCE.create()
.fromJson(
@@ -253,7 +287,7 @@ public class WxCpServiceImpl implements WxCpService {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create";
JsonObject o = new JsonObject();
o.addProperty("tagname", tagName);
String responseContent = execute(new SimplePostRequestExecutor(), url, o.toString());
String responseContent = post(url, o.toString());
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
return tmpJsonElement.getAsJsonObject().get("tagid").getAsString();
}
@@ -264,19 +298,19 @@ public class WxCpServiceImpl implements WxCpService {
JsonObject o = new JsonObject();
o.addProperty("tagid", tagId);
o.addProperty("tagname", tagName);
execute(new SimplePostRequestExecutor(), url, o.toString());
post(url, o.toString());
}
@Override
public void tagDelete(String tagId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId;
execute(new SimpleGetRequestExecutor(), url, null);
get(url, null);
}
@Override
public List<WxCpTag> tagGet() throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list";
String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
String responseContent = get(url, null);
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
return WxCpGsonBuilder.INSTANCE.create()
.fromJson(
@@ -288,7 +322,7 @@ public class WxCpServiceImpl implements WxCpService {
@Override
public List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId;
String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
String responseContent = get(url, null);
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
return WxCpGsonBuilder.INSTANCE.create()
.fromJson(
@@ -307,7 +341,7 @@ public class WxCpServiceImpl implements WxCpService {
jsonArray.add(new JsonPrimitive(userId));
}
jsonObject.add("userlist", jsonArray);
execute(new SimplePostRequestExecutor(), url, jsonObject.toString());
post(url, jsonObject.toString());
}
@Override
@@ -320,7 +354,7 @@ public class WxCpServiceImpl implements WxCpService {
jsonArray.add(new JsonPrimitive(userId));
}
jsonObject.add("userlist", jsonArray);
execute(new SimplePostRequestExecutor(), url, jsonObject.toString());
post(url, jsonObject.toString());
}
@Override
@@ -357,6 +391,31 @@ public class WxCpServiceImpl implements WxCpService {
}
}
@Override
public int invite(String userId, String inviteTips) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/invite/send";
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("userid", userId);
if (StringUtils.isNotEmpty(inviteTips)) {
jsonObject.addProperty("invite_tips", inviteTips);
}
String responseContent = post(url, jsonObject.toString());
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
return tmpJsonElement.getAsJsonObject().get("type").getAsInt();
}
@Override
public String[] getCallbackIp() throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip";
String responseContent = get(url, null);
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray();
String[] ips = new String[jsonArray.size()];
for(int i = 0; i < jsonArray.size(); i++) {
ips[i] = jsonArray.get(i).getAsString();
}
return ips;
}
public String get(String url, String queryParam) throws WxErrorException {
return execute(new SimpleGetRequestExecutor(), url, queryParam);

View File

@@ -489,6 +489,45 @@ public class WxCpXmlMessage {
this.sendLocationInfo = sendLocationInfo;
}
@Override
public String toString() {
return "WxCpXmlMessage{" +
"agentId=" + agentId +
", toUserName='" + toUserName + '\'' +
", fromUserName='" + fromUserName + '\'' +
", createTime=" + createTime +
", msgType='" + msgType + '\'' +
", content='" + content + '\'' +
", msgId=" + msgId +
", picUrl='" + picUrl + '\'' +
", mediaId='" + mediaId + '\'' +
", format='" + format + '\'' +
", thumbMediaId='" + thumbMediaId + '\'' +
", locationX=" + locationX +
", locationY=" + locationY +
", scale=" + scale +
", label='" + label + '\'' +
", title='" + title + '\'' +
", description='" + description + '\'' +
", url='" + url + '\'' +
", event='" + event + '\'' +
", eventKey='" + eventKey + '\'' +
", ticket='" + ticket + '\'' +
", latitude=" + latitude +
", longitude=" + longitude +
", precision=" + precision +
", recognition='" + recognition + '\'' +
", status='" + status + '\'' +
", totalCount=" + totalCount +
", filterCount=" + filterCount +
", sentCount=" + sentCount +
", errorCount=" + errorCount +
", scanCodeInfo=" + scanCodeInfo +
", sendPicsInfo=" + sendPicsInfo +
", sendLocationInfo=" + sendLocationInfo +
'}';
}
@XStreamAlias("ScanCodeInfo")
public static class ScanCodeInfo {

View File

@@ -42,6 +42,12 @@ public class WxCpMessageRouterTest {
.rule().async(async).eventKey("KEY_1").handler(new WxEchoCpMessageHandler(sb, "KEY_1")).end()
.rule().async(async).content("CONTENT_1").handler(new WxEchoCpMessageHandler(sb, "CONTENT_1")).end()
.rule().async(async).rContent(".*bc.*").handler(new WxEchoCpMessageHandler(sb, "abcd")).end()
.rule().async(async).matcher(new WxCpMessageMatcher() {
@Override
public boolean match(WxCpXmlMessage message) {
return "strangeformat".equals(message.getFormat());
}
}).handler(new WxEchoCpMessageHandler(sb, "matcher")).end()
.rule().async(async).handler(new WxEchoCpMessageHandler(sb, "ALL")).end();
;
}
@@ -92,6 +98,7 @@ public class WxCpMessageRouterTest {
Thread.sleep(1000l * 2);
}
@DataProvider(name="messages-1")
public Object[][] messages2() {
WxCpXmlMessage message1 = new WxCpXmlMessage();
@@ -109,9 +116,12 @@ public class WxCpMessageRouterTest {
WxCpXmlMessage message5 = new WxCpXmlMessage();
message5.setContent("BLA");
WxCpXmlMessage message6 = new WxCpXmlMessage();
WxCpXmlMessage message6 = new WxCpXmlMessage();
message6.setContent("abcd");
WxCpXmlMessage message7 = new WxCpXmlMessage();
message7.setFormat("strangeformat");
WxCpXmlMessage c2 = new WxCpXmlMessage();
c2.setMsgType(WxConsts.XML_MSG_TEXT);
c2.setEvent(WxConsts.EVT_CLICK);
@@ -126,7 +136,8 @@ public class WxCpMessageRouterTest {
c4.setEvent(WxConsts.EVT_CLICK);
c4.setEventKey("KEY_1");
c4.setContent("CONTENT_1");
return new Object[][] {
new Object[] { message1, WxConsts.XML_MSG_TEXT + "," },
new Object[] { message2, WxConsts.EVT_CLICK + "," },
@@ -134,6 +145,7 @@ public class WxCpMessageRouterTest {
new Object[] { message4, "CONTENT_1," },
new Object[] { message5, "ALL," },
new Object[] { message6, "abcd," },
new Object[] { message7, "matcher," },
new Object[] { c2, "COMBINE_2," },
new Object[] { c3, "COMBINE_3," },
new Object[] { c4, "COMBINE_4," }
@@ -290,7 +302,6 @@ public class WxCpMessageRouterTest {
return null;
}
}
}

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
</parent>
<artifactId>weixin-java-mp</artifactId>
<name>WeiXin Java Tools - MP</name>

View File

@@ -9,24 +9,22 @@ import me.chanjar.weixin.common.bean.WxAccessToken;
*/
public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
private static final long l = 7000 * 1000l;
protected volatile String appId;
protected volatile String secret;
protected volatile String token;
protected volatile String accessToken;
protected volatile String aesKey;
protected volatile long expiresTime;
protected String appId;
protected String secret;
protected String token;
protected String accessToken;
protected String aesKey;
protected long expiresTime;
protected volatile String oauth2redirectUri;
protected String oauth2redirectUri;
protected volatile String http_proxy_host;
protected volatile int http_proxy_port;
protected volatile String http_proxy_username;
protected volatile String http_proxy_password;
protected String http_proxy_host;
protected int http_proxy_port;
protected String http_proxy_username;
protected String http_proxy_password;
protected String jsapiTicket;
protected long jsapiTicketExpiresTime;
protected volatile String jsapiTicket;
protected volatile long jsapiTicketExpiresTime;
public String getAccessToken() {
return this.accessToken;

View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
@@ -23,6 +24,6 @@ public interface WxMpMessageHandler {
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context,
WxMpService wxMpService,
WxSessionManager sessionManager);
WxSessionManager sessionManager) throws WxErrorException;
}

View File

@@ -1,5 +1,6 @@
package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
@@ -24,6 +25,6 @@ public interface WxMpMessageInterceptor {
public boolean intercept(WxMpXmlMessage wxMessage,
Map<String, Object> context,
WxMpService wxMpService,
WxSessionManager sessionManager);
WxSessionManager sessionManager) throws WxErrorException;
}

View File

@@ -0,0 +1,17 @@
package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
/**
* 消息匹配器,用在消息路由的时候
*/
public interface WxMpMessageMatcher {
/**
* 消息是否匹配某种模式
* @param message
* @return
*/
public boolean match(WxMpXmlMessage message);
}

View File

@@ -1,7 +1,11 @@
package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.session.*;
import me.chanjar.weixin.common.session.InternalSession;
import me.chanjar.weixin.common.session.InternalSessionManager;
import me.chanjar.weixin.common.session.StandardSessionManager;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.LogExceptionHandler;
import me.chanjar.weixin.common.util.WxErrorExceptionHandler;
import me.chanjar.weixin.common.util.WxMessageDuplicateChecker;
import me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
@@ -10,14 +14,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
/**
* <pre>
@@ -25,8 +26,8 @@ import java.util.regex.Pattern;
*
* 说明:
* 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
* 2. 默认情况下消息只会被处理一次,除非使用 {@link Rule#next()}
* 3. 规则的结束必须用{@link Rule#end()}或者{@link Rule#next()},否则不会生效
* 2. 默认情况下消息只会被处理一次,除非使用 {@link WxMpMessageRouterRule#next()}
* 3. 规则的结束必须用{@link WxMpMessageRouterRule#end()}或者{@link WxMpMessageRouterRule#next()},否则不会生效
*
* 使用方法:
* WxMpMessageRouter router = new WxMpMessageRouter();
@@ -44,6 +45,8 @@ import java.util.regex.Pattern;
* router.route(message);
*
* </pre>
* @author Daniel Qian
*
*/
public class WxMpMessageRouter {
@@ -51,7 +54,7 @@ public class WxMpMessageRouter {
private static final int DEFAULT_THREAD_POOL_SIZE = 100;
private final List<Rule> rules = new ArrayList<Rule>();
private final List<WxMpMessageRouterRule> rules = new ArrayList<WxMpMessageRouterRule>();
private final WxMpService wxMpService;
@@ -61,11 +64,14 @@ public class WxMpMessageRouter {
private WxSessionManager sessionManager;
private WxErrorExceptionHandler exceptionHandler;
public WxMpMessageRouter(WxMpService wxMpService) {
this.wxMpService = wxMpService;
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker();
this.sessionManager = new StandardSessionManager();
this.exceptionHandler = new LogExceptionHandler();
}
/**
@@ -93,7 +99,7 @@ public class WxMpMessageRouter {
/**
* <pre>
* 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager}
* 如果不调用该方法,默认使用 {@linke SessionManagerImpl}
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager}
* </pre>
* @param sessionManager
*/
@@ -101,12 +107,27 @@ public class WxMpMessageRouter {
this.sessionManager = sessionManager;
}
/**
* <pre>
* 设置自定义的{@link me.chanjar.weixin.common.util.WxErrorExceptionHandler}
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler}
* </pre>
* @param exceptionHandler
*/
public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
List<WxMpMessageRouterRule> getRules() {
return this.rules;
}
/**
* 开始一个新的Route规则
* @return
*/
public Rule rule() {
return new Rule(this, wxMpService, sessionManager);
public WxMpMessageRouterRule rule() {
return new WxMpMessageRouterRule(this);
}
/**
@@ -119,12 +140,12 @@ public class WxMpMessageRouter {
return null;
}
final List<Rule> matchRules = new ArrayList<Rule>();
final List<WxMpMessageRouterRule> matchRules = new ArrayList<WxMpMessageRouterRule>();
// 收集匹配的规则
for (final Rule rule : rules) {
for (final WxMpMessageRouterRule rule : rules) {
if (rule.test(wxMessage)) {
matchRules.add(rule);
if(!rule.reEnter) {
if(!rule.isReEnter()) {
break;
}
}
@@ -136,18 +157,18 @@ public class WxMpMessageRouter {
WxMpXmlOutMessage res = null;
final List<Future> futures = new ArrayList<Future>();
for (final Rule rule : matchRules) {
for (final WxMpMessageRouterRule rule : matchRules) {
// 返回最后一个非异步的rule的执行结果
if(rule.async) {
if(rule.isAsync()) {
futures.add(
executorService.submit(new Runnable() {
public void run() {
rule.service(wxMessage);
rule.service(wxMessage, wxMpService, sessionManager, exceptionHandler);
}
})
);
} else {
res = rule.service(wxMessage);
res = rule.service(wxMessage, wxMpService, sessionManager, exceptionHandler);
// 在同步操作结束session访问结束
log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUserName());
sessionEndAccess(wxMessage);
@@ -180,7 +201,10 @@ public class WxMpMessageRouter {
String messageId = "";
if (wxMessage.getMsgId() == null) {
messageId = wxMessage.getFromUserName() + "-" + String.valueOf(wxMessage.getCreateTime());
messageId = String.valueOf(wxMessage.getCreateTime())
+ "-" + wxMessage.getFromUserName()
+ "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEvent());
;
} else {
messageId = String.valueOf(wxMessage.getMsgId());
}
@@ -204,218 +228,4 @@ public class WxMpMessageRouter {
}
}
public static class Rule {
private final WxMpMessageRouter routerBuilder;
private final WxMpService wxMpService;
private final WxSessionManager sessionManager;
private boolean async = true;
private String fromUser;
private String msgType;
private String event;
private String eventKey;
private String content;
private String rContent;
private boolean reEnter = false;
private List<WxMpMessageHandler> handlers = new ArrayList<WxMpMessageHandler>();
private List<WxMpMessageInterceptor> interceptors = new ArrayList<WxMpMessageInterceptor>();
protected Rule(WxMpMessageRouter routerBuilder, WxMpService wxMpService, WxSessionManager sessionManager) {
this.routerBuilder = routerBuilder;
this.wxMpService = wxMpService;
this.sessionManager = sessionManager;
}
/**
* 设置是否异步执行默认是true
* @param async
* @return
*/
public Rule async(boolean async) {
this.async = async;
return this;
}
/**
* 如果msgType等于某值
* @param msgType
* @return
*/
public Rule msgType(String msgType) {
this.msgType = msgType;
return this;
}
/**
* 如果event等于某值
* @param event
* @return
*/
public Rule event(String event) {
this.event = event;
return this;
}
/**
* 如果eventKey等于某值
* @param eventKey
* @return
*/
public Rule eventKey(String eventKey) {
this.eventKey = eventKey;
return this;
}
/**
* 如果content等于某值
* @param content
* @return
*/
public Rule content(String content) {
this.content = content;
return this;
}
/**
* 如果content匹配该正则表达式
* @param regex
* @return
*/
public Rule rContent(String regex) {
this.rContent = regex;
return this;
}
/**
* 如果fromUser等于某值
* @param fromUser
* @return
*/
public Rule fromUser(String fromUser) {
this.fromUser = fromUser;
return this;
}
/**
* 设置微信消息拦截器
* @param interceptor
* @return
*/
public Rule interceptor(WxMpMessageInterceptor interceptor) {
return interceptor(interceptor, (WxMpMessageInterceptor[]) null);
}
/**
* 设置微信消息拦截器
* @param interceptor
* @param otherInterceptors
* @return
*/
public Rule interceptor(WxMpMessageInterceptor interceptor, WxMpMessageInterceptor... otherInterceptors) {
this.interceptors.add(interceptor);
if (otherInterceptors != null && otherInterceptors.length > 0) {
for (WxMpMessageInterceptor i : otherInterceptors) {
this.interceptors.add(i);
}
}
return this;
}
/**
* 设置微信消息处理器
* @param handler
* @return
*/
public Rule handler(WxMpMessageHandler handler) {
return handler(handler, (WxMpMessageHandler[]) null);
}
/**
* 设置微信消息处理器
* @param handler
* @param otherHandlers
* @return
*/
public Rule handler(WxMpMessageHandler handler, WxMpMessageHandler... otherHandlers) {
this.handlers.add(handler);
if (otherHandlers != null && otherHandlers.length > 0) {
for (WxMpMessageHandler i : otherHandlers) {
this.handlers.add(i);
}
}
return this;
}
/**
* 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则
* @return
*/
public WxMpMessageRouter end() {
this.routerBuilder.rules.add(this);
return this.routerBuilder;
}
/**
* 规则结束,但是消息还会进入其他规则
* @return
*/
public WxMpMessageRouter next() {
this.reEnter = true;
return end();
}
protected boolean test(WxMpXmlMessage wxMessage) {
return
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
&&
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
&&
(this.event == null || this.event.equals(wxMessage.getEvent()))
&&
(this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey()))
&&
(this.content == null || this.content.equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim()))
&&
(this.rContent == null || Pattern.matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim()))
;
}
/**
* 处理微信推送过来的消息
* @param wxMessage
* @return true 代表继续执行别的routerfalse 代表停止执行别的router
*/
protected WxMpXmlOutMessage service(WxMpXmlMessage wxMessage) {
Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过
for (WxMpMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context, wxMpService, sessionManager)) {
return null;
}
}
// 交给handler处理
WxMpXmlOutMessage res = null;
for (WxMpMessageHandler handler : this.handlers) {
// 返回最后handler的结果
res = handler.handle(wxMessage, context, wxMpService, sessionManager);
}
return res;
}
}
}

View File

@@ -0,0 +1,353 @@
package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.WxErrorExceptionHandler;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class WxMpMessageRouterRule {
private final WxMpMessageRouter routerBuilder;
private boolean async = true;
private String fromUser;
private String msgType;
private String event;
private String eventKey;
private String content;
private String rContent;
private WxMpMessageMatcher matcher;
private boolean reEnter = false;
private List<WxMpMessageHandler> handlers = new ArrayList<WxMpMessageHandler>();
private List<WxMpMessageInterceptor> interceptors = new ArrayList<WxMpMessageInterceptor>();
public WxMpMessageRouterRule(WxMpMessageRouter routerBuilder) {
this.routerBuilder = routerBuilder;
}
/**
* 设置是否异步执行默认是true
*
* @param async
* @return
*/
public WxMpMessageRouterRule async(boolean async) {
this.async = async;
return this;
}
/**
* 如果msgType等于某值
*
* @param msgType
* @return
*/
public WxMpMessageRouterRule msgType(String msgType) {
this.msgType = msgType;
return this;
}
/**
* 如果event等于某值
*
* @param event
* @return
*/
public WxMpMessageRouterRule event(String event) {
this.event = event;
return this;
}
/**
* 如果eventKey等于某值
*
* @param eventKey
* @return
*/
public WxMpMessageRouterRule eventKey(String eventKey) {
this.eventKey = eventKey;
return this;
}
/**
* 如果content等于某值
*
* @param content
* @return
*/
public WxMpMessageRouterRule content(String content) {
this.content = content;
return this;
}
/**
* 如果content匹配该正则表达式
*
* @param regex
* @return
*/
public WxMpMessageRouterRule rContent(String regex) {
this.rContent = regex;
return this;
}
/**
* 如果fromUser等于某值
*
* @param fromUser
* @return
*/
public WxMpMessageRouterRule fromUser(String fromUser) {
this.fromUser = fromUser;
return this;
}
/**
* 如果消息匹配某个matcher用在用户需要自定义更复杂的匹配规则的时候
*
* @param matcher
* @return
*/
public WxMpMessageRouterRule matcher(WxMpMessageMatcher matcher) {
this.matcher = matcher;
return this;
}
/**
* 设置微信消息拦截器
*
* @param interceptor
* @return
*/
public WxMpMessageRouterRule interceptor(WxMpMessageInterceptor interceptor) {
return interceptor(interceptor, (WxMpMessageInterceptor[]) null);
}
/**
* 设置微信消息拦截器
*
* @param interceptor
* @param otherInterceptors
* @return
*/
public WxMpMessageRouterRule interceptor(WxMpMessageInterceptor interceptor, WxMpMessageInterceptor... otherInterceptors) {
this.interceptors.add(interceptor);
if (otherInterceptors != null && otherInterceptors.length > 0) {
for (WxMpMessageInterceptor i : otherInterceptors) {
this.interceptors.add(i);
}
}
return this;
}
/**
* 设置微信消息处理器
*
* @param handler
* @return
*/
public WxMpMessageRouterRule handler(WxMpMessageHandler handler) {
return handler(handler, (WxMpMessageHandler[]) null);
}
/**
* 设置微信消息处理器
*
* @param handler
* @param otherHandlers
* @return
*/
public WxMpMessageRouterRule handler(WxMpMessageHandler handler, WxMpMessageHandler... otherHandlers) {
this.handlers.add(handler);
if (otherHandlers != null && otherHandlers.length > 0) {
for (WxMpMessageHandler i : otherHandlers) {
this.handlers.add(i);
}
}
return this;
}
/**
* 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则
*
* @return
*/
public WxMpMessageRouter end() {
this.routerBuilder.getRules().add(this);
return this.routerBuilder;
}
/**
* 规则结束,但是消息还会进入其他规则
*
* @return
*/
public WxMpMessageRouter next() {
this.reEnter = true;
return end();
}
protected boolean test(WxMpXmlMessage wxMessage) {
return
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
&&
(this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
&&
(this.event == null || this.event.equals(wxMessage.getEvent()))
&&
(this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey()))
&&
(this.content == null || this.content
.equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim()))
&&
(this.rContent == null || Pattern
.matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim()))
&&
(this.matcher == null || this.matcher.match(wxMessage))
;
}
/**
* 处理微信推送过来的消息
*
* @param wxMessage
* @return true 代表继续执行别的routerfalse 代表停止执行别的router
*/
protected WxMpXmlOutMessage service(WxMpXmlMessage wxMessage,
WxMpService wxMpService,
WxSessionManager sessionManager,
WxErrorExceptionHandler exceptionHandler) {
try {
Map<String, Object> context = new HashMap<String, Object>();
// 如果拦截器不通过
for (WxMpMessageInterceptor interceptor : this.interceptors) {
if (!interceptor.intercept(wxMessage, context, wxMpService, sessionManager)) {
return null;
}
}
// 交给handler处理
WxMpXmlOutMessage res = null;
for (WxMpMessageHandler handler : this.handlers) {
// 返回最后handler的结果
res = handler.handle(wxMessage, context, wxMpService, sessionManager);
}
return res;
} catch (WxErrorException e) {
exceptionHandler.handle(e);
}
return null;
}
public WxMpMessageRouter getRouterBuilder() {
return routerBuilder;
}
public boolean isAsync() {
return async;
}
public void setAsync(boolean async) {
this.async = async;
}
public String getFromUser() {
return fromUser;
}
public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}
public String getMsgType() {
return msgType;
}
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
public String getEventKey() {
return eventKey;
}
public void setEventKey(String eventKey) {
this.eventKey = eventKey;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getrContent() {
return rContent;
}
public void setrContent(String rContent) {
this.rContent = rContent;
}
public WxMpMessageMatcher getMatcher() {
return matcher;
}
public void setMatcher(WxMpMessageMatcher matcher) {
this.matcher = matcher;
}
public boolean isReEnter() {
return reEnter;
}
public void setReEnter(boolean reEnter) {
this.reEnter = reEnter;
}
public List<WxMpMessageHandler> getHandlers() {
return handlers;
}
public void setHandlers(List<WxMpMessageHandler> handlers) {
this.handlers = handlers;
}
public List<WxMpMessageInterceptor> getInterceptors() {
return interceptors;
}
public void setInterceptors(List<WxMpMessageInterceptor> interceptors) {
this.interceptors = interceptors;
}
}

View File

@@ -83,12 +83,10 @@ public interface WxMpService {
*
* 详情请见http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
* </pre>
* @param timestamp 时间戳
* @param noncestr 用户自己生成的随机字符串
* @param url url
* @return
*/
public String createJsapiSignature(long timestamp, String noncestr, String url) throws WxErrorException;
public WxMpJsapiSignature createJsapiSignature(String url) throws WxErrorException;
/**
* <pre>

View File

@@ -42,10 +42,15 @@ import java.io.InputStream;
import java.io.StringReader;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Random;
import java.util.UUID;
public class WxMpServiceImpl implements WxMpService {
protected final String RANDOM_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
protected final Random RANDOM = new Random();
protected final Logger log = LoggerFactory.getLogger(WxMpServiceImpl.class);
/**
@@ -59,7 +64,7 @@ public class WxMpServiceImpl implements WxMpService {
protected final Object globalJsapiTicketRefreshLock = new Object();
protected WxMpConfigStorage wxMpConfigStorage;
protected CloseableHttpClient httpClient;
protected HttpHost httpProxy;
@@ -143,20 +148,36 @@ public class WxMpServiceImpl implements WxMpService {
return wxMpConfigStorage.getJsapiTicket();
}
public String createJsapiSignature(long timestamp, String noncestr, String url) throws WxErrorException {
public WxMpJsapiSignature createJsapiSignature(String url) throws WxErrorException {
long timestamp = System.currentTimeMillis() / 1000;
String noncestr = getRandomStr();
String jsapiTicket = getJsapiTicket(false);
try {
return SHA1.genWithAmple(
String signature = SHA1.genWithAmple(
"jsapi_ticket=" + jsapiTicket,
"noncestr=" + noncestr,
"timestamp=" + timestamp,
"url=" + url
);
WxMpJsapiSignature jsapiSignature = new WxMpJsapiSignature();
jsapiSignature.setTimestamp(timestamp);
jsapiSignature.setNoncestr(noncestr);
jsapiSignature.setUrl(url);
jsapiSignature.setSignature(signature);
return jsapiSignature;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
protected String getRandomStr() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 16; i++) {
sb.append(RANDOM_STR.charAt(RANDOM.nextInt(RANDOM_STR.length())));
}
return sb.toString();
}
public void customMessageSend(WxMpCustomMessage message) throws WxErrorException {
String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
execute(new SimplePostRequestExecutor(), url, message.toJson());

View File

@@ -619,4 +619,41 @@ public class WxMpXmlMessage {
}
}
@Override
public String toString() {
return "WxMpXmlMessage{" +
"toUserName='" + toUserName + '\'' +
", fromUserName='" + fromUserName + '\'' +
", createTime=" + createTime +
", msgType='" + msgType + '\'' +
", content='" + content + '\'' +
", msgId=" + msgId +
", picUrl='" + picUrl + '\'' +
", mediaId='" + mediaId + '\'' +
", format='" + format + '\'' +
", thumbMediaId='" + thumbMediaId + '\'' +
", locationX=" + locationX +
", locationY=" + locationY +
", scale=" + scale +
", label='" + label + '\'' +
", title='" + title + '\'' +
", description='" + description + '\'' +
", url='" + url + '\'' +
", event='" + event + '\'' +
", eventKey='" + eventKey + '\'' +
", ticket='" + ticket + '\'' +
", latitude=" + latitude +
", longitude=" + longitude +
", precision=" + precision +
", recognition='" + recognition + '\'' +
", status='" + status + '\'' +
", totalCount=" + totalCount +
", filterCount=" + filterCount +
", sentCount=" + sentCount +
", errorCount=" + errorCount +
", scanCodeInfo=" + scanCodeInfo +
", sendPicsInfo=" + sendPicsInfo +
", sendLocationInfo=" + sendLocationInfo +
'}';
}
}

View File

@@ -0,0 +1,58 @@
package me.chanjar.weixin.mp.bean.result;
/**
* jspai signature
*/
public class WxMpJsapiSignature {
private String noncestr;
private String jsapiTicket;
private long timestamp;
private String url;
private String signature;
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
public String getNoncestr() {
return noncestr;
}
public void setNoncestr(String noncestr) {
this.noncestr = noncestr;
}
public String getJsapiTicket() {
return jsapiTicket;
}
public void setJsapiTicket(String jsapiTicket) {
this.jsapiTicket = jsapiTicket;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@@ -42,6 +42,12 @@ public class WxMpMessageRouterTest {
.rule().async(async).eventKey("KEY_1").handler(new WxEchoMpMessageHandler(sb, "KEY_1")).end()
.rule().async(async).content("CONTENT_1").handler(new WxEchoMpMessageHandler(sb, "CONTENT_1")).end()
.rule().async(async).rContent(".*bc.*").handler(new WxEchoMpMessageHandler(sb, "abcd")).end()
.rule().async(async).matcher(new WxMpMessageMatcher() {
@Override
public boolean match(WxMpXmlMessage message) {
return "strangeformat".equals(message.getFormat());
}
}).handler(new WxEchoMpMessageHandler(sb, "matcher")).end()
.rule().async(async).handler(new WxEchoMpMessageHandler(sb, "ALL")).end();
;
}
@@ -109,9 +115,12 @@ public class WxMpMessageRouterTest {
WxMpXmlMessage message5 = new WxMpXmlMessage();
message5.setContent("BLA");
WxMpXmlMessage message6 = new WxMpXmlMessage();
WxMpXmlMessage message6 = new WxMpXmlMessage();
message6.setContent("abcd");
WxMpXmlMessage message7 = new WxMpXmlMessage();
message7.setFormat("strangeformat");
WxMpXmlMessage c2 = new WxMpXmlMessage();
c2.setMsgType(WxConsts.XML_MSG_TEXT);
c2.setEvent(WxConsts.EVT_CLICK);
@@ -134,6 +143,7 @@ public class WxMpMessageRouterTest {
new Object[] { message4, "CONTENT_1," },
new Object[] { message5, "ALL," },
new Object[] { message6, "abcd," },
new Object[] { message7, "matcher," },
new Object[] { c2, "COMBINE_2," },
new Object[] { c3, "COMBINE_3," },
new Object[] { c4, "COMBINE_4," }

View File

@@ -0,0 +1,115 @@
package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSession;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpMessageMatcher;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.WxMpCustomMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import java.util.Map;
import java.util.Random;
import java.util.regex.Pattern;
public class DemoGuessNumberHandler implements WxMpMessageHandler, WxMpMessageMatcher {
private Random random = new Random();
private Pattern pattern = Pattern.compile("\\d+");
@Override
public boolean match(WxMpXmlMessage message) {
return isUserWantGuess(message) || isUserAnswering(message);
}
private boolean isUserWantGuess(WxMpXmlMessage message) {
return "猜数字".equals(message.getContent());
}
private boolean isUserAnswering(WxMpXmlMessage message) {
return pattern.matcher(message.getContent()).matches();
}
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) throws WxErrorException {
if (isUserWantGuess(wxMessage)) {
letsGo(wxMessage, wxMpService, sessionManager);
}
if (isUserAnswering(wxMessage)) {
giveHint(wxMessage, wxMpService, sessionManager);
}
return null;
}
protected void letsGo(WxMpXmlMessage wxMessage, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
WxSession session = sessionManager.getSession(wxMessage.getFromUserName());
if (session.getAttribute("guessing") == null) {
WxMpCustomMessage m = WxMpCustomMessage
.TEXT()
.toUser(wxMessage.getFromUserName())
.content("请猜一个100以内的数字")
.build();
wxMpService.customMessageSend(m);
} else {
WxMpCustomMessage m = WxMpCustomMessage
.TEXT()
.toUser(wxMessage.getFromUserName())
.content("放弃了吗那请重新猜一个100以内的数字")
.build();
wxMpService.customMessageSend(m);
}
session.setAttribute("guessing", Boolean.TRUE);
session.setAttribute("number", random.nextInt(100));
}
protected void giveHint(WxMpXmlMessage wxMessage, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException {
WxSession session = sessionManager.getSession(wxMessage.getFromUserName());
if (session.getAttribute("guessing") == null) {
return;
}
boolean guessing = (Boolean) session.getAttribute("guessing");
if (!guessing) {
return;
}
int answer = (Integer) session.getAttribute("number");
int guessNumber = Integer.valueOf(wxMessage.getContent());
if (guessNumber < answer) {
WxMpCustomMessage m = WxMpCustomMessage
.TEXT()
.toUser(wxMessage.getFromUserName())
.content("小了")
.build();
wxMpService.customMessageSend(m);
} else if (guessNumber > answer) {
WxMpCustomMessage m = WxMpCustomMessage
.TEXT()
.toUser(wxMessage.getFromUserName())
.content("大了")
.build();
wxMpService.customMessageSend(m);
} else {
WxMpCustomMessage m = WxMpCustomMessage
.TEXT()
.toUser(wxMessage.getFromUserName())
.content("Bingo!")
.build();
session.removeAttribute("guessing");
wxMpService.customMessageSend(m);
}
}
}

View File

@@ -0,0 +1,38 @@
package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import java.io.IOException;
import java.util.Map;
public class DemoImageHandler implements WxMpMessageHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService wxMpService, WxSessionManager sessionManager) {
try {
WxMediaUploadResult wxMediaUploadResult = wxMpService
.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg"));
WxMpXmlOutImageMessage m
= WxMpXmlOutMessage
.IMAGE()
.mediaId(wxMediaUploadResult.getMediaId())
.fromUser(wxMessage.getToUserName())
.toUser(wxMessage.getFromUserName())
.build();
return m;
} catch (WxErrorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,21 @@
package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import java.util.Map;
/**
* Created by qianjia on 15/1/22.
*/
public class DemoLogHandler implements WxMpMessageHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
System.out.println(wxMessage.toString());
return null;
}
}

View File

@@ -0,0 +1,27 @@
package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import java.util.Map;
/**
* Created by qianjia on 15/1/22.
*/
public class DemoOAuth2Handler implements WxMpMessageHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService wxMpService, WxSessionManager sessionManager) {
String href = "<a href=\"" + wxMpService.oauth2buildAuthorizationUrl(WxConsts.OAUTH2_SCOPE_USER_INFO, null)
+ "\">测试oauth2</a>";
return WxMpXmlOutMessage
.TEXT()
.content(href)
.fromUser(wxMessage.getToUserName())
.toUser(wxMessage.getFromUserName()).build();
}
}

View File

@@ -0,0 +1,25 @@
package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage;
import java.util.Map;
/**
* Created by qianjia on 15/1/22.
*/
public class DemoTextHandler implements WxMpMessageHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService wxMpService, WxSessionManager sessionManager) {
WxMpXmlOutTextMessage m
= WxMpXmlOutMessage.TEXT().content("测试加密消息").fromUser(wxMessage.getToUserName())
.toUser(wxMessage.getFromUserName()).build();
return m;
}
}

View File

@@ -1,21 +1,12 @@
package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.*;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
public class WxMpDemoServer {
@@ -42,79 +33,26 @@ public class WxMpDemoServer {
}
private static void initWeixin() {
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml");
WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage.fromXml(is1);
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml");
WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage.fromXml(is1);
wxMpConfigStorage = config;
wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(config);
wxMpConfigStorage = config;
wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(config);
WxMpMessageHandler textHandler = new WxMpMessageHandler() {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService wxMpService, WxSessionManager sessionManager) {
WxMpXmlOutTextMessage m
= WxMpXmlOutMessage.TEXT().content("测试加密消息").fromUser(wxMessage.getToUserName())
.toUser(wxMessage.getFromUserName()).build();
return m;
}
};
WxMpMessageHandler logHandler = new DemoLogHandler();
WxMpMessageHandler textHandler = new DemoTextHandler();
WxMpMessageHandler imageHandler = new DemoImageHandler();
WxMpMessageHandler oauth2handler = new DemoOAuth2Handler();
DemoGuessNumberHandler guessNumberHandler = new DemoGuessNumberHandler();
WxMpMessageHandler imageHandler = new WxMpMessageHandler() {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService wxMpService, WxSessionManager sessionManager) {
try {
WxMediaUploadResult wxMediaUploadResult = wxMpService
.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg"));
WxMpXmlOutImageMessage m
= WxMpXmlOutMessage
.IMAGE()
.mediaId(wxMediaUploadResult.getMediaId())
.fromUser(wxMessage.getToUserName())
.toUser(wxMessage.getFromUserName())
.build();
return m;
} catch (WxErrorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
WxMpMessageHandler oauth2handler = new WxMpMessageHandler() {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService wxMpService, WxSessionManager sessionManager) {
String href = "<a href=\"" + wxMpService.oauth2buildAuthorizationUrl(WxConsts.OAUTH2_SCOPE_USER_INFO, null)
+ "\">测试oauth2</a>";
return WxMpXmlOutMessage
.TEXT()
.content(href)
.fromUser(wxMessage.getToUserName())
.toUser(wxMessage.getFromUserName()).build();
}
};
wxMpMessageRouter = new WxMpMessageRouter(wxMpService);
wxMpMessageRouter = new WxMpMessageRouter(wxMpService);
wxMpMessageRouter
.rule()
.async(false)
.content("哈哈") // 拦截内容为“哈哈”的消息
.handler(textHandler)
.end()
.rule()
.async(false)
.content("图片")
.handler(imageHandler)
.end()
.rule()
.async(false)
.content("oauth")
.handler(oauth2handler)
.end()
.rule().handler(logHandler).next()
.rule().msgType(WxConsts.XML_MSG_TEXT).matcher(guessNumberHandler).handler(guessNumberHandler).end()
.rule().async(false).content("哈哈").handler(textHandler).end()
.rule().async(false).content("图片").handler(imageHandler).end()
.rule().async(false).content("oauth").handler(oauth2handler).end()
;
}

View File

@@ -60,7 +60,9 @@ public class WxMpEndpointServlet extends HttpServlet {
// 明文传输的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream());
WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage);
response.getWriter().write(outMessage.toXml());
if (outMessage != null) {
response.getWriter().write(outMessage.toXml());
}
return;
}