feat: implement batch move and delete functionality for documents and FAQs

This commit is contained in:
mlogclub
2026-06-02 16:13:08 +08:00
parent 1bdc1cb2b6
commit 842f827c86
12 changed files with 727 additions and 2 deletions
@@ -183,6 +183,73 @@ func (s *knowledgeDocumentService) DeleteKnowledgeDocument(id int64) error {
return rag.Index.RemoveDocumentIndex(context.Background(), id)
}
func (s *knowledgeDocumentService) BatchMoveKnowledgeDocuments(req request.BatchMoveKnowledgeDocumentRequest, operator *dto.AuthPrincipal) error {
if operator == nil {
return errorsx.Unauthorized("未登录或登录已过期")
}
ids := uniquePositiveIDs(req.IDs)
if len(ids) == 0 {
return errorsx.InvalidParam("请选择要移动的文档")
}
kb := KnowledgeBaseService.Get(req.KnowledgeBaseID)
if kb == nil {
return errorsx.InvalidParam("知识库不存在")
}
if kb.KnowledgeType == string(enums.KnowledgeBaseTypeFAQ) {
return errorsx.InvalidParam("FAQ知识库不支持文档")
}
if _, err := KnowledgeDirectoryService.RequireUsableDirectory(req.KnowledgeBaseID, req.DirectoryID); err != nil {
return err
}
for _, id := range ids {
current := s.Get(id)
if current == nil || current.Status == enums.StatusDeleted {
return errorsx.InvalidParam("文档不存在")
}
if current.KnowledgeBaseID != req.KnowledgeBaseID {
return errorsx.InvalidParam("只能移动当前知识库下的文档")
}
}
now := time.Now()
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
for _, id := range ids {
if err := repositories.KnowledgeDocumentRepository.Updates(ctx.Tx, id, map[string]any{
"directory_id": req.DirectoryID,
"index_status": enums.KnowledgeDocumentIndexStatusPending,
"indexed_at": nil,
"index_error": "",
"update_user_id": operator.UserID,
"update_user_name": operator.Username,
"updated_at": now,
}); err != nil {
return err
}
}
return nil
}); err != nil {
return err
}
for _, id := range ids {
if err := rag.Index.IndexDocumentByID(context.Background(), id); err != nil {
slog.Error("failed to reindex moved knowledge document", "document_id", id, "error", err)
}
}
return nil
}
func (s *knowledgeDocumentService) BatchDeleteKnowledgeDocuments(req request.BatchDeleteKnowledgeDocumentRequest) error {
ids := uniquePositiveIDs(req.IDs)
if len(ids) == 0 {
return errorsx.InvalidParam("请选择要删除的文档")
}
for _, id := range ids {
if err := s.DeleteKnowledgeDocument(id); err != nil {
return err
}
}
return nil
}
func (s *knowledgeDocumentService) buildKnowledgeDocumentModel(req request.CreateKnowledgeDocumentRequest) (*models.KnowledgeDocument, error) {
if strs.IsBlank(string(req.ContentType)) {
req.ContentType = enums.KnowledgeDocumentContentTypeHTML
@@ -205,3 +272,19 @@ func (s *knowledgeDocumentService) buildKnowledgeDocumentModel(req request.Creat
}
return item, nil
}
func uniquePositiveIDs(ids []int64) []int64 {
result := make([]int64, 0, len(ids))
seen := make(map[int64]struct{}, len(ids))
for _, id := range ids {
if id <= 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
return result
}
@@ -122,6 +122,69 @@ func (s *knowledgeFAQService) DeleteKnowledgeFAQ(id int64) error {
return rag.Index.RemoveFAQIndex(context.Background(), id)
}
func (s *knowledgeFAQService) BatchMoveKnowledgeFAQs(req request.BatchMoveKnowledgeFAQRequest, operator *dto.AuthPrincipal) error {
if operator == nil {
return errorsx.Unauthorized("未登录或登录已过期")
}
ids := uniquePositiveIDs(req.IDs)
if len(ids) == 0 {
return errorsx.InvalidParam("请选择要移动的FAQ")
}
if _, err := s.requireFAQKnowledgeBase(req.KnowledgeBaseID); err != nil {
return err
}
if _, err := KnowledgeDirectoryService.RequireUsableDirectory(req.KnowledgeBaseID, req.DirectoryID); err != nil {
return err
}
for _, id := range ids {
current := s.Get(id)
if current == nil {
return errorsx.InvalidParam("FAQ不存在")
}
if current.KnowledgeBaseID != req.KnowledgeBaseID {
return errorsx.InvalidParam("只能移动当前知识库下的FAQ")
}
}
now := time.Now()
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
for _, id := range ids {
if err := repositories.KnowledgeFAQRepository.Updates(ctx.Tx, id, map[string]any{
"directory_id": req.DirectoryID,
"index_status": enums.KnowledgeDocumentIndexStatusPending,
"indexed_at": nil,
"index_error": "",
"update_user_id": operator.UserID,
"update_user_name": operator.Username,
"updated_at": now,
}); err != nil {
return err
}
}
return nil
}); err != nil {
return err
}
for _, id := range ids {
if err := rag.Index.IndexFAQByID(context.Background(), id); err != nil {
slog.Error("failed to reindex moved knowledge faq", "faq_id", id, "error", err)
}
}
return nil
}
func (s *knowledgeFAQService) BatchDeleteKnowledgeFAQs(req request.BatchDeleteKnowledgeFAQRequest) error {
ids := uniquePositiveIDs(req.IDs)
if len(ids) == 0 {
return errorsx.InvalidParam("请选择要删除的FAQ")
}
for _, id := range ids {
if err := s.DeleteKnowledgeFAQ(id); err != nil {
return err
}
}
return nil
}
func (s *knowledgeFAQService) buildKnowledgeFAQModel(req request.CreateKnowledgeFAQRequest) (*models.KnowledgeFAQ, error) {
if req.KnowledgeBaseID <= 0 {
return nil, errorsx.InvalidParam("知识库不存在")