網站中接入手機驗證碼和定時任務(含源碼)
在阿里云云市場搜索“短信”,一般都可用,選擇一個即可,例如如下:點擊“立即購買”開通
【資料圖】
這里開通的是【短信驗證碼- 快速報備簽名】
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 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@Slf4jpublic 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、Controllerservice-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
@Resourceprivate RedisTemplate redisTemplate;@Overridepublic 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目錄中
資料:資料>手機號綁定頁面
預約或取消預約成功后我們要 更新預約數以及 發送短信提醒,為了實現用戶下單和取消訂單的快速響應,這部分邏輯我們就交給MQ完成。
1、引入RabbitMQ1.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中引入依賴
org.springframework.boot spring-boot-starter-amqp org.projectlombok lombok
1.5、創建RabbitService類添加sendMessage方法
package com.atguigu.syt.rabbit;@Service@Slf4jpublic 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;@Configurationpublic 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、引入依賴 com.atguigu rabbit-utils 1.0
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@Slf4jpublic 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、引入依賴 com.atguigu rabbit-utils 1.0
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@Slf4jpublic 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
@Overridepublic 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、引入依賴 com.atguigu rabbit-utils 1.0
4.2、添加MQ配置spring: rabbitmq: host: 192.168.100.101 port: 5672 username: guest password: guest
4.3、修改下單方法OrderInfoServiceImpl類:
@Resourceprivate 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 //開啟定時任務@Slf4jpublic 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、添加就診人提醒定時任務@Resourceprivate OrderInfoService orderInfoService;@Scheduled(cron = "0 0 18 * * ?")public void patientAdviceTask(){ log.info("執行定時任務"); orderInfoService.patientAdvice();}
5.3、Service需求:就診前一天晚六點向用戶發送就醫提醒短信
接口:OrderInfoService
/** * 就診人提醒 */void patientAdvice();
實現:OrderInfoServiceImpl
@Overridepublic 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
標簽: