232 lines
6.2 KiB
Go
232 lines
6.2 KiB
Go
package contact
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"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"
|
|
)
|
|
|
|
// ContactRepository defines the interface for contact data operations
|
|
type ContactRepository interface {
|
|
// Create creates a new contact message
|
|
Create(ctx context.Context, contact *Contact) error
|
|
|
|
// GetByID retrieves a contact by ID
|
|
GetByID(ctx context.Context, id string) (*Contact, error)
|
|
|
|
// Search searches contacts with filters and pagination
|
|
Search(ctx context.Context, filters SearchContactsForm, pagination *response.Pagination) (*orm.PaginatedResult[Contact], error)
|
|
|
|
// UpdateStatus updates the status of a contact
|
|
UpdateStatus(ctx context.Context, id string, status ContactStatus, adminNotes *string, resolvedBy *string) error
|
|
|
|
// GetStats returns contact statistics
|
|
GetStats(ctx context.Context) (*ContactStats, error)
|
|
|
|
// Delete deletes a contact by ID
|
|
Delete(ctx context.Context, id string) error
|
|
}
|
|
|
|
// ContactStats represents contact statistics
|
|
type ContactStats struct {
|
|
Total int64 `json:"total"`
|
|
Pending int64 `json:"pending"`
|
|
InProgress int64 `json:"in_progress"`
|
|
Resolved int64 `json:"resolved"`
|
|
Closed int64 `json:"closed"`
|
|
}
|
|
|
|
// contactRepository implements ContactRepository interface
|
|
type contactRepository struct {
|
|
repo orm.Repository[Contact]
|
|
logger logger.Logger
|
|
}
|
|
|
|
// NewContactRepository creates a new contact repository
|
|
func NewContactRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) ContactRepository {
|
|
// Create indexes for contact collection
|
|
contactIndexes := []orm.Index{
|
|
*orm.NewIndex("email_idx", bson.D{{Key: "email", Value: 1}}),
|
|
*orm.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
|
|
*orm.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
|
|
*orm.CreateTextIndex("contact_text_idx", "full_name", "email", "message"),
|
|
}
|
|
|
|
// Create indexes
|
|
if err := mongoManager.CreateIndexes("contacts", contactIndexes); err != nil {
|
|
logger.Error("Failed to create contact indexes", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
collection := mongoManager.GetCollection("contacts")
|
|
return &contactRepository{
|
|
repo: orm.NewRepository[Contact](collection, logger),
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Create creates a new contact message
|
|
func (r *contactRepository) Create(ctx context.Context, contact *Contact) error {
|
|
// Set default status if not provided
|
|
if contact.Status == "" {
|
|
contact.Status = ContactStatusPending
|
|
}
|
|
|
|
return r.repo.Create(ctx, contact)
|
|
}
|
|
|
|
// GetByID retrieves a contact by ID
|
|
func (r *contactRepository) GetByID(ctx context.Context, id string) (*Contact, error) {
|
|
return r.repo.FindByID(ctx, id)
|
|
}
|
|
|
|
// Search searches contacts with filters and pagination
|
|
func (r *contactRepository) Search(ctx context.Context, filters SearchContactsForm, pagination *response.Pagination) (*orm.PaginatedResult[Contact], error) {
|
|
filter := bson.M{}
|
|
|
|
if filters.Search != nil && *filters.Search != "" {
|
|
filter["$text"] = bson.M{"$search": *filters.Search}
|
|
}
|
|
|
|
if filters.Status != nil && *filters.Status != "" {
|
|
filter["status"] = *filters.Status
|
|
}
|
|
|
|
if filters.DateFrom != nil || filters.DateTo != nil {
|
|
dateFilter := bson.M{}
|
|
if filters.DateFrom != nil {
|
|
dateFilter["$gte"] = *filters.DateFrom
|
|
}
|
|
if filters.DateTo != nil {
|
|
dateFilter["$lte"] = *filters.DateTo
|
|
}
|
|
filter["created_at"] = dateFilter
|
|
}
|
|
|
|
mongoPagination, err := orm.BuildListPagination(
|
|
pagination.Limit,
|
|
pagination.Offset,
|
|
pagination.Cursor,
|
|
pagination.SortBy,
|
|
pagination.SortOrder,
|
|
filter,
|
|
orm.ListPaginationOptions{},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return r.repo.FindAll(ctx, filter, mongoPagination)
|
|
}
|
|
|
|
// UpdateStatus updates the status of a contact
|
|
func (r *contactRepository) UpdateStatus(ctx context.Context, id string, status ContactStatus, adminNotes *string, resolvedBy *string) error {
|
|
// Get the existing contact
|
|
contact, err := r.GetByID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the contact fields
|
|
contact.Status = status
|
|
contact.UpdatedAt = time.Now().Unix()
|
|
|
|
if adminNotes != nil {
|
|
contact.AdminNotes = adminNotes
|
|
}
|
|
|
|
// Set resolved fields if status is resolved or closed
|
|
if status == ContactStatusResolved || status == ContactStatusClosed {
|
|
now := time.Now().Unix()
|
|
contact.ResolvedAt = &now
|
|
if resolvedBy != nil {
|
|
contact.ResolvedBy = resolvedBy
|
|
}
|
|
} else {
|
|
// Unset resolved fields if status is changed back to pending or in_progress
|
|
contact.ResolvedAt = nil
|
|
contact.ResolvedBy = nil
|
|
}
|
|
|
|
err = r.repo.Update(ctx, contact)
|
|
if err != nil {
|
|
r.logger.Error("Failed to update contact status", map[string]interface{}{
|
|
"id": id,
|
|
"status": status,
|
|
"error": err.Error(),
|
|
})
|
|
return fmt.Errorf("failed to update contact status: %w", err)
|
|
}
|
|
|
|
r.logger.Info("Contact status updated successfully", map[string]interface{}{
|
|
"id": id,
|
|
"status": status,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetStats returns contact statistics
|
|
func (r *contactRepository) GetStats(ctx context.Context) (*ContactStats, error) {
|
|
pipeline := mongo.Pipeline{
|
|
{
|
|
{Key: "$group", Value: bson.D{
|
|
{Key: "_id", Value: "$status"},
|
|
{Key: "count", Value: bson.D{{Key: "$sum", Value: 1}}},
|
|
}},
|
|
},
|
|
}
|
|
|
|
results, err := r.repo.Aggregate(ctx, pipeline)
|
|
if err != nil {
|
|
r.logger.Error("Failed to get contact stats", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, fmt.Errorf("failed to get contact stats: %w", err)
|
|
}
|
|
|
|
stats := &ContactStats{}
|
|
for _, result := range results {
|
|
status, ok := result["_id"].(string)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
count, ok := result["count"].(int32)
|
|
if !ok {
|
|
if count64, ok := result["count"].(int64); ok {
|
|
count = int32(count64)
|
|
} else {
|
|
continue
|
|
}
|
|
}
|
|
|
|
switch ContactStatus(status) {
|
|
case ContactStatusPending:
|
|
stats.Pending = int64(count)
|
|
case ContactStatusInProgress:
|
|
stats.InProgress = int64(count)
|
|
case ContactStatusResolved:
|
|
stats.Resolved = int64(count)
|
|
case ContactStatusClosed:
|
|
stats.Closed = int64(count)
|
|
}
|
|
|
|
stats.Total += int64(count)
|
|
}
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// Delete deletes a contact by ID
|
|
func (r *contactRepository) Delete(ctx context.Context, id string) error {
|
|
return r.repo.Delete(ctx, id)
|
|
}
|