diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelEnumConvert.java b/ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelEnumConvert.java index 2069c154f..9a3eb31db 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelEnumConvert.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelEnumConvert.java @@ -52,6 +52,8 @@ public class ExcelEnumConvert implements Converter { case BOOLEAN: textValue = cellData.getBooleanValue(); break; + default: + throw new IllegalArgumentException("单元格类型异常!"); } // 如果是空值 if (ObjectUtil.isNull(textValue)) { diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DropDownOptions.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DropDownOptions.java index f15d2f0bb..f74d6080d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DropDownOptions.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DropDownOptions.java @@ -63,11 +63,11 @@ public class DropDownOptions { StringBuilder stringBuffer = new StringBuilder(); String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$"; for (int i = 0; i < vars.length; i++) { - Object var = vars[i]; - if (!var.toString().matches(regex)) { + String var = StrUtil.trimToEmpty(String.valueOf(vars[i])); + if (!var.matches(regex)) { throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字"); } - stringBuffer.append(StrUtil.trimToEmpty(var.toString())); + stringBuffer.append(var); if (i < vars.length - 1) { // 直至最后一个前,都以_作为切割线 stringBuffer.append(DELIMITER); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelDownHandler.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelDownHandler.java index 5c5f997d7..442e353dd 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelDownHandler.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelDownHandler.java @@ -1,5 +1,7 @@ package com.ruoyi.common.excel; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -11,6 +13,7 @@ import com.ruoyi.common.annotation.ExcelDictFormat; import com.ruoyi.common.annotation.ExcelEnumFormat; import com.ruoyi.common.core.service.DictService; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StreamUtils; import com.ruoyi.common.utils.spring.SpringUtils; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; @@ -20,7 +23,6 @@ import org.apache.poi.xssf.usermodel.XSSFDataValidation; import java.lang.reflect.Field; import java.util.*; -import java.util.stream.Collectors; /** *

Excel表格下拉选操作

@@ -90,37 +92,24 @@ public class ExcelDownHandler implements SheetWriteHandler { List options = new ArrayList<>(); if (fields[i].isAnnotationPresent(ExcelDictFormat.class)) { // 如果指定了@ExcelDictFormat,则使用字典的逻辑 - ExcelDictFormat thisFiledExcelDictFormat = fields[i].getDeclaredAnnotation(ExcelDictFormat.class); - String dictType = thisFiledExcelDictFormat.dictType(); - String converterExp = thisFiledExcelDictFormat.readConverterExp(); + ExcelDictFormat format = fields[i].getDeclaredAnnotation(ExcelDictFormat.class); + String dictType = format.dictType(); + String converterExp = format.readConverterExp(); if (StrUtil.isNotBlank(dictType)) { // 如果传递了字典名,则依据字典建立下拉 - options = - new ArrayList<>( - Optional.ofNullable(dictService.getAllDictByDictType(dictType)) - .orElseThrow(() -> new ServiceException(String.format("字典 %s 不存在", dictType))) - .values() - ); + Collection values = Optional.ofNullable(dictService.getAllDictByDictType(dictType)) + .orElseThrow(() -> new ServiceException(String.format("字典 %s 不存在", dictType))) + .values(); + options = new ArrayList<>(values); } else if (StrUtil.isNotBlank(converterExp)) { // 如果指定了确切的值,则直接解析确切的值 - options = StrUtil.split( - converterExp, - thisFiledExcelDictFormat.separator(), - true, - true); + options = StrUtil.split(converterExp, format.separator(), true, true); } } else if (fields[i].isAnnotationPresent(ExcelEnumFormat.class)) { // 否则如果指定了@ExcelEnumFormat,则使用枚举的逻辑 - ExcelEnumFormat thisFiledExcelEnumFormat = fields[i].getDeclaredAnnotation(ExcelEnumFormat.class); - options = - EnumUtil - .getFieldValues( - thisFiledExcelEnumFormat.enumClass(), - thisFiledExcelEnumFormat.textField() - ) - .stream() - .map(String::valueOf) - .collect(Collectors.toList()); + ExcelEnumFormat format = fields[i].getDeclaredAnnotation(ExcelEnumFormat.class); + List values = EnumUtil.getFieldValues(format.enumClass(), format.textField()); + options = StreamUtils.toList(values, String::valueOf); } if (ObjectUtil.isNotEmpty(options)) { // 仅当下拉可选项不为空时执行 @@ -165,7 +154,7 @@ public class ExcelDownHandler implements SheetWriteHandler { if (ObjectUtil.isEmpty(value)) { return; } - this.markOptionsToSheet(helper, sheet, celIndex, helper.createExplicitListConstraint(value.toArray(new String[0]))); + this.markOptionsToSheet(helper, sheet, celIndex, helper.createExplicitListConstraint(ArrayUtil.toArray(value, String.class))); } /** @@ -206,9 +195,7 @@ public class ExcelDownHandler implements SheetWriteHandler { // 本次循环的一级选项值 String thisFirstOptionsValue = firstOptions.get(columIndex); // 创建第一行的数据 - Optional - // 获取第一行 - .ofNullable(linkedOptionsDataSheet.getRow(0)) + Optional.ofNullable(linkedOptionsDataSheet.getRow(0)) // 如果不存在则创建第一行 .orElseGet(() -> linkedOptionsDataSheet.createRow(finalI)) // 第一行当前列 @@ -218,7 +205,7 @@ public class ExcelDownHandler implements SheetWriteHandler { // 第二行开始,设置第二级别选项参数 List secondOptions = secoundOptionsMap.get(thisFirstOptionsValue); - if (ObjectUtil.isEmpty(secondOptions)) { + if (CollUtil.isEmpty(secondOptions)) { // 必须保证至少有一个关联选项,否则将导致Excel解析错误 secondOptions = Collections.singletonList("暂无_0"); } @@ -251,8 +238,7 @@ public class ExcelDownHandler implements SheetWriteHandler { int finalRowIndex = rowIndex + 1; int finalColumIndex = columIndex; - Row row = Optional - .ofNullable(linkedOptionsDataSheet.getRow(finalRowIndex)) + Row row = Optional.ofNullable(linkedOptionsDataSheet.getRow(finalRowIndex)) // 没有则创建 .orElseGet(() -> linkedOptionsDataSheet.createRow(finalRowIndex)); Optional @@ -315,9 +301,7 @@ public class ExcelDownHandler implements SheetWriteHandler { /** * 挂载下拉的列,仅限一级选项 */ - private void markOptionsToSheet(DataValidationHelper helper, - Sheet sheet, - Integer celIndex, + private void markOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer celIndex, DataValidationConstraint constraint) { // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, celIndex, celIndex); @@ -327,11 +311,8 @@ public class ExcelDownHandler implements SheetWriteHandler { /** * 挂载下拉的列,仅限二级选项 */ - private void markLinkedOptionsToSheet(DataValidationHelper helper, - Sheet sheet, - Integer rowIndex, - Integer celIndex, - DataValidationConstraint constraint) { + private void markLinkedOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer rowIndex, + Integer celIndex, DataValidationConstraint constraint) { // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 CellRangeAddressList addressList = new CellRangeAddressList(rowIndex, rowIndex, celIndex, celIndex); markDataValidationToSheet(helper, sheet, constraint, addressList); @@ -340,10 +321,8 @@ public class ExcelDownHandler implements SheetWriteHandler { /** * 应用数据校验 */ - private void markDataValidationToSheet(DataValidationHelper helper, - Sheet sheet, - DataValidationConstraint constraint, - CellRangeAddressList addressList) { + private void markDataValidationToSheet(DataValidationHelper helper, Sheet sheet, + DataValidationConstraint constraint, CellRangeAddressList addressList) { // 数据有效性对象 DataValidation dataValidation = helper.createValidation(constraint, addressList); // 处理Excel兼容性问题 @@ -381,7 +360,7 @@ public class ExcelDownHandler implements SheetWriteHandler { int thisCircleColumnIndex = columnIndex % 26; // 26一循环的次数大于0,则视为栏名至少两位 String columnPrefix = columnCircleCount == 0 - ? "" + ? StrUtil.EMPTY : StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount - 1, 1); // 从26一循环内取对应的栏位名 String columnNext = StrUtil.subWithLength(EXCEL_COLUMN_NAME, thisCircleColumnIndex, 1); 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 402c4e85b..8459e1478 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 @@ -85,7 +85,7 @@ public class ExcelUtil { try { resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); - exportExcel(list, sheetName, clazz, false, os); + exportExcel(list, sheetName, clazz, false, os, null); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } @@ -123,7 +123,7 @@ public class ExcelUtil { try { resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); - exportExcel(list, sheetName, clazz, merge, os); + exportExcel(list, sheetName, clazz, merge, os, null); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } @@ -158,7 +158,7 @@ public class ExcelUtil { * @param os 输出流 */ public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { - exportExcel(list, sheetName, clazz, false, os); + exportExcel(list, sheetName, clazz, false, os, null); } /** @@ -183,7 +183,8 @@ public class ExcelUtil { * @param merge 是否合并单元格 * @param os 输出流 */ - public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os) { + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, + OutputStream os, List options) { ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) .autoCloseStream(false) // 自动适配 @@ -195,28 +196,9 @@ public class ExcelUtil { // 合并处理器 builder.registerWriteHandler(new CellMergeStrategy(list, true)); } - builder.doWrite(list); - } - - /** - * 导出带有下拉框的Excel表格 - * - * @param options 下拉框数据 - */ - public static void exportExcel(List list, String sheetName, Class clazz, - boolean merge, OutputStream os, List options) { - ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) - .autoCloseStream(false) - // 自动适配 - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) + if (CollUtil.isNotEmpty(options)) { // 添加下拉框操作 - .registerWriteHandler(new ExcelDownHandler(options)) - .sheet(sheetName); - if (merge) { - // 合并处理器 - builder.registerWriteHandler(new CellMergeStrategy(list, true)); + builder.registerWriteHandler(new ExcelDownHandler(options)); } builder.doWrite(list); } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java index 6fba15ea6..e1ecb2074 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java @@ -1,6 +1,5 @@ package com.ruoyi.demo.controller; -import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.collection.CollUtil; import com.ruoyi.common.excel.ExcelResult; import com.ruoyi.common.utils.poi.ExcelUtil; @@ -90,7 +89,6 @@ public class TestExcelController { * * @param response / */ - @SaIgnore @GetMapping("/exportWithOptions") public void exportWithOptions(HttpServletResponse response) { exportExcelService.exportWithOptions(response); @@ -99,7 +97,6 @@ public class TestExcelController { /** * 导入表格 */ - @SaIgnore @PostMapping(value = "/importWithOptions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public List importWithOptions(@RequestPart("file") MultipartFile file) throws Exception { // 处理解析结果 diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ExportExcelServiceImpl.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ExportExcelServiceImpl.java index a0da4e69b..404987825 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ExportExcelServiceImpl.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ExportExcelServiceImpl.java @@ -1,7 +1,9 @@ package com.ruoyi.demo.service.impl; +import cn.hutool.core.util.StrUtil; import com.ruoyi.common.enums.UserStatus; import com.ruoyi.common.excel.DropDownOptions; +import com.ruoyi.common.utils.StreamUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.demo.domain.vo.ExportDemoVo; import com.ruoyi.demo.service.IExportExcelService; @@ -55,52 +57,31 @@ public class ExportExcelServiceImpl implements IExportExcelService { // 把所有的结果提取为规范的下拉选可选项 // 规范的一级省,用于级联省-市 - List provinceOptions = - provinceList.stream() - .map(everyProvince -> - DropDownOptions.createOptionValue( - everyProvince.getName(), - everyProvince.getId() - )) - .collect(Collectors.toList()); + List provinceOptions = StreamUtils.toList(provinceList, everyProvince -> + DropDownOptions.createOptionValue(everyProvince.getName(), everyProvince.getId())); // 规范的二级市,用于级联省-市 Map> provinceToCityOptions = new HashMap<>(); - cityList.stream() - .collect(Collectors.groupingBy(DemoCityData::getPData)) + StreamUtils.groupByKey(cityList, DemoCityData::getPData) .forEach((province, thisProvinceCityList) -> { // 每个省下二级的市可选项 - provinceToCityOptions.put( - DropDownOptions.createOptionValue(province.getName(), province.getId()), - thisProvinceCityList.stream() - .map(everyCity -> - DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId()) - ) - .collect(Collectors.toList()) - ); + String optionValue = DropDownOptions.createOptionValue(province.getName(), province.getId()); + List list = StreamUtils.toList(thisProvinceCityList, everyCity -> + DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId())); + provinceToCityOptions.put(optionValue, list); }); // 规范的一级市,用于级联市-县 - List cityOptions = cityList.stream() - .map(everyCity -> - DropDownOptions.createOptionValue( - everyCity.getName(), - everyCity.getId() - )) - .collect(Collectors.toList()); + List cityOptions = StreamUtils.toList(cityList, everyCity -> + DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId())); // 规范的二级县,用于级联市-县 Map> cityToAreaOptions = new HashMap<>(); - areaList.stream() - .collect(Collectors.groupingBy(DemoCityData::getPData)) + StreamUtils.groupByKey(areaList, DemoCityData::getPData) .forEach((city, thisCityAreaList) -> { // 每个市下二级的县可选项 - cityToAreaOptions.put( - DropDownOptions.createOptionValue(city.getName(), city.getId()), - thisCityAreaList.stream() - .map(everyArea -> - DropDownOptions.createOptionValue(everyArea.getName(), everyArea.getId()) - ) - .collect(Collectors.toList()) - ); + String optionValue = DropDownOptions.createOptionValue(city.getName(), city.getId()); + List list = StreamUtils.toList(thisCityAreaList, everyCity -> + DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId())); + cityToAreaOptions.put(optionValue, list); }); // 因为省市县三个都是联动,省级联市,市级联县,因此需要创建两个级联下拉,分别以省和市为判断依据创建 @@ -129,14 +110,14 @@ public class ExportExcelServiceImpl implements IExportExcelService { // 到此为止所有的下拉框可选项已全部配置完毕 // 接下来需要将Excel中的展示数据转换为对应的下拉选 - List outList = excelDataList.stream().map(everyRowData -> { + List outList = StreamUtils.toList(excelDataList, everyRowData -> { // 只需要处理没有使用@ExcelDictFormat注解的下拉框 // 一般来说,可以直接在数据库查询即查询出省市县信息,这里通过模拟操作赋值 everyRowData.setProvince(buildOptions(provinceList, everyRowData.getProvinceId())); everyRowData.setCity(buildOptions(cityList, everyRowData.getCityId())); everyRowData.setArea(buildOptions(areaList, everyRowData.getAreaId())); return everyRowData; - }).collect(Collectors.toList()); + }); ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options); } @@ -148,7 +129,7 @@ public class ExportExcelServiceImpl implements IExportExcelService { DemoCityData demoCityData = groupByIdMap.get(id).get(0); return DropDownOptions.createOptionValue(demoCityData.getName(), demoCityData.getId()); } else { - return ""; + return StrUtil.EMPTY; } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java index 6937f6734..c4079a9c0 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java @@ -281,14 +281,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService @Override public Map getAllDictByDictType(String dictType) { - List thisDictTypeDataList = selectDictDataByType(dictType); - Map dictMap = null; - for (SysDictData everyDictData : thisDictTypeDataList) { - if (ObjectUtil.isNull(dictMap)) { - dictMap = new HashMap<>(); - } - dictMap.put(everyDictData.getDictValue(), everyDictData.getDictLabel()); - } - return dictMap; + List list = selectDictDataByType(dictType); + return StreamUtils.toMap(list, SysDictData::getDictValue, SysDictData::getDictLabel); } }