233 lines
6.2 KiB
Go
233 lines
6.2 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
|
||
"photography-backend/internal/model/entity"
|
||
"photography-backend/internal/repository/interfaces"
|
||
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
type CategoryService struct {
|
||
categoryRepo interfaces.CategoryRepository
|
||
logger *zap.Logger
|
||
}
|
||
|
||
func NewCategoryService(categoryRepo interfaces.CategoryRepository, logger *zap.Logger) *CategoryService {
|
||
return &CategoryService{
|
||
categoryRepo: categoryRepo,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// GetCategories 获取分类列表
|
||
func (s *CategoryService) GetCategories(ctx context.Context, parentID *uint) ([]*entity.Category, error) {
|
||
categories, err := s.categoryRepo.List(ctx, parentID)
|
||
if err != nil {
|
||
s.logger.Error("Failed to get categories", zap.Error(err))
|
||
return nil, err
|
||
}
|
||
|
||
return categories, nil
|
||
}
|
||
|
||
// GetCategoryTree 获取分类树
|
||
func (s *CategoryService) GetCategoryTree(ctx context.Context) ([]*entity.CategoryTree, error) {
|
||
tree, err := s.categoryRepo.GetTree(ctx)
|
||
if err != nil {
|
||
s.logger.Error("Failed to get category tree", zap.Error(err))
|
||
return nil, err
|
||
}
|
||
|
||
return tree, nil
|
||
}
|
||
|
||
// GetCategoryByID 根据ID获取分类
|
||
func (s *CategoryService) GetCategoryByID(ctx context.Context, id uint) (*entity.Category, error) {
|
||
category, err := s.categoryRepo.GetByID(ctx, id)
|
||
if err != nil {
|
||
s.logger.Error("Failed to get category by ID", zap.Error(err), zap.Uint("id", id))
|
||
return nil, err
|
||
}
|
||
|
||
return category, nil
|
||
}
|
||
|
||
// GetCategoryBySlug 根据slug获取分类
|
||
func (s *CategoryService) GetCategoryBySlug(ctx context.Context, slug string) (*entity.Category, error) {
|
||
category, err := s.categoryRepo.GetBySlug(ctx, slug)
|
||
if err != nil {
|
||
s.logger.Error("Failed to get category by slug", zap.Error(err), zap.String("slug", slug))
|
||
return nil, err
|
||
}
|
||
|
||
return category, nil
|
||
}
|
||
|
||
// CreateCategory 创建分类
|
||
func (s *CategoryService) CreateCategory(ctx context.Context, req *entity.CreateCategoryRequest) (*entity.Category, error) {
|
||
// 验证slug唯一性
|
||
if err := s.categoryRepo.ValidateSlugUnique(ctx, req.Slug, 0); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 验证父分类存在性
|
||
if req.ParentID != nil {
|
||
if err := s.categoryRepo.ValidateParentCategory(ctx, 0, *req.ParentID); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
// 获取排序顺序
|
||
sortOrder, err := s.categoryRepo.GetNextSortOrder(ctx, req.ParentID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
category := &entity.Category{
|
||
Name: req.Name,
|
||
Slug: req.Slug,
|
||
Description: req.Description,
|
||
ParentID: req.ParentID,
|
||
SortOrder: sortOrder,
|
||
IsActive: true,
|
||
}
|
||
|
||
if err := s.categoryRepo.Create(ctx, category); err != nil {
|
||
s.logger.Error("Failed to create category", zap.Error(err))
|
||
return nil, err
|
||
}
|
||
|
||
s.logger.Info("Category created successfully", zap.Uint("id", category.ID))
|
||
return category, nil
|
||
}
|
||
|
||
// UpdateCategory 更新分类
|
||
func (s *CategoryService) UpdateCategory(ctx context.Context, id uint, req *entity.UpdateCategoryRequest) (*entity.Category, error) {
|
||
// 检查分类是否存在
|
||
category, err := s.categoryRepo.GetByID(ctx, id)
|
||
if err != nil {
|
||
s.logger.Error("Failed to get category", zap.Error(err), zap.Uint("id", id))
|
||
return nil, err
|
||
}
|
||
|
||
// 验证slug唯一性
|
||
if req.Slug != nil && *req.Slug != category.Slug {
|
||
if err := s.categoryRepo.ValidateSlugUnique(ctx, *req.Slug, id); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
// 验证父分类(防止循环引用)
|
||
if req.ParentID != nil {
|
||
// 检查是否有变更
|
||
if (category.ParentID == nil && *req.ParentID != 0) || (category.ParentID != nil && *req.ParentID != *category.ParentID) {
|
||
if err := s.categoryRepo.ValidateParentCategory(ctx, id, *req.ParentID); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新字段
|
||
if req.Name != nil {
|
||
category.Name = *req.Name
|
||
}
|
||
if req.Slug != nil {
|
||
category.Slug = *req.Slug
|
||
}
|
||
if req.Description != nil {
|
||
category.Description = *req.Description
|
||
}
|
||
if req.ParentID != nil {
|
||
if *req.ParentID == 0 {
|
||
category.ParentID = nil
|
||
} else {
|
||
category.ParentID = req.ParentID
|
||
}
|
||
}
|
||
if req.SortOrder != nil {
|
||
category.SortOrder = *req.SortOrder
|
||
}
|
||
if req.IsActive != nil {
|
||
category.IsActive = *req.IsActive
|
||
}
|
||
|
||
// 保存更新
|
||
if err := s.categoryRepo.Update(ctx, category); err != nil {
|
||
s.logger.Error("Failed to update category", zap.Error(err))
|
||
return nil, err
|
||
}
|
||
|
||
s.logger.Info("Category updated successfully", zap.Uint("id", id))
|
||
return category, nil
|
||
}
|
||
|
||
// DeleteCategory 删除分类
|
||
func (s *CategoryService) DeleteCategory(ctx context.Context, id uint) error {
|
||
// 检查分类是否存在
|
||
_, err := s.categoryRepo.GetByID(ctx, id)
|
||
if err != nil {
|
||
s.logger.Error("Failed to get category", zap.Error(err), zap.Uint("id", id))
|
||
return err
|
||
}
|
||
|
||
// 检查是否有子分类
|
||
children, err := s.categoryRepo.GetChildren(ctx, id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if len(children) > 0 {
|
||
return errors.New("cannot delete category with subcategories")
|
||
}
|
||
|
||
// 直接删除分类,在Repository层检查照片关联
|
||
if err := s.categoryRepo.Delete(ctx, id); err != nil {
|
||
s.logger.Error("Failed to delete category", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
s.logger.Info("Category deleted successfully", zap.Uint("id", id))
|
||
return nil
|
||
}
|
||
|
||
// ReorderCategories 重新排序分类
|
||
func (s *CategoryService) ReorderCategories(ctx context.Context, parentID *uint, categoryIDs []uint) error {
|
||
if len(categoryIDs) == 0 {
|
||
return nil
|
||
}
|
||
|
||
// 重新排序分类
|
||
if err := s.categoryRepo.Reorder(ctx, parentID, categoryIDs); err != nil {
|
||
s.logger.Error("Failed to reorder categories", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
s.logger.Info("Categories reordered successfully", zap.Int("count", len(categoryIDs)))
|
||
return nil
|
||
}
|
||
|
||
// GetCategoryStats 获取分类统计信息
|
||
func (s *CategoryService) GetCategoryStats(ctx context.Context) (*entity.CategoryStats, error) {
|
||
stats, err := s.categoryRepo.GetStats(ctx)
|
||
if err != nil {
|
||
s.logger.Error("Failed to get category stats", zap.Error(err))
|
||
return nil, err
|
||
}
|
||
|
||
return stats, nil
|
||
}
|
||
|
||
|
||
// GenerateSlug 生成唯一slug
|
||
func (s *CategoryService) GenerateSlug(ctx context.Context, name string) (string, error) {
|
||
slug, err := s.categoryRepo.GenerateUniqueSlug(ctx, name)
|
||
if err != nil {
|
||
s.logger.Error("Failed to generate unique slug", zap.Error(err))
|
||
return "", err
|
||
}
|
||
|
||
return slug, nil
|
||
} |