| @@ -0,0 +1,13 @@ | |||||
| language: java | |||||
| jdk: | |||||
| - oraclejdk7 | |||||
| script: "mvn clean install -Dmaven.test.skip=true" | |||||
| branches: | |||||
| only: | |||||
| - develop | |||||
| notifications: | |||||
| email: | |||||
| - chanjarster@gmail.com | |||||
| @@ -1,4 +1,8 @@ | |||||
| weixin-java-tools | weixin-java-tools | ||||
| [](https://travis-ci.org/chanjarster/weixin-java-tools) | |||||
|  | |||||
| =========== | =========== | ||||
| 微信公众号、企业号Java SDK。 | 微信公众号、企业号Java SDK。 | ||||
| @@ -5,7 +5,7 @@ | |||||
| <modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.1.1</version> | |||||
| <version>1.1.2</version> | |||||
| <packaging>pom</packaging> | <packaging>pom</packaging> | ||||
| <name>WeiXin Java Tools - Parent</name> | <name>WeiXin Java Tools - Parent</name> | ||||
| <description>微信公众号、企业号上级POM</description> | <description>微信公众号、企业号上级POM</description> | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.1.1</version> | |||||
| <version>1.1.2</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
| @@ -1,4 +1,4 @@ | |||||
| package me.chanjar.weixin.common.util; | |||||
| package me.chanjar.weixin.common.api; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| @@ -1,4 +1,4 @@ | |||||
| package me.chanjar.weixin.common.util; | |||||
| package me.chanjar.weixin.common.api; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -1,4 +1,4 @@ | |||||
| package me.chanjar.weixin.common.util; | |||||
| package me.chanjar.weixin.common.api; | |||||
| import java.util.Map; | import java.util.Map; | ||||
| import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
| @@ -2,7 +2,9 @@ package me.chanjar.weixin.common.bean; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
| public class WxAccessToken { | |||||
| import java.io.Serializable; | |||||
| public class WxAccessToken implements Serializable { | |||||
| private String accessToken; | private String accessToken; | ||||
| @@ -1,9 +1,11 @@ | |||||
| package me.chanjar.weixin.mp.bean.result; | |||||
| package me.chanjar.weixin.common.bean; | |||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * jspai signature | * jspai signature | ||||
| */ | */ | ||||
| public class WxMpJsapiSignature { | |||||
| public class WxJsapiSignature implements Serializable { | |||||
| private String noncestr; | private String noncestr; | ||||
| @@ -2,6 +2,7 @@ package me.chanjar.weixin.common.bean; | |||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -12,7 +13,7 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxMenu { | |||||
| public class WxMenu implements Serializable { | |||||
| private List<WxMenuButton> buttons = new ArrayList<WxMenuButton>(); | private List<WxMenuButton> buttons = new ArrayList<WxMenuButton>(); | ||||
| @@ -2,13 +2,15 @@ package me.chanjar.weixin.common.bean.result; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 微信错误码说明 | * 微信错误码说明 | ||||
| * http://mp.weixin.qq.com/wiki/index.php?title=全局返回码说明 | * http://mp.weixin.qq.com/wiki/index.php?title=全局返回码说明 | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxError { | |||||
| public class WxError implements Serializable { | |||||
| private int errorCode; | private int errorCode; | ||||
| @@ -2,7 +2,9 @@ package me.chanjar.weixin.common.bean.result; | |||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
| public class WxMediaUploadResult { | |||||
| import java.io.Serializable; | |||||
| public class WxMediaUploadResult implements Serializable { | |||||
| private String type; | private String type; | ||||
| private String mediaId; | private String mediaId; | ||||
| @@ -1,5 +1,6 @@ | |||||
| package me.chanjar.weixin.common.util; | package me.chanjar.weixin.common.util; | ||||
| import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| @@ -0,0 +1,17 @@ | |||||
| package me.chanjar.weixin.common.util; | |||||
| public class RandomUtils { | |||||
| private static final String RANDOM_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; | |||||
| private static final java.util.Random RANDOM = new java.util.Random(); | |||||
| public static String getRandomStr() { | |||||
| StringBuilder sb = new StringBuilder(); | |||||
| for (int i = 0; i < 16; i++) { | |||||
| sb.append(RANDOM_STR.charAt(RANDOM.nextInt(RANDOM_STR.length()))); | |||||
| } | |||||
| return sb.toString(); | |||||
| } | |||||
| } | |||||
| @@ -1,5 +1,6 @@ | |||||
| package me.chanjar.weixin.common.util; | package me.chanjar.weixin.common.util; | ||||
| import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; | |||||
| import org.testng.Assert; | import org.testng.Assert; | ||||
| import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.1.1</version> | |||||
| <version>1.1.2</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
| @@ -22,6 +22,20 @@ public interface WxCpConfigStorage { | |||||
| public void updateAccessToken(String accessToken, int expiresIn); | public void updateAccessToken(String accessToken, int expiresIn); | ||||
| public String getJsapiTicket(); | |||||
| public boolean isJsapiTicketExpired(); | |||||
| /** | |||||
| * 强制将jsapi ticket过期掉 | |||||
| */ | |||||
| public void expireJsapiTicket(); | |||||
| /** | |||||
| * 应该是线程安全的 | |||||
| * @param jsapiTicket | |||||
| */ | |||||
| public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | |||||
| public String getCorpId(); | public String getCorpId(); | ||||
| @@ -25,6 +25,9 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| protected volatile String http_proxy_username; | protected volatile String http_proxy_username; | ||||
| protected volatile String http_proxy_password; | protected volatile String http_proxy_password; | ||||
| protected volatile String jsapiTicket; | |||||
| protected volatile long jsapiTicketExpiresTime; | |||||
| public String getAccessToken() { | public String getAccessToken() { | ||||
| return this.accessToken; | return this.accessToken; | ||||
| } | } | ||||
| @@ -46,6 +49,37 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | ||||
| } | } | ||||
| @Override | |||||
| public String getJsapiTicket() { | |||||
| return jsapiTicket; | |||||
| } | |||||
| public void setJsapiTicket(String jsapiTicket) { | |||||
| this.jsapiTicket = jsapiTicket; | |||||
| } | |||||
| public long getJsapiTicketExpiresTime() { | |||||
| return jsapiTicketExpiresTime; | |||||
| } | |||||
| public void setJsapiTicketExpiresTime(long jsapiTicketExpiresTime) { | |||||
| this.jsapiTicketExpiresTime = jsapiTicketExpiresTime; | |||||
| } | |||||
| public boolean isJsapiTicketExpired() { | |||||
| return System.currentTimeMillis() > this.jsapiTicketExpiresTime; | |||||
| } | |||||
| public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
| this.jsapiTicket = jsapiTicket; | |||||
| // 预留200秒的时间 | |||||
| this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | |||||
| } | |||||
| public void expireJsapiTicket() { | |||||
| this.jsapiTicketExpiresTime = 0; | |||||
| } | |||||
| public String getCorpId() { | public String getCorpId() { | ||||
| return this.corpId; | return this.corpId; | ||||
| } | } | ||||
| @@ -153,6 +187,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| ", http_proxy_port=" + http_proxy_port + | ", http_proxy_port=" + http_proxy_port + | ||||
| ", http_proxy_username='" + http_proxy_username + '\'' + | ", http_proxy_username='" + http_proxy_username + '\'' + | ||||
| ", http_proxy_password='" + http_proxy_password + '\'' + | ", http_proxy_password='" + http_proxy_password + '\'' + | ||||
| ", jsapiTicket='" + jsapiTicket + '\'' + | |||||
| ", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' + | |||||
| '}'; | '}'; | ||||
| } | } | ||||
| @@ -5,9 +5,9 @@ import me.chanjar.weixin.common.session.InternalSessionManager; | |||||
| import me.chanjar.weixin.common.session.StandardSessionManager; | import me.chanjar.weixin.common.session.StandardSessionManager; | ||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
| import me.chanjar.weixin.common.util.LogExceptionHandler; | import me.chanjar.weixin.common.util.LogExceptionHandler; | ||||
| import me.chanjar.weixin.common.util.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.common.util.WxMessageDuplicateChecker; | |||||
| import me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker; | |||||
| import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; | |||||
| import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; | |||||
| import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | ||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | ||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| @@ -87,8 +87,8 @@ public class WxCpMessageRouter { | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 设置自定义的 {@link me.chanjar.weixin.common.util.WxMessageDuplicateChecker} | |||||
| * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker} | |||||
| * 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker} | |||||
| * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker} | |||||
| * </pre> | * </pre> | ||||
| * @param messageDuplicateChecker | * @param messageDuplicateChecker | ||||
| */ | */ | ||||
| @@ -109,7 +109,7 @@ public class WxCpMessageRouter { | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 设置自定义的{@link me.chanjar.weixin.common.util.WxErrorExceptionHandler} | |||||
| * 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} | |||||
| * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} | * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} | ||||
| * </pre> | * </pre> | ||||
| * @param exceptionHandler | * @param exceptionHandler | ||||
| @@ -204,7 +204,8 @@ public class WxCpMessageRouter { | |||||
| messageId = String.valueOf(wxMessage.getCreateTime()) | messageId = String.valueOf(wxMessage.getCreateTime()) | ||||
| + "-" +String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId()) | + "-" +String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId()) | ||||
| + "-" + wxMessage.getFromUserName() | + "-" + wxMessage.getFromUserName() | ||||
| + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEvent()); | |||||
| + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) | |||||
| + "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) | |||||
| ; | ; | ||||
| } else { | } else { | ||||
| messageId = String.valueOf(wxMessage.getMsgId()); | messageId = String.valueOf(wxMessage.getMsgId()); | ||||
| @@ -2,7 +2,7 @@ package me.chanjar.weixin.cp.api; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
| import me.chanjar.weixin.common.util.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | ||||
| import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | ||||
| @@ -1,5 +1,6 @@ | |||||
| package me.chanjar.weixin.cp.api; | package me.chanjar.weixin.cp.api; | ||||
| import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
| import me.chanjar.weixin.common.bean.WxMenu; | import me.chanjar.weixin.common.bean.WxMenu; | ||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| @@ -67,6 +68,38 @@ public interface WxCpService { | |||||
| */ | */ | ||||
| String getAccessToken(boolean forceRefresh) throws WxErrorException; | String getAccessToken(boolean forceRefresh) throws WxErrorException; | ||||
| /** | |||||
| * 获得jsapi_ticket,不强制刷新jsapi_ticket | |||||
| * @see #getJsapiTicket(boolean) | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| public String getJsapiTicket() throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 获得jsapi_ticket | |||||
| * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 | |||||
| * | |||||
| * 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | |||||
| * </pre> | |||||
| * @param forceRefresh 强制刷新 | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| public String getJsapiTicket(boolean forceRefresh) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 创建调用jsapi时所需要的签名 | |||||
| * | |||||
| * 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | |||||
| * </pre> | |||||
| * @param url url | |||||
| * @return | |||||
| */ | |||||
| public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 上传多媒体文件 | * 上传多媒体文件 | ||||
| @@ -320,10 +353,11 @@ public interface WxCpService { | |||||
| * 构造oauth2授权的url连接 | * 构造oauth2授权的url连接 | ||||
| * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | ||||
| * </pre> | * </pre> | ||||
| * @param redirectUri | |||||
| * @param state | * @param state | ||||
| * @return code | * @return code | ||||
| */ | */ | ||||
| String oauth2buildAuthorizationUrl(String state); | |||||
| String oauth2buildAuthorizationUrl(String redirectUri, String state); | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -8,6 +8,7 @@ import com.google.gson.internal.Streams; | |||||
| import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
| import com.google.gson.stream.JsonReader; | import com.google.gson.stream.JsonReader; | ||||
| import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
| import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
| import me.chanjar.weixin.common.bean.WxMenu; | import me.chanjar.weixin.common.bean.WxMenu; | ||||
| import me.chanjar.weixin.common.bean.result.WxError; | import me.chanjar.weixin.common.bean.result.WxError; | ||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
| @@ -15,6 +16,7 @@ import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.common.session.StandardSessionManager; | import me.chanjar.weixin.common.session.StandardSessionManager; | ||||
| import me.chanjar.weixin.common.session.WxSession; | import me.chanjar.weixin.common.session.WxSession; | ||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
| import me.chanjar.weixin.common.util.RandomUtils; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | import me.chanjar.weixin.common.util.StringUtils; | ||||
| import me.chanjar.weixin.common.util.crypto.SHA1; | import me.chanjar.weixin.common.util.crypto.SHA1; | ||||
| import me.chanjar.weixin.common.util.fs.FileUtils; | import me.chanjar.weixin.common.util.fs.FileUtils; | ||||
| @@ -44,6 +46,7 @@ import java.io.File; | |||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.io.StringReader; | import java.io.StringReader; | ||||
| import java.security.NoSuchAlgorithmException; | |||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.UUID; | import java.util.UUID; | ||||
| @@ -54,7 +57,12 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| /** | /** | ||||
| * 全局的是否正在刷新access token的锁 | * 全局的是否正在刷新access token的锁 | ||||
| */ | */ | ||||
| protected static final Object GLOBAL_ACCESS_TOKEN_REFRESH_LOCK = new Object(); | |||||
| protected final Object globalAccessTokenRefreshLock = new Object(); | |||||
| /** | |||||
| * 全局的是否正在刷新jsapi_ticket的锁 | |||||
| */ | |||||
| protected final Object globalJsapiTicketRefreshLock = new Object(); | |||||
| protected WxCpConfigStorage wxCpConfigStorage; | protected WxCpConfigStorage wxCpConfigStorage; | ||||
| @@ -90,7 +98,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| wxCpConfigStorage.expireAccessToken(); | wxCpConfigStorage.expireAccessToken(); | ||||
| } | } | ||||
| if (wxCpConfigStorage.isAccessTokenExpired()) { | if (wxCpConfigStorage.isAccessTokenExpired()) { | ||||
| synchronized (GLOBAL_ACCESS_TOKEN_REFRESH_LOCK) { | |||||
| synchronized (globalAccessTokenRefreshLock) { | |||||
| if (wxCpConfigStorage.isAccessTokenExpired()) { | if (wxCpConfigStorage.isAccessTokenExpired()) { | ||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | ||||
| + "&corpid=" + wxCpConfigStorage.getCorpId() | + "&corpid=" + wxCpConfigStorage.getCorpId() | ||||
| @@ -121,6 +129,52 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| return wxCpConfigStorage.getAccessToken(); | return wxCpConfigStorage.getAccessToken(); | ||||
| } | } | ||||
| public String getJsapiTicket() throws WxErrorException { | |||||
| return getJsapiTicket(false); | |||||
| } | |||||
| public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { | |||||
| if (forceRefresh) { | |||||
| wxCpConfigStorage.expireJsapiTicket(); | |||||
| } | |||||
| if (wxCpConfigStorage.isJsapiTicketExpired()) { | |||||
| synchronized (globalJsapiTicketRefreshLock) { | |||||
| if (wxCpConfigStorage.isJsapiTicketExpired()) { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket"; | |||||
| String responseContent = execute(new SimpleGetRequestExecutor(), url, null); | |||||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||||
| JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | |||||
| String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | |||||
| int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | |||||
| wxCpConfigStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds); | |||||
| } | |||||
| } | |||||
| } | |||||
| return wxCpConfigStorage.getJsapiTicket(); | |||||
| } | |||||
| public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { | |||||
| long timestamp = System.currentTimeMillis() / 1000; | |||||
| String noncestr = RandomUtils.getRandomStr(); | |||||
| String jsapiTicket = getJsapiTicket(false); | |||||
| try { | |||||
| 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); | |||||
| return jsapiSignature; | |||||
| } catch (NoSuchAlgorithmException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| public void messageSend(WxCpMessage message) throws WxErrorException { | public void messageSend(WxCpMessage message) throws WxErrorException { | ||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; | String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send"; | ||||
| post(url, message.toJson()); | post(url, message.toJson()); | ||||
| @@ -358,10 +412,10 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public String oauth2buildAuthorizationUrl(String state) { | |||||
| public String oauth2buildAuthorizationUrl(String redirectUri, String state) { | |||||
| String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" ; | String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" ; | ||||
| url += "appid=" + wxCpConfigStorage.getCorpId(); | url += "appid=" + wxCpConfigStorage.getCorpId(); | ||||
| url += "&redirect_uri=" + URIUtil.encodeURIComponent(wxCpConfigStorage.getOauth2redirectUri()); | |||||
| url += "&redirect_uri=" + URIUtil.encodeURIComponent(redirectUri); | |||||
| url += "&response_type=code"; | url += "&response_type=code"; | ||||
| url += "&scope=snsapi_base"; | url += "&scope=snsapi_base"; | ||||
| if (state != null) { | if (state != null) { | ||||
| @@ -373,22 +427,13 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| @Override | @Override | ||||
| public String[] oauth2getUserInfo(String code) throws WxErrorException { | public String[] oauth2getUserInfo(String code) throws WxErrorException { | ||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?"; | |||||
| url += "access_token=" + wxCpConfigStorage.getAccessToken(); | |||||
| url += "&code=" + code; | |||||
| url += "&agendid=" + wxCpConfigStorage.getAgentId(); | |||||
| try { | |||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | |||||
| String responseText = executor.execute(getHttpclient(), httpProxy, url, null); | |||||
| JsonElement je = Streams.parse(new JsonReader(new StringReader(responseText))); | |||||
| JsonObject jo = je.getAsJsonObject(); | |||||
| return new String[] {GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId")}; | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?" | |||||
| + "code=" + code | |||||
| + "&agendid=" + wxCpConfigStorage.getAgentId(); | |||||
| String responseText = get(url, null); | |||||
| JsonElement je = Streams.parse(new JsonReader(new StringReader(responseText))); | |||||
| JsonObject jo = je.getAsJsonObject(); | |||||
| return new String[] {GsonHelper.getString(jo, "UserId"), GsonHelper.getString(jo, "DeviceId")}; | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -477,7 +522,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| * 42001 access_token超时 | * 42001 access_token超时 | ||||
| */ | */ | ||||
| if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | ||||
| // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | |||||
| // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | |||||
| wxCpConfigStorage.expireAccessToken(); | wxCpConfigStorage.expireAccessToken(); | ||||
| return execute(executor, uri, data); | return execute(executor, uri, data); | ||||
| } | } | ||||
| @@ -2,12 +2,14 @@ package me.chanjar.weixin.cp.bean; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 微信部门 | * 微信部门 | ||||
| * | * | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public class WxCpDepart { | |||||
| public class WxCpDepart implements Serializable { | |||||
| private Integer id; | private Integer id; | ||||
| private String name; | private String name; | ||||
| @@ -3,6 +3,7 @@ package me.chanjar.weixin.cp.bean; | |||||
| import me.chanjar.weixin.cp.bean.messagebuilder.*; | import me.chanjar.weixin.cp.bean.messagebuilder.*; | ||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -11,7 +12,7 @@ import java.util.List; | |||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxCpMessage { | |||||
| public class WxCpMessage implements Serializable { | |||||
| private String toUser; | private String toUser; | ||||
| private String toParty; | private String toParty; | ||||
| @@ -2,10 +2,12 @@ package me.chanjar.weixin.cp.bean; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * Created by Daniel Qian | * Created by Daniel Qian | ||||
| */ | */ | ||||
| public class WxCpTag { | |||||
| public class WxCpTag implements Serializable { | |||||
| private String id; | private String id; | ||||
| @@ -2,6 +2,7 @@ package me.chanjar.weixin.cp.bean; | |||||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -10,7 +11,7 @@ import java.util.List; | |||||
| * | * | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public class WxCpUser { | |||||
| public class WxCpUser implements Serializable { | |||||
| private String userId; | private String userId; | ||||
| private String name; | private String name; | ||||
| @@ -10,6 +10,7 @@ import org.apache.commons.io.IOUtils; | |||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -25,7 +26,7 @@ import java.util.List; | |||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| @XStreamAlias("xml") | @XStreamAlias("xml") | ||||
| public class WxCpXmlMessage { | |||||
| public class WxCpXmlMessage implements Serializable { | |||||
| /////////////////////// | /////////////////////// | ||||
| // 以下都是微信推送过来的消息的xml的element所对应的属性 | // 以下都是微信推送过来的消息的xml的element所对应的属性 | ||||
| @@ -62,7 +62,7 @@ public class WxCpDemoServer { | |||||
| @Override | @Override | ||||
| public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, | public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, | ||||
| WxCpService wxCpService, WxSessionManager sessionManager) { | WxCpService wxCpService, WxSessionManager sessionManager) { | ||||
| String href = "<a href=\"" + wxCpService.oauth2buildAuthorizationUrl(null) | |||||
| String href = "<a href=\"" + wxCpService.oauth2buildAuthorizationUrl(wxCpConfigStorage.getOauth2redirectUri(), null) | |||||
| + "\">测试oauth2</a>"; | + "\">测试oauth2</a>"; | ||||
| return WxCpXmlOutMessage | return WxCpXmlOutMessage | ||||
| .TEXT() | .TEXT() | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.1.1</version> | |||||
| <version>1.1.2</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
| <name>WeiXin Java Tools - MP</name> | <name>WeiXin Java Tools - MP</name> | ||||
| @@ -0,0 +1,68 @@ | |||||
| /** | |||||
| * Created by qianjia on 15/1/25. | |||||
| */ | |||||
| public class TestNonAtomicLongAssignment { | |||||
| private static final long HI = 1l << 32; | |||||
| private static final long LO = 1l; | |||||
| private static final long TEST_NUMBER = HI | LO; | |||||
| private static long assignee = 0l; | |||||
| public static void main(String[] args) { | |||||
| Thread writer = new Thread(new Runnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| while (true) { | |||||
| assignee = TEST_NUMBER; | |||||
| } | |||||
| } | |||||
| }); | |||||
| writer.setDaemon(true); | |||||
| Thread reader = new Thread(new Runnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| long i = 0; | |||||
| while (true) { | |||||
| i++; | |||||
| long test = assignee; | |||||
| if (test != TEST_NUMBER) { | |||||
| System.out.print(i + " times:" + toBin(test)); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| }); | |||||
| // Thread worker = new Thread(new Runnable() { | |||||
| // @Override | |||||
| // public void run() { | |||||
| // double d = 89009808877238948224343435452333323113131313133434434341212323232424243434335354232390490189190420928348910913094983.323334401928d; | |||||
| // while(true) { | |||||
| // Math.cbrt(d); | |||||
| // d = d - 1l; | |||||
| // } | |||||
| // } | |||||
| // }); | |||||
| // worker.setDaemon(true); | |||||
| // worker.start(); | |||||
| writer.start(); | |||||
| reader.start(); | |||||
| } | |||||
| public static String toBin(long n) { | |||||
| StringBuilder sb = new StringBuilder(Long.toBinaryString(n)); | |||||
| int padding = 64 - sb.length(); | |||||
| while (padding > 0) { | |||||
| sb.insert(0, '0'); | |||||
| padding--; | |||||
| } | |||||
| return sb.toString(); | |||||
| } | |||||
| } | |||||
| @@ -51,6 +51,18 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| return jsapiTicket; | return jsapiTicket; | ||||
| } | } | ||||
| public void setJsapiTicket(String jsapiTicket) { | |||||
| this.jsapiTicket = jsapiTicket; | |||||
| } | |||||
| public long getJsapiTicketExpiresTime() { | |||||
| return jsapiTicketExpiresTime; | |||||
| } | |||||
| public void setJsapiTicketExpiresTime(long jsapiTicketExpiresTime) { | |||||
| this.jsapiTicketExpiresTime = jsapiTicketExpiresTime; | |||||
| } | |||||
| public boolean isJsapiTicketExpired() { | public boolean isJsapiTicketExpired() { | ||||
| return System.currentTimeMillis() > this.jsapiTicketExpiresTime; | return System.currentTimeMillis() > this.jsapiTicketExpiresTime; | ||||
| } | } | ||||
| @@ -167,5 +179,4 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
| ", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' + | ", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' + | ||||
| '}'; | '}'; | ||||
| } | } | ||||
| } | } | ||||
| @@ -5,9 +5,9 @@ import me.chanjar.weixin.common.session.InternalSessionManager; | |||||
| import me.chanjar.weixin.common.session.StandardSessionManager; | import me.chanjar.weixin.common.session.StandardSessionManager; | ||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
| import me.chanjar.weixin.common.util.LogExceptionHandler; | import me.chanjar.weixin.common.util.LogExceptionHandler; | ||||
| import me.chanjar.weixin.common.util.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.common.util.WxMessageDuplicateChecker; | |||||
| import me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker; | |||||
| import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; | |||||
| import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | ||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | ||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| @@ -87,8 +87,8 @@ public class WxMpMessageRouter { | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 设置自定义的 {@link me.chanjar.weixin.common.util.WxMessageDuplicateChecker} | |||||
| * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker} | |||||
| * 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker} | |||||
| * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker} | |||||
| * </pre> | * </pre> | ||||
| * @param messageDuplicateChecker | * @param messageDuplicateChecker | ||||
| */ | */ | ||||
| @@ -109,7 +109,7 @@ public class WxMpMessageRouter { | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 设置自定义的{@link me.chanjar.weixin.common.util.WxErrorExceptionHandler} | |||||
| * 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} | |||||
| * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} | * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} | ||||
| * </pre> | * </pre> | ||||
| * @param exceptionHandler | * @param exceptionHandler | ||||
| @@ -203,7 +203,8 @@ public class WxMpMessageRouter { | |||||
| if (wxMessage.getMsgId() == null) { | if (wxMessage.getMsgId() == null) { | ||||
| messageId = String.valueOf(wxMessage.getCreateTime()) | messageId = String.valueOf(wxMessage.getCreateTime()) | ||||
| + "-" + wxMessage.getFromUserName() | + "-" + wxMessage.getFromUserName() | ||||
| + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEvent()); | |||||
| + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) | |||||
| + "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) | |||||
| ; | ; | ||||
| } else { | } else { | ||||
| messageId = String.valueOf(wxMessage.getMsgId()); | messageId = String.valueOf(wxMessage.getMsgId()); | ||||
| @@ -2,7 +2,7 @@ package me.chanjar.weixin.mp.api; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
| import me.chanjar.weixin.common.util.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | ||||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | ||||
| @@ -1,10 +1,9 @@ | |||||
| package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
| import me.chanjar.weixin.common.bean.WxMenu; | import me.chanjar.weixin.common.bean.WxMenu; | ||||
| import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.common.session.WxSession; | |||||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | import me.chanjar.weixin.common.util.http.RequestExecutor; | ||||
| import me.chanjar.weixin.mp.bean.*; | import me.chanjar.weixin.mp.bean.*; | ||||
| import me.chanjar.weixin.mp.bean.result.*; | import me.chanjar.weixin.mp.bean.result.*; | ||||
| @@ -12,13 +11,16 @@ import me.chanjar.weixin.mp.bean.result.*; | |||||
| import java.io.File; | import java.io.File; | ||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.text.SimpleDateFormat; | |||||
| import java.util.Date; | |||||
| import java.util.List; | import java.util.List; | ||||
| /** | /** | ||||
| * 微信API的Service | * 微信API的Service | ||||
| */ | */ | ||||
| public interface WxMpService { | public interface WxMpService { | ||||
| public static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 验证推送过来的消息的正确性 | * 验证推送过来的消息的正确性 | ||||
| @@ -86,7 +88,7 @@ public interface WxMpService { | |||||
| * @param url url | * @param url url | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| public WxMpJsapiSignature createJsapiSignature(String url) throws WxErrorException; | |||||
| public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -422,6 +424,40 @@ public interface WxMpService { | |||||
| */ | */ | ||||
| public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); | public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); | ||||
| /** | |||||
| * <pre> | |||||
| * 获取微信服务器IP地址 | |||||
| * http://mp.weixin.qq.com/wiki/0/2ad4b6bfd29f30f71d39616c2a0fcedc.html | |||||
| * </pre> | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| String[] getCallbackIP() throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 获取用户增减数据 | |||||
| * http://mp.weixin.qq.com/wiki/3/ecfed6e1a0a03b5f35e5efac98e864b7.html | |||||
| * </pre> | |||||
| * @param beginDate 最大时间跨度7天 | |||||
| * @param endDate endDate不能早于begingDate | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| List<WxMpUserSummary> getUserSummary(Date beginDate, Date endDate) throws WxErrorException; | |||||
| /** | |||||
| * <pre> | |||||
| * 获取累计用户数据 | |||||
| * http://mp.weixin.qq.com/wiki/3/ecfed6e1a0a03b5f35e5efac98e864b7.html | |||||
| * </pre> | |||||
| * @param beginDate 最大时间跨度7天 | |||||
| * @param endDate endDate不能早于begingDate | |||||
| * @return | |||||
| * @throws WxErrorException | |||||
| */ | |||||
| List<WxMpUserCumulate> getUserCumulate(Date beginDate, Date endDate) throws WxErrorException; | |||||
| /** | /** | ||||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | ||||
| * @param url | * @param url | ||||
| @@ -1,5 +1,6 @@ | |||||
| package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
| import com.google.gson.JsonArray; | |||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
| import com.google.gson.internal.Streams; | import com.google.gson.internal.Streams; | ||||
| @@ -7,11 +8,13 @@ import com.google.gson.reflect.TypeToken; | |||||
| import com.google.gson.stream.JsonReader; | import com.google.gson.stream.JsonReader; | ||||
| import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
| import me.chanjar.weixin.common.bean.WxMenu; | import me.chanjar.weixin.common.bean.WxMenu; | ||||
| import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||||
| import me.chanjar.weixin.common.bean.result.WxError; | import me.chanjar.weixin.common.bean.result.WxError; | ||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | ||||
| import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
| import me.chanjar.weixin.common.session.StandardSessionManager; | import me.chanjar.weixin.common.session.StandardSessionManager; | ||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
| import me.chanjar.weixin.common.util.RandomUtils; | |||||
| import me.chanjar.weixin.common.util.StringUtils; | import me.chanjar.weixin.common.util.StringUtils; | ||||
| import me.chanjar.weixin.common.util.crypto.SHA1; | import me.chanjar.weixin.common.util.crypto.SHA1; | ||||
| import me.chanjar.weixin.common.util.fs.FileUtils; | import me.chanjar.weixin.common.util.fs.FileUtils; | ||||
| @@ -41,16 +44,12 @@ import java.io.IOException; | |||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.io.StringReader; | import java.io.StringReader; | ||||
| import java.security.NoSuchAlgorithmException; | import java.security.NoSuchAlgorithmException; | ||||
| import java.util.Date; | |||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Random; | |||||
| import java.util.UUID; | import java.util.UUID; | ||||
| public class WxMpServiceImpl implements WxMpService { | public class WxMpServiceImpl implements WxMpService { | ||||
| protected final String RANDOM_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; | |||||
| protected final Random RANDOM = new Random(); | |||||
| protected final Logger log = LoggerFactory.getLogger(WxMpServiceImpl.class); | protected final Logger log = LoggerFactory.getLogger(WxMpServiceImpl.class); | ||||
| /** | /** | ||||
| @@ -148,9 +147,9 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| return wxMpConfigStorage.getJsapiTicket(); | return wxMpConfigStorage.getJsapiTicket(); | ||||
| } | } | ||||
| public WxMpJsapiSignature createJsapiSignature(String url) throws WxErrorException { | |||||
| public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException { | |||||
| long timestamp = System.currentTimeMillis() / 1000; | long timestamp = System.currentTimeMillis() / 1000; | ||||
| String noncestr = getRandomStr(); | |||||
| String noncestr = RandomUtils.getRandomStr(); | |||||
| String jsapiTicket = getJsapiTicket(false); | String jsapiTicket = getJsapiTicket(false); | ||||
| try { | try { | ||||
| String signature = SHA1.genWithAmple( | String signature = SHA1.genWithAmple( | ||||
| @@ -159,7 +158,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| "timestamp=" + timestamp, | "timestamp=" + timestamp, | ||||
| "url=" + url | "url=" + url | ||||
| ); | ); | ||||
| WxMpJsapiSignature jsapiSignature = new WxMpJsapiSignature(); | |||||
| WxJsapiSignature jsapiSignature = new WxJsapiSignature(); | |||||
| jsapiSignature.setTimestamp(timestamp); | jsapiSignature.setTimestamp(timestamp); | ||||
| jsapiSignature.setNoncestr(noncestr); | jsapiSignature.setNoncestr(noncestr); | ||||
| jsapiSignature.setUrl(url); | jsapiSignature.setUrl(url); | ||||
| @@ -170,14 +169,6 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| } | } | ||||
| } | } | ||||
| protected String getRandomStr() { | |||||
| StringBuilder sb = new StringBuilder(); | |||||
| for (int i = 0; i < 16; i++) { | |||||
| sb.append(RANDOM_STR.charAt(RANDOM.nextInt(RANDOM_STR.length()))); | |||||
| } | |||||
| return sb.toString(); | |||||
| } | |||||
| public void customMessageSend(WxMpCustomMessage message) throws WxErrorException { | public void customMessageSend(WxMpCustomMessage message) throws WxErrorException { | ||||
| String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | ||||
| execute(new SimplePostRequestExecutor(), url, message.toJson()); | execute(new SimplePostRequestExecutor(), url, message.toJson()); | ||||
| @@ -446,7 +437,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| @Override | @Override | ||||
| public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken) { | public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken) { | ||||
| String url = "https://api.weixin.qq.com/sns/auth?"; | String url = "https://api.weixin.qq.com/sns/auth?"; | ||||
| url += "access_token=" + oAuth2AccessToken; | |||||
| url += "access_token=" + oAuth2AccessToken.getAccessToken(); | |||||
| url += "&openid=" + oAuth2AccessToken.getOpenId(); | url += "&openid=" + oAuth2AccessToken.getOpenId(); | ||||
| try { | try { | ||||
| @@ -462,6 +453,42 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| return true; | 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 = Streams.parse(new JsonReader(new StringReader(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 List<WxMpUserSummary> getUserSummary(Date beginDate, Date endDate) throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/datacube/getusersummary"; | |||||
| JsonObject param = new JsonObject(); | |||||
| param.addProperty("begin_date", SIMPLE_DATE_FORMAT.format(beginDate)); | |||||
| param.addProperty("end_date", SIMPLE_DATE_FORMAT.format(endDate)); | |||||
| String responseContent = post(url, param.toString()); | |||||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), new TypeToken<List<WxMpUserSummary>>(){}.getType()); | |||||
| } | |||||
| @Override | |||||
| public List<WxMpUserCumulate> getUserCumulate(Date beginDate, Date endDate) throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/datacube/getusercumulate"; | |||||
| JsonObject param = new JsonObject(); | |||||
| param.addProperty("begin_date", SIMPLE_DATE_FORMAT.format(beginDate)); | |||||
| param.addProperty("end_date", SIMPLE_DATE_FORMAT.format(endDate)); | |||||
| String responseContent = post(url, param.toString()); | |||||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), new TypeToken<List<WxMpUserCumulate>>(){}.getType()); | |||||
| } | |||||
| public String get(String url, String queryParam) throws WxErrorException { | public String get(String url, String queryParam) throws WxErrorException { | ||||
| return execute(new SimpleGetRequestExecutor(), url, queryParam); | return execute(new SimpleGetRequestExecutor(), url, queryParam); | ||||
| } | } | ||||
| @@ -3,6 +3,7 @@ package me.chanjar.weixin.mp.bean; | |||||
| import me.chanjar.weixin.mp.bean.custombuilder.*; | import me.chanjar.weixin.mp.bean.custombuilder.*; | ||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -11,7 +12,7 @@ import java.util.List; | |||||
| * @author chanjarster | * @author chanjarster | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxMpCustomMessage { | |||||
| public class WxMpCustomMessage implements Serializable { | |||||
| private String toUser; | private String toUser; | ||||
| private String msgType; | private String msgType; | ||||
| @@ -2,12 +2,14 @@ package me.chanjar.weixin.mp.bean; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 微信用户分组 | * 微信用户分组 | ||||
| * @author chanjarster | * @author chanjarster | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxMpGroup { | |||||
| public class WxMpGroup implements Serializable { | |||||
| private long id = -1; | private long id = -1; | ||||
| private String name; | private String name; | ||||
| @@ -2,12 +2,14 @@ package me.chanjar.weixin.mp.bean; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 分组群发的消息 | * 分组群发的消息 | ||||
| * | * | ||||
| * @author chanjarster | * @author chanjarster | ||||
| */ | */ | ||||
| public class WxMpMassGroupMessage { | |||||
| public class WxMpMassGroupMessage implements Serializable { | |||||
| private Long groupId; | private Long groupId; | ||||
| private String msgtype; | private String msgtype; | ||||
| @@ -2,6 +2,7 @@ package me.chanjar.weixin.mp.bean; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -10,7 +11,7 @@ import java.util.List; | |||||
| * @author chanjarster | * @author chanjarster | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxMpMassNews { | |||||
| public class WxMpMassNews implements Serializable { | |||||
| private List<WxMpMassNewsArticle> articles = new ArrayList<WxMpMassNewsArticle>(); | private List<WxMpMassNewsArticle> articles = new ArrayList<WxMpMassNewsArticle>(); | ||||
| @@ -2,6 +2,7 @@ package me.chanjar.weixin.mp.bean; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -10,7 +11,7 @@ import java.util.List; | |||||
| * | * | ||||
| * @author chanjarster | * @author chanjarster | ||||
| */ | */ | ||||
| public class WxMpMassOpenIdsMessage { | |||||
| public class WxMpMassOpenIdsMessage implements Serializable { | |||||
| private List<String> toUsers = new ArrayList<String>(); | private List<String> toUsers = new ArrayList<String>(); | ||||
| private String msgType; | private String msgType; | ||||
| @@ -2,12 +2,14 @@ package me.chanjar.weixin.mp.bean; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 群发时用到的视频素材 | * 群发时用到的视频素材 | ||||
| * | * | ||||
| * @author chanjarster | * @author chanjarster | ||||
| */ | */ | ||||
| public class WxMpMassVideo { | |||||
| public class WxMpMassVideo implements Serializable { | |||||
| private String mediaId; | private String mediaId; | ||||
| private String title; | private String title; | ||||
| @@ -2,6 +2,8 @@ package me.chanjar.weixin.mp.bean; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 语义理解查询用对象 | * 语义理解查询用对象 | ||||
| * | * | ||||
| @@ -9,7 +11,7 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| * | * | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public class WxMpSemanticQuery { | |||||
| public class WxMpSemanticQuery implements Serializable { | |||||
| private String query; | private String query; | ||||
| private String category; | private String category; | ||||
| @@ -1,9 +1,11 @@ | |||||
| package me.chanjar.weixin.mp.bean; | package me.chanjar.weixin.mp.bean; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public class WxMpTemplateData { | |||||
| public class WxMpTemplateData implements Serializable { | |||||
| private String name; | private String name; | ||||
| private String value; | private String value; | ||||
| @@ -2,10 +2,11 @@ package me.chanjar.weixin.mp.bean; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| public class WxMpTemplateMessage { | |||||
| public class WxMpTemplateMessage implements Serializable { | |||||
| private String toUser; | private String toUser; | ||||
| private String templateId; | private String templateId; | ||||
| @@ -10,6 +10,7 @@ import org.apache.commons.io.IOUtils; | |||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.io.Serializable; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.List; | import java.util.List; | ||||
| @@ -25,7 +26,7 @@ import java.util.List; | |||||
| * @author chanjarster | * @author chanjarster | ||||
| */ | */ | ||||
| @XStreamAlias("xml") | @XStreamAlias("xml") | ||||
| public class WxMpXmlMessage { | |||||
| public class WxMpXmlMessage implements Serializable { | |||||
| /////////////////////// | /////////////////////// | ||||
| // 以下都是微信推送过来的消息的xml的element所对应的属性 | // 以下都是微信推送过来的消息的xml的element所对应的属性 | ||||
| @@ -8,8 +8,10 @@ import me.chanjar.weixin.mp.bean.outxmlbuilder.*; | |||||
| import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; | import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; | ||||
| import me.chanjar.weixin.mp.util.xml.XStreamTransformer; | import me.chanjar.weixin.mp.util.xml.XStreamTransformer; | ||||
| import java.io.Serializable; | |||||
| @XStreamAlias("xml") | @XStreamAlias("xml") | ||||
| public abstract class WxMpXmlOutMessage { | |||||
| public abstract class WxMpXmlOutMessage implements Serializable { | |||||
| @XStreamAlias("ToUserName") | @XStreamAlias("ToUserName") | ||||
| @XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
| @@ -2,6 +2,8 @@ package me.chanjar.weixin.mp.bean.result; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 群发消息一发送就返回的结果 | * 群发消息一发送就返回的结果 | ||||
| @@ -13,7 +15,7 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| * @author chanjarster | * @author chanjarster | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxMpMassSendResult { | |||||
| public class WxMpMassSendResult implements Serializable { | |||||
| private String errorCode; | private String errorCode; | ||||
| private String errorMsg; | private String errorMsg; | ||||
| @@ -2,6 +2,8 @@ package me.chanjar.weixin.mp.bean.result; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| * 上传群发用的素材的结果 | * 上传群发用的素材的结果 | ||||
| @@ -10,7 +12,7 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| * @author chanjarster | * @author chanjarster | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxMpMassUploadResult { | |||||
| public class WxMpMassUploadResult implements Serializable { | |||||
| private String type; | private String type; | ||||
| private String mediaId; | private String mediaId; | ||||
| @@ -2,7 +2,9 @@ package me.chanjar.weixin.mp.bean.result; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| public class WxMpOAuth2AccessToken { | |||||
| import java.io.Serializable; | |||||
| public class WxMpOAuth2AccessToken implements Serializable { | |||||
| private String accessToken; | private String accessToken; | ||||
| @@ -2,12 +2,14 @@ package me.chanjar.weixin.mp.bean.result; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 换取二维码的Ticket | * 换取二维码的Ticket | ||||
| * | * | ||||
| * @author chanjarster | * @author chanjarster | ||||
| */ | */ | ||||
| public class WxMpQrCodeTicket { | |||||
| public class WxMpQrCodeTicket implements Serializable { | |||||
| protected String ticket; | protected String ticket; | ||||
| protected int expire_seconds = -1; | protected int expire_seconds = -1; | ||||
| @@ -2,6 +2,8 @@ package me.chanjar.weixin.mp.bean.result; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 语义理解查询结果对象 | * 语义理解查询结果对象 | ||||
| * | * | ||||
| @@ -9,7 +11,7 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| * | * | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public class WxMpSemanticQueryResult { | |||||
| public class WxMpSemanticQueryResult implements Serializable { | |||||
| private String query; | private String query; | ||||
| private String type; | private String type; | ||||
| @@ -2,12 +2,14 @@ package me.chanjar.weixin.mp.bean.result; | |||||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
| import java.io.Serializable; | |||||
| /** | /** | ||||
| * 微信用户信息 | * 微信用户信息 | ||||
| * @author chanjarster | * @author chanjarster | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxMpUser { | |||||
| public class WxMpUser implements Serializable { | |||||
| protected Boolean subscribe; | protected Boolean subscribe; | ||||
| protected String openId; | protected String openId; | ||||
| @@ -20,7 +22,11 @@ public class WxMpUser { | |||||
| protected String headImgUrl; | protected String headImgUrl; | ||||
| protected Long subscribeTime; | protected Long subscribeTime; | ||||
| protected String unionId; | protected String unionId; | ||||
| protected Integer sexId; | |||||
| public Boolean getSubscribe() { | |||||
| return subscribe; | |||||
| } | |||||
| public Boolean isSubscribe() { | public Boolean isSubscribe() { | ||||
| return subscribe; | return subscribe; | ||||
| } | } | ||||
| @@ -87,7 +93,16 @@ public class WxMpUser { | |||||
| public void setUnionId(String unionId) { | public void setUnionId(String unionId) { | ||||
| this.unionId = unionId; | this.unionId = unionId; | ||||
| } | } | ||||
| public Integer getSexId() { | |||||
| return sexId; | |||||
| } | |||||
| public void setSexId(Integer sexId) { | |||||
| this.sexId = sexId; | |||||
| } | |||||
| public static WxMpUser fromJson(String json) { | public static WxMpUser fromJson(String json) { | ||||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUser.class); | return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUser.class); | ||||
| } | } | ||||
| @@ -0,0 +1,41 @@ | |||||
| package me.chanjar.weixin.mp.bean.result; | |||||
| import java.io.Serializable; | |||||
| import java.util.Date; | |||||
| /** | |||||
| * <pre> | |||||
| * 累计用户数据接口的返回JSON数据包 | |||||
| * http://mp.weixin.qq.com/wiki/3/ecfed6e1a0a03b5f35e5efac98e864b7.html | |||||
| * </pre> | |||||
| */ | |||||
| public class WxMpUserCumulate implements Serializable { | |||||
| private Date refDate; | |||||
| private Integer cumulateUser; | |||||
| public Date getRefDate() { | |||||
| return refDate; | |||||
| } | |||||
| public void setRefDate(Date refDate) { | |||||
| this.refDate = refDate; | |||||
| } | |||||
| public Integer getCumulateUser() { | |||||
| return cumulateUser; | |||||
| } | |||||
| public void setCumulateUser(Integer cumulateUser) { | |||||
| this.cumulateUser = cumulateUser; | |||||
| } | |||||
| @Override | |||||
| public String toString() { | |||||
| return "WxMpUserCumulate{" + | |||||
| "refDate=" + refDate + | |||||
| ", cumulateUser=" + cumulateUser + | |||||
| '}'; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,63 @@ | |||||
| package me.chanjar.weixin.mp.bean.result; | |||||
| import java.io.Serializable; | |||||
| import java.util.Date; | |||||
| /** | |||||
| * <pre> | |||||
| * 用户增减数据接口的返回JSON数据包 | |||||
| * http://mp.weixin.qq.com/wiki/3/ecfed6e1a0a03b5f35e5efac98e864b7.html | |||||
| * </pre> | |||||
| */ | |||||
| public class WxMpUserSummary implements Serializable { | |||||
| private Date refDate; | |||||
| private Integer userSource; | |||||
| private Integer newUser; | |||||
| private Integer cancelUser; | |||||
| public Date getRefDate() { | |||||
| return refDate; | |||||
| } | |||||
| public void setRefDate(Date refDate) { | |||||
| this.refDate = refDate; | |||||
| } | |||||
| public Integer getUserSource() { | |||||
| return userSource; | |||||
| } | |||||
| public void setUserSource(Integer userSource) { | |||||
| this.userSource = userSource; | |||||
| } | |||||
| public Integer getNewUser() { | |||||
| return newUser; | |||||
| } | |||||
| public void setNewUser(Integer newUser) { | |||||
| this.newUser = newUser; | |||||
| } | |||||
| public Integer getCancelUser() { | |||||
| return cancelUser; | |||||
| } | |||||
| public void setCancelUser(Integer cancelUser) { | |||||
| this.cancelUser = cancelUser; | |||||
| } | |||||
| @Override | |||||
| public String toString() { | |||||
| return "WxMpUserSummary{" + | |||||
| "refDate=" + refDate + | |||||
| ", userSource=" + userSource + | |||||
| ", newUser=" + newUser + | |||||
| ", cancelUser=" + cancelUser + | |||||
| '}'; | |||||
| } | |||||
| } | |||||
| @@ -16,7 +16,7 @@ public class WxMpGsonBuilder { | |||||
| INSTANCE.registerTypeAdapter(WxMpMassGroupMessage.class, new WxMpMassGroupMessageGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpMassGroupMessage.class, new WxMpMassGroupMessageGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpMassOpenIdsMessage.class, new WxMpMassOpenIdsMessageGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpMassOpenIdsMessage.class, new WxMpMassOpenIdsMessageGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpGroup.class, new WxMpGroupGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpGroup.class, new WxMpGroupGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpUser.class, new WxUserGsonAdapter()); | |||||
| INSTANCE.registerTypeAdapter(WxMpUser.class, new WxMpUserGsonAdapter()); | |||||
| INSTANCE.registerTypeAdapter(WxMpUserList.class, new WxUserListGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpUserList.class, new WxUserListGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpMassVideo.class, new WxMpMassVideoAdapter()); | INSTANCE.registerTypeAdapter(WxMpMassVideo.class, new WxMpMassVideoAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpMassSendResult.class, new WxMpMassSendResultAdapter()); | INSTANCE.registerTypeAdapter(WxMpMassSendResult.class, new WxMpMassSendResultAdapter()); | ||||
| @@ -25,6 +25,8 @@ public class WxMpGsonBuilder { | |||||
| INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpTemplateMessage.class, new WxMpTemplateMessageGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter()); | INSTANCE.registerTypeAdapter(WxMpSemanticQueryResult.class, new WxMpSemanticQueryResultAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpOAuth2AccessToken.class, new WxMpOAuth2AccessTokenAdapter()); | INSTANCE.registerTypeAdapter(WxMpOAuth2AccessToken.class, new WxMpOAuth2AccessTokenAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpUserSummary.class, new WxMpUserSummaryGsonAdapter()); | |||||
| INSTANCE.registerTypeAdapter(WxMpUserCumulate.class, new WxMpUserCumulateGsonAdapter()); | |||||
| } | } | ||||
| public static Gson create() { | public static Gson create() { | ||||
| @@ -0,0 +1,47 @@ | |||||
| /* | |||||
| * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | |||||
| * | |||||
| * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | |||||
| * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | |||||
| * arose from modification of the original source, or other redistribution of this source | |||||
| * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | |||||
| */ | |||||
| package me.chanjar.weixin.mp.util.json; | |||||
| import com.google.gson.*; | |||||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpUserCumulate; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpUserSummary; | |||||
| import java.lang.reflect.Type; | |||||
| import java.text.ParseException; | |||||
| import java.text.SimpleDateFormat; | |||||
| /** | |||||
| * | |||||
| * @author Daniel Qian | |||||
| * | |||||
| */ | |||||
| public class WxMpUserCumulateGsonAdapter implements JsonDeserializer<WxMpUserCumulate> { | |||||
| private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); | |||||
| public WxMpUserCumulate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | |||||
| WxMpUserCumulate cumulate = new WxMpUserCumulate(); | |||||
| JsonObject summaryJsonObject = json.getAsJsonObject(); | |||||
| try { | |||||
| String refDate = GsonHelper.getString(summaryJsonObject, "ref_date"); | |||||
| if (refDate != null) { | |||||
| cumulate.setRefDate(SIMPLE_DATE_FORMAT.parse(refDate)); | |||||
| } | |||||
| cumulate.setCumulateUser(GsonHelper.getInteger(summaryJsonObject, "cumulate_user")); | |||||
| } catch (ParseException e) { | |||||
| throw new JsonParseException(e); | |||||
| } | |||||
| return cumulate; | |||||
| } | |||||
| } | |||||
| @@ -14,7 +14,7 @@ import me.chanjar.weixin.mp.bean.result.WxMpUser; | |||||
| import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
| public class WxUserGsonAdapter implements JsonDeserializer<WxMpUser> { | |||||
| public class WxMpUserGsonAdapter implements JsonDeserializer<WxMpUser> { | |||||
| public WxMpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | public WxMpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | ||||
| JsonObject o = json.getAsJsonObject(); | JsonObject o = json.getAsJsonObject(); | ||||
| @@ -29,10 +29,11 @@ public class WxUserGsonAdapter implements JsonDeserializer<WxMpUser> { | |||||
| wxMpUser.setProvince(GsonHelper.getString(o, "province")); | wxMpUser.setProvince(GsonHelper.getString(o, "province")); | ||||
| wxMpUser.setSubscribeTime(GsonHelper.getLong(o, "subscribe_time")); | wxMpUser.setSubscribeTime(GsonHelper.getLong(o, "subscribe_time")); | ||||
| wxMpUser.setUnionId(GsonHelper.getString(o, "unionid")); | wxMpUser.setUnionId(GsonHelper.getString(o, "unionid")); | ||||
| Integer sex = GsonHelper.getInteger(o, "sex"); | |||||
| if(new Integer(1).equals(sex)) { | |||||
| Integer sexId = GsonHelper.getInteger(o, "sex"); | |||||
| wxMpUser.setSexId(sexId); | |||||
| if(new Integer(1).equals(sexId)) { | |||||
| wxMpUser.setSex("男"); | wxMpUser.setSex("男"); | ||||
| } else if (new Integer(2).equals(sex)) { | |||||
| } else if (new Integer(2).equals(sexId)) { | |||||
| wxMpUser.setSex("女"); | wxMpUser.setSex("女"); | ||||
| } else { | } else { | ||||
| wxMpUser.setSex("未知"); | wxMpUser.setSex("未知"); | ||||
| @@ -0,0 +1,47 @@ | |||||
| /* | |||||
| * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | |||||
| * | |||||
| * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | |||||
| * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | |||||
| * arose from modification of the original source, or other redistribution of this source | |||||
| * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | |||||
| */ | |||||
| package me.chanjar.weixin.mp.util.json; | |||||
| import com.google.gson.*; | |||||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpUserSummary; | |||||
| import java.lang.reflect.Type; | |||||
| import java.text.ParseException; | |||||
| import java.text.SimpleDateFormat; | |||||
| /** | |||||
| * @author Daniel Qian | |||||
| */ | |||||
| public class WxMpUserSummaryGsonAdapter implements JsonDeserializer<WxMpUserSummary> { | |||||
| private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); | |||||
| public WxMpUserSummary deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) | |||||
| throws JsonParseException { | |||||
| WxMpUserSummary summary = new WxMpUserSummary(); | |||||
| JsonObject summaryJsonObject = json.getAsJsonObject(); | |||||
| try { | |||||
| String refDate = GsonHelper.getString(summaryJsonObject, "ref_date"); | |||||
| if (refDate != null) { | |||||
| summary.setRefDate(SIMPLE_DATE_FORMAT.parse(refDate)); | |||||
| } | |||||
| summary.setUserSource(GsonHelper.getInteger(summaryJsonObject, "user_source")); | |||||
| summary.setNewUser(GsonHelper.getInteger(summaryJsonObject, "new_user")); | |||||
| summary.setCancelUser(GsonHelper.getInteger(summaryJsonObject, "cancel_user")); | |||||
| } catch (ParseException e) { | |||||
| throw new JsonParseException(e); | |||||
| } | |||||
| return summary; | |||||
| } | |||||
| } | |||||
| @@ -22,9 +22,11 @@ public class WxUserListGsonAdapter implements JsonDeserializer<WxMpUserList> { | |||||
| wxMpUserList.setTotal(GsonHelper.getInteger(o, "total")); | wxMpUserList.setTotal(GsonHelper.getInteger(o, "total")); | ||||
| wxMpUserList.setCount(GsonHelper.getInteger(o, "count")); | wxMpUserList.setCount(GsonHelper.getInteger(o, "count")); | ||||
| wxMpUserList.setNextOpenId(GsonHelper.getString(o, "next_openid")); | wxMpUserList.setNextOpenId(GsonHelper.getString(o, "next_openid")); | ||||
| JsonArray data = o.get("data").getAsJsonObject().get("openid").getAsJsonArray(); | |||||
| for (int i = 0; i < data.size(); i++) { | |||||
| wxMpUserList.getOpenIds().add(GsonHelper.getAsString(data.get(i))); | |||||
| if (!o.get("data").isJsonNull() && !o.get("data").getAsJsonObject().get("openid").isJsonNull()) { | |||||
| JsonArray data = o.get("data").getAsJsonObject().get("openid").getAsJsonArray(); | |||||
| for (int i = 0; i < data.size(); i++) { | |||||
| wxMpUserList.getOpenIds().add(GsonHelper.getAsString(data.get(i))); | |||||
| } | |||||
| } | } | ||||
| return wxMpUserList; | return wxMpUserList; | ||||
| } | } | ||||
| @@ -0,0 +1,69 @@ | |||||
| package me.chanjar.weixin.mp.api; | |||||
| import com.google.inject.Inject; | |||||
| import me.chanjar.weixin.common.api.WxConsts; | |||||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||||
| import me.chanjar.weixin.common.session.WxSession; | |||||
| import me.chanjar.weixin.mp.bean.WxMpMassGroupMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpMassNews; | |||||
| import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage; | |||||
| import me.chanjar.weixin.mp.bean.WxMpMassVideo; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpUserCumulate; | |||||
| import me.chanjar.weixin.mp.bean.result.WxMpUserSummary; | |||||
| import org.testng.Assert; | |||||
| import org.testng.annotations.DataProvider; | |||||
| import org.testng.annotations.Guice; | |||||
| import org.testng.annotations.Test; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.text.ParseException; | |||||
| import java.text.SimpleDateFormat; | |||||
| import java.util.Arrays; | |||||
| import java.util.Date; | |||||
| import java.util.List; | |||||
| /** | |||||
| * | |||||
| * @author chanjarster | |||||
| * | |||||
| */ | |||||
| @Test(groups = "miscAPI", dependsOnGroups = { "baseAPI"}) | |||||
| @Guice(modules = ApiTestModule.class) | |||||
| public class WxMpMiscAPITest { | |||||
| @Inject | |||||
| protected WxMpServiceImpl wxService; | |||||
| @Test | |||||
| public void testGetCallbackIP() throws WxErrorException { | |||||
| String[] ipArray = wxService.getCallbackIP(); | |||||
| System.out.println(Arrays.toString(ipArray)); | |||||
| Assert.assertNotNull(ipArray); | |||||
| Assert.assertNotEquals(ipArray.length, 0); | |||||
| } | |||||
| @Test | |||||
| public void testGetUserSummary() throws WxErrorException, ParseException { | |||||
| SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); | |||||
| Date beginDate = simpleDateFormat.parse("2015-01-01"); | |||||
| Date endDate = simpleDateFormat.parse("2015-01-02"); | |||||
| List<WxMpUserSummary> summaries = wxService.getUserSummary(beginDate, endDate); | |||||
| System.out.println(summaries); | |||||
| Assert.assertNotNull(summaries); | |||||
| } | |||||
| @Test | |||||
| public void testGetUserCumulate() throws WxErrorException, ParseException { | |||||
| SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); | |||||
| Date beginDate = simpleDateFormat.parse("2015-01-01"); | |||||
| Date endDate = simpleDateFormat.parse("2015-01-02"); | |||||
| List<WxMpUserCumulate> cumulates = wxService.getUserCumulate(beginDate, endDate); | |||||
| System.out.println(cumulates); | |||||
| Assert.assertNotNull(cumulates); | |||||
| } | |||||
| } | |||||
| @@ -15,6 +15,7 @@ | |||||
| <class name="me.chanjar.weixin.mp.api.WxMpShortUrlAPITest" /> | <class name="me.chanjar.weixin.mp.api.WxMpShortUrlAPITest" /> | ||||
| <class name="me.chanjar.weixin.mp.api.WxMpMessageRouterTest" /> | <class name="me.chanjar.weixin.mp.api.WxMpMessageRouterTest" /> | ||||
| <class name="me.chanjar.weixin.mp.api.WxMpJsAPITest" /> | <class name="me.chanjar.weixin.mp.api.WxMpJsAPITest" /> | ||||
| <class name="me.chanjar.weixin.mp.api.WxMpMiscAPITest" /> | |||||
| </classes> | </classes> | ||||
| </test> | </test> | ||||