Add customer feedback statistics endpoint and refactor feedback handling
This commit is contained in:
+115
-35
@@ -19,9 +19,12 @@ type Repository interface {
|
||||
Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*orm.PaginatedResult[Feedback], error)
|
||||
GetByID(ctx context.Context, id string) (*Feedback, error)
|
||||
GetByCompanyID(ctx context.Context, companyID string, tender string) (*Feedback, error)
|
||||
GetByCustomerID(ctx context.Context, customerID string, tenderID string) (*Feedback, error)
|
||||
GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error)
|
||||
GetByTenderIDForCustomer(ctx context.Context, tenderID, customerID string) (*Feedback, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error)
|
||||
CustomerStats(ctx context.Context, customerID string) (*CustomerStatsResponse, error)
|
||||
}
|
||||
|
||||
// feedbackRepository implements FeedbackRepository interface using MongoDB ORM
|
||||
@@ -42,6 +45,11 @@ func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Re
|
||||
{Key: "tender_id", Value: 1},
|
||||
{Key: "company_id", Value: 1},
|
||||
}),
|
||||
*orm.NewIndex("customer_id_idx", bson.D{{Key: "customer_id", Value: 1}}),
|
||||
*orm.NewIndex("tender_customer_idx", bson.D{
|
||||
{Key: "tender_id", Value: 1},
|
||||
{Key: "customer_id", Value: 1},
|
||||
}),
|
||||
*orm.NewIndex("tender_type_idx", bson.D{
|
||||
{Key: "tender_id", Value: 1},
|
||||
{Key: "feedback_type", Value: 1},
|
||||
@@ -140,6 +148,23 @@ func (r *feedbackRepository) GetByCompanyID(ctx context.Context, companyID strin
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetByCustomerID retrieves feedback by customer ID and tender ID.
|
||||
func (r *feedbackRepository) GetByCustomerID(ctx context.Context, customerID string, tenderID string) (*Feedback, error) {
|
||||
filter := bson.M{"customer_id": customerID, "tender_id": tenderID}
|
||||
|
||||
result, err := r.repo.FindOne(ctx, filter)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to get feedback by customer ID and tender ID", map[string]interface{}{
|
||||
"customer_id": customerID,
|
||||
"tender_id": tenderID,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 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}
|
||||
@@ -157,6 +182,23 @@ func (r *feedbackRepository) GetByTenderID(ctx context.Context, tenderID, compan
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetByTenderIDForCustomer retrieves feedback by tender ID and customer ID.
|
||||
func (r *feedbackRepository) GetByTenderIDForCustomer(ctx context.Context, tenderID, customerID string) (*Feedback, error) {
|
||||
filter := bson.M{"tender_id": tenderID, "customer_id": customerID}
|
||||
|
||||
result, err := r.repo.FindOne(ctx, filter)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to get feedback by tender ID and customer ID", map[string]interface{}{
|
||||
"tender_id": tenderID,
|
||||
"customer_id": customerID,
|
||||
"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)
|
||||
@@ -199,20 +241,59 @@ func (r *feedbackRepository) Search(ctx context.Context, criteria SearchForm, pa
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Stats calculates feedback statistics for a company
|
||||
// CompanyStats calculates feedback statistics for a company.
|
||||
func (r *feedbackRepository) CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error) {
|
||||
counts, lastUpdated, err := r.aggregateFeedbackCounts(ctx, bson.M{"company_id": companyID})
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to get feedback stats", map[string]interface{}{
|
||||
"company_id": companyID,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StatsResponse{
|
||||
CompanyID: companyID,
|
||||
TotalFeedback: counts.totalFeedback,
|
||||
TotalLikes: counts.totalLikes,
|
||||
TotalDislikes: counts.totalDislikes,
|
||||
LastUpdated: lastUpdated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CustomerStats calculates feedback statistics for a customer.
|
||||
func (r *feedbackRepository) CustomerStats(ctx context.Context, customerID string) (*CustomerStatsResponse, error) {
|
||||
counts, lastUpdated, err := r.aggregateFeedbackCounts(ctx, bson.M{"customer_id": customerID})
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to get customer feedback stats", map[string]interface{}{
|
||||
"customer_id": customerID,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CustomerStatsResponse{
|
||||
CustomerID: customerID,
|
||||
TotalFeedback: counts.totalFeedback,
|
||||
TotalLikes: counts.totalLikes,
|
||||
TotalDislikes: counts.totalDislikes,
|
||||
LastUpdated: lastUpdated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type feedbackCountAggregate struct {
|
||||
totalFeedback int64
|
||||
totalLikes int64
|
||||
totalDislikes int64
|
||||
}
|
||||
|
||||
func (r *feedbackRepository) aggregateFeedbackCounts(ctx context.Context, match bson.M) (feedbackCountAggregate, int64, error) {
|
||||
pipeline := mongo.Pipeline{
|
||||
{
|
||||
{Key: "$match", Value: bson.M{
|
||||
"company_id": companyID,
|
||||
}},
|
||||
},
|
||||
{{Key: "$match", Value: match}},
|
||||
{
|
||||
{Key: "$group", Value: bson.M{
|
||||
"_id": nil,
|
||||
"total_feedback": bson.M{
|
||||
"$sum": 1,
|
||||
},
|
||||
"total_feedback": bson.M{"$sum": 1},
|
||||
"total_likes": bson.M{
|
||||
"$sum": bson.M{
|
||||
"$cond": []interface{}{
|
||||
@@ -238,38 +319,37 @@ func (r *feedbackRepository) CompanyStats(ctx context.Context, companyID string)
|
||||
|
||||
results, err := r.repo.Aggregate(ctx, pipeline)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to get feedback stats", map[string]interface{}{
|
||||
"company_id": companyID,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
return feedbackCountAggregate{}, time.Now().Unix(), err
|
||||
}
|
||||
|
||||
stats := &StatsResponse{
|
||||
CompanyID: companyID,
|
||||
TotalFeedback: 0,
|
||||
TotalLikes: 0,
|
||||
TotalDislikes: 0,
|
||||
LastUpdated: time.Now().Unix(),
|
||||
counts := feedbackCountAggregate{}
|
||||
lastUpdated := time.Now().Unix()
|
||||
if len(results) == 0 {
|
||||
return counts, lastUpdated, nil
|
||||
}
|
||||
|
||||
if len(results) > 0 {
|
||||
result := results[0]
|
||||
if totalFeedback, ok := result["total_feedback"].(int32); ok {
|
||||
stats.TotalFeedback = int64(totalFeedback)
|
||||
}
|
||||
if totalLikes, ok := result["total_likes"].(int32); ok {
|
||||
stats.TotalLikes = int64(totalLikes)
|
||||
}
|
||||
if totalDislikes, ok := result["total_dislikes"].(int32); ok {
|
||||
stats.TotalDislikes = int64(totalDislikes)
|
||||
}
|
||||
if lastUpdated, ok := result["last_updated"].(int64); ok {
|
||||
stats.LastUpdated = lastUpdated
|
||||
}
|
||||
result := results[0]
|
||||
counts.totalFeedback = aggregateInt64(result["total_feedback"])
|
||||
counts.totalLikes = aggregateInt64(result["total_likes"])
|
||||
counts.totalDislikes = aggregateInt64(result["total_dislikes"])
|
||||
if v, ok := result["last_updated"].(int64); ok && v > 0 {
|
||||
lastUpdated = v
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
return counts, lastUpdated, nil
|
||||
}
|
||||
|
||||
func aggregateInt64(value interface{}) int64 {
|
||||
switch v := value.(type) {
|
||||
case int32:
|
||||
return int64(v)
|
||||
case int64:
|
||||
return v
|
||||
case float64:
|
||||
return int64(v)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// buildSearchFilter builds MongoDB filter from search criteria
|
||||
|
||||
Reference in New Issue
Block a user