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.
This commit is contained in:
n.nakhostin
2025-09-22 11:47:31 +03:30
parent 733d726df6
commit ddf4d9abc4
4 changed files with 72 additions and 46 deletions
+24
View File
@@ -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 // CustomerListResponse represents the response for listing customers
type CustomerListResponse struct { type CustomerListResponse struct {
Customers []*CustomerResponse `json:"customers"` Customers []*CustomerResponse `json:"customers"`
+8 -2
View File
@@ -3,6 +3,7 @@ package customer
import ( import (
"context" "context"
"errors" "errors"
"slices"
"time" "time"
"tm/pkg/logger" "tm/pkg/logger"
orm "tm/pkg/mongo" orm "tm/pkg/mongo"
@@ -13,7 +14,7 @@ import (
// Repository defines the interface for customer data operations // Repository defines the interface for customer data operations
type Repository interface { 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 Register(ctx context.Context, customer *Customer) error
Update(ctx context.Context, customer *Customer) error Update(ctx context.Context, customer *Customer) error
UpdateStatus(ctx context.Context, id string, status CustomerStatus) 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 // 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 // Get customer first
customer, err := r.GetByID(ctx, id) customer, err := r.GetByID(ctx, id)
if err != nil { if err != nil {
return err 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 // Update last login timestamp
customer.LastLoginAt = &[]int64{time.Now().Unix()}[0] customer.LastLoginAt = &[]int64{time.Now().Unix()}[0]
customer.SetUpdatedAt(time.Now().Unix()) customer.SetUpdatedAt(time.Now().Unix())
+34 -38
View File
@@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"slices"
"strings" "strings"
"time" "time"
@@ -29,9 +28,10 @@ type Service interface {
Register(ctx context.Context, form *CreateCustomerForm) (*CustomerResponse, error) Register(ctx context.Context, form *CreateCustomerForm) (*CustomerResponse, error)
GetByID(ctx context.Context, id string) (*CustomerResponse, error) GetByID(ctx context.Context, id string) (*CustomerResponse, error)
Search(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) (*CustomerListResponse, error) Search(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) (*CustomerListResponse, error)
GetCustomersByIDs(ctx context.Context, ids []string) (*CustomerListResponse, error) SearchNotification(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) ([]*CustomerNotificationResponse, error)
GetCustomersByRole(ctx context.Context, role string) (*CustomerListResponse, error) GetCustomersByIDs(ctx context.Context, ids []string) ([]*CustomerNotificationResponse, error)
GetCustomersByCompanies(ctx context.Context, companies []string) (*CustomerListResponse, 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) Update(ctx context.Context, id string, form *UpdateCustomerForm) (*CustomerResponse, error)
Delete(ctx context.Context, id string) error Delete(ctx context.Context, id string) error
UpdateStatus(ctx context.Context, id string, form *UpdateStatusForm) error UpdateStatus(ctx context.Context, id string, form *UpdateStatusForm) error
@@ -216,25 +216,40 @@ func (s *customerService) Search(ctx context.Context, form *SearchCustomersForm,
}, nil }, 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 // 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) customers, err := s.repository.GetByIDs(ctx, ids)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var customerResponses []*CustomerResponse var customerResponses []*CustomerNotificationResponse
for _, customer := range customers { for _, customer := range customers {
customerResponses = append(customerResponses, customer.ToResponse(nil)) customerResponses = append(customerResponses, customer.ToNotificationResponse())
} }
return &CustomerListResponse{ return customerResponses, nil
Customers: customerResponses,
}, nil
} }
// GetCustomersByRole retrieves customers by role // 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)) customers, err := s.repository.GetByRole(ctx, CustomerRole(role))
if err != nil { if err != nil {
s.logger.Error("Failed to get customers by role", map[string]interface{}{ 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 return nil, err
} }
var customerResponses []*CustomerResponse var customerResponses []*CustomerNotificationResponse
for _, customer := range customers { for _, customer := range customers {
customerResponses = append(customerResponses, customer.ToResponse(nil)) customerResponses = append(customerResponses, customer.ToNotificationResponse())
} }
return &CustomerListResponse{ return customerResponses, nil
Customers: customerResponses,
}, nil
} }
// GetCustomersByCompanies retrieves customers by companies // 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) customers, err := s.repository.GetByCompanies(ctx, companies)
if err != nil { if err != nil {
s.logger.Error("Failed to get customers by companies", map[string]interface{}{ 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 return nil, err
} }
var customerResponses []*CustomerResponse var customerResponses []*CustomerNotificationResponse
for _, customer := range customers { for _, customer := range customers {
companies := make([]*CompanySummary, 0) customerResponses = append(customerResponses, customer.ToNotificationResponse())
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))
} }
return &CustomerListResponse{ return customerResponses, nil
Customers: customerResponses,
}, nil
} }
// Update updates a customer // 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") return nil, errors.New("failed to generate authentication tokens")
} }
// append device token to customer if not exists err = s.repository.Login(ctx, customer.ID.Hex(), form.DeviceToken)
if !slices.Contains(customer.DeviceToken, form.DeviceToken) {
customer.DeviceToken = append(customer.DeviceToken, form.DeviceToken)
}
err = s.repository.Login(ctx, customer.ID.Hex())
if err != nil { if err != nil {
s.logger.Error("Failed to update customer last login", map[string]interface{}{ s.logger.Error("Failed to update customer last login", map[string]interface{}{
"error": err.Error(), "error": err.Error(),
+6 -6
View File
@@ -367,7 +367,7 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string,
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, customer := range customers.Customers { for _, customer := range customers {
recipients = append(recipients, notificationRecipient{ recipients = append(recipients, notificationRecipient{
UserID: customer.ID, UserID: customer.ID,
Email: customer.Email, Email: customer.Email,
@@ -379,7 +379,7 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string,
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, customer := range customers.Customers { for _, customer := range customers {
recipients = append(recipients, notificationRecipient{ recipients = append(recipients, notificationRecipient{
UserID: customer.ID, UserID: customer.ID,
Email: customer.Email, Email: customer.Email,
@@ -391,7 +391,7 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string,
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, customer := range customers.Customers { for _, customer := range customers {
recipients = append(recipients, notificationRecipient{ recipients = append(recipients, notificationRecipient{
UserID: customer.ID, UserID: customer.ID,
Email: customer.Email, Email: customer.Email,
@@ -401,11 +401,11 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string,
} else { } else {
offset := 0 offset := 0
for { 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 { if err != nil {
return nil, err return nil, err
} }
for _, customer := range customers.Customers { for _, customer := range customers {
recipients = append(recipients, notificationRecipient{ recipients = append(recipients, notificationRecipient{
UserID: customer.ID, UserID: customer.ID,
Email: customer.Email, Email: customer.Email,
@@ -413,7 +413,7 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string,
}) })
} }
offset += 100 offset += 100
if len(customers.Customers) == 0 { if len(customers) == 0 {
break break
} }
} }