rabbitMq实现系统内的短信发送设计&动态获取BEAN

rabbitMq实现系统内的短信发送设计&动态获取BEAN

1.短信非系统的重要节点操作,可以在任务完成之后,比如下单成功,发送下单成功的mq消息,短信服务接收到mq消息,
动态的判断该短信的code,通过全局公共的父类(调用中台等接口获取全部所有需要的对象参数),获取短信中的{mobile}等参数来替换短信模板中的可变量。
这样系统中的所有的发送短信,都可以继承该父类,获取参数,从而实现方便快捷的扩展短信接入和对原来的短信模板内容的修改或新增短信中的可变量。

2.短信服务封装好短信的code和短信中的需要的参数,然后解析出来文本,将mobile和content等重要参数,调用第三方的短信供应商接口来发送短信。

3.系统中的短信模板表设计如下:

CREATE TABLE `tbl_sms_template` (
  `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `text_code` VARCHAR(80) NOT NULL COMMENT '唯一标识',
  `content` VARCHAR(1000) NOT NULL COMMENT '短信消息内容,动态参数以{开头,}结尾',
  `send_source_srv` VARCHAR(30) DEFAULT NULL COMMENT '发送方服务来源',
  `send_source_biz` VARCHAR(30) DEFAULT NULL COMMENT '发送方业务来源',
  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `create_op` VARCHAR(50) DEFAULT NULL COMMENT '创建人',
  `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_delete` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '删除标记 0未删除 1已删除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `text_code` (`text_code`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信模板表'


CREATE TABLE `tbl_sms_log` (
  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'ID主键',
  `mobile` BIGINT(20) DEFAULT NULL COMMENT '手机号',
  `content` TEXT COMMENT '短信内容',
  `type` INT(1) DEFAULT NULL COMMENT '分类',
  `remark` VARCHAR(100) DEFAULT NULL COMMENT '备注',
  `req_ip` VARCHAR(60) DEFAULT NULL COMMENT '请求IP',
  `req_beg_time` TIMESTAMP NULL DEFAULT NULL COMMENT '请求时间',
  `req_beg_back_time` TIMESTAMP NULL DEFAULT NULL COMMENT '请求返回时间',
  `req_status` INT(1) DEFAULT NULL COMMENT '发送结果(1:成功2:失败)',
  `res` VARCHAR(150) DEFAULT NULL COMMENT '短信返回结果',
  `supplier` INT(1) DEFAULT '1' COMMENT '短信平台服务供应商',
  `send_source_srv` VARCHAR(30) DEFAULT NULL COMMENT '发送方服务来源',
  `send_source_biz` VARCHAR(30) DEFAULT NULL COMMENT '发送方业务来源',
  `account_type` INT(1) DEFAULT NULL COMMENT '短信账户通道类型',
  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `create_op` VARCHAR(50) DEFAULT NULL COMMENT '创建人',
  `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_delete` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '删除标记 0未删除 1已删除',
  PRIMARY KEY (`id`),
  KEY `create_time` (`create_time`),
  KEY `mobile` (`mobile`,`req_status`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='短信发送日志表'

4.测试短信的方法:
4.1.走全流程等通过系统节点/接口/App/XxlJob调度任务来触发,看是否符合预期结果。
4.2.可以通过rabbitMq管理后台,找到队列名称,直接将json字符串通过队列来发送,检查短信服务是否正常接收且短信是否正常发送。

4.3.可以通过rabbitMq管理后台,找到交换机exchange名称,直接将json字符串通过交换机exchange和路由key来发送,检查短信服务是否正常接收且短信是否正常发送。

5.具体实现,伪代码
发送端

//取消订单
    CANCEL_ORDER_EVENT("sys-order-action","action.cancel.order.key","取消订单事件"),

   //发送MQ  只需要一个订单号。orderNo可以封装到一个对象中。
   //发送端不关心具体的短信可变量参数,减少耦合代码。
    baseProducer.sendTopicMessage(CANCEL_ORDER_EVENT.exchange, CANCEL_ORDER_EVENT.routingKey, orderNo);

接收端实现类

配置路由key和实现类的枚举类
CANCEL_ORDER_EVENT("action.cancel.order.key", "CancelOrderService"), //取消订单事件
    
配置短信模板:    
    //发送短信给售货商
    CANCEL_ORDER_TO_OWNER("CancelOrderToOwner", "您的订单(订单号:$orderNo$),订单金额($orderAmt$),请在($orderAddress$)配送地址,已取消,快去APP看看吧!")
    //发送短信给买货方
    CANCEL_ORDER_TO_BUYER("CancelOrderToBuyer", "您的订单(订单号:$orderNo$),订单金额($orderAmt$),请在($orderAddress$)配送地址,已取消,快去APP看看吧!")


@RabbitListener(bindings = {@QueueBinding(value = @Queue(value = "order_sms_queuetest", durable = "true"),
            exchange = @Exchange(value = "sys-order-action", durable = "true", type = "topic"), key = "action.#")
}, containerFactory = "orderRabbitListenerContainerFactory")
public void process(Message message) {
    log.info("receive order action message: " + new String(message.getBody()));
    try {
        //构建短信发送对象
        SmsMessage smsMessage = createSmsMessageService(message);
        if (Objects.nonNull(smsMessage)) {
            //发送短信
            sendSMSMessageData(smsMessage);
        }
    } catch (Exception e) {
        log.info("订单短信发送异常,msg:[{}]", e);
    }


public SmsMessage createSmsMessageService(Message message){
        //1.获取路由key
        String routeKeyName = message.getMessageProperties().getReceivedRoutingKey();
        
        //2.根据路由key 找到 实现类的service名称 (配置路由key和实现类的枚举类)
        String serviceName = getSmsServiceTemplate(routeKeyName);
        if(StringUtils.isBlank(serviceName))
        {
            log.info("该事件没有需要发送得短信,routeKeyName:[{}]",routeKeyName);
            return null;
        }
        
        //3.首字母小写
        serviceName = serviceName.substring(0, 1).toLowerCase() + serviceName.substring(1);
        log.info("该事件获取的smsMessage service为:[{}]",serviceName);
        
        //4.父类  根据serviceName名称,调用getBean方式获取父类,方法中具体逻辑由实现子类来实现。
        
        //5.调用实现子类获取短信发送的对象(短信参数)
        return smsMessage;
    }

//动态获取BEAN 参考:
Java动态获取实现类 Class.forName(clazz).newInstance()和applicationContext.getBean, bean Map寻找方式,Java Map定义和初始化方法
https://www.cnblogs.com/oktokeep/p/18235912

@Service
public class OrderSmsService implements  ApplicationContextAware {

    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    
    public <T> T getBean(String beanName) {
        if (applicationContext.containsBean(beanName)) {
            return (T) applicationContext.getBean(beanName);
        } else {
            return null;
        }
    }
    
}

扩展:
基础底层短信服务的设计思路
https://www.cnblogs.com/oktokeep/p/17663970.html

posted on 2024-06-06 19:48  oktokeep  阅读(39)  评论(0编辑  收藏  举报