mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-06-23 08:39:46 +08:00
公共登陆注册方法V2
This commit is contained in:
@@ -277,6 +277,253 @@ public class MemberService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 公共登录注册方法 V2
|
||||
*
|
||||
* <p>该方法与 {@link #memberRegisterAndLogin(MemberRegisterAndLoginDTO)} 保持相同业务逻辑,
|
||||
* 但将流程拆成更清晰的几个步骤:校验请求、获取锁、查找或创建会员、同步第三方身份、生成 token。</p>
|
||||
*
|
||||
* @param dto 登录/注册入参
|
||||
* @return 服务端生成的会员 token
|
||||
*/
|
||||
protected String memberRegisterAndLoginV2(MemberRegisterAndLoginDTO dto) {
|
||||
String phoneNumber = dto.getMobileNumber();
|
||||
String firstLevelMerchantId = dto.getFirstLevelMerchantId();
|
||||
log.info("公共登录注册方法V2, phoneNumber:{}, firstLevelMerchantId:{}, openId:{}", phoneNumber, firstLevelMerchantId, dto.getOpenId());
|
||||
|
||||
// 1. 先做参数校验,尽早拦截无效请求。
|
||||
validateMemberRegisterAndLoginV2Request(dto);
|
||||
|
||||
// 2. 使用“手机号 + 一级运营商”作为锁粒度,串行化同一会员的注册/登录流程。
|
||||
String lockKey = buildMemberRegisterAndLoginLockKey(phoneNumber, firstLevelMerchantId);
|
||||
String requestId = IdUtils.fastUUID();
|
||||
Boolean isLock = false;
|
||||
try {
|
||||
// 3. 获取锁,防止并发下重复注册或重复创建钱包。
|
||||
isLock = acquireMemberRegisterAndLoginLock(lockKey, requestId, phoneNumber, firstLevelMerchantId);
|
||||
|
||||
// 4. 会员不存在就静默注册,已存在则按请求来源同步 openId / buyerId。
|
||||
MemberBasicInfo memberBasicInfo = findOrCreateMemberForRegisterAndLoginV2(dto);
|
||||
|
||||
// 5. 最终统一生成会员 token 返回前端。
|
||||
return JWTUtils.createMemberToken(memberBasicInfo.getMemberId(), memberBasicInfo.getNickName());
|
||||
} finally {
|
||||
releaseMemberRegisterAndLoginLock(lockKey, requestId, phoneNumber, firstLevelMerchantId, isLock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 V2 登录/注册请求中的关键参数。
|
||||
*
|
||||
* @param dto 登录/注册入参
|
||||
*/
|
||||
private void validateMemberRegisterAndLoginV2Request(MemberRegisterAndLoginDTO dto) {
|
||||
if (StringUtils.isBlank(dto.getMobileNumber())) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_GET_MOBILE_NUMBER_BY_CODE_ERROR);
|
||||
}
|
||||
if (StringUtils.isBlank(dto.getFirstLevelMerchantId())) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_GET_MERCHANT_ID_BY_APP_ID_ERROR);
|
||||
}
|
||||
if (isWechatLiteRequest(dto) && StringUtils.isBlank(dto.getOpenId())) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_OPEN_ID_IS_NULL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成登录/注册流程使用的分布式锁 key。
|
||||
*
|
||||
* @param phoneNumber 手机号
|
||||
* @param firstLevelMerchantId 一级运营商 ID
|
||||
* @return 分布式锁 key
|
||||
*/
|
||||
private String buildMemberRegisterAndLoginLockKey(String phoneNumber, String firstLevelMerchantId) {
|
||||
return CacheConstants.USER_APP_REGISTER + phoneNumber + ":" + firstLevelMerchantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录/注册流程的分布式锁,避免同一会员并发注册。
|
||||
*
|
||||
* @param lockKey 锁 key
|
||||
* @param requestId 当前请求唯一标识
|
||||
* @param phoneNumber 手机号
|
||||
* @param firstLevelMerchantId 一级运营商 ID
|
||||
* @return 是否成功获取锁
|
||||
*/
|
||||
private boolean acquireMemberRegisterAndLoginLock(String lockKey, String requestId, String phoneNumber, String firstLevelMerchantId) {
|
||||
boolean isLock = redisCache.lock(lockKey, requestId, 60);
|
||||
if (!isLock) {
|
||||
log.warn("获取注册锁失败,可能有并发请求正在处理, phoneNumber:{}, merchantId:{}", phoneNumber, firstLevelMerchantId);
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_MEMBER_REGISTER_AND_LOGIN_PROCESSING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放登录/注册流程的分布式锁。
|
||||
*
|
||||
* @param lockKey 锁 key
|
||||
* @param requestId 当前请求唯一标识
|
||||
* @param phoneNumber 手机号
|
||||
* @param firstLevelMerchantId 一级运营商 ID
|
||||
* @param isLock 是否持有锁
|
||||
*/
|
||||
private void releaseMemberRegisterAndLoginLock(String lockKey, String requestId, String phoneNumber, String firstLevelMerchantId, Boolean isLock) {
|
||||
if (!Boolean.TRUE.equals(isLock)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object lockValue = redisCache.getCacheObject(lockKey);
|
||||
if (lockValue != null && requestId.equals(lockValue.toString())) {
|
||||
redisCache.unLock(lockKey);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("释放注册锁失败, phoneNumber:{}, merchantId:{}, error:{}", phoneNumber, firstLevelMerchantId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机号和运营商查询会员,不存在则静默注册,存在则同步第三方身份信息。
|
||||
*
|
||||
* @param dto 登录/注册入参
|
||||
* @return 已存在或新创建的会员信息
|
||||
*/
|
||||
private MemberBasicInfo findOrCreateMemberForRegisterAndLoginV2(MemberRegisterAndLoginDTO dto) {
|
||||
MemberBasicInfo memberBasicInfo = memberBasicInfoService.selectInfoByMobileNumber(dto.getMobileNumber(), dto.getFirstLevelMerchantId());
|
||||
if (Objects.isNull(memberBasicInfo)) {
|
||||
return registerMemberForRegisterAndLoginV2(dto);
|
||||
}
|
||||
syncMemberThirdPartyIdentityForRegisterAndLoginV2(memberBasicInfo, dto);
|
||||
return memberBasicInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行静默注册流程,并在唯一索引冲突时回查已创建的会员。
|
||||
*
|
||||
* @param dto 登录/注册入参
|
||||
* @return 注册完成后的会员信息
|
||||
*/
|
||||
private MemberBasicInfo registerMemberForRegisterAndLoginV2(MemberRegisterAndLoginDTO dto) {
|
||||
MemberBasicInfo memberBasicInfo = buildNewMemberForRegisterAndLoginV2(dto);
|
||||
MemberTransactionDTO memberTransactionDTO = buildMemberTransactionForRegisterAndLoginV2(memberBasicInfo, dto.getFirstLevelMerchantId());
|
||||
try {
|
||||
transactionService.createMember(memberTransactionDTO);
|
||||
return memberBasicInfo;
|
||||
} catch (DuplicateKeyException e) {
|
||||
return reloadMemberAfterDuplicateKeyForRegisterAndLoginV2(dto);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个新的会员基础信息对象。
|
||||
*
|
||||
* @param dto 登录/注册入参
|
||||
* @return 待入库的会员基础信息
|
||||
*/
|
||||
private MemberBasicInfo buildNewMemberForRegisterAndLoginV2(MemberRegisterAndLoginDTO dto) {
|
||||
String memberId = generateNewMemberId();
|
||||
MemberBasicInfo memberBasicInfo = new MemberBasicInfo();
|
||||
memberBasicInfo.setStatus(Constants.ONE);
|
||||
memberBasicInfo.setMemberId(memberId);
|
||||
memberBasicInfo.setNickName("会员" + memberId);
|
||||
memberBasicInfo.setMobileNumber(dto.getMobileNumber());
|
||||
memberBasicInfo.setMerchantId(Long.valueOf(dto.getFirstLevelMerchantId()));
|
||||
fillMemberThirdPartyIdentityForRegisterAndLoginV2(memberBasicInfo, dto);
|
||||
return memberBasicInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装会员注册事务对象,必要时附带创建钱包信息。
|
||||
*
|
||||
* @param memberBasicInfo 会员基础信息
|
||||
* @param firstLevelMerchantId 一级运营商 ID
|
||||
* @return 注册事务对象
|
||||
*/
|
||||
private MemberTransactionDTO buildMemberTransactionForRegisterAndLoginV2(MemberBasicInfo memberBasicInfo, String firstLevelMerchantId) {
|
||||
MemberTransactionDTO memberTransactionDTO = new MemberTransactionDTO();
|
||||
memberTransactionDTO.setMemberBasicInfo(memberBasicInfo);
|
||||
if (MerchantUtils.isXiXiaoMerchant(firstLevelMerchantId)) {
|
||||
MemberWalletInfo memberWalletInfo = MemberWalletInfo.builder()
|
||||
.memberId(memberBasicInfo.getMemberId())
|
||||
.merchantId(MerchantUtils.XIXIAO_MERCHANT_ID)
|
||||
.walletCode(memberBasicInfoService.generateWalletCode())
|
||||
.build();
|
||||
memberTransactionDTO.setMemberWalletInfo(memberWalletInfo);
|
||||
}
|
||||
return memberTransactionDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在注册发生唯一索引冲突时,回查已经被并发请求创建好的会员。
|
||||
*
|
||||
* @param dto 登录/注册入参
|
||||
* @return 已存在的会员信息
|
||||
*/
|
||||
private MemberBasicInfo reloadMemberAfterDuplicateKeyForRegisterAndLoginV2(MemberRegisterAndLoginDTO dto) {
|
||||
log.warn("会员注册时检测到唯一索引冲突,重新查询已存在的会员, phoneNumber:{}, merchantId:{}", dto.getMobileNumber(), dto.getFirstLevelMerchantId());
|
||||
MemberBasicInfo memberBasicInfo = memberBasicInfoService.selectInfoByMobileNumber(dto.getMobileNumber(), dto.getFirstLevelMerchantId());
|
||||
if (memberBasicInfo == null) {
|
||||
log.error("唯一索引冲突后重新查询会员信息为空, phoneNumber:{}, merchantId:{}", dto.getMobileNumber(), dto.getFirstLevelMerchantId());
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_MEMBER_REGISTER_AND_LOGIN_ERROR);
|
||||
}
|
||||
return memberBasicInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步已有会员的第三方身份标识。
|
||||
*
|
||||
* @param memberBasicInfo 已存在会员
|
||||
* @param dto 登录/注册入参
|
||||
*/
|
||||
private void syncMemberThirdPartyIdentityForRegisterAndLoginV2(MemberBasicInfo memberBasicInfo, MemberRegisterAndLoginDTO dto) {
|
||||
boolean needUpdate = false;
|
||||
if (isWechatLiteRequest(dto) && !StringUtils.equals(memberBasicInfo.getOpenId(), dto.getOpenId())) {
|
||||
memberBasicInfo.setOpenId(dto.getOpenId());
|
||||
needUpdate = true;
|
||||
}
|
||||
if (isAlipayLiteRequest(dto) && !StringUtils.equals(memberBasicInfo.getBuyerId(), dto.getBuyerId())) {
|
||||
memberBasicInfo.setBuyerId(dto.getBuyerId());
|
||||
needUpdate = true;
|
||||
}
|
||||
if (needUpdate) {
|
||||
memberBasicInfoService.updateMemberBasicInfo(memberBasicInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在新建会员时,根据请求来源写入第三方身份标识。
|
||||
*
|
||||
* @param memberBasicInfo 待入库会员
|
||||
* @param dto 登录/注册入参
|
||||
*/
|
||||
private void fillMemberThirdPartyIdentityForRegisterAndLoginV2(MemberBasicInfo memberBasicInfo, MemberRegisterAndLoginDTO dto) {
|
||||
if (isWechatLiteRequest(dto) && StringUtils.isNotBlank(dto.getOpenId())) {
|
||||
memberBasicInfo.setOpenId(dto.getOpenId());
|
||||
}
|
||||
if (isAlipayLiteRequest(dto) && StringUtils.isNotBlank(dto.getBuyerId())) {
|
||||
memberBasicInfo.setBuyerId(dto.getBuyerId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前请求是否来自微信小程序。
|
||||
*
|
||||
* @param dto 登录/注册入参
|
||||
* @return 是否为微信小程序请求
|
||||
*/
|
||||
private boolean isWechatLiteRequest(MemberRegisterAndLoginDTO dto) {
|
||||
return AdapayPayChannelEnum.WX_LITE.getValue().equals(dto.getRequestSource());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前请求是否来自支付宝小程序。
|
||||
*
|
||||
* @param dto 登录/注册入参
|
||||
* @return 是否为支付宝小程序请求
|
||||
*/
|
||||
private boolean isAlipayLiteRequest(MemberRegisterAndLoginDTO dto) {
|
||||
return AdapayPayChannelEnum.ALIPAY_LITE.getValue().equals(dto.getRequestSource());
|
||||
}
|
||||
|
||||
private String generateNewMemberId() {
|
||||
while (true) {
|
||||
String memberId = IdUtils.getMemberId();
|
||||
|
||||
Reference in New Issue
Block a user