implement archived

This commit is contained in:
Aditya Siregar 2025-09-21 19:21:12 +07:00
parent ca2acfd850
commit 90da195a2e

View File

@ -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
}