个人主页: 温情
•ࡇ• 山高路远,看世界也找自己
目录
一、QQ邮箱环境配置
首先,登录QQ邮箱,点击设置进入邮箱设置,选择“账号”。
往下滑,找到POM3/SMTP服务并开启,并获得授权码。
二、导入相关依赖
<dependency> <groupId>com.sun.mail</groupId> <artifactId>jakarta.mail</artifactId> </dependency>
三、application.yml文件中配置email参数
# 邮箱配置 todo 修改为自己的邮箱发送配置 mail: host: smtp.qq.com username: 填写自己的邮箱账号 password: 填写授权码 emailFrom: 填写自己的邮箱账号 properties: mail: smtp: ssl: enable: true
四、编写配置文件
新建config包,创建EmailConfig配置类
五、电子邮件常量类
constant包的EmailConstant电子邮件常量类
package com.kun.kunapibackend.constant;
/**
* @Author
* @Date: 2024/03/03 11:24:40
* @Version: 1.0
* @Description: 电子邮件常量
*/
public interface EmailConstant {
/**
* 电子邮件html内容路径 resources目录下
*/
String EMAIL_HTML_CONTENT_PATH = "email.html";
/**
* 电子邮件html支付成功路径
*/
String EMAIL_HTML_PAY_SUCCESS_PATH = "pay.html";
/**
* captcha缓存键
*/
String CAPTCHA_CACHE_KEY = "api:captcha:";
/**
* 电子邮件主题
*/
String EMAIL_SUBJECT = "验证码邮件";
/**
* 电子邮件标题
*/
String EMAIL_TITLE = "XXXXXXXXXXX";
/**
* 电子邮件标题英语
*/
String EMAIL_TITLE_ENGLISH = "KUN-API Open Interface Platform";
/**
* 平台负责人
*/
String PLATFORM_RESPONSIBLE_PERSON = "温情";
/**
* 平台地址
*/
String PLATFORM_ADDRESS = "<a href='http://XXXXXXXXXX/'>请联系我们</a>";
String PATH_ADDRESS = "'http://XXXXXXXXXXXX/'";
}
六、email.html
在resources下新建email.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="email code" name="description">
<meta content="width=device-width, initial-scale=1" name="viewport">
</head>
<!--邮箱验证码模板-->
<body>
<div style="background-color:#ECECEC; padding: 35px;">
<table align="center" cellpadding="0"
style="width: 600px;height: 100%; margin: 0px auto; text-align: left; position: relative; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-size: 14px; font-family:微软雅黑, 黑体; line-height: 1.5; box-shadow: rgb(153, 153, 153) 0px 0px 5px; border-collapse: collapse; background-position: initial initial; background-repeat: initial initial;background:#fff;">
<tbody>
<tr>
<th style="height: 25px; line-height: 25px; padding: 15px 35px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: RGB(148,0,211); background-color: RGB(148,0,211); border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px;"
valign="middle">
<font face="微软雅黑" size="5" style="color: rgb(255, 255, 255); ">{1}</font>
</th>
</tr>
<tr>
<td style="word-break:break-all">
<div style="padding:25px 35px 40px; background-color:#fff;opacity:0.8;">
<h2 style="margin: 5px 0px; ">
<font color="#333333" style="line-height: 20px; ">
<font size="4" style="line-height: 22px; ">
尊敬的用户:</font>
</font>
</h2>
<!-- 中文 -->
<p>您好!感谢您使用{1},您的账号正在进行邮箱验证,验证码为:<span style="color: '#ff8c00';font-size: 16px;font-weight: bold">{0}</span>
,有效期5分钟,请尽快填写验证码完成验证!</p><br>
<!-- 英文 -->
<h2 style="margin: 5px 0px; ">
<font color="#333333" style="line-height: 20px; ">
<font size="4" style="line-height: 22px; ">
Dear user:</font>
</font>
</h2>
<p>Hello! Thanks for using {2}, your account is being authenticated by email, the
verification code is: <span
style="color: '#ff8c00';font-size: 16px;font-weight: bold"> {0} </span> , valid for 5
minutes. Please fill in the
verification code as soon as
possible!</p>
<div style="width:100%;margin:0 auto;">
<div style="padding:10px 10px 0;border-top:1px solid #ccc;color:#747474;margin-bottom:20px;line-height:1.3em;font-size:12px;">
<p>{3}</p>
<p>此电子邮件仅限本人查看!如果有人要求你与他分享此 电子邮件或验证,或你认为误收此电子邮件,{4}</p>
<br>
<p>此为系统邮件,请勿回复<br>
Please do not reply to this system email
</p>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
七、Controller层
/**
* 用户接口
*
* @author
*/
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Resource
private EmailConfig emailConfig;
@Resource
private UserService userService;
@Resource
private JavaMailSender mailSender;
@Resource
private RedisTemplate<String, String> redisTemplate;
// region 登录相关
/**
* 用户注册
*
* @param userRegisterRequest 用户注册请求
* @return {@link BaseResponse}<{@link Long}>
*/
@PostMapping("/register")
public BaseResponse<Long> userRegister(@RequestBody UserRegisterRequest userRegisterRequest) {
if (userRegisterRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
long result = userService.userRegister(userRegisterRequest);
return ResultUtils.success(result);
}
/**
* 用户登录
*
* @param userLoginRequest 用户登录请求
* @param request 请求
* @return {@link BaseResponse}<{@link User}>
*/
@PostMapping("/login")
public BaseResponse<UserVO> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {
if (userLoginRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String userAccount = userLoginRequest.getUserAccount();
String userPassword = userLoginRequest.getUserPassword();
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
UserVO user = userService.userLogin(userAccount, userPassword, request);
return ResultUtils.success(user);
}
/**
* 用户电子邮件登录
*
* @param userEmailLoginRequest 用户登录请求
* @param request 请求
* @return {@link BaseResponse}<{@link User}>
*/
@PostMapping("/email/login")
public BaseResponse<UserVO> userEmailLogin(@RequestBody UserEmailLoginRequest userEmailLoginRequest, HttpServletRequest request) {
if (userEmailLoginRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
UserVO user = userService.userEmailLogin(userEmailLoginRequest, request);
redisTemplate.delete(CAPTCHA_CACHE_KEY + userEmailLoginRequest.getEmailAccount());
return ResultUtils.success(user);
}
/**
* 用户绑定电子邮件
*
* @param request 请求
* @param userBindEmailRequest 用户绑定电子邮件请求
* @return {@link BaseResponse}<{@link UserVO}>
*/
@PostMapping("/bind/login")
public BaseResponse<UserVO> userBindEmail(@RequestBody UserBindEmailRequest userBindEmailRequest, HttpServletRequest request) {
if (userBindEmailRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
UserVO user = userService.userBindEmail(userBindEmailRequest, request);
return ResultUtils.success(user);
}
/**
* 用户取消绑定电子邮件
*
* @param request 请求
* @param userUnBindEmailRequest 用户取消绑定电子邮件请求
* @return {@link BaseResponse}<{@link UserVO}>
*/
@PostMapping("/unbindEmail")
public BaseResponse<UserVO> userUnBindEmail(@RequestBody UserUnBindEmailRequest userUnBindEmailRequest, HttpServletRequest request) {
if (userUnBindEmailRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
UserVO user = userService.userUnBindEmail(userUnBindEmailRequest, request);
redisTemplate.delete(CAPTCHA_CACHE_KEY + userUnBindEmailRequest.getEmailAccount());
return ResultUtils.success(user);
}
/**
* 用户电子邮件注册
*
* @param userEmailRegisterRequest 用户电子邮件注册请求
* @return {@link BaseResponse}<{@link UserVO}>
*/
@PostMapping("/email/register")
public BaseResponse<Long> userEmailRegister(@RequestBody UserEmailRegisterRequest userEmailRegisterRequest) {
if (userEmailRegisterRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
long result = userService.userEmailRegister(userEmailRegisterRequest);
redisTemplate.delete(CAPTCHA_CACHE_KEY + userEmailRegisterRequest.getEmailAccount());
return ResultUtils.success(result);
}
/**
* 获取验证码
*
* @param emailAccount 电子邮件帐户
* @return {@link BaseResponse}<{@link String}>
*/
@GetMapping("/getCaptcha")
public BaseResponse<Boolean> getCaptcha(String emailAccount) {
if (StringUtils.isBlank(emailAccount)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String emailPattern = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
if (!Pattern.matches(emailPattern, emailAccount)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "不合法的邮箱地址!");
}
String captcha = RandomUtil.randomNumbers(6);
try {
sendEmail(emailAccount, captcha);
redisTemplate.opsForValue().set(CAPTCHA_CACHE_KEY + emailAccount, captcha, 5, TimeUnit.MINUTES);
return ResultUtils.success(true);
} catch (Exception e) {
log.error("【发送验证码失败】" + e.getMessage());
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码获取失败");
}
}
private void sendEmail(String emailAccount, String captcha) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
// 邮箱发送内容组成
MimeMessageHelper helper = new MimeMessageHelper(message, true);
//设置邮箱主题
helper.setSubject(EMAIL_SUBJECT);
//调用EmailUtil工具类的buildEmailContent方法构造邮件内容
helper.setText(buildEmailContent(EMAIL_HTML_CONTENT_PATH, captcha), true);
//设置收件人
helper.setTo(emailAccount);
// 设置发件人为EMAIL_TITLE + < + emailConfig.getEmailFrom() + >,
// 其中EMAIL_TITLE是电子邮件的标题,emailConfig.getEmailFrom()是电子邮件的发送地址。
helper.setFrom(EMAIL_TITLE + '<' + emailConfig.getEmailFrom() + '>');
mailSender.send(message);
}
}
八、Service层
/**
* @Version: 1.0
* @Description: 用户服务
*/
public interface UserService extends IService<User> {
/**
* 用户注册
*
* @param userRegisterRequest 用户注册请求
* @return 新用户 id
*/
long userRegister(UserRegisterRequest userRegisterRequest);
/**
* 用户电子邮件注册
*
* @param userEmailRegisterRequest 用户电子邮件注册请求
* @return long
*/
long userEmailRegister(UserEmailRegisterRequest userEmailRegisterRequest);
/**
* 用户登录
*
* @param userAccount 用户账户
* @param userPassword 用户密码
* @param request 请求
* @return 脱敏后的用户信息
*/
UserVO userLogin(String userAccount, String userPassword, HttpServletRequest request);
/**
* 用户电子邮件登录
*
* @param userEmailLoginRequest 用户电子邮件登录请求
* @param request 要求
* @return {@link UserVO}
*/
UserVO userEmailLogin(UserEmailLoginRequest userEmailLoginRequest, HttpServletRequest request);
}
九、ServiceImpl
/**
* 用户服务实现类
*
* @author
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private RedisTemplate<String, String> redisTemplate;
@Resource
private RedissonLockUtil redissonLockUtil;
/**
* 用户寄存器
*
* @param userRegisterRequest 用户注册请求
* @return long
*/
@Override
@Transactional(rollbackFor = Exception.class)
public long userRegister(UserRegisterRequest userRegisterRequest) {
String userAccount = userRegisterRequest.getUserAccount();
String userPassword = userRegisterRequest.getUserPassword();
String userName = userRegisterRequest.getUserName();
String checkPassword = userRegisterRequest.getCheckPassword();
String invitationCode = userRegisterRequest.getInvitationCode();
// 1. 校验
if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
}
if (userName.length() > 40) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "昵称过长");
}
if (userAccount.length() < 4) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
}
if (userPassword.length() < 8 || checkPassword.length() < 8) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
}
// 5. 账户不包含特殊字符
// 匹配由数字、小写字母、大写字母组成的字符串,且字符串的长度至少为1个字符
String pattern = "[0-9a-zA-Z]+";
if (!userAccount.matches(pattern)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号由数字、小写字母、大写字母组成");
}
// 密码和校验密码相同
if (!userPassword.equals(checkPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
}
String redissonLock = ("userRegister_" + userAccount).intern();
return redissonLockUtil.redissonDistributedLocks(redissonLock, () -> {
// 账户不能重复
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userAccount", userAccount);
long count = userMapper.selectCount(queryWrapper);
if (count > 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
}
User invitationCodeUser = null;
if (StringUtils.isNotBlank(invitationCode)) {
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
userLambdaQueryWrapper.eq(User::getInvitationCode, invitationCode);
// 可能出现重复invitationCode,查出的不是一条
invitationCodeUser = this.getOne(userLambdaQueryWrapper);
if (invitationCodeUser == null) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "该邀请码无效");
}
}
// 2. 加密
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
// ak/sk
String accessKey = DigestUtils.md5DigestAsHex((userAccount + SALT + VOUCHER).getBytes());
String secretKey = DigestUtils.md5DigestAsHex((SALT + VOUCHER + userAccount).getBytes());
// 3. 插入数据
User user = new User();
user.setUserAccount(userAccount);
user.setUserPassword(encryptPassword);
user.setUserName(userName);
user.setAccessKey(accessKey);
user.setSecretKey(secretKey);
if (invitationCodeUser != null) {
user.setBalance(100);
this.addWalletBalance(invitationCodeUser.getId(), 100);
}
//生成随机八位邀请码
user.setInvitationCode(generateRandomString(8));
boolean saveResult = this.save(user);
if (!saveResult) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败,数据库错误");
}
return user.getId();
}, "注册账号失败");
}
/**
* 用户电子邮件注册
*
* @param userEmailRegisterRequest 用户电子邮件注册请求
* @return long
*/
@Override
@Transactional(rollbackFor = Exception.class)
public long userEmailRegister(UserEmailRegisterRequest userEmailRegisterRequest) {
String emailAccount = userEmailRegisterRequest.getEmailAccount();
String captcha = userEmailRegisterRequest.getCaptcha();
String userName = userEmailRegisterRequest.getUserName();
String invitationCode = userEmailRegisterRequest.getInvitationCode();
if (StringUtils.isAnyBlank(emailAccount, captcha)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
if (userName.length() > 40) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "昵称过长");
}
String emailPattern = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
if (!Pattern.matches(emailPattern, emailAccount)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "不合法的邮箱地址!");
}
String cacheCaptcha = redisTemplate.opsForValue().get(CAPTCHA_CACHE_KEY + emailAccount);
if (StringUtils.isBlank(cacheCaptcha)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码已过期,请重新获取");
}
//确保字符串变量 captcha 去除了两端的任何空格或其他空白字符,
// 使得后续对 captcha 的处理或验证时不会受到这些无意义空白的影响
captcha = captcha.trim();
if (!cacheCaptcha.equals(captcha)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码输入有误");
}
// emailAccount 构建一个以 "userEmailRegister_" 为前缀的字符串作为锁名称,
// 并通过 intern() 方法将其添加到 JVM 字符串池中,以优化内存使用和提高字符串比较效率。
// 使用 Redisson 实现分布式锁,确保接下来的操作在并发环境下互斥执行。
String redissonLock = ("userEmailRegister_" + emailAccount).intern();
return redissonLockUtil.redissonDistributedLocks(redissonLock, () -> {
// 账户不能重复
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userAccount", emailAccount);
long count = userMapper.selectCount(queryWrapper);
if (count > 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
}
User invitationCodeUser = null;
if (StringUtils.isNotBlank(invitationCode)) {
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
userLambdaQueryWrapper.eq(User::getInvitationCode, invitationCode);
// 可能出现重复invitationCode,查出的不是一条
invitationCodeUser = this.getOne(userLambdaQueryWrapper);
if (invitationCodeUser == null) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "该邀请码无效");
}
}
// ak/sk
String accessKey = DigestUtils.md5DigestAsHex((Arrays.toString(RandomUtil.randomBytes(10)) + SALT + VOUCHER).getBytes());
String secretKey = DigestUtils.md5DigestAsHex((SALT + VOUCHER + Arrays.toString(RandomUtil.randomBytes(10))).getBytes());
// 3. 插入数据
User user = new User();
user.setUserAccount(emailAccount);
user.setUserName(userName);
user.setAccessKey(accessKey);
user.setEmail(emailAccount);
user.setSecretKey(secretKey);
if (invitationCodeUser != null) {
//邀请码有效且不为空,给用户提供100积分
user.setBalance(100);
this.addWalletBalance(invitationCodeUser.getId(), 100);
}
//给用户生成八位随机邀请码
user.setInvitationCode(generateRandomString(8));
boolean saveResult = this.save(user);
if (!saveResult) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败,数据库错误");
}
return user.getId();
//"邮箱账号注册失败" 是一个字符串参数,它作为最后一个参数传递给 redissonDistributedLocks 方法。
//这个字符串参数通常用于在发生异常时提供一个备用的错误消息。
// 具体用途取决于 redissonLockUtil.redissonDistributedLocks 方法的实现。
// 在某些情况下,当业务逻辑执行过程中发生异常且没有明确抛出 BusinessException 或类似异常时,
// redissonDistributedLocks 方法可能会捕获这些异常,并使用传入的备用错误消息构造一个新的异常抛出。
// 这样做有助于在发生未预期异常时,向调用者提供一个统一且易于理解的错误信息,便于问题定位和处理。
}, "邮箱账号注册失败");
}
/**
* 用户登录
*
* @param userAccount 用户帐户
* @param userPassword 用户密码
* @param request 要求
* @return {@link UserVO}
*/
@Override
public UserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
// 1. 校验
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
}
if (userAccount.length() < 4) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短,不能小于4位");
}
if (userPassword.length() < 8) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短,不能低于8位字符");
}
// 5. 账户不包含特殊字符
// 匹配由数字、小写字母、大写字母组成的字符串,且字符串的长度至少为1个字符
String pattern = "[0-9a-zA-Z]+";
if (!userAccount.matches(pattern)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号需由数字、小写字母、大写字母组成");
}
// 2. 加密
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
// 查询用户是否存在
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userAccount", userAccount);
queryWrapper.eq("userPassword", encryptPassword);
User user = userMapper.selectOne(queryWrapper);
// 用户不存在
if (user == null) {
log.info("user login failed, userAccount cannot match userPassword");
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
}
if (user.getStatus().equals(UserAccountStatusEnum.BAN.getValue())) {
throw new BusinessException(ErrorCode.PROHIBITED, "账号已封禁");
}
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
// 3. 记录用户的登录态
request.getSession().setAttribute(USER_LOGIN_STATE, userVO);
return userVO;
}
/**
* 用户电子邮件登录
*
* @param userEmailLoginRequest 用户电子邮件登录请求
* @param request 要求
* @return {@link UserVO}
*/
@Override
public UserVO userEmailLogin(UserEmailLoginRequest userEmailLoginRequest, HttpServletRequest request) {
String emailAccount = userEmailLoginRequest.getEmailAccount();
String captcha = userEmailLoginRequest.getCaptcha();
if (StringUtils.isAnyBlank(emailAccount, captcha)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String emailPattern = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
if (!Pattern.matches(emailPattern, emailAccount)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "不合法的邮箱地址!");
}
String cacheCaptcha = redisTemplate.opsForValue().get(CAPTCHA_CACHE_KEY + emailAccount);
if (StringUtils.isBlank(cacheCaptcha)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码已过期,请重新获取");
}
//确保字符串变量 captcha 去除了两端的任何空格或其他空白字符,
//使得后续对 captcha 的处理或验证时不会受到这些无意义空白的影响
captcha = captcha.trim();
if (!cacheCaptcha.equals(captcha)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码输入有误");
}
// 查询用户是否存在
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("email", emailAccount);
User user = userMapper.selectOne(queryWrapper);
// 用户不存在
if (user == null) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "该邮箱未绑定账号,请先绑定账号");
}
if (user.getStatus().equals(UserAccountStatusEnum.BAN.getValue())) {
throw new BusinessException(ErrorCode.PROHIBITED, "账号已封禁");
}
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user, userVO);
// 3. 记录用户的登录态
request.getSession().setAttribute(USER_LOGIN_STATE, userVO);
return userVO;
}
@Override
public UserVO userBindEmail(UserBindEmailRequest userEmailLoginRequest, HttpServletRequest request) {
String emailAccount = userEmailLoginRequest.getEmailAccount();
String captcha = userEmailLoginRequest.getCaptcha();
if (StringUtils.isAnyBlank(emailAccount, captcha)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String emailPattern = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
// Pattern.matches() 是 Java 正则表达式 API 中的一个静态方法,
// 用于检查给定的字符串(这里是 emailAccount)是否完全匹配某个正则表达式模式
if (!Pattern.matches(emailPattern, emailAccount)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "不合法的邮箱地址!");
}
String cacheCaptcha = redisTemplate.opsForValue().get(CAPTCHA_CACHE_KEY + emailAccount);
if (StringUtils.isBlank(cacheCaptcha)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码已过期,请重新获取");
}
captcha = captcha.trim();
if (!cacheCaptcha.equals(captcha)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码输入有误");
}
// 查询用户是否绑定该邮箱
UserVO loginUser = this.getLoginUser(request);
if (loginUser.getEmail() != null && emailAccount.equals(loginUser.getEmail())) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "该账号已绑定此邮箱,请更换新的邮箱!");
}
// 查询邮箱是否已经绑定
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("email", emailAccount);
User user = this.getOne(queryWrapper);
if (user != null) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "此邮箱已被绑定,请更换新的邮箱!");
}
user = new User();
user.setId(loginUser.getId());
user.setEmail(emailAccount);
boolean bindEmailResult = this.updateById(user);
if (!bindEmailResult) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "邮箱绑定失败,请稍后再试!");
}
loginUser.setEmail(emailAccount);
return loginUser;
}
@Override
public UserVO userUnBindEmail(UserUnBindEmailRequest userUnBindEmailRequest, HttpServletRequest request) {
String emailAccount = userUnBindEmailRequest.getEmailAccount();
String captcha = userUnBindEmailRequest.getCaptcha();
if (StringUtils.isAnyBlank(emailAccount, captcha)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String emailPattern = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
if (!Pattern.matches(emailPattern, emailAccount)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "不合法的邮箱地址!");
}
String cacheCaptcha = redisTemplate.opsForValue().get(CAPTCHA_CACHE_KEY + emailAccount);
if (StringUtils.isBlank(cacheCaptcha)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码已过期,请重新获取");
}
captcha = captcha.trim();
if (!cacheCaptcha.equals(captcha)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "验证码输入有误");
}
// 查询用户是否绑定该邮箱
UserVO loginUser = this.getLoginUser(request);
if (loginUser.getEmail() == null) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "该账号未绑定邮箱");
}
if (!emailAccount.equals(loginUser.getEmail())) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "该账号未绑定此邮箱");
}
User user = new User();
user.setId(loginUser.getId());
user.setEmail("");
boolean bindEmailResult = this.updateById(user);
if (!bindEmailResult) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "邮箱解绑失败,请稍后再试!");
}
loginUser.setEmail(null);
return loginUser;
}
/*
* 校验
* */
@Override
public void validUser(User user, boolean add) {
if (user == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String userAccount = user.getUserAccount();
String userPassword = user.getUserPassword();
Integer balance = user.getBalance();
// 创建时,所有参数必须非空
if (add) {
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 添加用户生成8位邀请码
user.setInvitationCode(generateRandomString(8));
}
// 5. 账户不包含特殊字符
// 匹配由数字、小写字母、大写字母组成的字符串,且字符串的长度至少为1个字符
String pattern = "[0-9a-zA-Z]+";
if (StringUtils.isNotBlank(userAccount) && !userAccount.matches(pattern)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号由数字、小写字母、大写字母组成");
}
if (ObjectUtils.isNotEmpty(balance) && balance < 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "钱包余额不能为负数");
}
if (StringUtils.isNotBlank(userPassword)) {
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
user.setUserPassword(encryptPassword);
}
// 账户不能重复
if (StringUtils.isNotBlank(userAccount)) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userAccount", userAccount);
long count = userMapper.selectCount(queryWrapper);
if (count > 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
}
}
}
/**
* 生成随机字符串
*
* @param length 长
* @return {@link String}
*/
public String generateRandomString(int length) {
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
int index = random.nextInt(characters.length());
char randomChar = characters.charAt(index);
sb.append(randomChar);
}
return sb.toString();
}
}
十、Mapper层
/**
* @Version: 1.0
* @Description: 用户映射器
*/
public interface UserMapper extends BaseMapper<User> {
}