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>
|
<version>${springdoc-openapi.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 邮件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mysql</groupId>
|
<groupId>com.mysql</groupId>
|
||||||
<artifactId>mysql-connector-j</artifactId>
|
<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:
|
api-docs:
|
||||||
enabled: false
|
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:
|
novel:
|
||||||
# 跨域配置
|
# 跨域配置
|
||||||
|
Loading…
x
Reference in New Issue
Block a user