package com.ruoyi.user.service.impl;
|
|
import java.security.SecureRandom;
|
import java.util.List;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import com.ruoyi.common.core.constant.UserConstants;
|
import com.ruoyi.common.core.exception.ServiceException;
|
import com.ruoyi.common.core.utils.StringUtils;
|
import com.ruoyi.common.security.utils.SecurityUtils;
|
import com.ruoyi.user.config.UniversalCodeProperties;
|
import com.ruoyi.user.constant.ReferralConstants;
|
import com.ruoyi.user.domain.AccountUser;
|
import com.ruoyi.user.mapper.AccountUserMapper;
|
import com.ruoyi.user.service.IAccountUserService;
|
|
/**
|
* 会员用户 业务层实现
|
*
|
* @author ruoyi
|
*/
|
@Service
|
public class AccountUserServiceImpl implements IAccountUserService
|
{
|
private static final Logger log = LoggerFactory.getLogger(AccountUserServiceImpl.class);
|
|
@Autowired
|
private AccountUserMapper accountUserMapper;
|
|
@Autowired
|
private UniversalCodeProperties universalCodeProperties;
|
|
/** 推荐码随机源(SecureRandom 线程安全,避免每次创建) */
|
private static final SecureRandom RANDOM = new SecureRandom();
|
|
@Override
|
public AccountUser selectAccountUserById(Long userId)
|
{
|
return accountUserMapper.selectAccountUserById(userId);
|
}
|
|
@Override
|
public AccountUser selectAccountUserByPhone(String phone)
|
{
|
return accountUserMapper.selectAccountUserByPhone(phone);
|
}
|
|
@Override
|
public List<AccountUser> selectAccountUserList(AccountUser accountUser)
|
{
|
return accountUserMapper.selectAccountUserList(accountUser);
|
}
|
|
@Override
|
public boolean checkUsernameUnique(AccountUser accountUser)
|
{
|
Long userId = StringUtils.isNull(accountUser.getUserId()) ? -1L : accountUser.getUserId();
|
AccountUser info = accountUserMapper.selectAccountUserByUsername(accountUser.getUserName());
|
if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
|
{
|
return UserConstants.NOT_UNIQUE;
|
}
|
return UserConstants.UNIQUE;
|
}
|
|
@Override
|
public boolean checkPhoneUnique(AccountUser accountUser)
|
{
|
// 手机号非必填,为空时不做唯一性校验,否则会与库里其它空手机号记录相撞
|
if (StringUtils.isEmpty(accountUser.getPhone()))
|
{
|
return UserConstants.UNIQUE;
|
}
|
Long userId = StringUtils.isNull(accountUser.getUserId()) ? -1L : accountUser.getUserId();
|
AccountUser info = accountUserMapper.selectAccountUserByPhone(accountUser.getPhone());
|
if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
|
{
|
return UserConstants.NOT_UNIQUE;
|
}
|
return UserConstants.UNIQUE;
|
}
|
|
@Override
|
public int insertAccountUser(AccountUser accountUser)
|
{
|
// C端注册无密码,密码为空时跳过加密,避免 encryptPassword(null) 抛 NPE
|
if (StringUtils.isNotEmpty(accountUser.getPassword()))
|
{
|
accountUser.setPassword(SecurityUtils.encryptPassword(accountUser.getPassword()));
|
}
|
return accountUserMapper.insertAccountUser(accountUser);
|
}
|
|
@Override
|
public int updateAccountUser(AccountUser accountUser)
|
{
|
// 不为空才加密;为空时保持 null,mapper 的 <if> 会跳过该列,原密码不受影响
|
if (StringUtils.isNotEmpty(accountUser.getPassword()))
|
{
|
accountUser.setPassword(SecurityUtils.encryptPassword(accountUser.getPassword()));
|
}
|
return accountUserMapper.updateAccountUser(accountUser);
|
}
|
|
@Override
|
public int deleteAccountUserByIds(Long[] userIds)
|
{
|
return accountUserMapper.deleteAccountUserByIds(userIds);
|
}
|
|
@Override
|
public AccountUser registerMember(String phone, String referralCode)
|
{
|
// STEP 1: 二次校验手机号未注册(防并发 / 防重复提交,网关侧已用分布式锁兜底)
|
if (StringUtils.isNotNull(accountUserMapper.selectAccountUserByPhone(phone)))
|
{
|
throw new ServiceException("手机号已注册,请前往登录");
|
}
|
// STEP 2: 解析推荐人——命中通用推荐码则不绑定推荐人,否则按码定位并校验
|
Long referrerUserId = resolveReferrerUserId(phone, referralCode);
|
// STEP 3: 生成全局唯一的本人推荐码(库内查重,冲突重试)
|
String myReferralCode = generateUniqueReferralCode();
|
// STEP 4: 组装会员信息(C端无密码,账号=手机号),落库(userId 回填)
|
AccountUser accountUser = new AccountUser();
|
accountUser.setUserName(phone);
|
accountUser.setPhone(phone);
|
accountUser.setNickname(phone);
|
accountUser.setStatus(UserConstants.NORMAL);
|
accountUser.setMyReferralCode(myReferralCode);
|
accountUser.setReferrerUserId(referrerUserId);
|
accountUser.setCreateBy(phone);
|
if (insertAccountUser(accountUser) <= 0)
|
{
|
throw new ServiceException("注册失败,请稍后重试");
|
}
|
return accountUser;
|
}
|
|
/**
|
* 解析注册时的推荐人用户ID
|
*
|
* 命中通用推荐码(测试便利)时跳过推荐人查询与校验,返回 null(不绑定推荐人);
|
* 否则按推荐码定位推荐人,不存在或已停用则拒绝注册。
|
*
|
* @param phone 注册手机号(仅用于日志留痕,脱敏输出)
|
* @param referralCode 推荐码
|
* @return 推荐人用户ID;命中通用码时为 null
|
*/
|
private Long resolveReferrerUserId(String phone, String referralCode)
|
{
|
String universalCode = universalCodeProperties.getUniversalReferralCode();
|
// 命中通用推荐码:不绑定推荐人
|
if (StringUtils.isNotEmpty(universalCode) && universalCode.equals(referralCode))
|
{
|
log.warn("[REFERRAL] universal code hit, not binding referrer, phone={}", maskPhone(phone));
|
return null;
|
}
|
// 按推荐码定位推荐人,不存在或已停用则拒绝注册
|
AccountUser referrer = accountUserMapper.selectAccountUserByReferralCode(referralCode);
|
if (StringUtils.isNull(referrer))
|
{
|
throw new ServiceException("推荐人码无效");
|
}
|
if (UserConstants.USER_DISABLE.equals(referrer.getStatus()))
|
{
|
throw new ServiceException("推荐人账号异常");
|
}
|
return referrer.getUserId();
|
}
|
|
/**
|
* 手机号脱敏,形如 138****0000
|
*
|
* @param phone 手机号
|
* @return 脱敏后的手机号
|
*/
|
private String maskPhone(String phone)
|
{
|
if (StringUtils.isEmpty(phone) || phone.length() < 7)
|
{
|
return phone;
|
}
|
return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
|
}
|
|
/**
|
* 生成库内唯一的推荐码
|
*
|
* @return 8 位大写字母数字推荐码
|
*/
|
private String generateUniqueReferralCode()
|
{
|
String chars = ReferralConstants.CODE_CHARS;
|
for (int retry = 0; retry < ReferralConstants.GEN_MAX_RETRY; retry++)
|
{
|
StringBuilder sb = new StringBuilder(ReferralConstants.CODE_LENGTH);
|
for (int i = 0; i < ReferralConstants.CODE_LENGTH; i++)
|
{
|
sb.append(chars.charAt(RANDOM.nextInt(chars.length())));
|
}
|
String code = sb.toString();
|
if (StringUtils.isNull(accountUserMapper.selectAccountUserByReferralCode(code)))
|
{
|
return code;
|
}
|
}
|
throw new ServiceException("注册失败,请稍后重试");
|
}
|
}
|