This commit is contained in:
2025-07-29 00:30:32 +08:00
parent 10d086414b
commit 83530c0f9b
15 changed files with 710 additions and 37 deletions

View File

@@ -183,6 +183,110 @@ func (r *GormProductRepository) ListProducts(ctx context.Context, query *queries
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

View File

@@ -15,7 +15,7 @@ import (
)
const (
SubscriptionsTable = "subscriptions"
SubscriptionsTable = "subscription"
SubscriptionCacheTTL = 60 * time.Minute
)

View File

@@ -56,6 +56,7 @@ func NewProductHandler(
// @Param is_enabled query bool false "是否启用"
// @Param is_visible query bool false "是否可见"
// @Param is_package query bool false "是否组合包"
// @Param is_subscribed query bool false "是否已订阅(需要认证)"
// @Param sort_by query string false "排序字段"
// @Param sort_order query string false "排序方向" Enums(asc, desc)
// @Success 200 {object} responses.ProductListResponse "获取产品列表成功"
@@ -63,6 +64,9 @@ func NewProductHandler(
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/products [get]
func (h *ProductHandler) ListProducts(c *gin.Context) {
// 获取当前用户ID可选认证
userID := h.getCurrentUserID(c)
// 解析查询参数
page := h.getIntQuery(c, "page", 1)
pageSize := h.getIntQuery(c, "page_size", 10)
@@ -101,6 +105,17 @@ func (h *ProductHandler) ListProducts(c *gin.Context) {
}
}
// 订阅状态筛选(需要认证)
if userID != "" {
if isSubscribed := c.Query("is_subscribed"); isSubscribed != "" {
if subscribed, err := strconv.ParseBool(isSubscribed); err == nil {
filters["is_subscribed"] = subscribed
}
}
// 添加用户ID到筛选条件
filters["user_id"] = userID
}
// 排序字段
sortBy := c.Query("sort_by")
if sortBy == "" {
@@ -141,6 +156,16 @@ func (h *ProductHandler) getIntQuery(c *gin.Context, key string, defaultValue in
return defaultValue
}
// getCurrentUserID 获取当前用户ID
func (h *ProductHandler) getCurrentUserID(c *gin.Context) string {
if userID, exists := c.Get("user_id"); exists {
if id, ok := userID.(string); ok {
return id
}
}
return ""
}
// GetProductDetail 获取产品详情
// @Summary 获取产品详情
// @Description 根据产品ID获取产品详细信息

View File

@@ -12,6 +12,7 @@ import (
type ProductRoutes struct {
productHandler *handlers.ProductHandler
auth *middleware.JWTAuthMiddleware
optionalAuth *middleware.OptionalAuthMiddleware
logger *zap.Logger
}
@@ -19,11 +20,13 @@ type ProductRoutes struct {
func NewProductRoutes(
productHandler *handlers.ProductHandler,
auth *middleware.JWTAuthMiddleware,
optionalAuth *middleware.OptionalAuthMiddleware,
logger *zap.Logger,
) *ProductRoutes {
return &ProductRoutes{
productHandler: productHandler,
auth: auth,
optionalAuth: optionalAuth,
logger: logger,
}
}
@@ -36,7 +39,7 @@ func (r *ProductRoutes) Register(router *sharedhttp.GinRouter) {
products := engine.Group("/api/v1/products")
{
// 获取产品列表(分页+筛选)
products.GET("", r.productHandler.ListProducts)
products.GET("", r.optionalAuth.Handle(), r.productHandler.ListProducts)
// 获取产品统计
products.GET("/stats", r.productHandler.GetProductStats)