From dfed700eb0b77f6cada26e041944c0a486c2d754 Mon Sep 17 00:00:00 2001 From: DDLeEHi <569198459@qq.com> Date: Wed, 3 May 2017 17:05:15 +0800 Subject: [PATCH 01/12] =?UTF-8?q?=E7=AD=BE=E5=90=8D=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20(#205)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github/binarywang/wxpay/service/impl/WxPayServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java index 1a949bc0..ed6e2c82 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java @@ -203,7 +203,7 @@ public class WxPayServiceImpl implements WxPayService { configMap.put("noncestr", String.valueOf(System.currentTimeMillis())); configMap.put("appid", appId); // 此map用于客户端与微信服务器交互 - payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey())); + payInfo.put("sign", SignUtils.createSign(configMap, this.getConfig().getMchKey())); payInfo.put("prepayId", prepayId); payInfo.put("partnerId", partnerid); payInfo.put("appId", appId); From 01b5d914164aca5c76950e1d1f9f8e76b6ad5959 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Wed, 3 May 2017 16:55:21 +0800 Subject: [PATCH 02/12] =?UTF-8?q?=E5=8D=87=E7=BA=A7qrcode-utils=E7=89=88?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E5=8F=96=E6=B6=88=E5=AF=B9logback=E7=9A=84?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=20#204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- weixin-java-pay/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index 82040214..284332ef 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -22,7 +22,7 @@ com.github.binarywang qrcode-utils - 1.0 + 1.1 From 49dc14af7319732a30e15f9c9996b31504bdab76 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Wed, 3 May 2017 17:05:59 +0800 Subject: [PATCH 03/12] =?UTF-8?q?=E5=8F=91=E5=B8=83=E4=B8=B4=E6=97=B6?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=89=88=E6=9C=AC2.6.1.BETA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- weixin-java-common/pom.xml | 2 +- weixin-java-cp/pom.xml | 2 +- weixin-java-mp/pom.xml | 2 +- weixin-java-pay/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5e219eb6..9a2fa9bd 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.github.binarywang weixin-java-parent - 2.7.0-SNAPSHOT + 2.6.1.BETA pom WeiXin Java Tools - Parent 微信公众号、企业号上级POM diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index e666caab..49815e50 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.7.0-SNAPSHOT + 2.6.1.BETA weixin-java-common diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index ab165436..e2afcb41 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.7.0-SNAPSHOT + 2.6.1.BETA weixin-java-cp diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 77d2488a..e736d4c2 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.7.0-SNAPSHOT + 2.6.1.BETA weixin-java-mp WeiXin Java Tools - MP diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index 284332ef..989fd9fa 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -5,7 +5,7 @@ weixin-java-parent com.github.binarywang - 2.7.0-SNAPSHOT + 2.6.1.BETA 4.0.0 From 1eb2cc7edd50a9bc81cf82381fc47bd9724800b8 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Wed, 3 May 2017 17:44:23 +0800 Subject: [PATCH 04/12] fix javadoc --- .../main/java/me/chanjar/weixin/mp/api/WxMpCardService.java | 2 -- .../me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java | 4 ---- 2 files changed, 6 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java index 35a23f70..fbfeb6fc 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java @@ -12,7 +12,6 @@ public interface WxMpCardService { /** * 得到WxMpService - * @return */ WxMpService getWxMpService(); @@ -20,7 +19,6 @@ public interface WxMpCardService { * 获得卡券api_ticket,不强制刷新卡券api_ticket * * @return 卡券api_ticket - * @throws WxErrorException * @see #getCardApiTicket(boolean) */ String getCardApiTicket() throws WxErrorException; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java index 9e64d043..4db5e0e7 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java @@ -36,10 +36,6 @@ public class WxMpCardServiceImpl implements WxMpCardService Date: Wed, 3 May 2017 17:45:37 +0800 Subject: [PATCH 05/12] =?UTF-8?q?=E7=AE=80=E5=8C=96=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github/binarywang/wxpay/config/WxPayConfig.java | 2 +- .../wxpay/service/impl/WxPayServiceImpl.java | 2 +- .../binarywang/wxpay/testbase/XmlWxPayConfig.java | 11 ++--------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java index c03b2482..895a827a 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java @@ -129,7 +129,7 @@ public class WxPayConfig { * 微信支付是否使用仿真测试环境 * 默认不使用 */ - public boolean useSandboxForWxPay() { + public boolean useSandbox() { return false; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java index ed6e2c82..c15a3ab6 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java @@ -45,7 +45,7 @@ public class WxPayServiceImpl implements WxPayService { } private String getPayBaseUrl() { - if (this.getConfig().useSandboxForWxPay()) { + if (this.getConfig().useSandbox()) { return PAY_BASE_URL + "/sandboxnew"; } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java index a10f36f3..d029590a 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java @@ -2,13 +2,6 @@ package com.github.binarywang.wxpay.testbase; import com.github.binarywang.wxpay.config.WxPayConfig; import com.thoughtworks.xstream.annotations.XStreamAlias; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.http.ssl.SSLContexts; - -import javax.net.ssl.SSLContext; -import java.io.File; -import java.io.FileInputStream; -import java.security.KeyStore; @XStreamAlias("xml") public class XmlWxPayConfig extends WxPayConfig { @@ -23,9 +16,9 @@ public class XmlWxPayConfig extends WxPayConfig { } @Override - public boolean useSandboxForWxPay() { + public boolean useSandbox() { //沙箱环境不成熟,有问题无法使用,暂时屏蔽掉 - // return true; +// return true; return false; } } From fbd02a85c9731634abdb906bcc68066b4efc334c Mon Sep 17 00:00:00 2001 From: crskyp Date: Thu, 4 May 2017 11:13:02 +0800 Subject: [PATCH 06/12] =?UTF-8?q?okhttp=E7=94=A8=E6=B3=95=E6=9C=89?= =?UTF-8?q?=E9=94=99=E8=AF=AF=EF=BC=9B=E6=B7=BB=E5=8A=A0=E4=BA=86=E6=9E=9A?= =?UTF-8?q?=E4=B8=BEHttpType=20(#207)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/http/AbstractRequestExecutor.java | 43 +- .../weixin/common/util/http/HttpType.java | 8 + .../http/MediaDownloadRequestExecutor.java | 2 +- .../util/http/MediaUploadRequestExecutor.java | 2 +- .../weixin/common/util/http/RequestHttp.java | 6 + .../util/http/SimpleGetRequestExecutor.java | 2 +- .../util/http/SimplePostRequestExecutor.java | 2 +- .../cp/api/impl/AbstractWxCpService.java | 632 ++++++++++++++++++ .../cp/api/impl/apache/WxCpServiceImpl.java | 10 +- .../cp/api/impl/jodd/WxCpServiceImpl.java | 6 + .../cp/api/impl/okhttp/WxCpServiceImpl.java | 13 +- .../mp/api/impl/AbstractWxMpService.java | 445 ++++++++++++ .../mp/api/impl/apache/WxMpServiceImpl.java | 22 +- .../mp/api/impl/jodd/WxMpServiceImpl.java | 6 + .../mp/api/impl/okhttp/WxMpServiceImpl.java | 8 +- .../http/MaterialDeleteRequestExecutor.java | 2 +- .../http/MaterialNewsInfoRequestExecutor.java | 2 +- .../http/MaterialUploadRequestExecutor.java | 2 +- .../MaterialVideoInfoRequestExecutor.java | 2 +- .../http/MediaImgUploadRequestExecutor.java | 2 +- .../mp/util/http/QrCodeRequestExecutor.java | 2 +- 21 files changed, 1182 insertions(+), 37 deletions(-) create mode 100644 weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java create mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/AbstractWxCpService.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/AbstractWxMpService.java diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/AbstractRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/AbstractRequestExecutor.java index 23881e66..731bec77 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/AbstractRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/AbstractRequestExecutor.java @@ -17,26 +17,29 @@ import okhttp3.ConnectionPool; public abstract class AbstractRequestExecutor implements RequestExecutor { @Override - public T execute(RequestHttp requestHttp, String uri, E data) throws WxErrorException, IOException{ - if (requestHttp.getRequestHttpClient() instanceof CloseableHttpClient) { - //apache-http请求 - CloseableHttpClient httpClient = (CloseableHttpClient) requestHttp.getRequestHttpClient(); - HttpHost httpProxy = (HttpHost) requestHttp.getRequestHttpProxy(); - return executeApache(httpClient, httpProxy, uri, data); - } - if (requestHttp.getRequestHttpClient() instanceof HttpConnectionProvider) { - //jodd-http请求 - HttpConnectionProvider provider = (HttpConnectionProvider) requestHttp.getRequestHttpClient(); - ProxyInfo proxyInfo = (ProxyInfo) requestHttp.getRequestHttpProxy(); - return executeJodd(provider, proxyInfo, uri, data); - } else if (requestHttp.getRequestHttpClient() instanceof ConnectionPool) { - //okhttp请求 - ConnectionPool pool = (ConnectionPool) requestHttp.getRequestHttpClient(); - OkhttpProxyInfo proxyInfo = (OkhttpProxyInfo) requestHttp.getRequestHttpProxy(); - return executeOkhttp(pool, proxyInfo, uri, data); - } else { - //TODO 这里需要抛出异常,需要优化 - return null; + public T execute(RequestHttp requestHttp, String uri, E data) throws WxErrorException, IOException { + switch (requestHttp.getRequestType()) { + case apacheHttp: { + //apache-http请求 + CloseableHttpClient httpClient = (CloseableHttpClient) requestHttp.getRequestHttpClient(); + HttpHost httpProxy = (HttpHost) requestHttp.getRequestHttpProxy(); + return executeApache(httpClient, httpProxy, uri, data); + } + case joddHttp: { + //jodd-http请求 + HttpConnectionProvider provider = (HttpConnectionProvider) requestHttp.getRequestHttpClient(); + ProxyInfo proxyInfo = (ProxyInfo) requestHttp.getRequestHttpProxy(); + return executeJodd(provider, proxyInfo, uri, data); + } + case okHttp: { + //okhttp请求 + ConnectionPool pool = (ConnectionPool) requestHttp.getRequestHttpClient(); + OkhttpProxyInfo proxyInfo = (OkhttpProxyInfo) requestHttp.getRequestHttpProxy(); + return executeOkhttp(pool, proxyInfo, uri, data); + } + default: + //TODO 这里需要抛出异常,需要优化 + return null; } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java new file mode 100644 index 00000000..5e78a6f1 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpType.java @@ -0,0 +1,8 @@ +package me.chanjar.weixin.common.util.http; + +/** + * Created by ecoolper on 2017/4/28. + */ +public enum HttpType { + joddHttp, apacheHttp, okHttp; +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java index e95f02de..6da7c65c 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaDownloadRequestExecutor.java @@ -213,7 +213,7 @@ public class MediaDownloadRequestExecutor extends AbstractRequestExecutor { */ P getRequestHttpProxy(); + /** + * + * @return + */ + HttpType getRequestType(); + } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java index 5edb70f3..f29739d6 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java @@ -141,7 +141,7 @@ public class SimpleGetRequestExecutor extends AbstractRequestExecutor implements WxCpService, RequestHttp { + + protected final Logger log = LoggerFactory.getLogger(AbstractWxCpService.class); + + /** + * 全局的是否正在刷新access token的锁 + */ + protected final Object globalAccessTokenRefreshLock = new Object(); + + /** + * 全局的是否正在刷新jsapi_ticket的锁 + */ + protected final Object globalJsapiTicketRefreshLock = new Object(); + + protected WxCpConfigStorage configStorage; + + + protected WxSessionManager sessionManager = new StandardSessionManager(); + /** + * 临时文件目录 + */ + protected File tmpDirFile; + private int retrySleepMillis = 1000; + private int maxRetryTimes = 5; + + @Override + public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { + try { + return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) + .equals(msgSignature); + } catch (Exception e) { + return false; + } + } + + @Override + public void userAuthenticated(String userId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId; + get(url, null); + } + + @Override + public String getAccessToken() throws WxErrorException { + return getAccessToken(false); + } + + + @Override + public String getJsapiTicket() throws WxErrorException { + return getJsapiTicket(false); + } + + @Override + public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { + if (forceRefresh) { + this.configStorage.expireJsapiTicket(); + } + if (this.configStorage.isJsapiTicketExpired()) { + synchronized (this.globalJsapiTicketRefreshLock) { + if (this.configStorage.isJsapiTicketExpired()) { + String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; + String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); + int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); + this.configStorage.updateJsapiTicket(jsapiTicket, + expiresInSeconds); + } + } + } + return this.configStorage.getJsapiTicket(); + } + + @Override + public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { + long timestamp = System.currentTimeMillis() / 1000; + String noncestr = RandomUtils.getRandomStr(); + String jsapiTicket = getJsapiTicket(false); + String signature = SHA1.genWithAmple( + "jsapi_ticket=" + jsapiTicket, + "noncestr=" + noncestr, + "timestamp=" + timestamp, + "url=" + url + ); + WxJsapiSignature jsapiSignature = new WxJsapiSignature(); + jsapiSignature.setTimestamp(timestamp); + jsapiSignature.setNonceStr(noncestr); + jsapiSignature.setUrl(url); + jsapiSignature.setSignature(signature); + + // Fixed bug + jsapiSignature.setAppId(this.configStorage.getCorpId()); + + return jsapiSignature; + } + + @Override + public void messageSend(WxCpMessage message) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; + post(url, message.toJson()); + } + + @Override + public void menuCreate(WxMenu menu) throws WxErrorException { + menuCreate(this.configStorage.getAgentId(), menu); + } + + @Override + public void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" + + this.configStorage.getAgentId(); + post(url, menu.toJson()); + } + + @Override + public void menuDelete() throws WxErrorException { + menuDelete(this.configStorage.getAgentId()); + } + + @Override + public void menuDelete(Integer agentId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId; + get(url, null); + } + + @Override + public WxMenu menuGet() throws WxErrorException { + return menuGet(this.configStorage.getAgentId()); + } + + @Override + public WxMenu menuGet(Integer agentId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId; + try { + String resultContent = get(url, null); + return WxMenu.fromJson(resultContent); + } catch (WxErrorException e) { + // 46003 不存在的菜单数据 + if (e.getError().getErrorCode() == 46003) { + return null; + } + throw e; + } + } + + @Override + public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) + throws WxErrorException, IOException { + return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); + } + + @Override + public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; + return execute(new MediaUploadRequestExecutor(), url, file); + } + + @Override + public File mediaDownload(String mediaId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; + return execute( + new MediaDownloadRequestExecutor( + this.configStorage.getTmpDirFile()), + url, "media_id=" + mediaId); + } + + + @Override + public Integer departCreate(WxCpDepart depart) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; + String responseContent = execute( + new SimplePostRequestExecutor(), + url, + depart.toJson()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); + } + + @Override + public void departUpdate(WxCpDepart group) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update"; + post(url, group.toJson()); + } + + @Override + public void departDelete(Integer departId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId; + get(url, null); + } + + @Override + public List departGet() throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"; + String responseContent = get(url, null); + /* + * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } + * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } + */ + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("department"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public void userCreate(WxCpUser user) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create"; + post(url, user.toJson()); + } + + @Override + public void userUpdate(WxCpUser user) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update"; + post(url, user.toJson()); + } + + @Override + public void userDelete(String userid) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userid; + get(url, null); + } + + @Override + public void userDelete(String[] userids) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; + JsonObject jsonObject = new JsonObject(); + JsonArray jsonArray = new JsonArray(); + for (String userid : userids) { + jsonArray.add(new JsonPrimitive(userid)); + } + jsonObject.add("useridlist", jsonArray); + post(url, jsonObject.toString()); + } + + @Override + public WxCpUser userGet(String userid) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid; + String responseContent = get(url, null); + return WxCpUser.fromJson(responseContent); + } + + @Override + public List userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId; + String params = ""; + if (fetchChild != null) { + params += "&fetch_child=" + (fetchChild ? "1" : "0"); + } + if (status != null) { + params += "&status=" + status; + } else { + params += "&status=0"; + } + + String responseContent = get(url, params); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("userlist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public List departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId; + String params = ""; + if (fetchChild != null) { + params += "&fetch_child=" + (fetchChild ? "1" : "0"); + } + if (status != null) { + params += "&status=" + status; + } else { + params += "&status=0"; + } + + String responseContent = get(url, params); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("userlist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public String tagCreate(String tagName) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create"; + JsonObject o = new JsonObject(); + o.addProperty("tagname", tagName); + String responseContent = post(url, o.toString()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return tmpJsonElement.getAsJsonObject().get("tagid").getAsString(); + } + + @Override + public void tagUpdate(String tagId, String tagName) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/update"; + JsonObject o = new JsonObject(); + o.addProperty("tagid", tagId); + o.addProperty("tagname", tagName); + post(url, o.toString()); + } + + @Override + public void tagDelete(String tagId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId; + get(url, null); + } + + @Override + public List tagGet() throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list"; + String responseContent = get(url, null); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("taglist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public List tagGetUsers(String tagId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId; + String responseContent = get(url, null); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return WxCpGsonBuilder.INSTANCE.create() + .fromJson( + tmpJsonElement.getAsJsonObject().get("userlist"), + new TypeToken>() { + }.getType() + ); + } + + @Override + public void tagAddUsers(String tagId, List userIds, List partyIds) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers"; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("tagid", tagId); + if (userIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String userId : userIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("userlist", jsonArray); + } + if (partyIds != null) { + JsonArray jsonArray = new JsonArray(); + for (String userId : partyIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("partylist", jsonArray); + } + post(url, jsonObject.toString()); + } + + @Override + public void tagRemoveUsers(String tagId, List userIds) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers"; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("tagid", tagId); + JsonArray jsonArray = new JsonArray(); + for (String userId : userIds) { + jsonArray.add(new JsonPrimitive(userId)); + } + jsonObject.add("userlist", jsonArray); + post(url, jsonObject.toString()); + } + + @Override + public String oauth2buildAuthorizationUrl(String state) { + return this.oauth2buildAuthorizationUrl( + this.configStorage.getOauth2redirectUri(), + state + ); + } + + @Override + public String oauth2buildAuthorizationUrl(String redirectUri, String state) { + String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; + url += "appid=" + this.configStorage.getCorpId(); + url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); + url += "&response_type=code"; + url += "&scope=snsapi_base"; + if (state != null) { + url += "&state=" + state; + } + url += "#wechat_redirect"; + return url; + } + + @Override + public String[] oauth2getUserInfo(String code) throws WxErrorException { + return oauth2getUserInfo(this.configStorage.getAgentId(), code); + } + + @Override + public String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" + + "code=" + code + + "&agentid=" + agentId; + String responseText = get(url, null); + JsonElement je = new JsonParser().parse(responseText); + JsonObject jo = je.getAsJsonObject(); + return new String[]{GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId"), GsonHelper.getString(jo, "OpenId")}; + } + + @Override + public int invite(String userId, String inviteTips) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/invite/send"; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("userid", userId); + if (StringUtils.isNotEmpty(inviteTips)) { + jsonObject.addProperty("invite_tips", inviteTips); + } + String responseContent = post(url, jsonObject.toString()); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + return tmpJsonElement.getAsJsonObject().get("type").getAsInt(); + } + + @Override + public String[] getCallbackIp() throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; + String responseContent = get(url, null); + JsonElement tmpJsonElement = new JsonParser().parse(responseContent); + JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); + String[] ips = new String[jsonArray.size()]; + for (int i = 0; i < jsonArray.size(); i++) { + ips[i] = jsonArray.get(i).getAsString(); + } + return ips; + } + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(new SimpleGetRequestExecutor(), url, queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(new SimplePostRequestExecutor(), url, postData); + } + + /** + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 + */ + @Override + public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + int retryTimes = 0; + do { + try { + T result = this.executeInternal(executor, uri, data); + this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",uri, data, result); + return result; + } catch (WxErrorException e) { + if (retryTimes + 1 > this.maxRetryTimes) { + this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + //最后一次重试失败后,直接抛出异常,不再等待 + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + WxError error = e.getError(); + /* + * -1 系统繁忙, 1000ms后重试 + */ + if (error.getErrorCode() == -1) { + int sleepMillis = this.retrySleepMillis * (1 << retryTimes); + try { + this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); + Thread.sleep(sleepMillis); + } catch (InterruptedException e1) { + throw new RuntimeException(e1); + } + } else { + throw e; + } + } + } while (retryTimes++ < this.maxRetryTimes); + + this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + public synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + if (uri.contains("access_token=")) { + throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); + } + String accessToken = getAccessToken(false); + + String uriWithAccessToken = uri; + uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; + + try { + return executor.execute(this, 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) { + // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token + this.configStorage.expireAccessToken(); + return execute(executor, uri, data); + } + + if (error.getErrorCode() != 0) { + this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, error); + throw new WxErrorException(error); + } + return null; + } catch (IOException e) { + this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uri, data, e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) { + this.configStorage = wxConfigProvider; + this.initHttp(); + } + + @Override + public void setRetrySleepMillis(int retrySleepMillis) { + this.retrySleepMillis = retrySleepMillis; + } + + + @Override + public void setMaxRetryTimes(int maxRetryTimes) { + this.maxRetryTimes = maxRetryTimes; + } + + @Override + public WxSession getSession(String id) { + if (this.sessionManager == null) { + return null; + } + return this.sessionManager.getSession(id); + } + + @Override + public WxSession getSession(String id, boolean create) { + if (this.sessionManager == null) { + return null; + } + return this.sessionManager.getSession(id, create); + } + + + @Override + public void setSessionManager(WxSessionManager sessionManager) { + this.sessionManager = sessionManager; + } + + @Override + public String replaceParty(String mediaId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("media_id", mediaId); + return post(url, jsonObject.toString()); + } + + @Override + public String replaceUser(String mediaId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("media_id", mediaId); + return post(url, jsonObject.toString()); + } + + @Override + public String getTaskResult(String joinId) throws WxErrorException { + String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=" + joinId; + return get(url, null); + } + + public File getTmpDirFile() { + return this.tmpDirFile; + } + + public void setTmpDirFile(File tmpDirFile) { + this.tmpDirFile = tmpDirFile; + } + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java index 92579d4f..7c1334b4 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java @@ -4,10 +4,11 @@ package me.chanjar.weixin.cp.api.impl.apache; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpType; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.impl.AbstractWxCpServiceImpl; +import me.chanjar.weixin.cp.api.impl.AbstractWxCpService; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; @@ -18,7 +19,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import java.io.IOException; -public class WxCpServiceImpl extends AbstractWxCpServiceImpl { +public class WxCpServiceImpl extends AbstractWxCpService { protected CloseableHttpClient httpClient; protected HttpHost httpProxy; @@ -32,6 +33,11 @@ public class WxCpServiceImpl extends AbstractWxCpServiceImpl implements WxMpService,RequestHttp { + + private static final JsonParser JSON_PARSER = new JsonParser(); + + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + protected WxSessionManager sessionManager = new StandardSessionManager(); + private WxMpConfigStorage wxMpConfigStorage; + private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); + private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); + private WxMpMenuService menuService = new WxMpMenuServiceImpl(this); + private WxMpUserService userService = new WxMpUserServiceImpl(this); + private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this); + private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); + private WxMpCardService cardService = new WxMpCardServiceImpl(this); + private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); + private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); + private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); + private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this); + private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this); + + private int retrySleepMillis = 1000; + private int maxRetryTimes = 5; + + + @Override + public boolean checkSignature(String timestamp, String nonce, String signature) { + try { + return SHA1.gen(this.getWxMpConfigStorage().getToken(), timestamp, nonce) + .equals(signature); + } catch (Exception e) { + return false; + } + } + + @Override + public String getJsapiTicket() throws WxErrorException { + return getJsapiTicket(false); + } + + @Override + public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { + Lock lock = this.getWxMpConfigStorage().getJsapiTicketLock(); + try { + lock.lock(); + + if (forceRefresh) { + this.getWxMpConfigStorage().expireJsapiTicket(); + } + + if (this.getWxMpConfigStorage().isJsapiTicketExpired()) { + String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; + String responseContent = execute(new SimpleGetRequestExecutor(), url, null); + JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); + JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); + int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); + this.getWxMpConfigStorage().updateJsapiTicket(jsapiTicket, expiresInSeconds); + } + } finally { + lock.unlock(); + } + return this.getWxMpConfigStorage().getJsapiTicket(); + } + + @Override + public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { + long timestamp = System.currentTimeMillis() / 1000; + String noncestr = RandomUtils.getRandomStr(); + String jsapiTicket = getJsapiTicket(false); + String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, + "noncestr=" + noncestr, "timestamp=" + timestamp, "url=" + url); + WxJsapiSignature jsapiSignature = new WxJsapiSignature(); + jsapiSignature.setAppId(this.getWxMpConfigStorage().getAppId()); + jsapiSignature.setTimestamp(timestamp); + jsapiSignature.setNonceStr(noncestr); + jsapiSignature.setUrl(url); + jsapiSignature.setSignature(signature); + return jsapiSignature; + } + + @Override + public String getAccessToken() throws WxErrorException { + return getAccessToken(false); + } + + @Override + public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; + String responseContent = this.post(url, news.toJson()); + return WxMpMassUploadResult.fromJson(responseContent); + } + + @Override + public WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo"; + String responseContent = this.post(url, video.toJson()); + return WxMpMassUploadResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"; + String responseContent = this.post(url, message.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/message/mass/send"; + String responseContent = this.post(url, message.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + + @Override + public WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception { + String url = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"; + String responseContent = this.post(url, wxMpMassPreviewMessage.toJson()); + return WxMpMassSendResult.fromJson(responseContent); + } + + @Override + public String shortUrl(String long_url) throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/shorturl"; + JsonObject o = new JsonObject(); + o.addProperty("action", "long2short"); + o.addProperty("long_url", long_url); + String responseContent = this.post(url, o.toString()); + JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); + return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); + } + + @Override + public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { + String url = "https://api.weixin.qq.com/semantic/semproxy/search"; + String responseContent = this.post(url, semanticQuery.toJson()); + return WxMpSemanticQueryResult.fromJson(responseContent); + } + + @Override + public String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state) { + StringBuilder url = new StringBuilder(); + url.append("https://open.weixin.qq.com/connect/oauth2/authorize?"); + url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); + url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); + url.append("&response_type=code"); + url.append("&scope=").append(scope); + if (state != null) { + url.append("&state=").append(state); + } + url.append("#wechat_redirect"); + return url.toString(); + } + + @Override + public String buildQrConnectUrl(String redirectURI, String scope, + String state) { + StringBuilder url = new StringBuilder(); + url.append("https://open.weixin.qq.com/connect/qrconnect?"); + url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); + url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); + url.append("&response_type=code"); + url.append("&scope=").append(scope); + if (state != null) { + url.append("&state=").append(state); + } + + url.append("#wechat_redirect"); + return url.toString(); + } + + private WxMpOAuth2AccessToken getOAuth2AccessToken(StringBuilder url) throws WxErrorException { + try { + RequestExecutor executor = new SimpleGetRequestExecutor(); + String responseText = executor.execute(this, url.toString(), null); + return WxMpOAuth2AccessToken.fromJson(responseText); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { + StringBuilder url = new StringBuilder(); + url.append("https://api.weixin.qq.com/sns/oauth2/access_token?"); + url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); + url.append("&secret=").append(this.getWxMpConfigStorage().getSecret()); + url.append("&code=").append(code); + url.append("&grant_type=authorization_code"); + + return this.getOAuth2AccessToken(url); + } + + @Override + public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { + StringBuilder url = new StringBuilder(); + url.append("https://api.weixin.qq.com/sns/oauth2/refresh_token?"); + url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); + url.append("&grant_type=refresh_token"); + url.append("&refresh_token=").append(refreshToken); + + return this.getOAuth2AccessToken(url); + } + + @Override + public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException { + StringBuilder url = new StringBuilder(); + url.append("https://api.weixin.qq.com/sns/userinfo?"); + url.append("access_token=").append(oAuth2AccessToken.getAccessToken()); + url.append("&openid=").append(oAuth2AccessToken.getOpenId()); + if (lang == null) { + url.append("&lang=zh_CN"); + } else { + url.append("&lang=").append(lang); + } + + try { + RequestExecutor executor = new SimpleGetRequestExecutor(); + String responseText = executor.execute(this, url.toString(), null); + return WxMpUser.fromJson(responseText); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken) { + StringBuilder url = new StringBuilder(); + url.append("https://api.weixin.qq.com/sns/auth?"); + url.append("access_token=").append(oAuth2AccessToken.getAccessToken()); + url.append("&openid=").append(oAuth2AccessToken.getOpenId()); + + try { + RequestExecutor executor = new SimpleGetRequestExecutor(); + executor.execute(this, url.toString(), null); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (WxErrorException e) { + return false; + } + return true; + } + + @Override + public String[] getCallbackIP() throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/getcallbackip"; + String responseContent = get(url, null); + JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); + JsonArray ipList = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); + String[] ipArray = new String[ipList.size()]; + for (int i = 0; i < ipList.size(); i++) { + ipArray[i] = ipList.get(i).getAsString(); + } + return ipArray; + } + + @Override + public String get(String url, String queryParam) throws WxErrorException { + return execute(new SimpleGetRequestExecutor(), url, queryParam); + } + + @Override + public String post(String url, String postData) throws WxErrorException { + return execute(new SimplePostRequestExecutor(), url, postData); + } + + /** + * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 + */ + public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { + int retryTimes = 0; + do { + try { + T result = executeInternal(executor, uri, data); + this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, result); + return result; + } catch (WxErrorException e) { + if (retryTimes + 1 > this.maxRetryTimes) { + this.log.warn("重试达到最大次数【{}】", maxRetryTimes); + //最后一次重试失败后,直接抛出异常,不再等待 + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + WxError error = e.getError(); + // -1 系统繁忙, 1000ms后重试 + if (error.getErrorCode() == -1) { + int sleepMillis = this.retrySleepMillis * (1 << retryTimes); + try { + this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); + Thread.sleep(sleepMillis); + } catch (InterruptedException e1) { + throw new RuntimeException(e1); + } + } else { + throw e; + } + } + } while (retryTimes++ < this.maxRetryTimes); + + this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + throw new RuntimeException("微信服务端异常,超出重试次数"); + } + + public synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { + if (uri.indexOf("access_token=") != -1) { + throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); + } + String accessToken = getAccessToken(false); + + String uriWithAccessToken = uri; + uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; + + try { + return executor.execute(this, 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) { + // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token + this.getWxMpConfigStorage().expireAccessToken(); + if (this.getWxMpConfigStorage().autoRefreshToken()) { + return this.execute(executor, uri, data); + } + } + + if (error.getErrorCode() != 0) { + this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, error); + throw new WxErrorException(error); + } + return null; + } catch (IOException e) { + this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uri, data, e.getMessage()); + throw new RuntimeException(e); + } + } + + @Override + public WxMpConfigStorage getWxMpConfigStorage() { + return this.wxMpConfigStorage; + } + + @Override + public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { + this.wxMpConfigStorage = wxConfigProvider; + this.initHttp(); + } + + @Override + public void setRetrySleepMillis(int retrySleepMillis) { + this.retrySleepMillis = retrySleepMillis; + } + + @Override + public void setMaxRetryTimes(int maxRetryTimes) { + this.maxRetryTimes = maxRetryTimes; + } + + @Override + public WxMpKefuService getKefuService() { + return this.kefuService; + } + + @Override + public WxMpMaterialService getMaterialService() { + return this.materialService; + } + + @Override + public WxMpMenuService getMenuService() { + return this.menuService; + } + + @Override + public WxMpUserService getUserService() { + return this.userService; + } + + @Override + public WxMpUserTagService getUserTagService() { + return this.tagService; + } + + @Override + public WxMpQrcodeService getQrcodeService() { + return this.qrCodeService; + } + + @Override + public WxMpCardService getCardService() { + return this.cardService; + } + + @Override + public WxMpDataCubeService getDataCubeService() { + return this.dataCubeService; + } + + @Override + public WxMpUserBlacklistService getBlackListService() { + return this.blackListService; + } + + @Override + public WxMpStoreService getStoreService() { + return this.storeService; + } + + @Override + public WxMpTemplateMsgService getTemplateMsgService() { + return this.templateMsgService; + } + + @Override + public WxMpDeviceService getDeviceService() { + return this.deviceService; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/apache/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/apache/WxMpServiceImpl.java index 3032f59d..835fbe18 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/apache/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/apache/WxMpServiceImpl.java @@ -1,13 +1,24 @@ package me.chanjar.weixin.mp.api.impl.apache; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.bean.WxJsapiSignature; import me.chanjar.weixin.common.bean.result.WxError; import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.common.session.StandardSessionManager; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.common.util.RandomUtils; +import me.chanjar.weixin.common.util.crypto.SHA1; +import me.chanjar.weixin.common.util.http.*; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; -import me.chanjar.weixin.mp.api.WxMpConfigStorage; -import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.AbstractWxMpServiceImpl; +import me.chanjar.weixin.mp.api.*; +import me.chanjar.weixin.mp.api.impl.*; +import me.chanjar.weixin.mp.bean.*; +import me.chanjar.weixin.mp.bean.result.*; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -35,6 +46,11 @@ public class WxMpServiceImpl extends AbstractWxMpServiceImpl Date: Thu, 4 May 2017 22:48:55 +0800 Subject: [PATCH 08/12] fix for previous commits --- .../cp/api/impl/AbstractWxCpService.java | 632 ------------------ .../cp/api/impl/apache/WxCpServiceImpl.java | 4 +- .../mp/api/impl/AbstractWxMpService.java | 445 ------------ 3 files changed, 2 insertions(+), 1079 deletions(-) delete mode 100644 weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/AbstractWxCpService.java delete mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/AbstractWxMpService.java diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/AbstractWxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/AbstractWxCpService.java deleted file mode 100644 index 82fcdef9..00000000 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/AbstractWxCpService.java +++ /dev/null @@ -1,632 +0,0 @@ -package me.chanjar.weixin.cp.api.impl; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.UUID; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.*; -import com.google.gson.reflect.TypeToken; - -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.bean.menu.WxMenu; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.session.StandardSessionManager; -import me.chanjar.weixin.common.session.WxSession; -import me.chanjar.weixin.common.session.WxSessionManager; -import me.chanjar.weixin.common.util.RandomUtils; -import me.chanjar.weixin.common.util.crypto.SHA1; -import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.*; -import me.chanjar.weixin.common.util.json.GsonHelper; -import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.WxCpService; -import me.chanjar.weixin.cp.bean.WxCpDepart; -import me.chanjar.weixin.cp.bean.WxCpMessage; -import me.chanjar.weixin.cp.bean.WxCpTag; -import me.chanjar.weixin.cp.bean.WxCpUser; -import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; - -public abstract class AbstractWxCpService implements WxCpService, RequestHttp { - - protected final Logger log = LoggerFactory.getLogger(AbstractWxCpService.class); - - /** - * 全局的是否正在刷新access token的锁 - */ - protected final Object globalAccessTokenRefreshLock = new Object(); - - /** - * 全局的是否正在刷新jsapi_ticket的锁 - */ - protected final Object globalJsapiTicketRefreshLock = new Object(); - - protected WxCpConfigStorage configStorage; - - - protected WxSessionManager sessionManager = new StandardSessionManager(); - /** - * 临时文件目录 - */ - protected File tmpDirFile; - private int retrySleepMillis = 1000; - private int maxRetryTimes = 5; - - @Override - public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) { - try { - return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data) - .equals(msgSignature); - } catch (Exception e) { - return false; - } - } - - @Override - public void userAuthenticated(String userId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId; - get(url, null); - } - - @Override - public String getAccessToken() throws WxErrorException { - return getAccessToken(false); - } - - - @Override - public String getJsapiTicket() throws WxErrorException { - return getJsapiTicket(false); - } - - @Override - public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { - if (forceRefresh) { - this.configStorage.expireJsapiTicket(); - } - if (this.configStorage.isJsapiTicketExpired()) { - synchronized (this.globalJsapiTicketRefreshLock) { - if (this.configStorage.isJsapiTicketExpired()) { - String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); - String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); - int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); - this.configStorage.updateJsapiTicket(jsapiTicket, - expiresInSeconds); - } - } - } - return this.configStorage.getJsapiTicket(); - } - - @Override - public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { - long timestamp = System.currentTimeMillis() / 1000; - String noncestr = RandomUtils.getRandomStr(); - String jsapiTicket = getJsapiTicket(false); - String signature = SHA1.genWithAmple( - "jsapi_ticket=" + jsapiTicket, - "noncestr=" + noncestr, - "timestamp=" + timestamp, - "url=" + url - ); - WxJsapiSignature jsapiSignature = new WxJsapiSignature(); - jsapiSignature.setTimestamp(timestamp); - jsapiSignature.setNonceStr(noncestr); - jsapiSignature.setUrl(url); - jsapiSignature.setSignature(signature); - - // Fixed bug - jsapiSignature.setAppId(this.configStorage.getCorpId()); - - return jsapiSignature; - } - - @Override - public void messageSend(WxCpMessage message) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; - post(url, message.toJson()); - } - - @Override - public void menuCreate(WxMenu menu) throws WxErrorException { - menuCreate(this.configStorage.getAgentId(), menu); - } - - @Override - public void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid=" - + this.configStorage.getAgentId(); - post(url, menu.toJson()); - } - - @Override - public void menuDelete() throws WxErrorException { - menuDelete(this.configStorage.getAgentId()); - } - - @Override - public void menuDelete(Integer agentId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId; - get(url, null); - } - - @Override - public WxMenu menuGet() throws WxErrorException { - return menuGet(this.configStorage.getAgentId()); - } - - @Override - public WxMenu menuGet(Integer agentId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId; - try { - String resultContent = get(url, null); - return WxMenu.fromJson(resultContent); - } catch (WxErrorException e) { - // 46003 不存在的菜单数据 - if (e.getError().getErrorCode() == 46003) { - return null; - } - throw e; - } - } - - @Override - public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) - throws WxErrorException, IOException { - return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); - } - - @Override - public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; - return execute(new MediaUploadRequestExecutor(), url, file); - } - - @Override - public File mediaDownload(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; - return execute( - new MediaDownloadRequestExecutor( - this.configStorage.getTmpDirFile()), - url, "media_id=" + mediaId); - } - - - @Override - public Integer departCreate(WxCpDepart depart) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create"; - String responseContent = execute( - new SimplePostRequestExecutor(), - url, - depart.toJson()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id")); - } - - @Override - public void departUpdate(WxCpDepart group) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/update"; - post(url, group.toJson()); - } - - @Override - public void departDelete(Integer departId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/delete?id=" + departId; - get(url, null); - } - - @Override - public List departGet() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/department/list"; - String responseContent = get(url, null); - /* - * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } - * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } - */ - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("department"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public void userCreate(WxCpUser user) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create"; - post(url, user.toJson()); - } - - @Override - public void userUpdate(WxCpUser user) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update"; - post(url, user.toJson()); - } - - @Override - public void userDelete(String userid) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userid; - get(url, null); - } - - @Override - public void userDelete(String[] userids) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete"; - JsonObject jsonObject = new JsonObject(); - JsonArray jsonArray = new JsonArray(); - for (String userid : userids) { - jsonArray.add(new JsonPrimitive(userid)); - } - jsonObject.add("useridlist", jsonArray); - post(url, jsonObject.toString()); - } - - @Override - public WxCpUser userGet(String userid) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid; - String responseContent = get(url, null); - return WxCpUser.fromJson(responseContent); - } - - @Override - public List userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId; - String params = ""; - if (fetchChild != null) { - params += "&fetch_child=" + (fetchChild ? "1" : "0"); - } - if (status != null) { - params += "&status=" + status; - } else { - params += "&status=0"; - } - - String responseContent = get(url, params); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public List departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId; - String params = ""; - if (fetchChild != null) { - params += "&fetch_child=" + (fetchChild ? "1" : "0"); - } - if (status != null) { - params += "&status=" + status; - } else { - params += "&status=0"; - } - - String responseContent = get(url, params); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public String tagCreate(String tagName) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/create"; - JsonObject o = new JsonObject(); - o.addProperty("tagname", tagName); - String responseContent = post(url, o.toString()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return tmpJsonElement.getAsJsonObject().get("tagid").getAsString(); - } - - @Override - public void tagUpdate(String tagId, String tagName) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/update"; - JsonObject o = new JsonObject(); - o.addProperty("tagid", tagId); - o.addProperty("tagname", tagName); - post(url, o.toString()); - } - - @Override - public void tagDelete(String tagId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?tagid=" + tagId; - get(url, null); - } - - @Override - public List tagGet() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/list"; - String responseContent = get(url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("taglist"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public List tagGetUsers(String tagId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/get?tagid=" + tagId; - String responseContent = get(url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return WxCpGsonBuilder.INSTANCE.create() - .fromJson( - tmpJsonElement.getAsJsonObject().get("userlist"), - new TypeToken>() { - }.getType() - ); - } - - @Override - public void tagAddUsers(String tagId, List userIds, List partyIds) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("tagid", tagId); - if (userIds != null) { - JsonArray jsonArray = new JsonArray(); - for (String userId : userIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("userlist", jsonArray); - } - if (partyIds != null) { - JsonArray jsonArray = new JsonArray(); - for (String userId : partyIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("partylist", jsonArray); - } - post(url, jsonObject.toString()); - } - - @Override - public void tagRemoveUsers(String tagId, List userIds) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/deltagusers"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("tagid", tagId); - JsonArray jsonArray = new JsonArray(); - for (String userId : userIds) { - jsonArray.add(new JsonPrimitive(userId)); - } - jsonObject.add("userlist", jsonArray); - post(url, jsonObject.toString()); - } - - @Override - public String oauth2buildAuthorizationUrl(String state) { - return this.oauth2buildAuthorizationUrl( - this.configStorage.getOauth2redirectUri(), - state - ); - } - - @Override - public String oauth2buildAuthorizationUrl(String redirectUri, String state) { - String url = "https://open.weixin.qq.com/connect/oauth2/authorize?"; - url += "appid=" + this.configStorage.getCorpId(); - url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); - url += "&response_type=code"; - url += "&scope=snsapi_base"; - if (state != null) { - url += "&state=" + state; - } - url += "#wechat_redirect"; - return url; - } - - @Override - public String[] oauth2getUserInfo(String code) throws WxErrorException { - return oauth2getUserInfo(this.configStorage.getAgentId(), code); - } - - @Override - public String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" - + "code=" + code - + "&agentid=" + agentId; - String responseText = get(url, null); - JsonElement je = new JsonParser().parse(responseText); - JsonObject jo = je.getAsJsonObject(); - return new String[]{GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId"), GsonHelper.getString(jo, "OpenId")}; - } - - @Override - public int invite(String userId, String inviteTips) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/invite/send"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("userid", userId); - if (StringUtils.isNotEmpty(inviteTips)) { - jsonObject.addProperty("invite_tips", inviteTips); - } - String responseContent = post(url, jsonObject.toString()); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - return tmpJsonElement.getAsJsonObject().get("type").getAsInt(); - } - - @Override - public String[] getCallbackIp() throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip"; - String responseContent = get(url, null); - JsonElement tmpJsonElement = new JsonParser().parse(responseContent); - JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); - String[] ips = new String[jsonArray.size()]; - for (int i = 0; i < jsonArray.size(); i++) { - ips[i] = jsonArray.get(i).getAsString(); - } - return ips; - } - - @Override - public String get(String url, String queryParam) throws WxErrorException { - return execute(new SimpleGetRequestExecutor(), url, queryParam); - } - - @Override - public String post(String url, String postData) throws WxErrorException { - return execute(new SimplePostRequestExecutor(), url, postData); - } - - /** - * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 - */ - @Override - public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { - int retryTimes = 0; - do { - try { - T result = this.executeInternal(executor, uri, data); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",uri, data, result); - return result; - } catch (WxErrorException e) { - if (retryTimes + 1 > this.maxRetryTimes) { - this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); - //最后一次重试失败后,直接抛出异常,不再等待 - throw new RuntimeException("微信服务端异常,超出重试次数"); - } - - WxError error = e.getError(); - /* - * -1 系统繁忙, 1000ms后重试 - */ - if (error.getErrorCode() == -1) { - int sleepMillis = this.retrySleepMillis * (1 << retryTimes); - try { - this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); - Thread.sleep(sleepMillis); - } catch (InterruptedException e1) { - throw new RuntimeException(e1); - } - } else { - throw e; - } - } - } while (retryTimes++ < this.maxRetryTimes); - - this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); - throw new RuntimeException("微信服务端异常,超出重试次数"); - } - - public synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { - if (uri.contains("access_token=")) { - throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); - } - String accessToken = getAccessToken(false); - - String uriWithAccessToken = uri; - uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; - - try { - return executor.execute(this, 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) { - // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token - this.configStorage.expireAccessToken(); - return execute(executor, uri, data); - } - - if (error.getErrorCode() != 0) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, error); - throw new WxErrorException(error); - } - return null; - } catch (IOException e) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uri, data, e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) { - this.configStorage = wxConfigProvider; - this.initHttp(); - } - - @Override - public void setRetrySleepMillis(int retrySleepMillis) { - this.retrySleepMillis = retrySleepMillis; - } - - - @Override - public void setMaxRetryTimes(int maxRetryTimes) { - this.maxRetryTimes = maxRetryTimes; - } - - @Override - public WxSession getSession(String id) { - if (this.sessionManager == null) { - return null; - } - return this.sessionManager.getSession(id); - } - - @Override - public WxSession getSession(String id, boolean create) { - if (this.sessionManager == null) { - return null; - } - return this.sessionManager.getSession(id, create); - } - - - @Override - public void setSessionManager(WxSessionManager sessionManager) { - this.sessionManager = sessionManager; - } - - @Override - public String replaceParty(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("media_id", mediaId); - return post(url, jsonObject.toString()); - } - - @Override - public String replaceUser(String mediaId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser"; - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("media_id", mediaId); - return post(url, jsonObject.toString()); - } - - @Override - public String getTaskResult(String joinId) throws WxErrorException { - String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=" + joinId; - return get(url, null); - } - - public File getTmpDirFile() { - return this.tmpDirFile; - } - - public void setTmpDirFile(File tmpDirFile) { - this.tmpDirFile = tmpDirFile; - } - -} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java index 7c1334b4..71ccbcd0 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/apache/WxCpServiceImpl.java @@ -8,8 +8,8 @@ import me.chanjar.weixin.common.util.http.HttpType; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; import me.chanjar.weixin.cp.api.WxCpConfigStorage; -import me.chanjar.weixin.cp.api.impl.AbstractWxCpService; +import me.chanjar.weixin.cp.api.impl.AbstractWxCpServiceImpl; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -19,7 +19,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import java.io.IOException; -public class WxCpServiceImpl extends AbstractWxCpService { +public class WxCpServiceImpl extends AbstractWxCpServiceImpl { protected CloseableHttpClient httpClient; protected HttpHost httpProxy; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/AbstractWxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/AbstractWxMpService.java deleted file mode 100644 index 5c6b58eb..00000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/AbstractWxMpService.java +++ /dev/null @@ -1,445 +0,0 @@ -package me.chanjar.weixin.mp.api.impl; - -import java.io.IOException; -import java.util.concurrent.locks.Lock; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.bean.result.WxError; -import me.chanjar.weixin.common.exception.WxErrorException; -import me.chanjar.weixin.common.session.StandardSessionManager; -import me.chanjar.weixin.common.session.WxSessionManager; -import me.chanjar.weixin.common.util.RandomUtils; -import me.chanjar.weixin.common.util.crypto.SHA1; -import me.chanjar.weixin.common.util.http.*; -import me.chanjar.weixin.mp.api.*; -import me.chanjar.weixin.mp.bean.*; -import me.chanjar.weixin.mp.bean.result.*; - -public abstract class AbstractWxMpService implements WxMpService,RequestHttp { - - private static final JsonParser JSON_PARSER = new JsonParser(); - - protected final Logger log = LoggerFactory.getLogger(this.getClass()); - protected WxSessionManager sessionManager = new StandardSessionManager(); - private WxMpConfigStorage wxMpConfigStorage; - private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); - private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); - private WxMpMenuService menuService = new WxMpMenuServiceImpl(this); - private WxMpUserService userService = new WxMpUserServiceImpl(this); - private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this); - private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); - private WxMpCardService cardService = new WxMpCardServiceImpl(this); - private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); - private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); - private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); - private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this); - private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this); - - private int retrySleepMillis = 1000; - private int maxRetryTimes = 5; - - - @Override - public boolean checkSignature(String timestamp, String nonce, String signature) { - try { - return SHA1.gen(this.getWxMpConfigStorage().getToken(), timestamp, nonce) - .equals(signature); - } catch (Exception e) { - return false; - } - } - - @Override - public String getJsapiTicket() throws WxErrorException { - return getJsapiTicket(false); - } - - @Override - public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { - Lock lock = this.getWxMpConfigStorage().getJsapiTicketLock(); - try { - lock.lock(); - - if (forceRefresh) { - this.getWxMpConfigStorage().expireJsapiTicket(); - } - - if (this.getWxMpConfigStorage().isJsapiTicketExpired()) { - String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; - String responseContent = execute(new SimpleGetRequestExecutor(), url, null); - JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); - JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); - String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); - int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); - this.getWxMpConfigStorage().updateJsapiTicket(jsapiTicket, expiresInSeconds); - } - } finally { - lock.unlock(); - } - return this.getWxMpConfigStorage().getJsapiTicket(); - } - - @Override - public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { - long timestamp = System.currentTimeMillis() / 1000; - String noncestr = RandomUtils.getRandomStr(); - String jsapiTicket = getJsapiTicket(false); - String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, - "noncestr=" + noncestr, "timestamp=" + timestamp, "url=" + url); - WxJsapiSignature jsapiSignature = new WxJsapiSignature(); - jsapiSignature.setAppId(this.getWxMpConfigStorage().getAppId()); - jsapiSignature.setTimestamp(timestamp); - jsapiSignature.setNonceStr(noncestr); - jsapiSignature.setUrl(url); - jsapiSignature.setSignature(signature); - return jsapiSignature; - } - - @Override - public String getAccessToken() throws WxErrorException { - return getAccessToken(false); - } - - @Override - public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; - String responseContent = this.post(url, news.toJson()); - return WxMpMassUploadResult.fromJson(responseContent); - } - - @Override - public WxMpMassUploadResult massVideoUpload(WxMpMassVideo video) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/media/uploadvideo"; - String responseContent = this.post(url, video.toJson()); - return WxMpMassUploadResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"; - String responseContent = this.post(url, message.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/message/mass/send"; - String responseContent = this.post(url, message.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - - @Override - public WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception { - String url = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"; - String responseContent = this.post(url, wxMpMassPreviewMessage.toJson()); - return WxMpMassSendResult.fromJson(responseContent); - } - - @Override - public String shortUrl(String long_url) throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/shorturl"; - JsonObject o = new JsonObject(); - o.addProperty("action", "long2short"); - o.addProperty("long_url", long_url); - String responseContent = this.post(url, o.toString()); - JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); - return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); - } - - @Override - public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { - String url = "https://api.weixin.qq.com/semantic/semproxy/search"; - String responseContent = this.post(url, semanticQuery.toJson()); - return WxMpSemanticQueryResult.fromJson(responseContent); - } - - @Override - public String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state) { - StringBuilder url = new StringBuilder(); - url.append("https://open.weixin.qq.com/connect/oauth2/authorize?"); - url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); - url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); - url.append("&response_type=code"); - url.append("&scope=").append(scope); - if (state != null) { - url.append("&state=").append(state); - } - url.append("#wechat_redirect"); - return url.toString(); - } - - @Override - public String buildQrConnectUrl(String redirectURI, String scope, - String state) { - StringBuilder url = new StringBuilder(); - url.append("https://open.weixin.qq.com/connect/qrconnect?"); - url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); - url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); - url.append("&response_type=code"); - url.append("&scope=").append(scope); - if (state != null) { - url.append("&state=").append(state); - } - - url.append("#wechat_redirect"); - return url.toString(); - } - - private WxMpOAuth2AccessToken getOAuth2AccessToken(StringBuilder url) throws WxErrorException { - try { - RequestExecutor executor = new SimpleGetRequestExecutor(); - String responseText = executor.execute(this, url.toString(), null); - return WxMpOAuth2AccessToken.fromJson(responseText); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { - StringBuilder url = new StringBuilder(); - url.append("https://api.weixin.qq.com/sns/oauth2/access_token?"); - url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); - url.append("&secret=").append(this.getWxMpConfigStorage().getSecret()); - url.append("&code=").append(code); - url.append("&grant_type=authorization_code"); - - return this.getOAuth2AccessToken(url); - } - - @Override - public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { - StringBuilder url = new StringBuilder(); - url.append("https://api.weixin.qq.com/sns/oauth2/refresh_token?"); - url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); - url.append("&grant_type=refresh_token"); - url.append("&refresh_token=").append(refreshToken); - - return this.getOAuth2AccessToken(url); - } - - @Override - public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken oAuth2AccessToken, String lang) throws WxErrorException { - StringBuilder url = new StringBuilder(); - url.append("https://api.weixin.qq.com/sns/userinfo?"); - url.append("access_token=").append(oAuth2AccessToken.getAccessToken()); - url.append("&openid=").append(oAuth2AccessToken.getOpenId()); - if (lang == null) { - url.append("&lang=zh_CN"); - } else { - url.append("&lang=").append(lang); - } - - try { - RequestExecutor executor = new SimpleGetRequestExecutor(); - String responseText = executor.execute(this, url.toString(), null); - return WxMpUser.fromJson(responseText); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken) { - StringBuilder url = new StringBuilder(); - url.append("https://api.weixin.qq.com/sns/auth?"); - url.append("access_token=").append(oAuth2AccessToken.getAccessToken()); - url.append("&openid=").append(oAuth2AccessToken.getOpenId()); - - try { - RequestExecutor executor = new SimpleGetRequestExecutor(); - executor.execute(this, url.toString(), null); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (WxErrorException e) { - return false; - } - return true; - } - - @Override - public String[] getCallbackIP() throws WxErrorException { - String url = "https://api.weixin.qq.com/cgi-bin/getcallbackip"; - String responseContent = get(url, null); - JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); - JsonArray ipList = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray(); - String[] ipArray = new String[ipList.size()]; - for (int i = 0; i < ipList.size(); i++) { - ipArray[i] = ipList.get(i).getAsString(); - } - return ipArray; - } - - @Override - public String get(String url, String queryParam) throws WxErrorException { - return execute(new SimpleGetRequestExecutor(), url, queryParam); - } - - @Override - public String post(String url, String postData) throws WxErrorException { - return execute(new SimplePostRequestExecutor(), url, postData); - } - - /** - * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 - */ - public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException { - int retryTimes = 0; - do { - try { - T result = executeInternal(executor, uri, data); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, result); - return result; - } catch (WxErrorException e) { - if (retryTimes + 1 > this.maxRetryTimes) { - this.log.warn("重试达到最大次数【{}】", maxRetryTimes); - //最后一次重试失败后,直接抛出异常,不再等待 - throw new RuntimeException("微信服务端异常,超出重试次数"); - } - - WxError error = e.getError(); - // -1 系统繁忙, 1000ms后重试 - if (error.getErrorCode() == -1) { - int sleepMillis = this.retrySleepMillis * (1 << retryTimes); - try { - this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); - Thread.sleep(sleepMillis); - } catch (InterruptedException e1) { - throw new RuntimeException(e1); - } - } else { - throw e; - } - } - } while (retryTimes++ < this.maxRetryTimes); - - this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); - throw new RuntimeException("微信服务端异常,超出重试次数"); - } - - public synchronized T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException { - if (uri.indexOf("access_token=") != -1) { - throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); - } - String accessToken = getAccessToken(false); - - String uriWithAccessToken = uri; - uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; - - try { - return executor.execute(this, 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) { - // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token - this.getWxMpConfigStorage().expireAccessToken(); - if (this.getWxMpConfigStorage().autoRefreshToken()) { - return this.execute(executor, uri, data); - } - } - - if (error.getErrorCode() != 0) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, error); - throw new WxErrorException(error); - } - return null; - } catch (IOException e) { - this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uri, data, e.getMessage()); - throw new RuntimeException(e); - } - } - - @Override - public WxMpConfigStorage getWxMpConfigStorage() { - return this.wxMpConfigStorage; - } - - @Override - public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { - this.wxMpConfigStorage = wxConfigProvider; - this.initHttp(); - } - - @Override - public void setRetrySleepMillis(int retrySleepMillis) { - this.retrySleepMillis = retrySleepMillis; - } - - @Override - public void setMaxRetryTimes(int maxRetryTimes) { - this.maxRetryTimes = maxRetryTimes; - } - - @Override - public WxMpKefuService getKefuService() { - return this.kefuService; - } - - @Override - public WxMpMaterialService getMaterialService() { - return this.materialService; - } - - @Override - public WxMpMenuService getMenuService() { - return this.menuService; - } - - @Override - public WxMpUserService getUserService() { - return this.userService; - } - - @Override - public WxMpUserTagService getUserTagService() { - return this.tagService; - } - - @Override - public WxMpQrcodeService getQrcodeService() { - return this.qrCodeService; - } - - @Override - public WxMpCardService getCardService() { - return this.cardService; - } - - @Override - public WxMpDataCubeService getDataCubeService() { - return this.dataCubeService; - } - - @Override - public WxMpUserBlacklistService getBlackListService() { - return this.blackListService; - } - - @Override - public WxMpStoreService getStoreService() { - return this.storeService; - } - - @Override - public WxMpTemplateMsgService getTemplateMsgService() { - return this.templateMsgService; - } - - @Override - public WxMpDeviceService getDeviceService() { - return this.deviceService; - } -} From ce77578ff67b9a9ecaf9712bd41ffa17fba11f53 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Thu, 4 May 2017 23:00:35 +0800 Subject: [PATCH 09/12] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E7=94=A8=E6=88=B7=E9=A2=86=E5=8F=96=E5=8D=A1?= =?UTF-8?q?=E5=88=B8=E4=BA=8B=E4=BB=B6=E4=B8=AD=E6=89=80=E9=9C=80=E4=B8=A4?= =?UTF-8?q?=E4=B8=AA=E5=B1=9E=E6=80=A7=20#210=20=E5=B9=B6=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E6=8F=90=E5=8F=96=E5=87=BA=E9=83=A8=E5=88=86=E5=86=85?= =?UTF-8?q?=E9=83=A8=E7=B1=BB=EF=BC=8C=E9=81=BF=E5=85=8DWxMpXmlMessage?= =?UTF-8?q?=E7=B1=BB=E8=BF=87=E4=BA=8E=E5=BA=9E=E5=A4=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/mp/bean/message/HardWare.java | 50 ++++ .../weixin/mp/bean/message/ScanCodeInfo.java | 52 ++++ .../mp/bean/message/SendLocationInfo.java | 82 ++++++ .../weixin/mp/bean/message/SendPicsInfo.java | 61 +++++ .../mp/bean/message/WxMpXmlMessage.java | 233 +++--------------- 5 files changed, 280 insertions(+), 198 deletions(-) create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java new file mode 100644 index 00000000..32dfcd7e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/HardWare.java @@ -0,0 +1,50 @@ +package me.chanjar.weixin.mp.bean.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; + +/** + *
+ *  Created by BinaryWang on 2017/5/4.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("HardWare") +public class HardWare { + /** + * 消息展示,目前支持myrank(排行榜) + */ + @XStreamAlias("MessageView") + @XStreamConverter(value = XStreamCDataConverter.class) + private String messageView; + /** + * 消息点击动作,目前支持ranklist(点击跳转排行榜) + */ + @XStreamAlias("MessageAction") + @XStreamConverter(value = XStreamCDataConverter.class) + private String messageAction; + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public String getMessageView() { + return messageView; + } + + public void setMessageView(String messageView) { + this.messageView = messageView; + } + + public String getMessageAction() { + return messageAction; + } + + public void setMessageAction(String messageAction) { + this.messageAction = messageAction; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java new file mode 100644 index 00000000..ddebffdf --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/ScanCodeInfo.java @@ -0,0 +1,52 @@ +package me.chanjar.weixin.mp.bean.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; + +/** + *
+ *  Created by BinaryWang on 2017/5/4.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("ScanCodeInfo") +public class ScanCodeInfo { + @XStreamAlias("ScanType") + @XStreamConverter(value = XStreamCDataConverter.class) + private String scanType; + @XStreamAlias("ScanResult") + @XStreamConverter(value = XStreamCDataConverter.class) + private String scanResult; + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + /** + * 扫描类型,一般是qrcode + */ + public String getScanType() { + + return this.scanType; + } + + public void setScanType(String scanType) { + this.scanType = scanType; + } + + /** + * 扫描结果,即二维码对应的字符串信息 + */ + public String getScanResult() { + return this.scanResult; + } + + public void setScanResult(String scanResult) { + this.scanResult = scanResult; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java new file mode 100644 index 00000000..a397ab81 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendLocationInfo.java @@ -0,0 +1,82 @@ +package me.chanjar.weixin.mp.bean.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; + +/** + *
+ *  Created by BinaryWang on 2017/5/4.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("SendLocationInfo") +public class SendLocationInfo { + + @XStreamAlias("Location_X") + @XStreamConverter(value = XStreamCDataConverter.class) + private String locationX; + + @XStreamAlias("Location_Y") + @XStreamConverter(value = XStreamCDataConverter.class) + private String locationY; + + @XStreamAlias("Scale") + @XStreamConverter(value = XStreamCDataConverter.class) + private String scale; + + @XStreamAlias("Label") + @XStreamConverter(value = XStreamCDataConverter.class) + private String label; + + @XStreamAlias("Poiname") + @XStreamConverter(value = XStreamCDataConverter.class) + private String poiname; + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public String getLocationX() { + return this.locationX; + } + + public void setLocationX(String locationX) { + this.locationX = locationX; + } + + public String getLocationY() { + return this.locationY; + } + + public void setLocationY(String locationY) { + this.locationY = locationY; + } + + public String getScale() { + return this.scale; + } + + public void setScale(String scale) { + this.scale = scale; + } + + public String getLabel() { + return this.label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getPoiname() { + return this.poiname; + } + + public void setPoiname(String poiname) { + this.poiname = poiname; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java new file mode 100644 index 00000000..3461355c --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/SendPicsInfo.java @@ -0,0 +1,61 @@ +package me.chanjar.weixin.mp.bean.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; + +import java.util.ArrayList; +import java.util.List; + +/** + *
+ *  Created by BinaryWang on 2017/5/4.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("SendPicsInfo") +public class SendPicsInfo { + @XStreamAlias("PicList") + protected final List picList = new ArrayList<>(); + @XStreamAlias("Count") + private Long count; + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public Long getCount() { + return this.count; + } + + public void setCount(Long count) { + this.count = count; + } + + public List getPicList() { + return this.picList; + } + + @XStreamAlias("item") + public static class Item { + @XStreamAlias("PicMd5Sum") + @XStreamConverter(value = XStreamCDataConverter.class) + private String picMd5Sum; + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public String getPicMd5Sum() { + return this.picMd5Sum; + } + + public void setPicMd5Sum(String picMd5Sum) { + this.picMd5Sum = picMd5Sum; + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java index 0f1a3ffe..6f420e67 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java @@ -12,8 +12,6 @@ import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; /** *
@@ -199,6 +197,18 @@ public class WxMpXmlMessage implements Serializable {
   @XStreamAlias("OuterId")
   private Integer outerId;
 
+  /**
+   * 用户删除会员卡后可重新找回,当用户本次操作为找回时,该值为1,否则为0
+   */
+  @XStreamAlias("IsRestoreMemberCard")
+  private String isRestoreMemberCard;
+
+  /**
+   * 领取场景值,用于领取渠道数据统计。可在生成二维码接口及添加Addcard接口中自定义该字段的字符串值。
+   */
+  @XStreamAlias("OuterStr")
+  private String outerStr;
+
   @XStreamAlias("ScanCodeInfo")
   private ScanCodeInfo scanCodeInfo = new ScanCodeInfo();
 
@@ -314,11 +324,11 @@ public class WxMpXmlMessage implements Serializable {
   /**
    * 从加密字符串转换
    *
-   * @param encryptedXml
-   * @param wxMpConfigStorage
-   * @param timestamp
-   * @param nonce
-   * @param msgSignature
+   * @param encryptedXml      密文
+   * @param wxMpConfigStorage 配置存储器对象
+   * @param timestamp         时间戳
+   * @param nonce             随机串
+   * @param msgSignature      签名串
    */
   public static WxMpXmlMessage fromEncryptedXml(String encryptedXml,
                                                 WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce,
@@ -750,28 +760,28 @@ public class WxMpXmlMessage implements Serializable {
     this.outerId = outerId;
   }
 
-  public WxMpXmlMessage.ScanCodeInfo getScanCodeInfo() {
+  public ScanCodeInfo getScanCodeInfo() {
     return this.scanCodeInfo;
   }
 
-  public void setScanCodeInfo(WxMpXmlMessage.ScanCodeInfo scanCodeInfo) {
+  public void setScanCodeInfo(ScanCodeInfo scanCodeInfo) {
     this.scanCodeInfo = scanCodeInfo;
   }
 
-  public WxMpXmlMessage.SendPicsInfo getSendPicsInfo() {
+  public SendPicsInfo getSendPicsInfo() {
     return this.sendPicsInfo;
   }
 
-  public void setSendPicsInfo(WxMpXmlMessage.SendPicsInfo sendPicsInfo) {
+  public void setSendPicsInfo(SendPicsInfo sendPicsInfo) {
     this.sendPicsInfo = sendPicsInfo;
   }
 
-  public WxMpXmlMessage.SendLocationInfo getSendLocationInfo() {
+  public SendLocationInfo getSendLocationInfo() {
     return this.sendLocationInfo;
   }
 
   public void setSendLocationInfo(
-    WxMpXmlMessage.SendLocationInfo sendLocationInfo) {
+    SendLocationInfo sendLocationInfo) {
     this.sendLocationInfo = sendLocationInfo;
   }
 
@@ -807,198 +817,25 @@ public class WxMpXmlMessage implements Serializable {
     this.fromKfAccount = fromKfAccount;
   }
 
-  @Override
-  public String toString() {
-    return ToStringUtils.toSimpleString(this);
+  public String getIsRestoreMemberCard() {
+    return isRestoreMemberCard;
   }
 
-  @XStreamAlias("HardWare")
-  public static class HardWare {
-    /**
-     * 消息展示,目前支持myrank(排行榜)
-     */
-    @XStreamAlias("MessageView")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String messageView;
-    /**
-     * 消息点击动作,目前支持ranklist(点击跳转排行榜)
-     */
-    @XStreamAlias("MessageAction")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String messageAction;
-
-    @Override
-    public String toString() {
-      return ToStringUtils.toSimpleString(this);
-    }
-
-    public String getMessageView() {
-      return messageView;
-    }
-
-    public void setMessageView(String messageView) {
-      this.messageView = messageView;
-    }
-
-    public String getMessageAction() {
-      return messageAction;
-    }
-
-    public void setMessageAction(String messageAction) {
-      this.messageAction = messageAction;
-    }
+  public void setIsRestoreMemberCard(String isRestoreMemberCard) {
+    this.isRestoreMemberCard = isRestoreMemberCard;
   }
 
-  @XStreamAlias("ScanCodeInfo")
-  public static class ScanCodeInfo {
-    @XStreamAlias("ScanType")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String scanType;
-    @XStreamAlias("ScanResult")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String scanResult;
-
-    @Override
-    public String toString() {
-      return ToStringUtils.toSimpleString(this);
-    }
-
-    /**
-     * 扫描类型,一般是qrcode
-     */
-    public String getScanType() {
-
-      return this.scanType;
-    }
-
-    public void setScanType(String scanType) {
-      this.scanType = scanType;
-    }
-
-    /**
-     * 扫描结果,即二维码对应的字符串信息
-     */
-    public String getScanResult() {
-      return this.scanResult;
-    }
-
-    public void setScanResult(String scanResult) {
-      this.scanResult = scanResult;
-    }
-
+  public String getOuterStr() {
+    return outerStr;
   }
 
-  @XStreamAlias("SendPicsInfo")
-  public static class SendPicsInfo {
-    @XStreamAlias("PicList")
-    protected final List picList = new ArrayList<>();
-    @XStreamAlias("Count")
-    private Long count;
-
-    @Override
-    public String toString() {
-      return ToStringUtils.toSimpleString(this);
-    }
-
-    public Long getCount() {
-      return this.count;
-    }
-
-    public void setCount(Long count) {
-      this.count = count;
-    }
-
-    public List getPicList() {
-      return this.picList;
-    }
-
-    @XStreamAlias("item")
-    public static class Item {
-      @XStreamAlias("PicMd5Sum")
-      @XStreamConverter(value = XStreamCDataConverter.class)
-      private String picMd5Sum;
-
-      @Override
-      public String toString() {
-        return ToStringUtils.toSimpleString(this);
-      }
-
-      public String getPicMd5Sum() {
-        return this.picMd5Sum;
-      }
-
-      public void setPicMd5Sum(String picMd5Sum) {
-        this.picMd5Sum = picMd5Sum;
-      }
-    }
+  public void setOuterStr(String outerStr) {
+    this.outerStr = outerStr;
   }
 
-  @XStreamAlias("SendLocationInfo")
-  public static class SendLocationInfo {
-
-    @XStreamAlias("Location_X")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String locationX;
-
-    @XStreamAlias("Location_Y")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String locationY;
-
-    @XStreamAlias("Scale")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String scale;
-
-    @XStreamAlias("Label")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String label;
-
-    @XStreamAlias("Poiname")
-    @XStreamConverter(value = XStreamCDataConverter.class)
-    private String poiname;
-
-    @Override
-    public String toString() {
-      return ToStringUtils.toSimpleString(this);
-    }
-
-    public String getLocationX() {
-      return this.locationX;
-    }
-
-    public void setLocationX(String locationX) {
-      this.locationX = locationX;
-    }
-
-    public String getLocationY() {
-      return this.locationY;
-    }
-
-    public void setLocationY(String locationY) {
-      this.locationY = locationY;
-    }
-
-    public String getScale() {
-      return this.scale;
-    }
-
-    public void setScale(String scale) {
-      this.scale = scale;
-    }
-
-    public String getLabel() {
-      return this.label;
-    }
-
-    public void setLabel(String label) {
-      this.label = label;
-    }
-
-    public String getPoiname() {
-      return this.poiname;
-    }
-
-    public void setPoiname(String poiname) {
-      this.poiname = poiname;
-    }
+  @Override
+  public String toString() {
+    return ToStringUtils.toSimpleString(this);
   }
+
 }

From ee834e2b7d38463d37adf5c6582a2a756763cc21 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Thu, 4 May 2017 23:05:17 +0800
Subject: [PATCH 10/12] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?=
 =?UTF-8?q?=E6=8E=A8=E9=80=81=E5=8D=A1=E5=88=B8=E8=BD=AC=E8=B5=A0=E4=BA=8B?=
 =?UTF-8?q?=E4=BB=B6=E4=B8=AD=E6=89=80=E9=9C=80=E4=B8=A4=E4=B8=AA=E5=B1=9E?=
 =?UTF-8?q?=E6=80=A7=20#211?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../mp/bean/message/WxMpXmlMessage.java       | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
index 6f420e67..e8802842 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
@@ -209,6 +209,18 @@ public class WxMpXmlMessage implements Serializable {
   @XStreamAlias("OuterStr")
   private String outerStr;
 
+  /**
+   * 是否转赠退回,0代表不是,1代表是。
+   */
+  @XStreamAlias("IsReturnBack")
+  private String isReturnBack;
+
+  /**
+   * 是否是群转赠,0代表不是,1代表是。
+   */
+  @XStreamAlias("IsChatRoom")
+  private String isChatRoom;
+
   @XStreamAlias("ScanCodeInfo")
   private ScanCodeInfo scanCodeInfo = new ScanCodeInfo();
 
@@ -833,6 +845,22 @@ public class WxMpXmlMessage implements Serializable {
     this.outerStr = outerStr;
   }
 
+  public String getIsReturnBack() {
+    return isReturnBack;
+  }
+
+  public void setIsReturnBack(String isReturnBack) {
+    this.isReturnBack = isReturnBack;
+  }
+
+  public String getIsChatRoom() {
+    return isChatRoom;
+  }
+
+  public void setIsChatRoom(String isChatRoom) {
+    this.isChatRoom = isChatRoom;
+  }
+
   @Override
   public String toString() {
     return ToStringUtils.toSimpleString(this);

From b2a65d5648b4adf5e46bb2660633d29d16b39b44 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Thu, 4 May 2017 23:09:49 +0800
Subject: [PATCH 11/12] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=8E=A8=E9=80=81?=
 =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=8D=A1=E5=88=B8=E8=BD=AC=E8=B5=A0=E4=BA=8B?=
 =?UTF-8?q?=E4=BB=B6=EF=BC=9AWxConsts=E5=A2=9E=E5=8A=A0=E7=BC=BA=E5=A4=B1?=
 =?UTF-8?q?=E7=9A=84user=5Fgifting=5Fcard=E4=BA=8B=E4=BB=B6=E5=B8=B8?=
 =?UTF-8?q?=E9=87=8F=20#212?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../main/java/me/chanjar/weixin/common/api/WxConsts.java | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
index 435ba8e0..2581950d 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
@@ -91,6 +91,10 @@ public class WxConsts {
   public static final String EVT_CARD_PASS_CHECK = "card_pass_check";
   public static final String EVT_CARD_NOT_PASS_CHECK = "card_not_pass_check";
   public static final String EVT_USER_GET_CARD = "user_get_card";
+  /**
+   * 卡券转赠事件
+   */
+  public static final String EVT_USER_GIFTING_CARD = "user_gifting_card";
   public static final String EVT_USER_DEL_CARD = "user_del_card";
   public static final String EVT_USER_CONSUME_CARD = "user_consume_card";
   public static final String EVT_USER_PAY_FROM_PAY_CELL = "user_pay_from_pay_cell";
@@ -101,7 +105,8 @@ public class WxConsts {
   public static final String EVT_KF_CLOSE_SESSION = "kf_close_session"; // 客服关闭会话
   public static final String EVT_KF_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话
   public static final String EVT_POI_CHECK_NOTIFY = "poi_check_notify"; //门店审核事件推送
-  public static final String EVN_SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; //接收会员信息事件推送
+  public static final String EVT_SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; //接收会员信息事件推送
+
   //以下为微信认证事件
   /**
    * 资质认证成功
@@ -185,7 +190,7 @@ public class WxConsts {
    * 跳转图文消息URL
    */
   public static final String BUTTON_VIEW_LIMITED = "view_limited";
-  
+
   /**
    * 不弹出授权页面,直接跳转,只能获取用户openid
    */

From a47a0bb580259cd6a62aecd66d339a58dfd25e10 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Mon, 8 May 2017 14:08:02 +0800
Subject: [PATCH 12/12] =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E4=B8=A4=E4=B8=AAcp?=
 =?UTF-8?q?=E4=B8=93=E7=94=A8=E7=9A=84bean=E5=88=B0cp=E9=87=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java     | 0
 .../main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java   | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename {weixin-java-common => weixin-java-cp}/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java (100%)
 rename {weixin-java-common => weixin-java-cp}/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java (100%)

diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java
similarity index 100%
rename from weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java
rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
similarity index 100%
rename from weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
rename to weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java