a342da193e
- Updated feedback repository and service methods to use a consistent naming convention, changing `NewTenderRepository` and similar methods to `NewRepository`. - Refactored feedback handler methods to improve clarity by renaming methods such as `ListFeedback` to `Search` and `GetFeedback` to `Get`. - Enhanced feedback response structures to include detailed company and tender information, improving the clarity of feedback data returned to API consumers. - Updated Swagger documentation to reflect changes in feedback response structures and endpoint paths, ensuring accurate representation of the API for consumers.
315 lines
8.3 KiB
Go
315 lines
8.3 KiB
Go
package feedback
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
"tm/pkg/logger"
|
|
orm "tm/pkg/mongo"
|
|
"tm/pkg/response"
|
|
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
"go.mongodb.org/mongo-driver/v2/mongo"
|
|
)
|
|
|
|
// FeedbackRepository interface defines feedback data access methods
|
|
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)
|
|
GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, 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 orm.Repository[Feedback]
|
|
logger logger.Logger
|
|
}
|
|
|
|
// NewFeedbackRepository creates a new feedback repository
|
|
func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
|
|
// Create indexes for feedback collection
|
|
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
|
|
*orm.NewIndex("tender_company_idx", bson.D{
|
|
{Key: "tender_id", Value: 1},
|
|
{Key: "company_id", Value: 1},
|
|
}),
|
|
*orm.NewIndex("tender_type_idx", bson.D{
|
|
{Key: "tender_id", Value: 1},
|
|
{Key: "feedback_type", Value: 1},
|
|
}),
|
|
}
|
|
|
|
// Create indexes
|
|
if err := mongoManager.CreateIndexes("feedback", feedbackIndexes); err != nil {
|
|
logger.Warn("Failed to create feedback indexes", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Create ORM repository
|
|
repo := orm.NewRepository[Feedback](mongoManager.GetCollection("feedback"), logger)
|
|
|
|
return &feedbackRepository{
|
|
repo: repo,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Create creates a new feedback
|
|
func (r *feedbackRepository) Create(ctx context.Context, feedback *Feedback) error {
|
|
// Set timestamps
|
|
now := time.Now().Unix()
|
|
feedback.CreatedAt = now
|
|
|
|
err := r.repo.Create(ctx, feedback)
|
|
if err != nil {
|
|
r.logger.Error("Failed to create feedback", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_id": feedback.TenderID,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Feedback created successfully", map[string]interface{}{
|
|
"feedback_id": feedback.GetID(),
|
|
"tender_id": feedback.TenderID,
|
|
"type": feedback.FeedbackType,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Update updates an existing feedback
|
|
func (r *feedbackRepository) Update(ctx context.Context, feedback *Feedback) error {
|
|
feedback.UpdatedAt = time.Now().Unix()
|
|
|
|
err := r.repo.Update(ctx, feedback)
|
|
if err != nil {
|
|
r.logger.Error("Failed to update feedback", map[string]interface{}{
|
|
"feedback_id": feedback.GetID(),
|
|
"error": err.Error(),
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Feedback updated successfully", map[string]interface{}{
|
|
"feedback_id": feedback.GetID(),
|
|
"tender_id": feedback.TenderID,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetByID retrieves feedback by ID
|
|
func (r *feedbackRepository) GetByID(ctx context.Context, id string) (*Feedback, error) {
|
|
feedback, err := r.repo.FindByID(ctx, id)
|
|
if err != nil {
|
|
r.logger.Error("Failed to get feedback by ID", map[string]interface{}{
|
|
"feedback_id": id,
|
|
"error": err.Error(),
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return feedback, nil
|
|
}
|
|
|
|
// GetByCompanyID retrieves feedback by company ID and tender ID
|
|
func (r *feedbackRepository) GetByCompanyID(ctx context.Context, companyID string, tenderID string) (*Feedback, error) {
|
|
filter := bson.M{"company_id": companyID, "tender_id": tenderID}
|
|
|
|
result, err := r.repo.FindOne(ctx, filter)
|
|
if err != nil {
|
|
r.logger.Error("Failed to get feedback by company ID and tender ID", map[string]interface{}{
|
|
"company_id": companyID,
|
|
"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}
|
|
|
|
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 {
|
|
r.logger.Error("Failed to delete feedback", map[string]interface{}{
|
|
"feedback_id": id,
|
|
"error": err.Error(),
|
|
})
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Search searches feedback based on criteria
|
|
func (r *feedbackRepository) Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) ([]Feedback, int64, error) {
|
|
filter := r.buildSearchFilter(criteria)
|
|
|
|
// Build sort
|
|
sort := "created_at"
|
|
if pagination.SortBy != "" {
|
|
sort = pagination.SortBy
|
|
}
|
|
|
|
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 list users", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, 0, err
|
|
}
|
|
|
|
return result.Items, result.TotalCount, nil
|
|
}
|
|
|
|
// 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,
|
|
}},
|
|
},
|
|
{
|
|
{Key: "$group", Value: bson.M{
|
|
"_id": nil,
|
|
"total_feedback": bson.M{
|
|
"$sum": 1,
|
|
},
|
|
"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,
|
|
},
|
|
},
|
|
},
|
|
"last_updated": bson.M{"$max": "$updated_at"},
|
|
}},
|
|
},
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
stats := &StatsResponse{
|
|
CompanyID: companyID,
|
|
TotalFeedback: 0,
|
|
TotalLikes: 0,
|
|
TotalDislikes: 0,
|
|
LastUpdated: time.Now().Unix(),
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// buildSearchFilter builds MongoDB filter from search criteria
|
|
func (r *feedbackRepository) buildSearchFilter(criteria SearchForm) bson.M {
|
|
filter := bson.M{}
|
|
|
|
// Always exclude deleted feedback unless specifically requested
|
|
if criteria.Tender != nil {
|
|
filter["tender_id"] = *criteria.Tender
|
|
}
|
|
|
|
if criteria.Customer != nil {
|
|
filter["customer_id"] = *criteria.Customer
|
|
}
|
|
|
|
if criteria.Company != nil {
|
|
filter["company_id"] = *criteria.Company
|
|
}
|
|
|
|
if criteria.Type != nil {
|
|
filter["feedback_type"] = *criteria.Type
|
|
}
|
|
|
|
if criteria.DateFrom != nil || criteria.DateTo != nil {
|
|
dateFilter := bson.M{}
|
|
if criteria.DateFrom != nil {
|
|
dateFilter["$gte"] = *criteria.DateFrom
|
|
}
|
|
if criteria.DateTo != nil {
|
|
dateFilter["$lte"] = *criteria.DateTo
|
|
}
|
|
filter["created_at"] = dateFilter
|
|
}
|
|
|
|
return filter
|
|
}
|