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

页面预览

绑定手机号

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

未绑定手机号

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

已绑定手机号

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

第01章-短信发送

1、云市场-短信API

1.1、开通三网106短信

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

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

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

1.2、获取开发参数

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

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

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

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

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

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

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<String,Object> paramsMap = new HashMap<String, Object>(){{             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<String, String> 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<String, String> querys = new HashMap<>();         Map<String, String> 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<String, String> 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<UserInfo> 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>     <!--rabbitmq消息队列-->     <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-amqp</artifactId>     </dependency>     <dependency>         <groupId>org.projectlombok</groupId>         <artifactId>lombok</artifactId>     </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、引入依赖

<!--MQ--> <dependency>     <groupId>com.atguigu</groupId>     <artifactId>rabbit-utils</artifactId>     <version>1.0</version> </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、引入依赖

<!--MQ--> <dependency>     <groupId>com.atguigu</groupId>     <artifactId>rabbit-utils</artifactId>     <version>1.0</version> </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、引入依赖

<!--MQ--> <dependency>     <groupId>com.atguigu</groupId>     <artifactId>rabbit-utils</artifactId>     <version>1.0</version> </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<String,Object> paramsSms = new HashMap<String, Object>(){{     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<String,Object> paramsSms = new HashMap<String, Object>(){{     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<OrderInfo> 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<OrderInfo> orderInfoList = baseMapper.selectList(queryWrapper);      for (OrderInfo orderInfo : orderInfoList) {         //短信对象         SmsVo smsVo = new SmsVo();         smsVo.setPhone(orderInfo.getPatientPhone());         //就诊提醒:您已预约%{hosname}%{depname}的号源,就诊时间:%{date},就诊人%{name},请您合理安排出行时间         smsVo.setTemplateCode("和客服申请模板编号");         Map<String,Object> paramsSms = new HashMap<String, Object>(){{             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

发表评论

评论已关闭。

相关文章