diff --git a/src/main/java/com/xdap/self_development/listener/SheetDataListener.java b/src/main/java/com/xdap/self_development/listener/SheetDataListener.java index fd18180..69b7d79 100644 --- a/src/main/java/com/xdap/self_development/listener/SheetDataListener.java +++ b/src/main/java/com/xdap/self_development/listener/SheetDataListener.java @@ -50,17 +50,18 @@ public class SheetDataListener implements ReadListener { .map(ReadCellData::getStringValue) .filter(Objects::nonNull) .collect(Collectors.toSet()); +// 第一次读取数据时检查表头 +// if (!headerChecked) { +// checkHeaderAndSkipIfNeeded(context); +// headerChecked = true; +// } currentSheetName = context.readSheetHolder().getSheetName(); log.info("开始解析Sheet: {}", currentSheetName); } @Override public void invoke(Parameter data, AnalysisContext context) { - // 第一次读取数据时检查表头 -// if (!headerChecked) { -// checkHeaderAndSkipIfNeeded(context); -// headerChecked = true; -// } + // 如果sheet无效,直接返回,不处理数据 // if (!validSheet) { @@ -69,8 +70,7 @@ public class SheetDataListener implements ReadListener { if (data.getSubsystemName() == null) { problems.setFlag(true); - problems.getErrors().add(currentSheetName +"模板的参数"+data.getParameterName()+"的子系统字段为空"); - return; + problems.getErrors().add("模板导入失败:"+currentSheetName +"模板的参数"+data.getParameterName()+"的子系统字段为空"); } if (data.getPartsName() == null || data.getPartsName().isEmpty()) { diff --git a/src/main/java/com/xdap/self_development/schedule/ParameterValueCalculationTimer.java b/src/main/java/com/xdap/self_development/schedule/ParameterValueCalculationTimer.java index 75c9faa..00af2fb 100644 --- a/src/main/java/com/xdap/self_development/schedule/ParameterValueCalculationTimer.java +++ b/src/main/java/com/xdap/self_development/schedule/ParameterValueCalculationTimer.java @@ -11,6 +11,7 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -25,9 +26,18 @@ import java.util.stream.Collectors; public class ParameterValueCalculationTimer { @Resource private BusinessDatabase bd; + + private ScriptEngineManager scriptEngineManager; + private ScriptEngine scriptEngine; + + @PostConstruct + public void init() { + scriptEngineManager = new ScriptEngineManager(); + scriptEngine = scriptEngineManager.getEngineByName("JavaScript"); + } -// @Scheduled(cron = "0 0 0 * * ?") -// @Scheduled(cron = "0 */1 * * * *") + // 每天凌晨执行一次 +// @Scheduled(cron = "0 0 0 * * ?") public MyResponse dailyCalculate() { MyResponse myResponse = new MyResponse(); myResponse.setErrors(new ArrayList<>()); @@ -81,6 +91,8 @@ public class ParameterValueCalculationTimer { .collect(Collectors.toList()); // 5.遍历参数,计算需运算的参数值 + int successCount = 0; + int failureCount = 0; for (CalculationParameterView parameterValue : parameterValues) { if (Objects.equals(parameterValue.getParameterSource(), "公式计算")) { String formula = parameterValue.getNumberOrFormula(); @@ -93,29 +105,49 @@ public class ParameterValueCalculationTimer { toUpdate.add(parameterValue); log.info("参数【{}】计算结果:{}", parameterValue.getParameterName(), formattedResult); myResponse.getErrors().add("参数【"+parameterValue.getParameterName()+"】计算结果:"+formattedResult); + successCount++; } catch (Exception e) { log.error("参数【{}】计算失败:{},公式:{},模型ID:{}", parameterValue.getParameterName(), e.getMessage(), formula, modelId, e); myResponse.getErrors().add("参数【"+parameterValue.getParameterName()+"】计算失败:"+e.getMessage()+",公式:"+formula+"模型ID:"+modelId); parameterValue.setParameterValue(null); + toUpdate.add(parameterValue); // 仍然添加到更新列表,用于更新null值 + failureCount++; } } } // 批量插入或更新计算结果 -// if (!toUpdate.isEmpty()) { -// toUpdate.forEach(parameterValue -> { -// if (parameterValue.getParameterValue() != null) { -// bd.getBusinessDatabase() -// .table("engine_param_detail") -// .eq("id", parameterValue.getId()) -// .update("parameter_value", parameterValue.getParameterValue()) -// .doUpdate(); -// } -// }); -// } -// log.info("{}参数计算完成,成功处理{}个参数,失败参数已跳过", today, toUpdate.stream() -// .filter(p -> p.getParameterValue() != null).count()); + if (!toUpdate.isEmpty()) { + int updatedCount = 0; + for (CalculationParameterView parameterValue : toUpdate) { + try { + if (parameterValue.getParameterValue() != null) { + bd.getBusinessDatabase() + .table("engine_param_detail") + .eq("id", parameterValue.getId()) + .update("parameter_value", parameterValue.getParameterValue()) + .doUpdate(); + updatedCount++; + } else { + // 对于计算失败的参数,也更新为null值 + bd.getBusinessDatabase() + .table("engine_param_detail") + .eq("id", parameterValue.getId()) + .update("parameter_value", null) + .doUpdate(); + updatedCount++; + } + } catch (Exception e) { + log.error("参数【{}】更新数据库失败:{}", parameterValue.getParameterName(), e.getMessage(), e); + myResponse.getErrors().add("参数【"+parameterValue.getParameterName()+"】更新数据库失败:"+e.getMessage()); + } + } + log.info("{}参数计算完成,成功处理{}个参数,失败{}个参数,更新{}个参数", today, successCount, failureCount, updatedCount); + } else { + log.info("{}参数计算完成,无需更新任何参数", today); + } + myResponse.getErrors().add("参数计算完成,成功处理"+successCount+"个参数,失败"+failureCount+"个参数"); return myResponse; } @@ -153,12 +185,10 @@ public class ParameterValueCalculationTimer { private Double calculateWithScriptEngine(String expression) { try { - ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine engine = manager.getEngineByName("JavaScript"); if (!isSafeExpression(expression)) { throw new SecurityException("表达式包含不安全内容"); } - Object result = engine.eval(expression); + Object result = scriptEngine.eval(expression); if (result instanceof Number) { return ((Number) result).doubleValue(); } @@ -171,11 +201,11 @@ public class ParameterValueCalculationTimer { private boolean isSafeExpression(String expression) { // 允许的字符:数字、基本运算符、括号、数学常量和函数 - String safePattern = "^[0-9+\\-*/().\\sMathPIEsincostanlogsqrtabs]*$"; + String safePattern = "^[0-9+\\-*/().\\sMathPIEsincostanlogsqrtabs]+$"; if (!expression.matches(safePattern)) { return false; } - String[] dangerousKeywords = {"java", "class", "runtime", "exec", "system", "process", "file"}; + String[] dangerousKeywords = {"java", "class", "runtime", "exec", "system", "process", "file", "import"}; for (String keyword : dangerousKeywords) { if (expression.toLowerCase().contains(keyword)) { return false; diff --git a/src/main/java/com/xdap/self_development/service/impl/TemplateServiceImpl.java b/src/main/java/com/xdap/self_development/service/impl/TemplateServiceImpl.java index 6f6510e..1eeb4bd 100644 --- a/src/main/java/com/xdap/self_development/service/impl/TemplateServiceImpl.java +++ b/src/main/java/com/xdap/self_development/service/impl/TemplateServiceImpl.java @@ -13,6 +13,7 @@ import com.xdap.self_development.listener.SheetDataListener; import com.xdap.self_development.pojo.*; import com.xdap.self_development.service.TemplateService; import com.xdap.self_development.utils.FormulaValidator; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -245,6 +246,10 @@ public class TemplateServiceImpl implements TemplateService { return; } + if (problems.getFlag()) { + return; + } + // 验证公式 for (Parameter data : dataList) { data.setCreatedBy(createBy); @@ -252,7 +257,7 @@ public class TemplateServiceImpl implements TemplateService { Map validationResult = FormulaValidator.validateFormulaDetailed( data.getNumberOrFormula()); if (!(Boolean) validationResult.get("isValid")) { - String errorMsg = String.format("模板'%s'的参数'%s'公式格式错误, %s", + String errorMsg = String.format("模板导入失败:"+"模板'%s'的参数'%s'公式格式错误, %s", sheetName, data.getParameterName(), validationResult.get("errorMessage")); problems.setFlag(true); @@ -274,7 +279,7 @@ public class TemplateServiceImpl implements TemplateService { // 页名和模板名不一致不允许导入 if (!Objects.equals(templateName, sheetName)) { problems.setFlag(true); - problems.getErrors().add("Excel页名与模板名不一致"); + problems.getErrors().add("\"模板导入失败:\"+Excel页名与模板名不一致"); return; } } @@ -297,14 +302,15 @@ public class TemplateServiceImpl implements TemplateService { .doQuery(TemplateAndParameter.class); // 最新模板有参数 if (!list.isEmpty()) { + // insertAndUpdateParameters(List dataList, Template lastTemplate) problems.setFlag(true); - problems.getErrors().add("模板‘"+templateName+"’已有参数,无法导入"); + problems.getErrors().add("模板导入失败:"+"模板‘"+templateName+"’已有参数,无法导入"); return; } break; case "APPROVING": problems.setFlag(true); - problems.getErrors().add("模板‘"+templateName+"‘的最新版本正在审核中,无法导入"); + problems.getErrors().add("模板导入失败:"+"模板‘"+templateName+"‘的最新版本正在审核中,无法导入"); return; // 最新版本已发布,增加版本号新增 default: @@ -395,4 +401,169 @@ public class TemplateServiceImpl implements TemplateService { return 1; } } + + private void insertAndUpdateParameters( + List dataList, Template lastTemplate + ) { + // 获取原模板参数 + List parameters = bd.getBusinessDatabase().viewQueryMode(true) + .view("selectParametersByTemplateId") + .setVar("template_id", lastTemplate.getId()) + .doQuery(Parameter.class); + + // 筛选变更和新增的参数 + ProcessResult processResult = processParameters(parameters, dataList); + List toAdd = processResult.getToAdd(); + List toUpdate = processResult.getToUpdate(); + + if (!toAdd.isEmpty()) { + // 保存参数 + bd.getBusinessDatabase().doBatchInsert(toAdd); + List relations = new ArrayList<>(); + + // 保存关联关系 + toAdd.forEach(parameter -> { + TemplateAndParameter relation = new TemplateAndParameter(); + RevisionRecord record = new RevisionRecord(); + relation.setTemplateId(lastTemplate.getId()); + relation.setParameterId(parameter.getId()); + relations.add(relation); + }); + bd.getBusinessDatabase().doBatchInsert(relations); + + // 保存修改记录 + List revisionRecords = toAdd.stream() + .map(parameter -> { + return new RevisionRecord( + lastTemplate.getTemplateName(), + lastTemplate.getVersion(), + parameter.getParameterNumber(), + "新增参数", + lastTemplate.getId(), + null, + Timestamp.valueOf(LocalDateTime.now()), + parameter.getId() + ); + }) + .collect(Collectors.toList()); + bd.getBusinessDatabase().doBatchInsert(revisionRecords); + } + + if (!toUpdate.isEmpty()) { + + toUpdate.forEach(parameterChange -> { + // 更新参数 + parameterChange.getUpdated().setId(parameterChange.getOriginal().getId()); + bd.getBusinessDatabase() + .eq("id", parameterChange.getUpdated().getId()) + .doUpdate(parameterChange.getUpdated()); + + // 保存修改记录 + RevisionRecord record = new RevisionRecord( + lastTemplate.getTemplateName(), + lastTemplate.getVersion(), + parameterChange.getUpdated().getParameterNumber(), + parameterChange.getChangeDescription(), + lastTemplate.getId(), + parameterChange.getOriginal().getId(), + Timestamp.valueOf(LocalDateTime.now()), + parameterChange.getUpdated().getId() + ); + bd.getBusinessDatabase().doInsert(record); + }); + } + } + + private String getParameterChange(Parameter originalParameter, Parameter updatedParameter) { + return handleParameterChange("参数名", originalParameter.getParameterName(), updatedParameter.getParameterName()) + + handleParameterChange("参数类型", originalParameter.getParameterType(), updatedParameter.getParameterType()) + + handleParameterChange("子系统", originalParameter.getSubsystemName(), updatedParameter.getSubsystemName()) + + handleParameterChange("部件名", originalParameter.getPartsName(), updatedParameter.getPartsName()) + + handleParameterChange("单位名", originalParameter.getUnit(), updatedParameter.getUnit()) + + handleParameterChange("参数来源", originalParameter.getParameterSource(), updatedParameter.getParameterSource()) + + handleParameterChange("公式或编号", originalParameter.getNumberOrFormula(), updatedParameter.getNumberOrFormula()) + + handleParameterChange("部门名", originalParameter.getDepartment(), updatedParameter.getDepartment()); + } + + private String handleParameterChange(String name, String originalParameter, String updatedParameter) { + StringBuilder sb = new StringBuilder(); + if (!Objects.equals(originalParameter, updatedParameter)) { + if (originalParameter == null || originalParameter.isEmpty()) { + if (updatedParameter == null || updatedParameter.isEmpty()) { + sb.append(name).append(": ").append("变更为空值").append("\n"); + } else { + sb.append(name).append(": ").append("变更为: '").append(updatedParameter).append("' ").append("\n"); + } + } else { + if (updatedParameter == null || updatedParameter.isEmpty()) { + sb.append(name).append(": '").append(originalParameter).append("' 变更为 空值").append("\n"); + } else { + sb.append(name).append(": '").append(originalParameter).append("' 变更为 '").append(updatedParameter).append("'\n"); + } + } + } + return sb.toString(); + } + + @Getter + public static class ProcessResult { + private List toAdd; + private List toUpdate; + + public ProcessResult(List toAdd, List toUpdate) { + this.toAdd = toAdd; + this.toUpdate = toUpdate; + } + + } + + @Getter + public static class ParameterChange { + private Parameter original; + private Parameter updated; + private String changeDescription; + + public ParameterChange(Parameter original, Parameter updated, String changeDescription) { + this.original = original; + this.updated = updated; + this.changeDescription = changeDescription; + } + + } + + public ProcessResult processParameters(List oldList, List newList) { + // 过滤掉newList中number为空的项 + List validNewList = newList.stream() + .filter(p -> p.getParameterNumber() != null + && !p.getParameterNumber().toString().trim().isEmpty()) + .collect(Collectors.toList()); + + List toAdd = new ArrayList<>(); + List toUpdate = new ArrayList<>(); + + // 创建旧列表中number到Parameter的映射 + Map oldMap = oldList.stream() + .filter(p -> p.getParameterNumber() != null + && !p.getParameterNumber().toString().trim().isEmpty()) + .collect(Collectors.toMap( + Parameter::getParameterNumber, + p -> p, + (existing, replacement) -> existing)); + + // 处理新列表中的每个参数 + for (Parameter newParam : validNewList) { + Integer number = newParam.getParameterNumber(); + + if (oldMap.containsKey(number)) { + Parameter oldParam = oldMap.get(number); + String changeDescription = getParameterChange(oldParam, newParam); + ParameterChange change = new ParameterChange(oldParam, newParam, changeDescription); + toUpdate.add(change); + oldMap.remove(number); + } else { + toAdd.add(newParam); + } + } + return new ProcessResult(toAdd, toUpdate); + } }