增加定时任务检查订单是否逾期
This commit is contained in:
		
							
								
								
									
										11
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								pom.xml
									
									
									
									
									
								
							| @ -227,6 +227,17 @@ | ||||
|             <artifactId>fastjson</artifactId> | ||||
|             <version>1.2.83</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>net.javacrumbs.shedlock</groupId> | ||||
|             <artifactId>shedlock-spring</artifactId> | ||||
|             <version>4.44.0</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>net.javacrumbs.shedlock</groupId> | ||||
|             <artifactId>shedlock-provider-redis-spring</artifactId> | ||||
|             <version>4.44.0</version> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|  | ||||
|     <!-- Build Configuration --> | ||||
|  | ||||
| @ -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