服务启动
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
package org.xyzh.message.config;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.xyzh.api.system.service.SysConfigService;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @description 动态配置加载器 - 从数据库加载邮件和短信配置
|
||||
* @filename DynamicConfigLoader.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
@Configuration
|
||||
@Order(100) // 确保在其他组件初始化之后再加载配置
|
||||
public class DynamicConfigLoader implements ApplicationRunner {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DynamicConfigLoader.class);
|
||||
|
||||
@DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false)
|
||||
private SysConfigService sysConfigService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private JavaMailSenderImpl mailSender;
|
||||
|
||||
@Autowired(required = false)
|
||||
private EmailConfigProperties emailConfigProperties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private SmsConfigProperties smsConfigProperties;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
logger.info("=== 开始加载动态配置 ===");
|
||||
|
||||
try {
|
||||
// 加载邮件配置
|
||||
if (emailConfigProperties != null) {
|
||||
loadEmailConfig();
|
||||
} else {
|
||||
logger.warn("EmailConfigProperties未注入,跳过邮件配置加载");
|
||||
}
|
||||
|
||||
// 加载短信配置
|
||||
if (smsConfigProperties != null) {
|
||||
loadSmsConfig();
|
||||
} else {
|
||||
logger.warn("SmsConfigProperties未注入,跳过短信配置加载");
|
||||
}
|
||||
|
||||
logger.info("=== 动态配置加载完成 ===");
|
||||
} catch (Exception e) {
|
||||
logger.error("动态配置加载失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载邮件配置
|
||||
*/
|
||||
private void loadEmailConfig() {
|
||||
try {
|
||||
if (sysConfigService == null) {
|
||||
logger.warn("SysConfigService未注入,无法加载邮件配置");
|
||||
return;
|
||||
}
|
||||
|
||||
String host = sysConfigService.getStringConfig("email.host");
|
||||
String port = sysConfigService.getStringConfig("email.port");
|
||||
String username = sysConfigService.getStringConfig("email.username");
|
||||
String password = sysConfigService.getStringConfig("email.password");
|
||||
String fromName = sysConfigService.getStringConfig("email.fromName");
|
||||
String sslEnable = sysConfigService.getStringConfig("email.ssl.enable");
|
||||
String timeout = sysConfigService.getStringConfig("email.timeout");
|
||||
|
||||
// 更新配置属性
|
||||
emailConfigProperties.setHost(host);
|
||||
emailConfigProperties.setPort(StringUtils.hasText(port) ? Integer.valueOf(port) : 587);
|
||||
emailConfigProperties.setUsername(username);
|
||||
emailConfigProperties.setPassword(password);
|
||||
emailConfigProperties.setFromName(fromName);
|
||||
emailConfigProperties.setSslEnable("true".equalsIgnoreCase(sslEnable));
|
||||
emailConfigProperties.setTimeout(StringUtils.hasText(timeout) ? Integer.valueOf(timeout) : 30000);
|
||||
|
||||
// 如果邮箱配置完整,则配置JavaMailSender
|
||||
if (mailSender != null && StringUtils.hasText(host) && StringUtils.hasText(username) && StringUtils.hasText(password)) {
|
||||
mailSender.setHost(host);
|
||||
mailSender.setPort(emailConfigProperties.getPort());
|
||||
mailSender.setUsername(username);
|
||||
mailSender.setPassword(password);
|
||||
|
||||
// 设置邮件属性
|
||||
Properties props = mailSender.getJavaMailProperties();
|
||||
props.put("mail.smtp.auth", "true");
|
||||
props.put("mail.smtp.starttls.enable", emailConfigProperties.getSslEnable() ? "true" : "false");
|
||||
props.put("mail.smtp.starttls.required", emailConfigProperties.getSslEnable() ? "true" : "false");
|
||||
props.put("mail.smtp.timeout", emailConfigProperties.getTimeout());
|
||||
props.put("mail.smtp.connectiontimeout", emailConfigProperties.getTimeout());
|
||||
|
||||
logger.info("邮件配置加载成功: host={}, port={}, username={}", host, emailConfigProperties.getPort(), username);
|
||||
} else {
|
||||
logger.warn("邮件配置不完整,将使用默认配置或模拟模式");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("加载邮件配置失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载短信配置
|
||||
*/
|
||||
private void loadSmsConfig() {
|
||||
try {
|
||||
if (sysConfigService == null) {
|
||||
logger.warn("SysConfigService未注入,无法加载短信配置");
|
||||
return;
|
||||
}
|
||||
|
||||
String provider = sysConfigService.getStringConfig("sms.provider");
|
||||
String accessKeyId = sysConfigService.getStringConfig("sms.accessKeyId");
|
||||
String accessKeySecret = sysConfigService.getStringConfig("sms.accessKeySecret");
|
||||
String signName = sysConfigService.getStringConfig("sms.signName");
|
||||
String templateCodeLogin = sysConfigService.getStringConfig("sms.templateCode.login");
|
||||
String templateCodeRegister = sysConfigService.getStringConfig("sms.templateCode.register");
|
||||
String timeout = sysConfigService.getStringConfig("sms.timeout");
|
||||
|
||||
// 更新配置属性
|
||||
smsConfigProperties.setProvider(StringUtils.hasText(provider) ? provider : "aliyun");
|
||||
smsConfigProperties.setAccessKeyId(accessKeyId);
|
||||
smsConfigProperties.setAccessKeySecret(accessKeySecret);
|
||||
smsConfigProperties.setSignName(StringUtils.hasText(signName) ? signName : "校园新闻");
|
||||
smsConfigProperties.setTemplateCodeLogin(templateCodeLogin);
|
||||
smsConfigProperties.setTemplateCodeRegister(templateCodeRegister);
|
||||
smsConfigProperties.setTimeout(StringUtils.hasText(timeout) ? Integer.valueOf(timeout) : 30000);
|
||||
|
||||
if (StringUtils.hasText(accessKeyId) && StringUtils.hasText(accessKeySecret)) {
|
||||
logger.info("短信配置加载成功: provider={}, signName={}", smsConfigProperties.getProvider(), smsConfigProperties.getSignName());
|
||||
} else {
|
||||
logger.warn("短信配置不完整,将使用模拟模式");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("加载短信配置失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.xyzh.message.config;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @description 邮件配置属性
|
||||
* @filename EmailConfigProperties.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-26
|
||||
*/
|
||||
@Component
|
||||
public class EmailConfigProperties {
|
||||
|
||||
/** SMTP服务器地址 */
|
||||
private String host;
|
||||
|
||||
/** SMTP端口 */
|
||||
private Integer port;
|
||||
|
||||
/** 发件人邮箱 */
|
||||
private String username;
|
||||
|
||||
/** 邮箱授权码 */
|
||||
private String password;
|
||||
|
||||
/** 发件人名称 */
|
||||
private String fromName;
|
||||
|
||||
/** 是否启用SSL */
|
||||
private Boolean sslEnable;
|
||||
|
||||
/** 连接超时时间(毫秒) */
|
||||
private Integer timeout;
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFromName() {
|
||||
return fromName;
|
||||
}
|
||||
|
||||
public void setFromName(String fromName) {
|
||||
this.fromName = fromName;
|
||||
}
|
||||
|
||||
public Boolean getSslEnable() {
|
||||
return sslEnable;
|
||||
}
|
||||
|
||||
public void setSslEnable(Boolean sslEnable) {
|
||||
this.sslEnable = sslEnable;
|
||||
}
|
||||
|
||||
public Integer getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(Integer timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.xyzh.message.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @description 邮件发送器配置 - 手动创建JavaMailSender bean
|
||||
* @filename MailSenderConfig.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-05
|
||||
*/
|
||||
@Configuration
|
||||
public class MailSenderConfig {
|
||||
|
||||
/**
|
||||
* 创建JavaMailSender bean
|
||||
* 初始值为默认配置,实际配置将在DynamicConfigLoader中从数据库加载
|
||||
*/
|
||||
@Bean
|
||||
public JavaMailSender javaMailSender() {
|
||||
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
|
||||
|
||||
// 设置默认配置(防止未配置时报错)
|
||||
mailSender.setHost("smtp.example.com");
|
||||
mailSender.setPort(587);
|
||||
mailSender.setUsername("default");
|
||||
mailSender.setPassword("default");
|
||||
|
||||
// 设置邮件属性
|
||||
Properties props = mailSender.getJavaMailProperties();
|
||||
props.put("mail.smtp.auth", "true");
|
||||
props.put("mail.smtp.starttls.enable", "true");
|
||||
props.put("mail.smtp.starttls.required", "false");
|
||||
props.put("mail.smtp.timeout", "30000");
|
||||
props.put("mail.smtp.connectiontimeout", "30000");
|
||||
|
||||
return mailSender;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.xyzh.message.config;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @description 短信配置属性
|
||||
* @filename SmsConfigProperties.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-26
|
||||
*/
|
||||
@Component
|
||||
public class SmsConfigProperties {
|
||||
|
||||
/** 短信服务商 */
|
||||
private String provider;
|
||||
|
||||
/** AccessKey ID */
|
||||
private String accessKeyId;
|
||||
|
||||
/** AccessKey Secret */
|
||||
private String accessKeySecret;
|
||||
|
||||
/** 短信签名 */
|
||||
private String signName;
|
||||
|
||||
/** 登录验证码模板 */
|
||||
private String templateCodeLogin;
|
||||
|
||||
/** 注册验证码模板 */
|
||||
private String templateCodeRegister;
|
||||
|
||||
/** 请求超时时间(毫秒) */
|
||||
private Integer timeout;
|
||||
|
||||
public String getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public void setProvider(String provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public String getAccessKeyId() {
|
||||
return accessKeyId;
|
||||
}
|
||||
|
||||
public void setAccessKeyId(String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
}
|
||||
|
||||
public String getAccessKeySecret() {
|
||||
return accessKeySecret;
|
||||
}
|
||||
|
||||
public void setAccessKeySecret(String accessKeySecret) {
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
}
|
||||
|
||||
public String getSignName() {
|
||||
return signName;
|
||||
}
|
||||
|
||||
public void setSignName(String signName) {
|
||||
this.signName = signName;
|
||||
}
|
||||
|
||||
public String getTemplateCodeLogin() {
|
||||
return templateCodeLogin;
|
||||
}
|
||||
|
||||
public void setTemplateCodeLogin(String templateCodeLogin) {
|
||||
this.templateCodeLogin = templateCodeLogin;
|
||||
}
|
||||
|
||||
public String getTemplateCodeRegister() {
|
||||
return templateCodeRegister;
|
||||
}
|
||||
|
||||
public void setTemplateCodeRegister(String templateCodeRegister) {
|
||||
this.templateCodeRegister = templateCodeRegister;
|
||||
}
|
||||
|
||||
public Integer getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(Integer timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package org.xyzh.message.service;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.xyzh.api.message.dto.TbMessageDTO;
|
||||
import org.xyzh.api.message.service.MessageService;
|
||||
import org.xyzh.api.message.vo.MessageVO;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.page.PageParam;
|
||||
import org.xyzh.message.config.DynamicConfigLoader;
|
||||
import org.xyzh.message.utils.EmailUtils;
|
||||
import org.xyzh.message.utils.SmsUtils;
|
||||
|
||||
@DubboService(
|
||||
version = "1.0.0",
|
||||
group = "message",
|
||||
timeout = 3000,
|
||||
retries = 0
|
||||
)
|
||||
public class MessageServiceImpl implements MessageService{
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private EmailUtils emailUtils;
|
||||
|
||||
@Autowired
|
||||
private SmsUtils smsUtils;
|
||||
|
||||
@Override
|
||||
public ResultDomain<String> sendSimpleEmail(String to, String subject, String content) {
|
||||
boolean flag = emailUtils.sendSimpleEmail(to, subject, content);
|
||||
if (flag){
|
||||
return ResultDomain.success("发生成功");
|
||||
}else{
|
||||
return ResultDomain.failure("发送失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<String> sendHtmlEmail(String to, String subject, String content) {
|
||||
boolean flag = emailUtils.sendHtmlEmail(to, subject, content);
|
||||
if (flag){
|
||||
return ResultDomain.success("发生成功");
|
||||
}else{
|
||||
return ResultDomain.failure("发送失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<String> sendEmailVerificationCode(String to, String code) {
|
||||
boolean flag = emailUtils.sendVerificationCode(to, code);
|
||||
if (flag){
|
||||
return ResultDomain.success("发生成功");
|
||||
}else{
|
||||
return ResultDomain.failure("发送失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<String> sendPhoneVerificationCode(String phone, String code) {
|
||||
boolean flag = smsUtils.sendVerificationCode(phone, code);
|
||||
if (flag){
|
||||
return ResultDomain.success("发生成功");
|
||||
}else{
|
||||
return ResultDomain.failure("发送失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> createMessage(MessageVO messageVO) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<Boolean> deleteMessage(String messageId) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<MessageVO> getMessageDetail(String messageId) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> getMessageList(TbMessageDTO filter) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> getMessagePage(TbMessageDTO filter, PageParam pageParam) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> getMyMessageDetail(String messageId) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> getMyMessageList(TbMessageDTO filter) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> getMyMessagePage(TbMessageDTO filter, PageParam pageParam) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> handleMessage(String messageId, String status) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<MessageVO> sendMessage(MessageVO messageVO) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> updateMessage(MessageVO messageVO) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbMessageDTO> withdrawMessage(String messageId) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package org.xyzh.message.utils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.message.config.EmailConfigProperties;
|
||||
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
/**
|
||||
* @description 邮件发送工具类
|
||||
* @filename EmailUtils.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-03
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnBean(JavaMailSender.class)
|
||||
public class EmailUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(EmailUtils.class);
|
||||
|
||||
@Autowired
|
||||
private JavaMailSender mailSender;
|
||||
|
||||
@Autowired
|
||||
private EmailConfigProperties emailConfigProperties;
|
||||
|
||||
/**
|
||||
* 发送简单文本邮件
|
||||
* @param to 收件人邮箱
|
||||
* @param subject 邮件主题
|
||||
* @param content 邮件内容
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
public boolean sendSimpleEmail(String to, String subject, String content) {
|
||||
try {
|
||||
SimpleMailMessage message = new SimpleMailMessage();
|
||||
String from = emailConfigProperties.getUsername();
|
||||
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");
|
||||
|
||||
String from = emailConfigProperties.getUsername();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
package org.xyzh.message.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.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.xyzh.message.config.SmsConfigProperties;
|
||||
|
||||
/**
|
||||
* @description 短信发送工具类 - 支持多种短信服务商
|
||||
* @filename SmsUtils.java
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-11-03
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnBean(SmsConfigProperties.class)
|
||||
public class SmsUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SmsUtils.class);
|
||||
|
||||
@Autowired
|
||||
private SmsConfigProperties smsConfigProperties;
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
* @param phone 手机号
|
||||
* @param code 验证码
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
public boolean sendVerificationCode(String phone, String code) {
|
||||
// 如果未启用短信服务,使用模拟模式
|
||||
String accessKeyId = smsConfigProperties.getAccessKeyId();
|
||||
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
|
||||
|
||||
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
||||
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
||||
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据配置的服务商选择发送方式
|
||||
String provider = smsConfigProperties.getProvider();
|
||||
if (provider == null) provider = "aliyun";
|
||||
|
||||
switch (provider.toLowerCase()) {
|
||||
case "aliyun":
|
||||
return sendByAliyun(phone, code, smsConfigProperties.getTemplateCodeLogin());
|
||||
case "tencent":
|
||||
logger.warn("腾讯云短信服务暂未实现,使用模拟模式");
|
||||
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
|
||||
return true;
|
||||
default:
|
||||
logger.error("未知的短信服务商: {}", provider);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用阿里云发送短信验证码
|
||||
* @param phone 手机号
|
||||
* @param code 验证码
|
||||
* @param templateCode 短信模板CODE
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
private boolean sendByAliyun(String phone, String code, String templateCode) {
|
||||
try {
|
||||
Client client = createAliyunClient();
|
||||
|
||||
SendSmsRequest request = new SendSmsRequest()
|
||||
.setPhoneNumbers(phone)
|
||||
.setSignName(smsConfigProperties.getSignName())
|
||||
.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(smsConfigProperties.getAccessKeyId())
|
||||
.setAccessKeySecret(smsConfigProperties.getAccessKeySecret())
|
||||
.setEndpoint("dysmsapi.aliyuncs.com");
|
||||
return new Client(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送通用短信(支持自定义模板)
|
||||
* @param phone 手机号
|
||||
* @param templateCode 模板CODE
|
||||
* @param templateParam 模板参数(JSON格式)
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
public boolean sendSms(String phone, String templateCode, String templateParam) {
|
||||
// 如果未启用短信服务,使用模拟模式
|
||||
String accessKeyId = smsConfigProperties.getAccessKeyId();
|
||||
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
|
||||
|
||||
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
||||
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
||||
logger.info("【模拟发送】短信,手机号: {}, 模板: {}, 参数: {}", phone, templateCode, templateParam);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据配置的服务商选择发送方式
|
||||
String provider = smsConfigProperties.getProvider();
|
||||
if (provider == null) provider = "aliyun";
|
||||
|
||||
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(smsConfigProperties.getSignName())
|
||||
.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) {
|
||||
// 如果未启用短信服务,使用模拟模式
|
||||
String accessKeyId = smsConfigProperties.getAccessKeyId();
|
||||
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
|
||||
|
||||
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
|
||||
logger.warn("短信服务未配置或未启用,使用模拟模式");
|
||||
logger.info("【模拟发送】批量短信,手机号: {}, 模板: {}, 参数: {}", phones, templateCode, templateParam);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据配置的服务商选择发送方式
|
||||
String provider = smsConfigProperties.getProvider();
|
||||
if (provider == null) provider = "aliyun";
|
||||
|
||||
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(smsConfigProperties.getSignName())
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@ urban-lifeline:
|
||||
- /error
|
||||
- /actuator/health
|
||||
- /actuator/info
|
||||
|
||||
|
||||
security:
|
||||
aes:
|
||||
secret-key: 1234567890qwer
|
||||
# ================== Spring ==================
|
||||
spring:
|
||||
application:
|
||||
|
||||
Reference in New Issue
Block a user