diff --git a/.idea/.cache/.Apifox_Helper/.toolWindow.db b/.idea/.cache/.Apifox_Helper/.toolWindow.db index b7b7bfd..11b441f 100644 Binary files a/.idea/.cache/.Apifox_Helper/.toolWindow.db and b/.idea/.cache/.Apifox_Helper/.toolWindow.db differ diff --git a/src/main/java/vvpkassistant/Tools/CacheHolder.java b/src/main/java/vvpkassistant/Tools/CacheHolder.java new file mode 100644 index 0000000..5535a28 --- /dev/null +++ b/src/main/java/vvpkassistant/Tools/CacheHolder.java @@ -0,0 +1,16 @@ +package vvpkassistant.Tools; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import java.util.concurrent.TimeUnit; + +/* + * @author: ziin + * @date: 2025/8/6 20:15 + */ +public class CacheHolder { + public static final Cache VERIFICATION_MAIL = Caffeine.newBuilder() + .expireAfterWrite(5, TimeUnit.MINUTES) + .build(); +} diff --git a/src/main/java/vvpkassistant/User/controller/UserController.java b/src/main/java/vvpkassistant/User/controller/UserController.java index 20cdeb3..93fad65 100644 --- a/src/main/java/vvpkassistant/User/controller/UserController.java +++ b/src/main/java/vvpkassistant/User/controller/UserController.java @@ -1,6 +1,7 @@ package vvpkassistant.User.controller; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.extra.mail.Mail; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import vvpkassistant.CoinRecords.CoinRecords; @@ -397,8 +398,8 @@ public class UserController { } @PostMapping("/resendMail") - public ResponseData resendMail(@RequestBody UserModelDTO userModelDTO){ - return ResponseData.success(mailService.resendMail(userModelDTO)); + public ResponseData resendMail(@RequestBody MailModel mailModel){ + return ResponseData.success(mailService.resendMail(mailModel)); } @GetMapping("/verificationMail") @@ -406,6 +407,10 @@ public class UserController { return ResponseData.success(userService.verificationMail(token)); } + @PostMapping("/forgetMail") + public ResponseData sendForgetPassWordMail(@RequestBody MailModel mailModel){ + return ResponseData.success(userService.sendForgetPassWordMail(mailModel)); + } @GetMapping("/qrcode") public ResponseData generatedQrcode(){ @@ -428,4 +433,30 @@ public class UserController { userService.confirm(scanInfoDTO); return ResponseData.success(""); } + + @PostMapping("/logout") + public ResponseData logOut(@RequestBody UserModelDTO userModelDTO){ + userService.logOut(userModelDTO.getId()); + return ResponseData.success(""); + } + + @PostMapping("/setPassword") + public ResponseDatasetPassWord(@RequestBody UserModelDTO userModelDTO){ + return ResponseData.success(userService.setPassWord(userModelDTO)); + } + + @GetMapping("/resetPassword/") + public ResponseDataresetPassWord(@RequestBody UserModelDTO userModelDTO){ + return ResponseData.success(userService.resetPassWord(userModelDTO)); + } + + @PostMapping("/updateUserMail") + public ResponseDataupdateUserMail(@RequestBody MailModel mailModel){ + return ResponseData.success(userService.updateUserMail(mailModel)); + } + + @PostMapping("/sendUpdateMailConfirmMail") + public ResponseDatasendUpdateMailConfirmMail(@RequestBody MailModel mailModel){ + return ResponseData.success(mailService.sendUpdateConfirmMail(mailModel)); + } } diff --git a/src/main/java/vvpkassistant/User/model/DTO/UserModelDTO.java b/src/main/java/vvpkassistant/User/model/DTO/UserModelDTO.java index 25b6e30..2455764 100644 --- a/src/main/java/vvpkassistant/User/model/DTO/UserModelDTO.java +++ b/src/main/java/vvpkassistant/User/model/DTO/UserModelDTO.java @@ -21,6 +21,8 @@ public class UserModelDTO { private Integer inviterId; // 邀请人id private String email; private String newPassword; + private String confirmPassword; private String oldPassword; private String password; + private String token; } diff --git a/src/main/java/vvpkassistant/User/service/UserService.java b/src/main/java/vvpkassistant/User/service/UserService.java index fecd2ea..e7ebeb1 100644 --- a/src/main/java/vvpkassistant/User/service/UserService.java +++ b/src/main/java/vvpkassistant/User/service/UserService.java @@ -5,7 +5,7 @@ import vvpkassistant.User.model.DTO.ScanInfoDTO; import vvpkassistant.User.model.DTO.UserModelDTO; import vvpkassistant.User.model.UserModel; import vvpkassistant.User.model.UserModelVO; - +import vvpkassistant.mail.model.MailModel; /* @@ -30,4 +30,14 @@ public interface UserService extends IService { void scanQrcode(ScanInfoDTO scanInfoDTO); void confirm(ScanInfoDTO scanInfoDTO); + + void logOut(Integer id); + + boolean setPassWord(UserModelDTO userModelDTO); + + Object sendForgetPassWordMail(MailModel mailModel); + + Object resetPassWord(UserModelDTO userModelDTO); + + Boolean updateUserMail(MailModel mailModel); } diff --git a/src/main/java/vvpkassistant/User/service/UserServiceImpl.java b/src/main/java/vvpkassistant/User/service/UserServiceImpl.java index e4f0db4..5253839 100644 --- a/src/main/java/vvpkassistant/User/service/UserServiceImpl.java +++ b/src/main/java/vvpkassistant/User/service/UserServiceImpl.java @@ -12,9 +12,11 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.google.zxing.WriterException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import vvpkassistant.Data.WxChatParam; import vvpkassistant.Tools.BcryptUtils; +import vvpkassistant.Tools.CacheHolder; import vvpkassistant.Tools.QRCodeUtil; import vvpkassistant.Tools.VVTools; import vvpkassistant.User.mapper.UserDao; @@ -25,6 +27,7 @@ import vvpkassistant.User.model.DTO.UserModelDTO; import vvpkassistant.User.model.enumeration.LoginStatusEnum; import vvpkassistant.common.ErrorCode; import vvpkassistant.exception.BusinessException; +import vvpkassistant.mail.model.MailModel; import vvpkassistant.mail.service.MailService; import javax.annotation.Resource; @@ -53,6 +56,9 @@ public class UserServiceImpl extends ServiceImpl implements .expireAfterWrite(2, TimeUnit.MINUTES) .build(); + + + @Override public UserModelVO loginWithMail(UserModelDTO model) { @@ -99,7 +105,7 @@ public class UserServiceImpl extends ServiceImpl implements // 用户有密码的情况下重新设置密码 if (userInfo.getPassword() != null && userModelDTO.getOldPassword() != null) { - if (BcryptUtils.matchPassword(userModelDTO.getOldPassword(), userInfo.getPassword())) { + if (BcryptUtils.matchPassword(userInfo.getPassword(),userModelDTO.getOldPassword())) { userModelDTO.setPassword(BcryptUtils.encryptPassword(userModelDTO.getNewPassword())); }else { throw new BusinessException(ErrorCode.PASSWORD_ERROR,"旧密码不正确"); @@ -150,6 +156,7 @@ public class UserServiceImpl extends ServiceImpl implements userDao.updateById(oldUser); } UserModelVO userModelVO = BeanUtil.copyProperties(userModelEntity, UserModelVO.class); + userModelVO.setHavaPassword(true); userModelVO.setNewAccount(true); userModelVO.setChatInfo(wxChatParam); return userModelVO; @@ -251,4 +258,77 @@ public class UserServiceImpl extends ServiceImpl implements } log.info("-------确认登录成功uuid:{}-------", scanInfoDTO.getUuid()); } + + @Override + public void logOut(Integer id) { + StpUtil.logout(id); + } + + @Override + public boolean setPassWord(UserModelDTO userModelDTO) { + UserModel userModel = userDao.selectById(userModelDTO.getId()); + if (userModel == null) { + throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); + } + if (userModel.getPassword()!= null){ + throw new BusinessException(ErrorCode.SYSTEM_ERROR,"已设置过密码"); + } + if (userModelDTO.getPassword().length()< 6 ){ + throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码长度不能小于 6 位"); + } + if (!Objects.equals(userModelDTO.getPassword(), userModelDTO.getConfirmPassword())) { + log.error("密码{},确认密码{}",userModelDTO.getPassword(),userModelDTO.getConfirmPassword()); + throw new BusinessException(ErrorCode.PARAMS_ERROR,"两次密码输入不一致"); + }else{ + UserModel saveEntity = BeanUtil.copyProperties(userModelDTO, UserModel.class); + saveEntity.setPassword(BcryptUtils.encryptPassword(userModelDTO.getPassword())); + return userDao.updateById(saveEntity) == 1 ; + } + } + + @Override + public Object sendForgetPassWordMail(MailModel mailModel) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + UserModel userModel = userDao.selectOne(lambdaQueryWrapper + .eq(UserModel::getEmail, mailModel.getMailAddress()) + .eq(UserModel::getStatus, 0) + .eq(UserModel::getMailVerification, 0)); + if (userModel == null) { + throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); + } + + mailService.sendForgetPassWordMail(mailModel.getMailAddress(),userModel.getId()); + return true; + } + + @Override + public Object resetPassWord(UserModelDTO userModelDTO) { + Integer i = SaTempUtil.parseToken(userModelDTO.getToken(), Integer.class); + UserModel userModel = userDao.selectById(i); + if (userModel == null) { + throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); + } + if (userModelDTO.getPassword().equals(userModelDTO.getConfirmPassword())) { + UserModel updateEntity = BeanUtil.copyProperties(userModelDTO, UserModel.class); + updateEntity.setPassword(BcryptUtils.encryptPassword(updateEntity.getPassword())); + return userDao.updateById(updateEntity) == 1; + } + return false; + } + + @Override + public Boolean updateUserMail(MailModel mailModel) { + String mail = CacheHolder.VERIFICATION_MAIL.getIfPresent(mailModel.getCode()); + if (mail != null && mail.isEmpty()) { + throw new BusinessException(ErrorCode.SYSTEM_ERROR,"验证码过期或验证码错误"); + } + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + UserModel userModel = userDao.selectOne(lambdaQueryWrapper + .eq(UserModel::getEmail, mail) + .eq(UserModel::getStatus, 0)); + userModel.setEmail(mailModel.getMailAddress()); + mailService.sendVerificationMail(mailModel.getMailAddress(),userModel.getId()); + userModel.setMailVerification(1); + return userDao.updateById(userModel) == 1; + } } diff --git a/src/main/java/vvpkassistant/config/SaTokenConfigure.java b/src/main/java/vvpkassistant/config/SaTokenConfigure.java index e269c02..95a9a8e 100644 --- a/src/main/java/vvpkassistant/config/SaTokenConfigure.java +++ b/src/main/java/vvpkassistant/config/SaTokenConfigure.java @@ -49,7 +49,7 @@ public class SaTokenConfigure implements WebMvcConfigurer { "/user/verificationMail", "/user/activate", "/user/qrcode", - "/user/check/*", + "/user/check/**", "/user/scan", "/user/confirm" diff --git a/src/main/java/vvpkassistant/mail/model/MailModel.java b/src/main/java/vvpkassistant/mail/model/MailModel.java index 8d78ec3..e52e1cd 100644 --- a/src/main/java/vvpkassistant/mail/model/MailModel.java +++ b/src/main/java/vvpkassistant/mail/model/MailModel.java @@ -9,5 +9,7 @@ import lombok.Data; @Data public class MailModel { private String mailAddress; - private Integer code; + private Integer userId; + private Integer type; + private String code; } diff --git a/src/main/java/vvpkassistant/mail/service/MailService.java b/src/main/java/vvpkassistant/mail/service/MailService.java index 8bb5faa..b79a790 100644 --- a/src/main/java/vvpkassistant/mail/service/MailService.java +++ b/src/main/java/vvpkassistant/mail/service/MailService.java @@ -1,6 +1,5 @@ package vvpkassistant.mail.service; -import vvpkassistant.User.model.DTO.UserModelDTO; import vvpkassistant.mail.model.MailModel; /* @@ -13,7 +12,9 @@ public interface MailService { void sendVerificationMail(String emailAddress,Integer userId); + Boolean resendMail(MailModel mailModel); + void sendForgetPassWordMail(String mailAddress, Integer id); - Boolean resendMail(UserModelDTO userModelDTO); + Object sendUpdateConfirmMail(MailModel mailModel); } diff --git a/src/main/java/vvpkassistant/mail/service/MailServiceImpl.java b/src/main/java/vvpkassistant/mail/service/MailServiceImpl.java index 2d685d3..cb0c663 100644 --- a/src/main/java/vvpkassistant/mail/service/MailServiceImpl.java +++ b/src/main/java/vvpkassistant/mail/service/MailServiceImpl.java @@ -1,17 +1,23 @@ package vvpkassistant.mail.service; import cn.dev33.satoken.temp.SaTempUtil; +import cn.hutool.core.util.RandomUtil; import cn.hutool.extra.mail.MailUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import vvpkassistant.User.model.DTO.UserModelDTO; +import vvpkassistant.Tools.CacheHolder; +import vvpkassistant.User.mapper.UserDao; +import vvpkassistant.User.model.UserModel; import vvpkassistant.common.ErrorCode; import vvpkassistant.exception.BusinessException; +import vvpkassistant.mail.model.MailModel; +import javax.annotation.Resource; import java.util.concurrent.TimeUnit; /* @@ -29,8 +35,15 @@ public class MailServiceImpl implements MailService { @Value("${verificationMailUrl}") private String verificationMailUrl; + @Value("${forgetPassWordUrl}") + private String forgetPassWordUrl; - private final Cache emailSendCache = Caffeine.newBuilder() + + @Resource + private UserDao userDao; + + + private static final Cache emailSendCache = Caffeine.newBuilder() .expireAfterWrite(1, TimeUnit.MINUTES) .build(); @@ -365,12 +378,217 @@ public class MailServiceImpl implements MailService { } @Override - public Boolean resendMail(UserModelDTO userModelDTO) { - try { - sendMail(userModelDTO.getEmail(), userModelDTO.getId()); + public Boolean resendMail(MailModel mailModel) { + Object ifPresent = emailSendCache.getIfPresent(mailModel.getMailAddress()); + if (ifPresent == null) { + switch (mailModel.getType()) { + case 1: + sendMail(mailModel.getMailAddress(), mailModel.getUserId()); + break; + case 2: + sendVerificationMail(mailModel.getMailAddress(), mailModel.getUserId()); + break; + case 3: + sendForgetPassWordMail(mailModel.getMailAddress(), mailModel.getUserId()); + break; + } + return true; + }else { + throw new BusinessException(ErrorCode.EMAIL_SEND_FREQUENT); + } + } + + @Override + public void sendForgetPassWordMail(String mailAddress, Integer userId) { + if (checkCache(mailAddress)){ + String token = SaTempUtil.createToken(userId, 600); + MailUtil.send(mailAddress, "验证你的邮箱", "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " Account Activation\n" + + " \n" + + "\n" + + "\n" + + "
\n" + + "
\n" + + " \n" + + "

Verification Mail

\n" + + "

Please click the button below to reset your password

\n" + + " \n" + + " \n" + + " Reset PassWord\n" + + " \n" + + " \n" + + "
\n" + + "
Important Notice:
\n" + + "
\n" + + " • This activation link is valid for 10 minutes
\n" + + " • Please do not share this link with anyone
\n" + + " • If you didn't register an account, please ignore this message\n" + + "
\n" + + "
\n" + + " \n" + + "
\n" + + " This is an automated message. Please do not reply to this email.\n" + + "
\n" + + "
\n" + + "\n" + + "\n", true); + emailSendCache.put(mailAddress, userId); + } + + } + + @Override + public Object sendUpdateConfirmMail(MailModel mailModel) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + UserModel userModel = userDao.selectOne( + lambdaQueryWrapper.eq(UserModel::getEmail, mailModel.getMailAddress()) + .eq(UserModel::getStatus, 0) + .eq(UserModel::getMailVerification, 0)); + if (userModel == null){ + throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); + } + if (checkCache(mailModel.getMailAddress())) { + String code = RandomUtil.randomString(6); + emailSendCache.put(mailModel.getMailAddress(), code); + CacheHolder.VERIFICATION_MAIL.put(code, mailModel.getMailAddress()); + MailUtil.send(mailModel.getMailAddress(),"your Verification code is :" + code, + "your Verification code is :" + code,false); + log.info("sendMailto:{},Verification code is :{}", mailModel.getMailAddress(),code); + } + return true; + } + + + public Boolean checkCache(String mailAddress){ + Object ifPresent = emailSendCache.getIfPresent(mailAddress); + if (ifPresent == null) { return true; - }catch (Exception e){ - throw new BusinessException(ErrorCode.SYSTEM_ERROR,e.getMessage()); + }else { + throw new BusinessException(ErrorCode.EMAIL_SEND_FREQUENT); } } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 054a339..4dc0438 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -43,4 +43,5 @@ sa-token: is-log: true activateUrl: http://192.168.1.174:8086/user/activate?token= -verificationMailUrl: http://192.168.1.174:8086/user/verification?token= \ No newline at end of file +verificationMailUrl: http://192.168.1.174:8086/user/verification?token= +forgetPassWordUrl: http://192.168.1.174:8086/user//resetPassword/?token= \ No newline at end of file diff --git a/src/main/resources/mail.setting b/src/main/resources/mail.setting index 7d89bf0..8a18366 100644 --- a/src/main/resources/mail.setting +++ b/src/main/resources/mail.setting @@ -3,8 +3,8 @@ host = smtp.exmail.qq.com # 邮件服务器的SMTP端口,可选,默认25 port = 465 # 发件人(必须正确,否则发送失败) -from = chenfu@bilibili.so +from = niuyuxi@hanxiaokj.cn # 用户名,默认为发件人邮箱前缀 -user = chenfu@bilibili.so -pass = Yt7rWTizdAvE4dc7 +user = niuyuxi@hanxiaokj.cn +pass = tF35umug9CBCBKqR sslEnable = true \ No newline at end of file diff --git a/target/classes/application.yml b/target/classes/application.yml index 054a339..4dc0438 100644 --- a/target/classes/application.yml +++ b/target/classes/application.yml @@ -43,4 +43,5 @@ sa-token: is-log: true activateUrl: http://192.168.1.174:8086/user/activate?token= -verificationMailUrl: http://192.168.1.174:8086/user/verification?token= \ No newline at end of file +verificationMailUrl: http://192.168.1.174:8086/user/verification?token= +forgetPassWordUrl: http://192.168.1.174:8086/user//resetPassword/?token= \ No newline at end of file