@@ -0,0 +1,169 @@ | |||
package me.chanjar.weixin.cp.api; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||
import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||
import me.chanjar.weixin.cp.bean.WxCpTpCorp; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
/** | |||
* 微信第三方应用API的Service | |||
* @author zhenjun cai | |||
*/ | |||
public interface WxCpTpService { | |||
String JSCODE_TO_SESSION_URL = "https://qyapi.weixin.qq.com/cgi-bin/service/miniprogram/jscode2session"; | |||
String GET_CORP_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token"; | |||
String GET_PERMANENT_CODE = "https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code"; | |||
/** | |||
* <pre> | |||
* 验证推送过来的消息的正确性 | |||
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90139/90968/消息体签名校验 | |||
* </pre> | |||
* | |||
* @param msgSignature 消息签名 | |||
* @param timestamp 时间戳 | |||
* @param nonce 随机数 | |||
* @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息 | |||
*/ | |||
boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); | |||
/** | |||
* 获取suite_access_token, 不强制刷新suite_access_token | |||
* | |||
* @see #getSuiteAccessToken(boolean) | |||
*/ | |||
String getSuiteAccessToken() throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 获取suite_access_token,本方法线程安全 | |||
* 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限 | |||
* 另:本service的所有方法都会在suite_access_token过期是调用此方法 | |||
* 程序员在非必要情况下尽量不要主动调用此方法 | |||
* 详情请见: https://work.weixin.qq.com/api/doc#90001/90143/90600 | |||
* </pre> | |||
* | |||
* @param forceRefresh 强制刷新 | |||
*/ | |||
String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException; | |||
/** | |||
* 获得suite_ticket,不强制刷新suite_ticket | |||
* | |||
* @see #getSuiteTicket(boolean) | |||
*/ | |||
String getSuiteTicket() throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 获得suite_ticket | |||
* 由于suite_ticket是微信服务器定时推送(每10分钟),不能主动获取,如果碰到过期只能抛异常 | |||
* | |||
* 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628 | |||
* </pre> | |||
* | |||
* @param forceRefresh 强制刷新 | |||
*/ | |||
String getSuiteTicket(boolean forceRefresh) throws WxErrorException; | |||
/** | |||
* 小程序登录凭证校验 | |||
* | |||
* @param jsCode 登录时获取的 code | |||
*/ | |||
WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException; | |||
/** | |||
* 获取企业凭证 | |||
* @param authCorpid 授权方corpid | |||
* @param permanentCode 永久授权码,通过get_permanent_code获取 | |||
*/ | |||
WxAccessToken getCorpToken(String authCorpid, String permanentCode) throws WxErrorException; | |||
/** | |||
* 获取企业永久授权码 | |||
* @param authCode | |||
* @return | |||
*/ | |||
WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException; | |||
/** | |||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | |||
* | |||
* @param url 接口地址 | |||
* @param queryParam 请求参数 | |||
*/ | |||
String get(String url, String queryParam) throws WxErrorException; | |||
/** | |||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 | |||
* | |||
* @param url 接口地址 | |||
* @param postData 请求body字符串 | |||
*/ | |||
String post(String url, String postData) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* Service没有实现某个API的时候,可以用这个, | |||
* 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。 | |||
* 可以参考,{@link MediaUploadRequestExecutor}的实现方法 | |||
* </pre> | |||
* | |||
* @param executor 执行器 | |||
* @param uri 请求地址 | |||
* @param data 参数 | |||
* @param <T> 请求值类型 | |||
* @param <E> 返回值类型 | |||
*/ | |||
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException; | |||
/** | |||
* <pre> | |||
* 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试 | |||
* 默认:1000ms | |||
* </pre> | |||
* | |||
* @param retrySleepMillis 重试休息时间 | |||
*/ | |||
void setRetrySleepMillis(int retrySleepMillis); | |||
/** | |||
* <pre> | |||
* 设置当微信系统响应系统繁忙时,最大重试次数 | |||
* 默认:5次 | |||
* </pre> | |||
* | |||
* @param maxRetryTimes 最大重试次数 | |||
*/ | |||
void setMaxRetryTimes(int maxRetryTimes); | |||
/** | |||
* 初始化http请求对象 | |||
*/ | |||
void initHttp(); | |||
/** | |||
* 获取WxMpConfigStorage 对象 | |||
* | |||
* @return WxMpConfigStorage | |||
*/ | |||
WxCpTpConfigStorage getWxCpTpConfigStorage(); | |||
/** | |||
* 注入 {@link WxCpTpConfigStorage} 的实现 | |||
* | |||
* @param wxConfigProvider 配置对象 | |||
*/ | |||
void setWxCpTpConfigStorage(WxCpTpConfigStorage wxConfigProvider); | |||
/** | |||
* http请求对象 | |||
*/ | |||
RequestHttp<?, ?> getRequestHttp(); | |||
} |
@@ -0,0 +1,244 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.google.common.base.Joiner; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import me.chanjar.weixin.common.WxType; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.common.util.DataUtils; | |||
import me.chanjar.weixin.common.util.crypto.SHA1; | |||
import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
import me.chanjar.weixin.common.util.http.RequestHttp; | |||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
import me.chanjar.weixin.cp.api.WxCpService; | |||
import me.chanjar.weixin.cp.api.WxCpTpService; | |||
import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||
import me.chanjar.weixin.cp.bean.WxCpTpCorp; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
/** | |||
* @author zhenjun cai | |||
*/ | |||
public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, RequestHttp<H, P> { | |||
protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
/** | |||
* 全局的是否正在刷新access token的锁 | |||
*/ | |||
protected final Object globalSuiteAccessTokenRefreshLock = new Object(); | |||
/** | |||
* 全局的是否正在刷新jsapi_ticket的锁 | |||
*/ | |||
protected final Object globalSuiteTicketRefreshLock = new Object(); | |||
protected WxCpTpConfigStorage configStorage; | |||
/** | |||
* 临时文件目录 | |||
*/ | |||
private 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) { | |||
this.log.error("Checking signature failed, and the reason is :" + e.getMessage()); | |||
return false; | |||
} | |||
} | |||
@Override | |||
public String getSuiteAccessToken() throws WxErrorException { | |||
return getSuiteAccessToken(false); | |||
} | |||
@Override | |||
public String getSuiteTicket() throws WxErrorException { | |||
return getSuiteTicket(false); | |||
} | |||
@Override | |||
public String getSuiteTicket(boolean forceRefresh) throws WxErrorException { | |||
// suite ticket由微信服务器推送,不能强制刷新 | |||
// if (forceRefresh) { | |||
// this.configStorage.expireSuiteTicket(); | |||
// } | |||
if (this.configStorage.isSuiteTicketExpired()) { | |||
// 本地suite ticket 不存在或者过期 | |||
WxError wxError = WxError.fromJson("{\"errcode\":40085, \"errmsg\":\"invaild suite ticket\"}", WxType.CP); | |||
throw new WxErrorException(wxError); | |||
} | |||
return this.configStorage.getSuiteTicket(); | |||
} | |||
@Override | |||
public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException { | |||
Map<String, String> params = new HashMap<>(2); | |||
params.put("js_code", jsCode); | |||
params.put("grant_type", "authorization_code"); | |||
String result = this.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | |||
return WxCpMaJsCode2SessionResult.fromJson(result); | |||
} | |||
@Override | |||
public WxAccessToken getCorpToken(String authCorpid, String permanentCode) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("auth_corpid", authCorpid); | |||
jsonObject.addProperty("permanent_code", permanentCode); | |||
String result = post(GET_CORP_TOKEN, jsonObject.toString()); | |||
return WxAccessToken.fromJson(result); | |||
} | |||
@Override | |||
public WxCpTpCorp getPermanentCode(String authCode) throws WxErrorException { | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("auth_code", authCode); | |||
String result = post(GET_PERMANENT_CODE, jsonObject.toString()); | |||
jsonObject = new JsonParser().parse(result).getAsJsonObject(); | |||
WxCpTpCorp wxCpTpCorp = WxCpTpCorp.fromJson(jsonObject.get("auth_corp_info").getAsString()); | |||
wxCpTpCorp.setPermanentCode(jsonObject.get("permanent_code").getAsString()); | |||
return wxCpTpCorp; | |||
} | |||
@Override | |||
public String get(String url, String queryParam) throws WxErrorException { | |||
return execute(SimpleGetRequestExecutor.create(this), url, queryParam); | |||
} | |||
@Override | |||
public String post(String url, String postData) throws WxErrorException { | |||
return execute(SimplePostRequestExecutor.create(this), url, postData); | |||
} | |||
/** | |||
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求. | |||
*/ | |||
@Override | |||
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||
int retryTimes = 0; | |||
do { | |||
try { | |||
return this.executeInternal(executor, uri, data); | |||
} 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) { | |||
Thread.currentThread().interrupt(); | |||
} | |||
} else { | |||
throw e; | |||
} | |||
} | |||
} while (retryTimes++ < this.maxRetryTimes); | |||
this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | |||
throw new RuntimeException("微信服务端异常,超出重试次数"); | |||
} | |||
protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||
E dataForLog = DataUtils.handleDataWithSecret(data); | |||
if (uri.contains("suite_access_token=")) { | |||
throw new IllegalArgumentException("uri参数中不允许有suite_access_token: " + uri); | |||
} | |||
String suiteAccessToken = getSuiteAccessToken(false); | |||
String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "suite_access_token=" + suiteAccessToken; | |||
try { | |||
T result = executor.execute(uriWithAccessToken, data); | |||
this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); | |||
return result; | |||
} catch (WxErrorException e) { | |||
WxError error = e.getError(); | |||
/* | |||
* 发生以下情况时尝试刷新suite_access_token | |||
* 42009 suite_access_token已过期 | |||
*/ | |||
if (error.getErrorCode() == 42009) { | |||
// 强制设置wxCpTpConfigStorage它的suite access token过期了,这样在下一次请求里就会刷新suite access token | |||
this.configStorage.expireSuiteAccessToken(); | |||
return execute(executor, uri, data); | |||
} | |||
if (error.getErrorCode() != 0) { | |||
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); | |||
throw new WxErrorException(error, e); | |||
} | |||
return null; | |||
} catch (IOException e) { | |||
this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
@Override | |||
public void setWxCpTpConfigStorage(WxCpTpConfigStorage wxConfigProvider) { | |||
this.configStorage = wxConfigProvider; | |||
this.initHttp(); | |||
} | |||
@Override | |||
public void setRetrySleepMillis(int retrySleepMillis) { | |||
this.retrySleepMillis = retrySleepMillis; | |||
} | |||
@Override | |||
public void setMaxRetryTimes(int maxRetryTimes) { | |||
this.maxRetryTimes = maxRetryTimes; | |||
} | |||
public File getTmpDirFile() { | |||
return this.tmpDirFile; | |||
} | |||
public void setTmpDirFile(File tmpDirFile) { | |||
this.tmpDirFile = tmpDirFile; | |||
} | |||
@Override | |||
public RequestHttp<?, ?> getRequestHttp() { | |||
return this; | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.error.WxErrorException; | |||
import me.chanjar.weixin.cp.api.WxCpTpService; | |||
/** | |||
* <pre> | |||
* 默认接口实现类,使用apache httpclient实现,配合第三方应用service使用 | |||
* Created by zhenjun cai. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class WxCpServiceOnTpImpl extends WxCpServiceApacheHttpClientImpl { | |||
//第三方应用service | |||
WxCpTpService wxCpTpService; | |||
public void setWxCpTpService(WxCpTpService wxCpTpService) { | |||
this.wxCpTpService = wxCpTpService; | |||
} | |||
@Override | |||
public String getAccessToken(boolean forceRefresh) throws WxErrorException { | |||
if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) { | |||
return this.configStorage.getAccessToken(); | |||
} | |||
//access token通过第三方应用service获取 | |||
//corpSecret对应企业永久授权码 | |||
WxAccessToken accessToken = wxCpTpService.getCorpToken(this.configStorage.getCorpId(), this.configStorage.getCorpSecret()); | |||
this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||
return this.configStorage.getAccessToken(); | |||
} | |||
} |
@@ -0,0 +1,114 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
import java.io.IOException; | |||
import org.apache.http.Consts; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.client.config.RequestConfig; | |||
import org.apache.http.client.methods.CloseableHttpResponse; | |||
import org.apache.http.client.methods.HttpPost; | |||
import org.apache.http.entity.StringEntity; | |||
import org.apache.http.impl.client.BasicResponseHandler; | |||
import org.apache.http.impl.client.CloseableHttpClient; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import me.chanjar.weixin.common.WxType; | |||
import me.chanjar.weixin.common.error.WxError; | |||
import me.chanjar.weixin.common.error.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.config.WxCpTpConfigStorage; | |||
public class WxCpTpServiceApacheHttpClientImpl extends BaseWxCpTpServiceImpl<CloseableHttpClient, HttpHost> { | |||
protected CloseableHttpClient httpClient; | |||
protected HttpHost httpProxy; | |||
@Override | |||
public CloseableHttpClient getRequestHttpClient() { | |||
return httpClient; | |||
} | |||
@Override | |||
public HttpHost getRequestHttpProxy() { | |||
return httpProxy; | |||
} | |||
@Override | |||
public HttpType getRequestType() { | |||
return HttpType.APACHE_HTTP; | |||
} | |||
@Override | |||
public String getSuiteAccessToken(boolean forceRefresh) throws WxErrorException { | |||
if (!this.configStorage.isSuiteAccessTokenExpired() && !forceRefresh) { | |||
return this.configStorage.getSuiteAccessToken(); | |||
} | |||
synchronized (this.globalSuiteAccessTokenRefreshLock) { | |||
String url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token"; | |||
try { | |||
HttpPost httpPost = new HttpPost(url); | |||
if (this.httpProxy != null) { | |||
RequestConfig config = RequestConfig.custom() | |||
.setProxy(this.httpProxy).build(); | |||
httpPost.setConfig(config); | |||
} | |||
JsonObject jsonObject = new JsonObject(); | |||
jsonObject.addProperty("suite_id", this.configStorage.getSuiteId()); | |||
jsonObject.addProperty("suite_secret", this.configStorage.getSuiteSecret()); | |||
jsonObject.addProperty("suite_ticket", this.getSuiteTicket()); | |||
StringEntity entity = new StringEntity(jsonObject.toString(), Consts.UTF_8); | |||
httpPost.setEntity(entity); | |||
String resultContent; | |||
try (CloseableHttpClient httpclient = getRequestHttpClient(); | |||
CloseableHttpResponse response = httpclient.execute(httpPost)) { | |||
resultContent = new BasicResponseHandler().handleResponse(response); | |||
} finally { | |||
httpPost.releaseConnection(); | |||
} | |||
WxError error = WxError.fromJson(resultContent, WxType.CP); | |||
if (error.getErrorCode() != 0) { | |||
throw new WxErrorException(error); | |||
} | |||
jsonObject = new JsonParser().parse(resultContent).getAsJsonObject(); | |||
String suiteAccussToken = jsonObject.get("suite_access_token").getAsString(); | |||
Integer expiresIn = jsonObject.get("expires_in").getAsInt(); | |||
this.configStorage.updateSuiteAccessToken(suiteAccussToken, expiresIn); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
return this.configStorage.getSuiteAccessToken(); | |||
} | |||
@Override | |||
public void initHttp() { | |||
ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage | |||
.getApacheHttpClientBuilder(); | |||
if (null == apacheHttpClientBuilder) { | |||
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | |||
} | |||
apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) | |||
.httpProxyPort(this.configStorage.getHttpProxyPort()) | |||
.httpProxyUsername(this.configStorage.getHttpProxyUsername()) | |||
.httpProxyPassword(this.configStorage.getHttpProxyPassword()); | |||
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { | |||
this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); | |||
} | |||
this.httpClient = apacheHttpClientBuilder.build(); | |||
} | |||
@Override | |||
public WxCpTpConfigStorage getWxCpTpConfigStorage() { | |||
return this.configStorage; | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
package me.chanjar.weixin.cp.api.impl; | |||
/** | |||
* <pre> | |||
* 默认接口实现类,使用apache httpclient实现 | |||
* Created by zhenjun cai. | |||
* </pre> | |||
* | |||
* @author <a href="https://github.com/binarywang">Binary Wang</a> | |||
*/ | |||
public class WxCpTpServiceImpl extends WxCpTpServiceApacheHttpClientImpl { | |||
} |
@@ -0,0 +1,42 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import com.google.gson.annotations.SerializedName; | |||
import lombok.Data; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
/** | |||
* 微信部门. | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
@Data | |||
public class WxCpTpCorp implements Serializable { | |||
private static final long serialVersionUID = -5028321625140879571L; | |||
@SerializedName("corpid") | |||
private String corpId; | |||
@SerializedName("corp_name") | |||
private String corpName; | |||
@SerializedName("corp_full_name") | |||
private String corpFullName; | |||
@SerializedName("corp_type") | |||
private String corpType; | |||
@SerializedName("corp_square_logo_url") | |||
private String corpSquareLogoUrl; | |||
@SerializedName("corp_user_max") | |||
private String corpUserMax; | |||
@SerializedName("permanent_code") | |||
private String permanentCode; | |||
public static WxCpTpCorp fromJson(String json) { | |||
return WxCpGsonBuilder.create().fromJson(json, WxCpTpCorp.class); | |||
} | |||
public String toJson() { | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import java.util.Map; | |||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
import lombok.Data; | |||
import lombok.extern.slf4j.Slf4j; | |||
import me.chanjar.weixin.common.util.XmlUtils; | |||
import me.chanjar.weixin.common.util.crypto.WxCryptUtil; | |||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.ImageBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.NewsBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.TextBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VideoBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VoiceBuilder; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | |||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | |||
/** | |||
* 回调推送的message | |||
* https://work.weixin.qq.com/api/doc#90001/90143/90612 | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
@XStreamAlias("xml") | |||
@Slf4j | |||
@Data | |||
public class WxCpTpXmlMessage implements Serializable { | |||
private static final long serialVersionUID = 6031833682211475786L; | |||
/** | |||
* 使用dom4j解析的存放所有xml属性和值的map. | |||
*/ | |||
private Map<String, Object> allFieldsMap; | |||
@XStreamAlias("SuiteId") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String suiteId; | |||
@XStreamAlias("InfoType") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String infoType; | |||
@XStreamAlias("TimeStamp") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String timeStamp; | |||
@XStreamAlias("SuiteTicket") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String suiteTicket; | |||
@XStreamAlias("AuthCode") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String authCode; | |||
public static WxCpTpXmlMessage fromXml(String xml) { | |||
//修改微信变态的消息内容格式,方便解析 | |||
//xml = xml.replace("</PicList><PicList>", ""); | |||
final WxCpTpXmlMessage xmlPackage = XStreamTransformer.fromXml(WxCpTpXmlMessage.class, xml); | |||
xmlPackage.setAllFieldsMap(XmlUtils.xml2Map(xml)); | |||
return xmlPackage; | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
package me.chanjar.weixin.cp.bean; | |||
import java.io.Serializable; | |||
import java.util.Map; | |||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
import lombok.Data; | |||
import me.chanjar.weixin.common.util.XmlUtils; | |||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.ImageBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.NewsBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.TextBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VideoBuilder; | |||
import me.chanjar.weixin.cp.bean.outxmlbuilder.VoiceBuilder; | |||
import me.chanjar.weixin.cp.config.WxCpConfigStorage; | |||
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; | |||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer; | |||
/** | |||
* 回调消息包. | |||
* https://work.weixin.qq.com/api/doc#90001/90143/91116 | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
@XStreamAlias("xml") | |||
@Data | |||
public class WxCpTpXmlPackage implements Serializable { | |||
private static final long serialVersionUID = 6031833682211475786L; | |||
/** | |||
* 使用dom4j解析的存放所有xml属性和值的map. | |||
*/ | |||
private Map<String, Object> allFieldsMap; | |||
@XStreamAlias("ToUserName") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String toUserName; | |||
@XStreamAlias("AgentID") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String agentId; | |||
@XStreamAlias("Encrypt") | |||
@XStreamConverter(value = XStreamCDataConverter.class) | |||
protected String msgEncrypt; | |||
public static WxCpTpXmlPackage fromXml(String xml) { | |||
//修改微信变态的消息内容格式,方便解析 | |||
//xml = xml.replace("</PicList><PicList>", ""); | |||
final WxCpTpXmlPackage xmlPackage = XStreamTransformer.fromXml(WxCpTpXmlPackage.class, xml); | |||
xmlPackage.setAllFieldsMap(XmlUtils.xml2Map(xml)); | |||
return xmlPackage; | |||
} | |||
} |
@@ -0,0 +1,72 @@ | |||
package me.chanjar.weixin.cp.config; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import java.io.File; | |||
/** | |||
* 微信客户端(第三方应用)配置存储 | |||
* | |||
* @author zhenjun cai | |||
*/ | |||
public interface WxCpTpConfigStorage { | |||
String getSuiteAccessToken(); | |||
boolean isSuiteAccessTokenExpired(); | |||
/** | |||
* 强制将suite access token过期掉 | |||
*/ | |||
void expireSuiteAccessToken(); | |||
void updateSuiteAccessToken(WxAccessToken suiteAccessToken); | |||
void updateSuiteAccessToken(String suiteAccessToken, int expiresIn); | |||
String getSuiteTicket(); | |||
boolean isSuiteTicketExpired(); | |||
/** | |||
* 强制将suite ticket过期掉 | |||
*/ | |||
void expireSuiteTicket(); | |||
/** | |||
* 应该是线程安全的 | |||
*/ | |||
void updateSuiteTicket(String suiteTicket, int expiresInSeconds); | |||
String getCorpId(); | |||
String getCorpSecret(); | |||
String getSuiteId(); | |||
String getSuiteSecret(); | |||
String getToken(); | |||
String getAesKey(); | |||
long getExpiresTime(); | |||
String getHttpProxyHost(); | |||
int getHttpProxyPort(); | |||
String getHttpProxyUsername(); | |||
String getHttpProxyPassword(); | |||
File getTmpDirFile(); | |||
/** | |||
* http client builder | |||
* | |||
* @return ApacheHttpClientBuilder | |||
*/ | |||
ApacheHttpClientBuilder getApacheHttpClientBuilder(); | |||
} |
@@ -0,0 +1,230 @@ | |||
package me.chanjar.weixin.cp.config; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | |||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
import java.io.File; | |||
/** | |||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class WxCpTpInMemoryConfigStorage implements WxCpTpConfigStorage { | |||
protected volatile String corpId; | |||
protected volatile String corpSecret; | |||
protected volatile String suiteId; | |||
protected volatile String suiteSecret; | |||
protected volatile String token; | |||
protected volatile String suiteAccessToken; | |||
protected volatile String aesKey; | |||
protected volatile long expiresTime; | |||
protected volatile String oauth2redirectUri; | |||
protected volatile String httpProxyHost; | |||
protected volatile int httpProxyPort; | |||
protected volatile String httpProxyUsername; | |||
protected volatile String httpProxyPassword; | |||
protected volatile String suiteTicket; | |||
protected volatile long suiteTicketExpiresTime; | |||
protected volatile File tmpDirFile; | |||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | |||
@Override | |||
public String getSuiteAccessToken() { | |||
return this.suiteAccessToken; | |||
} | |||
public void setSuiteAccessToken(String suiteAccessToken) { | |||
this.suiteAccessToken = suiteAccessToken; | |||
} | |||
@Override | |||
public boolean isSuiteAccessTokenExpired() { | |||
return System.currentTimeMillis() > this.expiresTime; | |||
} | |||
@Override | |||
public void expireSuiteAccessToken() { | |||
this.expiresTime = 0; | |||
} | |||
@Override | |||
public synchronized void updateSuiteAccessToken(WxAccessToken suiteAccessToken) { | |||
updateSuiteAccessToken(suiteAccessToken.getAccessToken(), suiteAccessToken.getExpiresIn()); | |||
} | |||
@Override | |||
public synchronized void updateSuiteAccessToken(String suiteAccessToken, int expiresInSeconds) { | |||
this.suiteAccessToken = suiteAccessToken; | |||
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||
} | |||
@Override | |||
public String getCorpId() { | |||
return this.corpId; | |||
} | |||
public void setCorpId(String corpId) { | |||
this.corpId = corpId; | |||
} | |||
@Override | |||
public String getCorpSecret() { | |||
return this.corpSecret; | |||
} | |||
public void setCorpSecret(String corpSecret) { | |||
this.corpSecret = corpSecret; | |||
} | |||
@Override | |||
public String getSuiteTicket() { | |||
return this.suiteTicket; | |||
} | |||
public void setSuiteTicket(String suiteTicket) { | |||
this.suiteTicket = suiteTicket; | |||
} | |||
public long getSuiteTicketExpiresTime() { | |||
return this.suiteTicketExpiresTime; | |||
} | |||
public void setSuiteTicketExpiresTime(long suiteTicketExpiresTime) { | |||
this.suiteTicketExpiresTime = suiteTicketExpiresTime; | |||
} | |||
@Override | |||
public boolean isSuiteTicketExpired() { | |||
return System.currentTimeMillis() > this.suiteTicketExpiresTime; | |||
} | |||
@Override | |||
public synchronized void updateSuiteTicket(String suiteTicket, int expiresInSeconds) { | |||
this.suiteTicket = suiteTicket; | |||
// 预留200秒的时间 | |||
this.suiteTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; | |||
} | |||
@Override | |||
public void expireSuiteTicket() { | |||
this.suiteTicketExpiresTime = 0; | |||
} | |||
@Override | |||
public String getSuiteId() { | |||
return this.suiteId; | |||
} | |||
public void setSuiteId(String corpId) { | |||
this.suiteId = corpId; | |||
} | |||
@Override | |||
public String getSuiteSecret() { | |||
return this.suiteSecret; | |||
} | |||
public void setSuiteSecret(String corpSecret) { | |||
this.suiteSecret = corpSecret; | |||
} | |||
@Override | |||
public String getToken() { | |||
return this.token; | |||
} | |||
public void setToken(String token) { | |||
this.token = token; | |||
} | |||
@Override | |||
public long getExpiresTime() { | |||
return this.expiresTime; | |||
} | |||
public void setExpiresTime(long expiresTime) { | |||
this.expiresTime = expiresTime; | |||
} | |||
@Override | |||
public String getAesKey() { | |||
return this.aesKey; | |||
} | |||
public void setAesKey(String aesKey) { | |||
this.aesKey = aesKey; | |||
} | |||
public void setOauth2redirectUri(String oauth2redirectUri) { | |||
this.oauth2redirectUri = oauth2redirectUri; | |||
} | |||
@Override | |||
public String getHttpProxyHost() { | |||
return this.httpProxyHost; | |||
} | |||
public void setHttpProxyHost(String httpProxyHost) { | |||
this.httpProxyHost = httpProxyHost; | |||
} | |||
@Override | |||
public int getHttpProxyPort() { | |||
return this.httpProxyPort; | |||
} | |||
public void setHttpProxyPort(int httpProxyPort) { | |||
this.httpProxyPort = httpProxyPort; | |||
} | |||
@Override | |||
public String getHttpProxyUsername() { | |||
return this.httpProxyUsername; | |||
} | |||
public void setHttpProxyUsername(String httpProxyUsername) { | |||
this.httpProxyUsername = httpProxyUsername; | |||
} | |||
@Override | |||
public String getHttpProxyPassword() { | |||
return this.httpProxyPassword; | |||
} | |||
public void setHttpProxyPassword(String httpProxyPassword) { | |||
this.httpProxyPassword = httpProxyPassword; | |||
} | |||
@Override | |||
public String toString() { | |||
return WxCpGsonBuilder.create().toJson(this); | |||
} | |||
@Override | |||
public File getTmpDirFile() { | |||
return this.tmpDirFile; | |||
} | |||
public void setTmpDirFile(File tmpDirFile) { | |||
this.tmpDirFile = tmpDirFile; | |||
} | |||
@Override | |||
public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||
return this.apacheHttpClientBuilder; | |||
} | |||
public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | |||
this.apacheHttpClientBuilder = apacheHttpClientBuilder; | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
/** | |||
* 对公众平台发送给公众账号的消息加解密示例代码. | |||
* | |||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
* <p> | |||
* 针对org.apache.commons.codec.binary.Base64, | |||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||
* <p> | |||
* 针对org.apache.commons.codec.binary.Base64, | |||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||
*/ | |||
// ------------------------------------------------------------------------ | |||
/** | |||
* 针对org.apache.commons.codec.binary.Base64, | |||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||
*/ | |||
package me.chanjar.weixin.cp.util.crypto; | |||
import org.apache.commons.codec.binary.Base64; | |||
import me.chanjar.weixin.common.util.crypto.WxCryptUtil; | |||
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage; | |||
public class WxCpTpCryptUtil extends WxCryptUtil { | |||
/** | |||
* 构造函数 | |||
* | |||
* @param wxCpConfigStorage | |||
*/ | |||
public WxCpTpCryptUtil(WxCpTpConfigStorage wxCpTpConfigStorage) { | |||
/* | |||
* @param token 公众平台上,开发者设置的token | |||
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | |||
* @param appidOrCorpid 公众平台corpId | |||
*/ | |||
String encodingAesKey = wxCpTpConfigStorage.getAesKey(); | |||
String token = wxCpTpConfigStorage.getToken(); | |||
String corpId = wxCpTpConfigStorage.getCorpId(); | |||
this.token = token; | |||
this.appidOrCorpid = corpId; | |||
this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||
} | |||
} |
@@ -1,117 +1,135 @@ | |||
package me.chanjar.weixin.cp.util.xml; | |||
import java.io.InputStream; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import com.thoughtworks.xstream.XStream; | |||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutImageMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutVideoMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutVoiceMessage; | |||
public class XStreamTransformer { | |||
protected static final Map<Class, XStream> CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); | |||
/** | |||
* xml -> pojo | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <T> T fromXml(Class<T> clazz, String xml) { | |||
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); | |||
return object; | |||
} | |||
@SuppressWarnings("unchecked") | |||
public static <T> T fromXml(Class<T> clazz, InputStream is) { | |||
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); | |||
return object; | |||
} | |||
/** | |||
* 注册扩展消息的解析器. | |||
* | |||
* @param clz 类型 | |||
* @param xStream xml解析器 | |||
*/ | |||
public static void register(Class clz, XStream xStream) { | |||
CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | |||
} | |||
/** | |||
* pojo -> xml. | |||
*/ | |||
public static <T> String toXml(Class<T> clazz, T object) { | |||
return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | |||
} | |||
private static Map<Class, XStream> configXStreamInstance() { | |||
Map<Class, XStream> map = new HashMap<>(); | |||
map.put(WxCpXmlMessage.class, configWxCpXmlMessage()); | |||
map.put(WxCpXmlOutNewsMessage.class, configWxCpXmlOutNewsMessage()); | |||
map.put(WxCpXmlOutTextMessage.class, configWxCpXmlOutTextMessage()); | |||
map.put(WxCpXmlOutImageMessage.class, configWxCpXmlOutImageMessage()); | |||
map.put(WxCpXmlOutVideoMessage.class, configWxCpXmlOutVideoMessage()); | |||
map.put(WxCpXmlOutVoiceMessage.class, configWxCpXmlOutVoiceMessage()); | |||
return map; | |||
} | |||
private static XStream configWxCpXmlMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlMessage.class); | |||
xstream.processAnnotations(WxCpXmlMessage.ScanCodeInfo.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.Item.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendLocationInfo.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutImageMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutImageMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutNewsMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutNewsMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutNewsMessage.Item.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutTextMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutTextMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutVideoMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVideoMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVideoMessage.Video.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutVoiceMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVoiceMessage.class); | |||
return xstream; | |||
} | |||
} | |||
package me.chanjar.weixin.cp.util.xml; | |||
import java.io.InputStream; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import com.thoughtworks.xstream.XStream; | |||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
import me.chanjar.weixin.cp.bean.WxCpTpXmlMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpTpXmlPackage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutImageMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutVideoMessage; | |||
import me.chanjar.weixin.cp.bean.WxCpXmlOutVoiceMessage; | |||
public class XStreamTransformer { | |||
protected static final Map<Class, XStream> CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); | |||
/** | |||
* xml -> pojo | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <T> T fromXml(Class<T> clazz, String xml) { | |||
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml); | |||
return object; | |||
} | |||
@SuppressWarnings("unchecked") | |||
public static <T> T fromXml(Class<T> clazz, InputStream is) { | |||
T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is); | |||
return object; | |||
} | |||
/** | |||
* 注册扩展消息的解析器. | |||
* | |||
* @param clz 类型 | |||
* @param xStream xml解析器 | |||
*/ | |||
public static void register(Class clz, XStream xStream) { | |||
CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | |||
} | |||
/** | |||
* pojo -> xml. | |||
*/ | |||
public static <T> String toXml(Class<T> clazz, T object) { | |||
return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | |||
} | |||
private static Map<Class, XStream> configXStreamInstance() { | |||
Map<Class, XStream> map = new HashMap<>(); | |||
map.put(WxCpXmlMessage.class, configWxCpXmlMessage()); | |||
map.put(WxCpXmlOutNewsMessage.class, configWxCpXmlOutNewsMessage()); | |||
map.put(WxCpXmlOutTextMessage.class, configWxCpXmlOutTextMessage()); | |||
map.put(WxCpXmlOutImageMessage.class, configWxCpXmlOutImageMessage()); | |||
map.put(WxCpXmlOutVideoMessage.class, configWxCpXmlOutVideoMessage()); | |||
map.put(WxCpXmlOutVoiceMessage.class, configWxCpXmlOutVoiceMessage()); | |||
map.put(WxCpTpXmlPackage.class, configWxCpTpXmlPackage()); | |||
map.put(WxCpTpXmlMessage.class, configWxCpTpXmlMessage()); | |||
return map; | |||
} | |||
private static XStream configWxCpXmlMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlMessage.class); | |||
xstream.processAnnotations(WxCpXmlMessage.ScanCodeInfo.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.Item.class); | |||
xstream.processAnnotations(WxCpXmlMessage.SendLocationInfo.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutImageMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutImageMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutNewsMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutNewsMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutNewsMessage.Item.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutTextMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutTextMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutVideoMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVideoMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVideoMessage.Video.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpXmlOutVoiceMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpXmlOutMessage.class); | |||
xstream.processAnnotations(WxCpXmlOutVoiceMessage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpTpXmlPackage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpTpXmlPackage.class); | |||
return xstream; | |||
} | |||
private static XStream configWxCpTpXmlMessage() { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxCpTpXmlMessage.class); | |||
return xstream; | |||
} | |||
} |