修改:模板导入冲突参数解决(预览版)

This commit is contained in:
zjh 2025-12-18 17:13:56 +08:00
parent 906fc81dc5
commit 221869b7f1
3 changed files with 232 additions and 31 deletions

View File

@ -50,17 +50,18 @@ public class SheetDataListener implements ReadListener<Parameter> {
.map(ReadCellData::getStringValue) .map(ReadCellData::getStringValue)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
// 第一次读取数据时检查表头
// if (!headerChecked) {
// checkHeaderAndSkipIfNeeded(context);
// headerChecked = true;
// }
currentSheetName = context.readSheetHolder().getSheetName(); currentSheetName = context.readSheetHolder().getSheetName();
log.info("开始解析Sheet: {}", currentSheetName); log.info("开始解析Sheet: {}", currentSheetName);
} }
@Override @Override
public void invoke(Parameter data, AnalysisContext context) { public void invoke(Parameter data, AnalysisContext context) {
// 第一次读取数据时检查表头
// if (!headerChecked) {
// checkHeaderAndSkipIfNeeded(context);
// headerChecked = true;
// }
// 如果sheet无效直接返回不处理数据 // 如果sheet无效直接返回不处理数据
// if (!validSheet) { // if (!validSheet) {
@ -69,8 +70,7 @@ public class SheetDataListener implements ReadListener<Parameter> {
if (data.getSubsystemName() == null) { if (data.getSubsystemName() == null) {
problems.setFlag(true); problems.setFlag(true);
problems.getErrors().add(currentSheetName +"模板的参数"+data.getParameterName()+"的子系统字段为空"); problems.getErrors().add("模板导入失败:"+currentSheetName +"模板的参数"+data.getParameterName()+"的子系统字段为空");
return;
} }
if (data.getPartsName() == null || data.getPartsName().isEmpty()) { if (data.getPartsName() == null || data.getPartsName().isEmpty()) {

View File

@ -11,6 +11,7 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager; import javax.script.ScriptEngineManager;
@ -26,8 +27,17 @@ public class ParameterValueCalculationTimer {
@Resource @Resource
private BusinessDatabase bd; private BusinessDatabase bd;
// @Scheduled(cron = "0 0 0 * * ?") private ScriptEngineManager scriptEngineManager;
// @Scheduled(cron = "0 */1 * * * *") private ScriptEngine scriptEngine;
@PostConstruct
public void init() {
scriptEngineManager = new ScriptEngineManager();
scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
}
// 每天凌晨执行一次
// @Scheduled(cron = "0 0 0 * * ?")
public MyResponse dailyCalculate() { public MyResponse dailyCalculate() {
MyResponse myResponse = new MyResponse(); MyResponse myResponse = new MyResponse();
myResponse.setErrors(new ArrayList<>()); myResponse.setErrors(new ArrayList<>());
@ -81,6 +91,8 @@ public class ParameterValueCalculationTimer {
.collect(Collectors.toList()); .collect(Collectors.toList());
// 5.遍历参数计算需运算的参数值 // 5.遍历参数计算需运算的参数值
int successCount = 0;
int failureCount = 0;
for (CalculationParameterView parameterValue : parameterValues) { for (CalculationParameterView parameterValue : parameterValues) {
if (Objects.equals(parameterValue.getParameterSource(), "公式计算")) { if (Objects.equals(parameterValue.getParameterSource(), "公式计算")) {
String formula = parameterValue.getNumberOrFormula(); String formula = parameterValue.getNumberOrFormula();
@ -93,29 +105,49 @@ public class ParameterValueCalculationTimer {
toUpdate.add(parameterValue); toUpdate.add(parameterValue);
log.info("参数【{}】计算结果:{}", parameterValue.getParameterName(), formattedResult); log.info("参数【{}】计算结果:{}", parameterValue.getParameterName(), formattedResult);
myResponse.getErrors().add("参数【"+parameterValue.getParameterName()+"】计算结果:"+formattedResult); myResponse.getErrors().add("参数【"+parameterValue.getParameterName()+"】计算结果:"+formattedResult);
successCount++;
} catch (Exception e) { } catch (Exception e) {
log.error("参数【{}】计算失败:{},公式:{}模型ID{}", log.error("参数【{}】计算失败:{},公式:{}模型ID{}",
parameterValue.getParameterName(), e.getMessage(), formula, modelId, e); parameterValue.getParameterName(), e.getMessage(), formula, modelId, e);
myResponse.getErrors().add("参数【"+parameterValue.getParameterName()+"】计算失败:"+e.getMessage()+",公式:"+formula+"模型ID"+modelId); myResponse.getErrors().add("参数【"+parameterValue.getParameterName()+"】计算失败:"+e.getMessage()+",公式:"+formula+"模型ID"+modelId);
parameterValue.setParameterValue(null); parameterValue.setParameterValue(null);
toUpdate.add(parameterValue); // 仍然添加到更新列表用于更新null值
failureCount++;
} }
} }
} }
// 批量插入或更新计算结果 // 批量插入或更新计算结果
// if (!toUpdate.isEmpty()) { if (!toUpdate.isEmpty()) {
// toUpdate.forEach(parameterValue -> { int updatedCount = 0;
// if (parameterValue.getParameterValue() != null) { for (CalculationParameterView parameterValue : toUpdate) {
// bd.getBusinessDatabase() try {
// .table("engine_param_detail") if (parameterValue.getParameterValue() != null) {
// .eq("id", parameterValue.getId()) bd.getBusinessDatabase()
// .update("parameter_value", parameterValue.getParameterValue()) .table("engine_param_detail")
// .doUpdate(); .eq("id", parameterValue.getId())
// } .update("parameter_value", parameterValue.getParameterValue())
// }); .doUpdate();
// } updatedCount++;
// log.info("{}参数计算完成,成功处理{}个参数,失败参数已跳过", today, toUpdate.stream() } else {
// .filter(p -> p.getParameterValue() != null).count()); // 对于计算失败的参数也更新为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; return myResponse;
} }
@ -153,12 +185,10 @@ public class ParameterValueCalculationTimer {
private Double calculateWithScriptEngine(String expression) { private Double calculateWithScriptEngine(String expression) {
try { try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
if (!isSafeExpression(expression)) { if (!isSafeExpression(expression)) {
throw new SecurityException("表达式包含不安全内容"); throw new SecurityException("表达式包含不安全内容");
} }
Object result = engine.eval(expression); Object result = scriptEngine.eval(expression);
if (result instanceof Number) { if (result instanceof Number) {
return ((Number) result).doubleValue(); return ((Number) result).doubleValue();
} }
@ -171,11 +201,11 @@ public class ParameterValueCalculationTimer {
private boolean isSafeExpression(String expression) { private boolean isSafeExpression(String expression) {
// 允许的字符数字基本运算符括号数学常量和函数 // 允许的字符数字基本运算符括号数学常量和函数
String safePattern = "^[0-9+\\-*/().\\sMathPIEsincostanlogsqrtabs]*$"; String safePattern = "^[0-9+\\-*/().\\sMathPIEsincostanlogsqrtabs]+$";
if (!expression.matches(safePattern)) { if (!expression.matches(safePattern)) {
return false; 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) { for (String keyword : dangerousKeywords) {
if (expression.toLowerCase().contains(keyword)) { if (expression.toLowerCase().contains(keyword)) {
return false; return false;

View File

@ -13,6 +13,7 @@ import com.xdap.self_development.listener.SheetDataListener;
import com.xdap.self_development.pojo.*; import com.xdap.self_development.pojo.*;
import com.xdap.self_development.service.TemplateService; import com.xdap.self_development.service.TemplateService;
import com.xdap.self_development.utils.FormulaValidator; import com.xdap.self_development.utils.FormulaValidator;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -245,6 +246,10 @@ public class TemplateServiceImpl implements TemplateService {
return; return;
} }
if (problems.getFlag()) {
return;
}
// 验证公式 // 验证公式
for (Parameter data : dataList) { for (Parameter data : dataList) {
data.setCreatedBy(createBy); data.setCreatedBy(createBy);
@ -252,7 +257,7 @@ public class TemplateServiceImpl implements TemplateService {
Map<String, Object> validationResult = FormulaValidator.validateFormulaDetailed( Map<String, Object> validationResult = FormulaValidator.validateFormulaDetailed(
data.getNumberOrFormula()); data.getNumberOrFormula());
if (!(Boolean) validationResult.get("isValid")) { if (!(Boolean) validationResult.get("isValid")) {
String errorMsg = String.format("模板'%s'的参数'%s'公式格式错误, %s", String errorMsg = String.format("模板导入失败:"+"模板'%s'的参数'%s'公式格式错误, %s",
sheetName, data.getParameterName(), sheetName, data.getParameterName(),
validationResult.get("errorMessage")); validationResult.get("errorMessage"));
problems.setFlag(true); problems.setFlag(true);
@ -274,7 +279,7 @@ public class TemplateServiceImpl implements TemplateService {
// 页名和模板名不一致不允许导入 // 页名和模板名不一致不允许导入
if (!Objects.equals(templateName, sheetName)) { if (!Objects.equals(templateName, sheetName)) {
problems.setFlag(true); problems.setFlag(true);
problems.getErrors().add("Excel页名与模板名不一致"); problems.getErrors().add("\"模板导入失败:\"+Excel页名与模板名不一致");
return; return;
} }
} }
@ -297,14 +302,15 @@ public class TemplateServiceImpl implements TemplateService {
.doQuery(TemplateAndParameter.class); .doQuery(TemplateAndParameter.class);
// 最新模板有参数 // 最新模板有参数
if (!list.isEmpty()) { if (!list.isEmpty()) {
// insertAndUpdateParameters(List<Parameter> dataList, Template lastTemplate)
problems.setFlag(true); problems.setFlag(true);
problems.getErrors().add("模板"+templateName+"’已有参数,无法导入"); problems.getErrors().add("模板导入失败:"+"模板"+templateName+"’已有参数,无法导入");
return; return;
} }
break; break;
case "APPROVING": case "APPROVING":
problems.setFlag(true); problems.setFlag(true);
problems.getErrors().add("模板"+templateName+"‘的最新版本正在审核中,无法导入"); problems.getErrors().add("模板导入失败:"+"模板"+templateName+"‘的最新版本正在审核中,无法导入");
return; return;
// 最新版本已发布增加版本号新增 // 最新版本已发布增加版本号新增
default: default:
@ -395,4 +401,169 @@ public class TemplateServiceImpl implements TemplateService {
return 1; return 1;
} }
} }
private void insertAndUpdateParameters(
List<Parameter> dataList, Template lastTemplate
) {
// 获取原模板参数
List<Parameter> parameters = bd.getBusinessDatabase().viewQueryMode(true)
.view("selectParametersByTemplateId")
.setVar("template_id", lastTemplate.getId())
.doQuery(Parameter.class);
// 筛选变更和新增的参数
ProcessResult processResult = processParameters(parameters, dataList);
List<Parameter> toAdd = processResult.getToAdd();
List<ParameterChange> toUpdate = processResult.getToUpdate();
if (!toAdd.isEmpty()) {
// 保存参数
bd.getBusinessDatabase().doBatchInsert(toAdd);
List<TemplateAndParameter> 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<RevisionRecord> 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<Parameter> toAdd;
private List<ParameterChange> toUpdate;
public ProcessResult(List<Parameter> toAdd, List<ParameterChange> 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<Parameter> oldList, List<Parameter> newList) {
// 过滤掉newList中number为空的项
List<Parameter> validNewList = newList.stream()
.filter(p -> p.getParameterNumber() != null
&& !p.getParameterNumber().toString().trim().isEmpty())
.collect(Collectors.toList());
List<Parameter> toAdd = new ArrayList<>();
List<ParameterChange> toUpdate = new ArrayList<>();
// 创建旧列表中number到Parameter的映射
Map<Object, Parameter> 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);
}
} }