package com.hikvision.building.cloud.inspect.common.utils.excel;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hikvision.building.cloud.gaia.common.exception.BusinessException;
import com.hikvision.building.cloud.util.common.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author: chenzhuo9
* @since: 2025年09月01日16:51
*/
@RunWith(MockitoJUnitRunner.class)
@Slf4j
public class DeviceAIAbilityExcelTest {
private final static String AI_JSON_PATH = "E:\\工作\\AI\\算法LED配置相关文件\\json\\";
private final static String IPC_G5_J40189120 = "IPC_G5_J40189120";
private final static String IPC_H7_C87513270 = "IPC_H7_C87513270";
private final static String IPC_H8_K96568605 = "IPC_H8_K96568605";
private final static String IPC_H9_AZ0730868 = "IPC_H9_AZ0730868";
private final static String NVR_KT2_J42839102 = "NVR_KT2_J42839102";
private final static String NVR_KT4_FV6903374 = "NVR_KT4_FV6903374";
private final static List<String> DEVICE_LIST = List.of(IPC_G5_J40189120, IPC_H7_C87513270, IPC_H8_K96568605,
IPC_H9_AZ0730868, NVR_KT2_J42839102, NVR_KT4_FV6903374);
private final static String IPC_G5_JSON = AI_JSON_PATH + IPC_G5_J40189120 +".json";
private final static String IPC_H7_JSON = AI_JSON_PATH + IPC_H7_C87513270 +".json";
private static ObjectMapper objectMapper = new ObjectMapper();
@Test
public void generateExcel() throws IOException {
String path = AI_JSON_PATH + "device_ai_ability_"+ System.currentTimeMillis()+".xlsx";
ExcelWriter excelWriter = EasyExcel.write(path).build();
DEVICE_LIST.forEach(device->{
JsonNode rootNode = null;
try {
rootNode = objectMapper.readTree(new File(AI_JSON_PATH + device + ".json"));
} catch (IOException e) {
log.info("device:{}", device);
throw new RuntimeException(e);
}
List<DeviceAIAbilityTestResultDTO> resultDTOList = convertJsonNode(rootNode);
excelWriter.write(resultDTOList, createSheet(device, resultDTOList));
});
excelWriter.finish();
}
private WriteSheet createSheet(String device, List<DeviceAIAbilityTestResultDTO> resultDTOList){
WriteSheet sheet1 = EasyExcel.writerSheet(device)
.head(DeviceAIAbilityTestResultDTO.class) // 设置表头类
.registerWriteHandler(new SafePrecomputedMergeStrategy(
1, // 标题行数
resultDTOList,
"taskType", "modelType" // 要合并的字段
))
.build();
return sheet1;
}
private List<DeviceAIAbilityTestResultDTO> convertJsonNode(JsonNode aiJson) {
if (ObjectUtils.isEmpty(aiJson)) {
throw new BusinessException(400, "aiJson不能为空");
}
JsonNode ruleRelatedCaps = aiJson.path("ruleRelatedCaps");
if (ObjectUtils.isEmpty(ruleRelatedCaps)) {
throw new BusinessException(400, "ruleRelatedCaps不能为空");
}
List<DeviceAIAbilityTestResultDTO> resultList = new ArrayList<>();
processJsonNodeTask(ruleRelatedCaps, resultList);
return resultList;
}
// private List<DeviceAIAbilityTestResultDTO> convert(AIJsonTestDTO aiJson) {
// if (ObjectUtils.isEmpty(aiJson)) {
// throw new BusinessException(400, "aiJson不能为空");
// }
// if (ObjectUtils.isEmpty(aiJson.getRuleRelatedCaps())) {
// throw new BusinessException(400, "ruleRelatedCaps不能为空");
// }
// List<DeviceAIAbilityTestResultDTO> resultList = new ArrayList<>();
// proce(aiJson, resultList);
// return resultList;
// }
private void processJsonNodeTask(JsonNode ruleRelatedCaps, List<DeviceAIAbilityTestResultDTO> resultList){
processVideoTask(ruleRelatedCaps.path("videoTask"), resultList, TaskTypeEnum.VIDEO_TASK);
// processVideoPollingAnalysisTask(ruleRelatedCaps.path("videoPollingAnalysisTask"), resultList, TaskTypeEnum.VIDEO_POLLING_ANALYSIS_TASK);
// processPicturePollingAnalysisTask(ruleRelatedCaps.path("picturePollingAnalysisTask"), resultList, TaskTypeEnum.PICTURE_POLLING_ANALYSIS_TASK);
}
private void processVideoTask(JsonNode taskDTO,
List<DeviceAIAbilityTestResultDTO> resultList,
TaskTypeEnum taskTypeEnum){
if (ObjectUtils.isNotEmpty(taskDTO)) {
JsonNode detect = taskDTO.path("detect");
if (ObjectUtils.isNotEmpty(detect)) {
Map<TriggerTypeEnum, JsonNode> triggerTypeEnumRuleConfigDTOMap =
Map.of( TriggerTypeEnum._$1073774593, detect.path("1073774593"),
TriggerTypeEnum._$1073758209, detect.path("1073758209"),
TriggerTypeEnum._$1073758211, detect.path("1073758211"),
TriggerTypeEnum._$1073758212, detect.path("1073758212"),
TriggerTypeEnum._$1073758213, detect.path("1073758213"),
TriggerTypeEnum._$1073758217, detect.path("1073758217")
);
processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.DETECT);
}
JsonNode classify = taskDTO.path("classify");
if (ObjectUtils.isNotEmpty(classify)) {
Map<TriggerTypeEnum, JsonNode> triggerTypeEnumRuleConfigDTOMap =
Map.of( TriggerTypeEnum._$1073774593, classify.path("1073774593"),
TriggerTypeEnum._$1073758210, classify.path("1073758210")
);
processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.CLASSIFY);
}
JsonNode mixed = taskDTO.path("mixed");
if (ObjectUtils.isNotEmpty(mixed)) {
Map<TriggerTypeEnum, JsonNode> triggerTypeEnumRuleConfigDTOMap =
Map.of( TriggerTypeEnum._$1073774593, mixed.path("1073774593"),
TriggerTypeEnum._$1073758209, mixed.path("1073758209"),
TriggerTypeEnum._$1073758211, mixed.path("1073758211"),
TriggerTypeEnum._$1073758212, mixed.path("1073758212"),
TriggerTypeEnum._$1073758213, mixed.path("1073758213")
);
processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.MIXED);
}
JsonNode ocr = taskDTO.path("ocr");
if (ObjectUtils.isNotEmpty(ocr)) {
Map<TriggerTypeEnum, JsonNode> triggerTypeEnumRuleConfigDTOMap =
Map.of( TriggerTypeEnum._$1073758217, ocr.path("1073758217"));
processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.OCR);
}
}
}
// private void processVideoPollingAnalysisTask(AIJsonTestDTO.RuleRelatedCapsDTO.VideoPollingAnalysisTaskDTO taskDTO,
// List<DeviceAIAbilityTestResultDTO> resultList,
// TaskTypeEnum taskTypeEnum){
// if (ObjectUtils.isNotEmpty(taskDTO)) {
// AIJsonTestDTO.RuleRelatedCapsDTO.VideoPollingAnalysisTaskDTO.DetectDTO detect = taskDTO.getDetect();
// if (ObjectUtils.isNotEmpty(detect)) {
// Map<TriggerTypeEnum, AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO> triggerTypeEnumRuleConfigDTOMap =
// Map.of( TriggerTypeEnum._$1073774593, taskDTO.getDetect().get_$1073774593(),
// TriggerTypeEnum._$1073758209, taskDTO.getDetect().get_$1073758209(),
// TriggerTypeEnum._$1073758211, taskDTO.getDetect().get_$1073758211(),
// TriggerTypeEnum._$1073758212, taskDTO.getDetect().get_$1073758212(),
// TriggerTypeEnum._$1073758213, taskDTO.getDetect().get_$1073758213()
// );
// processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.DETECT);
// }
// if (ObjectUtils.isNotEmpty(taskDTO.getClassify())) {
// Map<TriggerTypeEnum, AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO> triggerTypeEnumRuleConfigDTOMap =
// Map.of( TriggerTypeEnum._$1073774593, taskDTO.getClassify().get_$1073774593(),
// TriggerTypeEnum._$1073758210, taskDTO.getClassify().get_$1073758210()
// );
// processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.CLASSIFY);
// }
// if (ObjectUtils.isNotEmpty(taskDTO.getMixed())) {
// Map<TriggerTypeEnum, AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO> triggerTypeEnumRuleConfigDTOMap =
// Map.of( TriggerTypeEnum._$1073774593, taskDTO.getMixed().get_$1073774593(),
// TriggerTypeEnum._$1073758209, taskDTO.getMixed().get_$1073758209(),
// TriggerTypeEnum._$1073758211, taskDTO.getMixed().get_$1073758211(),
// TriggerTypeEnum._$1073758212, taskDTO.getMixed().get_$1073758212(),
// TriggerTypeEnum._$1073758213, taskDTO.getMixed().get_$1073758213()
// );
// processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.MIXED);
// }
// if (ObjectUtils.isNotEmpty(taskDTO.getOcr())) {
// Map<TriggerTypeEnum, AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO> triggerTypeEnumRuleConfigDTOMap =
// Map.of( TriggerTypeEnum._$1073758217, taskDTO.getOcr().get_$1073758217());
// processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.OCR);
// }
// }
// }
// private void processPicturePollingAnalysisTask(AIJsonTestDTO.RuleRelatedCapsDTO.PicturePollingAnalysisTaskDTO taskDTO,
// List<DeviceAIAbilityTestResultDTO> resultList,
// TaskTypeEnum taskTypeEnum){
// if (ObjectUtils.isNotEmpty(taskDTO)) {
//// if (ObjectUtils.isNotEmpty(taskDTO.getClassify())) {
//// Map<TriggerTypeEnum, AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO> triggerTypeEnumRuleConfigDTOMap =
//// Map.of( TriggerTypeEnum._$1073774593, taskDTO.getDetect().get_$1073774593(),
//// TriggerTypeEnum._$1073758209, taskDTO.getDetect().get_$1073758209(),
//// TriggerTypeEnum._$1073758211, taskDTO.getDetect().get_$1073758211(),
//// TriggerTypeEnum._$1073758212, taskDTO.getDetect().get_$1073758212(),
//// TriggerTypeEnum._$1073758213, taskDTO.getDetect().get_$1073758213()
//// );
//// processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.DETECT);
//// }
// if (ObjectUtils.isNotEmpty(taskDTO.getClassify())) {
// Map<TriggerTypeEnum, AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO> triggerTypeEnumRuleConfigDTOMap =
// Map.of(
// TriggerTypeEnum._$1073758210, taskDTO.getClassify().get_$1073758210()
// );
// processModelType(triggerTypeEnumRuleConfigDTOMap, resultList, taskTypeEnum, ModelTypeEnum.CLASSIFY);
// }
// }
// }
private void processModelType(Map<TriggerTypeEnum, JsonNode> triggerTypeEnumJsonNodeMap,
List<DeviceAIAbilityTestResultDTO> resultList,
TaskTypeEnum taskTypeEnum, ModelTypeEnum modelTypeEnum){
if (ObjectUtils.isNotEmpty(triggerTypeEnumJsonNodeMap)) {
triggerTypeEnumJsonNodeMap.forEach((triggerTypeEnum, jsonNode) -> {
if (!jsonNode.isEmpty()) {
AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO ruleConfigDTO = null;
try {
ruleConfigDTO = objectMapper.treeToValue(jsonNode, AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
resultList.add(convertRuleConfig(taskTypeEnum.desc,triggerTypeEnum, modelTypeEnum.desc, ruleConfigDTO));
}
});
}
}
private void processDetect(AIJsonTestDTO.RuleRelatedCapsDTO.VideoTaskDTO videoTask, List<DeviceAIAbilityTestResultDTO> resultList,
TaskTypeEnum taskTypeEnum, ModelTypeEnum modelTypeEnum){
if (ObjectUtils.isNotEmpty(videoTask.getDetect())) {
AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO _$1073758209 = videoTask.getDetect().get_$1073758209();
if (ObjectUtils.isNotEmpty(_$1073758209)) {
resultList.add(convertRuleConfig(taskTypeEnum.desc,TriggerTypeEnum._$1073758209, modelTypeEnum.desc, _$1073758209));
}
AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO _$1073758211 = videoTask.getDetect().get_$1073758211();
if (ObjectUtils.isNotEmpty(_$1073758211)) {
resultList.add(convertRuleConfig(taskTypeEnum.desc,TriggerTypeEnum._$1073758211,modelTypeEnum.desc, _$1073758211));
}
AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO _$1073758212 = videoTask.getDetect().get_$1073758212();
if (ObjectUtils.isNotEmpty(_$1073758212)) {
resultList.add(convertRuleConfig(taskTypeEnum.desc,TriggerTypeEnum._$1073758212,modelTypeEnum.desc, _$1073758212));
}
AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO _$1073758213 = videoTask.getDetect().get_$1073758213();
if (ObjectUtils.isNotEmpty(_$1073758213)) {
resultList.add(convertRuleConfig(taskTypeEnum.desc,TriggerTypeEnum._$1073758213,modelTypeEnum.desc, _$1073758213));
}
AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO _$1073758217 = videoTask.getDetect().get_$1073758217();
if (ObjectUtils.isNotEmpty(_$1073758217)) {
resultList.add(convertRuleConfig(taskTypeEnum.desc,TriggerTypeEnum._$1073758217,modelTypeEnum.desc, _$1073758217));
}
AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO _$1073774593 = videoTask.getDetect().get_$1073774593();
if (ObjectUtils.isNotEmpty(_$1073774593)) {
resultList.add(convertRuleConfig(taskTypeEnum.desc,TriggerTypeEnum._$1073774593,modelTypeEnum.desc, _$1073774593));
}
}
}
private DeviceAIAbilityTestResultDTO convertRuleConfig(String taskType, TriggerTypeEnum triggerTypeEnum, String modelType,
AIJsonTestDTO.RuleRelatedCapsDTO.RuleConfigDTO ruleConfigDTO) {
DeviceAIAbilityTestResultDTO dto = new DeviceAIAbilityTestResultDTO();
dto.setTaskType(taskType);
dto.setTriggerTypeStr(triggerTypeEnum.desc);
dto.setTriggerType(triggerTypeEnum.code);
dto.setModelType(modelType);
dto.setRegionType(RegionTypeEnum.getInstanceBySplit(ruleConfigDTO.getRegionType()));
dto.setTargetType(ruleConfigDTO.getTriggerType());
dto.setTargetStatus(ruleConfigDTO.getTargetStatus());
dto.setSubTargetNumberCondition(ruleConfigDTO.getSubTargetNumberCondition());
dto.setNumberCondition(ruleConfigDTO.getNumberCondition());
dto.setDuration(ruleConfigDTO.getDuration());
dto.setSubDuration(ruleConfigDTO.getSubDuration());
dto.setDurationSensitive(ruleConfigDTO.getDurationSensitive());
dto.setTriggerInterval(ruleConfigDTO.getTriggerInterval());
dto.setSensitive(ruleConfigDTO.getSensitive());
dto.setMaxTriggerTimes(ruleConfigDTO.getMaxTriggerTimes());
dto.setCountInterval(ruleConfigDTO.getCountInterval());
dto.setAlertConfThreshold(ruleConfigDTO.getAlertConfThreshold());
dto.setFilterParam(ruleConfigDTO.getFilterParam());
dto.setOOSDInfo(ruleConfigDTO.getOOSDInfo());
dto.setSizeFilter(ruleConfigDTO.getSizeFilter());
dto.setOSensorInfo(ruleConfigDTO.getOSensorInfo());
dto.setRelationAnalysis(ruleConfigDTO.getRelationAnalysis());
dto.setOverlapRatio(ruleConfigDTO.getOverlapRatio());
dto.setStillFilterParam(ruleConfigDTO.getStillFilterParam());
dto.setShieldArea(ruleConfigDTO.getShieldArea());
dto.setMaxCmpNum(ruleConfigDTO.getMaxCmpNum());
dto.setTopN(ruleConfigDTO.getTopN());
dto.setLibInfo(ruleConfigDTO.getLibInfo());
dto.setUploadSuccess(ruleConfigDTO.getUploadSuccess());
dto.setUploadFailed(ruleConfigDTO.getUploadFailed());
dto.setTriggerMode(ruleConfigDTO.getTriggerMode());
dto.setRuleOverlayEnable(ruleConfigDTO.getRuleOverlayEnable());
dto.setTargetOverlayEnable(ruleConfigDTO.getTargetOverlayEnable());
dto.setRuleNameInfo(ruleConfigDTO.getRuleNameInfo());
dto.setRatioCondition(ruleConfigDTO.getRatioCondition());
dto.setTargetInfoOverlayEnable(ruleConfigDTO.getTargetInfoOverlayEnable());
dto.setTargetAttrOverlayEnable(ruleConfigDTO.getTargetAttrOverlayEnable());
dto.setTargetAreaOverlayEnable(ruleConfigDTO.getTargetAreaOverlayEnable());
dto.setTargetConfidenceOverlayEnable(ruleConfigDTO.getTargetConfidenceOverlayEnable());
dto.setTargetIDOverlayEnable(ruleConfigDTO.getTargetIDOverlayEnable());
return dto;
}
protected enum TaskTypeEnum {
VIDEO_TASK("videoTask", "实时视频"),
VIDEO_POLLING_ANALYSIS_TASK("videoPollingAnalysisTask", "轮询视频"),
PICTURE_POLLING_ANALYSIS_TASK("picturePollingAnalysisTask", "定时抓图");
private String code;
private String desc;
TaskTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static TaskTypeEnum getInstance(String taskType) {
if (ObjectUtils.isEmpty(taskType)) {
throw new BusinessException(400, "taskType不能为空");
}
return Arrays.stream(TaskTypeEnum.values()).filter(e -> e.code.equals(taskType)).findFirst()
.orElseThrow(() -> new BusinessException(400, "无效的taskType"));
}
}
protected enum ModelTypeEnum {
DETECT("detect", "检测"),
CLASSIFY("classify", "分类"),
MIXED("mixed", "混合"),
OCR("ocr", "OCR"),
DIFF("diff", "diff"),
RETRIEVAL("retrieval", "retrieval");
private String code;
private String desc;
ModelTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static ModelTypeEnum getInstance(String modelType) {
if (ObjectUtils.isEmpty(modelType)) {
throw new BusinessException(400, "modelType不能为空");
}
return Arrays.stream(ModelTypeEnum.values()).filter(e -> e.code.equals(modelType)).findFirst()
.orElseThrow(() -> new BusinessException(400, "无效的modelType"));
}
}
protected enum TriggerTypeEnum {
_$1073758209("1073758209", "区域目标异常状态检测"),
_$1073774593("1073774593", "全分析规则"),
_$1073758210("1073758210", "区域异常状态检测"),
_$1073758211("1073758211", "跨线目标检测"),
_$1073758212("1073758212", "跨线目标统计"),
_$1073758213("1073758213", "区域目标数统计"),
_$1073758214("1073758214", "区域交叠比统计"),
_$1073758215("1073758215", "区域交叠比异常检测"),
_$1073758216("1073758216", "OCR触发抓拍"),
_$1073758217("1073758217", "区域状态变化检测(图像比对,相对背景)"),
// _$1073754112("1073754112", "区域状态变化检测(图像比对,相对背景)")
;
private String field;
private String code;
private String desc;
TriggerTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static TriggerTypeEnum getInstance(String triggerType) {
if (ObjectUtils.isEmpty(triggerType)) {
throw new BusinessException(400, "triggerType不能为空");
}
return Arrays.stream(TriggerTypeEnum.values()).filter(e -> e.code.equals(triggerType)).findFirst()
.orElseThrow(() -> new BusinessException(400, "无效的triggerType"));
}
}
protected enum RegionTypeEnum {
RECT("Rect", "矩形"),
AREA("Area", "多边形"),
LINE_CROSS("LineCross", "线");
private String code;
private String desc;
RegionTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static RegionTypeEnum getInstance(String regionType) {
if (ObjectUtils.isEmpty(regionType)) {
throw new BusinessException(400, "regionType不能为空");
}
return Arrays.stream(RegionTypeEnum.values()).filter(e -> e.code.equals(regionType)).findFirst()
.orElseThrow(() -> new BusinessException(400, "无效的regionType:"+regionType));
}
public static String getInstanceBySplit(String regionType) {
if (ObjectUtils.isEmpty(regionType)) {
throw new BusinessException(400, "regionType不能为空");
}
return Arrays.stream(regionType.split(","))
.map(rType -> getInstance(rType).desc) // 获取枚举的code
.collect(Collectors.joining(","));
}
}
public static class SafePrecomputedMergeStrategy implements SheetWriteHandler {
private final Map<Integer, List<CellRangeAddress>> mergeRegions = new HashMap<>();
private final int titleRowCount;
private final List<?> dataList;
private final List<String> mergeFields;
private CellStyle centerStyle;
/**
* @param titleRowCount 标题行数(通常是1)
* @param dataList 要写入的DTO列表
* @param mergeFields 需要合并的字段名(如:["taskType", "modelType"])
*/
public SafePrecomputedMergeStrategy(int titleRowCount, List<?> dataList, String... mergeFields) {
this.titleRowCount = titleRowCount;
this.dataList = dataList;
this.mergeFields = Arrays.asList(mergeFields);
// 预先计算所有合并区域
precomputeMergeRegions();
}
private void precomputeMergeRegions() {
Map<String, Integer> fieldToIndex = new HashMap<>();
// 通过反射获取字段在DTO中的索引位置
if (!dataList.isEmpty()) {
Object firstItem = dataList.get(0);
java.lang.reflect.Field[] fields = firstItem.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fieldToIndex.put(fields[i].getName(), i);
}
}
// 为每个合并字段计算合并区域
for (String field : mergeFields) {
Integer colIndex = fieldToIndex.get(field);
if (colIndex != null) {
mergeRegions.put(colIndex, calculateRegionsForColumn(colIndex));
}
}
}
private List<CellRangeAddress> calculateRegionsForColumn(int colIndex) {
List<CellRangeAddress> regions = new ArrayList<>();
if (dataList.isEmpty()) return regions;
int startRow = titleRowCount; // 数据起始行
Object lastValue = getFieldValue(dataList.get(0), colIndex);
for (int i = 1; i < dataList.size(); i++) {
Object currentValue = getFieldValue(dataList.get(i), colIndex);
// 值变化时记录合并区域
if (!Objects.equals(lastValue, currentValue)) {
if (i - 1 >= startRow) { // 至少2行相同
regions.add(new CellRangeAddress(
startRow,
titleRowCount + i - 1,
colIndex,
colIndex
));
}
startRow = titleRowCount + i;
}
lastValue = currentValue;
}
// 处理最后一段区域
if (dataList.size() - (startRow - titleRowCount) > 1) {
regions.add(new CellRangeAddress(
startRow,
titleRowCount + dataList.size() - 1,
colIndex,
colIndex
));
}
return regions;
}
private Object getFieldValue(Object dto, int fieldIndex) {
try {
java.lang.reflect.Field[] fields = dto.getClass().getDeclaredFields();
if (fieldIndex < fields.length) {
fields[fieldIndex].setAccessible(true);
return fields[fieldIndex].get(dto);
}
} catch (Exception e) {
// 忽略异常
}
return null;
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder,
WriteSheetHolder writeSheetHolder) {
// 创建居中样式(在创建工作表前初始化)
Workbook workbook = writeWorkbookHolder.getWorkbook();
this.centerStyle = workbook.createCellStyle();
// 设置水平居中
centerStyle.setAlignment(HorizontalAlignment.LEFT);
// 设置垂直居中
centerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 设置边框(可选)
centerStyle.setBorderTop(BorderStyle.THIN);
centerStyle.setBorderBottom(BorderStyle.THIN);
centerStyle.setBorderLeft(BorderStyle.THIN);
centerStyle.setBorderRight(BorderStyle.THIN);
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder,
WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
// 应用合并区域并设置样式
for (Map.Entry<Integer, List<CellRangeAddress>> entry : mergeRegions.entrySet()) {
int columnIndex = entry.getKey();
for (CellRangeAddress region : entry.getValue()) {
try {
// 添加合并区域
sheet.addMergedRegion(region);
// 设置合并区域样式为居中
applyCenterStyle(sheet, region, columnIndex);
} catch (Exception e) {
System.err.println("合并区域失败: " + region.formatAsString() +
" 原因: " + e.getMessage());
}
}
}
}
/**
* 为合并区域的所有单元格应用居中样式
*/
private void applyCenterStyle(Sheet sheet, CellRangeAddress region, int columnIndex) {
for (int rowNum = region.getFirstRow(); rowNum <= region.getLastRow(); rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null) continue;
Cell cell = row.getCell(columnIndex);
if (cell == null) continue;
// 应用居中样式
cell.setCellStyle(centerStyle);
}
}
}
}
帮我想一个设计模式,优化一下代码,将单元测试类和每个类都分开,另外我简单说一下逻辑:
现在我有多个类,例如IPCH5 NVRKT2 IPCH9,每个类都有单独的PicturePollingAnalysisTask VideoPollingAnalysisTask VideoTask,里面的逻辑都不一样,怎么调整
最新发布