package jnpf.dmsService.center; import jnpf.dmsIntegration.audit.DmsBusinessChangeAudit; import static jnpf.dmsIntegration.audit.DmsBusinessChangeAudit.values; import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity; import jnpf.dmsEntity.center.DmsCenterActivation; import jnpf.dmsEntity.permission.entity.DmsDocumentEntity; import jnpf.dmsMapper.*; import jnpf.dmsService.search.DmsSearchIndexJobService; import jnpf.exception.DataException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; /** Atomic center state transitions; no native form, workflow API or business queue dependency. */ @Service @RequiredArgsConstructor public class DmsCenterActivationService { private final DmsBusinessChangeAudit businessAudit; private final DmsDocumentMapper documents; private final DmsFileVersionMapper versions; private final DmsVersionStateMapper states; private final DmsSearchIndexJobService search; @Transactional(rollbackFor = Exception.class) public void activate(String tenantId, String actorId, List targets) { if (tenantId == null || tenantId.isBlank() || actorId == null || actorId.isBlank() || targets == null || targets.isEmpty()) throw new DataException("中心生效请求不完整"); Set seen = new HashSet<>(); for (DmsCenterActivation target : targets) { if (target == null || target.documentId() == null || target.fileVersionId() == null || !Set.of("CREATE", "REVISE", "REUSE", "RETIRE").contains(target.operation()) || !seen.add(target.documentId())) throw new DataException("中心生效目标无效或重复"); } List ordered = targets.stream().sorted(Comparator.comparing(t -> t.documentId().toString())).toList(); Map locked = new HashMap<>(); for (DmsCenterActivation target : ordered) { DmsDocumentEntity document = documents.selectByTenantAndIdForUpdate(tenantId, target.documentId()); if (document == null) throw new DataException("接收文件不存在"); locked.put(target.documentId(), document); } for (DmsCenterActivation target : ordered) { if (!"RETIRE".equals(target.operation())) continue; UUID mainId = locked.get(target.documentId()).getMainDocumentId(); if (mainId == null || !locked.containsKey(mainId) || locked.get(mainId).getMainDocumentId() != null || targets.stream().noneMatch(t -> mainId.equals(t.documentId()) && Set.of("REUSE", "REVISE").contains(t.operation()))) throw new DataException("只能将本次修订主文件目录中的记录转入历史"); } for (DmsCenterActivation target : ordered) { DmsFileVersionEntity current = versions.selectCurrentEffective(tenantId, target.documentId()); if ("RETIRE".equals(target.operation())) { if (current == null || !target.fileVersionId().equals(current.getId()) || !target.fileVersionId().equals(target.baseFileVersionId())) throw new DataException("移除记录已不是原批准的现行版本"); var before = states.selectAuditState(tenantId, target.fileVersionId()); if (states.retireCurrent(tenantId, target.documentId(), target.fileVersionId(), actorId) != 1) throw new DataException("移除记录转入历史失败"); recordState(tenantId, actorId, target.documentId(), target.fileVersionId(), before); continue; } if ("REUSE".equals(target.operation())) { if (current == null || !target.fileVersionId().equals(current.getId())) throw new DataException("沿用版本已不是现行版本"); continue; } DmsFileVersionEntity version = versions.selectByTenantAndId(tenantId, target.fileVersionId()); if (version == null || !target.documentId().equals(version.getDocumentId())) throw new DataException("生效版本不属于目标文档"); if ("REVISE".equals(target.operation())) { if (current == null || !Objects.equals(target.baseFileVersionId(), current.getId())) throw new DataException("修订来源版本已不是现行版本"); var before = states.selectAuditState(tenantId, current.getId()); if (states.supersedeCurrent(tenantId, target.documentId(), current.getId(), version.getVersionLabel(), actorId) != 1) throw new DataException("现行版本切换失败"); recordState(tenantId, actorId, target.documentId(), current.getId(), before); } else if (current != null) throw new DataException("新文件已存在有效版本"); var before = states.selectAuditState(tenantId, target.fileVersionId()); if (states.activateApproved(tenantId, target.fileVersionId(), actorId) != 1) throw new DataException("批准文件尚未到生效日期或已失效"); recordState(tenantId, actorId, target.documentId(), target.fileVersionId(), before); search.enqueueContentUpsert(tenantId, target.documentId(), target.fileVersionId(), target.checksumSha256()); } } private void recordState(String tenantId, String approvalActorId, UUID documentId, UUID versionId, jnpf.dmsIntegration.audit.DmsVersionStateAuditRow before) { var after = states.selectAuditState(tenantId, versionId); if (before == null || after == null) throw new DataException("文件状态审计快照缺失"); businessAudit.record(tenantId, "DMS_VERSION_STATE_CHANGED", "文件版本效力变更", "dms_version_states", versionId.toString(), state(before), state(after), after.getStatusReason(), values("document_id", documentId, "file_version_id", versionId, "approval_actor_id", approvalActorId)); } private Map state(jnpf.dmsIntegration.audit.DmsVersionStateAuditRow value) { return values("lifecycle_status", value.getLifecycleStatus(), "effective_from", value.getEffectiveFrom(), "effective_to", value.getEffectiveTo(), "status_reason", value.getStatusReason()); } }