在线表单发布自动生成权限字使用,精简自 tenant-admin 的同名 Mapper。为避免与 tenant-admin 同名 Mapper
+ * 的 bean 名冲突,采用 Online 前缀命名。
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+public interface OnlineSysPermCodeMapper extends BaseDaoMapper {
+
+ /**
+ * 按主键Id集合物理删除权限字。
+ * xy_sys_perm_code 上有 (perm_code, deleted_flag) 唯一索引,逻辑删除只是把 deleted_flag 置为 -1,
+ * 同一表单重复发布时第二次清理会与已存在的 -1 行冲突报 Duplicate entry。发布时自动生成的权限字属于派生数据,
+ * 无需保留删除痕迹,因此走物理删除。自定义 SQL 不受实体上 {@code @TableLogic} 的影响。
+ *
+ * @param permCodeIdList 权限字主键Id集合,不能为空。
+ * @return 删除行数。
+ */
+ int physicalDeleteByIdList(@Param("permCodeIdList") Collection permCodeIdList);
+}
diff --git a/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermCodePermMapper.java b/common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermCodePermMapper.java
similarity index 59%
rename from common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermCodePermMapper.java
rename to common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermCodePermMapper.java
index d4075b3..aeb7736 100644
--- a/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermCodePermMapper.java
+++ b/common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermCodePermMapper.java
@@ -5,10 +5,11 @@ import apelet.common.online.upms.model.SysPermCodePerm;
/**
* 权限字与权限资源关系数据访问操作接口。
- * 在线表单发布自动生成权限字使用,精简自 tenant-admin 的同名 Mapper。
+ * 在线表单发布自动生成权限字使用,精简自 tenant-admin 的同名 Mapper。为避免与 tenant-admin 同名 Mapper
+ * 的 bean 名冲突,采用 Online 前缀命名。
*
* @author guifc
* @date 2023-08-04
*/
-public interface SysPermCodePermMapper extends BaseDaoMapper {
+public interface OnlineSysPermCodePermMapper extends BaseDaoMapper {
}
diff --git a/common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermMapper.java b/common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermMapper.java
new file mode 100644
index 0000000..0c04505
--- /dev/null
+++ b/common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermMapper.java
@@ -0,0 +1,27 @@
+package apelet.common.online.upms.dao;
+
+import apelet.common.core.base.dao.BaseDaoMapper;
+import apelet.common.online.upms.model.SysPerm;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+
+/**
+ * 权限资源数据访问操作接口。
+ * 在线表单发布自动生成权限字使用,精简自 tenant-admin 的同名 Mapper。为避免与 tenant-admin 同名 Mapper
+ * 的 bean 名冲突,采用 Online 前缀命名。
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+public interface OnlineSysPermMapper extends BaseDaoMapper {
+
+ /**
+ * 按主键Id集合物理删除权限资源。
+ * 与权限字保持一致:发布时自动生成的权限资源属于派生数据,逻辑删除只会不断堆积 deleted_flag = -1 的垃圾行。
+ *
+ * @param permIdList 权限资源主键Id集合,不能为空。
+ * @return 删除行数。
+ */
+ int physicalDeleteByIdList(@Param("permIdList") Collection permIdList);
+}
diff --git a/common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermModuleMapper.java b/common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermModuleMapper.java
new file mode 100644
index 0000000..4264c63
--- /dev/null
+++ b/common/common-online/src/main/java/apelet/common/online/upms/dao/OnlineSysPermModuleMapper.java
@@ -0,0 +1,27 @@
+package apelet.common.online.upms.dao;
+
+import apelet.common.core.base.dao.BaseDaoMapper;
+import apelet.common.online.upms.model.SysPermModule;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+
+/**
+ * 权限模块数据访问操作接口。
+ * 在线表单发布自动生成权限字使用,精简自 tenant-admin 的同名 Mapper。为避免与 tenant-admin 同名 Mapper
+ * 的 bean 名冲突,采用 Online 前缀命名。
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+public interface OnlineSysPermModuleMapper extends BaseDaoMapper {
+
+ /**
+ * 按主键Id集合物理删除权限模块。
+ * 与权限字保持一致:发布时自动生成的权限模块属于派生数据,逻辑删除只会不断堆积 deleted_flag = -1 的垃圾行。
+ *
+ * @param moduleIdList 权限模块主键Id集合,不能为空。
+ * @return 删除行数。
+ */
+ int physicalDeleteByIdList(@Param("moduleIdList") Collection moduleIdList);
+}
diff --git a/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermCodeMapper.java b/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermCodeMapper.java
deleted file mode 100644
index d3225e7..0000000
--- a/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermCodeMapper.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package apelet.common.online.upms.dao;
-
-import apelet.common.core.base.dao.BaseDaoMapper;
-import apelet.common.online.upms.model.SysPermCode;
-
-/**
- * 权限字数据访问操作接口。
- * 在线表单发布自动生成权限字使用,精简自 tenant-admin 的同名 Mapper。
- *
- * @author guifc
- * @date 2023-08-04
- */
-public interface SysPermCodeMapper extends BaseDaoMapper {
-}
diff --git a/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermMapper.java b/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermMapper.java
deleted file mode 100644
index d290163..0000000
--- a/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermMapper.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package apelet.common.online.upms.dao;
-
-import apelet.common.core.base.dao.BaseDaoMapper;
-import apelet.common.online.upms.model.SysPerm;
-
-/**
- * 权限资源数据访问操作接口。
- * 在线表单发布自动生成权限字使用,精简自 tenant-admin 的同名 Mapper。
- *
- * @author guifc
- * @date 2023-08-04
- */
-public interface SysPermMapper extends BaseDaoMapper {
-}
diff --git a/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermModuleMapper.java b/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermModuleMapper.java
deleted file mode 100644
index 2cb8969..0000000
--- a/common/common-online/src/main/java/apelet/common/online/upms/dao/SysPermModuleMapper.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package apelet.common.online.upms.dao;
-
-import apelet.common.core.base.dao.BaseDaoMapper;
-import apelet.common.online.upms.model.SysPermModule;
-
-/**
- * 权限模块数据访问操作接口。
- * 在线表单发布自动生成权限字使用,精简自 tenant-admin 的同名 Mapper。
- *
- * @author guifc
- * @date 2023-08-04
- */
-public interface SysPermModuleMapper extends BaseDaoMapper {
-}
diff --git a/common/common-online/src/main/java/apelet/common/online/upms/service/impl/OnlinePermCodeServiceImpl.java b/common/common-online/src/main/java/apelet/common/online/upms/service/impl/OnlinePermCodeServiceImpl.java
index 7266b79..bf1659c 100644
--- a/common/common-online/src/main/java/apelet/common/online/upms/service/impl/OnlinePermCodeServiceImpl.java
+++ b/common/common-online/src/main/java/apelet/common/online/upms/service/impl/OnlinePermCodeServiceImpl.java
@@ -11,10 +11,10 @@ import apelet.common.online.dao.OnlinePageMapper;
import apelet.common.online.dao.OnlineTableMapper;
import apelet.common.online.model.OnlinePage;
import apelet.common.online.model.OnlineTable;
-import apelet.common.online.upms.dao.SysPermCodeMapper;
-import apelet.common.online.upms.dao.SysPermCodePermMapper;
-import apelet.common.online.upms.dao.SysPermMapper;
-import apelet.common.online.upms.dao.SysPermModuleMapper;
+import apelet.common.online.upms.dao.OnlineSysPermCodeMapper;
+import apelet.common.online.upms.dao.OnlineSysPermCodePermMapper;
+import apelet.common.online.upms.dao.OnlineSysPermMapper;
+import apelet.common.online.upms.dao.OnlineSysPermModuleMapper;
import apelet.common.online.upms.model.SysPerm;
import apelet.common.online.upms.model.SysPermCode;
import apelet.common.online.upms.model.SysPermCodePerm;
@@ -27,11 +27,12 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
+import javax.annotation.PreDestroy;
import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -61,6 +62,16 @@ public class OnlinePermCodeServiceImpl implements OnlinePermCodeService {
@Value("${common-online.urlPrefix:}")
private String urlPrefix;
+ /**
+ * 权限字生成/清理专用线程池,不依赖Spring异步体系,避免影响应用原有线程池配置。
+ */
+ private final ExecutorService permCodeExecutor = Executors.newFixedThreadPool(2, r -> {
+ Thread thread = new Thread(r);
+ thread.setName("perm-code-" + thread.getId());
+ thread.setDaemon(true);
+ return thread;
+ });
+
@Autowired
private OnlinePageMapper onlinePageMapper;
@Autowired
@@ -68,42 +79,58 @@ public class OnlinePermCodeServiceImpl implements OnlinePermCodeService {
@Autowired
private OnlineTableMapper onlineTableMapper;
@Autowired
- private SysPermModuleMapper sysPermModuleMapper;
+ private OnlineSysPermModuleMapper onlineSysPermModuleMapper;
@Autowired
- private SysPermMapper sysPermMapper;
+ private OnlineSysPermMapper onlineSysPermMapper;
@Autowired
- private SysPermCodeMapper sysPermCodeMapper;
+ private OnlineSysPermCodeMapper onlineSysPermCodeMapper;
@Autowired
- private SysPermCodePermMapper sysPermCodePermMapper;
+ private OnlineSysPermCodePermMapper onlineSysPermCodePermMapper;
@Autowired
private IdGeneratorWrapper idGenerator;
/**
* 发布后异步生成权限字,先生成前先清理旧数据保证幂等。
*/
- @Async("permCodeTaskExecutor")
- @Transactional(rollbackFor = Exception.class)
@Override
public void genPermCodeByPageId(Long pageId, Long userId) {
- try {
- this.doGenPermCode(pageId, userId);
- } catch (Exception e) {
- log.error("在线表单发布生成权限字失败,pageId={}", pageId, e);
- }
+ permCodeExecutor.execute(() -> {
+ // 异步线程没有请求上下文,需显式指定在线表单数据源。
+ Integer originalType = DefaultDataSourceResolver.setDataSourceType(ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE);
+ try {
+ this.doGenPermCode(pageId, userId);
+ } catch (Exception e) {
+ log.error("在线表单发布生成权限字失败,pageId={}", pageId, e);
+ } finally {
+ DefaultDataSourceResolver.unset(originalType);
+ }
+ });
}
/**
* 删除表单后异步清理权限字相关数据。
*/
- @Async("permCodeTaskExecutor")
- @Transactional(rollbackFor = Exception.class)
@Override
public void removePermCodeByPageCode(String pageCode) {
- try {
- this.doRemovePermCode(pageCode);
- } catch (Exception e) {
- log.error("在线表单删除清理权限字失败,pageCode={}", pageCode, e);
- }
+ permCodeExecutor.execute(() -> {
+ // 异步线程没有请求上下文,需显式指定在线表单数据源。
+ Integer originalType = DefaultDataSourceResolver.setDataSourceType(ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE);
+ try {
+ this.doRemovePermCode(pageCode);
+ } catch (Exception e) {
+ log.error("在线表单删除清理权限字失败,pageCode={}", pageCode, e);
+ } finally {
+ DefaultDataSourceResolver.unset(originalType);
+ }
+ });
+ }
+
+ /**
+ * 应用关闭时释放线程池。
+ */
+ @PreDestroy
+ public void shutdownExecutor() {
+ permCodeExecutor.shutdown();
}
private void doGenPermCode(Long pageId, Long userId) {
@@ -149,15 +176,15 @@ public class OnlinePermCodeServiceImpl implements OnlinePermCodeService {
Long operatorUserId = userId != null ? userId : 0L;
// 权限模块:moduleType=0 根模块 + moduleType=1 子模块,所有权限挂在子模块下。
SysPermModule rootModule = this.buildModule(0L, 0, pageName, 1, operatorUserId, now);
- sysPermModuleMapper.insert(rootModule);
+ onlineSysPermModuleMapper.insert(rootModule);
SysPermModule childModule = this.buildModule(rootModule.getModuleId(), 1, pageName, 1, operatorUserId, now);
- sysPermModuleMapper.insert(childModule);
+ onlineSysPermModuleMapper.insert(childModule);
// 权限字树:type=0 根 -> type=1 片段 -> type=2 操作。
SysPermCode code0 = this.buildPermCode(0L, pageCode, 0, pageName, 1, operatorUserId, now);
- sysPermCodeMapper.insert(code0);
+ onlineSysPermCodeMapper.insert(code0);
String code1Str = pageCode + ":" + pageCode;
SysPermCode code1 = this.buildPermCode(code0.getPermCodeId(), code1Str, 1, pageName, 1, operatorUserId, now);
- sysPermCodeMapper.insert(code1);
+ onlineSysPermCodeMapper.insert(code1);
// 每个表(主表+附表)各生成下载/上传两条 perm、两条操作权限字及其关联。
for (OnlFormHead head : headList) {
if (StrUtil.isBlank(head.getTableName())) {
@@ -176,13 +203,13 @@ public class OnlinePermCodeServiceImpl implements OnlinePermCodeService {
String uploadUrl = this.makeUrl(isMain ? UPLOAD_DATASOURCE : UPLOAD_ONE_TO_MANY, mainModelName);
String code2Base = code1Str + ":" + modelName;
SysPerm downloadPerm = this.buildPerm(childModule.getModuleId(), permNameBase + "下载文件", downloadUrl, 1, operatorUserId, now);
- sysPermMapper.insert(downloadPerm);
+ onlineSysPermMapper.insert(downloadPerm);
SysPerm uploadPerm = this.buildPerm(childModule.getModuleId(), permNameBase + "上传文件", uploadUrl, 2, operatorUserId, now);
- sysPermMapper.insert(uploadPerm);
+ onlineSysPermMapper.insert(uploadPerm);
SysPermCode code2Download = this.buildPermCode(code1.getPermCodeId(), code2Base + "download", 2, permNameBase + "下载文件", 1, operatorUserId, now);
- sysPermCodeMapper.insert(code2Download);
+ onlineSysPermCodeMapper.insert(code2Download);
SysPermCode code2Upload = this.buildPermCode(code1.getPermCodeId(), code2Base + "upload", 2, permNameBase + "上传文件", 2, operatorUserId, now);
- sysPermCodeMapper.insert(code2Upload);
+ onlineSysPermCodeMapper.insert(code2Upload);
this.insertPermCodePerm(code2Download.getPermCodeId(), downloadPerm.getPermId());
this.insertPermCodePerm(code2Upload.getPermCodeId(), uploadPerm.getPermId());
}
@@ -196,8 +223,7 @@ public class OnlinePermCodeServiceImpl implements OnlinePermCodeService {
if (StrUtil.isBlank(pageCode)) {
return;
}
- SysPermCode root = sysPermCodeMapper.selectOne(new QueryWrapper()
- .eq("perm_code", pageCode).eq("perm_code_type", 0));
+ SysPermCode root = onlineSysPermCodeMapper.selectOne(new QueryWrapper().eq("perm_code", pageCode).eq("perm_code_type", 0));
if (root == null) {
return;
}
@@ -207,33 +233,31 @@ public class OnlinePermCodeServiceImpl implements OnlinePermCodeService {
Set codeIdSet = new HashSet<>(codeIdList);
List parentIdList = new ArrayList<>(codeIdList);
while (CollUtil.isNotEmpty(parentIdList)) {
- List childList = sysPermCodeMapper.selectList(
- new QueryWrapper().in("parent_id", parentIdList));
+ List childList = onlineSysPermCodeMapper.selectList(new QueryWrapper().in("parent_id", parentIdList));
parentIdList = childList.stream().map(SysPermCode::getPermCodeId).collect(Collectors.toList());
codeIdSet.addAll(parentIdList);
}
- // 权限字与权限关联、权限资源及其模块。
- List relationList = sysPermCodePermMapper.selectList(
- new QueryWrapper().in("perm_code_id", codeIdSet));
+ // 权限字与权限关联、权限资源及其模块。注意 SysPermCode/SysPerm/SysPermModule 都带 @TableLogic,
+ // mapper.delete 只会把 deleted_flag 置为 -1,而 xy_sys_perm_code 上有 (perm_code, deleted_flag) 唯一索引,
+ // 同一表单重复发布时第二次清理必然撞 Duplicate entry,因此这里统一走物理删除。
+ List relationList = onlineSysPermCodePermMapper.selectList(new QueryWrapper().in("perm_code_id", codeIdSet));
if (CollUtil.isNotEmpty(relationList)) {
- sysPermCodePermMapper.delete(new QueryWrapper().in("perm_code_id", codeIdSet));
+ onlineSysPermCodePermMapper.delete(new QueryWrapper().in("perm_code_id", codeIdSet));
}
Set permIdSet = relationList.stream().map(SysPermCodePerm::getPermId).collect(Collectors.toSet());
Set moduleIdSet = new HashSet<>();
if (CollUtil.isNotEmpty(permIdSet)) {
- List permList = sysPermMapper.selectList(new QueryWrapper().in("perm_id", permIdSet));
+ List permList = onlineSysPermMapper.selectList(new QueryWrapper().in("perm_id", permIdSet));
moduleIdSet = permList.stream().map(SysPerm::getModuleId).collect(Collectors.toSet());
- sysPermMapper.delete(new QueryWrapper().in("perm_id", permIdSet));
+ onlineSysPermMapper.physicalDeleteByIdList(permIdSet);
}
- sysPermCodeMapper.delete(new QueryWrapper().in("perm_code_id", codeIdSet));
+ onlineSysPermCodeMapper.physicalDeleteByIdList(codeIdSet);
// 删除权限模块(子模块及其上级根模块)。
if (CollUtil.isNotEmpty(moduleIdSet)) {
- List moduleList = sysPermModuleMapper.selectList(
- new QueryWrapper().in("module_id", moduleIdSet));
- moduleIdSet = moduleList.stream().flatMap(m -> Stream.of(m.getModuleId(), m.getParentId()))
- .filter(Objects::nonNull).filter(id -> id != 0).collect(Collectors.toSet());
+ List moduleList = onlineSysPermModuleMapper.selectList(new QueryWrapper().in("module_id", moduleIdSet));
+ moduleIdSet = moduleList.stream().flatMap(m -> Stream.of(m.getModuleId(), m.getParentId())).filter(Objects::nonNull).filter(id -> id != 0).collect(Collectors.toSet());
if (CollUtil.isNotEmpty(moduleIdSet)) {
- sysPermModuleMapper.delete(new QueryWrapper().in("module_id", moduleIdSet));
+ onlineSysPermModuleMapper.physicalDeleteByIdList(moduleIdSet);
}
}
log.info("在线表单删除清理权限字完成,pageCode={}, 权限字数={}", pageCode, codeIdSet.size());
@@ -300,6 +324,6 @@ public class OnlinePermCodeServiceImpl implements OnlinePermCodeService {
SysPermCodePerm permCodePerm = new SysPermCodePerm();
permCodePerm.setPermCodeId(permCodeId);
permCodePerm.setPermId(permId);
- sysPermCodePermMapper.insert(permCodePerm);
+ onlineSysPermCodePermMapper.insert(permCodePerm);
}
}
diff --git a/common/common-online/src/main/java/apelet/common/online/util/OnlineOperationHelper.java b/common/common-online/src/main/java/apelet/common/online/util/OnlineOperationHelper.java
index 3d8fd37..1482d4e 100644
--- a/common/common-online/src/main/java/apelet/common/online/util/OnlineOperationHelper.java
+++ b/common/common-online/src/main/java/apelet/common/online/util/OnlineOperationHelper.java
@@ -167,7 +167,8 @@ public class OnlineOperationHelper {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
} else {
- if (value == null && !column.getNullable() && StrUtil.isBlank(column.getEncodedRule())) {
+ if (value == null && !column.getNullable() && StrUtil.isBlank(column.getEncodedRule())
+ && !FieldKind.isFileKind(column.getFieldKind())) {
errorMessage = "数据验证失败,数据表 ["
+ table.getTableName() + "] 字段 [" + column.getColumnName() + "] 不能为空值!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);