import_service.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package service
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "log"
  7. "path/filepath"
  8. "github.com/2930134478/AI-CS/backend/models"
  9. "github.com/2930134478/AI-CS/backend/repository"
  10. import_service "github.com/2930134478/AI-CS/backend/service/import"
  11. )
  12. // ImportService 导入服务
  13. type ImportService struct {
  14. docRepo *repository.DocumentRepository
  15. kbRepo *repository.KnowledgeBaseRepository
  16. documentService *DocumentService
  17. documentEmbeddingService interface{} // 使用 interface{} 避免循环依赖
  18. parsers []import_service.DocumentParser
  19. }
  20. // NewImportService 创建导入服务实例
  21. func NewImportService(
  22. docRepo *repository.DocumentRepository,
  23. kbRepo *repository.KnowledgeBaseRepository,
  24. documentService *DocumentService,
  25. documentEmbeddingService interface{},
  26. ) *ImportService {
  27. // 初始化解析器
  28. parsers := []import_service.DocumentParser{
  29. import_service.NewMarkdownParser(),
  30. import_service.NewPDFParser(),
  31. import_service.NewWordParser(),
  32. }
  33. return &ImportService{
  34. docRepo: docRepo,
  35. kbRepo: kbRepo,
  36. documentService: documentService,
  37. documentEmbeddingService: documentEmbeddingService,
  38. parsers: parsers,
  39. }
  40. }
  41. // ImportFiles 导入文件
  42. func (s *ImportService) ImportFiles(ctx context.Context, knowledgeBaseID uint, filePaths []string) (*ImportResult, error) {
  43. // 验证知识库是否存在
  44. _, err := s.kbRepo.GetByID(knowledgeBaseID)
  45. if err != nil {
  46. return nil, errors.New("知识库不存在")
  47. }
  48. result := &ImportResult{
  49. SuccessCount: 0,
  50. FailedCount: 0,
  51. FailedFiles: []string{},
  52. Errors: []string{},
  53. }
  54. // 解析文件
  55. documents := make([]*models.Document, 0)
  56. for _, filePath := range filePaths {
  57. // 查找合适的解析器
  58. var parser import_service.DocumentParser
  59. for _, p := range s.parsers {
  60. if p.Supports(filePath) {
  61. parser = p
  62. break
  63. }
  64. }
  65. if parser == nil {
  66. result.FailedCount++
  67. result.FailedFiles = append(result.FailedFiles, filepath.Base(filePath))
  68. result.Errors = append(result.Errors, fmt.Sprintf("文件 %s: 不支持的文件格式", filepath.Base(filePath)))
  69. continue
  70. }
  71. // 解析文件
  72. parsed, err := parser.Parse(filePath)
  73. if err != nil {
  74. result.FailedCount++
  75. result.FailedFiles = append(result.FailedFiles, filepath.Base(filePath))
  76. result.Errors = append(result.Errors, fmt.Sprintf("文件 %s: %v", filepath.Base(filePath), err))
  77. continue
  78. }
  79. // 创建文档
  80. doc := &models.Document{
  81. KnowledgeBaseID: knowledgeBaseID,
  82. Title: parsed.Title,
  83. Content: parsed.Content,
  84. Type: "document",
  85. Status: "draft",
  86. EmbeddingStatus: "pending",
  87. }
  88. documents = append(documents, doc)
  89. }
  90. // 批量创建文档
  91. docIDs := make([]uint, 0, len(documents))
  92. for _, doc := range documents {
  93. if err := s.docRepo.Create(doc); err != nil {
  94. result.FailedCount++
  95. result.FailedFiles = append(result.FailedFiles, doc.Title)
  96. result.Errors = append(result.Errors, fmt.Sprintf("文档 %s: 创建失败: %v", doc.Title, err))
  97. continue
  98. }
  99. docIDs = append(docIDs, doc.ID)
  100. result.SuccessCount++
  101. }
  102. // 批量向量化(异步)
  103. if len(docIDs) > 0 {
  104. ids := make([]uint, len(docIDs))
  105. copy(ids, docIDs)
  106. go func(ids []uint) {
  107. log.Printf("[导入] 文件导入已创建 %d 条文档,开始批量向量化", len(ids))
  108. _, err := s.BatchEmbedDocuments(context.Background(), ids)
  109. if err != nil {
  110. log.Printf("[导入] 批量向量化失败: %v", err)
  111. return
  112. }
  113. log.Printf("[导入] 批量向量化完成,%d 条文档已写入向量库", len(ids))
  114. }(ids)
  115. }
  116. return result, nil
  117. }
  118. // ImportResult 导入结果
  119. type ImportResult struct {
  120. SuccessCount int `json:"success_count"`
  121. FailedCount int `json:"failed_count"`
  122. FailedFiles []string `json:"failed_files"`
  123. Errors []string `json:"errors"`
  124. Message string `json:"message"`
  125. }