388 lines
12 KiB
Go
388 lines
12 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"tyapi-server/internal/application/product/dto/commands"
|
|
"tyapi-server/internal/domains/product/entities"
|
|
"tyapi-server/internal/domains/product/repositories"
|
|
"tyapi-server/internal/domains/product/repositories/queries"
|
|
"tyapi-server/internal/shared/interfaces"
|
|
)
|
|
|
|
// ProductManagementService 产品管理领域服务
|
|
// 负责产品的基本管理操作,包括创建、查询、更新等
|
|
type ProductManagementService struct {
|
|
productRepo repositories.ProductRepository
|
|
categoryRepo repositories.ProductCategoryRepository
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewProductManagementService 创建产品管理领域服务
|
|
func NewProductManagementService(
|
|
productRepo repositories.ProductRepository,
|
|
categoryRepo repositories.ProductCategoryRepository,
|
|
logger *zap.Logger,
|
|
) *ProductManagementService {
|
|
return &ProductManagementService{
|
|
productRepo: productRepo,
|
|
categoryRepo: categoryRepo,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// CreateProduct 创建产品
|
|
func (s *ProductManagementService) CreateProduct(ctx context.Context, product *entities.Product) (*entities.Product, error) {
|
|
// 验证产品信息
|
|
if err := s.ValidateProduct(product); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 验证产品编号唯一性
|
|
if err := s.ValidateProductCode(product.Code, ""); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 创建产品
|
|
createdProduct, err := s.productRepo.Create(ctx, *product)
|
|
if err != nil {
|
|
s.logger.Error("创建产品失败", zap.Error(err))
|
|
return nil, fmt.Errorf("创建产品失败: %w", err)
|
|
}
|
|
|
|
s.logger.Info("产品创建成功",
|
|
zap.String("product_id", createdProduct.ID),
|
|
zap.String("product_name", createdProduct.Name),
|
|
)
|
|
|
|
return &createdProduct, nil
|
|
}
|
|
|
|
// GetProductByID 根据ID获取产品
|
|
func (s *ProductManagementService) GetProductByID(ctx context.Context, productID string) (*entities.Product, error) {
|
|
product, err := s.productRepo.GetByID(ctx, productID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("产品不存在: %w", err)
|
|
}
|
|
return &product, nil
|
|
}
|
|
|
|
func (s *ProductManagementService) GetProductByCode(ctx context.Context, productCode string) (*entities.Product, error) {
|
|
product, err := s.productRepo.FindByCode(ctx, productCode)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("产品不存在: %w", err)
|
|
}
|
|
return product, nil
|
|
}
|
|
// GetProductWithCategory 获取产品及其分类信息
|
|
func (s *ProductManagementService) GetProductWithCategory(ctx context.Context, productID string) (*entities.Product, error) {
|
|
product, err := s.productRepo.GetByID(ctx, productID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("产品不存在: %w", err)
|
|
}
|
|
|
|
// 加载分类信息
|
|
if product.CategoryID != "" {
|
|
category, err := s.categoryRepo.GetByID(ctx, product.CategoryID)
|
|
if err == nil {
|
|
product.Category = &category
|
|
}
|
|
}
|
|
|
|
// 如果是组合包,加载子产品信息
|
|
if product.IsPackage {
|
|
packageItems, err := s.productRepo.GetPackageItems(ctx, productID)
|
|
if err == nil {
|
|
product.PackageItems = packageItems
|
|
}
|
|
}
|
|
|
|
return &product, nil
|
|
}
|
|
|
|
// GetPackageItems 获取组合包项目列表
|
|
func (s *ProductManagementService) GetPackageItems(ctx context.Context, packageID string) ([]*entities.ProductPackageItem, error) {
|
|
packageItems, err := s.productRepo.GetPackageItems(ctx, packageID)
|
|
if err != nil {
|
|
s.logger.Error("获取组合包项目失败", zap.Error(err))
|
|
return nil, fmt.Errorf("获取组合包项目失败: %w", err)
|
|
}
|
|
return packageItems, nil
|
|
}
|
|
|
|
// CreatePackageItem 创建组合包项目
|
|
func (s *ProductManagementService) CreatePackageItem(ctx context.Context, packageItem *entities.ProductPackageItem) error {
|
|
if err := s.productRepo.CreatePackageItem(ctx, packageItem); err != nil {
|
|
s.logger.Error("创建组合包项目失败", zap.Error(err))
|
|
return fmt.Errorf("创建组合包项目失败: %w", err)
|
|
}
|
|
|
|
s.logger.Info("组合包项目创建成功",
|
|
zap.String("package_id", packageItem.PackageID),
|
|
zap.String("product_id", packageItem.ProductID),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetPackageItemByID 根据ID获取组合包项目
|
|
func (s *ProductManagementService) GetPackageItemByID(ctx context.Context, itemID string) (*entities.ProductPackageItem, error) {
|
|
packageItem, err := s.productRepo.GetPackageItemByID(ctx, itemID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("组合包项目不存在: %w", err)
|
|
}
|
|
return packageItem, nil
|
|
}
|
|
|
|
// UpdatePackageItem 更新组合包项目
|
|
func (s *ProductManagementService) UpdatePackageItem(ctx context.Context, packageItem *entities.ProductPackageItem) error {
|
|
if err := s.productRepo.UpdatePackageItem(ctx, packageItem); err != nil {
|
|
s.logger.Error("更新组合包项目失败", zap.Error(err))
|
|
return fmt.Errorf("更新组合包项目失败: %w", err)
|
|
}
|
|
|
|
s.logger.Info("组合包项目更新成功",
|
|
zap.String("item_id", packageItem.ID),
|
|
zap.String("package_id", packageItem.PackageID),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeletePackageItem 删除组合包项目
|
|
func (s *ProductManagementService) DeletePackageItem(ctx context.Context, itemID string) error {
|
|
if err := s.productRepo.DeletePackageItem(ctx, itemID); err != nil {
|
|
s.logger.Error("删除组合包项目失败", zap.Error(err))
|
|
return fmt.Errorf("删除组合包项目失败: %w", err)
|
|
}
|
|
|
|
s.logger.Info("组合包项目删除成功", zap.String("item_id", itemID))
|
|
return nil
|
|
}
|
|
|
|
// UpdatePackageItemsBatch 批量更新组合包子产品
|
|
func (s *ProductManagementService) UpdatePackageItemsBatch(ctx context.Context, packageID string, items []commands.PackageItemData) error {
|
|
// 删除现有的所有子产品
|
|
if err := s.productRepo.DeletePackageItemsByPackageID(ctx, packageID); err != nil {
|
|
s.logger.Error("删除现有组合包子产品失败", zap.Error(err))
|
|
return fmt.Errorf("删除现有组合包子产品失败: %w", err)
|
|
}
|
|
|
|
// 创建新的子产品项目
|
|
for _, item := range items {
|
|
packageItem := &entities.ProductPackageItem{
|
|
PackageID: packageID,
|
|
ProductID: item.ProductID,
|
|
SortOrder: item.SortOrder,
|
|
}
|
|
|
|
if err := s.productRepo.CreatePackageItem(ctx, packageItem); err != nil {
|
|
s.logger.Error("创建组合包子产品失败", zap.Error(err))
|
|
return fmt.Errorf("创建组合包子产品失败: %w", err)
|
|
}
|
|
}
|
|
|
|
s.logger.Info("批量更新组合包子产品成功",
|
|
zap.String("package_id", packageID),
|
|
zap.Int("item_count", len(items)),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateProduct 更新产品
|
|
func (s *ProductManagementService) UpdateProduct(ctx context.Context, product *entities.Product) error {
|
|
// 验证产品信息
|
|
if err := s.ValidateProduct(product); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 验证产品编号唯一性(排除自己)
|
|
if err := s.ValidateProductCode(product.Code, product.ID); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := s.productRepo.Update(ctx, *product); err != nil {
|
|
s.logger.Error("更新产品失败", zap.Error(err))
|
|
return fmt.Errorf("更新产品失败: %w", err)
|
|
}
|
|
|
|
s.logger.Info("产品更新成功",
|
|
zap.String("product_id", product.ID),
|
|
zap.String("product_name", product.Name),
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteProduct 删除产品
|
|
func (s *ProductManagementService) DeleteProduct(ctx context.Context, productID string) error {
|
|
if err := s.productRepo.Delete(ctx, productID); err != nil {
|
|
s.logger.Error("删除产品失败", zap.Error(err))
|
|
return fmt.Errorf("删除产品失败: %w", err)
|
|
}
|
|
|
|
s.logger.Info("产品删除成功", zap.String("product_id", productID))
|
|
return nil
|
|
}
|
|
|
|
// GetVisibleProducts 获取可见产品列表
|
|
func (s *ProductManagementService) GetVisibleProducts(ctx context.Context) ([]*entities.Product, error) {
|
|
return s.productRepo.FindVisible(ctx)
|
|
}
|
|
|
|
// GetEnabledProducts 获取启用产品列表
|
|
func (s *ProductManagementService) GetEnabledProducts(ctx context.Context) ([]*entities.Product, error) {
|
|
return s.productRepo.FindEnabled(ctx)
|
|
}
|
|
|
|
// GetProductsByCategory 根据分类获取产品
|
|
func (s *ProductManagementService) GetProductsByCategory(ctx context.Context, categoryID string) ([]*entities.Product, error) {
|
|
return s.productRepo.FindByCategoryID(ctx, categoryID)
|
|
}
|
|
|
|
// ValidateProduct 验证产品
|
|
func (s *ProductManagementService) ValidateProduct(product *entities.Product) error {
|
|
if product == nil {
|
|
return errors.New("产品不能为空")
|
|
}
|
|
|
|
if strings.TrimSpace(product.Name) == "" {
|
|
return errors.New("产品名称不能为空")
|
|
}
|
|
|
|
if strings.TrimSpace(product.Code) == "" {
|
|
return errors.New("产品编号不能为空")
|
|
}
|
|
|
|
if product.Price.IsNegative() {
|
|
return errors.New("产品价格不能为负数")
|
|
}
|
|
|
|
// 验证分类是否存在
|
|
if product.CategoryID != "" {
|
|
category, err := s.categoryRepo.GetByID(context.Background(), product.CategoryID)
|
|
if err != nil {
|
|
return fmt.Errorf("产品分类不存在: %w", err)
|
|
}
|
|
if !category.IsValid() {
|
|
return errors.New("产品分类已禁用或删除")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateProductCode 验证产品编号唯一性
|
|
func (s *ProductManagementService) ValidateProductCode(code string, excludeID string) error {
|
|
if strings.TrimSpace(code) == "" {
|
|
return errors.New("产品编号不能为空")
|
|
}
|
|
|
|
existingProduct, err := s.productRepo.FindByCode(context.Background(), code)
|
|
if err == nil && existingProduct != nil && existingProduct.ID != excludeID {
|
|
return errors.New("产品编号已存在")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListProducts 获取产品列表(支持筛选和分页)
|
|
func (s *ProductManagementService) ListProducts(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) ([]*entities.Product, int64, error) {
|
|
// 构建查询条件
|
|
query := &queries.ListProductsQuery{
|
|
Page: options.Page,
|
|
PageSize: options.PageSize,
|
|
SortBy: options.Sort,
|
|
SortOrder: options.Order,
|
|
}
|
|
|
|
// 应用筛选条件
|
|
if keyword, ok := filters["keyword"].(string); ok && keyword != "" {
|
|
query.Keyword = keyword
|
|
}
|
|
if categoryID, ok := filters["category_id"].(string); ok && categoryID != "" {
|
|
query.CategoryID = categoryID
|
|
}
|
|
if isEnabled, ok := filters["is_enabled"].(bool); ok {
|
|
query.IsEnabled = &isEnabled
|
|
}
|
|
if isVisible, ok := filters["is_visible"].(bool); ok {
|
|
query.IsVisible = &isVisible
|
|
}
|
|
if isPackage, ok := filters["is_package"].(bool); ok {
|
|
query.IsPackage = &isPackage
|
|
}
|
|
|
|
// 调用仓储层获取产品列表
|
|
products, total, err := s.productRepo.ListProducts(ctx, query)
|
|
if err != nil {
|
|
s.logger.Error("获取产品列表失败", zap.Error(err))
|
|
return nil, 0, fmt.Errorf("获取产品列表失败: %w", err)
|
|
}
|
|
|
|
s.logger.Info("产品列表查询成功",
|
|
zap.Int("count", len(products)),
|
|
zap.Int64("total", total),
|
|
zap.Int("page", options.Page),
|
|
zap.Int("page_size", options.PageSize),
|
|
)
|
|
|
|
return products, total, nil
|
|
}
|
|
|
|
// ListProductsWithSubscriptionStatus 获取产品列表(包含订阅状态)
|
|
func (s *ProductManagementService) ListProductsWithSubscriptionStatus(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) ([]*entities.Product, map[string]bool, int64, error) {
|
|
// 构建查询条件
|
|
query := &queries.ListProductsQuery{
|
|
Page: options.Page,
|
|
PageSize: options.PageSize,
|
|
SortBy: options.Sort,
|
|
SortOrder: options.Order,
|
|
}
|
|
|
|
// 应用筛选条件
|
|
if keyword, ok := filters["keyword"].(string); ok && keyword != "" {
|
|
query.Keyword = keyword
|
|
}
|
|
if categoryID, ok := filters["category_id"].(string); ok && categoryID != "" {
|
|
query.CategoryID = categoryID
|
|
}
|
|
if isEnabled, ok := filters["is_enabled"].(bool); ok {
|
|
query.IsEnabled = &isEnabled
|
|
}
|
|
if isVisible, ok := filters["is_visible"].(bool); ok {
|
|
query.IsVisible = &isVisible
|
|
}
|
|
if isPackage, ok := filters["is_package"].(bool); ok {
|
|
query.IsPackage = &isPackage
|
|
}
|
|
if userID, ok := filters["user_id"].(string); ok && userID != "" {
|
|
query.UserID = userID
|
|
}
|
|
if isSubscribed, ok := filters["is_subscribed"].(bool); ok {
|
|
query.IsSubscribed = &isSubscribed
|
|
}
|
|
|
|
// 调用仓储层获取产品列表(包含订阅状态)
|
|
products, subscriptionStatusMap, total, err := s.productRepo.ListProductsWithSubscriptionStatus(ctx, query)
|
|
if err != nil {
|
|
s.logger.Error("获取产品列表失败", zap.Error(err))
|
|
return nil, nil, 0, fmt.Errorf("获取产品列表失败: %w", err)
|
|
}
|
|
|
|
s.logger.Info("产品列表查询成功",
|
|
zap.Int("count", len(products)),
|
|
zap.Int64("total", total),
|
|
zap.Int("page", options.Page),
|
|
zap.Int("page_size", options.PageSize),
|
|
zap.String("user_id", query.UserID),
|
|
)
|
|
|
|
return products, subscriptionStatusMap, total, nil
|
|
} |