165 lines
4.9 KiB
Go
165 lines
4.9 KiB
Go
package document
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"resume-platform/internal/model"
|
|
"resume-platform/internal/parser"
|
|
"resume-platform/internal/repository"
|
|
"resume-platform/internal/service/knowledge"
|
|
"resume-platform/internal/service/resume"
|
|
"resume-platform/pkg/logger"
|
|
)
|
|
|
|
type DocumentService interface {
|
|
UploadDocument(ctx context.Context, userID string, fileName string, fileType string, fileSize int64, content string) (*model.Document, error)
|
|
GetDocuments(ctx context.Context, userID string) ([]*model.Document, error)
|
|
GetDocument(ctx context.Context, userID string, documentID string) (*model.Document, error)
|
|
DeleteDocument(ctx context.Context, userID string, documentID string) error
|
|
ProcessDocument(ctx context.Context, documentID string) error
|
|
ParseAndGenerateResume(ctx context.Context, userID string, fileName string, content []byte) (*model.Resume, error)
|
|
}
|
|
|
|
type documentService struct {
|
|
repo repository.ResumeRepository
|
|
resumeGenerator *resume.ResumeGeneratorService
|
|
knowledgeBaseService *knowledge.KnowledgeBaseService
|
|
}
|
|
|
|
func NewDocumentService(repo repository.ResumeRepository, resumeGenerator *resume.ResumeGeneratorService) DocumentService {
|
|
return &documentService{repo: repo, resumeGenerator: resumeGenerator}
|
|
}
|
|
|
|
func NewDocumentServiceWithKnowledge(repo repository.ResumeRepository, resumeGenerator *resume.ResumeGeneratorService, knowledgeBaseService *knowledge.KnowledgeBaseService) DocumentService {
|
|
return &documentService{repo: repo, resumeGenerator: resumeGenerator, knowledgeBaseService: knowledgeBaseService}
|
|
}
|
|
|
|
func (s *documentService) UploadDocument(ctx context.Context, userID string, fileName string, fileType string, fileSize int64, content string) (*model.Document, error) {
|
|
document := &model.Document{
|
|
ID: generateID(),
|
|
UserID: userID,
|
|
Name: fileName,
|
|
Type: fileType,
|
|
Size: fileSize,
|
|
Content: content,
|
|
Status: "uploading",
|
|
}
|
|
|
|
err := s.repo.CreateDocument(document)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
go s.ProcessDocument(ctx, document.ID)
|
|
|
|
return document, nil
|
|
}
|
|
|
|
func (s *documentService) GetDocuments(ctx context.Context, userID string) ([]*model.Document, error) {
|
|
return s.repo.GetDocumentsByUserID(userID)
|
|
}
|
|
|
|
func (s *documentService) GetDocument(ctx context.Context, userID string, documentID string) (*model.Document, error) {
|
|
return s.repo.GetDocument(documentID, userID)
|
|
}
|
|
|
|
func (s *documentService) DeleteDocument(ctx context.Context, userID string, documentID string) error {
|
|
return s.repo.DeleteDocument(documentID, userID)
|
|
}
|
|
|
|
func (s *documentService) ProcessDocument(ctx context.Context, documentID string) error {
|
|
document, err := s.repo.GetDocumentByID(documentID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if document.Content == "" {
|
|
document.Status = "failed"
|
|
document.ErrorMsg = "document content is empty"
|
|
return s.repo.UpdateDocument(document)
|
|
}
|
|
|
|
chunks := s.splitText(document.Content, 512)
|
|
|
|
for i, chunk := range chunks {
|
|
metadata := map[string]interface{}{
|
|
"document_id": document.ID,
|
|
"chunk_index": i,
|
|
"user_id": document.UserID,
|
|
}
|
|
metadataJSON, _ := json.Marshal(metadata)
|
|
|
|
documentChunk := &model.DocumentChunk{
|
|
ID: generateID(),
|
|
DocumentID: document.ID,
|
|
ChunkIndex: i,
|
|
Content: chunk,
|
|
Metadata: string(metadataJSON),
|
|
}
|
|
|
|
err := s.repo.CreateDocumentChunk(documentChunk)
|
|
if err != nil {
|
|
document.Status = "failed"
|
|
document.ErrorMsg = err.Error()
|
|
s.repo.UpdateDocument(document)
|
|
return err
|
|
}
|
|
}
|
|
|
|
if s.knowledgeBaseService != nil {
|
|
err = s.knowledgeBaseService.AddDocument(ctx, document.ID, document.Content, document.UserID)
|
|
if err != nil {
|
|
logger.Warnf("Failed to add document to knowledge base: %v", err)
|
|
} else {
|
|
logger.Infof("Document %s added to knowledge base", document.ID)
|
|
}
|
|
}
|
|
|
|
document.Status = "processed"
|
|
return s.repo.UpdateDocument(document)
|
|
}
|
|
|
|
func (s *documentService) splitText(text string, chunkSize int) []string {
|
|
var chunks []string
|
|
words := []rune(text)
|
|
|
|
for i := 0; i < len(words); i += chunkSize {
|
|
end := i + chunkSize
|
|
if end > len(words) {
|
|
end = len(words)
|
|
}
|
|
chunks = append(chunks, string(words[i:end]))
|
|
}
|
|
|
|
return chunks
|
|
}
|
|
|
|
func generateID() string {
|
|
return uuid.New().String()
|
|
}
|
|
|
|
func (s *documentService) ParseAndGenerateResume(ctx context.Context, userID string, fileName string, content []byte) (*model.Resume, error) {
|
|
documentText, err := parser.ParseFile(fileName, bytes.NewReader(content))
|
|
if err != nil {
|
|
logger.Warnf("Failed to parse document %s: %v", fileName, err)
|
|
return nil, err
|
|
}
|
|
|
|
if documentText == "" {
|
|
return nil, fmt.Errorf("document content is empty after parsing")
|
|
}
|
|
|
|
resume, err := s.resumeGenerator.GenerateFromDocument(ctx, documentText)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resume.UserID = userID
|
|
fmt.Printf("DEBUG document.go: resume.UserID set to: '%s'\n", resume.UserID)
|
|
|
|
return resume, nil
|
|
} |