package jnpf.dmsService; import jnpf.dmsEntity.permission.entity.DmsFilePermissionEntity; import jnpf.dmsEntity.storage.approval.*; import jnpf.dmsIntegration.audit.DmsAuditAdapter; import jnpf.dmsMapper.DmsApprovedReadAccessMapper; import jnpf.dmsPermission.DmsPermissionProperties; 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.*; /** Internal activation participant. No endpoint and no relaxation of the permission administrator API. */ @Service @RequiredArgsConstructor public class DmsApprovedReadAccessService { public static final String SERVICE_ID = "dms-approved-read"; private final DmsApprovedReadAccessMapper mapper; private final DmsAuditAdapter audit; private final DmsPermissionProperties properties; @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class) public void apply(DmsApprovedBatch batch, List items, List currentSource) { if (batch.getId() == null || batch.getTenantId() == null || batch.getActivatedAt() == null) throw new DataException("查阅授权必须在批准文件实际生效事务内执行"); Map current = new HashMap<>(); for (DmsApprovedItem item : currentSource) { if (current.put(item.getSourceKey(), item) != null) throw new DataException("批准文件来源重复"); } Map> scopes = new LinkedHashMap<>(); Set departmentIds = new TreeSet<>(); int count = 0; for (DmsApprovedItem item : items) { DmsApprovedItem source = current.remove(item.getSourceKey()); if (source == null || !Objects.equals(item.getChangeType(), source.getChangeType()) || !Objects.equals(item.getBaseFileVersionId(), source.getBaseFileVersionId())) throw new DataException("查阅授权文件与批准来源不一致"); if ("RETIRE".equals(item.getChangeType())) continue; if (!Objects.equals(DmsApprovedDepartmentScope.normalize(item.getReadAccessScope()), DmsApprovedDepartmentScope.normalize(source.getReadAccessScope()))) throw new DataException("查阅授权范围与批准快照不一致"); if (!Set.of("CREATE", "REVISE", "REUSE").contains(item.getChangeType()) || item.getFileVersionId() == null) throw new DataException("查阅授权文件版本无效"); List scope = DmsApprovedDepartmentScope.parse(item.getReadAccessScope()); count += scope.size(); if (count > properties.getMaxGrantBatchSize()) throw new DataException("批准文件查阅授权超出批量上限"); scopes.put(item, scope); scope.forEach(d -> departmentIds.add(d.id())); } if (!current.isEmpty()) throw new DataException("查阅授权文件集合不完整"); if (!departmentIds.isEmpty()) { Set actual = new HashSet<>(); mapper.departments(batch.getTenantId(), new ArrayList<>(departmentIds)).forEach(d -> actual.add(d.id())); if (!actual.equals(departmentIds)) throw new DataException("查阅授权部门不存在、已停用或不属于当前租户"); } // Center activation has already locked all documents in deterministic order in this same transaction. for (var entry : scopes.entrySet()) { DmsApprovedItem item = entry.getKey(); // fileVersionId is the target activated by this review; never use baseFileVersionId for permissions. String sourceId = "approved-read:" + batch.getId() + ":" + item.getFileVersionId(); List old = mapper.owned(batch.getTenantId(), item.getFileVersionId()); if (old.stream().anyMatch(p -> sourceId.equals(p.getSourceId()))) throw new DataException("批准查阅授权已存在,禁止重复激活或复活已撤销权限"); List revoked = new ArrayList<>(); for (DmsFilePermissionEntity p : old) { if (p.getRevokedAt() != null) continue; if (!"REUSE".equals(item.getChangeType())) throw new DataException("新版本已有其他批准查阅授权"); if (mapper.revoke(batch.getTenantId(), p.getId()) != 1) throw new DataException("替换批准查阅授权失败"); revoked.add(p); } List created = new ArrayList<>(); for (var department : entry.getValue()) { DmsFilePermissionEntity p = new DmsFilePermissionEntity(); p.setTenantId(batch.getTenantId()); p.setResourceType("FILE_VERSION"); p.setResourceId(item.getFileVersionId()); p.setResourceScope("SELF"); p.setPrincipalType("ORG"); p.setPrincipalId(department.id()); p.setIncludeChildOrgs("DESCENDANTS".equals(department.scope())); p.setApprovedOrgScope(department.scope()); p.setActionCode("READ"); p.setSourceType("WORKFLOW"); p.setSourceId(sourceId); p.setValidFrom(batch.getActivatedAt()); p.setGrantReason("文件审核正式生效:" + batch.getAuditId() + ";部门范围:" + department.scope()); p.setGrantedByType("SERVICE"); p.setGrantedById(SERVICE_ID); created.add(mapper.insert(p)); } if (!revoked.isEmpty() || !created.isEmpty()) audit.permissionChange("PERMISSION_GRANT", "更新文件审核查阅授权", old, mapper.owned(batch.getTenantId(), item.getFileVersionId()), Collections.emptyList()); } } }