feat: 重构知识库文档导入任务化流程
- 新增上传建单、异步解析、分块处理与异步向量化闭环 - 收口分享页权限、完成态检索过滤与 SSE 局部状态刷新
This commit is contained in:
@@ -146,10 +146,14 @@ public class DocumentCollectionController extends BaseCurdController<DocumentCol
|
||||
)
|
||||
public Result<List<KnowledgeSearchResultItem>> search(@RequestParam BigInteger knowledgeId,
|
||||
@RequestParam String keyword,
|
||||
@RequestParam(required = false) String retrievalMode) {
|
||||
@RequestParam(required = false) String retrievalMode,
|
||||
@RequestParam(required = false) Integer docRecallMaxNum,
|
||||
@RequestParam(required = false) Double simThreshold) {
|
||||
KnowledgeRetrievalRequest request = new KnowledgeRetrievalRequest();
|
||||
request.setKnowledgeId(knowledgeId);
|
||||
request.setQuery(keyword);
|
||||
request.setLimit(docRecallMaxNum);
|
||||
request.setMinSimilarity(simThreshold);
|
||||
request.setRetrievalMode(KnowledgeRetrievalModes.parse(retrievalMode));
|
||||
request.setCallerType("API");
|
||||
request.setCallerId(String.valueOf(knowledgeId));
|
||||
|
||||
@@ -8,9 +8,12 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import tech.easyflow.ai.documentimport.DocumentImportDtos;
|
||||
import tech.easyflow.ai.documentimport.task.DocumentImportTaskStatusStreamService;
|
||||
import tech.easyflow.ai.entity.Document;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.entity.DocumentCollectionSplitParams;
|
||||
@@ -77,6 +80,9 @@ public class DocumentController extends BaseCurdController<DocumentService, Docu
|
||||
@Autowired
|
||||
private ResourceAccessService resourceAccessService;
|
||||
|
||||
@Autowired
|
||||
private DocumentImportTaskStatusStreamService documentImportTaskStatusStreamService;
|
||||
|
||||
@Value("${easyflow.storage.local.root:}")
|
||||
private String fileUploadPath;
|
||||
|
||||
@@ -233,6 +239,79 @@ public class DocumentController extends BaseCurdController<DocumentService, Docu
|
||||
return documentService.commitImport(request);
|
||||
}
|
||||
|
||||
@PostMapping("import/task/create")
|
||||
@SaCheckPermission("/api/v1/documentCollection/save")
|
||||
public Result<DocumentImportDtos.TaskCreateResponse> createImportTask(@JsonBody DocumentImportDtos.TaskCreateRequest request) {
|
||||
if (request.getKnowledgeId() == null) {
|
||||
throw new BusinessException("知识库id不能为空");
|
||||
}
|
||||
getDocumentCollection(request.getKnowledgeId().toString(), ResourceAction.MANAGE, "无权限管理知识库");
|
||||
return documentService.createImportTask(request);
|
||||
}
|
||||
|
||||
@GetMapping("import/task/detail")
|
||||
@SaCheckPermission("/api/v1/documentCollection/query")
|
||||
public Result<DocumentImportDtos.TaskDetailResponse> getImportTaskDetail(@RequestParam BigInteger taskId) {
|
||||
Result<DocumentImportDtos.TaskDetailResponse> result = documentService.getImportTaskDetail(taskId);
|
||||
if (result.getData() != null && result.getData().getKnowledgeId() != null) {
|
||||
getDocumentCollection(result.getData().getKnowledgeId().toString(), ResourceAction.READ, "无权限访问知识库");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅知识库文档任务状态流。
|
||||
*
|
||||
* @param knowledgeId 知识库 ID
|
||||
* @return SSE 推送连接
|
||||
*/
|
||||
@PostMapping(value = "import/task/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
@SaCheckPermission("/api/v1/documentCollection/query")
|
||||
public SseEmitter streamImportTask(@JsonBody(value = "knowledgeId", required = true) BigInteger knowledgeId) {
|
||||
getDocumentCollection(knowledgeId.toString(), ResourceAction.READ, "无权限访问知识库");
|
||||
return documentImportTaskStatusStreamService.subscribe(knowledgeId);
|
||||
}
|
||||
|
||||
@PostMapping("import/task/preview")
|
||||
@SaCheckPermission("/api/v1/documentCollection/save")
|
||||
public Result<DocumentImportDtos.PreviewResponse> previewImportTask(@JsonBody DocumentImportDtos.PreviewRequest request) {
|
||||
if (request.getKnowledgeId() == null) {
|
||||
throw new BusinessException("知识库id不能为空");
|
||||
}
|
||||
getDocumentCollection(request.getKnowledgeId().toString(), ResourceAction.MANAGE, "无权限管理知识库");
|
||||
return documentService.previewImportTask(request);
|
||||
}
|
||||
|
||||
@PostMapping("import/task/startIndex")
|
||||
@SaCheckPermission("/api/v1/documentCollection/save")
|
||||
public Result<DocumentImportDtos.TaskStartIndexResponse> startIndexTask(@JsonBody DocumentImportDtos.TaskStartIndexRequest request) {
|
||||
if (request.getKnowledgeId() == null) {
|
||||
throw new BusinessException("知识库id不能为空");
|
||||
}
|
||||
getDocumentCollection(request.getKnowledgeId().toString(), ResourceAction.MANAGE, "无权限管理知识库");
|
||||
return documentService.startIndexTask(request);
|
||||
}
|
||||
|
||||
@PostMapping("import/task/retryParse")
|
||||
@SaCheckPermission("/api/v1/documentCollection/save")
|
||||
public Result<DocumentImportDtos.TaskStartIndexResponse> retryParseTask(@JsonBody DocumentImportDtos.TaskRetryRequest request) {
|
||||
if (request.getKnowledgeId() == null) {
|
||||
throw new BusinessException("知识库id不能为空");
|
||||
}
|
||||
getDocumentCollection(request.getKnowledgeId().toString(), ResourceAction.MANAGE, "无权限管理知识库");
|
||||
return documentService.retryParseTask(request);
|
||||
}
|
||||
|
||||
@PostMapping("import/task/retryIndex")
|
||||
@SaCheckPermission("/api/v1/documentCollection/save")
|
||||
public Result<DocumentImportDtos.TaskStartIndexResponse> retryIndexTask(@JsonBody DocumentImportDtos.TaskRetryRequest request) {
|
||||
if (request.getKnowledgeId() == null) {
|
||||
throw new BusinessException("知识库id不能为空");
|
||||
}
|
||||
getDocumentCollection(request.getKnowledgeId().toString(), ResourceAction.MANAGE, "无权限管理知识库");
|
||||
return documentService.retryIndexTask(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 entity
|
||||
*
|
||||
|
||||
@@ -17,8 +17,10 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import tech.easyflow.ai.documentimport.DocumentImportDtos;
|
||||
import tech.easyflow.ai.documentimport.task.DocumentImportTaskStatusStreamService;
|
||||
import tech.easyflow.ai.dto.KnowledgeShareLimitedConfigRequest;
|
||||
import tech.easyflow.ai.dto.KnowledgeSearchResultItem;
|
||||
import tech.easyflow.ai.entity.Document;
|
||||
@@ -42,6 +44,7 @@ import tech.easyflow.ai.service.KnowledgeShareService;
|
||||
import tech.easyflow.ai.service.ModelService;
|
||||
import tech.easyflow.ai.vo.FaqImportResultVo;
|
||||
import tech.easyflow.ai.vo.KnowledgeShareAuthContext;
|
||||
import tech.easyflow.ai.vo.KnowledgeShareViewDetail;
|
||||
import tech.easyflow.common.domain.Result;
|
||||
import tech.easyflow.common.filestorage.FileStorageService;
|
||||
import tech.easyflow.common.vo.UploadResVo;
|
||||
@@ -99,6 +102,8 @@ public class ShareKnowledgeController {
|
||||
private KnowledgeEmbeddingService knowledgeEmbeddingService;
|
||||
@Resource(name = "default")
|
||||
private FileStorageService fileStorageService;
|
||||
@Resource
|
||||
private DocumentImportTaskStatusStreamService documentImportTaskStatusStreamService;
|
||||
|
||||
/**
|
||||
* 获取知识库详情。
|
||||
@@ -107,14 +112,17 @@ public class ShareKnowledgeController {
|
||||
* @return 知识库详情
|
||||
*/
|
||||
@GetMapping("/documentCollection/detail")
|
||||
public Result<DocumentCollection> detail(@RequestParam String shareKey) {
|
||||
public Result<KnowledgeShareViewDetail> detail(@RequestParam String shareKey) {
|
||||
KnowledgeShareAuthContext context = knowledgeShareService.assertUrlShareAccess(
|
||||
shareKey,
|
||||
null,
|
||||
KnowledgeShareActionScope.VIEW.name()
|
||||
);
|
||||
audit(context, "访问知识库分享页", "KNOWLEDGE_SHARE_URL_ACCESS", false, auditDetail("knowledgeId", context.getKnowledge().getId()));
|
||||
return Result.ok(context.getKnowledge());
|
||||
KnowledgeShareViewDetail detail = new KnowledgeShareViewDetail();
|
||||
detail.setKnowledge(context.getKnowledge());
|
||||
detail.setPermissionScopes(new java.util.ArrayList<String>(context.getShare().getPermissionScopeSet()));
|
||||
return Result.ok(detail);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,6 +242,26 @@ public class ShareKnowledgeController {
|
||||
return Result.ok(documentService.getDocumentList(context.getKnowledge().getId().toString(), pageSize, pageNumber, title));
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅分享知识库的文档任务状态流。
|
||||
*
|
||||
* @param shareKey 分享访问密钥
|
||||
* @param knowledgeId 知识库 ID
|
||||
* @return SSE 推送连接
|
||||
*/
|
||||
@PostMapping(value = "/document/import/task/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public SseEmitter streamDocumentTask(
|
||||
@RequestParam String shareKey,
|
||||
@JsonBody(value = "knowledgeId", required = true) BigInteger knowledgeId
|
||||
) {
|
||||
KnowledgeShareAuthContext context = knowledgeShareService.assertUrlShareAccess(
|
||||
shareKey,
|
||||
knowledgeId,
|
||||
KnowledgeShareActionScope.VIEW.name()
|
||||
);
|
||||
return documentImportTaskStatusStreamService.subscribe(context.getKnowledge().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文档。
|
||||
*/
|
||||
@@ -344,6 +372,104 @@ public class ShareKnowledgeController {
|
||||
return documentService.commitImport(request);
|
||||
}
|
||||
|
||||
@PostMapping("/document/import/task/create")
|
||||
public Result<DocumentImportDtos.TaskCreateResponse> createImportTask(
|
||||
@RequestParam String shareKey,
|
||||
@JsonBody DocumentImportDtos.TaskCreateRequest request
|
||||
) {
|
||||
KnowledgeShareAuthContext context = knowledgeShareService.assertUrlShareAccess(
|
||||
shareKey,
|
||||
request == null ? null : request.getKnowledgeId(),
|
||||
KnowledgeShareActionScope.CONTENT_CREATE.name()
|
||||
);
|
||||
BigInteger knowledgeId = resolveKnowledgeId(context, request == null ? null : request.getKnowledgeId());
|
||||
request.setKnowledgeId(knowledgeId);
|
||||
audit(context, "创建分享文档导入任务", "KNOWLEDGE_SHARE_URL_WRITE", true, auditDetail("knowledgeId", knowledgeId));
|
||||
return documentService.createImportTask(request);
|
||||
}
|
||||
|
||||
@GetMapping("/document/import/task/detail")
|
||||
public Result<DocumentImportDtos.TaskDetailResponse> getImportTaskDetail(
|
||||
@RequestParam String shareKey,
|
||||
@RequestParam BigInteger taskId
|
||||
) {
|
||||
KnowledgeShareAuthContext context = knowledgeShareService.assertUrlShareAccess(
|
||||
shareKey,
|
||||
null,
|
||||
KnowledgeShareActionScope.VIEW.name()
|
||||
);
|
||||
Result<DocumentImportDtos.TaskDetailResponse> result = documentService.getImportTaskDetail(taskId);
|
||||
BigInteger knowledgeId = result.getData() == null ? null : result.getData().getKnowledgeId();
|
||||
if (knowledgeId == null || knowledgeId.compareTo(context.getKnowledge().getId()) != 0) {
|
||||
throw new BusinessException("任务不存在");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping("/document/import/task/preview")
|
||||
public Result<DocumentImportDtos.PreviewResponse> previewImportTask(
|
||||
@RequestParam String shareKey,
|
||||
@JsonBody DocumentImportDtos.PreviewRequest request
|
||||
) {
|
||||
KnowledgeShareAuthContext context = knowledgeShareService.assertUrlShareAccess(
|
||||
shareKey,
|
||||
request == null ? null : request.getKnowledgeId(),
|
||||
KnowledgeShareActionScope.CONTENT_CREATE.name()
|
||||
);
|
||||
BigInteger knowledgeId = resolveKnowledgeId(context, request == null ? null : request.getKnowledgeId());
|
||||
request.setKnowledgeId(knowledgeId);
|
||||
audit(context, "预览分享文档分块", "KNOWLEDGE_SHARE_URL_WRITE", true, auditDetail("knowledgeId", knowledgeId));
|
||||
return documentService.previewImportTask(request);
|
||||
}
|
||||
|
||||
@PostMapping("/document/import/task/startIndex")
|
||||
public Result<DocumentImportDtos.TaskStartIndexResponse> startIndexTask(
|
||||
@RequestParam String shareKey,
|
||||
@JsonBody DocumentImportDtos.TaskStartIndexRequest request
|
||||
) {
|
||||
KnowledgeShareAuthContext context = knowledgeShareService.assertUrlShareAccess(
|
||||
shareKey,
|
||||
request == null ? null : request.getKnowledgeId(),
|
||||
KnowledgeShareActionScope.CONTENT_CREATE.name()
|
||||
);
|
||||
BigInteger knowledgeId = resolveKnowledgeId(context, request == null ? null : request.getKnowledgeId());
|
||||
request.setKnowledgeId(knowledgeId);
|
||||
audit(context, "启动分享文档向量化", "KNOWLEDGE_SHARE_URL_WRITE", true, auditDetail("knowledgeId", knowledgeId));
|
||||
return documentService.startIndexTask(request);
|
||||
}
|
||||
|
||||
@PostMapping("/document/import/task/retryParse")
|
||||
public Result<DocumentImportDtos.TaskStartIndexResponse> retryParseTask(
|
||||
@RequestParam String shareKey,
|
||||
@JsonBody DocumentImportDtos.TaskRetryRequest request
|
||||
) {
|
||||
KnowledgeShareAuthContext context = knowledgeShareService.assertUrlShareAccess(
|
||||
shareKey,
|
||||
request == null ? null : request.getKnowledgeId(),
|
||||
KnowledgeShareActionScope.CONTENT_CREATE.name()
|
||||
);
|
||||
BigInteger knowledgeId = resolveKnowledgeId(context, request == null ? null : request.getKnowledgeId());
|
||||
request.setKnowledgeId(knowledgeId);
|
||||
audit(context, "重试分享文档解析", "KNOWLEDGE_SHARE_URL_WRITE", true, auditDetail("knowledgeId", knowledgeId));
|
||||
return documentService.retryParseTask(request);
|
||||
}
|
||||
|
||||
@PostMapping("/document/import/task/retryIndex")
|
||||
public Result<DocumentImportDtos.TaskStartIndexResponse> retryIndexTask(
|
||||
@RequestParam String shareKey,
|
||||
@JsonBody DocumentImportDtos.TaskRetryRequest request
|
||||
) {
|
||||
KnowledgeShareAuthContext context = knowledgeShareService.assertUrlShareAccess(
|
||||
shareKey,
|
||||
request == null ? null : request.getKnowledgeId(),
|
||||
KnowledgeShareActionScope.CONTENT_CREATE.name()
|
||||
);
|
||||
BigInteger knowledgeId = resolveKnowledgeId(context, request == null ? null : request.getKnowledgeId());
|
||||
request.setKnowledgeId(knowledgeId);
|
||||
audit(context, "重试分享文档向量化", "KNOWLEDGE_SHARE_URL_WRITE", true, auditDetail("knowledgeId", knowledgeId));
|
||||
return documentService.retryIndexTask(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk 分页。
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user