This commit is contained in:
2025-07-28 01:46:39 +08:00
parent b03129667a
commit 357639462a
219 changed files with 21634 additions and 8138 deletions

View File

@@ -35,12 +35,12 @@ func (s *CategoryApplicationServiceImpl) CreateCategory(ctx context.Context, cmd
if err := s.validateCreateCategory(cmd); err != nil {
return err
}
// 2. 验证分类编号唯一性
if err := s.validateCategoryCode(cmd.Code, ""); err != nil {
return err
}
// 3. 创建分类实体
category := &entities.ProductCategory{
Name: cmd.Name,
@@ -50,14 +50,14 @@ func (s *CategoryApplicationServiceImpl) CreateCategory(ctx context.Context, cmd
IsEnabled: cmd.IsEnabled,
IsVisible: cmd.IsVisible,
}
// 4. 保存到仓储
createdCategory, err := s.categoryRepo.Create(ctx, *category)
if err != nil {
s.logger.Error("创建分类失败", zap.Error(err), zap.String("code", cmd.Code))
return fmt.Errorf("创建分类失败: %w", err)
}
s.logger.Info("创建分类成功", zap.String("id", createdCategory.ID), zap.String("code", cmd.Code))
return nil
}
@@ -68,18 +68,18 @@ func (s *CategoryApplicationServiceImpl) UpdateCategory(ctx context.Context, cmd
if err := s.validateUpdateCategory(cmd); err != nil {
return err
}
// 2. 获取现有分类
existingCategory, err := s.categoryRepo.GetByID(ctx, cmd.ID)
if err != nil {
return fmt.Errorf("分类不存在: %w", err)
}
// 3. 验证分类编号唯一性(排除当前分类)
if err := s.validateCategoryCode(cmd.Code, cmd.ID); err != nil {
return err
}
// 4. 更新分类信息
existingCategory.Name = cmd.Name
existingCategory.Code = cmd.Code
@@ -87,13 +87,13 @@ func (s *CategoryApplicationServiceImpl) UpdateCategory(ctx context.Context, cmd
existingCategory.Sort = cmd.Sort
existingCategory.IsEnabled = cmd.IsEnabled
existingCategory.IsVisible = cmd.IsVisible
// 5. 保存到仓储
if err := s.categoryRepo.Update(ctx, existingCategory); err != nil {
s.logger.Error("更新分类失败", zap.Error(err), zap.String("id", cmd.ID))
return fmt.Errorf("更新分类失败: %w", err)
}
s.logger.Info("更新分类成功", zap.String("id", cmd.ID), zap.String("code", cmd.Code))
return nil
}
@@ -105,16 +105,16 @@ func (s *CategoryApplicationServiceImpl) DeleteCategory(ctx context.Context, cmd
if err != nil {
return fmt.Errorf("分类不存在: %w", err)
}
// 2. 检查是否有产品(可选,根据业务需求决定)
// 这里可以添加检查逻辑,如果有产品则不允许删除
// 3. 删除分类
if err := s.categoryRepo.Delete(ctx, cmd.ID); err != nil {
s.logger.Error("删除分类失败", zap.Error(err), zap.String("id", cmd.ID))
return fmt.Errorf("删除分类失败: %w", err)
}
s.logger.Info("删除分类成功", zap.String("id", cmd.ID), zap.String("code", existingCategory.Code))
return nil
}
@@ -226,11 +226,11 @@ func (s *CategoryApplicationServiceImpl) validateCategoryCode(code, excludeID st
if code == "" {
return errors.New("分类编号不能为空")
}
existingCategory, err := s.categoryRepo.FindByCode(context.Background(), code)
if err == nil && existingCategory != nil && existingCategory.ID != excludeID {
return errors.New("分类编号已存在")
}
return nil
}
}

View File

@@ -0,0 +1,27 @@
package commands
// AddPackageItemCommand 添加组合包子产品命令
type AddPackageItemCommand struct {
ProductID string `json:"product_id" binding:"required,uuid" comment:"子产品ID"`
}
// UpdatePackageItemCommand 更新组合包子产品命令
type UpdatePackageItemCommand struct {
SortOrder int `json:"sort_order" binding:"required,min=0" comment:"排序"`
}
// ReorderPackageItemsCommand 重新排序组合包子产品命令
type ReorderPackageItemsCommand struct {
ItemIDs []string `json:"item_ids" binding:"required,dive,uuid" comment:"子产品ID列表"`
}
// UpdatePackageItemsCommand 批量更新组合包子产品命令
type UpdatePackageItemsCommand struct {
Items []PackageItemData `json:"items" binding:"required,dive" comment:"子产品列表"`
}
// PackageItemData 组合包子产品数据
type PackageItemData struct {
ProductID string `json:"product_id" binding:"required,uuid" comment:"子产品ID"`
SortOrder int `json:"sort_order" binding:"required,min=0" comment:"排序"`
}

View File

@@ -11,7 +11,7 @@ type CreateProductCommand struct {
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
IsVisible bool `json:"is_visible" comment:"是否展示"`
IsPackage bool `json:"is_package" comment:"是否组合包"`
// SEO信息
SEOTitle string `json:"seo_title" binding:"omitempty,max=100" comment:"SEO标题"`
SEODescription string `json:"seo_description" binding:"omitempty,max=200" comment:"SEO描述"`
@@ -30,7 +30,7 @@ type UpdateProductCommand struct {
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
IsVisible bool `json:"is_visible" comment:"是否展示"`
IsPackage bool `json:"is_package" comment:"是否组合包"`
// SEO信息
SEOTitle string `json:"seo_title" binding:"omitempty,max=100" comment:"SEO标题"`
SEODescription string `json:"seo_description" binding:"omitempty,max=200" comment:"SEO描述"`
@@ -40,4 +40,4 @@ type UpdateProductCommand struct {
// DeleteProductCommand 删除产品命令
type DeleteProductCommand struct {
ID string `json:"-" uri:"id" binding:"required,uuid" comment:"产品ID"`
}
}

View File

@@ -0,0 +1,10 @@
package queries
// GetAvailableProductsQuery 获取可选子产品查询
type GetAvailableProductsQuery struct {
ExcludePackageID string `form:"exclude_package_id" binding:"omitempty,uuid" comment:"排除的组合包ID"`
Keyword string `form:"keyword" binding:"omitempty,max=100" comment:"搜索关键词"`
CategoryID string `form:"category_id" binding:"omitempty,uuid" comment:"分类ID"`
Page int `form:"page" binding:"omitempty,min=1" comment:"页码"`
PageSize int `form:"page_size" binding:"omitempty,min=1,max=100" comment:"每页数量"`
}

View File

@@ -0,0 +1,43 @@
package responses
import "time"
// ProductApiConfigResponse 产品API配置响应
type ProductApiConfigResponse struct {
ID string `json:"id" comment:"配置ID"`
ProductID string `json:"product_id" comment:"产品ID"`
RequestParams []RequestParamResponse `json:"request_params" comment:"请求参数配置"`
ResponseFields []ResponseFieldResponse `json:"response_fields" comment:"响应字段配置"`
ResponseExample map[string]interface{} `json:"response_example" comment:"响应示例"`
CreatedAt time.Time `json:"created_at" comment:"创建时间"`
UpdatedAt time.Time `json:"updated_at" comment:"更新时间"`
}
// RequestParamResponse 请求参数响应
type RequestParamResponse struct {
Name string `json:"name" comment:"参数名称"`
Field string `json:"field" comment:"参数字段名"`
Type string `json:"type" comment:"参数类型"`
Required bool `json:"required" comment:"是否必填"`
Description string `json:"description" comment:"参数描述"`
Example string `json:"example" comment:"参数示例"`
Validation string `json:"validation" comment:"验证规则"`
}
// ResponseFieldResponse 响应字段响应
type ResponseFieldResponse struct {
Name string `json:"name" comment:"字段名称"`
Path string `json:"path" comment:"字段路径"`
Type string `json:"type" comment:"字段类型"`
Description string `json:"description" comment:"字段描述"`
Required bool `json:"required" comment:"是否必填"`
Example string `json:"example" comment:"字段示例"`
}
// ProductApiConfigListResponse 产品API配置列表响应
type ProductApiConfigListResponse struct {
Total int64 `json:"total" comment:"总数"`
Page int `json:"page" comment:"页码"`
Size int `json:"size" comment:"每页数量"`
Items []ProductApiConfigResponse `json:"items" comment:"配置列表"`
}

View File

@@ -2,6 +2,16 @@ package responses
import "time"
// PackageItemResponse 组合包项目响应
type PackageItemResponse struct {
ID string `json:"id" comment:"项目ID"`
ProductID string `json:"product_id" comment:"子产品ID"`
ProductCode string `json:"product_code" comment:"子产品编号"`
ProductName string `json:"product_name" comment:"子产品名称"`
SortOrder int `json:"sort_order" comment:"排序"`
Price float64 `json:"price" comment:"子产品价格"`
}
// ProductInfoResponse 产品详情响应
type ProductInfoResponse struct {
ID string `json:"id" comment:"产品ID"`
@@ -23,6 +33,9 @@ type ProductInfoResponse struct {
// 关联信息
Category *CategoryInfoResponse `json:"category,omitempty" comment:"分类信息"`
// 组合包信息
PackageItems []*PackageItemResponse `json:"package_items,omitempty" comment:"组合包项目列表"`
CreatedAt time.Time `json:"created_at" comment:"创建时间"`
UpdatedAt time.Time `json:"updated_at" comment:"更新时间"`
}

View File

@@ -0,0 +1,206 @@
package product
import (
"context"
"errors"
"tyapi-server/internal/application/product/dto/responses"
"tyapi-server/internal/domains/product/entities"
"tyapi-server/internal/domains/product/services"
"go.uber.org/zap"
)
// ProductApiConfigApplicationService 产品API配置应用服务接口
type ProductApiConfigApplicationService interface {
// 获取产品API配置
GetProductApiConfig(ctx context.Context, productID string) (*responses.ProductApiConfigResponse, error)
// 根据产品代码获取API配置
GetProductApiConfigByCode(ctx context.Context, productCode string) (*responses.ProductApiConfigResponse, error)
// 批量获取产品API配置
GetProductApiConfigsByProductIDs(ctx context.Context, productIDs []string) ([]*responses.ProductApiConfigResponse, error)
// 创建产品API配置
CreateProductApiConfig(ctx context.Context, productID string, config *responses.ProductApiConfigResponse) error
// 更新产品API配置
UpdateProductApiConfig(ctx context.Context, configID string, config *responses.ProductApiConfigResponse) error
// 删除产品API配置
DeleteProductApiConfig(ctx context.Context, configID string) error
}
// ProductApiConfigApplicationServiceImpl 产品API配置应用服务实现
type ProductApiConfigApplicationServiceImpl struct {
apiConfigService services.ProductApiConfigService
logger *zap.Logger
}
// NewProductApiConfigApplicationService 创建产品API配置应用服务
func NewProductApiConfigApplicationService(
apiConfigService services.ProductApiConfigService,
logger *zap.Logger,
) ProductApiConfigApplicationService {
return &ProductApiConfigApplicationServiceImpl{
apiConfigService: apiConfigService,
logger: logger,
}
}
// GetProductApiConfig 获取产品API配置
func (s *ProductApiConfigApplicationServiceImpl) GetProductApiConfig(ctx context.Context, productID string) (*responses.ProductApiConfigResponse, error) {
config, err := s.apiConfigService.GetApiConfigByProductID(ctx, productID)
if err != nil {
return nil, err
}
return s.convertToResponse(config), nil
}
// GetProductApiConfigByCode 根据产品代码获取API配置
func (s *ProductApiConfigApplicationServiceImpl) GetProductApiConfigByCode(ctx context.Context, productCode string) (*responses.ProductApiConfigResponse, error) {
config, err := s.apiConfigService.GetApiConfigByProductCode(ctx, productCode)
if err != nil {
return nil, err
}
return s.convertToResponse(config), nil
}
// GetProductApiConfigsByProductIDs 批量获取产品API配置
func (s *ProductApiConfigApplicationServiceImpl) GetProductApiConfigsByProductIDs(ctx context.Context, productIDs []string) ([]*responses.ProductApiConfigResponse, error) {
configs, err := s.apiConfigService.GetApiConfigsByProductIDs(ctx, productIDs)
if err != nil {
return nil, err
}
var responses []*responses.ProductApiConfigResponse
for _, config := range configs {
responses = append(responses, s.convertToResponse(config))
}
return responses, nil
}
// CreateProductApiConfig 创建产品API配置
func (s *ProductApiConfigApplicationServiceImpl) CreateProductApiConfig(ctx context.Context, productID string, configResponse *responses.ProductApiConfigResponse) error {
// 检查是否已存在配置
exists, err := s.apiConfigService.ExistsByProductID(ctx, productID)
if err != nil {
return err
}
if exists {
return errors.New("产品API配置已存在")
}
// 转换为实体
config := s.convertToEntity(configResponse)
config.ProductID = productID
return s.apiConfigService.CreateApiConfig(ctx, config)
}
// UpdateProductApiConfig 更新产品API配置
func (s *ProductApiConfigApplicationServiceImpl) UpdateProductApiConfig(ctx context.Context, configID string, configResponse *responses.ProductApiConfigResponse) error {
// 获取现有配置
existingConfig, err := s.apiConfigService.GetApiConfigByProductID(ctx, configResponse.ProductID)
if err != nil {
return err
}
// 更新配置
config := s.convertToEntity(configResponse)
config.ID = configID
config.ProductID = existingConfig.ProductID
return s.apiConfigService.UpdateApiConfig(ctx, config)
}
// DeleteProductApiConfig 删除产品API配置
func (s *ProductApiConfigApplicationServiceImpl) DeleteProductApiConfig(ctx context.Context, configID string) error {
return s.apiConfigService.DeleteApiConfig(ctx, configID)
}
// convertToResponse 转换为响应DTO
func (s *ProductApiConfigApplicationServiceImpl) convertToResponse(config *entities.ProductApiConfig) *responses.ProductApiConfigResponse {
requestParams, _ := config.GetRequestParams()
responseFields, _ := config.GetResponseFields()
responseExample, _ := config.GetResponseExample()
// 转换请求参数
var requestParamResponses []responses.RequestParamResponse
for _, param := range requestParams {
requestParamResponses = append(requestParamResponses, responses.RequestParamResponse{
Name: param.Name,
Field: param.Field,
Type: param.Type,
Required: param.Required,
Description: param.Description,
Example: param.Example,
Validation: param.Validation,
})
}
// 转换响应字段
var responseFieldResponses []responses.ResponseFieldResponse
for _, field := range responseFields {
responseFieldResponses = append(responseFieldResponses, responses.ResponseFieldResponse{
Name: field.Name,
Path: field.Path,
Type: field.Type,
Description: field.Description,
Required: field.Required,
Example: field.Example,
})
}
return &responses.ProductApiConfigResponse{
ID: config.ID,
ProductID: config.ProductID,
RequestParams: requestParamResponses,
ResponseFields: responseFieldResponses,
ResponseExample: responseExample,
CreatedAt: config.CreatedAt,
UpdatedAt: config.UpdatedAt,
}
}
// convertToEntity 转换为实体
func (s *ProductApiConfigApplicationServiceImpl) convertToEntity(configResponse *responses.ProductApiConfigResponse) *entities.ProductApiConfig {
// 转换请求参数
var requestParams []entities.RequestParam
for _, param := range configResponse.RequestParams {
requestParams = append(requestParams, entities.RequestParam{
Name: param.Name,
Field: param.Field,
Type: param.Type,
Required: param.Required,
Description: param.Description,
Example: param.Example,
Validation: param.Validation,
})
}
// 转换响应字段
var responseFields []entities.ResponseField
for _, field := range configResponse.ResponseFields {
responseFields = append(responseFields, entities.ResponseField{
Name: field.Name,
Path: field.Path,
Type: field.Type,
Description: field.Description,
Required: field.Required,
Example: field.Example,
})
}
config := &entities.ProductApiConfig{}
// 设置JSON字段
config.SetRequestParams(requestParams)
config.SetResponseFields(responseFields)
config.SetResponseExample(configResponse.ResponseExample)
return config
}

View File

@@ -5,6 +5,7 @@ import (
"tyapi-server/internal/application/product/dto/commands"
"tyapi-server/internal/application/product/dto/queries"
"tyapi-server/internal/application/product/dto/responses"
"tyapi-server/internal/shared/interfaces"
)
// ProductApplicationService 产品应用服务接口
@@ -15,12 +16,26 @@ type ProductApplicationService interface {
DeleteProduct(ctx context.Context, cmd *commands.DeleteProductCommand) error
GetProductByID(ctx context.Context, query *queries.GetProductQuery) (*responses.ProductInfoResponse, error)
ListProducts(ctx context.Context, query *queries.ListProductsQuery) (*responses.ProductListResponse, error)
ListProducts(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductListResponse, error)
GetProductsByIDs(ctx context.Context, query *queries.GetProductsByIDsQuery) ([]*responses.ProductInfoResponse, error)
// 业务查询
GetSubscribableProducts(ctx context.Context, query *queries.GetSubscribableProductsQuery) ([]*responses.ProductInfoResponse, error)
GetProductStats(ctx context.Context) (*responses.ProductStatsResponse, error)
// 组合包管理
AddPackageItem(ctx context.Context, packageID string, cmd *commands.AddPackageItemCommand) error
UpdatePackageItem(ctx context.Context, packageID, itemID string, cmd *commands.UpdatePackageItemCommand) error
RemovePackageItem(ctx context.Context, packageID, itemID string) error
ReorderPackageItems(ctx context.Context, packageID string, cmd *commands.ReorderPackageItemsCommand) error
UpdatePackageItems(ctx context.Context, packageID string, cmd *commands.UpdatePackageItemsCommand) error
GetAvailableProducts(ctx context.Context, query *queries.GetAvailableProductsQuery) (*responses.ProductListResponse, error)
// API配置管理
GetProductApiConfig(ctx context.Context, productID string) (*responses.ProductApiConfigResponse, error)
CreateProductApiConfig(ctx context.Context, productID string, config *responses.ProductApiConfigResponse) error
UpdateProductApiConfig(ctx context.Context, configID string, config *responses.ProductApiConfigResponse) error
DeleteProductApiConfig(ctx context.Context, configID string) error
}
// CategoryApplicationService 分类应用服务接口

View File

@@ -2,7 +2,9 @@ package product
import (
"context"
"fmt"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"tyapi-server/internal/application/product/dto/commands"
@@ -10,25 +12,29 @@ import (
"tyapi-server/internal/application/product/dto/responses"
"tyapi-server/internal/domains/product/entities"
product_service "tyapi-server/internal/domains/product/services"
"tyapi-server/internal/shared/interfaces"
)
// ProductApplicationServiceImpl 产品应用服务实现
// 负责业务流程编排、事务管理、数据转换,不直接操作仓库
type ProductApplicationServiceImpl struct {
productManagementService *product_service.ProductManagementService
productSubscriptionService *product_service.ProductSubscriptionService
logger *zap.Logger
productManagementService *product_service.ProductManagementService
productSubscriptionService *product_service.ProductSubscriptionService
productApiConfigAppService ProductApiConfigApplicationService
logger *zap.Logger
}
// NewProductApplicationService 创建产品应用服务
func NewProductApplicationService(
productManagementService *product_service.ProductManagementService,
productSubscriptionService *product_service.ProductSubscriptionService,
productApiConfigAppService ProductApiConfigApplicationService,
logger *zap.Logger,
) ProductApplicationService {
return &ProductApplicationServiceImpl{
productManagementService: productManagementService,
productSubscriptionService: productSubscriptionService,
productApiConfigAppService: productApiConfigAppService,
logger: logger,
}
}
@@ -43,7 +49,7 @@ func (s *ProductApplicationServiceImpl) CreateProduct(ctx context.Context, cmd *
Description: cmd.Description,
Content: cmd.Content,
CategoryID: cmd.CategoryID,
Price: cmd.Price,
Price: decimal.NewFromFloat(cmd.Price),
IsEnabled: cmd.IsEnabled,
IsVisible: cmd.IsVisible,
IsPackage: cmd.IsPackage,
@@ -72,7 +78,7 @@ func (s *ProductApplicationServiceImpl) UpdateProduct(ctx context.Context, cmd *
existingProduct.Description = cmd.Description
existingProduct.Content = cmd.Content
existingProduct.CategoryID = cmd.CategoryID
existingProduct.Price = cmd.Price
existingProduct.Price = decimal.NewFromFloat(cmd.Price)
existingProduct.IsEnabled = cmd.IsEnabled
existingProduct.IsVisible = cmd.IsVisible
existingProduct.IsPackage = cmd.IsPackage
@@ -92,22 +98,9 @@ func (s *ProductApplicationServiceImpl) DeleteProduct(ctx context.Context, cmd *
// ListProducts 获取产品列表
// 业务流程1. 获取产品列表 2. 构建响应数据
func (s *ProductApplicationServiceImpl) ListProducts(ctx context.Context, query *appQueries.ListProductsQuery) (*responses.ProductListResponse, error) {
// 根据查询条件获取产品列表
var products []*entities.Product
var err error
if query.CategoryID != "" {
products, err = s.productManagementService.GetProductsByCategory(ctx, query.CategoryID)
} else if query.IsVisible != nil && *query.IsVisible {
products, err = s.productManagementService.GetVisibleProducts(ctx)
} else if query.IsEnabled != nil && *query.IsEnabled {
products, err = s.productManagementService.GetEnabledProducts(ctx)
} else {
// 默认获取可见产品
products, err = s.productManagementService.GetVisibleProducts(ctx)
}
func (s *ProductApplicationServiceImpl) ListProducts(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductListResponse, error) {
// 调用领域服务获取产品列表
products, total, err := s.productManagementService.ListProducts(ctx, filters, options)
if err != nil {
return nil, err
}
@@ -119,9 +112,9 @@ func (s *ProductApplicationServiceImpl) ListProducts(ctx context.Context, query
}
return &responses.ProductListResponse{
Total: int64(len(items)),
Page: query.Page,
Size: query.PageSize,
Total: total,
Page: options.Page,
Size: options.PageSize,
Items: items,
}, nil
}
@@ -186,6 +179,177 @@ func (s *ProductApplicationServiceImpl) GetProductStats(ctx context.Context) (*r
}, nil
}
// AddPackageItem 添加组合包子产品
func (s *ProductApplicationServiceImpl) AddPackageItem(ctx context.Context, packageID string, cmd *commands.AddPackageItemCommand) error {
// 验证组合包是否存在
packageProduct, err := s.productManagementService.GetProductByID(ctx, packageID)
if err != nil {
return err
}
if !packageProduct.IsPackage {
return fmt.Errorf("产品不是组合包")
}
// 验证子产品是否存在且不是组合包
subProduct, err := s.productManagementService.GetProductByID(ctx, cmd.ProductID)
if err != nil {
return err
}
if subProduct.IsPackage {
return fmt.Errorf("不能将组合包作为子产品")
}
// 检查是否已经存在
existingItems, err := s.productManagementService.GetPackageItems(ctx, packageID)
if err == nil {
for _, item := range existingItems {
if item.ProductID == cmd.ProductID {
return fmt.Errorf("该产品已在组合包中")
}
}
}
// 获取当前最大排序号
maxSortOrder := 0
if existingItems != nil {
for _, item := range existingItems {
if item.SortOrder > maxSortOrder {
maxSortOrder = item.SortOrder
}
}
}
// 创建组合包项目
packageItem := &entities.ProductPackageItem{
PackageID: packageID,
ProductID: cmd.ProductID,
SortOrder: maxSortOrder + 1,
}
return s.productManagementService.CreatePackageItem(ctx, packageItem)
}
// UpdatePackageItem 更新组合包子产品
func (s *ProductApplicationServiceImpl) UpdatePackageItem(ctx context.Context, packageID, itemID string, cmd *commands.UpdatePackageItemCommand) error {
// 验证组合包项目是否存在
packageItem, err := s.productManagementService.GetPackageItemByID(ctx, itemID)
if err != nil {
return err
}
if packageItem.PackageID != packageID {
return fmt.Errorf("组合包项目不属于指定组合包")
}
// 更新项目
packageItem.SortOrder = cmd.SortOrder
return s.productManagementService.UpdatePackageItem(ctx, packageItem)
}
// RemovePackageItem 移除组合包子产品
func (s *ProductApplicationServiceImpl) RemovePackageItem(ctx context.Context, packageID, itemID string) error {
// 验证组合包项目是否存在
packageItem, err := s.productManagementService.GetPackageItemByID(ctx, itemID)
if err != nil {
return err
}
if packageItem.PackageID != packageID {
return fmt.Errorf("组合包项目不属于指定组合包")
}
return s.productManagementService.DeletePackageItem(ctx, itemID)
}
// ReorderPackageItems 重新排序组合包子产品
func (s *ProductApplicationServiceImpl) ReorderPackageItems(ctx context.Context, packageID string, cmd *commands.ReorderPackageItemsCommand) error {
// 验证所有项目是否属于该组合包
for i, itemID := range cmd.ItemIDs {
packageItem, err := s.productManagementService.GetPackageItemByID(ctx, itemID)
if err != nil {
return err
}
if packageItem.PackageID != packageID {
return fmt.Errorf("组合包项目不属于指定组合包")
}
// 更新排序
packageItem.SortOrder = i + 1
if err := s.productManagementService.UpdatePackageItem(ctx, packageItem); err != nil {
return err
}
}
return nil
}
// UpdatePackageItems 批量更新组合包子产品
func (s *ProductApplicationServiceImpl) UpdatePackageItems(ctx context.Context, packageID string, cmd *commands.UpdatePackageItemsCommand) error {
// 验证组合包是否存在
packageProduct, err := s.productManagementService.GetProductByID(ctx, packageID)
if err != nil {
return err
}
if !packageProduct.IsPackage {
return fmt.Errorf("产品不是组合包")
}
// 验证所有子产品是否存在且不是组合包
for _, item := range cmd.Items {
subProduct, err := s.productManagementService.GetProductByID(ctx, item.ProductID)
if err != nil {
return err
}
if subProduct.IsPackage {
return fmt.Errorf("不能将组合包作为子产品")
}
}
// 使用事务进行批量更新
return s.productManagementService.UpdatePackageItemsBatch(ctx, packageID, cmd.Items)
}
// GetAvailableProducts 获取可选子产品列表
func (s *ProductApplicationServiceImpl) GetAvailableProducts(ctx context.Context, query *appQueries.GetAvailableProductsQuery) (*responses.ProductListResponse, error) {
// 构建筛选条件
filters := make(map[string]interface{})
filters["is_package"] = false // 只获取非组合包产品
filters["is_enabled"] = true // 只获取启用产品
if query.Keyword != "" {
filters["keyword"] = query.Keyword
}
if query.CategoryID != "" {
filters["category_id"] = query.CategoryID
}
// 设置分页选项
options := interfaces.ListOptions{
Page: query.Page,
PageSize: query.PageSize,
Sort: "created_at",
Order: "desc",
}
// 获取产品列表
products, total, err := s.productManagementService.ListProducts(ctx, filters, options)
if err != nil {
return nil, err
}
// 转换为响应对象
items := make([]responses.ProductInfoResponse, len(products))
for i := range products {
items[i] = *s.convertToProductInfoResponse(products[i])
}
return &responses.ProductListResponse{
Total: total,
Page: options.Page,
Size: options.PageSize,
Items: items,
}, nil
}
// convertToProductInfoResponse 转换为产品信息响应
func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *entities.Product) *responses.ProductInfoResponse {
response := &responses.ProductInfoResponse{
@@ -195,7 +359,7 @@ func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *en
Description: product.Description,
Content: product.Content,
CategoryID: product.CategoryID,
Price: product.Price,
Price: product.Price.InexactFloat64(),
IsEnabled: product.IsEnabled,
IsVisible: product.IsVisible,
IsPackage: product.IsPackage,
@@ -211,6 +375,21 @@ func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *en
response.Category = s.convertToCategoryInfoResponse(product.Category)
}
// 转换组合包项目信息
if product.IsPackage && len(product.PackageItems) > 0 {
response.PackageItems = make([]*responses.PackageItemResponse, len(product.PackageItems))
for i, item := range product.PackageItems {
response.PackageItems[i] = &responses.PackageItemResponse{
ID: item.ID,
ProductID: item.ProductID,
ProductCode: item.Product.Code,
ProductName: item.Product.Name,
SortOrder: item.SortOrder,
Price: item.Product.Price.InexactFloat64(),
}
}
}
return response
}
@@ -224,4 +403,24 @@ func (s *ProductApplicationServiceImpl) convertToCategoryInfoResponse(category *
CreatedAt: category.CreatedAt,
UpdatedAt: category.UpdatedAt,
}
}
// GetProductApiConfig 获取产品API配置
func (s *ProductApplicationServiceImpl) GetProductApiConfig(ctx context.Context, productID string) (*responses.ProductApiConfigResponse, error) {
return s.productApiConfigAppService.GetProductApiConfig(ctx, productID)
}
// CreateProductApiConfig 创建产品API配置
func (s *ProductApplicationServiceImpl) CreateProductApiConfig(ctx context.Context, productID string, config *responses.ProductApiConfigResponse) error {
return s.productApiConfigAppService.CreateProductApiConfig(ctx, productID, config)
}
// UpdateProductApiConfig 更新产品API配置
func (s *ProductApplicationServiceImpl) UpdateProductApiConfig(ctx context.Context, configID string, config *responses.ProductApiConfigResponse) error {
return s.productApiConfigAppService.UpdateProductApiConfig(ctx, configID, config)
}
// DeleteProductApiConfig 删除产品API配置
func (s *ProductApplicationServiceImpl) DeleteProductApiConfig(ctx context.Context, configID string) error {
return s.productApiConfigAppService.DeleteProductApiConfig(ctx, configID)
}

View File

@@ -4,12 +4,15 @@ import (
"context"
"fmt"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"tyapi-server/internal/application/product/dto/commands"
appQueries "tyapi-server/internal/application/product/dto/queries"
"tyapi-server/internal/application/product/dto/responses"
"tyapi-server/internal/domains/product/entities"
repoQueries "tyapi-server/internal/domains/product/repositories/queries"
product_service "tyapi-server/internal/domains/product/services"
)
@@ -41,7 +44,7 @@ func (s *SubscriptionApplicationServiceImpl) UpdateSubscriptionPrice(ctx context
}
// 2. 更新订阅价格
subscription.Price = cmd.Price
subscription.Price = decimal.NewFromFloat(cmd.Price)
// 3. 保存订阅
// 这里需要扩展领域服务来支持更新操作
@@ -70,13 +73,26 @@ func (s *SubscriptionApplicationServiceImpl) GetSubscriptionByID(ctx context.Con
// ListSubscriptions 获取订阅列表
// 业务流程1. 获取订阅列表 2. 构建响应数据
func (s *SubscriptionApplicationServiceImpl) ListSubscriptions(ctx context.Context, query *appQueries.ListSubscriptionsQuery) (*responses.SubscriptionListResponse, error) {
// 这里需要扩展领域服务来支持列表查询
// 暂时返回空列表
repoQuery := &repoQueries.ListSubscriptionsQuery{
Page: query.Page,
PageSize: query.PageSize,
}
subscriptions, total, err := s.productSubscriptionService.ListSubscriptions(ctx, repoQuery)
if err != nil {
return nil, err
}
items := make([]responses.SubscriptionInfoResponse, len(subscriptions))
for i := range subscriptions {
resp := s.convertToSubscriptionInfoResponse(subscriptions[i])
if resp != nil {
items[i] = *resp // 解引用指针
}
}
return &responses.SubscriptionListResponse{
Total: 0,
Total: total,
Page: query.Page,
Size: query.PageSize,
Items: []responses.SubscriptionInfoResponse{},
Items: items,
}, nil
}
@@ -137,7 +153,8 @@ func (s *SubscriptionApplicationServiceImpl) convertToSubscriptionInfoResponse(s
ID: subscription.ID,
UserID: subscription.UserID,
ProductID: subscription.ProductID,
Price: subscription.Price,
Price: subscription.Price.InexactFloat64(),
Product: s.convertToProductSimpleResponse(subscription.Product),
APIUsed: subscription.APIUsed,
CreatedAt: subscription.CreatedAt,
UpdatedAt: subscription.UpdatedAt,
@@ -151,7 +168,8 @@ func (s *SubscriptionApplicationServiceImpl) convertToProductSimpleResponse(prod
Name: product.Name,
Code: product.Code,
Description: product.Description,
Price: product.Price,
Price: product.Price.InexactFloat64(),
Category: s.convertToCategorySimpleResponse(product.Category),
IsPackage: product.IsPackage,
}
}