拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 讯息推送界面设计(内含原始码)

讯息推送界面设计(内含原始码)

白鹭 - 2022-02-22 2102 0 0

我是3y,一年CRUD经验用十年的markdown程序员???????常年被誉为优质八股文选手

今天要做的就是实作austin-apiaustin-api-impl模块的部分代码,这块完成了之后模块之间的一整条链路就打通咯


austin项目核心功能:发送讯息

项目出现意义:只要公司内有发送讯息的需求,都应该要有类似austin的项目,对各类讯息进行统一发送处理,这有利于对功能的收拢,以及提高业务需求开发的效率

不多BB,开始今天的正题

01、界面设计

austini-api模块下定义发送讯息的界面,在austin-api-impl下实作具体的逻辑,我的界面实作定义:

public interface SendService {


    /**
     * 单模板单文案发送界面
     * @param sendRequest
     * @return
     */
    SendResponse send(SendRequest sendRequest);


    /**
     * 单模板多文案发送界面
     * @param batchSendRequest
     * @return
     */
    SendResponse batchSend(BatchSendRequest batchSendRequest);

}

对外提供的界面,除了需要提供Single界面,最好还提供个Batch界面,因为很有可能业务方是需要一次批量执行的(如果只有Single界面,那就需要多次远程呼叫,这样对业务而言就不太合适了)

我所定义的界面自变量如下:

public class SendRequest {

    /**
     * 执行业务型别
     */
    private String code;

    /**
     * 讯息模板Id
     */
    private Long messageTemplateId;


    /**
     * 讯息相关的自变量
     */
    private MessageParam messageParam;
    
}

通过messageTemplateId可以去数据库查出整个模板的信息,而MessageParam则是业务自行传入的自变量(重要的是接收者以及文案的自变量信息),而code则代表着当前请求要执行什么业务型别的(可基于该code扩展,后面会继续聊到)

02、代码实作

从流程可以看到,austin-api接收到请求之后,是把讯息发到MQ

这样做有什么好处呢?假设某讯息的服务超时,austin-api如果是直接呼叫下发界面服务,那可能会存在超时风险,拖垮整个界面性能,MQ在这是为了做异步和解耦,并且在一定程度上抗住业务流量,

对于绝大多数发送的讯息而言,业务方也不太关心是不是能在界面呼叫时就知道发送结果,并且某些渠道在发送的时候也不知道发送的结果(最后的结果是异步告知的,比如短信和PUSH推送)

基于以上的原因,引入MQ来承载界面的流量以及做异步,是非常合理的事,

前两天我在博客平台上发了一篇文章《面试官:系统需求多变时如何设计? 》,有网友评论了一把:

在这次实作中,我也是用了责任链模式,具体完整的代码大家就去Gitee拉就好了,很多同学拉完代码发现看不懂了,大家可以按照下面的图去梳理下责任链的各个角色,如果实在看不懂,建议翻下我以前写过的责任链文章(已经投稿过两篇了)

回到代码实作吧,这次我实作的业务是:自变量前置检查->自变量拼装->发送讯息

呀,都画了这幺多图了,先点个赞,关注一波先咯,

在这几个流程中,可能你下次拉代码的时候,会看到有“后置检查”,或者别的什么的,但不管怎么样,加这种逻辑我再也不用在同一个类上写各种if else啦,只要在某个节点处添加一个Action就完事了,

(注:这是第一版实作,后面肯定会在基础上添加逻辑或注释的,其实已经在写了,但我一般是有个小阶段再push代码,所以记得star下gitee方便看最新的代码)

先来说前置检查吧,主要就判断模板ID是否有传入,讯息自变量是否有传入(对自变量的常规检查,如果有问题,直接break掉链路,回传告诉呼叫方有问题)

接着来看自变量拼装,这块主要就是通过模板ID去查整个模板的内容,然后根据业务入参拼装出自己的TaskInfo(任务讯息),

可能有同学会有疑问?:为什么不能直接用模板的POJO呢?反而需要拼装成TaskInfo?

其实还是比较好理解的,模板是作为给用户去配置该讯息的信息,这是最最原始的信息,但是我们发送的时候是需要做处理的,比如,我要在用户写好的URL链接上拼接自变量,我要对占位符进行替换真实的值,我要在模板的基础上增加业务ID进而追踪资料 等等等,

说白了,TaskInfo是基于模板的,在模板的基础上添加了某些平台性的栏位(businessId),决议出用户设定的模板而想要发送的真实内容等等,

在这里,值得要说明的是msgContent该栏位的说明,在模板中,该栏位我在数据库注释所下的定义是(这个栏位存入数据库一定是JSON格式的):

`msg_content`        varchar(600) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '讯息内容 占位符用{$var}表示',

不同的渠道的JSON结构还不一样:

  • 短信:{"content":"","url":""}
  • 邮件:{"content":"","subTitle":""}
  • Push:{"content":"","subTitle":"","phoneImgUrl":""}
  • 小程序:{"content":"","pagePath":"" .......}

第一反应,我是想把所有渠道可能用到的栏位都定义在TaskInfo下,后来感觉这样不太好看,于是我就定义了各种Model(不同的发送渠道拥有着自己的内容模型)

于是,我在组装TaskInfo的时候利用反射来进行映射,替换占位符则借助的是PropertyPlaceholderHelper

而发送则很简单了,我是直接把TaskInfo序列化为JSON,然后读取的时候再反序列化就好了,

值得注意的是,因为TaskInfo用的是ContentModel来存盘着内容模型,所以我们在序列化JSON的时候需要把"类信息"写进去,不然在反序列的时候是拿不到子类的资料的,

03、总结

对于有原始码的项目,其实我是不太愿意每一步讲解我写的代码的,因为我认为我本身写得也没那么复杂,也没有炫技的成分在内,

但自从push了代码以后,在群里提醒各位跟着做项目的小伙伴后,有好几位向我反馈看不太懂,所以这篇我就单独拎出来讲讲,

再回过头看,其实在austin-api层接收到请求之后,在发送讯息至MQ之前,在这里的操作都是非常简单,其实是可以把通用业务做在这(比如说通用去重的功能),但经我考虑之后,还是不太合适,

austin-api算是一个接入层,到目前为止它只是通过id去数据库读取配置,就没有耗时的操作(这意味着他能承载的并发是极大的),假设通过ID去数据库读取将来存在瓶颈,我们还可以考虑将配置从Redis甚至本地存储器里取,

这是由业务可以决定的:一个模板的变更往往并不多,即便快取存在强一致性的问题,但就那点点时间是完全可接受的,

Question :为什么发个讯息需要MQ?

Answer:发送讯息实际上是呼叫各个服务提供的API,假设某讯息的服务超时,austin-api如果是直接呼叫服务,那存在超时风险,拖垮整个界面性能,MQ在这是为了做异步和解耦,并且在一定程度上抗住业务流量,

Question:能简单说下接入层做了什么事吗?

Answer

欢迎关注我的微信公众号【Java3y】来聊聊Java面试,对线面试官系列持续更新中!

【对线面试官+从零撰写Java项目】 持续高强度更新中!求star!!原创不易!!求三连!!

Gitee链接:https://gitee.com/austin

GitHub链接:https://github.com/austin

更多的文章可往:文章的目录导航
标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *