package jnpf.elnService.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import jnpf.base.UserInfo; import jnpf.database.util.TenantDataSourceUtil; import jnpf.elnEntity.runtime.ElnRecordEntity; import jnpf.elnEntity.runtime.ElnRecordFieldValueEntity; import jnpf.elnEntity.runtime.ElnRecordFormulaCalculationResult; import jnpf.elnEntity.runtime.ElnRecordRepeatValueEntity; import jnpf.elnEntity.runtime.ElnRecordForms; import jnpf.elnEntity.runtime.ElnRecordRevisionEntity; import jnpf.elnEntity.runtime.ElnRecordVos; import jnpf.elnMapper.ElnRecordFieldValueMapper; import jnpf.elnMapper.ElnRecordMapper; import jnpf.elnMapper.ElnRecordRepeatValueMapper; import jnpf.elnMapper.ElnRecordRevisionMapper; import jnpf.elnService.ElnTestWorkItemSnapshotProvider; import jnpf.elnService.ElnRecordFormulaCalculationService; import jnpf.elnService.ElnRecordRecalculationService; import jnpf.exception.DataException; import jnpf.lims.api.model.TestWorkItemSnapshotBatch; import jnpf.lims.api.model.TestWorkItemSnapshot; import jnpf.model.tenant.TenantVO; import jnpf.util.UserProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.Collections; import java.util.List; @Service public class ElnRecordRecalculationServiceImpl implements ElnRecordRecalculationService { private final ElnRecordMapper recordMapper; private final ElnRecordRevisionMapper revisionMapper; private final ElnRecordFieldValueMapper valueMapper; private final ElnRecordRepeatValueMapper repeatValueMapper; private final ElnTestWorkItemSnapshotProvider taskProvider; private final ElnRecordFormulaCalculationService calculationService; private final CurrentUserProvider currentUserProvider; @Autowired public ElnRecordRecalculationServiceImpl( ElnRecordMapper recordMapper, ElnRecordRevisionMapper revisionMapper, ElnRecordFieldValueMapper valueMapper, ElnRecordRepeatValueMapper repeatValueMapper, ElnTestWorkItemSnapshotProvider taskProvider, ElnRecordFormulaCalculationService calculationService) { this(recordMapper, revisionMapper, valueMapper, repeatValueMapper, taskProvider, calculationService, ElnRecordRecalculationServiceImpl::currentUser); } ElnRecordRecalculationServiceImpl( ElnRecordMapper recordMapper, ElnRecordRevisionMapper revisionMapper, ElnRecordFieldValueMapper valueMapper, ElnTestWorkItemSnapshotProvider taskProvider, ElnRecordFormulaCalculationService calculationService, CurrentUserProvider currentUserProvider) { this(recordMapper, revisionMapper, valueMapper, null, taskProvider, calculationService, currentUserProvider); } ElnRecordRecalculationServiceImpl( ElnRecordMapper recordMapper, ElnRecordRevisionMapper revisionMapper, ElnRecordFieldValueMapper valueMapper, ElnRecordRepeatValueMapper repeatValueMapper, ElnTestWorkItemSnapshotProvider taskProvider, ElnRecordFormulaCalculationService calculationService, CurrentUserProvider currentUserProvider) { this.recordMapper = recordMapper; this.revisionMapper = revisionMapper; this.valueMapper = valueMapper; this.repeatValueMapper = repeatValueMapper; this.taskProvider = taskProvider; this.calculationService = calculationService; this.currentUserProvider = currentUserProvider; } @Override @Transactional(rollbackFor = Exception.class) public ElnRecordVos.Recalculated recalculateAll( String recordId, ElnRecordForms.Recalculate command) { String actualRecordId = required(recordId, "电子记录不能为空"); String operationCode = required( command == null ? null : command.getOperationCode(), "计算操作码不能为空"); Integer expectedVersion = command == null ? null : command.getExpectedRecordVersion(); if (expectedVersion == null || expectedVersion < 0) { throw new DataException("电子记录版本不能为空"); } UserContext user = currentUserProvider.get(); ElnRecordEntity record = recordMapper.selectOne( Wrappers.lambdaQuery() .eq(ElnRecordEntity::getFTenantId, user.tenantId) .eq(ElnRecordEntity::getId, actualRecordId) .and(q -> q.isNull(ElnRecordEntity::getFDeleteMark) .or().eq(ElnRecordEntity::getFDeleteMark, 0)).last("limit 1")); if (record == null || !StringUtils.hasText(record.getDangqianXiudingId())) { throw new DataException("电子记录不存在或当前修订无效"); } ElnRecordRevisionEntity revision = revisionMapper.selectOne( Wrappers.lambdaQuery() .eq(ElnRecordRevisionEntity::getFTenantId, user.tenantId) .eq(ElnRecordRevisionEntity::getId, record.getDangqianXiudingId()) .eq(ElnRecordRevisionEntity::getDianziJiluId, record.getId()) .and(q -> q.isNull(ElnRecordRevisionEntity::getFDeleteMark) .or().eq(ElnRecordRevisionEntity::getFDeleteMark, 0)).last("limit 1")); if (revision == null || !"DRAFT".equals(revision.getBizStatus())) { throw new DataException("电子记录当前修订不允许重新计算"); } assertTaskAccess(record); if (!expectedVersion.equals(record.getFVersion()) || recordMapper.advanceVersion(user.tenantId, record.getId(), expectedVersion, user.userId) != 1) { throw new DataException("电子记录已被其他操作更新"); } List values = valueMapper.selectList( Wrappers.lambdaQuery() .eq(ElnRecordFieldValueEntity::getFTenantId, user.tenantId) .eq(ElnRecordFieldValueEntity::getDianziJiluId, record.getId()) .eq(ElnRecordFieldValueEntity::getXiudingId, revision.getId()) .and(q -> q.isNull(ElnRecordFieldValueEntity::getFDeleteMark) .or().eq(ElnRecordFieldValueEntity::getFDeleteMark, 0))); ElnRecordFormulaCalculationResult calculated = calculationService.recalculateAllWithRepeats( user.tenantId, record, revision, values, operationCode, user.userId); for (ElnRecordFieldValueEntity value : calculated.getFieldValues()) { if (valueMapper.updateById(value) != 1) { throw new DataException("公式结果字段保存冲突"); } } for (ElnRecordRepeatValueEntity value : calculated.getRepeatValues()) { if (repeatValueMapper == null || repeatValueMapper.updateById(value) != 1) { throw new DataException("重复公式结果字段保存冲突"); } } return result(record, revision, operationCode, expectedVersion + 1, calculated); } private void assertTaskAccess(ElnRecordEntity record) { TestWorkItemSnapshotBatch batch = taskProvider.resolve( Collections.singletonList(record.getJianyanXiangId()), Collections.emptyList()); if (batch == null || batch.getItems() == null || batch.getItems().size() != 1) { throw new DataException("LIMS 检验任务快照返回无效"); } TestWorkItemSnapshot item = batch.getItems().get(0); if (item == null || !record.getJianyanXiangId().equals(item.getTestWorkItemId()) || !"SUCCESS".equals(item.getOutcomeCode())) { throw new DataException(item != null && StringUtils.hasText(item.getReason()) ? item.getReason() : "当前用户或检验项状态不允许重新计算电子记录"); } } private static ElnRecordVos.Recalculated result( ElnRecordEntity record, ElnRecordRevisionEntity revision, String operationCode, int recordVersion, ElnRecordFormulaCalculationResult calculated) { ElnRecordVos.Recalculated result = new ElnRecordVos.Recalculated(); result.setRecordId(record.getId()); result.setRevisionId(revision.getId()); result.setOperationCode(operationCode); result.setRecordVersion(recordVersion); List statuses = new java.util.ArrayList<>(); for (ElnRecordFieldValueEntity value : calculated.getFieldValues()) { statuses.add(value.getJisuanZhuangtai()); } for (ElnRecordRepeatValueEntity value : calculated.getRepeatValues()) { statuses.add(value.getJisuanZhuangtai()); } for (String status : statuses) { if ("SUCCESS".equals(status)) { result.setSuccessCount(result.getSuccessCount() + 1); } else if ("MISSING_INPUT".equals(status)) { result.setMissingInputCount(result.getMissingInputCount() + 1); } else { result.setErrorCount(result.getErrorCount() + 1); } } return result; } private static String required(String value, String message) { if (!StringUtils.hasText(value)) throw new DataException(message); return value.trim(); } private static UserContext currentUser() { UserInfo user = UserProvider.getUser(); if (user == null || !StringUtils.hasText(user.getUserId())) { throw new DataException("当前登录用户无效"); } TenantVO routed = TenantDataSourceUtil.getTenantInfo(); String tenantId = routed != null && StringUtils.hasText(routed.getEnCode()) ? routed.getEnCode().trim() : user.getTenantId(); return new UserContext(StringUtils.hasText(tenantId) ? tenantId.trim() : "0", user.getUserId().trim()); } @FunctionalInterface interface CurrentUserProvider { UserContext get(); } static final class UserContext { private final String tenantId; private final String userId; UserContext(String tenantId, String userId) { this.tenantId = tenantId; this.userId = userId; } } }