Compare commits

...

27 Commits

Author SHA1 Message Date
chenchuchuan cefb7ecf67 feat(common): 添加重置行数据功能和自定义页面跳转支持 2 months ago
chenchuchuan bb30a3454a feat(online): 添加在线字典映射获取功能和重置页面类型 2 months ago
chenchuchuan 1bae360e2a refactor(permission): 将按钮权限检查器和相关服务迁移至公共模块 2 months ago
chenchuchuan f9ecb2aaef refactor(permission): 将权限数据规则方案控制器迁移至公共模块 2 months ago
chenchuchuan bc20c40671 refactor(orm): 优化数据权限规则和部门管理功能 2 months ago
chenchuchuan 7045cdea35 fix(controller): 修复权限数据规则模式控制器中的参数绑定和空指针问题 2 months ago
chenchuchuan 9ec9758bd5 refactor(upms): 重构角色组织关联逻辑并优化用户角色分配 2 months ago
chenchuchuan 88d393d1f7 feat(role): 角色模块支持组织多对多关联 2 months ago
chenchuchuan b2dc6af80f refactor(tenant): 重构角色表单按钮权限配置服务 2 months ago
chenchuchuan b6e8b1914e refactor(tenant): 重构按钮权限配置服务以支持多角色合并 2 months ago
chenchuchuan f3aa6ab636 Merge branch 'online-form-direct-creation' of http://124.222.6.97:3000/sunquan/apelet_tenant into feature/form-permission-control 2 months ago
chenchuchuan 65f2f7938d Merge branch 'master' of http://124.222.6.97:3000/sunquan/apelet_tenant into feature/form-permission-control 2 months ago
chenchuchuan 1f96a131a6 refactor(permission): 将权限数据规则方案相关代码迁移到common-online模块 2 months ago
chenchuchuan b8a4b5727f refactor(permission): 将权限数据规则方案相关代码迁移到common-online模块 2 months ago
chenchuchuan cb1b2dd9c3 feat(role): 添加角色按钮权限配置功能 2 months ago
chenchuchuan eadce13994 feat(upms): 添加部门组织类型支持及角色用户关联功能 2 months ago
chenchuchuan 35ee89c750 feat(tenant): 添加在线表单按钮权限控制功能 2 months ago
chenchuchuan 9f5efe49e3 feat(tenant): 添加权限数据规则方案管理功能 2 months ago
chenchuchuan 3104f1b026 refactor(common-generator): 优化在线表单头服务实现 2 months ago
chenchuchuan 6fc400ab77 feat(online): 表单字段保存时,修改字段备注取值逻辑 2 months ago
chenchuchuan e9c3b36512 feat(online): 添加数据表字段保存功能 2 months ago
chenchuchuan 7dcb3bb5c9 refactor(online): 使用常量替换硬编码的元数据源字符串 2 months ago
chenchuchuan f405113552 fix(controller): 修正在线页面控制器返回类型 2 months ago
sunquan 15dd345744 fix:补在线表单控件和数据库字段类型 2 months ago
sunquan 1d42dfc702 fix:去掉nacos配置 app-config、t-admin-dev 2 months ago
sunquan c665cde835 fix:去掉nacos配置 2 months ago
sunquan 610a4d10ed feature-online-form-direct-creation:收到请求后自动创建数据库物理表 + 元数据记录 + 数据源配置 2 months ago
  1. 23
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java
  2. 8
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysDeptController.java
  3. 64
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysRoleController.java
  4. 119
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java
  5. 23
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/RoleDeptItemDto.java
  6. 6
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDeptDto.java
  7. 1
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDept.java
  8. 6
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUserRole.java
  9. 23
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysRoleService.java
  10. 36
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java
  11. 15
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysDeptServiceImpl.java
  12. 62
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysRoleServiceImpl.java
  13. 120
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java
  14. 6
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDeptVo.java
  15. 6
      application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysRoleVo.java
  16. 161
      application-tenant/tenant-admin/src/main/resources/application-config.yml
  17. 22
      application-tenant/tenant-admin/src/main/resources/bootstrap.yml
  18. 82
      application-tenant/tenant-admin/src/main/resources/logback-spring.xml
  19. 355
      application-tenant/tenant-admin/src/main/resources/tenant-admin-dev.yml
  20. 7
      common/common-generator/src/main/java/apelet/common/generator/controller/OnlFormHeadController.java
  21. 3
      common/common-generator/src/main/java/apelet/common/generator/model/OnlCgreportItem.java
  22. 2
      common/common-generator/src/main/java/apelet/common/generator/model/OnlFormHead.java
  23. 11
      common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java
  24. 39
      common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java
  25. 18
      common/common-online/src/main/java/apelet/common/online/abstractplugin/ExecutePluginParent.java
  26. 33
      common/common-online/src/main/java/apelet/common/online/abstractplugin/ListDataOrmService.java
  27. 10
      common/common-online/src/main/java/apelet/common/online/abstractplugin/ListDataService.java
  28. 1
      common/common-online/src/main/java/apelet/common/online/abstractplugin/model/ListColumn.java
  29. 455
      common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java
  30. 95
      common/common-online/src/main/java/apelet/common/online/controller/OnlineOperationController.java
  31. 91
      common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java
  32. 184
      common/common-online/src/main/java/apelet/common/online/controller/PermissionDataRuleSchemaController.java
  33. 117
      common/common-online/src/main/java/apelet/common/online/controller/RoleFormButtonController.java
  34. 27
      common/common-online/src/main/java/apelet/common/online/dao/PermissionDataRuleSchemaMapper.java
  35. 23
      common/common-online/src/main/java/apelet/common/online/dao/RoleFormButtonMapper.java
  36. 14
      common/common-online/src/main/java/apelet/common/online/dao/mapper/OnlineFormMapper.xml
  37. 27
      common/common-online/src/main/java/apelet/common/online/dao/mapper/PermissionDataRuleSchemaMapper.xml
  38. 21
      common/common-online/src/main/java/apelet/common/online/dao/mapper/RoleFormButtonMapper.xml
  39. 14
      common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java
  40. 35
      common/common-online/src/main/java/apelet/common/online/dto/RoleFormButtonDto.java
  41. 13
      common/common-online/src/main/java/apelet/common/online/model/OnlineForm.java
  42. 138
      common/common-online/src/main/java/apelet/common/online/model/PermissionDataRuleSchema.java
  43. 69
      common/common-online/src/main/java/apelet/common/online/model/RoleFormButton.java
  44. 1
      common/common-online/src/main/java/apelet/common/online/model/constant/AttributeEnum.java
  45. 35
      common/common-online/src/main/java/apelet/common/online/model/constant/DataRuleType.java
  46. 1
      common/common-online/src/main/java/apelet/common/online/model/constant/ShowTypeEnum.java
  47. 20
      common/common-online/src/main/java/apelet/common/online/service/FormButtonPermissionChecker.java
  48. 49
      common/common-online/src/main/java/apelet/common/online/service/IPermissionDataRuleSchemaService.java
  49. 84
      common/common-online/src/main/java/apelet/common/online/service/IRoleFormButtonService.java
  50. 10
      common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java
  51. 17
      common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java
  52. 3
      common/common-online/src/main/java/apelet/common/online/service/OnlineDictService.java
  53. 8
      common/common-online/src/main/java/apelet/common/online/service/OnlineFormService.java
  54. 7
      common/common-online/src/main/java/apelet/common/online/service/OnlinePageService.java
  55. 69
      common/common-online/src/main/java/apelet/common/online/service/impl/FormButtonPermissionCheckerImpl.java
  56. 29
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java
  57. 218
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java
  58. 6
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java
  59. 20
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDictServiceImpl.java
  60. 20
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlineFormServiceImpl.java
  61. 5
      common/common-online/src/main/java/apelet/common/online/service/impl/OnlinePageServiceImpl.java
  62. 93
      common/common-online/src/main/java/apelet/common/online/service/impl/PermissionDataRuleSchemaServiceImpl.java
  63. 280
      common/common-online/src/main/java/apelet/common/online/service/impl/RoleFormButtonServiceImpl.java
  64. 87
      common/common-online/src/main/java/apelet/common/online/util/WidgetFieldTypeMapping.java
  65. 93
      common/common-online/src/main/java/apelet/common/online/util/WidgetJsonButtonParser.java
  66. 19
      common/common-online/src/main/java/apelet/common/online/vo/PermissionDataRuleSchemaVo.java
  67. 17
      common/common-online/src/main/java/apelet/common/online/vo/RoleFormButtonVo.java
  68. 3
      common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java

23
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java

@ -8,7 +8,10 @@ import apelet.common.core.annotation.NoAuthInterface; @@ -8,7 +8,10 @@ import apelet.common.core.annotation.NoAuthInterface;
import apelet.common.core.constant.ApplicationConstant;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.object.*;
import apelet.common.core.upload.*;
import apelet.common.core.upload.BaseUpDownloader;
import apelet.common.core.upload.UpDownloaderFactory;
import apelet.common.core.upload.UploadResponseInfo;
import apelet.common.core.upload.UploadStoreInfo;
import apelet.common.core.util.*;
import apelet.common.datafilter.config.DataFilterProperties;
import apelet.common.eas.util.EasConfig;
@ -21,13 +24,19 @@ import apelet.common.mobile.model.MobileEntry; @@ -21,13 +24,19 @@ import apelet.common.mobile.model.MobileEntry;
import apelet.common.mobile.object.MobileEntryExtraData;
import apelet.common.mobile.service.MobileEntryService;
import apelet.common.online.service.OnlineOperationService;
import apelet.common.orm.impl.*;
import apelet.common.orm.impl.Filter;
import apelet.common.orm.impl.FilterItem;
import apelet.common.orm.impl.Selector;
import apelet.common.orm.impl.SelectorItem;
import apelet.common.redis.cache.SessionCacheHelper;
import apelet.common.report.service.ReportOperationService;
import apelet.tenantadmin.config.ApplicationConfig;
import apelet.tenantadmin.upms.bo.SysMenuPerm;
import apelet.tenantadmin.upms.model.*;
import apelet.tenantadmin.upms.model.constant.*;
import apelet.tenantadmin.upms.model.constant.SysMenuType;
import apelet.tenantadmin.upms.model.constant.SysOnlineMenuPermType;
import apelet.tenantadmin.upms.model.constant.SysUserStatus;
import apelet.tenantadmin.upms.model.constant.SysUserType;
import apelet.tenantadmin.upms.service.*;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
@ -69,7 +78,7 @@ import java.nio.charset.StandardCharsets; @@ -69,7 +78,7 @@ import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import apelet.common.eas.util.EasUtil;
/**
* 登录接口控制器类
*
@ -1251,9 +1260,10 @@ public class LoginController { @@ -1251,9 +1260,10 @@ public class LoginController {
Set<Long> postIdSet = userPostList.stream().map(SysUserPost::getPostId).collect(Collectors.toSet());
tokenData.setPostIds(StrUtil.join(",", postIdSet));
}
// 2026-07-28 角色用户关联表,增加组织,导致角色id 可能为空
List<SysUserRole> userRoleList = sysRoleService.getSysUserRoleListByUserId(user.getUserId());
if (CollUtil.isNotEmpty(userRoleList)) {
Set<Long> userRoleIdSet = userRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
Set<Long> userRoleIdSet = userRoleList.stream().map(SysUserRole::getRoleId).filter(Objects::nonNull).collect(Collectors.toSet());
tokenData.setRoleIds(StrUtil.join(",", userRoleIdSet));
}
return tokenData;
@ -1281,9 +1291,10 @@ public class LoginController { @@ -1281,9 +1291,10 @@ public class LoginController {
Set<Long> postIdSet = userPostList.stream().map(SysUserPost::getPostId).collect(Collectors.toSet());
tokenData.setPostIds(StrUtil.join(",", postIdSet));
}
// 2026-07-28 角色用户关联表,增加组织,导致角色id 可能为空
List<SysUserRole> userRoleList = sysRoleService.getSysUserRoleListByUserId(user.getUserId());
if (CollUtil.isNotEmpty(userRoleList)) {
Set<Long> userRoleIdSet = userRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
Set<Long> userRoleIdSet = userRoleList.stream().map(SysUserRole::getRoleId).filter(Objects::nonNull).collect(Collectors.toSet());
tokenData.setRoleIds(StrUtil.join(",", userRoleIdSet));
}
return tokenData;

8
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysDeptController.java

@ -29,7 +29,10 @@ import org.springdoc.api.annotations.ParameterObject; @@ -29,7 +29,10 @@ import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -145,6 +148,9 @@ public class SysDeptController { @@ -145,6 +148,9 @@ public class SysDeptController {
}
SysDept sysDeptFilter = MyModelUtil.copyTo(sysDeptDtoFilter, SysDept.class);
String orderBy = MyOrderParam.buildOrderBy(orderParam, SysDept.class);
if (sysDeptFilter == null){
sysDeptFilter = new SysDept();
}
List<SysDept> sysDeptList = sysDeptService.getSysDeptListWithRelation(sysDeptFilter, orderBy);
return ResponseResult.success(MyPageUtil.makeResponseData(sysDeptList, SysDept.INSTANCE));
}

64
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysRoleController.java

@ -9,6 +9,8 @@ import apelet.common.core.util.MyPageUtil; @@ -9,6 +9,8 @@ import apelet.common.core.util.MyPageUtil;
import apelet.common.core.validator.UpdateGroup;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.online.dto.RoleFormButtonDto;
import apelet.common.online.service.IRoleFormButtonService;
import apelet.tenantadmin.upms.dto.SysRoleDto;
import apelet.tenantadmin.upms.dto.SysUserDto;
import apelet.tenantadmin.upms.model.SysRole;
@ -16,6 +18,7 @@ import apelet.tenantadmin.upms.model.SysUser; @@ -16,6 +18,7 @@ import apelet.tenantadmin.upms.model.SysUser;
import apelet.tenantadmin.upms.model.SysUserRole;
import apelet.tenantadmin.upms.service.SysRoleService;
import apelet.tenantadmin.upms.service.SysUserService;
import apelet.tenantadmin.upms.vo.SysDeptVo;
import apelet.tenantadmin.upms.vo.SysRoleVo;
import apelet.tenantadmin.upms.vo.SysUserVo;
import com.alibaba.fastjson.TypeReference;
@ -48,19 +51,24 @@ public class SysRoleController { @@ -48,19 +51,24 @@ public class SysRoleController {
private SysRoleService sysRoleService;
@Autowired
private SysUserService sysUserService;
@Autowired
private IRoleFormButtonService roleFormButtonService;
/**
* 新增角色操作
*
* @param sysRoleDto 新增角色对象
* @param menuIdListString 与当前角色Id绑定的menuId列表多个menuId之间逗号分隔
* @param buttonConfigList 按钮权限配置列表可选不传不影响原有逻辑
* @return 应答结果对象包含新增角色的主键Id
*/
@ApiOperationSupport(ignoreParameters = {"sysRoleDto.roleId", "sysRoleDto.createTimeStart", "sysRoleDto.createTimeEnd"})
@OperationLog(type = SysOperationLogType.ADD)
@PostMapping("/add")
public ResponseResult<Long> add(
@MyRequestBody SysRoleDto sysRoleDto, @MyRequestBody String menuIdListString) {
@MyRequestBody SysRoleDto sysRoleDto,
@MyRequestBody String menuIdListString,
@MyRequestBody List<RoleFormButtonDto> buttonConfigList) {
String errorMessage = MyCommonUtil.getModelValidationError(sysRoleDto);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
@ -75,6 +83,11 @@ public class SysRoleController { @@ -75,6 +83,11 @@ public class SysRoleController {
menuIdSet = result.getData().getObject("menuIdSet", new TypeReference<Set<Long>>(){});
}
sysRoleService.saveNew(sysRole, menuIdSet);
// 按钮权限配置(可选,不传不影响原有逻辑)
if (buttonConfigList != null && !buttonConfigList.isEmpty()) {
buttonConfigList.forEach(dto -> dto.setRoleId(sysRole.getRoleId()));
roleFormButtonService.batchSaveButtonConfig(buttonConfigList);
}
return ResponseResult.success(sysRole.getRoleId());
}
@ -83,12 +96,15 @@ public class SysRoleController { @@ -83,12 +96,15 @@ public class SysRoleController {
*
* @param sysRoleDto 更新角色对象
* @param menuIdListString 与当前角色Id绑定的menuId列表多个menuId之间逗号分隔
* @param buttonConfigList 按钮权限配置列表可选不传不影响原有逻辑
* @return 应答结果对象
*/
@OperationLog(type = SysOperationLogType.UPDATE)
@PostMapping("/update")
public ResponseResult<Void> update(
@MyRequestBody SysRoleDto sysRoleDto, @MyRequestBody String menuIdListString) {
@MyRequestBody SysRoleDto sysRoleDto,
@MyRequestBody String menuIdListString,
@MyRequestBody List<RoleFormButtonDto> buttonConfigList) {
String errorMessage = MyCommonUtil.getModelValidationError(sysRoleDto, Default.class, UpdateGroup.class);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
@ -111,6 +127,11 @@ public class SysRoleController { @@ -111,6 +127,11 @@ public class SysRoleController {
errorMessage = "更新失败,数据不存在,请刷新后重试!";
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
}
// 按钮权限配置(可选,不传不影响原有逻辑)
if (buttonConfigList != null && !buttonConfigList.isEmpty()) {
buttonConfigList.forEach(dto -> dto.setRoleId(sysRole.getRoleId()));
roleFormButtonService.batchSaveButtonConfig(buttonConfigList);
}
return ResponseResult.success();
}
@ -173,9 +194,11 @@ public class SysRoleController { @@ -173,9 +194,11 @@ public class SysRoleController {
}
SysRole sysRole = sysRoleService.getByIdWithRelation(roleId, MyRelationParam.full());
if (sysRole == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
SysRoleVo sysRoleVo = MyModelUtil.copyTo(sysRole, SysRoleVo.class);
// 填充关联的组织列表(多对多,仅需要deptId和deptName)
sysRoleVo.setDeptList(sysRoleService.getRoleDept(roleId));
return ResponseResult.success(sysRoleVo);
}
@ -349,4 +372,39 @@ public class SysRoleController { @@ -349,4 +372,39 @@ public class SysRoleController {
}
return ResponseResult.success(sysRoleService.getSysPermCodeListWithDetail(roleId, permCode));
}
/**
* 查询角色关联的组织列表用于编辑页回显多对多
*
* @param roleId 角色Id
* @return 应答结果对象包含角色关联的组织信息列表
*/
@GetMapping("/getRoleDept")
public ResponseResult<List<SysDeptVo>> getRoleDept(@RequestParam Long roleId) {
if (MyCommonUtil.existBlankArgument(roleId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
List<SysDeptVo> deptVoList = sysRoleService.getRoleDept(roleId);
return ResponseResult.success(deptVoList);
}
/**
* 根据组织ID分页查询关联角色运行时使用
*
* @param deptId 组织Id
* @param orderParam 排序参数
* @param pageParam 分页参数
* @return 应答结果对象包含分页的角色列表
*/
@PostMapping("/listRoleByDeptId")
public ResponseResult<MyPageData<SysRoleVo>> listRoleByDeptId(
@MyRequestBody Long deptId,
@MyRequestBody MyOrderParam orderParam,
@MyRequestBody MyPageParam pageParam) {
if (MyCommonUtil.existBlankArgument(deptId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
MyPageData<SysRoleVo> pageData = sysRoleService.listRoleByDeptId(deptId, orderParam, pageParam);
return ResponseResult.success(pageData);
}
}

119
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java

@ -4,45 +4,41 @@ import apelet.common.core.annotation.MyRequestBody; @@ -4,45 +4,41 @@ import apelet.common.core.annotation.MyRequestBody;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.exception.MyRuntimeException;
import apelet.common.core.object.*;
import apelet.common.core.upload.*;
import apelet.common.core.upload.BaseUpDownloader;
import apelet.common.core.upload.UpDownloaderFactory;
import apelet.common.core.upload.UploadResponseInfo;
import apelet.common.core.upload.UploadStoreInfo;
import apelet.common.core.util.MyCommonUtil;
import apelet.common.core.util.MyModelUtil;
import apelet.common.core.util.MyPageUtil;
import apelet.common.eas.service.impl.EASSyncService;
import apelet.common.generator.utils.OrmGenDataSourceUtil;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.redis.cache.SessionCacheHelper;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import apelet.tenantadmin.config.ApplicationConfig;
import apelet.tenantadmin.upms.dto.RoleDeptItemDto;
import apelet.tenantadmin.upms.dto.SysUserDto;
import apelet.tenantadmin.upms.model.SysUser;
import apelet.tenantadmin.upms.service.SysUserService;
import apelet.tenantadmin.upms.vo.SysUserVo;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.alibaba.fastjson.TypeReference;
import com.github.pagehelper.page.PageMethod;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.xxl.job.core.handler.annotation.XxlJob;
import io.swagger.v3.oas.annotations.tags.Tag;
import javafx.concurrent.Task;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 用户管理操作控制器类
@ -74,6 +70,7 @@ public class SysUserController { @@ -74,6 +70,7 @@ public class SysUserController {
* @param deptPostIdListString 逗号分隔的部门岗位Id列表
* @param dataPermIdListString 逗号分隔的数据权限Id列表
* @param roleIdListString 逗号分隔的角色Id列表
* @param deptIdListString 逗号分隔的组织Id列表可选写入SysUserRole.deptId
* @return 应答结果对象包含新增用户的主键Id
*/
@ApiOperationSupport(ignoreParameters = {
@ -86,24 +83,26 @@ public class SysUserController { @@ -86,24 +83,26 @@ public class SysUserController {
@MyRequestBody SysUserDto sysUserDto,
@MyRequestBody String deptPostIdListString,
@MyRequestBody String dataPermIdListString,
@MyRequestBody String roleIdListString) {
@MyRequestBody String roleIdListString,
@MyRequestBody String deptIdListString) {
String errorMessage = MyCommonUtil.getModelValidationError(sysUserDto);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class);
CallResult result = sysUserService.verifyRelatedData(
sysUser, null, roleIdListString, deptPostIdListString, dataPermIdListString);
sysUser, null, roleIdListString, deptPostIdListString, dataPermIdListString, deptIdListString);
if (!result.isSuccess()) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage());
}
Set<Long> deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference<Set<Long>>() {
});
Set<Long> roleIdSet = result.getData().getObject("roleIdSet", new TypeReference<Set<Long>>() {
});
Set<Long> dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference<Set<Long>>() {
});
sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet);
Set<Long> deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference<Set<Long>>(){});
Set<Long> roleIdSet = result.getData().getObject("roleIdSet", new TypeReference<Set<Long>>(){});
Set<Long> dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference<Set<Long>>(){});
Set<Long> deptIdSet = null;
if (result.getData().containsKey("deptIdSet")) {
deptIdSet = result.getData().getObject("deptIdSet", new TypeReference<Set<Long>>(){});
}
sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet, deptIdSet);
return ResponseResult.success(sysUser.getUserId());
}
@ -127,17 +126,14 @@ public class SysUserController { @@ -127,17 +126,14 @@ public class SysUserController {
}
SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class);
CallResult result = sysUserService.verifyRelatedData(
sysUser, null, "1801273688285270000", "1821796396504723500", "1821788867364139000");
sysUser, null, "1801273688285270000", "1821796396504723500", "1821788867364139000", null);
if (!result.isSuccess()) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage());
}
Set<Long> deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference<Set<Long>>() {
});
Set<Long> roleIdSet = result.getData().getObject("roleIdSet", new TypeReference<Set<Long>>() {
});
Set<Long> dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference<Set<Long>>() {
});
SysUser sysUserO = sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet);
Set<Long> deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference<Set<Long>>(){});
Set<Long> roleIdSet = result.getData().getObject("roleIdSet", new TypeReference<Set<Long>>(){});
Set<Long> dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference<Set<Long>>(){});
SysUser sysUserO = sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet, null);
try {
ObjectValue userInfoObj = new ObjectValue("sys_user_info");
@ -164,6 +160,7 @@ public class SysUserController { @@ -164,6 +160,7 @@ public class SysUserController {
* @param deptPostIdListString 逗号分隔的部门岗位Id列表
* @param dataPermIdListString 逗号分隔的数据权限Id列表
* @param roleIdListString 逗号分隔的角色Id列表
* @param deptIdListString 逗号分隔的组织Id列表可选写入SysUserRole.deptId
* @return 应答结果对象
*/
@ApiOperationSupport(ignoreParameters = {
@ -175,7 +172,8 @@ public class SysUserController { @@ -175,7 +172,8 @@ public class SysUserController {
@MyRequestBody SysUserDto sysUserDto,
@MyRequestBody String deptPostIdListString,
@MyRequestBody String dataPermIdListString,
@MyRequestBody String roleIdListString) {
@MyRequestBody String roleIdListString,
@MyRequestBody String deptIdListString) {
String errorMessage = MyCommonUtil.getModelValidationError(sysUserDto, true);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
@ -186,23 +184,49 @@ public class SysUserController { @@ -186,23 +184,49 @@ public class SysUserController {
}
SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class);
CallResult result = sysUserService.verifyRelatedData(
sysUser, originalUser, roleIdListString, deptPostIdListString, dataPermIdListString);
sysUser, originalUser, roleIdListString, deptPostIdListString, dataPermIdListString, deptIdListString);
if (!result.isSuccess()) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage());
}
Set<Long> roleIdSet = result.getData().getObject("roleIdSet", new TypeReference<Set<Long>>() {
});
Set<Long> deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference<Set<Long>>() {
});
Set<Long> dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference<Set<Long>>() {
});
if (!sysUserService.update(sysUser, originalUser, roleIdSet, deptPostIdSet, dataPermIdSet)) {
Set<Long> roleIdSet = result.getData().getObject("roleIdSet", new TypeReference<Set<Long>>(){});
Set<Long> deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference<Set<Long>>(){});
Set<Long> dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference<Set<Long>>(){});
Set<Long> deptIdSet = null;
if (result.getData().containsKey("deptIdSet")) {
deptIdSet = result.getData().getObject("deptIdSet", new TypeReference<Set<Long>>(){});
}
if (!sysUserService.update(sysUser, originalUser, roleIdSet, deptPostIdSet, dataPermIdSet, deptIdSet)) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success();
}
/**
* 分配用户组织角色
* 先删除用户原有的角色关联xy_sys_user_role再按 roleDeptList 逐条写入
*
* @param userId 用户Id
* @param roleDeptList 角色-组织关联列表每项含 roleId/deptId均可为空roleId 为空则跳过
* @return 应答结果对象
*/
@OperationLog(type = SysOperationLogType.UPDATE)
@PostMapping("/assignRoleDept")
public ResponseResult<Void> assignRoleDept(@MyRequestBody Long userId, @MyRequestBody List<RoleDeptItemDto> roleDeptList) {
if (MyCommonUtil.existBlankArgument(userId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
if (!sysUserService.existId(userId)) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, "用户不存在,请刷新后重试!");
}
CallResult result = sysUserService.verifyRoleDeptMap(roleDeptList);
if (!result.isSuccess()) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage());
}
sysUserService.assignRoleDept(userId, roleDeptList);
return ResponseResult.success();
}
/**
* 删除用户管理数据
*
* @param userId 删除对象主键Id
@ -482,6 +506,21 @@ public class SysUserController { @@ -482,6 +506,21 @@ public class SysUserController {
return ResponseResult.success(resultList);
}
/**
* 查询所有组织及其下角色列表并标记用户是否拥有该角色
*
* @param userId 用户Id
* @return 应答结果对象包含组织-角色树及用户勾选状态
*/
@GetMapping("/listUserDept")
public ResponseResult<List<Map<String, Object>>> listUserDept(@RequestParam Long userId) {
if (MyCommonUtil.existBlankArgument(userId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
List<Map<String, Object>> result = sysUserService.listUserDept(userId);
return ResponseResult.success(result);
}
private ResponseResult<Void> doDelete(Long userId) {
String errorMessage;
// 验证关联Id的数据合法性

23
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/RoleDeptItemDto.java

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
package apelet.tenantadmin.upms.dto;
import lombok.Data;
/**
* 用户-角色-组织关联参数对象用于 assignRoleDept 接口接收前端传值
*
* @author chenchuchuan
* @date 2026-07-28
*/
@Data
public class RoleDeptItemDto {
/**
* 角色Id可为空为空时跳过该条记录
*/
private String roleId;
/**
* 组织Id可为空表示不限定组织
*/
private String deptId;
}

6
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/dto/SysDeptDto.java

@ -43,4 +43,10 @@ public class SysDeptDto { @@ -43,4 +43,10 @@ public class SysDeptDto {
*/
@Schema(description = "父部门Id")
private Long parentId;
/**
* 类型0-部门1-组织
*/
@Schema(description = "类型:0-部门,1-组织")
private Integer type;
}

1
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysDept.java

@ -41,7 +41,6 @@ public class SysDept { @@ -41,7 +41,6 @@ public class SysDept {
* 父部门Id
*/
private Long parentId;
/**
* 逻辑删除标记字段(1: 正常 -1: 已删除)
*/

6
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/model/SysUserRole.java

@ -25,4 +25,10 @@ public class SysUserRole { @@ -25,4 +25,10 @@ public class SysUserRole {
*/
@TableField(value = "role_id")
private Long roleId;
/**
* 组织Id可为空表示不限定组织
*/
@TableField(value = "dept_id")
private Long deptId;
}

23
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysRoleService.java

@ -2,8 +2,13 @@ package apelet.tenantadmin.upms.service; @@ -2,8 +2,13 @@ package apelet.tenantadmin.upms.service;
import apelet.common.core.base.service.IBaseService;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.MyOrderParam;
import apelet.common.core.object.MyPageData;
import apelet.common.core.object.MyPageParam;
import apelet.tenantadmin.upms.model.SysRole;
import apelet.tenantadmin.upms.model.SysUserRole;
import apelet.tenantadmin.upms.vo.SysDeptVo;
import apelet.tenantadmin.upms.vo.SysRoleVo;
import java.util.List;
import java.util.Map;
@ -104,4 +109,22 @@ public interface SysRoleService extends IBaseService<SysRole, Long> { @@ -104,4 +109,22 @@ public interface SysRoleService extends IBaseService<SysRole, Long> {
* @return 包含从角色到权限字的权限分配路径信息的查询结果列表
*/
List<Map<String, Object>> getSysPermCodeListWithDetail(Long roleId, String permCode);
/**
* 查询角色关联的组织列表用于编辑页回显多对多
*
* @param roleId 角色Id
* @return 角色关联的组织Vo列表如果不存在返回空列表
*/
List<SysDeptVo> getRoleDept(Long roleId);
/**
* 根据组织ID分页查询关联角色
*
* @param deptId 组织Id
* @param orderParam 排序参数
* @param pageParam 分页参数
* @return 分页的角色列表
*/
MyPageData<SysRoleVo> listRoleByDeptId(Long deptId, MyOrderParam orderParam, MyPageParam pageParam);
}

36
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java

@ -3,6 +3,7 @@ package apelet.tenantadmin.upms.service; @@ -3,6 +3,7 @@ package apelet.tenantadmin.upms.service;
import apelet.common.core.base.service.IBaseService;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.ResponseResult;
import apelet.tenantadmin.upms.dto.RoleDeptItemDto;
import apelet.tenantadmin.upms.model.SysUser;
import java.util.List;
@ -32,9 +33,10 @@ public interface SysUserService extends IBaseService<SysUser, Long> { @@ -32,9 +33,10 @@ public interface SysUserService extends IBaseService<SysUser, Long> {
* @param roleIdSet 用户角色Id集合
* @param deptPostIdSet 部门岗位Id集合
* @param dataPermIdSet 数据权限Id集合
* @param deptIdSet 组织Id集合写入SysUserRole.deptId
* @return 新增后的用户对象
*/
SysUser saveNew(SysUser user, Set<Long> roleIdSet, Set<Long> deptPostIdSet, Set<Long> dataPermIdSet);
SysUser saveNew(SysUser user, Set<Long> roleIdSet, Set<Long> deptPostIdSet, Set<Long> dataPermIdSet, Set<Long> deptIdSet);
/**
* 更新用户对象
@ -44,9 +46,10 @@ public interface SysUserService extends IBaseService<SysUser, Long> { @@ -44,9 +46,10 @@ public interface SysUserService extends IBaseService<SysUser, Long> {
* @param roleIdSet 用户角色Id列表
* @param deptPostIdSet 部门岗位Id集合
* @param dataPermIdSet 数据权限Id集合
* @param deptIdSet 组织Id集合写入SysUserRole.deptId
* @return 更新成功返回true否则false
*/
boolean update(SysUser user, SysUser originalUser, Set<Long> roleIdSet, Set<Long> deptPostIdSet, Set<Long> dataPermIdSet);
boolean update(SysUser user, SysUser originalUser, Set<Long> roleIdSet, Set<Long> deptPostIdSet, Set<Long> dataPermIdSet, Set<Long> deptIdSet);
/**
* 修改用户密码
@ -226,10 +229,11 @@ public interface SysUserService extends IBaseService<SysUser, Long> { @@ -226,10 +229,11 @@ public interface SysUserService extends IBaseService<SysUser, Long> {
* @param roleIds 逗号分隔的角色Id列表字符串
* @param deptPostIds 逗号分隔的部门岗位Id列表字符串
* @param dataPermIds 逗号分隔的数据权限Id列表字符串
* @param deptIds 逗号分隔的组织Id列表字符串可选写入SysUserRole.deptId
* @return 验证结果
*/
CallResult verifyRelatedData(
SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds);
SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds, String deptIds);
/**
* 根据云之家openId获取当前用户信息
@ -253,4 +257,30 @@ public interface SysUserService extends IBaseService<SysUser, Long> { @@ -253,4 +257,30 @@ public interface SysUserService extends IBaseService<SysUser, Long> {
boolean myUpdateBatch(List<SysUser> sysUserList);
ResponseResult<?> syncEasUSer();
/**
* 查询所有组织及其下角色列表并标记用户是否拥有该角色
*
* @param userId 用户Id
* @return 组织-角色树列表包含用户勾选状态
*/
List<Map<String, Object>> listUserDept(Long userId);
/**
* 验证角色-组织映射数据的合法性
*
* @param roleDeptList 角色-组织关联列表
* @return 验证结果
*/
CallResult verifyRoleDeptMap(List<RoleDeptItemDto> roleDeptList);
/**
* 分配用户组织角色
* 先删除用户原有的角色关联再按 roleDeptList 逐条写入 xy_sys_user_role
* roleId 为空则跳过该条deptId 为空则不限组织
*
* @param userId 用户Id
* @param roleDeptList 角色-组织关联列表
*/
void assignRoleDept(Long userId, List<RoleDeptItemDto> roleDeptList);
}

15
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysDeptServiceImpl.java

@ -3,14 +3,20 @@ package apelet.tenantadmin.upms.service.impl; @@ -3,14 +3,20 @@ package apelet.tenantadmin.upms.service.impl;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.core.base.service.BaseService;
import apelet.common.core.constant.GlobalDeletedFlag;
import apelet.common.core.object.*;
import apelet.common.core.object.MyOrderParam;
import apelet.common.core.object.MyPageData;
import apelet.common.core.object.MyPageParam;
import apelet.common.core.object.MyRelationParam;
import apelet.common.core.util.MyModelUtil;
import apelet.common.core.util.MyPageUtil;
import apelet.common.ext.base.BizWidgetDatasource;
import apelet.common.ext.constant.BizWidgetDatasourceType;
import apelet.common.ext.util.BizWidgetDatasourceExtHelper;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import apelet.tenantadmin.upms.dao.*;
import apelet.tenantadmin.upms.dao.SysDataPermDeptMapper;
import apelet.tenantadmin.upms.dao.SysDeptMapper;
import apelet.tenantadmin.upms.dao.SysDeptPostMapper;
import apelet.tenantadmin.upms.dao.SysDeptRelationMapper;
import apelet.tenantadmin.upms.model.*;
import apelet.tenantadmin.upms.service.SysDeptService;
import apelet.tenantadmin.upms.service.SysUserService;
@ -29,7 +35,10 @@ import org.springframework.stereotype.Service; @@ -29,7 +35,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**

62
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysRoleServiceImpl.java

@ -4,26 +4,37 @@ import apelet.common.core.annotation.MyDataSource; @@ -4,26 +4,37 @@ import apelet.common.core.annotation.MyDataSource;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.core.base.service.BaseService;
import apelet.common.core.constant.GlobalDeletedFlag;
import apelet.common.core.object.*;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.MyOrderParam;
import apelet.common.core.object.MyPageData;
import apelet.common.core.object.MyPageParam;
import apelet.common.core.util.MyModelUtil;
import apelet.common.core.util.MyPageUtil;
import apelet.common.ext.base.BizWidgetDatasource;
import apelet.common.ext.constant.BizWidgetDatasourceType;
import apelet.common.ext.util.BizWidgetDatasourceExtHelper;
import apelet.common.online.service.IRoleFormButtonService;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import apelet.tenantadmin.config.DataSourceType;
import apelet.tenantadmin.upms.dao.SysDeptMapper;
import apelet.tenantadmin.upms.dao.SysRoleMapper;
import apelet.tenantadmin.upms.dao.SysRoleMenuMapper;
import apelet.tenantadmin.upms.dao.SysUserRoleMapper;
import apelet.tenantadmin.upms.model.SysDept;
import apelet.tenantadmin.upms.model.SysRole;
import apelet.tenantadmin.upms.model.SysRoleMenu;
import apelet.tenantadmin.upms.model.SysUserRole;
import apelet.tenantadmin.upms.service.SysMenuService;
import apelet.tenantadmin.upms.service.SysRoleService;
import apelet.tenantadmin.upms.vo.SysDeptVo;
import apelet.tenantadmin.upms.vo.SysRoleVo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -55,9 +66,13 @@ public class SysRoleServiceImpl extends BaseService<SysRole, Long> implements Sy @@ -55,9 +66,13 @@ public class SysRoleServiceImpl extends BaseService<SysRole, Long> implements Sy
@Autowired
private SysMenuService sysMenuService;
@Autowired
private SysDeptMapper sysDeptMapper;
@Autowired
private IdGeneratorWrapper idGenerator;
@Autowired
private BizWidgetDatasourceExtHelper bizWidgetDatasourceExtHelper;
@Autowired
private IRoleFormButtonService roleFormButtonService;
@PostConstruct
private void registerBizWidgetDatasource() {
@ -169,6 +184,7 @@ public class SysRoleServiceImpl extends BaseService<SysRole, Long> implements Sy @@ -169,6 +184,7 @@ public class SysRoleServiceImpl extends BaseService<SysRole, Long> implements Sy
SysUserRole userRole = new SysUserRole();
userRole.setRoleId(roleId);
sysUserRoleMapper.delete(new QueryWrapper<>(userRole));
roleFormButtonService.removeByRoleId(roleId);
return true;
}
@ -266,4 +282,48 @@ public class SysRoleServiceImpl extends BaseService<SysRole, Long> implements Sy @@ -266,4 +282,48 @@ public class SysRoleServiceImpl extends BaseService<SysRole, Long> implements Sy
public List<Map<String, Object>> getSysPermCodeListWithDetail(Long roleId, String permCode) {
return sysRoleMapper.getSysPermCodeListWithDetail(roleId, permCode);
}
@Override
public List<SysDeptVo> getRoleDept(Long roleId) {
LambdaQueryWrapper<SysUserRole> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUserRole::getRoleId, roleId).isNotNull(SysUserRole::getDeptId);
List<SysUserRole> userRoleList = sysUserRoleMapper.selectList(queryWrapper);
if (CollUtil.isEmpty(userRoleList)) {
return Collections.emptyList();
}
Set<Long> deptIdSet = userRoleList.stream().map(SysUserRole::getDeptId).collect(Collectors.toSet());
List<SysDept> deptList = sysDeptMapper.selectBatchIds(deptIdSet);
if (CollUtil.isEmpty(deptList)) {
return Collections.emptyList();
}
return MyModelUtil.copyCollectionTo(deptList, SysDeptVo.class);
}
@Override
public MyPageData<SysRoleVo> listRoleByDeptId(Long deptId, MyOrderParam orderParam, MyPageParam pageParam) {
LambdaQueryWrapper<SysUserRole> roleQuery = new LambdaQueryWrapper<>();
roleQuery.eq(SysUserRole::getDeptId, deptId);
List<SysUserRole> userRoleList = sysUserRoleMapper.selectList(roleQuery);
if (CollUtil.isEmpty(userRoleList)) {
return new MyPageData<>(Collections.emptyList(), 0L);
}
Set<Long> roleIdList = userRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
// 分页查询角色,使用IN条件
if (pageParam != null) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
}
String orderBy = MyOrderParam.buildOrderBy(orderParam, SysRole.class);
LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(SysRole::getRoleId, roleIdList);
if (StrUtil.isNotBlank(orderBy)) {
queryWrapper.last("ORDER BY " + orderBy);
}
List<SysRole> roleList = sysRoleMapper.selectList(queryWrapper);
List<SysRoleVo> roleVoList = MyModelUtil.copyCollectionTo(roleList, SysRoleVo.class);
long totalCount = 0L;
if (roleList instanceof Page) {
totalCount = ((Page<SysRole>) roleList).getTotal();
}
return MyPageUtil.makeResponseData(roleVoList, totalCount);
}
}

120
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
package apelet.tenantadmin.upms.service.impl;
import apelet.common.botp.db.util.DbUtil;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.core.base.service.BaseService;
import apelet.common.core.constant.GlobalDeletedFlag;
@ -13,10 +12,9 @@ import apelet.common.eas.service.impl.EASSyncService; @@ -13,10 +12,9 @@ import apelet.common.eas.service.impl.EASSyncService;
import apelet.common.ext.base.BizWidgetDatasource;
import apelet.common.ext.constant.BizWidgetDatasourceType;
import apelet.common.ext.util.BizWidgetDatasourceExtHelper;
import apelet.common.generator.utils.OrmGenDataSourceUtil;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import apelet.tenantadmin.upms.controller.SysUserController;
import apelet.tenantadmin.upms.dao.*;
import apelet.tenantadmin.upms.dto.RoleDeptItemDto;
import apelet.tenantadmin.upms.model.*;
import apelet.tenantadmin.upms.model.constant.SysUserStatus;
import apelet.tenantadmin.upms.service.*;
@ -32,6 +30,7 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; @@ -32,6 +30,7 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
@ -73,6 +72,10 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy @@ -73,6 +72,10 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy
@Autowired
private SysDeptService sysDeptService;
@Autowired
private SysDeptMapper sysDeptMapper;
@Autowired
private SysRoleMapper sysRoleMapper;
@Autowired
private IdGeneratorWrapper idGenerator;
@Autowired
private PasswordEncoder passwordEncoder;
@ -195,7 +198,7 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy @@ -195,7 +198,7 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy
*/
@Transactional(rollbackFor = Exception.class)
@Override
public SysUser saveNew(SysUser user, Set<Long> roleIdSet, Set<Long> deptPostIdSet, Set<Long> dataPermIdSet) {
public SysUser saveNew(SysUser user, Set<Long> roleIdSet, Set<Long> deptPostIdSet, Set<Long> dataPermIdSet, Set<Long> deptIdSet) {
user.setUserId(idGenerator.nextLongId());
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setUserStatus(SysUserStatus.STATUS_NORMAL);
@ -243,7 +246,7 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy @@ -243,7 +246,7 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy
*/
@Transactional(rollbackFor = Exception.class)
@Override
public boolean update(SysUser user, SysUser originalUser, Set<Long> roleIdSet, Set<Long> deptPostIdSet, Set<Long> dataPermIdSet) {
public boolean update(SysUser user, SysUser originalUser, Set<Long> roleIdSet, Set<Long> deptPostIdSet, Set<Long> dataPermIdSet, Set<Long> deptIdSet) {
user.setLoginName(originalUser.getLoginName());
user.setPassword(originalUser.getPassword());
MyModelUtil.fillCommonsForUpdate(user, originalUser);
@ -341,6 +344,45 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy @@ -341,6 +344,45 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy
return true;
}
@Transactional(rollbackFor = Exception.class)
@Override
public CallResult verifyRoleDeptMap(List<RoleDeptItemDto> roleDeptList) {
if (CollUtil.isEmpty(roleDeptList)) {
return CallResult.ok();
}
Set<Long> roleIdSet = roleDeptList.stream().filter(i -> StrUtil.isNotBlank(i.getRoleId())).map(i -> Long.valueOf(i.getRoleId())).collect(Collectors.toSet());
if (CollUtil.isNotEmpty(roleIdSet) && !sysRoleService.existAllPrimaryKeys(roleIdSet)) {
return CallResult.error("数据验证失败,存在不合法的角色,请刷新后重试!");
}
Set<Long> deptIdSet = roleDeptList.stream().filter(i -> StrUtil.isNotBlank(i.getDeptId())).map(i -> Long.valueOf(i.getDeptId())).collect(Collectors.toSet());
if (CollUtil.isNotEmpty(deptIdSet) && !sysDeptService.existAllPrimaryKeys(deptIdSet)) {
return CallResult.error("数据验证失败,存在不合法的组织,请刷新后重试!");
}
return CallResult.ok();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void assignRoleDept(Long userId, List<RoleDeptItemDto> roleDeptList) {
// 先删除用户原有的角色关联
SysUserRole deletedUserRole = new SysUserRole();
deletedUserRole.setUserId(userId);
sysUserRoleMapper.delete(new QueryWrapper<>(deletedUserRole));
// 再按 roleDeptList 逐条写入,roleId 为空则跳过
if (CollUtil.isNotEmpty(roleDeptList)) {
for (RoleDeptItemDto item : roleDeptList) {
if (StringUtils.isAllBlank(item.getRoleId(), item.getDeptId())) {
continue;
}
SysUserRole userRole = new SysUserRole();
userRole.setUserId(userId);
userRole.setRoleId(StringUtils.isNotBlank(item.getRoleId()) ? Long.valueOf(item.getRoleId()) : null);
userRole.setDeptId(StrUtil.isNotBlank(item.getDeptId()) ? Long.valueOf(item.getDeptId()) : null);
sysUserRoleMapper.insert(userRole);
}
}
}
/**
* 获取单表查询结果由于没有关联数据查询因此在仅仅获取单表数据的场景下效率更高
* 如果需要同时获取关联数据请移步(getSysUserListWithRelation)方法
@ -530,7 +572,7 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy @@ -530,7 +572,7 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy
*/
@Override
public CallResult verifyRelatedData(
SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds) {
SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds, String deptIds) {
JSONObject jsonObject = new JSONObject();
if (StrUtil.isBlank(deptPostIds)) {
return CallResult.error("数据验证失败,用户的部门岗位数据不能为空!");
@ -563,6 +605,14 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy @@ -563,6 +605,14 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy
&& !sysDeptService.existId(sysUser.getDeptId())) {
return CallResult.error("数据验证失败,关联的用户部门Id并不存在,请刷新后重试!");
}
// 验证组织ID(可选,写入SysUserRole.deptId)
if (StrUtil.isNotBlank(deptIds)) {
Set<Long> deptIdSet = Arrays.stream(deptIds.split(",")).map(Long::valueOf).collect(Collectors.toSet());
if (!sysDeptService.existAllPrimaryKeys(deptIdSet)) {
return CallResult.error("数据验证失败,存在不合法的组织数据,请刷新后重试!");
}
jsonObject.put("deptIdSet", deptIdSet);
}
return CallResult.ok(jsonObject);
}
@ -756,5 +806,63 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy @@ -756,5 +806,63 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy
}
}
@Override
public List<Map<String, Object>> listUserDept(Long userId) {
// 1. 查询所有组织(不再区分type,部门和组织共用一张表)
List<SysDept> allDepts = sysDeptMapper.selectList(new LambdaQueryWrapper<>());
if (CollUtil.isEmpty(allDepts)) {
return Collections.emptyList();
}
// 2. 查询当前用户的角色记录(含deptId)
List<SysUserRole> userRoleList = sysUserRoleMapper.selectList(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
Set<Long> userRoleIdSet = userRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
Set<Long> userDeptIdSet = userRoleList.stream().map(SysUserRole::getDeptId).filter(Objects::nonNull).collect(Collectors.toSet());
// 3. 批量查询所有组织的角色关联(通过SysUserRole.deptId)
List<Long> allDeptIds = allDepts.stream().map(SysDept::getDeptId).collect(Collectors.toList());
LambdaQueryWrapper<SysUserRole> roleDeptQuery = new LambdaQueryWrapper<>();
roleDeptQuery.in(SysUserRole::getDeptId, allDeptIds);
List<SysUserRole> allRoleDeptList = sysUserRoleMapper.selectList(roleDeptQuery);
Map<Long, List<Long>> deptRoleMap = new HashMap<>();
Set<Long> allRoleIdSet = new HashSet<>();
if (CollUtil.isNotEmpty(allRoleDeptList)) {
for (SysUserRole ur : allRoleDeptList) {
deptRoleMap.computeIfAbsent(ur.getDeptId(), k -> new ArrayList<>()).add(ur.getRoleId());
allRoleIdSet.add(ur.getRoleId());
}
}
// 4. 批量查询所有角色
Map<Long, SysRole> roleMap = Collections.emptyMap();
if (CollUtil.isNotEmpty(allRoleIdSet)) {
List<SysRole> allRoleList = sysRoleMapper.selectBatchIds(allRoleIdSet);
roleMap = allRoleList.stream().collect(Collectors.toMap(SysRole::getRoleId, r -> r));
}
// 5. 组装结果
List<Map<String, Object>> result = new ArrayList<>();
for (SysDept dept : allDepts) {
Map<String, Object> item = new HashMap<>();
Map<String, Object> deptInfo = new HashMap<>();
deptInfo.put("deptId", dept.getDeptId());
deptInfo.put("deptName", dept.getDeptName());
item.put("deptinfo", deptInfo);
List<Long> roleIds = deptRoleMap.getOrDefault(dept.getDeptId(), Collections.emptyList());
boolean deptCheck = userDeptIdSet.contains(dept.getDeptId()) || roleIds.stream().anyMatch(userRoleIdSet::contains);
deptInfo.put("check", deptCheck);
List<Map<String, Object>> roleInfoList = new ArrayList<>();
for (Long roleId : roleIds) {
SysRole role = roleMap.get(roleId);
if (role != null) {
Map<String, Object> roleInfo = new HashMap<>();
roleInfo.put("roleId", role.getRoleId());
roleInfo.put("roleName", role.getRoleName());
roleInfo.put("check", userRoleIdSet.contains(roleId));
roleInfoList.add(roleInfo);
}
}
item.put("roleInfoList", roleInfoList);
result.add(item);
}
return result;
}
}

6
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysDeptVo.java

@ -40,6 +40,12 @@ public class SysDeptVo { @@ -40,6 +40,12 @@ public class SysDeptVo {
private Long parentId;
/**
* 类型0-部门1-组织
*/
@Schema(description = "类型:0-部门,1-组织")
private Integer type;
/**
* 创建者Id
*/
@Schema(description = "创建者Id")

6
application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/vo/SysRoleVo.java

@ -36,4 +36,10 @@ public class SysRoleVo extends BaseVo { @@ -36,4 +36,10 @@ public class SysRoleVo extends BaseVo {
*/
@Schema(description = "角色与菜单关联对象列表")
private List<Map<String, Object>> sysRoleMenuList;
/**
* 关联的组织列表仅返回deptId和deptName多对多
*/
@Schema(description = "关联的组织列表")
private List<SysDeptVo> deptList;
}

161
application-tenant/tenant-admin/src/main/resources/application-config.yml

@ -0,0 +1,161 @@ @@ -0,0 +1,161 @@
server:
tomcat:
uri-encoding: UTF-8
threads:
max: 100
min-spare: 10
servlet:
encoding:
force: true
charset: UTF-8
enabled: true
logging:
config: classpath:logback-spring.xml
spring:
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
mvc:
converters:
preferred-json-mapper: fastjson
freemarker:
template-loader-path: classpath:/template/
cache: false
charset: UTF-8
check-template-location: true
content-type: text/html
expose-request-attributes: false
expose-session-attributes: false
request-context-attribute: request
suffix: .ftl
cloud:
sentinel:
transport:
# sentinel控制台地址。
# 由于8080端口容易与其他服务端口冲突,可以在启动sentinel控制台时动态修改,如:
# java -Dserver.port=8858 -jar sentinel-dashboard-$VERSION.jar
# 改为8858后,下面的配置端口也需要改为:localhost:8858。
dashboard: localhost:8858
# feign 配置
feign:
sentinel:
enabled: false
httpclient:
enabled: true
max-connections: 200
max-connections-per-route: 50
client:
config:
default:
# 输出feignclient的日志,缺省值为none,可选值为none/basic/headers/full
# 注意:需要把feignclient类所在包的日志级别设置为debug时才生效。如:
# logging:
# level:
# com.demo.multi: info
# com.demo.multi.coursepaperinterface.client: debug
loggerLevel: full
connectTimeout: 5000
readTimeout: 10000
compression:
request:
enabled: true
response:
enabled: true
common-core:
# 可选值为 mysql / postgresql / oracle / dm8 / kingbase
databaseType: mysql
swagger:
# 当enabled为false的时候,则可禁用swagger。
enabled: true
# 工程的基础包名。
basePackage: apelet
# 工程服务的基础包名。
serviceBasePackage:
title: 小猿微服务工程
description: 小猿微服务工程详情
version: 1.0
knife4j:
# 打开knife4j增强模式。
enable: true
aliyun:
oss:
enabled: false
expireSeconds: 1000
# 下面几项均需在申请阿里云OSS后,根据自己的实际情况进行配置。
endpoint:
accessKey:
secretKey:
bucketName:
minio:
enabled: false
endpoint:
accessKey:
secretKey:
bucketName:
# 暴露监控端点
management:
metrics:
tags:
application: ${spring.application.name}
endpoints:
web:
exposure:
include: '*'
jmx:
exposure:
include: '*'
endpoint:
# 与中间件相关的健康详情也会被展示
health:
show-details: always
configprops:
# 在/actuator/configprops中,所有包含password的配置,将用 * 隐藏。
# 如果不想隐藏任何配置项的值,可以直接使用如下被注释的空值。
# keys-to-sanitize:
keys-to-sanitize: password
server:
base-path: "/"
# 存储session数据的Redis,所有服务均需要,因此放到公共配置中。
# 根据实际情况,该Redis也可以用于存储其他数据。
common-redis:
# redisson的配置。每个服务可以自己的配置文件中覆盖此选项。
redisson:
# 如果该值为false,系统将不会创建RedissionClient的bean。
enabled: true
# mode的可用值为,single/cluster/sentinel/master-slave
mode: single
# single: 单机模式
# address: redis://localhost:6379
# cluster: 集群模式
# 每个节点逗号分隔,同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
# sentinel:
# 每个节点逗号分隔,同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
# master-slave:
# 每个节点逗号分隔,第一个为主节点,其余为从节点。同时每个节点前必须以redis://开头。
# address: redis://localhost:6379,redis://localhost:6378,...
address: redis://192.168.100.25:6379
# 链接超时,单位毫秒。
timeout: 6000
# 单位毫秒。分布式锁的超时检测时长。
# 如果一次锁内操作超该毫秒数,或在释放锁之前异常退出,Redis会在该时长之后主动删除该锁使用的key。
lockWatchdogTimeout: 60000
# redis 密码,空可以不填。
password:
database: 0
pool:
# 连接池数量。
poolSize: 20
# 连接池中最小空闲数量。
minIdle: 5

22
application-tenant/tenant-admin/src/main/resources/bootstrap.yml

@ -3,20 +3,14 @@ spring: @@ -3,20 +3,14 @@ spring:
name: tenant-admin
profiles:
active: @profile.name@
cloud:
nacos:
discovery:
server-addr: @discovery.server-addr@
config:
server-addr: @config.server-addr@
group: @config.group@
namespace: @config.namespace@
file-extension: yaml
# 共享配置文件,排序越高后,优先级越高。
shared-configs:
- data-id: application-${spring.profiles.active}.yaml
group: @config.group@
refresh: true
main:
allow-bean-definition-overriding: true
allow-circular-references: true
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
config:
import:
- classpath:tenant-admin-dev.yml
- classpath:application-config.yml # 引入共享配置

82
application-tenant/tenant-admin/src/main/resources/logback-spring.xml

@ -16,6 +16,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息, @@ -16,6 +16,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息,
<property name="LOG_PATTERN" value="[%-5level] [%d{yyyy-MM-dd HH:mm:ss}] [%thread] ==> [%tid] %msg%n" />
<property name="LOG_PATTERN_EX" value="[%-5level] [%d{yyyy-MM-dd HH:mm:ss}] T:[%X{traceId}] S:[%X{sessionId}] U:[%X{userId}] [%thread] ==> [%tid] %msg%n" />
<!--
<appender name="kafka_log" class="com.github.danielwegener.logback.kafka.KafkaAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
@ -27,6 +28,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息, @@ -27,6 +28,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息,
<deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy" />
<producerConfig>bootstrap.servers=@kafka.log-server-addr@</producerConfig>
</appender>
-->
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
@ -89,48 +91,48 @@ debug:当此属性设置为true时,将打印出logback内部日志信息, @@ -89,48 +91,48 @@ debug:当此属性设置为true时,将打印出logback内部日志信息,
</logger>
<logger name="apelet" additivity="false" level="info">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<!-- 这里将dao的日志级别设置为DEBUG,是为了SQL语句的输出 -->
<!-- <appender-ref ref="kafka_log"/> -->
<appender-ref ref="file_log" />
</logger>
<!-- 这里将dao的日志级别设置为DEBUG,是为了SQL语句的输出 -->
<logger name="apelet.tenantadmin.tenant.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.tenantadmin.upms.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.tenantadmin.app.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.mobile.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.online.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.flow.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="org.flowable" additivity="false" level="info">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.report.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<appender-ref ref="kafka_log"/>
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.tenantadmin.upms.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.tenantadmin.app.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.mobile.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.online.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.flow.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="org.flowable" additivity="false" level="info">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<logger name="apelet.common.report.dao" additivity="false" level="debug">
<appender-ref ref="console" />
<!-- <appender-ref ref="kafka_log"/>-->
<appender-ref ref="file_log" />
</logger>
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>

355
application-tenant/tenant-admin/src/main/resources/tenant-admin-dev.yml

@ -0,0 +1,355 @@ @@ -0,0 +1,355 @@
logging:
level:
# 这里设置的日志级别优先于logback-spring.xml文件Loggers中的日志级别。
apelet: info
config: classpath:logback-spring.xml
server:
port: 8075
application:
# Jwt令牌加密的签名值。该值的长度要超过10个字符(过短会报错)。
tokenSigningKey: tenantadmin-signing-key-signing-key
# Jwt令牌在Http Header中的键名称。
tokenHeaderKey: Authorization
# Jwt令牌刷新后在Http Header中的键名称。
refreshedTokenHeaderKey: RefreshedToken
# Jwt令牌过期时间(毫秒)。
expiration: 72000000
# 初始化密码。
defaultUserPassword: 123456
# 缺省的文件上传根目录。
uploadFileBaseDir: ./zz-resource/upload-files/tenant-admin
# 跨域的IP(http://192.168.10.10:8086)白名单列表,多个IP之间逗号分隔(* 表示全部信任,空白表示禁用跨域信任)。
credentialIpList: "*"
# 同步租户公共管理数据到消息队列的主题,再由同步消费者服务将数据同步到目标数据库中。
tenantSyncTopic: TenantSync
# Session会话和用户和权限在Redis中的过期时间(秒)。
sessionExpiredSeconds: 86400
# Session的数据权限信息在Redis中的过期时间(秒)。
dataPermExpiredSeconds: 86460
# 是否排他登录。
excludeLogin: false
common-eas:
urlPrefix: /tenantadmin/eas
common-ext:
urlPrefix: /tenantadmin/commonext
common-mobile:
# 注意不要以反斜杠(/)结尾。
urlPrefix: /tenantadmin/mobile
# 在线表单业务数据上传资源路径
uploadFileBaseDir: ./zz-resource/upload-files/mobile
common-online:
# 注意不要以反斜杠(/)结尾。
urlPrefix: /tenantadmin/online
# 打印接口的路径,不要以反斜杠(/)结尾。
printUrlPath: /tenantadmin/report/reportPrint/print
# 在线表单业务数据上传资源路径
uploadFileBaseDir: ./zz-resource/upload-files/online
# 如果为false,在线表单模块中所有Controller接口将不能使用。
operationEnabled: true
# 1: minio 2: aliyun-oss 3: qcloud-cos。
distributeStoreType: 2
# 调用render接口时候,是否打开一级缓存加速。
enableRenderCache: false
# 脱敏字段的掩码字符,只能为单个字符。
maskChar: '*'
# 下面的url列表,请保持反斜杠(/)结尾。
viewUrlList:
- ${common-online.urlPrefix}/onlineOperation/viewByDatasourceId/
- ${common-online.urlPrefix}/onlineOperation/viewByOneToManyRelationId/
- ${common-online.urlPrefix}/onlineOperation/listByDatasourceId/
- ${common-online.urlPrefix}/onlineOperation/listByOneToManyRelationId/
- ${common-online.urlPrefix}/onlineOperation/exportByDatasourceId/
- ${common-online.urlPrefix}/onlineOperation/exportByOneToManyRelationId/
- ${common-online.urlPrefix}/onlineOperation/downloadDatasource/
- ${common-online.urlPrefix}/onlineOperation/downloadOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/print/
editUrlList:
- ${common-online.urlPrefix}/onlineOperation/addDatasource/
- ${common-online.urlPrefix}/onlineOperation/addOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/updateDatasource/
- ${common-online.urlPrefix}/onlineOperation/updateOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/deleteDatasource/
- ${common-online.urlPrefix}/onlineOperation/deleteOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/deleteBatchDatasource/
- ${common-online.urlPrefix}/onlineOperation/deleteBatchOneToManyRelation/
- ${common-online.urlPrefix}/onlineOperation/uploadDatasource/
- ${common-online.urlPrefix}/onlineOperation/uploadOneToManyRelation/
common-flow:
# 请慎重修改urlPrefix的缺省配置,注意不要以反斜杠(/)结尾。如必须修改其他路径,请同步修改数据库脚本。
urlPrefix: /tenantadmin/flow
# 如果为false,流程模块的所有Controller中的接口将不能使用。
operationEnabled: true
common-report:
# 注意不要以反斜杠(/)结尾。
urlPrefix: /tenantadmin/report
# 如果为false,报表模块的所有Controller中的接口将不能使用。
operationEnabled: true
# 该配置项仅当打印模板中,打印图片字段时,才会需要。
# 这里的url配置只是一个示例,并不能保证开箱即用,代码示例和说明可参考common-report模块
# example包内的ReportExampleController中的代码和详细说明。
imageDownloadUrl: "http://localhost:8083/tenantadmin/report/example/downloadDirectly"
# 该配置用于报表部分的数据权限过滤功能。
# 当前数据权限需要获取指定部门Ids的所有下级子部门Ids的时候,会调用该接口。
dataPermAllChildrenDeptIdUrl: "http://localhost:8083/tenantadmin/upms/sysDept/listAllChildDeptIdByParentIds"
# 当前服务是否为可视化后台服务。
isVisualization: false
# 下面的url列表,请保持反斜杠(/)结尾。
viewUrlList:
- ${common-report.urlPrefix}/reportOperation/listData/
datasync:
producer:
# upms服务作为生产者,需要打开该选项。
enabled: true
# Redisson 分布式锁的名称。
# 当存在多个发送者实例时,需要分布式锁控制只有一个实例可以执行消息流水读取并发送到MQ的操作。
distributeLockName: tenant-datasync
# 每次读取生产者消息流水的等待时间间隔,单位分钟。
fetchTransIntervalMins: 2
# 每次读取生产者消息流水的记录数量。
fetchCount: 10
rocketmq:
name-server: 127.0.0.1:9876
producer:
group: apelet_saas
datafilter:
tenant:
enabled: false
dataperm:
enabled: true
# 在拼接数据权限过滤的SQL时,我们会用到sys_dept_relation表,该表的前缀由此配置项指定。
# 如果没有前缀,请使用 "" 。
deptRelationTablePrefix: xy_
# 是否在每次执行数据权限查询过滤时,都要进行菜单Id和URL之间的越权验证。
enableMenuPermVerify: true
sequence:
# 是否使用基于美团Leaf的分布式Id生成器。
advanceIdGenerator: true
# 多个zk服务之间逗号分隔。
zkAddress: localhost:2181
# 与本机的ip一起构成zk中标识不同服务实例的key值。
idPort: 18083
# zk中生成WorkNode的路径。不同的业务可以使用不同的路径,以免冲突。
zkPath: apelet
common-log:
# 操作日志配置,对应配置文件common-log/OperationLogProperties.java
operation-log:
enabled: false
kafkaTopic: SysOperationLog-TenantAdmin
mybatis-plus:
mapper-locations: classpath:apelet/tenantadmin/tenant/dao/mapper/*Mapper.xml,apelet/tenantadmin/upms/dao/mapper/*Mapper.xml,apelet/common/report/dao/mapper/*Mapper.xml,apelet/common/datasync/dao/mapper/*Mapper.xml,apelet/common/log/dao/mapper/*Mapper.xml,apelet/common/flow/dao/mapper/*Mapper.xml,apelet/common/online/dao/mapper/*Mapper.xml,apelet/common/generator/dao/mapper/*Mapper.xml,apelet/common/botp/dao/mapper/*Mapper.xml,apelet/tenantadmin/app/dao/mapper/*Mapper.xml,apelet/common/mobile/dao/mapper/*Mapper.xml,apelet/common/eas/dao/mapper/*Mapper.xml,classpath:mapper/*Mapper.xml
type-aliases-package: apelet.tenantadmin.*.model,apelet.common.report.model,apelet.common.datasync.model,apelet.common.log.model,apelet.common.flow.model,apelet.common.online.model,apelet.common.mobile.model,apelet.common.eas.model
global-config:
db-config:
logic-delete-value: -1
logic-not-delete-value: 1
# 自动分页的配置
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: false
params: count=countSql
aj:
captcha:
# 缓存local/redis..
cache-type: redis
# 验证码类型default两种都实例化。
type: default
# 右下角水印文字(我的水印)
water-mark: 我的水印
#点选字体样式 默认Font.BOLD
font-style: 1
#点选字体字体大小
font-size: 25
# 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/jigsaw
jigsaw: classpath:images/jigsaw
# 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/pic-click
pic-click: classpath:images/pic-click
history-data-clear-enable: false
# 接口请求次数一分钟限制是否开启 true|false
req-frequency-limit-enable: false
# 验证失败5次,get接口锁定
req-get-lock-limit: 5
# 验证失败后,锁定时间间隔,s
req-get-lock-seconds: 360
# get接口一分钟内请求数限制
req-get-minute-limit: 30
# check接口一分钟内请求数限制
req-check-minute-limit: 30
# verify接口一分钟内请求数限制
req-verify-minute-limit: 60
# 校验滑动拼图允许误差偏移量(默认5像素)
slip-offset: 5
# aes加密坐标开启或者禁用(true|false)
aes-status: true
# 滑动干扰项(0/1/2)
interference-options: 2
# 开发数据库相关配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 租户管理数据源。
tenant-admin:
url: jdbc:mysql://192.168.100.35:3306/apelet_admin_xk?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: X7&9p8L2@6z4K7!8
# 租户业务平台的通用业务数据,如字典、在线表单、流程、报表的定义等数据。
tenant-common:
url: jdbc:mysql://192.168.100.35:3306/apelet_admin_xk?characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
username: root
password: X7&9p8L2@6z4K7!8
# 默认生成的操作日志数据源配置。
operation-log:
url: jdbc:mysql://192.168.100.35:3306/apelet_admin_xk?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: X7&9p8L2@6z4K7!8
driverClassName: com.mysql.cj.jdbc.Driver
name: tenant-admin
initialSize: 10
minIdle: 10
maxActive: 50
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
maxOpenPreparedStatements: 20
validationQuery: SELECT 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
filters: stat,wall
useGlobalDataSourceStat: true
web-stat-filter:
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*,/actuator/*,/ai/*"
stat-view-servlet:
enabled: true
urlPattern: /druid/*
resetEnable: true
eas:
config:
easurl:
userName:
password: 1
dcName:
dbType: 2
ltpaToken: C:\work\company\project\plat\orm\apelet_saas\common\common-eas\src\main\resources\LtpaToken.properties
#easurl: https://ehr.jztweb.com
#userName: SSQadmin
#password: SSQadmin
#dcName: EHR_001
#dbType: 2
#ltpaToken: C:\work\company\project\plat\orm\apelet_saas\common\common-eas\src\main\resources\LtpaToken_zs.properties
hubCloud:
#测试
#secret: RXmdRE0lf5ngGfLJSSj4
#appId: 501019058
secret:
appId:
dingTalk:
secret: 95gAMy_7nWGqRwFbt_kLzRpI9BqKhB7LM3SPgpcvV8Q7s0Az_PMei_yRxWE6LmhV
project:
server:
url: https://jgzy-api.nbjgcloud.com
sync:
url: http://10.22.98.37:9999
loginUrl: /sys/login
loginName: nbjg
loginPassword: NBjj123456
userUrl:
projectUrl:
ai:
dify:
baseUrl:
datasetKey:
appKey:
appUrl:
pandoc:
panurl: pandoc
genurl:
templateFileName: sample-orgin-change.docx
luaFileName: parse-html.lua
preUrl:
mineru:
workspace:
batchfile:
timeout: 10000
baseurl: http://192.168.77.6:8000
pdfurl: /file_parse
mdSaveDir: /app/work/pdf
cqurl:
config:
url:
gys:
#client_id: xy_mobile
#client_secret: asdasdnsandlkadnlkas!@#@!3WWW
#username: sunquan
client_id:
client_secret:
username:
accountId:
msg:
notice:
# 是否启用定时消息提醒(true启用,false禁用)
enabled: true
# 定时任务cron表达式,控制提醒执行频率
# 默认值:0 0 * * * ? (每小时整点执行)
# 常用示例:
# 0 0 * * * ? - 每小时执行一次
# 0 0 */2 * * ? - 每2小时执行一次
# 0 0 9 * * ? - 每天上午9点执行
# 0 0 9,18 * * ? - 每天上午9点和下午6点执行
# 0 */30 * * * ? - 每30分钟执行一次
cron: "0 0 * * * ?"
# 默认提醒消息内容
default-message: hello
# ====================================
# 短信通知配置
# ====================================
sms:
# 是否启用短信通知
enabled: false
# 短信API地址(根据实际短信服务商配置)
api-url: https://sms.example.com/send
# 短信API密钥
api-key: your-sms-api-key
# 短信签名
sign: "【您的签名】"
# ====================================
# 邮箱通知配置
# ====================================
email:
# 是否启用邮箱通知
enabled: true
# SMTP服务器地址
smtp-host: smtp.qq.com
# SMTP端口(25或465或587)
smtp-port: 465
# 发件人邮箱地址
from-email: 287962685@qq.com
# 发件人密码或授权码
password: evccxezdmdnpbhbh
# 是否启用SSL(如果使用465端口,建议启用SSL)
ssl-enabled: true

7
common/common-generator/src/main/java/apelet/common/generator/controller/OnlFormHeadController.java

@ -301,11 +301,12 @@ public class OnlFormHeadController { @@ -301,11 +301,12 @@ public class OnlFormHeadController {
*
* @param name 查询对象name
* @param type 是否查询字段关联的元数据 1: 0:
* @param isChild 是否查询子表 1: 0:
* @return
*/
@GetMapping(value = "/selectOnlFrom")
@Schema(description = "查询对象的所有元数据")
public ResponseResult<?> selectOnlFrom(@RequestParam String name, @RequestParam String type) {
public ResponseResult<?> selectOnlFrom(@RequestParam String name, @RequestParam String type, @RequestParam(required = false) Integer isChild) {
ArrayList<Object> arrayList = new ArrayList<>();
OnlFormHead onlFormHead = onlFormHeadService.getHeadTableNameCache(name);
if (onlFormHead == null) {
@ -321,8 +322,8 @@ public class OnlFormHeadController { @@ -321,8 +322,8 @@ public class OnlFormHeadController {
List list2 = selectRefOnl(list1, type, "1");
head.put("child", list2);
arrayList.add(head);
//关联附表
if (StringUtils.isNotEmpty(onlFormHead.getChildName())) {
//关联附表 并且是否需要附表 为 1 则返回
if (StringUtils.isNotEmpty(onlFormHead.getChildName()) && (isChild == null || isChild == 1)) {
List<String> list = Arrays.asList(onlFormHead.getChildName().split(","));
list.forEach(f -> {
OnlFormHead childOnlForm = onlFormHeadService.getHeadTableNameCache(f);

3
common/common-generator/src/main/java/apelet/common/generator/model/OnlCgreportItem.java

@ -56,6 +56,9 @@ public class OnlCgreportItem extends BaseModel implements Serializable { @@ -56,6 +56,9 @@ public class OnlCgreportItem extends BaseModel implements Serializable {
@Schema(description = "是否显示 0否,1显示")
private Integer isShow;
@Schema(description = "是否下拉显示 0否,1显示")
private Integer isSelectShow;
@Schema(description = "排序")
private Integer orderNum;

2
common/common-generator/src/main/java/apelet/common/generator/model/OnlFormHead.java

@ -26,7 +26,7 @@ public class OnlFormHead extends BaseModel implements Serializable { @@ -26,7 +26,7 @@ public class OnlFormHead extends BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id")
private Long id;
// 0 附表,1 主表,2单表
@Schema(description = "表类型")
private String tableType;

11
common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java

@ -119,4 +119,15 @@ public interface IOnlFormHeadService extends IService<OnlFormHead> { @@ -119,4 +119,15 @@ public interface IOnlFormHeadService extends IService<OnlFormHead> {
* @return
*/
OnlFormHead getHeadTableNameCache(String tableName);
/**
* 创建元数据含默认字段并同步数据库. 返回创建后的 OnlFormHead.
*
* @param tableName 表名
* @param tableTxt 表描述
* @param metaType 表类型: "0"=附表(分录), "1"=主表(业务单据), "2"=单表(基础资料)
* @param sortFiled 关联字段附表时传主表表名
* @return OnlFormHead
*/
OnlFormHead createMetaAndSync(String tableName, String tableTxt, String metaType, String sortFiled);
}

39
common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java

@ -1660,4 +1660,43 @@ public class OnlFormHeadServiceImpl extends ServiceImpl<OnlFormHeadMapper, OnlFo @@ -1660,4 +1660,43 @@ public class OnlFormHeadServiceImpl extends ServiceImpl<OnlFormHeadMapper, OnlFo
public void saveEasDict(List<OnlineFromDict> list) {
onlFormHeadMapper.saveEasDict(list);
}
@Override
@Transactional(rollbackFor = Exception.class)
public OnlFormHead createMetaAndSync(String tableName, String tableTxt, String metaType, String sortFiled) {
OnlFormHead head = new OnlFormHead();
head.setId(idGenerator.nextLongId());
head.setTableName(tableName);
head.setTableTxt(tableTxt);
head.setTableType(metaType);
head.setStatus("0"); // 未同步状态
head.setLine(CustomUtil.MetaType_Entry.equals(metaType) ? 1 : 0);
if (StringUtils.isNotEmpty(sortFiled)) {
head.setSort(sortFiled);
head.setSortFiled("parent_id");
}
// 初始化审计字段(createUserId/updateUserId/createTime/updateTime/deletedFlag)
addOrUpdateBaseData(head, head.getId());
// Build DTO
OnlFormHeadDto dto = new OnlFormHeadDto();
BeanUtil.copyProperties(head, dto);
// Generate default fields
List<OnlFormField> defaultFields = new ArrayList<>();
defaultData(defaultFields, null, null, metaType);
dto.setOnlFormFieldList(defaultFields);
// Save
this.addAll(dto);
// Reload to get full info
OnlFormHead saved = this.getById(head.getId());
// Sync database (type=0: CREATE TABLE or ALTER TABLE ADD COLUMN)
this.syncDB(saved, 0L);
return saved;
}
}

18
common/common-online/src/main/java/apelet/common/online/abstractplugin/ExecutePluginParent.java

@ -1,25 +1,25 @@ @@ -1,25 +1,25 @@
package apelet.common.online.abstractplugin;
import apelet.common.core.object.Model;
import apelet.common.core.object.ObjectValue;
import apelet.common.core.util.ApplicationContextHolder;
import apelet.common.generator.utils.OrmGenDataSourceUtil;
import apelet.common.online.dto.OnlineEventPluginExecuteDto;
import apelet.common.online.model.*;
import apelet.common.online.model.FilterInfo;
import apelet.common.online.model.ShowParameter;
import apelet.common.online.model.constant.AttributeEnum;
import apelet.common.online.service.OnlineDatasourceService;
import apelet.common.online.service.OnlineFormService;
import apelet.common.online.vo.OnlineEventPluginExecuteEntryVo;
import apelet.common.online.vo.OnlineEventPluginExecuteVo;
import apelet.common.orm.impl.OrmDataSourceUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import lombok.Setter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 插件执行抽象类父类
@ -569,4 +569,10 @@ public abstract class ExecutePluginParent { @@ -569,4 +569,10 @@ public abstract class ExecutePluginParent {
return getDto().getModel().getCellField();
}
public void childPageCallback(String widgetVariableName, ObjectValue objectValue) {
}
}

33
common/common-online/src/main/java/apelet/common/online/abstractplugin/ListDataOrmService.java

@ -6,13 +6,14 @@ import apelet.common.core.object.ResponseResult; @@ -6,13 +6,14 @@ import apelet.common.core.object.ResponseResult;
import apelet.common.online.abstractplugin.model.GridData;
import apelet.common.online.dto.OnlineEventPluginExecuteDto;
import apelet.common.online.dto.OnlineFilterDto;
import apelet.common.online.model.OnlineColumn;
import apelet.common.online.model.OnlineDatasourceRelation;
import apelet.common.online.model.OnlineForm;
import apelet.common.online.model.OnlineTable;
import apelet.common.online.model.*;
import apelet.common.online.model.constant.DataRuleType;
import apelet.common.online.model.constant.FieldFilterType;
import apelet.common.online.model.constant.RelationType;
import apelet.common.orm.impl.*;
import apelet.common.orm.parser.FilterParser;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
@ -61,11 +62,35 @@ public class ListDataOrmService extends ListDataService { @@ -61,11 +62,35 @@ public class ListDataOrmService extends ListDataService {
List<Map<String, Object>> resList = new ArrayList<>();
OnlineTable masterTable = datasourceResult.getData().getMasterTable();
OnlineForm onlineForm = onlineFormService.getOnlineFormFromCache(dto.getFormId());
// 根据 metaCode 过滤权限规则:非空→基础数据权限,为空→列表权限
List<PermissionDataRuleSchema> ruleSchemaList = permissionDataRuleSchemaService.listByFormId(onlFormHead.getId());
String conditionExpression = null;
if (CollUtil.isNotEmpty(ruleSchemaList)) {
List<PermissionDataRuleSchema> filtered = new ArrayList<>();
if (StrUtil.isNotBlank(metaCode)) {
filtered = ruleSchemaList.stream().filter(s -> DataRuleType.BASIC_DATA == s.getRuleType() && metaCode.equals(s.getMetaCode())).collect(Collectors.toList());
}
if (CollUtil.isEmpty(filtered)) {
filtered = ruleSchemaList.stream().filter(s -> DataRuleType.LIST == s.getRuleType()).collect(Collectors.toList());
}
if (CollUtil.isNotEmpty(filtered)) {
conditionExpression = filtered.stream().map(PermissionDataRuleSchema::getConditionExpression).collect(Collectors.joining(") AND (", "(", ")"));
}
}
JSONObject widgetJson = JSON.parseObject(onlineForm.getWidgetJson());
JSONObject widgetList = widgetJson.getJSONObject("pc").getJSONObject("tableWidget");
Set<String> selectorSet = new TreeSet<>();
getOrmSelector(widgetList, selectorSet, masterTable, new HashMap<>());
Filter filter = getFilter();
if (StrUtil.isNotBlank(conditionExpression)) {
Filter parsed = FilterParser.parse(conditionExpression);
if (CollUtil.isNotEmpty(parsed.getItems())) {
for (FilterItem item : parsed.getItems()) {
filter.add(item);
}
}
}
Sorter sorter = new Sorter();
if (orderParam != null) {

10
common/common-online/src/main/java/apelet/common/online/abstractplugin/ListDataService.java

@ -11,15 +11,11 @@ import apelet.common.online.abstractplugin.model.GridData; @@ -11,15 +11,11 @@ import apelet.common.online.abstractplugin.model.GridData;
import apelet.common.online.dto.OnlineEventPluginExecuteDto;
import apelet.common.online.dto.OnlineFilterDto;
import apelet.common.online.model.OnlineDatasource;
import apelet.common.online.service.OnlineDictService;
import apelet.common.online.service.OnlineFormService;
import apelet.common.online.service.OnlineOperationService;
import apelet.common.online.service.OnlineTableService;
import apelet.common.online.service.*;
import apelet.common.online.util.OnlineOperationHelper;
import apelet.common.orm.impl.Filter;
import apelet.common.orm.impl.OrmDataSourceUtil;
import apelet.common.orm.impl.Sorter;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@ -41,6 +37,7 @@ public abstract class ListDataService { @@ -41,6 +37,7 @@ public abstract class ListDataService {
protected MyPageParam pageParam;
protected TraceParamDto traceParamDto;
protected OnlFormHead onlFormHead;
protected String metaCode;
protected OrmDataSourceUtil ormDataSourceUtil;
@ -51,6 +48,7 @@ public abstract class ListDataService { @@ -51,6 +48,7 @@ public abstract class ListDataService {
protected OnlineOperationHelper onlineOperationHelper;
protected OnlineOperationService onlineOperationService;
protected OnlineTableService onlineTableService;
protected IPermissionDataRuleSchemaService permissionDataRuleSchemaService;
protected ResponseResult<OnlineDatasource> datasourceResult;
@ -64,6 +62,7 @@ public abstract class ListDataService { @@ -64,6 +62,7 @@ public abstract class ListDataService {
onlFormHead = (OnlFormHead) listParams.get("onlFormHead");
datasourceResult =
(ResponseResult<OnlineDatasource>) listParams.get("datasourceResult");
metaCode = (String) listParams.get("metaCode");
onlineFormService = ApplicationContextHolder.getBean("onlineFormService");
onlFormFieldService = ApplicationContextHolder.getBean("onlFormFieldServiceImpl");
@ -72,6 +71,7 @@ public abstract class ListDataService { @@ -72,6 +71,7 @@ public abstract class ListDataService {
onlineOperationService = ApplicationContextHolder.getBean("onlineOperationService");
onlineTableService = ApplicationContextHolder.getBean("onlineTableService");
ormDataSourceUtil = ApplicationContextHolder.getBean("ormGenDataSourceUtil");
permissionDataRuleSchemaService = ApplicationContextHolder.getBean("permissionDataRuleSchemaService");
}
@Setter

1
common/common-online/src/main/java/apelet/common/online/abstractplugin/model/ListColumn.java

@ -27,5 +27,6 @@ public class ListColumn { @@ -27,5 +27,6 @@ public class ListColumn {
private boolean uploadFile;//是否是附件
private boolean treeLine;//是否树形结构
private Map<String, Object> column = null; //Html字段设置 "column:"{ "fieldKind" : 34}
private boolean customize;//是否跳转自定义页面
//
}

455
common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java

@ -3,6 +3,7 @@ package apelet.common.online.controller; @@ -3,6 +3,7 @@ package apelet.common.online.controller;
import apelet.common.core.annotation.MyRequestBody;
import apelet.common.core.annotation.NoAuthInterface;
import apelet.common.core.cache.CacheConfig;
import apelet.common.core.cache.CacheKey;
import apelet.common.core.constant.AppDeviceType;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.object.*;
@ -12,16 +13,21 @@ import apelet.common.core.util.MyPageUtil; @@ -12,16 +13,21 @@ import apelet.common.core.util.MyPageUtil;
import apelet.common.core.validator.UpdateGroup;
import apelet.common.dbutil.object.SqlTable;
import apelet.common.dbutil.object.SqlTableColumn;
import apelet.common.generator.model.OnlFormField;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.generator.service.IOnlFormFieldService;
import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.generator.utils.CustomUtil;
import apelet.common.generator.utils.cache.MetaCacheService;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.online.config.OnlineProperties;
import apelet.common.online.dto.*;
import apelet.common.online.dto.OnlineEventPluginExecuteDto;
import apelet.common.online.dto.OnlineFormDto;
import apelet.common.online.model.*;
import apelet.common.online.service.*;
import apelet.common.online.util.OnlineRedisKeyUtil;
import apelet.common.online.util.WidgetFieldTypeMapping;
import apelet.common.online.vo.OnlineFormVo;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
@ -34,6 +40,7 @@ import com.alibaba.fastjson.JSONObject; @@ -34,6 +40,7 @@ import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.page.PageMethod;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.google.common.collect.Lists;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -47,8 +54,9 @@ import org.springframework.web.bind.annotation.*; @@ -47,8 +54,9 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.groups.Default;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@ -83,12 +91,20 @@ public class OnlineFormController { @@ -83,12 +91,20 @@ public class OnlineFormController {
@Autowired
IOnlFormHeadService onlFormHeadService;
@Autowired
IOnlFormFieldService onlFormFieldService;
@Autowired
OnlinePageService onlinePageService;
@Autowired
private OnlineProperties properties;
@Resource(name = "caffeineCacheManager")
private CacheManager cacheManager;
@Autowired
private MetaCacheService metaCacheService;
@Autowired(required = false)
private FormButtonPermissionChecker buttonPermissionChecker;
/**
* 新增在线表单数据
*
@ -326,6 +342,13 @@ public class OnlineFormController { @@ -326,6 +342,13 @@ public class OnlineFormController {
onlineForm.setWidgetJson(jsonObject.toJSONString());
}
// ★ 新增: 处理新增字段和子表
JSONObject updatedJson = syncNewFieldsAndSubTables(onlineForm, onlineFormDto, JSON.parseObject(onlineForm.getWidgetJson()));
if (updatedJson != null) {
onlineForm.setWidgetJson(updatedJson.toJSONString());
onlineFormDto.setWidgetJson(updatedJson.toJSONString());
}
if (!onlineFormService.update(onlineForm, originalOnlineForm, datasourceIdSet)) {
redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineFormKey(onlineForm.getFormId())).delete();
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
@ -574,6 +597,19 @@ public class OnlineFormController { @@ -574,6 +597,19 @@ public class OnlineFormController {
@PostMapping("/eventPluginExecute")
public ResponseResult<List<Object>> eventPluginExecute(
@MyRequestBody OnlineEventPluginExecuteDto onlineEventPluginExecuteDto) {
// // 按钮权限校验
// if (buttonPermissionChecker != null) {
// Long formId = onlineEventPluginExecuteDto.getFormId();
// String buttonName = onlineEventPluginExecuteDto.getEventWidgetVariableName();
// if (formId != null && buttonName != null) {
// if (!buttonPermissionChecker.hasPermission(formId, buttonName)) {
// return ResponseResult.error(ErrorCodeEnum.NO_OPERATION_PERMISSION,
// "当前角色无此按钮操作权限!");
// }
// }
// }
List<Object> eventPluginExecuteVoList =
onlineFormService.eventPluginExecute(onlineEventPluginExecuteDto);
return ResponseResult.success(eventPluginExecuteVoList);
@ -685,4 +721,419 @@ public class OnlineFormController { @@ -685,4 +721,419 @@ public class OnlineFormController {
}
return ResponseResult.success(onlineFormList);
}
/**
* 根据表单类型查询表单列表
*
* @param formType 表单类型
* @return 表单列表
* @see apelet.common.online.model.constant.FormType
*/
@PostMapping(value = "/queryFormByType")
public ResponseResult<List<OnlineForm>> queryFormByType(@MyRequestBody Integer formType) {
if (formType == null) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
return ResponseResult.success(onlineFormService.queryListByFormType(Lists.newArrayList(formType)));
}
// ============ 字段diff+同步 相关私有方法 ============
/**
* 对比 widgetJson 与现有 OnlFormField / OnlFormHead 识别新增字段和子表
* 创建 OnlFormField 记录同步数据库创建 OnlineColumn回填 columnId/relationId
*
* @return 修改后的 widgetJson (含回填的 columnId/relationId)无变更则返回 null
*/
private JSONObject syncNewFieldsAndSubTables(OnlineForm form, OnlineFormDto dto, JSONObject widgetJson) {
// 1. 获取数据源 → OnlineTable → OnlFormHead
OnlineDatasource datasource = onlineDatasourceService.getById(dto.getDatasourceIdList().get(0));
OnlineTable onlineTable = onlineTableService.getById(datasource.getMasterTableId());
OnlFormHead onlFormHead = onlFormHeadService.getOne(
new LambdaQueryWrapper<OnlFormHead>()
.eq(OnlFormHead::getTableName, onlineTable.getTableName()));
if (onlFormHead == null) {
return null;
}
boolean changed = false;
// === Diff 新增字段 ===
Set<String> widgetColumnNames = collectAllBindColumnNames(widgetJson);
List<OnlFormField> existingFields = onlFormFieldService.list(
new LambdaQueryWrapper<OnlFormField>().eq(OnlFormField::getHeadId, onlFormHead.getId()));
Set<String> existingFieldNames = existingFields.stream()
.map(OnlFormField::getFieldName).collect(Collectors.toSet());
Set<String> newColumnNames = new HashSet<>(widgetColumnNames);
newColumnNames.removeAll(existingFieldNames);
if (!newColumnNames.isEmpty()) {
Map<String, JSONObject> nameToWidget = mapColumnNameToWidget(widgetJson);
OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId());
// 预校验:过滤掉 widgetType 不在映射中的控件,避免 getFieldTypeId 抛异常导致部分数据已入库
Set<String> validColumnNames = new LinkedHashSet<>();
for (String fieldName : newColumnNames) {
JSONObject widget = nameToWidget.get(fieldName);
Integer widgetType = widget != null ? widget.getInteger("widgetType") : null;
if (widgetType == null || !WidgetFieldTypeMapping.isDataWidget(widgetType)) {
continue;
}
validColumnNames.add(fieldName);
}
if (validColumnNames.isEmpty()) {
return changed ? widgetJson : null;
}
for (String fieldName : validColumnNames) {
JSONObject widget = nameToWidget.get(fieldName);
// 从 widget 的 bindData.columnComment 设置字段备注
JSONObject bindData = widget.getJSONObject("bindData");
this.saveField(widget.getInteger("widgetType"), bindData.getString("columnComment"), widget.getString("showName"), onlFormHead, fieldName);
}
// ALTER TABLE ADD COLUMN
onlFormHeadService.syncDB(onlFormHead, 0L);
// 批量获取所有列信息,避免 N+1 查询
List<SqlTableColumn> allColumns = onlineDblinkService.getDblinkTableColumnList(
dblink, onlineTable.getTableName());
Map<String, SqlTableColumn> columnMap = new HashMap<>();
if (CollUtil.isNotEmpty(allColumns)) {
for (SqlTableColumn col : allColumns) {
if (col.getColumnName() != null) {
columnMap.put(col.getColumnName().toLowerCase(), col);
}
}
}
// 增量创建 OnlineColumn + 回填 columnId 到 widgetJson
for (String fieldName : validColumnNames) {
SqlTableColumn sqlCol = columnMap.get(fieldName.toLowerCase());
if (sqlCol != null) {
// 回填 columnId 到 widgetJson
JSONObject widget = nameToWidget.get(fieldName);
String showName = widget == null ? null : widget.getString("showName");
long columnId = onlineColumnService.saveBySqlTable(sqlCol, onlineTable.getTableId(), showName);
if (widget != null) {
JSONObject bindData2 = widget.getJSONObject("bindData");
if (bindData2 != null) {
bindData2.put("columnId", columnId);
bindData2.put("tableId", onlineTable.getTableId());
}
}
}
}
redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineTableKey(onlineTable.getTableId())).delete();
redissonClient.getBucket(CacheKey.makeTableKey(onlineTable.getTableName())).delete();
redissonClient.getBucket(CacheKey.makeChildFieldListKey(onlFormHead.getId())).delete();
metaCacheService.removeData(onlineTable.getTableName());
changed = true;
}
// === Diff 新增子表 ===
// 取 widgetList->bindData -> tableId 在去 zz_online_table 查询,这样就可以判断哪些是新增,哪些是 已有的表
// 如果存在附表
Map<String, JSONObject> tableIdAndPropMap = collectSubTableAndPropMap(widgetJson);
if (MapUtil.isNotEmpty(tableIdAndPropMap)) {
//搜集的所有tableId, 要么是 tableId(已存在),要么是 tableName(不存在)
Set<String> allTableIdSet = tableIdAndPropMap.keySet();
Set<String> newSubTableNames = new HashSet<>();
Set<Long> existingSubTableIdSet = new HashSet<>();
for (String tableId : allTableIdSet) {
if (tableId.matches("\\d+")) {
existingSubTableIdSet.add(Long.parseLong(tableId));
} else {
newSubTableNames.add(tableId);
}
}
//分两步,
//step1 : 看看存在的附表,是否存在不存在的字段
if (!existingSubTableIdSet.isEmpty()) {
// 拿到tableId 下面的所有字段,先查询 zz_online_table ,在根据 tableName 查询 onl_form_head 表
List<OnlineTable> tableList = onlineTableService.listByIds(existingSubTableIdSet);
Map<Long, String> tableIdAndNameMap = tableList.stream().collect(Collectors.toMap(OnlineTable::getTableId, OnlineTable::getTableName));
List<String> tableNames = tableList.stream().map(OnlineTable::getTableName).collect(Collectors.toList());
List<OnlFormHead> onlFormHeadList = onlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().in(OnlFormHead::getTableName, tableNames));
Map<String, OnlFormHead> tableNameAndHeadIdMap = onlFormHeadList.stream().collect(Collectors.toMap(OnlFormHead::getTableName, Function.identity()));
//因为这里是zz_online_column.column_id 的id和 列名的混合,我只要列名
Map<String, JSONObject> nameToWidget = mapColumnIdAndWidget(widgetJson);
Set<String> newSubColumnNameSet = nameToWidget.keySet().stream().filter(s -> !s.matches("\\d+")).collect(Collectors.toSet());
//这里要走 新增逻辑
if (!newSubColumnNameSet.isEmpty()) {
// ALTER TABLE ADD COLUMN, 这里取第一个就行,因为绑定的是一个表
JSONObject columnInfo = nameToWidget.values().iterator().next();
Long tableId = columnInfo.getLong("tableId");
String tableName = tableIdAndNameMap.get(tableId);
OnlFormHead entryHead = tableNameAndHeadIdMap.get(tableName);
for (String newSubColumnName : newSubColumnNameSet) {
//这里取出来的结构是 widgetList -》 props -》 tableColumnList
JSONObject columnInfoJson = nameToWidget.get(newSubColumnName);
//step 1 : 先保存 field 表
String showName = columnInfoJson.getString("showName");
// 默认是字符串类型
this.saveField(750, showName, null, entryHead, newSubColumnName);
}
onlFormHeadService.syncDB(entryHead, 0L);
//step 2 :在保存 OnlineColumn 表
OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId());
List<SqlTableColumn> allColumns = onlineDblinkService.getDblinkTableColumnList(dblink, tableName);
Map<String, SqlTableColumn> columnMap = new HashMap<>();
if (CollUtil.isNotEmpty(allColumns)) {
columnMap = allColumns.stream().collect(Collectors.toMap(SqlTableColumn::getColumnName, Function.identity()));
}
for (String fieldName : newSubColumnNameSet) {
SqlTableColumn sqlCol = columnMap.get(fieldName.toLowerCase());
if (sqlCol != null) {
// 回填 columnId 到 widgetJson
JSONObject widget = nameToWidget.get(fieldName);
String showName = widget == null ? null : widget.getString("showName");
long columnId = onlineColumnService.saveBySqlTable(sqlCol, tableId, showName);
if (widget != null) {
//step 3 :回填 columnId + showFieldName 到 json里面去
widget.put("columnId", columnId);
widget.put("showFieldName", fieldName);
}
}
}
//step 4 :删除缓存
redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineTableKey(tableId)).delete();
redissonClient.getBucket(CacheKey.makeTableKey(tableName)).delete();
redissonClient.getBucket(CacheKey.makeChildFieldListKey(entryHead.getId())).delete();
metaCacheService.removeData(tableName);
changed = true;
}
}
//step2 : 不存在的附表,新增
if (newSubTableNames.isEmpty()) {
//如果他为空,说明我们走tableId取值没取到,我们就去一下 tableName;
newSubTableNames = this.collectSubTableName(widgetJson);
}
if (!newSubTableNames.isEmpty()) {
for (String subTableName : newSubTableNames) {
OnlFormHead subHead = onlFormHeadService.createMetaAndSync(subTableName, onlFormHead.getTableTxt() + "附表", CustomUtil.MetaType_Entry, onlFormHead.getTableName());
// 获取子表的 OnlineTable
OnlineTable subOnlineTable = onlineTableService.getOne(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, subTableName.toLowerCase()));
if (subOnlineTable == null) {
// 从DB获取表结构并创建 OnlineTable
SqlTable subSqlTable = onlineDblinkService.getDblinkTable(onlineDblinkService.getById(datasource.getDblinkId()), subTableName);
if (subSqlTable != null) {
subOnlineTable = onlineTableService.saveNewFromSqlTable(subSqlTable);
}
}
OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId());
onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, datasource, null, dblink);
if (subOnlineTable != null) {
backfillRelationId(widgetJson, subTableName, subOnlineTable.getTableId());
}
}
changed = true;
}
}
return changed ? widgetJson : null;
}
private void saveField(Integer widgetType, String columnComment, String showName, OnlFormHead onlFormHead, String fieldName) {
OnlFormField field = new OnlFormField();
field.setHeadId(onlFormHead.getId());
field.setFieldName(fieldName);
field.setFieldType(WidgetFieldTypeMapping.getFieldTypeId(widgetType));
Integer length = WidgetFieldTypeMapping.getDefaultLength(widgetType);
if (length != null) {
field.setFieldLength(length);
}
field.setIsEmpty(1);
field.setIsDefault(0);
field.setRowDisabled(0);
field.setSyncFlag(0);
// 从 widget 的 bindData.columnComment 设置字段备注
field.setFieldRemark(columnComment);
if (StringUtils.isNotBlank(showName) && (StringUtils.isBlank(field.getFieldRemark()) || StringUtils.equals("null", field.getFieldRemark()))) {
field.setFieldRemark(showName);
}
onlFormFieldService.save(field);
}
/**
* 遍历 pc/mobile widget 收集所有 bindData.columnName
*/
private Set<String> collectAllBindColumnNames(JSONObject widgetJson) {
Set<String> names = new HashSet<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
names.add(name);
}
}
});
return names;
}
/**
* 遍历 pc/mobile widget 收集 widgetType=100 (TableContainer) bindData 下的 tableId
*/
private Map<String, JSONObject> collectSubTableAndPropMap(JSONObject widgetJson) {
Map<String, JSONObject> resultMap = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String tableId = bindData.getString("tableId");
if (StrUtil.isBlank(tableId)) {
continue;
}
JSONObject props = childWidget.getJSONObject("props");
if (props != null) {
resultMap.put(tableId, props);
}
}
}
});
return resultMap;
}
private Set<String> collectSubTableName(JSONObject widgetJson) {
Set<String> tableNameSet = new HashSet<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
if (bindData.containsKey("tableName")) {
tableNameSet.add(bindData.getString("tableName"));
}
}
}
});
return tableNameSet;
}
/**
* 构建 columnName widget 的映射
*/
private Map<String, JSONObject> mapColumnNameToWidget(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
map.put(name, widget);
}
}
});
return map;
}
private Map<String, JSONObject> mapColumnIdAndWidget(JSONObject widgetJson) {
Map<String, JSONObject> resultMap = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String tableId = bindData.getString("tableId");
if (StrUtil.isBlank(tableId)) {
continue;
}
JSONObject props = childWidget.getJSONObject("props");
if (props == null) {
continue;
}
JSONArray tableColumnList = props.getJSONArray("tableColumnList");
for (int j = 0; j < tableColumnList.size(); j++) {
JSONObject tableColumn = tableColumnList.getJSONObject(j);
resultMap.put(tableColumn.getString("columnId"), tableColumn);
}
}
}
});
return resultMap;
}
/**
* widgetJson 中查找匹配 tableName TableContainer 控件回填 tableId
*/
private void backfillRelationId(JSONObject widgetJson, String tableName, Long tableId) {
walkAllModes(widgetJson, widget -> {
if (widget.getInteger("widgetType") != null && 100 == widget.getInteger("widgetType")) {
JSONObject props = widget.getJSONObject("bindData");
if (props != null && tableName.equals(props.getString("tableName"))) {
props.put("tableId", tableId);
}
}
});
}
/**
* 遍历 "pc" "mobile" 两个模式的控件树
*/
private void walkAllModes(JSONObject widgetJson, Consumer<JSONObject> visitor) {
for (String mode : new String[]{"pc", "mobile"}) {
JSONObject modeObj = widgetJson.getJSONObject(mode);
if (modeObj != null) {
walkWidgets(modeObj, visitor);
}
}
}
/**
* 递归遍历控件树访问每个控件及其子控件(widgetList)
*/
private void walkWidgets(JSONObject node, Consumer<JSONObject> visitor) {
if (node == null) {
return;
}
visitor.accept(node);
JSONArray children = node.getJSONArray("widgetList");
if (children != null) {
for (int i = 0; i < children.size(); i++) {
walkWidgets(children.getJSONObject(i), visitor);
}
}
}
}

95
common/common-online/src/main/java/apelet/common/online/controller/OnlineOperationController.java

@ -1,54 +1,60 @@ @@ -1,54 +1,60 @@
package apelet.common.online.controller;
import apelet.common.core.upload.LocalUpDownloader;
import apelet.common.eas.service.impl.EASSyncService;
import apelet.common.generator.model.OnlFormField;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.generator.service.IOnlFormFieldService;
import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.generator.utils.OrmGenDataSourceUtil;
import apelet.common.online.abstractplugin.ListDataEasService;
import apelet.common.online.abstractplugin.ListDataOrmService;
import apelet.common.online.abstractplugin.ListDataService;
import apelet.common.online.abstractplugin.model.ExportData;
import apelet.common.online.abstractplugin.model.GridData;
import apelet.common.online.dto.OnlineEventPluginExecuteDto;
import apelet.common.online.dto.OnlinePluginExecuteDto;
import apelet.common.online.model.constant.*;
import apelet.common.online.vo.OnlineEventPluginExecuteVo;
import apelet.common.orm.impl.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import apelet.common.core.annotation.MyRequestBody;
import apelet.common.core.constant.DictType;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.constant.ObjectFieldType;
import apelet.common.core.exception.MyRuntimeException;
import apelet.common.core.object.*;
import apelet.common.core.upload.LocalUpDownloader;
import apelet.common.core.util.*;
import apelet.common.dict.model.GlobalDictItem;
import apelet.common.dict.model.TenantGlobalDict;
import apelet.common.dict.model.TenantGlobalDictItem;
import apelet.common.dict.service.GlobalDictService;
import apelet.common.dict.service.TenantGlobalDictService;
import apelet.common.eas.service.impl.EASSyncService;
import apelet.common.generator.model.OnlFormField;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.generator.service.IOnlFormFieldService;
import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.generator.utils.OrmGenDataSourceUtil;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.online.abstractplugin.ListDataEasService;
import apelet.common.online.abstractplugin.ListDataOrmService;
import apelet.common.online.abstractplugin.ListDataService;
import apelet.common.online.abstractplugin.model.ExportData;
import apelet.common.online.abstractplugin.model.GridData;
import apelet.common.online.config.OnlineProperties;
import apelet.common.online.dto.OnlineEventPluginExecuteDto;
import apelet.common.online.dto.OnlineFilterDto;
import apelet.common.online.dto.OnlinePluginExecuteDto;
import apelet.common.online.exception.OnlineRuntimeException;
import apelet.common.online.model.*;
import apelet.common.online.model.constant.EventEnum;
import apelet.common.online.model.constant.FieldFilterType;
import apelet.common.online.model.constant.FieldKind;
import apelet.common.online.model.constant.RelationType;
import apelet.common.online.service.*;
import apelet.common.online.util.OnlineConstant;
import apelet.common.online.util.OnlineOperationHelper;
import apelet.common.online.vo.OnlineEventPluginExecuteVo;
import apelet.common.orm.impl.*;
import apelet.common.redis.cache.SessionCacheHelper;
import apelet.common.redis.util.CommonRedisUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -59,7 +65,8 @@ import org.springframework.web.bind.annotation.*; @@ -59,7 +65,8 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
@ -113,6 +120,9 @@ public class OnlineOperationController { @@ -113,6 +120,9 @@ public class OnlineOperationController {
@Autowired
private OnlineDblinkService onlineDblinkService;
@Autowired(required = false)
private FormButtonPermissionChecker buttonPermissionChecker;
/**
* 执行功能插件
*
@ -127,6 +137,18 @@ public class OnlineOperationController { @@ -127,6 +137,18 @@ public class OnlineOperationController {
@MyRequestBody JSONObject masterData,
@MyRequestBody JSONObject slaveData) {
// //todo 暂时不开启 按钮权限校验
// if (buttonPermissionChecker != null) {
// Long formId = onlinePluginExecuteDto.getFormId();
// String buttonName = onlinePluginExecuteDto.getButtonName();
// if (formId != null && buttonName != null) {
// if (!buttonPermissionChecker.hasPermission(formId, buttonName)) {
// return ResponseResult.error(ErrorCodeEnum.NO_OPERATION_PERMISSION,
// "当前角色无此按钮操作权限!");
// }
// }
// }
return ResponseResult.success(onlineOperationService.executePlugin(onlinePluginExecuteDto, masterData, slaveData));
}
@ -795,7 +817,8 @@ public class OnlineOperationController { @@ -795,7 +817,8 @@ public class OnlineOperationController {
@MyRequestBody MyPageParam pageParam,
@MyRequestBody String ignoreMaskFields,
@MyRequestBody TraceParamDto traceParamDto,
@MyRequestBody OnlineEventPluginExecuteDto onlineEventPluginExecuteDto
@MyRequestBody OnlineEventPluginExecuteDto onlineEventPluginExecuteDto,
@MyRequestBody String metaCode
) {
// 1. 验证数据源及其关联
ResponseResult<OnlineDatasource> datasourceResult =
@ -807,6 +830,16 @@ public class OnlineOperationController { @@ -807,6 +830,16 @@ public class OnlineOperationController {
if (!filterDtoListResult.isSuccess()) {
return ResponseResult.errorFrom(filterDtoListResult);
}
// 透传 metaCode 到 ListDataOrmService,用于权限规则类型过滤
if (onlineEventPluginExecuteDto == null) {
onlineEventPluginExecuteDto = new OnlineEventPluginExecuteDto();
}
Map<String, Object> listParams = onlineEventPluginExecuteDto.getListParams();
if (listParams == null) {
listParams = new HashMap<>();
onlineEventPluginExecuteDto.setListParams(listParams);
}
listParams.put("metaCode", metaCode);
OnlFormHead onlFormHead = onlFormHeadService.getFormHeadByDatasource(datasourceId);
GridData pluginData = this.exeListPlugin(formId,filterDtoList,orderParam,pageParam,
traceParamDto,onlFormHead,datasourceResult,onlineEventPluginExecuteDto
@ -840,7 +873,10 @@ public class OnlineOperationController { @@ -840,7 +873,10 @@ public class OnlineOperationController {
}
onlineEventPluginExecuteDto.setFormId(formId);
onlineEventPluginExecuteDto.setEventCode(EventEnum.LOADLISTDATA.getCode());
Map<String,Object> listParams = new HashMap<>();
Map<String,Object> listParams = onlineEventPluginExecuteDto.getListParams();
if (listParams == null) {
listParams = new HashMap<>();
}
listParams.put("filterDtoList",filterDtoList);
listParams.put("pageParam",pageParam);
listParams.put("orderParam",orderParam);
@ -873,7 +909,6 @@ public class OnlineOperationController { @@ -873,7 +909,6 @@ public class OnlineOperationController {
return gridData;
}
private void normalizeFilterList(OnlineTable table
, List<OnlineFilterDto> filterList) {
OnlineDblink dblink = onlineDblinkService.getById(table.getDblinkId());

91
common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java

@ -16,7 +16,10 @@ import apelet.common.generator.service.IOnlFormHeadService; @@ -16,7 +16,10 @@ import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.generator.utils.CustomUtil;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.online.dto.*;
import apelet.common.online.dto.OnlineDatasourceDto;
import apelet.common.online.dto.OnlineDatasourceRelationDto;
import apelet.common.online.dto.OnlinePageDatasourceDto;
import apelet.common.online.dto.OnlinePageDto;
import apelet.common.online.model.*;
import apelet.common.online.model.constant.PageStatus;
import apelet.common.online.service.*;
@ -36,6 +39,7 @@ import lombok.extern.slf4j.Slf4j; @@ -36,6 +39,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.validation.groups.Default;
@ -74,6 +78,10 @@ public class OnlinePageController { @@ -74,6 +78,10 @@ public class OnlinePageController {
@Autowired
private IOnlFormFieldService onlFormFieldService;
//new=新建元数据,exist=现有元数据
private static final String META_SOURCE_NEW = "new";
private static final String META_SOURCE_EXIST = "exist";
/**
* 新增在线表单页面数据
*
@ -82,41 +90,82 @@ public class OnlinePageController { @@ -82,41 +90,82 @@ public class OnlinePageController {
*/
@ApiOperationSupport(ignoreParameters = {"onlinePageDto.pageId"})
@OperationLog(type = SysOperationLogType.ADD)
@Transactional(rollbackFor = Exception.class)
@PostMapping("/add")
public ResponseResult<Long> add(@MyRequestBody OnlinePageDto onlinePageDto) {
public ResponseResult<OnlinePage> add(@MyRequestBody OnlinePageDto onlinePageDto) {
String errorMessage = MyCommonUtil.getModelValidationError(onlinePageDto);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// Validate based on metaSource
String metaSource = onlinePageDto.getMetaSource();
if (META_SOURCE_NEW.equals(metaSource)) {
// New metadata flow: metaType and pageCode are required
if (StrUtil.isEmpty(onlinePageDto.getMetaType())) {
errorMessage = "数据验证失败,新建元数据时表类型(metaType)不能为空!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
if (StrUtil.isEmpty(onlinePageDto.getPageCode())) {
errorMessage = "数据验证失败,新建元数据时页面编码不能为空!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
} else {
// Existing metadata flow: masterTableId is required
if (StrUtil.isEmpty(onlinePageDto.getMasterTableId())) {
errorMessage = "数据验证失败,表单元数据不能为空!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
}
OnlinePage onlinePage = MyModelUtil.copyTo(onlinePageDto, OnlinePage.class);
if (onlinePageService.existByPageCode(onlinePage.getPageCode())) {
errorMessage = "数据验证失败,页面编码已经存在!";
return ResponseResult.error(ErrorCodeEnum.DUPLICATED_UNIQUE_KEY, errorMessage);
}
OnlFormHead onlFormHead;
if (META_SOURCE_NEW.equals(metaSource)) {
// === New branch: create fresh metadata ===
String metaType = onlinePageDto.getMetaType();
String tableName = "t_" + onlinePageDto.getPageCode().toLowerCase();
String tableTxt = onlinePageDto.getPageName();
onlFormHead = iOnlFormHeadService.createMetaAndSync(tableName, tableTxt, metaType, null);
Long masterTableId = onlFormHead.getId();
onlinePage.setMasterTableId(String.valueOf(masterTableId));
onlinePage.setMasterTableName(onlFormHead.getTableName());
// For business documents (metaType="1"): auto-create sub-table
if (CustomUtil.MetaType_Main.equals(metaType)) {
String entryTableName = tableName + "_entry";
iOnlFormHeadService.createMetaAndSync(entryTableName, tableTxt + "附表", CustomUtil.MetaType_Entry, tableName);
}
} else {
// === Existing branch: use existing metadata ===
String formMetadata = onlinePageDto.getMasterTableId();
onlFormHead = iOnlFormHeadService.getById(formMetadata);
}
try {
onlinePage = onlinePageService.saveNew(onlinePage);
} catch (DuplicateKeyException e) {
errorMessage = "数据验证失败,当前应用的页面编码已经存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
//根据所传表单元数据id查出对应表单信息
String formMetadata = onlinePageDto.getMasterTableId();
//在线表单主表对象
OnlFormHead onlFormHead = iOnlFormHeadService.getById(formMetadata);
//准备创建数据模型所需的数据
// 准备创建数据模型所需的数据
OnlineDatasourceDto onlineDatasourceDto = new OnlineDatasourceDto();
//数据源标识-取表名驼峰
// 数据源标识-取表名驼峰
onlineDatasourceDto.setVariableName(CustomUtil.UnderlineToCamel(onlFormHead.getTableName()));
//数据主表标识
// 数据主表标识
onlineDatasourceDto.setMasterTableName(onlFormHead.getTableName());
//数据源名称-取表名
// 数据源名称-取表名
onlineDatasourceDto.setDatasourceName((onlFormHead.getTableName()));
//目前系统只有一个数据源,所以默认1740180645717610505
// 目前系统只有一个数据源,所以默认1740180645717610505
OnlineDblink onlineDblink = onlineDblinkService.getById(1740180645717610505L);
onlineDatasourceDto.setDblinkId(1740180645717610505L);
//创建数据模型
// 创建数据模型
SqlTable sqlTable = onlineDblinkService.getDblinkTable(onlineDblink, onlineDatasourceDto.getMasterTableName());
if (sqlTable == null) {
errorMessage = "数据验证失败,指定的数据表名不存在!";
@ -130,22 +179,20 @@ public class OnlinePageController { @@ -130,22 +179,20 @@ public class OnlinePageController {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
/**
*创建数据表关联关系 先查出改元数据表的所有附表关系
* 创建数据表关联关系 先查出改元数据表的所有附表关系
* 一对多
*
*/
addOnlineDatasourceRelation(onlFormHead, onlineDatasource, errorMessage, onlineDblink);
return ResponseResult.success(onlinePage.getPageId());
onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, onlineDatasource, errorMessage, onlineDblink);
return ResponseResult.success(onlinePage);
}
/**
*创建数据表关联关系 先查出改元数据表的所有附表关系
*
* 方法迁移 onlineDatasourceRelationService.addOnlineDatasourceRelation
*/
@Deprecated
public ResponseResult<Long> addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink) {
//历史数据 --做比较新数据写入,已存在的不用管,不存在的删除
OnlineDatasourceRelation onlineDatasourceRelations = new OnlineDatasourceRelation();
@ -399,7 +446,7 @@ public class OnlinePageController { @@ -399,7 +446,7 @@ public class OnlinePageController {
onlineColumnService.saveNewList(sqlTableColumnList, onlineDatasourceList.get(0).getMasterTableId());
//写表关联关系 --一对多,一对一
onlineDatasourceService.getOnlineDatasourceListByPageId(onlinePageDto.getPageId(), null, null);
addOnlineDatasourceRelation(onlFormHeadList.get(0), onlineDatasourceList.get(0), errorMessage, onlineDblink);
onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHeadList.get(0), onlineDatasourceList.get(0), errorMessage, onlineDblink);
ArrayList<OnlineColumn> onlineColumns = new ArrayList<>();
List<OnlFormField> filedByHeadIdCache = onlFormFieldService.getFiledByHeadIdCache(onlFormHeadList.get(0).getId());

184
common/common-online/src/main/java/apelet/common/online/controller/PermissionDataRuleSchemaController.java

@ -0,0 +1,184 @@ @@ -0,0 +1,184 @@
package apelet.common.online.controller;
import apelet.common.core.annotation.MyRequestBody;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.MyOrderParam;
import apelet.common.core.object.MyPageParam;
import apelet.common.core.object.ResponseResult;
import apelet.common.core.util.MyCommonUtil;
import apelet.common.core.util.MyPageUtil;
import apelet.common.core.validator.UpdateGroup;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.online.model.PermissionDataRuleSchema;
import apelet.common.online.service.IPermissionDataRuleSchemaService;
import com.github.pagehelper.page.PageMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.groups.Default;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* 权限数据规则方案表 控制器
*
* @author chenchuchuan
* @date 2026-07-15
*/
@RestController
@RequestMapping("${common-online.urlPrefix}/permissionDataRuleSchema")
public class PermissionDataRuleSchemaController {
@Autowired
private IPermissionDataRuleSchemaService service;
// ========== 新增 ==========
/**
* 新增数据规则方案
*
* @param schema 新增的规则方案对象
* @return 新增后的规则方案对象
*/
@OperationLog(type = SysOperationLogType.ADD)
@PostMapping("/add")
public ResponseResult<?> add(@RequestBody PermissionDataRuleSchema schema) {
schema.setCreateTime(new Date());
// 1. 基础字段校验(@NotBlank / @NotNull)
String errorMessage = MyCommonUtil.getModelValidationError(schema);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// 2. 业务校验:formId → 主表/单表,并填充 tableType
CallResult result = service.verifyRelatedData(schema);
if (!result.isSuccess()) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage());
}
// 3. 保存
service.saveNew(schema);
return ResponseResult.success(schema);
}
// ========== 编辑(含启用/禁用) ==========
/**
* 更新数据规则方案含启用/禁用
*
* @param schema 更新的规则方案对象
* @return 应答结果
*/
@OperationLog(type = SysOperationLogType.UPDATE)
@PostMapping("/update")
public ResponseResult<?> update(@RequestBody PermissionDataRuleSchema schema) {
// 1. 基础校验(需校验 id 非空 + 其他必填字段)
String errorMessage = MyCommonUtil.getModelValidationError(schema, Default.class, UpdateGroup.class);
if (errorMessage != null) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// 2. 检查数据是否存在
PermissionDataRuleSchema original = service.getById(schema.getId());
if (original == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST,
"数据验证失败,当前规则方案不存在,请刷新后重试!");
}
// 3. 业务校验:formId → 主表/单表,填充 tableType
CallResult result = service.verifyRelatedData(schema);
if (!result.isSuccess()) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage());
}
// 4. 更新
schema.setUpdateTime(new Date());
if (!service.updateById(schema)) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success();
}
// ========== 删除(启用状态禁止删) ==========
/**
* 删除数据规则方案已启用的规则不允许删除
*
* @param id 主键ID
* @return 应答结果
*/
@OperationLog(type = SysOperationLogType.DELETE)
@PostMapping("/delete")
public ResponseResult<?> delete(@MyRequestBody Long id) {
if (MyCommonUtil.existBlankArgument(id)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
PermissionDataRuleSchema schema = service.getById(id);
if (schema == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
// 启用状态不允许删除
if (Objects.equals(schema.getStatus(), 1)) {
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED,
"该规则方案已启用,请先禁用后再删除!");
}
if (!service.removeById(id)) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success();
}
// ========== 分页列表 ==========
/**
* 分页查询数据规则方案列表
*
* @param filter 过滤条件支持 ruleCode/ruleName/status
* @param orderParam 排序参数
* @param pageParam 分页参数
* @return 分页结果
*/
@PostMapping("/list")
public ResponseResult<?> list(
@MyRequestBody PermissionDataRuleSchema filter,
@MyRequestBody MyOrderParam orderParam,
@MyRequestBody MyPageParam pageParam) {
if (pageParam != null) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
}
if (filter == null){
filter = new PermissionDataRuleSchema();
}
List<PermissionDataRuleSchema> list = service.getListByFilter(filter, MyOrderParam.buildOrderBy(orderParam, PermissionDataRuleSchema.class));
return ResponseResult.success(MyPageUtil.makeResponseData(list, PermissionDataRuleSchema.INSTANCE));
}
// ========== 根据 formId 查询启用的规则(运行时用) ==========
/**
* 根据 formId 查询启用状态的数据规则方案运行时用
*
* @param formId 在线表ID
* @return 启用状态的规则方案列表
*/
@GetMapping("/listByFormId")
public ResponseResult<List<PermissionDataRuleSchema>> listByFormId(@RequestParam Long formId) {
if (MyCommonUtil.existBlankArgument(formId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
List<PermissionDataRuleSchema> list = service.listByFormId(formId);
return ResponseResult.success(list);
}
// ========== 查看详情 ==========
/**
* 查看数据规则方案详情
*
* @param id 主键ID
* @return 规则方案详情
*/
@GetMapping("/view")
public ResponseResult<PermissionDataRuleSchema> view(@RequestParam Long id) {
if (MyCommonUtil.existBlankArgument(id)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
PermissionDataRuleSchema schema = service.getById(id);
if (schema == null) {
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
}
return ResponseResult.success(schema);
}
}

117
common/common-online/src/main/java/apelet/common/online/controller/RoleFormButtonController.java

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
package apelet.common.online.controller;
import apelet.common.core.annotation.MyRequestBody;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.object.*;
import apelet.common.core.util.MyCommonUtil;
import apelet.common.core.util.MyPageUtil;
import apelet.common.log.annotation.OperationLog;
import apelet.common.log.model.constant.SysOperationLogType;
import apelet.common.online.dto.RoleFormButtonDto;
import apelet.common.online.model.RoleFormButton;
import apelet.common.online.service.IRoleFormButtonService;
import com.github.pagehelper.page.PageMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 角色-表单按钮权限控制器
*
* @author chenchuchuan
* @date 2026-07-17
*/
@RestController
@RequestMapping("${common-online.urlPrefix}/roleFormButton")
public class RoleFormButtonController {
@Autowired
private IRoleFormButtonService service;
/**
* 查询当前登录用户对指定表单的按钮权限合并所有角色
* 根据当前登录用户的所有角色查询各角色在该表单的按钮配置取并集返回
*
* @param formId 在线表单ID必填
* @return 包含 formId, buttonList[{name, checked}]
*/
@GetMapping("/getButtonConfig")
public ResponseResult<Map<String, Set<String>>> getButtonConfig(@RequestParam(required = false) Long formId) {
return ResponseResult.success(service.getButtonConfigVo(formId));
}
/**
* 批量保存角色+表单的按钮权限配置新增或更新
*
* @param dtoList 列表每个元素包含 roleId必填, formId必填, id编辑时传, buttonList
* @return 应答结果
*/
@OperationLog(type = SysOperationLogType.ADD)
@PostMapping("/saveButtonConfig")
public ResponseResult<Void> saveButtonConfig(
@MyRequestBody List<RoleFormButtonDto> dtoList) {
if (dtoList == null || dtoList.isEmpty()) {
return ResponseResult.error(
ErrorCodeEnum.ARGUMENT_NULL_EXIST, "数据验证失败,按钮配置列表不能为空!");
}
CallResult result = service.batchSaveButtonConfig(dtoList);
if (!result.isSuccess()) {
return ResponseResult.error(
ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage());
}
return ResponseResult.success();
}
/**
* 根据角色ID查询该角色下所有绑定的按钮权限配置列表
*
* @param roleId 角色ID
* @return 按钮权限配置列表
*/
@GetMapping("/listByRoleId")
public ResponseResult<List<RoleFormButton>> listByRoleId(
@RequestParam Long roleId) {
if (MyCommonUtil.existBlankArgument(roleId)) {
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
}
return ResponseResult.success(service.listByRoleId(roleId));
}
/**
* 分页查询所有在线表单下的所有按钮
* 遍历全部在线表单从每个表单的 widgetJson 中提取已开启的按钮
* 如果传了 roleId则只返回该角色已配置的表单并合并已保存的勾选状态
*
* @param roleId 角色ID可选用于过滤已配置的表单
* @param orderParam 排序参数
* @param pageParam 分页参数
* @return 分页结果每个元素包含 formId, formName, buttonList[{name, checked}]
*/
@PostMapping("/listAllFormButtons")
public ResponseResult<MyPageData<Map<String, Object>>> listAllFormButtons(
@MyRequestBody Long roleId,
@MyRequestBody MyOrderParam orderParam,
@MyRequestBody MyPageParam pageParam) {
return ResponseResult.success(service.listAllFormButtons(roleId, orderParam, pageParam));
}
/**
* 分页查询按钮权限配置列表
*/
@PostMapping("/list")
public ResponseResult<MyPageData<?>> list(
@MyRequestBody RoleFormButton filter,
@MyRequestBody MyOrderParam orderParam,
@MyRequestBody MyPageParam pageParam) {
if (pageParam != null) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
}
List<RoleFormButton> list = service.getListByFilter(
filter, MyOrderParam.buildOrderBy(orderParam, RoleFormButton.class));
return ResponseResult.success(
MyPageUtil.makeResponseData(list, RoleFormButton.INSTANCE));
}
}

27
common/common-online/src/main/java/apelet/common/online/dao/PermissionDataRuleSchemaMapper.java

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
package apelet.common.online.dao;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.online.model.PermissionDataRuleSchema;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 权限数据规则方案表 Mapper 接口
*
* @author chenchuchuan
* @date 2026-07-15
*/
public interface PermissionDataRuleSchemaMapper extends BaseDaoMapper<PermissionDataRuleSchema> {
/**
* 分页条件查询支持 rule_code/rule_name/status 过滤 + 排序
*
* @param filter 过滤条件
* @param orderBy 排序语句
* @return 规则方案列表
*/
List<PermissionDataRuleSchema> getListByFilter(
@Param("filter") PermissionDataRuleSchema filter,
@Param("orderBy") String orderBy);
}

23
common/common-online/src/main/java/apelet/common/online/dao/RoleFormButtonMapper.java

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
package apelet.common.online.dao;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.online.model.RoleFormButton;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 角色-表单按钮权限配置数据访问操作接口
*
* @author chenchuchuan
* @date 2026-07-17
*/
public interface RoleFormButtonMapper extends BaseDaoMapper<RoleFormButton> {
/**
* 分页条件查询
*/
List<RoleFormButton> getListByFilter(
@Param("filter") RoleFormButton filter,
@Param("orderBy") String orderBy);
}

14
common/common-online/src/main/java/apelet/common/online/dao/mapper/OnlineFormMapper.xml

@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
AND zz_online_form.form_code = #{onlineFormFilter.formCode}
</if>
<if test="onlineFormFilter.formName != null and onlineFormFilter.formName != ''">
<bind name= "safeFormName" value= "'%' + onlineFormFilter.formName + '%'"/>
<bind name="safeFormName" value="'%' + onlineFormFilter.formName + '%'"/>
AND zz_online_form.form_name LIKE #{safeFormName}
</if>
<if test="onlineFormFilter.formType != null">
@ -56,6 +56,18 @@ @@ -56,6 +56,18 @@
<if test="onlineFormFilter.masterTableId != null">
AND zz_online_form.master_table_id = #{onlineFormFilter.masterTableId}
</if>
<if test="onlineFormFilter.pageIdSet != null">
AND zz_online_form.page_id IN
<foreach collection="onlineFormFilter.pageIdSet" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</if>
<if test="onlineFormFilter.formIdSet != null">
AND zz_online_form.form_id IN
<foreach collection="onlineFormFilter.formIdSet" item="item" separator="," open="(" close=")">
#{item}
</foreach>
</if>
</if>
</sql>

27
common/common-online/src/main/java/apelet/common/online/dao/mapper/PermissionDataRuleSchemaMapper.xml

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="apelet.common.online.dao.PermissionDataRuleSchemaMapper">
<select id="getListByFilter"
resultType="apelet.common.online.model.PermissionDataRuleSchema">
SELECT * FROM zz_permission_data_rule_schema
WHERE 1=1
<if test="filter.ruleCode != null and filter.ruleCode != ''">
AND rule_code LIKE CONCAT('%', #{filter.ruleCode}, '%')
</if>
<if test="filter.ruleName != null and filter.ruleName != ''">
AND rule_name LIKE CONCAT('%', #{filter.ruleName}, '%')
</if>
<if test="filter.status != null">
AND status = #{filter.status}
</if>
<if test="filter.ruleType != null">
AND rule_type = #{filter.ruleType}
</if>
<if test="orderBy != null and orderBy != ''">
ORDER BY ${orderBy}
</if>
</select>
</mapper>

21
common/common-online/src/main/java/apelet/common/online/dao/mapper/RoleFormButtonMapper.xml

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="apelet.common.online.dao.RoleFormButtonMapper">
<select id="getListByFilter"
resultType="apelet.common.online.model.RoleFormButton">
SELECT * FROM xy_sys_role_form_button
WHERE 1=1
<if test="filter.roleId != null">
AND role_id = #{filter.roleId}
</if>
<if test="filter.formId != null">
AND form_id = #{filter.formId}
</if>
<if test="orderBy != null and orderBy != ''">
ORDER BY ${orderBy}
</if>
</select>
</mapper>

14
common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java

@ -60,13 +60,23 @@ public class OnlinePageDto { @@ -60,13 +60,23 @@ public class OnlinePageDto {
* 表单元数据
*/
@Schema(description = "表单元数据")
@NotNull(message = "数据验证失败,表单元数据不能为空!")
private String masterTableId;
/**
* 表单元数据
*/
@Schema(description = "表单元数据表名")
// @NotNull(message = "数据验证失败,表单元数据不能为空!")
private String masterTableName;
/**
* 数据来源: "new"=新建元数据, "existing"=使用已有元数据
*/
@Schema(description = "数据来源: new=新建元数据, existing=使用已有元数据")
private String metaSource;
/**
* 表类型: "2"=基础资料(单表), "1"=业务单据(主表+附表)仅在metaSource="new"时有效
*/
@Schema(description = "表类型: 2=基础资料(单表), 1=业务单据(主表+附表)。仅在metaSource=new时有效")
private String metaType;
}

35
common/common-online/src/main/java/apelet/common/online/dto/RoleFormButtonDto.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
package apelet.common.online.dto;
import lombok.Data;
import java.util.List;
/**
* 角色表单按钮权限保存入参
*
* @author chenchuchuan
* @date 2026-07-17
*/
@Data
public class RoleFormButtonDto {
/** 角色ID(必填) */
private Long roleId;
/** 表单ID(必填) */
private Long formId;
/** 主键ID(编辑时传,新增时不传) */
private Long id;
/** 按钮勾选列表 */
private List<ButtonItem> buttonList;
@Data
public static class ButtonItem {
/** 按钮名称 */
private String name;
/** 是否勾选 */
private Boolean checked;
}
}

13
common/common-online/src/main/java/apelet/common/online/model/OnlineForm.java

@ -16,6 +16,7 @@ import org.mapstruct.factory.Mappers; @@ -16,6 +16,7 @@ import org.mapstruct.factory.Mappers;
import java.util.Date;
import java.util.Map;
import java.util.Set;
/**
* 在线表单实体对象
@ -139,6 +140,18 @@ public class OnlineForm { @@ -139,6 +140,18 @@ public class OnlineForm {
@TableField(exist = false)
private Map<String, Object> formTypeDictMap;
/**
* 页面id集合
*/
@TableField(exist = false)
private Set<Long> pageIdSet;
/**
* 表单id集合
*/
@TableField(exist = false)
private Set<Long> formIdSet;
@Mapper
public interface OnlineFormModelMapper extends BaseModelMapper<OnlineFormVo, OnlineForm> {
/**

138
common/common-online/src/main/java/apelet/common/online/model/PermissionDataRuleSchema.java

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
package apelet.common.online.model;
import apelet.common.core.base.mapper.BaseModelMapper;
import apelet.common.core.base.model.BaseModel;
import apelet.common.core.validator.UpdateGroup;
import apelet.common.online.vo.PermissionDataRuleSchemaVo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
/**
* 权限数据规则方案表 实体对象
*
* @author chenchuchuan
* @date 2026-07-15
*/
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("zz_permission_data_rule_schema")
public class PermissionDataRuleSchema extends BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
@NotNull(message = "数据验证失败,id不能为空!", groups = {UpdateGroup.class})
private Long id;
/**
* onl_form_head Id
*/
@TableField(value = "form_id")
@NotNull(message = "数据验证失败,form_id不能为空!")
private Long formId;
/**
* 表单元数据编码
*/
@TableField(value = "meta_code")
// @NotBlank(message = "数据验证失败,表单元数据编码不能为空!")
private String metaCode;
/**
* 主表类型1-主表2-单表由后端自动填充前端无需传入
*/
@TableField(value = "table_type")
private Integer tableType;
/**
* 规则编码
*/
@TableField(value = "rule_code")
@NotBlank(message = "数据验证失败,规则编码不能为空!")
private String ruleCode;
/**
* 规则方案名称
*/
@TableField(value = "rule_name")
@NotBlank(message = "数据验证失败,规则方案名称不能为空!")
private String ruleName;
/**
* 条件表达式JSON
*/
@TableField(value = "condition_expression")
@NotBlank(message = "数据验证失败,条件表达式不能为空!")
private String conditionExpression;
/**
* 元数据展示名称
*/
@TableField(value = "meta_show_name")
private String metaShowName;
/**
* 规则类型1-列表权限2-基础数据权限
* @see apelet.common.online.model.constant.DataRuleType
*/
@TableField(value = "rule_type")
private Integer ruleType;
/**
* 状态1-启用0-禁用
*/
@TableField(value = "status")
private Integer status;
/**
* 备注
*/
@TableField(value = "remark")
private String remark;
/**
* 创建时间
*/
@TableField(value = "create_time")
private Date createTime;
/**
* 创建者
*/
@TableField(value = "create_user_id")
private Long createUserId;
/**
* 更新时间
*/
@TableField(value = "update_time")
private Date updateTime;
/**
* 更新者
*/
@TableField(value = "update_user_id")
private Long updateUserId;
// ========== MapStruct ==========
@Mapper
public interface PermissionDataRuleSchemaModelMapper
extends BaseModelMapper<PermissionDataRuleSchemaVo, PermissionDataRuleSchema> {
}
public static final PermissionDataRuleSchemaModelMapper INSTANCE =
Mappers.getMapper(PermissionDataRuleSchemaModelMapper.class);
}

69
common/common-online/src/main/java/apelet/common/online/model/RoleFormButton.java

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
package apelet.common.online.model;
import apelet.common.core.base.mapper.BaseModelMapper;
import apelet.common.core.base.model.BaseModel;
import apelet.common.core.validator.UpdateGroup;
import apelet.common.online.vo.RoleFormButtonVo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;
/**
* 角色-表单按钮权限配置实体对象
*
* @author chenchuchuan
* @date 2026-07-17
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("xy_sys_role_form_button")
public class RoleFormButton extends BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id")
@NotNull(message = "数据验证失败,id不能为空!", groups = {UpdateGroup.class})
private Long id;
/** 角色ID */
@TableField(value = "role_id")
@NotNull(message = "数据验证失败,角色ID不能为空!")
private Long roleId;
/** 在线表单ID */
@TableField(value = "form_id")
@NotNull(message = "数据验证失败,表单ID不能为空!")
private Long formId;
/** 按钮配置JSON */
@TableField(value = "button_config")
private String buttonConfig;
@TableField(value = "create_time")
private Date createTime;
@TableField(value = "create_user_id")
private Long createUserId;
@TableField(value = "update_time")
private Date updateTime;
@TableField(value = "update_user_id")
private Long updateUserId;
@Mapper
public interface RoleFormButtonModelMapper
extends BaseModelMapper<RoleFormButtonVo, RoleFormButton> {
}
public static final RoleFormButtonModelMapper INSTANCE =
Mappers.getMapper(RoleFormButtonModelMapper.class);
}

1
common/common-online/src/main/java/apelet/common/online/model/constant/AttributeEnum.java

@ -57,6 +57,7 @@ public enum AttributeEnum { @@ -57,6 +57,7 @@ public enum AttributeEnum {
BACK_PAGE("backPage", "返回上一页"),
OPEN_URL_PAGE("openUrlPage", "打开前端页面"),
LISTEN_TIMER_ELAPSED("listentimerElapsed", "前端定时发送请求,触发此界面事件。"),
RESETROWS("resetrows", "重置行数据"),
;

35
common/common-online/src/main/java/apelet/common/online/model/constant/DataRuleType.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
package apelet.common.online.model.constant;
import java.util.HashMap;
import java.util.Map;
/**
* 数据权限规则类型常量
*
* @author chenchuchuan
* @date 2026-07-21
*/
public final class DataRuleType {
/**
* 列表权限
*/
public static final int LIST = 1;
/**
* 基础数据权限
*/
public static final int BASIC_DATA = 2;
private static final Map<Object, String> DICT_MAP = new HashMap<>(2);
static {
DICT_MAP.put(LIST, "列表权限");
DICT_MAP.put(BASIC_DATA, "基础数据权限");
}
public static boolean isValid(Integer value) {
return value != null && DICT_MAP.containsKey(value);
}
private DataRuleType() {
}
}

1
common/common-online/src/main/java/apelet/common/online/model/constant/ShowTypeEnum.java

@ -13,6 +13,7 @@ import java.util.stream.Collectors; @@ -13,6 +13,7 @@ import java.util.stream.Collectors;
public enum ShowTypeEnum {
OPEN_ONLINE_MODAL("openOnlineModal", "弹窗"),
RESET_ONLINE_PAGE("resetOnlinePage", "重置跳转页面"),
OPEN_ONLINE_PAGE("openOnlinePage", "跳转页面");
@Getter

20
common/common-online/src/main/java/apelet/common/online/service/FormButtonPermissionChecker.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
package apelet.common.online.service;
/**
* 在线表单按钮权限检查器接口
* tenant-admin 模块实现并注册为 Spring Bean
*
* @author chenchuchuan
* @date 2026-07-17
*/
public interface FormButtonPermissionChecker {
/**
* 检查当前用户是否有指定表单上指定按钮的操作权限
*
* @param formId 在线表单ID (zz_online_form.form_id)
* @param buttonName 按钮名称对应 operationList 中的 name 字段
* @return true-有权限false-无权限
*/
boolean hasPermission(Long formId, String buttonName);
}

49
common/common-online/src/main/java/apelet/common/online/service/IPermissionDataRuleSchemaService.java

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
package apelet.common.online.service;
import apelet.common.core.base.service.IBaseService;
import apelet.common.core.object.CallResult;
import apelet.common.online.model.PermissionDataRuleSchema;
import java.util.List;
/**
* 权限数据规则方案表 服务接口
*
* @author chenchuchuan
* @date 2026-07-15
*/
public interface IPermissionDataRuleSchemaService extends IBaseService<PermissionDataRuleSchema, Long> {
/**
* 分页条件查询规则方案列表
*
* @param filter 过滤条件
* @param orderBy 排序语句
* @return 规则方案列表
*/
List<PermissionDataRuleSchema> getListByFilter(PermissionDataRuleSchema filter, String orderBy);
/**
* 根据 formId 查询启用状态的规则运行时用
*
* @param formId 在线表ID
* @return 启用状态的规则列表
*/
List<PermissionDataRuleSchema> listByFormId(Long formId);
/**
* 校验关联数据验证 formId 对应的是主表或单表并自动填充 tableType
*
* @param schema 待校验的规则方案对象
* @return 校验结果
*/
CallResult verifyRelatedData(PermissionDataRuleSchema schema);
/**
* 保存新增 ID生成 + 默认状态设置
*
* @param schema 新增的规则方案对象
* @return 新增后的规则方案对象
*/
PermissionDataRuleSchema saveNew(PermissionDataRuleSchema schema);
}

84
common/common-online/src/main/java/apelet/common/online/service/IRoleFormButtonService.java

@ -0,0 +1,84 @@ @@ -0,0 +1,84 @@
package apelet.common.online.service;
import apelet.common.core.base.service.IBaseService;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.MyOrderParam;
import apelet.common.core.object.MyPageData;
import apelet.common.core.object.MyPageParam;
import apelet.common.online.dto.RoleFormButtonDto;
import apelet.common.online.model.RoleFormButton;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 角色-表单按钮权限配置服务接口
*
* @author chenchuchuan
* @date 2026-07-17
*/
public interface IRoleFormButtonService
extends IBaseService<RoleFormButton, Long> {
/**
* 分页条件查询
*/
List<RoleFormButton> getListByFilter(RoleFormButton filter, String orderBy);
// ========== 核心业务接口 ==========
/**
* 查询当前登录用户对指定表单的按钮权限合并所有角色的并集
*
* @param formId 在线表单ID
* @return 包含 formId, buttonList(含name+checked的列表)
*/
Map<String, Set<String>> getButtonConfigVo(Long formId);
/**
* 批量保存按钮权限配置同一事务内全部成功或全部回滚
*
* @param dtoList 保存入参列表
* @return 保存结果
*/
CallResult batchSaveButtonConfig(List<RoleFormButtonDto> dtoList);
/**
* 保存单条按钮权限配置仅供内部批量方法调用不做事务注解
*
* @param dto 保存入参
* @return 保存结果
*/
CallResult saveButtonConfig(RoleFormButtonDto dto);
/**
* 根据角色ID查询该角色下所有绑定的按钮权限配置
*
* @param roleId 角色ID
* @return 按钮权限配置列表
*/
List<RoleFormButton> listByRoleId(Long roleId);
/**
* 根据角色ID和表单ID查询唯一配置记录
*/
RoleFormButton getByRoleIdAndFormId(Long roleId, Long formId);
/**
* 删除角色下绑定的所有按钮权限配置角色删除时调用
*/
void removeByRoleId(Long roleId);
/**
* 分页查询所有在线表单下的所有按钮
* 遍历全部在线表单从每个表单的 widgetJson 中提取已开启的按钮
* 如果传了 roleId则只返回该角色已配置的表单并合并已保存的勾选状态
*
* @param roleId 角色ID可选用于过滤已配置的表单
* @param orderParam 排序参数
* @param pageParam 分页参数
* @return 分页结果每个元素包含 formId, formName, buttonList[{name, checked}]
*/
MyPageData<Map<String, Object>> listAllFormButtons(Long roleId, MyOrderParam orderParam, MyPageParam pageParam);
}

10
common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java

@ -27,6 +27,16 @@ public interface OnlineColumnService extends IBaseService<OnlineColumn, Long> { @@ -27,6 +27,16 @@ public interface OnlineColumnService extends IBaseService<OnlineColumn, Long> {
List<OnlineColumn> saveNewList(List<SqlTableColumn> columnList, Long onlineTableId);
/**
* 保存新增数据表字段
*
* @param sqlTableColumn 新增数据表字段对象
* @param tableId 在线表对象的主键Id
* @param columnComment 字段名
* @return 插入的在线表字段数据
*/
long saveBySqlTable(SqlTableColumn sqlTableColumn, Long tableId, String columnComment);
/**
* 更新数据对象
*
* @param onlineColumn 更新的对象

17
common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java

@ -1,9 +1,13 @@ @@ -1,9 +1,13 @@
package apelet.common.online.service;
import apelet.common.core.base.service.IBaseService;
import apelet.common.core.object.ResponseResult;
import apelet.common.dbutil.object.SqlTable;
import apelet.common.dbutil.object.SqlTableColumn;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.online.model.OnlineDatasource;
import apelet.common.online.model.OnlineDatasourceRelation;
import apelet.common.online.model.OnlineDblink;
import java.util.List;
import java.util.Set;
@ -82,4 +86,17 @@ public interface OnlineDatasourceRelationService extends IBaseService<OnlineData @@ -82,4 +86,17 @@ public interface OnlineDatasourceRelationService extends IBaseService<OnlineData
*/
List<OnlineDatasourceRelation> getOnlineDatasourceRelationListWithRelation(
OnlineDatasourceRelation filter, String orderBy);
/***
* 功能: 迁移 OnlinePageController#addOnlineDatasourceRelation(OnlFormHead, OnlineDatasource, String, OnlineDblink)
* 方法创建数据表关联关系 先查出改元数据表的所有附表关系
*
* @author ${chenchuchuan}
* @param onlFormHead 主表信息
* @param onlineDatasource 数据库链接配置
* @param errorMessage 错误描述
* @param onlineDblink 数据库实际链接
* @return ResponseResult<Long>
*/
ResponseResult<Long> addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink);
}

3
common/common-online/src/main/java/apelet/common/online/service/OnlineDictService.java

@ -4,6 +4,7 @@ import apelet.common.core.base.service.IBaseService; @@ -4,6 +4,7 @@ import apelet.common.core.base.service.IBaseService;
import apelet.common.online.model.OnlineDict;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@ -75,4 +76,6 @@ public interface OnlineDictService extends IBaseService<OnlineDict, Long> { @@ -75,4 +76,6 @@ public interface OnlineDictService extends IBaseService<OnlineDict, Long> {
* @return 在线字典对象集合
*/
List<OnlineDict> getOnlineDictListFromCache(Set<Long> dictIdSet);
Map<String, String> getDictMap(Long dictId);
}

8
common/common-online/src/main/java/apelet/common/online/service/OnlineFormService.java

@ -139,4 +139,12 @@ public interface OnlineFormService extends IBaseService<OnlineForm, Long> { @@ -139,4 +139,12 @@ public interface OnlineFormService extends IBaseService<OnlineForm, Long> {
* @return
*/
ObjectValue getObjectValue(Map<String, Object> masterData, Map<String, Object> savedata, String tableName);
/**
* 根据表单类型查询表单列表
*
* @param formType 表单类型
* @return 表单列表
*/
List<OnlineForm> queryListByFormType(List<Integer> formType);
}

7
common/common-online/src/main/java/apelet/common/online/service/OnlinePageService.java

@ -135,4 +135,11 @@ public interface OnlinePageService extends IBaseService<OnlinePage, Long> { @@ -135,4 +135,11 @@ public interface OnlinePageService extends IBaseService<OnlinePage, Long> {
* @return 在线表单页面列表
*/
List<OnlinePage> getInListWithNonTenant(List<Long> pageIds, String orderBy);
/**
* 查询启用的在线表单页面列表
*
* @return 在线表单页面列表
*/
List<OnlinePage> queryByEnableList();
}

69
common/common-online/src/main/java/apelet/common/online/service/impl/FormButtonPermissionCheckerImpl.java

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
package apelet.common.online.service.impl;
import apelet.common.core.object.TokenData;
import apelet.common.online.model.RoleFormButton;
import apelet.common.online.service.FormButtonPermissionChecker;
import apelet.common.online.service.IRoleFormButtonService;
import apelet.common.online.util.WidgetJsonButtonParser;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/**
* 按钮权限检查器实现
* 通过当前用户的所有角色查询 xy_sys_role_form_button
* 判断是否有指定表单-按钮的权限
*
* @author chenchuchuan
* @date 2026-07-17
*/
@Slf4j
@Component
public class FormButtonPermissionCheckerImpl implements FormButtonPermissionChecker {
@Autowired
private IRoleFormButtonService roleFormButtonService;
@Override
public boolean hasPermission(Long formId, String buttonName) {
try {
// 1. 获取当前登录用户信息
TokenData tokenData = TokenData.takeFromRequest();
if (tokenData == null) {
log.warn("TokenData 为空,无法校验按钮权限");
return false;
}
Long userId = tokenData.getUserId();
if (userId == null) {
return false;
}
// 2. 从 TokenData 获取用户的所有角色ID
if (StrUtil.isBlank(tokenData.getRoleIds())) {
return false;
}
List<Long> roleIds = StrUtil.split(tokenData.getRoleIds(), ',').stream().map(Long::valueOf).collect(Collectors.toList());
// 3. 遍历角色,检查是否有任一角色授权了该表单的该按钮
for (Long roleId : roleIds) {
RoleFormButton config = roleFormButtonService.getByRoleIdAndFormId(roleId, formId);
if (config != null && config.getButtonConfig() != null) {
List<WidgetJsonButtonParser.ButtonInfo> buttons = JSON.parseArray(config.getButtonConfig(), WidgetJsonButtonParser.ButtonInfo.class);
for (WidgetJsonButtonParser.ButtonInfo btn : buttons) {
if (buttonName.equals(btn.getName()) && Boolean.TRUE.equals(btn.getEnabled())) {
return true;
}
}
}
}
} catch (Exception e) {
log.error("校验按钮权限时发生异常", e);
}
return false;
}
}

29
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java

@ -31,13 +31,17 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -31,13 +31,17 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
import com.google.common.base.CaseFormat;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* 字段数据数据操作服务类
@ -106,6 +110,29 @@ public class OnlineColumnServiceImpl extends BaseService<OnlineColumn, Long> imp @@ -106,6 +110,29 @@ public class OnlineColumnServiceImpl extends BaseService<OnlineColumn, Long> imp
return onlineColumnList;
}
@Override
public long saveBySqlTable(SqlTableColumn sqlTableColumn, Long tableId, String columnComment) {
OnlineColumn onlineColumn = new OnlineColumn();
BeanUtil.copyProperties(sqlTableColumn, onlineColumn, false);
onlineColumn.setColumnId(idGenerator.nextLongId());
onlineColumn.setTableId(tableId);
// 传了描述,并且不等于原先的描述,并且原先的为空或者为"null",就赋值
if (StringUtil.isNotBlank(columnComment) && !StringUtil.equals(columnComment, onlineColumn.getColumnComment())
&& (StringUtil.isBlank(onlineColumn.getColumnComment()) || StringUtil.equals("null", onlineColumn.getColumnComment()))) {
onlineColumn.setColumnComment(columnComment);
}
if (StringUtil.isBlank(onlineColumn.getColumnComment())) {
onlineColumn.setColumnComment(onlineColumn.getColumnName());
}
this.setDefault(sqlTableColumn, onlineColumn);
if (super.save(onlineColumn)) {
sqlTableColumn.setColumnName(onlineColumn.getColumnName().toLowerCase());
return onlineColumn.getColumnId();
}
return 0;
}
/**
* 更新数据对象
*

218
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java

@ -4,21 +4,31 @@ import apelet.common.core.annotation.MyDataSourceResolver; @@ -4,21 +4,31 @@ import apelet.common.core.annotation.MyDataSourceResolver;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.core.base.service.BaseService;
import apelet.common.core.constant.ApplicationConstant;
import apelet.common.core.constant.ErrorCodeEnum;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.MyRelationParam;
import apelet.common.core.object.ResponseResult;
import apelet.common.core.object.TokenData;
import apelet.common.core.util.DefaultDataSourceResolver;
import apelet.common.core.util.MyModelUtil;
import apelet.common.dbutil.object.SqlTable;
import apelet.common.dbutil.object.SqlTableColumn;
import apelet.common.generator.model.OnlFormField;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.generator.service.IOnlFormFieldService;
import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.online.dao.OnlineDatasourceRelationMapper;
import apelet.common.online.dao.OnlineDatasourceTableMapper;
import apelet.common.online.dto.OnlineDatasourceRelationDto;
import apelet.common.online.model.*;
import apelet.common.online.service.*;
import apelet.common.online.util.OnlineRedisKeyUtil;
import apelet.common.redis.util.CommonRedisUtil;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
@ -30,6 +40,7 @@ import org.springframework.stereotype.Service; @@ -30,6 +40,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 数据源关联数据操作服务类
@ -61,6 +72,14 @@ public class OnlineDatasourceRelationServiceImpl @@ -61,6 +72,14 @@ public class OnlineDatasourceRelationServiceImpl
private RedissonClient redissonClient;
@Autowired
private CommonRedisUtil commonRedisUtil;
@Autowired
private OnlineDblinkService onlineDblinkService;
@Autowired
private IOnlFormHeadService iOnlFormHeadService;
@Autowired
private OnlineDatasourceRelationService onlineDatasourceRelationService;
@Autowired
private IOnlFormFieldService onlFormFieldService;
/**
* 返回当前Service的主表Mapper对象
@ -289,4 +308,203 @@ public class OnlineDatasourceRelationServiceImpl @@ -289,4 +308,203 @@ public class OnlineDatasourceRelationServiceImpl
}
return CallResult.ok();
}
@Override
public ResponseResult<Long> addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink) {
//历史数据 --做比较新数据写入,已存在的不用管,不存在的删除
OnlineDatasourceRelation onlineDatasourceRelations = new OnlineDatasourceRelation();
onlineDatasourceRelations.setDatasourceId(onlineDatasource.getDatasourceId());
List<OnlineDatasourceRelation> onlineDatasourceRelationList =
onlineDatasourceRelationService.getOnlineDatasourceRelationListWithRelation(onlineDatasourceRelations, null);
Map<String, OnlineDatasourceRelation> oldmap01 =
onlineDatasourceRelationList.stream().collect(Collectors.toMap(obj -> obj.getMasterColumnId() + "_" + obj.getRelationType() + "_" + obj.getSlaveTableId(), obj -> obj,
(key1, key2) -> key1
));
//查出表单元数据的所有附表关系
//主表id
List<OnlineTable> onlineTableList = onlineTableService.list(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, onlFormHead.getTableName().toLowerCase()));
//创建一对多关系:1
List<OnlFormHead> oneToMoreObjsList = iOnlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().eq(OnlFormHead::getLine, 1).eq(OnlFormHead::getSort, onlFormHead.getTableName()));
if (!oneToMoreObjsList.isEmpty()) {
List<Long> tableIdList = onlineTableList.stream().map(OnlineTable::getTableId).collect(Collectors.toList());
// //主表列id对象
OnlineColumn onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper<OnlineColumn>()
.eq(OnlineColumn::getColumnName, "id")
.in(OnlineColumn::getTableId, tableIdList));
for (OnlFormHead formHead : oneToMoreObjsList) {
List<OnlineTable> onlineTableListfu = onlineTableService.list(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, formHead.getTableName().toLowerCase()));
//附表关联的主表字段+附表id
if ((onlineTableListfu == null || onlineTableListfu.size() < 1) || !oldmap01.containsKey(onlineColumn.getColumnId() + "_1_" + onlineTableListfu.get(0).getTableId())) {
OnlineDatasourceRelationDto onlineDatasourceRelationDto = new OnlineDatasourceRelationDto();
onlineDatasourceRelationDto.setDatasourceId(onlineDatasource.getDatasourceId());
//关联名称
onlineDatasourceRelationDto.setRelationName(onlFormHead.getTableName() + "_" + formHead.getTableName());
//关联标识
onlineDatasourceRelationDto.setVariableName(onlFormHead.getTableName() + "_" + formHead.getTableName());
//关联类型
onlineDatasourceRelationDto.setRelationType(1);
//主键字段id
onlineDatasourceRelationDto.setMasterColumnId(onlineColumn.getColumnId());
//从表名称
onlineDatasourceRelationDto.setSlaveTableName(formHead.getTableName());
//从表字段名称
onlineDatasourceRelationDto.setSlaveColumnName(formHead.getSortFiled());
//是否级联删除
onlineDatasourceRelationDto.setCascadeDelete(true);
//是否左连接查询
onlineDatasourceRelationDto.setLeftJoin(false);
OnlineDatasourceRelation onlineDatasourceRelation =
MyModelUtil.copyTo(onlineDatasourceRelationDto, OnlineDatasourceRelation.class);
String appCode = TokenData.takeFromRequest().getAppCode();
if (!StrUtil.equals(onlineDatasource.getAppCode(), appCode)) {
errorMessage = "数据验证失败,当前应用并不包含该数据源Id!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// OnlineDblink onlineDblink = onlineDblinkService.getById(onlineDatasource.getDblinkId());
SqlTable slaveTable = onlineDblinkService.getDblinkTable(
onlineDblink, onlineDatasourceRelationDto.getSlaveTableName());
if (slaveTable == null) {
errorMessage = "数据验证失败,指定的数据表不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
SqlTableColumn slaveColumn = null;
for (SqlTableColumn column : slaveTable.getColumnList()) {
if (column.getColumnName().equals(onlineDatasourceRelationDto.getSlaveColumnName())) {
slaveColumn = column;
break;
}
}
if (slaveColumn == null) {
errorMessage = "数据验证失败,指定的数据表字段 [" + onlineDatasourceRelationDto.getSlaveColumnName() + "] 不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// 验证关联Id的数据合法性
CallResult callResult =
onlineDatasourceRelationService.verifyRelatedData(onlineDatasourceRelation, null);
if (!callResult.isSuccess()) {
errorMessage = callResult.getErrorMessage();
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
onlineDatasourceRelation = onlineDatasourceRelationService.saveNew(onlineDatasourceRelation, slaveTable, slaveColumn);
} else {
//更新表字段
SqlTable slaveTable = onlineDblinkService.getDblinkTable(
onlineDblink, formHead.getTableName());
if (slaveTable == null) {
errorMessage = "数据验证失败,指定的数据表不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
List<SqlTableColumn> sqlTableColumnList = slaveTable.getColumnList();
Set<Long> tableids = new HashSet<>();
tableids.add(onlineTableListfu.get(0).getTableId());
List<OnlineColumn> listColumn = onlineColumnService.getOnlineColumnListByTableIds(tableids);
List<String> columnNames = listColumn.stream().map(OnlineColumn::getColumnName).collect(Collectors.toList());
sqlTableColumnList = sqlTableColumnList.stream().filter(x -> !columnNames.contains(x.getColumnName().toLowerCase())).collect(Collectors.toList());
onlineColumnService.saveNewList(sqlTableColumnList, onlineTableListfu.get(0).getTableId());
}
}
}
//创建一对一关系:0
List<OnlFormField> onlFormFieldList = onlFormFieldService.list(new LambdaQueryWrapper<OnlFormField>()
.eq(OnlFormField::getHeadId, onlFormHead.getId()).isNotNull(OnlFormField::getRefPropert));
//List<OnlFormHead> oneToMoreObjsList = iOnlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().eq(OnlFormHead::getLine, 1).eq(OnlFormHead::getSort, onlFormHead.getTableName()));
if (!onlFormFieldList.isEmpty()) {
Set<String> updatedTable = new HashSet<>();
//附表
for (OnlFormField onlFormField : onlFormFieldList) {
//附表
List<OnlFormHead> onlFormHeadList = iOnlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().eq(OnlFormHead::getId, onlFormField.getRefPropert()));
if (onlFormHeadList.isEmpty()) {
continue;
}
OnlFormHead formHead = onlFormHeadList.get(0);
//根据表名去找在线附表
List<OnlineTable> fuonlineTableList = onlineTableService.list(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, onlFormHeadList.get(0).getTableName()));
//主表id对象
OnlineColumn onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper<OnlineColumn>()
.eq(OnlineColumn::getColumnName, onlFormField.getFieldName())
.in(OnlineColumn::getTableId, onlineTableList.get(0).getTableId()));
if (onlineColumn == null) {
onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper<OnlineColumn>()
.eq(OnlineColumn::getColumnName, onlFormField.getFieldName().toLowerCase())
.in(OnlineColumn::getTableId, onlineTableList.get(0).getTableId()));
}
//附表关联的主表字段+附表id
if (fuonlineTableList == null || fuonlineTableList.isEmpty() || (!oldmap01.containsKey(onlineColumn.getColumnId() + "_0_" + fuonlineTableList.get(0).getTableId()))) {
OnlineDatasourceRelationDto onlineDatasourceRelationDto = new OnlineDatasourceRelationDto();
onlineDatasourceRelationDto.setDatasourceId(onlineDatasource.getDatasourceId());
//关联名称
onlineDatasourceRelationDto.setRelationName(onlFormHead.getTableName() + "_" + onlFormField.getFieldName() + "_" + formHead.getTableName());
//关联标识
onlineDatasourceRelationDto.setVariableName(onlFormHead.getTableName() + "_" + onlFormField.getFieldName() + "_" + formHead.getTableName());
//关联类型
onlineDatasourceRelationDto.setRelationType(0);
//主键字段id
onlineDatasourceRelationDto.setMasterColumnId(onlineColumn.getColumnId());
//从表名称
onlineDatasourceRelationDto.setSlaveTableName(formHead.getTableName());
//从表字段名称
onlineDatasourceRelationDto.setSlaveColumnName("id");
//是否级联删除
onlineDatasourceRelationDto.setCascadeDelete(true);
//是否左连接查询
onlineDatasourceRelationDto.setLeftJoin(false);
OnlineDatasourceRelation onlineDatasourceRelation =
MyModelUtil.copyTo(onlineDatasourceRelationDto, OnlineDatasourceRelation.class);
String appCode = TokenData.takeFromRequest().getAppCode();
if (!StrUtil.equals(onlineDatasource.getAppCode(), appCode)) {
errorMessage = "数据验证失败,当前应用并不包含该数据源Id!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// OnlineDblink onlineDblink = onlineDblinkService.getById(onlineDatasource.getDblinkId());
SqlTable slaveTable = onlineDblinkService.getDblinkTable(
onlineDblink, onlineDatasourceRelationDto.getSlaveTableName());
if (slaveTable == null) {
errorMessage = "数据验证失败,指定的数据表不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
SqlTableColumn slaveColumn = null;
for (SqlTableColumn column : slaveTable.getColumnList()) {
if (column.getColumnName().equals(onlineDatasourceRelationDto.getSlaveColumnName())) {
slaveColumn = column;
break;
}
}
if (slaveColumn == null) {
errorMessage = "数据验证失败,指定的数据表字段 [" + onlineDatasourceRelationDto.getSlaveColumnName() + "] 不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
// 验证关联Id的数据合法性
CallResult callResult =
onlineDatasourceRelationService.verifyRelatedData(onlineDatasourceRelation, null);
if (!callResult.isSuccess()) {
errorMessage = callResult.getErrorMessage();
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
onlineDatasourceRelation = onlineDatasourceRelationService.saveNew(onlineDatasourceRelation, slaveTable, slaveColumn);
} else {
if (updatedTable.contains(formHead.getTableName())) {
continue;
}
updatedTable.add(formHead.getTableName());
//更新表字段
SqlTable slaveTable = onlineDblinkService.getDblinkTable(
onlineDblink, formHead.getTableName());
if (slaveTable == null) {
errorMessage = "数据验证失败,指定的数据表不存在!";
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
}
List<SqlTableColumn> sqlTableColumnList = slaveTable.getColumnList();
Set<Long> tableids = new HashSet<>();
tableids.add(fuonlineTableList.get(0).getTableId());
List<OnlineColumn> listColumn = onlineColumnService.getOnlineColumnListByTableIds(tableids);
List<String> columnNames = listColumn.stream().map(OnlineColumn::getColumnName).collect(Collectors.toList());
sqlTableColumnList = sqlTableColumnList.stream().filter(x -> !columnNames.contains(x.getColumnName().toLowerCase())).collect(Collectors.toList());
onlineColumnService.saveNewList(sqlTableColumnList, fuonlineTableList.get(0).getTableId());
}
}
}
return null;
}
}

6
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java

@ -11,7 +11,10 @@ import apelet.common.dbutil.object.SqlTable; @@ -11,7 +11,10 @@ import apelet.common.dbutil.object.SqlTable;
import apelet.common.online.dao.OnlineDatasourceMapper;
import apelet.common.online.dao.OnlineDatasourceTableMapper;
import apelet.common.online.dao.OnlinePageDatasourceMapper;
import apelet.common.online.model.*;
import apelet.common.online.model.OnlineDatasource;
import apelet.common.online.model.OnlineDatasourceTable;
import apelet.common.online.model.OnlinePageDatasource;
import apelet.common.online.model.OnlineTable;
import apelet.common.online.service.OnlineDatasourceRelationService;
import apelet.common.online.service.OnlineDatasourceService;
import apelet.common.online.service.OnlineTableService;
@ -83,6 +86,7 @@ public class OnlineDatasourceServiceImpl extends BaseService<OnlineDatasource, L @@ -83,6 +86,7 @@ public class OnlineDatasourceServiceImpl extends BaseService<OnlineDatasource, L
TokenData tokenData = TokenData.takeFromRequest();
OnlineTable onlineTable = onlineTableService.saveNewFromSqlTable(sqlTable);
onlineDatasource.setDatasourceId(idGenerator.nextLongId());
onlineDatasource.setDatasourceName(sqlTable.getTableName());
onlineDatasource.setAppCode(tokenData.getAppCode());
onlineDatasource.setMasterTableId(onlineTable.getTableId());
Date now = new Date();

20
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDictServiceImpl.java

@ -15,6 +15,9 @@ import apelet.common.online.service.OnlineDictService; @@ -15,6 +15,9 @@ import apelet.common.online.service.OnlineDictService;
import apelet.common.online.util.OnlineRedisKeyUtil;
import apelet.common.redis.util.CommonRedisUtil;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
import lombok.extern.slf4j.Slf4j;
@ -184,4 +187,21 @@ public class OnlineDictServiceImpl extends BaseService<OnlineDict, Long> impleme @@ -184,4 +187,21 @@ public class OnlineDictServiceImpl extends BaseService<OnlineDict, Long> impleme
}
return CallResult.ok();
}
@Override
public Map<String, String> getDictMap(Long dictId) {
Map<String, String> map = new HashMap<>();
OnlineDict onlineDict = this.getOnlineDictFromCache(dictId);
if (onlineDict == null) {
return map;
}
JSONObject dictJson = JSON.parseObject(onlineDict.getDictDataJson());
JSONArray jsonArray = dictJson.getJSONArray("dictData");
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
map.put(jsonObject.getString("id"), jsonObject.getString("name"));
}
return map;
}
}

20
common/common-online/src/main/java/apelet/common/online/service/impl/OnlineFormServiceImpl.java

@ -26,6 +26,7 @@ import apelet.common.orm.impl.LinkEntityColumn; @@ -26,6 +26,7 @@ import apelet.common.orm.impl.LinkEntityColumn;
import apelet.common.redis.util.CommonRedisUtil;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
@ -35,7 +36,6 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -35,7 +36,6 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
@ -358,6 +358,7 @@ public class OnlineFormServiceImpl extends BaseService<OnlineForm, Long> impleme @@ -358,6 +358,7 @@ public class OnlineFormServiceImpl extends BaseService<OnlineForm, Long> impleme
return pluginResult;
}
try {
//todo 这里要写 数据权限那块
JSONObject jsonObject = JSONObject.parseObject(onlineForm.getWidgetJson());
String deviceType = AppDeviceType.getDeviceType();
JSONArray jsonArray = jsonObject.getJSONObject(deviceType).getJSONArray("pluginList");
@ -460,6 +461,23 @@ public class OnlineFormServiceImpl extends BaseService<OnlineForm, Long> impleme @@ -460,6 +461,23 @@ public class OnlineFormServiceImpl extends BaseService<OnlineForm, Long> impleme
return objectValue;
}
@Override
public List<OnlineForm> queryListByFormType(List<Integer> formType) {
// 查询已发布的 page
List<OnlinePage> pageList = onlinePageService.queryByEnableList();
if (CollectionUtil.isEmpty(pageList)) {
return Collections.emptyList();
}
Set<Long> pageIdSet = pageList.stream().map(OnlinePage::getPageId).collect(Collectors.toSet());
List<OnlineForm> formList = onlineFormMapper.selectList(new LambdaQueryWrapper<OnlineForm>().in(OnlineForm::getFormType, formType).in(OnlineForm::getPageId, pageIdSet));
if (CollectionUtil.isNotEmpty(formList)) {
formList.forEach(item -> {
item.setWidgetJson(null);
});
}
return formList;
}
/**
* 设置关联表字段转换为对象
*

5
common/common-online/src/main/java/apelet/common/online/service/impl/OnlinePageServiceImpl.java

@ -296,4 +296,9 @@ public class OnlinePageServiceImpl extends BaseService<OnlinePage, Long> impleme @@ -296,4 +296,9 @@ public class OnlinePageServiceImpl extends BaseService<OnlinePage, Long> impleme
}
return onlinePageMapper.selectList(queryWrapper);
}
@Override
public List<OnlinePage> queryByEnableList() {
return onlinePageMapper.selectList(new LambdaQueryWrapper<OnlinePage>().eq(OnlinePage::getPublished, true));
}
}

93
common/common-online/src/main/java/apelet/common/online/service/impl/PermissionDataRuleSchemaServiceImpl.java

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
package apelet.common.online.service.impl;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.core.base.service.BaseService;
import apelet.common.core.object.CallResult;
import apelet.common.generator.model.OnlFormHead;
import apelet.common.generator.service.IOnlFormHeadService;
import apelet.common.online.dao.PermissionDataRuleSchemaMapper;
import apelet.common.online.model.PermissionDataRuleSchema;
import apelet.common.online.model.constant.DataRuleType;
import apelet.common.online.service.IPermissionDataRuleSchemaService;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 权限数据规则方案表 服务实现类
*
* @author chenchuchuan
* @date 2026-07-15
*/
@Service("permissionDataRuleSchemaService")
@Slf4j
public class PermissionDataRuleSchemaServiceImpl extends BaseService<PermissionDataRuleSchema, Long> implements IPermissionDataRuleSchemaService {
@Autowired
PermissionDataRuleSchemaMapper mapper;
@Autowired
private IdGeneratorWrapper idGenerator;
@Autowired
private IOnlFormHeadService onlFormHeadService;
// ========== BaseService 要求重写 ==========
@Override
protected BaseDaoMapper<PermissionDataRuleSchema> mapper() {
return mapper;
}
// ========== 分页查询 ==========
@Override
public List<PermissionDataRuleSchema> getListByFilter(PermissionDataRuleSchema filter, String orderBy) {
return mapper.getListByFilter(filter, orderBy);
}
// ========== 按 formId 查询启用状态的规则 ==========
@Override
public List<PermissionDataRuleSchema> listByFormId(Long formId) {
LambdaQueryWrapper<PermissionDataRuleSchema> qw = new LambdaQueryWrapper<>();
qw.eq(PermissionDataRuleSchema::getFormId, formId)
.eq(PermissionDataRuleSchema::getStatus, 1);
return this.list(qw);
}
// ========== 新增(含 ID生成 + 默认状态) ==========
@Override
public PermissionDataRuleSchema saveNew(PermissionDataRuleSchema schema) {
if (schema.getStatus() == null) {
schema.setStatus(1);
}
if (schema.getRuleType() == null) {
schema.setRuleType(DataRuleType.LIST);
}
this.save(schema);
return schema;
}
// ========== 核心校验: formId → 主表/单表,自动填充 tableType ==========
@Override
public CallResult verifyRelatedData(PermissionDataRuleSchema schema) {
// 1. 通过 tableName 查询 onl_form_head 获取 tableType
OnlFormHead formHead = onlFormHeadService.getById(schema.getFormId());
if (formHead == null) {
return CallResult.error("数据验证失败,无法获取表类型信息,请检查表单配置!");
}
// 2. tableType 校验: 0=附表拒绝, 1=主表, 2=单表
String tableType = formHead.getTableType();
if (!"1".equals(tableType) && !"2".equals(tableType)) {
return CallResult.error("数据验证失败,仅主表和单表支持配置数据规则,当前为附表!");
}
// 3. 自动填充 tableType
schema.setTableType(Integer.valueOf(tableType));
return CallResult.ok();
}
}

280
common/common-online/src/main/java/apelet/common/online/service/impl/RoleFormButtonServiceImpl.java

@ -0,0 +1,280 @@ @@ -0,0 +1,280 @@
package apelet.common.online.service.impl;
import apelet.common.core.base.dao.BaseDaoMapper;
import apelet.common.core.base.service.BaseService;
import apelet.common.core.object.*;
import apelet.common.online.dao.RoleFormButtonMapper;
import apelet.common.online.dto.RoleFormButtonDto;
import apelet.common.online.model.OnlineForm;
import apelet.common.online.model.OnlinePage;
import apelet.common.online.model.RoleFormButton;
import apelet.common.online.service.IRoleFormButtonService;
import apelet.common.online.service.OnlineFormService;
import apelet.common.online.service.OnlinePageService;
import apelet.common.online.util.WidgetJsonButtonParser;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;
/**
* 角色-表单按钮权限配置服务实现
*
* @author chenchuchuan
* @date 2026-07-17
*/
@Service
@Slf4j
public class RoleFormButtonServiceImpl extends BaseService<RoleFormButton, Long> implements IRoleFormButtonService {
@Autowired
private RoleFormButtonMapper mapper;
@Autowired
private IdGeneratorWrapper idGenerator;
@Autowired
private OnlineFormService onlineFormService;
@Autowired
private OnlinePageService onlinePageService;
@Override
protected BaseDaoMapper<RoleFormButton> mapper() {
return mapper;
}
@Override
public List<RoleFormButton> getListByFilter(
RoleFormButton filter, String orderBy) {
return mapper.getListByFilter(filter, orderBy);
}
// ========== 核心:查询按钮配置VO(合并当前用户所有角色) ==========
@Override
public Map<String, Set<String>> getButtonConfigVo(Long formId) {
// 1. 获取当前登录用户的所有角色
Set<Long> roleIds = getCurrentUserRoleIds();
// Set<Long> roleIds = CollectionUtil.newHashSet(2080534691714174976L, 1694263314487447643L);
if (CollectionUtil.isEmpty(roleIds)) {
return Collections.emptyMap();
}
List<RoleFormButton> roleFormButtonList = mapper.selectList(new LambdaQueryWrapper<RoleFormButton>().in(RoleFormButton::getRoleId, roleIds));
if (CollectionUtil.isEmpty(roleFormButtonList)) {
return Collections.emptyMap();
}
// 2. 遍历所有角色,合并各角色在该表单的勾选按钮(取并集)
Map<String, Set<String>> roleCheckedNamesMap = new HashMap<>();
for (RoleFormButton roleFormButton : roleFormButtonList) {
if (StringUtils.isBlank(roleFormButton.getButtonConfig())) {
continue;
}
List<WidgetJsonButtonParser.ButtonInfo> buttonInfoList = JSON.parseArray(roleFormButton.getButtonConfig(), WidgetJsonButtonParser.ButtonInfo.class);
Set<String> checkedNames = buttonInfoList.stream().filter(btn -> btn.getEnabled() != null && btn.getEnabled()).map(WidgetJsonButtonParser.ButtonInfo::getName).collect(Collectors.toSet());
roleCheckedNamesMap.computeIfAbsent(Long.toString(roleFormButton.getFormId()), k -> new HashSet<>()).addAll(checkedNames);
}
return roleCheckedNamesMap;
}
/**
* 获取当前登录用户的所有角色ID集合 TokenData 中解析
*/
private Set<Long> getCurrentUserRoleIds() {
TokenData tokenData = TokenData.takeFromRequest();
if (tokenData == null || StrUtil.isBlank(tokenData.getRoleIds())) {
return Collections.emptySet();
}
return StrUtil.split(tokenData.getRoleIds(), ',').stream().map(Long::valueOf).collect(Collectors.toSet());
}
// ========== 核心:批量保存按钮配置(事务) ==========
@Override
@Transactional(rollbackFor = Exception.class)
public CallResult batchSaveButtonConfig(List<RoleFormButtonDto> dtoList) {
for (RoleFormButtonDto dto : dtoList) {
CallResult result = this.saveButtonConfig(dto);
if (!result.isSuccess()) {
return result;
}
}
return CallResult.ok();
}
// ========== 核心:保存单条按钮配置(无独立事务,由外层统一管理) ==========
@Override
public CallResult saveButtonConfig(RoleFormButtonDto dto) {
// 1. 必填校验
if (dto.getRoleId() == null) {
return CallResult.error("数据验证失败,角色ID不能为空!");
}
if (dto.getFormId() == null) {
return CallResult.error("数据验证失败,表单ID不能为空!");
}
// 2. 构建 button_config JSON
List<Map<String, Object>> configList = new ArrayList<>();
if (dto.getButtonList() != null) {
for (RoleFormButtonDto.ButtonItem item : dto.getButtonList()) {
Map<String, Object> configItem = new HashMap<>();
configItem.put("name", item.getName());
configItem.put("enabled", Objects.nonNull(item.getChecked()) && item.getChecked());
configList.add(configItem);
}
}
String buttonConfigJson = JSON.toJSONString(configList);
// 3. 查询是否已存在配置(role_id + form_id 唯一)
RoleFormButton existing =
getByRoleIdAndFormId(dto.getRoleId(), dto.getFormId());
if (existing != null) {
// 更新
existing.setButtonConfig(buttonConfigJson);
existing.setUpdateTime(new Date());
this.updateById(existing);
} else {
// 新增
RoleFormButton entity = new RoleFormButton();
entity.setRoleId(dto.getRoleId());
entity.setFormId(dto.getFormId());
entity.setButtonConfig(buttonConfigJson);
entity.setId(idGenerator.nextLongId());
this.save(entity);
}
return CallResult.ok();
}
@Override
public List<RoleFormButton> listByRoleId(Long roleId) {
LambdaQueryWrapper<RoleFormButton> qw = new LambdaQueryWrapper<>();
qw.eq(RoleFormButton::getRoleId, roleId);
return this.list(qw);
}
@Override
public RoleFormButton getByRoleIdAndFormId(Long roleId, Long formId) {
LambdaQueryWrapper<RoleFormButton> qw = new LambdaQueryWrapper<>();
qw.eq(RoleFormButton::getRoleId, roleId)
.eq(RoleFormButton::getFormId, formId);
return this.getOne(qw);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeByRoleId(Long roleId) {
LambdaQueryWrapper<RoleFormButton> qw = new LambdaQueryWrapper<>();
qw.eq(RoleFormButton::getRoleId, roleId);
this.remove(qw);
}
private static @NotNull Map<String, Object> getButtonResult(OnlineForm form, List<WidgetJsonButtonParser.ButtonInfo> buttons) {
Map<String, Object> item = new HashMap<>();
item.put("formId", form.getFormId().toString());
item.put("formName", form.getFormName());
List<Map<String, Object>> buttonList = new ArrayList<>();
if (!CollectionUtils.isEmpty(buttons)) {
for (WidgetJsonButtonParser.ButtonInfo btn : buttons) {
Map<String, Object> btnMap = new HashMap<>();
btnMap.put("name", btn.getName());
btnMap.put("checked", false);
buttonList.add(btnMap);
}
}
item.put("buttonList", buttonList);
return item;
}
@Override
public MyPageData<Map<String, Object>> listAllFormButtons(Long roleId, MyOrderParam orderParam, MyPageParam pageParam) {
// 1. 查询已发布的 page(不分页,获取全量)
List<OnlinePage> pageList = onlinePageService.queryByEnableList();
if (CollectionUtil.isEmpty(pageList)) {
return new MyPageData<>(Collections.emptyList(), 0L);
}
Set<Long> pageIdSet = pageList.stream().map(OnlinePage::getPageId).collect(Collectors.toSet());
// 2. 分页查询 OnlineForm
if (pageParam != null) {
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
}
String orderBy = MyOrderParam.buildOrderBy(orderParam, OnlineForm.class);
OnlineForm onlineForm = new OnlineForm();
onlineForm.setPageIdSet(pageIdSet);
List<OnlineForm> formList = onlineFormService.getOnlineFormList(onlineForm, orderBy);
long total = formList instanceof Page ? ((Page) formList).getTotal() : formList.size();
// 3. 如果传了 roleId,查询 RoleFormButton 获取勾选状态
Map<Long, Set<String>> formCheckedMap = Collections.emptyMap();
if (roleId != null) {
List<RoleFormButton> configList = this.listByRoleId(roleId);
if (CollectionUtil.isNotEmpty(configList)) {
formCheckedMap = new HashMap<>(configList.size());
for (RoleFormButton config : configList) {
formCheckedMap.put(config.getFormId(), extractCheckedNames(config));
}
}
}
// 4. 构建结果
List<Map<String, Object>> result = new ArrayList<>();
for (OnlineForm form : formList) {
List<WidgetJsonButtonParser.ButtonInfo> buttons =
WidgetJsonButtonParser.extractEnabledButtons(form.getWidgetJson());
Map<String, Object> item = getButtonResult(form, buttons);
if (roleId != null) {
Set<String> checkedNames = formCheckedMap.get(form.getFormId());
if (CollectionUtil.isNotEmpty(checkedNames)) {
applyCheckedStatus(item, checkedNames);
}
}
result.add(item);
}
return new MyPageData<>(result, total);
}
/**
* RoleFormButton.buttonConfig 中提取已勾选的按钮名称集合
*/
private Set<String> extractCheckedNames(RoleFormButton config) {
if (config.getButtonConfig() == null) {
return Collections.emptySet();
}
List<WidgetJsonButtonParser.ButtonInfo> savedButtons =
JSON.parseArray(config.getButtonConfig(), WidgetJsonButtonParser.ButtonInfo.class);
if (CollectionUtils.isEmpty(savedButtons)) {
return Collections.emptySet();
}
return savedButtons.stream()
.filter(b -> Boolean.TRUE.equals(b.getEnabled()))
.map(WidgetJsonButtonParser.ButtonInfo::getName)
.collect(Collectors.toSet());
}
/**
* 将已勾选的按钮名称集合应用到返回结果中
*/
private void applyCheckedStatus(Map<String, Object> item, Set<String> checkedNames) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> buttonList =
(List<Map<String, Object>>) item.get("buttonList");
if (buttonList != null) {
for (Map<String, Object> btn : buttonList) {
btn.put("checked", checkedNames.contains(btn.get("name")));
}
}
}
}

87
common/common-online/src/main/java/apelet/common/online/util/WidgetFieldTypeMapping.java

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
package apelet.common.online.util;
import java.util.HashMap;
import java.util.Map;
public class WidgetFieldTypeMapping {
private WidgetFieldTypeMapping() {
}
private static final Map<Integer, String> WIDGET_TO_FIELD_TYPE = new HashMap<>();
private static final Map<Integer, Integer> DEFAULT_LENGTH = new HashMap<>();
static {
// 文本输入框 → varchar
WIDGET_TO_FIELD_TYPE.put(1, "0");
DEFAULT_LENGTH.put(1, 255);
// 数字输入框 → int
WIDGET_TO_FIELD_TYPE.put(3, "1");
// 开关组件 → int
WIDGET_TO_FIELD_TYPE.put(5, "1");
// 滑块组件 → decimal
WIDGET_TO_FIELD_TYPE.put(6, "4");
// 单选组件 → varchar
WIDGET_TO_FIELD_TYPE.put(7, "0");
DEFAULT_LENGTH.put(7, 255);
// 复选框 → varchar
WIDGET_TO_FIELD_TYPE.put(8, "0");
DEFAULT_LENGTH.put(8, 255);
// 下拉选择框 → varchar
WIDGET_TO_FIELD_TYPE.put(10, "0");
DEFAULT_LENGTH.put(10, 255);
// 级联选择框 → varchar
WIDGET_TO_FIELD_TYPE.put(12, "0");
DEFAULT_LENGTH.put(12, 255);
// 树形选择 → varchar
WIDGET_TO_FIELD_TYPE.put(13, "0");
DEFAULT_LENGTH.put(13, 255);
// 评分组件 → int
WIDGET_TO_FIELD_TYPE.put(14, "1");
DEFAULT_LENGTH.put(14, 5);
// 日期选择框 → datetime
WIDGET_TO_FIELD_TYPE.put(20, "3");
// 颜色选择 → varchar
WIDGET_TO_FIELD_TYPE.put(30, "0");
DEFAULT_LENGTH.put(30, 50);
// 附件上传 → varchar
WIDGET_TO_FIELD_TYPE.put(31, "0");
DEFAULT_LENGTH.put(31, 255);
// 图片上传 → varchar
WIDGET_TO_FIELD_TYPE.put(653, "0");
DEFAULT_LENGTH.put(653, 255);
// 富文本编辑 → varchar(2000)
WIDGET_TO_FIELD_TYPE.put(32, "0");
DEFAULT_LENGTH.put(32, 2000);
// MD编辑器 → varchar(2000)
WIDGET_TO_FIELD_TYPE.put(760, "0");
DEFAULT_LENGTH.put(760, 2000);
// 用户选择 → bigint
WIDGET_TO_FIELD_TYPE.put(400, "2");
// 部门选择 → bigint
WIDGET_TO_FIELD_TYPE.put(401, "2");
// 关联选择 → bigint
WIDGET_TO_FIELD_TYPE.put(402, "2");
// 时间选择器 → datetime
WIDGET_TO_FIELD_TYPE.put(652, "3");
// 文件上传 → varchar
WIDGET_TO_FIELD_TYPE.put(750, "0");
DEFAULT_LENGTH.put(750, 255);
}
public static String getFieldTypeId(Integer widgetType) {
String type = WIDGET_TO_FIELD_TYPE.get(widgetType);
if (type == null) {
throw new IllegalArgumentException("不支持的控件类型: " + widgetType);
}
return type;
}
public static Integer getDefaultLength(Integer widgetType) {
return DEFAULT_LENGTH.get(widgetType);
}
public static boolean isDataWidget(Integer widgetType) {
return WIDGET_TO_FIELD_TYPE.containsKey(widgetType);
}
}

93
common/common-online/src/main/java/apelet/common/online/util/WidgetJsonButtonParser.java

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
package apelet.common.online.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* widgetJson 按钮解析工具类
* 从在线表单的 widgetJson 中提取所有 operationList 下的按钮
*
* @author chenchuchuan
* @date 2026-07-17
*/
@Slf4j
public class WidgetJsonButtonParser {
/**
* 按钮信息DTO内部类轻量
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class ButtonInfo {
/** 按钮名称 */
private String name;
/** 是否在设计器中开启 */
private Boolean enabled;
}
/**
* widgetJson 中提取所有开启的按钮去重按name
*
* @param widgetJson 在线表单的 widget_json 字段值
* @return 按钮列表只包含 enabled=true 的按钮 name 去重
*/
public static List<ButtonInfo> extractEnabledButtons(String widgetJson) {
List<ButtonInfo> allButtons = extractAllButtons(widgetJson);
List<ButtonInfo> result = new ArrayList<>();
Set<String> seen = new HashSet<>();
for (ButtonInfo btn : allButtons) {
if (Boolean.TRUE.equals(btn.getEnabled()) && seen.add(btn.getName())) {
result.add(btn);
}
}
return result;
}
/**
* widgetJson 中提取所有按钮不去重不过滤
* 仅提取 pc.operationList mobile.operationList 两个顶层数组
* 不递归 tableWidgetwidgetListchildWidgetList 等子节点下的 operationList
*
* @param widgetJson 在线表单的 widget_json 字段值
* @return 全部按钮列表
*/
public static List<ButtonInfo> extractAllButtons(String widgetJson) {
List<ButtonInfo> result = new ArrayList<>();
if (widgetJson == null || widgetJson.isEmpty()) {
return result;
}
try {
JSONObject root = JSON.parseObject(widgetJson);
// 仅取 pc 和 mobile 两端的顶层 operationList
for (String device : new String[]{"pc", "mobile"}) {
JSONObject deviceJson = root.getJSONObject(device);
if (deviceJson == null) continue;
JSONArray operationList = deviceJson.getJSONArray("operationList");
if (operationList != null) {
for (int i = 0; i < operationList.size(); i++) {
JSONObject btn = operationList.getJSONObject(i);
String name = btn.getString("name");
Boolean enabled = btn.getBoolean("enabled");
if (name != null) {
result.add(new ButtonInfo(name, enabled != null ? enabled : false));
}
}
}
}
} catch (Exception e) {
log.error("解析 widgetJson 失败", e);
}
return result;
}
}

19
common/common-online/src/main/java/apelet/common/online/vo/PermissionDataRuleSchemaVo.java

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
package apelet.common.online.vo;
import apelet.common.online.model.PermissionDataRuleSchema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* 权限数据规则方案表 VO
*
* @author chenchuchuan
* @date 2026-07-15
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class PermissionDataRuleSchemaVo extends PermissionDataRuleSchema implements Serializable {
private static final long serialVersionUID = 1L;
}

17
common/common-online/src/main/java/apelet/common/online/vo/RoleFormButtonVo.java

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
package apelet.common.online.vo;
import apelet.common.online.model.RoleFormButton;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 角色-表单按钮权限配置VO对象
*
* @author chenchuchuan
* @date 2026-07-17
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RoleFormButtonVo extends RoleFormButton {
private static final long serialVersionUID = 1L;
}

3
common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java

@ -8,6 +8,7 @@ import apelet.common.online.controller.OnlinePageController; @@ -8,6 +8,7 @@ import apelet.common.online.controller.OnlinePageController;
import apelet.common.online.dto.OnlineDatasourceDto;
import apelet.common.online.dto.OnlinePageDatasourceDto;
import apelet.common.online.dto.OnlinePageDto;
import apelet.common.online.model.OnlinePage;
import apelet.common.online.vo.OnlineDatasourceVo;
import apelet.common.online.vo.OnlinePageDatasourceVo;
import apelet.common.online.vo.OnlinePageVo;
@ -43,7 +44,7 @@ public class TenantOnlinePageController extends OnlinePageController { @@ -43,7 +44,7 @@ public class TenantOnlinePageController extends OnlinePageController {
@OperationLog(type = SysOperationLogType.ADD)
@PostMapping("/add")
@Override
public ResponseResult<Long> add(@MyRequestBody OnlinePageDto onlinePageDto) {
public ResponseResult<OnlinePage> add(@MyRequestBody OnlinePageDto onlinePageDto) {
return super.add(onlinePageDto);
}

Loading…
Cancel
Save