Refactor customer domain to use string IDs and integrate MongoDB ORM
- Updated Customer entity to embed MongoDB model and replace uuid.UUID with string for ID fields. - Modified repository methods to accept string IDs instead of uuid.UUID, enhancing compatibility with MongoDB. - Refactored service and handler methods to align with the new ID type, ensuring consistent usage across customer management functionality. - Improved response handling in CustomerResponse to directly use string IDs. - Streamlined index creation in the customer repository using the MongoDB ORM for better maintainability. - Added new forms for customer suspension and updated validation rules. - Enhanced Swagger documentation for customer-related endpoints to reflect changes in ID handling and request structures.
This commit is contained in:
+80
-67
@@ -1,7 +1,7 @@
|
|||||||
package customer
|
package customer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/uuid"
|
"tm/pkg/mongo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CustomerStatus represents customer account status
|
// CustomerStatus represents customer account status
|
||||||
@@ -25,77 +25,105 @@ const (
|
|||||||
|
|
||||||
// Customer represents a customer in the tender management system
|
// Customer represents a customer in the tender management system
|
||||||
type Customer struct {
|
type Customer struct {
|
||||||
ID uuid.UUID `bson:"_id"`
|
mongo.Model
|
||||||
Type CustomerType `bson:"type"`
|
Type CustomerType `bson:"type" json:"type"`
|
||||||
Status CustomerStatus `bson:"status"`
|
Status CustomerStatus `bson:"status" json:"status"`
|
||||||
CompanyID *uuid.UUID `bson:"company_id,omitempty"`
|
CompanyID *string `bson:"company_id,omitempty" json:"company_id,omitempty"`
|
||||||
|
|
||||||
// Individual customer fields
|
// Individual customer fields
|
||||||
FirstName *string `bson:"first_name,omitempty"`
|
FirstName *string `bson:"first_name,omitempty" json:"first_name,omitempty"`
|
||||||
LastName *string `bson:"last_name,omitempty"`
|
LastName *string `bson:"last_name,omitempty" json:"last_name,omitempty"`
|
||||||
FullName *string `bson:"full_name,omitempty"`
|
FullName *string `bson:"full_name,omitempty" json:"full_name,omitempty"`
|
||||||
Username string `bson:"username"` // Username for authentication
|
Username string `bson:"username" json:"username"` // Username for authentication
|
||||||
Email string `bson:"email"`
|
Email string `bson:"email" json:"email"`
|
||||||
Password string `bson:"password"` // Hashed password for authentication
|
Password string `bson:"password" json:"-"` // Hashed password for authentication
|
||||||
Phone *string `bson:"phone,omitempty"`
|
Phone *string `bson:"phone,omitempty" json:"phone,omitempty"`
|
||||||
Mobile *string `bson:"mobile,omitempty"`
|
Mobile *string `bson:"mobile,omitempty" json:"mobile,omitempty"`
|
||||||
|
|
||||||
// Company customer fields
|
// Company customer fields
|
||||||
CompanyName *string `bson:"company_name,omitempty"`
|
CompanyName *string `bson:"company_name,omitempty" json:"company_name,omitempty"`
|
||||||
RegistrationNumber *string `bson:"registration_number,omitempty"`
|
RegistrationNumber *string `bson:"registration_number,omitempty" json:"registration_number,omitempty"`
|
||||||
TaxID *string `bson:"tax_id,omitempty"`
|
TaxID *string `bson:"tax_id,omitempty" json:"tax_id,omitempty"`
|
||||||
Industry *string `bson:"industry,omitempty"`
|
Industry *string `bson:"industry,omitempty" json:"industry,omitempty"`
|
||||||
|
|
||||||
// Address information
|
// Address information
|
||||||
Address *Address `bson:"address,omitempty"`
|
Address *Address `bson:"address,omitempty" json:"address,omitempty"`
|
||||||
|
|
||||||
// Business information
|
// Business information
|
||||||
BusinessType *string `bson:"business_type,omitempty"`
|
BusinessType *string `bson:"business_type,omitempty" json:"business_type,omitempty"`
|
||||||
EmployeeCount *int `bson:"employee_count,omitempty"`
|
EmployeeCount *int `bson:"employee_count,omitempty" json:"employee_count,omitempty"`
|
||||||
AnnualRevenue *float64 `bson:"annual_revenue,omitempty"`
|
AnnualRevenue *float64 `bson:"annual_revenue,omitempty" json:"annual_revenue,omitempty"`
|
||||||
FoundedYear *int `bson:"founded_year,omitempty"`
|
FoundedYear *int `bson:"founded_year,omitempty" json:"founded_year,omitempty"`
|
||||||
|
|
||||||
// Contact person (for company customers)
|
// Contact person (for company customers)
|
||||||
ContactPerson *ContactPerson `bson:"contact_person,omitempty"`
|
ContactPerson *ContactPerson `bson:"contact_person,omitempty" json:"contact_person,omitempty"`
|
||||||
|
|
||||||
// Verification and compliance
|
// Verification and compliance
|
||||||
IsVerified bool `bson:"is_verified"`
|
IsVerified bool `bson:"is_verified" json:"is_verified"`
|
||||||
IsCompliant bool `bson:"is_compliant"`
|
IsCompliant bool `bson:"is_compliant" json:"is_compliant"`
|
||||||
ComplianceNotes *string `bson:"compliance_notes,omitempty"`
|
ComplianceNotes *string `bson:"compliance_notes,omitempty" json:"compliance_notes,omitempty"`
|
||||||
|
|
||||||
// Preferences and settings
|
// Preferences and settings
|
||||||
Language string `bson:"language"` // Default: "en"
|
Language string `bson:"language" json:"language"` // Default: "en"
|
||||||
Currency string `bson:"currency"` // Default: "USD"
|
Currency string `bson:"currency" json:"currency"` // Default: "USD"
|
||||||
Timezone string `bson:"timezone"` // Default: "UTC"
|
Timezone string `bson:"timezone" json:"timezone"` // Default: "UTC"
|
||||||
|
|
||||||
// Audit fields
|
// Audit fields
|
||||||
CreatedAt int64 `bson:"created_at"` // Unix timestamp
|
CreatedBy *string `bson:"created_by,omitempty" json:"created_by,omitempty"`
|
||||||
UpdatedAt int64 `bson:"updated_at"` // Unix timestamp
|
UpdatedBy *string `bson:"updated_by,omitempty" json:"updated_by,omitempty"`
|
||||||
CreatedBy *uuid.UUID `bson:"created_by,omitempty"`
|
}
|
||||||
UpdatedBy *uuid.UUID `bson:"updated_by,omitempty"`
|
|
||||||
|
// SetID sets the customer ID (implements IDSetter interface)
|
||||||
|
func (c *Customer) SetID(id string) {
|
||||||
|
c.ID = id
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID returns the customer ID (implements IDGetter interface)
|
||||||
|
func (c *Customer) GetID() string {
|
||||||
|
return c.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCreatedAt sets the created timestamp (implements Timestampable interface)
|
||||||
|
func (c *Customer) SetCreatedAt(timestamp int64) {
|
||||||
|
c.CreatedAt = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUpdatedAt sets the updated timestamp (implements Timestampable interface)
|
||||||
|
func (c *Customer) SetUpdatedAt(timestamp int64) {
|
||||||
|
c.UpdatedAt = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCreatedAt returns the created timestamp (implements Timestampable interface)
|
||||||
|
func (c *Customer) GetCreatedAt() int64 {
|
||||||
|
return c.CreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUpdatedAt returns the updated timestamp (implements Timestampable interface)
|
||||||
|
func (c *Customer) GetUpdatedAt() int64 {
|
||||||
|
return c.UpdatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address represents a customer's address
|
// Address represents a customer's address
|
||||||
type Address struct {
|
type Address struct {
|
||||||
Street string `bson:"street"`
|
Street string `bson:"street" json:"street"`
|
||||||
City string `bson:"city"`
|
City string `bson:"city" json:"city"`
|
||||||
State string `bson:"state"`
|
State string `bson:"state" json:"state"`
|
||||||
PostalCode string `bson:"postal_code"`
|
PostalCode string `bson:"postal_code" json:"postal_code"`
|
||||||
Country string `bson:"country"`
|
Country string `bson:"country" json:"country"`
|
||||||
AddressType string `bson:"address_type"` // billing, shipping, etc.
|
AddressType string `bson:"address_type" json:"address_type"` // billing, shipping, etc.
|
||||||
IsDefault bool `bson:"is_default"`
|
IsDefault bool `bson:"is_default" json:"is_default"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContactPerson represents a contact person for company customers
|
// ContactPerson represents a contact person for company customers
|
||||||
type ContactPerson struct {
|
type ContactPerson struct {
|
||||||
FirstName string `bson:"first_name"`
|
FirstName string `bson:"first_name" json:"first_name"`
|
||||||
LastName string `bson:"last_name"`
|
LastName string `bson:"last_name" json:"last_name"`
|
||||||
FullName string `bson:"full_name"`
|
FullName string `bson:"full_name" json:"full_name"`
|
||||||
Position string `bson:"position"`
|
Position string `bson:"position" json:"position"`
|
||||||
Email string `bson:"email"`
|
Email string `bson:"email" json:"email"`
|
||||||
Phone string `bson:"phone"`
|
Phone string `bson:"phone" json:"phone"`
|
||||||
Mobile *string `bson:"mobile,omitempty"`
|
Mobile *string `bson:"mobile,omitempty" json:"mobile,omitempty"`
|
||||||
IsPrimary bool `bson:"is_primary"`
|
IsPrimary bool `bson:"is_primary" json:"is_primary"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomerResponse represents the customer data sent in API responses
|
// CustomerResponse represents the customer data sent in API responses
|
||||||
@@ -135,26 +163,11 @@ type CustomerResponse struct {
|
|||||||
|
|
||||||
// ToResponse converts Customer to CustomerResponse
|
// ToResponse converts Customer to CustomerResponse
|
||||||
func (c *Customer) ToResponse() *CustomerResponse {
|
func (c *Customer) ToResponse() *CustomerResponse {
|
||||||
companyID := ""
|
|
||||||
if c.CompanyID != nil {
|
|
||||||
companyID = c.CompanyID.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
createdBy := ""
|
|
||||||
if c.CreatedBy != nil {
|
|
||||||
createdBy = c.CreatedBy.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedBy := ""
|
|
||||||
if c.UpdatedBy != nil {
|
|
||||||
updatedBy = c.UpdatedBy.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return &CustomerResponse{
|
return &CustomerResponse{
|
||||||
ID: c.ID.String(),
|
ID: c.ID,
|
||||||
Type: string(c.Type),
|
Type: string(c.Type),
|
||||||
Status: string(c.Status),
|
Status: string(c.Status),
|
||||||
CompanyID: &companyID,
|
CompanyID: c.CompanyID,
|
||||||
FirstName: c.FirstName,
|
FirstName: c.FirstName,
|
||||||
LastName: c.LastName,
|
LastName: c.LastName,
|
||||||
FullName: c.FullName,
|
FullName: c.FullName,
|
||||||
@@ -180,7 +193,7 @@ func (c *Customer) ToResponse() *CustomerResponse {
|
|||||||
Timezone: c.Timezone,
|
Timezone: c.Timezone,
|
||||||
CreatedAt: c.CreatedAt,
|
CreatedAt: c.CreatedAt,
|
||||||
UpdatedAt: c.UpdatedAt,
|
UpdatedAt: c.UpdatedAt,
|
||||||
CreatedBy: &createdBy,
|
CreatedBy: c.CreatedBy,
|
||||||
UpdatedBy: &updatedBy,
|
UpdatedBy: c.UpdatedBy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,13 +99,18 @@ type UpdateCustomerStatusForm struct {
|
|||||||
Status string `json:"status" valid:"required,in(active|inactive|suspended|pending)"`
|
Status string `json:"status" valid:"required,in(active|inactive|suspended|pending)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCustomerVerificationForm represents the form for updating customer verification
|
// UpdateCustomerVerificationForm represents the form for updating customer verification status
|
||||||
type UpdateCustomerVerificationForm struct {
|
type UpdateCustomerVerificationForm struct {
|
||||||
IsVerified bool `json:"is_verified"`
|
IsVerified bool `json:"is_verified"`
|
||||||
IsCompliant bool `json:"is_compliant"`
|
IsCompliant bool `json:"is_compliant"`
|
||||||
ComplianceNotes *string `json:"compliance_notes,omitempty" valid:"optional,length(0|1000)"`
|
ComplianceNotes *string `json:"compliance_notes,omitempty" valid:"optional,length(0|1000)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SuspendCustomerForm represents the form for suspending a customer
|
||||||
|
type SuspendCustomerForm struct {
|
||||||
|
Reason string `json:"reason" valid:"required,length(10|500)"`
|
||||||
|
}
|
||||||
|
|
||||||
// AddressForm represents the form for customer address
|
// AddressForm represents the form for customer address
|
||||||
type AddressForm struct {
|
type AddressForm struct {
|
||||||
Street string `json:"street" valid:"required,length(5|200)"`
|
Street string `json:"street" valid:"required,length(5|200)"`
|
||||||
|
|||||||
+155
-158
@@ -7,7 +7,6 @@ import (
|
|||||||
"tm/pkg/logger"
|
"tm/pkg/logger"
|
||||||
"tm/pkg/response"
|
"tm/pkg/response"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -81,8 +80,11 @@ func (h *Handler) CreateCustomer(c echo.Context) error {
|
|||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get user ID from JWT token
|
// Get user ID from JWT token context
|
||||||
createdBy := uuid.New() // This should come from JWT token
|
createdBy, err := user.GetUserIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
|
}
|
||||||
|
|
||||||
customer, err := h.service.CreateCustomer(c.Request().Context(), form, &createdBy)
|
customer, err := h.service.CreateCustomer(c.Request().Context(), form, &createdBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -98,32 +100,28 @@ func (h *Handler) CreateCustomer(c echo.Context) error {
|
|||||||
return response.Created(c, customer.ToResponse(), "Customer created successfully")
|
return response.Created(c, customer.ToResponse(), "Customer created successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCustomerByID retrieves a customer by ID
|
// GetCustomerByID retrieves a customer by ID (Web Panel)
|
||||||
// @Summary Get customer by ID
|
// @Summary Get customer by ID
|
||||||
// @Description Retrieve detailed customer information by unique customer ID. Returns comprehensive customer data including personal details, company information, address, verification status, and audit fields.
|
// @Description Retrieve detailed customer information by customer ID
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Customer UUID" format(uuid)
|
// @Param id path string true "Customer ID"
|
||||||
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Customer retrieved successfully"
|
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Customer retrieved successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID format"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer does not exist"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/customers/{id} [get]
|
// @Router /admin/v1/customers/{id} [get]
|
||||||
func (h *Handler) GetCustomerByID(c echo.Context) error {
|
func (h *Handler) GetCustomerByID(c echo.Context) error {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := uuid.Parse(idStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid customer ID", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
customer, err := h.service.GetCustomerByID(c.Request().Context(), id)
|
customer, err := h.service.GetCustomerByID(c.Request().Context(), idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
}
|
}
|
||||||
return response.InternalServerError(c, "Failed to retrieve customer")
|
return response.InternalServerError(c, "Failed to get customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, customer.ToResponse(), "Customer retrieved successfully")
|
return response.Success(c, customer.ToResponse(), "Customer retrieved successfully")
|
||||||
@@ -131,15 +129,15 @@ func (h *Handler) GetCustomerByID(c echo.Context) error {
|
|||||||
|
|
||||||
// UpdateCustomer updates a customer (Web Panel)
|
// UpdateCustomer updates a customer (Web Panel)
|
||||||
// @Summary Update customer information
|
// @Summary Update customer information
|
||||||
// @Description Update existing customer information with comprehensive details. All fields are optional and only provided fields will be updated. This endpoint is used by the web panel for full customer management.
|
// @Description Update customer information including personal details, company information, address, and business details
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Customer UUID" format(uuid)
|
// @Param id path string true "Customer ID"
|
||||||
// @Param customer body UpdateCustomerForm true "Customer update information - all fields are optional"
|
// @Param customer body UpdateCustomerForm true "Updated customer information"
|
||||||
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Customer updated successfully"
|
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Customer updated successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID or input data"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer does not exist"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
// @Failure 409 {object} response.APIResponse "Conflict - Customer with this email/company already exists"
|
// @Failure 409 {object} response.APIResponse "Conflict - Customer with this email/company already exists"
|
||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
@@ -147,20 +145,18 @@ func (h *Handler) GetCustomerByID(c echo.Context) error {
|
|||||||
// @Router /admin/v1/customers/{id} [put]
|
// @Router /admin/v1/customers/{id} [put]
|
||||||
func (h *Handler) UpdateCustomer(c echo.Context) error {
|
func (h *Handler) UpdateCustomer(c echo.Context) error {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := uuid.Parse(idStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid customer ID", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
form, err := response.Parse[UpdateCustomerForm](c)
|
form, err := response.Parse[UpdateCustomerForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get user ID from JWT token
|
// Get user ID from JWT token context
|
||||||
updatedBy := uuid.New() // This should come from JWT token
|
updatedBy, err := user.GetUserIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
|
}
|
||||||
|
|
||||||
customer, err := h.service.UpdateCustomer(c.Request().Context(), id, form, &updatedBy)
|
customer, err := h.service.UpdateCustomer(c.Request().Context(), idStr, form, &updatedBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
@@ -177,30 +173,29 @@ func (h *Handler) UpdateCustomer(c echo.Context) error {
|
|||||||
return response.Success(c, customer.ToResponse(), "Customer updated successfully")
|
return response.Success(c, customer.ToResponse(), "Customer updated successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCustomer deletes a customer (soft delete)
|
// DeleteCustomer deletes a customer (Web Panel)
|
||||||
// @Summary Delete customer
|
// @Summary Delete customer
|
||||||
// @Description Soft delete a customer by setting status to inactive. The customer record is preserved but marked as deleted. This is a reversible operation.
|
// @Description Soft delete a customer by setting status to inactive
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Customer UUID" format(uuid)
|
// @Param id path string true "Customer ID"
|
||||||
// @Success 200 {object} response.APIResponse "Customer deleted successfully"
|
// @Success 200 {object} response.APIResponse "Customer deleted successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID format"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer does not exist"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/customers/{id} [delete]
|
// @Router /admin/v1/customers/{id} [delete]
|
||||||
func (h *Handler) DeleteCustomer(c echo.Context) error {
|
func (h *Handler) DeleteCustomer(c echo.Context) error {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := uuid.Parse(idStr)
|
|
||||||
|
// Get user ID from JWT token context
|
||||||
|
deletedBy, err := user.GetUserIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Invalid customer ID", err.Error())
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get user ID from JWT token
|
err = h.service.DeleteCustomer(c.Request().Context(), idStr, &deletedBy)
|
||||||
deletedBy := uuid.New() // This should come from JWT token
|
|
||||||
|
|
||||||
err = h.service.DeleteCustomer(c.Request().Context(), id, &deletedBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
@@ -208,7 +203,9 @@ func (h *Handler) DeleteCustomer(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to delete customer")
|
return response.InternalServerError(c, "Failed to delete customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, nil, "Customer deleted successfully")
|
return response.Success(c, map[string]interface{}{
|
||||||
|
"message": "Customer deleted successfully",
|
||||||
|
}, "Customer deleted successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCustomers lists customers with filters and pagination
|
// ListCustomers lists customers with filters and pagination
|
||||||
@@ -250,37 +247,35 @@ func (h *Handler) ListCustomers(c echo.Context) error {
|
|||||||
return response.Success(c, result, "Customers retrieved successfully")
|
return response.Success(c, result, "Customers retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCustomerStatus updates customer status
|
// UpdateCustomerStatus updates customer status (Web Panel)
|
||||||
// @Summary Update customer status
|
// @Summary Update customer status
|
||||||
// @Description Update the status of a customer account. Valid statuses include active, inactive, suspended, and pending. This affects the customer's ability to access the system.
|
// @Description Update customer account status (active, inactive, suspended, pending)
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Customer UUID" format(uuid)
|
// @Param id path string true "Customer ID"
|
||||||
// @Param status body UpdateCustomerStatusForm true "Status update information"
|
// @Param status body UpdateCustomerStatusForm true "New customer status"
|
||||||
// @Success 200 {object} response.APIResponse "Customer status updated successfully"
|
// @Success 200 {object} response.APIResponse "Customer status updated successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID or status"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer does not exist"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid status value"
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/customers/{id}/status [patch]
|
// @Router /admin/v1/customers/{id}/status [patch]
|
||||||
func (h *Handler) UpdateCustomerStatus(c echo.Context) error {
|
func (h *Handler) UpdateCustomerStatus(c echo.Context) error {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := uuid.Parse(idStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid customer ID", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
form, err := response.Parse[UpdateCustomerStatusForm](c)
|
form, err := response.Parse[UpdateCustomerStatusForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get user ID from JWT token
|
// Get user ID from JWT token context
|
||||||
updatedBy := uuid.New() // This should come from JWT token
|
updatedBy, err := user.GetUserIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
|
}
|
||||||
|
|
||||||
err = h.service.UpdateCustomerStatus(c.Request().Context(), id, form, &updatedBy)
|
err = h.service.UpdateCustomerStatus(c.Request().Context(), idStr, form, &updatedBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
@@ -288,40 +283,40 @@ func (h *Handler) UpdateCustomerStatus(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to update customer status")
|
return response.InternalServerError(c, "Failed to update customer status")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, nil, "Customer status updated successfully")
|
return response.Success(c, map[string]interface{}{
|
||||||
|
"message": "Customer status updated successfully",
|
||||||
|
}, "Customer status updated successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCustomerVerification updates customer verification status
|
// UpdateCustomerVerification updates customer verification status (Web Panel)
|
||||||
// @Summary Update customer verification and compliance status
|
// @Summary Update customer verification status
|
||||||
// @Description Update the verification and compliance status of a customer. This includes marking customers as verified and compliant, with optional compliance notes for audit purposes.
|
// @Description Update customer verification and compliance status
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Customer UUID" format(uuid)
|
// @Param id path string true "Customer ID"
|
||||||
// @Param verification body UpdateCustomerVerificationForm true "Verification and compliance update information"
|
// @Param verification body UpdateCustomerVerificationForm true "Verification status update"
|
||||||
// @Success 200 {object} response.APIResponse "Customer verification updated successfully"
|
// @Success 200 {object} response.APIResponse "Customer verification updated successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer does not exist"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid verification data"
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/customers/{id}/verification [patch]
|
// @Router /admin/v1/customers/{id}/verification [patch]
|
||||||
func (h *Handler) UpdateCustomerVerification(c echo.Context) error {
|
func (h *Handler) UpdateCustomerVerification(c echo.Context) error {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := uuid.Parse(idStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid customer ID", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
form, err := response.Parse[UpdateCustomerVerificationForm](c)
|
form, err := response.Parse[UpdateCustomerVerificationForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get user ID from JWT token
|
// Get user ID from JWT token context
|
||||||
updatedBy := uuid.New() // This should come from JWT token
|
updatedBy, err := user.GetUserIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
|
}
|
||||||
|
|
||||||
err = h.service.UpdateCustomerVerification(c.Request().Context(), id, form, &updatedBy)
|
err = h.service.UpdateCustomerVerification(c.Request().Context(), idStr, form, &updatedBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
@@ -329,33 +324,34 @@ func (h *Handler) UpdateCustomerVerification(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to update customer verification")
|
return response.InternalServerError(c, "Failed to update customer verification")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, nil, "Customer verification updated successfully")
|
return response.Success(c, map[string]interface{}{
|
||||||
|
"message": "Customer verification updated successfully",
|
||||||
|
}, "Customer verification updated successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyCustomer verifies a customer
|
// VerifyCustomer verifies a customer (Web Panel)
|
||||||
// @Summary Verify customer
|
// @Summary Verify customer
|
||||||
// @Description Mark a customer as verified. This is a simple verification action that sets the customer's verification status to true.
|
// @Description Mark a customer as verified
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Customer UUID" format(uuid)
|
// @Param id path string true "Customer ID"
|
||||||
// @Success 200 {object} response.APIResponse "Customer verified successfully"
|
// @Success 200 {object} response.APIResponse "Customer verified successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer does not exist"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/customers/{id}/verify [post]
|
// @Router /admin/v1/customers/{id}/verify [post]
|
||||||
func (h *Handler) VerifyCustomer(c echo.Context) error {
|
func (h *Handler) VerifyCustomer(c echo.Context) error {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := uuid.Parse(idStr)
|
|
||||||
|
// Get user ID from JWT token context
|
||||||
|
verifiedBy, err := user.GetUserIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Invalid customer ID", err.Error())
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get user ID from JWT token
|
err = h.service.VerifyCustomer(c.Request().Context(), idStr, &verifiedBy)
|
||||||
verifiedBy := uuid.New() // This should come from JWT token
|
|
||||||
|
|
||||||
err = h.service.VerifyCustomer(c.Request().Context(), id, &verifiedBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
@@ -363,44 +359,40 @@ func (h *Handler) VerifyCustomer(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to verify customer")
|
return response.InternalServerError(c, "Failed to verify customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, nil, "Customer verified successfully")
|
return response.Success(c, map[string]interface{}{
|
||||||
|
"message": "Customer verified successfully",
|
||||||
|
}, "Customer verified successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuspendCustomer suspends a customer
|
// SuspendCustomer suspends a customer (Web Panel)
|
||||||
// @Summary Suspend customer account
|
// @Summary Suspend customer
|
||||||
// @Description Suspend a customer account by setting their status to suspended. Suspended customers cannot access the system. A reason for suspension must be provided.
|
// @Description Suspend a customer account with optional reason
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Customer UUID" format(uuid)
|
// @Param id path string true "Customer ID"
|
||||||
// @Param reason body object true "Suspension reason" SchemaExample({"reason": "Violation of terms of service"})
|
// @Param suspension body SuspendCustomerForm true "Suspension information"
|
||||||
// @Success 200 {object} response.APIResponse "Customer suspended successfully"
|
// @Success 200 {object} response.APIResponse "Customer suspended successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID or missing reason"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer does not exist"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/customers/{id}/suspend [post]
|
// @Router /admin/v1/customers/{id}/suspend [post]
|
||||||
func (h *Handler) SuspendCustomer(c echo.Context) error {
|
func (h *Handler) SuspendCustomer(c echo.Context) error {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := uuid.Parse(idStr)
|
form, err := response.Parse[SuspendCustomerForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Invalid customer ID", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestBody map[string]string
|
// Get user ID from JWT token context
|
||||||
if err := c.Bind(&requestBody); err != nil {
|
suspendedBy, err := user.GetUserIDFromContext(c)
|
||||||
return response.BadRequest(c, "Invalid request body", err.Error())
|
if err != nil {
|
||||||
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
reason := requestBody["reason"]
|
err = h.service.SuspendCustomer(c.Request().Context(), idStr, form.Reason, &suspendedBy)
|
||||||
if reason == "" {
|
|
||||||
return response.BadRequest(c, "Suspension reason is required", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Get user ID from JWT token
|
|
||||||
suspendedBy := uuid.New() // This should come from JWT token
|
|
||||||
|
|
||||||
err = h.service.SuspendCustomer(c.Request().Context(), id, reason, &suspendedBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
@@ -408,33 +400,34 @@ func (h *Handler) SuspendCustomer(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to suspend customer")
|
return response.InternalServerError(c, "Failed to suspend customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, nil, "Customer suspended successfully")
|
return response.Success(c, map[string]interface{}{
|
||||||
|
"message": "Customer suspended successfully",
|
||||||
|
}, "Customer suspended successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActivateCustomer activates a customer
|
// ActivateCustomer activates a customer (Web Panel)
|
||||||
// @Summary Activate suspended customer account
|
// @Summary Activate customer
|
||||||
// @Description Reactivate a suspended customer account by setting their status back to active. This reverses the suspension action.
|
// @Description Activate a suspended customer account
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Customer UUID" format(uuid)
|
// @Param id path string true "Customer ID"
|
||||||
// @Success 200 {object} response.APIResponse "Customer activated successfully"
|
// @Success 200 {object} response.APIResponse "Customer activated successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer does not exist"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/customers/{id}/activate [post]
|
// @Router /admin/v1/customers/{id}/activate [post]
|
||||||
func (h *Handler) ActivateCustomer(c echo.Context) error {
|
func (h *Handler) ActivateCustomer(c echo.Context) error {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := uuid.Parse(idStr)
|
|
||||||
|
// Get user ID from JWT token context
|
||||||
|
activatedBy, err := user.GetUserIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Invalid customer ID", err.Error())
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Get user ID from JWT token
|
err = h.service.ActivateCustomer(c.Request().Context(), idStr, &activatedBy)
|
||||||
activatedBy := uuid.New() // This should come from JWT token
|
|
||||||
|
|
||||||
err = h.service.ActivateCustomer(c.Request().Context(), id, &activatedBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
@@ -442,61 +435,61 @@ func (h *Handler) ActivateCustomer(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to activate customer")
|
return response.InternalServerError(c, "Failed to activate customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, nil, "Customer activated successfully")
|
return response.Success(c, map[string]interface{}{
|
||||||
|
"message": "Customer activated successfully",
|
||||||
|
}, "Customer activated successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCustomersByCompanyID retrieves customers by company ID
|
// GetCustomersByCompanyID retrieves customers by company ID (Web Panel)
|
||||||
// @Summary Get customers by company ID
|
// @Summary Get customers by company ID
|
||||||
// @Description Retrieve all customers associated with a specific company. This is useful for company administrators to see all users within their organization.
|
// @Description Retrieve all customers associated with a specific company
|
||||||
// @Tags Customers-Admin
|
// @Tags Customers-Admin
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param companyId path string true "Company UUID" format(uuid)
|
// @Param companyId path string true "Company ID"
|
||||||
// @Param limit query integer false "Number of customers per page (1-100)" minimum(1) maximum(100) default(20)
|
// @Param limit query int false "Number of customers to return (default: 10, max: 100)"
|
||||||
// @Param offset query integer false "Number of customers to skip for pagination" minimum(0) default(0)
|
// @Param offset query int false "Number of customers to skip (default: 0)"
|
||||||
// @Success 200 {object} response.APIResponse{data=[]CustomerResponse,meta=response.Meta} "Customers retrieved successfully"
|
// @Success 200 {object} response.APIResponse{data=[]CustomerResponse} "Customers retrieved successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid company ID format"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid company ID"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/customers/company/{companyId} [get]
|
// @Router /admin/v1/customers/company/{companyId} [get]
|
||||||
func (h *Handler) GetCustomersByCompanyID(c echo.Context) error {
|
func (h *Handler) GetCustomersByCompanyID(c echo.Context) error {
|
||||||
companyIDStr := c.Param("companyId")
|
companyIDStr := c.Param("companyId")
|
||||||
companyID, err := uuid.Parse(companyIDStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid company ID", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
limit := 20
|
// Parse pagination parameters
|
||||||
|
limit := 10 // Default limit
|
||||||
|
offset := 0 // Default offset
|
||||||
|
|
||||||
if limitStr := c.QueryParam("limit"); limitStr != "" {
|
if limitStr := c.QueryParam("limit"); limitStr != "" {
|
||||||
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
|
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
|
||||||
limit = parsedLimit
|
limit = parsedLimit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := 0
|
|
||||||
if offsetStr := c.QueryParam("offset"); offsetStr != "" {
|
if offsetStr := c.QueryParam("offset"); offsetStr != "" {
|
||||||
if parsedOffset, err := strconv.Atoi(offsetStr); err == nil && parsedOffset >= 0 {
|
if parsedOffset, err := strconv.Atoi(offsetStr); err == nil && parsedOffset >= 0 {
|
||||||
offset = parsedOffset
|
offset = parsedOffset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customers, total, err := h.service.GetCustomersByCompanyID(c.Request().Context(), companyID, limit, offset)
|
customers, total, err := h.service.GetCustomersByCompanyID(c.Request().Context(), companyIDStr, limit, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.InternalServerError(c, "Failed to retrieve customers by company")
|
return response.InternalServerError(c, "Failed to get customers by company")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert to responses
|
||||||
var customerResponses []*CustomerResponse
|
var customerResponses []*CustomerResponse
|
||||||
for _, customer := range customers {
|
for _, customer := range customers {
|
||||||
customerResponses = append(customerResponses, customer.ToResponse())
|
customerResponses = append(customerResponses, customer.ToResponse())
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := &response.Meta{
|
return response.Success(c, map[string]interface{}{
|
||||||
Total: int(total),
|
"customers": customerResponses,
|
||||||
Limit: limit,
|
"total": total,
|
||||||
Offset: offset,
|
"limit": limit,
|
||||||
}
|
"offset": offset,
|
||||||
|
}, "Customers retrieved successfully")
|
||||||
return response.SuccessWithMeta(c, customerResponses, meta, "Customers retrieved successfully")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCustomersByType retrieves customers by type
|
// GetCustomersByType retrieves customers by type
|
||||||
@@ -676,66 +669,70 @@ func (h *Handler) RefreshToken(c echo.Context) error {
|
|||||||
return response.Success(c, authResponse, "Token refreshed successfully")
|
return response.Success(c, authResponse, "Token refreshed successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProfile retrieves customer profile information
|
// GetProfile retrieves customer profile information (Mobile)
|
||||||
// @Summary Get customer profile
|
// @Summary Get customer profile
|
||||||
// @Description Retrieve authenticated customer's profile information. This endpoint requires a valid access token and returns the customer's own profile data.
|
// @Description Retrieve current customer profile information
|
||||||
// @Tags Customers-Authorization
|
// @Tags Customers-Mobile
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Profile retrieved successfully"
|
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Profile retrieved successfully"
|
||||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing access token"
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing token"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Customer profile not found"
|
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Router /api/v1/profile [get]
|
// @Router /api/v1/profile [get]
|
||||||
func (h *Handler) GetProfile(c echo.Context) error {
|
func (h *Handler) GetProfile(c echo.Context) error {
|
||||||
// Get customer ID from context (set by AuthMiddleware)
|
// Extract customer ID from JWT token context
|
||||||
customerID, err := GetCustomerIDFromContext(c)
|
customerID, err := GetCustomerIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Unauthorized(c, "Customer ID not found in context")
|
return response.Unauthorized(c, "Customer not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call service
|
|
||||||
customer, err := h.service.GetProfile(c.Request().Context(), customerID)
|
customer, err := h.service.GetProfile(c.Request().Context(), customerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "customer not found" {
|
if err.Error() == "customer not found" {
|
||||||
return response.NotFound(c, "Customer not found")
|
return response.NotFound(c, "Customer not found")
|
||||||
}
|
}
|
||||||
return response.InternalServerError(c, "Failed to retrieve customer profile")
|
return response.InternalServerError(c, "Failed to get customer profile")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, customer.ToResponse(), "Profile retrieved successfully")
|
return response.Success(c, customer.ToResponse(), "Profile retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logout handles customer logout
|
// Logout handles customer logout (Mobile)
|
||||||
// @Summary Customer logout
|
// @Summary Customer logout
|
||||||
// @Description Logout customer and invalidate access token. This ensures the token cannot be used for future requests.
|
// @Description Logout customer and invalidate access token
|
||||||
// @Tags Customers-Authorization
|
// @Tags Customers-Mobile
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Success 200 {object} response.APIResponse "Logout successful"
|
// @Success 200 {object} response.APIResponse "Logout successful"
|
||||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing access token"
|
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing token"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Router /api/v1/logout [delete]
|
// @Router /api/v1/logout [delete]
|
||||||
func (h *Handler) Logout(c echo.Context) error {
|
func (h *Handler) Logout(c echo.Context) error {
|
||||||
// Get customer ID from context (set by AuthMiddleware)
|
// Extract customer ID from JWT token context
|
||||||
customerID, err := GetCustomerIDFromContext(c)
|
customerID, err := GetCustomerIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Unauthorized(c, "Customer ID not found in context")
|
return response.Unauthorized(c, "Customer not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get access token from context
|
// Get access token from context
|
||||||
accessToken, err := GetAccessTokenFromContext(c)
|
accessToken, err := GetAccessTokenFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Unauthorized(c, "Access token not found in context")
|
return response.Unauthorized(c, "Access token not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call service
|
|
||||||
err = h.service.Logout(c.Request().Context(), customerID, accessToken)
|
err = h.service.Logout(c.Request().Context(), customerID, accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.InternalServerError(c, "Failed to logout customer")
|
// Don't return error here as the customer should still be logged out
|
||||||
|
h.logger.Warn("Failed to invalidate access token during logout", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"customer_id": customerID,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, nil, "Logout successful")
|
return response.Success(c, map[string]interface{}{
|
||||||
|
"message": "Logout successful",
|
||||||
|
}, "Logout successful")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"tm/pkg/response"
|
"tm/pkg/response"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,13 +43,7 @@ func (h *Handler) AuthMiddleware() echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract customer information from token
|
// Extract customer information from token
|
||||||
customerID, err := uuid.Parse(validationResult.Claims.UserID)
|
customerID := validationResult.Claims.UserID
|
||||||
if err != nil {
|
|
||||||
h.logger.Error("Failed to parse customer ID from token", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return response.Unauthorized(c, "Invalid token format")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store customer information in context for handlers to use
|
// Store customer information in context for handlers to use
|
||||||
c.Set("customer_id", customerID)
|
c.Set("customer_id", customerID)
|
||||||
@@ -66,10 +59,10 @@ func (h *Handler) AuthMiddleware() echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCustomerIDFromContext extracts customer ID from Echo context
|
// GetCustomerIDFromContext extracts customer ID from Echo context
|
||||||
func GetCustomerIDFromContext(c echo.Context) (uuid.UUID, error) {
|
func GetCustomerIDFromContext(c echo.Context) (string, error) {
|
||||||
customerID, ok := c.Get("customer_id").(uuid.UUID)
|
customerID, ok := c.Get("customer_id").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return uuid.Nil, echo.NewHTTPError(http.StatusUnauthorized, "Customer ID not found in context")
|
return "", echo.NewHTTPError(http.StatusUnauthorized, "Customer ID not found in context")
|
||||||
}
|
}
|
||||||
return customerID, nil
|
return customerID, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+263
-264
@@ -7,162 +7,94 @@ import (
|
|||||||
"tm/pkg/logger"
|
"tm/pkg/logger"
|
||||||
mongopkg "tm/pkg/mongo"
|
mongopkg "tm/pkg/mongo"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repository defines the interface for customer data operations
|
// Repository defines the interface for customer data operations
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
Create(ctx context.Context, customer *Customer) error
|
Create(ctx context.Context, customer *Customer) error
|
||||||
GetByID(ctx context.Context, id uuid.UUID) (*Customer, error)
|
GetByID(ctx context.Context, id string) (*Customer, error)
|
||||||
GetByEmail(ctx context.Context, email string) (*Customer, error)
|
GetByEmail(ctx context.Context, email string) (*Customer, error)
|
||||||
GetByUsername(ctx context.Context, username string) (*Customer, error)
|
GetByUsername(ctx context.Context, username string) (*Customer, error)
|
||||||
GetByCompanyName(ctx context.Context, companyName string) (*Customer, error)
|
GetByCompanyName(ctx context.Context, companyName string) (*Customer, error)
|
||||||
GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Customer, error)
|
GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Customer, error)
|
||||||
GetByTaxID(ctx context.Context, taxID string) (*Customer, error)
|
GetByTaxID(ctx context.Context, taxID string) (*Customer, error)
|
||||||
GetByCompanyID(ctx context.Context, companyID uuid.UUID, limit, offset int) ([]*Customer, error)
|
GetByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*Customer, error)
|
||||||
Update(ctx context.Context, customer *Customer) error
|
Update(ctx context.Context, customer *Customer) error
|
||||||
Delete(ctx context.Context, id uuid.UUID) error
|
Delete(ctx context.Context, id string) error
|
||||||
List(ctx context.Context, limit, offset int) ([]*Customer, error)
|
List(ctx context.Context, limit, offset int) ([]*Customer, error)
|
||||||
Search(ctx context.Context, search string, customerType *string, status *string, companyID *uuid.UUID, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, limit, offset int, sortBy, sortOrder string) ([]*Customer, error)
|
Search(ctx context.Context, search string, customerType *string, status *string, companyID *string, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, limit, offset int, sortBy, sortOrder string) ([]*Customer, error)
|
||||||
CountByCompanyID(ctx context.Context, companyID uuid.UUID) (int64, error)
|
CountByCompanyID(ctx context.Context, companyID string) (int64, error)
|
||||||
CountByType(ctx context.Context, customerType CustomerType) (int64, error)
|
CountByType(ctx context.Context, customerType CustomerType) (int64, error)
|
||||||
CountByStatus(ctx context.Context, status CustomerStatus) (int64, error)
|
CountByStatus(ctx context.Context, status CustomerStatus) (int64, error)
|
||||||
UpdateStatus(ctx context.Context, id uuid.UUID, status CustomerStatus) error
|
UpdateStatus(ctx context.Context, id string, status CustomerStatus) error
|
||||||
UpdateVerification(ctx context.Context, id uuid.UUID, isVerified, isCompliant bool, complianceNotes *string) error
|
UpdateVerification(ctx context.Context, id string, isVerified, isCompliant bool, complianceNotes *string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// customerRepository implements the Repository interface
|
// customerRepository implements the Repository interface using the MongoDB ORM
|
||||||
type customerRepository struct {
|
type customerRepository struct {
|
||||||
collection *mongo.Collection
|
ormRepo mongopkg.Repository[Customer]
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCustomerRepository creates a new customer repository
|
// NewCustomerRepository creates a new customer repository
|
||||||
func NewCustomerRepository(mongoManager *mongopkg.ConnectionManager, logger logger.Logger) Repository {
|
func NewCustomerRepository(mongoManager *mongopkg.ConnectionManager, logger logger.Logger) Repository {
|
||||||
collection := mongoManager.GetCollection("customers")
|
// Create indexes using the ORM's index management
|
||||||
|
indexes := []mongopkg.Index{
|
||||||
|
*mongopkg.CreateUniqueIndex("email_idx", bson.D{{Key: "email", Value: 1}}),
|
||||||
|
*mongopkg.CreateUniqueIndex("username_idx", bson.D{{Key: "username", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("company_name_idx", bson.D{{Key: "company_name", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("tax_id_idx", bson.D{{Key: "tax_id", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("company_id_idx", bson.D{{Key: "company_id", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("type_idx", bson.D{{Key: "type", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("industry_idx", bson.D{{Key: "industry", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("is_verified_idx", bson.D{{Key: "is_verified", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("is_compliant_idx", bson.D{{Key: "is_compliant", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("language_idx", bson.D{{Key: "language", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("currency_idx", bson.D{{Key: "currency", Value: 1}}),
|
||||||
|
*mongopkg.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
|
||||||
|
*mongopkg.CreateTextIndex("search_idx", "first_name", "last_name", "full_name", "company_name", "email", "username"),
|
||||||
|
}
|
||||||
|
|
||||||
// Create indexes
|
// Create indexes
|
||||||
repo := &customerRepository{
|
err := mongoManager.CreateIndexes("customers", indexes)
|
||||||
collection: collection,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
|
|
||||||
repo.createIndexes()
|
|
||||||
return repo
|
|
||||||
}
|
|
||||||
|
|
||||||
// createIndexes creates necessary database indexes
|
|
||||||
func (r *customerRepository) createIndexes() {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// Create indexes
|
|
||||||
indexes := []mongo.IndexModel{
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "email", Value: 1},
|
|
||||||
},
|
|
||||||
Options: options.Index().SetUnique(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "username", Value: 1},
|
|
||||||
},
|
|
||||||
Options: options.Index().SetUnique(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "company_name", Value: 1},
|
|
||||||
},
|
|
||||||
Options: options.Index().SetSparse(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "registration_number", Value: 1},
|
|
||||||
},
|
|
||||||
Options: options.Index().SetSparse(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "tax_id", Value: 1},
|
|
||||||
},
|
|
||||||
Options: options.Index().SetSparse(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "company_id", Value: 1},
|
|
||||||
},
|
|
||||||
Options: options.Index().SetSparse(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "type", Value: 1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "status", Value: 1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "industry", Value: 1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "created_at", Value: -1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "updated_at", Value: -1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := r.collection.Indexes().CreateMany(ctx, indexes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to create customer indexes", map[string]interface{}{
|
logger.Warn("Failed to create customer indexes", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
r.logger.Info("Customer indexes created successfully", map[string]interface{}{})
|
|
||||||
|
// Create ORM repository
|
||||||
|
ormRepo := mongopkg.NewRepository[Customer](mongoManager.GetCollection("customers"), logger)
|
||||||
|
|
||||||
|
return &customerRepository{
|
||||||
|
ormRepo: ormRepo,
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new customer
|
// Create creates a new customer
|
||||||
func (r *customerRepository) Create(ctx context.Context, customer *Customer) error {
|
func (r *customerRepository) Create(ctx context.Context, customer *Customer) error {
|
||||||
// Set timestamps
|
// Set created/updated timestamps using Unix timestamps
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
customer.CreatedAt = now
|
customer.SetCreatedAt(now)
|
||||||
customer.UpdatedAt = now
|
customer.SetUpdatedAt(now)
|
||||||
|
|
||||||
// Set defaults if not provided
|
// Use ORM to create customer
|
||||||
if customer.Language == "" {
|
err := r.ormRepo.Create(ctx, customer)
|
||||||
customer.Language = "en"
|
|
||||||
}
|
|
||||||
if customer.Currency == "" {
|
|
||||||
customer.Currency = "USD"
|
|
||||||
}
|
|
||||||
if customer.Timezone == "" {
|
|
||||||
customer.Timezone = "UTC"
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := r.collection.InsertOne(ctx, customer)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if mongo.IsDuplicateKeyError(err) {
|
r.logger.Error("Failed to create customer", map[string]interface{}{
|
||||||
return errors.New("customer with this email already exists")
|
"error": err.Error(),
|
||||||
}
|
"email": customer.Email,
|
||||||
|
"username": customer.Username,
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
r.logger.Info("Customer created successfully", map[string]interface{}{
|
r.logger.Info("Customer created successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
"type": customer.Type,
|
"type": customer.Type,
|
||||||
})
|
})
|
||||||
@@ -171,107 +103,143 @@ func (r *customerRepository) Create(ctx context.Context, customer *Customer) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetByID retrieves a customer by ID
|
// GetByID retrieves a customer by ID
|
||||||
func (r *customerRepository) GetByID(ctx context.Context, id uuid.UUID) (*Customer, error) {
|
func (r *customerRepository) GetByID(ctx context.Context, id string) (*Customer, error) {
|
||||||
var customer Customer
|
customer, err := r.ormRepo.FindByID(ctx, id)
|
||||||
err := r.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&customer)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == mongo.ErrNoDocuments {
|
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
||||||
return nil, errors.New("customer not found")
|
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 nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByEmail retrieves a customer by email
|
// GetByEmail retrieves a customer by email
|
||||||
func (r *customerRepository) GetByEmail(ctx context.Context, email string) (*Customer, error) {
|
func (r *customerRepository) GetByEmail(ctx context.Context, email string) (*Customer, error) {
|
||||||
var customer Customer
|
filter := bson.M{"email": email}
|
||||||
err := r.collection.FindOne(ctx, bson.M{"email": email}).Decode(&customer)
|
customer, err := r.ormRepo.FindOne(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == mongo.ErrNoDocuments {
|
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
||||||
return nil, errors.New("customer not found")
|
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 nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByUsername retrieves a customer by username
|
// GetByUsername retrieves a customer by username
|
||||||
func (r *customerRepository) GetByUsername(ctx context.Context, username string) (*Customer, error) {
|
func (r *customerRepository) GetByUsername(ctx context.Context, username string) (*Customer, error) {
|
||||||
var customer Customer
|
filter := bson.M{"username": username}
|
||||||
err := r.collection.FindOne(ctx, bson.M{"username": username}).Decode(&customer)
|
customer, err := r.ormRepo.FindOne(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == mongo.ErrNoDocuments {
|
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
||||||
return nil, errors.New("customer not found")
|
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 nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByCompanyName retrieves a customer by company name
|
// GetByCompanyName retrieves a customer by company name
|
||||||
func (r *customerRepository) GetByCompanyName(ctx context.Context, companyName string) (*Customer, error) {
|
func (r *customerRepository) GetByCompanyName(ctx context.Context, companyName string) (*Customer, error) {
|
||||||
var customer Customer
|
filter := bson.M{"company_name": companyName}
|
||||||
err := r.collection.FindOne(ctx, bson.M{"company_name": companyName}).Decode(&customer)
|
customer, err := r.ormRepo.FindOne(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == mongo.ErrNoDocuments {
|
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
||||||
return nil, errors.New("customer not found")
|
return nil, errors.New("customer not found")
|
||||||
}
|
}
|
||||||
|
r.logger.Error("Failed to get customer by company name", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"company_name": companyName,
|
||||||
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByRegistrationNumber retrieves a customer by registration number
|
// GetByRegistrationNumber retrieves a customer by registration number
|
||||||
func (r *customerRepository) GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Customer, error) {
|
func (r *customerRepository) GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Customer, error) {
|
||||||
var customer Customer
|
filter := bson.M{"registration_number": registrationNumber}
|
||||||
err := r.collection.FindOne(ctx, bson.M{"registration_number": registrationNumber}).Decode(&customer)
|
customer, err := r.ormRepo.FindOne(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == mongo.ErrNoDocuments {
|
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
||||||
return nil, errors.New("customer not found")
|
return nil, errors.New("customer not found")
|
||||||
}
|
}
|
||||||
|
r.logger.Error("Failed to get customer by registration number", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"registration_number": registrationNumber,
|
||||||
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByTaxID retrieves a customer by tax ID
|
// GetByTaxID retrieves a customer by tax ID
|
||||||
func (r *customerRepository) GetByTaxID(ctx context.Context, taxID string) (*Customer, error) {
|
func (r *customerRepository) GetByTaxID(ctx context.Context, taxID string) (*Customer, error) {
|
||||||
var customer Customer
|
filter := bson.M{"tax_id": taxID}
|
||||||
err := r.collection.FindOne(ctx, bson.M{"tax_id": taxID}).Decode(&customer)
|
customer, err := r.ormRepo.FindOne(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == mongo.ErrNoDocuments {
|
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
||||||
return nil, errors.New("customer not found")
|
return nil, errors.New("customer not found")
|
||||||
}
|
}
|
||||||
|
r.logger.Error("Failed to get customer by tax ID", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"tax_id": taxID,
|
||||||
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByCompanyID retrieves customers by company ID with pagination
|
// GetByCompanyID retrieves customers by company ID with pagination
|
||||||
func (r *customerRepository) GetByCompanyID(ctx context.Context, companyID uuid.UUID, limit, offset int) ([]*Customer, error) {
|
func (r *customerRepository) GetByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*Customer, error) {
|
||||||
filter := bson.M{"company_id": companyID}
|
// Build pagination
|
||||||
|
pagination := mongopkg.NewPaginationBuilder().
|
||||||
|
Limit(limit).
|
||||||
|
Skip(offset).
|
||||||
|
SortDesc("created_at").
|
||||||
|
Build()
|
||||||
|
|
||||||
opts := options.Find().
|
// Filter by company ID and active status
|
||||||
SetLimit(int64(limit)).
|
filter := bson.M{
|
||||||
SetSkip(int64(offset)).
|
"company_id": companyID,
|
||||||
SetSort(bson.D{{Key: "created_at", Value: -1}})
|
"status": bson.M{"$ne": CustomerStatusInactive},
|
||||||
|
}
|
||||||
|
|
||||||
cursor, err := r.collection.Find(ctx, filter, opts)
|
// Use ORM to find customers
|
||||||
|
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to get customers by company ID", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"company_id": companyID,
|
||||||
|
"limit": limit,
|
||||||
|
"offset": offset,
|
||||||
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close(ctx)
|
|
||||||
|
|
||||||
var customers []*Customer
|
// Convert []Customer to []*Customer
|
||||||
if err = cursor.All(ctx, &customers); err != nil {
|
customers := make([]*Customer, len(result.Items))
|
||||||
return nil, err
|
for i := range result.Items {
|
||||||
|
customers[i] = &result.Items[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
return customers, nil
|
return customers, nil
|
||||||
@@ -279,22 +247,21 @@ func (r *customerRepository) GetByCompanyID(ctx context.Context, companyID uuid.
|
|||||||
|
|
||||||
// Update updates a customer
|
// Update updates a customer
|
||||||
func (r *customerRepository) Update(ctx context.Context, customer *Customer) error {
|
func (r *customerRepository) Update(ctx context.Context, customer *Customer) error {
|
||||||
customer.UpdatedAt = time.Now().Unix()
|
// Set updated timestamp using Unix timestamp
|
||||||
|
customer.SetUpdatedAt(time.Now().Unix())
|
||||||
|
|
||||||
filter := bson.M{"_id": customer.ID}
|
// Use ORM to update customer
|
||||||
update := bson.M{"$set": customer}
|
err := r.ormRepo.Update(ctx, customer)
|
||||||
|
|
||||||
result, err := r.collection.UpdateOne(ctx, filter, update)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to update customer", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"customer_id": customer.ID,
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.MatchedCount == 0 {
|
|
||||||
return errors.New("customer not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Info("Customer updated successfully", map[string]interface{}{
|
r.logger.Info("Customer updated successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -302,26 +269,29 @@ func (r *customerRepository) Update(ctx context.Context, customer *Customer) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes a customer (soft delete by setting status to inactive)
|
// Delete deletes a customer (soft delete by setting status to inactive)
|
||||||
func (r *customerRepository) Delete(ctx context.Context, id uuid.UUID) error {
|
func (r *customerRepository) Delete(ctx context.Context, id string) error {
|
||||||
filter := bson.M{"_id": id}
|
// Get customer first
|
||||||
update := bson.M{
|
customer, err := r.GetByID(ctx, id)
|
||||||
"$set": bson.M{
|
|
||||||
"status": CustomerStatusInactive,
|
|
||||||
"updated_at": time.Now().Unix(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := r.collection.UpdateOne(ctx, filter, update)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.MatchedCount == 0 {
|
// Update status to inactive
|
||||||
return errors.New("customer not found")
|
customer.Status = CustomerStatusInactive
|
||||||
|
customer.SetUpdatedAt(time.Now().Unix())
|
||||||
|
|
||||||
|
// Update in database
|
||||||
|
err = r.ormRepo.Update(ctx, customer)
|
||||||
|
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{}{
|
r.logger.Info("Customer deleted successfully", map[string]interface{}{
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -329,124 +299,125 @@ func (r *customerRepository) Delete(ctx context.Context, id uuid.UUID) error {
|
|||||||
|
|
||||||
// List retrieves customers with pagination
|
// List retrieves customers with pagination
|
||||||
func (r *customerRepository) List(ctx context.Context, limit, offset int) ([]*Customer, error) {
|
func (r *customerRepository) List(ctx context.Context, limit, offset int) ([]*Customer, error) {
|
||||||
opts := options.Find().
|
// Build pagination
|
||||||
SetLimit(int64(limit)).
|
pagination := mongopkg.NewPaginationBuilder().
|
||||||
SetSkip(int64(offset)).
|
Limit(limit).
|
||||||
SetSort(bson.D{{Key: "created_at", Value: -1}})
|
Skip(offset).
|
||||||
|
SortDesc("created_at").
|
||||||
|
Build()
|
||||||
|
|
||||||
cursor, err := r.collection.Find(ctx, bson.M{}, opts)
|
// Only active customers by default
|
||||||
|
filter := bson.M{"status": bson.M{"$ne": CustomerStatusInactive}}
|
||||||
|
|
||||||
|
// Use ORM to find customers
|
||||||
|
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to list customers", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"limit": limit,
|
||||||
|
"offset": offset,
|
||||||
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close(ctx)
|
|
||||||
|
|
||||||
var customers []*Customer
|
// Convert []Customer to []*Customer
|
||||||
if err = cursor.All(ctx, &customers); err != nil {
|
customers := make([]*Customer, len(result.Items))
|
||||||
return nil, err
|
for i := range result.Items {
|
||||||
|
customers[i] = &result.Items[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
return customers, nil
|
return customers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search searches customers with filters
|
// Search retrieves customers with search and filters
|
||||||
func (r *customerRepository) Search(ctx context.Context, search string, customerType *string, status *string, companyID *uuid.UUID, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, limit, offset int, sortBy, sortOrder string) ([]*Customer, error) {
|
func (r *customerRepository) Search(ctx context.Context, search string, customerType *string, status *string, companyID *string, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, limit, offset int, sortBy, sortOrder string) ([]*Customer, error) {
|
||||||
|
// Build filter
|
||||||
filter := bson.M{}
|
filter := bson.M{}
|
||||||
|
|
||||||
// Add search filter
|
|
||||||
if search != "" {
|
if search != "" {
|
||||||
filter["$or"] = []bson.M{
|
filter["$text"] = bson.M{"$search": search}
|
||||||
{"email": primitive.Regex{Pattern: search, Options: "i"}},
|
|
||||||
{"company_name": primitive.Regex{Pattern: search, Options: "i"}},
|
|
||||||
{"first_name": primitive.Regex{Pattern: search, Options: "i"}},
|
|
||||||
{"last_name": primitive.Regex{Pattern: search, Options: "i"}},
|
|
||||||
{"full_name": primitive.Regex{Pattern: search, Options: "i"}},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add type filter
|
|
||||||
if customerType != nil {
|
if customerType != nil {
|
||||||
filter["type"] = *customerType
|
filter["type"] = *customerType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add status filter
|
|
||||||
if status != nil {
|
if status != nil {
|
||||||
filter["status"] = *status
|
filter["status"] = *status
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add company ID filter
|
|
||||||
if companyID != nil {
|
if companyID != nil {
|
||||||
filter["company_id"] = *companyID
|
filter["company_id"] = *companyID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add industry filter
|
|
||||||
if industry != nil {
|
if industry != nil {
|
||||||
filter["industry"] = *industry
|
filter["industry"] = *industry
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add verification filter
|
|
||||||
if isVerified != nil {
|
if isVerified != nil {
|
||||||
filter["is_verified"] = *isVerified
|
filter["is_verified"] = *isVerified
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add compliance filter
|
|
||||||
if isCompliant != nil {
|
if isCompliant != nil {
|
||||||
filter["is_compliant"] = *isCompliant
|
filter["is_compliant"] = *isCompliant
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add language filter
|
|
||||||
if language != nil {
|
if language != nil {
|
||||||
filter["language"] = *language
|
filter["language"] = *language
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add currency filter
|
|
||||||
if currency != nil {
|
if currency != nil {
|
||||||
filter["currency"] = *currency
|
filter["currency"] = *currency
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sort options
|
// Build pagination
|
||||||
sortDirection := 1
|
pagination := mongopkg.NewPaginationBuilder().
|
||||||
|
Limit(limit).
|
||||||
|
Skip(offset)
|
||||||
|
|
||||||
|
// Set sort
|
||||||
|
if sortBy != "" {
|
||||||
if sortOrder == "desc" {
|
if sortOrder == "desc" {
|
||||||
sortDirection = -1
|
pagination.SortDesc(sortBy)
|
||||||
|
} else {
|
||||||
|
pagination.SortAsc(sortBy)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pagination.SortDesc("created_at") // Default sort
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortField string
|
// Use ORM to find customers
|
||||||
switch sortBy {
|
result, err := r.ormRepo.FindAll(ctx, filter, pagination.Build())
|
||||||
case "email":
|
|
||||||
sortField = "email"
|
|
||||||
case "company_name":
|
|
||||||
sortField = "company_name"
|
|
||||||
case "updated_at":
|
|
||||||
sortField = "updated_at"
|
|
||||||
case "status":
|
|
||||||
sortField = "status"
|
|
||||||
default:
|
|
||||||
sortField = "created_at"
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := options.Find().
|
|
||||||
SetLimit(int64(limit)).
|
|
||||||
SetSkip(int64(offset)).
|
|
||||||
SetSort(bson.D{{Key: sortField, Value: sortDirection}})
|
|
||||||
|
|
||||||
cursor, err := r.collection.Find(ctx, filter, opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to search customers", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"search": search,
|
||||||
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close(ctx)
|
|
||||||
|
|
||||||
var customers []*Customer
|
// Convert []Customer to []*Customer
|
||||||
if err = cursor.All(ctx, &customers); err != nil {
|
customers := make([]*Customer, len(result.Items))
|
||||||
return nil, err
|
for i := range result.Items {
|
||||||
|
customers[i] = &result.Items[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
return customers, nil
|
return customers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountByCompanyID counts customers by company ID
|
// CountByCompanyID counts customers by company ID
|
||||||
func (r *customerRepository) CountByCompanyID(ctx context.Context, companyID uuid.UUID) (int64, error) {
|
func (r *customerRepository) CountByCompanyID(ctx context.Context, companyID string) (int64, error) {
|
||||||
filter := bson.M{"company_id": companyID}
|
filter := bson.M{
|
||||||
count, err := r.collection.CountDocuments(ctx, filter)
|
"company_id": companyID,
|
||||||
|
"status": bson.M{"$ne": CustomerStatusInactive},
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := r.ormRepo.Count(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to count customers by company ID", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"company_id": companyID,
|
||||||
|
})
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,9 +426,17 @@ func (r *customerRepository) CountByCompanyID(ctx context.Context, companyID uui
|
|||||||
|
|
||||||
// CountByType counts customers by type
|
// CountByType counts customers by type
|
||||||
func (r *customerRepository) CountByType(ctx context.Context, customerType CustomerType) (int64, error) {
|
func (r *customerRepository) CountByType(ctx context.Context, customerType CustomerType) (int64, error) {
|
||||||
filter := bson.M{"type": customerType}
|
filter := bson.M{
|
||||||
count, err := r.collection.CountDocuments(ctx, filter)
|
"type": customerType,
|
||||||
|
"status": bson.M{"$ne": CustomerStatusInactive},
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := r.ormRepo.Count(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to count customers by type", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"customer_type": customerType,
|
||||||
|
})
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,8 +446,13 @@ func (r *customerRepository) CountByType(ctx context.Context, customerType Custo
|
|||||||
// CountByStatus counts customers by status
|
// CountByStatus counts customers by status
|
||||||
func (r *customerRepository) CountByStatus(ctx context.Context, status CustomerStatus) (int64, error) {
|
func (r *customerRepository) CountByStatus(ctx context.Context, status CustomerStatus) (int64, error) {
|
||||||
filter := bson.M{"status": status}
|
filter := bson.M{"status": status}
|
||||||
count, err := r.collection.CountDocuments(ctx, filter)
|
|
||||||
|
count, err := r.ormRepo.Count(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to count customers by status", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"status": status,
|
||||||
|
})
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,50 +460,65 @@ func (r *customerRepository) CountByStatus(ctx context.Context, status CustomerS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStatus updates customer status
|
// UpdateStatus updates customer status
|
||||||
func (r *customerRepository) UpdateStatus(ctx context.Context, id uuid.UUID, status CustomerStatus) error {
|
func (r *customerRepository) UpdateStatus(ctx context.Context, id string, status CustomerStatus) error {
|
||||||
filter := bson.M{"_id": id}
|
// Get customer first
|
||||||
update := bson.M{
|
customer, err := r.GetByID(ctx, id)
|
||||||
"$set": bson.M{
|
|
||||||
"status": status,
|
|
||||||
"updated_at": time.Now().Unix(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := r.collection.UpdateOne(ctx, filter, update)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.MatchedCount == 0 {
|
// Update status
|
||||||
return errors.New("customer not found")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateVerification updates customer verification status
|
// UpdateVerification updates customer verification status
|
||||||
func (r *customerRepository) UpdateVerification(ctx context.Context, id uuid.UUID, isVerified, isCompliant bool, complianceNotes *string) error {
|
func (r *customerRepository) UpdateVerification(ctx context.Context, id string, isVerified, isCompliant bool, complianceNotes *string) error {
|
||||||
filter := bson.M{"_id": id}
|
// Get customer first
|
||||||
update := bson.M{
|
customer, err := r.GetByID(ctx, id)
|
||||||
"$set": bson.M{
|
|
||||||
"is_verified": isVerified,
|
|
||||||
"is_compliant": isCompliant,
|
|
||||||
"updated_at": time.Now().Unix(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if complianceNotes != nil {
|
|
||||||
update["$set"].(bson.M)["compliance_notes"] = *complianceNotes
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := r.collection.UpdateOne(ctx, filter, update)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.MatchedCount == 0 {
|
// Update verification fields
|
||||||
return errors.New("customer not found")
|
customer.IsVerified = isVerified
|
||||||
|
customer.IsCompliant = isCompliant
|
||||||
|
customer.ComplianceNotes = complianceNotes
|
||||||
|
customer.SetUpdatedAt(time.Now().Unix())
|
||||||
|
|
||||||
|
// Update in database
|
||||||
|
err = r.ormRepo.Update(ctx, customer)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to update customer verification", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"customer_id": id,
|
||||||
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.logger.Info("Customer verification updated successfully", map[string]interface{}{
|
||||||
|
"customer_id": id,
|
||||||
|
"is_verified": isVerified,
|
||||||
|
"is_compliant": isCompliant,
|
||||||
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+92
-113
@@ -16,35 +16,35 @@ import (
|
|||||||
// Service defines business logic for customer operations
|
// Service defines business logic for customer operations
|
||||||
type Service interface {
|
type Service interface {
|
||||||
// Core customer operations
|
// Core customer operations
|
||||||
CreateCustomer(ctx context.Context, form *CreateCustomerForm, createdBy *uuid.UUID) (*Customer, error)
|
CreateCustomer(ctx context.Context, form *CreateCustomerForm, createdBy *string) (*Customer, error)
|
||||||
GetCustomerByID(ctx context.Context, id uuid.UUID) (*Customer, error)
|
GetCustomerByID(ctx context.Context, id string) (*Customer, error)
|
||||||
UpdateCustomer(ctx context.Context, id uuid.UUID, form *UpdateCustomerForm, updatedBy *uuid.UUID) (*Customer, error)
|
UpdateCustomer(ctx context.Context, id string, form *UpdateCustomerForm, updatedBy *string) (*Customer, error)
|
||||||
DeleteCustomer(ctx context.Context, id uuid.UUID, deletedBy *uuid.UUID) error
|
DeleteCustomer(ctx context.Context, id string, deletedBy *string) error
|
||||||
|
|
||||||
// Customer listing and search
|
// Customer listing and search
|
||||||
ListCustomers(ctx context.Context, form *ListCustomersForm) (*CustomerListResponse, error)
|
ListCustomers(ctx context.Context, form *ListCustomersForm) (*CustomerListResponse, error)
|
||||||
GetCustomersByCompanyID(ctx context.Context, companyID uuid.UUID, limit, offset int) ([]*Customer, int64, error)
|
GetCustomersByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*Customer, int64, error)
|
||||||
GetCustomersByType(ctx context.Context, customerType CustomerType, limit, offset int) ([]*Customer, int64, error)
|
GetCustomersByType(ctx context.Context, customerType CustomerType, limit, offset int) ([]*Customer, int64, error)
|
||||||
GetCustomersByStatus(ctx context.Context, status CustomerStatus, limit, offset int) ([]*Customer, int64, error)
|
GetCustomersByStatus(ctx context.Context, status CustomerStatus, limit, offset int) ([]*Customer, int64, error)
|
||||||
|
|
||||||
// Customer management
|
// Customer management
|
||||||
UpdateCustomerStatus(ctx context.Context, id uuid.UUID, form *UpdateCustomerStatusForm, updatedBy *uuid.UUID) error
|
UpdateCustomerStatus(ctx context.Context, id string, form *UpdateCustomerStatusForm, updatedBy *string) error
|
||||||
UpdateCustomerVerification(ctx context.Context, id uuid.UUID, form *UpdateCustomerVerificationForm, updatedBy *uuid.UUID) error
|
UpdateCustomerVerification(ctx context.Context, id string, form *UpdateCustomerVerificationForm, updatedBy *string) error
|
||||||
|
|
||||||
// Mobile-specific operations (simplified)
|
// Mobile-specific operations (simplified)
|
||||||
CreateCustomerMobile(ctx context.Context, form *CreateCustomerMobileForm, createdBy *uuid.UUID) (*Customer, error)
|
CreateCustomerMobile(ctx context.Context, form *CreateCustomerMobileForm, createdBy *string) (*Customer, error)
|
||||||
UpdateCustomerMobile(ctx context.Context, id uuid.UUID, form *UpdateCustomerMobileForm, updatedBy *uuid.UUID) (*Customer, error)
|
UpdateCustomerMobile(ctx context.Context, id string, form *UpdateCustomerMobileForm, updatedBy *string) (*Customer, error)
|
||||||
|
|
||||||
// Business operations
|
// Business operations
|
||||||
VerifyCustomer(ctx context.Context, id uuid.UUID, verifiedBy *uuid.UUID) error
|
VerifyCustomer(ctx context.Context, id string, verifiedBy *string) error
|
||||||
SuspendCustomer(ctx context.Context, id uuid.UUID, reason string, suspendedBy *uuid.UUID) error
|
SuspendCustomer(ctx context.Context, id string, reason string, suspendedBy *string) error
|
||||||
ActivateCustomer(ctx context.Context, id uuid.UUID, activatedBy *uuid.UUID) error
|
ActivateCustomer(ctx context.Context, id string, activatedBy *string) error
|
||||||
|
|
||||||
// Authentication operations
|
// Authentication operations
|
||||||
Login(ctx context.Context, form *LoginForm) (*AuthResponse, error)
|
Login(ctx context.Context, form *LoginForm) (*AuthResponse, error)
|
||||||
RefreshToken(ctx context.Context, refreshToken string) (*AuthResponse, error)
|
RefreshToken(ctx context.Context, refreshToken string) (*AuthResponse, error)
|
||||||
GetProfile(ctx context.Context, customerID uuid.UUID) (*Customer, error)
|
GetProfile(ctx context.Context, customerID string) (*Customer, error)
|
||||||
Logout(ctx context.Context, customerID uuid.UUID, accessToken string) error
|
Logout(ctx context.Context, customerID string, accessToken string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// customerService implements the Service interface
|
// customerService implements the Service interface
|
||||||
@@ -64,7 +64,7 @@ func NewCustomerService(repository Repository, logger logger.Logger, authService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateCustomer creates a new customer
|
// CreateCustomer creates a new customer
|
||||||
func (s *customerService) CreateCustomer(ctx context.Context, form *CreateCustomerForm, createdBy *uuid.UUID) (*Customer, error) {
|
func (s *customerService) CreateCustomer(ctx context.Context, form *CreateCustomerForm, createdBy *string) (*Customer, error) {
|
||||||
// Check if email already exists
|
// Check if email already exists
|
||||||
existingCustomer, _ := s.repository.GetByEmail(ctx, form.Email)
|
existingCustomer, _ := s.repository.GetByEmail(ctx, form.Email)
|
||||||
if existingCustomer != nil {
|
if existingCustomer != nil {
|
||||||
@@ -96,27 +96,31 @@ func (s *customerService) CreateCustomer(ctx context.Context, form *CreateCustom
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse company ID if provided
|
// Parse company ID if provided
|
||||||
var companyID *uuid.UUID
|
var companyID *string
|
||||||
if form.CompanyID != nil {
|
if form.CompanyID != nil {
|
||||||
parsedID, err := uuid.Parse(*form.CompanyID)
|
companyID = form.CompanyID
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("invalid company ID")
|
|
||||||
}
|
}
|
||||||
companyID = &parsedID
|
|
||||||
|
// Hash password
|
||||||
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to hash password", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return nil, errors.New("failed to process password")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create customer
|
// Create customer
|
||||||
customer := &Customer{
|
customer := &Customer{
|
||||||
ID: uuid.New(),
|
|
||||||
Type: CustomerType(form.Type),
|
Type: CustomerType(form.Type),
|
||||||
Status: CustomerStatusActive,
|
Status: CustomerStatusPending,
|
||||||
CompanyID: companyID,
|
CompanyID: companyID,
|
||||||
FirstName: form.FirstName,
|
FirstName: form.FirstName,
|
||||||
LastName: form.LastName,
|
LastName: form.LastName,
|
||||||
FullName: form.FullName,
|
FullName: form.FullName,
|
||||||
Username: form.Username,
|
Username: form.Username,
|
||||||
Email: form.Email,
|
Email: form.Email,
|
||||||
Password: "", // Will be set after hashing
|
Password: string(hashedPassword),
|
||||||
Phone: form.Phone,
|
Phone: form.Phone,
|
||||||
Mobile: form.Mobile,
|
Mobile: form.Mobile,
|
||||||
CompanyName: form.CompanyName,
|
CompanyName: form.CompanyName,
|
||||||
@@ -134,56 +138,43 @@ func (s *customerService) CreateCustomer(ctx context.Context, form *CreateCustom
|
|||||||
Language: s.getDefaultLanguage(form.Language),
|
Language: s.getDefaultLanguage(form.Language),
|
||||||
Currency: s.getDefaultCurrency(form.Currency),
|
Currency: s.getDefaultCurrency(form.Currency),
|
||||||
Timezone: s.getDefaultTimezone(form.Timezone),
|
Timezone: s.getDefaultTimezone(form.Timezone),
|
||||||
CreatedAt: time.Now().Unix(),
|
|
||||||
UpdatedAt: time.Now().Unix(),
|
|
||||||
CreatedBy: createdBy,
|
CreatedBy: createdBy,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash password
|
|
||||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), bcrypt.DefaultCost)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to hash password", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return nil, errors.New("failed to process password")
|
|
||||||
}
|
|
||||||
customer.Password = string(hashedPassword)
|
|
||||||
|
|
||||||
// Save to database
|
// Save to database
|
||||||
err = s.repository.Create(ctx, customer)
|
err = s.repository.Create(ctx, customer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to create customer", map[string]interface{}{
|
s.logger.Error("Failed to create customer", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"email": form.Email,
|
"email": form.Email,
|
||||||
"type": form.Type,
|
"username": form.Username,
|
||||||
"created_by": createdBy.String(),
|
|
||||||
})
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer created successfully", map[string]interface{}{
|
s.logger.Info("Customer created successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
"type": customer.Type,
|
"type": customer.Type,
|
||||||
"created_by": createdBy.String(),
|
"created_by": *createdBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCustomerByID retrieves a customer by ID
|
// GetCustomerByID retrieves a customer by ID
|
||||||
func (s *customerService) GetCustomerByID(ctx context.Context, id uuid.UUID) (*Customer, error) {
|
func (s *customerService) GetCustomerByID(ctx context.Context, id string) (*Customer, error) {
|
||||||
customer, err := s.repository.GetByID(ctx, id)
|
customer, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get customer by ID", map[string]interface{}{
|
s.logger.Error("Failed to get customer by ID", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer retrieved successfully", map[string]interface{}{
|
s.logger.Info("Customer retrieved successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -191,13 +182,13 @@ func (s *customerService) GetCustomerByID(ctx context.Context, id uuid.UUID) (*C
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCustomer updates a customer
|
// UpdateCustomer updates a customer
|
||||||
func (s *customerService) UpdateCustomer(ctx context.Context, id uuid.UUID, form *UpdateCustomerForm, updatedBy *uuid.UUID) (*Customer, error) {
|
func (s *customerService) UpdateCustomer(ctx context.Context, id string, form *UpdateCustomerForm, updatedBy *string) (*Customer, error) {
|
||||||
// Get existing customer
|
// Get existing customer
|
||||||
customer, err := s.repository.GetByID(ctx, id)
|
customer, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get customer for update", map[string]interface{}{
|
s.logger.Error("Failed to get customer for update", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return nil, errors.New("customer not found")
|
return nil, errors.New("customer not found")
|
||||||
}
|
}
|
||||||
@@ -253,11 +244,7 @@ func (s *customerService) UpdateCustomer(ctx context.Context, id uuid.UUID, form
|
|||||||
}
|
}
|
||||||
|
|
||||||
if form.CompanyID != nil {
|
if form.CompanyID != nil {
|
||||||
parsedID, err := uuid.Parse(*form.CompanyID)
|
customer.CompanyID = form.CompanyID
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("invalid company ID")
|
|
||||||
}
|
|
||||||
customer.CompanyID = &parsedID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.FirstName != nil {
|
if form.FirstName != nil {
|
||||||
@@ -329,22 +316,22 @@ func (s *customerService) UpdateCustomer(ctx context.Context, id uuid.UUID, form
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update customer", map[string]interface{}{
|
s.logger.Error("Failed to update customer", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return nil, errors.New("failed to update customer")
|
return nil, errors.New("failed to update customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer updated successfully", map[string]interface{}{
|
s.logger.Info("Customer updated successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
"updated_by": updatedBy.String(),
|
"updated_by": *updatedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCustomer deletes a customer (soft delete)
|
// DeleteCustomer deletes a customer (soft delete)
|
||||||
func (s *customerService) DeleteCustomer(ctx context.Context, id uuid.UUID, deletedBy *uuid.UUID) error {
|
func (s *customerService) DeleteCustomer(ctx context.Context, id string, deletedBy *string) error {
|
||||||
// Get customer to check if exists
|
// Get customer to check if exists
|
||||||
customer, err := s.repository.GetByID(ctx, id)
|
customer, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -359,14 +346,14 @@ func (s *customerService) DeleteCustomer(ctx context.Context, id uuid.UUID, dele
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to delete customer", map[string]interface{}{
|
s.logger.Error("Failed to delete customer", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return errors.New("failed to delete customer")
|
return errors.New("failed to delete customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer deleted successfully", map[string]interface{}{
|
s.logger.Info("Customer deleted successfully", map[string]interface{}{
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
"deleted_by": deletedBy.String(),
|
"deleted_by": deletedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -401,13 +388,9 @@ func (s *customerService) ListCustomers(ctx context.Context, form *ListCustomers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse company ID if provided
|
// Parse company ID if provided
|
||||||
var companyID *uuid.UUID
|
var companyID *string
|
||||||
if form.CompanyID != nil {
|
if form.CompanyID != nil {
|
||||||
parsedID, err := uuid.Parse(*form.CompanyID)
|
companyID = form.CompanyID
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("invalid company ID")
|
|
||||||
}
|
|
||||||
companyID = &parsedID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get customers
|
// Get customers
|
||||||
@@ -439,12 +422,12 @@ func (s *customerService) ListCustomers(ctx context.Context, form *ListCustomers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCustomersByCompanyID retrieves customers by company ID with pagination
|
// GetCustomersByCompanyID retrieves customers by company ID with pagination
|
||||||
func (s *customerService) GetCustomersByCompanyID(ctx context.Context, companyID uuid.UUID, limit, offset int) ([]*Customer, int64, error) {
|
func (s *customerService) GetCustomersByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*Customer, int64, error) {
|
||||||
customers, err := s.repository.GetByCompanyID(ctx, companyID, limit, offset)
|
customers, err := s.repository.GetByCompanyID(ctx, companyID, limit, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get customers by company ID", map[string]interface{}{
|
s.logger.Error("Failed to get customers by company ID", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"company_id": companyID.String(),
|
"company_id": companyID,
|
||||||
})
|
})
|
||||||
return nil, 0, errors.New("failed to get customers by company")
|
return nil, 0, errors.New("failed to get customers by company")
|
||||||
}
|
}
|
||||||
@@ -454,7 +437,7 @@ func (s *customerService) GetCustomersByCompanyID(ctx context.Context, companyID
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to count customers by company ID", map[string]interface{}{
|
s.logger.Error("Failed to count customers by company ID", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"company_id": companyID.String(),
|
"company_id": companyID,
|
||||||
})
|
})
|
||||||
return customers, 0, nil // Return customers even if count fails
|
return customers, 0, nil // Return customers even if count fails
|
||||||
}
|
}
|
||||||
@@ -517,7 +500,7 @@ func (s *customerService) GetCustomersByStatus(ctx context.Context, status Custo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCustomerStatus updates customer status
|
// UpdateCustomerStatus updates customer status
|
||||||
func (s *customerService) UpdateCustomerStatus(ctx context.Context, id uuid.UUID, form *UpdateCustomerStatusForm, updatedBy *uuid.UUID) error {
|
func (s *customerService) UpdateCustomerStatus(ctx context.Context, id string, form *UpdateCustomerStatusForm, updatedBy *string) error {
|
||||||
// Get customer to check if exists
|
// Get customer to check if exists
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
_, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -530,23 +513,23 @@ func (s *customerService) UpdateCustomerStatus(ctx context.Context, id uuid.UUID
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update customer status", map[string]interface{}{
|
s.logger.Error("Failed to update customer status", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
"status": form.Status,
|
"status": form.Status,
|
||||||
})
|
})
|
||||||
return errors.New("failed to update customer status")
|
return errors.New("failed to update customer status")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer status updated successfully", map[string]interface{}{
|
s.logger.Info("Customer status updated successfully", map[string]interface{}{
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
"status": form.Status,
|
"status": form.Status,
|
||||||
"updated_by": updatedBy.String(),
|
"updated_by": updatedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCustomerVerification updates customer verification status
|
// UpdateCustomerVerification updates customer verification status
|
||||||
func (s *customerService) UpdateCustomerVerification(ctx context.Context, id uuid.UUID, form *UpdateCustomerVerificationForm, updatedBy *uuid.UUID) error {
|
func (s *customerService) UpdateCustomerVerification(ctx context.Context, id string, form *UpdateCustomerVerificationForm, updatedBy *string) error {
|
||||||
// Get customer to check if exists
|
// Get customer to check if exists
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
_, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -558,23 +541,23 @@ func (s *customerService) UpdateCustomerVerification(ctx context.Context, id uui
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update customer verification", map[string]interface{}{
|
s.logger.Error("Failed to update customer verification", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return errors.New("failed to update customer verification")
|
return errors.New("failed to update customer verification")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer verification updated successfully", map[string]interface{}{
|
s.logger.Info("Customer verification updated successfully", map[string]interface{}{
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
"is_verified": form.IsVerified,
|
"is_verified": form.IsVerified,
|
||||||
"is_compliant": form.IsCompliant,
|
"is_compliant": form.IsCompliant,
|
||||||
"updated_by": updatedBy.String(),
|
"updated_by": updatedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCustomerMobile creates a new customer via mobile app (simplified)
|
// CreateCustomerMobile creates a new customer via mobile app (simplified)
|
||||||
func (s *customerService) CreateCustomerMobile(ctx context.Context, form *CreateCustomerMobileForm, createdBy *uuid.UUID) (*Customer, error) {
|
func (s *customerService) CreateCustomerMobile(ctx context.Context, form *CreateCustomerMobileForm, createdBy *string) (*Customer, error) {
|
||||||
// Check if email already exists
|
// Check if email already exists
|
||||||
existingCustomer, _ := s.repository.GetByEmail(ctx, form.Email)
|
existingCustomer, _ := s.repository.GetByEmail(ctx, form.Email)
|
||||||
if existingCustomer != nil {
|
if existingCustomer != nil {
|
||||||
@@ -583,7 +566,6 @@ func (s *customerService) CreateCustomerMobile(ctx context.Context, form *Create
|
|||||||
|
|
||||||
// Create customer with mobile form
|
// Create customer with mobile form
|
||||||
customer := &Customer{
|
customer := &Customer{
|
||||||
ID: uuid.New(),
|
|
||||||
Type: CustomerType(form.Type),
|
Type: CustomerType(form.Type),
|
||||||
Status: CustomerStatusActive,
|
Status: CustomerStatusActive,
|
||||||
FirstName: form.FirstName,
|
FirstName: form.FirstName,
|
||||||
@@ -601,8 +583,6 @@ func (s *customerService) CreateCustomerMobile(ctx context.Context, form *Create
|
|||||||
Language: "en",
|
Language: "en",
|
||||||
Currency: "USD",
|
Currency: "USD",
|
||||||
Timezone: "UTC",
|
Timezone: "UTC",
|
||||||
CreatedAt: time.Now().Unix(),
|
|
||||||
UpdatedAt: time.Now().Unix(),
|
|
||||||
CreatedBy: createdBy,
|
CreatedBy: createdBy,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,23 +603,23 @@ func (s *customerService) CreateCustomerMobile(ctx context.Context, form *Create
|
|||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"email": form.Email,
|
"email": form.Email,
|
||||||
"type": form.Type,
|
"type": form.Type,
|
||||||
"created_by": createdBy.String(),
|
"created_by": createdBy,
|
||||||
})
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer created via mobile successfully", map[string]interface{}{
|
s.logger.Info("Customer created via mobile successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
"type": customer.Type,
|
"type": customer.Type,
|
||||||
"created_by": createdBy.String(),
|
"created_by": *createdBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCustomerMobile updates a customer via mobile app (simplified)
|
// UpdateCustomerMobile updates a customer via mobile app (simplified)
|
||||||
func (s *customerService) UpdateCustomerMobile(ctx context.Context, id uuid.UUID, form *UpdateCustomerMobileForm, updatedBy *uuid.UUID) (*Customer, error) {
|
func (s *customerService) UpdateCustomerMobile(ctx context.Context, id string, form *UpdateCustomerMobileForm, updatedBy *string) (*Customer, error) {
|
||||||
// Get existing customer
|
// Get existing customer
|
||||||
customer, err := s.repository.GetByID(ctx, id)
|
customer, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -683,22 +663,22 @@ func (s *customerService) UpdateCustomerMobile(ctx context.Context, id uuid.UUID
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update customer via mobile", map[string]interface{}{
|
s.logger.Error("Failed to update customer via mobile", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return nil, errors.New("failed to update customer")
|
return nil, errors.New("failed to update customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer updated via mobile successfully", map[string]interface{}{
|
s.logger.Info("Customer updated via mobile successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
"updated_by": updatedBy.String(),
|
"updated_by": *updatedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return customer, nil
|
return customer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyCustomer verifies a customer
|
// VerifyCustomer verifies a customer
|
||||||
func (s *customerService) VerifyCustomer(ctx context.Context, id uuid.UUID, verifiedBy *uuid.UUID) error {
|
func (s *customerService) VerifyCustomer(ctx context.Context, id string, verifiedBy *string) error {
|
||||||
// Get customer to check if exists
|
// Get customer to check if exists
|
||||||
customer, err := s.repository.GetByID(ctx, id)
|
customer, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -710,21 +690,21 @@ func (s *customerService) VerifyCustomer(ctx context.Context, id uuid.UUID, veri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to verify customer", map[string]interface{}{
|
s.logger.Error("Failed to verify customer", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return errors.New("failed to verify customer")
|
return errors.New("failed to verify customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer verified successfully", map[string]interface{}{
|
s.logger.Info("Customer verified successfully", map[string]interface{}{
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
"verified_by": verifiedBy.String(),
|
"verified_by": verifiedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuspendCustomer suspends a customer
|
// SuspendCustomer suspends a customer
|
||||||
func (s *customerService) SuspendCustomer(ctx context.Context, id uuid.UUID, reason string, suspendedBy *uuid.UUID) error {
|
func (s *customerService) SuspendCustomer(ctx context.Context, id string, reason string, suspendedBy *string) error {
|
||||||
// Get customer to check if exists
|
// Get customer to check if exists
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
_, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -736,22 +716,22 @@ func (s *customerService) SuspendCustomer(ctx context.Context, id uuid.UUID, rea
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to suspend customer", map[string]interface{}{
|
s.logger.Error("Failed to suspend customer", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return errors.New("failed to suspend customer")
|
return errors.New("failed to suspend customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer suspended successfully", map[string]interface{}{
|
s.logger.Info("Customer suspended successfully", map[string]interface{}{
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
"reason": reason,
|
"reason": reason,
|
||||||
"suspended_by": suspendedBy.String(),
|
"suspended_by": suspendedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActivateCustomer activates a customer
|
// ActivateCustomer activates a customer
|
||||||
func (s *customerService) ActivateCustomer(ctx context.Context, id uuid.UUID, activatedBy *uuid.UUID) error {
|
func (s *customerService) ActivateCustomer(ctx context.Context, id string, activatedBy *string) error {
|
||||||
// Get customer to check if exists
|
// Get customer to check if exists
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
_, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -763,14 +743,14 @@ func (s *customerService) ActivateCustomer(ctx context.Context, id uuid.UUID, ac
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to activate customer", map[string]interface{}{
|
s.logger.Error("Failed to activate customer", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
})
|
})
|
||||||
return errors.New("failed to activate customer")
|
return errors.New("failed to activate customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer activated successfully", map[string]interface{}{
|
s.logger.Info("Customer activated successfully", map[string]interface{}{
|
||||||
"customer_id": id.String(),
|
"customer_id": id,
|
||||||
"activated_by": activatedBy.String(),
|
"activated_by": activatedBy,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -795,7 +775,7 @@ func (s *customerService) Login(ctx context.Context, form *LoginForm) (*AuthResp
|
|||||||
if customer.Status != CustomerStatusActive {
|
if customer.Status != CustomerStatusActive {
|
||||||
s.logger.Warn("Customer login failed - account not active", map[string]interface{}{
|
s.logger.Warn("Customer login failed - account not active", map[string]interface{}{
|
||||||
"username": form.Username,
|
"username": form.Username,
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"status": string(customer.Status),
|
"status": string(customer.Status),
|
||||||
})
|
})
|
||||||
return nil, errors.New("account is not active")
|
return nil, errors.New("account is not active")
|
||||||
@@ -805,7 +785,7 @@ func (s *customerService) Login(ctx context.Context, form *LoginForm) (*AuthResp
|
|||||||
err = bcrypt.CompareHashAndPassword([]byte(customer.Password), []byte(form.Password))
|
err = bcrypt.CompareHashAndPassword([]byte(customer.Password), []byte(form.Password))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warn("Customer login failed - wrong password", map[string]interface{}{
|
s.logger.Warn("Customer login failed - wrong password", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
})
|
})
|
||||||
return nil, errors.New("invalid credentials")
|
return nil, errors.New("invalid credentials")
|
||||||
@@ -814,11 +794,11 @@ func (s *customerService) Login(ctx context.Context, form *LoginForm) (*AuthResp
|
|||||||
// Generate JWT tokens using authorization service
|
// Generate JWT tokens using authorization service
|
||||||
companyID := ""
|
companyID := ""
|
||||||
if customer.CompanyID != nil {
|
if customer.CompanyID != nil {
|
||||||
companyID = customer.CompanyID.String()
|
companyID = *customer.CompanyID
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenPair, err := s.authService.GenerateTokenPair(
|
tokenPair, err := s.authService.GenerateTokenPair(
|
||||||
customer.ID.String(),
|
customer.ID,
|
||||||
customer.Email,
|
customer.Email,
|
||||||
"customer", // Role for customers
|
"customer", // Role for customers
|
||||||
companyID,
|
companyID,
|
||||||
@@ -826,14 +806,14 @@ func (s *customerService) Login(ctx context.Context, form *LoginForm) (*AuthResp
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to generate JWT tokens", map[string]interface{}{
|
s.logger.Error("Failed to generate JWT tokens", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
})
|
})
|
||||||
return nil, errors.New("failed to generate authentication tokens")
|
return nil, errors.New("failed to generate authentication tokens")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer login successful", map[string]interface{}{
|
s.logger.Info("Customer login successful", map[string]interface{}{
|
||||||
"username": form.Username,
|
"username": form.Username,
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"customer_type": string(customer.Type),
|
"customer_type": string(customer.Type),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -885,7 +865,7 @@ func (s *customerService) RefreshToken(ctx context.Context, refreshToken string)
|
|||||||
return nil, errors.New("invalid token format")
|
return nil, errors.New("invalid token format")
|
||||||
}
|
}
|
||||||
|
|
||||||
customer, err := s.repository.GetByID(ctx, customerID)
|
customer, err := s.repository.GetByID(ctx, customerID.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get customer for token refresh", map[string]interface{}{
|
s.logger.Error("Failed to get customer for token refresh", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
@@ -900,8 +880,7 @@ func (s *customerService) RefreshToken(ctx context.Context, refreshToken string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Access token refreshed successfully", map[string]interface{}{
|
s.logger.Info("Access token refreshed successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID.String(),
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
|
||||||
"customer_type": string(customer.Type),
|
"customer_type": string(customer.Type),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -914,22 +893,22 @@ func (s *customerService) RefreshToken(ctx context.Context, refreshToken string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetProfile retrieves customer profile information
|
// GetProfile retrieves customer profile information
|
||||||
func (s *customerService) GetProfile(ctx context.Context, customerID uuid.UUID) (*Customer, error) {
|
func (s *customerService) GetProfile(ctx context.Context, customerID string) (*Customer, error) {
|
||||||
s.logger.Info("Getting customer profile", map[string]interface{}{
|
s.logger.Info("Getting customer profile", map[string]interface{}{
|
||||||
"customer_id": customerID.String(),
|
"customer_id": customerID,
|
||||||
})
|
})
|
||||||
|
|
||||||
customer, err := s.repository.GetByID(ctx, customerID)
|
customer, err := s.repository.GetByID(ctx, customerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get customer profile", map[string]interface{}{
|
s.logger.Error("Failed to get customer profile", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": customerID.String(),
|
"customer_id": customerID,
|
||||||
})
|
})
|
||||||
return nil, errors.New("customer not found")
|
return nil, errors.New("customer not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer profile retrieved successfully", map[string]interface{}{
|
s.logger.Info("Customer profile retrieved successfully", map[string]interface{}{
|
||||||
"customer_id": customerID.String(),
|
"customer_id": customerID,
|
||||||
"customer_type": string(customer.Type),
|
"customer_type": string(customer.Type),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -937,9 +916,9 @@ func (s *customerService) GetProfile(ctx context.Context, customerID uuid.UUID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Logout handles customer logout
|
// Logout handles customer logout
|
||||||
func (s *customerService) Logout(ctx context.Context, customerID uuid.UUID, accessToken string) error {
|
func (s *customerService) Logout(ctx context.Context, customerID string, accessToken string) error {
|
||||||
s.logger.Info("Customer logout", map[string]interface{}{
|
s.logger.Info("Customer logout", map[string]interface{}{
|
||||||
"customer_id": customerID.String(),
|
"customer_id": customerID,
|
||||||
"access_token": accessToken[:10] + "...", // Log only first 10 chars for security
|
"access_token": accessToken[:10] + "...", // Log only first 10 chars for security
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -948,13 +927,13 @@ func (s *customerService) Logout(ctx context.Context, customerID uuid.UUID, acce
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to invalidate access token", map[string]interface{}{
|
s.logger.Error("Failed to invalidate access token", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": customerID.String(),
|
"customer_id": customerID,
|
||||||
})
|
})
|
||||||
// Don't return error here as the customer should still be logged out
|
// Don't return error here as the customer should still be logged out
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer logout successful", map[string]interface{}{
|
s.logger.Info("Customer logout successful", map[string]interface{}{
|
||||||
"customer_id": customerID.String(),
|
"customer_id": customerID,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user