add f
This commit is contained in:
@@ -2,17 +2,18 @@ package commands
|
||||
|
||||
// CreateProductCommand 创建产品命令
|
||||
type CreateProductCommand struct {
|
||||
Name string `json:"name" binding:"required,min=2,max=100" comment:"产品名称"`
|
||||
Code string `json:"code" binding:"required,product_code" comment:"产品编号"`
|
||||
Description string `json:"description" binding:"omitempty,max=500" comment:"产品描述"`
|
||||
Content string `json:"content" binding:"omitempty,max=5000" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" binding:"required,uuid" comment:"产品分类ID"`
|
||||
Price float64 `json:"price" binding:"price,min=0" comment:"产品价格"`
|
||||
CostPrice float64 `json:"cost_price" binding:"omitempty,min=0" comment:"成本价"`
|
||||
Remark string `json:"remark" binding:"omitempty,max=1000" comment:"备注"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
Name string `json:"name" binding:"required,min=2,max=100" comment:"产品名称"`
|
||||
Code string `json:"code" binding:"required,product_code" comment:"产品编号"`
|
||||
Description string `json:"description" binding:"omitempty,max=500" comment:"产品描述"`
|
||||
Content string `json:"content" binding:"omitempty,max=5000" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" binding:"required,uuid" comment:"一级分类ID"`
|
||||
SubCategoryID *string `json:"sub_category_id" binding:"omitempty,uuid" comment:"二级分类ID"`
|
||||
Price float64 `json:"price" binding:"price,min=0" comment:"产品价格"`
|
||||
CostPrice float64 `json:"cost_price" binding:"omitempty,min=0" comment:"成本价"`
|
||||
Remark string `json:"remark" binding:"omitempty,max=1000" comment:"备注"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
|
||||
// UI组件相关字段
|
||||
SellUIComponent bool `json:"sell_ui_component" comment:"是否出售UI组件"`
|
||||
@@ -26,18 +27,19 @@ type CreateProductCommand struct {
|
||||
|
||||
// UpdateProductCommand 更新产品命令
|
||||
type UpdateProductCommand struct {
|
||||
ID string `json:"-" uri:"id" binding:"required,uuid" comment:"产品ID"`
|
||||
Name string `json:"name" binding:"required,min=2,max=100" comment:"产品名称"`
|
||||
Code string `json:"code" binding:"required,product_code" comment:"产品编号"`
|
||||
Description string `json:"description" binding:"omitempty,max=500" comment:"产品描述"`
|
||||
Content string `json:"content" binding:"omitempty,max=5000" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" binding:"required,uuid" comment:"产品分类ID"`
|
||||
Price float64 `json:"price" binding:"price,min=0" comment:"产品价格"`
|
||||
CostPrice float64 `json:"cost_price" binding:"omitempty,min=0" comment:"成本价"`
|
||||
Remark string `json:"remark" binding:"omitempty,max=1000" comment:"备注"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
ID string `json:"-" uri:"id" binding:"required,uuid" comment:"产品ID"`
|
||||
Name string `json:"name" binding:"required,min=2,max=100" comment:"产品名称"`
|
||||
Code string `json:"code" binding:"required,product_code" comment:"产品编号"`
|
||||
Description string `json:"description" binding:"omitempty,max=500" comment:"产品描述"`
|
||||
Content string `json:"content" binding:"omitempty,max=5000" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" binding:"required,uuid" comment:"一级分类ID"`
|
||||
SubCategoryID *string `json:"sub_category_id" binding:"omitempty,uuid" comment:"二级分类ID"`
|
||||
Price float64 `json:"price" binding:"price,min=0" comment:"产品价格"`
|
||||
CostPrice float64 `json:"cost_price" binding:"omitempty,min=0" comment:"成本价"`
|
||||
Remark string `json:"remark" binding:"omitempty,max=1000" comment:"备注"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
|
||||
// UI组件相关字段
|
||||
SellUIComponent bool `json:"sell_ui_component" comment:"是否出售UI组件"`
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package commands
|
||||
|
||||
// CreateSubCategoryCommand 创建二级分类命令
|
||||
type CreateSubCategoryCommand struct {
|
||||
Name string `json:"name" binding:"required,min=2,max=100" comment:"二级分类名称"`
|
||||
Code string `json:"code" binding:"required,min=2,max=50" comment:"二级分类编号"`
|
||||
Description string `json:"description" binding:"omitempty,max=500" comment:"二级分类描述"`
|
||||
CategoryID string `json:"category_id" binding:"required,uuid" comment:"一级分类ID"`
|
||||
Sort int `json:"sort" binding:"min=0" comment:"排序"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
}
|
||||
|
||||
// UpdateSubCategoryCommand 更新二级分类命令
|
||||
type UpdateSubCategoryCommand struct {
|
||||
ID string `json:"-" uri:"id" binding:"required,uuid" comment:"二级分类ID"`
|
||||
Name string `json:"name" binding:"required,min=2,max=100" comment:"二级分类名称"`
|
||||
Code string `json:"code" binding:"required,min=2,max=50" comment:"二级分类编号"`
|
||||
Description string `json:"description" binding:"omitempty,max=500" comment:"二级分类描述"`
|
||||
CategoryID string `json:"category_id" binding:"required,uuid" comment:"一级分类ID"`
|
||||
Sort int `json:"sort" binding:"min=0" comment:"排序"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
}
|
||||
|
||||
// DeleteSubCategoryCommand 删除二级分类命令
|
||||
type DeleteSubCategoryCommand struct {
|
||||
ID string `json:"-" uri:"id" binding:"required,uuid" comment:"二级分类ID"`
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package queries
|
||||
|
||||
// GetSubCategoryQuery 获取二级分类查询
|
||||
type GetSubCategoryQuery struct {
|
||||
ID string `json:"id" form:"id" binding:"omitempty,uuid" comment:"二级分类ID"`
|
||||
}
|
||||
|
||||
// ListSubCategoriesQuery 获取二级分类列表查询
|
||||
type ListSubCategoriesQuery struct {
|
||||
Page int `json:"page" form:"page" binding:"min=1" comment:"页码"`
|
||||
PageSize int `json:"page_size" form:"page_size" binding:"min=1,max=100" comment:"每页数量"`
|
||||
CategoryID string `json:"category_id" form:"category_id" binding:"omitempty,uuid" comment:"一级分类ID"`
|
||||
IsEnabled *bool `json:"is_enabled" form:"is_enabled" comment:"是否启用"`
|
||||
IsVisible *bool `json:"is_visible" form:"is_visible" comment:"是否展示"`
|
||||
SortBy string `json:"sort_by" form:"sort_by" comment:"排序字段"`
|
||||
SortOrder string `json:"sort_order" form:"sort_order" comment:"排序方向"`
|
||||
}
|
||||
@@ -4,23 +4,23 @@ import "time"
|
||||
|
||||
// CategoryInfoResponse 分类详情响应
|
||||
type CategoryInfoResponse struct {
|
||||
ID string `json:"id" comment:"分类ID"`
|
||||
Name string `json:"name" comment:"分类名称"`
|
||||
Code string `json:"code" comment:"分类编号"`
|
||||
Description string `json:"description" comment:"分类描述"`
|
||||
Sort int `json:"sort" comment:"排序"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
|
||||
ID string `json:"id" comment:"分类ID"`
|
||||
Name string `json:"name" comment:"分类名称"`
|
||||
Code string `json:"code" comment:"分类编号"`
|
||||
Description string `json:"description" comment:"分类描述"`
|
||||
Sort int `json:"sort" comment:"排序"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" comment:"更新时间"`
|
||||
}
|
||||
|
||||
// CategoryListResponse 分类列表响应
|
||||
type CategoryListResponse struct {
|
||||
Total int64 `json:"total" comment:"总数"`
|
||||
Page int `json:"page" comment:"页码"`
|
||||
Size int `json:"size" comment:"每页数量"`
|
||||
Total int64 `json:"total" comment:"总数"`
|
||||
Page int `json:"page" comment:"页码"`
|
||||
Size int `json:"size" comment:"每页数量"`
|
||||
Items []CategoryInfoResponse `json:"items" comment:"分类列表"`
|
||||
}
|
||||
|
||||
@@ -29,4 +29,38 @@ type CategorySimpleResponse struct {
|
||||
ID string `json:"id" comment:"分类ID"`
|
||||
Name string `json:"name" comment:"分类名称"`
|
||||
Code string `json:"code" comment:"分类编号"`
|
||||
}
|
||||
}
|
||||
|
||||
// SubCategoryInfoResponse 二级分类详情响应
|
||||
type SubCategoryInfoResponse struct {
|
||||
ID string `json:"id" comment:"二级分类ID"`
|
||||
Name string `json:"name" comment:"二级分类名称"`
|
||||
Code string `json:"code" comment:"二级分类编号"`
|
||||
Description string `json:"description" comment:"二级分类描述"`
|
||||
CategoryID string `json:"category_id" comment:"一级分类ID"`
|
||||
Sort int `json:"sort" comment:"排序"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否展示"`
|
||||
|
||||
// 关联信息
|
||||
Category *CategoryInfoResponse `json:"category,omitempty" comment:"一级分类信息"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" comment:"更新时间"`
|
||||
}
|
||||
|
||||
// SubCategoryListResponse 二级分类列表响应
|
||||
type SubCategoryListResponse struct {
|
||||
Total int64 `json:"total" comment:"总数"`
|
||||
Page int `json:"page" comment:"页码"`
|
||||
Size int `json:"size" comment:"每页数量"`
|
||||
Items []SubCategoryInfoResponse `json:"items" comment:"二级分类列表"`
|
||||
}
|
||||
|
||||
// SubCategorySimpleResponse 二级分类简单信息响应
|
||||
type SubCategorySimpleResponse struct {
|
||||
ID string `json:"id" comment:"二级分类ID"`
|
||||
Name string `json:"name" comment:"二级分类名称"`
|
||||
Code string `json:"code" comment:"二级分类编号"`
|
||||
CategoryID string `json:"category_id" comment:"一级分类ID"`
|
||||
}
|
||||
|
||||
@@ -15,17 +15,18 @@ type PackageItemResponse struct {
|
||||
|
||||
// ProductInfoResponse 产品详情响应
|
||||
type ProductInfoResponse struct {
|
||||
ID string `json:"id" comment:"产品ID"`
|
||||
OldID *string `json:"old_id,omitempty" comment:"旧产品ID"`
|
||||
Name string `json:"name" comment:"产品名称"`
|
||||
Code string `json:"code" comment:"产品编号"`
|
||||
Description string `json:"description" comment:"产品简介"`
|
||||
Content string `json:"content" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" comment:"产品分类ID"`
|
||||
Price float64 `json:"price" comment:"产品价格"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
IsSubscribed *bool `json:"is_subscribed,omitempty" comment:"当前用户是否已订阅"`
|
||||
ID string `json:"id" comment:"产品ID"`
|
||||
OldID *string `json:"old_id,omitempty" comment:"旧产品ID"`
|
||||
Name string `json:"name" comment:"产品名称"`
|
||||
Code string `json:"code" comment:"产品编号"`
|
||||
Description string `json:"description" comment:"产品简介"`
|
||||
Content string `json:"content" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" comment:"一级分类ID"`
|
||||
SubCategoryID *string `json:"sub_category_id,omitempty" comment:"二级分类ID"`
|
||||
Price float64 `json:"price" comment:"产品价格"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
IsSubscribed *bool `json:"is_subscribed,omitempty" comment:"当前用户是否已订阅"`
|
||||
// UI组件相关字段
|
||||
SellUIComponent bool `json:"sell_ui_component" comment:"是否出售UI组件"`
|
||||
UIComponentPrice float64 `json:"ui_component_price" comment:"UI组件销售价格(组合包使用)"`
|
||||
@@ -36,7 +37,8 @@ type ProductInfoResponse struct {
|
||||
SEOKeywords string `json:"seo_keywords" comment:"SEO关键词"`
|
||||
|
||||
// 关联信息
|
||||
Category *CategoryInfoResponse `json:"category,omitempty" comment:"分类信息"`
|
||||
Category *CategoryInfoResponse `json:"category,omitempty" comment:"一级分类信息"`
|
||||
SubCategory *SubCategoryInfoResponse `json:"sub_category,omitempty" comment:"二级分类信息"`
|
||||
|
||||
// 组合包信息
|
||||
PackageItems []*PackageItemResponse `json:"package_items,omitempty" comment:"组合包项目列表"`
|
||||
@@ -91,19 +93,20 @@ type ProductStatsResponse struct {
|
||||
|
||||
// ProductAdminInfoResponse 管理员产品详情响应
|
||||
type ProductAdminInfoResponse struct {
|
||||
ID string `json:"id" comment:"产品ID"`
|
||||
OldID *string `json:"old_id,omitempty" comment:"旧产品ID"`
|
||||
Name string `json:"name" comment:"产品名称"`
|
||||
Code string `json:"code" comment:"产品编号"`
|
||||
Description string `json:"description" comment:"产品简介"`
|
||||
Content string `json:"content" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" comment:"产品分类ID"`
|
||||
Price float64 `json:"price" comment:"产品价格"`
|
||||
CostPrice float64 `json:"cost_price" comment:"成本价"`
|
||||
Remark string `json:"remark" comment:"备注"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否可见"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
ID string `json:"id" comment:"产品ID"`
|
||||
OldID *string `json:"old_id,omitempty" comment:"旧产品ID"`
|
||||
Name string `json:"name" comment:"产品名称"`
|
||||
Code string `json:"code" comment:"产品编号"`
|
||||
Description string `json:"description" comment:"产品简介"`
|
||||
Content string `json:"content" comment:"产品内容"`
|
||||
CategoryID string `json:"category_id" comment:"一级分类ID"`
|
||||
SubCategoryID *string `json:"sub_category_id,omitempty" comment:"二级分类ID"`
|
||||
Price float64 `json:"price" comment:"产品价格"`
|
||||
CostPrice float64 `json:"cost_price" comment:"成本价"`
|
||||
Remark string `json:"remark" comment:"备注"`
|
||||
IsEnabled bool `json:"is_enabled" comment:"是否启用"`
|
||||
IsVisible bool `json:"is_visible" comment:"是否可见"`
|
||||
IsPackage bool `json:"is_package" comment:"是否组合包"`
|
||||
|
||||
// UI组件相关字段
|
||||
SellUIComponent bool `json:"sell_ui_component" comment:"是否出售UI组件"`
|
||||
@@ -115,7 +118,8 @@ type ProductAdminInfoResponse struct {
|
||||
SEOKeywords string `json:"seo_keywords" comment:"SEO关键词"`
|
||||
|
||||
// 关联信息
|
||||
Category *CategoryInfoResponse `json:"category,omitempty" comment:"分类信息"`
|
||||
Category *CategoryInfoResponse `json:"category,omitempty" comment:"一级分类信息"`
|
||||
SubCategory *SubCategoryInfoResponse `json:"sub_category,omitempty" comment:"二级分类信息"`
|
||||
|
||||
// 组合包信息
|
||||
PackageItems []*PackageItemResponse `json:"package_items,omitempty" comment:"组合包项目列表"`
|
||||
|
||||
@@ -50,7 +50,7 @@ func NewProductApplicationService(
|
||||
}
|
||||
|
||||
// CreateProduct 创建产品
|
||||
// 业务流程<EFBFBD>?. 构建产品实体 2. 创建产品
|
||||
// 业务流程:1. 构建产品实体 2. 创建产品
|
||||
func (s *ProductApplicationServiceImpl) CreateProduct(ctx context.Context, cmd *commands.CreateProductCommand) (*responses.ProductAdminInfoResponse, error) {
|
||||
// 1. 构建产品实体
|
||||
product := &entities.Product{
|
||||
@@ -59,6 +59,7 @@ func (s *ProductApplicationServiceImpl) CreateProduct(ctx context.Context, cmd *
|
||||
Description: cmd.Description,
|
||||
Content: cmd.Content,
|
||||
CategoryID: cmd.CategoryID,
|
||||
SubCategoryID: cmd.SubCategoryID,
|
||||
Price: decimal.NewFromFloat(cmd.Price),
|
||||
CostPrice: decimal.NewFromFloat(cmd.CostPrice),
|
||||
Remark: cmd.Remark,
|
||||
@@ -97,6 +98,7 @@ func (s *ProductApplicationServiceImpl) UpdateProduct(ctx context.Context, cmd *
|
||||
existingProduct.Description = cmd.Description
|
||||
existingProduct.Content = cmd.Content
|
||||
existingProduct.CategoryID = cmd.CategoryID
|
||||
existingProduct.SubCategoryID = cmd.SubCategoryID
|
||||
existingProduct.Price = decimal.NewFromFloat(cmd.Price)
|
||||
existingProduct.CostPrice = decimal.NewFromFloat(cmd.CostPrice)
|
||||
existingProduct.Remark = cmd.Remark
|
||||
@@ -490,30 +492,36 @@ func (s *ProductApplicationServiceImpl) GetProductByIDForUser(ctx context.Contex
|
||||
// convertToProductInfoResponse 转换为产品信息响应
|
||||
func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *entities.Product) *responses.ProductInfoResponse {
|
||||
response := &responses.ProductInfoResponse{
|
||||
ID: product.ID,
|
||||
OldID: product.OldID,
|
||||
Name: product.Name,
|
||||
Code: product.Code,
|
||||
Description: product.Description,
|
||||
Content: product.Content,
|
||||
CategoryID: product.CategoryID,
|
||||
Price: product.Price.InexactFloat64(),
|
||||
IsEnabled: product.IsEnabled,
|
||||
IsPackage: product.IsPackage,
|
||||
SellUIComponent: product.SellUIComponent,
|
||||
ID: product.ID,
|
||||
OldID: product.OldID,
|
||||
Name: product.Name,
|
||||
Code: product.Code,
|
||||
Description: product.Description,
|
||||
Content: product.Content,
|
||||
CategoryID: product.CategoryID,
|
||||
SubCategoryID: product.SubCategoryID,
|
||||
Price: product.Price.InexactFloat64(),
|
||||
IsEnabled: product.IsEnabled,
|
||||
IsPackage: product.IsPackage,
|
||||
SellUIComponent: product.SellUIComponent,
|
||||
UIComponentPrice: product.UIComponentPrice.InexactFloat64(),
|
||||
SEOTitle: product.SEOTitle,
|
||||
SEODescription: product.SEODescription,
|
||||
SEOKeywords: product.SEOKeywords,
|
||||
CreatedAt: product.CreatedAt,
|
||||
UpdatedAt: product.UpdatedAt,
|
||||
SEOTitle: product.SEOTitle,
|
||||
SEODescription: product.SEODescription,
|
||||
SEOKeywords: product.SEOKeywords,
|
||||
CreatedAt: product.CreatedAt,
|
||||
UpdatedAt: product.UpdatedAt,
|
||||
}
|
||||
|
||||
// 添加分类信息
|
||||
// 添加一级分类信息
|
||||
if product.Category != nil {
|
||||
response.Category = s.convertToCategoryInfoResponse(product.Category)
|
||||
}
|
||||
|
||||
// 添加二级分类信息
|
||||
if product.SubCategory != nil {
|
||||
response.SubCategory = s.convertToSubCategoryInfoResponse(product.SubCategory)
|
||||
}
|
||||
|
||||
// 转换组合包项目信息
|
||||
if product.IsPackage && len(product.PackageItems) > 0 {
|
||||
response.PackageItems = make([]*responses.PackageItemResponse, len(product.PackageItems))
|
||||
@@ -536,33 +544,39 @@ func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *en
|
||||
// convertToProductAdminInfoResponse 转换为管理员产品信息响应
|
||||
func (s *ProductApplicationServiceImpl) convertToProductAdminInfoResponse(product *entities.Product) *responses.ProductAdminInfoResponse {
|
||||
response := &responses.ProductAdminInfoResponse{
|
||||
ID: product.ID,
|
||||
OldID: product.OldID,
|
||||
Name: product.Name,
|
||||
Code: product.Code,
|
||||
Description: product.Description,
|
||||
Content: product.Content,
|
||||
CategoryID: product.CategoryID,
|
||||
Price: product.Price.InexactFloat64(),
|
||||
CostPrice: product.CostPrice.InexactFloat64(),
|
||||
Remark: product.Remark,
|
||||
IsEnabled: product.IsEnabled,
|
||||
IsVisible: product.IsVisible, // 管理员可以看到可见状态
|
||||
IsPackage: product.IsPackage,
|
||||
SellUIComponent: product.SellUIComponent,
|
||||
ID: product.ID,
|
||||
OldID: product.OldID,
|
||||
Name: product.Name,
|
||||
Code: product.Code,
|
||||
Description: product.Description,
|
||||
Content: product.Content,
|
||||
CategoryID: product.CategoryID,
|
||||
SubCategoryID: product.SubCategoryID,
|
||||
Price: product.Price.InexactFloat64(),
|
||||
CostPrice: product.CostPrice.InexactFloat64(),
|
||||
Remark: product.Remark,
|
||||
IsEnabled: product.IsEnabled,
|
||||
IsVisible: product.IsVisible, // 管理员可以看到可见状态
|
||||
IsPackage: product.IsPackage,
|
||||
SellUIComponent: product.SellUIComponent,
|
||||
UIComponentPrice: product.UIComponentPrice.InexactFloat64(),
|
||||
SEOTitle: product.SEOTitle,
|
||||
SEODescription: product.SEODescription,
|
||||
SEOKeywords: product.SEOKeywords,
|
||||
CreatedAt: product.CreatedAt,
|
||||
UpdatedAt: product.UpdatedAt,
|
||||
SEOTitle: product.SEOTitle,
|
||||
SEODescription: product.SEODescription,
|
||||
SEOKeywords: product.SEOKeywords,
|
||||
CreatedAt: product.CreatedAt,
|
||||
UpdatedAt: product.UpdatedAt,
|
||||
}
|
||||
|
||||
// 添加分类信息
|
||||
// 添加一级分类信息
|
||||
if product.Category != nil {
|
||||
response.Category = s.convertToCategoryInfoResponse(product.Category)
|
||||
}
|
||||
|
||||
// 添加二级分类信息
|
||||
if product.SubCategory != nil {
|
||||
response.SubCategory = s.convertToSubCategoryInfoResponse(product.SubCategory)
|
||||
}
|
||||
|
||||
// 转换组合包项目信息
|
||||
if product.IsPackage && len(product.PackageItems) > 0 {
|
||||
response.PackageItems = make([]*responses.PackageItemResponse, len(product.PackageItems))
|
||||
@@ -594,6 +608,28 @@ func (s *ProductApplicationServiceImpl) convertToCategoryInfoResponse(category *
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProductApplicationServiceImpl) convertToSubCategoryInfoResponse(subCategory *entities.ProductSubCategory) *responses.SubCategoryInfoResponse {
|
||||
response := &responses.SubCategoryInfoResponse{
|
||||
ID: subCategory.ID,
|
||||
Name: subCategory.Name,
|
||||
Code: subCategory.Code,
|
||||
Description: subCategory.Description,
|
||||
CategoryID: subCategory.CategoryID,
|
||||
Sort: subCategory.Sort,
|
||||
IsEnabled: subCategory.IsEnabled,
|
||||
IsVisible: subCategory.IsVisible,
|
||||
CreatedAt: subCategory.CreatedAt,
|
||||
UpdatedAt: subCategory.UpdatedAt,
|
||||
}
|
||||
|
||||
// 添加一级分类信息
|
||||
if subCategory.Category != nil {
|
||||
response.Category = s.convertToCategoryInfoResponse(subCategory.Category)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// GetProductApiConfig 获取产品API配置
|
||||
func (s *ProductApplicationServiceImpl) GetProductApiConfig(ctx context.Context, productID string) (*responses.ProductApiConfigResponse, error) {
|
||||
return s.productApiConfigAppService.GetProductApiConfig(ctx, productID)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/application/product/dto/commands"
|
||||
"tyapi-server/internal/application/product/dto/queries"
|
||||
"tyapi-server/internal/application/product/dto/responses"
|
||||
)
|
||||
|
||||
// SubCategoryApplicationService 二级分类应用服务接口
|
||||
type SubCategoryApplicationService interface {
|
||||
// 二级分类管理
|
||||
CreateSubCategory(ctx context.Context, cmd *commands.CreateSubCategoryCommand) error
|
||||
UpdateSubCategory(ctx context.Context, cmd *commands.UpdateSubCategoryCommand) error
|
||||
DeleteSubCategory(ctx context.Context, cmd *commands.DeleteSubCategoryCommand) error
|
||||
|
||||
GetSubCategoryByID(ctx context.Context, query *queries.GetSubCategoryQuery) (*responses.SubCategoryInfoResponse, error)
|
||||
ListSubCategories(ctx context.Context, query *queries.ListSubCategoriesQuery) (*responses.SubCategoryListResponse, error)
|
||||
ListSubCategoriesByCategoryID(ctx context.Context, categoryID string) ([]*responses.SubCategorySimpleResponse, error)
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
package product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"tyapi-server/internal/application/product/dto/commands"
|
||||
"tyapi-server/internal/application/product/dto/queries"
|
||||
"tyapi-server/internal/application/product/dto/responses"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
"tyapi-server/internal/domains/product/repositories"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// SubCategoryApplicationServiceImpl 二级分类应用服务实现
|
||||
type SubCategoryApplicationServiceImpl struct {
|
||||
categoryRepo repositories.ProductCategoryRepository
|
||||
subCategoryRepo repositories.ProductSubCategoryRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewSubCategoryApplicationService 创建二级分类应用服务
|
||||
func NewSubCategoryApplicationService(
|
||||
categoryRepo repositories.ProductCategoryRepository,
|
||||
subCategoryRepo repositories.ProductSubCategoryRepository,
|
||||
logger *zap.Logger,
|
||||
) SubCategoryApplicationService {
|
||||
return &SubCategoryApplicationServiceImpl{
|
||||
categoryRepo: categoryRepo,
|
||||
subCategoryRepo: subCategoryRepo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSubCategory 创建二级分类
|
||||
func (s *SubCategoryApplicationServiceImpl) CreateSubCategory(ctx context.Context, cmd *commands.CreateSubCategoryCommand) error {
|
||||
// 1. 参数验证
|
||||
if err := s.validateCreateSubCategory(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 验证一级分类是否存在
|
||||
category, err := s.categoryRepo.GetByID(ctx, cmd.CategoryID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("一级分类不存在: %w", err)
|
||||
}
|
||||
if !category.IsValid() {
|
||||
return errors.New("一级分类已禁用或删除")
|
||||
}
|
||||
|
||||
// 3. 验证二级分类编号唯一性
|
||||
if err := s.validateSubCategoryCode(cmd.Code, "", cmd.CategoryID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. 创建二级分类实体
|
||||
subCategory := &entities.ProductSubCategory{
|
||||
Name: cmd.Name,
|
||||
Code: cmd.Code,
|
||||
Description: cmd.Description,
|
||||
CategoryID: cmd.CategoryID,
|
||||
Sort: cmd.Sort,
|
||||
IsEnabled: cmd.IsEnabled,
|
||||
IsVisible: cmd.IsVisible,
|
||||
}
|
||||
|
||||
// 5. 保存到仓储
|
||||
createdSubCategory, err := s.subCategoryRepo.Create(ctx, *subCategory)
|
||||
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", createdSubCategory.ID), zap.String("code", cmd.Code))
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateSubCategory 更新二级分类
|
||||
func (s *SubCategoryApplicationServiceImpl) UpdateSubCategory(ctx context.Context, cmd *commands.UpdateSubCategoryCommand) error {
|
||||
// 1. 参数验证
|
||||
if err := s.validateUpdateSubCategory(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 获取现有二级分类
|
||||
existingSubCategory, err := s.subCategoryRepo.GetByID(ctx, cmd.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("二级分类不存在: %w", err)
|
||||
}
|
||||
|
||||
// 3. 验证一级分类是否存在
|
||||
category, err := s.categoryRepo.GetByID(ctx, cmd.CategoryID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("一级分类不存在: %w", err)
|
||||
}
|
||||
if !category.IsValid() {
|
||||
return errors.New("一级分类已禁用或删除")
|
||||
}
|
||||
|
||||
// 4. 验证二级分类编号唯一性(排除当前分类)
|
||||
if err := s.validateSubCategoryCode(cmd.Code, cmd.ID, cmd.CategoryID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. 更新二级分类信息
|
||||
existingSubCategory.Name = cmd.Name
|
||||
existingSubCategory.Code = cmd.Code
|
||||
existingSubCategory.Description = cmd.Description
|
||||
existingSubCategory.CategoryID = cmd.CategoryID
|
||||
existingSubCategory.Sort = cmd.Sort
|
||||
existingSubCategory.IsEnabled = cmd.IsEnabled
|
||||
existingSubCategory.IsVisible = cmd.IsVisible
|
||||
|
||||
// 6. 保存到仓储
|
||||
if err := s.subCategoryRepo.Update(ctx, *existingSubCategory); 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
|
||||
}
|
||||
|
||||
// DeleteSubCategory 删除二级分类
|
||||
func (s *SubCategoryApplicationServiceImpl) DeleteSubCategory(ctx context.Context, cmd *commands.DeleteSubCategoryCommand) error {
|
||||
// 1. 检查二级分类是否存在
|
||||
existingSubCategory, err := s.subCategoryRepo.GetByID(ctx, cmd.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("二级分类不存在: %w", err)
|
||||
}
|
||||
|
||||
// 2. 删除二级分类
|
||||
if err := s.subCategoryRepo.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", existingSubCategory.Code))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSubCategoryByID 根据ID获取二级分类
|
||||
func (s *SubCategoryApplicationServiceImpl) GetSubCategoryByID(ctx context.Context, query *queries.GetSubCategoryQuery) (*responses.SubCategoryInfoResponse, error) {
|
||||
subCategory, err := s.subCategoryRepo.GetByID(ctx, query.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("二级分类不存在: %w", err)
|
||||
}
|
||||
|
||||
// 加载一级分类信息
|
||||
if subCategory.CategoryID != "" {
|
||||
category, err := s.categoryRepo.GetByID(ctx, subCategory.CategoryID)
|
||||
if err == nil {
|
||||
subCategory.Category = &category
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为响应对象
|
||||
response := s.convertToSubCategoryInfoResponse(subCategory)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ListSubCategories 获取二级分类列表
|
||||
func (s *SubCategoryApplicationServiceImpl) ListSubCategories(ctx context.Context, query *queries.ListSubCategoriesQuery) (*responses.SubCategoryListResponse, error) {
|
||||
// 构建查询条件
|
||||
categoryID := query.CategoryID
|
||||
isEnabled := query.IsEnabled
|
||||
isVisible := query.IsVisible
|
||||
|
||||
var subCategories []*entities.ProductSubCategory
|
||||
var err error
|
||||
|
||||
// 根据条件查询
|
||||
if categoryID != "" {
|
||||
// 按一级分类查询
|
||||
subCategories, err = s.subCategoryRepo.FindByCategoryID(ctx, categoryID)
|
||||
} else {
|
||||
// 查询所有二级分类
|
||||
subCategories, err = s.subCategoryRepo.List(ctx)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.logger.Error("获取二级分类列表失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("获取二级分类列表失败: %w", err)
|
||||
}
|
||||
|
||||
// 过滤状态
|
||||
filteredSubCategories := make([]*entities.ProductSubCategory, 0)
|
||||
for _, subCategory := range subCategories {
|
||||
if isEnabled != nil && *isEnabled != subCategory.IsEnabled {
|
||||
continue
|
||||
}
|
||||
if isVisible != nil && *isVisible != subCategory.IsVisible {
|
||||
continue
|
||||
}
|
||||
filteredSubCategories = append(filteredSubCategories, subCategory)
|
||||
}
|
||||
|
||||
// 加载一级分类信息
|
||||
for _, subCategory := range filteredSubCategories {
|
||||
if subCategory.CategoryID != "" {
|
||||
category, err := s.categoryRepo.GetByID(ctx, subCategory.CategoryID)
|
||||
if err == nil {
|
||||
subCategory.Category = &category
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为响应对象
|
||||
items := make([]responses.SubCategoryInfoResponse, len(filteredSubCategories))
|
||||
for i, subCategory := range filteredSubCategories {
|
||||
items[i] = *s.convertToSubCategoryInfoResponse(subCategory)
|
||||
}
|
||||
|
||||
return &responses.SubCategoryListResponse{
|
||||
Total: int64(len(items)),
|
||||
Page: query.Page,
|
||||
Size: query.PageSize,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListSubCategoriesByCategoryID 根据一级分类ID获取二级分类列表
|
||||
func (s *SubCategoryApplicationServiceImpl) ListSubCategoriesByCategoryID(ctx context.Context, categoryID string) ([]*responses.SubCategorySimpleResponse, error) {
|
||||
subCategories, err := s.subCategoryRepo.FindByCategoryID(ctx, categoryID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取二级分类列表失败: %w", err)
|
||||
}
|
||||
|
||||
// 转换为响应对象
|
||||
items := make([]*responses.SubCategorySimpleResponse, len(subCategories))
|
||||
for i, subCategory := range subCategories {
|
||||
items[i] = &responses.SubCategorySimpleResponse{
|
||||
ID: subCategory.ID,
|
||||
Name: subCategory.Name,
|
||||
Code: subCategory.Code,
|
||||
CategoryID: subCategory.CategoryID,
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// convertToSubCategoryInfoResponse 转换为二级分类信息响应
|
||||
func (s *SubCategoryApplicationServiceImpl) convertToSubCategoryInfoResponse(subCategory *entities.ProductSubCategory) *responses.SubCategoryInfoResponse {
|
||||
response := &responses.SubCategoryInfoResponse{
|
||||
ID: subCategory.ID,
|
||||
Name: subCategory.Name,
|
||||
Code: subCategory.Code,
|
||||
Description: subCategory.Description,
|
||||
CategoryID: subCategory.CategoryID,
|
||||
Sort: subCategory.Sort,
|
||||
IsEnabled: subCategory.IsEnabled,
|
||||
IsVisible: subCategory.IsVisible,
|
||||
CreatedAt: subCategory.CreatedAt,
|
||||
UpdatedAt: subCategory.UpdatedAt,
|
||||
}
|
||||
|
||||
// 添加一级分类信息
|
||||
if subCategory.Category != nil {
|
||||
response.Category = &responses.CategoryInfoResponse{
|
||||
ID: subCategory.Category.ID,
|
||||
Name: subCategory.Category.Name,
|
||||
Description: subCategory.Category.Description,
|
||||
Sort: subCategory.Category.Sort,
|
||||
IsEnabled: subCategory.Category.IsEnabled,
|
||||
IsVisible: subCategory.Category.IsVisible,
|
||||
CreatedAt: subCategory.Category.CreatedAt,
|
||||
UpdatedAt: subCategory.Category.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// validateCreateSubCategory 验证创建二级分类参数
|
||||
func (s *SubCategoryApplicationServiceImpl) validateCreateSubCategory(cmd *commands.CreateSubCategoryCommand) error {
|
||||
if cmd.Name == "" {
|
||||
return errors.New("二级分类名称不能为空")
|
||||
}
|
||||
if cmd.Code == "" {
|
||||
return errors.New("二级分类编号不能为空")
|
||||
}
|
||||
if cmd.CategoryID == "" {
|
||||
return errors.New("一级分类ID不能为空")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateUpdateSubCategory 验证更新二级分类参数
|
||||
func (s *SubCategoryApplicationServiceImpl) validateUpdateSubCategory(cmd *commands.UpdateSubCategoryCommand) error {
|
||||
if cmd.ID == "" {
|
||||
return errors.New("二级分类ID不能为空")
|
||||
}
|
||||
if cmd.Name == "" {
|
||||
return errors.New("二级分类名称不能为空")
|
||||
}
|
||||
if cmd.Code == "" {
|
||||
return errors.New("二级分类编号不能为空")
|
||||
}
|
||||
if cmd.CategoryID == "" {
|
||||
return errors.New("一级分类ID不能为空")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateSubCategoryCode 验证二级分类编号唯一性
|
||||
func (s *SubCategoryApplicationServiceImpl) validateSubCategoryCode(code, excludeID, categoryID string) error {
|
||||
if code == "" {
|
||||
return errors.New("二级分类编号不能为空")
|
||||
}
|
||||
|
||||
existingSubCategory, err := s.subCategoryRepo.FindByCode(context.Background(), code)
|
||||
if err == nil && existingSubCategory != nil && existingSubCategory.ID != excludeID {
|
||||
// 如果指定了分类ID,检查是否在同一分类下
|
||||
if categoryID == "" || existingSubCategory.CategoryID == categoryID {
|
||||
return errors.New("二级分类编号已存在")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user