package jnpf.elnService.formula.impl; import cn.hutool.core.util.IdUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import jnpf.audit.model.AuditEventDTO; import jnpf.audit.sdk.AuditClient; import jnpf.elnEntity.field.ElnFieldVersionEntity; 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.ElnTemplateBindingEntity; import jnpf.elnEntity.template.ElnTemplateVersionEntity; import jnpf.elnMapper.ElnTemplateMapper; import jnpf.elnMapper.ElnFieldVersionMapper; import jnpf.elnMapper.ElnFormulaDefMapper; import jnpf.elnMapper.ElnFormulaParameterSlotMapper; import jnpf.elnMapper.ElnFormulaVersionMapper; import jnpf.elnMapper.ElnTemplateBindingMapper; import jnpf.elnMapper.ElnTemplateCrossReferenceMapper; import jnpf.elnMapper.ElnTemplateFormulaBindingMapper; import jnpf.elnService.formula.ElnTemplateFormulaBindingService; import jnpf.elnService.formula.FormulaDependencyGraph; import jnpf.elnService.template.TemplateAnnotationAuthorization; import jnpf.exception.DataException; import jnpf.util.JsonUtil; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; @Service @Transactional(rollbackFor = Exception.class) public class ElnTemplateFormulaBindingServiceImpl implements ElnTemplateFormulaBindingService { private static final String ACTIVE = "ACTIVE"; private static final String PENDING = "PENDING"; private static final String CANCELLED = "CANCELLED"; private static final String REMOVED = "REMOVED"; private static final String PUBLISHED = "PUBLISHED"; private static final String PRE_RELEASE = "PRE_RELEASE"; private final Store store; private final FormulaDependencyGraph graph; private final Supplier idSupplier; private final Consumer auditRecorder; private final long pendingTimeoutMinutes; private final ResultBindingGuard resultBindingGuard; @Autowired public ElnTemplateFormulaBindingServiceImpl(ElnTemplateMapper templateMapper, ElnFormulaDefMapper definitionMapper, ElnFormulaVersionMapper formulaVersionMapper, ElnFormulaParameterSlotMapper slotMapper, ElnTemplateBindingMapper fieldBindingMapper, ElnFieldVersionMapper fieldVersionMapper, ElnTemplateFormulaBindingMapper formulaBindingMapper, ElnTemplateCrossReferenceMapper crossReferenceMapper, ObjectProvider auditClientProvider, @Value("${eln.template-formula-binding.pending-timeout-minutes:30}") long timeout) { this(new MyBatisStore(templateMapper, definitionMapper, formulaVersionMapper, slotMapper, fieldBindingMapper, fieldVersionMapper, formulaBindingMapper), new FormulaDependencyGraph(), IdUtil::getSnowflakeNextIdStr, event -> { AuditClient client = auditClientProvider.getIfAvailable(); if (client != null) client.record(event); }, timeout, (tenantId, bindingId) -> crossReferenceMapper.selectCount( Wrappers.lambdaQuery() .eq(jnpf.elnEntity.template.ElnTemplateCrossReferenceEntity::getFTenantId, tenantId) .eq(jnpf.elnEntity.template.ElnTemplateCrossReferenceEntity::getMubanBangdingId, bindingId) .eq(jnpf.elnEntity.template.ElnTemplateCrossReferenceEntity::getFDeleteMark, 0)) > 0); } public ElnTemplateFormulaBindingServiceImpl(Store store, FormulaDependencyGraph graph, Supplier idSupplier, Consumer auditRecorder, long pendingTimeoutMinutes) { this(store, graph, idSupplier, auditRecorder, pendingTimeoutMinutes, (tenantId, bindingId) -> false); } ElnTemplateFormulaBindingServiceImpl(Store store, FormulaDependencyGraph graph, Supplier idSupplier, Consumer auditRecorder, long pendingTimeoutMinutes, ResultBindingGuard resultBindingGuard) { this.store = store; this.graph = graph; this.idSupplier = idSupplier; this.auditRecorder = auditRecorder; this.pendingTimeoutMinutes = pendingTimeoutMinutes; this.resultBindingGuard = resultBindingGuard; } @Override @Transactional(readOnly = true) public List list(TemplateAnnotationAuthorization authorization) { requireAuthorization(authorization); List result = new ArrayList<>(); for (ElnTemplateFormulaBindingEntity item : store.listBindings( authorization.getTenantId(), authorization.getTemplateId())) { if (!REMOVED.equals(item.getBangdingZhuangtai()) && !CANCELLED.equals(item.getBangdingZhuangtai())) result.add(toVo(item)); } return result; } @Override @Transactional(readOnly = true) public ElnFormulaVos.TemplateMappingContext context( TemplateAnnotationAuthorization authorization) { requireAuthorization(authorization); ElnFormulaVos.TemplateMappingContext result = new ElnFormulaVos.TemplateMappingContext(); List bindings = store.listBindings( authorization.getTenantId(), authorization.getTemplateId()); for (ElnTemplateFormulaBindingEntity item : bindings) { if (!REMOVED.equals(item.getBangdingZhuangtai()) && !CANCELLED.equals(item.getBangdingZhuangtai())) { result.getBindings().add(toVo(item)); } if (PENDING.equals(item.getBangdingZhuangtai()) && StringUtils.hasText(item.getLaiyuanGongshiBangdingId())) { result.getCopiedPendingBindingIds().add(item.getId()); } } if (!result.getCopiedPendingBindingIds().isEmpty()) { ElnTemplateVersionEntity template = store.findTemplate( authorization.getTenantId(), authorization.getTemplateId()); if (template != null) result.setUpgradeOperationCode(template.getShengbanCaozuoBianma()); } addInheritedFormulaLockBindings(authorization, result); Map definitions = new LinkedHashMap<>(); for (ElnFormulaDefEntity item : store.listFormulaDefinitions(authorization.getTenantId())) { definitions.put(item.getId(), item); } for (ElnFormulaVersionEntity item : store.listFormulaVersions(authorization.getTenantId())) { ElnFormulaDefEntity definition = definitions.get(item.getGongshiDingyiId()); if (definition == null || (!PRE_RELEASE.equals(item.getBizStatus()) && !PUBLISHED.equals(item.getBizStatus()))) continue; result.getVersions().add(toTemplateFormulaVersion(authorization.getTenantId(), definition, item)); } return result; } private void addInheritedFormulaLockBindings(TemplateAnnotationAuthorization authorization, ElnFormulaVos.TemplateMappingContext context) { ElnTemplateVersionEntity target = store.findTemplate( authorization.getTenantId(), authorization.getTemplateId()); if (target == null || !StringUtils.hasText(target.getLaiyuanMubanId())) return; Set sourceResultBindingIds = new LinkedHashSet<>(); for (ElnTemplateFormulaBindingEntity formula : store.listBindings( authorization.getTenantId(), target.getLaiyuanMubanId())) { if (ACTIVE.equals(formula.getBangdingZhuangtai())) { sourceResultBindingIds.add(formula.getJieguoBangdingId()); } } if (sourceResultBindingIds.isEmpty()) return; List targetBindings = store.listFieldBindings( authorization.getTenantId(), authorization.getTemplateId()); Set targetResultBindingIds = new LinkedHashSet<>(); for (ElnTemplateBindingEntity binding : targetBindings) { if (PENDING.equals(binding.getBangdingZhuangtai()) && "PRIMARY".equals(binding.getKongjianJuese()) && sourceResultBindingIds.contains(binding.getLaiyuanBangdingId())) { targetResultBindingIds.add(binding.getId()); } } List inherited = new ArrayList<>(); for (ElnTemplateBindingEntity binding : targetBindings) { if (!PENDING.equals(binding.getBangdingZhuangtai())) continue; if (targetResultBindingIds.contains(binding.getId()) || ("REFERENCE".equals(binding.getKongjianJuese()) && targetResultBindingIds.contains(binding.getYinyongShiliId()))) { inherited.add(binding.getId()); } } Collections.sort(inherited); context.setInheritedFormulaLockBindingIds(inherited); } @Override public ElnFormulaVos.TemplateBinding reserve(TemplateAnnotationAuthorization authorization, ElnFormulaForms.TemplateBinding form) { requireAuthorization(authorization); String operation = normalizeOperation(form == null ? null : form.getOperationCode()); form.setOperationCode(operation); ElnTemplateFormulaBindingEntity existing = store.findByOperation(authorization.getTenantId(), authorization.getTemplateId(), operation); if (existing != null) { ElnFormulaVersionEntity requestedVersion = store.findFormulaVersion( authorization.getTenantId(), trim(form.getFormulaVersionId())); if (requestedVersion == null || !sameReserve(existing, authorization, form, requestedVersion.getGongshiDingyiId(), operation)) throw new DataException("操作码已用于其他公式映射请求"); return toVo(existing); } ValidatedMapping validated = validateRequest(authorization, form, null); requireOriginalResultLockSnapshot(authorization, form.getResultBindingId(), form.getJieguoSuoKuaizhao()); ElnTemplateFormulaBindingEntity created = new ElnTemplateFormulaBindingEntity(); created.setId(idSupplier.get()); created.setFTenantId(authorization.getTenantId()); created.setMubanId(authorization.getTemplateId()); created.setGongshiDingyiId(validated.definition.getId()); created.setGongshiBanbenId(validated.version.getId()); created.setCanshuYingshe(new LinkedHashMap<>(form.getParameterBindings())); created.setJieguoBangdingId(form.getResultBindingId().trim()); created.setBangdingZhuangtai(PENDING); created.setCaozuoBianma(operation); created.setJieguoSuoKuaizhao(copyControls(form.getJieguoSuoKuaizhao())); created.setDaoqiShijian(new Date(System.currentTimeMillis() + pendingTimeoutMinutes * 60_000L)); created.setFVersion(1); created.setFCreatorUserId(authorization.getUserId()); created.setFCreatorTime(new Date()); created.setFDeleteMark(0); if (!store.insertIfAbsent(created)) { ElnTemplateFormulaBindingEntity concurrent = store.findByOperation( authorization.getTenantId(), authorization.getTemplateId(), operation); if (concurrent == null || !sameReserve(concurrent, authorization, form, validated.definition.getId(), operation)) throw new DataException("操作码已用于其他公式映射请求"); return toVo(concurrent); } audit("ELN_TEMPLATE_FORMULA_RESERVE", authorization, created, null, created, null); return toVo(created); } @Override public void activate(TemplateAnnotationAuthorization authorization, String formulaBindingId, ElnFormulaForms.ActivateFormulaBinding form) { lockTemplateGraph(authorization); ElnTemplateFormulaBindingEntity binding = requireOwned(authorization, formulaBindingId); String operation = form == null ? null : normalizeOperation(form.getOperationCode()); if (form != null) form.setOperationCode(operation); if (form != null && ACTIVE.equals(binding.getBangdingZhuangtai()) && alreadyApplied(binding, form.getExpectedVersion(), operation)) return; requireMutation(binding, operation, form == null ? null : form.getExpectedVersion(), PENDING); validateRequest(authorization, request(binding), binding.getId()); requireResultLocked(authorization, binding.getJieguoBangdingId(), form.getControls()); ElnTemplateFormulaBindingEntity before = copy(binding); if (!store.updateStatus(binding, form.getExpectedVersion(), ACTIVE, null)) throw new DataException("公式映射版本冲突"); FormulaDependencyGraph.Result result = validateGraph( authorization.getTenantId(), authorization.getTemplateId()); if (!result.isValid()) throw new DataException("公式依赖存在循环: " + result.getCyclePath()); audit("ELN_TEMPLATE_FORMULA_ACTIVATE", authorization, binding, before, binding, null); } @Override public void cancel(TemplateAnnotationAuthorization authorization, String formulaBindingId, ElnFormulaForms.CancelFormulaBinding form) { ElnTemplateFormulaBindingEntity binding = requireOwned(authorization, formulaBindingId); String operation = form == null ? null : normalizeOperation(form.getOperationCode()); if (form != null) form.setOperationCode(operation); if (form != null && CANCELLED.equals(binding.getBangdingZhuangtai()) && alreadyApplied(binding, form.getExpectedVersion(), operation)) return; requireMutation(binding, operation, form == null ? null : form.getExpectedVersion(), PENDING); ElnTemplateFormulaBindingEntity before = copy(binding); if (!store.updateStatus(binding, form.getExpectedVersion(), CANCELLED, trimToNull(form.getReason()))) throw new DataException("公式映射版本冲突"); audit("ELN_TEMPLATE_FORMULA_CANCEL", authorization, binding, before, binding, trimToNull(form.getReason())); } @Override public ElnFormulaVos.MappingCheck checkUpgrade(TemplateAnnotationAuthorization authorization, String formulaBindingId, ElnFormulaForms.TemplateBinding target) { ElnTemplateFormulaBindingEntity binding = requireOwned(authorization, formulaBindingId); requireStatus(binding, ACTIVE); String operation = requireAvailableOperation( authorization, target == null ? null : target.getOperationCode(), binding.getId()); target.setOperationCode(operation); validateRequest(authorization, target, binding.getId()); ElnFormulaVos.MappingCheck result = new ElnFormulaVos.MappingCheck(); result.setOperationCode(operation); result.getExpectedVersions().put(binding.getId(), binding.getFVersion()); return result; } @Override public void confirmUpgrade(TemplateAnnotationAuthorization authorization, String formulaBindingId, ElnFormulaForms.UpgradeMappingConfirm form) { lockTemplateGraph(authorization); ElnTemplateFormulaBindingEntity binding = requireOwned(authorization, formulaBindingId); if (form == null || form.getTarget() == null) throw new DataException("升级确认参数不能为空"); String operation = requireAvailableOperation( authorization, form.getOperationCode(), binding.getId()); String targetOperation = requireAvailableOperation( authorization, form.getTarget().getOperationCode(), binding.getId()); if (!operation.equals(targetOperation)) throw new DataException("升级操作码与目标映射不一致"); form.setOperationCode(operation); form.getTarget().setOperationCode(targetOperation); if (alreadyApplied(binding, form.getExpectedVersion(), operation) && sameRequest(binding, form.getTarget())) return; requireStatus(binding, ACTIVE); requireMutationVersion(binding, form.getExpectedVersion()); ValidatedMapping validated = validateRequest(authorization, form.getTarget(), binding.getId()); requireResultLocked(authorization, form.getTarget().getResultBindingId(), form.getControls()); ElnTemplateFormulaBindingEntity before = copy(binding); binding.setGongshiDingyiId(validated.definition.getId()); binding.setGongshiBanbenId(validated.version.getId()); binding.setCanshuYingshe(new LinkedHashMap<>(form.getTarget().getParameterBindings())); binding.setJieguoBangdingId(form.getTarget().getResultBindingId()); binding.setCaozuoBianma(operation); if (!store.updateStatus(binding, form.getExpectedVersion(), ACTIVE, null)) throw new DataException("公式映射版本冲突"); FormulaDependencyGraph.Result result = validateGraph(authorization.getTenantId(), authorization.getTemplateId()); if (!result.isValid()) throw new DataException("公式依赖存在循环: " + result.getCyclePath()); audit("ELN_TEMPLATE_FORMULA_UPGRADE", authorization, binding, before, binding, null); } @Override public ElnFormulaVos.MappingCheck checkRemove(TemplateAnnotationAuthorization authorization, String formulaBindingId) { ElnTemplateFormulaBindingEntity binding = requireOwned(authorization, formulaBindingId); requireStatus(binding, ACTIVE); ElnFormulaVos.MappingCheck result = new ElnFormulaVos.MappingCheck(); result.setOperationCode("remove:" + binding.getId() + ":" + binding.getFVersion()); result.getExpectedVersions().put(binding.getId(), binding.getFVersion()); for (ElnTemplateFormulaBindingEntity candidate : store.listBindings( authorization.getTenantId(), authorization.getTemplateId())) { if (ACTIVE.equals(candidate.getBangdingZhuangtai()) && candidate.getCanshuYingshe() != null && candidate.getCanshuYingshe().containsValue(binding.getJieguoBangdingId())) result.getDownstreamFormulaBindingIds().add(candidate.getId()); } return result; } @Override public void confirmRemove(TemplateAnnotationAuthorization authorization, String formulaBindingId, ElnFormulaForms.RemoveMappingConfirm form) { ElnTemplateFormulaBindingEntity binding = requireOwned(authorization, formulaBindingId); if (form == null) throw new DataException("移除确认参数不能为空"); String operation = normalizeOperation(form.getOperationCode()); form.setOperationCode(operation); if (alreadyApplied(binding, form.getExpectedVersion(), operation) && REMOVED.equals(binding.getBangdingZhuangtai())) return; requireMutationVersion(binding, form.getExpectedVersion()); ElnFormulaVos.MappingCheck checked = checkRemove(authorization, formulaBindingId); if (!checked.getOperationCode().equals(operation)) throw new DataException("移除操作码与预检结果不一致"); if (!checked.getDownstreamFormulaBindingIds().isEmpty()) throw new DataException("公式结果仍被下游公式引用"); requireResultUnlocked(authorization, binding, form.getControls()); ElnTemplateFormulaBindingEntity before = copy(binding); binding.setCaozuoBianma(operation); if (!store.updateStatus(binding, form.getExpectedVersion(), REMOVED, null)) throw new DataException("公式映射版本冲突"); validateGraph(authorization.getTenantId(), authorization.getTemplateId()); audit("ELN_TEMPLATE_FORMULA_REMOVE", authorization, binding, before, binding, null); } @Override public void confirmVersionCopy(TemplateAnnotationAuthorization authorization, ElnFormulaForms.FormulaVersionCopyConfirm form) { lockTemplateGraph(authorization); String upgradeOperation = normalizeOperation( form == null ? null : form.getUpgradeOperationCode()); form.setUpgradeOperationCode(upgradeOperation); ElnTemplateVersionEntity template = store.findTemplate( authorization.getTenantId(), authorization.getTemplateId()); if (template == null || !upgradeOperation.equals( template.getShengbanCaozuoBianma())) throw new DataException("版本复制确认与模板升版操作不匹配"); List copied = new ArrayList<>(); List activeCopied = new ArrayList<>(); for (ElnTemplateFormulaBindingEntity item : store.listBindings( authorization.getTenantId(), authorization.getTemplateId())) { if (item.getLaiyuanGongshiBangdingId() != null && (PENDING.equals(item.getBangdingZhuangtai()) || ACTIVE.equals(item.getBangdingZhuangtai()))) { String expectedOperation = upgradeOperation + ":" + item.getLaiyuanGongshiBangdingId(); if (!expectedOperation.equals(item.getCaozuoBianma())) throw new DataException("版本复制公式映射操作码不匹配"); if (PENDING.equals(item.getBangdingZhuangtai()) && item.getDaoqiShijian() != null) throw new DataException("版本复制公式映射不能设置到期时间"); if (PENDING.equals(item.getBangdingZhuangtai())) copied.add(item); else if (ACTIVE.equals(item.getBangdingZhuangtai())) activeCopied.add(item); } } if (!copied.isEmpty() && !activeCopied.isEmpty()) throw new DataException("版本复制公式映射状态混合,不能部分确认"); if (copied.isEmpty() && form.getExpectedVersions().keySet().equals(ids(activeCopied))) { boolean retry = true; for (ElnTemplateFormulaBindingEntity item : activeCopied) { Integer expected = form.getExpectedVersions().get(item.getId()); retry &= expected != null && item.getFVersion() != null && item.getFVersion() == expected + 1; } if (retry) return; } if (!form.getExpectedVersions().keySet().equals(ids(copied))) throw new DataException("版本复制公式映射集合不完整"); for (ElnTemplateFormulaBindingEntity item : copied) { Integer expected = form.getExpectedVersions().get(item.getId()); requireMutationVersion(item, expected); } requireResultsLocked(authorization, copied, form.getControls()); for (ElnTemplateFormulaBindingEntity item : copied) { ElnTemplateFormulaBindingEntity before = copy(item); if (!store.updateStatus(item, form.getExpectedVersions().get(item.getId()), ACTIVE, null)) throw new DataException("版本复制公式映射版本冲突"); audit("ELN_TEMPLATE_FORMULA_VERSION_COPY_CONFIRM", authorization, item, before, item, null); } FormulaDependencyGraph.Result result = validateGraph(authorization.getTenantId(), authorization.getTemplateId()); if (!result.isValid()) throw new DataException("公式依赖存在循环: " + result.getCyclePath()); } @Override public FormulaDependencyGraph.Result validateGraph(String tenantId, String templateId) { List active = new ArrayList<>(); for (ElnTemplateFormulaBindingEntity item : store.listBindings(tenantId, templateId)) if (ACTIVE.equals(item.getBangdingZhuangtai())) active.add(item); FormulaDependencyGraph.Result result = graph.validate(active); if (result.isValid()) { for (int index = 0; index < result.getTopologicalOrder().size(); index++) { String id = result.getTopologicalOrder().get(index); for (ElnTemplateFormulaBindingEntity item : active) if (id.equals(item.getId())) store.updateTopology(item, index); } } return result; } private ValidatedMapping validateRequest(TemplateAnnotationAuthorization auth, ElnFormulaForms.TemplateBinding form, String ignoredResultOwnerId) { if (form == null || !StringUtils.hasText(form.getFormulaVersionId()) || !StringUtils.hasText(form.getResultBindingId())) throw new DataException("公式映射参数不完整"); ElnTemplateVersionEntity template = store.findTemplate(auth.getTenantId(), auth.getTemplateId()); if (template == null || !auth.getTemplateId().equals(template.getId())) throw new DataException("模板不存在或跨租户"); ElnFormulaVersionEntity version = store.findFormulaVersion(auth.getTenantId(), form.getFormulaVersionId()); if (version == null) throw new DataException("公式版本不存在或跨租户"); ElnFormulaDefEntity definition = store.findFormulaDefinition(auth.getTenantId(), version.getGongshiDingyiId()); if (definition == null) throw new DataException("公式定义不存在或跨租户"); if (!"enabled".equals(definition.getBizEnabled())) throw new DataException("停用公式不能新增映射"); boolean currentPublished = PUBLISHED.equals(version.getBizStatus()) && version.getId().equals(definition.getDangqianGongshiBanbenId()); if ("DRAFT".equals(template.getBizStatus())) { if (!PRE_RELEASE.equals(version.getBizStatus()) && !currentPublished) throw new DataException("历史已发布公式版本只允许模板升版继承"); } else if (!PUBLISHED.equals(version.getBizStatus())) { throw new DataException("正式模板只能引用已发布公式版本"); } List slots = store.listSlots(auth.getTenantId(), version.getId()); Map parameters = form.getParameterBindings() == null ? Collections.emptyMap() : form.getParameterBindings(); Set expectedKeys = new LinkedHashSet<>(); for (ElnFormulaParameterSlotEntity slot : slots) expectedKeys.add(slot.getCanshuBianma()); if (!expectedKeys.equals(parameters.keySet())) throw new DataException("参数映射必须与公式参数槽完全一致"); Map parameterBindings = new LinkedHashMap<>(); for (ElnFormulaParameterSlotEntity slot : slots) { ElnTemplateBindingEntity fieldBinding = requireFormulaField( auth, parameters.get(slot.getCanshuBianma())); ElnFieldVersionEntity fieldVersion = requireFieldVersion(auth, fieldBinding); requireParameterCompatible(slot.getShujuLeixing(), fieldBinding, fieldVersion.getShujuLeixing()); parameterBindings.put(slot.getCanshuBianma(), fieldBinding); } ElnTemplateBindingEntity resultBinding = requireFormulaField(auth, form.getResultBindingId()); if (StringUtils.hasText(resultBinding.getCheckboxGroupCode())) { throw new DataException("选择组成员不能作为公式结果字段"); } if (resultBindingGuard.isCrossTemplateTarget(auth.getTenantId(), resultBinding.getId())) { throw new DataException("跨模板字段只能作为公式输入,不能作为公式结果"); } if (parameters.containsValue(resultBinding.getId())) throw new DataException("结果字段不能作为公式自身参数"); ElnFieldVersionEntity resultVersion = requireFieldVersion(auth, resultBinding); requireResultCompatible(version.getJieguoLeixing(), resultVersion.getShujuLeixing()); requireFormulaShape(slots, parameterBindings, resultBinding); requireFormulaResult(resultVersion); for (ElnTemplateFormulaBindingEntity item : store.listBindings(auth.getTenantId(), auth.getTemplateId())) { if ((PENDING.equals(item.getBangdingZhuangtai()) || ACTIVE.equals(item.getBangdingZhuangtai())) && resultBinding.getId().equals(item.getJieguoBangdingId()) && !item.getId().equals(ignoredResultOwnerId)) throw new DataException("结果字段只允许一个公式写入"); } return new ValidatedMapping(definition, version); } private ElnTemplateBindingEntity requireFormulaField( TemplateAnnotationAuthorization auth, String id) { ElnTemplateBindingEntity binding = store.findFieldBinding(auth.getTenantId(), trim(id)); if (binding == null || !auth.getTemplateId().equals(binding.getMubanId()) || !ACTIVE.equals(binding.getBangdingZhuangtai()) || !("PRIMARY".equals(binding.getKongjianJuese()) || "REPEAT".equals(binding.getKongjianJuese()))) throw new DataException("参数和结果必须引用当前模板 ACTIVE PRIMARY/REPEAT 字段绑定"); return binding; } private ElnFieldVersionEntity requireFieldVersion(TemplateAnnotationAuthorization auth, ElnTemplateBindingEntity binding) { ElnFieldVersionEntity version = store.findFieldVersion(auth.getTenantId(), binding.getZiduanBanbenId()); if (version == null || !Objects.equals(version.getId(), binding.getZiduanBanbenId()) || !Objects.equals(version.getZiduanDingyiId(), binding.getZiduanDingyiId())) throw new DataException("字段固定版本不存在或跨租户"); return version; } private static void requireParameterCompatible(String formulaType, ElnTemplateBindingEntity binding, String fieldType) { if ("DECIMAL_LIST".equals(formulaType)) { if (!"REPEAT".equals(binding.getKongjianJuese()) || !"DECIMAL".equals(fieldType)) { throw new DataException("DECIMAL_LIST 参数必须映射 DECIMAL 重复字段"); } return; } requireCompatible(formulaType, fieldType); } private static void requireResultCompatible(String formulaType, String fieldType) { if ("DECIMAL_LIST".equals(formulaType)) { throw new DataException("UNSUPPORTED_COLLECTION_BINDING"); } requireCompatible(formulaType, fieldType); } private static void requireCompatible(String formulaType, String fieldType) { boolean compatible = "DECIMAL".equals(formulaType) && "DECIMAL".equals(fieldType) || "TEXT".equals(formulaType) && ("TEXT".equals(fieldType) || "TEXTAREA".equals(fieldType) || "SELECT".equals(fieldType)) || "BOOLEAN".equals(formulaType) && "CHECKBOX".equals(fieldType); if (!compatible) throw new DataException("公式参数或结果类型与字段类型不兼容"); } interface ResultBindingGuard { boolean isCrossTemplateTarget(String tenantId, String bindingId); } private static void requireFormulaShape(List slots, Map parameters, ElnTemplateBindingEntity result) { boolean rowFormula = "REPEAT".equals(result.getKongjianJuese()); for (ElnFormulaParameterSlotEntity slot : slots) { ElnTemplateBindingEntity parameter = parameters.get(slot.getCanshuBianma()); boolean list = "DECIMAL_LIST".equals(slot.getShujuLeixing()); if (list && rowFormula) throw new DataException("逐行公式不支持 DECIMAL_LIST 参数"); if (rowFormula && !"REPEAT".equals(parameter.getKongjianJuese())) { throw new DataException("逐行公式参数必须映射同组重复字段"); } if (!list && !rowFormula && "REPEAT".equals(parameter.getKongjianJuese())) { throw new DataException("聚合公式的重复字段必须映射 DECIMAL_LIST 参数"); } if (rowFormula && "REPEAT".equals(parameter.getKongjianJuese()) && !Objects.equals(result.getRepeatGroupCode(), parameter.getRepeatGroupCode())) { throw new DataException("逐行公式参数和结果必须属于同一重复组"); } } } @SuppressWarnings("unchecked") private static void requireFormulaResult(ElnFieldVersionEntity version) { Map config = StringUtils.hasText(version.getBangdingPeizhi()) ? JsonUtil.getJsonToBean(version.getBangdingPeizhi(), Map.class) : null; Map read = config == null ? null : (Map) config.get("read"); Map write = config == null ? null : (Map) config.get("write"); if (read == null || write == null || !"NONE".equals(read.get("source")) || !"ELN_RECORD".equals(write.get("target")) || !"RECORD_PRIVATE".equals(version.getPiciMoshi())) throw new DataException("结果字段必须为 NONE/ELN_RECORD/RECORD_PRIVATE"); } private void requireResultLocked(TemplateAnnotationAuthorization auth, String resultBindingId, List controls) { Set expected = resultControlBindingIds(auth, resultBindingId); requireControlsLocked(auth, expected, controls); } private void requireOriginalResultLockSnapshot(TemplateAnnotationAuthorization auth, String resultBindingId, List controls) { Set expected = resultControlBindingIds(auth, resultBindingId); Map actual = byBindingId(controls); if (!actual.keySet().equals(expected) || controls == null || controls.size() != expected.size()) throw new DataException("FORMULA_RESULT_LOCK_MISMATCH"); for (String id : expected) { ElnTemplateBindingEntity binding = store.findFieldBinding(auth.getTenantId(), id); ElnFormulaForms.ControlSnapshot control = actual.get(id); if (!Objects.equals(binding.getKongjianBiaoqian(), control.getControlTag()) || !Objects.equals(binding.getKongjianJuese(), control.getControlRole()) || !isOriginalLock(control.getLock())) throw new DataException("FORMULA_RESULT_LOCK_MISMATCH"); } } private void requireResultsLocked(TemplateAnnotationAuthorization auth, List formulaBindings, List controls) { Set expected = new LinkedHashSet<>(); for (ElnTemplateFormulaBindingEntity binding : formulaBindings) { expected.addAll(resultControlBindingIds(auth, binding)); } requireControlsLocked(auth, expected, controls); } private void requireControlsLocked(TemplateAnnotationAuthorization auth, Set expected, List controls) { Map actual = byBindingId(controls); if (!actual.keySet().equals(expected) || controls == null || controls.size() != expected.size()) throw new DataException("FORMULA_RESULT_LOCK_MISMATCH"); for (String id : expected) { ElnTemplateBindingEntity binding = store.findFieldBinding(auth.getTenantId(), id); ElnFormulaForms.ControlSnapshot control = actual.get(id); if (!Objects.equals(binding.getKongjianBiaoqian(), control.getControlTag()) || !Objects.equals(binding.getKongjianJuese(), control.getControlRole()) || !"sdtContentLocked".equals(control.getLock())) throw new DataException("FORMULA_RESULT_LOCK_MISMATCH"); } } private void requireResultUnlocked(TemplateAnnotationAuthorization auth, ElnTemplateFormulaBindingEntity formulaBinding, List controls) { Set expected = resultControlBindingIds(auth, formulaBinding); Map actual = byBindingId(controls); if (!actual.keySet().equals(expected) || controls == null || controls.size() != expected.size()) throw new DataException("FORMULA_RESULT_LOCK_MISMATCH"); for (String id : expected) { ElnTemplateBindingEntity binding = store.findFieldBinding(auth.getTenantId(), id); ElnFormulaForms.ControlSnapshot control = actual.get(id); if (!Objects.equals(binding.getKongjianBiaoqian(), control.getControlTag()) || !Objects.equals(binding.getKongjianJuese(), control.getControlRole()) || StringUtils.hasText(control.getLock())) throw new DataException("FORMULA_RESULT_LOCK_MISMATCH"); } } private Set resultControlBindingIds(TemplateAnnotationAuthorization auth, ElnTemplateFormulaBindingEntity formulaBinding) { return resultControlBindingIds(auth, formulaBinding.getJieguoBangdingId()); } private Set resultControlBindingIds(TemplateAnnotationAuthorization auth, String resultBindingId) { Set result = new LinkedHashSet<>(); result.add(resultBindingId); for (ElnTemplateBindingEntity item : store.listFieldBindings(auth.getTenantId(), auth.getTemplateId())) if (ACTIVE.equals(item.getBangdingZhuangtai()) && "REFERENCE".equals(item.getKongjianJuese()) && resultBindingId.equals(item.getYinyongShiliId())) result.add(item.getId()); return result; } private static Map byBindingId( List controls) { Map result = new LinkedHashMap<>(); if (controls != null) for (ElnFormulaForms.ControlSnapshot item : controls) if (item != null && StringUtils.hasText(item.getBindingId())) result.put(item.getBindingId(), item); return result; } private static boolean isOriginalLock(String lock) { return "".equals(lock) || "contentLocked".equals(lock) || "sdtContentLocked".equals(lock) || "unlocked".equals(lock); } private ElnTemplateFormulaBindingEntity requireOwned(TemplateAnnotationAuthorization auth, String id) { requireAuthorization(auth); ElnTemplateFormulaBindingEntity binding = store.findBinding(auth.getTenantId(), trim(id)); if (binding == null || !auth.getTemplateId().equals(binding.getMubanId())) throw new DataException("公式映射不存在或不属于当前模板"); return binding; } private static void requireAuthorization(TemplateAnnotationAuthorization auth) { if (auth == null || !StringUtils.hasText(auth.getTenantId()) || !StringUtils.hasText(auth.getTemplateId())) throw new DataException("标注授权无效"); } private void lockTemplateGraph(TemplateAnnotationAuthorization authorization) { requireAuthorization(authorization); if (store.lockTemplate(authorization.getTenantId(), authorization.getTemplateId()) == null) throw new DataException("模板不存在或跨租户"); } private static String normalizeOperation(String operation) { String normalized = operation == null ? null : operation.trim(); if (!StringUtils.hasText(normalized)) throw new DataException("操作码不能为空"); if (normalized.length() > 160) throw new DataException("操作码长度不能超过160个字符"); return normalized; } private String requireAvailableOperation(TemplateAnnotationAuthorization auth, String operation, String currentBindingId) { String normalized = normalizeOperation(operation); ElnTemplateFormulaBindingEntity owner = store.findByOperation( auth.getTenantId(), auth.getTemplateId(), normalized); if (owner != null && !Objects.equals(owner.getId(), currentBindingId)) throw new DataException("操作码已用于其他公式映射请求"); return normalized; } private static void requireMutation(ElnTemplateFormulaBindingEntity binding, String operation, Integer version, String status) { String normalized = normalizeOperation(operation); if (!Objects.equals(binding.getCaozuoBianma(), normalized)) throw new DataException("操作码不匹配"); requireStatus(binding, status); requireMutationVersion(binding, version); } private static void requireMutationVersion(ElnTemplateFormulaBindingEntity binding, Integer version) { if (version == null || !version.equals(binding.getFVersion())) throw new DataException("公式映射版本冲突"); } private static boolean alreadyApplied(ElnTemplateFormulaBindingEntity binding, Integer expectedVersion, String operationCode) { return expectedVersion != null && binding.getFVersion() != null && binding.getFVersion() == expectedVersion + 1 && Objects.equals(operationCode, binding.getCaozuoBianma()); } private static void requireStatus(ElnTemplateFormulaBindingEntity binding, String status) { if (!status.equals(binding.getBangdingZhuangtai())) throw new DataException("公式映射状态无效"); } private static boolean sameRequest(ElnTemplateFormulaBindingEntity item, ElnFormulaForms.TemplateBinding form) { return Objects.equals(item.getGongshiBanbenId(), trim(form.getFormulaVersionId())) && Objects.equals(item.getJieguoBangdingId(), trim(form.getResultBindingId())) && Objects.equals(item.getCanshuYingshe(), form.getParameterBindings()); } private static boolean sameReserve(ElnTemplateFormulaBindingEntity item, TemplateAnnotationAuthorization authorization, ElnFormulaForms.TemplateBinding form, String definitionId, String operation) { return Objects.equals(item.getFTenantId(), authorization.getTenantId()) && Objects.equals(item.getMubanId(), authorization.getTemplateId()) && Objects.equals(item.getGongshiDingyiId(), definitionId) && Objects.equals(item.getGongshiBanbenId(), trim(form.getFormulaVersionId())) && Objects.equals(item.getCanshuYingshe(), form.getParameterBindings()) && Objects.equals(item.getJieguoBangdingId(), trim(form.getResultBindingId())) && sameControls(item.getJieguoSuoKuaizhao(), form.getJieguoSuoKuaizhao()) && Objects.equals(item.getCaozuoBianma(), operation) && item.getLaiyuanGongshiBangdingId() == null && item.getDaoqiShijian() != null && (PENDING.equals(item.getBangdingZhuangtai()) || ACTIVE.equals(item.getBangdingZhuangtai())); } private static ElnFormulaForms.TemplateBinding request(ElnTemplateFormulaBindingEntity item) { ElnFormulaForms.TemplateBinding form = new ElnFormulaForms.TemplateBinding(); form.setFormulaVersionId(item.getGongshiBanbenId()); form.setParameterBindings(item.getCanshuYingshe()); form.setResultBindingId(item.getJieguoBangdingId()); form.setOperationCode(item.getCaozuoBianma()); return form; } private static List copyControls( List controls) { List result = new ArrayList<>(); if (controls != null) for (ElnFormulaForms.ControlSnapshot item : controls) { if (item == null) continue; ElnFormulaForms.ControlSnapshot copied = new ElnFormulaForms.ControlSnapshot(); copied.setBindingId(item.getBindingId()); copied.setControlTag(item.getControlTag()); copied.setControlRole(item.getControlRole()); copied.setLock(item.getLock()); result.add(copied); } return result; } private static boolean sameControls(List left, List right) { return copyControls(left).equals(copyControls(right)); } private static Set ids(List items) { Set result = new LinkedHashSet<>(); for (ElnTemplateFormulaBindingEntity item : items) result.add(item.getId()); return result; } private static String trim(String value) { return value == null ? null : value.trim(); } private static String trimToNull(String value) { return StringUtils.hasText(value) ? value.trim() : null; } private void audit(String action, TemplateAnnotationAuthorization auth, ElnTemplateFormulaBindingEntity target, Object before, Object after, String reason) { Map snapshot = new LinkedHashMap<>(); snapshot.put("templateId", target.getMubanId()); snapshot.put("formulaDefinitionId", target.getGongshiDingyiId()); snapshot.put("formulaVersionId", target.getGongshiBanbenId()); snapshot.put("parameterBindings", target.getCanshuYingshe()); snapshot.put("resultBindingId", target.getJieguoBangdingId()); snapshot.put("operationCode", target.getCaozuoBianma()); snapshot.put("reason", reason); snapshot.put("before", before); snapshot.put("after", after); auditRecorder.accept(AuditEventDTO.builder().tenantId(auth.getTenantId()).operatorId(auth.getUserId()) .eventTime(new Date()).eventType("DATA_CHANGE").actionCode(action) .actionLabel(action).bizModule("eln").bizType("eln_template_formula_binding") .targetTable("eln_template_formula_binding").targetId(target.getId()).reason(reason) .dataSnapshot(JsonUtil.getObjectToString(snapshot)).build()); } private static ElnFormulaVos.TemplateBinding toVo(ElnTemplateFormulaBindingEntity item) { ElnFormulaVos.TemplateBinding result = new ElnFormulaVos.TemplateBinding(); result.setId(item.getId()); result.setTemplateId(item.getMubanId()); result.setFormulaDefinitionId(item.getGongshiDingyiId()); result.setFormulaVersionId(item.getGongshiBanbenId()); result.setParameterBindings(item.getCanshuYingshe()); result.setResultBindingId(item.getJieguoBangdingId()); result.setStatus(item.getBangdingZhuangtai()); result.setTopologicalOrder(item.getTuopuXuhao()); result.setOperationCode(item.getCaozuoBianma()); result.setVersion(item.getFVersion()); result.setJieguoSuoKuaizhao(copyControls(item.getJieguoSuoKuaizhao())); return result; } private ElnFormulaVos.TemplateFormulaVersion toTemplateFormulaVersion(String tenantId, ElnFormulaDefEntity definition, ElnFormulaVersionEntity version) { ElnFormulaVos.TemplateFormulaVersion result = new ElnFormulaVos.TemplateFormulaVersion(); result.setId(version.getId()); result.setFormulaCode(definition.getGongshiBianma()); result.setFormulaName(definition.getGongshiMingcheng()); result.setFormulaEnabled("enabled".equals(definition.getBizEnabled())); result.setIsCurrent(version.getId().equals(definition.getDangqianGongshiBanbenId())); result.setResultType(version.getJieguoLeixing()); result.setStatus(version.getBizStatus()); for (ElnFormulaParameterSlotEntity item : store.listSlots(tenantId, version.getId())) { ElnFormulaVos.TemplateFormulaSlot slot = new ElnFormulaVos.TemplateFormulaSlot(); slot.setCode(item.getCanshuBianma()); slot.setName(item.getCanshuMingcheng()); slot.setRequired(Integer.valueOf(1).equals(item.getShifouBitian())); slot.setType(item.getShujuLeixing()); result.getSlots().add(slot); } return result; } private static ElnTemplateFormulaBindingEntity copy(ElnTemplateFormulaBindingEntity item) { return JsonUtil.getJsonToBean(JsonUtil.getObjectToString(item), ElnTemplateFormulaBindingEntity.class); } private static final class ValidatedMapping { private final ElnFormulaDefEntity definition; private final ElnFormulaVersionEntity version; private ValidatedMapping(ElnFormulaDefEntity definition, ElnFormulaVersionEntity version) { this.definition = definition; this.version = version; } } public interface Store { ElnTemplateVersionEntity findTemplate(String tenantId, String id); ElnTemplateVersionEntity lockTemplate(String tenantId, String id); ElnFormulaDefEntity findFormulaDefinition(String tenantId, String id); default List listFormulaDefinitions(String tenantId) { return Collections.emptyList(); } ElnFormulaVersionEntity findFormulaVersion(String tenantId, String id); default List listFormulaVersions(String tenantId) { return Collections.emptyList(); } List listSlots(String tenantId, String versionId); ElnTemplateBindingEntity findFieldBinding(String tenantId, String id); default List listFieldBindings(String tenantId, String templateId) { return Collections.emptyList(); } ElnFieldVersionEntity findFieldVersion(String tenantId, String id); ElnTemplateFormulaBindingEntity findByOperation(String tenantId, String templateId, String operation); ElnTemplateFormulaBindingEntity findBinding(String tenantId, String id); List listBindings(String tenantId, String templateId); boolean insertIfAbsent(ElnTemplateFormulaBindingEntity binding); boolean updateStatus(ElnTemplateFormulaBindingEntity binding, int expectedVersion, String status, String reason); void updateTopology(ElnTemplateFormulaBindingEntity binding, int order); } private static final class MyBatisStore implements Store { private final ElnTemplateMapper templates; private final ElnFormulaDefMapper definitions; private final ElnFormulaVersionMapper formulaVersions; private final ElnFormulaParameterSlotMapper slots; private final ElnTemplateBindingMapper fieldBindings; private final ElnFieldVersionMapper fieldVersions; private final ElnTemplateFormulaBindingMapper formulaBindings; private MyBatisStore(ElnTemplateMapper templates, ElnFormulaDefMapper definitions, ElnFormulaVersionMapper formulaVersions, ElnFormulaParameterSlotMapper slots, ElnTemplateBindingMapper fieldBindings, ElnFieldVersionMapper fieldVersions, ElnTemplateFormulaBindingMapper formulaBindings) { this.templates=templates; this.definitions=definitions; this.formulaVersions=formulaVersions; this.slots=slots; this.fieldBindings=fieldBindings; this.fieldVersions=fieldVersions; this.formulaBindings=formulaBindings; } @Override public ElnTemplateVersionEntity findTemplate(String t,String id){return templates.selectOne(Wrappers.lambdaQuery().eq(ElnTemplateVersionEntity::getFTenantId,t).eq(ElnTemplateVersionEntity::getId,id).and(q->q.isNull(ElnTemplateVersionEntity::getFDeleteMark).or().eq(ElnTemplateVersionEntity::getFDeleteMark,0)));} @Override public ElnTemplateVersionEntity lockTemplate(String t,String id){return templates.lockTemplateForUpdate(t,id);} @Override public ElnFormulaDefEntity findFormulaDefinition(String t,String id){return definitions.selectOne(Wrappers.lambdaQuery().eq(ElnFormulaDefEntity::getFTenantId,t).eq(ElnFormulaDefEntity::getId,id).and(q->q.isNull(ElnFormulaDefEntity::getFDeleteMark).or().eq(ElnFormulaDefEntity::getFDeleteMark,0)));} @Override public List listFormulaDefinitions(String t){return definitions.selectList(Wrappers.lambdaQuery().eq(ElnFormulaDefEntity::getFTenantId,t).and(q->q.isNull(ElnFormulaDefEntity::getFDeleteMark).or().eq(ElnFormulaDefEntity::getFDeleteMark,0)).orderByAsc(ElnFormulaDefEntity::getId));} @Override public ElnFormulaVersionEntity findFormulaVersion(String t,String id){return formulaVersions.selectOne(Wrappers.lambdaQuery().eq(ElnFormulaVersionEntity::getFTenantId,t).eq(ElnFormulaVersionEntity::getId,id).and(q->q.isNull(ElnFormulaVersionEntity::getFDeleteMark).or().eq(ElnFormulaVersionEntity::getFDeleteMark,0)));} @Override public List listFormulaVersions(String t){return formulaVersions.selectList(Wrappers.lambdaQuery().eq(ElnFormulaVersionEntity::getFTenantId,t).and(q->q.isNull(ElnFormulaVersionEntity::getFDeleteMark).or().eq(ElnFormulaVersionEntity::getFDeleteMark,0)).orderByAsc(ElnFormulaVersionEntity::getGongshiDingyiId).orderByAsc(ElnFormulaVersionEntity::getBanbenHao));} @Override public List listSlots(String t,String id){return slots.selectList(Wrappers.lambdaQuery().eq(ElnFormulaParameterSlotEntity::getFTenantId,t).eq(ElnFormulaParameterSlotEntity::getGongshiBanbenId,id).and(q->q.isNull(ElnFormulaParameterSlotEntity::getFDeleteMark).or().eq(ElnFormulaParameterSlotEntity::getFDeleteMark,0)).orderByAsc(ElnFormulaParameterSlotEntity::getPaixu));} @Override public ElnTemplateBindingEntity findFieldBinding(String t,String id){return fieldBindings.selectOne(Wrappers.lambdaQuery().eq(ElnTemplateBindingEntity::getFTenantId,t).eq(ElnTemplateBindingEntity::getId,id).and(q->q.isNull(ElnTemplateBindingEntity::getFDeleteMark).or().eq(ElnTemplateBindingEntity::getFDeleteMark,0)));} @Override public List listFieldBindings(String t,String m){return fieldBindings.selectList(Wrappers.lambdaQuery().eq(ElnTemplateBindingEntity::getFTenantId,t).eq(ElnTemplateBindingEntity::getMubanId,m).and(q->q.isNull(ElnTemplateBindingEntity::getFDeleteMark).or().eq(ElnTemplateBindingEntity::getFDeleteMark,0)));} @Override public ElnFieldVersionEntity findFieldVersion(String t,String id){return fieldVersions.selectOne(Wrappers.lambdaQuery().eq(ElnFieldVersionEntity::getFTenantId,t).eq(ElnFieldVersionEntity::getId,id).and(q->q.isNull(ElnFieldVersionEntity::getFDeleteMark).or().eq(ElnFieldVersionEntity::getFDeleteMark,0)));} @Override public ElnTemplateFormulaBindingEntity findByOperation(String t,String m,String o){return formulaBindings.selectOne(Wrappers.lambdaQuery().eq(ElnTemplateFormulaBindingEntity::getFTenantId,t).eq(ElnTemplateFormulaBindingEntity::getMubanId,m).eq(ElnTemplateFormulaBindingEntity::getCaozuoBianma,o).and(q->q.isNull(ElnTemplateFormulaBindingEntity::getFDeleteMark).or().eq(ElnTemplateFormulaBindingEntity::getFDeleteMark,0)));} @Override public ElnTemplateFormulaBindingEntity findBinding(String t,String id){return formulaBindings.selectOne(Wrappers.lambdaQuery().eq(ElnTemplateFormulaBindingEntity::getFTenantId,t).eq(ElnTemplateFormulaBindingEntity::getId,id).and(q->q.isNull(ElnTemplateFormulaBindingEntity::getFDeleteMark).or().eq(ElnTemplateFormulaBindingEntity::getFDeleteMark,0)));} @Override public List listBindings(String t,String m){return formulaBindings.selectList(Wrappers.lambdaQuery().eq(ElnTemplateFormulaBindingEntity::getFTenantId,t).eq(ElnTemplateFormulaBindingEntity::getMubanId,m).and(q->q.isNull(ElnTemplateFormulaBindingEntity::getFDeleteMark).or().eq(ElnTemplateFormulaBindingEntity::getFDeleteMark,0)).orderByAsc(ElnTemplateFormulaBindingEntity::getId));} @Override public boolean insertIfAbsent(ElnTemplateFormulaBindingEntity b){return formulaBindings.insertIfAbsent(b)==1;} @Override public boolean updateStatus(ElnTemplateFormulaBindingEntity b,int v,String s,String r){int n=formulaBindings.updateStatus(b,v,s,r); if(n==1){b.setBangdingZhuangtai(s);b.setZhuangtaiYuanyin(r);b.setFVersion(v+1);} return n==1;} @Override public void updateTopology(ElnTemplateFormulaBindingEntity b,int order){formulaBindings.update(null,Wrappers.lambdaUpdate().eq(ElnTemplateFormulaBindingEntity::getFTenantId,b.getFTenantId()).eq(ElnTemplateFormulaBindingEntity::getId,b.getId()).eq(ElnTemplateFormulaBindingEntity::getBangdingZhuangtai,ACTIVE).set(ElnTemplateFormulaBindingEntity::getTuopuXuhao,order));b.setTuopuXuhao(order);} } }