feat: implement batch move and delete functionality for documents and FAQs
This commit is contained in:
@@ -263,6 +263,8 @@ func registerDashboardKnowledgeDirectoryRoutes(group *gin.RouterGroup) {
|
||||
|
||||
func registerDashboardKnowledgeDocumentRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.KnowledgeDocumentGetBy)
|
||||
group.POST("/batch_delete", dashboard.KnowledgeDocumentPostBatch_delete)
|
||||
group.POST("/batch_move", dashboard.KnowledgeDocumentPostBatch_move)
|
||||
group.POST("/create", dashboard.KnowledgeDocumentPostCreate)
|
||||
group.POST("/delete", dashboard.KnowledgeDocumentPostDelete)
|
||||
group.Any("/list", dashboard.KnowledgeDocumentAnyList)
|
||||
@@ -273,6 +275,8 @@ func registerDashboardKnowledgeFAQRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/import_template", dashboard.KnowledgeFAQGetImport_template)
|
||||
group.GET("/export", dashboard.KnowledgeFAQGetExport)
|
||||
group.GET("/:id", dashboard.KnowledgeFAQGetBy)
|
||||
group.POST("/batch_delete", dashboard.KnowledgeFAQPostBatch_delete)
|
||||
group.POST("/batch_move", dashboard.KnowledgeFAQPostBatch_move)
|
||||
group.POST("/create", dashboard.KnowledgeFAQPostCreate)
|
||||
group.POST("/delete", dashboard.KnowledgeFAQPostDelete)
|
||||
group.POST("/import", dashboard.KnowledgeFAQPostImport)
|
||||
|
||||
@@ -144,3 +144,38 @@ func KnowledgeDocumentPostDelete(ctx *gin.Context) {
|
||||
}
|
||||
httpx.WriteJSON(ctx, nil)
|
||||
}
|
||||
|
||||
func KnowledgeDocumentPostBatch_move(ctx *gin.Context) {
|
||||
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentUpdate)
|
||||
if err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
req := request.BatchMoveKnowledgeDocumentRequest{}
|
||||
if err := params.ReadJSON(ctx, &req); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
if err := services.KnowledgeDocumentService.BatchMoveKnowledgeDocuments(req, operator); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(ctx, nil)
|
||||
}
|
||||
|
||||
func KnowledgeDocumentPostBatch_delete(ctx *gin.Context) {
|
||||
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeDocumentDelete); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
req := request.BatchDeleteKnowledgeDocumentRequest{}
|
||||
if err := params.ReadJSON(ctx, &req); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
if err := services.KnowledgeDocumentService.BatchDeleteKnowledgeDocuments(req); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -192,6 +192,41 @@ func KnowledgeFAQPostDelete(ctx *gin.Context) {
|
||||
httpx.WriteJSON(ctx, nil)
|
||||
}
|
||||
|
||||
func KnowledgeFAQPostBatch_move(ctx *gin.Context) {
|
||||
operator, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeFAQUpdate)
|
||||
if err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
req := request.BatchMoveKnowledgeFAQRequest{}
|
||||
if err := params.ReadJSON(ctx, &req); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
if err := services.KnowledgeFAQService.BatchMoveKnowledgeFAQs(req, operator); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(ctx, nil)
|
||||
}
|
||||
|
||||
func KnowledgeFAQPostBatch_delete(ctx *gin.Context) {
|
||||
if _, err := services.AuthService.RequirePermission(ctx, constants.PermissionKnowledgeFAQDelete); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
req := request.BatchDeleteKnowledgeFAQRequest{}
|
||||
if err := params.ReadJSON(ctx, &req); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
if err := services.KnowledgeFAQService.BatchDeleteKnowledgeFAQs(req); err != nil {
|
||||
httpx.WriteJSON(ctx, err)
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(ctx, nil)
|
||||
}
|
||||
|
||||
func writeKnowledgeFAQExcelFile(ctx *gin.Context, file *response.KnowledgeFAQExportedFile) {
|
||||
ctx.Header("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, file.Filename))
|
||||
ctx.Header("Cache-Control", "no-store")
|
||||
|
||||
@@ -55,6 +55,16 @@ type UpdateKnowledgeDocumentRequest struct {
|
||||
CreateKnowledgeDocumentRequest
|
||||
}
|
||||
|
||||
type BatchMoveKnowledgeDocumentRequest struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
IDs []int64 `json:"ids"`
|
||||
}
|
||||
|
||||
type BatchDeleteKnowledgeDocumentRequest struct {
|
||||
IDs []int64 `json:"ids"`
|
||||
}
|
||||
|
||||
type CreateKnowledgeFAQRequest struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
@@ -69,6 +79,16 @@ type UpdateKnowledgeFAQRequest struct {
|
||||
CreateKnowledgeFAQRequest
|
||||
}
|
||||
|
||||
type BatchMoveKnowledgeFAQRequest struct {
|
||||
KnowledgeBaseID int64 `json:"knowledgeBaseId"`
|
||||
DirectoryID int64 `json:"directoryId"`
|
||||
IDs []int64 `json:"ids"`
|
||||
}
|
||||
|
||||
type BatchDeleteKnowledgeFAQRequest struct {
|
||||
IDs []int64 `json:"ids"`
|
||||
}
|
||||
|
||||
type KnowledgeFAQImportMode string
|
||||
|
||||
const (
|
||||
|
||||
@@ -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("知识库不存在")
|
||||
|
||||
Reference in New Issue
Block a user