完成自动取消订单任务功能
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.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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user