1.用户邮箱登录注册接口实现
This commit is contained in:
25
.idea/MyBatisCodeHelperDatasource.xml
generated
Normal file
25
.idea/MyBatisCodeHelperDatasource.xml
generated
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="MyBatisCodeHelperDatasource">
|
||||||
|
<option name="projectProfile">
|
||||||
|
<ProjectProfile>
|
||||||
|
<option name="controllerTemplateString" value=" #* @vtlvariable name="tableName" type="java.lang.String" *# #* @vtlvariable name="entityPackageName" type="java.lang.String" *# #* @vtlvariable name="entityClassName" type="java.lang.String" *# #* @vtlvariable name="servicePackageName" type="java.lang.String" *# #* @vtlvariable name="serviceInterfacePackage" type="java.lang.String" *# #* @vtlvariable name="serviceClassName" type="java.lang.String" *# #* @vtlvariable name="serviceInterfaceClassName" type="java.lang.String" *# #* @vtlvariable name="mapperPackageName" type="java.lang.String" *# #* @vtlvariable name="mapperClassName" type="java.lang.String" *# #* @vtlvariable name="controllerPackage" type="java.lang.String" *# #* @vtlvariable name="tableRemark" type="java.lang.String" *# #* @vtlvariable name="myDate" type="java.util.Date" *# #* @vtlvariable name="simpleDateFormat" type="java.text.SimpleDateFormat" *# package $!{controllerPackage}; import $!{entityPackageName}.$!{entityClassName}; ###set($realServiceName = $!{serviceClassName}+'Impl') import $!{servicePackageName}.$!{serviceClassName}; import org.springframework.web.bind.annotation.*; #set($serviceFirstLower = $!{serviceClassName.substring(0,1).toLowerCase()}+$!{serviceClassName.substring(1,$!{serviceClassName.length()})}) import org.springframework.beans.factory.annotation.Autowired; /** * $!{tableRemark}($!{tableName})表控制层 * * @author xxxxx */ @RestController @RequestMapping("/$!{tableName}") public class $!{entityClassName}Controller { /** * 服务对象 */ @Autowired private $!{serviceClassName} $!{serviceFirstLower}; /** * 通过主键查询单条数据 * * @param id 主键 * @return 单条数据 */ @GetMapping("selectOne") public $!{entityClassName} selectOne(Integer id) { return $!{serviceFirstLower}.selectByPrimaryKey(id); } }" />
|
||||||
|
<option name="tableGenerateConfigs">
|
||||||
|
<map>
|
||||||
|
<entry key="vv_assistant:user">
|
||||||
|
<value>
|
||||||
|
<TableGenerateConfig>
|
||||||
|
<option name="generatedKey" value="id" />
|
||||||
|
<option name="javaModelName" value="User" />
|
||||||
|
<option name="sequenceColumn" value="" />
|
||||||
|
<option name="sequenceId" value="" />
|
||||||
|
<option name="useActualColumnName" value="false" />
|
||||||
|
</TableGenerateConfig>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</ProjectProfile>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
pom.xml
6
pom.xml
@@ -141,7 +141,11 @@
|
|||||||
<version>5.8.38</version>
|
<version>5.8.38</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun.mail</groupId>
|
||||||
|
<artifactId>javax.mail</artifactId>
|
||||||
|
<version>1.6.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package vvpkassistant;
|
package vvpkassistant;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableAsync
|
||||||
public class Application {
|
public class Application {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ public class UserController {
|
|||||||
@Resource
|
@Resource
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MailService mailService;
|
||||||
|
|
||||||
// 配置用户信息
|
// 配置用户信息
|
||||||
@PostMapping("inputUserInfo")
|
@PostMapping("inputUserInfo")
|
||||||
public ResponseData<Object> inputUserInfo(@RequestBody Map<String,Object> param) {
|
public ResponseData<Object> inputUserInfo(@RequestBody Map<String,Object> param) {
|
||||||
@@ -371,4 +374,15 @@ public class UserController {
|
|||||||
public ResponseData<Object> mailRegister(@RequestBody UserModelDTO model){
|
public ResponseData<Object> mailRegister(@RequestBody UserModelDTO model){
|
||||||
return ResponseData.success(userService.addUserWithMail(model));
|
return ResponseData.success(userService.addUserWithMail(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/activate")
|
||||||
|
public ResponseData<Object> activate(@RequestParam("token") String token){
|
||||||
|
return ResponseData.success(userService.activateAccount(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/resendMail")
|
||||||
|
public ResponseData<Object> resendMail(@RequestBody UserModelDTO userModelDTO){
|
||||||
|
return ResponseData.success(mailService.resendMail(userModelDTO));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package vvpkassistant.User.model.DTO;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -24,5 +23,4 @@ public class UserModelDTO {
|
|||||||
private String newPassword;
|
private String newPassword;
|
||||||
private String oldPassword;
|
private String oldPassword;
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,4 +17,6 @@ public interface UserService extends IService<UserModel> {
|
|||||||
UserModelVO updateUserInfo(UserModelDTO userModelDTO);
|
UserModelVO updateUserInfo(UserModelDTO userModelDTO);
|
||||||
|
|
||||||
UserModelVO addUserWithMail(UserModelDTO model);
|
UserModelVO addUserWithMail(UserModelDTO model);
|
||||||
|
|
||||||
|
Boolean activateAccount(String token);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package vvpkassistant.User.service;
|
package vvpkassistant.User.service;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.dev33.satoken.temp.SaTempUtil;
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
@@ -15,7 +16,7 @@ import vvpkassistant.User.model.UserModel;
|
|||||||
import vvpkassistant.User.model.UserModelVO;
|
import vvpkassistant.User.model.UserModelVO;
|
||||||
import vvpkassistant.common.ErrorCode;
|
import vvpkassistant.common.ErrorCode;
|
||||||
import vvpkassistant.exception.BusinessException;
|
import vvpkassistant.exception.BusinessException;
|
||||||
import vvpkassistant.mail.model.MailModel;
|
import vvpkassistant.mail.service.MailService;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
@@ -31,6 +32,8 @@ public class UserServiceImpl extends ServiceImpl<UserDao, UserModel> implements
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private WxChatParam wxChatParam;
|
private WxChatParam wxChatParam;
|
||||||
|
@Autowired
|
||||||
|
private MailService mailService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModelVO loginWithMail(UserModelDTO model) {
|
public UserModelVO loginWithMail(UserModelDTO model) {
|
||||||
@@ -96,33 +99,52 @@ public class UserServiceImpl extends ServiceImpl<UserDao, UserModel> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModelVO addUserWithMail(UserModelDTO model) {
|
public UserModelVO addUserWithMail(UserModelDTO userModelDTO) {
|
||||||
|
|
||||||
LambdaQueryWrapper<UserModel> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<UserModel> lambdaQueryWrapper = new LambdaQueryWrapper<>();
|
||||||
lambdaQueryWrapper.eq(UserModel::getEmail,model.getEmail());
|
lambdaQueryWrapper.eq(UserModel::getEmail,userModelDTO.getEmail());
|
||||||
UserModel userModel = userDao.selectOne(lambdaQueryWrapper);
|
UserModel userModel = userDao.selectOne(lambdaQueryWrapper);
|
||||||
if (userModel != null) {
|
if (userModel != null) {
|
||||||
throw new BusinessException(ErrorCode.MAIL_ALREADY_EXIST);
|
throw new BusinessException(ErrorCode.MAIL_ALREADY_EXIST);
|
||||||
}
|
}
|
||||||
if (model.getPassword().length() < 6 ){
|
if (userModelDTO.getPassword().length() < 6 ){
|
||||||
throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码长度不能小于 6 位");
|
throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码长度不能小于 6 位");
|
||||||
}
|
}
|
||||||
model.setPassword(BcryptUtils.encryptPassword(model.getPassword()));
|
userModelDTO.setPassword(BcryptUtils.encryptPassword(userModelDTO.getPassword()));
|
||||||
model.setCreateTime(VVTools.currentTimeStamp());
|
userModelDTO.setCreateTime(VVTools.currentTimeStamp());
|
||||||
//设置状态为正常
|
//设置状态为待验证
|
||||||
model.setStatus(2);
|
userModelDTO.setStatus(2);
|
||||||
//设置积分为0
|
//设置积分为0
|
||||||
model.setPoints(0);
|
userModelDTO.setPoints(0);
|
||||||
userDao.insert(BeanUtil.copyProperties(model, UserModel.class));
|
UserModel userModelEntity = BeanUtil.copyProperties(userModelDTO, UserModel.class);
|
||||||
|
if ( userDao.insert(userModelEntity) != 1){
|
||||||
|
throw new BusinessException(ErrorCode.ADD_FAILED,"用户注册失败");
|
||||||
|
}
|
||||||
|
mailService.sendMail(userModelDTO.getEmail(),userModelEntity.getId());
|
||||||
// 判断用户是否为邀请用户
|
// 判断用户是否为邀请用户
|
||||||
if (model.getInviterId() != null) {
|
if (userModelDTO.getInviterId() != null) {
|
||||||
UserModel oldUser = userDao.selectById(model.getInviterId());
|
UserModel oldUser = userDao.selectById(userModelDTO.getInviterId());
|
||||||
oldUser.setPoints(oldUser.getPoints() + 10);
|
oldUser.setPoints(oldUser.getPoints() + 10);
|
||||||
userDao.updateById(oldUser);
|
userDao.updateById(oldUser);
|
||||||
}
|
}
|
||||||
UserModelVO userModelVO = BeanUtil.copyProperties(model, UserModelVO.class);
|
UserModelVO userModelVO = BeanUtil.copyProperties(userModelEntity, UserModelVO.class);
|
||||||
userModelVO.setNewAccount(true);
|
userModelVO.setNewAccount(true);
|
||||||
userModelVO.setChatInfo(wxChatParam);
|
userModelVO.setChatInfo(wxChatParam);
|
||||||
return userModelVO;
|
return userModelVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean activateAccount(String token) {
|
||||||
|
Integer userId = SaTempUtil.parseToken(token, Integer.class);
|
||||||
|
UserModel userModel = userDao.selectById(userId);
|
||||||
|
if (userModel == null) {
|
||||||
|
throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST);
|
||||||
|
}
|
||||||
|
userModel.setStatus(0);
|
||||||
|
if (userDao.updateById(userModel) == 1){
|
||||||
|
return true;
|
||||||
|
}else {
|
||||||
|
throw new BusinessException(ErrorCode.UPDATE_FAILED,"激活失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public enum ErrorCode {
|
|||||||
UPDATE_FAILED(1003, "更新失败"),
|
UPDATE_FAILED(1003, "更新失败"),
|
||||||
CONFIG_NAME_DUPLICATE(1004, "配置名称重复"),
|
CONFIG_NAME_DUPLICATE(1004, "配置名称重复"),
|
||||||
SIGN_IN_FAIL(1004, "当天已签到"),
|
SIGN_IN_FAIL(1004, "当天已签到"),
|
||||||
|
EMAIL_SEND_FREQUENT(1005,"邮件发送太频繁,请 1 分钟后再试"),
|
||||||
|
|
||||||
/* =============== 主播相关 =============== */
|
/* =============== 主播相关 =============== */
|
||||||
ANCHOR_ALREADY_EXISTS(2001, "主播已存在"),
|
ANCHOR_ALREADY_EXISTS(2001, "主播已存在"),
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
|||||||
private String[] getExcludePaths() {
|
private String[] getExcludePaths() {
|
||||||
return new String[]{
|
return new String[]{
|
||||||
// Swagger & Knife4j 相关
|
// Swagger & Knife4j 相关
|
||||||
|
"/error",
|
||||||
"/doc.html",
|
"/doc.html",
|
||||||
"/webjars/**",
|
"/webjars/**",
|
||||||
"/swagger-resources/**",
|
"/swagger-resources/**",
|
||||||
@@ -42,8 +43,9 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
|||||||
"/favicon.ico",
|
"/favicon.ico",
|
||||||
// 你的其他放行路径,例如登录接口
|
// 你的其他放行路径,例如登录接口
|
||||||
"/user/loginWithPhoneNumber",
|
"/user/loginWithPhoneNumber",
|
||||||
"/user/registerWithMail",
|
"/user/registerWithMail",
|
||||||
"/user/loginWithMail"
|
"/user/loginWithMail",
|
||||||
|
"/user/activate"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package vvpkassistant.mail.service;
|
package vvpkassistant.mail.service;
|
||||||
|
|
||||||
|
import vvpkassistant.User.model.DTO.UserModelDTO;
|
||||||
import vvpkassistant.mail.model.MailModel;
|
import vvpkassistant.mail.model.MailModel;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -7,5 +8,8 @@ import vvpkassistant.mail.model.MailModel;
|
|||||||
* @date: 2025/8/4 15:42
|
* @date: 2025/8/4 15:42
|
||||||
*/
|
*/
|
||||||
public interface MailService {
|
public interface MailService {
|
||||||
Boolean sendMail(MailModel model);
|
|
||||||
|
void sendMail(String emailAddress,Integer userId);
|
||||||
|
|
||||||
|
Boolean resendMail(UserModelDTO userModelDTO);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +1,208 @@
|
|||||||
package vvpkassistant.mail.service;
|
package vvpkassistant.mail.service;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.temp.SaTempUtil;
|
||||||
|
import cn.hutool.extra.mail.MailUtil;
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import com.mailgun.api.v3.MailgunMessagesApi;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import com.mailgun.client.MailgunClient;
|
|
||||||
import com.mailgun.model.message.Message;
|
|
||||||
import com.mailgun.model.message.MessageResponse;
|
|
||||||
import feign.AsyncClient;
|
|
||||||
import feign.Client;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import vvpkassistant.mail.model.MailModel;
|
import vvpkassistant.User.model.DTO.UserModelDTO;
|
||||||
|
import vvpkassistant.common.ErrorCode;
|
||||||
|
import vvpkassistant.exception.BusinessException;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @author: ziin
|
* @author: ziin
|
||||||
* @date: 2025/8/4 15:42
|
* @date: 2025/8/4 15:42
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class MailServiceImpl implements MailService {
|
public class MailServiceImpl implements MailService {
|
||||||
|
|
||||||
@Value("${mailgun}")
|
|
||||||
private String mailgunKey;
|
|
||||||
|
|
||||||
public final Cache<String, String> codeCache = Caffeine.newBuilder()
|
@Value("${activateUrl}")
|
||||||
.expireAfterWrite(10, TimeUnit.MINUTES) // 5 分钟过期
|
private String activateUrl;
|
||||||
.maximumSize(10_000) // 防止内存暴涨
|
|
||||||
|
private final Cache<String, Object> emailSendCache = Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean sendMail(MailModel model) {
|
@Async("taskExecutor")
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(2);
|
public void sendMail(String emailAddress,Integer userId) {
|
||||||
AsyncClient.Default<Object> asyncClient = new AsyncClient.Default<>(
|
log.info("Sending email to {}", emailAddress);
|
||||||
new Client.Default(null, null), executor);
|
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>Account Activation</h1>\n" +
|
||||||
|
" <p class=\"subtitle\">Please click the button below to activate your account</p>\n" +
|
||||||
|
" \n" +
|
||||||
|
" <a href=\""+ activateUrl+ token + "\" class=\"activate-button\">\n" +
|
||||||
|
" Activate Account\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 24 hours<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);
|
||||||
|
}
|
||||||
|
|
||||||
MailgunMessagesApi mailgunAsyncMessagesApi = MailgunClient.config(mailgunKey)
|
@Override
|
||||||
.client(asyncClient)
|
public Boolean resendMail(UserModelDTO userModelDTO) {
|
||||||
.createAsyncApi(MailgunMessagesApi.class);
|
try {
|
||||||
|
sendMail(userModelDTO.getEmail(), userModelDTO.getId());
|
||||||
Message message = Message.builder()
|
return true;
|
||||||
.from("")
|
}catch (Exception e){
|
||||||
.to(model.getMailAddress())
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR,e.getMessage());
|
||||||
.subject("your mail address Verification code is : ")
|
}
|
||||||
.html("<html>\n" +
|
|
||||||
"<body>\n" +
|
|
||||||
"\t<h1>Sending HTML emails with Mailgun</h1>\n" +
|
|
||||||
"\t<p style=\"color:blue; font-size:30px;\">Hello world</p>\n" +
|
|
||||||
"\t<p style=\"font-size:30px;\">More examples can be found <a href=\"https://documentation.mailgun.com/en/latest/api-sending.html#examples\">here</a></p>\n" +
|
|
||||||
"</body>\n" +
|
|
||||||
"</html>")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
MessageResponse messageResponse = mailgunAsyncMessagesApi.sendMessage("www.baidu.com", message);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,25 @@ server:
|
|||||||
spring:
|
spring:
|
||||||
profiles:
|
profiles:
|
||||||
active: local
|
active: local
|
||||||
|
task:
|
||||||
|
# Spring 执行器配置,对应 TaskExecutionProperties 配置类。对于 Spring 异步任务,会使用该执行器。
|
||||||
|
execution:
|
||||||
|
thread-name-prefix: mail-task # 线程池的线程名的前缀。默认为 task- ,建议根据自己应用来设置
|
||||||
|
pool: # 线程池相关
|
||||||
|
core-size: 10 # 核心线程数,线程池创建时候初始化的线程数。默认为 8 。
|
||||||
|
max-size: 20 # 最大线程数,线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE
|
||||||
|
keep-alive: 60s # 允许线程的空闲时间,当超过了核心线程之外的线程,在空闲时间到达之后会被销毁。默认为 60 秒
|
||||||
|
queue-capacity: 200 # 缓冲队列大小,用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。
|
||||||
|
allow-core-thread-timeout: true # 是否允许核心线程超时,即开启线程池的动态增长和缩小。默认为 true 。
|
||||||
|
shutdown:
|
||||||
|
await-termination: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
|
||||||
|
await-termination-period: 60 # 等待任务完成的最大时长,单位为秒。默认为 0 ,根据自己应用来设置
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-plus:
|
||||||
mailgun: b4888f65c7fd452213f3a8aea9d88269-812b35f5-006d7fd6
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: auto
|
||||||
|
|
||||||
|
|
||||||
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
||||||
@@ -15,9 +30,9 @@ sa-token:
|
|||||||
# token 名称(同时也是 cookie 名称)
|
# token 名称(同时也是 cookie 名称)
|
||||||
token-name: token
|
token-name: token
|
||||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||||
timeout: 2592000
|
timeout: -1
|
||||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||||
active-timeout: -1
|
active-timeout: 648000
|
||||||
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||||
is-concurrent: true
|
is-concurrent: true
|
||||||
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||||
@@ -26,3 +41,5 @@ sa-token:
|
|||||||
token-style: random-128
|
token-style: random-128
|
||||||
# 是否输出操作日志
|
# 是否输出操作日志
|
||||||
is-log: true
|
is-log: true
|
||||||
|
|
||||||
|
activateUrl: http://192.168.1.174:8086/user/activate?token=
|
||||||
10
src/main/resources/mail.setting
Normal file
10
src/main/resources/mail.setting
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# 邮件服务器的SMTP地址,可选,默认为smtp.<发件人邮箱后缀>
|
||||||
|
host = smtp.exmail.qq.com
|
||||||
|
# 邮件服务器的SMTP端口,可选,默认25
|
||||||
|
port = 465
|
||||||
|
# 发件人(必须正确,否则发送失败)
|
||||||
|
from = chenfu@bilibili.so
|
||||||
|
# 用户名,默认为发件人邮箱前缀
|
||||||
|
user = chenfu@bilibili.so
|
||||||
|
pass = Yt7rWTizdAvE4dc7
|
||||||
|
sslEnable = true
|
||||||
@@ -4,10 +4,25 @@ server:
|
|||||||
spring:
|
spring:
|
||||||
profiles:
|
profiles:
|
||||||
active: local
|
active: local
|
||||||
|
task:
|
||||||
|
# Spring 执行器配置,对应 TaskExecutionProperties 配置类。对于 Spring 异步任务,会使用该执行器。
|
||||||
|
execution:
|
||||||
|
thread-name-prefix: mail-task # 线程池的线程名的前缀。默认为 task- ,建议根据自己应用来设置
|
||||||
|
pool: # 线程池相关
|
||||||
|
core-size: 10 # 核心线程数,线程池创建时候初始化的线程数。默认为 8 。
|
||||||
|
max-size: 20 # 最大线程数,线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE
|
||||||
|
keep-alive: 60s # 允许线程的空闲时间,当超过了核心线程之外的线程,在空闲时间到达之后会被销毁。默认为 60 秒
|
||||||
|
queue-capacity: 200 # 缓冲队列大小,用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。
|
||||||
|
allow-core-thread-timeout: true # 是否允许核心线程超时,即开启线程池的动态增长和缩小。默认为 true 。
|
||||||
|
shutdown:
|
||||||
|
await-termination: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
|
||||||
|
await-termination-period: 60 # 等待任务完成的最大时长,单位为秒。默认为 0 ,根据自己应用来设置
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-plus:
|
||||||
mailgun: b4888f65c7fd452213f3a8aea9d88269-812b35f5-006d7fd6
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: auto
|
||||||
|
|
||||||
|
|
||||||
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
||||||
@@ -26,3 +41,5 @@ sa-token:
|
|||||||
token-style: random-128
|
token-style: random-128
|
||||||
# 是否输出操作日志
|
# 是否输出操作日志
|
||||||
is-log: true
|
is-log: true
|
||||||
|
|
||||||
|
activateUrl: http://192.168.1.174:8086/user/activate?token=
|
||||||
Reference in New Issue
Block a user