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