OSS短信服务,下发短信到客户

最近要做一个小程序,业务流程最后需要下发短信给客户,其中短信内容含地址变量。

经调研后,使用OSS短信服务进行短信下发。并采用rocketmq与OSS SDK组合方式进行下发短信。

step1 开通OSS短信服务

登录阿里云工作台,开通短信服务。工作台链接:阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台

选择开通短信服务,这一步省略,开通后,进行购买套餐,页面如下图所示(已经购买了)

step2 设置短信签名和模板

点击添加签名,按照要求填写

点击模板管理,点击添加模板

模板创建完成后,会有模板code,在代码中会使用到他。

以上短信模板配置完成,接下来通过代码下发短信。

step3 代码(mq与sms结合)

3-1 nacos配置(也可以放到项目文件)yml

aliyun:
  sms-enable: true
  sms:
    access-key-id: #开通时获取的
    access-key-secret: #开通是获取的
    endpoint: dysmsapi.aliyuncs.com #固定的
    region: cn-hangzhou #oss服务器地
    common-code-sign-name: 测试签名 #控制台的签名管理
    common-code-template-code: SMS_DASD #模板CODE
topic:
  sms: sms # mq的topic
rocketmq: #mq 配置
	active: dev #开发环境
  enable: true
  endAppendEnv: true
  enableAcl: false
  accessKey: 
  secretKey: 
  access-channel-str: 
  consumer:
    consumer-list:
      - namesrv-addr: 127.0.0.1:9876 #mq地址
        consumer-group: smsConsumerGroup #消费者组名
        topic: sms
        sub-expression:
        pull-batch-size: 10
        model: CLUSTERING
        handler: com.test.oss.consumer.SmsConsumer #消费者的包位置
  producer:
    namesrv-addr: 127.0.0.1:9876
    group: testProducerGroup

3-2 发送消息接口

/**
 * @author ming
 */
public interface SmsService {

    /**
     * 通过mq发送短信
     *
     * @param msgDTO
     */
    void sendSmsToMq(MsgDTO msgDTO);

    /**
     * 直接发送同步短信
     *
     * @param phoneNumber   接收短信的手机号码
     * @param signName      短信签名名称
     * @param templateCode  短信模板CODE
     * @param templateParam 短信模板变量对应的实际值- ${path} ${suffix} 两个变量对应的值
     *					可以先建一个对象包含着两个字段,将值放进去后,转成String,再传参
     * @param outId         外部流水扩展字段
     * @return
     */
    boolean syncSendMsg(String phoneNumber, String signName, String templateCode,
                        String templateParam, String outId);

    /**
     * 直接发送异步短信
     *
     * @param phoneNumber   接收短信的手机号码
     * @param signName      短信签名名称
     * @param templateCode  短信模板CODE
     * @param templateParam 短信模板变量对应的实际值
     * @param outId         外部流水扩展字段
     * @return
     */
    void asyncSendMsg(String phoneNumber, String signName, String templateCode,
                      String templateParam, String outId);
}
@Data
public class MsgDTO implements Serializable {


    /**
     * 消息内容
     */
    private String content;

    /**
     * 消息类型枚举
     */
    private MsgTypeEnum msgTypeEnum;

}

public enum MsgTypeEnum {

    /** 短信验证码 */
    SMS_VERIFICATION_CODE,

    /** 短信注册通知 */
    SMS_REGISTER_NOTICE,

    /** 短信通用通知 */
    SMS_COMMON_NOTICE,
}
@Slf4j
@Service
@ConditionalOnProperty(prefix = "aliyun", name = "sms-enable", havingValue = "true")
public class SmsServiceImpl implements SmsService {

    private final RocketMqProducer rocketMqProducer;

    private final com.aliyun.dysmsapi20170525.Client smsSyncClient;

    private final com.aliyun.sdk.service.dysmsapi20170525.AsyncClient smsAsyncClient;

    public SmsServiceImpl(RocketMqProducer rocketMqProducer, Client smsSyncClient, AsyncClient smsAsyncClient) {
        this.rocketMqProducer = rocketMqProducer;
        this.smsSyncClient = smsSyncClient;
        this.smsAsyncClient = smsAsyncClient;
    }

    @Value("${topic.sms}")
    private String smsTopicName;

    @Override
    public void sendSmsToMq(MsgDTO msgDTO) {
        //mq发送短信
        rocketMqProducer.sendMsg(msgDTO.getContent(), msgDTO.getMsgTypeEnum().toString(), smsTopicName);
    }

    @Override
    public boolean syncSendMsg(String phoneNumber, String signName, String templateCode, String templateParam, String outId) {
        //直接发送短信
        SendSmsRequest sendSmsRequest = new SendSmsRequest()
                .setSignName(signName)
                .setTemplateCode(templateCode)
                .setPhoneNumbers(phoneNumber)
                .setTemplateParam(templateParam);
        if (StringUtils.hasLength(outId)) {
            sendSmsRequest.setOutId(outId);
        }
        RuntimeOptions runtime = new RuntimeOptions();
        try {
            SendSmsResponse sendSmsResponse = smsSyncClient.sendSmsWithOptions(sendSmsRequest, runtime);
            if (sendSmsResponse != null) {
                return "OK".equals(sendSmsResponse.getBody().code);
            }
        } catch (Exception error) {
            log.error("aliyun sms sync error.", error);
        }
        return false;
    }

    @Override
    public void asyncSendMsg(String phoneNumber, String signName, String templateCode, String templateParam, String outId) {
        //直接发送短信
        com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest.Builder builder = com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest.builder()
                .signName(signName)
                .templateCode(templateCode)
                .phoneNumbers(phoneNumber)
                .templateParam(templateParam);
        if (StringUtils.hasLength(outId)) {
            builder.outId(outId);
        }
        smsAsyncClient.sendSms(builder.build());
    }
}

mq producer

@Slf4j
@Component
@ConditionalOnProperty(prefix = "rocketmq", name = "enable", havingValue = "true")
public class RocketMqProducer {

    private final RocketMQProperties rocketMQProperties;

    private final RocketMQProducerProperties producerProperties;

    public RocketMqProducer(RocketMQProperties rocketMQProperties, RocketMQProducerProperties producerProperties) {
        this.rocketMQProperties = rocketMQProperties;
        this.producerProperties = producerProperties;
    }

    private DefaultMQProducer mqProducer;

    public DefaultMQProducer getMqProducer() {
        return mqProducer;
    }

    @PostConstruct
    public void initMqProducer() {
        //初始加载
        if (ObjectUtil.equal(rocketMQProperties.getEnableAcl(), Boolean.TRUE)) {
            mqProducer = new DefaultMQProducer(producerProperties.getGroup(), new AclClientRPCHook(new SessionCredentials(rocketMQProperties.getAccessKey(), rocketMQProperties.getSecretKey())));
        } else {
            mqProducer = new DefaultMQProducer(producerProperties.getGroup());
        }
        mqProducer.setNamesrvAddr(producerProperties.getNamesrvAddr());
        mqProducer.setAccessChannel(rocketMQProperties.getAccessChannel());
        mqProducer.setVipChannelEnabled(false);
        try {
            mqProducer.start();
        } catch (MQClientException e) {
            log.error("rocketmq producer start error.", e);
        }
    }

    @PreDestroy
    public void destroy() {
        //执行完 销毁
        mqProducer.shutdown();
    }

    /**
     * 发送同步消息
     *
     * @param msgContent 消息内容
     * @param tags       tags
     * @param topic      topic
     */
    public void sendMsg(String msgContent, String tags, String topic) {
        //根据环境获取topic名称 细看mqUtil,以此区分环境
        topic = ObjectUtil.equal(rocketMQProperties.getEndAppendEnv(), Boolean.TRUE) ? MqUtil.getInstance().getMqTopicNameByAppProfileEnv(topic) : topic;
        log.info("send [{}:{}] msg {}", topic, tags, msgContent);
        try {
            //通过topic和tags发送mq消息
            Message msg = new Message(topic, tags, msgContent.getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = this.mqProducer.send(msg);
            if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                throw new CommonException(sendResult.getSendStatus().toString());
            }
        } catch (Exception e) {
            log.error("send [{}:{}] msg {} error.", topic, tags, msgContent);
        }
    }
}

mq消费 consumer 

@Slf4j
public class SmsConsumer implements Consumer<List<MessageExt>> {

    private final SmsService smsService = SpringUtil.getBean(SmsServiceImpl.class);

    @Override
    public void accept(List<MessageExt> messageExts) {
        try {
            for (MessageExt messageExt : messageExts) {
                //消费mq消息,并获取发送的msgContent
                String msgContent = StrUtil.str(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);
                SmsMsg smsMsg = JSONUtil.toBean(msgContent, SmsMsg.class);
                log.info("messageExt: {}", msgContent);
                smsService.asyncSendMsg(smsMsg.getPhoneNumber(),
                        aliyunSmsProperties.getCommonNoticeCodeSignName(),
                        aliyunSmsProperties.getCommonNoticeTemplateCode(),
                        JSONUtil.toJsonStr(smsMsg.getTemplateParam()), null);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}
public class MqUtil {

    private final ApplicationProfileEnvProperties applicationProfileEnvProperties = SpringUtil.getBean(ApplicationProfileEnvProperties.class);

    private static volatile MqUtil instance;

    private MqUtil() {
    }

    /**
     * getInstance方法会进行两次判空操作;第一次,判断是否实例化了,有实例对象则直接返回,不需要其他操作。
     * 如果没有实例化,则说明是程序第一次去获取实例对像,会进行一次加锁操作,只允许一个线程进入方法,
     * 进入方法之后的判空操作是为了仅允许第一个进入的线程进行实例化,其他线程不允许实例化。
     * 因为实例化操作仅需要进行一次同步,所以可以用第一次判空操作来进行避免。至于第二次判空操作,则是为了保证单例。
     *
     * @return
     */
    public static MqUtil getInstance() {
        if (instance == null) {
            synchronized (MqUtil.class) {
                if (instance == null) {
                    instance = new MqUtil();
                }
            }
        }
        return instance;
    }

    /**
     * 通过环境env,获取mq得topic名字
     *
     * @param topicName
     * @return 原始topic名字-env名称,例sms-test, sms-prod
     */
    public String getMqTopicNameByAppProfileEnv(String topicName) {
        return topicName + Constants.UNDER_LINE + applicationProfileEnvProperties.getActive();
    }

    /**
     * 通过环境env,获取mq得consumerGroup名字
     *
     * @param consumerGroup
     * @return 原始consumerGroup名字-env名称,例algorithmTaskOvertimeConsumerGroup-test, 例algorithmTaskOvertimeConsumerGroup-prod
     */
    public String getMqConsumerGroupByAppProfileEnv(String consumerGroup) {
        return consumerGroup + Constants.UNDER_LINE + applicationProfileEnvProperties.getActive();
    }
}
@Data
@Component
@ConditionalOnProperty(prefix = "rocketmq", name = "enable", havingValue = "true")
public class ApplicationProfileEnvProperties {
	//环境(dev test prod)
    private String active;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值