package jnpf.dmsService; import jnpf.audit.sdk.annotation.AuditLog; import jnpf.base.ActionResult; import jnpf.dmsEntity.center.*; import jnpf.dmsEntity.storage.approval.DmsDisableRequest; import jnpf.dmsEntity.storage.expiry.DmsFileExpiryResource; import jnpf.dmsMapper.*; import jnpf.dmsService.center.DmsCenterDisableService; import jnpf.exception.DataException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.*; /** One item, one transaction. No workflow/storage/search RPC while holding locks. */ @Service @RequiredArgsConstructor public class DmsApprovedRevocationExecutor { private final DmsRevocationMapper mapper; private final DmsCenterDisableService center; private final DmsFileExpiryProjectionMapper resources; private final DmsFileExpiryMapper cleanup; private final DmsDisableApplicationMapper occupancies; private final DmsBusinessLifecycleGuard businessOccupancy; @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) @AuditLog(action = "DMS_APPLY_APPROVED_REVOCATION", bizCodeExpr = "#command.requestId") public ActionResult execute(DmsCenterDisableCommand command) { if (command == null || command.getTenantId() == null || command.getRequestId() == null || command.getMainDocumentId() == null || command.getExpectedMainVersionId() == null || command.getActorId() == null || command.getEffectiveAt() == null || command.getLeaseToken() == null || !"yichexiao".equals(command.getReason())) throw new DataException("撤销执行参数无效或操作类型不受支持"); DmsDisableRequest row = mapper.lockRequest(command); if (row == null || !"yichexiao".equals(row.getOperation()) || !Objects.equals(row.getDocumentId(), command.getMainDocumentId()) || !Objects.equals(row.getFileVersionId(), command.getExpectedMainVersionId()) || !Objects.equals(row.getActorId(), command.getActorId()) || row.getScheduledAt() == null || row.getScheduledAt().getTime() != command.getEffectiveAt().getTime()) throw new DataException("撤销请求与已批准登记快照不一致"); if ("APPLIED".equals(row.getStatus())) return ActionResult.success("文件撤销已完成"); if (!"WAITING".equals(row.getStatus())) throw new DataException("遗留中心执行状态需核对后补偿,不能直接认定撤销成功"); if (!mapper.validLease(row, command.getLeaseToken()) || !mapper.approvedSource(row)) throw new DataException("撤销任务租约、执行日期、审批来源或占用已变化"); var businessReceipts=businessOccupancy.requireExecution(row.getTenantId(),row.getTaskId(),row.getApplicationId(),"REVOCATION",List.of(row.getFileId())); var discovered=businessOccupancy.isEnabled() ? mapper.currentTargets(command) : null; if(discovered!=null) businessOccupancy.guardVersions(row.getTenantId(),row.getTaskId(), discovered.stream().map(t->t.getFileVersionId().toString()).toList()); Date at = mapper.executionTime(); if (!center.alreadyIneffective(command, row, at)) { List targets = center.revoke(command, at); if(discovered!=null && !new HashSet<>(discovered.stream().map(DmsInvalidationTarget::getFileVersionId).toList()) .equals(new HashSet<>(targets.stream().map(DmsInvalidationTarget::getFileVersionId).toList()))) throw new DataException("执行目录成员已变化,请重试"); for (DmsInvalidationTarget target : targets) { if (resources.hasPendingProjection(row.getTenantId(), target.getFileVersionId())) throw new DataException("批准版本尚未完成业务回写"); List matches = resources.lockResources(row.getTenantId(), target.getFileVersionId()); if (matches.size() != 1) throw new DataException("撤销版本缺少唯一的当前业务资源映射"); DmsFileExpiryResource resource = matches.get(0); String version = target.getFileVersionId().toString(); if (!version.equals(resource.getCenterVersionId()) || !version.equals(resource.getVersionCenterId()) || !("effective".equals(resource.getBizStatus()) || ("disabled".equals(resource.getBizStatus()) && "yidaoqi".equals(resource.getDisabledSource()) && target.getEffectiveTo() != null && !target.getEffectiveTo().after(at))) || (target.getDocumentId().equals(row.getDocumentId()) && (!row.getFileId().equals(resource.getId()) || !row.getBusinessVersionId().equals(resource.getCurrentVersionId())))) throw new DataException("撤销业务资源版本或状态已变化"); if (mapper.disableResource(row, resource, at) != 1) throw new DataException("撤销业务回写失败"); if (mapper.revokeBusinessVersion(row, resource, at) != 1) throw new DataException("撤销业务版本状态已变化"); Date validityEnd = target.getEffectiveTo() != null && target.getEffectiveTo().before(at) ? target.getEffectiveTo() : at; resources.closeRelations(row.getTenantId(), resource.getId(), validityEnd, row.getActorId()); cleanup.enqueueCleanup(row.getTenantId(), target.getFileVersionId()); } } if (mapper.recordExecution(row, at) != 1) throw new DataException("原生撤销明细回写失败"); if (occupancies.releaseFile(row.getTenantId(), row.getTaskId(), row.getOperation(), row.getFileId()) != 1) throw new DataException("撤销申请占用已变化"); if (mapper.complete(row, at, command.getLeaseToken()) != 1) throw new DataException("撤销执行租约已过期,撤回本次变更后重试"); businessOccupancy.completeExecution(businessReceipts); return ActionResult.success("文件撤销已完成"); } }