| @@ -5,6 +5,7 @@ import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | ||||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | import me.chanjar.weixin.common.util.http.RequestExecutor; | ||||
| import me.chanjar.weixin.common.util.http.RequestHttp; | import me.chanjar.weixin.common.util.http.RequestHttp; | ||||
| import me.chanjar.weixin.mp.api.impl.BaseWxMpServiceImpl; | |||||
| import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | ||||
| import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; | import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; | ||||
| import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | ||||
| @@ -304,42 +305,49 @@ public interface WxMpService { | |||||
| WxMpConfigStorage getWxMpConfigStorage(); | WxMpConfigStorage getWxMpConfigStorage(); | ||||
| /** | /** | ||||
| * 注入 {@link WxMpConfigStorage} 的实现. | |||||
| * 设置 {@link WxMpConfigStorage} 的实现. 兼容老版本 | |||||
| */ | */ | ||||
| void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider); | void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider); | ||||
| /** | /** | ||||
| * {@link Map<String, WxMpConfigStorage>} 加入新的 {@link WxMpConfigStorage},适用于动态添加新的微信应用 | |||||
| * @param configStorages | |||||
| * {@link Map<String, WxMpConfigStorage>} 加入新的 {@link WxMpConfigStorage},适用于动态添加新的微信公众号配置 | |||||
| * @param configStorage 新的微信配置 | |||||
| */ | */ | ||||
| void addWxMpConfigStorage(String label, WxMpConfigStorage configStorages); | |||||
| void addConfigStorage(String mpId, WxMpConfigStorage configStorage); | |||||
| /** | /** | ||||
| * 从{@link Map<String, WxMpConfigStorage>} 移除 {@link String label} 所对应的 {@link WxMpConfigStorage},适用于动态移除的微信应用 | |||||
| * @param label | |||||
| * 从{@link Map<String, WxMpConfigStorage>} 移除 {@link String mpId} 所对应的 {@link WxMpConfigStorage},适用于动态移除微信公众号配置 | |||||
| * @param mpId 对应公众号的标识 | |||||
| */ | */ | ||||
| void removeWxMpConfigStorage(String label); | |||||
| void removeConfigStorage(String mpId); | |||||
| /** | /** | ||||
| * 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String label} 值 | |||||
| * 随机采用一个{@link String lable}进行Http初始化操作 | |||||
| * @param configStorages | |||||
| * 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String mpId} 值 | |||||
| * 随机采用一个{@link String mpId}进行Http初始化操作 | |||||
| * @param configStorages WxMpConfigStorage map | |||||
| */ | */ | ||||
| void setMultiWxMpConfigStorage(Map<String, WxMpConfigStorage> configStorages); | |||||
| void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages); | |||||
| /** | /** | ||||
| * 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String label} 值 | * 注入多个 {@link WxMpConfigStorage} 的实现. 并为每个 {@link WxMpConfigStorage} 赋予不同的 {@link String label} 值 | ||||
| * @param configStorages | |||||
| * @param defaultInitLabel 设置一个{@link WxMpConfigStorage} 所对应的{@link String label}进行Http初始化 | |||||
| * @param configStorages WxMpConfigStorage map | |||||
| * @param defaultMpId 设置一个{@link WxMpConfigStorage} 所对应的{@link String mpId}进行Http初始化 | |||||
| */ | |||||
| void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages, String defaultMpId); | |||||
| /** | |||||
| * 进行相应的公众号切换 | |||||
| * @param mpId 公众号标识 | |||||
| * @return 切换是否成功 | |||||
| */ | */ | ||||
| void setMultiWxMpConfigStorage(Map<String, WxMpConfigStorage> configStorages, String defaultInitLabel); | |||||
| boolean switchover(String mpId); | |||||
| /** | /** | ||||
| * 进行相应的 WxApp 切换 | |||||
| * @param label | |||||
| * @return | |||||
| * 进行相应的公众号切换 | |||||
| * @param mpId 公众号标识 | |||||
| * @return 切换成功,则返回当前对象,方便链式调用,否则抛出异常 | |||||
| */ | */ | ||||
| boolean switchover(String label); | |||||
| WxMpService switchover1(String mpId); | |||||
| /** | /** | ||||
| * 返回客服接口方法实现类,以方便调用其各个接口. | * 返回客服接口方法实现类,以方便调用其各个接口. | ||||
| @@ -1,15 +1,7 @@ | |||||
| package me.chanjar.weixin.mp.api.impl; | package me.chanjar.weixin.mp.api.impl; | ||||
| import java.io.IOException; | |||||
| import java.util.HashMap; | |||||
| import java.util.concurrent.locks.Lock; | |||||
| import me.chanjar.weixin.mp.api.*; | |||||
| import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import com.google.common.collect.ImmutableMap; | |||||
| import com.google.common.collect.Maps; | |||||
| import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
| @@ -23,14 +15,21 @@ import me.chanjar.weixin.common.util.DataUtils; | |||||
| import me.chanjar.weixin.common.util.RandomUtils; | import me.chanjar.weixin.common.util.RandomUtils; | ||||
| import me.chanjar.weixin.common.util.crypto.SHA1; | import me.chanjar.weixin.common.util.crypto.SHA1; | ||||
| import me.chanjar.weixin.common.util.http.*; | import me.chanjar.weixin.common.util.http.*; | ||||
| import me.chanjar.weixin.mp.api.*; | |||||
| import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | ||||
| import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; | import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; | ||||
| import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | ||||
| import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; | import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; | ||||
| import me.chanjar.weixin.mp.bean.result.WxMpUser; | import me.chanjar.weixin.mp.bean.result.WxMpUser; | ||||
| import me.chanjar.weixin.mp.enums.TicketType; | import me.chanjar.weixin.mp.enums.TicketType; | ||||
| import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import java.io.IOException; | |||||
| import java.util.Map; | import java.util.Map; | ||||
| import java.util.concurrent.locks.Lock; | |||||
| /** | /** | ||||
| * 基础实现类. | * 基础实现类. | ||||
| @@ -63,12 +62,11 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH | |||||
| private WxMpWifiService wifiService = new WxMpWifiServiceImpl(this); | private WxMpWifiService wifiService = new WxMpWifiServiceImpl(this); | ||||
| private WxMpMarketingService marketingService = new WxMpMarketingServiceImpl(this); | private WxMpMarketingService marketingService = new WxMpMarketingServiceImpl(this); | ||||
| private Map<String, WxMpConfigStorage> wxMpConfigStoragePool; | |||||
| private Map<String, WxMpConfigStorage> configStorageMap; | |||||
| private int retrySleepMillis = 1000; | private int retrySleepMillis = 1000; | ||||
| private int maxRetryTimes = 5; | private int maxRetryTimes = 5; | ||||
| @Override | @Override | ||||
| public boolean checkSignature(String timestamp, String nonce, String signature) { | public boolean checkSignature(String timestamp, String nonce, String signature) { | ||||
| try { | try { | ||||
| @@ -335,52 +333,62 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH | |||||
| @Override | @Override | ||||
| public WxMpConfigStorage getWxMpConfigStorage() { | public WxMpConfigStorage getWxMpConfigStorage() { | ||||
| return wxMpConfigStoragePool.get(WxMpConfigStorageHolder.get()); | |||||
| return this.configStorageMap.get(WxMpConfigStorageHolder.get()); | |||||
| } | } | ||||
| @Override | @Override | ||||
| public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { | public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { | ||||
| Map<String, WxMpConfigStorage> map = new HashMap<>(1); | |||||
| map.put(WxMpConfigStorageHolder.get(), wxConfigProvider); | |||||
| setMultiWxMpConfigStorage(map, WxMpConfigStorageHolder.get()); | |||||
| final String defaultMpId = WxMpConfigStorageHolder.get(); | |||||
| this.setMultiConfigStorages(ImmutableMap.of(defaultMpId, wxConfigProvider), defaultMpId); | |||||
| } | } | ||||
| @Override | @Override | ||||
| public void setMultiWxMpConfigStorage(Map<String, WxMpConfigStorage> configStorages) { | |||||
| String randomKey = configStorages.keySet().iterator().next(); | |||||
| setMultiWxMpConfigStorage(configStorages, randomKey); | |||||
| public void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages) { | |||||
| this.setMultiConfigStorages(configStorages, configStorages.keySet().iterator().next()); | |||||
| } | } | ||||
| @Override | @Override | ||||
| public void setMultiWxMpConfigStorage(Map<String, WxMpConfigStorage> configStorages, String defaultInitLabel) { | |||||
| wxMpConfigStoragePool = configStorages; | |||||
| WxMpConfigStorageHolder.set(defaultInitLabel); | |||||
| public void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages, String defaultMpId) { | |||||
| this.configStorageMap = Maps.newHashMap(configStorages); | |||||
| WxMpConfigStorageHolder.set(defaultMpId); | |||||
| this.initHttp(); | this.initHttp(); | ||||
| } | } | ||||
| @Override | @Override | ||||
| public void addWxMpConfigStorage(String label, WxMpConfigStorage configStorages) { | |||||
| public void addConfigStorage(String mpId, WxMpConfigStorage configStorages) { | |||||
| synchronized (this) { | synchronized (this) { | ||||
| if (wxMpConfigStoragePool.containsKey(label)) { | |||||
| throw new RuntimeException("该label已存在,请重新设置一个label"); | |||||
| if (this.configStorageMap.containsKey(mpId)) { | |||||
| throw new RuntimeException("该公众号标识已存在,请更换其他标识!"); | |||||
| } | } | ||||
| wxMpConfigStoragePool.put(label, configStorages); | |||||
| this.configStorageMap.put(mpId, configStorages); | |||||
| } | } | ||||
| } | } | ||||
| @Override | @Override | ||||
| public void removeWxMpConfigStorage(String label) { | |||||
| public void removeConfigStorage(String mpId) { | |||||
| synchronized (this) { | synchronized (this) { | ||||
| wxMpConfigStoragePool.remove(label); | |||||
| this.configStorageMap.remove(mpId); | |||||
| } | } | ||||
| } | } | ||||
| @Override | @Override | ||||
| public boolean switchover(String label) { | |||||
| if (wxMpConfigStoragePool.containsKey(label)) { | |||||
| WxMpConfigStorageHolder.set(label); | |||||
| public WxMpService switchover1(String mpId) { | |||||
| if (this.configStorageMap.containsKey(mpId)) { | |||||
| WxMpConfigStorageHolder.set(mpId); | |||||
| return this; | |||||
| } | |||||
| throw new RuntimeException(String.format("无法找到对应【%s】的公众号配置信息,请核实!", mpId)); | |||||
| } | |||||
| @Override | |||||
| public boolean switchover(String mpId) { | |||||
| if (this.configStorageMap.containsKey(mpId)) { | |||||
| WxMpConfigStorageHolder.set(mpId); | |||||
| return true; | return true; | ||||
| } | } | ||||
| log.error("无法找到对应【{}】的公众号配置信息,请核实!", mpId); | |||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -0,0 +1,40 @@ | |||||
| package me.chanjar.weixin.mp.api.impl; | |||||
| import com.google.inject.Inject; | |||||
| import me.chanjar.weixin.common.error.WxErrorException; | |||||
| import me.chanjar.weixin.mp.api.WxMpService; | |||||
| import me.chanjar.weixin.mp.api.test.ApiTestModule; | |||||
| import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder; | |||||
| import org.testng.annotations.Guice; | |||||
| import org.testng.annotations.Test; | |||||
| import static org.assertj.core.api.Assertions.assertThat; | |||||
| import static org.testng.Assert.assertFalse; | |||||
| import static org.testng.Assert.assertTrue; | |||||
| /** | |||||
| * <pre> | |||||
| * Created by BinaryWang on 2019/3/29. | |||||
| * </pre> | |||||
| * | |||||
| * @author <a href="https://github.com/binarywang">Binary Wang</a> | |||||
| */ | |||||
| @Test | |||||
| @Guice(modules = ApiTestModule.class) | |||||
| public class BaseWxMpServiceImplTest { | |||||
| @Inject | |||||
| private WxMpService wxService; | |||||
| @Test | |||||
| public void testSwitchover() { | |||||
| assertTrue(this.wxService.switchover("another")); | |||||
| assertThat(WxMpConfigStorageHolder.get()).isEqualTo("another"); | |||||
| assertFalse(this.wxService.switchover("whatever")); | |||||
| } | |||||
| @Test | |||||
| public void testSwitchover1() throws WxErrorException { | |||||
| assertThat(this.wxService.switchover1("another").getAccessToken()).isNotEmpty(); | |||||
| assertThat(WxMpConfigStorageHolder.get()).isEqualTo("another"); | |||||
| } | |||||
| } | |||||
| @@ -9,9 +9,11 @@ import me.chanjar.weixin.mp.api.WxMpService; | |||||
| import me.chanjar.weixin.mp.api.test.ApiTestModule; | import me.chanjar.weixin.mp.api.test.ApiTestModule; | ||||
| import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; | import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; | ||||
| import me.chanjar.weixin.mp.bean.menu.WxMpMenu; | import me.chanjar.weixin.mp.bean.menu.WxMpMenu; | ||||
| import org.testng.annotations.*; | |||||
| import org.testng.annotations.DataProvider; | |||||
| import org.testng.annotations.Guice; | |||||
| import org.testng.annotations.Test; | |||||
| import static org.testng.Assert.*; | |||||
| import static org.testng.Assert.assertNotNull; | |||||
| /** | /** | ||||
| * 测试菜单 | * 测试菜单 | ||||
| @@ -19,7 +21,7 @@ import static org.testng.Assert.*; | |||||
| * @author chanjarster | * @author chanjarster | ||||
| * @author Binary Wang | * @author Binary Wang | ||||
| */ | */ | ||||
| @Test(groups = "menuAPI") | |||||
| @Test | |||||
| @Guice(modules = ApiTestModule.class) | @Guice(modules = ApiTestModule.class) | ||||
| public class WxMpMenuServiceImplTest { | public class WxMpMenuServiceImplTest { | ||||
| @@ -85,9 +87,6 @@ public class WxMpMenuServiceImplTest { | |||||
| "}"; | "}"; | ||||
| this.menuId = this.wxService.getMenuService().menuCreate(json); | this.menuId = this.wxService.getMenuService().menuCreate(json); | ||||
| if (this.wxService.switchover("test-1")) { | |||||
| this.menuId = this.wxService.getMenuService().menuCreate(json); | |||||
| } | |||||
| System.out.println(this.menuId); | System.out.println(this.menuId); | ||||
| } | } | ||||
| @@ -129,9 +128,7 @@ public class WxMpMenuServiceImplTest { | |||||
| " \"language\":\"zh_CN\"\n" + | " \"language\":\"zh_CN\"\n" + | ||||
| " }\n" + | " }\n" + | ||||
| "}"; | "}"; | ||||
| if (this.wxService.switchover("test-1")) { | |||||
| this.menuId = this.wxService.getMenuService().menuCreate(json); | |||||
| } | |||||
| this.menuId = this.wxService.getMenuService().menuCreate(json); | |||||
| System.out.println(this.menuId); | System.out.println(this.menuId); | ||||
| } | } | ||||
| @@ -194,7 +191,7 @@ public class WxMpMenuServiceImplTest { | |||||
| System.out.println(wxMenu.toJson()); | System.out.println(wxMenu.toJson()); | ||||
| } | } | ||||
| @Test(dependsOnMethods = {"testMenuGet","testMenuCreate"}) | |||||
| @Test(dependsOnMethods = {"testMenuGet", "testMenuCreate"}) | |||||
| public void testMenuDelete() throws WxErrorException { | public void testMenuDelete() throws WxErrorException { | ||||
| this.wxService.getMenuService().menuDelete(); | this.wxService.getMenuService().menuDelete(); | ||||
| } | } | ||||
| @@ -2,8 +2,6 @@ package me.chanjar.weixin.mp.api.test; | |||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import java.util.concurrent.locks.ReentrantLock; | import java.util.concurrent.locks.ReentrantLock; | ||||
| import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl; | import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl; | ||||
| @@ -16,7 +14,6 @@ import com.thoughtworks.xstream.XStream; | |||||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | import me.chanjar.weixin.common.util.xml.XStreamInitializer; | ||||
| import me.chanjar.weixin.mp.api.WxMpConfigStorage; | import me.chanjar.weixin.mp.api.WxMpConfigStorage; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
| import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl; | |||||
| public class ApiTestModule implements Module { | public class ApiTestModule implements Module { | ||||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | private final Logger log = LoggerFactory.getLogger(this.getClass()); | ||||
| @@ -31,14 +28,13 @@ public class ApiTestModule implements Module { | |||||
| TestConfigStorage config = this.fromXml(TestConfigStorage.class, inputStream); | TestConfigStorage config = this.fromXml(TestConfigStorage.class, inputStream); | ||||
| config.setAccessTokenLock(new ReentrantLock()); | config.setAccessTokenLock(new ReentrantLock()); | ||||
| WxMpService wxMpServiceMulti = new WxMpServiceHttpClientImpl(); | |||||
| WxMpService mpService = new WxMpServiceHttpClientImpl(); | |||||
| // TODO 多WxAppId | |||||
| wxMpServiceMulti.setWxMpConfigStorage(config); | |||||
| wxMpServiceMulti.addWxMpConfigStorage("test-1", config); | |||||
| mpService.setWxMpConfigStorage(config); | |||||
| mpService.addConfigStorage("another", config); | |||||
| binder.bind(WxMpConfigStorage.class).toInstance(config); | binder.bind(WxMpConfigStorage.class).toInstance(config); | ||||
| binder.bind(WxMpService.class).toInstance(wxMpServiceMulti); | |||||
| binder.bind(WxMpService.class).toInstance(mpService); | |||||
| } catch (IOException e) { | } catch (IOException e) { | ||||
| this.log.error(e.getMessage(), e); | this.log.error(e.getMessage(), e); | ||||
| } | } | ||||