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.
383 lines
11 KiB
Go
383 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) ([]Customer, int64, 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", "full_name", "email", "username", "phone"),
|
|
}
|
|
|
|
// 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) {
|
|
customers, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": ids}}, 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) {
|
|
customers, err := r.ormRepo.FindAll(ctx, bson.M{"companies": bson.M{"$in": companies}}, 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) ([]Customer, int64, error) {
|
|
// Build filter
|
|
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
|
|
}
|
|
|
|
// Build pagination
|
|
paginationBuilder := orm.NewPaginationBuilder().
|
|
Limit(pagination.Limit).
|
|
Skip(pagination.Offset).
|
|
SortBy(pagination.SortBy, pagination.SortOrder)
|
|
|
|
// Use ORM to find customers
|
|
result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder.Build())
|
|
if err != nil {
|
|
r.logger.Error("Failed to search customers", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"search": form.Search,
|
|
})
|
|
return nil, 0, err
|
|
}
|
|
|
|
return result.Items, result.TotalCount, 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
|
|
}
|