完成自动取消订单任务功能

This commit is contained in:
2025-08-19 19:40:00 +08:00
parent 23fe389507
commit 4fc30e53cf
8 changed files with 370 additions and 14 deletions

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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.TenantLineInnerInterceptor;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.context.TenantContext;
import com.gxwebsoft.common.system.entity.User;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
@@ -70,6 +71,12 @@ public class MybatisPlusConfig {
@Override
public boolean ignoreTable(String tableName) {
// 如果当前上下文设置了忽略租户隔离,则忽略所有表的租户隔离
if (TenantContext.isIgnoreTenant()) {
return true;
}
// 系统级别的表始终忽略租户隔离
return Arrays.asList(
"sys_tenant",
"sys_dictionary",
@@ -84,7 +91,7 @@ public class MybatisPlusConfig {
// "shop_order_goods",
// "shop_goods"
// "shop_users",
// "shop_order",
// "shop_order" // 移除shop_order改为通过注解控制
// "shop_order_info",
// "booking_user_invoice"
).contains(tableName);

View File

@@ -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);
}
}
}

View File

@@ -1,6 +1,7 @@
package com.gxwebsoft.shop.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
import com.gxwebsoft.shop.entity.*;
import com.gxwebsoft.shop.service.*;
import lombok.extern.slf4j.Slf4j;
@@ -113,6 +114,7 @@ public class OrderCancelServiceImpl implements OrderCancelService {
}
@Override
@IgnoreTenant("定时任务需要查询所有租户的超时订单")
public List<ShopOrder> findExpiredUnpaidOrders(Integer timeoutMinutes, Integer batchSize) {
LocalDateTime expireTime = LocalDateTime.now().minusMinutes(timeoutMinutes);
@@ -123,10 +125,13 @@ public class OrderCancelServiceImpl implements OrderCancelService {
.orderByAsc(ShopOrder::getCreateTime)
.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
@IgnoreTenant("定时任务需要查询特定租户的超时订单")
public List<ShopOrder> findExpiredUnpaidOrdersByTenant(Integer tenantId, Integer timeoutMinutes, Integer batchSize) {
LocalDateTime expireTime = LocalDateTime.now().minusMinutes(timeoutMinutes);

View File

@@ -1,5 +1,6 @@
package com.gxwebsoft.shop.task;
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
import com.gxwebsoft.shop.config.OrderConfigProperties;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.service.OrderCancelService;
@@ -38,6 +39,7 @@ public class OrderAutoCancelTask {
* 开发环境每1分钟执行一次便于测试
*/
@Scheduled(cron = "${shop.order.auto-cancel.cron:0 */5 * * * ?}")
@IgnoreTenant("定时任务需要处理所有租户的超时订单")
public void cancelExpiredOrders() {
if (!orderConfig.getAutoCancel().isEnabled()) {
log.debug("订单自动取消功能已禁用");
@@ -45,7 +47,7 @@ public class OrderAutoCancelTask {
}
log.info("开始执行订单自动取消任务...");
try {
long startTime = System.currentTimeMillis();
int totalCancelledCount = 0;
@@ -61,7 +63,7 @@ public class OrderAutoCancelTask {
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
log.info("订单自动取消任务完成,总取消数量: {},默认配置: {},租户配置: {},耗时: {}ms",
log.info("订单自动取消任务完成,总取消数量: {},默认配置: {},租户配置: {},耗时: {}ms",
totalCancelledCount, defaultCancelledCount, tenantCancelledCount, duration);
// 开发环境输出更详细的日志
@@ -85,7 +87,7 @@ public class OrderAutoCancelTask {
log.debug("处理默认超时订单,超时时间: {}分钟,批量大小: {}", defaultTimeout, batchSize);
List<ShopOrder> expiredOrders = orderCancelService.findExpiredUnpaidOrders(defaultTimeout, batchSize);
if (expiredOrders.isEmpty()) {
log.debug("没有找到使用默认配置的超时订单");
return 0;
@@ -93,14 +95,14 @@ public class OrderAutoCancelTask {
// 过滤掉有特殊租户配置的订单
List<ShopOrder> ordersToCancel = filterOrdersWithoutTenantConfig(expiredOrders);
if (ordersToCancel.isEmpty()) {
log.debug("过滤后没有需要使用默认配置取消的订单");
return 0;
}
int cancelledCount = orderCancelService.batchCancelOrders(ordersToCancel);
log.info("默认配置取消订单完成,找到: {}个,过滤后: {}个,成功取消: {}个",
log.info("默认配置取消订单完成,找到: {}个,过滤后: {}个,成功取消: {}个",
expiredOrders.size(), ordersToCancel.size(), cancelledCount);
return cancelledCount;
@@ -131,7 +133,7 @@ public class OrderAutoCancelTask {
continue;
}
log.debug("处理租户{}的超时订单,超时时间: {}分钟",
log.debug("处理租户{}的超时订单,超时时间: {}分钟",
tenantConfig.getTenantId(), tenantConfig.getTimeoutMinutes());
List<ShopOrder> tenantExpiredOrders = orderCancelService.findExpiredUnpaidOrdersByTenant(
@@ -140,8 +142,8 @@ public class OrderAutoCancelTask {
if (!tenantExpiredOrders.isEmpty()) {
int cancelledCount = orderCancelService.batchCancelOrders(tenantExpiredOrders);
totalCancelledCount += cancelledCount;
log.info("租户{}取消订单完成,找到: {}个,成功取消: {}个",
log.info("租户{}取消订单完成,找到: {}个,成功取消: {}个",
tenantConfig.getTenantId(), tenantExpiredOrders.size(), cancelledCount);
}
}
@@ -175,6 +177,7 @@ public class OrderAutoCancelTask {
/**
* 手动触发订单自动取消任务(用于测试)
*/
@IgnoreTenant("手动触发的定时任务需要处理所有租户的超时订单")
public void manualCancelExpiredOrders() {
log.info("手动触发订单自动取消任务...");
cancelExpiredOrders();