diff --git a/.gitignore b/.gitignore index ba4f93dca..740251bad 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,6 @@ target .project .classpath -src/test/resources/test-config.xml sw-pom.xml *.iml +test-config.xml diff --git a/src/test/resources/testng.xml b/src/test/resources/testng.xml deleted file mode 100644 index 3a0c80aa1..000000000 --- a/src/test/resources/testng.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LICENSE b/weixin-java-enterprise/LICENSE similarity index 100% rename from LICENSE rename to weixin-java-enterprise/LICENSE diff --git a/weixin-java-enterprise/README.md b/weixin-java-enterprise/README.md new file mode 100644 index 000000000..b0a11d316 --- /dev/null +++ b/weixin-java-enterprise/README.md @@ -0,0 +1,56 @@ +weixin-java-tools +=========== + +微信java开发工具集,本项目主要分为两大块:微信消息路由器、微信Java API + +特性列表: + +1. 不基于Servlet、和其他MVC框架,仅作为工具使用,提供更多的灵活性 +2. 详尽的单元测试代码,可以拿来当example用 +3. 详尽的javadoc +4. access token过期自动刷新的功能 +5. 微信服务端繁忙自动重试的功能 +6. 提供微信错误信息的异常处理机制 + + +详细文档请看 [wiki](https://github.com/chanjarster/weixin-java-tools/wiki) + +# Quickstart + +在你的maven项目中添加: +```xml + + me.chanjar + weixin-java-tools + 1.0.2 + +``` + +如果要使用``*-SNAPSHOT``版,则需要在你的``pom.xml``中添加这段: + +```xml + + + + sonatype snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + +``` + +## Hello World +```java +WxConfigStorage config = new WxInMemoryConfigStorage(); +config.setAppId(...); // 设置微信公众号的appid +config.setSecret(...); // 设置微信公众号的app corpSecret +config.setToken(...); // 设置微信公众号的token + +WxServiceImpl wxService = new WxServiceImpl(); +wxService.setWxConfigStorage(config); + +// 用户的openid在下面地址获得 +// https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type=用户管理&form=获取关注者列表接口%20/user/get +String openId = ...; +WxCustomMessage message = WxCustomMessage.TEXT().toUser(openId).content("Hello World").build(); +wxService.customMessageSend(message); +``` diff --git a/weixin-java-enterprise/pom.xml b/weixin-java-enterprise/pom.xml new file mode 100644 index 000000000..204d6916a --- /dev/null +++ b/weixin-java-enterprise/pom.xml @@ -0,0 +1,226 @@ + + + 4.0.0 + me.chanjar + weixin-java-enterprise-tools + 1.0.3-SNAPSHOT + WeiXin Java Enterprise Tools + 微信企业号Java SDK + https://github.com/chanjarster/weixin-java-tools + + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + Daniel Qian + chanjarster@gmail.com + + + + + scm:git:https://github.com/chanjarster/weixin-java-tools.git + scm:git:git@github.com:chanjarster/weixin-java-tools.git + https://github.com/chanjarster/weixin-java-tools + + + + UTF-8 + true + true + 4.3.5 + + + + junit + junit + 4.11 + test + + + org.apache.httpcomponents + fluent-hc + ${httpclient.version} + + + org.apache.httpcomponents + httpmime + ${httpclient.version} + + + javax.xml.bind + jaxb-api + 2.2.7 + + + com.sun.xml.bind + jaxb-impl + 2.2.7 + + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.client + 1.0.0 + + + com.google.code.gson + gson + 2.2.2 + + + org.testng + testng + 6.8.7 + test + + + org.mockito + mockito-all + 1.9.5 + test + + + com.google.inject + guice + 3.0 + test + + + org.apache.commons + commons-lang3 + 3.1 + + + commons-codec + commons-codec + 1.9 + + + commons-io + commons-io + 2.4 + + + org.eclipse.jetty + jetty-server + 9.3.0.M0 + test + + + org.eclipse.jetty + jetty-servlet + 9.3.0.M0 + test + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + release + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + UTF-8 + zh_CN + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.17 + + + src/test/resources/testng.xml + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + ossrh + https://oss.sonatype.org/ + false + + + + org.apache.maven.plugins + maven-release-plugin + 2.5 + + true + false + release + deploy + + + + + + + diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxConsts.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxConsts.java new file mode 100644 index 000000000..d1cefe687 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxConsts.java @@ -0,0 +1,128 @@ +package me.chanjar.weixin.enterprise.api; + +import java.util.HashMap; +import java.util.Map; + +public class WxConsts { + + /////////////////////// + // 微信推送过来的消息的类型,和发送给微信xml格式消息的消息类型 + /////////////////////// + public static final String XML_MSG_TEXT = "text"; + public static final String XML_MSG_IMAGE = "image"; + public static final String XML_MSG_VOICE = "voice"; + public static final String XML_MSG_VIDEO = "video"; + public static final String XML_MSG_NEWS = "news"; + public static final String XML_MSG_MUSIC = "music"; + public static final String XML_MSG_LOCATION = "location"; + public static final String XML_MSG_LINK = "link"; + public static final String XML_MSG_EVENT = "event"; + public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; + + /////////////////////// + // 客服消息的消息类型 + /////////////////////// + public static final String CUSTOM_MSG_TEXT = "text"; + public static final String CUSTOM_MSG_IMAGE = "image"; + public static final String CUSTOM_MSG_VOICE = "voice"; + public static final String CUSTOM_MSG_VIDEO = "video"; + public static final String CUSTOM_MSG_MUSIC = "music"; + public static final String CUSTOM_MSG_NEWS = "news"; + + /////////////////////// + // 群发消息的消息类型 + /////////////////////// + public static final String MASS_MSG_NEWS = "mpnews"; + public static final String MASS_MSG_TEXT = "text"; + public static final String MASS_MSG_VOICE = "voice"; + public static final String MASS_MSG_IMAGE = "image"; + public static final String MASS_MSG_VIDEO = "mpvideo"; + + /////////////////////// + // 群发消息后微信端推送给服务器的反馈消息 + /////////////////////// + public static final String MASS_ST_SUCCESS = "send success"; + public static final String MASS_ST_FAIL = "send fail"; + public static final String MASS_ST_涉嫌广告 = "err(10001)"; + public static final String MASS_ST_涉嫌政治 = "err(20001)"; + public static final String MASS_ST_涉嫌社会 = "err(20004)"; + public static final String MASS_ST_涉嫌色情 = "err(20002)"; + public static final String MASS_ST_涉嫌违法犯罪 = "err(20006)"; + public static final String MASS_ST_涉嫌欺诈 = "err(20008)"; + public static final String MASS_ST_涉嫌版权 = "err(20013)"; + public static final String MASS_ST_涉嫌互推_互相宣传 = "err(22000)"; + public static final String MASS_ST_涉嫌其他 = "err(21000)"; + + /** + * 群发反馈消息代码所对应的文字描述 + */ + public static final Map MASS_ST_2_DESC = new HashMap(); + static { + MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); + MASS_ST_2_DESC.put(MASS_ST_FAIL, "发送失败"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌广告, "涉嫌广告"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌政治, "涉嫌政治"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌社会, "涉嫌社会"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌色情, "涉嫌色情"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌违法犯罪, "涉嫌违法犯罪"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌欺诈, "涉嫌欺诈"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌版权, "涉嫌版权"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌互推_互相宣传, "涉嫌互推_互相宣传"); + MASS_ST_2_DESC.put(MASS_ST_涉嫌其他, "涉嫌其他"); + } + + /////////////////////// + // 微信端推送过来的事件类型 + /////////////////////// + public static final String EVT_SUBSCRIBE = "subscribe"; + public static final String EVT_UNSUBSCRIBE = "unsubscribe"; + public static final String EVT_SCAN = "SCAN"; + public static final String EVT_LOCATION = "LOCATION"; + public static final String EVT_CLICK = "CLICK"; + public static final String EVT_VIEW = "VIEW"; + public static final String EVT_MASS_SEND_JOB_FINISH = "MASSSENDJOBFINISH"; + public static final String EVT_SCANCODE_PUSH = "scancode_push"; + public static final String EVT_SCANCODE_WAITMSG = "scancode_waitmsg"; + public static final String EVT_PIC_SYSPHOTO = "pic_sysphoto"; + public static final String EVT_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; + public static final String EVT_PIC_WEIXIN = "pic_weixin"; + public static final String EVT_LOCATION_SELECT = "location_select"; + + /////////////////////// + // 上传多媒体文件的类型 + /////////////////////// + public static final String MEDIA_IMAGE = "image"; + public static final String MEDIA_VOICE = "voice"; + public static final String MEDIA_VIDEO = "video"; + public static final String MEDIA_THUMB = "thumb"; + + /////////////////////// + // 文件类型 + /////////////////////// + public static final String FILE_JPG = "jpeg"; + public static final String FILE_MP3 = "mp3"; + public static final String FILE_ARM = "arm"; + public static final String FILE_MP4 = "mp4"; + + + /////////////////////// + // 自定义菜单的按钮类型 + /////////////////////// + /** 点击推事件 */ + public static final String BUTTON_CLICK = "click"; + /** 跳转URL */ + public static final String BUTTON_VIEW = "view"; + /** 扫码推事件 */ + public static final String BUTTON_SCANCODE_PUSH = "scancode_push"; + /** 扫码推事件且弹出“消息接收中”提示框 */ + public static final String BUTTON_SCANCODE_WAITMSG = "scancode_waitmsg"; + /** 弹出系统拍照发图 */ + public static final String PIC_SYSPHOTO = "pic_sysphoto"; + /** 弹出拍照或者相册发图 */ + public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; + /** 弹出微信相册发图器 */ + public static final String PIC_WEIXIN = "pic_weixin"; + /** 弹出地理位置选择器 */ + public static final String LOCATION_SELECT = "location_select"; + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpConfigStorage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpConfigStorage.java new file mode 100644 index 000000000..cefac4a45 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpConfigStorage.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.enterprise.api; + +import me.chanjar.weixin.enterprise.bean.WxAccessToken; + +/** + * 微信客户端配置存储 + * @author Daniel Qian + * + */ +public interface WxCpConfigStorage { + + public void updateAccessToken(WxAccessToken accessToken); + + public void updateAccessToken(String accessToken, int expiresIn); + + public String getAccessToken(); + + public String getCorpId(); + + public String getCorpSecret(); + + public String getAgentId(); + + public String getToken(); + + public String getAesKey(); + + public int getExpiresIn(); + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageHandler.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageHandler.java new file mode 100644 index 000000000..fabf0d293 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageHandler.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.enterprise.api; + +import java.util.Map; + +import me.chanjar.weixin.enterprise.bean.WxCpXmlMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; + +/** + * 处理微信推送消息的处理器接口 + * @author Daniel Qian + * + */ +public interface WxCpMessageHandler { + + /** + * + * @param wxMessage + * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 + * @return xml格式的消息,如果在异步规则里处理的话,可以返回null + */ + public WxXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context); + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageInterceptor.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageInterceptor.java new file mode 100644 index 000000000..bf8ec2336 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageInterceptor.java @@ -0,0 +1,22 @@ +package me.chanjar.weixin.enterprise.api; + +import java.util.Map; + +import me.chanjar.weixin.enterprise.bean.WxCpXmlMessage; + +/** + * 微信消息拦截器,可以用来做验证 + * @author Daniel Qian + * + */ +public interface WxCpMessageInterceptor { + + /** + * 拦截微信消息 + * @param wxMessage + * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 + * @return true代表OK,false代表不OK + */ + public boolean intercept(WxCpXmlMessage wxMessage, Map context); + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageRouter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageRouter.java new file mode 100644 index 000000000..682cfb161 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpMessageRouter.java @@ -0,0 +1,293 @@ +package me.chanjar.weixin.enterprise.api; + +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 java.util.regex.Pattern; + +import me.chanjar.weixin.enterprise.bean.WxCpXmlMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; + +/** + *
+ * 微信消息路由器,通过代码化的配置,把来自微信的消息交给handler处理
+ * 
+ * 说明:
+ * 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
+ * 2. 默认情况下消息只会被处理一次,除非使用 {@link Rule#next()}
+ * 3. 规则的结束必须用{@link Rule#end()}或者{@link Rule#next()},否则不会生效
+ * 
+ * 使用方法:
+ * WxMessageRouter router = new WxMessageRouter();
+ * router
+ *   .rule()
+ *       .msgType("MSG_TYPE").event("EVENT").eventKey("EVENT_KEY").content("CONTENT")
+ *       .interceptor(interceptor, ...).handler(handler, ...)
+ *   .end()
+ *   .rule()
+ *       // 另外一个匹配规则
+ *   .end()
+ * ;
+ * 
+ * // 将WxXmlMessage交给消息路由器
+ * router.route(message);
+ * 
+ * 
+ * @author Daniel Qian + * + */ +public class WxCpMessageRouter { + + private final List rules = new ArrayList(); + + private final ExecutorService es = Executors.newCachedThreadPool(); + + /** + * 开始一个新的Route规则 + * @return + */ + public Rule rule() { + return new Rule(this); + } + + /** + * 处理微信消息 + * @param wxMessage + */ + public WxXmlOutMessage route(final WxCpXmlMessage wxMessage) { + final List matchRules = new ArrayList(); + // 收集匹配的规则 + for (final Rule rule : rules) { + 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); + if (!rule.reEnter) { + break; + } + } + } + }); + return null; + } + + WxXmlOutMessage res = null; + for (final Rule rule : matchRules) { + // 返回最后一个匹配规则的结果 + res = rule.service(wxMessage); + if (!rule.reEnter) { + break; + } + } + return res; + } + + public static class Rule { + + private final WxCpMessageRouter routerBuilder; + + private boolean async = true; + + private String msgType; + + private String event; + + private String eventKey; + + private String content; + + private String rContent; + + private boolean reEnter = false; + + private List handlers = new ArrayList(); + + private List interceptors = new ArrayList(); + + protected Rule(WxCpMessageRouter routerBuilder) { + this.routerBuilder = routerBuilder; + } + + /** + * 设置是否异步执行,默认是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; + } + + /** + * 设置微信消息拦截器 + * @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.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 代表继续执行别的router,false 代表停止执行别的router + */ + protected WxXmlOutMessage service(WxCpXmlMessage wxMessage) { + Map context = new HashMap(); + // 如果拦截器不通过 + for (WxCpMessageInterceptor interceptor : this.interceptors) { + if (!interceptor.intercept(wxMessage, context)) { + return null; + } + } + + // 交给handler处理 + WxXmlOutMessage res = null; + for (WxCpMessageHandler handler : this.handlers) { + // 返回最后handler的结果 + res = handler.handle(wxMessage, context); + } + return res; + } + + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpService.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpService.java new file mode 100644 index 000000000..3ca1345b9 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpService.java @@ -0,0 +1,183 @@ +package me.chanjar.weixin.enterprise.api; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import me.chanjar.weixin.enterprise.bean.*; +import me.chanjar.weixin.enterprise.bean.WxDepartment; +import me.chanjar.weixin.enterprise.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.enterprise.bean.result.WxUser; +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +/** + * 微信API的Service + */ +public interface WxCpService { + + /** + *
+   * 验证推送过来的消息的正确性
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性
+   * 
+ * @param msgSignature + * @param timestamp + * @param nonce + * @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息 + * @return + */ + public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); + + /** + *
+   * 获取access_token,本方法线程安全
+   * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
+   * 
+   * 另:本service的所有方法都会在access_token过期是调用此方法
+   * 
+   * 程序员在非必要情况下尽量不要主动调用此方法
+
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token
+   * 
+ * @throws me.chanjar.weixin.enterprise.exception.WxErrorException + */ + public void accessTokenRefresh() throws WxErrorException; + + /** + *
+   * 上传多媒体文件
+   * 
+   * 上传的多媒体文件有格式和大小限制,如下:
+   *   图片(image): 1M,支持JPG格式
+   *   语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
+   *   视频(video):10MB,支持MP4格式
+   *   缩略图(thumb):64KB,支持JPG格式
+   *    
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
+   * 
+ * @param mediaType 媒体类型, 请看{@link WxConsts} + * @param fileType 文件类型,请看{@link WxConsts} + * @param inputStream 输入流 + * @throws WxErrorException + */ + public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException; + + /** + * @see #mediaUpload(String, String, InputStream) + * @param mediaType + * @param file + * @throws WxErrorException + */ + public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; + + /** + *
+   * 下载多媒体文件
+   * 根据微信文档,视频文件下载不了,会返回null
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
+   * 
+ * @params media_id + * @return 保存到本地的临时文件 + * @throws WxErrorException + */ + public File mediaDownload(String media_id) throws WxErrorException; + + /** + *
+   * 发送客服消息
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息
+   * 
+ * @param message + * @throws WxErrorException + */ + public void messageSend(WxCustomMessage message) throws WxErrorException; + + /** + *
+   * 自定义菜单创建接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口
+   * 
+ * @param menu + * @throws WxErrorException + */ + public void menuCreate(WxMenu menu) throws WxErrorException; + + /** + *
+   * 自定义菜单删除接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口
+   * 
+ * @throws WxErrorException + */ + public void menuDelete() throws WxErrorException; + + /** + *
+   * 自定义菜单查询接口
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口
+   * 
+ * @return + * @throws WxErrorException + */ + public WxMenu menuGet() throws WxErrorException; + + /** + *
+   * 分组管理接口 - 创建分组
+   * 最多支持创建500个分组
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口
+   * 
+ * @param name 分组名字(30个字符以内) + * @throws WxErrorException + */ + public WxDepartment departmentCreate(String name) throws WxErrorException; + + /** + *
+   * 分组管理接口 - 查询所有分组
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口
+   * 
+ * @return + * @throws WxErrorException + */ + public List departmentGet() throws WxErrorException; + + /** + *
+   * 分组管理接口 - 修改分组名
+   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口
+   * 
+   * 如果id为0(未分组),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误
+   * 
+ * @param group 要更新的group,group的id,name必须设置 + * @throws WxErrorException + */ + public void departmentUpdate(WxDepartment group) throws WxErrorException; + + /** + *
+   * 部门管理接口 - 删除部门
+   *
+   * 
+ * @param department + * @throws WxErrorException + */ + public void departmentDelete(WxDepartment department) throws WxErrorException; + + public void userCreate(WxUser user) throws WxErrorException; + + public void userUpdate(WxUser user) throws WxErrorException; + + public void userDelete(String userid) throws WxErrorException; + + public WxUser userGet(String userid) throws WxErrorException; + + public List userGetByDepartment(String departmentId) throws WxErrorException; + + /** + * 注入 {@link WxCpConfigStorage} 的实现 + * @param wxConfigProvider + */ + public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider); +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpServiceImpl.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpServiceImpl.java new file mode 100644 index 000000000..0c0d7fa74 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxCpServiceImpl.java @@ -0,0 +1,278 @@ +package me.chanjar.weixin.enterprise.api; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import me.chanjar.weixin.enterprise.bean.*; +import me.chanjar.weixin.enterprise.util.crypto.WxCryptUtil; +import me.chanjar.weixin.enterprise.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.enterprise.util.crypto.SHA1; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import me.chanjar.weixin.enterprise.bean.WxDepartment; +import me.chanjar.weixin.enterprise.bean.result.WxError; +import me.chanjar.weixin.enterprise.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.enterprise.bean.result.WxUser; +import me.chanjar.weixin.enterprise.exception.WxErrorException; +import me.chanjar.weixin.enterprise.util.fs.FileUtils; +import me.chanjar.weixin.enterprise.util.http.MediaDownloadRequestExecutor; +import me.chanjar.weixin.enterprise.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.enterprise.util.http.RequestExecutor; +import me.chanjar.weixin.enterprise.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; + +public class WxCpServiceImpl implements WxCpService { + + /** + * 全局的是否正在刷新Access Token的flag + * true: 正在刷新 + * false: 没有刷新 + */ + protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false); + + protected static final CloseableHttpClient httpclient = HttpClients.createDefault(); + + protected WxCpConfigStorage wxCpConfigStorage; + + protected final ThreadLocal retryTimes = new ThreadLocal(); + + public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { + try { + return SHA1.gen(wxCpConfigStorage.getToken(), timestamp, nonce, data).equals(msgSignature); + } catch (Exception e) { + return false; + } + } + + + public void accessTokenRefresh() throws WxErrorException { + if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) { + try { + String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" + + "&corpid=" + wxCpConfigStorage.getCorpId() + + "&corpsecret=" + wxCpConfigStorage.getCorpSecret(); + try { + HttpGet httpGet = new HttpGet(url); + CloseableHttpResponse response = httpclient.execute(httpGet); + String resultContent = new BasicResponseHandler().handleResponse(response); + WxError error = WxError.fromJson(resultContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + wxCpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } catch (ClientProtocolException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } finally { + GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false); + } + } else { + // 每隔100ms检查一下是否刷新完毕了 + while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + // 刷新完毕了,就没他什么事儿了 + } + } + + public void messageSend(WxCustomMessage message) throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; + execute(new SimplePostRequestExecutor(), url, message.toJson()); + } + + public void menuCreate(WxMenu menu) throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/menu/create"; + execute(new SimplePostRequestExecutor(), url, menu.toJson()); + } + + public void menuDelete() throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/menu/delete"; + execute(new SimpleGetRequestExecutor(), url, null); + } + + public WxMenu menuGet() throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/menu/get"; + try { + String resultContent = execute(new SimpleGetRequestExecutor(), url, null); + return WxMenu.fromJson(resultContent); + } catch (WxErrorException e) { + // 46003 不存在的菜单数据 + if (e.getError().getErrorCode() == 46003) { + return null; + } + throw e; + } + } + + public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) + throws WxErrorException, IOException { + return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); + } + + public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { + String url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; + return execute(new MediaUploadRequestExecutor(), url, file); + } + + public File mediaDownload(String media_id) throws WxErrorException { + String url = "http://file.api.weixin.qq.com/cgi-bin/media/get"; + return execute(new MediaDownloadRequestExecutor(), url, "media_id=" + media_id); + } + + + public WxDepartment departmentCreate(String name) throws WxErrorException { + // TODO + String url = "https://api.weixin.qq.com/cgi-bin/groups/create"; + JsonObject json = new JsonObject(); + JsonObject groupJson = new JsonObject(); + json.add("group", groupJson); + groupJson.addProperty("name", name); + + String responseContent = execute( + new SimplePostRequestExecutor(), + url, + json.toString()); + return WxDepartment.fromJson(responseContent); + } + + public void departmentUpdate(WxDepartment group) throws WxErrorException { + // TODO + String url = "https://api.weixin.qq.com/cgi-bin/groups/update"; + execute(new SimplePostRequestExecutor(), url, group.toJson()); + } + + public List departmentGet() throws WxErrorException { + // TODO + String url = "https://api.weixin.qq.com/cgi-bin/groups/get"; + String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + /* + * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } + * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } + */ + JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); + return WxGsonBuilder.INSTANCE.create() + .fromJson(tmpJsonElement.getAsJsonObject().get("groups"), new TypeToken>() { + }.getType()); + } + + public void departmentDelete(WxDepartment department) throws WxErrorException { + // TODO + + } + + @Override + public void userCreate(WxUser user) throws WxErrorException { + // TODO + } + + @Override + public void userUpdate(WxUser user) throws WxErrorException { + // TODO + } + + @Override + public void userDelete(String userid) throws WxErrorException { + // TODO + } + + @Override + public WxUser userGet(String userid) throws WxErrorException { + return null; + } + + @Override + public List userGetByDepartment(String departmentId) throws WxErrorException { + return null; + } + + /** + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 + * + * @param executor + * @param uri + * @param data + * @return + * @throws WxErrorException + */ + public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + if (StringUtils.isBlank(wxCpConfigStorage.getAccessToken())) { + accessTokenRefresh(); + } + String accessToken = wxCpConfigStorage.getAccessToken(); + + String uriWithAccessToken = uri; + uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; + + try { + return executor.execute(uriWithAccessToken, data); + } catch (WxErrorException e) { + WxError error = e.getError(); + /* + * 发生以下情况时尝试刷新access_token + * 40001 获取access_token时AppSecret错误,或者access_token无效 + * 42001 access_token超时 + */ + if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { + accessTokenRefresh(); + return execute(executor, uri, data); + } + /** + * -1 系统繁忙, 1000ms后重试 + */ + if (error.getErrorCode() == -1) { + if (retryTimes.get() == null) { + retryTimes.set(0); + } + if (retryTimes.get() > 4) { + retryTimes.set(0); + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + int sleepMillis = 1000 * (1 << retryTimes.get()); + try { + System.out.println("微信系统繁忙," + sleepMillis + "ms后重试"); + Thread.sleep(sleepMillis); + retryTimes.set(retryTimes.get() + 1); + return execute(executor, uri, data); + } catch (InterruptedException e1) { + throw new RuntimeException(e1); + } + } + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return null; + } catch (ClientProtocolException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) { + this.wxCpConfigStorage = wxConfigProvider; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxInMemoryCpConfigStorage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxInMemoryCpConfigStorage.java new file mode 100644 index 000000000..ae47c33d5 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/api/WxInMemoryCpConfigStorage.java @@ -0,0 +1,85 @@ +package me.chanjar.weixin.enterprise.api; + +import me.chanjar.weixin.enterprise.bean.WxAccessToken; + +/** + * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 + * @author Daniel Qian + * + */ +public class WxInMemoryCpConfigStorage implements WxCpConfigStorage { + + protected String corpId; + protected String corpSecret; + + protected String token; + protected String accessToken; + protected String aesKey; + protected String agentId; + protected int expiresIn; + + public void updateAccessToken(WxAccessToken accessToken) { + updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } + + public void updateAccessToken(String accessToken, int expiresIn) { + this.accessToken = accessToken; + this.expiresIn = expiresIn; + } + + public String getAccessToken() { + return this.accessToken; + } + + public String getCorpId() { + return this.corpId; + } + + public String getCorpSecret() { + return this.corpSecret; + } + + public String getToken() { + return this.token; + } + + public int getExpiresIn() { + return this.expiresIn; + } + + public void setCorpId(String corpId) { + this.corpId = corpId; + } + + public void setCorpSecret(String corpSecret) { + this.corpSecret = corpSecret; + } + + public void setToken(String token) { + this.token = token; + } + + public String getAesKey() { + return aesKey; + } + + public void setAesKey(String aesKey) { + this.aesKey = aesKey; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } + + public String getAgentId() { + return agentId; + } + + public void setAgentId(String agentId) { + this.agentId = agentId; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxAccessToken.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxAccessToken.java new file mode 100644 index 000000000..83251cf33 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxAccessToken.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +public class WxAccessToken { + + private String accessToken; + + private int expiresIn = -1; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public int getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } + + public static WxAccessToken fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxAccessToken.class); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxCpXmlMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxCpXmlMessage.java new file mode 100644 index 000000000..0f4311d84 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxCpXmlMessage.java @@ -0,0 +1,639 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.api.WxCpConfigStorage; +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.util.xml.AdapterCDATA; +import me.chanjar.weixin.enterprise.util.xml.XmlTransformer; +import me.chanjar.weixin.enterprise.util.crypto.WxCryptUtil; +import org.apache.commons.io.IOUtils; + +import javax.xml.bind.JAXBException; +import javax.xml.bind.annotation.*; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + *
+ * 微信推送过来的消息,也是同步回复给用户的消息,xml格式
+ * 相关字段的解释看微信开发者文档:
+ * http://mp.weixin.qq.com/wiki/index.php?title=接收普通消息
+ * http://mp.weixin.qq.com/wiki/index.php?title=接收事件推送
+ * http://mp.weixin.qq.com/wiki/index.php?title=接收语音识别结果
+ * 
+ * + * @author Daniel Qian + */ +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxCpXmlMessage { + + /////////////////////// + // 以下都是微信推送过来的消息的xml的element所对应的属性 + /////////////////////// + + @XmlElement(name = "ToUserName") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String toUserName; + + @XmlElement(name = "FromUserName") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String fromUserName; + + @XmlElement(name = "CreateTime") + private Long createTime; + + @XmlElement(name = "MsgType") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String msgType; + + @XmlElement(name = "Content") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String content; + + @XmlElement(name = "MsgId") + private Long msgId; + + @XmlElement(name = "PicUrl") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String picUrl; + + @XmlElement(name = "MediaId") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String mediaId; + + @XmlElement(name = "Format") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String format; + + @XmlElement(name = "ThumbMediaId") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String thumbMediaId; + + @XmlElement(name = "Location_X") + private Double locationX; + + @XmlElement(name = "Location_Y") + private Double locationY; + + @XmlElement(name = "Scale") + private Double scale; + + @XmlElement(name = "Label") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String label; + + @XmlElement(name = "Title") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String title; + + @XmlElement(name = "Description") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String description; + + @XmlElement(name = "Url") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String url; + + @XmlElement(name = "Event") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String event; + + @XmlElement(name = "EventKey") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String eventKey; + + @XmlElement(name = "Ticket") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String ticket; + + @XmlElement(name = "Latitude") + private Double latitude; + + @XmlElement(name = "Longitude") + private Double longitude; + + @XmlElement(name = "Precision") + private Double precision; + + @XmlElement(name = "Recognition") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String recognition; + + /////////////////////////////////////// + // 群发消息返回的结果 + /////////////////////////////////////// + /** + * 群发的结果 + */ + @XmlElement(name = "Status") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String status; + /** + * group_id下粉丝数;或者openid_list中的粉丝数 + */ + @XmlElement(name = "TotalCount") + private Integer totalCount; + /** + * 过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount + */ + @XmlElement(name = "FilterCount") + private Integer filterCount; + /** + * 发送成功的粉丝数 + */ + @XmlElement(name = "SentCount") + private Integer sentCount; + /** + * 发送失败的粉丝数 + */ + @XmlElement(name = "ErrorCount") + private Integer errorCount; + + @XmlElement(name = "ScanCodeInfo") + private ScanCodeInfo scanCodeInfo = new ScanCodeInfo(); + + @XmlElement(name = "SendPicsInfo") + private SendPicsInfo sendPicsInfo = new SendPicsInfo(); + + @XmlElement(name = "SendLocationInfo") + private SendLocationInfo sendLocationInfo = new SendLocationInfo(); + + public String getToUserName() { + return toUserName; + } + + public void setToUserName(String toUserName) { + this.toUserName = toUserName; + } + + public Long getCreateTime() { + return createTime; + } + + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } + + /** + *
+   * 当接受用户消息时,可能会获得以下值:
+   * {@link WxConsts#XML_MSG_TEXT}
+   * {@link WxConsts#XML_MSG_IMAGE}
+   * {@link WxConsts#XML_MSG_VOICE}
+   * {@link WxConsts#XML_MSG_VIDEO}
+   * {@link WxConsts#XML_MSG_LOCATION}
+   * {@link WxConsts#XML_MSG_LINK}
+   * {@link WxConsts#XML_MSG_EVENT}
+   * 
+ * + * @return + */ + public String getMsgType() { + return msgType; + } + + /** + *
+   * 当发送消息的时候使用:
+   * {@link WxConsts#XML_MSG_TEXT}
+   * {@link WxConsts#XML_MSG_IMAGE}
+   * {@link WxConsts#XML_MSG_VOICE}
+   * {@link WxConsts#XML_MSG_VIDEO}
+   * {@link WxConsts#XML_MSG_NEWS}
+   * {@link WxConsts#XML_MSG_MUSIC}
+   * 
+ * + * @param msgType + */ + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Long getMsgId() { + return msgId; + } + + public void setMsgId(Long msgId) { + this.msgId = msgId; + } + + public String getPicUrl() { + return picUrl; + } + + public void setPicUrl(String picUrl) { + this.picUrl = picUrl; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public String getThumbMediaId() { + return thumbMediaId; + } + + public void setThumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + } + + public Double getLocationX() { + return locationX; + } + + public void setLocationX(Double locationX) { + this.locationX = locationX; + } + + public Double getLocationY() { + return locationY; + } + + public void setLocationY(Double locationY) { + this.locationY = locationY; + } + + public Double getScale() { + return scale; + } + + public void setScale(Double scale) { + this.scale = scale; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + 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 getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + public Double getLatitude() { + return latitude; + } + + public void setLatitude(Double latitude) { + this.latitude = latitude; + } + + public Double getLongitude() { + return longitude; + } + + public void setLongitude(Double longitude) { + this.longitude = longitude; + } + + public Double getPrecision() { + return precision; + } + + public void setPrecision(Double precision) { + this.precision = precision; + } + + public String getRecognition() { + return recognition; + } + + public void setRecognition(String recognition) { + this.recognition = recognition; + } + + public String getFromUserName() { + return fromUserName; + } + + public void setFromUserName(String fromUserName) { + this.fromUserName = fromUserName; + } + + public static WxCpXmlMessage fromXml(String xml) { + try { + return XmlTransformer.fromXml(WxCpXmlMessage.class, xml); + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + + public static WxCpXmlMessage fromXml(InputStream is) { + try { + return XmlTransformer.fromXml(WxCpXmlMessage.class, is); + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + + /** + * 从加密字符串转换 + * + * @param encryptedXml + * @param wxCpConfigStorage + * @param timestamp + * @param nonce + * @param msgSignature + * @return + */ + public static WxCpXmlMessage fromEncryptedXml( + String encryptedXml, + WxCpConfigStorage wxCpConfigStorage, + String timestamp, String nonce, String msgSignature) { + WxCryptUtil cryptUtil = new WxCryptUtil(wxCpConfigStorage); + String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); + return fromXml(plainText); + } + + public static WxCpXmlMessage fromEncryptedXml( + InputStream is, + WxCpConfigStorage wxCpConfigStorage, + String timestamp, String nonce, String msgSignature) { + try { + return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxCpConfigStorage, timestamp, nonce, msgSignature); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Integer getTotalCount() { + return totalCount; + } + + public void setTotalCount(Integer totalCount) { + this.totalCount = totalCount; + } + + public Integer getFilterCount() { + return filterCount; + } + + public void setFilterCount(Integer filterCount) { + this.filterCount = filterCount; + } + + public Integer getSentCount() { + return sentCount; + } + + public void setSentCount(Integer sentCount) { + this.sentCount = sentCount; + } + + public Integer getErrorCount() { + return errorCount; + } + + public void setErrorCount(Integer errorCount) { + this.errorCount = errorCount; + } + + public WxCpXmlMessage.ScanCodeInfo getScanCodeInfo() { + return scanCodeInfo; + } + + public void setScanCodeInfo(WxCpXmlMessage.ScanCodeInfo scanCodeInfo) { + this.scanCodeInfo = scanCodeInfo; + } + + public WxCpXmlMessage.SendPicsInfo getSendPicsInfo() { + return sendPicsInfo; + } + + public void setSendPicsInfo(WxCpXmlMessage.SendPicsInfo sendPicsInfo) { + this.sendPicsInfo = sendPicsInfo; + } + + public WxCpXmlMessage.SendLocationInfo getSendLocationInfo() { + return sendLocationInfo; + } + + public void setSendLocationInfo(WxCpXmlMessage.SendLocationInfo sendLocationInfo) { + this.sendLocationInfo = sendLocationInfo; + } + + @XmlRootElement(name = "ScanCodeInfo") + @XmlAccessorType(XmlAccessType.FIELD) + public static class ScanCodeInfo { + + @XmlElement(name = "ScanType") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String scanType; + + @XmlElement(name = "ScanResult") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String scanResult; + + /** + * 扫描类型,一般是qrcode + * @return + */ + public String getScanType() { + + return scanType; + } + + public void setScanType(String scanType) { + this.scanType = scanType; + } + + /** + * 扫描结果,即二维码对应的字符串信息 + * @return + */ + public String getScanResult() { + return scanResult; + } + + public void setScanResult(String scanResult) { + this.scanResult = scanResult; + } + + } + + @XmlRootElement(name = "SendPicsInfo") + @XmlAccessorType(XmlAccessType.FIELD) + public static class SendPicsInfo { + + @XmlElement(name = "Count") + private Long count; + + @XmlElementWrapper(name="PicList") + @XmlElement(name = "item") + protected final List picList = new ArrayList(); + + public Long getCount() { + return count; + } + + public void setCount(Long count) { + this.count = count; + } + + public List getPicList() { + return picList; + } + + @XmlRootElement(name = "item") + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(name = "WxXmlMessage.SendPicsInfo.Item") + public static class Item { + + @XmlElement(name = "PicMd5Sum") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String PicMd5Sum; + + public String getPicMd5Sum() { + return PicMd5Sum; + } + + public void setPicMd5Sum(String picMd5Sum) { + PicMd5Sum = picMd5Sum; + } + } + } + + @XmlRootElement(name = "SendLocationInfo") + @XmlAccessorType(XmlAccessType.FIELD) + public static class SendLocationInfo { + + @XmlElement(name = "Location_X") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String locationX; + + @XmlElement(name = "Location_Y") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String locationY; + + @XmlElement(name = "Scale") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String scale; + + @XmlElement(name = "Label") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String label; + + @XmlElement(name = "Poiname") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String poiname; + + public String getLocationX() { + return locationX; + } + + public void setLocationX(String locationX) { + this.locationX = locationX; + } + + public String getLocationY() { + return locationY; + } + + public void setLocationY(String locationY) { + this.locationY = locationY; + } + + public String getScale() { + return scale; + } + + public void setScale(String scale) { + this.scale = scale; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getPoiname() { + return poiname; + } + + public void setPoiname(String poiname) { + this.poiname = poiname; + } + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxCustomMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxCustomMessage.java new file mode 100644 index 000000000..fbd614ebd --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxCustomMessage.java @@ -0,0 +1,193 @@ +package me.chanjar.weixin.enterprise.bean; + +import java.util.ArrayList; +import java.util.List; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.custombuilder.ImageBuilder; +import me.chanjar.weixin.enterprise.bean.custombuilder.MusicBuilder; +import me.chanjar.weixin.enterprise.bean.custombuilder.NewsBuilder; +import me.chanjar.weixin.enterprise.bean.custombuilder.TextBuilder; +import me.chanjar.weixin.enterprise.bean.custombuilder.VideoBuilder; +import me.chanjar.weixin.enterprise.bean.custombuilder.VoiceBuilder; +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 客服消息 + * @author Daniel Qian + * + */ +public class WxCustomMessage { + + private String toUser; + private String msgType; + private String content; + private String mediaId; + private String thumbMediaId; + private String title; + private String description; + private String musicUrl; + private String hqMusicUrl; + private List articles = new ArrayList(); + + public String getToUser() { + return toUser; + } + public void setToUser(String toUser) { + this.toUser = toUser; + } + public String getMsgType() { + return msgType; + } + + /** + *
+   * 请使用
+   * {@link WxConsts#CUSTOM_MSG_TEXT}
+   * {@link WxConsts#CUSTOM_MSG_IMAGE}
+   * {@link WxConsts#CUSTOM_MSG_VOICE}
+   * {@link WxConsts#CUSTOM_MSG_MUSIC}
+   * {@link WxConsts#CUSTOM_MSG_VIDEO}
+   * {@link WxConsts#CUSTOM_MSG_NEWS}
+   * 
+ * @param msgType + */ + public void setMsgType(String msgType) { + this.msgType = msgType; + } + public String getContent() { + return content; + } + public void setContent(String content) { + this.content = content; + } + public String getMediaId() { + return mediaId; + } + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + public String getThumbMediaId() { + return thumbMediaId; + } + public void setThumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + } + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + public String getMusicUrl() { + return musicUrl; + } + public void setMusicUrl(String musicUrl) { + this.musicUrl = musicUrl; + } + public String getHqMusicUrl() { + return hqMusicUrl; + } + public void setHqMusicUrl(String hqMusicUrl) { + this.hqMusicUrl = hqMusicUrl; + } + public List getArticles() { + return articles; + } + public void setArticles(List articles) { + this.articles = articles; + } + + public String toJson() { + return WxGsonBuilder.INSTANCE.create().toJson(this); + } + + public static class WxArticle { + + private String title; + private String description; + private String url; + private String picUrl; + + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + public String getPicUrl() { + return picUrl; + } + public void setPicUrl(String picUrl) { + this.picUrl = picUrl; + } + + } + + /** + * 获得文本消息builder + * @return + */ + public static TextBuilder TEXT() { + return new TextBuilder(); + } + + /** + * 获得图片消息builder + * @return + */ + public static ImageBuilder IMAGE() { + return new ImageBuilder(); + } + + /** + * 获得语音消息builder + * @return + */ + public static VoiceBuilder VOICE() { + return new VoiceBuilder(); + } + + /** + * 获得视频消息builder + * @return + */ + public static VideoBuilder VIDEO() { + return new VideoBuilder(); + } + + /** + * 获得音乐消息builder + * @return + */ + public static MusicBuilder MUSIC() { + return new MusicBuilder(); + } + + /** + * 获得图文消息builder + * @return + */ + public static NewsBuilder NEWS() { + return new NewsBuilder(); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxDepartment.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxDepartment.java new file mode 100644 index 000000000..4c760c01e --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxDepartment.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 微信用户分组 + * @author Daniel Qian + * + */ +public class WxDepartment { + + private long id = -1; + private String name; + private long count; + public long getId() { + return id; + } + public void setId(long id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public long getCount() { + return count; + } + public void setCount(long count) { + this.count = count; + } + + public static WxDepartment fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxDepartment.class); + } + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + @Override + public String toString() { + return "WxGroup [id=" + id + ", name=" + name + ", count=" + count + "]"; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassGroupMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassGroupMessage.java new file mode 100644 index 000000000..3965b060e --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassGroupMessage.java @@ -0,0 +1,70 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 分组群发的消息 + * + * @author Daniel Qian + */ +public class WxMassGroupMessage { + + private long groupId; + private String msgtype; + private String content; + private String mediaId; + + public WxMassGroupMessage() { + super(); + } + + public String getMsgtype() { + return msgtype; + } + + /** + *
+   * 请使用
+   * {@link WxConsts#MASS_MSG_IMAGE}
+   * {@link WxConsts#MASS_MSG_NEWS}
+   * {@link WxConsts#MASS_MSG_TEXT}
+   * {@link WxConsts#MASS_MSG_VIDEO}
+   * {@link WxConsts#MASS_MSG_VOICE}
+   * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
+   * 
+ * @param msgtype + */ + public void setMsgtype(String msgtype) { + this.msgtype = msgtype; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public String toJson() { + return WxGsonBuilder.INSTANCE.create().toJson(this); + } + + public long getGroupId() { + return groupId; + } + + public void setGroupId(long groupId) { + this.groupId = groupId; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassNews.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassNews.java new file mode 100644 index 000000000..b2ab7f79c --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassNews.java @@ -0,0 +1,117 @@ +package me.chanjar.weixin.enterprise.bean; + +import java.util.ArrayList; +import java.util.List; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 群发时用到的图文消息素材 + * @author Daniel Qian + * + */ +public class WxMassNews { + + private List articles = new ArrayList(); + + public List getArticles() { + return articles; + } + + public void addArticle(WxMassNewsArticle article) { + this.articles.add(article); + } + + public String toJson() { + return WxGsonBuilder.INSTANCE.create().toJson(this); + } + + /** + *
+   * 群发图文消息article
+   * 1. thumbMediaId  (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得
+   * 2. author          图文消息的作者
+   * 3. title           (必填) 图文消息的标题
+   * 4. contentSourceUrl 在图文消息页面点击“阅读原文”后的页面链接
+   * 5. content (必填)  图文消息页面的内容,支持HTML标签
+   * 6. digest          图文消息的描述
+   * 7, showCoverPic  是否显示封面,true为显示,false为不显示
+   * 
+ * @author Daniel Qian + * + */ + public static class WxMassNewsArticle { + /** + * (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得 + */ + private String thumbMediaId; + /** + * 图文消息的作者 + */ + private String author; + /** + * (必填) 图文消息的标题 + */ + private String title; + /** + * 在图文消息页面点击“阅读原文”后的页面链接 + */ + private String contentSourceUrl; + /** + * (必填) 图文消息页面的内容,支持HTML标签 + */ + private String content; + /** + * 图文消息的描述 + */ + private String digest; + /** + * 是否显示封面,true为显示,false为不显示 + */ + private boolean showCoverPic; + + public String getThumbMediaId() { + return thumbMediaId; + } + public void setThumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + } + public String getAuthor() { + return author; + } + public void setAuthor(String author) { + this.author = author; + } + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + public String getContentSourceUrl() { + return contentSourceUrl; + } + public void setContentSourceUrl(String contentSourceUrl) { + this.contentSourceUrl = contentSourceUrl; + } + public String getContent() { + return content; + } + public void setContent(String content) { + this.content = content; + } + public String getDigest() { + return digest; + } + public void setDigest(String digest) { + this.digest = digest; + } + public boolean isShowCoverPic() { + return showCoverPic; + } + public void setShowCoverPic(boolean showCoverPic) { + this.showCoverPic = showCoverPic; + } + + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassOpenIdsMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassOpenIdsMessage.java new file mode 100644 index 000000000..6bf7b1a6a --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassOpenIdsMessage.java @@ -0,0 +1,79 @@ +package me.chanjar.weixin.enterprise.bean; + +import java.util.ArrayList; +import java.util.List; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * OpenId列表群发的消息 + * + * @author Daniel Qian + */ +public class WxMassOpenIdsMessage { + + private List toUsers = new ArrayList(); + private String msgType; + private String content; + private String mediaId; + + public WxMassOpenIdsMessage() { + super(); + } + + public String getMsgType() { + return msgType; + } + + /** + *
+   * 请使用
+   * {@link me.chanjar.weixin.enterprise.api.WxConsts#MASS_MSG_IMAGE}
+   * {@link me.chanjar.weixin.enterprise.api.WxConsts#MASS_MSG_NEWS}
+   * {@link me.chanjar.weixin.enterprise.api.WxConsts#MASS_MSG_TEXT}
+   * {@link me.chanjar.weixin.enterprise.api.WxConsts#MASS_MSG_VIDEO}
+   * {@link me.chanjar.weixin.enterprise.api.WxConsts#MASS_MSG_VOICE}
+   * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
+   * 
+ * @param msgType + */ + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public String toJson() { + return WxGsonBuilder.INSTANCE.create().toJson(this); + } + + /** + * OpenId列表,最多支持10,000个 + * @return + */ + public List getToUsers() { + return toUsers; + } + + /** + * 添加OpenId,最多支持10,000个 + * @param openId + */ + public void addUser(String openId) { + this.toUsers.add(openId); + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassVideo.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassVideo.java new file mode 100644 index 000000000..ae7b1879d --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMassVideo.java @@ -0,0 +1,43 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 群发时用到的视频素材 + * + * @author Daniel Qian + */ +public class WxMassVideo { + + private String mediaId; + private String title; + private String description; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public String toJson() { + return WxGsonBuilder.INSTANCE.create().toJson(this); + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMenu.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMenu.java new file mode 100644 index 000000000..0132fa838 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxMenu.java @@ -0,0 +1,90 @@ +package me.chanjar.weixin.enterprise.bean; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 公众号菜单 + * @author Daniel Qian + * + */ +public class WxMenu { + + private List buttons = new ArrayList(); + + public List getButtons() { + return buttons; + } + + public void setButtons(List buttons) { + this.buttons = buttons; + } + + public String toJson() { + return WxGsonBuilder.create().toJson(this); + } + + public static WxMenu fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMenu.class); + } + + public static WxMenu fromJson(InputStream is) { + return WxGsonBuilder.create().fromJson(new InputStreamReader(is), WxMenu.class); + } + + public static class WxMenuButton { + + private String type; + private String name; + private String key; + private String url; + + private List subButtons = new ArrayList(); + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public List getSubButtons() { + return subButtons; + } + + public void setSubButtons(List subButtons) { + this.subButtons = subButtons; + } + + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutImageMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutImageMessage.java new file mode 100644 index 000000000..f8c7df631 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutImageMessage.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.enterprise.bean; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.util.xml.MediaIdMarshaller; + +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxXmlOutImageMessage extends WxXmlOutMessage { + + @XmlElement(name="Image") + @XmlJavaTypeAdapter(MediaIdMarshaller.class) + private String mediaId; + + public WxXmlOutImageMessage() { + this.msgType = WxConsts.XML_MSG_IMAGE; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMessage.java new file mode 100644 index 000000000..2793d512d --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMessage.java @@ -0,0 +1,137 @@ +package me.chanjar.weixin.enterprise.bean; + +import javax.xml.bind.JAXBException; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import me.chanjar.weixin.enterprise.api.WxCpConfigStorage; +import me.chanjar.weixin.enterprise.bean.outxmlbuilder.ImageBuilder; +import me.chanjar.weixin.enterprise.bean.outxmlbuilder.MusicBuilder; +import me.chanjar.weixin.enterprise.bean.outxmlbuilder.NewsBuilder; +import me.chanjar.weixin.enterprise.bean.outxmlbuilder.TextBuilder; +import me.chanjar.weixin.enterprise.bean.outxmlbuilder.VideoBuilder; +import me.chanjar.weixin.enterprise.bean.outxmlbuilder.VoiceBuilder; +import me.chanjar.weixin.enterprise.util.crypto.WxCryptUtil; +import me.chanjar.weixin.enterprise.util.xml.AdapterCDATA; +import me.chanjar.weixin.enterprise.util.xml.XmlTransformer; + +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxXmlOutMessage { + + @XmlElement(name="ToUserName") + @XmlJavaTypeAdapter(AdapterCDATA.class) + protected String toUserName; + + @XmlElement(name="FromUserName") + @XmlJavaTypeAdapter(AdapterCDATA.class) + protected String fromUserName; + + @XmlElement(name="CreateTime") + protected Long createTime; + + @XmlElement(name="MsgType") + @XmlJavaTypeAdapter(AdapterCDATA.class) + protected String msgType; + + public String getToUserName() { + return toUserName; + } + + public void setToUserName(String toUserName) { + this.toUserName = toUserName; + } + + public String getFromUserName() { + return fromUserName; + } + + public void setFromUserName(String fromUserName) { + this.fromUserName = fromUserName; + } + + public Long getCreateTime() { + return createTime; + } + + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } + + public String getMsgType() { + return msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String toXml() { + try { + return XmlTransformer.toXml((Class)this.getClass(), this); + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + + /** + * 转换成加密的xml格式 + * @return + */ + public String toEncryptedXml(WxCpConfigStorage wxCpConfigStorage) { + String plainXml = toXml(); + WxCryptUtil pc = new WxCryptUtil(wxCpConfigStorage); + return pc.encrypt(plainXml); + } + + /** + * 获得文本消息builder + * @return + */ + public static TextBuilder TEXT() { + return new TextBuilder(); + } + + /** + * 获得图片消息builder + * @return + */ + public static ImageBuilder IMAGE() { + return new ImageBuilder(); + } + + /** + * 获得语音消息builder + * @return + */ + public static VoiceBuilder VOICE() { + return new VoiceBuilder(); + } + + /** + * 获得视频消息builder + * @return + */ + public static VideoBuilder VIDEO() { + return new VideoBuilder(); + } + + /** + * 获得音乐消息builder + * @return + */ + public static MusicBuilder MUSIC() { + return new MusicBuilder(); + } + + /** + * 获得图文消息builder + * @return + */ + public static NewsBuilder NEWS() { + return new NewsBuilder(); + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMewsMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMewsMessage.java new file mode 100644 index 000000000..d241afc31 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMewsMessage.java @@ -0,0 +1,100 @@ +package me.chanjar.weixin.enterprise.bean; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.util.xml.AdapterCDATA; + +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxXmlOutMewsMessage extends WxXmlOutMessage { + + @XmlElement(name = "ArticleCount") + protected int articleCount; + + @XmlElementWrapper(name="Articles") + @XmlElement(name = "item") + protected final List articles = new ArrayList(); + + public WxXmlOutMewsMessage() { + this.msgType = WxConsts.XML_MSG_NEWS; + } + + public int getArticleCount() { + return articleCount; + } + + public void addArticle(Item item) { + this.articles.add(item); + this.articleCount = this.articles.size(); + } + + public List getArticles() { + return articles; + } + + + @XmlRootElement(name = "Item") + @XmlAccessorType(XmlAccessType.FIELD) + public static class Item { + + @XmlElement(name = "Title") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String Title; + + @XmlElement(name = "Description") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String Description; + + @XmlElement(name="PicUrl") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String PicUrl; + + @XmlElement(name="Url") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String Url; + + public String getTitle() { + return Title; + } + + public void setTitle(String title) { + Title = title; + } + + public String getDescription() { + return Description; + } + + public void setDescription(String description) { + Description = description; + } + + public String getPicUrl() { + return PicUrl; + } + + public void setPicUrl(String picUrl) { + PicUrl = picUrl; + } + + public String getUrl() { + return Url; + } + + public void setUrl(String url) { + Url = url; + } + + } + + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMusicMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMusicMessage.java new file mode 100644 index 000000000..9199fd085 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMusicMessage.java @@ -0,0 +1,129 @@ +package me.chanjar.weixin.enterprise.bean; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.util.xml.AdapterCDATA; + +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxXmlOutMusicMessage extends WxXmlOutMessage { + + @XmlElement(name = "Music") + protected final Music music = new Music(); + + public WxXmlOutMusicMessage() { + this.msgType = WxConsts.XML_MSG_MUSIC; + } + + public String getTitle() { + return music.getTitle(); + } + + public void setTitle(String title) { + music.setTitle(title); + } + + public String getDescription() { + return music.getDescription(); + } + + public void setDescription(String description) { + music.setDescription(description); + } + + public String getThumbMediaId() { + return music.getThumbMediaId(); + } + + public void setThumbMediaId(String thumbMediaId) { + music.setThumbMediaId(thumbMediaId); + } + + public String getMusicUrl() { + return music.getMusicUrl(); + } + + public void setMusicUrl(String musicUrl) { + music.setMusicUrl(musicUrl); + } + + public String getHqMusicUrl() { + return music.getHqMusicUrl(); + } + + public void setHqMusicUrl(String hqMusicUrl) { + music.setHqMusicUrl(hqMusicUrl); + } + + @XmlRootElement(name = "Music") + @XmlAccessorType(XmlAccessType.FIELD) + private static class Music { + + @XmlElement(name = "Title") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String title; + + @XmlElement(name = "Description") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String description; + + @XmlElement(name="ThumbMediaId") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String thumbMediaId; + + @XmlElement(name="MusicUrl") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String musicUrl; + + @XmlElement(name="HQMusicUrl") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String hqMusicUrl; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getThumbMediaId() { + return thumbMediaId; + } + + public void setThumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + } + + public String getMusicUrl() { + return musicUrl; + } + + public void setMusicUrl(String musicUrl) { + this.musicUrl = musicUrl; + } + + public String getHqMusicUrl() { + return hqMusicUrl; + } + + public void setHqMusicUrl(String hqMusicUrl) { + this.hqMusicUrl = hqMusicUrl; + } + + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutTextMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutTextMessage.java new file mode 100644 index 000000000..d3854fb6a --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutTextMessage.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.enterprise.bean; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.util.xml.AdapterCDATA; + +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxXmlOutTextMessage extends WxXmlOutMessage { + + @XmlElement(name="Content") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String content; + + public WxXmlOutTextMessage() { + this.msgType = WxConsts.XML_MSG_TEXT; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVideoMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVideoMessage.java new file mode 100644 index 000000000..1c8c46413 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVideoMessage.java @@ -0,0 +1,90 @@ +package me.chanjar.weixin.enterprise.bean; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.util.xml.AdapterCDATA; + +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxXmlOutVideoMessage extends WxXmlOutMessage { + + @XmlElement(name = "Video") + protected final Video video = new Video(); + + public WxXmlOutVideoMessage() { + this.msgType = WxConsts.XML_MSG_VIDEO; + } + + public String getMediaId() { + return video.getMediaId(); + } + + public void setMediaId(String mediaId) { + video.setMediaId(mediaId); + } + + public String getTitle() { + return video.getTitle(); + } + + public void setTitle(String title) { + video.setTitle(title); + } + + public String getDescription() { + return video.getDescription(); + } + + public void setDescription(String description) { + video.setDescription(description); + } + + + @XmlRootElement(name = "Video") + @XmlAccessorType(XmlAccessType.FIELD) + private static class Video { + + @XmlElement(name = "MediaId") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String mediaId; + + @XmlElement(name = "Title") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String title; + + @XmlElement(name = "Description") + @XmlJavaTypeAdapter(AdapterCDATA.class) + private String description; + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVoiceMessage.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVoiceMessage.java new file mode 100644 index 000000000..aa2b6d0d0 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVoiceMessage.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.enterprise.bean; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.util.xml.MediaIdMarshaller; + +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +public class WxXmlOutVoiceMessage extends WxXmlOutMessage { + + @XmlElement(name="Voice") + @XmlJavaTypeAdapter(MediaIdMarshaller.class) + private String mediaId; + + public WxXmlOutVoiceMessage() { + this.msgType = WxConsts.XML_MSG_VOICE; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/BaseBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/BaseBuilder.java new file mode 100644 index 000000000..c16d86ebc --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/BaseBuilder.java @@ -0,0 +1,20 @@ +package me.chanjar.weixin.enterprise.bean.custombuilder; + +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; + +public class BaseBuilder { + protected String msgType; + protected String toUser; + + public T toUser(String toUser) { + this.toUser = toUser; + return (T) this; + } + + public WxCustomMessage build() { + WxCustomMessage m = new WxCustomMessage(); + m.setMsgType(this.msgType); + m.setToUser(this.toUser); + return m; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/ImageBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/ImageBuilder.java new file mode 100644 index 000000000..7c5e448cb --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/ImageBuilder.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.enterprise.bean.custombuilder; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; + +/** + * 获得消息builder + *
+ * 用法: WxCustomMessage m = WxCustomMessage.IMAGE().mediaId(...).toUser(...).build();
+ * 
+ * @author Daniel Qian + * + */ +public final class ImageBuilder extends BaseBuilder { + private String mediaId; + + public ImageBuilder() { + this.msgType = WxConsts.CUSTOM_MSG_IMAGE; + } + + public ImageBuilder mediaId(String media_id) { + this.mediaId = media_id; + return this; + } + + public WxCustomMessage build() { + WxCustomMessage m = super.build(); + m.setMediaId(this.mediaId); + return m; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/MusicBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/MusicBuilder.java new file mode 100644 index 000000000..0060d6c10 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/MusicBuilder.java @@ -0,0 +1,64 @@ +package me.chanjar.weixin.enterprise.bean.custombuilder; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; + +/** + * 音乐消息builder + *
+ * 用法: WxCustomMessage m = WxCustomMessage.MUSIC()
+ *                      .musicUrl(...)
+ *                      .hqMusicUrl(...)
+ *                      .title(...)
+ *                      .thumbMediaId(..)
+ *                      .description(..)
+ *                      .toUser(...)
+ *                      .build();
+ * 
+ */ +public final class MusicBuilder extends BaseBuilder { + private String title; + private String description; + private String thumbMediaId; + private String musicUrl; + private String hqMusicUrl; + + public MusicBuilder() { + this.msgType = WxConsts.CUSTOM_MSG_MUSIC; + } + + public MusicBuilder musicUrl(String musicurl) { + this.musicUrl = musicurl; + return this; + } + + public MusicBuilder hqMusicUrl(String hqMusicurl) { + this.hqMusicUrl = hqMusicurl; + return this; + } + + public MusicBuilder title(String title) { + this.title = title; + return this; + } + + public MusicBuilder description(String description) { + this.description = description; + return this; + } + + public MusicBuilder thumbMediaId(String thumb_media_id) { + this.thumbMediaId = thumb_media_id; + return this; + } + + public WxCustomMessage build() { + WxCustomMessage m = super.build(); + m.setMusicUrl(this.musicUrl); + m.setHqMusicUrl(this.hqMusicUrl); + m.setTitle(title); + m.setDescription(description); + m.setThumbMediaId(thumbMediaId); + return m; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/NewsBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/NewsBuilder.java new file mode 100644 index 000000000..61b7bb66f --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/NewsBuilder.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.enterprise.bean.custombuilder; + +import java.util.ArrayList; +import java.util.List; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; + +/** + * 图文消息builder + *
+ * 用法:
+ * WxCustomMessage m = WxCustomMessage.NEWS().addArticle(article).toUser(...).build();
+ * 
+ * @author Daniel Qian + * + */ +public final class NewsBuilder extends BaseBuilder { + + private List articles = new ArrayList(); + + public NewsBuilder() { + this.msgType = WxConsts.CUSTOM_MSG_NEWS; + } + + public NewsBuilder addArticle(WxCustomMessage.WxArticle article) { + this.articles.add(article); + return this; + } + + public WxCustomMessage build() { + WxCustomMessage m = super.build(); + m.setArticles(this.articles); + return m; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/TextBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/TextBuilder.java new file mode 100644 index 000000000..2965428d1 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/TextBuilder.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.enterprise.bean.custombuilder; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; + +/** + * 文本消息builder + *
+ * 用法: WxCustomMessage m = WxCustomMessage.TEXT().content(...).toUser(...).build();
+ * 
+ * @author Daniel Qian + * + */ +public final class TextBuilder extends BaseBuilder { + private String content; + + public TextBuilder() { + this.msgType = WxConsts.CUSTOM_MSG_TEXT; + } + + public TextBuilder content(String content) { + this.content = content; + return this; + } + + public WxCustomMessage build() { + WxCustomMessage m = super.build(); + m.setContent(this.content); + return m; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/VideoBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/VideoBuilder.java new file mode 100644 index 000000000..d2f73351d --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/VideoBuilder.java @@ -0,0 +1,58 @@ +package me.chanjar.weixin.enterprise.bean.custombuilder; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; + +/** + * 视频消息builder + *
+ * 用法: WxCustomMessage m = WxCustomMessage.VOICE()
+ *                              .mediaId(...)
+ *                              .title(...)
+ *                              .thumbMediaId(..)
+ *                              .description(..)
+ *                              .toUser(...)
+ *                              .build();
+ * 
+ * @author Daniel Qian + * + */ +public final class VideoBuilder extends BaseBuilder { + private String mediaId; + private String title; + private String description; + private String thumbMediaId; + + public VideoBuilder() { + this.msgType = WxConsts.CUSTOM_MSG_VIDEO; + } + + public VideoBuilder mediaId(String mediaId) { + this.mediaId = mediaId; + return this; + } + + public VideoBuilder title(String title) { + this.title = title; + return this; + } + + public VideoBuilder description(String description) { + this.description = description; + return this; + } + + public VideoBuilder thumbMediaId(String thumb_media_id) { + this.thumbMediaId = thumb_media_id; + return this; + } + + public WxCustomMessage build() { + WxCustomMessage m = super.build(); + m.setMediaId(this.mediaId); + m.setTitle(title); + m.setDescription(description); + m.setThumbMediaId(thumbMediaId); + return m; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/VoiceBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/VoiceBuilder.java new file mode 100644 index 000000000..f3cb77f59 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/custombuilder/VoiceBuilder.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.enterprise.bean.custombuilder; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; + +/** + * 语音消息builder + *
+ * 用法: WxCustomMessage m = WxCustomMessage.VOICE().mediaId(...).toUser(...).build();
+ * 
+ * @author Daniel Qian + * + */ +public final class VoiceBuilder extends BaseBuilder { + private String mediaId; + + public VoiceBuilder() { + this.msgType = WxConsts.CUSTOM_MSG_VOICE; + } + + public VoiceBuilder mediaId(String media_id) { + this.mediaId = media_id; + return this; + } + + public WxCustomMessage build() { + WxCustomMessage m = super.build(); + m.setMediaId(this.mediaId); + return m; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/BaseBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/BaseBuilder.java new file mode 100644 index 000000000..cba5e1ef5 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/BaseBuilder.java @@ -0,0 +1,29 @@ +package me.chanjar.weixin.enterprise.bean.outxmlbuilder; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; + +public abstract class BaseBuilder { + + protected String toUserName; + + protected String fromUserName; + + public BuilderType toUser(String touser) { + this.toUserName = touser; + return (BuilderType) this; + } + + public BuilderType fromUser(String fromusername) { + this.fromUserName = fromusername; + return (BuilderType) this; + } + + public abstract ValueType build(); + + public void setCommon(WxXmlOutMessage m) { + m.setToUserName(this.toUserName); + m.setFromUserName(this.fromUserName); + m.setCreateTime(System.currentTimeMillis() / 1000l); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/ImageBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/ImageBuilder.java new file mode 100644 index 000000000..7a7497a00 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/ImageBuilder.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.enterprise.bean.outxmlbuilder; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutImageMessage; + +/** + * 图片消息builder + * @author Daniel Qian + */ +public final class ImageBuilder extends BaseBuilder { + + private String mediaId; + + public ImageBuilder mediaId(String media_id) { + this.mediaId = media_id; + return this; + } + + public WxXmlOutImageMessage build() { + WxXmlOutImageMessage m = new WxXmlOutImageMessage(); + setCommon(m); + m.setMediaId(this.mediaId); + return m; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/MusicBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/MusicBuilder.java new file mode 100644 index 000000000..9de3d951e --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/MusicBuilder.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.enterprise.bean.outxmlbuilder; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutMusicMessage; + +/** + * 音乐消息builder + * + * @author Daniel Qian + */ +public final class MusicBuilder extends BaseBuilder { + + private String title; + private String description; + private String hqMusicUrl; + private String musicUrl; + private String thumbMediaId; + + public MusicBuilder title(String title) { + this.title = title; + return this; + } + + public MusicBuilder description(String description) { + this.description = description; + return this; + } + + public MusicBuilder hqMusicUrl(String hqMusicUrl) { + this.hqMusicUrl = hqMusicUrl; + return this; + } + + public MusicBuilder musicUrl(String musicUrl) { + this.musicUrl = musicUrl; + return this; + } + + public MusicBuilder thumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + return this; + } + + public WxXmlOutMusicMessage build() { + WxXmlOutMusicMessage m = new WxXmlOutMusicMessage(); + setCommon(m); + m.setTitle(title); + m.setDescription(description); + m.setHqMusicUrl(hqMusicUrl); + m.setMusicUrl(musicUrl); + m.setThumbMediaId(thumbMediaId); + return m; + } + +} diff --git a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/NewsBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/NewsBuilder.java similarity index 71% rename from src/main/java/me/chanjar/weixin/bean/outxmlbuilder/NewsBuilder.java rename to weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/NewsBuilder.java index 6168705b6..314bbb317 100644 --- a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/NewsBuilder.java +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/NewsBuilder.java @@ -1,14 +1,14 @@ -package me.chanjar.weixin.bean.outxmlbuilder; +package me.chanjar.weixin.enterprise.bean.outxmlbuilder; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.bean.WxXmlOutMewsMessage; -import me.chanjar.weixin.bean.WxXmlOutMewsMessage.Item; +import me.chanjar.weixin.enterprise.bean.WxXmlOutMewsMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutMewsMessage.Item; /** * 图文消息builder - * @author chanjarster + * @author Daniel Qian */ public final class NewsBuilder extends BaseBuilder { diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/TextBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/TextBuilder.java new file mode 100644 index 000000000..b5af407f6 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/TextBuilder.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.enterprise.bean.outxmlbuilder; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutTextMessage; + +/** + * 文本消息builder + * @author Daniel Qian + * + */ +public final class TextBuilder extends BaseBuilder { + private String content; + + public TextBuilder content(String content) { + this.content = content; + return this; + } + + public WxXmlOutTextMessage build() { + WxXmlOutTextMessage m = new WxXmlOutTextMessage(); + setCommon(m); + m.setContent(this.content); + return m; + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/VideoBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/VideoBuilder.java new file mode 100644 index 000000000..8c50ccbf2 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/VideoBuilder.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.enterprise.bean.outxmlbuilder; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutVideoMessage; + +/** + * 视频消息builder + * @author Daniel Qian + * + */ +public final class VideoBuilder extends BaseBuilder { + + private String mediaId; + private String title; + private String description; + + public VideoBuilder title(String title) { + this.title = title; + return this; + } + public VideoBuilder description(String description) { + this.description = description; + return this; + } + public VideoBuilder mediaId(String mediaId) { + this.mediaId = mediaId; + return this; + } + + public WxXmlOutVideoMessage build() { + WxXmlOutVideoMessage m = new WxXmlOutVideoMessage(); + setCommon(m); + m.setTitle(title); + m.setDescription(description); + m.setMediaId(mediaId); + return m; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/VoiceBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/VoiceBuilder.java new file mode 100644 index 000000000..5c35227e3 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/outxmlbuilder/VoiceBuilder.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.enterprise.bean.outxmlbuilder; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutVoiceMessage; + +/** + * 语音消息builder + * @author Daniel Qian + */ +public final class VoiceBuilder extends BaseBuilder { + + private String mediaId; + + public VoiceBuilder mediaId(String mediaId) { + this.mediaId = mediaId; + return this; + } + + public WxXmlOutVoiceMessage build() { + WxXmlOutVoiceMessage m = new WxXmlOutVoiceMessage(); + setCommon(m); + m.setMediaId(mediaId); + return m; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxError.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxError.java new file mode 100644 index 000000000..fcfc9a3c8 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxError.java @@ -0,0 +1,136 @@ +package me.chanjar.weixin.enterprise.bean.result; + +import java.util.HashMap; +import java.util.Map; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 微信错误码说明 + * http://mp.weixin.qq.com/wiki/index.php?title=全局返回码说明 + * @author Daniel Qian + * + */ +public class WxError { + + protected static final Map errMap = new HashMap(); + + static { + errMap.put(-1, "系统繁忙"); + errMap.put(0, "请求成功"); + errMap.put(40001, "获取access_token时AppSecret错误,或者access_token无效"); + errMap.put(40002, "不合法的凭证类型"); + errMap.put(40003, "不合法的OpenID"); + errMap.put(40004, "不合法的媒体文件类型"); + errMap.put(40005, "不合法的文件类型"); + errMap.put(40006, "不合法的文件大小"); + errMap.put(40007, "不合法的媒体文件id"); + errMap.put(40008, "不合法的消息类型"); + errMap.put(40009, "不合法的图片文件大小"); + errMap.put(40010, "不合法的语音文件大小"); + errMap.put(40011, "不合法的视频文件大小"); + errMap.put(40012, "不合法的缩略图文件大小"); + errMap.put(40013, "不合法的APPID"); + errMap.put(40014, "不合法的access_token"); + errMap.put(40015, "不合法的菜单类型"); + errMap.put(40016, "不合法的按钮个数"); + errMap.put(40017, "不合法的按钮个数"); + errMap.put(40018, "不合法的按钮名字长度"); + errMap.put(40019, "不合法的按钮KEY长度"); + errMap.put(40020, "不合法的按钮URL长度"); + errMap.put(40021, "不合法的菜单版本号"); + errMap.put(40022, "不合法的子菜单级数"); + errMap.put(40023, "不合法的子菜单按钮个数"); + errMap.put(40024, "不合法的子菜单按钮类型"); + errMap.put(40025, "不合法的子菜单按钮名字长度"); + errMap.put(40026, "不合法的子菜单按钮KEY长度"); + errMap.put(40027, "不合法的子菜单按钮URL长度"); + errMap.put(40028, "不合法的自定义菜单使用用户"); + errMap.put(40029, "不合法的oauth_code"); + errMap.put(40030, "不合法的refresh_token"); + errMap.put(40031, "不合法的openid列表"); + errMap.put(40032, "不合法的openid列表长度"); + errMap.put(40033, "不合法的请求字符,不能包含\\uxxxx格式的字符"); + errMap.put(40035, "不合法的参数"); + errMap.put(40038, "不合法的请求格式"); + errMap.put(40039, "不合法的URL长度"); + errMap.put(40050, "不合法的分组id"); + errMap.put(40051, "分组名字不合法"); + errMap.put(41001, "缺少access_token参数"); + errMap.put(41002, "缺少appid参数"); + errMap.put(41003, "缺少refresh_token参数"); + errMap.put(41004, "缺少secret参数"); + errMap.put(41005, "缺少多媒体文件数据"); + errMap.put(41006, "缺少media_id参数"); + errMap.put(41007, "缺少子菜单数据"); + errMap.put(41008, "缺少oauth code"); + errMap.put(41009, "缺少openid"); + errMap.put(42001, "access_token超时"); + errMap.put(42002, "refresh_token超时"); + errMap.put(42003, "oauth_code超时"); + errMap.put(43001, "需要GET请求"); + errMap.put(43002, "需要POST请求"); + errMap.put(43003, "需要HTTPS请求"); + errMap.put(43004, "需要接收者关注"); + errMap.put(43005, "需要好友关系"); + errMap.put(44001, "多媒体文件为空"); + errMap.put(44002, "POST的数据包为空"); + errMap.put(44003, "图文消息内容为空"); + errMap.put(44004, "文本消息内容为空"); + errMap.put(45001, "多媒体文件大小超过限制"); + errMap.put(45002, "消息内容超过限制"); + errMap.put(45003, "标题字段超过限制"); + errMap.put(45004, "描述字段超过限制"); + errMap.put(45005, "链接字段超过限制"); + errMap.put(45006, "图片链接字段超过限制"); + errMap.put(45007, "语音播放时间超过限制"); + errMap.put(45008, "图文消息超过限制"); + errMap.put(45009, "接口调用超过限制"); + errMap.put(45010, "创建菜单个数超过限制"); + errMap.put(45015, "回复时间超过限制"); + errMap.put(45016, "系统分组,不允许修改"); + errMap.put(45017, "分组名字过长"); + errMap.put(45018, "分组数量超过上限"); + errMap.put(46001, "不存在媒体数据"); + errMap.put(46002, "不存在的菜单版本"); + errMap.put(46003, "不存在的菜单数据"); + errMap.put(46004, "不存在的用户"); + errMap.put(47001, "解析JSON/XML内容错误"); + errMap.put(48001, "api功能未授权"); + errMap.put(50001, "用户未授权该api"); + } + + protected int errorCode; + + protected String errorMsg; + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public String getDescription() { + return errMap.get(errorCode); + } + + public static WxError fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxError.class); + } + + @Override + public String toString() { + return "微信错误 errcode=" + errorCode + ", errmsg=" + errorMsg + ", description=" + getDescription(); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMassSendResult.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMassSendResult.java new file mode 100644 index 000000000..9f155b3d2 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMassSendResult.java @@ -0,0 +1,55 @@ +package me.chanjar.weixin.enterprise.bean.result; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + *
+ * 群发消息一发送就返回的结果
+ * 
+ * 真正的群发消息是否发送成功要看
+ * http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口#.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81.E7.BE.A4.E5.8F.91.E7.BB.93.E6.9E.9C
+ * 
+ * 
+ * @author Daniel Qian + * + */ +public class WxMassSendResult { + + private String errorCode; + private String errorMsg; + private String msgId; + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public static WxMassSendResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMassSendResult.class); + } + + @Override + public String toString() { + return "WxMassSendResult [errcode=" + errorCode + ", errmsg=" + errorMsg + ", msg_id=" + msgId + "]"; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMassUploadResult.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMassUploadResult.java new file mode 100644 index 000000000..57ae98a0e --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMassUploadResult.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.enterprise.bean.result; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + *
+ * 上传群发用的素材的结果
+ * 视频和图文消息需要在群发前上传素材
+ * 
+ * @author Daniel Qian + * + */ +public class WxMassUploadResult { + + private String type; + private String mediaId; + private long createdAt; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } + + public static WxMassUploadResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMassUploadResult.class); + } + + @Override + public String toString() { + return "WxUploadResult [type=" + type + ", media_id=" + mediaId + ", created_at=" + createdAt + "]"; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMediaUploadResult.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMediaUploadResult.java new file mode 100644 index 000000000..0fd5a7958 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxMediaUploadResult.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.enterprise.bean.result; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +public class WxMediaUploadResult { + + private String type; + private String mediaId; + private String thumbMediaId; + private long createdAt; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMediaId() { + return mediaId; + } + + public void setMediaId(String mediaId) { + this.mediaId = mediaId; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } + + public static WxMediaUploadResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMediaUploadResult.class); + } + + public String getThumbMediaId() { + return thumbMediaId; + } + + public void setThumbMediaId(String thumbMediaId) { + this.thumbMediaId = thumbMediaId; + } + + @Override + public String toString() { + return "WxUploadResult [type=" + type + ", media_id=" + mediaId + ", thumb_media_id=" + thumbMediaId + + ", created_at=" + createdAt + "]"; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxQrCodeTicket.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxQrCodeTicket.java new file mode 100644 index 000000000..5301aeeb0 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxQrCodeTicket.java @@ -0,0 +1,46 @@ +package me.chanjar.weixin.enterprise.bean.result; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 换取二维码的Ticket + * + * @author Daniel Qian + */ +public class WxQrCodeTicket { + + protected String ticket; + protected int expire_seconds = -1; + protected String url; + + public String getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + /** + * 如果返回-1说明是永久 + */ + public int getExpire_seconds() { + return expire_seconds; + } + + public void setExpire_seconds(int expire_seconds) { + this.expire_seconds = expire_seconds; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public static WxQrCodeTicket fromJson(String json) { + return WxGsonBuilder.INSTANCE.create().fromJson(json, WxQrCodeTicket.class); + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxUser.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxUser.java new file mode 100644 index 000000000..92459d922 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxUser.java @@ -0,0 +1,95 @@ +package me.chanjar.weixin.enterprise.bean.result; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 微信用户信息 + * @author Daniel Qian + * + */ +public class WxUser { + + protected boolean subscribe; + protected String openId; + protected String nickname; + protected String sex; + protected String language; + protected String city; + protected String province; + protected String country; + protected String headImgUrl; + protected long subscribeTime; + protected String unionId; + + public boolean isSubscribe() { + return subscribe; + } + public void setSubscribe(boolean subscribe) { + this.subscribe = subscribe; + } + public String getOpenId() { + return openId; + } + public void setOpenId(String openId) { + this.openId = openId; + } + public String getNickname() { + return nickname; + } + public void setNickname(String nickname) { + this.nickname = nickname; + } + public String getSex() { + return sex; + } + public void setSex(String sex) { + this.sex = sex; + } + public String getLanguage() { + return language; + } + public void setLanguage(String language) { + this.language = language; + } + public String getCity() { + return city; + } + public void setCity(String city) { + this.city = city; + } + public String getProvince() { + return province; + } + public void setProvince(String province) { + this.province = province; + } + public String getCountry() { + return country; + } + public void setCountry(String country) { + this.country = country; + } + public String getHeadImgUrl() { + return headImgUrl; + } + public void setHeadImgUrl(String headImgUrl) { + this.headImgUrl = headImgUrl; + } + public long getSubscribeTime() { + return subscribeTime; + } + public void setSubscribeTime(long subscribeTime) { + this.subscribeTime = subscribeTime; + } + public String getUnionId() { + return unionId; + } + public void setUnionId(String unionId) { + this.unionId = unionId; + } + + public static WxUser fromJson(String json) { + return WxGsonBuilder.INSTANCE.create().fromJson(json, WxUser.class); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxUserList.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxUserList.java new file mode 100644 index 000000000..de4dbb4a4 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/bean/result/WxUserList.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.enterprise.bean.result; + +import java.util.ArrayList; +import java.util.List; + +import me.chanjar.weixin.enterprise.util.json.WxGsonBuilder; + +/** + * 关注者列表 + * @author Daniel Qian + * + */ +public class WxUserList { + + protected int total = -1; + protected int count = -1; + protected List openIds = new ArrayList(); + protected String nextOpenId; + public int getTotal() { + return total; + } + public void setTotal(int total) { + this.total = total; + } + public int getCount() { + return count; + } + public void setCount(int count) { + this.count = count; + } + public List getOpenIds() { + return openIds; + } + public void setOpenIds(List openIds) { + this.openIds = openIds; + } + public String getNextOpenId() { + return nextOpenId; + } + public void setNextOpenId(String nextOpenId) { + this.nextOpenId = nextOpenId; + } + + public static WxUserList fromJson(String json) { + return WxGsonBuilder.INSTANCE.create().fromJson(json, WxUserList.class); + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/exception/WxErrorException.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/exception/WxErrorException.java new file mode 100644 index 000000000..54a51eef8 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/exception/WxErrorException.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.enterprise.exception; + +import me.chanjar.weixin.enterprise.bean.result.WxError; + +public class WxErrorException extends Exception { + + private static final long serialVersionUID = -6357149550353160810L; + + private WxError error; + + public WxErrorException(WxError error) { + super(error.toString()); + this.error = error; + } + + public WxError getError() { + return error; + } + + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/ByteGroup.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/ByteGroup.java new file mode 100755 index 000000000..8f4f10d03 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/ByteGroup.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.enterprise.util.crypto; + +import java.util.ArrayList; + +class ByteGroup { + ArrayList byteContainer = new ArrayList(); + + byte[] toBytes() { + byte[] bytes = new byte[byteContainer.size()]; + for (int i = 0; i < byteContainer.size(); i++) { + bytes[i] = byteContainer.get(i); + } + return bytes; + } + + ByteGroup addBytes(byte[] bytes) { + for (byte b : bytes) { + byteContainer.add(b); + } + return this; + } + + int size() { + return byteContainer.size(); + } +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/PKCS7Encoder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/PKCS7Encoder.java new file mode 100755 index 000000000..dd6893ab7 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/PKCS7Encoder.java @@ -0,0 +1,68 @@ +/** + * 对公众平台发送给公众账号的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +package me.chanjar.weixin.enterprise.util.crypto; + +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * 提供基于PKCS7算法的加解 + */ +class PKCS7Encoder { + + private static final Charset CHARSET = Charset.forName("utf-8"); + private static final int BLOCK_SIZE = 32; + + /** + * 获得对明文进行补位填充的字节. + * + * @param count 需要进行填充补位操作的明文字节个数 + * @return 补齐用的字节数组 + */ + static byte[] encode(int count) { + // 计算需要填充的位数 + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + if (amountToPad == 0) { + amountToPad = BLOCK_SIZE; + } + // 获得补位所用的字符 + char padChr = chr(amountToPad); + String tmp = new String(); + for (int index = 0; index < amountToPad; index++) { + tmp += padChr; + } + return tmp.getBytes(CHARSET); + } + + /** + * 删除解密后明文的补位字符 + * + * @param decrypted 解密后的明文 + * @return 删除补位字符后的明文 + */ + static byte[] decode(byte[] decrypted) { + int pad = (int) decrypted[decrypted.length - 1]; + if (pad < 1 || pad > 32) { + pad = 0; + } + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + + /** + * 将数字转化成ASCII码对应的字符,用于对明文进行补码 + * + * @param a 需要转化的数字 + * @return 转化得到的字符 + */ + static char chr(int a) { + byte target = (byte) (a & 0xFF); + return (char) target; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/SHA1.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/SHA1.java new file mode 100644 index 000000000..99e9951c8 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/SHA1.java @@ -0,0 +1,42 @@ +package me.chanjar.weixin.enterprise.util.crypto; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +/** + * Created by Daniel Qian on 14/10/19. + */ +public class SHA1 { + + /** + * 生成SHA1签名 + * @param arr + * @return + */ + public static String gen(String... arr) throws NoSuchAlgorithmException { + Arrays.sort(arr); + StringBuilder sb = new StringBuilder(); + for(String a : arr) { + sb.append(a); + } + + MessageDigest sha1 = MessageDigest.getInstance("SHA1"); + sha1.update(sb.toString().getBytes()); + byte[] output = sha1.digest(); + return bytesToHex(output); + } + + + protected static String bytesToHex(byte[] b) { + char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + StringBuffer buf = new StringBuffer(); + for (int j = 0; j < b.length; j++) { + buf.append(hexDigit[(b[j] >> 4) & 0x0f]); + buf.append(hexDigit[b[j] & 0x0f]); + } + return buf.toString(); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/WxCryptUtil.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/WxCryptUtil.java new file mode 100755 index 000000000..0a5fe8dd4 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/crypto/WxCryptUtil.java @@ -0,0 +1,322 @@ +/** + * 对公众平台发送给公众账号的消息加解密示例代码. + * + * @copyright Copyright (c) 1998-2014 Tencent Inc. + */ + +// ------------------------------------------------------------------------ + +/** + * 针对org.apache.commons.codec.binary.Base64, + * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) + * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi + */ +package me.chanjar.weixin.enterprise.util.crypto; + +import java.io.StringReader; +import java.nio.charset.Charset; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Random; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import me.chanjar.weixin.enterprise.api.WxCpConfigStorage; +import org.apache.commons.codec.binary.Base64; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.InputSource; + +public class WxCryptUtil { + + private static final Base64 base64 = new Base64(); + private static final Charset CHARSET = Charset.forName("utf-8"); + + private static final ThreadLocal builderLocal = + new ThreadLocal() { + @Override protected DocumentBuilder initialValue() { + try { + return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (ParserConfigurationException exc) { + throw new IllegalArgumentException(exc); + } + } + }; + + private byte[] aesKey; + private String token; + private String corpId; + + /** + * 构造函数 + * + * @param wxCpConfigStorage + */ + public WxCryptUtil(WxCpConfigStorage wxCpConfigStorage) { + /* + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param corpId 公众平台appid + */ + String encodingAesKey = wxCpConfigStorage.getAesKey(); + String token = wxCpConfigStorage.getToken(); + String corpId = wxCpConfigStorage.getCorpId(); + + this.token = token; + this.corpId = corpId; + this.aesKey = Base64.decodeBase64(encodingAesKey + "="); + + } + + /** + * 构造函数 + * + * @param token 公众平台上,开发者设置的token + * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey + * @param corpId 公众平台appid + */ + public WxCryptUtil(String token, String encodingAesKey, String corpId) { + this.token = token; + this.corpId = corpId; + this.aesKey = Base64.decodeBase64(encodingAesKey + "="); + } + + /** + * 将公众平台回复用户的消息加密打包. + *
    + *
  1. 对要发送的消息进行AES-CBC加密
  2. + *
  3. 生成安全签名
  4. + *
  5. 将消息密文和安全签名打包成xml格式
  6. + *
+ * + * @param plainText 公众平台待回复用户的消息,xml格式的字符串 + * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 + */ + public String encrypt(String plainText) { + // 加密 + String encryptedXml = encrypt(genRandomStr(), plainText); + + // 生成安全签名 + String timeStamp = timeStamp = Long.toString(System.currentTimeMillis() / 1000l); + String nonce = genRandomStr(); + + try { + String signature = SHA1.gen(token, timeStamp, nonce, encryptedXml); + String result = generateXml(encryptedXml, signature, timeStamp, nonce); + return result; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + /** + * 对明文进行加密. + * + * @param plainText 需要加密的明文 + * @return 加密后base64编码的字符串 + */ + protected String encrypt(String randomStr, String plainText) { + ByteGroup byteCollector = new ByteGroup(); + byte[] randomStringBytes = randomStr.getBytes(CHARSET); + byte[] plainTextBytes = plainText.getBytes(CHARSET); + byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder(plainTextBytes.length); + byte[] appIdBytes = corpId.getBytes(CHARSET); + + // randomStr + networkBytesOrder + text + appid + byteCollector.addBytes(randomStringBytes); + byteCollector.addBytes(bytesOfSizeInNetworkOrder); + byteCollector.addBytes(plainTextBytes); + byteCollector.addBytes(appIdBytes); + + // ... + pad: 使用自定义的填充方式对明文进行补位填充 + byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); + byteCollector.addBytes(padBytes); + + // 获得最终的字节流, 未加密 + byte[] unencrypted = byteCollector.toBytes(); + + try { + // 设置加密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); + + // 加密 + byte[] encrypted = cipher.doFinal(unencrypted); + + // 使用BASE64对加密后的字符串进行编码 + String base64Encrypted = base64.encodeToString(encrypted); + + return base64Encrypted; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 检验消息的真实性,并且获取解密后的明文. + *
    + *
  1. 利用收到的密文生成安全签名,进行签名验证
  2. + *
  3. 若验证通过,则提取xml中的加密消息
  4. + *
  5. 对消息进行解密
  6. + *
+ * + * @param msgSignature 签名串,对应URL参数的msg_signature + * @param timeStamp 时间戳,对应URL参数的timestamp + * @param nonce 随机串,对应URL参数的nonce + * @param encryptedXml 密文,对应POST请求的数据 + * @return 解密后的原文 + */ + public String decrypt(String msgSignature, String timeStamp, String nonce, String encryptedXml) { + // 密钥,公众账号的app corpSecret + // 提取密文 + String cipherText = extractEncryptPart(encryptedXml); + + try { + // 验证安全签名 + String signature = SHA1.gen(token, timeStamp, nonce, cipherText); + if (!signature.equals(msgSignature)) { + throw new RuntimeException("加密消息签名校验失败"); + } + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + + // 解密 + String result = decrypt(cipherText); + return result; + } + + /** + * 对密文进行解密. + * + * @param cipherText 需要解密的密文 + * @return 解密得到的明文 + */ + public String decrypt(String cipherText) { + byte[] original; + try { + // 设置解密模式为AES的CBC模式 + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); + cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); + + // 使用BASE64对密文进行解码 + byte[] encrypted = Base64.decodeBase64(cipherText); + + // 解密 + original = cipher.doFinal(encrypted); + } catch (Exception e) { + throw new RuntimeException(e); + } + + String xmlContent, from_appid; + try { + // 去除补位字符 + byte[] bytes = PKCS7Encoder.decode(original); + + // 分离16位随机字符串,网络字节序和AppId + byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); + + int xmlLength = bytesNetworkOrder2Number(networkOrder); + + xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); + from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), + CHARSET); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // appid不相同的情况 + if (!from_appid.equals(corpId)) { + throw new RuntimeException("AppID不正确"); + } + + return xmlContent; + + } + + /** + * 将一个数字转换成生成4个字节的网络字节序bytes数组 + * + * @param number + */ + private byte[] number2BytesInNetworkOrder(int number) { + byte[] orderBytes = new byte[4]; + orderBytes[3] = (byte) (number & 0xFF); + orderBytes[2] = (byte) (number >> 8 & 0xFF); + orderBytes[1] = (byte) (number >> 16 & 0xFF); + orderBytes[0] = (byte) (number >> 24 & 0xFF); + return orderBytes; + } + + /** + * 4个字节的网络字节序bytes数组还原成一个数字 + * + * @param bytesInNetworkOrder + */ + private int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { + int sourceNumber = 0; + for (int i = 0; i < 4; i++) { + sourceNumber <<= 8; + sourceNumber |= bytesInNetworkOrder[i] & 0xff; + } + return sourceNumber; + } + + /** + * 随机生成16位字符串 + * + * @return + */ + private String genRandomStr() { + String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 16; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } + + /** + * 生成xml消息 + * + * @param encrypt 加密后的消息密文 + * @param signature 安全签名 + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @return 生成的xml字符串 + */ + private String generateXml(String encrypt, String signature, String timestamp, String nonce) { + String format = + "\n" + + "\n" + + "\n" + + "%3$s\n" + + "\n" + + ""; + return String.format(format, encrypt, signature, timestamp, nonce); + } + + static String extractEncryptPart(String xml) { + try { + DocumentBuilder db = builderLocal.get(); + Document document = db.parse(new InputSource(new StringReader(xml))); + + Element root = document.getDocumentElement(); + return root.getElementsByTagName("Encrypt").item(0).getTextContent(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/fs/FileUtils.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/fs/FileUtils.java new file mode 100644 index 000000000..204e72eac --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/fs/FileUtils.java @@ -0,0 +1,47 @@ +package me.chanjar.weixin.enterprise.util.fs; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class FileUtils { + + /** + * 创建临时文件 + * @param inputStream + * @param name 文件名 + * @param ext 扩展名 + * @return + * @throws IOException + */ + public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException { + FileOutputStream fos = null; + try { + File tmpFile = File.createTempFile(name, '.' + ext); + tmpFile.deleteOnExit(); + fos = new FileOutputStream(tmpFile); + int read = 0; + byte[] bytes = new byte[1024 * 100]; + while ((read = inputStream.read(bytes)) != -1) { + fos.write(bytes, 0, read); + } + fos.flush(); + return tmpFile; + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + } + } + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + } + } + } + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/InputStreamResponseHandler.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/InputStreamResponseHandler.java new file mode 100644 index 000000000..3b7fa2dde --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/InputStreamResponseHandler.java @@ -0,0 +1,27 @@ +package me.chanjar.weixin.enterprise.util.http; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.ResponseHandler; +import org.apache.http.util.EntityUtils; + +public class InputStreamResponseHandler implements ResponseHandler { + + public static final ResponseHandler INSTANCE = new InputStreamResponseHandler(); + + public InputStream handleResponse(final HttpResponse response) throws HttpResponseException, IOException { + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity entity = response.getEntity(); + if (statusLine.getStatusCode() >= 300) { + EntityUtils.consume(entity); + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + return entity == null ? null : entity.getContent(); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/MediaDownloadRequestExecutor.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/MediaDownloadRequestExecutor.java new file mode 100644 index 000000000..6ae419071 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/MediaDownloadRequestExecutor.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.enterprise.util.http; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.ContentType; + +import me.chanjar.weixin.enterprise.bean.result.WxError; +import me.chanjar.weixin.enterprise.exception.WxErrorException; +import me.chanjar.weixin.enterprise.util.fs.FileUtils; + +/** + * 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File + * @author Daniel Qian + * + */ +public class MediaDownloadRequestExecutor implements RequestExecutor { + + @Override + public File execute(String uri, String queryParam) throws WxErrorException, ClientProtocolException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + HttpGet httpGet = new HttpGet(uri); + CloseableHttpResponse response = httpclient.execute(httpGet); + + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0) { + // 下载媒体文件出错 + if (ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent)); + } + } + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response); + + // 视频文件不支持下载 + String fileName = getFileName(response); + if (StringUtils.isBlank(fileName)) { + return null; + } + String[] name_ext = fileName.split("\\."); + File localFile = FileUtils.createTmpFile(inputStream, name_ext[0], name_ext[1]); + return localFile; + } + + protected String getFileName(CloseableHttpResponse response) { + Header[] contentDispositionHeader = response.getHeaders("Content-disposition"); + Pattern p = Pattern.compile(".*filename=\"(.*)\""); + Matcher m = p.matcher(contentDispositionHeader[0].getValue()); + m.matches(); + String fileName = m.group(1); + return fileName; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/MediaUploadRequestExecutor.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/MediaUploadRequestExecutor.java new file mode 100644 index 000000000..96ef28c20 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/MediaUploadRequestExecutor.java @@ -0,0 +1,44 @@ +package me.chanjar.weixin.enterprise.util.http; + +import java.io.File; +import java.io.IOException; + +import org.apache.http.HttpEntity; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.MultipartEntityBuilder; + +import me.chanjar.weixin.enterprise.bean.result.WxError; +import me.chanjar.weixin.enterprise.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +/** + * 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String + * @author Daniel Qian + * + */ +public class MediaUploadRequestExecutor implements RequestExecutor { + + @Override + public WxMediaUploadResult execute(String uri, File file) throws WxErrorException, ClientProtocolException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (file != null) { + HttpEntity entity = MultipartEntityBuilder + .create() + .addBinaryBody("media", file) + .build(); + httpPost.setEntity(entity); + httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); + } + CloseableHttpResponse response = httpclient.execute(httpPost); + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return WxMediaUploadResult.fromJson(responseContent); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/QrCodeRequestExecutor.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/QrCodeRequestExecutor.java new file mode 100644 index 000000000..71c6ac874 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/QrCodeRequestExecutor.java @@ -0,0 +1,56 @@ +package me.chanjar.weixin.enterprise.util.http; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.UUID; + +import me.chanjar.weixin.enterprise.bean.result.WxQrCodeTicket; +import me.chanjar.weixin.enterprise.exception.WxErrorException; +import me.chanjar.weixin.enterprise.util.fs.FileUtils; +import org.apache.http.Header; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.ContentType; + +import me.chanjar.weixin.enterprise.bean.result.WxError; + +/** + * 获得QrCode图片 请求执行器 + * @author Daniel Qian + * + */ +public class QrCodeRequestExecutor implements RequestExecutor { + + @Override + public File execute(String uri, WxQrCodeTicket ticket) throws WxErrorException, ClientProtocolException, IOException { + if (ticket != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? + "ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8") + : + "&ticket=" + URLEncoder.encode(ticket.getTicket(), "UTF-8"); + } + + HttpGet httpGet = new HttpGet(uri); + CloseableHttpResponse response = httpclient.execute(httpGet); + + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0) { + // 出错 + if (ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent)); + } + } + InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response); + + File localFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg"); + return localFile; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/RequestExecutor.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/RequestExecutor.java new file mode 100644 index 000000000..9f30369c1 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/RequestExecutor.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.enterprise.util.http; + +import java.io.IOException; + +import org.apache.http.client.ClientProtocolException; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +/** + * http请求执行器 + * @author Daniel Qian + * + * @param 返回值类型 + * @param 请求参数类型 + */ +public interface RequestExecutor { + + public static final CloseableHttpClient httpclient = HttpClients.createDefault(); + + public T execute(String uri, E data) throws WxErrorException, ClientProtocolException, IOException; + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/SimpleGetRequestExecutor.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/SimpleGetRequestExecutor.java new file mode 100644 index 000000000..4463e3c75 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/SimpleGetRequestExecutor.java @@ -0,0 +1,37 @@ +package me.chanjar.weixin.enterprise.util.http; + +import java.io.IOException; + +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; + +import me.chanjar.weixin.enterprise.bean.result.WxError; +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +/** + * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String + * @author Daniel Qian + * + */ +public class SimpleGetRequestExecutor implements RequestExecutor { + + @Override + public String execute(String uri, String queryParam) throws WxErrorException, ClientProtocolException, IOException { + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + HttpGet httpGet = new HttpGet(uri); + CloseableHttpResponse response = httpclient.execute(httpGet); + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return responseContent; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/SimplePostRequestExecutor.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/SimplePostRequestExecutor.java new file mode 100644 index 000000000..6cea13c1b --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/SimplePostRequestExecutor.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.enterprise.util.http; + +import java.io.IOException; + +import me.chanjar.weixin.enterprise.bean.result.WxError; +import me.chanjar.weixin.enterprise.exception.WxErrorException; +import org.apache.http.Consts; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; + +/** + * 简单的POST请求执行器,请求的参数是String, 返回的结果也是String + * @author Daniel Qian + * + */ +public class SimplePostRequestExecutor implements RequestExecutor { + + @Override + public String execute(String uri, String postEntity) throws WxErrorException, ClientProtocolException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (postEntity != null) { + StringEntity entity = new StringEntity(postEntity, Consts.UTF_8); + httpPost.setEntity(entity); + } + CloseableHttpResponse response = httpclient.execute(httpPost); + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + WxError error = WxError.fromJson(responseContent); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return responseContent; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/Utf8ResponseHandler.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/Utf8ResponseHandler.java new file mode 100644 index 000000000..21456c2dc --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/http/Utf8ResponseHandler.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.enterprise.util.http; + +import java.io.IOException; + +import org.apache.http.Consts; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.ResponseHandler; +import org.apache.http.util.EntityUtils; + +/** + * copy from {@link org.apache.http.impl.client.BasicResponseHandler} + * @author Daniel Qian + * + */ +public class Utf8ResponseHandler implements ResponseHandler { + + public static final ResponseHandler INSTANCE = new Utf8ResponseHandler(); + + public String handleResponse(final HttpResponse response) throws HttpResponseException, IOException { + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity entity = response.getEntity(); + if (statusLine.getStatusCode() >= 300) { + EntityUtils.consume(entity); + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + return entity == null ? null : EntityUtils.toString(entity, Consts.UTF_8); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/GsonHelper.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/GsonHelper.java new file mode 100644 index 000000000..5db3acc90 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/GsonHelper.java @@ -0,0 +1,115 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + + +public class GsonHelper { + + public static boolean isNull(JsonElement element) { + return element == null || element.isJsonNull(); + } + + public static boolean isNotNull(JsonElement element) { + return !isNull(element); + } + + public static Long getLong(JsonObject json, String property) { + return getAsLong(json.get(property)); + } + + public static long getPrimitiveLong(JsonObject json, String property) { + return getAsPrimitiveLong(json.get(property)); + } + + public static Integer getInteger(JsonObject json, String property) { + return getAsInteger(json.get(property)); + } + + public static int getPrimitiveInteger(JsonObject json, String property) { + return getAsPrimitiveInt(json.get(property)); + } + + public static Double getDouble(JsonObject json, String property) { + return getAsDouble(json.get(property)); + } + + public static double getPrimitiveDouble(JsonObject json, String property) { + return getAsPrimitiveDouble(json.get(property)); + } + + public static Float getFloat(JsonObject json, String property) { + return getAsFloat(json.get(property)); + } + + public static float getPrimitiveFloat(JsonObject json, String property) { + return getAsPrimitiveFloat(json.get(property)); + } + + public static Boolean getBoolean(JsonObject json, String property) { + return getAsBoolean(json.get(property)); + } + + public static String getString(JsonObject json, String property) { + return getAsString(json.get(property)); + } + + public static String getAsString(JsonElement element) { + return isNull(element) ? null : element.getAsString(); + } + + public static Long getAsLong(JsonElement element) { + return isNull(element) ? null : element.getAsLong(); + } + + public static long getAsPrimitiveLong(JsonElement element) { + Long r = getAsLong(element); + return r == null ? 0l : r; + } + + public static Integer getAsInteger(JsonElement element) { + return isNull(element) ? null : element.getAsInt(); + } + + public static int getAsPrimitiveInt(JsonElement element) { + Integer r = getAsInteger(element); + return r == null ? 0 : r; + } + + public static Boolean getAsBoolean(JsonElement element) { + return isNull(element) ? null : element.getAsBoolean(); + } + + public static boolean getAsPrimitiveBool(JsonElement element) { + Boolean r = getAsBoolean(element); + return r == null ? false : r.booleanValue(); + } + + public static Double getAsDouble(JsonElement element) { + return isNull(element) ? null : element.getAsDouble(); + } + + public static double getAsPrimitiveDouble(JsonElement element) { + Double r = getAsDouble(element); + return r == null ? 0d : r; + } + + public static Float getAsFloat(JsonElement element) { + return isNull(element) ? null : element.getAsFloat(); + } + + public static float getAsPrimitiveFloat(JsonElement element) { + Float r = getAsFloat(element); + return r == null ? 0f : r; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxAccessTokenAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxAccessTokenAdapter.java new file mode 100644 index 000000000..1ddf72a61 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxAccessTokenAdapter.java @@ -0,0 +1,36 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.enterprise.bean.WxAccessToken; + +import java.lang.reflect.Type; + +/** + * + * @author Daniel Qian + * + */ +public class WxAccessTokenAdapter implements JsonDeserializer { + + public WxAccessToken deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + WxAccessToken accessToken = new WxAccessToken(); + JsonObject accessTokenJsonObject = json.getAsJsonObject(); + + if (accessTokenJsonObject.get("access_token") != null && !accessTokenJsonObject.get("access_token").isJsonNull()) { + accessToken.setAccessToken(GsonHelper.getAsString(accessTokenJsonObject.get("access_token"))); + } + if (accessTokenJsonObject.get("expires_in") != null && !accessTokenJsonObject.get("expires_in").isJsonNull()) { + accessToken.setExpiresIn(GsonHelper.getAsPrimitiveInt(accessTokenJsonObject.get("expires_in"))); + } + return accessToken; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxCustomMessageGsonAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxCustomMessageGsonAdapter.java new file mode 100644 index 000000000..a5e973436 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxCustomMessageGsonAdapter.java @@ -0,0 +1,87 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import java.lang.reflect.Type; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * + * @author Daniel Qian + * + */ +public class WxCustomMessageGsonAdapter implements JsonSerializer { + + public JsonElement serialize(WxCustomMessage message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("touser", message.getToUser()); + messageJson.addProperty("msgtype", message.getMsgType()); + + if (WxConsts.CUSTOM_MSG_TEXT.equals(message.getMsgType())) { + JsonObject text = new JsonObject(); + text.addProperty("content", message.getContent()); + messageJson.add("text", text); + } + + if (WxConsts.CUSTOM_MSG_IMAGE.equals(message.getMsgType())) { + JsonObject image = new JsonObject(); + image.addProperty("media_id", message.getMediaId()); + messageJson.add("image", image); + } + + if (WxConsts.CUSTOM_MSG_VOICE.equals(message.getMsgType())) { + JsonObject voice = new JsonObject(); + voice.addProperty("media_id", message.getMediaId()); + messageJson.add("voice", voice); + } + + if (WxConsts.CUSTOM_MSG_VIDEO.equals(message.getMsgType())) { + JsonObject video = new JsonObject(); + video.addProperty("media_id", message.getMediaId()); + video.addProperty("thumb_media_id", message.getThumbMediaId()); + video.addProperty("title", message.getTitle()); + video.addProperty("description", message.getDescription()); + messageJson.add("video", video); + } + + if (WxConsts.CUSTOM_MSG_MUSIC.equals(message.getMsgType())) { + JsonObject music = new JsonObject(); + music.addProperty("title", message.getTitle()); + music.addProperty("description", message.getDescription()); + music.addProperty("thumb_media_id", message.getThumbMediaId()); + music.addProperty("musicurl", message.getMusicUrl()); + music.addProperty("hqmusicurl", message.getHqMusicUrl()); + messageJson.add("music", music); + } + + if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { + JsonArray articleJsonArray = new JsonArray(); + for (WxCustomMessage.WxArticle article : message.getArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("description", article.getDescription()); + articleJson.addProperty("url", article.getUrl()); + articleJson.addProperty("picurl", article.getPicUrl()); + articleJsonArray.add(articleJson); + } + messageJson.add("articles", articleJsonArray); + } + + return messageJson; + } + +} diff --git a/src/main/java/me/chanjar/weixin/util/json/WxErrorAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxErrorAdapter.java similarity index 90% rename from src/main/java/me/chanjar/weixin/util/json/WxErrorAdapter.java rename to weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxErrorAdapter.java index 5779bbae1..5ad0b64b1 100644 --- a/src/main/java/me/chanjar/weixin/util/json/WxErrorAdapter.java +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxErrorAdapter.java @@ -6,11 +6,10 @@ * arose from modification of the original source, or other redistribution of this source * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. */ -package me.chanjar.weixin.util.json; +package me.chanjar.weixin.enterprise.util.json; import com.google.gson.*; -import me.chanjar.weixin.bean.WxAccessToken; -import me.chanjar.weixin.bean.result.WxError; +import me.chanjar.weixin.enterprise.bean.result.WxError; import java.lang.reflect.Type; diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxGroupGsonAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxGroupGsonAdapter.java new file mode 100644 index 000000000..de67e98c2 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxGroupGsonAdapter.java @@ -0,0 +1,58 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import java.lang.reflect.Type; + +import me.chanjar.weixin.enterprise.bean.WxDepartment; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * + * @author Daniel Qian + * + */ +public class WxGroupGsonAdapter implements JsonSerializer, JsonDeserializer { + + public JsonElement serialize(WxDepartment group, Type typeOfSrc, JsonSerializationContext context) { + JsonObject json = new JsonObject(); + JsonObject groupJson = new JsonObject(); + groupJson.addProperty("name", group.getName()); + groupJson.addProperty("id", group.getId()); + groupJson.addProperty("count", group.getCount()); + json.add("group", groupJson); + return json; + } + + public WxDepartment deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + WxDepartment group = new WxDepartment(); + JsonObject groupJson = json.getAsJsonObject(); + if (json.getAsJsonObject().get("group") != null) { + groupJson = json.getAsJsonObject().get("group").getAsJsonObject(); + } + if (groupJson.get("name") != null && !groupJson.get("name").isJsonNull()) { + group.setName(GsonHelper.getAsString(groupJson.get("name"))); + } + if (groupJson.get("id") != null && !groupJson.get("id").isJsonNull()) { + group.setId(GsonHelper.getAsPrimitiveLong(groupJson.get("id"))); + } + if (groupJson.get("count") != null && !groupJson.get("count").isJsonNull()) { + group.setCount(GsonHelper.getAsPrimitiveLong(groupJson.get("count"))); + } + return group; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxGsonBuilder.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxGsonBuilder.java new file mode 100644 index 000000000..618da74d6 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxGsonBuilder.java @@ -0,0 +1,36 @@ +package me.chanjar.weixin.enterprise.util.json; + + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import me.chanjar.weixin.enterprise.bean.*; +import me.chanjar.weixin.enterprise.bean.result.*; + +public class WxGsonBuilder { + + public static final GsonBuilder INSTANCE = new GsonBuilder(); + + static { + INSTANCE.disableHtmlEscaping(); + INSTANCE.registerTypeAdapter(WxCustomMessage.class, new WxCustomMessageGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMenu.class, new WxMenuGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMassNews.class, new WxMassNewsGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMassGroupMessage.class, new WxMassMessageGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMassOpenIdsMessage.class, new WxMassOpenIdsMessageGsonAdapter()); + INSTANCE.registerTypeAdapter(WxDepartment.class, new WxGroupGsonAdapter()); + INSTANCE.registerTypeAdapter(WxUser.class, new WxUserGsonAdapter()); + INSTANCE.registerTypeAdapter(WxUserList.class, new WxUserListGsonAdapter()); + INSTANCE.registerTypeAdapter(WxAccessToken.class, new WxAccessTokenAdapter()); + INSTANCE.registerTypeAdapter(WxError.class, new WxErrorAdapter()); + INSTANCE.registerTypeAdapter(WxMassVideo.class, new WxMassVideoAdapter()); + INSTANCE.registerTypeAdapter(WxMediaUploadResult.class, new WxMediaUploadResultAdapter()); + INSTANCE.registerTypeAdapter(WxMassSendResult.class, new WxMassSendResultAdapter()); + INSTANCE.registerTypeAdapter(WxMassUploadResult.class, new WxMassUploadResultAdapter()); + INSTANCE.registerTypeAdapter(WxQrCodeTicket.class, new WxQrCodeTicketAdapter()); + } + + public static Gson create() { + return INSTANCE.create(); + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassMessageGsonAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassMessageGsonAdapter.java new file mode 100644 index 000000000..d9722b8aa --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassMessageGsonAdapter.java @@ -0,0 +1,64 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import java.lang.reflect.Type; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxMassGroupMessage; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * + * @author Daniel Qian + * + */ +public class WxMassMessageGsonAdapter implements JsonSerializer { + + public JsonElement serialize(WxMassGroupMessage message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject messageJson = new JsonObject(); + + JsonObject filter = new JsonObject(); + filter.addProperty("group_id", message.getGroupId()); + messageJson.add("filter", filter); + + if (WxConsts.MASS_MSG_NEWS.equals(message.getMsgtype())) { + JsonObject sub = new JsonObject(); + sub.addProperty("media_id", message.getMediaId()); + messageJson.add(WxConsts.MASS_MSG_NEWS, sub); + } + if (WxConsts.MASS_MSG_TEXT.equals(message.getMsgtype())) { + JsonObject sub = new JsonObject(); + sub.addProperty("content", message.getContent()); + messageJson.add(WxConsts.MASS_MSG_TEXT, sub); + } + if (WxConsts.MASS_MSG_VOICE.equals(message.getMsgtype())) { + JsonObject sub = new JsonObject(); + sub.addProperty("media_id", message.getMediaId()); + messageJson.add(WxConsts.MASS_MSG_VOICE, sub); + } + if (WxConsts.MASS_MSG_IMAGE.equals(message.getMsgtype())) { + JsonObject sub = new JsonObject(); + sub.addProperty("media_id", message.getMediaId()); + messageJson.add(WxConsts.MASS_MSG_IMAGE, sub); + } + if (WxConsts.MASS_MSG_VIDEO.equals(message.getMsgtype())) { + JsonObject sub = new JsonObject(); + sub.addProperty("media_id", message.getMediaId()); + messageJson.add(WxConsts.MASS_MSG_VIDEO, sub); + } + messageJson.addProperty("msgtype", message.getMsgtype()); + return messageJson; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassNewsGsonAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassNewsGsonAdapter.java new file mode 100644 index 000000000..93ea7e40e --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassNewsGsonAdapter.java @@ -0,0 +1,55 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import java.lang.reflect.Type; + +import me.chanjar.weixin.enterprise.bean.WxMassNews; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * + * @author Daniel Qian + * + */ +public class WxMassNewsGsonAdapter implements JsonSerializer { + + public JsonElement serialize(WxMassNews message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject newsJson = new JsonObject(); + + JsonArray articleJsonArray = new JsonArray(); + for (WxMassNews.WxMassNewsArticle article : message.getArticles()) { + JsonObject articleJson = new JsonObject(); + articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); + articleJson.addProperty("title", article.getTitle()); + articleJson.addProperty("content", article.getContent()); + + if (null != article.getAuthor()) { + articleJson.addProperty("author", article.getAuthor()); + } + if (null != article.getContentSourceUrl()) { + articleJson.addProperty("content_source_url", article.getContentSourceUrl()); + } + if (null != article.getDigest()) { + articleJson.addProperty("digest", article.getDigest()); + } + articleJson.addProperty("show_cover_pic", article.isShowCoverPic() ? "1" : "0"); + articleJsonArray.add(articleJson); + } + newsJson.add("articles", articleJsonArray); + + return newsJson; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassOpenIdsMessageGsonAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassOpenIdsMessageGsonAdapter.java new file mode 100644 index 000000000..782b45dba --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassOpenIdsMessageGsonAdapter.java @@ -0,0 +1,68 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import java.lang.reflect.Type; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxMassOpenIdsMessage; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * + * @author Daniel Qian + * + */ +public class WxMassOpenIdsMessageGsonAdapter implements JsonSerializer { + + public JsonElement serialize(WxMassOpenIdsMessage message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject messageJson = new JsonObject(); + + JsonArray toUsers = new JsonArray(); + for (String openId : message.getToUsers()) { + toUsers.add(new JsonPrimitive(openId)); + } + messageJson.add("touser", toUsers); + + if (WxConsts.MASS_MSG_NEWS.equals(message.getMsgType())) { + JsonObject sub = new JsonObject(); + sub.addProperty("media_id", message.getMediaId()); + messageJson.add(WxConsts.MASS_MSG_NEWS, sub); + } + if (WxConsts.MASS_MSG_TEXT.equals(message.getMsgType())) { + JsonObject sub = new JsonObject(); + sub.addProperty("content", message.getContent()); + messageJson.add(WxConsts.MASS_MSG_TEXT, sub); + } + if (WxConsts.MASS_MSG_VOICE.equals(message.getMsgType())) { + JsonObject sub = new JsonObject(); + sub.addProperty("media_id", message.getMediaId()); + messageJson.add(WxConsts.MASS_MSG_VOICE, sub); + } + if (WxConsts.MASS_MSG_IMAGE.equals(message.getMsgType())) { + JsonObject sub = new JsonObject(); + sub.addProperty("media_id", message.getMediaId()); + messageJson.add(WxConsts.MASS_MSG_IMAGE, sub); + } + if (WxConsts.MASS_MSG_VIDEO.equals(message.getMsgType())) { + JsonObject sub = new JsonObject(); + sub.addProperty("media_id", message.getMediaId()); + messageJson.add(WxConsts.MASS_MSG_VIDEO, sub); + } + messageJson.addProperty("msgtype", message.getMsgType()); + return messageJson; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassSendResultAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassSendResultAdapter.java new file mode 100644 index 000000000..89a88bd33 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassSendResultAdapter.java @@ -0,0 +1,39 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.enterprise.bean.result.WxMassSendResult; + +import java.lang.reflect.Type; + +/** + * + * @author Daniel Qian + * + */ +public class WxMassSendResultAdapter implements JsonDeserializer { + + public WxMassSendResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + WxMassSendResult sendResult = new WxMassSendResult(); + JsonObject sendResultJsonObject = json.getAsJsonObject(); + + if (sendResultJsonObject.get("errcode") != null && !sendResultJsonObject.get("errcode").isJsonNull()) { + sendResult.setErrorCode(GsonHelper.getAsString(sendResultJsonObject.get("errcode"))); + } + if (sendResultJsonObject.get("errmsg") != null && !sendResultJsonObject.get("errmsg").isJsonNull()) { + sendResult.setErrorMsg(GsonHelper.getAsString(sendResultJsonObject.get("errmsg"))); + } + if (sendResultJsonObject.get("msg_id") != null && !sendResultJsonObject.get("msg_id").isJsonNull()) { + sendResult.setMsgId(GsonHelper.getAsString(sendResultJsonObject.get("msg_id"))); + } + return sendResult; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassUploadResultAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassUploadResultAdapter.java new file mode 100644 index 000000000..9a021871f --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassUploadResultAdapter.java @@ -0,0 +1,39 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.enterprise.bean.result.WxMassUploadResult; + +import java.lang.reflect.Type; + +/** + * + * @author Daniel Qian + * + */ +public class WxMassUploadResultAdapter implements JsonDeserializer { + + public WxMassUploadResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + WxMassUploadResult uploadResult = new WxMassUploadResult(); + JsonObject uploadResultJsonObject = json.getAsJsonObject(); + + if (uploadResultJsonObject.get("type") != null && !uploadResultJsonObject.get("type").isJsonNull()) { + uploadResult.setType(GsonHelper.getAsString(uploadResultJsonObject.get("type"))); + } + if (uploadResultJsonObject.get("media_id") != null && !uploadResultJsonObject.get("media_id").isJsonNull()) { + uploadResult.setMediaId(GsonHelper.getAsString(uploadResultJsonObject.get("media_id"))); + } + if (uploadResultJsonObject.get("created_at") != null && !uploadResultJsonObject.get("created_at").isJsonNull()) { + uploadResult.setCreatedAt(GsonHelper.getAsPrimitiveLong(uploadResultJsonObject.get("created_at"))); + } + return uploadResult; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassVideoAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassVideoAdapter.java new file mode 100644 index 000000000..221bad884 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMassVideoAdapter.java @@ -0,0 +1,31 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.enterprise.bean.WxMassVideo; + +import java.lang.reflect.Type; + +/** + * + * @author Daniel Qian + * + */ +public class WxMassVideoAdapter implements JsonSerializer { + + public JsonElement serialize(WxMassVideo message, Type typeOfSrc, JsonSerializationContext context) { + JsonObject messageJson = new JsonObject(); + messageJson.addProperty("media_id", message.getMediaId()); + messageJson.addProperty("description", message.getDescription()); + messageJson.addProperty("title", message.getTitle()); + return messageJson; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMediaUploadResultAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMediaUploadResultAdapter.java new file mode 100644 index 000000000..cbace41cf --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMediaUploadResultAdapter.java @@ -0,0 +1,42 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import com.google.gson.*; +import me.chanjar.weixin.enterprise.bean.result.WxMediaUploadResult; + +import java.lang.reflect.Type; + +/** + * + * @author Daniel Qian + * + */ +public class WxMediaUploadResultAdapter implements JsonDeserializer { + + public WxMediaUploadResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + WxMediaUploadResult uploadResult = new WxMediaUploadResult(); + JsonObject uploadResultJsonObject = json.getAsJsonObject(); + + if (uploadResultJsonObject.get("type") != null && !uploadResultJsonObject.get("type").isJsonNull()) { + uploadResult.setType(GsonHelper.getAsString(uploadResultJsonObject.get("type"))); + } + if (uploadResultJsonObject.get("media_id") != null && !uploadResultJsonObject.get("media_id").isJsonNull()) { + uploadResult.setMediaId(GsonHelper.getAsString(uploadResultJsonObject.get("media_id"))); + } + if (uploadResultJsonObject.get("thumb_media_id") != null && !uploadResultJsonObject.get("thumb_media_id").isJsonNull()) { + uploadResult.setThumbMediaId(GsonHelper.getAsString(uploadResultJsonObject.get("thumb_media_id"))); + } + if (uploadResultJsonObject.get("created_at") != null && !uploadResultJsonObject.get("created_at").isJsonNull()) { + uploadResult.setCreatedAt(GsonHelper.getAsPrimitiveLong(uploadResultJsonObject.get("created_at"))); + } + return uploadResult; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMenuGsonAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMenuGsonAdapter.java new file mode 100644 index 000000000..ac55d5a16 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxMenuGsonAdapter.java @@ -0,0 +1,94 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import java.lang.reflect.Type; + +import me.chanjar.weixin.enterprise.bean.WxMenu; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * + * @author Daniel Qian + * + */ +public class WxMenuGsonAdapter implements JsonSerializer, JsonDeserializer { + + public JsonElement serialize(WxMenu menu, Type typeOfSrc, JsonSerializationContext context) { + JsonObject json = new JsonObject(); + + JsonArray buttonArray = new JsonArray(); + for (WxMenu.WxMenuButton button : menu.getButtons()) { + JsonObject buttonJson = convertToJson(button); + buttonArray.add(buttonJson); + } + json.add("button", buttonArray); + + return json; + } + + protected JsonObject convertToJson(WxMenu.WxMenuButton button) { + JsonObject buttonJson = new JsonObject(); + buttonJson.addProperty("type", button.getType()); + buttonJson.addProperty("name", button.getName()); + buttonJson.addProperty("key", button.getKey()); + buttonJson.addProperty("url", button.getUrl()); + if (button.getSubButtons() != null && button.getSubButtons().size() > 0) { + JsonArray buttonArray = new JsonArray(); + for (WxMenu.WxMenuButton sub_button : button.getSubButtons()) { + buttonArray.add(convertToJson(sub_button)); + } + buttonJson.add("sub_button", buttonArray); + } + return buttonJson; + } + + public WxMenu deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + /* + * 操蛋的微信 + * 创建菜单时是 { button : ... } + * 查询菜单时是 { menu : { button : ... } } + */ + WxMenu menu = new WxMenu(); + JsonObject menuJson = json.getAsJsonObject().get("menu").getAsJsonObject(); + JsonArray buttonsJson = menuJson.get("button").getAsJsonArray(); + for (int i = 0; i < buttonsJson.size(); i++) { + JsonObject buttonJson = buttonsJson.get(i).getAsJsonObject(); + WxMenu.WxMenuButton button = convertFromJson(buttonJson); + menu.getButtons().add(button); + if (buttonJson.get("sub_button") == null || buttonJson.get("sub_button").isJsonNull()) { + continue; + } + JsonArray sub_buttonsJson = buttonJson.get("sub_button").getAsJsonArray(); + for (int j = 0; j < sub_buttonsJson.size(); j++) { + JsonObject sub_buttonJson = sub_buttonsJson.get(j).getAsJsonObject(); + button.getSubButtons().add(convertFromJson(sub_buttonJson)); + } + } + return menu; + } + + protected WxMenu.WxMenuButton convertFromJson(JsonObject json) { + WxMenu.WxMenuButton button = new WxMenu.WxMenuButton(); + button.setName(GsonHelper.getString(json, "name")); + button.setKey(GsonHelper.getString(json, "key")); + button.setUrl(GsonHelper.getString(json, "url")); + button.setType(GsonHelper.getString(json, "type")); + return button; + } + +} diff --git a/src/main/java/me/chanjar/weixin/util/json/WxQrCodeTicketAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxQrCodeTicketAdapter.java similarity index 90% rename from src/main/java/me/chanjar/weixin/util/json/WxQrCodeTicketAdapter.java rename to weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxQrCodeTicketAdapter.java index f50d9fb0c..917417f5b 100644 --- a/src/main/java/me/chanjar/weixin/util/json/WxQrCodeTicketAdapter.java +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxQrCodeTicketAdapter.java @@ -6,11 +6,10 @@ * arose from modification of the original source, or other redistribution of this source * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. */ -package me.chanjar.weixin.util.json; +package me.chanjar.weixin.enterprise.util.json; import com.google.gson.*; -import me.chanjar.weixin.bean.result.WxMassUploadResult; -import me.chanjar.weixin.bean.result.WxQrCodeTicket; +import me.chanjar.weixin.enterprise.bean.result.WxQrCodeTicket; import java.lang.reflect.Type; diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxUserGsonAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxUserGsonAdapter.java new file mode 100644 index 000000000..94ae4e5a9 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxUserGsonAdapter.java @@ -0,0 +1,52 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import java.lang.reflect.Type; + +import me.chanjar.weixin.enterprise.bean.result.WxUser; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +/** + * + * @author Daniel Qian + * + */ +public class WxUserGsonAdapter implements JsonDeserializer { + + public WxUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject o = json.getAsJsonObject(); + WxUser wxUser = new WxUser(); + wxUser.setSubscribe(new Integer(0).equals(GsonHelper.getInteger(o, "subscribe")) ? false : true); + wxUser.setCity(GsonHelper.getString(o, "city")); + wxUser.setCountry(GsonHelper.getString(o, "country")); + wxUser.setHeadImgUrl(GsonHelper.getString(o, "headimgurl")); + wxUser.setLanguage(GsonHelper.getString(o, "language")); + wxUser.setNickname(GsonHelper.getString(o, "nickname")); + wxUser.setOpenId(GsonHelper.getString(o, "openid")); + wxUser.setProvince(GsonHelper.getString(o, "province")); + wxUser.setSubscribeTime(GsonHelper.getLong(o, "subscribe_time")); + wxUser.setUnionId(GsonHelper.getString(o, "unionid")); + Integer sex = GsonHelper.getInteger(o, "sex"); + if(new Integer(1).equals(sex)) { + wxUser.setSex("男"); + } else if (new Integer(2).equals(sex)) { + wxUser.setSex("女"); + } else { + wxUser.setSex("未知"); + } + return wxUser; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxUserListGsonAdapter.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxUserListGsonAdapter.java new file mode 100644 index 000000000..414951102 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/json/WxUserListGsonAdapter.java @@ -0,0 +1,42 @@ +/* + * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. + * + * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended + * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction + * arose from modification of the original source, or other redistribution of this source + * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. + */ +package me.chanjar.weixin.enterprise.util.json; + +import java.lang.reflect.Type; + +import me.chanjar.weixin.enterprise.bean.result.WxUserList; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +/** + * + * @author Daniel Qian + * + */ +public class WxUserListGsonAdapter implements JsonDeserializer { + + public WxUserList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject o = json.getAsJsonObject(); + WxUserList wxUserList = new WxUserList(); + wxUserList.setTotal(GsonHelper.getInteger(o, "total")); + wxUserList.setCount(GsonHelper.getInteger(o, "count")); + wxUserList.setNextOpenId(GsonHelper.getString(o, "next_openid")); + JsonArray data = o.get("data").getAsJsonObject().get("openid").getAsJsonArray(); + for (int i = 0; i < data.size(); i++) { + wxUserList.getOpenIds().add(GsonHelper.getAsString(data.get(i))); + } + return wxUserList; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/AdapterCDATA.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/AdapterCDATA.java new file mode 100644 index 000000000..c45516326 --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/AdapterCDATA.java @@ -0,0 +1,24 @@ +package me.chanjar.weixin.enterprise.util.xml; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * + * http://stackoverflow.com/questions/14193944/jaxb-marshalling-unmarshalling-with-cdata + * + * @author Daniel Qian + * + */ +public class AdapterCDATA extends XmlAdapter { + + @Override + public String marshal(String arg0) throws Exception { + return ""; + } + + @Override + public String unmarshal(String arg0) throws Exception { + return arg0; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/MediaIdMarshaller.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/MediaIdMarshaller.java new file mode 100644 index 000000000..93ed2ab0c --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/MediaIdMarshaller.java @@ -0,0 +1,21 @@ +package me.chanjar.weixin.enterprise.util.xml; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * @author Daniel Qian + */ +public class MediaIdMarshaller extends XmlAdapter { + + @Override + public String marshal(String arg0) throws Exception { + return ""; + } + + @Override + public String unmarshal(String arg0) throws Exception { + // do nothing + return arg0; + } + +} diff --git a/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/XmlTransformer.java b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/XmlTransformer.java new file mode 100644 index 000000000..5504c273c --- /dev/null +++ b/weixin-java-enterprise/src/main/java/me/chanjar/weixin/enterprise/util/xml/XmlTransformer.java @@ -0,0 +1,94 @@ +package me.chanjar.weixin.enterprise.util.xml; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; + +import javax.xml.bind.*; + +import me.chanjar.weixin.enterprise.bean.*; +import me.chanjar.weixin.enterprise.bean.*; +import org.xml.sax.InputSource; + +import com.sun.xml.bind.marshaller.CharacterEscapeHandler; + +public class XmlTransformer { + + protected static final JAXBContext JAXB_CONTEXT = initJAXBContext(); + + /** + * xml -> pojo + * + * @param clazz + * @param xml + * @return + * @throws JAXBException + */ + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, String xml) throws JAXBException { + Unmarshaller um = JAXB_CONTEXT.createUnmarshaller(); + T object = (T) um.unmarshal(new StringReader(xml)); + return object; + } + + @SuppressWarnings("unchecked") + public static T fromXml(Class clazz, InputStream is) throws JAXBException { + Unmarshaller um = JAXB_CONTEXT.createUnmarshaller(); + InputSource inputSource = new InputSource(is); + inputSource.setEncoding("utf-8"); + T object = (T) um.unmarshal(inputSource); + return object; + } + + /** + * pojo -> xml + * + * @param clazz + * @param object + * @return + * @throws JAXBException + */ + public static String toXml(Class clazz, T object) throws JAXBException { + StringWriter stringWriter = new StringWriter(); + toXml(clazz, object, stringWriter); + return stringWriter.getBuffer().toString(); + } + + public static void toXml(Class clazz, T object, Writer writer) throws JAXBException { + Marshaller m = JAXB_CONTEXT.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER); + m.marshal(object, writer); + } + + protected static final CharacterEscapeHandler CHAR_ESCAPE_HANDLER = new CharacterUnescapeHandler(); + + protected static class CharacterUnescapeHandler implements CharacterEscapeHandler { + public void escape(char[] ac, int i, int j, boolean flag, Writer writer) throws IOException { + writer.write(ac, i, j); + } + } + + private static JAXBContext initJAXBContext() { + /* + * JAXBContext对象是线程安全的,根据官方文档的建议将对象作为全局实例 + * https://jaxb.java.net/guide/Performance_and_thread_safety.html + */ + try { + return JAXBContext.newInstance( + WxXmlOutMessage.class, + WxXmlOutImageMessage.class, + WxXmlOutMewsMessage.class, + WxXmlOutMusicMessage.class, + WxXmlOutTextMessage.class, + WxXmlOutVideoMessage.class, + WxXmlOutVoiceMessage.class, + WxCpXmlMessage.class); + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/ApiTestModule.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/ApiTestModule.java new file mode 100644 index 000000000..fd82b3dfc --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/ApiTestModule.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.enterprise.api; + +import java.io.InputStream; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +import com.google.inject.Binder; +import com.google.inject.Module; +import org.xml.sax.InputSource; + +public class ApiTestModule implements Module { + + @Override + public void configure(Binder binder) { + try { + InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); + WxXmlCpConfigStorage config = fromXml(WxXmlCpConfigStorage.class, is1); + WxCpServiceImpl wxService = new WxCpServiceImpl(); + wxService.setWxCpConfigStorage(config); + + binder.bind(WxCpServiceImpl.class).toInstance(wxService); + binder.bind(WxCpConfigStorage.class).toInstance(config); + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + + public static T fromXml(Class clazz, InputStream is) throws JAXBException { + Unmarshaller um = JAXBContext.newInstance(clazz).createUnmarshaller(); + InputSource inputSource = new InputSource(is); + inputSource.setEncoding("utf-8"); + T object = (T) um.unmarshal(inputSource); + return object; + } + + @XmlRootElement(name = "xml") + @XmlAccessorType(XmlAccessType.FIELD) + public static class WxXmlCpConfigStorage extends WxInMemoryCpConfigStorage { + + protected String openId; + + public String getOpenId() { + return openId; + } + public void setOpenId(String openId) { + this.openId = openId; + } + @Override + public String toString() { + return "SimpleWxConfigProvider [corpId=" + corpId + ", corpSecret=" + corpSecret + ", accessToken=" + accessToken + + ", expiresIn=" + expiresIn + ", token=" + token + ", openId=" + openId + "]"; + } + + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxBaseAPITest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxBaseAPITest.java new file mode 100644 index 000000000..453ea36a0 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxBaseAPITest.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.enterprise.api; + +import org.apache.commons.lang3.StringUtils; +import org.testng.Assert; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +import com.google.inject.Inject; + +/** + * 基础API测试 + * @author Daniel Qian + * + */ +@Test(groups = "baseAPI") +@Guice(modules = ApiTestModule.class) +public class WxBaseAPITest { + + @Inject + protected WxCpServiceImpl wxService; + + public void testRefreshAccessToken() throws WxErrorException { + WxCpConfigStorage configStorage = wxService.wxCpConfigStorage; + String before = configStorage.getAccessToken(); + wxService.accessTokenRefresh(); + + String after = configStorage.getAccessToken(); + + Assert.assertNotEquals(before, after); + Assert.assertTrue(StringUtils.isNotBlank(after)); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxCpMessageRouterTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxCpMessageRouterTest.java new file mode 100644 index 000000000..1a2e8bbf1 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxCpMessageRouterTest.java @@ -0,0 +1,159 @@ +package me.chanjar.weixin.enterprise.api; + +import java.util.Map; + +import me.chanjar.weixin.enterprise.bean.WxCpXmlMessage; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; + +/** + * 测试消息路由器 + * @author Daniel Qian + * + */ +@Test +public class WxCpMessageRouterTest { + + @Test(enabled = false) + public void prepare(boolean async, StringBuffer sb, WxCpMessageRouter router) { + router + .rule() + .async(async) + .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1").content("CONTENT_1") + .handler(new WxEchoCpMessageHandler(sb, "COMBINE_4")) + .end() + .rule() + .async(async) + .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1") + .handler(new WxEchoCpMessageHandler(sb, "COMBINE_3")) + .end() + .rule() + .async(async) + .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK) + .handler(new WxEchoCpMessageHandler(sb, "COMBINE_2")) + .end() + .rule().async(async).msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoCpMessageHandler(sb, WxConsts.XML_MSG_TEXT)).end() + .rule().async(async).event(WxConsts.EVT_CLICK).handler(new WxEchoCpMessageHandler(sb, WxConsts.EVT_CLICK)).end() + .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).handler(new WxEchoCpMessageHandler(sb, "ALL")).end(); + ; + } + + @Test(dataProvider="messages-1") + public void testSync(WxCpXmlMessage message, String expected) { + StringBuffer sb = new StringBuffer(); + WxCpMessageRouter router = new WxCpMessageRouter(); + prepare(false, sb, router); + router.route(message); + Assert.assertEquals(sb.toString(), expected); + } + + @Test(dataProvider="messages-1") + public void testAsync(WxCpXmlMessage message, String expected) throws InterruptedException { + StringBuffer sb = new StringBuffer(); + WxCpMessageRouter router = new WxCpMessageRouter(); + prepare(true, sb, router); + router.route(message); + Thread.sleep(500l); + Assert.assertEquals(sb.toString(), expected); + } + + public void testConcurrency() throws InterruptedException { + final WxCpMessageRouter router = new WxCpMessageRouter(); + router.rule().handler(new WxCpMessageHandler() { + @Override + public WxXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context) { + return null; + } + }).end(); + + final WxCpXmlMessage m = new WxCpXmlMessage(); + 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() { + WxCpXmlMessage message1 = new WxCpXmlMessage(); + message1.setMsgType(WxConsts.XML_MSG_TEXT); + + WxCpXmlMessage message2 = new WxCpXmlMessage(); + message2.setEvent(WxConsts.EVT_CLICK); + + WxCpXmlMessage message3 = new WxCpXmlMessage(); + message3.setEventKey("KEY_1"); + + WxCpXmlMessage message4 = new WxCpXmlMessage(); + message4.setContent("CONTENT_1"); + + WxCpXmlMessage message5 = new WxCpXmlMessage(); + message5.setContent("BLA"); + + WxCpXmlMessage message6 = new WxCpXmlMessage(); + message6.setContent("abcd"); + + WxCpXmlMessage c2 = new WxCpXmlMessage(); + c2.setMsgType(WxConsts.XML_MSG_TEXT); + c2.setEvent(WxConsts.EVT_CLICK); + + WxCpXmlMessage c3 = new WxCpXmlMessage(); + c3.setMsgType(WxConsts.XML_MSG_TEXT); + c3.setEvent(WxConsts.EVT_CLICK); + c3.setEventKey("KEY_1"); + + WxCpXmlMessage c4 = new WxCpXmlMessage(); + c4.setMsgType(WxConsts.XML_MSG_TEXT); + 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 + "," }, + new Object[] { message3, "KEY_1," }, + new Object[] { message4, "CONTENT_1," }, + new Object[] { message5, "ALL," }, + new Object[] { message6, "abcd," }, + new Object[] { c2, "COMBINE_2," }, + new Object[] { c3, "COMBINE_3," }, + new Object[] { c4, "COMBINE_4," } + }; + + } + + public static class WxEchoCpMessageHandler implements WxCpMessageHandler { + + private StringBuffer sb; + private String echoStr; + + public WxEchoCpMessageHandler(StringBuffer sb, String echoStr) { + this.sb = sb; + this.echoStr = echoStr; + } + + @Override + public WxXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context) { + sb.append(this.echoStr).append(','); + return null; + } + + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxCustomMessageAPITest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxCustomMessageAPITest.java new file mode 100644 index 000000000..d11fd55b9 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxCustomMessageAPITest.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.enterprise.api; + +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +import com.google.inject.Inject; + +/*** + * 测试发送客服消息 + * @author Daniel Qian + * + */ +@Test(groups="customMessageAPI", dependsOnGroups = "baseAPI") +@Guice(modules = ApiTestModule.class) +public class WxCustomMessageAPITest { + + @Inject + protected WxCpServiceImpl wxService; + + public void testSendCustomMessage() throws WxErrorException { + ApiTestModule.WxXmlCpConfigStorage configStorage = (ApiTestModule.WxXmlCpConfigStorage) wxService.wxCpConfigStorage; + WxCustomMessage message = new WxCustomMessage(); + message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); + message.setToUser(configStorage.getOpenId()); + message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World"); + + wxService.messageSend(message); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxGroupAPITest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxGroupAPITest.java new file mode 100644 index 000000000..0d8b1df75 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxGroupAPITest.java @@ -0,0 +1,50 @@ +package me.chanjar.weixin.enterprise.api; + +import java.util.List; + +import me.chanjar.weixin.enterprise.bean.WxDepartment; +import org.testng.Assert; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +import com.google.inject.Inject; + +/** + * 测试分组接口 + * + * @author Daniel Qian + */ +@Test(groups = "groupAPI", dependsOnGroups = "baseAPI") +@Guice(modules = ApiTestModule.class) +public class WxGroupAPITest { + + @Inject + protected WxCpServiceImpl wxService; + + protected WxDepartment group; + + public void testGroupCreate() throws WxErrorException { + WxDepartment res = wxService.departmentCreate("测试分组1"); + Assert.assertEquals(res.getName(), "测试分组1"); + } + + @Test(dependsOnMethods="testGroupCreate") + public void testGroupGet() throws WxErrorException { + List groupList = wxService.departmentGet(); + Assert.assertNotNull(groupList); + Assert.assertTrue(groupList.size() > 0); + for (WxDepartment g : groupList) { + group = g; + Assert.assertNotNull(g.getName()); + } + } + + @Test(dependsOnMethods={"testGroupGet", "testGroupCreate"}) + public void getGroupUpdate() throws WxErrorException { + group.setName("分组改名"); + wxService.departmentUpdate(group); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxMediaAPITest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxMediaAPITest.java new file mode 100644 index 000000000..2c26f146e --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxMediaAPITest.java @@ -0,0 +1,72 @@ +package me.chanjar.weixin.enterprise.api; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import me.chanjar.weixin.enterprise.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +import com.google.inject.Inject; + +/** + * 测试多媒体文件上传下载 + * @author Daniel Qian + * + */ +@Test(groups="mediaAPI", dependsOnGroups="baseAPI") +@Guice(modules = ApiTestModule.class) +public class WxMediaAPITest { + + @Inject + protected WxCpServiceImpl wxService; + + private List media_ids = new ArrayList(); + + @Test(dataProvider="uploadMedia") + public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException { + InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName); + WxMediaUploadResult res = wxService.mediaUpload(mediaType, fileType, inputStream); + Assert.assertNotNull(res.getType()); + Assert.assertNotNull(res.getCreatedAt()); + Assert.assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null); + + if (res.getMediaId() != null) { + media_ids.add(res.getMediaId()); + } + if (res.getThumbMediaId() != null) { + media_ids.add(res.getThumbMediaId()); + } + } + + @DataProvider + public Object[][] uploadMedia() { + return new Object[][] { + new Object[] { WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, "mm.jpeg" }, + new Object[] { WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, "mm.mp3" }, + new Object[] { WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, "mm.mp4" }, + new Object[] { WxConsts.MEDIA_THUMB, WxConsts.FILE_JPG, "mm.jpeg" } + }; + } + + @Test(dependsOnMethods = { "testUploadMedia" }, dataProvider="downloadMedia") + public void testDownloadMedia(String media_id) throws WxErrorException { + wxService.mediaDownload(media_id); + } + + @DataProvider + public Object[][] downloadMedia() { + Object[][] params = new Object[this.media_ids.size()][]; + for (int i = 0; i < this.media_ids.size(); i++) { + params[i] = new Object[] { this.media_ids.get(i) }; + } + return params; + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxMenuAPITest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxMenuAPITest.java new file mode 100644 index 000000000..1f9f2f3c2 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/api/WxMenuAPITest.java @@ -0,0 +1,91 @@ +package me.chanjar.weixin.enterprise.api; + +import javax.xml.bind.JAXBException; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import com.google.inject.Inject; + +import me.chanjar.weixin.enterprise.bean.WxMenu; +import me.chanjar.weixin.enterprise.bean.WxMenu.WxMenuButton; +import me.chanjar.weixin.enterprise.exception.WxErrorException; + +/** + * 测试菜单 + * @author Daniel Qian + * + */ +@Test(groups="menuAPI", dependsOnGroups="baseAPI") +@Guice(modules = ApiTestModule.class) +public class WxMenuAPITest { + + @Inject + protected WxCpServiceImpl wxService; + + @Test(dataProvider = "menu") + public void testCreateMenu(WxMenu wxMenu) throws WxErrorException { + wxService.menuCreate(wxMenu); + } + + @Test(dependsOnMethods = { "testCreateMenu"}) + public void testGetMenu() throws WxErrorException { + Assert.assertNotNull(wxService.menuGet()); + } + + @Test(dependsOnMethods = { "testGetMenu"}) + public void testDeleteMenu() throws WxErrorException { + wxService.menuDelete(); + } + + @DataProvider(name="menu") + public Object[][] getMenu() throws JAXBException { + WxMenu menu = new WxMenu(); + WxMenuButton button1 = new WxMenuButton(); + button1.setType(WxConsts.BUTTON_CLICK); + button1.setName("今日歌曲"); + button1.setKey("V1001_TODAY_MUSIC"); + + WxMenuButton button2 = new WxMenuButton(); + button2.setType(WxConsts.BUTTON_CLICK); + button2.setName("歌手简介"); + button2.setKey("V1001_TODAY_SINGER"); + + WxMenuButton button3 = new WxMenuButton(); + button3.setName("菜单"); + + menu.getButtons().add(button1); + menu.getButtons().add(button2); + menu.getButtons().add(button3); + + WxMenuButton button31 = new WxMenuButton(); + button31.setType(WxConsts.BUTTON_VIEW); + button31.setName("搜索"); + button31.setUrl("http://www.soso.com/"); + + WxMenuButton button32 = new WxMenuButton(); + button32.setType(WxConsts.BUTTON_VIEW); + button32.setName("视频"); + button32.setUrl("http://v.qq.com/"); + + WxMenuButton button33 = new WxMenuButton(); + button33.setType(WxConsts.BUTTON_CLICK); + button33.setName("赞一下我们"); + button33.setKey("V1001_GOOD"); + + button3.getSubButtons().add(button31); + button3.getSubButtons().add(button32); + button3.getSubButtons().add(button33); + + return new Object[][] { + new Object[] { + menu + } + }; + + } + + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxAccessTokenTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxAccessTokenTest.java new file mode 100644 index 000000000..e0c4c59da --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxAccessTokenTest.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxAccessToken; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WxAccessTokenTest { + + public void testFromJson() { + + String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}"; + WxAccessToken wxError = WxAccessToken.fromJson(json); + Assert.assertEquals(wxError.getAccessToken(), "ACCESS_TOKEN"); + Assert.assertTrue(wxError.getExpiresIn() == 7200); + + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxCpXmlMessageTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxCpXmlMessageTest.java new file mode 100644 index 000000000..d84bc269a --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxCpXmlMessageTest.java @@ -0,0 +1,91 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WxCpXmlMessageTest { + + public void testFromXml() { + + String xml = "" + + "" + + " " + + "1348831860" + + "" + + "" + + "1234567890123456" + + "" + + "" + + "" + + "" + + "23.134521" + + "113.358803" + + "20" + + "" + + "" + + "" + + "<![CDATA[公众平台官网链接]]>" + + "" + + "" + + "" + + "23.137466" + + "113.352425" + + "119.385040" + + "" + + " " + + " " + + "" + + "" + + " 1\n" + + " " + + " " + + " " + + " " + + " " + + "" + + "" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "" + + ""; + WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); + Assert.assertEquals(wxMessage.getToUserName(), "toUser"); + Assert.assertEquals(wxMessage.getFromUserName(), "fromUser"); + Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860l)); + Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); + Assert.assertEquals(wxMessage.getContent(), "this is a test"); + Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l)); + Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); + Assert.assertEquals(wxMessage.getMediaId(), "media_id"); + Assert.assertEquals(wxMessage.getFormat(), "Format"); + Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); + Assert.assertEquals(wxMessage.getLocationX(), new Double(23.134521d)); + Assert.assertEquals(wxMessage.getLocationY(), new Double(113.358803d)); + Assert.assertEquals(wxMessage.getScale(), new Double(20)); + Assert.assertEquals(wxMessage.getLabel(), "位置信息"); + Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); + Assert.assertEquals(wxMessage.getUrl(), "url"); + Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); + Assert.assertEquals(wxMessage.getEvent(), "subscribe"); + Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); + Assert.assertEquals(wxMessage.getTicket(), "TICKET"); + Assert.assertEquals(wxMessage.getLatitude(), new Double(23.137466)); + Assert.assertEquals(wxMessage.getLongitude(), new Double(113.352425)); + Assert.assertEquals(wxMessage.getPrecision(), new Double(119.385040)); + Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); + Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); + Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1l)); + Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); + Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); + Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); + Assert.assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); + Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); + Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxCustomMessageTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxCustomMessageTest.java new file mode 100644 index 000000000..bbd8f6fa8 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxCustomMessageTest.java @@ -0,0 +1,133 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxCustomMessage; +import org.testng.Assert; +import org.testng.annotations.Test; + +import me.chanjar.weixin.enterprise.api.WxConsts; +import me.chanjar.weixin.enterprise.bean.WxCustomMessage.WxArticle; + +@Test +public class WxCustomMessageTest { + + public void testTextReply() { + WxCustomMessage reply = new WxCustomMessage(); + reply.setToUser("OPENID"); + reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT); + reply.setContent("sfsfdsdf"); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + } + + public void testTextBuild() { + WxCustomMessage reply = WxCustomMessage.TEXT().toUser("OPENID").content("sfsfdsdf").build(); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); + } + + public void testImageReply() { + WxCustomMessage reply = new WxCustomMessage(); + reply.setToUser("OPENID"); + reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE); + reply.setMediaId("MEDIA_ID"); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + } + + public void testImageBuild() { + WxCustomMessage reply = WxCustomMessage.IMAGE().toUser("OPENID").mediaId("MEDIA_ID").build(); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); + } + + public void testVoiceReply() { + WxCustomMessage reply = new WxCustomMessage(); + reply.setToUser("OPENID"); + reply.setMsgType(WxConsts.CUSTOM_MSG_VOICE); + reply.setMediaId("MEDIA_ID"); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); + } + + public void testVoiceBuild() { + WxCustomMessage reply = WxCustomMessage.VOICE().toUser("OPENID").mediaId("MEDIA_ID").build(); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); + } + + public void testVideoReply() { + WxCustomMessage reply = new WxCustomMessage(); + reply.setToUser("OPENID"); + reply.setMsgType(WxConsts.CUSTOM_MSG_VIDEO); + reply.setMediaId("MEDIA_ID"); + reply.setThumbMediaId("MEDIA_ID"); + reply.setTitle("TITLE"); + reply.setDescription("DESCRIPTION"); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); + } + + public void testVideoBuild() { + WxCustomMessage reply = WxCustomMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); + } + + public void testMusicReply() { + WxCustomMessage reply = new WxCustomMessage(); + reply.setToUser("OPENID"); + reply.setMsgType(WxConsts.CUSTOM_MSG_MUSIC); + reply.setThumbMediaId("MEDIA_ID"); + reply.setDescription("DESCRIPTION"); + reply.setTitle("TITLE"); + reply.setMusicUrl("MUSIC_URL"); + reply.setHqMusicUrl("HQ_MUSIC_URL"); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"music\",\"music\":{\"title\":\"TITLE\",\"description\":\"DESCRIPTION\",\"thumb_media_id\":\"MEDIA_ID\",\"musicurl\":\"MUSIC_URL\",\"hqmusicurl\":\"HQ_MUSIC_URL\"}}"); + } + + public void testMusicBuild() { + WxCustomMessage reply = WxCustomMessage.MUSIC() + .toUser("OPENID") + .title("TITLE") + .thumbMediaId("MEDIA_ID") + .description("DESCRIPTION") + .musicUrl("MUSIC_URL") + .hqMusicUrl("HQ_MUSIC_URL") + .build(); + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"music\",\"music\":{\"title\":\"TITLE\",\"description\":\"DESCRIPTION\",\"thumb_media_id\":\"MEDIA_ID\",\"musicurl\":\"MUSIC_URL\",\"hqmusicurl\":\"HQ_MUSIC_URL\"}}"); + } + + public void testNewsReply() { + WxCustomMessage reply = new WxCustomMessage(); + reply.setToUser("OPENID"); + reply.setMsgType(WxConsts.CUSTOM_MSG_NEWS); + + WxArticle article1 = new WxArticle(); + article1.setUrl("URL"); + article1.setPicUrl("PIC_URL"); + article1.setDescription("Is Really A Happy Day"); + article1.setTitle("Happy Day"); + reply.getArticles().add(article1); + + WxArticle article2 = new WxArticle(); + article2.setUrl("URL"); + article2.setPicUrl("PIC_URL"); + article2.setDescription("Is Really A Happy Day"); + article2.setTitle("Happy Day"); + reply.getArticles().add(article2); + + + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); + } + + public void testNewsBuild() { + WxArticle article1 = new WxArticle(); + article1.setUrl("URL"); + article1.setPicUrl("PIC_URL"); + article1.setDescription("Is Really A Happy Day"); + article1.setTitle("Happy Day"); + + WxArticle article2 = new WxArticle(); + article2.setUrl("URL"); + article2.setPicUrl("PIC_URL"); + article2.setDescription("Is Really A Happy Day"); + article2.setTitle("Happy Day"); + + WxCustomMessage reply = WxCustomMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); + + Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxErrorTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxErrorTest.java new file mode 100644 index 000000000..5a6545401 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxErrorTest.java @@ -0,0 +1,38 @@ +package me.chanjar.weixin.enterprise.bean; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import me.chanjar.weixin.enterprise.bean.result.WxError; + +@Test +public class WxErrorTest { + + public void testFromJson() { + + String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\" }"; + WxError wxError = WxError.fromJson(json); + Assert.assertTrue(wxError.getErrorCode() == 40003); + Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); + + } + + public void testFromBadJson1() { + + String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\", \"media_id\": \"12323423dsfafsf232f\" }"; + WxError wxError = WxError.fromJson(json); + Assert.assertTrue(wxError.getErrorCode() == 40003); + Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); + + } + + public void testFromBadJson2() { + + String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}"; + WxError wxError = WxError.fromJson(json); + Assert.assertTrue(wxError.getErrorCode() == 0); + Assert.assertEquals(wxError.getErrorMsg(), null); + + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxMenuTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxMenuTest.java new file mode 100644 index 000000000..86025571e --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxMenuTest.java @@ -0,0 +1,112 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxMenu; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import me.chanjar.weixin.enterprise.bean.WxMenu.WxMenuButton; + +@Test +public class WxMenuTest { + + @Test(dataProvider="wxReturnMenu") + public void testFromJson(String json) { + WxMenu menu = WxMenu.fromJson(json); + Assert.assertEquals(menu.getButtons().size(), 3); + } + + @Test(dataProvider="wxPushMenu") + public void testToJson(String json) { + WxMenu menu = new WxMenu(); + WxMenuButton button1 = new WxMenuButton(); + button1.setType("click"); + button1.setName("今日歌曲"); + button1.setKey("V1001_TODAY_MUSIC"); + + WxMenuButton button2 = new WxMenuButton(); + button2.setType("click"); + button2.setName("歌手简介"); + button2.setKey("V1001_TODAY_SINGER"); + + WxMenuButton button3 = new WxMenuButton(); + button3.setName("菜单"); + + menu.getButtons().add(button1); + menu.getButtons().add(button2); + menu.getButtons().add(button3); + + WxMenuButton button31 = new WxMenuButton(); + button31.setType("view"); + button31.setName("搜索"); + button31.setUrl("http://www.soso.com/"); + + WxMenuButton button32 = new WxMenuButton(); + button32.setType("view"); + button32.setName("视频"); + button32.setUrl("http://v.qq.com/"); + + WxMenuButton button33 = new WxMenuButton(); + button33.setType("click"); + button33.setName("赞一下我们"); + button33.setKey("V1001_GOOD"); + + button3.getSubButtons().add(button31); + button3.getSubButtons().add(button32); + button3.getSubButtons().add(button33); + + Assert.assertEquals(menu.toJson(), json); + } + + @DataProvider + public Object[][] wxReturnMenu() { + Object[][] res = menuJson(); + String json = "{ \"menu\" : " + res[0][0] + " }"; + return new Object[][] { + new Object[] { json } + }; + } + + @DataProvider(name="wxPushMenu") + public Object[][] menuJson() { + String json = + "{" + +"\"button\":[" + +"{" + +"\"type\":\"click\"," + +"\"name\":\"今日歌曲\"," + +"\"key\":\"V1001_TODAY_MUSIC\"" + +"}," + +"{" + +"\"type\":\"click\"," + +"\"name\":\"歌手简介\"," + +"\"key\":\"V1001_TODAY_SINGER\"" + +"}," + +"{" + +"\"name\":\"菜单\"," + +"\"sub_button\":[" + +"{" + +"\"type\":\"view\"," + +"\"name\":\"搜索\"," + +"\"url\":\"http://www.soso.com/\"" + +"}," + +"{" + +"\"type\":\"view\"," + +"\"name\":\"视频\"," + +"\"url\":\"http://v.qq.com/\"" + +"}," + +"{" + +"\"type\":\"click\"," + +"\"name\":\"赞一下我们\"," + +"\"key\":\"V1001_GOOD\"" + +"}" + +"]" + +"}" + +"]" + +"}"; + return new Object[][] { + new Object[] { json } + }; + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutImageMessageTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutImageMessageTest.java new file mode 100644 index 000000000..eb0f2f0a9 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutImageMessageTest.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutImageMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WxXmlOutImageMessageTest { + + public void test() { + WxXmlOutImageMessage m = new WxXmlOutImageMessage(); + m.setMediaId("ddfefesfsdfef"); + m.setCreateTime(1122l); + m.setFromUserName("from"); + m.setToUserName("to"); + + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + "" + + ""; + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + } + + public void testBuild() { + WxXmlOutImageMessage m = WxXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + "" + + ""; + System.out.println(m.toXml()); + Assert.assertEquals( + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") + ); + + } +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMusicMessageTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMusicMessageTest.java new file mode 100644 index 000000000..0a135e186 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutMusicMessageTest.java @@ -0,0 +1,76 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutMusicMessage; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WxXmlOutMusicMessageTest { + + public void test() { + WxXmlOutMusicMessage m = new WxXmlOutMusicMessage(); + m.setTitle("title"); + m.setDescription("ddfff"); + m.setHqMusicUrl("hQMusicUrl"); + m.setMusicUrl("musicUrl"); + m.setThumbMediaId("thumbMediaId"); + m.setCreateTime(1122l); + m.setFromUserName("fromUser"); + m.setToUserName("toUser"); + + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + "" + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + ""; + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + } + + public void testBuild() { + WxXmlOutMusicMessage m = WxXmlOutMessage.MUSIC() + .fromUser("fromUser") + .toUser("toUser") + .title("title") + .description("ddfff") + .hqMusicUrl("hQMusicUrl") + .musicUrl("musicUrl") + .thumbMediaId("thumbMediaId") + .build(); + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + "" + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + ""; + System.out.println(m.toXml()); + Assert.assertEquals( + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") + ); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutNewsMessageTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutNewsMessageTest.java new file mode 100644 index 000000000..f46ceac63 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutNewsMessageTest.java @@ -0,0 +1,97 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutMewsMessage; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WxXmlOutNewsMessageTest { + + public void test() { + WxXmlOutMewsMessage m = new WxXmlOutMewsMessage(); + m.setCreateTime(1122l); + m.setFromUserName("fromUser"); + m.setToUserName("toUser"); + + WxXmlOutMewsMessage.Item item = new WxXmlOutMewsMessage.Item(); + item.setDescription("description"); + item.setPicUrl("picUrl"); + item.setTitle("title"); + item.setUrl("url"); + m.addArticle(item); + m.addArticle(item); + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + " 2" + + " " + + " " + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + ""; + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + } + + public void testBuild() { + WxXmlOutMewsMessage.Item item = new WxXmlOutMewsMessage.Item(); + item.setDescription("description"); + item.setPicUrl("picUrl"); + item.setTitle("title"); + item.setUrl("url"); + + WxXmlOutMewsMessage m = WxXmlOutMessage.NEWS() + .fromUser("fromUser") + .toUser("toUser") + .addArticle(item) + .addArticle(item) + .build(); + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + " 2" + + " " + + " " + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + " <![CDATA[title]]>" + + " " + + " " + + " " + + " " + + " " + + ""; + System.out.println(m.toXml()); + Assert.assertEquals( + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") + ); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutTextMessageTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutTextMessageTest.java new file mode 100644 index 000000000..c6c8f92a8 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutTextMessageTest.java @@ -0,0 +1,54 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutTextMessage; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WxXmlOutTextMessageTest { + + public void test() { + WxXmlOutTextMessage m = new WxXmlOutTextMessage(); + m.setContent("content"); + m.setCreateTime(1122l); + m.setFromUserName("from"); + m.setToUserName("to"); + + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + "" + + ""; + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + } + + public void testBuild() { + WxXmlOutTextMessage m = WxXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + "" + + ""; + System.out.println(m.toXml()); + Assert.assertEquals( + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") + ); + + } + + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVideoMessageTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVideoMessageTest.java new file mode 100644 index 000000000..4fdb6f36f --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVideoMessageTest.java @@ -0,0 +1,68 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutVideoMessage; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WxXmlOutVideoMessageTest { + + public void test() { + WxXmlOutVideoMessage m = new WxXmlOutVideoMessage(); + m.setMediaId("media_id"); + m.setTitle("title"); + m.setDescription("ddfff"); + m.setCreateTime(1122l); + m.setFromUserName("fromUser"); + m.setToUserName("toUser"); + + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + " " + + ""; + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + } + + public void testBuild() { + WxXmlOutVideoMessage m = WxXmlOutMessage.VIDEO() + .mediaId("media_id") + .fromUser("fromUser") + .toUser("toUser") + .title("title") + .description("ddfff") + .build(); + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + " " + + ""; + System.out.println(m.toXml()); + Assert.assertEquals( + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") + ); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVoiceMessageTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVoiceMessageTest.java new file mode 100644 index 000000000..a0da16098 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/bean/WxXmlOutVoiceMessageTest.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.enterprise.bean; + +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutVoiceMessage; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WxXmlOutVoiceMessageTest { + + public void test() { + WxXmlOutVoiceMessage m = new WxXmlOutVoiceMessage(); + m.setMediaId("ddfefesfsdfef"); + m.setCreateTime(1122l); + m.setFromUserName("from"); + m.setToUserName("to"); + + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + "" + + ""; + System.out.println(m.toXml()); + Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); + } + + public void testBuild() { + WxXmlOutVoiceMessage m = WxXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); + String expected = "" + + "" + + "" + + "" + + "1122" + + "" + + "" + + ""; + System.out.println(m.toXml()); + Assert.assertEquals( + m + .toXml() + .replaceAll("\\s", "") + .replaceAll(".*?", ""), + expected + .replaceAll("\\s", "") + .replaceAll(".*?", "") + ); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxCpTestServer.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxCpTestServer.java new file mode 100644 index 000000000..d4c1415a2 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxCpTestServer.java @@ -0,0 +1,20 @@ +package me.chanjar.weixin.enterprise.demo; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; + +/** + * @author Daniel Qian + */ +public class WxCpTestServer { + public static void main(String[] args) throws Exception { + Server server = new Server(8080); + + ServletHandler handler = new ServletHandler(); + server.setHandler(handler); + + handler.addServletWithMapping(WxCpTestServlet.class, "/*"); + server.start(); + server.join(); + } +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxCpTestServlet.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxCpTestServlet.java new file mode 100644 index 000000000..3389047c4 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxCpTestServlet.java @@ -0,0 +1,94 @@ +package me.chanjar.weixin.enterprise.demo; + +import me.chanjar.weixin.enterprise.api.*; +import me.chanjar.weixin.enterprise.bean.WxCpXmlMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutMessage; +import me.chanjar.weixin.enterprise.bean.WxXmlOutTextMessage; +import me.chanjar.weixin.enterprise.util.crypto.WxCryptUtil; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.bind.JAXBException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * @author Daniel Qian + */ +public class WxCpTestServlet extends HttpServlet { + + protected WxCpService wxCpService; + protected WxCpConfigStorage wxCpConfigStorage; + protected WxCpMessageRouter wxCpMessageRouter; + + @Override public void init() throws ServletException { + // + super.init(); + try { + InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); + WxTestCpConfigStorage config = WxTestCpConfigStorage.fromXml(is1); + + wxCpConfigStorage = config; + wxCpService = new WxCpServiceImpl(); + wxCpService.setWxCpConfigStorage(config); + + WxCpMessageHandler handler = new WxCpMessageHandler() { + @Override public WxXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context) { + WxXmlOutTextMessage m + = WxXmlOutMessage.TEXT().content("测试加密消息").fromUser(wxMessage.getToUserName()) + .toUser(wxMessage.getFromUserName()).build(); + return m; + } + }; + + wxCpMessageRouter = new WxCpMessageRouter(); + wxCpMessageRouter + .rule() + .async(false) + .content("哈哈") // 拦截内容为“哈哈”的消息 + .handler(handler) + .end(); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + + @Override protected void service(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + String msgSignature = request.getParameter("msg_signature"); + String nonce = request.getParameter("nonce"); + String timestamp = request.getParameter("timestamp"); + String echostr = request.getParameter("echostr"); + + response.setContentType("text/html;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + if (StringUtils.isNotBlank(echostr)) { + if (!wxCpService.checkSignature(msgSignature, timestamp, nonce, echostr)) { + // 消息签名不正确,说明不是公众平台发过来的消息 + response.getWriter().println("非法请求"); + return; + } + WxCryptUtil cryptUtil = new WxCryptUtil(wxCpConfigStorage); + String plainText = cryptUtil.decrypt(echostr); + // 说明是一个仅仅用来验证的请求,回显echostr + response.getWriter().println(plainText); + return; + } + + + WxCpXmlMessage inMessage = WxCpXmlMessage.fromEncryptedXml(request.getInputStream(), wxCpConfigStorage, timestamp, nonce, msgSignature); + +// WxXmlOutMessage outMessage = wxCpMessageRouter.route(inMessage); + + + return; + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxTestCpConfigStorage.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxTestCpConfigStorage.java new file mode 100644 index 000000000..7de397996 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/demo/WxTestCpConfigStorage.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.enterprise.demo; + +import me.chanjar.weixin.enterprise.api.WxInMemoryCpConfigStorage; +import org.xml.sax.InputSource; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import java.io.InputStream; + +/** + * @author Daniel Qian + */ +@XmlRootElement(name = "xml") +@XmlAccessorType(XmlAccessType.FIELD) +class WxTestCpConfigStorage extends WxInMemoryCpConfigStorage { + + @Override + public String toString() { + return "SimpleWxConfigProvider [corpId=" + corpId + ", corpSecret=" + corpSecret + ", accessToken=" + accessToken + + ", expiresIn=" + expiresIn + ", token=" + token + ", aesKey=" + aesKey + "]"; + } + + + public static WxTestCpConfigStorage fromXml(InputStream is) throws JAXBException { + Unmarshaller um = JAXBContext.newInstance(WxTestCpConfigStorage.class).createUnmarshaller(); + InputSource inputSource = new InputSource(is); + inputSource.setEncoding("utf-8"); + return (WxTestCpConfigStorage) um.unmarshal(inputSource); + } + +} diff --git a/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/util/crypto/WxCryptUtilTest.java b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/util/crypto/WxCryptUtilTest.java new file mode 100755 index 000000000..eb5028401 --- /dev/null +++ b/weixin-java-enterprise/src/test/java/me/chanjar/weixin/enterprise/util/crypto/WxCryptUtilTest.java @@ -0,0 +1,95 @@ +package me.chanjar.weixin.enterprise.util.crypto; + +import me.chanjar.weixin.enterprise.util.crypto.WxCryptUtil; +import org.testng.annotations.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; + +import static org.testng.Assert.*; + +@Test +public class WxCryptUtilTest { + String encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; + String token = "pamtest"; + String timestamp = "1409304348"; + String nonce = "xxxxxx"; + String appId = "wxb11529c136998cb6"; + String randomStr = "aaaabbbbccccdddd"; + + String xmlFormat = ""; + String replyMsg = "我是中文abcd123"; + + String afterAesEncrypt = "jn1L23DB+6ELqJ+6bruv21Y6MD7KeIfP82D6gU39rmkgczbWwt5+3bnyg5K55bgVtVzd832WzZGMhkP72vVOfg=="; + + String replyMsg2 = "1407743423"; + String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb"; + + public void testNormal() throws ParserConfigurationException, SAXException, IOException { + WxCryptUtil pc = new WxCryptUtil(token, encodingAesKey, appId); + String encryptedXml = pc.encrypt(replyMsg); + + System.out.println(encryptedXml); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); + + Element root = document.getDocumentElement(); + String cipherText = root.getElementsByTagName("Encrypt").item(0).getTextContent(); + String msgSignature = root.getElementsByTagName("MsgSignature").item(0).getTextContent(); + String timestamp = root.getElementsByTagName("TimeStamp").item(0).getTextContent(); + String nonce = root.getElementsByTagName("Nonce").item(0).getTextContent(); + + String messageText = String.format(xmlFormat, cipherText); + + // 第三方收到公众号平台发送的消息 + String plainMessage = pc.decrypt(cipherText); + + System.out.println(plainMessage); + assertEquals(plainMessage, replyMsg); + } + + public void testAesEncrypt() { + WxCryptUtil pc = new WxCryptUtil(token, encodingAesKey, appId); + assertEquals(pc.encrypt(randomStr, replyMsg), afterAesEncrypt); + } + + public void testAesEncrypt2() { + WxCryptUtil pc = new WxCryptUtil(token, encodingAesKey, appId); + assertEquals(pc.encrypt(randomStr, replyMsg2), afterAesEncrypt2); + } + + public void testValidateSignatureError() throws ParserConfigurationException, SAXException, + IOException { + try { + WxCryptUtil pc = new WxCryptUtil(token, encodingAesKey, appId); + String afterEncrpt = pc.encrypt(replyMsg); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + StringReader sr = new StringReader(afterEncrpt); + InputSource is = new InputSource(sr); + Document document = db.parse(is); + + Element root = document.getDocumentElement(); + NodeList nodelist1 = root.getElementsByTagName("Encrypt"); + + String encrypt = nodelist1.item(0).getTextContent(); + String fromXML = String.format(xmlFormat, encrypt); + pc.decrypt("12345", timestamp, nonce, fromXML); // 这里签名错误 + } catch (RuntimeException e) { + assertEquals(e.getMessage(), "加密消息签名校验失败"); + return; + } + fail("错误流程不抛出异常???"); + } + +} diff --git a/src/test/resources/mm.jpeg b/weixin-java-enterprise/src/test/resources/mm.jpeg similarity index 100% rename from src/test/resources/mm.jpeg rename to weixin-java-enterprise/src/test/resources/mm.jpeg diff --git a/src/test/resources/mm.mp3 b/weixin-java-enterprise/src/test/resources/mm.mp3 similarity index 100% rename from src/test/resources/mm.mp3 rename to weixin-java-enterprise/src/test/resources/mm.mp3 diff --git a/src/test/resources/mm.mp4 b/weixin-java-enterprise/src/test/resources/mm.mp4 similarity index 100% rename from src/test/resources/mm.mp4 rename to weixin-java-enterprise/src/test/resources/mm.mp4 diff --git a/weixin-java-enterprise/src/test/resources/test-config.sample.xml b/weixin-java-enterprise/src/test/resources/test-config.sample.xml new file mode 100644 index 000000000..b7e660a71 --- /dev/null +++ b/weixin-java-enterprise/src/test/resources/test-config.sample.xml @@ -0,0 +1,12 @@ + + 企业号corpid + 企业号corpsecret + 企业号应用id + 企业号应用Token + 公众号应用EncodingAESKey + 可以不填写 + 可以不填写 + 企业号通讯录里的某个userid + 企业号通讯录的某个部门id + 企业号通讯录里的某个tagid + diff --git a/weixin-java-enterprise/src/test/resources/testng.xml b/weixin-java-enterprise/src/test/resources/testng.xml new file mode 100644 index 000000000..af00d0f8f --- /dev/null +++ b/weixin-java-enterprise/src/test/resources/testng.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/weixin-java-mp/LICENSE b/weixin-java-mp/LICENSE new file mode 100644 index 000000000..5c304d1a4 --- /dev/null +++ b/weixin-java-mp/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/weixin-java-mp/README.md similarity index 100% rename from README.md rename to weixin-java-mp/README.md diff --git a/pom.xml b/weixin-java-mp/pom.xml similarity index 98% rename from pom.xml rename to weixin-java-mp/pom.xml index 1ac3f48c9..fbcf60a7d 100644 --- a/pom.xml +++ b/weixin-java-mp/pom.xml @@ -4,10 +4,10 @@ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 me.chanjar - weixin-java-tools + weixin-java-mp-tools 1.0.3-SNAPSHOT - WeiXin Java Tools - 用于开发微信公众号的Java工具 + WeiXin Java MP Tools + 微信公众号Java SDK https://github.com/chanjarster/weixin-java-tools diff --git a/src/main/java/me/chanjar/weixin/api/WxConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxConfigStorage.java similarity index 83% rename from src/main/java/me/chanjar/weixin/api/WxConfigStorage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxConfigStorage.java index 57bdcd3ba..7c6cc4e50 100644 --- a/src/main/java/me/chanjar/weixin/api/WxConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxConfigStorage.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.api; +package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.bean.WxAccessToken; +import me.chanjar.weixin.mp.bean.WxAccessToken; /** * 微信客户端配置存储 diff --git a/src/main/java/me/chanjar/weixin/api/WxConsts.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxConsts.java similarity index 99% rename from src/main/java/me/chanjar/weixin/api/WxConsts.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxConsts.java index f2152428b..49617cdaf 100644 --- a/src/main/java/me/chanjar/weixin/api/WxConsts.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxConsts.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.api; +package me.chanjar.weixin.mp.api; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/me/chanjar/weixin/api/WxInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxInMemoryConfigStorage.java similarity index 94% rename from src/main/java/me/chanjar/weixin/api/WxInMemoryConfigStorage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxInMemoryConfigStorage.java index b4a734c5a..1656fa831 100644 --- a/src/main/java/me/chanjar/weixin/api/WxInMemoryConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxInMemoryConfigStorage.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.api; +package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.bean.WxAccessToken; +import me.chanjar.weixin.mp.bean.WxAccessToken; /** * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 diff --git a/src/main/java/me/chanjar/weixin/api/WxMessageHandler.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageHandler.java similarity index 78% rename from src/main/java/me/chanjar/weixin/api/WxMessageHandler.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageHandler.java index b433f9572..0c8c3b330 100644 --- a/src/main/java/me/chanjar/weixin/api/WxMessageHandler.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageHandler.java @@ -1,9 +1,9 @@ -package me.chanjar.weixin.api; +package me.chanjar.weixin.mp.api; import java.util.Map; -import me.chanjar.weixin.bean.WxXmlMessage; -import me.chanjar.weixin.bean.WxXmlOutMessage; +import me.chanjar.weixin.mp.bean.WxXmlMessage; +import me.chanjar.weixin.mp.bean.WxXmlOutMessage; /** * 处理微信推送消息的处理器接口 diff --git a/src/main/java/me/chanjar/weixin/api/WxMessageInterceptor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageInterceptor.java similarity index 84% rename from src/main/java/me/chanjar/weixin/api/WxMessageInterceptor.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageInterceptor.java index ff4e659cf..22b3e8f8c 100644 --- a/src/main/java/me/chanjar/weixin/api/WxMessageInterceptor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageInterceptor.java @@ -1,9 +1,8 @@ -package me.chanjar.weixin.api; +package me.chanjar.weixin.mp.api; import java.util.Map; -import me.chanjar.weixin.bean.WxXmlMessage; - +import me.chanjar.weixin.mp.bean.WxXmlMessage; /** * 微信消息拦截器,可以用来做验证 diff --git a/src/main/java/me/chanjar/weixin/api/WxMessageRouter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageRouter.java similarity index 98% rename from src/main/java/me/chanjar/weixin/api/WxMessageRouter.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageRouter.java index 2a651596b..00ea678b7 100644 --- a/src/main/java/me/chanjar/weixin/api/WxMessageRouter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMessageRouter.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.api; +package me.chanjar.weixin.mp.api; import java.util.ArrayList; import java.util.HashMap; @@ -8,8 +8,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Pattern; -import me.chanjar.weixin.bean.WxXmlMessage; -import me.chanjar.weixin.bean.WxXmlOutMessage; +import me.chanjar.weixin.mp.bean.WxXmlMessage; +import me.chanjar.weixin.mp.bean.WxXmlOutMessage; /** *
diff --git a/src/main/java/me/chanjar/weixin/api/WxService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxService.java
similarity index 89%
rename from src/main/java/me/chanjar/weixin/api/WxService.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxService.java
index c5db88bcd..9e37227ac 100644
--- a/src/main/java/me/chanjar/weixin/api/WxService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxService.java
@@ -1,24 +1,24 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
-import me.chanjar.weixin.bean.WxCustomMessage;
-import me.chanjar.weixin.bean.WxGroup;
-import me.chanjar.weixin.bean.WxMassGroupMessage;
-import me.chanjar.weixin.bean.WxMassNews;
-import me.chanjar.weixin.bean.WxMassOpenIdsMessage;
-import me.chanjar.weixin.bean.WxMassVideo;
-import me.chanjar.weixin.bean.WxMenu;
-import me.chanjar.weixin.bean.result.WxMassSendResult;
-import me.chanjar.weixin.bean.result.WxMassUploadResult;
-import me.chanjar.weixin.bean.result.WxMediaUploadResult;
-import me.chanjar.weixin.bean.result.WxQrCodeTicket;
-import me.chanjar.weixin.bean.result.WxUser;
-import me.chanjar.weixin.bean.result.WxUserList;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.WxCustomMessage;
+import me.chanjar.weixin.mp.bean.WxGroup;
+import me.chanjar.weixin.mp.bean.WxMassGroupMessage;
+import me.chanjar.weixin.mp.bean.WxMassNews;
+import me.chanjar.weixin.mp.bean.WxMassOpenIdsMessage;
+import me.chanjar.weixin.mp.bean.WxMassVideo;
+import me.chanjar.weixin.mp.bean.WxMenu;
+import me.chanjar.weixin.mp.bean.result.WxMassSendResult;
+import me.chanjar.weixin.mp.bean.result.WxMassUploadResult;
+import me.chanjar.weixin.mp.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.mp.bean.result.WxQrCodeTicket;
+import me.chanjar.weixin.mp.bean.result.WxUser;
+import me.chanjar.weixin.mp.bean.result.WxUserList;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 /**
  * 微信API的Service
@@ -48,7 +48,7 @@ public interface WxService {
 
    * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token
    * 
- * @throws WxErrorException + * @throws me.chanjar.weixin.mp.exception.WxErrorException */ public void accessTokenRefresh() throws WxErrorException; @@ -109,8 +109,8 @@ public interface WxService { * * @param news * @throws WxErrorException - * @see #massGroupMessageSend(WxMassGroupMessage) - * @see #massOpenIdsMessageSend(WxMassOpenIdsMessage) + * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMassGroupMessage) + * @see #massOpenIdsMessageSend(me.chanjar.weixin.mp.bean.WxMassOpenIdsMessage) */ public WxMassUploadResult massNewsUpload(WxMassNews news) throws WxErrorException; @@ -121,8 +121,8 @@ public interface WxService { * * @return * @throws WxErrorException - * @see #massGroupMessageSend(WxMassGroupMessage) - * @see #massOpenIdsMessageSend(WxMassOpenIdsMessage) + * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMassGroupMessage) + * @see #massOpenIdsMessageSend(me.chanjar.weixin.mp.bean.WxMassOpenIdsMessage) */ public WxMassUploadResult massVideoUpload(WxMassVideo video) throws WxErrorException; diff --git a/src/main/java/me/chanjar/weixin/api/WxServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxServiceImpl.java similarity index 90% rename from src/main/java/me/chanjar/weixin/api/WxServiceImpl.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxServiceImpl.java index 28fc3e7e2..a81bc0680 100644 --- a/src/main/java/me/chanjar/weixin/api/WxServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxServiceImpl.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.api; +package me.chanjar.weixin.mp.api; import java.io.File; import java.io.IOException; @@ -8,9 +8,15 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import me.chanjar.weixin.bean.WxCustomMessage; -import me.chanjar.weixin.util.crypto.SHA1; -import me.chanjar.weixin.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.WxCustomMessage; +import me.chanjar.weixin.mp.bean.result.WxError; +import me.chanjar.weixin.mp.bean.result.WxMassSendResult; +import me.chanjar.weixin.mp.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.mp.bean.result.WxUserList; +import me.chanjar.weixin.mp.exception.WxErrorException; +import me.chanjar.weixin.mp.util.crypto.SHA1; +import me.chanjar.weixin.mp.util.json.GsonHelper; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; @@ -19,29 +25,23 @@ import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import me.chanjar.weixin.bean.WxAccessToken; -import me.chanjar.weixin.bean.WxGroup; -import me.chanjar.weixin.bean.WxMassGroupMessage; -import me.chanjar.weixin.bean.WxMassNews; -import me.chanjar.weixin.bean.WxMassOpenIdsMessage; -import me.chanjar.weixin.bean.WxMassVideo; -import me.chanjar.weixin.bean.WxMenu; -import me.chanjar.weixin.bean.result.WxError; -import me.chanjar.weixin.bean.result.WxMassSendResult; -import me.chanjar.weixin.bean.result.WxMassUploadResult; -import me.chanjar.weixin.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.bean.result.WxQrCodeTicket; -import me.chanjar.weixin.bean.result.WxUser; -import me.chanjar.weixin.bean.result.WxUserList; -import me.chanjar.weixin.exception.WxErrorException; -import me.chanjar.weixin.util.fs.FileUtils; -import me.chanjar.weixin.util.http.MediaDownloadRequestExecutor; -import me.chanjar.weixin.util.http.MediaUploadRequestExecutor; -import me.chanjar.weixin.util.http.QrCodeRequestExecutor; -import me.chanjar.weixin.util.http.RequestExecutor; -import me.chanjar.weixin.util.http.SimpleGetRequestExecutor; -import me.chanjar.weixin.util.http.SimplePostRequestExecutor; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.bean.WxAccessToken; +import me.chanjar.weixin.mp.bean.WxGroup; +import me.chanjar.weixin.mp.bean.WxMassGroupMessage; +import me.chanjar.weixin.mp.bean.WxMassNews; +import me.chanjar.weixin.mp.bean.WxMassOpenIdsMessage; +import me.chanjar.weixin.mp.bean.WxMassVideo; +import me.chanjar.weixin.mp.bean.WxMenu; +import me.chanjar.weixin.mp.bean.result.WxMassUploadResult; +import me.chanjar.weixin.mp.bean.result.WxQrCodeTicket; +import me.chanjar.weixin.mp.bean.result.WxUser; +import me.chanjar.weixin.mp.util.fs.FileUtils; +import me.chanjar.weixin.mp.util.http.MediaDownloadRequestExecutor; +import me.chanjar.weixin.mp.util.http.MediaUploadRequestExecutor; +import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; +import me.chanjar.weixin.mp.util.http.RequestExecutor; +import me.chanjar.weixin.mp.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.mp.util.http.SimplePostRequestExecutor; import com.google.gson.JsonElement; import com.google.gson.JsonObject; diff --git a/src/main/java/me/chanjar/weixin/bean/WxAccessToken.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxAccessToken.java similarity index 85% rename from src/main/java/me/chanjar/weixin/bean/WxAccessToken.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxAccessToken.java index 1cbb71549..c9166effa 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxAccessToken.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxAccessToken.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; public class WxAccessToken { diff --git a/src/main/java/me/chanjar/weixin/bean/WxCustomMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxCustomMessage.java similarity index 88% rename from src/main/java/me/chanjar/weixin/bean/WxCustomMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxCustomMessage.java index eea1113bc..e832eb80d 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxCustomMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxCustomMessage.java @@ -1,16 +1,16 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.bean.custombuilder.ImageBuilder; -import me.chanjar.weixin.bean.custombuilder.MusicBuilder; -import me.chanjar.weixin.bean.custombuilder.NewsBuilder; -import me.chanjar.weixin.bean.custombuilder.TextBuilder; -import me.chanjar.weixin.bean.custombuilder.VideoBuilder; -import me.chanjar.weixin.bean.custombuilder.VoiceBuilder; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.bean.custombuilder.ImageBuilder; +import me.chanjar.weixin.mp.bean.custombuilder.MusicBuilder; +import me.chanjar.weixin.mp.bean.custombuilder.NewsBuilder; +import me.chanjar.weixin.mp.bean.custombuilder.TextBuilder; +import me.chanjar.weixin.mp.bean.custombuilder.VideoBuilder; +import me.chanjar.weixin.mp.bean.custombuilder.VoiceBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** * 客服消息 diff --git a/src/main/java/me/chanjar/weixin/bean/WxGroup.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxGroup.java similarity index 90% rename from src/main/java/me/chanjar/weixin/bean/WxGroup.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxGroup.java index 0174f5624..be2388bf6 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxGroup.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxGroup.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** * 微信用户分组 diff --git a/src/main/java/me/chanjar/weixin/bean/WxMassGroupMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassGroupMessage.java similarity index 90% rename from src/main/java/me/chanjar/weixin/bean/WxMassGroupMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassGroupMessage.java index 32bd6c907..07c4dbae3 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxMassGroupMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassGroupMessage.java @@ -1,7 +1,7 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** * 分组群发的消息 diff --git a/src/main/java/me/chanjar/weixin/bean/WxMassNews.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassNews.java similarity index 97% rename from src/main/java/me/chanjar/weixin/bean/WxMassNews.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassNews.java index af136a83a..01872005c 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxMassNews.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassNews.java @@ -1,9 +1,9 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** * 群发时用到的图文消息素材 diff --git a/src/main/java/me/chanjar/weixin/bean/WxMassOpenIdsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassOpenIdsMessage.java similarity index 76% rename from src/main/java/me/chanjar/weixin/bean/WxMassOpenIdsMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassOpenIdsMessage.java index 56397afb0..8dc190d04 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxMassOpenIdsMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassOpenIdsMessage.java @@ -1,9 +1,9 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** * OpenId列表群发的消息 @@ -28,11 +28,11 @@ public class WxMassOpenIdsMessage { /** *
    * 请使用
-   * {@link me.chanjar.weixin.api.WxConsts#MASS_MSG_IMAGE}
-   * {@link me.chanjar.weixin.api.WxConsts#MASS_MSG_NEWS}
-   * {@link me.chanjar.weixin.api.WxConsts#MASS_MSG_TEXT}
-   * {@link me.chanjar.weixin.api.WxConsts#MASS_MSG_VIDEO}
-   * {@link me.chanjar.weixin.api.WxConsts#MASS_MSG_VOICE}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#MASS_MSG_IMAGE}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#MASS_MSG_NEWS}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#MASS_MSG_TEXT}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#MASS_MSG_VIDEO}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#MASS_MSG_VOICE}
    * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误
    * 
* @param msgType diff --git a/src/main/java/me/chanjar/weixin/bean/WxMassVideo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassVideo.java similarity index 88% rename from src/main/java/me/chanjar/weixin/bean/WxMassVideo.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassVideo.java index 47d73683b..4c89c80f5 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxMassVideo.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMassVideo.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** * 群发时用到的视频素材 diff --git a/src/main/java/me/chanjar/weixin/bean/WxMenu.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMenu.java similarity index 94% rename from src/main/java/me/chanjar/weixin/bean/WxMenu.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMenu.java index 5363ab0de..b1c9a71d0 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxMenu.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMenu.java @@ -1,11 +1,11 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** * 公众号菜单 diff --git a/src/main/java/me/chanjar/weixin/bean/WxXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlMessage.java similarity index 92% rename from src/main/java/me/chanjar/weixin/bean/WxXmlMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlMessage.java index aa70047d9..5d7f07aa4 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlMessage.java @@ -1,10 +1,9 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; -import me.chanjar.weixin.api.WxConfigStorage; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.util.crypto.WxCryptUtil; -import me.chanjar.weixin.util.xml.AdapterCDATA; -import me.chanjar.weixin.util.xml.XmlTransformer; +import me.chanjar.weixin.mp.api.WxConfigStorage; +import me.chanjar.weixin.mp.util.crypto.WxCryptUtil; +import me.chanjar.weixin.mp.util.xml.AdapterCDATA; +import me.chanjar.weixin.mp.util.xml.XmlTransformer; import org.apache.commons.io.IOUtils; import javax.xml.bind.JAXBException; @@ -180,13 +179,13 @@ public class WxXmlMessage { /** *
    * 当接受用户消息时,可能会获得以下值:
-   * {@link WxConsts#XML_MSG_TEXT}
-   * {@link WxConsts#XML_MSG_IMAGE}
-   * {@link WxConsts#XML_MSG_VOICE}
-   * {@link WxConsts#XML_MSG_VIDEO}
-   * {@link WxConsts#XML_MSG_LOCATION}
-   * {@link WxConsts#XML_MSG_LINK}
-   * {@link WxConsts#XML_MSG_EVENT}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_TEXT}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_IMAGE}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_VOICE}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_VIDEO}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_LOCATION}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_LINK}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_EVENT}
    * 
* * @return @@ -198,12 +197,12 @@ public class WxXmlMessage { /** *
    * 当发送消息的时候使用:
-   * {@link WxConsts#XML_MSG_TEXT}
-   * {@link WxConsts#XML_MSG_IMAGE}
-   * {@link WxConsts#XML_MSG_VOICE}
-   * {@link WxConsts#XML_MSG_VIDEO}
-   * {@link WxConsts#XML_MSG_NEWS}
-   * {@link WxConsts#XML_MSG_MUSIC}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_TEXT}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_IMAGE}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_VOICE}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_VIDEO}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_NEWS}
+   * {@link me.chanjar.weixin.mp.api.WxConsts#XML_MSG_MUSIC}
    * 
* * @param msgType @@ -554,7 +553,7 @@ public class WxXmlMessage { @XmlRootElement(name = "item") @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "me.chanjar.weixin.bean.WxXmlMessage.SendPicsInfo.Item") + @XmlType(name = "WxXmlMessage.SendPicsInfo.Item") public static class Item { @XmlElement(name = "PicMd5Sum") diff --git a/src/main/java/me/chanjar/weixin/bean/WxXmlOutImageMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutImageMessage.java similarity index 84% rename from src/main/java/me/chanjar/weixin/bean/WxXmlOutImageMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutImageMessage.java index 59ac53946..4f1cb5f83 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxXmlOutImageMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutImageMessage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -6,8 +6,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.util.xml.MediaIdMarshaller; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.util.xml.MediaIdMarshaller; @XmlRootElement(name = "xml") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/me/chanjar/weixin/bean/WxXmlOutMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMessage.java similarity index 81% rename from src/main/java/me/chanjar/weixin/bean/WxXmlOutMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMessage.java index 7a03d29e1..b64ae73dc 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxXmlOutMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMessage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import javax.xml.bind.JAXBException; import javax.xml.bind.annotation.XmlAccessType; @@ -7,16 +7,16 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import me.chanjar.weixin.api.WxConfigStorage; -import me.chanjar.weixin.bean.outxmlbuilder.ImageBuilder; -import me.chanjar.weixin.bean.outxmlbuilder.MusicBuilder; -import me.chanjar.weixin.bean.outxmlbuilder.NewsBuilder; -import me.chanjar.weixin.bean.outxmlbuilder.TextBuilder; -import me.chanjar.weixin.bean.outxmlbuilder.VideoBuilder; -import me.chanjar.weixin.bean.outxmlbuilder.VoiceBuilder; -import me.chanjar.weixin.util.crypto.WxCryptUtil; -import me.chanjar.weixin.util.xml.AdapterCDATA; -import me.chanjar.weixin.util.xml.XmlTransformer; +import me.chanjar.weixin.mp.api.WxConfigStorage; +import me.chanjar.weixin.mp.bean.outxmlbuilder.ImageBuilder; +import me.chanjar.weixin.mp.bean.outxmlbuilder.MusicBuilder; +import me.chanjar.weixin.mp.bean.outxmlbuilder.NewsBuilder; +import me.chanjar.weixin.mp.bean.outxmlbuilder.TextBuilder; +import me.chanjar.weixin.mp.bean.outxmlbuilder.VideoBuilder; +import me.chanjar.weixin.mp.bean.outxmlbuilder.VoiceBuilder; +import me.chanjar.weixin.mp.util.crypto.WxCryptUtil; +import me.chanjar.weixin.mp.util.xml.AdapterCDATA; +import me.chanjar.weixin.mp.util.xml.XmlTransformer; @XmlRootElement(name = "xml") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/me/chanjar/weixin/bean/WxXmlOutMewsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMewsMessage.java similarity index 94% rename from src/main/java/me/chanjar/weixin/bean/WxXmlOutMewsMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMewsMessage.java index ef3f6c863..f783b1347 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxXmlOutMewsMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMewsMessage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import java.util.ArrayList; import java.util.List; @@ -10,8 +10,8 @@ import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.util.xml.AdapterCDATA; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.util.xml.AdapterCDATA; @XmlRootElement(name = "xml") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/me/chanjar/weixin/bean/WxXmlOutMusicMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessage.java similarity index 95% rename from src/main/java/me/chanjar/weixin/bean/WxXmlOutMusicMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessage.java index 40f6ebad8..69f7359d2 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxXmlOutMusicMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -6,8 +6,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.util.xml.AdapterCDATA; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.util.xml.AdapterCDATA; @XmlRootElement(name = "xml") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/me/chanjar/weixin/bean/WxXmlOutTextMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessage.java similarity index 84% rename from src/main/java/me/chanjar/weixin/bean/WxXmlOutTextMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessage.java index 3e4d3c088..3bafc7440 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxXmlOutTextMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -6,8 +6,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.util.xml.AdapterCDATA; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.util.xml.AdapterCDATA; @XmlRootElement(name = "xml") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/me/chanjar/weixin/bean/WxXmlOutVideoMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessage.java similarity index 93% rename from src/main/java/me/chanjar/weixin/bean/WxXmlOutVideoMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessage.java index 08498084f..3a7ec0ba4 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxXmlOutVideoMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -6,8 +6,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.util.xml.AdapterCDATA; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.util.xml.AdapterCDATA; @XmlRootElement(name = "xml") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/me/chanjar/weixin/bean/WxXmlOutVoiceMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessage.java similarity index 84% rename from src/main/java/me/chanjar/weixin/bean/WxXmlOutVoiceMessage.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessage.java index d70232680..a875bf9ac 100644 --- a/src/main/java/me/chanjar/weixin/bean/WxXmlOutVoiceMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessage.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.bean; +package me.chanjar.weixin.mp.bean; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -6,8 +6,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.util.xml.MediaIdMarshaller; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.util.xml.MediaIdMarshaller; @XmlRootElement(name = "xml") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/src/main/java/me/chanjar/weixin/bean/custombuilder/BaseBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/BaseBuilder.java similarity index 77% rename from src/main/java/me/chanjar/weixin/bean/custombuilder/BaseBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/BaseBuilder.java index 5cb924b07..85f150c48 100644 --- a/src/main/java/me/chanjar/weixin/bean/custombuilder/BaseBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/BaseBuilder.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean.custombuilder; +package me.chanjar.weixin.mp.bean.custombuilder; -import me.chanjar.weixin.bean.WxCustomMessage; +import me.chanjar.weixin.mp.bean.WxCustomMessage; public class BaseBuilder { protected String msgType; diff --git a/src/main/java/me/chanjar/weixin/bean/custombuilder/ImageBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/ImageBuilder.java similarity index 80% rename from src/main/java/me/chanjar/weixin/bean/custombuilder/ImageBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/ImageBuilder.java index de5cc8d0a..37d30a724 100644 --- a/src/main/java/me/chanjar/weixin/bean/custombuilder/ImageBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/ImageBuilder.java @@ -1,7 +1,7 @@ -package me.chanjar.weixin.bean.custombuilder; +package me.chanjar.weixin.mp.bean.custombuilder; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.bean.WxCustomMessage; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.bean.WxCustomMessage; /** * 获得消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/custombuilder/MusicBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/MusicBuilder.java similarity index 91% rename from src/main/java/me/chanjar/weixin/bean/custombuilder/MusicBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/MusicBuilder.java index b0c587b3b..06f48eb6d 100644 --- a/src/main/java/me/chanjar/weixin/bean/custombuilder/MusicBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/MusicBuilder.java @@ -1,7 +1,7 @@ -package me.chanjar.weixin.bean.custombuilder; +package me.chanjar.weixin.mp.bean.custombuilder; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.bean.WxCustomMessage; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.bean.WxCustomMessage; /** * 音乐消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/custombuilder/NewsBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/NewsBuilder.java similarity index 83% rename from src/main/java/me/chanjar/weixin/bean/custombuilder/NewsBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/NewsBuilder.java index f05b5dbc9..8ab490889 100644 --- a/src/main/java/me/chanjar/weixin/bean/custombuilder/NewsBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/NewsBuilder.java @@ -1,10 +1,10 @@ -package me.chanjar.weixin.bean.custombuilder; +package me.chanjar.weixin.mp.bean.custombuilder; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.bean.WxCustomMessage; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.bean.WxCustomMessage; /** * 图文消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/custombuilder/TextBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/TextBuilder.java similarity index 79% rename from src/main/java/me/chanjar/weixin/bean/custombuilder/TextBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/TextBuilder.java index e36aebe84..9db18631e 100644 --- a/src/main/java/me/chanjar/weixin/bean/custombuilder/TextBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/TextBuilder.java @@ -1,7 +1,7 @@ -package me.chanjar.weixin.bean.custombuilder; +package me.chanjar.weixin.mp.bean.custombuilder; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.bean.WxCustomMessage; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.bean.WxCustomMessage; /** * 文本消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/custombuilder/VideoBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/VideoBuilder.java similarity index 90% rename from src/main/java/me/chanjar/weixin/bean/custombuilder/VideoBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/VideoBuilder.java index d38c61e30..5d7ec8c0c 100644 --- a/src/main/java/me/chanjar/weixin/bean/custombuilder/VideoBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/VideoBuilder.java @@ -1,7 +1,7 @@ -package me.chanjar.weixin.bean.custombuilder; +package me.chanjar.weixin.mp.bean.custombuilder; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.bean.WxCustomMessage; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.bean.WxCustomMessage; /** * 视频消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/custombuilder/VoiceBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/VoiceBuilder.java similarity index 80% rename from src/main/java/me/chanjar/weixin/bean/custombuilder/VoiceBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/VoiceBuilder.java index 4d1e2cf90..593c6e70f 100644 --- a/src/main/java/me/chanjar/weixin/bean/custombuilder/VoiceBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/VoiceBuilder.java @@ -1,7 +1,7 @@ -package me.chanjar.weixin.bean.custombuilder; +package me.chanjar.weixin.mp.bean.custombuilder; -import me.chanjar.weixin.api.WxConsts; -import me.chanjar.weixin.bean.WxCustomMessage; +import me.chanjar.weixin.mp.api.WxConsts; +import me.chanjar.weixin.mp.bean.WxCustomMessage; /** * 语音消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/BaseBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/BaseBuilder.java similarity index 85% rename from src/main/java/me/chanjar/weixin/bean/outxmlbuilder/BaseBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/BaseBuilder.java index 9bb68aba3..641ecc013 100644 --- a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/BaseBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/BaseBuilder.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean.outxmlbuilder; +package me.chanjar.weixin.mp.bean.outxmlbuilder; -import me.chanjar.weixin.bean.WxXmlOutMessage; +import me.chanjar.weixin.mp.bean.WxXmlOutMessage; public abstract class BaseBuilder { diff --git a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/ImageBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/ImageBuilder.java similarity index 81% rename from src/main/java/me/chanjar/weixin/bean/outxmlbuilder/ImageBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/ImageBuilder.java index 0cb18cccd..3dbce7450 100644 --- a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/ImageBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/ImageBuilder.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean.outxmlbuilder; +package me.chanjar.weixin.mp.bean.outxmlbuilder; -import me.chanjar.weixin.bean.WxXmlOutImageMessage; +import me.chanjar.weixin.mp.bean.WxXmlOutImageMessage; /** * 图片消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/MusicBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/MusicBuilder.java similarity index 91% rename from src/main/java/me/chanjar/weixin/bean/outxmlbuilder/MusicBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/MusicBuilder.java index ca8a4cec9..0ec3ccbd9 100644 --- a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/MusicBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/MusicBuilder.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean.outxmlbuilder; +package me.chanjar.weixin.mp.bean.outxmlbuilder; -import me.chanjar.weixin.bean.WxXmlOutMusicMessage; +import me.chanjar.weixin.mp.bean.WxXmlOutMusicMessage; /** * 音乐消息builder diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/NewsBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/NewsBuilder.java new file mode 100644 index 000000000..c2edaf73d --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/NewsBuilder.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.mp.bean.outxmlbuilder; + +import java.util.ArrayList; +import java.util.List; + +import me.chanjar.weixin.mp.bean.WxXmlOutMewsMessage; + +/** + * 图文消息builder + * @author chanjarster + */ +public final class NewsBuilder extends BaseBuilder { + + protected final List articles = new ArrayList(); + + public NewsBuilder addArticle(WxXmlOutMewsMessage.Item item) { + this.articles.add(item); + return this; + } + + public WxXmlOutMewsMessage build() { + WxXmlOutMewsMessage m = new WxXmlOutMewsMessage(); + for(WxXmlOutMewsMessage.Item item : articles) { + m.addArticle(item); + } + setCommon(m); + return m; + } + +} diff --git a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/TextBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TextBuilder.java similarity index 80% rename from src/main/java/me/chanjar/weixin/bean/outxmlbuilder/TextBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TextBuilder.java index 56393a332..43c54feba 100644 --- a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/TextBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TextBuilder.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean.outxmlbuilder; +package me.chanjar.weixin.mp.bean.outxmlbuilder; -import me.chanjar.weixin.bean.WxXmlOutTextMessage; +import me.chanjar.weixin.mp.bean.WxXmlOutTextMessage; /** * 文本消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/VideoBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/VideoBuilder.java similarity index 87% rename from src/main/java/me/chanjar/weixin/bean/outxmlbuilder/VideoBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/VideoBuilder.java index 51046dbf4..6fb946d18 100644 --- a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/VideoBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/VideoBuilder.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean.outxmlbuilder; +package me.chanjar.weixin.mp.bean.outxmlbuilder; -import me.chanjar.weixin.bean.WxXmlOutVideoMessage; +import me.chanjar.weixin.mp.bean.WxXmlOutVideoMessage; /** * 视频消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/VoiceBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/VoiceBuilder.java similarity index 80% rename from src/main/java/me/chanjar/weixin/bean/outxmlbuilder/VoiceBuilder.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/VoiceBuilder.java index 73b820df6..4720f135c 100644 --- a/src/main/java/me/chanjar/weixin/bean/outxmlbuilder/VoiceBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/VoiceBuilder.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean.outxmlbuilder; +package me.chanjar.weixin.mp.bean.outxmlbuilder; -import me.chanjar.weixin.bean.WxXmlOutVoiceMessage; +import me.chanjar.weixin.mp.bean.WxXmlOutVoiceMessage; /** * 语音消息builder diff --git a/src/main/java/me/chanjar/weixin/bean/result/WxError.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxError.java similarity index 98% rename from src/main/java/me/chanjar/weixin/bean/result/WxError.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxError.java index 1e42e42de..37d32aefa 100644 --- a/src/main/java/me/chanjar/weixin/bean/result/WxError.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxError.java @@ -1,9 +1,9 @@ -package me.chanjar.weixin.bean.result; +package me.chanjar.weixin.mp.bean.result; import java.util.HashMap; import java.util.Map; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** * 微信错误码说明 diff --git a/src/main/java/me/chanjar/weixin/bean/result/WxMassSendResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMassSendResult.java similarity index 92% rename from src/main/java/me/chanjar/weixin/bean/result/WxMassSendResult.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMassSendResult.java index ed23e86ec..d71b579d2 100644 --- a/src/main/java/me/chanjar/weixin/bean/result/WxMassSendResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMassSendResult.java @@ -1,6 +1,6 @@ -package me.chanjar.weixin.bean.result; +package me.chanjar.weixin.mp.bean.result; -import me.chanjar.weixin.util.json.WxGsonBuilder; +import me.chanjar.weixin.mp.util.json.WxGsonBuilder; /** *
diff --git a/src/main/java/me/chanjar/weixin/bean/result/WxMassUploadResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMassUploadResult.java
similarity index 90%
rename from src/main/java/me/chanjar/weixin/bean/result/WxMassUploadResult.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMassUploadResult.java
index 074ea3d3b..f3efe6859 100644
--- a/src/main/java/me/chanjar/weixin/bean/result/WxMassUploadResult.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMassUploadResult.java
@@ -1,6 +1,6 @@
-package me.chanjar.weixin.bean.result;
+package me.chanjar.weixin.mp.bean.result;
 
-import me.chanjar.weixin.util.json.WxGsonBuilder;
+import me.chanjar.weixin.mp.util.json.WxGsonBuilder;
 
 /**
  * 
diff --git a/src/main/java/me/chanjar/weixin/bean/result/WxMediaUploadResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMediaUploadResult.java
similarity index 91%
rename from src/main/java/me/chanjar/weixin/bean/result/WxMediaUploadResult.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMediaUploadResult.java
index e3dba600c..83a838df7 100644
--- a/src/main/java/me/chanjar/weixin/bean/result/WxMediaUploadResult.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMediaUploadResult.java
@@ -1,6 +1,6 @@
-package me.chanjar.weixin.bean.result;
+package me.chanjar.weixin.mp.bean.result;
 
-import me.chanjar.weixin.util.json.WxGsonBuilder;
+import me.chanjar.weixin.mp.util.json.WxGsonBuilder;
 
 public class WxMediaUploadResult {
 
diff --git a/src/main/java/me/chanjar/weixin/bean/result/WxQrCodeTicket.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxQrCodeTicket.java
similarity index 89%
rename from src/main/java/me/chanjar/weixin/bean/result/WxQrCodeTicket.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxQrCodeTicket.java
index d9da02aab..fd13ade83 100644
--- a/src/main/java/me/chanjar/weixin/bean/result/WxQrCodeTicket.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxQrCodeTicket.java
@@ -1,6 +1,6 @@
-package me.chanjar.weixin.bean.result;
+package me.chanjar.weixin.mp.bean.result;
 
-import me.chanjar.weixin.util.json.WxGsonBuilder;
+import me.chanjar.weixin.mp.util.json.WxGsonBuilder;
 
 /**
  * 换取二维码的Ticket
diff --git a/src/main/java/me/chanjar/weixin/bean/result/WxUser.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxUser.java
similarity index 95%
rename from src/main/java/me/chanjar/weixin/bean/result/WxUser.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxUser.java
index a3103fc65..c6fd81e5f 100644
--- a/src/main/java/me/chanjar/weixin/bean/result/WxUser.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxUser.java
@@ -1,6 +1,6 @@
-package me.chanjar.weixin.bean.result;
+package me.chanjar.weixin.mp.bean.result;
 
-import me.chanjar.weixin.util.json.WxGsonBuilder;
+import me.chanjar.weixin.mp.util.json.WxGsonBuilder;
 
 /**
  * 微信用户信息
diff --git a/src/main/java/me/chanjar/weixin/bean/result/WxUserList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxUserList.java
similarity index 90%
rename from src/main/java/me/chanjar/weixin/bean/result/WxUserList.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxUserList.java
index fbcce2183..29ab1a60f 100644
--- a/src/main/java/me/chanjar/weixin/bean/result/WxUserList.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxUserList.java
@@ -1,10 +1,9 @@
-package me.chanjar.weixin.bean.result;
+package me.chanjar.weixin.mp.bean.result;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import me.chanjar.weixin.util.json.WxGsonBuilder;
-
+import me.chanjar.weixin.mp.util.json.WxGsonBuilder;
 
 /**
  * 关注者列表
diff --git a/src/main/java/me/chanjar/weixin/exception/WxErrorException.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/exception/WxErrorException.java
similarity index 77%
rename from src/main/java/me/chanjar/weixin/exception/WxErrorException.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/exception/WxErrorException.java
index 311d00fb1..5685289d1 100644
--- a/src/main/java/me/chanjar/weixin/exception/WxErrorException.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/exception/WxErrorException.java
@@ -1,6 +1,6 @@
-package me.chanjar.weixin.exception;
+package me.chanjar.weixin.mp.exception;
 
-import me.chanjar.weixin.bean.result.WxError;
+import me.chanjar.weixin.mp.bean.result.WxError;
 
 public class WxErrorException extends Exception {
 
diff --git a/src/main/java/me/chanjar/weixin/util/crypto/ByteGroup.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/ByteGroup.java
similarity index 86%
rename from src/main/java/me/chanjar/weixin/util/crypto/ByteGroup.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/ByteGroup.java
index b0bda3cb1..329bf4172 100755
--- a/src/main/java/me/chanjar/weixin/util/crypto/ByteGroup.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/ByteGroup.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.crypto;
+package me.chanjar.weixin.mp.util.crypto;
 
 import java.util.ArrayList;
 
diff --git a/src/main/java/me/chanjar/weixin/util/crypto/PKCS7Encoder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/PKCS7Encoder.java
similarity index 93%
rename from src/main/java/me/chanjar/weixin/util/crypto/PKCS7Encoder.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/PKCS7Encoder.java
index e02b509cf..5bd23383f 100755
--- a/src/main/java/me/chanjar/weixin/util/crypto/PKCS7Encoder.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/PKCS7Encoder.java
@@ -6,7 +6,7 @@
 
 // ------------------------------------------------------------------------
 
-package me.chanjar.weixin.util.crypto;
+package me.chanjar.weixin.mp.util.crypto;
 
 import java.nio.charset.Charset;
 import java.util.Arrays;
diff --git a/src/main/java/me/chanjar/weixin/util/crypto/SHA1.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/SHA1.java
similarity index 95%
rename from src/main/java/me/chanjar/weixin/util/crypto/SHA1.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/SHA1.java
index eec3c53ba..1a842822a 100644
--- a/src/main/java/me/chanjar/weixin/util/crypto/SHA1.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/SHA1.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.crypto;
+package me.chanjar.weixin.mp.util.crypto;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
diff --git a/src/main/java/me/chanjar/weixin/util/crypto/WxCryptUtil.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxCryptUtil.java
similarity index 96%
rename from src/main/java/me/chanjar/weixin/util/crypto/WxCryptUtil.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxCryptUtil.java
index 764d851b5..d4931472a 100755
--- a/src/main/java/me/chanjar/weixin/util/crypto/WxCryptUtil.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/crypto/WxCryptUtil.java
@@ -11,7 +11,7 @@
  * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
  * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
  */
-package me.chanjar.weixin.util.crypto;
+package me.chanjar.weixin.mp.util.crypto;
 
 import java.io.StringReader;
 import java.nio.charset.Charset;
@@ -26,7 +26,7 @@ import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
-import me.chanjar.weixin.api.WxConfigStorage;
+import me.chanjar.weixin.mp.api.WxConfigStorage;
 import org.apache.commons.codec.binary.Base64;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
diff --git a/src/main/java/me/chanjar/weixin/util/fs/FileUtils.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/fs/FileUtils.java
similarity index 96%
rename from src/main/java/me/chanjar/weixin/util/fs/FileUtils.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/fs/FileUtils.java
index 1acc4561c..c73efa587 100644
--- a/src/main/java/me/chanjar/weixin/util/fs/FileUtils.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/fs/FileUtils.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.fs;
+package me.chanjar.weixin.mp.util.fs;
 
 import java.io.File;
 import java.io.FileOutputStream;
diff --git a/src/main/java/me/chanjar/weixin/util/http/InputStreamResponseHandler.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/InputStreamResponseHandler.java
similarity index 95%
rename from src/main/java/me/chanjar/weixin/util/http/InputStreamResponseHandler.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/InputStreamResponseHandler.java
index 8854e0f8c..e8926a44a 100644
--- a/src/main/java/me/chanjar/weixin/util/http/InputStreamResponseHandler.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/InputStreamResponseHandler.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.http;
+package me.chanjar.weixin.mp.util.http;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/src/main/java/me/chanjar/weixin/util/http/MediaDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaDownloadRequestExecutor.java
similarity index 92%
rename from src/main/java/me/chanjar/weixin/util/http/MediaDownloadRequestExecutor.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaDownloadRequestExecutor.java
index b62ad35fd..83df39dab 100644
--- a/src/main/java/me/chanjar/weixin/util/http/MediaDownloadRequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaDownloadRequestExecutor.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.http;
+package me.chanjar.weixin.mp.util.http;
 
 import java.io.File;
 import java.io.IOException;
@@ -6,6 +6,9 @@ import java.io.InputStream;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import me.chanjar.weixin.mp.bean.result.WxError;
+import me.chanjar.weixin.mp.exception.WxErrorException;
+import me.chanjar.weixin.mp.util.fs.FileUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.Header;
 import org.apache.http.client.ClientProtocolException;
@@ -13,10 +16,6 @@ import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.entity.ContentType;
 
-import me.chanjar.weixin.bean.result.WxError;
-import me.chanjar.weixin.exception.WxErrorException;
-import me.chanjar.weixin.util.fs.FileUtils;
-
 /**
  * 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File
  * @author chanjarster
diff --git a/src/main/java/me/chanjar/weixin/util/http/MediaUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaUploadRequestExecutor.java
similarity index 87%
rename from src/main/java/me/chanjar/weixin/util/http/MediaUploadRequestExecutor.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaUploadRequestExecutor.java
index 15c8c8a0e..d7c85fdf8 100644
--- a/src/main/java/me/chanjar/weixin/util/http/MediaUploadRequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/MediaUploadRequestExecutor.java
@@ -1,8 +1,11 @@
-package me.chanjar.weixin.util.http;
+package me.chanjar.weixin.mp.util.http;
 
 import java.io.File;
 import java.io.IOException;
 
+import me.chanjar.weixin.mp.bean.result.WxError;
+import me.chanjar.weixin.mp.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 import org.apache.http.HttpEntity;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.methods.CloseableHttpResponse;
@@ -10,10 +13,6 @@ import org.apache.http.client.methods.HttpPost;
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.MultipartEntityBuilder;
 
-import me.chanjar.weixin.bean.result.WxError;
-import me.chanjar.weixin.bean.result.WxMediaUploadResult;
-import me.chanjar.weixin.exception.WxErrorException;
-
 /**
  * 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String
  * @author chanjarster
diff --git a/src/main/java/me/chanjar/weixin/util/http/QrCodeRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java
similarity index 87%
rename from src/main/java/me/chanjar/weixin/util/http/QrCodeRequestExecutor.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java
index b33fb5010..e018d7248 100644
--- a/src/main/java/me/chanjar/weixin/util/http/QrCodeRequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/QrCodeRequestExecutor.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.http;
+package me.chanjar.weixin.mp.util.http;
 
 import java.io.File;
 import java.io.IOException;
@@ -12,10 +12,10 @@ import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.entity.ContentType;
 
-import me.chanjar.weixin.bean.result.WxError;
-import me.chanjar.weixin.bean.result.WxQrCodeTicket;
-import me.chanjar.weixin.exception.WxErrorException;
-import me.chanjar.weixin.util.fs.FileUtils;
+import me.chanjar.weixin.mp.bean.result.WxError;
+import me.chanjar.weixin.mp.bean.result.WxQrCodeTicket;
+import me.chanjar.weixin.mp.exception.WxErrorException;
+import me.chanjar.weixin.mp.util.fs.FileUtils;
 
 /**
  * 获得QrCode图片 请求执行器
diff --git a/src/main/java/me/chanjar/weixin/util/http/RequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/RequestExecutor.java
similarity index 85%
rename from src/main/java/me/chanjar/weixin/util/http/RequestExecutor.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/RequestExecutor.java
index 589016728..f729d08a7 100644
--- a/src/main/java/me/chanjar/weixin/util/http/RequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/RequestExecutor.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.http;
+package me.chanjar.weixin.mp.util.http;
 
 import java.io.IOException;
 
@@ -6,7 +6,7 @@ import org.apache.http.client.ClientProtocolException;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 /**
  * http请求执行器
diff --git a/src/main/java/me/chanjar/weixin/util/http/SimpleGetRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/SimpleGetRequestExecutor.java
similarity index 87%
rename from src/main/java/me/chanjar/weixin/util/http/SimpleGetRequestExecutor.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/SimpleGetRequestExecutor.java
index 079241070..d03145164 100644
--- a/src/main/java/me/chanjar/weixin/util/http/SimpleGetRequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/SimpleGetRequestExecutor.java
@@ -1,14 +1,13 @@
-package me.chanjar.weixin.util.http;
+package me.chanjar.weixin.mp.util.http;
 
 import java.io.IOException;
 
+import me.chanjar.weixin.mp.bean.result.WxError;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 
-import me.chanjar.weixin.bean.result.WxError;
-import me.chanjar.weixin.exception.WxErrorException;
-
 /**
  * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String
  * @author chanjarster
diff --git a/src/main/java/me/chanjar/weixin/util/http/SimplePostRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/SimplePostRequestExecutor.java
similarity index 88%
rename from src/main/java/me/chanjar/weixin/util/http/SimplePostRequestExecutor.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/SimplePostRequestExecutor.java
index ffb053272..2af893268 100644
--- a/src/main/java/me/chanjar/weixin/util/http/SimplePostRequestExecutor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/SimplePostRequestExecutor.java
@@ -1,9 +1,9 @@
-package me.chanjar.weixin.util.http;
+package me.chanjar.weixin.mp.util.http;
 
 import java.io.IOException;
 
-import me.chanjar.weixin.bean.result.WxError;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.result.WxError;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 import org.apache.http.Consts;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.methods.CloseableHttpResponse;
diff --git a/src/main/java/me/chanjar/weixin/util/http/Utf8ResponseHandler.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/Utf8ResponseHandler.java
similarity index 96%
rename from src/main/java/me/chanjar/weixin/util/http/Utf8ResponseHandler.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/Utf8ResponseHandler.java
index 3c19c132e..e4b008def 100644
--- a/src/main/java/me/chanjar/weixin/util/http/Utf8ResponseHandler.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/http/Utf8ResponseHandler.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.http;
+package me.chanjar.weixin.mp.util.http;
 
 import java.io.IOException;
 
diff --git a/src/main/java/me/chanjar/weixin/util/json/GsonHelper.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/GsonHelper.java
similarity index 98%
rename from src/main/java/me/chanjar/weixin/util/json/GsonHelper.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/GsonHelper.java
index 4dc16de05..7957dae05 100644
--- a/src/main/java/me/chanjar/weixin/util/json/GsonHelper.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/GsonHelper.java
@@ -6,7 +6,7 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 
 import com.google.gson.JsonElement;
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxAccessTokenAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxAccessTokenAdapter.java
similarity index 93%
rename from src/main/java/me/chanjar/weixin/util/json/WxAccessTokenAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxAccessTokenAdapter.java
index 60e3475af..b91e678ca 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxAccessTokenAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxAccessTokenAdapter.java
@@ -6,10 +6,10 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import com.google.gson.*;
-import me.chanjar.weixin.bean.WxAccessToken;
+import me.chanjar.weixin.mp.bean.WxAccessToken;
 
 import java.lang.reflect.Type;
 
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxCustomMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxCustomMessageGsonAdapter.java
similarity index 96%
rename from src/main/java/me/chanjar/weixin/util/json/WxCustomMessageGsonAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxCustomMessageGsonAdapter.java
index d1dc55c6f..699b7d3fd 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxCustomMessageGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxCustomMessageGsonAdapter.java
@@ -6,12 +6,12 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import java.lang.reflect.Type;
 
-import me.chanjar.weixin.api.WxConsts;
-import me.chanjar.weixin.bean.WxCustomMessage;
+import me.chanjar.weixin.mp.api.WxConsts;
+import me.chanjar.weixin.mp.bean.WxCustomMessage;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxErrorAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxErrorAdapter.java
new file mode 100644
index 000000000..e85314b01
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxErrorAdapter.java
@@ -0,0 +1,36 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package me.chanjar.weixin.mp.util.json;
+
+import com.google.gson.*;
+import me.chanjar.weixin.mp.bean.result.WxError;
+
+import java.lang.reflect.Type;
+
+/**
+ * 
+ * @author Daniel Qian
+ *
+ */
+public class WxErrorAdapter implements JsonDeserializer {
+
+  public WxError deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    WxError wxError = new WxError();
+    JsonObject wxErrorJsonObject = json.getAsJsonObject();
+
+    if (wxErrorJsonObject.get("errcode") != null && !wxErrorJsonObject.get("errcode").isJsonNull()) {
+      wxError.setErrorCode(GsonHelper.getAsPrimitiveInt(wxErrorJsonObject.get("errcode")));
+    }
+    if (wxErrorJsonObject.get("errmsg") != null && !wxErrorJsonObject.get("errmsg").isJsonNull()) {
+      wxError.setErrorMsg(GsonHelper.getAsString(wxErrorJsonObject.get("errmsg")));
+    }
+    return wxError;
+  }
+  
+}
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxGroupGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxGroupGsonAdapter.java
similarity index 96%
rename from src/main/java/me/chanjar/weixin/util/json/WxGroupGsonAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxGroupGsonAdapter.java
index c95e84e93..9a4214302 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxGroupGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxGroupGsonAdapter.java
@@ -6,11 +6,11 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import java.lang.reflect.Type;
 
-import me.chanjar.weixin.bean.WxGroup;
+import me.chanjar.weixin.mp.bean.WxGroup;
 
 import com.google.gson.JsonDeserializationContext;
 import com.google.gson.JsonDeserializer;
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxGsonBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxGsonBuilder.java
similarity index 93%
rename from src/main/java/me/chanjar/weixin/util/json/WxGsonBuilder.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxGsonBuilder.java
index 09d8247cd..320335039 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxGsonBuilder.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxGsonBuilder.java
@@ -1,10 +1,11 @@
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import me.chanjar.weixin.bean.*;
-import me.chanjar.weixin.bean.result.*;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import me.chanjar.weixin.mp.bean.*;
+import me.chanjar.weixin.mp.bean.result.*;
 
 public class WxGsonBuilder {
 
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxMassMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassMessageGsonAdapter.java
similarity index 94%
rename from src/main/java/me/chanjar/weixin/util/json/WxMassMessageGsonAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassMessageGsonAdapter.java
index cd985a465..5a24b5bb9 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxMassMessageGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassMessageGsonAdapter.java
@@ -6,12 +6,12 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import java.lang.reflect.Type;
 
-import me.chanjar.weixin.api.WxConsts;
-import me.chanjar.weixin.bean.WxMassGroupMessage;
+import me.chanjar.weixin.mp.api.WxConsts;
+import me.chanjar.weixin.mp.bean.WxMassGroupMessage;
 
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxMassNewsGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassNewsGsonAdapter.java
similarity index 95%
rename from src/main/java/me/chanjar/weixin/util/json/WxMassNewsGsonAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassNewsGsonAdapter.java
index fcaa4c1ad..42b35da19 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxMassNewsGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassNewsGsonAdapter.java
@@ -6,11 +6,11 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import java.lang.reflect.Type;
 
-import me.chanjar.weixin.bean.WxMassNews;
+import me.chanjar.weixin.mp.bean.WxMassNews;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxMassOpenIdsMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassOpenIdsMessageGsonAdapter.java
similarity index 94%
rename from src/main/java/me/chanjar/weixin/util/json/WxMassOpenIdsMessageGsonAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassOpenIdsMessageGsonAdapter.java
index 3cb1e7f6b..2b8a7403d 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxMassOpenIdsMessageGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassOpenIdsMessageGsonAdapter.java
@@ -6,12 +6,12 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import java.lang.reflect.Type;
 
-import me.chanjar.weixin.api.WxConsts;
-import me.chanjar.weixin.bean.WxMassOpenIdsMessage;
+import me.chanjar.weixin.mp.api.WxConsts;
+import me.chanjar.weixin.mp.bean.WxMassOpenIdsMessage;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxMassSendResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassSendResultAdapter.java
similarity index 93%
rename from src/main/java/me/chanjar/weixin/util/json/WxMassSendResultAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassSendResultAdapter.java
index ad5dbd6e7..da8085d13 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxMassSendResultAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassSendResultAdapter.java
@@ -6,10 +6,10 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import com.google.gson.*;
-import me.chanjar.weixin.bean.result.WxMassSendResult;
+import me.chanjar.weixin.mp.bean.result.WxMassSendResult;
 
 import java.lang.reflect.Type;
 
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxMassUploadResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassUploadResultAdapter.java
similarity index 94%
rename from src/main/java/me/chanjar/weixin/util/json/WxMassUploadResultAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassUploadResultAdapter.java
index c58f35244..30fa554c1 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxMassUploadResultAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassUploadResultAdapter.java
@@ -6,10 +6,10 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import com.google.gson.*;
-import me.chanjar.weixin.bean.result.WxMassUploadResult;
+import me.chanjar.weixin.mp.bean.result.WxMassUploadResult;
 
 import java.lang.reflect.Type;
 
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxMassVideoAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassVideoAdapter.java
similarity index 91%
rename from src/main/java/me/chanjar/weixin/util/json/WxMassVideoAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassVideoAdapter.java
index ba98acf8a..d7071f9b8 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxMassVideoAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMassVideoAdapter.java
@@ -6,10 +6,10 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import com.google.gson.*;
-import me.chanjar.weixin.bean.WxMassVideo;
+import me.chanjar.weixin.mp.bean.WxMassVideo;
 
 import java.lang.reflect.Type;
 
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxMediaUploadResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMediaUploadResultAdapter.java
similarity index 94%
rename from src/main/java/me/chanjar/weixin/util/json/WxMediaUploadResultAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMediaUploadResultAdapter.java
index 0fbfa7acf..eec8519c5 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxMediaUploadResultAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMediaUploadResultAdapter.java
@@ -6,10 +6,10 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import com.google.gson.*;
-import me.chanjar.weixin.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.mp.bean.result.WxMediaUploadResult;
 
 import java.lang.reflect.Type;
 
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxMenuGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMenuGsonAdapter.java
similarity index 97%
rename from src/main/java/me/chanjar/weixin/util/json/WxMenuGsonAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMenuGsonAdapter.java
index 6fae9d2c9..f60d39ea5 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxMenuGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMenuGsonAdapter.java
@@ -6,11 +6,11 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import java.lang.reflect.Type;
 
-import me.chanjar.weixin.bean.WxMenu;
+import me.chanjar.weixin.mp.bean.WxMenu;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonDeserializationContext;
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxQrCodeTicketAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxQrCodeTicketAdapter.java
new file mode 100644
index 000000000..441644792
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxQrCodeTicketAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package me.chanjar.weixin.mp.util.json;
+
+import com.google.gson.*;
+import me.chanjar.weixin.mp.bean.result.WxQrCodeTicket;
+
+import java.lang.reflect.Type;
+
+/**
+ * 
+ * @author Daniel Qian
+ *
+ */
+public class WxQrCodeTicketAdapter implements JsonDeserializer {
+
+  public WxQrCodeTicket deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    WxQrCodeTicket ticket = new WxQrCodeTicket();
+    JsonObject ticketJsonObject = json.getAsJsonObject();
+
+    if (ticketJsonObject.get("ticket") != null && !ticketJsonObject.get("ticket").isJsonNull()) {
+      ticket.setTicket(GsonHelper.getAsString(ticketJsonObject.get("ticket")));
+    }
+    if (ticketJsonObject.get("expire_seconds") != null && !ticketJsonObject.get("expire_seconds").isJsonNull()) {
+      ticket.setExpire_seconds(GsonHelper.getAsPrimitiveInt(ticketJsonObject.get("expire_seconds")));
+    }
+    if (ticketJsonObject.get("url") != null && !ticketJsonObject.get("url").isJsonNull()) {
+      ticket.setUrl(GsonHelper.getAsString(ticketJsonObject.get("url")));
+    }
+    return ticket;
+  }
+  
+}
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxUserGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserGsonAdapter.java
similarity index 95%
rename from src/main/java/me/chanjar/weixin/util/json/WxUserGsonAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserGsonAdapter.java
index ea50adfc0..d6bd0f9c8 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxUserGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserGsonAdapter.java
@@ -6,11 +6,11 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import java.lang.reflect.Type;
 
-import me.chanjar.weixin.bean.result.WxUser;
+import me.chanjar.weixin.mp.bean.result.WxUser;
 
 import com.google.gson.JsonDeserializationContext;
 import com.google.gson.JsonDeserializer;
diff --git a/src/main/java/me/chanjar/weixin/util/json/WxUserListGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java
similarity index 94%
rename from src/main/java/me/chanjar/weixin/util/json/WxUserListGsonAdapter.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java
index fe5ae7305..56fa11387 100644
--- a/src/main/java/me/chanjar/weixin/util/json/WxUserListGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java
@@ -6,11 +6,11 @@
  * arose from modification of the original source, or other redistribution of this source
  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
  */
-package me.chanjar.weixin.util.json;
+package me.chanjar.weixin.mp.util.json;
 
 import java.lang.reflect.Type;
 
-import me.chanjar.weixin.bean.result.WxUserList;
+import me.chanjar.weixin.mp.bean.result.WxUserList;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonDeserializationContext;
diff --git a/src/main/java/me/chanjar/weixin/util/xml/AdapterCDATA.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/AdapterCDATA.java
similarity index 92%
rename from src/main/java/me/chanjar/weixin/util/xml/AdapterCDATA.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/AdapterCDATA.java
index edcceed88..b7e137153 100644
--- a/src/main/java/me/chanjar/weixin/util/xml/AdapterCDATA.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/AdapterCDATA.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.xml;
+package me.chanjar.weixin.mp.util.xml;
 
 import javax.xml.bind.annotation.adapters.XmlAdapter;
 
diff --git a/src/main/java/me/chanjar/weixin/util/xml/MediaIdMarshaller.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/MediaIdMarshaller.java
similarity index 91%
rename from src/main/java/me/chanjar/weixin/util/xml/MediaIdMarshaller.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/MediaIdMarshaller.java
index 392251369..1be8a080e 100644
--- a/src/main/java/me/chanjar/weixin/util/xml/MediaIdMarshaller.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/MediaIdMarshaller.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.xml;
+package me.chanjar.weixin.mp.util.xml;
 
 import javax.xml.bind.annotation.adapters.XmlAdapter;
 
diff --git a/src/main/java/me/chanjar/weixin/util/xml/XmlTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XmlTransformer.java
similarity index 97%
rename from src/main/java/me/chanjar/weixin/util/xml/XmlTransformer.java
rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XmlTransformer.java
index 026cc0a56..707b8743c 100644
--- a/src/main/java/me/chanjar/weixin/util/xml/XmlTransformer.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XmlTransformer.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.util.xml;
+package me.chanjar.weixin.mp.util.xml;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -9,6 +9,7 @@ import java.io.Writer;
 import javax.xml.bind.*;
 
 import me.chanjar.weixin.bean.*;
+import me.chanjar.weixin.mp.bean.*;
 import org.xml.sax.InputSource;
 
 import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
diff --git a/src/test/java/me/chanjar/weixin/api/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
similarity index 90%
rename from src/test/java/me/chanjar/weixin/api/ApiTestModule.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
index 68a9f8a27..07a20e4a3 100644
--- a/src/test/java/me/chanjar/weixin/api/ApiTestModule.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import java.io.InputStream;
 
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 
-import me.chanjar.weixin.api.WxInMemoryConfigStorage;
-import me.chanjar.weixin.api.WxServiceImpl;
-import me.chanjar.weixin.util.xml.XmlTransformer;
+import me.chanjar.weixin.mp.api.WxConfigStorage;
+import me.chanjar.weixin.mp.api.WxInMemoryConfigStorage;
+import me.chanjar.weixin.mp.api.WxServiceImpl;
 
 import com.google.inject.Binder;
 import com.google.inject.Module;
diff --git a/src/test/java/me/chanjar/weixin/api/WxBaseAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxBaseAPITest.java
similarity index 89%
rename from src/test/java/me/chanjar/weixin/api/WxBaseAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxBaseAPITest.java
index af5246157..b6f565e65 100644
--- a/src/test/java/me/chanjar/weixin/api/WxBaseAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxBaseAPITest.java
@@ -1,11 +1,11 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import org.apache.commons.lang3.StringUtils;
 import org.testng.Assert;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 import com.google.inject.Inject;
 
diff --git a/src/test/java/me/chanjar/weixin/api/WxCustomMessageAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxCustomMessageAPITest.java
similarity index 86%
rename from src/test/java/me/chanjar/weixin/api/WxCustomMessageAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxCustomMessageAPITest.java
index d8a955a55..5a601a518 100644
--- a/src/test/java/me/chanjar/weixin/api/WxCustomMessageAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxCustomMessageAPITest.java
@@ -1,10 +1,10 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.bean.WxCustomMessage;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.WxCustomMessage;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 import com.google.inject.Inject;
 
diff --git a/src/test/java/me/chanjar/weixin/api/WxGroupAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxGroupAPITest.java
similarity index 89%
rename from src/test/java/me/chanjar/weixin/api/WxGroupAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxGroupAPITest.java
index 74abb5637..7fdb520f8 100644
--- a/src/test/java/me/chanjar/weixin/api/WxGroupAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxGroupAPITest.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import java.util.List;
 
@@ -6,8 +6,8 @@ import org.testng.Assert;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.bean.WxGroup;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.WxGroup;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 import com.google.inject.Inject;
 
diff --git a/src/test/java/me/chanjar/weixin/api/WxMassMessageAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMassMessageAPITest.java
similarity index 89%
rename from src/test/java/me/chanjar/weixin/api/WxMassMessageAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMassMessageAPITest.java
index 26e8b352c..53cf74f94 100644
--- a/src/test/java/me/chanjar/weixin/api/WxMassMessageAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMassMessageAPITest.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -8,16 +8,15 @@ import org.testng.annotations.DataProvider;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.api.ApiTestModule.WxXmlConfigStorage;
-import me.chanjar.weixin.bean.WxMassGroupMessage;
-import me.chanjar.weixin.bean.WxMassNews;
-import me.chanjar.weixin.bean.WxMassNews.WxMassNewsArticle;
-import me.chanjar.weixin.bean.WxMassOpenIdsMessage;
-import me.chanjar.weixin.bean.WxMassVideo;
-import me.chanjar.weixin.bean.result.WxMassSendResult;
-import me.chanjar.weixin.bean.result.WxMassUploadResult;
-import me.chanjar.weixin.bean.result.WxMediaUploadResult;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.WxMassGroupMessage;
+import me.chanjar.weixin.mp.bean.WxMassNews;
+import me.chanjar.weixin.mp.bean.WxMassNews.WxMassNewsArticle;
+import me.chanjar.weixin.mp.bean.WxMassOpenIdsMessage;
+import me.chanjar.weixin.mp.bean.WxMassVideo;
+import me.chanjar.weixin.mp.bean.result.WxMassSendResult;
+import me.chanjar.weixin.mp.bean.result.WxMassUploadResult;
+import me.chanjar.weixin.mp.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 import com.google.inject.Inject;
 
@@ -36,7 +35,7 @@ public class WxMassMessageAPITest {
   @Test
   public void testTextMassOpenIdsMessageSend() throws WxErrorException {
     // 发送群发消息
-    WxXmlConfigStorage configProvider = (WxXmlConfigStorage) wxService.wxConfigStorage;
+    ApiTestModule.WxXmlConfigStorage configProvider = (ApiTestModule.WxXmlConfigStorage) wxService.wxConfigStorage;
     WxMassOpenIdsMessage massMessage = new WxMassOpenIdsMessage();
     massMessage.setMsgType(WxConsts.MASS_MSG_TEXT);
     massMessage.setContent("测试群发消息\n欢迎欢迎,热烈欢迎\n换行测试\n超链接:Hello World");
@@ -50,7 +49,7 @@ public class WxMassMessageAPITest {
   @Test(dataProvider="massMessages")
   public void testMediaMassOpenIdsMessageSend(String massMsgType, String mediaId) throws WxErrorException, IOException {
     // 发送群发消息
-    WxXmlConfigStorage configProvider = (WxXmlConfigStorage) wxService.wxConfigStorage;
+    ApiTestModule.WxXmlConfigStorage configProvider = (ApiTestModule.WxXmlConfigStorage) wxService.wxConfigStorage;
     WxMassOpenIdsMessage massMessage = new WxMassOpenIdsMessage();
     massMessage.setMsgType(massMsgType);
     massMessage.setMediaId(mediaId);
diff --git a/src/test/java/me/chanjar/weixin/api/WxMediaAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMediaAPITest.java
similarity index 93%
rename from src/test/java/me/chanjar/weixin/api/WxMediaAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMediaAPITest.java
index f5d59594f..58c7c25fc 100644
--- a/src/test/java/me/chanjar/weixin/api/WxMediaAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMediaAPITest.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -10,8 +10,8 @@ import org.testng.annotations.DataProvider;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.bean.result.WxMediaUploadResult;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 import com.google.inject.Inject;
 
diff --git a/src/test/java/me/chanjar/weixin/api/WxMenuAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMenuAPITest.java
similarity index 92%
rename from src/test/java/me/chanjar/weixin/api/WxMenuAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMenuAPITest.java
index e0f3d3a3c..1050bc944 100644
--- a/src/test/java/me/chanjar/weixin/api/WxMenuAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMenuAPITest.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import javax.xml.bind.JAXBException;
 
@@ -9,9 +9,9 @@ import org.testng.annotations.Test;
 
 import com.google.inject.Inject;
 
-import me.chanjar.weixin.bean.WxMenu;
-import me.chanjar.weixin.bean.WxMenu.WxMenuButton;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.WxMenu;
+import me.chanjar.weixin.mp.bean.WxMenu.WxMenuButton;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 /**
  * 测试菜单
diff --git a/src/test/java/me/chanjar/weixin/api/WxMessageRouterTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMessageRouterTest.java
similarity index 94%
rename from src/test/java/me/chanjar/weixin/api/WxMessageRouterTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMessageRouterTest.java
index f1d1163dc..9b944bcbc 100644
--- a/src/test/java/me/chanjar/weixin/api/WxMessageRouterTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMessageRouterTest.java
@@ -1,16 +1,16 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import java.util.Map;
 
-import me.chanjar.weixin.api.WxConsts;
-import me.chanjar.weixin.api.WxMessageHandler;
-import me.chanjar.weixin.api.WxMessageRouter;
+import me.chanjar.weixin.mp.api.WxConsts;
+import me.chanjar.weixin.mp.api.WxMessageHandler;
+import me.chanjar.weixin.mp.api.WxMessageRouter;
 import org.testng.Assert;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.bean.WxXmlMessage;
-import me.chanjar.weixin.bean.WxXmlOutMessage;
+import me.chanjar.weixin.mp.bean.WxXmlMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutMessage;
 
 /**
  * 测试消息路由器
diff --git a/src/test/java/me/chanjar/weixin/api/WxQrCodeAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxQrCodeAPITest.java
similarity index 89%
rename from src/test/java/me/chanjar/weixin/api/WxQrCodeAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxQrCodeAPITest.java
index 1c07072f5..0e4fef1e1 100644
--- a/src/test/java/me/chanjar/weixin/api/WxQrCodeAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxQrCodeAPITest.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import java.io.File;
 
@@ -6,8 +6,8 @@ import org.testng.Assert;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.bean.result.WxQrCodeTicket;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.result.WxQrCodeTicket;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 import com.google.inject.Inject;
 
diff --git a/src/test/java/me/chanjar/weixin/api/WxShortUrlAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxShortUrlAPITest.java
similarity index 85%
rename from src/test/java/me/chanjar/weixin/api/WxShortUrlAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxShortUrlAPITest.java
index 3df6e2178..ac815b030 100644
--- a/src/test/java/me/chanjar/weixin/api/WxShortUrlAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxShortUrlAPITest.java
@@ -1,10 +1,10 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import org.testng.Assert;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 import com.google.inject.Inject;
 
diff --git a/src/test/java/me/chanjar/weixin/api/WxUserAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxUserAPITest.java
similarity index 87%
rename from src/test/java/me/chanjar/weixin/api/WxUserAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxUserAPITest.java
index f737dba45..ce4ba6c4e 100644
--- a/src/test/java/me/chanjar/weixin/api/WxUserAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxUserAPITest.java
@@ -1,13 +1,13 @@
-package me.chanjar.weixin.api;
+package me.chanjar.weixin.mp.api;
 
 import org.testng.Assert;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.api.ApiTestModule.WxXmlConfigStorage;
-import me.chanjar.weixin.bean.result.WxUser;
-import me.chanjar.weixin.bean.result.WxUserList;
-import me.chanjar.weixin.exception.WxErrorException;
+import me.chanjar.weixin.mp.api.ApiTestModule.WxXmlConfigStorage;
+import me.chanjar.weixin.mp.bean.result.WxUser;
+import me.chanjar.weixin.mp.bean.result.WxUserList;
+import me.chanjar.weixin.mp.exception.WxErrorException;
 
 import com.google.inject.Inject;
 
diff --git a/src/test/java/me/chanjar/weixin/bean/WxAccessTokenTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxAccessTokenTest.java
similarity index 82%
rename from src/test/java/me/chanjar/weixin/bean/WxAccessTokenTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxAccessTokenTest.java
index 7391f49b7..2ada1fbd3 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxAccessTokenTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxAccessTokenTest.java
@@ -1,5 +1,6 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxAccessToken;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/src/test/java/me/chanjar/weixin/bean/WxCustomMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxCustomMessageTest.java
similarity index 96%
rename from src/test/java/me/chanjar/weixin/bean/WxCustomMessageTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxCustomMessageTest.java
index 1d483898d..730900537 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxCustomMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxCustomMessageTest.java
@@ -1,10 +1,11 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxCustomMessage;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.api.WxConsts;
-import me.chanjar.weixin.bean.WxCustomMessage.WxArticle;
+import me.chanjar.weixin.mp.api.WxConsts;
+import me.chanjar.weixin.mp.bean.WxCustomMessage.WxArticle;
 
 @Test
 public class WxCustomMessageTest {
diff --git a/src/test/java/me/chanjar/weixin/bean/WxErrorTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxErrorTest.java
similarity index 92%
rename from src/test/java/me/chanjar/weixin/bean/WxErrorTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxErrorTest.java
index a7501d16f..f20f20807 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxErrorTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxErrorTest.java
@@ -1,9 +1,9 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.bean.result.WxError;
+import me.chanjar.weixin.mp.bean.result.WxError;
 
 @Test
 public class WxErrorTest {
diff --git a/src/test/java/me/chanjar/weixin/bean/WxMenuTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMenuTest.java
similarity index 95%
rename from src/test/java/me/chanjar/weixin/bean/WxMenuTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMenuTest.java
index de5e86912..0ba058c28 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxMenuTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMenuTest.java
@@ -1,10 +1,11 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxMenu;
 import org.testng.Assert;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
-import me.chanjar.weixin.bean.WxMenu.WxMenuButton;
+import me.chanjar.weixin.mp.bean.WxMenu.WxMenuButton;
 
 @Test
 public class WxMenuTest {
diff --git a/src/test/java/me/chanjar/weixin/bean/WxXmlMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlMessageTest.java
similarity index 97%
rename from src/test/java/me/chanjar/weixin/bean/WxXmlMessageTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlMessageTest.java
index 8517a8b0c..3db6e8290 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxXmlMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlMessageTest.java
@@ -1,6 +1,7 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
-import me.chanjar.weixin.api.WxConsts;
+import me.chanjar.weixin.mp.api.WxConsts;
+import me.chanjar.weixin.mp.bean.WxXmlMessage;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/src/test/java/me/chanjar/weixin/bean/WxXmlOutImageMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutImageMessageTest.java
similarity index 92%
rename from src/test/java/me/chanjar/weixin/bean/WxXmlOutImageMessageTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutImageMessageTest.java
index bbb5ba6e2..0d8b06494 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxXmlOutImageMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutImageMessageTest.java
@@ -1,5 +1,7 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxXmlOutImageMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutMessage;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/src/test/java/me/chanjar/weixin/bean/WxXmlOutMusicMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessageTest.java
similarity index 94%
rename from src/test/java/me/chanjar/weixin/bean/WxXmlOutMusicMessageTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessageTest.java
index 9a9ef6b2d..b3c4c6626 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxXmlOutMusicMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessageTest.java
@@ -1,5 +1,7 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxXmlOutMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutMusicMessage;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/src/test/java/me/chanjar/weixin/bean/WxXmlOutNewsMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutNewsMessageTest.java
similarity index 96%
rename from src/test/java/me/chanjar/weixin/bean/WxXmlOutNewsMessageTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutNewsMessageTest.java
index 3bac0da99..1fcdbbc54 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxXmlOutNewsMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutNewsMessageTest.java
@@ -1,5 +1,7 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxXmlOutMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutMewsMessage;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/src/test/java/me/chanjar/weixin/bean/WxXmlOutTextMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessageTest.java
similarity index 92%
rename from src/test/java/me/chanjar/weixin/bean/WxXmlOutTextMessageTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessageTest.java
index d3168ba63..58d7840c0 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxXmlOutTextMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessageTest.java
@@ -1,5 +1,7 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxXmlOutMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutTextMessage;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/src/test/java/me/chanjar/weixin/bean/WxXmlOutVideoMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessageTest.java
similarity index 93%
rename from src/test/java/me/chanjar/weixin/bean/WxXmlOutVideoMessageTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessageTest.java
index 5a61e347c..16e08fa5c 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxXmlOutVideoMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessageTest.java
@@ -1,5 +1,7 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxXmlOutMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutVideoMessage;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/src/test/java/me/chanjar/weixin/bean/WxXmlOutVoiceMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessageTest.java
similarity index 92%
rename from src/test/java/me/chanjar/weixin/bean/WxXmlOutVoiceMessageTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessageTest.java
index e59608752..b3aee0e04 100644
--- a/src/test/java/me/chanjar/weixin/bean/WxXmlOutVoiceMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessageTest.java
@@ -1,5 +1,7 @@
-package me.chanjar.weixin.bean;
+package me.chanjar.weixin.mp.bean;
 
+import me.chanjar.weixin.mp.bean.WxXmlOutMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutVoiceMessage;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
diff --git a/src/test/java/me/chanjar/weixin/demo/WxTestConfigStorage.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestConfigStorage.java
similarity index 91%
rename from src/test/java/me/chanjar/weixin/demo/WxTestConfigStorage.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestConfigStorage.java
index d5921e0c0..3cfe9e478 100644
--- a/src/test/java/me/chanjar/weixin/demo/WxTestConfigStorage.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestConfigStorage.java
@@ -1,6 +1,6 @@
-package me.chanjar.weixin.demo;
+package me.chanjar.weixin.mp.demo;
 
-import me.chanjar.weixin.api.WxInMemoryConfigStorage;
+import me.chanjar.weixin.mp.api.WxInMemoryConfigStorage;
 import org.xml.sax.InputSource;
 
 import javax.xml.bind.JAXBContext;
diff --git a/src/test/java/me/chanjar/weixin/demo/WxTestServer.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestServer.java
similarity index 92%
rename from src/test/java/me/chanjar/weixin/demo/WxTestServer.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestServer.java
index e9c6d9d09..c51f0f8d5 100644
--- a/src/test/java/me/chanjar/weixin/demo/WxTestServer.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestServer.java
@@ -1,4 +1,4 @@
-package me.chanjar.weixin.demo;
+package me.chanjar.weixin.mp.demo;
 
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.servlet.ServletHandler;
diff --git a/src/test/java/me/chanjar/weixin/demo/WxTestServlet.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestServlet.java
similarity index 94%
rename from src/test/java/me/chanjar/weixin/demo/WxTestServlet.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestServlet.java
index aa042916d..91e2e2660 100644
--- a/src/test/java/me/chanjar/weixin/demo/WxTestServlet.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxTestServlet.java
@@ -1,9 +1,9 @@
-package me.chanjar.weixin.demo;
+package me.chanjar.weixin.mp.demo;
 
-import me.chanjar.weixin.api.*;
-import me.chanjar.weixin.bean.WxXmlMessage;
-import me.chanjar.weixin.bean.WxXmlOutMessage;
-import me.chanjar.weixin.bean.WxXmlOutTextMessage;
+import me.chanjar.weixin.mp.api.*;
+import me.chanjar.weixin.mp.bean.WxXmlMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutMessage;
+import me.chanjar.weixin.mp.bean.WxXmlOutTextMessage;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.servlet.ServletException;
diff --git a/src/test/java/me/chanjar/weixin/util/crypto/WxCryptUtilTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/util/crypto/WxCryptUtilTest.java
similarity index 96%
rename from src/test/java/me/chanjar/weixin/util/crypto/WxCryptUtilTest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/util/crypto/WxCryptUtilTest.java
index 0c61d3d08..bac5ae5a9 100755
--- a/src/test/java/me/chanjar/weixin/util/crypto/WxCryptUtilTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/util/crypto/WxCryptUtilTest.java
@@ -1,5 +1,6 @@
-package me.chanjar.weixin.util.crypto;
+package me.chanjar.weixin.mp.util.crypto;
 
+import me.chanjar.weixin.mp.util.crypto.WxCryptUtil;
 import org.testng.annotations.Test;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
diff --git a/weixin-java-mp/src/test/resources/mm.jpeg b/weixin-java-mp/src/test/resources/mm.jpeg
new file mode 100644
index 000000000..183699e96
Binary files /dev/null and b/weixin-java-mp/src/test/resources/mm.jpeg differ
diff --git a/weixin-java-mp/src/test/resources/mm.mp3 b/weixin-java-mp/src/test/resources/mm.mp3
new file mode 100644
index 000000000..d818e510b
Binary files /dev/null and b/weixin-java-mp/src/test/resources/mm.mp3 differ
diff --git a/weixin-java-mp/src/test/resources/mm.mp4 b/weixin-java-mp/src/test/resources/mm.mp4
new file mode 100644
index 000000000..ff74317ae
Binary files /dev/null and b/weixin-java-mp/src/test/resources/mm.mp4 differ
diff --git a/src/test/resources/test-config.sample.xml b/weixin-java-mp/src/test/resources/test-config.sample.xml
similarity index 100%
rename from src/test/resources/test-config.sample.xml
rename to weixin-java-mp/src/test/resources/test-config.sample.xml
diff --git a/weixin-java-mp/src/test/resources/testng.xml b/weixin-java-mp/src/test/resources/testng.xml
new file mode 100644
index 000000000..4318ffdb8
--- /dev/null
+++ b/weixin-java-mp/src/test/resources/testng.xml
@@ -0,0 +1,35 @@
+
+
+
+	
+		
+			
+			
+			
+			
+			
+			
+			
+			
+			
+			
+		
+	
+
+	
+		
+			
+			
+			
+			
+			
+			
+			
+			
+			
+			
+			
+            
+		
+	
+