1、定义延时订单类
定义唯一的订单编号、活动ID、超时时间、类型等你业务可能需要用到的字段
类需要实现Delayed, Serializable 接口
@Data
public class TaskDelayedVo implements Delayed, Serializable {
/**
* 任务id
*/
private String orderNo;
/**
* 活动id
*/
private Long activityId;
/**
* 任务状态
*/
private String taskStates;
/**
* 订单过期时间(毫秒)
*/
private long expTime;
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
// return Long.valueOf(this.expTime).compareTo(Long.valueOf(((OrderDelayedVo) o).expTime));
return Long.compare(this.expTime, ((TaskDelayedVo) o).expTime);
}
/**
* 重写用于删除
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
TaskDelayedVo order = (TaskDelayedVo) obj;
if (order.getOrderNo() == null) {
return false;
} else return orderNo.equals(order.getOrderNo());
}
}
2、处理超时订单实现类
@Slf4j
@Service("taskDelayService")
public class TaskDelayService implements InitializingBean {
@Resource
private PrPraiseTaskService prPraiseTaskService;
private static final DelayQueue<TaskDelayedVo> delayQueue = new DelayQueue<>();
private static final AtomicBoolean start = new AtomicBoolean(false);
// 线程池
private static final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("taskDelay-pool-%d").build();
private static final ExecutorService executorService = new ThreadPoolExecutor(
3, // 核心线程池大小
6, // 最大线程池大小
60L, // 线程最大空闲时间
TimeUnit.MILLISECONDS, // 时间单位(毫秒)
new LinkedBlockingQueue<>(), // 线程等待队列
threadFactory, // 线程创建工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
@Override
public void afterPropertiesSet() throws Exception {
start();
}
public <T> void start() {
if (start.get()) {
return;
}
start.set(true);
// 处理已超时任务
List<PrPraiseTask> dealList = prPraiseTaskService.dealOverTimeTask();
if (!CollectionUtils.isEmpty(dealList)) {
for (PrPraiseTask prPraiseTask : dealList) {
// 设置任务超时
boolean result = prPraiseTaskService.setTaskOverTime(prPraiseTask.getId(),prPraiseTask.getActivityId());
log.info("关闭店铺{}超时订单====>> 订单号:{},是否调用成功:{}",prPraiseTask.getShopName(), prPraiseTask.getOrderNo(),result);
}
log.info("已处理超时订单共{}条", dealList.size());
}
// 查询未超时订单 放入队列
List<PrPraiseTask> taskList = prPraiseTaskService.getNotOverTimeTask();
if (!CollectionUtils.isEmpty(taskList)) {
List<TaskDelayedVo> list = new ArrayList<>();
for (PrPraiseTask prPraiseTask : taskList) {
TaskDelayedVo vo = new TaskDelayedVo();
vo.setOrderNo(prPraiseTask.getOrderNo());
vo.setTaskStates(prPraiseTask.getTaskStates());// drawOrder
vo.setExpTime(prPraiseTask.getCreateTime().getTime() + (30 * 60 * 1000));// 超时时间:30分钟后
// vo.setExpTime(prPraiseTask.getCreateTime().getTime() + (5 * 1000));// 超时时间:5秒
list.add(vo);
}
log.info("加入超时订单队列,共{}条。", list.size());
delayQueue.addAll(list);
}
executorService.submit(() -> {
// 取消订单
while (start.get()) {
try {
TaskDelayedVo order = delayQueue.take();
boolean flag = false;
// 处理订单过期
if (order.getTaskStates().equals("drawOrder")){// 领取任务状态
//业务逻辑
flag = prPraiseTaskService.setTaskOverTime(valueOf(order.getOrderNo()),order.getActivityId());
} else if (order.getTaskStates().equals("verifyOrder")){// 凭证审核通过
// 业务逻辑
........
}
if (flag) {
log.info("超时订单延时队列====>>> 【通知{}关闭超时订单】====>> 订单号:{},是否成功调用:{}", order.getOrderNo(),order.getOrderNo(), flag);
} else {
log.info("超时订单延时队列====>>> 订单号为[{}],是否执行了关闭操作:{}。", order.getOrderNo(), flag);
}
} catch (Exception e) {
e.printStackTrace();
log.info("超时订单延时队列====>>> 处理队列出现异常.");
}
}
});
}
public void add(TaskDelayedVo order) {
if (!start.get()) {
throw new DollycatException(DollyResultCode.ORDER_SERVER_NOT_START);
}
log.info("加入超时订单队列,订单号:{}", order.getOrderNo());
delayQueue.add(order);
}
public boolean delete(String orderNo) {
if (!start.get()) {
throw new DollycatException(DollyResultCode.ORDER_SERVER_NOT_START);
}
log.info("移除超时订单队列,订单号:{}", orderNo);
return delayQueue.remove(orderNo);
}
public int getSize() {
if (!start.get()) {
throw new DollycatException(DollyResultCode.ORDER_SERVER_NOT_START);
}
return delayQueue.size();
}
}
这里就是处理超时后的逻辑,对应上面代码领取任务状态过去的处理逻辑。
@Service("prPraiseTaskService")
public class PrPraiseTaskServiceImpl implements PrPraiseTaskService {
@Resource
private PrTempRemainDailyCountDao prTempRemainDailyCountDao;
@Resource
private PrPraiseTaskDao prPraiseTaskDao;
/**
* 根据任务id设置任务超时
* @param id 任务id
* @param activityId 活动id
* @return boolean
*/
@Override
public boolean setTaskOverTime(Long id,Long activityId) {
PrPraiseTask prPraiseTask = prPraiseTaskDao.queryById(id);
boolean flag = prPraiseTaskDao.setTaskOverTimeById(id) > 0;
if (flag) {
// 任务创建日期(yyyy-MM-dd)
Date taskCreateTime = DateUtil.parse(DateUtil.format(prPraiseTask.getCreateTime(), DateUtils.ISO_DATE_FORMAT));
// 当前日期(yyyy-MM-dd)
Date nowDate = DateUtil.parse(DateUtil.format(DateUtil.parse(DateUtil.now()), DateUtils.ISO_DATE_FORMAT));
// 任务的创建时间必须等于当天任务释放才回赠一个活动数量
AssertUtils.isTrue(0 == DateUtil.compare(taskCreateTime, nowDate),
prPraiseTask,
cus -> prTempRemainDailyCountDao.releaseCountBack(activityId));
}
return flag;
}
}
3、在创建订单或任务的方法中加入延时队列
@Service("prPraiseTaskService")
public class PrPraiseTaskServiceImpl implements PrPraiseTaskService {
@Resource
private TaskDelayService taskDelayService;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean receiveTaskByNumberId(Long id) {
// 业务逻辑
.........
// 延时队列
TaskDelayedVo taskDelayedVo = new TaskDelayedVo();
taskDelayedVo.setOrderNo(prPraiseTask.getId().toString());// 刚创建任务的id
taskDelayedVo.setActivityId(prPraiseTask.getActivityId());// 该活动的id,用于释放时去回增余量
taskDelayedVo.setTaskStates(prPraiseTask.getTaskStates());// 默认状态 drawOrder
// taskDelayedVo.setExpTime(prPraiseTask.getCreateTime().getTime() + (30 * 60 * 1000));// 创建时间
taskDelayedVo.setExpTime(prPraiseTask.getCreateTime().getTime() + (5 * 1000));// 创建时间,这里为了方便测试我就只设置了5秒
taskDelayService.add(taskDelayedVo);
}