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.
287 lines
8.0 KiB
Go
287 lines
8.0 KiB
Go
package inquiry
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
"tm/pkg/logger"
|
|
orm "tm/pkg/mongo"
|
|
"tm/pkg/response"
|
|
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
)
|
|
|
|
// Repository defines methods for inquiry data access
|
|
type Repository interface {
|
|
Create(ctx context.Context, inquiry *Inquiry) error
|
|
GetByID(ctx context.Context, id string) (*Inquiry, error)
|
|
UpdateStatus(ctx context.Context, id string, status, reason, changedBy, description string) error
|
|
Search(ctx context.Context, form *SearchInquiriesForm, pagination *response.Pagination) ([]Inquiry, int64, error)
|
|
Delete(ctx context.Context, id string) error
|
|
CheckPendingInquiry(ctx context.Context, email string) (*Inquiry, error)
|
|
CheckPendingInquiryByPhone(ctx context.Context, phone string) (*Inquiry, error)
|
|
}
|
|
|
|
// inquiryRepository implements the Repository interface using the MongoDB ORM
|
|
type inquiryRepository struct {
|
|
ormRepo orm.Repository[Inquiry]
|
|
logger logger.Logger
|
|
}
|
|
|
|
// NewInquiryRepository creates a new inquiry repository
|
|
func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
|
|
// Create indexes using the ORM's index management
|
|
indexes := []orm.Index{
|
|
*orm.CreateTextIndex("search_idx", "full_name", "company_name", "work_email"),
|
|
*orm.NewIndex("company_name_idx", bson.D{bson.E{Key: "company_name", Value: 1}}),
|
|
*orm.NewIndex("work_email_idx", bson.D{bson.E{Key: "work_email", Value: 1}}),
|
|
*orm.NewIndex("phone_number_idx", bson.D{bson.E{Key: "phone_number", Value: 1}}),
|
|
*orm.NewIndex("status_idx", bson.D{bson.E{Key: "status", Value: 1}}),
|
|
*orm.NewIndex("created_at_idx", bson.D{bson.E{Key: "created_at", Value: -1}}),
|
|
// Compound index for duplicate prevention
|
|
*orm.NewIndex("email_status_idx", bson.D{
|
|
bson.E{Key: "work_email", Value: 1},
|
|
bson.E{Key: "status", Value: 1},
|
|
}),
|
|
*orm.NewIndex("phone_status_idx", bson.D{
|
|
bson.E{Key: "phone_number", Value: 1},
|
|
bson.E{Key: "status", Value: 1},
|
|
}),
|
|
}
|
|
|
|
// Create indexes
|
|
err := mongoManager.CreateIndexes("inquiries", indexes)
|
|
if err != nil {
|
|
logger.Warn("Failed to create inquiry indexes", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Create ORM repository
|
|
ormRepo := orm.NewRepository[Inquiry](mongoManager.GetCollection("inquiries"), logger)
|
|
|
|
return &inquiryRepository{
|
|
ormRepo: ormRepo,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Create creates a new inquiry
|
|
func (r *inquiryRepository) Create(ctx context.Context, inquiry *Inquiry) error {
|
|
// Set created timestamp using Unix timestamp
|
|
now := time.Now().Unix()
|
|
inquiry.SetCreatedAt(now)
|
|
inquiry.SetUpdatedAt(now)
|
|
|
|
// Set initial status to pending
|
|
inquiry.Status = StatusPending
|
|
|
|
// Add initial status change to history
|
|
inquiry.AddStatusChange(StatusPending, "Inquiry submitted", "system", "Initial inquiry submission")
|
|
|
|
// Use ORM to create inquiry
|
|
err := r.ormRepo.Create(ctx, inquiry)
|
|
if err != nil {
|
|
r.logger.Error("Failed to create inquiry", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"work_email": inquiry.WorkEmail,
|
|
"company_name": inquiry.CompanyName,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Inquiry created successfully", map[string]interface{}{
|
|
"inquiry_id": inquiry.ID,
|
|
"work_email": inquiry.WorkEmail,
|
|
"company_name": inquiry.CompanyName,
|
|
"status": inquiry.Status,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetByID retrieves an inquiry by ID
|
|
func (r *inquiryRepository) GetByID(ctx context.Context, id string) (*Inquiry, error) {
|
|
inquiry, err := r.ormRepo.FindByID(ctx, id)
|
|
if err != nil {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, errors.New("inquiry not found")
|
|
}
|
|
r.logger.Error("Failed to get inquiry by ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"inquiry_id": id,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return inquiry, nil
|
|
}
|
|
|
|
// UpdateStatus updates the status of an inquiry
|
|
func (r *inquiryRepository) UpdateStatus(ctx context.Context, id string, status, reason, changedBy, description string) error {
|
|
// Get inquiry first
|
|
inquiry, err := r.GetByID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Add status change to history
|
|
inquiry.AddStatusChange(status, reason, changedBy, description)
|
|
|
|
// Update in database - use the correct Update method signature
|
|
err = r.ormRepo.Update(ctx, inquiry)
|
|
if err != nil {
|
|
r.logger.Error("Failed to update inquiry status", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"inquiry_id": id,
|
|
"new_status": status,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Inquiry status updated successfully", map[string]interface{}{
|
|
"inquiry_id": id,
|
|
"old_status": inquiry.StatusHistory[len(inquiry.StatusHistory)-2].Status, // Previous status
|
|
"new_status": status,
|
|
"changed_by": changedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// CheckPendingInquiry checks if there's a pending inquiry for the given email
|
|
func (r *inquiryRepository) CheckPendingInquiry(ctx context.Context, email string) (*Inquiry, error) {
|
|
filter := bson.M{
|
|
"work_email": email,
|
|
"status": bson.M{"$in": []string{StatusPending, StatusReviewed}},
|
|
}
|
|
|
|
pagination := orm.NewPaginationBuilder().
|
|
Limit(1).
|
|
SortBy("created_at", "desc").
|
|
Build()
|
|
|
|
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
|
if err != nil {
|
|
r.logger.Error("Failed to check pending inquiry by email", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"work_email": email,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
if len(result.Items) > 0 {
|
|
return &result.Items[0], nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// CheckPendingInquiryByPhone checks if there's a pending inquiry for the given phone number
|
|
func (r *inquiryRepository) CheckPendingInquiryByPhone(ctx context.Context, phone string) (*Inquiry, error) {
|
|
filter := bson.M{
|
|
"phone_number": phone,
|
|
"status": bson.M{"$in": []string{StatusPending, StatusReviewed}},
|
|
}
|
|
|
|
pagination := orm.NewPaginationBuilder().
|
|
Limit(1).
|
|
SortBy("created_at", "desc").
|
|
Build()
|
|
|
|
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
|
if err != nil {
|
|
r.logger.Error("Failed to check pending inquiry by phone", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"phone_number": phone,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
if len(result.Items) > 0 {
|
|
return &result.Items[0], nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// Search retrieves inquiries with pagination and filters
|
|
func (r *inquiryRepository) Search(ctx context.Context, form *SearchInquiriesForm, pagination *response.Pagination) ([]Inquiry, int64, error) {
|
|
filter := bson.M{}
|
|
|
|
// Add search filter
|
|
if form.Search != nil {
|
|
filter["$or"] = bson.A{
|
|
bson.M{"full_name": bson.M{"$regex": form.Search, "$options": "i"}},
|
|
bson.M{"company_name": bson.M{"$regex": form.Search, "$options": "i"}},
|
|
bson.M{"work_email": bson.M{"$regex": form.Search, "$options": "i"}},
|
|
}
|
|
}
|
|
|
|
// Add status filter
|
|
if form.Status != nil {
|
|
filter["status"] = *form.Status
|
|
}
|
|
|
|
// 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 inquiries
|
|
result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder)
|
|
if err != nil {
|
|
r.logger.Error("Failed to search inquiries", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, 0, err
|
|
}
|
|
|
|
return result.Items, result.TotalCount, nil
|
|
}
|
|
|
|
// Delete deletes an inquiry
|
|
func (r *inquiryRepository) Delete(ctx context.Context, id string) error {
|
|
// Get inquiry first to ensure it exists
|
|
_, err := r.GetByID(ctx, id)
|
|
if err != nil {
|
|
r.logger.Error("Failed to delete inquiry", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"inquiry_id": id,
|
|
})
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return errors.New("inquiry not found")
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Delete in database
|
|
err = r.ormRepo.Delete(ctx, id)
|
|
if err != nil {
|
|
r.logger.Error("Failed to delete inquiry", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"inquiry_id": id,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Inquiry deleted successfully", map[string]interface{}{
|
|
"inquiry_id": id,
|
|
})
|
|
|
|
return nil
|
|
}
|