写点什么

浅聊一下邮件,短信的通知推送

作者:为自己带盐
  • 2022 年 8 月 25 日
    河北
  • 本文字数:2918 字

    阅读完需:约 10 分钟

浅聊一下邮件,短信的通知推送

背景

今天上午,做了一个可以实现邮件和短信推送的功能,涉及到模板设定,单人推送和多人推送。我觉得有点意思,拿来简单分享一下。

这个需求是这样

  • 管理人员可以自定义多个邮件(短信)发送模板

  • 模板中涉及到信息接收人员的一些关键信息,比如账号,登陆地址,等等,可以理解为是该人员的个人专属信息

  • 流程要简单,设定好模板之后,可以给系统内的制定人员批量发送邮件或短信,或同时发送。


浅析

这个需求里,我觉得有两个点需要注意

  1. 给多人发送的时候,要在发送时给不同的人发送同一个模板,但实际内容不一样的消息

  2. 批量发送不需要在页面停留,可以立刻进行其他的操作


实现

搞清楚关键点之后,就可以动手实践了

消息模板

首先要完成消息模板的设定,模板中需要包含几个关键点,除了独有的一些业务属性,还应该包括,标题,附件,正文信息。

其中,正文里应该可以有一些特殊占位符,用来替换实际的接收人员信息。

这个部分比较简单,代码就不贴了,直接看下效果吧。

这个部分的解体思路,重点就是多附件上传和占位符的使用。

附近上传就是另一种形式的文件上传,需要验证文件类型,md5 验证等。一般做这种管理系统,文件上传的模块都是相对独立的,这里只需要根据实际情况在业务模块进行一个简单的二次封装,就 ok 了。

而占位符,就是模板中替换关键信息的占位信息,在发送邮件的时候,通过替换的形式,就可以完成信息的替换,从而实现给不同的人,发送不同的邮件内容了。


批量发送

这个是我今天想分享的重点。

批量发送,我这里最开始的思路就是非常简单粗暴的遍历检索结果,然后执行模板的 Render 流程,最后再发送到指定的接收人员那里。

然后,这样做肯定是会有问题的,因为邮件发送本身需要调用一些 STMP 的服务,每次执行都会消耗一定的服务端资源,而带来的后果就是管理人员会感到明显的系统卡顿,迟迟收不到服务端的结果信息。

那怎么解决呢?

首先,云服务环境十分成熟的当下,选择第三方的服务来完成推送,是一种非常可靠的解决方案,比如腾讯云有一个邮件推送的服务,除了域名和发信配置,进入到业务流程后,它的步骤和上面提到的模板设置差不多,也是设定关键字作为占位符,调用的时候把占位符当参数传入接口,就可以实现推送效果了。


这种方式还可以设定回调地址,和我们本地的服务器完成数据关联,是一种非常好的推送方案。


但这里我还是采用的企业邮箱发送,也就是本地调用 stmp 服务发送邮件。实现方式,就是通过数据队列!

整体思路是

管理人员设定模板--->选择收信人--->提交发送任务--->队列线程检测到新的任务队列--->执行发送任务

也就是把发送的过程和业务系统进行了解耦。

业务系统只负责提交发送任务,发送过程有队列线程检测并执行。

看下关键代码

编写发送任务

依赖了Coravel中间件,之前也写过类似的实践方案,👉https://xie.infoq.cn/article/5b60e3449daac958a5aeb6252

这里的场景,还是先编写实际的巡检任务

		/// <summary>    /// 信息发送任务    /// </summary>    public class SmsTasks : IInvocable, IInvocableWithPayload<EvaluationExpertSendInfoTask>    {        public EvaluationExpertSendInfoTask Payload { get; set; }        private readonly IExpertEngageRepo _engageRepo;                /// <summary>        /// 构造函数        /// </summary>        /// <param name="engageRepo"></param>        public SmsTasks(IExpertEngageRepo engageRepo)        {            _engageRepo = engageRepo;        }
/// <summary> /// 启动任务 /// </summary> /// <returns></returns> public async Task Invoke() { await EmailCheck(); } //邮件任务巡检 private async Task EmailCheck() { if (await RedisHelper.HLenAsync("EmailTask") == 0) { Console.WriteLine($"{DateTime.Now},无邮件任务"); await CipAssistant.Logger.writeLogToRedis("无队列邮件需要发送", "info"); return; } var rets = RedisHelper.HGetAll("EmailTask"); string msg = $"{DateTime.Now},邮件发送任务启动,共计{rets.Count}封邮件待发送"; Console.WriteLine(msg); await CipAssistant.Logger.writeLogToRedis(msg, "info"); foreach (var item in rets) { string key = item.Key; var parts = key.Split("|"); int emailId = Convert.ToInt32(parts[0]); int[] expertIds = Array.ConvertAll(item.Value.Split(','), s => int.Parse(s)); await Task.Run(() => _engageRepo.SendExpertEmail(expertIds, emailId)); await RedisHelper.HDelAsync("EmailTask", key); await RedisHelper.LPushAsync("EmailTaskEnd", $"{{ timestamp: {parts[1]}, emailId:{emailId}, expertIds:{item.Value} }}"); } Console.WriteLine($"{DateTime.Now},邮件发送任务结束"); await CipAssistant.Logger.writeLogToRedis($"{DateTime.Now},邮件发送任务结束,共计{rets.Count}封", "info");
} //短信同理 }
复制代码


依赖注入

任务编写完成后,把发送任务注册成瞬时任务注入到系统

在 ConfigureServices 方法中

services.AddScheduler();services.AddTransient<SmsTasks>();services.AddQueue();
复制代码

在 Configure 方法

var provider = app.ApplicationServices;            provider.UseScheduler(scheduler =>            {                scheduler.Schedule<SmsTasks>()                                .EveryMinute();            });
复制代码

调用

准备工作完成后,就可以在控制器调用了



/// <summary> /// 发送邮件 /// </summary> /// <param name="expertIds"></param> /// <param name="emailId"></param> /// <returns></returns> [HttpPost,ValidateAntiForgeryToken] public async Task<IActionResult> SendEmail([FromServices] IQueue _queue, int[] expertIds, int emailId) { await RedisHelper.HSetAsync("EmailTask", $"{emailId}|{CipAssistant.Utils.DateTimeToTimeStamp(DateTime.Now)}", string.Join(',',expertIds)); _queue.QueueInvocable<Common.SmsTasks>(); return Json(_resp.success($"邮件发送任务已提交到后台任务队列,共计挂起{expertIds.Length}封邮件发送任务,将于1分钟后开始发送,期间您可以进行其他操作")); }
复制代码


修改完成后,再配合调整一下 UI 的展示逻辑,就可以的到一个非常友好的批量发送的管理界面了。

实际运行的效果如下



其实我这里还是用 Redis 的队列数据结果完成的队列任务,而没有用消息队列中间件来做,整体思路基本都是一样的。

好了,基本就是这样了。


发布于: 刚刚阅读数: 6
用户头像

学着码代码,学着码人生。 2019.04.11 加入

狂奔的小码农 http://www.tonydf.top (这是个循环圈)

评论

发布
暂无评论
浅聊一下邮件,短信的通知推送_dotnetcore_为自己带盐_InfoQ写作社区