package jnpf.dmsService;
|
|
import jnpf.dmsEntity.permission.entity.DmsFilePermissionEntity;
|
import jnpf.dmsEntity.permission.entity.DmsPermissionUsageEntity;
|
import jnpf.dmsEntity.permission.enums.DmsPrincipalType;
|
import jnpf.dmsEntity.permission.enums.DmsResourceType;
|
import jnpf.dmsEntity.permission.model.DmsResourcePath;
|
import jnpf.dmsEntity.permission.vo.DmsCursorPageVO;
|
import jnpf.dmsEntity.permission.vo.DmsPermissionGrantVO;
|
import jnpf.dmsEntity.permission.vo.DmsPrincipalOptionVO;
|
import jnpf.dmsEntity.permission.vo.DmsPermissionUsageVO;
|
import jnpf.dmsMapper.DmsFilePermissionMapper;
|
import jnpf.dmsMapper.DmsPermissionGroupMapper;
|
import jnpf.dmsMapper.DmsPermissionUsageMapper;
|
import jnpf.dmsPermission.DmsCurrentIdentity;
|
import jnpf.dmsPermission.DmsCursorCodec;
|
import jnpf.dmsPermission.DmsPermissionError;
|
import jnpf.dmsPermission.DmsPermissionException;
|
import jnpf.dmsPermission.DmsPermissionMatcher;
|
import jnpf.dmsPermission.DmsPermissionProperties;
|
import jnpf.dmsPermission.DmsResourceResolver;
|
import jnpf.dmsPermission.DmsUserDisplayNameResolver;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.util.Date;
|
import java.util.Collections;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.UUID;
|
import java.util.stream.Collectors;
|
|
@Service
|
@RequiredArgsConstructor
|
public class DmsPermissionAdminQueryService {
|
private final DmsCurrentIdentity currentIdentity;
|
private final DmsPermissionProperties properties;
|
private final DmsCursorCodec cursorCodec;
|
private final DmsFilePermissionMapper permissionMapper;
|
private final DmsPermissionGroupMapper permissionGroupMapper;
|
private final DmsPermissionUsageMapper usageMapper;
|
private final DmsPermissionGrantService grantService;
|
private final DmsResourceResolver resourceResolver;
|
private final DmsPermissionMatcher matcher;
|
private final DmsUserDisplayNameResolver userDisplayNameResolver;
|
|
@Transactional(readOnly = true)
|
public DmsCursorPageVO<DmsPermissionGrantVO> grants(UUID resourceId, String principalType,
|
String principalId, String cursor, Integer pageSize) {
|
currentIdentity.requirePermissionAdministrator();
|
String normalizedPrincipalType = normalizePrincipalType(principalType);
|
int limit = pageSize(pageSize);
|
DmsCursorCodec.Cursor decoded = cursorCodec.decode(cursor);
|
List<DmsFilePermissionEntity> rows = permissionMapper.selectPage(currentIdentity.tenantId(), resourceId,
|
normalizedPrincipalType, trimToNull(principalId), decoded.getTime(), decoded.getId(), limit + 1);
|
return page(rows, limit);
|
}
|
|
@Transactional(readOnly = true)
|
public DmsPermissionGrantVO grant(UUID id) {
|
currentIdentity.requirePermissionAdministrator();
|
DmsFilePermissionEntity value = permissionMapper.selectByTenantAndId(currentIdentity.tenantId(), id);
|
if (value == null) {
|
throw DmsPermissionException.of(DmsPermissionError.RESOURCE_NOT_FOUND, "权限不存在: " + id);
|
}
|
DmsPermissionGrantVO result = enriched(value, false);
|
enrichUserNames(Collections.singletonList(result));
|
return result;
|
}
|
|
@Transactional(readOnly = true)
|
public List<DmsPermissionGrantVO> resourceGrants(DmsResourceType resourceType, UUID resourceId) {
|
currentIdentity.requirePermissionAdministrator();
|
String tenantId = currentIdentity.tenantId();
|
DmsResourcePath path = resourceResolver.resolve(tenantId, resourceType, resourceId);
|
List<UUID> candidateIds = resourceResolver.candidateResourceIds(path);
|
List<DmsPermissionGrantVO> result = permissionMapper.selectForResourceIds(tenantId, candidateIds, 1000).stream()
|
.filter(permission -> matcher.coversResource(permission, path))
|
.map(permission -> enriched(permission, !permission.getResourceId().equals(resourceId)))
|
.collect(Collectors.toList());
|
enrichUserNames(result);
|
return result;
|
}
|
|
@Transactional(readOnly = true)
|
public List<DmsPermissionUsageVO> usages(UUID permissionId, Integer pageSize) {
|
currentIdentity.requirePermissionAdministrator();
|
String tenantId = currentIdentity.tenantId();
|
if (permissionMapper.selectByTenantAndId(tenantId, permissionId) == null) {
|
throw DmsPermissionException.of(DmsPermissionError.RESOURCE_NOT_FOUND,
|
"权限不存在: " + permissionId);
|
}
|
return usageMapper.selectByPermission(tenantId, permissionId, pageSize(pageSize)).stream()
|
.map(this::usageVO).collect(Collectors.toList());
|
}
|
|
@Transactional(readOnly = true)
|
public List<DmsPrincipalOptionVO> permissionGroups(String keyword, Integer pageSize) {
|
currentIdentity.requirePermissionAdministrator();
|
return permissionGroupMapper.selectEnabled(trimToNull(keyword), pageSize(pageSize)).stream()
|
.map(group -> new DmsPrincipalOptionVO(group.getId(), group.getFullName()))
|
.collect(Collectors.toList());
|
}
|
|
private DmsCursorPageVO<DmsPermissionGrantVO> page(List<DmsFilePermissionEntity> rows, int limit) {
|
DmsCursorPageVO<DmsPermissionGrantVO> result = new DmsCursorPageVO<>();
|
boolean hasMore = rows.size() > limit;
|
if (hasMore) {
|
rows = rows.subList(0, limit);
|
}
|
List<DmsPermissionGrantVO> values = rows.stream().map(row -> enriched(row, false))
|
.collect(Collectors.toList());
|
enrichUserNames(values);
|
result.setList(values);
|
result.setHasMore(hasMore);
|
if (hasMore && !rows.isEmpty()) {
|
DmsFilePermissionEntity last = rows.get(rows.size() - 1);
|
result.setNextCursor(cursorCodec.encode(last.getCreatedAt(), last.getId()));
|
}
|
return result;
|
}
|
|
private void enrichUserNames(List<DmsPermissionGrantVO> values) {
|
Map<String, String> names = userDisplayNameResolver.resolve(values.stream()
|
.filter(value -> DmsPrincipalType.USER.name().equals(value.getPrincipalType()))
|
.map(DmsPermissionGrantVO::getPrincipalId)
|
.collect(Collectors.toList()));
|
values.stream()
|
.filter(value -> DmsPrincipalType.USER.name().equals(value.getPrincipalType()))
|
.forEach(value -> value.setPrincipalName(names.get(value.getPrincipalId())));
|
}
|
|
private DmsPermissionGrantVO enriched(DmsFilePermissionEntity value, boolean inherited) {
|
DmsPermissionGrantVO result = grantService.toVO(value);
|
result.setInherited(inherited);
|
result.setStatus(status(value));
|
if (value.getMaxUseCount() != null) {
|
result.setRemainingUseCount(value.getMaxUseCount() - value.getUsedCount());
|
}
|
return result;
|
}
|
|
private String status(DmsFilePermissionEntity value) {
|
Date now = new Date();
|
if (value.getRevokedAt() != null) {
|
return "REVOKED";
|
}
|
if (value.getValidFrom().after(now)) {
|
return "PENDING";
|
}
|
if (value.getValidTo() != null && !now.before(value.getValidTo())) {
|
return "EXPIRED";
|
}
|
if (value.getMaxUseCount() != null && value.getUsedCount() >= value.getMaxUseCount()) {
|
return "EXHAUSTED";
|
}
|
return "ACTIVE";
|
}
|
|
private DmsPermissionUsageVO usageVO(DmsPermissionUsageEntity value) {
|
DmsPermissionUsageVO result = new DmsPermissionUsageVO();
|
result.setId(value.getId());
|
result.setPermissionId(value.getPermissionId());
|
result.setUserId(value.getUserId());
|
result.setActionCode(value.getActionCode());
|
result.setActualResourceType(value.getActualResourceType());
|
result.setActualResourceId(value.getActualResourceId());
|
result.setOperationId(value.getOperationId());
|
result.setSessionExpiresAt(value.getSessionExpiresAt());
|
result.setUsedAt(value.getUsedAt());
|
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 normalizePrincipalType(String value) {
|
if (value == null || value.trim().isEmpty()) {
|
return null;
|
}
|
try {
|
return DmsPrincipalType.valueOf(value.trim().toUpperCase()).name();
|
} catch (IllegalArgumentException ex) {
|
throw DmsPermissionException.of(DmsPermissionError.INVALID_REQUEST, "principalType 无效");
|
}
|
}
|
|
private String trimToNull(String value) {
|
return value == null || value.trim().isEmpty() ? null : value.trim();
|
}
|
}
|