|
|
@@ -147,7 +147,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
List<CostSurveyTemplateUpload> uploadList = costSurveyTemplateUploadManager.listByTaskId(taskId);
|
|
|
//创建记录
|
|
|
if (uploadList.isEmpty()) {
|
|
|
- List<CostSurveyTemplate> costSurveyTemplates = costSurveyTemplateManager.taskListByCatalogId(catalogId);
|
|
|
+ CostProjectTask task = costProjectTaskManager.getById(taskId);
|
|
|
+ List<CostSurveyTemplate> costSurveyTemplates = costSurveyTemplateManager.taskListByCatalogId(task.getCatalogId());
|
|
|
for (CostSurveyTemplate template : costSurveyTemplates) {
|
|
|
CostSurveyTemplateUpload upload = new CostSurveyTemplateUpload();
|
|
|
upload.setSurveyTemplateId(template.getSurveyTemplateId());
|
|
|
@@ -863,10 +864,19 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 创建表头行
|
|
|
Row headerRow = sheet.createRow(0);
|
|
|
+ int colIndex = 0;
|
|
|
+
|
|
|
+ // 添加原有表头
|
|
|
for (int i = 0; i < headersList.size(); i++) {
|
|
|
- headerRow.createCell(i).setCellValue(headersList.get(i).getFieldName());
|
|
|
+ headerRow.createCell(colIndex++).setCellValue(headersList.get(i).getFieldName());
|
|
|
}
|
|
|
|
|
|
+ // 添加行ID列和父行ID列(固定表需要)
|
|
|
+ int rowIdColIndex = colIndex;
|
|
|
+ headerRow.createCell(colIndex++).setCellValue("行ID");
|
|
|
+ int parentIdColIndex = colIndex;
|
|
|
+ headerRow.createCell(colIndex++).setCellValue("父行ID");
|
|
|
+
|
|
|
// 填充数据
|
|
|
if (itemsList != null && !itemsList.isEmpty()) {
|
|
|
|
|
|
@@ -887,7 +897,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
System.out.println(" headersId: " + headersId + ", 数量: " + count)
|
|
|
);
|
|
|
|
|
|
- fillExcelDataVerify(sheet, itemsList, headerIndexMap);
|
|
|
+ fillExcelDataVerify(sheet, itemsList, headerIndexMap, rowIdColIndex, parentIdColIndex);
|
|
|
}
|
|
|
|
|
|
// 列宽处理
|
|
|
@@ -896,6 +906,10 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
sheet.setColumnWidth(i, Math.max(sheet.getColumnWidth(i), 3000));
|
|
|
}
|
|
|
|
|
|
+ // 隐藏行ID和父行ID列
|
|
|
+ sheet.setColumnHidden(rowIdColIndex, true);
|
|
|
+ sheet.setColumnHidden(parentIdColIndex, true);
|
|
|
+
|
|
|
// 输出
|
|
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
response.setCharacterEncoding("utf-8");
|
|
|
@@ -1047,6 +1061,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
List<CostSurveyTemplateUploadData> dataList = new ArrayList<>();
|
|
|
+ Map<String, Integer> rowIdToExcelRowMap = new HashMap<>(); // rowId -> Excel行号映射
|
|
|
int dataRowCount = 0;
|
|
|
|
|
|
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
@@ -1072,6 +1087,9 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
"row_" + System.currentTimeMillis() : "row_" + rowIndex;
|
|
|
}
|
|
|
|
|
|
+ // 记录 rowId 到 Excel 行号的映射(Excel行号从1开始,加上表头行,所以是 rowIndex + 1)
|
|
|
+ rowIdToExcelRowMap.put(currentRowId, rowIndex + 1);
|
|
|
+
|
|
|
// 读取父行ID
|
|
|
String parentRowId = null;
|
|
|
if (parentIdColumnIndex != null) {
|
|
|
@@ -1149,7 +1167,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
return CommonResult.<String>error().message("Excel文件中没有有效数据");
|
|
|
}
|
|
|
|
|
|
- verifyImportData(dataList, type, surveyTemplateId);
|
|
|
+ verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
|
|
|
// 更新上传状态
|
|
|
@@ -1213,9 +1231,11 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
return CommonResult.<String>error().message("Excel文件第一行为空");
|
|
|
}
|
|
|
|
|
|
- // 检查是否有立项年限列和备注列
|
|
|
+ // 检查是否有立项年限列、备注列、行ID列和父行ID列
|
|
|
Map<String, Integer> auditPeriodColumnMap = new HashMap<>(); // 年限->列索引
|
|
|
Integer remarkColumnIndex = null; // 备注列索引
|
|
|
+ Integer rowIdColumnIndex = null; // 行ID列索引
|
|
|
+ Integer parentIdColumnIndex = null; // 父行ID列索引
|
|
|
Map<Integer, CostSurveyFdTemplateHeaders> columnIndexMap = new HashMap<>();
|
|
|
for (int i = 0; i < headerRow.getLastCellNum(); i++) {
|
|
|
Cell cell = headerRow.getCell(i);
|
|
|
@@ -1225,6 +1245,10 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
String trimmedValue = cellValue.trim();
|
|
|
if ("备注".equals(trimmedValue)) {
|
|
|
remarkColumnIndex = i;
|
|
|
+ } else if ("行ID".equals(trimmedValue)) {
|
|
|
+ rowIdColumnIndex = i;
|
|
|
+ } else if ("父行ID".equals(trimmedValue)) {
|
|
|
+ parentIdColumnIndex = i;
|
|
|
} else if (trimmedValue.matches("\\d{4}")) {
|
|
|
// 如果是4位数字,认为是年限列
|
|
|
auditPeriodColumnMap.put(trimmedValue, i);
|
|
|
@@ -1243,14 +1267,44 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
List<CostSurveyTemplateUploadData> dataList = new ArrayList<>();
|
|
|
+ Map<String, Integer> rowIdToExcelRowMap = new HashMap<>(); // rowId -> Excel行号映射
|
|
|
int dataRowCount = 0;
|
|
|
|
|
|
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
Row dataRow = sheet.getRow(rowIndex);
|
|
|
if (dataRow == null || isEmptyRow(dataRow)) continue;
|
|
|
|
|
|
- String currentRowId = "1".equals(templateType) ?
|
|
|
- "row_" + System.currentTimeMillis() : "row_" + rowIndex;
|
|
|
+ // 确定当前行的rowId
|
|
|
+ String currentRowId;
|
|
|
+ if (rowIdColumnIndex != null) {
|
|
|
+ // 如果有行ID列,必须使用Excel中的行ID
|
|
|
+ Cell rowIdCell = dataRow.getCell(rowIdColumnIndex);
|
|
|
+ String excelRowId = getCellStringValue(rowIdCell);
|
|
|
+ if (StringUtil.isNotEmpty(excelRowId)) {
|
|
|
+ currentRowId = excelRowId.trim();
|
|
|
+ } else {
|
|
|
+ // 固定表和动态表都不支持新增行,行ID不能为空
|
|
|
+ // 如果为空则使用行号作为兜底
|
|
|
+ currentRowId = "row_" + rowIndex;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 没有行ID列,使用原有逻辑
|
|
|
+ currentRowId = "1".equals(templateType) ?
|
|
|
+ "row_" + System.currentTimeMillis() : "row_" + rowIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录 rowId 到 Excel 行号的映射(Excel行号从1开始,加上表头行,所以是 rowIndex + 1)
|
|
|
+ rowIdToExcelRowMap.put(currentRowId, rowIndex + 1);
|
|
|
+
|
|
|
+ // 读取父行ID
|
|
|
+ String parentRowId = null;
|
|
|
+ if (parentIdColumnIndex != null) {
|
|
|
+ Cell parentIdCell = dataRow.getCell(parentIdColumnIndex);
|
|
|
+ parentRowId = getCellStringValue(parentIdCell);
|
|
|
+ if (StringUtil.isNotEmpty(parentRowId)) {
|
|
|
+ parentRowId = parentRowId.trim();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
// 读取原有字段数据(包括空值)
|
|
|
for (Map.Entry<Integer, CostSurveyFdTemplateHeaders> entry : columnIndexMap.entrySet()) {
|
|
|
@@ -1317,7 +1371,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (dataList.isEmpty()) {
|
|
|
return CommonResult.<String>error().message("Excel文件中没有有效数据");
|
|
|
}
|
|
|
- verifyImportData(dataList, type, surveyTemplateId);
|
|
|
+ verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
CostProjectTaskMaterial material = costProjectTaskMaterialManager.getById(materialId);
|
|
|
material.setIsUpload("1");
|
|
|
@@ -1371,15 +1425,25 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
return CommonResult.<String>error().message("Excel表头行为空");
|
|
|
}
|
|
|
|
|
|
+ // 识别行ID列和父行ID列
|
|
|
+ Integer rowIdColumnIndex = null;
|
|
|
+ Integer parentIdColumnIndex = null;
|
|
|
Map<Integer, CostVerifyTemplateHeaders> columnIndexMap = new HashMap<>();
|
|
|
for (int i = 0; i < headerRow.getLastCellNum(); i++) {
|
|
|
Cell cell = headerRow.getCell(i);
|
|
|
if (cell != null) {
|
|
|
String cellValue = getCellStringValue(cell);
|
|
|
if (StringUtil.isNotEmpty(cellValue)) {
|
|
|
- CostVerifyTemplateHeaders header = headerNameMap.get(cellValue.trim());
|
|
|
- if (header != null) {
|
|
|
- columnIndexMap.put(i, header);
|
|
|
+ String trimmedValue = cellValue.trim();
|
|
|
+ if ("行ID".equals(trimmedValue)) {
|
|
|
+ rowIdColumnIndex = i;
|
|
|
+ } else if ("父行ID".equals(trimmedValue)) {
|
|
|
+ parentIdColumnIndex = i;
|
|
|
+ } else {
|
|
|
+ CostVerifyTemplateHeaders header = headerNameMap.get(trimmedValue);
|
|
|
+ if (header != null) {
|
|
|
+ columnIndexMap.put(i, header);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1391,13 +1455,43 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 读取数据行
|
|
|
List<CostSurveyTemplateUploadData> dataList = new ArrayList<>();
|
|
|
+ Map<String, Integer> rowIdToExcelRowMap = new HashMap<>(); // rowId -> Excel行号映射
|
|
|
int importRowCount = 0;
|
|
|
|
|
|
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
Row dataRow = sheet.getRow(rowIndex);
|
|
|
if (dataRow == null || isEmptyRow(dataRow)) continue;
|
|
|
|
|
|
- String rowId = "row_" + rowIndex;
|
|
|
+ // 确定当前行的rowId
|
|
|
+ String currentRowId;
|
|
|
+ if (rowIdColumnIndex != null) {
|
|
|
+ // 如果有行ID列,必须使用Excel中的行ID
|
|
|
+ Cell rowIdCell = dataRow.getCell(rowIdColumnIndex);
|
|
|
+ String excelRowId = getCellStringValue(rowIdCell);
|
|
|
+ if (StringUtil.isNotEmpty(excelRowId)) {
|
|
|
+ currentRowId = excelRowId.trim();
|
|
|
+ } else {
|
|
|
+ // 固定表不支持新增行,行ID不能为空
|
|
|
+ // 如果为空则使用行号作为兜底
|
|
|
+ currentRowId = "row_" + rowIndex;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 没有行ID列,使用行号
|
|
|
+ currentRowId = "row_" + rowIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录 rowId 到 Excel 行号的映射(Excel行号从1开始,加上表头行,所以是 rowIndex + 1)
|
|
|
+ rowIdToExcelRowMap.put(currentRowId, rowIndex + 1);
|
|
|
+
|
|
|
+ // 读取父行ID
|
|
|
+ String parentRowId = null;
|
|
|
+ if (parentIdColumnIndex != null) {
|
|
|
+ Cell parentIdCell = dataRow.getCell(parentIdColumnIndex);
|
|
|
+ parentRowId = getCellStringValue(parentIdCell);
|
|
|
+ if (StringUtil.isNotEmpty(parentRowId)) {
|
|
|
+ parentRowId = parentRowId.trim();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
for (Map.Entry<Integer, CostVerifyTemplateHeaders> entry : columnIndexMap.entrySet()) {
|
|
|
Cell cell = dataRow.getCell(entry.getKey());
|
|
|
@@ -1405,7 +1499,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
if (StringUtil.isNotEmpty(cellValue)) {
|
|
|
CostSurveyTemplateUploadData uploadData = createUploadData(
|
|
|
- surveyTemplateId, taskId, uploadId, rowId,
|
|
|
+ surveyTemplateId, taskId, uploadId, currentRowId,
|
|
|
entry.getValue(), cellValue, periodRecordId, type
|
|
|
);
|
|
|
dataList.add(uploadData);
|
|
|
@@ -1417,7 +1511,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (dataList.isEmpty()) {
|
|
|
return CommonResult.<String>error().message("Excel文件没有有效数据");
|
|
|
}
|
|
|
- verifyImportData(dataList, type, surveyTemplateId);
|
|
|
+ verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
// 保存
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
|
|
|
@@ -1436,8 +1530,19 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取友好的行号显示
|
|
|
+ */
|
|
|
+ private String getRowDisplay(String rowid, Map<String, Integer> rowIdToExcelRowMap) {
|
|
|
+ if (rowIdToExcelRowMap == null) {
|
|
|
+ return "行[" + rowid + "]";
|
|
|
+ }
|
|
|
+ Integer excelRow = rowIdToExcelRowMap.get(rowid);
|
|
|
+ return excelRow != null ? "第" + excelRow + "行" : "行[" + rowid + "]";
|
|
|
+ }
|
|
|
+
|
|
|
// 校验逻辑
|
|
|
- private void verifyImportData(List<CostSurveyTemplateUploadData> dataList, String type, String surveyTemplateId) throws Exception {
|
|
|
+ private void verifyImportData(List<CostSurveyTemplateUploadData> dataList, String type, String surveyTemplateId, Map<String, Integer> rowIdToExcelRowMap) throws Exception {
|
|
|
if (dataList == null || dataList.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -1466,7 +1571,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
for (Map.Entry<String, Map<String, String>> rowEntry : rowDataMap.entrySet()) {
|
|
|
String rowid = rowEntry.getKey();
|
|
|
Map<String, String> rowData = rowEntry.getValue();
|
|
|
- validateFieldRulesExcludingPeriods(rowid, rowData, headersList);
|
|
|
+ validateFieldRulesExcludingPeriods(rowid, rowData, headersList, rowIdToExcelRowMap);
|
|
|
}
|
|
|
|
|
|
// 2. 计算公式校验/结构校验(仅针对固定表,动态表不需要)
|
|
|
@@ -1547,7 +1652,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 针对每个年限分别校验(直接抛异常,不需要检查errors)
|
|
|
for (String period : periods) {
|
|
|
- validateRowFormulasForPeriod(rowid, rowData, rowItems, globalCellCodeMap, period, type);
|
|
|
+ validateRowFormulasForPeriod(rowid, rowData, rowItems, globalCellCodeMap, period, type,rowIdToExcelRowMap);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1677,9 +1782,10 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
* 字段级别校验
|
|
|
*/
|
|
|
private void validateFieldRulesExcludingPeriods(String rowid, Map<String, String> rowData,
|
|
|
- List<? extends Object> headersList) {
|
|
|
+ List<? extends Object> headersList, Map<String, Integer> rowIdToExcelRowMap) {
|
|
|
+ String rowDisplay = getRowDisplay(rowid, rowIdToExcelRowMap);
|
|
|
System.out.println("========================================");
|
|
|
- System.out.println("开始校验行[" + rowid + "]的字段(排除年限列)");
|
|
|
+ System.out.println("开始校验" + rowDisplay + "的字段(排除年限列)");
|
|
|
System.out.println("表头数量: " + headersList.size());
|
|
|
System.out.println("行数据: " + rowData);
|
|
|
|
|
|
@@ -1702,7 +1808,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
// 1. 必填校验(支持 "1" 和 "true" 两种格式)
|
|
|
if (("1".equals(isRequired) || "true".equalsIgnoreCase(isRequired)) && StringUtil.isEmpty(value)) {
|
|
|
System.out.println(" ❌ 必填校验失败!");
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]为必填项,不能为空", rowid, fieldEname, fieldName));
|
|
|
+ throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]为必填项,不能为空", rowDisplay, fieldEname, fieldName));
|
|
|
}
|
|
|
|
|
|
// 如果值为空且非必填,跳过后续校验
|
|
|
@@ -1784,7 +1890,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
/**
|
|
|
* 字段类型校验
|
|
|
*/
|
|
|
- private void validateFieldType(String rowid, String fieldEname, String fieldName,
|
|
|
+ private void validateFieldType(String rowDisplay, String fieldEname, String fieldName,
|
|
|
String value, String fieldType) {
|
|
|
try {
|
|
|
switch (fieldType.toLowerCase()) {
|
|
|
@@ -1794,7 +1900,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
try {
|
|
|
Long.parseLong(value);
|
|
|
} catch (NumberFormatException e) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]类型错误,应为整数,实际值:%s", rowid, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]类型错误,应为整数,实际值:%s", rowDisplay, fieldEname, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
@@ -1805,21 +1911,21 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
try {
|
|
|
Double.parseDouble(value);
|
|
|
} catch (NumberFormatException e) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]类型错误,应为数字,实际值:%s", rowid, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]类型错误,应为数字,实际值:%s", rowDisplay, fieldEname, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case "date":
|
|
|
// 日期格式校验(可以根据实际需求调整)
|
|
|
if (!value.matches("\\d{4}-\\d{2}-\\d{2}")) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]日期格式错误,应为yyyy-MM-dd,实际值:%s", rowid, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]日期格式错误,应为yyyy-MM-dd,实际值:%s", rowDisplay, fieldEname, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case "datetime":
|
|
|
// 日期时间格式校验
|
|
|
if (!value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]日期时间格式错误,应为yyyy-MM-dd HH:mm:ss,实际值:%s", rowid, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]日期时间格式错误,应为yyyy-MM-dd HH:mm:ss,实际值:%s", rowDisplay, fieldEname, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
@@ -1830,8 +1936,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (!lowerValue.equals("true") && !lowerValue.equals("false") &&
|
|
|
!lowerValue.equals("1") && !lowerValue.equals("0") &&
|
|
|
!lowerValue.equals("是") && !lowerValue.equals("否")) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]类型错误,应为布尔值(true/false, 1/0, 是/否),实际值:%s",
|
|
|
- rowid, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]类型错误,应为布尔值(true/false, 1/0, 是/否),实际值:%s",
|
|
|
+ rowDisplay, fieldEname, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
@@ -1848,7 +1954,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
throw e;
|
|
|
} catch (Exception e) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]类型校验异常:%s", rowid, fieldEname, fieldName, e.getMessage()));
|
|
|
+ throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]类型校验异常:%s", rowDisplay, fieldEname, fieldName, e.getMessage()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2084,7 +2190,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
*/
|
|
|
private void validateRowFormulasForPeriod(String rowid, Map<String, String> rowData,
|
|
|
List<? extends Object> rowItems, Map<String, String> globalCellCodeMap,
|
|
|
- String period, String type) {
|
|
|
+ String period, String type,Map<String, Integer> rowIdToExcelRowMap) {
|
|
|
// 找到该行的计算公式(同一行的所有模板项共享同一个公式)
|
|
|
String calculationFormula = null;
|
|
|
String cellCode = null;
|
|
|
@@ -2159,8 +2265,11 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (Math.abs(calculatedValue - inputValue) > 0.01) {
|
|
|
System.out.println("校验结果: ❌ 不匹配");
|
|
|
System.out.println("========================================");
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] [%s]列汇总值错误:计算值[%.2f],填写值[%.2f]",
|
|
|
- rowid, period + "年", calculatedValue, inputValue));
|
|
|
+ // 获取友好的行号显示
|
|
|
+ Integer excelRow = rowIdToExcelRowMap.get(rowid);
|
|
|
+ String rowDisplay = excelRow != null ? "第" + excelRow + "行" : "行[" + rowid + "]";
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]列汇总值错误:计算值[%.2f],填写值[%.2f]",
|
|
|
+ rowDisplay, period + "年", calculatedValue, inputValue));
|
|
|
} else {
|
|
|
System.out.println("校验结果: ✓ 通过");
|
|
|
System.out.println("========================================");
|
|
|
@@ -2631,8 +2740,62 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 按父子关系排序rowId(核定表)
|
|
|
+ */
|
|
|
+ private List<String> sortRowIdsByParentChildVerify(Map<String, List<CostVerifyTemplateItems>> itemsByRowId) {
|
|
|
+ List<String> result = new ArrayList<>();
|
|
|
+
|
|
|
+ // 构建rowId到parentid的映射
|
|
|
+ Map<String, String> rowIdToParentId = new HashMap<>();
|
|
|
+ for (Map.Entry<String, List<CostVerifyTemplateItems>> entry : itemsByRowId.entrySet()) {
|
|
|
+ String rowId = entry.getKey();
|
|
|
+ List<CostVerifyTemplateItems> items = entry.getValue();
|
|
|
+ if (!items.isEmpty()) {
|
|
|
+ String parentId = items.get(0).getParentid();
|
|
|
+ rowIdToParentId.put(rowId, parentId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找出所有根节点(parentid为空或不存在的)
|
|
|
+ Set<String> allRowIds = new HashSet<>(itemsByRowId.keySet());
|
|
|
+ List<String> rootRowIds = allRowIds.stream()
|
|
|
+ .filter(rowId -> {
|
|
|
+ String parentId = rowIdToParentId.get(rowId);
|
|
|
+ return StringUtil.isEmpty(parentId) || !allRowIds.contains(parentId);
|
|
|
+ })
|
|
|
+ .sorted()
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 递归添加节点及其子节点
|
|
|
+ for (String rootRowId : rootRowIds) {
|
|
|
+ addRowIdWithChildrenVerify(rootRowId, rowIdToParentId, allRowIds, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归添加rowId及其所有子节点(核定表)
|
|
|
+ */
|
|
|
+ private void addRowIdWithChildrenVerify(String rowId, Map<String, String> rowIdToParentId,
|
|
|
+ Set<String> allRowIds, List<String> result) {
|
|
|
+ result.add(rowId);
|
|
|
+
|
|
|
+ // 找出所有子节点
|
|
|
+ List<String> children = allRowIds.stream()
|
|
|
+ .filter(id -> rowId.equals(rowIdToParentId.get(id)))
|
|
|
+ .sorted()
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 递归处理子节点
|
|
|
+ for (String childRowId : children) {
|
|
|
+ addRowIdWithChildrenVerify(childRowId, rowIdToParentId, allRowIds, result);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private void fillExcelDataVerify(Sheet sheet, List<CostVerifyTemplateItems> itemsList,
|
|
|
- Map<String, Integer> headerIndexMap) {
|
|
|
+ Map<String, Integer> headerIndexMap, int rowIdColIndex, int parentIdColIndex) {
|
|
|
|
|
|
if (itemsList == null || itemsList.isEmpty()) {
|
|
|
return;
|
|
|
@@ -2643,22 +2806,41 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
.filter(item -> StringUtil.isNotEmpty(item.getRowid()))
|
|
|
.collect(Collectors.groupingBy(CostVerifyTemplateItems::getRowid));
|
|
|
|
|
|
+ // 按父子关系排序
|
|
|
+ List<String> sortedRowIds = sortRowIdsByParentChildVerify(itemsByRowId);
|
|
|
+
|
|
|
int rowNum = 1;
|
|
|
int totalCellsWritten = 0;
|
|
|
int cellsSkippedNoMapping = 0;
|
|
|
|
|
|
- for (Map.Entry<String, List<CostVerifyTemplateItems>> entry : itemsByRowId.entrySet()) {
|
|
|
- Row dataRow = sheet.createRow(rowNum++);
|
|
|
- for (CostVerifyTemplateItems item : entry.getValue()) {
|
|
|
- Integer colIndex = headerIndexMap.get(item.getHeadersId());
|
|
|
- if (colIndex != null) {
|
|
|
- String cellValue = item.getRvalue();
|
|
|
- if (StringUtil.isNotEmpty(cellValue)) {
|
|
|
- dataRow.createCell(colIndex).setCellValue(cellValue);
|
|
|
- totalCellsWritten++;
|
|
|
+ for (String rowId : sortedRowIds) {
|
|
|
+ List<CostVerifyTemplateItems> rowItems = itemsByRowId.get(rowId);
|
|
|
+ if (rowItems != null && !rowItems.isEmpty()) {
|
|
|
+ Row dataRow = sheet.createRow(rowNum++);
|
|
|
+
|
|
|
+ // 填充数据列
|
|
|
+ for (CostVerifyTemplateItems item : rowItems) {
|
|
|
+ Integer colIndex = headerIndexMap.get(item.getHeadersId());
|
|
|
+ if (colIndex != null) {
|
|
|
+ String cellValue = item.getRvalue();
|
|
|
+ if (StringUtil.isNotEmpty(cellValue)) {
|
|
|
+ dataRow.createCell(colIndex).setCellValue(cellValue);
|
|
|
+ totalCellsWritten++;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ cellsSkippedNoMapping++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 填充 rowId 和 parentId 列
|
|
|
+ if (rowIdColIndex >= 0) {
|
|
|
+ dataRow.createCell(rowIdColIndex).setCellValue(rowId);
|
|
|
+ }
|
|
|
+ if (parentIdColIndex >= 0) {
|
|
|
+ String parentId = rowItems.get(0).getParentid();
|
|
|
+ if (StringUtil.isNotEmpty(parentId)) {
|
|
|
+ dataRow.createCell(parentIdColIndex).setCellValue(parentId);
|
|
|
}
|
|
|
- } else {
|
|
|
- cellsSkippedNoMapping++;
|
|
|
}
|
|
|
}
|
|
|
}
|