增加定时任务检查订单是否逾期
This commit is contained in:
@ -3,6 +3,7 @@ package com.sczx.order;
|
||||
import com.sczx.order.common.constant.SystemConstants;
|
||||
import com.sczx.order.utils.ComputerInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@ -12,6 +13,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.retry.annotation.EnableRetry;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -24,6 +26,8 @@ import java.io.IOException;
|
||||
@EnableTransactionManagement
|
||||
@EnableHystrix
|
||||
@MapperScan("com.sczx.order.mapper") // 扫描 Mapper 接口
|
||||
@EnableScheduling
|
||||
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
20
src/main/java/com/sczx/order/config/SchedulerConfig.java
Normal file
20
src/main/java/com/sczx/order/config/SchedulerConfig.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.sczx.order.config;
|
||||
|
||||
import net.javacrumbs.shedlock.core.LockProvider;
|
||||
import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider;
|
||||
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
|
||||
public class SchedulerConfig {
|
||||
|
||||
@Bean
|
||||
public LockProvider lockProvider(RedisConnectionFactory connectionFactory) {
|
||||
return new RedisLockProvider(connectionFactory);
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,6 @@ package com.sczx.order.service;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.sczx.order.dto.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public interface OrderService {
|
||||
|
||||
/**
|
||||
@ -45,28 +42,6 @@ public interface OrderService {
|
||||
*/
|
||||
OrderDetailDTO getOrderDetailByOrderNo(String orderNo);
|
||||
|
||||
/**
|
||||
* 计算订单逾期天数
|
||||
* @param endRentTime
|
||||
* @return
|
||||
*/
|
||||
Integer getOrderOverdueDays(LocalDateTime endRentTime);
|
||||
|
||||
/**
|
||||
* 计算订单即将到期天数
|
||||
* @param endRentTime
|
||||
* @return
|
||||
*/
|
||||
Integer getOrderExpectedDays(LocalDateTime endRentTime);
|
||||
|
||||
/**
|
||||
* 计算逾期金额
|
||||
* @param overdueDays
|
||||
* @param overdueFee
|
||||
* @return
|
||||
*/
|
||||
BigDecimal getOrderOverdueAmount(Integer overdueDays, BigDecimal overdueFee);
|
||||
|
||||
/**
|
||||
* 查询用户当前未完成的订单
|
||||
* @param customerId
|
||||
|
||||
@ -33,7 +33,6 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -316,7 +315,7 @@ public class OrderServiceImpl implements OrderService {
|
||||
}
|
||||
}
|
||||
//生成租车订单
|
||||
BigDecimal overDueAmount = getOrderOverdueAmount(orderMainPO.getOverdueDays(), orderMainPO.getOverdueFee());
|
||||
BigDecimal overDueAmount = OrderUtil.getOrderOverdueAmount(orderMainPO.getOverdueDays(), orderMainPO.getOverdueFee());
|
||||
//生成租车子订单
|
||||
OrderSubPO rentOrder = new OrderSubPO();
|
||||
rentOrder.setOrderId(orderMainPO.getOrderId());
|
||||
@ -437,12 +436,12 @@ public class OrderServiceImpl implements OrderService {
|
||||
//如果是租车中,需要判断是否逾期了
|
||||
log.info("判断订单是否逾期");
|
||||
if(orderMainPO.getEndRentTime()!=null){
|
||||
Integer overdueDays = getOrderOverdueDays(orderMainPO.getEndRentTime());
|
||||
Integer overdueDays = OrderUtil.getOrderOverdueDays(orderMainPO.getEndRentTime());
|
||||
log.info("预计还车时间:{},订单逾期天数:{}",orderMainPO.getEndRentTime(),overdueDays);
|
||||
//逾期天数>0,则改为逾期,并且计算逾期天数以及逾期金额
|
||||
if(overdueDays>0){
|
||||
orderDetailDTO.setOverdueDays(overdueDays);
|
||||
orderDetailDTO.setOverdueAmount(getOrderOverdueAmount(overdueDays, orderMainPO.getOverdueFee()));
|
||||
orderDetailDTO.setOverdueAmount(OrderUtil.getOrderOverdueAmount(overdueDays, orderMainPO.getOverdueFee()));
|
||||
orderDetailDTO.setOrderStatus(OrderStatusEnum.RENT_OVERDUE.getCode());
|
||||
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.set(OrderMainPO::getOrderStatus, OrderStatusEnum.RENT_OVERDUE.getCode());
|
||||
@ -451,14 +450,14 @@ public class OrderServiceImpl implements OrderService {
|
||||
orderMainRepo.update(updateWrapper);
|
||||
}else {
|
||||
//没逾期则计算到期天数
|
||||
orderDetailDTO.setExpectedDays(getOrderExpectedDays(orderMainPO.getEndRentTime()));
|
||||
orderDetailDTO.setExpectedDays(OrderUtil.getOrderExpectedDays(orderMainPO.getEndRentTime()));
|
||||
}
|
||||
}
|
||||
|
||||
} else if(OrderStatusEnum.RENT_OVERDUE.getCode().equalsIgnoreCase(orderMainPO.getOrderStatus())){
|
||||
log.info("订单已逾期的,计算逾期金额");
|
||||
if(orderMainPO.getEndRentTime()!=null){
|
||||
orderDetailDTO.setOverdueAmount(getOrderOverdueAmount(orderMainPO.getOverdueDays(), orderMainPO.getOverdueFee()));
|
||||
orderDetailDTO.setOverdueAmount(OrderUtil.getOrderOverdueAmount(orderMainPO.getOverdueDays(), orderMainPO.getOverdueFee()));
|
||||
}
|
||||
} else if(OrderStatusEnum.WAIT_PAY.getCode().equalsIgnoreCase(orderMainPO.getOrderStatus())||OrderStatusEnum.RERENT_WAIT_PAY.getCode().equalsIgnoreCase(orderMainPO.getOrderStatus())){
|
||||
//TODO 待支付状态要拉起支付
|
||||
@ -467,37 +466,6 @@ public class OrderServiceImpl implements OrderService {
|
||||
return orderDetailDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getOrderOverdueDays(LocalDateTime endRentTime) {
|
||||
if(endRentTime!=null){
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if(now.isAfter(endRentTime)){
|
||||
return (int) ChronoUnit.DAYS.between(endRentTime, now);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getOrderExpectedDays(LocalDateTime endRentTime) {
|
||||
if(endRentTime!=null){
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if(now.isBefore(endRentTime)){
|
||||
return (int) ChronoUnit.DAYS.between(now, endRentTime);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getOrderOverdueAmount(Integer overdueDays, BigDecimal overdueFee) {
|
||||
if(overdueDays!=null&&overdueFee!=null){
|
||||
BigDecimal overdueDaysBd = new BigDecimal(overdueDays);
|
||||
return overdueDaysBd.multiply(overdueFee);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderDetailDTO getCurrentNoEndOrder(Long customerId) {
|
||||
if(customerId==null){
|
||||
|
||||
101
src/main/java/com/sczx/order/task/OrderOverdueTask.java
Normal file
101
src/main/java/com/sczx/order/task/OrderOverdueTask.java
Normal file
@ -0,0 +1,101 @@
|
||||
package com.sczx.order.task;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.sczx.order.common.enums.OrderStatusEnum;
|
||||
import com.sczx.order.po.OrderMainPO;
|
||||
import com.sczx.order.repository.OrderMainRepo;
|
||||
import com.sczx.order.utils.OrderUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class OrderOverdueTask {
|
||||
@Autowired
|
||||
private OrderMainRepo orderMainRepo;
|
||||
|
||||
/**
|
||||
* 每30分钟检查一次逾期订单
|
||||
* 使用ShedLock确保在分布式环境下只有一个实例执行
|
||||
* 分布式锁机制:ShedLock使用Redis作为锁存储,确保同一时间只有一个服务实例执行定时任务
|
||||
* 任务名称:@SchedulerLock 注解中的 name 属性标识任务名称,相同名称的任务在分布式环境下互斥执行
|
||||
* 锁时长配置:
|
||||
* lockAtMostFor:锁最多持有时间,防止节点宕机导致锁无法释放
|
||||
* lockAtLeastFor:锁最少持有时间,防止任务执行过快导致频繁执行
|
||||
*/
|
||||
@Scheduled(cron = "0 */30 * * * ?")
|
||||
@SchedulerLock(name = "checkOverdueOrders", lockAtMostFor = "9m", lockAtLeastFor = "1m")
|
||||
public void checkOverdueOrders() {
|
||||
log.info("开始执行逾期订单检查任务");
|
||||
try {
|
||||
processOverdueOrders();
|
||||
log.info("逾期订单检查任务执行完成");
|
||||
} catch (Exception e) {
|
||||
log.error("执行逾期订单检查任务失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理逾期订单
|
||||
*/
|
||||
private void processOverdueOrders() {
|
||||
try {
|
||||
// 查询所有进行中的订单且当前时间已超过预计还车时间
|
||||
LambdaQueryWrapper<OrderMainPO> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(OrderMainPO::getOrderStatus,
|
||||
Collections.singletonList(OrderStatusEnum.RENT_ING.getCode()))
|
||||
.lt(OrderMainPO::getEndRentTime, LocalDateTime.now());
|
||||
|
||||
List<OrderMainPO> overdueOrders = orderMainRepo.list(queryWrapper);
|
||||
|
||||
if (overdueOrders.isEmpty()) {
|
||||
log.info("未发现逾期订单");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("发现{}个逾期订单,开始处理", overdueOrders.size());
|
||||
|
||||
int successCount = 0;
|
||||
for (OrderMainPO order : overdueOrders) {
|
||||
try {
|
||||
//先计算逾期天数
|
||||
Integer overdueDays = OrderUtil.getOrderOverdueDays(order.getEndRentTime());
|
||||
if(overdueDays>0){
|
||||
// 更新订单状态为逾期
|
||||
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.set(OrderMainPO::getOrderStatus, OrderStatusEnum.RENT_OVERDUE.getCode())
|
||||
.set(OrderMainPO::getOverdueDays, overdueDays)
|
||||
.eq(OrderMainPO::getOrderId, order.getOrderId())
|
||||
// 确保状态未被其他节点修改
|
||||
.eq(OrderMainPO::getOrderStatus, OrderStatusEnum.RENT_ING.getCode());
|
||||
|
||||
boolean updated = orderMainRepo.update(updateWrapper);
|
||||
|
||||
if (updated ) {
|
||||
log.info("订单{}已逾期,状态已更新", order.getOrderNo());
|
||||
successCount++;
|
||||
} else {
|
||||
log.info("订单{}状态已变更,无需更新", order.getOrderNo());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("更新订单{}状态失败", order.getOrderNo(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("逾期订单处理完成,成功处理{}个订单", successCount);
|
||||
} catch (Exception e) {
|
||||
log.error("处理逾期订单失败", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
package com.sczx.order.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OrderUtil {
|
||||
@ -37,4 +39,50 @@ public class OrderUtil {
|
||||
String uuidSuffix = UUID.randomUUID().toString().replace("-", "").substring(0, 6).toUpperCase();
|
||||
return prefix + timestamp + uuidSuffix; // sub代表子订单号
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单逾期天数
|
||||
* @param endRentTime 订单结束时间
|
||||
* @return 逾期天数
|
||||
*/
|
||||
public static Integer getOrderOverdueDays(LocalDateTime endRentTime) {
|
||||
if(endRentTime!=null){
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if(now.isAfter(endRentTime)){
|
||||
return (int) ChronoUnit.DAYS.between(endRentTime, now);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算订单预计天数
|
||||
* @param endRentTime 订单结束时间
|
||||
* @return 逾期天数
|
||||
*/
|
||||
public static Integer getOrderExpectedDays(LocalDateTime endRentTime) {
|
||||
if(endRentTime!=null){
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if(now.isBefore(endRentTime)){
|
||||
return (int) ChronoUnit.DAYS.between(now, endRentTime);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算订单逾期金额
|
||||
* @param overdueDays 逾期天数
|
||||
* @param overdueFee 逾期费用
|
||||
* @return 逾期金额
|
||||
*/
|
||||
public static BigDecimal getOrderOverdueAmount(Integer overdueDays, BigDecimal overdueFee) {
|
||||
if(overdueDays!=null&&overdueFee!=null){
|
||||
BigDecimal overdueDaysBd = new BigDecimal(overdueDays);
|
||||
return overdueDaysBd.multiply(overdueFee);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ spring:
|
||||
redis:
|
||||
host: 115.190.8.52
|
||||
port: 6379
|
||||
database: 0
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
|
||||
Reference in New Issue
Block a user