1.用户修改邮箱逻辑
2.用户验证邮箱逻辑
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,3 +27,4 @@ replay_pid*
|
||||
/.idea/
|
||||
/target/
|
||||
|
||||
|
||||
|
||||
BIN
.idea/.cache/.Apifox_Helper/.toolWindow.db
generated
BIN
.idea/.cache/.Apifox_Helper/.toolWindow.db
generated
Binary file not shown.
@@ -375,8 +375,8 @@ public class UserController {
|
||||
return ResponseData.success(userService.addUserWithMail(model));
|
||||
}
|
||||
|
||||
@GetMapping("/activate")
|
||||
public ResponseData<Object> activate(@RequestParam("token") String token){
|
||||
@GetMapping("/activateAccount")
|
||||
public ResponseData<Object> activateAccount(@RequestParam("token") String token){
|
||||
return ResponseData.success(userService.activateAccount(token));
|
||||
}
|
||||
|
||||
@@ -385,4 +385,8 @@ public class UserController {
|
||||
return ResponseData.success(mailService.resendMail(userModelDTO));
|
||||
}
|
||||
|
||||
@GetMapping("/verificationMail")
|
||||
public ResponseData<Object> verificationMail(@RequestParam("token") String token){
|
||||
return ResponseData.success(userService.verificationMail(token));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,5 +22,5 @@ public class UserModel {
|
||||
private Integer inviterId; // 邀请人id
|
||||
private String email;
|
||||
private String password;
|
||||
|
||||
private Integer mailVerification;
|
||||
}
|
||||
|
||||
@@ -19,4 +19,6 @@ public interface UserService extends IService<UserModel> {
|
||||
UserModelVO addUserWithMail(UserModelDTO model);
|
||||
|
||||
Boolean activateAccount(String token);
|
||||
|
||||
Boolean verificationMail(String token);
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ public class UserServiceImpl extends ServiceImpl<UserDao, UserModel> implements
|
||||
userModelDTO.setPassword(BcryptUtils.encryptPassword(userModelDTO.getNewPassword()));
|
||||
}
|
||||
}
|
||||
if (userModelDTO.getEmail() != null) {
|
||||
mailService.sendVerificationMail(userModelDTO.getEmail(), userModelDTO.getId());
|
||||
}
|
||||
|
||||
// 用户有密码的情况下重新设置密码
|
||||
if (userInfo.getPassword() != null && userModelDTO.getOldPassword() != null) {
|
||||
if (BcryptUtils.matchPassword(userModelDTO.getOldPassword(), userInfo.getPassword())) {
|
||||
@@ -117,6 +121,7 @@ public class UserServiceImpl extends ServiceImpl<UserDao, UserModel> implements
|
||||
//设置积分为0
|
||||
userModelDTO.setPoints(0);
|
||||
UserModel userModelEntity = BeanUtil.copyProperties(userModelDTO, UserModel.class);
|
||||
userModelEntity.setMailVerification(1);
|
||||
if ( userDao.insert(userModelEntity) != 1){
|
||||
throw new BusinessException(ErrorCode.ADD_FAILED,"用户注册失败");
|
||||
}
|
||||
@@ -141,10 +146,22 @@ public class UserServiceImpl extends ServiceImpl<UserDao, UserModel> implements
|
||||
throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST);
|
||||
}
|
||||
userModel.setStatus(0);
|
||||
userModel.setMailVerification(0);
|
||||
if (userDao.updateById(userModel) == 1){
|
||||
return true;
|
||||
}else {
|
||||
throw new BusinessException(ErrorCode.UPDATE_FAILED,"激活失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean verificationMail(String token) {
|
||||
Integer userId = SaTempUtil.parseToken(token, Integer.class);
|
||||
UserModel userModel = userDao.selectById(userId);
|
||||
userModel.setMailVerification(0);
|
||||
if (userDao.updateById(userModel) == 1){
|
||||
return true;
|
||||
}
|
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"邮箱验证失败");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,5 +11,9 @@ public interface MailService {
|
||||
|
||||
void sendMail(String emailAddress,Integer userId);
|
||||
|
||||
void sendVerificationMail(String emailAddress,Integer userId);
|
||||
|
||||
|
||||
|
||||
Boolean resendMail(UserModelDTO userModelDTO);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ public class MailServiceImpl implements MailService {
|
||||
@Value("${activateUrl}")
|
||||
private String activateUrl;
|
||||
|
||||
@Value("${verificationMailUrl}")
|
||||
private String verificationMailUrl;
|
||||
|
||||
|
||||
private final Cache<String, Object> emailSendCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build();
|
||||
@@ -196,6 +200,170 @@ public class MailServiceImpl implements MailService {
|
||||
emailSendCache.put(emailAddress, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendVerificationMail(String emailAddress, Integer userId) {
|
||||
log.info("Sending email to {}", emailAddress);
|
||||
String token = SaTempUtil.createToken(userId, 600);
|
||||
if (emailSendCache.getIfPresent(emailAddress) != null) {
|
||||
throw new BusinessException(ErrorCode.EMAIL_SEND_FREQUENT);
|
||||
}
|
||||
MailUtil.send(emailAddress, "验证你的邮箱", "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"en\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
|
||||
" <title>Account Activation</title>\n" +
|
||||
" <style>\n" +
|
||||
" * {\n" +
|
||||
" margin: 0;\n" +
|
||||
" padding: 0;\n" +
|
||||
" box-sizing: border-box;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" body {\n" +
|
||||
" font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n" +
|
||||
" background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n" +
|
||||
" min-height: 100vh;\n" +
|
||||
" display: flex;\n" +
|
||||
" align-items: center;\n" +
|
||||
" justify-content: center;\n" +
|
||||
" padding: 20px;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .container {\n" +
|
||||
" background: white;\n" +
|
||||
" border-radius: 12px;\n" +
|
||||
" box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);\n" +
|
||||
" padding: 40px;\n" +
|
||||
" text-align: center;\n" +
|
||||
" max-width: 400px;\n" +
|
||||
" width: 100%;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .icon {\n" +
|
||||
" width: 60px;\n" +
|
||||
" height: 60px;\n" +
|
||||
" background: #4CAF50;\n" +
|
||||
" border-radius: 50%;\n" +
|
||||
" display: flex;\n" +
|
||||
" align-items: center;\n" +
|
||||
" justify-content: center;\n" +
|
||||
" margin: 0 auto 20px;\n" +
|
||||
" color: white;\n" +
|
||||
" font-size: 24px;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" h1 {\n" +
|
||||
" color: #333;\n" +
|
||||
" font-size: 24px;\n" +
|
||||
" margin-bottom: 10px;\n" +
|
||||
" font-weight: 600;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .subtitle {\n" +
|
||||
" color: #666;\n" +
|
||||
" font-size: 16px;\n" +
|
||||
" margin-bottom: 30px;\n" +
|
||||
" line-height: 1.5;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .activate-button {\n" +
|
||||
" display: inline-block;\n" +
|
||||
" background: #4CAF50;\n" +
|
||||
" border: none;\n" +
|
||||
" border-radius: 8px;\n" +
|
||||
" padding: 15px 30px;\n" +
|
||||
" margin: 20px 0;\n" +
|
||||
" font-size: 18px;\n" +
|
||||
" font-weight: bold;\n" +
|
||||
" color: white;\n" +
|
||||
" text-decoration: none;\n" +
|
||||
" cursor: pointer;\n" +
|
||||
" transition: all 0.3s ease;\n" +
|
||||
" box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n" +
|
||||
" }\n" +
|
||||
" \n" +
|
||||
" .activate-button:hover {\n" +
|
||||
" background: #45a049;\n" +
|
||||
" transform: translateY(-2px);\n" +
|
||||
" box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);\n" +
|
||||
" }\n" +
|
||||
" \n" +
|
||||
" .activate-button:active {\n" +
|
||||
" transform: translateY(0);\n" +
|
||||
" box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .info {\n" +
|
||||
" background: #e3f2fd;\n" +
|
||||
" border-left: 4px solid #2196F3;\n" +
|
||||
" padding: 15px;\n" +
|
||||
" margin: 20px 0;\n" +
|
||||
" text-align: left;\n" +
|
||||
" border-radius: 4px;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .info-title {\n" +
|
||||
" font-weight: 600;\n" +
|
||||
" color: #1976D2;\n" +
|
||||
" margin-bottom: 5px;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .info-text {\n" +
|
||||
" color: #424242;\n" +
|
||||
" font-size: 14px;\n" +
|
||||
" line-height: 1.4;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .footer {\n" +
|
||||
" margin-top: 30px;\n" +
|
||||
" padding-top: 20px;\n" +
|
||||
" border-top: 1px solid #eee;\n" +
|
||||
" color: #999;\n" +
|
||||
" font-size: 12px;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" @media (max-width: 480px) {\n" +
|
||||
" .container {\n" +
|
||||
" padding: 30px 20px;\n" +
|
||||
" }\n" +
|
||||
" \n" +
|
||||
" .verification-code {\n" +
|
||||
" font-size: 24px;\n" +
|
||||
" letter-spacing: 2px;\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" </style>\n" +
|
||||
"</head>\n" +
|
||||
"<body>\n" +
|
||||
" <div class=\"container\">\n" +
|
||||
" <div class=\"icon\">✉</div>\n" +
|
||||
" \n" +
|
||||
" <h1>Verification Mail</h1>\n" +
|
||||
" <p class=\"subtitle\">Please click the button below to verification your mail</p>\n" +
|
||||
" \n" +
|
||||
" <a href=\""+ verificationMailUrl+ token + "\" class=\"activate-button\">\n" +
|
||||
" Verification\n" +
|
||||
" </a>\n" +
|
||||
" \n" +
|
||||
" <div class=\"info\">\n" +
|
||||
" <div class=\"info-title\">Important Notice:</div>\n" +
|
||||
" <div class=\"info-text\">\n" +
|
||||
" • This activation link is valid for 10 minutes<br>\n" +
|
||||
" • Please do not share this link with anyone<br>\n" +
|
||||
" • If you didn't register an account, please ignore this message\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" \n" +
|
||||
" <div class=\"footer\">\n" +
|
||||
" This is an automated message. Please do not reply to this email.\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>\n", true);
|
||||
emailSendCache.put(emailAddress, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean resendMail(UserModelDTO userModelDTO) {
|
||||
try {
|
||||
|
||||
@@ -42,4 +42,5 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
activateUrl: http://192.168.1.174:8086/user/activate?token=
|
||||
activateUrl: http://192.168.1.174:8086/user/activate?token=
|
||||
verificationMailUrl: http://192.168.1.174:8086/user/verification?token=
|
||||
Reference in New Issue
Block a user