|
|
@@ -172,13 +172,22 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
costSurveyTemplateUploadManager.save(upload);
|
|
|
}
|
|
|
}
|
|
|
- return CommonResult.ok().value(costSurveyTemplateUploadManager.listByTaskId(taskId));
|
|
|
+ List<CostSurveyTemplateUpload> costSurveyTemplateUploads = costSurveyTemplateUploadManager.listByTaskId(taskId);
|
|
|
+ for (CostSurveyTemplateUpload costSurveyTemplateUpload : costSurveyTemplateUploads) {
|
|
|
+ CostProjectTask task = costProjectTaskManager.getById(taskId);
|
|
|
+ costSurveyTemplateUpload.setAuditedUnitId(task.getAuditedUnitId());
|
|
|
+ }
|
|
|
+ return CommonResult.ok().value(costSurveyTemplateUploads);
|
|
|
}
|
|
|
case "2": {
|
|
|
// 财务数据表逻辑
|
|
|
QueryWrapper<CostProjectTaskMaterial> wrapper = new QueryWrapper<>();
|
|
|
wrapper.eq("task_id", taskId);
|
|
|
List<CostProjectTaskMaterial> materialList = costProjectTaskMaterialManager.list(wrapper);
|
|
|
+ for (CostProjectTaskMaterial costProjectTaskMaterial : materialList) {
|
|
|
+ CostProjectTask task = costProjectTaskManager.getById(taskId);
|
|
|
+ costProjectTaskMaterial.setAuditedUnitId(task.getAuditedUnitId()); ;
|
|
|
+ }
|
|
|
return CommonResult.ok().value(materialList);
|
|
|
}
|
|
|
default:
|
|
|
@@ -358,7 +367,6 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
wrapper.eq("period_record_id", queryData.getPeriodRecordId());
|
|
|
}
|
|
|
|
|
|
- wrapper.orderByAsc("rowid", "rkey");
|
|
|
List<CostSurveyTemplateUploadData> dataList = costSurveyTemplateUploadDataManager.list(wrapper);
|
|
|
for (CostSurveyTemplateUploadData costSurveyTemplateUploadData : dataList) {
|
|
|
costSurveyTemplateUploadData.setUploadId(costSurveyTemplateUploadData.getRefId());
|
|
|
@@ -600,8 +608,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
surveyTemplateId, currentVersion.getId());
|
|
|
|
|
|
Workbook workbook = new XSSFWorkbook();
|
|
|
- String sheetName = currentVersion.getSurveyTemplateName() != null ?
|
|
|
- currentVersion.getSurveyTemplateName() : "成本调查表";
|
|
|
+ String sheetName = System.currentTimeMillis() + "_成本调查表";
|
|
|
Sheet sheet = workbook.createSheet(sheetName);
|
|
|
|
|
|
Row headerRow = sheet.createRow(0);
|
|
|
@@ -656,12 +663,19 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
response.setCharacterEncoding("utf-8");
|
|
|
- String fileName = sheetName + "_" + System.currentTimeMillis() + ".xlsx";
|
|
|
+ String fileName = "成本调查表_" + System.currentTimeMillis() + ".xlsx";
|
|
|
response.setHeader("Content-Disposition", "attachment; filename=" +
|
|
|
URLEncoder.encode(fileName, "UTF-8"));
|
|
|
|
|
|
- workbook.write(response.getOutputStream());
|
|
|
- workbook.close();
|
|
|
+ try {
|
|
|
+ workbook.write(response.getOutputStream());
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("成本调查表导出失败: " + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ throw e;
|
|
|
+ } finally {
|
|
|
+ workbook.close();
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
// 财务数据表逻辑
|
|
|
@@ -715,8 +729,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 5.返回excel
|
|
|
Workbook workbook = new XSSFWorkbook();
|
|
|
- String sheetName = currentVersion.getSurveyTemplateName() != null ?
|
|
|
- currentVersion.getSurveyTemplateName() : "财务数据表";
|
|
|
+ String sheetName = System.currentTimeMillis() + "_财务数据表";
|
|
|
Sheet sheet = workbook.createSheet(sheetName);
|
|
|
|
|
|
Row headerRow = sheet.createRow(0);
|
|
|
@@ -771,12 +784,19 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
response.setCharacterEncoding("utf-8");
|
|
|
- String fileName = sheetName + "_" + System.currentTimeMillis() + ".xlsx";
|
|
|
+ String fileName = "财务数据表_" + System.currentTimeMillis() + ".xlsx";
|
|
|
response.setHeader("Content-Disposition", "attachment; filename=" +
|
|
|
URLEncoder.encode(fileName, "UTF-8"));
|
|
|
|
|
|
- workbook.write(response.getOutputStream());
|
|
|
- workbook.close();
|
|
|
+ try {
|
|
|
+ workbook.write(response.getOutputStream());
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("财务数据表导出失败: " + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ throw e;
|
|
|
+ } finally {
|
|
|
+ workbook.close();
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
// 核定表逻辑
|
|
|
@@ -786,6 +806,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到指定的核定表模板");
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
List<CostVerifyTemplateHeaders> headersList =
|
|
|
costVerifyTemplateHeadersDao.selectList(
|
|
|
new QueryWrapper<CostVerifyTemplateHeaders>()
|
|
|
@@ -796,12 +817,12 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
.sorted(Comparator.comparing(h -> {
|
|
|
String orderNum = h.getOrderNum();
|
|
|
if (StringUtil.isEmpty(orderNum)) {
|
|
|
- return Integer.MAX_VALUE; // 空值排到最后
|
|
|
+ return Integer.MAX_VALUE;
|
|
|
}
|
|
|
try {
|
|
|
return Integer.parseInt(orderNum.trim());
|
|
|
} catch (NumberFormatException e) {
|
|
|
- return Integer.MAX_VALUE; // 无效数字排到最后
|
|
|
+ return Integer.MAX_VALUE;
|
|
|
}
|
|
|
}))
|
|
|
.collect(Collectors.toList());
|
|
|
@@ -811,124 +832,56 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 查询核定表数据项
|
|
|
List<CostVerifyTemplateItems> itemsList =
|
|
|
costVerifyTemplateItemsDao.selectByVerifyTemplateId(surveyTemplateId, null);
|
|
|
|
|
|
- // 添加数据验证日志
|
|
|
- System.out.println("核定表导出 - 模板ID: " + surveyTemplateId + ", 表头数量: " + headersList.size() + ", 数据项数量: " + (itemsList != null ? itemsList.size() : 0));
|
|
|
-
|
|
|
- // 在创建 Excel 之前,先补充缺失的表头
|
|
|
- if (itemsList != null && !itemsList.isEmpty()) {
|
|
|
- // 收集所有数据项中的 headersId
|
|
|
- Set<String> allHeadersIds = itemsList.stream()
|
|
|
- .map(CostVerifyTemplateItems::getHeadersId)
|
|
|
- .filter(StringUtil::isNotEmpty)
|
|
|
- .collect(Collectors.toSet());
|
|
|
-
|
|
|
- System.out.println("数据项中的唯一 headersId 数量: " + allHeadersIds.size());
|
|
|
-
|
|
|
- // 检查是否有 headersId 在表头中找不到
|
|
|
- Set<String> existingHeaderIds = headersList.stream()
|
|
|
- .map(CostVerifyTemplateHeaders::getId)
|
|
|
- .collect(Collectors.toSet());
|
|
|
-
|
|
|
- Set<String> missingHeaderIds = new HashSet<>(allHeadersIds);
|
|
|
- missingHeaderIds.removeAll(existingHeaderIds);
|
|
|
-
|
|
|
- if (!missingHeaderIds.isEmpty()) {
|
|
|
- System.err.println("警告:发现 " + missingHeaderIds.size() + " 个数据项的 headersId 在表头中不存在:");
|
|
|
- for (String missingId : missingHeaderIds) {
|
|
|
- System.err.println(" - " + missingId);
|
|
|
- // 查询这个缺失的表头信息
|
|
|
- CostVerifyTemplateHeaders missingHeader = costVerifyTemplateHeadersDao.selectById(missingId);
|
|
|
- if (missingHeader != null) {
|
|
|
- System.err.println(" 找到表头: " + missingHeader.getFieldName() + ", showVisible: " + missingHeader.getShowVisible());
|
|
|
- // 将缺失的表头添加到列表中
|
|
|
- headersList.add(missingHeader);
|
|
|
- } else {
|
|
|
- System.err.println(" 表头不存在于数据库中");
|
|
|
- }
|
|
|
- }
|
|
|
- System.out.println("已补充缺失的表头,新的表头总数: " + headersList.size());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 打印表头统计
|
|
|
- System.out.println("最终表头列表: 共 " + headersList.size() + " 列");
|
|
|
-
|
|
|
Workbook workbook = new XSSFWorkbook();
|
|
|
- String sheetName = template.getSurveyTemplateName() != null ? template.getSurveyTemplateName() : "核定表";
|
|
|
-
|
|
|
- // Excel sheet 名称不能包含以下字符: : \ / ? * [ ]
|
|
|
- // 并且长度不能超过 31 个字符
|
|
|
- sheetName = sheetName.replaceAll("[:\\\\/*?\\[\\]]", "_");
|
|
|
- if (sheetName.length() > 31) {
|
|
|
- sheetName = sheetName.substring(0, 31);
|
|
|
- }
|
|
|
- System.out.println("Sheet 名称: " + sheetName);
|
|
|
-
|
|
|
+ String sheetName = System.currentTimeMillis() + "_核定表";
|
|
|
Sheet sheet = workbook.createSheet(sheetName);
|
|
|
|
|
|
- // 创建表头行
|
|
|
Row headerRow = sheet.createRow(0);
|
|
|
int colIndex = 0;
|
|
|
|
|
|
- // 添加原有表头
|
|
|
for (int i = 0; i < headersList.size(); i++) {
|
|
|
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()) {
|
|
|
-
|
|
|
Map<String, Integer> headerIndexMap = new HashMap<>();
|
|
|
for (int i = 0; i < headersList.size(); i++) {
|
|
|
headerIndexMap.put(headersList.get(i).getId(), i);
|
|
|
- System.out.println("表头映射 - ID: " + headersList.get(i).getId() + ", 列索引: " + i + ", 字段名: " + headersList.get(i).getFieldName());
|
|
|
}
|
|
|
-
|
|
|
- // 打印 items 的 headersId 信息
|
|
|
- System.out.println("数据项的 headersId 分布:");
|
|
|
- Map<String, Long> headersIdCount = itemsList.stream()
|
|
|
- .collect(Collectors.groupingBy(
|
|
|
- item -> item.getHeadersId() != null ? item.getHeadersId() : "null",
|
|
|
- Collectors.counting()
|
|
|
- ));
|
|
|
- headersIdCount.forEach((headersId, count) ->
|
|
|
- System.out.println(" headersId: " + headersId + ", 数量: " + count)
|
|
|
- );
|
|
|
-
|
|
|
fillExcelDataVerify(sheet, itemsList, headerIndexMap, rowIdColIndex, parentIdColIndex);
|
|
|
}
|
|
|
|
|
|
- // 列宽处理
|
|
|
for (int i = 0; i < headersList.size(); i++) {
|
|
|
sheet.autoSizeColumn(i);
|
|
|
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");
|
|
|
- String fileName = sheetName + "_" + System.currentTimeMillis() + ".xlsx";
|
|
|
+ String fileName = "核定表_" + System.currentTimeMillis() + ".xlsx";
|
|
|
response.setHeader("Content-Disposition", "attachment; filename=" +
|
|
|
URLEncoder.encode(fileName, "UTF-8"));
|
|
|
|
|
|
- System.out.println("开始写入 Excel 文件到输出流...");
|
|
|
- workbook.write(response.getOutputStream());
|
|
|
- workbook.close();
|
|
|
- System.out.println("Excel 文件写入完成");
|
|
|
+ try {
|
|
|
+ workbook.write(response.getOutputStream());
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("核定表导出失败: " + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ throw e;
|
|
|
+ } finally {
|
|
|
+ workbook.close();
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
// 其它不支持
|
|
|
@@ -978,6 +931,12 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (refId == null) {
|
|
|
refId = materialId;
|
|
|
}
|
|
|
+ if (periodRecordId !=null){
|
|
|
+ CostAuditPeriodRecord periodRecord = costAuditPeriodRecordManager.get(periodRecordId);
|
|
|
+ if (periodRecord != null){
|
|
|
+ templateType = periodRecord.getType();
|
|
|
+ }
|
|
|
+ }
|
|
|
switch (type) {
|
|
|
// 成本调查表逻辑
|
|
|
case "1": {
|
|
|
@@ -1173,7 +1132,9 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
List<String> errors = verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
if (!errors.isEmpty()) {
|
|
|
- return CommonResult.<String>error().message("导入失败,发现以下问题:\n" + String.join("\n", errors));
|
|
|
+ CommonResult<String> result = CommonResult.<String>error().message("导入失败,发现以下问题:<br>" + String.join("<br>", errors));
|
|
|
+ result.setCode(250);
|
|
|
+ return result;
|
|
|
}
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
|
|
|
@@ -1379,12 +1340,16 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
List<String> errors = verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
if (!errors.isEmpty()) {
|
|
|
- return CommonResult.<String>error().message("导入失败,发现以下问题:\n" + String.join("\n", errors));
|
|
|
+ CommonResult<String> result = CommonResult.<String>error().message("导入失败,发现以下问题:<br>" + String.join("<br>", errors));
|
|
|
+ result.setCode(250);
|
|
|
+ return result;
|
|
|
}
|
|
|
costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
- CostProjectTaskMaterial material = costProjectTaskMaterialManager.getById(materialId);
|
|
|
- material.setIsUpload("1");
|
|
|
- costProjectTaskMaterialManager.updateById(material);
|
|
|
+ if (materialId != null) {
|
|
|
+ CostProjectTaskMaterial material = costProjectTaskMaterialManager.getById(materialId);
|
|
|
+ material.setIsUpload("1");
|
|
|
+ costProjectTaskMaterialManager.updateById(material);
|
|
|
+ }
|
|
|
|
|
|
return CommonResult.<String>ok().message("导入成功,共导入 " + dataRowCount + " 行数据");
|
|
|
} catch (Exception e) {
|
|
|
@@ -1519,13 +1484,9 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
if (dataList.isEmpty()) {
|
|
|
return CommonResult.<String>error().message("Excel文件没有有效数据");
|
|
|
}
|
|
|
- List<String> errors = verifyImportData(dataList, type, surveyTemplateId, rowIdToExcelRowMap);
|
|
|
- if (!errors.isEmpty()) {
|
|
|
- return CommonResult.<String>error().message("导入失败,发现以下问题:\n" + String.join("\n", errors));
|
|
|
- }
|
|
|
- // 保存
|
|
|
- costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
|
|
|
+ // 保存数据(核定表不需要复杂的字段校验)
|
|
|
+ costSurveyTemplateUploadDataManager.saveData(dataList);
|
|
|
|
|
|
return CommonResult.<String>ok().message("导入成功,共导入 " + importRowCount + " 行数据");
|
|
|
|
|
|
@@ -1818,12 +1779,13 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
String fieldType = getHeaderFieldType(headerObj);
|
|
|
Integer fieldTypelen = getHeaderFieldTypelen(headerObj);
|
|
|
Integer fieldTypenointlen = getHeaderFieldTypenointlen(headerObj);
|
|
|
+ String format = getHeaderFormat(headerObj);
|
|
|
String isRequired = getHeaderIsRequired(headerObj);
|
|
|
String isDict = getHeaderIsDict(headerObj);
|
|
|
String dictCode = getHeaderDictCode(headerObj);
|
|
|
|
|
|
- // 获取用户输入的值
|
|
|
- String value = rowData.get(fieldEname);
|
|
|
+ // 获取用户输入的值(注意:rowData的key是fieldName中文字段名)
|
|
|
+ String value = rowData.get(fieldName);
|
|
|
|
|
|
System.out.println(" 校验字段: " + fieldEname + "(" + fieldName + "), 值: " + value +
|
|
|
", 必填: " + isRequired + ", 类型: " + fieldType + ", 字典: " + isDict);
|
|
|
@@ -1842,25 +1804,16 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
// 2. 字典校验(支持 "1" 和 "true" 两种格式)
|
|
|
if (("1".equals(isDict) || "true".equalsIgnoreCase(isDict)) && StringUtil.isNotEmpty(dictCode)) {
|
|
|
try {
|
|
|
- validateDictValue(rowid, fieldEname, fieldName, value, dictCode);
|
|
|
+ validateDictValue(rowDisplay, fieldEname, fieldName, value, dictCode);
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
errors.add(e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 3. 字段类型校验
|
|
|
+ // 3. 字段类型和长度校验(已合并)
|
|
|
if (StringUtil.isNotEmpty(fieldType)) {
|
|
|
try {
|
|
|
- validateFieldType(rowDisplay, fieldEname, fieldName, value, fieldType);
|
|
|
- } catch (IllegalArgumentException e) {
|
|
|
- errors.add(e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 4. 字段长度校验
|
|
|
- if (fieldTypelen != null && fieldTypelen > 0) {
|
|
|
- try {
|
|
|
- validateFieldLength(rowid, fieldEname, fieldName, value, fieldType, fieldTypelen, fieldTypenointlen);
|
|
|
+ validateFieldType(rowDisplay, fieldEname, fieldName, value, fieldType, fieldTypelen, fieldTypenointlen, format);
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
errors.add(e.getMessage());
|
|
|
}
|
|
|
@@ -1873,7 +1826,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
/**
|
|
|
* 字典校验
|
|
|
*/
|
|
|
- private void validateDictValue(String rowid, String fieldEname, String fieldName,
|
|
|
+ private void validateDictValue(String rowDisplay, String fieldEname, String fieldName,
|
|
|
String value, String dictCode) {
|
|
|
if (StringUtil.isEmpty(value) || StringUtil.isEmpty(dictCode)) {
|
|
|
return;
|
|
|
@@ -1890,14 +1843,14 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
List<DataDict> dictDataList = dataDictManager.list(wrapper);
|
|
|
|
|
|
if (dictDataList == null || dictDataList.isEmpty()) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] [%s]的选项配置异常",
|
|
|
- rowid, fieldName));
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]的选项配置异常",
|
|
|
+ rowDisplay, fieldName));
|
|
|
}
|
|
|
|
|
|
// 检查值是否在字典允许的范围内(支持多选,用逗号分隔)
|
|
|
String[] values = value.split(",");
|
|
|
Set<String> validValues = dictDataList.stream()
|
|
|
- .map(DataDict::getKey)
|
|
|
+ .map(DataDict::getName)
|
|
|
.filter(StringUtil::isNotEmpty)
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
|
@@ -1910,56 +1863,151 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
if (!invalidValues.isEmpty()) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] [%s]的值[%s]不在允许的选项范围内",
|
|
|
- rowid, fieldName, String.join(", ", invalidValues)));
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]的值[%s]不在允许的选项范围内",
|
|
|
+ rowDisplay, fieldName, String.join(", ", invalidValues)));
|
|
|
}
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
throw e;
|
|
|
} catch (Exception e) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] [%s]选项校验异常:%s",
|
|
|
- rowid, fieldName, e.getMessage()));
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]选项校验异常:%s",
|
|
|
+ rowDisplay, fieldName, e.getMessage()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 字段类型校验
|
|
|
+ * 根据日期格式字符串生成正则表达式
|
|
|
+ */
|
|
|
+ private String getDateRegexByFormat(String format) {
|
|
|
+ if (StringUtil.isEmpty(format)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (format) {
|
|
|
+ case "yyyy-MM-dd":
|
|
|
+ return "\\d{4}-\\d{2}-\\d{2}";
|
|
|
+ case "yyyy-MM-dd HH:mm:ss":
|
|
|
+ return "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}";
|
|
|
+ case "yyyy/MM/dd":
|
|
|
+ return "\\d{4}/\\d{2}/\\d{2}";
|
|
|
+ case "yyyy/MM/dd HH:mm:ss":
|
|
|
+ return "\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2}";
|
|
|
+ case "yyyy年MM月dd日":
|
|
|
+ return "\\d{4}年\\d{2}月\\d{2}日";
|
|
|
+ case "yyyyMMdd":
|
|
|
+ return "\\d{8}";
|
|
|
+ default:
|
|
|
+ // 对于其他格式,尝试通用转换
|
|
|
+ return format
|
|
|
+ .replace("yyyy", "\\\\d{4}")
|
|
|
+ .replace("MM", "\\\\d{2}")
|
|
|
+ .replace("dd", "\\\\d{2}")
|
|
|
+ .replace("HH", "\\\\d{2}")
|
|
|
+ .replace("mm", "\\\\d{2}")
|
|
|
+ .replace("ss", "\\\\d{2}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 字段类型和长度校验(合并)
|
|
|
*/
|
|
|
private void validateFieldType(String rowDisplay, String fieldEname, String fieldName,
|
|
|
- String value, String fieldType) {
|
|
|
+ String value, String fieldType, Integer fieldTypelen, Integer fieldTypenointlen, String format) {
|
|
|
try {
|
|
|
switch (fieldType.toLowerCase()) {
|
|
|
case "int":
|
|
|
case "integer":
|
|
|
- case "bigint":
|
|
|
try {
|
|
|
Long.parseLong(value);
|
|
|
+
|
|
|
+ // 校验整数位数(必须精确匹配)
|
|
|
+ if (fieldTypelen != null && fieldTypelen > 0) {
|
|
|
+ String absValue = value.replace("-", "").replace("+", ""); // 去除正负号
|
|
|
+ if (absValue.length() != fieldTypelen) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ String.format("%s [%s]必须是%d位整数,实际值:%s",
|
|
|
+ rowDisplay, fieldName, fieldTypelen, value));
|
|
|
+ }
|
|
|
+ }
|
|
|
} catch (NumberFormatException e) {
|
|
|
throw new IllegalArgumentException(String.format("%s [%s]应为整数,实际值:%s", rowDisplay, fieldName, value));
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
- case "decimal":
|
|
|
case "double":
|
|
|
- case "zi":
|
|
|
- case "number":
|
|
|
try {
|
|
|
Double.parseDouble(value);
|
|
|
+
|
|
|
+ // 数字精度校验
|
|
|
+ String[] parts = value.split("\\.");
|
|
|
+
|
|
|
+ // 整数部分长度
|
|
|
+ int intPartLen = parts[0].replace("-", "").replace("+", "").length();
|
|
|
+ if (fieldTypenointlen != null && fieldTypelen != null && intPartLen > (fieldTypelen - fieldTypenointlen)) {
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]整数部分长度超限,最大:%d,实际:%d",
|
|
|
+ rowDisplay, fieldName, (fieldTypelen - fieldTypenointlen), intPartLen));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 小数部分长度(必须精确匹配)
|
|
|
+ if (fieldTypenointlen != null && fieldTypenointlen > 0) {
|
|
|
+ if (parts.length == 1) {
|
|
|
+ // 没有小数部分,但要求有小数位
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ String.format("%s [%s]必须包含%d位小数,实际值:%s",
|
|
|
+ rowDisplay, fieldName, fieldTypenointlen, value));
|
|
|
+ } else {
|
|
|
+ int decimalPartLen = parts[1].length();
|
|
|
+ // 改为精确匹配,而不是"不超过"
|
|
|
+ if (decimalPartLen != fieldTypenointlen) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ String.format("%s [%s]必须是%d位小数,实际:%d位",
|
|
|
+ rowDisplay, fieldName, fieldTypenointlen, decimalPartLen));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
} catch (NumberFormatException e) {
|
|
|
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]日期格式应为yyyy-MM-dd,实际值:%s", rowDisplay, fieldName, value));
|
|
|
+ case "varchar":
|
|
|
+ case "string":
|
|
|
+ case "text":
|
|
|
+ // 字符串长度校验(使用 format 字段)
|
|
|
+ if (StringUtil.isNotEmpty(format)) {
|
|
|
+ try {
|
|
|
+ int maxLength = Integer.parseInt(format);
|
|
|
+ if (value.length() > maxLength) {
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]长度超限,最大长度:%d,实际长度:%d",
|
|
|
+ rowDisplay, fieldName, maxLength, value.length()));
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // format 不是数字,跳过长度校验
|
|
|
+ }
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
+ case "date":
|
|
|
case "datetime":
|
|
|
- // 日期时间格式校验 - 支持两种格式
|
|
|
- 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));
|
|
|
+ // 日期格式校验(使用 format 字段)
|
|
|
+ if (StringUtil.isNotEmpty(format)) {
|
|
|
+ String regex = getDateRegexByFormat(format);
|
|
|
+ if (StringUtil.isNotEmpty(regex) && !value.matches(regex)) {
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]日期格式应为%s,实际值:%s",
|
|
|
+ rowDisplay, fieldName, format, value));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果没有 format,使用默认校验
|
|
|
+ if ("date".equals(fieldType.toLowerCase())) {
|
|
|
+ if (!value.matches("\\d{4}-\\d{2}-\\d{2}")) {
|
|
|
+ throw new IllegalArgumentException(String.format("%s [%s]日期格式应为yyyy-MM-dd,实际值:%s",
|
|
|
+ rowDisplay, fieldName, value));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ 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;
|
|
|
|
|
|
@@ -1975,11 +2023,6 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
- case "varchar":
|
|
|
- case "string":
|
|
|
- case "text":
|
|
|
- // 字符串类型,无需特殊校验
|
|
|
- break;
|
|
|
default:
|
|
|
// 未知类型,不校验
|
|
|
break;
|
|
|
@@ -1991,61 +2034,6 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 字段长度校验
|
|
|
- */
|
|
|
- private void validateFieldLength(String rowid, String fieldEname, String fieldName, String value,
|
|
|
- String fieldType, Integer fieldTypelen, Integer fieldTypenointlen) {
|
|
|
- try {
|
|
|
- switch (fieldType.toLowerCase()) {
|
|
|
- case "varchar":
|
|
|
- case "string":
|
|
|
- case "text":
|
|
|
- // 字符串长度校验
|
|
|
- if (value.length() > fieldTypelen) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] [%s]长度超限,最大长度:%d,实际长度:%d",
|
|
|
- rowid, fieldName, fieldTypelen, value.length()));
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case "decimal":
|
|
|
- case "double":
|
|
|
- case "float":
|
|
|
- case "number":
|
|
|
- // 数字精度校验
|
|
|
- try {
|
|
|
- double numValue = Double.parseDouble(value);
|
|
|
- String[] parts = value.split("\\.");
|
|
|
-
|
|
|
- // 整数部分长度
|
|
|
- int intPartLen = parts[0].replace("-", "").length();
|
|
|
- if (fieldTypenointlen != null && intPartLen > (fieldTypelen - fieldTypenointlen)) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]整数部分长度超限,最大:%d,实际:%d",
|
|
|
- rowid, fieldEname, fieldName, (fieldTypelen - fieldTypenointlen), intPartLen));
|
|
|
- }
|
|
|
-
|
|
|
- // 小数部分长度
|
|
|
- if (parts.length > 1 && fieldTypenointlen != null) {
|
|
|
- int decimalPartLen = parts[1].length();
|
|
|
- if (decimalPartLen > fieldTypenointlen) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]小数位数超限,最大:%d,实际:%d",
|
|
|
- rowid, fieldEname, fieldName, fieldTypenointlen, decimalPartLen));
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (NumberFormatException e) {
|
|
|
- // 类型校验已经处理过了,这里不重复报错
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
- } catch (IllegalArgumentException e) {
|
|
|
- throw e;
|
|
|
- } catch (Exception e) {
|
|
|
- throw new IllegalArgumentException(String.format("行[%s] 字段[%s(%s)]长度校验异常:%s", rowid, fieldEname, fieldName, e.getMessage()));
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
/**
|
|
|
* 获取表头的字段英文名
|
|
|
@@ -2160,6 +2148,20 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 获取表头的格式
|
|
|
+ */
|
|
|
+ private String getHeaderFormat(Object header) {
|
|
|
+ if (header instanceof CostSurveyTemplateHeaders) {
|
|
|
+ return ((CostSurveyTemplateHeaders) header).getFormat();
|
|
|
+ } else if (header instanceof CostSurveyFdTemplateHeaders) {
|
|
|
+ return ((CostSurveyFdTemplateHeaders) header).getFormat();
|
|
|
+ } else if (header instanceof CostVerifyTemplateHeaders) {
|
|
|
+ return ((CostVerifyTemplateHeaders) header).getFormat();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 根据类型获取模板项
|
|
|
*/
|
|
|
private List<? extends Object> getTemplateItems(String type, String surveyTemplateId) {
|
|
|
@@ -2803,19 +2805,29 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 找出所有根节点(parentid为空或不存在的)
|
|
|
+ // 找出所有根节点(parentid为空或不存在的),按 orderNum 排序
|
|
|
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()
|
|
|
+ .sorted((id1, id2) -> {
|
|
|
+ List<CostVerifyTemplateItems> items1 = itemsByRowId.get(id1);
|
|
|
+ List<CostVerifyTemplateItems> items2 = itemsByRowId.get(id2);
|
|
|
+ if (items1 == null || items1.isEmpty()) return 1;
|
|
|
+ if (items2 == null || items2.isEmpty()) return -1;
|
|
|
+ Integer order1 = items1.get(0).getOrderNum();
|
|
|
+ Integer order2 = items2.get(0).getOrderNum();
|
|
|
+ if (order1 == null) return 1;
|
|
|
+ if (order2 == null) return -1;
|
|
|
+ return order1.compareTo(order2);
|
|
|
+ })
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
// 递归添加节点及其子节点
|
|
|
for (String rootRowId : rootRowIds) {
|
|
|
- addRowIdWithChildrenVerify(rootRowId, rowIdToParentId, allRowIds, result);
|
|
|
+ addRowIdWithChildrenVerify(rootRowId, rowIdToParentId, allRowIds, result, itemsByRowId);
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
@@ -2825,18 +2837,28 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
* 递归添加rowId及其所有子节点(核定表)
|
|
|
*/
|
|
|
private void addRowIdWithChildrenVerify(String rowId, Map<String, String> rowIdToParentId,
|
|
|
- Set<String> allRowIds, List<String> result) {
|
|
|
+ Set<String> allRowIds, List<String> result, Map<String, List<CostVerifyTemplateItems>> itemsByRowId) {
|
|
|
result.add(rowId);
|
|
|
|
|
|
- // 找出所有子节点
|
|
|
+ // 找出所有子节点,按 orderNum 排序
|
|
|
List<String> children = allRowIds.stream()
|
|
|
.filter(id -> rowId.equals(rowIdToParentId.get(id)))
|
|
|
- .sorted()
|
|
|
+ .sorted((id1, id2) -> {
|
|
|
+ List<CostVerifyTemplateItems> items1 = itemsByRowId.get(id1);
|
|
|
+ List<CostVerifyTemplateItems> items2 = itemsByRowId.get(id2);
|
|
|
+ if (items1 == null || items1.isEmpty()) return 1;
|
|
|
+ if (items2 == null || items2.isEmpty()) return -1;
|
|
|
+ Integer order1 = items1.get(0).getOrderNum();
|
|
|
+ Integer order2 = items2.get(0).getOrderNum();
|
|
|
+ if (order1 == null) return 1;
|
|
|
+ if (order2 == null) return -1;
|
|
|
+ return order1.compareTo(order2);
|
|
|
+ })
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
// 递归处理子节点
|
|
|
for (String childRowId : children) {
|
|
|
- addRowIdWithChildrenVerify(childRowId, rowIdToParentId, allRowIds, result);
|
|
|
+ addRowIdWithChildrenVerify(childRowId, rowIdToParentId, allRowIds, result, itemsByRowId);
|
|
|
}
|
|
|
}
|
|
|
|