This commit is contained in:
2026-01-09 15:58:09 +08:00
parent bd76520d22
commit ead5f17b7c
18 changed files with 1175 additions and 121 deletions

View File

@@ -10,19 +10,20 @@ import (
// Product 产品实体
type Product struct {
ID string `gorm:"primaryKey;type:varchar(36)" comment:"产品ID"`
OldID *string `gorm:"type:varchar(36);index" comment:"旧产品ID用于兼容"`
Name string `gorm:"type:varchar(100);not null" comment:"产品名称"`
Code string `gorm:"type:varchar(50);uniqueIndex;not null" comment:"产品编号"`
Description string `gorm:"type:text" comment:"产品简介"`
Content string `gorm:"type:text" comment:"产品内容"`
CategoryID string `gorm:"type:varchar(36);not null" comment:"产品分类ID"`
Price decimal.Decimal `gorm:"type:decimal(10,2);not null;default:0" comment:"产品价格"`
CostPrice decimal.Decimal `gorm:"type:decimal(10,2);default:0" comment:"成本价"`
Remark string `gorm:"type:text" comment:"备注"`
IsEnabled bool `gorm:"default:false" comment:"是否启用"`
IsVisible bool `gorm:"default:false" comment:"是否展示"`
IsPackage bool `gorm:"default:false" comment:"是否组合包"`
ID string `gorm:"primaryKey;type:varchar(36)" comment:"产品ID"`
OldID *string `gorm:"type:varchar(36);index" comment:"旧产品ID用于兼容"`
Name string `gorm:"type:varchar(100);not null" comment:"产品名称"`
Code string `gorm:"type:varchar(50);uniqueIndex;not null" comment:"产品编号"`
Description string `gorm:"type:text" comment:"产品简介"`
Content string `gorm:"type:text" comment:"产品内容"`
CategoryID string `gorm:"type:varchar(36);not null" comment:"一级分类ID"`
SubCategoryID *string `gorm:"type:varchar(36);index" comment:"二级分类ID"`
Price decimal.Decimal `gorm:"type:decimal(10,2);not null;default:0" comment:"产品价格"`
CostPrice decimal.Decimal `gorm:"type:decimal(10,2);default:0" comment:"成本价"`
Remark string `gorm:"type:text" comment:"备注"`
IsEnabled bool `gorm:"default:false" comment:"是否启用"`
IsVisible bool `gorm:"default:false" comment:"是否展示"`
IsPackage bool `gorm:"default:false" comment:"是否组合包"`
// 组合包相关关联
PackageItems []*ProductPackageItem `gorm:"foreignKey:PackageID" comment:"组合包项目列表"`
// UI组件相关字段
@@ -34,7 +35,8 @@ type Product struct {
SEOKeywords string `gorm:"type:text" comment:"SEO关键词"`
// 关联关系
Category *ProductCategory `gorm:"foreignKey:CategoryID" comment:"产品分类"`
Category *ProductCategory `gorm:"foreignKey:CategoryID" comment:"一级分类"`
SubCategory *ProductSubCategory `gorm:"foreignKey:SubCategoryID" comment:"二级分类"`
Documentation *ProductDocumentation `gorm:"foreignKey:ProductID" comment:"产品文档"`
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
@@ -118,3 +120,34 @@ func (p *Product) GetOldID() string {
func (p *Product) HasOldID() bool {
return p.OldID != nil && *p.OldID != ""
}
// HasSubCategory 检查是否有二级分类
func (p *Product) HasSubCategory() bool {
return p.SubCategoryID != nil && *p.SubCategoryID != ""
}
// GetFullCategoryPath 获取完整分类路径(一级分类/二级分类)
func (p *Product) GetFullCategoryPath() string {
if p.Category == nil {
return ""
}
if p.SubCategory != nil {
return p.Category.Name + " / " + p.SubCategory.Name
}
return p.Category.Name
}
// GetFullCategoryCode 获取完整分类编号(一级分类编号.二级分类编号)
func (p *Product) GetFullCategoryCode() string {
if p.Category == nil {
return ""
}
if p.SubCategory != nil {
return p.Category.Code + "." + p.SubCategory.Code
}
return p.Category.Code
}

View File

@@ -18,7 +18,8 @@ type ProductCategory struct {
IsVisible bool `gorm:"default:true" comment:"是否展示"`
// 关联关系
Products []Product `gorm:"foreignKey:CategoryID" comment:"产品列表"`
Products []Product `gorm:"foreignKey:CategoryID" comment:"产品列表"`
SubCategories []ProductSubCategory `gorm:"foreignKey:CategoryID" comment:"二级分类列表"`
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`

View File

@@ -0,0 +1,82 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// ProductSubCategory 产品二级分类实体
type ProductSubCategory struct {
ID string `gorm:"primaryKey;type:varchar(36)" comment:"二级分类ID"`
Name string `gorm:"type:varchar(100);not null" comment:"二级分类名称"`
Code string `gorm:"type:varchar(50);uniqueIndex;not null" comment:"二级分类编号"`
Description string `gorm:"type:text" comment:"二级分类描述"`
CategoryID string `gorm:"type:varchar(36);not null;index" comment:"一级分类ID"`
Sort int `gorm:"default:0" comment:"排序"`
IsEnabled bool `gorm:"default:true" comment:"是否启用"`
IsVisible bool `gorm:"default:true" comment:"是否展示"`
// 关联关系
Category *ProductCategory `gorm:"foreignKey:CategoryID" comment:"一级分类"`
Products []Product `gorm:"foreignKey:SubCategoryID" comment:"产品列表"`
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
}
// BeforeCreate GORM钩子创建前自动生成UUID
func (psc *ProductSubCategory) BeforeCreate(tx *gorm.DB) error {
if psc.ID == "" {
psc.ID = uuid.New().String()
}
return nil
}
// IsValid 检查二级分类是否有效
func (psc *ProductSubCategory) IsValid() bool {
return psc.DeletedAt.Time.IsZero() && psc.IsEnabled
}
// IsVisibleToUser 检查二级分类是否对用户可见
func (psc *ProductSubCategory) IsVisibleToUser() bool {
return psc.IsValid() && psc.IsVisible
}
// Enable 启用二级分类
func (psc *ProductSubCategory) Enable() {
psc.IsEnabled = true
}
// Disable 禁用二级分类
func (psc *ProductSubCategory) Disable() {
psc.IsEnabled = false
}
// Show 显示二级分类
func (psc *ProductSubCategory) Show() {
psc.IsVisible = true
}
// Hide 隐藏二级分类
func (psc *ProductSubCategory) Hide() {
psc.IsVisible = false
}
// GetFullPath 获取完整路径(一级分类/二级分类)
func (psc *ProductSubCategory) GetFullPath() string {
if psc.Category != nil {
return psc.Category.Name + " / " + psc.Name
}
return psc.Name
}
// GetFullCode 获取完整编号(一级分类编号.二级分类编号)
func (psc *ProductSubCategory) GetFullCode() string {
if psc.Category != nil {
return psc.Category.Code + "." + psc.Code
}
return psc.Code
}

View File

@@ -0,0 +1,22 @@
package repositories
import (
"context"
"tyapi-server/internal/domains/product/entities"
)
// ProductSubCategoryRepository 产品二级分类仓储接口
type ProductSubCategoryRepository interface {
// 基础CRUD方法
GetByID(ctx context.Context, id string) (*entities.ProductSubCategory, error)
Create(ctx context.Context, category entities.ProductSubCategory) (*entities.ProductSubCategory, error)
Update(ctx context.Context, category entities.ProductSubCategory) error
Delete(ctx context.Context, id string) error
List(ctx context.Context) ([]*entities.ProductSubCategory, error)
// 查询方法
FindByCode(ctx context.Context, code string) (*entities.ProductSubCategory, error)
FindByCategoryID(ctx context.Context, categoryID string) ([]*entities.ProductSubCategory, error)
FindVisible(ctx context.Context) ([]*entities.ProductSubCategory, error)
FindEnabled(ctx context.Context) ([]*entities.ProductSubCategory, error)
}

View File

@@ -18,21 +18,24 @@ import (
// ProductManagementService 产品管理领域服务
// 负责产品的基本管理操作,包括创建、查询、更新等
type ProductManagementService struct {
productRepo repositories.ProductRepository
categoryRepo repositories.ProductCategoryRepository
logger *zap.Logger
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,
logger: logger,
productRepo: productRepo,
categoryRepo: categoryRepo,
subCategoryRepo: subCategoryRepo,
logger: logger,
}
}
@@ -306,6 +309,21 @@ func (s *ProductManagementService) ValidateProduct(product *entities.Product) er
}
}
// 验证二级分类是否存在(如果设置了二级分类)
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
}