feat(data-management): 添加知识库管理功能

- 在DataManagementErrorCode中新增知识库相关错误码定义
- 在数据库初始化脚本中创建知识集和知识条目表结构
- 新增KnowledgeItemApplicationService实现知识条目的CRUD操作
- 新增KnowledgeSetApplicationService实现知识集的CRUD操作
- 定义KnowledgeContentType、KnowledgeSourceType和KnowledgeStatusType枚举类型
- 创建KnowledgeItem和KnowledgeSet领域模型实体
- 实现KnowledgeItemMapper和KnowledgeSetMapper数据访问接口
- 提供KnowledgeItemRepositoryImpl和KnowledgeSetRepositoryImpl仓储实现
- 添加知识条目按条件分页查询功能
- 实现知识条目从数据集文件导入的功能
- 支持知识集和知识条目的标签管理和状态控制
This commit is contained in:
2026-01-21 11:32:45 +08:00
parent ecec69422d
commit e78acbea0a
27 changed files with 1740 additions and 1 deletions

View File

@@ -0,0 +1,340 @@
package com.datamate.datamanagement.application;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datamate.common.infrastructure.exception.BusinessAssert;
import com.datamate.common.infrastructure.exception.BusinessException;
import com.datamate.common.infrastructure.exception.CommonErrorCode;
import com.datamate.common.infrastructure.exception.SystemErrorCode;
import com.datamate.common.interfaces.PagedResponse;
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import com.datamate.datamanagement.domain.model.dataset.Dataset;
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.KnowledgeSet;
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.DatasetRepository;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
import com.datamate.datamanagement.interfaces.converter.KnowledgeConverter;
import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeItemRequest;
import com.datamate.datamanagement.interfaces.dto.ImportKnowledgeItemsRequest;
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery;
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemResponse;
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeItemRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
* 知识条目应用服务
*/
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class KnowledgeItemApplicationService {
private static final Set<String> SUPPORTED_TEXT_EXTENSIONS = Set.of("txt", "md", "markdown");
private final KnowledgeItemRepository knowledgeItemRepository;
private final KnowledgeSetRepository knowledgeSetRepository;
private final DatasetRepository datasetRepository;
private final DatasetFileRepository datasetFileRepository;
private final TagMapper tagMapper;
public KnowledgeItem createKnowledgeItem(String setId, CreateKnowledgeItemRequest request) {
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
KnowledgeItem knowledgeItem = KnowledgeConverter.INSTANCE.convertToKnowledgeItem(request);
knowledgeItem.setId(UUID.randomUUID().toString());
knowledgeItem.setSetId(setId);
knowledgeItem.setSourceType(KnowledgeSourceType.MANUAL);
if (knowledgeItem.getStatus() == null) {
knowledgeItem.setStatus(KnowledgeStatusType.DRAFT);
}
applyDefaultsFromSet(knowledgeItem, knowledgeSet, request.getTags(), request.getDomain(),
request.getBusinessLine(), request.getOwner(), request.getSensitivity(),
request.getValidFrom(), request.getValidTo(), request.getMetadata());
validateEffectivePeriod(knowledgeItem.getValidFrom(), knowledgeItem.getValidTo());
validatePublishable(knowledgeItem);
knowledgeItemRepository.save(knowledgeItem);
return knowledgeItem;
}
public KnowledgeItem updateKnowledgeItem(String setId, String itemId, UpdateKnowledgeItemRequest request) {
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeItem.getStatus()),
DataManagementErrorCode.KNOWLEDGE_ITEM_STATUS_ERROR);
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
if (request.getTitle() != null) {
BusinessAssert.isTrue(StringUtils.isNotBlank(request.getTitle()), CommonErrorCode.PARAM_ERROR);
knowledgeItem.setTitle(request.getTitle());
}
if (request.getContent() != null) {
BusinessAssert.isTrue(StringUtils.isNotBlank(request.getContent()), CommonErrorCode.PARAM_ERROR);
knowledgeItem.setContent(request.getContent());
}
if (request.getContentType() != null) {
knowledgeItem.setContentType(request.getContentType());
}
if (request.getStatus() != null) {
knowledgeItem.setStatus(request.getStatus());
}
if (request.getTags() != null) {
knowledgeItem.setTags(processTagNames(request.getTags()));
}
if (request.getDomain() != null) {
knowledgeItem.setDomain(request.getDomain());
}
if (request.getBusinessLine() != null) {
knowledgeItem.setBusinessLine(request.getBusinessLine());
}
if (request.getOwner() != null) {
knowledgeItem.setOwner(request.getOwner());
}
if (request.getValidFrom() != null || request.getValidTo() != null) {
knowledgeItem.setValidFrom(request.getValidFrom());
knowledgeItem.setValidTo(request.getValidTo());
}
if (request.getSensitivity() != null) {
knowledgeItem.setSensitivity(request.getSensitivity());
}
if (request.getMetadata() != null) {
knowledgeItem.setMetadata(request.getMetadata());
}
validateEffectivePeriod(knowledgeItem.getValidFrom(), knowledgeItem.getValidTo());
validatePublishable(knowledgeItem);
knowledgeItemRepository.updateById(knowledgeItem);
return knowledgeItem;
}
public void deleteKnowledgeItem(String setId, String itemId) {
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
knowledgeItemRepository.removeById(itemId);
}
@Transactional(readOnly = true)
public KnowledgeItem getKnowledgeItem(String setId, String itemId) {
KnowledgeItem knowledgeItem = knowledgeItemRepository.getById(itemId);
BusinessAssert.notNull(knowledgeItem, DataManagementErrorCode.KNOWLEDGE_ITEM_NOT_FOUND);
BusinessAssert.isTrue(Objects.equals(knowledgeItem.getSetId(), setId), CommonErrorCode.PARAM_ERROR);
return knowledgeItem;
}
@Transactional(readOnly = true)
public PagedResponse<KnowledgeItemResponse> getKnowledgeItems(String setId, KnowledgeItemPagingQuery query) {
query.setSetId(setId);
IPage<KnowledgeItem> page = new Page<>(query.getPage(), query.getSize());
page = knowledgeItemRepository.findByCriteria(page, query);
List<KnowledgeItemResponse> responses = KnowledgeConverter.INSTANCE.convertItemResponses(page.getRecords());
return PagedResponse.of(responses, page.getCurrent(), page.getTotal(), page.getPages());
}
public List<KnowledgeItem> importKnowledgeItems(String setId, ImportKnowledgeItemsRequest request) {
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
Dataset dataset = datasetRepository.getById(request.getDatasetId());
BusinessAssert.notNull(dataset, DataManagementErrorCode.DATASET_NOT_FOUND);
List<KnowledgeItem> items = new ArrayList<>();
for (String fileId : request.getFileIds()) {
DatasetFile datasetFile = datasetFileRepository.getById(fileId);
BusinessAssert.notNull(datasetFile, DataManagementErrorCode.DATASET_FILE_NOT_FOUND);
BusinessAssert.isTrue(Objects.equals(datasetFile.getDatasetId(), dataset.getId()), CommonErrorCode.PARAM_ERROR);
KnowledgeContentType contentType = resolveContentType(datasetFile);
String content = readTextContent(datasetFile, dataset);
KnowledgeItem knowledgeItem = new KnowledgeItem();
knowledgeItem.setId(UUID.randomUUID().toString());
knowledgeItem.setSetId(setId);
knowledgeItem.setTitle(stripExtension(datasetFile.getFileName()));
knowledgeItem.setContent(content);
knowledgeItem.setContentType(contentType);
knowledgeItem.setSourceType(KnowledgeSourceType.DATASET_FILE);
knowledgeItem.setSourceDatasetId(dataset.getId());
knowledgeItem.setSourceFileId(datasetFile.getId());
knowledgeItem.setStatus(request.getStatus() == null ? KnowledgeStatusType.DRAFT : request.getStatus());
applyDefaultsFromSet(knowledgeItem, knowledgeSet, request.getTags(), request.getDomain(),
request.getBusinessLine(), request.getOwner(), request.getSensitivity(),
request.getValidFrom(), request.getValidTo(), request.getMetadata());
validateEffectivePeriod(knowledgeItem.getValidFrom(), knowledgeItem.getValidTo());
validatePublishable(knowledgeItem);
items.add(knowledgeItem);
}
if (CollectionUtils.isNotEmpty(items)) {
knowledgeItemRepository.saveBatch(items, items.size());
}
return items;
}
private KnowledgeSet requireKnowledgeSet(String setId) {
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
return knowledgeSet;
}
private KnowledgeContentType resolveContentType(DatasetFile datasetFile) {
String extension = getFileExtension(datasetFile);
if (StringUtils.isBlank(extension)) {
throw BusinessException.of(DataManagementErrorCode.KNOWLEDGE_ITEM_SOURCE_NOT_SUPPORTED);
}
String normalized = extension.toLowerCase();
if (!SUPPORTED_TEXT_EXTENSIONS.contains(normalized)) {
throw BusinessException.of(DataManagementErrorCode.KNOWLEDGE_ITEM_SOURCE_NOT_SUPPORTED);
}
return normalized.equals("md") || normalized.equals("markdown")
? KnowledgeContentType.MARKDOWN
: KnowledgeContentType.TEXT;
}
private String getFileExtension(DatasetFile datasetFile) {
String fileName = datasetFile.getFileName();
if (StringUtils.isNotBlank(fileName) && fileName.contains(".")) {
return fileName.substring(fileName.lastIndexOf('.') + 1);
}
String fileType = datasetFile.getFileType();
if (StringUtils.isNotBlank(fileType) && fileType.startsWith(".")) {
return fileType.substring(1);
}
return fileType;
}
private String readTextContent(DatasetFile datasetFile, Dataset dataset) {
Path filePath = Paths.get(datasetFile.getFilePath()).normalize();
Path datasetPath = Paths.get(dataset.getPath()).normalize();
BusinessAssert.isTrue(filePath.startsWith(datasetPath), CommonErrorCode.PARAM_ERROR);
BusinessAssert.isTrue(Files.exists(filePath) && Files.isRegularFile(filePath), CommonErrorCode.PARAM_ERROR);
try {
return Files.readString(filePath, StandardCharsets.UTF_8);
} catch (IOException e) {
log.error("read knowledge content error, fileId: {}", datasetFile.getId(), e);
throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR);
}
}
private String stripExtension(String fileName) {
if (StringUtils.isBlank(fileName)) {
return "未命名";
}
int dotIndex = fileName.lastIndexOf('.');
if (dotIndex <= 0) {
return fileName;
}
return fileName.substring(0, dotIndex);
}
private void applyDefaultsFromSet(KnowledgeItem knowledgeItem,
KnowledgeSet knowledgeSet,
List<String> tags,
String domain,
String businessLine,
String owner,
String sensitivity,
LocalDate validFrom,
LocalDate validTo,
String metadata) {
if (tags != null) {
knowledgeItem.setTags(processTagNames(tags));
} else if (knowledgeSet.getTags() != null) {
knowledgeItem.setTags(new HashSet<>(knowledgeSet.getTags()));
} else {
knowledgeItem.setTags(new HashSet<>());
}
knowledgeItem.setDomain(StringUtils.isNotBlank(domain) ? domain : knowledgeSet.getDomain());
knowledgeItem.setBusinessLine(StringUtils.isNotBlank(businessLine) ? businessLine : knowledgeSet.getBusinessLine());
knowledgeItem.setOwner(StringUtils.isNotBlank(owner) ? owner : knowledgeSet.getOwner());
knowledgeItem.setSensitivity(StringUtils.isNotBlank(sensitivity) ? sensitivity : knowledgeSet.getSensitivity());
knowledgeItem.setValidFrom(validFrom != null ? validFrom : knowledgeSet.getValidFrom());
knowledgeItem.setValidTo(validTo != null ? validTo : knowledgeSet.getValidTo());
knowledgeItem.setMetadata(metadata != null ? metadata : knowledgeSet.getMetadata());
}
private boolean isReadOnlyStatus(KnowledgeStatusType status) {
return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
}
private void validatePublishable(KnowledgeItem knowledgeItem) {
if (knowledgeItem.getStatus() != KnowledgeStatusType.PUBLISHED) {
return;
}
BusinessAssert.isTrue(StringUtils.isNotBlank(knowledgeItem.getDomain()), CommonErrorCode.PARAM_ERROR);
BusinessAssert.isTrue(StringUtils.isNotBlank(knowledgeItem.getBusinessLine()), CommonErrorCode.PARAM_ERROR);
BusinessAssert.isTrue(StringUtils.isNotBlank(knowledgeItem.getOwner()), CommonErrorCode.PARAM_ERROR);
BusinessAssert.isTrue(StringUtils.isNotBlank(knowledgeItem.getSensitivity()), CommonErrorCode.PARAM_ERROR);
BusinessAssert.notNull(knowledgeItem.getSourceType(), CommonErrorCode.PARAM_ERROR);
BusinessAssert.notNull(knowledgeItem.getValidFrom(), CommonErrorCode.PARAM_ERROR);
BusinessAssert.notNull(knowledgeItem.getValidTo(), CommonErrorCode.PARAM_ERROR);
}
private void validateEffectivePeriod(LocalDate validFrom, LocalDate validTo) {
if (validFrom == null || validTo == null) {
return;
}
BusinessAssert.isTrue(!validFrom.isAfter(validTo), CommonErrorCode.PARAM_ERROR);
}
private Set<Tag> processTagNames(List<String> tagNames) {
if (CollectionUtils.isEmpty(tagNames)) {
return new HashSet<>();
}
Set<Tag> tags = new HashSet<>();
for (String tagName : tagNames) {
if (StringUtils.isBlank(tagName)) {
continue;
}
Tag tag = tagMapper.findByName(tagName);
if (tag == null) {
Tag newTag = new Tag(tagName, null, null, "#007bff");
newTag.setUsageCount(0L);
newTag.setId(UUID.randomUUID().toString());
tagMapper.insert(newTag);
tag = newTag;
}
tag.setUsageCount(tag.getUsageCount() == null ? 1L : tag.getUsageCount() + 1);
tagMapper.updateUsageCount(tag.getId(), tag.getUsageCount());
tags.add(tag);
}
return tags;
}
}

View File

@@ -0,0 +1,187 @@
package com.datamate.datamanagement.application;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datamate.common.infrastructure.exception.BusinessAssert;
import com.datamate.common.infrastructure.exception.CommonErrorCode;
import com.datamate.common.interfaces.PagedResponse;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import com.datamate.datamanagement.domain.model.dataset.Tag;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
import com.datamate.datamanagement.infrastructure.exception.DataManagementErrorCode;
import com.datamate.datamanagement.infrastructure.persistence.mapper.TagMapper;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
import com.datamate.datamanagement.interfaces.converter.KnowledgeConverter;
import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeSetRequest;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetResponse;
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeSetRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
* 知识集应用服务
*/
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class KnowledgeSetApplicationService {
private final KnowledgeSetRepository knowledgeSetRepository;
private final TagMapper tagMapper;
public KnowledgeSet createKnowledgeSet(CreateKnowledgeSetRequest request) {
BusinessAssert.isTrue(knowledgeSetRepository.findByName(request.getName()) == null,
DataManagementErrorCode.KNOWLEDGE_SET_ALREADY_EXISTS);
KnowledgeSet knowledgeSet = KnowledgeConverter.INSTANCE.convertToKnowledgeSet(request);
knowledgeSet.setId(UUID.randomUUID().toString());
if (knowledgeSet.getStatus() == null) {
knowledgeSet.setStatus(KnowledgeStatusType.DRAFT);
}
Set<Tag> tags = processTagNames(request.getTags());
knowledgeSet.setTags(tags);
validateEffectivePeriod(knowledgeSet.getValidFrom(), knowledgeSet.getValidTo());
validatePublishable(knowledgeSet);
knowledgeSetRepository.save(knowledgeSet);
return knowledgeSet;
}
public KnowledgeSet updateKnowledgeSet(String setId, UpdateKnowledgeSetRequest request) {
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);
if (StringUtils.hasText(request.getName()) && !Objects.equals(request.getName(), knowledgeSet.getName())) {
KnowledgeSet exist = knowledgeSetRepository.findByName(request.getName());
BusinessAssert.isTrue(exist == null || Objects.equals(exist.getId(), setId),
DataManagementErrorCode.KNOWLEDGE_SET_ALREADY_EXISTS);
knowledgeSet.setName(request.getName());
}
if (request.getDescription() != null) {
knowledgeSet.setDescription(request.getDescription());
}
if (request.getTags() != null) {
knowledgeSet.setTags(processTagNames(request.getTags()));
}
if (request.getStatus() != null) {
knowledgeSet.setStatus(request.getStatus());
}
if (request.getDomain() != null) {
knowledgeSet.setDomain(request.getDomain());
}
if (request.getBusinessLine() != null) {
knowledgeSet.setBusinessLine(request.getBusinessLine());
}
if (request.getOwner() != null) {
knowledgeSet.setOwner(request.getOwner());
}
if (request.getValidFrom() != null || request.getValidTo() != null) {
knowledgeSet.setValidFrom(request.getValidFrom());
knowledgeSet.setValidTo(request.getValidTo());
}
if (request.getSourceType() != null) {
knowledgeSet.setSourceType(request.getSourceType());
}
if (request.getSensitivity() != null) {
knowledgeSet.setSensitivity(request.getSensitivity());
}
if (request.getMetadata() != null) {
knowledgeSet.setMetadata(request.getMetadata());
}
validateEffectivePeriod(knowledgeSet.getValidFrom(), knowledgeSet.getValidTo());
validatePublishable(knowledgeSet);
knowledgeSetRepository.updateById(knowledgeSet);
return knowledgeSet;
}
public void deleteKnowledgeSet(String setId) {
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
knowledgeSetRepository.removeById(setId);
}
@Transactional(readOnly = true)
public KnowledgeSet getKnowledgeSet(String setId) {
KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
return knowledgeSet;
}
@Transactional(readOnly = true)
public PagedResponse<KnowledgeSetResponse> getKnowledgeSets(KnowledgeSetPagingQuery query) {
IPage<KnowledgeSet> page = new Page<>(query.getPage(), query.getSize());
page = knowledgeSetRepository.findByCriteria(page, query);
List<KnowledgeSetResponse> responses = KnowledgeConverter.INSTANCE.convertSetResponses(page.getRecords());
return PagedResponse.of(responses, page.getCurrent(), page.getTotal(), page.getPages());
}
private boolean isReadOnlyStatus(KnowledgeStatusType status) {
return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
}
private void validatePublishable(KnowledgeSet knowledgeSet) {
if (knowledgeSet.getStatus() != KnowledgeStatusType.PUBLISHED) {
return;
}
BusinessAssert.isTrue(StringUtils.hasText(knowledgeSet.getDomain()), CommonErrorCode.PARAM_ERROR);
BusinessAssert.isTrue(StringUtils.hasText(knowledgeSet.getBusinessLine()), CommonErrorCode.PARAM_ERROR);
BusinessAssert.isTrue(StringUtils.hasText(knowledgeSet.getOwner()), CommonErrorCode.PARAM_ERROR);
BusinessAssert.isTrue(StringUtils.hasText(knowledgeSet.getSensitivity()), CommonErrorCode.PARAM_ERROR);
BusinessAssert.notNull(knowledgeSet.getSourceType(), CommonErrorCode.PARAM_ERROR);
BusinessAssert.notNull(knowledgeSet.getValidFrom(), CommonErrorCode.PARAM_ERROR);
BusinessAssert.notNull(knowledgeSet.getValidTo(), CommonErrorCode.PARAM_ERROR);
}
private void validateEffectivePeriod(LocalDate validFrom, LocalDate validTo) {
if (validFrom == null || validTo == null) {
return;
}
BusinessAssert.isTrue(!validFrom.isAfter(validTo), CommonErrorCode.PARAM_ERROR);
}
private Set<Tag> processTagNames(List<String> tagNames) {
if (CollectionUtils.isEmpty(tagNames)) {
return new HashSet<>();
}
Set<Tag> tags = new HashSet<>();
for (String tagName : tagNames) {
if (!StringUtils.hasText(tagName)) {
continue;
}
Tag tag = tagMapper.findByName(tagName);
if (tag == null) {
Tag newTag = new Tag(tagName, null, null, "#007bff");
newTag.setUsageCount(0L);
newTag.setId(UUID.randomUUID().toString());
tagMapper.insert(newTag);
tag = newTag;
}
tag.setUsageCount(tag.getUsageCount() == null ? 1L : tag.getUsageCount() + 1);
tagMapper.updateUsageCount(tag.getId(), tag.getUsageCount());
tags.add(tag);
}
return tags;
}
}

View File

@@ -0,0 +1,15 @@
package com.datamate.datamanagement.common.enums;
/**
* 知识内容类型
*/
public enum KnowledgeContentType {
/**
* 纯文本
*/
TEXT,
/**
* Markdown
*/
MARKDOWN
}

View File

@@ -0,0 +1,15 @@
package com.datamate.datamanagement.common.enums;
/**
* 知识来源类型
*/
public enum KnowledgeSourceType {
/**
* 手工录入
*/
MANUAL,
/**
* 数据集文件导入
*/
DATASET_FILE
}

View File

@@ -0,0 +1,23 @@
package com.datamate.datamanagement.common.enums;
/**
* 知识状态类型
*/
public enum KnowledgeStatusType {
/**
* 草稿状态
*/
DRAFT,
/**
* 已发布状态
*/
PUBLISHED,
/**
* 已归档状态
*/
ARCHIVED,
/**
* 已废弃状态
*/
DEPRECATED
}

View File

@@ -0,0 +1,90 @@
package com.datamate.datamanagement.domain.model.knowledge;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.datamate.common.domain.model.base.BaseEntity;
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import com.datamate.datamanagement.domain.model.dataset.Tag;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
import java.util.Collection;
import java.util.HashSet;
/**
* 知识条目实体(与数据库表 t_dm_knowledge_items 对齐)
*/
@Getter
@Setter
@TableName(value = "t_dm_knowledge_items", autoResultMap = true)
public class KnowledgeItem extends BaseEntity<String> {
/**
* 所属知识集ID
*/
private String setId;
/**
* 标题
*/
private String title;
/**
* 内容
*/
private String content;
/**
* 内容类型
*/
private KnowledgeContentType contentType;
/**
* 状态
*/
private KnowledgeStatusType status;
/**
* 领域
*/
private String domain;
/**
* 业务线
*/
private String businessLine;
/**
* 负责人
*/
private String owner;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
/**
* 来源类型
*/
private KnowledgeSourceType sourceType;
/**
* 敏感级别
*/
private String sensitivity;
/**
* 来源数据集ID
*/
private String sourceDatasetId;
/**
* 来源文件ID
*/
private String sourceFileId;
/**
* 标签列表
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Collection<Tag> tags = new HashSet<>();
/**
* 扩展元数据
*/
private String metadata;
}

View File

@@ -0,0 +1,73 @@
package com.datamate.datamanagement.domain.model.knowledge;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.datamate.common.domain.model.base.BaseEntity;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import com.datamate.datamanagement.domain.model.dataset.Tag;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
import java.util.Collection;
import java.util.HashSet;
/**
* 知识集实体(与数据库表 t_dm_knowledge_sets 对齐)
*/
@Getter
@Setter
@TableName(value = "t_dm_knowledge_sets", autoResultMap = true)
public class KnowledgeSet extends BaseEntity<String> {
/**
* 知识集名称
*/
private String name;
/**
* 知识集描述
*/
private String description;
/**
* 知识集状态
*/
private KnowledgeStatusType status;
/**
* 领域
*/
private String domain;
/**
* 业务线
*/
private String businessLine;
/**
* 负责人
*/
private String owner;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
/**
* 来源类型
*/
private KnowledgeSourceType sourceType;
/**
* 敏感级别
*/
private String sensitivity;
/**
* 标签列表
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Collection<Tag> tags = new HashSet<>();
/**
* 扩展元数据
*/
private String metadata;
}

View File

@@ -44,7 +44,35 @@ public enum DataManagementErrorCode implements ErrorCode {
/**
* 存在子数据集
*/
DATASET_HAS_CHILDREN("data_management.0008", "存在子数据集,禁止删除或移动");
DATASET_HAS_CHILDREN("data_management.0008", "存在子数据集,禁止删除或移动"),
/**
* 数据集文件不存在
*/
DATASET_FILE_NOT_FOUND("data_management.0009", "数据集文件不存在"),
/**
* 知识集不存在
*/
KNOWLEDGE_SET_NOT_FOUND("data_management.0101", "知识集不存在"),
/**
* 知识集已存在
*/
KNOWLEDGE_SET_ALREADY_EXISTS("data_management.0102", "知识集已存在"),
/**
* 知识集状态不可修改
*/
KNOWLEDGE_SET_STATUS_ERROR("data_management.0103", "知识集状态不可修改"),
/**
* 知识条目不存在
*/
KNOWLEDGE_ITEM_NOT_FOUND("data_management.0104", "知识条目不存在"),
/**
* 知识条目状态不可修改
*/
KNOWLEDGE_ITEM_STATUS_ERROR("data_management.0105", "知识条目状态不可修改"),
/**
* 知识条目来源不支持
*/
KNOWLEDGE_ITEM_SOURCE_NOT_SUPPORTED("data_management.0106", "知识条目来源不支持");
private final String code;
private final String message;

View File

@@ -0,0 +1,9 @@
package com.datamate.datamanagement.infrastructure.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface KnowledgeItemMapper extends BaseMapper<KnowledgeItem> {
}

View File

@@ -0,0 +1,9 @@
package com.datamate.datamanagement.infrastructure.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface KnowledgeSetMapper extends BaseMapper<KnowledgeSet> {
}

View File

@@ -0,0 +1,15 @@
package com.datamate.datamanagement.infrastructure.persistence.repository;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.repository.IRepository;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery;
/**
* 知识条目仓储接口
*/
public interface KnowledgeItemRepository extends IRepository<KnowledgeItem> {
IPage<KnowledgeItem> findByCriteria(IPage<KnowledgeItem> page, KnowledgeItemPagingQuery query);
long countBySetId(String setId);
}

View File

@@ -0,0 +1,15 @@
package com.datamate.datamanagement.infrastructure.persistence.repository;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.repository.IRepository;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
/**
* 知识集仓储接口
*/
public interface KnowledgeSetRepository extends IRepository<KnowledgeSet> {
KnowledgeSet findByName(String name);
IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query);
}

View File

@@ -0,0 +1,60 @@
package com.datamate.datamanagement.infrastructure.persistence.repository.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.repository.CrudRepository;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
import com.datamate.datamanagement.infrastructure.persistence.mapper.KnowledgeItemMapper;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
/**
* 知识条目仓储实现类
*/
@Repository
@RequiredArgsConstructor
public class KnowledgeItemRepositoryImpl extends CrudRepository<KnowledgeItemMapper, KnowledgeItem> implements KnowledgeItemRepository {
private final KnowledgeItemMapper knowledgeItemMapper;
@Override
public IPage<KnowledgeItem> findByCriteria(IPage<KnowledgeItem> page, KnowledgeItemPagingQuery query) {
LambdaQueryWrapper<KnowledgeItem> wrapper = new LambdaQueryWrapper<KnowledgeItem>()
.eq(StringUtils.isNotBlank(query.getSetId()), KnowledgeItem::getSetId, query.getSetId())
.eq(query.getStatus() != null, KnowledgeItem::getStatus, query.getStatus())
.eq(query.getContentType() != null, KnowledgeItem::getContentType, query.getContentType())
.eq(StringUtils.isNotBlank(query.getDomain()), KnowledgeItem::getDomain, query.getDomain())
.eq(StringUtils.isNotBlank(query.getBusinessLine()), KnowledgeItem::getBusinessLine, query.getBusinessLine())
.eq(StringUtils.isNotBlank(query.getOwner()), KnowledgeItem::getOwner, query.getOwner())
.eq(StringUtils.isNotBlank(query.getSensitivity()), KnowledgeItem::getSensitivity, query.getSensitivity())
.eq(query.getSourceType() != null, KnowledgeItem::getSourceType, query.getSourceType())
.ge(query.getValidFrom() != null, KnowledgeItem::getValidFrom, query.getValidFrom())
.le(query.getValidTo() != null, KnowledgeItem::getValidTo, query.getValidTo());
if (StringUtils.isNotBlank(query.getKeyword())) {
wrapper.and(w -> w.like(KnowledgeItem::getTitle, query.getKeyword())
.or()
.like(KnowledgeItem::getContent, query.getKeyword()));
}
for (String tagName : query.getTags()) {
wrapper.and(w ->
w.apply("tags IS NOT NULL " +
"AND JSON_VALID(tags) = 1 " +
"AND JSON_LENGTH(tags) > 0 " +
"AND JSON_SEARCH(tags, 'one', {0}, NULL, '$[*].name') IS NOT NULL", tagName)
);
}
wrapper.orderByDesc(KnowledgeItem::getCreatedAt);
return knowledgeItemMapper.selectPage(page, wrapper);
}
@Override
public long countBySetId(String setId) {
return knowledgeItemMapper.selectCount(new LambdaQueryWrapper<KnowledgeItem>()
.eq(KnowledgeItem::getSetId, setId));
}
}

View File

@@ -0,0 +1,57 @@
package com.datamate.datamanagement.infrastructure.persistence.repository.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.repository.CrudRepository;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
import com.datamate.datamanagement.infrastructure.persistence.mapper.KnowledgeSetMapper;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
/**
* 知识集仓储实现类
*/
@Repository
@RequiredArgsConstructor
public class KnowledgeSetRepositoryImpl extends CrudRepository<KnowledgeSetMapper, KnowledgeSet> implements KnowledgeSetRepository {
private final KnowledgeSetMapper knowledgeSetMapper;
@Override
public KnowledgeSet findByName(String name) {
return knowledgeSetMapper.selectOne(new LambdaQueryWrapper<KnowledgeSet>().eq(KnowledgeSet::getName, name));
}
@Override
public IPage<KnowledgeSet> findByCriteria(IPage<KnowledgeSet> page, KnowledgeSetPagingQuery query) {
LambdaQueryWrapper<KnowledgeSet> wrapper = new LambdaQueryWrapper<KnowledgeSet>()
.eq(query.getStatus() != null, KnowledgeSet::getStatus, query.getStatus())
.eq(StringUtils.isNotBlank(query.getDomain()), KnowledgeSet::getDomain, query.getDomain())
.eq(StringUtils.isNotBlank(query.getBusinessLine()), KnowledgeSet::getBusinessLine, query.getBusinessLine())
.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());
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()) {
wrapper.and(w ->
w.apply("tags IS NOT NULL " +
"AND JSON_VALID(tags) = 1 " +
"AND JSON_LENGTH(tags) > 0 " +
"AND JSON_SEARCH(tags, 'one', {0}, NULL, '$[*].name') IS NOT NULL", tagName)
);
}
wrapper.orderByDesc(KnowledgeSet::getCreatedAt);
return knowledgeSetMapper.selectPage(page, wrapper);
}
}

View File

@@ -0,0 +1,35 @@
package com.datamate.datamanagement.interfaces.converter;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeItemRequest;
import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeSetRequest;
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemResponse;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetResponse;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 知识管理转换器
*/
@Mapper
public interface KnowledgeConverter {
KnowledgeConverter INSTANCE = Mappers.getMapper(KnowledgeConverter.class);
@Mapping(target = "tags", ignore = true)
KnowledgeSet convertToKnowledgeSet(CreateKnowledgeSetRequest request);
@Mapping(target = "tags", ignore = true)
KnowledgeItem convertToKnowledgeItem(CreateKnowledgeItemRequest request);
KnowledgeSetResponse convertToResponse(KnowledgeSet knowledgeSet);
List<KnowledgeSetResponse> convertSetResponses(List<KnowledgeSet> sets);
KnowledgeItemResponse convertToResponse(KnowledgeItem knowledgeItem);
List<KnowledgeItemResponse> convertItemResponses(List<KnowledgeItem> items);
}

View File

@@ -0,0 +1,80 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDate;
import java.util.List;
/**
* 创建知识条目请求DTO
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class CreateKnowledgeItemRequest {
/**
* 标题
*/
@Size(min = 1, max = 200)
@NotBlank(message = "标题不能为空")
private String title;
/**
* 内容
*/
@NotBlank(message = "内容不能为空")
private String content;
/**
* 内容类型
*/
@NotNull(message = "内容类型不能为空")
private KnowledgeContentType contentType;
/**
* 状态
*/
private KnowledgeStatusType status;
/**
* 标签列表
*/
private List<String> tags;
/**
* 领域
*/
@Size(max = 100)
private String domain;
/**
* 业务线
*/
@Size(max = 100)
private String businessLine;
/**
* 负责人
*/
@Size(max = 100)
private String owner;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
/**
* 敏感级别
*/
@Size(max = 50)
private String sensitivity;
/**
* 扩展元数据
*/
private String metadata;
}

View File

@@ -0,0 +1,78 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDate;
import java.util.List;
/**
* 创建知识集请求DTO
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class CreateKnowledgeSetRequest {
/**
* 知识集名称
*/
@Size(min = 1, max = 100)
@NotBlank(message = "知识集名称不能为空")
private String name;
/**
* 知识集描述
*/
@Size(max = 1000)
private String description;
/**
* 标签列表
*/
private List<String> tags;
/**
* 状态
*/
private KnowledgeStatusType status;
/**
* 领域
*/
@Size(max = 100)
private String domain;
/**
* 业务线
*/
@Size(max = 100)
private String businessLine;
/**
* 负责人
*/
@Size(max = 100)
private String owner;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
/**
* 来源类型
*/
private KnowledgeSourceType sourceType;
/**
* 敏感级别
*/
@Size(max = 50)
private String sensitivity;
/**
* 扩展元数据
*/
private String metadata;
}

View File

@@ -0,0 +1,69 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
import java.util.List;
/**
* 导入知识条目请求DTO(基于数据集文件)
*/
@Getter
@Setter
public class ImportKnowledgeItemsRequest {
/**
* 数据集ID
*/
@NotBlank(message = "数据集ID不能为空")
private String datasetId;
/**
* 文件ID列表
*/
@NotEmpty(message = "文件列表不能为空")
private List<String> fileIds;
/**
* 状态
*/
private KnowledgeStatusType status;
/**
* 标签列表
*/
private List<String> tags;
/**
* 领域
*/
@Size(max = 100)
private String domain;
/**
* 业务线
*/
@Size(max = 100)
private String businessLine;
/**
* 负责人
*/
@Size(max = 100)
private String owner;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
/**
* 敏感级别
*/
@Size(max = 50)
private String sensitivity;
/**
* 扩展元数据
*/
private String metadata;
}

View File

@@ -0,0 +1,72 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.common.interfaces.PagingQuery;
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
/**
* 知识条目分页查询请求
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class KnowledgeItemPagingQuery extends PagingQuery {
/**
* 所属知识集ID
*/
private String setId;
/**
* 标签名过滤
*/
private List<String> tags = new ArrayList<>();
/**
* 关键词搜索(标题或内容)
*/
private String keyword;
/**
* 状态过滤
*/
private KnowledgeStatusType status;
/**
* 内容类型过滤
*/
private KnowledgeContentType contentType;
/**
* 领域
*/
private String domain;
/**
* 业务线
*/
private String businessLine;
/**
* 负责人
*/
private String owner;
/**
* 敏感级别
*/
private String sensitivity;
/**
* 来源类型
*/
private KnowledgeSourceType sourceType;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
}

View File

@@ -0,0 +1,40 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* 知识条目响应DTO
*/
@Getter
@Setter
public class KnowledgeItemResponse {
private String id;
private String setId;
private String title;
private String content;
private KnowledgeContentType contentType;
private KnowledgeStatusType status;
private List<TagResponse> tags;
private String domain;
private String businessLine;
private String owner;
private LocalDate validFrom;
private LocalDate validTo;
private KnowledgeSourceType sourceType;
private String sensitivity;
private String sourceDatasetId;
private String sourceFileId;
private String metadata;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String createdBy;
private String updatedBy;
}

View File

@@ -0,0 +1,63 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.common.interfaces.PagingQuery;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
/**
* 知识集分页查询请求
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class KnowledgeSetPagingQuery extends PagingQuery {
/**
* 标签名过滤
*/
private List<String> tags = new ArrayList<>();
/**
* 关键词搜索(名称或描述)
*/
private String keyword;
/**
* 状态过滤
*/
private KnowledgeStatusType status;
/**
* 领域
*/
private String domain;
/**
* 业务线
*/
private String businessLine;
/**
* 负责人
*/
private String owner;
/**
* 敏感级别
*/
private String sensitivity;
/**
* 来源类型
*/
private KnowledgeSourceType sourceType;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
}

View File

@@ -0,0 +1,35 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* 知识集响应DTO
*/
@Getter
@Setter
public class KnowledgeSetResponse {
private String id;
private String name;
private String description;
private KnowledgeStatusType status;
private List<TagResponse> tags;
private String domain;
private String businessLine;
private String owner;
private LocalDate validFrom;
private LocalDate validTo;
private KnowledgeSourceType sourceType;
private String sensitivity;
private String metadata;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String createdBy;
private String updatedBy;
}

View File

@@ -0,0 +1,71 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.datamanagement.common.enums.KnowledgeContentType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
import java.util.List;
/**
* 更新知识条目请求DTO
*/
@Getter
@Setter
public class UpdateKnowledgeItemRequest {
/**
* 标题
*/
@Size(min = 1, max = 200)
private String title;
/**
* 内容
*/
private String content;
/**
* 内容类型
*/
private KnowledgeContentType contentType;
/**
* 状态
*/
private KnowledgeStatusType status;
/**
* 标签列表
*/
private List<String> tags;
/**
* 领域
*/
@Size(max = 100)
private String domain;
/**
* 业务线
*/
@Size(max = 100)
private String businessLine;
/**
* 负责人
*/
@Size(max = 100)
private String owner;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
/**
* 敏感级别
*/
@Size(max = 50)
private String sensitivity;
/**
* 扩展元数据
*/
private String metadata;
}

View File

@@ -0,0 +1,74 @@
package com.datamate.datamanagement.interfaces.dto;
import com.datamate.datamanagement.common.enums.KnowledgeSourceType;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
import java.util.List;
/**
* 更新知识集请求DTO
*/
@Getter
@Setter
public class UpdateKnowledgeSetRequest {
/**
* 知识集名称
*/
@Size(min = 1, max = 100)
@NotBlank(message = "知识集名称不能为空")
private String name;
/**
* 知识集描述
*/
@Size(max = 1000)
private String description;
/**
* 标签列表
*/
private List<String> tags;
/**
* 状态
*/
private KnowledgeStatusType status;
/**
* 领域
*/
@Size(max = 100)
private String domain;
/**
* 业务线
*/
@Size(max = 100)
private String businessLine;
/**
* 负责人
*/
@Size(max = 100)
private String owner;
/**
* 有效期开始
*/
private LocalDate validFrom;
/**
* 有效期结束
*/
private LocalDate validTo;
/**
* 来源类型
*/
private KnowledgeSourceType sourceType;
/**
* 敏感级别
*/
@Size(max = 50)
private String sensitivity;
/**
* 扩展元数据
*/
private String metadata;
}

View File

@@ -0,0 +1,69 @@
package com.datamate.datamanagement.interfaces.rest;
import com.datamate.common.interfaces.PagedResponse;
import com.datamate.datamanagement.application.KnowledgeItemApplicationService;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
import com.datamate.datamanagement.interfaces.converter.KnowledgeConverter;
import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeItemRequest;
import com.datamate.datamanagement.interfaces.dto.ImportKnowledgeItemsRequest;
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery;
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemResponse;
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeItemRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 知识条目 REST 控制器
*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/data-management/knowledge-sets/{setId}/items")
public class KnowledgeItemController {
private final KnowledgeItemApplicationService knowledgeItemApplicationService;
@GetMapping
public PagedResponse<KnowledgeItemResponse> getKnowledgeItems(@PathVariable("setId") String setId,
KnowledgeItemPagingQuery query) {
return knowledgeItemApplicationService.getKnowledgeItems(setId, query);
}
@PostMapping
public KnowledgeItemResponse createKnowledgeItem(@PathVariable("setId") String setId,
@RequestBody @Valid CreateKnowledgeItemRequest request) {
KnowledgeItem knowledgeItem = knowledgeItemApplicationService.createKnowledgeItem(setId, request);
return KnowledgeConverter.INSTANCE.convertToResponse(knowledgeItem);
}
@PostMapping("/import")
public List<KnowledgeItemResponse> importKnowledgeItems(@PathVariable("setId") String setId,
@RequestBody @Valid ImportKnowledgeItemsRequest request) {
List<KnowledgeItem> items = knowledgeItemApplicationService.importKnowledgeItems(setId, request);
return KnowledgeConverter.INSTANCE.convertItemResponses(items);
}
@GetMapping("/{itemId}")
public KnowledgeItemResponse getKnowledgeItemById(@PathVariable("setId") String setId,
@PathVariable("itemId") String itemId) {
KnowledgeItem knowledgeItem = knowledgeItemApplicationService.getKnowledgeItem(setId, itemId);
return KnowledgeConverter.INSTANCE.convertToResponse(knowledgeItem);
}
@PutMapping("/{itemId}")
public KnowledgeItemResponse updateKnowledgeItem(@PathVariable("setId") String setId,
@PathVariable("itemId") String itemId,
@RequestBody @Valid UpdateKnowledgeItemRequest request) {
KnowledgeItem knowledgeItem = knowledgeItemApplicationService.updateKnowledgeItem(setId, itemId, request);
return KnowledgeConverter.INSTANCE.convertToResponse(knowledgeItem);
}
@DeleteMapping("/{itemId}")
public void deleteKnowledgeItem(@PathVariable("setId") String setId,
@PathVariable("itemId") String itemId) {
knowledgeItemApplicationService.deleteKnowledgeItem(setId, itemId);
}
}

View File

@@ -0,0 +1,54 @@
package com.datamate.datamanagement.interfaces.rest;
import com.datamate.common.interfaces.PagedResponse;
import com.datamate.datamanagement.application.KnowledgeSetApplicationService;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
import com.datamate.datamanagement.interfaces.converter.KnowledgeConverter;
import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeSetRequest;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetResponse;
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeSetRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 知识集 REST 控制器
*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/data-management/knowledge-sets")
public class KnowledgeSetController {
private final KnowledgeSetApplicationService knowledgeSetApplicationService;
@GetMapping
public PagedResponse<KnowledgeSetResponse> getKnowledgeSets(KnowledgeSetPagingQuery query) {
return knowledgeSetApplicationService.getKnowledgeSets(query);
}
@PostMapping
public KnowledgeSetResponse createKnowledgeSet(@RequestBody @Valid CreateKnowledgeSetRequest request) {
KnowledgeSet knowledgeSet = knowledgeSetApplicationService.createKnowledgeSet(request);
return KnowledgeConverter.INSTANCE.convertToResponse(knowledgeSet);
}
@GetMapping("/{setId}")
public KnowledgeSetResponse getKnowledgeSetById(@PathVariable("setId") String setId) {
KnowledgeSet knowledgeSet = knowledgeSetApplicationService.getKnowledgeSet(setId);
return KnowledgeConverter.INSTANCE.convertToResponse(knowledgeSet);
}
@PutMapping("/{setId}")
public KnowledgeSetResponse updateKnowledgeSet(@PathVariable("setId") String setId,
@RequestBody @Valid UpdateKnowledgeSetRequest request) {
KnowledgeSet knowledgeSet = knowledgeSetApplicationService.updateKnowledgeSet(setId, request);
return KnowledgeConverter.INSTANCE.convertToResponse(knowledgeSet);
}
@DeleteMapping("/{setId}")
public void deleteKnowledgeSet(@PathVariable("setId") String setId) {
knowledgeSetApplicationService.deleteKnowledgeSet(setId);
}
}