update 优化 !pr345 代码结构

This commit is contained in:
疯狂的狮子Li 2023-05-13 23:28:57 +08:00
parent 72882374be
commit 0f5603aed4
7 changed files with 57 additions and 123 deletions

View File

@ -52,6 +52,8 @@ public class ExcelEnumConvert implements Converter<Object> {
case BOOLEAN: case BOOLEAN:
textValue = cellData.getBooleanValue(); textValue = cellData.getBooleanValue();
break; break;
default:
throw new IllegalArgumentException("单元格类型异常!");
} }
// 如果是空值 // 如果是空值
if (ObjectUtil.isNull(textValue)) { if (ObjectUtil.isNull(textValue)) {

View File

@ -63,11 +63,11 @@ public class DropDownOptions {
StringBuilder stringBuffer = new StringBuilder(); StringBuilder stringBuffer = new StringBuilder();
String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$"; String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$";
for (int i = 0; i < vars.length; i++) { for (int i = 0; i < vars.length; i++) {
Object var = vars[i]; String var = StrUtil.trimToEmpty(String.valueOf(vars[i]));
if (!var.toString().matches(regex)) { if (!var.matches(regex)) {
throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字"); throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字");
} }
stringBuffer.append(StrUtil.trimToEmpty(var.toString())); stringBuffer.append(var);
if (i < vars.length - 1) { if (i < vars.length - 1) {
// 直至最后一个前都以_作为切割线 // 直至最后一个前都以_作为切割线
stringBuffer.append(DELIMITER); stringBuffer.append(DELIMITER);

View File

@ -1,5 +1,7 @@
package com.ruoyi.common.excel; 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.EnumUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; 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.annotation.ExcelEnumFormat;
import com.ruoyi.common.core.service.DictService; import com.ruoyi.common.core.service.DictService;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StreamUtils;
import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.*;
@ -20,7 +23,6 @@ import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
/** /**
* <h1>Excel表格下拉选操作</h1> * <h1>Excel表格下拉选操作</h1>
@ -90,37 +92,24 @@ public class ExcelDownHandler implements SheetWriteHandler {
List<String> options = new ArrayList<>(); List<String> options = new ArrayList<>();
if (fields[i].isAnnotationPresent(ExcelDictFormat.class)) { if (fields[i].isAnnotationPresent(ExcelDictFormat.class)) {
// 如果指定了@ExcelDictFormat则使用字典的逻辑 // 如果指定了@ExcelDictFormat则使用字典的逻辑
ExcelDictFormat thisFiledExcelDictFormat = fields[i].getDeclaredAnnotation(ExcelDictFormat.class); ExcelDictFormat format = fields[i].getDeclaredAnnotation(ExcelDictFormat.class);
String dictType = thisFiledExcelDictFormat.dictType(); String dictType = format.dictType();
String converterExp = thisFiledExcelDictFormat.readConverterExp(); String converterExp = format.readConverterExp();
if (StrUtil.isNotBlank(dictType)) { if (StrUtil.isNotBlank(dictType)) {
// 如果传递了字典名则依据字典建立下拉 // 如果传递了字典名则依据字典建立下拉
options = Collection<String> values = Optional.ofNullable(dictService.getAllDictByDictType(dictType))
new ArrayList<>( .orElseThrow(() -> new ServiceException(String.format("字典 %s 不存在", dictType)))
Optional.ofNullable(dictService.getAllDictByDictType(dictType)) .values();
.orElseThrow(() -> new ServiceException(String.format("字典 %s 不存在", dictType))) options = new ArrayList<>(values);
.values()
);
} else if (StrUtil.isNotBlank(converterExp)) { } else if (StrUtil.isNotBlank(converterExp)) {
// 如果指定了确切的值则直接解析确切的值 // 如果指定了确切的值则直接解析确切的值
options = StrUtil.split( options = StrUtil.split(converterExp, format.separator(), true, true);
converterExp,
thisFiledExcelDictFormat.separator(),
true,
true);
} }
} else if (fields[i].isAnnotationPresent(ExcelEnumFormat.class)) { } else if (fields[i].isAnnotationPresent(ExcelEnumFormat.class)) {
// 否则如果指定了@ExcelEnumFormat则使用枚举的逻辑 // 否则如果指定了@ExcelEnumFormat则使用枚举的逻辑
ExcelEnumFormat thisFiledExcelEnumFormat = fields[i].getDeclaredAnnotation(ExcelEnumFormat.class); ExcelEnumFormat format = fields[i].getDeclaredAnnotation(ExcelEnumFormat.class);
options = List<Object> values = EnumUtil.getFieldValues(format.enumClass(), format.textField());
EnumUtil options = StreamUtils.toList(values, String::valueOf);
.getFieldValues(
thisFiledExcelEnumFormat.enumClass(),
thisFiledExcelEnumFormat.textField()
)
.stream()
.map(String::valueOf)
.collect(Collectors.toList());
} }
if (ObjectUtil.isNotEmpty(options)) { if (ObjectUtil.isNotEmpty(options)) {
// 仅当下拉可选项不为空时执行 // 仅当下拉可选项不为空时执行
@ -165,7 +154,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
if (ObjectUtil.isEmpty(value)) { if (ObjectUtil.isEmpty(value)) {
return; 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); String thisFirstOptionsValue = firstOptions.get(columIndex);
// 创建第一行的数据 // 创建第一行的数据
Optional Optional.ofNullable(linkedOptionsDataSheet.getRow(0))
// 获取第一行
.ofNullable(linkedOptionsDataSheet.getRow(0))
// 如果不存在则创建第一行 // 如果不存在则创建第一行
.orElseGet(() -> linkedOptionsDataSheet.createRow(finalI)) .orElseGet(() -> linkedOptionsDataSheet.createRow(finalI))
// 第一行当前列 // 第一行当前列
@ -218,7 +205,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
// 第二行开始设置第二级别选项参数 // 第二行开始设置第二级别选项参数
List<String> secondOptions = secoundOptionsMap.get(thisFirstOptionsValue); List<String> secondOptions = secoundOptionsMap.get(thisFirstOptionsValue);
if (ObjectUtil.isEmpty(secondOptions)) { if (CollUtil.isEmpty(secondOptions)) {
// 必须保证至少有一个关联选项否则将导致Excel解析错误 // 必须保证至少有一个关联选项否则将导致Excel解析错误
secondOptions = Collections.singletonList("暂无_0"); secondOptions = Collections.singletonList("暂无_0");
} }
@ -251,8 +238,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
int finalRowIndex = rowIndex + 1; int finalRowIndex = rowIndex + 1;
int finalColumIndex = columIndex; int finalColumIndex = columIndex;
Row row = Optional Row row = Optional.ofNullable(linkedOptionsDataSheet.getRow(finalRowIndex))
.ofNullable(linkedOptionsDataSheet.getRow(finalRowIndex))
// 没有则创建 // 没有则创建
.orElseGet(() -> linkedOptionsDataSheet.createRow(finalRowIndex)); .orElseGet(() -> linkedOptionsDataSheet.createRow(finalRowIndex));
Optional Optional
@ -315,9 +301,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
/** /**
* 挂载下拉的列仅限一级选项 * 挂载下拉的列仅限一级选项
*/ */
private void markOptionsToSheet(DataValidationHelper helper, private void markOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer celIndex,
Sheet sheet,
Integer celIndex,
DataValidationConstraint constraint) { DataValidationConstraint constraint) {
// 设置数据有效性加载在哪个单元格上,四个参数分别是起始行终止行起始列终止列 // 设置数据有效性加载在哪个单元格上,四个参数分别是起始行终止行起始列终止列
CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, celIndex, celIndex); CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, celIndex, celIndex);
@ -327,11 +311,8 @@ public class ExcelDownHandler implements SheetWriteHandler {
/** /**
* 挂载下拉的列仅限二级选项 * 挂载下拉的列仅限二级选项
*/ */
private void markLinkedOptionsToSheet(DataValidationHelper helper, private void markLinkedOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer rowIndex,
Sheet sheet, Integer celIndex, DataValidationConstraint constraint) {
Integer rowIndex,
Integer celIndex,
DataValidationConstraint constraint) {
// 设置数据有效性加载在哪个单元格上,四个参数分别是起始行终止行起始列终止列 // 设置数据有效性加载在哪个单元格上,四个参数分别是起始行终止行起始列终止列
CellRangeAddressList addressList = new CellRangeAddressList(rowIndex, rowIndex, celIndex, celIndex); CellRangeAddressList addressList = new CellRangeAddressList(rowIndex, rowIndex, celIndex, celIndex);
markDataValidationToSheet(helper, sheet, constraint, addressList); markDataValidationToSheet(helper, sheet, constraint, addressList);
@ -340,10 +321,8 @@ public class ExcelDownHandler implements SheetWriteHandler {
/** /**
* 应用数据校验 * 应用数据校验
*/ */
private void markDataValidationToSheet(DataValidationHelper helper, private void markDataValidationToSheet(DataValidationHelper helper, Sheet sheet,
Sheet sheet, DataValidationConstraint constraint, CellRangeAddressList addressList) {
DataValidationConstraint constraint,
CellRangeAddressList addressList) {
// 数据有效性对象 // 数据有效性对象
DataValidation dataValidation = helper.createValidation(constraint, addressList); DataValidation dataValidation = helper.createValidation(constraint, addressList);
// 处理Excel兼容性问题 // 处理Excel兼容性问题
@ -381,7 +360,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
int thisCircleColumnIndex = columnIndex % 26; int thisCircleColumnIndex = columnIndex % 26;
// 26一循环的次数大于0则视为栏名至少两位 // 26一循环的次数大于0则视为栏名至少两位
String columnPrefix = columnCircleCount == 0 String columnPrefix = columnCircleCount == 0
? "" ? StrUtil.EMPTY
: StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount - 1, 1); : StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount - 1, 1);
// 从26一循环内取对应的栏位名 // 从26一循环内取对应的栏位名
String columnNext = StrUtil.subWithLength(EXCEL_COLUMN_NAME, thisCircleColumnIndex, 1); String columnNext = StrUtil.subWithLength(EXCEL_COLUMN_NAME, thisCircleColumnIndex, 1);

View File

@ -85,7 +85,7 @@ public class ExcelUtil {
try { try {
resetResponse(sheetName, response); resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream(); ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, false, os); exportExcel(list, sheetName, clazz, false, os, null);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("导出Excel异常"); throw new RuntimeException("导出Excel异常");
} }
@ -123,7 +123,7 @@ public class ExcelUtil {
try { try {
resetResponse(sheetName, response); resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream(); ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, merge, os); exportExcel(list, sheetName, clazz, merge, os, null);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("导出Excel异常"); throw new RuntimeException("导出Excel异常");
} }
@ -158,7 +158,7 @@ public class ExcelUtil {
* @param os 输出流 * @param os 输出流
*/ */
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os) { public static <T> void exportExcel(List<T> list, String sheetName, Class<T> 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 merge 是否合并单元格
* @param os 输出流 * @param os 输出流
*/ */
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, OutputStream os) { public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge,
OutputStream os, List<DropDownOptions> options) {
ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
.autoCloseStream(false) .autoCloseStream(false)
// 自动适配 // 自动适配
@ -195,28 +196,9 @@ public class ExcelUtil {
// 合并处理器 // 合并处理器
builder.registerWriteHandler(new CellMergeStrategy(list, true)); builder.registerWriteHandler(new CellMergeStrategy(list, true));
} }
builder.doWrite(list); if (CollUtil.isNotEmpty(options)) {
}
/**
* 导出带有下拉框的Excel表格
*
* @param options 下拉框数据
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz,
boolean merge, OutputStream os, List<DropDownOptions> options) {
ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
.autoCloseStream(false)
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
// 添加下拉框操作 // 添加下拉框操作
.registerWriteHandler(new ExcelDownHandler(options)) builder.registerWriteHandler(new ExcelDownHandler(options));
.sheet(sheetName);
if (merge) {
// 合并处理器
builder.registerWriteHandler(new CellMergeStrategy(list, true));
} }
builder.doWrite(list); builder.doWrite(list);
} }

View File

@ -1,6 +1,5 @@
package com.ruoyi.demo.controller; package com.ruoyi.demo.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.excel.ExcelResult; import com.ruoyi.common.excel.ExcelResult;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
@ -90,7 +89,6 @@ public class TestExcelController {
* *
* @param response / * @param response /
*/ */
@SaIgnore
@GetMapping("/exportWithOptions") @GetMapping("/exportWithOptions")
public void exportWithOptions(HttpServletResponse response) { public void exportWithOptions(HttpServletResponse response) {
exportExcelService.exportWithOptions(response); exportExcelService.exportWithOptions(response);
@ -99,7 +97,6 @@ public class TestExcelController {
/** /**
* 导入表格 * 导入表格
*/ */
@SaIgnore
@PostMapping(value = "/importWithOptions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/importWithOptions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public List<ExportDemoVo> importWithOptions(@RequestPart("file") MultipartFile file) throws Exception { public List<ExportDemoVo> importWithOptions(@RequestPart("file") MultipartFile file) throws Exception {
// 处理解析结果 // 处理解析结果

View File

@ -1,7 +1,9 @@
package com.ruoyi.demo.service.impl; package com.ruoyi.demo.service.impl;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.enums.UserStatus; import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.excel.DropDownOptions; import com.ruoyi.common.excel.DropDownOptions;
import com.ruoyi.common.utils.StreamUtils;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.demo.domain.vo.ExportDemoVo; import com.ruoyi.demo.domain.vo.ExportDemoVo;
import com.ruoyi.demo.service.IExportExcelService; import com.ruoyi.demo.service.IExportExcelService;
@ -55,52 +57,31 @@ public class ExportExcelServiceImpl implements IExportExcelService {
// 把所有的结果提取为规范的下拉选可选项 // 把所有的结果提取为规范的下拉选可选项
// 规范的一级省用于级联省- // 规范的一级省用于级联省-
List<String> provinceOptions = List<String> provinceOptions = StreamUtils.toList(provinceList, everyProvince ->
provinceList.stream() DropDownOptions.createOptionValue(everyProvince.getName(), everyProvince.getId()));
.map(everyProvince ->
DropDownOptions.createOptionValue(
everyProvince.getName(),
everyProvince.getId()
))
.collect(Collectors.toList());
// 规范的二级市用于级联省- // 规范的二级市用于级联省-
Map<String, List<String>> provinceToCityOptions = new HashMap<>(); Map<String, List<String>> provinceToCityOptions = new HashMap<>();
cityList.stream() StreamUtils.groupByKey(cityList, DemoCityData::getPData)
.collect(Collectors.groupingBy(DemoCityData::getPData))
.forEach((province, thisProvinceCityList) -> { .forEach((province, thisProvinceCityList) -> {
// 每个省下二级的市可选项 // 每个省下二级的市可选项
provinceToCityOptions.put( String optionValue = DropDownOptions.createOptionValue(province.getName(), province.getId());
DropDownOptions.createOptionValue(province.getName(), province.getId()), List<String> list = StreamUtils.toList(thisProvinceCityList, everyCity ->
thisProvinceCityList.stream() DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId()));
.map(everyCity -> provinceToCityOptions.put(optionValue, list);
DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId())
)
.collect(Collectors.toList())
);
}); });
// 规范的一级市用于级联市- // 规范的一级市用于级联市-
List<String> cityOptions = cityList.stream() List<String> cityOptions = StreamUtils.toList(cityList, everyCity ->
.map(everyCity -> DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId()));
DropDownOptions.createOptionValue(
everyCity.getName(),
everyCity.getId()
))
.collect(Collectors.toList());
// 规范的二级县用于级联市- // 规范的二级县用于级联市-
Map<String, List<String>> cityToAreaOptions = new HashMap<>(); Map<String, List<String>> cityToAreaOptions = new HashMap<>();
areaList.stream() StreamUtils.groupByKey(areaList, DemoCityData::getPData)
.collect(Collectors.groupingBy(DemoCityData::getPData))
.forEach((city, thisCityAreaList) -> { .forEach((city, thisCityAreaList) -> {
// 每个市下二级的县可选项 // 每个市下二级的县可选项
cityToAreaOptions.put( String optionValue = DropDownOptions.createOptionValue(city.getName(), city.getId());
DropDownOptions.createOptionValue(city.getName(), city.getId()), List<String> list = StreamUtils.toList(thisCityAreaList, everyCity ->
thisCityAreaList.stream() DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId()));
.map(everyArea -> cityToAreaOptions.put(optionValue, list);
DropDownOptions.createOptionValue(everyArea.getName(), everyArea.getId())
)
.collect(Collectors.toList())
);
}); });
// 因为省市县三个都是联动省级联市市级联县因此需要创建两个级联下拉分别以省和市为判断依据创建 // 因为省市县三个都是联动省级联市市级联县因此需要创建两个级联下拉分别以省和市为判断依据创建
@ -129,14 +110,14 @@ public class ExportExcelServiceImpl implements IExportExcelService {
// 到此为止所有的下拉框可选项已全部配置完毕 // 到此为止所有的下拉框可选项已全部配置完毕
// 接下来需要将Excel中的展示数据转换为对应的下拉选 // 接下来需要将Excel中的展示数据转换为对应的下拉选
List<ExportDemoVo> outList = excelDataList.stream().map(everyRowData -> { List<ExportDemoVo> outList = StreamUtils.toList(excelDataList, everyRowData -> {
// 只需要处理没有使用@ExcelDictFormat注解的下拉框 // 只需要处理没有使用@ExcelDictFormat注解的下拉框
// 一般来说可以直接在数据库查询即查询出省市县信息这里通过模拟操作赋值 // 一般来说可以直接在数据库查询即查询出省市县信息这里通过模拟操作赋值
everyRowData.setProvince(buildOptions(provinceList, everyRowData.getProvinceId())); everyRowData.setProvince(buildOptions(provinceList, everyRowData.getProvinceId()));
everyRowData.setCity(buildOptions(cityList, everyRowData.getCityId())); everyRowData.setCity(buildOptions(cityList, everyRowData.getCityId()));
everyRowData.setArea(buildOptions(areaList, everyRowData.getAreaId())); everyRowData.setArea(buildOptions(areaList, everyRowData.getAreaId()));
return everyRowData; return everyRowData;
}).collect(Collectors.toList()); });
ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options); ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options);
} }
@ -148,7 +129,7 @@ public class ExportExcelServiceImpl implements IExportExcelService {
DemoCityData demoCityData = groupByIdMap.get(id).get(0); DemoCityData demoCityData = groupByIdMap.get(id).get(0);
return DropDownOptions.createOptionValue(demoCityData.getName(), demoCityData.getId()); return DropDownOptions.createOptionValue(demoCityData.getName(), demoCityData.getId());
} else { } else {
return ""; return StrUtil.EMPTY;
} }
} }

View File

@ -281,14 +281,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override @Override
public Map<String, String> getAllDictByDictType(String dictType) { public Map<String, String> getAllDictByDictType(String dictType) {
List<SysDictData> thisDictTypeDataList = selectDictDataByType(dictType); List<SysDictData> list = selectDictDataByType(dictType);
Map<String, String> dictMap = null; return StreamUtils.toMap(list, SysDictData::getDictValue, SysDictData::getDictLabel);
for (SysDictData everyDictData : thisDictTypeDataList) {
if (ObjectUtil.isNull(dictMap)) {
dictMap = new HashMap<>();
}
dictMap.put(everyDictData.getDictValue(), everyDictData.getDictLabel());
}
return dictMap;
} }
} }