a342da193e
- Updated feedback repository and service methods to use a consistent naming convention, changing `NewTenderRepository` and similar methods to `NewRepository`. - Refactored feedback handler methods to improve clarity by renaming methods such as `ListFeedback` to `Search` and `GetFeedback` to `Get`. - Enhanced feedback response structures to include detailed company and tender information, improving the clarity of feedback data returned to API consumers. - Updated Swagger documentation to reflect changes in feedback response structures and endpoint paths, ensuring accurate representation of the API for consumers.
315 lines
12 KiB
Go
315 lines
12 KiB
Go
package tender
|
|
|
|
import (
|
|
"tm/pkg/logger"
|
|
"tm/pkg/response"
|
|
|
|
"github.com/asaskevich/govalidator"
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// TenderHandler handles tender-related HTTP requests
|
|
type TenderHandler struct {
|
|
service Service
|
|
logger logger.Logger
|
|
}
|
|
|
|
// NewHandler creates a new tender handler
|
|
func NewHandler(service Service, logger logger.Logger) *TenderHandler {
|
|
return &TenderHandler{
|
|
service: service,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// *****************************************************
|
|
// * ADMIN HANDLERS *
|
|
// *****************************************************
|
|
|
|
// GetTender retrieves a tender by ID
|
|
// @Summary Get tender by ID
|
|
// @Description Retrieve a specific tender by its ID
|
|
// @Tags Admin-Tenders
|
|
// @Produce json
|
|
// @Param id path string true "Tender ID"
|
|
// @Success 200 {object} response.APIResponse{data=TenderResponse}
|
|
// @Failure 404 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/tenders/{id} [get]
|
|
// @Security BearerAuth
|
|
func (h *TenderHandler) Get(c echo.Context) error {
|
|
id := c.Param("id")
|
|
if id == "" {
|
|
return response.BadRequest(c, "Tender ID is required", "ID parameter cannot be empty")
|
|
}
|
|
|
|
tender, err := h.service.GetByID(c.Request().Context(), id)
|
|
if err != nil {
|
|
return response.NotFound(c, "Tender not found")
|
|
}
|
|
|
|
return response.Success(c, tender, "Tender retrieved successfully")
|
|
}
|
|
|
|
// UpdateTender updates an existing tender
|
|
// @Summary Update tender
|
|
// @Description Update an existing tender with the provided information
|
|
// @Tags Admin-Tenders
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Tender ID"
|
|
// @Param tender body UpdateTenderRequest true "Updated tender information"
|
|
// @Success 200 {object} response.APIResponse{data=TenderResponse}
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 404 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/tenders/{id} [put]
|
|
// @Security BearerAuth
|
|
func (h *TenderHandler) Update(c echo.Context) error {
|
|
id := c.Param("id")
|
|
if id == "" {
|
|
return response.BadRequest(c, "Tender ID is required", "ID parameter cannot be empty")
|
|
}
|
|
|
|
var req UpdateTenderRequest
|
|
if err := c.Bind(&req); err != nil {
|
|
return response.ValidationError(c, "Invalid request format", err.Error())
|
|
}
|
|
|
|
req.ID = id
|
|
|
|
if _, err := govalidator.ValidateStruct(req); err != nil {
|
|
return response.ValidationError(c, "Validation failed", err.Error())
|
|
}
|
|
|
|
tender, err := h.service.Update(c.Request().Context(), req)
|
|
if err != nil {
|
|
if err.Error() == "tender not found" {
|
|
return response.NotFound(c, "Tender not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to update tender")
|
|
}
|
|
|
|
return response.Success(c, tender, "Tender updated successfully")
|
|
}
|
|
|
|
// DeleteTender deletes a tender
|
|
// @Summary Delete tender
|
|
// @Description Delete a tender by its ID
|
|
// @Tags Admin-Tenders
|
|
// @Param id path string true "Tender ID"
|
|
// @Success 200 {object} response.APIResponse
|
|
// @Failure 404 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/tenders/{id} [delete]
|
|
// @Security BearerAuth
|
|
func (h *TenderHandler) Delete(c echo.Context) error {
|
|
id := c.Param("id")
|
|
if id == "" {
|
|
return response.BadRequest(c, "Tender ID is required", "ID parameter cannot be empty")
|
|
}
|
|
|
|
err := h.service.Delete(c.Request().Context(), id)
|
|
if err != nil {
|
|
if err.Error() == "tender not found" {
|
|
return response.NotFound(c, "Tender not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to delete tender")
|
|
}
|
|
|
|
return response.Success(c, map[string]interface{}{"deleted": true}, "Tender deleted successfully")
|
|
}
|
|
|
|
// ListTenders retrieves tenders with pagination and filtering
|
|
// @Summary List tenders
|
|
// @Description Retrieve tenders with pagination, filtering, and search capabilities with optional company-based matching
|
|
// @Tags Admin-Tenders
|
|
// @Produce json
|
|
// @Param company_id query string false "Company ID for match percentage calculation and sorting"
|
|
// @Param q query string false "Search query for title and description"
|
|
// @Param notice_type query string false "Filter by notice type code"
|
|
// @Param procurement_type query string false "Filter by procurement type code"
|
|
// @Param country_codes query []string false "Filter by country codes (comma-separated)"
|
|
// @Param status query []string false "Filter by status (comma-separated)"
|
|
// @Param source query []string false "Filter by source (comma-separated)"
|
|
// @Param min_estimated_value query number false "Minimum estimated value"
|
|
// @Param max_estimated_value query number false "Maximum estimated value"
|
|
// @Param currency query string false "Filter by currency"
|
|
// @Param deadline_from query number false "Deadline from"
|
|
// @Param deadline_to query number false "Deadline to"
|
|
// @Param publication_date_from query number false "Publication date from"
|
|
// @Param publication_date_to query number false "Publication date to"
|
|
// @Param languages query []string false "Filter by languages (comma-separated)"
|
|
// @Param buyer_organization_id query string false "Filter by buyer organization ID"
|
|
// @Param limit query int false "Number of items per page (default: 20, max: 100)"
|
|
// @Param offset query int false "Number of items to skip (default: 0)"
|
|
// @Param sort_by query string false "Sort by field"
|
|
// @Param sort_order query string false "Sort order (asc or desc)"
|
|
// @Success 200 {object} response.APIResponse{data=map[string]interface{}}
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/tenders [get]
|
|
// @Security BearerAuth
|
|
func (h *TenderHandler) Search(c echo.Context) error {
|
|
form, err := response.Parse[SearchForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
// Validate form
|
|
if _, err := govalidator.ValidateStruct(form); err != nil {
|
|
return response.ValidationError(c, "Validation failed", err.Error())
|
|
}
|
|
|
|
// Call service
|
|
tenders, err := h.service.Search(c.Request().Context(), form, response.NewPagination(c))
|
|
if err != nil {
|
|
return response.InternalServerError(c, "Failed to list tenders")
|
|
}
|
|
|
|
return response.SuccessWithMeta(c, tenders, tenders.Metadata, "Tenders retrieved successfully")
|
|
}
|
|
|
|
// *****************************************************
|
|
// * PUBLIC HANDLERS *
|
|
// *****************************************************
|
|
|
|
// GetPublicTenders retrieves public tenders for mobile app
|
|
// @Summary Get public tenders
|
|
// @Description Retrieve active tenders for mobile application users with automatic company-based matching from customer profile
|
|
// @Tags Tenders
|
|
// @Produce json
|
|
// @Param q query string false "Search query for title and description"
|
|
// @Param notice_type query string false "Filter by notice type code"
|
|
// @Param procurement_type query string false "Filter by procurement type code"
|
|
// @Param country_codes query []string false "Filter by country codes (comma-separated)"
|
|
// @Param status query []string false "Filter by status (comma-separated)"
|
|
// @Param source query []string false "Filter by source (comma-separated)"
|
|
// @Param min_estimated_value query number false "Minimum estimated value"
|
|
// @Param max_estimated_value query number false "Maximum estimated value"
|
|
// @Param currency query string false "Filter by currency"
|
|
// @Param deadline_from query number false "Deadline from"
|
|
// @Param deadline_to query number false "Deadline to"
|
|
// @Param publication_date_from query number false "Publication date from"
|
|
// @Param publication_date_to query number false "Publication date to"
|
|
// @Param languages query []string false "Filter by languages (comma-separated)"
|
|
// @Param buyer_organization_id query string false "Filter by buyer organization ID"
|
|
// @Param limit query int false "Number of items per page (default: 20, max: 100)"
|
|
// @Param offset query int false "Number of items to skip (default: 0)"
|
|
// @Param sort_by query string false "Sort by field"
|
|
// @Param sort_order query string false "Sort order (asc or desc)"
|
|
// @Success 200 {object} response.APIResponse{data=SearchResponse}
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tenders [get]
|
|
func (h *TenderHandler) GetPublicTenders(c echo.Context) error {
|
|
|
|
form, err := response.Parse[SearchForm](c)
|
|
if err != nil {
|
|
return response.BadRequest(c, "Invalid request format", "")
|
|
}
|
|
|
|
if _, err := govalidator.ValidateStruct(form); err != nil {
|
|
return response.ValidationError(c, err.Error(), "")
|
|
}
|
|
|
|
form.Status = []string{string(TenderStatusActive)}
|
|
|
|
// Get company ID from customer context for automatic matching
|
|
var companyID *string
|
|
if customerCompanyID, ok := c.Get("company_id").(string); ok {
|
|
companyID = &customerCompanyID
|
|
}
|
|
form.CompanyID = companyID
|
|
|
|
result, err := h.service.Search(c.Request().Context(), form, response.NewPagination(c))
|
|
if err != nil {
|
|
return response.InternalServerError(c, "Failed to retrieve tenders")
|
|
}
|
|
|
|
return response.SuccessWithMeta(c, result, result.Metadata, "Tenders retrieved successfully")
|
|
}
|
|
|
|
// GetPublicTenders retrieves public tenders for mobile app
|
|
// @Summary Get public tenders
|
|
// @Description Retrieve active tenders for mobile application users with automatic company-based matching from customer profile
|
|
// @Tags Tenders
|
|
// @Produce json
|
|
// @Param q query string false "Search query for title and description"
|
|
// @Param notice_type query string false "Filter by notice type code"
|
|
// @Param procurement_type query string false "Filter by procurement type code"
|
|
// @Param country_codes query []string false "Filter by country codes (comma-separated)"
|
|
// @Param status query []string false "Filter by status (comma-separated)"
|
|
// @Param source query []string false "Filter by source (comma-separated)"
|
|
// @Param min_estimated_value query number false "Minimum estimated value"
|
|
// @Param max_estimated_value query number false "Maximum estimated value"
|
|
// @Param currency query string false "Filter by currency"
|
|
// @Param deadline_from query number false "Deadline from"
|
|
// @Param deadline_to query number false "Deadline to"
|
|
// @Param publication_date_from query number false "Publication date from"
|
|
// @Param publication_date_to query number false "Publication date to"
|
|
// @Param languages query []string false "Filter by languages (comma-separated)"
|
|
// @Param buyer_organization_id query string false "Filter by buyer organization ID"
|
|
// @Param limit query int false "Number of items per page (default: 20, max: 100)"
|
|
// @Param offset query int false "Number of items to skip (default: 0)"
|
|
// @Param sort_by query string false "Sort by field"
|
|
// @Param sort_order query string false "Sort order (asc or desc)"
|
|
// @Success 200 {object} response.APIResponse{data=SearchResponse}
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tenders/recommend [get]
|
|
func (h *TenderHandler) RecommendTenders(c echo.Context) error {
|
|
|
|
form, err := response.Parse[SearchForm](c)
|
|
if err != nil {
|
|
return response.BadRequest(c, "Invalid request format", "")
|
|
}
|
|
|
|
if _, err := govalidator.ValidateStruct(form); err != nil {
|
|
return response.ValidationError(c, err.Error(), "")
|
|
}
|
|
|
|
form.Status = []string{string(TenderStatusActive)}
|
|
|
|
var companyID *string
|
|
if customerCompanyID, ok := c.Get("company_id").(string); ok {
|
|
companyID = &customerCompanyID
|
|
}
|
|
|
|
form.CompanyID = companyID
|
|
|
|
result, err := h.service.Recommend(c.Request().Context(), form, response.NewPagination(c))
|
|
if err != nil {
|
|
return response.InternalServerError(c, "Failed to retrieve tenders")
|
|
}
|
|
|
|
return response.SuccessWithMeta(c, result, result.Metadata, "Tenders retrieved successfully")
|
|
}
|
|
|
|
// GetPublicTenderDetails retrieves public tender details for mobile app
|
|
// @Summary Get public tender details
|
|
// @Description Retrieve detailed information about a specific tender for mobile application users
|
|
// @Tags Tenders
|
|
// @Produce json
|
|
// @Param id path string true "Tender ID"
|
|
// @Success 200 {object} response.APIResponse{data=map[string]interface{}}
|
|
// @Failure 404 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/tenders/details/{id} [get]
|
|
func (h *TenderHandler) GetPublicTenderDetails(c echo.Context) error {
|
|
id := c.Param("id")
|
|
if id == "" {
|
|
return response.BadRequest(c, "Tender ID is required", "ID parameter cannot be empty")
|
|
}
|
|
|
|
tender, err := h.service.GetByID(c.Request().Context(), id)
|
|
if err != nil {
|
|
return response.NotFound(c, "Tender not found")
|
|
}
|
|
|
|
return response.Success(c, tender, "Tender details retrieved successfully")
|
|
}
|