framework-starter-web - Web 功能自动配置模块
framework-starter-notify - 通知功能自动配置模块
1. 模块概述
framework-starter-notify 是 epoch-framework 中的通知功能自动配置模块,提供了邮件发送、短信发送(预留)、通知模板等核心功能。该模块通过自动配置的方式,简化了通知功能的集成和使用,支持多种通知渠道的统一管理和发送。
2. 模块结构
framework-starter-notify/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/epoch/framework/starter/notify/
│ │ │ ├── config/ # 自动配置类
│ │ │ ├── email/ # 邮件发送实现
│ │ │ ├── sms/ # 短信发送实现(预留)
│ │ │ ├── template/ # 通知模板实现
│ │ │ ├── support/ # 支持工具类
│ │ │ └── enums/ # 枚举定义
│ │ └── resources/
│ │ └── META-INF/
│ │ └── spring.factories # Spring Boot 自动配置入口
│ └── test/ # 测试代码
└── pom.xml # Maven 依赖配置
3. 核心功能
3.1 邮件发送自动配置
- 基于 Spring Boot Mail 实现邮件发送
- 支持简单文本邮件、HTML 邮件、附件邮件
- 支持抄送、密送、回复等高级功能
- 提供邮件发送状态和结果回调
3.2 短信发送自动配置(预留)
- 提供短信发送统一接口
- 支持主流短信服务商集成(预留)
- 支持短信模板和参数替换
- 提供短信发送记录和状态查询
3.3 通知模板自动配置
- 支持 Velocity、Freemarker 等模板引擎
- 提供模板管理和参数替换功能
- 支持模板缓存和热加载
- 提供模板预览和测试功能
3.4 通知渠道管理
- 支持多种通知渠道的统一管理
- 提供渠道选择和路由策略
- 支持渠道优先级和降级处理
- 提供渠道状态监控和报警
3.5 通知记录和统计
- 提供通知发送记录存储
- 支持通知状态查询和统计
- 提供通知失败重试机制
- 支持通知发送报表生成
4. 配置说明
4.1 邮件发送配置
spring:
mail:
host: smtp.example.com
port: 587
username: admin@example.com
password: password
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
debug: false
default-encoding: UTF-8
from: admin@example.com
4.2 短信发送配置(预留)
epoch:
framework:
notify:
sms:
enabled: false
provider: aliyun # 支持 aliyun、tencent 等
access-key: your-access-key
access-secret: your-access-secret
sign-name: Epoch
default-template: sms-default-template
region-id: cn-hangzhou
endpoint: dysmsapi.aliyuncs.com
4.3 通知模板配置
epoch:
framework:
notify:
template:
enabled: true
type: velocity # 支持 velocity、freemarker
encoding: UTF-8
cache: true
template-location: classpath:/templates/notify/
4.4 通知渠道配置
epoch:
framework:
notify:
channel:
enabled: true
default-channel: email
retry:
enabled: true
max-attempts: 3
delay: 1000
multiplier: version-v2
fallback:
enabled: true
fallback-channel: sms
4.5 通知记录配置
epoch:
framework:
notify:
record:
enabled: true
storage-type: database # 支持 database、redis
expire-days: 30
max-size: 10000
5. 使用指南
5.1 引入依赖
<dependency>
<groupId>com.epoch</groupId>
<artifactId>framework-starter-notify</artifactId>
<version>${epoch.version}</version>
</dependency>
5.2 邮件发送使用
5.2.1 简单文本邮件
@Autowired
private EmailService emailService;
public void sendSimpleEmail() {
EmailRequest request = EmailRequest.builder()
.to("user@example.com")
.subject("简单文本邮件测试")
.content("这是一封简单的文本邮件测试")
.build();
EmailResult result = emailService.send(request);
if (result.isSuccess()) {
System.out.println("邮件发送成功");
} else {
System.out.println("邮件发送失败: " + result.getErrorMessage());
}
}
5.2.2 HTML 邮件
public void sendHtmlEmail() {
String htmlContent = """
<html>
<body>
<h1>HTML 邮件测试</h1>
<p>这是一封 <strong>HTML 格式</strong> 的邮件测试</p>
<p><a href='https://www.example.com'>点击访问网站</a></p>
</body>
</html>
""";
EmailRequest request = EmailRequest.builder()
.to("user@example.com")
.subject("HTML 邮件测试")
.content(htmlContent)
.contentType(EmailContentType.HTML)
.build();
EmailResult result = emailService.send(request);
// 处理发送结果
}
5.2.3 带附件邮件
public void sendEmailWithAttachment() {
// 创建附件
EmailAttachment attachment1 = EmailAttachment.builder()
.fileName("document.pdf")
.contentType("application/pdf")
.filePath("/path/to/document.pdf")
.build();
EmailAttachment attachment2 = EmailAttachment.builder()
.fileName("image.jpg")
.contentType("image/jpeg")
.fileBytes(Files.readAllBytes(Paths.get("/path/to/image.jpg")))
.build();
EmailRequest request = EmailRequest.builder()
.to("user@example.com")
.subject("带附件邮件测试")
.content("这是一封带附件的邮件测试")
.attachments(Arrays.asList(attachment1, attachment2))
.build();
EmailResult result = emailService.send(request);
// 处理发送结果
}
5.2.4 带抄送和密送邮件
public void sendEmailWithCcAndBcc() {
EmailRequest request = EmailRequest.builder()
.to("user@example.com")
.cc(Arrays.asList("cc1@example.com", "cc2@example.com"))
.bcc(Arrays.asList("bcc1@example.com", "bcc2@example.com"))
.subject("带抄送和密送邮件测试")
.content("这是一封带抄送和密送的邮件测试")
.build();
EmailResult result = emailService.send(request);
// 处理发送结果
}
5.3 短信发送使用(预留)
@Autowired
private SmsService smsService;
public void sendSms() {
SmsRequest request = SmsRequest.builder()
.phoneNumbers(Arrays.asList("13800138000", "13900139000"))
.templateCode("SMS_123456789")
.templateParam(Collections.singletonMap("code", "123456"))
.build();
SmsResult result = smsService.send(request);
if (result.isSuccess()) {
System.out.println("短信发送成功");
} else {
System.out.println("短信发送失败: " + result.getErrorMessage());
}
}
5.4 通知模板使用
5.4.1 创建模板文件
在 src/main/resources/templates/notify/ 目录下创建 welcome.vm 模板文件:
#set($title = "欢迎加入 Epoch 平台")
<html>
<body>
<h1>${title}</h1>
<p>尊敬的 ${username}:</p>
<p>欢迎加入 Epoch 平台!您的账号已成功创建,账号信息如下:</p>
<ul>
<li>用户名:${username}</li>
<li>邮箱:${email}</li>
<li>注册时间:${registerTime}</li>
</ul>
<p>请点击以下链接激活您的账号:</p>
<p><a href='${activationUrl}'>激活账号</a></p>
<p>感谢您的支持!</p>
<p>Epoch 团队</p>
</body>
</html>
5.4.2 使用模板发送邮件
@Autowired
private NotifyService notifyService;
public void sendTemplateEmail() {
// 准备模板参数
Map<String, Object> templateParams = new HashMap<>();
templateParams.put("username", "张三");
templateParams.put("email", "zhangsan@example.com");
templateParams.put("registerTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
templateParams.put("activationUrl", "https://www.example.com/activate?code=123456");
// 创建通知请求
NotifyRequest request = NotifyRequest.builder()
.channel(NotifyChannel.EMAIL)
.recipients(Arrays.asList("zhangsan@example.com"))
.subject("欢迎加入 Epoch 平台")
.template("welcome.vm")
.templateParams(templateParams)
.contentType(NotifyContentType.HTML)
.build();
NotifyResult result = notifyService.send(request);
if (result.isSuccess()) {
System.out.println("通知发送成功");
} else {
System.out.println("通知发送失败: " + result.getErrorMessage());
}
}
5.5 通知记录查询
@Autowired
private NotifyRecordService notifyRecordService;
public void queryNotifyRecords() {
// 查询指定时间段的通知记录
LocalDateTime startTime = LocalDateTime.now().minusDays(7);
LocalDateTime endTime = LocalDateTime.now();
List<NotifyRecord> records = notifyRecordService.queryByTimeRange(startTime, endTime);
// 查询指定状态的通知记录
List<NotifyRecord> successRecords = notifyRecordService.queryByStatus(NotifyStatus.SUCCESS);
List<NotifyRecord> failedRecords = notifyRecordService.queryByStatus(NotifyStatus.FAILED);
// 查询指定渠道的通知记录
List<NotifyRecord> emailRecords = notifyRecordService.queryByChannel(NotifyChannel.EMAIL);
// 统计通知发送情况
NotifyStatistics statistics = notifyRecordService.getStatistics(startTime, endTime);
System.out.println("总发送量: " + statistics.getTotalCount());
System.out.println("成功量: " + statistics.getSuccessCount());
System.out.println("失败量: " + statistics.getFailedCount());
System.out.println("成功率: " + statistics.getSuccessRate() + "%");
}
6. 自定义配置
6.1 自定义邮件发送器
@Configuration
public class CustomEmailConfig {
@Bean
public JavaMailSender customJavaMailSender(MailProperties mailProperties) {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(mailProperties.getHost());
mailSender.setPort(mailProperties.getPort());
mailSender.setUsername(mailProperties.getUsername());
mailSender.setPassword(mailProperties.getPassword());
mailSender.setDefaultEncoding(mailProperties.getDefaultEncoding().name());
// 配置 JavaMail 属性
Properties properties = mailSender.getJavaMailProperties();
properties.putAll(mailProperties.getProperties());
// 添加自定义配置
properties.put("mail.smtp.connectiontimeout", "5000");
properties.put("mail.smtp.timeout", "5000");
properties.put("mail.smtp.writetimeout", "5000");
return mailSender;
}
}
6.2 自定义模板引擎
@Configuration
public class CustomTemplateConfig {
@Bean
public TemplateEngine customTemplateEngine() {
// 使用 Freemarker 模板引擎
FreeMarkerConfigurationFactory factory = new FreeMarkerConfigurationFactory();
factory.setTemplateLoaderPath("classpath:/templates/notify/");
factory.setDefaultEncoding("UTF-8");
FreeMarkerTemplateEngine templateEngine = new FreeMarkerTemplateEngine();
templateEngine.setConfiguration(factory.createConfiguration());
// 添加自定义模板处理器
templateEngine.addTemplateProcessor(new CustomTemplateProcessor());
return templateEngine;
}
}
6.3 自定义通知渠道
@Component
public class WeChatChannel implements NotifyChannelHandler {
@Override
public NotifyChannel getChannel() {
return NotifyChannel.WECHAT;
}
@Override
public NotifyResult send(NotifyRequest request) {
try {
// 实现微信通知发送逻辑
System.out.println("发送微信通知: " + request);
return NotifyResult.success();
} catch (Exception e) {
return NotifyResult.failure(e.getMessage());
}
}
}
6.4 自定义通知记录存储
@Component
public class RedisNotifyRecordRepository implements NotifyRecordRepository {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String KEY_PREFIX = "notify:record:";
private static final long EXPIRE_DAYS = 30;
@Override
public void save(NotifyRecord record) {
String key = KEY_PREFIX + record.getId();
redisTemplate.opsForValue().set(key, record, EXPIRE_DAYS, TimeUnit.DAYS);
}
@Override
public NotifyRecord findById(String id) {
String key = KEY_PREFIX + id;
return (NotifyRecord) redisTemplate.opsForValue().get(key);
}
// 实现其他方法...
}
7. 最佳实践
7.1 邮件发送最佳实践
- 使用模板发送:使用通知模板发送邮件,提高代码可维护性
- 设置合理的超时时间:避免邮件发送阻塞主线程
- 处理发送失败:实现邮件发送失败重试机制
- 避免垃圾邮件:确保邮件内容合法,设置正确的发件人信息
- 监控发送状态:定期检查邮件发送状态,及时发现问题
- 使用异步发送:对于非紧急邮件,使用异步方式发送,提高系统响应速度
7.2 短信发送注意事项(预留)
- 遵守短信发送规定:严格遵守国家短信发送相关法律法规
- 控制发送频率:避免频繁发送短信,影响用户体验
- 使用模板发送:使用短信模板发送,确保内容合规
- 保护用户隐私:妥善保管用户手机号,避免泄露
- 监控发送状态:定期检查短信发送状态,及时处理失败情况
- 设置合理的签名:使用合规的短信签名,提高短信可信度
7.3 通知模板最佳实践
- 使用变量替换:在模板中使用变量替换,提高模板复用性
- 保持模板简洁:模板内容应简洁明了,避免过于复杂
- 使用响应式设计:对于 HTML 模板,使用响应式设计,适配不同设备
- 测试模板渲染:在正式使用前,测试模板渲染效果
- 版本控制模板:对通知模板进行版本控制,方便回滚
- 使用模板缓存:启用模板缓存,提高模板渲染性能
7.4 性能优化建议
- 使用异步发送:对于非紧急通知,使用异步方式发送
- 批量发送:对于大量通知,使用批量发送方式,减少网络请求
- 使用连接池:对于邮件发送,使用连接池,减少连接创建开销
- 启用缓存:启用模板缓存、配置缓存等,提高系统性能
- 合理设置超时:设置合理的发送超时时间,避免阻塞
7.5 安全配置建议
- 保护敏感信息:不要在代码中硬编码邮件密码、短信密钥等敏感信息
- 使用环境变量:将敏感信息配置在环境变量中,提高安全性
- 限制访问权限:限制通知发送接口的访问权限
- 验证请求来源:对通知发送请求进行来源验证,防止滥用
- 加密存储:对通知记录中的敏感信息进行加密存储
8. 常见问题
8.1 邮件发送失败
原因:邮件服务器配置不正确、网络连接失败、邮件内容不符合要求等。
解决方案:
- 检查邮件服务器配置是否正确
- 确保网络连接正常,邮件服务器可访问
- 检查邮件内容是否符合邮件服务器的要求
- 查看邮件发送日志,查找具体错误信息
- 尝试使用 telnet 命令测试邮件服务器连接
8.2 模板渲染错误
原因:模板文件不存在、模板语法错误、模板参数缺失等。
解决方案:
- 检查模板文件是否存在于正确的位置
- 检查模板语法是否正确
- 确保提供了模板所需的所有参数
- 启用模板调试,查看详细的错误信息
- 测试模板渲染效果,确保符合预期
8.3 短信发送失败(预留)
原因:短信服务配置不正确、余额不足、短信内容不符合要求等。
解决方案:
- 检查短信服务配置是否正确
- 确保短信服务余额充足
- 检查短信内容是否符合短信服务商的要求
- 查看短信发送日志,查找具体错误信息
- 联系短信服务商,寻求技术支持
8.4 通知发送超时
原因:网络连接缓慢、邮件服务器响应延迟、短信服务响应延迟等。
解决方案:
- 检查网络连接是否正常
- 增加通知发送超时时间
- 使用异步发送方式,避免阻塞主线程
- 检查邮件服务器或短信服务是否正常运行
- 考虑使用降级策略,当主渠道超时使用备用渠道
8.5 通知记录丢失
原因:通知记录存储配置不正确、存储服务不可用、记录过期等。
解决方案:
- 检查通知记录存储配置是否正确
- 确保存储服务(如数据库、Redis)正常运行
- 调整通知记录的过期时间
- 实现通知记录的备份机制
- 定期检查通知记录存储状态
9. 版本变更
9.1 主要变更
-
3.4.0:
- 优化邮件发送配置,支持更多邮件服务器
- 完善通知模板功能,支持 Velocity 和 Freemarker
- 增加通知记录和统计功能
- 优化通知发送性能
-
3.3.0:
- 新增通知功能自动配置模块
- 实现邮件发送核心功能
- 预留短信发送接口
- 实现通知模板基本功能
-
3.2.0:
- 初始版本,提供邮件发送基本功能
- 支持简单文本邮件和 HTML 邮件
- 提供邮件发送状态查询