package com.ruoyi.auth.service;
|
|
import java.util.regex.Pattern;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import com.ruoyi.auth.enums.SmsSceneEnum;
|
import com.ruoyi.common.core.constant.SecurityConstants;
|
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.enums.UserStatus;
|
import com.ruoyi.common.core.exception.ServiceException;
|
import com.ruoyi.common.core.utils.StringUtils;
|
import com.ruoyi.system.api.RemoteAccountUserService;
|
import com.ruoyi.system.api.domain.AccountMember;
|
import com.ruoyi.system.api.model.AccountResetPasswordRequest;
|
|
/**
|
* 忘记密码(重置密码)编排服务
|
*
|
* 身份校验:登录手机号 + 短信验证码(scene=reset);校验通过后由 ruoyi-user 侧 BCrypt 加密落库。
|
*
|
* @author hugray
|
*/
|
@Service
|
public class PasswordResetService
|
{
|
private static final Logger log = LoggerFactory.getLogger(PasswordResetService.class);
|
|
/** 手机号正则 */
|
private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
|
|
@Autowired
|
private SmsCodeService smsCodeService;
|
|
@Autowired
|
private RemoteAccountUserService remoteAccountUserService;
|
|
/**
|
* 重置密码
|
*
|
* @param phone 手机号(账号标识)
|
* @param code 短信验证码
|
* @param password 新密码(明文)
|
*/
|
public void resetPassword(String phone, String code, String password)
|
{
|
// STEP 1: 入参兜底校验(Controller 已校验,此处做服务层兜底)
|
if (StringUtils.isEmpty(phone) || !PHONE_PATTERN.matcher(phone).matches())
|
{
|
throw new ServiceException("请输入正确手机号");
|
}
|
if (StringUtils.isEmpty(code))
|
{
|
throw new ServiceException("请输入验证码");
|
}
|
if (StringUtils.isEmpty(password))
|
{
|
throw new ServiceException("请输入新密码");
|
}
|
|
// STEP 2: 先查询会员与状态,避免无谓消费验证码(与 phoneCodeLogin 口径一致)
|
AccountMember member = getRegisteredMember(phone);
|
checkMemberStatus(member);
|
|
// STEP 3: 校验并消费短信验证码(scene=reset,一次性)
|
smsCodeService.verifyAndConsume(phone, SmsSceneEnum.RESET.getCode(), code);
|
|
// STEP 4: 调用 ruoyi-user 内部接口落库(服务内 BCrypt 加密,只更新 password 一列)
|
AccountResetPasswordRequest request = new AccountResetPasswordRequest();
|
request.setPhone(phone);
|
request.setPassword(password);
|
R<Boolean> resetResult = remoteAccountUserService.resetPassword(request, SecurityConstants.INNER);
|
if (R.FAIL == resetResult.getCode())
|
{
|
throw new ServiceException(resetResult.getMsg());
|
}
|
log.info("[PASSWORD_RESET] reset success, phone={}", maskPhone(phone));
|
}
|
|
/**
|
* 按手机号查询会员,不存在时抛出「未注册」业务异常
|
*
|
* @param phone 手机号
|
* @return 会员信息
|
*/
|
private AccountMember getRegisteredMember(String phone)
|
{
|
R<AccountMember> memberResult = remoteAccountUserService.getMemberByPhone(phone, SecurityConstants.INNER);
|
if (R.FAIL == memberResult.getCode())
|
{
|
throw new ServiceException(memberResult.getMsg());
|
}
|
AccountMember member = memberResult.getData();
|
if (StringUtils.isNull(member))
|
{
|
throw new ServiceException("该手机号未注册,请先注册");
|
}
|
return member;
|
}
|
|
/**
|
* 校验会员状态,停用账号拒绝重置密码
|
*
|
* @param member C端会员信息
|
*/
|
private void checkMemberStatus(AccountMember member)
|
{
|
if (UserStatus.DISABLE.getCode().equals(member.getStatus()))
|
{
|
throw new ServiceException("账号已停用,请联系管理员");
|
}
|
}
|
|
/**
|
* 手机号脱敏,形如 138****0000
|
*/
|
private String maskPhone(String phone)
|
{
|
if (StringUtils.isEmpty(phone) || phone.length() < 7)
|
{
|
return phone;
|
}
|
return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
|
}
|
}
|