From e0cd5381e248fcfa840dee56d54a2bd2357ce91a Mon Sep 17 00:00:00 2001 From: RuoYi Date: Wed, 3 Aug 2022 16:48:55 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E8=BF=9B=E8=A1=8C=E5=94=AF=E4=B8=80?= =?UTF-8?q?=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/mapper/system/SysDeptMapper.xml | 2 +- .../src/main/resources/mapper/system/SysRoleMapper.xml | 6 +++--- .../src/main/resources/mapper/system/SysUserMapper.xml | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml index 984f89cc..000166ed 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -82,7 +82,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml index a8d3ede5..12e7f78e 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -85,12 +85,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -139,7 +139,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - update sys_role set del_flag = '2' where role_id = #{roleId} + update sys_role set del_flag = '2' where role_id = #{roleId} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 371c429a..9f1839c1 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -122,7 +122,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -208,7 +208,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - update sys_user set del_flag = '2' where user_id = #{userId} + update sys_user set del_flag = '2' where user_id = #{userId} From e73dbd470ab6ff6482d0248fcf79d633e2ea8dc0 Mon Sep 17 00:00:00 2001 From: RuoYi Date: Sun, 7 Aug 2022 18:33:04 +0800 Subject: [PATCH 2/5] =?UTF-8?q?Excel=E6=B3=A8=E8=A7=A3=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=AF=B9=E8=B1=A1=E7=9A=84=E5=AD=90=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ruoyi/common/annotation/Excel.java | 23 +- .../com/ruoyi/common/utils/poi/ExcelUtil.java | 204 +++++++++++++++++- 2 files changed, 202 insertions(+), 25 deletions(-) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java index 804db72f..c6c8bc59 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java @@ -88,6 +88,11 @@ public @interface Excel */ public String[] combo() default {}; + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + /** * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. */ @@ -104,7 +109,7 @@ public @interface Excel public boolean isStatistics() default false; /** - * 导出类型(0数字 1字符串) + * 导出类型(0数字 1字符串 2图片) */ public ColumnType cellType() default ColumnType.STRING; @@ -143,22 +148,6 @@ public @interface Excel */ public String[] args() default {}; - public enum Align - { - AUTO(0), LEFT(1), CENTER(2), RIGHT(3); - private final int value; - - Align(int value) - { - this.value = value; - } - - public int value() - { - return this.value; - } - } - /** * 字段类型(0:导出导入;1:仅导出;2:仅导入) */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index b8dedf80..69f7eed9 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -7,12 +7,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.math.BigDecimal; import java.text.DecimalFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.HashMap; @@ -24,6 +26,7 @@ import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.poi.hssf.usermodel.HSSFClientAnchor; import org.apache.poi.hssf.usermodel.HSSFPicture; import org.apache.poi.hssf.usermodel.HSSFPictureData; @@ -149,6 +152,26 @@ public class ExcelUtil */ private short maxHeight; + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Method subMethod; + + /** + * 对象的子列表属性 + */ + private List subFields; + /** * 统计列表 */ @@ -198,6 +221,7 @@ public class ExcelUtil createExcelField(); createWorkbook(); createTitle(); + createSubHead(); } /** @@ -207,13 +231,48 @@ public class ExcelUtil { if (StringUtils.isNotEmpty(title)) { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + titleLastCol = titleLastCol + subFields.size() - 1; + } Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); titleRow.setHeightInPoints(30); Cell titleCell = titleRow.createCell(0); titleCell.setCellStyle(styles.get("title")); titleCell.setCellValue(title); - sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), - this.fields.size() - 1)); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) + { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; } } @@ -593,8 +652,20 @@ public class ExcelUtil // 写入各个字段的列头名称 for (Object[] os : fields) { + Field field = (Field) os[0]; Excel excel = (Excel) os[1]; - this.createCell(excel, row, column++); + if (Collection.class.isAssignableFrom(field.getType())) + { + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } } if (Type.EXPORT.equals(type)) { @@ -610,21 +681,60 @@ public class ExcelUtil * @param index 序号 * @param row 单元格行 */ + @SuppressWarnings("unchecked") public void fillExcelData(int index, Row row) { int startNo = index * sheetSize; int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; for (int i = startNo; i < endNo; i++) { - row = sheet.createRow(i + 1 + rownum - startNo); + rowNo = i > 1 ? rowNo + 1 : rowNo + i; + row = sheet.createRow(rowNo); // 得到导出对象. T vo = (T) list.get(i); + Collection subList = null; + if (isSubListValue(vo)) + { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } + int column = 0; for (Object[] os : fields) { Field field = (Field) os[0]; Excel excel = (Excel) os[1]; - this.addCell(excel, row, vo, field, column++); + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) + { + boolean subFirst = false; + for (Object obj : subList) + { + if (subFirst) + { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) + { + if (subField.isAnnotationPresent(Excel.class)) + { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } + else + { + this.addCell(excel, row, vo, field, column++); + } } } } @@ -759,7 +869,7 @@ public class ExcelUtil /** * 创建单元格 */ - public Cell createCell(Excel attr, Row row, int column) + public Cell createHeadCell(Excel attr, Row row, int column) { // 创建列 Cell cell = row.createCell(column); @@ -767,6 +877,15 @@ public class ExcelUtil cell.setCellValue(attr.name()); setDataValidation(attr, row, column); cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } return cell; } @@ -874,6 +993,11 @@ public class ExcelUtil { // 创建cell cell = row.createCell(column); + if (isSubListValue(vo) && attr.needMerge()) + { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); // 用于读取对象中的属性 @@ -969,7 +1093,7 @@ public class ExcelUtil for (String item : convertSource) { String[] itemArray = item.split("="); - if (StringUtils.containsAny(separator, propertyValue)) + if (StringUtils.containsAny(propertyValue, separator)) { for (String value : propertyValue.split(separator)) { @@ -1006,7 +1130,7 @@ public class ExcelUtil for (String item : convertSource) { String[] itemArray = item.split("="); - if (StringUtils.containsAny(separator, propertyValue)) + if (StringUtils.containsAny(propertyValue, separator)) { for (String value : propertyValue.split(separator)) { @@ -1230,6 +1354,13 @@ public class ExcelUtil field.setAccessible(true); fields.add(new Object[] { field, attr }); } + if (Collection.class.isAssignableFrom(field.getType())) + { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } } // 多注解 @@ -1473,4 +1604,61 @@ public class ExcelUtil } return str; } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue(Object obj) + { + Object value; + try + { + value = subMethod.invoke(obj, new Object[] {}); + } + catch (Exception e) + { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } } From d0f399a66a06376b441d0e9107bb6201821c4910 Mon Sep 17 00:00:00 2001 From: RuoYi Date: Sun, 7 Aug 2022 19:30:54 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BC=98=E5=8C=96xss=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E5=90=8E=E6=A0=BC=E5=BC=8F=E5=87=BA=E7=8E=B0=E7=9A=84=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java index d3b211a4..587ee6f6 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java @@ -387,7 +387,7 @@ public final class HTMLFilter { paramValue = processParamProtocol(paramValue); } - params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\""); + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); } } From fb1bac211498a6d5a53c551899dc73ac5a3bed07 Mon Sep 17 00:00:00 2001 From: RuoYi Date: Sun, 7 Aug 2022 19:31:04 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=88=A0=E9=99=A4Hash?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E6=9F=90=E6=9D=A1=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ruoyi/common/core/redis/RedisCache.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java index 719e9df9..03f062ef 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java @@ -255,6 +255,18 @@ public class RedisCache return redisTemplate.opsForHash().multiGet(key, hKeys); } + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return Boolean.TRUE.equals(redisTemplate.opsForHash().delete(key, hKey)); + } + /** * 获得缓存的基本对象列表 * From cbedec7ca68c28d4f85efd0a969be719031da369 Mon Sep 17 00:00:00 2001 From: RuoYi Date: Mon, 8 Aug 2022 09:23:52 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=A7=A3=E9=94=81=E8=B4=A6=E6=88=B7=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitor/SysLogininforController.java | 15 +++++++- ruoyi-ui/src/api/monitor/logininfor.js | 8 +++++ .../src/views/monitor/logininfor/index.vue | 28 ++++++++++++++- sql/{ry_20220712.sql => ry_20220808.sql} | 34 ++++++++++--------- 4 files changed, 67 insertions(+), 18 deletions(-) rename sql/{ry_20220712.sql => ry_20220808.sql} (96%) diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java index 5bd40bae..00c6eda9 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -16,6 +16,7 @@ import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPasswordService; import com.ruoyi.system.domain.SysLogininfor; import com.ruoyi.system.service.ISysLogininforService; @@ -31,6 +32,9 @@ public class SysLogininforController extends BaseController @Autowired private ISysLogininforService logininforService; + @Autowired + private SysPasswordService passwordService; + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") @GetMapping("/list") public TableDataInfo list(SysLogininfor logininfor) @@ -64,6 +68,15 @@ public class SysLogininforController extends BaseController public AjaxResult clean() { logininforService.cleanLogininfor(); - return AjaxResult.success(); + return success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock//{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); } } diff --git a/ruoyi-ui/src/api/monitor/logininfor.js b/ruoyi-ui/src/api/monitor/logininfor.js index cd781517..c49a40e1 100644 --- a/ruoyi-ui/src/api/monitor/logininfor.js +++ b/ruoyi-ui/src/api/monitor/logininfor.js @@ -17,6 +17,14 @@ export function delLogininfor(infoId) { }) } +// 解锁用户登录状态 +export function unlockLogininfor(userName) { + return request({ + url: '/monitor/logininfor/unlock/' + userName, + method: 'get' + }) +} + // 清空登录日志 export function cleanLogininfor() { return request({ diff --git a/ruoyi-ui/src/views/monitor/logininfor/index.vue b/ruoyi-ui/src/views/monitor/logininfor/index.vue index e9490e79..c7776f52 100644 --- a/ruoyi-ui/src/views/monitor/logininfor/index.vue +++ b/ruoyi-ui/src/views/monitor/logininfor/index.vue @@ -73,6 +73,17 @@ v-hasPermi="['monitor:logininfor:remove']" >清空 + + 解锁 +