2025-11-17 12:17:44 +07:00

147 lines
4.1 KiB
Go

package services
import (
"errors"
"lost-and-found/internal/models"
"lost-and-found/internal/repositories"
"strings"
"gorm.io/gorm"
)
type CategoryService struct {
categoryRepo *repositories.CategoryRepository
auditLogRepo *repositories.AuditLogRepository
}
func NewCategoryService(db *gorm.DB) *CategoryService {
return &CategoryService{
categoryRepo: repositories.NewCategoryRepository(db),
auditLogRepo: repositories.NewAuditLogRepository(db),
}
}
// CreateCategoryRequest represents category creation data
type CreateCategoryRequest struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
}
// UpdateCategoryRequest represents category update data
type UpdateCategoryRequest struct {
Name string `json:"name"`
Description string `json:"description"`
}
// GetAllCategories gets all categories
func (s *CategoryService) GetAllCategories() ([]models.CategoryResponse, error) {
return s.categoryRepo.GetAllWithItemCount()
}
// GetCategoryByID gets category by ID
func (s *CategoryService) GetCategoryByID(id uint) (*models.Category, error) {
return s.categoryRepo.FindByID(id)
}
// GetCategoryBySlug gets category by slug
func (s *CategoryService) GetCategoryBySlug(slug string) (*models.Category, error) {
return s.categoryRepo.FindBySlug(slug)
}
// CreateCategory creates a new category (admin only)
func (s *CategoryService) CreateCategory(adminID uint, req CreateCategoryRequest, ipAddress, userAgent string) (*models.Category, error) {
// Generate slug from name
slug := s.generateSlug(req.Name)
// Check if slug already exists
existing, _ := s.categoryRepo.FindBySlug(slug)
if existing != nil {
return nil, errors.New("category with similar name already exists")
}
category := &models.Category{
Name: req.Name,
Slug: slug,
Description: req.Description,
}
if err := s.categoryRepo.Create(category); err != nil {
return nil, errors.New("failed to create category")
}
// Log audit
s.auditLogRepo.Log(&adminID, models.ActionCreate, models.EntityCategory, &category.ID,
"Category created: "+category.Name, ipAddress, userAgent)
return category, nil
}
// UpdateCategory updates a category (admin only)
func (s *CategoryService) UpdateCategory(adminID, categoryID uint, req UpdateCategoryRequest, ipAddress, userAgent string) (*models.Category, error) {
category, err := s.categoryRepo.FindByID(categoryID)
if err != nil {
return nil, err
}
// Update fields
if req.Name != "" {
category.Name = req.Name
category.Slug = s.generateSlug(req.Name)
}
if req.Description != "" {
category.Description = req.Description
}
if err := s.categoryRepo.Update(category); err != nil {
return nil, errors.New("failed to update category")
}
// Log audit
s.auditLogRepo.Log(&adminID, models.ActionUpdate, models.EntityCategory, &categoryID,
"Category updated: "+category.Name, ipAddress, userAgent)
return category, nil
}
// DeleteCategory deletes a category (admin only)
func (s *CategoryService) DeleteCategory(adminID, categoryID uint, ipAddress, userAgent string) error {
category, err := s.categoryRepo.FindByID(categoryID)
if err != nil {
return err
}
// Check if category has items
_, count, err := s.categoryRepo.GetCategoryWithItemCount(categoryID)
if err != nil {
return err
}
if count > 0 {
return errors.New("cannot delete category with existing items")
}
if err := s.categoryRepo.Delete(categoryID); err != nil {
return errors.New("failed to delete category")
}
// Log audit
s.auditLogRepo.Log(&adminID, models.ActionDelete, models.EntityCategory, &categoryID,
"Category deleted: "+category.Name, ipAddress, userAgent)
return nil
}
// generateSlug generates URL-friendly slug from name
func (s *CategoryService) generateSlug(name string) string {
slug := strings.ToLower(name)
slug = strings.ReplaceAll(slug, " ", "_")
slug = strings.ReplaceAll(slug, "/", "_")
// Remove special characters
validChars := "abcdefghijklmnopqrstuvwxyz0123456789_"
result := ""
for _, char := range slug {
if strings.ContainsRune(validChars, char) {
result += string(char)
}
}
return result
}