mirror of
				https://gitee.com/binary/weixin-java-tools.git
				synced 2025-10-31 18:46:10 +08:00 
			
		
		
		
	🎨 #571 微信小程序接口请求增加多种http客户端
This commit is contained in:
		| @ -23,7 +23,7 @@ | ||||
|     wx.miniapp.config-storage.redis.host = 127.0.0.1 | ||||
|     wx.miniapp.config-storage.redis.port = 6379 | ||||
|     # http客户端配置 | ||||
|     wx.miniapp.config-storage.http-client-type=HttpClient      # http客户端类型: HttpClient(默认) | ||||
|     wx.miniapp.config-storage.http-client-type=HttpClient      # http客户端类型: HttpClient(默认), OkHttp, JoddHttp | ||||
|     wx.miniapp.config-storage.http-proxy-host= | ||||
|     wx.miniapp.config-storage.http-proxy-port= | ||||
|     wx.miniapp.config-storage.http-proxy-username= | ||||
|  | ||||
| @ -1,11 +1,14 @@ | ||||
| package com.binarywang.spring.starter.wxjava.miniapp.config; | ||||
|  | ||||
| import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
| import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl; | ||||
| import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; | ||||
| import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl; | ||||
| import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl; | ||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
| import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; | ||||
| import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl; | ||||
| import com.binarywang.spring.starter.wxjava.miniapp.enums.StorageType; | ||||
| import com.binarywang.spring.starter.wxjava.miniapp.enums.HttpClientType; | ||||
| import com.binarywang.spring.starter.wxjava.miniapp.properties.ConfigStorage; | ||||
| import com.binarywang.spring.starter.wxjava.miniapp.properties.RedisProperties; | ||||
| import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; | ||||
| @ -49,9 +52,19 @@ public class WxMaAutoConfiguration { | ||||
|   @Bean | ||||
|   @ConditionalOnMissingBean(WxMaService.class) | ||||
|   public WxMaService service(WxMaConfig wxMaConfig) { | ||||
|     final WxMaServiceImpl service = new WxMaServiceImpl(); | ||||
|     service.setWxMaConfig(wxMaConfig); | ||||
|     return service; | ||||
|     HttpClientType httpClientType = wxMaProperties.getConfigStorage().getHttpClientType(); | ||||
|     WxMaService wxMaService; | ||||
|     if (httpClientType == HttpClientType.OkHttp) { | ||||
|       wxMaService = new WxMaServiceOkHttpImpl(); | ||||
|     } else if (httpClientType == HttpClientType.JoddHttp) { | ||||
|       wxMaService = new WxMaServiceJoddHttpImpl(); | ||||
|     } else if (httpClientType == HttpClientType.HttpClient) { | ||||
|       wxMaService = new WxMaServiceHttpClientImpl(); | ||||
|     } else { | ||||
|       wxMaService = new WxMaServiceImpl(); | ||||
|     } | ||||
|     wxMaService.setWxMaConfig(wxMaConfig); | ||||
|     return wxMaService; | ||||
|   } | ||||
|  | ||||
|   @Bean | ||||
|  | ||||
| @ -10,5 +10,13 @@ public enum HttpClientType { | ||||
|   /** | ||||
|    * HttpClient. | ||||
|    */ | ||||
|   HttpClient | ||||
|   HttpClient, | ||||
|   /** | ||||
|    * OkHttp. | ||||
|    */ | ||||
|   OkHttp, | ||||
|   /** | ||||
|    * JoddHttp. | ||||
|    */ | ||||
|   JoddHttp, | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,397 @@ | ||||
| package cn.binarywang.wx.miniapp.api.impl; | ||||
|  | ||||
| import cn.binarywang.wx.miniapp.api.*; | ||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
| import com.google.common.base.Joiner; | ||||
| import com.google.common.collect.ImmutableMap; | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.JsonObject; | ||||
| import com.google.gson.JsonParser; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| 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.common.util.json.WxGsonBuilder; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import java.util.concurrent.locks.Lock; | ||||
|  | ||||
| import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | ||||
|  | ||||
| /** | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  * @see #doGetAccessTokenRequest | ||||
|  */ | ||||
| @Slf4j | ||||
| public abstract class BaseWxMaServiceImpl<H, P> implements WxMaService, RequestHttp<H, P> { | ||||
|   private static final JsonParser JSON_PARSER = new JsonParser(); | ||||
|   private WxMaConfig wxMaConfig; | ||||
|  | ||||
|   private final WxMaMsgService kefuService = new WxMaMsgServiceImpl(this); | ||||
|   private final WxMaMediaService materialService = new WxMaMediaServiceImpl(this); | ||||
|   private final WxMaUserService userService = new WxMaUserServiceImpl(this); | ||||
|   private final WxMaQrcodeService qrCodeService = new WxMaQrcodeServiceImpl(this); | ||||
|   private final WxMaTemplateService templateService = new WxMaTemplateServiceImpl(this); | ||||
|   private final WxMaAnalysisService analysisService = new WxMaAnalysisServiceImpl(this); | ||||
|   private final WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | ||||
|   private final WxMaSettingService settingService = new WxMaSettingServiceImpl(this); | ||||
|   private final WxMaJsapiService jsapiService = new WxMaJsapiServiceImpl(this); | ||||
|   private final WxMaShareService shareService = new WxMaShareServiceImpl(this); | ||||
|   private final WxMaRunService runService = new WxMaRunServiceImpl(this); | ||||
|   private final WxMaSecCheckService secCheckService = new WxMaSecCheckServiceImpl(this); | ||||
|   private final WxMaPluginService pluginService = new WxMaPluginServiceImpl(this); | ||||
|   private final WxMaExpressService expressService = new WxMaExpressServiceImpl(this); | ||||
|   private final WxMaSubscribeService subscribeService = new WxMaSubscribeServiceImpl(this); | ||||
|   private final WxMaCloudService cloudService = new WxMaCloudServiceImpl(this); | ||||
|   private final WxMaLiveService liveService = new WxMaLiveServiceImpl(this); | ||||
|  | ||||
|   private int retrySleepMillis = 1000; | ||||
|   private int maxRetryTimes = 5; | ||||
|  | ||||
|   protected static final Gson GSON = new Gson(); | ||||
|  | ||||
|   @Override | ||||
|   public RequestHttp getRequestHttp() { | ||||
|     return this; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) | ||||
|     throws WxErrorException { | ||||
|     Map<String, String> params = new HashMap<>(8); | ||||
|     params.put("openid", openid); | ||||
|  | ||||
|     if (StringUtils.isNotEmpty(transactionId)) { | ||||
|       params.put("transaction_id", transactionId); | ||||
|     } | ||||
|  | ||||
|     if (StringUtils.isNotEmpty(mchId)) { | ||||
|       params.put("mch_id", mchId); | ||||
|     } | ||||
|  | ||||
|     if (StringUtils.isNotEmpty(outTradeNo)) { | ||||
|       params.put("out_trade_no", outTradeNo); | ||||
|     } | ||||
|  | ||||
|     String responseContent = this.get(GET_PAID_UNION_ID_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | ||||
|     WxError error = WxError.fromJson(responseContent, WxType.MiniApp); | ||||
|     if (error.getErrorCode() != 0) { | ||||
|       throw new WxErrorException(error); | ||||
|     } | ||||
|  | ||||
|     return JSON_PARSER.parse(responseContent).getAsJsonObject().get("unionid").getAsString(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { | ||||
|     final WxMaConfig config = getWxMaConfig(); | ||||
|     Map<String, String> params = new HashMap<>(8); | ||||
|     params.put("appid", config.getAppid()); | ||||
|     params.put("secret", config.getSecret()); | ||||
|     params.put("js_code", jsCode); | ||||
|     params.put("grant_type", "authorization_code"); | ||||
|  | ||||
|     String result = get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | ||||
|     return WxMaJscode2SessionResult.fromJson(result); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setDynamicData(int lifespan, String type, int scene, String data) throws WxErrorException { | ||||
|     JsonObject jsonObject = new JsonObject(); | ||||
|     jsonObject.addProperty("lifespan", lifespan); | ||||
|     jsonObject.addProperty("query", WxGsonBuilder.create().toJson(ImmutableMap.of("type", type))); | ||||
|     jsonObject.addProperty("data", data); | ||||
|     jsonObject.addProperty("scene", scene); | ||||
|  | ||||
|     this.post(SET_DYNAMIC_DATA_URL, jsonObject.toString()); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public boolean checkSignature(String timestamp, String nonce, String signature) { | ||||
|     try { | ||||
|       return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature); | ||||
|     } catch (Exception e) { | ||||
|       log.error("Checking signature failed, and the reason is :" + e.getMessage()); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String getAccessToken() throws WxErrorException { | ||||
|     return getAccessToken(false); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String getAccessToken(boolean forceRefresh) throws WxErrorException { | ||||
|     if (!forceRefresh && !this.getWxMaConfig().isAccessTokenExpired()) { | ||||
|       return this.getWxMaConfig().getAccessToken(); | ||||
|     } | ||||
|  | ||||
|     Lock lock = this.getWxMaConfig().getAccessTokenLock(); | ||||
|     boolean locked = false; | ||||
|     try { | ||||
|       do { | ||||
|         locked = lock.tryLock(100, TimeUnit.MILLISECONDS); | ||||
|         if (!forceRefresh && !this.getWxMaConfig().isAccessTokenExpired()) { | ||||
|           return this.getWxMaConfig().getAccessToken(); | ||||
|         } | ||||
|       } while (!locked); | ||||
|       String response = doGetAccessTokenRequest(); | ||||
|       return extractAccessToken(response); | ||||
|     } catch (IOException | InterruptedException e) { | ||||
|       throw new RuntimeException(e); | ||||
|     } finally { | ||||
|       if (locked) { | ||||
|         lock.unlock(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * 通过网络请求获取AccessToken | ||||
|    * | ||||
|    * @return | ||||
|    * @throws IOException | ||||
|    */ | ||||
|   protected abstract String doGetAccessTokenRequest() throws IOException; | ||||
|  | ||||
|   @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); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String post(String url, Object obj) throws WxErrorException { | ||||
|     return this.execute(SimplePostRequestExecutor.create(this), url, WxGsonBuilder.create().toJson(obj)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * 向微信端发送请求,在这里执行的策略是当发生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) { | ||||
|           log.warn("重试达到最大次数【{}】", maxRetryTimes); | ||||
|           //最后一次重试失败后,直接抛出异常,不再等待 | ||||
|           throw new WxErrorException(WxError.builder() | ||||
|             .errorCode(e.getError().getErrorCode()) | ||||
|             .errorMsg("微信服务端异常,超出重试次数!") | ||||
|             .build()); | ||||
|         } | ||||
|  | ||||
|         WxError error = e.getError(); | ||||
|         // -1 系统繁忙, 1000ms后重试 | ||||
|         if (error.getErrorCode() == -1) { | ||||
|           int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | ||||
|           try { | ||||
|             log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | ||||
|             Thread.sleep(sleepMillis); | ||||
|           } catch (InterruptedException e1) { | ||||
|             Thread.currentThread().interrupt(); | ||||
|           } | ||||
|         } else { | ||||
|           throw e; | ||||
|         } | ||||
|       } | ||||
|     } while (retryTimes++ < this.maxRetryTimes); | ||||
|  | ||||
|     log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | ||||
|     throw new RuntimeException("微信服务端异常,超出重试次数"); | ||||
|   } | ||||
|  | ||||
|   private <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | ||||
|     E dataForLog = DataUtils.handleDataWithSecret(data); | ||||
|  | ||||
|     if (uri.contains("access_token=")) { | ||||
|       throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); | ||||
|     } | ||||
|     String accessToken = getAccessToken(false); | ||||
|  | ||||
|     String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken; | ||||
|  | ||||
|     try { | ||||
|       T result = executor.execute(uriWithAccessToken, data, WxType.MiniApp); | ||||
|       log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); | ||||
|       return result; | ||||
|     } catch (WxErrorException e) { | ||||
|       WxError error = e.getError(); | ||||
|       /* | ||||
|        * 发生以下情况时尝试刷新access_token | ||||
|        */ | ||||
|       if (error.getErrorCode() == ERR_40001 | ||||
|         || error.getErrorCode() == ERR_42001 | ||||
|         || error.getErrorCode() == ERR_40014) { | ||||
|         // 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token | ||||
|         Lock lock = this.getWxMaConfig().getAccessTokenLock(); | ||||
|         lock.lock(); | ||||
|         try { | ||||
|           if (StringUtils.equals(this.getWxMaConfig().getAccessToken(), accessToken)) { | ||||
|             this.getWxMaConfig().expireAccessToken(); | ||||
|           } | ||||
|         } catch (Exception ex) { | ||||
|           this.getWxMaConfig().expireAccessToken(); | ||||
|         } finally { | ||||
|           lock.unlock(); | ||||
|         } | ||||
|         if (this.getWxMaConfig().autoRefreshToken()) { | ||||
|           return this.execute(executor, uri, data); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (error.getErrorCode() != 0) { | ||||
|         log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); | ||||
|         throw new WxErrorException(error, e); | ||||
|       } | ||||
|       return null; | ||||
|     } catch (IOException e) { | ||||
|       log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); | ||||
|       throw new RuntimeException(e); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * 设置当前的AccessToken | ||||
|    * | ||||
|    * @param resultContent | ||||
|    * @return | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
|   protected String extractAccessToken(String resultContent) throws WxErrorException { | ||||
|     WxMaConfig config = this.getWxMaConfig(); | ||||
|     WxError error = WxError.fromJson(resultContent, WxType.MiniApp); | ||||
|     if (error.getErrorCode() != 0) { | ||||
|       throw new WxErrorException(error); | ||||
|     } | ||||
|     WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | ||||
|     config.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | ||||
|     return accessToken.getAccessToken(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaConfig getWxMaConfig() { | ||||
|     return this.wxMaConfig; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setWxMaConfig(WxMaConfig wxConfigProvider) { | ||||
|     this.wxMaConfig = wxConfigProvider; | ||||
|     this.initHttp(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setRetrySleepMillis(int retrySleepMillis) { | ||||
|     this.retrySleepMillis = retrySleepMillis; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setMaxRetryTimes(int maxRetryTimes) { | ||||
|     this.maxRetryTimes = maxRetryTimes; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaMsgService getMsgService() { | ||||
|     return this.kefuService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaMediaService getMediaService() { | ||||
|     return this.materialService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaUserService getUserService() { | ||||
|     return this.userService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaQrcodeService getQrcodeService() { | ||||
|     return this.qrCodeService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaTemplateService getTemplateService() { | ||||
|     return this.templateService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaSubscribeService getSubscribeService() { | ||||
|     return this.subscribeService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaAnalysisService getAnalysisService() { | ||||
|     return this.analysisService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaCodeService getCodeService() { | ||||
|     return this.codeService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaJsapiService getJsapiService() { | ||||
|     return this.jsapiService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaSettingService getSettingService() { | ||||
|     return this.settingService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaShareService getShareService() { | ||||
|     return this.shareService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaRunService getRunService() { | ||||
|     return this.runService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaSecCheckService getSecCheckService() { | ||||
|     return this.secCheckService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaPluginService getPluginService() { | ||||
|     return this.pluginService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaExpressService getExpressService() { | ||||
|     return this.expressService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaCloudService getCloudService() { | ||||
|     return this.cloudService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaLiveService getLiveService() { | ||||
|     return this.liveService; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,88 @@ | ||||
| package cn.binarywang.wx.miniapp.api.impl; | ||||
|  | ||||
| import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| 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 org.apache.http.HttpHost; | ||||
| import org.apache.http.client.config.RequestConfig; | ||||
| import org.apache.http.client.methods.CloseableHttpResponse; | ||||
| import org.apache.http.client.methods.HttpGet; | ||||
| import org.apache.http.impl.client.BasicResponseHandler; | ||||
| import org.apache.http.impl.client.CloseableHttpClient; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| /** | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  */ | ||||
| @Slf4j | ||||
| public class WxMaServiceHttpClientImpl extends BaseWxMaServiceImpl { | ||||
|   private CloseableHttpClient httpClient; | ||||
|   private HttpHost httpProxy; | ||||
|  | ||||
|   @Override | ||||
|   public void initHttp() { | ||||
|     WxMaConfig configStorage = this.getWxMaConfig(); | ||||
|     ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); | ||||
|     if (null == apacheHttpClientBuilder) { | ||||
|       apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | ||||
|     } | ||||
|  | ||||
|     apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) | ||||
|       .httpProxyPort(configStorage.getHttpProxyPort()) | ||||
|       .httpProxyUsername(configStorage.getHttpProxyUsername()) | ||||
|       .httpProxyPassword(configStorage.getHttpProxyPassword()); | ||||
|  | ||||
|     if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | ||||
|       this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); | ||||
|     } | ||||
|  | ||||
|     this.httpClient = apacheHttpClientBuilder.build(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public CloseableHttpClient getRequestHttpClient() { | ||||
|     return httpClient; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public HttpHost getRequestHttpProxy() { | ||||
|     return httpProxy; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public HttpType getRequestType() { | ||||
|     return HttpType.APACHE_HTTP; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   protected String doGetAccessTokenRequest() throws IOException { | ||||
|     String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), this.getWxMaConfig().getSecret()); | ||||
|  | ||||
|     HttpGet httpGet = null; | ||||
|     CloseableHttpResponse response = null; | ||||
|     try { | ||||
|       httpGet = new HttpGet(url); | ||||
|       if (this.getRequestHttpProxy() != null) { | ||||
|         RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); | ||||
|         httpGet.setConfig(config); | ||||
|       } | ||||
|       response = getRequestHttpClient().execute(httpGet); | ||||
|       return new BasicResponseHandler().handleResponse(response); | ||||
|     } finally { | ||||
|       if (httpGet != null) { | ||||
|         httpGet.releaseConnection(); | ||||
|       } | ||||
|       if (response != null) { | ||||
|         try { | ||||
|           response.close(); | ||||
|         } catch (IOException e) { | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @ -1,428 +1,11 @@ | ||||
| package cn.binarywang.wx.miniapp.api.impl; | ||||
|  | ||||
| import cn.binarywang.wx.miniapp.api.*; | ||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
| import com.google.common.base.Joiner; | ||||
| import com.google.common.collect.ImmutableMap; | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.JsonObject; | ||||
| import com.google.gson.JsonParser; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| 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.*; | ||||
| import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; | ||||
| import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder; | ||||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| 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.HttpGet; | ||||
| import org.apache.http.impl.client.BasicResponseHandler; | ||||
| import org.apache.http.impl.client.CloseableHttpClient; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.locks.Lock; | ||||
|  | ||||
| import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*; | ||||
|  | ||||
| /** | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  */ | ||||
| @Slf4j | ||||
| public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpClient, HttpHost> { | ||||
|   private static final JsonParser JSON_PARSER = new JsonParser(); | ||||
|   private CloseableHttpClient httpClient; | ||||
|   private HttpHost httpProxy; | ||||
|   private WxMaConfig wxMaConfig; | ||||
| public class WxMaServiceImpl extends WxMaServiceHttpClientImpl { | ||||
|  | ||||
|   private final WxMaMsgService kefuService = new WxMaMsgServiceImpl(this); | ||||
|   private final WxMaMediaService materialService = new WxMaMediaServiceImpl(this); | ||||
|   private final WxMaUserService userService = new WxMaUserServiceImpl(this); | ||||
|   private final WxMaQrcodeService qrCodeService = new WxMaQrcodeServiceImpl(this); | ||||
|   private final WxMaTemplateService templateService = new WxMaTemplateServiceImpl(this); | ||||
|   private final WxMaAnalysisService analysisService = new WxMaAnalysisServiceImpl(this); | ||||
|   private final WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | ||||
|   private final WxMaSettingService settingService = new WxMaSettingServiceImpl(this); | ||||
|   private final WxMaJsapiService jsapiService = new WxMaJsapiServiceImpl(this); | ||||
|   private final WxMaShareService shareService = new WxMaShareServiceImpl(this); | ||||
|   private final WxMaRunService runService = new WxMaRunServiceImpl(this); | ||||
|   private final WxMaSecCheckService secCheckService = new WxMaSecCheckServiceImpl(this); | ||||
|   private final WxMaPluginService pluginService = new WxMaPluginServiceImpl(this); | ||||
|   private final WxMaExpressService expressService = new WxMaExpressServiceImpl(this); | ||||
|   private final WxMaSubscribeService subscribeService = new WxMaSubscribeServiceImpl(this); | ||||
|   private final WxMaCloudService cloudService = new WxMaCloudServiceImpl(this); | ||||
|   private final WxMaLiveService liveService = new WxMaLiveServiceImpl(this); | ||||
|  | ||||
|   private int retrySleepMillis = 1000; | ||||
|   private int maxRetryTimes = 5; | ||||
|  | ||||
|   protected static final Gson GSON = new Gson(); | ||||
|  | ||||
|   @Override | ||||
|   public CloseableHttpClient getRequestHttpClient() { | ||||
|     return httpClient; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public HttpHost getRequestHttpProxy() { | ||||
|     return httpProxy; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public HttpType getRequestType() { | ||||
|     return HttpType.APACHE_HTTP; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void initHttp() { | ||||
|     WxMaConfig configStorage = this.getWxMaConfig(); | ||||
|     ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); | ||||
|     if (null == apacheHttpClientBuilder) { | ||||
|       apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | ||||
|     } | ||||
|  | ||||
|     apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) | ||||
|       .httpProxyPort(configStorage.getHttpProxyPort()) | ||||
|       .httpProxyUsername(configStorage.getHttpProxyUsername()) | ||||
|       .httpProxyPassword(configStorage.getHttpProxyPassword()); | ||||
|  | ||||
|     if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | ||||
|       this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); | ||||
|     } | ||||
|  | ||||
|     this.httpClient = apacheHttpClientBuilder.build(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public RequestHttp getRequestHttp() { | ||||
|     return this; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String getAccessToken(boolean forceRefresh) throws WxErrorException { | ||||
|     if (!this.getWxMaConfig().isAccessTokenExpired() && !forceRefresh) { | ||||
|       return this.getWxMaConfig().getAccessToken(); | ||||
|     } | ||||
|  | ||||
|     Lock lock = this.getWxMaConfig().getAccessTokenLock(); | ||||
|     lock.lock(); | ||||
|     try { | ||||
|       if (!this.getWxMaConfig().isAccessTokenExpired() && !forceRefresh) { | ||||
|         return this.getWxMaConfig().getAccessToken(); | ||||
|       } | ||||
|        | ||||
|       String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), | ||||
|         this.getWxMaConfig().getSecret()); | ||||
|       try { | ||||
|         HttpGet httpGet = new HttpGet(url); | ||||
|         if (this.getRequestHttpProxy() != null) { | ||||
|           RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build(); | ||||
|           httpGet.setConfig(config); | ||||
|         } | ||||
|         try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) { | ||||
|           String resultContent = new BasicResponseHandler().handleResponse(response); | ||||
|           WxError error = WxError.fromJson(resultContent, WxType.MiniApp); | ||||
|           if (error.getErrorCode() != 0) { | ||||
|             throw new WxErrorException(error); | ||||
|           } | ||||
|           WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | ||||
|           this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | ||||
|  | ||||
|           return this.getWxMaConfig().getAccessToken(); | ||||
|         } finally { | ||||
|           httpGet.releaseConnection(); | ||||
|         } | ||||
|       } catch (IOException e) { | ||||
|         throw new RuntimeException(e); | ||||
|       } | ||||
|     } finally { | ||||
|       lock.unlock(); | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) | ||||
|     throws WxErrorException { | ||||
|     Map<String, String> params = new HashMap<>(8); | ||||
|     params.put("openid", openid); | ||||
|  | ||||
|     if (StringUtils.isNotEmpty(transactionId)) { | ||||
|       params.put("transaction_id", transactionId); | ||||
|     } | ||||
|  | ||||
|     if (StringUtils.isNotEmpty(mchId)) { | ||||
|       params.put("mch_id", mchId); | ||||
|     } | ||||
|  | ||||
|     if (StringUtils.isNotEmpty(outTradeNo)) { | ||||
|       params.put("out_trade_no", outTradeNo); | ||||
|     } | ||||
|  | ||||
|     String responseContent = this.get(GET_PAID_UNION_ID_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | ||||
|     WxError error = WxError.fromJson(responseContent, WxType.MiniApp); | ||||
|     if (error.getErrorCode() != 0) { | ||||
|       throw new WxErrorException(error); | ||||
|     } | ||||
|  | ||||
|     return JSON_PARSER.parse(responseContent).getAsJsonObject().get("unionid").getAsString(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException { | ||||
|     final WxMaConfig config = getWxMaConfig(); | ||||
|     Map<String, String> params = new HashMap<>(8); | ||||
|     params.put("appid", config.getAppid()); | ||||
|     params.put("secret", config.getSecret()); | ||||
|     params.put("js_code", jsCode); | ||||
|     params.put("grant_type", "authorization_code"); | ||||
|  | ||||
|     String result = get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params)); | ||||
|     return WxMaJscode2SessionResult.fromJson(result); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setDynamicData(int lifespan, String type, int scene, String data) throws WxErrorException { | ||||
|     JsonObject jsonObject = new JsonObject(); | ||||
|     jsonObject.addProperty("lifespan", lifespan); | ||||
|     jsonObject.addProperty("query", WxGsonBuilder.create().toJson(ImmutableMap.of("type", type))); | ||||
|     jsonObject.addProperty("data", data); | ||||
|     jsonObject.addProperty("scene", scene); | ||||
|  | ||||
|     this.post(SET_DYNAMIC_DATA_URL, jsonObject.toString()); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public boolean checkSignature(String timestamp, String nonce, String signature) { | ||||
|     try { | ||||
|       return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature); | ||||
|     } catch (Exception e) { | ||||
|       log.error("Checking signature failed, and the reason is :" + e.getMessage()); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String getAccessToken() throws WxErrorException { | ||||
|     return getAccessToken(false); | ||||
|   } | ||||
|  | ||||
|   @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); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String post(String url, Object obj) throws WxErrorException { | ||||
|     return this.execute(SimplePostRequestExecutor.create(this), url, WxGsonBuilder.create().toJson(obj)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * 向微信端发送请求,在这里执行的策略是当发生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) { | ||||
|           log.warn("重试达到最大次数【{}】", maxRetryTimes); | ||||
|           //最后一次重试失败后,直接抛出异常,不再等待 | ||||
|           throw new WxErrorException(WxError.builder() | ||||
|             .errorCode(e.getError().getErrorCode()) | ||||
|             .errorMsg("微信服务端异常,超出重试次数!") | ||||
|             .build()); | ||||
|         } | ||||
|  | ||||
|         WxError error = e.getError(); | ||||
|         // -1 系统繁忙, 1000ms后重试 | ||||
|         if (error.getErrorCode() == -1) { | ||||
|           int sleepMillis = this.retrySleepMillis * (1 << retryTimes); | ||||
|           try { | ||||
|             log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); | ||||
|             Thread.sleep(sleepMillis); | ||||
|           } catch (InterruptedException e1) { | ||||
|             Thread.currentThread().interrupt(); | ||||
|           } | ||||
|         } else { | ||||
|           throw e; | ||||
|         } | ||||
|       } | ||||
|     } while (retryTimes++ < this.maxRetryTimes); | ||||
|  | ||||
|     log.warn("重试达到最大次数【{}】", this.maxRetryTimes); | ||||
|     throw new RuntimeException("微信服务端异常,超出重试次数"); | ||||
|   } | ||||
|  | ||||
|   private <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | ||||
|     E dataForLog = DataUtils.handleDataWithSecret(data); | ||||
|  | ||||
|     if (uri.contains("access_token=")) { | ||||
|       throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri); | ||||
|     } | ||||
|     String accessToken = getAccessToken(false); | ||||
|  | ||||
|     String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken; | ||||
|  | ||||
|     try { | ||||
|       T result = executor.execute(uriWithAccessToken, data, WxType.MiniApp); | ||||
|       log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); | ||||
|       return result; | ||||
|     } catch (WxErrorException e) { | ||||
|       WxError error = e.getError(); | ||||
|       /* | ||||
|        * 发生以下情况时尝试刷新access_token | ||||
|        */ | ||||
|       if (error.getErrorCode() == ERR_40001 | ||||
|         || error.getErrorCode() == ERR_42001 | ||||
|         || error.getErrorCode() == ERR_40014) { | ||||
|         // 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token | ||||
|         Lock lock = this.getWxMaConfig().getAccessTokenLock(); | ||||
|         lock.lock(); | ||||
|         try { | ||||
|           if (StringUtils.equals(this.getWxMaConfig().getAccessToken(), accessToken)) { | ||||
|             this.getWxMaConfig().expireAccessToken(); | ||||
|           } | ||||
|         } catch (Exception ex) { | ||||
|           this.getWxMaConfig().expireAccessToken(); | ||||
|         } finally { | ||||
|           lock.unlock(); | ||||
|         } | ||||
|         if (this.getWxMaConfig().autoRefreshToken()) { | ||||
|           return this.execute(executor, uri, data); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (error.getErrorCode() != 0) { | ||||
|         log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); | ||||
|         throw new WxErrorException(error, e); | ||||
|       } | ||||
|       return null; | ||||
|     } catch (IOException e) { | ||||
|       log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); | ||||
|       throw new RuntimeException(e); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaConfig getWxMaConfig() { | ||||
|     return this.wxMaConfig; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setWxMaConfig(WxMaConfig wxConfigProvider) { | ||||
|     this.wxMaConfig = wxConfigProvider; | ||||
|     this.initHttp(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setRetrySleepMillis(int retrySleepMillis) { | ||||
|     this.retrySleepMillis = retrySleepMillis; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setMaxRetryTimes(int maxRetryTimes) { | ||||
|     this.maxRetryTimes = maxRetryTimes; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaMsgService getMsgService() { | ||||
|     return this.kefuService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaMediaService getMediaService() { | ||||
|     return this.materialService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaUserService getUserService() { | ||||
|     return this.userService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaQrcodeService getQrcodeService() { | ||||
|     return this.qrCodeService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaTemplateService getTemplateService() { | ||||
|     return this.templateService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaSubscribeService getSubscribeService() { | ||||
|     return this.subscribeService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaAnalysisService getAnalysisService() { | ||||
|     return this.analysisService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaCodeService getCodeService() { | ||||
|     return this.codeService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaJsapiService getJsapiService() { | ||||
|     return this.jsapiService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaSettingService getSettingService() { | ||||
|     return this.settingService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaShareService getShareService() { | ||||
|     return this.shareService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaRunService getRunService() { | ||||
|     return this.runService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaSecCheckService getSecCheckService() { | ||||
|     return this.secCheckService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaPluginService getPluginService() { | ||||
|     return this.pluginService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaExpressService getExpressService() { | ||||
|     return this.expressService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaCloudService getCloudService() { | ||||
|     return this.cloudService; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public WxMaLiveService getLiveService() { | ||||
|     return this.liveService; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,59 @@ | ||||
| package cn.binarywang.wx.miniapp.api.impl; | ||||
|  | ||||
| import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
| import jodd.http.HttpConnectionProvider; | ||||
| import jodd.http.HttpRequest; | ||||
| import jodd.http.ProxyInfo; | ||||
| import jodd.http.net.SocketHttpConnectionProvider; | ||||
| import me.chanjar.weixin.common.util.http.HttpType; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| /** | ||||
|  * jodd-http方式实现. | ||||
|  * | ||||
|  * @author someone | ||||
|  */ | ||||
| public class WxMaServiceJoddHttpImpl extends BaseWxMaServiceImpl<HttpConnectionProvider, ProxyInfo> { | ||||
|   private HttpConnectionProvider httpClient; | ||||
|   private ProxyInfo httpProxy; | ||||
|  | ||||
|   @Override | ||||
|   public void initHttp() { | ||||
|     WxMaConfig configStorage = this.getWxMaConfig(); | ||||
|     if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | ||||
|       this.httpProxy = new ProxyInfo(ProxyInfo.ProxyType.HTTP, configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort(), configStorage.getHttpProxyUsername(), configStorage.getHttpProxyPassword()); | ||||
|     } | ||||
|     this.httpClient = new SocketHttpConnectionProvider(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public HttpConnectionProvider getRequestHttpClient() { | ||||
|     return httpClient; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public ProxyInfo getRequestHttpProxy() { | ||||
|     return httpProxy; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public HttpType getRequestType() { | ||||
|     return HttpType.JODD_HTTP; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   protected String doGetAccessTokenRequest() throws IOException { | ||||
|     String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), this.getWxMaConfig().getSecret()); | ||||
|     HttpRequest request = HttpRequest.get(url); | ||||
|     if (this.getRequestHttpProxy() != null) { | ||||
|       SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | ||||
|       provider.useProxy(getRequestHttpProxy()); | ||||
|  | ||||
|       request.withConnectionProvider(provider); | ||||
|     } | ||||
|     return request.send().bodyText(); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,72 @@ | ||||
| package cn.binarywang.wx.miniapp.api.impl; | ||||
|  | ||||
| import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
| import me.chanjar.weixin.common.util.http.HttpType; | ||||
| import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; | ||||
| import okhttp3.*; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.Objects; | ||||
|  | ||||
| /** | ||||
|  * okhttp实现. | ||||
|  */ | ||||
| public class WxMaServiceOkHttpImpl extends BaseWxMaServiceImpl<OkHttpClient, OkHttpProxyInfo> { | ||||
|  | ||||
|   private OkHttpClient httpClient; | ||||
|   private OkHttpProxyInfo httpProxy; | ||||
|  | ||||
|   @Override | ||||
|   public void initHttp() { | ||||
|     WxMaConfig wxMpConfigStorage = this.getWxMaConfig(); | ||||
|     //设置代理 | ||||
|     if (wxMpConfigStorage.getHttpProxyHost() != null && wxMpConfigStorage.getHttpProxyPort() > 0) { | ||||
|       httpProxy = OkHttpProxyInfo.httpProxy(wxMpConfigStorage.getHttpProxyHost(), | ||||
|         wxMpConfigStorage.getHttpProxyPort(), | ||||
|         wxMpConfigStorage.getHttpProxyUsername(), | ||||
|         wxMpConfigStorage.getHttpProxyPassword()); | ||||
|     } | ||||
|  | ||||
|     OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); | ||||
|     if (httpProxy != null) { | ||||
|       clientBuilder.proxy(getRequestHttpProxy().getProxy()); | ||||
|  | ||||
|       //设置授权 | ||||
|       clientBuilder.authenticator(new Authenticator() { | ||||
|         @Override | ||||
|         public Request authenticate(Route route, Response response) throws IOException { | ||||
|           String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword()); | ||||
|           return response.request().newBuilder() | ||||
|             .header("Authorization", credential) | ||||
|             .build(); | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|     httpClient = clientBuilder.build(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public OkHttpClient getRequestHttpClient() { | ||||
|     return httpClient; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public OkHttpProxyInfo getRequestHttpProxy() { | ||||
|     return httpProxy; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public HttpType getRequestType() { | ||||
|     return HttpType.OK_HTTP; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   protected String doGetAccessTokenRequest() throws IOException { | ||||
|     String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(), this.getWxMaConfig().getSecret()); | ||||
|     Request request = new Request.Builder().url(url).get().build(); | ||||
|     try (Response response = getRequestHttpClient().newCall(request).execute()) { | ||||
|       return Objects.requireNonNull(response.body()).string(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -1,5 +1,6 @@ | ||||
| package me.chanjar.weixin.open.api.impl; | ||||
|  | ||||
| import cn.binarywang.wx.miniapp.api.WxMaService; | ||||
| import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; | ||||
| import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; | ||||
| import cn.binarywang.wx.miniapp.config.WxMaConfig; | ||||
| @ -196,6 +197,7 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | ||||
|  | ||||
|   /** | ||||
|    * 解除绑定小程序体验者 | ||||
|    * | ||||
|    * @param userstr 人员对应的唯一字符串, 可通过获取已绑定的体验者列表获取人员对应的字符串 | ||||
|    * @return | ||||
|    * @throws WxErrorException | ||||
| @ -306,7 +308,8 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | ||||
|   public File getTestQrcode(String pagePath, Map<String, String> params) throws WxErrorException { | ||||
|     WxMaQrcodeParam qrcodeParam = WxMaQrcodeParam.create(pagePath); | ||||
|     qrcodeParam.addPageParam(params); | ||||
|     return execute(MaQrCodeRequestExecutor.create(getRequestHttp()), API_TEST_QRCODE, qrcodeParam); | ||||
|     WxMaService wxMaService = this; | ||||
|     return wxMaService.execute(MaQrCodeRequestExecutor.create(getRequestHttp()), API_TEST_QRCODE, qrcodeParam); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
| @ -547,7 +550,6 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | ||||
|   } | ||||
|  | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * 加急审核申请 | ||||
|    * 有加急次数的第三方可以通过该接口,对已经提审的小程序进行加急操作,加急后的小程序预计2-12小时内审完。 | ||||
| @ -564,6 +566,7 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | ||||
|  | ||||
|   /** | ||||
|    * (1)增加或修改二维码规则 | ||||
|    * | ||||
|    * @param wxQrcodeJumpRule | ||||
|    * @return | ||||
|    * @throws WxErrorException | ||||
| @ -576,6 +579,7 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | ||||
|  | ||||
|   /** | ||||
|    * (2)获取已设置的二维码规则 | ||||
|    * | ||||
|    * @return | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
| @ -587,6 +591,7 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | ||||
|  | ||||
|   /** | ||||
|    * (3)获取校验文件名称及内容 | ||||
|    * | ||||
|    * @return | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
| @ -598,6 +603,7 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | ||||
|  | ||||
|   /** | ||||
|    * (4)删除已设置的二维码规则 | ||||
|    * | ||||
|    * @param prefix | ||||
|    * @return | ||||
|    * @throws WxErrorException | ||||
| @ -612,6 +618,7 @@ public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaServ | ||||
|  | ||||
|   /** | ||||
|    * (5)发布已设置的二维码规则 | ||||
|    * | ||||
|    * @param prefix | ||||
|    * @return | ||||
|    * @throws WxErrorException | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Mario Luo
					Mario Luo