Files
tm_back/internal/feedback/service.go
T
n.nakhostin a342da193e 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.
2025-09-24 12:20:19 +03:30

305 lines
9.2 KiB
Go

package feedback
import (
"context"
"tm/internal/company"
"tm/internal/tender"
"tm/pkg/logger"
"tm/pkg/response"
)
// FeedbackService interface defines feedback business logic methods
type Service interface {
Toggle(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error)
Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*FeedbackListResponse, error)
Get(ctx context.Context, id string) (*FeedbackResponse, error)
GetByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error)
Delete(ctx context.Context, id string) error
CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error)
getDetails(ctx context.Context, tenderID, companyID string) (*tenderResponse, *companyResponse, error)
}
// feedbackService implements FeedbackService interface
type feedbackService struct {
feedbackRepo Repository
tenderService tender.Service
companyService company.Service
logger logger.Logger
}
// NewService creates a new feedback service
func NewService(feedbackRepo Repository, tenderService tender.Service, companyService company.Service, logger logger.Logger) Service {
return &feedbackService{
feedbackRepo: feedbackRepo,
tenderService: tenderService,
companyService: companyService,
logger: logger,
}
}
func (s *feedbackService) Toggle(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) {
existing, err := s.feedbackRepo.GetByCompanyID(ctx, *companyID, req.Tender)
if err != nil {
feedback := &Feedback{
TenderID: req.Tender,
FeedbackType: req.Type,
CompanyID: companyID,
CustomerID: customerID,
}
err = s.feedbackRepo.Create(ctx, feedback)
if err != nil {
s.logger.Error("Failed to create new feedback", map[string]interface{}{
"tender_id": req.Tender,
"company_id": *companyID,
"error": err.Error(),
})
return nil, err
}
tender, company, err := s.getDetails(ctx, req.Tender, *companyID)
if err != nil {
s.logger.Error("Failed to get details", map[string]interface{}{
"tender_id": req.Tender,
"company_id": *companyID,
"error": err.Error(),
})
}
return feedback.ToResponse(tender, company), nil
}
if existing.FeedbackType == req.Type {
err = s.feedbackRepo.Delete(ctx, existing.GetID())
if err != nil {
s.logger.Error("Failed to update existing feedback", map[string]interface{}{
"feedback_id": existing.GetID(),
"tender_id": req.Tender,
"company_id": *companyID,
"error": err.Error(),
})
return nil, err
}
if req.Type == FeedbackTypeLike {
existing.FeedbackType = "unlike"
} else {
existing.FeedbackType = "undislike"
}
tender, company, err := s.getDetails(ctx, req.Tender, *companyID)
if err != nil {
s.logger.Error("Failed to get details", map[string]interface{}{
"tender_id": req.Tender,
"company_id": *companyID,
"error": err.Error(),
})
}
return existing.ToResponse(tender, company), nil
}
if existing.FeedbackType == FeedbackTypeLike {
existing.FeedbackType = FeedbackTypeDislike
} else {
existing.FeedbackType = FeedbackTypeLike
}
err = s.feedbackRepo.Update(ctx, existing)
if err != nil {
s.logger.Error("Failed to update existing feedback", map[string]interface{}{
"feedback_id": existing.GetID(),
"tender_id": req.Tender,
"company_id": *companyID,
"error": err.Error(),
})
return nil, err
}
tender, company, err := s.getDetails(ctx, req.Tender, *companyID)
if err != nil {
s.logger.Error("Failed to get details", map[string]interface{}{
"tender_id": req.Tender,
"company_id": *companyID,
"error": err.Error(),
})
}
return existing.ToResponse(tender, company), nil
}
func (s *feedbackService) Get(ctx context.Context, id string) (*FeedbackResponse, error) {
feedback, err := s.feedbackRepo.GetByID(ctx, id)
if err != nil {
s.logger.Error("Failed to get feedback", map[string]interface{}{
"id": id,
"error": err.Error(),
})
return nil, err
}
tender, company, err := s.getDetails(ctx, feedback.TenderID, *feedback.CompanyID)
if err != nil {
s.logger.Error("Failed to get details", map[string]interface{}{
"tender_id": feedback.TenderID,
"company_id": *feedback.CompanyID,
"error": err.Error(),
})
}
return feedback.ToResponse(tender, company), nil
}
func (s *feedbackService) Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*FeedbackListResponse, error) {
feedbacks, total, err := s.feedbackRepo.Search(ctx, criteria, pagination)
if err != nil {
s.logger.Error("Failed to list feedback", map[string]interface{}{
"criteria": criteria,
"limit": pagination.Limit,
"offset": pagination.Offset,
"error": err.Error(),
})
return nil, err
}
meta := &response.Meta{
Total: int(total),
Limit: pagination.Limit,
Offset: pagination.Offset,
Page: (pagination.Offset / pagination.Limit) + 1,
Pages: int((total + int64(pagination.Limit) - 1) / int64(pagination.Limit)),
}
result := make([]*FeedbackResponse, len(feedbacks))
for i, feedback := range feedbacks {
tender, company, _ := s.getDetails(ctx, feedback.TenderID, *feedback.CompanyID)
result[i] = feedback.ToResponse(tender, company)
}
return &FeedbackListResponse{
Feedback: result,
Meta: meta,
}, nil
}
func (s *feedbackService) Delete(ctx context.Context, id string) error {
return s.feedbackRepo.Delete(ctx, id)
}
func (s *feedbackService) GetByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error) {
feedback, err := s.feedbackRepo.GetByTenderID(ctx, tenderID, companyID)
if err != nil {
s.logger.Error("Failed to get feedback by tender ID", map[string]interface{}{
"tender_id": tenderID,
"error": err.Error(),
})
return nil, err
}
tender, err := s.tenderService.GetByID(ctx, feedback.TenderID)
if err != nil {
s.logger.Error("Failed to get tender", map[string]interface{}{
"tender_id": feedback.TenderID,
"error": err.Error(),
})
}
company, err := s.companyService.GetByID(ctx, *feedback.CompanyID)
if err != nil {
s.logger.Error("Failed to get company", map[string]interface{}{
"company_id": feedback.CompanyID,
"error": err.Error(),
})
}
return feedback.ToResponse(
&tenderResponse{
ID: tender.ID,
NoticePublicationID: tender.NoticePublicationID,
PublicationDate: tender.PublicationDate,
Title: tender.Title,
Description: tender.Description,
ProcurementTypeCode: tender.ProcurementTypeCode,
ProcedureCode: tender.ProcedureCode,
EstimatedValue: tender.EstimatedValue,
Currency: tender.Currency,
Duration: tender.Duration,
DurationUnit: tender.DurationUnit,
TenderDeadline: tender.TenderDeadline,
SubmissionDeadline: tender.SubmissionDeadline,
CountryCode: tender.CountryCode,
BuyerOrganization: &organizationResponse{Name: tender.BuyerOrganization.Name},
Status: string(tender.Status),
},
&companyResponse{
ID: company.ID,
Name: company.Name,
},
), nil
}
func (s *feedbackService) getDetails(ctx context.Context, tenderID, companyID string) (*tenderResponse, *companyResponse, error) {
tender, err := s.tenderService.GetByID(ctx, tenderID)
if err != nil {
s.logger.Error("Failed to get tender", map[string]interface{}{
"tender_id": tenderID,
"error": err.Error(),
})
}
company, err := s.companyService.GetByID(ctx, companyID)
if err != nil {
s.logger.Error("Failed to get company", map[string]interface{}{
"company_id": companyID,
"error": err.Error(),
})
}
return &tenderResponse{
ID: tender.ID,
NoticePublicationID: tender.NoticePublicationID,
PublicationDate: tender.PublicationDate,
Title: tender.Title,
Description: tender.Description,
ProcurementTypeCode: tender.ProcurementTypeCode,
ProcedureCode: tender.ProcedureCode,
EstimatedValue: tender.EstimatedValue,
Currency: tender.Currency,
Duration: tender.Duration,
DurationUnit: tender.DurationUnit,
TenderDeadline: tender.TenderDeadline,
SubmissionDeadline: tender.SubmissionDeadline,
CountryCode: tender.CountryCode,
BuyerOrganization: &organizationResponse{Name: tender.BuyerOrganization.Name},
Status: string(tender.Status),
}, &companyResponse{
ID: company.ID,
Name: company.Name,
}, nil
}
func (s *feedbackService) CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error) {
s.logger.Info("Getting company feedback statistics", map[string]interface{}{
"company_id": companyID,
})
stats, err := s.feedbackRepo.CompanyStats(ctx, companyID)
if err != nil {
s.logger.Error("Failed to get company stats", map[string]interface{}{
"company_id": companyID,
"error": err.Error(),
})
return nil, err
}
s.logger.Info("Company stats retrieved successfully", map[string]interface{}{
"company_id": companyID,
"total_feedback": stats.TotalFeedback,
"total_likes": stats.TotalLikes,
"total_dislikes": stats.TotalDislikes,
})
return stats, nil
}