package jnpf.dmsService;
|
|
import jnpf.base.UserInfo;
|
import jnpf.dmsEntity.permission.form.UserFolderPermissionTreeForm;
|
import jnpf.dmsEntity.permission.model.DmsSubjectSet;
|
import jnpf.dmsEntity.permission.model.DmsUserFolderPermissionTreeRow;
|
import jnpf.dmsEntity.permission.vo.DmsCursorPageVO;
|
import jnpf.dmsEntity.permission.vo.DmsUserFolderPermissionTreeNodeVO;
|
import jnpf.dmsEntity.permission.vo.DmsUserFolderPermissionVO;
|
import jnpf.dmsMapper.DmsUserFolderPermissionTreeMapper;
|
import jnpf.dmsPermission.DmsCurrentIdentity;
|
import jnpf.dmsPermission.DmsFileCenterCursorCodec;
|
import jnpf.dmsPermission.DmsPermissionError;
|
import jnpf.dmsPermission.DmsPermissionException;
|
import jnpf.dmsPermission.DmsPermissionGroupMembershipResolver;
|
import jnpf.dmsPermission.DmsPermissionProperties;
|
import jnpf.dmsPermission.DmsUserFolderPermissionPrincipal;
|
import jnpf.dmsPermission.DmsUserFolderPermissionTreeQuery;
|
import jnpf.dmsPermission.DmsUserSubjectBatchResolver;
|
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.LinkedHashMap;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.UUID;
|
|
@Service
|
@RequiredArgsConstructor
|
public class DmsUserFolderPermissionTreeService {
|
private static final String CURSOR_KIND = "USER_FOLDER_PERMISSION_TREE";
|
|
private final DmsCurrentIdentity currentIdentity;
|
private final DmsPermissionProperties properties;
|
private final DmsFileCenterCursorCodec cursorCodec;
|
private final DmsUserSubjectBatchResolver subjectResolver;
|
private final DmsPermissionGroupMembershipResolver groupMembershipResolver;
|
private final DmsUserFolderPermissionTreeMapper mapper;
|
|
@Transactional(readOnly = true)
|
public DmsCursorPageVO<DmsUserFolderPermissionTreeNodeVO> tree(UserFolderPermissionTreeForm form) {
|
currentIdentity.requirePermissionAdministrator();
|
UserInfo actor = currentIdentity.requireUser();
|
String tenantId = currentIdentity.tenantId(actor);
|
List<String> userIds = normalizeUserIds(form == null ? null : form.getUserIds());
|
int pageSize = pageSize(form == null ? null : form.getPageSize());
|
UUID parentId = form == null ? null : form.getParentId();
|
String fingerprint = cursorCodec.fingerprint(String.join("\n", userIds));
|
DmsFileCenterCursorCodec.Cursor cursor = cursorCodec.decode(
|
form == null ? null : form.getCursor(), CURSOR_KIND, tenantId,
|
actor.getUserId().trim(), parentId, fingerprint);
|
|
List<DmsSubjectSet> subjects = subjectResolver.resolve(tenantId, userIds);
|
groupMembershipResolver.resolveAll(subjects, properties.getMaxPermissionGroupScan());
|
|
DmsUserFolderPermissionTreeQuery query = new DmsUserFolderPermissionTreeQuery();
|
query.setTenantId(tenantId);
|
query.setParentId(parentId);
|
query.setCursorSortCode(cursor.getSortCode());
|
query.setCursorId(cursor.getId());
|
query.setLimit(pageSize + 1);
|
query.setPrincipals(principals(subjects));
|
|
List<DmsUserFolderPermissionTreeRow> rows = mapper.selectTree(query);
|
return page(rows, userIds, pageSize, tenantId, actor.getUserId().trim(), parentId, fingerprint);
|
}
|
|
/** Internal business entry: only the authenticated user's full PRINT folder scope. */
|
@Transactional(readOnly = true)
|
public List<UUID> currentUserPrintFolderIds() {
|
UserInfo actor = currentIdentity.requireUser();
|
String tenantId = currentIdentity.tenantId(actor);
|
String userId = actor.getUserId().trim();
|
List<DmsSubjectSet> subjects = subjectResolver.resolve(tenantId, Collections.singletonList(userId));
|
for (DmsSubjectSet subject : subjects) {
|
if (!userId.equals(subject.getUserId())) {
|
throw invalid("用户授权主体不匹配");
|
}
|
}
|
groupMembershipResolver.resolveAll(subjects, properties.getMaxPermissionGroupScan());
|
DmsUserFolderPermissionTreeQuery query = new DmsUserFolderPermissionTreeQuery();
|
query.setTenantId(tenantId);
|
query.setPrincipals(principals(subjects));
|
List<UUID> ids = new ArrayList<>();
|
for (DmsUserFolderPermissionTreeRow row : mapper.selectPrintFolders(query)) {
|
ids.add(row.getId());
|
}
|
return ids;
|
}
|
|
/** Personal workflow grants are version-scoped and never expanded into folder permissions. */
|
@Transactional(readOnly = true)
|
public List<UUID> currentUserWorkflowPrintVersionIds() {
|
UserInfo actor = currentIdentity.requireUser();
|
List<UUID> ids = new ArrayList<>();
|
for (DmsUserFolderPermissionTreeRow row : mapper.selectWorkflowPrintVersions(
|
currentIdentity.tenantId(actor), actor.getUserId().trim())) {
|
ids.add(row.getId());
|
}
|
return ids;
|
}
|
|
private List<String> normalizeUserIds(List<String> values) {
|
if (values == null || values.isEmpty()) {
|
throw invalid("userIds 不能为空");
|
}
|
LinkedHashSet<String> normalized = new LinkedHashSet<>();
|
for (String value : values) {
|
if (value == null || value.trim().isEmpty()) {
|
throw invalid("userIds 不能包含空值");
|
}
|
normalized.add(value.trim());
|
}
|
if (normalized.size() > properties.getMaxUserFolderTreeUsers()) {
|
throw invalid("userIds 数量不能超过 " + properties.getMaxUserFolderTreeUsers());
|
}
|
return new ArrayList<>(normalized);
|
}
|
|
private int pageSize(Integer requested) {
|
int value = requested == null ? properties.getDefaultPageSize() : requested;
|
if (value < 1 || value > properties.getMaxPageSize()) {
|
throw invalid("pageSize 必须在 1 到 " + properties.getMaxPageSize() + " 之间");
|
}
|
return value;
|
}
|
|
private List<DmsUserFolderPermissionPrincipal> principals(List<DmsSubjectSet> subjects) {
|
LinkedHashMap<String, DmsUserFolderPermissionPrincipal> values = new LinkedHashMap<>();
|
for (DmsSubjectSet subject : subjects) {
|
addPrincipal(values, subject.getUserId(), "USER", subject.getUserId());
|
addPrincipals(values, subject.getUserId(), "ORG_DIRECT", subject.getDirectOrgIds());
|
addPrincipals(values, subject.getUserId(), "ORG_ANCESTOR", subject.getAncestorOrgIds());
|
addPrincipals(values, subject.getUserId(), "POSITION", subject.getPositionIds());
|
addPrincipals(values, subject.getUserId(), "ROLE", subject.getRoleIds());
|
addPrincipals(values, subject.getUserId(), "GROUP", subject.getPermissionGroupIds());
|
}
|
return new ArrayList<>(values.values());
|
}
|
|
private void addPrincipals(Map<String, DmsUserFolderPermissionPrincipal> values,
|
String userId, String kind, Set<String> ids) {
|
if (ids == null) {
|
return;
|
}
|
for (String id : ids) {
|
addPrincipal(values, userId, kind, id);
|
}
|
}
|
|
private void addPrincipal(Map<String, DmsUserFolderPermissionPrincipal> values,
|
String userId, String kind, String id) {
|
if (id == null || id.trim().isEmpty()) {
|
return;
|
}
|
String normalizedId = id.trim();
|
String key = userId + "\n" + kind + "\n" + normalizedId;
|
values.computeIfAbsent(key,
|
ignored -> new DmsUserFolderPermissionPrincipal(userId, kind, normalizedId));
|
}
|
|
private DmsCursorPageVO<DmsUserFolderPermissionTreeNodeVO> page(
|
List<DmsUserFolderPermissionTreeRow> sourceRows, List<String> userIds, int pageSize,
|
String tenantId, String actorId, UUID parentId, String fingerprint) {
|
List<DmsUserFolderPermissionTreeRow> rows = sourceRows == null
|
? Collections.<DmsUserFolderPermissionTreeRow>emptyList() : sourceRows;
|
LinkedHashMap<UUID, DmsUserFolderPermissionTreeNodeVO> nodes = new LinkedHashMap<>();
|
Map<UUID, Map<String, Set<String>>> actionsByFolder = new LinkedHashMap<>();
|
for (DmsUserFolderPermissionTreeRow row : rows) {
|
DmsUserFolderPermissionTreeNodeVO node = nodes.computeIfAbsent(row.getId(), ignored -> node(row));
|
if (row.getUserId() != null && row.getActionCode() != null) {
|
actionsByFolder.computeIfAbsent(node.getId(), ignored -> new LinkedHashMap<>())
|
.computeIfAbsent(row.getUserId(), ignored -> new LinkedHashSet<>())
|
.add(row.getActionCode());
|
}
|
}
|
|
boolean hasMore = nodes.size() > pageSize;
|
List<DmsUserFolderPermissionTreeNodeVO> list = new ArrayList<>(nodes.values());
|
if (hasMore) {
|
list = new ArrayList<>(list.subList(0, pageSize));
|
}
|
for (DmsUserFolderPermissionTreeNodeVO node : list) {
|
Map<String, Set<String>> byUser = actionsByFolder.getOrDefault(
|
node.getId(), Collections.<String, Set<String>>emptyMap());
|
for (String userId : userIds) {
|
Set<String> actions = byUser.get(userId);
|
if (actions == null || actions.isEmpty()) {
|
continue;
|
}
|
DmsUserFolderPermissionVO permission = new DmsUserFolderPermissionVO();
|
permission.setUserId(userId);
|
List<String> ordered = new ArrayList<>();
|
if (actions.contains("READ")) {
|
ordered.add("READ");
|
}
|
if (actions.contains("PRINT")) {
|
ordered.add("PRINT");
|
}
|
permission.setActions(ordered);
|
node.getUserPermissions().add(permission);
|
}
|
}
|
|
DmsCursorPageVO<DmsUserFolderPermissionTreeNodeVO> result = new DmsCursorPageVO<>();
|
result.setList(list);
|
result.setHasMore(hasMore);
|
if (hasMore && !list.isEmpty()) {
|
DmsUserFolderPermissionTreeNodeVO last = list.get(list.size() - 1);
|
result.setNextCursor(cursorCodec.encode(CURSOR_KIND, tenantId, actorId, parentId, fingerprint,
|
new DmsFileCenterCursorCodec.Cursor(
|
last.getName(), null, null, last.getSortCode(), last.getId())));
|
}
|
return result;
|
}
|
|
private DmsUserFolderPermissionTreeNodeVO node(DmsUserFolderPermissionTreeRow row) {
|
DmsUserFolderPermissionTreeNodeVO node = new DmsUserFolderPermissionTreeNodeVO();
|
node.setId(row.getId());
|
node.setParentId(row.getParentId());
|
node.setName(row.getName());
|
node.setSortCode(row.getSortCode());
|
node.setHasChildren(row.isHasChildren());
|
return node;
|
}
|
|
private DmsPermissionException invalid(String message) {
|
return DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST, message);
|
}
|
}
|