406 lines
11 KiB
Go
406 lines
11 KiB
Go
package customer
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
"tm/pkg/logger"
|
|
orm "tm/pkg/mongo"
|
|
"tm/pkg/response"
|
|
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
)
|
|
|
|
// Repository defines the interface for customer data operations
|
|
type Repository interface {
|
|
Login(ctx context.Context, id, deviceToken string) error
|
|
Register(ctx context.Context, customer *Customer) error
|
|
Update(ctx context.Context, customer *Customer) error
|
|
UpdateStatus(ctx context.Context, id string, status CustomerStatus) error
|
|
GetByID(ctx context.Context, id string) (*Customer, error)
|
|
GetByEmail(ctx context.Context, email string) (*Customer, error)
|
|
GetByUsername(ctx context.Context, username string) (*Customer, error)
|
|
GetByIDs(ctx context.Context, ids []string) ([]Customer, error)
|
|
GetByRole(ctx context.Context, role CustomerRole) ([]Customer, error)
|
|
GetByCompanies(ctx context.Context, companies []string) ([]Customer, error)
|
|
GetByCompanyID(ctx context.Context, companyID string, pagination *response.Pagination) ([]*Customer, int64, error)
|
|
Search(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) (*orm.PaginatedResult[Customer], error)
|
|
Delete(ctx context.Context, id string) error
|
|
}
|
|
|
|
// customerRepository implements the Repository interface using the MongoDB ORM
|
|
type customerRepository struct {
|
|
ormRepo orm.Repository[Customer]
|
|
logger logger.Logger
|
|
}
|
|
|
|
func collection() string {
|
|
return "customers"
|
|
}
|
|
|
|
// NewCustomerRepository creates a new customer repository
|
|
func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
|
|
// Create indexes using the ORM's index management
|
|
indexes := []orm.Index{
|
|
*orm.CreateUniqueIndex("username_idx", bson.D{{Key: "username", Value: 1}}),
|
|
*orm.CreateTextIndex("search_idx", "email", "username"),
|
|
}
|
|
|
|
// Create indexes
|
|
err := mongoManager.CreateIndexes(collection(), indexes)
|
|
if err != nil {
|
|
logger.Warn("Failed to create customer indexes", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Create ORM repository
|
|
ormRepo := orm.NewRepository[Customer](mongoManager.GetCollection(collection()), logger)
|
|
|
|
return &customerRepository{
|
|
ormRepo: ormRepo,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Register creates a new customer
|
|
func (r *customerRepository) Register(ctx context.Context, customer *Customer) error {
|
|
// Set created/updated timestamps using Unix timestamps
|
|
now := time.Now().Unix()
|
|
customer.SetCreatedAt(now)
|
|
|
|
// Use ORM to create customer
|
|
err := r.ormRepo.Create(ctx, customer)
|
|
if err != nil {
|
|
r.logger.Error("Failed to register customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"email": customer.Email,
|
|
"username": customer.Username,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Customer registered successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
"type": customer.Type,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Login updates the last login timestamp
|
|
func (r *customerRepository) Login(ctx context.Context, id, deviceToken string) error {
|
|
// Get customer first
|
|
customer, err := r.GetByID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// append device token to customer if not exists
|
|
// if !slices.Contains(customer.DeviceToken, deviceToken) {
|
|
customer.DeviceToken = []string{deviceToken}
|
|
// }
|
|
|
|
// Update last login timestamp
|
|
customer.LastLoginAt = &[]int64{time.Now().Unix()}[0]
|
|
customer.SetUpdatedAt(time.Now().Unix())
|
|
|
|
// Update in database
|
|
err = r.ormRepo.Update(ctx, customer)
|
|
if err != nil {
|
|
r.logger.Error("Failed to update last login", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Last login updated successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetByID retrieves a customer by ID
|
|
func (r *customerRepository) GetByID(ctx context.Context, id string) (*Customer, error) {
|
|
customer, err := r.ormRepo.FindByID(ctx, id)
|
|
if err != nil {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, errors.New("customer not found")
|
|
}
|
|
r.logger.Error("Failed to get customer by ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// GetByEmail retrieves a customer by email
|
|
func (r *customerRepository) GetByEmail(ctx context.Context, email string) (*Customer, error) {
|
|
filter := bson.M{"email": email}
|
|
customer, err := r.ormRepo.FindOne(ctx, filter)
|
|
if err != nil {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, errors.New("customer not found")
|
|
}
|
|
r.logger.Error("Failed to get customer by email", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"email": email,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// GetByUsername retrieves a customer by username
|
|
func (r *customerRepository) GetByUsername(ctx context.Context, username string) (*Customer, error) {
|
|
filter := bson.M{"username": username}
|
|
customer, err := r.ormRepo.FindOne(ctx, filter)
|
|
if err != nil {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, errors.New("customer not found")
|
|
}
|
|
r.logger.Error("Failed to get customer by username", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"username": username,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// GetByIDs retrieves customers by IDs
|
|
func (r *customerRepository) GetByIDs(ctx context.Context, ids []string) ([]Customer, error) {
|
|
objectIDs := make([]bson.ObjectID, 0)
|
|
for _, id := range ids {
|
|
objectID, err := bson.ObjectIDFromHex(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
objectIDs = append(objectIDs, objectID)
|
|
}
|
|
|
|
customers, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": objectIDs}}, orm.NewPaginationBuilder().Limit(len(ids)).Build())
|
|
if err != nil {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, errors.New("customers not found")
|
|
}
|
|
r.logger.Error("Failed to get customers by IDs", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"ids": ids,
|
|
})
|
|
return nil, err
|
|
}
|
|
return customers.Items, nil
|
|
}
|
|
|
|
// GetByRole retrieves customers by role
|
|
func (r *customerRepository) GetByRole(ctx context.Context, role CustomerRole) ([]Customer, error) {
|
|
customers, err := r.ormRepo.FindAll(ctx, bson.M{"role": role}, orm.NewPaginationBuilder().Build())
|
|
if err != nil {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return nil, errors.New("customers not found")
|
|
}
|
|
r.logger.Error("Failed to get customers by role", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"role": role,
|
|
})
|
|
return nil, err
|
|
}
|
|
return customers.Items, nil
|
|
}
|
|
|
|
// GetByCompanies retrieves customers by companies
|
|
|
|
func (r *customerRepository) GetByCompanies(ctx context.Context, companies []string) ([]Customer, error) {
|
|
companyIDs := make([]bson.ObjectID, 0)
|
|
for _, company := range companies {
|
|
id, err := bson.ObjectIDFromHex(company)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
companyIDs = append(companyIDs, id)
|
|
}
|
|
|
|
customers, err := r.ormRepo.FindAll(ctx, bson.M{"companies": bson.M{"$in": companyIDs}}, orm.NewPaginationBuilder().Build())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return customers.Items, nil
|
|
}
|
|
|
|
// GetByCompanyID retrieves customers by company ID with pagination
|
|
func (r *customerRepository) GetByCompanyID(ctx context.Context, companyID string, pagination *response.Pagination) ([]*Customer, int64, error) {
|
|
// Build pagination
|
|
paginationBuilder := orm.NewPaginationBuilder().
|
|
Limit(pagination.Limit).
|
|
Skip(pagination.Offset).
|
|
SortBy(pagination.SortBy, pagination.SortOrder).
|
|
Build()
|
|
|
|
// Filter by company ID and active status
|
|
filter := bson.M{
|
|
"company_id": companyID,
|
|
"status": bson.M{"$ne": CustomerStatusInactive},
|
|
}
|
|
|
|
// Use ORM to find customers
|
|
result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder)
|
|
if err != nil {
|
|
r.logger.Error("Failed to get customers by company ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
"limit": pagination.Limit,
|
|
"offset": pagination.Offset,
|
|
})
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Convert []Customer to []*Customer
|
|
customers := make([]*Customer, len(result.Items))
|
|
for i := range result.Items {
|
|
customers[i] = &result.Items[i]
|
|
}
|
|
|
|
return customers, result.TotalCount, nil
|
|
}
|
|
|
|
// Update updates a customer
|
|
func (r *customerRepository) Update(ctx context.Context, customer *Customer) error {
|
|
// Set updated timestamp using Unix timestamp
|
|
customer.SetUpdatedAt(time.Now().Unix())
|
|
|
|
// Use ORM to update customer
|
|
err := r.ormRepo.Update(ctx, customer)
|
|
if err != nil {
|
|
r.logger.Error("Failed to update customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customer.ID,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Customer updated successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Delete deletes a customer (hard delete)
|
|
func (r *customerRepository) Delete(ctx context.Context, id string) error {
|
|
// Get customer first
|
|
_, err := r.GetByID(ctx, id)
|
|
if err != nil {
|
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
|
return errors.New("customer not found")
|
|
}
|
|
r.logger.Error("Failed to delete customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
// Update in database
|
|
err = r.ormRepo.Delete(ctx, id)
|
|
if err != nil {
|
|
r.logger.Error("Failed to delete customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Customer deleted successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Search retrieves customers with search and filters
|
|
func (r *customerRepository) Search(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) (*orm.PaginatedResult[Customer], error) {
|
|
filter := bson.M{}
|
|
|
|
if form.Search != nil {
|
|
filter["$text"] = bson.M{"$search": *form.Search}
|
|
}
|
|
|
|
if form.Type != nil {
|
|
filter["type"] = *form.Type
|
|
}
|
|
|
|
if form.Status != nil {
|
|
filter["status"] = *form.Status
|
|
}
|
|
|
|
if form.CompanyID != nil {
|
|
filter["company_id"] = *form.CompanyID
|
|
}
|
|
|
|
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 customers", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"search": form.Search,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// UpdateStatus updates customer status
|
|
func (r *customerRepository) UpdateStatus(ctx context.Context, id string, status CustomerStatus) error {
|
|
// Get customer first
|
|
customer, err := r.GetByID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update status
|
|
customer.Status = status
|
|
customer.SetUpdatedAt(time.Now().Unix())
|
|
|
|
// Update in database
|
|
err = r.ormRepo.Update(ctx, customer)
|
|
if err != nil {
|
|
r.logger.Error("Failed to update customer status", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
"status": status,
|
|
})
|
|
return err
|
|
}
|
|
|
|
r.logger.Info("Customer status updated successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
"status": status,
|
|
})
|
|
|
|
return nil
|
|
}
|