苍穹外卖个人技术总结Day06
1.Spring task1.1介绍1SpringTask是Spring框架提供的一个任务调度工具它可以按照所约定的时间定时自动执行设定的某个代码块。2常见应用场景有定时批量处理订单银行贷款还款提醒等1.2 cron表达式1前言Spring Task作为一个定时处理任务的一个框架这个定时便是基于这个cron表达式来实现的2cron表达式就是一个字符串通过该表达式就可以定义该代码逻辑执行的时机。常见形式由6个或7个域组成每个域分别代表秒分钟小时日月周年可有可无。案例每年的1月1日0点0分 对应的cron表达式为(cron 0 0 0 1 1 ? * )cron表达式生成器在线Cron表达式生成器 (qqe2.com)1.3 定时处理订单状态实现1.3.1 需求分析用户下单后可能存在的情况1下单后未支付订单一直处于待支付状态2用户收货后管理端未点击完成按钮订单一直处于派送中状态对于上面两种情况我们不可能随时随地及时处理所以需要通过定时任务来处理让程序自动修改订单状态此处采用以下处理方案1每分钟检查一次是否存在支付超时订单如果存在则修改订单状态为“已取消”2每天凌晨1点检查一次是否存在“派送中”订单存在则修改订单状态为“已完成”1.3.2 代码实现1开启权限在skyApplication类中开启任务调度权限打上EnableScheduling注解2定义专门的定时任务类在sky-server包下定义一个task包在该包下定义Mytask类专门处理定时任务逻辑3编写定时任务类的方法处理支付超时订单AutowiredprivateOrderMapperorderMapper;/** * 处理超时订单 */Scheduled(cron0 * * * * ?)//每分钟执行一次publicvoidprocessTimeOutOrder(){//查看数据库去查订单状态为待支付的订单数据如果超时15分钟就自动取消订单并设置订单取消原因IntegerpayStatusOrders.PENDING_PAYMENT;//订单状态为待付款LocalDateTimenowLocalDateTime.now().plusMinutes(-15);//订单支付时间超过15分钟log.info(超时时间{},now);ListOrdersoutTimeOrdersListorderMapper.getOutTimeOrders(payStatus,now);if(outTimeOrdersList!nulloutTimeOrdersList.size()0){for(Ordersorders:outTimeOrdersList){orders.setStatus(Orders.CANCELLED);orders.setCancelTime(LocalDateTime.now());orders.setCancelReason(订单超时取消);orderMapper.update(orders);}}}处理派送中订单/** * 处理配送中订单 */Scheduled(cron0 0 1 * * ?)//每天凌晨一点统一处理publicvoidprocessDeliveryOrder(){IntegerdeliveryStatusOrders.DELIVERY_IN_PROGRESS;LocalDateTimenowLocalDateTime.now().plusHours(-1);log.info(待配送时间{},now);ListOrdersoutTimeOrdersListorderMapper.getOutTimeOrders(deliveryStatus,now);if(outTimeOrdersList!nulloutTimeOrdersList.size()0){for(Ordersorders:outTimeOrdersList){orders.setStatus(Orders.COMPLETED);orders.setDeliveryTime(LocalDateTime.now());orderMapper.update(orders);}}}在OrderMapper接口中添加查询方法/** * 根据状态和时间查询订单 */Select(select * from orders where status #{status} and order_time #{time})ListOrdersgetOutTimeOrders(Integerstatus,LocalDateTimetime);2.WebSocket2.1 介绍WebSocket是基于TCP的一种新的网络协议可以只通过完成一次握手就实现服务器和客户端的双向数据传输建立长连接。提问前期该项目的功能实现都是基于Http协议实现的为什么此处还要用到WebSocket这个新协议解释1Http协议是支持单向通信即客户端发起一次请求服务器就响应一次请求是基于请求响应模式的并且由于Http协议的特性客户端每次的请求都是独立的即新的请求是不具备上一次请求的信息的服务器和客户端是建立了短连接。这就会导致在一些需要客户端和服务器实时共享信息的情况下基于Http协议不能很好的实现所以这时候就需要基于WebSocket协议。2Websocket协议是支持双向通信的即客户端与服务器完成一次握手后客户端可以向服务器发消息的同时服务器也可以向客户端主动发消息就不必基于请求响应模式。服务器和客户端是建立了长连接在视频弹幕或者体育赛事更新等需要实时更新信息的场景下就需要WebSocket。2.2 环境配置1导入Maven坐标在sky-server包下的pom.xml中已定义dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-websocket/artifactId/dependency2定义WebSocket的配置类/** * WebSocket配置类用于注册WebSocket的Bean */ConfigurationpublicclassWebSocketConfiguration{BeanpublicServerEndpointExporterserverEndpointExporter(){returnnewServerEndpointExporter();}}3定义WebSocket的服务端组件在sky-server包下创建WebSocket包定义专门的WebSocket类再在类中定义相关代码/** * WebSocket服务 */ComponentServerEndpoint(/ws/{sid})publicclassWebSocketServer{//存放会话对象privatestaticMapString,SessionsessionMapnewHashMap();/** * 连接建立成功调用的方法 */OnOpenpublicvoidonOpen(Sessionsession,PathParam(sid)Stringsid){System.out.println(客户端sid建立连接);sessionMap.put(sid,session);}/** * 收到客户端消息后调用的方法 * * param message 客户端发送过来的消息 */OnMessagepublicvoidonMessage(Stringmessage,PathParam(sid)Stringsid){System.out.println(收到来自客户端sid的信息:message);}/** * 连接关闭调用的方法 * * param sid */OnClosepublicvoidonClose(PathParam(sid)Stringsid){System.out.println(连接断开:sid);sessionMap.remove(sid);}/** * 群发 * * param message */publicvoidsendToAllClient(Stringmessage){CollectionSessionsessionssessionMap.values();for(Sessionsession:sessions){try{//服务器向客户端发送消息session.getBasicRemote().sendText(message);}catch(Exceptione){e.printStackTrace();}}}}2.3来电提醒2.3.1 需求分析用户在小程序端提交订单并支付成功后需要提醒商家有新的订单要处理此处是通过WebSocket将服务器和客户端建立连接后由服务器向客户端推送信息然后由客户端解析后产生相对应的语音播报和提示框提示。2.3.2 代码实现在OrderServiceImpl中注入WebSocketServer对象此处因为是订单提醒所以直接在OrderServiceImpl中的paySuccess方法中进行修改添加下列代码//根据websocket向商家推送订单信息MapString,ObjectmapnewHashMap();map.put(type,1);//1表示订单提示2表示催单map.put(orderId,orders.getId());map.put(content,订单号ordersDB.getNumber());log.info(向商家推送订单信息{},map);webSocketServer.sendToAllClient(JSON.toJSONString(map));2.4 客户催单2.4.1 需求分析用户在小程序端点击催单按钮后需要提醒商家有订单要及时处理此处还是通过WebSocket将服务器和客户端建立连接后由服务器向客户端推送信息然后由客户端解析后产生相对应的语音播报和提示框提示。2.4.2 代码实现1用户端的OrderController层新增催单方法remindOrder/** * 用户催单 * param id * return */GetMapping(/reminder/{id})ApiOperation(用户催单)publicResultremindOrder(PathVariableLongid){orderService.remindOrder(id);returnResult.success();}2OrderService层/** * 催单 * param id */voidremindOrder(Longid);}3OrderServiceImpl层/** * 催单 * param id */OverridepublicvoidremindOrder(Longid){OrdersordersorderMapper.getDetail(id);//判断订单状态是否为待接单是就可以催单不是则不能if(orders.getStatus().equals(Orders.TO_BE_CONFIRMED)){//根据websocket向商家催单MapString,ObjectmapnewHashMap();map.put(type,2);//1表示订单提示2表示催单map.put(orderId,id);map.put(content,订单号orders.getNumber());log.info(向商家推送订单信息{},map);webSocketServer.sendToAllClient(JSON.toJSONString(map));}}4OrderMapper层/** * 根据id查询订单详情 * param id */Select(select * from orders where id #{id})OrdersgetDetail(Longid);