You've already forked DataMate
fix(auth): harden confidential knowledge access checks and sensitivity filtering
This commit is contained in:
@@ -83,11 +83,13 @@ public class UserContextFilter implements GlobalFilter, Ordered {
|
|||||||
String userId = String.valueOf(claims.get("userId"));
|
String userId = String.valueOf(claims.get("userId"));
|
||||||
String username = claims.getSubject();
|
String username = claims.getSubject();
|
||||||
List<String> roles = gatewayJwtUtils.getStringListClaim(claims, "roles");
|
List<String> roles = gatewayJwtUtils.getStringListClaim(claims, "roles");
|
||||||
|
List<String> permissions = gatewayJwtUtils.getStringListClaim(claims, "permissions");
|
||||||
|
|
||||||
ServerHttpRequest mutatedRequest = request.mutate()
|
ServerHttpRequest mutatedRequest = request.mutate()
|
||||||
.header("X-User-Id", userId)
|
.header("X-User-Id", userId)
|
||||||
.header("X-User-Name", username)
|
.header("X-User-Name", username)
|
||||||
.header("X-User-Roles", String.join(",", roles))
|
.header("X-User-Roles", String.join(",", roles))
|
||||||
|
.header("X-User-Permissions", String.join(",", permissions))
|
||||||
.build();
|
.build();
|
||||||
return chain.filter(exchange.mutate().request(mutatedRequest).build());
|
return chain.filter(exchange.mutate().request(mutatedRequest).build());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.datamate.datamanagement.application;
|
package com.datamate.datamanagement.application;
|
||||||
|
|
||||||
|
import com.datamate.common.auth.application.ResourceAccessService;
|
||||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||||
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
||||||
|
import com.datamate.common.infrastructure.exception.SystemErrorCode;
|
||||||
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
||||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItemDirectory;
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItemDirectory;
|
||||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||||
@@ -32,17 +34,19 @@ public class KnowledgeDirectoryApplicationService {
|
|||||||
private final KnowledgeItemDirectoryRepository knowledgeItemDirectoryRepository;
|
private final KnowledgeItemDirectoryRepository knowledgeItemDirectoryRepository;
|
||||||
private final KnowledgeItemRepository knowledgeItemRepository;
|
private final KnowledgeItemRepository knowledgeItemRepository;
|
||||||
private final KnowledgeSetRepository knowledgeSetRepository;
|
private final KnowledgeSetRepository knowledgeSetRepository;
|
||||||
|
private final ResourceAccessService resourceAccessService;
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<KnowledgeItemDirectory> getKnowledgeDirectories(String setId, KnowledgeDirectoryQuery query) {
|
public List<KnowledgeItemDirectory> getKnowledgeDirectories(String setId, KnowledgeDirectoryQuery query) {
|
||||||
BusinessAssert.notNull(query, CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.notNull(query, CommonErrorCode.PARAM_ERROR);
|
||||||
|
requireAccessibleKnowledgeSet(setId);
|
||||||
query.setSetId(setId);
|
query.setSetId(setId);
|
||||||
return knowledgeItemDirectoryRepository.findByCriteria(query);
|
return knowledgeItemDirectoryRepository.findByCriteria(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KnowledgeItemDirectory createKnowledgeDirectory(String setId, CreateKnowledgeDirectoryRequest request) {
|
public KnowledgeItemDirectory createKnowledgeDirectory(String setId, CreateKnowledgeDirectoryRequest request) {
|
||||||
BusinessAssert.notNull(request, CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.notNull(request, CommonErrorCode.PARAM_ERROR);
|
||||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||||
|
|
||||||
@@ -71,7 +75,7 @@ public class KnowledgeDirectoryApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteKnowledgeDirectory(String setId, String relativePath) {
|
public void deleteKnowledgeDirectory(String setId, String relativePath) {
|
||||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||||
|
|
||||||
@@ -88,6 +92,15 @@ public class KnowledgeDirectoryApplicationService {
|
|||||||
return knowledgeSet;
|
return knowledgeSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private KnowledgeSet requireAccessibleKnowledgeSet(String setId) {
|
||||||
|
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||||
|
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(knowledgeSet.getSensitivity())) {
|
||||||
|
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||||
|
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||||
|
}
|
||||||
|
return knowledgeSet;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isReadOnlyStatus(KnowledgeStatusType status) {
|
private boolean isReadOnlyStatus(KnowledgeStatusType status) {
|
||||||
return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
|
return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.datamate.datamanagement.application;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.datamate.common.auth.application.ResourceAccessService;
|
||||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||||
import com.datamate.common.infrastructure.exception.BusinessException;
|
import com.datamate.common.infrastructure.exception.BusinessException;
|
||||||
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
||||||
@@ -12,11 +13,11 @@ import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
|
|||||||
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
||||||
import com.datamate.datamanagement.domain.model.dataset.Dataset;
|
import com.datamate.datamanagement.domain.model.dataset.Dataset;
|
||||||
import com.datamate.datamanagement.domain.model.dataset.DatasetFile;
|
import com.datamate.datamanagement.domain.model.dataset.DatasetFile;
|
||||||
|
import com.datamate.datamanagement.domain.model.dataset.Tag;
|
||||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
||||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||||
import com.datamate.datamanagement.infrastructure.config.DataManagementProperties;
|
import com.datamate.datamanagement.infrastructure.config.DataManagementProperties;
|
||||||
import com.datamate.datamanagement.infrastructure.exception.DataManagementErrorCode;
|
import com.datamate.datamanagement.infrastructure.exception.DataManagementErrorCode;
|
||||||
import com.datamate.datamanagement.infrastructure.persistence.mapper.TagMapper;
|
|
||||||
import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetFileRepository;
|
import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetFileRepository;
|
||||||
import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetRepository;
|
import com.datamate.datamanagement.infrastructure.persistence.repository.DatasetRepository;
|
||||||
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
|
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
|
||||||
@@ -30,6 +31,7 @@ import com.datamate.datamanagement.interfaces.dto.KnowledgeItemResponse;
|
|||||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchQuery;
|
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchQuery;
|
||||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse;
|
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse;
|
||||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeManagementStatisticsResponse;
|
import com.datamate.datamanagement.interfaces.dto.KnowledgeManagementStatisticsResponse;
|
||||||
|
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
|
||||||
import com.datamate.datamanagement.interfaces.dto.ReplaceKnowledgeItemFileRequest;
|
import com.datamate.datamanagement.interfaces.dto.ReplaceKnowledgeItemFileRequest;
|
||||||
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeItemRequest;
|
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeItemRequest;
|
||||||
import com.datamate.datamanagement.interfaces.dto.UploadKnowledgeItemsRequest;
|
import com.datamate.datamanagement.interfaces.dto.UploadKnowledgeItemsRequest;
|
||||||
@@ -56,12 +58,15 @@ import java.nio.file.Paths;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识条目应用服务
|
* 知识条目应用服务
|
||||||
@@ -88,11 +93,11 @@ public class KnowledgeItemApplicationService {
|
|||||||
private final DatasetRepository datasetRepository;
|
private final DatasetRepository datasetRepository;
|
||||||
private final DatasetFileRepository datasetFileRepository;
|
private final DatasetFileRepository datasetFileRepository;
|
||||||
private final DataManagementProperties dataManagementProperties;
|
private final DataManagementProperties dataManagementProperties;
|
||||||
private final TagMapper tagMapper;
|
|
||||||
private final KnowledgeItemPreviewService knowledgeItemPreviewService;
|
private final KnowledgeItemPreviewService knowledgeItemPreviewService;
|
||||||
|
private final ResourceAccessService resourceAccessService;
|
||||||
|
|
||||||
public KnowledgeItem createKnowledgeItem(String setId, CreateKnowledgeItemRequest request) {
|
public KnowledgeItem createKnowledgeItem(String setId, CreateKnowledgeItemRequest request) {
|
||||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||||
|
|
||||||
@@ -112,7 +117,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<KnowledgeItem> uploadKnowledgeItems(String setId, UploadKnowledgeItemsRequest request) {
|
public List<KnowledgeItem> uploadKnowledgeItems(String setId, UploadKnowledgeItemsRequest request) {
|
||||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||||
|
|
||||||
@@ -176,7 +181,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public KnowledgeItem updateKnowledgeItem(String setId, String itemId, UpdateKnowledgeItemRequest request) {
|
public KnowledgeItem updateKnowledgeItem(String setId, String itemId, UpdateKnowledgeItemRequest request) {
|
||||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||||
@@ -199,6 +204,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteKnowledgeItem(String setId, String itemId) {
|
public void deleteKnowledgeItem(String setId, String itemId) {
|
||||||
|
requireAccessibleKnowledgeSet(setId);
|
||||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||||
@@ -209,6 +215,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteKnowledgeItems(String setId, DeleteKnowledgeItemsRequest request) {
|
public void deleteKnowledgeItems(String setId, DeleteKnowledgeItemsRequest request) {
|
||||||
|
requireAccessibleKnowledgeSet(setId);
|
||||||
BusinessAssert.notNull(request, CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.notNull(request, CommonErrorCode.PARAM_ERROR);
|
||||||
List<String> ids = request.getIds();
|
List<String> ids = request.getIds();
|
||||||
BusinessAssert.isTrue(CollectionUtils.isNotEmpty(ids), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(CollectionUtils.isNotEmpty(ids), CommonErrorCode.PARAM_ERROR);
|
||||||
@@ -231,6 +238,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public KnowledgeItem getKnowledgeItem(String setId, String itemId) {
|
public KnowledgeItem getKnowledgeItem(String setId, String itemId) {
|
||||||
|
requireAccessibleKnowledgeSet(setId);
|
||||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||||
@@ -239,6 +247,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public PagedResponse<KnowledgeItemResponse> getKnowledgeItems(String setId, KnowledgeItemPagingQuery query) {
|
public PagedResponse<KnowledgeItemResponse> getKnowledgeItems(String setId, KnowledgeItemPagingQuery query) {
|
||||||
|
requireAccessibleKnowledgeSet(setId);
|
||||||
query.setSetId(setId);
|
query.setSetId(setId);
|
||||||
IPage<KnowledgeItem> page = new Page<>(query.getPage(), query.getSize());
|
IPage<KnowledgeItem> page = new Page<>(query.getPage(), query.getSize());
|
||||||
page = knowledgeItemRepository.findByCriteria(page, query);
|
page = knowledgeItemRepository.findByCriteria(page, query);
|
||||||
@@ -248,19 +257,52 @@ public class KnowledgeItemApplicationService {
|
|||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public KnowledgeManagementStatisticsResponse getKnowledgeManagementStatistics() {
|
public KnowledgeManagementStatisticsResponse getKnowledgeManagementStatistics() {
|
||||||
|
boolean excludeConfidential = !resourceAccessService.canViewConfidential();
|
||||||
|
String ownerFilterUserId = resourceAccessService.resolveOwnerFilterUserId();
|
||||||
|
KnowledgeSetPagingQuery baseQuery = new KnowledgeSetPagingQuery();
|
||||||
KnowledgeManagementStatisticsResponse response = new KnowledgeManagementStatisticsResponse();
|
KnowledgeManagementStatisticsResponse response = new KnowledgeManagementStatisticsResponse();
|
||||||
response.setTotalKnowledgeSets(knowledgeSetRepository.count());
|
|
||||||
|
|
||||||
long totalFiles = knowledgeItemRepository.countBySourceTypes(List.of(
|
long totalSets = knowledgeSetRepository.countByCriteria(baseQuery, ownerFilterUserId, excludeConfidential);
|
||||||
|
response.setTotalKnowledgeSets(totalSets);
|
||||||
|
|
||||||
|
List<String> accessibleSetIds = knowledgeSetRepository.listSetIdsByCriteria(baseQuery, ownerFilterUserId, excludeConfidential);
|
||||||
|
List<KnowledgeSet> accessibleSets = knowledgeSetRepository.listByIds(accessibleSetIds);
|
||||||
|
if (CollectionUtils.isEmpty(accessibleSets)) {
|
||||||
|
response.setTotalFiles(0L);
|
||||||
|
response.setTotalSize(0L);
|
||||||
|
response.setTotalTags(0L);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> normalizedSetIds = accessibleSets.stream()
|
||||||
|
.map(KnowledgeSet::getId)
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.toList();
|
||||||
|
if (CollectionUtils.isEmpty(normalizedSetIds)) {
|
||||||
|
response.setTotalFiles(0L);
|
||||||
|
response.setTotalSize(0L);
|
||||||
|
response.setTotalTags(0L);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
long totalFiles = knowledgeItemRepository.countBySourceTypesAndSetIds(List.of(
|
||||||
KnowledgeSourceType.DATASET_FILE,
|
KnowledgeSourceType.DATASET_FILE,
|
||||||
KnowledgeSourceType.FILE_UPLOAD
|
KnowledgeSourceType.FILE_UPLOAD
|
||||||
));
|
), normalizedSetIds);
|
||||||
response.setTotalFiles(totalFiles);
|
response.setTotalFiles(totalFiles);
|
||||||
|
|
||||||
long datasetFileSize = safeLong(knowledgeItemRepository.sumDatasetFileSize());
|
long datasetFileSize = safeLong(knowledgeItemRepository.sumDatasetFileSizeBySetIds(normalizedSetIds));
|
||||||
long uploadFileSize = calculateUploadFileTotalSize();
|
long uploadFileSize = calculateUploadFileTotalSize(normalizedSetIds);
|
||||||
response.setTotalSize(datasetFileSize + uploadFileSize);
|
response.setTotalSize(datasetFileSize + uploadFileSize);
|
||||||
response.setTotalTags(safeLong(tagMapper.countKnowledgeSetTags()));
|
|
||||||
|
long totalTags = accessibleSets.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.flatMap(set -> CollectionUtils.isEmpty(set.getTags()) ? Collections.<Tag>emptyList().stream() : set.getTags().stream())
|
||||||
|
.map(tag -> StringUtils.trimToNull(tag == null ? null : tag.getName()))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toCollection(HashSet::new))
|
||||||
|
.size();
|
||||||
|
response.setTotalTags(totalTags);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -271,8 +313,9 @@ public class KnowledgeItemApplicationService {
|
|||||||
String keyword = StringUtils.trimToEmpty(query.getKeyword());
|
String keyword = StringUtils.trimToEmpty(query.getKeyword());
|
||||||
BusinessAssert.isTrue(StringUtils.isNotBlank(keyword), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(StringUtils.isNotBlank(keyword), CommonErrorCode.PARAM_ERROR);
|
||||||
|
|
||||||
|
boolean excludeConfidential = !resourceAccessService.canViewConfidential();
|
||||||
IPage<KnowledgeItemSearchResponse> page = new Page<>(query.getPage(), query.getSize());
|
IPage<KnowledgeItemSearchResponse> page = new Page<>(query.getPage(), query.getSize());
|
||||||
IPage<KnowledgeItemSearchResponse> result = knowledgeItemRepository.searchFileItems(page, keyword);
|
IPage<KnowledgeItemSearchResponse> result = knowledgeItemRepository.searchFileItems(page, keyword, excludeConfidential);
|
||||||
List<KnowledgeItemSearchResponse> responses = result.getRecords()
|
List<KnowledgeItemSearchResponse> responses = result.getRecords()
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::normalizeSearchResponse)
|
.map(this::normalizeSearchResponse)
|
||||||
@@ -281,7 +324,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<KnowledgeItem> importKnowledgeItems(String setId, ImportKnowledgeItemsRequest request) {
|
public List<KnowledgeItem> importKnowledgeItems(String setId, ImportKnowledgeItemsRequest request) {
|
||||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||||
Dataset dataset = datasetRepository.getById(request.getDatasetId());
|
Dataset dataset = datasetRepository.getById(request.getDatasetId());
|
||||||
@@ -318,7 +361,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public void exportKnowledgeItems(String setId, HttpServletResponse response) {
|
public void exportKnowledgeItems(String setId, HttpServletResponse response) {
|
||||||
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
||||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||||
List<KnowledgeItem> items = knowledgeItemRepository.findAllBySetId(setId);
|
List<KnowledgeItem> items = knowledgeItemRepository.findAllBySetId(setId);
|
||||||
|
|
||||||
response.setContentType(EXPORT_CONTENT_TYPE);
|
response.setContentType(EXPORT_CONTENT_TYPE);
|
||||||
@@ -347,6 +390,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public void downloadKnowledgeItemFile(String setId, String itemId, HttpServletResponse response) {
|
public void downloadKnowledgeItemFile(String setId, String itemId, HttpServletResponse response) {
|
||||||
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
||||||
|
requireAccessibleKnowledgeSet(setId);
|
||||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||||
@@ -380,6 +424,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public void previewKnowledgeItemFile(String setId, String itemId, HttpServletResponse response) {
|
public void previewKnowledgeItemFile(String setId, String itemId, HttpServletResponse response) {
|
||||||
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
||||||
|
requireAccessibleKnowledgeSet(setId);
|
||||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||||
@@ -441,7 +486,7 @@ public class KnowledgeItemApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public KnowledgeItem replaceKnowledgeItemFile(String setId, String itemId, ReplaceKnowledgeItemFileRequest request) {
|
public KnowledgeItem replaceKnowledgeItemFile(String setId, String itemId, ReplaceKnowledgeItemFileRequest request) {
|
||||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
KnowledgeSet knowledgeSet = requireAccessibleKnowledgeSet(setId);
|
||||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||||
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
|
||||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||||
@@ -655,8 +700,8 @@ public class KnowledgeItemApplicationService {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long calculateUploadFileTotalSize() {
|
private long calculateUploadFileTotalSize(List<String> setIds) {
|
||||||
List<KnowledgeItem> items = knowledgeItemRepository.findFileUploadItems();
|
List<KnowledgeItem> items = knowledgeItemRepository.findFileUploadItemsBySetIds(setIds);
|
||||||
if (CollectionUtils.isEmpty(items)) {
|
if (CollectionUtils.isEmpty(items)) {
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
@@ -846,6 +891,18 @@ public class KnowledgeItemApplicationService {
|
|||||||
return knowledgeSet;
|
return knowledgeSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验当前用户是否可访问指定知识集(含保密权限检查)
|
||||||
|
*/
|
||||||
|
private KnowledgeSet requireAccessibleKnowledgeSet(String setId) {
|
||||||
|
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||||
|
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(knowledgeSet.getSensitivity())) {
|
||||||
|
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||||
|
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||||
|
}
|
||||||
|
return knowledgeSet;
|
||||||
|
}
|
||||||
|
|
||||||
private String buildExportFileName(String setId) {
|
private String buildExportFileName(String setId) {
|
||||||
return EXPORT_FILE_PREFIX + setId + "_" + LocalDateTime.now().format(EXPORT_TIME_FORMATTER) + EXPORT_FILE_SUFFIX;
|
return EXPORT_FILE_PREFIX + setId + "_" + LocalDateTime.now().format(EXPORT_TIME_FORMATTER) + EXPORT_FILE_SUFFIX;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package com.datamate.datamanagement.application;
|
package com.datamate.datamanagement.application;
|
||||||
|
|
||||||
|
import com.datamate.common.auth.application.ResourceAccessService;
|
||||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||||
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
||||||
|
import com.datamate.common.infrastructure.exception.SystemErrorCode;
|
||||||
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
|
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
|
||||||
import com.datamate.datamanagement.common.enums.KnowledgeItemPreviewStatus;
|
import com.datamate.datamanagement.common.enums.KnowledgeItemPreviewStatus;
|
||||||
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
|
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
|
||||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
||||||
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||||
import com.datamate.datamanagement.infrastructure.config.DataManagementProperties;
|
import com.datamate.datamanagement.infrastructure.config.DataManagementProperties;
|
||||||
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
|
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
|
||||||
|
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
|
||||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPreviewStatusResponse;
|
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPreviewStatusResponse;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -38,8 +42,10 @@ public class KnowledgeItemPreviewService {
|
|||||||
private static final DateTimeFormatter PREVIEW_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
private static final DateTimeFormatter PREVIEW_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||||
|
|
||||||
private final KnowledgeItemRepository knowledgeItemRepository;
|
private final KnowledgeItemRepository knowledgeItemRepository;
|
||||||
|
private final KnowledgeSetRepository knowledgeSetRepository;
|
||||||
private final DataManagementProperties dataManagementProperties;
|
private final DataManagementProperties dataManagementProperties;
|
||||||
private final KnowledgeItemPreviewAsyncService knowledgeItemPreviewAsyncService;
|
private final KnowledgeItemPreviewAsyncService knowledgeItemPreviewAsyncService;
|
||||||
|
private final ResourceAccessService resourceAccessService;
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
public KnowledgeItemPreviewStatusResponse getPreviewStatus(String setId, String itemId) {
|
public KnowledgeItemPreviewStatusResponse getPreviewStatus(String setId, String itemId) {
|
||||||
@@ -138,6 +144,14 @@ public class KnowledgeItemPreviewService {
|
|||||||
private KnowledgeItem requireKnowledgeItem(String setId, String itemId) {
|
private KnowledgeItem requireKnowledgeItem(String setId, String itemId) {
|
||||||
BusinessAssert.isTrue(StringUtils.isNotBlank(setId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(StringUtils.isNotBlank(setId), CommonErrorCode.PARAM_ERROR);
|
||||||
BusinessAssert.isTrue(StringUtils.isNotBlank(itemId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(StringUtils.isNotBlank(itemId), CommonErrorCode.PARAM_ERROR);
|
||||||
|
|
||||||
|
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
||||||
|
BusinessAssert.notNull(knowledgeSet, CommonErrorCode.PARAM_ERROR);
|
||||||
|
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(knowledgeSet.getSensitivity())) {
|
||||||
|
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||||
|
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
|
||||||
BusinessAssert.notNull(knowledgeItem, CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.notNull(knowledgeItem, CommonErrorCode.PARAM_ERROR);
|
||||||
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.datamate.common.auth.application.ResourceAccessService;
|
import com.datamate.common.auth.application.ResourceAccessService;
|
||||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||||
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
import com.datamate.common.infrastructure.exception.CommonErrorCode;
|
||||||
|
import com.datamate.common.infrastructure.exception.SystemErrorCode;
|
||||||
import com.datamate.common.interfaces.PagedResponse;
|
import com.datamate.common.interfaces.PagedResponse;
|
||||||
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
|
||||||
import com.datamate.datamanagement.domain.model.dataset.Tag;
|
import com.datamate.datamanagement.domain.model.dataset.Tag;
|
||||||
@@ -46,9 +47,11 @@ public class KnowledgeSetApplicationService {
|
|||||||
public KnowledgeSet createKnowledgeSet(CreateKnowledgeSetRequest request) {
|
public KnowledgeSet createKnowledgeSet(CreateKnowledgeSetRequest request) {
|
||||||
BusinessAssert.isTrue(knowledgeSetRepository.findByName(request.getName()) == null,
|
BusinessAssert.isTrue(knowledgeSetRepository.findByName(request.getName()) == null,
|
||||||
DataManagementErrorCode.KNOWLEDGE_SET_ALREADY_EXISTS);
|
DataManagementErrorCode.KNOWLEDGE_SET_ALREADY_EXISTS);
|
||||||
|
assertCanUseSensitivity(request.getSensitivity());
|
||||||
|
|
||||||
KnowledgeSet knowledgeSet = KnowledgeConverter.INSTANCE.convertToKnowledgeSet(request);
|
KnowledgeSet knowledgeSet = KnowledgeConverter.INSTANCE.convertToKnowledgeSet(request);
|
||||||
knowledgeSet.setId(UUID.randomUUID().toString());
|
knowledgeSet.setId(UUID.randomUUID().toString());
|
||||||
|
knowledgeSet.setSensitivity(normalizeSensitivity(knowledgeSet.getSensitivity()));
|
||||||
if (knowledgeSet.getStatus() == null) {
|
if (knowledgeSet.getStatus() == null) {
|
||||||
knowledgeSet.setStatus(KnowledgeStatusType.DRAFT);
|
knowledgeSet.setStatus(KnowledgeStatusType.DRAFT);
|
||||||
}
|
}
|
||||||
@@ -67,6 +70,7 @@ public class KnowledgeSetApplicationService {
|
|||||||
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
||||||
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
||||||
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
||||||
|
assertConfidentialAccess(knowledgeSet);
|
||||||
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
|
||||||
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
|
||||||
|
|
||||||
@@ -106,7 +110,8 @@ public class KnowledgeSetApplicationService {
|
|||||||
knowledgeSet.setSourceType(request.getSourceType());
|
knowledgeSet.setSourceType(request.getSourceType());
|
||||||
}
|
}
|
||||||
if (request.getSensitivity() != null) {
|
if (request.getSensitivity() != null) {
|
||||||
knowledgeSet.setSensitivity(request.getSensitivity());
|
assertCanUseSensitivity(request.getSensitivity());
|
||||||
|
knowledgeSet.setSensitivity(normalizeSensitivity(request.getSensitivity()));
|
||||||
}
|
}
|
||||||
if (request.getMetadata() != null) {
|
if (request.getMetadata() != null) {
|
||||||
knowledgeSet.setMetadata(request.getMetadata());
|
knowledgeSet.setMetadata(request.getMetadata());
|
||||||
@@ -123,6 +128,7 @@ public class KnowledgeSetApplicationService {
|
|||||||
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
||||||
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
||||||
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
||||||
|
assertConfidentialAccess(knowledgeSet);
|
||||||
knowledgeSetRepository.removeById(setId);
|
knowledgeSetRepository.removeById(setId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +137,7 @@ public class KnowledgeSetApplicationService {
|
|||||||
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
|
||||||
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
|
||||||
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
|
||||||
|
assertConfidentialAccess(knowledgeSet);
|
||||||
return knowledgeSet;
|
return knowledgeSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,11 +145,33 @@ public class KnowledgeSetApplicationService {
|
|||||||
public PagedResponse<KnowledgeSetResponse> getKnowledgeSets(KnowledgeSetPagingQuery query) {
|
public PagedResponse<KnowledgeSetResponse> getKnowledgeSets(KnowledgeSetPagingQuery query) {
|
||||||
IPage<KnowledgeSet> page = new Page<>(query.getPage(), query.getSize());
|
IPage<KnowledgeSet> page = new Page<>(query.getPage(), query.getSize());
|
||||||
String ownerFilterUserId = resourceAccessService.resolveOwnerFilterUserId();
|
String ownerFilterUserId = resourceAccessService.resolveOwnerFilterUserId();
|
||||||
page = knowledgeSetRepository.findByCriteria(page, query, ownerFilterUserId);
|
boolean excludeConfidential = !resourceAccessService.canViewConfidential();
|
||||||
|
page = knowledgeSetRepository.findByCriteria(page, query, ownerFilterUserId, excludeConfidential);
|
||||||
List<KnowledgeSetResponse> responses = KnowledgeConverter.INSTANCE.convertSetResponses(page.getRecords());
|
List<KnowledgeSetResponse> responses = KnowledgeConverter.INSTANCE.convertSetResponses(page.getRecords());
|
||||||
return PagedResponse.of(responses, page.getCurrent(), page.getTotal(), page.getPages());
|
return PagedResponse.of(responses, page.getCurrent(), page.getTotal(), page.getPages());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertConfidentialAccess(KnowledgeSet knowledgeSet) {
|
||||||
|
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(knowledgeSet.getSensitivity())) {
|
||||||
|
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||||
|
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCanUseSensitivity(String sensitivity) {
|
||||||
|
if (ResourceAccessService.CONFIDENTIAL_SENSITIVITY.equalsIgnoreCase(sensitivity)) {
|
||||||
|
BusinessAssert.isTrue(resourceAccessService.canViewConfidential(),
|
||||||
|
SystemErrorCode.INSUFFICIENT_PERMISSIONS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeSensitivity(String sensitivity) {
|
||||||
|
if (!StringUtils.hasText(sensitivity)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sensitivity.trim().toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isReadOnlyStatus(KnowledgeStatusType status) {
|
private boolean isReadOnlyStatus(KnowledgeStatusType status) {
|
||||||
return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
|
return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
import org.apache.ibatis.annotations.Select;
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface KnowledgeItemMapper extends BaseMapper<KnowledgeItem> {
|
public interface KnowledgeItemMapper extends BaseMapper<KnowledgeItem> {
|
||||||
@Select("""
|
@Select("""
|
||||||
|
<script>
|
||||||
SELECT
|
SELECT
|
||||||
ki.id AS id,
|
ki.id AS id,
|
||||||
ki.set_id AS setId,
|
ki.set_id AS setId,
|
||||||
@@ -34,19 +37,32 @@ public interface KnowledgeItemMapper extends BaseMapper<KnowledgeItem> {
|
|||||||
FROM t_dm_knowledge_items ki
|
FROM t_dm_knowledge_items ki
|
||||||
LEFT JOIN t_dm_knowledge_sets ks ON ki.set_id = ks.id
|
LEFT JOIN t_dm_knowledge_sets ks ON ki.set_id = ks.id
|
||||||
LEFT JOIN t_dm_dataset_files df ON ki.source_file_id = df.id AND ki.source_type = 'DATASET_FILE'
|
LEFT JOIN t_dm_dataset_files df ON ki.source_file_id = df.id AND ki.source_type = 'DATASET_FILE'
|
||||||
WHERE (ki.source_type = 'FILE_UPLOAD' AND (ki.source_file_id LIKE CONCAT('%', #{keyword}, '%')
|
WHERE ((ki.source_type = 'FILE_UPLOAD' AND (ki.source_file_id LIKE CONCAT('%', #{keyword}, '%')
|
||||||
OR ki.relative_path LIKE CONCAT('%', #{keyword}, '%')))
|
OR ki.relative_path LIKE CONCAT('%', #{keyword}, '%')))
|
||||||
OR (ki.source_type = 'DATASET_FILE' AND (df.file_name LIKE CONCAT('%', #{keyword}, '%')
|
OR (ki.source_type = 'DATASET_FILE' AND (df.file_name LIKE CONCAT('%', #{keyword}, '%')
|
||||||
OR ki.relative_path LIKE CONCAT('%', #{keyword}, '%')))
|
OR ki.relative_path LIKE CONCAT('%', #{keyword}, '%'))))
|
||||||
|
<if test="excludeConfidential">
|
||||||
|
AND (ks.sensitivity IS NULL OR UPPER(TRIM(ks.sensitivity)) != 'CONFIDENTIAL')
|
||||||
|
</if>
|
||||||
ORDER BY ki.created_at DESC
|
ORDER BY ki.created_at DESC
|
||||||
|
</script>
|
||||||
""")
|
""")
|
||||||
IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, @Param("keyword") String keyword);
|
IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, @Param("keyword") String keyword,
|
||||||
|
@Param("excludeConfidential") boolean excludeConfidential);
|
||||||
|
|
||||||
@Select("""
|
@Select("""
|
||||||
|
<script>
|
||||||
SELECT COALESCE(SUM(df.file_size), 0)
|
SELECT COALESCE(SUM(df.file_size), 0)
|
||||||
FROM t_dm_knowledge_items ki
|
FROM t_dm_knowledge_items ki
|
||||||
LEFT JOIN t_dm_dataset_files df ON ki.source_file_id = df.id
|
LEFT JOIN t_dm_dataset_files df ON ki.source_file_id = df.id
|
||||||
WHERE ki.source_type = 'DATASET_FILE'
|
WHERE ki.source_type = 'DATASET_FILE'
|
||||||
|
<if test="setIds != null and setIds.size() > 0">
|
||||||
|
AND ki.set_id IN
|
||||||
|
<foreach collection="setIds" item="setId" open="(" separator="," close=")">
|
||||||
|
#{setId}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
</script>
|
||||||
""")
|
""")
|
||||||
Long sumDatasetFileSize();
|
Long sumDatasetFileSizeBySetIds(@Param("setIds") List<String> setIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package com.datamate.datamanagement.infrastructure.persistence.repository;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.repository.IRepository;
|
import com.baomidou.mybatisplus.extension.repository.IRepository;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
||||||
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
|
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
|
||||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
|
||||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery;
|
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery;
|
||||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse;
|
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,13 +19,13 @@ public interface KnowledgeItemRepository extends IRepository<KnowledgeItem> {
|
|||||||
|
|
||||||
List<KnowledgeItem> findAllBySetId(String setId);
|
List<KnowledgeItem> findAllBySetId(String setId);
|
||||||
|
|
||||||
long countBySourceTypes(List<KnowledgeSourceType> sourceTypes);
|
long countBySourceTypesAndSetIds(List<KnowledgeSourceType> sourceTypes, List<String> setIds);
|
||||||
|
|
||||||
List<KnowledgeItem> findFileUploadItems();
|
List<KnowledgeItem> findFileUploadItemsBySetIds(List<String> setIds);
|
||||||
|
|
||||||
IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, String keyword);
|
IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, String keyword, boolean excludeConfidential);
|
||||||
|
|
||||||
Long sumDatasetFileSize();
|
Long sumDatasetFileSizeBySetIds(List<String> setIds);
|
||||||
|
|
||||||
boolean existsBySetIdAndRelativePath(String setId, String relativePath);
|
boolean existsBySetIdAndRelativePath(String setId, String relativePath);
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,18 @@ import com.baomidou.mybatisplus.extension.repository.IRepository;
|
|||||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
|
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识集仓储接口
|
* 知识集仓储接口
|
||||||
*/
|
*/
|
||||||
public interface KnowledgeSetRepository extends IRepository<KnowledgeSet> {
|
public interface KnowledgeSetRepository extends IRepository<KnowledgeSet> {
|
||||||
KnowledgeSet findByName(String name);
|
KnowledgeSet findByName(String name);
|
||||||
|
|
||||||
IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query, String createdBy);
|
IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query, String createdBy,
|
||||||
|
boolean excludeConfidential);
|
||||||
|
|
||||||
|
long countByCriteria(KnowledgeSetPagingQuery query, String createdBy, boolean excludeConfidential);
|
||||||
|
|
||||||
|
List<String> listSetIdsByCriteria(KnowledgeSetPagingQuery query, String createdBy, boolean excludeConfidential);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,26 +61,37 @@ public class KnowledgeItemRepositoryImpl extends CrudRepository<KnowledgeItemMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long countBySourceTypes(List<KnowledgeSourceType> sourceTypes) {
|
public long countBySourceTypesAndSetIds(List<KnowledgeSourceType> sourceTypes, List<String> setIds) {
|
||||||
|
if (sourceTypes == null || sourceTypes.isEmpty() || setIds == null || setIds.isEmpty()) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
return knowledgeItemMapper.selectCount(new LambdaQueryWrapper<KnowledgeItem>()
|
return knowledgeItemMapper.selectCount(new LambdaQueryWrapper<KnowledgeItem>()
|
||||||
.in(KnowledgeItem::getSourceType, sourceTypes));
|
.in(KnowledgeItem::getSourceType, sourceTypes)
|
||||||
|
.in(KnowledgeItem::getSetId, setIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<KnowledgeItem> findFileUploadItems() {
|
public List<KnowledgeItem> findFileUploadItemsBySetIds(List<String> setIds) {
|
||||||
|
if (setIds == null || setIds.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
return knowledgeItemMapper.selectList(new LambdaQueryWrapper<KnowledgeItem>()
|
return knowledgeItemMapper.selectList(new LambdaQueryWrapper<KnowledgeItem>()
|
||||||
.eq(KnowledgeItem::getSourceType, KnowledgeSourceType.FILE_UPLOAD)
|
.eq(KnowledgeItem::getSourceType, KnowledgeSourceType.FILE_UPLOAD)
|
||||||
.select(KnowledgeItem::getId, KnowledgeItem::getContent, KnowledgeItem::getSourceFileId));
|
.in(KnowledgeItem::getSetId, setIds)
|
||||||
|
.select(KnowledgeItem::getId, KnowledgeItem::getSetId, KnowledgeItem::getContent, KnowledgeItem::getSourceFileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, String keyword) {
|
public IPage<KnowledgeItemSearchResponse> searchFileItems(IPage<?> page, String keyword, boolean excludeConfidential) {
|
||||||
return knowledgeItemMapper.searchFileItems(page, keyword);
|
return knowledgeItemMapper.searchFileItems(page, keyword, excludeConfidential);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long sumDatasetFileSize() {
|
public Long sumDatasetFileSizeBySetIds(List<String> setIds) {
|
||||||
return knowledgeItemMapper.sumDatasetFileSize();
|
if (setIds == null || setIds.isEmpty()) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
return knowledgeItemMapper.sumDatasetFileSizeBySetIds(setIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.datamate.datamanagement.infrastructure.persistence.repository.impl;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.repository.CrudRepository;
|
import com.baomidou.mybatisplus.extension.repository.CrudRepository;
|
||||||
|
import com.datamate.common.auth.application.ResourceAccessService;
|
||||||
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
|
||||||
import com.datamate.datamanagement.infrastructure.persistence.mapper.KnowledgeSetMapper;
|
import com.datamate.datamanagement.infrastructure.persistence.mapper.KnowledgeSetMapper;
|
||||||
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
|
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
|
||||||
@@ -11,6 +12,9 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识集仓储实现类
|
* 知识集仓储实现类
|
||||||
*/
|
*/
|
||||||
@@ -25,25 +29,62 @@ public class KnowledgeSetRepositoryImpl extends CrudRepository<KnowledgeSetMappe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query, String createdBy) {
|
public IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query, String createdBy,
|
||||||
LambdaQueryWrapper<KnowledgeSet> wrapper = new LambdaQueryWrapper<KnowledgeSet>()
|
boolean excludeConfidential) {
|
||||||
.eq(query.getStatus() != null, KnowledgeSet::getStatus, query.getStatus())
|
LambdaQueryWrapper<KnowledgeSet> wrapper = buildCriteriaWrapper(query, createdBy, excludeConfidential);
|
||||||
.eq(StringUtils.isNotBlank(query.getDomain()), KnowledgeSet::getDomain, query.getDomain())
|
wrapper.orderByDesc(KnowledgeSet::getCreatedAt);
|
||||||
.eq(StringUtils.isNotBlank(query.getBusinessLine()), KnowledgeSet::getBusinessLine, query.getBusinessLine())
|
return knowledgeSetMapper.selectPage(page, wrapper);
|
||||||
.eq(StringUtils.isNotBlank(query.getOwner()), KnowledgeSet::getOwner, query.getOwner())
|
|
||||||
.eq(StringUtils.isNotBlank(query.getSensitivity()), KnowledgeSet::getSensitivity, query.getSensitivity())
|
|
||||||
.eq(query.getSourceType() != null, KnowledgeSet::getSourceType, query.getSourceType())
|
|
||||||
.ge(query.getValidFrom() != null, KnowledgeSet::getValidFrom, query.getValidFrom())
|
|
||||||
.le(query.getValidTo() != null, KnowledgeSet::getValidTo, query.getValidTo())
|
|
||||||
.eq(StringUtils.isNotBlank(createdBy), KnowledgeSet::getCreatedBy, createdBy);
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(query.getKeyword())) {
|
|
||||||
wrapper.and(w -> w.like(KnowledgeSet::getName, query.getKeyword())
|
|
||||||
.or()
|
|
||||||
.like(KnowledgeSet::getDescription, query.getKeyword()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String tagName : query.getTags()) {
|
@Override
|
||||||
|
public long countByCriteria(KnowledgeSetPagingQuery query, String createdBy, boolean excludeConfidential) {
|
||||||
|
return knowledgeSetMapper.selectCount(buildCriteriaWrapper(query, createdBy, excludeConfidential));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> listSetIdsByCriteria(KnowledgeSetPagingQuery query, String createdBy, boolean excludeConfidential) {
|
||||||
|
LambdaQueryWrapper<KnowledgeSet> wrapper = buildCriteriaWrapper(query, createdBy, excludeConfidential)
|
||||||
|
.select(KnowledgeSet::getId)
|
||||||
|
.orderByDesc(KnowledgeSet::getCreatedAt);
|
||||||
|
List<KnowledgeSet> sets = knowledgeSetMapper.selectList(wrapper);
|
||||||
|
if (sets == null || sets.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return sets.stream().map(KnowledgeSet::getId).filter(StringUtils::isNotBlank).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LambdaQueryWrapper<KnowledgeSet> buildCriteriaWrapper(KnowledgeSetPagingQuery query,
|
||||||
|
String createdBy,
|
||||||
|
boolean excludeConfidential) {
|
||||||
|
KnowledgeSetPagingQuery safeQuery = query == null ? new KnowledgeSetPagingQuery() : query;
|
||||||
|
|
||||||
|
LambdaQueryWrapper<KnowledgeSet> wrapper = new LambdaQueryWrapper<KnowledgeSet>()
|
||||||
|
.eq(safeQuery.getStatus() != null, KnowledgeSet::getStatus, safeQuery.getStatus())
|
||||||
|
.eq(StringUtils.isNotBlank(safeQuery.getDomain()), KnowledgeSet::getDomain, safeQuery.getDomain())
|
||||||
|
.eq(StringUtils.isNotBlank(safeQuery.getBusinessLine()), KnowledgeSet::getBusinessLine, safeQuery.getBusinessLine())
|
||||||
|
.eq(StringUtils.isNotBlank(safeQuery.getOwner()), KnowledgeSet::getOwner, safeQuery.getOwner())
|
||||||
|
.eq(safeQuery.getSourceType() != null, KnowledgeSet::getSourceType, safeQuery.getSourceType())
|
||||||
|
.ge(safeQuery.getValidFrom() != null, KnowledgeSet::getValidFrom, safeQuery.getValidFrom())
|
||||||
|
.le(safeQuery.getValidTo() != null, KnowledgeSet::getValidTo, safeQuery.getValidTo())
|
||||||
|
.eq(StringUtils.isNotBlank(createdBy), KnowledgeSet::getCreatedBy, createdBy);
|
||||||
|
|
||||||
|
if (queryHasSensitivity(safeQuery)) {
|
||||||
|
wrapper.apply("UPPER(TRIM(sensitivity)) = {0}", normalizeSensitivity(safeQuery.getSensitivity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludeConfidential) {
|
||||||
|
wrapper.and(w -> w.isNull(KnowledgeSet::getSensitivity)
|
||||||
|
.or()
|
||||||
|
.apply("UPPER(TRIM(sensitivity)) != {0}", ResourceAccessService.CONFIDENTIAL_SENSITIVITY));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(safeQuery.getKeyword())) {
|
||||||
|
wrapper.and(w -> w.like(KnowledgeSet::getName, safeQuery.getKeyword())
|
||||||
|
.or()
|
||||||
|
.like(KnowledgeSet::getDescription, safeQuery.getKeyword()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String tagName : safeQuery.getTags()) {
|
||||||
wrapper.and(w ->
|
wrapper.and(w ->
|
||||||
w.apply("tags IS NOT NULL " +
|
w.apply("tags IS NOT NULL " +
|
||||||
"AND JSON_VALID(tags) = 1 " +
|
"AND JSON_VALID(tags) = 1 " +
|
||||||
@@ -52,7 +93,15 @@ public class KnowledgeSetRepositoryImpl extends CrudRepository<KnowledgeSetMappe
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapper.orderByDesc(KnowledgeSet::getCreatedAt);
|
return wrapper;
|
||||||
return knowledgeSetMapper.selectPage(page, wrapper);
|
}
|
||||||
|
|
||||||
|
private boolean queryHasSensitivity(KnowledgeSetPagingQuery query) {
|
||||||
|
String normalized = normalizeSensitivity(query.getSensitivity());
|
||||||
|
return StringUtils.isNotBlank(normalized) && !"ALL".equals(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeSensitivity(String sensitivity) {
|
||||||
|
return StringUtils.upperCase(StringUtils.trimToNull(sensitivity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,23 @@ import java.util.Objects;
|
|||||||
@Service
|
@Service
|
||||||
public class ResourceAccessService {
|
public class ResourceAccessService {
|
||||||
public static final String ADMIN_ROLE_CODE = "ROLE_ADMIN";
|
public static final String ADMIN_ROLE_CODE = "ROLE_ADMIN";
|
||||||
|
public static final String VIEW_CONFIDENTIAL_PERMISSION = "knowledge:view-confidential";
|
||||||
|
public static final String CONFIDENTIAL_SENSITIVITY = "CONFIDENTIAL";
|
||||||
|
|
||||||
public boolean isAdmin() {
|
public boolean isAdmin() {
|
||||||
return RequestUserContextHolder.hasRole(ADMIN_ROLE_CODE);
|
return RequestUserContextHolder.hasRole(ADMIN_ROLE_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前用户是否有权查看保密知识
|
||||||
|
*/
|
||||||
|
public boolean canViewConfidential() {
|
||||||
|
if (isAdmin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return RequestUserContextHolder.hasPermission(VIEW_CONFIDENTIAL_PERMISSION);
|
||||||
|
}
|
||||||
|
|
||||||
public String getCurrentUserId() {
|
public String getCurrentUserId() {
|
||||||
return RequestUserContextHolder.getCurrentUserId();
|
return RequestUserContextHolder.getCurrentUserId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,19 +15,21 @@ public class RequestUserContext {
|
|||||||
private final String userId;
|
private final String userId;
|
||||||
private final String username;
|
private final String username;
|
||||||
private final List<String> roles;
|
private final List<String> roles;
|
||||||
|
private final List<String> permissions;
|
||||||
|
|
||||||
private RequestUserContext(String userId, String username, List<String> roles) {
|
private RequestUserContext(String userId, String username, List<String> roles, List<String> permissions) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.roles = roles == null ? Collections.emptyList() : List.copyOf(roles);
|
this.roles = roles == null ? Collections.emptyList() : List.copyOf(roles);
|
||||||
|
this.permissions = permissions == null ? Collections.emptyList() : List.copyOf(permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RequestUserContext of(String userId, String username, List<String> roles) {
|
public static RequestUserContext of(String userId, String username, List<String> roles, List<String> permissions) {
|
||||||
return new RequestUserContext(userId, username, roles);
|
return new RequestUserContext(userId, username, roles, permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RequestUserContext empty() {
|
public static RequestUserContext empty() {
|
||||||
return new RequestUserContext(null, null, Collections.emptyList());
|
return new RequestUserContext(null, null, Collections.emptyList(), Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasRole(String roleCode) {
|
public boolean hasRole(String roleCode) {
|
||||||
@@ -36,5 +38,12 @@ public class RequestUserContext {
|
|||||||
}
|
}
|
||||||
return roles.stream().anyMatch(role -> StringUtils.hasText(role) && Objects.equals(role.trim(), roleCode));
|
return roles.stream().anyMatch(role -> StringUtils.hasText(role) && Objects.equals(role.trim(), roleCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasPermission(String permissionCode) {
|
||||||
|
if (!StringUtils.hasText(permissionCode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return permissions.stream().anyMatch(p -> StringUtils.hasText(p) && Objects.equals(p.trim(), permissionCode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,19 @@ public final class RequestUserContextHolder {
|
|||||||
.anyMatch(role -> StringUtils.hasText(role) && roleCode.equalsIgnoreCase(role.trim()));
|
.anyMatch(role -> StringUtils.hasText(role) && roleCode.equalsIgnoreCase(role.trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<String> getCurrentPermissions() {
|
||||||
|
List<String> permissions = get().getPermissions();
|
||||||
|
return permissions == null ? Collections.emptyList() : permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasPermission(String permissionCode) {
|
||||||
|
if (!StringUtils.hasText(permissionCode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return getCurrentPermissions().stream()
|
||||||
|
.anyMatch(p -> StringUtils.hasText(p) && permissionCode.equals(p.trim()));
|
||||||
|
}
|
||||||
|
|
||||||
public static void clear() {
|
public static void clear() {
|
||||||
USER_CONTEXT_HOLDER.remove();
|
USER_CONTEXT_HOLDER.remove();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,15 @@ public class RequestUserContextInterceptor implements HandlerInterceptor {
|
|||||||
private static final String HEADER_USER_ID = "X-User-Id";
|
private static final String HEADER_USER_ID = "X-User-Id";
|
||||||
private static final String HEADER_USER_NAME = "X-User-Name";
|
private static final String HEADER_USER_NAME = "X-User-Name";
|
||||||
private static final String HEADER_USER_ROLES = "X-User-Roles";
|
private static final String HEADER_USER_ROLES = "X-User-Roles";
|
||||||
|
private static final String HEADER_USER_PERMISSIONS = "X-User-Permissions";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||||
String userId = normalizeValue(request.getHeader(HEADER_USER_ID));
|
String userId = normalizeValue(request.getHeader(HEADER_USER_ID));
|
||||||
String username = normalizeValue(request.getHeader(HEADER_USER_NAME));
|
String username = normalizeValue(request.getHeader(HEADER_USER_NAME));
|
||||||
List<String> roleCodes = parseRoleCodes(request.getHeader(HEADER_USER_ROLES));
|
List<String> roleCodes = parseCommaSeparatedValues(request.getHeader(HEADER_USER_ROLES));
|
||||||
RequestUserContextHolder.set(RequestUserContext.of(userId, username, roleCodes));
|
List<String> permissionCodes = parseCommaSeparatedValues(request.getHeader(HEADER_USER_PERMISSIONS));
|
||||||
|
RequestUserContextHolder.set(RequestUserContext.of(userId, username, roleCodes, permissionCodes));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,11 +42,11 @@ public class RequestUserContextInterceptor implements HandlerInterceptor {
|
|||||||
return value.trim();
|
return value.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> parseRoleCodes(String roleHeader) {
|
private List<String> parseCommaSeparatedValues(String header) {
|
||||||
if (!StringUtils.hasText(roleHeader)) {
|
if (!StringUtils.hasText(header)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return Arrays.stream(roleHeader.split(","))
|
return Arrays.stream(header.split(","))
|
||||||
.map(String::trim)
|
.map(String::trim)
|
||||||
.filter(StringUtils::hasText)
|
.filter(StringUtils::hasText)
|
||||||
.toList();
|
.toList();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { Card, Button, Table, Tooltip, Tag, App, Statistic } from "antd";
|
import { Card, Button, Table, Tooltip, Tag, App, Statistic } from "antd";
|
||||||
import { DeleteOutlined, EditOutlined } from "@ant-design/icons";
|
import { DeleteOutlined, EditOutlined, LockOutlined } from "@ant-design/icons";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import CardView from "@/components/CardView";
|
import CardView from "@/components/CardView";
|
||||||
import { SearchControls } from "@/components/SearchControls";
|
import { SearchControls } from "@/components/SearchControls";
|
||||||
@@ -15,8 +15,14 @@ import {
|
|||||||
knowledgeStatusOptions,
|
knowledgeStatusOptions,
|
||||||
mapKnowledgeSet,
|
mapKnowledgeSet,
|
||||||
KnowledgeSetView,
|
KnowledgeSetView,
|
||||||
|
sensitivityMap,
|
||||||
|
sensitivityOptions,
|
||||||
} from "../knowledge-management.const";
|
} from "../knowledge-management.const";
|
||||||
import { KnowledgeManagementStatistics, KnowledgeSet } from "../knowledge-management.model";
|
import {
|
||||||
|
KnowledgeManagementStatistics,
|
||||||
|
KnowledgeSensitivityType,
|
||||||
|
KnowledgeSet,
|
||||||
|
} from "../knowledge-management.model";
|
||||||
import CreateKnowledgeSet from "../components/CreateKnowledgeSet";
|
import CreateKnowledgeSet from "../components/CreateKnowledgeSet";
|
||||||
import {
|
import {
|
||||||
createDatasetTagUsingPost,
|
createDatasetTagUsingPost,
|
||||||
@@ -76,6 +82,11 @@ export default function KnowledgeManagementPage() {
|
|||||||
label: "状态",
|
label: "状态",
|
||||||
options: knowledgeStatusOptions,
|
options: knowledgeStatusOptions,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "sensitivity",
|
||||||
|
label: "敏感级别",
|
||||||
|
options: sensitivityOptions,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "tags",
|
key: "tags",
|
||||||
label: "标签",
|
label: "标签",
|
||||||
@@ -101,7 +112,16 @@ export default function KnowledgeManagementPage() {
|
|||||||
30000,
|
30000,
|
||||||
false,
|
false,
|
||||||
[],
|
[],
|
||||||
0
|
0,
|
||||||
|
(filters) => {
|
||||||
|
const sensitivity = Array.isArray(filters?.sensitivity)
|
||||||
|
? (filters.sensitivity[0] as string | undefined)
|
||||||
|
: undefined;
|
||||||
|
if (!sensitivity || sensitivity === "all") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return { sensitivity: sensitivity.toUpperCase() };
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchStatistics = useCallback(async () => {
|
const fetchStatistics = useCallback(async () => {
|
||||||
@@ -204,6 +224,25 @@ export default function KnowledgeManagementPage() {
|
|||||||
<Tag color={status?.color}>{status?.label}</Tag>
|
<Tag color={status?.color}>{status?.label}</Tag>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "敏感级别",
|
||||||
|
dataIndex: "sensitivity",
|
||||||
|
key: "sensitivity",
|
||||||
|
width: 100,
|
||||||
|
render: (sensitivity: string) => {
|
||||||
|
const normalized = sensitivity ? sensitivity.toUpperCase() : "";
|
||||||
|
const meta = normalized ? sensitivityMap[normalized] : null;
|
||||||
|
if (!meta) return "-";
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
color={meta.color}
|
||||||
|
icon={normalized === KnowledgeSensitivityType.CONFIDENTIAL ? <LockOutlined /> : undefined}
|
||||||
|
>
|
||||||
|
{meta.label}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "领域",
|
title: "领域",
|
||||||
dataIndex: "domain",
|
dataIndex: "domain",
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ import {
|
|||||||
import {
|
import {
|
||||||
knowledgeSourceTypeOptions,
|
knowledgeSourceTypeOptions,
|
||||||
knowledgeStatusOptions,
|
knowledgeStatusOptions,
|
||||||
// sensitivityOptions,
|
sensitivityOptions,
|
||||||
} from "../knowledge-management.const";
|
} from "../knowledge-management.const";
|
||||||
import {
|
import {
|
||||||
KnowledgeSet,
|
KnowledgeSet,
|
||||||
|
KnowledgeSensitivityType,
|
||||||
KnowledgeStatusType,
|
KnowledgeStatusType,
|
||||||
} from "../knowledge-management.model";
|
} from "../knowledge-management.model";
|
||||||
import { queryDatasetTagsUsingGet } from "@/pages/DataManagement/dataset.api";
|
import { queryDatasetTagsUsingGet } from "@/pages/DataManagement/dataset.api";
|
||||||
@@ -65,7 +66,7 @@ export default function CreateKnowledgeSet({
|
|||||||
validFrom: data.validFrom ? dayjs(data.validFrom) : null,
|
validFrom: data.validFrom ? dayjs(data.validFrom) : null,
|
||||||
validTo: data.validTo ? dayjs(data.validTo) : null,
|
validTo: data.validTo ? dayjs(data.validTo) : null,
|
||||||
sourceType: data.sourceType,
|
sourceType: data.sourceType,
|
||||||
sensitivity: data.sensitivity,
|
sensitivity: data.sensitivity?.toUpperCase() || undefined,
|
||||||
tags: data.tags?.map((tag) => tag.name) || [],
|
tags: data.tags?.map((tag) => tag.name) || [],
|
||||||
metadata: data.metadata,
|
metadata: data.metadata,
|
||||||
});
|
});
|
||||||
@@ -85,6 +86,9 @@ export default function CreateKnowledgeSet({
|
|||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
...values,
|
...values,
|
||||||
|
sensitivity: values.sensitivity
|
||||||
|
? String(values.sensitivity).toUpperCase()
|
||||||
|
: undefined,
|
||||||
validFrom,
|
validFrom,
|
||||||
validTo,
|
validTo,
|
||||||
tags: values.tags || [],
|
tags: values.tags || [],
|
||||||
@@ -124,7 +128,11 @@ export default function CreateKnowledgeSet({
|
|||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
form.setFieldsValue({ status: KnowledgeStatusType.DRAFT, tags: [] });
|
form.setFieldsValue({
|
||||||
|
status: KnowledgeStatusType.DRAFT,
|
||||||
|
sensitivity: KnowledgeSensitivityType.INTERNAL,
|
||||||
|
tags: [],
|
||||||
|
});
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -170,9 +178,9 @@ export default function CreateKnowledgeSet({
|
|||||||
<Form.Item label="负责人" name="owner">
|
<Form.Item label="负责人" name="owner">
|
||||||
<Input placeholder="请输入负责人" />
|
<Input placeholder="请输入负责人" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{/* <Form.Item label="敏感级别" name="sensitivity">
|
<Form.Item label="敏感级别" name="sensitivity">
|
||||||
<Select options={sensitivityOptions} placeholder="请选择敏感级别" />
|
<Select options={sensitivityOptions} placeholder="请选择敏感级别" allowClear />
|
||||||
</Form.Item> */}
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<Form.Item label="有效期开始" name="validFrom">
|
<Form.Item label="有效期开始" name="validFrom">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
KnowledgeContentType,
|
KnowledgeContentType,
|
||||||
KnowledgeItem,
|
KnowledgeItem,
|
||||||
KnowledgeSet,
|
KnowledgeSet,
|
||||||
|
KnowledgeSensitivityType,
|
||||||
KnowledgeSourceType,
|
KnowledgeSourceType,
|
||||||
KnowledgeStatusType,
|
KnowledgeStatusType,
|
||||||
} from "./knowledge-management.model";
|
} from "./knowledge-management.model";
|
||||||
@@ -66,10 +67,22 @@ export const knowledgeSourceTypeOptions = [
|
|||||||
{ label: "文件上传", value: KnowledgeSourceType.FILE_UPLOAD },
|
{ label: "文件上传", value: KnowledgeSourceType.FILE_UPLOAD },
|
||||||
];
|
];
|
||||||
|
|
||||||
// export const sensitivityOptions = [
|
export const sensitivityOptions = [
|
||||||
// { label: "敏感", value: "敏感" },
|
{ label: "公开", value: KnowledgeSensitivityType.PUBLIC },
|
||||||
// { label: "不敏感", value: "不敏感" },
|
{ label: "内部", value: KnowledgeSensitivityType.INTERNAL },
|
||||||
// ];
|
{ label: "保密", value: KnowledgeSensitivityType.CONFIDENTIAL },
|
||||||
|
];
|
||||||
|
|
||||||
|
export type SensitivityMeta = {
|
||||||
|
label: string;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sensitivityMap: Record<string, SensitivityMeta> = {
|
||||||
|
[KnowledgeSensitivityType.PUBLIC]: { label: "公开", color: "#52c41a" },
|
||||||
|
[KnowledgeSensitivityType.INTERNAL]: { label: "内部", color: "#1677ff" },
|
||||||
|
[KnowledgeSensitivityType.CONFIDENTIAL]: { label: "保密", color: "#f5222d" },
|
||||||
|
};
|
||||||
|
|
||||||
export type KnowledgeSetView = {
|
export type KnowledgeSetView = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -118,6 +131,7 @@ export type KnowledgeItemView = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function mapKnowledgeSet(data: KnowledgeSet): KnowledgeSetView {
|
export function mapKnowledgeSet(data: KnowledgeSet): KnowledgeSetView {
|
||||||
|
const normalizedSensitivity = data.sensitivity?.toUpperCase();
|
||||||
return {
|
return {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@@ -131,7 +145,7 @@ export function mapKnowledgeSet(data: KnowledgeSet): KnowledgeSetView {
|
|||||||
validFrom: data.validFrom,
|
validFrom: data.validFrom,
|
||||||
validTo: data.validTo,
|
validTo: data.validTo,
|
||||||
sourceType: data.sourceType,
|
sourceType: data.sourceType,
|
||||||
sensitivity: data.sensitivity,
|
sensitivity: normalizedSensitivity,
|
||||||
metadata: data.metadata,
|
metadata: data.metadata,
|
||||||
createdAt: data.createdAt ? formatDateTime(data.createdAt) : "",
|
createdAt: data.createdAt ? formatDateTime(data.createdAt) : "",
|
||||||
updatedAt: data.updatedAt ? formatDateTime(data.updatedAt) : "",
|
updatedAt: data.updatedAt ? formatDateTime(data.updatedAt) : "",
|
||||||
@@ -142,6 +156,7 @@ export function mapKnowledgeSet(data: KnowledgeSet): KnowledgeSetView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function mapKnowledgeItem(data: KnowledgeItem): KnowledgeItemView {
|
export function mapKnowledgeItem(data: KnowledgeItem): KnowledgeItemView {
|
||||||
|
const normalizedSensitivity = data.sensitivity?.toUpperCase();
|
||||||
return {
|
return {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
setId: data.setId,
|
setId: data.setId,
|
||||||
@@ -156,7 +171,7 @@ export function mapKnowledgeItem(data: KnowledgeItem): KnowledgeItemView {
|
|||||||
validFrom: data.validFrom,
|
validFrom: data.validFrom,
|
||||||
validTo: data.validTo,
|
validTo: data.validTo,
|
||||||
sourceType: data.sourceType,
|
sourceType: data.sourceType,
|
||||||
sensitivity: data.sensitivity,
|
sensitivity: normalizedSensitivity,
|
||||||
sourceDatasetId: data.sourceDatasetId,
|
sourceDatasetId: data.sourceDatasetId,
|
||||||
sourceFileId: data.sourceFileId,
|
sourceFileId: data.sourceFileId,
|
||||||
relativePath: data.relativePath,
|
relativePath: data.relativePath,
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ export enum KnowledgeSourceType {
|
|||||||
FILE_UPLOAD = "FILE_UPLOAD",
|
FILE_UPLOAD = "FILE_UPLOAD",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum KnowledgeSensitivityType {
|
||||||
|
PUBLIC = "PUBLIC",
|
||||||
|
INTERNAL = "INTERNAL",
|
||||||
|
CONFIDENTIAL = "CONFIDENTIAL",
|
||||||
|
}
|
||||||
|
|
||||||
export interface KnowledgeTag {
|
export interface KnowledgeTag {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ VALUES ('perm-dm-read', 'module:data-management:read', '数据管理读取', 'da
|
|||||||
('perm-content-use', 'module:content-generation:use', '内容生成功能使用', 'content-generation', 'use', '/api/content-generation/**', 'POST,PUT,PATCH', 1, 1),
|
('perm-content-use', 'module:content-generation:use', '内容生成功能使用', 'content-generation', 'use', '/api/content-generation/**', 'POST,PUT,PATCH', 1, 1),
|
||||||
('perm-user-manage', 'system:user:manage', '用户管理', 'system', 'manage-user', '/api/auth/users/**', 'GET,POST,PUT,PATCH,DELETE', 1, 1),
|
('perm-user-manage', 'system:user:manage', '用户管理', 'system', 'manage-user', '/api/auth/users/**', 'GET,POST,PUT,PATCH,DELETE', 1, 1),
|
||||||
('perm-role-manage', 'system:role:manage', '角色管理', 'system', 'manage-role', '/api/auth/roles/**', 'GET,POST,PUT,PATCH,DELETE', 1, 1),
|
('perm-role-manage', 'system:role:manage', '角色管理', 'system', 'manage-role', '/api/auth/roles/**', 'GET,POST,PUT,PATCH,DELETE', 1, 1),
|
||||||
('perm-perm-manage', 'system:permission:manage', '权限管理', 'system', 'manage-permission', '/api/auth/permissions/**', 'GET,POST,PUT,PATCH,DELETE', 1, 1);
|
('perm-perm-manage', 'system:permission:manage', '权限管理', 'system', 'manage-permission', '/api/auth/permissions/**', 'GET,POST,PUT,PATCH,DELETE', 1, 1),
|
||||||
|
('perm-km-view-confidential', 'knowledge:view-confidential', '允许查看保密知识', 'knowledge-management', 'view-confidential', '', '', 1, 1);
|
||||||
|
|
||||||
-- 管理员拥有所有权限
|
-- 管理员拥有所有权限
|
||||||
INSERT IGNORE INTO t_auth_role_permissions (role_id, permission_id)
|
INSERT IGNORE INTO t_auth_role_permissions (role_id, permission_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user