|
@@ -12,8 +12,9 @@ import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.lang.Assert;
|
|
|
import cn.hutool.core.util.ObjUtil;
|
|
|
import cn.hutool.core.util.ReflectUtil;
|
|
|
+import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
|
|
|
+import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
|
|
|
import com.citu.framework.common.enums.CommonStatusEnum;
|
|
|
-import com.citu.framework.common.enums.UserTypeEnum;
|
|
|
import com.citu.framework.common.pojo.PageResult;
|
|
|
import com.citu.framework.common.util.cache.CacheUtils;
|
|
|
import com.citu.framework.common.util.http.HttpUtils;
|
|
@@ -26,8 +27,6 @@ import com.citu.module.system.dal.dataobject.social.SocialClientDO;
|
|
|
import com.citu.module.system.dal.mysql.social.SocialClientMapper;
|
|
|
import com.citu.module.system.dal.redis.RedisKeyConstants;
|
|
|
import com.citu.module.system.enums.social.SocialTypeEnum;
|
|
|
-import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
|
|
|
-import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
|
import com.google.common.cache.CacheLoader;
|
|
|
import com.google.common.cache.LoadingCache;
|
|
@@ -59,7 +58,6 @@ import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
|
|
|
import static com.citu.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
-import static com.citu.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
|
|
|
import static com.citu.framework.common.util.collection.MapUtils.findAndThen;
|
|
|
import static com.citu.framework.common.util.json.JsonUtils.toJsonString;
|
|
|
import static com.citu.module.system.enums.ErrorCodeConstants.*;
|
|
@@ -72,10 +70,10 @@ import static com.citu.module.system.enums.ErrorCodeConstants.*;
|
|
|
@Service
|
|
|
@Slf4j
|
|
|
public class SocialClientServiceImpl implements SocialClientService {
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 小程序码要打开的小程序版本
|
|
|
- *
|
|
|
+ * <p>
|
|
|
* 1. release:正式版
|
|
|
* 2. trial:体验版
|
|
|
* 3. develop:开发版
|
|
@@ -84,7 +82,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
public String envVersion;
|
|
|
/**
|
|
|
* 订阅消息跳转小程序类型
|
|
|
- *
|
|
|
+ * <p>
|
|
|
* 1. developer:开发版
|
|
|
* 2. trial:体验版
|
|
|
* 3. formal:正式版
|
|
@@ -103,10 +101,10 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它
|
|
|
/**
|
|
|
* 缓存 WxMpService 对象
|
|
|
- *
|
|
|
+ * <p>
|
|
|
* key:使用微信公众号的 appId + secret 拼接,即 {@link SocialClientDO} 的 clientId 和 clientSecret 属性。
|
|
|
* 为什么 key 使用这种格式?因为 {@link SocialClientDO} 在管理后台可以变更,通过这个 key 存储它的单例。
|
|
|
- *
|
|
|
+ * <p>
|
|
|
* 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。
|
|
|
*/
|
|
|
private final LoadingCache<String, WxMpService> wxMpServiceCache = CacheUtils.buildAsyncReloadingCache(
|
|
@@ -127,7 +125,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
private WxMaProperties wxMaProperties;
|
|
|
/**
|
|
|
* 缓存 WxMaService 对象
|
|
|
- *
|
|
|
+ * <p>
|
|
|
* 说明同 {@link #wxMpServiceCache} 变量
|
|
|
*/
|
|
|
private final LoadingCache<String, WxMaService> wxMaServiceCache = CacheUtils.buildAsyncReloadingCache(
|
|
@@ -146,18 +144,18 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
private SocialClientMapper socialClientMapper;
|
|
|
|
|
|
@Override
|
|
|
- public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
|
|
|
+ public String getAuthorizeUrl(String application, Integer socialType, Integer userType, String redirectUri) {
|
|
|
// 获得对应的 AuthRequest 实现
|
|
|
- AuthRequest authRequest = buildAuthRequest(socialType, userType);
|
|
|
+ AuthRequest authRequest = buildAuthRequest(application, socialType, userType);
|
|
|
// 生成跳转地址
|
|
|
String authorizeUri = authRequest.authorize(AuthStateUtils.createState());
|
|
|
return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state) {
|
|
|
+ public AuthUser getAuthUser(String application, Integer socialType, Integer userType, String code, String state) {
|
|
|
// 构建请求
|
|
|
- AuthRequest authRequest = buildAuthRequest(socialType, userType);
|
|
|
+ AuthRequest authRequest = buildAuthRequest(application, socialType, userType);
|
|
|
AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build();
|
|
|
// 执行请求
|
|
|
AuthResponse<?> authResponse = authRequest.login(authCallback);
|
|
@@ -173,16 +171,16 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
* 构建 AuthRequest 对象,支持多租户配置
|
|
|
*
|
|
|
* @param socialType 社交类型
|
|
|
- * @param userType 用户类型
|
|
|
+ * @param userType 用户类型
|
|
|
* @return AuthRequest 对象
|
|
|
*/
|
|
|
@VisibleForTesting
|
|
|
- AuthRequest buildAuthRequest(Integer socialType, Integer userType) {
|
|
|
+ AuthRequest buildAuthRequest(String application, Integer socialType, Integer userType) {
|
|
|
// 1. 先查找默认的配置项,从 application-*.yaml 中读取
|
|
|
AuthRequest request = authRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource());
|
|
|
Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType));
|
|
|
// 2. 查询 DB 的配置项,如果存在则进行覆盖
|
|
|
- SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(socialType, userType);
|
|
|
+ SocialClientDO client = socialClientMapper.selectBySocialApplicationAndTypeAndUserType(application, socialType, userType);
|
|
|
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
|
|
// 2.1 构造新的 AuthConfig 对象
|
|
|
AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(request, "config");
|
|
@@ -204,8 +202,8 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
|
|
|
@Override
|
|
|
@SneakyThrows
|
|
|
- public WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url) {
|
|
|
- WxMpService service = getWxMpService(userType);
|
|
|
+ public WxJsapiSignature createWxMpJsapiSignature(String application, Integer userType, String url) {
|
|
|
+ WxMpService service = getWxMpService(application, userType);
|
|
|
return service.createJsapiSignature(url);
|
|
|
}
|
|
|
|
|
@@ -216,9 +214,9 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
* @return WxMpService 对象
|
|
|
*/
|
|
|
@VisibleForTesting
|
|
|
- WxMpService getWxMpService(Integer userType) {
|
|
|
+ WxMpService getWxMpService(String application, Integer userType) {
|
|
|
// 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象
|
|
|
- SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
|
|
|
+ SocialClientDO client = socialClientMapper.selectBySocialApplicationAndTypeAndUserType(application,
|
|
|
SocialTypeEnum.WECHAT_MP.getType(), userType);
|
|
|
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
|
|
return wxMpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
|
|
@@ -230,7 +228,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
/**
|
|
|
* 创建 clientId + clientSecret 对应的 WxMpService 对象
|
|
|
*
|
|
|
- * @param clientId 微信公众号 appId
|
|
|
+ * @param clientId 微信公众号 appId
|
|
|
* @param clientSecret 微信公众号 secret
|
|
|
* @return WxMpService 对象
|
|
|
*/
|
|
@@ -251,8 +249,8 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
// =================== 微信小程序独有 ===================
|
|
|
|
|
|
@Override
|
|
|
- public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
|
|
|
- WxMaService service = getWxMaService(userType);
|
|
|
+ public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(String application, Integer userType, String phoneCode) {
|
|
|
+ WxMaService service = getWxMaService(application, userType);
|
|
|
try {
|
|
|
return service.getUserService().getPhoneNoInfo(phoneCode);
|
|
|
} catch (WxErrorException e) {
|
|
@@ -263,7 +261,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
|
|
|
@Override
|
|
|
public byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO) {
|
|
|
- WxMaService service = getWxMaService(UserTypeEnum.MEMBER.getValue());
|
|
|
+ WxMaService service = getWxMaService(reqVO.getApplication(), reqVO.getUserType());
|
|
|
try {
|
|
|
return service.getQrcodeService().createWxaCodeUnlimitBytes(
|
|
|
ObjUtil.defaultIfEmpty(reqVO.getScene(), SocialWxQrcodeReqDTO.SCENE),
|
|
@@ -281,10 +279,10 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- @Cacheable(cacheNames = RedisKeyConstants.WXA_SUBSCRIBE_TEMPLATE, key = "#userType",
|
|
|
+ @Cacheable(cacheNames = RedisKeyConstants.WXA_SUBSCRIBE_TEMPLATE, key = "#application+':'+#userType",
|
|
|
unless = "#result == null")
|
|
|
- public List<TemplateInfo> getSubscribeTemplateList(Integer userType) {
|
|
|
- WxMaService service = getWxMaService(userType);
|
|
|
+ public List<TemplateInfo> getSubscribeTemplateList(String application, Integer userType) {
|
|
|
+ WxMaService service = getWxMaService(application, userType);
|
|
|
try {
|
|
|
WxMaSubscribeService subscribeService = service.getSubscribeService();
|
|
|
return subscribeService.getTemplateList();
|
|
@@ -296,7 +294,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
|
|
|
@Override
|
|
|
public void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId) {
|
|
|
- WxMaService service = getWxMaService(reqDTO.getUserType());
|
|
|
+ WxMaService service = getWxMaService(reqDTO.getApplication(), reqDTO.getUserType());
|
|
|
try {
|
|
|
WxMaSubscribeService subscribeService = service.getSubscribeService();
|
|
|
subscribeService.sendSubscribeMsg(buildMessageSendReqDTO(reqDTO, templateId, openId));
|
|
@@ -335,9 +333,9 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
* @return WxMpService 对象
|
|
|
*/
|
|
|
@VisibleForTesting
|
|
|
- WxMaService getWxMaService(Integer userType) {
|
|
|
+ WxMaService getWxMaService(String application, Integer userType) {
|
|
|
// 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象
|
|
|
- SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
|
|
|
+ SocialClientDO client = socialClientMapper.selectBySocialApplicationAndTypeAndUserType(application,
|
|
|
SocialTypeEnum.WECHAT_MINI_APP.getType(), userType);
|
|
|
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
|
|
return wxMaServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
|
|
@@ -349,7 +347,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
/**
|
|
|
* 创建 clientId + clientSecret 对应的 WxMaService 对象
|
|
|
*
|
|
|
- * @param clientId 微信小程序 appId
|
|
|
+ * @param clientId 微信小程序 appId
|
|
|
* @param clientSecret 微信小程序 secret
|
|
|
* @return WxMaService 对象
|
|
|
*/
|
|
@@ -372,7 +370,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
@Override
|
|
|
public Long createSocialClient(SocialClientSaveReqVO createReqVO) {
|
|
|
// 校验重复
|
|
|
- validateSocialClientUnique(null, createReqVO.getUserType(), createReqVO.getSocialType());
|
|
|
+ validateSocialClientUnique(null, createReqVO.getApplication(), createReqVO.getUserType(), createReqVO.getSocialType());
|
|
|
|
|
|
// 插入
|
|
|
SocialClientDO client = BeanUtils.toBean(createReqVO, SocialClientDO.class);
|
|
@@ -385,7 +383,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
// 校验存在
|
|
|
validateSocialClientExists(updateReqVO.getId());
|
|
|
// 校验重复
|
|
|
- validateSocialClientUnique(updateReqVO.getId(), updateReqVO.getUserType(), updateReqVO.getSocialType());
|
|
|
+ validateSocialClientUnique(updateReqVO.getId(), updateReqVO.getApplication(), updateReqVO.getUserType(), updateReqVO.getSocialType());
|
|
|
|
|
|
// 更新
|
|
|
SocialClientDO updateObj = BeanUtils.toBean(updateReqVO, SocialClientDO.class);
|
|
@@ -407,17 +405,24 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 校验社交应用是否重复,需要保证 userType + socialType 唯一
|
|
|
+ * 校验社交应用是否重复,需要保证 application + userType + socialType 唯一
|
|
|
+ * <p>
|
|
|
+ * 原因是,不同端(userType)选择某个社交登录(socialType)时,需要通过 {@link #buildAuthRequest(String, Integer, Integer)} 构建对应的请求
|
|
|
*
|
|
|
- * 原因是,不同端(userType)选择某个社交登录(socialType)时,需要通过 {@link #buildAuthRequest(Integer, Integer)} 构建对应的请求
|
|
|
- *
|
|
|
- * @param id 编号
|
|
|
- * @param userType 用户类型
|
|
|
+ * @param id 编号
|
|
|
+ * @param userType 用户类型
|
|
|
* @param socialType 社交类型
|
|
|
*/
|
|
|
- private void validateSocialClientUnique(Long id, Integer userType, Integer socialType) {
|
|
|
- SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
|
|
|
- socialType, userType);
|
|
|
+ private void validateSocialClientUnique(Long id, String application, Integer userType, Integer socialType) {
|
|
|
+ SocialClientDO exist = socialClientMapper.selectByApplication(application);
|
|
|
+ if (null == id && null != exist) {
|
|
|
+ // 重复的application
|
|
|
+ throw exception(SOCIAL_CLIENT_UNIQUE);
|
|
|
+ }
|
|
|
+ if (null != exist && ObjUtil.notEqual(id, exist.getId())) {
|
|
|
+ throw exception(SOCIAL_CLIENT_UNIQUE);
|
|
|
+ }
|
|
|
+ SocialClientDO client = socialClientMapper.selectBySocialApplicationAndTypeAndUserType(application, socialType, userType);
|
|
|
if (client == null) {
|
|
|
return;
|
|
|
}
|
|
@@ -425,6 +430,7 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
|| ObjUtil.notEqual(id, client.getId())) { // 更新时,如果 id 不一致,说明重复
|
|
|
throw exception(SOCIAL_CLIENT_UNIQUE);
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -432,6 +438,11 @@ public class SocialClientServiceImpl implements SocialClientService {
|
|
|
return socialClientMapper.selectById(id);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public SocialClientDO getSocialClientByApplication(String application) {
|
|
|
+ return socialClientMapper.selectByApplication(application);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public PageResult<SocialClientDO> getSocialClientPage(SocialClientPageReqVO pageReqVO) {
|
|
|
return socialClientMapper.selectPage(pageReqVO);
|