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 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 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); } }