增加定时任务检查订单是否逾期
This commit is contained in:
11
pom.xml
11
pom.xml
@ -227,6 +227,17 @@
|
|||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
<version>1.2.83</version>
|
<version>1.2.83</version>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
<!-- Build Configuration -->
|
<!-- Build Configuration -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.sczx.order;
|
|||||||
import com.sczx.order.common.constant.SystemConstants;
|
import com.sczx.order.common.constant.SystemConstants;
|
||||||
import com.sczx.order.utils.ComputerInfo;
|
import com.sczx.order.utils.ComputerInfo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
@ -12,6 +13,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
|
|||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.retry.annotation.EnableRetry;
|
import org.springframework.retry.annotation.EnableRetry;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -24,6 +26,8 @@ import java.io.IOException;
|
|||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
@EnableHystrix
|
@EnableHystrix
|
||||||
@MapperScan("com.sczx.order.mapper") // 扫描 Mapper 接口
|
@MapperScan("com.sczx.order.mapper") // 扫描 Mapper 接口
|
||||||
|
@EnableScheduling
|
||||||
|
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
|
||||||
public class Application {
|
public class Application {
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
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.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.sczx.order.dto.*;
|
import com.sczx.order.dto.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
public interface OrderService {
|
public interface OrderService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,28 +42,6 @@ public interface OrderService {
|
|||||||
*/
|
*/
|
||||||
OrderDetailDTO getOrderDetailByOrderNo(String orderNo);
|
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
|
* @param customerId
|
||||||
|
|||||||
@ -33,7 +33,6 @@ import org.springframework.util.CollectionUtils;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
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();
|
OrderSubPO rentOrder = new OrderSubPO();
|
||||||
rentOrder.setOrderId(orderMainPO.getOrderId());
|
rentOrder.setOrderId(orderMainPO.getOrderId());
|
||||||
@ -437,12 +436,12 @@ public class OrderServiceImpl implements OrderService {
|
|||||||
//如果是租车中,需要判断是否逾期了
|
//如果是租车中,需要判断是否逾期了
|
||||||
log.info("判断订单是否逾期");
|
log.info("判断订单是否逾期");
|
||||||
if(orderMainPO.getEndRentTime()!=null){
|
if(orderMainPO.getEndRentTime()!=null){
|
||||||
Integer overdueDays = getOrderOverdueDays(orderMainPO.getEndRentTime());
|
Integer overdueDays = OrderUtil.getOrderOverdueDays(orderMainPO.getEndRentTime());
|
||||||
log.info("预计还车时间:{},订单逾期天数:{}",orderMainPO.getEndRentTime(),overdueDays);
|
log.info("预计还车时间:{},订单逾期天数:{}",orderMainPO.getEndRentTime(),overdueDays);
|
||||||
//逾期天数>0,则改为逾期,并且计算逾期天数以及逾期金额
|
//逾期天数>0,则改为逾期,并且计算逾期天数以及逾期金额
|
||||||
if(overdueDays>0){
|
if(overdueDays>0){
|
||||||
orderDetailDTO.setOverdueDays(overdueDays);
|
orderDetailDTO.setOverdueDays(overdueDays);
|
||||||
orderDetailDTO.setOverdueAmount(getOrderOverdueAmount(overdueDays, orderMainPO.getOverdueFee()));
|
orderDetailDTO.setOverdueAmount(OrderUtil.getOrderOverdueAmount(overdueDays, orderMainPO.getOverdueFee()));
|
||||||
orderDetailDTO.setOrderStatus(OrderStatusEnum.RENT_OVERDUE.getCode());
|
orderDetailDTO.setOrderStatus(OrderStatusEnum.RENT_OVERDUE.getCode());
|
||||||
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
|
LambdaUpdateWrapper<OrderMainPO> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
updateWrapper.set(OrderMainPO::getOrderStatus, OrderStatusEnum.RENT_OVERDUE.getCode());
|
updateWrapper.set(OrderMainPO::getOrderStatus, OrderStatusEnum.RENT_OVERDUE.getCode());
|
||||||
@ -451,14 +450,14 @@ public class OrderServiceImpl implements OrderService {
|
|||||||
orderMainRepo.update(updateWrapper);
|
orderMainRepo.update(updateWrapper);
|
||||||
}else {
|
}else {
|
||||||
//没逾期则计算到期天数
|
//没逾期则计算到期天数
|
||||||
orderDetailDTO.setExpectedDays(getOrderExpectedDays(orderMainPO.getEndRentTime()));
|
orderDetailDTO.setExpectedDays(OrderUtil.getOrderExpectedDays(orderMainPO.getEndRentTime()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(OrderStatusEnum.RENT_OVERDUE.getCode().equalsIgnoreCase(orderMainPO.getOrderStatus())){
|
} else if(OrderStatusEnum.RENT_OVERDUE.getCode().equalsIgnoreCase(orderMainPO.getOrderStatus())){
|
||||||
log.info("订单已逾期的,计算逾期金额");
|
log.info("订单已逾期的,计算逾期金额");
|
||||||
if(orderMainPO.getEndRentTime()!=null){
|
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())){
|
} else if(OrderStatusEnum.WAIT_PAY.getCode().equalsIgnoreCase(orderMainPO.getOrderStatus())||OrderStatusEnum.RERENT_WAIT_PAY.getCode().equalsIgnoreCase(orderMainPO.getOrderStatus())){
|
||||||
//TODO 待支付状态要拉起支付
|
//TODO 待支付状态要拉起支付
|
||||||
@ -467,37 +466,6 @@ public class OrderServiceImpl implements OrderService {
|
|||||||
return orderDetailDTO;
|
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
|
@Override
|
||||||
public OrderDetailDTO getCurrentNoEndOrder(Long customerId) {
|
public OrderDetailDTO getCurrentNoEndOrder(Long customerId) {
|
||||||
if(customerId==null){
|
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;
|
package com.sczx.order.utils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class OrderUtil {
|
public class OrderUtil {
|
||||||
@ -37,4 +39,50 @@ public class OrderUtil {
|
|||||||
String uuidSuffix = UUID.randomUUID().toString().replace("-", "").substring(0, 6).toUpperCase();
|
String uuidSuffix = UUID.randomUUID().toString().replace("-", "").substring(0, 6).toUpperCase();
|
||||||
return prefix + timestamp + uuidSuffix; // sub代表子订单号
|
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:
|
redis:
|
||||||
host: 115.190.8.52
|
host: 115.190.8.52
|
||||||
port: 6379
|
port: 6379
|
||||||
|
database: 0
|
||||||
lettuce:
|
lettuce:
|
||||||
pool:
|
pool:
|
||||||
max-active: 8
|
max-active: 8
|
||||||
|
|||||||
Reference in New Issue
Block a user