Refactor tender approval service to optimize tender detail retrieval
continuous-integration/drone/push Build is passing

- Replaced the direct fetching of tender details within the `GetTenderApprovalsByCompanyIDWithTender`, `GetTenderApprovalsByStatusWithTender`, and `GetTenderApprovalsBySubmissionModeWithTender` methods with a new approach that loads tender details in bulk using `loadTendersByIDs`.
- Introduced helper functions `fullTenderDetailsFromResponse` and `listTenderDetailsFromResponse` to streamline the mapping of tender responses to `TenderDetails`.
- Added a new file `tender_details.go` to encapsulate tender detail mapping logic, improving code organization and readability.

This update enhances the performance of the tender approval service by reducing redundant calls to fetch tender details, leading to more efficient data handling.
This commit is contained in:
Mazyar
2026-07-08 01:17:17 +03:30
parent 107469c328
commit adaae4c4bc
2 changed files with 119 additions and 142 deletions
+9 -142
View File
@@ -292,40 +292,12 @@ func (s *tenderApprovalService) GetTenderApprovalsByCompanyIDWithTender(ctx cont
return nil, err
}
// Convert to response with tender details
tenders := s.loadTendersByIDs(ctx, uniqueTenderIDs(tenderApprovals))
responses := &TenderApprovalWithTenderListResponse{
Approvals: []*TenderApprovalWithTenderResponse{},
Approvals: mapApprovalsWithTender(tenderApprovals, tenders, listTenderDetailsFromResponse),
Metadata: &response.Meta{},
}
for _, approval := range tenderApprovals {
// Get tender details
tenderResponse, err := s.tenderService.GetByID(ctx, approval.TenderID, "")
if err != nil {
s.logger.Warn("Failed to get tender details", map[string]interface{}{
"tender_id": approval.TenderID,
"error": err.Error(),
})
// Continue without tender details rather than failing completely
responses.Approvals = append(responses.Approvals, approval.ToResponseWithTender(nil))
continue
}
// Convert tender response to tender details
tenderDetails := &TenderDetails{
ID: tenderResponse.ID,
Title: tenderResponse.Title,
Description: tenderResponse.Description,
EstimatedValue: tenderResponse.EstimatedValue,
Currency: tenderResponse.Currency,
TenderDeadline: tenderResponse.TenderDeadline,
SubmissionDeadline: tenderResponse.SubmissionDeadline,
CountryCode: tenderResponse.CountryCode,
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
Status: string(tenderResponse.Status),
}
responses.Approvals = append(responses.Approvals, approval.ToResponseWithTender(tenderDetails))
}
responses.Metadata = &response.Meta{
Total: int(total),
@@ -780,43 +752,8 @@ func (s *tenderApprovalService) GetTenderApprovalsByStatusWithTender(ctx context
return nil, 0, err
}
// Convert to response with tender details
var responses []*TenderApprovalWithTenderResponse
for _, approval := range tenderApprovals {
// Get tender details
tenderResponse, err := s.tenderService.GetByID(ctx, approval.TenderID, "")
if err != nil {
s.logger.Warn("Failed to get tender details", map[string]interface{}{
"tender_id": approval.TenderID,
"error": err.Error(),
})
// Continue without tender details rather than failing completely
responses = append(responses, approval.ToResponseWithTender(nil))
continue
}
// Convert tender response to tender details
tenderDetails := &TenderDetails{
ID: tenderResponse.ID,
NoticePublicationID: tenderResponse.NoticePublicationID,
PublicationDate: tenderResponse.PublicationDate,
Title: tenderResponse.Title,
Description: tenderResponse.Description,
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
ProcedureCode: tenderResponse.ProcurementTypeCode,
EstimatedValue: tenderResponse.EstimatedValue,
Currency: tenderResponse.Currency,
Duration: tenderResponse.Duration,
DurationUnit: tenderResponse.DurationUnit,
TenderDeadline: tenderResponse.TenderDeadline,
SubmissionDeadline: tenderResponse.SubmissionDeadline,
CountryCode: tenderResponse.CountryCode,
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
Status: string(tenderResponse.Status),
}
responses = append(responses, approval.ToResponseWithTender(tenderDetails))
}
tenders := s.loadTendersByIDs(ctx, uniqueTenderIDs(tenderApprovals))
responses := mapApprovalsWithTender(tenderApprovals, tenders, fullTenderDetailsFromResponse)
return responses, total, nil
}
@@ -828,43 +765,8 @@ func (s *tenderApprovalService) GetTenderApprovalsBySubmissionModeWithTender(ctx
return nil, 0, err
}
// Convert to response with tender details
var responses []*TenderApprovalWithTenderResponse
for _, approval := range tenderApprovals {
// Get tender details
tenderResponse, err := s.tenderService.GetByID(ctx, approval.TenderID, "")
if err != nil {
s.logger.Warn("Failed to get tender details", map[string]interface{}{
"tender_id": approval.TenderID,
"error": err.Error(),
})
// Continue without tender details rather than failing completely
responses = append(responses, approval.ToResponseWithTender(nil))
continue
}
// Convert tender response to tender details
tenderDetails := &TenderDetails{
ID: tenderResponse.ID,
NoticePublicationID: tenderResponse.NoticePublicationID,
PublicationDate: tenderResponse.PublicationDate,
Title: tenderResponse.Title,
Description: tenderResponse.Description,
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
ProcedureCode: tenderResponse.ProcedureCode,
EstimatedValue: tenderResponse.EstimatedValue,
Currency: tenderResponse.Currency,
Duration: tenderResponse.Duration,
DurationUnit: tenderResponse.DurationUnit,
TenderDeadline: tenderResponse.TenderDeadline,
SubmissionDeadline: tenderResponse.SubmissionDeadline,
CountryCode: tenderResponse.CountryCode,
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
Status: string(tenderResponse.Status),
}
responses = append(responses, approval.ToResponseWithTender(tenderDetails))
}
tenders := s.loadTendersByIDs(ctx, uniqueTenderIDs(tenderApprovals))
responses := mapApprovalsWithTender(tenderApprovals, tenders, fullTenderDetailsFromResponse)
return responses, total, nil
}
@@ -926,43 +828,8 @@ func (s *tenderApprovalService) ListTenderApprovalsWithTender(ctx context.Contex
total = int64(len(tenderApprovals)) // Use actual count if total count fails
}
// Convert to response with tender details
var responses []*TenderApprovalWithTenderResponse
for _, approval := range tenderApprovals {
// Get tender details
tenderResponse, err := s.tenderService.GetByID(ctx, approval.TenderID, "")
if err != nil {
s.logger.Warn("Failed to get tender details", map[string]interface{}{
"tender_id": approval.TenderID,
"error": err.Error(),
})
// Continue without tender details rather than failing completely
responses = append(responses, approval.ToResponseWithTender(nil))
continue
}
// Convert tender response to tender details
tenderDetails := &TenderDetails{
ID: tenderResponse.ID,
NoticePublicationID: tenderResponse.NoticePublicationID,
PublicationDate: tenderResponse.PublicationDate,
Title: tenderResponse.Title,
Description: tenderResponse.Description,
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
ProcedureCode: tenderResponse.ProcedureCode,
EstimatedValue: tenderResponse.EstimatedValue,
Currency: tenderResponse.Currency,
Duration: tenderResponse.Duration,
DurationUnit: tenderResponse.DurationUnit,
TenderDeadline: tenderResponse.TenderDeadline,
SubmissionDeadline: tenderResponse.SubmissionDeadline,
CountryCode: tenderResponse.CountryCode,
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
Status: string(tenderResponse.Status),
}
responses = append(responses, approval.ToResponseWithTender(tenderDetails))
}
tenders := s.loadTendersByIDs(ctx, uniqueTenderIDs(tenderApprovals))
responses := mapApprovalsWithTender(tenderApprovals, tenders, fullTenderDetailsFromResponse)
// Calculate pagination metadata
pages := int(total) / limit
+110
View File
@@ -0,0 +1,110 @@
package tender_approval
import (
"context"
"tm/internal/tender"
)
func fullTenderDetailsFromResponse(resp *tender.TenderResponse) *TenderDetails {
if resp == nil {
return nil
}
details := &TenderDetails{
ID: resp.ID,
NoticePublicationID: resp.NoticePublicationID,
PublicationDate: resp.PublicationDate,
Title: resp.Title,
Description: resp.Description,
ProcurementTypeCode: resp.ProcurementTypeCode,
ProcedureCode: resp.ProcedureCode,
EstimatedValue: resp.EstimatedValue,
Currency: resp.Currency,
Duration: resp.Duration,
DurationUnit: resp.DurationUnit,
TenderDeadline: resp.TenderDeadline,
SubmissionDeadline: resp.SubmissionDeadline,
CountryCode: resp.CountryCode,
Status: string(resp.Status),
}
if resp.BuyerOrganization != nil && resp.BuyerOrganization.Name != "" {
details.BuyerOrganization = &OrganizationResponse{Name: resp.BuyerOrganization.Name}
}
return details
}
func listTenderDetailsFromResponse(resp *tender.TenderResponse) *TenderDetails {
if resp == nil {
return nil
}
details := &TenderDetails{
ID: resp.ID,
Title: resp.Title,
Description: resp.Description,
EstimatedValue: resp.EstimatedValue,
Currency: resp.Currency,
TenderDeadline: resp.TenderDeadline,
SubmissionDeadline: resp.SubmissionDeadline,
CountryCode: resp.CountryCode,
Status: string(resp.Status),
}
if resp.BuyerOrganization != nil && resp.BuyerOrganization.Name != "" {
details.BuyerOrganization = &OrganizationResponse{Name: resp.BuyerOrganization.Name}
}
return details
}
func uniqueTenderIDs(approvals []*TenderApproval) []string {
if len(approvals) == 0 {
return nil
}
seen := make(map[string]struct{}, len(approvals))
ids := make([]string, 0, len(approvals))
for _, approval := range approvals {
if approval == nil || approval.TenderID == "" {
continue
}
if _, ok := seen[approval.TenderID]; ok {
continue
}
seen[approval.TenderID] = struct{}{}
ids = append(ids, approval.TenderID)
}
return ids
}
func (s *tenderApprovalService) loadTendersByIDs(ctx context.Context, tenderIDs []string) map[string]*tender.TenderResponse {
if len(tenderIDs) == 0 {
return map[string]*tender.TenderResponse{}
}
tenders, err := s.tenderService.GetByIDs(ctx, tenderIDs)
if err != nil {
s.logger.Warn("Failed to load tenders by IDs", map[string]interface{}{
"count": len(tenderIDs),
"error": err.Error(),
})
return map[string]*tender.TenderResponse{}
}
return tenders
}
func mapApprovalsWithTender(
approvals []*TenderApproval,
tenders map[string]*tender.TenderResponse,
toDetails func(*tender.TenderResponse) *TenderDetails,
) []*TenderApprovalWithTenderResponse {
responses := make([]*TenderApprovalWithTenderResponse, 0, len(approvals))
for _, approval := range approvals {
var details *TenderDetails
if tenderResp, ok := tenders[approval.TenderID]; ok {
details = toDetails(tenderResp)
}
responses = append(responses, approval.ToResponseWithTender(details))
}
return responses
}