package jnpf.dmsService; import jnpf.base.UserInfo; import jnpf.dmsEntity.permission.model.DmsSubjectSet; import jnpf.dmsEntity.permission.vo.DmsCursorPageVO; import jnpf.dmsEntity.permission.vo.DmsFileCenterItemVO; import jnpf.dmsMapper.DmsFileCenterMapper; import jnpf.dmsPermission.DmsCurrentIdentity; import jnpf.dmsPermission.DmsFileCenterCursorCodec; import jnpf.dmsPermission.DmsFileCenterQuery; import jnpf.dmsPermission.DmsPermissionError; import jnpf.dmsPermission.DmsPermissionException; import jnpf.dmsPermission.DmsPermissionGroupMembershipResolver; import jnpf.dmsPermission.DmsPermissionProperties; import jnpf.dmsPermission.DmsSubjectResolver; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.function.Function; @Service @RequiredArgsConstructor public class DmsFileCenterService { private static final String FOLDER_CURSOR = "FOLDER"; private static final String CURRENT_CURSOR = "CURRENT_FOLDER"; private static final String HISTORICAL_CURSOR = "HISTORICAL_FOLDER"; private static final String DOCUMENT_VERSIONS_CURSOR = "DOCUMENT_VERSIONS"; private static final String SHARED_CURRENT_CURSOR = "SHARED_CURRENT"; private static final String SHARED_HISTORICAL_CURSOR = "SHARED_HISTORICAL"; private final DmsCurrentIdentity currentIdentity; private final DmsSubjectResolver subjectResolver; private final DmsPermissionGroupMembershipResolver groupMembershipResolver; private final DmsPermissionProperties properties; private final DmsFileCenterCursorCodec cursorCodec; private final DmsFileCenterMapper mapper; @Transactional(readOnly = true) public DmsCursorPageVO rootFolders(String cursor, Integer pageSize) { return folders(null, cursor, pageSize); } @Transactional(readOnly = true) public DmsCursorPageVO childFolders(UUID parentId, String cursor, Integer pageSize) { return folders(parentId, cursor, pageSize); } @Transactional(readOnly = true) public DmsCursorPageVO currentDocuments(UUID folderId, String cursor, Integer pageSize, String keyword) { DmsFileCenterQuery query = query(); query.setFolderId(folderId); query.setKeyword(normalizeKeyword(keyword)); int limit = pageSize(pageSize); String fingerprint = cursorCodec.fingerprint(query.getKeyword()); applyCursor(query, cursorCodec.decode(cursor, CURRENT_CURSOR, query.getTenantId(), query.getUserId(), folderId, fingerprint)); query.setLimit(limit + 1); return page(mapper.selectCurrentDocuments(query), limit, item -> cursorCodec.encode(CURRENT_CURSOR, query.getTenantId(), query.getUserId(), folderId, fingerprint, cursor(item))); } @Transactional(readOnly = true) public DmsCursorPageVO historicalVersions(UUID folderId, String cursor, Integer pageSize, String keyword) { DmsFileCenterQuery query = query(); query.setFolderId(folderId); query.setKeyword(normalizeKeyword(keyword)); int limit = pageSize(pageSize); String fingerprint = cursorCodec.fingerprint(query.getKeyword()); applyCursor(query, cursorCodec.decode(cursor, HISTORICAL_CURSOR, query.getTenantId(), query.getUserId(), folderId, fingerprint)); query.setLimit(limit + 1); return page(mapper.selectHistoricalVersions(query), limit, item -> cursorCodec.encode(HISTORICAL_CURSOR, query.getTenantId(), query.getUserId(), folderId, fingerprint, cursor(item))); } @Transactional(readOnly = true) public DmsCursorPageVO documentVersions(UUID documentId, String cursor, Integer pageSize) { DmsFileCenterQuery query = query(); query.setDocumentId(documentId); int limit = pageSize(pageSize); String fingerprint = cursorCodec.fingerprint(null); applyCursor(query, cursorCodec.decode(cursor, DOCUMENT_VERSIONS_CURSOR, query.getTenantId(), query.getUserId(), documentId, fingerprint)); query.setLimit(limit + 1); return page(mapper.selectDocumentVersions(query), limit, item -> cursorCodec.encode(DOCUMENT_VERSIONS_CURSOR, query.getTenantId(), query.getUserId(), documentId, fingerprint, cursor(item))); } @Transactional(readOnly = true) public DmsCursorPageVO sharedDocuments(String center, String cursor, Integer pageSize) { boolean historical = normalizeCenter(center); String cursorKind = historical ? SHARED_HISTORICAL_CURSOR : SHARED_CURRENT_CURSOR; DmsFileCenterQuery query = query(); int limit = pageSize(pageSize); String fingerprint = cursorCodec.fingerprint(null); applyCursor(query, cursorCodec.decode(cursor, cursorKind, query.getTenantId(), query.getUserId(), null, fingerprint)); query.setLimit(limit + 1); List rows = historical ? mapper.selectSharedHistoricalVersions(query) : mapper.selectSharedDocuments(query); return page(rows, limit, item -> cursorCodec.encode(cursorKind, query.getTenantId(), query.getUserId(), null, fingerprint, cursor(item))); } private DmsCursorPageVO folders(UUID parentId, String cursor, Integer pageSize) { DmsFileCenterQuery query = query(); query.setParentId(parentId); int limit = pageSize(pageSize); String fingerprint = cursorCodec.fingerprint(null); applyCursor(query, cursorCodec.decode(cursor, FOLDER_CURSOR, query.getTenantId(), query.getUserId(), parentId, fingerprint)); query.setLimit(limit + 1); return page(mapper.selectFolders(query), limit, item -> cursorCodec.encode(FOLDER_CURSOR, query.getTenantId(), query.getUserId(), parentId, fingerprint, cursor(item))); } private DmsFileCenterQuery query() { UserInfo user = currentIdentity.requireUser(); DmsFileCenterQuery query = new DmsFileCenterQuery(); query.setTenantId(currentIdentity.tenantId(user)); query.setUserId(user.getUserId().trim()); query.setAdministrator(currentIdentity.isPermissionAdministrator()); if (query.isAdministrator()) { return query; } DmsSubjectSet subjects = subjectResolver.resolve(user); groupMembershipResolver.resolveAll(subjects, properties.getMaxPermissionGroupScan()); query.setDirectOrgIds(new ArrayList<>(subjects.getDirectOrgIds())); query.setAncestorOrgIds(new ArrayList<>(subjects.getAncestorOrgIds())); query.setPositionIds(new ArrayList<>(subjects.getPositionIds())); query.setRoleIds(new ArrayList<>(subjects.getRoleIds())); query.setPermissionGroupIds(new ArrayList<>(subjects.getPermissionGroupIds())); return query; } private void applyCursor(DmsFileCenterQuery query, DmsFileCenterCursorCodec.Cursor cursor) { query.setCursorName(cursor.getName()); query.setCursorDocumentId(cursor.getDocumentId()); query.setCursorVersionNo(cursor.getVersionNo()); query.setCursorId(cursor.getId()); } private DmsFileCenterCursorCodec.Cursor cursor(DmsFileCenterItemVO item) { return new DmsFileCenterCursorCodec.Cursor( item.getName(), item.getDocumentId(), item.getVersionNo(), item.getId()); } private DmsCursorPageVO page( List values, int limit, Function encoder) { List rows = values == null ? Collections.emptyList() : values; boolean hasMore = rows.size() > limit; if (hasMore) { rows = new ArrayList<>(rows.subList(0, limit)); } DmsCursorPageVO result = new DmsCursorPageVO<>(); result.setList(rows); result.setHasMore(hasMore); if (hasMore && !rows.isEmpty()) { result.setNextCursor(encoder.apply(rows.get(rows.size() - 1))); } return result; } private int pageSize(Integer requested) { int value = requested == null ? properties.getDefaultPageSize() : requested; if (value < 1 || value > properties.getMaxPageSize()) { throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST, "pageSize 必须在 1 到 " + properties.getMaxPageSize() + " 之间"); } return value; } private String normalizeKeyword(String value) { if (value == null || value.trim().isEmpty()) { return null; } String normalized = value.trim(); if (normalized.length() > 200) { throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST, "keyword 长度不能超过 200 个字符"); } return normalized; } private boolean normalizeCenter(String value) { if (value == null || value.trim().isEmpty() || "CURRENT".equalsIgnoreCase(value.trim())) { return false; } if ("HISTORICAL".equalsIgnoreCase(value.trim())) { return true; } throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST, "center 必须是 CURRENT 或 HISTORICAL"); } }