148 lines
4.1 KiB
Go
148 lines
4.1 KiB
Go
// internal/services/category_service.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
|
|
} |