最近在备考六级,今天补上。
@SpringBootApplication
@Slf4j
@EnableTransactionManagement // 开启注解方式的事务管理
@EnableScheduling // 开启注解方式的定时管理
@EnableCaching // 开启注解方式的redis缓存
public class SkyApplication {
public static void main(String[] args) {
SpringApplication.run(SkyApplication.class, args);
log.info("后台服务已启动...");
}
}
task相关代码:
@Component
@Slf4j
public class OrderTask {
@Autowired
private OrderMapper orderMapper;
private static final String AUTOCANCEL = "订单已取消";
/**
* 自动取消待支付的订单 40分钟后自动删除
* 每分钟触发一次
*/
@Scheduled(cron = "0 * * * * ?")
public void processTimeoutOrder() {
log.info("定时处理超时订单:{}", LocalDateTime.now());
// 我这里就设置超过40分钟就自动取消
LocalDateTime time = LocalDateTime.now().plusMinutes(-40);
// 从数据库里获得待支付的订单 目的是寻找时间间隔 >= 40分钟的订单
List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time);
if (ordersList != null && ordersList.size() > 0) {
for(Orders orders : ordersList) {
orders.setStatus(Orders.CANCELLED); // 修改为已取消
orders.setCancelReason(AUTOCANCEL); // 修改取消原因 定义为常量 优雅的代码风格
orders.setCancelTime(LocalDateTime.now()); // 修改取消时间
orderMapper.update(orders);
}
}
}
/**
* 自动设置派送中的订单为已完成
* 在每天凌晨1点执行
*/
@Scheduled(cron = "0 0 1 * * ?")
public void processDeliveryOrder() {
log.info("定时处理处于派送中的订单:{}", LocalDateTime.now());
// 派送时间超过 >= 60分钟的订单
LocalDateTime time = LocalDateTime.now().plusMinutes(-60);
List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, time);
if (ordersList != null && ordersList.size() > 0) {
for(Orders orders : ordersList) {
orders.setStatus(Orders.COMPLETED); // 设置为已完成
orderMapper.update(orders);
}
}
}
}
不同:
HTTP是短连接,意思是客户端向服务端发送一次请求就是一次连接,服务器响应完毕后这个连接就消失了。
WebSocket是长连接。
HTTP通信是单向的,基于请求响应模式。
WebSoceket支持双向通信,意思是客户端可以向服务端及时发送消息,服务端也可以。
相同:都是TCP连接。
xml:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
<version>3.2.0version>
dependency>
代码如下:
// websocket配置类
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
通过websocket快速建立服务端与客户端的双向连接,需要把它放在springioc容器里面,方便进行依赖注入、生命周期管理、利用注解进行标记等操作。注意如果浏览器没有声音记得打开音频权限。
代码如下:
@Component
public class WebSocketTask {
@Autowired
private WebSocketServer webSocketServer;
/**
* 通过WebSocket每隔30秒向客户端发送消息
*/
@Scheduled(cron = "0/30 * * * * ?")
public void sendMessageToClient() {
webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));
}
}
这一个群发操作基本上就是固定的代码了,通过遍历session向客户端发送消息。
向我们的后台管理系统进行弹窗,上传json。
代码如下:
/**
* WebSocket服务
*/
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
//存放会话对象
private static Map<String, Session> sessionMap = new HashMap();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
System.out.println("客户端:" + sid + "建立连接");
sessionMap.put(sid, session);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}
/**
* 连接关闭调用的方法
*
* @param sid
*/
@OnClose
public void onClose(@PathParam("sid") String sid) {
System.out.println("连接断开:" + sid);
sessionMap.remove(sid);
}
/**
* 群发
*
* @param message
*/
public void sendToAllClient(String message) {
Collection<Session> sessions = sessionMap.values();
for (Session session : sessions) {
try {
// 服务器向客户端发送消息
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
由于跳过了微信支付,所以我们直接写在payment这个方法里。通过用户支付成功后,服务端生成一个json,通过websocket协议向后台管理客户端发送json信息,从而实现及时通讯,及时提醒的操作。
代码如下:
/**
* 订单支付
* @param ordersPaymentDTO
* @return
*/
@Transactional
public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
// TODO:注释的功能就是没跳过微信支付验证
// 1.当前登录用户id
Long userId = BaseContext.getCurrentId();
User user = userMapper.getById(userId);
// 2.调用微信支付接口,生成预支付交易单
/** JSONObject jsonObject = weChatPayUtil.pay(
ordersPaymentDTO.getOrderNumber(), //商户订单号
new BigDecimal(0.01), //支付金额,单位 元
"苍穹外卖订单", //商品描述
user.getOpenid() //微信用户的openid
);
**/
/** if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
throw new OrderBusinessException("该订单已支付");
}
**/
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", "ORDERPAID");
OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
vo.setPackageStr(jsonObject.getString("package"));
// 为替代微信支付成功后的数据库订单状态更新 多定义一个方法进行修改
Integer OrderPaidStatus = Orders.PAID; // 已支付
Integer OrderStatus = Orders.TO_BE_CONFIRMED; // 待接单
// 发现没有将支付时间check_out属性赋值 所以在这里更新
LocalDateTime check_out_time = LocalDateTime.now();
// 获取订单号码
String orderNumber = ordersPaymentDTO.getOrderNumber();
// 操作
orderMapper.updateStatus(OrderStatus,OrderPaidStatus,check_out_time,orderNumber);
// 3.来单提醒
//通过websocket向客户端浏览器推送消息 type orderId content
Map map = new HashMap();
map.put("type",1);
map.put("orderId",this.orders.getId());
map.put("content","订单号:"+this.orders.getNumber());
String json = JSON.toJSONString(map);
webSocketServer.sendToAllClient(json);
System.out.println(json);
return vo;
}
代码如下:
/**
* 客户催单
*/
@GetMapping("/reminder/{id}")
@ApiOperation("用户催单")
public Result reminder(@PathVariable("id") Long id){
log.info("用户催单,{}",id);
orderService.reminder(id);
return Result.success();
}
记得向接口声明该方法。
代码如下:
/**
* 用户催单
* @param id
*/
@Override
public void reminder(Long id) {
// 1.先查询该订单是否存在
Orders ordersDB = orderMapper.getById(id);
if (ordersDB == null) {
throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR); // 状态错误
}
// 2.封装json传至服务端 再通过服务端传至后台管理客户端
Map map = new HashMap();
map.put("type", 2); // 1是提醒 2是催单
map.put("orderId", id);
map.put("content","订单号:" + ordersDB.getNumber());
// 3.向后台管理客户端推送催单提醒
webSocketServer.sendToAllClient(JSON.toJSONString(map));
}
SpringTask是一个定时任务的小框架,能够实现快速定时任务开发,websocket是一种基于tcp的双工通信协议,能够实现客户端与服务端的长连接以及及时通讯。