dukcapil/internal/service/letter_service.go
2025-09-21 12:42:52 +07:00

789 lines
26 KiB
Go

package service
import (
"context"
"fmt"
"log"
"time"
"eslogad-be/internal/appcontext"
"eslogad-be/internal/constant"
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
"eslogad-be/internal/processor"
"eslogad-be/internal/repository"
"eslogad-be/internal/transformer"
"github.com/google/uuid"
)
const (
DefaultIncomingLetterID = "ESLI"
)
type LetterProcessor interface {
CreateIncomingLetter(ctx context.Context, req *contract.CreateIncomingLetterRequest) (*contract.IncomingLetterResponse, error)
GetIncomingLetterByID(ctx context.Context, id uuid.UUID) (*contract.IncomingLetterResponse, error)
ListIncomingLetters(ctx context.Context, filter repository.ListIncomingLettersFilter, page, limit int) ([]entities.LetterIncoming, int64, error)
GetLetterUnreadCounts(ctx context.Context) (*contract.LetterUnreadCountResponse, error)
MarkIncomingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error)
MarkOutgoingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error)
UpdateIncomingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateIncomingLetterRequest) (*contract.IncomingLetterResponse, error)
SoftDeleteIncomingLetter(ctx context.Context, id uuid.UUID) error
BulkArchiveIncomingLetters(ctx context.Context, letterIDs []uuid.UUID) (int64, error)
BulkArchiveIncomingLettersForUser(ctx context.Context, letterIDs []uuid.UUID, userID uuid.UUID) (int64, error)
// Batch loading methods
GetBatchAttachments(ctx context.Context, letterIDs []uuid.UUID) (map[uuid.UUID][]entities.LetterIncomingAttachment, error)
GetBatchPriorities(ctx context.Context, priorityIDs []uuid.UUID) (map[uuid.UUID]*entities.Priority, error)
GetBatchInstitutions(ctx context.Context, institutionIDs []uuid.UUID) (map[uuid.UUID]*entities.Institution, error)
GetBatchRecipientsByUser(ctx context.Context, letterIDs []uuid.UUID, userID uuid.UUID) (map[uuid.UUID]*entities.LetterIncomingRecipient, error)
CountUnreadByUser(ctx context.Context, userID uuid.UUID) (int, error)
CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error)
GetEnhancedDispositionsByLetter(ctx context.Context, letterID uuid.UUID) (*contract.ListEnhancedDispositionsResponse, error)
CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)
UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, string, error)
GetDepartmentDispositionStatus(ctx context.Context, req *contract.GetDepartmentDispositionStatusRequest) (*contract.ListDepartmentDispositionStatusResponse, error)
UpdateDispositionStatus(ctx context.Context, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error)
GetLetterCTA(ctx context.Context, letterID uuid.UUID, departmentID uuid.UUID) (*contract.LetterCTAResponse, error)
}
type LetterServiceImpl struct {
processor LetterProcessor
txManager *repository.TxManager
numberGenerator NumberGenerator
recipientProcessor RecipientProcessor
activityLogger ActivityLogger
letterDispositionProcessor LetterDispositionProcessor
notificationProcessor processor.NotificationProcessor
activityProcessor ActivityLogger
}
type NumberGenerator interface {
GenerateNumber(ctx context.Context, prefixKey, sequenceKey, defaultPrefix string) (string, error)
}
type RecipientProcessor interface {
CreateDefaultRecipients(ctx context.Context, letterID uuid.UUID) ([]entities.LetterIncomingRecipient, error)
CreateRecipients(ctx context.Context, letterID uuid.UUID, departmentIDs []uuid.UUID) ([]entities.LetterIncomingRecipient, error)
CreateSingleRecipient(ctx context.Context, recipient *entities.LetterIncomingRecipient) error
}
type ActivityLogger interface {
LogLetterCreated(ctx context.Context, letterID uuid.UUID, userID uuid.UUID, letterNumber string) error
LogAttachmentUploaded(ctx context.Context, letterID uuid.UUID, userID uuid.UUID, fileName string, fileType string) error
LogLetterDispositionStatusUpdate(ctx context.Context, letterID uuid.UUID, userID uuid.UUID, status string) error
}
type LetterDispositionProcessor interface {
CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error)
}
func NewLetterService(
processor LetterProcessor,
txManager *repository.TxManager,
numberGenerator NumberGenerator,
recipientProcessor RecipientProcessor,
activityLogger ActivityLogger,
letterDispositionProcessor LetterDispositionProcessor,
notificationProcessor processor.NotificationProcessor,
activityProc ActivityLogger,
) *LetterServiceImpl {
return &LetterServiceImpl{
processor: processor,
txManager: txManager,
numberGenerator: numberGenerator,
recipientProcessor: recipientProcessor,
activityLogger: activityLogger,
letterDispositionProcessor: letterDispositionProcessor,
notificationProcessor: notificationProcessor,
activityProcessor: activityProc,
}
}
func (s *LetterServiceImpl) CreateIncomingLetter(ctx context.Context, req *contract.CreateIncomingLetterRequest) (*contract.IncomingLetterResponse, error) {
var result *contract.IncomingLetterResponse
var recipients []entities.LetterIncomingRecipient
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
letterNumber, err := s.generateLetterNumber(txCtx)
if err != nil {
return err
}
req.LetterNumber = letterNumber
result, err = s.processor.CreateIncomingLetter(txCtx, req)
if err != nil {
return err
}
recipients, err = s.createDefaultRecipients(txCtx, result.ID)
if err != nil {
return err
}
if err := s.createDispositionsForRecipients(txCtx, result.ID, recipients); err != nil {
return err
}
s.logLetterCreation(txCtx, result.ID, letterNumber)
return nil
})
if err != nil {
return nil, err
}
if s.notificationProcessor != nil && len(recipients) > 0 {
go s.sendLetterNotifications(context.Background(), result, recipients)
}
return result, nil
}
func (s *LetterServiceImpl) generateLetterNumber(ctx context.Context) (string, error) {
return s.numberGenerator.GenerateNumber(
ctx,
contract.SettingIncomingLetterPrefix,
contract.SettingIncomingLetterSequence,
DefaultIncomingLetterID,
)
}
func (s *LetterServiceImpl) createDefaultRecipients(ctx context.Context, letterID uuid.UUID) ([]entities.LetterIncomingRecipient, error) {
return s.recipientProcessor.CreateDefaultRecipients(ctx, letterID)
}
func (s *LetterServiceImpl) createDispositionsForRecipients(ctx context.Context, letterID uuid.UUID, recipients []entities.LetterIncomingRecipient) error {
if len(recipients) == 0 || s.letterDispositionProcessor == nil {
return nil
}
departmentIDs := s.extractUniqueDepartmentIDs(recipients)
if len(departmentIDs) == 0 {
return nil
}
systemDeptID := constant.SystemDepartmentID
systemUserID := constant.SystemUserID
dispositionReq := &contract.CreateLetterDispositionRequest{
FromDepartment: systemDeptID,
LetterID: letterID,
ToDepartmentIDs: departmentIDs,
Notes: nil,
CreatedBy: systemUserID,
}
_, err := s.letterDispositionProcessor.CreateDispositions(ctx, dispositionReq)
return err
}
func (s *LetterServiceImpl) extractUniqueDepartmentIDs(recipients []entities.LetterIncomingRecipient) []uuid.UUID {
deptMap := make(map[uuid.UUID]bool)
var departmentIDs []uuid.UUID
for _, recipient := range recipients {
if recipient.RecipientDepartmentID != nil && !deptMap[*recipient.RecipientDepartmentID] {
deptMap[*recipient.RecipientDepartmentID] = true
departmentIDs = append(departmentIDs, *recipient.RecipientDepartmentID)
}
}
return departmentIDs
}
func (s *LetterServiceImpl) logLetterCreation(ctx context.Context, letterID uuid.UUID, letterNumber string) {
if s.activityLogger == nil {
return
}
userID := appcontext.FromGinContext(ctx).UserID
err := s.activityLogger.LogLetterCreated(ctx, letterID, userID, letterNumber)
if err != nil {
// Log error but don't fail the operation
}
}
func (s *LetterServiceImpl) addCreatorAsRecipient(ctx context.Context, letterID uuid.UUID, creatorID uuid.UUID) (*entities.LetterIncomingRecipient, error) {
// Check if creator is already a recipient (to avoid duplicates)
existingRecipients, err := s.processor.GetBatchRecipientsByUser(ctx, []uuid.UUID{letterID}, creatorID)
if err != nil {
return nil, err
}
// If creator is already a recipient, skip
if _, exists := existingRecipients[letterID]; exists {
return nil, nil
}
// Create recipient entry for the creator
recipient := entities.LetterIncomingRecipient{
ID: uuid.New(),
LetterID: letterID,
RecipientUserID: &creatorID,
Status: entities.RecipientStatusNew,
CreatedAt: time.Now(),
}
// Save the recipient
if err := s.recipientProcessor.CreateSingleRecipient(ctx, &recipient); err != nil {
// Failed to add creator as recipient
return nil, err
}
return &recipient, nil
}
func (s *LetterServiceImpl) sendLetterNotifications(ctx context.Context, letter *contract.IncomingLetterResponse, recipients []entities.LetterIncomingRecipient) {
for _, recipient := range recipients {
if recipient.Status != "completed" {
err := s.notificationProcessor.SendIncomingLetterNotification(
ctx,
letter.ID,
*recipient.RecipientUserID,
"Surat Masuk",
fmt.Sprintf("%s: %s", letter.SenderInstitution.Name, letter.Subject))
if err != nil {
// Failed to send notification, continue anyway
}
}
}
}
func (s *LetterServiceImpl) sendDispositionNotifications(ctx context.Context, letterID uuid.UUID, recipients []entities.LetterIncomingRecipient) {
// Get letter details for notification
appContext := appcontext.FromGinContext(ctx)
letter, err := s.processor.GetIncomingLetterByID(ctx, letterID)
if err != nil {
return
}
for _, recipient := range recipients {
if recipient.RecipientUserID != nil && recipient.Status != entities.RecipientStatusCompleted {
subject := "Surat Masuk"
message := fmt.Sprintf("Disposisi surat dari %s: %s", appContext.UserName, letter.Subject)
err := s.notificationProcessor.SendIncomingLetterNotification(
ctx,
letterID,
*recipient.RecipientUserID,
subject,
message)
if err != nil {
// Failed to send notification, continue anyway
}
}
}
}
func (s *LetterServiceImpl) GetIncomingLetterByID(ctx context.Context, id uuid.UUID) (*contract.IncomingLetterResponse, error) {
return s.processor.GetIncomingLetterByID(ctx, id)
}
func (s *LetterServiceImpl) ListIncomingLetters(ctx context.Context, req *contract.ListIncomingLettersRequest) (*contract.ListIncomingLettersResponse, error) {
appCtx := appcontext.FromGinContext(ctx)
userID := appCtx.UserID
departmentID := appCtx.DepartmentID
filter := repository.ListIncomingLettersFilter{
Status: req.Status,
Query: req.Query,
DepartmentID: &departmentID,
UserID: &userID,
IsRead: req.IsRead,
PriorityIDs: req.PriorityIDs,
IsDispositioned: req.IsDispositioned,
IsArchived: req.IsArchived,
}
letters, total, err := s.processor.ListIncomingLetters(ctx, filter, req.Page, req.Limit)
if err != nil {
return nil, err
}
if len(letters) == 0 {
return &contract.ListIncomingLettersResponse{
Letters: []contract.IncomingLetterResponse{},
Pagination: transformer.CreatePaginationResponse(int(total), req.Page, req.Limit),
TotalUnread: 0,
}, nil
}
letterIDs := make([]uuid.UUID, 0, len(letters))
priorityIDSet := make(map[uuid.UUID]bool)
institutionIDSet := make(map[uuid.UUID]bool)
for _, letter := range letters {
letterIDs = append(letterIDs, letter.ID)
if letter.PriorityID != nil {
priorityIDSet[*letter.PriorityID] = true
}
if letter.SenderInstitutionID != nil {
institutionIDSet[*letter.SenderInstitutionID] = true
}
}
priorityIDs := make([]uuid.UUID, 0, len(priorityIDSet))
for id := range priorityIDSet {
priorityIDs = append(priorityIDs, id)
}
institutionIDs := make([]uuid.UUID, 0, len(institutionIDSet))
for id := range institutionIDSet {
institutionIDs = append(institutionIDs, id)
}
type batchResult struct {
attachments map[uuid.UUID][]entities.LetterIncomingAttachment
priorities map[uuid.UUID]*entities.Priority
institutions map[uuid.UUID]*entities.Institution
recipients map[uuid.UUID]*entities.LetterIncomingRecipient
err error
}
resultChan := make(chan batchResult, 1)
go func() {
result := batchResult{
attachments: make(map[uuid.UUID][]entities.LetterIncomingAttachment),
priorities: make(map[uuid.UUID]*entities.Priority),
institutions: make(map[uuid.UUID]*entities.Institution),
recipients: make(map[uuid.UUID]*entities.LetterIncomingRecipient),
}
errChan := make(chan error, 4)
go func() {
var err error
result.attachments, err = s.processor.GetBatchAttachments(ctx, letterIDs)
errChan <- err
}()
go func() {
var err error
result.priorities, err = s.processor.GetBatchPriorities(ctx, priorityIDs)
errChan <- err
}()
go func() {
var err error
result.institutions, err = s.processor.GetBatchInstitutions(ctx, institutionIDs)
errChan <- err
}()
go func() {
var err error
result.recipients, err = s.processor.GetBatchRecipientsByUser(ctx, letterIDs, userID)
errChan <- err
}()
for i := 0; i < 4; i++ {
if err := <-errChan; err != nil {
// Batch load error, continue anyway
}
}
resultChan <- result
}()
batchData := <-resultChan
respList := make([]contract.IncomingLetterResponse, 0, len(letters))
for _, letter := range letters {
attachments := batchData.attachments[letter.ID]
if attachments == nil {
attachments = []entities.LetterIncomingAttachment{}
}
var priority *entities.Priority
if letter.PriorityID != nil {
priority = batchData.priorities[*letter.PriorityID]
}
var institution *entities.Institution
if letter.SenderInstitutionID != nil {
institution = batchData.institutions[*letter.SenderInstitutionID]
}
isRead := false
if recipient, exists := batchData.recipients[letter.ID]; exists && recipient != nil {
isRead = recipient.ReadAt != nil
}
resp := transformer.LetterEntityToContract(&letter, attachments, priority, institution)
resp.IsRead = isRead
respList = append(respList, *resp)
}
totalUnread, _ := s.processor.CountUnreadByUser(ctx, userID)
return &contract.ListIncomingLettersResponse{
Letters: respList,
Pagination: transformer.CreatePaginationResponse(int(total), req.Page, req.Limit),
TotalUnread: totalUnread,
}, nil
}
func (s *LetterServiceImpl) GetLetterUnreadCounts(ctx context.Context) (*contract.LetterUnreadCountResponse, error) {
return s.processor.GetLetterUnreadCounts(ctx)
}
func (s *LetterServiceImpl) MarkIncomingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error) {
return s.processor.MarkIncomingLetterAsRead(ctx, letterID)
}
func (s *LetterServiceImpl) MarkOutgoingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error) {
return s.processor.MarkOutgoingLetterAsRead(ctx, letterID)
}
func (s *LetterServiceImpl) UpdateIncomingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateIncomingLetterRequest) (*contract.IncomingLetterResponse, error) {
return s.processor.UpdateIncomingLetter(ctx, id, req)
}
func (s *LetterServiceImpl) SoftDeleteIncomingLetter(ctx context.Context, id uuid.UUID) error {
return s.processor.SoftDeleteIncomingLetter(ctx, id)
}
func (s *LetterServiceImpl) CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error) {
log.Printf("[DEBUG] CreateDispositions START - LetterID: %s\n", req.LetterID.String())
userID := appcontext.FromGinContext(ctx).UserID
req.CreatedBy = userID
if req.FromDepartment == uuid.Nil {
req.FromDepartment = appcontext.FromGinContext(ctx).DepartmentID
}
var result *contract.ListDispositionsResponse
var recipients []entities.LetterIncomingRecipient
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
var err error
result, err = s.processor.CreateDispositions(txCtx, req)
if err != nil {
return err
}
if len(req.ToDepartmentIDs) > 0 && s.recipientProcessor != nil {
recipients, err = s.recipientProcessor.CreateRecipients(txCtx, req.LetterID, req.ToDepartmentIDs)
if err != nil {
return err
}
}
if s.activityLogger != nil && result != nil && len(result.Dispositions) > 0 {
if err := s.activityLogger.LogLetterDispositionStatusUpdate(txCtx, req.LetterID, userID, "disposition_created"); err != nil {
}
}
return nil
})
if err != nil {
return nil, err
}
// Send notifications to newly created recipients asynchronously
if s.notificationProcessor != nil {
// Send notifications to newly created recipients
if len(recipients) > 0 {
go s.sendDispositionNotifications(context.Background(), req.LetterID, recipients)
}
// Send notification to letter creator about new disposition
go s.sendDispositionCreatorNotification(context.Background(), req.LetterID, userID)
}
return result, nil
}
func (s *LetterServiceImpl) GetEnhancedDispositionsByLetter(ctx context.Context, letterID uuid.UUID) (*contract.ListEnhancedDispositionsResponse, error) {
return s.processor.GetEnhancedDispositionsByLetter(ctx, letterID)
}
func (s *LetterServiceImpl) CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error) {
userID := appcontext.FromGinContext(ctx).UserID
var result *contract.LetterDiscussionResponse
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
var err error
result, err = s.processor.CreateDiscussion(txCtx, letterID, req)
if err != nil {
return err
}
// Log activity for discussion creation
if s.activityLogger != nil && result != nil {
if err := s.activityLogger.LogLetterDispositionStatusUpdate(txCtx, letterID, userID, "discussion_created"); err != nil {
// Don't fail the transaction for logging errors
}
}
return nil
})
if err != nil {
return nil, err
}
// Send notifications to mentioned users asynchronously
if s.notificationProcessor != nil && req.Mentions != nil {
go s.sendDiscussionMentionNotifications(context.Background(), letterID, userID, req.Mentions, req.Message)
}
return result, nil
}
func (s *LetterServiceImpl) UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error) {
userID := appcontext.FromGinContext(ctx).UserID
var result *contract.LetterDiscussionResponse
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
var err error
var oldMessage string
result, oldMessage, err = s.processor.UpdateDiscussion(txCtx, letterID, discussionID, req)
if err != nil {
return err
}
// Log activity for discussion update (could use oldMessage for more detailed logging)
if s.activityLogger != nil && result != nil {
// Create a simple activity log - oldMessage could be included in a more detailed log
_ = oldMessage // Mark as intentionally unused for now
if err := s.activityLogger.LogLetterDispositionStatusUpdate(txCtx, letterID, userID, "discussion_updated"); err != nil {
// Don't fail the transaction for logging errors
}
}
return nil
})
if err != nil {
return nil, err
}
return result, nil
}
func (s *LetterServiceImpl) GetDepartmentDispositionStatus(ctx context.Context, req *contract.GetDepartmentDispositionStatusRequest) (*contract.ListDepartmentDispositionStatusResponse, error) {
return s.processor.GetDepartmentDispositionStatus(ctx, req)
}
func (s *LetterServiceImpl) UpdateDispositionStatus(ctx context.Context, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error) {
var result *contract.DepartmentDispositionStatusResponse
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
var err error
result, err = s.processor.UpdateDispositionStatus(txCtx, req)
if err != nil {
return err
}
// Log activity for disposition status update
if s.activityLogger != nil && result != nil {
userID := appcontext.FromGinContext(txCtx).UserID
if err := s.activityLogger.LogLetterDispositionStatusUpdate(txCtx, req.LetterIncomingID, userID, req.Status); err != nil {
// Don't fail the transaction for logging errors
}
}
return nil
})
if err != nil {
return nil, err
}
// Send notification to letter creator asynchronously
if s.notificationProcessor != nil && result != nil {
go s.sendDispositionStatusUpdateNotification(context.Background(), req.LetterIncomingID, req.Status)
}
return result, nil
}
func (s *LetterServiceImpl) GetLetterCTA(ctx context.Context, letterID uuid.UUID) (*contract.LetterCTAResponse, error) {
departmentID := appcontext.FromGinContext(ctx).DepartmentID
return s.processor.GetLetterCTA(ctx, letterID, departmentID)
}
func (s *LetterServiceImpl) BulkArchiveIncomingLetters(ctx context.Context, letterIDs []uuid.UUID) (*contract.BulkArchiveLettersResponse, error) {
// Extract user context to archive only for the current user
appCtx := appcontext.FromGinContext(ctx)
userID := appCtx.UserID
// Archive letters only for the current user
archivedCount, err := s.processor.BulkArchiveIncomingLettersForUser(ctx, letterIDs, userID)
if err != nil {
return nil, err
}
return &contract.BulkArchiveLettersResponse{
Success: true,
Message: "Letters archived successfully",
ArchivedCount: int(archivedCount),
}, nil
}
func (s *LetterServiceImpl) sendDiscussionMentionNotifications(ctx context.Context, letterID uuid.UUID, senderUserID uuid.UUID, mentions map[string]interface{}, message string) {
// Extract user_ids from mentions
userIDs := s.extractUserIDsFromMentions(mentions)
if len(userIDs) == 0 {
return
}
// Get letter details for notification
letter, err := s.processor.GetIncomingLetterByID(ctx, letterID)
if err != nil {
return
}
// Get sender user name (you might need to implement this)
appContext := appcontext.FromGinContext(ctx)
senderName := appContext.UserName // or get from user service
// Send notification to each mentioned user
for _, mentionedUserID := range userIDs {
// Don't send notification to the sender themselves
if mentionedUserID == senderUserID {
continue
}
subject := "Anda Disebutkan dalam Diskusi"
notificationMessage := fmt.Sprintf("%s menyebutkan Anda dalam diskusi surat: %s", senderName, letter.Subject)
err := s.notificationProcessor.SendIncomingLetterNotification(
ctx,
letterID,
mentionedUserID,
subject,
notificationMessage)
if err != nil {
// Log error but continue with other notifications
}
}
}
func (s *LetterServiceImpl) extractUserIDsFromMentions(mentions map[string]interface{}) []uuid.UUID {
userIDs := make([]uuid.UUID, 0)
if userIDsInterface, exists := mentions["user_ids"]; exists {
switch userIDsValue := userIDsInterface.(type) {
case []interface{}:
for _, userIDInterface := range userIDsValue {
if userIDStr, ok := userIDInterface.(string); ok {
if userID, err := uuid.Parse(userIDStr); err == nil {
userIDs = append(userIDs, userID)
}
}
}
case []string:
for _, userIDStr := range userIDsValue {
if userID, err := uuid.Parse(userIDStr); err == nil {
userIDs = append(userIDs, userID)
}
}
}
}
return userIDs
}
func (s *LetterServiceImpl) sendDispositionCreatorNotification(ctx context.Context, letterID uuid.UUID, dispositionCreatorID uuid.UUID) {
// Get letter details
letter, err := s.processor.GetIncomingLetterByID(ctx, letterID)
if err != nil {
return
}
fmt.Printf("[DEBUG] Starting sendDispositionCreatorNotification for letterID: %s\n", letterID.String())
fmt.Printf("[DEBUG] Successfully retrieved letter: %s\n", letter.Subject)
fmt.Printf("[DEBUG] Successfully retrieved letter: %s\n", letter.CreatedBy)
letterCreatorID := letter.CreatedBy
// Don't send notification if the disposition creator is the same as letter creator
if letterCreatorID == dispositionCreatorID {
return
}
// Get disposition creator name from context
appContext := appcontext.FromGinContext(ctx)
dispositionCreatorName := appContext.UserName
subject := "Disposisi Baru pada Surat Anda"
message := fmt.Sprintf("Surat yang Anda buat telah didisposisikan %s: %s",
dispositionCreatorName, letter.Subject)
err = s.notificationProcessor.SendIncomingLetterNotification(
ctx,
letterID,
letterCreatorID,
subject,
message)
if err != nil {
// Log error but don't fail the operation
// You might want to add proper logging here
}
}
func (s *LetterServiceImpl) sendDispositionStatusUpdateNotification(ctx context.Context, letterID uuid.UUID, newStatus string) {
// Get letter details
letter, err := s.processor.GetIncomingLetterByID(ctx, letterID)
if err != nil {
// Log error but don't fail
return
}
// Get current user context (the one updating the status)
appContext := appcontext.FromGinContext(ctx)
updaterUserID := appContext.UserID
updaterName := appContext.UserName
letterCreatorID := letter.CreatedBy
// Don't send notification if the updater is the same as letter creator
if letterCreatorID == updaterUserID {
return
}
// Create status-specific notification message
var statusMessage string
switch newStatus {
case "pending":
statusMessage = "sedang menunggu"
case "in_progress":
statusMessage = "sedang diproses"
case "completed":
statusMessage = "telah diselesaikan"
case "cancelled":
statusMessage = "dibatalkan"
default:
statusMessage = fmt.Sprintf("diubah statusnya menjadi %s", newStatus)
}
subject := "Status Disposisi Surat Diperbarui"
message := fmt.Sprintf("Disposisi surat '%s' %s %s",
letter.Subject, statusMessage, updaterName)
err = s.notificationProcessor.SendIncomingLetterNotification(
ctx,
letterID,
letterCreatorID,
subject,
message)
if err != nil {
// Log error but don't fail the operation
// You might want to add proper logging here
}
}