@@ -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.RequestExecutor; | |||
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.result.WxMpCurrentAutoReplyInfo; | |||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||
@@ -304,42 +305,49 @@ public interface WxMpService { | |||
WxMpConfigStorage getWxMpConfigStorage(); | |||
/** | |||
* 注入 {@link WxMpConfigStorage} 的实现. | |||
* 设置 {@link WxMpConfigStorage} 的实现. 兼容老版本 | |||
*/ | |||
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} 值 | |||
* @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; | |||
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.JsonElement; | |||
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.crypto.SHA1; | |||
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.result.WxMpCurrentAutoReplyInfo; | |||
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||
import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; | |||
import me.chanjar.weixin.mp.bean.result.WxMpUser; | |||
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.concurrent.locks.Lock; | |||
/** | |||
* 基础实现类. | |||
@@ -63,12 +62,11 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH | |||
private WxMpWifiService wifiService = new WxMpWifiServiceImpl(this); | |||
private WxMpMarketingService marketingService = new WxMpMarketingServiceImpl(this); | |||
private Map<String, WxMpConfigStorage> wxMpConfigStoragePool; | |||
private Map<String, WxMpConfigStorage> configStorageMap; | |||
private int retrySleepMillis = 1000; | |||
private int maxRetryTimes = 5; | |||
@Override | |||
public boolean checkSignature(String timestamp, String nonce, String signature) { | |||
try { | |||
@@ -335,52 +333,62 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH | |||
@Override | |||
public WxMpConfigStorage getWxMpConfigStorage() { | |||
return wxMpConfigStoragePool.get(WxMpConfigStorageHolder.get()); | |||
return this.configStorageMap.get(WxMpConfigStorageHolder.get()); | |||
} | |||
@Override | |||
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 | |||
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 | |||
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(); | |||
} | |||
@Override | |||
public void addWxMpConfigStorage(String label, WxMpConfigStorage configStorages) { | |||
public void addConfigStorage(String mpId, WxMpConfigStorage configStorages) { | |||
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 | |||
public void removeWxMpConfigStorage(String label) { | |||
public void removeConfigStorage(String mpId) { | |||
synchronized (this) { | |||
wxMpConfigStoragePool.remove(label); | |||
this.configStorageMap.remove(mpId); | |||
} | |||
} | |||
@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; | |||
} | |||
log.error("无法找到对应【{}】的公众号配置信息,请核实!", mpId); | |||
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.bean.menu.WxMpGetSelfMenuInfoResult; | |||
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 Binary Wang | |||
*/ | |||
@Test(groups = "menuAPI") | |||
@Test | |||
@Guice(modules = ApiTestModule.class) | |||
public class WxMpMenuServiceImplTest { | |||
@@ -85,9 +87,6 @@ public class WxMpMenuServiceImplTest { | |||
"}"; | |||
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); | |||
} | |||
@@ -129,9 +128,7 @@ public class WxMpMenuServiceImplTest { | |||
" \"language\":\"zh_CN\"\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); | |||
} | |||
@@ -194,7 +191,7 @@ public class WxMpMenuServiceImplTest { | |||
System.out.println(wxMenu.toJson()); | |||
} | |||
@Test(dependsOnMethods = {"testMenuGet","testMenuCreate"}) | |||
@Test(dependsOnMethods = {"testMenuGet", "testMenuCreate"}) | |||
public void testMenuDelete() throws WxErrorException { | |||
this.wxService.getMenuService().menuDelete(); | |||
} | |||
@@ -2,8 +2,6 @@ package me.chanjar.weixin.mp.api.test; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.concurrent.locks.ReentrantLock; | |||
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.mp.api.WxMpConfigStorage; | |||
import me.chanjar.weixin.mp.api.WxMpService; | |||
import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl; | |||
public class ApiTestModule implements Module { | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
@@ -31,14 +28,13 @@ public class ApiTestModule implements Module { | |||
TestConfigStorage config = this.fromXml(TestConfigStorage.class, inputStream); | |||
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(WxMpService.class).toInstance(wxMpServiceMulti); | |||
binder.bind(WxMpService.class).toInstance(mpService); | |||
} catch (IOException e) { | |||
this.log.error(e.getMessage(), e); | |||
} | |||