2025年消息中心设计

消息中心设计消息类型 即时消息 定时消息 消息通道 钉钉 短信 邮件等 前言 由于业务需求 最近需要做一个通知中心 其中包含短信 钉钉 邮箱 站内信 app 推送等不同通道的消息发送 今天简单介绍一下我的实现思路 供大家参考 需求分析 消息类型 即时消息 定时消息

大家好,我是讯享网,很高兴认识大家。

  • 消息类型:即时消息、定时消息。
  • 消息通道:钉钉、短信、邮件等。

前言

  • 由于业务需求,最近需要做一个通知中心,其中包含短信、钉钉、邮箱、站内信、app推送等不同通道的消息发送,今天简单介绍一下我的实现思路,供大家参考。

需求分析 


讯享网

 

  • 消息类型:即时消息、定时消息。
  • 消息通道:钉钉、短信、邮件等。

 大致流程

 

  • 不同业务通过统一服务解析模板以及发送通道,然后找到对应的实现发送对应的消息。

实现思路 

 

  • 考虑到以后的扩展性,系统内部可以通过MQ方式调用,同时会对外提供接口,如果以后提供给第三方使用可以通过http方式接入。
  • 实现思路如上图:不同方式调用统一的服务处理,首先消息落库(便于消息查询以及状态跟踪等)然后校验处理解析模板获取配置信息以及接收主题信息,找到对应的通道通过消息发送的实现发送对应通道的消息,同时记录发送日志以及回写消息记录表的消息状态。(需要提前维护消息配置/模板配置、以及接收主题配置)

表设计

 

  • 初步构想5张表,分别是:
  1. 消息通道表:记录通道信息以及通道唯一码,用于发送消息时使用。
  2. 消息配置表:消息编码,消息类型,发送通道(管理通道表)以及默认消息体(支持通配符)等。
  3. 主题配置表:接收方主题配置,发送消息的要素,例如:手机号、邮箱等,可配可不配,如果上游给到的内容能直接用于消息发送则不需要配置,主要用于某一类的信息获取和发送。
  4. 消息记录表:记录上游的消息内容以及消息状态等。
  5. 发送日志表:记录消息的发送内容时间以及结果等。

核心代码设计

  • 消息通道设计:策略加模板方法

执行定义

  • 定义执行发送消息的方法
/ * @author leexh * @since 2022/5/18 19:41 * desc: 执行发送消息定义 */ public interface MessageChannelService<T> { void execute(T messageInfo); }

讯享网

 抽象模板类

讯享网/ * @author leexh * @since 2022/5/16 16:17 * desc: */ @Slf4j public abstract class BaseMessageChannelProvider<T extends BaseMessage, S extends MessageResult> implements MessageChannelService<T> { @Resource private NcMessageRecordService ncMessageRecordService; / * 是否需要获取主题 */ private boolean isAccessTheme = true; public void isAccessTheme(boolean isAccessTheme) { this.isAccessTheme = isAccessTheme; } / * 获取主题配置 * * @param messageInfo 主题编码 * @return 解析好的主题信息 */ protected abstract T getThemeConfig(T messageInfo); / * 发送消息 * * @param messageInfo 消息信息 * @return 发送结果 */ protected abstract S sendMessage(T messageInfo); / * 执行消息发送 * * @param messageInfo 消息信息 */ public void execute(T messageInfo) { // 1.获取接收主题 if (isAccessTheme) { messageInfo = getThemeConfig(messageInfo); } // 2.发送消息 S s = sendMessage(messageInfo); Long messageRecordId = messageInfo.getMessageRecordId(); if (messageRecordId != null) { NcMessageRecord updateRecord = new NcMessageRecord(); updateRecord.setId(messageRecordId); // baseDaoInitialService.initialUpdateBaseDaoSystemValue(updateRecord); // 是否返回结果 updateRecord.setSendStatus(s.getIsBack() ? s.getResultStatus().getCode() : ResultStatus.SENT.getCode()); ncMessageRecordService.updateById(updateRecord); } } }

首先定义BaseMessageChannelProvider抽象类,并实现执行接口,BaseMessageChannelProvider中定义了两个抽象方法需要子类实现,第一个是:getThemeConfig(获取发送主题),第二个是:sendMessage(发送消息),不同通道的主题配置以及发送处理不一样,固有子类自己实现处理;同时定义了两个普通方法:isAccessTheme和execute,子类可实现可不实现;isAccessTheme用于标记是否需要获取主题,如果上游能直接给到接收对象,则可以把isAccessTheme置为false,代表不需要获取主题,方法execute用来执行发送消息以及更新消息状态,可由子类自行实现处理,也可使用父类方法。

钉钉消息处理类

/ * @author leexh * @since 2022/5/16 16:38 * desc: */ @Slf4j @Component public class DingTalkMessageChannelServiceImpl extends BaseMessageChannelProvider<DingTalkMessageInfo, MessageResult> { @Resource private NcThemeConfigService themeConfigService; @Override protected DingTalkMessageInfo getThemeConfig(DingTalkMessageInfo messageInfo) { if(log.isDebugEnabled()){ log.debug("获取钉钉模板..."); } System.out.println("获取钉钉模板..."); return messageInfo; } @Override protected MessageResult sendMessage(DingTalkMessageInfo messageInfo) { // TODO... if(log.isDebugEnabled()){ log.debug("发送钉钉消息..."); } System.out.println("发送钉钉消息..."); isAccessTheme(true); MessageResult result = new MessageResult(); result.isBack(false); return result; } }

消息体基类 

讯享网/ * @author leexh * @since 2022/5/16 16:34 * desc: */ @Data public class BaseMessage { / * 消息id */ private Long messageRecordId; / * 消息编码(对应消息配置) */ private String msgCode; / * 模板编码(对应模板配置) */ private String themeCode; public BaseMessage() { } public BaseMessage(String msgCode, String themeCode) { this.msgCode = msgCode; this.themeCode = themeCode; } }
  • 可根据实际情况自行扩展和修改,所有通道的消息实体都要基础该类。

发送结果基类 

/ * @author leexh * @since 2022/5/16 16:33 * desc: */ @Data public class MessageResult { private ResultStatus resultStatus; private Boolean isBack = true; public void isBack(boolean isBack) { this.isBack = isBack; } }
  • 每种通道发完消息使用此类或者其子类处理后继逻辑。

发送结果枚举类 

讯享网/ * @author leexh * @since 2022/5/16 21:58 * desc: */ @Getter @AllArgsConstructor public enum ResultStatus { / * 已发送 */ SENT("2", "已发送"), / * 发送成功 */ SUCCESS("3", "发送成功"), / * 发送失败 */ FAILED("4", "发送失败"); private final String code; private final String desc; }

使用demo 

public static void main(String[] args) { // 获取模板配置 // NcPropertyConfig config = new NcPropertyConfig(); // Map<String, MessageChannelService> channelMap = config.getChannelMap(); // MessageChannelService service = channelMap.get("AA"); // DingTalkMessageInfo messageInfo = new DingTalkMessageInfo(); // messageInfo.setMsgCode("AA"); // service.execute(messageInfo); MessageChannelService<DingTalkMessageInfo> provider = new DingTalkMessageChannelServiceImpl(); DingTalkMessageInfo messageInfo = new DingTalkMessageInfo(); messageInfo.setMsgCode("AA"); ((DingTalkMessageChannelServiceImpl) provider).isAccessTheme(true); provider.execute(messageInfo); }

运行结果 

讯享网13:00:26.521 [main] DEBUG com.nc.service.channel.DingTalkMessageChannelServiceImpl - 获取钉钉模板... 获取钉钉模板... 13:00:26.525 [main] DEBUG com.nc.service.channel.DingTalkMessageChannelServiceImpl - 发送钉钉消息... 发送钉钉消息... Process finished with exit code 0

总结 

  • 一个消息通知中心的技术难度不大,但是要对接不同的通道以及考虑到后期第三方使用的扩展,想做好做强还要不断改善,结合自身的业务以及消息量来进行设计以及技术方案选择,例如后期需要实现消息推送以及移动端的使用,可以考虑使用微消息队列MQTT等,总之没有最好的只有适合自己的,在满足自己业务需求的情况下做到最简最易使用才更好!
  • 本文更多是为了分享实现思路以及设计方案,没有过多的讨论技术,以上代码示例是demo没有具体逻辑,至于不同通道的对接需要自行实现,如有不解或者疑问欢迎评论区指出,谢谢大家!

小讯
上一篇 2025-03-19 18:01
下一篇 2025-02-16 22:56

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/66024.html