完善Excel模板文本格式与资产状态下拉

This commit is contained in:
wkc
2026-03-13 15:10:13 +08:00
parent d2f7810b46
commit 77f53cb991
6 changed files with 204 additions and 0 deletions

View File

@@ -2,7 +2,9 @@ package com.ruoyi.info.collection.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data;
import java.io.Serial;
@@ -26,6 +28,7 @@ public class CcdiAssetInfoExcel implements Serializable {
@ExcelProperty(value = "关系人证件号*", index = 0)
@ColumnWidth(22)
@Required
@TextFormat
private String personId;
/** 资产大类 */
@@ -75,6 +78,7 @@ public class CcdiAssetInfoExcel implements Serializable {
/** 资产状态 */
@ExcelProperty(value = "资产状态*", index = 9)
@ColumnWidth(14)
@DictDropdown(dictType = "ccdi_asset_status")
@Required
private String assetStatus;

View File

@@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown;
import com.ruoyi.common.annotation.Required;
import com.ruoyi.common.annotation.TextFormat;
import lombok.Data;
import java.io.Serial;
@@ -63,6 +64,7 @@ public class CcdiStaffFmyRelationExcel implements Serializable {
@ExcelProperty(value = "关系人证件号码*", index = 6)
@ColumnWidth(20)
@Required
@TextFormat
private String relationCertNo;
/** 手机号码1 */

View File

@@ -0,0 +1,78 @@
package com.ruoyi.info.collection.handler;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.ruoyi.common.annotation.TextFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Excel文本列写入处理器
*
* @author ruoyi
*/
@Slf4j
public class TextFormatWriteHandler implements SheetWriteHandler {
private final Class<?> modelClass;
public TextFormatWriteHandler(Class<?> modelClass) {
this.modelClass = modelClass;
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Workbook workbook = writeWorkbookHolder.getWorkbook();
Sheet sheet = writeSheetHolder.getSheet();
CellStyle textStyle = createTextStyle(workbook);
for (Field field : getAllFields(modelClass)) {
if (field.getAnnotation(TextFormat.class) == null) {
continue;
}
Integer columnIndex = getColumnIndex(field);
if (columnIndex == null) {
log.warn("字段[{}]没有指定@ExcelProperty的index跳过文本格式设置", field.getName());
continue;
}
sheet.setDefaultColumnStyle(columnIndex, textStyle);
log.info("成功将列[{}]设置为文本格式", columnIndex);
}
}
private CellStyle createTextStyle(Workbook workbook) {
DataFormat dataFormat = workbook.createDataFormat();
CellStyle style = workbook.createCellStyle();
style.setDataFormat(dataFormat.getFormat("@"));
return style;
}
private List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
while (clazz != null && clazz != Object.class) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
clazz = clazz.getSuperclass();
}
return fields;
}
private Integer getColumnIndex(Field field) {
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
if (excelProperty != null && excelProperty.index() >= 0) {
return excelProperty.index();
}
return null;
}
}

View File

@@ -5,6 +5,7 @@ import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.ruoyi.info.collection.handler.DictDropdownWriteHandler;
import com.ruoyi.info.collection.handler.RequiredFieldWriteHandler;
import com.ruoyi.info.collection.handler.TextFormatWriteHandler;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@@ -174,6 +175,7 @@ public class EasyExcelUtil {
.sheet(sheetName)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new DictDropdownWriteHandler(clazz))
.registerWriteHandler(new TextFormatWriteHandler(clazz))
.registerWriteHandler(new RequiredFieldWriteHandler(clazz))
.doWrite(List.of());
} catch (IOException e) {
@@ -200,6 +202,7 @@ public class EasyExcelUtil {
.sheet(sheetName)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new DictDropdownWriteHandler(clazz))
.registerWriteHandler(new TextFormatWriteHandler(clazz))
.registerWriteHandler(new RequiredFieldWriteHandler(clazz))
.doWrite(List.of());
} catch (IOException e) {
@@ -226,6 +229,7 @@ public class EasyExcelUtil {
.sheet(sheetName)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new DictDropdownWriteHandler(clazz))
.registerWriteHandler(new TextFormatWriteHandler(clazz))
.registerWriteHandler(new RequiredFieldWriteHandler(clazz))
.doWrite(list);
} catch (IOException e) {
@@ -253,6 +257,7 @@ public class EasyExcelUtil {
.sheet(sheetName)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new DictDropdownWriteHandler(clazz))
.registerWriteHandler(new TextFormatWriteHandler(clazz))
.registerWriteHandler(new RequiredFieldWriteHandler(clazz))
.doWrite(list);
} catch (IOException e) {

View File

@@ -0,0 +1,98 @@
package com.ruoyi.info.collection.utils;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.mock.web.MockHttpServletResponse;
import java.io.ByteArrayInputStream;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mockStatic;
class EasyExcelUtilTemplateTest {
@Test
void importTemplateWithDictDropdown_shouldAddAssetStatusDropdownToAssetTemplate() throws Exception {
MockHttpServletResponse response = new MockHttpServletResponse();
try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
mocked.when(() -> DictUtils.getDictCache("ccdi_asset_status"))
.thenReturn(List.of(
buildDictData("正常"),
buildDictData("冻结"),
buildDictData("处置中"),
buildDictData("报废")
));
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiAssetInfoExcel.class, "亲属资产信息");
}
try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
Sheet sheet = workbook.getSheetAt(0);
assertTrue(hasValidationOnColumn(sheet, 9), "资产状态列应包含下拉校验");
}
}
@Test
void importTemplateWithDictDropdown_shouldFormatCertificateColumnsAsText() throws Exception {
MockHttpServletResponse assetResponse = new MockHttpServletResponse();
MockHttpServletResponse familyResponse = new MockHttpServletResponse();
try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
mocked.when(() -> DictUtils.getDictCache("ccdi_asset_status"))
.thenReturn(List.of(buildDictData("正常")));
mocked.when(() -> DictUtils.getDictCache("ccdi_relation_type"))
.thenReturn(List.of(buildDictData("配偶")));
mocked.when(() -> DictUtils.getDictCache("ccdi_indiv_gender"))
.thenReturn(List.of(buildDictData("")));
mocked.when(() -> DictUtils.getDictCache("ccdi_certificate_type"))
.thenReturn(List.of(buildDictData("居民身份证")));
EasyExcelUtil.importTemplateWithDictDropdown(assetResponse, CcdiAssetInfoExcel.class, "亲属资产信息");
EasyExcelUtil.importTemplateWithDictDropdown(familyResponse, CcdiStaffFmyRelationExcel.class, "员工亲属关系信息");
}
try (Workbook assetWorkbook = WorkbookFactory.create(new ByteArrayInputStream(assetResponse.getContentAsByteArray()));
Workbook familyWorkbook = WorkbookFactory.create(new ByteArrayInputStream(familyResponse.getContentAsByteArray()))) {
assertTextColumn(assetWorkbook.getSheetAt(0), 0);
assertTextColumn(familyWorkbook.getSheetAt(0), 6);
}
}
private void assertTextColumn(Sheet sheet, int columnIndex) {
CellStyle style = sheet.getColumnStyle(columnIndex);
assertNotNull(style, "文本列应设置默认样式");
assertEquals("@", style.getDataFormatString(), "证件号列应使用文本格式");
}
private boolean hasValidationOnColumn(Sheet sheet, int columnIndex) {
for (DataValidation validation : sheet.getDataValidations()) {
for (CellRangeAddress address : validation.getRegions().getCellRangeAddresses()) {
if (address.getFirstColumn() <= columnIndex && address.getLastColumn() >= columnIndex) {
return true;
}
}
}
return false;
}
private SysDictData buildDictData(String label) {
SysDictData dictData = new SysDictData();
dictData.setDictLabel(label);
dictData.setDictValue(label);
return dictData;
}
}

View File

@@ -0,0 +1,17 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel文本格式注解
* 用于在生成Excel模板时将列设置为文本格式避免证件号等内容被自动转成数值。
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TextFormat {
}