This commit is contained in:
2025-07-15 13:21:34 +08:00
parent 807004f78d
commit 83bf9aea7d
44 changed files with 9798 additions and 8 deletions

View File

@@ -0,0 +1,367 @@
package repositories
import (
"context"
"tyapi-server/internal/domains/product/entities"
"tyapi-server/internal/domains/product/repositories"
"tyapi-server/internal/domains/product/repositories/queries"
"tyapi-server/internal/shared/interfaces"
"go.uber.org/zap"
"gorm.io/gorm"
)
// GormProductCategoryRepository GORM产品分类仓储实现
type GormProductCategoryRepository struct {
db *gorm.DB
logger *zap.Logger
}
// 编译时检查接口实现
var _ repositories.ProductCategoryRepository = (*GormProductCategoryRepository)(nil)
// NewGormProductCategoryRepository 创建GORM产品分类仓储
func NewGormProductCategoryRepository(db *gorm.DB, logger *zap.Logger) repositories.ProductCategoryRepository {
return &GormProductCategoryRepository{
db: db,
logger: logger,
}
}
// Create 创建产品分类
func (r *GormProductCategoryRepository) Create(ctx context.Context, entity entities.ProductCategory) (entities.ProductCategory, error) {
r.logger.Info("创建产品分类", zap.String("id", entity.ID), zap.String("name", entity.Name))
err := r.db.WithContext(ctx).Create(&entity).Error
return entity, err
}
// GetByID 根据ID获取产品分类
func (r *GormProductCategoryRepository) GetByID(ctx context.Context, id string) (entities.ProductCategory, error) {
var entity entities.ProductCategory
err := r.db.WithContext(ctx).Where("id = ?", id).First(&entity).Error
return entity, err
}
// Update 更新产品分类
func (r *GormProductCategoryRepository) Update(ctx context.Context, entity entities.ProductCategory) error {
r.logger.Info("更新产品分类", zap.String("id", entity.ID))
return r.db.WithContext(ctx).Save(&entity).Error
}
// Delete 删除产品分类
func (r *GormProductCategoryRepository) Delete(ctx context.Context, id string) error {
r.logger.Info("删除产品分类", zap.String("id", id))
return r.db.WithContext(ctx).Delete(&entities.ProductCategory{}, "id = ?", id).Error
}
// FindByCode 根据编号查找产品分类
func (r *GormProductCategoryRepository) FindByCode(ctx context.Context, code string) (*entities.ProductCategory, error) {
var entity entities.ProductCategory
err := r.db.WithContext(ctx).Where("code = ?", code).First(&entity).Error
if err != nil {
return nil, err
}
return &entity, nil
}
// FindByParentID 根据父级ID查找产品分类
func (r *GormProductCategoryRepository) FindByParentID(ctx context.Context, parentID *string) ([]*entities.ProductCategory, error) {
var categories []entities.ProductCategory
query := r.db.WithContext(ctx)
if parentID == nil {
query = query.Where("parent_id IS NULL")
} else {
query = query.Where("parent_id = ?", *parentID)
}
err := query.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
}
// FindRootCategories 查找根分类
func (r *GormProductCategoryRepository) FindRootCategories(ctx context.Context) ([]*entities.ProductCategory, error) {
return r.FindByParentID(ctx, nil)
}
// FindVisible 查找可见分类
func (r *GormProductCategoryRepository) FindVisible(ctx context.Context) ([]*entities.ProductCategory, error) {
var categories []entities.ProductCategory
err := r.db.WithContext(ctx).Where("is_visible = ? AND is_enabled = ?", true, true).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.db.WithContext(ctx).Where("is_enabled = ?", true).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.db.WithContext(ctx).Model(&entities.ProductCategory{})
// 应用筛选条件
if query.ParentID != nil {
dbQuery = dbQuery.Where("parent_id = ?", *query.ParentID)
}
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_order 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
}
// GetCategoryTree 获取分类树
func (r *GormProductCategoryRepository) GetCategoryTree(ctx context.Context) ([]*entities.ProductCategory, error) {
var categories []entities.ProductCategory
err := r.db.WithContext(ctx).Where("is_enabled = ?", true).Order("sort_order ASC, created_at ASC").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
}
// FindCategoriesByLevel 根据层级查找分类
func (r *GormProductCategoryRepository) FindCategoriesByLevel(ctx context.Context, level int) ([]*entities.ProductCategory, error) {
var categories []entities.ProductCategory
err := r.db.WithContext(ctx).Where("level = ? AND is_enabled = ?", level, true).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
}
// FindCategoryPath 查找分类路径
func (r *GormProductCategoryRepository) FindCategoryPath(ctx context.Context, categoryID string) ([]*entities.ProductCategory, error) {
// 这里需要递归查找父级分类,简化实现
var entity entities.ProductCategory
err := r.db.WithContext(ctx).Where("id = ?", categoryID).First(&entity).Error
if err != nil {
return nil, err
}
result := []*entities.ProductCategory{&entity}
return result, nil
}
// CountByParent 统计父级下的分类数量
func (r *GormProductCategoryRepository) CountByParent(ctx context.Context, parentID *string) (int64, error) {
var count int64
query := r.db.WithContext(ctx).Model(&entities.ProductCategory{})
if parentID == nil {
query = query.Where("parent_id IS NULL")
} else {
query = query.Where("parent_id = ?", *parentID)
}
err := query.Count(&count).Error
return count, err
}
// CountEnabled 统计启用分类数量
func (r *GormProductCategoryRepository) CountEnabled(ctx context.Context) (int64, error) {
var count int64
err := r.db.WithContext(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.db.WithContext(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.db.WithContext(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.db.WithContext(ctx).Where("id IN ?", ids).Find(&categories).Error
return categories, err
}
// CreateBatch 批量创建分类
func (r *GormProductCategoryRepository) CreateBatch(ctx context.Context, categories []entities.ProductCategory) error {
return r.db.WithContext(ctx).Create(&categories).Error
}
// UpdateBatch 批量更新分类
func (r *GormProductCategoryRepository) UpdateBatch(ctx context.Context, categories []entities.ProductCategory) error {
return r.db.WithContext(ctx).Save(&categories).Error
}
// DeleteBatch 批量删除分类
func (r *GormProductCategoryRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.db.WithContext(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.db.WithContext(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)
}
// 应用分页
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.db.WithContext(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.db.WithContext(ctx).Delete(&entities.ProductCategory{}, "id = ?", id).Error
}
// Restore 恢复软删除的分类
func (r *GormProductCategoryRepository) Restore(ctx context.Context, id string) error {
return r.db.WithContext(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{
db: gormTx,
logger: r.logger,
}
}
return r
}

View File

@@ -0,0 +1,347 @@
package repositories
import (
"context"
"tyapi-server/internal/domains/product/entities"
"tyapi-server/internal/domains/product/repositories"
"tyapi-server/internal/domains/product/repositories/queries"
"tyapi-server/internal/shared/interfaces"
"go.uber.org/zap"
"gorm.io/gorm"
)
// GormProductRepository GORM产品仓储实现
type GormProductRepository struct {
db *gorm.DB
logger *zap.Logger
}
// 编译时检查接口实现
var _ repositories.ProductRepository = (*GormProductRepository)(nil)
// NewGormProductRepository 创建GORM产品仓储
func NewGormProductRepository(db *gorm.DB, logger *zap.Logger) repositories.ProductRepository {
return &GormProductRepository{
db: db,
logger: logger,
}
}
// Create 创建产品
func (r *GormProductRepository) Create(ctx context.Context, entity entities.Product) (entities.Product, error) {
r.logger.Info("创建产品", zap.String("id", entity.ID), zap.String("name", entity.Name))
err := r.db.WithContext(ctx).Create(&entity).Error
return entity, err
}
// GetByID 根据ID获取产品
func (r *GormProductRepository) GetByID(ctx context.Context, id string) (entities.Product, error) {
var entity entities.Product
err := r.db.WithContext(ctx).Where("id = ?", id).First(&entity).Error
return entity, err
}
// Update 更新产品
func (r *GormProductRepository) Update(ctx context.Context, entity entities.Product) error {
r.logger.Info("更新产品", zap.String("id", entity.ID))
return r.db.WithContext(ctx).Save(&entity).Error
}
// Delete 删除产品
func (r *GormProductRepository) Delete(ctx context.Context, id string) error {
r.logger.Info("删除产品", zap.String("id", id))
return r.db.WithContext(ctx).Delete(&entities.Product{}, "id = ?", id).Error
}
// FindByCode 根据编号查找产品
func (r *GormProductRepository) FindByCode(ctx context.Context, code string) (*entities.Product, error) {
var entity entities.Product
err := r.db.WithContext(ctx).Where("code = ?", code).First(&entity).Error
if err != nil {
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.db.WithContext(ctx).Where("category_id = ?", categoryID).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.db.WithContext(ctx).Where("is_visible = ? AND is_enabled = ?", true, true).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.db.WithContext(ctx).Where("is_enabled = ?", true).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.db.WithContext(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.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
}
// FindSubscribableProducts 查找可订阅产品
func (r *GormProductRepository) FindSubscribableProducts(ctx context.Context, userID string) ([]*entities.Product, error) {
var productEntities []entities.Product
err := r.db.WithContext(ctx).Where("is_enabled = ? AND is_visible = ?", true, true).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.db.WithContext(ctx).Where("id IN ?", ids).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.db.WithContext(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.db.WithContext(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.db.WithContext(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.db.WithContext(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.db.WithContext(ctx).Where("id IN ?", ids).Find(&products).Error
return products, err
}
// CreateBatch 批量创建产品
func (r *GormProductRepository) CreateBatch(ctx context.Context, products []entities.Product) error {
return r.db.WithContext(ctx).Create(&products).Error
}
// UpdateBatch 批量更新产品
func (r *GormProductRepository) UpdateBatch(ctx context.Context, products []entities.Product) error {
return r.db.WithContext(ctx).Save(&products).Error
}
// DeleteBatch 批量删除产品
func (r *GormProductRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.db.WithContext(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.db.WithContext(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)
}
// 应用分页
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.db.WithContext(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.db.WithContext(ctx).Delete(&entities.Product{}, "id = ?", id).Error
}
// Restore 恢复软删除的产品
func (r *GormProductRepository) Restore(ctx context.Context, id string) error {
return r.db.WithContext(ctx).Unscoped().Model(&entities.Product{}).Where("id = ?", id).Update("deleted_at", nil).Error
}
// WithTx 使用事务
func (r *GormProductRepository) WithTx(tx interface{}) interfaces.Repository[entities.Product] {
if gormTx, ok := tx.(*gorm.DB); ok {
return &GormProductRepository{
db: gormTx,
logger: r.logger,
}
}
return r
}

View File

@@ -0,0 +1,374 @@
package repositories
import (
"context"
"time"
"tyapi-server/internal/domains/product/entities"
"tyapi-server/internal/domains/product/repositories"
"tyapi-server/internal/domains/product/repositories/queries"
"tyapi-server/internal/shared/interfaces"
"go.uber.org/zap"
"gorm.io/gorm"
)
// GormSubscriptionRepository GORM订阅仓储实现
type GormSubscriptionRepository struct {
db *gorm.DB
logger *zap.Logger
}
// 编译时检查接口实现
var _ repositories.SubscriptionRepository = (*GormSubscriptionRepository)(nil)
// NewGormSubscriptionRepository 创建GORM订阅仓储
func NewGormSubscriptionRepository(db *gorm.DB, logger *zap.Logger) repositories.SubscriptionRepository {
return &GormSubscriptionRepository{
db: db,
logger: logger,
}
}
// Create 创建订阅
func (r *GormSubscriptionRepository) Create(ctx context.Context, entity entities.Subscription) (entities.Subscription, error) {
r.logger.Info("创建订阅", zap.String("id", entity.ID), zap.String("user_id", entity.UserID))
err := r.db.WithContext(ctx).Create(&entity).Error
return entity, err
}
// GetByID 根据ID获取订阅
func (r *GormSubscriptionRepository) GetByID(ctx context.Context, id string) (entities.Subscription, error) {
var entity entities.Subscription
err := r.db.WithContext(ctx).Where("id = ?", id).First(&entity).Error
return entity, err
}
// Update 更新订阅
func (r *GormSubscriptionRepository) Update(ctx context.Context, entity entities.Subscription) error {
r.logger.Info("更新订阅", zap.String("id", entity.ID))
return r.db.WithContext(ctx).Save(&entity).Error
}
// Delete 删除订阅
func (r *GormSubscriptionRepository) Delete(ctx context.Context, id string) error {
r.logger.Info("删除订阅", zap.String("id", id))
return r.db.WithContext(ctx).Delete(&entities.Subscription{}, "id = ?", id).Error
}
// FindByUserID 根据用户ID查找订阅
func (r *GormSubscriptionRepository) FindByUserID(ctx context.Context, userID string) ([]*entities.Subscription, error) {
var subscriptions []entities.Subscription
err := r.db.WithContext(ctx).Where("user_id = ?", userID).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.db.WithContext(ctx).Where("product_id = ?", productID).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
err := r.db.WithContext(ctx).Where("user_id = ? AND product_id = ?", userID, productID).First(&entity).Error
if err != nil {
return nil, err
}
return &entity, nil
}
// FindActive 查找活跃订阅
func (r *GormSubscriptionRepository) FindActive(ctx context.Context) ([]*entities.Subscription, error) {
var subscriptions []entities.Subscription
err := r.db.WithContext(ctx).Where("status = ?", entities.SubscriptionStatusActive).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
}
// ListSubscriptions 获取订阅列表
func (r *GormSubscriptionRepository) ListSubscriptions(ctx context.Context, query *queries.ListSubscriptionsQuery) ([]*entities.Subscription, int64, error) {
var subscriptions []entities.Subscription
var total int64
dbQuery := r.db.WithContext(ctx).Model(&entities.Subscription{})
// 应用筛选条件
if query.UserID != "" {
dbQuery = dbQuery.Where("user_id = ?", query.UserID)
}
if query.ProductID != "" {
dbQuery = dbQuery.Where("product_id = ?", query.ProductID)
}
if query.Status != "" {
dbQuery = dbQuery.Where("status = ?", query.Status)
}
// 获取总数
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.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
}
// FindUserActiveSubscriptions 查找用户活跃订阅
func (r *GormSubscriptionRepository) FindUserActiveSubscriptions(ctx context.Context, userID string) ([]*entities.Subscription, error) {
var subscriptions []entities.Subscription
err := r.db.WithContext(ctx).Where("user_id = ? AND status = ?", userID, entities.SubscriptionStatusActive).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
}
// FindExpiredSubscriptions 查找过期订阅
func (r *GormSubscriptionRepository) FindExpiredSubscriptions(ctx context.Context) ([]*entities.Subscription, error) {
var subscriptions []entities.Subscription
now := time.Now()
err := r.db.WithContext(ctx).Where("end_date IS NOT NULL AND end_date < ? AND status = ?", now, entities.SubscriptionStatusActive).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
}
// FindSubscriptionsByStatus 根据状态查找订阅
func (r *GormSubscriptionRepository) FindSubscriptionsByStatus(ctx context.Context, status entities.SubscriptionStatus) ([]*entities.Subscription, error) {
var subscriptions []entities.Subscription
err := r.db.WithContext(ctx).Where("status = ?", status).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
}
// FindSubscriptionsByDateRange 根据日期范围查找订阅
func (r *GormSubscriptionRepository) FindSubscriptionsByDateRange(ctx context.Context, startDate, endDate string) ([]*entities.Subscription, error) {
var subscriptions []entities.Subscription
err := r.db.WithContext(ctx).Where("created_at BETWEEN ? AND ?", startDate, endDate).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
}
// CountByUser 统计用户订阅数量
func (r *GormSubscriptionRepository) CountByUser(ctx context.Context, userID string) (int64, error) {
var count int64
err := r.db.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.db.WithContext(ctx).Model(&entities.Subscription{}).Where("product_id = ?", productID).Count(&count).Error
return count, err
}
// CountByStatus 根据状态统计订阅数量
func (r *GormSubscriptionRepository) CountByStatus(ctx context.Context, status entities.SubscriptionStatus) (int64, error) {
var count int64
err := r.db.WithContext(ctx).Model(&entities.Subscription{}).Where("status = ?", status).Count(&count).Error
return count, err
}
// CountActive 统计活跃订阅数量
func (r *GormSubscriptionRepository) CountActive(ctx context.Context) (int64, error) {
var count int64
err := r.db.WithContext(ctx).Model(&entities.Subscription{}).Where("status = ?", entities.SubscriptionStatusActive).Count(&count).Error
return count, err
}
// 基础Repository接口方法
// Count 返回订阅总数
func (r *GormSubscriptionRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
var count int64
query := r.db.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.db.WithContext(ctx).Where("id IN ?", ids).Find(&subscriptions).Error
return subscriptions, err
}
// CreateBatch 批量创建订阅
func (r *GormSubscriptionRepository) CreateBatch(ctx context.Context, subscriptions []entities.Subscription) error {
return r.db.WithContext(ctx).Create(&subscriptions).Error
}
// UpdateBatch 批量更新订阅
func (r *GormSubscriptionRepository) UpdateBatch(ctx context.Context, subscriptions []entities.Subscription) error {
return r.db.WithContext(ctx).Save(&subscriptions).Error
}
// DeleteBatch 批量删除订阅
func (r *GormSubscriptionRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.db.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.db.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)
}
// 应用分页
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.db.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.db.WithContext(ctx).Delete(&entities.Subscription{}, "id = ?", id).Error
}
// Restore 恢复软删除的订阅
func (r *GormSubscriptionRepository) Restore(ctx context.Context, id string) error {
return r.db.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{
db: gormTx,
logger: r.logger,
}
}
return r
}

View File

@@ -33,7 +33,7 @@ func NewAdminHandler(
// Login 管理员登录
// @Summary 管理员登录
// @Description 使用用户名和密码进行管理员登录返回JWT令牌
// @Tags 管理员认证
// @Tags 管理员管理
// @Accept json
// @Produce json
// @Param request body commands.AdminLoginCommand true "管理员登录请求"

View File

@@ -0,0 +1,444 @@
package handlers
import (
"strconv"
"tyapi-server/internal/application/product"
"tyapi-server/internal/application/product/dto/commands"
"tyapi-server/internal/application/product/dto/queries"
"tyapi-server/internal/shared/interfaces"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// ProductHandler 产品相关HTTP处理器
type ProductHandler struct {
appService product.ProductApplicationService
categoryService product.CategoryApplicationService
subAppService product.SubscriptionApplicationService
responseBuilder interfaces.ResponseBuilder
logger *zap.Logger
}
// NewProductHandler 创建产品HTTP处理器
func NewProductHandler(
appService product.ProductApplicationService,
categoryService product.CategoryApplicationService,
subAppService product.SubscriptionApplicationService,
responseBuilder interfaces.ResponseBuilder,
logger *zap.Logger,
) *ProductHandler {
return &ProductHandler{
appService: appService,
categoryService: categoryService,
subAppService: subAppService,
responseBuilder: responseBuilder,
logger: logger,
}
}
// ListProducts 获取产品列表(数据大厅)
// @Summary 获取产品列表
// @Description 分页获取可用的产品列表,支持筛选
// @Tags 数据大厅
// @Accept json
// @Produce json
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param keyword query string false "搜索关键词"
// @Param category_id query string false "分类ID"
// @Param min_price query number false "最低价格"
// @Param max_price query number false "最高价格"
// @Param is_enabled query bool false "是否启用"
// @Param is_visible query bool false "是否可见"
// @Param is_package query bool false "是否组合包"
// @Param sort_by query string false "排序字段"
// @Param sort_order query string false "排序方向" Enums(asc, desc)
// @Success 200 {object} responses.ProductListResponse "获取产品列表成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/products [get]
func (h *ProductHandler) ListProducts(c *gin.Context) {
var query queries.ListProductsQuery
if err := c.ShouldBindQuery(&query); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误")
return
}
// 设置默认值
if query.Page <= 0 {
query.Page = 1
}
if query.PageSize <= 0 {
query.PageSize = 10
}
if query.PageSize > 100 {
query.PageSize = 100
}
result, err := h.appService.ListProducts(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取产品列表失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取产品列表失败")
return
}
h.responseBuilder.Success(c, result, "获取产品列表成功")
}
// GetProductDetail 获取产品详情
// @Summary 获取产品详情
// @Description 根据产品ID获取产品详细信息
// @Tags 数据大厅
// @Accept json
// @Produce json
// @Param id path string true "产品ID"
// @Success 200 {object} responses.ProductInfoResponse "获取产品详情成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 404 {object} map[string]interface{} "产品不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/products/{id} [get]
func (h *ProductHandler) GetProductDetail(c *gin.Context) {
var query queries.GetProductQuery
query.ID = c.Param("id")
if query.ID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
result, err := h.appService.GetProductByID(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取产品详情失败", zap.Error(err), zap.String("product_id", query.ID))
h.responseBuilder.NotFound(c, "产品不存在")
return
}
h.responseBuilder.Success(c, result, "获取产品详情成功")
}
// SubscribeProduct 订阅产品
// @Summary 订阅产品
// @Description 用户订阅指定产品
// @Tags 数据大厅
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "产品ID"
// @Param request body commands.CreateSubscriptionCommand true "订阅请求"
// @Success 200 {object} map[string]interface{} "订阅成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/products/{id}/subscribe [post]
func (h *ProductHandler) SubscribeProduct(c *gin.Context) {
userID := c.GetString("user_id") // 从JWT中间件获取
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未认证")
return
}
productID := c.Param("id")
if productID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
var cmd commands.CreateSubscriptionCommand
if err := c.ShouldBindJSON(&cmd); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误")
return
}
// 设置用户ID和产品ID
cmd.UserID = userID
cmd.ProductID = productID
// 设置默认值
if cmd.APILimit <= 0 {
cmd.APILimit = 1000 // 默认API调用限制
}
if cmd.Duration == "" {
cmd.Duration = "30d" // 默认订阅30天
}
if err := h.subAppService.CreateSubscription(c.Request.Context(), &cmd); err != nil {
h.logger.Error("订阅产品失败", zap.Error(err), zap.String("user_id", userID), zap.String("product_id", productID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "订阅成功")
}
// GetProductStats 获取产品统计信息
// @Summary 获取产品统计
// @Description 获取产品相关的统计信息
// @Tags 数据大厅
// @Accept json
// @Produce json
// @Success 200 {object} responses.ProductStatsResponse "获取统计信息成功"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/products/stats [get]
func (h *ProductHandler) GetProductStats(c *gin.Context) {
result, err := h.appService.GetProductStats(c.Request.Context())
if err != nil {
h.logger.Error("获取产品统计失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取产品统计失败")
return
}
h.responseBuilder.Success(c, result, "获取产品统计成功")
}
// ================ 分类相关方法 ================
// ListCategories 获取分类列表
// @Summary 获取分类列表
// @Description 获取产品分类列表,支持层级筛选
// @Tags 数据大厅
// @Accept json
// @Produce json
// @Param parent_id query string false "父级分类ID"
// @Param level query int false "分类层级"
// @Success 200 {object} responses.CategoryListResponse "获取分类列表成功"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/categories [get]
func (h *ProductHandler) ListCategories(c *gin.Context) {
// 解析查询参数
parentID := c.Query("parent_id")
levelStr := c.Query("level")
// 构建查询命令
query := &queries.ListCategoriesQuery{
Page: 1,
PageSize: 100,
SortBy: "sort_order",
SortOrder: "asc",
}
// 设置父级分类ID
if parentID != "" {
query.ParentID = &parentID
}
// 设置分类层级
if levelStr != "" {
if level, err := strconv.Atoi(levelStr); err == nil {
query.Level = &level
}
}
// 调用应用服务
categories, err := h.categoryService.ListCategories(c.Request.Context(), query)
if err != nil {
h.logger.Error("获取分类列表失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取分类列表失败")
return
}
// 返回结果
h.responseBuilder.Success(c, categories, "获取分类列表成功")
}
// GetCategoryDetail 获取分类详情
// @Summary 获取分类详情
// @Description 根据分类ID获取分类详细信息
// @Tags 数据大厅
// @Accept json
// @Produce json
// @Param id path string true "分类ID"
// @Success 200 {object} responses.CategoryInfoResponse "获取分类详情成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 404 {object} map[string]interface{} "分类不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/categories/{id} [get]
func (h *ProductHandler) GetCategoryDetail(c *gin.Context) {
categoryID := c.Param("id")
if categoryID == "" {
h.responseBuilder.BadRequest(c, "分类ID不能为空")
return
}
// 构建查询命令
query := &queries.GetCategoryQuery{
ID: categoryID,
}
// 调用应用服务
category, err := h.categoryService.GetCategoryByID(c.Request.Context(), query)
if err != nil {
h.logger.Error("获取分类详情失败", zap.String("category_id", categoryID), zap.Error(err))
h.responseBuilder.NotFound(c, "分类不存在")
return
}
// 返回结果
h.responseBuilder.Success(c, category, "获取分类详情成功")
}
// ================ 我的订阅相关方法 ================
// ListMySubscriptions 获取我的订阅列表
// @Summary 获取我的订阅列表
// @Description 获取当前用户的订阅列表
// @Tags 我的订阅
// @Accept json
// @Produce json
// @Security Bearer
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param status query string false "订阅状态"
// @Param sort_by query string false "排序字段"
// @Param sort_order query string false "排序方向" Enums(asc, desc)
// @Success 200 {object} responses.SubscriptionListResponse "获取订阅列表成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/my/subscriptions [get]
func (h *ProductHandler) ListMySubscriptions(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未认证")
return
}
var query queries.ListSubscriptionsQuery
if err := c.ShouldBindQuery(&query); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误")
return
}
// 设置默认值
if query.Page <= 0 {
query.Page = 1
}
if query.PageSize <= 0 {
query.PageSize = 10
}
if query.PageSize > 100 {
query.PageSize = 100
}
// 设置用户ID
query.UserID = userID
result, err := h.subAppService.ListSubscriptions(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取我的订阅列表失败", zap.Error(err), zap.String("user_id", userID))
h.responseBuilder.InternalError(c, "获取我的订阅列表失败")
return
}
h.responseBuilder.Success(c, result, "获取我的订阅列表成功")
}
// GetMySubscriptionStats 获取我的订阅统计
// @Summary 获取我的订阅统计
// @Description 获取当前用户的订阅统计信息
// @Tags 我的订阅
// @Accept json
// @Produce json
// @Security Bearer
// @Success 200 {object} responses.SubscriptionStatsResponse "获取订阅统计成功"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/my/subscriptions/stats [get]
func (h *ProductHandler) GetMySubscriptionStats(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未认证")
return
}
result, err := h.subAppService.GetSubscriptionStats(c.Request.Context())
if err != nil {
h.logger.Error("获取我的订阅统计失败", zap.Error(err), zap.String("user_id", userID))
h.responseBuilder.InternalError(c, "获取我的订阅统计失败")
return
}
h.responseBuilder.Success(c, result, "获取我的订阅统计成功")
}
// GetMySubscriptionDetail 获取我的订阅详情
// @Summary 获取我的订阅详情
// @Description 获取指定订阅的详细信息
// @Tags 我的订阅
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "订阅ID"
// @Success 200 {object} responses.SubscriptionInfoResponse "获取订阅详情成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "订阅不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/my/subscriptions/{id} [get]
func (h *ProductHandler) GetMySubscriptionDetail(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未认证")
return
}
subscriptionID := c.Param("id")
if subscriptionID == "" {
h.responseBuilder.BadRequest(c, "订阅ID不能为空")
return
}
var query queries.GetSubscriptionQuery
query.ID = subscriptionID
result, err := h.subAppService.GetSubscriptionByID(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取我的订阅详情失败", zap.Error(err), zap.String("user_id", userID), zap.String("subscription_id", subscriptionID))
h.responseBuilder.NotFound(c, "订阅不存在")
return
}
h.responseBuilder.Success(c, result, "获取我的订阅详情成功")
}
// GetMySubscriptionUsage 获取我的订阅使用情况
// @Summary 获取我的订阅使用情况
// @Description 获取指定订阅的使用情况统计
// @Tags 我的订阅
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "订阅ID"
// @Success 200 {object} responses.SubscriptionUsageResponse "获取使用情况成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "订阅不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/my/subscriptions/{id}/usage [get]
func (h *ProductHandler) GetMySubscriptionUsage(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未认证")
return
}
subscriptionID := c.Param("id")
if subscriptionID == "" {
h.responseBuilder.BadRequest(c, "订阅ID不能为空")
return
}
result, err := h.subAppService.GetSubscriptionUsage(c.Request.Context(), subscriptionID)
if err != nil {
h.logger.Error("获取我的订阅使用情况失败", zap.Error(err), zap.String("user_id", userID), zap.String("subscription_id", subscriptionID))
h.responseBuilder.NotFound(c, "订阅不存在")
return
}
h.responseBuilder.Success(c, result, "获取我的订阅使用情况成功")
}

View File

@@ -213,6 +213,33 @@ func (h *UserHandler) ChangePassword(c *gin.Context) {
h.response.Success(c, nil, "密码修改成功")
}
// ResetPassword 重置密码
// @Summary 重置密码
// @Description 使用手机号、验证码和新密码重置用户密码(忘记密码时使用)
// @Tags 用户认证
// @Accept json
// @Produce json
// @Param request body commands.ResetPasswordCommand true "重置密码请求"
// @Success 200 {object} map[string]interface{} "密码重置成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误或验证码无效"
// @Failure 404 {object} map[string]interface{} "用户不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/users/reset-password [post]
func (h *UserHandler) ResetPassword(c *gin.Context) {
var cmd commands.ResetPasswordCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
if err := h.appService.ResetPassword(c.Request.Context(), &cmd); err != nil {
h.logger.Error("重置密码失败", zap.Error(err))
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, nil, "密码重置成功")
}
// getCurrentUserID 获取当前用户ID
func (h *UserHandler) getCurrentUserID(c *gin.Context) string {
if userID, exists := c.Get("user_id"); exists {

View File

@@ -0,0 +1,81 @@
package routes
import (
"tyapi-server/internal/infrastructure/http/handlers"
sharedhttp "tyapi-server/internal/shared/http"
"tyapi-server/internal/shared/middleware"
"go.uber.org/zap"
)
// ProductRoutes 产品路由
type ProductRoutes struct {
productHandler *handlers.ProductHandler
auth *middleware.JWTAuthMiddleware
logger *zap.Logger
}
// NewProductRoutes 创建产品路由
func NewProductRoutes(
productHandler *handlers.ProductHandler,
auth *middleware.JWTAuthMiddleware,
logger *zap.Logger,
) *ProductRoutes {
return &ProductRoutes{
productHandler: productHandler,
auth: auth,
logger: logger,
}
}
// Register 注册产品相关路由
func (r *ProductRoutes) Register(router *sharedhttp.GinRouter) {
engine := router.GetEngine()
// 数据大厅 - 公开接口
products := engine.Group("/api/v1/products")
{
// 获取产品列表(分页+筛选)
products.GET("", r.productHandler.ListProducts)
// 获取产品详情
products.GET("/:id", r.productHandler.GetProductDetail)
// 获取产品统计
products.GET("/stats", r.productHandler.GetProductStats)
// 订阅产品(需要认证)
products.POST("/:id/subscribe", r.auth.Handle(), r.productHandler.SubscribeProduct)
}
// 分类 - 公开接口
categories := engine.Group("/api/v1/categories")
{
// 获取分类列表
categories.GET("", r.productHandler.ListCategories)
// 获取分类详情
categories.GET("/:id", r.productHandler.GetCategoryDetail)
}
// 我的订阅 - 需要认证
my := engine.Group("/api/v1/my", r.auth.Handle())
{
subscriptions := my.Group("/subscriptions")
{
// 获取我的订阅列表
subscriptions.GET("", r.productHandler.ListMySubscriptions)
// 获取我的订阅统计
subscriptions.GET("/stats", r.productHandler.GetMySubscriptionStats)
// 获取订阅详情
subscriptions.GET("/:id", r.productHandler.GetMySubscriptionDetail)
// 获取订阅使用情况
subscriptions.GET("/:id/usage", r.productHandler.GetMySubscriptionUsage)
}
}
r.logger.Info("产品路由注册完成")
}

View File

@@ -39,6 +39,7 @@ func (r *UserRoutes) Register(router *sharedhttp.GinRouter) {
usersGroup.POST("/register", r.handler.Register) // 用户注册
usersGroup.POST("/login-password", r.handler.LoginWithPassword) // 密码登录
usersGroup.POST("/login-sms", r.handler.LoginWithSMS) // 短信验证码登录
usersGroup.POST("/reset-password", r.handler.ResetPassword) // 重置密码
// 需要认证的路由
authenticated := usersGroup.Group("")