Add Company Category Management Functionality and Update API Documentation
- Introduced a new company category management feature, including CRUD operations for categories in both admin and public API endpoints. - Implemented category entity, service, repository, and handler layers following clean architecture principles. - Added new API routes for searching, retrieving, updating, deleting, and toggling publish status of categories, enhancing the overall functionality of the system. - Updated Swagger and YAML documentation to include detailed descriptions and examples for the new category endpoints, ensuring clarity for API consumers. - Enhanced error handling for duplicate categories and validation, improving the robustness of the category management process. - Updated existing routes to integrate category handling, ensuring a seamless user experience across the application.
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
package company_category
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"tm/pkg/logger"
|
||||
"tm/pkg/response"
|
||||
)
|
||||
|
||||
// Service defines business logic for category operations
|
||||
type Service interface {
|
||||
// Core category operations
|
||||
Create(ctx context.Context, form *CategoryForm) (*CategoryResponse, error)
|
||||
Update(ctx context.Context, id string, form *CategoryForm) (*CategoryResponse, error)
|
||||
GetByID(ctx context.Context, id string) (*CategoryResponse, error)
|
||||
GetByIDs(ctx context.Context, ids []string) ([]*CategoryResponse, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
|
||||
// Category listing and search
|
||||
SearchCategories(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*CategoryListResponse, error)
|
||||
|
||||
// Category management
|
||||
TogglePublish(ctx context.Context, id string) (bool, error)
|
||||
}
|
||||
|
||||
// categoryService implements the Service interface
|
||||
type categoryService struct {
|
||||
repository Repository
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewCategoryService creates a new category service
|
||||
func NewCategoryService(repository Repository, logger logger.Logger) Service {
|
||||
return &categoryService{
|
||||
repository: repository,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates a new category
|
||||
func (s *categoryService) Create(ctx context.Context, form *CategoryForm) (*CategoryResponse, error) {
|
||||
// Check if category name already exists
|
||||
existingCategory, _ := s.repository.GetByName(ctx, form.Name)
|
||||
if existingCategory != nil {
|
||||
return nil, errors.New("category with this name already exists")
|
||||
}
|
||||
|
||||
// Create category
|
||||
category := &Category{
|
||||
Name: form.Name,
|
||||
Description: form.Description,
|
||||
Thumbnail: form.Thumbnail,
|
||||
Published: false, // Default to unpublished
|
||||
}
|
||||
|
||||
// Save to database
|
||||
err := s.repository.Create(ctx, category)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to create category", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"name": form.Name,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.logger.Info("Category created successfully", map[string]interface{}{
|
||||
"category_id": category.ID,
|
||||
"name": category.Name,
|
||||
"published": category.Published,
|
||||
})
|
||||
|
||||
return category.ToResponse(), nil
|
||||
}
|
||||
|
||||
// GetByIDs retrieves categories by IDs
|
||||
func (s *categoryService) GetByIDs(ctx context.Context, ids []string) ([]*CategoryResponse, error) {
|
||||
categories, err := s.repository.GetByIDs(ctx, ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var categoryResponses []*CategoryResponse
|
||||
for _, category := range categories {
|
||||
categoryResponses = append(categoryResponses, category.ToResponse())
|
||||
}
|
||||
|
||||
return categoryResponses, nil
|
||||
}
|
||||
|
||||
// GetByID retrieves a category by ID
|
||||
func (s *categoryService) GetByID(ctx context.Context, id string) (*CategoryResponse, error) {
|
||||
category, err := s.repository.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get category by ID", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"category_id": id,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.logger.Info("Category retrieved successfully", map[string]interface{}{
|
||||
"category_id": category.ID,
|
||||
"name": category.Name,
|
||||
})
|
||||
|
||||
return category.ToResponse(), nil
|
||||
}
|
||||
|
||||
// Update updates a category
|
||||
func (s *categoryService) Update(ctx context.Context, id string, form *CategoryForm) (*CategoryResponse, error) {
|
||||
// Get existing category
|
||||
category, err := s.repository.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get category for update", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"category_id": id,
|
||||
})
|
||||
return nil, errors.New("category not found")
|
||||
}
|
||||
|
||||
// Check if name already exists (if changing name)
|
||||
if form.Name != "" && form.Name != category.Name {
|
||||
existingCategory, _ := s.repository.GetByName(ctx, form.Name)
|
||||
if existingCategory != nil {
|
||||
return nil, errors.New("category with this name already exists")
|
||||
}
|
||||
category.Name = form.Name
|
||||
}
|
||||
|
||||
// Update fields if provided
|
||||
if form.Description != nil {
|
||||
category.Description = form.Description
|
||||
}
|
||||
|
||||
if form.Thumbnail != nil {
|
||||
category.Thumbnail = form.Thumbnail
|
||||
}
|
||||
|
||||
// Update in database
|
||||
err = s.repository.Update(ctx, category)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update category", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"category_id": id,
|
||||
})
|
||||
return nil, errors.New("failed to update category")
|
||||
}
|
||||
|
||||
s.logger.Info("Category updated successfully", map[string]interface{}{
|
||||
"category_id": category.ID,
|
||||
"name": category.Name,
|
||||
})
|
||||
|
||||
return category.ToResponse(), nil
|
||||
}
|
||||
|
||||
// Delete deletes a category
|
||||
func (s *categoryService) Delete(ctx context.Context, id string) error {
|
||||
// Delete category
|
||||
err := s.repository.Delete(ctx, id)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to delete category", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"category_id": id,
|
||||
})
|
||||
return errors.New("failed to delete category")
|
||||
}
|
||||
|
||||
s.logger.Info("Category deleted successfully", map[string]interface{}{
|
||||
"category_id": id,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SearchCategories searches categories with filters
|
||||
func (s *categoryService) SearchCategories(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*CategoryListResponse, error) {
|
||||
// Get categories using search
|
||||
categories, total, err := s.repository.Search(ctx, form, pagination)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to search categories", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, errors.New("failed to search categories")
|
||||
}
|
||||
|
||||
// Convert to responses
|
||||
var categoryResponses []*CategoryResponse
|
||||
for _, category := range categories {
|
||||
categoryResponses = append(categoryResponses, category.ToResponse())
|
||||
}
|
||||
|
||||
return &CategoryListResponse{
|
||||
Categories: categoryResponses,
|
||||
Meta: pagination.Response(total),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TogglePublish toggles category publish status
|
||||
func (s *categoryService) TogglePublish(ctx context.Context, id string) (bool, error) {
|
||||
// Get category to check if exists
|
||||
_, err := s.repository.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return false, errors.New("category not found")
|
||||
}
|
||||
|
||||
// Toggle publish status
|
||||
published, err := s.repository.TogglePublish(ctx, id)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to toggle category publish status", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"category_id": id,
|
||||
})
|
||||
return false, errors.New("failed to toggle category publish status")
|
||||
}
|
||||
|
||||
s.logger.Info("Category publish status updated successfully", map[string]interface{}{
|
||||
"category_id": id,
|
||||
"published": published,
|
||||
})
|
||||
|
||||
return published, nil
|
||||
}
|
||||
Reference in New Issue
Block a user