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.
This commit is contained in:
+69
-277
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user