Add document saved
This commit is contained in:
@@ -39,6 +39,10 @@ type LetterOutgoingProcessor interface {
|
||||
|
||||
GetApprovalsByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingApproval, error)
|
||||
GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error)
|
||||
|
||||
// GetOutgoingLetterWithDetails fetches letter with all related data
|
||||
GetOutgoingLetterWithDetails(ctx context.Context, letterID uuid.UUID) (*entities.LetterOutgoing, error)
|
||||
GetUsersByIDs(ctx context.Context, userIDs []uuid.UUID) ([]entities.User, error)
|
||||
}
|
||||
|
||||
type LetterOutgoingProcessorImpl struct {
|
||||
@@ -166,35 +170,50 @@ func (p *LetterOutgoingProcessorImpl) createRecipientsFromApprovalFlow(ctx conte
|
||||
return err
|
||||
}
|
||||
|
||||
recipient := p.createRecipientFromApprovalStep(flow.Steps[0], letter.ID)
|
||||
if recipient == nil {
|
||||
return nil // No valid recipient could be created
|
||||
// Find the minimum step order (first step)
|
||||
minStepOrder := flow.Steps[0].StepOrder
|
||||
for _, step := range flow.Steps {
|
||||
if step.StepOrder < minStepOrder {
|
||||
minStepOrder = step.StepOrder
|
||||
}
|
||||
}
|
||||
|
||||
return p.recipientRepo.CreateBulk(ctx, []entities.LetterOutgoingRecipient{*recipient})
|
||||
// Collect all recipients from the first step (can be multiple if parallel)
|
||||
var recipients []entities.LetterOutgoingRecipient
|
||||
for i, step := range flow.Steps {
|
||||
// Only process steps with the minimum step order (first step)
|
||||
if step.StepOrder != minStepOrder {
|
||||
continue
|
||||
}
|
||||
|
||||
recipient := p.createRecipientFromApprovalStep(step, letter.ID)
|
||||
if recipient != nil {
|
||||
// Mark the first recipient as primary
|
||||
if i == 0 {
|
||||
recipient.IsPrimary = true
|
||||
} else {
|
||||
recipient.IsPrimary = false
|
||||
}
|
||||
recipients = append(recipients, *recipient)
|
||||
}
|
||||
}
|
||||
|
||||
// If no recipients were created, return without error
|
||||
if len(recipients) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bulk create all recipients
|
||||
return p.recipientRepo.CreateBulk(ctx, recipients)
|
||||
}
|
||||
|
||||
// createRecipientFromApprovalStep creates a recipient from an approval flow step
|
||||
func (p *LetterOutgoingProcessorImpl) createRecipientFromApprovalStep(step entities.ApprovalFlowStep, letterID uuid.UUID) *entities.LetterOutgoingRecipient {
|
||||
recipient := &entities.LetterOutgoingRecipient{
|
||||
LetterID: letterID,
|
||||
IsPrimary: true,
|
||||
}
|
||||
|
||||
if step.ApproverUser != nil {
|
||||
recipient.RecipientName = step.ApproverUser.Name
|
||||
recipient.RecipientEmail = &step.ApproverUser.Email
|
||||
|
||||
// Extract position from user profile if available
|
||||
if step.ApproverUser.Profile != nil && step.ApproverUser.Profile.JobTitle != nil {
|
||||
recipient.RecipientPosition = step.ApproverUser.Profile.JobTitle
|
||||
}
|
||||
} else if step.ApproverRole != nil {
|
||||
recipient.RecipientName = step.ApproverRole.Name
|
||||
position := "Role: " + step.ApproverRole.Name
|
||||
recipient.RecipientPosition = &position
|
||||
} else {
|
||||
return nil // No valid approver found
|
||||
LetterID: letterID,
|
||||
Status: "pending",
|
||||
IsArchived: false,
|
||||
UserID: &step.ApproverUser.ID,
|
||||
}
|
||||
|
||||
return recipient
|
||||
@@ -537,3 +556,60 @@ func (p *LetterOutgoingProcessorImpl) GetApprovalsByLetter(ctx context.Context,
|
||||
func (p *LetterOutgoingProcessorImpl) GetApprovalFlow(ctx context.Context, flowID uuid.UUID) (*entities.ApprovalFlow, error) {
|
||||
return p.approvalFlowRepo.Get(ctx, flowID)
|
||||
}
|
||||
|
||||
// GetOutgoingLetterWithDetails fetches letter with all related data including approvals, discussions, and users
|
||||
func (p *LetterOutgoingProcessorImpl) GetOutgoingLetterWithDetails(ctx context.Context, letterID uuid.UUID) (*entities.LetterOutgoing, error) {
|
||||
letter, err := p.letterRepo.GetWithRelations(ctx, letterID, []string{
|
||||
"Priority",
|
||||
"ReceiverInstitution",
|
||||
"Creator",
|
||||
"Creator.Profile",
|
||||
"ApprovalFlow",
|
||||
"ApprovalFlow.Steps",
|
||||
"ApprovalFlow.Steps.ApproverRole",
|
||||
"ApprovalFlow.Steps.ApproverUser",
|
||||
"ApprovalFlow.Steps.ApproverUser.Profile",
|
||||
"Recipients",
|
||||
"Recipients.User",
|
||||
"Recipients.User.Profile",
|
||||
"Recipients.Department",
|
||||
"Attachments",
|
||||
"Approvals",
|
||||
"Approvals.Step",
|
||||
"Approvals.Step.ApproverRole",
|
||||
"Approvals.Step.ApproverUser",
|
||||
"Approvals.Step.ApproverUser.Profile",
|
||||
"Approvals.Approver",
|
||||
"Approvals.Approver.Profile",
|
||||
"Discussions",
|
||||
"Discussions.User",
|
||||
"Discussions.User.Profile",
|
||||
"Discussions.Attachments",
|
||||
"ActivityLogs",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return letter, nil
|
||||
}
|
||||
|
||||
// GetUsersByIDs fetches users by their IDs
|
||||
func (p *LetterOutgoingProcessorImpl) GetUsersByIDs(ctx context.Context, userIDs []uuid.UUID) ([]entities.User, error) {
|
||||
if len(userIDs) == 0 {
|
||||
return []entities.User{}, nil
|
||||
}
|
||||
|
||||
var users []entities.User
|
||||
err := p.db.WithContext(ctx).
|
||||
Preload("Profile").
|
||||
Where("id IN ?", userIDs).
|
||||
Find(&users).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
@@ -182,18 +182,20 @@ func (p *LetterProcessorImpl) GetIncomingLetterByID(ctx context.Context, id uuid
|
||||
|
||||
func (p *LetterProcessorImpl) ListIncomingLetters(ctx context.Context, req *contract.ListIncomingLettersRequest) (*contract.ListIncomingLettersResponse, error) {
|
||||
page, limit := req.Page, req.Limit
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
|
||||
filter := repository.ListIncomingLettersFilter{
|
||||
Status: req.Status,
|
||||
Query: req.Query,
|
||||
DepartmentID: req.DepartmentID,
|
||||
}
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
filter := repository.ListIncomingLettersFilter{Status: req.Status, Query: req.Query}
|
||||
|
||||
list, total, err := p.letterRepo.List(ctx, filter, limit, (page-1)*limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respList := make([]contract.IncomingLetterResponse, 0, len(list))
|
||||
|
||||
for _, e := range list {
|
||||
atts, _ := p.attachRepo.ListByLetter(ctx, e.ID)
|
||||
var pr *entities.Priority
|
||||
@@ -202,12 +204,14 @@ func (p *LetterProcessorImpl) ListIncomingLetters(ctx context.Context, req *cont
|
||||
pr = got
|
||||
}
|
||||
}
|
||||
|
||||
var inst *entities.Institution
|
||||
if e.SenderInstitutionID != nil && p.institutionRepo != nil {
|
||||
if got, err := p.institutionRepo.Get(ctx, *e.SenderInstitutionID); err == nil {
|
||||
inst = got
|
||||
}
|
||||
}
|
||||
|
||||
resp := transformer.LetterEntityToContract(&e, atts, pr, inst)
|
||||
respList = append(respList, *resp)
|
||||
}
|
||||
@@ -433,7 +437,7 @@ func (p *LetterProcessorImpl) CreateDiscussion(ctx context.Context, letterID uui
|
||||
return err
|
||||
}
|
||||
if p.activity != nil {
|
||||
action := "reference_numberdiscussion.created"
|
||||
action := "discussion.created"
|
||||
tgt := "discussion"
|
||||
ctxMap := map[string]interface{}{"message": req.Message, "parent_id": req.ParentID}
|
||||
if err := p.activity.Log(txCtx, letterID, action, &userID, nil, &tgt, &disc.ID, nil, nil, ctxMap); err != nil {
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"eslogad-be/internal/entities"
|
||||
"eslogad-be/internal/repository"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OnlyOfficeProcessor interface {
|
||||
GetDocumentSessionByKey(ctx context.Context, documentKey string) (*entities.DocumentSession, error)
|
||||
GetOrCreateDocumentSession(ctx context.Context, documentID, userID uuid.UUID) (*entities.DocumentSession, error)
|
||||
UpdateDocumentSession(ctx context.Context, session *entities.DocumentSession) error
|
||||
CreateDocumentVersion(ctx context.Context, version *entities.DocumentVersion) error
|
||||
UpdateDocumentURL(ctx context.Context, documentID uuid.UUID, newURL string) error
|
||||
LockDocument(ctx context.Context, documentID, userID uuid.UUID) error
|
||||
UnlockDocument(ctx context.Context, documentID, userID uuid.UUID) error
|
||||
LogDocumentError(ctx context.Context, documentID uuid.UUID, errorMsg string, details interface{}) error
|
||||
GetDocumentDetails(ctx context.Context, documentID uuid.UUID, documentType string) (*DocumentDetails, error)
|
||||
}
|
||||
|
||||
type DocumentDetails struct {
|
||||
DocumentID uuid.UUID
|
||||
FileName string
|
||||
FileType string
|
||||
FileURL string
|
||||
FileSize int64
|
||||
DocumentType string
|
||||
ReferenceID uuid.UUID
|
||||
}
|
||||
|
||||
type OnlyOfficeProcessorImpl struct {
|
||||
db *gorm.DB
|
||||
sessionRepo *repository.DocumentSessionRepository
|
||||
versionRepo *repository.DocumentVersionRepository
|
||||
metadataRepo *repository.DocumentMetadataRepository
|
||||
errorRepo *repository.DocumentErrorRepository
|
||||
txManager *repository.TxManager
|
||||
}
|
||||
|
||||
func NewOnlyOfficeProcessor(
|
||||
db *gorm.DB,
|
||||
sessionRepo *repository.DocumentSessionRepository,
|
||||
versionRepo *repository.DocumentVersionRepository,
|
||||
metadataRepo *repository.DocumentMetadataRepository,
|
||||
errorRepo *repository.DocumentErrorRepository,
|
||||
txManager *repository.TxManager,
|
||||
) *OnlyOfficeProcessorImpl {
|
||||
return &OnlyOfficeProcessorImpl{
|
||||
db: db,
|
||||
sessionRepo: sessionRepo,
|
||||
versionRepo: versionRepo,
|
||||
metadataRepo: metadataRepo,
|
||||
errorRepo: errorRepo,
|
||||
txManager: txManager,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDocumentSessionByKey retrieves a document session by its OnlyOffice key
|
||||
func (p *OnlyOfficeProcessorImpl) GetDocumentSessionByKey(ctx context.Context, documentKey string) (*entities.DocumentSession, error) {
|
||||
return p.sessionRepo.GetByKey(ctx, documentKey)
|
||||
}
|
||||
|
||||
// GetOrCreateDocumentSession gets an existing session or creates a new one
|
||||
func (p *OnlyOfficeProcessorImpl) GetOrCreateDocumentSession(ctx context.Context, documentID, userID uuid.UUID) (*entities.DocumentSession, error) {
|
||||
// Try to get existing active session
|
||||
session, err := p.sessionRepo.GetActiveByDocument(ctx, documentID)
|
||||
if err == nil && session != nil {
|
||||
// Return existing session if it's not closed
|
||||
if session.Status != 4 { // Status 4 = closed
|
||||
return session, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create new session
|
||||
session = &entities.DocumentSession{
|
||||
DocumentID: documentID,
|
||||
UserID: userID,
|
||||
Status: 0, // Initial status
|
||||
Version: 1,
|
||||
IsLocked: false,
|
||||
}
|
||||
|
||||
// Generate unique document key with retry logic
|
||||
for attempts := 0; attempts < 3; attempts++ {
|
||||
session.DocumentKey = p.generateDocumentKey(documentID, userID)
|
||||
err = p.sessionRepo.Create(ctx, session)
|
||||
if err == nil {
|
||||
return session, nil
|
||||
}
|
||||
// If it's not a duplicate key error, return the error
|
||||
if !errors.Is(err, gorm.ErrDuplicatedKey) && !contains(err.Error(), "duplicate key") {
|
||||
return nil, fmt.Errorf("failed to create document session: %w", err)
|
||||
}
|
||||
// Wait a bit before retrying
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to create document session after retries: %w", err)
|
||||
}
|
||||
|
||||
// UpdateDocumentSession updates an existing document session
|
||||
func (p *OnlyOfficeProcessorImpl) UpdateDocumentSession(ctx context.Context, session *entities.DocumentSession) error {
|
||||
return p.sessionRepo.Update(ctx, session)
|
||||
}
|
||||
|
||||
// CreateDocumentVersion creates a new document version
|
||||
func (p *OnlyOfficeProcessorImpl) CreateDocumentVersion(ctx context.Context, version *entities.DocumentVersion) error {
|
||||
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
// Deactivate previous versions if this is the active one
|
||||
if version.IsActive {
|
||||
err := p.versionRepo.DeactivateAllVersions(txCtx, version.DocumentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deactivate previous versions: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create new version
|
||||
err := p.versionRepo.Create(txCtx, version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create document version: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateDocumentURL updates the document URL in the appropriate table
|
||||
func (p *OnlyOfficeProcessorImpl) UpdateDocumentURL(ctx context.Context, documentID uuid.UUID, newURL string) error {
|
||||
// Get document metadata to determine type
|
||||
metadata, err := p.metadataRepo.GetByDocumentID(ctx, documentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get document metadata: %w", err)
|
||||
}
|
||||
|
||||
// Update based on document type
|
||||
switch metadata.DocumentType {
|
||||
case "letter_attachment":
|
||||
return p.updateLetterAttachmentURL(ctx, metadata.ReferenceID, newURL)
|
||||
case "outgoing_attachment":
|
||||
return p.updateOutgoingAttachmentURL(ctx, metadata.ReferenceID, newURL)
|
||||
case "discussion_attachment":
|
||||
return p.updateDiscussionAttachmentURL(ctx, metadata.ReferenceID, newURL)
|
||||
default:
|
||||
return fmt.Errorf("unknown document type: %s", metadata.DocumentType)
|
||||
}
|
||||
}
|
||||
|
||||
// LockDocument locks a document for editing
|
||||
func (p *OnlyOfficeProcessorImpl) LockDocument(ctx context.Context, documentID, userID uuid.UUID) error {
|
||||
session, err := p.sessionRepo.GetActiveByDocument(ctx, documentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get document session: %w", err)
|
||||
}
|
||||
|
||||
// Check if already locked by another user
|
||||
if session.IsLocked && session.LockedBy != nil && *session.LockedBy != userID {
|
||||
return errors.New("document is already locked by another user")
|
||||
}
|
||||
|
||||
// Lock the document
|
||||
session.IsLocked = true
|
||||
session.LockedBy = &userID
|
||||
now := time.Now()
|
||||
session.LockedAt = &now
|
||||
|
||||
return p.sessionRepo.Update(ctx, session)
|
||||
}
|
||||
|
||||
// UnlockDocument unlocks a document
|
||||
func (p *OnlyOfficeProcessorImpl) UnlockDocument(ctx context.Context, documentID, userID uuid.UUID) error {
|
||||
session, err := p.sessionRepo.GetActiveByDocument(ctx, documentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get document session: %w", err)
|
||||
}
|
||||
|
||||
// Check if locked by the requesting user
|
||||
if session.IsLocked && session.LockedBy != nil && *session.LockedBy != userID {
|
||||
return errors.New("document is locked by another user")
|
||||
}
|
||||
|
||||
// Unlock the document
|
||||
session.IsLocked = false
|
||||
session.LockedBy = nil
|
||||
session.LockedAt = nil
|
||||
|
||||
return p.sessionRepo.Update(ctx, session)
|
||||
}
|
||||
|
||||
// LogDocumentError logs an error related to document operations
|
||||
func (p *OnlyOfficeProcessorImpl) LogDocumentError(ctx context.Context, documentID uuid.UUID, errorMsg string, details interface{}) error {
|
||||
detailsMap := make(map[string]interface{})
|
||||
|
||||
// Convert details to map if possible
|
||||
if details != nil {
|
||||
detailsJSON, _ := json.Marshal(details)
|
||||
json.Unmarshal(detailsJSON, &detailsMap)
|
||||
}
|
||||
|
||||
docError := &entities.DocumentError{
|
||||
DocumentID: documentID,
|
||||
ErrorType: "onlyoffice_callback",
|
||||
ErrorMsg: errorMsg,
|
||||
Details: detailsMap,
|
||||
}
|
||||
|
||||
// Get current session if available
|
||||
if session, err := p.sessionRepo.GetActiveByDocument(ctx, documentID); err == nil && session != nil {
|
||||
docError.SessionID = &session.ID
|
||||
}
|
||||
|
||||
return p.errorRepo.Create(ctx, docError)
|
||||
}
|
||||
|
||||
// GetDocumentDetails retrieves document details based on type
|
||||
func (p *OnlyOfficeProcessorImpl) GetDocumentDetails(ctx context.Context, documentID uuid.UUID, documentType string) (*DocumentDetails, error) {
|
||||
// Get metadata
|
||||
metadata, err := p.metadataRepo.GetByDocumentID(ctx, documentID)
|
||||
if err != nil {
|
||||
// If metadata doesn't exist, create it based on the document type
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return p.createDocumentMetadata(ctx, documentID, documentType)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the actual file URL from the appropriate table
|
||||
fileURL, err := p.getDocumentURL(ctx, metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DocumentDetails{
|
||||
DocumentID: metadata.DocumentID,
|
||||
FileName: metadata.FileName,
|
||||
FileType: metadata.FileType,
|
||||
FileURL: fileURL,
|
||||
FileSize: metadata.FileSize,
|
||||
DocumentType: metadata.DocumentType,
|
||||
ReferenceID: metadata.ReferenceID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
func (p *OnlyOfficeProcessorImpl) generateDocumentKey(documentID, userID uuid.UUID) string {
|
||||
// Use nanoseconds for better precision
|
||||
now := time.Now().UnixNano()
|
||||
|
||||
// Generate random bytes for additional uniqueness
|
||||
randomBytes := make([]byte, 4)
|
||||
rand.Read(randomBytes)
|
||||
randomHex := hex.EncodeToString(randomBytes)
|
||||
|
||||
return fmt.Sprintf("%s_%s_%d_%s", documentID.String()[:8], userID.String()[:8], now, randomHex)
|
||||
}
|
||||
|
||||
func (p *OnlyOfficeProcessorImpl) updateLetterAttachmentURL(ctx context.Context, attachmentID uuid.UUID, newURL string) error {
|
||||
// Update letter attachment URL
|
||||
return p.db.WithContext(ctx).
|
||||
Table("letter_incoming_attachments").
|
||||
Where("id = ?", attachmentID).
|
||||
Update("file_url", newURL).Error
|
||||
}
|
||||
|
||||
func (p *OnlyOfficeProcessorImpl) updateOutgoingAttachmentURL(ctx context.Context, attachmentID uuid.UUID, newURL string) error {
|
||||
// Update outgoing letter attachment URL
|
||||
return p.db.WithContext(ctx).
|
||||
Table("letter_outgoing_attachments").
|
||||
Where("id = ?", attachmentID).
|
||||
Update("file_url", newURL).Error
|
||||
}
|
||||
|
||||
func (p *OnlyOfficeProcessorImpl) updateDiscussionAttachmentURL(ctx context.Context, attachmentID uuid.UUID, newURL string) error {
|
||||
// Update discussion attachment URL
|
||||
return p.db.WithContext(ctx).
|
||||
Table("letter_outgoing_discussion_attachments").
|
||||
Where("id = ?", attachmentID).
|
||||
Update("file_url", newURL).Error
|
||||
}
|
||||
|
||||
func (p *OnlyOfficeProcessorImpl) createDocumentMetadata(ctx context.Context, documentID uuid.UUID, documentType string) (*DocumentDetails, error) {
|
||||
var metadata entities.DocumentMetadata
|
||||
var details DocumentDetails
|
||||
|
||||
// Fetch document information based on type
|
||||
switch documentType {
|
||||
case "letter_attachment":
|
||||
var attachment struct {
|
||||
ID uuid.UUID `gorm:"column:id"`
|
||||
LetterID uuid.UUID `gorm:"column:letter_id"`
|
||||
FileName string `gorm:"column:file_name"`
|
||||
FileURL string `gorm:"column:file_url"`
|
||||
FileSize int64 `gorm:"column:file_size"`
|
||||
FileType string `gorm:"column:file_type"`
|
||||
}
|
||||
err := p.db.WithContext(ctx).
|
||||
Table("letter_incoming_attachments").
|
||||
Where("id = ?", documentID).
|
||||
First(&attachment).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get letter attachment: %w", err)
|
||||
}
|
||||
|
||||
metadata = entities.DocumentMetadata{
|
||||
DocumentID: documentID,
|
||||
DocumentType: documentType,
|
||||
ReferenceID: attachment.LetterID,
|
||||
FileName: attachment.FileName,
|
||||
FileType: attachment.FileType,
|
||||
FileSize: attachment.FileSize,
|
||||
}
|
||||
details = DocumentDetails{
|
||||
DocumentID: documentID,
|
||||
FileName: attachment.FileName,
|
||||
FileType: attachment.FileType,
|
||||
FileURL: attachment.FileURL,
|
||||
FileSize: attachment.FileSize,
|
||||
DocumentType: documentType,
|
||||
ReferenceID: attachment.LetterID,
|
||||
}
|
||||
|
||||
case "outgoing_attachment":
|
||||
var attachment struct {
|
||||
ID uuid.UUID `gorm:"column:id"`
|
||||
LetterID uuid.UUID `gorm:"column:letter_id"`
|
||||
FileName string `gorm:"column:file_name"`
|
||||
FileURL string `gorm:"column:file_url"`
|
||||
FileSize int64 `gorm:"column:file_size"`
|
||||
FileType string `gorm:"column:file_type"`
|
||||
}
|
||||
err := p.db.WithContext(ctx).
|
||||
Table("letter_outgoing_attachments").
|
||||
Where("id = ?", documentID).
|
||||
First(&attachment).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get outgoing letter attachment: %w", err)
|
||||
}
|
||||
|
||||
metadata = entities.DocumentMetadata{
|
||||
DocumentID: documentID,
|
||||
DocumentType: documentType,
|
||||
ReferenceID: attachment.LetterID,
|
||||
FileName: attachment.FileName,
|
||||
FileType: attachment.FileType,
|
||||
FileSize: attachment.FileSize,
|
||||
}
|
||||
details = DocumentDetails{
|
||||
DocumentID: documentID,
|
||||
FileName: attachment.FileName,
|
||||
FileType: attachment.FileType,
|
||||
FileURL: attachment.FileURL,
|
||||
FileSize: attachment.FileSize,
|
||||
DocumentType: documentType,
|
||||
ReferenceID: attachment.LetterID,
|
||||
}
|
||||
|
||||
case "discussion_attachment":
|
||||
var attachment struct {
|
||||
ID uuid.UUID `gorm:"column:id"`
|
||||
DiscussionID uuid.UUID `gorm:"column:discussion_id"`
|
||||
FileName string `gorm:"column:file_name"`
|
||||
FileURL string `gorm:"column:file_url"`
|
||||
FileSize int64 `gorm:"column:file_size"`
|
||||
FileType string `gorm:"column:file_type"`
|
||||
}
|
||||
err := p.db.WithContext(ctx).
|
||||
Table("letter_outgoing_discussion_attachments").
|
||||
Where("id = ?", documentID).
|
||||
First(&attachment).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get discussion attachment: %w", err)
|
||||
}
|
||||
|
||||
metadata = entities.DocumentMetadata{
|
||||
DocumentID: documentID,
|
||||
DocumentType: documentType,
|
||||
ReferenceID: attachment.DiscussionID,
|
||||
FileName: attachment.FileName,
|
||||
FileType: attachment.FileType,
|
||||
FileSize: attachment.FileSize,
|
||||
}
|
||||
details = DocumentDetails{
|
||||
DocumentID: documentID,
|
||||
FileName: attachment.FileName,
|
||||
FileType: attachment.FileType,
|
||||
FileURL: attachment.FileURL,
|
||||
FileSize: attachment.FileSize,
|
||||
DocumentType: documentType,
|
||||
ReferenceID: attachment.DiscussionID,
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown document type: %s", documentType)
|
||||
}
|
||||
|
||||
// Create metadata in database
|
||||
err := p.metadataRepo.Create(ctx, &metadata)
|
||||
if err != nil {
|
||||
// If it already exists (race condition), try to get it
|
||||
if strings.Contains(err.Error(), "duplicate key") {
|
||||
existingMetadata, getErr := p.metadataRepo.GetByDocumentID(ctx, documentID)
|
||||
if getErr == nil {
|
||||
// Get the file URL
|
||||
fileURL, urlErr := p.getDocumentURL(ctx, existingMetadata)
|
||||
if urlErr == nil {
|
||||
details.FileURL = fileURL
|
||||
}
|
||||
return &details, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("failed to create document metadata: %w", err)
|
||||
}
|
||||
|
||||
return &details, nil
|
||||
}
|
||||
|
||||
func (p *OnlyOfficeProcessorImpl) getDocumentURL(ctx context.Context, metadata *entities.DocumentMetadata) (string, error) {
|
||||
var fileURL string
|
||||
|
||||
switch metadata.DocumentType {
|
||||
case "letter_attachment":
|
||||
err := p.db.WithContext(ctx).
|
||||
Table("letter_incoming_attachments").
|
||||
Where("id = ?", metadata.ReferenceID).
|
||||
Select("file_url").
|
||||
Scan(&fileURL).Error
|
||||
return fileURL, err
|
||||
|
||||
case "outgoing_attachment":
|
||||
err := p.db.WithContext(ctx).
|
||||
Table("letter_outgoing_attachments").
|
||||
Where("id = ?", metadata.ReferenceID).
|
||||
Select("file_url").
|
||||
Scan(&fileURL).Error
|
||||
return fileURL, err
|
||||
|
||||
case "discussion_attachment":
|
||||
err := p.db.WithContext(ctx).
|
||||
Table("letter_outgoing_discussion_attachments").
|
||||
Where("id = ?", metadata.ReferenceID).
|
||||
Select("file_url").
|
||||
Scan(&fileURL).Error
|
||||
return fileURL, err
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("unknown document type: %s", metadata.DocumentType)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if string contains substring
|
||||
func contains(s, substr string) bool {
|
||||
return strings.Contains(s, substr)
|
||||
}
|
||||
Reference in New Issue
Block a user