当前位置: 首页 > news >正文

唐山seo快速排名贵州萝岗seo整站优化

唐山seo快速排名,贵州萝岗seo整站优化,重庆网站制作济南,全影网的网站哪儿做d前言 发送邮件功能,借鉴 刚果商城,根据文档及项目代码实现。整理总结便有了此文,文章有不对的点,请联系博主指出,请多多点赞收藏,您的支持是我最大的动力~ 发送邮件功能主要借助 mail、freemarker以及rocke…

前言

发送邮件功能,借鉴 刚果商城,根据文档及项目代码实现。整理总结便有了此文,文章有不对的点,请联系博主指出,请多多点赞收藏,您的支持是我最大的动力~

发送邮件功能主要借助 mail、freemarker以及rocketmq实现。

刚果商城是个分布式项目,近看发送消息模块即可。

image-20231205211959763

标准的DDD分层架构。

RocketMQ部署

方便起见,使用docker部署环境

RocketMQ 4.5.1 安装部署

安装 NameServer

docker run -d -p 9876:9876 --name rmqnamesrv foxiswho/rocketmq:server-4.5.1

安装 Brocker

1)新建配置目录

mkdir -p /mydata/rocketmq/conf

2)新建配置文件 broker.conf

brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
# 此处为本地ip, 如果部署服务器, 需要填写服务器外网ip
brokerIP1 = xx.xx.xx.xx

3)创建容器

docker run -d \
-p 10911:10911 \
-p 10909:10909 \
--name rmqbroker \
--link rmqnamesrv:namesrv \
-v /mydata/rocketmq/conf/broker.conf:/etc/rocketmq/broker.conf \
-e "NAMESRV_ADDR=namesrv:9876" \
-e "JAVA_OPTS=-Duser.home=/opt" \
-e "JAVA_OPT_EXT=-server -Xms512m -Xmx512m" \
foxiswho/rocketmq:broker-4.5.1

安装 rocketmq 控制台

docker pull pangliang/rocketmq-console-ng
docker run -d \
--link rmqnamesrv:namesrv \
-e "JAVA_OPTS=-Drocketmq.config.namesrvAddr=namesrv:9876 -Drocketmq.config.isVIPChannel=false" \
--name rmqconsole \
-p 8088:8080 \
-t pangliang/rocketmq-console-ng

运行成功,稍等几秒启动时间,浏览器输入 ip:8088 查看。

记得放行上述所有端口,最终结果如下:

image-20231205220952290

RocketMQ安装成功~

引入主要依赖

        <!-- 发送邮件主要依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><!-- 模板引擎 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency><!-- 消息队列 实现解耦 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-stream-rocketmq</artifactId></dependency>

配置文件

image-20231205212430700

主要看application.yaml 和 application-dev.yaml

application.yaml

server:port: 8001spring:profiles:active: devapplication:name: message-servicestream:bindings:# 主要是如下两个通道的配置 (消费者通道)mailSend:consumer:concurrency: 4max-attempts: 1content-type: application/jsondestination: message-center_topicgroup: message-center_mail-send_cg# 生产者通道   messageOutput:content-type: application/jsondestination: message-center_topicgroup: message-center_general-send_pgrocketmq:bindings:mailSend:consumer:delay-level-when-next-consume: -1tags: common_message-center_mail-send_tag# ...        

application-dev.yaml

spring:cloud:nacos:discovery:server-addr: 127.0.0.1:8848stream:rocketmq:binder:name-server: 127.0.0.1:9876 # rocketmq服务mail:default-encoding: UTF-8host: smtp.163.compassword: xxxport: 25protocol: smtpusername: xxx@163.com

重要的是mail中参数(用的是网易邮箱),username:网易邮箱账号,password:登录 SMTP server 的密码

登录 SMTP server 密码

password获取步骤如下

一、登录网页版邮箱(https://email.163.com/),进入邮箱首页。

二、点击上方设置,选择POP/SMTP/IMAP选项。

image-20231205213826857

三、在客户端协议界面,选择开启对应的协议,IMAP或者POP3分别为不同的收信协议,选择只开启需要的收信协议,比如IMAP,推荐使用IMAP协议来收发邮件,它可以和网页版完全同步。

image-20231205214131071

四、点击开启,继续开启,手机扫码发送短信后,得到的一串密码即为登录密码

image-20231205214306587

image-20231205214336717

image-20231205214600255

真正代码实现

interfaces层

image-20231205214957319

用户接口层,入参为CQRS风格,参数都在application层

发送邮件入参:

@Data
@ApiModel("邮箱发送")
public class MailSendCommand {@ApiModelProperty(value = "标题", example = "刚果商城邮箱验证码提醒")@NotBlank(message = "邮箱标题不能为空")private String title;@Email@ApiModelProperty(value = "发送者", example = "congomall@163.com")@NotBlank(message = "邮箱发送者不能为空")private String sender;@Email@ApiModelProperty(value = "接收者", example = "7798432@163.com", notes = "实际发送时更改为自己邮箱")@NotBlank(message = "邮箱接收者不能为空")private String receiver;@Email@ApiModelProperty("抄送者")private String cc;@ApiModelProperty(value = "消息参数")private List<String> paramList;// 与数据库对应@ApiModelProperty(value = "模板ID", example = "userRegisterVerification")@NotBlank(message = "邮箱模板ID不能为空")private String templateId;
}

application层

image-20231205215452481

直接调用到application层Service实现类方法,该层封装好参数直接调用基础层中消息生产者。

domain层

image-20231205215755887

领域层里面主要是一些常量、实体类,接口以及仓储接口具体实现在基础层。

infrastructure层 ☆

image-20231205220340090

image-20231205221308019

消息通道配置

source -> sink

public interface MessageSource {String OUTPUT = "messageOutput";@Output(MessageSource.OUTPUT)MessageChannel messageOutput();
}
public interface MessageSink {String MAIL_SEND = "mailSend";@Input(MessageSink.MAIL_SEND)SubscribableChannel mailSend();
}

常量与配置文件中通道名称保持一致

消息生产者

@Slf4j
@Component
@AllArgsConstructor
public class MessageSendProduce {// 属性名与配置文件中通道名保持一致private final MessageChannel messageOutput;/*** 邮箱消息发送*/public void mailMessageSend(MailMessageSendEvent mailMessageSendEvent) {String keys = UUID.randomUUID().toString();Message<?> message = MessageBuilder.withPayload(JSON.toJSONString(mailMessageSendEvent)).setHeader(MessageConst.PROPERTY_KEYS, keys).setHeader(MessageConst.PROPERTY_TAGS, MessageRocketMQConstants.MESSAGE_MAIL_SEND_TAG).build();long startTime = SystemClock.now();boolean sendResult = false;try {// 发送消息给mqsendResult = messageOutput.send(message, 2000L);} finally {log.info("邮箱消息发送,发送状态: {}, Keys: {}, 执行时间: {} ms, 消息内容: {}", sendResult, keys, SystemClock.now() - startTime, JSON.toJSONString(mailMessageSendEvent));}}
}

消息消费者

@Slf4j
@Component
@RequiredArgsConstructor
public class MailMessageSendConsume {private final MessageSendFacade messageSendFacade;// 幂等性注解,还没研究@Idempotent(uniqueKeyPrefix = "mail_message_send:",key = "#event.messageSendId+'_'+#event.hashCode()",type = IdempotentTypeEnum.SPEL,scene = IdempotentSceneEnum.MQ,keyTimeout = 600L)@StreamListener(MessageSink.MAIL_SEND)public void mailMessageSend(@Payload MailMessageSendEvent event, @Headers Map headers) {long startTime = System.currentTimeMillis();try {MessageSend messageSend = BeanUtil.toBean(event, MessageSend.class);// 【外观模式】: 抽象消息发送、消息存储以及失败回调业务方等逻辑messageSendFacade.mailMessageSend(messageSend);} finally {log.info("Keys: {}, Msg id: {}, Execute time: {} ms, Message: {}", headers.get("rocketmq_KEYS"), headers.get("rocketmq_MESSAGE_ID"), System.currentTimeMillis() - startTime,JSON.toJSONString(event));}}
}

【外观模式】

直接与外观类交互,外观类封装了做某件事的所有操作,无需与一个个子操作一一交互,降低了复杂性,提高了可维护性

以消息发送为例,将发送邮箱以及消息存储和失败回调业务封装为一个方法降低调用处理复杂度。

image-20231205221631822

发送邮箱核心实现类

@Slf4j
@Component
@AllArgsConstructor
public class MailMessageProduceImpl implements ApplicationListener<ApplicationInitializingEvent>, MailMessageProduce {private final MailTemplateMapper mailTemplateMapper;private final JavaMailSender javaMailSender;private final Configuration configuration;@SneakyThrows@Overridepublic boolean send(MessageSend messageSend) {try {// 根据模板id查询模板 模板id:userRegisterVerificationMailTemplateDO mailTemplateDO = mailTemplateMapper.selectOne(Wrappers.lambdaQuery(MailTemplateDO.class).eq(MailTemplateDO::getTemplateId, messageSend.getTemplateId()));MimeMessage mimeMessage = javaMailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setFrom(messageSend.getSender());helper.setSubject(messageSend.getTitle());if (StrUtil.isNotBlank(messageSend.getCc())) {helper.setCc(messageSend.getCc().split(","));}if (StrUtil.isNotBlank(messageSend.getReceiver())) {helper.setTo(messageSend.getReceiver().split(","));}Map<String, Object> model = Maps.newHashMap();// 模板参数名称与下面freemarker模板中参数一一对应String[] templateParams = mailTemplateDO.getTemplateParam().split(",");if (ArrayUtil.isNotEmpty(templateParams)) {for (int i = 0; i < templateParams.length; i++) {model.put(templateParams[i], messageSend.getParamList().get(i));}}// 模板id就是模板名String templateKey = messageSend.getTemplateId() + ".ftl";// 从单例对象容器获取模板Template template = Singleton.get(templateKey, () -> {try {return configuration.getTemplate(templateKey);} catch (IOException e) {throw new RuntimeException(e);}});String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);helper.setText(html, true);// freemarker填充参数,发送邮箱javaMailSender.send(mimeMessage);} catch (Throwable ex) {log.error("邮件发送失败,Request: {}", JSONUtil.toJsonStr(messageSend), ex);return false;}return true;}/*** 初始化邮箱模板 【率先先将所有模板初始化到单例对象容器中】*/@SneakyThrows@Overridepublic void onApplicationEvent(ApplicationInitializingEvent event) {Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourceUtils.CLASSPATH_URL_PREFIX + "templates/*.ftl");for (Resource resource : resources) {String templateName = resource.getFilename();Singleton.put(templateName, configuration.getTemplate(templateName));}}
}

image-20231205222229490

模板具体内容:

<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<div><p style="caret-color: rgb(86, 90, 92); color: rgb(86, 90, 92); font-family: Helvetica, 微软雅黑, 宋体; text-size-adjust: auto; font-size: 20px;">亲爱的用户:</p><p style="caret-color: rgb(86, 90, 92); color: rgb(86, 90, 92); font-family: Helvetica, 微软雅黑, 宋体; font-size: 18px; text-size-adjust: auto;">您好!感谢您的使用,您本次的验证码为:<span class="Apple-converted-space">&nbsp;</span></p><bstyle="font-family: Helvetica, 微软雅黑, 宋体; text-size-adjust: auto; font-size: 32px; color: rgb(45, 123, 255);">${validCode}</b><p style="caret-color: rgb(86, 90, 92); color: rgb(86, 90, 92); font-family: Helvetica, 微软雅黑, 宋体; text-size-adjust: auto; font-size: 20px;">安全提示:</p><p style="caret-color: rgb(86, 90, 92); color: rgb(86, 90, 92); font-family: Helvetica, 微软雅黑, 宋体; font-size: 18px; text-size-adjust: auto;">为保障您的帐户安全,请在 5 分钟内完成验证,否则验证码将自动失效。<span class="Apple-converted-space">&nbsp;</span></p>
</div>
<div><includetail><!--<![endif]--></includetail>
</div>
</body>
</html>

image-20231205222432490

最终实现效果

测试结果如下:

收件为QQ邮箱:

image-20231205210059105

收件为谷歌邮箱:

image-20231205225618465

经我测试发现,配置的是网易邮箱,发送者就只能是网易邮箱,接收者可以是任意邮箱

http://www.15wanjia.com/news/32146.html

相关文章:

  • 网站开发毕业设计周志汤阴县seo快速排名有哪家好
  • wordpress反向代理 谷歌点击精灵seo
  • 佛山响应式网站设计搜索引擎营销的步骤
  • 潍坊网站建设小程序百度企业网盘
  • 湖北省建设厅网站证件常见的网站推广方法
  • 网站建设玖金手指排名12濮阳市网站建设
  • 前端电商网站设计论文seo引擎优化教程
  • 中国网站制作公司it培训机构培训费用
  • 万江仿做网站网站建设公司哪家好?该如何选择
  • 常见的动态网站开发技术有哪些免费的网站推广在线推广
  • 安徽网站建设有限公司无限制搜索引擎排名
  • 怎么做二手房网站如何建造自己的网站
  • 企业网站建设的原则包括自动友链网
  • 江西建设厅网站证书查询江门网站开发多少钱
  • 网站收录量低怎么做磁力猫torrentkitty官网
  • 网站建设的条件域名被墙检测
  • 怎么上传做 好的网站怎么查询最新网站
  • 北京都有哪些公司名称关键词优化百家号
  • 做网站还要维护吗百度智能小程序怎么优化排名
  • 网站手机模板的特点网络推广seo公司
  • 手机制作网站百度广告投放收费标准
  • 网站如何做301重定向百度手机助手免费下载
  • 网站设计模板源码搜索引擎推广成功的案例
  • 网站标题logo制作百度24小时人工客服
  • 成都定制企业网站制作杭州百度快照优化公司
  • 做静态网站软件搜索引擎优化的基本内容
  • mysql做网站怎么查看数据网络推广平台有哪些公司
  • 网站 网安备案查询什么推广平台比较好
  • 辽宁省建设工程信息网出问题了跨境电商seo是什么意思
  • 织梦网站转移福州seo排名优化