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

打印 上一主题 下一主题

主题 892|帖子 892|积分 2676

页面预览

绑定手机号


未绑定手机号


已绑定手机号


第01章-短信发送

1、云市场-短信API

1.1、开通三网106短信

在阿里云云市场搜索“短信”,一般都可用,选择一个即可,例如如下:点击“立即购买”开通
这里开通的是【短信验证码- 快速报备签名】

1.2、获取开发参数

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

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

将工具类放入service-yun微服务的utils包中:资料:资料>短信发送>工具类
参考如下例子,复制代码在test目录进行测试

2、发送短息

2.1、Controller

创建FrontSmsController
  1. package com.atguigu.syt.yun.controller.front;
  2. @Api(tags = "短信接口")
  3. @RestController
  4. @RequestMapping("/front/yun/sms")
  5. public class FrontSmsController {
  6.     @Resource
  7.     private SmsService smsService;
  8.     @Resource
  9.     private RedisTemplate redisTemplate;
  10.     @ApiOperation("发送短信")
  11.     @ApiImplicitParam(name = "phone",value = "手机号")
  12.     @PostMapping("/send/{phone}")
  13.     public Result send(@PathVariable String phone) {
  14.         //生成验证码
  15.         int minutes = 5; //验证码5分钟有效
  16.         String code = RandomUtil.getFourBitRandom();
  17.         //创建短信发送对象
  18.         SmsVo smsVo = new SmsVo();
  19.         smsVo.setPhone(phone);
  20.         smsVo.setTemplateCode("CST_qozfh101");
  21.         Map<String,Object> paramsMap = new HashMap<String, Object>(){{
  22.             put("code", code);
  23.             put("expire_at", 5);
  24.         }};
  25.         smsVo.setParam(paramsMap);
  26.         //发送短信
  27.         smsService.send(smsVo);
  28.         //验证码存入redis
  29.         redisTemplate.opsForValue().set("code:" + phone, code, minutes, TimeUnit.MINUTES);
  30.         return Result.ok().message("短信发送成功");
  31.     }
  32. }
复制代码
2.2、Service

接口:SmsService
  1. package com.atguigu.syt.yun.service;
  2. public interface SmsService {
  3.     /**
  4.      * 发送短信
  5.      * @param smsVo
  6.      */
  7.     void send(SmsVo smsVo);
  8. }
复制代码
实现:SmsServiceImpl
  1. package com.atguigu.syt.yun.service.impl;
  2. @Service
  3. @Slf4j
  4. public class SmsServiceImpl implements SmsService {
  5.     @Override
  6.     public void send(SmsVo smsVo) {
  7.         String host = "https://dfsns.market.alicloudapi.com";
  8.         String path = "/data/send_sms";
  9.         String method = "POST";
  10.         String appcode = "你的appcode";
  11.         Map<String, String> headers = new HashMap<>();
  12.         //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
  13.         headers.put("Authorization", "APPCODE " + appcode);
  14.         //根据API的要求,定义相对应的Content-Type
  15.         headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
  16.         Map<String, String> querys = new HashMap<>();
  17.         Map<String, String> bodys = new HashMap<>();
  18.         StringBuffer contentBuffer = new StringBuffer();
  19.         smsVo.getParam().entrySet().forEach( item -> {
  20.             contentBuffer.append(item.getKey()).append(":").append(item.getValue()).append(",");
  21.         });
  22.         String content = contentBuffer.substring(0, contentBuffer.length() - 1);
  23.         bodys.put("content", content);
  24.         bodys.put("phone_number", smsVo.getPhone());
  25.         bodys.put("template_id", smsVo.getTemplateCode());
  26.         try {
  27.             /**
  28.              * 重要提示如下:
  29.              * HttpUtils请从
  30.              * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
  31.              * 下载
  32.              *
  33.              * 相应的依赖请参照
  34.              * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
  35.              */
  36.             HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
  37.             //获取response的body
  38.             String data = EntityUtils.toString(response.getEntity());
  39.             HashMap<String, String> resultMap = JSONObject.parseObject(data, HashMap.class);
  40.             String status = resultMap.get("status");
  41.             if(!"OK".equals(status)){
  42.                 String reason = resultMap.get("reason");
  43.                 log.error("短信发送失败:status = " + status + ", reason = " + reason);
  44.                 throw new GuiguException(ResultCodeEnum.FAIL.getCode(), "短信发送失败");
  45.             }
  46.         } catch (Exception e) {
  47.             log.error(ExceptionUtils.getStackTrace(e));
  48.             throw new GuiguException(ResultCodeEnum.FAIL.getCode(), "短信发送失败");
  49.         }
  50.     }
  51. }
复制代码
3、绑定手机号

3.1、Controller

service-user微服务FrontUserInfoController中添加接口方法
  1. @ApiOperation("绑定手机号")
  2. @ApiImplicitParams({
  3.     @ApiImplicitParam(name = "phone",value = "手机号", required = true),
  4.     @ApiImplicitParam(name = "code",value = "验证码", required = true)})
  5. @PostMapping("/auth/bindPhone/{phone}/{code}")
  6. public Result bindPhone(@PathVariable String phone, @PathVariable String code, HttpServletRequest request, HttpServletResponse response) {
  7.     Long userId = authContextHolder.checkAuth(request, resposne);
  8.     userInfoService.bindPhone(userId, phone, code);
  9.     return Result.ok().message("绑定成功");
  10. }
复制代码
3.2、Service

接口:UserInfoService
  1. /**
  2.      * 绑定当前用户的手机号
  3.      * @param userId
  4.      * @param phone
  5.      * @param code
  6.      */
  7. void bindPhone(Long userId, String phone, String code);
复制代码
实现:UserInfoServiceImpl
  1. @Resource
  2. private RedisTemplate redisTemplate;
  3. @Override
  4. public void bindPhone(Long userId, String phone, String code) {
  5.     //校验参数
  6.     if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {
  7.         throw new GuiguException(ResultCodeEnum.PARAM_ERROR);
  8.     }
  9.     //校验验证码
  10.     String phoneCode = (String)redisTemplate.opsForValue().get("code:" + phone);
  11.     if(!code.equals(phoneCode)) {
  12.         throw new GuiguException(ResultCodeEnum.CODE_ERROR);
  13.     }
  14.     //根据手机号查找会员
  15.     LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
  16.     //手机号没有被其他人绑定过
  17.     queryWrapper.eq(UserInfo::getPhone, phone).ne(UserInfo::getId, userId);
  18.     UserInfo userInfo = baseMapper.selectOne(queryWrapper);
  19.     //手机号已存在
  20.     if(userInfo != null) {
  21.         throw new GuiguException(ResultCodeEnum.REGISTER_MOBILE_ERROR);
  22.     }
  23.     //设置绑定手机号
  24.     userInfo = new UserInfo();
  25.     userInfo.setId(userId);
  26.     userInfo.setPhone(phone);
  27.     baseMapper.updateById(userInfo);
  28. }
复制代码
4、前端整合

4.1、api

创建sms.js
  1. import request from '~/utils/request'
  2. export default {
  3.   sendCode(phone) {
  4.     return request({
  5.       url: `/front/yun/sms/send/${phone}`,
  6.       method: `post`
  7.     })
  8.   }
  9. }
复制代码
在userInfo.js中添加方法
  1. bindPhone(phone, code) {
  2.     return request({
  3.         url: `/front/user/userInfo/auth/bindPhone/${phone}/${code}`,
  4.         method: `post`
  5.     })
  6. },
复制代码
4.2、页面组件

复制页面到项目pages目录中
资料:资料>手机号绑定页面
第02章-引入MQ

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

1.1、安装RabbitMQ
  1. #拉取镜像
  2. docker pull rabbitmq:3.8-management
  3. #创建容器启动
  4. 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中引入依赖
  1. <dependencies>
  2.    
  3.     <dependency>
  4.         <groupId>org.springframework.boot</groupId>
  5.         <artifactId>spring-boot-starter-amqp</artifactId>
  6.     </dependency>
  7.     <dependency>
  8.         <groupId>org.projectlombok</groupId>
  9.         <artifactId>lombok</artifactId>
  10.     </dependency>
  11. </dependencies>
复制代码
1.5、创建RabbitService类

添加sendMessage方法
  1. package com.atguigu.syt.rabbit;
  2. @Service
  3. @Slf4j
  4. public class RabbitService {
  5.     @Resource
  6.     private RabbitTemplate rabbitTemplate;
  7.    
  8.     /**
  9.      *  发送消息
  10.      * @param exchange 交换机
  11.      * @param routingKey 路由
  12.      * @param message 消息
  13.      */
  14.     public boolean sendMessage(String exchange, String routingKey, Object message) {
  15.         log.info("发送消息...........");
  16.         rabbitTemplate.convertAndSend(exchange, routingKey, message);
  17.         return true;
  18.     }
  19. }
复制代码
1.6、配置MQ消息转换器
  1. package com.atguigu.syt.rabbit.config;
  2. @Configuration
  3. public class MQConfig {
  4.     @Bean
  5.     public MessageConverter messageConverter(){
  6.         //配置json字符串转换器,默认使用SimpleMessageConverter
  7.         return new Jackson2JsonMessageConverter();
  8.     }
  9. }
复制代码
1.7、添加常量类
  1. package com.atguigu.syt.rabbit.config;
  2. public class MQConst {
  3.     /**
  4.      * 预约/取消订单
  5.      */
  6.     public static final String EXCHANGE_DIRECT_ORDER = "exchange.direct.order";
  7.     public static final String ROUTING_ORDER = "routing.order";
  8.     public static final String QUEUE_ORDER  = "queue.order";
  9.     /**
  10.      * 短信
  11.      */
  12.     public static final String EXCHANGE_DIRECT_SMS = "exchange.direct.sms";
  13.     public static final String ROUTING_SMS = "routing.sms";
  14.     public static final String QUEUE_SMS  = "queue.sms";
  15. }
复制代码
2、service-yun中发送短信

2.1、引入依赖
  1. <dependency>
  2.     <groupId>com.atguigu</groupId>
  3.     <artifactId>rabbit-utils</artifactId>
  4.     <version>1.0</version>
  5. </dependency>
复制代码
2.2、添加MQ配置
  1. spring:
  2.   rabbitmq:
  3.     host: 192.168.100.101
  4.     port: 5672
  5.     username: guest
  6.     password: guest
复制代码
2.3、封装MQ监听器

在监听器中发送短信:
  1. package com.atguigu.syt.yun.receiver;
  2. @Component
  3. @Slf4j
  4. public class SmsReceiver {
  5.     @Resource
  6.     private SmsService smsService;
  7.     /**
  8.      * 监听MQ中的消息
  9.      * @param smsVo
  10.      */
  11.     @RabbitListener(bindings = @QueueBinding(
  12.             value = @Queue(value = MQConst.QUEUE_SMS, durable = "true"), //消息队列,并持久化
  13.             exchange = @Exchange(value = MQConst.EXCHANGE_DIRECT_SMS), //交换机
  14.             key = {MQConst.ROUTING_SMS} //路由
  15.     ))
  16.     public void receive(SmsVo smsVo){
  17.         log.info("SmsReceiver 监听器监听到消息......");
  18.         smsService.send(smsVo);
  19.     }
  20. }
复制代码
3、service-hosp中更新排班数量

3.1、引入依赖
  1. <dependency>
  2.     <groupId>com.atguigu</groupId>
  3.     <artifactId>rabbit-utils</artifactId>
  4.     <version>1.0</version>
  5. </dependency>
复制代码
3.2、添加MQ配置
  1. spring:
  2.   rabbitmq:
  3.     host: 192.168.100.101
  4.     port: 5672
  5.     username: guest
  6.     password: guest
复制代码
3.3、封装MQ监听器

在监听器中更新排班数量:
  1. package com.atguigu.syt.hosp.receiver;
  2. @Component
  3. @Slf4j
  4. public class HospReceiver {
  5.     @Resource
  6.     private ScheduleService scheduleService;
  7.     /**
  8.      * 监听MQ中的消息
  9.      * @param orderMqVo
  10.      */
  11.     @RabbitListener(bindings = @QueueBinding(
  12.             value = @Queue(value = MQConst.QUEUE_ORDER, durable = "true"), //消息队列,并持久化
  13.             exchange = @Exchange(value = MQConst.EXCHANGE_DIRECT_ORDER), //交换机
  14.             key = {MQConst.ROUTING_ORDER} //路由
  15.     ))
  16.     public void receive(OrderMqVo orderMqVo){
  17.         //修改排班信息
  18.         log.info("HospReceiver 监听器监听到消息......");
  19.         scheduleService.updateByOrderMqVo(orderMqVo);
  20.     }
  21. }
复制代码
接口:ScheduleService
  1. /**
  2.      * 更新排班数量
  3.      * @param orderMqVo
  4.      */
  5. void updateByOrderMqVo(OrderMqVo orderMqVo);
复制代码
实现:ScheduleServiceImpl
  1. @Override
  2. public void updateByOrderMqVo(OrderMqVo orderMqVo) {
  3.     //下单成功更新预约数
  4.     ObjectId objectId = new ObjectId(orderMqVo.getScheduleId());
  5.     //id是objectId
  6.     Schedule schedule = scheduleRepository.findById(objectId).get();
  7.     //id是string
  8.     //      Schedule schedule = scheduleRepository.findById(orderMqVo.getScheduleId()).get();
  9.     schedule.setReservedNumber(orderMqVo.getReservedNumber());
  10.     schedule.setAvailableNumber(orderMqVo.getAvailableNumber());
  11.     //主键一致就是更新
  12.     scheduleRepository.save(schedule);
  13. }
复制代码
4、完善service-orde订单接口

4.1、引入依赖
  1. <dependency>
  2.     <groupId>com.atguigu</groupId>
  3.     <artifactId>rabbit-utils</artifactId>
  4.     <version>1.0</version>
  5. </dependency>
复制代码
4.2、添加MQ配置
  1. spring:
  2.   rabbitmq:
  3.     host: 192.168.100.101
  4.     port: 5672
  5.     username: guest
  6.     password: guest
复制代码
4.3、修改下单方法

OrderInfoServiceImpl类:
  1. @Resource
  2. private RabbitService rabbitService;
复制代码
submitOrder方法中添加发送消息的业务逻辑:
  1. //使用这两个数据更新平台端的最新的排班数量
  2. Integer reservedNumber = data.getInteger("reservedNumber");
  3. Integer availableNumber = data.getInteger("availableNumber");
  4. //目的1:更新mongodb数据库中的排班数量
  5. //组装数据同步消息对象
  6. OrderMqVo orderMqVo = new OrderMqVo();
  7. orderMqVo.setAvailableNumber(availableNumber);
  8. orderMqVo.setReservedNumber(reservedNumber);
  9. orderMqVo.setScheduleId(scheduleId);
  10. //发消息
  11. rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_ORDER, MQConst.ROUTING_ORDER, orderMqVo);
  12. //目的2:给就诊人发短信
  13. //组装短信消息对象
  14. SmsVo smsVo = new SmsVo();
  15. smsVo.setPhone(orderInfo.getPatientPhone());
  16. //亲爱的用户:您已预约%{hosname}%{hosdepname}%{date}就诊
  17. //请您于%{time}至%{address}取号,
  18. //您的就诊号码是%{number},请您及时就诊
  19. smsVo.setTemplateCode("和客服申请模板编号");
  20. Map<String,Object> paramsSms = new HashMap<String, Object>(){{
  21.     put("hosname", orderInfo.getHosname());
  22.     put("hosdepname", orderInfo.getDepname());
  23.     put("date", new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd"));
  24.     put("time", orderInfo.getFetchTime());
  25.     put("address", orderInfo.getFetchAddress());
  26.     put("number", orderInfo.getNumber());
  27. }};
  28. smsVo.setParam(paramsSms);
  29. //向MQ发消息
  30. rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_SMS, MQConst.ROUTING_SMS, smsVo);
复制代码
4.4、修改取消订单方法

cancelOrder方法中添加发送消息的业务逻辑:
  1. //获取返回数据
  2. JSONObject jsonObject = jsonResult.getJSONObject("data");
  3. Integer reservedNumber = jsonObject.getInteger("reservedNumber");
  4. Integer availableNumber = jsonObject.getInteger("availableNumber");
  5. //目的1:更新mongodb数据库中的排班数量
  6. //组装数据同步消息对象
  7. OrderMqVo orderMqVo = new OrderMqVo();
  8. orderMqVo.setAvailableNumber(availableNumber);
  9. orderMqVo.setReservedNumber(reservedNumber);
  10. orderMqVo.setScheduleId(orderInfo.getScheduleId());
  11. //发消息
  12. rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_ORDER, MQConst.ROUTING_ORDER, orderMqVo);
  13. //目的2:给就诊人发短信
  14. //组装短信消息对象
  15. SmsVo smsVo = new SmsVo();
  16. smsVo.setPhone(orderInfo.getPatientPhone());
  17. //亲爱的用户:您已取消%{hosname}%{hosdepname}%{date}就诊
  18. smsVo.setTemplateCode("和客服申请模板编号");
  19. Map<String,Object> paramsSms = new HashMap<String, Object>(){{
  20.     put("hosname", orderInfo.getHosname());
  21.     put("hosdepname", orderInfo.getDepname());
  22.     put("date", new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd"));
  23. }};
  24. smsVo.setParam(paramsSms);
  25. //向MQ发消息
  26. rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_SMS, MQConst.ROUTING_SMS, smsVo);
复制代码
5、就诊人提醒

5.1、添加定时任务

在service-order微服务中添加定时任务:创建ScheduledTask类
cron表达式参考:https://qqe2.com/cron
  1. package com.atguigu.syt.order.schedule;
  2. @Component
  3. @EnableScheduling  //开启定时任务
  4. @Slf4j
  5. public class ScheduledTask {
  6.     /**
  7.      * 测试
  8.      * (cron="秒 分 时 日 月 周")
  9.      * *:每隔一秒执行
  10.      * 0/3:从第0秒开始,每隔3秒执行一次
  11.      * 1-3: 从第1秒开始执行,到第3秒结束执行
  12.      * 1,2,3:第1、2、3秒执行
  13.      * ?:不指定,若指定日期,则不指定周,反之同理
  14.      */
  15.     @Scheduled(cron="0/3 * * * * ?")
  16.     public void task1() {
  17.         log.info("task1 执行");
  18.     }
  19. }
复制代码
5.2、添加就诊人提醒定时任务
  1. @Resource
  2. private OrderInfoService orderInfoService;
  3. @Scheduled(cron = "0 0 18 * * ?")
  4. public void patientAdviceTask(){
  5.     log.info("执行定时任务");
  6.     orderInfoService.patientAdvice();
  7. }
复制代码
5.3、Service

需求:就诊前一天晚六点向用户发送就医提醒短信
接口:OrderInfoService
  1. /**
  2.      * 就诊人提醒
  3.      */
  4. void patientAdvice();
复制代码
实现:OrderInfoServiceImpl
  1. @Override
  2. public void patientAdvice() {
  3.     //查询明天的预约信息
  4.     LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
  5.     //明天
  6.     String tomorrow = new DateTime().plusDays(1).toString("yyyy-MM-dd");
  7.     queryWrapper.eq(OrderInfo::getReserveDate, tomorrow);
  8.     //未取消
  9.     queryWrapper.ne(OrderInfo::getOrderStatus, OrderStatusEnum.CANCLE.getStatus());
  10.     List<OrderInfo> orderInfoList = baseMapper.selectList(queryWrapper);
  11.     for (OrderInfo orderInfo : orderInfoList) {
  12.         //短信对象
  13.         SmsVo smsVo = new SmsVo();
  14.         smsVo.setPhone(orderInfo.getPatientPhone());
  15.         //就诊提醒:您已预约%{hosname}%{depname}的号源,就诊时间:%{date},就诊人%{name},请您合理安排出行时间
  16.         smsVo.setTemplateCode("和客服申请模板编号");
  17.         Map<String,Object> paramsSms = new HashMap<String, Object>(){{
  18.             put("hosname", orderInfo.getHosname());
  19.             put("hosdepname", orderInfo.getDepname());
  20.             put("date", new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd"));
  21.             put("name", orderInfo.getPatientName());
  22.         }};
  23.         smsVo.setParam(paramsSms);
  24.         //发消息
  25.         rabbitService.sendMessage(MQConst.EXCHANGE_DIRECT_SMS, MQConst.ROUTING_SMS, smsVo);
  26.     }
  27. }
复制代码
源码:https://gitee.com/dengyaojava/guigu-syt-parent

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

没腿的鸟

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表