From a342da193e31549c3537e1df42a8edea0033af9b Mon Sep 17 00:00:00 2001 From: "n.nakhostin" Date: Wed, 24 Sep 2025 12:20:19 +0330 Subject: [PATCH] Refactor Feedback Module for Consistency and Clarity - 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. --- cmd/scraper/bootstrap/bootstrap.go | 2 +- cmd/web/docs/docs.go | 124 +++++++-- cmd/web/docs/swagger.json | 124 +++++++-- cmd/web/docs/swagger.yaml | 82 +++++- cmd/web/main.go | 42 +-- cmd/web/router/routes.go | 16 +- internal/assets/service.go | 4 +- internal/company/repository.go | 2 +- internal/company/service.go | 4 +- internal/company_category/repository.go | 2 +- internal/company_category/service.go | 4 +- internal/customer/repository.go | 2 +- internal/customer/service.go | 2 +- internal/feedback/entity.go | 52 ++-- internal/feedback/form.go | 128 +++------ internal/feedback/handler.go | 154 ++--------- internal/feedback/repository.go | 346 +++++------------------- internal/feedback/service.go | 230 +++++++++++----- internal/inquiry/repository.go | 14 +- internal/inquiry/service.go | 4 +- internal/notification/service.go | 4 +- internal/tender/handler.go | 4 +- internal/tender/repository.go | 2 +- internal/tender/service.go | 4 +- internal/tender_approval/repository.go | 2 +- internal/tender_approval/service.go | 4 +- internal/user/handler.go | 4 +- internal/user/repository.go | 2 +- internal/user/service.go | 4 +- 29 files changed, 666 insertions(+), 702 deletions(-) diff --git a/cmd/scraper/bootstrap/bootstrap.go b/cmd/scraper/bootstrap/bootstrap.go index daa839f..c1fc2e5 100644 --- a/cmd/scraper/bootstrap/bootstrap.go +++ b/cmd/scraper/bootstrap/bootstrap.go @@ -76,7 +76,7 @@ func InitMongoDB(conf config.MongoConfig, log logger.Logger) *mongo.ConnectionMa // init TED scraper func InitTEDScraper(config Config, mongoManager *mongo.ConnectionManager, appLogger logger.Logger) { // Initialize tender repository - tenderRepo := tender.NewTenderRepository(mongoManager, appLogger) + tenderRepo := tender.NewRepository(mongoManager, appLogger) // Initialize TED scraper tedScraper := ted.NewTEDScraper( diff --git a/cmd/web/docs/docs.go b/cmd/web/docs/docs.go index 2d91973..45ee7a4 100644 --- a/cmd/web/docs/docs.go +++ b/cmd/web/docs/docs.go @@ -5026,7 +5026,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "$ref": "#/definitions/feedback.CompanyFeedbackStatsResponse" + "$ref": "#/definitions/feedback.StatsResponse" } } } @@ -7675,27 +7675,6 @@ const docTemplate = `{ } } }, - "feedback.CompanyFeedbackStatsResponse": { - "type": "object", - "properties": { - "company_id": { - "type": "string" - }, - "last_updated": { - "description": "Unix timestamp", - "type": "integer" - }, - "total_dislikes": { - "type": "integer" - }, - "total_feedback": { - "type": "integer" - }, - "total_likes": { - "type": "integer" - } - } - }, "feedback.FeedbackListResponse": { "type": "object", "properties": { @@ -7713,6 +7692,9 @@ const docTemplate = `{ "feedback.FeedbackResponse": { "type": "object", "properties": { + "company": { + "$ref": "#/definitions/feedback.companyResponse" + }, "company_id": { "type": "string" }, @@ -7729,6 +7711,9 @@ const docTemplate = `{ "type": "string" }, "tender": { + "$ref": "#/definitions/feedback.tenderResponse" + }, + "tender_id": { "type": "string" }, "updated_at": { @@ -7747,6 +7732,29 @@ const docTemplate = `{ "FeedbackTypeDislike" ] }, + "feedback.StatsResponse": { + "type": "object", + "properties": { + "company": { + "$ref": "#/definitions/feedback.companyResponse" + }, + "company_id": { + "type": "string" + }, + "last_updated": { + "type": "integer" + }, + "total_dislikes": { + "type": "integer" + }, + "total_feedback": { + "type": "integer" + }, + "total_likes": { + "type": "integer" + } + } + }, "feedback.ToggleFeedbackForm": { "type": "object", "required": [ @@ -7770,6 +7778,78 @@ const docTemplate = `{ } } }, + "feedback.companyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "feedback.organizationResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "feedback.tenderResponse": { + "type": "object", + "properties": { + "buyer_organization": { + "$ref": "#/definitions/feedback.organizationResponse" + }, + "country_code": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "duration_unit": { + "type": "string" + }, + "estimated_value": { + "type": "number" + }, + "id": { + "type": "string" + }, + "notice_publication_id": { + "type": "string" + }, + "procedure_code": { + "type": "string" + }, + "procurement_type_code": { + "type": "string" + }, + "publication_date": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "submission_deadline": { + "type": "integer" + }, + "tender_deadline": { + "type": "integer" + }, + "title": { + "type": "string" + } + } + }, "inquiry.CreateInquiryForm": { "type": "object", "properties": { diff --git a/cmd/web/docs/swagger.json b/cmd/web/docs/swagger.json index f6f9637..db31896 100644 --- a/cmd/web/docs/swagger.json +++ b/cmd/web/docs/swagger.json @@ -5020,7 +5020,7 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/feedback.CompanyFeedbackStatsResponse" + "$ref": "#/definitions/feedback.StatsResponse" } } } @@ -7669,27 +7669,6 @@ } } }, - "feedback.CompanyFeedbackStatsResponse": { - "type": "object", - "properties": { - "company_id": { - "type": "string" - }, - "last_updated": { - "description": "Unix timestamp", - "type": "integer" - }, - "total_dislikes": { - "type": "integer" - }, - "total_feedback": { - "type": "integer" - }, - "total_likes": { - "type": "integer" - } - } - }, "feedback.FeedbackListResponse": { "type": "object", "properties": { @@ -7707,6 +7686,9 @@ "feedback.FeedbackResponse": { "type": "object", "properties": { + "company": { + "$ref": "#/definitions/feedback.companyResponse" + }, "company_id": { "type": "string" }, @@ -7723,6 +7705,9 @@ "type": "string" }, "tender": { + "$ref": "#/definitions/feedback.tenderResponse" + }, + "tender_id": { "type": "string" }, "updated_at": { @@ -7741,6 +7726,29 @@ "FeedbackTypeDislike" ] }, + "feedback.StatsResponse": { + "type": "object", + "properties": { + "company": { + "$ref": "#/definitions/feedback.companyResponse" + }, + "company_id": { + "type": "string" + }, + "last_updated": { + "type": "integer" + }, + "total_dislikes": { + "type": "integer" + }, + "total_feedback": { + "type": "integer" + }, + "total_likes": { + "type": "integer" + } + } + }, "feedback.ToggleFeedbackForm": { "type": "object", "required": [ @@ -7764,6 +7772,78 @@ } } }, + "feedback.companyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "feedback.organizationResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "feedback.tenderResponse": { + "type": "object", + "properties": { + "buyer_organization": { + "$ref": "#/definitions/feedback.organizationResponse" + }, + "country_code": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "duration_unit": { + "type": "string" + }, + "estimated_value": { + "type": "number" + }, + "id": { + "type": "string" + }, + "notice_publication_id": { + "type": "string" + }, + "procedure_code": { + "type": "string" + }, + "procurement_type_code": { + "type": "string" + }, + "publication_date": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "submission_deadline": { + "type": "integer" + }, + "tender_deadline": { + "type": "integer" + }, + "title": { + "type": "string" + } + } + }, "inquiry.CreateInquiryForm": { "type": "object", "properties": { diff --git a/cmd/web/docs/swagger.yaml b/cmd/web/docs/swagger.yaml index 5520cdc..440ab52 100644 --- a/cmd/web/docs/swagger.yaml +++ b/cmd/web/docs/swagger.yaml @@ -512,20 +512,6 @@ definitions: example: reset_token_here type: string type: object - feedback.CompanyFeedbackStatsResponse: - properties: - company_id: - type: string - last_updated: - description: Unix timestamp - type: integer - total_dislikes: - type: integer - total_feedback: - type: integer - total_likes: - type: integer - type: object feedback.FeedbackListResponse: properties: feedback: @@ -537,6 +523,8 @@ definitions: type: object feedback.FeedbackResponse: properties: + company: + $ref: '#/definitions/feedback.companyResponse' company_id: type: string created_at: @@ -548,6 +536,8 @@ definitions: id: type: string tender: + $ref: '#/definitions/feedback.tenderResponse' + tender_id: type: string updated_at: type: integer @@ -560,6 +550,21 @@ definitions: x-enum-varnames: - FeedbackTypeLike - FeedbackTypeDislike + feedback.StatsResponse: + properties: + company: + $ref: '#/definitions/feedback.companyResponse' + company_id: + type: string + last_updated: + type: integer + total_dislikes: + type: integer + total_feedback: + type: integer + total_likes: + type: integer + type: object feedback.ToggleFeedbackForm: properties: feedback_type: @@ -574,6 +579,53 @@ definitions: - feedback_type - tender_id type: object + feedback.companyResponse: + properties: + id: + type: string + name: + type: string + type: object + feedback.organizationResponse: + properties: + name: + type: string + type: object + feedback.tenderResponse: + properties: + buyer_organization: + $ref: '#/definitions/feedback.organizationResponse' + country_code: + type: string + currency: + type: string + description: + type: string + duration: + type: string + duration_unit: + type: string + estimated_value: + type: number + id: + type: string + notice_publication_id: + type: string + procedure_code: + type: string + procurement_type_code: + type: string + publication_date: + type: integer + status: + type: string + submission_deadline: + type: integer + tender_deadline: + type: integer + title: + type: string + type: object inquiry.CreateInquiryForm: properties: company_name: @@ -4508,7 +4560,7 @@ paths: - $ref: '#/definitions/response.APIResponse' - properties: data: - $ref: '#/definitions/feedback.CompanyFeedbackStatsResponse' + $ref: '#/definitions/feedback.StatsResponse' type: object "400": description: Bad request - Missing company association diff --git a/cmd/web/main.go b/cmd/web/main.go index 160fe6b..980f6df 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -134,14 +134,14 @@ func main() { customerAuthService := bootstrap.InitAuthorizationService(conf.CustomerAuth.JWT, redisClient, logger) // Initialize repositories with MongoDB connection manager - userRepository := user.NewUserRepository(mongoManager, logger) - customerRepository := customer.NewCustomerRepository(mongoManager, logger) - companyRepository := company.NewCompanyRepository(mongoManager, logger) - categoryRepository := company_category.NewCategoryRepository(mongoManager, logger) - tenderRepository := tender.NewTenderRepository(mongoManager, logger) - feedbackRepo := feedback.NewFeedbackRepository(mongoManager, logger) - tenderApprovalRepository := tender_approval.NewTenderApprovalRepository(mongoManager, logger) - inquiryRepository := inquiry.NewInquiryRepository(mongoManager, logger) + userRepository := user.NewRepository(mongoManager, logger) + customerRepository := customer.NewRepository(mongoManager, logger) + companyRepository := company.NewRepository(mongoManager, logger) + categoryRepository := company_category.NewRepository(mongoManager, logger) + tenderRepository := tender.NewRepository(mongoManager, logger) + feedbackRepo := feedback.NewRepository(mongoManager, logger) + tenderApprovalRepository := tender_approval.NewRepository(mongoManager, logger) + inquiryRepository := inquiry.NewRepository(mongoManager, logger) logger.Info("Repositories initialized successfully", map[string]interface{}{ "repositories": []string{"customer", "user", "company", "category", "tender", "feedback", "tender_approval", "inquiry"}, }) @@ -150,27 +150,27 @@ func main() { userValidator := user.NewValidationService() // Initialize services with repositories - userService := user.NewUserService(userRepository, logger, userAuthService, userValidator, notificationSDK) - categoryService := company_category.NewCategoryService(categoryRepository, logger) - companyService := company.NewCompanyService(companyRepository, categoryService, logger) - customerService := customer.New(customerRepository, logger, customerAuthService, companyService, redisClient, notificationSDK) - tenderService := tender.NewTenderService(tenderRepository, companyService, logger) - feedbackService := feedback.NewFeedbackService(feedbackRepo, tenderService, logger) - tenderApprovalService := tender_approval.NewTenderApprovalService(tenderApprovalRepository, tenderService, logger) - inquiryService := inquiry.NewInquiryService(inquiryRepository, notificationSDK, logger) - flagService := assets.NewFlagService(logger, conf.Assets.FlagsPath) - notificationService := notification.NewNotificationService(notificationSDK, userService, customerService, logger) + userService := user.NewService(userRepository, logger, userAuthService, userValidator, notificationSDK) + categoryService := company_category.NewService(categoryRepository, logger) + companyService := company.NewService(companyRepository, categoryService, logger) + customerService := customer.NewService(customerRepository, logger, customerAuthService, companyService, redisClient, notificationSDK) + tenderService := tender.NewService(tenderRepository, companyService, logger) + feedbackService := feedback.NewService(feedbackRepo, tenderService, companyService, logger) + tenderApprovalService := tender_approval.NewService(tenderApprovalRepository, tenderService, logger) + inquiryService := inquiry.NewService(inquiryRepository, notificationSDK, logger) + flagService := assets.NewService(logger, conf.Assets.FlagsPath) + notificationService := notification.NewService(notificationSDK, userService, customerService, logger) logger.Info("Services initialized successfully", map[string]interface{}{ "services": []string{"customer", "user", "company", "category", "tender", "feedback", "tender_approval", "inquiry", "flag", "notification"}, }) // Initialize handlers with services - userHandler := user.NewUserHandler(userService, logger, userValidator, userAuthService) + userHandler := user.NewHandler(userService, logger, userValidator, userAuthService) customerHandler := customer.NewHandler(customerService, userHandler, customerAuthService, logger) companyHandler := company.NewHandler(companyService, userHandler, logger) categoryHandler := company_category.NewHandler(categoryService, logger) - tenderHandler := tender.NewTenderHandler(tenderService, logger) - feedbackHandler := feedback.NewFeedbackHandler(feedbackService, userHandler, customerHandler, logger) + tenderHandler := tender.NewHandler(tenderService, logger) + feedbackHandler := feedback.NewHandler(feedbackService, userHandler, customerHandler, logger) tenderApprovalHandler := tender_approval.NewHandler(tenderApprovalService, logger) inquiryHandler := inquiry.NewHandler(inquiryService, logger) flagHandler := assets.NewHandler(flagService, logger) diff --git a/cmd/web/router/routes.go b/cmd/web/router/routes.go index 890e09b..30e2c45 100644 --- a/cmd/web/router/routes.go +++ b/cmd/web/router/routes.go @@ -99,9 +99,9 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler feedbackGP := adminV1.Group("/feedback") { feedbackGP.Use(userHandler.AuthMiddleware()) - feedbackGP.GET("", feedbackHandler.ListFeedback) - feedbackGP.GET("/:id", feedbackHandler.GetFeedback) - feedbackGP.DELETE("/:id", feedbackHandler.DeleteFeedback) + feedbackGP.GET("", feedbackHandler.Search) + feedbackGP.GET("/:id", feedbackHandler.Get) + feedbackGP.DELETE("/:id", feedbackHandler.Delete) } // Admin Tender-Approvals Routes @@ -175,11 +175,11 @@ func RegisterPublicRoutes(e *echo.Echo, customerHandler *customer.Handler, tende feedbackGP := v1.Group("/feedback") { feedbackGP.Use(customerHandler.AuthMiddleware()) - feedbackGP.POST("", feedbackHandler.ToggleFeedback) - feedbackGP.GET("", feedbackHandler.PublicListFeedback) - feedbackGP.GET("/tender/:tender_id", feedbackHandler.PublicGetFeedbackByTenderID) - feedbackGP.GET("/:id", feedbackHandler.PublicGetFeedback) - feedbackGP.GET("/stats/company", feedbackHandler.GetCompanyFeedbackStats) + feedbackGP.POST("", feedbackHandler.Toggle) + feedbackGP.GET("", feedbackHandler.PublicList) + feedbackGP.GET("/tender/:tender_id", feedbackHandler.PublicGetByTenderID) + feedbackGP.GET("/:id", feedbackHandler.PublicGet) + feedbackGP.GET("/stats/company", feedbackHandler.GetCompanyStats) } // Public Tender Approvals Routes diff --git a/internal/assets/service.go b/internal/assets/service.go index 497b712..17bd06b 100644 --- a/internal/assets/service.go +++ b/internal/assets/service.go @@ -20,8 +20,8 @@ type flagService struct { flagsPath string } -// NewFlagService creates a new flag service -func NewFlagService(logger logger.Logger, flagsPath string) Service { +// NewService creates a new flag service +func NewService(logger logger.Logger, flagsPath string) Service { return &flagService{ logger: logger, flagsPath: flagsPath, diff --git a/internal/company/repository.go b/internal/company/repository.go index b82cdc7..85be786 100644 --- a/internal/company/repository.go +++ b/internal/company/repository.go @@ -33,7 +33,7 @@ type companyRepository struct { } // NewCompanyRepository creates a new company repository -func NewCompanyRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { +func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { // Create indexes using the ORM's index management indexes := []orm.Index{ *orm.NewIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}), diff --git a/internal/company/service.go b/internal/company/service.go index 4c6f717..eca67d2 100644 --- a/internal/company/service.go +++ b/internal/company/service.go @@ -36,8 +36,8 @@ type companyService struct { logger logger.Logger } -// NewCompanyService creates a new company service -func NewCompanyService(repository Repository, categoryService company_category.Service, logger logger.Logger) Service { +// NewService creates a new company service +func NewService(repository Repository, categoryService company_category.Service, logger logger.Logger) Service { return &companyService{ repository: repository, categoryService: categoryService, diff --git a/internal/company_category/repository.go b/internal/company_category/repository.go index cf34048..46084e7 100644 --- a/internal/company_category/repository.go +++ b/internal/company_category/repository.go @@ -30,7 +30,7 @@ type categoryRepository struct { } // NewCategoryRepository creates a new category repository -func NewCategoryRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { +func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { // Create indexes using the ORM's index management indexes := []orm.Index{ *orm.NewIndex("name_idx", bson.D{{Key: "name", Value: 1}}), diff --git a/internal/company_category/service.go b/internal/company_category/service.go index 26523ea..1d48033 100644 --- a/internal/company_category/service.go +++ b/internal/company_category/service.go @@ -30,8 +30,8 @@ type categoryService struct { logger logger.Logger } -// NewCategoryService creates a new category service -func NewCategoryService(repository Repository, logger logger.Logger) Service { +// NewService creates a new category service +func NewService(repository Repository, logger logger.Logger) Service { return &categoryService{ repository: repository, logger: logger, diff --git a/internal/customer/repository.go b/internal/customer/repository.go index 6de48eb..036edaf 100644 --- a/internal/customer/repository.go +++ b/internal/customer/repository.go @@ -39,7 +39,7 @@ func collection() string { } // NewCustomerRepository creates a new customer repository -func NewCustomerRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { +func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { // Create indexes using the ORM's index management indexes := []orm.Index{ *orm.CreateUniqueIndex("username_idx", bson.D{{Key: "username", Value: 1}}), diff --git a/internal/customer/service.go b/internal/customer/service.go index d88b509..1b69721 100644 --- a/internal/customer/service.go +++ b/internal/customer/service.go @@ -63,7 +63,7 @@ type customerService struct { } // New creates a new customer service -func New(repository Repository, logger logger.Logger, authService authorization.AuthorizationService, companyService company.Service, redisClient redis.Client, notificationSDK notification.SDK) Service { +func NewService(repository Repository, logger logger.Logger, authService authorization.AuthorizationService, companyService company.Service, redisClient redis.Client, notificationSDK notification.SDK) Service { return &customerService{ repository: repository, logger: logger, diff --git a/internal/feedback/entity.go b/internal/feedback/entity.go index 57f1729..ab5d050 100644 --- a/internal/feedback/entity.go +++ b/internal/feedback/entity.go @@ -1,8 +1,9 @@ package feedback import ( - "time" "tm/pkg/mongo" + + "go.mongodb.org/mongo-driver/v2/bson" ) // FeedbackType represents the type of feedback @@ -22,31 +23,36 @@ type Feedback struct { FeedbackType FeedbackType `bson:"feedback_type" json:"feedback_type" validate:"required,oneof=like dislike"` } -// FeedbackSummary represents aggregated feedback statistics for a tender -type FeedbackSummary struct { - TenderID string `bson:"tender_id" json:"tender_id"` - TotalLikes int64 `bson:"total_likes" json:"total_likes"` - TotalDislikes int64 `bson:"total_dislikes" json:"total_dislikes"` - LastUpdated int64 `bson:"last_updated" json:"last_updated"` // Unix timestamp +// SetID sets the feedback ID (implements IDSetter interface) +func (f *Feedback) SetID(id string) { + f.ID, _ = bson.ObjectIDFromHex(id) } -// FeedbackSearchCriteria represents search criteria for feedback -type FeedbackSearchCriteria struct { - TenderID *string `json:"tender_id,omitempty"` - CustomerID *string `json:"customer_id,omitempty"` - CompanyID *string `json:"company_id,omitempty"` - FeedbackType *FeedbackType `json:"feedback_type,omitempty"` - DateFrom *int64 `json:"date_from,omitempty"` // Unix timestamp - DateTo *int64 `json:"date_to,omitempty"` // Unix timestamp - Limit int `json:"limit,omitempty"` - Offset int `json:"offset,omitempty"` -} - -// GetID returns the feedback ID +// GetID returns the feedback ID (implements IDGetter interface) func (f *Feedback) GetID() string { return f.ID.Hex() } +// SetCreatedAt sets the created timestamp (implements Timestamp interface) +func (f *Feedback) SetCreatedAt(timestamp int64) { + f.CreatedAt = timestamp +} + +// SetUpdatedAt sets the updated timestamp (implements Timestamp interface) +func (f *Feedback) SetUpdatedAt(timestamp int64) { + f.UpdatedAt = timestamp +} + +// GetCreatedAt returns the created timestamp (implements Timestamp interface) +func (f *Feedback) GetCreatedAt() int64 { + return f.CreatedAt +} + +// GetUpdatedAt returns the updated timestamp (implements Timestamp interface) +func (f *Feedback) GetUpdatedAt() int64 { + return f.UpdatedAt +} + // IsLike returns true if the feedback is a like func (f *Feedback) IsLike() bool { return f.FeedbackType == FeedbackTypeLike @@ -56,9 +62,3 @@ func (f *Feedback) IsLike() bool { func (f *Feedback) IsDislike() bool { return f.FeedbackType == FeedbackTypeDislike } - -// UpdateFeedbackType updates the feedback type and updated timestamp -func (f *Feedback) UpdateFeedbackType(feedbackType FeedbackType) { - f.FeedbackType = feedbackType - f.UpdatedAt = time.Now().Unix() -} diff --git a/internal/feedback/form.go b/internal/feedback/form.go index 16e61cb..e492ca4 100644 --- a/internal/feedback/form.go +++ b/internal/feedback/form.go @@ -1,61 +1,37 @@ package feedback -import ( - "time" - "tm/pkg/response" -) - -// ListFeedbackForm represents the form for listing feedback -type ListFeedbackForm struct { - Feedbacks []FeedbackResponse `json:"feedbacks"` - DateFrom *int64 `json:"date_from,omitempty" validate:"omitempty,min=0"` - DateTo *int64 `json:"date_to,omitempty" validate:"omitempty,min=0"` - Limit int `json:"limit" validate:"required,min=1,max=100"` - Offset int `json:"offset" validate:"required,min=0"` -} +import "tm/pkg/response" // ToggleFeedbackForm represents the form for toggling feedback (like/dislike) type ToggleFeedbackForm struct { - TenderID string `json:"tender_id" validate:"required"` - FeedbackType FeedbackType `json:"feedback_type" validate:"required,oneof=like dislike"` + Tender string `json:"tender_id" validate:"required"` + Type FeedbackType `json:"feedback_type" validate:"required,oneof=like dislike"` } -// FeedbackResponse represents the response for feedback operations -type FeedbackResponse struct { - ID string `json:"id"` - FeedbackType FeedbackType `json:"feedback_type"` - UpdatedAt int64 `json:"updated_at"` - CreatedAt int64 `json:"created_at"` - TenderID string `json:"tender"` - CustomerID *string `json:"customer_id"` - CompanyId *string `json:"company_id"` +// SearchForm represents search criteria for feedback +type SearchForm struct { + Tender *string `json:"tender_id,omitempty" valid:"optional"` + Customer *string `json:"customer_id,omitempty" valid:"optional"` + Company *string `json:"company_id,omitempty" valid:"optional"` + Type *FeedbackType `json:"feedback_type,omitempty" valid:"optional,in(like|dislike)"` + DateFrom *int64 `json:"date_from,omitempty" valid:"optional"` + DateTo *int64 `json:"date_to,omitempty" valid:"optional"` } -// FeedbackListResponse represents a paginated list of feedback -type FeedbackListResponse struct { - Feedback []*FeedbackResponse `json:"feedback"` - Meta *response.Meta `json:"meta"` -} - -// FeedbackListResponse represents a paginated list of feedback -type FeedbackListWithTenderResponse struct { - Feedback []*FeedbackWithTenderResponse `json:"feedback"` - Meta *response.Meta `json:"meta"` -} -type FeedbackWithTenderResponse struct { - ID string `json:"id"` - FeedbackType FeedbackType `json:"feedback_type"` - UpdatedAt int64 `json:"updated_at"` - CreatedAt int64 `json:"created_at"` - TenderID string `json:"tender_id"` - CustomerID *string `json:"customer_id"` - CompanyId *string `json:"company_id"` - Tender *tenderDetails `json:"tender"` -} - -// tenderDetails represents essential tender information for the response type ( - tenderDetails struct { + FeedbackResponse struct { + ID string `json:"id"` + FeedbackType FeedbackType `json:"feedback_type"` + UpdatedAt int64 `json:"updated_at"` + CreatedAt int64 `json:"created_at"` + TenderID string `json:"tender_id"` + CustomerID *string `json:"customer_id"` + CompanyId *string `json:"company_id"` + Tender *tenderResponse `json:"tender"` + Company *companyResponse `json:"company"` + } + + tenderResponse struct { ID string `json:"id"` NoticePublicationID string `json:"notice_publication_id"` PublicationDate int64 `json:"publication_date"` @@ -73,39 +49,23 @@ type ( BuyerOrganization *organizationResponse `json:"buyer_organization"` Status string `json:"status"` } + organizationResponse struct { Name string `json:"name"` } + + companyResponse struct { + ID string `json:"id"` + Name string `json:"name"` + } ) -// CompanyFeedbackStatsResponse represents the response for company feedback statistics -type CompanyFeedbackStatsResponse struct { - CompanyID string `json:"company_id"` - TotalLikes int64 `json:"total_likes"` - TotalDislikes int64 `json:"total_dislikes"` - TotalFeedback int64 `json:"total_feedback"` - LastUpdated int64 `json:"last_updated"` // Unix timestamp +type FeedbackListResponse struct { + Feedback []*FeedbackResponse `json:"feedback"` + Meta *response.Meta `json:"meta"` } -// GetDateFromTime returns the date_from as time.Time -func (f *ListFeedbackForm) GetDateFromTime() *time.Time { - if f.DateFrom == nil { - return nil - } - t := time.Unix(*f.DateFrom, 0) - return &t -} - -// GetDateToTime returns the date_to as time.Time -func (f *ListFeedbackForm) GetDateToTime() *time.Time { - if f.DateTo == nil { - return nil - } - t := time.Unix(*f.DateTo, 0) - return &t -} - -func (f *Feedback) ToResponse() *FeedbackResponse { +func (f *Feedback) ToResponse(tender *tenderResponse, company *companyResponse) *FeedbackResponse { return &FeedbackResponse{ ID: f.ID.Hex(), FeedbackType: f.FeedbackType, @@ -114,18 +74,16 @@ func (f *Feedback) ToResponse() *FeedbackResponse { CompanyId: f.CompanyID, UpdatedAt: f.UpdatedAt, CreatedAt: f.CreatedAt, + Tender: tender, + Company: company, } } -func (f *Feedback) ToResponseWithTender(tender *tenderDetails) *FeedbackWithTenderResponse { - return &FeedbackWithTenderResponse{ - ID: f.ID.Hex(), - FeedbackType: f.FeedbackType, - TenderID: f.TenderID, - CustomerID: f.CustomerID, - CompanyId: f.CompanyID, - UpdatedAt: f.UpdatedAt, - CreatedAt: f.CreatedAt, - Tender: tender, - } +type StatsResponse struct { + CompanyID string `json:"company_id"` + Company *companyResponse `json:"company"` + TotalLikes int64 `json:"total_likes"` + TotalDislikes int64 `json:"total_dislikes"` + TotalFeedback int64 `json:"total_feedback"` + LastUpdated int64 `json:"last_updated"` } diff --git a/internal/feedback/handler.go b/internal/feedback/handler.go index f1962dd..3f8c947 100644 --- a/internal/feedback/handler.go +++ b/internal/feedback/handler.go @@ -1,7 +1,6 @@ package feedback import ( - "strconv" "tm/internal/customer" "tm/internal/user" "tm/pkg/logger" @@ -13,15 +12,15 @@ import ( // Handler handles HTTP requests for feedback operations type Handler struct { - service FeedbackService + service Service logger logger.Logger userHandler *user.Handler customerHandler *customer.Handler } -// NewFeedbackHandler creates a new feedback handler -func NewFeedbackHandler( - service FeedbackService, +// NewHandler creates a new feedback handler +func NewHandler( + service Service, userHandler *user.Handler, customerHandler *customer.Handler, logger logger.Logger, @@ -55,65 +54,13 @@ func NewFeedbackHandler( // @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error" // @Security BearerAuth // @Router /admin/v1/feedback [get] -func (h *Handler) ListFeedback(c echo.Context) error { - // Parse query parameters - limitStr := c.QueryParam("limit") - offsetStr := c.QueryParam("offset") - - limit := 20 // default limit - offset := 0 // default offset - - if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 100 { - limit = l +func (h *Handler) Search(c echo.Context) error { + form, err := response.Parse[SearchForm](c) + if err != nil { + return response.ValidationError(c, "Invalid request data", err.Error()) } - if o, err := strconv.Atoi(offsetStr); err == nil && o >= 0 { - offset = o - } - - // Build search criteria - criteria := FeedbackSearchCriteria{ - Limit: limit, - Offset: offset, - } - - // Parse optional filters - if tenderID := c.QueryParam("tender_id"); tenderID != "" { - criteria.TenderID = &tenderID - } - - if customerID := c.QueryParam("customer_id"); customerID != "" { - criteria.CustomerID = &customerID - } - - if companyID := c.QueryParam("company_id"); companyID != "" { - criteria.CompanyID = &companyID - } - - if feedbackType := c.QueryParam("feedback_type"); feedbackType != "" { - ft := FeedbackType(feedbackType) - criteria.FeedbackType = &ft - } - - if dateFrom := c.QueryParam("date_from"); dateFrom != "" { - if df, err := strconv.ParseInt(dateFrom, 10, 64); err == nil { - criteria.DateFrom = &df - } - } - - if dateTo := c.QueryParam("date_to"); dateTo != "" { - if dt, err := strconv.ParseInt(dateTo, 10, 64); err == nil { - criteria.DateTo = &dt - } - } - - // Validate date range - if criteria.DateFrom != nil && criteria.DateTo != nil && *criteria.DateFrom >= *criteria.DateTo { - return response.ValidationError(c, "Invalid date range", "date_from must be before date_to") - } - - // Get enhanced feedback list with related data - feedbackList, err := h.service.ListFeedback(c.Request().Context(), criteria, limit, offset) + feedbackList, err := h.service.Search(c.Request().Context(), *form, response.NewPagination(c)) if err != nil { return response.InternalServerError(c, "Failed to retrieve feedback list") } @@ -134,13 +81,13 @@ func (h *Handler) ListFeedback(c echo.Context) error { // @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error" // @Security BearerAuth // @Router /admin/v1/feedback/{id} [get] -func (h *Handler) GetFeedback(c echo.Context) error { +func (h *Handler) Get(c echo.Context) error { feedbackID := c.Param("id") if feedbackID == "" { return response.BadRequest(c, "Feedback ID is required", "Feedback ID parameter is missing") } - feedback, err := h.service.GetFeedback(c.Request().Context(), feedbackID) + feedback, err := h.service.Get(c.Request().Context(), feedbackID) if err != nil { if err == ErrFeedbackNotFound { return response.NotFound(c, "Feedback not found") @@ -169,14 +116,14 @@ func (h *Handler) GetFeedback(c echo.Context) error { // @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error" // @Security BearerAuth // @Router /admin/v1/feedback/{id} [delete] -func (h *Handler) DeleteFeedback(c echo.Context) error { +func (h *Handler) Delete(c echo.Context) error { id := c.Param("id") if id == "" { return response.BadRequest(c, "Feedback ID is required", "") } // Call service - err := h.service.DeleteFeedback(c.Request().Context(), id) + err := h.service.Delete(c.Request().Context(), id) if err != nil { switch err { case ErrFeedbackNotFound: @@ -206,7 +153,7 @@ func (h *Handler) DeleteFeedback(c echo.Context) error { // @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error" // @Security BearerAuth // @Router /api/v1/feedback [post] -func (h *Handler) ToggleFeedback(c echo.Context) error { +func (h *Handler) Toggle(c echo.Context) error { form, err := response.Parse[ToggleFeedbackForm](c) if err != nil { return response.ValidationError(c, "Invalid request data", err.Error()) @@ -230,7 +177,7 @@ func (h *Handler) ToggleFeedback(c echo.Context) error { } // Call service - result, err := h.service.ToggleFeedback(c.Request().Context(), *form, &customerID, &companyID) + result, err := h.service.Toggle(c.Request().Context(), *form, &customerID, &companyID) if err != nil { return response.InternalServerError(c, "Failed to toggle feedback") } @@ -258,61 +205,20 @@ func (h *Handler) ToggleFeedback(c echo.Context) error { // @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error" // @Security BearerAuth // @Router /api/v1/feedback [get] -func (h *Handler) PublicListFeedback(c echo.Context) error { - // Parse query parameters - limitStr := c.QueryParam("limit") - offsetStr := c.QueryParam("offset") - - limit := 20 - offset := 0 - - if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 100 { - limit = l - } - - if o, err := strconv.Atoi(offsetStr); err == nil && o >= 0 { - offset = o - } - +func (h *Handler) PublicList(c echo.Context) error { companyID, err := user.GetCompanyIDFromContext(c) if err != nil { return response.BadRequest(c, "Company ID required", "User must be associated with a company") } - // Build search criteria - criteria := FeedbackSearchCriteria{ - Limit: limit, - Offset: offset, - CompanyID: &companyID, - } - - if feedbackType := c.QueryParam("feedback_type"); feedbackType != "" { - ft := FeedbackType(feedbackType) - if ft != FeedbackTypeLike && ft != FeedbackTypeDislike { - return response.BadRequest(c, "Invalid feedback type", "Must be 'like' or 'dislike'") - } - criteria.FeedbackType = &ft - } - - if dateFrom := c.QueryParam("date_from"); dateFrom != "" { - if df, err := strconv.ParseInt(dateFrom, 10, 64); err == nil && df >= 0 { - criteria.DateFrom = &df - } - } - - if dateTo := c.QueryParam("date_to"); dateTo != "" { - if dt, err := strconv.ParseInt(dateTo, 10, 64); err == nil && dt >= 0 { - criteria.DateTo = &dt - } - } - - // Validate date range - if criteria.DateFrom != nil && criteria.DateTo != nil && *criteria.DateFrom >= *criteria.DateTo { - return response.ValidationError(c, "Invalid date range", "date_from must be before date_to") + form, err := response.Parse[SearchForm](c) + if err != nil { + return response.ValidationError(c, "Invalid request data", err.Error()) } + form.Company = &companyID // Call service - result, err := h.service.ListFeedback(c.Request().Context(), criteria, limit, offset) + result, err := h.service.Search(c.Request().Context(), *form, response.NewPagination(c)) if err != nil { return response.InternalServerError(c, "Failed to list feedback") } @@ -333,13 +239,13 @@ func (h *Handler) PublicListFeedback(c echo.Context) error { // @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error" // @Security BearerAuth // @Router /api/v1/feedback/{id} [get] -func (h *Handler) PublicGetFeedback(c echo.Context) error { +func (h *Handler) PublicGet(c echo.Context) error { feedbackID := c.Param("id") if feedbackID == "" { return response.BadRequest(c, "Feedback ID is required", "Feedback ID parameter is missing") } - feedback, err := h.service.GetFeedback(c.Request().Context(), feedbackID) + feedback, err := h.service.Get(c.Request().Context(), feedbackID) if err != nil { return response.BadRequest(c, "Failed to get feedback", "") } @@ -360,7 +266,7 @@ func (h *Handler) PublicGetFeedback(c echo.Context) error { // @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error" // @Security BearerAuth // @Router /api/v1/feedback/tender/{tender_id} [get] -func (h *Handler) PublicGetFeedbackByTenderID(c echo.Context) error { +func (h *Handler) PublicGetByTenderID(c echo.Context) error { tenderID := c.Param("tender_id") if tenderID == "" { return response.BadRequest(c, "Tender ID is required", "Tender ID parameter is missing") @@ -371,7 +277,7 @@ func (h *Handler) PublicGetFeedbackByTenderID(c echo.Context) error { return response.BadRequest(c, "Company ID required", "User must be associated with a company") } - feedback, err := h.service.GetFeedbackByTenderID(c.Request().Context(), tenderID, companyID) + feedback, err := h.service.GetByTenderID(c.Request().Context(), tenderID, companyID) if err != nil { return response.BadRequest(c, "Failed to get feedback", "") } @@ -385,22 +291,22 @@ func (h *Handler) PublicGetFeedbackByTenderID(c echo.Context) error { // @Tags Feedback // @Accept json // @Produce json -// @Success 200 {object} response.APIResponse{data=CompanyFeedbackStatsResponse} "Company feedback statistics retrieved successfully" +// @Success 200 {object} response.APIResponse{data=StatsResponse} "Company feedback statistics retrieved successfully" // @Failure 400 {object} response.APIResponse{error=string,message=string} "Bad request - Missing company association" // @Failure 401 {object} response.APIResponse{error=string,message=string} "Unauthorized - User not authenticated" // @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error" // @Security BearerAuth // @Router /api/v1/feedback/stats/company [get] -func (h *Handler) GetCompanyFeedbackStats(c echo.Context) error { +func (h *Handler) GetCompanyStats(c echo.Context) error { companyID, err := user.GetCompanyIDFromContext(c) if err != nil { return response.BadRequest(c, "Company ID required", "User must be associated with a company") } - stats, err := h.service.CompanyFeedbackStats(c.Request().Context(), companyID) + stats, err := h.service.CompanyStats(c.Request().Context(), companyID) if err != nil { - return response.InternalServerError(c, "Failed to retrieve company feedback statistics") + return response.InternalServerError(c, "Failed to retrieve company stats") } - return response.Success(c, stats, "Company feedback statistics retrieved successfully") + return response.Success(c, stats, "Company stats retrieved successfully") } diff --git a/internal/feedback/repository.go b/internal/feedback/repository.go index 000781b..ef0388b 100644 --- a/internal/feedback/repository.go +++ b/internal/feedback/repository.go @@ -4,10 +4,11 @@ import ( "context" "time" "tm/pkg/logger" - "tm/pkg/mongo" + orm "tm/pkg/mongo" + "tm/pkg/response" "go.mongodb.org/mongo-driver/v2/bson" - driver "go.mongodb.org/mongo-driver/v2/mongo" + "go.mongodb.org/mongo-driver/v2/mongo" ) // FeedbackRepository interface defines feedback data access methods @@ -15,37 +16,33 @@ type Repository interface { // Basic CRUD operations Create(ctx context.Context, feedback *Feedback) error Update(ctx context.Context, feedback *Feedback) error + Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) ([]Feedback, int64, error) GetByID(ctx context.Context, id string) (*Feedback, error) GetByCompanyID(ctx context.Context, companyID string, tender string) (*Feedback, error) - Delete(ctx context.Context, id string) error - List(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) ([]*Feedback, int64, error) - Search(ctx context.Context, criteria FeedbackSearchCriteria) ([]*Feedback, error) - GetFeedbackSummary(ctx context.Context, tenderID string) (*FeedbackSummary, error) - GetFeedbackCountByType(ctx context.Context) (map[FeedbackType]int64, error) GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error) - GetCompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) + Delete(ctx context.Context, id string) error + CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error) } // feedbackRepository implements FeedbackRepository interface using MongoDB ORM type feedbackRepository struct { - repo mongo.Repository[Feedback] + repo orm.Repository[Feedback] logger logger.Logger } // NewFeedbackRepository creates a new feedback repository -func NewFeedbackRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger) Repository { +func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { // Create indexes for feedback collection - feedbackIndexes := []mongo.Index{ - *mongo.NewIndex("tender_id_idx", bson.D{{Key: "tender_id", Value: 1}}), - *mongo.NewIndex("customer_id_idx", bson.D{{Key: "customer_id", Value: 1}}), - *mongo.NewIndex("company_id_idx", bson.D{{Key: "company_id", Value: 1}}), - *mongo.NewIndex("feedback_type_idx", bson.D{{Key: "feedback_type", Value: 1}}), + feedbackIndexes := []orm.Index{ + *orm.NewIndex("tender_id_idx", bson.D{{Key: "tender_id", Value: 1}}), + *orm.NewIndex("company_id_idx", bson.D{{Key: "company_id", Value: 1}}), + *orm.NewIndex("feedback_type_idx", bson.D{{Key: "feedback_type", Value: 1}}), // Compound indexes for common queries - *mongo.NewIndex("tender_customer_idx", bson.D{ + *orm.NewIndex("tender_company_idx", bson.D{ {Key: "tender_id", Value: 1}, - {Key: "customer_id", Value: 1}, + {Key: "company_id", Value: 1}, }), - *mongo.NewIndex("tender_type_idx", bson.D{ + *orm.NewIndex("tender_type_idx", bson.D{ {Key: "tender_id", Value: 1}, {Key: "feedback_type", Value: 1}, }), @@ -59,7 +56,7 @@ func NewFeedbackRepository(mongoManager *mongo.ConnectionManager, logger logger. } // Create ORM repository - repo := mongo.NewRepository[Feedback](mongoManager.GetCollection("feedback"), logger) + repo := orm.NewRepository[Feedback](mongoManager.GetCollection("feedback"), logger) return &feedbackRepository{ repo: repo, @@ -72,7 +69,6 @@ func (r *feedbackRepository) Create(ctx context.Context, feedback *Feedback) err // Set timestamps now := time.Now().Unix() feedback.CreatedAt = now - feedback.UpdatedAt = now err := r.repo.Create(ctx, feedback) if err != nil { @@ -144,7 +140,24 @@ func (r *feedbackRepository) GetByCompanyID(ctx context.Context, companyID strin return result, nil } -// Delete permanently deletes feedback by ID +// GetByTenderID retrieves feedback by tender ID and company ID +func (r *feedbackRepository) GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error) { + filter := bson.M{"tender_id": tenderID, "company_id": companyID} + + result, err := r.repo.FindOne(ctx, filter) + if err != nil { + r.logger.Error("Failed to get feedback by tender ID and company ID", map[string]interface{}{ + "tender_id": tenderID, + "company_id": companyID, + "error": err.Error(), + }) + return nil, err + } + + return result, nil +} + +// Delete deletes a feedback by ID func (r *feedbackRepository) Delete(ctx context.Context, id string) error { err := r.repo.Delete(ctx, id) if err != nil { @@ -155,267 +168,46 @@ func (r *feedbackRepository) Delete(ctx context.Context, id string) error { return err } - r.logger.Info("Feedback deleted successfully", map[string]interface{}{ - "feedback_id": id, - }) - return nil } -// List retrieves feedback with pagination and filtering -func (r *feedbackRepository) List(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) ([]*Feedback, int64, error) { - filter := r.buildSearchFilter(criteria) - - pagination := mongo.Pagination{ - Limit: limit, - Skip: offset, - SortField: "created_at", - SortOrder: -1, - } - - result, err := r.repo.FindAll(ctx, filter, pagination) - if err != nil { - r.logger.Error("Failed to list feedback", map[string]interface{}{ - "error": err.Error(), - }) - return nil, 0, err - } - - // Convert []TenderFeedback to []*TenderFeedback - feedback := make([]*Feedback, len(result.Items)) - for i := range result.Items { - feedback[i] = &result.Items[i] - } - - return feedback, result.TotalCount, nil -} - -// GetByFeedbackType retrieves feedback by type -func (r *feedbackRepository) GetByFeedbackType(ctx context.Context, feedbackType FeedbackType, limit, offset int) ([]*Feedback, int64, error) { - filter := bson.M{"feedback_type": feedbackType} - - pagination := mongo.Pagination{ - Limit: limit, - Skip: offset, - SortField: "created_at", - SortOrder: -1, - } - - result, err := r.repo.FindAll(ctx, filter, pagination) - if err != nil { - r.logger.Error("Failed to get feedback by type", map[string]interface{}{ - "feedback_type": feedbackType, - "error": err.Error(), - }) - return nil, 0, err - } - - // Convert []TenderFeedback to []*TenderFeedback - feedback := make([]*Feedback, len(result.Items)) - for i := range result.Items { - feedback[i] = &result.Items[i] - } - - return feedback, result.TotalCount, nil -} - // Search searches feedback based on criteria -func (r *feedbackRepository) Search(ctx context.Context, criteria FeedbackSearchCriteria) ([]*Feedback, error) { +func (r *feedbackRepository) Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) ([]Feedback, int64, error) { filter := r.buildSearchFilter(criteria) - pagination := mongo.Pagination{ - Limit: 100, - SortField: "created_at", - SortOrder: -1, + // Build sort + sort := "created_at" + if pagination.SortBy != "" { + sort = pagination.SortBy } - result, err := r.repo.FindAll(ctx, filter, pagination) + sortOrder := "desc" + if pagination.SortOrder != "" { + sortOrder = pagination.SortOrder + } + + // Build pagination + paginationBuilder := orm.NewPaginationBuilder(). + Limit(pagination.Limit). + Skip(pagination.Offset). + SortBy(sort, sortOrder). + Build() + + // Use ORM to find users + result, err := r.repo.FindAll(ctx, filter, paginationBuilder) if err != nil { - r.logger.Error("Failed to search feedback", map[string]interface{}{ + r.logger.Error("Failed to list users", map[string]interface{}{ "error": err.Error(), }) - return nil, err - } - - // Convert []TenderFeedback to []*TenderFeedback - feedback := make([]*Feedback, len(result.Items)) - for i := range result.Items { - feedback[i] = &result.Items[i] - } - - return feedback, nil -} - -// GetFeedbackSummary calculates feedback summary for a tender -func (r *feedbackRepository) GetFeedbackSummary(ctx context.Context, tenderID string) (*FeedbackSummary, error) { - pipeline := driver.Pipeline{ - { - {Key: "$match", Value: bson.M{ - "tender_id": tenderID, - }}, - }, - { - {Key: "$group", Value: bson.M{ - "_id": nil, - "total_likes": bson.M{ - "$sum": bson.M{ - "$cond": []interface{}{ - bson.M{"$eq": []string{"$feedback_type", string(FeedbackTypeLike)}}, - 1, - 0, - }, - }, - }, - "total_dislikes": bson.M{ - "$sum": bson.M{ - "$cond": []interface{}{ - bson.M{"$eq": []string{"$feedback_type", string(FeedbackTypeDislike)}}, - 1, - 0, - }, - }, - }, - "total_comments": bson.M{ - "$sum": bson.M{ - "$cond": []interface{}{ - bson.M{"$ne": []interface{}{"$comment", nil}}, - 1, - 0, - }, - }, - }, - "total_ratings": bson.M{ - "$sum": bson.M{ - "$cond": []interface{}{ - bson.M{"$gt": []interface{}{"$rating", 0}}, - 1, - 0, - }, - }, - }, - "rating_sum": bson.M{ - "$sum": bson.M{ - "$cond": []interface{}{ - bson.M{"$gt": []interface{}{"$rating", 0}}, - "$rating", - 0, - }, - }, - }, - "last_updated": bson.M{"$max": "$updated_at"}, - }}, - }, - } - - results, err := r.repo.Aggregate(ctx, pipeline) - if err != nil { - r.logger.Error("Failed to get feedback summary", map[string]interface{}{ - "tender_id": tenderID, - "error": err.Error(), - }) - return nil, err - } - - summary := &FeedbackSummary{ - TenderID: tenderID, - TotalLikes: 0, - TotalDislikes: 0, - LastUpdated: time.Now().Unix(), - } - - if len(results) > 0 { - result := results[0] - if totalLikes, ok := result["total_likes"].(int32); ok { - summary.TotalLikes = int64(totalLikes) - } - if totalDislikes, ok := result["total_dislikes"].(int32); ok { - summary.TotalDislikes = int64(totalDislikes) - } - if lastUpdated, ok := result["last_updated"].(int64); ok { - summary.LastUpdated = lastUpdated - } - } - - return summary, nil -} - -// GetFeedbackCountByType gets feedback count grouped by type -func (r *feedbackRepository) GetFeedbackCountByType(ctx context.Context) (map[FeedbackType]int64, error) { - pipeline := driver.Pipeline{ - { - {Key: "$group", Value: bson.M{ - "_id": "$feedback_type", - "count": bson.M{"$sum": 1}, - }}, - }, - } - - results, err := r.repo.Aggregate(ctx, pipeline) - if err != nil { - return nil, err - } - - counts := make(map[FeedbackType]int64) - for _, result := range results { - if feedbackType, ok := result["_id"].(string); ok { - if count, ok := result["count"].(int32); ok { - counts[FeedbackType(feedbackType)] = int64(count) - } - } - } - - return counts, nil -} - -// GetCustomerFeedbackHistory gets the feedback history for a specific customer -func (r *feedbackRepository) GetCustomerFeedbackHistory(ctx context.Context, customerID string, limit, offset int) ([]*Feedback, int64, error) { - filter := bson.M{ - "customer_id": customerID, - } - - pagination := mongo.Pagination{ - Limit: limit, - Skip: offset, - SortField: "created_at", - SortOrder: -1, - } - - result, err := r.repo.FindAll(ctx, filter, pagination) - if err != nil { - r.logger.Error("Failed to get customer feedback history", map[string]interface{}{ - "customer_id": customerID, - "error": err.Error(), - }) return nil, 0, err } - // Convert []TenderFeedback to []*TenderFeedback - feedback := make([]*Feedback, len(result.Items)) - for i := range result.Items { - feedback[i] = &result.Items[i] - } - - return feedback, result.TotalCount, nil + return result.Items, result.TotalCount, nil } -func (r *feedbackRepository) GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error) { - filter := bson.M{"tender_id": tenderID, "company_id": companyID} - - result, err := r.repo.FindOne(ctx, filter) - if err != nil { - r.logger.Error("Failed to get feedback by tender ID", map[string]interface{}{ - "tender_id": tenderID, - "error": err.Error(), - }) - return nil, err - } - - return result, nil -} - -// GetCompanyFeedbackStats calculates feedback statistics for a company -func (r *feedbackRepository) GetCompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) { - pipeline := driver.Pipeline{ +// Stats calculates feedback statistics for a company +func (r *feedbackRepository) CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error) { + pipeline := mongo.Pipeline{ { {Key: "$match", Value: bson.M{ "company_id": companyID, @@ -452,14 +244,14 @@ func (r *feedbackRepository) GetCompanyFeedbackStats(ctx context.Context, compan results, err := r.repo.Aggregate(ctx, pipeline) if err != nil { - r.logger.Error("Failed to get company feedback stats", map[string]interface{}{ + r.logger.Error("Failed to get feedback stats", map[string]interface{}{ "company_id": companyID, "error": err.Error(), }) return nil, err } - stats := &CompanyFeedbackStatsResponse{ + stats := &StatsResponse{ CompanyID: companyID, TotalFeedback: 0, TotalLikes: 0, @@ -487,24 +279,24 @@ func (r *feedbackRepository) GetCompanyFeedbackStats(ctx context.Context, compan } // buildSearchFilter builds MongoDB filter from search criteria -func (r *feedbackRepository) buildSearchFilter(criteria FeedbackSearchCriteria) bson.M { +func (r *feedbackRepository) buildSearchFilter(criteria SearchForm) bson.M { filter := bson.M{} // Always exclude deleted feedback unless specifically requested - if criteria.TenderID != nil { - filter["tender_id"] = *criteria.TenderID + if criteria.Tender != nil { + filter["tender_id"] = *criteria.Tender } - if criteria.CustomerID != nil { - filter["customer_id"] = *criteria.CustomerID + if criteria.Customer != nil { + filter["customer_id"] = *criteria.Customer } - if criteria.CompanyID != nil { - filter["company_id"] = *criteria.CompanyID + if criteria.Company != nil { + filter["company_id"] = *criteria.Company } - if criteria.FeedbackType != nil { - filter["feedback_type"] = *criteria.FeedbackType + if criteria.Type != nil { + filter["feedback_type"] = *criteria.Type } if criteria.DateFrom != nil || criteria.DateTo != nil { diff --git a/internal/feedback/service.go b/internal/feedback/service.go index 8a3bb84..00f1cf3 100644 --- a/internal/feedback/service.go +++ b/internal/feedback/service.go @@ -2,44 +2,48 @@ package feedback import ( "context" + "tm/internal/company" "tm/internal/tender" "tm/pkg/logger" "tm/pkg/response" ) // FeedbackService interface defines feedback business logic methods -type FeedbackService interface { - ToggleFeedback(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) - GetFeedback(ctx context.Context, id string) (*FeedbackResponse, error) - ListFeedback(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) (*FeedbackListWithTenderResponse, error) - DeleteFeedback(ctx context.Context, id string) error - GetFeedbackByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error) - CompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) +type Service interface { + Toggle(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) + Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*FeedbackListResponse, error) + Get(ctx context.Context, id string) (*FeedbackResponse, error) + GetByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error) + Delete(ctx context.Context, id string) error + CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error) + getDetails(ctx context.Context, tenderID, companyID string) (*tenderResponse, *companyResponse, error) } // feedbackService implements FeedbackService interface type feedbackService struct { - feedbackRepo Repository - tenderService tender.Service - logger logger.Logger + feedbackRepo Repository + tenderService tender.Service + companyService company.Service + logger logger.Logger } -// NewFeedbackService creates a new feedback service -func NewFeedbackService(feedbackRepo Repository, tenderService tender.Service, logger logger.Logger) FeedbackService { +// NewService creates a new feedback service +func NewService(feedbackRepo Repository, tenderService tender.Service, companyService company.Service, logger logger.Logger) Service { return &feedbackService{ - feedbackRepo: feedbackRepo, - tenderService: tenderService, - logger: logger, + feedbackRepo: feedbackRepo, + tenderService: tenderService, + companyService: companyService, + logger: logger, } } -func (s *feedbackService) ToggleFeedback(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) { - existing, err := s.feedbackRepo.GetByCompanyID(ctx, *companyID, req.TenderID) +func (s *feedbackService) Toggle(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) { + existing, err := s.feedbackRepo.GetByCompanyID(ctx, *companyID, req.Tender) if err != nil { feedback := &Feedback{ - TenderID: req.TenderID, - FeedbackType: req.FeedbackType, + TenderID: req.Tender, + FeedbackType: req.Type, CompanyID: companyID, CustomerID: customerID, } @@ -47,35 +51,53 @@ func (s *feedbackService) ToggleFeedback(ctx context.Context, req ToggleFeedback err = s.feedbackRepo.Create(ctx, feedback) if err != nil { s.logger.Error("Failed to create new feedback", map[string]interface{}{ - "tender_id": req.TenderID, + "tender_id": req.Tender, "company_id": *companyID, "error": err.Error(), }) return nil, err } - return feedback.ToResponse(), nil + tender, company, err := s.getDetails(ctx, req.Tender, *companyID) + if err != nil { + s.logger.Error("Failed to get details", map[string]interface{}{ + "tender_id": req.Tender, + "company_id": *companyID, + "error": err.Error(), + }) + } + + return feedback.ToResponse(tender, company), nil } - if existing.FeedbackType == req.FeedbackType { + if existing.FeedbackType == req.Type { err = s.feedbackRepo.Delete(ctx, existing.GetID()) if err != nil { s.logger.Error("Failed to update existing feedback", map[string]interface{}{ "feedback_id": existing.GetID(), - "tender_id": req.TenderID, + "tender_id": req.Tender, "company_id": *companyID, "error": err.Error(), }) return nil, err } - if req.FeedbackType == FeedbackTypeLike { + if req.Type == FeedbackTypeLike { existing.FeedbackType = "unlike" } else { existing.FeedbackType = "undislike" } - return existing.ToResponse(), nil + tender, company, err := s.getDetails(ctx, req.Tender, *companyID) + if err != nil { + s.logger.Error("Failed to get details", map[string]interface{}{ + "tender_id": req.Tender, + "company_id": *companyID, + "error": err.Error(), + }) + } + + return existing.ToResponse(tender, company), nil } if existing.FeedbackType == FeedbackTypeLike { @@ -88,17 +110,26 @@ func (s *feedbackService) ToggleFeedback(ctx context.Context, req ToggleFeedback if err != nil { s.logger.Error("Failed to update existing feedback", map[string]interface{}{ "feedback_id": existing.GetID(), - "tender_id": req.TenderID, + "tender_id": req.Tender, "company_id": *companyID, "error": err.Error(), }) return nil, err } - return existing.ToResponse(), nil + tender, company, err := s.getDetails(ctx, req.Tender, *companyID) + if err != nil { + s.logger.Error("Failed to get details", map[string]interface{}{ + "tender_id": req.Tender, + "company_id": *companyID, + "error": err.Error(), + }) + } + + return existing.ToResponse(tender, company), nil } -func (s *feedbackService) GetFeedback(ctx context.Context, id string) (*FeedbackResponse, error) { +func (s *feedbackService) Get(ctx context.Context, id string) (*FeedbackResponse, error) { feedback, err := s.feedbackRepo.GetByID(ctx, id) if err != nil { s.logger.Error("Failed to get feedback", map[string]interface{}{ @@ -108,16 +139,25 @@ func (s *feedbackService) GetFeedback(ctx context.Context, id string) (*Feedback return nil, err } - return feedback.ToResponse(), nil + tender, company, err := s.getDetails(ctx, feedback.TenderID, *feedback.CompanyID) + if err != nil { + s.logger.Error("Failed to get details", map[string]interface{}{ + "tender_id": feedback.TenderID, + "company_id": *feedback.CompanyID, + "error": err.Error(), + }) + } + + return feedback.ToResponse(tender, company), nil } -func (s *feedbackService) ListFeedback(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) (*FeedbackListWithTenderResponse, error) { - feedbacks, total, err := s.feedbackRepo.List(ctx, criteria, limit, offset) +func (s *feedbackService) Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*FeedbackListResponse, error) { + feedbacks, total, err := s.feedbackRepo.Search(ctx, criteria, pagination) if err != nil { s.logger.Error("Failed to list feedback", map[string]interface{}{ "criteria": criteria, - "limit": limit, - "offset": offset, + "limit": pagination.Limit, + "offset": pagination.Offset, "error": err.Error(), }) return nil, err @@ -125,23 +165,56 @@ func (s *feedbackService) ListFeedback(ctx context.Context, criteria FeedbackSea meta := &response.Meta{ Total: int(total), - Limit: limit, - Offset: offset, - Page: (offset / limit) + 1, - Pages: int((total + int64(limit) - 1) / int64(limit)), + Limit: pagination.Limit, + Offset: pagination.Offset, + Page: (pagination.Offset / pagination.Limit) + 1, + Pages: int((total + int64(pagination.Limit) - 1) / int64(pagination.Limit)), } - result := make([]*FeedbackWithTenderResponse, len(feedbacks)) + result := make([]*FeedbackResponse, len(feedbacks)) for i, feedback := range feedbacks { - tender, err := s.tenderService.GetByID(ctx, feedback.TenderID) - if err != nil { - s.logger.Error("Failed to get tender", map[string]interface{}{ - "tender_id": feedback.TenderID, - "error": err.Error(), - }) - return nil, err - } - resp := feedback.ToResponseWithTender(&tenderDetails{ + tender, company, _ := s.getDetails(ctx, feedback.TenderID, *feedback.CompanyID) + result[i] = feedback.ToResponse(tender, company) + } + + return &FeedbackListResponse{ + Feedback: result, + Meta: meta, + }, nil +} + +func (s *feedbackService) Delete(ctx context.Context, id string) error { + return s.feedbackRepo.Delete(ctx, id) +} + +func (s *feedbackService) GetByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error) { + feedback, err := s.feedbackRepo.GetByTenderID(ctx, tenderID, companyID) + if err != nil { + s.logger.Error("Failed to get feedback by tender ID", map[string]interface{}{ + "tender_id": tenderID, + "error": err.Error(), + }) + return nil, err + } + + tender, err := s.tenderService.GetByID(ctx, feedback.TenderID) + if err != nil { + s.logger.Error("Failed to get tender", map[string]interface{}{ + "tender_id": feedback.TenderID, + "error": err.Error(), + }) + } + + company, err := s.companyService.GetByID(ctx, *feedback.CompanyID) + if err != nil { + s.logger.Error("Failed to get company", map[string]interface{}{ + "company_id": feedback.CompanyID, + "error": err.Error(), + }) + } + + return feedback.ToResponse( + &tenderResponse{ ID: tender.ID, NoticePublicationID: tender.NoticePublicationID, PublicationDate: tender.PublicationDate, @@ -158,46 +231,69 @@ func (s *feedbackService) ListFeedback(ctx context.Context, criteria FeedbackSea CountryCode: tender.CountryCode, BuyerOrganization: &organizationResponse{Name: tender.BuyerOrganization.Name}, Status: string(tender.Status), - }) - result[i] = resp - } - return &FeedbackListWithTenderResponse{ - Feedback: result, - Meta: meta, - }, nil + }, + &companyResponse{ + ID: company.ID, + Name: company.Name, + }, + ), nil } -func (s *feedbackService) DeleteFeedback(ctx context.Context, id string) error { - return s.feedbackRepo.Delete(ctx, id) -} - -func (s *feedbackService) GetFeedbackByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error) { - feedback, err := s.feedbackRepo.GetByTenderID(ctx, tenderID, companyID) +func (s *feedbackService) getDetails(ctx context.Context, tenderID, companyID string) (*tenderResponse, *companyResponse, error) { + tender, err := s.tenderService.GetByID(ctx, tenderID) if err != nil { - s.logger.Error("Failed to get feedback by tender ID", map[string]interface{}{ + s.logger.Error("Failed to get tender", map[string]interface{}{ "tender_id": tenderID, "error": err.Error(), }) - return nil, err } - return feedback.ToResponse(), nil + + company, err := s.companyService.GetByID(ctx, companyID) + if err != nil { + s.logger.Error("Failed to get company", map[string]interface{}{ + "company_id": companyID, + "error": err.Error(), + }) + } + + return &tenderResponse{ + ID: tender.ID, + NoticePublicationID: tender.NoticePublicationID, + PublicationDate: tender.PublicationDate, + Title: tender.Title, + Description: tender.Description, + ProcurementTypeCode: tender.ProcurementTypeCode, + ProcedureCode: tender.ProcedureCode, + EstimatedValue: tender.EstimatedValue, + Currency: tender.Currency, + Duration: tender.Duration, + DurationUnit: tender.DurationUnit, + TenderDeadline: tender.TenderDeadline, + SubmissionDeadline: tender.SubmissionDeadline, + CountryCode: tender.CountryCode, + BuyerOrganization: &organizationResponse{Name: tender.BuyerOrganization.Name}, + Status: string(tender.Status), + }, &companyResponse{ + ID: company.ID, + Name: company.Name, + }, nil } -func (s *feedbackService) CompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) { +func (s *feedbackService) CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error) { s.logger.Info("Getting company feedback statistics", map[string]interface{}{ "company_id": companyID, }) - stats, err := s.feedbackRepo.GetCompanyFeedbackStats(ctx, companyID) + stats, err := s.feedbackRepo.CompanyStats(ctx, companyID) if err != nil { - s.logger.Error("Failed to get company feedback statistics", map[string]interface{}{ + s.logger.Error("Failed to get company stats", map[string]interface{}{ "company_id": companyID, "error": err.Error(), }) return nil, err } - s.logger.Info("Company feedback statistics retrieved successfully", map[string]interface{}{ + s.logger.Info("Company stats retrieved successfully", map[string]interface{}{ "company_id": companyID, "total_feedback": stats.TotalFeedback, "total_likes": stats.TotalLikes, diff --git a/internal/inquiry/repository.go b/internal/inquiry/repository.go index 629e9ab..b7b0194 100644 --- a/internal/inquiry/repository.go +++ b/internal/inquiry/repository.go @@ -29,7 +29,7 @@ type inquiryRepository struct { } // NewInquiryRepository creates a new inquiry repository -func NewInquiryRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { +func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { // Create indexes using the ORM's index management indexes := []orm.Index{ *orm.CreateTextIndex("search_idx", "full_name", "company_name", "work_email"), @@ -72,10 +72,10 @@ func (r *inquiryRepository) Create(ctx context.Context, inquiry *Inquiry) error now := time.Now().Unix() inquiry.SetCreatedAt(now) inquiry.SetUpdatedAt(now) - + // Set initial status to pending inquiry.Status = StatusPending - + // Add initial status change to history inquiry.AddStatusChange(StatusPending, "Inquiry submitted", "system", "Initial inquiry submission") @@ -140,10 +140,10 @@ func (r *inquiryRepository) UpdateStatus(ctx context.Context, id string, status, } r.logger.Info("Inquiry status updated successfully", map[string]interface{}{ - "inquiry_id": id, - "old_status": inquiry.StatusHistory[len(inquiry.StatusHistory)-2].Status, // Previous status - "new_status": status, - "changed_by": changedBy, + "inquiry_id": id, + "old_status": inquiry.StatusHistory[len(inquiry.StatusHistory)-2].Status, // Previous status + "new_status": status, + "changed_by": changedBy, }) return nil diff --git a/internal/inquiry/service.go b/internal/inquiry/service.go index 38806fd..dff3c72 100644 --- a/internal/inquiry/service.go +++ b/internal/inquiry/service.go @@ -25,8 +25,8 @@ type inquiryService struct { logger logger.Logger } -// NewInquiryService creates a new inquiry service -func NewInquiryService(repository Repository, sdk notification.SDK, logger logger.Logger) Service { +// NewService creates a new inquiry service +func NewService(repository Repository, sdk notification.SDK, logger logger.Logger) Service { return &inquiryService{ repository: repository, sdk: sdk, diff --git a/internal/notification/service.go b/internal/notification/service.go index fead16d..2f42504 100644 --- a/internal/notification/service.go +++ b/internal/notification/service.go @@ -33,8 +33,8 @@ type notificationService struct { logger logger.Logger } -// NewNotificationService creates a new notification service -func NewNotificationService( +// NewService creates a new notification service +func NewService( sdk notification.SDK, userService user.Service, customerService customer.Service, diff --git a/internal/tender/handler.go b/internal/tender/handler.go index ab25c5a..11b56a1 100644 --- a/internal/tender/handler.go +++ b/internal/tender/handler.go @@ -14,8 +14,8 @@ type TenderHandler struct { logger logger.Logger } -// NewTenderHandler creates a new tender handler -func NewTenderHandler(service Service, logger logger.Logger) *TenderHandler { +// NewHandler creates a new tender handler +func NewHandler(service Service, logger logger.Logger) *TenderHandler { return &TenderHandler{ service: service, logger: logger, diff --git a/internal/tender/repository.go b/internal/tender/repository.go index 5346207..e8a238d 100644 --- a/internal/tender/repository.go +++ b/internal/tender/repository.go @@ -40,7 +40,7 @@ type tenderRepository struct { } // NewTenderRepository creates a new tender repository -func NewTenderRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) TenderRepository { +func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) TenderRepository { // Create indexes for tenders collection tenderIndexes := []orm.Index{ *orm.CreateUniqueIndex("notice_publication_id_idx", bson.D{{Key: "notice_publication_id", Value: 1}}), diff --git a/internal/tender/service.go b/internal/tender/service.go index 0eed84e..e21f4ea 100644 --- a/internal/tender/service.go +++ b/internal/tender/service.go @@ -30,8 +30,8 @@ type tenderService struct { logger logger.Logger } -// NewTenderService creates a new tender service -func NewTenderService(repository TenderRepository, companyService company.Service, logger logger.Logger) Service { +// NewService creates a new tender service +func NewService(repository TenderRepository, companyService company.Service, logger logger.Logger) Service { return &tenderService{ repository: repository, companyService: companyService, diff --git a/internal/tender_approval/repository.go b/internal/tender_approval/repository.go index 3576f2a..f3aa24b 100644 --- a/internal/tender_approval/repository.go +++ b/internal/tender_approval/repository.go @@ -39,7 +39,7 @@ type tenderApprovalRepository struct { } // NewTenderApprovalRepository creates a new tender approval repository -func NewTenderApprovalRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger) Repository { +func NewRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger) Repository { // Create indexes using the ORM's index management indexes := []mongo.Index{ *mongo.CreateUniqueIndex("tender_company_idx", bson.D{{Key: "tender_id", Value: 1}, {Key: "company_id", Value: 1}}), diff --git a/internal/tender_approval/service.go b/internal/tender_approval/service.go index a09783e..072fcd3 100644 --- a/internal/tender_approval/service.go +++ b/internal/tender_approval/service.go @@ -55,8 +55,8 @@ type tenderApprovalService struct { logger logger.Logger } -// NewTenderApprovalService creates a new tender approval service -func NewTenderApprovalService(repository Repository, tenderService tender.Service, logger logger.Logger) Service { +// NewService creates a new tender approval service +func NewService(repository Repository, tenderService tender.Service, logger logger.Logger) Service { return &tenderApprovalService{ repository: repository, tenderService: tenderService, diff --git a/internal/user/handler.go b/internal/user/handler.go index 8b35258..1053858 100644 --- a/internal/user/handler.go +++ b/internal/user/handler.go @@ -17,8 +17,8 @@ type Handler struct { authService authorization.AuthorizationService } -// NewUserHandler creates a new user handler -func NewUserHandler(service Service, logger logger.Logger, validator ValidationService, authService authorization.AuthorizationService) *Handler { +// NewHandler creates a new user handler +func NewHandler(service Service, logger logger.Logger, validator ValidationService, authService authorization.AuthorizationService) *Handler { return &Handler{ service: service, logger: logger, diff --git a/internal/user/repository.go b/internal/user/repository.go index bd50b12..0aa960e 100644 --- a/internal/user/repository.go +++ b/internal/user/repository.go @@ -31,7 +31,7 @@ type userRepository struct { } // NewUserRepository creates a new user repository -func NewUserRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { +func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository { // Create indexes using the ORM's index management indexes := []orm.Index{ *orm.CreateTextIndex("search_idx", "full_name", "email", "username"), diff --git a/internal/user/service.go b/internal/user/service.go index bfb9839..ecbff30 100644 --- a/internal/user/service.go +++ b/internal/user/service.go @@ -45,8 +45,8 @@ type userService struct { notification notification.SDK } -// NewUserService creates a new user service -func NewUserService(repository Repository, logger logger.Logger, authService authorization.AuthorizationService, validator ValidationService, notificationSDK notification.SDK) Service { +// NewService creates a new user service +func NewService(repository Repository, logger logger.Logger, authService authorization.AuthorizationService, validator ValidationService, notificationSDK notification.SDK) Service { return &userService{ repository: repository, logger: logger,