完成自动取消订单任务功能
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
package com.gxwebsoft.common.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略租户隔离注解
|
||||||
|
*
|
||||||
|
* 用于标记需要跨租户操作的方法,如定时任务、系统管理等场景
|
||||||
|
*
|
||||||
|
* @author WebSoft
|
||||||
|
* @since 2025-01-26
|
||||||
|
*/
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface IgnoreTenant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明信息,用于记录为什么需要忽略租户隔离
|
||||||
|
*/
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否记录日志
|
||||||
|
*/
|
||||||
|
boolean logAccess() default true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.gxwebsoft.common.core.aspect;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
|
||||||
|
import com.gxwebsoft.common.core.context.TenantContext;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略租户隔离切面
|
||||||
|
*
|
||||||
|
* 自动处理 @IgnoreTenant 注解标记的方法,临时禁用租户隔离
|
||||||
|
*
|
||||||
|
* @author WebSoft
|
||||||
|
* @since 2025-01-26
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
@Order(1) // 确保在其他切面之前执行
|
||||||
|
public class IgnoreTenantAspect {
|
||||||
|
|
||||||
|
@Around("@annotation(com.gxwebsoft.common.core.annotation.IgnoreTenant)")
|
||||||
|
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
IgnoreTenant ignoreTenant = method.getAnnotation(IgnoreTenant.class);
|
||||||
|
|
||||||
|
// 记录原始状态
|
||||||
|
boolean originalIgnore = TenantContext.isIgnoreTenant();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 设置忽略租户隔离
|
||||||
|
TenantContext.setIgnoreTenant(true);
|
||||||
|
|
||||||
|
// 记录日志
|
||||||
|
if (ignoreTenant.logAccess()) {
|
||||||
|
String className = joinPoint.getTarget().getClass().getSimpleName();
|
||||||
|
String methodName = method.getName();
|
||||||
|
String reason = ignoreTenant.value();
|
||||||
|
|
||||||
|
if (reason.isEmpty()) {
|
||||||
|
log.debug("执行跨租户操作: {}.{}", className, methodName);
|
||||||
|
} else {
|
||||||
|
log.debug("执行跨租户操作: {}.{} - {}", className, methodName, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行目标方法
|
||||||
|
return joinPoint.proceed();
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// 恢复原始状态
|
||||||
|
TenantContext.setIgnoreTenant(originalIgnore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||||
|
import com.gxwebsoft.common.core.context.TenantContext;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
import net.sf.jsqlparser.expression.LongValue;
|
import net.sf.jsqlparser.expression.LongValue;
|
||||||
@@ -70,6 +71,12 @@ public class MybatisPlusConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean ignoreTable(String tableName) {
|
public boolean ignoreTable(String tableName) {
|
||||||
|
// 如果当前上下文设置了忽略租户隔离,则忽略所有表的租户隔离
|
||||||
|
if (TenantContext.isIgnoreTenant()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统级别的表始终忽略租户隔离
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
"sys_tenant",
|
"sys_tenant",
|
||||||
"sys_dictionary",
|
"sys_dictionary",
|
||||||
@@ -84,7 +91,7 @@ public class MybatisPlusConfig {
|
|||||||
// "shop_order_goods",
|
// "shop_order_goods",
|
||||||
// "shop_goods"
|
// "shop_goods"
|
||||||
// "shop_users",
|
// "shop_users",
|
||||||
// "shop_order",
|
// "shop_order" // 移除shop_order,改为通过注解控制
|
||||||
// "shop_order_info",
|
// "shop_order_info",
|
||||||
// "booking_user_invoice"
|
// "booking_user_invoice"
|
||||||
).contains(tableName);
|
).contains(tableName);
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.gxwebsoft.common.core.context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户上下文管理器
|
||||||
|
*
|
||||||
|
* 用于在特定场景下临时禁用租户隔离
|
||||||
|
*
|
||||||
|
* @author WebSoft
|
||||||
|
* @since 2025-01-26
|
||||||
|
*/
|
||||||
|
public class TenantContext {
|
||||||
|
|
||||||
|
private static final ThreadLocal<Boolean> IGNORE_TENANT = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置忽略租户隔离
|
||||||
|
*/
|
||||||
|
public static void setIgnoreTenant(boolean ignore) {
|
||||||
|
IGNORE_TENANT.set(ignore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否忽略租户隔离
|
||||||
|
*/
|
||||||
|
public static boolean isIgnoreTenant() {
|
||||||
|
Boolean ignore = IGNORE_TENANT.get();
|
||||||
|
return ignore != null && ignore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除租户上下文
|
||||||
|
*/
|
||||||
|
public static void clear() {
|
||||||
|
IGNORE_TENANT.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在忽略租户隔离的上下文中执行操作
|
||||||
|
*
|
||||||
|
* @param runnable 要执行的操作
|
||||||
|
*/
|
||||||
|
public static void runIgnoreTenant(Runnable runnable) {
|
||||||
|
boolean originalIgnore = isIgnoreTenant();
|
||||||
|
try {
|
||||||
|
setIgnoreTenant(true);
|
||||||
|
runnable.run();
|
||||||
|
} finally {
|
||||||
|
setIgnoreTenant(originalIgnore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在忽略租户隔离的上下文中执行操作并返回结果
|
||||||
|
*
|
||||||
|
* @param supplier 要执行的操作
|
||||||
|
* @return 操作结果
|
||||||
|
*/
|
||||||
|
public static <T> T callIgnoreTenant(java.util.function.Supplier<T> supplier) {
|
||||||
|
boolean originalIgnore = isIgnoreTenant();
|
||||||
|
try {
|
||||||
|
setIgnoreTenant(true);
|
||||||
|
return supplier.get();
|
||||||
|
} finally {
|
||||||
|
setIgnoreTenant(originalIgnore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.gxwebsoft.shop.service.impl;
|
package com.gxwebsoft.shop.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
|
||||||
import com.gxwebsoft.shop.entity.*;
|
import com.gxwebsoft.shop.entity.*;
|
||||||
import com.gxwebsoft.shop.service.*;
|
import com.gxwebsoft.shop.service.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -113,6 +114,7 @@ public class OrderCancelServiceImpl implements OrderCancelService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@IgnoreTenant("定时任务需要查询所有租户的超时订单")
|
||||||
public List<ShopOrder> findExpiredUnpaidOrders(Integer timeoutMinutes, Integer batchSize) {
|
public List<ShopOrder> findExpiredUnpaidOrders(Integer timeoutMinutes, Integer batchSize) {
|
||||||
LocalDateTime expireTime = LocalDateTime.now().minusMinutes(timeoutMinutes);
|
LocalDateTime expireTime = LocalDateTime.now().minusMinutes(timeoutMinutes);
|
||||||
|
|
||||||
@@ -123,10 +125,13 @@ public class OrderCancelServiceImpl implements OrderCancelService {
|
|||||||
.orderByAsc(ShopOrder::getCreateTime)
|
.orderByAsc(ShopOrder::getCreateTime)
|
||||||
.last("LIMIT " + batchSize);
|
.last("LIMIT " + batchSize);
|
||||||
|
|
||||||
return shopOrderService.list(queryWrapper);
|
final List<ShopOrder> list = shopOrderService.list(queryWrapper);
|
||||||
|
System.out.println("list = " + list.size());
|
||||||
|
return shopOrderService.list(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@IgnoreTenant("定时任务需要查询特定租户的超时订单")
|
||||||
public List<ShopOrder> findExpiredUnpaidOrdersByTenant(Integer tenantId, Integer timeoutMinutes, Integer batchSize) {
|
public List<ShopOrder> findExpiredUnpaidOrdersByTenant(Integer tenantId, Integer timeoutMinutes, Integer batchSize) {
|
||||||
LocalDateTime expireTime = LocalDateTime.now().minusMinutes(timeoutMinutes);
|
LocalDateTime expireTime = LocalDateTime.now().minusMinutes(timeoutMinutes);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.gxwebsoft.shop.task;
|
package com.gxwebsoft.shop.task;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
|
||||||
import com.gxwebsoft.shop.config.OrderConfigProperties;
|
import com.gxwebsoft.shop.config.OrderConfigProperties;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
import com.gxwebsoft.shop.service.OrderCancelService;
|
import com.gxwebsoft.shop.service.OrderCancelService;
|
||||||
@@ -38,6 +39,7 @@ public class OrderAutoCancelTask {
|
|||||||
* 开发环境:每1分钟执行一次(便于测试)
|
* 开发环境:每1分钟执行一次(便于测试)
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "${shop.order.auto-cancel.cron:0 */5 * * * ?}")
|
@Scheduled(cron = "${shop.order.auto-cancel.cron:0 */5 * * * ?}")
|
||||||
|
@IgnoreTenant("定时任务需要处理所有租户的超时订单")
|
||||||
public void cancelExpiredOrders() {
|
public void cancelExpiredOrders() {
|
||||||
if (!orderConfig.getAutoCancel().isEnabled()) {
|
if (!orderConfig.getAutoCancel().isEnabled()) {
|
||||||
log.debug("订单自动取消功能已禁用");
|
log.debug("订单自动取消功能已禁用");
|
||||||
@@ -45,7 +47,7 @@ public class OrderAutoCancelTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.info("开始执行订单自动取消任务...");
|
log.info("开始执行订单自动取消任务...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
int totalCancelledCount = 0;
|
int totalCancelledCount = 0;
|
||||||
@@ -61,7 +63,7 @@ public class OrderAutoCancelTask {
|
|||||||
long endTime = System.currentTimeMillis();
|
long endTime = System.currentTimeMillis();
|
||||||
long duration = endTime - startTime;
|
long duration = endTime - startTime;
|
||||||
|
|
||||||
log.info("订单自动取消任务完成,总取消数量: {},默认配置: {},租户配置: {},耗时: {}ms",
|
log.info("订单自动取消任务完成,总取消数量: {},默认配置: {},租户配置: {},耗时: {}ms",
|
||||||
totalCancelledCount, defaultCancelledCount, tenantCancelledCount, duration);
|
totalCancelledCount, defaultCancelledCount, tenantCancelledCount, duration);
|
||||||
|
|
||||||
// 开发环境输出更详细的日志
|
// 开发环境输出更详细的日志
|
||||||
@@ -85,7 +87,7 @@ public class OrderAutoCancelTask {
|
|||||||
log.debug("处理默认超时订单,超时时间: {}分钟,批量大小: {}", defaultTimeout, batchSize);
|
log.debug("处理默认超时订单,超时时间: {}分钟,批量大小: {}", defaultTimeout, batchSize);
|
||||||
|
|
||||||
List<ShopOrder> expiredOrders = orderCancelService.findExpiredUnpaidOrders(defaultTimeout, batchSize);
|
List<ShopOrder> expiredOrders = orderCancelService.findExpiredUnpaidOrders(defaultTimeout, batchSize);
|
||||||
|
|
||||||
if (expiredOrders.isEmpty()) {
|
if (expiredOrders.isEmpty()) {
|
||||||
log.debug("没有找到使用默认配置的超时订单");
|
log.debug("没有找到使用默认配置的超时订单");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -93,14 +95,14 @@ public class OrderAutoCancelTask {
|
|||||||
|
|
||||||
// 过滤掉有特殊租户配置的订单
|
// 过滤掉有特殊租户配置的订单
|
||||||
List<ShopOrder> ordersToCancel = filterOrdersWithoutTenantConfig(expiredOrders);
|
List<ShopOrder> ordersToCancel = filterOrdersWithoutTenantConfig(expiredOrders);
|
||||||
|
|
||||||
if (ordersToCancel.isEmpty()) {
|
if (ordersToCancel.isEmpty()) {
|
||||||
log.debug("过滤后没有需要使用默认配置取消的订单");
|
log.debug("过滤后没有需要使用默认配置取消的订单");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cancelledCount = orderCancelService.batchCancelOrders(ordersToCancel);
|
int cancelledCount = orderCancelService.batchCancelOrders(ordersToCancel);
|
||||||
log.info("默认配置取消订单完成,找到: {}个,过滤后: {}个,成功取消: {}个",
|
log.info("默认配置取消订单完成,找到: {}个,过滤后: {}个,成功取消: {}个",
|
||||||
expiredOrders.size(), ordersToCancel.size(), cancelledCount);
|
expiredOrders.size(), ordersToCancel.size(), cancelledCount);
|
||||||
|
|
||||||
return cancelledCount;
|
return cancelledCount;
|
||||||
@@ -131,7 +133,7 @@ public class OrderAutoCancelTask {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("处理租户{}的超时订单,超时时间: {}分钟",
|
log.debug("处理租户{}的超时订单,超时时间: {}分钟",
|
||||||
tenantConfig.getTenantId(), tenantConfig.getTimeoutMinutes());
|
tenantConfig.getTenantId(), tenantConfig.getTimeoutMinutes());
|
||||||
|
|
||||||
List<ShopOrder> tenantExpiredOrders = orderCancelService.findExpiredUnpaidOrdersByTenant(
|
List<ShopOrder> tenantExpiredOrders = orderCancelService.findExpiredUnpaidOrdersByTenant(
|
||||||
@@ -140,8 +142,8 @@ public class OrderAutoCancelTask {
|
|||||||
if (!tenantExpiredOrders.isEmpty()) {
|
if (!tenantExpiredOrders.isEmpty()) {
|
||||||
int cancelledCount = orderCancelService.batchCancelOrders(tenantExpiredOrders);
|
int cancelledCount = orderCancelService.batchCancelOrders(tenantExpiredOrders);
|
||||||
totalCancelledCount += cancelledCount;
|
totalCancelledCount += cancelledCount;
|
||||||
|
|
||||||
log.info("租户{}取消订单完成,找到: {}个,成功取消: {}个",
|
log.info("租户{}取消订单完成,找到: {}个,成功取消: {}个",
|
||||||
tenantConfig.getTenantId(), tenantExpiredOrders.size(), cancelledCount);
|
tenantConfig.getTenantId(), tenantExpiredOrders.size(), cancelledCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,6 +177,7 @@ public class OrderAutoCancelTask {
|
|||||||
/**
|
/**
|
||||||
* 手动触发订单自动取消任务(用于测试)
|
* 手动触发订单自动取消任务(用于测试)
|
||||||
*/
|
*/
|
||||||
|
@IgnoreTenant("手动触发的定时任务需要处理所有租户的超时订单")
|
||||||
public void manualCancelExpiredOrders() {
|
public void manualCancelExpiredOrders() {
|
||||||
log.info("手动触发订单自动取消任务...");
|
log.info("手动触发订单自动取消任务...");
|
||||||
cancelExpiredOrders();
|
cancelExpiredOrders();
|
||||||
|
|||||||
@@ -146,21 +146,21 @@ shop:
|
|||||||
# 默认超时时间(分钟)
|
# 默认超时时间(分钟)
|
||||||
default-timeout-minutes: 30
|
default-timeout-minutes: 30
|
||||||
# 定时任务检查间隔(分钟)
|
# 定时任务检查间隔(分钟)
|
||||||
check-interval-minutes: 5
|
check-interval-minutes: 1
|
||||||
# 批量处理大小
|
# 批量处理大小
|
||||||
batch-size: 100
|
batch-size: 100
|
||||||
# 定时任务执行时间(cron表达式)
|
# 定时任务执行时间(cron表达式)
|
||||||
# 生产环境:每5分钟执行一次
|
# 生产环境:每5分钟执行一次
|
||||||
# 开发环境:每1分钟执行一次(便于测试)
|
# 开发环境:每1分钟执行一次(便于测试)
|
||||||
cron: "0 */5 * * * ?"
|
cron: "0 */1 * * * ?"
|
||||||
# 开发环境可以设置为: "0 */1 * * * ?"
|
# 开发环境可以设置为: "0 */1 * * * ?"
|
||||||
|
|
||||||
# 租户特殊配置
|
# 租户特殊配置
|
||||||
tenant-configs:
|
tenant-configs:
|
||||||
- tenant-id: 10324
|
- tenant-id: 10324
|
||||||
tenant-name: "百色中学"
|
tenant-name: "百色中学"
|
||||||
timeout-minutes: 60 # 捐款订单给更长的支付时间
|
timeout-minutes: 1 # 测试环境:1分钟超时,便于测试
|
||||||
enabled: true
|
enabled: true # 使用注解方案,重新启用
|
||||||
# 可以添加更多租户配置
|
# 可以添加更多租户配置
|
||||||
# - tenant-id: 10550
|
# - tenant-id: 10550
|
||||||
# tenant-name: "其他租户"
|
# tenant-name: "其他租户"
|
||||||
|
|||||||
182
src/test/java/com/gxwebsoft/shop/OrderQueryTest.java
Normal file
182
src/test/java/com/gxwebsoft/shop/OrderQueryTest.java
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
package com.gxwebsoft.shop;
|
||||||
|
|
||||||
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
|
import com.gxwebsoft.shop.service.ShopOrderService;
|
||||||
|
import com.gxwebsoft.shop.service.OrderCancelService;
|
||||||
|
import com.gxwebsoft.shop.config.OrderConfigProperties;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单查询测试
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@SpringBootTest
|
||||||
|
public class OrderQueryTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ShopOrderService shopOrderService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrderCancelService orderCancelService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrderConfigProperties orderConfig;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQuerySpecificOrder() {
|
||||||
|
String orderNo = "1957754623870595072";
|
||||||
|
log.info("查询订单号: {}", orderNo);
|
||||||
|
|
||||||
|
ShopOrder order = shopOrderService.getByOutTradeNo(orderNo);
|
||||||
|
if (order != null) {
|
||||||
|
log.info("订单信息:");
|
||||||
|
log.info(" 订单ID: {}", order.getOrderId());
|
||||||
|
log.info(" 订单号: {}", order.getOrderNo());
|
||||||
|
log.info(" 订单状态: {} (0=待支付, 1=待发货, 2=已取消, 3=已完成)", order.getOrderStatus());
|
||||||
|
log.info(" 支付状态: {} (false=未支付, true=已支付)", order.getPayStatus());
|
||||||
|
log.info(" 创建时间: {}", order.getCreateTime());
|
||||||
|
log.info(" 支付时间: {}", order.getPayTime());
|
||||||
|
log.info(" 取消时间: {}", order.getCancelTime());
|
||||||
|
log.info(" 租户ID: {}", order.getTenantId());
|
||||||
|
log.info(" 订单金额: {}", order.getTotalPrice());
|
||||||
|
log.info(" 取消原因: {}", order.getCancelReason());
|
||||||
|
|
||||||
|
// 检查是否符合自动取消条件
|
||||||
|
checkAutoCancelConditions(order);
|
||||||
|
|
||||||
|
// 计算什么时候会符合自动取消条件
|
||||||
|
calculateCancelTime(order);
|
||||||
|
} else {
|
||||||
|
log.warn("未找到订单号为 {} 的订单", orderNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAutoCancelConditions(ShopOrder order) {
|
||||||
|
log.info("\n=== 检查自动取消条件 ===");
|
||||||
|
|
||||||
|
// 1. 检查订单状态
|
||||||
|
boolean statusOk = (order.getOrderStatus() != null && order.getOrderStatus() == 0);
|
||||||
|
log.info("1. 订单状态检查: {} (需要为0-待支付)", statusOk ? "✓通过" : "✗不通过");
|
||||||
|
|
||||||
|
// 2. 检查支付状态
|
||||||
|
boolean payStatusOk = (order.getPayStatus() != null && !order.getPayStatus());
|
||||||
|
log.info("2. 支付状态检查: {} (需要为false-未支付)", payStatusOk ? "✓通过" : "✗不通过");
|
||||||
|
|
||||||
|
// 3. 检查创建时间是否超时
|
||||||
|
if (order.getCreateTime() != null) {
|
||||||
|
LocalDateTime createTime = order.getCreateTime();
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
// 获取超时配置
|
||||||
|
Integer timeoutMinutes = getTimeoutMinutes(order.getTenantId());
|
||||||
|
LocalDateTime expireTime = createTime.plusMinutes(timeoutMinutes);
|
||||||
|
|
||||||
|
boolean timeoutOk = now.isAfter(expireTime);
|
||||||
|
long minutesElapsed = java.time.Duration.between(createTime, now).toMinutes();
|
||||||
|
|
||||||
|
log.info("3. 超时检查:");
|
||||||
|
log.info(" 创建时间: {}", createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
log.info(" 当前时间: {}", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
log.info(" 超时配置: {}分钟", timeoutMinutes);
|
||||||
|
log.info(" 过期时间: {}", expireTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
log.info(" 已过时间: {}分钟", minutesElapsed);
|
||||||
|
log.info(" 是否超时: {} (需要超过{}分钟)", timeoutOk ? "✓是" : "✗否", timeoutMinutes);
|
||||||
|
|
||||||
|
// 4. 综合判断
|
||||||
|
boolean shouldCancel = statusOk && payStatusOk && timeoutOk;
|
||||||
|
log.info("\n=== 综合判断 ===");
|
||||||
|
log.info("是否符合自动取消条件: {}", shouldCancel ? "✓是" : "✗否");
|
||||||
|
|
||||||
|
if (shouldCancel) {
|
||||||
|
log.info("该订单符合自动取消任务的处理条件");
|
||||||
|
} else {
|
||||||
|
log.info("该订单不符合自动取消任务的处理条件");
|
||||||
|
if (!statusOk) log.info(" - 订单状态不是待支付状态");
|
||||||
|
if (!payStatusOk) log.info(" - 订单已支付");
|
||||||
|
if (!timeoutOk) log.info(" - 订单未超时");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("订单创建时间为空,无法判断是否超时");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateCancelTime(ShopOrder order) {
|
||||||
|
log.info("\n=== 计算自动取消时间点 ===");
|
||||||
|
|
||||||
|
if (order.getCreateTime() == null) {
|
||||||
|
log.warn("订单创建时间为空,无法计算取消时间");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取超时配置
|
||||||
|
Integer timeoutMinutes = getTimeoutMinutes(order.getTenantId());
|
||||||
|
LocalDateTime createTime = order.getCreateTime();
|
||||||
|
LocalDateTime cancelTime = createTime.plusMinutes(timeoutMinutes);
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
log.info("订单创建时间: {}", createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
log.info("当前时间: {}", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
log.info("超时配置: {}分钟", timeoutMinutes);
|
||||||
|
log.info("预计取消时间: {}", cancelTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
|
||||||
|
if (now.isBefore(cancelTime)) {
|
||||||
|
long minutesLeft = java.time.Duration.between(now, cancelTime).toMinutes();
|
||||||
|
long secondsLeft = java.time.Duration.between(now, cancelTime).getSeconds() % 60;
|
||||||
|
log.info("距离自动取消还有: {}分{}秒", minutesLeft, secondsLeft);
|
||||||
|
log.info("状态: ⏰ 等待中");
|
||||||
|
} else {
|
||||||
|
long minutesOverdue = java.time.Duration.between(cancelTime, now).toMinutes();
|
||||||
|
log.info("已超时: {}分钟", minutesOverdue);
|
||||||
|
log.info("状态: ⚠️ 应该被取消");
|
||||||
|
|
||||||
|
// 检查为什么没有被取消
|
||||||
|
if (order.getPayStatus() != null && order.getPayStatus()) {
|
||||||
|
log.info("原因: 订单已支付,不会被自动取消");
|
||||||
|
} else if (order.getOrderStatus() != null && order.getOrderStatus() != 0) {
|
||||||
|
log.info("原因: 订单状态不是待支付({}), 不会被自动取消", order.getOrderStatus());
|
||||||
|
} else {
|
||||||
|
log.info("原因: 订单符合取消条件,可能定时任务尚未执行或执行失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getTimeoutMinutes(Integer tenantId) {
|
||||||
|
// 检查是否有租户特殊配置
|
||||||
|
List<OrderConfigProperties.TenantCancelConfig> tenantConfigs = orderConfig.getAutoCancel().getTenantConfigs();
|
||||||
|
if (tenantConfigs != null) {
|
||||||
|
for (OrderConfigProperties.TenantCancelConfig config : tenantConfigs) {
|
||||||
|
if (config.isEnabled() && config.getTenantId().equals(tenantId)) {
|
||||||
|
return config.getTimeoutMinutes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用默认配置
|
||||||
|
return orderConfig.getAutoCancel().getDefaultTimeoutMinutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindExpiredOrders() {
|
||||||
|
log.info("=== 测试查找超时订单 ===");
|
||||||
|
|
||||||
|
Integer defaultTimeout = orderConfig.getAutoCancel().getDefaultTimeoutMinutes();
|
||||||
|
Integer batchSize = orderConfig.getAutoCancel().getBatchSize();
|
||||||
|
|
||||||
|
log.info("默认超时时间: {}分钟", defaultTimeout);
|
||||||
|
log.info("批量大小: {}", batchSize);
|
||||||
|
|
||||||
|
List<ShopOrder> expiredOrders = orderCancelService.findExpiredUnpaidOrders(defaultTimeout, batchSize);
|
||||||
|
log.info("找到{}个超时订单", expiredOrders.size());
|
||||||
|
|
||||||
|
for (ShopOrder order : expiredOrders) {
|
||||||
|
log.info("超时订单: {} - 创建时间: {}", order.getOrderNo(), order.getCreateTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user