|
@@ -0,0 +1,213 @@
|
|
|
|
|
+package org.dromara.common.tenant.helper;
|
|
|
|
|
+
|
|
|
|
|
+import cn.dev33.satoken.context.SaHolder;
|
|
|
|
|
+import cn.dev33.satoken.context.model.SaStorage;
|
|
|
|
|
+import cn.hutool.core.collection.CollectionUtil;
|
|
|
|
|
+import cn.hutool.core.convert.Convert;
|
|
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
|
|
|
|
|
+import lombok.AccessLevel;
|
|
|
|
|
+import lombok.NoArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.dromara.common.core.constant.GlobalConstants;
|
|
|
|
|
+import org.dromara.common.core.context.PlatformContext;
|
|
|
|
|
+import org.dromara.common.core.utils.SpringUtils;
|
|
|
|
|
+import org.dromara.common.core.utils.StringUtils;
|
|
|
|
|
+import org.dromara.common.redis.utils.RedisUtils;
|
|
|
|
|
+import org.dromara.common.satoken.utils.LoginHelper;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.Stack;
|
|
|
|
|
+import java.util.function.Supplier;
|
|
|
|
|
+
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
|
|
|
+public class PlatformHelper {
|
|
|
|
|
+
|
|
|
|
|
+ private static final String DYNAMIC_PLATFORM_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicPlatform";
|
|
|
|
|
+
|
|
|
|
|
+ private static final ThreadLocal<String> TEMP_DYNAMIC_PLATFORM = new ThreadLocal<>();
|
|
|
|
|
+
|
|
|
|
|
+ private static final ThreadLocal<Stack<Integer>> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new);
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 平台功能是否启用
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean isEnable() {
|
|
|
|
|
+ return Convert.toBool(SpringUtils.getProperty("platform.enable"), false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 开启忽略平台(需手动调用 disableIgnore 关闭)
|
|
|
|
|
+ * 复用 tenantLine 作为平台忽略标志(与 PlatformDataScopeInterceptor 配合)
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void enableIgnore() {
|
|
|
|
|
+ InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());
|
|
|
|
|
+ Stack<Integer> reentrantStack = REENTRANT_IGNORE.get();
|
|
|
|
|
+ reentrantStack.push(reentrantStack.size() + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 关闭忽略平台
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void disableIgnore() {
|
|
|
|
|
+ IgnoreStrategy current = InterceptorIgnoreHelper.getIgnoreStrategy("tenant");
|
|
|
|
|
+ if (current == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Stack<Integer> reentrantStack = REENTRANT_IGNORE.get();
|
|
|
|
|
+ boolean isOutermost = reentrantStack.isEmpty() || reentrantStack.pop() == 1;
|
|
|
|
|
+
|
|
|
|
|
+ if (!isOutermost) {
|
|
|
|
|
+ // 嵌套调用,仅关闭 tenantLine
|
|
|
|
|
+ current.setTenantLine(false);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否还有其他忽略项
|
|
|
|
|
+ boolean hasOtherIgnore = Boolean.TRUE.equals(current.getDynamicTableName())
|
|
|
|
|
+ || Boolean.TRUE.equals(current.getBlockAttack())
|
|
|
|
|
+ || Boolean.TRUE.equals(current.getIllegalSql())
|
|
|
|
|
+ || Boolean.TRUE.equals(current.getDataPermission())
|
|
|
|
|
+ || CollectionUtil.isNotEmpty(current.getOthers());
|
|
|
|
|
+
|
|
|
|
|
+ if (hasOtherIgnore) {
|
|
|
|
|
+ // 保留其他忽略项,仅关闭 tenantLine
|
|
|
|
|
+ current.setTenantLine(false);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 完全无忽略项,清除整个策略
|
|
|
|
|
+ InterceptorIgnoreHelper.clearIgnoreStrategy();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在忽略平台上下文中执行(无返回值)
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void ignore(Runnable handle) {
|
|
|
|
|
+ enableIgnore();
|
|
|
|
|
+ try {
|
|
|
|
|
+ handle.run();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ disableIgnore();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在忽略平台上下文中执行(有返回值)
|
|
|
|
|
+ */
|
|
|
|
|
+ public static <T> T ignore(Supplier<T> handle) {
|
|
|
|
|
+ enableIgnore();
|
|
|
|
|
+ try {
|
|
|
|
|
+ return handle.get();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ disableIgnore();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设置动态平台(仅当前线程或全局)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param platformCode 平台编码,如 "web", "app"
|
|
|
|
|
+ * @param global 是否全局生效(需用户已登录)
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void setDynamic(String platformCode, boolean global) {
|
|
|
|
|
+ if (!isEnable()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!global || !LoginHelper.isLogin()) {
|
|
|
|
|
+ TEMP_DYNAMIC_PLATFORM.set(platformCode);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String cacheKey = DYNAMIC_PLATFORM_KEY + ":" + LoginHelper.getUserId();
|
|
|
|
|
+ RedisUtils.setCacheObject(cacheKey, platformCode);
|
|
|
|
|
+ SaHolder.getStorage().set(cacheKey, platformCode);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static void setDynamic(String platformCode) {
|
|
|
|
|
+ setDynamic(platformCode, false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取动态平台
|
|
|
|
|
+ */
|
|
|
|
|
+ public static String getDynamic() {
|
|
|
|
|
+ if (!isEnable()) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ String platform = TEMP_DYNAMIC_PLATFORM.get();
|
|
|
|
|
+ if (StringUtils.isNotBlank(platform)) {
|
|
|
|
|
+ return platform;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ SaStorage storage = SaHolder.getStorage();
|
|
|
|
|
+ if (storage == null || LoginHelper.getUserId() == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String cacheKey = DYNAMIC_PLATFORM_KEY + ":" + LoginHelper.getUserId();
|
|
|
|
|
+ platform = storage.getString(cacheKey);
|
|
|
|
|
+ if (StringUtils.isNotBlank(platform)) {
|
|
|
|
|
+ return "-1".equals(platform) ? null : platform;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ platform = RedisUtils.getCacheObject(cacheKey);
|
|
|
|
|
+ storage.set(cacheKey, StringUtils.isBlank(platform) ? "-1" : platform);
|
|
|
|
|
+ return platform;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 清除动态平台
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void clearDynamic() {
|
|
|
|
|
+ if (!isEnable()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ TEMP_DYNAMIC_PLATFORM.remove();
|
|
|
|
|
+
|
|
|
|
|
+ SaStorage storage = SaHolder.getStorage();
|
|
|
|
|
+ if (storage != null && LoginHelper.getUserId() != null) {
|
|
|
|
|
+ String cacheKey = DYNAMIC_PLATFORM_KEY + ":" + LoginHelper.getUserId();
|
|
|
|
|
+ RedisUtils.deleteObject(cacheKey);
|
|
|
|
|
+ storage.delete(cacheKey);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 在指定动态平台下执行(自动清理)
|
|
|
|
|
+ */
|
|
|
|
|
+ public static void dynamic(String platformCode, Runnable handle) {
|
|
|
|
|
+ setDynamic(platformCode);
|
|
|
|
|
+ try {
|
|
|
|
|
+ handle.run();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ clearDynamic();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static <T> T dynamic(String platformCode, Supplier<T> handle) {
|
|
|
|
|
+ setDynamic(platformCode);
|
|
|
|
|
+ try {
|
|
|
|
|
+ return handle.get();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ clearDynamic();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取当前平台(动态平台优先,否则从 PlatformContext 获取)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 当前生效的 platformCode
|
|
|
|
|
+ */
|
|
|
|
|
+ public static String getPlatform() {
|
|
|
|
|
+ if (!isEnable()) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ String platform = getDynamic();
|
|
|
|
|
+ if (StringUtils.isBlank(platform)) {
|
|
|
|
|
+ platform = PlatformContext.getPlatform();
|
|
|
|
|
+ }
|
|
|
|
|
+ return platform;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|