From e5c5bcd8f8f63c2444a4f9aaa6194dcbb2715685 Mon Sep 17 00:00:00 2001 From: atlus Date: Thu, 8 Sep 2022 21:58:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0ruoyi=20-ue=E8=87=B3=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E7=89=88=E6=9C=AC-20220908?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 +- .../controller/common/CaptchaController.java | 6 +- .../controller/monitor/CacheController.java | 5 +- .../monitor/SysLogininforController.java | 15 +- .../controller/system/SysDeptController.java | 35 +- .../system/SysProfileController.java | 2 + .../controller/system/SysRoleController.java | 22 +- .../controller/system/SysUserController.java | 15 + .../src/main/resources/application-druid.yml | 2 +- .../src/main/resources/application.yml | 10 +- .../main/resources/i18n/messages.properties | 2 +- .../ruoyi/common/annotation/DataScope.java | 5 + .../com/ruoyi/common/annotation/Excel.java | 40 ++- .../ruoyi/common/constant/CacheConstants.java | 5 + .../com/ruoyi/common/constant/Constants.java | 5 + .../core/domain/entity/SysDictData.java | 2 +- .../common/core/domain/entity/SysRole.java | 16 +- .../ruoyi/common/core/redis/RedisCache.java | 50 ++- .../com/ruoyi/common/core/text/Convert.java | 4 +- .../common/exception/GlobalException.java | 2 +- .../common/exception/ServiceException.java | 1 + ...UserPasswordRetryLimitExceedException.java | 16 + .../filter/PropertyPreExcludeFilter.java | 24 ++ .../filter/RepeatedlyRequestWrapper.java | 7 +- .../com/ruoyi/common/filter/XssFilter.java | 3 +- .../com/ruoyi/common/utils/StringUtils.java | 26 ++ .../ruoyi/common/utils/html/HTMLFilter.java | 2 +- .../com/ruoyi/common/utils/poi/ExcelUtil.java | 322 +++++++++++++++--- .../framework/aspectj/DataScopeAspect.java | 26 +- .../ruoyi/framework/aspectj/LogAspect.java | 16 +- .../framework/config/KaptchaTextCreator.java | 15 +- .../context/AuthenticationContextHolder.java | 28 ++ .../context/PermissionContextHolder.java | 27 ++ .../web/service/PermissionService.java | 3 + .../web/service/SysLoginService.java | 14 +- .../web/service/SysPasswordService.java | 94 +++++ .../web/service/SysPermissionService.java | 18 +- .../web/service/SysRegisterService.java | 4 +- .../web/service/UserDetailsServiceImpl.java | 5 + .../src/main/resources/vm/vue/v3/index.vue.vm | 10 +- .../quartz/controller/SysJobController.java | 6 +- .../ruoyi/quartz/service/ISysJobService.java | 2 +- .../service/impl/SysJobServiceImpl.java | 11 +- .../com/ruoyi/quartz/util/JobInvokeUtil.java | 4 +- .../com/ruoyi/quartz/util/ScheduleUtils.java | 7 +- .../ruoyi/system/mapper/SysMenuMapper.java | 8 + .../system/service/ISysConfigService.java | 2 +- .../ruoyi/system/service/ISysDeptService.java | 8 + .../ruoyi/system/service/ISysMenuService.java | 8 + .../service/impl/SysConfigServiceImpl.java | 8 +- .../service/impl/SysDeptServiceImpl.java | 13 + .../service/impl/SysMenuServiceImpl.java | 25 +- .../resources/mapper/system/SysDeptMapper.xml | 2 +- .../resources/mapper/system/SysMenuMapper.xml | 7 + .../resources/mapper/system/SysRoleMapper.xml | 6 +- .../resources/mapper/system/SysUserMapper.xml | 10 +- ruoyi-ui/src/api/monitor/logininfor.js | 8 + ruoyi-ui/src/api/system/dept.js | 16 - ruoyi-ui/src/api/system/role.js | 10 +- ruoyi-ui/src/api/system/user.js | 8 + ruoyi-ui/src/assets/styles/transition.scss | 5 + ruoyi-ui/src/components/DictData/index.js | 30 +- ruoyi-ui/src/components/FileUpload/index.vue | 28 +- .../src/components/ImagePreview/index.vue | 8 +- ruoyi-ui/src/components/ImageUpload/index.vue | 58 ++-- ruoyi-ui/src/components/RightPanel/index.vue | 43 +-- .../src/components/RightToolbar/index.vue | 21 +- ruoyi-ui/src/layout/components/AppMain.vue | 10 +- .../layout/components/IframeToggle/index.vue | 24 ++ .../src/layout/components/InnerLink/index.vue | 62 ++-- .../src/layout/components/Settings/index.vue | 139 ++++---- .../src/layout/components/TagsView/index.vue | 6 + ruoyi-ui/src/store/getters.js | 1 + ruoyi-ui/src/store/index.js | 2 + ruoyi-ui/src/store/modules/dict.js | 50 +++ ruoyi-ui/src/store/modules/tagsView.js | 45 ++- ruoyi-ui/src/views/index.vue | 4 +- ruoyi-ui/src/views/login.vue | 10 +- .../src/views/monitor/logininfor/index.vue | 28 +- ruoyi-ui/src/views/register.vue | 10 +- ruoyi-ui/src/views/system/dict/data.vue | 3 + ruoyi-ui/src/views/system/dict/index.vue | 1 + ruoyi-ui/src/views/system/menu/index.vue | 14 +- ruoyi-ui/src/views/system/role/index.vue | 17 +- ruoyi-ui/src/views/system/user/index.vue | 11 +- .../src/views/system/user/profile/index.vue | 2 +- .../views/system/user/profile/resetPwd.vue | 2 +- sql/{ry_20220625.sql => ry_20220822.sql} | 59 ++-- 88 files changed, 1319 insertions(+), 453 deletions(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java create mode 100644 ruoyi-ui/src/layout/components/IframeToggle/index.vue create mode 100644 ruoyi-ui/src/store/modules/dict.js rename sql/{ry_20220625.sql => ry_20220822.sql} (96%) diff --git a/pom.xml b/pom.xml index f3a43d4..550814c 100644 --- a/pom.xml +++ b/pom.xml @@ -23,9 +23,9 @@ 3.0.0 2.3.2 2.2.2 - 1.4.1 - 2.0.8 - 6.1.6 + 1.4.3 + 2.0.12 + 6.2.2 2.11.0 1.4 3.2.2 diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java index 7bdfb51..d2d6e8c 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java @@ -46,9 +46,9 @@ public class CaptchaController public AjaxResult getCode(HttpServletResponse response) throws IOException { AjaxResult ajax = AjaxResult.success(); - boolean captchaOnOff = configService.selectCaptchaOnOff(); - ajax.put("captchaOnOff", captchaOnOff); - if (!captchaOnOff) + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) { return ajax; } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java index d59ad45..69470d0 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -41,6 +41,7 @@ public class CacheController caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码")); caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交")); caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理")); + caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); } @PreAuthorize("@ss.hasPermi('monitor:cache:list')") @@ -78,8 +79,8 @@ public class CacheController @GetMapping("/getKeys/{cacheName}") public AjaxResult getCacheKeys(@PathVariable String cacheName) { - Set cacheKyes = redisTemplate.keys(cacheName + "*"); - return AjaxResult.success(cacheKyes); + Set cacheKeys = redisTemplate.keys(cacheName + "*"); + return AjaxResult.success(cacheKeys); } @PreAuthorize("@ss.hasPermi('monitor:cache:list')") diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java index 41dcf98..e0175f4 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -16,6 +16,7 @@ import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPasswordService; import com.ruoyi.system.domain.SysLogininfor; import com.ruoyi.system.service.ISysLogininforService; @@ -31,6 +32,9 @@ public class SysLogininforController extends BaseController @Autowired private ISysLogininforService logininforService; + @Autowired + private SysPasswordService passwordService; + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") @GetMapping("/list") public TableDataInfo list(SysLogininfor logininfor) @@ -64,6 +68,15 @@ public class SysLogininforController extends BaseController public AjaxResult clean() { logininforService.cleanLogininfor(); - return AjaxResult.success(); + return success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java index 867ac71..b74fece 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -1,6 +1,5 @@ package com.ruoyi.web.controller.system; -import java.util.Iterator; import java.util.List; import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -54,16 +53,7 @@ public class SysDeptController extends BaseController public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) { List depts = deptService.selectDeptList(new SysDept()); - Iterator it = depts.iterator(); - while (it.hasNext()) - { - SysDept d = (SysDept) it.next(); - if (d.getDeptId().intValue() == deptId - || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")) - { - it.remove(); - } - } + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); return AjaxResult.success(depts); } @@ -78,29 +68,6 @@ public class SysDeptController extends BaseController return AjaxResult.success(deptService.selectDeptById(deptId)); } - /** - * 获取部门下拉树列表 - */ - @GetMapping("/treeselect") - public AjaxResult treeselect(SysDept dept) - { - List depts = deptService.selectDeptList(dept); - return AjaxResult.success(deptService.buildDeptTreeSelect(depts)); - } - - /** - * 加载对应角色部门列表树 - */ - @GetMapping(value = "/roleDeptTreeselect/{roleId}") - public AjaxResult roleDeptTreeselect(@PathVariable("roleId") Long roleId) - { - List depts = deptService.selectDeptList(new SysDept()); - AjaxResult ajax = AjaxResult.success(); - ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); - ajax.put("depts", deptService.buildDeptTreeSelect(depts)); - return ajax; - } - /** * 新增部门 */ diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java index 7ea5a1f..7b34da2 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -75,6 +75,8 @@ public class SysProfileController extends BaseController } user.setUserId(sysUser.getUserId()); user.setPassword(null); + user.setAvatar(null); + user.setDeptId(null); if (userService.updateUserProfile(user) > 0) { // 更新缓存用户信息 diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java index 2abec1b..a479544 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -17,6 +17,7 @@ import com.ruoyi.common.annotation.Log; import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.core.domain.entity.SysRole; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; @@ -27,6 +28,7 @@ import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.web.service.SysPermissionService; import com.ruoyi.framework.web.service.TokenService; import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.service.ISysDeptService; import com.ruoyi.system.service.ISysRoleService; import com.ruoyi.system.service.ISysUserService; @@ -44,13 +46,16 @@ public class SysRoleController extends BaseController @Autowired private TokenService tokenService; - + @Autowired private SysPermissionService permissionService; - + @Autowired private ISysUserService userService; + @Autowired + private ISysDeptService deptService; + @PreAuthorize("@ss.hasPermi('system:role:list')") @GetMapping("/list") public TableDataInfo list(SysRole role) @@ -242,4 +247,17 @@ public class SysRoleController extends BaseController roleService.checkRoleDataScope(roleId); return toAjax(roleService.insertAuthUsers(roleId, userIds)); } + + /** + * 获取对应角色部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/deptTree/{roleId}") + public AjaxResult deptTree(@PathVariable("roleId") Long roleId) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return ajax; + } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java index 089f711..42e2b2d 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -20,6 +20,7 @@ import com.ruoyi.common.annotation.Log; import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.core.domain.entity.SysRole; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.page.TableDataInfo; @@ -27,6 +28,7 @@ import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDeptService; import com.ruoyi.system.service.ISysPostService; import com.ruoyi.system.service.ISysRoleService; import com.ruoyi.system.service.ISysUserService; @@ -46,6 +48,9 @@ public class SysUserController extends BaseController @Autowired private ISysRoleService roleService; + @Autowired + private ISysDeptService deptService; + @Autowired private ISysPostService postService; @@ -234,4 +239,14 @@ public class SysUserController extends BaseController userService.insertUserAuth(userId, roleIds); return success(); } + + /** + * 获取部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/deptTree") + public AjaxResult deptTree(SysDept dept) + { + return AjaxResult.success(deptService.selectDeptTreeList(dept)); + } } diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml index 7444a05..e625d34 100644 --- a/ruoyi-admin/src/main/resources/application-druid.yml +++ b/ruoyi-admin/src/main/resources/application-druid.yml @@ -6,7 +6,7 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://10.100.0.114:3306/nocode?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true + url: jdbc:mysql://10.100.0.114:3306/mycode?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true username: root password: root # 从库数据源 diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index b73311f..fbdaf3c 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -39,6 +39,14 @@ logging: com.ruoyi: debug org.springframework: warn +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + # Spring配置 spring: # 资源信息 @@ -87,7 +95,7 @@ spring: port: 27017 username: admin password: admin - database: nocode + database: mycode authentication-database: admin activiti: check-process-definitions: false diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties index 71cf52d..4098fc9 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -5,7 +5,7 @@ user.jcaptcha.expire=验证码已失效 user.not.exists=用户不存在/密码错误 user.password.not.match=用户不存在/密码错误 user.password.retry.limit.count=密码输入错误{0}次 -user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 user.password.delete=对不起,您的账号已被删除 user.blocked=用户已封禁,请联系管理员 role.blocked=角色已封禁,请联系管理员 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java index 176878e..be49c80 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java @@ -25,4 +25,9 @@ public @interface DataScope * 用户表的别名 */ public String userAlias() default ""; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来 + */ + public String permission() default ""; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java index 6626986..f7aaca5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java @@ -88,6 +88,11 @@ public @interface Excel */ public String[] combo() default {}; + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + /** * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. */ @@ -104,12 +109,27 @@ public @interface Excel public boolean isStatistics() default false; /** - * 导出类型(0数字 1字符串) + * 导出类型(0数字 1字符串 2图片) */ public ColumnType cellType() default ColumnType.STRING; /** - * 导出字体颜色 + * 导出列头背景色 + */ + public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景色 + */ + public IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 */ public IndexedColors color() default IndexedColors.BLACK; @@ -128,22 +148,6 @@ public @interface Excel */ public String[] args() default {}; - public enum Align - { - AUTO(0), LEFT(1), CENTER(2), RIGHT(3); - private final int value; - - Align(int value) - { - this.value = value; - } - - public int value() - { - return this.value; - } - } - /** * 字段类型(0:导出导入;1:仅导出;2:仅导入) */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java index 7345fe4..0080343 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -36,4 +36,9 @@ public class CacheConstants * 限流 redis key */ public static final String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index bb549d0..0e9125f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -19,6 +19,11 @@ public class Constants */ public static final String GBK = "GBK"; + /** + * www主域 + */ + public static final String WWW = "www."; + /** * http请求 */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java index 3f152b3..738f12c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java @@ -131,7 +131,7 @@ public class SysDictData extends BaseEntity public boolean getDefault() { - return UserConstants.YES.equals(this.isDefault) ? true : false; + return UserConstants.YES.equals(this.isDefault); } public String getIsDefault() diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java index 36629eb..f4cc38b 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java @@ -1,5 +1,6 @@ package com.ruoyi.common.core.domain.entity; +import java.util.Set; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -59,6 +60,9 @@ public class SysRole extends BaseEntity /** 部门组(数据权限) */ private Long[] deptIds; + /** 角色菜单权限 */ + private Set permissions; + public SysRole() { @@ -203,7 +207,17 @@ public class SysRole extends BaseEntity { this.deptIds = deptIds; } - + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java index de4a9d4..44e80d8 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java @@ -74,6 +74,28 @@ public class RedisCache return redisTemplate.expire(key, timeout, unit); } + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + /** * 获得缓存的基本对象。 * @@ -102,9 +124,9 @@ public class RedisCache * @param collection 多个对象 * @return */ - public long deleteObject(final Collection collection) + public boolean deleteObject(final Collection collection) { - return redisTemplate.delete(collection); + return redisTemplate.delete(collection) > 0; } /** @@ -209,18 +231,6 @@ public class RedisCache return opsForHash.get(key, hKey); } - /** - * 删除Hash中的数据 - * - * @param key - * @param hKey - */ - public void delCacheMapValue(final String key, final String hKey) - { - HashOperations hashOperations = redisTemplate.opsForHash(); - hashOperations.delete(key, hKey); - } - /** * 获取多个Hash中的数据 * @@ -233,6 +243,18 @@ public class RedisCache return redisTemplate.opsForHash().multiGet(key, hKeys); } + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + /** * 获得缓存的基本对象列表 * diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java index 32200ee..b82321c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -894,7 +894,7 @@ public class Convert */ public static String toSBC(String input, Set notConvertSet) { - char c[] = input.toCharArray(); + char[] c = input.toCharArray(); for (int i = 0; i < c.length; i++) { if (null != notConvertSet && notConvertSet.contains(c[i])) @@ -936,7 +936,7 @@ public class Convert */ public static String toDBC(String text, Set notConvertSet) { - char c[] = text.toCharArray(); + char[] c = text.toCharArray(); for (int i = 0; i < c.length; i++) { if (null != notConvertSet && notConvertSet.contains(c[i])) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java index 211441b..81a71b5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java @@ -7,7 +7,6 @@ package com.ruoyi.common.exception; */ public class GlobalException extends RuntimeException { - private static final long serialVersionUID = 1L; /** @@ -45,6 +44,7 @@ public class GlobalException extends RuntimeException return this; } + @Override public String getMessage() { return message; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java index 6297f87..fcc7ab6 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java @@ -49,6 +49,7 @@ public final class ServiceException extends RuntimeException return detailMessage; } + @Override public String getMessage() { return message; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java new file mode 100644 index 0000000..c887cf1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户错误最大次数异常类 + * + * @author ruoyi + */ +public class UserPasswordRetryLimitExceedException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) + { + super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java new file mode 100644 index 0000000..e1e431b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author ruoyi + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter +{ + public PropertyPreExcludeFilter() + { + } + + public PropertyPreExcludeFilter addExcludes(String... filters) + { + for (int i = 0; i < filters.length; i++) + { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java index 614c24c..407d1ba 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -10,6 +10,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.common.constant.Constants; /** * 构建可重复读取inputStream的request @@ -23,10 +24,10 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException { super(request); - request.setCharacterEncoding("UTF-8"); - response.setCharacterEncoding("UTF-8"); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); - body = HttpHelper.getBodyString(request).getBytes("UTF-8"); + body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); } @Override diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java index 99323ed..9052f0d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -12,6 +12,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.enums.HttpMethod; /** * 防止XSS攻击的过滤器 @@ -59,7 +60,7 @@ public class XssFilter implements Filter String url = request.getServletPath(); String method = request.getMethod(); // GET DELETE 不过滤 - if (method == null || method.matches("GET") || method.matches("DELETE")) + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) { return true; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java index 6b70889..fe52025 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -324,6 +324,32 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils return list; } + /** + * 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param set 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + /** * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 * diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java index db069bc..ebff3fd 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java @@ -387,7 +387,7 @@ public final class HTMLFilter { paramValue = processParamProtocol(paramValue); } - params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\""); + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index 26a33ce..8ea91a3 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -7,12 +7,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.text.DecimalFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.HashMap; @@ -22,7 +24,9 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.poi.hssf.usermodel.HSSFClientAnchor; import org.apache.poi.hssf.usermodel.HSSFPicture; import org.apache.poi.hssf.usermodel.HSSFPictureData; @@ -148,6 +152,26 @@ public class ExcelUtil */ private short maxHeight; + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Method subMethod; + + /** + * 对象的子列表属性 + */ + private List subFields; + /** * 统计列表 */ @@ -163,11 +187,27 @@ public class ExcelUtil */ public Class clazz; + /** + * 需要排除列属性 + */ + public String[] excludeFields; + public ExcelUtil(Class clazz) { this.clazz = clazz; } + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * @throws Exception + */ + public void hideColumn(String... fields) + { + this.excludeFields = fields; + } + public void init(List list, String sheetName, String title, Type type) { if (list == null) @@ -181,6 +221,7 @@ public class ExcelUtil createExcelField(); createWorkbook(); createTitle(); + createSubHead(); } /** @@ -190,13 +231,48 @@ public class ExcelUtil { if (StringUtils.isNotEmpty(title)) { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + titleLastCol = titleLastCol + subFields.size() - 1; + } Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); titleRow.setHeightInPoints(30); Cell titleCell = titleRow.createCell(0); titleCell.setCellStyle(styles.get("title")); titleCell.setCellValue(title); - sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), - this.fields.size() - 1)); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) + { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; } } @@ -576,8 +652,20 @@ public class ExcelUtil // 写入各个字段的列头名称 for (Object[] os : fields) { + Field field = (Field) os[0]; Excel excel = (Excel) os[1]; - this.createCell(excel, row, column++); + if (Collection.class.isAssignableFrom(field.getType())) + { + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } } if (Type.EXPORT.equals(type)) { @@ -593,21 +681,60 @@ public class ExcelUtil * @param index 序号 * @param row 单元格行 */ + @SuppressWarnings("unchecked") public void fillExcelData(int index, Row row) { int startNo = index * sheetSize; int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; for (int i = startNo; i < endNo; i++) { - row = sheet.createRow(i + 1 + rownum - startNo); + rowNo = i > 1 ? rowNo + 1 : rowNo + i; + row = sheet.createRow(rowNo); // 得到导出对象. T vo = (T) list.get(i); + Collection subList = null; + if (isSubListValue(vo)) + { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } + int column = 0; for (Object[] os : fields) { Field field = (Field) os[0]; Excel excel = (Excel) os[1]; - this.addCell(excel, row, vo, field, column++); + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) + { + boolean subFirst = false; + for (Object obj : subList) + { + if (subFirst) + { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) + { + if (subField.isAnnotationPresent(Excel.class)) + { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } + else + { + this.addCell(excel, row, vo, field, column++); + } } } } @@ -649,20 +776,6 @@ public class ExcelUtil style.setFont(dataFont); styles.put("data", style); - style = wb.createCellStyle(); - style.cloneStyleFrom(styles.get("data")); - style.setAlignment(HorizontalAlignment.CENTER); - style.setVerticalAlignment(VerticalAlignment.CENTER); - style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setFillPattern(FillPatternType.SOLID_FOREGROUND); - Font headerFont = wb.createFont(); - headerFont.setFontName("Arial"); - headerFont.setFontHeightInPoints((short) 10); - headerFont.setBold(true); - headerFont.setColor(IndexedColors.WHITE.getIndex()); - style.setFont(headerFont); - styles.put("header", style); - style = wb.createCellStyle(); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); @@ -672,24 +785,60 @@ public class ExcelUtil style.setFont(totalFont); styles.put("total", style); - styles.putAll(annotationStyles(wb)); + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); return styles; } /** - * 根据Excel注解创建表格样式 + * 根据Excel注解创建表格头样式 * * @param wb 工作薄对象 * @return 自定义样式列表 */ - private Map annotationStyles(Workbook wb) + private Map annotationHeaderStyles(Workbook wb, Map styles) + { + Map headerStyles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) { Map styles = new HashMap(); for (Object[] os : fields) { Excel excel = (Excel) os[1]; - String key = "data_" + excel.align() + "_" + excel.color(); + String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); if (!styles.containsKey(key)) { CellStyle style = wb.createCellStyle(); @@ -704,6 +853,8 @@ public class ExcelUtil style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); style.setBorderBottom(BorderStyle.THIN); style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); Font dataFont = wb.createFont(); dataFont.setFontName("Arial"); dataFont.setFontHeightInPoints((short) 10); @@ -718,14 +869,23 @@ public class ExcelUtil /** * 创建单元格 */ - public Cell createCell(Excel attr, Row row, int column) + public Cell createHeadCell(Excel attr, Row row, int column) { // 创建列 Cell cell = row.createCell(column); // 写入列信息 cell.setCellValue(attr.name()); setDataValidation(attr, row, column); - cell.setCellStyle(styles.get("header")); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } return cell; } @@ -833,7 +993,12 @@ public class ExcelUtil { // 创建cell cell = row.createCell(column); - cell.setCellStyle(styles.get("data_" + attr.align() + "_" + attr.color())); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) + { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); // 用于读取对象中的属性 Object value = getTargetValue(vo, field, attr); @@ -855,7 +1020,7 @@ public class ExcelUtil } else if (value instanceof BigDecimal && -1 != attr.scale()) { - cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString()); + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { @@ -928,7 +1093,7 @@ public class ExcelUtil for (String item : convertSource) { String[] itemArray = item.split("="); - if (StringUtils.containsAny(separator, propertyValue)) + if (StringUtils.containsAny(propertyValue, separator)) { for (String value : propertyValue.split(separator)) { @@ -965,7 +1130,7 @@ public class ExcelUtil for (String item : convertSource) { String[] itemArray = item.split("="); - if (StringUtils.containsAny(separator, propertyValue)) + if (StringUtils.containsAny(propertyValue, separator)) { for (String value : propertyValue.split(separator)) { @@ -1178,29 +1343,39 @@ public class ExcelUtil tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); for (Field field : tempFields) { - // 单注解 - if (field.isAnnotationPresent(Excel.class)) + if (!ArrayUtils.contains(this.excludeFields, field.getName())) { - Excel attr = field.getAnnotation(Excel.class); - if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) - { - field.setAccessible(true); - fields.add(new Object[] { field, attr }); - } - } - - // 多注解 - if (field.isAnnotationPresent(Excels.class)) - { - Excels attrs = field.getAnnotation(Excels.class); - Excel[] excels = attrs.value(); - for (Excel attr : excels) + // 单注解 + if (field.isAnnotationPresent(Excel.class)) { + Excel attr = field.getAnnotation(Excel.class); if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) { field.setAccessible(true); fields.add(new Object[] { field, attr }); } + if (Collection.class.isAssignableFrom(field.getType())) + { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + } } } } @@ -1429,4 +1604,61 @@ public class ExcelUtil } return str; } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue(Object obj) + { + Object value; + try + { + value = subMethod.invoke(obj, new Object[] {}); + } + catch (Exception e) + { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java index 56dc7da..746530f 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -1,5 +1,7 @@ package com.ruoyi.framework.aspectj; +import java.util.ArrayList; +import java.util.List; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @@ -9,8 +11,10 @@ import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.entity.SysRole; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; /** * 数据过滤处理 @@ -68,8 +72,9 @@ public class DataScopeAspect // 如果是超级管理员,则不过滤数据 if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), - controllerDataScope.userAlias()); + controllerDataScope.userAlias(), permission); } } } @@ -79,15 +84,27 @@ public class DataScopeAspect * * @param joinPoint 切点 * @param user 用户 - * @param userAlias 别名 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + * @param permission 权限字符 */ - public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) { StringBuilder sqlString = new StringBuilder(); + List conditions = new ArrayList(); for (SysRole role : user.getRoles()) { String dataScope = role.getDataScope(); + if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) + { + continue; + } + if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions()) + && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + continue; + } if (DATA_SCOPE_ALL.equals(dataScope)) { sqlString = new StringBuilder(); @@ -121,6 +138,7 @@ public class DataScopeAspect sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); } } + conditions.add(dataScope); } if (StringUtils.isNotBlank(sqlString.toString())) diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java index 30742b8..912684d 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -19,10 +19,11 @@ import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.enums.BusinessStatus; import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.filter.PropertyPreExcludeFilter; +import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.IpUtils; -import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.framework.manager.AsyncManager; import com.ruoyi.framework.manager.factory.AsyncFactory; import com.ruoyi.system.domain.SysOperLog; @@ -38,6 +39,9 @@ public class LogAspect { private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + /** 排除敏感属性字段 */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + /** * 处理完请求后执行 * @@ -168,7 +172,7 @@ public class LogAspect { try { - Object jsonObj = JSON.toJSON(o); + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter()); params += jsonObj.toString() + " "; } catch (Exception e) @@ -180,6 +184,14 @@ public class LogAspect return params.trim(); } + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter() + { + return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES); + } + /** * 判断是否需要过滤的对象。 * diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java index 3e74580..7f8e1d5 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java @@ -5,7 +5,7 @@ import com.google.code.kaptcha.text.impl.DefaultTextCreator; /** * 验证码文本生成器 - * + * * @author ruoyi */ public class KaptchaTextCreator extends DefaultTextCreator @@ -20,7 +20,7 @@ public class KaptchaTextCreator extends DefaultTextCreator int x = random.nextInt(10); int y = random.nextInt(10); StringBuilder suChinese = new StringBuilder(); - int randomoperands = (int) Math.round(Math.random() * 2); + int randomoperands = random.nextInt(3); if (randomoperands == 0) { result = x * y; @@ -30,7 +30,7 @@ public class KaptchaTextCreator extends DefaultTextCreator } else if (randomoperands == 1) { - if (!(x == 0) && y % x == 0) + if ((x != 0) && y % x == 0) { result = y / x; suChinese.append(CNUMBERS[y]); @@ -45,7 +45,7 @@ public class KaptchaTextCreator extends DefaultTextCreator suChinese.append(CNUMBERS[y]); } } - else if (randomoperands == 2) + else { if (x >= y) { @@ -62,13 +62,6 @@ public class KaptchaTextCreator extends DefaultTextCreator suChinese.append(CNUMBERS[x]); } } - else - { - result = x + y; - suChinese.append(CNUMBERS[x]); - suChinese.append("+"); - suChinese.append(CNUMBERS[y]); - } suChinese.append("=?@" + result); return suChinese.toString(); } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java new file mode 100644 index 0000000..6c776ce --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author ruoyi + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java new file mode 100644 index 0000000..5472f3d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import com.ruoyi.common.core.text.Convert; + +/** + * 权限信息 + * + * @author ruoyi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java index 68b1d69..c2f97a1 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java @@ -7,6 +7,7 @@ import com.ruoyi.common.core.domain.entity.SysRole; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; /** * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 @@ -43,6 +44,7 @@ public class PermissionService { return false; } + PermissionContextHolder.setContext(permission); return hasPermissions(loginUser.getPermissions(), permission); } @@ -74,6 +76,7 @@ public class PermissionService { return false; } + PermissionContextHolder.setContext(permissions); Set authorities = loginUser.getPermissions(); for (String permission : permissions.split(PERMISSION_DELIMETER)) { diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java index a9383e2..2e15267 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java @@ -23,6 +23,7 @@ import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.framework.manager.AsyncManager; import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.system.service.ISysUserService; @@ -60,9 +61,9 @@ public class SysLoginService */ public String login(String username, String password, String code, String uuid) { - boolean captchaOnOff = configService.selectCaptchaOnOff(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); // 验证码开关 - if (captchaOnOff) + if (captchaEnabled) { validateCaptcha(username, code, uuid); } @@ -70,9 +71,10 @@ public class SysLoginService Authentication authentication = null; try { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); + AuthenticationContextHolder.setContext(authenticationToken); // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername - authentication = authenticationManager - .authenticate(new UsernamePasswordAuthenticationToken(username, password)); + authentication = authenticationManager.authenticate(authenticationToken); } catch (Exception e) { @@ -87,6 +89,10 @@ public class SysLoginService throw new ServiceException(e.getMessage()); } } + finally + { + AuthenticationContextHolder.clearContext(); + } AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); LoginUser loginUser = (LoginUser) authentication.getPrincipal(); recordLoginInfo(loginUser.getUserId()); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java new file mode 100644 index 0000000..6ad91b0 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java @@ -0,0 +1,94 @@ +package com.ruoyi.framework.web.service; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; + +/** + * 登录密码方法 + * + * @author ruoyi + */ +@Component +public class SysPasswordService +{ + @Autowired + private RedisCache redisCache; + + @Value(value = "${user.password.maxRetryCount}") + private int maxRetryCount; + + @Value(value = "${user.password.lockTime}") + private int lockTime; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user) + { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); + + if (retryCount == null) + { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, + MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime))); + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, + MessageUtils.message("user.password.retry.limit.count", retryCount))); + redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new UserPasswordNotMatchException(); + } + else + { + clearLoginRecordCache(username); + } + } + + public boolean matches(SysUser user, String rawPassword) + { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache(String loginName) + { + if (redisCache.hasKey(getCacheKey(loginName))) + { + redisCache.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java index feb8038..402f25b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java @@ -1,9 +1,11 @@ package com.ruoyi.framework.web.service; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.ruoyi.common.core.domain.entity.SysRole; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.system.service.ISysMenuService; import com.ruoyi.system.service.ISysRoleService; @@ -59,7 +61,21 @@ public class SysPermissionService } else { - perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + List roles = user.getRoles(); + if (!roles.isEmpty() && roles.size() > 1) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } } return perms; } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java index 2de5c8b..a3b273b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java @@ -42,9 +42,9 @@ public class SysRegisterService { String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); - boolean captchaOnOff = configService.selectCaptchaOnOff(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); // 验证码开关 - if (captchaOnOff) + if (captchaEnabled) { validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java index c462b07..bb810ea 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java @@ -31,6 +31,9 @@ public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; @Autowired private SysPermissionService permissionService; @@ -52,6 +55,8 @@ public class UserDetailsServiceImpl implements UserDetailsService { throw new ServiceException("对不起,您的账号:" + username + " 已停用"); } +// passwordService.validate(user); + return createLoginUser(user); } diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm index a363ef3..f66cc3b 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm @@ -492,8 +492,8 @@ function handleAdd() { /** 修改按钮操作 */ function handleUpdate(row) { reset(); - const ${pkColumn.javaField} = row.${pkColumn.javaField} || ids.value - get${BusinessName}(${pkColumn.javaField}).then(response => { + const _${pkColumn.javaField} = row.${pkColumn.javaField} || ids.value + get${BusinessName}(_${pkColumn.javaField}).then(response => { form.value = response.data; #foreach ($column in $columns) #if($column.htmlType == "checkbox") @@ -539,9 +539,9 @@ function submitForm() { /** 删除按钮操作 */ function handleDelete(row) { - const ${pkColumn.javaField}s = row.${pkColumn.javaField} || ids.value; - proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + ${pkColumn.javaField}s + '"的数据项?').then(function() { - return del${BusinessName}(${pkColumn.javaField}s); + const _${pkColumn.javaField}s = row.${pkColumn.javaField} || ids.value; + proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + _${pkColumn.javaField}s + '"的数据项?').then(function() { + return del${BusinessName}(_${pkColumn.javaField}s); }).then(() => { getList(); proxy.#[[$modal]]#.msgSuccess("删除成功"); diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java index f248430..bae3c53 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java @@ -167,8 +167,8 @@ public class SysJobController extends BaseController @PutMapping("/run") public AjaxResult run(@RequestBody SysJob job) throws SchedulerException { - jobService.run(job); - return AjaxResult.success(); + boolean result = jobService.run(job); + return result ? success() : error("任务不存在或已过期!"); } /** @@ -180,6 +180,6 @@ public class SysJobController extends BaseController public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException { jobService.deleteJobByIds(jobIds); - return AjaxResult.success(); + return success(); } } diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java index 6d62661..437ade8 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java @@ -74,7 +74,7 @@ public interface ISysJobService * @param job 调度信息 * @return 结果 */ - public void run(SysJob job) throws SchedulerException; + public boolean run(SysJob job) throws SchedulerException; /** * 新增任务 diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java index 0d9a53d..77fdbb5 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java @@ -174,15 +174,22 @@ public class SysJobServiceImpl implements ISysJobService */ @Override @Transactional(rollbackFor = Exception.class) - public void run(SysJob job) throws SchedulerException + public boolean run(SysJob job) throws SchedulerException { + boolean result = false; Long jobId = job.getJobId(); String jobGroup = job.getJobGroup(); SysJob properties = selectJobById(job.getJobId()); // 参数 JobDataMap dataMap = new JobDataMap(); dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); - scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap); + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + result = true; + scheduler.triggerJob(jobKey, dataMap); + } + return result; } /** diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java index e2e420e..3da377a 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java @@ -52,12 +52,12 @@ public class JobInvokeUtil { if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) { - Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams)); + Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams)); method.invoke(bean, getMethodParamsValue(methodParams)); } else { - Method method = bean.getClass().getDeclaredMethod(methodName); + Method method = bean.getClass().getMethod(methodName); method.invoke(bean); } } diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java index 5d00c2c..f885d42 100644 --- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java @@ -83,7 +83,12 @@ public class ScheduleUtils scheduler.deleteJob(getJobKey(jobId, jobGroup)); } - scheduler.scheduleJob(jobDetail, trigger); + // 判断任务是否过期 + if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) + { + // 执行调度任务 + scheduler.scheduleJob(jobDetail, trigger); + } // 暂停任务 if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java index 123b47f..99c0c50 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java @@ -34,6 +34,14 @@ public interface SysMenuMapper */ public List selectMenuListByUserId(SysMenu menu); + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public List selectMenuPermsByRoleId(Long roleId); + /** * 根据用户ID查询权限 * diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java index 00b037a..58bdc29 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java @@ -31,7 +31,7 @@ public interface ISysConfigService * * @return true开启,false关闭 */ - public boolean selectCaptchaOnOff(); + public boolean selectCaptchaEnabled(); /** * 查询参数配置列表 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java index 72ca048..166ba16 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java @@ -19,6 +19,14 @@ public interface ISysDeptService */ public List selectDeptList(SysDept dept); + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + public List selectDeptTreeList(SysDept dept); + /** * 构建前端所需要树结构 * diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java index 1b43174..2e32c62 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java @@ -38,6 +38,14 @@ public interface ISysMenuService */ public Set selectMenuPermsByUserId(Long userId); + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public Set selectMenuPermsByRoleId(Long roleId); + /** * 根据用户ID查询菜单树信息 * diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java index 01467ce..73aa801 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java @@ -86,14 +86,14 @@ public class SysConfigServiceImpl implements ISysConfigService * @return true开启,false关闭 */ @Override - public boolean selectCaptchaOnOff() + public boolean selectCaptchaEnabled() { - String captchaOnOff = selectConfigByKey("sys.account.captchaOnOff"); - if (StringUtils.isEmpty(captchaOnOff)) + String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled"); + if (StringUtils.isEmpty(captchaEnabled)) { return true; } - return Convert.toBool(captchaOnOff); + return Convert.toBool(captchaEnabled); } /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java index 96d215e..95b25b9 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java @@ -48,6 +48,19 @@ public class SysDeptServiceImpl implements ISysDeptService return deptMapper.selectDeptList(dept); } + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + @Override + public List selectDeptTreeList(SysDept dept) + { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + /** * 构建前端所需要树结构 * diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java index b2ac3f0..481a9ec 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java @@ -100,6 +100,27 @@ public class SysMenuServiceImpl implements ISysMenuService return permsSet; } + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) + { + List perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + /** * 根据用户ID查询菜单 * @@ -508,7 +529,7 @@ public class SysMenuServiceImpl implements ISysMenuService */ public String innerLinkReplaceEach(String path) { - return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS }, - new String[] { "", "" }); + return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, "." }, + new String[] { "", "", "", "/" }); } } diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml index b01bb32..058bf13 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -82,7 +82,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml index 2ad833c..6762007 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -111,6 +111,13 @@ where m.status = '0' and r.status = '0' and ur.user_id = #{userId} + + - where r.role_name=#{roleName} limit 1 + where r.role_name=#{roleName} and r.del_flag = '0' limit 1 @@ -139,7 +139,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - update sys_role set del_flag = '2' where role_id = #{roleId} + update sys_role set del_flag = '2' where role_id = #{roleId} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 8a36ede..baa1983 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -122,7 +122,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -208,7 +208,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - update sys_user set del_flag = '2' where user_id = #{userId} + update sys_user set del_flag = '2' where user_id = #{userId} diff --git a/ruoyi-ui/src/api/monitor/logininfor.js b/ruoyi-ui/src/api/monitor/logininfor.js index 26a46eb..4d112b7 100644 --- a/ruoyi-ui/src/api/monitor/logininfor.js +++ b/ruoyi-ui/src/api/monitor/logininfor.js @@ -17,6 +17,14 @@ export function delLogininfor(infoId) { }) } +// 解锁用户登录状态 +export function unlockLogininfor(userName) { + return request({ + url: '/monitor/logininfor/unlock/' + userName, + method: 'get' + }) +} + // 清空登录日志 export function cleanLogininfor() { return request({ diff --git a/ruoyi-ui/src/api/system/dept.js b/ruoyi-ui/src/api/system/dept.js index 2804676..fc943cd 100644 --- a/ruoyi-ui/src/api/system/dept.js +++ b/ruoyi-ui/src/api/system/dept.js @@ -25,22 +25,6 @@ export function getDept(deptId) { }) } -// 查询部门下拉树结构 -export function treeselect() { - return request({ - url: '/system/dept/treeselect', - method: 'get' - }) -} - -// 根据角色ID查询部门树结构 -export function roleDeptTreeselect(roleId) { - return request({ - url: '/system/dept/roleDeptTreeselect/' + roleId, - method: 'get' - }) -} - // 新增部门 export function addDept(data) { return request({ diff --git a/ruoyi-ui/src/api/system/role.js b/ruoyi-ui/src/api/system/role.js index 4b455e1..f13e6f4 100644 --- a/ruoyi-ui/src/api/system/role.js +++ b/ruoyi-ui/src/api/system/role.js @@ -108,4 +108,12 @@ export function authUserSelectAll(data) { method: 'put', params: data }) -} \ No newline at end of file +} + +// 根据角色ID查询部门树结构 +export function deptTreeSelect(roleId) { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get' + }) +} diff --git a/ruoyi-ui/src/api/system/user.js b/ruoyi-ui/src/api/system/user.js index 4fd752b..f2f76ef 100644 --- a/ruoyi-ui/src/api/system/user.js +++ b/ruoyi-ui/src/api/system/user.js @@ -125,3 +125,11 @@ export function updateAuthRole(data) { params: data }) } + +// 查询部门下拉树结构 +export function deptTreeSelect() { + return request({ + url: '/system/user/deptTree', + method: 'get' + }) +} diff --git a/ruoyi-ui/src/assets/styles/transition.scss b/ruoyi-ui/src/assets/styles/transition.scss index 4cb27cc..eb49895 100644 --- a/ruoyi-ui/src/assets/styles/transition.scss +++ b/ruoyi-ui/src/assets/styles/transition.scss @@ -12,11 +12,16 @@ } /* fade-transform */ +.fade-transform--move, .fade-transform-leave-active, .fade-transform-enter-active { transition: all .5s; } +.fade-transform-leave-active { + position: absolute; +} + .fade-transform-enter { opacity: 0; transform: translateX(-30px); diff --git a/ruoyi-ui/src/components/DictData/index.js b/ruoyi-ui/src/components/DictData/index.js index c2a0359..7b85d4a 100644 --- a/ruoyi-ui/src/components/DictData/index.js +++ b/ruoyi-ui/src/components/DictData/index.js @@ -1,7 +1,23 @@ import Vue from 'vue' +import store from '@/store' import DataDict from '@/utils/dict' import { getDicts as getDicts } from '@/api/system/dict/data' +function searchDictByKey(dict, key) { + if (key == null && key == "") { + return null + } + try { + for (let i = 0; i < dict.length; i++) { + if (dict[i].key == key) { + return dict[i].value + } + } + } catch (e) { + return null + } +} + function install() { Vue.use(DataDict, { metas: { @@ -9,7 +25,19 @@ function install() { labelField: 'dictLabel', valueField: 'dictValue', request(dictMeta) { - return getDicts(dictMeta.type).then(res => res.data) + const storeDict = searchDictByKey(store.getters.dict, dictMeta.type) + if (storeDict) { + return new Promise(resolve => { resolve(storeDict) }) + } else { + return new Promise((resolve, reject) => { + getDicts(dictMeta.type).then(res => { + store.dispatch('dict/setDict', { key: dictMeta.type, value: res.data }) + resolve(res.data) + }).catch(error => { + reject(error) + }) + }) + } }, }, }, diff --git a/ruoyi-ui/src/components/FileUpload/index.vue b/ruoyi-ui/src/components/FileUpload/index.vue index aa2296b..4e94cb0 100644 --- a/ruoyi-ui/src/components/FileUpload/index.vue +++ b/ruoyi-ui/src/components/FileUpload/index.vue @@ -12,7 +12,7 @@ :show-file-list="false" :headers="headers" class="upload-file-uploader" - ref="upload" + ref="fileUpload" > 选取文件 @@ -151,14 +151,16 @@ export default { this.$modal.closeLoading() }, // 上传成功回调 - handleUploadSuccess(res) { - this.uploadList.push({ name: res.fileName, url: res.fileName }); - if (this.uploadList.length === this.number) { - this.fileList = this.fileList.concat(this.uploadList); - this.uploadList = []; - this.number = 0; - this.$emit("input", this.listToString(this.fileList)); + handleUploadSuccess(res, file) { + if (res.code === 200) { + this.uploadList.push({ name: res.fileName, url: res.fileName }); + this.uploadedSuccessfully(); + } else { + this.number--; this.$modal.closeLoading(); + this.$modal.msgError(res.msg); + this.$refs.fileUpload.handleRemove(file); + this.uploadedSuccessfully(); } }, // 删除文件 @@ -166,6 +168,16 @@ export default { this.fileList.splice(index, 1); this.$emit("input", this.listToString(this.fileList)); }, + // 上传结束处理 + uploadedSuccessfully() { + if (this.number > 0 && this.uploadList.length === this.number) { + this.fileList = this.fileList.concat(this.uploadList); + this.uploadList = []; + this.number = 0; + this.$emit("input", this.listToString(this.fileList)); + this.$modal.closeLoading(); + } + }, // 获取文件名称 getFileName(name) { if (name.lastIndexOf("/") > -1) { diff --git a/ruoyi-ui/src/components/ImagePreview/index.vue b/ruoyi-ui/src/components/ImagePreview/index.vue index 743d8d5..3c770c7 100644 --- a/ruoyi-ui/src/components/ImagePreview/index.vue +++ b/ruoyi-ui/src/components/ImagePreview/index.vue @@ -19,7 +19,7 @@ export default { props: { src: { type: String, - required: true + default: "" }, width: { type: [Number, String], @@ -32,6 +32,9 @@ export default { }, computed: { realSrc() { + if (!this.src) { + return; + } let real_src = this.src.split(",")[0]; if (isExternal(real_src)) { return real_src; @@ -39,6 +42,9 @@ export default { return process.env.VUE_APP_BASE_API + real_src; }, realSrcList() { + if (!this.src) { + return; + } let real_src_list = this.src.split(","); let srcList = []; real_src_list.forEach(item => { diff --git a/ruoyi-ui/src/components/ImageUpload/index.vue b/ruoyi-ui/src/components/ImageUpload/index.vue index 4068b67..b57a15e 100644 --- a/ruoyi-ui/src/components/ImageUpload/index.vue +++ b/ruoyi-ui/src/components/ImageUpload/index.vue @@ -9,8 +9,8 @@ :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed" - name="file" - :on-remove="handleRemove" + ref="imageUpload" + :on-remove="handleDelete" :show-file-list="true" :headers="headers" :file-list="fileList" @@ -117,25 +117,6 @@ export default { }, }, methods: { - // 删除图片 - handleRemove(file, fileList) { - const findex = this.fileList.map(f => f.name).indexOf(file.name); - if(findex > -1) { - this.fileList.splice(findex, 1); - this.$emit("input", this.listToString(this.fileList)); - } - }, - // 上传成功回调 - handleUploadSuccess(res) { - this.uploadList.push({ name: res.fileName, url: res.fileName }); - if (this.uploadList.length === this.number) { - this.fileList = this.fileList.concat(this.uploadList); - this.uploadList = []; - this.number = 0; - this.$emit("input", this.listToString(this.fileList)); - this.$modal.closeLoading(); - } - }, // 上传前loading加载 handleBeforeUpload(file) { let isImg = false; @@ -171,11 +152,42 @@ export default { handleExceed() { this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`); }, + // 上传成功回调 + handleUploadSuccess(res, file) { + if (res.code === 200) { + this.uploadList.push({ name: res.fileName, url: res.fileName }); + this.uploadedSuccessfully(); + } else { + this.number--; + this.$modal.closeLoading(); + this.$modal.msgError(res.msg); + this.$refs.imageUpload.handleRemove(file); + this.uploadedSuccessfully(); + } + }, + // 删除图片 + handleDelete(file) { + const findex = this.fileList.map(f => f.name).indexOf(file.name); + if(findex > -1) { + this.fileList.splice(findex, 1); + this.$emit("input", this.listToString(this.fileList)); + } + }, // 上传失败 handleUploadError() { this.$modal.msgError("上传图片失败,请重试"); this.$modal.closeLoading(); }, + // 上传结束处理 + uploadedSuccessfully() { + if (this.number > 0 && this.uploadList.length === this.number) { + this.fileList = this.fileList.concat(this.uploadList); + this.uploadList = []; + this.number = 0; + this.$emit("input", this.listToString(this.fileList)); + this.$modal.closeLoading(); + } + }, // 预览 handlePictureCardPreview(file) { this.dialogImageUrl = file.url; @@ -186,7 +198,9 @@ export default { let strs = ""; separator = separator || ","; for (let i in list) { - strs += list[i].url.replace(this.baseUrl, "") + separator; + if (list[i].url) { + strs += list[i].url.replace(this.baseUrl, "") + separator; + } } return strs != '' ? strs.substr(0, strs.length - 1) : ''; } diff --git a/ruoyi-ui/src/components/RightPanel/index.vue b/ruoyi-ui/src/components/RightPanel/index.vue index fbf27eb..2d6122b 100644 --- a/ruoyi-ui/src/components/RightPanel/index.vue +++ b/ruoyi-ui/src/components/RightPanel/index.vue @@ -1,5 +1,5 @@ - -