Files
tm_back/internal/tender/recommendation_response.go
T
Mazyar f68b6d7787
continuous-integration/drone/push Build is passing
Refactor recommendation response handling and enhance enrichment logic
- Removed the `buildRecommendedTenderResponsesParallel` method and replaced it with `buildRecommendedTenderListResponses`, simplifying the response building process.
- Introduced `enrichRecommendedTenderResponsesParallel` to handle enrichment of tender responses in parallel, improving performance.
- Updated the `Recommend` method to utilize the new enrichment logic, ensuring timely and efficient processing of recommendations.
- Cleaned up unused functions and imports in `recommendation_page_cache.go` and `recommendation_response.go`, enhancing code clarity and maintainability.

This update streamlines the recommendation response handling and improves the overall efficiency of the tender recommendation service.
2026-07-08 02:27:25 +03:30

164 lines
4.4 KiB
Go

package tender
import (
"context"
"fmt"
"strings"
"sync"
"go.mongodb.org/mongo-driver/v2/bson"
"golang.org/x/sync/errgroup"
"tm/pkg/ai_summarizer"
)
type rankedRecommendedTender struct {
tender Tender
rank int
analysis string
tenderRef string
}
type recommendationTranslationCache struct {
mu sync.Mutex
items map[string]ai_summarizer.StoredTranslation
}
func newRecommendationTranslationCache() *recommendationTranslationCache {
return &recommendationTranslationCache{
items: make(map[string]ai_summarizer.StoredTranslation),
}
}
func recommendationTranslationCacheKey(tenderID, language string) string {
return strings.TrimSpace(tenderID) + "|" + strings.TrimSpace(language)
}
func (c *recommendationTranslationCache) get(tenderID, language string) (ai_summarizer.StoredTranslation, bool) {
if c == nil {
return ai_summarizer.StoredTranslation{}, false
}
c.mu.Lock()
defer c.mu.Unlock()
stored, ok := c.items[recommendationTranslationCacheKey(tenderID, language)]
return stored, ok
}
func (c *recommendationTranslationCache) set(tenderID, language string, stored ai_summarizer.StoredTranslation) {
if c == nil {
return
}
c.mu.Lock()
defer c.mu.Unlock()
c.items[recommendationTranslationCacheKey(tenderID, language)] = stored
}
// buildRecommendedTenderListResponses builds list responses without MinIO translation lookups.
// Recommendation pages use MongoDB text; tender detail endpoints still enrich from storage.
func (s *tenderService) buildRecommendedTenderListResponses(items []rankedRecommendedTender, language string) []TenderResponse {
if len(items) == 0 {
return nil
}
out := make([]TenderResponse, len(items))
for i, item := range items {
resp := item.tender.ToResponseWithLanguage(language)
resp.Rank = item.rank
resp.Analysis = item.analysis
resp.ProcedureRef = item.tenderRef
out[i] = *resp
}
return out
}
func (s *tenderService) enrichRecommendedTenderResponsesParallel(ctx context.Context, responses []TenderResponse, language string) []TenderResponse {
if len(responses) == 0 || s.aiSummarizerStorage == nil {
return responses
}
out := make([]TenderResponse, len(responses))
if len(responses) == 1 {
out[0] = s.enrichRecommendedTenderResponse(ctx, responses[0], language, newRecommendationTranslationCache())
return out
}
translationCache := newRecommendationTranslationCache()
group, groupCtx := errgroup.WithContext(ctx)
group.SetLimit(recommendationPageCacheConcurrency)
for i := range responses {
i := i
group.Go(func() error {
out[i] = s.enrichRecommendedTenderResponse(groupCtx, responses[i], language, translationCache)
return nil
})
}
if err := group.Wait(); err != nil {
for i, resp := range responses {
out[i] = s.enrichRecommendedTenderResponse(ctx, resp, language, translationCache)
}
}
return out
}
func (s *tenderService) enrichRecommendedTenderResponse(
ctx context.Context,
resp TenderResponse,
language string,
cache *recommendationTranslationCache,
) TenderResponse {
enriched := resp
tender := tenderFromRecommendationResponse(resp)
s.enrichWithTranslationTimeout(
ctx,
&tender,
&enriched,
language,
cache,
recommendTranslationEnrichmentTimeout,
)
return enriched
}
func tenderFromRecommendationResponse(item TenderResponse) Tender {
var tender Tender
if id, err := bson.ObjectIDFromHex(strings.TrimSpace(item.ID)); err == nil {
tender.ID = id
}
tender.ContractFolderID = item.ContractFolderID
tender.NoticePublicationID = item.NoticePublicationID
tender.RelatedNoticePublicationIDs = append([]string(nil), item.RelatedNoticePublicationIDs...)
tender.Status = item.Status
tender.PublicationDate = item.PublicationDate
tender.SubmissionDeadline = item.SubmissionDeadline
tender.TenderDeadline = item.TenderDeadline
return tender
}
func (s *tenderService) loadRecommendationExclusions(
ctx context.Context,
form *SearchForm,
companyID string,
companyIDs []string,
) (map[string]struct{}, map[string]map[string]struct{}, error) {
if !form.ExcludeRejectedTenders {
return nil, nil, nil
}
if len(companyIDs) > 0 {
excludedByCompany, err := s.buildRejectedTenderIDSetsForCompanies(ctx, companyIDs)
if err != nil {
return nil, nil, fmt.Errorf("failed to build recommendation exclusions: %w", err)
}
return nil, excludedByCompany, nil
}
excluded, err := s.buildRejectedTenderIDSet(ctx, companyID)
if err != nil {
return nil, nil, fmt.Errorf("failed to build recommendation exclusions: %w", err)
}
return excluded, nil, nil
}