• 网站中接入手机验证码和定时任务(含源码)


    页面预览

    绑定手机号#

    image-20230302220351371

    未绑定手机号#

    image-20230302220501741

    已绑定手机号#

    image-20230302220435826

    第01章-短信发送

    1、云市场-短信API#

    1.1、开通三网106短信#

    在阿里云云市场搜索“短信”,一般都可用,选择一个即可,例如如下:点击“立即购买”开通

    这里开通的是【短信验证码- 快速报备签名】

    image-20230302152247156

    1.2、获取开发参数#

    登录云市场控制台,在已购买的服务中可以查看到所有购买成功的API商品情况,下图红框中的就是AppKey/AppSecret,AppCode的信息。

    image-20230320093109432

    1.3、API方式使用云市场服务#

    将工具类放入service-yun微服务的utils包中:资料:资料>短信发送>工具类

    参考如下例子,复制代码在test目录进行测试

    image-20230302175615657

    2、发送短息#

    2.1、Controller#

    创建FrontSmsController

    package com.atguigu.syt.yun.controller.front;
    
    @Api(tags = "短信接口")
    @RestController
    @RequestMapping("/front/yun/sms")
    public class FrontSmsController {
    
        @Resource
        private SmsService smsService;
    
        @Resource
        private RedisTemplate redisTemplate;
    
        @ApiOperation("发送短信")
        @ApiImplicitParam(name = "phone",value = "手机号")
        @PostMapping("/send/{phone}")
        public Result send(@PathVariable String phone) {
    
            //生成验证码
            int minutes = 5; //验证码5分钟有效
            String code = RandomUtil.getFourBitRandom();
    
            //创建短信发送对象
            SmsVo smsVo = new SmsVo();
            smsVo.setPhone(phone);
            smsVo.setTemplateCode("CST_qozfh101");
            Map paramsMap = new HashMap(){{
                put("code", code);
                put("expire_at", 5);
            }};
            smsVo.setParam(paramsMap);
    
            //发送短信
            smsService.send(smsVo);
    
            //验证码存入redis
            redisTemplate.opsForValue().set("code:" + phone, code, minutes, TimeUnit.MINUTES);
    
            return Result.ok().message("短信发送成功");
        }
    }
    

    2.2、Service#

    接口:SmsService

    package com.atguigu.syt.yun.service;
    
    public interface SmsService {
        /**
         * 发送短信
         * @param smsVo
         */
        void send(SmsVo smsVo);
    }
    

    实现:SmsServiceImpl

    package com.atguigu.syt.yun.service.impl;
    
    @Service
    @Slf4j
    public class SmsServiceImpl implements SmsService {
    
        @Override
        public void send(SmsVo smsVo) {
    
            String host = "https://dfsns.market.alicloudapi.com";
            String path = "/data/send_sms";
            String method = "POST";
            String appcode = "你的appcode";
            Map headers = new HashMap<>();
            //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
            headers.put("Authorization", "APPCODE " + appcode);
            //根据API的要求,定义相对应的Content-Type
            headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            Map querys = new HashMap<>();
            Map bodys = new HashMap<>();
    
            StringBuffer contentBuffer = new StringBuffer();
            smsVo.getParam().entrySet().forEach( item -> {
                contentBuffer.append(item.getKey()).append(":").append(item.getValue()).append(",");
            });
            String content = contentBuffer.substring(0, contentBuffer.length() - 1);
    
            bodys.put("content", content);
            bodys.put("phone_number", smsVo.getPhone());
            bodys.put("template_id", smsVo.getTemplateCode());
    
            try {
                /**
                 * 重要提示如下:
                 * HttpUtils请从
                 * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
                 * 下载
                 *
                 * 相应的依赖请参照
                 * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
                 */
                HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
    
                //获取response的body
                String data = EntityUtils.toString(response.getEntity());
    
                HashMap resultMap = JSONObject.parseObject(data, HashMap.class);
                String status = resultMap.get("status");
    
                if(!"OK".equals(status)){
                    String reason = resultMap.get("reason");
                    log.error("短信发送失败:status = " + status + ", reason = " + reason);
                    throw new GuiguException(ResultCodeEnum.FAIL.getCode(), "短信发送失败");
                }
    
            } catch (Exception e) {
                log.error(ExceptionUtils.getStackTrace(e));
                throw new GuiguException(ResultCodeEnum.FAIL.getCode(), "短信发送失败");
            }
        }
    }
    

    3、绑定手机号#

    3.1、Controller#

    service-user微服务FrontUserInfoController中添加接口方法

    @ApiOperation("绑定手机号")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "phone",value = "手机号", required = true),
        @ApiImplicitParam(name = "code",value = "验证码", required = true)})
    @PostMapping("/auth/bindPhone/{phone}/{code}")
    public Result bindPhone(@PathVariable String phone, @PathVariable String code, HttpServletRequest request, HttpServletResponse response) {
    
        Long userId = authContextHolder.checkAuth(request, resposne);
        userInfoService.bindPhone(userId, phone, code);
        return Result.ok().message("绑定成功");
    }
    

    3.2、Service#

    接口:UserInfoService

    /**
         * 绑定当前用户的手机号
         * @param userId
         * @param phone
         * @param code
         */
    void bindPhone(Long userId, String phone, String code);
    

    实现:UserInfoServiceImpl

    @Resource
    private RedisTemplate redisTemplate;
    
    @Override
    public void bindPhone(Long userId, String phone, String code) {
    
        //校验参数
        if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {
            throw new GuiguException(ResultCodeEnum.PARAM_ERROR);
        }
    
        //校验验证码
        String phoneCode = (String)redisTemplate.opsForValue().get("code:" + phone);
        if(!code.equals(phoneCode)) {
            throw new GuiguException(ResultCodeEnum.CODE_ERROR);
        }
    
        //根据手机号查找会员
        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
        //手机号没有被其他人绑定过
        queryWrapper.eq(UserInfo::getPhone, phone).ne(UserInfo::getId, userId);
        UserInfo userInfo = baseMapper.selectOne(queryWrapper);
    
        //手机号已存在
        if(userInfo != null) {
            throw new GuiguException(ResultCodeEnum.REGISTER_MOBILE_ERROR);
        }
        //设置绑定手机号
        userInfo = new UserInfo();
        userInfo.setId(userId);
        userInfo.setPhone(phone);
        baseMapper.updateById(userInfo);
    }
    

    4、前端整合#

    4.1、api#

    创建sms.js

    import request from '~/utils/request'
    
    export default {
    
      sendCode(phone) {
        return request({
          url: `/front/yun/sms/send/${phone}`,
          method: `post`
        })
      }
    }
    

    在userInfo.js中添加方法

    bindPhone(phone, code) {
        return request({
            url: `/front/user/userInfo/auth/bindPhone/${phone}/${code}`,
            method: `post`
        })
    },
    

    4.2、页面组件#

    复制页面到项目pages目录中

    资料:资料>手机号绑定页面

    第02章-引入MQ

    预约或取消预约成功后我们要 更新预约数 以及 发送短信提醒,为了实现用户下单和取消订单的快速响应,这部分逻辑我们就交给MQ完成。

    1、引入RabbitMQ#

    1.1、安装RabbitMQ#

    #拉取镜像
    docker pull rabbitmq:3.8-management
    #创建容器启动
    docker run -d --restart=always -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:3.8-management
    

    1.2、访问管理后台#

    http://192.168.100.101:15672

    登录:guest / guest

    1.3、创建rabbit-utils模块#

    在common模块中创建rabbit-utils模块

    1.4、引入依赖#

    在rabbit-utils中引入依赖

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-amqpartifactId>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>
    dependencies>
    

    1.5、创建RabbitService类#

    添加sendMessage方法

    package com.atguigu.syt.rabbit;
    
    @Service
    @Slf4j
    public class RabbitService {
    
        @Resource
        private RabbitTemplate rabbitTemplate;
        
        /**
         *  发送消息
         * @param exchange 交换机
         * @param routingKey 路由
         * @param message 消息
         */
        public boolean sendMessage(String exchange, String routingKey, Object message) {
            log.info("发送消息...........");
            rabbitTemplate.convertAndSend(exchange, routingKey, message);
            return true;
        }
    }
    

    1.6、配置MQ消息转换器#

    package com.atguigu.syt.rabbit.config;
    
    @Configuration
    public class MQConfig {
    
        @Bean
        public MessageConverter messageConverter(){
            //配置json字符串转换器,默认使用SimpleMessageConverter
            return new Jackson2JsonMessageConverter();
        }
    }
    

    1.7、添加常量类#

    package com.atguigu.syt.rabbit.config;
    public class MQConst {
    
        /**
         * 预约/取消订单
         */
        public static final String EXCHANGE_DIRECT_ORDER = "exchange.direct.order";
        public static final String ROUTING_ORDER = "routing.order";
        public static final String QUEUE_ORDER  = "queue.order";
    
        /**
         * 短信
         */
        public static final String EXCHANGE_DIRECT_SMS = "exchange.direct.sms";
        public static final String ROUTING_SMS = "routing.sms";
        public static final String QUEUE_SMS  = "queue.sms";
    }
    

    2、service-yun中发送短信#

    2.1、引入依赖#

    
    <dependency>
        <groupId>com.atguigugroupId>
        <artifactId>rabbit-utilsartifactId>
        <version>1.0version>
    dependency>
    

    2.2、添加MQ配置#

    spring:
      rabbitmq:
        host: 192.168.100.101
        port: 5672
        username: guest
        password: guest
    

    2.3、封装MQ监听器#

    在监听器中发送短信:

    package com.atguigu.syt.yun.receiver;
    
    @Component
    @Slf4j
    public class SmsReceiver {
    
        @Resource
        private SmsService smsService;
    
        /**
         * 监听MQ中的消息
         * @param smsVo
         */
        @RabbitListener(bindings = @QueueBinding(
                value = @Queue(value = MQConst.QUEUE_SMS, durable = "true"), //消息队列,并持久化
                exchange = @Exchange(value = MQConst.EXCHANGE_DIRECT_SMS), //交换机
                key = {MQConst.ROUTING_SMS} //路由
        ))
        public void receive(SmsVo smsVo){
            log.info("SmsReceiver 监听器监听到消息......");
            smsService.send(smsVo);
        }
    }
    

    3、service-hosp中更新排班数量#

    3.1、引入依赖#

    
    <dependency>
        <groupId>com.atguigugroupId>
        <artifactId>rabbit-utilsartifactId>
        <version>1.0version>
    dependency>
    

    3.2、添加MQ配置#

    spring:
      rabbitmq:
        host: 192.168.100.101
        port: 5672
        username: guest
        password: guest
    

    3.3、封装MQ监听器#

    在监听器中更新排班数量:

    package com.atguigu.syt.hosp.receiver;
    
    @Component
    @Slf4j
    public class HospReceiver {
    
        @Resource
        private ScheduleService scheduleService;
    
        /**
         * 监听MQ中的消息
         * @param orderMqVo
         */
        @RabbitListener(bindings = @QueueBinding(
                value = @Queue(value = MQConst.QUEUE_ORDER, durable = "true"), //消息队列,并持久化
                exchange = @Exchange(value = MQConst.EXCHANGE_DIRECT_ORDER), //交换机
                key = {MQConst.ROUTING_ORDER} //路由
        ))
        public void receive(OrderMqVo orderMqVo){
            //修改排班信息
            log.info("HospReceiver 监听器监听到消息......");
            scheduleService.updateByOrderMqVo(orderMqVo);
        }
    }
    

    接口:ScheduleService

    /**
         * 更新排班数量
         * @param orderMqVo
         */
    void updateByOrderMqVo(OrderMqVo orderMqVo);
    

    实现:ScheduleServiceImpl

    @Override
    public void updateByOrderMqVo(OrderMqVo orderMqVo) {
        //下单成功更新预约数
        ObjectId objectId = new ObjectId(orderMqVo.getScheduleId());
        //id是objectId
        Schedule schedule = scheduleRepository.findById(objectId).get();
        //id是string
        //      Schedule schedule = scheduleRepository.findById(orderMqVo.getScheduleId()).get();
        schedule.setReservedNumber(orderMqVo.getReservedNumber());
        schedule.setAvailableNumber(orderMqVo.getAvailableNumber());
    
        //主键一致就是更新
        scheduleRepository.save(schedule);
    }
    

    4、完善service-orde订单接口#

    4.1、引入依赖#

    
    <dependency>
        <groupId>com.atguigugroupId>
        <artifactId>rabbit-utilsartifactId>
        <version>1.0version>
    dependency>
    

    4.2、添加MQ配置#

    spring:
      rabbitmq:
        host: 192.168.100.101
        port: 5672
        username: guest
        password: guest
    

    4.3、修改下单方法#

    OrderInfoServiceImpl类:

    @Resource
    private RabbitService rabbitService;
    

    submitOrder方法中添加发送消息的业务逻辑:

    //使用这两个数据更新平台端的最新的排班数量
    Integer reservedNumber = data.getInteger("reservedNumber");
    Integer availableNumber = data.getInteger("availableNumber");
    
    //目的1:更新mongodb数据库中的排班数量
    //组装数据同步消息对象
    OrderMqVo orderMqVo = new OrderMqVo();
    orderMqVo.setAvailableNumber(availableNumber);
    orderMqVo.setReservedNumber(reservedNumber);
    orderMqVo.setScheduleId(scheduleId);
    //发消息
    rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_ORDER, MQConst.ROUTING_ORDER, orderMqVo);
    
    //目的2:给就诊人发短信
    //组装短信消息对象
    SmsVo smsVo = new SmsVo();
    smsVo.setPhone(orderInfo.getPatientPhone());
    //亲爱的用户:您已预约%{hosname}%{hosdepname}%{date}就诊
    //请您于%{time}至%{address}取号,
    //您的就诊号码是%{number},请您及时就诊
    smsVo.setTemplateCode("和客服申请模板编号");
    Map paramsSms = new HashMap(){{
        put("hosname", orderInfo.getHosname());
        put("hosdepname", orderInfo.getDepname());
        put("date", new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd"));
        put("time", orderInfo.getFetchTime());
        put("address", orderInfo.getFetchAddress());
        put("number", orderInfo.getNumber());
    }};
    smsVo.setParam(paramsSms);
    //向MQ发消息
    rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_SMS, MQConst.ROUTING_SMS, smsVo);
    
    

    4.4、修改取消订单方法#

    cancelOrder方法中添加发送消息的业务逻辑:

     //获取返回数据
    JSONObject jsonObject = jsonResult.getJSONObject("data");
    Integer reservedNumber = jsonObject.getInteger("reservedNumber");
    Integer availableNumber = jsonObject.getInteger("availableNumber");
    
    //目的1:更新mongodb数据库中的排班数量
    //组装数据同步消息对象
    OrderMqVo orderMqVo = new OrderMqVo();
    orderMqVo.setAvailableNumber(availableNumber);
    orderMqVo.setReservedNumber(reservedNumber);
    orderMqVo.setScheduleId(orderInfo.getScheduleId());
    //发消息
    rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_ORDER, MQConst.ROUTING_ORDER, orderMqVo);
    
    //目的2:给就诊人发短信
    //组装短信消息对象
    SmsVo smsVo = new SmsVo();
    smsVo.setPhone(orderInfo.getPatientPhone());
    //亲爱的用户:您已取消%{hosname}%{hosdepname}%{date}就诊
    smsVo.setTemplateCode("和客服申请模板编号");
    Map paramsSms = new HashMap(){{
        put("hosname", orderInfo.getHosname());
        put("hosdepname", orderInfo.getDepname());
        put("date", new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd"));
    }};
    smsVo.setParam(paramsSms);
    //向MQ发消息
    rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_SMS, MQConst.ROUTING_SMS, smsVo);
    

    5、就诊人提醒#

    5.1、添加定时任务#

    在service-order微服务中添加定时任务:创建ScheduledTask类

    cron表达式参考:https://qqe2.com/cron

    package com.atguigu.syt.order.schedule;
    
    @Component
    @EnableScheduling  //开启定时任务
    @Slf4j
    public class ScheduledTask {
        /**
         * 测试
         * (cron="秒 分 时 日 月 周")
         * *:每隔一秒执行
         * 0/3:从第0秒开始,每隔3秒执行一次
         * 1-3: 从第1秒开始执行,到第3秒结束执行
         * 1,2,3:第1、2、3秒执行
         * ?:不指定,若指定日期,则不指定周,反之同理
         */
        @Scheduled(cron="0/3 * * * * ?")
        public void task1() {
            log.info("task1 执行");
        }
    }
    

    5.2、添加就诊人提醒定时任务#

    @Resource
    private OrderInfoService orderInfoService;
    
    @Scheduled(cron = "0 0 18 * * ?")
    public void patientAdviceTask(){
    
        log.info("执行定时任务");
        orderInfoService.patientAdvice();
    }
    

    5.3、Service#

    需求:就诊前一天晚六点向用户发送就医提醒短信

    接口:OrderInfoService

    /**
         * 就诊人提醒
         */
    void patientAdvice();
    

    实现:OrderInfoServiceImpl

    @Override
    public void patientAdvice() {
    
        //查询明天的预约信息
        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
    
        //明天
        String tomorrow = new DateTime().plusDays(1).toString("yyyy-MM-dd");
        queryWrapper.eq(OrderInfo::getReserveDate, tomorrow);
        //未取消
        queryWrapper.ne(OrderInfo::getOrderStatus, OrderStatusEnum.CANCLE.getStatus());
        List orderInfoList = baseMapper.selectList(queryWrapper);
    
        for (OrderInfo orderInfo : orderInfoList) {
            //短信对象
            SmsVo smsVo = new SmsVo();
            smsVo.setPhone(orderInfo.getPatientPhone());
            //就诊提醒:您已预约%{hosname}%{depname}的号源,就诊时间:%{date},就诊人%{name},请您合理安排出行时间
            smsVo.setTemplateCode("和客服申请模板编号");
            Map paramsSms = new HashMap(){{
                put("hosname", orderInfo.getHosname());
                put("hosdepname", orderInfo.getDepname());
                put("date", new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd"));
                put("name", orderInfo.getPatientName());
            }};
            smsVo.setParam(paramsSms);
            //发消息
            rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_SMS, MQConst.ROUTING_SMS, smsVo);
        }
    }
    

    源码:https://gitee.com/dengyaojava/guigu-syt-parent

  • 相关阅读:
    ClickHouse的数据类型
    黑号照妖镜API接口 淘宝旺旺信誉
    【基于pyAudioKits的Python音频信号处理(四)】傅里叶变换:从时域到频域
    浅谈Vue中的$nextTick方法
    香港科技大学广州|智能制造学域&机器人与自主系统学域博士招生宣讲会—中国科学技术大学专场
    工作常用sql 总结-长期更新
    【C语言】详细分析库函数qsort
    Codeforces Round #832 (Div. 2)「D 思维 + 异或前缀和 + 奇偶二分优化」
    知识点滴 - 什么是开放数据
    ceph文件系统
  • 原文地址:https://www.cnblogs.com/deyo/p/17502063.html