| | |
| | | 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; |
| | |
| | | @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 |
| | |
| | | @Override |
| | | public int insertAccountUser(AccountUser accountUser) |
| | | { |
| | | accountUser.setPassword(SecurityUtils.encryptPassword(accountUser.getPassword())); |
| | | // C端注册无密码,密码为空时跳过加密,避免 encryptPassword(null) 抛 NPE |
| | | if (StringUtils.isNotEmpty(accountUser.getPassword())) |
| | | { |
| | | accountUser.setPassword(SecurityUtils.encryptPassword(accountUser.getPassword())); |
| | | } |
| | | return accountUserMapper.insertAccountUser(accountUser); |
| | | } |
| | | |
| | |
| | | { |
| | | 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("注册失败,请稍后重试"); |
| | | } |
| | | } |