Implement Feedback Management System with CRUD Operations and API Enhancements

- Introduced a new feedback management system, including the creation of feedback entities, services, and repositories.
- Implemented CRUD operations for feedback, allowing users to submit likes and dislikes on tenders.
- Developed administrative endpoints for listing and retrieving feedback with comprehensive filtering options.
- Enhanced public API to allow users to view feedback related to tenders.
- Updated Swagger documentation to reflect new feedback endpoints and their functionalities.
- Removed obsolete configuration file `config.yaml` as part of the transition to a modular configuration system.
- Ensured adherence to Clean Architecture principles throughout the implementation.
This commit is contained in:
n.nakhostin
2025-08-16 16:00:03 +03:30
parent 126913365f
commit 21b736b55b
15 changed files with 3548 additions and 108 deletions
+32 -32
View File
@@ -5,7 +5,7 @@ import (
"errors"
"time"
"tm/pkg/logger"
mongopkg "tm/pkg/mongo"
"tm/pkg/mongo"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
@@ -39,36 +39,36 @@ type Repository interface {
// companyRepository implements the Repository interface using the MongoDB ORM
type companyRepository struct {
ormRepo mongopkg.Repository[Company]
ormRepo mongo.Repository[Company]
logger logger.Logger
}
// NewCompanyRepository creates a new company repository
func NewCompanyRepository(mongoManager *mongopkg.ConnectionManager, logger logger.Logger) Repository {
func NewCompanyRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger) Repository {
// Create indexes using the ORM's index management
indexes := []mongopkg.Index{
*mongopkg.CreateUniqueIndex("name_idx", bson.D{{Key: "name", Value: 1}}),
*mongopkg.CreateUniqueIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}),
*mongopkg.CreateUniqueIndex("tax_id_idx", bson.D{{Key: "tax_id", Value: 1}}),
*mongopkg.NewIndex("type_idx", bson.D{{Key: "type", Value: 1}}),
*mongopkg.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
*mongopkg.NewIndex("industry_idx", bson.D{{Key: "industry", Value: 1}}),
*mongopkg.NewIndex("is_verified_idx", bson.D{{Key: "is_verified", Value: 1}}),
*mongopkg.NewIndex("is_compliant_idx", bson.D{{Key: "is_compliant", Value: 1}}),
*mongopkg.NewIndex("language_idx", bson.D{{Key: "language", Value: 1}}),
*mongopkg.NewIndex("currency_idx", bson.D{{Key: "currency", Value: 1}}),
*mongopkg.NewIndex("employee_count_idx", bson.D{{Key: "employee_count", Value: 1}}),
*mongopkg.NewIndex("annual_revenue_idx", bson.D{{Key: "annual_revenue", Value: 1}}),
*mongopkg.NewIndex("founded_year_idx", bson.D{{Key: "founded_year", Value: 1}}),
*mongopkg.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
indexes := []mongo.Index{
*mongo.CreateUniqueIndex("name_idx", bson.D{{Key: "name", Value: 1}}),
*mongo.CreateUniqueIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}),
*mongo.CreateUniqueIndex("tax_id_idx", bson.D{{Key: "tax_id", Value: 1}}),
*mongo.NewIndex("type_idx", bson.D{{Key: "type", Value: 1}}),
*mongo.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
*mongo.NewIndex("industry_idx", bson.D{{Key: "industry", Value: 1}}),
*mongo.NewIndex("is_verified_idx", bson.D{{Key: "is_verified", Value: 1}}),
*mongo.NewIndex("is_compliant_idx", bson.D{{Key: "is_compliant", Value: 1}}),
*mongo.NewIndex("language_idx", bson.D{{Key: "language", Value: 1}}),
*mongo.NewIndex("currency_idx", bson.D{{Key: "currency", Value: 1}}),
*mongo.NewIndex("employee_count_idx", bson.D{{Key: "employee_count", Value: 1}}),
*mongo.NewIndex("annual_revenue_idx", bson.D{{Key: "annual_revenue", Value: 1}}),
*mongo.NewIndex("founded_year_idx", bson.D{{Key: "founded_year", Value: 1}}),
*mongo.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
// Tag indexes for efficient search
*mongopkg.NewIndex("cpv_codes_idx", bson.D{{Key: "tags.cpv_codes", Value: 1}}),
*mongopkg.NewIndex("categories_idx", bson.D{{Key: "tags.categories", Value: 1}}),
*mongopkg.NewIndex("keywords_idx", bson.D{{Key: "tags.keywords", Value: 1}}),
*mongopkg.NewIndex("specializations_idx", bson.D{{Key: "tags.specializations", Value: 1}}),
*mongopkg.NewIndex("certifications_idx", bson.D{{Key: "tags.certifications", Value: 1}}),
*mongo.NewIndex("cpv_codes_idx", bson.D{{Key: "tags.cpv_codes", Value: 1}}),
*mongo.NewIndex("categories_idx", bson.D{{Key: "tags.categories", Value: 1}}),
*mongo.NewIndex("keywords_idx", bson.D{{Key: "tags.keywords", Value: 1}}),
*mongo.NewIndex("specializations_idx", bson.D{{Key: "tags.specializations", Value: 1}}),
*mongo.NewIndex("certifications_idx", bson.D{{Key: "tags.certifications", Value: 1}}),
// Text index for search
*mongopkg.CreateTextIndex("search_idx", "name", "description", "industry"),
*mongo.CreateTextIndex("search_idx", "name", "description", "industry"),
}
// Create indexes
@@ -80,7 +80,7 @@ func NewCompanyRepository(mongoManager *mongopkg.ConnectionManager, logger logge
}
// Create ORM repository
ormRepo := mongopkg.NewRepository[Company](mongoManager.GetCollection("companies"), logger)
ormRepo := mongo.NewRepository[Company](mongoManager.GetCollection("companies"), logger)
return &companyRepository{
ormRepo: ormRepo,
@@ -119,7 +119,7 @@ func (r *companyRepository) Create(ctx context.Context, company *Company) error
func (r *companyRepository) GetByID(ctx context.Context, id string) (*Company, error) {
company, err := r.ormRepo.FindByID(ctx, id)
if err != nil {
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
if errors.Is(err, mongo.ErrDocumentNotFound) {
return nil, errors.New("company not found")
}
r.logger.Error("Failed to get company by ID", map[string]interface{}{
@@ -137,7 +137,7 @@ func (r *companyRepository) GetByName(ctx context.Context, name string) (*Compan
filter := bson.M{"name": name}
company, err := r.ormRepo.FindOne(ctx, filter)
if err != nil {
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
if errors.Is(err, mongo.ErrDocumentNotFound) {
return nil, errors.New("company not found")
}
r.logger.Error("Failed to get company by name", map[string]interface{}{
@@ -155,7 +155,7 @@ func (r *companyRepository) GetByRegistrationNumber(ctx context.Context, registr
filter := bson.M{"registration_number": registrationNumber}
company, err := r.ormRepo.FindOne(ctx, filter)
if err != nil {
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
if errors.Is(err, mongo.ErrDocumentNotFound) {
return nil, errors.New("company not found")
}
r.logger.Error("Failed to get company by registration number", map[string]interface{}{
@@ -173,7 +173,7 @@ func (r *companyRepository) GetByTaxID(ctx context.Context, taxID string) (*Comp
filter := bson.M{"tax_id": taxID}
company, err := r.ormRepo.FindOne(ctx, filter)
if err != nil {
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
if errors.Is(err, mongo.ErrDocumentNotFound) {
return nil, errors.New("company not found")
}
r.logger.Error("Failed to get company by tax ID", map[string]interface{}{
@@ -289,7 +289,7 @@ func (r *companyRepository) Delete(ctx context.Context, id string) error {
// List retrieves companies with pagination
func (r *companyRepository) List(ctx context.Context, limit, offset int) ([]*Company, error) {
// Build pagination
pagination := mongopkg.NewPaginationBuilder().
pagination := mongo.NewPaginationBuilder().
Limit(limit).
Skip(offset).
SortDesc("created_at").
@@ -407,7 +407,7 @@ func (r *companyRepository) Search(ctx context.Context, search string, companyTy
}
// Build pagination
pagination := mongopkg.NewPaginationBuilder().
pagination := mongo.NewPaginationBuilder().
Limit(limit).
Skip(offset)
@@ -755,7 +755,7 @@ func (r *companyRepository) SearchByTags(ctx context.Context, cpvCodes []string,
filter["status"] = bson.M{"$ne": CompanyStatusInactive}
// Build pagination
pagination := mongopkg.NewPaginationBuilder().
pagination := mongo.NewPaginationBuilder().
Limit(limit).
Skip(offset).
SortDesc("created_at").