outgoing letter is read

This commit is contained in:
efrilm 2025-10-08 11:52:11 +07:00
parent d8942fa918
commit e7455f107a
5 changed files with 80 additions and 4 deletions

View File

@ -110,6 +110,7 @@ type OutgoingLetterResponse struct {
CreatedBy uuid.UUID `json:"created_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
IsRead bool `json:"is_read"`
Recipients []OutgoingLetterRecipientResponse `json:"recipients,omitempty"`
Attachments []OutgoingLetterAttachmentResponse `json:"attachments,omitempty"`
Approvals []OutgoingLetterApprovalResponse `json:"approvals,omitempty"`
@ -139,6 +140,7 @@ type ListOutgoingLettersRequest struct {
SortBy string `form:"sort_by" json:"sort_by,omitempty"`
SortOrder string `form:"sort_order" json:"sort_order,omitempty"`
IsArchived *bool `form:"is_archived" json:"is_archived,omitempty"`
IsRead *bool `form:"is_read,omitempty"`
}
type ListOutgoingLettersResponse struct {

View File

@ -4,6 +4,7 @@ import (
"context"
"eslogad-be/internal/appcontext"
"eslogad-be/internal/contract"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
@ -101,6 +102,8 @@ func (h *LetterOutgoingHandler) ListOutgoingLetters(c *gin.Context) {
req.Limit = 10
}
fmt.Printf("[DEBUG] request: %v\n", req)
resp, err := h.svc.ListOutgoingLetters(c.Request.Context(), &req)
if err != nil {
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})

View File

@ -51,6 +51,8 @@ type LetterOutgoingProcessor interface {
GetBatchRecipients(ctx context.Context, letterIDs []uuid.UUID) (map[uuid.UUID][]entities.LetterOutgoingRecipient, 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)
GetBatchOutgoingRecipientsByUser(ctx context.Context, letterIDs []uuid.UUID, userID uuid.UUID) (map[uuid.UUID]*entities.LetterOutgoingRecipient, error)
}
type LetterOutgoingProcessorImpl struct {
@ -806,6 +808,13 @@ func (p *LetterOutgoingProcessorImpl) GetBatchRecipients(ctx context.Context, le
return p.recipientRepo.ListByLetterIDs(ctx, letterIDs)
}
func (p *LetterOutgoingProcessorImpl) GetBatchOutgoingRecipientsByUser(ctx context.Context, letterIDs []uuid.UUID, userID uuid.UUID) (map[uuid.UUID]*entities.LetterOutgoingRecipient, error) {
if p.recipientRepo == nil || len(letterIDs) == 0 {
return make(map[uuid.UUID]*entities.LetterOutgoingRecipient), nil
}
return p.recipientRepo.GetByLetterIDsAndUser(ctx, letterIDs, userID)
}
// GetBatchPriorities fetches priorities by IDs in a single query
func (p *LetterOutgoingProcessorImpl) GetBatchPriorities(ctx context.Context, priorityIDs []uuid.UUID) (map[uuid.UUID]*entities.Priority, error) {
if p.priorityRepo == nil || len(priorityIDs) == 0 {

View File

@ -2,6 +2,7 @@ package repository
import (
"context"
"fmt"
"time"
"eslogad-be/internal/entities"
@ -93,6 +94,7 @@ type ListOutgoingLettersFilter struct {
SortBy *string
SortOrder *string
IsArchived *bool
IsRead *bool
}
func (r *LetterOutgoingRepository) List(ctx context.Context, filter ListOutgoingLettersFilter, limit, offset int) ([]entities.LetterOutgoing, int64, error) {
@ -120,10 +122,25 @@ func (r *LetterOutgoingRepository) List(ctx context.Context, filter ListOutgoing
}
// Filter by UserID through recipients
if filter.UserID != nil {
query = query.Joins("LEFT JOIN letter_outgoing_recipients ON letter_outgoing_recipients.letter_id = letters_outgoing.id").
Where("letter_outgoing_recipients.user_id = ?", *filter.UserID).
Distinct()
query = query.Joins("LEFT JOIN letter_outgoing_recipients ON letter_outgoing_recipients.letter_id = letters_outgoing.id")
query = query.Where("letter_outgoing_recipients.user_id = ?", *filter.UserID)
fmt.Printf("[DEBUG] filter.UserID: %v\n", filter.UserID)
fmt.Printf("[DEBUG] filter.isRead: %v\n", filter.IsRead)
// Tambahkan filter IsRead
if filter.IsRead != nil {
if *filter.IsRead {
query = query.Where("letter_outgoing_recipients.read_at IS NOT NULL")
} else {
query = query.Where("letter_outgoing_recipients.read_at IS NULL")
}
}
query = query.Distinct()
}
if filter.ReceiverInstitutionID != nil {
query = query.Where("receiver_institution_id = ?", *filter.ReceiverInstitutionID)
}
@ -485,6 +502,27 @@ func (r *LetterOutgoingDiscussionRepository) ListByLetter(ctx context.Context, l
return list, nil
}
func (r *LetterOutgoingRecipientRepository) GetByLetterIDsAndUser(ctx context.Context, letterIDs []uuid.UUID, userID uuid.UUID) (map[uuid.UUID]*entities.LetterOutgoingRecipient, error) {
if len(letterIDs) == 0 {
return make(map[uuid.UUID]*entities.LetterOutgoingRecipient), nil
}
db := DBFromContext(ctx, r.db)
var recipients []entities.LetterOutgoingRecipient
if err := db.WithContext(ctx).
Where("letter_id IN ? AND user_id = ?", letterIDs, userID).
Find(&recipients).Error; err != nil {
return nil, err
}
result := make(map[uuid.UUID]*entities.LetterOutgoingRecipient)
for i := range recipients {
result[recipients[i].LetterID] = &recipients[i]
}
return result, nil
}
func (r *LetterOutgoingDiscussionRepository) Update(ctx context.Context, e *entities.LetterOutgoingDiscussion) error {
db := DBFromContext(ctx, r.db)
now := time.Now()

View File

@ -212,6 +212,7 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req
ReceiverInstitutionID: req.ReceiverInstitutionID,
PriorityID: req.PriorityID,
UserID: &userID,
IsRead: req.IsRead,
}
if departmentID != uuid.Nil {
@ -250,6 +251,12 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req
filter.IsArchived = req.IsArchived
}
if filter.IsRead != nil {
filter.IsRead = req.IsRead
}
fmt.Printf("[DEBUG] filter: %v\n", filter)
// Get raw letters data
letters, total, err := s.processor.ListOutgoingLetters(ctx, filter, req.Limit, offset)
if err != nil {
@ -294,6 +301,7 @@ 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)
@ -346,7 +354,23 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req
}
}
items[i] = transformLetterToResponse(&letter)
isRead := false
recipientByUser := make(map[uuid.UUID]*entities.LetterOutgoingRecipient)
recipientByUser, err = s.processor.GetBatchOutgoingRecipientsByUser(ctx, letterIDs, userID)
if err != nil {
// Handle error
return nil, err
}
// Ambil isRead dari recipientByUser berdasarkan letter.ID
if recipient, exists := recipientByUser[letter.ID]; exists && recipient != nil {
isRead = recipient.ReadAt != nil
}
response := transformLetterToResponse(&letter)
response.IsRead = isRead
items[i] = response
}
return &contract.ListOutgoingLettersResponse{