Enhance API documentation and restructure routes for improved clarity

- Updated README.md to include comprehensive Swagger documentation details, highlighting new features and endpoint categories.
- Introduced a new router structure to streamline route registration for admin and public endpoints, enhancing maintainability.
- Updated health check endpoint documentation to reflect comprehensive server status information.
- Enhanced customer and company management routes with improved descriptions and examples in Swagger.
- Refactored customer and user handlers to remove obsolete route registrations, aligning with the new router structure.
- Improved response handling in customer and user services to utilize string IDs consistently.
- Updated validation rules and forms for customer management to support multiple company assignments.
- Enhanced logging practices across services to ensure better traceability and error handling.
This commit is contained in:
n.nakhostin
2025-08-11 18:24:34 +03:30
parent 566fa07574
commit 3e4831c2e7
23 changed files with 3605 additions and 2087 deletions
+181 -35
View File
@@ -28,38 +28,6 @@ func NewHandler(service Service, userHandler *user.Handler, authService authoriz
}
}
// RegisterRoutes registers customer routes
func (h *Handler) RegisterRoutes(e *echo.Echo) {
adminV1 := e.Group("/admin/v1/customers")
adminV1.Use(h.userHandler.AuthMiddleware())
adminV1.POST("", h.CreateCustomer)
adminV1.GET("", h.ListCustomers)
adminV1.GET("/:id", h.GetCustomerByID)
adminV1.PUT("/:id", h.UpdateCustomer)
adminV1.DELETE("/:id", h.DeleteCustomer)
adminV1.PATCH("/:id/status", h.UpdateCustomerStatus)
adminV1.PATCH("/:id/verification", h.UpdateCustomerVerification)
adminV1.POST("/:id/verify", h.VerifyCustomer)
adminV1.POST("/:id/suspend", h.SuspendCustomer)
adminV1.POST("/:id/activate", h.ActivateCustomer)
adminV1.GET("/company/:companyId", h.GetCustomersByCompanyID)
adminV1.GET("/type/:type", h.GetCustomersByType)
adminV1.GET("/status/:status", h.GetCustomersByStatus)
// Mobile-specific endpoints
v1 := e.Group("/api/v1")
authorizationGP := v1.Group("")
authorizationGP.POST("/login", h.Login)
authorizationGP.POST("/refresh-token", h.RefreshToken)
profileGP := v1.Group("")
profileGP.Use(h.AuthMiddleware())
profileGP.GET("/profile", h.GetProfile)
profileGP.DELETE("/logout", h.Logout)
}
// CreateCustomer creates a new customer (Web Panel)
// @Summary Create a new customer
// @Description Create a new customer with comprehensive information including personal details, company information, address, and business details. This endpoint is used by the web panel for full customer registration.
@@ -127,6 +95,33 @@ func (h *Handler) GetCustomerByID(c echo.Context) error {
return response.Success(c, customer.ToResponse(), "Customer retrieved successfully")
}
// GetCustomerByIDWithCompanies retrieves a customer by ID with companies (Web Panel)
// @Summary Get customer by ID with companies
// @Description Retrieve detailed customer information by customer ID including assigned companies
// @Tags Customers-Admin
// @Accept json
// @Produce json
// @Param id path string true "Customer ID"
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Customer retrieved successfully"
// @Failure 400 {object} response.APIResponse "Bad request - Invalid customer ID"
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
// @Failure 500 {object} response.APIResponse "Internal server error"
// @Security BearerAuth
// @Router /admin/v1/customers/{id}/with-companies [get]
func (h *Handler) GetCustomerByIDWithCompanies(c echo.Context) error {
idStr := c.Param("id")
customer, err := h.service.GetCustomerByIDWithCompanies(c.Request().Context(), idStr)
if err != nil {
if err.Error() == "customer not found" {
return response.NotFound(c, "Customer not found")
}
return response.InternalServerError(c, "Failed to get customer")
}
return response.Success(c, customer, "Customer retrieved successfully")
}
// UpdateCustomer updates a customer (Web Panel)
// @Summary Update customer information
// @Description Update customer information including personal details, company information, address, and business details
@@ -247,6 +242,45 @@ func (h *Handler) ListCustomers(c echo.Context) error {
return response.Success(c, result, "Customers retrieved successfully")
}
// ListCustomersWithCompanies lists customers with companies and filters
// @Summary List customers with companies and filters
// @Description Retrieve a paginated list of customers with their assigned companies and advanced filtering options including search, type, status, company, industry, verification status, and compliance status. Supports sorting and pagination.
// @Tags Customers-Admin
// @Accept json
// @Produce json
// @Param search query string false "Search term to filter customers by name, email, or company name"
// @Param type query string false "Filter by customer type" Enums(individual, company, government)
// @Param status query string false "Filter by customer status" Enums(active, inactive, suspended, pending)
// @Param company_id query string false "Filter by company ID" format(uuid)
// @Param industry query string false "Filter by industry"
// @Param is_verified query boolean false "Filter by verification status"
// @Param is_compliant query boolean false "Filter by compliance status"
// @Param language query string false "Filter by preferred language"
// @Param currency query string false "Filter by preferred currency"
// @Param limit query integer false "Number of customers per page (1-100)" minimum(1) maximum(100) default(20)
// @Param offset query integer false "Number of customers to skip for pagination" minimum(0) default(0)
// @Param sort_by query string false "Field to sort by" Enums(email, company_name, created_at, updated_at, status) default(created_at)
// @Param sort_order query string false "Sort order" Enums(asc, desc) default(desc)
// @Success 200 {object} response.APIResponse{data=CustomerListResponse} "Customers with companies retrieved successfully"
// @Failure 400 {object} response.APIResponse "Bad request - Invalid query parameters"
// @Failure 422 {object} response.APIResponse "Validation error - Invalid query parameters"
// @Failure 500 {object} response.APIResponse "Internal server error"
// @Security BearerAuth
// @Router /admin/v1/customers/with-companies [get]
func (h *Handler) ListCustomersWithCompanies(c echo.Context) error {
form, err := response.Parse[ListCustomersForm](c)
if err != nil {
return response.ValidationError(c, "Invalid request data", err.Error())
}
result, err := h.service.ListCustomersWithCompanies(c.Request().Context(), form)
if err != nil {
return response.InternalServerError(c, "Failed to list customers with companies")
}
return response.Success(c, result, "Customers with companies retrieved successfully")
}
// UpdateCustomerStatus updates customer status (Web Panel)
// @Summary Update customer status
// @Description Update customer account status (active, inactive, suspended, pending)
@@ -604,6 +638,88 @@ func (h *Handler) GetCustomersByStatus(c echo.Context) error {
return response.SuccessWithMeta(c, customerResponses, meta, "Customers retrieved successfully")
}
// AssignCompaniesToCustomer assigns companies to a customer (Web Panel)
// @Summary Assign companies to a customer
// @Description Assign one or more companies to a specific customer.
// @Tags Customers-Admin
// @Accept json
// @Produce json
// @Param id path string true "Customer ID"
// @Param companies body AssignCompaniesForm true "List of company IDs to assign"
// @Success 200 {object} response.APIResponse "Companies assigned successfully"
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
// @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"
// @Security BearerAuth
// @Router /admin/v1/customers/{id}/companies/assign [post]
func (h *Handler) AssignCompaniesToCustomer(c echo.Context) error {
idStr := c.Param("id")
form, err := response.Parse[AssignCompaniesForm](c)
if err != nil {
return response.ValidationError(c, "Invalid request data", err.Error())
}
// Get user ID from JWT token context
assignedBy, err := user.GetUserIDFromContext(c)
if err != nil {
return response.Unauthorized(c, "User not authenticated")
}
err = h.service.AssignCompaniesToCustomer(c.Request().Context(), idStr, form.CompanyIDs, &assignedBy)
if err != nil {
if err.Error() == "customer not found" {
return response.NotFound(c, "Customer not found")
}
return response.InternalServerError(c, "Failed to assign companies to customer")
}
return response.Success(c, map[string]interface{}{
"message": "Companies assigned successfully",
}, "Companies assigned successfully")
}
// RemoveCompaniesFromCustomer removes companies from a customer (Web Panel)
// @Summary Remove companies from a customer
// @Description Remove one or more companies from a specific customer.
// @Tags Customers-Admin
// @Accept json
// @Produce json
// @Param id path string true "Customer ID"
// @Param companies body RemoveCompaniesForm true "List of company IDs to remove"
// @Success 200 {object} response.APIResponse "Companies removed successfully"
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
// @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"
// @Security BearerAuth
// @Router /admin/v1/customers/{id}/companies/remove [post]
func (h *Handler) RemoveCompaniesFromCustomer(c echo.Context) error {
idStr := c.Param("id")
form, err := response.Parse[RemoveCompaniesForm](c)
if err != nil {
return response.ValidationError(c, "Invalid request data", err.Error())
}
// Get user ID from JWT token context
removedBy, err := user.GetUserIDFromContext(c)
if err != nil {
return response.Unauthorized(c, "User not authenticated")
}
err = h.service.RemoveCompaniesFromCustomer(c.Request().Context(), idStr, form.CompanyIDs, &removedBy)
if err != nil {
if err.Error() == "customer not found" {
return response.NotFound(c, "Customer not found")
}
return response.InternalServerError(c, "Failed to remove companies from customer")
}
return response.Success(c, map[string]interface{}{
"message": "Companies removed successfully",
}, "Companies removed successfully")
}
// Login handles customer authentication
// @Summary Customer login
// @Description Authenticate customer with username (email) and password. Returns access token, refresh token, and customer information upon successful authentication.
@@ -616,7 +732,7 @@ func (h *Handler) GetCustomersByStatus(c echo.Context) error {
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid credentials or inactive account"
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
// @Failure 500 {object} response.APIResponse "Internal server error"
// @Router /api/v1/login [post]
// @Router /api/v1/profile/login [post]
func (h *Handler) Login(c echo.Context) error {
form, err := response.Parse[LoginForm](c)
if err != nil {
@@ -650,7 +766,7 @@ func (h *Handler) Login(c echo.Context) error {
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired refresh token"
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
// @Failure 500 {object} response.APIResponse "Internal server error"
// @Router /api/v1/refresh-token [post]
// @Router /api/v1/profile/refresh-token [post]
func (h *Handler) RefreshToken(c echo.Context) error {
form, err := response.Parse[RefreshTokenForm](c)
if err != nil {
@@ -680,7 +796,7 @@ func (h *Handler) RefreshToken(c echo.Context) error {
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing token"
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
// @Failure 500 {object} response.APIResponse "Internal server error"
// @Router /api/v1/profile [get]
// @Router /api/v1/ [get]
func (h *Handler) GetProfile(c echo.Context) error {
// Extract customer ID from JWT token context
customerID, err := GetCustomerIDFromContext(c)
@@ -699,6 +815,36 @@ func (h *Handler) GetProfile(c echo.Context) error {
return response.Success(c, customer.ToResponse(), "Profile retrieved successfully")
}
// GetProfileWithCompanies retrieves customer profile with companies (Mobile)
// @Summary Get customer profile with companies
// @Description Retrieve current customer profile information along with their assigned companies.
// @Tags Customers-Authorization
// @Accept json
// @Produce json
// @Security BearerAuth
// @Success 200 {object} response.APIResponse{data=CustomerResponse} "Profile retrieved successfully"
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or missing token"
// @Failure 404 {object} response.APIResponse "Not found - Customer not found"
// @Failure 500 {object} response.APIResponse "Internal server error"
// @Router /api/v1/with-companies [get]
func (h *Handler) GetProfileWithCompanies(c echo.Context) error {
// Extract customer ID from JWT token context
customerID, err := GetCustomerIDFromContext(c)
if err != nil {
return response.Unauthorized(c, "Customer not authenticated")
}
customer, err := h.service.GetProfileWithCompanies(c.Request().Context(), customerID)
if err != nil {
if err.Error() == "customer not found" {
return response.NotFound(c, "Customer not found")
}
return response.InternalServerError(c, "Failed to get customer profile with companies")
}
return response.Success(c, customer, "Profile retrieved successfully")
}
// Logout handles customer logout (Mobile)
// @Summary Customer logout
// @Description Logout customer and invalidate access token