Files
tyapi-server/internal/domains/product/services/product_management_service.go
2026-05-27 15:18:43 +08:00

466 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
subCategoryRepo repositories.ProductSubCategoryRepository
logger *zap.Logger
}
// NewProductManagementService 创建产品管理领域服务
func NewProductManagementService(
productRepo repositories.ProductRepository,
categoryRepo repositories.ProductCategoryRepository,
subCategoryRepo repositories.ProductSubCategoryRepository,
logger *zap.Logger,
) *ProductManagementService {
return &ProductManagementService{
productRepo: productRepo,
categoryRepo: categoryRepo,
subCategoryRepo: subCategoryRepo,
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
}
// GetProductByOldIDWithCategory 根据旧ID获取产品及其分类信息
func (s *ProductManagementService) GetProductByOldIDWithCategory(ctx context.Context, oldID string) (*entities.Product, error) {
product, err := s.productRepo.FindByOldID(ctx, oldID)
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, product.ID)
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.CostPrice.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("产品分类已禁用或删除")
}
}
// 验证二级分类是否存在(如果设置了二级分类)
if product.SubCategoryID != nil && *product.SubCategoryID != "" {
subCategory, err := s.subCategoryRepo.GetByID(context.Background(), *product.SubCategoryID)
if err != nil {
return fmt.Errorf("产品二级分类不存在: %w", err)
}
if !subCategory.IsValid() {
return errors.New("产品二级分类已禁用或删除")
}
// 验证二级分类是否属于指定的一级分类
if subCategory.CategoryID != product.CategoryID {
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
}
// GetAllProductsForDictionary 获取所有启用且可见的产品(用于导出产品字典)
func (s *ProductManagementService) GetAllProductsForDictionary(ctx context.Context) ([]*entities.Product, error) {
// 构建查询条件:启用且可见
isEnabled := true
isVisible := true
filters := map[string]interface{}{
"is_enabled": isEnabled,
"is_visible": isVisible,
}
options := interfaces.ListOptions{
Page: 1,
PageSize: 1000, // 获取所有产品
Sort: "id", // 按产品ID排序避免JOIN问题
Order: "asc",
}
// 获取产品列表
products, _, err := s.ListProducts(ctx, filters, options)
if err != nil {
s.logger.Error("获取产品字典数据失败", zap.Error(err))
return nil, fmt.Errorf("获取产品字典数据失败: %w", err)
}
return products, nil
}