登录注册、手机号、邮箱

This commit is contained in:
2025-11-03 13:37:55 +08:00
parent 16754b527e
commit 35aee59178
26 changed files with 4292 additions and 163 deletions

View File

@@ -31,6 +31,19 @@
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- Spring Mail for Email -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- Aliyun SMS Service -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>4.2.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,152 @@
package org.xyzh.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
/**
* @description 邮件发送工具类
* @filename EmailUtils.java
* @author yslg
* @copyright xyzh
* @since 2025-11-03
*/
@Component
public class EmailUtils {
private static final Logger logger = LoggerFactory.getLogger(EmailUtils.class);
@Autowired
private JavaMailSender mailSender;
@Value("${spring.mail.username:}")
private String from;
/**
* 发送简单文本邮件
* @param to 收件人邮箱
* @param subject 邮件主题
* @param content 邮件内容
* @return 是否发送成功
*/
public boolean sendSimpleEmail(String to, String subject, String content) {
try {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
message.setText(content);
mailSender.send(message);
logger.info("简单邮件发送成功,收件人: {}", to);
return true;
} catch (Exception e) {
logger.error("简单邮件发送失败,收件人: {}, 错误: {}", to, e.getMessage(), e);
return false;
}
}
/**
* 发送HTML格式邮件
* @param to 收件人邮箱
* @param subject 邮件主题
* @param content HTML格式的邮件内容
* @return 是否发送成功
*/
public boolean sendHtmlEmail(String to, String subject, String content) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true); // true表示HTML格式
mailSender.send(message);
logger.info("HTML邮件发送成功收件人: {}", to);
return true;
} catch (MessagingException e) {
logger.error("HTML邮件发送失败收件人: {}, 错误: {}", to, e.getMessage(), e);
return false;
}
}
/**
* 发送验证码邮件
* @param to 收件人邮箱
* @param code 验证码
* @return 是否发送成功
*/
public boolean sendVerificationCode(String to, String code) {
String subject = "【红色思政学习平台】邮箱验证码";
String content = buildVerificationCodeHtml(code);
return sendHtmlEmail(to, subject, content);
}
/**
* 构建验证码邮件的HTML内容
* @param code 验证码
* @return HTML内容
*/
private String buildVerificationCodeHtml(String code) {
return "<!DOCTYPE html>" +
"<html>" +
"<head>" +
"<meta charset=\"UTF-8\">" +
"<style>" +
"body { font-family: 'Microsoft YaHei', Arial, sans-serif; background-color: #f5f5f5; margin: 0; padding: 20px; }" +
".container { max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); overflow: hidden; }" +
".header { background: linear-gradient(135deg, #C62828 0%, #E53935 100%); padding: 30px; text-align: center; }" +
".header h1 { color: #ffffff; margin: 0; font-size: 24px; }" +
".content { padding: 40px 30px; }" +
".content p { color: #333333; line-height: 1.8; margin: 10px 0; }" +
".code-box { background-color: #f8f9fa; border-left: 4px solid #C62828; padding: 20px; margin: 20px 0; text-align: center; }" +
".code { font-size: 32px; font-weight: bold; color: #C62828; letter-spacing: 5px; font-family: 'Courier New', monospace; }" +
".tips { color: #666666; font-size: 14px; margin-top: 20px; line-height: 1.6; }" +
".footer { background-color: #f8f9fa; padding: 20px; text-align: center; color: #999999; font-size: 12px; }" +
"</style>" +
"</head>" +
"<body>" +
"<div class=\"container\">" +
"<div class=\"header\">" +
"<h1>红色思政学习平台</h1>" +
"</div>" +
"<div class=\"content\">" +
"<p>尊敬的用户,您好!</p>" +
"<p>您正在进行邮箱验证,您的验证码为:</p>" +
"<div class=\"code-box\">" +
"<div class=\"code\">" + code + "</div>" +
"</div>" +
"<div class=\"tips\">" +
"<p>• 验证码有效期为10分钟请尽快完成验证</p>" +
"<p>• 如果这不是您的操作,请忽略此邮件</p>" +
"<p>• 为了保护您的账号安全,请勿将验证码告知他人</p>" +
"</div>" +
"</div>" +
"<div class=\"footer\">" +
"<p>此邮件由系统自动发送,请勿回复</p>" +
"<p>Copyright © 红色思政智能体平台</p>" +
"</div>" +
"</div>" +
"</body>" +
"</html>";
}
/**
* 生成6位数字验证码
* @return 验证码
*/
public static String generateVerificationCode() {
return String.valueOf((int)((Math.random() * 9 + 1) * 100000));
}
}

View File

@@ -0,0 +1,259 @@
package org.xyzh.common.utils;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @description 短信发送工具类 - 支持多种短信服务商
* @filename SmsUtils.java
* @author yslg
* @copyright xyzh
* @since 2025-11-03
*/
@Component
public class SmsUtils {
private static final Logger logger = LoggerFactory.getLogger(SmsUtils.class);
@Value("${sms.enabled:false}")
private boolean enabled;
@Value("${sms.provider:aliyun}")
private String provider;
@Value("${sms.access-key-id:}")
private String accessKeyId;
@Value("${sms.access-key-secret:}")
private String accessKeySecret;
@Value("${sms.sign-name:红色思政学习平台}")
private String signName;
@Value("${sms.template-code:}")
private String templateCode;
@Value("${sms.region-id:cn-hangzhou}")
private String regionId;
@Value("${sms.endpoint:dysmsapi.aliyuncs.com}")
private String endpoint;
/**
* 发送短信验证码
* @param phone 手机号
* @param code 验证码
* @return 是否发送成功
*/
public boolean sendVerificationCode(String phone, String code) {
// 如果未启用短信服务,使用模拟模式
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
logger.warn("短信服务未配置或未启用,使用模拟模式");
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
return true;
}
// 根据配置的服务商选择发送方式
switch (provider.toLowerCase()) {
case "aliyun":
return sendByAliyun(phone, code);
case "tencent":
logger.warn("腾讯云短信服务暂未实现,使用模拟模式");
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
return true;
default:
logger.error("未知的短信服务商: {}", provider);
return false;
}
}
/**
* 使用阿里云发送短信验证码
* @param phone 手机号
* @param code 验证码
* @return 是否发送成功
*/
private boolean sendByAliyun(String phone, String code) {
try {
Client client = createAliyunClient();
SendSmsRequest request = new SendSmsRequest()
.setPhoneNumbers(phone)
.setSignName(signName)
.setTemplateCode(templateCode)
.setTemplateParam("{\"code\":\"" + code + "\"}");
SendSmsResponse response = client.sendSms(request);
if ("OK".equals(response.getBody().getCode())) {
logger.info("阿里云短信发送成功,手机号: {}, BizId: {}", phone, response.getBody().getBizId());
return true;
} else {
logger.error("阿里云短信发送失败,手机号: {}, Code: {}, Message: {}",
phone, response.getBody().getCode(), response.getBody().getMessage());
return false;
}
} catch (Exception e) {
logger.error("阿里云短信发送异常,手机号: {}, 错误: {}", phone, e.getMessage(), e);
return false;
}
}
/**
* 创建阿里云短信客户端
* @return 短信客户端
*/
private Client createAliyunClient() throws Exception {
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret)
.setEndpoint(endpoint);
return new Client(config);
}
/**
* 发送通用短信(支持自定义模板)
* @param phone 手机号
* @param templateCode 模板CODE
* @param templateParam 模板参数JSON格式
* @return 是否发送成功
*/
public boolean sendSms(String phone, String templateCode, String templateParam) {
// 如果未启用短信服务,使用模拟模式
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
logger.warn("短信服务未配置或未启用,使用模拟模式");
logger.info("【模拟发送】短信,手机号: {}, 模板: {}, 参数: {}", phone, templateCode, templateParam);
return true;
}
// 根据配置的服务商选择发送方式
switch (provider.toLowerCase()) {
case "aliyun":
return sendSmsAliyun(phone, templateCode, templateParam);
case "tencent":
logger.warn("腾讯云短信服务暂未实现,使用模拟模式");
logger.info("【模拟发送】短信,手机号: {}, 模板: {}, 参数: {}", phone, templateCode, templateParam);
return true;
default:
logger.error("未知的短信服务商: {}", provider);
return false;
}
}
/**
* 使用阿里云发送通用短信
*/
private boolean sendSmsAliyun(String phone, String templateCode, String templateParam) {
try {
Client client = createAliyunClient();
SendSmsRequest request = new SendSmsRequest()
.setPhoneNumbers(phone)
.setSignName(signName)
.setTemplateCode(templateCode)
.setTemplateParam(templateParam);
SendSmsResponse response = client.sendSms(request);
if ("OK".equals(response.getBody().getCode())) {
logger.info("阿里云短信发送成功,手机号: {}, BizId: {}", phone, response.getBody().getBizId());
return true;
} else {
logger.error("阿里云短信发送失败,手机号: {}, Code: {}, Message: {}",
phone, response.getBody().getCode(), response.getBody().getMessage());
return false;
}
} catch (Exception e) {
logger.error("阿里云短信发送异常,手机号: {}, 错误: {}", phone, e.getMessage(), e);
return false;
}
}
/**
* 批量发送短信
* @param phones 手机号列表,用逗号分隔
* @param templateCode 模板CODE
* @param templateParam 模板参数JSON格式
* @return 是否发送成功
*/
public boolean sendBatchSms(String phones, String templateCode, String templateParam) {
// 如果未启用短信服务,使用模拟模式
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
logger.warn("短信服务未配置或未启用,使用模拟模式");
logger.info("【模拟发送】批量短信,手机号: {}, 模板: {}, 参数: {}", phones, templateCode, templateParam);
return true;
}
// 根据配置的服务商选择发送方式
switch (provider.toLowerCase()) {
case "aliyun":
return sendBatchSmsAliyun(phones, templateCode, templateParam);
case "tencent":
logger.warn("腾讯云短信服务暂未实现,使用模拟模式");
logger.info("【模拟发送】批量短信,手机号: {}, 模板: {}, 参数: {}", phones, templateCode, templateParam);
return true;
default:
logger.error("未知的短信服务商: {}", provider);
return false;
}
}
/**
* 使用阿里云批量发送短信
*/
private boolean sendBatchSmsAliyun(String phones, String templateCode, String templateParam) {
try {
Client client = createAliyunClient();
SendSmsRequest request = new SendSmsRequest()
.setPhoneNumbers(phones)
.setSignName(signName)
.setTemplateCode(templateCode)
.setTemplateParam(templateParam);
SendSmsResponse response = client.sendSms(request);
if ("OK".equals(response.getBody().getCode())) {
logger.info("阿里云批量短信发送成功,手机号: {}, BizId: {}", phones, response.getBody().getBizId());
return true;
} else {
logger.error("阿里云批量短信发送失败,手机号: {}, Code: {}, Message: {}",
phones, response.getBody().getCode(), response.getBody().getMessage());
return false;
}
} catch (Exception e) {
logger.error("阿里云批量短信发送异常,手机号: {}, 错误: {}", phones, e.getMessage(), e);
return false;
}
}
/**
* 生成6位数字验证码
* @return 验证码
*/
public static String generateVerificationCode() {
return String.valueOf((int)((Math.random() * 9 + 1) * 100000));
}
/**
* 验证手机号格式
* @param phone 手机号
* @return 是否有效
*/
public static boolean isValidPhone(String phone) {
if (phone == null || phone.trim().isEmpty()) {
return false;
}
// 中国大陆手机号验证
String regex = "^1[3-9]\\d{9}$";
return phone.matches(regex);
}
}