package jnpf.elnService.formula.impl; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.util.IdUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import jnpf.audit.model.AuditEventDTO; import jnpf.audit.sdk.AuditClient; import jnpf.base.UserInfo; import jnpf.database.util.TenantDataSourceUtil; import jnpf.elnEntity.formula.ElnFormulaDefEntity; import jnpf.elnEntity.formula.ElnFormulaForms; import jnpf.elnEntity.formula.ElnFormulaParameterSlotEntity; import jnpf.elnEntity.formula.ElnFormulaVersionEntity; import jnpf.elnEntity.formula.ElnFormulaVos; import jnpf.elnEntity.formula.ElnTemplateFormulaBindingEntity; import jnpf.elnEntity.template.ElnTemplateVersionEntity; import jnpf.elnFormula.FormulaParser; import jnpf.elnFormula.FormulaType; import jnpf.elnMapper.ElnFormulaDefMapper; import jnpf.elnMapper.ElnFormulaParameterSlotMapper; import jnpf.elnMapper.ElnFormulaVersionMapper; import jnpf.elnMapper.ElnTemplateMapper; import jnpf.elnMapper.ElnTemplateFormulaBindingMapper; import jnpf.elnService.formula.ElnFormulaAvailability; import jnpf.elnService.formula.ElnFormulaCatalogService; import jnpf.elnService.formula.FormulaValidationService; import jnpf.elnService.impl.ElnFieldCodeGenerator; import jnpf.exception.DataException; import jnpf.model.tenant.TenantVO; import jnpf.util.JsonUtil; import jnpf.util.UserProvider; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Supplier; @Service @Transactional(rollbackFor = Exception.class) public class ElnFormulaCatalogServiceImpl implements ElnFormulaCatalogService, ElnFormulaAvailability { private static final String MENU_PERMISSION = "dianziJiluGongshiGuanli"; private static final String ENABLED = "enabled"; private static final String DISABLED = "disabled"; private static final String DRAFT = "DRAFT"; private static final String PRE_RELEASE = "PRE_RELEASE"; private static final String PUBLISHED = "PUBLISHED"; private static final String CANCELLED = "CANCELLED"; private static final String ACTIVE = "ACTIVE"; private static final String PENDING = "PENDING"; private static final int CODE_RETRY_LIMIT = 3; private final FormulaCatalogStore store; private final FormulaValidationService validationService; private final DraftTemplateReferenceChecker referenceChecker; private final Supplier userContextSupplier; private final Supplier idSupplier; private final ElnFieldCodeGenerator codeGenerator; private final Consumer auditRecorder; private final BooleanSupplier maintainPermission; @Autowired public ElnFormulaCatalogServiceImpl(ElnFormulaDefMapper definitionMapper, ElnFormulaVersionMapper versionMapper, ElnFormulaParameterSlotMapper slotMapper, ElnTemplateMapper templateMapper, ElnTemplateFormulaBindingMapper templateFormulaBindingMapper, FormulaValidationService validationService, ObjectProvider auditClientProvider, ObjectProvider checkerProvider) { this(new MyBatisFormulaCatalogStore(definitionMapper, versionMapper, slotMapper, templateMapper, templateFormulaBindingMapper), validationService, checker(checkerProvider), ElnFormulaCatalogServiceImpl::currentUserContext, IdUtil::getSnowflakeNextIdStr, new ElnFieldCodeGenerator(() -> IdUtil.fastSimpleUUID().substring(0, 8), 8), event -> { AuditClient client = auditClientProvider.getIfAvailable(); if (client != null) { client.record(event); } }, ElnFormulaCatalogServiceImpl::hasMaintainPermission); } ElnFormulaCatalogServiceImpl(FormulaCatalogStore store, FormulaValidationService validationService, DraftTemplateReferenceChecker referenceChecker, Supplier userContextSupplier, Supplier idSupplier, ElnFieldCodeGenerator codeGenerator, Consumer auditRecorder, BooleanSupplier maintainPermission) { this.store = store; this.validationService = validationService; this.referenceChecker = resolveChecker(referenceChecker); this.userContextSupplier = userContextSupplier; this.idSupplier = idSupplier; this.codeGenerator = codeGenerator; this.auditRecorder = auditRecorder; this.maintainPermission = maintainPermission; } @Override public ElnFormulaVos.Detail create(ElnFormulaForms.Create form) { UserContext context = requireCatalogAccess(); validateDefinition(form == null ? null : form.getGongshiMingcheng(), form == null ? null : form.getYewuYongtu(), form == null ? null : form.getBeizhu()); validateVersionRules(form); ElnFormulaDefEntity definition = insertDefinition(context, form); audit("ELN_FORMULA_CREATE", "eln_formula_def", definition.getId(), context, null, definition, null); ElnFormulaVersionEntity version = newVersion(context, definition.getId(), 1); applyVersion(version, form); store.insertVersion(version); List slots = replaceDraftSlots( context, version, Collections.emptyList(), form.getParameters()); audit("ELN_FORMULA_VERSION_CREATE", "eln_formula_version", version.getId(), context, null, versionSnapshot(version, slots), null); return toDetail(definition, Collections.singletonList(version)); } @Override public ElnFormulaVos.Detail createDraft(ElnFormulaForms.CreateDraft form) { UserContext context = requireCatalogAccess(); validateDefinition(form == null ? null : form.getGongshiMingcheng(), form == null ? null : form.getYewuYongtu(), form == null ? null : form.getBeizhu()); List parameters = form == null ? null : form.getParameters(); if (parameters != null) { for (ElnFormulaForms.Parameter parameter : parameters) { validateParameter(parameter); } } ElnFormulaForms.Create definitionForm = new ElnFormulaForms.Create(); definitionForm.setGongshiMingcheng(form.getGongshiMingcheng()); definitionForm.setYewuYongtu(form.getYewuYongtu()); definitionForm.setBeizhu(form.getBeizhu()); ElnFormulaDefEntity definition = insertDefinition(context, definitionForm); audit("ELN_FORMULA_CREATE", "eln_formula_def", definition.getId(), context, null, definition, null); ElnFormulaVersionEntity version = newVersion(context, definition.getId(), 1); version.setBiaodashi(""); version.setJieguoLeixing(FormulaType.DECIMAL.name()); version.setXiuyueXiaoshuWei(null); version.setXiuyueMoshi(null); store.insertVersion(version); List slots = replaceDraftSlots( context, version, Collections.emptyList(), parameters); audit("ELN_FORMULA_VERSION_CREATE", "eln_formula_version", version.getId(), context, null, versionSnapshot(version, slots), null); return toDetail(definition, Collections.singletonList(version)); } @Override public ElnFormulaVos.Detail updateDefinition(String formulaId, ElnFormulaForms.UpdateDefinition form) { UserContext context = requireCatalogAccess(); validateDefinition(form == null ? null : form.getGongshiMingcheng(), form == null ? null : form.getYewuYongtu(), form == null ? null : form.getBeizhu()); ElnFormulaDefEntity definition = requireDefinition(context.getTenantId(), formulaId, true); ElnFormulaDefEntity before = copyDefinition(definition); definition.setGongshiMingcheng(form.getGongshiMingcheng().trim()); definition.setYewuYongtu(trimToNull(form.getYewuYongtu())); definition.setBeizhu(trimToNull(form.getBeizhu())); definition.setFLastModifyUserId(context.getUserId()); definition.setFLastModifyTime(new Date()); store.updateDefinition(definition); audit("ELN_FORMULA_UPDATE", "eln_formula_def", definition.getId(), context, before, definition, null); return toDetail(definition, store.listVersions(context.getTenantId(), definition.getId())); } @Override @Transactional(readOnly = true) public IPage page(ElnFormulaForms.Query query) { UserContext context = requireCatalogAccess(); ElnFormulaForms.Query actual = query == null ? new ElnFormulaForms.Query() : query; if (actual.getCurrentPage() < 1 || actual.getPageSize() < 1) { throw new DataException("分页参数必须大于 0"); } IPage source = store.page(context.getTenantId(), actual); Set definitionIds = ids(source.getRecords()); Map referenceCounts = referenceCounts( store.listLiveTemplateFormulaBindings(context.getTenantId(), definitionIds)); Map> versionsByDefinition = new LinkedHashMap<>(); for (ElnFormulaVersionEntity version : store.listVersionsByDefinitionIds( context.getTenantId(), definitionIds)) { versionsByDefinition.computeIfAbsent(version.getGongshiDingyiId(), key -> new ArrayList<>()).add(version); } List records = new ArrayList<>(); for (ElnFormulaDefEntity definition : source.getRecords()) { ElnFormulaVos.ListItem item = toListItem(definition, versionsByDefinition.getOrDefault(definition.getId(), Collections.emptyList())); item.setMubanYinyongShu(referenceCounts.getOrDefault(definition.getId(), 0)); records.add(item); } return new Page(source.getCurrent(), source.getSize(), source.getTotal()) .setRecords(records); } @Override @Transactional(readOnly = true) public ElnFormulaVos.Detail detail(String formulaId) { UserContext context = requireCatalogAccess(); ElnFormulaDefEntity definition = requireDefinition(context.getTenantId(), formulaId, false); return toDetail(definition, store.listVersions(context.getTenantId(), definition.getId())); } @Override @Transactional(readOnly = true) public IPage pageTemplateReferences( String formulaId, ElnFormulaForms.ReferenceQuery query) { UserContext context = requireCatalogAccess(); ElnFormulaDefEntity definition = requireDefinition( context.getTenantId(), formulaId, false); ElnFormulaForms.ReferenceQuery actual = query == null ? new ElnFormulaForms.ReferenceQuery() : query; if (actual.getCurrentPage() < 1 || actual.getPageSize() < 1) { throw new DataException("分页参数必须大于 0"); } actual.setKeyword(trimToNull(actual.getKeyword())); return pageReferenceSummaries(context.getTenantId(), definition.getId(), actual); } @Override @Transactional(readOnly = true) public boolean isEnabled(String tenantId, String formulaId) { if (!StringUtils.hasText(tenantId) || !StringUtils.hasText(formulaId)) { return false; } ElnFormulaDefEntity definition = store.findDefinition( tenantId.trim(), formulaId.trim(), false); return definition != null && ENABLED.equals(definition.getBizEnabled()); } @Override public ElnFormulaVos.Version createVersion(String formulaId, ElnFormulaForms.CreateVersion form) { UserContext context = requireCatalogAccess(); ElnFormulaDefEntity definition = requireDefinition(context.getTenantId(), formulaId, true); requireEnabled(definition); if (form == null || !StringUtils.hasText(form.getSourceVersionId())) { throw new DataException("复制版本必须指定来源版本"); } ElnFormulaVersionEntity source = requireVersion( context.getTenantId(), form.getSourceVersionId(), false); requireSameDefinition(definition, source); if (!PRE_RELEASE.equals(source.getBizStatus()) && !PUBLISHED.equals(source.getBizStatus())) { throw new DataException("只能基于预发布或已发布版本创建新版本"); } ElnFormulaVersionEntity created = newVersion(context, definition.getId(), nextVersionNumber(context.getTenantId(), definition.getId())); copyVersionRules(source, created); store.insertVersion(created); List copied = new ArrayList<>(); for (ElnFormulaParameterSlotEntity sourceSlot : store.listSlots(context.getTenantId(), source.getId())) { ElnFormulaParameterSlotEntity slot = newSlot(context, created.getId()); slot.setCanshuBianma(sourceSlot.getCanshuBianma()); copySlotRules(sourceSlot, slot); store.insertSlot(slot); copied.add(slot); } audit("ELN_FORMULA_VERSION_CREATE", "eln_formula_version", created.getId(), context, null, versionSnapshot(created, copied), null); return toVersion(created, copied); } @Override public ElnFormulaVos.Version updateDraft(String versionId, ElnFormulaForms.UpdateVersion form) { UserContext context = requireCatalogAccess(); ElnFormulaVersionEntity version = requireVersion(context.getTenantId(), versionId, true); if (!DRAFT.equals(version.getBizStatus())) { throw new DataException("只有草稿版本可以修改"); } ElnFormulaDefEntity definition = requireDefinition( context.getTenantId(), version.getGongshiDingyiId(), true); requireEnabled(definition); validateVersionRules(form); List beforeSlots = store.listSlots( context.getTenantId(), version.getId()); Object before = versionSnapshot(copyVersion(version), copySlots(beforeSlots)); applyVersion(version, form); version.setFLastModifyUserId(context.getUserId()); version.setFLastModifyTime(new Date()); store.updateVersion(version); List afterSlots = replaceDraftSlots( context, version, beforeSlots, form.getParameters()); audit("ELN_FORMULA_VERSION_UPDATE", "eln_formula_version", version.getId(), context, before, versionSnapshot(version, afterSlots), null); return toVersion(version, afterSlots); } @Override public void preRelease(String versionId) { UserContext context = requireCatalogAccess(); ElnFormulaVersionEntity version = requireVersion(context.getTenantId(), versionId, true); if (!DRAFT.equals(version.getBizStatus())) { throw new DataException("只有草稿版本可以提交预发布"); } ElnFormulaDefEntity definition = requireDefinition( context.getTenantId(), version.getGongshiDingyiId(), true); requireEnabled(definition); List slots = store.listSlots( context.getTenantId(), version.getId()); validateStoredFormula(version, slots); changeVersionStatus(version, slots, PRE_RELEASE, null, context, "ELN_FORMULA_VERSION_PRE_RELEASE"); } @Override public void publish(String versionId) { UserContext context = requireCatalogAccess(); ElnFormulaVersionEntity version = requireVersion(context.getTenantId(), versionId, true); if (!PRE_RELEASE.equals(version.getBizStatus())) { throw new DataException("只有预发布版本可以发布"); } ElnFormulaDefEntity definition = requireDefinition( context.getTenantId(), version.getGongshiDingyiId(), true); requireEnabled(definition); List slots = store.listSlots( context.getTenantId(), version.getId()); Object before = publishSnapshot(copyDefinition(definition), copyVersion(version), slots); version.setBizStatus(PUBLISHED); version.setFLastModifyUserId(context.getUserId()); version.setFLastModifyTime(new Date()); store.updateVersion(version); definition.setDangqianGongshiBanbenId(version.getId()); definition.setFLastModifyUserId(context.getUserId()); definition.setFLastModifyTime(new Date()); store.updateDefinition(definition); audit("ELN_FORMULA_VERSION_PUBLISH", "eln_formula_version", version.getId(), context, before, publishSnapshot(definition, version, slots), null); } @Override public void cancel(String versionId, String reason) { UserContext context = requireCatalogAccess(); String actualReason = requireReason(reason); ElnFormulaVersionEntity version = requireVersion(context.getTenantId(), versionId, true); if (!DRAFT.equals(version.getBizStatus()) && !PRE_RELEASE.equals(version.getBizStatus())) { throw new DataException("只有草稿或预发布版本可以作废"); } if (PRE_RELEASE.equals(version.getBizStatus()) && referenceChecker.hasDraftReferences(context.getTenantId(), version.getId())) { throw new DataException("预发布公式版本仍被草稿模板引用,不能作废"); } List slots = store.listSlots( context.getTenantId(), version.getId()); changeVersionStatus(version, slots, CANCELLED, actualReason, context, "ELN_FORMULA_VERSION_CANCEL"); } @Override public void switchCurrent(String formulaId, String versionId, String reason) { UserContext context = requireCatalogAccess(); String actualReason = requireReason(reason); ElnFormulaDefEntity definition = requireDefinition(context.getTenantId(), formulaId, true); requireEnabled(definition); ElnFormulaVersionEntity version = requireVersion(context.getTenantId(), versionId, false); requireSameDefinition(definition, version); if (!PUBLISHED.equals(version.getBizStatus())) { throw new DataException("当前版本只能切换到已发布版本"); } ElnFormulaDefEntity before = copyDefinition(definition); definition.setDangqianGongshiBanbenId(version.getId()); definition.setFLastModifyUserId(context.getUserId()); definition.setFLastModifyTime(new Date()); store.updateDefinition(definition); audit("ELN_FORMULA_SWITCH_CURRENT", "eln_formula_def", definition.getId(), context, before, definition, actualReason); } @Override public void enable(String formulaId, String reason) { changeEnabled(formulaId, ENABLED, requireReason(reason), "ELN_FORMULA_ENABLE"); } @Override public void disable(String formulaId, String reason) { changeEnabled(formulaId, DISABLED, requireReason(reason), "ELN_FORMULA_DISABLE"); } private ElnFormulaDefEntity insertDefinition(UserContext context, ElnFormulaForms.Create form) { DuplicateKeyException collision = null; for (int attempt = 0; attempt < CODE_RETRY_LIMIT; attempt++) { ElnFormulaDefEntity definition = new ElnFormulaDefEntity(); initializeBase(definition, context); definition.setGongshiBianma(codeGenerator.generateSegmented( form.getGongshiMingcheng(), "formula", "f")); definition.setGongshiMingcheng(form.getGongshiMingcheng().trim()); definition.setYewuYongtu(trimToNull(form.getYewuYongtu())); definition.setBizEnabled(ENABLED); definition.setBeizhu(trimToNull(form.getBeizhu())); try { store.insertDefinition(definition); return definition; } catch (DuplicateKeyException duplicate) { collision = duplicate; } } throw new DataException(collision == null ? "公式编码生成失败" : "公式编码生成冲突,请重试"); } private List replaceDraftSlots( UserContext context, ElnFormulaVersionEntity version, List existing, List requested) { Map remaining = new LinkedHashMap<>(); for (ElnFormulaParameterSlotEntity slot : existing) { remaining.put(slot.getId(), slot); } List result = new ArrayList<>(); List forms = requested == null ? Collections.emptyList() : requested; int index = 0; for (ElnFormulaForms.Parameter form : forms) { validateParameter(form); ElnFormulaParameterSlotEntity slot; if (StringUtils.hasText(form.getId())) { slot = remaining.remove(form.getId().trim()); if (slot == null) { throw new DataException("参数槽不属于当前草稿版本"); } applySlot(slot, form, index, context); store.updateSlot(slot); } else { slot = insertGeneratedSlot(context, version.getId(), form, index); } result.add(slot); index++; } for (ElnFormulaParameterSlotEntity removed : remaining.values()) { store.deleteSlot(removed); } result.sort(Comparator.comparing(ElnFormulaParameterSlotEntity::getPaixu)); return result; } private ElnFormulaParameterSlotEntity insertGeneratedSlot( UserContext context, String versionId, ElnFormulaForms.Parameter form, int index) { DuplicateKeyException collision = null; for (int attempt = 0; attempt < CODE_RETRY_LIMIT; attempt++) { ElnFormulaParameterSlotEntity slot = newSlot(context, versionId); slot.setCanshuBianma(codeGenerator.generate( form.getCanshuMingcheng(), "parameter", "p")); if (FormulaParser.isReservedParameterCode(slot.getCanshuBianma())) { continue; } applySlot(slot, form, index, context); try { store.insertSlot(slot); return slot; } catch (DuplicateKeyException duplicate) { collision = duplicate; } } throw new DataException(collision == null ? "参数编码生成失败" : "参数编码生成冲突,请重试"); } private void validateStoredFormula(ElnFormulaVersionEntity version, List slots) { List parameters = new ArrayList<>(); for (ElnFormulaParameterSlotEntity slot : slots) { parameters.add(new FormulaValidationService.ParameterSlot(slot.getCanshuBianma(), slot.getCanshuMingcheng(), formulaType(slot.getShujuLeixing(), "参数类型无效"))); } FormulaValidationService.ValidationResult result = validationService.validate( new FormulaValidationService.FormulaValidationRequest(version.getBiaodashi(), parameters, formulaType(version.getJieguoLeixing(), "结果类型无效"), version.getXiuyueXiaoshuWei(), version.getXiuyueMoshi())); if (result.hasErrors()) { throw new DataException("公式校验存在错误,不能提交预发布"); } } private void changeEnabled(String formulaId, String target, String reason, String action) { UserContext context = requireCatalogAccess(); ElnFormulaDefEntity definition = requireDefinition(context.getTenantId(), formulaId, true); if (target.equals(definition.getBizEnabled())) { throw new DataException(ENABLED.equals(target) ? "公式已经启用" : "公式已经停用"); } ElnFormulaDefEntity before = copyDefinition(definition); definition.setBizEnabled(target); definition.setFLastModifyUserId(context.getUserId()); definition.setFLastModifyTime(new Date()); store.updateDefinition(definition); audit(action, "eln_formula_def", definition.getId(), context, before, definition, reason); } private void changeVersionStatus(ElnFormulaVersionEntity version, List slots, String status, String reason, UserContext context, String action) { Object before = versionSnapshot(copyVersion(version), copySlots(slots)); version.setBizStatus(status); version.setZuofeiYuanyin(reason); version.setFLastModifyUserId(context.getUserId()); version.setFLastModifyTime(new Date()); store.updateVersion(version); audit(action, "eln_formula_version", version.getId(), context, before, versionSnapshot(version, slots), reason); } private static void validateDefinition(String name, String purpose, String remark) { requireText(name, 200, "公式名称不能为空", "公式名称不能超过 200 个字符"); maxLength(purpose, 500, "业务用途不能超过 500 个字符"); maxLength(remark, 500, "备注不能超过 500 个字符"); } private static void validateVersionRules(ElnFormulaForms.VersionRule form) { if (form == null) { throw new DataException("公式版本内容不能为空"); } requireText(form.getBiaodashi(), 8192, "公式表达式不能为空", "公式表达式不能超过 8192 个字符"); FormulaType resultType = formulaType(form.getJieguoLeixing(), "结果类型无效"); if (resultType != FormulaType.DECIMAL && resultType != FormulaType.TEXT && resultType != FormulaType.BOOLEAN) { throw new DataException("结果类型无效"); } if ((form.getXiuyueXiaoshuWei() == null) != (form.getXiuyueMoshi() == null)) { throw new DataException("修约位数和模式必须同时提供"); } if (form.getXiuyueXiaoshuWei() != null) { if (resultType != FormulaType.DECIMAL || form.getXiuyueXiaoshuWei() < 0 || form.getXiuyueXiaoshuWei() > 12 || !("HALF_UP".equals(form.getXiuyueMoshi()) || "HALF_EVEN".equals(form.getXiuyueMoshi()) || "UP".equals(form.getXiuyueMoshi()) || "DOWN".equals(form.getXiuyueMoshi()))) { throw new DataException("修约配置无效"); } } if (form.getParameters() != null) { Set ids = new HashSet<>(); for (ElnFormulaForms.Parameter parameter : form.getParameters()) { validateParameter(parameter); if (StringUtils.hasText(parameter.getId()) && !ids.add(parameter.getId().trim())) { throw new DataException("参数槽不能重复提交"); } } } } private static void validateParameter(ElnFormulaForms.Parameter parameter) { if (parameter == null) { throw new DataException("参数项不能为空"); } requireText(parameter.getCanshuMingcheng(), 200, "参数名称不能为空", "参数名称不能超过 200 个字符"); FormulaType type = formulaType(parameter.getShujuLeixing(), "参数类型无效"); if (type == FormulaType.NULL) { throw new DataException("参数类型无效"); } maxLength(parameter.getDanwei(), 50, "参数单位不能超过 50 个字符"); if (parameter.getShifouBitian() != null && parameter.getShifouBitian() != 0 && parameter.getShifouBitian() != 1) { throw new DataException("参数必填标志只能为 0 或 1"); } } private ElnFormulaDefEntity requireDefinition(String tenantId, String id, boolean forUpdate) { if (!StringUtils.hasText(id)) { throw new DataException("公式 id 不能为空"); } ElnFormulaDefEntity definition = store.findDefinition(tenantId, id.trim(), forUpdate); if (definition == null) { throw new DataException("公式不存在"); } return definition; } private ElnFormulaVersionEntity requireVersion(String tenantId, String id, boolean forUpdate) { if (!StringUtils.hasText(id)) { throw new DataException("公式版本 id 不能为空"); } ElnFormulaVersionEntity version = store.findVersion(tenantId, id.trim(), forUpdate); if (version == null) { throw new DataException("公式版本不存在"); } return version; } private UserContext requireCatalogAccess() { UserContext context = userContextSupplier.get(); if (context == null || !StringUtils.hasText(context.getTenantId()) || !StringUtils.hasText(context.getUserId())) { throw new DataException("请先登录后再维护公式目录"); } if (!maintainPermission.getAsBoolean()) { throw new DataException("没有公式维护权限"); } return context; } private static void requireEnabled(ElnFormulaDefEntity definition) { if (!ENABLED.equals(definition.getBizEnabled())) { throw new DataException("公式已停用,不能执行该操作"); } } private static void requireSameDefinition(ElnFormulaDefEntity definition, ElnFormulaVersionEntity version) { if (!definition.getId().equals(version.getGongshiDingyiId())) { throw new DataException("公式版本不属于当前公式"); } } private int nextVersionNumber(String tenantId, String definitionId) { int max = 0; for (ElnFormulaVersionEntity version : store.listVersions(tenantId, definitionId)) { if (version.getBanbenHao() != null && version.getBanbenHao() > max) { max = version.getBanbenHao(); } } return max + 1; } private ElnFormulaVersionEntity newVersion(UserContext context, String definitionId, int versionNumber) { ElnFormulaVersionEntity version = new ElnFormulaVersionEntity(); initializeBase(version, context); version.setGongshiDingyiId(definitionId); version.setBanbenHao(versionNumber); version.setBizStatus(DRAFT); return version; } private ElnFormulaParameterSlotEntity newSlot(UserContext context, String versionId) { ElnFormulaParameterSlotEntity slot = new ElnFormulaParameterSlotEntity(); initializeBase(slot, context); slot.setGongshiBanbenId(versionId); return slot; } private void initializeBase(Object target, UserContext context) { Date now = new Date(); if (target instanceof ElnFormulaDefEntity) { ElnFormulaDefEntity entity = (ElnFormulaDefEntity) target; entity.setId(idSupplier.get()); entity.setFTenantId(context.getTenantId()); entity.setFVersion(0); entity.setFDeleteMark(0); entity.setFCreatorUserId(context.getUserId()); entity.setFLastModifyUserId(context.getUserId()); entity.setFCreatorTime(now); entity.setFLastModifyTime(now); } else if (target instanceof ElnFormulaVersionEntity) { ElnFormulaVersionEntity entity = (ElnFormulaVersionEntity) target; entity.setId(idSupplier.get()); entity.setFTenantId(context.getTenantId()); entity.setFVersion(0); entity.setFDeleteMark(0); entity.setFCreatorUserId(context.getUserId()); entity.setFLastModifyUserId(context.getUserId()); entity.setFCreatorTime(now); entity.setFLastModifyTime(now); } else { ElnFormulaParameterSlotEntity entity = (ElnFormulaParameterSlotEntity) target; entity.setId(idSupplier.get()); entity.setFTenantId(context.getTenantId()); entity.setFVersion(0); entity.setFDeleteMark(0); entity.setFCreatorUserId(context.getUserId()); entity.setFLastModifyUserId(context.getUserId()); entity.setFCreatorTime(now); entity.setFLastModifyTime(now); } } private static void applyVersion(ElnFormulaVersionEntity target, ElnFormulaForms.VersionRule source) { target.setBiaodashi(source.getBiaodashi().trim()); target.setJieguoLeixing(source.getJieguoLeixing().trim()); target.setXiuyueXiaoshuWei(source.getXiuyueXiaoshuWei()); target.setXiuyueMoshi(trimToNull(source.getXiuyueMoshi())); } private static void copyVersionRules(ElnFormulaVersionEntity source, ElnFormulaVersionEntity target) { target.setBiaodashi(source.getBiaodashi()); target.setJieguoLeixing(source.getJieguoLeixing()); target.setXiuyueXiaoshuWei(source.getXiuyueXiaoshuWei()); target.setXiuyueMoshi(source.getXiuyueMoshi()); } private static void applySlot(ElnFormulaParameterSlotEntity target, ElnFormulaForms.Parameter source, int index, UserContext context) { target.setCanshuMingcheng(source.getCanshuMingcheng().trim()); target.setShujuLeixing(source.getShujuLeixing().trim()); target.setDanwei(trimToNull(source.getDanwei())); target.setShifouBitian(source.getShifouBitian() == null ? 1 : source.getShifouBitian()); target.setPaixu(source.getPaixu() == null ? index : source.getPaixu()); target.setFLastModifyUserId(context.getUserId()); target.setFLastModifyTime(new Date()); } private static void copySlotRules(ElnFormulaParameterSlotEntity source, ElnFormulaParameterSlotEntity target) { target.setCanshuMingcheng(source.getCanshuMingcheng()); target.setShujuLeixing(source.getShujuLeixing()); target.setDanwei(source.getDanwei()); target.setShifouBitian(source.getShifouBitian()); target.setPaixu(source.getPaixu()); } private ElnFormulaVos.Detail toDetail(ElnFormulaDefEntity definition, List versions) { ElnFormulaVos.Detail result = new ElnFormulaVos.Detail(); result.setId(definition.getId()); result.setGongshiBianma(definition.getGongshiBianma()); result.setGongshiMingcheng(definition.getGongshiMingcheng()); result.setYewuYongtu(definition.getYewuYongtu()); result.setDangqianGongshiBanbenId(definition.getDangqianGongshiBanbenId()); result.setBizEnabled(definition.getBizEnabled()); result.setBeizhu(definition.getBeizhu()); ElnFormulaForms.ReferenceQuery referenceQuery = new ElnFormulaForms.ReferenceQuery(); referenceQuery.setPageSize(5); IPage references = pageReferenceSummaries( definition.getFTenantId(), definition.getId(), referenceQuery); result.setMubanYinyongShu(Math.toIntExact(references.getTotal())); result.setTemplateReferences(references.getRecords()); versions.sort(Comparator.comparing(ElnFormulaVersionEntity::getBanbenHao)); for (ElnFormulaVersionEntity version : versions) { result.getVersions().add(toVersion(version, store.listSlots(definition.getFTenantId(), version.getId()))); } return result; } private IPage pageReferenceSummaries( String tenantId, String definitionId, ElnFormulaForms.ReferenceQuery query) { IPage source = store.pageLiveTemplateFormulaBindings(tenantId, definitionId, query); List bindings = source.getRecords(); if (bindings.isEmpty()) { return new Page( source.getCurrent(), source.getSize(), source.getTotal()) .setRecords(Collections.emptyList()); } Map templates = new LinkedHashMap<>(); for (ElnTemplateVersionEntity template : store.listTemplates(tenantId, templateIds(bindings))) { templates.put(template.getId(), template); } List result = new ArrayList<>(); for (ElnTemplateFormulaBindingEntity binding : bindings) { ElnTemplateVersionEntity template = templates.get(binding.getMubanId()); ElnFormulaVos.ReferenceSummary summary = new ElnFormulaVos.ReferenceSummary(); summary.setBindingId(binding.getId()); summary.setTemplateId(binding.getMubanId()); summary.setTemplateName(template == null ? null : template.getJianyanXiangmu()); summary.setTemplateStatus(template == null ? null : template.getBizStatus()); summary.setBindingStatus(binding.getBangdingZhuangtai()); summary.setFormulaVersionId(binding.getGongshiBanbenId()); result.add(summary); } return new Page( source.getCurrent(), source.getSize(), source.getTotal()).setRecords(result); } private static Map referenceCounts( List bindings) { Map result = new LinkedHashMap<>(); for (ElnTemplateFormulaBindingEntity binding : bindings) { String formulaId = binding.getGongshiDingyiId(); result.put(formulaId, result.containsKey(formulaId) ? result.get(formulaId) + 1 : 1); } return result; } private static Set ids(List definitions) { Set result = new HashSet<>(); for (ElnFormulaDefEntity definition : definitions) { result.add(definition.getId()); } return result; } private static Set templateIds(List bindings) { Set result = new HashSet<>(); for (ElnTemplateFormulaBindingEntity binding : bindings) { if (StringUtils.hasText(binding.getMubanId())) { result.add(binding.getMubanId()); } } return result; } private ElnFormulaVos.ListItem toListItem(ElnFormulaDefEntity definition, List versions) { ElnFormulaVos.ListItem result = new ElnFormulaVos.ListItem(); result.setId(definition.getId()); result.setGongshiBianma(definition.getGongshiBianma()); result.setGongshiMingcheng(definition.getGongshiMingcheng()); result.setYewuYongtu(definition.getYewuYongtu()); result.setDangqianGongshiBanbenId(definition.getDangqianGongshiBanbenId()); result.setBizEnabled(definition.getBizEnabled()); result.setGengxinShijian(definition.getFLastModifyTime()); int latestPublished = 0; for (ElnFormulaVersionEntity version : versions) { if (PUBLISHED.equals(version.getBizStatus()) && version.getBanbenHao() != null) { latestPublished = Math.max(latestPublished, version.getBanbenHao()); } if (version.getId().equals(definition.getDangqianGongshiBanbenId())) { result.setDangqianBanbenHao(version.getBanbenHao()); } } result.setZuixinFabuBanbenHao(latestPublished == 0 ? null : latestPublished); return result; } private static ElnFormulaVos.Version toVersion(ElnFormulaVersionEntity source, List slots) { ElnFormulaVos.Version result = new ElnFormulaVos.Version(); result.setId(source.getId()); result.setGongshiDingyiId(source.getGongshiDingyiId()); result.setBanbenHao(source.getBanbenHao()); result.setBiaodashi(source.getBiaodashi()); result.setJieguoLeixing(source.getJieguoLeixing()); result.setXiuyueXiaoshuWei(source.getXiuyueXiaoshuWei()); result.setXiuyueMoshi(source.getXiuyueMoshi()); result.setBizStatus(source.getBizStatus()); result.setZuofeiYuanyin(source.getZuofeiYuanyin()); result.setChuangjianShijian(source.getFCreatorTime()); result.setGengxinShijian(source.getFLastModifyTime()); slots.sort(Comparator.comparing(ElnFormulaParameterSlotEntity::getPaixu)); for (ElnFormulaParameterSlotEntity slot : slots) { result.getParameters().add(toParameter(slot)); } return result; } private static ElnFormulaVos.Parameter toParameter(ElnFormulaParameterSlotEntity source) { ElnFormulaVos.Parameter result = new ElnFormulaVos.Parameter(); result.setId(source.getId()); result.setGongshiBanbenId(source.getGongshiBanbenId()); result.setCanshuBianma(source.getCanshuBianma()); result.setCanshuMingcheng(source.getCanshuMingcheng()); result.setShujuLeixing(source.getShujuLeixing()); result.setDanwei(source.getDanwei()); result.setShifouBitian(source.getShifouBitian()); result.setPaixu(source.getPaixu()); return result; } private void audit(String actionCode, String table, String targetId, UserContext context, Object before, Object after, String reason) { Map snapshot = new LinkedHashMap<>(); snapshot.put("before", before); snapshot.put("after", after); AuditEventDTO event = AuditEventDTO.builder() .tenantId(context.getTenantId()).operatorId(context.getUserId()) .eventTime(new Date()).eventType("DATA_CHANGE") .actionCode(actionCode).actionLabel(actionCode).bizModule("eln") .bizType("eln_formula_def".equals(table) ? "eln_formula" : "eln_formula_version") .targetTable(table).targetId(targetId).reason(reason) .dataSnapshot(JsonUtil.getObjectToString(snapshot)).build(); auditRecorder.accept(event); } private static Object versionSnapshot(ElnFormulaVersionEntity version, List slots) { Map result = new LinkedHashMap<>(); result.put("version", version); result.put("parameters", slots); return result; } private static Object publishSnapshot(ElnFormulaDefEntity definition, ElnFormulaVersionEntity version, List slots) { Map result = new LinkedHashMap<>(); result.put("definition", definition); result.put("version", version); result.put("parameters", slots); return result; } private static ElnFormulaDefEntity copyDefinition(ElnFormulaDefEntity source) { return JsonUtil.getJsonToBean(source, ElnFormulaDefEntity.class); } private static ElnFormulaVersionEntity copyVersion(ElnFormulaVersionEntity source) { return JsonUtil.getJsonToBean(source, ElnFormulaVersionEntity.class); } private static List copySlots( List source) { List result = new ArrayList<>(); for (ElnFormulaParameterSlotEntity slot : source) { result.add(JsonUtil.getJsonToBean(slot, ElnFormulaParameterSlotEntity.class)); } return result; } private static FormulaType formulaType(String value, String message) { if (!StringUtils.hasText(value)) { throw new DataException(message); } try { return FormulaType.valueOf(value.trim()); } catch (IllegalArgumentException exception) { throw new DataException(message); } } private static void requireText(String value, int maxLength, String emptyMessage, String lengthMessage) { if (!StringUtils.hasText(value)) { throw new DataException(emptyMessage); } if (value.trim().length() > maxLength) { throw new DataException(lengthMessage); } } private static void maxLength(String value, int maxLength, String message) { if (value != null && value.trim().length() > maxLength) { throw new DataException(message); } } private static String requireReason(String reason) { if (!StringUtils.hasText(reason)) { throw new DataException("操作原因不能为空"); } String value = reason.trim(); if (value.length() > 500) { throw new DataException("操作原因不能超过 500 个字符"); } return value; } private static String trimToNull(String value) { return StringUtils.hasText(value) ? value.trim() : null; } private static DraftTemplateReferenceChecker checker( ObjectProvider provider) { return resolveChecker(provider.getIfAvailable()); } static DraftTemplateReferenceChecker resolveChecker( DraftTemplateReferenceChecker checker) { if (checker != null) { return checker; } return (tenantId, versionId) -> { throw new DataException("引用检查能力不可用"); }; } static int nextVersionAfterUpdate(int affectedRows, Integer expectedVersion, String conflictMessage) { if (affectedRows != 1 || expectedVersion == null) { throw new DataException(conflictMessage); } return expectedVersion + 1; } private static UserContext currentUserContext() { UserInfo user = UserProvider.getUser(); String tenantId = null; TenantVO routed = TenantDataSourceUtil.getTenantInfo(); if (routed != null && StringUtils.hasText(routed.getEnCode())) { tenantId = routed.getEnCode().trim(); } else if (user != null && StringUtils.hasText(user.getTenantId())) { tenantId = user.getTenantId().trim(); } if (!StringUtils.hasText(tenantId)) { tenantId = "0"; } return new UserContext(tenantId, user == null ? null : trimToNull(user.getUserId())); } private static boolean hasMaintainPermission() { UserInfo user = UserProvider.getUser(); return user != null && (Boolean.TRUE.equals(user.getIsAdministrator()) || StpUtil.hasPermission(MENU_PERMISSION)); } @FunctionalInterface public interface DraftTemplateReferenceChecker { boolean hasDraftReferences(String tenantId, String versionId); } static final class UserContext { private final String tenantId; private final String userId; UserContext(String tenantId, String userId) { this.tenantId = tenantId; this.userId = userId; } String getTenantId() { return tenantId; } String getUserId() { return userId; } } interface FormulaCatalogStore { void insertDefinition(ElnFormulaDefEntity entity); void insertVersion(ElnFormulaVersionEntity entity); void insertSlot(ElnFormulaParameterSlotEntity entity); ElnFormulaDefEntity findDefinition(String tenantId, String id, boolean forUpdate); ElnFormulaVersionEntity findVersion(String tenantId, String id, boolean forUpdate); List listVersions(String tenantId, String definitionId); List listVersionsByDefinitionIds( String tenantId, Collection definitionIds); List listSlots(String tenantId, String versionId); List listLiveTemplateFormulaBindings( String tenantId, Collection definitionIds); IPage pageLiveTemplateFormulaBindings( String tenantId, String definitionId, ElnFormulaForms.ReferenceQuery query); List listTemplates(String tenantId, Collection templateIds); IPage page(String tenantId, ElnFormulaForms.Query query); void updateDefinition(ElnFormulaDefEntity entity); void updateVersion(ElnFormulaVersionEntity entity); void updateSlot(ElnFormulaParameterSlotEntity entity); void deleteSlot(ElnFormulaParameterSlotEntity entity); } private static final class MyBatisFormulaCatalogStore implements FormulaCatalogStore { private final ElnFormulaDefMapper definitionMapper; private final ElnFormulaVersionMapper versionMapper; private final ElnFormulaParameterSlotMapper slotMapper; private final ElnTemplateMapper templateMapper; private final ElnTemplateFormulaBindingMapper templateFormulaBindingMapper; private MyBatisFormulaCatalogStore(ElnFormulaDefMapper definitionMapper, ElnFormulaVersionMapper versionMapper, ElnFormulaParameterSlotMapper slotMapper, ElnTemplateMapper templateMapper, ElnTemplateFormulaBindingMapper templateFormulaBindingMapper) { this.definitionMapper = definitionMapper; this.versionMapper = versionMapper; this.slotMapper = slotMapper; this.templateMapper = templateMapper; this.templateFormulaBindingMapper = templateFormulaBindingMapper; } @Override public void insertDefinition(ElnFormulaDefEntity entity) { if (definitionMapper.insertIgnoreCode(entity) != 1) { throw new DuplicateKeyException("公式编码已存在"); } } @Override public void insertVersion(ElnFormulaVersionEntity entity) { versionMapper.insert(entity); } @Override public void insertSlot(ElnFormulaParameterSlotEntity entity) { if (slotMapper.insertIgnoreCode(entity) != 1) { throw new DuplicateKeyException("参数编码已存在"); } } @Override public ElnFormulaDefEntity findDefinition(String tenantId, String id, boolean forUpdate) { LambdaQueryWrapper query = Wrappers.lambdaQuery() .eq(ElnFormulaDefEntity::getFTenantId, tenantId) .eq(ElnFormulaDefEntity::getId, id) .and(item -> item.isNull(ElnFormulaDefEntity::getFDeleteMark) .or().eq(ElnFormulaDefEntity::getFDeleteMark, 0)); if (forUpdate) { query.last("FOR UPDATE"); } return definitionMapper.selectOne(query); } @Override public ElnFormulaVersionEntity findVersion(String tenantId, String id, boolean forUpdate) { LambdaQueryWrapper query = Wrappers .lambdaQuery() .eq(ElnFormulaVersionEntity::getFTenantId, tenantId) .eq(ElnFormulaVersionEntity::getId, id) .and(item -> item.isNull(ElnFormulaVersionEntity::getFDeleteMark) .or().eq(ElnFormulaVersionEntity::getFDeleteMark, 0)); if (forUpdate) { query.last("FOR UPDATE"); } return versionMapper.selectOne(query); } @Override public List listVersions(String tenantId, String definitionId) { return versionMapper.selectList(Wrappers.lambdaQuery() .eq(ElnFormulaVersionEntity::getFTenantId, tenantId) .eq(ElnFormulaVersionEntity::getGongshiDingyiId, definitionId) .and(item -> item.isNull(ElnFormulaVersionEntity::getFDeleteMark) .or().eq(ElnFormulaVersionEntity::getFDeleteMark, 0)) .orderByAsc(ElnFormulaVersionEntity::getBanbenHao)); } @Override public List listVersionsByDefinitionIds( String tenantId, Collection definitionIds) { if (definitionIds == null || definitionIds.isEmpty()) { return Collections.emptyList(); } return versionMapper.selectList(Wrappers.lambdaQuery() .eq(ElnFormulaVersionEntity::getFTenantId, tenantId) .in(ElnFormulaVersionEntity::getGongshiDingyiId, definitionIds) .and(item -> item.isNull(ElnFormulaVersionEntity::getFDeleteMark) .or().eq(ElnFormulaVersionEntity::getFDeleteMark, 0)) .orderByAsc(ElnFormulaVersionEntity::getGongshiDingyiId) .orderByAsc(ElnFormulaVersionEntity::getBanbenHao)); } @Override public List listSlots(String tenantId, String versionId) { return slotMapper.selectList(Wrappers.lambdaQuery() .eq(ElnFormulaParameterSlotEntity::getFTenantId, tenantId) .eq(ElnFormulaParameterSlotEntity::getGongshiBanbenId, versionId) .and(item -> item.isNull(ElnFormulaParameterSlotEntity::getFDeleteMark) .or().eq(ElnFormulaParameterSlotEntity::getFDeleteMark, 0)) .orderByAsc(ElnFormulaParameterSlotEntity::getPaixu)); } @Override public List listLiveTemplateFormulaBindings( String tenantId, Collection definitionIds) { if (definitionIds == null || definitionIds.isEmpty()) { return Collections.emptyList(); } return templateFormulaBindingMapper.selectList( Wrappers.lambdaQuery() .eq(ElnTemplateFormulaBindingEntity::getFTenantId, tenantId) .in(ElnTemplateFormulaBindingEntity::getGongshiDingyiId, definitionIds) .in(ElnTemplateFormulaBindingEntity::getBangdingZhuangtai, ACTIVE, PENDING) .and(item -> item.isNull(ElnTemplateFormulaBindingEntity::getFDeleteMark) .or().eq(ElnTemplateFormulaBindingEntity::getFDeleteMark, 0))); } @Override public IPage pageLiveTemplateFormulaBindings( String tenantId, String definitionId, ElnFormulaForms.ReferenceQuery form) { LambdaQueryWrapper query = Wrappers .lambdaQuery() .eq(ElnTemplateFormulaBindingEntity::getFTenantId, tenantId) .eq(ElnTemplateFormulaBindingEntity::getGongshiDingyiId, definitionId) .in(ElnTemplateFormulaBindingEntity::getBangdingZhuangtai, ACTIVE, PENDING) .and(item -> item.isNull(ElnTemplateFormulaBindingEntity::getFDeleteMark) .or().eq(ElnTemplateFormulaBindingEntity::getFDeleteMark, 0)); if (StringUtils.hasText(form.getKeyword())) { String keyword = form.getKeyword().trim(); List templates = templateMapper.selectList( Wrappers.lambdaQuery() .eq(ElnTemplateVersionEntity::getFTenantId, tenantId) .like(ElnTemplateVersionEntity::getJianyanXiangmu, keyword) .and(item -> item.isNull(ElnTemplateVersionEntity::getFDeleteMark) .or().eq(ElnTemplateVersionEntity::getFDeleteMark, 0))); Set matchedTemplateIds = new HashSet<>(); for (ElnTemplateVersionEntity template : templates) { matchedTemplateIds.add(template.getId()); } query.and(item -> { item.like(ElnTemplateFormulaBindingEntity::getMubanId, keyword); if (!matchedTemplateIds.isEmpty()) { item.or().in(ElnTemplateFormulaBindingEntity::getMubanId, matchedTemplateIds); } }); } query.orderByAsc(ElnTemplateFormulaBindingEntity::getMubanId) .orderByAsc(ElnTemplateFormulaBindingEntity::getGongshiBanbenId) .orderByAsc(ElnTemplateFormulaBindingEntity::getBangdingZhuangtai) .orderByAsc(ElnTemplateFormulaBindingEntity::getId); return templateFormulaBindingMapper.selectPage( new Page<>(form.getCurrentPage(), form.getPageSize()), query); } @Override public List listTemplates( String tenantId, Collection templateIds) { if (templateIds == null || templateIds.isEmpty()) { return Collections.emptyList(); } return templateMapper.selectList(Wrappers.lambdaQuery() .eq(ElnTemplateVersionEntity::getFTenantId, tenantId) .in(ElnTemplateVersionEntity::getId, templateIds) .and(item -> item.isNull(ElnTemplateVersionEntity::getFDeleteMark) .or().eq(ElnTemplateVersionEntity::getFDeleteMark, 0))); } @Override public IPage page(String tenantId, ElnFormulaForms.Query form) { LambdaQueryWrapper query = Wrappers.lambdaQuery() .eq(ElnFormulaDefEntity::getFTenantId, tenantId) .and(item -> item.isNull(ElnFormulaDefEntity::getFDeleteMark) .or().eq(ElnFormulaDefEntity::getFDeleteMark, 0)); if (StringUtils.hasText(form.getBizEnabled())) { query.eq(ElnFormulaDefEntity::getBizEnabled, form.getBizEnabled().trim()); } if (StringUtils.hasText(form.getKeyword())) { String keyword = form.getKeyword().trim(); query.and(item -> item.like(ElnFormulaDefEntity::getGongshiBianma, keyword) .or().like(ElnFormulaDefEntity::getGongshiMingcheng, keyword) .or().like(ElnFormulaDefEntity::getYewuYongtu, keyword)); } query.orderByDesc(ElnFormulaDefEntity::getFLastModifyTime) .orderByDesc(ElnFormulaDefEntity::getFCreatorTime); return definitionMapper.selectPage( new Page<>(form.getCurrentPage(), form.getPageSize()), query); } @Override public void updateDefinition(ElnFormulaDefEntity entity) { Integer expectedVersion = entity.getFVersion(); int affected = definitionMapper.updateMutableByTenantAndVersion( entity, expectedVersion); entity.setFVersion(nextVersionAfterUpdate(affected, expectedVersion, "公式状态已变化,请刷新后重试")); } @Override public void updateVersion(ElnFormulaVersionEntity entity) { Integer expectedVersion = entity.getFVersion(); int affected = versionMapper.updateMutableByTenantAndVersion(entity, expectedVersion); entity.setFVersion(nextVersionAfterUpdate(affected, expectedVersion, "公式版本状态已变化,请刷新后重试")); } @Override public void updateSlot(ElnFormulaParameterSlotEntity entity) { Integer expectedVersion = entity.getFVersion(); int affected = slotMapper.updateMutableByTenantAndVersion(entity, expectedVersion); entity.setFVersion(nextVersionAfterUpdate(affected, expectedVersion, "参数槽状态已变化,请刷新后重试")); } @Override public void deleteSlot(ElnFormulaParameterSlotEntity entity) { int count = slotMapper.deleteByTenantAndVersion(entity, entity.getFVersion()); if (count != 1) { throw new DataException("参数槽状态已变化,请刷新后重试"); } } } }