From ddf4d9abc473aa2a259c89d491801a1b7f3aa94e Mon Sep 17 00:00:00 2001 From: "n.nakhostin" Date: Mon, 22 Sep 2025 11:47:31 +0330 Subject: [PATCH] Enhance Customer Service with Notification Response Structures - Introduced CustomerNotificationResponse struct to provide a detailed response format for customer notifications, improving the clarity of notification data. - Updated the Customer service interface to include methods for searching and retrieving customers with notification-specific responses, enhancing the flexibility of customer data retrieval. - Modified the Login method to accept a device token, allowing for better tracking of customer devices during login. - Refactored existing methods to utilize the new notification response structure, ensuring consistency across customer-related API responses. - Enhanced error handling and logging in the service layer for improved traceability during customer searches and logins. --- internal/customer/form.go | 24 +++++++++++ internal/customer/repository.go | 10 ++++- internal/customer/service.go | 72 +++++++++++++++----------------- internal/notification/service.go | 12 +++--- 4 files changed, 72 insertions(+), 46 deletions(-) diff --git a/internal/customer/form.go b/internal/customer/form.go index 14d4460..e4cb328 100644 --- a/internal/customer/form.go +++ b/internal/customer/form.go @@ -80,6 +80,30 @@ func (c *Customer) ToResponse(companies []*CompanySummary) *CustomerResponse { } } +type CustomerNotificationResponse struct { + ID string `json:"id"` + FullName *string `json:"full_name"` + Username string `json:"username"` + Email string `json:"email"` + Type string `json:"type"` + Role string `json:"role"` + Phone *string `json:"phone"` + DeviceToken []string `json:"device_token"` +} + +func (c *Customer) ToNotificationResponse() *CustomerNotificationResponse { + return &CustomerNotificationResponse{ + ID: c.ID.Hex(), + FullName: c.FullName, + Username: c.Username, + Email: c.Email, + Type: string(c.Type), + Role: string(c.Role), + Phone: c.Phone, + DeviceToken: c.DeviceToken, + } +} + // CustomerListResponse represents the response for listing customers type CustomerListResponse struct { Customers []*CustomerResponse `json:"customers"` diff --git a/internal/customer/repository.go b/internal/customer/repository.go index aa93226..7cba42e 100644 --- a/internal/customer/repository.go +++ b/internal/customer/repository.go @@ -3,6 +3,7 @@ package customer import ( "context" "errors" + "slices" "time" "tm/pkg/logger" orm "tm/pkg/mongo" @@ -13,7 +14,7 @@ import ( // Repository defines the interface for customer data operations type Repository interface { - Login(ctx context.Context, id string) error + 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 @@ -90,13 +91,18 @@ func (r *customerRepository) Register(ctx context.Context, customer *Customer) e } // Login updates the last login timestamp -func (r *customerRepository) Login(ctx context.Context, id string) error { +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 = append(customer.DeviceToken, deviceToken) + } + // Update last login timestamp customer.LastLoginAt = &[]int64{time.Now().Unix()}[0] customer.SetUpdatedAt(time.Now().Unix()) diff --git a/internal/customer/service.go b/internal/customer/service.go index 02966b6..d88b509 100644 --- a/internal/customer/service.go +++ b/internal/customer/service.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "math/big" - "slices" "strings" "time" @@ -29,9 +28,10 @@ type Service interface { Register(ctx context.Context, form *CreateCustomerForm) (*CustomerResponse, error) GetByID(ctx context.Context, id string) (*CustomerResponse, error) Search(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) (*CustomerListResponse, error) - GetCustomersByIDs(ctx context.Context, ids []string) (*CustomerListResponse, error) - GetCustomersByRole(ctx context.Context, role string) (*CustomerListResponse, error) - GetCustomersByCompanies(ctx context.Context, companies []string) (*CustomerListResponse, error) + SearchNotification(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) ([]*CustomerNotificationResponse, error) + GetCustomersByIDs(ctx context.Context, ids []string) ([]*CustomerNotificationResponse, error) + GetCustomersByRole(ctx context.Context, role string) ([]*CustomerNotificationResponse, error) + GetCustomersByCompanies(ctx context.Context, companies []string) ([]*CustomerNotificationResponse, error) Update(ctx context.Context, id string, form *UpdateCustomerForm) (*CustomerResponse, error) Delete(ctx context.Context, id string) error UpdateStatus(ctx context.Context, id string, form *UpdateStatusForm) error @@ -216,25 +216,40 @@ func (s *customerService) Search(ctx context.Context, form *SearchCustomersForm, }, nil } +func (s *customerService) SearchNotification(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) ([]*CustomerNotificationResponse, error) { + customers, _, err := s.repository.Search(ctx, form, pagination) + if err != nil { + s.logger.Error("Failed to search customers", map[string]interface{}{ + "error": err.Error(), + }) + return nil, errors.New("failed to search customers") + } + + var customerResponses []*CustomerNotificationResponse + for _, customer := range customers { + customerResponses = append(customerResponses, customer.ToNotificationResponse()) + } + + return customerResponses, nil +} + // GetCustomersByIDs retrieves customers by IDs -func (s *customerService) GetCustomersByIDs(ctx context.Context, ids []string) (*CustomerListResponse, error) { +func (s *customerService) GetCustomersByIDs(ctx context.Context, ids []string) ([]*CustomerNotificationResponse, error) { customers, err := s.repository.GetByIDs(ctx, ids) if err != nil { return nil, err } - var customerResponses []*CustomerResponse + var customerResponses []*CustomerNotificationResponse for _, customer := range customers { - customerResponses = append(customerResponses, customer.ToResponse(nil)) + customerResponses = append(customerResponses, customer.ToNotificationResponse()) } - return &CustomerListResponse{ - Customers: customerResponses, - }, nil + return customerResponses, nil } // GetCustomersByRole retrieves customers by role -func (s *customerService) GetCustomersByRole(ctx context.Context, role string) (*CustomerListResponse, error) { +func (s *customerService) GetCustomersByRole(ctx context.Context, role string) ([]*CustomerNotificationResponse, error) { customers, err := s.repository.GetByRole(ctx, CustomerRole(role)) if err != nil { s.logger.Error("Failed to get customers by role", map[string]interface{}{ @@ -244,18 +259,16 @@ func (s *customerService) GetCustomersByRole(ctx context.Context, role string) ( return nil, err } - var customerResponses []*CustomerResponse + var customerResponses []*CustomerNotificationResponse for _, customer := range customers { - customerResponses = append(customerResponses, customer.ToResponse(nil)) + customerResponses = append(customerResponses, customer.ToNotificationResponse()) } - return &CustomerListResponse{ - Customers: customerResponses, - }, nil + return customerResponses, nil } // GetCustomersByCompanies retrieves customers by companies -func (s *customerService) GetCustomersByCompanies(ctx context.Context, companies []string) (*CustomerListResponse, error) { +func (s *customerService) GetCustomersByCompanies(ctx context.Context, companies []string) ([]*CustomerNotificationResponse, error) { customers, err := s.repository.GetByCompanies(ctx, companies) if err != nil { s.logger.Error("Failed to get customers by companies", map[string]interface{}{ @@ -265,24 +278,12 @@ func (s *customerService) GetCustomersByCompanies(ctx context.Context, companies return nil, err } - var customerResponses []*CustomerResponse + var customerResponses []*CustomerNotificationResponse for _, customer := range customers { - companies := make([]*CompanySummary, 0) - if len(customer.Companies) > 0 { - companies, err = s.loadCompaniesForCustomer(ctx, &customer) - if err != nil { - s.logger.Error("Failed to load companies for customer", map[string]interface{}{ - "error": err.Error(), - "customer_id": customer.ID, - }) - } - } - customerResponses = append(customerResponses, customer.ToResponse(companies)) + customerResponses = append(customerResponses, customer.ToNotificationResponse()) } - return &CustomerListResponse{ - Customers: customerResponses, - }, nil + return customerResponses, nil } // Update updates a customer @@ -619,12 +620,7 @@ func (s *customerService) Login(ctx context.Context, form *LoginForm) (*AuthResp return nil, errors.New("failed to generate authentication tokens") } - // append device token to customer if not exists - if !slices.Contains(customer.DeviceToken, form.DeviceToken) { - customer.DeviceToken = append(customer.DeviceToken, form.DeviceToken) - } - - err = s.repository.Login(ctx, customer.ID.Hex()) + err = s.repository.Login(ctx, customer.ID.Hex(), form.DeviceToken) if err != nil { s.logger.Error("Failed to update customer last login", map[string]interface{}{ "error": err.Error(), diff --git a/internal/notification/service.go b/internal/notification/service.go index 354cdcb..fead16d 100644 --- a/internal/notification/service.go +++ b/internal/notification/service.go @@ -367,7 +367,7 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string, if err != nil { return nil, err } - for _, customer := range customers.Customers { + for _, customer := range customers { recipients = append(recipients, notificationRecipient{ UserID: customer.ID, Email: customer.Email, @@ -379,7 +379,7 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string, if err != nil { return nil, err } - for _, customer := range customers.Customers { + for _, customer := range customers { recipients = append(recipients, notificationRecipient{ UserID: customer.ID, Email: customer.Email, @@ -391,7 +391,7 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string, if err != nil { return nil, err } - for _, customer := range customers.Customers { + for _, customer := range customers { recipients = append(recipients, notificationRecipient{ UserID: customer.ID, Email: customer.Email, @@ -401,11 +401,11 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string, } else { offset := 0 for { - customers, err := s.customerService.Search(ctx, &customer.SearchCustomersForm{}, &response.Pagination{Limit: 100, Offset: offset}) + customers, err := s.customerService.SearchNotification(ctx, &customer.SearchCustomersForm{}, &response.Pagination{Limit: 100, Offset: offset}) if err != nil { return nil, err } - for _, customer := range customers.Customers { + for _, customer := range customers { recipients = append(recipients, notificationRecipient{ UserID: customer.ID, Email: customer.Email, @@ -413,7 +413,7 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string, }) } offset += 100 - if len(customers.Customers) == 0 { + if len(customers) == 0 { break } }