This commit is contained in:
2026-04-21 22:36:48 +08:00
commit 488c695fdf
748 changed files with 266838 additions and 0 deletions

View File

@@ -0,0 +1,189 @@
package repositories
import (
"context"
"encoding/json"
"errors"
"time"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"hyapi-server/internal/shared/database"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
ComponentReportDownloadsTable = "component_report_downloads"
)
type GormComponentReportRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ repositories.ComponentReportRepository = (*GormComponentReportRepository)(nil)
func NewGormComponentReportRepository(db *gorm.DB, logger *zap.Logger) repositories.ComponentReportRepository {
return &GormComponentReportRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, ComponentReportDownloadsTable),
}
}
func (r *GormComponentReportRepository) Create(ctx context.Context, download *entities.ComponentReportDownload) error {
return r.CreateEntity(ctx, download)
}
func (r *GormComponentReportRepository) UpdateDownload(ctx context.Context, download *entities.ComponentReportDownload) error {
return r.UpdateEntity(ctx, download)
}
func (r *GormComponentReportRepository) GetDownloadByID(ctx context.Context, id string) (*entities.ComponentReportDownload, error) {
var download entities.ComponentReportDownload
err := r.SmartGetByID(ctx, id, &download)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &download, nil
}
func (r *GormComponentReportRepository) GetUserDownloads(ctx context.Context, userID string, productID *string) ([]*entities.ComponentReportDownload, error) {
var downloads []entities.ComponentReportDownload
query := r.GetDB(ctx).Where("user_id = ?", userID)
if productID != nil && *productID != "" {
query = query.Where("product_id = ?", *productID)
}
err := query.Order("created_at DESC").Find(&downloads).Error
if err != nil {
return nil, err
}
result := make([]*entities.ComponentReportDownload, len(downloads))
for i := range downloads {
result[i] = &downloads[i]
}
return result, nil
}
func (r *GormComponentReportRepository) HasUserDownloaded(ctx context.Context, userID string, productCode string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.ComponentReportDownload{}).
Where("user_id = ? AND product_code = ?", userID, productCode).
Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}
func (r *GormComponentReportRepository) GetUserDownloadedProductCodes(ctx context.Context, userID string) ([]string, error) {
var downloads []entities.ComponentReportDownload
err := r.GetDB(ctx).
Select("DISTINCT sub_product_codes").
Where("user_id = ?", userID).
Find(&downloads).Error
if err != nil {
return nil, err
}
codesMap := make(map[string]bool)
for _, download := range downloads {
if download.SubProductCodes != "" {
var codes []string
if err := json.Unmarshal([]byte(download.SubProductCodes), &codes); err == nil {
for _, code := range codes {
codesMap[code] = true
}
}
}
// 也添加主产品编号
if download.ProductCode != "" {
codesMap[download.ProductCode] = true
}
}
codes := make([]string, 0, len(codesMap))
for code := range codesMap {
codes = append(codes, code)
}
return codes, nil
}
func (r *GormComponentReportRepository) GetDownloadByPaymentOrderID(ctx context.Context, orderID string) (*entities.ComponentReportDownload, error) {
var download entities.ComponentReportDownload
err := r.GetDB(ctx).Where("order_id = ?", orderID).First(&download).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &download, nil
}
// GetActiveDownload 获取用户有效的下载记录
func (r *GormComponentReportRepository) GetActiveDownload(ctx context.Context, userID, productID string) (*entities.ComponentReportDownload, error) {
var download entities.ComponentReportDownload
// 先尝试查找有支付订单号的下载记录(已支付)
err := r.GetDB(ctx).
Where("user_id = ? AND product_id = ? AND order_number IS NOT NULL AND deleted_at IS NULL", userID, productID).
Order("created_at DESC").
First(&download).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 如果没有找到有支付订单号的记录,尝试查找任何有效的下载记录
err = r.GetDB(ctx).
Where("user_id = ? AND product_id = ? AND deleted_at IS NULL", userID, productID).
Order("created_at DESC").
First(&download).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
} else {
return nil, err
}
}
// 如果找到了下载记录,检查关联的购买订单状态
if download.OrderID != nil {
// 这里需要查询购买订单状态,但当前仓库没有依赖购买订单仓库
// 所以只检查是否有过期时间设置,如果有则认为已支付
if download.ExpiresAt == nil {
return nil, nil // 没有过期时间,表示未支付
}
}
// 检查是否已过期
if download.IsExpired() {
return nil, nil
}
return &download, nil
}
// UpdateFilePath 更新下载记录文件路径
func (r *GormComponentReportRepository) UpdateFilePath(ctx context.Context, downloadID, filePath string) error {
return r.GetDB(ctx).Model(&entities.ComponentReportDownload{}).Where("id = ?", downloadID).Update("file_path", filePath).Error
}
// IncrementDownloadCount 增加下载次数
func (r *GormComponentReportRepository) IncrementDownloadCount(ctx context.Context, downloadID string) error {
now := time.Now()
return r.GetDB(ctx).Model(&entities.ComponentReportDownload{}).
Where("id = ?", downloadID).
Updates(map[string]interface{}{
"download_count": gorm.Expr("download_count + 1"),
"last_download_at": &now,
}).Error
}

View File

@@ -0,0 +1,92 @@
package repositories
import (
"context"
"errors"
"time"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"hyapi-server/internal/shared/database"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
ProductApiConfigsTable = "product_api_configs"
ProductApiConfigCacheTTL = 30 * time.Minute
)
type GormProductApiConfigRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ repositories.ProductApiConfigRepository = (*GormProductApiConfigRepository)(nil)
func NewGormProductApiConfigRepository(db *gorm.DB, logger *zap.Logger) repositories.ProductApiConfigRepository {
return &GormProductApiConfigRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, ProductApiConfigsTable),
}
}
func (r *GormProductApiConfigRepository) Create(ctx context.Context, config entities.ProductApiConfig) error {
return r.CreateEntity(ctx, &config)
}
func (r *GormProductApiConfigRepository) Update(ctx context.Context, config entities.ProductApiConfig) error {
return r.UpdateEntity(ctx, &config)
}
func (r *GormProductApiConfigRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.ProductApiConfig{})
}
func (r *GormProductApiConfigRepository) GetByID(ctx context.Context, id string) (*entities.ProductApiConfig, error) {
var config entities.ProductApiConfig
err := r.SmartGetByID(ctx, id, &config)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &config, nil
}
func (r *GormProductApiConfigRepository) FindByProductID(ctx context.Context, productID string) (*entities.ProductApiConfig, error) {
var config entities.ProductApiConfig
err := r.SmartGetByField(ctx, &config, "product_id", productID, ProductApiConfigCacheTTL)
if err != nil {
return nil, err
}
return &config, nil
}
func (r *GormProductApiConfigRepository) FindByProductCode(ctx context.Context, productCode string) (*entities.ProductApiConfig, error) {
var config entities.ProductApiConfig
err := r.GetDB(ctx).Joins("JOIN products ON products.id = product_api_configs.product_id").
Where("products.code = ?", productCode).
First(&config).Error
if err != nil {
return nil, err
}
return &config, nil
}
func (r *GormProductApiConfigRepository) FindByProductIDs(ctx context.Context, productIDs []string) ([]*entities.ProductApiConfig, error) {
var configs []*entities.ProductApiConfig
err := r.GetDB(ctx).Where("product_id IN ?", productIDs).Find(&configs).Error
if err != nil {
return nil, err
}
return configs, nil
}
func (r *GormProductApiConfigRepository) ExistsByProductID(ctx context.Context, productID string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.ProductApiConfig{}).Where("product_id = ?", productID).Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}

View File

@@ -0,0 +1,281 @@
package repositories
import (
"context"
"errors"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"hyapi-server/internal/domains/product/repositories/queries"
"hyapi-server/internal/shared/database"
"hyapi-server/internal/shared/interfaces"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
ProductCategoriesTable = "product_categories"
)
type GormProductCategoryRepository struct {
*database.CachedBaseRepositoryImpl
}
func (r *GormProductCategoryRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.ProductCategory{})
}
var _ repositories.ProductCategoryRepository = (*GormProductCategoryRepository)(nil)
func NewGormProductCategoryRepository(db *gorm.DB, logger *zap.Logger) repositories.ProductCategoryRepository {
return &GormProductCategoryRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, ProductCategoriesTable),
}
}
func (r *GormProductCategoryRepository) Create(ctx context.Context, entity entities.ProductCategory) (entities.ProductCategory, error) {
err := r.CreateEntity(ctx, &entity)
return entity, err
}
func (r *GormProductCategoryRepository) GetByID(ctx context.Context, id string) (entities.ProductCategory, error) {
var entity entities.ProductCategory
err := r.SmartGetByID(ctx, id, &entity)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return entities.ProductCategory{}, gorm.ErrRecordNotFound
}
return entities.ProductCategory{}, err
}
return entity, nil
}
func (r *GormProductCategoryRepository) Update(ctx context.Context, entity entities.ProductCategory) error {
return r.UpdateEntity(ctx, &entity)
}
// FindByCode 根据编号查找产品分类
func (r *GormProductCategoryRepository) FindByCode(ctx context.Context, code string) (*entities.ProductCategory, error) {
var entity entities.ProductCategory
err := r.GetDB(ctx).Where("code = ?", code).First(&entity).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// FindVisible 查找可见分类
func (r *GormProductCategoryRepository) FindVisible(ctx context.Context) ([]*entities.ProductCategory, error) {
var categories []entities.ProductCategory
err := r.GetDB(ctx).Where("is_visible = ? AND is_enabled = ?", true, true).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}
// FindEnabled 查找启用分类
func (r *GormProductCategoryRepository) FindEnabled(ctx context.Context) ([]*entities.ProductCategory, error) {
var categories []entities.ProductCategory
err := r.GetDB(ctx).Where("is_enabled = ?", true).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}
// ListCategories 获取分类列表
func (r *GormProductCategoryRepository) ListCategories(ctx context.Context, query *queries.ListCategoriesQuery) ([]*entities.ProductCategory, int64, error) {
var categories []entities.ProductCategory
var total int64
dbQuery := r.GetDB(ctx).Model(&entities.ProductCategory{})
// 应用筛选条件
if query.IsEnabled != nil {
dbQuery = dbQuery.Where("is_enabled = ?", *query.IsEnabled)
}
if query.IsVisible != nil {
dbQuery = dbQuery.Where("is_visible = ?", *query.IsVisible)
}
// 获取总数
if err := dbQuery.Count(&total).Error; err != nil {
return nil, 0, err
}
// 应用排序
if query.SortBy != "" {
order := query.SortBy
if query.SortOrder == "desc" {
order += " DESC"
} else {
order += " ASC"
}
dbQuery = dbQuery.Order(order)
} else {
// 默认按排序字段和创建时间排序
dbQuery = dbQuery.Order("sort ASC, created_at DESC")
}
// 应用分页
if query.Page > 0 && query.PageSize > 0 {
offset := (query.Page - 1) * query.PageSize
dbQuery = dbQuery.Offset(offset).Limit(query.PageSize)
}
// 获取数据
if err := dbQuery.Find(&categories).Error; err != nil {
return nil, 0, err
}
// 转换为指针切片
result := make([]*entities.ProductCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, total, nil
}
// CountEnabled 统计启用分类数量
func (r *GormProductCategoryRepository) CountEnabled(ctx context.Context) (int64, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.ProductCategory{}).Where("is_enabled = ?", true).Count(&count).Error
return count, err
}
// CountVisible 统计可见分类数量
func (r *GormProductCategoryRepository) CountVisible(ctx context.Context) (int64, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.ProductCategory{}).Where("is_visible = ? AND is_enabled = ?", true, true).Count(&count).Error
return count, err
}
// 基础Repository接口方法
// Count 返回分类总数
func (r *GormProductCategoryRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
var count int64
query := r.GetDB(ctx).Model(&entities.ProductCategory{})
// 应用筛选条件
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
// 应用搜索条件
if options.Search != "" {
query = query.Where("name LIKE ? OR description LIKE ?", "%"+options.Search+"%", "%"+options.Search+"%")
}
err := query.Count(&count).Error
return count, err
}
// GetByIDs 根据ID列表获取分类
func (r *GormProductCategoryRepository) GetByIDs(ctx context.Context, ids []string) ([]entities.ProductCategory, error) {
var categories []entities.ProductCategory
err := r.GetDB(ctx).Where("id IN ?", ids).Order("sort ASC, created_at DESC").Find(&categories).Error
return categories, err
}
// CreateBatch 批量创建分类
func (r *GormProductCategoryRepository) CreateBatch(ctx context.Context, categories []entities.ProductCategory) error {
return r.GetDB(ctx).Create(&categories).Error
}
// UpdateBatch 批量更新分类
func (r *GormProductCategoryRepository) UpdateBatch(ctx context.Context, categories []entities.ProductCategory) error {
return r.GetDB(ctx).Save(&categories).Error
}
// DeleteBatch 批量删除分类
func (r *GormProductCategoryRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.GetDB(ctx).Delete(&entities.ProductCategory{}, "id IN ?", ids).Error
}
// List 获取分类列表(基础方法)
func (r *GormProductCategoryRepository) List(ctx context.Context, options interfaces.ListOptions) ([]entities.ProductCategory, error) {
var categories []entities.ProductCategory
query := r.GetDB(ctx).Model(&entities.ProductCategory{})
// 应用筛选条件
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
// 应用搜索条件
if options.Search != "" {
query = query.Where("name LIKE ? OR description LIKE ?", "%"+options.Search+"%", "%"+options.Search+"%")
}
// 应用排序
if options.Sort != "" {
order := options.Sort
if options.Order == "desc" {
order += " DESC"
} else {
order += " ASC"
}
query = query.Order(order)
} else {
// 默认按排序字段和创建时间倒序
query = query.Order("sort ASC, created_at DESC")
}
// 应用分页
if options.Page > 0 && options.PageSize > 0 {
offset := (options.Page - 1) * options.PageSize
query = query.Offset(offset).Limit(options.PageSize)
}
err := query.Find(&categories).Error
return categories, err
}
// Exists 检查分类是否存在
func (r *GormProductCategoryRepository) Exists(ctx context.Context, id string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.ProductCategory{}).Where("id = ?", id).Count(&count).Error
return count > 0, err
}
// SoftDelete 软删除分类
func (r *GormProductCategoryRepository) SoftDelete(ctx context.Context, id string) error {
return r.GetDB(ctx).Delete(&entities.ProductCategory{}, "id = ?", id).Error
}
// Restore 恢复软删除的分类
func (r *GormProductCategoryRepository) Restore(ctx context.Context, id string) error {
return r.GetDB(ctx).Unscoped().Model(&entities.ProductCategory{}).Where("id = ?", id).Update("deleted_at", nil).Error
}
// WithTx 使用事务
func (r *GormProductCategoryRepository) WithTx(tx interface{}) interfaces.Repository[entities.ProductCategory] {
if gormTx, ok := tx.(*gorm.DB); ok {
return &GormProductCategoryRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(gormTx, r.GetLogger(), ProductCategoriesTable),
}
}
return r
}

View File

@@ -0,0 +1,108 @@
package repositories
import (
"context"
"errors"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"hyapi-server/internal/shared/database"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
ProductDocumentationsTable = "product_documentations"
)
type GormProductDocumentationRepository struct {
*database.CachedBaseRepositoryImpl
}
func (r *GormProductDocumentationRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.ProductDocumentation{})
}
var _ repositories.ProductDocumentationRepository = (*GormProductDocumentationRepository)(nil)
func NewGormProductDocumentationRepository(db *gorm.DB, logger *zap.Logger) repositories.ProductDocumentationRepository {
return &GormProductDocumentationRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, ProductDocumentationsTable),
}
}
// Create 创建文档
func (r *GormProductDocumentationRepository) Create(ctx context.Context, documentation *entities.ProductDocumentation) error {
return r.CreateEntity(ctx, documentation)
}
// Update 更新文档
func (r *GormProductDocumentationRepository) Update(ctx context.Context, documentation *entities.ProductDocumentation) error {
return r.UpdateEntity(ctx, documentation)
}
// FindByID 根据ID查找文档
func (r *GormProductDocumentationRepository) FindByID(ctx context.Context, id string) (*entities.ProductDocumentation, error) {
var entity entities.ProductDocumentation
err := r.SmartGetByID(ctx, id, &entity)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// FindByProductID 根据产品ID查找文档
func (r *GormProductDocumentationRepository) FindByProductID(ctx context.Context, productID string) (*entities.ProductDocumentation, error) {
var entity entities.ProductDocumentation
err := r.GetDB(ctx).Where("product_id = ?", productID).First(&entity).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// FindByProductIDs 根据产品ID列表批量查找文档
func (r *GormProductDocumentationRepository) FindByProductIDs(ctx context.Context, productIDs []string) ([]*entities.ProductDocumentation, error) {
var documentations []entities.ProductDocumentation
err := r.GetDB(ctx).Where("product_id IN ?", productIDs).Find(&documentations).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductDocumentation, len(documentations))
for i := range documentations {
result[i] = &documentations[i]
}
return result, nil
}
// UpdateBatch 批量更新文档
func (r *GormProductDocumentationRepository) UpdateBatch(ctx context.Context, documentations []*entities.ProductDocumentation) error {
if len(documentations) == 0 {
return nil
}
// 使用事务进行批量更新
return r.GetDB(ctx).Transaction(func(tx *gorm.DB) error {
for _, doc := range documentations {
if err := tx.Save(doc).Error; err != nil {
return err
}
}
return nil
})
}
// CountByProductID 统计指定产品的文档数量
func (r *GormProductDocumentationRepository) CountByProductID(ctx context.Context, productID string) (int64, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.ProductDocumentation{}).Where("product_id = ?", productID).Count(&count).Error
return count, err
}

View File

@@ -0,0 +1,521 @@
package repositories
import (
"context"
"errors"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"hyapi-server/internal/domains/product/repositories/queries"
"hyapi-server/internal/shared/database"
"hyapi-server/internal/shared/interfaces"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
ProductsTable = "products"
)
type GormProductRepository struct {
*database.CachedBaseRepositoryImpl
}
func (r *GormProductRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.Product{})
}
var _ repositories.ProductRepository = (*GormProductRepository)(nil)
func NewGormProductRepository(db *gorm.DB, logger *zap.Logger) repositories.ProductRepository {
return &GormProductRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, ProductsTable),
}
}
func (r *GormProductRepository) Create(ctx context.Context, entity entities.Product) (entities.Product, error) {
err := r.CreateEntity(ctx, &entity)
return entity, err
}
func (r *GormProductRepository) GetByID(ctx context.Context, id string) (entities.Product, error) {
var entity entities.Product
err := r.SmartGetByID(ctx, id, &entity)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return entities.Product{}, gorm.ErrRecordNotFound
}
return entities.Product{}, err
}
return entity, nil
}
func (r *GormProductRepository) Update(ctx context.Context, entity entities.Product) error {
return r.UpdateEntity(ctx, &entity)
}
// 其它方法同理迁移全部用r.GetDB(ctx)
// FindByCode 根据编号查找产品
func (r *GormProductRepository) FindByCode(ctx context.Context, code string) (*entities.Product, error) {
var entity entities.Product
err := r.SmartGetByField(ctx, &entity, "code", code) // 自动缓存
if err != nil {
return nil, err
}
return &entity, nil
}
// FindByOldID 根据旧ID查找产品
func (r *GormProductRepository) FindByOldID(ctx context.Context, oldID string) (*entities.Product, error) {
var entity entities.Product
err := r.GetDB(ctx).Where("old_id = ?", oldID).First(&entity).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// FindByCategoryID 根据分类ID查找产品
func (r *GormProductRepository) FindByCategoryID(ctx context.Context, categoryID string) ([]*entities.Product, error) {
var productEntities []entities.Product
err := r.GetDB(ctx).Preload("Category").Where("category_id = ?", categoryID).Order("created_at DESC").Find(&productEntities).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.Product, len(productEntities))
for i := range productEntities {
result[i] = &productEntities[i]
}
return result, nil
}
// FindVisible 查找可见产品
func (r *GormProductRepository) FindVisible(ctx context.Context) ([]*entities.Product, error) {
var productEntities []entities.Product
err := r.GetDB(ctx).Preload("Category").Where("is_visible = ? AND is_enabled = ?", true, true).Order("created_at DESC").Find(&productEntities).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.Product, len(productEntities))
for i := range productEntities {
result[i] = &productEntities[i]
}
return result, nil
}
// FindEnabled 查找启用产品
func (r *GormProductRepository) FindEnabled(ctx context.Context) ([]*entities.Product, error) {
var productEntities []entities.Product
err := r.GetDB(ctx).Preload("Category").Where("is_enabled = ?", true).Order("created_at DESC").Find(&productEntities).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.Product, len(productEntities))
for i := range productEntities {
result[i] = &productEntities[i]
}
return result, nil
}
// ListProducts 获取产品列表
func (r *GormProductRepository) ListProducts(ctx context.Context, query *queries.ListProductsQuery) ([]*entities.Product, int64, error) {
var productEntities []entities.Product
var total int64
dbQuery := r.GetDB(ctx).Model(&entities.Product{})
// 应用筛选条件
if query.Keyword != "" {
dbQuery = dbQuery.Where("name LIKE ? OR description LIKE ? OR code LIKE ?",
"%"+query.Keyword+"%", "%"+query.Keyword+"%", "%"+query.Keyword+"%")
}
if query.CategoryID != "" {
dbQuery = dbQuery.Where("category_id = ?", query.CategoryID)
}
if query.MinPrice != nil {
dbQuery = dbQuery.Where("price >= ?", *query.MinPrice)
}
if query.MaxPrice != nil {
dbQuery = dbQuery.Where("price <= ?", *query.MaxPrice)
}
if query.IsEnabled != nil {
dbQuery = dbQuery.Where("is_enabled = ?", *query.IsEnabled)
}
if query.IsVisible != nil {
dbQuery = dbQuery.Where("is_visible = ?", *query.IsVisible)
}
if query.IsPackage != nil {
dbQuery = dbQuery.Where("is_package = ?", *query.IsPackage)
}
// 获取总数
if err := dbQuery.Count(&total).Error; err != nil {
return nil, 0, err
}
// 应用排序
if query.SortBy != "" {
order := query.SortBy
if query.SortOrder == "desc" {
order += " DESC"
} else {
order += " ASC"
}
dbQuery = dbQuery.Order(order)
} else {
dbQuery = dbQuery.Order("created_at DESC")
}
// 应用分页
if query.Page > 0 && query.PageSize > 0 {
offset := (query.Page - 1) * query.PageSize
dbQuery = dbQuery.Offset(offset).Limit(query.PageSize)
}
// 预加载分类信息并获取数据
if err := dbQuery.Preload("Category").Find(&productEntities).Error; err != nil {
return nil, 0, err
}
// 转换为指针切片
result := make([]*entities.Product, len(productEntities))
for i := range productEntities {
result[i] = &productEntities[i]
}
return result, total, nil
}
// ListProductsWithSubscriptionStatus 获取产品列表(包含订阅状态)
func (r *GormProductRepository) ListProductsWithSubscriptionStatus(ctx context.Context, query *queries.ListProductsQuery) ([]*entities.Product, map[string]bool, int64, error) {
var productEntities []entities.Product
var total int64
dbQuery := r.GetDB(ctx).Model(&entities.Product{})
// 应用筛选条件
if query.Keyword != "" {
dbQuery = dbQuery.Where("name LIKE ? OR description LIKE ? OR code LIKE ?",
"%"+query.Keyword+"%", "%"+query.Keyword+"%", "%"+query.Keyword+"%")
}
if query.CategoryID != "" {
dbQuery = dbQuery.Where("category_id = ?", query.CategoryID)
}
if query.MinPrice != nil {
dbQuery = dbQuery.Where("price >= ?", *query.MinPrice)
}
if query.MaxPrice != nil {
dbQuery = dbQuery.Where("price <= ?", *query.MaxPrice)
}
if query.IsEnabled != nil {
dbQuery = dbQuery.Where("is_enabled = ?", *query.IsEnabled)
}
if query.IsVisible != nil {
dbQuery = dbQuery.Where("is_visible = ?", *query.IsVisible)
}
if query.IsPackage != nil {
dbQuery = dbQuery.Where("is_package = ?", *query.IsPackage)
}
// 如果指定了用户ID添加订阅状态筛选
if query.UserID != "" && query.IsSubscribed != nil {
if *query.IsSubscribed {
// 筛选已订阅的产品
dbQuery = dbQuery.Where("EXISTS (SELECT 1 FROM subscription WHERE subscription.product_id = product.id AND subscription.user_id = ?)", query.UserID)
} else {
// 筛选未订阅的产品
dbQuery = dbQuery.Where("NOT EXISTS (SELECT 1 FROM subscription WHERE subscription.product_id = product.id AND subscription.user_id = ?)", query.UserID)
}
}
// 获取总数
if err := dbQuery.Count(&total).Error; err != nil {
return nil, nil, 0, err
}
// 应用排序
if query.SortBy != "" {
order := query.SortBy
if query.SortOrder == "desc" {
order += " DESC"
} else {
order += " ASC"
}
dbQuery = dbQuery.Order(order)
} else {
dbQuery = dbQuery.Order("created_at DESC")
}
// 应用分页
if query.Page > 0 && query.PageSize > 0 {
offset := (query.Page - 1) * query.PageSize
dbQuery = dbQuery.Offset(offset).Limit(query.PageSize)
}
// 预加载分类信息并获取数据
if err := dbQuery.Preload("Category").Find(&productEntities).Error; err != nil {
return nil, nil, 0, err
}
// 转换为指针切片
result := make([]*entities.Product, len(productEntities))
for i := range productEntities {
result[i] = &productEntities[i]
}
// 获取订阅状态映射
subscriptionStatusMap := make(map[string]bool)
if query.UserID != "" && len(result) > 0 {
productIDs := make([]string, len(result))
for i, product := range result {
productIDs[i] = product.ID
}
// 查询用户的订阅状态
var subscriptions []struct {
ProductID string `gorm:"column:product_id"`
}
err := r.GetDB(ctx).Table("subscription").
Select("product_id").
Where("user_id = ? AND product_id IN ?", query.UserID, productIDs).
Find(&subscriptions).Error
if err == nil {
for _, sub := range subscriptions {
subscriptionStatusMap[sub.ProductID] = true
}
}
}
return result, subscriptionStatusMap, total, nil
}
// FindSubscribableProducts 查找可订阅产品
func (r *GormProductRepository) FindSubscribableProducts(ctx context.Context, userID string) ([]*entities.Product, error) {
var productEntities []entities.Product
err := r.GetDB(ctx).Where("is_enabled = ? AND is_visible = ?", true, true).Order("created_at DESC").Find(&productEntities).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.Product, len(productEntities))
for i := range productEntities {
result[i] = &productEntities[i]
}
return result, nil
}
// FindProductsByIDs 根据ID列表查找产品
func (r *GormProductRepository) FindProductsByIDs(ctx context.Context, ids []string) ([]*entities.Product, error) {
var productEntities []entities.Product
err := r.GetDB(ctx).Where("id IN ?", ids).Order("created_at DESC").Find(&productEntities).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.Product, len(productEntities))
for i := range productEntities {
result[i] = &productEntities[i]
}
return result, nil
}
// CountByCategory 统计分类下的产品数量
func (r *GormProductRepository) CountByCategory(ctx context.Context, categoryID string) (int64, error) {
var count int64
query := r.GetDB(ctx).Model(&entities.Product{})
if categoryID != "" {
query = query.Where("category_id = ?", categoryID)
}
err := query.Count(&count).Error
return count, err
}
// CountEnabled 统计启用产品数量
func (r *GormProductRepository) CountEnabled(ctx context.Context) (int64, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.Product{}).Where("is_enabled = ?", true).Count(&count).Error
return count, err
}
// CountVisible 统计可见产品数量
func (r *GormProductRepository) CountVisible(ctx context.Context) (int64, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.Product{}).Where("is_visible = ? AND is_enabled = ?", true, true).Count(&count).Error
return count, err
}
// Count 返回产品总数
func (r *GormProductRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
var count int64
query := r.GetDB(ctx).Model(&entities.Product{})
// 应用筛选条件
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
// 应用搜索条件
if options.Search != "" {
query = query.Where("name LIKE ? OR description LIKE ?", "%"+options.Search+"%", "%"+options.Search+"%")
}
err := query.Count(&count).Error
return count, err
}
// GetByIDs 根据ID列表获取产品
func (r *GormProductRepository) GetByIDs(ctx context.Context, ids []string) ([]entities.Product, error) {
var products []entities.Product
err := r.GetDB(ctx).Where("id IN ?", ids).Order("created_at DESC").Find(&products).Error
return products, err
}
// CreateBatch 批量创建产品
func (r *GormProductRepository) CreateBatch(ctx context.Context, products []entities.Product) error {
return r.GetDB(ctx).Create(&products).Error
}
// UpdateBatch 批量更新产品
func (r *GormProductRepository) UpdateBatch(ctx context.Context, products []entities.Product) error {
return r.GetDB(ctx).Save(&products).Error
}
// DeleteBatch 批量删除产品
func (r *GormProductRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.GetDB(ctx).Delete(&entities.Product{}, "id IN ?", ids).Error
}
// List 获取产品列表(基础方法)
func (r *GormProductRepository) List(ctx context.Context, options interfaces.ListOptions) ([]entities.Product, error) {
var products []entities.Product
query := r.GetDB(ctx).Model(&entities.Product{})
// 应用筛选条件
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
// 应用搜索条件
if options.Search != "" {
query = query.Where("name LIKE ? OR description LIKE ?", "%"+options.Search+"%", "%"+options.Search+"%")
}
// 应用排序
if options.Sort != "" {
order := options.Sort
if options.Order == "desc" {
order += " DESC"
} else {
order += " ASC"
}
query = query.Order(order)
} else {
// 默认按创建时间倒序
query = query.Order("created_at DESC")
}
// 应用分页
if options.Page > 0 && options.PageSize > 0 {
offset := (options.Page - 1) * options.PageSize
query = query.Offset(offset).Limit(options.PageSize)
}
err := query.Find(&products).Error
return products, err
}
// Exists 检查产品是否存在
func (r *GormProductRepository) Exists(ctx context.Context, id string) (bool, error) {
var count int64
err := r.GetDB(ctx).Model(&entities.Product{}).Where("id = ?", id).Count(&count).Error
return count > 0, err
}
// SoftDelete 软删除产品
func (r *GormProductRepository) SoftDelete(ctx context.Context, id string) error {
return r.GetDB(ctx).Delete(&entities.Product{}, "id = ?", id).Error
}
// Restore 恢复软删除的产品
func (r *GormProductRepository) Restore(ctx context.Context, id string) error {
return r.GetDB(ctx).Unscoped().Model(&entities.Product{}).Where("id = ?", id).Update("deleted_at", nil).Error
}
// GetPackageItems 获取组合包项目
func (r *GormProductRepository) GetPackageItems(ctx context.Context, packageID string) ([]*entities.ProductPackageItem, error) {
var packageItems []entities.ProductPackageItem
err := r.GetDB(ctx).
Preload("Product").
Where("package_id = ?", packageID).
Order("sort_order ASC").
Find(&packageItems).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductPackageItem, len(packageItems))
for i := range packageItems {
result[i] = &packageItems[i]
}
return result, nil
}
// CreatePackageItem 创建组合包项目
func (r *GormProductRepository) CreatePackageItem(ctx context.Context, packageItem *entities.ProductPackageItem) error {
return r.GetDB(ctx).Create(packageItem).Error
}
// GetPackageItemByID 根据ID获取组合包项目
func (r *GormProductRepository) GetPackageItemByID(ctx context.Context, itemID string) (*entities.ProductPackageItem, error) {
var packageItem entities.ProductPackageItem
err := r.GetDB(ctx).
Preload("Product").
Preload("Package").
Where("id = ?", itemID).
First(&packageItem).Error
if err != nil {
return nil, err
}
return &packageItem, nil
}
// UpdatePackageItem 更新组合包项目
func (r *GormProductRepository) UpdatePackageItem(ctx context.Context, packageItem *entities.ProductPackageItem) error {
return r.GetDB(ctx).Save(packageItem).Error
}
// DeletePackageItem 删除组合包项目(硬删除)
func (r *GormProductRepository) DeletePackageItem(ctx context.Context, itemID string) error {
return r.GetDB(ctx).Unscoped().Delete(&entities.ProductPackageItem{}, "id = ?", itemID).Error
}
// DeletePackageItemsByPackageID 根据组合包ID删除所有子产品硬删除
func (r *GormProductRepository) DeletePackageItemsByPackageID(ctx context.Context, packageID string) error {
return r.GetDB(ctx).Unscoped().Delete(&entities.ProductPackageItem{}, "package_id = ?", packageID).Error
}
// WithTx 使用事务
func (r *GormProductRepository) WithTx(tx interface{}) interfaces.Repository[entities.Product] {
if gormTx, ok := tx.(*gorm.DB); ok {
return &GormProductRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(gormTx, r.GetLogger(), ProductsTable),
}
}
return r
}

View File

@@ -0,0 +1,137 @@
package repositories
import (
"context"
"errors"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"hyapi-server/internal/shared/database"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
ProductSubCategoriesTable = "product_sub_categories"
)
type GormProductSubCategoryRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ repositories.ProductSubCategoryRepository = (*GormProductSubCategoryRepository)(nil)
func NewGormProductSubCategoryRepository(db *gorm.DB, logger *zap.Logger) repositories.ProductSubCategoryRepository {
return &GormProductSubCategoryRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, ProductSubCategoriesTable),
}
}
// Create 创建二级分类
func (r *GormProductSubCategoryRepository) Create(ctx context.Context, category entities.ProductSubCategory) (*entities.ProductSubCategory, error) {
err := r.CreateEntity(ctx, &category)
if err != nil {
return nil, err
}
return &category, nil
}
// GetByID 根据ID获取二级分类
func (r *GormProductSubCategoryRepository) GetByID(ctx context.Context, id string) (*entities.ProductSubCategory, error) {
var entity entities.ProductSubCategory
err := r.SmartGetByID(ctx, id, &entity)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// Update 更新二级分类
func (r *GormProductSubCategoryRepository) Update(ctx context.Context, category entities.ProductSubCategory) error {
return r.UpdateEntity(ctx, &category)
}
// Delete 删除二级分类
func (r *GormProductSubCategoryRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.ProductSubCategory{})
}
// List 获取所有二级分类
func (r *GormProductSubCategoryRepository) List(ctx context.Context) ([]*entities.ProductSubCategory, error) {
var categories []entities.ProductSubCategory
err := r.GetDB(ctx).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductSubCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}
// FindByCode 根据编号查找二级分类
func (r *GormProductSubCategoryRepository) FindByCode(ctx context.Context, code string) (*entities.ProductSubCategory, error) {
var entity entities.ProductSubCategory
err := r.GetDB(ctx).Where("code = ?", code).First(&entity).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// FindByCategoryID 根据一级分类ID查找二级分类
func (r *GormProductSubCategoryRepository) FindByCategoryID(ctx context.Context, categoryID string) ([]*entities.ProductSubCategory, error) {
var categories []entities.ProductSubCategory
err := r.GetDB(ctx).Where("category_id = ?", categoryID).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductSubCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}
// FindVisible 查找可见的二级分类
func (r *GormProductSubCategoryRepository) FindVisible(ctx context.Context) ([]*entities.ProductSubCategory, error) {
var categories []entities.ProductSubCategory
err := r.GetDB(ctx).Where("is_visible = ? AND is_enabled = ?", true, true).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductSubCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}
// FindEnabled 查找启用的二级分类
func (r *GormProductSubCategoryRepository) FindEnabled(ctx context.Context) ([]*entities.ProductSubCategory, error) {
var categories []entities.ProductSubCategory
err := r.GetDB(ctx).Where("is_enabled = ?", true).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductSubCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}

View File

@@ -0,0 +1,80 @@
package repositories
import (
"context"
"fmt"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"gorm.io/gorm"
)
// GormProductUIComponentRepository 产品UI组件关联仓储实现
type GormProductUIComponentRepository struct {
db *gorm.DB
}
// NewGormProductUIComponentRepository 创建产品UI组件关联仓储实例
func NewGormProductUIComponentRepository(db *gorm.DB) repositories.ProductUIComponentRepository {
return &GormProductUIComponentRepository{db: db}
}
// Create 创建产品UI组件关联
func (r *GormProductUIComponentRepository) Create(ctx context.Context, relation entities.ProductUIComponent) (entities.ProductUIComponent, error) {
if err := r.db.WithContext(ctx).Create(&relation).Error; err != nil {
return entities.ProductUIComponent{}, fmt.Errorf("创建产品UI组件关联失败: %w", err)
}
return relation, nil
}
// GetByProductID 根据产品ID获取UI组件关联列表
func (r *GormProductUIComponentRepository) GetByProductID(ctx context.Context, productID string) ([]entities.ProductUIComponent, error) {
var relations []entities.ProductUIComponent
if err := r.db.WithContext(ctx).
Preload("UIComponent").
Where("product_id = ?", productID).
Find(&relations).Error; err != nil {
return nil, fmt.Errorf("获取产品UI组件关联列表失败: %w", err)
}
return relations, nil
}
// GetByUIComponentID 根据UI组件ID获取产品关联列表
func (r *GormProductUIComponentRepository) GetByUIComponentID(ctx context.Context, componentID string) ([]entities.ProductUIComponent, error) {
var relations []entities.ProductUIComponent
if err := r.db.WithContext(ctx).
Preload("Product").
Where("ui_component_id = ?", componentID).
Find(&relations).Error; err != nil {
return nil, fmt.Errorf("获取UI组件产品关联列表失败: %w", err)
}
return relations, nil
}
// Delete 删除产品UI组件关联
func (r *GormProductUIComponentRepository) Delete(ctx context.Context, id string) error {
if err := r.db.WithContext(ctx).Delete(&entities.ProductUIComponent{}, id).Error; err != nil {
return fmt.Errorf("删除产品UI组件关联失败: %w", err)
}
return nil
}
// DeleteByProductID 根据产品ID删除所有关联
func (r *GormProductUIComponentRepository) DeleteByProductID(ctx context.Context, productID string) error {
if err := r.db.WithContext(ctx).Where("product_id = ?", productID).Delete(&entities.ProductUIComponent{}).Error; err != nil {
return fmt.Errorf("根据产品ID删除UI组件关联失败: %w", err)
}
return nil
}
// BatchCreate 批量创建产品UI组件关联
func (r *GormProductUIComponentRepository) BatchCreate(ctx context.Context, relations []entities.ProductUIComponent) error {
if len(relations) == 0 {
return nil
}
if err := r.db.WithContext(ctx).CreateInBatches(relations, 100).Error; err != nil {
return fmt.Errorf("批量创建产品UI组件关联失败: %w", err)
}
return nil
}

View File

@@ -0,0 +1,354 @@
package repositories
import (
"context"
"errors"
"time"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"hyapi-server/internal/domains/product/repositories/queries"
"hyapi-server/internal/shared/database"
"hyapi-server/internal/shared/interfaces"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
SubscriptionsTable = "subscription"
SubscriptionCacheTTL = 60 * time.Minute
)
type GormSubscriptionRepository struct {
*database.CachedBaseRepositoryImpl
}
func (r *GormSubscriptionRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.Subscription{})
}
var _ repositories.SubscriptionRepository = (*GormSubscriptionRepository)(nil)
func NewGormSubscriptionRepository(db *gorm.DB, logger *zap.Logger) repositories.SubscriptionRepository {
return &GormSubscriptionRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, SubscriptionsTable),
}
}
func (r *GormSubscriptionRepository) Create(ctx context.Context, entity entities.Subscription) (entities.Subscription, error) {
err := r.CreateEntity(ctx, &entity)
return entity, err
}
func (r *GormSubscriptionRepository) GetByID(ctx context.Context, id string) (entities.Subscription, error) {
var entity entities.Subscription
err := r.SmartGetByID(ctx, id, &entity)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return entities.Subscription{}, gorm.ErrRecordNotFound
}
return entities.Subscription{}, err
}
return entity, nil
}
func (r *GormSubscriptionRepository) Update(ctx context.Context, entity entities.Subscription) error {
return r.UpdateEntity(ctx, &entity)
}
// FindByUserID 根据用户ID查找订阅
func (r *GormSubscriptionRepository) FindByUserID(ctx context.Context, userID string) ([]*entities.Subscription, error) {
var subscriptions []entities.Subscription
err := r.GetDB(ctx).WithContext(ctx).Where("user_id = ?", userID).Order("created_at DESC").Find(&subscriptions).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.Subscription, len(subscriptions))
for i := range subscriptions {
result[i] = &subscriptions[i]
}
return result, nil
}
// FindByProductID 根据产品ID查找订阅
func (r *GormSubscriptionRepository) FindByProductID(ctx context.Context, productID string) ([]*entities.Subscription, error) {
var subscriptions []entities.Subscription
err := r.GetDB(ctx).WithContext(ctx).Where("product_id = ?", productID).Order("created_at DESC").Find(&subscriptions).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.Subscription, len(subscriptions))
for i := range subscriptions {
result[i] = &subscriptions[i]
}
return result, nil
}
// FindByUserAndProduct 根据用户和产品查找订阅
func (r *GormSubscriptionRepository) FindByUserAndProduct(ctx context.Context, userID, productID string) (*entities.Subscription, error) {
var entity entities.Subscription
// 组合缓存key的条件
where := "user_id = ? AND product_id = ?"
ttl := SubscriptionCacheTTL // 缓存10分钟可根据业务调整
err := r.GetWithCache(ctx, &entity, ttl, where, userID, productID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// ListSubscriptions 获取订阅列表
func (r *GormSubscriptionRepository) ListSubscriptions(ctx context.Context, query *queries.ListSubscriptionsQuery) ([]*entities.Subscription, int64, error) {
var subscriptions []entities.Subscription
var total int64
dbQuery := r.GetDB(ctx).WithContext(ctx).Model(&entities.Subscription{})
// 应用筛选条件
if query.UserID != "" {
dbQuery = dbQuery.Where("subscription.user_id = ?", query.UserID)
}
// 关键词搜索(产品名称或编码)
if query.Keyword != "" {
dbQuery = dbQuery.Joins("LEFT JOIN product ON product.id = subscription.product_id").
Where("product.name LIKE ? OR product.code LIKE ?", "%"+query.Keyword+"%", "%"+query.Keyword+"%")
}
// 产品名称筛选
if query.ProductName != "" {
dbQuery = dbQuery.Joins("LEFT JOIN product ON product.id = subscription.product_id").
Where("product.name LIKE ?", "%"+query.ProductName+"%")
}
// 企业名称筛选(需要关联用户和企业信息)
if query.CompanyName != "" {
dbQuery = dbQuery.Joins("LEFT JOIN users ON users.id = subscription.user_id").
Joins("LEFT JOIN enterprise_infos ON enterprise_infos.user_id = users.id").
Where("enterprise_infos.company_name LIKE ?", "%"+query.CompanyName+"%")
}
// 时间范围筛选
if query.StartTime != "" {
if t, err := time.Parse("2006-01-02 15:04:05", query.StartTime); err == nil {
dbQuery = dbQuery.Where("subscription.created_at >= ?", t)
}
}
if query.EndTime != "" {
if t, err := time.Parse("2006-01-02 15:04:05", query.EndTime); err == nil {
dbQuery = dbQuery.Where("subscription.created_at <= ?", t)
}
}
// 获取总数
if err := dbQuery.Count(&total).Error; err != nil {
return nil, 0, err
}
// 应用排序
if query.SortBy != "" {
order := query.SortBy
if query.SortOrder == "desc" {
order += " DESC"
} else {
order += " ASC"
}
dbQuery = dbQuery.Order(order)
} else {
dbQuery = dbQuery.Order("subscription.created_at DESC")
}
// 应用分页
if query.Page > 0 && query.PageSize > 0 {
offset := (query.Page - 1) * query.PageSize
dbQuery = dbQuery.Offset(offset).Limit(query.PageSize)
}
// 预加载Product的id、name、code、price、cost_price、is_package字段并同时预加载ProductCategory的id、name、code字段
if err := dbQuery.
Preload("Product", func(db *gorm.DB) *gorm.DB {
return db.Select("id", "name", "code", "price", "cost_price", "is_package", "category_id").
Preload("Category", func(db2 *gorm.DB) *gorm.DB {
return db2.Select("id", "name", "code")
})
}).
Find(&subscriptions).Error; err != nil {
return nil, 0, err
}
// 转换为指针切片
result := make([]*entities.Subscription, len(subscriptions))
for i := range subscriptions {
result[i] = &subscriptions[i]
}
return result, total, nil
}
// CountByUser 统计用户订阅数量
func (r *GormSubscriptionRepository) CountByUser(ctx context.Context, userID string) (int64, error) {
var count int64
err := r.GetDB(ctx).WithContext(ctx).Model(&entities.Subscription{}).Where("user_id = ?", userID).Count(&count).Error
return count, err
}
// CountByProduct 统计产品的订阅数量
func (r *GormSubscriptionRepository) CountByProduct(ctx context.Context, productID string) (int64, error) {
var count int64
err := r.GetDB(ctx).WithContext(ctx).Model(&entities.Subscription{}).Where("product_id = ?", productID).Count(&count).Error
return count, err
}
// GetTotalRevenue 获取总收入
func (r *GormSubscriptionRepository) GetTotalRevenue(ctx context.Context) (float64, error) {
var total decimal.Decimal
err := r.GetDB(ctx).WithContext(ctx).Model(&entities.Subscription{}).Select("COALESCE(SUM(price), 0)").Scan(&total).Error
if err != nil {
return 0, err
}
return total.InexactFloat64(), nil
}
// 基础Repository接口方法
// Count 返回订阅总数
func (r *GormSubscriptionRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
var count int64
query := r.GetDB(ctx).WithContext(ctx).Model(&entities.Subscription{})
// 应用筛选条件
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
// 应用搜索条件
if options.Search != "" {
query = query.Where("user_id LIKE ? OR product_id LIKE ?", "%"+options.Search+"%", "%"+options.Search+"%")
}
err := query.Count(&count).Error
return count, err
}
// GetByIDs 根据ID列表获取订阅
func (r *GormSubscriptionRepository) GetByIDs(ctx context.Context, ids []string) ([]entities.Subscription, error) {
var subscriptions []entities.Subscription
err := r.GetDB(ctx).WithContext(ctx).Where("id IN ?", ids).Order("created_at DESC").Find(&subscriptions).Error
return subscriptions, err
}
// CreateBatch 批量创建订阅
func (r *GormSubscriptionRepository) CreateBatch(ctx context.Context, subscriptions []entities.Subscription) error {
return r.GetDB(ctx).WithContext(ctx).Create(&subscriptions).Error
}
// UpdateBatch 批量更新订阅
func (r *GormSubscriptionRepository) UpdateBatch(ctx context.Context, subscriptions []entities.Subscription) error {
return r.GetDB(ctx).WithContext(ctx).Save(&subscriptions).Error
}
// DeleteBatch 批量删除订阅
func (r *GormSubscriptionRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.GetDB(ctx).WithContext(ctx).Delete(&entities.Subscription{}, "id IN ?", ids).Error
}
// List 获取订阅列表(基础方法)
func (r *GormSubscriptionRepository) List(ctx context.Context, options interfaces.ListOptions) ([]entities.Subscription, error) {
var subscriptions []entities.Subscription
query := r.GetDB(ctx).WithContext(ctx).Model(&entities.Subscription{})
// 应用筛选条件
if options.Filters != nil {
for key, value := range options.Filters {
query = query.Where(key+" = ?", value)
}
}
// 应用搜索条件
if options.Search != "" {
query = query.Where("user_id LIKE ? OR product_id LIKE ?", "%"+options.Search+"%", "%"+options.Search+"%")
}
// 应用排序
if options.Sort != "" {
order := options.Sort
if options.Order == "desc" {
order += " DESC"
} else {
order += " ASC"
}
query = query.Order(order)
} else {
// 默认按创建时间倒序
query = query.Order("created_at DESC")
}
// 应用分页
if options.Page > 0 && options.PageSize > 0 {
offset := (options.Page - 1) * options.PageSize
query = query.Offset(offset).Limit(options.PageSize)
}
err := query.Find(&subscriptions).Error
return subscriptions, err
}
// Exists 检查订阅是否存在
func (r *GormSubscriptionRepository) Exists(ctx context.Context, id string) (bool, error) {
var count int64
err := r.GetDB(ctx).WithContext(ctx).Model(&entities.Subscription{}).Where("id = ?", id).Count(&count).Error
return count > 0, err
}
// SoftDelete 软删除订阅
func (r *GormSubscriptionRepository) SoftDelete(ctx context.Context, id string) error {
return r.GetDB(ctx).WithContext(ctx).Delete(&entities.Subscription{}, "id = ?", id).Error
}
// Restore 恢复软删除的订阅
func (r *GormSubscriptionRepository) Restore(ctx context.Context, id string) error {
return r.GetDB(ctx).WithContext(ctx).Unscoped().Model(&entities.Subscription{}).Where("id = ?", id).Update("deleted_at", nil).Error
}
// WithTx 使用事务
func (r *GormSubscriptionRepository) WithTx(tx interface{}) interfaces.Repository[entities.Subscription] {
if gormTx, ok := tx.(*gorm.DB); ok {
return &GormSubscriptionRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(gormTx, r.GetLogger(), SubscriptionsTable),
}
}
return r
}
// IncrementAPIUsageWithOptimisticLock 使用乐观锁增加API使用次数
func (r *GormSubscriptionRepository) IncrementAPIUsageWithOptimisticLock(ctx context.Context, subscriptionID string, increment int64) error {
// 使用原生SQL进行乐观锁更新
result := r.GetDB(ctx).WithContext(ctx).Exec(`
UPDATE subscription
SET api_used = api_used + ?, version = version + 1, updated_at = NOW()
WHERE id = ? AND version = (
SELECT version FROM subscription WHERE id = ?
)
`, increment, subscriptionID, subscriptionID)
if result.Error != nil {
return result.Error
}
// 检查是否有行被更新
if result.RowsAffected == 0 {
return gorm.ErrRecordNotFound
}
return nil
}

View File

@@ -0,0 +1,130 @@
package repositories
import (
"context"
"fmt"
"hyapi-server/internal/domains/product/entities"
"hyapi-server/internal/domains/product/repositories"
"gorm.io/gorm"
)
// GormUIComponentRepository UI组件仓储实现
type GormUIComponentRepository struct {
db *gorm.DB
}
// NewGormUIComponentRepository 创建UI组件仓储实例
func NewGormUIComponentRepository(db *gorm.DB) repositories.UIComponentRepository {
return &GormUIComponentRepository{db: db}
}
// Create 创建UI组件
func (r *GormUIComponentRepository) Create(ctx context.Context, component entities.UIComponent) (entities.UIComponent, error) {
if err := r.db.WithContext(ctx).Create(&component).Error; err != nil {
return entities.UIComponent{}, fmt.Errorf("创建UI组件失败: %w", err)
}
return component, nil
}
// GetByID 根据ID获取UI组件
func (r *GormUIComponentRepository) GetByID(ctx context.Context, id string) (*entities.UIComponent, error) {
var component entities.UIComponent
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&component).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, fmt.Errorf("获取UI组件失败: %w", err)
}
return &component, nil
}
// GetByCode 根据编码获取UI组件
func (r *GormUIComponentRepository) GetByCode(ctx context.Context, code string) (*entities.UIComponent, error) {
var component entities.UIComponent
if err := r.db.WithContext(ctx).Where("component_code = ?", code).First(&component).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, fmt.Errorf("获取UI组件失败: %w", err)
}
return &component, nil
}
// List 获取UI组件列表
func (r *GormUIComponentRepository) List(ctx context.Context, filters map[string]interface{}) ([]entities.UIComponent, int64, error) {
var components []entities.UIComponent
var total int64
query := r.db.WithContext(ctx).Model(&entities.UIComponent{})
// 应用过滤条件
if isActive, ok := filters["is_active"]; ok {
query = query.Where("is_active = ?", isActive)
}
if keyword, ok := filters["keyword"]; ok && keyword != "" {
query = query.Where("component_name LIKE ? OR component_code LIKE ? OR description LIKE ?",
"%"+keyword.(string)+"%", "%"+keyword.(string)+"%", "%"+keyword.(string)+"%")
}
// 获取总数
if err := query.Count(&total).Error; err != nil {
return nil, 0, fmt.Errorf("获取UI组件总数失败: %w", err)
}
// 分页
if page, ok := filters["page"]; ok {
if pageSize, ok := filters["page_size"]; ok {
offset := (page.(int) - 1) * pageSize.(int)
query = query.Offset(offset).Limit(pageSize.(int))
}
}
// 排序
if sortBy, ok := filters["sort_by"]; ok {
if sortOrder, ok := filters["sort_order"]; ok {
query = query.Order(fmt.Sprintf("%s %s", sortBy, sortOrder))
}
} else {
query = query.Order("sort_order ASC, created_at DESC")
}
// 获取数据
if err := query.Find(&components).Error; err != nil {
return nil, 0, fmt.Errorf("获取UI组件列表失败: %w", err)
}
return components, total, nil
}
// Update 更新UI组件
func (r *GormUIComponentRepository) Update(ctx context.Context, component entities.UIComponent) error {
if err := r.db.WithContext(ctx).Save(&component).Error; err != nil {
return fmt.Errorf("更新UI组件失败: %w", err)
}
return nil
}
// Delete 删除UI组件
func (r *GormUIComponentRepository) Delete(ctx context.Context, id string) error {
// 记录删除操作的详细信息
if err := r.db.WithContext(ctx).Where("id = ?", id).Delete(&entities.UIComponent{}).Error; err != nil {
return fmt.Errorf("删除UI组件失败: %w", err)
}
return nil
}
// GetByCodes 根据编码列表获取UI组件
func (r *GormUIComponentRepository) GetByCodes(ctx context.Context, codes []string) ([]entities.UIComponent, error) {
var components []entities.UIComponent
if len(codes) == 0 {
return components, nil
}
if err := r.db.WithContext(ctx).Where("component_code IN ?", codes).Find(&components).Error; err != nil {
return nil, fmt.Errorf("根据编码列表获取UI组件失败: %w", err)
}
return components, nil
}