hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java
@@ -1,11 +1,17 @@
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;
@@ -18,13 +24,27 @@
@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
@@ -65,7 +85,11 @@
    @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);
    }
@@ -85,4 +109,103 @@
    {
        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("注册失败,请稍后重试");
    }
}