Prechádzať zdrojové kódy

新增项目管理业务子模块

longping 4 mesiacov pred
rodič
commit
f2cd5c9652
100 zmenil súbory, kde vykonal 8512 pridanie a 0 odobranie
  1. 105 0
      ruoyi-modules/ruoyi-subject/pom.xml
  2. 55 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/monitor/CacheController.java
  3. 89 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/monitor/SysLogininforController.java
  4. 75 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/monitor/SysOperlogController.java
  5. 130 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/monitor/SysUserOnlineController.java
  6. 115 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysClientController.java
  7. 137 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysConfigController.java
  8. 140 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysDeptController.java
  9. 123 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysDictDataController.java
  10. 125 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysDictTypeController.java
  11. 174 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysMenuController.java
  12. 90 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysNoticeController.java
  13. 105 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysOssConfigController.java
  14. 108 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysOssController.java
  15. 133 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysPostController.java
  16. 132 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysProfileController.java
  17. 229 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysRoleController.java
  18. 38 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysSocialController.java
  19. 193 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysTenantController.java
  20. 142 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysTenantPackageController.java
  21. 302 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysUserController.java
  22. 47 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysCache.java
  23. 77 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysClient.java
  24. 51 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysConfig.java
  25. 83 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysDept.java
  26. 71 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysDictData.java
  27. 41 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysDictType.java
  28. 85 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysLogininfor.java
  29. 191 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysMenu.java
  30. 51 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysNotice.java
  31. 115 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysOperLog.java
  32. 50 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysOss.java
  33. 89 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysOssConfig.java
  34. 61 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysPost.java
  35. 79 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysRole.java
  36. 29 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysRoleDept.java
  37. 29 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysRoleMenu.java
  38. 136 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysSocial.java
  39. 103 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysTenant.java
  40. 54 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysTenantPackage.java
  41. 115 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysUser.java
  42. 63 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysUserOnline.java
  43. 29 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysUserPost.java
  44. 29 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysUserRole.java
  45. 80 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysClientBo.java
  46. 59 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysConfigBo.java
  47. 76 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysDeptBo.java
  48. 80 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysDictDataBo.java
  49. 50 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysDictTypeBo.java
  50. 87 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysLogininforBo.java
  51. 110 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysMenuBo.java
  52. 61 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysNoticeBo.java
  53. 127 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysOperLogBo.java
  54. 49 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysOssBo.java
  55. 109 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysOssConfigBo.java
  56. 75 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysPostBo.java
  57. 94 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysRoleBo.java
  58. 142 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysSocialBo.java
  59. 114 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysTenantBo.java
  60. 59 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysTenantPackageBo.java
  61. 119 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysUserBo.java
  62. 29 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysUserPasswordBo.java
  63. 53 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysUserProfileBo.java
  64. 18 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/AvatarVo.java
  65. 23 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/CacheListInfoVo.java
  66. 26 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/DeptTreeSelectVo.java
  67. 26 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/MenuTreeSelectVo.java
  68. 61 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/MetaVo.java
  69. 29 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/ProfileVo.java
  70. 62 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/RouterVo.java
  71. 90 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysClientVo.java
  72. 72 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysConfigVo.java
  73. 102 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysDeptVo.java
  74. 88 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysDictDataVo.java
  75. 59 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysDictTypeVo.java
  76. 106 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysLogininforVo.java
  77. 116 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysMenuVo.java
  78. 73 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysNoticeVo.java
  79. 144 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysOperLogVo.java
  80. 97 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysOssConfigVo.java
  81. 28 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysOssUploadVo.java
  82. 72 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysOssVo.java
  83. 91 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysPostVo.java
  84. 100 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysRoleVo.java
  85. 144 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysSocialVo.java
  86. 66 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysTenantPackageVo.java
  87. 115 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysTenantVo.java
  88. 96 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysUserExportVo.java
  89. 76 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysUserImportVo.java
  90. 40 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysUserInfoVo.java
  91. 142 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysUserVo.java
  92. 30 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/UserInfoVo.java
  93. 127 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/listener/SysUserImportListener.java
  94. 15 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysClientMapper.java
  95. 14 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysConfigMapper.java
  96. 46 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysDeptMapper.java
  97. 23 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysDictDataMapper.java
  98. 14 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysDictTypeMapper.java
  99. 14 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysLogininforMapper.java
  100. 76 0
      ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysMenuMapper.java

+ 105 - 0
ruoyi-modules/ruoyi-subject/pom.xml

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-modules</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-subject</artifactId>
+
+    <description>
+        项目模块
+    </description>
+
+    <dependencies>
+        <!-- 通用工具-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-doc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-mybatis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-translation</artifactId>
+        </dependency>
+
+        <!-- OSS功能模块 -->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-oss</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-log</artifactId>
+        </dependency>
+
+        <!-- excel-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-excel</artifactId>
+        </dependency>
+
+        <!-- SMS功能模块 -->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-sms</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-tenant</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-security</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-idempotent</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-sensitive</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-encrypt</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-websocket</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-sse</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 55 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/monitor/CacheController.java

@@ -0,0 +1,55 @@
+package org.dromara.subject.controller.monitor;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.subject.domain.vo.CacheListInfoVo;
+import lombok.RequiredArgsConstructor;
+import org.redisson.spring.data.connection.RedissonConnectionFactory;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.*;
+
+/**
+ * 缓存监控
+ *
+ * @author Lion Li
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/monitor/cache")
+public class CacheController {
+
+    private final RedissonConnectionFactory connectionFactory;
+
+    /**
+     * 获取缓存监控列表
+     */
+    @SaCheckPermission("monitor:cache:list")
+    @GetMapping()
+    public R<CacheListInfoVo> getInfo() throws Exception {
+        RedisConnection connection = connectionFactory.getConnection();
+        Properties commandStats = connection.commands().info("commandstats");
+
+        List<Map<String, String>> pieList = new ArrayList<>();
+        if (commandStats != null) {
+            commandStats.stringPropertyNames().forEach(key -> {
+                Map<String, String> data = new HashMap<>(2);
+                String property = commandStats.getProperty(key);
+                data.put("name", StringUtils.removeStart(key, "cmdstat_"));
+                data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
+                pieList.add(data);
+            });
+        }
+
+        CacheListInfoVo infoVo = new CacheListInfoVo();
+        infoVo.setInfo(connection.commands().info());
+        infoVo.setDbSize(connection.commands().dbSize());
+        infoVo.setCommandStats(pieList);
+        return R.ok(infoVo);
+    }
+
+}

+ 89 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/monitor/SysLogininforController.java

@@ -0,0 +1,89 @@
+package org.dromara.subject.controller.monitor;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.constant.CacheConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysLogininforBo;
+import org.dromara.subject.domain.vo.SysLogininforVo;
+import org.dromara.subject.service.ISysLogininforService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 系统访问记录
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/monitor/logininfor")
+public class SysLogininforController extends BaseController {
+
+    private final ISysLogininforService logininforService;
+
+    /**
+     * 获取系统访问记录列表
+     */
+    @SaCheckPermission("monitor:logininfor:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysLogininforVo> list(SysLogininforBo logininfor, PageQuery pageQuery) {
+        return logininforService.selectPageLogininforList(logininfor, pageQuery);
+    }
+
+    /**
+     * 导出系统访问记录列表
+     */
+    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
+    @SaCheckPermission("monitor:logininfor:export")
+    @PostMapping("/export")
+    public void export(SysLogininforBo logininfor, HttpServletResponse response) {
+        List<SysLogininforVo> list = logininforService.selectLogininforList(logininfor);
+        ExcelUtil.exportExcel(list, "登录日志", SysLogininforVo.class, response);
+    }
+
+    /**
+     * 批量删除登录日志
+     * @param infoIds 日志ids
+     */
+    @SaCheckPermission("monitor:logininfor:remove")
+    @Log(title = "登录日志", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{infoIds}")
+    public R<Void> remove(@PathVariable Long[] infoIds) {
+        return toAjax(logininforService.deleteLogininforByIds(infoIds));
+    }
+
+    /**
+     * 清理系统访问记录
+     */
+    @SaCheckPermission("monitor:logininfor:remove")
+    @Log(title = "登录日志", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/clean")
+    public R<Void> clean() {
+        logininforService.cleanLogininfor();
+        return R.ok();
+    }
+
+    @SaCheckPermission("monitor:logininfor:unlock")
+    @Log(title = "账户解锁", businessType = BusinessType.OTHER)
+    @GetMapping("/unlock/{userName}")
+    public R<Void> unlock(@PathVariable("userName") String userName) {
+        String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName;
+        if (RedisUtils.hasKey(loginName)) {
+            RedisUtils.deleteObject(loginName);
+        }
+        return R.ok();
+    }
+
+}

+ 75 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/monitor/SysOperlogController.java

@@ -0,0 +1,75 @@
+package org.dromara.subject.controller.monitor;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.subject.domain.bo.SysOperLogBo;
+import org.dromara.subject.domain.vo.SysOperLogVo;
+import org.dromara.subject.service.ISysOperLogService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 操作日志记录
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/monitor/operlog")
+public class SysOperlogController extends BaseController {
+
+    private final ISysOperLogService operLogService;
+
+    /**
+     * 获取操作日志记录列表
+     */
+    @SaCheckPermission("monitor:operlog:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysOperLogVo> list(SysOperLogBo operLog, PageQuery pageQuery) {
+        return operLogService.selectPageOperLogList(operLog, pageQuery);
+    }
+
+    /**
+     * 导出操作日志记录列表
+     */
+    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
+    @SaCheckPermission("monitor:operlog:export")
+    @PostMapping("/export")
+    public void export(SysOperLogBo operLog, HttpServletResponse response) {
+        List<SysOperLogVo> list = operLogService.selectOperLogList(operLog);
+        ExcelUtil.exportExcel(list, "操作日志", SysOperLogVo.class, response);
+    }
+
+    /**
+     * 批量删除操作日志记录
+     * @param operIds 日志ids
+     */
+    @Log(title = "操作日志", businessType = BusinessType.DELETE)
+    @SaCheckPermission("monitor:operlog:remove")
+    @DeleteMapping("/{operIds}")
+    public R<Void> remove(@PathVariable Long[] operIds) {
+        return toAjax(operLogService.deleteOperLogByIds(operIds));
+    }
+
+    /**
+     * 清理操作日志记录
+     */
+    @Log(title = "操作日志", businessType = BusinessType.CLEAN)
+    @SaCheckPermission("monitor:operlog:remove")
+    @DeleteMapping("/clean")
+    public R<Void> clean() {
+        operLogService.cleanOperLog();
+        return R.ok();
+    }
+}

+ 130 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/monitor/SysUserOnlineController.java

@@ -0,0 +1,130 @@
+package org.dromara.subject.controller.monitor;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.constant.CacheConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.domain.dto.UserOnlineDTO;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.SysUserOnline;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 在线用户监控
+ *
+ * @author Lion Li
+ */
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/monitor/online")
+public class SysUserOnlineController extends BaseController {
+
+    /**
+     * 获取在线用户监控列表
+     *
+     * @param ipaddr   IP地址
+     * @param userName 用户名
+     */
+    @SaCheckPermission("monitor:online:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysUserOnline> list(String ipaddr, String userName) {
+        // 获取所有未过期的 token
+        List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
+        List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
+        for (String key : keys) {
+            String token = StringUtils.substringAfterLast(key, ":");
+            // 如果已经过期则跳过
+            if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) {
+                continue;
+            }
+            userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token));
+        }
+        if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
+            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
+                StringUtils.equals(ipaddr, userOnline.getIpaddr()) &&
+                    StringUtils.equals(userName, userOnline.getUserName())
+            );
+        } else if (StringUtils.isNotEmpty(ipaddr)) {
+            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
+                StringUtils.equals(ipaddr, userOnline.getIpaddr())
+            );
+        } else if (StringUtils.isNotEmpty(userName)) {
+            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
+                StringUtils.equals(userName, userOnline.getUserName())
+            );
+        }
+        Collections.reverse(userOnlineDTOList);
+        userOnlineDTOList.removeAll(Collections.singleton(null));
+        List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
+        return TableDataInfo.build(userOnlineList);
+    }
+
+    /**
+     * 强退用户
+     *
+     * @param tokenId token值
+     */
+    @SaCheckPermission("monitor:online:forceLogout")
+    @Log(title = "在线用户", businessType = BusinessType.FORCE)
+    @DeleteMapping("/{tokenId}")
+    public R<Void> forceLogout(@PathVariable String tokenId) {
+        try {
+            StpUtil.kickoutByTokenValue(tokenId);
+        } catch (NotLoginException ignored) {
+        }
+        return R.ok();
+    }
+
+    /**
+     * 获取当前用户登录在线设备
+     */
+    @GetMapping()
+    public TableDataInfo<SysUserOnline> getInfo() {
+        // 获取指定账号 id 的 token 集合
+        List<String> tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString());
+        List<UserOnlineDTO> userOnlineDTOList = tokenIds.stream()
+            .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1)
+            .map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token))
+            .collect(Collectors.toList());
+        //复制和处理 SysUserOnline 对象列表
+        Collections.reverse(userOnlineDTOList);
+        userOnlineDTOList.removeAll(Collections.singleton(null));
+        List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
+        return TableDataInfo.build(userOnlineList);
+    }
+
+    /**
+     * 强退当前在线设备
+     *
+     * @param tokenId token值
+     */
+    @Log(title = "在线设备", businessType = BusinessType.FORCE)
+    @PostMapping("/{tokenId}")
+    public R<Void> remove(@PathVariable("tokenId") String tokenId) {
+        try {
+            // 获取指定账号 id 的 token 集合
+            List<String> keys = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString());
+            keys.stream()
+                .filter(key -> key.equals(tokenId))
+                .findFirst()
+                .ifPresent(key -> StpUtil.kickoutByTokenValue(tokenId));
+        } catch (NotLoginException ignored) {
+        }
+        return R.ok();
+    }
+
+}

+ 115 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysClientController.java

@@ -0,0 +1,115 @@
+package org.dromara.subject.controller.system;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.subject.domain.bo.SysClientBo;
+import org.dromara.subject.domain.vo.SysClientVo;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.subject.service.ISysClientService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 客户端管理
+ *
+ * @author Michelle.Chung
+ * @date 2023-06-18
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/client")
+public class SysClientController extends BaseController {
+
+    private final ISysClientService sysClientService;
+
+    /**
+     * 查询客户端管理列表
+     */
+    @SaCheckPermission("system:client:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysClientVo> list(SysClientBo bo, PageQuery pageQuery) {
+        return sysClientService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出客户端管理列表
+     */
+    @SaCheckPermission("system:client:export")
+    @Log(title = "客户端管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(SysClientBo bo, HttpServletResponse response) {
+        List<SysClientVo> list = sysClientService.queryList(bo);
+        ExcelUtil.exportExcel(list, "客户端管理", SysClientVo.class, response);
+    }
+
+    /**
+     * 获取客户端管理详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("system:client:query")
+    @GetMapping("/{id}")
+    public R<SysClientVo> getInfo(@NotNull(message = "主键不能为空")
+                                  @PathVariable Long id) {
+        return R.ok(sysClientService.queryById(id));
+    }
+
+    /**
+     * 新增客户端管理
+     */
+    @SaCheckPermission("system:client:add")
+    @Log(title = "客户端管理", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysClientBo bo) {
+        return toAjax(sysClientService.insertByBo(bo));
+    }
+
+    /**
+     * 修改客户端管理
+     */
+    @SaCheckPermission("system:client:edit")
+    @Log(title = "客户端管理", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysClientBo bo) {
+        return toAjax(sysClientService.updateByBo(bo));
+    }
+
+    /**
+     * 状态修改
+     */
+    @SaCheckPermission("system:client:edit")
+    @Log(title = "客户端管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestBody SysClientBo bo) {
+        return toAjax(sysClientService.updateUserStatus(bo.getClientId(), bo.getStatus()));
+    }
+
+    /**
+     * 删除客户端管理
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("system:client:remove")
+    @Log(title = "客户端管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable Long[] ids) {
+        return toAjax(sysClientService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 137 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysConfigController.java

@@ -0,0 +1,137 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysConfigBo;
+import org.dromara.subject.domain.vo.SysConfigVo;
+import org.dromara.subject.service.ISysConfigService;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 参数配置 信息操作处理
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/config")
+public class SysConfigController extends BaseController {
+
+    private final ISysConfigService configService;
+
+    /**
+     * 获取参数配置列表
+     */
+    @SaCheckPermission("system:config:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysConfigVo> list(SysConfigBo config, PageQuery pageQuery) {
+        return configService.selectPageConfigList(config, pageQuery);
+    }
+
+    /**
+     * 导出参数配置列表
+     */
+    @Log(title = "参数管理", businessType = BusinessType.EXPORT)
+    @SaCheckPermission("system:config:export")
+    @PostMapping("/export")
+    public void export(SysConfigBo config, HttpServletResponse response) {
+        List<SysConfigVo> list = configService.selectConfigList(config);
+        ExcelUtil.exportExcel(list, "参数数据", SysConfigVo.class, response);
+    }
+
+    /**
+     * 根据参数编号获取详细信息
+     *
+     * @param configId 参数ID
+     */
+    @SaCheckPermission("system:config:query")
+    @GetMapping(value = "/{configId}")
+    public R<SysConfigVo> getInfo(@PathVariable Long configId) {
+        return R.ok(configService.selectConfigById(configId));
+    }
+
+    /**
+     * 根据参数键名查询参数值
+     *
+     * @param configKey 参数Key
+     */
+    @GetMapping(value = "/configKey/{configKey}")
+    public R<String> getConfigKey(@PathVariable String configKey) {
+        return R.ok("操作成功", configService.selectConfigByKey(configKey));
+    }
+
+    /**
+     * 新增参数配置
+     */
+    @SaCheckPermission("system:config:add")
+    @Log(title = "参数管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysConfigBo config) {
+        if (!configService.checkConfigKeyUnique(config)) {
+            return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
+        }
+        configService.insertConfig(config);
+        return R.ok();
+    }
+
+    /**
+     * 修改参数配置
+     */
+    @SaCheckPermission("system:config:edit")
+    @Log(title = "参数管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysConfigBo config) {
+        if (!configService.checkConfigKeyUnique(config)) {
+            return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
+        }
+        configService.updateConfig(config);
+        return R.ok();
+    }
+
+    /**
+     * 根据参数键名修改参数配置
+     */
+    @SaCheckPermission("system:config:edit")
+    @Log(title = "参数管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateByKey")
+    public R<Void> updateByKey(@RequestBody SysConfigBo config) {
+        configService.updateConfig(config);
+        return R.ok();
+    }
+
+    /**
+     * 删除参数配置
+     *
+     * @param configIds 参数ID串
+     */
+    @SaCheckPermission("system:config:remove")
+    @Log(title = "参数管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{configIds}")
+    public R<Void> remove(@PathVariable Long[] configIds) {
+        configService.deleteConfigByIds(configIds);
+        return R.ok();
+    }
+
+    /**
+     * 刷新参数缓存
+     */
+    @SaCheckPermission("system:config:remove")
+    @Log(title = "参数管理", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/refreshCache")
+    public R<Void> refreshCache() {
+        configService.resetConfigCache();
+        return R.ok();
+    }
+}

+ 140 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysDeptController.java

@@ -0,0 +1,140 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.convert.Convert;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysDeptBo;
+import org.dromara.subject.domain.vo.SysDeptVo;
+import org.dromara.subject.service.ISysDeptService;
+import org.dromara.subject.service.ISysPostService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 部门信息
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/dept")
+public class SysDeptController extends BaseController {
+
+    private final ISysDeptService deptService;
+    private final ISysPostService postService;
+
+    /**
+     * 获取部门列表
+     */
+    @SaCheckPermission("system:dept:list")
+    @GetMapping("/list")
+    public R<List<SysDeptVo>> list(SysDeptBo dept) {
+        List<SysDeptVo> depts = deptService.selectDeptList(dept);
+        return R.ok(depts);
+    }
+
+    /**
+     * 查询部门列表(排除节点)
+     *
+     * @param deptId 部门ID
+     */
+    @SaCheckPermission("system:dept:list")
+    @GetMapping("/list/exclude/{deptId}")
+    public R<List<SysDeptVo>> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
+        List<SysDeptVo> depts = deptService.selectDeptList(new SysDeptBo());
+        depts.removeIf(d -> d.getDeptId().equals(deptId)
+            || StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId)));
+        return R.ok(depts);
+    }
+
+    /**
+     * 根据部门编号获取详细信息
+     *
+     * @param deptId 部门ID
+     */
+    @SaCheckPermission("system:dept:query")
+    @GetMapping(value = "/{deptId}")
+    public R<SysDeptVo> getInfo(@PathVariable Long deptId) {
+        deptService.checkDeptDataScope(deptId);
+        return R.ok(deptService.selectDeptById(deptId));
+    }
+
+    /**
+     * 新增部门
+     */
+    @SaCheckPermission("system:dept:add")
+    @Log(title = "部门管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysDeptBo dept) {
+        if (!deptService.checkDeptNameUnique(dept)) {
+            return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
+        }
+        return toAjax(deptService.insertDept(dept));
+    }
+
+    /**
+     * 修改部门
+     */
+    @SaCheckPermission("system:dept:edit")
+    @Log(title = "部门管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysDeptBo dept) {
+        Long deptId = dept.getDeptId();
+        deptService.checkDeptDataScope(deptId);
+        if (!deptService.checkDeptNameUnique(dept)) {
+            return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
+        } else if (dept.getParentId().equals(deptId)) {
+            return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
+        } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())) {
+            if (deptService.selectNormalChildrenDeptById(deptId) > 0) {
+                return R.fail("该部门包含未停用的子部门!");
+            } else if (deptService.checkDeptExistUser(deptId)) {
+                return R.fail("该部门下存在已分配用户,不能禁用!");
+            }
+        }
+        return toAjax(deptService.updateDept(dept));
+    }
+
+    /**
+     * 删除部门
+     *
+     * @param deptId 部门ID
+     */
+    @SaCheckPermission("system:dept:remove")
+    @Log(title = "部门管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{deptId}")
+    public R<Void> remove(@PathVariable Long deptId) {
+        if (deptService.hasChildByDeptId(deptId)) {
+            return R.warn("存在下级部门,不允许删除");
+        }
+        if (deptService.checkDeptExistUser(deptId)) {
+            return R.warn("部门存在用户,不允许删除");
+        }
+        if (postService.countPostByDeptId(deptId) > 0) {
+            return R.warn("部门存在岗位,不允许删除");
+        }
+        deptService.checkDeptDataScope(deptId);
+        return toAjax(deptService.deleteDeptById(deptId));
+    }
+
+    /**
+     * 获取部门选择框列表
+     *
+     * @param deptIds 部门ID串
+     */
+    @SaCheckPermission("system:dept:query")
+    @GetMapping("/optionselect")
+    public R<List<SysDeptVo>> optionselect(@RequestParam(required = false) Long[] deptIds) {
+        return R.ok(deptService.selectDeptByIds(deptIds == null ? null : List.of(deptIds)));
+    }
+
+}

+ 123 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysDictDataController.java

@@ -0,0 +1,123 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.ObjectUtil;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.subject.domain.bo.SysDictDataBo;
+import org.dromara.subject.domain.vo.SysDictDataVo;
+import org.dromara.subject.service.ISysDictDataService;
+import org.dromara.subject.service.ISysDictTypeService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 数据字典信息
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/dict/data")
+public class SysDictDataController extends BaseController {
+
+    private final ISysDictDataService dictDataService;
+    private final ISysDictTypeService dictTypeService;
+
+    /**
+     * 查询字典数据列表
+     */
+    @SaCheckPermission("system:dict:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysDictDataVo> list(SysDictDataBo dictData, PageQuery pageQuery) {
+        return dictDataService.selectPageDictDataList(dictData, pageQuery);
+    }
+
+    /**
+     * 导出字典数据列表
+     */
+    @Log(title = "字典数据", businessType = BusinessType.EXPORT)
+    @SaCheckPermission("system:dict:export")
+    @PostMapping("/export")
+    public void export(SysDictDataBo dictData, HttpServletResponse response) {
+        List<SysDictDataVo> list = dictDataService.selectDictDataList(dictData);
+        ExcelUtil.exportExcel(list, "字典数据", SysDictDataVo.class, response);
+    }
+
+    /**
+     * 查询字典数据详细
+     *
+     * @param dictCode 字典code
+     */
+    @SaCheckPermission("system:dict:query")
+    @GetMapping(value = "/{dictCode}")
+    public R<SysDictDataVo> getInfo(@PathVariable Long dictCode) {
+        return R.ok(dictDataService.selectDictDataById(dictCode));
+    }
+
+    /**
+     * 根据字典类型查询字典数据信息
+     *
+     * @param dictType 字典类型
+     */
+    @GetMapping(value = "/type/{dictType}")
+    public R<List<SysDictDataVo>> dictType(@PathVariable String dictType) {
+        List<SysDictDataVo> data = dictTypeService.selectDictDataByType(dictType);
+        if (ObjectUtil.isNull(data)) {
+            data = new ArrayList<>();
+        }
+        return R.ok(data);
+    }
+
+    /**
+     * 新增字典类型
+     */
+    @SaCheckPermission("system:dict:add")
+    @Log(title = "字典数据", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysDictDataBo dict) {
+        if (!dictDataService.checkDictDataUnique(dict)) {
+            return R.fail("新增字典数据'" + dict.getDictValue() + "'失败,字典键值已存在");
+        }
+        dictDataService.insertDictData(dict);
+        return R.ok();
+    }
+
+    /**
+     * 修改保存字典类型
+     */
+    @SaCheckPermission("system:dict:edit")
+    @Log(title = "字典数据", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysDictDataBo dict) {
+        if (!dictDataService.checkDictDataUnique(dict)) {
+            return R.fail("修改字典数据'" + dict.getDictValue() + "'失败,字典键值已存在");
+        }
+        dictDataService.updateDictData(dict);
+        return R.ok();
+    }
+
+    /**
+     * 删除字典类型
+     *
+     * @param dictCodes 字典code串
+     */
+    @SaCheckPermission("system:dict:remove")
+    @Log(title = "字典类型", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{dictCodes}")
+    public R<Void> remove(@PathVariable Long[] dictCodes) {
+        dictDataService.deleteDictDataByIds(dictCodes);
+        return R.ok();
+    }
+}

+ 125 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysDictTypeController.java

@@ -0,0 +1,125 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysDictTypeBo;
+import org.dromara.subject.domain.vo.SysDictTypeVo;
+import org.dromara.subject.service.ISysDictTypeService;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 数据字典信息
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/dict/type")
+public class SysDictTypeController extends BaseController {
+
+    private final ISysDictTypeService dictTypeService;
+
+    /**
+     * 查询字典类型列表
+     */
+    @SaCheckPermission("system:dict:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysDictTypeVo> list(SysDictTypeBo dictType, PageQuery pageQuery) {
+        return dictTypeService.selectPageDictTypeList(dictType, pageQuery);
+    }
+
+    /**
+     * 导出字典类型列表
+     */
+    @Log(title = "字典类型", businessType = BusinessType.EXPORT)
+    @SaCheckPermission("system:dict:export")
+    @PostMapping("/export")
+    public void export(SysDictTypeBo dictType, HttpServletResponse response) {
+        List<SysDictTypeVo> list = dictTypeService.selectDictTypeList(dictType);
+        ExcelUtil.exportExcel(list, "字典类型", SysDictTypeVo.class, response);
+    }
+
+    /**
+     * 查询字典类型详细
+     *
+     * @param dictId 字典ID
+     */
+    @SaCheckPermission("system:dict:query")
+    @GetMapping(value = "/{dictId}")
+    public R<SysDictTypeVo> getInfo(@PathVariable Long dictId) {
+        return R.ok(dictTypeService.selectDictTypeById(dictId));
+    }
+
+    /**
+     * 新增字典类型
+     */
+    @SaCheckPermission("system:dict:add")
+    @Log(title = "字典类型", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysDictTypeBo dict) {
+        if (!dictTypeService.checkDictTypeUnique(dict)) {
+            return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
+        }
+        dictTypeService.insertDictType(dict);
+        return R.ok();
+    }
+
+    /**
+     * 修改字典类型
+     */
+    @SaCheckPermission("system:dict:edit")
+    @Log(title = "字典类型", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysDictTypeBo dict) {
+        if (!dictTypeService.checkDictTypeUnique(dict)) {
+            return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
+        }
+        dictTypeService.updateDictType(dict);
+        return R.ok();
+    }
+
+    /**
+     * 删除字典类型
+     *
+     * @param dictIds 字典ID串
+     */
+    @SaCheckPermission("system:dict:remove")
+    @Log(title = "字典类型", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{dictIds}")
+    public R<Void> remove(@PathVariable Long[] dictIds) {
+        dictTypeService.deleteDictTypeByIds(dictIds);
+        return R.ok();
+    }
+
+    /**
+     * 刷新字典缓存
+     */
+    @SaCheckPermission("system:dict:remove")
+    @Log(title = "字典类型", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/refreshCache")
+    public R<Void> refreshCache() {
+        dictTypeService.resetDictCache();
+        return R.ok();
+    }
+
+    /**
+     * 获取字典选择框列表
+     */
+    @GetMapping("/optionselect")
+    public R<List<SysDictTypeVo>> optionselect() {
+        List<SysDictTypeVo> dictTypes = dictTypeService.selectDictTypeAll();
+        return R.ok(dictTypes);
+    }
+}

+ 174 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysMenuController.java

@@ -0,0 +1,174 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaCheckRole;
+import cn.dev33.satoken.annotation.SaMode;
+import cn.hutool.core.lang.tree.Tree;
+import org.dromara.common.core.constant.TenantConstants;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysMenuBo;
+import org.dromara.subject.domain.vo.MenuTreeSelectVo;
+import org.dromara.subject.domain.vo.RouterVo;
+import org.dromara.subject.domain.vo.SysMenuVo;
+import org.dromara.subject.domain.SysMenu;
+import org.dromara.subject.service.ISysMenuService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 菜单信息
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/menu")
+public class SysMenuController extends BaseController {
+
+    private final ISysMenuService menuService;
+
+    /**
+     * 获取路由信息
+     *
+     * @return 路由信息
+     */
+    @GetMapping("/getRouters")
+    public R<List<RouterVo>> getRouters() {
+        List<SysMenu> menus = menuService.selectMenuTreeByUserId(LoginHelper.getUserId());
+        return R.ok(menuService.buildMenus(menus));
+    }
+
+    /**
+     * 获取菜单列表
+     */
+    @SaCheckRole(value = {
+            TenantConstants.SUPER_ADMIN_ROLE_KEY,
+            TenantConstants.TENANT_ADMIN_ROLE_KEY
+    }, mode = SaMode.OR)
+    @SaCheckPermission("system:menu:list")
+    @GetMapping("/list")
+    public R<List<SysMenuVo>> list(SysMenuBo menu) {
+        List<SysMenuVo> menus = menuService.selectMenuList(menu, LoginHelper.getUserId());
+        return R.ok(menus);
+    }
+
+    /**
+     * 根据菜单编号获取详细信息
+     *
+     * @param menuId 菜单ID
+     */
+    @SaCheckRole(value = {
+            TenantConstants.SUPER_ADMIN_ROLE_KEY,
+            TenantConstants.TENANT_ADMIN_ROLE_KEY
+    }, mode = SaMode.OR)
+    @SaCheckPermission("system:menu:query")
+    @GetMapping(value = "/{menuId}")
+    public R<SysMenuVo> getInfo(@PathVariable Long menuId) {
+        return R.ok(menuService.selectMenuById(menuId));
+    }
+
+    /**
+     * 获取菜单下拉树列表
+     */
+    @SaCheckPermission("system:menu:query")
+    @GetMapping("/treeselect")
+    public R<List<Tree<Long>>> treeselect(SysMenuBo menu) {
+        List<SysMenuVo> menus = menuService.selectMenuList(menu, LoginHelper.getUserId());
+        return R.ok(menuService.buildMenuTreeSelect(menus));
+    }
+
+    /**
+     * 加载对应角色菜单列表树
+     *
+     * @param roleId 角色ID
+     */
+    @SaCheckPermission("system:menu:query")
+    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
+    public R<MenuTreeSelectVo> roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
+        List<SysMenuVo> menus = menuService.selectMenuList(LoginHelper.getUserId());
+        MenuTreeSelectVo selectVo = new MenuTreeSelectVo();
+        selectVo.setCheckedKeys(menuService.selectMenuListByRoleId(roleId));
+        selectVo.setMenus(menuService.buildMenuTreeSelect(menus));
+        return R.ok(selectVo);
+    }
+
+    /**
+     * 加载对应租户套餐菜单列表树
+     *
+     * @param packageId 租户套餐ID
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:menu:query")
+    @GetMapping(value = "/tenantPackageMenuTreeselect/{packageId}")
+    public R<MenuTreeSelectVo> tenantPackageMenuTreeselect(@PathVariable("packageId") Long packageId) {
+        List<SysMenuVo> menus = menuService.selectMenuList(LoginHelper.getUserId());
+        MenuTreeSelectVo selectVo = new MenuTreeSelectVo();
+        selectVo.setCheckedKeys(menuService.selectMenuListByPackageId(packageId));
+        selectVo.setMenus(menuService.buildMenuTreeSelect(menus));
+        return R.ok(selectVo);
+    }
+
+    /**
+     * 新增菜单
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:menu:add")
+    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysMenuBo menu) {
+        if (!menuService.checkMenuNameUnique(menu)) {
+            return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
+        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
+            return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+        }
+        return toAjax(menuService.insertMenu(menu));
+    }
+
+    /**
+     * 修改菜单
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:menu:edit")
+    @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysMenuBo menu) {
+        if (!menuService.checkMenuNameUnique(menu)) {
+            return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
+        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
+            return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
+        } else if (menu.getMenuId().equals(menu.getParentId())) {
+            return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
+        }
+        return toAjax(menuService.updateMenu(menu));
+    }
+
+    /**
+     * 删除菜单
+     *
+     * @param menuId 菜单ID
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:menu:remove")
+    @Log(title = "菜单管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{menuId}")
+    public R<Void> remove(@PathVariable("menuId") Long menuId) {
+        if (menuService.hasChildByMenuId(menuId)) {
+            return R.warn("存在子菜单,不允许删除");
+        }
+        if (menuService.checkMenuExistRole(menuId)) {
+            return R.warn("菜单已分配,不允许删除");
+        }
+        return toAjax(menuService.deleteMenuById(menuId));
+    }
+
+}

+ 90 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysNoticeController.java

@@ -0,0 +1,90 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.service.DictService;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.sse.utils.SseMessageUtils;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysNoticeBo;
+import org.dromara.subject.domain.vo.SysNoticeVo;
+import org.dromara.subject.service.ISysNoticeService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 公告 信息操作处理
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/notice")
+public class SysNoticeController extends BaseController {
+
+    private final ISysNoticeService noticeService;
+    private final DictService dictService;
+
+    /**
+     * 获取通知公告列表
+     */
+    @SaCheckPermission("system:notice:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysNoticeVo> list(SysNoticeBo notice, PageQuery pageQuery) {
+        return noticeService.selectPageNoticeList(notice, pageQuery);
+    }
+
+    /**
+     * 根据通知公告编号获取详细信息
+     *
+     * @param noticeId 公告ID
+     */
+    @SaCheckPermission("system:notice:query")
+    @GetMapping(value = "/{noticeId}")
+    public R<SysNoticeVo> getInfo(@PathVariable Long noticeId) {
+        return R.ok(noticeService.selectNoticeById(noticeId));
+    }
+
+    /**
+     * 新增通知公告
+     */
+    @SaCheckPermission("system:notice:add")
+    @Log(title = "通知公告", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysNoticeBo notice) {
+        int rows = noticeService.insertNotice(notice);
+        if (rows <= 0) {
+            return R.fail();
+        }
+        String type = dictService.getDictLabel("sys_notice_type", notice.getNoticeType());
+        SseMessageUtils.publishAll("[" + type + "] " + notice.getNoticeTitle());
+        return R.ok();
+    }
+
+    /**
+     * 修改通知公告
+     */
+    @SaCheckPermission("system:notice:edit")
+    @Log(title = "通知公告", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysNoticeBo notice) {
+        return toAjax(noticeService.updateNotice(notice));
+    }
+
+    /**
+     * 删除通知公告
+     *
+     * @param noticeIds 公告ID串
+     */
+    @SaCheckPermission("system:notice:remove")
+    @Log(title = "通知公告", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{noticeIds}")
+    public R<Void> remove(@PathVariable Long[] noticeIds) {
+        return toAjax(noticeService.deleteNoticeByIds(noticeIds));
+    }
+}

+ 105 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysOssConfigController.java

@@ -0,0 +1,105 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.core.validate.QueryGroup;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.subject.domain.bo.SysOssConfigBo;
+import org.dromara.subject.domain.vo.SysOssConfigVo;
+import org.dromara.subject.service.ISysOssConfigService;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 对象存储配置
+ *
+ * @author Lion Li
+ * @author 孤舟烟雨
+ * @date 2021-08-13
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/resource/oss/config")
+public class SysOssConfigController extends BaseController {
+
+    private final ISysOssConfigService ossConfigService;
+
+    /**
+     * 查询对象存储配置列表
+     */
+    @SaCheckPermission("system:ossConfig:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) {
+        return ossConfigService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取对象存储配置详细信息
+     *
+     * @param ossConfigId OSS配置ID
+     */
+    @SaCheckPermission("system:ossConfig:list")
+    @GetMapping("/{ossConfigId}")
+    public R<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable Long ossConfigId) {
+        return R.ok(ossConfigService.queryById(ossConfigId));
+    }
+
+    /**
+     * 新增对象存储配置
+     */
+    @SaCheckPermission("system:ossConfig:add")
+    @Log(title = "对象存储配置", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) {
+        return toAjax(ossConfigService.insertByBo(bo));
+    }
+
+    /**
+     * 修改对象存储配置
+     */
+    @SaCheckPermission("system:ossConfig:edit")
+    @Log(title = "对象存储配置", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {
+        return toAjax(ossConfigService.updateByBo(bo));
+    }
+
+    /**
+     * 删除对象存储配置
+     *
+     * @param ossConfigIds OSS配置ID串
+     */
+    @SaCheckPermission("system:ossConfig:remove")
+    @Log(title = "对象存储配置", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ossConfigIds}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable Long[] ossConfigIds) {
+        return toAjax(ossConfigService.deleteWithValidByIds(List.of(ossConfigIds), true));
+    }
+
+    /**
+     * 状态修改
+     */
+    @SaCheckPermission("system:ossConfig:edit")
+    @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) {
+        return toAjax(ossConfigService.updateOssConfigStatus(bo));
+    }
+}

+ 108 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysOssController.java

@@ -0,0 +1,108 @@
+package org.dromara.subject.controller.system;
+
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.ObjectUtil;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.QueryGroup;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.subject.domain.bo.SysOssBo;
+import org.dromara.subject.domain.vo.SysOssUploadVo;
+import org.dromara.subject.domain.vo.SysOssVo;
+import org.dromara.subject.service.ISysOssService;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 文件上传 控制层
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/resource/oss")
+public class SysOssController extends BaseController {
+
+    private final ISysOssService ossService;
+
+    /**
+     * 查询OSS对象存储列表
+     */
+    @SaCheckPermission("system:oss:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) {
+        return ossService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 查询OSS对象基于id串
+     *
+     * @param ossIds OSS对象ID串
+     */
+    @SaCheckPermission("system:oss:list")
+    @GetMapping("/listByIds/{ossIds}")
+    public R<List<SysOssVo>> listByIds(@NotEmpty(message = "主键不能为空")
+                                       @PathVariable Long[] ossIds) {
+        List<SysOssVo> list = ossService.listByIds(Arrays.asList(ossIds));
+        return R.ok(list);
+    }
+
+    /**
+     * 上传OSS对象存储
+     *
+     * @param file 文件
+     */
+    @SaCheckPermission("system:oss:upload")
+    @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
+    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public R<SysOssUploadVo> upload(@RequestPart("file") MultipartFile file) {
+        if (ObjectUtil.isNull(file)) {
+            return R.fail("上传文件不能为空");
+        }
+        SysOssVo oss = ossService.upload(file);
+        SysOssUploadVo uploadVo = new SysOssUploadVo();
+        uploadVo.setUrl(oss.getUrl());
+        uploadVo.setFileName(oss.getOriginalName());
+        uploadVo.setOssId(oss.getOssId().toString());
+        return R.ok(uploadVo);
+    }
+
+    /**
+     * 下载OSS对象
+     *
+     * @param ossId OSS对象ID
+     */
+    @SaCheckPermission("system:oss:download")
+    @GetMapping("/download/{ossId}")
+    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
+        ossService.download(ossId, response);
+    }
+
+    /**
+     * 删除OSS对象存储
+     *
+     * @param ossIds OSS对象ID串
+     */
+    @SaCheckPermission("system:oss:remove")
+    @Log(title = "OSS对象存储", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ossIds}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable Long[] ossIds) {
+        return toAjax(ossService.deleteWithValidByIds(List.of(ossIds), true));
+    }
+
+}

+ 133 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysPostController.java

@@ -0,0 +1,133 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.ObjectUtil;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysPostBo;
+import org.dromara.subject.domain.vo.SysPostVo;
+import org.dromara.subject.service.ISysPostService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 岗位信息操作处理
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/post")
+public class SysPostController extends BaseController {
+
+    private final ISysPostService postService;
+
+    /**
+     * 获取岗位列表
+     */
+    @SaCheckPermission("system:post:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysPostVo> list(SysPostBo post, PageQuery pageQuery) {
+        return postService.selectPagePostList(post, pageQuery);
+    }
+
+    /**
+     * 导出岗位列表
+     */
+    @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
+    @SaCheckPermission("system:post:export")
+    @PostMapping("/export")
+    public void export(SysPostBo post, HttpServletResponse response) {
+        List<SysPostVo> list = postService.selectPostList(post);
+        ExcelUtil.exportExcel(list, "岗位数据", SysPostVo.class, response);
+    }
+
+    /**
+     * 根据岗位编号获取详细信息
+     *
+     * @param postId 岗位ID
+     */
+    @SaCheckPermission("system:post:query")
+    @GetMapping(value = "/{postId}")
+    public R<SysPostVo> getInfo(@PathVariable Long postId) {
+        return R.ok(postService.selectPostById(postId));
+    }
+
+    /**
+     * 新增岗位
+     */
+    @SaCheckPermission("system:post:add")
+    @Log(title = "岗位管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysPostBo post) {
+        if (!postService.checkPostNameUnique(post)) {
+            return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
+        } else if (!postService.checkPostCodeUnique(post)) {
+            return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
+        }
+        return toAjax(postService.insertPost(post));
+    }
+
+    /**
+     * 修改岗位
+     */
+    @SaCheckPermission("system:post:edit")
+    @Log(title = "岗位管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysPostBo post) {
+        if (!postService.checkPostNameUnique(post)) {
+            return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
+        } else if (!postService.checkPostCodeUnique(post)) {
+            return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
+        } else if (UserConstants.POST_DISABLE.equals(post.getStatus())
+            && postService.countUserPostById(post.getPostId()) > 0) {
+            return R.fail("该岗位下存在已分配用户,不能禁用!");
+        }
+        return toAjax(postService.updatePost(post));
+    }
+
+    /**
+     * 删除岗位
+     *
+     * @param postIds 岗位ID串
+     */
+    @SaCheckPermission("system:post:remove")
+    @Log(title = "岗位管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{postIds}")
+    public R<Void> remove(@PathVariable Long[] postIds) {
+        return toAjax(postService.deletePostByIds(postIds));
+    }
+
+    /**
+     * 获取岗位选择框列表
+     *
+     * @param postIds 岗位ID串
+     * @param deptId  部门id
+     */
+    @SaCheckPermission("system:post:query")
+    @GetMapping("/optionselect")
+    public R<List<SysPostVo>> optionselect(@RequestParam(required = false) Long[] postIds, @RequestParam(required = false) Long deptId) {
+        List<SysPostVo> list = new ArrayList<>();
+        if (ObjectUtil.isNotNull(deptId)) {
+            SysPostBo post = new SysPostBo();
+            post.setDeptId(deptId);
+            list = postService.selectPostList(post);
+        } else if (postIds != null) {
+            list = postService.selectPostByIds(List.of(postIds));
+        }
+        return R.ok(list);
+    }
+
+}

+ 132 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysProfileController.java

@@ -0,0 +1,132 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.secure.BCrypt;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.io.FileUtil;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.file.MimeTypeUtils;
+import org.dromara.common.encrypt.annotation.ApiEncrypt;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.helper.DataPermissionHelper;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysUserBo;
+import org.dromara.subject.domain.bo.SysUserPasswordBo;
+import org.dromara.subject.domain.bo.SysUserProfileBo;
+import org.dromara.subject.domain.vo.AvatarVo;
+import org.dromara.subject.domain.vo.ProfileVo;
+import org.dromara.subject.domain.vo.SysOssVo;
+import org.dromara.subject.domain.vo.SysUserVo;
+import org.dromara.subject.service.ISysOssService;
+import org.dromara.subject.service.ISysUserService;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Arrays;
+
+/**
+ * 个人信息 业务处理
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/user/profile")
+public class SysProfileController extends BaseController {
+
+    private final ISysUserService userService;
+    private final ISysOssService ossService;
+
+    /**
+     * 个人信息
+     */
+    @GetMapping
+    public R<ProfileVo> profile() {
+        SysUserVo user = userService.selectUserById(LoginHelper.getUserId());
+        ProfileVo profileVo = new ProfileVo();
+        profileVo.setUser(user);
+        profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId()));
+        profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserId()));
+        return R.ok(profileVo);
+    }
+
+    /**
+     * 修改用户信息
+     */
+    @RepeatSubmit
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> updateProfile(@Validated @RequestBody SysUserProfileBo profile) {
+        SysUserBo user = BeanUtil.toBean(profile, SysUserBo.class);
+        user.setUserId(LoginHelper.getUserId());
+        String username = LoginHelper.getUsername();
+        if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
+            return R.fail("修改用户'" + username + "'失败,手机号码已存在");
+        }
+        if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
+            return R.fail("修改用户'" + username + "'失败,邮箱账号已存在");
+        }
+        int rows = DataPermissionHelper.ignore(() -> userService.updateUserProfile(user));
+        if (rows > 0) {
+            return R.ok();
+        }
+        return R.fail("修改个人信息异常,请联系管理员");
+    }
+
+    /**
+     * 重置密码
+     *
+     * @param bo 新旧密码
+     */
+    @RepeatSubmit
+    @ApiEncrypt
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+    @PutMapping("/updatePwd")
+    public R<Void> updatePwd(@Validated @RequestBody SysUserPasswordBo bo) {
+        SysUserVo user = userService.selectUserById(LoginHelper.getUserId());
+        String password = user.getPassword();
+        if (!BCrypt.checkpw(bo.getOldPassword(), password)) {
+            return R.fail("修改密码失败,旧密码错误");
+        }
+        if (BCrypt.checkpw(bo.getNewPassword(), password)) {
+            return R.fail("新密码不能与旧密码相同");
+        }
+
+        if (userService.resetUserPwd(user.getUserId(), BCrypt.hashpw(bo.getNewPassword())) > 0) {
+            return R.ok();
+        }
+        return R.fail("修改密码异常,请联系管理员");
+    }
+
+    /**
+     * 头像上传
+     *
+     * @param avatarfile 用户头像
+     */
+    @RepeatSubmit
+    @Log(title = "用户头像", businessType = BusinessType.UPDATE)
+    @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public R<AvatarVo> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) {
+        if (!avatarfile.isEmpty()) {
+            String extension = FileUtil.extName(avatarfile.getOriginalFilename());
+            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
+                return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");
+            }
+            SysOssVo oss = ossService.upload(avatarfile);
+            String avatar = oss.getUrl();
+            if (userService.updateUserAvatar(LoginHelper.getUserId(), oss.getOssId())) {
+                AvatarVo avatarVo = new AvatarVo();
+                avatarVo.setImgUrl(avatar);
+                return R.ok(avatarVo);
+            }
+        }
+        return R.fail("上传图片异常,请联系管理员");
+    }
+}

+ 229 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysRoleController.java

@@ -0,0 +1,229 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysDeptBo;
+import org.dromara.subject.domain.bo.SysRoleBo;
+import org.dromara.subject.domain.bo.SysUserBo;
+import org.dromara.subject.domain.vo.DeptTreeSelectVo;
+import org.dromara.subject.domain.vo.SysRoleVo;
+import org.dromara.subject.domain.vo.SysUserVo;
+import org.dromara.subject.domain.SysUserRole;
+import org.dromara.subject.service.ISysDeptService;
+import org.dromara.subject.service.ISysRoleService;
+import org.dromara.subject.service.ISysUserService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 角色信息
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/role")
+public class SysRoleController extends BaseController {
+
+    private final ISysRoleService roleService;
+    private final ISysUserService userService;
+    private final ISysDeptService deptService;
+
+    /**
+     * 获取角色信息列表
+     */
+    @SaCheckPermission("system:role:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysRoleVo> list(SysRoleBo role, PageQuery pageQuery) {
+        return roleService.selectPageRoleList(role, pageQuery);
+    }
+
+    /**
+     * 导出角色信息列表
+     */
+    @Log(title = "角色管理", businessType = BusinessType.EXPORT)
+    @SaCheckPermission("system:role:export")
+    @PostMapping("/export")
+    public void export(SysRoleBo role, HttpServletResponse response) {
+        List<SysRoleVo> list = roleService.selectRoleList(role);
+        ExcelUtil.exportExcel(list, "角色数据", SysRoleVo.class, response);
+    }
+
+    /**
+     * 根据角色编号获取详细信息
+     *
+     * @param roleId 角色ID
+     */
+    @SaCheckPermission("system:role:query")
+    @GetMapping(value = "/{roleId}")
+    public R<SysRoleVo> getInfo(@PathVariable Long roleId) {
+        roleService.checkRoleDataScope(roleId);
+        return R.ok(roleService.selectRoleById(roleId));
+    }
+
+    /**
+     * 新增角色
+     */
+    @SaCheckPermission("system:role:add")
+    @Log(title = "角色管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysRoleBo role) {
+        roleService.checkRoleAllowed(role);
+        if (!roleService.checkRoleNameUnique(role)) {
+            return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
+        } else if (!roleService.checkRoleKeyUnique(role)) {
+            return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
+        }
+        return toAjax(roleService.insertRole(role));
+
+    }
+
+    /**
+     * 修改保存角色
+     */
+    @SaCheckPermission("system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysRoleBo role) {
+        roleService.checkRoleAllowed(role);
+        roleService.checkRoleDataScope(role.getRoleId());
+        if (!roleService.checkRoleNameUnique(role)) {
+            return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
+        } else if (!roleService.checkRoleKeyUnique(role)) {
+            return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
+        }
+
+        if (roleService.updateRole(role) > 0) {
+            roleService.cleanOnlineUserByRole(role.getRoleId());
+            return R.ok();
+        }
+        return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
+    }
+
+    /**
+     * 修改保存数据权限
+     */
+    @SaCheckPermission("system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/dataScope")
+    public R<Void> dataScope(@RequestBody SysRoleBo role) {
+        roleService.checkRoleAllowed(role);
+        roleService.checkRoleDataScope(role.getRoleId());
+        return toAjax(roleService.authDataScope(role));
+    }
+
+    /**
+     * 状态修改
+     */
+    @SaCheckPermission("system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestBody SysRoleBo role) {
+        roleService.checkRoleAllowed(role);
+        roleService.checkRoleDataScope(role.getRoleId());
+        return toAjax(roleService.updateRoleStatus(role.getRoleId(), role.getStatus()));
+    }
+
+    /**
+     * 删除角色
+     *
+     * @param roleIds 角色ID串
+     */
+    @SaCheckPermission("system:role:remove")
+    @Log(title = "角色管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{roleIds}")
+    public R<Void> remove(@PathVariable Long[] roleIds) {
+        return toAjax(roleService.deleteRoleByIds(roleIds));
+    }
+
+    /**
+     * 获取角色选择框列表
+     *
+     * @param roleIds 角色ID串
+     */
+    @SaCheckPermission("system:role:query")
+    @GetMapping("/optionselect")
+    public R<List<SysRoleVo>> optionselect(@RequestParam(required = false) Long[] roleIds) {
+        return R.ok(roleService.selectRoleByIds(roleIds == null ? null : List.of(roleIds)));
+    }
+
+    /**
+     * 查询已分配用户角色列表
+     */
+    @SaCheckPermission("system:role:list")
+    @GetMapping("/authUser/allocatedList")
+    public TableDataInfo<SysUserVo> allocatedList(SysUserBo user, PageQuery pageQuery) {
+        return userService.selectAllocatedList(user, pageQuery);
+    }
+
+    /**
+     * 查询未分配用户角色列表
+     */
+    @SaCheckPermission("system:role:list")
+    @GetMapping("/authUser/unallocatedList")
+    public TableDataInfo<SysUserVo> unallocatedList(SysUserBo user, PageQuery pageQuery) {
+        return userService.selectUnallocatedList(user, pageQuery);
+    }
+
+    /**
+     * 取消授权用户
+     */
+    @SaCheckPermission("system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/cancel")
+    public R<Void> cancelAuthUser(@RequestBody SysUserRole userRole) {
+        return toAjax(roleService.deleteAuthUser(userRole));
+    }
+
+    /**
+     * 批量取消授权用户
+     *
+     * @param roleId  角色ID
+     * @param userIds 用户ID串
+     */
+    @SaCheckPermission("system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/cancelAll")
+    public R<Void> cancelAuthUserAll(Long roleId, Long[] userIds) {
+        return toAjax(roleService.deleteAuthUsers(roleId, userIds));
+    }
+
+    /**
+     * 批量选择用户授权
+     *
+     * @param roleId  角色ID
+     * @param userIds 用户ID串
+     */
+    @SaCheckPermission("system:role:edit")
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authUser/selectAll")
+    public R<Void> selectAuthUserAll(Long roleId, Long[] userIds) {
+        roleService.checkRoleDataScope(roleId);
+        return toAjax(roleService.insertAuthUsers(roleId, userIds));
+    }
+
+    /**
+     * 获取对应角色部门树列表
+     *
+     * @param roleId 角色ID
+     */
+    @SaCheckPermission("system:role:list")
+    @GetMapping(value = "/deptTree/{roleId}")
+    public R<DeptTreeSelectVo> roleDeptTreeselect(@PathVariable("roleId") Long roleId) {
+        DeptTreeSelectVo selectVo = new DeptTreeSelectVo();
+        selectVo.setCheckedKeys(deptService.selectDeptListByRoleId(roleId));
+        selectVo.setDepts(deptService.selectDeptTreeList(new SysDeptBo()));
+        return R.ok(selectVo);
+    }
+}

+ 38 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysSocialController.java

@@ -0,0 +1,38 @@
+package org.dromara.subject.controller.system;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.vo.SysSocialVo;
+import org.dromara.subject.service.ISysSocialService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 社会化关系
+ *
+ * @author thiszhc
+ * @date 2023-06-16
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/social")
+public class SysSocialController extends BaseController {
+
+    private final ISysSocialService socialUserService;
+
+    /**
+     * 查询社会化关系列表
+     */
+    @GetMapping("/list")
+    public R<List<SysSocialVo>> list() {
+        return R.ok(socialUserService.queryListByUserId(LoginHelper.getUserId()));
+    }
+
+}

+ 193 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysTenantController.java

@@ -0,0 +1,193 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaCheckRole;
+import com.baomidou.lock.annotation.Lock4j;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.constant.TenantConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.encrypt.annotation.ApiEncrypt;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysTenantBo;
+import org.dromara.subject.domain.vo.SysTenantVo;
+import org.dromara.subject.service.ISysTenantService;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 租户管理
+ *
+ * @author Michelle.Chung
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/tenant")
+@ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
+public class SysTenantController extends BaseController {
+
+    private final ISysTenantService tenantService;
+
+    /**
+     * 查询租户列表
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenant:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysTenantVo> list(SysTenantBo bo, PageQuery pageQuery) {
+        return tenantService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出租户列表
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenant:export")
+    @Log(title = "租户", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(SysTenantBo bo, HttpServletResponse response) {
+        List<SysTenantVo> list = tenantService.queryList(bo);
+        ExcelUtil.exportExcel(list, "租户", SysTenantVo.class, response);
+    }
+
+    /**
+     * 获取租户详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenant:query")
+    @GetMapping("/{id}")
+    public R<SysTenantVo> getInfo(@NotNull(message = "主键不能为空")
+                                  @PathVariable Long id) {
+        return R.ok(tenantService.queryById(id));
+    }
+
+    /**
+     * 新增租户
+     */
+    @ApiEncrypt
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenant:add")
+    @Log(title = "租户", businessType = BusinessType.INSERT)
+    @Lock4j
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysTenantBo bo) {
+        if (!tenantService.checkCompanyNameUnique(bo)) {
+            return R.fail("新增租户'" + bo.getCompanyName() + "'失败,企业名称已存在");
+        }
+        return toAjax(TenantHelper.ignore(() -> tenantService.insertByBo(bo)));
+    }
+
+    /**
+     * 修改租户
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenant:edit")
+    @Log(title = "租户", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysTenantBo bo) {
+        tenantService.checkTenantAllowed(bo.getTenantId());
+        if (!tenantService.checkCompanyNameUnique(bo)) {
+            return R.fail("修改租户'" + bo.getCompanyName() + "'失败,公司名称已存在");
+        }
+        return toAjax(tenantService.updateByBo(bo));
+    }
+
+    /**
+     * 状态修改
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenant:edit")
+    @Log(title = "租户", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestBody SysTenantBo bo) {
+        tenantService.checkTenantAllowed(bo.getTenantId());
+        return toAjax(tenantService.updateTenantStatus(bo));
+    }
+
+    /**
+     * 删除租户
+     *
+     * @param ids 主键串
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenant:remove")
+    @Log(title = "租户", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable Long[] ids) {
+        return toAjax(tenantService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    /**
+     * 动态切换租户
+     *
+     * @param tenantId 租户ID
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @GetMapping("/dynamic/{tenantId}")
+    public R<Void> dynamicTenant(@NotBlank(message = "租户ID不能为空") @PathVariable String tenantId) {
+        TenantHelper.setDynamic(tenantId, true);
+        return R.ok();
+    }
+
+    /**
+     * 清除动态租户
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @GetMapping("/dynamic/clear")
+    public R<Void> dynamicClear() {
+        TenantHelper.clearDynamic();
+        return R.ok();
+    }
+
+
+    /**
+     * 同步租户套餐
+     *
+     * @param tenantId  租户id
+     * @param packageId 套餐id
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenant:edit")
+    @Log(title = "租户", businessType = BusinessType.UPDATE)
+    @GetMapping("/syncTenantPackage")
+    public R<Void> syncTenantPackage(@NotBlank(message = "租户ID不能为空") String tenantId,
+                                     @NotNull(message = "套餐ID不能为空") Long packageId) {
+        return toAjax(TenantHelper.ignore(() -> tenantService.syncTenantPackage(tenantId, packageId)));
+    }
+
+    /**
+     * 同步租户字典
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @Log(title = "同步租户字典", businessType = BusinessType.INSERT)
+    @GetMapping("/syncTenantDict")
+    public R<Void> syncTenantDict() {
+        if (!TenantHelper.isEnable()) {
+            return R.fail("当前未开启租户模式");
+        }
+        tenantService.syncTenantDict();
+        return R.ok("同步租户字典成功");
+    }
+
+}

+ 142 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysTenantPackageController.java

@@ -0,0 +1,142 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaCheckRole;
+import org.dromara.common.core.constant.TenantConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysTenantPackageBo;
+import org.dromara.subject.domain.vo.SysTenantPackageVo;
+import org.dromara.subject.service.ISysTenantPackageService;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 租户套餐管理
+ *
+ * @author Michelle.Chung
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/tenant/package")
+@ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
+public class SysTenantPackageController extends BaseController {
+
+    private final ISysTenantPackageService tenantPackageService;
+
+    /**
+     * 查询租户套餐列表
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenantPackage:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysTenantPackageVo> list(SysTenantPackageBo bo, PageQuery pageQuery) {
+        return tenantPackageService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 查询租户套餐下拉选列表
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenantPackage:list")
+    @GetMapping("/selectList")
+    public R<List<SysTenantPackageVo>> selectList() {
+        return R.ok(tenantPackageService.selectList());
+    }
+
+    /**
+     * 导出租户套餐列表
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenantPackage:export")
+    @Log(title = "租户套餐", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(SysTenantPackageBo bo, HttpServletResponse response) {
+        List<SysTenantPackageVo> list = tenantPackageService.queryList(bo);
+        ExcelUtil.exportExcel(list, "租户套餐", SysTenantPackageVo.class, response);
+    }
+
+    /**
+     * 获取租户套餐详细信息
+     *
+     * @param packageId 主键
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenantPackage:query")
+    @GetMapping("/{packageId}")
+    public R<SysTenantPackageVo> getInfo(@NotNull(message = "主键不能为空")
+                                     @PathVariable Long packageId) {
+        return R.ok(tenantPackageService.queryById(packageId));
+    }
+
+    /**
+     * 新增租户套餐
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenantPackage:add")
+    @Log(title = "租户套餐", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysTenantPackageBo bo) {
+        if (!tenantPackageService.checkPackageNameUnique(bo)) {
+            return R.fail("新增套餐'" + bo.getPackageName() + "'失败,套餐名称已存在");
+        }
+        return toAjax(tenantPackageService.insertByBo(bo));
+    }
+
+    /**
+     * 修改租户套餐
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenantPackage:edit")
+    @Log(title = "租户套餐", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysTenantPackageBo bo) {
+        if (!tenantPackageService.checkPackageNameUnique(bo)) {
+            return R.fail("修改套餐'" + bo.getPackageName() + "'失败,套餐名称已存在");
+        }
+        return toAjax(tenantPackageService.updateByBo(bo));
+    }
+
+    /**
+     * 状态修改
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenantPackage:edit")
+    @Log(title = "租户套餐", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestBody SysTenantPackageBo bo) {
+        return toAjax(tenantPackageService.updatePackageStatus(bo));
+    }
+
+    /**
+     * 删除租户套餐
+     *
+     * @param packageIds 主键串
+     */
+    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
+    @SaCheckPermission("system:tenantPackage:remove")
+    @Log(title = "租户套餐", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{packageIds}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable Long[] packageIds) {
+        return toAjax(tenantPackageService.deleteWithValidByIds(List.of(packageIds), true));
+    }
+}

+ 302 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/controller/system/SysUserController.java

@@ -0,0 +1,302 @@
+package org.dromara.subject.controller.system;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.secure.BCrypt;
+import cn.hutool.core.lang.tree.Tree;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.domain.model.LoginUser;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.encrypt.annotation.ApiEncrypt;
+import org.dromara.common.excel.core.ExcelResult;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.subject.domain.bo.SysDeptBo;
+import org.dromara.subject.domain.bo.SysPostBo;
+import org.dromara.subject.domain.bo.SysRoleBo;
+import org.dromara.subject.domain.bo.SysUserBo;
+import org.dromara.subject.domain.vo.*;
+import org.dromara.subject.service.*;
+import org.dromara.system.domain.vo.*;
+import org.dromara.subject.listener.SysUserImportListener;
+import org.dromara.system.service.*;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 用户信息
+ *
+ * @author Lion Li
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/system/user")
+public class SysUserController extends BaseController {
+
+    private final ISysUserService userService;
+    private final ISysRoleService roleService;
+    private final ISysPostService postService;
+    private final ISysDeptService deptService;
+    private final ISysTenantService tenantService;
+
+    /**
+     * 获取用户列表
+     */
+    @SaCheckPermission("system:user:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysUserVo> list(SysUserBo user, PageQuery pageQuery) {
+        return userService.selectPageUserList(user, pageQuery);
+    }
+
+    /**
+     * 导出用户列表
+     */
+    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
+    @SaCheckPermission("system:user:export")
+    @PostMapping("/export")
+    public void export(SysUserBo user, HttpServletResponse response) {
+        List<SysUserExportVo> list = userService.selectUserExportList(user);
+        ExcelUtil.exportExcel(list, "用户数据", SysUserExportVo.class, response);
+    }
+
+    /**
+     * 导入数据
+     *
+     * @param file          导入文件
+     * @param updateSupport 是否更新已存在数据
+     */
+    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
+    @SaCheckPermission("system:user:import")
+    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public R<Void> importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception {
+        ExcelResult<SysUserImportVo> result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport));
+        return R.ok(result.getAnalysis());
+    }
+
+    /**
+     * 获取导入模板
+     */
+    @PostMapping("/importTemplate")
+    public void importTemplate(HttpServletResponse response) {
+        ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response);
+    }
+
+    /**
+     * 获取用户信息
+     *
+     * @return 用户信息
+     */
+    @GetMapping("/getInfo")
+    public R<UserInfoVo> getInfo() {
+        UserInfoVo userInfoVo = new UserInfoVo();
+        LoginUser loginUser = LoginHelper.getLoginUser();
+        if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
+            // 超级管理员 如果重新加载用户信息需清除动态租户
+            TenantHelper.clearDynamic();
+        }
+        SysUserVo user = userService.selectUserById(loginUser.getUserId());
+        if (ObjectUtil.isNull(user)) {
+            return R.fail("没有权限访问用户数据!");
+        }
+        userInfoVo.setUser(user);
+        userInfoVo.setPermissions(loginUser.getMenuPermission());
+        userInfoVo.setRoles(loginUser.getRolePermission());
+        return R.ok(userInfoVo);
+    }
+
+    /**
+     * 根据用户编号获取详细信息
+     *
+     * @param userId 用户ID
+     */
+    @SaCheckPermission("system:user:query")
+    @GetMapping(value = {"/", "/{userId}"})
+    public R<SysUserInfoVo> getInfo(@PathVariable(value = "userId", required = false) Long userId) {
+        userService.checkUserDataScope(userId);
+        SysUserInfoVo userInfoVo = new SysUserInfoVo();
+        SysRoleBo roleBo = new SysRoleBo();
+        roleBo.setStatus(UserConstants.ROLE_NORMAL);
+        List<SysRoleVo> roles = roleService.selectRoleList(roleBo);
+        userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin()));
+        if (ObjectUtil.isNotNull(userId)) {
+            SysUserVo sysUser = userService.selectUserById(userId);
+            userInfoVo.setUser(sysUser);
+            userInfoVo.setRoleIds(roleService.selectRoleListByUserId(userId));
+            Long deptId = sysUser.getDeptId();
+            if (ObjectUtil.isNotNull(deptId)) {
+                SysPostBo postBo = new SysPostBo();
+                postBo.setDeptId(deptId);
+                userInfoVo.setPosts(postService.selectPostList(postBo));
+                userInfoVo.setPostIds(postService.selectPostListByUserId(userId));
+            }
+        }
+        return R.ok(userInfoVo);
+    }
+
+    /**
+     * 新增用户
+     */
+    @SaCheckPermission("system:user:add")
+    @Log(title = "用户管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R<Void> add(@Validated @RequestBody SysUserBo user) {
+        deptService.checkDeptDataScope(user.getDeptId());
+        if (!userService.checkUserNameUnique(user)) {
+            return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
+        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
+            return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
+        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
+            return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+        }
+        if (TenantHelper.isEnable()) {
+            if (!tenantService.checkAccountBalance(TenantHelper.getTenantId())) {
+                return R.fail("当前租户下用户名额不足,请联系管理员");
+            }
+        }
+        user.setPassword(BCrypt.hashpw(user.getPassword()));
+        return toAjax(userService.insertUser(user));
+    }
+
+    /**
+     * 修改用户
+     */
+    @SaCheckPermission("system:user:edit")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R<Void> edit(@Validated @RequestBody SysUserBo user) {
+        userService.checkUserAllowed(user.getUserId());
+        userService.checkUserDataScope(user.getUserId());
+        deptService.checkDeptDataScope(user.getDeptId());
+        if (!userService.checkUserNameUnique(user)) {
+            return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
+        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
+            return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
+        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
+            return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
+        }
+        return toAjax(userService.updateUser(user));
+    }
+
+    /**
+     * 删除用户
+     *
+     * @param userIds 角色ID串
+     */
+    @SaCheckPermission("system:user:remove")
+    @Log(title = "用户管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{userIds}")
+    public R<Void> remove(@PathVariable Long[] userIds) {
+        if (ArrayUtil.contains(userIds, LoginHelper.getUserId())) {
+            return R.fail("当前用户不能删除");
+        }
+        return toAjax(userService.deleteUserByIds(userIds));
+    }
+
+    /**
+     * 根据用户ID串批量获取用户基础信息
+     *
+     * @param userIds 用户ID串
+     * @param deptId  部门ID
+     */
+    @SaCheckPermission("system:user:query")
+    @GetMapping("/optionselect")
+    public R<List<SysUserVo>> optionselect(@RequestParam(required = false) Long[] userIds,
+                                           @RequestParam(required = false) Long deptId) {
+        return R.ok(userService.selectUserByIds(userIds == null ? null : List.of(userIds), deptId));
+    }
+
+    /**
+     * 重置密码
+     */
+    @ApiEncrypt
+    @SaCheckPermission("system:user:resetPwd")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/resetPwd")
+    public R<Void> resetPwd(@RequestBody SysUserBo user) {
+        userService.checkUserAllowed(user.getUserId());
+        userService.checkUserDataScope(user.getUserId());
+        user.setPassword(BCrypt.hashpw(user.getPassword()));
+        return toAjax(userService.resetUserPwd(user.getUserId(), user.getPassword()));
+    }
+
+    /**
+     * 状态修改
+     */
+    @SaCheckPermission("system:user:edit")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestBody SysUserBo user) {
+        userService.checkUserAllowed(user.getUserId());
+        userService.checkUserDataScope(user.getUserId());
+        return toAjax(userService.updateUserStatus(user.getUserId(), user.getStatus()));
+    }
+
+    /**
+     * 根据用户编号获取授权角色
+     *
+     * @param userId 用户ID
+     */
+    @SaCheckPermission("system:user:query")
+    @GetMapping("/authRole/{userId}")
+    public R<SysUserInfoVo> authRole(@PathVariable Long userId) {
+        userService.checkUserDataScope(userId);
+        SysUserVo user = userService.selectUserById(userId);
+        List<SysRoleVo> roles = roleService.selectRolesAuthByUserId(userId);
+        SysUserInfoVo userInfoVo = new SysUserInfoVo();
+        userInfoVo.setUser(user);
+        userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin()));
+        return R.ok(userInfoVo);
+    }
+
+    /**
+     * 用户授权角色
+     *
+     * @param userId  用户Id
+     * @param roleIds 角色ID串
+     */
+    @SaCheckPermission("system:user:edit")
+    @Log(title = "用户管理", businessType = BusinessType.GRANT)
+    @PutMapping("/authRole")
+    public R<Void> insertAuthRole(Long userId, Long[] roleIds) {
+        userService.checkUserDataScope(userId);
+        userService.insertUserAuth(userId, roleIds);
+        return R.ok();
+    }
+
+    /**
+     * 获取部门树列表
+     */
+    @SaCheckPermission("system:user:list")
+    @GetMapping("/deptTree")
+    public R<List<Tree<Long>>> deptTree(SysDeptBo dept) {
+        return R.ok(deptService.selectDeptTreeList(dept));
+    }
+
+    /**
+     * 获取部门下的所有用户信息
+     */
+    @SaCheckPermission("system:user:list")
+    @GetMapping("/list/dept/{deptId}")
+    public R<List<SysUserVo>> listByDept(@PathVariable @NotNull Long deptId) {
+        return R.ok(userService.selectUserListByDept(deptId));
+    }
+
+}

+ 47 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysCache.java

@@ -0,0 +1,47 @@
+package org.dromara.subject.domain;
+
+import org.dromara.common.core.utils.StringUtils;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 缓存信息
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+public class SysCache {
+
+    /**
+     * 缓存名称
+     */
+    private String cacheName = "";
+
+    /**
+     * 缓存键名
+     */
+    private String cacheKey = "";
+
+    /**
+     * 缓存内容
+     */
+    private String cacheValue = "";
+
+    /**
+     * 备注
+     */
+    private String remark = "";
+
+    public SysCache(String cacheName, String remark) {
+        this.cacheName = cacheName;
+        this.remark = remark;
+    }
+
+    public SysCache(String cacheName, String cacheKey, String cacheValue) {
+        this.cacheName = StringUtils.replace(cacheName, ":", "");
+        this.cacheKey = StringUtils.replace(cacheKey, cacheName, "");
+        this.cacheValue = cacheValue;
+    }
+
+}

+ 77 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysClient.java

@@ -0,0 +1,77 @@
+package org.dromara.subject.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 授权管理对象 sys_client
+ *
+ * @author Michelle.Chung
+ * @date 2023-05-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_client")
+public class SysClient extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 客户端id
+     */
+    private String clientId;
+
+    /**
+     * 客户端key
+     */
+    private String clientKey;
+
+    /**
+     * 客户端秘钥
+     */
+    private String clientSecret;
+
+    /**
+     * 授权类型
+     */
+    private String grantType;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * token活跃超时时间
+     */
+    private Long activeTimeout;
+
+    /**
+     * token固定超时时间
+     */
+    private Long timeout;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+
+}

+ 51 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysConfig.java

@@ -0,0 +1,51 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.tenant.core.TenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 参数配置表 sys_config
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_config")
+public class SysConfig extends TenantEntity {
+
+    /**
+     * 参数主键
+     */
+    @TableId(value = "config_id")
+    private Long configId;
+
+    /**
+     * 参数名称
+     */
+    private String configName;
+
+    /**
+     * 参数键名
+     */
+    private String configKey;
+
+    /**
+     * 参数键值
+     */
+    private String configValue;
+
+    /**
+     * 系统内置(Y是 N否)
+     */
+    private String configType;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 83 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysDept.java

@@ -0,0 +1,83 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+
+/**
+ * 部门表 sys_dept
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_dept")
+public class SysDept extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 部门ID
+     */
+    @TableId(value = "dept_id")
+    private Long deptId;
+
+    /**
+     * 父部门ID
+     */
+    private Long parentId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 部门类别编码
+     */
+    private String deptCategory;
+
+    /**
+     * 显示顺序
+     */
+    private Integer orderNum;
+
+    /**
+     * 负责人
+     */
+    private Long leader;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 邮箱
+     */
+    private String email;
+
+    /**
+     * 部门状态:0正常,1停用
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 祖级列表
+     */
+    private String ancestors;
+
+}

+ 71 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysDictData.java

@@ -0,0 +1,71 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.tenant.core.TenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 字典数据表 sys_dict_data
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_dict_data")
+public class SysDictData extends TenantEntity {
+
+    /**
+     * 字典编码
+     */
+    @TableId(value = "dict_code")
+    private Long dictCode;
+
+    /**
+     * 字典排序
+     */
+    private Integer dictSort;
+
+    /**
+     * 字典标签
+     */
+    private String dictLabel;
+
+    /**
+     * 字典键值
+     */
+    private String dictValue;
+
+    /**
+     * 字典类型
+     */
+    private String dictType;
+
+    /**
+     * 样式属性(其他样式扩展)
+     */
+    private String cssClass;
+
+    /**
+     * 表格字典样式
+     */
+    private String listClass;
+
+    /**
+     * 是否默认(Y是 N否)
+     */
+    private String isDefault;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    public boolean getDefault() {
+        return UserConstants.YES.equals(this.isDefault);
+    }
+
+}

+ 41 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysDictType.java

@@ -0,0 +1,41 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.tenant.core.TenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 字典类型表 sys_dict_type
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_dict_type")
+public class SysDictType extends TenantEntity {
+
+    /**
+     * 字典主键
+     */
+    @TableId(value = "dict_id")
+    private Long dictId;
+
+    /**
+     * 字典名称
+     */
+    private String dictName;
+
+    /**
+     * 字典类型
+     */
+    private String dictType;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 85 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysLogininfor.java

@@ -0,0 +1,85 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统访问记录表 sys_logininfor
+ *
+ * @author Lion Li
+ */
+
+@Data
+@TableName("sys_logininfor")
+public class SysLogininfor implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @TableId(value = "info_id")
+    private Long infoId;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 用户账号
+     */
+    private String userName;
+
+    /**
+     * 客户端
+     */
+    private String clientKey;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * 登录状态 0成功 1失败
+     */
+    private String status;
+
+    /**
+     * 登录IP地址
+     */
+    private String ipaddr;
+
+    /**
+     * 登录地点
+     */
+    private String loginLocation;
+
+    /**
+     * 浏览器类型
+     */
+    private String browser;
+
+    /**
+     * 操作系统
+     */
+    private String os;
+
+    /**
+     * 提示消息
+     */
+    private String msg;
+
+    /**
+     * 访问时间
+     */
+    private Date loginTime;
+
+}

+ 191 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysMenu.java

@@ -0,0 +1,191 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.core.constant.Constants;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 菜单权限表 sys_menu
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_menu")
+public class SysMenu extends BaseEntity {
+
+    /**
+     * 菜单ID
+     */
+    @TableId(value = "menu_id")
+    private Long menuId;
+
+    /**
+     * 父菜单ID
+     */
+    private Long parentId;
+
+    /**
+     * 菜单名称
+     */
+    private String menuName;
+
+    /**
+     * 显示顺序
+     */
+    private Integer orderNum;
+
+    /**
+     * 路由地址
+     */
+    private String path;
+
+    /**
+     * 组件路径
+     */
+    private String component;
+
+    /**
+     * 路由参数
+     */
+    private String queryParam;
+
+    /**
+     * 是否为外链(0是 1否)
+     */
+    private String isFrame;
+
+    /**
+     * 是否缓存(0缓存 1不缓存)
+     */
+    private String isCache;
+
+    /**
+     * 类型(M目录 C菜单 F按钮)
+     */
+    private String menuType;
+
+    /**
+     * 显示状态(0显示 1隐藏)
+     */
+    private String visible;
+
+    /**
+     * 菜单状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 权限字符串
+     */
+    private String perms;
+
+    /**
+     * 菜单图标
+     */
+    private String icon;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 父菜单名称
+     */
+    @TableField(exist = false)
+    private String parentName;
+
+    /**
+     * 子菜单
+     */
+    @TableField(exist = false)
+    private List<SysMenu> children = new ArrayList<>();
+
+    /**
+     * 获取路由名称
+     */
+    public String getRouteName() {
+        String routerName = StringUtils.capitalize(path);
+        // 非外链并且是一级目录(类型为目录)
+        if (isMenuFrame()) {
+            routerName = StringUtils.EMPTY;
+        }
+        return routerName;
+    }
+
+    /**
+     * 获取路由地址
+     */
+    public String getRouterPath() {
+        String routerPath = this.path;
+        // 内链打开外网方式
+        if (getParentId() != 0L && isInnerLink()) {
+            routerPath = innerLinkReplaceEach(routerPath);
+        }
+        // 非外链并且是一级目录(类型为目录)
+        if (0L == getParentId() && UserConstants.TYPE_DIR.equals(getMenuType())
+            && UserConstants.NO_FRAME.equals(getIsFrame())) {
+            routerPath = "/" + this.path;
+        }
+        // 非外链并且是一级目录(类型为菜单)
+        else if (isMenuFrame()) {
+            routerPath = "/";
+        }
+        return routerPath;
+    }
+
+    /**
+     * 获取组件信息
+     */
+    public String getComponentInfo() {
+        String component = UserConstants.LAYOUT;
+        if (StringUtils.isNotEmpty(this.component) && !isMenuFrame()) {
+            component = this.component;
+        } else if (StringUtils.isEmpty(this.component) && getParentId() != 0L && isInnerLink()) {
+            component = UserConstants.INNER_LINK;
+        } else if (StringUtils.isEmpty(this.component) && isParentView()) {
+            component = UserConstants.PARENT_VIEW;
+        }
+        return component;
+    }
+
+    /**
+     * 是否为菜单内部跳转
+     */
+    public boolean isMenuFrame() {
+        return getParentId() == 0L && UserConstants.TYPE_MENU.equals(menuType) && isFrame.equals(UserConstants.NO_FRAME);
+    }
+
+    /**
+     * 是否为内链组件
+     */
+    public boolean isInnerLink() {
+        return isFrame.equals(UserConstants.NO_FRAME) && StringUtils.ishttp(path);
+    }
+
+    /**
+     * 是否为parent_view组件
+     */
+    public boolean isParentView() {
+        return getParentId() != 0L && UserConstants.TYPE_DIR.equals(menuType);
+    }
+
+    /**
+     * 内链域名特殊字符替换
+     */
+    public static String innerLinkReplaceEach(String path) {
+        return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":"},
+            new String[]{"", "", "", "/", "/"});
+    }
+}

+ 51 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysNotice.java

@@ -0,0 +1,51 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.tenant.core.TenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+/**
+ * 通知公告表 sys_notice
+ *
+ * @author Lion Li
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_notice")
+public class SysNotice extends TenantEntity {
+
+    /**
+     * 公告ID
+     */
+    @TableId(value = "notice_id")
+    private Long noticeId;
+
+    /**
+     * 公告标题
+     */
+    private String noticeTitle;
+
+    /**
+     * 公告类型(1通知 2公告)
+     */
+    private String noticeType;
+
+    /**
+     * 公告内容
+     */
+    private String noticeContent;
+
+    /**
+     * 公告状态(0正常 1关闭)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 115 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysOperLog.java

@@ -0,0 +1,115 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 操作日志记录表 oper_log
+ *
+ * @author Lion Li
+ */
+
+@Data
+@TableName("sys_oper_log")
+public class SysOperLog implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 日志主键
+     */
+    @TableId(value = "oper_id")
+    private Long operId;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 操作模块
+     */
+    private String title;
+
+    /**
+     * 业务类型(0其它 1新增 2修改 3删除)
+     */
+    private Integer businessType;
+
+    /**
+     * 请求方法
+     */
+    private String method;
+
+    /**
+     * 请求方式
+     */
+    private String requestMethod;
+
+    /**
+     * 操作类别(0其它 1后台用户 2手机端用户)
+     */
+    private Integer operatorType;
+
+    /**
+     * 操作人员
+     */
+    private String operName;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 请求url
+     */
+    private String operUrl;
+
+    /**
+     * 操作地址
+     */
+    private String operIp;
+
+    /**
+     * 操作地点
+     */
+    private String operLocation;
+
+    /**
+     * 请求参数
+     */
+    private String operParam;
+
+    /**
+     * 返回参数
+     */
+    private String jsonResult;
+
+    /**
+     * 操作状态(0正常 1异常)
+     */
+    private Integer status;
+
+    /**
+     * 错误消息
+     */
+    private String errorMsg;
+
+    /**
+     * 操作时间
+     */
+    private Date operTime;
+
+    /**
+     * 消耗时间
+     */
+    private Long costTime;
+
+}

+ 50 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysOss.java

@@ -0,0 +1,50 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.tenant.core.TenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * OSS对象存储对象
+ *
+ * @author Lion Li
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_oss")
+public class SysOss extends TenantEntity {
+
+    /**
+     * 对象存储主键
+     */
+    @TableId(value = "oss_id")
+    private Long ossId;
+
+    /**
+     * 文件名
+     */
+    private String fileName;
+
+    /**
+     * 原名
+     */
+    private String originalName;
+
+    /**
+     * 文件后缀名
+     */
+    private String fileSuffix;
+
+    /**
+     * URL地址
+     */
+    private String url;
+
+    /**
+     * 服务商
+     */
+    private String service;
+
+}

+ 89 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysOssConfig.java

@@ -0,0 +1,89 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+/**
+ * 对象存储配置对象 sys_oss_config
+ *
+ * @author Lion Li
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_oss_config")
+public class SysOssConfig extends BaseEntity {
+
+    /**
+     * 主键
+     */
+    @TableId(value = "oss_config_id")
+    private Long ossConfigId;
+
+    /**
+     * 配置key
+     */
+    private String configKey;
+
+    /**
+     * accessKey
+     */
+    private String accessKey;
+
+    /**
+     * 秘钥
+     */
+    private String secretKey;
+
+    /**
+     * 桶名称
+     */
+    private String bucketName;
+
+    /**
+     * 前缀
+     */
+    private String prefix;
+
+    /**
+     * 访问站点
+     */
+    private String endpoint;
+
+    /**
+     * 自定义域名
+     */
+    private String domain;
+
+    /**
+     * 是否https(0否 1是)
+     */
+    private String isHttps;
+
+    /**
+     * 域
+     */
+    private String region;
+
+    /**
+     * 是否默认(0=是,1=否)
+     */
+    private String status;
+
+    /**
+     * 扩展字段
+     */
+    private String ext1;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 桶权限类型(0private 1public 2custom)
+     */
+    private String accessPolicy;
+}

+ 61 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysPost.java

@@ -0,0 +1,61 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.tenant.core.TenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 岗位表 sys_post
+ *
+ * @author Lion Li
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_post")
+public class SysPost extends TenantEntity {
+
+    /**
+     * 岗位序号
+     */
+    @TableId(value = "post_id")
+    private Long postId;
+
+    /**
+     * 部门id
+     */
+    private Long deptId;
+
+    /**
+     * 岗位编码
+     */
+    private String postCode;
+
+    /**
+     * 岗位名称
+     */
+    private String postName;
+
+    /**
+     * 岗位类别编码
+     */
+    private String postCategory;
+
+    /**
+     * 岗位排序
+     */
+    private Integer postSort;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 79 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysRole.java

@@ -0,0 +1,79 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.tenant.core.TenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * 角色表 sys_role
+ *
+ * @author Lion Li
+ */
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_role")
+public class SysRole extends TenantEntity {
+
+    /**
+     * 角色ID
+     */
+    @TableId(value = "role_id")
+    private Long roleId;
+
+    /**
+     * 角色名称
+     */
+    private String roleName;
+
+    /**
+     * 角色权限
+     */
+    private String roleKey;
+
+    /**
+     * 角色排序
+     */
+    private Integer roleSort;
+
+    /**
+     * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
+     */
+    private String dataScope;
+
+    /**
+     * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示)
+     */
+    private Boolean menuCheckStrictly;
+
+    /**
+     * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 )
+     */
+    private Boolean deptCheckStrictly;
+
+    /**
+     * 角色状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    public SysRole(Long roleId) {
+        this.roleId = roleId;
+    }
+
+}

+ 29 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysRoleDept.java

@@ -0,0 +1,29 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * 角色和部门关联 sys_role_dept
+ *
+ * @author Lion Li
+ */
+
+@Data
+@TableName("sys_role_dept")
+public class SysRoleDept {
+
+    /**
+     * 角色ID
+     */
+    @TableId(type = IdType.INPUT)
+    private Long roleId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+}

+ 29 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysRoleMenu.java

@@ -0,0 +1,29 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * 角色和菜单关联 sys_role_menu
+ *
+ * @author Lion Li
+ */
+
+@Data
+@TableName("sys_role_menu")
+public class SysRoleMenu {
+
+    /**
+     * 角色ID
+     */
+    @TableId(type = IdType.INPUT)
+    private Long roleId;
+
+    /**
+     * 菜单ID
+     */
+    private Long menuId;
+
+}

+ 136 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysSocial.java

@@ -0,0 +1,136 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+
+/**
+ * 社会化关系对象 sys_social
+ *
+ * @author thiszhc
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_social")
+public class SysSocial extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 的唯一ID
+     */
+    private String authId;
+
+    /**
+     * 用户来源
+     */
+    private String source;
+
+    /**
+     * 用户的授权令牌
+     */
+    private String accessToken;
+
+    /**
+     * 用户的授权令牌的有效期,部分平台可能没有
+     */
+    private int expireIn;
+
+    /**
+     * 刷新令牌,部分平台可能没有
+     */
+    private String refreshToken;
+
+    /**
+     * 用户的 open id
+     */
+    private String openId;
+
+    /**
+     * 授权的第三方账号
+     */
+    private String userName;
+
+    /**
+     * 授权的第三方昵称
+     */
+    private String nickName;
+
+    /**
+     * 授权的第三方邮箱
+     */
+    private String email;
+
+    /**
+     * 授权的第三方头像地址
+     */
+    private String avatar;
+
+    /**
+     * 平台的授权信息,部分平台可能没有
+     */
+    private String accessCode;
+
+    /**
+     * 用户的 unionid
+     */
+    private String unionId;
+
+    /**
+     * 授予的权限,部分平台可能没有
+     */
+    private String scope;
+
+    /**
+     * 个别平台的授权信息,部分平台可能没有
+     */
+    private String tokenType;
+
+    /**
+     * id token,部分平台可能没有
+     */
+    private String idToken;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macAlgorithm;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macKey;
+
+    /**
+     * 用户的授权code,部分平台可能没有
+     */
+    private String code;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthToken;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthTokenSecret;
+
+
+}

+ 103 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysTenant.java

@@ -0,0 +1,103 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * 租户对象 sys_tenant
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_tenant")
+public class SysTenant extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 联系人
+     */
+    private String contactUserName;
+
+    /**
+     * 联系电话
+     */
+    private String contactPhone;
+
+    /**
+     * 企业名称
+     */
+    private String companyName;
+
+    /**
+     * 统一社会信用代码
+     */
+    private String licenseNumber;
+
+    /**
+     * 地址
+     */
+    private String address;
+
+    /**
+     * 域名
+     */
+    private String domain;
+
+    /**
+     * 企业简介
+     */
+    private String intro;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 租户套餐编号
+     */
+    private Long packageId;
+
+    /**
+     * 过期时间
+     */
+    private Date expireTime;
+
+    /**
+     * 用户数量(-1不限制)
+     */
+    private Long accountCount;
+
+    /**
+     * 租户状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+}

+ 54 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysTenantPackage.java

@@ -0,0 +1,54 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import java.io.Serial;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+/**
+ * 租户套餐对象 sys_tenant_package
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_tenant_package")
+public class SysTenantPackage extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户套餐id
+     */
+    @TableId(value = "package_id")
+    private Long packageId;
+    /**
+     * 套餐名称
+     */
+    private String packageName;
+    /**
+     * 关联菜单id
+     */
+    private String menuIds;
+    /**
+     * 备注
+     */
+    private String remark;
+    /**
+     * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示)
+     */
+    private Boolean menuCheckStrictly;
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+}

+ 115 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysUser.java

@@ -0,0 +1,115 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.tenant.core.TenantEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * 用户对象 sys_user
+ *
+ * @author Lion Li
+ */
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_user")
+public class SysUser extends TenantEntity {
+
+    /**
+     * 用户ID
+     */
+    @TableId(value = "user_id")
+    private Long userId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 用户账号
+     */
+    private String userName;
+
+    /**
+     * 用户昵称
+     */
+    private String nickName;
+
+    /**
+     * 用户类型(sys_user系统用户)
+     */
+    private String userType;
+
+    /**
+     * 用户邮箱
+     */
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    private String phonenumber;
+
+    /**
+     * 用户性别
+     */
+    private String sex;
+
+    /**
+     * 用户头像
+     */
+    private Long avatar;
+
+    /**
+     * 密码
+     */
+    @TableField(
+        insertStrategy = FieldStrategy.NOT_EMPTY,
+        updateStrategy = FieldStrategy.NOT_EMPTY,
+        whereStrategy = FieldStrategy.NOT_EMPTY
+    )
+    private String password;
+
+    /**
+     * 帐号状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 最后登录IP
+     */
+    private String loginIp;
+
+    /**
+     * 最后登录时间
+     */
+    private Date loginDate;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+    public SysUser(Long userId) {
+        this.userId = userId;
+    }
+
+    public boolean isSuperAdmin() {
+        return UserConstants.SUPER_ADMIN_ID.equals(this.userId);
+    }
+
+}

+ 63 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysUserOnline.java

@@ -0,0 +1,63 @@
+package org.dromara.subject.domain;
+
+import lombok.Data;
+
+/**
+ * 当前在线会话
+ *
+ * @author Lion Li
+ */
+@Data
+public class SysUserOnline {
+
+    /**
+     * 会话编号
+     */
+    private String tokenId;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 用户名称
+     */
+    private String userName;
+
+    /**
+     * 客户端
+     */
+    private String clientKey;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * 登录IP地址
+     */
+    private String ipaddr;
+
+    /**
+     * 登录地址
+     */
+    private String loginLocation;
+
+    /**
+     * 浏览器类型
+     */
+    private String browser;
+
+    /**
+     * 操作系统
+     */
+    private String os;
+
+    /**
+     * 登录时间
+     */
+    private Long loginTime;
+
+}

+ 29 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysUserPost.java

@@ -0,0 +1,29 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * 用户和岗位关联 sys_user_post
+ *
+ * @author Lion Li
+ */
+
+@Data
+@TableName("sys_user_post")
+public class SysUserPost {
+
+    /**
+     * 用户ID
+     */
+    @TableId(type = IdType.INPUT)
+    private Long userId;
+
+    /**
+     * 岗位ID
+     */
+    private Long postId;
+
+}

+ 29 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/SysUserRole.java

@@ -0,0 +1,29 @@
+package org.dromara.subject.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * 用户和角色关联 sys_user_role
+ *
+ * @author Lion Li
+ */
+
+@Data
+@TableName("sys_user_role")
+public class SysUserRole {
+
+    /**
+     * 用户ID
+     */
+    @TableId(type = IdType.INPUT)
+    private Long userId;
+
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+
+}

+ 80 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysClientBo.java

@@ -0,0 +1,80 @@
+package org.dromara.subject.domain.bo;
+
+import org.dromara.subject.domain.SysClient;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import java.util.List;
+
+/**
+ * 授权管理业务对象 sys_client
+ *
+ * @author Michelle.Chung
+ * @date 2023-05-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysClient.class, reverseConvertGenerate = false)
+public class SysClientBo extends BaseEntity {
+
+    /**
+     * id
+     */
+    @NotNull(message = "id不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 客户端id
+     */
+    private String clientId;
+
+    /**
+     * 客户端key
+     */
+    @NotBlank(message = "客户端key不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String clientKey;
+
+    /**
+     * 客户端秘钥
+     */
+    @NotBlank(message = "客户端秘钥不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String clientSecret;
+
+    /**
+     * 授权类型
+     */
+    @NotNull(message = "授权类型不能为空", groups = { AddGroup.class, EditGroup.class })
+    private List<String> grantTypeList;
+
+    /**
+     * 授权类型
+     */
+    private String grantType;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * token活跃超时时间
+     */
+    private Long activeTimeout;
+
+    /**
+     * token固定超时时间
+     */
+    private Long timeout;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+
+}

+ 59 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysConfigBo.java

@@ -0,0 +1,59 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysConfig;
+
+/**
+ * 参数配置业务对象 sys_config
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysConfig.class, reverseConvertGenerate = false)
+public class SysConfigBo extends BaseEntity {
+
+    /**
+     * 参数主键
+     */
+    private Long configId;
+
+    /**
+     * 参数名称
+     */
+    @NotBlank(message = "参数名称不能为空")
+    @Size(min = 0, max = 100, message = "参数名称不能超过{max}个字符")
+    private String configName;
+
+    /**
+     * 参数键名
+     */
+    @NotBlank(message = "参数键名不能为空")
+    @Size(min = 0, max = 100, message = "参数键名长度不能超过{max}个字符")
+    private String configKey;
+
+    /**
+     * 参数键值
+     */
+    @NotBlank(message = "参数键值不能为空")
+    @Size(min = 0, max = 500, message = "参数键值长度不能超过{max}个字符")
+    private String configValue;
+
+    /**
+     * 系统内置(Y是 N否)
+     */
+    private String configType;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 76 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysDeptBo.java

@@ -0,0 +1,76 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysDept;
+
+/**
+ * 部门业务对象 sys_dept
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysDept.class, reverseConvertGenerate = false)
+public class SysDeptBo extends BaseEntity {
+
+    /**
+     * 部门id
+     */
+    private Long deptId;
+
+    /**
+     * 父部门ID
+     */
+    private Long parentId;
+
+    /**
+     * 部门名称
+     */
+    @NotBlank(message = "部门名称不能为空")
+    @Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符")
+    private String deptName;
+
+    /**
+     * 部门类别编码
+     */
+    @Size(min = 0, max = 100, message = "部门类别编码长度不能超过{max}个字符")
+    private String deptCategory;
+
+    /**
+     * 显示顺序
+     */
+    @NotNull(message = "显示顺序不能为空")
+    private Integer orderNum;
+
+    /**
+     * 负责人
+     */
+    private Long leader;
+
+    /**
+     * 联系电话
+     */
+    @Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符")
+    private String phone;
+
+    /**
+     * 邮箱
+     */
+    @Email(message = "邮箱格式不正确")
+    @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
+    private String email;
+
+    /**
+     * 部门状态(0正常 1停用)
+     */
+    private String status;
+
+}

+ 80 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysDictDataBo.java

@@ -0,0 +1,80 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysDictData;
+
+/**
+ * 字典数据业务对象 sys_dict_data
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysDictData.class, reverseConvertGenerate = false)
+public class SysDictDataBo extends BaseEntity {
+
+    /**
+     * 字典编码
+     */
+    private Long dictCode;
+
+    /**
+     * 字典排序
+     */
+    private Integer dictSort;
+
+    /**
+     * 字典标签
+     */
+    @NotBlank(message = "字典标签不能为空")
+    @Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符")
+    private String dictLabel;
+
+    /**
+     * 字典键值
+     */
+    @NotBlank(message = "字典键值不能为空")
+    @Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符")
+    private String dictValue;
+
+    /**
+     * 字典类型
+     */
+    @NotBlank(message = "字典类型不能为空")
+    @Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符")
+    private String dictType;
+
+    /**
+     * 样式属性(其他样式扩展)
+     */
+    @Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符")
+    private String cssClass;
+
+    /**
+     * 表格回显样式
+     */
+    private String listClass;
+
+    /**
+     * 是否默认(Y是 N否)
+     */
+    private String isDefault;
+
+    /**
+     * 创建部门
+     */
+    private Long createDept;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 50 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysDictTypeBo.java

@@ -0,0 +1,50 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.constant.RegexConstants;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysDictType;
+
+/**
+ * 字典类型业务对象 sys_dict_type
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysDictType.class, reverseConvertGenerate = false)
+public class SysDictTypeBo extends BaseEntity {
+
+    /**
+     * 字典主键
+     */
+    private Long dictId;
+
+    /**
+     * 字典名称
+     */
+    @NotBlank(message = "字典名称不能为空")
+    @Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符")
+    private String dictName;
+
+    /**
+     * 字典类型
+     */
+    @NotBlank(message = "字典类型不能为空")
+    @Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符")
+    @Pattern(regexp = RegexConstants.DICTIONARY_TYPE, message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
+    private String dictType;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 87 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysLogininforBo.java

@@ -0,0 +1,87 @@
+package org.dromara.subject.domain.bo;
+
+import org.dromara.subject.domain.SysLogininfor;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 系统访问记录业务对象 sys_logininfor
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@AutoMapper(target = SysLogininfor.class, reverseConvertGenerate = false)
+public class SysLogininforBo {
+
+    /**
+     * 访问ID
+     */
+    private Long infoId;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 用户账号
+     */
+    private String userName;
+
+    /**
+     * 客户端
+     */
+    private String clientKey;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * 登录IP地址
+     */
+    private String ipaddr;
+
+    /**
+     * 登录地点
+     */
+    private String loginLocation;
+
+    /**
+     * 浏览器类型
+     */
+    private String browser;
+
+    /**
+     * 操作系统
+     */
+    private String os;
+
+    /**
+     * 登录状态(0成功 1失败)
+     */
+    private String status;
+
+    /**
+     * 提示消息
+     */
+    private String msg;
+
+    /**
+     * 访问时间
+     */
+    private Date loginTime;
+
+    /**
+     * 请求参数
+     */
+    private Map<String, Object> params = new HashMap<>();
+
+
+}

+ 110 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysMenuBo.java

@@ -0,0 +1,110 @@
+package org.dromara.subject.domain.bo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.constant.RegexConstants;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysMenu;
+
+/**
+ * 菜单权限业务对象 sys_menu
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysMenu.class, reverseConvertGenerate = false)
+public class SysMenuBo extends BaseEntity {
+
+    /**
+     * 菜单ID
+     */
+    private Long menuId;
+
+    /**
+     * 父菜单ID
+     */
+    private Long parentId;
+
+    /**
+     * 菜单名称
+     */
+    @NotBlank(message = "菜单名称不能为空")
+    @Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符")
+    private String menuName;
+
+    /**
+     * 显示顺序
+     */
+    @NotNull(message = "显示顺序不能为空")
+    private Integer orderNum;
+
+    /**
+     * 路由地址
+     */
+    @Size(min = 0, max = 200, message = "路由地址不能超过{max}个字符")
+    private String path;
+
+    /**
+     * 组件路径
+     */
+    @Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符")
+    private String component;
+
+    /**
+     * 路由参数
+     */
+    private String queryParam;
+
+    /**
+     * 是否为外链(0是 1否)
+     */
+    private String isFrame;
+
+    /**
+     * 是否缓存(0缓存 1不缓存)
+     */
+    private String isCache;
+
+    /**
+     * 菜单类型(M目录 C菜单 F按钮)
+     */
+    @NotBlank(message = "菜单类型不能为空")
+    private String menuType;
+
+    /**
+     * 显示状态(0显示 1隐藏)
+     */
+    private String visible;
+
+    /**
+     * 菜单状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 权限标识
+     */
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符")
+    @Pattern(regexp = RegexConstants.PERMISSION_STRING, message = "权限标识必须符合 tool:build:list 格式")
+    private String perms;
+
+    /**
+     * 菜单图标
+     */
+    private String icon;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 61 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysNoticeBo.java

@@ -0,0 +1,61 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.xss.Xss;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysNotice;
+
+/**
+ * 通知公告业务对象 sys_notice
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysNotice.class, reverseConvertGenerate = false)
+public class SysNoticeBo extends BaseEntity {
+
+    /**
+     * 公告ID
+     */
+    private Long noticeId;
+
+    /**
+     * 公告标题
+     */
+    @Xss(message = "公告标题不能包含脚本字符")
+    @NotBlank(message = "公告标题不能为空")
+    @Size(min = 0, max = 50, message = "公告标题不能超过{max}个字符")
+    private String noticeTitle;
+
+    /**
+     * 公告类型(1通知 2公告)
+     */
+    private String noticeType;
+
+    /**
+     * 公告内容
+     */
+    private String noticeContent;
+
+    /**
+     * 公告状态(0正常 1关闭)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建人名称
+     */
+    private String createByName;
+
+}

+ 127 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysOperLogBo.java

@@ -0,0 +1,127 @@
+package org.dromara.subject.domain.bo;
+
+import org.dromara.common.log.event.OperLogEvent;
+import org.dromara.subject.domain.SysOperLog;
+import io.github.linpeilie.annotations.AutoMapper;
+import io.github.linpeilie.annotations.AutoMappers;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 操作日志记录业务对象 sys_oper_log
+ *
+ * @author Michelle.Chung
+ * @date 2023-02-07
+ */
+
+@Data
+@AutoMappers({
+    @AutoMapper(target = SysOperLog.class, reverseConvertGenerate = false),
+    @AutoMapper(target = OperLogEvent.class)
+})
+public class SysOperLogBo {
+
+    /**
+     * 日志主键
+     */
+    private Long operId;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 模块标题
+     */
+    private String title;
+
+    /**
+     * 业务类型(0其它 1新增 2修改 3删除)
+     */
+    private Integer businessType;
+
+    /**
+     * 业务类型数组
+     */
+    private Integer[] businessTypes;
+
+    /**
+     * 方法名称
+     */
+    private String method;
+
+    /**
+     * 请求方式
+     */
+    private String requestMethod;
+
+    /**
+     * 操作类别(0其它 1后台用户 2手机端用户)
+     */
+    private Integer operatorType;
+
+    /**
+     * 操作人员
+     */
+    private String operName;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 请求URL
+     */
+    private String operUrl;
+
+    /**
+     * 主机地址
+     */
+    private String operIp;
+
+    /**
+     * 操作地点
+     */
+    private String operLocation;
+
+    /**
+     * 请求参数
+     */
+    private String operParam;
+
+    /**
+     * 返回参数
+     */
+    private String jsonResult;
+
+    /**
+     * 操作状态(0正常 1异常)
+     */
+    private Integer status;
+
+    /**
+     * 错误消息
+     */
+    private String errorMsg;
+
+    /**
+     * 操作时间
+     */
+    private Date operTime;
+
+    /**
+     * 消耗时间
+     */
+    private Long costTime;
+
+    /**
+     * 请求参数
+     */
+    private Map<String, Object> params = new HashMap<>();
+
+}

+ 49 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysOssBo.java

@@ -0,0 +1,49 @@
+package org.dromara.subject.domain.bo;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysOss;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * OSS对象存储分页查询对象 sys_oss
+ *
+ * @author Lion Li
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysOss.class, reverseConvertGenerate = false)
+public class SysOssBo extends BaseEntity {
+
+    /**
+     * ossId
+     */
+    private Long ossId;
+
+    /**
+     * 文件名
+     */
+    private String fileName;
+
+    /**
+     * 原名
+     */
+    private String originalName;
+
+    /**
+     * 文件后缀名
+     */
+    private String fileSuffix;
+
+    /**
+     * URL地址
+     */
+    private String url;
+
+    /**
+     * 服务商
+     */
+    private String service;
+
+}

+ 109 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysOssConfigBo.java

@@ -0,0 +1,109 @@
+package org.dromara.subject.domain.bo;
+
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysOssConfig;
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 对象存储配置业务对象 sys_oss_config
+ *
+ * @author Lion Li
+ * @author 孤舟烟雨
+ * @date 2021-08-13
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysOssConfig.class, reverseConvertGenerate = false)
+public class SysOssConfigBo extends BaseEntity {
+
+    /**
+     * 主键
+     */
+    @NotNull(message = "主键不能为空", groups = {EditGroup.class})
+    private Long ossConfigId;
+
+    /**
+     * 配置key
+     */
+    @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class})
+    @Size(min = 2, max = 100, message = "configKey长度必须介于{min}和{max} 之间")
+    private String configKey;
+
+    /**
+     * accessKey
+     */
+    @NotBlank(message = "accessKey不能为空", groups = {AddGroup.class, EditGroup.class})
+    @Size(min = 2, max = 100, message = "accessKey长度必须介于{min}和{max} 之间")
+    private String accessKey;
+
+    /**
+     * 秘钥
+     */
+    @NotBlank(message = "secretKey不能为空", groups = {AddGroup.class, EditGroup.class})
+    @Size(min = 2, max = 100, message = "secretKey长度必须介于{min}和{max} 之间")
+    private String secretKey;
+
+    /**
+     * 桶名称
+     */
+    @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class})
+    @Size(min = 2, max = 100, message = "bucketName长度必须介于{min}和{max}之间")
+    private String bucketName;
+
+    /**
+     * 前缀
+     */
+    private String prefix;
+
+    /**
+     * 访问站点
+     */
+    @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class})
+    @Size(min = 2, max = 100, message = "endpoint长度必须介于{min}和{max}之间")
+    private String endpoint;
+
+    /**
+     * 自定义域名
+     */
+    private String domain;
+
+    /**
+     * 是否https(Y=是,N=否)
+     */
+    private String isHttps;
+
+    /**
+     * 是否默认(0=是,1=否)
+     */
+    private String status;
+
+    /**
+     * 域
+     */
+    private String region;
+
+    /**
+     * 扩展字段
+     */
+    private String ext1;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 桶权限类型(0private 1public 2custom)
+     */
+    @NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String accessPolicy;
+
+}

+ 75 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysPostBo.java

@@ -0,0 +1,75 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysPost;
+
+/**
+ * 岗位信息业务对象 sys_post
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysPost.class, reverseConvertGenerate = false)
+public class SysPostBo extends BaseEntity {
+
+    /**
+     * 岗位ID
+     */
+    private Long postId;
+
+    /**
+     * 部门id(单部门)
+     */
+    @NotNull(message = "部门id不能为空")
+    private Long deptId;
+
+    /**
+     * 归属部门id(部门树)
+     */
+    private Long belongDeptId;
+
+    /**
+     * 岗位编码
+     */
+    @NotBlank(message = "岗位编码不能为空")
+    @Size(min = 0, max = 64, message = "岗位编码长度不能超过{max}个字符")
+    private String postCode;
+
+    /**
+     * 岗位名称
+     */
+    @NotBlank(message = "岗位名称不能为空")
+    @Size(min = 0, max = 50, message = "岗位名称长度不能超过{max}个字符")
+    private String postName;
+
+    /**
+     * 岗位类别编码
+     */
+    @Size(min = 0, max = 100, message = "类别编码长度不能超过{max}个字符")
+    private String postCategory;
+
+    /**
+     * 显示顺序
+     */
+    @NotNull(message = "显示顺序不能为空")
+    private Integer postSort;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 94 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysRoleBo.java

@@ -0,0 +1,94 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysRole;
+
+/**
+ * 角色信息业务对象 sys_role
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysRole.class, reverseConvertGenerate = false)
+public class SysRoleBo extends BaseEntity {
+
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+
+    /**
+     * 角色名称
+     */
+    @NotBlank(message = "角色名称不能为空")
+    @Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符")
+    private String roleName;
+
+    /**
+     * 角色权限字符串
+     */
+    @NotBlank(message = "角色权限字符串不能为空")
+    @Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符")
+    private String roleKey;
+
+    /**
+     * 显示顺序
+     */
+    @NotNull(message = "显示顺序不能为空")
+    private Integer roleSort;
+
+    /**
+     * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)
+     */
+    private String dataScope;
+
+    /**
+     * 菜单树选择项是否关联显示
+     */
+    private Boolean menuCheckStrictly;
+
+    /**
+     * 部门树选择项是否关联显示
+     */
+    private Boolean deptCheckStrictly;
+
+    /**
+     * 角色状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 菜单组
+     */
+    private Long[] menuIds;
+
+    /**
+     * 部门组(数据权限)
+     */
+    private Long[] deptIds;
+
+    public SysRoleBo(Long roleId) {
+        this.roleId = roleId;
+    }
+
+    public boolean isSuperAdmin() {
+        return UserConstants.SUPER_ADMIN_ID.equals(this.roleId);
+    }
+
+}

+ 142 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysSocialBo.java

@@ -0,0 +1,142 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.tenant.core.TenantEntity;
+import org.dromara.subject.domain.SysSocial;
+
+/**
+ * 社会化关系业务对象 sys_social
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysSocial.class, reverseConvertGenerate = false)
+public class SysSocialBo extends TenantEntity {
+
+    /**
+     * 主键
+     */
+    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 认证唯一ID
+     */
+    @NotBlank(message = "认证唯一ID不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String authId;
+
+    /**
+     * 用户来源
+     */
+    @NotBlank(message = "用户来源不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String source;
+
+    /**
+     * 用户的授权令牌
+     */
+    @NotBlank(message = "用户的授权令牌不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String accessToken;
+
+    /**
+     * 用户的授权令牌的有效期,部分平台可能没有
+     */
+    private int expireIn;
+
+    /**
+     * 刷新令牌,部分平台可能没有
+     */
+    private String refreshToken;
+
+    /**
+     * 平台唯一id
+     */
+    private String openId;
+
+    /**
+     * 用户的 ID
+     */
+    @NotBlank(message = "用户的ID不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long userId;
+
+    /**
+     * 平台的授权信息,部分平台可能没有
+     */
+    private String accessCode;
+
+    /**
+     * 用户的 unionid
+     */
+    private String unionId;
+
+    /**
+     * 授予的权限,部分平台可能没有
+     */
+    private String scope;
+
+    /**
+     * 授权的第三方账号
+     */
+    private String userName;
+
+    /**
+     * 授权的第三方昵称
+     */
+    private String nickName;
+
+    /**
+     * 授权的第三方邮箱
+     */
+    private String email;
+
+    /**
+     * 授权的第三方头像地址
+     */
+    private String avatar;
+
+    /**
+     * 个别平台的授权信息,部分平台可能没有
+     */
+    private String tokenType;
+
+    /**
+     * id token,部分平台可能没有
+     */
+    private String idToken;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macAlgorithm;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macKey;
+
+    /**
+     * 用户的授权code,部分平台可能没有
+     */
+    private String code;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthToken;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthTokenSecret;
+
+
+
+}

+ 114 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysTenantBo.java

@@ -0,0 +1,114 @@
+package org.dromara.subject.domain.bo;
+
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.subject.domain.SysTenant;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import java.util.Date;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+/**
+ * 租户业务对象 sys_tenant
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysTenant.class, reverseConvertGenerate = false)
+public class SysTenantBo extends BaseEntity {
+
+    /**
+     * id
+     */
+    @NotNull(message = "id不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 联系人
+     */
+    @NotBlank(message = "联系人不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String contactUserName;
+
+    /**
+     * 联系电话
+     */
+    @NotBlank(message = "联系电话不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String contactPhone;
+
+    /**
+     * 企业名称
+     */
+    @NotBlank(message = "企业名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String companyName;
+
+    /**
+     * 用户名(创建系统用户)
+     */
+    @NotBlank(message = "用户名不能为空", groups = { AddGroup.class })
+    private String username;
+
+    /**
+     * 密码(创建系统用户)
+     */
+    @NotBlank(message = "密码不能为空", groups = { AddGroup.class })
+    private String password;
+
+    /**
+     * 统一社会信用代码
+     */
+    private String licenseNumber;
+
+    /**
+     * 地址
+     */
+    private String address;
+
+    /**
+     * 域名
+     */
+    private String domain;
+
+    /**
+     * 企业简介
+     */
+    private String intro;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 租户套餐编号
+     */
+    @NotNull(message = "租户套餐不能为空", groups = { AddGroup.class })
+    private Long packageId;
+
+    /**
+     * 过期时间
+     */
+    private Date expireTime;
+
+    /**
+     * 用户数量(-1不限制)
+     */
+    private Long accountCount;
+
+    /**
+     * 租户状态(0正常 1停用)
+     */
+    private String status;
+
+
+}

+ 59 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysTenantPackageBo.java

@@ -0,0 +1,59 @@
+package org.dromara.subject.domain.bo;
+
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.subject.domain.SysTenantPackage;
+import io.github.linpeilie.annotations.AutoMapper;
+import io.github.linpeilie.annotations.AutoMapping;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+/**
+ * 租户套餐业务对象 sys_tenant_package
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysTenantPackage.class, reverseConvertGenerate = false)
+public class SysTenantPackageBo extends BaseEntity {
+
+    /**
+     * 租户套餐id
+     */
+    @NotNull(message = "租户套餐id不能为空", groups = { EditGroup.class })
+    private Long packageId;
+
+    /**
+     * 套餐名称
+     */
+    @NotBlank(message = "套餐名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String packageName;
+
+    /**
+     * 关联菜单id
+     */
+    @AutoMapping(target = "menuIds", expression = "java(org.dromara.common.core.utils.StringUtils.join(source.getMenuIds(), \",\"))")
+    private Long[] menuIds;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 菜单树选择项是否关联显示
+     */
+    private Boolean menuCheckStrictly;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+
+}

+ 119 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysUserBo.java

@@ -0,0 +1,119 @@
+package org.dromara.subject.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.core.xss.Xss;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.subject.domain.SysUser;
+
+/**
+ * 用户信息业务对象 sys_user
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysUser.class, reverseConvertGenerate = false)
+public class SysUserBo extends BaseEntity {
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 用户账号
+     */
+    @Xss(message = "用户账号不能包含脚本字符")
+    @NotBlank(message = "用户账号不能为空")
+    @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
+    private String userName;
+
+    /**
+     * 用户昵称
+     */
+    @Xss(message = "用户昵称不能包含脚本字符")
+    @NotBlank(message = "用户昵称不能为空")
+    @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
+    private String nickName;
+
+    /**
+     * 用户类型(sys_user系统用户)
+     */
+    private String userType;
+
+    /**
+     * 用户邮箱
+     */
+    @Email(message = "邮箱格式不正确")
+    @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    private String phonenumber;
+
+    /**
+     * 用户性别(0男 1女 2未知)
+     */
+    private String sex;
+
+    /**
+     * 密码
+     */
+    private String password;
+
+    /**
+     * 帐号状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 角色组
+     */
+    @Size(min = 1, message = "用户角色不能为空")
+    private Long[] roleIds;
+
+    /**
+     * 岗位组
+     */
+    private Long[] postIds;
+
+    /**
+     * 数据权限 当前角色ID
+     */
+    private Long roleId;
+
+    /**
+     * 排除不查询的用户(工作流用)
+     */
+    private String excludeUserIds;
+
+    public SysUserBo(Long userId) {
+        this.userId = userId;
+    }
+
+    public boolean isSuperAdmin() {
+        return UserConstants.SUPER_ADMIN_ID.equals(this.userId);
+    }
+
+}

+ 29 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysUserPasswordBo.java

@@ -0,0 +1,29 @@
+package org.dromara.subject.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 用户密码修改bo
+ */
+@Data
+public class SysUserPasswordBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 旧密码
+     */
+    @NotBlank(message = "旧密码不能为空")
+    private String oldPassword;
+
+    /**
+     * 新密码
+     */
+    @NotBlank(message = "新密码不能为空")
+    private String newPassword;
+}

+ 53 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/bo/SysUserProfileBo.java

@@ -0,0 +1,53 @@
+package org.dromara.subject.domain.bo;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.dromara.common.core.constant.RegexConstants;
+import org.dromara.common.core.xss.Xss;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.sensitive.annotation.Sensitive;
+import org.dromara.common.sensitive.core.SensitiveStrategy;
+
+/**
+ * 个人信息业务处理
+ *
+ * @author Michelle.Chung
+ */
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SysUserProfileBo extends BaseEntity {
+
+    /**
+     * 用户昵称
+     */
+    @Xss(message = "用户昵称不能包含脚本字符")
+    @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
+    private String nickName;
+
+    /**
+     * 用户邮箱
+     */
+    @Sensitive(strategy = SensitiveStrategy.EMAIL)
+    @Email(message = "邮箱格式不正确")
+    @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    @Pattern(regexp = RegexConstants.MOBILE, message = "手机号格式不正确")
+    @Sensitive(strategy = SensitiveStrategy.PHONE)
+    private String phonenumber;
+
+    /**
+     * 用户性别(0男 1女 2未知)
+     */
+    private String sex;
+
+}

+ 18 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/AvatarVo.java

@@ -0,0 +1,18 @@
+package org.dromara.subject.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 用户头像信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class AvatarVo {
+
+    /**
+     * 头像地址
+     */
+    private String imgUrl;
+
+}

+ 23 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/CacheListInfoVo.java

@@ -0,0 +1,23 @@
+package org.dromara.subject.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * 缓存监控列表信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class CacheListInfoVo {
+
+    private Properties info;
+
+    private Long dbSize;
+
+    private List<Map<String, String>> commandStats;
+
+}

+ 26 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/DeptTreeSelectVo.java

@@ -0,0 +1,26 @@
+package org.dromara.subject.domain.vo;
+
+import cn.hutool.core.lang.tree.Tree;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 角色部门列表树信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class DeptTreeSelectVo {
+
+    /**
+     * 选中部门列表
+     */
+    private List<Long> checkedKeys;
+
+    /**
+     * 下拉树结构列表
+     */
+    private List<Tree<Long>> depts;
+
+}

+ 26 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/MenuTreeSelectVo.java

@@ -0,0 +1,26 @@
+package org.dromara.subject.domain.vo;
+
+import cn.hutool.core.lang.tree.Tree;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 角色菜单列表树信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class MenuTreeSelectVo {
+
+    /**
+     * 选中菜单列表
+     */
+    private List<Long> checkedKeys;
+
+    /**
+     * 菜单下拉树结构列表
+     */
+    private List<Tree<Long>> menus;
+
+}

+ 61 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/MetaVo.java

@@ -0,0 +1,61 @@
+package org.dromara.subject.domain.vo;
+
+import org.dromara.common.core.utils.StringUtils;
+import lombok.Data;
+
+/**
+ * 路由显示信息
+ *
+ * @author ruoyi
+ */
+
+@Data
+public class MetaVo {
+
+    /**
+     * 设置该路由在侧边栏和面包屑中展示的名字
+     */
+    private String title;
+
+    /**
+     * 设置该路由的图标,对应路径src/assets/icons/svg
+     */
+    private String icon;
+
+    /**
+     * 设置为true,则不会被 <keep-alive>缓存
+     */
+    private boolean noCache;
+
+    /**
+     * 内链地址(http(s)://开头)
+     */
+    private String link;
+
+    public MetaVo(String title, String icon) {
+        this.title = title;
+        this.icon = icon;
+    }
+
+    public MetaVo(String title, String icon, boolean noCache) {
+        this.title = title;
+        this.icon = icon;
+        this.noCache = noCache;
+    }
+
+    public MetaVo(String title, String icon, String link) {
+        this.title = title;
+        this.icon = icon;
+        this.link = link;
+    }
+
+    public MetaVo(String title, String icon, boolean noCache, String link) {
+        this.title = title;
+        this.icon = icon;
+        this.noCache = noCache;
+        if (StringUtils.ishttp(link)) {
+            this.link = link;
+        }
+    }
+
+}

+ 29 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/ProfileVo.java

@@ -0,0 +1,29 @@
+package org.dromara.subject.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 用户个人信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class ProfileVo {
+
+    /**
+     * 用户信息
+     */
+    private SysUserVo user;
+
+    /**
+     * 用户所属角色组
+     */
+    private String roleGroup;
+
+    /**
+     * 用户所属岗位组
+     */
+    private String postGroup;
+
+
+}

+ 62 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/RouterVo.java

@@ -0,0 +1,62 @@
+package org.dromara.subject.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 路由配置信息
+ *
+ * @author Lion Li
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class RouterVo {
+
+    /**
+     * 路由名字
+     */
+    private String name;
+
+    /**
+     * 路由地址
+     */
+    private String path;
+
+    /**
+     * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现
+     */
+    private boolean hidden;
+
+    /**
+     * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
+     */
+    private String redirect;
+
+    /**
+     * 组件地址
+     */
+    private String component;
+
+    /**
+     * 路由参数:如 {"id": 1, "name": "ry"}
+     */
+    private String query;
+
+    /**
+     * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
+     */
+    private Boolean alwaysShow;
+
+    /**
+     * 其他元素
+     */
+    private MetaVo meta;
+
+    /**
+     * 子路由
+     */
+    private List<RouterVo> children;
+
+}

+ 90 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysClientVo.java

@@ -0,0 +1,90 @@
+package org.dromara.subject.domain.vo;
+
+import org.dromara.subject.domain.SysClient;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 授权管理视图对象 sys_client
+ *
+ * @author Michelle.Chung
+ * @date 2023-05-15
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysClient.class)
+public class SysClientVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @ExcelProperty(value = "id")
+    private Long id;
+
+    /**
+     * 客户端id
+     */
+    @ExcelProperty(value = "客户端id")
+    private String clientId;
+
+    /**
+     * 客户端key
+     */
+    @ExcelProperty(value = "客户端key")
+    private String clientKey;
+
+    /**
+     * 客户端秘钥
+     */
+    @ExcelProperty(value = "客户端秘钥")
+    private String clientSecret;
+
+    /**
+     * 授权类型
+     */
+    @ExcelProperty(value = "授权类型")
+    private List<String> grantTypeList;
+
+    /**
+     * 授权类型
+     */
+    private String grantType;
+
+    /**
+     * 设备类型
+     */
+    private String deviceType;
+
+    /**
+     * token活跃超时时间
+     */
+    @ExcelProperty(value = "token活跃超时时间")
+    private Long activeTimeout;
+
+    /**
+     * token固定超时时间
+     */
+    @ExcelProperty(value = "token固定超时时间")
+    private Long timeout;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+
+}

+ 72 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysConfigVo.java

@@ -0,0 +1,72 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysConfig;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 参数配置视图对象 sys_config
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysConfig.class)
+public class SysConfigVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 参数主键
+     */
+    @ExcelProperty(value = "参数主键")
+    private Long configId;
+
+    /**
+     * 参数名称
+     */
+    @ExcelProperty(value = "参数名称")
+    private String configName;
+
+    /**
+     * 参数键名
+     */
+    @ExcelProperty(value = "参数键名")
+    private String configKey;
+
+    /**
+     * 参数键值
+     */
+    @ExcelProperty(value = "参数键值")
+    private String configValue;
+
+    /**
+     * 系统内置(Y是 N否)
+     */
+    @ExcelProperty(value = "系统内置", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_yes_no")
+    private String configType;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+}

+ 102 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysDeptVo.java

@@ -0,0 +1,102 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysDept;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 部门视图对象 sys_dept
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysDept.class)
+public class SysDeptVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 部门id
+     */
+    @ExcelProperty(value = "部门id")
+    private Long deptId;
+
+    /**
+     * 父部门id
+     */
+    private Long parentId;
+
+    /**
+     * 父部门名称
+     */
+    private String parentName;
+
+    /**
+     * 祖级列表
+     */
+    private String ancestors;
+
+    /**
+     * 部门名称
+     */
+    @ExcelProperty(value = "部门名称")
+    private String deptName;
+
+    /**
+     * 部门类别编码
+     */
+    @ExcelProperty(value = "部门类别编码")
+    private String deptCategory;
+
+    /**
+     * 显示顺序
+     */
+    private Integer orderNum;
+
+    /**
+     * 负责人ID
+     */
+    private Long leader;
+
+    /**
+     * 负责人
+     */
+    @ExcelProperty(value = "负责人")
+    private String leaderName;
+
+    /**
+     * 联系电话
+     */
+    @ExcelProperty(value = "联系电话")
+    private String phone;
+
+    /**
+     * 邮箱
+     */
+    @ExcelProperty(value = "邮箱")
+    private String email;
+
+    /**
+     * 部门状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "部门状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_normal_disable")
+    private String status;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+}

+ 88 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysDictDataVo.java

@@ -0,0 +1,88 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysDictData;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 字典数据视图对象 sys_dict_data
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysDictData.class)
+public class SysDictDataVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 字典编码
+     */
+    @ExcelProperty(value = "字典编码")
+    private Long dictCode;
+
+    /**
+     * 字典排序
+     */
+    @ExcelProperty(value = "字典排序")
+    private Integer dictSort;
+
+    /**
+     * 字典标签
+     */
+    @ExcelProperty(value = "字典标签")
+    private String dictLabel;
+
+    /**
+     * 字典键值
+     */
+    @ExcelProperty(value = "字典键值")
+    private String dictValue;
+
+    /**
+     * 字典类型
+     */
+    @ExcelProperty(value = "字典类型")
+    private String dictType;
+
+    /**
+     * 样式属性(其他样式扩展)
+     */
+    private String cssClass;
+
+    /**
+     * 表格回显样式
+     */
+    private String listClass;
+
+    /**
+     * 是否默认(Y是 N否)
+     */
+    @ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_yes_no")
+    private String isDefault;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+}

+ 59 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysDictTypeVo.java

@@ -0,0 +1,59 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysDictType;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 字典类型视图对象 sys_dict_type
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysDictType.class)
+public class SysDictTypeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 字典主键
+     */
+    @ExcelProperty(value = "字典主键")
+    private Long dictId;
+
+    /**
+     * 字典名称
+     */
+    @ExcelProperty(value = "字典名称")
+    private String dictName;
+
+    /**
+     * 字典类型
+     */
+    @ExcelProperty(value = "字典类型")
+    private String dictType;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+}

+ 106 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysLogininforVo.java

@@ -0,0 +1,106 @@
+package org.dromara.subject.domain.vo;
+
+import java.util.Date;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysLogininfor;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+
+/**
+ * 系统访问记录视图对象 sys_logininfor
+ *
+ * @author Michelle.Chung
+ * @date 2023-02-07
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysLogininfor.class)
+public class SysLogininforVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 访问ID
+     */
+    @ExcelProperty(value = "序号")
+    private Long infoId;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 用户账号
+     */
+    @ExcelProperty(value = "用户账号")
+    private String userName;
+
+    /**
+     * 客户端
+     */
+    @ExcelProperty(value = "客户端")
+    private String clientKey;
+
+    /**
+     * 设备类型
+     */
+    @ExcelProperty(value = "设备类型", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_device_type")
+    private String deviceType;
+
+    /**
+     * 登录状态(0成功 1失败)
+     */
+    @ExcelProperty(value = "登录状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_common_status")
+    private String status;
+
+    /**
+     * 登录IP地址
+     */
+    @ExcelProperty(value = "登录地址")
+    private String ipaddr;
+
+    /**
+     * 登录地点
+     */
+    @ExcelProperty(value = "登录地点")
+    private String loginLocation;
+
+    /**
+     * 浏览器类型
+     */
+    @ExcelProperty(value = "浏览器")
+    private String browser;
+
+    /**
+     * 操作系统
+     */
+    @ExcelProperty(value = "操作系统")
+    private String os;
+
+
+    /**
+     * 提示消息
+     */
+    @ExcelProperty(value = "提示消息")
+    private String msg;
+
+    /**
+     * 访问时间
+     */
+    @ExcelProperty(value = "访问时间")
+    private Date loginTime;
+
+
+}

+ 116 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysMenuVo.java

@@ -0,0 +1,116 @@
+package org.dromara.subject.domain.vo;
+
+import org.dromara.subject.domain.SysMenu;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * 菜单权限视图对象 sys_menu
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@AutoMapper(target = SysMenu.class)
+public class SysMenuVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 菜单ID
+     */
+    private Long menuId;
+
+    /**
+     * 菜单名称
+     */
+    private String menuName;
+
+    /**
+     * 父菜单ID
+     */
+    private Long parentId;
+
+    /**
+     * 显示顺序
+     */
+    private Integer orderNum;
+
+    /**
+     * 路由地址
+     */
+    private String path;
+
+    /**
+     * 组件路径
+     */
+    private String component;
+
+    /**
+     * 路由参数
+     */
+    private String queryParam;
+
+    /**
+     * 是否为外链(0是 1否)
+     */
+    private String isFrame;
+
+    /**
+     * 是否缓存(0缓存 1不缓存)
+     */
+    private String isCache;
+
+    /**
+     * 菜单类型(M目录 C菜单 F按钮)
+     */
+    private String menuType;
+
+    /**
+     * 显示状态(0显示 1隐藏)
+     */
+    private String visible;
+
+    /**
+     * 菜单状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 权限标识
+     */
+    private String perms;
+
+    /**
+     * 菜单图标
+     */
+    private String icon;
+
+    /**
+     * 创建部门
+     */
+    private Long createDept;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 子菜单
+     */
+    private List<SysMenuVo> children = new ArrayList<>();
+
+}

+ 73 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysNoticeVo.java

@@ -0,0 +1,73 @@
+package org.dromara.subject.domain.vo;
+
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.subject.domain.SysNotice;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * 通知公告视图对象 sys_notice
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@AutoMapper(target = SysNotice.class)
+public class SysNoticeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 公告ID
+     */
+    private Long noticeId;
+
+    /**
+     * 公告标题
+     */
+    private String noticeTitle;
+
+    /**
+     * 公告类型(1通知 2公告)
+     */
+    private String noticeType;
+
+    /**
+     * 公告内容
+     */
+    private String noticeContent;
+
+    /**
+     * 公告状态(0正常 1关闭)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建者
+     */
+    private Long createBy;
+
+    /**
+     * 创建人名称
+     */
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
+    private String createByName;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+}

+ 144 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysOperLogVo.java

@@ -0,0 +1,144 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysOperLog;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 操作日志记录视图对象 sys_oper_log
+ *
+ * @author Michelle.Chung
+ * @date 2023-02-07
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysOperLog.class)
+public class SysOperLogVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 日志主键
+     */
+    @ExcelProperty(value = "日志主键")
+    private Long operId;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 模块标题
+     */
+    @ExcelProperty(value = "操作模块")
+    private String title;
+
+    /**
+     * 业务类型(0其它 1新增 2修改 3删除)
+     */
+    @ExcelProperty(value = "业务类型", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_oper_type")
+    private Integer businessType;
+
+    /**
+     * 业务类型数组
+     */
+    private Integer[] businessTypes;
+
+    /**
+     * 方法名称
+     */
+    @ExcelProperty(value = "请求方法")
+    private String method;
+
+    /**
+     * 请求方式
+     */
+    @ExcelProperty(value = "请求方式")
+    private String requestMethod;
+
+    /**
+     * 操作类别(0其它 1后台用户 2手机端用户)
+     */
+    @ExcelProperty(value = "操作类别", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=其它,1=后台用户,2=手机端用户")
+    private Integer operatorType;
+
+    /**
+     * 操作人员
+     */
+    @ExcelProperty(value = "操作人员")
+    private String operName;
+
+    /**
+     * 部门名称
+     */
+    @ExcelProperty(value = "部门名称")
+    private String deptName;
+
+    /**
+     * 请求URL
+     */
+    @ExcelProperty(value = "请求地址")
+    private String operUrl;
+
+    /**
+     * 主机地址
+     */
+    @ExcelProperty(value = "操作地址")
+    private String operIp;
+
+    /**
+     * 操作地点
+     */
+    @ExcelProperty(value = "操作地点")
+    private String operLocation;
+
+    /**
+     * 请求参数
+     */
+    @ExcelProperty(value = "请求参数")
+    private String operParam;
+
+    /**
+     * 返回参数
+     */
+    @ExcelProperty(value = "返回参数")
+    private String jsonResult;
+
+    /**
+     * 操作状态(0正常 1异常)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_common_status")
+    private Integer status;
+
+    /**
+     * 错误消息
+     */
+    @ExcelProperty(value = "错误消息")
+    private String errorMsg;
+
+    /**
+     * 操作时间
+     */
+    @ExcelProperty(value = "操作时间")
+    private Date operTime;
+
+    /**
+     * 消耗时间
+     */
+    @ExcelProperty(value = "消耗时间")
+    private Long costTime;
+}

+ 97 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysOssConfigVo.java

@@ -0,0 +1,97 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import org.dromara.subject.domain.SysOssConfig;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 对象存储配置视图对象 sys_oss_config
+ *
+ * @author Lion Li
+ * @author 孤舟烟雨
+ * @date 2021-08-13
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysOssConfig.class)
+public class SysOssConfigVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    private Long ossConfigId;
+
+    /**
+     * 配置key
+     */
+    private String configKey;
+
+    /**
+     * accessKey
+     */
+    private String accessKey;
+
+    /**
+     * 秘钥
+     */
+    private String secretKey;
+
+    /**
+     * 桶名称
+     */
+    private String bucketName;
+
+    /**
+     * 前缀
+     */
+    private String prefix;
+
+    /**
+     * 访问站点
+     */
+    private String endpoint;
+
+    /**
+     * 自定义域名
+     */
+    private String domain;
+
+    /**
+     * 是否https(Y=是,N=否)
+     */
+    private String isHttps;
+
+    /**
+     * 域
+     */
+    private String region;
+
+    /**
+     * 是否默认(0=是,1=否)
+     */
+    private String status;
+
+    /**
+     * 扩展字段
+     */
+    private String ext1;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 桶权限类型(0private 1public 2custom)
+     */
+    private String accessPolicy;
+
+}

+ 28 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysOssUploadVo.java

@@ -0,0 +1,28 @@
+package org.dromara.subject.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 上传对象信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class SysOssUploadVo {
+
+    /**
+     * URL地址
+     */
+    private String url;
+
+    /**
+     * 文件名
+     */
+    private String fileName;
+
+    /**
+     * 对象存储主键
+     */
+    private String ossId;
+
+}

+ 72 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysOssVo.java

@@ -0,0 +1,72 @@
+package org.dromara.subject.domain.vo;
+
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.subject.domain.SysOss;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * OSS对象存储视图对象 sys_oss
+ *
+ * @author Lion Li
+ */
+@Data
+@AutoMapper(target = SysOss.class)
+public class SysOssVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 对象存储主键
+     */
+    private Long ossId;
+
+    /**
+     * 文件名
+     */
+    private String fileName;
+
+    /**
+     * 原名
+     */
+    private String originalName;
+
+    /**
+     * 文件后缀名
+     */
+    private String fileSuffix;
+
+    /**
+     * URL地址
+     */
+    private String url;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 上传人
+     */
+    private Long createBy;
+
+    /**
+     * 上传人名称
+     */
+    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
+    private String createByName;
+
+    /**
+     * 服务商
+     */
+    private String service;
+
+
+}

+ 91 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysPostVo.java

@@ -0,0 +1,91 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.subject.domain.SysPost;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 岗位信息视图对象 sys_post
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysPost.class)
+public class SysPostVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 岗位ID
+     */
+    @ExcelProperty(value = "岗位序号")
+    private Long postId;
+
+    /**
+     * 部门id
+     */
+    @ExcelProperty(value = "部门id")
+    private Long deptId;
+
+    /**
+     * 岗位编码
+     */
+    @ExcelProperty(value = "岗位编码")
+    private String postCode;
+
+    /**
+     * 岗位名称
+     */
+    @ExcelProperty(value = "岗位名称")
+    private String postName;
+
+    /**
+     * 岗位类别编码
+     */
+    @ExcelProperty(value = "类别编码")
+    private String postCategory;
+
+    /**
+     * 显示顺序
+     */
+    @ExcelProperty(value = "岗位排序")
+    private Integer postSort;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_normal_disable")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+    /**
+     * 部门名
+     */
+    @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId")
+    private String deptName;
+
+}

+ 100 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysRoleVo.java

@@ -0,0 +1,100 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysRole;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 角色信息视图对象 sys_role
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysRole.class)
+public class SysRoleVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 角色ID
+     */
+    @ExcelProperty(value = "角色序号")
+    private Long roleId;
+
+    /**
+     * 角色名称
+     */
+    @ExcelProperty(value = "角色名称")
+    private String roleName;
+
+    /**
+     * 角色权限字符串
+     */
+    @ExcelProperty(value = "角色权限")
+    private String roleKey;
+
+    /**
+     * 显示顺序
+     */
+    @ExcelProperty(value = "角色排序")
+    private Integer roleSort;
+
+    /**
+     * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)
+     */
+    @ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
+    private String dataScope;
+
+    /**
+     * 菜单树选择项是否关联显示
+     */
+    @ExcelProperty(value = "菜单树选择项是否关联显示")
+    private Boolean menuCheckStrictly;
+
+    /**
+     * 部门树选择项是否关联显示
+     */
+    @ExcelProperty(value = "部门树选择项是否关联显示")
+    private Boolean deptCheckStrictly;
+
+    /**
+     * 角色状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "角色状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_normal_disable")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+    /**
+     * 用户是否存在此角色标识 默认不存在
+     */
+    private boolean flag = false;
+
+    public boolean isSuperAdmin() {
+        return UserConstants.SUPER_ADMIN_ID.equals(this.roleId);
+    }
+
+}

+ 144 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysSocialVo.java

@@ -0,0 +1,144 @@
+package org.dromara.subject.domain.vo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.subject.domain.SysSocial;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 社会化关系视图对象 sys_social
+ *
+ * @author thiszhc
+ */
+@Data
+@AutoMapper(target = SysSocial.class)
+public class SysSocialVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 的唯一ID
+     */
+    private String authId;
+
+    /**
+     * 用户来源
+     */
+    private String source;
+
+    /**
+     * 用户的授权令牌
+     */
+    private String accessToken;
+
+    /**
+     * 用户的授权令牌的有效期,部分平台可能没有
+     */
+    private int expireIn;
+
+    /**
+     * 刷新令牌,部分平台可能没有
+     */
+    private String refreshToken;
+
+    /**
+     * 用户的 open id
+     */
+    private String openId;
+
+    /**
+     * 授权的第三方账号
+     */
+    private String userName;
+
+    /**
+     * 授权的第三方昵称
+     */
+    private String nickName;
+
+    /**
+     * 授权的第三方邮箱
+     */
+    private String email;
+
+    /**
+     * 授权的第三方头像地址
+     */
+    private String avatar;
+
+
+    /**
+     * 平台的授权信息,部分平台可能没有
+     */
+    private String accessCode;
+
+    /**
+     * 用户的 unionid
+     */
+    private String unionId;
+
+    /**
+     * 授予的权限,部分平台可能没有
+     */
+    private String scope;
+
+    /**
+     * 个别平台的授权信息,部分平台可能没有
+     */
+    private String tokenType;
+
+    /**
+     * id token,部分平台可能没有
+     */
+    private String idToken;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macAlgorithm;
+
+    /**
+     * 小米平台用户的附带属性,部分平台可能没有
+     */
+    private String macKey;
+
+    /**
+     * 用户的授权code,部分平台可能没有
+     */
+    private String code;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthToken;
+
+    /**
+     * Twitter平台用户的附带属性,部分平台可能没有
+     */
+    private String oauthTokenSecret;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+}

+ 66 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysTenantPackageVo.java

@@ -0,0 +1,66 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysTenantPackage;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 租户套餐视图对象 sys_tenant_package
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysTenantPackage.class)
+public class SysTenantPackageVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 租户套餐id
+     */
+    @ExcelProperty(value = "租户套餐id")
+    private Long packageId;
+
+    /**
+     * 套餐名称
+     */
+    @ExcelProperty(value = "套餐名称")
+    private String packageName;
+
+    /**
+     * 关联菜单id
+     */
+    @ExcelProperty(value = "关联菜单id")
+    private String menuIds;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 菜单树选择项是否关联显示
+     */
+    @ExcelProperty(value = "菜单树选择项是否关联显示")
+    private Boolean menuCheckStrictly;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+
+}

+ 115 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysTenantVo.java

@@ -0,0 +1,115 @@
+package org.dromara.subject.domain.vo;
+
+import java.util.Date;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.subject.domain.SysTenant;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 租户视图对象 sys_tenant
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysTenant.class)
+public class SysTenantVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @ExcelProperty(value = "id")
+    private Long id;
+
+    /**
+     * 租户编号
+     */
+    @ExcelProperty(value = "租户编号")
+    private String tenantId;
+
+    /**
+     * 联系人
+     */
+    @ExcelProperty(value = "联系人")
+    private String contactUserName;
+
+    /**
+     * 联系电话
+     */
+    @ExcelProperty(value = "联系电话")
+    private String contactPhone;
+
+    /**
+     * 企业名称
+     */
+    @ExcelProperty(value = "企业名称")
+    private String companyName;
+
+    /**
+     * 统一社会信用代码
+     */
+    @ExcelProperty(value = "统一社会信用代码")
+    private String licenseNumber;
+
+    /**
+     * 地址
+     */
+    @ExcelProperty(value = "地址")
+    private String address;
+
+    /**
+     * 域名
+     */
+    @ExcelProperty(value = "域名")
+    private String domain;
+
+    /**
+     * 企业简介
+     */
+    @ExcelProperty(value = "企业简介")
+    private String intro;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 租户套餐编号
+     */
+    @ExcelProperty(value = "租户套餐编号")
+    private Long packageId;
+
+    /**
+     * 过期时间
+     */
+    @ExcelProperty(value = "过期时间")
+    private Date expireTime;
+
+    /**
+     * 用户数量(-1不限制)
+     */
+    @ExcelProperty(value = "用户数量")
+    private Long accountCount;
+
+    /**
+     * 租户状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "租户状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+
+}

+ 96 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysUserExportVo.java

@@ -0,0 +1,96 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import io.github.linpeilie.annotations.ReverseAutoMapping;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 用户对象导出VO
+ *
+ * @author Lion Li
+ */
+
+@Data
+@NoArgsConstructor
+public class SysUserExportVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    @ExcelProperty(value = "用户序号")
+    private Long userId;
+
+    /**
+     * 用户账号
+     */
+    @ExcelProperty(value = "登录名称")
+    private String userName;
+
+    /**
+     * 用户昵称
+     */
+    @ExcelProperty(value = "用户名称")
+    private String nickName;
+
+    /**
+     * 用户邮箱
+     */
+    @ExcelProperty(value = "用户邮箱")
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    @ExcelProperty(value = "手机号码")
+    private String phonenumber;
+
+    /**
+     * 用户性别
+     */
+    @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_user_sex")
+    private String sex;
+
+    /**
+     * 帐号状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_normal_disable")
+    private String status;
+
+    /**
+     * 最后登录IP
+     */
+    @ExcelProperty(value = "最后登录IP")
+    private String loginIp;
+
+    /**
+     * 最后登录时间
+     */
+    @ExcelProperty(value = "最后登录时间")
+    private Date loginDate;
+
+    /**
+     * 部门名称
+     */
+    @ExcelProperty(value = "部门名称")
+    private String deptName;
+
+    /**
+     * 负责人
+     */
+    @ExcelProperty(value = "部门负责人")
+    private String leaderName;
+
+}

+ 76 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysUserImportVo.java

@@ -0,0 +1,76 @@
+package org.dromara.subject.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 用户对象导入VO
+ *
+ * @author Lion Li
+ */
+
+@Data
+@NoArgsConstructor
+// @Accessors(chain = true) // 导入不允许使用 会找不到set方法
+public class SysUserImportVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    @ExcelProperty(value = "用户序号")
+    private Long userId;
+
+    /**
+     * 部门ID
+     */
+    @ExcelProperty(value = "部门编号")
+    private Long deptId;
+
+    /**
+     * 用户账号
+     */
+    @ExcelProperty(value = "登录名称")
+    private String userName;
+
+    /**
+     * 用户昵称
+     */
+    @ExcelProperty(value = "用户名称")
+    private String nickName;
+
+    /**
+     * 用户邮箱
+     */
+    @ExcelProperty(value = "用户邮箱")
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    @ExcelProperty(value = "手机号码")
+    private String phonenumber;
+
+    /**
+     * 用户性别
+     */
+    @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_user_sex")
+    private String sex;
+
+    /**
+     * 帐号状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(dictType = "sys_normal_disable")
+    private String status;
+
+}

+ 40 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysUserInfoVo.java

@@ -0,0 +1,40 @@
+package org.dromara.subject.domain.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 用户信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class SysUserInfoVo {
+
+    /**
+     * 用户信息
+     */
+    private SysUserVo user;
+
+    /**
+     * 角色ID列表
+     */
+    private List<Long> roleIds;
+
+    /**
+     * 角色列表
+     */
+    private List<SysRoleVo> roles;
+
+    /**
+     * 岗位ID列表
+     */
+    private List<Long> postIds;
+
+    /**
+     * 岗位列表
+     */
+    private List<SysPostVo> posts;
+
+}

+ 142 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/SysUserVo.java

@@ -0,0 +1,142 @@
+package org.dromara.subject.domain.vo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.dromara.common.sensitive.annotation.Sensitive;
+import org.dromara.common.sensitive.core.SensitiveStrategy;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.subject.domain.SysUser;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * 用户信息视图对象 sys_user
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@AutoMapper(target = SysUser.class)
+public class SysUserVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 用户账号
+     */
+    private String userName;
+
+    /**
+     * 用户昵称
+     */
+    private String nickName;
+
+    /**
+     * 用户类型(sys_user系统用户)
+     */
+    private String userType;
+
+    /**
+     * 用户邮箱
+     */
+    @Sensitive(strategy = SensitiveStrategy.EMAIL, perms = "system:user:edit")
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    @Sensitive(strategy = SensitiveStrategy.PHONE, perms = "system:user:edit")
+    private String phonenumber;
+
+    /**
+     * 用户性别(0男 1女 2未知)
+     */
+    private String sex;
+
+    /**
+     * 头像地址
+     */
+    @Translation(type = TransConstant.OSS_ID_TO_URL)
+    private Long avatar;
+
+    /**
+     * 密码
+     */
+    @JsonIgnore
+    @JsonProperty
+    private String password;
+
+    /**
+     * 帐号状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 最后登录IP
+     */
+    private String loginIp;
+
+    /**
+     * 最后登录时间
+     */
+    private Date loginDate;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 部门名
+     */
+    @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId")
+    private String deptName;
+
+    /**
+     * 角色对象
+     */
+    private List<SysRoleVo> roles;
+
+    /**
+     * 角色组
+     */
+    private Long[] roleIds;
+
+    /**
+     * 岗位组
+     */
+    private Long[] postIds;
+
+    /**
+     * 数据权限 当前角色ID
+     */
+    private Long roleId;
+
+}

+ 30 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/domain/vo/UserInfoVo.java

@@ -0,0 +1,30 @@
+package org.dromara.subject.domain.vo;
+
+import lombok.Data;
+
+import java.util.Set;
+
+/**
+ * 登录用户信息
+ *
+ * @author Michelle.Chung
+ */
+@Data
+public class UserInfoVo {
+
+    /**
+     * 用户基本信息
+     */
+    private SysUserVo user;
+
+    /**
+     * 菜单权限
+     */
+    private Set<String> permissions;
+
+    /**
+     * 角色权限
+     */
+    private Set<String> roles;
+
+}

+ 127 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/listener/SysUserImportListener.java

@@ -0,0 +1,127 @@
+package org.dromara.subject.listener;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.crypto.digest.BCrypt;
+import cn.hutool.http.HtmlUtil;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.ValidatorUtils;
+import org.dromara.common.excel.core.ExcelListener;
+import org.dromara.common.excel.core.ExcelResult;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.subject.domain.bo.SysUserBo;
+import org.dromara.subject.domain.vo.SysUserImportVo;
+import org.dromara.subject.domain.vo.SysUserVo;
+import org.dromara.subject.service.ISysConfigService;
+import org.dromara.subject.service.ISysUserService;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+/**
+ * 系统用户自定义导入
+ *
+ * @author Lion Li
+ */
+@Slf4j
+public class SysUserImportListener extends AnalysisEventListener<SysUserImportVo> implements ExcelListener<SysUserImportVo> {
+
+    private final ISysUserService userService;
+
+    private final String password;
+
+    private final Boolean isUpdateSupport;
+
+    private final Long operUserId;
+
+    private int successNum = 0;
+    private int failureNum = 0;
+    private final StringBuilder successMsg = new StringBuilder();
+    private final StringBuilder failureMsg = new StringBuilder();
+
+    public SysUserImportListener(Boolean isUpdateSupport) {
+        String initPassword = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.user.initPassword");
+        this.userService = SpringUtils.getBean(ISysUserService.class);
+        this.password = BCrypt.hashpw(initPassword);
+        this.isUpdateSupport = isUpdateSupport;
+        this.operUserId = LoginHelper.getUserId();
+    }
+
+    @Override
+    public void invoke(SysUserImportVo userVo, AnalysisContext context) {
+        SysUserVo sysUser = this.userService.selectUserByUserName(userVo.getUserName());
+        try {
+            // 验证是否存在这个用户
+            if (ObjectUtil.isNull(sysUser)) {
+                SysUserBo user = BeanUtil.toBean(userVo, SysUserBo.class);
+                ValidatorUtils.validate(user);
+                user.setPassword(password);
+                user.setCreateBy(operUserId);
+                userService.insertUser(user);
+                successNum++;
+                successMsg.append("<br/>").append(successNum).append("、账号 ").append(user.getUserName()).append(" 导入成功");
+            } else if (isUpdateSupport) {
+                Long userId = sysUser.getUserId();
+                SysUserBo user = BeanUtil.toBean(userVo, SysUserBo.class);
+                user.setUserId(userId);
+                ValidatorUtils.validate(user);
+                userService.checkUserAllowed(user.getUserId());
+                userService.checkUserDataScope(user.getUserId());
+                user.setUpdateBy(operUserId);
+                userService.updateUser(user);
+                successNum++;
+                successMsg.append("<br/>").append(successNum).append("、账号 ").append(user.getUserName()).append(" 更新成功");
+            } else {
+                failureNum++;
+                failureMsg.append("<br/>").append(failureNum).append("、账号 ").append(sysUser.getUserName()).append(" 已存在");
+            }
+        } catch (Exception e) {
+            failureNum++;
+            String msg = "<br/>" + failureNum + "、账号 " + HtmlUtil.cleanHtmlTag(userVo.getUserName()) + " 导入失败:";
+            String message = e.getMessage();
+            if (e instanceof ConstraintViolationException cvException) {
+                message = StreamUtils.join(cvException.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
+            }
+            failureMsg.append(msg).append(message);
+            log.error(msg, e);
+        }
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+
+    }
+
+    @Override
+    public ExcelResult<SysUserImportVo> getExcelResult() {
+        return new ExcelResult<>() {
+
+            @Override
+            public String getAnalysis() {
+                if (failureNum > 0) {
+                    failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
+                    throw new ServiceException(failureMsg.toString());
+                } else {
+                    successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
+                }
+                return successMsg.toString();
+            }
+
+            @Override
+            public List<SysUserImportVo> getList() {
+                return null;
+            }
+
+            @Override
+            public List<String> getErrorList() {
+                return null;
+            }
+        };
+    }
+}

+ 15 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysClientMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.subject.mapper;
+
+import org.dromara.subject.domain.vo.SysClientVo;
+import org.dromara.subject.domain.SysClient;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 授权管理Mapper接口
+ *
+ * @author Michelle.Chung
+ * @date 2023-05-15
+ */
+public interface SysClientMapper extends BaseMapperPlus<SysClient, SysClientVo> {
+
+}

+ 14 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysConfigMapper.java

@@ -0,0 +1,14 @@
+package org.dromara.subject.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.subject.domain.vo.SysConfigVo;
+import org.dromara.subject.domain.SysConfig;
+
+/**
+ * 参数配置 数据层
+ *
+ * @author Lion Li
+ */
+public interface SysConfigMapper extends BaseMapperPlus<SysConfig, SysConfigVo> {
+
+}

+ 46 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysDeptMapper.java

@@ -0,0 +1,46 @@
+package org.dromara.subject.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.dromara.common.mybatis.annotation.DataColumn;
+import org.dromara.common.mybatis.annotation.DataPermission;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.subject.domain.vo.SysDeptVo;
+import org.dromara.subject.domain.SysDept;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 部门管理 数据层
+ *
+ * @author Lion Li
+ */
+public interface SysDeptMapper extends BaseMapperPlus<SysDept, SysDeptVo> {
+
+    /**
+     * 查询部门管理数据
+     *
+     * @param queryWrapper 查询条件
+     * @return 部门信息集合
+     */
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id")
+    })
+    List<SysDeptVo> selectDeptList(@Param(Constants.WRAPPER) Wrapper<SysDept> queryWrapper);
+
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id")
+    })
+    long countDeptById(Long deptId);
+
+    /**
+     * 根据角色ID查询部门树信息
+     *
+     * @param roleId            角色ID
+     * @param deptCheckStrictly 部门树选择项是否关联显示
+     * @return 选中部门列表
+     */
+    List<Long> selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly);
+
+}

+ 23 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysDictDataMapper.java

@@ -0,0 +1,23 @@
+package org.dromara.subject.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.subject.domain.SysDictData;
+import org.dromara.subject.domain.vo.SysDictDataVo;
+
+import java.util.List;
+
+/**
+ * 字典表 数据层
+ *
+ * @author Lion Li
+ */
+public interface SysDictDataMapper extends BaseMapperPlus<SysDictData, SysDictDataVo> {
+
+    default List<SysDictDataVo> selectDictDataByType(String dictType) {
+        return selectVoList(
+            new LambdaQueryWrapper<SysDictData>()
+                .eq(SysDictData::getDictType, dictType)
+                .orderByAsc(SysDictData::getDictSort));
+    }
+}

+ 14 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysDictTypeMapper.java

@@ -0,0 +1,14 @@
+package org.dromara.subject.mapper;
+
+import org.dromara.subject.domain.SysDictType;
+import org.dromara.subject.domain.vo.SysDictTypeVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 字典表 数据层
+ *
+ * @author Lion Li
+ */
+public interface SysDictTypeMapper extends BaseMapperPlus<SysDictType, SysDictTypeVo> {
+
+}

+ 14 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysLogininforMapper.java

@@ -0,0 +1,14 @@
+package org.dromara.subject.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.subject.domain.SysLogininfor;
+import org.dromara.subject.domain.vo.SysLogininforVo;
+
+/**
+ * 系统访问日志情况信息 数据层
+ *
+ * @author Lion Li
+ */
+public interface SysLogininforMapper extends BaseMapperPlus<SysLogininfor, SysLogininforVo> {
+
+}

+ 76 - 0
ruoyi-modules/ruoyi-subject/src/main/java/org/dromara/subject/mapper/SysMenuMapper.java

@@ -0,0 +1,76 @@
+package org.dromara.subject.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import org.dromara.common.core.constant.UserConstants;
+import org.dromara.subject.domain.SysMenu;
+import org.dromara.subject.domain.vo.SysMenuVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 菜单表 数据层
+ *
+ * @author Lion Li
+ */
+public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> {
+
+    /**
+     * 根据用户查询系统菜单列表
+     *
+     * @param queryWrapper 查询条件
+     * @return 菜单列表
+     */
+    List<SysMenu> selectMenuListByUserId(@Param(Constants.WRAPPER) Wrapper<SysMenu> queryWrapper);
+
+    /**
+     * 根据用户ID查询权限
+     *
+     * @param userId 用户ID
+     * @return 权限列表
+     */
+    List<String> selectMenuPermsByUserId(Long userId);
+
+    /**
+     * 根据角色ID查询权限
+     *
+     * @param roleId 角色ID
+     * @return 权限列表
+     */
+    List<String> selectMenuPermsByRoleId(Long roleId);
+
+    /**
+     * 根据用户ID查询菜单
+     *
+     * @return 菜单列表
+     */
+    default List<SysMenu> selectMenuTreeAll() {
+        LambdaQueryWrapper<SysMenu> lqw = new LambdaQueryWrapper<SysMenu>()
+            .in(SysMenu::getMenuType, UserConstants.TYPE_DIR, UserConstants.TYPE_MENU)
+            .eq(SysMenu::getStatus, UserConstants.MENU_NORMAL)
+            .orderByAsc(SysMenu::getParentId)
+            .orderByAsc(SysMenu::getOrderNum);
+        return this.selectList(lqw);
+    }
+
+    /**
+     * 根据用户ID查询菜单
+     *
+     * @param userId 用户ID
+     * @return 菜单列表
+     */
+    List<SysMenu> selectMenuTreeByUserId(Long userId);
+
+    /**
+     * 根据角色ID查询菜单树信息
+     *
+     * @param roleId            角色ID
+     * @param menuCheckStrictly 菜单树选择项是否关联显示
+     * @return 选中菜单列表
+     */
+    List<Long> selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly);
+
+}

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov