upgrade to v3.8.2

This commit is contained in:
shaingsense 2022-05-09 12:48:40 +08:00
parent 581ddfd527
commit 9e96ccf463
295 changed files with 35182 additions and 34590 deletions

View File

@ -45,6 +45,8 @@ https://gitee.com/whiteshader/images/raw/master/demo.mp4
## 前端开发注意事项
安装依赖yarn
正常启动请运行: npm run start:dev
Mock测试模式请运行: npm run start

View File

@ -1,6 +1,6 @@
@echo off
echo.
echo [信息] 清理工程target生成路径。
echo [信息] 清理工程target生成路径。
echo.
%~d0

View File

@ -1,6 +1,6 @@
@echo off
@echo off
echo.
echo [信息] 使用Jar命令运行Web工程。
echo [信息] 使用Jar命令运行Web工程。
echo.
cd %~dp0

30
pom.xml
View File

@ -6,14 +6,14 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId>
<version>3.8.0</version>
<version>3.8.2</version>
<name>ruoyi</name>
<url>http://www.ruoyi.vip</url>
<description>若依管理系统</description>
<properties>
<ruoyi.version>3.8.0</ruoyi.version>
<ruoyi.version>3.8.2</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
@ -22,18 +22,17 @@
<bitwalker.version>1.21</bitwalker.version>
<swagger.version>3.0.0</swagger.version>
<kaptcha.version>2.3.2</kaptcha.version>
<mybatis-spring-boot.version>2.2.0</mybatis-spring-boot.version>
<pagehelper.boot.version>1.4.0</pagehelper.boot.version>
<fastjson.version>1.2.78</fastjson.version>
<oshi.version>5.8.2</oshi.version>
<jna.version>5.9.0</jna.version>
<mybatis-spring-boot.version>2.2.2</mybatis-spring-boot.version>
<pagehelper.boot.version>1.4.1</pagehelper.boot.version>
<fastjson.version>1.2.80</fastjson.version>
<oshi.version>6.1.2</oshi.version>
<jna.version>5.10.0</jna.version>
<commons.io.version>2.11.0</commons.io.version>
<commons.fileupload.version>1.4</commons.fileupload.version>
<commons.collections.version>3.2.2</commons.collections.version>
<poi.version>4.1.2</poi.version>
<velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version>
<log4j2.version>2.16.0</log4j2.version>
</properties>
<!-- 依赖声明 -->
@ -44,7 +43,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.6</version>
<version>2.5.13</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -151,19 +150,6 @@
<version>${fastjson.version}</version>
</dependency>
<!-- log4j日志组件 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>${log4j2.version}</version>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.0</version>
<version>3.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>

View File

@ -1,5 +1,7 @@
package com.ruoyi.web.controller.common;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
@ -8,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
@ -24,6 +27,7 @@ import com.ruoyi.framework.config.ServerConfig;
* @author ruoyi
*/
@RestController
@RequestMapping("/common")
public class CommonController
{
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
@ -31,13 +35,15 @@ public class CommonController
@Autowired
private ServerConfig serverConfig;
private static final String FILE_DELIMETER = ",";
/**
* 通用下载请求
*
* @param fileName 文件名称
* @param delete 是否删除
*/
@GetMapping("common/download")
@GetMapping("/download")
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
{
try
@ -64,9 +70,9 @@ public class CommonController
}
/**
* 通用上传请求
* 通用上传请求单个
*/
@PostMapping("/common/upload")
@PostMapping("/upload")
public AjaxResult uploadFile(MultipartFile file) throws Exception
{
try
@ -77,8 +83,47 @@ public class CommonController
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
AjaxResult ajax = AjaxResult.success();
ajax.put("fileName", fileName);
ajax.put("url", url);
ajax.put("fileName", fileName);
ajax.put("newFileName", FileUtils.getName(fileName));
ajax.put("originalFilename", file.getOriginalFilename());
return ajax;
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
* 通用上传请求多个
*/
@PostMapping("/uploads")
public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
{
try
{
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
List<String> urls = new ArrayList<String>();
List<String> fileNames = new ArrayList<String>();
List<String> newFileNames = new ArrayList<String>();
List<String> originalFilenames = new ArrayList<String>();
for (MultipartFile file : files)
{
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
urls.add(url);
fileNames.add(fileName);
newFileNames.add(FileUtils.getName(fileName));
originalFilenames.add(file.getOriginalFilename());
}
AjaxResult ajax = AjaxResult.success();
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
return ajax;
}
catch (Exception e)
@ -90,7 +135,7 @@ public class CommonController
/**
* 本地资源通用下载
*/
@GetMapping("/common/download/resource")
@GetMapping("/download/resource")
public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
throws Exception
{

View File

@ -125,16 +125,17 @@ public class SysDeptController extends BaseController
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDept dept)
{
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
{
return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
else if (dept.getParentId().equals(dept.getDeptId()))
else if (dept.getParentId().equals(deptId))
{
return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
}
else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
&& deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0)
else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0)
{
return AjaxResult.error("该部门包含未停用的子部门!");
}
@ -158,6 +159,7 @@ public class SysDeptController extends BaseController
{
return AjaxResult.error("部门存在用户,不允许删除");
}
deptService.checkDeptDataScope(deptId);
return toAjax(deptService.deleteDeptById(deptId));
}
}

View File

@ -111,6 +111,7 @@ public class SysRoleController extends BaseController
public AjaxResult edit(@Validated @RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role)))
{
return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
@ -145,6 +146,7 @@ public class SysRoleController extends BaseController
public AjaxResult dataScope(@RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
return toAjax(roleService.authDataScope(role));
}
@ -157,6 +159,7 @@ public class SysRoleController extends BaseController
public AjaxResult changeStatus(@RequestBody SysRole role)
{
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
role.setUpdateBy(getUsername());
return toAjax(roleService.updateRoleStatus(role));
}
@ -236,6 +239,7 @@ public class SysRoleController extends BaseController
@PutMapping("/authUser/selectAll")
public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds)
{
roleService.checkRoleDataScope(roleId);
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
}

View File

@ -104,9 +104,10 @@ public class SysUserController extends BaseController
ajax.put("posts", postService.selectPostAll());
if (StringUtils.isNotNull(userId))
{
ajax.put(AjaxResult.DATA_TAG, userService.selectUserById(userId));
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", roleService.selectRoleListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
return ajax;
}
@ -147,6 +148,7 @@ public class SysUserController extends BaseController
public AjaxResult edit(@Validated @RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
if (StringUtils.isNotEmpty(user.getPhonenumber())
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
{
@ -185,6 +187,7 @@ public class SysUserController extends BaseController
public AjaxResult resetPwd(@RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
user.setUpdateBy(getUsername());
return toAjax(userService.resetPwd(user));
@ -199,6 +202,7 @@ public class SysUserController extends BaseController
public AjaxResult changeStatus(@RequestBody SysUser user)
{
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setUpdateBy(getUsername());
return toAjax(userService.updateUserStatus(user));
}
@ -226,6 +230,7 @@ public class SysUserController extends BaseController
@PutMapping("/authRole")
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
{
userService.checkUserDataScope(userId);
userService.insertUserAuth(userId, roleIds);
return success();
}

View File

@ -3,9 +3,9 @@ ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.8.0
version: 3.8.2
# 版权年份
copyrightYear: 2021
copyrightYear: 2022
# 实例演示开关
demoEnabled: true
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.0</version>
<version>3.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,6 +5,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.ruoyi.common.utils.poi.ExcelHandlerAdapter;
/**
@ -56,11 +58,6 @@ public @interface Excel
*/
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/**
* 导出类型0数字 1字符串
*/
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出时在excel中每个列的高度 单位为字符
*/
@ -107,9 +104,19 @@ public @interface Excel
public boolean isStatistics() default false;
/**
* 导出字段对齐方式0默认1靠左2居中3靠右
* 导出类型0数字 1字符串
*/
public Align align() default Align.AUTO;
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出字体颜色
*/
public IndexedColors color() default IndexedColors.BLACK;
/**
* 导出字段对齐方式
*/
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
/**
* 自定义数据处理器

View File

@ -142,16 +142,26 @@ public class Constants
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi://";
public static final String LOOKUP_RMI = "rmi:";
/**
* LDAP 远程方法调用
*/
public static final String LOOKUP_LDAP = "ldap://";
public static final String LOOKUP_LDAP = "ldap:";
/**
* LDAPS 远程方法调用
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 定时任务白名单配置仅允许访问的包名如其他需要可以自行添加
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework.jndi" };
"org.springframework", "org.apache", "com.ruoyi.common.utils.file" };
}

View File

@ -109,6 +109,9 @@ public class GenConstants
/** 模糊查询 */
public static final String QUERY_LIKE = "LIKE";
/** 相等查询 */
public static final String QUERY_EQ = "EQ";
/** 需要 */
public static final String REQUIRE = "1";
}

View File

@ -16,6 +16,7 @@ import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sql.SqlUtil;
@ -51,15 +52,7 @@ public class BaseController
*/
protected void startPage()
{
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
{
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
}
PageUtils.startPage();
}
/**
@ -75,6 +68,14 @@ public class BaseController
}
}
/**
* 清理分页的线程变量
*/
protected void clearPage()
{
PageUtils.clearPage();
}
/**
* 响应请求分页数据
*/

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -31,7 +32,7 @@ public class SysDept extends BaseEntity
private String deptName;
/** 显示顺序 */
private String orderNum;
private Integer orderNum;
/** 负责人 */
private String leader;
@ -96,13 +97,13 @@ public class SysDept extends BaseEntity
this.deptName = deptName;
}
@NotBlank(message = "显示顺序不能为空")
public String getOrderNum()
@NotNull(message = "显示顺序不能为空")
public Integer getOrderNum()
{
return orderNum;
}
public void setOrderNum(String orderNum)
public void setOrderNum(Integer orderNum)
{
this.orderNum = orderNum;
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.common.core.domain.entity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -57,6 +58,7 @@ public class SysDictType extends BaseEntity
@NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
public String getDictType()
{
return dictType;

View File

@ -3,6 +3,7 @@ package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList;
import java.util.List;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -30,7 +31,7 @@ public class SysMenu extends BaseEntity
private Long parentId;
/** 显示顺序 */
private String orderNum;
private Integer orderNum;
/** 路由地址 */
private String path;
@ -107,13 +108,13 @@ public class SysMenu extends BaseEntity
this.parentId = parentId;
}
@NotBlank(message = "显示顺序不能为空")
public String getOrderNum()
@NotNull(message = "显示顺序不能为空")
public Integer getOrderNum()
{
return orderNum;
}
public void setOrderNum(String orderNum)
public void setOrderNum(Integer orderNum)
{
this.orderNum = orderNum;
}

View File

@ -25,7 +25,7 @@ public class LoginBody
/**
* 唯一标识
*/
private String uuid = "";
private String uuid;
public String getUsername()
{

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.core.page;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.ServletUtils;
/**
@ -40,8 +41,8 @@ public class TableSupport
public static PageDomain getPageDomain()
{
PageDomain pageDomain = new PageDomain();
pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM));
pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE));
pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));

View File

@ -213,12 +213,12 @@ public class RedisCache
* 删除Hash中的数据
*
* @param key
* @param mapkey
* @param hKey
*/
public void delCacheMapValue(final String key, final String hkey)
public void delCacheMapValue(final String key, final String hKey)
{
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hkey);
hashOperations.delete(key, hKey);
}
/**

View File

@ -3,6 +3,11 @@ package com.ruoyi.common.utils;
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
@ -131,6 +136,14 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
return new Date(time);
}
/**
* 计算相差天数
*/
public static int differentDaysByMillisecond(Date date1, Date date2)
{
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
}
/**
* 计算两个时间差
*/
@ -152,4 +165,23 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
// long sec = diff % nd % nh % nm / ns;
return day + "" + hour + "小时" + min + "分钟";
}
/**
* 增加 LocalDateTime ==> Date
*/
public static Date toDate(LocalDateTime temporalAccessor)
{
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
/**
* 增加 LocalDate ==> Date
*/
public static Date toDate(LocalDate temporalAccessor)
{
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
}

View File

@ -41,8 +41,7 @@ public class DictUtils
Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
if (StringUtils.isNotNull(cacheObj))
{
List<SysDictData> dictDatas = StringUtils.cast(cacheObj);
return dictDatas;
return StringUtils.cast(cacheObj);
}
return null;
}
@ -92,7 +91,7 @@ public class DictUtils
{
if (value.equals(dict.getDictValue()))
{
propertyString.append(dict.getDictLabel() + separator);
propertyString.append(dict.getDictLabel()).append(separator);
break;
}
}
@ -132,7 +131,7 @@ public class DictUtils
{
if (label.equals(dict.getDictLabel()))
{
propertyString.append(dict.getDictValue() + separator);
propertyString.append(dict.getDictValue()).append(separator);
break;
}
}

View File

@ -18,8 +18,7 @@ public class ExceptionUtil
{
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw, true));
String str = sw.toString();
return str;
return sw.toString();
}
public static String getRootErrorMessage(Exception e)

View File

@ -0,0 +1,35 @@
package com.ruoyi.common.utils;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.sql.SqlUtil;
/**
* 分页工具类
*
* @author ruoyi
*/
public class PageUtils extends PageHelper
{
/**
* 设置请求分页数据
*/
public static void startPage()
{
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
}
/**
* 清理分页的线程变量
*/
public static void clearPage()
{
PageHelper.clearPage();
}
}

View File

@ -99,9 +99,8 @@ public class ServletUtils
*
* @param response 渲染对象
* @param string 待渲染的字符串
* @return null
*/
public static String renderString(HttpServletResponse response, String string)
public static void renderString(HttpServletResponse response, String string)
{
try
{
@ -114,7 +113,6 @@ public class ServletUtils
{
e.printStackTrace();
}
return null;
}
/**
@ -125,13 +123,13 @@ public class ServletUtils
public static boolean isAjaxRequest(HttpServletRequest request)
{
String accept = request.getHeader("accept");
if (accept != null && accept.indexOf("application/json") != -1)
if (accept != null && accept.contains("application/json"))
{
return true;
}
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest"))
{
return true;
}
@ -143,10 +141,6 @@ public class ServletUtils
}
String ajax = request.getParameter("__ajax");
if (StringUtils.inStringIgnoreCase(ajax, "json", "xml"))
{
return true;
}
return false;
return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
}
}

View File

@ -531,4 +531,53 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
{
return (T) obj;
}
/**
* 数字左边补齐0使之达到指定长度注意如果数字转换为字符串后长度大于size则只保留 最后size个字符
*
* @param num 数字对象
* @param size 字符串指定长度
* @return 返回数字的字符串格式该字符串为指定长度
*/
public static final String padl(final Number num, final int size)
{
return padl(num.toString(), size, '0');
}
/**
* 字符串左补齐如果原始字符串s长度大于size则只保留最后size个字符
*
* @param s 原始字符串
* @param size 字符串指定长度
* @param c 用于补齐的字符
* @return 返回指定长度的字符串由原字符串左补齐或截取得到
*/
public static final String padl(final String s, final int size, final char c)
{
final StringBuilder sb = new StringBuilder(size);
if (s != null)
{
final int len = s.length();
if (s.length() <= size)
{
for (int i = size - len; i > 0; i--)
{
sb.append(c);
}
sb.append(s);
}
else
{
return s.substring(len - size, len);
}
}
else
{
for (int i = size; i > 0; i--)
{
sb.append(c);
}
}
return sb.toString();
}
}

View File

@ -2,6 +2,8 @@ package com.ruoyi.common.utils.file;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
@ -11,7 +13,7 @@ import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.common.utils.uuid.Seq;
/**
* 文件上传工具类
@ -100,7 +102,7 @@ public class FileUploadUtils
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException
{
int fileNamelength = file.getOriginalFilename().length();
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
{
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
@ -110,10 +112,9 @@ public class FileUploadUtils
String fileName = extractFilename(file);
File desc = getAbsoluteFile(baseDir, fileName);
file.transferTo(desc);
String pathFileName = getPathFileName(baseDir, fileName);
return pathFileName;
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
file.transferTo(Paths.get(absPath));
return getPathFileName(baseDir, fileName);
}
/**
@ -121,10 +122,8 @@ public class FileUploadUtils
*/
public static final String extractFilename(MultipartFile file)
{
String fileName = file.getOriginalFilename();
String extension = getExtension(file);
fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
return fileName;
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
}
public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
@ -145,8 +144,7 @@ public class FileUploadUtils
{
int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
return pathFileName;
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
}
/**
@ -161,7 +159,7 @@ public class FileUploadUtils
throws FileSizeLimitExceededException, InvalidExtensionException
{
long size = file.getSize();
if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
if (size > DEFAULT_MAX_SIZE)
{
throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
}
@ -228,7 +226,7 @@ public class FileUploadUtils
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
if (StringUtils.isEmpty(extension))
{
extension = MimeTypeUtils.getExtension(file.getContentType());
extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
}
return extension;
}

View File

@ -17,6 +17,7 @@ import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import org.apache.commons.io.FilenameUtils;
/**
* 文件处理工具类
@ -196,7 +197,6 @@ public class FileUtils
*
* @param response 响应对象
* @param realFileName 真实文件名
* @return
*/
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
{
@ -210,7 +210,6 @@ public class FileUtils
.append("utf-8''")
.append(percentEncodedFileName);
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
response.setHeader("Content-disposition", contentDispositionValue.toString());
response.setHeader("download-filename", percentEncodedFileName);
@ -256,4 +255,39 @@ public class FileUtils
}
return strFileExtendName;
}
/**
* 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
*
* @param fileName 路径名称
* @return 没有文件路径的名称
*/
public static String getName(String fileName)
{
if (fileName == null)
{
return null;
}
int lastUnixPos = fileName.lastIndexOf('/');
int lastWindowsPos = fileName.lastIndexOf('\\');
int index = Math.max(lastUnixPos, lastWindowsPos);
return fileName.substring(index + 1);
}
/**
* 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
*
* @param fileName 路径名称
* @return 没有文件路径和后缀的名称
*/
public static String getNameNotSuffix(String fileName)
{
if (fileName == null)
{
return null;
}
String baseName = FilenameUtils.getBaseName(fileName);
return baseName;
}
}

View File

@ -1,7 +1,6 @@
package com.ruoyi.common.utils.file;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
@ -59,13 +58,12 @@ public class ImageUtils
/**
* 读取文件为字节数据
*
* @param key 地址
* @param url 地址
* @return 字节数据
*/
public static byte[] readFile(String url)
{
InputStream in = null;
ByteArrayOutputStream baos = null;
try
{
if (url.startsWith("http"))
@ -95,7 +93,6 @@ public class ImageUtils
finally
{
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(baos);
}
}
}

View File

@ -4,7 +4,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletRequest;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
@ -25,7 +25,7 @@ public class HttpHelper
BufferedReader reader = null;
try (InputStream inputStream = request.getInputStream())
{
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line = "";
while ((line = reader.readLine()) != null)
{

View File

@ -9,6 +9,7 @@ import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
@ -130,9 +131,8 @@ public class HttpUtils
StringBuilder result = new StringBuilder();
try
{
String urlNameString = url;
log.info("sendPost - {}", urlNameString);
URL realUrl = new URL(urlNameString);
log.info("sendPost - {}", url);
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
@ -144,7 +144,7 @@ public class HttpUtils
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null)
{
@ -218,7 +218,7 @@ public class HttpUtils
{
if (ret != null && !"".equals(ret.trim()))
{
result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
}
}
log.info("recv - {}", result);

View File

@ -25,7 +25,6 @@ public class AddressUtils
public static String getRealAddressByIP(String ip)
{
String address = UNKNOWN;
// 内网不查询
if (IpUtils.internalIp(ip))
{
@ -51,6 +50,6 @@ public class AddressUtils
log.error("获取地理位置异常 {}", ip);
}
}
return address;
return UNKNOWN;
}
}

View File

@ -4,7 +4,6 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.html.EscapeUtil;
/**
* 获取IP方法
@ -13,6 +12,12 @@ import com.ruoyi.common.utils.html.EscapeUtil;
*/
public class IpUtils
{
/**
* 获取客户端IP
*
* @param request 请求对象
* @return IP地址
*/
public static String getIpAddr(HttpServletRequest request)
{
if (request == null)
@ -41,15 +46,28 @@ public class IpUtils
{
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : EscapeUtil.clean(ip);
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
}
/**
* 检查是否为内部IP地址
*
* @param ip IP地址
* @return 结果
*/
public static boolean internalIp(String ip)
{
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
/**
* 检查是否为内部IP地址
*
* @param addr byte地址
* @return 结果
*/
private static boolean internalIp(byte[] addr)
{
if (StringUtils.isNull(addr) || addr.length < 2)
@ -110,7 +128,8 @@ public class IpUtils
{
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L)) {
if ((l < 0L) || (l > 4294967295L))
{
return null;
}
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
@ -120,12 +139,14 @@ public class IpUtils
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L)) {
if ((l < 0L) || (l > 255L))
{
return null;
}
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L)) {
if ((l < 0L) || (l > 16777215L))
{
return null;
}
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
@ -136,13 +157,15 @@ public class IpUtils
for (i = 0; i < 2; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
if ((l < 0L) || (l > 255L))
{
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L)) {
if ((l < 0L) || (l > 65535L))
{
return null;
}
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
@ -152,7 +175,8 @@ public class IpUtils
for (i = 0; i < 4; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
if ((l < 0L) || (l > 255L))
{
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
@ -169,6 +193,11 @@ public class IpUtils
return bytes;
}
/**
* 获取IP地址
*
* @return 本地IP地址
*/
public static String getHostIp()
{
try
@ -181,6 +210,11 @@ public class IpUtils
return "127.0.0.1";
}
/**
* 获取主机名
*
* @return 本地主机名
*/
public static String getHostName()
{
try
@ -192,4 +226,39 @@ public class IpUtils
}
return "未知";
}
/**
* 从多级反向代理中获得第一个非unknown IP地址
*
* @param ip 获得的IP地址
* @return 第一个非unknown IP地址
*/
public static String getMultistageReverseProxyIp(String ip)
{
// 多级反向代理检测
if (ip != null && ip.indexOf(",") > 0)
{
final String[] ips = ip.trim().split(",");
for (String subIp : ips)
{
if (false == isUnknown(subIp))
{
ip = subIp;
break;
}
}
}
return ip;
}
/**
* 检测给定字符串是否为未知多用于检测HTTP请求相关
*
* @param checkString 被检测的字符串
* @return 是否未知
*/
public static boolean isUnknown(String checkString)
{
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
}
}

View File

@ -9,6 +9,8 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
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.Comparator;
@ -20,6 +22,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.RegExUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFPicture;
import org.apache.poi.hssf.usermodel.HSSFPictureData;
@ -86,6 +89,10 @@ public class ExcelUtil<T>
{
private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
public static final String FORMULA_REGEX_STR = "=|-|\\+|@";
public static final String[] FORMULA_STR = { "=", "-", "+", "@" };
/**
* Excel sheet最大行数默认65536
*/
@ -312,7 +319,7 @@ public class ExcelUtil<T>
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat))
{
val = DateUtils.parseDateToStr(dateFormat, (Date) val);
val = parseDateToStr(dateFormat, val);
}
else
{
@ -324,7 +331,7 @@ public class ExcelUtil<T>
{
val = Convert.toInt(val);
}
else if (Long.TYPE == fieldType || Long.class == fieldType)
else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
{
val = Convert.toLong(val);
}
@ -429,7 +436,6 @@ public class ExcelUtil<T>
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @return 结果
* @throws IOException
*/
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName)
{
@ -444,7 +450,6 @@ public class ExcelUtil<T>
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
* @throws IOException
*/
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title)
{
@ -667,21 +672,6 @@ public class ExcelUtil<T>
style.setFont(totalFont);
styles.put("total", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.LEFT);
styles.put("data1", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.CENTER);
styles.put("data2", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(HorizontalAlignment.RIGHT);
styles.put("data3", style);
return styles;
}
@ -710,7 +700,13 @@ public class ExcelUtil<T>
{
if (ColumnType.STRING == attr.cellType())
{
cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());
String cellValue = Convert.toStr(value);
// 对于任何以表达式触发字符 =-+@开头的单元格直接使用tab字符作为前缀防止CSV注入
if (StringUtils.startsWithAny(cellValue, FORMULA_STR))
{
cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0");
}
cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix());
}
else if (ColumnType.NUMERIC == attr.cellType())
{
@ -775,17 +771,10 @@ public class ExcelUtil<T>
// 设置列宽
sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
}
// 如果设置了提示信息则鼠标放上去提示.
if (StringUtils.isNotEmpty(attr.prompt()))
if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0)
{
// 这里默认设了2-101列提示.
setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);
}
// 如果设置了combo属性则本列只能选择不能输入
if (attr.combo().length > 0)
{
// 这里默认设了2-101列只能选择不能输入.
setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
// 提示信息或只能选择不能输入的列内容.
setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);
}
}
@ -804,8 +793,7 @@ public class ExcelUtil<T>
{
// 创建cell
cell = row.createCell(column);
int align = attr.align().value();
cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));
setDataCell(cell, attr);
// 用于读取对象中的属性
Object value = getTargetValue(vo, field, attr);
@ -815,7 +803,7 @@ public class ExcelUtil<T>
String dictType = attr.dictType();
if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
{
cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
cell.setCellValue(parseDateToStr(dateFormat, value));
}
else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
{
@ -849,48 +837,57 @@ public class ExcelUtil<T>
}
/**
* 设置 POI XSSFSheet 单元格提示
* 设置单元格样式
*
* @param cell 单元格
* @param excel 注解信息
*/
public void setDataCell(Cell cell, Excel excel)
{
CellStyle style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setAlignment(excel.align());
Font dataFont = wb.createFont();
dataFont.setFontName("Arial");
dataFont.setFontHeightInPoints((short) 10);
dataFont.setColor(excel.color().index);
style.setFont(dataFont);
cell.setCellStyle(style);
}
/**
* 设置 POI XSSFSheet 单元格提示或选择框
*
* @param sheet 表单
* @param promptTitle 提示标题
* @param textlist 下拉框显示的内容
* @param promptContent 提示内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
*/
public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,
public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow,
int firstCol, int endCol)
{
DataValidationHelper helper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = helper.createCustomConstraint("DD1");
DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1");
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
DataValidation dataValidation = helper.createValidation(constraint, regions);
dataValidation.createPromptBox(promptTitle, promptContent);
dataValidation.setShowPromptBox(true);
sheet.addValidationData(dataValidation);
}
/**
* 设置某些列的值只能输入预制的数据,显示下拉框.
*
* @param sheet 要设置的sheet.
* @param textlist 下拉框显示的内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
* @return 设置好的sheet.
*/
public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol)
if (StringUtils.isNotEmpty(promptContent))
{
DataValidationHelper helper = sheet.getDataValidationHelper();
// 加载下拉列表内容
DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);
// 设置数据有效性加载在哪个单元格上,四个参数分别是起始行终止行起始列终止列
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
// 数据有效性对象
DataValidation dataValidation = helper.createValidation(constraint, regions);
// 如果设置了提示信息则鼠标放上去提示
dataValidation.createPromptBox("", promptContent);
dataValidation.setShowPromptBox(true);
}
// 处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation)
{
@ -901,7 +898,6 @@ public class ExcelUtil<T>
{
dataValidation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(dataValidation);
}
@ -1113,7 +1109,7 @@ public class ExcelUtil<T>
if (StringUtils.isNotEmpty(excel.targetAttr()))
{
String target = excel.targetAttr();
if (target.indexOf(".") > -1)
if (target.contains("."))
{
String[] targets = target.split("[.]");
for (String name : targets)
@ -1208,7 +1204,7 @@ public class ExcelUtil<T>
for (Object[] os : this.fields)
{
Excel excel = (Excel) os[1];
maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();
maxHeight = Math.max(maxHeight, excel.height());
}
return (short) (maxHeight * 20);
}
@ -1388,4 +1384,37 @@ public class ExcelUtil<T>
}
return sheetIndexPicMap;
}
/**
* 格式化不同类型的日期对象
*
* @param dateFormat 日期格式
* @param val 被格式化的日期对象
* @return 格式化后的日期字符
*/
public String parseDateToStr(String dateFormat, Object val)
{
if (val == null)
{
return "";
}
String str;
if (val instanceof Date)
{
str = DateUtils.parseDateToStr(dateFormat, (Date) val);
}
else if (val instanceof LocalDateTime)
{
str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val));
}
else if (val instanceof LocalDate)
{
str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val));
}
else
{
str = val.toString();
}
return str;
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.utils.sign;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -55,7 +56,7 @@ public class Md5Utils
{
try
{
return new String(toHex(md5(s)).getBytes("UTF-8"), "UTF-8");
return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
}
catch (Exception e)
{

View File

@ -10,6 +10,11 @@ import com.ruoyi.common.utils.StringUtils;
*/
public class SqlUtil
{
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
/**
* 仅支持字母数字下划线空格逗号小数点支持多个字段排序
*/
@ -34,4 +39,23 @@ public class SqlUtil
{
return value.matches(SQL_PATTERN);
}
/**
* SQL关键字检查
*/
public static void filterKeyword(String value)
{
if (StringUtils.isEmpty(value))
{
return;
}
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords)
{
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1)
{
throw new UtilException("参数存在SQL注入风险");
}
}
}
}

View File

@ -0,0 +1,86 @@
package com.ruoyi.common.utils.uuid;
import java.util.concurrent.atomic.AtomicInteger;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
/**
* @author ruoyi 序列生成类
*/
public class Seq
{
// 通用序列类型
public static final String commSeqType = "COMMON";
// 上传序列类型
public static final String uploadSeqType = "UPLOAD";
// 通用接口序列数
private static AtomicInteger commSeq = new AtomicInteger(1);
// 上传接口序列数
private static AtomicInteger uploadSeq = new AtomicInteger(1);
// 机器标识
private static String machineCode = "A";
/**
* 获取通用序列号
*
* @return 序列值
*/
public static String getId()
{
return getId(commSeqType);
}
/**
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
*
* @return 序列值
*/
public static String getId(String type)
{
AtomicInteger atomicInt = commSeq;
if (uploadSeqType.equals(type))
{
atomicInt = uploadSeq;
}
return getId(atomicInt, 3);
}
/**
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
*
* @param atomicInt 序列数
* @param length 数值长度
* @return 序列值
*/
public static String getId(AtomicInteger atomicInt, int length)
{
String result = DateUtils.dateTimeNow();
result += machineCode;
result += getSeq(atomicInt, length);
return result;
}
/**
* 序列循环递增字符串[1, 10 (length)幂次方), 用0左补齐length位数
*
* @return 序列值
*/
private synchronized static String getSeq(AtomicInteger atomicInt, int length)
{
// 先取值再+1
int value = atomicInt.getAndIncrement();
// 如果更新后值>=10 (length)幂次方则重置为1
int maxSeq = (int) Math.pow(10, length);
if (atomicInt.get() >= maxSeq)
{
atomicInt.set(1);
}
// 转字符串用0左补齐
return StringUtils.padl(value, length);
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.xss;
import com.ruoyi.common.utils.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
@ -12,15 +13,19 @@ import java.util.regex.Pattern;
*/
public class XssValidator implements ConstraintValidator<Xss, String>
{
private final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext)
{
if (StringUtils.isBlank(value))
{
return true;
}
return !containsHtml(value);
}
public boolean containsHtml(String value)
public static boolean containsHtml(String value)
{
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.0</version>
<version>3.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -65,7 +65,6 @@ public class LogAspect
{
try
{
// 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser();

View File

@ -1,13 +1,13 @@
package com.ruoyi.framework.config;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import com.ruoyi.common.utils.Threads;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import com.ruoyi.common.utils.Threads;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置
@ -49,7 +49,8 @@ public class ThreadPoolConfig
protected ScheduledExecutorService scheduledExecutorService()
{
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy())
{
@Override
protected void afterExecute(Runnable r, Throwable t)

View File

@ -60,14 +60,10 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
String url = request.getRequestURI();
// 唯一值没有消息头则使用请求地址
String submitKey = request.getHeader(header);
if (StringUtils.isEmpty(submitKey))
{
submitKey = url;
}
String submitKey = StringUtils.trimToEmpty(request.getHeader(header));
// 唯一标识指定key + 消息头
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
// 唯一标识指定key + url + 消息头
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + url + submitKey;
Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
if (sessionObj != null)

View File

@ -119,4 +119,12 @@ public class Jvm
{
return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate());
}
/**
* 运行参数
*/
public String getInputArgs()
{
return ManagementFactory.getRuntimeMXBean().getInputArguments().toString();
}
}

View File

@ -17,6 +17,7 @@ import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
@ -102,7 +103,7 @@ public class SysLoginService
*/
public void validateCaptcha(String username, String code, String uuid)
{
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String verifyKey = Constants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null)

View File

@ -100,7 +100,7 @@ public class SysRegisterService
*/
public void validateCaptcha(String username, String code, String uuid)
{
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String verifyKey = Constants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null)

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.0</version>
<version>3.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -59,12 +59,12 @@ public class GenController extends BaseController
* 修改代码生成业务
*/
@PreAuthorize("@ss.hasPermi('tool:gen:query')")
@GetMapping(value = "/{talbleId}")
public AjaxResult getInfo(@PathVariable Long talbleId)
@GetMapping(value = "/{tableId}")
public AjaxResult getInfo(@PathVariable Long tableId)
{
GenTable table = genTableService.selectGenTableById(talbleId);
GenTable table = genTableService.selectGenTableById(tableId);
List<GenTable> tables = genTableService.selectGenTableAll();
List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(talbleId);
List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId);
Map<String, Object> map = new HashMap<String, Object>();
map.put("info", table);
map.put("rows", list);
@ -88,7 +88,7 @@ public class GenController extends BaseController
* 查询数据表字段列表
*/
@PreAuthorize("@ss.hasPermi('tool:gen:list')")
@GetMapping(value = "/column/{talbleId}")
@GetMapping(value = "/column/{tableId}")
public TableDataInfo columnList(Long tableId)
{
TableDataInfo dataInfo = new TableDataInfo();

View File

@ -7,6 +7,7 @@ import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@ -286,7 +287,7 @@ public class GenTableServiceImpl implements IGenTableService
{
GenTable table = genTableMapper.selectGenTableByName(tableName);
List<GenTableColumn> tableColumns = table.getColumns();
List<String> tableColumnNames = tableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
Map<String, GenTableColumn> tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity()));
List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
if (StringUtils.isEmpty(dbTableColumns))
@ -296,9 +297,29 @@ public class GenTableServiceImpl implements IGenTableService
List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
dbTableColumns.forEach(column -> {
if (!tableColumnNames.contains(column.getColumnName()))
{
GenUtils.initColumnField(column, table);
if (tableColumnMap.containsKey(column.getColumnName()))
{
GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName());
column.setColumnId(prevColumn.getColumnId());
if (column.isList())
{
// 如果是列表继续保留查询方式/字典类型选项
column.setDictType(prevColumn.getDictType());
column.setQueryType(prevColumn.getQueryType());
}
if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk()
&& (column.isInsert() || column.isEdit())
&& ((column.isUsableColumn()) || (!column.isSuperColumn())))
{
// 如果是(新增/修改&非主键/非忽略及父属性)继续保留必填/显示类型选项
column.setIsRequired(prevColumn.getIsRequired());
column.setHtmlType(prevColumn.getHtmlType());
}
genTableColumnMapper.updateGenTableColumn(column);
}
else
{
genTableColumnMapper.insertGenTableColumn(column);
}
});

View File

@ -42,6 +42,7 @@ public class GenUtils
column.setJavaField(StringUtils.toCamelCase(columnName));
// 设置默认类型
column.setJavaType(GenConstants.TYPE_STRING);
column.setQueryType(GenConstants.QUERY_EQ);
if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType))
{
@ -151,8 +152,7 @@ public class GenUtils
{
int lastIndex = packageName.lastIndexOf(".");
int nameLength = packageName.length();
String moduleName = StringUtils.substring(packageName, lastIndex + 1, nameLength);
return moduleName;
return StringUtils.substring(packageName, lastIndex + 1, nameLength);
}
/**
@ -165,8 +165,7 @@ public class GenUtils
{
int lastIndex = tableName.lastIndexOf("_");
int nameLength = tableName.length();
String businessName = StringUtils.substring(tableName, lastIndex + 1, nameLength);
return businessName;
return StringUtils.substring(tableName, lastIndex + 1, nameLength);
}
/**

View File

@ -3,6 +3,7 @@ package com.ruoyi.generator.util;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.velocity.VelocityContext;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.GenConstants;
@ -227,8 +228,7 @@ public class VelocityUtils
public static String getPackagePrefix(String packageName)
{
int lastIndex = packageName.lastIndexOf(".");
String basePackage = StringUtils.substring(packageName, 0, lastIndex);
return basePackage;
return StringUtils.substring(packageName, 0, lastIndex);
}
/**
@ -270,7 +270,24 @@ public class VelocityUtils
public static String getDicts(GenTable genTable)
{
List<GenTableColumn> columns = genTable.getColumns();
List<String> dicts = new ArrayList<String>();
Set<String> dicts = new HashSet<String>();
addDicts(dicts, columns);
if (StringUtils.isNotNull(genTable.getSubTable()))
{
List<GenTableColumn> subColumns = genTable.getSubTable().getColumns();
addDicts(dicts, subColumns);
}
return StringUtils.join(dicts, ", ");
}
/**
* 添加字典列表
*
* @param dicts 字典列表
* @param columns 列集合
*/
public static void addDicts(Set<String> dicts, List<GenTableColumn> columns)
{
for (GenTableColumn column : columns)
{
if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
@ -280,7 +297,6 @@ public class VelocityUtils
dicts.add("'" + column.getDictType() + "'");
}
}
return StringUtils.join(dicts, ", ");
}
/**

View File

@ -1,6 +1,6 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
@ -17,13 +17,12 @@
v-model="queryParams.${column.javaField}"
placeholder="请输入${comment}"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option
v-for="dict in dict.type.${dictType}"
:key="dict.value"
@ -34,13 +33,13 @@
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-date-picker clearable size="small"
<el-date-picker clearable
v-model="queryParams.${column.javaField}"
type="date"
value-format="yyyy-MM-dd"
@ -51,7 +50,6 @@
<el-form-item label="${comment}">
<el-date-picker
v-model="daterange${AttrName}"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
@ -80,14 +78,24 @@
v-hasPermi="['${moduleName}:${businessName}:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-sort"
size="mini"
@click="toggleExpandAll"
>展开/折叠</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-if="refreshTable"
v-loading="loading"
:data="${businessName}List"
row-key="${treeCode}"
default-expand-all
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>
#foreach($column in $columns)
@ -105,6 +113,12 @@
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
#elseif($column.list && $column.htmlType == "imageUpload")
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
</template>
</el-table-column>
#elseif($column.list && "" != $column.dictType)
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template slot-scope="scope">
@ -174,11 +188,11 @@
</el-form-item>
#elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}">
<imageUpload v-model="form.${field}"/>
<image-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}">
<fileUpload v-model="form.${field}"/>
<file-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "editor")
<el-form-item label="${comment}">
@ -238,7 +252,7 @@
</el-form-item>
#elseif($column.htmlType == "datetime")
<el-form-item label="${comment}" prop="${field}">
<el-date-picker clearable size="small"
<el-date-picker clearable
v-model="form.${field}"
type="date"
value-format="yyyy-MM-dd"
@ -289,6 +303,10 @@ export default {
title: "",
// 是否显示弹出层
open: false,
// 是否展开,默认全部展开
isExpandAll: true,
// 重新渲染表格状态
refreshTable: true,
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
@ -419,6 +437,14 @@ export default {
this.open = true;
this.title = "添加${functionName}";
},
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false;
this.isExpandAll = !this.isExpandAll;
this.$nextTick(() => {
this.refreshTable = true;
});
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();

View File

@ -1,6 +1,6 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
@ -17,13 +17,12 @@
v-model="queryParams.${column.javaField}"
placeholder="请输入${comment}"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option
v-for="dict in dict.type.${dictType}"
:key="dict.value"
@ -34,24 +33,23 @@
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-date-picker clearable size="small"
<el-date-picker clearable
v-model="queryParams.${column.javaField}"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择${comment}">
placeholder="选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
<el-form-item label="${comment}">
<el-date-picker
v-model="daterange${AttrName}"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
@ -133,6 +131,12 @@
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
#elseif($column.list && $column.htmlType == "imageUpload")
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
</template>
</el-table-column>
#elseif($column.list && "" != $column.dictType)
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template slot-scope="scope">
@ -195,11 +199,11 @@
</el-form-item>
#elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}">
<imageUpload v-model="form.${field}"/>
<image-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}">
<fileUpload v-model="form.${field}"/>
<file-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "editor")
<el-form-item label="${comment}">
@ -259,11 +263,11 @@
</el-form-item>
#elseif($column.htmlType == "datetime")
<el-form-item label="${comment}" prop="${field}">
<el-date-picker clearable size="small"
<el-date-picker clearable
v-model="form.${field}"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择${comment}">
placeholder="选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "textarea")
@ -296,12 +300,39 @@
#set($comment=$column.columnComment)
#end
#if($column.pk || $javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
<el-table-column label="$comment" prop="${javaField}">
#elseif($column.list && $column.htmlType == "input")
<el-table-column label="$comment" prop="${javaField}" width="150">
<template slot-scope="scope">
<el-input v-model="scope.row.$javaField" placeholder="请输入$comment" />
</template>
</el-table-column>
#elseif($column.list && $column.htmlType == "datetime")
<el-table-column label="$comment" prop="${javaField}" width="240">
<template slot-scope="scope">
<el-date-picker clearable v-model="scope.row.$javaField" type="date" value-format="yyyy-MM-dd" placeholder="请选择$comment" />
</template>
</el-table-column>
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
<el-table-column label="$comment" prop="${javaField}" width="150">
<template slot-scope="scope">
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
<el-option
v-for="dict in dict.type.$column.dictType"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</template>
</el-table-column>
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
<el-table-column label="$comment" prop="${javaField}" width="150">
<template slot-scope="scope">
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
<el-option label="请选择字典生成" value="" />
</el-select>
</template>
</el-table-column>
#end
#end
</el-table>

View File

@ -17,13 +17,12 @@
v-model="queryParams.${column.javaField}"
placeholder="请输入${comment}"
clearable
size="small"
@keyup.enter="handleQuery"
/>
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option
v-for="dict in ${dictType}"
:key="dict.value"
@ -34,13 +33,13 @@
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-date-picker clearable size="small"
<el-date-picker clearable
v-model="queryParams.${column.javaField}"
type="date"
value-format="YYYY-MM-DD"
@ -48,11 +47,9 @@
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
<el-form-item label="${comment}">
<el-form-item label="${comment}" style="width: 308px">
<el-date-picker
v-model="daterange${AttrName}"
size="small"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
@ -64,8 +61,8 @@
#end
#end
<el-form-item>
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -75,19 +72,27 @@
type="primary"
plain
icon="Plus"
size="mini"
@click="handleAdd"
v-hasPermi="['${moduleName}:${businessName}:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="Sort"
@click="toggleExpandAll"
>展开/折叠</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-if="refreshTable"
v-loading="loading"
:data="${businessName}List"
row-key="${treeCode}"
default-expand-all
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>
#foreach($column in $columns)
@ -105,6 +110,12 @@
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
#elseif($column.list && $column.htmlType == "imageUpload")
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
<template #default="scope">
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
</template>
</el-table-column>
#elseif($column.list && "" != $column.dictType)
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template #default="scope">
@ -126,21 +137,18 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
size="mini"
type="text"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="Plus"
@click="handleAdd(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:add']"
>新增</el-button>
<el-button
size="mini"
type="text"
icon="Delete"
@click="handleDelete(scope.row)"
@ -166,11 +174,13 @@
#set($dictType=$column.dictType)
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
<el-form-item label="${comment}" prop="${treeParentCode}">
<tree-select
v-model:value="form.${treeParentCode}"
:options="${businessName}Options"
:objMap="{ value: '${treeCode}', label: '${treeName}', children: 'children' }"
<el-tree-select
v-model="form.${treeParentCode}"
:data="${businessName}Options"
:props="{ value: '${treeCode}', label: '${treeName}', children: 'children' }"
value-key="${treeCode}"
placeholder="请选择${comment}"
check-strictly
/>
</el-form-item>
#elseif($column.htmlType == "input")
@ -179,11 +189,11 @@
</el-form-item>
#elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}">
<imageUpload v-model="form.${field}"/>
<image-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}">
<fileUpload v-model="form.${field}"/>
<file-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "editor")
<el-form-item label="${comment}">
@ -243,7 +253,7 @@
</el-form-item>
#elseif($column.htmlType == "datetime")
<el-form-item label="${comment}" prop="${field}">
<el-date-picker clearable size="small"
<el-date-picker clearable
v-model="form.${field}"
type="date"
value-format="YYYY-MM-DD"
@ -284,6 +294,8 @@ const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const title = ref("");
const isExpandAll = ref(true);
const refreshTable = ref(true);
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
@ -344,8 +356,8 @@ function getList() {
}
/** 查询${functionName}下拉树结构 */
async function getTreeselect() {
await list${BusinessName}().then(response => {
function getTreeselect() {
list${BusinessName}().then(response => {
${businessName}Options.value = [];
const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
data.children = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
@ -394,9 +406,9 @@ function resetQuery() {
}
/** 新增按钮操作 */
async function handleAdd(row) {
function handleAdd(row) {
reset();
await getTreeselect();
getTreeselect();
if (row != null && row.${treeCode}) {
form.value.${treeParentCode} = row.${treeCode};
} else {
@ -406,6 +418,15 @@ async function handleAdd(row) {
title.value = "添加${functionName}";
}
/** 展开/折叠操作 */
function toggleExpandAll() {
refreshTable.value = false;
isExpandAll.value = !isExpandAll.value;
nextTick(() => {
refreshTable.value = true;
});
}
/** 修改按钮操作 */
async function handleUpdate(row) {
reset();

View File

@ -17,13 +17,12 @@
v-model="queryParams.${column.javaField}"
placeholder="请输入${comment}"
clearable
size="small"
@keyup.enter="handleQuery"
/>
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option
v-for="dict in ${dictType}"
:key="dict.value"
@ -34,25 +33,23 @@
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-date-picker clearable size="small"
<el-date-picker clearable
v-model="queryParams.${column.javaField}"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择${comment}">
placeholder="选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
<el-form-item label="${comment}">
<el-form-item label="${comment}" style="width: 308px">
<el-date-picker
v-model="daterange${AttrName}"
size="small"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
@ -64,8 +61,8 @@
#end
#end
<el-form-item>
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
@ -75,7 +72,6 @@
type="primary"
plain
icon="Plus"
size="mini"
@click="handleAdd"
v-hasPermi="['${moduleName}:${businessName}:add']"
>新增</el-button>
@ -85,7 +81,6 @@
type="success"
plain
icon="Edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['${moduleName}:${businessName}:edit']"
@ -96,7 +91,6 @@
type="danger"
plain
icon="Delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['${moduleName}:${businessName}:remove']"
@ -107,7 +101,6 @@
type="warning"
plain
icon="Download"
size="mini"
@click="handleExport"
v-hasPermi="['${moduleName}:${businessName}:export']"
>导出</el-button>
@ -133,6 +126,12 @@
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
#elseif($column.list && $column.htmlType == "imageUpload")
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
<template #default="scope">
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
</template>
</el-table-column>
#elseif($column.list && "" != $column.dictType)
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template #default="scope">
@ -150,14 +149,12 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
size="mini"
type="text"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="Delete"
@click="handleDelete(scope.row)"
@ -170,8 +167,8 @@
<pagination
v-show="total>0"
:total="total"
v-model:p:page="queryParams.pageNum"
v-model:p:limit="queryParams.pageSize"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
@ -195,11 +192,11 @@
</el-form-item>
#elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}">
<imageUpload v-model="form.${field}"/>
<image-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}">
<fileUpload v-model="form.${field}"/>
<file-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "editor")
<el-form-item label="${comment}">
@ -259,11 +256,11 @@
</el-form-item>
#elseif($column.htmlType == "datetime")
<el-form-item label="${comment}" prop="${field}">
<el-date-picker clearable size="small"
<el-date-picker clearable
v-model="form.${field}"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择${comment}">
placeholder="选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "textarea")
@ -278,10 +275,10 @@
<el-divider content-position="center">${subTable.functionName}信息</el-divider>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" icon="Plus" size="mini" @click="handleAdd${subClassName}">添加</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd${subClassName}">添加</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" icon="Delete" size="mini" @click="handleDelete${subClassName}">删除</el-button>
<el-button type="danger" icon="Delete" @click="handleDelete${subClassName}">删除</el-button>
</el-col>
</el-row>
<el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}">
@ -296,12 +293,44 @@
#set($comment=$column.columnComment)
#end
#if($column.pk || $javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
<el-table-column label="$comment" prop="${javaField}">
#elseif($column.list && $column.htmlType == "input")
<el-table-column label="$comment" prop="${javaField}" width="150">
<template #default="scope">
<el-input v-model="scope.row.$javaField" placeholder="请输入$comment" />
</template>
</el-table-column>
#elseif($column.list && $column.htmlType == "datetime")
<el-table-column label="$comment" prop="${javaField}" width="240">
<template #default="scope">
<el-date-picker clearable
v-model="scope.row.$javaField"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择$comment">
</el-date-picker>
</template>
</el-table-column>
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
<el-table-column label="$comment" prop="${javaField}" width="150">
<template #default="scope">
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
<el-option
v-for="dict in $column.dictType"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</template>
</el-table-column>
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
<el-table-column label="$comment" prop="${javaField}" width="150">
<template #default="scope">
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
<el-option label="请选择字典生成" value="" />
</el-select>
</template>
</el-table-column>
#end
#end
</el-table>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.0</version>
<version>3.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -1,57 +1,57 @@
package com.ruoyi.quartz.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.util.Properties;
/**
* 定时任务配置单机部署建议删除此类和qrtz数据库表默认走内存会最高效
*
* @author ruoyi
*/
@Configuration
public class ScheduleConfig
{
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
{
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
// quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
// 线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
// JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
// 集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
// sqlserver 启用
// prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
factory.setQuartzProperties(prop);
factory.setSchedulerName("RuoyiScheduler");
// 延时启动
factory.setStartupDelay(1);
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
// 可选QuartzScheduler
// 启动时更新己存在的Job这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factory.setOverwriteExistingJobs(true);
// 设置自动启动默认为true
factory.setAutoStartup(true);
return factory;
}
}
//package com.ruoyi.quartz.config;
//
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.scheduling.quartz.SchedulerFactoryBean;
//import javax.sql.DataSource;
//import java.util.Properties;
//
///**
// * 定时任务配置单机部署建议删除此类和qrtz数据库表默认走内存会最高效
// *
// * @author ruoyi
// */
//@Configuration
//public class ScheduleConfig
//{
// @Bean
// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
// {
// SchedulerFactoryBean factory = new SchedulerFactoryBean();
// factory.setDataSource(dataSource);
//
// // quartz参数
// Properties prop = new Properties();
// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
// prop.put("org.quartz.scheduler.instanceId", "AUTO");
// // 线程池配置
// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
// prop.put("org.quartz.threadPool.threadCount", "20");
// prop.put("org.quartz.threadPool.threadPriority", "5");
// // JobStore配置
// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
// // 集群配置
// prop.put("org.quartz.jobStore.isClustered", "true");
// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
//
// // sqlserver 启用
// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
// prop.put("org.quartz.jobStore.misfireThreshold", "12000");
// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
// factory.setQuartzProperties(prop);
//
// factory.setSchedulerName("RuoyiScheduler");
// // 延时启动
// factory.setStartupDelay(1);
// factory.setApplicationContextSchedulerContextKey("applicationContextKey");
// // 可选QuartzScheduler
// // 启动时更新己存在的Job这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
// factory.setOverwriteExistingJobs(true);
// // 设置自动启动默认为true
// factory.setAutoStartup(true);
//
// return factory;
// }
//}

View File

@ -25,6 +25,7 @@ import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.service.ISysJobService;
import com.ruoyi.quartz.util.CronUtils;
import com.ruoyi.quartz.util.ScheduleUtils;
/**
* 调度任务信息操作处理
@ -87,20 +88,24 @@ public class SysJobController extends BaseController
}
else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
}
else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP))
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap://'调用");
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
}
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
}
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规");
}
else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
{
return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
}
job.setCreateBy(getUsername());
return toAjax(jobService.insertJob(job));
}
@ -119,20 +124,24 @@ public class SysJobController extends BaseController
}
else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
{
return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
}
else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP))
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS }))
{
return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap://'调用");
return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
}
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
{
return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
}
else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
{
return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规");
}
else if (!ScheduleUtils.whiteList(job.getInvokeTarget()))
{
return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
}
job.setUpdateBy(getUsername());
return toAjax(jobService.updateJob(job));
}

View File

@ -10,9 +10,12 @@ import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.exception.job.TaskException.Code;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.quartz.domain.SysJob;
/**
@ -110,4 +113,22 @@ public class ScheduleUtils
+ "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
}
}
/**
* 检查包名是否为白名单配置
*
* @param invokeTarget 目标字符串
* @return 结果
*/
public static boolean whiteList(String invokeTarget)
{
String packageName = StringUtils.substringBefore(invokeTarget, "(");
int count = StringUtils.countMatches(packageName, ".");
if (count > 1)
{
return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR);
}
Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);
return StringUtils.containsAnyIgnoreCase(obj.getClass().getPackage().getName(), Constants.JOB_WHITELIST_STR);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.0</version>
<version>3.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -20,7 +20,7 @@ public interface SysUserMapper
public List<SysUser> selectUserList(SysUser sysUser);
/**
* 根据条件分页查询已配用户角色列表
* 根据条件分页查询已配用户角色列表
*
* @param user 用户信息
* @return 用户信息集合信息

View File

@ -61,7 +61,6 @@ public interface ISysConfigService
* 批量删除参数信息
*
* @param configIds 需要删除的参数ID
* @return 结果
*/
public void deleteConfigByIds(Long[] configIds);

View File

@ -39,7 +39,6 @@ public interface ISysDictDataService
* 批量删除字典数据信息
*
* @param dictCodes 需要删除的字典数据ID
* @return 结果
*/
public void deleteDictDataByIds(Long[] dictCodes);

View File

@ -54,7 +54,6 @@ public interface ISysDictTypeService
* 批量删除字典信息
*
* @param dictIds 需要删除的字典ID
* @return 结果
*/
public void deleteDictTypeByIds(Long[] dictIds);

View File

@ -29,7 +29,7 @@ public interface ISysLogininforService
* 批量删除系统登录日志
*
* @param infoIds 需要删除的登录日志ID
* @return
* @return 结果
*/
public int deleteLogininforByIds(Long[] infoIds);

View File

@ -78,7 +78,6 @@ public interface ISysPostService
*
* @param postIds 需要删除的岗位ID
* @return 结果
* @throws Exception 异常
*/
public int deletePostByIds(Long[] postIds);

View File

@ -146,7 +146,6 @@ public class SysConfigServiceImpl implements ISysConfigService
* 批量删除参数信息
*
* @param configIds 需要删除的参数ID
* @return 结果
*/
@Override
public void deleteConfigByIds(Long[] configIds)

View File

@ -63,9 +63,8 @@ public class SysDeptServiceImpl implements ISysDeptService
{
tempList.add(dept.getDeptId());
}
for (Iterator<SysDept> iterator = depts.iterator(); iterator.hasNext();)
for (SysDept dept : depts)
{
SysDept dept = (SysDept) iterator.next();
// 如果是顶级节点, 遍历该父节点的所有子节点
if (!tempList.contains(dept.getParentId()))
{

View File

@ -60,7 +60,6 @@ public class SysDictDataServiceImpl implements ISysDictDataService
* 批量删除字典数据信息
*
* @param dictCodes 需要删除的字典数据ID
* @return 结果
*/
@Override
public void deleteDictDataByIds(Long[] dictCodes)

View File

@ -1,5 +1,13 @@
package com.ruoyi.system.service.impl;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.entity.SysDictType;
@ -9,11 +17,6 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.mapper.SysDictDataMapper;
import com.ruoyi.system.mapper.SysDictTypeMapper;
import com.ruoyi.system.service.ISysDictTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* 字典 业务层处理
@ -112,7 +115,6 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
* 批量删除字典类型信息
*
* @param dictIds 需要删除的字典ID
* @return 结果
*/
@Override
public void deleteDictTypeByIds(Long[] dictIds)
@ -135,11 +137,12 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService
@Override
public void loadingDictCache()
{
List<SysDictType> dictTypeList = dictTypeMapper.selectDictTypeAll();
for (SysDictType dictType : dictTypeList)
SysDictData dictData = new SysDictData();
dictData.setStatus("0");
Map<String, List<SysDictData>> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType));
for (Map.Entry<String, List<SysDictData>> entry : dictDataMap.entrySet())
{
List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dictType.getDictType());
DictUtils.setDictCache(dictType.getDictType(), dictDatas);
DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList()));
}
}

View File

@ -46,7 +46,7 @@ public class SysLogininforServiceImpl implements ISysLogininforService
* 批量删除系统登录日志
*
* @param infoIds 需要删除的登录日志ID
* @return
* @return 结果
*/
@Override
public int deleteLogininforByIds(Long[] infoIds)

View File

@ -176,7 +176,7 @@ public class SysMenuServiceImpl implements ISysMenuService
else if (menu.getParentId().intValue() == 0 && isInnerLink(menu))
{
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
router.setPath("/inner");
router.setPath("/");
List<RouterVo> childrenList = new ArrayList<RouterVo>();
RouterVo children = new RouterVo();
String routerPath = innerLinkReplaceEach(menu.getPath());
@ -498,7 +498,7 @@ public class SysMenuServiceImpl implements ISysMenuService
*/
private boolean hasChild(List<SysMenu> list, SysMenu t)
{
return getChildList(list, t).size() > 0 ? true : false;
return getChildList(list, t).size() > 0;
}
/**

View File

@ -137,7 +137,6 @@ public class SysPostServiceImpl implements ISysPostService
*
* @param postIds 需要删除的岗位ID
* @return 结果
* @throws Exception 异常
*/
@Override
public int deletePostByIds(Long[] postIds)

View File

@ -361,6 +361,7 @@ public class SysRoleServiceImpl implements ISysRoleService
for (Long roleId : roleIds)
{
checkRoleAllowed(new SysRole(roleId));
checkRoleDataScope(roleId);
SysRole role = selectRoleById(roleId);
if (countUserRoleByRoleId(roleId) > 0)
{
@ -403,7 +404,7 @@ public class SysRoleServiceImpl implements ISysRoleService
* 批量选择授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要删除的用户数据ID
* @param userIds 需要授权的用户数据ID
* @return 结果
*/
@Override

View File

@ -482,6 +482,7 @@ public class SysUserServiceImpl implements ISysUserService
for (Long userId : userIds)
{
checkUserAllowed(new SysUser(userId));
checkUserDataScope(userId);
}
// 删除用户与角色关联
userRoleMapper.deleteUserRole(userIds);

View File

@ -91,7 +91,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="parentId != null and parentId != 0">parent_id,</if>
<if test="deptName != null and deptName != ''">dept_name,</if>
<if test="ancestors != null and ancestors != ''">ancestors,</if>
<if test="orderNum != null and orderNum != ''">order_num,</if>
<if test="orderNum != null">order_num,</if>
<if test="leader != null and leader != ''">leader,</if>
<if test="phone != null and phone != ''">phone,</if>
<if test="email != null and email != ''">email,</if>
@ -103,7 +103,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="parentId != null and parentId != 0">#{parentId},</if>
<if test="deptName != null and deptName != ''">#{deptName},</if>
<if test="ancestors != null and ancestors != ''">#{ancestors},</if>
<if test="orderNum != null and orderNum != ''">#{orderNum},</if>
<if test="orderNum != null">#{orderNum},</if>
<if test="leader != null and leader != ''">#{leader},</if>
<if test="phone != null and phone != ''">#{phone},</if>
<if test="email != null and email != ''">#{email},</if>
@ -119,7 +119,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="parentId != null and parentId != 0">parent_id = #{parentId},</if>
<if test="deptName != null and deptName != ''">dept_name = #{deptName},</if>
<if test="ancestors != null and ancestors != ''">ancestors = #{ancestors},</if>
<if test="orderNum != null and orderNum != ''">order_num = #{orderNum},</if>
<if test="orderNum != null">order_num = #{orderNum},</if>
<if test="leader != null">leader = #{leader},</if>
<if test="phone != null">phone = #{phone},</if>
<if test="email != null">email = #{email},</if>

View File

@ -28,7 +28,7 @@
</resultMap>
<sql id="selectMenuVo">
select menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
from sys_menu
</sql>
@ -49,13 +49,13 @@
</select>
<select id="selectMenuTreeAll" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m where m.menu_type in ('M', 'C') and m.status = 0
order by m.parent_id, m.order_num
</select>
<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
left join sys_user_role ur on rm.role_id = ur.role_id
@ -74,7 +74,7 @@
</select>
<select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
left join sys_user_role ur on rm.role_id = ur.role_id
@ -130,10 +130,10 @@
<set>
<if test="menuName != null and menuName != ''">menu_name = #{menuName},</if>
<if test="parentId != null">parent_id = #{parentId},</if>
<if test="orderNum != null and orderNum != ''">order_num = #{orderNum},</if>
<if test="orderNum != null">order_num = #{orderNum},</if>
<if test="path != null and path != ''">path = #{path},</if>
<if test="component != null">component = #{component},</if>
<if test="query != null">query = #{query},</if>
<if test="query != null">`query` = #{query},</if>
<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
<if test="menuType != null and menuType != ''">menu_type = #{menuType},</if>
@ -153,10 +153,10 @@
<if test="menuId != null and menuId != 0">menu_id,</if>
<if test="parentId != null and parentId != 0">parent_id,</if>
<if test="menuName != null and menuName != ''">menu_name,</if>
<if test="orderNum != null and orderNum != ''">order_num,</if>
<if test="orderNum != null">order_num,</if>
<if test="path != null and path != ''">path,</if>
<if test="component != null and component != ''">component,</if>
<if test="query != null and query != ''">query,</if>
<if test="query != null and query != ''">`query`,</if>
<if test="isFrame != null and isFrame != ''">is_frame,</if>
<if test="isCache != null and isCache != ''">is_cache,</if>
<if test="menuType != null and menuType != ''">menu_type,</if>
@ -171,7 +171,7 @@
<if test="menuId != null and menuId != 0">#{menuId},</if>
<if test="parentId != null and parentId != 0">#{parentId},</if>
<if test="menuName != null and menuName != ''">#{menuName},</if>
<if test="orderNum != null and orderNum != ''">#{orderNum},</if>
<if test="orderNum != null">#{orderNum},</if>
<if test="path != null and path != ''">#{path},</if>
<if test="component != null and component != ''">#{component},</if>
<if test="query != null and query != ''">#{query},</if>

View File

@ -31,6 +31,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<id property="deptId" column="dept_id" />
<result property="parentId" column="parent_id" />
<result property="deptName" column="dept_name" />
<result property="ancestors" column="ancestors" />
<result property="orderNum" column="order_num" />
<result property="leader" column="leader" />
<result property="status" column="dept_status" />
@ -47,7 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectUserVo">
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
d.dept_id, d.parent_id, d.dept_name, d.order_num, d.leader, d.status as dept_status,
d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id

28
ry.bat
View File

@ -1,21 +1,21 @@
@echo off
@echo off
rem jar平级目录
rem jar平级目录
set AppName=ruoyi-admin.jar
rem JVM参数
set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
rem JVM参数
set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
ECHO.
ECHO. [1] 启动%AppName%
ECHO. [2] 关闭%AppName%
ECHO. [3] 重启%AppName%
ECHO. [4] 启动状态 %AppName%
ECHO. [5] 退 出
ECHO. [1] 启动%AppName%
ECHO. [2] 关闭%AppName%
ECHO. [3] 重启%AppName%
ECHO. [4] 启动状态 %AppName%
ECHO. [5] 退 出
ECHO.
ECHO.请输入选择项目的序号:
ECHO.请输入选择项目的序号:
set /p ID=
IF "%id%"=="1" GOTO start
IF "%id%"=="2" GOTO stop
@ -33,13 +33,13 @@ PAUSE
PAUSE
)
start javaw -jar %JAVA_OPTS% ruoyi-admin.jar
start javaw %JVM_OPTS% -jar %AppName%
echo starting……
echo starting……
echo Start %AppName% success...
goto:eof
rem 函数stop通过jps命令查找pid并结束进程
rem 函数stop通过jps命令查找pid并结束进程
:stop
for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
set pid=%%a
@ -48,7 +48,7 @@ rem
if not defined pid (echo process %AppName% does not exists) else (
echo prepare to kill %image_name%
echo start kill %pid% ...
rem 根据进程IDkill进程
rem 根据进程IDkill进程
taskkill /f /pid %pid%
)
goto:eof

10
ry.sh
View File

@ -1,13 +1,9 @@
#!/bin/sh
# author ruoyi
# ./ry.sh start 启动
# ./ry.sh stop 停止
# ./ry.sh restart 重启
# ./ry.sh status 状态
# ./ry.sh start 启动 stop 停止 restart 重启 status 状态
AppName=ruoyi-admin.jar
# JVM参数
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
APP_HOME=`pwd`
LOG_PATH=$APP_HOME/logs/$AppName.log
@ -30,7 +26,7 @@ function start()
if [ x"$PID" != x"" ]; then
echo "$AppName is running..."
else
nohup java -jar $JVM_OPTS target/$AppName > /dev/null 2>&1 &
nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 &
echo "Start $AppName success..."
fi
}