mirror of
https://github.com/201206030/novel.git
synced 2025-04-27 07:30:50 +00:00
feat: 模版方法模式实现消息发送器
This commit is contained in:
parent
46d03883ed
commit
c866702e48
6
pom.xml
6
pom.xml
@ -185,6 +185,12 @@
|
||||
<version>${springdoc-openapi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 邮件 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
|
@ -0,0 +1,14 @@
|
||||
package io.github.xxyopen.novel.core.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* mail 配置属性
|
||||
*
|
||||
* @author xiongxiaoyang
|
||||
* @date 2023/3/25
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.mail")
|
||||
public record MailProperties(String nickname, String username) {
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.github.xxyopen.novel.core.constant;
|
||||
|
||||
/**
|
||||
* 消息发送器的类型
|
||||
*
|
||||
* @author xiongxiaoyang
|
||||
* @date 2023/3/24
|
||||
*/
|
||||
public class MessageSenderTypeConsts {
|
||||
|
||||
private MessageSenderTypeConsts() {
|
||||
throw new IllegalStateException("Constant class");
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册成功的邮件发送器
|
||||
*/
|
||||
public static final String REGISTER_MAIL_SENDER = "registerMailSender";
|
||||
|
||||
/**
|
||||
* 秒杀活动的系统通知发送器
|
||||
*/
|
||||
public static final String SECKILL_SYS_NOTICE_SENDER = "seckillSysNoticeSender";
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package io.github.xxyopen.novel.manager.message;
|
||||
|
||||
import io.github.xxyopen.novel.core.config.MailProperties;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
|
||||
/**
|
||||
* 抽象的邮件消息发送者
|
||||
*
|
||||
* @author xiongxiaoyang
|
||||
* @date 2023/3/24
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AbstractMailSender extends AbstractMessageSender {
|
||||
|
||||
private final MailProperties mailProperties;
|
||||
|
||||
private final JavaMailSender mailSender;
|
||||
|
||||
@Override
|
||||
protected void sendMessage(Long toUserId, String messageTitle, String messageContent) {
|
||||
// TODO 根据消息接收方的用户ID查询出消息接收方的邮件地址
|
||||
String toEmail = "xxyopen@foxmail.com";
|
||||
// 开始发送邮件
|
||||
log.info("发送 HTML 邮件开始:{},{},{}", toEmail, messageTitle, messageContent);
|
||||
// 使用 MimeMessage,MIME 协议
|
||||
MimeMessage message = mailSender.createMimeMessage();
|
||||
MimeMessageHelper helper;
|
||||
// MimeMessageHelper 帮助我们设置更丰富的内容
|
||||
try {
|
||||
helper = new MimeMessageHelper(message, true);
|
||||
helper.setFrom(new InternetAddress(mailProperties.username(), mailProperties.nickname(), "UTF-8"));
|
||||
helper.setTo(toEmail);
|
||||
helper.setSubject(messageTitle);
|
||||
// 第二个参数 true 代表支持 html
|
||||
helper.setText(messageContent, true);
|
||||
mailSender.send(message);
|
||||
log.info("发送 HTML 邮件 to {} 成功", toEmail);
|
||||
} catch (Exception e) {
|
||||
// 邮件发送失败不会重试
|
||||
log.error("发送 HTML 邮件 to {} 失败", toEmail, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package io.github.xxyopen.novel.manager.message;
|
||||
|
||||
/**
|
||||
* 抽象的消息发送器
|
||||
* <p>
|
||||
* 遵循松耦合的设计原则,所有的属性都使用构造函数注入,与 Spring 框架解藕
|
||||
* <p>
|
||||
* 所有的消息发送器既可以注册到 Spring 容器中,作为 Spring 的一个组件使用,也可以直接通过 new 对象的方式使用
|
||||
* <p>
|
||||
* 每种类型的消息发送时机可能都不一样,不同类型和发送时机的消息格式可能也不一样,所以由各个子类去拓展消息的格式
|
||||
*
|
||||
* @author xiongxiaoyang
|
||||
* @date 2023/3/24
|
||||
*/
|
||||
public abstract class AbstractMessageSender implements MessageSender {
|
||||
|
||||
private static final String PLACEHOLDER = "{}";
|
||||
|
||||
/**
|
||||
* 定义消息发送的模版,子类不能修改此模版
|
||||
*/
|
||||
@Override
|
||||
public final void sendMessage(Long toUserId, Object... args) {
|
||||
// 1.获取消息标题模版
|
||||
String titleTemplate = getTitleTemplate();
|
||||
// 2.获取消息内容模版
|
||||
String contentTemplate = getContentTemplate();
|
||||
// 3.解析消息模版,得到最终需要发送的消息标题
|
||||
String title = resolveTitle(titleTemplate, args);
|
||||
// 4.解析消息内容,得到最终需要发送的消息内容
|
||||
String content = resolveContent(contentTemplate, args);
|
||||
// 5.发送消息
|
||||
sendMessage(toUserId, title, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息,具体发送到哪里由子类决定
|
||||
*
|
||||
* @param toUserId 消息接收方的用户ID
|
||||
* @param messageTitle 消息标题
|
||||
* @param messageContent 消息内容
|
||||
*/
|
||||
protected abstract void sendMessage(Long toUserId, String messageTitle, String messageContent);
|
||||
|
||||
/**
|
||||
* 获取消息标题的模版,具体如何制定模版由子类决定
|
||||
*
|
||||
* @return 消息标题
|
||||
*/
|
||||
protected abstract String getTitleTemplate();
|
||||
|
||||
/**
|
||||
* 获取消息内容的模版,具体如何制定模版由子类决定
|
||||
*
|
||||
* @return 消息内容
|
||||
*/
|
||||
protected abstract String getContentTemplate();
|
||||
|
||||
/**
|
||||
* 通过给定的参数列表解析消息标题模版,默认固定标题,不需要解析,可以由子类来拓展它的功能
|
||||
*
|
||||
* @param titleTemplate 消息标题模版
|
||||
* @param arguments 用来解析的参数列表
|
||||
* @return 解析后的消息标题
|
||||
*/
|
||||
protected String resolveTitle(String titleTemplate, Object... arguments) {
|
||||
return titleTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过给定的参数列表解析消息内容模版,默认实现是使用参数列表来替换消息内容模版中的占位符,可以由子类来拓展它的功能
|
||||
* <p>
|
||||
* 子类可以根据第一个/前几个参数去数据库中查询动态内容,然后重组参数列表
|
||||
*
|
||||
* @param contentTemplate 消息内容模版
|
||||
* @param args 用来解析的参数列表
|
||||
* @return 解析后的消息内容
|
||||
*/
|
||||
protected String resolveContent(String contentTemplate, Object... args) {
|
||||
if (args.length > 0) {
|
||||
StringBuilder formattedContent = new StringBuilder(contentTemplate);
|
||||
for (Object arg : args) {
|
||||
int start = formattedContent.indexOf(PLACEHOLDER);
|
||||
formattedContent.replace(start, start + PLACEHOLDER.length(),
|
||||
String.valueOf(arg));
|
||||
}
|
||||
return formattedContent.toString();
|
||||
}
|
||||
return contentTemplate;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package io.github.xxyopen.novel.manager.message;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 抽象的系统通知发送者
|
||||
*
|
||||
* @author xiongxiaoyang
|
||||
* @date 2023/3/24
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractSysNoticeSender extends AbstractMessageSender {
|
||||
|
||||
@Override
|
||||
protected void sendMessage(Long toUserId, String messageTitle, String messageContent) {
|
||||
// 生成消息的发送时间
|
||||
LocalDateTime messageDateTime = LocalDateTime.now();
|
||||
// TODO 在数据库系统通知表中插入一条记录
|
||||
log.info("系统通知发送成功,{},{},{},{}", toUserId, messageDateTime.format(DateTimeFormatter.ISO_DATE_TIME),
|
||||
messageTitle, messageContent);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package io.github.xxyopen.novel.manager.message;
|
||||
|
||||
/**
|
||||
* 消息发送器接口,用来发送各种消息
|
||||
* <p>
|
||||
* 消息按类型分系统通知、邮件、短信、小程序通知等,按发送时机分注册成功消息、充值成功消息、活动通知消息、账户封禁消息、小说下架消息等
|
||||
*
|
||||
* @author xiongxiaoyang
|
||||
* @date 2023/3/25
|
||||
*/
|
||||
public interface MessageSender {
|
||||
|
||||
/**
|
||||
* 发送消息,支持动态消息标题和动态消息内容
|
||||
*
|
||||
* @param toUserId 消息接收方的用户ID
|
||||
* @param args 用来动态生成消息标题和消息内容的参数列表
|
||||
*/
|
||||
void sendMessage(Long toUserId, Object... args);
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package io.github.xxyopen.novel.manager.message;
|
||||
|
||||
import io.github.xxyopen.novel.core.config.MailProperties;
|
||||
import io.github.xxyopen.novel.core.constant.MessageSenderTypeConsts;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 注册成功的邮件发送器
|
||||
*
|
||||
* @author xiongxiaoyang
|
||||
* @date 2023/3/24
|
||||
*/
|
||||
@Component(value = MessageSenderTypeConsts.REGISTER_MAIL_SENDER)
|
||||
@EnableConfigurationProperties(MailProperties.class)
|
||||
public class RegisterMailSender extends AbstractMailSender {
|
||||
|
||||
public RegisterMailSender(MailProperties mailProperties, JavaMailSender mailSender) {
|
||||
super(mailProperties, mailSender);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitleTemplate() {
|
||||
return "欢迎来到小说精品屋";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getContentTemplate() {
|
||||
return """
|
||||
<div>
|
||||
感谢你注册小说精品屋!你的账户现在处于活动状态。
|
||||
</div>
|
||||
<ul>
|
||||
<li> 你的账户电子邮件:{}
|
||||
<li> 你的账户用户名:{}
|
||||
</ul>
|
||||
<div style="padding: 10px 0 50px 0; text-align: center;">
|
||||
<a style="background: #0274be; color: #fff; padding: 12px 30px; text-decoration: none; border-radius: 3px; letter-spacing: 0.3px;" href="{}" target="_blank" rel="noopener">
|
||||
登录我们的网站
|
||||
</a>
|
||||
</div>
|
||||
|
||||
如果你有任何问题,请通过 {} 与我们联系。
|
||||
""";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String resolveContent(String content, Object... args) {
|
||||
// TODO 去数据库/配置文件中查询网站配置
|
||||
String websiteLink = "https://www.xxyopen.com";
|
||||
String websiteEmail = "xxyopen@foxmail.com";
|
||||
return super.resolveContent(content,
|
||||
Stream.of(args, new Object[]{websiteLink, websiteEmail}).flatMap(Arrays::stream).toArray());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.github.xxyopen.novel.manager.message;
|
||||
|
||||
import io.github.xxyopen.novel.core.constant.MessageSenderTypeConsts;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 秒杀活动的系统通知发送器
|
||||
*
|
||||
* @author xiongxiaoyang
|
||||
* @date 2023/3/24
|
||||
*/
|
||||
@Component(value = MessageSenderTypeConsts.SECKILL_SYS_NOTICE_SENDER)
|
||||
public class SeckillSystemNoticeSender extends AbstractSysNoticeSender {
|
||||
|
||||
@Override
|
||||
protected String getTitleTemplate() {
|
||||
return "秒杀即将开始";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getContentTemplate() {
|
||||
return "{}秒杀,{}即将开始,不要错过哦!点击 {} 前往。";
|
||||
}
|
||||
|
||||
}
|
@ -189,6 +189,32 @@ springdoc:
|
||||
api-docs:
|
||||
enabled: false
|
||||
|
||||
--- #----------------------邮箱配置-----------------------------
|
||||
#邮箱服务器
|
||||
spring:
|
||||
mail:
|
||||
host: smtp.163.com
|
||||
#发件人昵称
|
||||
nickname: xxyopen
|
||||
#邮箱账户
|
||||
username: xxx@163.com
|
||||
#邮箱第三方授权码
|
||||
password: xxx
|
||||
#编码类型
|
||||
default-encoding: UTF-8
|
||||
port: 465
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
starttls:
|
||||
enable: true
|
||||
required: rue
|
||||
socketFactory:
|
||||
port: 465
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
fallback: false
|
||||
|
||||
--- #---------------------自定义配置----------------------------
|
||||
novel:
|
||||
# 跨域配置
|
||||
|
Loading…
x
Reference in New Issue
Block a user