Parcourir la source

新增 租户、租户套餐、租户与套餐绑定
新增 在线用户会话查询
新增 用户角色菜单与租户菜单集合重合查询

18339543638 il y a 2 ans
Parent
commit
7e49b498b8
80 fichiers modifiés avec 2055 ajouts et 272 suppressions
  1. 9 3
      tr-framework/src/main/java/cn/tr/core/tree/TreeNode.java
  2. 3 1
      tr-framework/src/main/java/cn/tr/core/utils/TreeUtil.java
  3. 5 0
      tr-modules/tr-module-system/pom.xml
  4. 1 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/dict/controller/SysDictItemController.java
  5. 3 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/dict/dto/SysDictDTO.java
  6. 2 2
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/dict/dto/SysDictItemDTO.java
  7. 3 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/dict/service/ISysDictItemService.java
  8. 2 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/file/package-info.java
  9. 4 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/log/dto/SysOperLogDTO.java
  10. 28 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/mapper/tenant/SysTenantMapper.java
  11. 24 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/mapper/tenant/SysTenantPackageMapper.java
  12. 26 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/mapper/tenant/SysTenantPackageMenuMapper.java
  13. 6 8
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/controller/CurrentUserController.java
  14. 43 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/operator/OAuth2UserOperator.java
  15. 30 8
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/psw/operator/LoginOAuth2PswUserOperator.java
  16. 71 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/service/CurrentUserService.java
  17. 80 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/controller/SysTenantController.java
  18. 91 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/controller/SysTenantPackageController.java
  19. 50 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantAddDTO.java
  20. 50 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantCommonDTO.java
  21. 49 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantPackageDTO.java
  22. 21 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantPackageMenuDTO.java
  23. 22 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantPackageQueryDTO.java
  24. 24 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantQueryDTO.java
  25. 2 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/package-info.java
  26. 51 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/po/SysTenantPO.java
  27. 25 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/po/SysTenantPackageMenuPO.java
  28. 40 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/po/SysTenantPackagePO.java
  29. 18 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/repository/SysTenantPackageMenuRepository.java
  30. 18 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/repository/SysTenantPackageRepository.java
  31. 18 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/repository/SysTenantRepository.java
  32. 42 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/ISysTenantPackageMenuService.java
  33. 53 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/ISysTenantPackageService.java
  34. 84 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/ISysTenantService.java
  35. 82 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/impl/SysTenantPackageMenuServiceImpl.java
  36. 98 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/impl/SysTenantPackageServiceImpl.java
  37. 156 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/impl/SysTenantServiceImpl.java
  38. 8 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/controller/SysMenuController.java
  39. 6 6
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/controller/SysOrgController.java
  40. 27 9
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/controller/SysRoleController.java
  41. 80 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/controller/SysUserOnlineController.java
  42. 29 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/OnlineUserOperationDTO.java
  43. 24 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/OnlineUserQueryDTO.java
  44. 49 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/OnlineUserSessionDTO.java
  45. 37 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/OnlineUserTokenSessionDTO.java
  46. 13 14
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysMenuDTO.java
  47. 2 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysMenuQueryDTO.java
  48. 13 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysOrgDTO.java
  49. 4 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysPositionDTO.java
  50. 12 8
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysRoleDTO.java
  51. 13 2
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysUserDTO.java
  52. 15 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/enums/RoleEnum.java
  53. 6 11
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/po/SysRolePO.java
  54. 4 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/po/SysUserPO.java
  55. 46 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysMenuService.java
  56. 3 2
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysRoleMenuService.java
  57. 3 6
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysRoleService.java
  58. 7 0
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysUserRoleService.java
  59. 50 10
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysMenuServiceImpl.java
  60. 16 13
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysRoleMenuServiceImpl.java
  61. 36 66
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysRoleServiceImpl.java
  62. 6 1
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysUserRoleServiceImpl.java
  63. 48 10
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysUserServiceImpl.java
  64. 0 5
      tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/vo/RouteItemVO.java
  65. 2 2
      tr-modules/tr-module-system/src/main/resources/mapper/user/SysMenuMapper.xml
  66. 2 2
      tr-modules/tr-module-system/src/main/resources/mapper/user/SysRoleMapper.xml
  67. 7 0
      tr-plugins/tr-spring-boot-starter-plugin-biz-tenant/src/main/java/cn/tr/plugin/biz/tenant/TrTenantAutoConfiguration.java
  68. 16 0
      tr-plugins/tr-spring-boot-starter-plugin-biz-tenant/src/main/java/cn/tr/plugin/biz/tenant/config/aop/CacheIgnoreTenant.java
  69. 41 0
      tr-plugins/tr-spring-boot-starter-plugin-biz-tenant/src/main/java/cn/tr/plugin/biz/tenant/config/aop/TenantCacheAspect.java
  70. 30 0
      tr-plugins/tr-spring-boot-starter-plugin-mybatis/src/main/java/cn/tr/plugin/mybatis/pojo/BaseDTO.java
  71. 2 2
      tr-plugins/tr-spring-boot-starter-plugin-mybatis/src/main/java/cn/tr/plugin/mybatis/pojo/BasePO.java
  72. 4 2
      tr-plugins/tr-spring-boot-starter-plugin-mybatis/src/main/java/cn/tr/plugin/mybatis/pojo/TenantPO.java
  73. 0 65
      tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/bo/LoginUserBO.java
  74. 35 0
      tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/bo/UserLoginInfoBO.java
  75. 2 0
      tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/constant/SecurityConstant.java
  76. 0 1
      tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/context/LoginUserContextHolder.java
  77. 5 1
      tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/utils/SaTokenUtils.java
  78. 3 2
      tr-plugins/tr-spring-boot-starter-plugin-web/src/main/java/cn/tr/plugin/web/TrWebAutoConfiguration.java
  79. 6 1
      tr-plugins/tr-spring-boot-starter-plugin-web/src/main/java/cn/tr/plugin/web/config/TrWebMvcConfig.java
  80. 9 2
      tr-test/src/main/resources/application.yml

+ 9 - 3
tr-framework/src/main/java/cn/tr/core/tree/TreeNode.java

@@ -1,7 +1,13 @@
 package cn.tr.core.tree;
 
+import cn.tr.core.validation.Insert;
+import cn.tr.core.validation.Update;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -11,18 +17,20 @@ import java.util.List;
  * @author ruoyi
  */
 @Data
-public abstract class TreeNode<PK>
+public abstract class TreeNode<PK> implements Serializable
 {
     private static final long serialVersionUID = 1L;
 
     /**
      * 父节点
      */
+    @NotNull(message = "父级id不能为空",groups = {Update.class, Insert.class})
     private PK parentId;
 
     /**
      * 顺序
      */
+    @NotNull(message = "排序不能为空",groups = {Update.class, Insert.class})
     private Integer sort;
 
     /**
@@ -35,9 +43,7 @@ public abstract class TreeNode<PK>
      */
     private String nodePath;
 
-    @JsonIgnore
     public abstract PK getId();
 
-    @JsonIgnore
     public abstract String getName();
 }

+ 3 - 1
tr-framework/src/main/java/cn/tr/core/utils/TreeUtil.java

@@ -64,7 +64,9 @@ public class TreeUtil {
                     .collect(Collectors.toList());
         }
         //找到顶级节点
-        sources.stream().filter(source-> ObjectUtil.isNull(source.getSort())).forEach(source->source.setSort(999));
+        sources.stream()
+                .peek(node->node.setParentId(ObjectUtil.isEmpty(node.getParentId())?"0":node.getParentId()))
+                .filter(source-> ObjectUtil.isNull(source.getSort())).forEach(source->source.setSort(999));
         List<Node> result = sources.stream()
                 .filter(node -> StrUtil.equals("0",String.valueOf(node.getParentId())))
                 .collect(Collectors.toList());

+ 5 - 0
tr-modules/tr-module-system/pom.xml

@@ -18,6 +18,11 @@
             <artifactId>tr-spring-boot-starter-plugin-dict</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>cn.tr</groupId>
+            <artifactId>tr-spring-boot-starter-plugin-file</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>cn.tr</groupId>
             <artifactId>tr-spring-boot-starter-plugin-biz-constant</artifactId>

+ 1 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/dict/controller/SysDictItemController.java

@@ -40,7 +40,7 @@ public class SysDictItemController extends BaseController {
     @ApiOperation(value = "查询相应字典下所有字典项",notes = "无权限")
     public CommonResult<List<SysDictItemSmallDTO>> selectList(@RequestBody SysDictItemQueryDTO query){
         if(StrUtil.isEmpty(query.getDictCode())){
-            SysDictDTO dict = dictService.selectDictById(query.getDictCode());
+            SysDictDTO dict = dictService.selectDictById(query.getDictId());
             if(dict==null){
                 return CommonResult.success();
             }

+ 3 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/dict/dto/SysDictDTO.java

@@ -2,6 +2,7 @@ package cn.tr.module.sys.dict.dto;
 
 import cn.tr.core.validation.Insert;
 import cn.tr.core.validation.Update;
+import cn.tr.plugin.mybatis.pojo.BaseDTO;
 import cn.tr.plugin.mybatis.pojo.BasePO;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -19,7 +20,8 @@ import java.io.Serializable;
  */
 @Data
 @ToString
-public class SysDictDTO implements Serializable {
+@EqualsAndHashCode(callSuper = true)
+public class SysDictDTO extends BaseDTO {
     private static final long serialVersionUID = 1L;
 
     @ApiModelProperty(value = "主键")

+ 2 - 2
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/dict/dto/SysDictItemDTO.java

@@ -2,7 +2,7 @@ package cn.tr.module.sys.dict.dto;
 
 import cn.tr.core.validation.Insert;
 import cn.tr.core.validation.Update;
-import cn.tr.plugin.mybatis.pojo.BasePO;
+import cn.tr.plugin.mybatis.pojo.BaseDTO;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -22,7 +22,7 @@ import javax.validation.constraints.NotNull;
 @ApiModel("字典项传输对象")
 @ToString
 @EqualsAndHashCode(callSuper = true)
-public class SysDictItemDTO extends BasePO {
+public class SysDictItemDTO extends BaseDTO {
     private static final long serialVersionUID = 1L;
 
     @ApiModelProperty(value = "主键")

+ 3 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/dict/service/ISysDictItemService.java

@@ -3,6 +3,7 @@ package cn.tr.module.sys.dict.service;
 import cn.tr.module.sys.dict.dto.SysDictItemDTO;
 import cn.tr.module.sys.dict.dto.SysDictItemQueryDTO;
 import cn.tr.module.sys.dict.dto.SysDictItemSmallDTO;
+import cn.tr.plugin.biz.tenant.config.aop.CacheIgnoreTenant;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 
@@ -58,10 +59,12 @@ public interface ISysDictItemService {
      * @return 字典项列表
      */
     @Cacheable(value = "sys:dict",key = "#dictCode")
+    @CacheIgnoreTenant
     List<SysDictItemSmallDTO> selectDictItemsByDictCode(String dictCode);
 
 
     @CacheEvict(value = "sys:dict",key = "#dictCode")
+    @CacheIgnoreTenant
     default void delCache(String dictCode){
 
     };

+ 2 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/file/package-info.java

@@ -0,0 +1,2 @@
+package cn.tr.module.sys.file;
+//文件相关包

+ 4 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/log/dto/SysOperLogDTO.java

@@ -1,8 +1,10 @@
 package cn.tr.module.sys.log.dto;
 
 
+import cn.tr.plugin.mybatis.pojo.BaseDTO;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import lombok.ToString;
 
 import java.io.Serializable;
@@ -16,7 +18,8 @@ import java.util.Date;
  */
 @Data
 @ToString
-public class SysOperLogDTO implements Serializable {
+@EqualsAndHashCode(callSuper = true)
+public class SysOperLogDTO extends BaseDTO {
     private static final long serialVersionUID = 1L;
 
     @ApiModelProperty(value = "主键")

+ 28 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/mapper/tenant/SysTenantMapper.java

@@ -0,0 +1,28 @@
+package cn.tr.module.sys.mapper.tenant;
+
+import cn.tr.module.sys.tenant.dto.SysTenantAddDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantCommonDTO;
+import cn.tr.module.sys.tenant.po.SysTenantPO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * @ClassName : SysTenantMapper
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Mapper
+public interface SysTenantMapper {
+    SysTenantMapper INSTANCE = Mappers.getMapper(SysTenantMapper.class);
+
+    List<SysTenantCommonDTO> toCommonDTOList(List<SysTenantPO> source);
+
+    SysTenantCommonDTO toCommonDTO(SysTenantPO source);
+
+    SysTenantPO toPO(SysTenantCommonDTO source);
+
+    SysTenantPO toPO(SysTenantAddDTO source);
+}

+ 24 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/mapper/tenant/SysTenantPackageMapper.java

@@ -0,0 +1,24 @@
+package cn.tr.module.sys.mapper.tenant;
+
+import cn.tr.module.sys.tenant.dto.SysTenantPackageDTO;
+import cn.tr.module.sys.tenant.po.SysTenantPackagePO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * @ClassName : SysTenantMapper
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Mapper
+public interface SysTenantPackageMapper {
+    SysTenantPackageMapper INSTANCE = Mappers.getMapper(SysTenantPackageMapper.class);
+
+    List<SysTenantPackageDTO> toDTOList(List<SysTenantPackagePO> source);
+
+    SysTenantPackageDTO toDTO(SysTenantPackagePO source);
+
+    SysTenantPackagePO toPO(SysTenantPackageDTO source);}

+ 26 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/mapper/tenant/SysTenantPackageMenuMapper.java

@@ -0,0 +1,26 @@
+package cn.tr.module.sys.mapper.tenant;
+
+import cn.tr.module.sys.tenant.dto.SysTenantPackageMenuDTO;
+import cn.tr.module.sys.tenant.po.SysTenantPackageMenuPO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * @ClassName : SysTenantMapper
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Mapper
+public interface SysTenantPackageMenuMapper {
+    SysTenantPackageMenuMapper INSTANCE = Mappers.getMapper(SysTenantPackageMenuMapper.class);
+
+    List<SysTenantPackageMenuDTO> toDTOList(List<SysTenantPackageMenuPO> source);
+
+    List<SysTenantPackageMenuPO> toPOList(List<SysTenantPackageMenuDTO> source);
+
+    SysTenantPackageMenuDTO toDTO(SysTenantPackageMenuPO source);
+
+    SysTenantPackageMenuPO toPO(SysTenantPackageMenuDTO source);}

+ 6 - 8
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/controller/CurrentUserController.java

@@ -1,14 +1,12 @@
 package cn.tr.module.sys.oauth2.controller;
 
-import cn.hutool.core.util.StrUtil;
 import cn.tr.core.exception.ServiceException;
 import cn.tr.core.exception.TRExcCode;
 import cn.tr.core.pojo.CommonResult;
 import cn.tr.core.utils.TreeUtil;
 import cn.tr.module.sys.oauth2.psw.operator.AbstractOAuth2PswUserOperator;
 import cn.tr.module.sys.oauth2.psw.operator.OAuth2PswUserOperatorManager;
-import cn.tr.module.sys.user.dto.SysMenuDTO;
-import cn.tr.module.sys.user.enums.MenuEnum;
+import cn.tr.module.sys.oauth2.service.CurrentUserService;
 import cn.tr.module.sys.user.service.ISysRoleService;
 import cn.tr.module.sys.user.vo.RouteItemVO;
 import cn.tr.plugin.security.context.LoginUserContextHolder;
@@ -20,10 +18,9 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.Collection;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * @ClassName : CurrentUserController
@@ -38,6 +35,7 @@ import java.util.stream.Collectors;
 public class CurrentUserController {
     private final OAuth2PswUserOperatorManager pswUserOperatorManager;
     private final ISysRoleService roleService;
+    private final CurrentUserService currentUserService;
     @ApiOperation("当前用户的登陆信息")
     @GetMapping("/loginInfo")
     public CommonResult accountInfo(){
@@ -51,14 +49,14 @@ public class CurrentUserController {
 
     @GetMapping("/getPermissionInfo")
     @ApiOperation( "获取登录用户的权限信息")
-    public CommonResult<Set<String>> getPermissionInfo() {
-        return CommonResult.success( roleService.findUserPermission(String.valueOf(SaTokenUtils.getStpUtil().getLoginId())));
+    public CommonResult<Collection<String>> getPermissionInfo() {
+        return CommonResult.success(currentUserService.currentUserPermission());
     }
 
     @GetMapping("/listMenus")
     @ApiOperation("获得登录用户的菜单列表")
     public CommonResult<List<RouteItemVO>> getMenus() {
-        return CommonResult.success(TreeUtil.buildTree( roleService.findUserRouteMenus(String.valueOf(SaTokenUtils.getStpUtil().getLoginId()))));
+        return CommonResult.success(currentUserService.currentUserMenus());
     }
 
 }

+ 43 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/operator/OAuth2UserOperator.java

@@ -15,9 +15,21 @@
  */
 package cn.tr.module.sys.oauth2.operator;
 
+import cn.dev33.satoken.session.SaSession;
+import cn.dev33.satoken.session.TokenSign;
+import cn.hutool.http.HttpUtil;
 import cn.tr.module.sys.oauth2.LoginTypeMatcher;
 import cn.tr.module.sys.oauth2.dto.OAuth2PswReqDTO;
 import cn.tr.module.sys.oauth2.enums.OAuth2ModelEnum;
+import cn.tr.module.sys.user.dto.OnlineUserSessionDTO;
+import cn.tr.module.sys.user.dto.OnlineUserTokenSessionDTO;
+import cn.tr.plugin.security.bo.UserLoginInfoBO;
+import cn.tr.plugin.security.constant.SecurityConstant;
+import cn.tr.plugin.security.utils.SaTokenUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
 
 /**
  * oauth2的用户接口操作接口
@@ -52,4 +64,35 @@ public interface OAuth2UserOperator extends LoginTypeMatcher {
 	 * @return
 	 */
 	Object getUserLoginInfo();
+
+	default void setSessionUser(String tokenValue, UserLoginInfoBO loginInfo){
+		SaTokenUtils.set(SecurityConstant.LOGIN_USER,loginInfo);
+		OnlineUserSessionDTO userSession = SaTokenUtils.getValue(SecurityConstant.SESSION_USER, OnlineUserSessionDTO.class);
+		if(userSession==null){
+			userSession=new OnlineUserSessionDTO();
+		}
+		SaSession session = SaTokenUtils.getStpUtil().getSession();
+		List<TokenSign> tokenSignList = session.getTokenSignList();
+		String sessionId = SaTokenUtils.getStpUtil().getSession().getId();
+
+		OnlineUserTokenSessionDTO tokenSessionUser = new OnlineUserTokenSessionDTO();
+		tokenSessionUser.setTokenValue(tokenValue);
+		tokenSessionUser.setBrowser(loginInfo.getBrowser());
+		tokenSessionUser.setOs(loginInfo.getOs());
+		tokenSessionUser.setLoginIp(loginInfo.getLoginIp());
+		tokenSessionUser.setLoginLocation(loginInfo.getLoginLocation());
+		tokenSessionUser.setLoginTime(loginInfo.getLoginTime());
+
+		userSession.setSessionId(sessionId);
+		userSession.setUsername(loginInfo.getUsername());
+		userSession.setNickname(loginInfo.getNickname());
+		userSession.setAvatar(loginInfo.getAvatar());
+		userSession.setLastLoginLocation(loginInfo.getLoginLocation());
+		userSession.setLastLoginIp(loginInfo.getLoginIp());
+		userSession.setLastLoginTime(loginInfo.getLoginTime());
+		List<OnlineUserTokenSessionDTO> tokenSessions = Optional.ofNullable(userSession.getTokenSessionList()).orElse(new ArrayList<>());
+		tokenSessions.add(0,tokenSessionUser);
+		userSession.setTokenSessionList(tokenSessions);
+		SaTokenUtils.set(SecurityConstant.SESSION_USER,userSession);
+	}
 }

+ 30 - 8
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/psw/operator/LoginOAuth2PswUserOperator.java

@@ -2,7 +2,11 @@ package cn.tr.module.sys.oauth2.psw.operator;
 
 import cn.dev33.satoken.stp.StpLogic;
 import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import cn.tr.core.annotation.TenantIgnore;
 import cn.tr.core.exception.ServiceException;
 import cn.tr.core.exception.TRExcCode;
 import cn.tr.core.strategy.LoginUserStrategy;
@@ -14,15 +18,15 @@ import cn.tr.module.sys.oauth2.dto.OAuth2PswReqDTO;
 import cn.tr.module.sys.user.dto.SysUserDTO;
 import cn.tr.module.sys.user.enums.UserStatusEnum;
 import cn.tr.module.sys.user.service.ISysUserService;
-import cn.tr.plugin.biz.tenant.context.TenantContextHolder;
+import cn.tr.plugin.security.bo.UserLoginInfoBO;
 import cn.tr.plugin.security.utils.SaTokenUtils;
-import lombok.AllArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
 import java.util.Date;
+import java.util.Optional;
 
 /**
  * @ClassName : LoginOauth2PswUserOperator
@@ -38,10 +42,10 @@ public class LoginOAuth2PswUserOperator extends AbstractOAuth2PswUserOperator{
     @Autowired
     private ISysUserService sysUserService;
     @Override
+    @TenantIgnore
     public String auth(OAuth2PswReqDTO source) {
         String username = source.getUsername();
         String psw = source.getPassword();
-        TenantContextHolder.setIgnore(true);
         //对账号进行校验
         SysUserDTO user = sysUserService.selectUserByUsername(username);
         if(user==null){
@@ -59,19 +63,37 @@ public class LoginOAuth2PswUserOperator extends AbstractOAuth2PswUserOperator{
         }
         StpLogic stpUtil = SaTokenUtils.getStpUtil();
         stpUtil.login(user.getUserId());
-
+        Date loginTime = new Date();
         //更新最后登录信息
-        String cityInfo = IpUtil.getCityInfo(ServletUtils.getClientIP());
+        String ip = ServletUtils.getClientIP();
+        String cityInfo = IpUtil.getCityInfo(ip);
         SysUserDTO updateUser = new SysUserDTO();
         updateUser.setUserId(user.getUserId());
-        updateUser.setLastLoginDate(new Date());
-        updateUser.setLastLoginIp(ServletUtils.getClientIP());
+        updateUser.setLastLoginDate(loginTime);
+        updateUser.setLastLoginIp(ip);
         updateUser.setLastLoginAddress(cityInfo);
         sysUserService.updateSysUserById(updateUser);
         //清除缓存
         self.delUserLoginInfoCache(user.getUserId());
 
-        return stpUtil.getTokenValue();
+        UserAgent userAgent = Optional.ofNullable(UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"))).orElse(new UserAgent());
+        String browser = ObjectUtil.isEmpty(userAgent.getBrowser()) ? "未知" : userAgent.getBrowser().getName();
+        String os = ObjectUtil.isEmpty(userAgent.getOs()) ? "未知" : userAgent.getOs().getName();
+        String tokenValue = stpUtil.getTokenValue();
+        UserLoginInfoBO loginInfo = UserLoginInfoBO.builder()
+                .userId(user.getUserId())
+                .username(username)
+                .tenantId(user.getTenantId())
+                .token(tokenValue)
+                .loginType(StpUtil.TYPE)
+                .loginIp(updateUser.getLastLoginIp())
+                .loginLocation(cityInfo)
+                .loginTime(loginTime)
+                .browser(browser)
+                .os(os)
+                .build();
+        setSessionUser(tokenValue,loginInfo);
+        return tokenValue;
     }
 
     @Override

+ 71 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/oauth2/service/CurrentUserService.java

@@ -0,0 +1,71 @@
+package cn.tr.module.sys.oauth2.service;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.core.strategy.LoginUserStrategy;
+import cn.tr.module.sys.tenant.service.ISysTenantService;
+import cn.tr.module.sys.user.dto.SysMenuDTO;
+import cn.tr.module.sys.user.enums.MenuEnum;
+import cn.tr.module.sys.user.service.ISysMenuService;
+import cn.tr.module.sys.user.service.ISysRoleService;
+import cn.tr.module.sys.user.vo.RouteItemVO;
+import cn.tr.plugin.security.context.LoginUserContextHolder;
+import cn.tr.plugin.security.utils.SaTokenUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : CurrentUserService
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月07日
+ */
+@AllArgsConstructor
+@Component
+public class CurrentUserService {
+    private final ISysRoleService roleService;
+    private final ISysTenantService tenantService;
+    private final ISysMenuService menuService;
+    /**
+     * 获取当前用户权限
+     * @return
+     */
+    public Collection<String> currentUserPermission(){
+        //用户所在租户拥有的权限
+        Set<String> tenantPermissions = tenantService.findTenantPermission();
+        ;
+        //用户角色所拥有的权限
+        Set<String> rolePermissions = roleService.findUserPermission(String.valueOf(SaTokenUtils.getStpUtil().getLoginId()));
+
+        return CollectionUtil.intersection(tenantPermissions,rolePermissions);
+    };
+
+    /**
+     * 获取当前用户菜单
+     * @return
+     */
+    public List<RouteItemVO> currentUserMenus(){
+        Set<SysMenuDTO> tenantMenus = tenantService.currentTenantMenus();
+        Set<SysMenuDTO> roleMenus = roleService.findUserMenus(LoginUserContextHolder.getUser().getUserId());
+        Collection<SysMenuDTO> allMenus = CollectionUtil.union(tenantMenus, roleMenus);
+        if(CollectionUtil.isEmpty(allMenus)){
+            return new ArrayList<>();
+        }
+        return allMenus
+                .stream()
+                .collect(Collectors.groupingBy(SysMenuDTO::getId, Collectors.collectingAndThen(Collectors.toList(), CollUtil::getFirst)))
+                //未重复的菜单
+                .values()
+                .stream()
+                .filter(menu -> !StrUtil.equals(MenuEnum.button.name(), menu.getMenuType()))
+                .map(menuService::convertToRoute)
+                .collect(Collectors.toList());
+    }
+}

+ 80 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/controller/SysTenantController.java

@@ -0,0 +1,80 @@
+package cn.tr.module.sys.tenant.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.tr.core.pojo.CommonResult;
+import cn.tr.core.pojo.TableDataInfo;
+import cn.tr.core.validation.Insert;
+import cn.tr.core.validation.Update;
+import cn.tr.module.sys.tenant.dto.SysTenantAddDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantCommonDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantQueryDTO;
+import cn.tr.module.sys.tenant.service.ISysTenantService;
+import cn.tr.plugin.mybatis.base.BaseController;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Collection;
+
+/**
+ * @ClassName : SysTenantController
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月24日
+ */
+@RestController
+@RequestMapping("/sys/tenant")
+@Api(tags = "租户")
+@AllArgsConstructor
+public class SysTenantController extends BaseController {
+    private final ISysTenantService orgService;
+
+
+    @PostMapping("/query/page")
+    @ApiOperationSupport(author = "lf")
+    @ApiOperation(value = "根据条件查询租户",notes = "权限: sys:tenant:query")
+    @SaCheckPermission("sys:tenant:query")
+    public TableDataInfo<SysTenantCommonDTO> selectList(@RequestBody SysTenantQueryDTO query){
+        startPage();
+        return getDataTable(orgService.selectSysTenantList(query));
+    }
+
+    @GetMapping("/detail/{id}")
+    @ApiOperationSupport(author = "lf")
+    @ApiOperation(value = "根据id查询租户",notes = "权限: sys:tenant:query")
+    @SaCheckPermission("sys:tenant:query")
+    public CommonResult<SysTenantCommonDTO> findById(@PathVariable("id") String id){
+        return CommonResult.success(orgService.selectSysTenantById(id));
+    }
+
+    @PostMapping("/edit")
+    @ApiOperationSupport(author = "lf")
+    @SaCheckPermission("sys:tenant:edit")
+    @ApiOperation(value = "根据id更新租户",notes = "权限: sys:tenant:edit")
+    public CommonResult<Boolean> edit(@RequestBody@Validated(Update.class) SysTenantCommonDTO source){
+        return CommonResult.success(orgService.updateSysTenantById(source));
+    }
+
+    @PostMapping("/add")
+    @ApiOperationSupport(author = "lf")
+    @SaCheckPermission("sys:tenant:add")
+    @ApiOperation(value = "新增租户",notes = "权限: sys:tenant:add")
+    public CommonResult<Boolean> add(@RequestBody@Validated(Insert.class) SysTenantAddDTO source){
+        return CommonResult.success(orgService.insertSysTenant(source));
+    }
+
+    @PostMapping("/deleteByIds")
+    @ApiOperationSupport(author = "lf")
+    @ApiOperation(value = "删除租户",notes = "权限: sys:tenant:del")
+    @SaCheckPermission("sys:tenant:del")
+    public CommonResult<Integer> deleteByIds(@RequestBody Collection<String> ids){
+        return CommonResult.success(orgService.deleteSysTenantByIds(ids));
+    }
+    
+
+    //todo 导出
+
+}

+ 91 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/controller/SysTenantPackageController.java

@@ -0,0 +1,91 @@
+package cn.tr.module.sys.tenant.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.tr.core.pojo.CommonResult;
+import cn.tr.core.pojo.TableDataInfo;
+import cn.tr.core.validation.Insert;
+import cn.tr.core.validation.Update;
+import cn.tr.module.sys.tenant.dto.SysTenantAddDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantCommonDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantPackageMenuDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantQueryDTO;
+import cn.tr.module.sys.tenant.service.ISysTenantPackageMenuService;
+import cn.tr.module.sys.tenant.service.ISysTenantService;
+import cn.tr.plugin.mybatis.base.BaseController;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @ClassName : SysTenantController
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月24日
+ */
+@RestController
+@RequestMapping("/sys/tenantPackage")
+@Api(tags = "租户套餐")
+@AllArgsConstructor
+public class SysTenantPackageController extends BaseController {
+    private final ISysTenantService tenantService;
+    private final ISysTenantPackageMenuService tenantPackageMenuService;
+    @PostMapping("/assign")
+    @ApiOperationSupport(author = "lf")
+    @ApiOperation(value = "为套餐分配菜单",notes = "权限: sys:tenantPackage:assign")
+    @SaCheckPermission("sys:tenantPackage:assign")
+    public CommonResult<Boolean> selectList(@RequestBody@Validated List<SysTenantPackageMenuDTO> source){
+        tenantPackageMenuService.assignPackageMenu(source);
+        return CommonResult.success(true);
+    }
+
+    @PostMapping("/query/page")
+    @ApiOperationSupport(author = "lf")
+    @ApiOperation(value = "根据条件查询租户套餐",notes = "权限: sys:tenantPackage:query")
+    @SaCheckPermission("sys:tenantPackage:query")
+    public TableDataInfo<SysTenantCommonDTO> selectList(@RequestBody SysTenantQueryDTO query){
+        startPage();
+        return getDataTable(tenantService.selectSysTenantList(query));
+    }
+
+    @GetMapping("/detail/{id}")
+    @ApiOperationSupport(author = "lf")
+    @ApiOperation(value = "根据id查询租户套餐",notes = "权限: sys:tenantPackage:query")
+    @SaCheckPermission("sys:tenantPackage:query")
+    public CommonResult<SysTenantCommonDTO> findById(@PathVariable("id") String id){
+        return CommonResult.success(tenantService.selectSysTenantById(id));
+    }
+
+    @PostMapping("/edit")
+    @ApiOperationSupport(author = "lf")
+    @SaCheckPermission("sys:tenantPackage:edit")
+    @ApiOperation(value = "根据id更新租户套餐",notes = "权限: sys:tenantPackage:edit")
+    public CommonResult<Boolean> edit(@RequestBody@Validated(Update.class) SysTenantCommonDTO source){
+        return CommonResult.success(tenantService.updateSysTenantById(source));
+    }
+
+    @PostMapping("/add")
+    @ApiOperationSupport(author = "lf")
+    @SaCheckPermission("sys:tenantPackage:add")
+    @ApiOperation(value = "新增租户套餐",notes = "权限: sys:tenantPackage:add")
+    public CommonResult<Boolean> add(@RequestBody@Validated(Insert.class) SysTenantAddDTO source){
+        return CommonResult.success(tenantService.insertSysTenant(source));
+    }
+
+    @PostMapping("/deleteByIds")
+    @ApiOperationSupport(author = "lf")
+    @ApiOperation(value = "删除租户套餐",notes = "权限: sys:tenantPackage:del")
+    @SaCheckPermission("sys:tenantPackage:del")
+    public CommonResult<Integer> deleteByIds(@RequestBody Collection<String> ids){
+        return CommonResult.success(tenantService.deleteSysTenantByIds(ids));
+    }
+    
+
+    //todo 导出
+
+}

+ 50 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantAddDTO.java

@@ -0,0 +1,50 @@
+package cn.tr.module.sys.tenant.dto;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import java.io.Serializable;
+
+/**
+ * @ClassName : SysTenantPO
+ * @Description : 租户新增对象
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Data
+@ApiModel("租户新增对象")
+public class SysTenantAddDTO implements Serializable {
+    private static final long serialVersionUID = 1841053739406204228L;
+    @ApiModelProperty(value = "租户名称",required = true)
+    @NotEmpty(message = "extends不能为空")
+    private String name;
+
+    @ApiModelProperty(value = "租户套餐id",required = true)
+    @NotEmpty(message = "租户套餐不能为空")
+    private String packageId;
+
+    @ApiModelProperty(value = "租户联系人",required = true)
+    @NotEmpty(message = "租户联系人不能为空")
+    private String contractUser;
+
+    @ApiModelProperty(value = "用户名称",required = true)
+    @NotEmpty(message = "用户名称不能为空")
+    private String username;
+
+    @ApiModelProperty(value = "用户密码",required = true)
+    @NotEmpty(message = "用户密码不能为空")
+    private String password;
+
+    @ApiModelProperty("租户联系人电话")
+    private String contactMobile;
+
+    @ApiModelProperty("租户备注")
+    private String remark;
+
+    @ApiModelProperty(value = "租户状态 0、开启 1、关闭",required = true)
+    @NotEmpty(message = "租户状态不能为空")
+    private Boolean disable;
+}

+ 50 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantCommonDTO.java

@@ -0,0 +1,50 @@
+package cn.tr.module.sys.tenant.dto;
+
+
+import cn.tr.core.validation.Update;
+import cn.tr.plugin.mybatis.pojo.BaseDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotEmpty;
+import java.io.Serializable;
+
+/**
+ * @ClassName : SysTenantQueryDTO
+ * @Description : 租户通用对象
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("租户查询对象")
+public class SysTenantCommonDTO extends BaseDTO {
+    private static final long serialVersionUID = 1841053739406204228L;
+    @ApiModelProperty(value = "租户id",required = true)
+    @NotEmpty(message = "租户id不能为空",groups = {Update.class})
+    private String id;
+
+    @ApiModelProperty(value = "租户名称",required = true)
+    @NotEmpty(message = "租户名称不能为空",groups = {Update.class})
+    private String name;
+
+    @ApiModelProperty(value = "租户套餐id",required = true)
+    @NotEmpty(message = "租户套餐不能为空",groups = {Update.class})
+    private String packageId;
+
+    @ApiModelProperty(value = "租户联系人",required = true)
+    @NotEmpty(message = "租户联系人不能为空",groups = {Update.class})
+    private String contractUser;
+
+    @ApiModelProperty("租户联系人电话")
+    private String contactMobile;
+
+    @ApiModelProperty("租户备注")
+    private String remark;
+
+    @ApiModelProperty(value = "租户状态 0、开启 1、关闭",required = true)
+    @NotEmpty(message = "租户状态不能为空",groups = {Update.class})
+    private Boolean disable;
+}

+ 49 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantPackageDTO.java

@@ -0,0 +1,49 @@
+package cn.tr.module.sys.tenant.dto;
+
+
+import cn.tr.core.validation.Insert;
+import cn.tr.core.validation.Update;
+import cn.tr.plugin.mybatis.pojo.BaseDTO;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotEmpty;
+import java.io.Serializable;
+
+/**
+ * @ClassName : SysTenantPackageDTO
+ * @Description : 租户套餐
+ * @Author : LF
+ * @Date: 2023年04月03日
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysTenantPackageDTO extends BaseDTO {
+    private static final long serialVersionUID = 4662146956169137606L;
+    @ApiModelProperty("套餐id")
+    @NotEmpty(message = "套餐id不能为空",groups = {Update.class})
+    private String id;
+
+    @ApiModelProperty(value = "套餐编码",required = true)
+    @NotEmpty(message = "套餐编码不能为空",groups = {Insert.class, Update.class})
+    private String packageCode;
+
+    @ApiModelProperty(value = "套餐类型 sys、系统套餐 custom、自定义套餐",required = true)
+    @NotEmpty(message = "套餐类型不能为空",groups = {Insert.class, Update.class})
+    private String type;
+
+    @ApiModelProperty(value = "套餐名称",required = true)
+    @NotEmpty(message = "套餐名称不能为空",groups = {Insert.class, Update.class})
+    private String packageName;
+
+    @ApiModelProperty("套餐备注")
+    private String remark;
+
+    @ApiModelProperty("排序")
+    private Integer sort;
+
+    @ApiModelProperty(value = "是否启用 0、启用 1、关闭",required = true)
+    @NotEmpty(message = "套餐状态不能为空",groups = {Insert.class, Update.class})
+    private Boolean disable;
+}

+ 21 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantPackageMenuDTO.java

@@ -0,0 +1,21 @@
+package cn.tr.module.sys.tenant.dto;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * @ClassName : SysRoleMenuPO
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月04日
+ */
+@Data
+public class SysTenantPackageMenuDTO {
+    private String id;
+
+    @NotEmpty(message = "套餐不能为空")
+    private String packageId;
+
+    @NotEmpty(message = "菜单不能为空")
+    private String menuId;
+}

+ 22 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantPackageQueryDTO.java

@@ -0,0 +1,22 @@
+package cn.tr.module.sys.tenant.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @ClassName : SysTenantPackageQueryDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月07日
+ */
+@Data
+@ApiModel("套餐查询参数")
+public class SysTenantPackageQueryDTO {
+    @ApiModelProperty("套餐编码")
+    private String packageCode;
+    @ApiModelProperty("套餐名称")
+    private String packageName;
+    @ApiModelProperty("套餐名称")
+    private Boolean disable;
+}

+ 24 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/dto/SysTenantQueryDTO.java

@@ -0,0 +1,24 @@
+package cn.tr.module.sys.tenant.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @ClassName : SysTenantQueryDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Data
+@ApiModel("租户查询参数")
+public class SysTenantQueryDTO {
+    @ApiModelProperty("租户名")
+    private String name;
+    @ApiModelProperty("联系人")
+    private String contractUser;
+    @ApiModelProperty("联系电话")
+    private String contactMobile;
+    @ApiModelProperty("租户状态")
+    private Boolean disable;
+}

+ 2 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/package-info.java

@@ -0,0 +1,2 @@
+package cn.tr.module.sys.tenant;
+//租户相关包

+ 51 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/po/SysTenantPO.java

@@ -0,0 +1,51 @@
+package cn.tr.module.sys.tenant.po;
+
+
+import cn.tr.core.annotation.ColumnDefaultValue;
+import cn.tr.core.annotation.Comment;
+import cn.tr.plugin.mybatis.pojo.BasePO;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import org.apache.ibatis.type.JdbcType;
+
+/**
+ * @ClassName : SysTenantPO
+ * @Description : 租户
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Data
+@TableName("sys_tenant")
+public class SysTenantPO extends BasePO {
+    @Comment("租户id")
+    private String id;
+
+    @Comment("租户名称")
+    private String name;
+
+    @Comment("租户联系人")
+    private String contractUser;
+
+    @Comment("租户联系人电话")
+    private String contactMobile;
+
+    @Comment("租户所属用户id")
+    private String tenantUserId;
+
+    @Comment("租户套餐id")
+    private String packageId;
+
+    @Comment("租户备注")
+    private String remark;
+
+    @Comment("租户状态 0、开启 1、关闭")
+    private Boolean disable;
+
+    @Comment("删除标记")
+    @ColumnDefaultValue("0")
+    @TableLogic
+    @TableField(jdbcType = JdbcType.TINYINT)
+    private Boolean deleted;
+}

+ 25 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/po/SysTenantPackageMenuPO.java

@@ -0,0 +1,25 @@
+package cn.tr.module.sys.tenant.po;
+
+import cn.tr.core.annotation.Comment;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * @ClassName : 租户套餐权限分配
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月04日
+ */
+@Data
+@TableName("sys_tenant_package_menu")
+public class SysTenantPackageMenuPO {
+    @TableId
+    private String id;
+
+    @Comment("套餐id")
+    private String packageId;
+
+    @Comment("菜单id")
+    private String menuId;
+}

+ 40 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/po/SysTenantPackagePO.java

@@ -0,0 +1,40 @@
+package cn.tr.module.sys.tenant.po;
+
+import cn.tr.core.annotation.Comment;
+import cn.tr.plugin.mybatis.pojo.BasePO;
+import cn.tr.plugin.mybatis.pojo.TenantPO;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * @ClassName : SysRolePO
+ * @Description : 租户套餐
+ * @Author : LF
+ * @Date: 2023年04月03日
+ */
+@Data
+@TableName("sys_tenant_package")
+public class SysTenantPackagePO extends BasePO {
+    @TableId
+    @Comment("套餐id")
+    private String id;
+
+    @Comment("套餐编码")
+    private String packageCode;
+
+    @Comment("套餐名称")
+    private String packageName;
+
+    @Comment("套餐备注")
+    private String remark;
+
+    @Comment("套餐类型 sys、系统套餐 custom、自定义套餐")
+    private String type;
+
+    @Comment("排序")
+    private Integer sort;
+
+    @Comment("是否启用 0、启用 1、关闭")
+    private Boolean disable;
+}

+ 18 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/repository/SysTenantPackageMenuRepository.java

@@ -0,0 +1,18 @@
+package cn.tr.module.sys.tenant.repository;
+
+import cn.tr.module.sys.tenant.po.SysTenantPackageMenuPO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @ClassName : SysRoleMenuRepository
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月31日
+ */
+@Mapper
+@Repository
+public interface SysTenantPackageMenuRepository extends BaseMapper<SysTenantPackageMenuPO> {
+
+}

+ 18 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/repository/SysTenantPackageRepository.java

@@ -0,0 +1,18 @@
+package cn.tr.module.sys.tenant.repository;
+
+import cn.tr.module.sys.tenant.po.SysTenantPackagePO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @ClassName : SysRoleMenuRepository
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月31日
+ */
+@Mapper
+@Repository
+public interface SysTenantPackageRepository extends BaseMapper<SysTenantPackagePO> {
+
+}

+ 18 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/repository/SysTenantRepository.java

@@ -0,0 +1,18 @@
+package cn.tr.module.sys.tenant.repository;
+
+import cn.tr.module.sys.tenant.po.SysTenantPO;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @ClassName : SysRoleMenuRepository
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月31日
+ */
+@Mapper
+@Repository
+public interface SysTenantRepository extends BaseMapper<SysTenantPO> {
+
+}

+ 42 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/ISysTenantPackageMenuService.java

@@ -0,0 +1,42 @@
+package cn.tr.module.sys.tenant.service;
+
+import cn.tr.module.sys.tenant.dto.SysTenantPackageMenuDTO;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+
+import java.util.*;
+/**
+ * @ClassName : ISysTenantPackageMenuService
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月07日
+ */
+
+public interface ISysTenantPackageMenuService {
+    /**
+     * 为租户套餐分配菜单
+     * @param source 租户套餐菜单关联信息
+     * @return
+     */
+    void assignPackageMenu(List<SysTenantPackageMenuDTO> source);
+
+    /**
+     * 通过菜单id找到所有持有该菜单的套餐
+     * @param menuIds
+     * @param containsSys 是否包含系统套餐
+     * @return
+     */
+    Set<String> findPackageIdByMenuId(Collection<String> menuIds,boolean containsSys);
+
+    @CacheEvict(value = "tenant:package:menu",key = "#packageId")
+    default void delCacheMenuIdByPackageId(String packageId){
+
+    }
+    /**
+     * 找到套餐下所有的菜单id
+     * @param packageId
+     * @return
+     */
+    @Cacheable(value = "tenant:package:menu",key = "#packageId")
+    Set<String> findMenuIdByPackageId(String packageId);
+}

+ 53 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/ISysTenantPackageService.java

@@ -0,0 +1,53 @@
+package cn.tr.module.sys.tenant.service;
+
+import cn.tr.module.sys.tenant.dto.SysTenantPackageDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantPackageQueryDTO;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @ClassName : ISysTenantPackageService
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月31日
+ */
+public interface ISysTenantPackageService {
+
+    /**
+     * 根据条件查询
+     * @param query 查询参数
+     * @return
+     */
+    List<SysTenantPackageDTO> selectSysTenantPackageList(SysTenantPackageQueryDTO query);
+
+    /**
+     * 根据id查询操作租户套餐
+     * @param id 租户套餐id
+     * @return 租户套餐
+     */
+    SysTenantPackageDTO selectSysTenantPackageById(String id);
+
+    /**
+     * 更新租户套餐
+     * @param source 更新租户套餐
+     * @return true:更新成功
+     */
+    boolean updateSysTenantPackageById(SysTenantPackageDTO source);
+
+    /**
+     * 新增租户套餐
+     * @param source 新增租户套餐
+     * @return true:新增成功
+     */
+    boolean insertSysTenantPackage(SysTenantPackageDTO source);
+
+    /**
+     * 根据id删除租户套餐
+     * @param ids 租户套餐id
+     * @return 删除数量
+     */
+    int deleteSysTenantPackageByIds(Collection<String> ids);
+
+    Collection<SysTenantPackageDTO> findAllSysPackages();
+}

+ 84 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/ISysTenantService.java

@@ -0,0 +1,84 @@
+package cn.tr.module.sys.tenant.service;
+
+import cn.hutool.core.util.StrUtil;
+import cn.tr.module.sys.tenant.dto.SysTenantAddDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantQueryDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantCommonDTO;
+import cn.tr.module.sys.user.dto.SysMenuDTO;
+import cn.tr.plugin.biz.tenant.config.aop.CacheIgnoreTenant;
+import cn.tr.plugin.biz.tenant.context.TenantContextHolder;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : ISysTenantService
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月31日
+ */
+
+public interface ISysTenantService {
+    /**
+     * 根据条件查询
+     * @param query 查询参数
+     * @return
+     */
+    List<SysTenantCommonDTO> selectSysTenantList(SysTenantQueryDTO query);
+
+    /**
+     * 根据id查询操作租户
+     * @param id 租户id
+     * @return 租户
+     */
+    SysTenantCommonDTO selectSysTenantById(String id);
+
+    /**
+     * 更新租户
+     * @param source 更新租户
+     * @return true:更新成功
+     */
+    @CacheIgnoreTenant
+    boolean updateSysTenantById(SysTenantCommonDTO source);
+
+    /**
+     * 新增租户
+     * @param source 新增租户
+     * @return true:新增成功
+     */
+    boolean insertSysTenant(SysTenantAddDTO source);
+
+    /**
+     * 根据id删除租户
+     * @param ids 租户id
+     * @return 删除数量
+     */
+    int deleteSysTenantByIds(Collection<String> ids);
+    /**
+     * 获取当前租户的菜单id
+     * @return
+     */
+
+    /**
+     * 查询租户的权限
+     * @return
+     */
+    default Set<String>  findTenantPermission(){
+        return currentTenantMenus()
+                .stream()
+                .filter(Objects::nonNull)
+                .filter(menu->Boolean.FALSE.equals(menu.getDisable()))
+                .map(SysMenuDTO::getPermission)
+                .filter(StrUtil::isNotEmpty)
+                .collect(Collectors.toSet());
+    };
+
+    default Set<SysMenuDTO> currentTenantMenus(){
+        return getTenantMenus(TenantContextHolder.getTenantId());
+    };
+
+    Set<SysMenuDTO> getTenantMenus(String tenantId);
+}

+ 82 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/impl/SysTenantPackageMenuServiceImpl.java

@@ -0,0 +1,82 @@
+package cn.tr.module.sys.tenant.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import cn.tr.module.sys.mapper.tenant.SysTenantPackageMenuMapper;
+import cn.tr.module.sys.tenant.dto.SysTenantPackageDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantPackageMenuDTO;
+import cn.tr.module.sys.tenant.po.SysTenantPackageMenuPO;
+import cn.tr.module.sys.tenant.po.SysTenantPackagePO;
+import cn.tr.module.sys.tenant.repository.SysTenantPackageMenuRepository;
+import cn.tr.module.sys.tenant.service.ISysTenantPackageMenuService;
+import cn.tr.module.sys.tenant.service.ISysTenantPackageService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : SysTenantPackageMenuServiceImpl
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月07日
+ */
+@Service
+public class SysTenantPackageMenuServiceImpl  extends ServiceImpl<SysTenantPackageMenuRepository, SysTenantPackageMenuPO>  implements ISysTenantPackageMenuService {
+    @Autowired
+    @Lazy
+    private ISysTenantPackageService tenantPackageService;
+    @Autowired
+    @Lazy
+    private ISysTenantPackageMenuService self;
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void assignPackageMenu(List<SysTenantPackageMenuDTO> source) {
+        List<SysTenantPackageMenuPO> packageMenus = SysTenantPackageMenuMapper.INSTANCE.toPOList(source);
+        Map<String, List<SysTenantPackageMenuPO>> groupByPackageIdMap = packageMenus
+                .stream()
+                .collect(Collectors.groupingBy(SysTenantPackageMenuPO::getPackageId));
+        groupByPackageIdMap.forEach((packageId,pms)->{
+            baseMapper.delete(
+                    new LambdaQueryWrapper<SysTenantPackageMenuPO>().eq(SysTenantPackageMenuPO::getPackageId,packageId)
+            );
+            self.delCacheMenuIdByPackageId(packageId);
+        });
+        this.saveBatch(packageMenus);
+    }
+
+    @Override
+    public Set<String> findPackageIdByMenuId(Collection<String> menuIds,boolean containsSys) {
+        Set<String> packageIds = this.list(new LambdaQueryWrapper<SysTenantPackageMenuPO>()
+                .in(SysTenantPackageMenuPO::getMenuId, menuIds))
+                .stream()
+                .map(SysTenantPackageMenuPO::getPackageId)
+                .collect(Collectors.toSet());
+        Set<String> sysPackageIds=new HashSet<>();
+        if(containsSys){
+            sysPackageIds = tenantPackageService.findAllSysPackages().stream()
+                    .map(SysTenantPackageDTO::getId)
+                    .collect(Collectors.toSet());
+
+        }
+        sysPackageIds.addAll(packageIds);
+        return sysPackageIds;
+    }
+
+    @Override
+    public Set<String> findMenuIdByPackageId(String packageId) {
+        SysTenantPackageDTO packageDTO = tenantPackageService.selectSysTenantPackageById(packageId);
+        if(StrUtil.equals(packageDTO.getType(),"sys")){
+            //所有目录id
+
+        }
+        return this.list(new LambdaQueryWrapper<SysTenantPackageMenuPO>().eq(SysTenantPackageMenuPO::getPackageId, packageId))
+                .stream()
+                .map(SysTenantPackageMenuPO::getMenuId)
+                .collect(Collectors.toSet());
+    }
+}

+ 98 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/impl/SysTenantPackageServiceImpl.java

@@ -0,0 +1,98 @@
+package cn.tr.module.sys.tenant.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.core.exception.ServiceException;
+import cn.tr.core.exception.TRExcCode;
+import cn.tr.core.utils.JsonUtils;
+import cn.tr.module.sys.mapper.tenant.SysTenantPackageMapper;
+import cn.tr.module.sys.tenant.dto.SysTenantPackageDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantPackageQueryDTO;
+import cn.tr.module.sys.tenant.po.SysTenantPO;
+import cn.tr.module.sys.tenant.po.SysTenantPackagePO;
+import cn.tr.module.sys.tenant.repository.SysTenantPackageRepository;
+import cn.tr.module.sys.tenant.repository.SysTenantRepository;
+import cn.tr.module.sys.tenant.service.ISysTenantPackageService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : SysTenantPackageServiceImpl
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月07日
+ */
+@Service
+public class SysTenantPackageServiceImpl implements ISysTenantPackageService {
+    @Autowired
+    private SysTenantPackageRepository tenantPackageRepository;
+
+    @Autowired
+    private SysTenantRepository tenantRepository;
+
+    @Override
+    public List<SysTenantPackageDTO> selectSysTenantPackageList(SysTenantPackageQueryDTO query) {
+        return SysTenantPackageMapper.INSTANCE.toDTOList(
+                tenantPackageRepository.selectList(new LambdaQueryWrapper<SysTenantPackagePO>()
+                        .like(StrUtil.isNotEmpty(query.getPackageCode()),SysTenantPackagePO::getPackageCode,query.getPackageCode())
+                        .like(StrUtil.isNotEmpty(query.getPackageName()),SysTenantPackagePO::getPackageName,query.getPackageName())
+                        .eq(ObjectUtil.isNotNull(query.getDisable()),SysTenantPackagePO::getDisable,query.getDisable())
+                ));
+    }
+
+    @Override
+    public SysTenantPackageDTO selectSysTenantPackageById(String id) {
+        return SysTenantPackageMapper.INSTANCE.toDTO(tenantPackageRepository.selectById(id));
+    }
+
+    @Override
+    public boolean updateSysTenantPackageById(SysTenantPackageDTO source) {
+        SysTenantPackagePO exitPackage = tenantPackageRepository.selectOne(new LambdaQueryWrapper<SysTenantPackagePO>()
+                .eq(SysTenantPackagePO::getPackageCode, source.getPackageCode())
+                .ne(SysTenantPackagePO::getId,source.getId())
+                .last("limit 1"));
+        if(exitPackage!=null){
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("租户套餐编号 %s 已存在,无法重复添加",source.getPackageCode()));
+        }
+        return tenantPackageRepository.updateById(SysTenantPackageMapper.INSTANCE.toPO(source))!=0;
+    }
+
+    @Override
+    public boolean insertSysTenantPackage(SysTenantPackageDTO source) {
+        SysTenantPackagePO exitPackage = tenantPackageRepository.selectOne(new LambdaQueryWrapper<SysTenantPackagePO>()
+                .eq(SysTenantPackagePO::getPackageCode, source.getPackageCode())
+                .last("limit 1"));
+        if(exitPackage!=null){
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("租户套餐编号 %s 已存在,无法重复添加",source.getPackageCode()));
+        }
+        return tenantPackageRepository.insert(SysTenantPackageMapper.INSTANCE.toPO(source))!=0;
+    }
+
+    @Override
+    public int deleteSysTenantPackageByIds(Collection<String> ids) {
+        List<SysTenantPO> tenants = tenantRepository.selectList(new LambdaQueryWrapper<SysTenantPO>()
+                .in(SysTenantPO::getPackageId, ids));
+        if(CollectionUtil.isNotEmpty(tenants)){
+            Set<String> packageIds = tenants.stream().map(SysTenantPO::getPackageId).collect(Collectors.toSet());
+            List<SysTenantPackagePO> packages = tenantPackageRepository.selectBatchIds(packageIds);
+            if(CollectionUtil.isNotEmpty(packages)){
+                Set<String> packageNames = packages.stream().map(SysTenantPackagePO::getPackageName).collect(Collectors.toSet());
+                throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("租户套餐 %s 正在被使用,无法删除", JsonUtils.toJsonString(packageNames)));
+            }
+        }
+        return tenantPackageRepository.deleteBatchIds(ids);
+    }
+
+    @Override
+    public Collection<SysTenantPackageDTO> findAllSysPackages() {
+        return SysTenantPackageMapper.INSTANCE.toDTOList(tenantPackageRepository.selectList(new LambdaQueryWrapper<SysTenantPackagePO>()
+                .eq(SysTenantPackagePO::getType,"sys")));
+    }
+}

+ 156 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/tenant/service/impl/SysTenantServiceImpl.java

@@ -0,0 +1,156 @@
+package cn.tr.module.sys.tenant.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.module.sys.mapper.tenant.SysTenantMapper;
+import cn.tr.module.sys.mapper.user.SysMenuMapper;
+import cn.tr.module.sys.tenant.dto.SysTenantAddDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantCommonDTO;
+import cn.tr.module.sys.tenant.dto.SysTenantQueryDTO;
+import cn.tr.module.sys.tenant.service.ISysTenantPackageMenuService;
+import cn.tr.module.sys.tenant.service.ISysTenantPackageService;
+import cn.tr.module.sys.user.dto.*;
+import cn.tr.module.sys.user.enums.RoleEnum;
+import cn.tr.module.sys.user.enums.UserStatusEnum;
+import cn.tr.module.sys.tenant.po.SysTenantPO;
+import cn.tr.module.sys.tenant.repository.SysTenantRepository;
+import cn.tr.module.sys.user.po.SysMenuPO;
+import cn.tr.module.sys.user.repository.SysMenuRepository;
+import cn.tr.module.sys.user.service.ISysMenuService;
+import cn.tr.module.sys.user.service.ISysOrgService;
+import cn.tr.module.sys.user.service.ISysRoleService;
+import cn.tr.module.sys.tenant.service.ISysTenantService;
+import cn.tr.module.sys.user.service.ISysUserService;
+import cn.tr.plugin.biz.tenant.context.TenantContextHolder;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+/**
+ * @ClassName : SysTenantServiceImpl
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+@Service
+public class SysTenantServiceImpl implements ISysTenantService {
+    @Autowired
+    private SysTenantRepository tenantRepository;
+
+    @Autowired
+    @Lazy
+    private ISysUserService userService;
+
+    @Autowired
+    @Lazy
+    private ISysOrgService orgService;
+
+    @Autowired
+    @Lazy
+    private ISysRoleService roleService;
+
+    @Autowired
+    private SysMenuRepository menuRepository;
+
+    @Autowired
+    @Lazy
+    private ISysTenantPackageMenuService tenantPackageMenuService;
+    @Override
+    public List<SysTenantCommonDTO> selectSysTenantList(SysTenantQueryDTO query) {
+        return SysTenantMapper.INSTANCE.toCommonDTOList(tenantRepository.selectList(new LambdaQueryWrapper<SysTenantPO>()
+                .like(StrUtil.isNotEmpty(query.getName()),SysTenantPO::getName,query.getName())
+                .like(StrUtil.isNotEmpty(query.getContactMobile()),SysTenantPO::getContactMobile,query.getContactMobile())
+                .like(StrUtil.isNotEmpty(query.getContractUser()),SysTenantPO::getContractUser,query.getContractUser())
+                .like(ObjectUtil.isNotNull(query.getDisable()),SysTenantPO::getDisable,query.getDisable())
+        ));
+    }
+
+    @Override
+    public SysTenantCommonDTO selectSysTenantById(String id) {
+        return SysTenantMapper.INSTANCE.toCommonDTO(tenantRepository.selectById(id));
+    }
+
+    @Override
+    public boolean updateSysTenantById(SysTenantCommonDTO source) {
+        return tenantRepository.updateById(SysTenantMapper.INSTANCE.toPO(source))!=0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean insertSysTenant(SysTenantAddDTO source) {
+        String orgId= IdWorker.getIdStr();
+        String userId= IdWorker.getIdStr();
+        String roleId= IdWorker.getIdStr();
+        SysTenantPO tenant = SysTenantMapper.INSTANCE.toPO(source);
+        tenant.setTenantUserId(userId);
+        boolean result = tenantRepository.insert(tenant) != 0;
+        if(!result){
+            return false;
+        }
+        String tenantId = tenant.getId();
+        TenantContextHolder.setIgnore(false);
+        TenantContextHolder.setTenantId(tenantId);
+        //创建部门
+        orgService.insertSysOrg(buildOrg(orgId,source.getName()));
+        //创建角色
+        roleService.insertSysRole(buildRole(roleId));
+        //创建租户用户
+        userService.insertSysUser(buildUser(userId,roleId,orgId,source.getUsername(),source.getPassword(),source.getName()));
+        return result;
+    }
+
+    @Override
+    public int deleteSysTenantByIds(Collection<String> ids) {
+        return tenantRepository.deleteBatchIds(ids);
+    }
+
+    @Override
+    public Set<SysMenuDTO> getTenantMenus(String tenantId) {
+        SysTenantPO tenant = tenantRepository.selectById(tenantId);
+        Set<String> menuIds = tenantPackageMenuService.findMenuIdByPackageId(tenant.getPackageId());
+        return new HashSet<>(SysMenuMapper.INSTANCE.toSysMenuDTOList( menuRepository.selectBatchIds(menuIds)));
+    }
+
+    private SysOrgDTO buildOrg(String orgId,String tenantName){
+        SysOrgDTO org = new SysOrgDTO();
+        org.setId(orgId);
+        org.setParentId("0");
+        org.setSort(1);
+        org.setName(tenantName);
+        org.setDisable(false);
+        return org;
+    }
+
+    private SysRoleDTO buildRole(String roleId){
+        SysRoleDTO role = new SysRoleDTO();
+        role.setId(roleId);
+        role.setDataScope("1");
+        role.setCode("admin");
+        role.setName("超级管理员");
+        role.setRemark("系统角色");
+        role.setDisable(false);
+        role.setSort(1);
+        role.setType(RoleEnum.sys.name());
+        return role;
+    }
+
+    private SysUserDTO buildUser(String userId,String roleId,String orgId,String username,String password,String tenantName){
+        SysUserDTO user = new SysUserDTO();
+        user.setUserId(userId);
+        user.setOrgId(orgId);
+        user.setUsername(username);
+        user.setPassword(password);
+        user.setNickname(tenantName);
+        user.setRoleIds(Collections.singleton(roleId));
+        user.setStatus(UserStatusEnum.normal.getValue());
+
+        return user;
+    }
+}

+ 8 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/controller/SysMenuController.java

@@ -17,7 +17,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.Collection;
-
+import java.util.*;
 /**
  * @ClassName : SysMenuController
  * @Description :
@@ -31,6 +31,13 @@ import java.util.Collection;
 public class SysMenuController extends BaseController {
     private final ISysMenuService menuService;
 
+    @PostMapping("/query/tree")
+    @ApiOperationSupport(author = "lf")
+    @ApiOperation(value = "菜单树",notes = "权限: 无")
+    public CommonResult<List<SysMenuDTO>> selectTree(@RequestBody SysMenuQueryDTO query){
+        return CommonResult.success(menuService.selectSysMenuTree(query));
+    }
+
     @PostMapping("/query/page")
     @ApiOperationSupport(author = "lf")
     @ApiOperation(value = "根据条件查询菜单",notes = "权限: sys:menu:query")

+ 6 - 6
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/controller/SysOrgController.java

@@ -58,24 +58,24 @@ public class SysOrgController extends BaseController {
 
     @PostMapping("/edit")
     @ApiOperationSupport(author = "lf")
-    @SaCheckPermission("sys:Org:edit")
-    @ApiOperation(value = "根据id更新组织",notes = "权限: sys:Org:edit")
+    @SaCheckPermission("sys:org:edit")
+    @ApiOperation(value = "根据id更新组织",notes = "权限: sys:org:edit")
     public CommonResult<Boolean> edit(@RequestBody@Validated(Update.class) SysOrgDTO source){
         return CommonResult.success(orgService.updateSysOrgById(source));
     }
 
     @PostMapping("/add")
     @ApiOperationSupport(author = "lf")
-    @SaCheckPermission("sys:Org:add")
-    @ApiOperation(value = "新增组织",notes = "权限: sys:Org:add")
+    @SaCheckPermission("sys:org:add")
+    @ApiOperation(value = "新增组织",notes = "权限: sys:org:add")
     public CommonResult<Boolean> add(@RequestBody@Validated(Insert.class) SysOrgDTO source){
         return CommonResult.success(orgService.insertSysOrg(source));
     }
 
     @PostMapping("/deleteByIds")
     @ApiOperationSupport(author = "lf")
-    @ApiOperation(value = "删除组织",notes = "权限: sys:Org:del")
-    @SaCheckPermission("sys:Org:del")
+    @ApiOperation(value = "删除组织",notes = "权限: sys:org:del")
+    @SaCheckPermission("sys:org:del")
     public CommonResult<Integer> deleteByIds(@RequestBody Collection<String> ids){
         return CommonResult.success(orgService.deleteSysOrgByIds(ids));
     }

+ 27 - 9
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/controller/SysRoleController.java

@@ -3,13 +3,17 @@ package cn.tr.module.sys.user.controller;
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import cn.tr.core.pojo.CommonResult;
 import cn.tr.core.pojo.TableDataInfo;
+import cn.tr.core.utils.TreeUtil;
 import cn.tr.core.validation.Insert;
 import cn.tr.core.validation.Update;
+import cn.tr.module.sys.user.dto.SysMenuDTO;
 import cn.tr.module.sys.user.dto.SysRoleDTO;
 import cn.tr.module.sys.user.dto.SysRoleMenuDTO;
 import cn.tr.module.sys.user.dto.SysRoleQueryDTO;
+import cn.tr.module.sys.user.service.ISysMenuService;
 import cn.tr.module.sys.user.service.ISysRoleMenuService;
 import cn.tr.module.sys.user.service.ISysRoleService;
+import cn.tr.module.sys.user.vo.RouteItemVO;
 import cn.tr.plugin.mybatis.base.BaseController;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import io.swagger.annotations.Api;
@@ -18,7 +22,9 @@ import lombok.AllArgsConstructor;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.*;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @ClassName : SysRoleController
@@ -31,8 +37,20 @@ import java.util.*;
 @Api(tags = "角色")
 @AllArgsConstructor
 public class SysRoleController extends BaseController {
-    private final ISysRoleService RoleService;
+    private final ISysRoleService roleService;
     private final ISysRoleMenuService roleMenuService;
+    private final ISysMenuService menuService;
+
+    @GetMapping("/menu/tree/{roleId}")
+    @ApiOperation(value = "查询角色下的权限信息",notes = "权限: 无")
+    public CommonResult<List<RouteItemVO>> listMenu(@PathVariable("roleId") String roleId) {
+        return CommonResult.success(TreeUtil.buildTree(
+                menuService.findAllMenuByRoleId(roleId)
+                        .stream()
+                        .map(menuService::convertToRoute)
+                        .collect(Collectors.toList())
+        ));
+    }
 
     @PostMapping("/query/page")
     @ApiOperationSupport(author = "lf")
@@ -40,7 +58,7 @@ public class SysRoleController extends BaseController {
     @SaCheckPermission("sys:role:query")
     public TableDataInfo<SysRoleDTO> selectList(@RequestBody SysRoleQueryDTO query){
         startPage();
-        return getDataTable(RoleService.selectSysRoleList(query));
+        return getDataTable(roleService.selectSysRoleList(query));
     }
 
     @PostMapping("/assign")
@@ -57,7 +75,7 @@ public class SysRoleController extends BaseController {
     @ApiOperation(value = "根据id查询角色",notes = "权限: sys:role:query")
     @SaCheckPermission("sys:role:query")
     public CommonResult<SysRoleDTO> findById(@PathVariable("id") String id){
-        return CommonResult.success(RoleService.selectSysRoleById(id));
+        return CommonResult.success(roleService.selectSysRoleById(id));
     }
 
     @PostMapping("/edit")
@@ -65,7 +83,7 @@ public class SysRoleController extends BaseController {
     @SaCheckPermission("sys:role:edit")
     @ApiOperation(value = "根据id更新角色",notes = "权限: sys:role:edit")
     public CommonResult<Boolean> edit(@RequestBody@Validated(Update.class) SysRoleDTO source){
-        return CommonResult.success(RoleService.updateSysRoleById(source));
+        return CommonResult.success(roleService.updateSysRoleById(source));
     }
 
     @PostMapping("/add")
@@ -73,17 +91,17 @@ public class SysRoleController extends BaseController {
     @SaCheckPermission("sys:role:add")
     @ApiOperation(value = "新增角色",notes = "权限: sys:role:add")
     public CommonResult<Boolean> add(@RequestBody@Validated(Insert.class) SysRoleDTO source){
-        return CommonResult.success(RoleService.insertSysRole(source));
+        return CommonResult.success(roleService.insertSysRole(source));
     }
-    
+
     @PostMapping("/deleteByIds")
     @ApiOperationSupport(author = "lf")
     @ApiOperation(value = "删除角色",notes = "权限: sys:role:del")
     @SaCheckPermission("sys:role:del")
     public CommonResult<Integer> deleteByIds(@RequestBody Collection<String> ids){
-        return CommonResult.success(RoleService.deleteSysRoleByIds(ids));
+        return CommonResult.success(roleService.deleteSysRoleByIds(ids));
     }
-    
+
 
     //todo 导出
 

+ 80 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/controller/SysUserOnlineController.java

@@ -0,0 +1,80 @@
+package cn.tr.module.sys.user.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.session.SaSession;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.PageUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.tr.core.pojo.CommonResult;
+import cn.tr.core.pojo.PageInfo;
+import cn.tr.core.pojo.TableDataInfo;
+import cn.tr.core.strategy.PageStrategy;
+import cn.tr.module.sys.user.dto.OnlineUserOperationDTO;
+import cn.tr.module.sys.user.dto.OnlineUserQueryDTO;
+import cn.tr.module.sys.user.dto.OnlineUserSessionDTO;
+import cn.tr.plugin.mybatis.base.BaseController;
+import cn.tr.plugin.security.constant.SecurityConstant;
+import cn.tr.plugin.security.utils.SaTokenUtils;
+import com.github.pagehelper.Page;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 在线用户监控
+ *
+ * @author ruoyi
+ */
+@RestController
+@Api(tags = "在线用户监控")
+@RequestMapping("/online")
+public class SysUserOnlineController extends BaseController {
+
+    @SaCheckPermission("sys:online:list")
+    @PostMapping("/list")
+    @ApiOperation(value = "在线用户列表",notes = "权限 - monitor:online:list")
+    @ApiImplicitParams(
+            @ApiImplicitParam(name = "name",example = "用户名或昵称查询")
+    )
+    public TableDataInfo<OnlineUserSessionDTO> list(@RequestBody@Validated OnlineUserQueryDTO query) {
+        startPage();
+        StpLogic stpUtil = SaTokenUtils.getStpUtil(query.getLoginType());
+        Page<?> page = PageStrategy.tr.getPage.get();
+        int pageNum = page.getPageNum()-1;
+        int pageSize = page.getPageSize();
+        List<String> sessionIds = stpUtil.searchSessionId(StrUtil.isNotEmpty(query.getBlurry())?query.getBlurry():"", PageUtil.getStart(pageNum,pageSize), pageSize, true);
+        List<OnlineUserSessionDTO> result = sessionIds.parallelStream()
+                .map(sessionId -> SaTokenUtils.getValue(stpUtil.getSessionBySessionId(sessionId), SecurityConstant.SESSION_USER, OnlineUserSessionDTO.class))
+                .collect(Collectors.toList());
+        int total = CollectionUtil.size(stpUtil.searchSessionId( "", 0, Integer.MAX_VALUE, false));
+        return new TableDataInfo<>(PageInfo.of(Integer.valueOf(total).longValue(), page.getPageNum(), pageSize,result));
+    }
+
+    /**
+     * 强退用户
+     */
+    @SaCheckPermission("sys:online:forceLogout")
+    @ApiOperation("强退用户")
+    @PostMapping("/forceLogout")
+    public CommonResult forceLogout(@RequestBody @Validated OnlineUserOperationDTO source) {
+        StpLogic stpLogic = SaTokenUtils.getStpUtil(source.getLoginType());
+        if (CollectionUtil.isNotEmpty(source.getTokenValues())) {
+            source.getTokenValues().forEach(stpLogic::logoutByTokenValue);
+        }
+        if(CollectionUtil.isNotEmpty(source.getSessionIds())){
+            source.getSessionIds().parallelStream().map(stpLogic::getSessionBySessionId)
+                    .forEach(SaSession::logout);
+        }
+        return CommonResult.success();
+    }
+}

+ 29 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/OnlineUserOperationDTO.java

@@ -0,0 +1,29 @@
+package cn.tr.module.sys.user.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import java.util.*;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * @ClassName : SysUserOnlineOperationDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2022年11月30日
+ */
+@ApiModel("在线用户操作")
+@Data
+public class OnlineUserOperationDTO implements Serializable {
+    private static final long serialVersionUID = -7177488270688840814L;
+    @ApiModelProperty(value = "登录方式",required = true)
+    @NotNull(message = "登录方式不能为空")
+    private String loginType;
+
+    @ApiModelProperty(value = "tokenValues,与sessionIds不能同时为空")
+    private List<String> tokenValues;
+
+    @ApiModelProperty(value = "会话id集合,与tokenValues不能同时为空")
+    private List<String>  sessionIds;
+}

+ 24 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/OnlineUserQueryDTO.java

@@ -0,0 +1,24 @@
+package cn.tr.module.sys.user.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * @ClassName : OnlineUserQueryDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2022年11月30日
+ */
+@ApiModel("在线用户查询")
+@Data
+public class OnlineUserQueryDTO {
+    @ApiModelProperty("用户名模糊查询")
+    private String blurry;
+
+    @ApiModelProperty("账户体系")
+    @NotEmpty(message = "账户体系不能为空")
+    private String loginType;
+}

+ 49 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/OnlineUserSessionDTO.java

@@ -0,0 +1,49 @@
+package cn.tr.module.sys.user.dto;
+
+import cn.hutool.core.collection.CollectionUtil;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * @ClassName : OnlineUserSessionDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月06日
+ */
+@Data
+@ApiModel("在线会话对象")
+public class OnlineUserSessionDTO implements Serializable {
+    private static final long serialVersionUID = -1823111581365786434L;
+    @ApiModelProperty("会话id")
+    private String sessionId;
+
+    @ApiModelProperty("用户会话用户名")
+    private String username;
+
+    @ApiModelProperty("用户会话昵称")
+    private String nickname;
+
+    @ApiModelProperty("用户会话头像")
+    private String avatar;
+
+    @ApiModelProperty("会话最后登录地点")
+    private String lastLoginLocation;
+
+    @ApiModelProperty("会话最后登录ip")
+    private String lastLoginIp;
+
+    @ApiModelProperty("会话最后登录时间")
+    private Date lastLoginTime;
+
+    @ApiModelProperty("token会话列表")
+    private List<OnlineUserTokenSessionDTO> tokenSessionList;
+
+    @ApiModelProperty("token令牌数量")
+    public Integer getTokenCount(){
+        return CollectionUtil.size(tokenSessionList);
+    }
+}

+ 37 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/OnlineUserTokenSessionDTO.java

@@ -0,0 +1,37 @@
+package cn.tr.module.sys.user.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @ClassName : OnlineUserSessionDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月06日
+ */
+@Data
+@ApiModel("在线token会话对象")
+public class OnlineUserTokenSessionDTO implements Serializable {
+    private static final long serialVersionUID = -1823111581365786434L;
+    @ApiModelProperty("token令牌值")
+    private String tokenValue;
+
+    @ApiModelProperty(value = "登录IP地址",readOnly = true)
+    private String loginIp;
+
+    @ApiModelProperty(value = "登录地点",readOnly = true)
+    private String loginLocation;
+
+    @ApiModelProperty(value = "浏览器类型",readOnly = true)
+    private String browser;
+
+    @ApiModelProperty(value = "操作系统",readOnly = true)
+    private String os;
+
+    @ApiModelProperty(value = "登录时间",readOnly = true)
+    private Date loginTime;
+}

+ 13 - 14
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysMenuDTO.java

@@ -3,6 +3,7 @@ package cn.tr.module.sys.user.dto;
 import cn.tr.core.tree.TreeNode;
 import cn.tr.core.validation.Insert;
 import cn.tr.core.validation.Update;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -10,6 +11,7 @@ import lombok.ToString;
 
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
+import java.util.Date;
 
 /**
  * 菜单对象 sys_menu
@@ -28,14 +30,6 @@ public class SysMenuDTO extends TreeNode<String> {
     @NotEmpty(message = "菜单主键不能为空",groups = Update.class)
     private String id;
 
-    @ApiModelProperty(value = "父级id 顶级目录的父级id为0",required = true)
-    @NotEmpty(message = "父级id不能为空",groups = {Update.class, Insert.class})
-    private String parentId;
-
-    @ApiModelProperty(value="排序",required = true)
-    @NotEmpty(message = "排序不能为空",groups = {Update.class, Insert.class})
-    private Integer sort;
-
     /**
      * {@link cn.tr.module.sys.user.enums.MenuEnum}
      */
@@ -64,25 +58,20 @@ public class SysMenuDTO extends TreeNode<String> {
     private String icon;
 
     @ApiModelProperty(value = "是否缓存 0不缓存;1缓存",required = true)
-    @NotNull(message = "缓存状态不能为空",groups = {Update.class, Insert.class})
     private Boolean keepalive;
 
 
     @ApiModelProperty(value = "是否外链 0否;1是",required = true)
-    @NotNull(message = "外链状态不能为空",groups = {Update.class, Insert.class})
     private Boolean linkExternal;
 
 
     @ApiModelProperty(value = "是否显示 0隐藏;1显示",required = true)
-    @NotNull(message = "显示装填不能为空",groups = {Update.class, Insert.class})
     private Boolean visible;
 
 
     @ApiModelProperty(value = "是否内嵌 0不内嵌;1、内嵌",required = true)
-    @NotNull(message = "内嵌状态不能为空",groups = {Update.class, Insert.class})
     private Boolean frame;
 
-
     @ApiModelProperty(value = "外部链接",required = true)
     private String linkUrl;
 
@@ -90,9 +79,19 @@ public class SysMenuDTO extends TreeNode<String> {
     @ApiModelProperty(value = "备注")
     private String remark;
 
-
     @ApiModelProperty(value = "状态 0正常;1停用",required = true)
     @NotNull(message = "菜单状态不能为空",groups = {Update.class, Insert.class})
     private Boolean disable;
 
+    /**
+     * 创建时间
+     */
+    @JsonIgnoreProperties(allowGetters = true)
+    private Date createTime;
+
+    /**
+     * 最后更新时间
+     */
+    @JsonIgnoreProperties(allowGetters = true)
+    private Date updateTime;
 }

+ 2 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysMenuQueryDTO.java

@@ -16,7 +16,8 @@ import java.io.Serializable;
 @ApiModel("菜单查询参数")
 public class SysMenuQueryDTO implements Serializable {
     private static final long serialVersionUID = -262693810220247854L;
-
+    @ApiModelProperty("父级id")
+    private String parentId;
 
     @ApiModelProperty("菜单名称")
     private String name;

+ 13 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysOrgDTO.java

@@ -3,11 +3,13 @@ package cn.tr.module.sys.user.dto;
 import cn.tr.core.tree.TreeNode;
 import cn.tr.core.validation.Insert;
 import cn.tr.core.validation.Update;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.validation.constraints.NotNull;
+import java.util.Date;
 
 /**
  * 部门对象 sys_menu
@@ -49,4 +51,15 @@ public class SysOrgDTO extends TreeNode<String> {
     @NotNull(message = "部门状态不能为空",groups = {Insert.class, Update.class})
     private Boolean disable;
 
+    /**
+     * 创建时间
+     */
+    @JsonIgnoreProperties(allowGetters = true)
+    private Date createTime;
+
+    /**
+     * 最后更新时间
+     */
+    @JsonIgnoreProperties(allowGetters = true)
+    private Date updateTime;
 }

+ 4 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysPositionDTO.java

@@ -2,8 +2,10 @@ package cn.tr.module.sys.user.dto;
 
 import cn.tr.core.validation.Insert;
 import cn.tr.core.validation.Update;
+import cn.tr.plugin.mybatis.pojo.BaseDTO;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
 import javax.validation.constraints.NotEmpty;
 import java.io.Serializable;
@@ -12,7 +14,8 @@ import java.io.Serializable;
  * 岗位
  */
 @Data
-public class SysPositionDTO implements Serializable {
+@EqualsAndHashCode(callSuper = true)
+public class SysPositionDTO extends BaseDTO {
 
     private static final long serialVersionUID = -7314723402227370899L;
     /** id */

+ 12 - 8
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysRoleDTO.java

@@ -2,12 +2,15 @@ package cn.tr.module.sys.user.dto;
 
 import cn.tr.core.validation.Insert;
 import cn.tr.core.validation.Update;
+import cn.tr.plugin.mybatis.pojo.BaseDTO;
 import cn.tr.plugin.mybatis.pojo.TenantPO;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
 import javax.validation.constraints.NotEmpty;
+import java.io.Serializable;
 
 /**
  * @ClassName : SysRolePO
@@ -16,24 +19,29 @@ import javax.validation.constraints.NotEmpty;
  * @Date: 2023年04月03日
  */
 @Data
-public class SysRoleDTO extends TenantPO {
+@EqualsAndHashCode(callSuper = true)
+public class SysRoleDTO extends BaseDTO {
+    private static final long serialVersionUID = -522591939307162613L;
     @ApiModelProperty(value = "角色id",required = true)
     @NotEmpty(message = "角色id不能为空",groups = {Insert.class, Update.class})
     private String id;
 
     @ApiModelProperty(value = "角色编码",required = true)
     @NotEmpty(message = "角色编码不能为空",groups = {Insert.class, Update.class})
-    private String roleCode;
+    private String code;
 
     @ApiModelProperty(value = "角色名称",required = true)
     @NotEmpty(message = "角色名称不能为空",groups = {Insert.class, Update.class})
-    private String roleName;
+    private String name;
 
     @ApiModelProperty(value = "数据范围 1、全部数据权限;2、自定义数据权限;3、本部门数据权限;4、本部门及以下数据权限",required = true)
     @NotEmpty(message = "数据范围不能为空",groups = {Insert.class, Update.class})
     private String dataScope;
 
-    @ApiModelProperty(value = "角色类型 user:用户角色  app:应用角色  tenant:租户角色",required = true)
+    /**
+     * {@link cn.tr.module.sys.user.enums.RoleEnum}
+     */
+    @ApiModelProperty(value = "角色类型 sys、系统角色 custom、自定义角色",required = true)
     @NotEmpty(message = "角色类型不能为空",groups = {Insert.class, Update.class})
     private String type;
 
@@ -45,10 +53,6 @@ public class SysRoleDTO extends TenantPO {
     @NotEmpty(message = "启用状态 不能为空",groups = {Insert.class, Update.class})
     private Boolean disable;
 
-    @ApiModelProperty(value = "是否为管理员角色",readOnly = true)
-    @JsonIgnoreProperties(allowGetters = true)
-    private Boolean admin;
-
 
     @ApiModelProperty("备注")
     private String remark;

+ 13 - 2
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/dto/SysUserDTO.java

@@ -2,12 +2,17 @@ package cn.tr.module.sys.user.dto;
 
 import cn.tr.core.validation.Insert;
 import cn.tr.core.validation.Update;
+import cn.tr.module.sys.user.enums.UserStatusEnum;
+import cn.tr.plugin.mybatis.pojo.BaseDTO;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 
 import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
 import java.util.Date;
+import java.util.Set;
 
 /**
  * @ClassName : SysUserPO
@@ -16,7 +21,8 @@ import java.util.Date;
  * @Date: 2023年03月31日
  */
 @Data
-public class SysUserDTO {
+@EqualsAndHashCode(callSuper = true)
+public class SysUserDTO extends BaseDTO {
     /**
      * 用户主键Id
      */
@@ -41,6 +47,9 @@ public class SysUserDTO {
     @JsonIgnoreProperties(allowSetters = true)
     private String password;
 
+    @ApiModelProperty("角色id")
+    @NotNull(message = "角色id不能为空",groups = {Insert.class,Update.class})
+    private Set<String> roleIds;
     /**
      * 性别
      */
@@ -80,7 +89,7 @@ public class SysUserDTO {
     private String avatar;
 
     /**
-     * 用户状态
+     * {@link UserStatusEnum#getValue()}
      */
     @ApiModelProperty(value = "用户状态",required = true)
     @NotEmpty(message = "用户状态",groups = {Insert.class, Update.class})
@@ -103,4 +112,6 @@ public class SysUserDTO {
      */
     private String remark;
 
+    private String tenantId;
+
 }

+ 15 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/enums/RoleEnum.java

@@ -0,0 +1,15 @@
+package cn.tr.module.sys.user.enums;
+
+/**
+ * @ClassName : RoleEnum
+ * @Description : 角色类型
+ * @Author : LF
+ * @Date: 2023年04月05日
+ */
+
+public enum  RoleEnum {
+    //系统类型
+    sys,
+    //自定义类型
+    custom
+}

+ 6 - 11
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/po/SysRolePO.java

@@ -1,10 +1,7 @@
 package cn.tr.module.sys.user.po;
 
-import cn.tr.core.annotation.ColumnDefaultValue;
 import cn.tr.core.annotation.Comment;
 import cn.tr.plugin.mybatis.pojo.TenantPO;
-import com.baomidou.mybatisplus.annotation.FieldStrategy;
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
@@ -23,25 +20,23 @@ public class SysRolePO extends TenantPO {
     private String id;
 
     @Comment("角色编码")
-    private String roleCode;
+    private String code;
 
     @Comment("角色名称")
-    private String roleName;
+    private String name;
 
     @Comment("数据范围 1、全部数据权限;2、自定义数据权限;3、本部门数据权限;4、本部门及以下数据权限")
     private String dataScope;
 
-    @Comment("角色类型 user:用户角色  app:应用角色  tenant:租户角色")
+    /**
+     * {@link cn.tr.module.sys.user.enums.RoleEnum}
+     */
+    @Comment("角色类型 sys、系统角色 custom、自定义角色")
     private String type;
 
     @Comment("备注")
     private String remark;
 
-    @Comment("是否为管理员账号, 0、否 1、是")
-    @ColumnDefaultValue("0")
-    @TableField(updateStrategy = FieldStrategy.NEVER)
-    private Boolean admin;
-
     @Comment("排序")
     private Integer sort;
 

+ 4 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/po/SysUserPO.java

@@ -6,6 +6,7 @@ import cn.tr.module.sys.user.enums.UserStatusEnum;
 import cn.tr.plugin.mybatis.pojo.TenantPO;
 import com.baomidou.mybatisplus.annotation.*;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import org.apache.ibatis.type.JdbcType;
 import java.util.Date;
 
@@ -16,8 +17,10 @@ import java.util.Date;
  * @Date: 2023年03月31日
  */
 @Data
-@TableName("sys_user")
+@TableName(value = "sys_user",autoResultMap = true)
+@EqualsAndHashCode(callSuper = true)
 public class SysUserPO extends TenantPO {
+    private static final long serialVersionUID = -588007107794525494L;
     @Comment("用户主键Id")
     @TableId
     private String userId;

+ 46 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysMenuService.java

@@ -1,13 +1,20 @@
 package cn.tr.module.sys.user.service;
 
+import cn.hutool.core.util.StrUtil;
+import cn.tr.module.sys.mapper.user.SysMenuMapper;
+import cn.tr.module.sys.user.constant.MenuConstants;
 import cn.tr.module.sys.user.dto.SysMenuDTO;
 import cn.tr.module.sys.user.dto.SysMenuQueryDTO;
+import cn.tr.module.sys.user.enums.MenuEnum;
+import cn.tr.module.sys.user.vo.RouteItemVO;
+import cn.tr.module.sys.user.vo.RouteMetoVO;
 import cn.tr.plugin.mybatis.service.ITreeService;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * @ClassName : ISysMenuService
@@ -73,6 +80,12 @@ public interface ISysMenuService extends ITreeService {
      */
     int deleteSysMenuByIds(Collection<String> ids);
 
+    /**
+     * 查询所有菜单
+     * @return
+     */
+    public List<SysMenuDTO> findAllMenu();
+
     /**
      * 找到角色id下的所有菜单信息
      * @param roleId
@@ -87,4 +100,37 @@ public interface ISysMenuService extends ITreeService {
 
     };
 
+    default RouteItemVO convertToRoute(SysMenuDTO item) {
+        RouteItemVO node = SysMenuMapper.INSTANCE.toRouteItemVO(item);
+        node.setPath(item.getRoutePath());
+        node.setName(item.getRoutePath());
+        // 一级目录
+        if (Objects.equals(item.getMenuType(), MenuEnum.dir.name()) && (StrUtil.equals(item.getParentId(),"0")||StrUtil.isEmpty(item.getParentId()))) {
+            node.setPath("/" + item.getRoutePath());
+            node.setComponent(MenuConstants.LAYOUT);
+        }
+        // 外部链接
+        if (Objects.equals(item.getMenuType(), MenuEnum.menu.name()) && Boolean.TRUE.equals(item.getLinkExternal())) {
+            node.setComponent(MenuConstants.IFRAME);
+        }
+        RouteMetoVO routeMetoVO = new RouteMetoVO();
+        routeMetoVO.setTitle(item.getName());
+        routeMetoVO.setIcon(item.getIcon());
+        routeMetoVO.setHideMenu(!Boolean.TRUE.equals(item.getVisible()));
+        // 菜单
+        if (Objects.equals(item.getMenuType(), MenuEnum.menu.name())) {
+            routeMetoVO.setIgnoreKeepAlive(!Boolean.TRUE.equals(item.getKeepalive()));
+        }
+        // 外部链接
+        if (Objects.equals(item.getMenuType(), MenuEnum.menu.name()) && Boolean.TRUE.equals(item.getLinkExternal())) {
+            // 内嵌
+            if (Boolean.TRUE.equals(item.getFrame())) {
+                routeMetoVO.setFrameSrc(item.getLinkUrl());
+            }else {
+                node.setPath(item.getLinkUrl());
+            }
+        }
+        node.setMeta(routeMetoVO);
+        return node;
+    }
 }

+ 3 - 2
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysRoleMenuService.java

@@ -23,8 +23,9 @@ public interface ISysRoleMenuService {
 
     /**
      * 通过菜单id找到所有持有该菜单的角色
-     * @param menuId
+     * @param menuIds
+     * @param containsSys 是否包含系统角色
      * @return
      */
-    Set<String> findRoleByMenuId(String menuId);
+    Set<String> findRoleByMenuId(Collection<String> menuIds,boolean containsSys);
 }

+ 3 - 6
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysRoleService.java

@@ -1,6 +1,7 @@
 package cn.tr.module.sys.user.service;
 
 import cn.hutool.core.util.StrUtil;
+import cn.tr.core.annotation.TenantIgnore;
 import cn.tr.module.sys.user.dto.SysMenuDTO;
 import cn.tr.module.sys.user.dto.SysRoleDTO;
 import cn.tr.module.sys.user.dto.SysRoleQueryDTO;
@@ -38,12 +39,8 @@ public interface ISysRoleService {
      */
     Set<SysMenuDTO>  findUserMenus(String userId);
 
-    /**
-     * 查询用户的路由菜单
-     * @param userId 用户id
-     * @return
-     */
-    List<RouteItemVO>  findUserRouteMenus(String userId);
+    @TenantIgnore
+    public Collection<SysRoleDTO> findAllSysRoles();
 
     /**
      * 查询用户的权限

+ 7 - 0
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/ISysUserRoleService.java

@@ -27,4 +27,11 @@ public interface ISysUserRoleService {
      * @return
      */
     Set<String> findUserByRoleId(String roleId);
+
+    /**
+     * 通过用户id找到该用户所持有的所有角色id
+     * @param userId
+     * @return
+     */
+    Set<String> findRolesByUserId(String userId);
 }

+ 50 - 10
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysMenuServiceImpl.java

@@ -3,21 +3,27 @@ package cn.tr.module.sys.user.service.impl;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.tr.core.exception.ServiceException;
+import cn.tr.core.exception.TRExcCode;
 import cn.tr.module.sys.mapper.user.SysMenuMapper;
+import cn.tr.module.sys.tenant.service.ISysTenantPackageMenuService;
+import cn.tr.module.sys.tenant.service.ISysTenantService;
 import cn.tr.module.sys.user.dto.SysMenuDTO;
 import cn.tr.module.sys.user.dto.SysMenuQueryDTO;
+import cn.tr.module.sys.user.dto.SysRoleDTO;
 import cn.tr.module.sys.user.po.SysMenuPO;
 import cn.tr.module.sys.user.repository.SysMenuRepository;
 import cn.tr.module.sys.user.service.ISysMenuService;
 import cn.tr.module.sys.user.service.ISysRoleMenuService;
+import cn.tr.module.sys.user.service.ISysRoleService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+import java.util.stream.Stream;
 
 /**
  * @ClassName : SysMenuServiceImpl
@@ -32,6 +38,19 @@ public class SysMenuServiceImpl implements ISysMenuService {
     @Autowired
     @Lazy
     private ISysRoleMenuService roleMenuService;
+
+    @Autowired
+    @Lazy
+    private ISysTenantPackageMenuService tenantPackageMenuService;
+
+    @Autowired
+    @Lazy
+    private ISysTenantService tenantService;
+
+    @Autowired
+    @Lazy
+    private ISysRoleService roleService;
+
     @Autowired
     @Lazy
     private ISysMenuService self;
@@ -39,7 +58,8 @@ public class SysMenuServiceImpl implements ISysMenuService {
     public List<SysMenuDTO> selectSysMenuList(SysMenuQueryDTO query) {
         return SysMenuMapper.INSTANCE.toSysMenuDTOList(menuRepository.selectList(new LambdaQueryWrapper<SysMenuPO>()
                 .like(StrUtil.isNotEmpty(query.getName()),SysMenuPO::getName,query.getName())
-                .eq(ObjectUtil.isNotNull(query.getDisable()),SysMenuPO::getDisable,query.getDisable()))
+                .eq(ObjectUtil.isNotNull(query.getDisable()),SysMenuPO::getDisable,query.getDisable())
+                .eq(ObjectUtil.isNotNull(query.getParentId()),SysMenuPO::getParentId,query.getParentId()))
         );
     }
 
@@ -49,32 +69,52 @@ public class SysMenuServiceImpl implements ISysMenuService {
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public boolean updateSysMenuById(SysMenuDTO source) {
-        Set<String> roleIds = roleMenuService.findRoleByMenuId(source.getId());
+        Set<String> roleIds = roleMenuService.findRoleByMenuId(Collections.singleton(source.getId()),true);
         roleIds.parallelStream().forEach(self::delRoleMenusCache);
+        Set<String> packageIds = tenantPackageMenuService.findPackageIdByMenuId(Collections.singleton(source.getId()),true);
+        packageIds.parallelStream().forEach(tenantPackageMenuService::delCacheMenuIdByPackageId);
         return menuRepository.updateById(SysMenuMapper.INSTANCE.toSysMenuPO(source))!=0;
     }
+
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public boolean insertSysMenu(SysMenuDTO source) {
         return menuRepository.insert(SysMenuMapper.INSTANCE.toSysMenuPO(source))!=0;
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public int deleteSysMenuByIds(Collection<String> ids) {
         List<SysMenuPO> menus = menuRepository.selectBatchIds(ids);
         if(CollectionUtil.isEmpty(menus)){
             return CollectionUtil.size(ids);
         }
-        menus.parallelStream()
-                .map(SysMenuPO::getId)
-                .map(roleMenuService::findRoleByMenuId)
-                .flatMap(Collection::stream)
-                .forEach(self::delRoleMenusCache);
+        //删除相应缓存
+        Set<String> roleIds = roleMenuService.findRoleByMenuId(ids,false);
+        if(CollectionUtil.isNotEmpty(roleIds)){
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"所选菜单已与角色相关联,请取消关联后再删除菜单");
+        }
+        Set<String> packageIds = tenantPackageMenuService.findPackageIdByMenuId(ids,false);
+        if(CollectionUtil.isNotEmpty(packageIds)){
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"所选菜单已与租户套餐相关联,请取消关联后再删除菜单");
+        }
         return menuRepository.deleteBatchIds(ids);
     }
 
+    @Override
+    public List<SysMenuDTO> findAllMenu() {
+        return SysMenuMapper.INSTANCE.toSysMenuDTOList(menuRepository.selectList(new LambdaQueryWrapper<>()));
+    }
+
     @Override
     public List<SysMenuDTO> findAllMenuByRoleId(String roleId) {
+        SysRoleDTO roleDTO = roleService.selectSysRoleById(roleId);
+        if(StrUtil.equals(roleDTO.getType(),"sys")){
+            //当前租户的所有菜单
+            return new ArrayList<>(tenantService.currentTenantMenus());
+        }
         return SysMenuMapper.INSTANCE.toSysMenuDTOList(menuRepository.findAllMenuByRoleId(roleId));
     }
 }

+ 16 - 13
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysRoleMenuServiceImpl.java

@@ -1,22 +1,20 @@
 package cn.tr.module.sys.user.service.impl;
 
-import cn.hutool.core.collection.CollectionUtil;
 import cn.tr.module.sys.mapper.user.SysRoleMenuMapper;
+import cn.tr.module.sys.user.dto.SysRoleDTO;
 import cn.tr.module.sys.user.dto.SysRoleMenuDTO;
-import cn.tr.module.sys.user.po.SysMenuPO;
 import cn.tr.module.sys.user.po.SysRoleMenuPO;
 import cn.tr.module.sys.user.repository.SysRoleMenuRepository;
 import cn.tr.module.sys.user.service.ISysMenuService;
 import cn.tr.module.sys.user.service.ISysRoleMenuService;
+import cn.tr.module.sys.user.service.ISysRoleService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -29,7 +27,7 @@ import java.util.stream.Collectors;
 @AllArgsConstructor
 public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuRepository,SysRoleMenuPO> implements ISysRoleMenuService {
     private final ISysMenuService menuService;
-
+    private final ISysRoleService roleService;
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void assignRoleMenu(List<SysRoleMenuDTO> source) {
@@ -40,16 +38,21 @@ public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuRepository,Sy
             baseMapper.delete(new LambdaQueryWrapper<SysRoleMenuPO>().eq(SysRoleMenuPO::getRoleId,k));
             //清除角色缓存
             menuService.delRoleMenusCache(k);
-            if(CollectionUtil.isEmpty(relations)){
-                return;
-            }
-            saveBatch(relations);
         });
+        saveBatch(roleMenus);
     }
 
     @Override
-    public Set<String> findRoleByMenuId(String menuId) {
-        List<SysRoleMenuPO> roleMenus = this.list(new LambdaQueryWrapper<SysRoleMenuPO>().eq(SysRoleMenuPO::getMenuId, menuId));
-        return roleMenus.stream().map(SysRoleMenuPO::getRoleId).collect(Collectors.toSet());
+    public Set<String> findRoleByMenuId(Collection<String > menuIds,boolean containsSys) {
+        List<SysRoleMenuPO> roleMenus = this.list(new LambdaQueryWrapper<SysRoleMenuPO>().in(SysRoleMenuPO::getMenuId, menuIds));
+        Set<String> sysRoleIds=new HashSet<>();
+        if(containsSys){
+            sysRoleIds = roleService.findAllSysRoles().stream()
+                    .map(SysRoleDTO::getId)
+                    .collect(Collectors.toSet());
+        }
+        Set<String> roleIds = roleMenus.stream().map(SysRoleMenuPO::getRoleId).collect(Collectors.toSet());
+        sysRoleIds.addAll(roleIds);
+        return sysRoleIds;
     }
 }

+ 36 - 66
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysRoleServiceImpl.java

@@ -5,20 +5,17 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.tr.core.exception.ServiceException;
 import cn.tr.core.exception.TRExcCode;
-import cn.tr.module.sys.mapper.user.SysMenuMapper;
 import cn.tr.module.sys.mapper.user.SysRoleMapper;
-import cn.tr.module.sys.user.constant.MenuConstants;
+import cn.tr.module.sys.tenant.service.ISysTenantService;
 import cn.tr.module.sys.user.dto.SysMenuDTO;
 import cn.tr.module.sys.user.dto.SysRoleDTO;
 import cn.tr.module.sys.user.dto.SysRoleQueryDTO;
-import cn.tr.module.sys.user.enums.MenuEnum;
+import cn.tr.module.sys.user.enums.RoleEnum;
 import cn.tr.module.sys.user.po.SysRolePO;
 import cn.tr.module.sys.user.repository.SysRoleRepository;
 import cn.tr.module.sys.user.service.ISysMenuService;
 import cn.tr.module.sys.user.service.ISysRoleService;
 import cn.tr.module.sys.user.service.ISysUserRoleService;
-import cn.tr.module.sys.user.vo.RouteItemVO;
-import cn.tr.module.sys.user.vo.RouteMetoVO;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
@@ -46,21 +43,31 @@ public class SysRoleServiceImpl implements ISysRoleService {
     @Lazy
     private ISysUserRoleService userRoleService;
 
+    @Autowired
+    @Lazy
+    private ISysTenantService tenantService;
+
     @Autowired
     @Lazy
     private ISysRoleService self;
     @Override
     public List<SysRoleDTO> selectSysRoleList(SysRoleQueryDTO query) {
         return SysRoleMapper.INSTANCE.toSysRoleDTOList(roleRepository.selectList(new LambdaQueryWrapper<SysRolePO>()
-                .like(StrUtil.isNotEmpty(query.getRoleCode()),SysRolePO::getRoleCode,query.getRoleCode())
-                .like(StrUtil.isNotEmpty(query.getRoleName()),SysRolePO::getRoleName,query.getRoleName()).eq(SysRolePO::getType,query.getType()
-                ).like(ObjectUtil.isNotNull(query.getDisable()),SysRolePO::getDisable,query.getDisable())
+                .like(StrUtil.isNotEmpty(query.getRoleCode()),SysRolePO::getCode,query.getRoleCode())
+                .like(StrUtil.isNotEmpty(query.getRoleName()),SysRolePO::getName,query.getRoleName())
+                .like(ObjectUtil.isNotNull(query.getDisable()),SysRolePO::getDisable,query.getDisable())
         ));
     }
 
     @Override
     public Set<SysMenuDTO>  findUserMenus(String userId){
         List<SysRoleDTO> roles = self.findAllRolesByUserId(userId);
+        //判断是否存在管理员账户
+        for (SysRoleDTO role : roles) {
+            if(StrUtil.equals(role.getType(),"sys")){
+                return tenantService.currentTenantMenus();
+            }
+        }
         if (CollectionUtil.isEmpty(roles)) {
             return new HashSet<>();
         }
@@ -75,12 +82,9 @@ public class SysRoleServiceImpl implements ISysRoleService {
     }
 
     @Override
-    public List<RouteItemVO> findUserRouteMenus(String userId) {
-        Set<SysMenuDTO> userMenus = findUserMenus(userId);
-        return userMenus.stream()
-                .filter(menu->!StrUtil.equals(MenuEnum.button.name(),menu.getMenuType()))
-                .map(this::convertToRoute)
-                .collect(Collectors.toList());
+    public Collection<SysRoleDTO> findAllSysRoles() {
+        return SysRoleMapper.INSTANCE.toSysRoleDTOList( roleRepository.selectList(new LambdaQueryWrapper<SysRolePO>()
+                .eq(SysRolePO::getType,"sys")));
     }
 
     ;
@@ -110,18 +114,18 @@ public class SysRoleServiceImpl implements ISysRoleService {
     @Override
     public int deleteSysRoleByIds(Collection<String> ids) {
         List<SysRolePO> roles = roleRepository.selectBatchIds(ids);
+        Optional<SysRolePO> adminRole = roles.stream()
+                .filter(po->StrUtil.equals(po.getType(), RoleEnum.sys.name()))
+                .findFirst();
+        if(adminRole.isPresent()){
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"无法对系统角色进行操作");
+        }
         for (SysRolePO role : roles) {
             Set<String> userIds = userRoleService.findUserByRoleId(role.getId());
             if(CollectionUtil.isNotEmpty(userIds)){
-                throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("角色编码{%s}已被使用,无法删除",role.getRoleCode()));
+                throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("角色编码{%s}已被使用,无法删除",role.getCode()));
             }
         }
-        Optional<SysRolePO> adminRole = roles.stream()
-                .filter(po->Boolean.TRUE.equals(po.getAdmin()))
-                .findFirst();
-        if(adminRole.isPresent()){
-            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"管理员角色无法删除");
-        }
         return roleRepository.deleteBatchIds(ids);
     }
 
@@ -132,56 +136,22 @@ public class SysRoleServiceImpl implements ISysRoleService {
 
     private void validateRoleSource(SysRolePO source){
         SysRolePO role = roleRepository.selectOne(new LambdaQueryWrapper<SysRolePO>()
-                .eq(SysRolePO::getRoleName, source.getRoleName())
-                .eq(SysRolePO::getRoleCode, source.getRoleCode())
+                .eq(SysRolePO::getCode, source.getCode())
                 .last("limit 1"));
         if(role!=null&& StrUtil.equals(role.getId(),source.getId())){
-            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("角色编码{%s}或角色名称{%s}不能重复",source.getRoleCode(),source.getRoleName()));
-        }
-        if(Boolean.TRUE.equals(source.getAdmin())){
-            role = roleRepository.selectOne(new LambdaQueryWrapper<SysRolePO>()
-                    .eq(SysRolePO::getAdmin, true)
-                    .last("limit 1"));
-            if(role!=null&& StrUtil.equals(role.getId(),source.getId())){
-                throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"管理员角色已存在,请勿重复添加");
-            }else if(role!=null){
-                throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"管理员角色无法操作");
-            }
-        }
-    }
-
-
-    private RouteItemVO convertToRoute(SysMenuDTO item) {
-        RouteItemVO node = SysMenuMapper.INSTANCE.toRouteItemVO(item);
-        node.setPath(item.getRoutePath());
-        node.setName(item.getRoutePath());
-        // 一级目录
-        if (Objects.equals(item.getMenuType(), MenuEnum.dir.name()) && (StrUtil.equals(item.getParentId(),"0")||StrUtil.isEmpty(item.getParentId()))) {
-            node.setPath("/" + item.getRoutePath());
-            node.setComponent(MenuConstants.LAYOUT);
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("角色编码{%s}不能重复",source.getCode()));
         }
-        // 外部链接
-        if (Objects.equals(item.getMenuType(), MenuEnum.menu.name()) && Boolean.TRUE.equals(item.getLinkExternal())) {
-            node.setComponent(MenuConstants.IFRAME);
+        if(StrUtil.equals(source.getType(), RoleEnum.sys.name())){
+            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"无法对系统角色进行操作");
         }
-        RouteMetoVO routeMetoVO = new RouteMetoVO();
-        routeMetoVO.setTitle(item.getName());
-        routeMetoVO.setIcon(item.getIcon());
-        routeMetoVO.setHideMenu(!Boolean.TRUE.equals(item.getVisible()));
-        // 菜单
-        if (Objects.equals(item.getMenuType(), MenuEnum.menu.name())) {
-            routeMetoVO.setIgnoreKeepAlive(!Boolean.TRUE.equals(item.getKeepalive()));
-        }
-        // 外部链接
-        if (Objects.equals(item.getMenuType(), MenuEnum.menu.name()) && Boolean.TRUE.equals(item.getLinkExternal())) {
-            // 内嵌
-            if (Boolean.TRUE.equals(item.getFrame())) {
-                routeMetoVO.setFrameSrc(item.getLinkUrl());
-            }else {
-                node.setPath(item.getLinkUrl());
+        if(StrUtil.isNotEmpty(source.getId())){
+            SysRolePO sysRolePO = roleRepository.selectById(source.getId());
+            if(sysRolePO==null){
+                return;
+            }
+            if(StrUtil.equals(sysRolePO.getType(), RoleEnum.sys.name())){
+                throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,"无法对系统角色进行操作");
             }
         }
-        node.setMeta(routeMetoVO);
-        return node;
     }
 }

+ 6 - 1
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysUserRoleServiceImpl.java

@@ -9,7 +9,6 @@ import cn.tr.module.sys.user.service.ISysRoleService;
 import cn.tr.module.sys.user.service.ISysUserRoleService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import lombok.AllArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -52,4 +51,10 @@ public class SysUserRoleServiceImpl extends ServiceImpl<SysUserRoleRepository, S
         List<SysUserRolePO> userRoles = this.list(new LambdaQueryWrapper<SysUserRolePO>().eq(SysUserRolePO::getRoleId, roleId));
         return userRoles.stream().map(SysUserRolePO::getUserId).collect(Collectors.toSet());
     }
+
+    @Override
+    public Set<String> findRolesByUserId(String userId) {
+        List<SysUserRolePO> userRoles = this.list(new LambdaQueryWrapper<SysUserRolePO>().eq(SysUserRolePO::getUserId, userId));
+        return userRoles.stream().map(SysUserRolePO::getRoleId).collect(Collectors.toSet());
+    }
 }

+ 48 - 10
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/service/impl/SysUserServiceImpl.java

@@ -8,6 +8,8 @@ import cn.tr.core.utils.PswUtils;
 import cn.tr.module.sys.mapper.user.SysUserMapper;
 import cn.tr.module.sys.user.dto.SysUserDTO;
 import cn.tr.module.sys.user.dto.SysUserQueryDTO;
+import cn.tr.module.sys.user.dto.SysUserRoleDTO;
+import cn.tr.module.sys.user.service.ISysUserRoleService;
 import cn.tr.module.sys.user.service.ISysUserService;
 import cn.tr.module.sys.user.po.SysUserPO;
 import cn.tr.module.sys.user.repository.SysUserRepository;
@@ -19,6 +21,8 @@ import org.springframework.transaction.annotation.Transactional;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * @ClassName : SysUserServiceImpl
@@ -34,7 +38,9 @@ public class SysUserServiceImpl implements ISysUserService {
     @Autowired
     @Lazy
     private ISysUserService userService;
-
+    @Autowired
+    @Lazy
+    private ISysUserRoleService userRoleService;
     @Override
     public List<SysUserDTO> selectSysUserList(SysUserQueryDTO query) {
         return null;
@@ -42,13 +48,22 @@ public class SysUserServiceImpl implements ISysUserService {
 
     @Override
     public SysUserDTO selectSysUserById(String id) {
-        return SysUserMapper.INSTANCE.toUserDTO(userRepository.selectById(id));
+        SysUserDTO result = SysUserMapper.INSTANCE.toUserDTO(userRepository.selectById(id));
+        if(result==null){
+            return result;
+        }
+        result.setRoleIds(userRoleService.findRolesByUserId(id));
+        return result;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean updateSysUserById(SysUserDTO source) {
-        return userRepository.updateById(SysUserMapper.INSTANCE.toUserPO(source))!=0;
+        boolean result=userRepository.updateById(SysUserMapper.INSTANCE.toUserPO(source))!=0;
+        if(result){
+            assignUserRole(source.getUserId(),source.getRoleIds());
+        }
+        return result;
     }
 
     @Override
@@ -73,12 +88,15 @@ public class SysUserServiceImpl implements ISysUserService {
         SysUserPO updateUser = new SysUserPO();
         updateUser.setUserId(user.getUserId());
         updateUser.setPassword(PswUtils.encryptPassword(newPsw));
-        return userRepository.updateById(updateUser)!=0;
+        return  userRepository.updateById(updateUser) != 0;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean insertSysUser(SysUserDTO source) {
+        boolean result=false;
+        SysUserPO insertUser = SysUserMapper.INSTANCE.toUserPO(source);
+        insertUser.setPassword(PswUtils.encryptPassword(insertUser.getPassword()));
         //所有租户下的正常使用的账号
         List<SysUserDTO> allUsingUser = userService.selectUserByUsernameIgnoreTenant(source.getUsername());
         if(CollectionUtil.isNotEmpty(allUsingUser)){
@@ -87,14 +105,19 @@ public class SysUserServiceImpl implements ISysUserService {
         //当前租户下的账号
         SysUserPO delUser = userRepository.selectUserByUsernameIgnoreDel(source.getUsername());
         if(delUser==null){
-            return userRepository.insert(delUser)!=0;
-        }
-        if(Boolean.TRUE.equals(delUser.getDeleted())){
-            userRepository.recoverBatch(Arrays.asList(delUser));
-            return userRepository.updateById(delUser)!=0;
+            result= userRepository.insert(insertUser)!=0;
         }else {
-            throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("当前租户中已存在账户{%s},不可重复添加",source.getUsername()));
+            if(Boolean.TRUE.equals(delUser.getDeleted())){
+                userRepository.recoverBatch(Arrays.asList(delUser));
+                result= userRepository.updateById(insertUser)!=0;
+            }else {
+                throw new ServiceException(TRExcCode.SYSTEM_ERROR_B0001,String.format("当前租户中已存在账户{%s},不可重复添加",source.getUsername()));
+            }
         }
+        if(result){
+            assignUserRole(source.getUserId(),source.getRoleIds());
+        }
+        return result;
     }
 
     @Override
@@ -109,4 +132,19 @@ public class SysUserServiceImpl implements ISysUserService {
     public int deleteSysUserByIds(Collection<String> ids) {
         return userRepository.deleteBatchIds(ids);
     }
+
+
+    private void assignUserRole(String userId, Set<String > roleIds){
+        if(CollectionUtil.isEmpty(roleIds)){
+            return;
+        }
+        List<SysUserRoleDTO> userRoles = roleIds.stream()
+                .map(roleId -> {
+                    SysUserRoleDTO userRole = new SysUserRoleDTO();
+                    userRole.setRoleId(roleId);
+                    userRole.setUserId(userId);
+                    return userRole;
+                }).collect(Collectors.toList());
+        userRoleService.assignUserRole(userRoles);
+    }
 }

+ 0 - 5
tr-modules/tr-module-system/src/main/java/cn/tr/module/sys/user/vo/RouteItemVO.java

@@ -31,9 +31,4 @@ public class RouteItemVO extends TreeNode<String> {
     private String name;
 
     private String redirect;
-    @Override
-    @JsonIgnore
-    public String getParentId() {
-        return super.getParentId();
-    }
 }

+ 2 - 2
tr-modules/tr-module-system/src/main/resources/mapper/user/SysMenuMapper.xml

@@ -8,8 +8,8 @@
     <select id="findAllMenuByRoleId" resultType="cn.tr.module.sys.user.po.SysMenuPO" parameterType="java.lang.String">
             select m.*
             from (select * from sys_role where id=#{roleId}) as r
-            left join sys_role_menu  as rm on rm.role_id=r.id
-            left join sys_menu as m on m.id = rm.menu_id
+            join sys_role_menu  as rm on rm.role_id=r.id
+            join sys_menu as m on m.id = rm.menu_id
     </select>
 
 

+ 2 - 2
tr-modules/tr-module-system/src/main/resources/mapper/user/SysRoleMapper.xml

@@ -8,8 +8,8 @@
     <select id="findAllRoleByUserId" resultType="cn.tr.module.sys.user.po.SysRolePO">
          select r.*
             from (select * from sys_user where user_id=#{userId}) as u
-            left join sys_user_role  as ur on ur.user_id=u.user_id
-            left join sys_role as r on r.id = ur.role_id
+            join sys_user_role  as ur on ur.user_id=u.user_id
+            join sys_role as r on r.id = ur.role_id
     </select>
 
 

+ 7 - 0
tr-plugins/tr-spring-boot-starter-plugin-biz-tenant/src/main/java/cn/tr/plugin/biz/tenant/TrTenantAutoConfiguration.java

@@ -1,6 +1,7 @@
 package cn.tr.plugin.biz.tenant;
 
 import cn.tr.core.enums.WebFilterOrderEnum;
+import cn.tr.plugin.biz.tenant.config.aop.TenantCacheAspect;
 import cn.tr.plugin.biz.tenant.config.aop.TenantIgnoreAspect;
 import cn.tr.plugin.biz.tenant.config.db.TenantCreateAndUpdateMetaObjectHandler;
 import cn.tr.plugin.biz.tenant.config.db.TenantDatabaseInterceptor;
@@ -14,6 +15,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.cache.CacheManager;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.context.annotation.Primary;
@@ -83,4 +85,9 @@ public class TrTenantAutoConfiguration implements BeanPostProcessor {
         return new TenantIgnoreUrlConfig(handlerMappings,tenantProperties);
     }
 
+    // ========== 缓存 ==========
+    @Bean
+    public TenantCacheAspect tenantCacheAspect(){
+        return new TenantCacheAspect();
+    }
 }

+ 16 - 0
tr-plugins/tr-spring-boot-starter-plugin-biz-tenant/src/main/java/cn/tr/plugin/biz/tenant/config/aop/CacheIgnoreTenant.java

@@ -0,0 +1,16 @@
+package cn.tr.plugin.biz.tenant.config.aop;
+
+import java.lang.annotation.*;
+
+/**
+ * @Interface : CacheIgnoreTenant
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月07日
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface CacheIgnoreTenant {
+}

+ 41 - 0
tr-plugins/tr-spring-boot-starter-plugin-biz-tenant/src/main/java/cn/tr/plugin/biz/tenant/config/aop/TenantCacheAspect.java

@@ -0,0 +1,41 @@
+package cn.tr.plugin.biz.tenant.config.aop;
+
+import cn.hutool.core.util.StrUtil;
+import cn.tr.core.annotation.TenantIgnore;
+import cn.tr.plugin.biz.tenant.context.TenantContextHolder;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+
+import java.util.stream.Stream;
+
+/**
+ * @ClassName : TenantCacheAspect
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年04月06日
+ */
+@Aspect
+public class TenantCacheAspect {
+    @Around("getCachePointcut()")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        Object name = joinPoint.getArgs()[0];
+        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+        CacheIgnoreTenant cacheIgnoreTenant = methodSignature.getMethod().getAnnotation(CacheIgnoreTenant.class);
+        TenantIgnore tenantIgnore = methodSignature.getMethod().getAnnotation(TenantIgnore.class);
+        if(cacheIgnoreTenant !=null&&tenantIgnore!=null){
+            String cacheName = TenantContextHolder.getTenantId() + StrUtil.COLON + name;
+            return joinPoint.proceed(Stream.of(cacheName).toArray());
+        }
+
+
+        return joinPoint.proceed();
+    }
+
+    //拦截指定的方法
+    @Pointcut("execution(* org.springframework.cache.CacheManager.getCache(..))")
+    private void getCachePointcut() {
+    }
+}

+ 30 - 0
tr-plugins/tr-spring-boot-starter-plugin-mybatis/src/main/java/cn/tr/plugin/mybatis/pojo/BaseDTO.java

@@ -0,0 +1,30 @@
+package cn.tr.plugin.mybatis.pojo;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @ClassName : BasePO
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年03月03日
+ */
+@Data
+public class BaseDTO implements Serializable {
+    private static final long serialVersionUID = -4315985598485514817L;
+
+    /**
+     * 创建时间
+     */
+    @JsonIgnoreProperties(allowGetters = true)
+    private Date createTime;
+
+    /**
+     * 最后更新时间
+     */
+    @JsonIgnoreProperties(allowGetters = true)
+    private Date updateTime;
+}

+ 2 - 2
tr-plugins/tr-spring-boot-starter-plugin-mybatis/src/main/java/cn/tr/plugin/mybatis/pojo/BasePO.java

@@ -41,7 +41,7 @@ public class BasePO implements Serializable {
      */
     @Comment("创建人")
     @TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
-    private String create_by;
+    private String createBy;
     /**
      * 更新者,目前使用 SysUser 的 id 编号
      *
@@ -49,7 +49,7 @@ public class BasePO implements Serializable {
      */
     @Comment("更新人")
     @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR)
-    private String update_by;
+    private String updateBy;
 
 //    /**
 //     * 是否删除

+ 4 - 2
tr-plugins/tr-spring-boot-starter-plugin-mybatis/src/main/java/cn/tr/plugin/mybatis/pojo/TenantPO.java

@@ -2,7 +2,9 @@ package cn.tr.plugin.mybatis.pojo;
 
 import cn.tr.core.annotation.Comment;
 import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Data;
 import org.apache.ibatis.type.JdbcType;
 
 /**
@@ -11,9 +13,9 @@ import org.apache.ibatis.type.JdbcType;
  * @Author : LF
  * @Date: 2023年04月01日
  */
-
+@Data
 public class TenantPO extends BasePO {
     @Comment("租户id")
-    @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.NEVER,fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
     private String tenantId;
 }

+ 0 - 65
tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/bo/LoginUserBO.java

@@ -1,65 +0,0 @@
-package cn.tr.plugin.security.bo;
-
-import cn.hutool.core.map.MapUtil;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @ClassName : LoginUserBO
- * @Description :
- * @Author : LF
- * @Date: 2023年03月02日
- */
-@Data
-@NoArgsConstructor
-public class LoginUserBO implements Serializable {
-
-    private static final long serialVersionUID = 8753311972940702858L;
-
-    private String userId;
-
-    private String username;
-
-    /**
-     * 租户id
-     */
-    private String tenantId;
-
-    /**
-     * 授权范围
-     */
-    private List<String> scopes;
-
-    // ========== 上下文 ==========
-    /**
-     * 上下文字段,不进行持久化
-     *
-     * 1. 用于基于 LoginUser 维度的临时缓存
-     */
-    @JsonIgnore
-    private Map<String, Object> context;
-
-    public LoginUserBO(String userId, String username, String tenantId) {
-        this.userId = userId;
-        this.username = username;
-        this.tenantId = tenantId;
-    }
-
-    public void setContext(String key, Object value) {
-        if (context == null) {
-            context = new HashMap<>();
-        }
-        context.put(key, value);
-    }
-
-    public <T> T getContext(String key, Class<T> type) {
-        return MapUtil.get(context, key, type);
-    }
-}

+ 35 - 0
tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/bo/UserLoginInfoBO.java

@@ -1,8 +1,10 @@
 package cn.tr.plugin.security.bo;
 
+import lombok.Builder;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.util.Date;
 
 /**
  * @ClassName : SysLoginUserInfoDTO
@@ -11,6 +13,7 @@ import java.io.Serializable;
  * @Date: 2023年03月31日
  */
 @Data
+@Builder
 public class UserLoginInfoBO implements Serializable {
     private static final long serialVersionUID = 2738015085612369615L;
     /**
@@ -23,6 +26,17 @@ public class UserLoginInfoBO implements Serializable {
      */
     private String username;
 
+    /**
+     * 用户名
+     */
+    private String token;
+
+
+    /**
+     * 密码
+     */
+    private String loginType;
+
     /**
      * 密码
      */
@@ -48,6 +62,27 @@ public class UserLoginInfoBO implements Serializable {
      */
     private String loginIp;
 
+    /**
+     * 登录地址
+     */
+    private String loginLocation;
+
+    /**
+     * 登录地址
+     */
+    private String browser;
+
+
+    /**
+     * 登录地址
+     */
+    private Date loginTime;
+
+    /**
+     * 登录地址
+     */
+    private String os;
+
     /**
      * 租户id
      */

+ 2 - 0
tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/constant/SecurityConstant.java

@@ -13,6 +13,8 @@ public class SecurityConstant {
      */
     public static final String LOGIN_USER="login_user";
 
+    public static final String SESSION_USER="session_user";
+
     /**
      * 登录类型
      */

+ 0 - 1
tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/context/LoginUserContextHolder.java

@@ -1,7 +1,6 @@
 package cn.tr.plugin.security.context;
 
 import cn.tr.core.context.SecurityContextHolder;
-import cn.tr.plugin.security.bo.LoginUserBO;
 import cn.tr.plugin.security.bo.UserLoginInfoBO;
 import cn.tr.plugin.security.constant.SecurityConstant;
 

+ 5 - 1
tr-plugins/tr-spring-boot-starter-plugin-satoken/src/main/java/cn/tr/plugin/security/utils/SaTokenUtils.java

@@ -20,7 +20,11 @@ public class SaTokenUtils {
      * @return {@link StpLogic}
      */
     public StpLogic getStpUtil(){
-        return SaManager.getStpLogic(LoginUserContextHolder.getStpType());
+        return getStpUtil(LoginUserContextHolder.getStpType());
+    }
+
+    public StpLogic getStpUtil(String loginType){
+        return SaManager.getStpLogic(loginType);
     }
 
     /**

+ 3 - 2
tr-plugins/tr-spring-boot-starter-plugin-web/src/main/java/cn/tr/plugin/web/TrWebAutoConfiguration.java

@@ -9,6 +9,7 @@ import cn.tr.plugin.web.filter.ExceptionFilter;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.web.servlet.HandlerInterceptor;
@@ -35,8 +36,8 @@ public class TrWebAutoConfiguration implements BeanPostProcessor {
      * @return
      */
     @Bean
-    public WebMvcConfigurer trWebMvcConfig(ObjectMapper objectMapper, List<HandlerInterceptor> interceptors){
-        return new TrWebMvcConfig(objectMapper,interceptors);
+    public WebMvcConfigurer trWebMvcConfig(ObjectMapper objectMapper, List<HandlerInterceptor> interceptors, JacksonProperties jacksonProperties){
+        return new TrWebMvcConfig(objectMapper,interceptors,jacksonProperties);
     }
 
     @Bean(initMethod = "init")

+ 6 - 1
tr-plugins/tr-spring-boot-starter-plugin-web/src/main/java/cn/tr/plugin/web/config/TrWebMvcConfig.java

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.*;
 import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@@ -12,6 +13,7 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 import java.io.IOException;
+import java.text.SimpleDateFormat;
 import java.util.List;
 import java.util.TimeZone;
 
@@ -25,10 +27,12 @@ public class TrWebMvcConfig implements WebMvcConfigurer {
     private ObjectMapper objectMapper;
 
     private List<HandlerInterceptor> interceptors;
+    private JacksonProperties jacksonProperties;
 
-    public TrWebMvcConfig(ObjectMapper objectMapper, List<HandlerInterceptor> interceptors) {
+    public TrWebMvcConfig(ObjectMapper objectMapper, List<HandlerInterceptor> interceptors, JacksonProperties jacksonProperties) {
         this.objectMapper = objectMapper;
         this.interceptors = interceptors;
+        this.jacksonProperties=jacksonProperties;
     }
 
     @Bean
@@ -46,6 +50,7 @@ public class TrWebMvcConfig implements WebMvcConfigurer {
                 gen.writeNull();
             }
         });
+        objectMapper.setDateFormat(new SimpleDateFormat(jacksonProperties.getDateFormat()));
         converter.setObjectMapper(objectMapper);
         converters.add(0, converter);
     }

+ 9 - 2
tr-test/src/main/resources/application.yml

@@ -45,6 +45,8 @@ spring:
     type: caffeine
   profiles:
     include: doc
+  jackson:
+    date-format: yyyy-mm-dd HH:mm:ss
 
 
 tr:
@@ -67,11 +69,16 @@ tr:
       - /oauth2/psw/**
   tenant:
     ignore-tables:
-      - sys_menu
+      - gen_config
       - gen_basic
+      - sys_menu
       - sys_dict
       - sys_dict_item
-      - gen_config
+      - sys_tenant
+      - sys_tenant_package
+      - sys_tenant_package_menu
+      - sys_role_menu
+      - sys_user_role
 
 sa-token:
   is-read-header: true