Enhance Tender Management with Company-Based Matching and API Updates
- Updated the tender service to include company-based matching for tenders, allowing for more relevant results based on company CPV codes. - Modified the ListTenders endpoint to accept a company ID parameter for calculating match percentages and sorting tenders accordingly. - Refactored the tender response structure to include match percentage and days until deadline for better client-side handling. - Updated API documentation to reflect changes in the tender listing functionality and added new endpoints for public tender access. - Removed deprecated customer-related methods and streamlined customer listing functionality for improved clarity and performance.
This commit is contained in:
+107
-8
@@ -3,7 +3,9 @@ package tender
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
"tm/internal/company"
|
||||
"tm/pkg/logger"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
@@ -36,15 +38,17 @@ type TenderService interface {
|
||||
|
||||
// tenderService implements TenderService interface
|
||||
type tenderService struct {
|
||||
repository TenderRepository
|
||||
logger logger.Logger
|
||||
repository TenderRepository
|
||||
companyService company.Service
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewTenderService creates a new tender service
|
||||
func NewTenderService(repository TenderRepository, logger logger.Logger) TenderService {
|
||||
func NewTenderService(repository TenderRepository, companyService company.Service, logger logger.Logger) TenderService {
|
||||
return &tenderService{
|
||||
repository: repository,
|
||||
logger: logger,
|
||||
repository: repository,
|
||||
companyService: companyService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,11 +226,19 @@ func (s *tenderService) DeleteTender(ctx context.Context, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TenderWithMatch represents a tender with calculated match percentage for sorting
|
||||
type TenderWithMatch struct {
|
||||
Tender *Tender
|
||||
MatchPercentage int
|
||||
DaysUntilDeadline int
|
||||
}
|
||||
|
||||
// ListTenders retrieves tenders with pagination and filtering
|
||||
func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest) (*ListTendersResponse, error) {
|
||||
s.logger.Info("Listing tenders", map[string]interface{}{
|
||||
"limit": req.Limit,
|
||||
"offset": req.Offset,
|
||||
"limit": req.Limit,
|
||||
"offset": req.Offset,
|
||||
"company_id": req.CompanyID,
|
||||
})
|
||||
|
||||
if req.Limit <= 0 {
|
||||
@@ -244,8 +256,95 @@ func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest)
|
||||
return nil, fmt.Errorf("failed to list tenders: %w", err)
|
||||
}
|
||||
|
||||
// If no company ID provided, return tenders without match percentage
|
||||
if req.CompanyID == nil || *req.CompanyID == "" {
|
||||
tenderResponses := make([]TenderResponse, 0, len(tenders))
|
||||
for _, tender := range tenders {
|
||||
tenderResponses = append(tenderResponses, *tender.ToTenderResponse())
|
||||
}
|
||||
|
||||
return &ListTendersResponse{
|
||||
Tenders: tenderResponses,
|
||||
Total: total,
|
||||
Limit: req.Limit,
|
||||
Offset: req.Offset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get company CPV codes for match calculation
|
||||
comp, err := s.companyService.GetCompanyByID(ctx, *req.CompanyID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get company for match calculation", map[string]interface{}{
|
||||
"company_id": *req.CompanyID,
|
||||
"error": err.Error(),
|
||||
})
|
||||
// Continue without match percentage if company not found
|
||||
tenderResponses := make([]TenderResponse, 0, len(tenders))
|
||||
for _, tender := range tenders {
|
||||
tenderResponses = append(tenderResponses, *tender.ToTenderResponse())
|
||||
}
|
||||
|
||||
return &ListTendersResponse{
|
||||
Tenders: tenderResponses,
|
||||
Total: total,
|
||||
Limit: req.Limit,
|
||||
Offset: req.Offset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var companyCPVCodes []string
|
||||
if comp.Tags != nil && comp.Tags.CPVCodes != nil {
|
||||
companyCPVCodes = comp.Tags.CPVCodes
|
||||
}
|
||||
|
||||
// Calculate match percentages and prepare for sorting
|
||||
tenderMatches := make([]TenderWithMatch, 0, len(tenders))
|
||||
for _, tender := range tenders {
|
||||
tenderMatch := TenderWithMatch{
|
||||
Tender: &tender,
|
||||
MatchPercentage: tender.CalculateMatchPercentage(companyCPVCodes),
|
||||
DaysUntilDeadline: tender.GetDaysUntilDeadline(),
|
||||
}
|
||||
tenderMatches = append(tenderMatches, tenderMatch)
|
||||
}
|
||||
|
||||
// Sort by match percentage (descending) and then by deadline preference
|
||||
sort.Slice(tenderMatches, func(i, j int) bool {
|
||||
matchI := tenderMatches[i].MatchPercentage
|
||||
matchJ := tenderMatches[j].MatchPercentage
|
||||
|
||||
// First, sort by match percentage (descending)
|
||||
if matchI != matchJ {
|
||||
return matchI > matchJ
|
||||
}
|
||||
|
||||
// For equal match percentages, prioritize tenders with >30 days deadline
|
||||
daysI := tenderMatches[i].DaysUntilDeadline
|
||||
daysJ := tenderMatches[j].DaysUntilDeadline
|
||||
|
||||
// Both have >30 days or both have <=30 days, sort by deadline descending (more time is better)
|
||||
if (daysI > 30 && daysJ > 30) || (daysI <= 30 && daysJ <= 30) {
|
||||
return daysI > daysJ
|
||||
}
|
||||
|
||||
// One has >30 days, one has <=30 days - prioritize the one with >30 days
|
||||
return daysI > 30
|
||||
})
|
||||
|
||||
// Convert to TenderResponse objects with match percentages
|
||||
tenderResponses := make([]TenderResponse, 0, len(tenderMatches))
|
||||
for _, tenderMatch := range tenderMatches {
|
||||
tenderResponses = append(tenderResponses, *tenderMatch.Tender.ToTenderResponseWithMatch(companyCPVCodes))
|
||||
}
|
||||
|
||||
s.logger.Info("Tenders sorted by match percentage", map[string]interface{}{
|
||||
"company_id": *req.CompanyID,
|
||||
"company_cpv_codes": len(companyCPVCodes),
|
||||
"total_tenders": len(tenderResponses),
|
||||
})
|
||||
|
||||
return &ListTendersResponse{
|
||||
Tenders: tenders,
|
||||
Tenders: tenderResponses,
|
||||
Total: total,
|
||||
Limit: req.Limit,
|
||||
Offset: req.Offset,
|
||||
|
||||
Reference in New Issue
Block a user