Add customer feedback statistics endpoint and refactor feedback handling
This commit is contained in:
@@ -265,6 +265,7 @@ func RegisterPublicRoutes(e *echo.Echo, customerHandler *customer.Handler, tende
|
|||||||
feedbackGP.GET("", feedbackHandler.PublicList)
|
feedbackGP.GET("", feedbackHandler.PublicList)
|
||||||
feedbackGP.GET("/tender/:tender_id", feedbackHandler.PublicGetByTenderID)
|
feedbackGP.GET("/tender/:tender_id", feedbackHandler.PublicGetByTenderID)
|
||||||
feedbackGP.GET("/:id", feedbackHandler.PublicGet)
|
feedbackGP.GET("/:id", feedbackHandler.PublicGet)
|
||||||
|
feedbackGP.GET("/stats/customer", feedbackHandler.GetCustomerStats)
|
||||||
feedbackGP.GET("/stats/company", feedbackHandler.GetCompanyStats)
|
feedbackGP.GET("/stats/company", feedbackHandler.GetCompanyStats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,3 +87,12 @@ type StatsResponse struct {
|
|||||||
TotalFeedback int64 `json:"total_feedback"`
|
TotalFeedback int64 `json:"total_feedback"`
|
||||||
LastUpdated int64 `json:"last_updated"`
|
LastUpdated int64 `json:"last_updated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CustomerStatsResponse represents feedback statistics for a single customer.
|
||||||
|
type CustomerStatsResponse struct {
|
||||||
|
CustomerID string `json:"customer_id"`
|
||||||
|
TotalLikes int64 `json:"total_likes"`
|
||||||
|
TotalDislikes int64 `json:"total_dislikes"`
|
||||||
|
TotalFeedback int64 `json:"total_feedback"`
|
||||||
|
LastUpdated int64 `json:"last_updated"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ func (h *Handler) Toggle(c echo.Context) error {
|
|||||||
|
|
||||||
// ListFeedback retrieves feedback with pagination and filtering
|
// ListFeedback retrieves feedback with pagination and filtering
|
||||||
// @Summary List feedback with filters
|
// @Summary List feedback with filters
|
||||||
// @Description Retrieve paginated list of feedback with optional filtering by various criteria. This endpoint is for public use and filters results based on the authenticated user's company.
|
// @Description Retrieve paginated list of feedback for the authenticated customer with optional filters.
|
||||||
// @Tags Feedback
|
// @Tags Feedback
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
@@ -214,16 +214,16 @@ func (h *Handler) Toggle(c echo.Context) error {
|
|||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /api/v1/feedback [get]
|
// @Router /api/v1/feedback [get]
|
||||||
func (h *Handler) PublicList(c echo.Context) error {
|
func (h *Handler) PublicList(c echo.Context) error {
|
||||||
companyID, err := user.GetCompanyIDFromContext(c)
|
customerID, err := customer.GetCustomerIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Company ID required", "User must be associated with a company")
|
return response.BadRequest(c, "Customer ID required", "User must be associated with a customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
form, err := response.Parse[SearchForm](c)
|
form, err := response.Parse[SearchForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
form.Company = &companyID
|
form.Customer = &customerID
|
||||||
|
|
||||||
pagination, err := response.NewPagination(c)
|
pagination, err := response.NewPagination(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -287,12 +287,12 @@ func (h *Handler) PublicGetByTenderID(c echo.Context) error {
|
|||||||
return response.BadRequest(c, "Tender ID is required", "Tender ID parameter is missing")
|
return response.BadRequest(c, "Tender ID is required", "Tender ID parameter is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
companyID, err := user.GetCompanyIDFromContext(c)
|
customerID, err := customer.GetCustomerIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Company ID required", "User must be associated with a company")
|
return response.BadRequest(c, "Customer ID required", "User must be associated with a customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
feedback, err := h.service.GetByTenderID(c.Request().Context(), tenderID, companyID)
|
feedback, err := h.service.GetByTenderIDForCustomer(c.Request().Context(), tenderID, customerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Failed to get feedback", "")
|
return response.BadRequest(c, "Failed to get feedback", "")
|
||||||
}
|
}
|
||||||
@@ -300,28 +300,44 @@ func (h *Handler) PublicGetByTenderID(c echo.Context) error {
|
|||||||
return response.Success(c, feedback, "Feedback retrieved successfully")
|
return response.Success(c, feedback, "Feedback retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompanyFeedbackStats retrieves feedback statistics for the authenticated user's company
|
// GetCustomerStats retrieves feedback statistics for the authenticated customer.
|
||||||
// @Summary Get company feedback statistics
|
// @Summary Get customer feedback statistics
|
||||||
// @Description Retrieve comprehensive feedback statistics for the authenticated user's company including total likes, dislikes, and percentage calculations.
|
// @Description Retrieve feedback statistics (likes, dislikes, totals) for the authenticated customer only.
|
||||||
// @Tags Feedback
|
// @Tags Feedback
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} response.APIResponse{data=StatsResponse} "Company feedback statistics retrieved successfully"
|
// @Success 200 {object} response.APIResponse{data=CustomerStatsResponse} "Customer feedback statistics retrieved successfully"
|
||||||
// @Failure 400 {object} response.APIResponse{error=string,message=string} "Bad request - Missing company association"
|
// @Failure 400 {object} response.APIResponse{error=string,message=string} "Bad request - Missing customer association"
|
||||||
|
// @Failure 401 {object} response.APIResponse{error=string,message=string} "Unauthorized - User not authenticated"
|
||||||
|
// @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error"
|
||||||
|
// @Security BearerAuth
|
||||||
|
// @Router /api/v1/feedback/stats/customer [get]
|
||||||
|
func (h *Handler) GetCustomerStats(c echo.Context) error {
|
||||||
|
customerID, err := customer.GetCustomerIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return response.BadRequest(c, "Customer ID required", "User must be associated with a customer")
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := h.service.CustomerStats(c.Request().Context(), customerID)
|
||||||
|
if err != nil {
|
||||||
|
return response.InternalServerError(c, "Failed to retrieve customer stats")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Success(c, stats, "Customer stats retrieved successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyStats returns customer-scoped stats for backward compatibility with clients using /stats/company.
|
||||||
|
// @Summary Get feedback statistics (customer-scoped, legacy path)
|
||||||
|
// @Description Same as GET /feedback/stats/customer. Prefer /stats/customer for new integrations.
|
||||||
|
// @Tags Feedback
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.APIResponse{data=CustomerStatsResponse} "Customer feedback statistics retrieved successfully"
|
||||||
|
// @Failure 400 {object} response.APIResponse{error=string,message=string} "Bad request - Missing customer association"
|
||||||
// @Failure 401 {object} response.APIResponse{error=string,message=string} "Unauthorized - User not authenticated"
|
// @Failure 401 {object} response.APIResponse{error=string,message=string} "Unauthorized - User not authenticated"
|
||||||
// @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error"
|
// @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /api/v1/feedback/stats/company [get]
|
// @Router /api/v1/feedback/stats/company [get]
|
||||||
func (h *Handler) GetCompanyStats(c echo.Context) error {
|
func (h *Handler) GetCompanyStats(c echo.Context) error {
|
||||||
companyID, err := user.GetCompanyIDFromContext(c)
|
return h.GetCustomerStats(c)
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Company ID required", "User must be associated with a company")
|
|
||||||
}
|
|
||||||
|
|
||||||
stats, err := h.service.CompanyStats(c.Request().Context(), companyID)
|
|
||||||
if err != nil {
|
|
||||||
return response.InternalServerError(c, "Failed to retrieve company stats")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, stats, "Company stats retrieved successfully")
|
|
||||||
}
|
}
|
||||||
|
|||||||
+115
-35
@@ -19,9 +19,12 @@ type Repository interface {
|
|||||||
Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*orm.PaginatedResult[Feedback], error)
|
Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*orm.PaginatedResult[Feedback], error)
|
||||||
GetByID(ctx context.Context, id string) (*Feedback, error)
|
GetByID(ctx context.Context, id string) (*Feedback, error)
|
||||||
GetByCompanyID(ctx context.Context, companyID string, tender 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)
|
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
|
Delete(ctx context.Context, id string) error
|
||||||
CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error)
|
CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error)
|
||||||
|
CustomerStats(ctx context.Context, customerID string) (*CustomerStatsResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// feedbackRepository implements FeedbackRepository interface using MongoDB ORM
|
// 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: "tender_id", Value: 1},
|
||||||
{Key: "company_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{
|
*orm.NewIndex("tender_type_idx", bson.D{
|
||||||
{Key: "tender_id", Value: 1},
|
{Key: "tender_id", Value: 1},
|
||||||
{Key: "feedback_type", Value: 1},
|
{Key: "feedback_type", Value: 1},
|
||||||
@@ -140,6 +148,23 @@ func (r *feedbackRepository) GetByCompanyID(ctx context.Context, companyID strin
|
|||||||
return result, nil
|
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
|
// GetByTenderID retrieves feedback by tender ID and company ID
|
||||||
func (r *feedbackRepository) GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error) {
|
func (r *feedbackRepository) GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error) {
|
||||||
filter := bson.M{"tender_id": tenderID, "company_id": companyID}
|
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
|
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
|
// Delete deletes a feedback by ID
|
||||||
func (r *feedbackRepository) Delete(ctx context.Context, id string) error {
|
func (r *feedbackRepository) Delete(ctx context.Context, id string) error {
|
||||||
err := r.repo.Delete(ctx, id)
|
err := r.repo.Delete(ctx, id)
|
||||||
@@ -199,20 +241,59 @@ func (r *feedbackRepository) Search(ctx context.Context, criteria SearchForm, pa
|
|||||||
return result, nil
|
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) {
|
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{
|
pipeline := mongo.Pipeline{
|
||||||
{
|
{{Key: "$match", Value: match}},
|
||||||
{Key: "$match", Value: bson.M{
|
|
||||||
"company_id": companyID,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
{Key: "$group", Value: bson.M{
|
{Key: "$group", Value: bson.M{
|
||||||
"_id": nil,
|
"_id": nil,
|
||||||
"total_feedback": bson.M{
|
"total_feedback": bson.M{"$sum": 1},
|
||||||
"$sum": 1,
|
|
||||||
},
|
|
||||||
"total_likes": bson.M{
|
"total_likes": bson.M{
|
||||||
"$sum": bson.M{
|
"$sum": bson.M{
|
||||||
"$cond": []interface{}{
|
"$cond": []interface{}{
|
||||||
@@ -238,38 +319,37 @@ func (r *feedbackRepository) CompanyStats(ctx context.Context, companyID string)
|
|||||||
|
|
||||||
results, err := r.repo.Aggregate(ctx, pipeline)
|
results, err := r.repo.Aggregate(ctx, pipeline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to get feedback stats", map[string]interface{}{
|
return feedbackCountAggregate{}, time.Now().Unix(), err
|
||||||
"company_id": companyID,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := &StatsResponse{
|
counts := feedbackCountAggregate{}
|
||||||
CompanyID: companyID,
|
lastUpdated := time.Now().Unix()
|
||||||
TotalFeedback: 0,
|
if len(results) == 0 {
|
||||||
TotalLikes: 0,
|
return counts, lastUpdated, nil
|
||||||
TotalDislikes: 0,
|
|
||||||
LastUpdated: time.Now().Unix(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(results) > 0 {
|
result := results[0]
|
||||||
result := results[0]
|
counts.totalFeedback = aggregateInt64(result["total_feedback"])
|
||||||
if totalFeedback, ok := result["total_feedback"].(int32); ok {
|
counts.totalLikes = aggregateInt64(result["total_likes"])
|
||||||
stats.TotalFeedback = int64(totalFeedback)
|
counts.totalDislikes = aggregateInt64(result["total_dislikes"])
|
||||||
}
|
if v, ok := result["last_updated"].(int64); ok && v > 0 {
|
||||||
if totalLikes, ok := result["total_likes"].(int32); ok {
|
lastUpdated = v
|
||||||
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
|
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
|
// buildSearchFilter builds MongoDB filter from search criteria
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ type Service interface {
|
|||||||
Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*FeedbackListResponse, error)
|
Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*FeedbackListResponse, error)
|
||||||
Get(ctx context.Context, id string) (*FeedbackResponse, error)
|
Get(ctx context.Context, id string) (*FeedbackResponse, error)
|
||||||
GetByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error)
|
GetByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error)
|
||||||
|
GetByTenderIDForCustomer(ctx context.Context, tenderID, customerID string) (*FeedbackResponse, error)
|
||||||
Delete(ctx context.Context, id string) error
|
Delete(ctx context.Context, id string) error
|
||||||
CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error)
|
CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error)
|
||||||
|
CustomerStats(ctx context.Context, customerID string) (*CustomerStatsResponse, error)
|
||||||
getDetails(ctx context.Context, tenderID, companyID string) (*tenderResponse, *companyResponse)
|
getDetails(ctx context.Context, tenderID, companyID string) (*tenderResponse, *companyResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ func NewService(feedbackRepo Repository, tenderService tender.Service, companySe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *feedbackService) Toggle(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) {
|
func (s *feedbackService) Toggle(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) {
|
||||||
existing, err := s.feedbackRepo.GetByCompanyID(ctx, *companyID, req.Tender)
|
existing, err := s.feedbackRepo.GetByCustomerID(ctx, *customerID, req.Tender)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
feedback := &Feedback{
|
feedback := &Feedback{
|
||||||
@@ -51,9 +53,10 @@ func (s *feedbackService) Toggle(ctx context.Context, req ToggleFeedbackForm, cu
|
|||||||
err = s.feedbackRepo.Create(ctx, feedback)
|
err = s.feedbackRepo.Create(ctx, feedback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to create new feedback", map[string]interface{}{
|
s.logger.Error("Failed to create new feedback", map[string]interface{}{
|
||||||
"tender_id": req.Tender,
|
"tender_id": req.Tender,
|
||||||
"company_id": *companyID,
|
"customer_id": *customerID,
|
||||||
"error": err.Error(),
|
"company_id": *companyID,
|
||||||
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -66,10 +69,10 @@ func (s *feedbackService) Toggle(ctx context.Context, req ToggleFeedbackForm, cu
|
|||||||
if existing.FeedbackType == req.Type {
|
if existing.FeedbackType == req.Type {
|
||||||
err = s.feedbackRepo.Delete(ctx, existing.GetID())
|
err = s.feedbackRepo.Delete(ctx, existing.GetID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update existing feedback", map[string]interface{}{
|
s.logger.Error("Failed to delete existing feedback", map[string]interface{}{
|
||||||
"feedback_id": existing.GetID(),
|
"feedback_id": existing.GetID(),
|
||||||
"tender_id": req.Tender,
|
"tender_id": req.Tender,
|
||||||
"company_id": *companyID,
|
"customer_id": *customerID,
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -97,7 +100,7 @@ func (s *feedbackService) Toggle(ctx context.Context, req ToggleFeedbackForm, cu
|
|||||||
s.logger.Error("Failed to update existing feedback", map[string]interface{}{
|
s.logger.Error("Failed to update existing feedback", map[string]interface{}{
|
||||||
"feedback_id": existing.GetID(),
|
"feedback_id": existing.GetID(),
|
||||||
"tender_id": req.Tender,
|
"tender_id": req.Tender,
|
||||||
"company_id": *companyID,
|
"customer_id": *customerID,
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -177,6 +180,22 @@ func (s *feedbackService) GetByTenderID(ctx context.Context, tenderID, companyID
|
|||||||
return feedback.ToResponse(tender, company), nil
|
return feedback.ToResponse(tender, company), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *feedbackService) GetByTenderIDForCustomer(ctx context.Context, tenderID, customerID string) (*FeedbackResponse, error) {
|
||||||
|
feedback, err := s.feedbackRepo.GetByTenderIDForCustomer(ctx, tenderID, customerID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get feedback by tender ID for customer", map[string]interface{}{
|
||||||
|
"tender_id": tenderID,
|
||||||
|
"customer_id": customerID,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tender, company := s.getDetails(ctx, feedback.TenderID, feedbackCompanyID(*feedback))
|
||||||
|
|
||||||
|
return feedback.ToResponse(tender, company), nil
|
||||||
|
}
|
||||||
|
|
||||||
func feedbackCompanyID(f Feedback) string {
|
func feedbackCompanyID(f Feedback) string {
|
||||||
if f.CompanyID == nil {
|
if f.CompanyID == nil {
|
||||||
return ""
|
return ""
|
||||||
@@ -346,3 +365,27 @@ func (s *feedbackService) CompanyStats(ctx context.Context, companyID string) (*
|
|||||||
|
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *feedbackService) CustomerStats(ctx context.Context, customerID string) (*CustomerStatsResponse, error) {
|
||||||
|
s.logger.Info("Getting customer feedback statistics", map[string]interface{}{
|
||||||
|
"customer_id": customerID,
|
||||||
|
})
|
||||||
|
|
||||||
|
stats, err := s.feedbackRepo.CustomerStats(ctx, customerID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get customer stats", map[string]interface{}{
|
||||||
|
"customer_id": customerID,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("Customer stats retrieved successfully", map[string]interface{}{
|
||||||
|
"customer_id": customerID,
|
||||||
|
"total_feedback": stats.TotalFeedback,
|
||||||
|
"total_likes": stats.TotalLikes,
|
||||||
|
"total_dislikes": stats.TotalDislikes,
|
||||||
|
})
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user