zhaowenxuan
2024-10-16 ef998cdc3655a6640df84c36b5da332ff901e54d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package com.ltkj.web.config.redis;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ltkj.common.core.domain.AjaxResult;
import com.ltkj.hosp.domain.TjReservation;
import com.ltkj.hosp.service.ITbTransitionService;
import com.ltkj.hosp.service.ITjReservationService;
import com.ltkj.mall.domain.MallOrder;
import com.ltkj.mall.domain.MallTimeConfig;
import com.ltkj.mall.mallOrderUtils.OrderHandleOption;
import com.ltkj.mall.mallOrderUtils.OrderUtil;
import com.ltkj.mall.service.IMallOrderService;
import com.ltkj.mall.service.IMallTimeConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
 
import javax.annotation.Resource;
import java.util.Date;
import java.util.Set;
 
@Component
@Slf4j
public class OrderDelayService  implements InitializingBean {
  //redis zset key
  public static final String ORDER_DELAY_TASK_KEY = "delaytask:order";
  public static final String RESERVATION_DELAY_TASK_KEY = "delaytask:reservationId";
 
  @Resource
  private StringRedisTemplate stringRedisTemplate;
  @Resource
  private ITjReservationService reservationService;
  @Resource
  private ITbTransitionService transitionService;
  @Autowired
  private IMallTimeConfigService mallTimeConfigService;
 
  @Autowired
  private IMallOrderService orderService;
 
  //生成订单-order为订单信息,可以是订单流水号,用于延时任务达到时效后关闭订单
  public void produce(String orderId){
    stringRedisTemplate.opsForZSet().add(
            ORDER_DELAY_TASK_KEY,     // redis key
            orderId,    // zset  member
            //30分钟延时
            System.currentTimeMillis() + (30 * 60 * 1000)    //zset score
//            System.currentTimeMillis() + (10 * 1000)    //zset score
    );
  }
 
 
  //预约超时
  public void reservation(String reservationId){
 
    TjReservation tjReservation = reservationService.getById(reservationId);
    if(null !=tjReservation){
      stringRedisTemplate.opsForZSet().add(
              RESERVATION_DELAY_TASK_KEY,     // redis key
              reservationId,    // zset  member
              //预约时间的 晚上7点过时
              tjReservation.getReservationTime().getTime() + (60 * 60 * 1000 * 19)    //zset score
      );
    }
  }
 
  //延时任务,也是异步任务,延时任务达到时效之后关闭订单,并将延时任务从redis zset删除
  @Async("async")
  public void consuming(){
 
    //订单
      Set<ZSetOperations.TypedTuple<String>> orderSerialNos = stringRedisTemplate.opsForZSet().rangeByScoreWithScores(
              ORDER_DELAY_TASK_KEY,
              0,  //延时任务score最小值
              System.currentTimeMillis() //延时任务score最大值(当前时间)
      );
      if (!CollectionUtils.isEmpty(orderSerialNos)) {
        for (ZSetOperations.TypedTuple<String> orderId : orderSerialNos) {
          //这里根据orderSerialNo去检查用户是否完成了订单支付
          //如果用户没有支付订单,去执行订单关闭的操作
 
          MallOrder order = orderService.getById(orderId.getValue());
          if (order == null) {
            log.info("订单" + orderId.getValue() + "不存在 被自动关闭");
            stringRedisTemplate.opsForZSet().remove(ORDER_DELAY_TASK_KEY, orderId.getValue());
          }else {
            // 检测是否能够取消
            OrderHandleOption handleOption = OrderUtil.build(order);
            if (!handleOption.isCancel()) {
              log.info("订单" + orderId.getValue() + "不能取消 被自动关闭");
              stringRedisTemplate.opsForZSet().remove(ORDER_DELAY_TASK_KEY, orderId.getValue());
            }else {
              // 设置订单已取消状态
              order.setOrderStatus(OrderUtil.STATUS_CANCEL.longValue());
              order.setCloseTime(new Date());
              order.setUpdateTime(new Date());
              orderService.updateById(order);
              //对应预约时间数量+1
              final TjReservation byId = reservationService.getById(order.getReservationId());
              final Date reservationTime = byId.getReservationTime();
              LambdaQueryWrapper<MallTimeConfig> wq=new LambdaQueryWrapper<>();
              wq.eq(MallTimeConfig::getTime,reservationTime);
              final MallTimeConfig one = mallTimeConfigService.getOne(wq);
              one.setNowNum(one.getNowNum()+1);
              mallTimeConfigService.updateById(one);
              byId.setIsExpire(1);
              reservationService.updateById(byId);
              transitionService.deletedTbTransitionListByCusIdAndTjNum(byId.getIdCard(),byId.getCardId());
            }
          }
          //订单关闭之后,将订单延时任务从队列中删除
          stringRedisTemplate.opsForZSet().remove(ORDER_DELAY_TASK_KEY, orderId.getValue());
        }
      }
 
 
    //预约超时
    Set<ZSetOperations.TypedTuple<String>> reservation = stringRedisTemplate.opsForZSet().rangeByScoreWithScores(
            RESERVATION_DELAY_TASK_KEY,
            0,  //延时任务score最小值
            System.currentTimeMillis() //延时任务score最大值(当前时间)
    );
    if (!CollectionUtils.isEmpty(reservation)) {
      for (ZSetOperations.TypedTuple<String> reservationId : reservation) {
        TjReservation tjReservation = reservationService.getById(reservationId.getValue());
        if(null==tjReservation) continue;
        if(tjReservation.getIsExpire()==2){
          //对应预约时间数量+1
          LambdaQueryWrapper<MallTimeConfig> wq=new LambdaQueryWrapper<>();
          wq.eq(MallTimeConfig::getTime,tjReservation.getReservationTime());
          final MallTimeConfig one = mallTimeConfigService.getOne(wq);
          one.setNowNum(one.getNowNum()+1);
          mallTimeConfigService.updateById(one);
          tjReservation.setIsExpire(1);
          reservationService.updateById(tjReservation);
          transitionService.deletedTbTransitionByCusId(tjReservation.getIdCard());
        }
        //关闭之后,将任务从队列中删除
        stringRedisTemplate.opsForZSet().remove(RESERVATION_DELAY_TASK_KEY, reservationId.getValue());
      }
    }
  }
 
  //该类对象Bean实例化之后,就开启while扫描任务
  @Override
  public void afterPropertiesSet() throws Exception {
    new Thread(() -> {  //开启新的线程,否则SpringBoot应用初始化无法启动
      while(true){
        try {
          Thread.sleep(3 * 1000);   //每5秒扫描一次redis库获取延时数据,不用太频繁没必要
//          log.info("我执行了一次~~~~~~");
        } catch (InterruptedException e) {
          e.printStackTrace();  //本文只是示例,生产环境请做好相关的异常处理
        }
        consuming();
      }
    }).start();
  }
}