Files
tm_back/internal/tender/recommendation_page.go
T
Mazyar f108733c2a
continuous-integration/drone/push Build is passing
Enhance tender recommendation service with improved caching and pagination
- Introduced a context with a timeout for Redis cache retrieval in the `getCachedAIRecommendations` method, ensuring more robust handling of potential delays.
- Added a new file `recommendation_page.go` to encapsulate the logic for building recommendation pages, improving code organization and readability.
- Implemented a `buildRecommendationPage` method to handle pagination and batch processing of recommendations, enhancing performance and user experience.
- Created a new test file `recommendation_page_test.go` to validate the functionality of the recommendation page building process, ensuring correct pagination and batch resolution.

This update significantly improves the efficiency and maintainability of the tender recommendation service by optimizing caching and implementing structured pagination handling.
2026-07-08 01:32:27 +03:30

95 lines
2.2 KiB
Go

package tender
import (
"context"
"strings"
"tm/internal/company"
)
const recommendationResolveBatchSize = 100
type recommendationPageResult struct {
items []rankedRecommendedTender
total int64
}
func (s *tenderService) buildRecommendationPage(
ctx context.Context,
recommendations []company.RecommendedTenderResponse,
form *SearchForm,
companyIDs []string,
excluded map[string]struct{},
excludedByCompany map[string]map[string]struct{},
now int64,
offset int,
limit int,
) (recommendationPageResult, error) {
seenTenderIDs := make(map[string]struct{}, len(recommendations))
pageStart := offset
pageEnd := offset + limit
if pageStart < 0 {
pageStart = 0
}
if pageEnd < pageStart {
pageEnd = pageStart
}
result := recommendationPageResult{
items: make([]rankedRecommendedTender, 0, limit),
}
for start := 0; start < len(recommendations); start += recommendationResolveBatchSize {
end := start + recommendationResolveBatchSize
if end > len(recommendations) {
end = len(recommendations)
}
tenderByRef, err := s.resolveRecommendedTenders(ctx, recommendations[start:end])
if err != nil {
return recommendationPageResult{}, err
}
for _, rec := range recommendations[start:end] {
tenderRef := strings.TrimSpace(rec.TenderID)
tender, ok := tenderByRef[tenderRef]
if !ok {
continue
}
if len(companyIDs) > 0 {
if isRecommendedTenderExcludedForAllCompanies(tender, tenderRef, excludedByCompany, companyIDs) {
continue
}
} else if isRecommendedTenderExcluded(tender, tenderRef, excluded) {
continue
}
if form.OnlyActiveDeadlines && !tender.IsRecommendable(now) {
continue
}
if len(form.Status) > 0 && !statusAllowed(tender.Status, form.Status) {
continue
}
if markResolvedRecommendedTenderSeen(seenTenderIDs, tender) {
continue
}
result.total++
if int(result.total) <= pageStart || len(result.items) >= limit {
continue
}
if int(result.total) > pageEnd {
continue
}
result.items = append(result.items, rankedRecommendedTender{
tender: tender,
rank: rec.Rank,
analysis: rec.Analysis,
tenderRef: tenderRef,
})
}
}
return result, nil
}