fix: 【图表】下钻后导出格式错位

This commit is contained in:
tjlygdx
2026-05-18 15:25:54 +08:00
parent bd1887061e
commit 894d04609a
2 changed files with 138 additions and 18 deletions

View File

@@ -140,6 +140,7 @@ public class ChartDataServer implements ChartDataApi {
request.setHeader(dsHeader);
request.setExcelTypes(dsTypes);
}
viewDTO.setData(chartViewInfo.getData());
request.setDetails(tableRow);
request.setData(chartViewInfo.getData());
} catch (Exception e) {
@@ -386,19 +387,12 @@ public class ChartDataServer implements ChartDataApi {
public static void setExcelData(Sheet detailsSheet, CellStyle cellStyle, Object[] header, List<Object[]> details, ViewDetailField[] detailFields, Integer[] excelTypes, Comment comment, ChartViewDTO viewInfo, Workbook wb) {
List<CellStyle> styles = new ArrayList<>();
List<ChartViewFieldDTO> xAxis = new ArrayList<>();
xAxis.addAll(viewInfo.getXAxis());
xAxis.addAll(viewInfo.getYAxis());
xAxis.addAll(viewInfo.getXAxisExt());
xAxis.addAll(viewInfo.getYAxisExt());
xAxis.addAll(viewInfo.getExtStack());
xAxis.addAll(viewInfo.getDrillFields());
List<ChartViewFieldDTO> exportFields = resolveExportFields(viewInfo, header);
TableHeader tableHeader = null;
Integer totalDepth = 0;
List<CellRangeAddress> mergeConfig = new ArrayList<>();
if (StringUtils.equalsAnyIgnoreCase(viewInfo.getType(), "table-normal", "table-info")) {
for (ChartViewFieldDTO tmpAxis : xAxis) {
for (ChartViewFieldDTO tmpAxis : exportFields) {
if (tmpAxis.isHide()) {
continue;
}
@@ -415,10 +409,7 @@ public class ChartDataServer implements ChartDataApi {
if (tableHeaderMap.get("headerGroup") != null && Boolean.parseBoolean(tableHeaderMap.get("headerGroup").toString())) {
var tmpHeader = JsonUtil.parseObject((String) JsonUtil.toJSONString(customAttr.get("tableHeader")), TableHeader.class);
// 校验字段数量和顺序
var allAxis = new ArrayList<>(viewInfo.getXAxis().stream().filter(x -> !x.isHide()).toList());
if (StringUtils.equalsIgnoreCase(viewInfo.getType(), "table-normal")) {
allAxis.addAll(viewInfo.getYAxis().stream().filter(x -> !x.isHide()).toList());
}
var allAxis = new ArrayList<>(exportFields.stream().filter(x -> !x.isHide()).toList());
if (validateHeaderGroup(tmpHeader, allAxis)) {
tableHeader = tmpHeader;
for (TableHeader.ColumnInfo column : tableHeader.getHeaderGroupConfig().getColumns()) {
@@ -430,7 +421,6 @@ public class ChartDataServer implements ChartDataApi {
}
}
if ("table-info".equalsIgnoreCase(viewInfo.getType()) && !"dataset".equalsIgnoreCase(viewInfo.getDownloadType())) {
xAxis = xAxis.stream().filter(x -> !x.isHide()).toList();
Map<String, Object> tableCell = (Map<String, Object>) viewInfo.getCustomAttr().get("tableCell");
Boolean mergeCells = (Boolean) tableCell.get("mergeCells");
if (mergeCells != null && mergeCells) {
@@ -506,7 +496,7 @@ public class ChartDataServer implements ChartDataApi {
int width = 0;
Integer depth = 0;
for (TableHeader.ColumnInfo column : tableHeader.getHeaderGroupConfig().getColumns()) {
createCell(tableHeader, column, width, depth, detailsSheet, cellStyle, totalDepth, rowMap, xAxis);
createCell(tableHeader, column, width, depth, detailsSheet, cellStyle, totalDepth, rowMap, exportFields);
width = width + column.getWidth();
}
}
@@ -556,9 +546,11 @@ public class ChartDataServer implements ChartDataApi {
detailsSheet.setColumnWidth(j, 255 * 20);
} else if (cellValObj != null) {
try {
if (StringUtils.equalsAnyIgnoreCase(viewInfo.getType(), "table-info", "table-normal") && Arrays.asList(DeTypeConstants.DE_INT,DeTypeConstants.DE_FLOAT).contains(xAxis.get(j).getDeType())) {
if (StringUtils.equalsAnyIgnoreCase(viewInfo.getType(), "table-info", "table-normal")
&& j < exportFields.size()
&& Arrays.asList(DeTypeConstants.DE_INT, DeTypeConstants.DE_FLOAT).contains(exportFields.get(j).getDeType())) {
try {
FormatterCfgDTO formatterCfgDTO = xAxis.get(j).getFormatterCfg() == null ? new FormatterCfgDTO().setUnitLanguage(Lang.isChinese() ? "ch" : "en") : xAxis.get(j).getFormatterCfg();
FormatterCfgDTO formatterCfgDTO = exportFields.get(j).getFormatterCfg() == null ? new FormatterCfgDTO().setUnitLanguage(Lang.isChinese() ? "ch" : "en") : exportFields.get(j).getFormatterCfg();
row.getCell(j).setCellStyle(styles.get(j));
row.getCell(j).setCellValue(Double.valueOf(cellValue(formatterCfgDTO, new BigDecimal(cellValObj.toString()))));
} catch (Exception e) {
@@ -581,7 +573,7 @@ public class ChartDataServer implements ChartDataApi {
ChartSeniorFunctionCfgDTO functionCfgDTO = JsonUtil.parseObject((String) JsonUtil.toJSONString(senior.get("functionCfg")), ChartSeniorFunctionCfgDTO.class);
if (functionCfgDTO != null && StringUtils.isNotEmpty(functionCfgDTO.getEmptyDataStrategy()) && functionCfgDTO.getEmptyDataStrategy().equalsIgnoreCase("setZero")) {
if ((viewInfo.getType().equalsIgnoreCase("table-normal") || viewInfo.getType().equalsIgnoreCase("table-info"))) {
if (functionCfgDTO.getEmptyDataFieldCtrl().contains(xAxis.get(j).getDataeaseName())) {
if (j < exportFields.size() && functionCfgDTO.getEmptyDataFieldCtrl().contains(exportFields.get(j).getDataeaseName())) {
cell.setCellValue(0);
}
} else {
@@ -599,6 +591,53 @@ public class ChartDataServer implements ChartDataApi {
}
}
static List<ChartViewFieldDTO> resolveExportFields(ChartViewDTO viewInfo, Object[] header) {
List<ChartViewFieldDTO> fields = new ArrayList<>();
if (viewInfo != null && viewInfo.getData() != null && viewInfo.getData().get("fields") != null) {
Object fieldsObj = viewInfo.getData().get("fields");
if (fieldsObj instanceof List<?> fieldList && !fieldList.isEmpty() && fieldList.getFirst() instanceof ChartViewFieldDTO) {
fields.addAll(fieldList.stream().map(ChartViewFieldDTO.class::cast).toList());
} else {
fields.addAll(JsonUtil.parseList(JsonUtil.toJSONString(fieldsObj).toString(), new TypeReference<List<ChartViewFieldDTO>>() {
}));
}
}
if (CollectionUtils.isEmpty(fields)) {
appendFields(fields, viewInfo == null ? null : viewInfo.getXAxis());
appendFields(fields, viewInfo == null ? null : viewInfo.getYAxis());
appendFields(fields, viewInfo == null ? null : viewInfo.getXAxisExt());
appendFields(fields, viewInfo == null ? null : viewInfo.getYAxisExt());
appendFields(fields, viewInfo == null ? null : viewInfo.getExtStack());
appendFields(fields, viewInfo == null ? null : viewInfo.getDrillFields());
}
if (ArrayUtils.isEmpty(header) || CollectionUtils.isEmpty(fields)) {
return fields;
}
Map<String, Deque<ChartViewFieldDTO>> fieldMap = new HashMap<>();
fields.forEach(field -> fieldMap.computeIfAbsent(getExportFieldName(field), key -> new ArrayDeque<>()).add(field));
List<ChartViewFieldDTO> orderedFields = new ArrayList<>();
for (Object headerItem : header) {
if (headerItem == null) {
continue;
}
Deque<ChartViewFieldDTO> matchedFields = fieldMap.get(headerItem.toString());
if (matchedFields != null && !matchedFields.isEmpty()) {
orderedFields.add(matchedFields.removeFirst());
}
}
return CollectionUtils.isNotEmpty(orderedFields) ? orderedFields : fields;
}
private static void appendFields(List<ChartViewFieldDTO> target, List<ChartViewFieldDTO> source) {
if (CollectionUtils.isNotEmpty(source)) {
target.addAll(source);
}
}
private static String getExportFieldName(ChartViewFieldDTO field) {
return StringUtils.isNotBlank(field.getChartShowName()) ? field.getChartShowName() : field.getName();
}
private static List<CellRangeAddress> getMergeConfig(List<Object[]> data, int colIndex, int offsetHeight) {
var result = new ArrayList<CellRangeAddress>();
var preRange = new ArrayList<Integer[]>();

View File

@@ -3,17 +3,27 @@ package io.dataease.chart.server;
import io.dataease.api.chart.request.ChartExcelRequest;
import io.dataease.chart.constant.ChartConstants;
import io.dataease.chart.manage.ChartDataManage;
import io.dataease.constant.DeTypeConstants;
import io.dataease.exportCenter.manage.ExportCenterLimitManage;
import io.dataease.exportCenter.util.ExportCenterUtils;
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.FormatterCfgDTO;
import org.junit.Test;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -63,12 +73,75 @@ public class ChartDataServerTest {
assertEquals(100_000, resultCount);
}
@Test
public void findExcelDataStoresCalculatedFieldsOnViewInfo() throws Exception {
ChartDataServer chartDataServer = chartDataServerWithExportLimits(1_000_000L, 1_000_000L);
ChartViewDTO view = new ChartViewDTO();
view.setResultMode(ChartConstants.VIEW_RESULT_MODE.ALL);
ChartExcelRequest request = new ChartExcelRequest();
request.setDownloadType("view");
request.setViewInfo(view);
chartDataServer.findExcelData(request);
assertNotNull(view.getData());
assertEquals(1, ((List<?>) view.getData().get("fields")).size());
}
@Test
public void setExcelDataUsesCurrentDrillFieldOrderForNumericFormats() {
ChartViewFieldDTO province = field("province", "省份", DeTypeConstants.DE_STRING);
ChartViewFieldDTO city = field("city", "城市", DeTypeConstants.DE_STRING);
ChartViewFieldDTO amount = field("amount", "销售额", DeTypeConstants.DE_FLOAT);
amount.setFormatterCfg(new FormatterCfgDTO().setType("value").setDecimalCount(2));
ChartViewDTO view = new ChartViewDTO();
view.setType("table-info");
view.setXAxis(new ArrayList<>(Arrays.asList(province, amount)));
view.setDrillFields(new ArrayList<>(List.of(city)));
Map<String, Object> customAttr = new HashMap<>();
customAttr.put("tableHeader", new HashMap<String, Object>());
customAttr.put("tableCell", new HashMap<>(Map.of("mergeCells", false)));
view.setCustomAttr(customAttr);
Map<String, Object> data = new HashMap<>();
data.put("fields", new ArrayList<>(Arrays.asList(province, city, amount)));
view.setData(data);
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("data");
CellStyle headStyle = workbook.createCellStyle();
List<Object[]> details = new ArrayList<>();
details.add(new Object[]{"省份", "城市", "销售额"});
details.add(new Object[]{"浙江", "杭州", "1234.5"});
ChartDataServer.setExcelData(
sheet,
headStyle,
new Object[]{"省份", "城市", "销售额"},
details,
null,
new Integer[]{DeTypeConstants.DE_STRING, DeTypeConstants.DE_STRING, DeTypeConstants.DE_FLOAT},
view,
workbook
);
assertEquals("General", sheet.getRow(1).getCell(1).getCellStyle().getDataFormatString());
assertEquals("0.00", sheet.getRow(1).getCell(2).getCellStyle().getDataFormatString());
assertEquals(1234.5D, sheet.getRow(1).getCell(2).getNumericCellValue(), 0.0001D);
} catch (Exception e) {
throw new AssertionError(e);
}
}
private ChartDataServer chartDataServerWithExportLimits(Long viewLimit, Long datasetLimit) throws Exception {
ChartDataManage chartDataManage = mock(ChartDataManage.class);
when(chartDataManage.calcData(any(ChartViewDTO.class))).thenAnswer(invocation -> {
ChartViewDTO result = new ChartViewDTO();
Map<String, Object> data = new HashMap<>();
data.put("sourceData", new ArrayList<Object[]>());
data.put("fields", List.of(field("name", "名称", DeTypeConstants.DE_STRING)));
result.setData(data);
return result;
});
@@ -82,4 +155,12 @@ public class ChartDataServerTest {
ReflectionTestUtils.setField(chartDataServer, "chartDataManage", chartDataManage);
return chartDataServer;
}
private static ChartViewFieldDTO field(String dataeaseName, String name, Integer deType) {
ChartViewFieldDTO field = new ChartViewFieldDTO();
field.setDataeaseName(dataeaseName);
field.setName(name);
field.setDeType(deType);
return field;
}
}