增加忘记密码
This commit is contained in:
parent
47b6f6a8a5
commit
5ad0473e03
|
|
@ -24,11 +24,20 @@ public class ValidationUtils {
|
|||
|
||||
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
|
||||
|
||||
private static final Pattern PATTERN_EMAIL = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
|
||||
|
||||
public static boolean isMobile(String mobile) {
|
||||
return StringUtils.hasText(mobile)
|
||||
&& PATTERN_MOBILE.matcher(mobile).matches();
|
||||
}
|
||||
|
||||
public static boolean isEmail(String email) {
|
||||
|
||||
return StringUtils.hasText(email)
|
||||
&& PATTERN_EMAIL.matcher(email).matches();
|
||||
}
|
||||
|
||||
|
||||
public static boolean isURL(String url) {
|
||||
return StringUtils.hasText(url)
|
||||
&& PATTERN_URL.matcher(url).matches();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package cn.hangtag.framework.common.validation;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({
|
||||
ElementType.METHOD,
|
||||
ElementType.FIELD,
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.CONSTRUCTOR,
|
||||
ElementType.PARAMETER,
|
||||
ElementType.TYPE_USE
|
||||
})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Constraint(
|
||||
validatedBy = EmailValidator.class
|
||||
)
|
||||
public @interface Email {
|
||||
|
||||
String message() default "邮箱格式不正确";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package cn.hangtag.framework.common.validation;
|
||||
|
||||
import cn.hangtag.framework.common.util.validation.ValidationUtils;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public class EmailValidator implements ConstraintValidator<Email, String> {
|
||||
|
||||
@Override
|
||||
public void initialize(Email annotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||
// 如果手机号为空,默认不校验,即校验通过
|
||||
if (StrUtil.isEmpty(value)) {
|
||||
return true;
|
||||
}
|
||||
// 校验手机
|
||||
return ValidationUtils.isEmail(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package cn.hangtag.module.system.api.mail;
|
||||
|
||||
import cn.hangtag.framework.common.exception.ServiceException;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 短信验证码 API 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface MailCodeApi {
|
||||
|
||||
/**
|
||||
* 创建邮箱验证码,并进行发送
|
||||
*
|
||||
* @param reqDTO 发送请求
|
||||
*/
|
||||
void sendMailCode(@Valid MailCodeSendReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 验证邮箱验证码,并进行使用
|
||||
* 如果正确,则将验证码标记成已使用
|
||||
* 如果错误,则抛出 {@link ServiceException} 异常
|
||||
*
|
||||
* @param reqDTO 使用请求
|
||||
*/
|
||||
void useMailCode(@Valid MailCodeUseReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 检查验证码是否有效
|
||||
*
|
||||
* @param reqDTO 校验请求
|
||||
*/
|
||||
void validateMailCode(@Valid MailCodeValidateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package cn.hangtag.module.system.api.mail.dto.code;
|
||||
|
||||
import cn.hangtag.framework.common.validation.Email;
|
||||
import cn.hangtag.framework.common.validation.InEnum;
|
||||
import cn.hangtag.framework.common.validation.Mobile;
|
||||
import cn.hangtag.module.system.enums.mail.MailSceneEnum;
|
||||
import lombok.Data;
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ public class MailCodeSendReqDTO {
|
|||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Mobile
|
||||
@Email
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
private String mail;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package cn.hangtag.module.system.api.mail.dto.code;
|
||||
|
||||
import cn.hangtag.framework.common.validation.Email;
|
||||
import cn.hangtag.framework.common.validation.InEnum;
|
||||
import cn.hangtag.framework.common.validation.Mobile;
|
||||
import cn.hangtag.module.system.enums.sms.SmsSceneEnum;
|
||||
|
|
@ -19,7 +20,7 @@ public class MailCodeUseReqDTO {
|
|||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Mobile
|
||||
@Email
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
private String mail;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import lombok.Getter;
|
|||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 用户短信验证码发送场景的枚举
|
||||
* 用户邮箱验证码发送场景的枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
|
|
@ -16,12 +16,12 @@ import java.util.Arrays;
|
|||
@AllArgsConstructor
|
||||
public enum MailSceneEnum implements IntArrayValuable {
|
||||
|
||||
MEMBER_LOGIN(1, "user-sms-login", "会员用户 - 邮箱登陆"),
|
||||
MEMBER_UPDATE_MOBILE(2, "user-update-mobile", "会员用户 - 修改手机"),
|
||||
MEMBER_LOGIN(1, "user-mail-login", "会员用户 - 邮箱登陆"),
|
||||
MEMBER_UPDATE_MOBILE(2, "user-update-mail", "会员用户 - 修改手机"),
|
||||
MEMBER_UPDATE_PASSWORD(3, "user-update-password", "会员用户 - 修改密码"),
|
||||
MEMBER_RESET_PASSWORD(4, "user-reset-password", "会员用户 - 忘记密码"),
|
||||
|
||||
ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 邮箱登录");
|
||||
ADMIN_MEMBER_LOGIN(21, "admin-mail-login", "后台用户 - 邮箱登录");
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MailSceneEnum::getScene).toArray();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package cn.hangtag.module.system.api.mail;
|
||||
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.SmsCodeApi;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.service.mail.MailCodeService;
|
||||
import cn.hangtag.module.system.service.sms.SmsCodeService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 短信验证码 API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class MailCodeApiImpl implements MailCodeApi {
|
||||
|
||||
@Resource
|
||||
private MailCodeService mailCodeService;
|
||||
|
||||
@Override
|
||||
public void sendMailCode(MailCodeSendReqDTO reqDTO) {
|
||||
mailCodeService.sendMailCode(reqDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useMailCode(MailCodeUseReqDTO reqDTO) {
|
||||
mailCodeService.useMailCode(reqDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateMailCode(MailCodeValidateReqDTO reqDTO) {
|
||||
mailCodeService.validateMailCode(reqDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -146,6 +146,27 @@ public class AuthController {
|
|||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
// ========== 邮箱相关 ==========
|
||||
|
||||
@PostMapping("/send-mail-code")
|
||||
@PermitAll
|
||||
@Operation(summary = "发送邮箱验证码")
|
||||
public CommonResult<Boolean> sendForgetPasswordEmailCode(@RequestBody @Valid AuthMailSendReqVO reqVO) {
|
||||
authService.sendMailCode(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/mail-modifypwd")
|
||||
@PermitAll
|
||||
@Operation(summary = "使用邮箱验证码修改密码")
|
||||
public CommonResult<Boolean> smsLogin(@RequestBody @Valid AuthMailModifyPwdReqVO reqVO) {
|
||||
authService.mailResetPwd(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ========== 社交登录相关 ==========
|
||||
|
||||
@GetMapping("/social-auth-redirect")
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ public class AuthLoginReqVO {
|
|||
|
||||
@Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "hangtagyuanma")
|
||||
@NotEmpty(message = "登录账号不能为空")
|
||||
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||
//@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
||||
//@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package cn.hangtag.module.system.controller.admin.auth.vo;
|
||||
|
||||
import cn.hangtag.framework.common.validation.Email;
|
||||
import cn.hangtag.framework.common.validation.Mobile;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
@Schema(description = "管理后台 - 邮箱验证码的重置密码 Request VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AuthMailModifyPwdReqVO {
|
||||
|
||||
@Schema(description = "邮箱账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxxx@qq.com")
|
||||
@NotEmpty(message = "邮箱账号不能为空")
|
||||
@Email
|
||||
private String mail;
|
||||
|
||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxxx@qq.com")
|
||||
@NotEmpty(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "确认密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxxx@qq.com")
|
||||
@NotEmpty(message = "确认密码不能为空")
|
||||
private String checkpassword;
|
||||
|
||||
@Schema(description = "短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotEmpty(message = "验证码不能为空")
|
||||
private String code;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package cn.hangtag.module.system.controller.admin.auth.vo;
|
||||
|
||||
import cn.hangtag.framework.common.validation.Email;
|
||||
import cn.hangtag.framework.common.validation.InEnum;
|
||||
import cn.hangtag.framework.common.validation.Mobile;
|
||||
import cn.hangtag.module.system.enums.mail.MailSceneEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 发送手机验证码 Request VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AuthMailSendReqVO {
|
||||
|
||||
@Schema(description = "邮箱账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxxxx@qq.com")
|
||||
@NotEmpty(message = "邮箱账号不能为空")
|
||||
@Email
|
||||
private String mail;
|
||||
|
||||
@Schema(description = "邮箱场景", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
@InEnum(MailSceneEnum.class)
|
||||
private Integer scene;
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package cn.hangtag.module.system.convert.auth;
|
||||
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hangtag.framework.common.util.object.BeanUtils;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
|
|
@ -85,4 +87,8 @@ public interface AuthConvert {
|
|||
|
||||
SmsCodeUseReqDTO convert(AuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
|
||||
|
||||
MailCodeSendReqDTO convert(AuthMailSendReqVO reqVO);
|
||||
|
||||
MailCodeUseReqDTO convert(AuthMailModifyPwdReqVO reqVO, Integer scene, String usedIp);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
package cn.hangtag.module.system.dal.dataobject.mail;
|
||||
|
||||
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 邮箱验证码 DO
|
||||
*
|
||||
* idx_mobile 索引:基于 {@link #mobile} 字段
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("system_mail_code")
|
||||
@KeySequence("system_mail_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MailCodeDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 邮箱号
|
||||
*/
|
||||
private String mail;
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 发送场景
|
||||
*
|
||||
* 枚举 {@link MailCodeDO}
|
||||
*/
|
||||
private Integer scene;
|
||||
/**
|
||||
* 创建 IP
|
||||
*/
|
||||
private String createIp;
|
||||
/**
|
||||
* 今日发送的第几条
|
||||
*/
|
||||
private Integer todayIndex;
|
||||
/**
|
||||
* 是否使用
|
||||
*/
|
||||
private Boolean used;
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
private LocalDateTime usedTime;
|
||||
/**
|
||||
* 使用 IP
|
||||
*/
|
||||
private String usedIp;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package cn.hangtag.module.system.dal.mysql.mail;
|
||||
|
||||
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.hangtag.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.hangtag.module.system.dal.dataobject.mail.MailCodeDO;
|
||||
import cn.hangtag.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface MailCodeMapper extends BaseMapperX<MailCodeDO> {
|
||||
|
||||
/**
|
||||
* 获得手机号的最后一个手机验证码
|
||||
*
|
||||
* @param mail 手机号
|
||||
* @param scene 发送场景,选填
|
||||
* @param code 验证码 选填
|
||||
* @return 手机验证码
|
||||
*/
|
||||
default MailCodeDO selectLastByMail(String mail, String code, Integer scene) {
|
||||
return selectOne(new QueryWrapperX<MailCodeDO>()
|
||||
.eq("mail", mail)
|
||||
.eqIfPresent("scene", scene)
|
||||
.eqIfPresent("code", code)
|
||||
.orderByDesc("id")
|
||||
.limitN(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -70,4 +70,14 @@ public interface AdminAuthService {
|
|||
*/
|
||||
AuthLoginRespVO refreshToken(String refreshToken);
|
||||
|
||||
|
||||
/**
|
||||
* 邮箱验证码发送
|
||||
*
|
||||
* @param reqVO 发送请求
|
||||
*/
|
||||
void sendMailCode(AuthMailSendReqVO reqVO);
|
||||
|
||||
|
||||
boolean mailResetPwd(AuthMailModifyPwdReqVO reqVO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package cn.hangtag.module.system.service.auth;
|
||||
|
||||
import cn.hangtag.module.system.api.mail.MailCodeApi;
|
||||
import cn.hangtag.module.system.dal.mysql.user.AdminUserMapper;
|
||||
import cn.hangtag.module.system.enums.mail.MailSceneEnum;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hangtag.framework.common.enums.CommonStatusEnum;
|
||||
import cn.hangtag.framework.common.enums.UserTypeEnum;
|
||||
|
|
@ -29,6 +32,7 @@ import com.xingyuv.captcha.model.vo.CaptchaVO;
|
|||
import com.xingyuv.captcha.service.CaptchaService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
|
@ -64,6 +68,13 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||
private CaptchaService captchaService;
|
||||
@Resource
|
||||
private SmsCodeApi smsCodeApi;
|
||||
@Resource
|
||||
private MailCodeApi mailCodeApi;
|
||||
@Resource
|
||||
private PasswordEncoder passwordEncoder;
|
||||
@Resource
|
||||
private AdminUserMapper userMapper;
|
||||
|
||||
|
||||
/**
|
||||
* 验证码的开关,默认为 true
|
||||
|
|
@ -247,4 +258,43 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||
return UserTypeEnum.ADMIN;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendMailCode(AuthMailSendReqVO reqVO) {
|
||||
// 登录场景,验证是否存在
|
||||
if (userService.getUserByMail(reqVO.getMail()) == null) {
|
||||
throw exception(MAIL_ACCOUNT_NOT_EXISTS);
|
||||
}
|
||||
// 发送验证码
|
||||
mailCodeApi.sendMailCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mailResetPwd(AuthMailModifyPwdReqVO reqVO) {
|
||||
// 校验验证码
|
||||
mailCodeApi.useMailCode(AuthConvert.INSTANCE.convert(reqVO, MailSceneEnum.MEMBER_RESET_PASSWORD.getScene(), getClientIP()));
|
||||
|
||||
// 获得用户信息
|
||||
AdminUserDO user = userService.getUserByMail(reqVO.getMail());
|
||||
if (user == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
// 执行更新
|
||||
user.setPassword(encodePassword(reqVO.getPassword())); // 加密密码
|
||||
int i = userMapper.updateById(user);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对密码进行加密
|
||||
*
|
||||
* @param password 密码
|
||||
* @return 加密后的密码
|
||||
*/
|
||||
private String encodePassword(String password) {
|
||||
return passwordEncoder.encode(password);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package cn.hangtag.module.system.service.mail;
|
|||
|
||||
import cn.hangtag.framework.common.exception.ServiceException;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
|
||||
|
|
@ -28,13 +30,13 @@ public interface MailCodeService {
|
|||
*
|
||||
* @param reqDTO 使用请求
|
||||
*/
|
||||
void useMailCode(@Valid SmsCodeUseReqDTO reqDTO);
|
||||
void useMailCode(@Valid MailCodeUseReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 检查验证码是否有效
|
||||
*
|
||||
* @param reqDTO 校验请求
|
||||
*/
|
||||
void validateMailCode(@Valid SmsCodeValidateReqDTO reqDTO);
|
||||
void validateMailCode(@Valid MailCodeValidateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
package cn.hangtag.module.system.service.mail;
|
||||
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeSendReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeUseReqDTO;
|
||||
import cn.hangtag.module.system.api.mail.dto.code.MailCodeValidateReqDTO;
|
||||
import cn.hangtag.module.system.dal.dataobject.mail.MailCodeDO;
|
||||
import cn.hangtag.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import cn.hangtag.module.system.dal.mysql.sms.SmsCodeMapper;
|
||||
import cn.hangtag.module.system.dal.mysql.mail.MailCodeMapper;
|
||||
import cn.hangtag.module.system.enums.mail.MailSceneEnum;
|
||||
import cn.hangtag.module.system.enums.sms.SmsSceneEnum;
|
||||
import cn.hangtag.module.system.framework.sms.config.SmsCodeProperties;
|
||||
import cn.hangtag.module.system.service.sms.SmsCodeService;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
|
|
@ -16,6 +17,7 @@ import org.springframework.validation.annotation.Validated;
|
|||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import static cn.hangtag.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.hangtag.framework.common.util.date.DateUtils.isToday;
|
||||
|
|
@ -35,32 +37,39 @@ public class MailCodeServiceImpl implements MailCodeService {
|
|||
private SmsCodeProperties smsCodeProperties;
|
||||
|
||||
@Resource
|
||||
private SmsCodeMapper smsCodeMapper;
|
||||
private MailCodeMapper mailCodeMapper;
|
||||
|
||||
@Resource
|
||||
private MailSendService mailSendService;
|
||||
|
||||
@Override
|
||||
public void sendMailCode(MailCodeSendReqDTO reqDTO) {
|
||||
SmsSceneEnum sceneEnum = SmsSceneEnum.getCodeByScene(reqDTO.getScene());
|
||||
MailSceneEnum sceneEnum = MailSceneEnum.getCodeByScene(reqDTO.getScene());
|
||||
Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene());
|
||||
// 创建验证码
|
||||
String code = createMailCode(reqDTO.getMail(), reqDTO.getScene(), reqDTO.getCreateIp());
|
||||
|
||||
LinkedHashMap<String, Object> params = new LinkedHashMap<>();
|
||||
params.put("key1",reqDTO.getMail());
|
||||
params.put("key2",reqDTO.getMail());
|
||||
params.put("key3",code);
|
||||
String templateCode = sceneEnum.getTemplateCode();
|
||||
|
||||
// 发送验证码
|
||||
mailSendService.sendSingleMail(reqDTO.getMail(), null, null,
|
||||
sceneEnum.getTemplateCode(), MapUtil.of("code", code));
|
||||
sceneEnum.getTemplateCode(), params);
|
||||
}
|
||||
|
||||
private String createMailCode(String mobile, Integer scene, String ip) {
|
||||
// 校验是否可以发送验证码,不用筛选场景
|
||||
SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null, null);
|
||||
if (lastSmsCode != null) {
|
||||
if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis()
|
||||
MailCodeDO lastMailCode = mailCodeMapper.selectLastByMail(mobile, null, null);
|
||||
if (lastMailCode != null) {
|
||||
if (LocalDateTimeUtil.between(lastMailCode.getCreateTime(), LocalDateTime.now()).toMillis()
|
||||
< smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁
|
||||
throw exception(SMS_CODE_SEND_TOO_FAST);
|
||||
}
|
||||
if (isToday(lastSmsCode.getCreateTime()) && // 必须是今天,才能计算超过当天的上限
|
||||
lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
|
||||
if (isToday(lastMailCode.getCreateTime()) && // 必须是今天,才能计算超过当天的上限
|
||||
lastMailCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
|
||||
throw exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
|
||||
}
|
||||
// TODO 芋艿:提升,每个 IP 每天可发送数量
|
||||
|
|
@ -70,44 +79,44 @@ public class MailCodeServiceImpl implements MailCodeService {
|
|||
// 创建验证码记录
|
||||
String code = String.format("%0" + smsCodeProperties.getEndCode().toString().length() + "d",
|
||||
randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1));
|
||||
SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code).scene(scene)
|
||||
.todayIndex(lastSmsCode != null && isToday(lastSmsCode.getCreateTime()) ? lastSmsCode.getTodayIndex() + 1 : 1)
|
||||
MailCodeDO newSmsCode = MailCodeDO.builder().mail(mobile).code(code).scene(scene)
|
||||
.todayIndex(lastMailCode != null && isToday(lastMailCode.getCreateTime()) ? lastMailCode.getTodayIndex() + 1 : 1)
|
||||
.createIp(ip).used(false).build();
|
||||
smsCodeMapper.insert(newSmsCode);
|
||||
mailCodeMapper.insert(newSmsCode);
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void useMailCode(SmsCodeUseReqDTO reqDTO) {
|
||||
public void useMailCode(MailCodeUseReqDTO reqDTO) {
|
||||
// 检测验证码是否有效
|
||||
SmsCodeDO lastSmsCode = validateMailCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene());
|
||||
MailCodeDO lastMailCode = validateMailCode0(reqDTO.getMail(), reqDTO.getCode(), reqDTO.getScene());
|
||||
// 使用验证码
|
||||
smsCodeMapper.updateById(SmsCodeDO.builder().id(lastSmsCode.getId())
|
||||
mailCodeMapper.updateById(MailCodeDO.builder().id(lastMailCode.getId())
|
||||
.used(true).usedTime(LocalDateTime.now()).usedIp(reqDTO.getUsedIp()).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateMailCode(SmsCodeValidateReqDTO reqDTO) {
|
||||
validateMailCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene());
|
||||
public void validateMailCode(MailCodeValidateReqDTO reqDTO) {
|
||||
validateMailCode0(reqDTO.getMail(), reqDTO.getCode(), reqDTO.getScene());
|
||||
}
|
||||
|
||||
private SmsCodeDO validateMailCode0(String mobile, String code, Integer scene) {
|
||||
private MailCodeDO validateMailCode0(String mobile, String code, Integer scene) {
|
||||
// 校验验证码
|
||||
SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, scene);
|
||||
MailCodeDO lastMailCode = mailCodeMapper.selectLastByMail(mobile, code, scene);
|
||||
// 若验证码不存在,抛出异常
|
||||
if (lastSmsCode == null) {
|
||||
if (lastMailCode == null) {
|
||||
throw exception(SMS_CODE_NOT_FOUND);
|
||||
}
|
||||
// 超过时间
|
||||
if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis()
|
||||
if (LocalDateTimeUtil.between(lastMailCode.getCreateTime(), LocalDateTime.now()).toMillis()
|
||||
>= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期
|
||||
throw exception(SMS_CODE_EXPIRED);
|
||||
}
|
||||
// 判断验证码是否已被使用
|
||||
if (Boolean.TRUE.equals(lastSmsCode.getUsed())) {
|
||||
if (Boolean.TRUE.equals(lastMailCode.getUsed())) {
|
||||
throw exception(SMS_CODE_USED);
|
||||
}
|
||||
return lastSmsCode;
|
||||
return lastMailCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import cn.hangtag.module.system.dal.mysql.mail.MailTemplateMapper;
|
|||
import cn.hangtag.module.system.dal.redis.RedisKeyConstants;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -41,7 +42,7 @@ public class MailTemplateServiceImpl implements MailTemplateService {
|
|||
/**
|
||||
* 正则表达式,匹配 {} 中的变量
|
||||
*/
|
||||
private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}");
|
||||
private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{\\{(.*?)}}");
|
||||
|
||||
@Resource
|
||||
private MailTemplateMapper mailTemplateMapper;
|
||||
|
|
@ -122,6 +123,12 @@ public class MailTemplateServiceImpl implements MailTemplateService {
|
|||
|
||||
@Override
|
||||
public String formatMailTemplateContent(String content, Map<String, Object> params) {
|
||||
if(StringUtils.isNotBlank(content)){
|
||||
/* content = content.replaceFirst("<p>", "").replace("</p>", "");
|
||||
content = content.replaceAll("<", "<").replaceAll(">", ">");
|
||||
content = content.replaceAll(" ", "");*/
|
||||
content = content.replaceAll("\\{\\{", "{").replaceAll("}}","}");
|
||||
}
|
||||
return StrUtil.format(content, params);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -201,4 +201,15 @@ public interface AdminUserService {
|
|||
*/
|
||||
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通过邮箱号获取用户
|
||||
*
|
||||
* @param mail 邮箱号
|
||||
* @return 用户对象信息
|
||||
*/
|
||||
AdminUserDO getUserByMail(String mail);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -496,6 +496,11 @@ public class AdminUserServiceImpl implements AdminUserService {
|
|||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminUserDO getUserByMail(String mail) {
|
||||
return userMapper.selectByEmail(mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对密码进行加密
|
||||
*
|
||||
|
|
|
|||
|
|
@ -251,8 +251,8 @@ hangtag:
|
|||
expire-times: 10m
|
||||
send-frequency: 1m
|
||||
send-maximum-quantity-per-day: 10
|
||||
begin-code: 9999 # 这里配置 9999 的原因是,测试方便。
|
||||
end-code: 9999 # 这里配置 9999 的原因是,测试方便。
|
||||
begin-code: 000001 # 这里配置 9999 的原因是,测试方便。
|
||||
end-code: 999999 # 这里配置 9999 的原因是,测试方便。
|
||||
trade:
|
||||
order:
|
||||
app-id: 1 # 商户编号
|
||||
|
|
|
|||
|
|
@ -12,6 +12,19 @@ export interface SmsLoginVO {
|
|||
code: string
|
||||
}
|
||||
|
||||
export interface MailCodeVO {
|
||||
mail: string
|
||||
scene: number
|
||||
}
|
||||
|
||||
|
||||
export interface MailModifyPwdVO {
|
||||
mail: string
|
||||
password: string
|
||||
checkpassword: string
|
||||
code: string
|
||||
}
|
||||
|
||||
// 登录
|
||||
export const login = (data: UserLoginVO) => {
|
||||
return request.post({ url: '/system/auth/login', data })
|
||||
|
|
@ -47,6 +60,16 @@ export const sendSmsCode = (data: SmsCodeVO) => {
|
|||
return request.post({ url: '/system/auth/send-sms-code', data })
|
||||
}
|
||||
|
||||
//获取邮箱登录验证码
|
||||
export const sendMailCode = (data: MailCodeVO) => {
|
||||
return request.post({ url: '/system/auth/send-mail-code', data })
|
||||
}
|
||||
|
||||
// 邮箱码修改密码
|
||||
export const mailModifyPwd = (data: MailModifyPwdVO) => {
|
||||
return request.post({ url: '/system/auth/mail-modifypwd', data })
|
||||
}
|
||||
|
||||
// 短信验证码登录
|
||||
export const smsLogin = (data: SmsLoginVO) => {
|
||||
return request.post({ url: '/system/auth/sms-login', data })
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ export default {
|
|||
},
|
||||
login: {
|
||||
welcome: 'Welcome to the system',
|
||||
message: 'Backstage management system',
|
||||
message: '',
|
||||
tenantname: 'TenantName',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
|
|
@ -133,14 +133,17 @@ export default {
|
|||
codePlaceholder: 'Please Enter Verification Code',
|
||||
mobileTitle: 'Mobile sign in',
|
||||
mobileNumber: 'Mobile Number',
|
||||
mobileNumberPlaceholder: 'Plaease Enter Mobile Number',
|
||||
mobileNumberPlaceholder: 'Plaease Enter Mobile Code',
|
||||
emailNumberPlaceholder: 'Plaease Enter Email Code',
|
||||
backLogin: 'back',
|
||||
getSmsCode: 'Get SMS Code',
|
||||
getMailCode: 'Get Mail Code',
|
||||
btnMobile: 'Mobile sign in',
|
||||
btnQRCode: 'QR code sign in',
|
||||
qrcode: 'Scan the QR code to log in',
|
||||
btnRegister: 'Sign up',
|
||||
SmsSendMsg: 'code has been sent'
|
||||
SmsSendMsg: 'code has been sent',
|
||||
pwdResetSuccess: 'password reset success'
|
||||
},
|
||||
captcha: {
|
||||
verification: 'Please complete security verification',
|
||||
|
|
|
|||
|
|
@ -134,13 +134,16 @@ export default {
|
|||
mobileTitle: '手机登录',
|
||||
mobileNumber: '手机号码',
|
||||
mobileNumberPlaceholder: '请输入手机号码',
|
||||
emailNumberPlaceholder: '请输入邮箱号码',
|
||||
backLogin: '返回',
|
||||
getSmsCode: '获取验证码',
|
||||
getMailCode: '获取验证码',
|
||||
btnMobile: '手机登录',
|
||||
btnQRCode: '二维码登录',
|
||||
qrcode: '扫描二维码登录',
|
||||
btnRegister: '注册',
|
||||
SmsSendMsg: '验证码已发送'
|
||||
SmsSendMsg: '验证码已发送',
|
||||
pwdResetSuccess: '密码重置成功'
|
||||
},
|
||||
captcha: {
|
||||
verification: '请完成安全验证',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
<div class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px">
|
||||
<!-- 右上角的主题、语言选择 -->
|
||||
<!-- <div
|
||||
<div
|
||||
class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
|
||||
>
|
||||
<div class="flex items-center at-2xl:hidden at-xl:hidden">
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
<ThemeSwitch />
|
||||
<LocaleDropdown class="dark:text-white lt-xl:text-white" />
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
<!-- 右边的登录界面 -->
|
||||
<Transition appear enter-active-class="animate__animated animate__bounceInRight">
|
||||
<div
|
||||
|
|
@ -56,6 +56,8 @@
|
|||
<RegisterForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<!-- 三方登录 -->
|
||||
<SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
<!-- 忘记密码 -->
|
||||
<ForgetPasswordForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
|
@ -70,7 +72,7 @@ import { useAppStore } from '@/store/modules/app'
|
|||
import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
|
||||
import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
|
||||
|
||||
import { LoginForm, MobileForm, QrCodeForm, RegisterForm, SSOLoginVue } from './components'
|
||||
import { LoginForm, MobileForm, ForgetPasswordForm, QrCodeForm, RegisterForm, SSOLoginVue } from './components'
|
||||
|
||||
defineOptions({ name: 'Login' })
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,262 @@
|
|||
<template>
|
||||
<el-form
|
||||
v-show="getShow"
|
||||
ref="formSmsLogin"
|
||||
:model="loginData.loginForm"
|
||||
:rules="rules"
|
||||
class="login-form"
|
||||
label-position="top"
|
||||
label-width="120px"
|
||||
size="large"
|
||||
autocomplete="off"
|
||||
>
|
||||
<el-row style="margin-right: -10px; margin-left: -10px">
|
||||
<!-- 租户名 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.tenantName"
|
||||
:placeholder="t('login.tenantNamePlaceholder')"
|
||||
:prefix-icon="iconHouse"
|
||||
type="primary"
|
||||
link
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>-->
|
||||
<!-- 手机号 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="mailNumber">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.mailNumber"
|
||||
:placeholder="t('login.emailNumberPlaceholder')"
|
||||
:prefix-icon="iconCellemail"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 密码 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
type="password"
|
||||
disableautocomplete
|
||||
autocomplete="off"
|
||||
show-password="true"
|
||||
v-model="loginData.loginForm.password"
|
||||
:placeholder="t('login.password')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
|
||||
<!-- 确认密码 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="checkPassword">
|
||||
<el-input
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
disableautocomplete
|
||||
show-password="true"
|
||||
v-model="loginData.loginForm.checkPassword"
|
||||
:placeholder="t('login.checkPassword')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 验证码 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="code">
|
||||
<el-row :gutter="5" justify="space-between" style="width: 100%">
|
||||
<el-col :span="24">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.code"
|
||||
:placeholder="t('login.codePlaceholder')"
|
||||
:prefix-icon="iconCircleCheck"
|
||||
>
|
||||
<!-- <el-button class="w-[100%]"> -->
|
||||
<template #append>
|
||||
<span
|
||||
v-if="mailCodeTimer <= 0"
|
||||
class="getMobileCode"
|
||||
style="cursor: pointer"
|
||||
@click="getMailCode"
|
||||
>
|
||||
{{ t('login.getMailCode') }}
|
||||
</span>
|
||||
<span v-if="mailCodeTimer > 0" class="getMobileCode" style="cursor: pointer">
|
||||
{{ mailCodeTimer }}秒后可重新获取
|
||||
</span>
|
||||
</template>
|
||||
</el-input>
|
||||
<!-- </el-button> -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 登录按钮 / 返回按钮 -->
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.login')"
|
||||
class="w-[100%]"
|
||||
type="primary"
|
||||
@click="modifyPwdIn()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.backLogin')"
|
||||
class="w-[100%]"
|
||||
@click="handleBackLogin()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
|
||||
import { useIcon } from '@/hooks/web/useIcon'
|
||||
|
||||
import { setTenantId, setToken } from '@/utils/auth'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { getTenantIdByName, sendMailCode, mailModifyPwd } from '@/api/login'
|
||||
import LoginFormTitle from './LoginFormTitle.vue'
|
||||
import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
|
||||
import { ElLoading } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'ForgetPasswordForm' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
const permissionStore = usePermissionStore()
|
||||
const { currentRoute, push } = useRouter()
|
||||
const formSmsLogin = ref()
|
||||
const loginLoading = ref(false)
|
||||
const iconHouse = useIcon({ icon: 'ep:house' })
|
||||
const iconCellphone = useIcon({ icon: 'ep:cellphone' })
|
||||
const iconCellemail= useIcon({ icon: 'ep:cellphone' })
|
||||
const iconCircleCheck = useIcon({ icon: 'ep:circle-check' })
|
||||
const { validForm } = useFormValid(formSmsLogin)
|
||||
const { handleBackLogin, getLoginState } = useLoginState()
|
||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.RESET_PASSWORD)
|
||||
|
||||
const rules = {
|
||||
tenantName: [required],
|
||||
mobileNumber: [required],
|
||||
code: [required]
|
||||
}
|
||||
const loginData = reactive({
|
||||
codeImg: '',
|
||||
tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
|
||||
token: '',
|
||||
loading: {
|
||||
modifyPwdIn: false
|
||||
},
|
||||
loginForm: {
|
||||
uuid: '',
|
||||
tenantName: '芋道源码',
|
||||
mobileNumber: '',
|
||||
code: ''
|
||||
}
|
||||
})
|
||||
const mailVO = reactive({
|
||||
mailCode: {
|
||||
mail: '',
|
||||
scene: 4
|
||||
},
|
||||
loginMail: {
|
||||
mail: '',
|
||||
code: ''
|
||||
}
|
||||
})
|
||||
const mailCodeTimer = ref(0)
|
||||
const redirect = ref<string>('')
|
||||
const getMailCode = async () => {
|
||||
await getTenantId()
|
||||
mailVO.mailCode.mail = loginData.loginForm.mailNumber
|
||||
await sendMailCode(mailVO.mailCode).then(async () => {
|
||||
message.success(t('login.SmsSendMsg'))
|
||||
// 设置倒计时
|
||||
mailCodeTimer.value = 60
|
||||
let msgTimer = setInterval(() => {
|
||||
mailCodeTimer.value = mailCodeTimer.value - 1
|
||||
if (mailCodeTimer.value <= 0) {
|
||||
clearInterval(msgTimer)
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => currentRoute.value,
|
||||
(route: RouteLocationNormalizedLoaded) => {
|
||||
redirect.value = route?.query?.redirect as string
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
// 获取租户 ID
|
||||
const getTenantId = async () => {
|
||||
if (loginData.tenantEnable === 'true') {
|
||||
const res = await getTenantIdByName(loginData.loginForm.tenantName)
|
||||
setTenantId(res)
|
||||
}
|
||||
}
|
||||
// 登录
|
||||
const modifyPwdIn = async () => {
|
||||
await getTenantId()
|
||||
const data = await validForm()
|
||||
if (!data) return
|
||||
/* ElLoading.service({
|
||||
lock: true,
|
||||
text: '正在加载系统中...',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})*/
|
||||
loginLoading.value = true
|
||||
mailVO.loginMail.mail = loginData.loginForm.mailNumber
|
||||
mailVO.loginMail.code = loginData.loginForm.code
|
||||
mailVO.loginMail.password = loginData.loginForm.password
|
||||
mailVO.loginMail.checkpassword = loginData.loginForm.checkPassword
|
||||
await mailModifyPwd(mailVO.loginMail)
|
||||
.then(async (res) => {
|
||||
/* setToken(res)
|
||||
if (!redirect.value) {
|
||||
redirect.value = '/'
|
||||
}
|
||||
push({ path: redirect.value || permissionStore.addRouters[0].path })*/
|
||||
await message.alertSuccess(t('login.pwdResetSuccess'))
|
||||
handleBackLogin()
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
loginLoading.value = false
|
||||
setTimeout(() => {
|
||||
const loadingInstance = ElLoading.service()
|
||||
loadingInstance.close()
|
||||
}, 400)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.anticon) {
|
||||
&:hover {
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.smsbtn {
|
||||
margin-top: 33px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -58,9 +58,9 @@
|
|||
{{ t('login.remember') }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
<!-- <el-col :offset="6" :span="12">
|
||||
<el-link style="float: right" type="primary">{{ t('login.forgetPassword') }}</el-link>
|
||||
</el-col>-->
|
||||
<el-col :offset="6" :span="12">
|
||||
<el-link style="float: right" type="primary" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">{{ t('login.forgetPassword') }}</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import LoginForm from './LoginForm.vue'
|
||||
import MobileForm from './MobileForm.vue'
|
||||
import ForgetPasswordForm from './ForgetPasswordForm.vue'
|
||||
import LoginFormTitle from './LoginFormTitle.vue'
|
||||
import RegisterForm from './RegisterForm.vue'
|
||||
import QrCodeForm from './QrCodeForm.vue'
|
||||
import SSOLoginVue from './SSOLogin.vue'
|
||||
|
||||
export { LoginForm, MobileForm, LoginFormTitle, RegisterForm, QrCodeForm, SSOLoginVue }
|
||||
export { LoginForm, MobileForm, ForgetPasswordForm, LoginFormTitle, RegisterForm, QrCodeForm, SSOLoginVue }
|
||||
|
|
|
|||
Loading…
Reference in New Issue