319 lines
8.4 KiB
Go
319 lines
8.4 KiB
Go
package feedback
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"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) (*orm.PaginatedResult[Feedback], 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 {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, ErrFeedbackNotFound
|
|
}
|
|
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 {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, ErrFeedbackNotFound
|
|
}
|
|
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 {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, ErrFeedbackNotFound
|
|
}
|
|
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) (*orm.PaginatedResult[Feedback], error) {
|
|
filter := r.buildSearchFilter(criteria)
|
|
|
|
mongoPagination, err := orm.BuildListPagination(
|
|
pagination.Limit,
|
|
pagination.Offset,
|
|
pagination.Cursor,
|
|
pagination.SortBy,
|
|
pagination.SortOrder,
|
|
filter,
|
|
orm.ListPaginationOptions{},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result, err := r.repo.FindAll(ctx, filter, mongoPagination)
|
|
if err != nil {
|
|
r.logger.Error("Failed to search feedback", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return result, 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
|
|
}
|