Files
tm_back/internal/inquiry/repository.go
T
2026-05-17 17:21:36 +03:30

281 lines
7.8 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) (*orm.PaginatedResult[Inquiry], 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) (*orm.PaginatedResult[Inquiry], 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
}
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.ormRepo.FindAll(ctx, filter, mongoPagination)
if err != nil {
r.logger.Error("Failed to search inquiries", map[string]interface{}{
"error": err.Error(),
})
return nil, err
}
return result, 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
}