首次提交:初始化项目代码
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user