From 67b37a9c1ed2975fe695667c591b372ba6c74c15 Mon Sep 17 00:00:00 2001 From: efrilm Date: Thu, 30 Oct 2025 12:40:32 +0700 Subject: [PATCH] bulk delete outgoing letter --- internal/handler/letter_outgoing_handler.go | 29 ++++++++++++++- .../processor/letter_outgoing_processor.go | 32 +++++++++++++++- .../repository/letter_outgoing_repository.go | 10 +++++ internal/router/health_handler.go | 1 + internal/router/router.go | 1 + internal/service/letter_outgoing_service.go | 37 +++++++++++++++---- 6 files changed, 100 insertions(+), 10 deletions(-) diff --git a/internal/handler/letter_outgoing_handler.go b/internal/handler/letter_outgoing_handler.go index 40a4657..aa1b925 100644 --- a/internal/handler/letter_outgoing_handler.go +++ b/internal/handler/letter_outgoing_handler.go @@ -19,6 +19,7 @@ type LetterOutgoingService interface { SearchOutgoingLetters(ctx context.Context, req *contract.SearchOutgoingLettersRequest) (*contract.SearchOutgoingLettersResponse, error) UpdateOutgoingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) DeleteOutgoingLetter(ctx context.Context, id uuid.UUID) error + BulkDeleteOutgoingLetters(ctx context.Context, ids []uuid.UUID) error SubmitForApproval(ctx context.Context, letterID uuid.UUID) error ApproveOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.ApproveLetterRequest) error @@ -158,7 +159,33 @@ func (h *LetterOutgoingHandler) DeleteOutgoingLetter(c *gin.Context) { return } - c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "deleted"}) + c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{Message: "deleted"})) +} + +func (h *LetterOutgoingHandler) BulkDeleteOutgoingLetters(c *gin.Context) { + var req struct { + IDs []uuid.UUID `json:"ids" binding:"required,min=1"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, &contract.ErrorResponse{ + Error: "Invalid request body", + Code: http.StatusBadRequest, + }) + return + } + + if err := h.svc.BulkDeleteOutgoingLetters(c.Request.Context(), req.IDs); err != nil { + c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{ + Error: err.Error(), + Code: http.StatusInternalServerError, + }) + return + } + + c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{ + Message: fmt.Sprintf("%d letters deleted successfully", len(req.IDs)), + })) } func (h *LetterOutgoingHandler) SubmitForApproval(c *gin.Context) { diff --git a/internal/processor/letter_outgoing_processor.go b/internal/processor/letter_outgoing_processor.go index 17780c3..a14cf46 100644 --- a/internal/processor/letter_outgoing_processor.go +++ b/internal/processor/letter_outgoing_processor.go @@ -23,6 +23,7 @@ type LetterOutgoingProcessor interface { SearchOutgoingLetters(ctx context.Context, filters map[string]interface{}, page, limit int, sortBy, sortOrder string) ([]entities.LetterOutgoing, int64, error) UpdateOutgoingLetter(ctx context.Context, letter *entities.LetterOutgoing, userID uuid.UUID) error DeleteOutgoingLetter(ctx context.Context, id uuid.UUID, userID uuid.UUID) error + BulkDeleteOutgoingLetters(ctx context.Context, ids []uuid.UUID, userID uuid.UUID) error UpdateLetterStatus(ctx context.Context, letterID uuid.UUID, status entities.LetterOutgoingStatus, userID uuid.UUID, fromStatus, toStatus *string) error ArchiveOutgoingLetter(ctx context.Context, letterID uuid.UUID, userID uuid.UUID) error @@ -349,15 +350,42 @@ func (p *LetterOutgoingProcessorImpl) DeleteOutgoingLetter(ctx context.Context, return err } - letter, _ := p.letterRepo.Get(txCtx, id) activityLog := &entities.LetterOutgoingActivityLog{ - LetterID: letter.ID, + LetterID: id, ActionType: entities.LetterOutgoingActionDeleted, ActorUserID: &userID, } + if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil { return err } + + return nil + }) +} + +func (p *LetterOutgoingProcessorImpl) BulkDeleteOutgoingLetters(ctx context.Context, ids []uuid.UUID, userID uuid.UUID) error { + if len(ids) == 0 { + return nil + } + + return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error { + + // Create activity log untuk setiap letter yang dihapus + for _, id := range ids { + activityLog := &entities.LetterOutgoingActivityLog{ + LetterID: id, + ActionType: entities.LetterOutgoingActionDeleted, + ActorUserID: &userID, + } + if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil { + return err + } + } + + if err := p.letterRepo.BulkSoftDelete(txCtx, ids); err != nil { + return err + } return nil }) diff --git a/internal/repository/letter_outgoing_repository.go b/internal/repository/letter_outgoing_repository.go index 5d7a23d..dd68e86 100644 --- a/internal/repository/letter_outgoing_repository.go +++ b/internal/repository/letter_outgoing_repository.go @@ -71,6 +71,16 @@ func (r *LetterOutgoingRepository) SoftDelete(ctx context.Context, id uuid.UUID) return db.WithContext(ctx).Model(&entities.LetterOutgoing{}).Where("id = ? AND deleted_at IS NULL", id).Update("deleted_at", now).Error } +func (r *LetterOutgoingRepository) BulkSoftDelete(ctx context.Context, ids []uuid.UUID) error { + if len(ids) == 0 { + return nil + } + + db := DBFromContext(ctx, r.db) + now := time.Now() + return db.WithContext(ctx).Model(&entities.LetterOutgoing{}).Where("id IN ? AND deleted_at IS NULL", ids).Update("deleted_at", now).Error +} + func (r *LetterOutgoingRepository) BulkArchive(ctx context.Context, letterIDs []uuid.UUID) (int64, error) { db := DBFromContext(ctx, r.db) now := time.Now() diff --git a/internal/router/health_handler.go b/internal/router/health_handler.go index 8d64d03..11664c8 100644 --- a/internal/router/health_handler.go +++ b/internal/router/health_handler.go @@ -103,6 +103,7 @@ type LetterOutgoingHandler interface { SearchOutgoingLetters(c *gin.Context) UpdateOutgoingLetter(c *gin.Context) DeleteOutgoingLetter(c *gin.Context) + BulkDeleteOutgoingLetters(c *gin.Context) SubmitForApproval(c *gin.Context) ApproveOutgoingLetter(c *gin.Context) diff --git a/internal/router/router.go b/internal/router/router.go index 266457d..9ed3add 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -193,6 +193,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) { lettersch.PUT("/outgoing/:id", r.letterOutgoingHandler.UpdateOutgoingLetter) lettersch.PUT("/outgoing/:id/read", r.letterHandler.MarkOutgoingLetterAsRead) lettersch.DELETE("/outgoing/:id", r.letterOutgoingHandler.DeleteOutgoingLetter) + lettersch.DELETE("/outgoing/delete", r.letterOutgoingHandler.BulkDeleteOutgoingLetters) lettersch.POST("/outgoing/:id/submit", r.letterOutgoingHandler.SubmitForApproval) lettersch.POST("/outgoing/:id/approve", r.letterOutgoingHandler.ApproveOutgoingLetter) diff --git a/internal/service/letter_outgoing_service.go b/internal/service/letter_outgoing_service.go index 24f01d4..367f540 100644 --- a/internal/service/letter_outgoing_service.go +++ b/internal/service/letter_outgoing_service.go @@ -24,6 +24,7 @@ type LetterOutgoingService interface { SearchOutgoingLetters(ctx context.Context, req *contract.SearchOutgoingLettersRequest) (*contract.SearchOutgoingLettersResponse, error) UpdateOutgoingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) DeleteOutgoingLetter(ctx context.Context, id uuid.UUID) error + BulkDeleteOutgoingLetters(ctx context.Context, ids []uuid.UUID) error SubmitForApproval(ctx context.Context, letterID uuid.UUID) error ApproveOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.ApproveLetterRequest) error @@ -591,18 +592,40 @@ func (s *LetterOutgoingServiceImpl) UpdateOutgoingLetter(ctx context.Context, id func (s *LetterOutgoingServiceImpl) DeleteOutgoingLetter(ctx context.Context, id uuid.UUID) error { userID := getUserIDFromContext(ctx) - letter, err := s.processor.GetOutgoingLetterByID(ctx, id) - if err != nil { - return err - } + //letter, err := s.processor.GetOutgoingLetterByID(ctx, id) + //if err != nil { + // return err + //} - if letter.Status != entities.LetterOutgoingStatusDraft { - return gorm.ErrInvalidData - } + //if letter.Status != entities.LetterOutgoingStatusDraft { + // return gorm.ErrInvalidData + //} return s.processor.DeleteOutgoingLetter(ctx, id, userID) } +func (s *LetterOutgoingServiceImpl) BulkDeleteOutgoingLetters(ctx context.Context, ids []uuid.UUID) error { + if len(ids) == 0 { + return nil + } + + userID := getUserIDFromContext(ctx) + + // Validasi semua letters sebelum delete + //for _, id := range ids { + // letter, err := s.processor.GetOutgoingLetterByID(ctx, id) + // if err != nil { + // return err + // } + // + // if letter.Status != entities.LetterOutgoingStatusDraft { + // return gorm.ErrInvalidData + // } + //} + + return s.processor.BulkDeleteOutgoingLetters(ctx, ids, userID) +} + func (s *LetterOutgoingServiceImpl) SubmitForApproval(ctx context.Context, letterID uuid.UUID) error { userID := getUserIDFromContext(ctx)