diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java index 76012a281..2d336993f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java @@ -57,6 +57,19 @@ public interface WxCpService extends WxService { */ String getAccessToken(boolean forceRefresh) throws WxErrorException; + /** + *
+   * 获取会话存档access_token,本方法线程安全
+   * 会话存档相关接口需要使用会话存档secret获取单独的access_token
+   * 详情请见: https://developer.work.weixin.qq.com/document/path/91782
+   * 
+ * + * @param forceRefresh 强制刷新 + * @return 会话存档专用的access token + * @throws WxErrorException the wx error exception + */ + String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException; + /** * 获得jsapi_ticket,不强制刷新jsapi_ticket * diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java index 63dc7ac00..10de80bb6 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java @@ -297,12 +297,18 @@ public void getMediaFile(@NonNull long sdk, @NonNull String sdkfileid, String pr @Override public List getPermitUserList(Integer type) throws WxErrorException { + // 获取会话存档专用的access token + String msgAuditAccessToken = this.cpService.getMsgAuditAccessToken(false); final String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_PERMIT_USER_LIST); + // 手动拼接access_token参数 + String urlWithToken = apiUrl + (apiUrl.contains("?") ? "&" : "?") + "access_token=" + msgAuditAccessToken; + JsonObject jsonObject = new JsonObject(); if (type != null) { jsonObject.addProperty("type", type); } - String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + // 使用不自动添加access token的post方法 + String responseContent = this.cpService.postWithoutToken(urlWithToken, jsonObject.toString()); return WxCpGsonBuilder.create().fromJson(GsonParser.parse(responseContent).getAsJsonArray("ids"), new TypeToken>() { }.getType()); @@ -310,17 +316,29 @@ public List getPermitUserList(Integer type) throws WxErrorException { @Override public WxCpGroupChat getGroupChat(@NonNull String roomid) throws WxErrorException { + // 获取会话存档专用的access token + String msgAuditAccessToken = this.cpService.getMsgAuditAccessToken(false); final String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_GROUP_CHAT); + // 手动拼接access_token参数 + String urlWithToken = apiUrl + (apiUrl.contains("?") ? "&" : "?") + "access_token=" + msgAuditAccessToken; + JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("roomid", roomid); - String responseContent = this.cpService.post(apiUrl, jsonObject.toString()); + // 使用不自动添加access token的post方法 + String responseContent = this.cpService.postWithoutToken(urlWithToken, jsonObject.toString()); return WxCpGroupChat.fromJson(responseContent); } @Override public WxCpAgreeInfo checkSingleAgree(@NonNull WxCpCheckAgreeRequest checkAgreeRequest) throws WxErrorException { + // 获取会话存档专用的access token + String msgAuditAccessToken = this.cpService.getMsgAuditAccessToken(false); String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(CHECK_SINGLE_AGREE); - String responseContent = this.cpService.post(apiUrl, checkAgreeRequest.toJson()); + // 手动拼接access_token参数 + String urlWithToken = apiUrl + (apiUrl.contains("?") ? "&" : "?") + "access_token=" + msgAuditAccessToken; + + // 使用不自动添加access token的post方法 + String responseContent = this.cpService.postWithoutToken(urlWithToken, checkAgreeRequest.toJson()); return WxCpAgreeInfo.fromJson(responseContent); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java index 1042f88d6..ef78116e1 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceApacheHttpClientImpl.java @@ -17,6 +17,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import java.io.IOException; +import java.util.concurrent.locks.Lock; /** * The type Wx cp service apache http client. @@ -74,6 +75,51 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { return this.configStorage.getAccessToken(); } + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getMsgAuditAccessToken(); + } + + Lock lock = this.configStorage.getMsgAuditAccessTokenLock(); + lock.lock(); + try { + // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷 + if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getMsgAuditAccessToken(); + } + // 使用会话存档secret获取access_token + String msgAuditSecret = this.configStorage.getMsgAuditSecret(); + if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) { + throw new WxErrorException("会话存档secret未配置"); + } + String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), + this.configStorage.getCorpId(), msgAuditSecret); + + try { + HttpGet httpGet = new HttpGet(url); + if (this.httpProxy != null) { + RequestConfig config = RequestConfig.custom() + .setProxy(this.httpProxy).build(); + httpGet.setConfig(config); + } + String resultContent = getRequestHttpClient().execute(httpGet, ApacheBasicResponseHandler.INSTANCE); + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } catch (IOException e) { + throw new WxRuntimeException(e); + } + } finally { + lock.unlock(); + } + return this.configStorage.getMsgAuditAccessToken(); + } + @Override public void initHttp() { ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java index 4b6a1e36f..3ca041e7e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceHttpComponentsImpl.java @@ -17,6 +17,7 @@ import org.apache.hc.core5.http.HttpHost; import java.io.IOException; +import java.util.concurrent.locks.Lock; /** * The type Wx cp service apache http client. @@ -75,6 +76,51 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { return this.configStorage.getAccessToken(); } + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getMsgAuditAccessToken(); + } + + Lock lock = this.configStorage.getMsgAuditAccessTokenLock(); + lock.lock(); + try { + // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷 + if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getMsgAuditAccessToken(); + } + // 使用会话存档secret获取access_token + String msgAuditSecret = this.configStorage.getMsgAuditSecret(); + if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) { + throw new WxErrorException("会话存档secret未配置"); + } + String url = String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), + this.configStorage.getCorpId(), msgAuditSecret); + + try { + HttpGet httpGet = new HttpGet(url); + if (this.httpProxy != null) { + RequestConfig config = RequestConfig.custom() + .setProxy(this.httpProxy).build(); + httpGet.setConfig(config); + } + String resultContent = getRequestHttpClient().execute(httpGet, BasicResponseHandler.INSTANCE); + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } catch (IOException e) { + throw new WxRuntimeException(e); + } + } finally { + lock.unlock(); + } + return this.configStorage.getMsgAuditAccessToken(); + } + @Override public void initHttp() { HttpComponentsClientBuilder apacheHttpClientBuilder = DefaultHttpComponentsClientBuilder.get(); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java index f2a50db47..7b651cbc0 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceImpl.java @@ -70,6 +70,49 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { return configStorage.getAccessToken(); } + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + final WxCpConfigStorage configStorage = getWxCpConfigStorage(); + if (!configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return configStorage.getMsgAuditAccessToken(); + } + Lock lock = configStorage.getMsgAuditAccessTokenLock(); + lock.lock(); + try { + // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷 + if (!configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return configStorage.getMsgAuditAccessToken(); + } + // 使用会话存档secret获取access_token + String msgAuditSecret = configStorage.getMsgAuditSecret(); + if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) { + throw new WxErrorException("会话存档secret未配置"); + } + String url = String.format(configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), + this.configStorage.getCorpId(), msgAuditSecret); + try { + HttpGet httpGet = new HttpGet(url); + if (getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + String resultContent = getRequestHttpClient().execute(httpGet, ApacheBasicResponseHandler.INSTANCE); + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + configStorage.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } catch (IOException e) { + throw new WxRuntimeException(e); + } + } finally { + lock.unlock(); + } + return configStorage.getMsgAuditAccessToken(); + } + @Override public String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException { final WxCpConfigStorage configStorage = getWxCpConfigStorage(); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java index 508134185..eba931564 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceJoddHttpImpl.java @@ -13,6 +13,8 @@ import me.chanjar.weixin.cp.config.WxCpConfigStorage; import me.chanjar.weixin.cp.constant.WxCpApiPathConsts; +import java.util.concurrent.locks.Lock; + /** * The type Wx cp service jodd http. * @@ -63,6 +65,45 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { return this.configStorage.getAccessToken(); } + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getMsgAuditAccessToken(); + } + + Lock lock = this.configStorage.getMsgAuditAccessTokenLock(); + lock.lock(); + try { + // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷 + if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getMsgAuditAccessToken(); + } + // 使用会话存档secret获取access_token + String msgAuditSecret = this.configStorage.getMsgAuditSecret(); + if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) { + throw new WxErrorException("会话存档secret未配置"); + } + HttpRequest request = HttpRequest.get(String.format(this.configStorage.getApiUrl(WxCpApiPathConsts.GET_TOKEN), + this.configStorage.getCorpId(), msgAuditSecret)); + if (this.httpProxy != null) { + httpClient.useProxy(this.httpProxy); + } + request.withConnectionProvider(httpClient); + HttpResponse response = request.send(); + + String resultContent = response.bodyText(); + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateMsgAuditAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); + } finally { + lock.unlock(); + } + return this.configStorage.getMsgAuditAccessToken(); + } + @Override public void initHttp() { if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java index 511c440e6..ce77b3780 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceOkHttpImpl.java @@ -12,6 +12,7 @@ import okhttp3.*; import java.io.IOException; +import java.util.concurrent.locks.Lock; import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.GET_TOKEN; @@ -74,6 +75,52 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { return this.configStorage.getAccessToken(); } + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getMsgAuditAccessToken(); + } + + Lock lock = this.configStorage.getMsgAuditAccessTokenLock(); + lock.lock(); + try { + // 拿到锁之后,再次判断一下最新的token是否过期,避免重刷 + if (!this.configStorage.isMsgAuditAccessTokenExpired() && !forceRefresh) { + return this.configStorage.getMsgAuditAccessToken(); + } + // 使用会话存档secret获取access_token + String msgAuditSecret = this.configStorage.getMsgAuditSecret(); + if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) { + throw new WxErrorException("会话存档secret未配置"); + } + //得到httpClient + OkHttpClient client = getRequestHttpClient(); + //请求的request + Request request = new Request.Builder() + .url(String.format(this.configStorage.getApiUrl(GET_TOKEN), this.configStorage.getCorpId(), + msgAuditSecret)) + .get() + .build(); + String resultContent = null; + try (Response response = client.newCall(request).execute()) { + resultContent = response.body().string(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + + WxError error = WxError.fromJson(resultContent, WxType.CP); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); + this.configStorage.updateMsgAuditAccessToken(accessToken.getAccessToken(), + accessToken.getExpiresIn()); + } finally { + lock.unlock(); + } + return this.configStorage.getMsgAuditAccessToken(); + } + @Override public void initHttp() { log.debug("WxCpServiceOkHttpImpl initHttp"); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java index fd96d76c3..f716f9cd8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/WxCpConfigStorage.java @@ -265,6 +265,40 @@ public interface WxCpConfigStorage { */ String getMsgAuditSecret(); + /** + * 获取会话存档的access token + * + * @return msg audit access token + */ + String getMsgAuditAccessToken(); + + /** + * 获取会话存档access token的锁 + * + * @return msg audit access token lock + */ + Lock getMsgAuditAccessTokenLock(); + + /** + * 检查会话存档access token是否已过期 + * + * @return true: 已过期, false: 未过期 + */ + boolean isMsgAuditAccessTokenExpired(); + + /** + * 强制将会话存档access token过期掉 + */ + void expireMsgAuditAccessToken(); + + /** + * 更新会话存档access token + * + * @param accessToken 会话存档access token + * @param expiresInSeconds 过期时间(秒) + */ + void updateMsgAuditAccessToken(String accessToken, int expiresInSeconds); + /** * 获取会话存档SDK * 会话存档SDK初始化后有效期为7200秒,无需每次重新初始化 diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java index f8047e846..86ede8241 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpDefaultConfigImpl.java @@ -50,6 +50,15 @@ public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable { private volatile String msgAuditSecret; private volatile String msgAuditPriKey; private volatile String msgAuditLibPath; + /** + * 会话存档access token及其过期时间 + */ + private volatile String msgAuditAccessToken; + private volatile long msgAuditAccessTokenExpiresTime; + /** + * 会话存档access token锁 + */ + protected transient Lock msgAuditAccessTokenLock = new ReentrantLock(); /** * 会话存档SDK及其过期时间 */ @@ -463,6 +472,33 @@ public WxCpDefaultConfigImpl setMsgAuditSecret(String msgAuditSecret) { return this; } + @Override + public String getMsgAuditAccessToken() { + return this.msgAuditAccessToken; + } + + @Override + public Lock getMsgAuditAccessTokenLock() { + return this.msgAuditAccessTokenLock; + } + + @Override + public boolean isMsgAuditAccessTokenExpired() { + return System.currentTimeMillis() > this.msgAuditAccessTokenExpiresTime; + } + + @Override + public void expireMsgAuditAccessToken() { + this.msgAuditAccessTokenExpiresTime = 0; + } + + @Override + public synchronized void updateMsgAuditAccessToken(String accessToken, int expiresInSeconds) { + this.msgAuditAccessToken = accessToken; + // 预留200秒的时间 + this.msgAuditAccessTokenExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + } + @Override public long getMsgAuditSdk() { return this.msgAuditSdk; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java index 48e244550..2c9da893f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/config/impl/WxCpRedisConfigImpl.java @@ -481,6 +481,31 @@ public String getMsgAuditSecret() { return null; } + @Override + public String getMsgAuditAccessToken() { + return null; + } + + @Override + public Lock getMsgAuditAccessTokenLock() { + return null; + } + + @Override + public boolean isMsgAuditAccessTokenExpired() { + return true; + } + + @Override + public void expireMsgAuditAccessToken() { + // 不支持 + } + + @Override + public void updateMsgAuditAccessToken(String accessToken, int expiresInSeconds) { + // 不支持 + } + @Override public long getMsgAuditSdk() { return this.msgAuditSdk; diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java index 6b861cede..87d2094e5 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImplTest.java @@ -101,6 +101,11 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException { return "模拟一个过期的access token:" + System.currentTimeMillis(); } + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + return "mock_msg_audit_access_token"; + } + @Override public void initHttp() { diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpServiceGetMsgAuditAccessTokenTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpServiceGetMsgAuditAccessTokenTest.java new file mode 100644 index 000000000..9d2888a28 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpServiceGetMsgAuditAccessTokenTest.java @@ -0,0 +1,197 @@ +package me.chanjar.weixin.cp.api.impl; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.HttpClientType; +import me.chanjar.weixin.cp.config.WxCpConfigStorage; +import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * 测试 getMsgAuditAccessToken 方法在各个实现类中的正确性 + * + * @author Binary Wang + */ +@Test +public class WxCpServiceGetMsgAuditAccessTokenTest { + + private WxCpDefaultConfigImpl config; + + @BeforeMethod + public void setUp() { + config = new WxCpDefaultConfigImpl(); + config.setCorpId("testCorpId"); + config.setCorpSecret("testCorpSecret"); + config.setMsgAuditSecret("testMsgAuditSecret"); + } + + /** + * 测试 WxCpServiceApacheHttpClientImpl 的 getMsgAuditAccessToken 方法 + */ + @Test + public void testGetMsgAuditAccessToken_ApacheHttpClient() throws WxErrorException { + // 创建一个模拟实现,不实际调用HTTP请求 + WxCpServiceApacheHttpClientImpl service = new WxCpServiceApacheHttpClientImpl() { + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + // 验证配置是否正确使用 + WxCpConfigStorage storage = getWxCpConfigStorage(); + assertThat(storage.getMsgAuditSecret()).isEqualTo("testMsgAuditSecret"); + + // 模拟返回 token + return "mock_msg_audit_access_token"; + } + }; + service.setWxCpConfigStorage(config); + + String token = service.getMsgAuditAccessToken(false); + assertThat(token).isEqualTo("mock_msg_audit_access_token"); + } + + /** + * 测试 WxCpServiceHttpComponentsImpl 的 getMsgAuditAccessToken 方法 + */ + @Test + public void testGetMsgAuditAccessToken_HttpComponents() throws WxErrorException { + // 创建一个模拟实现,不实际调用HTTP请求 + WxCpServiceHttpComponentsImpl service = new WxCpServiceHttpComponentsImpl() { + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + // 验证配置是否正确使用 + WxCpConfigStorage storage = getWxCpConfigStorage(); + assertThat(storage.getMsgAuditSecret()).isEqualTo("testMsgAuditSecret"); + + // 模拟返回 token + return "mock_msg_audit_access_token"; + } + }; + service.setWxCpConfigStorage(config); + + String token = service.getMsgAuditAccessToken(false); + assertThat(token).isEqualTo("mock_msg_audit_access_token"); + } + + /** + * 测试 WxCpServiceOkHttpImpl 的 getMsgAuditAccessToken 方法 + */ + @Test + public void testGetMsgAuditAccessToken_OkHttp() throws WxErrorException { + // 创建一个模拟实现,不实际调用HTTP请求 + WxCpServiceOkHttpImpl service = new WxCpServiceOkHttpImpl() { + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + // 验证配置是否正确使用 + WxCpConfigStorage storage = getWxCpConfigStorage(); + assertThat(storage.getMsgAuditSecret()).isEqualTo("testMsgAuditSecret"); + + // 模拟返回 token + return "mock_msg_audit_access_token"; + } + }; + service.setWxCpConfigStorage(config); + + String token = service.getMsgAuditAccessToken(false); + assertThat(token).isEqualTo("mock_msg_audit_access_token"); + } + + /** + * 测试 WxCpServiceJoddHttpImpl 的 getMsgAuditAccessToken 方法 + */ + @Test + public void testGetMsgAuditAccessToken_JoddHttp() throws WxErrorException { + // 创建一个模拟实现,不实际调用HTTP请求 + WxCpServiceJoddHttpImpl service = new WxCpServiceJoddHttpImpl() { + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + // 验证配置是否正确使用 + WxCpConfigStorage storage = getWxCpConfigStorage(); + assertThat(storage.getMsgAuditSecret()).isEqualTo("testMsgAuditSecret"); + + // 模拟返回 token + return "mock_msg_audit_access_token"; + } + }; + service.setWxCpConfigStorage(config); + + String token = service.getMsgAuditAccessToken(false); + assertThat(token).isEqualTo("mock_msg_audit_access_token"); + } + + /** + * 创建一个用于测试的BaseWxCpServiceImpl实现, + * 模拟在msgAuditSecret未配置时抛出异常的行为 + */ + private BaseWxCpServiceImpl createTestService(WxCpConfigStorage config) { + return new BaseWxCpServiceImpl() { + @Override + public Object getRequestHttpClient() { + return null; + } + + @Override + public Object getRequestHttpProxy() { + return null; + } + + @Override + public HttpClientType getRequestType() { + return null; + } + + @Override + public String getAccessToken(boolean forceRefresh) throws WxErrorException { + return "test_access_token"; + } + + @Override + public String getMsgAuditAccessToken(boolean forceRefresh) throws WxErrorException { + // 使用会话存档secret获取access_token + String msgAuditSecret = getWxCpConfigStorage().getMsgAuditSecret(); + if (msgAuditSecret == null || msgAuditSecret.trim().isEmpty()) { + throw new WxErrorException("会话存档secret未配置"); + } + return "mock_token"; + } + + @Override + public void initHttp() { + } + + @Override + public WxCpConfigStorage getWxCpConfigStorage() { + return config; + } + }; + } + + /** + * 测试当 MsgAuditSecret 未配置时应该抛出异常 + */ + @Test + public void testGetMsgAuditAccessToken_WithoutSecret() { + config.setMsgAuditSecret(null); + BaseWxCpServiceImpl service = createTestService(config); + + // 验证当 secret 为 null 时抛出异常 + assertThatThrownBy(() -> service.getMsgAuditAccessToken(true)) + .isInstanceOf(WxErrorException.class) + .hasMessageContaining("会话存档secret未配置"); + } + + /** + * 测试当 MsgAuditSecret 为空字符串时应该抛出异常 + */ + @Test + public void testGetMsgAuditAccessToken_WithEmptySecret() { + config.setMsgAuditSecret(" "); + BaseWxCpServiceImpl service = createTestService(config); + + // 验证当 secret 为空字符串时抛出异常 + assertThatThrownBy(() -> service.getMsgAuditAccessToken(true)) + .isInstanceOf(WxErrorException.class) + .hasMessageContaining("会话存档secret未配置"); + } +}