|
|
@@ -131,6 +131,90 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
|
|
|
/**
|
|
|
+ * 查询跨表引用的所有值
|
|
|
+ *
|
|
|
+ * @param taskId 任务ID
|
|
|
+ * @param surveyTemplateId 当前模板ID
|
|
|
+ * @param type 类型:1-成本调查表 2-财务数据表
|
|
|
+ * @return 跨表数据,格式:{"isCrossTable": true, "crossTableName": "表名", "data": {"2024": {"表名.Q1": "100"}}}
|
|
|
+ */
|
|
|
+ @GetMapping(value = "/getCrossTableData")
|
|
|
+ @ApiOperation(value = "查询跨表引用的所有值", httpMethod = "GET")
|
|
|
+ public CommonResult<Map<String, Object>> getCrossTableData(
|
|
|
+ @ApiParam(name = "taskId", value = "任务ID", required = true)
|
|
|
+ @RequestParam(required = true) String taskId,
|
|
|
+ @ApiParam(name = "surveyTemplateId", value = "当前模板ID", required = true)
|
|
|
+ @RequestParam(required = true) String surveyTemplateId,
|
|
|
+ @ApiParam(name = "type", value = "类型:1-成本调查表 2-财务数据表", required = true)
|
|
|
+ @RequestParam(required = true) String type) throws Exception {
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("isCrossTable", false);
|
|
|
+ result.put("data", new HashMap<>());
|
|
|
+
|
|
|
+ // 获取当前模板的跨表引用ID
|
|
|
+ String calculationTemplateId = null;
|
|
|
+ List<? extends Object> itemsList = getTemplateItems(type, surveyTemplateId);
|
|
|
+ System.out.println("getCrossTableData - itemsList size: " + (itemsList != null ? itemsList.size() : 0));
|
|
|
+ if (itemsList != null && !itemsList.isEmpty()) {
|
|
|
+ for (Object item : itemsList) {
|
|
|
+ String refId = getItemCalculationTemplateId(item);
|
|
|
+ System.out.println("getCrossTableData - refId: " + refId);
|
|
|
+ if (StringUtil.isNotEmpty(refId)) {
|
|
|
+ calculationTemplateId = refId;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("getCrossTableData - calculationTemplateId: " + calculationTemplateId);
|
|
|
+ if (StringUtil.isEmpty(calculationTemplateId)) {
|
|
|
+ return CommonResult.<Map<String, Object>>ok().value(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取被引用表的名称
|
|
|
+ Object crossTemplate = getTemplateById(type, calculationTemplateId);
|
|
|
+ String crossTableName = getTemplateName(crossTemplate);
|
|
|
+ if (StringUtil.isEmpty(crossTableName)) {
|
|
|
+ return CommonResult.<Map<String, Object>>ok().value(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.put("isCrossTable", true);
|
|
|
+ result.put("crossTableName", crossTableName);
|
|
|
+ Map<String, Object> dataByPeriod = new HashMap<>();
|
|
|
+
|
|
|
+ // 查询当前任务中的所有年限
|
|
|
+ Set<String> allPeriods = new HashSet<>();
|
|
|
+ QueryWrapper<CostSurveyTemplateUploadData> wrapper = new QueryWrapper<>();
|
|
|
+ wrapper.eq("task_id", taskId)
|
|
|
+ .eq("type", type)
|
|
|
+ .eq("is_deleted", "0");
|
|
|
+ List<CostSurveyTemplateUploadData> allData = costSurveyTemplateUploadDataManager.list(wrapper);
|
|
|
+
|
|
|
+ for (CostSurveyTemplateUploadData data : allData) {
|
|
|
+ if (StringUtil.isNotEmpty(data.getRkey()) && data.getRkey().matches("\\d{4}")) {
|
|
|
+ allPeriods.add(data.getRkey());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("getCrossTableData - allPeriods: " + allPeriods);
|
|
|
+ for (String period : allPeriods) {
|
|
|
+ Map<String, String> crossData = cellDataQueryService.getCrossTableFormulaData(
|
|
|
+ taskId, calculationTemplateId, type, period);
|
|
|
+ System.out.println("getCrossTableData - period: " + period + ", crossData size: " + (crossData != null ? crossData.size() : 0));
|
|
|
+ Map<String, String> periodData = new HashMap<>();
|
|
|
+ for (Map.Entry<String, String> entry : crossData.entrySet()) {
|
|
|
+ String cellCode = entry.getKey().split("_")[0];
|
|
|
+ periodData.put(crossTableName + "." + cellCode, entry.getValue());
|
|
|
+ }
|
|
|
+ dataByPeriod.put(period, periodData);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.put("data", dataByPeriod);
|
|
|
+ return CommonResult.<Map<String, Object>>ok().value(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 企业报送-调查表-列表
|
|
|
*
|
|
|
* @param taskId 任务ID
|
|
|
@@ -146,11 +230,12 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
@ApiParam(name = "type", value = "类型:1-成本调查表 2-财务数据表", required = true)
|
|
|
@RequestParam(required = true) String type) throws Exception {
|
|
|
CostProjectTask task = costProjectTaskManager.getById(taskId);
|
|
|
+ CostProjectApproval approval = costProjectApprovalManager.getById(task.getProjectId());
|
|
|
|
|
|
switch (type) {
|
|
|
case "1": {
|
|
|
List<CostCatalogSurvey> list = costCatalogSurveyManager.list(
|
|
|
- new LambdaQueryWrapper<CostCatalogSurvey>().eq(CostCatalogSurvey::getCatalogId, task.getCatalogId())
|
|
|
+ new LambdaQueryWrapper<CostCatalogSurvey>().eq(CostCatalogSurvey::getCatalogId, approval.getCatalogId())
|
|
|
);
|
|
|
// 成本调查表逻辑
|
|
|
List<CostSurveyTemplateUpload> uploadList = costSurveyTemplateUploadManager.listByTaskId(taskId);
|
|
|
@@ -196,6 +281,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
* 企业报送-调查表-获取所需表格字段
|
|
|
*
|
|
|
* @param surveyTemplateId 调查表模板ID
|
|
|
+ *
|
|
|
+ * {"q1":123,"q2":123}
|
|
|
* @param type 类型:1-成本调查表 2-财务数据表
|
|
|
* @return 指标项数据列表(带key-value拼接)
|
|
|
*/
|
|
|
@@ -584,7 +671,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
case ("1"): {
|
|
|
CostSurveyTemplate template = costSurveyTemplateManager.get(surveyTemplateId);
|
|
|
if (template == null) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到指定的调查模板");
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("未找到指定的调查模板")));
|
|
|
return;
|
|
|
}
|
|
|
templateType = template.getTemplateType();
|
|
|
@@ -594,7 +682,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
costSurveyTemplateVersionManager.get(versionId) : costSurveyTemplateVersionManager.selectCurrentVersion(surveyTemplateId);
|
|
|
|
|
|
if (currentVersion == null) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到指定的模板版本");
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("未找到指定的模板版本")));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -618,7 +707,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
|
|
|
if (headersList.isEmpty()) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到表头信息");
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("未找到表头信息")));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -628,12 +718,26 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
// 校验跨表数据完整性
|
|
|
for (CostSurveyTemplateItems item : itemsList) {
|
|
|
if (StringUtil.isNotEmpty(item.getCalculationTemplateId())) {
|
|
|
- // 有跨表引用,检查跨表数据是否存在
|
|
|
- Map<String, String> crossData = cellDataQueryService.getCrossTableFormulaData(
|
|
|
- taskId, item.getCalculationTemplateId(), type, "0");
|
|
|
- if (crossData.isEmpty()) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST,
|
|
|
- "导出失败:该表有跨表引用,但另外一个表(ID: " + item.getCalculationTemplateId() + ")的数据缺失。请先完成另外一个表的数据导入。");
|
|
|
+ // 有跨表引用,检查所有年限的跨表数据
|
|
|
+ Object crossTemplate = getTemplateById(type, item.getCalculationTemplateId());
|
|
|
+ String crossTableName = getTemplateName(crossTemplate);
|
|
|
+
|
|
|
+ Set<String> missingPeriods = new HashSet<>();
|
|
|
+ if (auditPeriods != null) {
|
|
|
+ for (String period : auditPeriods) {
|
|
|
+ Map<String, String> crossData = cellDataQueryService.getCrossTableFormulaData(
|
|
|
+ taskId, item.getCalculationTemplateId(), type, period.trim());
|
|
|
+ if (crossData.isEmpty()) {
|
|
|
+ missingPeriods.add(period.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!missingPeriods.isEmpty()) {
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message(
|
|
|
+ "导出失败:该表有跨表引用 [" + crossTableName + "],以下年限数据缺失:" +
|
|
|
+ String.join("、", missingPeriods) + "。请先完成 [" + crossTableName + "] 的数据导入。")));
|
|
|
return;
|
|
|
}
|
|
|
break;
|
|
|
@@ -752,7 +856,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
// 1.获取模板信息
|
|
|
CostSurveyFdTemplate template = costSurveyFdTemplateManager.get(surveyTemplateId);
|
|
|
if (template == null) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到指定的财务数据表模板");
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("未找到指定的财务数据表模板")));
|
|
|
return;
|
|
|
}
|
|
|
templateType = template.getTemplateType();
|
|
|
@@ -763,7 +868,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
costSurveyFdTemplateVersionManager.get(versionId) : costSurveyFdTemplateVersionManager.selectCurrentVersion(surveyTemplateId);
|
|
|
|
|
|
if (currentVersion == null) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到指定的模板版本");
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("未找到指定的模板版本")));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -788,7 +894,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
|
|
|
if (headersList.isEmpty()) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到表头信息");
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("未找到表头信息")));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -799,12 +906,26 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
// 校验跨表数据完整性
|
|
|
for (CostSurveyFdTemplateItems item : itemsList) {
|
|
|
if (StringUtil.isNotEmpty(item.getCalculationTemplateId())) {
|
|
|
- // 有跨表引用,检查跨表数据是否存在
|
|
|
- Map<String, String> crossData = cellDataQueryService.getCrossTableFormulaData(
|
|
|
- taskId, item.getCalculationTemplateId(), type, "0");
|
|
|
- if (crossData.isEmpty()) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST,
|
|
|
- "导出失败:该表有跨表引用,但另外一个表(ID: " + item.getCalculationTemplateId() + ")的数据缺失。请先完成另外一个表的数据导入。");
|
|
|
+ // 有跨表引用,检查所有年限的跨表数据
|
|
|
+ Object crossTemplate = getTemplateById(type, item.getCalculationTemplateId());
|
|
|
+ String crossTableName = getTemplateName(crossTemplate);
|
|
|
+
|
|
|
+ Set<String> missingPeriods = new HashSet<>();
|
|
|
+ if (auditPeriods != null) {
|
|
|
+ for (String period : auditPeriods) {
|
|
|
+ Map<String, String> crossData = cellDataQueryService.getCrossTableFormulaData(
|
|
|
+ taskId, item.getCalculationTemplateId(), type, period.trim());
|
|
|
+ if (crossData.isEmpty()) {
|
|
|
+ missingPeriods.add(period.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!missingPeriods.isEmpty()) {
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message(
|
|
|
+ "导出失败:该表有跨表引用 [" + crossTableName + "],以下年限数据缺失:" +
|
|
|
+ String.join("、", missingPeriods) + "。请先完成 [" + crossTableName + "] 的数据导入。")));
|
|
|
return;
|
|
|
}
|
|
|
break;
|
|
|
@@ -923,7 +1044,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
case ("3"): {
|
|
|
CostVerifyTemplate template = costVerifyTemplateManager.get(surveyTemplateId);
|
|
|
if (template == null) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到指定的核定表模板");
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("未找到指定的核定表模板")));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -948,7 +1070,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
if (headersList.isEmpty()) {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未找到表头信息");
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("未找到表头信息")));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -1032,8 +1155,9 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
// 其它不支持
|
|
|
default: {
|
|
|
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "不支持的类型");
|
|
|
- break;
|
|
|
+ response.setContentType("application/json;charset=UTF-8");
|
|
|
+ response.getWriter().write(JSON.toJSONString(CommonResult.<String>error().message("不支持的类型")));
|
|
|
+ return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1781,35 +1905,6 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
return errors;
|
|
|
}
|
|
|
|
|
|
- // 0. 检查是否修改了已有数据
|
|
|
- String refId = dataList.get(0).getRefId();
|
|
|
- if (StringUtil.isNotEmpty(refId)) {
|
|
|
- QueryWrapper<CostSurveyTemplateUploadData> existingWrapper = new QueryWrapper<>();
|
|
|
- existingWrapper.eq("ref_id", refId).eq("type", type).eq("is_deleted", "0");
|
|
|
- List<CostSurveyTemplateUploadData> existingDataList = costSurveyTemplateUploadDataManager.list(existingWrapper);
|
|
|
-
|
|
|
- if (!existingDataList.isEmpty()) {
|
|
|
- // 构建已有数据的映射:rowid_rkey -> rvalue
|
|
|
- Map<String, String> existingDataMap = new HashMap<>();
|
|
|
- for (CostSurveyTemplateUploadData data : existingDataList) {
|
|
|
- String key = data.getRowid() + "_" + data.getRkey();
|
|
|
- existingDataMap.put(key, data.getRvalue());
|
|
|
- }
|
|
|
-
|
|
|
- // 检查导入数据是否修改了已有数据
|
|
|
- for (CostSurveyTemplateUploadData data : dataList) {
|
|
|
- String key = data.getRowid() + "_" + data.getRkey();
|
|
|
- String existingValue = existingDataMap.get(key);
|
|
|
- if (existingValue != null && !existingValue.equals(data.getRvalue())) {
|
|
|
- Integer excelRowNum = rowIdToExcelRowMap.get(data.getRowid());
|
|
|
- String rowInfo = excelRowNum != null ? "第" + excelRowNum + "行" : "行[" + data.getRowid() + "]";
|
|
|
- errors.add(String.format("%s 字段[%s] 不能修改已有数据。原值:%s,新值:%s",
|
|
|
- rowInfo, data.getRkey(), existingValue, data.getRvalue()));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
// 获取模板类型(templateType)
|
|
|
String templateType = getTemplateType(type, surveyTemplateId);
|
|
|
|
|
|
@@ -1903,10 +1998,22 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
// 处理跨表引用(检查模板项中的 calculation_template_id)
|
|
|
+ Map<String, String> tableNameToIdMap = new HashMap<>(); // 表名 -> 表ID映射
|
|
|
for (Object item : itemsList) {
|
|
|
String calculationTemplateId = getItemCalculationTemplateId(item);
|
|
|
if (StringUtil.isNotEmpty(calculationTemplateId)) {
|
|
|
- String crossTableType = type;
|
|
|
+ // 自动检测被引用表的类型
|
|
|
+ String crossTableType = detectTemplateType(calculationTemplateId);
|
|
|
+ if (StringUtil.isEmpty(crossTableType)) {
|
|
|
+ crossTableType = type; // 默认使用当前表的类型
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取被引用表的名称
|
|
|
+ Object crossTemplate = getTemplateById(crossTableType, calculationTemplateId);
|
|
|
+ String crossTableName = getTemplateName(crossTemplate);
|
|
|
+ if (StringUtil.isNotEmpty(crossTableName)) {
|
|
|
+ tableNameToIdMap.put(crossTableName, calculationTemplateId);
|
|
|
+ }
|
|
|
|
|
|
// 收集所有年限
|
|
|
Set<String> allPeriods = new HashSet<>();
|
|
|
@@ -1977,7 +2084,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 针对每个年限分别校验(收集错误)
|
|
|
for (String period : periods) {
|
|
|
- List<String> formulaErrors = validateRowFormulasForPeriod(rowid, rowData, rowItems, globalCellCodeMap, period, type, rowIdToExcelRowMap, cellCodeToRowIdMap);
|
|
|
+ List<String> formulaErrors = validateRowFormulasForPeriod(rowid, rowData, rowItems, globalCellCodeMap, period, type, rowIdToExcelRowMap, cellCodeToRowIdMap, tableNameToIdMap);
|
|
|
errors.addAll(formulaErrors);
|
|
|
}
|
|
|
}
|
|
|
@@ -2578,7 +2685,7 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
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,
|
|
|
- Map<String, String> cellCodeToRowIdMap) {
|
|
|
+ Map<String, String> cellCodeToRowIdMap, Map<String, String> tableNameToIdMap) {
|
|
|
List<String> errors = new ArrayList<>();
|
|
|
// 找到该行的计算公式(同一行的所有模板项共享同一个公式)
|
|
|
String calculationFormula = null;
|
|
|
@@ -2600,31 +2707,43 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
}
|
|
|
|
|
|
// 检查公式引用的单元格在当前年限是否有任何一个有值
|
|
|
- // 提取公式中的所有单元格引用(如 A1, A2, A3)
|
|
|
+ // 提取公式中的所有单元格引用(支持 "表名.单元格代码" 或 "单元格代码" 两种格式)
|
|
|
boolean hasAnyReferencedValue = false;
|
|
|
- java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("[A-Z]+\\d+");
|
|
|
- java.util.regex.Matcher matcher = pattern.matcher(calculationFormula);
|
|
|
+ java.util.regex.Pattern tableRefPattern = java.util.regex.Pattern.compile("([\\u4e00-\\u9fa5a-zA-Z0-9_]+)\\.([A-Z]+\\d+)");
|
|
|
+ java.util.regex.Pattern cellPattern = java.util.regex.Pattern.compile("[A-Z]+\\d+");
|
|
|
+ java.util.regex.Matcher matcher = tableRefPattern.matcher(calculationFormula);
|
|
|
List<String> referencedCellsDebug = new ArrayList<>();
|
|
|
|
|
|
+ // 先处理 "表名.单元格" 格式
|
|
|
+ Set<String> processedCells = new HashSet<>();
|
|
|
while (matcher.find()) {
|
|
|
- String referencedCell = matcher.group();
|
|
|
- String mapKey = referencedCell + "_" + period;
|
|
|
- String value = null;
|
|
|
+ String tableName = matcher.group(1);
|
|
|
+ String cellCode_ref = matcher.group(2);
|
|
|
+ String tableId = tableNameToIdMap.get(tableName);
|
|
|
+ String mapKey = (tableId != null ? tableId + "_" : "") + cellCode_ref + "_" + period;
|
|
|
+ String value = globalCellCodeMap.get(mapKey);
|
|
|
|
|
|
- if (StringUtil.isNotEmpty(calculationTemplateId)) {
|
|
|
- // 有跨表引用
|
|
|
- if (cellCodeToRowIdMap.containsKey(referencedCell)) {
|
|
|
- // 单元格在当前表
|
|
|
- value = globalCellCodeMap.get(mapKey);
|
|
|
- } else {
|
|
|
- // 单元格在跨表
|
|
|
- value = globalCellCodeMap.get(calculationTemplateId + "_" + mapKey);
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 没有跨表引用,只查当前表
|
|
|
- value = globalCellCodeMap.get(mapKey);
|
|
|
+ String displayValue = StringUtil.isNotEmpty(value) ? value : "0";
|
|
|
+ referencedCellsDebug.add(tableName + "." + cellCode_ref + "=" + displayValue);
|
|
|
+ processedCells.add(cellCode_ref);
|
|
|
+ if (StringUtil.isNotEmpty(value)) {
|
|
|
+ hasAnyReferencedValue = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理不含表名的单元格引用(当前表)
|
|
|
+ // 避免重复处理已经在 "表名.单元格" 中出现过的单元格
|
|
|
+ matcher = cellPattern.matcher(calculationFormula);
|
|
|
+ while (matcher.find()) {
|
|
|
+ String referencedCell = matcher.group();
|
|
|
+ // 检查是否已在 "表名.单元格" 中处理过
|
|
|
+ if (processedCells.contains(referencedCell)) {
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
+ String mapKey = referencedCell + "_" + period;
|
|
|
+ String value = globalCellCodeMap.get(mapKey);
|
|
|
+
|
|
|
String displayValue = StringUtil.isNotEmpty(value) ? value : "0";
|
|
|
referencedCellsDebug.add(referencedCell + "=" + displayValue);
|
|
|
if (StringUtil.isNotEmpty(value)) {
|
|
|
@@ -2641,24 +2760,34 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
String inputValueStr = rowData.get(period);
|
|
|
|
|
|
try {
|
|
|
- // 将公式中的单元格引用替换为 cellCode_年限 的形式
|
|
|
- // 例如:(A1+A2+A3) -> (A1_2024+A2_2024+A3_2024)
|
|
|
- // 对于跨表引用,需要加上表ID前缀:(B1) -> (calculationTemplateId_B1_2024)
|
|
|
+ // 将公式中的单元格引用替换为带年限的形式
|
|
|
+ // 例如:(A1+A2) -> (A1_2024+A2_2024)
|
|
|
+ // 例如:(表1.Q1+表1.Q3) -> (tableId1_Q1_2024+tableId1_Q3_2024)
|
|
|
+ // 例如:(表1.Q1+A1) -> (tableId1_Q1_2024+A1_2024)
|
|
|
String formulaWithPeriod = calculationFormula;
|
|
|
- matcher = pattern.matcher(calculationFormula);
|
|
|
+
|
|
|
+ // 先处理 "表名.单元格" 格式
|
|
|
+ matcher = tableRefPattern.matcher(formulaWithPeriod);
|
|
|
StringBuffer sb = new StringBuffer();
|
|
|
while (matcher.find()) {
|
|
|
- String referencedCell = matcher.group();
|
|
|
- String mapKey = referencedCell + "_" + period;
|
|
|
-
|
|
|
- // 检查是否是跨表引用
|
|
|
- String currentTableRowId = cellCodeToRowIdMap.get(referencedCell);
|
|
|
- if (StringUtil.isEmpty(currentTableRowId) && StringUtil.isNotEmpty(calculationTemplateId)) {
|
|
|
- // 是跨表引用,使用表ID前缀
|
|
|
- mapKey = calculationTemplateId + "_" + mapKey;
|
|
|
- }
|
|
|
+ String tableName = matcher.group(1);
|
|
|
+ String cellCode_ref = matcher.group(2);
|
|
|
+ String tableId = tableNameToIdMap.get(tableName);
|
|
|
+ String mapKey = (tableId != null ? tableId + "_" : "") + cellCode_ref + "_" + period;
|
|
|
+ matcher.appendReplacement(sb, java.util.regex.Matcher.quoteReplacement(mapKey));
|
|
|
+ }
|
|
|
+ matcher.appendTail(sb);
|
|
|
+ formulaWithPeriod = sb.toString();
|
|
|
|
|
|
- matcher.appendReplacement(sb, mapKey);
|
|
|
+ // 处理不含表名的单元格引用(当前表)
|
|
|
+ // 使用负向断言避免匹配已被替换的部分(如 tableId_A2_2026 中的 A2)
|
|
|
+ java.util.regex.Pattern cellPatternWithNegativeLookbehind = java.util.regex.Pattern.compile("(?<!_)([A-Z]+\\d+)(?!_)");
|
|
|
+ matcher = cellPatternWithNegativeLookbehind.matcher(formulaWithPeriod);
|
|
|
+ sb = new StringBuffer();
|
|
|
+ while (matcher.find()) {
|
|
|
+ String referencedCell = matcher.group(1);
|
|
|
+ String mapKey = referencedCell + "_" + period;
|
|
|
+ matcher.appendReplacement(sb, java.util.regex.Matcher.quoteReplacement(mapKey));
|
|
|
}
|
|
|
matcher.appendTail(sb);
|
|
|
formulaWithPeriod = sb.toString();
|
|
|
@@ -2742,8 +2871,8 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
jep.setAllowUndeclared(true);
|
|
|
jep.setImplicitMul(true);
|
|
|
|
|
|
- // 提取公式中所有的单元格引用(如 A1_2024, Q2_2024)
|
|
|
- java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("[A-Z]+\\d+_\\d{4}");
|
|
|
+ // 提取公式中所有的单元格引用(支持两种格式:A1_2024 或 tableId_A1_2024)
|
|
|
+ java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(?:\\d+_)?[A-Z]+\\d+_\\d{4}");
|
|
|
java.util.regex.Matcher matcher = pattern.matcher(formula);
|
|
|
Set<String> referencedCells = new HashSet<>();
|
|
|
while (matcher.find()) {
|
|
|
@@ -2752,6 +2881,9 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
// 为所有引用的单元格设置变量值
|
|
|
Map<String, Double> variableValues = new HashMap<>();
|
|
|
+ Map<String, String> cellCodeToVarName = new HashMap<>(); // 原始cellCode -> 合法变量名映射
|
|
|
+ String processedFormula = formula;
|
|
|
+
|
|
|
for (String cellCode : referencedCells) {
|
|
|
String value = cellCodeMap.get(cellCode);
|
|
|
Double numValue;
|
|
|
@@ -2766,12 +2898,21 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
// 如果单元格没有值(用户未填写),默认为0
|
|
|
numValue = 0.0;
|
|
|
}
|
|
|
+
|
|
|
+ // 如果变量名以数字开头,添加前缀 v_
|
|
|
+ String varName = cellCode;
|
|
|
+ if (Character.isDigit(cellCode.charAt(0))) {
|
|
|
+ varName = "v_" + cellCode;
|
|
|
+ processedFormula = processedFormula.replace(cellCode, varName);
|
|
|
+ }
|
|
|
+
|
|
|
variableValues.put(cellCode, numValue);
|
|
|
- jep.addVariable(cellCode, numValue);
|
|
|
+ cellCodeToVarName.put(cellCode, varName);
|
|
|
+ jep.addVariable(varName, numValue);
|
|
|
}
|
|
|
|
|
|
// 解析并计算公式
|
|
|
- jep.parseExpression(formula);
|
|
|
+ jep.parseExpression(processedFormula);
|
|
|
if (jep.hasError()) {
|
|
|
throw new IllegalArgumentException("公式语法错误,请检查模板配置。公式: " + formula + ", 错误: " + jep.getErrorInfo());
|
|
|
}
|
|
|
@@ -3869,6 +4010,50 @@ public class CostProjectTaskSurveyGenericController {
|
|
|
|
|
|
return style;
|
|
|
}
|
|
|
+ /**
|
|
|
+ * 自动检测模板的类型
|
|
|
+ */
|
|
|
+ private String detectTemplateType(String templateId) {
|
|
|
+ if (StringUtil.isEmpty(templateId)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 尝试从三种表类型中查找
|
|
|
+ if (costSurveyTemplateManager.getDetail(templateId) != null) {
|
|
|
+ return "1";
|
|
|
+ }
|
|
|
+ if (costSurveyFdTemplateManager.getDetail(templateId) != null) {
|
|
|
+ return "2";
|
|
|
+ }
|
|
|
+ if (costVerifyTemplateManager.get(templateId) != null) {
|
|
|
+ return "3";
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object getTemplateById(String type, String templateId) {
|
|
|
+ if ("1".equals(type)) {
|
|
|
+ return costSurveyTemplateManager.getDetail(templateId);
|
|
|
+ } else if ("2".equals(type)) {
|
|
|
+ return costSurveyFdTemplateManager.getDetail(templateId);
|
|
|
+ } else if ("3".equals(type)) {
|
|
|
+ return costVerifyTemplateManager.get(templateId);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从模板对象获取名称
|
|
|
+ */
|
|
|
+ private String getTemplateName(Object template) {
|
|
|
+ if (template instanceof CostSurveyTemplate) {
|
|
|
+ return ((CostSurveyTemplate) template).getSurveyTemplateName();
|
|
|
+ } else if (template instanceof CostSurveyFdTemplate) {
|
|
|
+ return ((CostSurveyFdTemplate) template).getSurveyTemplateName();
|
|
|
+ } else if (template instanceof CostVerifyTemplate) {
|
|
|
+ return ((CostVerifyTemplate) template).getSurveyTemplateName();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* 获取 item 的 calculation_template_id(跨表引用的模板ID)
|