temp
This commit is contained in:
93
internal/domains/product/entities/product.go
Normal file
93
internal/domains/product/entities/product.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Product 产品实体
|
||||
type Product 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:"产品简介"`
|
||||
Content string `gorm:"type:longtext" comment:"产品内容"`
|
||||
CategoryID string `gorm:"type:varchar(36);not null" comment:"产品分类ID"`
|
||||
Price float64 `gorm:"type:decimal(10,2);not null;default:0" comment:"产品价格"`
|
||||
IsEnabled bool `gorm:"default:true" comment:"是否启用"`
|
||||
IsVisible bool `gorm:"default:true" comment:"是否展示"`
|
||||
IsPackage bool `gorm:"default:false" comment:"是否组合包"`
|
||||
|
||||
// SEO信息
|
||||
SEOTitle string `gorm:"type:varchar(200)" comment:"SEO标题"`
|
||||
SEODescription string `gorm:"type:text" comment:"SEO描述"`
|
||||
SEOKeywords string `gorm:"type:text" comment:"SEO关键词"`
|
||||
|
||||
// 关联关系
|
||||
Category *ProductCategory `gorm:"foreignKey:CategoryID" comment:"产品分类"`
|
||||
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
// IsValid 检查产品是否有效
|
||||
func (p *Product) IsValid() bool {
|
||||
return p.DeletedAt.Time.IsZero() && p.IsEnabled
|
||||
}
|
||||
|
||||
// IsVisibleToUser 检查产品是否对用户可见
|
||||
func (p *Product) IsVisibleToUser() bool {
|
||||
return p.IsValid() && p.IsVisible
|
||||
}
|
||||
|
||||
// CanBeSubscribed 检查产品是否可以订阅
|
||||
func (p *Product) CanBeSubscribed() bool {
|
||||
return p.IsValid() && p.IsVisible
|
||||
}
|
||||
|
||||
// GetDisplayPrice 获取显示价格
|
||||
func (p *Product) GetDisplayPrice() float64 {
|
||||
if p.Price < 0 {
|
||||
return 0
|
||||
}
|
||||
return p.Price
|
||||
}
|
||||
|
||||
// UpdateSEO 更新SEO信息
|
||||
func (p *Product) UpdateSEO(title, description, keywords string) {
|
||||
p.SEOTitle = title
|
||||
p.SEODescription = description
|
||||
p.SEOKeywords = keywords
|
||||
}
|
||||
|
||||
// Enable 启用产品
|
||||
func (p *Product) Enable() {
|
||||
p.IsEnabled = true
|
||||
}
|
||||
|
||||
// Disable 禁用产品
|
||||
func (p *Product) Disable() {
|
||||
p.IsEnabled = false
|
||||
}
|
||||
|
||||
// Show 显示产品
|
||||
func (p *Product) Show() {
|
||||
p.IsVisible = true
|
||||
}
|
||||
|
||||
// Hide 隐藏产品
|
||||
func (p *Product) Hide() {
|
||||
p.IsVisible = false
|
||||
}
|
||||
|
||||
// SetAsPackage 设置为组合包
|
||||
func (p *Product) SetAsPackage() {
|
||||
p.IsPackage = true
|
||||
}
|
||||
|
||||
// SetAsSingle 设置为单个产品
|
||||
func (p *Product) SetAsSingle() {
|
||||
p.IsPackage = false
|
||||
}
|
||||
80
internal/domains/product/entities/product_category.go
Normal file
80
internal/domains/product/entities/product_category.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ProductCategory 产品分类实体
|
||||
type ProductCategory 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:"分类描述"`
|
||||
ParentID *string `gorm:"type:varchar(36)" comment:"父分类ID"`
|
||||
Level int `gorm:"default:1" comment:"分类层级"`
|
||||
Sort int `gorm:"default:0" comment:"排序"`
|
||||
IsEnabled bool `gorm:"default:true" comment:"是否启用"`
|
||||
IsVisible bool `gorm:"default:true" comment:"是否展示"`
|
||||
|
||||
// 关联关系
|
||||
Parent *ProductCategory `gorm:"foreignKey:ParentID" comment:"父分类"`
|
||||
Children []ProductCategory `gorm:"foreignKey:ParentID" comment:"子分类"`
|
||||
Products []Product `gorm:"foreignKey:CategoryID" comment:"产品列表"`
|
||||
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
// IsValid 检查分类是否有效
|
||||
func (pc *ProductCategory) IsValid() bool {
|
||||
return pc.DeletedAt.Time.IsZero() && pc.IsEnabled
|
||||
}
|
||||
|
||||
// IsVisibleToUser 检查分类是否对用户可见
|
||||
func (pc *ProductCategory) IsVisibleToUser() bool {
|
||||
return pc.IsValid() && pc.IsVisible
|
||||
}
|
||||
|
||||
// IsRoot 检查是否为根分类
|
||||
func (pc *ProductCategory) IsRoot() bool {
|
||||
return pc.ParentID == nil || *pc.ParentID == ""
|
||||
}
|
||||
|
||||
// IsLeaf 检查是否为叶子分类
|
||||
func (pc *ProductCategory) IsLeaf() bool {
|
||||
return len(pc.Children) == 0
|
||||
}
|
||||
|
||||
// GetFullPath 获取完整分类路径
|
||||
func (pc *ProductCategory) GetFullPath() string {
|
||||
if pc.IsRoot() {
|
||||
return pc.Name
|
||||
}
|
||||
if pc.Parent != nil {
|
||||
return pc.Parent.GetFullPath() + " > " + pc.Name
|
||||
}
|
||||
return pc.Name
|
||||
}
|
||||
|
||||
// Enable 启用分类
|
||||
func (pc *ProductCategory) Enable() {
|
||||
pc.IsEnabled = true
|
||||
}
|
||||
|
||||
// Disable 禁用分类
|
||||
func (pc *ProductCategory) Disable() {
|
||||
pc.IsEnabled = false
|
||||
}
|
||||
|
||||
// Show 显示分类
|
||||
func (pc *ProductCategory) Show() {
|
||||
pc.IsVisible = true
|
||||
}
|
||||
|
||||
// Hide 隐藏分类
|
||||
func (pc *ProductCategory) Hide() {
|
||||
pc.IsVisible = false
|
||||
}
|
||||
69
internal/domains/product/entities/product_documentation.go
Normal file
69
internal/domains/product/entities/product_documentation.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ProductDocumentation 产品文档实体
|
||||
type ProductDocumentation struct {
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"文档ID"`
|
||||
ProductID string `gorm:"type:varchar(36);not null;uniqueIndex" comment:"产品ID"`
|
||||
Title string `gorm:"type:varchar(200);not null" comment:"文档标题"`
|
||||
Content string `gorm:"type:longtext;not null" comment:"文档内容"`
|
||||
UsageGuide string `gorm:"type:longtext" comment:"使用指南"`
|
||||
APIDocs string `gorm:"type:longtext" comment:"API文档"`
|
||||
Examples string `gorm:"type:longtext" comment:"使用示例"`
|
||||
FAQ string `gorm:"type:longtext" comment:"常见问题"`
|
||||
Version string `gorm:"type:varchar(20);default:'1.0'" comment:"文档版本"`
|
||||
Published bool `gorm:"default:false" comment:"是否已发布"`
|
||||
|
||||
// 关联关系
|
||||
Product *Product `gorm:"foreignKey:ProductID" comment:"产品"`
|
||||
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
// IsValid 检查文档是否有效
|
||||
func (pd *ProductDocumentation) IsValid() bool {
|
||||
return pd.DeletedAt.Time.IsZero()
|
||||
}
|
||||
|
||||
// IsPublished 检查文档是否已发布
|
||||
func (pd *ProductDocumentation) IsPublished() bool {
|
||||
return pd.Published
|
||||
}
|
||||
|
||||
// Publish 发布文档
|
||||
func (pd *ProductDocumentation) Publish() {
|
||||
pd.Published = true
|
||||
}
|
||||
|
||||
// Unpublish 取消发布文档
|
||||
func (pd *ProductDocumentation) Unpublish() {
|
||||
pd.Published = false
|
||||
}
|
||||
|
||||
// UpdateContent 更新文档内容
|
||||
func (pd *ProductDocumentation) UpdateContent(title, content, usageGuide, apiDocs, examples, faq string) {
|
||||
pd.Title = title
|
||||
pd.Content = content
|
||||
pd.UsageGuide = usageGuide
|
||||
pd.APIDocs = apiDocs
|
||||
pd.Examples = examples
|
||||
pd.FAQ = faq
|
||||
}
|
||||
|
||||
// IncrementVersion 增加版本号
|
||||
func (pd *ProductDocumentation) IncrementVersion() {
|
||||
// 简单的版本号递增逻辑,实际项目中可能需要更复杂的版本管理
|
||||
if pd.Version == "" {
|
||||
pd.Version = "1.0"
|
||||
} else {
|
||||
// 这里可以实现更复杂的版本号递增逻辑
|
||||
pd.Version = pd.Version + ".1"
|
||||
}
|
||||
}
|
||||
59
internal/domains/product/entities/subscription.go
Normal file
59
internal/domains/product/entities/subscription.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// SubscriptionStatus 订阅状态枚举
|
||||
type SubscriptionStatus string
|
||||
|
||||
const (
|
||||
SubscriptionStatusActive SubscriptionStatus = "active" // 活跃
|
||||
SubscriptionStatusInactive SubscriptionStatus = "inactive" // 非活跃
|
||||
SubscriptionStatusExpired SubscriptionStatus = "expired" // 已过期
|
||||
SubscriptionStatusCanceled SubscriptionStatus = "canceled" // 已取消
|
||||
)
|
||||
|
||||
// Subscription 订阅实体
|
||||
type Subscription struct {
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"订阅ID"`
|
||||
UserID string `gorm:"type:varchar(36);not null;index" comment:"用户ID"`
|
||||
ProductID string `gorm:"type:varchar(36);not null;index" comment:"产品ID"`
|
||||
Status SubscriptionStatus `gorm:"type:varchar(20);not null;default:'active'" comment:"订阅状态"`
|
||||
Price float64 `gorm:"type:decimal(10,2);not null" comment:"订阅价格"`
|
||||
APIUsed int64 `gorm:"default:0" comment:"已使用API调用次数"`
|
||||
|
||||
// 关联关系
|
||||
Product *Product `gorm:"foreignKey:ProductID" comment:"产品"`
|
||||
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
// IsValid 检查订阅是否有效
|
||||
func (s *Subscription) IsValid() bool {
|
||||
return s.DeletedAt.Time.IsZero()
|
||||
}
|
||||
|
||||
// IncrementAPIUsage 增加API使用次数
|
||||
func (s *Subscription) IncrementAPIUsage(count int64) {
|
||||
s.APIUsed += count
|
||||
}
|
||||
|
||||
// Activate 激活订阅
|
||||
func (s *Subscription) Activate() {
|
||||
s.Status = SubscriptionStatusActive
|
||||
}
|
||||
|
||||
// Deactivate 停用订阅
|
||||
func (s *Subscription) Deactivate() {
|
||||
s.Status = SubscriptionStatusInactive
|
||||
}
|
||||
|
||||
// ResetAPIUsage 重置API使用次数
|
||||
func (s *Subscription) ResetAPIUsage() {
|
||||
s.APIUsed = 0
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
"tyapi-server/internal/domains/product/repositories/queries"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// ProductCategoryRepository 产品分类仓储接口
|
||||
type ProductCategoryRepository interface {
|
||||
interfaces.Repository[entities.ProductCategory]
|
||||
|
||||
// 基础查询方法
|
||||
FindByCode(ctx context.Context, code string) (*entities.ProductCategory, error)
|
||||
FindByParentID(ctx context.Context, parentID *string) ([]*entities.ProductCategory, error)
|
||||
FindRootCategories(ctx context.Context) ([]*entities.ProductCategory, error)
|
||||
FindVisible(ctx context.Context) ([]*entities.ProductCategory, error)
|
||||
FindEnabled(ctx context.Context) ([]*entities.ProductCategory, error)
|
||||
|
||||
// 复杂查询方法
|
||||
ListCategories(ctx context.Context, query *queries.ListCategoriesQuery) ([]*entities.ProductCategory, int64, error)
|
||||
GetCategoryTree(ctx context.Context) ([]*entities.ProductCategory, error)
|
||||
|
||||
// 业务查询方法
|
||||
FindCategoriesByLevel(ctx context.Context, level int) ([]*entities.ProductCategory, error)
|
||||
FindCategoryPath(ctx context.Context, categoryID string) ([]*entities.ProductCategory, error)
|
||||
|
||||
// 统计方法
|
||||
CountByParent(ctx context.Context, parentID *string) (int64, error)
|
||||
CountEnabled(ctx context.Context) (int64, error)
|
||||
CountVisible(ctx context.Context) (int64, error)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
"tyapi-server/internal/domains/product/repositories/queries"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// ProductRepository 产品仓储接口
|
||||
type ProductRepository interface {
|
||||
interfaces.Repository[entities.Product]
|
||||
|
||||
// 基础查询方法
|
||||
FindByCode(ctx context.Context, code string) (*entities.Product, error)
|
||||
FindByCategoryID(ctx context.Context, categoryID string) ([]*entities.Product, error)
|
||||
FindVisible(ctx context.Context) ([]*entities.Product, error)
|
||||
FindEnabled(ctx context.Context) ([]*entities.Product, error)
|
||||
|
||||
// 复杂查询方法
|
||||
ListProducts(ctx context.Context, query *queries.ListProductsQuery) ([]*entities.Product, int64, error)
|
||||
|
||||
// 业务查询方法
|
||||
FindSubscribableProducts(ctx context.Context, userID string) ([]*entities.Product, error)
|
||||
FindProductsByIDs(ctx context.Context, ids []string) ([]*entities.Product, error)
|
||||
|
||||
// 统计方法
|
||||
CountByCategory(ctx context.Context, categoryID string) (int64, error)
|
||||
CountEnabled(ctx context.Context) (int64, error)
|
||||
CountVisible(ctx context.Context) (int64, error)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package queries
|
||||
|
||||
// ListCategoriesQuery 分类列表查询
|
||||
type ListCategoriesQuery struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
ParentID *string `json:"parent_id"`
|
||||
Level *int `json:"level"`
|
||||
IsEnabled *bool `json:"is_enabled"`
|
||||
IsVisible *bool `json:"is_visible"`
|
||||
SortBy string `json:"sort_by"`
|
||||
SortOrder string `json:"sort_order"`
|
||||
}
|
||||
|
||||
// GetCategoryQuery 获取分类详情查询
|
||||
type GetCategoryQuery struct {
|
||||
ID string `json:"id"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// GetCategoryTreeQuery 获取分类树查询
|
||||
type GetCategoryTreeQuery struct {
|
||||
IncludeDisabled bool `json:"include_disabled"`
|
||||
IncludeHidden bool `json:"include_hidden"`
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package queries
|
||||
|
||||
// ListProductsQuery 产品列表查询
|
||||
type ListProductsQuery struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
Keyword string `json:"keyword"`
|
||||
CategoryID string `json:"category_id"`
|
||||
MinPrice *float64 `json:"min_price"`
|
||||
MaxPrice *float64 `json:"max_price"`
|
||||
IsEnabled *bool `json:"is_enabled"`
|
||||
IsVisible *bool `json:"is_visible"`
|
||||
IsPackage *bool `json:"is_package"`
|
||||
SortBy string `json:"sort_by"`
|
||||
SortOrder string `json:"sort_order"`
|
||||
}
|
||||
|
||||
// SearchProductsQuery 产品搜索查询
|
||||
type SearchProductsQuery struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
Keyword string `json:"keyword"`
|
||||
CategoryID string `json:"category_id"`
|
||||
MinPrice *float64 `json:"min_price"`
|
||||
MaxPrice *float64 `json:"max_price"`
|
||||
IsEnabled *bool `json:"is_enabled"`
|
||||
IsVisible *bool `json:"is_visible"`
|
||||
IsPackage *bool `json:"is_package"`
|
||||
SortBy string `json:"sort_by"`
|
||||
SortOrder string `json:"sort_order"`
|
||||
}
|
||||
|
||||
// GetProductQuery 获取产品详情查询
|
||||
type GetProductQuery struct {
|
||||
ID string `json:"id"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// GetProductsByIDsQuery 根据ID列表获取产品查询
|
||||
type GetProductsByIDsQuery struct {
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package queries
|
||||
|
||||
import "tyapi-server/internal/domains/product/entities"
|
||||
|
||||
// ListSubscriptionsQuery 订阅列表查询
|
||||
type ListSubscriptionsQuery struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
UserID string `json:"user_id"`
|
||||
ProductID string `json:"product_id"`
|
||||
Status entities.SubscriptionStatus `json:"status"`
|
||||
SortBy string `json:"sort_by"`
|
||||
SortOrder string `json:"sort_order"`
|
||||
}
|
||||
|
||||
// GetSubscriptionQuery 获取订阅详情查询
|
||||
type GetSubscriptionQuery struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// GetUserSubscriptionsQuery 获取用户订阅查询
|
||||
type GetUserSubscriptionsQuery struct {
|
||||
UserID string `json:"user_id"`
|
||||
Status *entities.SubscriptionStatus `json:"status"`
|
||||
}
|
||||
|
||||
// GetProductSubscriptionsQuery 获取产品订阅查询
|
||||
type GetProductSubscriptionsQuery struct {
|
||||
ProductID string `json:"product_id"`
|
||||
Status *entities.SubscriptionStatus `json:"status"`
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
"tyapi-server/internal/domains/product/repositories/queries"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// SubscriptionRepository 订阅仓储接口
|
||||
type SubscriptionRepository interface {
|
||||
interfaces.Repository[entities.Subscription]
|
||||
|
||||
// 基础查询方法
|
||||
FindByUserID(ctx context.Context, userID string) ([]*entities.Subscription, error)
|
||||
FindByProductID(ctx context.Context, productID string) ([]*entities.Subscription, error)
|
||||
FindByUserAndProduct(ctx context.Context, userID, productID string) (*entities.Subscription, error)
|
||||
FindActive(ctx context.Context) ([]*entities.Subscription, error)
|
||||
|
||||
// 复杂查询方法
|
||||
ListSubscriptions(ctx context.Context, query *queries.ListSubscriptionsQuery) ([]*entities.Subscription, int64, error)
|
||||
FindUserActiveSubscriptions(ctx context.Context, userID string) ([]*entities.Subscription, error)
|
||||
FindExpiredSubscriptions(ctx context.Context) ([]*entities.Subscription, error)
|
||||
|
||||
// 业务查询方法
|
||||
FindSubscriptionsByStatus(ctx context.Context, status entities.SubscriptionStatus) ([]*entities.Subscription, error)
|
||||
FindSubscriptionsByDateRange(ctx context.Context, startDate, endDate string) ([]*entities.Subscription, error)
|
||||
|
||||
// 统计方法
|
||||
CountByUser(ctx context.Context, userID string) (int64, error)
|
||||
CountByProduct(ctx context.Context, productID string) (int64, error)
|
||||
CountByStatus(ctx context.Context, status entities.SubscriptionStatus) (int64, error)
|
||||
CountActive(ctx context.Context) (int64, error)
|
||||
}
|
||||
151
internal/domains/product/services/product_service.go
Normal file
151
internal/domains/product/services/product_service.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
"tyapi-server/internal/domains/product/repositories"
|
||||
)
|
||||
|
||||
// ProductService 产品领域服务
|
||||
type ProductService struct {
|
||||
productRepo repositories.ProductRepository
|
||||
categoryRepo repositories.ProductCategoryRepository
|
||||
subscriptionRepo repositories.SubscriptionRepository
|
||||
}
|
||||
|
||||
// NewProductService 创建产品领域服务
|
||||
func NewProductService(
|
||||
productRepo repositories.ProductRepository,
|
||||
categoryRepo repositories.ProductCategoryRepository,
|
||||
subscriptionRepo repositories.SubscriptionRepository,
|
||||
) *ProductService {
|
||||
return &ProductService{
|
||||
productRepo: productRepo,
|
||||
categoryRepo: categoryRepo,
|
||||
subscriptionRepo: subscriptionRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateProduct 验证产品
|
||||
func (s *ProductService) 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 < 0 {
|
||||
return errors.New("产品价格不能为负数")
|
||||
}
|
||||
|
||||
// 验证分类是否存在
|
||||
if product.CategoryID != "" {
|
||||
category, err := s.categoryRepo.GetByID(nil, product.CategoryID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("产品分类不存在: %w", err)
|
||||
}
|
||||
if !category.IsValid() {
|
||||
return errors.New("产品分类已禁用或删除")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateProductCode 验证产品编号唯一性
|
||||
func (s *ProductService) ValidateProductCode(code string, excludeID string) error {
|
||||
if strings.TrimSpace(code) == "" {
|
||||
return errors.New("产品编号不能为空")
|
||||
}
|
||||
|
||||
existingProduct, err := s.productRepo.FindByCode(nil, code)
|
||||
if err == nil && existingProduct != nil && existingProduct.ID != excludeID {
|
||||
return errors.New("产品编号已存在")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanUserSubscribeProduct 检查用户是否可以订阅产品
|
||||
func (s *ProductService) CanUserSubscribeProduct(userID string, productID string) (bool, error) {
|
||||
// 检查产品是否存在且可订阅
|
||||
product, err := s.productRepo.GetByID(nil, productID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("产品不存在: %w", err)
|
||||
}
|
||||
|
||||
if !product.CanBeSubscribed() {
|
||||
return false, errors.New("产品不可订阅")
|
||||
}
|
||||
|
||||
// 检查用户是否已有该产品的订阅
|
||||
existingSubscription, err := s.subscriptionRepo.FindByUserAndProduct(nil, userID, productID)
|
||||
if err == nil && existingSubscription != nil {
|
||||
return false, errors.New("用户已有该产品的订阅")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetProductWithCategory 获取产品及其分类信息
|
||||
func (s *ProductService) GetProductWithCategory(productID string) (*entities.Product, error) {
|
||||
product, err := s.productRepo.GetByID(nil, productID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("产品不存在: %w", err)
|
||||
}
|
||||
|
||||
// 加载分类信息
|
||||
if product.CategoryID != "" {
|
||||
category, err := s.categoryRepo.GetByID(nil, product.CategoryID)
|
||||
if err == nil {
|
||||
product.Category = &category
|
||||
}
|
||||
}
|
||||
|
||||
return &product, nil
|
||||
}
|
||||
|
||||
// GetVisibleProducts 获取可见产品列表
|
||||
func (s *ProductService) GetVisibleProducts() ([]*entities.Product, error) {
|
||||
return s.productRepo.FindVisible(nil)
|
||||
}
|
||||
|
||||
// GetEnabledProducts 获取启用产品列表
|
||||
func (s *ProductService) GetEnabledProducts() ([]*entities.Product, error) {
|
||||
return s.productRepo.FindEnabled(nil)
|
||||
}
|
||||
|
||||
// GetProductsByCategory 根据分类获取产品
|
||||
func (s *ProductService) GetProductsByCategory(categoryID string) ([]*entities.Product, error) {
|
||||
return s.productRepo.FindByCategoryID(nil, categoryID)
|
||||
}
|
||||
|
||||
// GetProductStats 获取产品统计信息
|
||||
func (s *ProductService) GetProductStats() (map[string]int64, error) {
|
||||
stats := make(map[string]int64)
|
||||
|
||||
total, err := s.productRepo.CountByCategory(nil, "")
|
||||
if err == nil {
|
||||
stats["total"] = total
|
||||
}
|
||||
|
||||
enabled, err := s.productRepo.CountEnabled(nil)
|
||||
if err == nil {
|
||||
stats["enabled"] = enabled
|
||||
}
|
||||
|
||||
visible, err := s.productRepo.CountVisible(nil)
|
||||
if err == nil {
|
||||
stats["visible"] = visible
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
@@ -134,6 +134,28 @@ func (u *User) SetPassword(password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetPassword 重置密码(忘记密码时使用)
|
||||
func (u *User) ResetPassword(newPassword, confirmPassword string) error {
|
||||
// 1. 验证确认密码
|
||||
if newPassword != confirmPassword {
|
||||
return NewValidationError("新密码和确认新密码不匹配")
|
||||
}
|
||||
|
||||
// 2. 验证新密码强度
|
||||
if err := u.validatePasswordStrength(newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 更新密码
|
||||
hashedPassword, err := u.hashPassword(newPassword)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码加密失败: %w", err)
|
||||
}
|
||||
u.Password = hashedPassword
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanLogin 检查用户是否可以登录
|
||||
func (u *User) CanLogin() bool {
|
||||
// 检查用户是否被删除
|
||||
|
||||
Reference in New Issue
Block a user