|
|
@@ -8,7 +8,9 @@ import com.hotent.base.constants.ApiGroupConsts;
|
|
|
import com.hotent.base.model.CommonResult;
|
|
|
import com.hotent.base.util.StringUtil;
|
|
|
import com.hotent.baseInfo.manager.CostCatalogSurveyManager;
|
|
|
+import com.hotent.baseInfo.manager.CostDictTypeManager;
|
|
|
import com.hotent.baseInfo.model.CostCatalogSurvey;
|
|
|
+import com.hotent.baseInfo.model.CostDictType;
|
|
|
import com.hotent.enterpriseDeclare.manager.CostAuditPeriodRecordManager;
|
|
|
import com.hotent.enterpriseDeclare.manager.CostSurveyTemplateUploadDataManager;
|
|
|
import com.hotent.enterpriseDeclare.model.CostAuditPeriodRecord;
|
|
|
@@ -23,8 +25,10 @@ import com.hotent.surveyinfo.dao.*;
|
|
|
import com.hotent.surveyinfo.manager.*;
|
|
|
import com.hotent.surveyinfo.model.*;
|
|
|
import com.hotent.surveyinfo.model.dto.CostItemData;
|
|
|
-import com.hotent.baseInfo.manager.CostDictDataManager;
|
|
|
-import com.hotent.baseInfo.model.CostDictData;
|
|
|
+import com.hotent.sys.persistence.manager.DataDictManager;
|
|
|
+import com.hotent.sys.persistence.manager.SysTypeManager;
|
|
|
+import com.hotent.sys.persistence.model.DataDict;
|
|
|
+import com.hotent.sys.persistence.model.SysType;
|
|
|
import io.swagger.annotations.Api;
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
import io.swagger.annotations.ApiParam;
|
|
|
@@ -37,6 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import java.io.InputStream;
|
|
|
import java.net.URLEncoder;
|
|
|
@@ -69,6 +74,11 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
@Autowired
|
|
|
private CostSurveyTemplateItemsDao costSurveyTemplateItemsDao;
|
|
|
+ @Resource
|
|
|
+ DataDictManager dataDictManager;
|
|
|
+ @Resource
|
|
|
+ SysTypeManager sysTypeManager;
|
|
|
+
|
|
|
|
|
|
|
|
|
// 财务数据表相关
|
|
|
@@ -119,8 +129,6 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
@Autowired
|
|
|
private CostSurveyFdTemplateHeadersManager costSurveyFdTemplateHeadersManager;
|
|
|
|
|
|
- @Autowired
|
|
|
- private CostDictDataManager costDictDataManager;
|
|
|
|
|
|
/**
|
|
|
* 企业报送-调查表-列表
|
|
|
@@ -148,7 +156,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
//创建记录
|
|
|
if (uploadList.isEmpty()) {
|
|
|
CostProjectTask task = costProjectTaskManager.getById(taskId);
|
|
|
- List<CostSurveyTemplate> costSurveyTemplates = costSurveyTemplateManager.taskListByCatalogId(task.getCatalogId());
|
|
|
+ CostProjectApproval approval = costProjectApprovalManager.getById(task.getProjectId());
|
|
|
+ List<CostSurveyTemplate> costSurveyTemplates = costSurveyTemplateManager.taskListByCatalogId(approval.getCatalogId());
|
|
|
for (CostSurveyTemplate template : costSurveyTemplates) {
|
|
|
CostSurveyTemplateUpload upload = new CostSurveyTemplateUpload();
|
|
|
upload.setSurveyTemplateId(template.getSurveyTemplateId());
|
|
|
@@ -201,7 +210,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
List<CostSurveyTemplateItems> items = costSurveyTemplateItemsDao.selectBySurveyTemplateIdAndVersion(surveyTemplateId, versionTemplate.getId());
|
|
|
- // 使用已有的manager方法替代新接口
|
|
|
+
|
|
|
List<CostSurveyTemplateHeaders> headList = costSurveyTemplateHeadersManager.listVisibleBySurveyTemplateIdAndVersion(surveyTemplateId, versionTemplate.getId());
|
|
|
CostItemData costItemData = buildCostItemData(items, headList);
|
|
|
return CommonResult.<CostItemData>ok().value(costItemData);
|
|
|
@@ -214,7 +223,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
List<CostSurveyFdTemplateItems> items = costSurveyFdTemplateItemsDao.selectBySurveyTemplateIdAndVersion(surveyTemplateId, versionTemplate.getId());
|
|
|
- // 使用已有的manager方法替代新接口
|
|
|
+
|
|
|
List<CostSurveyFdTemplateHeaders> headList = costSurveyFdTemplateHeadersManager.listVisibleBySurveyTemplateIdAndVersion(surveyTemplateId, versionTemplate.getId());
|
|
|
CostItemData costItemData = buildCostItemDataFd(items, headList);
|
|
|
return CommonResult.<CostItemData>ok().value(costItemData);
|
|
|
@@ -291,14 +300,13 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 设置 type
|
|
|
for (CostSurveyTemplateUploadData data : dataList) {
|
|
|
data.setType(type);
|
|
|
}
|
|
|
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
|
|
|
- // 只有成本调查表需要更新upload状态dataList = {ArrayList@29238} size = 27
|
|
|
+ // 更新成本调查表
|
|
|
if ("1".equals(type) && refId != null && !refId.isEmpty()) {
|
|
|
CostSurveyTemplateUpload upload = costSurveyTemplateUploadManager.getById(refId);
|
|
|
if (upload != null) {
|
|
|
@@ -1061,7 +1069,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
List<CostSurveyTemplateUploadData> dataList = new ArrayList<>();
|
|
|
- Map<String, Integer> rowIdToExcelRowMap = new HashMap<>(); // rowId -> Excel行号映射
|
|
|
+ Map<String, Integer> rowIdToExcelRowMap = new HashMap<>();
|
|
|
int dataRowCount = 0;
|
|
|
|
|
|
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
@@ -1078,16 +1086,12 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
currentRowId = excelRowId.trim();
|
|
|
} else {
|
|
|
// 固定表和动态表都不支持新增行,行ID不能为空
|
|
|
- // 如果为空则使用行号作为兜底
|
|
|
- currentRowId = "row_" + rowIndex;
|
|
|
+ return CommonResult.<String>error().message(String.format("第%d行 行ID不能为空,请使用系统导出的模板文件", rowIndex + 1));
|
|
|
}
|
|
|
} else {
|
|
|
- // 没有行ID列,使用原有逻辑
|
|
|
- currentRowId = "1".equals(templateType) ?
|
|
|
- "row_" + System.currentTimeMillis() : "row_" + rowIndex;
|
|
|
+ return CommonResult.<String>error().message("固定表/动态表导入失败:Excel文件缺少【行ID】列。请使用系统导出的模板,不要删除隐藏列。");
|
|
|
}
|
|
|
|
|
|
- // 记录 rowId 到 Excel 行号的映射(Excel行号从1开始,加上表头行,所以是 rowIndex + 1)
|
|
|
rowIdToExcelRowMap.put(currentRowId, rowIndex + 1);
|
|
|
|
|
|
// 读取父行ID
|
|
|
@@ -1167,11 +1171,14 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
return CommonResult.<String>error().message("Excel文件中没有有效数据");
|
|
|
}
|
|
|
|
|
|
- verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
+ List<String> errors = verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
+ if (!errors.isEmpty()) {
|
|
|
+ return CommonResult.<String>error().message("导入失败,发现以下问题:\n" + String.join("\n", errors));
|
|
|
+ }
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
|
|
|
// 更新上传状态
|
|
|
- CostSurveyTemplateUpload upload = costSurveyTemplateUploadManager.getById(uploadId);
|
|
|
+ CostSurveyTemplateUpload upload = costSurveyTemplateUploadManager.getById(materialId);
|
|
|
if (upload != null) {
|
|
|
upload.setIsUpload("1");
|
|
|
costSurveyTemplateUploadManager.updateById(upload);
|
|
|
@@ -1199,7 +1206,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
return CommonResult.<String>error().message("未找到启用的模板版本");
|
|
|
}
|
|
|
|
|
|
- // 使用已有的manager方法替代新接口
|
|
|
+
|
|
|
List<CostSurveyFdTemplateHeaders> headersList = costSurveyFdTemplateHeadersManager.listVisibleBySurveyTemplateIdAndVersion(
|
|
|
surveyTemplateId, currentVersion.getId()).stream()
|
|
|
.filter(header -> StringUtil.isEmpty(header.getShowVisible()) || "1".equals(header.getShowVisible()))
|
|
|
@@ -1267,7 +1274,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
List<CostSurveyTemplateUploadData> dataList = new ArrayList<>();
|
|
|
- Map<String, Integer> rowIdToExcelRowMap = new HashMap<>(); // rowId -> Excel行号映射
|
|
|
+ Map<String, Integer> rowIdToExcelRowMap = new HashMap<>();
|
|
|
int dataRowCount = 0;
|
|
|
|
|
|
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
@@ -1284,13 +1291,10 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
currentRowId = excelRowId.trim();
|
|
|
} else {
|
|
|
// 固定表和动态表都不支持新增行,行ID不能为空
|
|
|
- // 如果为空则使用行号作为兜底
|
|
|
- currentRowId = "row_" + rowIndex;
|
|
|
+ return CommonResult.<String>error().message(String.format("第%d行 行ID不能为空,请使用系统导出的模板文件", rowIndex + 1));
|
|
|
}
|
|
|
} else {
|
|
|
- // 没有行ID列,使用原有逻辑
|
|
|
- currentRowId = "1".equals(templateType) ?
|
|
|
- "row_" + System.currentTimeMillis() : "row_" + rowIndex;
|
|
|
+ return CommonResult.<String>error().message("固定表/动态表导入失败:Excel文件缺少【行ID】列。请使用系统导出的模板,不要删除隐藏列。");
|
|
|
}
|
|
|
|
|
|
// 记录 rowId 到 Excel 行号的映射(Excel行号从1开始,加上表头行,所以是 rowIndex + 1)
|
|
|
@@ -1315,6 +1319,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
CostSurveyTemplateUploadData uploadData = createUploadData(
|
|
|
surveyTemplateId, taskId, uploadId, currentRowId,
|
|
|
entry.getValue(), cellValue != null ? cellValue : "", periodRecordId, type);
|
|
|
+ uploadData.setUploadId(uploadId);
|
|
|
+ uploadData.setRefId(refId);
|
|
|
dataList.add(uploadData);
|
|
|
}
|
|
|
|
|
|
@@ -1333,7 +1339,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
periodData.setUploadId(uploadId);
|
|
|
periodData.setType(type);
|
|
|
periodData.setRowid(currentRowId);
|
|
|
- periodData.setRkey(period); // 年限,如:2024
|
|
|
+ periodData.setRkey(period);
|
|
|
periodData.setRvalue(cellValue != null ? cellValue.trim() : "");
|
|
|
if (StringUtil.isNotEmpty(periodRecordId)) {
|
|
|
periodData.setPeriodRecordId(periodRecordId);
|
|
|
@@ -1371,7 +1377,10 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (dataList.isEmpty()) {
|
|
|
return CommonResult.<String>error().message("Excel文件中没有有效数据");
|
|
|
}
|
|
|
- verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
+ List<String> errors = verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
+ if (!errors.isEmpty()) {
|
|
|
+ return CommonResult.<String>error().message("导入失败,发现以下问题:\n" + String.join("\n", errors));
|
|
|
+ }
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
CostProjectTaskMaterial material = costProjectTaskMaterialManager.getById(materialId);
|
|
|
material.setIsUpload("1");
|
|
|
@@ -1455,7 +1464,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 读取数据行
|
|
|
List<CostSurveyTemplateUploadData> dataList = new ArrayList<>();
|
|
|
- Map<String, Integer> rowIdToExcelRowMap = new HashMap<>(); // rowId -> Excel行号映射
|
|
|
+ Map<String, Integer> rowIdToExcelRowMap = new HashMap<>();
|
|
|
int importRowCount = 0;
|
|
|
|
|
|
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
@@ -1471,13 +1480,12 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (StringUtil.isNotEmpty(excelRowId)) {
|
|
|
currentRowId = excelRowId.trim();
|
|
|
} else {
|
|
|
- // 固定表不支持新增行,行ID不能为空
|
|
|
- // 如果为空则使用行号作为兜底
|
|
|
- currentRowId = "row_" + rowIndex;
|
|
|
+ // 核定表不支持新增行,行ID不能为空
|
|
|
+ return CommonResult.<String>error().message(String.format("第%d行 行ID不能为空,请使用系统导出的模板文件", rowIndex + 1));
|
|
|
}
|
|
|
} else {
|
|
|
- // 没有行ID列,使用行号
|
|
|
- currentRowId = "row_" + rowIndex;
|
|
|
+ // 核定表必须有行ID列
|
|
|
+ return CommonResult.<String>error().message("核定表导入失败:Excel文件缺少【行ID】列。请使用系统导出的模板文件。");
|
|
|
}
|
|
|
|
|
|
// 记录 rowId 到 Excel 行号的映射(Excel行号从1开始,加上表头行,所以是 rowIndex + 1)
|
|
|
@@ -1511,7 +1519,10 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (dataList.isEmpty()) {
|
|
|
return CommonResult.<String>error().message("Excel文件没有有效数据");
|
|
|
}
|
|
|
- verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
+ List<String> errors = verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
+ if (!errors.isEmpty()) {
|
|
|
+ return CommonResult.<String>error().message("导入失败,发现以下问题:\n" + String.join("\n", errors));
|
|
|
+ }
|
|
|
// 保存
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
|
|
|
@@ -1542,9 +1553,10 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
// 校验逻辑
|
|
|
- private void verifyImportData(List<CostSurveyTemplateUploadData> dataList, String type, String surveyTemplateId, Map<String, Integer> rowIdToExcelRowMap) throws Exception {
|
|
|
+ private List<String> verifyImportData(List<CostSurveyTemplateUploadData> dataList, String type, String surveyTemplateId, Map<String, Integer> rowIdToExcelRowMap) {
|
|
|
+ List<String> errors = new ArrayList<>();
|
|
|
if (dataList == null || dataList.isEmpty()) {
|
|
|
- return;
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
// 获取模板类型(templateType)
|
|
|
@@ -1563,7 +1575,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
// 获取表头信息(用于字段校验)
|
|
|
List<? extends Object> headersList = getTemplateHeaders(type, surveyTemplateId);
|
|
|
if (headersList == null || headersList.isEmpty()) {
|
|
|
- return;
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
// 1. 字段级别校验(必填、类型、长度等)
|
|
|
@@ -1571,14 +1583,16 @@ 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, rowIdToExcelRowMap);
|
|
|
+ List<String> fieldErrors = validateFieldRulesExcludingPeriods(rowid, rowData, headersList, rowIdToExcelRowMap);
|
|
|
+ errors.addAll(fieldErrors);
|
|
|
}
|
|
|
|
|
|
// 2. 计算公式校验/结构校验(仅针对固定表,动态表不需要)
|
|
|
- if ("2".equals(templateType)) {
|
|
|
+ if ("2".equals(templateType)) {
|
|
|
System.out.println("========================================");
|
|
|
System.out.println("固定表校验:开始结构校验和公式校验");
|
|
|
- validateFixedTableStructure(rowDataMap, headersList, surveyTemplateId, type);
|
|
|
+ List<String> structureErrors = validateFixedTableStructure(rowDataMap, headersList, surveyTemplateId, type);
|
|
|
+ errors.addAll(structureErrors);
|
|
|
|
|
|
// 根据类型获取模板项
|
|
|
List<? extends Object> itemsList = getTemplateItems(type, surveyTemplateId);
|
|
|
@@ -1611,8 +1625,9 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
String cellCode = rowidToCellCodeMap.get(rowid);
|
|
|
|
|
|
if (StringUtil.isEmpty(cellCode)) {
|
|
|
- // rowid 不匹配说明导入的Excel不是从当前模板导出的,直接报错
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 的行ID在模板中不存在,请使用系统导出的最新模板文件", rowid));
|
|
|
+ // rowid 不匹配说明导入的Excel不是从当前模板导出的,添加到错误列表
|
|
|
+ errors.add(String.format("行[%s] 的行ID在模板中不存在,请使用系统导出的最新模板文件", rowid));
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
// 处理年限列的数据(key是4位数字,如"2024")
|
|
|
@@ -1650,25 +1665,29 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- // 针对每个年限分别校验(直接抛异常,不需要检查errors)
|
|
|
+ // 针对每个年限分别校验(收集错误)
|
|
|
for (String period : periods) {
|
|
|
- validateRowFormulasForPeriod(rowid, rowData, rowItems, globalCellCodeMap, period, type,rowIdToExcelRowMap);
|
|
|
+ List<String> formulaErrors = validateRowFormulasForPeriod(rowid, rowData, rowItems, globalCellCodeMap, period, type, rowIdToExcelRowMap);
|
|
|
+ errors.addAll(formulaErrors);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 固定表结构校验:行列数量必须匹配
|
|
|
*/
|
|
|
- private void validateFixedTableStructure(Map<String, Map<String, String>> rowDataMap,
|
|
|
+ private List<String> validateFixedTableStructure(Map<String, Map<String, String>> rowDataMap,
|
|
|
List<? extends Object> headersList,
|
|
|
String surveyTemplateId, String type) {
|
|
|
+ List<String> errors = new ArrayList<>();
|
|
|
// 1. 获取模板定义的行数
|
|
|
List<? extends Object> templateItems = getTemplateItems(type, surveyTemplateId);
|
|
|
if (templateItems == null || templateItems.isEmpty()) {
|
|
|
- throw new IllegalArgumentException("固定表模板未定义任何数据行,无法导入");
|
|
|
+ errors.add("固定表模板未定义任何数据行,无法导入");
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
// 按 rowid 分组模板项,得到模板定义的行数
|
|
|
@@ -1682,7 +1701,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 2. 校验行数是否匹配
|
|
|
if (actualRowCount != expectedRowCount) {
|
|
|
- throw new IllegalArgumentException(String.format("固定表行数不匹配!模板定义:%d 行,导入数据:%d 行。固定表不允许增加或减少行。",
|
|
|
+ errors.add(String.format("固定表行数不匹配!模板定义:%d 行,导入数据:%d 行。固定表不允许增加或减少行。",
|
|
|
expectedRowCount, actualRowCount));
|
|
|
}
|
|
|
|
|
|
@@ -1703,7 +1722,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
// 移除年限列(4位数字的字段,如"2024")和备注列(remark)
|
|
|
extraFields.removeIf(field -> field.matches("\\d{4}") || "remark".equals(field));
|
|
|
if (!extraFields.isEmpty()) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 包含模板中未定义的字段:%s。固定表不允许添加额外字段。",
|
|
|
+ errors.add(String.format("行[%s] 包含模板中未定义的字段:%s。固定表不允许添加额外字段。",
|
|
|
rowid, String.join(", ", extraFields)));
|
|
|
}
|
|
|
|
|
|
@@ -1715,11 +1734,12 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 如果是必填字段但导入数据中没有这个字段
|
|
|
if ("1".equals(isRequired) && !actualFields.contains(fieldEname)) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 缺少必填字段:%s(%s)。固定表必须包含所有必填字段。",
|
|
|
+ errors.add(String.format("行[%s] 缺少必填字段:%s(%s)。固定表必须包含所有必填字段。",
|
|
|
rowid, fieldEname, fieldName));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -1781,8 +1801,9 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
/**
|
|
|
* 字段级别校验
|
|
|
*/
|
|
|
- private void validateFieldRulesExcludingPeriods(String rowid, Map<String, String> rowData,
|
|
|
+ private List<String> validateFieldRulesExcludingPeriods(String rowid, Map<String, String> rowData,
|
|
|
List<? extends Object> headersList, Map<String, Integer> rowIdToExcelRowMap) {
|
|
|
+ List<String> errors = new ArrayList<>();
|
|
|
String rowDisplay = getRowDisplay(rowid, rowIdToExcelRowMap);
|
|
|
System.out.println("========================================");
|
|
|
System.out.println("开始校验" + rowDisplay + "的字段(排除年限列)");
|
|
|
@@ -1807,8 +1828,8 @@ 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)]为必填项,不能为空", rowDisplay, fieldEname, fieldName));
|
|
|
+ errors.add(String.format("%s [%s]为必填项,不能为空", rowDisplay, fieldName));
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
// 如果值为空且非必填,跳过后续校验
|
|
|
@@ -1818,22 +1839,33 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 2. 字典校验(支持 "1" 和 "true" 两种格式)
|
|
|
if (("1".equals(isDict) || "true".equalsIgnoreCase(isDict)) && StringUtil.isNotEmpty(dictCode)) {
|
|
|
- validateDictValue(rowid, fieldEname, fieldName, value, dictCode);
|
|
|
+ try {
|
|
|
+ validateDictValue(rowid, fieldEname, fieldName, value, dictCode);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ errors.add(e.getMessage());
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 3. 字段类型校验
|
|
|
if (StringUtil.isNotEmpty(fieldType)) {
|
|
|
- validateFieldType(rowid, fieldEname, fieldName, value, fieldType);
|
|
|
+ try {
|
|
|
+ validateFieldType(rowDisplay, fieldEname, fieldName, value, fieldType);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ errors.add(e.getMessage());
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 4. 字段长度校验
|
|
|
if (fieldTypelen != null && fieldTypelen > 0) {
|
|
|
- validateFieldLength(rowid, fieldEname, fieldName, value, fieldType, fieldTypelen, fieldTypenointlen);
|
|
|
+ try {
|
|
|
+ validateFieldLength(rowid, fieldEname, fieldName, value, fieldType, fieldTypelen, fieldTypenointlen);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ errors.add(e.getMessage());
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 注意:年限列(如2024、2025)和备注列(remark)不在headersList中,
|
|
|
- // 它们存储在rowData中但不会被上面的循环处理,因此自动被排除
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -1847,20 +1879,23 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
try {
|
|
|
// 查询字典数据
|
|
|
- QueryWrapper<CostDictData> wrapper = new QueryWrapper<>();
|
|
|
- wrapper.eq("dict_type", dictCode)
|
|
|
- .eq("status", "0"); // 只查询正常状态的字典项
|
|
|
- List<CostDictData> dictDataList = costDictDataManager.list(wrapper);
|
|
|
+ SysType TYPE = sysTypeManager.getOne(
|
|
|
+ new QueryWrapper<SysType>()
|
|
|
+ .eq("TYPE_KEY_", dictCode)
|
|
|
+ );
|
|
|
+ QueryWrapper<DataDict> wrapper = new QueryWrapper<>();
|
|
|
+ wrapper.eq("TYPE_ID_", TYPE.getId());
|
|
|
+ List<DataDict> dictDataList = dataDictManager.list(wrapper);
|
|
|
|
|
|
if (dictDataList == null || dictDataList.isEmpty()) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]的字典配置[%s]不存在或未配置字典项",
|
|
|
- rowid, fieldEname, fieldName, dictCode));
|
|
|
+ throw new IllegalArgumentException(String.format("行[%s] [%s]的选项配置异常",
|
|
|
+ rowid, fieldName));
|
|
|
}
|
|
|
|
|
|
// 检查值是否在字典允许的范围内(支持多选,用逗号分隔)
|
|
|
String[] values = value.split(",");
|
|
|
Set<String> validValues = dictDataList.stream()
|
|
|
- .map(CostDictData::getDictValue)
|
|
|
+ .map(DataDict::getKey)
|
|
|
.filter(StringUtil::isNotEmpty)
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
|
@@ -1873,17 +1908,14 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
if (!invalidValues.isEmpty()) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]的值[%s]不在字典[%s]允许的范围内,有效值:%s",
|
|
|
- rowid, fieldEname, fieldName,
|
|
|
- String.join(", ", invalidValues),
|
|
|
- dictCode,
|
|
|
- String.join(", ", validValues)));
|
|
|
+ throw new IllegalArgumentException(String.format("行[%s] [%s]的值[%s]不在允许的选项范围内",
|
|
|
+ rowid, fieldName, String.join(", ", invalidValues)));
|
|
|
}
|
|
|
} 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",
|
|
|
+ rowid, fieldName, e.getMessage()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1900,32 +1932,32 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
try {
|
|
|
Long.parseLong(value);
|
|
|
} catch (NumberFormatException e) {
|
|
|
- throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]类型错误,应为整数,实际值:%s", rowDisplay, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]应为整数,实际值:%s", rowDisplay, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case "decimal":
|
|
|
case "double":
|
|
|
- case "float":
|
|
|
+ case "zi":
|
|
|
case "number":
|
|
|
try {
|
|
|
Double.parseDouble(value);
|
|
|
} catch (NumberFormatException e) {
|
|
|
- throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]类型错误,应为数字,实际值:%s", rowDisplay, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]应为数字,实际值:%s", rowDisplay, 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", rowDisplay, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]日期格式应为yyyy-MM-dd,实际值:%s", rowDisplay, 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", rowDisplay, fieldEname, fieldName, value));
|
|
|
+ // 日期时间格式校验 - 支持两种格式
|
|
|
+ if (!value.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}") && !value.matches("\\d{4}-\\d{2}-\\d{2}")) {
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]日期格式应为yyyy-MM-dd或yyyy-MM-dd HH:mm:ss,实际值:%s", rowDisplay, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
@@ -1936,8 +1968,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",
|
|
|
- rowDisplay, fieldEname, fieldName, value));
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]应为布尔值(true/false, 1/0, 是/否),实际值:%s",
|
|
|
+ rowDisplay, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
@@ -1946,7 +1978,6 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
case "text":
|
|
|
// 字符串类型,无需特殊校验
|
|
|
break;
|
|
|
-
|
|
|
default:
|
|
|
// 未知类型,不校验
|
|
|
break;
|
|
|
@@ -1954,7 +1985,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
throw e;
|
|
|
} catch (Exception e) {
|
|
|
- throw new IllegalArgumentException(String.format("%s 字段[%s(%s)]类型校验异常:%s", rowDisplay, fieldEname, fieldName, e.getMessage()));
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]格式校验异常:%s", rowDisplay, fieldName, e.getMessage()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1970,8 +2001,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
case "text":
|
|
|
// 字符串长度校验
|
|
|
if (value.length() > fieldTypelen) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]长度超限,最大长度:%d,实际长度:%d",
|
|
|
- rowid, fieldEname, fieldName, fieldTypelen, value.length()));
|
|
|
+ throw new IllegalArgumentException(String.format("行[%s] [%s]长度超限,最大长度:%d,实际长度:%d",
|
|
|
+ rowid, fieldName, fieldTypelen, value.length()));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
@@ -2188,9 +2219,10 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
/**
|
|
|
* 校验一行数据的计算公式(针对特定年限)
|
|
|
*/
|
|
|
- private void validateRowFormulasForPeriod(String rowid, Map<String, String> rowData,
|
|
|
+ private List<String> validateRowFormulasForPeriod(String rowid, Map<String, String> rowData,
|
|
|
List<? extends Object> rowItems, Map<String, String> globalCellCodeMap,
|
|
|
- String period, String type,Map<String, Integer> rowIdToExcelRowMap) {
|
|
|
+ String period, String type, Map<String, Integer> rowIdToExcelRowMap) {
|
|
|
+ List<String> errors = new ArrayList<>();
|
|
|
// 找到该行的计算公式(同一行的所有模板项共享同一个公式)
|
|
|
String calculationFormula = null;
|
|
|
String cellCode = null;
|
|
|
@@ -2205,7 +2237,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 如果该行没有公式,跳过
|
|
|
if (StringUtil.isEmpty(calculationFormula)) {
|
|
|
- return;
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
// 检查公式引用的单元格在当前年限是否有任何一个有值
|
|
|
@@ -2216,7 +2248,6 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
List<String> referencedCellsDebug = new ArrayList<>();
|
|
|
while (matcher.find()) {
|
|
|
String referencedCell = matcher.group();
|
|
|
- // 构建 cellCode_年限 的key(如 A1_2024)
|
|
|
String mapKey = referencedCell + "_" + period;
|
|
|
String value = globalCellCodeMap.get(mapKey);
|
|
|
String displayValue = StringUtil.isNotEmpty(value) ? value : "0";
|
|
|
@@ -2228,7 +2259,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 如果公式引用的单元格在当前年限都没有值,跳过校验
|
|
|
if (!hasAnyReferencedValue) {
|
|
|
- return;
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
// 获取用户输入的汇总值(当前年限)
|
|
|
@@ -2263,30 +2294,43 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 比较计算值和输入值(允许小数点后2位的误差)
|
|
|
if (Math.abs(calculatedValue - inputValue) > 0.01) {
|
|
|
- System.out.println("校验结果: ❌ 不匹配");
|
|
|
+ System.out.println("校验结果: 不匹配");
|
|
|
System.out.println("========================================");
|
|
|
// 获取友好的行号显示
|
|
|
Integer excelRow = rowIdToExcelRowMap.get(rowid);
|
|
|
String rowDisplay = excelRow != null ? "第" + excelRow + "行" : "行[" + rowid + "]";
|
|
|
- throw new IllegalArgumentException(String.format("%s [%s]列汇总值错误:计算值[%.2f],填写值[%.2f]",
|
|
|
- rowDisplay, period + "年", calculatedValue, inputValue));
|
|
|
+ errors.add(String.format("%s %s年列数据错误:计算结果应为%.2f,实际填写%.2f",
|
|
|
+ rowDisplay, period, calculatedValue, inputValue));
|
|
|
} else {
|
|
|
System.out.println("校验结果: ✓ 通过");
|
|
|
System.out.println("========================================");
|
|
|
}
|
|
|
- } catch (IllegalArgumentException e) {
|
|
|
- // 重新抛出校验错误
|
|
|
- throw e;
|
|
|
} catch (Exception e) {
|
|
|
+ // 统一处理计算错误,提供更详细但不重复的错误信息
|
|
|
System.out.println("========================================");
|
|
|
System.out.println("行[" + rowid + "] [" + period + "年]列公式校验");
|
|
|
System.out.println("公式: " + calculationFormula);
|
|
|
System.out.println("引用值: " + String.join(", ", referencedCellsDebug));
|
|
|
- System.out.println("校验结果: ❌ 计算错误 - " + e.getMessage());
|
|
|
+ System.out.println("校验结果: 计算错误 - " + e.getMessage());
|
|
|
System.out.println("========================================");
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] [%s]列计算错误:%s",
|
|
|
- rowid, period + "年", e.getMessage()));
|
|
|
+
|
|
|
+ // 获取友好的行号显示
|
|
|
+ Integer excelRow = rowIdToExcelRowMap.get(rowid);
|
|
|
+ String rowDisplay = excelRow != null ? "第" + excelRow + "行" : "行[" + rowid + "]";
|
|
|
+
|
|
|
+ // 简化错误信息,只显示关键信息
|
|
|
+ String simpleError;
|
|
|
+ if (e.getMessage().contains("除以零") || e.getMessage().contains("无穷大")) {
|
|
|
+ simpleError = "除数为0,无法计算";
|
|
|
+ } else if (e.getMessage().contains("溢出")) {
|
|
|
+ simpleError = "数值过大,计算溢出";
|
|
|
+ } else {
|
|
|
+ simpleError = "公式计算错误";
|
|
|
+ }
|
|
|
+
|
|
|
+ errors.add(String.format("%s %s年列计算错误:%s", rowDisplay, period, simpleError));
|
|
|
}
|
|
|
+ return errors;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -2851,12 +2895,30 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
private String getCellStringValue(Cell cell) {
|
|
|
if (cell == null) return "";
|
|
|
- switch (cell.getCellType()) {
|
|
|
- case STRING:
|
|
|
- return cell.getStringCellValue();
|
|
|
+
|
|
|
+ try {
|
|
|
+ switch (cell.getCellType()) {
|
|
|
+ case STRING:
|
|
|
+ return cell.getStringCellValue();
|
|
|
case NUMERIC:
|
|
|
if (DateUtil.isCellDateFormatted(cell)) {
|
|
|
- return cell.getDateCellValue().toString();
|
|
|
+ // 格式化日期,根据单元格的格式判断是日期还是日期时间
|
|
|
+ java.util.Date dateValue = cell.getDateCellValue();
|
|
|
+ java.text.SimpleDateFormat dateFormat;
|
|
|
+
|
|
|
+ // 检查是否包含时间信息(时分秒不全为0)
|
|
|
+ java.util.Calendar cal = java.util.Calendar.getInstance();
|
|
|
+ cal.setTime(dateValue);
|
|
|
+ boolean hasTime = cal.get(java.util.Calendar.HOUR_OF_DAY) != 0 ||
|
|
|
+ cal.get(java.util.Calendar.MINUTE) != 0 ||
|
|
|
+ cal.get(java.util.Calendar.SECOND) != 0;
|
|
|
+
|
|
|
+ if (hasTime) {
|
|
|
+ dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ } else {
|
|
|
+ dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ }
|
|
|
+ return dateFormat.format(dateValue);
|
|
|
} else {
|
|
|
double numericValue = cell.getNumericCellValue();
|
|
|
return numericValue == (long) numericValue ?
|
|
|
@@ -2866,16 +2928,48 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
return String.valueOf(cell.getBooleanCellValue());
|
|
|
case FORMULA:
|
|
|
try {
|
|
|
- return cell.getStringCellValue();
|
|
|
- } catch (Exception e) {
|
|
|
- try {
|
|
|
- return String.valueOf(cell.getNumericCellValue());
|
|
|
- } catch (Exception ex) {
|
|
|
- return "";
|
|
|
+ // 先尝试获取公式计算后的值类型
|
|
|
+ switch (cell.getCachedFormulaResultType()) {
|
|
|
+ case STRING:
|
|
|
+ return cell.getStringCellValue();
|
|
|
+ case NUMERIC:
|
|
|
+ if (DateUtil.isCellDateFormatted(cell)) {
|
|
|
+ // 处理公式结果为日期的情况
|
|
|
+ java.util.Date dateValue = cell.getDateCellValue();
|
|
|
+ java.text.SimpleDateFormat dateFormat;
|
|
|
+ java.util.Calendar cal = java.util.Calendar.getInstance();
|
|
|
+ cal.setTime(dateValue);
|
|
|
+ boolean hasTime = cal.get(java.util.Calendar.HOUR_OF_DAY) != 0 ||
|
|
|
+ cal.get(java.util.Calendar.MINUTE) != 0 ||
|
|
|
+ cal.get(java.util.Calendar.SECOND) != 0;
|
|
|
+ if (hasTime) {
|
|
|
+ dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ } else {
|
|
|
+ dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ }
|
|
|
+ return dateFormat.format(dateValue);
|
|
|
+ } else {
|
|
|
+ double numericValue = cell.getNumericCellValue();
|
|
|
+ return numericValue == (long) numericValue ?
|
|
|
+ String.valueOf((long) numericValue) : String.valueOf(numericValue);
|
|
|
+ }
|
|
|
+ case BOOLEAN:
|
|
|
+ return String.valueOf(cell.getBooleanCellValue());
|
|
|
+ default:
|
|
|
+ return "";
|
|
|
}
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 如果无法获取公式计算结果,返回空字符串
|
|
|
+ return "";
|
|
|
}
|
|
|
- default:
|
|
|
- return "";
|
|
|
+ default:
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 如果读取单元格时发生任何异常,返回空字符串并记录错误
|
|
|
+ System.err.println("Error reading cell at row " + cell.getRowIndex() +
|
|
|
+ ", column " + cell.getColumnIndex() + ": " + e.getMessage());
|
|
|
+ return "";
|
|
|
}
|
|
|
}
|
|
|
|