From 90da195a2e0ad1315589042a449a609c63dc8396 Mon Sep 17 00:00:00 2001 From: Aditya Siregar Date: Sun, 21 Sep 2025 19:21:12 +0700 Subject: [PATCH] implement archived --- internal/service/letter_outgoing_service.go | 116 ++++++++++---------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/internal/service/letter_outgoing_service.go b/internal/service/letter_outgoing_service.go index 777c749..af02f9c 100644 --- a/internal/service/letter_outgoing_service.go +++ b/internal/service/letter_outgoing_service.go @@ -54,15 +54,15 @@ type LetterOutgoingService interface { } type LetterOutgoingServiceImpl struct { - processor processor.LetterOutgoingProcessor - txManager *repository.TxManager - validationProcessor processor.LetterValidationProcessor - creationProcessor processor.LetterCreationProcessor - approvalProcessor processor.LetterApprovalProcessor - attachmentProcessor processor.LetterAttachmentProcessor - recipientProcessor processor.LetterOutgoingRecipientProcessor + processor processor.LetterOutgoingProcessor + txManager *repository.TxManager + validationProcessor processor.LetterValidationProcessor + creationProcessor processor.LetterCreationProcessor + approvalProcessor processor.LetterApprovalProcessor + attachmentProcessor processor.LetterAttachmentProcessor + recipientProcessor processor.LetterOutgoingRecipientProcessor notificationProcessor processor.NotificationProcessor - activityProcessor processor.LetterActivityProcessor + activityProcessor processor.LetterActivityProcessor } func NewLetterOutgoingService( @@ -77,15 +77,15 @@ func NewLetterOutgoingService( activityProcessor processor.LetterActivityProcessor, ) *LetterOutgoingServiceImpl { return &LetterOutgoingServiceImpl{ - processor: processor, - txManager: txManager, - validationProcessor: validationProcessor, - creationProcessor: creationProcessor, - approvalProcessor: approvalProcessor, - attachmentProcessor: attachmentProcessor, - recipientProcessor: recipientProcessor, + processor: processor, + txManager: txManager, + validationProcessor: validationProcessor, + creationProcessor: creationProcessor, + approvalProcessor: approvalProcessor, + attachmentProcessor: attachmentProcessor, + recipientProcessor: recipientProcessor, notificationProcessor: notificationProcessor, - activityProcessor: activityProcessor, + activityProcessor: activityProcessor, } } @@ -172,7 +172,7 @@ func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, re return nil, err } - // Send notifications if letter needs approval + // Send notifications if letter needs approval log.Printf("[DEBUG] createOutgoingLetter Finsig") log.Printf("[DEBUG] NotificationProcessor is nil: %v", s.notificationProcessor == nil) if s.notificationProcessor != nil && len(result.Approvals) > 0 { @@ -180,7 +180,6 @@ func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, re go s.sendStepApprovalNotifications(context.Background(), result.ID, result.Subject, 1) } - return transformLetterToResponse(result), nil } @@ -242,7 +241,11 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req } } - filter.IsArchived = req.IsArchived + archived := true + filter.IsArchived = &archived + if filter.IsArchived != nil { + filter.IsArchived = req.IsArchived + } // Get raw letters data letters, total, err := s.processor.ListOutgoingLetters(ctx, filter, req.Limit, offset) @@ -254,7 +257,7 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req letterIDs := make([]uuid.UUID, len(letters)) priorityIDs := make(map[uuid.UUID]bool) institutionIDs := make(map[uuid.UUID]bool) - + for i, letter := range letters { letterIDs[i] = letter.ID if letter.PriorityID != nil { @@ -270,7 +273,7 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req for id := range priorityIDs { priorityIDSlice = append(priorityIDSlice, id) } - + institutionIDSlice := make([]uuid.UUID, 0, len(institutionIDs)) for id := range institutionIDs { institutionIDSlice = append(institutionIDSlice, id) @@ -287,31 +290,31 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req result := batchResult{} errChan := make(chan error, 4) - + // Load attachments go func() { result.attachments, err = s.processor.GetBatchAttachments(ctx, letterIDs) errChan <- err }() - + // Load recipients go func() { result.recipients, err = s.processor.GetBatchRecipients(ctx, letterIDs) errChan <- err }() - + // Load priorities go func() { result.priorities, err = s.processor.GetBatchPriorities(ctx, priorityIDSlice) errChan <- err }() - + // Load institutions go func() { result.institutions, err = s.processor.GetBatchInstitutions(ctx, institutionIDSlice) errChan <- err }() - + // Wait for all goroutines and check for errors for i := 0; i < 4; i++ { if err := <-errChan; err != nil { @@ -339,7 +342,7 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req letter.ReceiverInstitution = institution } } - + items[i] = transformLetterToResponse(&letter) } @@ -479,12 +482,12 @@ func (s *LetterOutgoingServiceImpl) ApproveOutgoingLetter(ctx context.Context, l if s.notificationProcessor != nil { // Step approved but not final - notify creator about step completion AND next approvers creatorMessage := fmt.Sprintf("Surat keluar '%s' telah disetujui pada tahap %d, menunggu persetujuan tahap berikutnya", letter.Subject, currentApproval.StepOrder) - go s.sendApprovalNotificationToCreator(context.Background(), letterID, letter.CreatedBy, "Surat Keluar Disetujui Tahap " + fmt.Sprintf("%d", currentApproval.StepOrder), creatorMessage) - + go s.sendApprovalNotificationToCreator(context.Background(), letterID, letter.CreatedBy, "Surat Keluar Disetujui Tahap "+fmt.Sprintf("%d", currentApproval.StepOrder), creatorMessage) + // Notify next step approvers nextStepOrder := currentApproval.StepOrder + 1 go s.sendStepApprovalNotifications(context.Background(), letterID, letter.Subject, nextStepOrder) - + } return nil @@ -734,7 +737,6 @@ func (s *LetterOutgoingServiceImpl) CreateDiscussion(ctx context.Context, letter go s.sendOutgoingDiscussionMentionNotifications(context.Background(), letterID, userID, req.Mentions, req.Message) } - return transformDiscussionToResponse(result), nil } @@ -1390,7 +1392,7 @@ func (s *LetterOutgoingServiceImpl) GetApprovalTimeline(ctx context.Context, let eventType := "approval" action := "approved" status := "approved" - + if approval.Status == "rejected" { eventType = "rejection" action = "rejected" @@ -1399,7 +1401,7 @@ func (s *LetterOutgoingServiceImpl) GetApprovalTimeline(ctx context.Context, let continue // Skip pending approvals as they haven't happened yet } - description := fmt.Sprintf("Step %d: %s by %s", + description := fmt.Sprintf("Step %d: %s by %s", approval.StepOrder, action, getApproverName(approval.Approver)) @@ -1461,7 +1463,7 @@ func (s *LetterOutgoingServiceImpl) calculateTimelineSummary( completedSteps := 0 pendingSteps := 0 currentStep := 0 - + // Count unique step orders stepMap := make(map[int]string) for _, approval := range approvals { @@ -1469,7 +1471,7 @@ func (s *LetterOutgoingServiceImpl) calculateTimelineSummary( stepMap[approval.StepOrder] = approval.Status totalSteps++ } - + switch approval.Status { case "approved": if stepMap[approval.StepOrder] == "approved" { @@ -1487,12 +1489,12 @@ func (s *LetterOutgoingServiceImpl) calculateTimelineSummary( // Calculate duration totalDuration := "" averageStepTime := "" - + if len(timeline) > 0 { lastEvent := timeline[len(timeline)-1] duration := lastEvent.Timestamp.Sub(letter.CreatedAt) totalDuration = formatDuration(duration) - + if completedSteps > 0 { avgDuration := duration / time.Duration(completedSteps) averageStepTime = formatDuration(avgDuration) @@ -1548,7 +1550,7 @@ func formatDuration(d time.Duration) string { days := int(d.Hours() / 24) hours := int(d.Hours()) % 24 minutes := int(d.Minutes()) % 60 - + if days > 0 { return fmt.Sprintf("%dd %dh %dm", days, hours, minutes) } else if hours > 0 { @@ -1578,7 +1580,7 @@ func (s *LetterOutgoingServiceImpl) BulkArchiveOutgoingLetters(ctx context.Conte if err != nil { return nil, err } - + return &contract.BulkArchiveLettersResponse{ Success: true, Message: "Letters archived successfully", @@ -1588,7 +1590,7 @@ func (s *LetterOutgoingServiceImpl) BulkArchiveOutgoingLetters(ctx context.Conte func (s *LetterOutgoingServiceImpl) sendStepApprovalNotifications(ctx context.Context, letterID uuid.UUID, subject string, stepOrder int) { log.Printf("[DEBUG] sendStepApprovalNotifications START - LetterID: %s, StepOrder: %d", letterID.String(), stepOrder) - + approvals, err := s.processor.GetApprovalsByLetter(ctx, letterID) if err != nil { log.Printf("[ERROR] Failed to get approvals: %v", err) @@ -1596,15 +1598,15 @@ func (s *LetterOutgoingServiceImpl) sendStepApprovalNotifications(ctx context.Co } log.Printf("[DEBUG] Found %d approvals", len(approvals)) - + // Find approvers for the specified step for _, approval := range approvals { - log.Printf("[DEBUG] Checking approval: Step=%d, Status=%s, ApproverID=%v", + log.Printf("[DEBUG] Checking approval: Step=%d, Status=%s, ApproverID=%v", approval.StepOrder, approval.Status, approval.ApproverID) - - if approval.StepOrder == stepOrder { + + if approval.StepOrder == stepOrder { log.Printf("[DEBUG] Sending notification to approver %s for step %d", approval.ApproverID.String(), stepOrder) - + err := s.notificationProcessor.SendOutgoingLetterNotification( ctx, letterID, @@ -1624,7 +1626,7 @@ func (s *LetterOutgoingServiceImpl) sendStepApprovalNotifications(ctx context.Co // Kirim notifikasi ke creator func (s *LetterOutgoingServiceImpl) sendApprovalNotificationToCreator(ctx context.Context, letterID uuid.UUID, creatorID uuid.UUID, title string, message string) { log.Printf("[DEBUG] sendApprovalNotificationToCreator START - LetterID: %s, CreatorID: %s", letterID.String(), creatorID.String()) - + err := s.notificationProcessor.SendOutgoingLetterNotification( ctx, letterID, @@ -1641,47 +1643,47 @@ func (s *LetterOutgoingServiceImpl) sendApprovalNotificationToCreator(ctx contex func (s *LetterOutgoingServiceImpl) sendOutgoingDiscussionMentionNotifications(ctx context.Context, letterID uuid.UUID, senderUserID uuid.UUID, mentions map[string]interface{}, message string) { log.Printf("[DEBUG] sendOutgoingDiscussionMentionNotifications START - LetterID: %s", letterID.String()) - + // Extract user_ids dari mentions userIDs := s.extractUserIDsFromMentions(mentions) if len(userIDs) == 0 { log.Printf("[DEBUG] No user IDs found in mentions") return } - + log.Printf("[DEBUG] Found %d mentioned users", len(userIDs)) - + // Get letter details untuk notification letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID) if err != nil { log.Printf("[ERROR] Failed to get letter details: %v", err) return } - + // Get sender user name dari context (bisa juga dari user service) appContext := appcontext.FromGinContext(ctx) senderName := appContext.UserName if senderName == "" { senderName = "Seseorang" // fallback jika nama tidak tersedia } - + // Kirim notification ke setiap mentioned user for _, mentionedUserID := range userIDs { // Jangan kirim notification ke sender sendiri if mentionedUserID == senderUserID { continue } - + subject := "Anda Disebutkan dalam Diskusi Surat Keluar" notificationMessage := fmt.Sprintf("%s menyebutkan Anda dalam diskusi surat keluar: %s", senderName, letter.Subject) - + err := s.notificationProcessor.SendOutgoingLetterNotification( ctx, letterID, mentionedUserID, subject, notificationMessage) - + if err != nil { log.Printf("[ERROR] Failed to send mention notification to user %s: %v", mentionedUserID.String(), err) } else { @@ -1693,11 +1695,11 @@ func (s *LetterOutgoingServiceImpl) sendOutgoingDiscussionMentionNotifications(c // Helper function untuk extract user IDs dari mentions map func (s *LetterOutgoingServiceImpl) extractUserIDsFromMentions(mentions map[string]interface{}) []uuid.UUID { userIDs := make([]uuid.UUID, 0) - + if mentions == nil { return userIDs } - + if userIDsInterface, exists := mentions["user_ids"]; exists { switch userIDsValue := userIDsInterface.(type) { case []interface{}: @@ -1718,6 +1720,6 @@ func (s *LetterOutgoingServiceImpl) extractUserIDsFromMentions(mentions map[stri userIDs = userIDsValue } } - + return userIDs } \ No newline at end of file