mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-18 19:20:05 +08:00
saas:支持社交应用的多租户配置(wx 小程序)
This commit is contained in:
parent
6f757e5297
commit
979b484d7f
@ -19,7 +19,6 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确");
|
||||
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_004_003_001, "登录失败,账号被禁用");
|
||||
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_004_003_005, "未绑定账号,需要进行绑定");
|
||||
ErrorCode AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_004_003_006, "获得手机号失败");
|
||||
ErrorCode AUTH_MOBILE_USED = new ErrorCode(1_004_003_007, "手机号已经被使用");
|
||||
|
||||
// ========== 用户收件地址 1-004-004-000 ==========
|
||||
|
@ -43,10 +43,6 @@
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-weixin</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
|
@ -1,7 +1,5 @@
|
||||
package cn.iocoder.yudao.module.member.service.auth;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
@ -23,6 +21,7 @@ import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants;
|
||||
@ -61,9 +60,6 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
@Resource
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
|
||||
@Resource
|
||||
private WxMaService wxMaService;
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) {
|
||||
// 使用手机 + 密码,进行登录。
|
||||
@ -124,15 +120,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
@Override
|
||||
public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) {
|
||||
// 获得对应的手机号信息
|
||||
// TODO @芋艿:需要弱化微信小程序的依赖,通过 system 获取手机号
|
||||
WxMaPhoneNumberInfo phoneNumberInfo;
|
||||
try {
|
||||
phoneNumberInfo = wxMaService.getUserService().getPhoneNoInfo(reqVO.getPhoneCode());
|
||||
} catch (Exception exception) {
|
||||
throw exception(AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
|
||||
}
|
||||
SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
|
||||
UserTypeEnum.MEMBER.getValue(), reqVO.getPhoneCode());
|
||||
Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
|
||||
|
||||
// 获得获得注册用户
|
||||
MemberUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(), getClientIP(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal());
|
||||
MemberUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(),
|
||||
getClientIP(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal());
|
||||
Assert.notNull(user, "获取用户失败,结果为空");
|
||||
|
||||
// 绑定社交用户
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.system.api.social;
|
||||
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
|
||||
/**
|
||||
@ -21,7 +22,7 @@ public interface SocialClientApi {
|
||||
String getAuthorizeUrl(Integer type, Integer userType, String redirectUri);
|
||||
|
||||
/**
|
||||
* 创建微信 JS SDK 初始化所需的签名
|
||||
* 创建微信公众号 JS SDK 初始化所需的签名
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param url 访问的 URL 地址
|
||||
@ -29,4 +30,13 @@ public interface SocialClientApi {
|
||||
*/
|
||||
SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url);
|
||||
|
||||
/**
|
||||
* 获得微信小程序的手机信息
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param phoneCode 手机授权码
|
||||
* @return 手机信息
|
||||
*/
|
||||
SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.system.api.social.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 微信小程序的手机信息 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class SocialWxPhoneNumberInfoRespDTO {
|
||||
|
||||
/**
|
||||
* 用户绑定的手机号(国外手机号会有区号)
|
||||
*/
|
||||
private String phoneNumber;
|
||||
|
||||
/**
|
||||
* 没有区号的手机号
|
||||
*/
|
||||
private String purePhoneNumber;
|
||||
/**
|
||||
* 区号
|
||||
*/
|
||||
private String countryCode;
|
||||
|
||||
}
|
@ -119,6 +119,8 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1_002_018_001, "社交解绑失败,非当前用户绑定");
|
||||
ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_002, "社交授权失败,找不到对应的用户");
|
||||
|
||||
ErrorCode SOCIAL_APP_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_002_018_103, "获得手机号失败");
|
||||
|
||||
// ========== 系统敏感词 1-002-019-000 =========
|
||||
ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1_002_019_000, "系统敏感词在所有标签中都不存在");
|
||||
ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1_002_019_001, "系统敏感词已在标签中存在");
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.iocoder.yudao.module.system.api.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import cn.iocoder.yudao.module.system.convert.social.SocialClientConvert;
|
||||
import cn.iocoder.yudao.module.system.service.social.SocialClientService;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
@ -32,4 +34,10 @@ public class SocialClientApiImpl implements SocialClientApi {
|
||||
return SocialClientConvert.INSTANCE.convert(signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
|
||||
WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode);
|
||||
return SocialClientConvert.INSTANCE.convert(info);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.iocoder.yudao.module.system.convert.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
@ -12,4 +14,6 @@ public interface SocialClientConvert {
|
||||
|
||||
SocialWxJsapiSignatureRespDTO convert(WxJsapiSignature bean);
|
||||
|
||||
SocialWxPhoneNumberInfoRespDTO convert(WxMaPhoneNumberInfo bean);
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.system.service.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
import com.xingyuv.jushauth.model.AuthUser;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
@ -32,8 +33,10 @@ public interface SocialClientService {
|
||||
*/
|
||||
AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state);
|
||||
|
||||
// =================== 微信公众号独有 ===================
|
||||
|
||||
/**
|
||||
* 创建微信 JS SDK 初始化所需的签名
|
||||
* 创建微信公众号的 JS SDK 初始化所需的签名
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param url 访问的 URL 地址
|
||||
@ -41,4 +44,15 @@ public interface SocialClientService {
|
||||
*/
|
||||
WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url);
|
||||
|
||||
// =================== 微信小程序独有 ===================
|
||||
|
||||
/**
|
||||
* 获得微信小程序的手机信息
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param phoneCode 手机授权码
|
||||
* @return 手机信息
|
||||
*/
|
||||
WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode);
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
package cn.iocoder.yudao.module.system.service.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
@ -10,6 +14,7 @@ import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper;
|
||||
import cn.iocoder.yudao.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.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
@ -22,6 +27,7 @@ import com.xingyuv.jushauth.utils.AuthStateUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
|
||||
@ -35,6 +41,7 @@ import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_APP_WEIXIN_MINI_APP_PHONE_CODE_ERROR;
|
||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_AUTH_FAILURE;
|
||||
|
||||
/**
|
||||
@ -50,11 +57,11 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
private YudaoAuthRequestFactory yudaoAuthRequestFactory;
|
||||
|
||||
@Resource
|
||||
private WxMpService mpService;
|
||||
private WxMpService wxMpService;
|
||||
@Resource
|
||||
private WxMpProperties wxMpProperties;
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它
|
||||
@Resource
|
||||
private WxMpProperties mpProperties;
|
||||
/**
|
||||
* 缓存 WxMpService 对象
|
||||
*
|
||||
@ -63,14 +70,35 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
*
|
||||
* 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。
|
||||
*/
|
||||
private final LoadingCache<String, WxMpService> mpServiceCache = CacheUtils.buildAsyncReloadingCache(
|
||||
private final LoadingCache<String, WxMpService> wxMpServiceCache = CacheUtils.buildAsyncReloadingCache(
|
||||
Duration.ofSeconds(10L),
|
||||
new CacheLoader<String, WxMpService>() {
|
||||
|
||||
@Override
|
||||
public WxMpService load(String key) {
|
||||
String[] keys = key.split(":");
|
||||
return buildMpService(keys[0], keys[1]);
|
||||
return buildWxMpService(keys[0], keys[1]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Resource
|
||||
private WxMaService wxMaService;
|
||||
@Resource
|
||||
private WxMaProperties wxMaProperties;
|
||||
/**
|
||||
* 缓存 WxMaService 对象
|
||||
*
|
||||
* 说明同 {@link #wxMpServiceCache} 变量
|
||||
*/
|
||||
private final LoadingCache<String, WxMaService> wxMaServiceCache = CacheUtils.buildAsyncReloadingCache(
|
||||
Duration.ofSeconds(10L),
|
||||
new CacheLoader<String, WxMaService>() {
|
||||
|
||||
@Override
|
||||
public WxMaService load(String key) {
|
||||
String[] keys = key.split(":");
|
||||
return buildWxMaService(keys[0], keys[1]);
|
||||
}
|
||||
|
||||
});
|
||||
@ -129,28 +157,30 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
return request;
|
||||
}
|
||||
|
||||
// =================== 微信公众号独有 ===================
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url) {
|
||||
WxMpService mpService = buildMpService(userType);
|
||||
return mpService.createJsapiSignature(url);
|
||||
WxMpService service = getWxMpService(userType);
|
||||
return service.createJsapiSignature(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 clientId + clientSecret 对应的 WxMpService 对象
|
||||
* 获得 clientId + clientSecret 对应的 WxMpService 对象
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @return WxMpService 对象
|
||||
*/
|
||||
private WxMpService buildMpService(Integer userType) {
|
||||
private WxMpService getWxMpService(Integer userType) {
|
||||
// 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象
|
||||
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
|
||||
SocialTypeEnum.WECHAT_MP.getType(), userType);
|
||||
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
||||
return mpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
|
||||
return wxMpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
|
||||
}
|
||||
// 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMpService 对象
|
||||
return mpService;
|
||||
return wxMpService;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,11 +190,11 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
* @param clientSecret 微信公众号 secret
|
||||
* @return WxMpService 对象
|
||||
*/
|
||||
private WxMpService buildMpService(String clientId, String clientSecret) {
|
||||
private WxMpService buildWxMpService(String clientId, String clientSecret) {
|
||||
// 第一步,创建 WxMpRedisConfigImpl 对象
|
||||
WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl(
|
||||
new RedisTemplateWxRedisOps(stringRedisTemplate),
|
||||
mpProperties.getConfigStorage().getKeyPrefix());
|
||||
wxMpProperties.getConfigStorage().getKeyPrefix());
|
||||
configStorage.setAppId(clientId);
|
||||
configStorage.setSecret(clientSecret);
|
||||
|
||||
@ -174,4 +204,55 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
return service;
|
||||
}
|
||||
|
||||
// =================== 微信小程序独有 ===================
|
||||
|
||||
@Override
|
||||
public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode) {
|
||||
WxMaService service = getWxMaService(userType);
|
||||
try {
|
||||
return service.getUserService().getPhoneNoInfo(phoneCode);
|
||||
} catch (WxErrorException e) {
|
||||
log.error("[getPhoneNoInfo][userType({}) phoneCode({}) 获得手机号失败]", userType, phoneCode, e);
|
||||
throw exception(SOCIAL_APP_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 clientId + clientSecret 对应的 WxMpService 对象
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @return WxMpService 对象
|
||||
*/
|
||||
private WxMaService getWxMaService(Integer userType) {
|
||||
// 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象
|
||||
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
|
||||
SocialTypeEnum.WECHAT_MINI_APP.getType(), userType);
|
||||
if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
||||
return wxMaServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret());
|
||||
}
|
||||
// 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMaService 对象
|
||||
return wxMaService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 clientId + clientSecret 对应的 WxMaService 对象
|
||||
*
|
||||
* @param clientId 微信小程序 appId
|
||||
* @param clientSecret 微信小程序 secret
|
||||
* @return WxMaService 对象
|
||||
*/
|
||||
private WxMaService buildWxMaService(String clientId, String clientSecret) {
|
||||
// 第一步,创建 WxMaRedisBetterConfigImpl 对象
|
||||
WxMaRedisBetterConfigImpl configStorage = new WxMaRedisBetterConfigImpl(
|
||||
new RedisTemplateWxRedisOps(stringRedisTemplate),
|
||||
wxMaProperties.getConfigStorage().getKeyPrefix());
|
||||
configStorage.setAppid(clientId);
|
||||
configStorage.setSecret(clientSecret);
|
||||
|
||||
// 第二步,创建 WxMpService 对象
|
||||
WxMaService service = new WxMaServiceImpl();
|
||||
service.setWxMaConfig(configStorage);
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user