add sender and receiver
This commit is contained in:
@@ -21,6 +21,7 @@ type LetterOutgoingService interface {
|
||||
CreateOutgoingLetter(ctx context.Context, req *contract.CreateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error)
|
||||
GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*contract.OutgoingLetterResponse, error)
|
||||
ListOutgoingLetters(ctx context.Context, req *contract.ListOutgoingLettersRequest) (*contract.ListOutgoingLettersResponse, error)
|
||||
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
|
||||
|
||||
@@ -99,6 +100,7 @@ func (s *LetterOutgoingServiceImpl) CreateOutgoingLetter(ctx context.Context, re
|
||||
Description: req.Description,
|
||||
PriorityID: req.PriorityID,
|
||||
ReceiverInstitutionID: req.ReceiverInstitutionID,
|
||||
ReceiverName: req.ReceiverName,
|
||||
IssueDate: req.IssueDate,
|
||||
CreatedBy: userID,
|
||||
}
|
||||
@@ -352,6 +354,159 @@ func (s *LetterOutgoingServiceImpl) ListOutgoingLetters(ctx context.Context, req
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *LetterOutgoingServiceImpl) SearchOutgoingLetters(ctx context.Context, req *contract.SearchOutgoingLettersRequest) (*contract.SearchOutgoingLettersResponse, error) {
|
||||
userID := getUserIDFromContext(ctx)
|
||||
departmentID := getDepartmentIDFromContext(ctx)
|
||||
|
||||
// Build search filters
|
||||
filters := buildOutgoingSearchFilters(req, userID, departmentID)
|
||||
|
||||
// Execute search with pagination
|
||||
letters, total, err := s.processor.SearchOutgoingLetters(ctx, filters, req.Page, req.Limit, req.SortBy, req.SortOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Collect IDs for batch loading
|
||||
letterIDs := make([]uuid.UUID, len(letters))
|
||||
priorityIDMap := make(map[uuid.UUID]bool)
|
||||
institutionIDMap := make(map[uuid.UUID]bool)
|
||||
|
||||
for i, letter := range letters {
|
||||
letterIDs[i] = letter.ID
|
||||
if letter.PriorityID != nil {
|
||||
priorityIDMap[*letter.PriorityID] = true
|
||||
}
|
||||
if letter.ReceiverInstitutionID != nil {
|
||||
institutionIDMap[*letter.ReceiverInstitutionID] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Convert maps to slices
|
||||
priorityIDSlice := make([]uuid.UUID, 0, len(priorityIDMap))
|
||||
for id := range priorityIDMap {
|
||||
priorityIDSlice = append(priorityIDSlice, id)
|
||||
}
|
||||
|
||||
institutionIDSlice := make([]uuid.UUID, 0, len(institutionIDMap))
|
||||
for id := range institutionIDMap {
|
||||
institutionIDSlice = append(institutionIDSlice, id)
|
||||
}
|
||||
|
||||
// Parallel batch loading
|
||||
type batchLoadResult struct {
|
||||
attachments map[uuid.UUID][]entities.LetterOutgoingAttachment
|
||||
recipients map[uuid.UUID][]entities.LetterOutgoingRecipient
|
||||
priorities map[uuid.UUID]*entities.Priority
|
||||
institutions map[uuid.UUID]*entities.Institution
|
||||
}
|
||||
|
||||
var result batchLoadResult
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Transform letters with batch loaded data
|
||||
items := make([]contract.OutgoingLetterResponse, len(letters))
|
||||
for i, letter := range letters {
|
||||
// Attach batch loaded data to letter
|
||||
if attachments, ok := result.attachments[letter.ID]; ok {
|
||||
letter.Attachments = attachments
|
||||
}
|
||||
if recipients, ok := result.recipients[letter.ID]; ok {
|
||||
letter.Recipients = recipients
|
||||
}
|
||||
if letter.PriorityID != nil {
|
||||
if priority, ok := result.priorities[*letter.PriorityID]; ok {
|
||||
letter.Priority = priority
|
||||
}
|
||||
}
|
||||
if letter.ReceiverInstitutionID != nil {
|
||||
if institution, ok := result.institutions[*letter.ReceiverInstitutionID]; ok {
|
||||
letter.ReceiverInstitution = institution
|
||||
}
|
||||
}
|
||||
|
||||
items[i] = *transformLetterToResponse(&letter)
|
||||
}
|
||||
|
||||
return &contract.SearchOutgoingLettersResponse{
|
||||
Letters: items,
|
||||
TotalCount: total,
|
||||
Page: req.Page,
|
||||
Limit: req.Limit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildOutgoingSearchFilters(req *contract.SearchOutgoingLettersRequest, userID, departmentID uuid.UUID) map[string]interface{} {
|
||||
filters := make(map[string]interface{})
|
||||
|
||||
if req.Query != "" {
|
||||
filters["query"] = req.Query
|
||||
}
|
||||
if req.LetterNumber != "" {
|
||||
filters["letter_number"] = req.LetterNumber
|
||||
}
|
||||
if req.Subject != "" {
|
||||
filters["subject"] = req.Subject
|
||||
}
|
||||
if req.Status != "" {
|
||||
filters["status"] = req.Status
|
||||
}
|
||||
if req.PriorityID != nil {
|
||||
filters["priority_id"] = *req.PriorityID
|
||||
}
|
||||
if req.InstitutionID != nil {
|
||||
filters["receiver_institution_id"] = *req.InstitutionID
|
||||
}
|
||||
if req.CreatedBy != nil {
|
||||
filters["created_by"] = *req.CreatedBy
|
||||
}
|
||||
if req.DateFrom != nil {
|
||||
filters["date_from"] = *req.DateFrom
|
||||
}
|
||||
if req.DateTo != nil {
|
||||
filters["date_to"] = *req.DateTo
|
||||
}
|
||||
|
||||
// Add user/department context filters
|
||||
filters["user_context"] = map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"department_id": departmentID,
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
||||
|
||||
func (s *LetterOutgoingServiceImpl) UpdateOutgoingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error) {
|
||||
userID := getUserIDFromContext(ctx)
|
||||
|
||||
@@ -382,6 +537,9 @@ func (s *LetterOutgoingServiceImpl) UpdateOutgoingLetter(ctx context.Context, id
|
||||
if req.ReferenceNumber != nil {
|
||||
letter.ReferenceNumber = req.ReferenceNumber
|
||||
}
|
||||
if req.ReceiverName != nil {
|
||||
letter.ReceiverName = req.ReceiverName
|
||||
}
|
||||
|
||||
err = s.processor.UpdateOutgoingLetter(ctx, letter, userID)
|
||||
if err != nil {
|
||||
@@ -1245,6 +1403,7 @@ func transformLetterToResponse(letter *entities.LetterOutgoing) *contract.Outgoi
|
||||
Description: letter.Description,
|
||||
PriorityID: letter.PriorityID,
|
||||
ReceiverInstitutionID: letter.ReceiverInstitutionID,
|
||||
ReceiverName: letter.ReceiverName,
|
||||
IssueDate: letter.IssueDate,
|
||||
Status: string(letter.Status),
|
||||
ApprovalFlowID: letter.ApprovalFlowID,
|
||||
|
||||
@@ -25,6 +25,7 @@ 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)
|
||||
SearchIncomingLetters(ctx context.Context, filters map[string]interface{}, page, limit int, sortBy, sortOrder string) ([]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)
|
||||
@@ -448,6 +449,208 @@ func (s *LetterServiceImpl) SoftDeleteIncomingLetter(ctx context.Context, id uui
|
||||
return s.processor.SoftDeleteIncomingLetter(ctx, id)
|
||||
}
|
||||
|
||||
func (s *LetterServiceImpl) SearchIncomingLetters(ctx context.Context, req *contract.SearchIncomingLettersRequest) (*contract.SearchIncomingLettersResponse, error) {
|
||||
appCtx := appcontext.FromGinContext(ctx)
|
||||
userID := appCtx.UserID
|
||||
departmentID := appCtx.DepartmentID
|
||||
|
||||
// Build search filters
|
||||
filters := buildIncomingSearchFilters(req, userID, departmentID)
|
||||
|
||||
// Execute search with pagination
|
||||
letters, total, err := s.processor.SearchIncomingLetters(ctx, filters, req.Page, req.Limit, req.SortBy, req.SortOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Collect IDs for batch loading
|
||||
letterIDs := make([]uuid.UUID, len(letters))
|
||||
priorityIDMap := make(map[uuid.UUID]bool)
|
||||
institutionIDMap := make(map[uuid.UUID]bool)
|
||||
|
||||
for i, letter := range letters {
|
||||
letterIDs[i] = letter.ID
|
||||
if letter.PriorityID != nil {
|
||||
priorityIDMap[*letter.PriorityID] = true
|
||||
}
|
||||
if letter.SenderInstitutionID != nil {
|
||||
institutionIDMap[*letter.SenderInstitutionID] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Convert maps to slices
|
||||
priorityIDSlice := make([]uuid.UUID, 0, len(priorityIDMap))
|
||||
for id := range priorityIDMap {
|
||||
priorityIDSlice = append(priorityIDSlice, id)
|
||||
}
|
||||
|
||||
institutionIDSlice := make([]uuid.UUID, 0, len(institutionIDMap))
|
||||
for id := range institutionIDMap {
|
||||
institutionIDSlice = append(institutionIDSlice, id)
|
||||
}
|
||||
|
||||
// Parallel batch loading
|
||||
type batchLoadResult struct {
|
||||
attachments map[uuid.UUID][]entities.LetterIncomingAttachment
|
||||
recipients map[uuid.UUID]*entities.LetterIncomingRecipient
|
||||
priorities map[uuid.UUID]*entities.Priority
|
||||
institutions map[uuid.UUID]*entities.Institution
|
||||
}
|
||||
|
||||
var result batchLoadResult
|
||||
errChan := make(chan error, 4)
|
||||
|
||||
// Load attachments
|
||||
go func() {
|
||||
result.attachments, err = s.processor.GetBatchAttachments(ctx, letterIDs)
|
||||
errChan <- err
|
||||
}()
|
||||
|
||||
// Load recipients for user
|
||||
go func() {
|
||||
result.recipients, err = s.processor.GetBatchRecipientsByUser(ctx, letterIDs, userID)
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Transform letters with batch loaded data
|
||||
items := make([]contract.IncomingLetterResponse, len(letters))
|
||||
for i, letter := range letters {
|
||||
// Attach batch loaded data
|
||||
attachmentResponses := []contract.IncomingLetterAttachmentResponse{}
|
||||
if attachments, ok := result.attachments[letter.ID]; ok {
|
||||
for _, att := range attachments {
|
||||
attachmentResponses = append(attachmentResponses, contract.IncomingLetterAttachmentResponse{
|
||||
ID: att.ID,
|
||||
FileURL: att.FileURL,
|
||||
FileName: att.FileName,
|
||||
FileType: att.FileType,
|
||||
UploadedAt: att.UploadedAt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var priorityResp *contract.PriorityResponse
|
||||
if letter.PriorityID != nil {
|
||||
if priority, ok := result.priorities[*letter.PriorityID]; ok {
|
||||
priorityResp = &contract.PriorityResponse{
|
||||
ID: priority.ID.String(),
|
||||
Name: priority.Name,
|
||||
Level: priority.Level,
|
||||
CreatedAt: priority.CreatedAt,
|
||||
UpdatedAt: priority.UpdatedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var institutionResp *contract.InstitutionResponse
|
||||
if letter.SenderInstitutionID != nil {
|
||||
if institution, ok := result.institutions[*letter.SenderInstitutionID]; ok {
|
||||
institutionResp = &contract.InstitutionResponse{
|
||||
ID: institution.ID.String(),
|
||||
Name: institution.Name,
|
||||
Type: string(institution.Type),
|
||||
Address: institution.Address,
|
||||
ContactPerson: institution.ContactPerson,
|
||||
Phone: institution.Phone,
|
||||
Email: institution.Email,
|
||||
CreatedAt: institution.CreatedAt,
|
||||
UpdatedAt: institution.UpdatedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRead := false
|
||||
if recipient, ok := result.recipients[letter.ID]; ok && recipient.ReadAt != nil {
|
||||
isRead = true
|
||||
}
|
||||
|
||||
items[i] = contract.IncomingLetterResponse{
|
||||
ID: letter.ID,
|
||||
LetterNumber: letter.LetterNumber,
|
||||
ReferenceNumber: letter.ReferenceNumber,
|
||||
Subject: letter.Subject,
|
||||
Description: letter.Description,
|
||||
Priority: priorityResp,
|
||||
SenderInstitution: institutionResp,
|
||||
SenderName: letter.SenderName,
|
||||
ReceivedDate: letter.ReceivedDate,
|
||||
DueDate: letter.DueDate,
|
||||
Status: string(letter.Status),
|
||||
CreatedBy: letter.CreatedBy,
|
||||
CreatedAt: letter.CreatedAt,
|
||||
UpdatedAt: letter.UpdatedAt,
|
||||
Attachments: attachmentResponses,
|
||||
IsRead: isRead,
|
||||
}
|
||||
}
|
||||
|
||||
return &contract.SearchIncomingLettersResponse{
|
||||
Letters: items,
|
||||
TotalCount: total,
|
||||
Page: req.Page,
|
||||
Limit: req.Limit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildIncomingSearchFilters(req *contract.SearchIncomingLettersRequest, userID, departmentID uuid.UUID) map[string]interface{} {
|
||||
filters := make(map[string]interface{})
|
||||
|
||||
if req.Query != "" {
|
||||
filters["query"] = req.Query
|
||||
}
|
||||
if req.LetterNumber != "" {
|
||||
filters["letter_number"] = req.LetterNumber
|
||||
}
|
||||
if req.Subject != "" {
|
||||
filters["subject"] = req.Subject
|
||||
}
|
||||
if req.Status != "" {
|
||||
filters["status"] = req.Status
|
||||
}
|
||||
if req.PriorityID != nil {
|
||||
filters["priority_id"] = *req.PriorityID
|
||||
}
|
||||
if req.InstitutionID != nil {
|
||||
filters["sender_institution_id"] = *req.InstitutionID
|
||||
}
|
||||
if req.CreatedBy != nil {
|
||||
filters["created_by"] = *req.CreatedBy
|
||||
}
|
||||
if req.DateFrom != nil {
|
||||
filters["date_from"] = *req.DateFrom
|
||||
}
|
||||
if req.DateTo != nil {
|
||||
filters["date_to"] = *req.DateTo
|
||||
}
|
||||
|
||||
// Add user/department context filters
|
||||
filters["user_context"] = map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"department_id": departmentID,
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user