package tender // CreateTenderRequest represents a request to create a new tender type CreateTenderRequest struct { ContractNoticeID string `json:"contract_notice_id" valid:"required~Contract notice ID is required"` NoticePublicationID string `json:"notice_publication_id" valid:"required~Notice publication ID is required"` ContractFolderID string `json:"contract_folder_id" valid:"required~Contract folder ID is required"` NoticeTypeCode string `json:"notice_type_code" valid:"required~Notice type code is required"` NoticeSubTypeCode string `json:"notice_sub_type_code,omitempty"` NoticeLanguageCode string `json:"notice_language_code,omitempty" valid:"stringlength(3|3)~Language code must be 3 characters"` IssueDate int64 `json:"issue_date" valid:"required~Issue date is required"` // Unix milliseconds IssueTime int64 `json:"issue_time,omitempty"` // Unix milliseconds PublicationDate int64 `json:"publication_date,omitempty"` // Unix milliseconds GazetteID string `json:"gazette_id,omitempty"` Title string `json:"title" valid:"required~Title is required,stringlength(1|500)~Title must be between 1 and 500 characters"` Description string `json:"description,omitempty" valid:"stringlength(0|5000)~Description must not exceed 5000 characters"` ProcurementTypeCode string `json:"procurement_type_code,omitempty"` ProcedureCode string `json:"procedure_code,omitempty"` MainClassification string `json:"main_classification,omitempty"` AdditionalClassifications []string `json:"additional_classifications,omitempty"` EstimatedValue float64 `json:"estimated_value,omitempty" valid:"range(0|999999999999)~Estimated value must be between 0 and 999999999999"` Currency string `json:"currency,omitempty" valid:"stringlength(3|3)~Currency must be 3 characters"` Duration string `json:"duration,omitempty"` DurationUnit string `json:"duration_unit,omitempty"` TenderDeadline int64 `json:"tender_deadline,omitempty"` // Unix milliseconds PlaceOfPerformance string `json:"place_of_performance,omitempty"` CountryCode string `json:"country_code,omitempty" valid:"stringlength(2|2)~Country code must be 2 characters"` RegionCode string `json:"region_code,omitempty"` CityName string `json:"city_name,omitempty"` PostalCode string `json:"postal_code,omitempty"` DocumentURI string `json:"document_uri,omitempty" valid:"url~Document URI must be a valid URL"` TenderURL string `json:"tender_url,omitempty" valid:"url~Tender URL must be a valid URL"` BuyerOrganization *Organization `json:"buyer_organization,omitempty"` ReviewOrganization *Organization `json:"review_organization,omitempty"` Organizations []Organization `json:"organizations,omitempty"` SelectionCriteria []SelectionCriterion `json:"selection_criteria,omitempty"` OfficialLanguages []string `json:"official_languages,omitempty"` Source TenderSource `json:"source" valid:"required~Source is required"` SourceFileURL string `json:"source_file_url,omitempty" valid:"url~Source file URL must be a valid URL"` SourceFileName string `json:"source_file_name,omitempty"` } // UpdateTenderRequest represents a request to update an existing tender type UpdateTenderRequest struct { ID string `json:"id" valid:"required~Tender ID is required"` Title *string `json:"title,omitempty" valid:"stringlength(1|500)~Title must be between 1 and 500 characters"` Description *string `json:"description,omitempty" valid:"stringlength(0|5000)~Description must not exceed 5000 characters"` Status *TenderStatus `json:"status,omitempty"` EstimatedValue *float64 `json:"estimated_value,omitempty" valid:"range(0|999999999999)~Estimated value must be between 0 and 999999999999"` Currency *string `json:"currency,omitempty" valid:"stringlength(3|3)~Currency must be 3 characters"` TenderDeadline *int64 `json:"tender_deadline,omitempty"` // Unix milliseconds } // ListTendersRequest represents a request to list tenders with pagination and filtering type ListTendersRequest struct { Criteria TenderSearchCriteria `json:"criteria"` Limit int `json:"limit" valid:"range(1|100)~Limit must be between 1 and 100"` Offset int `json:"offset" valid:"range(0|999999)~Offset must be non-negative"` CompanyID *string `json:"company_id,omitempty"` // Optional company ID for match percentage calculation } // ListTendersResponse represents the response for listing tenders type ListTendersResponse struct { Tenders []TenderResponse `json:"tenders"` Total int64 `json:"total"` Limit int `json:"limit"` Offset int `json:"offset"` } // SearchTendersRequest represents a request to search tenders type SearchTendersRequest struct { Query string `json:"query" valid:"required~Search query is required,stringlength(1|200)~Query must be between 1 and 200 characters"` Criteria TenderSearchCriteria `json:"criteria"` } // SearchTendersResponse represents the response for searching tenders type SearchTendersResponse struct { Tenders []Tender `json:"tenders"` Query string `json:"query"` Total int `json:"total"` } // StartScrapingJobRequest represents a request to start a scraping job type StartScrapingJobRequest struct { Type ScrapingJobType `json:"type" valid:"required~Job type is required"` Config map[string]interface{} `json:"config,omitempty"` } // ListScrapingJobsRequest represents a request to list scraping jobs type ListScrapingJobsRequest struct { Limit int `json:"limit" valid:"range(1|100)~Limit must be between 1 and 100"` Offset int `json:"offset" valid:"range(0|999999)~Offset must be non-negative"` } // ListScrapingJobsResponse represents the response for listing scraping jobs type ListScrapingJobsResponse struct { Jobs []ScrapingJob `json:"jobs"` Total int64 `json:"total"` Limit int `json:"limit"` Offset int `json:"offset"` } // BatchProcessingResult represents the result of batch processing type BatchProcessingResult struct { Total int `json:"total"` Processed int `json:"processed"` Errors []string `json:"errors"` Tenders []*Tender `json:"tenders"` SuccessRate float64 `json:"success_rate"` } // DashboardData represents dashboard data type DashboardData struct { Statistics *TenderStatistics `json:"statistics"` RecentTenders []Tender `json:"recent_tenders"` ActiveJobs []ScrapingJob `json:"active_jobs"` } // TenderResponse represents a tender in API responses type TenderResponse struct { ID string `json:"id"` ContractNoticeID string `json:"contract_notice_id"` NoticePublicationID string `json:"notice_publication_id"` ContractFolderID string `json:"contract_folder_id"` NoticeTypeCode string `json:"notice_type_code"` NoticeSubTypeCode string `json:"notice_sub_type_code"` NoticeLanguageCode string `json:"notice_language_code"` IssueDate int64 `json:"issue_date"` IssueTime int64 `json:"issue_time"` PublicationDate int64 `json:"publication_date"` GazetteID string `json:"gazette_id"` Title string `json:"title"` Description string `json:"description"` ProcurementTypeCode string `json:"procurement_type_code"` ProcedureCode string `json:"procedure_code"` MainClassification string `json:"main_classification"` AdditionalClassifications []string `json:"additional_classifications"` EstimatedValue float64 `json:"estimated_value"` Currency string `json:"currency"` FormattedEstimatedValue string `json:"formatted_estimated_value"` Duration string `json:"duration"` DurationUnit string `json:"duration_unit"` TenderDeadline int64 `json:"tender_deadline"` PlaceOfPerformance string `json:"place_of_performance"` CountryCode string `json:"country_code"` RegionCode string `json:"region_code"` CityName string `json:"city_name"` PostalCode string `json:"postal_code"` DocumentURI string `json:"document_uri"` TenderURL string `json:"tender_url"` BuyerOrganization *Organization `json:"buyer_organization"` ReviewOrganization *Organization `json:"review_organization,omitempty"` Organizations []Organization `json:"organizations"` SelectionCriteria []SelectionCriterion `json:"selection_criteria"` OfficialLanguages []string `json:"official_languages"` Status TenderStatus `json:"status"` Source TenderSource `json:"source"` SourceFileURL string `json:"source_file_url"` SourceFileName string `json:"source_file_name"` ProcessingMetadata ProcessingMetadata `json:"processing_metadata"` IsActive bool `json:"is_active"` IsExpired bool `json:"is_expired"` HasErrors bool `json:"has_errors"` MatchPercentage *int `json:"match_percentage,omitempty"` // Match percentage for company (0-100) CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } // ScrapingJobResponse represents a scraping job in API responses type ScrapingJobResponse struct { ID string `json:"id"` Type ScrapingJobType `json:"type"` Status ScrapingJobStatus `json:"status"` StartedAt int64 `json:"started_at"` CompletedAt *int64 `json:"completed_at,omitempty"` Duration *int64 `json:"duration,omitempty"` Progress JobProgress `json:"progress"` Results JobResults `json:"results"` Errors []string `json:"errors,omitempty"` Config map[string]interface{} `json:"config,omitempty"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } // ScrapingStateResponse represents scraping state in API responses type ScrapingStateResponse struct { ID string `json:"id"` LastProcessedYear int `json:"last_processed_year"` LastProcessedNumber int `json:"last_processed_number"` LastRunAt int64 `json:"last_run_at"` NextRunAt int64 `json:"next_run_at"` IsRunning bool `json:"is_running"` CurrentJobID string `json:"current_job_id,omitempty"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } // ValidationErrorResponse represents validation errors in API responses type ValidationErrorResponse struct { Field string `json:"field"` Message string `json:"message"` Value string `json:"value,omitempty"` } // ErrorResponse represents error responses type ErrorResponse struct { Error string `json:"error"` Message string `json:"message"` ValidationErrors []ValidationErrorResponse `json:"validation_errors,omitempty"` Timestamp int64 `json:"timestamp"` } // ToTenderResponse converts a Tender entity to TenderResponse func (t *Tender) ToTenderResponse() *TenderResponse { return t.ToTenderResponseWithMatch(nil) } // ToTenderResponseWithMatch converts a Tender entity to TenderResponse with optional match percentage func (t *Tender) ToTenderResponseWithMatch(companyCPVCodes []string) *TenderResponse { response := &TenderResponse{ ID: t.ID.Hex(), ContractNoticeID: t.ContractNoticeID, NoticePublicationID: t.NoticePublicationID, ContractFolderID: t.ContractFolderID, NoticeTypeCode: t.NoticeTypeCode, NoticeSubTypeCode: t.NoticeSubTypeCode, NoticeLanguageCode: t.NoticeLanguageCode, IssueDate: t.IssueDate, IssueTime: t.IssueTime, PublicationDate: t.PublicationDate, GazetteID: t.GazetteID, Title: t.Title, Description: t.Description, ProcurementTypeCode: t.ProcurementTypeCode, ProcedureCode: t.ProcedureCode, MainClassification: t.MainClassification, AdditionalClassifications: t.AdditionalClassifications, EstimatedValue: t.EstimatedValue, Currency: t.Currency, FormattedEstimatedValue: t.GetFormattedEstimatedValue(), Duration: t.Duration, DurationUnit: t.DurationUnit, TenderDeadline: t.TenderDeadline, PlaceOfPerformance: t.PlaceOfPerformance, CountryCode: t.CountryCode, RegionCode: t.RegionCode, CityName: t.CityName, PostalCode: t.PostalCode, DocumentURI: t.DocumentURI, TenderURL: t.TenderURL, BuyerOrganization: t.BuyerOrganization, ReviewOrganization: t.ReviewOrganization, Organizations: t.Organizations, SelectionCriteria: t.SelectionCriteria, OfficialLanguages: t.OfficialLanguages, Status: t.Status, Source: t.Source, SourceFileURL: t.SourceFileURL, SourceFileName: t.SourceFileName, ProcessingMetadata: t.ProcessingMetadata, IsActive: t.IsActive(), IsExpired: t.IsExpired(), HasErrors: t.HasErrors(), CreatedAt: t.CreatedAt, UpdatedAt: t.UpdatedAt, } // Calculate match percentage if company CPV codes are provided if companyCPVCodes != nil { matchPercentage := t.CalculateMatchPercentage(companyCPVCodes) response.MatchPercentage = &matchPercentage } return response } // ToScrapingJobResponse converts a ScrapingJob entity to ScrapingJobResponse func (j *ScrapingJob) ToScrapingJobResponse() *ScrapingJobResponse { return &ScrapingJobResponse{ ID: j.ID, Type: j.Type, Status: j.Status, StartedAt: j.StartedAt, CompletedAt: j.CompletedAt, Duration: j.Duration, Progress: j.Progress, Results: j.Results, Errors: j.Errors, Config: j.Config, CreatedAt: j.CreatedAt, UpdatedAt: j.UpdatedAt, } } // ToScrapingStateResponse converts a ScrapingState entity to ScrapingStateResponse func (s *ScrapingState) ToScrapingStateResponse() *ScrapingStateResponse { return &ScrapingStateResponse{ ID: s.ID, LastProcessedYear: s.LastProcessedYear, LastProcessedNumber: s.LastProcessedNumber, LastRunAt: s.LastRunAt, NextRunAt: s.NextRunAt, IsRunning: s.IsRunning, CurrentJobID: s.CurrentJobID, CreatedAt: s.CreatedAt, UpdatedAt: s.UpdatedAt, } }