diff --git a/README.md b/README.md index c3dd194da..b23ac6b7c 100644 --- a/README.md +++ b/README.md @@ -88,4 +88,4 @@ ## 若依交流群 -QQ群: [![加入QQ群](https://img.shields.io/badge/已满-1389287-blue.svg)](https://jq.qq.com/?_wv=1027&k=5HBAaYN) [![加入QQ群](https://img.shields.io/badge/已满-1679294-blue.svg)](https://jq.qq.com/?_wv=1027&k=5cHeRVW) [![加入QQ群](https://img.shields.io/badge/已满-1529866-blue.svg)](https://jq.qq.com/?_wv=1027&k=53R0L5Z) [![加入QQ群](https://img.shields.io/badge/已满-1772718-blue.svg)](https://jq.qq.com/?_wv=1027&k=5g75dCU) [![加入QQ群](https://img.shields.io/badge/已满-1366522-blue.svg)](https://jq.qq.com/?_wv=1027&k=58cPoHA) [![加入QQ群](https://img.shields.io/badge/已满-1382251-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Ofd4Pb) [![加入QQ群](https://img.shields.io/badge/已满-1145125-blue.svg)](https://jq.qq.com/?_wv=1027&k=5yugASz) [![加入QQ群](https://img.shields.io/badge/已满-86752435-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Rf3d2P) [![加入QQ群](https://img.shields.io/badge/已满-134072510-blue.svg)](https://jq.qq.com/?_wv=1027&k=5ZIjaeP) [![加入QQ群](https://img.shields.io/badge/已满-210336300-blue.svg)](https://jq.qq.com/?_wv=1027&k=5CJw1jY) [![加入QQ群](https://img.shields.io/badge/已满-339522636-blue.svg)](https://jq.qq.com/?_wv=1027&k=5omzbKc) [![加入QQ群](https://img.shields.io/badge/已满-130035985-blue.svg)](https://jq.qq.com/?_wv=1027&k=qPIKBb7s) [![加入QQ群](https://img.shields.io/badge/143151071-blue.svg)](https://jq.qq.com/?_wv=1027&k=4NsjKbtU) \ No newline at end of file +QQ群: [![加入QQ群](https://img.shields.io/badge/已满-1389287-blue.svg)](https://jq.qq.com/?_wv=1027&k=5HBAaYN) [![加入QQ群](https://img.shields.io/badge/已满-1679294-blue.svg)](https://jq.qq.com/?_wv=1027&k=5cHeRVW) [![加入QQ群](https://img.shields.io/badge/已满-1529866-blue.svg)](https://jq.qq.com/?_wv=1027&k=53R0L5Z) [![加入QQ群](https://img.shields.io/badge/已满-1772718-blue.svg)](https://jq.qq.com/?_wv=1027&k=5g75dCU) [![加入QQ群](https://img.shields.io/badge/已满-1366522-blue.svg)](https://jq.qq.com/?_wv=1027&k=58cPoHA) [![加入QQ群](https://img.shields.io/badge/已满-1382251-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Ofd4Pb) [![加入QQ群](https://img.shields.io/badge/已满-1145125-blue.svg)](https://jq.qq.com/?_wv=1027&k=5yugASz) [![加入QQ群](https://img.shields.io/badge/已满-86752435-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Rf3d2P) [![加入QQ群](https://img.shields.io/badge/已满-134072510-blue.svg)](https://jq.qq.com/?_wv=1027&k=5ZIjaeP) [![加入QQ群](https://img.shields.io/badge/已满-210336300-blue.svg)](https://jq.qq.com/?_wv=1027&k=5CJw1jY) [![加入QQ群](https://img.shields.io/badge/已满-339522636-blue.svg)](https://jq.qq.com/?_wv=1027&k=5omzbKc) [![加入QQ群](https://img.shields.io/badge/已满-130035985-blue.svg)](https://jq.qq.com/?_wv=1027&k=qPIKBb7s) [![加入QQ群](https://img.shields.io/badge/已满-143151071-blue.svg)](https://jq.qq.com/?_wv=1027&k=4NsjKbtU) [![加入QQ群](https://img.shields.io/badge/已满-158781320-blue.svg)](https://jq.qq.com/?_wv=1027&k=VD2pkz2G) [![加入QQ群](https://img.shields.io/badge/201531282-blue.svg)](https://jq.qq.com/?_wv=1027&k=HlshFwkJ) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 78cefbe3f..39b46e9ec 100644 --- a/pom.xml +++ b/pom.xml @@ -5,26 +5,27 @@ com.ruoyi ruoyi - 4.6.0 + 4.6.1 ruoyi http://www.ruoyi.vip 若依管理系统 - 4.6.0 + 4.6.1 UTF-8 UTF-8 1.8 3.1.1 1.7.1 2.0.0 - 1.2.4 + 1.2.6 1.21 2.3.2 2.9.2 + 2.1.4 1.3.0 - 1.2.75 + 1.2.76 5.6.0 5.7.0 2.5 @@ -95,6 +96,13 @@ ${bitwalker.version} + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot.version} + + com.github.pagehelper diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 58df34fc3..bec326bca 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 4.6.0 + 4.6.1 4.0.0 jar @@ -106,7 +106,39 @@ false ${project.artifactId} - + + ${project.artifactId} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java index 61cdcd73b..82c53ceb9 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java @@ -12,13 +12,17 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; 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.core.text.Convert; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; /** * 表格相关 @@ -107,6 +111,44 @@ public class DemoTableController extends BaseController return prefix + "/export"; } + /** + * 表格导出选择列 + */ + @GetMapping("/exportSelected") + public String exportSelected() + { + return prefix + "/exportSelected"; + } + + /** + * 导出数据 + */ + @PostMapping("/exportData") + @ResponseBody + public AjaxResult exportSelected(UserTableModel userModel, String userIds) + { + List userList = new ArrayList(Arrays.asList(new UserTableModel[users.size()])); + Collections.copy(userList, users); + + // 条件过滤 + if (StringUtils.isNotEmpty(userIds)) + { + userList.clear(); + for (Long userId : Convert.toLongArray(userIds)) + { + for (UserTableModel user : users) + { + if (user.getUserId() == userId) + { + userList.add(user); + } + } + } + } + ExcelUtil util = new ExcelUtil(UserTableModel.class); + return util.exportExcel(userList, "用户数据"); + } + /** * 翻页记住选择 */ @@ -217,12 +259,21 @@ public class DemoTableController extends BaseController } /** - * 表格拖拽操作 + * 表格行拖拽操作 */ - @GetMapping("/reorder") - public String reorder() + @GetMapping("/reorderRows") + public String reorderRows() { - return prefix + "/reorder"; + return prefix + "/reorderRows"; + } + + /** + * 表格列拖拽操作 + */ + @GetMapping("/reorderColumns") + public String reorderColumns() + { + return prefix + "/reorderColumns"; } /** @@ -399,21 +450,26 @@ class UserTableModel private int userId; /** 用户编号 */ + @Excel(name = "用户编号", cellType = ColumnType.NUMERIC) private String userCode; /** 用户姓名 */ + @Excel(name = "用户姓名") private String userName; /** 用户性别 */ private String userSex; /** 用户手机 */ + @Excel(name = "用户手机") private String userPhone; /** 用户邮箱 */ + @Excel(name = "用户邮箱") private String userEmail; /** 用户余额 */ + @Excel(name = "用户余额", cellType = ColumnType.NUMERIC) private double userBalance; /** 用户状态(0正常 1停用) */ diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/DruidController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/DruidController.java index b2647893c..98e4242f5 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/DruidController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/DruidController.java @@ -21,6 +21,6 @@ public class DruidController extends BaseController @GetMapping() public String index() { - return redirect(prefix + "/index"); + return redirect(prefix + "/index.html"); } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java index 468866e18..2375fe6fc 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java @@ -61,6 +61,7 @@ public class SysOperlogController extends BaseController return util.exportExcel(list, "操作日志"); } + @Log(title = "操作日志", businessType = BusinessType.DELETE) @RequiresPermissions("monitor:operlog:remove") @PostMapping("/remove") @ResponseBody diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java index 96307b84f..1d0197c80 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -129,19 +129,20 @@ public class SysConfigController extends BaseController @ResponseBody public AjaxResult remove(String ids) { - return toAjax(configService.deleteConfigByIds(ids)); + configService.deleteConfigByIds(ids); + return success(); } /** - * 清空缓存 + * 刷新参数缓存 */ @RequiresPermissions("system:config:remove") @Log(title = "参数管理", businessType = BusinessType.CLEAN) - @GetMapping("/clearCache") + @GetMapping("/refreshCache") @ResponseBody - public AjaxResult clearCache() + public AjaxResult refreshCache() { - configService.clearCache(); + configService.resetConfigCache(); return success(); } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java index b0cba0472..00e6f7ea0 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -59,6 +59,10 @@ public class SysDeptController extends BaseController @GetMapping("/add/{parentId}") public String add(@PathVariable("parentId") Long parentId, ModelMap mmap) { + if (!ShiroUtils.getSysUser().isAdmin()) + { + parentId = ShiroUtils.getSysUser().getDeptId(); + } mmap.put("dept", deptService.selectDeptById(parentId)); return prefix + "/add"; } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java index 87109589d..2cd7159a6 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -115,6 +115,7 @@ public class SysDictDataController extends BaseController @ResponseBody public AjaxResult remove(String ids) { - return toAjax(dictDataService.deleteDictDataByIds(ids)); + dictDataService.deleteDictDataByIds(ids); + return success(); } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java index e8b352190..4dfc2c78f 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java @@ -125,19 +125,20 @@ public class SysDictTypeController extends BaseController @ResponseBody public AjaxResult remove(String ids) { - return toAjax(dictTypeService.deleteDictTypeByIds(ids)); + dictTypeService.deleteDictTypeByIds(ids); + return success(); } /** - * 清空缓存 + * 刷新字典缓存 */ @RequiresPermissions("system:dict:remove") @Log(title = "字典类型", businessType = BusinessType.CLEAN) - @GetMapping("/clearCache") + @GetMapping("/refreshCache") @ResponseBody - public AjaxResult clearCache() + public AjaxResult refreshCache() { - dictTypeService.clearCache(); + dictTypeService.resetDictCache(); return success(); } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java index 880dab685..6454990f1 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -13,12 +13,14 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.ShiroUtils; +import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.file.FileUploadUtils; import com.ruoyi.framework.shiro.service.SysPasswordService; import com.ruoyi.system.service.ISysUserService; @@ -135,6 +137,16 @@ public class SysProfileController extends BaseController currentUser.setEmail(user.getEmail()); currentUser.setPhonenumber(user.getPhonenumber()); currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.USER_PHONE_NOT_UNIQUE.equals(userService.checkPhoneUnique(currentUser))) + { + return error("修改用户'" + currentUser.getLoginName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.USER_EMAIL_NOT_UNIQUE.equals(userService.checkEmailUnique(currentUser))) + { + return error("修改用户'" + currentUser.getLoginName() + "'失败,邮箱账号已存在"); + } if (userService.updateUserInfo(currentUser) > 0) { ShiroUtils.setSysUser(userService.selectUserById(currentUser.getUserId())); diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml index 5243abd67..3c46fd50b 100644 --- a/ruoyi-admin/src/main/resources/application-druid.yml +++ b/ruoyi-admin/src/main/resources/application-druid.yml @@ -43,7 +43,7 @@ spring: allow: url-pattern: /druid/* # 控制台管理用户名和密码 - login-username: admin + login-username: ruoyi login-password: 123456 filter: stat: diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index d013a3f1e..764a13fd2 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -3,7 +3,7 @@ ruoyi: # 名称 name: RuoYi # 版本 - version: 4.6.0 + version: 4.6.1 # 版权年份 copyrightYear: 2021 # 实例演示开关 @@ -108,8 +108,6 @@ shiro: httpOnly: true # 设置Cookie的过期时间,天为单位 maxAge: 30 - # 设置密钥,务必保持唯一性(生成方式,直接拷贝到main运行即可)KeyGenerator keygen = KeyGenerator.getInstance("AES"); SecretKey deskey = keygen.generateKey(); System.out.println(Base64.encodeToString(deskey.getEncoded())); - cipherKey: zSyK5Kp6PZAAjlT+eeNMlg== session: # Session超时时间,-1代表永不过期(默认30分钟) expireTime: 30 diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/export/bootstrap-table-export.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/export/bootstrap-table-export.js deleted file mode 100644 index 01aabefc2..000000000 --- a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/export/bootstrap-table-export.js +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @author zhixin wen - * extensions: https://github.com/kayalshri/tableExport.jquery.plugin - */ - -(function ($) { - 'use strict'; - var sprintf = $.fn.bootstrapTable.utils.sprintf; - - var TYPE_NAME = { - csv: 'CSV', - txt: 'TXT', - doc: 'Word', - excel: 'Excel' - }; - - $.extend($.fn.bootstrapTable.defaults, { - showExport: false, - exportDataType: 'all', // basic, all, selected - exportTypes: ['csv', 'txt', 'doc', 'excel'], - exportOptions: { - ignoreColumn: [0] //忽略列索引 - } - }); - - $.extend($.fn.bootstrapTable.defaults.icons, { - export: 'glyphicon glyphicon-save' - }); - - $.extend($.fn.bootstrapTable.locales, { - formatExport: function () { - return '导出'; - } - }); - $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales); - - var BootstrapTable = $.fn.bootstrapTable.Constructor, - _initToolbar = BootstrapTable.prototype.initToolbar; - - BootstrapTable.prototype.initToolbar = function () { - this.showToolbar = this.options.showExport; - - _initToolbar.apply(this, Array.prototype.slice.apply(arguments)); - - if (this.options.showExport) { - var that = this, - $btnGroup = this.$toolbar.find('>.btn-group'), - $export = $btnGroup.find('div.export'); - - if (!$export.length) { - $export = $([ - '
', - '', - '', - '
'].join('')).appendTo($btnGroup); - - var $menu = $export.find('.dropdown-menu'), - exportTypes = this.options.exportTypes; - - if (typeof this.options.exportTypes === 'string') { - var types = this.options.exportTypes.slice(1, -1).replace(/ /g, '').split(','); - - exportTypes = []; - $.each(types, function (i, value) { - exportTypes.push(value.slice(1, -1)); - }); - } - $.each(exportTypes, function (i, type) { - if (TYPE_NAME.hasOwnProperty(type)) { - $menu.append(['
  • ', - '', - TYPE_NAME[type], - '', - '
  • '].join('')); - } - }); - - $menu.find('li').click(function () { - var type = $(this).data('type'), - doExport = function () { - that.$el.tableExport($.extend({}, that.options.exportOptions, { - type: type, - escape: false - })); - }; - - if (that.options.exportDataType === 'all' && that.options.pagination) { - that.$el.one(that.options.sidePagination === 'server' ? 'post-body.bs.table' : 'page-change.bs.table', function () { - doExport(); - that.togglePagination(); - }); - that.togglePagination(); - } else if (that.options.exportDataType === 'selected') { - //修改sidePagination属性为server无法导出选中数据 - var trs = that.$body.children(); - for (var i = 0; i < trs.length; i++) { - var $this = $(trs[i]); - if(!$this.find(sprintf('[name="%s"]',that.options.selectItemName)).prop('checked')){ - $this['hide'](); - }} - doExport(); - that.getRowsHidden(true); - } else { - doExport(); - } - }); - } - } - }; -})(jQuery); diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/export/bootstrap-table-export.min.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/export/bootstrap-table-export.min.js new file mode 100644 index 000000000..edd4f2ec1 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/export/bootstrap-table-export.min.js @@ -0,0 +1,10 @@ +/** + * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation) + * + * @version v1.18.2 + * @homepage https://bootstrap-table.com + * @author wenzhixin (http://wenzhixin.net.cn/) + * @license MIT + */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).jQuery)}(this,(function(t){"use strict";function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t),r="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function o(t,e){return t(e={exports:{}},e.exports),e.exports}var i=function(t){return t&&t.Math==Math&&t},a=i("object"==typeof globalThis&&globalThis)||i("object"==typeof window&&window)||i("object"==typeof self&&self)||i("object"==typeof r&&r)||function(){return this}()||Function("return this")(),c=function(t){try{return!!t()}catch(t){return!0}},l=!c((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),u={}.propertyIsEnumerable,f=Object.getOwnPropertyDescriptor,s={f:f&&!u.call({1:2},1)?function(t){var e=f(this,t);return!!e&&e.enumerable}:u},p=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},d={}.toString,h=function(t){return d.call(t).slice(8,-1)},g="".split,v=c((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==h(t)?g.call(t,""):Object(t)}:Object,y=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},b=function(t){return v(y(t))},x=function(t){return"object"==typeof t?null!==t:"function"==typeof t},m=function(t,e){if(!x(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!x(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!x(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!x(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")},E={}.hasOwnProperty,S=function(t,e){return E.call(t,e)},w=a.document,T=x(w)&&x(w.createElement),O=function(t){return T?w.createElement(t):{}},j=!l&&!c((function(){return 7!=Object.defineProperty(O("div"),"a",{get:function(){return 7}}).a})),P=Object.getOwnPropertyDescriptor,A={f:l?P:function(t,e){if(t=b(t),e=m(e,!0),j)try{return P(t,e)}catch(t){}if(S(t,e))return p(!s.f.call(t,e),t[e])}},C=function(t){if(!x(t))throw TypeError(String(t)+" is not an object");return t},R=Object.defineProperty,I={f:l?R:function(t,e,n){if(C(t),e=m(e,!0),C(n),j)try{return R(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},L=l?function(t,e,n){return I.f(t,e,p(1,n))}:function(t,e,n){return t[e]=n,t},D=function(t,e){try{L(a,t,e)}catch(n){a[t]=e}return e},M="__core-js_shared__",_=a[M]||D(M,{}),$=Function.toString;"function"!=typeof _.inspectSource&&(_.inspectSource=function(t){return $.call(t)});var k,N,F,V=_.inspectSource,B=a.WeakMap,U="function"==typeof B&&/native code/.test(V(B)),G=o((function(t){(t.exports=function(t,e){return _[t]||(_[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.8.1",mode:"global",copyright:"© 2020 Denis Pushkarev (zloirock.ru)"})})),H=0,q=Math.random(),X=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++H+q).toString(36)},K=G("keys"),W=function(t){return K[t]||(K[t]=X(t))},z={},Y=a.WeakMap;if(U){var Q=_.state||(_.state=new Y),J=Q.get,Z=Q.has,tt=Q.set;k=function(t,e){return e.facade=t,tt.call(Q,t,e),e},N=function(t){return J.call(Q,t)||{}},F=function(t){return Z.call(Q,t)}}else{var et=W("state");z[et]=!0,k=function(t,e){return e.facade=t,L(t,et,e),e},N=function(t){return S(t,et)?t[et]:{}},F=function(t){return S(t,et)}}var nt,rt,ot={set:k,get:N,has:F,enforce:function(t){return F(t)?N(t):k(t,{})},getterFor:function(t){return function(e){var n;if(!x(e)||(n=N(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return n}}},it=o((function(t){var e=ot.get,n=ot.enforce,r=String(String).split("String");(t.exports=function(t,e,o,i){var c,l=!!i&&!!i.unsafe,u=!!i&&!!i.enumerable,f=!!i&&!!i.noTargetGet;"function"==typeof o&&("string"!=typeof e||S(o,"name")||L(o,"name",e),(c=n(o)).source||(c.source=r.join("string"==typeof e?e:""))),t!==a?(l?!f&&t[e]&&(u=!0):delete t[e],u?t[e]=o:L(t,e,o)):u?t[e]=o:D(e,o)})(Function.prototype,"toString",(function(){return"function"==typeof this&&e(this).source||V(this)}))})),at=a,ct=function(t){return"function"==typeof t?t:void 0},lt=function(t,e){return arguments.length<2?ct(at[t])||ct(a[t]):at[t]&&at[t][e]||a[t]&&a[t][e]},ut=Math.ceil,ft=Math.floor,st=function(t){return isNaN(t=+t)?0:(t>0?ft:ut)(t)},pt=Math.min,dt=function(t){return t>0?pt(st(t),9007199254740991):0},ht=Math.max,gt=Math.min,vt=function(t,e){var n=st(t);return n<0?ht(n+e,0):gt(n,e)},yt=function(t){return function(e,n,r){var o,i=b(e),a=dt(i.length),c=vt(r,a);if(t&&n!=n){for(;a>c;)if((o=i[c++])!=o)return!0}else for(;a>c;c++)if((t||c in i)&&i[c]===n)return t||c||0;return!t&&-1}},bt={includes:yt(!0),indexOf:yt(!1)}.indexOf,xt=function(t,e){var n,r=b(t),o=0,i=[];for(n in r)!S(z,n)&&S(r,n)&&i.push(n);for(;e.length>o;)S(r,n=e[o++])&&(~bt(i,n)||i.push(n));return i},mt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Et=mt.concat("length","prototype"),St={f:Object.getOwnPropertyNames||function(t){return xt(t,Et)}},wt={f:Object.getOwnPropertySymbols},Tt=lt("Reflect","ownKeys")||function(t){var e=St.f(C(t)),n=wt.f;return n?e.concat(n(t)):e},Ot=function(t,e){for(var n=Tt(e),r=I.f,o=A.f,i=0;i=74)&&(nt=Xt.match(/Chrome\/(\d+)/))&&(rt=nt[1]);var Yt=rt&&+rt,Qt=Gt("species"),Jt=function(t){return Yt>=51||!c((function(){var e=[];return(e.constructor={})[Qt]=function(){return{foo:1}},1!==e[t](Boolean).foo}))},Zt=Gt("isConcatSpreadable"),te=9007199254740991,ee="Maximum allowed index exceeded",ne=Yt>=51||!c((function(){var t=[];return t[Zt]=!1,t.concat()[0]!==t})),re=Jt("concat"),oe=function(t){if(!x(t))return!1;var e=t[Zt];return void 0!==e?!!e:_t(t)};Mt({target:"Array",proto:!0,forced:!ne||!re},{concat:function(t){var e,n,r,o,i,a=$t(this),c=qt(a,0),l=0;for(e=-1,r=arguments.length;ete)throw TypeError(ee);for(n=0;n=te)throw TypeError(ee);kt(c,l++,i)}return c.length=l,c}});var ie,ae=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t},ce=[].push,le=function(t){var e=1==t,n=2==t,r=3==t,o=4==t,i=6==t,a=7==t,c=5==t||i;return function(l,u,f,s){for(var p,d,h=$t(l),g=v(h),y=function(t,e,n){if(ae(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}(u,f,3),b=dt(g.length),x=0,m=s||qt,E=e?m(l,b):n||a?m(l,0):void 0;b>x;x++)if((c||x in g)&&(d=y(p=g[x],x,h),t))if(e)E[x]=d;else if(d)switch(t){case 3:return!0;case 5:return p;case 6:return x;case 2:ce.call(E,p)}else switch(t){case 4:return!1;case 7:ce.call(E,p)}return i?-1:r||o?o:E}},ue={forEach:le(0),map:le(1),filter:le(2),some:le(3),every:le(4),find:le(5),findIndex:le(6),filterOut:le(7)},fe=Object.keys||function(t){return xt(t,mt)},se=l?Object.defineProperties:function(t,e){C(t);for(var n,r=fe(e),o=r.length,i=0;o>i;)I.f(t,n=r[i++],e[n]);return t},pe=lt("document","documentElement"),de=W("IE_PROTO"),he=function(){},ge=function(t){return" diff --git a/ruoyi-admin/src/main/resources/templates/demo/modal/table.html b/ruoyi-admin/src/main/resources/templates/demo/modal/table.html index e93131830..5ff3818cc 100644 --- a/ruoyi-admin/src/main/resources/templates/demo/modal/table.html +++ b/ruoyi-admin/src/main/resources/templates/demo/modal/table.html @@ -26,6 +26,7 @@

    弹出复选框表格及单选框表格(点击提交后得到数据并回显到父窗体)。

    +

    @@ -48,6 +49,25 @@ $.modal.open("选择用户", prefix + "/parent"); } + function selectUsersToParentCallBack(){ + var options = { + title: '选择用户', + url: prefix + "/parent", + callBack: doSubmit + }; + $.modal.openOptions(options); + } + + function doSubmit(index, layero){ + var rows = layero.find("iframe")[0].contentWindow.getSelections(); + if (rows.length == 0) { + $.modal.alertWarning("请至少选择一条记录"); + return; + } + $('#userids').html(rows.join()) + layer.close(index); + } + function selectUsers(){ alert(1); } diff --git a/ruoyi-admin/src/main/resources/templates/demo/modal/table/parent.html b/ruoyi-admin/src/main/resources/templates/demo/modal/table/parent.html index 40019c7a9..766061702 100644 --- a/ruoyi-admin/src/main/resources/templates/demo/modal/table/parent.html +++ b/ruoyi-admin/src/main/resources/templates/demo/modal/table/parent.html @@ -72,7 +72,7 @@ $.table.init(options); }); - /* 添加用户-选择用户-提交 */ + /* 添加用户-选择用户-提交(子页面调用父页面形式) */ function submitHandler(index, layero) { var rows = $.table.selectFirstColumns(); if (rows.length == 0) { @@ -85,6 +85,11 @@ // 父页面的变量 parent.$('#userids').html(rows.join()); } + + /* 添加用户-选择用户-提交(回调形式-父页面调用子页面) */ + function getSelections() { + return $.table.selectFirstColumns(); + } \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/templates/demo/table/export.html b/ruoyi-admin/src/main/resources/templates/demo/table/export.html index 82be299a5..083a87d7c 100644 --- a/ruoyi-admin/src/main/resources/templates/demo/table/export.html +++ b/ruoyi-admin/src/main/resources/templates/demo/table/export.html @@ -25,9 +25,13 @@ showToggle: false, showColumns: false, showExport: true, + exportDataType: 'selected', // 导出选择数据 + exportTypes: ['json', 'xml', 'csv', 'txt', 'sql', 'excel'], // 导出的文件类型 exportOptions: { - ignoreColumn: [0, 8] //忽略第一列和最后一列 + fileName: '用户数据', // 文件名称设置 + ignoreColumn: [0, 8] // 忽略第一列和最后一列 }, + clickToSelect: true, columns: [{ checkbox: true }, diff --git a/ruoyi-admin/src/main/resources/templates/demo/table/exportSelected.html b/ruoyi-admin/src/main/resources/templates/demo/table/exportSelected.html new file mode 100644 index 000000000..4aec88df4 --- /dev/null +++ b/ruoyi-admin/src/main/resources/templates/demo/table/exportSelected.html @@ -0,0 +1,120 @@ + + + + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + 勾选数据导出指定列,否则为全部 + + 导出 + +
    +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/templates/demo/table/reorderColumns.html b/ruoyi-admin/src/main/resources/templates/demo/table/reorderColumns.html new file mode 100644 index 000000000..8e15f2e53 --- /dev/null +++ b/ruoyi-admin/src/main/resources/templates/demo/table/reorderColumns.html @@ -0,0 +1,84 @@ + + + + + + +
    + +
    +
    +

    按住表格列拖拽

    +
    +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/templates/demo/table/reorder.html b/ruoyi-admin/src/main/resources/templates/demo/table/reorderRows.html similarity index 90% rename from ruoyi-admin/src/main/resources/templates/demo/table/reorder.html rename to ruoyi-admin/src/main/resources/templates/demo/table/reorderRows.html index 8c40c4da9..bda2b73ee 100644 --- a/ruoyi-admin/src/main/resources/templates/demo/table/reorder.html +++ b/ruoyi-admin/src/main/resources/templates/demo/table/reorderRows.html @@ -1,13 +1,13 @@ - +
    -

    按住表格拖拽

    +

    按住表格行拖拽

    @@ -15,7 +15,7 @@
    - + - + - - - - + + + +
    @@ -157,7 +157,7 @@
    - +
    @@ -170,10 +170,16 @@ - -
    - - + +
    + + +
    + + +
    + +
    @@ -193,8 +199,8 @@
    - - + +
    diff --git a/ruoyi-admin/src/main/resources/templates/index-topnav.html b/ruoyi-admin/src/main/resources/templates/index-topnav.html index fe9f9320d..0e85cab7e 100644 --- a/ruoyi-admin/src/main/resources/templates/index-topnav.html +++ b/ruoyi-admin/src/main/resources/templates/index-topnav.html @@ -14,7 +14,7 @@ - +
    @@ -123,6 +123,7 @@
  • 数据汇总
  • 组合表头
  • 表格导出
  • +
  • 导出选择列
  • 翻页记住选择
  • 跳转至指定页
  • 自定义查询参数
  • @@ -136,7 +137,8 @@
  • 表格父子视图
  • 表格图片预览
  • 动态增删改查
  • -
  • 表格拖拽操作
  • +
  • 表格行拖拽操作
  • +
  • 表格列拖拽操作
  • 表格列宽拖动
  • 表格行内编辑
  • 主子表提交
  • @@ -216,7 +218,10 @@
  • - + + [[${menu.menuName}]] + + [[${menu.menuName}]]
  • @@ -309,8 +314,8 @@ - - + + @@ -379,6 +384,7 @@ function applyPath(url) { } $(function() { + var lockPath = storage.get('lockPath'); if($.common.equals("history", mode) && window.performance.navigation.type == 1) { var url = storage.get('publicPath'); if ($.common.isNotEmpty(url)) { @@ -386,6 +392,9 @@ $(function() { } else { $(".navbar-toolbar li a").eq(0).click(); } + } else if($.common.isNotEmpty(lockPath)) { + applyPath(lockPath); + storage.remove('lockPath'); } else { var hash = location.hash; if ($.common.isNotEmpty(hash)) { diff --git a/ruoyi-admin/src/main/resources/templates/index.html b/ruoyi-admin/src/main/resources/templates/index.html index 13aac3b48..b684e9b67 100644 --- a/ruoyi-admin/src/main/resources/templates/index.html +++ b/ruoyi-admin/src/main/resources/templates/index.html @@ -14,7 +14,7 @@ - +
    @@ -104,6 +104,7 @@
  • 数据汇总
  • 组合表头
  • 表格导出
  • +
  • 导出选择列
  • 翻页记住选择
  • 跳转至指定页
  • 自定义查询参数
  • @@ -117,7 +118,8 @@
  • 表格父子视图
  • 表格图片预览
  • 动态增删改查
  • -
  • 表格拖拽操作
  • +
  • 表格行拖拽操作
  • +
  • 表格列拖拽操作
  • 表格列宽拖动
  • 表格行内编辑
  • 主子表提交
  • @@ -259,8 +261,8 @@ - - + + - + + @@ -78,7 +79,7 @@ - + diff --git a/ruoyi-admin/src/main/resources/templates/main.html b/ruoyi-admin/src/main/resources/templates/main.html index 3e356b374..89a3689b2 100644 --- a/ruoyi-admin/src/main/resources/templates/main.html +++ b/ruoyi-admin/src/main/resources/templates/main.html @@ -80,7 +80,7 @@

    官网:http://www.ruoyi.vip

    -

    QQ群:满1389287 满1679294 满1529866 满1772718 满1366522 满1382251 满1145125 满86752435 满134072510 满210336300 满339522636 满130035985 143151071 +

    QQ群:满1389287 满1679294 满1529866 满1772718 满1366522 满1382251 满1145125 满86752435 满134072510 满210336300 满339522636 满130035985 满143151071 满158781320 201531282

    微信:/ *若依

    @@ -97,13 +97,59 @@
    +
    +
    +
    + v4.6.12021.04.12 +
    +
    +
    +
    +
      +
    1. 新增IE浏览器版本过低提示页面
    2. +
    3. 新增详细信息tab页签方式
    4. +
    5. 新增解锁屏幕打开上次页签
    6. +
    7. 数据监控默认账户密码防止越权访问
    8. +
    9. 新增表格示例(导出选择列)
    10. +
    11. 个人信息添加手机&邮箱重复验证
    12. +
    13. 个人中心刷新后样式问题
    14. +
    15. 操作日志返回参数添加非空验证
    16. +
    17. velocity剔除commons-collections版本,防止3.2.1版本的反序列化漏洞
    18. +
    19. 子表模板默认日期格式化
    20. +
    21. 代码生成预览语言根据后缀名高亮显示
    22. +
    23. 代码生成主子表相同字段导致数据问题
    24. +
    25. 升级SpringBoot到最新版本2.2.13
    26. +
    27. 升级shiro到最新版1.7.1 阻止身份认证绕过漏洞
    28. +
    29. 升级bootstrapTable到最新版本v1.18.2
    30. +
    31. 升级bootstrapTable相关组件到最新版本v1.18.2
    32. +
    33. 升级fastjson到最新版1.2.75
    34. +
    35. 升级druid到最新版本v1.2.4
    36. +
    37. 升级oshi到最新版本v5.6.0
    38. +
    39. 修改ip字段长度防止ipv6地址长度不够
    40. +
    41. 搜索建议示例选择后隐藏列表
    42. +
    43. 主子表示例增加初始化数据
    44. +
    45. 优化Excel导入增加空行判断
    46. +
    47. 修复横向菜单无法打开页签问题
    48. +
    49. 修复导入数据为负浮点数时,导入结果会丢失精度问题
    50. +
    51. 优化更多操作按钮左侧移入内容闪现消失情况
    52. +
    53. 修复主子表提交中列隐藏后出现列偏移问题
    54. +
    55. 单据打印网页时通过hidden-print隐藏元素
    56. +
    57. 表格销毁清除记住选择数据
    58. +
    59. 增加表格动态列示例
    60. +
    61. 代码生成选择主子表关联元素必填
    62. +
    63. tree根据Id和Name选中指定节点增加空判断
    64. +
    65. 其他细节优化
    66. +
    +
    +
    +
    v4.6.02021.01.01
    -
    +
    1. 新增缓存监控管理
    2. diff --git a/ruoyi-admin/src/main/resources/templates/register.html b/ruoyi-admin/src/main/resources/templates/register.html index 71783ff10..8d0468f72 100644 --- a/ruoyi-admin/src/main/resources/templates/register.html +++ b/ruoyi-admin/src/main/resources/templates/register.html @@ -9,7 +9,7 @@ - + @@ -50,7 +50,7 @@
    @@ -77,7 +77,7 @@ - + diff --git a/ruoyi-admin/src/main/resources/templates/skin.html b/ruoyi-admin/src/main/resources/templates/skin.html index 68fe198d4..0bd2073f8 100644 --- a/ruoyi-admin/src/main/resources/templates/skin.html +++ b/ruoyi-admin/src/main/resources/templates/skin.html @@ -140,7 +140,7 @@ - + diff --git a/ruoyi-admin/src/main/resources/templates/system/dict/type/type.html b/ruoyi-admin/src/main/resources/templates/system/dict/type/type.html index cf6e53c46..cfd18cb6a 100644 --- a/ruoyi-admin/src/main/resources/templates/system/dict/type/type.html +++ b/ruoyi-admin/src/main/resources/templates/system/dict/type/type.html @@ -50,8 +50,8 @@ 导出 - - 清理缓存 + + 刷新缓存
    @@ -139,9 +139,9 @@ $.modal.openTab("字典数据", url); } - /** 清理字典缓存 */ - function clearCache() { - $.operate.get(prefix + "/clearCache"); + /** 刷新字典缓存 */ + function refreshCache() { + $.operate.get(prefix + "/refreshCache"); } diff --git a/ruoyi-admin/src/main/resources/templates/system/user/profile/profile.html b/ruoyi-admin/src/main/resources/templates/system/user/profile/profile.html index a579211cb..6b8906bf8 100644 --- a/ruoyi-admin/src/main/resources/templates/system/user/profile/profile.html +++ b/ruoyi-admin/src/main/resources/templates/system/user/profile/profile.html @@ -280,7 +280,7 @@ }, newPassword: { required: "请输入新密码", - minlength: "密码不能小于5个字符", + minlength: "密码不能小于6个字符", maxlength: "密码不能大于20个字符" }, confirmPassword: { diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 7148fca3a..fd7b8bfe1 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 4.6.0 + 4.6.1 4.0.0 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java index 866831977..5e5391224 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java @@ -108,7 +108,7 @@ public class Arith "The scale must be a positive integer or zero"); } BigDecimal b = new BigDecimal(Double.toString(v)); - BigDecimal one = new BigDecimal("1"); + BigDecimal one = BigDecimal.ONE; return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java index d5c324fc9..4ba08dc15 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -150,6 +150,16 @@ public class DictUtils return StringUtils.stripEnd(propertyString.toString(), separator); } + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + CacheUtils.remove(getCacheName(), getCacheKey(key)); + } + /** * 清空字典缓存 */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java index 4ca52838a..f35aa0468 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java @@ -22,7 +22,7 @@ public class ExceptionUtil return str; } - public static String getRootErrorMseeage(Exception e) + public static String getRootErrorMessage(Exception e) { Throwable root = ExceptionUtils.getRootCause(e); root = (root == null ? e : root); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java index 51174f1ea..882e94a64 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -257,7 +257,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils } /** - * 下划线转驼峰命名 + * 驼峰转下划线命名 */ public static String toUnderScoreCase(String str) { diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index e6996c73d..88a287d97 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -543,7 +543,10 @@ public class ExcelUtil } else if (ColumnType.NUMERIC == attr.cellType()) { - cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } } else if (ColumnType.IMAGE == attr.cellType()) { diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/security/CipherUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/security/CipherUtils.java new file mode 100644 index 000000000..34c1a79de --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/security/CipherUtils.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.utils.security; + +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import javax.crypto.KeyGenerator; + +/** + * 对称密钥密码算法工具类 + * + * @author ruoyi + */ +public class CipherUtils +{ + /** + * 生成随机秘钥 + * + * @param keyBitSize 字节大小 + * @param algorithmName 算法名称 + * @return 创建密匙 + */ + public static Key generateNewKey(int keyBitSize, String algorithmName) + { + KeyGenerator kg; + try + { + kg = KeyGenerator.getInstance(algorithmName); + } + catch (NoSuchAlgorithmException e) + { + String msg = "Unable to acquire " + algorithmName + " algorithm. This is required to function."; + throw new IllegalStateException(msg, e); + } + kg.init(keyBitSize); + return kg.generateKey(); + } +} diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml index 9287ec410..4785ff9eb 100644 --- a/ruoyi-framework/pom.xml +++ b/ruoyi-framework/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 4.6.0 + 4.6.1 4.0.0 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java index 6b5470e8f..e1d2e6876 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -63,6 +63,7 @@ public class DataScopeAspect @Before("dataScopePointCut()") public void doBefore(JoinPoint point) throws Throwable { + clearDataScope(point); handleDataScope(point); } @@ -163,4 +164,17 @@ public class DataScopeAspect } return null; } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java index b638a791f..0cd8d13fa 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -1,7 +1,11 @@ package com.ruoyi.framework.aspectj; import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Iterator; import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; @@ -12,6 +16,8 @@ import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.support.spring.PropertyPreFilters; import com.ruoyi.common.annotation.Log; @@ -89,7 +95,10 @@ public class LogAspect String ip = ShiroUtils.getIp(); operLog.setOperIp(ip); // 返回参数 - operLog.setJsonResult(StringUtils.substring(JSON.marshal(jsonResult), 0, 2000)); + if (StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JSON.marshal(jsonResult), 0, 2000)); + } operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); if (currentUser != null) @@ -114,7 +123,7 @@ public class LogAspect // 设置请求方式 operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); // 处理设置注解上的参数 - getControllerMethodDescription(controllerLog, operLog); + getControllerMethodDescription(joinPoint, controllerLog, operLog); // 保存数据库 AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); } @@ -134,7 +143,7 @@ public class LogAspect * @param operLog 操作日志 * @throws Exception */ - public void getControllerMethodDescription(Log log, SysOperLog operLog) throws Exception + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception { // 设置action动作 operLog.setBusinessType(log.businessType().ordinal()); @@ -146,7 +155,7 @@ public class LogAspect if (log.isSaveRequestData()) { // 获取参数的信息,传入到数据库中。 - setRequestValue(operLog); + setRequestValue(joinPoint, operLog); } } @@ -156,16 +165,23 @@ public class LogAspect * @param operLog 操作日志 * @throws Exception 异常 */ - private void setRequestValue(SysOperLog operLog) throws Exception + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception { Map map = ServletUtils.getRequest().getParameterMap(); if (StringUtils.isNotEmpty(map)) { - PropertyPreFilters.MySimplePropertyPreFilter excludefilter = new PropertyPreFilters().addFilter(); - excludefilter.addExcludes(EXCLUDE_PROPERTIES); - String params = JSONObject.toJSONString(map, excludefilter); + String params = JSONObject.toJSONString(map, excludePropertyPreFilter()); operLog.setOperParam(StringUtils.substring(params, 0, 2000)); } + else + { + Object args = joinPoint.getArgs(); + if (StringUtils.isNotNull(args)) + { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } + } } /** @@ -183,4 +199,67 @@ public class LogAspect } return null; } + + /** + * 忽略敏感属性 + */ + public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter() + { + return new PropertyPreFilters().addFilter().addExcludes(EXCLUDE_PROPERTIES); + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (int i = 0; i < paramsArray.length; i++) + { + if (StringUtils.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i])) + { + Object jsonObj = JSONObject.toJSONString(paramsArray[i], excludePropertyPreFilter()); + params += jsonObj.toString() + " "; + } + } + } + return params.trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Iterator iter = collection.iterator(); iter.hasNext();) + { + return iter.next() instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java index d6feedb82..d69672785 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java @@ -8,7 +8,6 @@ import java.util.Map; import javax.servlet.Filter; import org.apache.commons.io.IOUtils; import org.apache.shiro.cache.ehcache.EhCacheManager; -import org.apache.shiro.codec.Base64; import org.apache.shiro.config.ConfigurationException; import org.apache.shiro.io.ResourceUtils; import org.apache.shiro.mgt.SecurityManager; @@ -23,6 +22,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.security.CipherUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.shiro.realm.UserRealm; import com.ruoyi.framework.shiro.session.OnlineSessionDAO; @@ -104,12 +104,6 @@ public class ShiroConfig @Value("${shiro.cookie.maxAge}") private int maxAge; - /** - * 设置cipherKey密钥 - */ - @Value("${shiro.cookie.cipherKey}") - private String cipherKey; - /** * 登录地址 */ @@ -271,6 +265,7 @@ public class ShiroConfig // 对静态资源设置匿名访问 filterChainDefinitionMap.put("/favicon.ico**", "anon"); filterChainDefinitionMap.put("/ruoyi.png**", "anon"); + filterChainDefinitionMap.put("/html/**", "anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/docs/**", "anon"); filterChainDefinitionMap.put("/fonts/**", "anon"); @@ -356,7 +351,7 @@ public class ShiroConfig { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); - cookieRememberMeManager.setCipherKey(Base64.decode(cipherKey)); + cookieRememberMeManager.setCipherKey(CipherUtils.generateNewKey(128, "AES").getEncoded()); return cookieRememberMeManager; } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/kickout/KickoutSessionFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/kickout/KickoutSessionFilter.java index 32789b42e..d92db3401 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/kickout/KickoutSessionFilter.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/kickout/KickoutSessionFilter.java @@ -94,18 +94,8 @@ public class KickoutSessionFilter extends AccessControlFilter // 如果队列里的sessionId数超出最大会话数,开始踢人 while (deque.size() > maxSession) { - Serializable kickoutSessionId = null; // 是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户; - if (kickoutAfter) - { - // 踢出后者 - kickoutSessionId = deque.removeFirst(); - } - else - { - // 踢出前者 - kickoutSessionId = deque.removeLast(); - } + Serializable kickoutSessionId = kickoutAfter ? deque.removeFirst() : deque.removeLast(); // 踢出后再更新下缓存队列 cache.put(loginName, deque); @@ -126,7 +116,7 @@ public class KickoutSessionFilter extends AccessControlFilter } // 如果被踢出了,(前者或后者)直接退出,重定向到踢出后的地址 - if ((Boolean) session.getAttribute("kickout") != null && (Boolean) session.getAttribute("kickout") == true) + if (session.getAttribute("kickout") != null && (Boolean) session.getAttribute("kickout") == true) { // 退出登录 subject.logout(); @@ -183,4 +173,4 @@ public class KickoutSessionFilter extends AccessControlFilter // 必须和ehcache缓存配置中的缓存name一致 this.cache = cacheManager.getCache(ShiroConstants.SYS_USERCACHE); } -} +} \ No newline at end of file diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml index 199f909d5..233c57822 100644 --- a/ruoyi-generator/pom.xml +++ b/ruoyi-generator/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 4.6.0 + 4.6.1 4.0.0 diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java index 889ae9fd7..4be1eecbb 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java @@ -22,7 +22,7 @@ public class VelocityInitializer // 加载classpath目录下的vm文件 p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); // 定义字符集 - p.setProperty(Velocity.ENCODING_DEFAULT, Constants.UTF8); + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); p.setProperty(Velocity.OUTPUT_ENCODING, Constants.UTF8); // 初始化Velocity引擎,指定配置Properties Velocity.init(p); diff --git a/ruoyi-generator/src/main/resources/templates/tool/gen/edit.html b/ruoyi-generator/src/main/resources/templates/tool/gen/edit.html index e6ddfc03e..94c717260 100644 --- a/ruoyi-generator/src/main/resources/templates/tool/gen/edit.html +++ b/ruoyi-generator/src/main/resources/templates/tool/gen/edit.html @@ -257,7 +257,7 @@ - +