fix(auth): harden confidential knowledge access checks and sensitivity filtering

This commit is contained in:
2026-02-09 17:09:34 +08:00
parent 71f8f7d1c3
commit 2f8645a011
19 changed files with 383 additions and 80 deletions

View File

@@ -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());
} }

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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);

View File

@@ -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;
} }

View File

@@ -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);
} }

View File

@@ -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);

View File

@@ -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);
} }

View File

@@ -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

View File

@@ -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,
boolean excludeConfidential) {
LambdaQueryWrapper<KnowledgeSet> wrapper = buildCriteriaWrapper(query, createdBy, excludeConfidential);
wrapper.orderByDesc(KnowledgeSet::getCreatedAt);
return knowledgeSetMapper.selectPage(page, wrapper);
}
@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>() LambdaQueryWrapper<KnowledgeSet> wrapper = new LambdaQueryWrapper<KnowledgeSet>()
.eq(query.getStatus() != null, KnowledgeSet::getStatus, query.getStatus()) .eq(safeQuery.getStatus() != null, KnowledgeSet::getStatus, safeQuery.getStatus())
.eq(StringUtils.isNotBlank(query.getDomain()), KnowledgeSet::getDomain, query.getDomain()) .eq(StringUtils.isNotBlank(safeQuery.getDomain()), KnowledgeSet::getDomain, safeQuery.getDomain())
.eq(StringUtils.isNotBlank(query.getBusinessLine()), KnowledgeSet::getBusinessLine, query.getBusinessLine()) .eq(StringUtils.isNotBlank(safeQuery.getBusinessLine()), KnowledgeSet::getBusinessLine, safeQuery.getBusinessLine())
.eq(StringUtils.isNotBlank(query.getOwner()), KnowledgeSet::getOwner, query.getOwner()) .eq(StringUtils.isNotBlank(safeQuery.getOwner()), KnowledgeSet::getOwner, safeQuery.getOwner())
.eq(StringUtils.isNotBlank(query.getSensitivity()), KnowledgeSet::getSensitivity, query.getSensitivity()) .eq(safeQuery.getSourceType() != null, KnowledgeSet::getSourceType, safeQuery.getSourceType())
.eq(query.getSourceType() != null, KnowledgeSet::getSourceType, query.getSourceType()) .ge(safeQuery.getValidFrom() != null, KnowledgeSet::getValidFrom, safeQuery.getValidFrom())
.ge(query.getValidFrom() != null, KnowledgeSet::getValidFrom, query.getValidFrom()) .le(safeQuery.getValidTo() != null, KnowledgeSet::getValidTo, safeQuery.getValidTo())
.le(query.getValidTo() != null, KnowledgeSet::getValidTo, query.getValidTo())
.eq(StringUtils.isNotBlank(createdBy), KnowledgeSet::getCreatedBy, createdBy); .eq(StringUtils.isNotBlank(createdBy), KnowledgeSet::getCreatedBy, createdBy);
if (StringUtils.isNotBlank(query.getKeyword())) { if (queryHasSensitivity(safeQuery)) {
wrapper.and(w -> w.like(KnowledgeSet::getName, query.getKeyword()) wrapper.apply("UPPER(TRIM(sensitivity)) = {0}", normalizeSensitivity(safeQuery.getSensitivity()));
.or()
.like(KnowledgeSet::getDescription, query.getKeyword()));
} }
for (String tagName : query.getTags()) { 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));
} }
} }

View File

@@ -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();
} }

View File

@@ -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));
}
} }

View File

@@ -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();
} }

View File

@@ -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();

View File

@@ -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",

View File

@@ -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">

View File

@@ -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,

View File

@@ -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;

View File

@@ -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)