Files

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
}