Files
tyapi-server/internal/infrastructure/http/handlers/product_handler.go

554 lines
18 KiB
Go
Raw Normal View History

2025-07-15 13:21:34 +08:00
package handlers
import (
2025-07-28 01:46:39 +08:00
"strconv"
2025-07-15 13:21:34 +08:00
"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
2025-07-28 01:46:39 +08:00
apiConfigService product.ProductApiConfigApplicationService
2025-07-15 13:21:34 +08:00
categoryService product.CategoryApplicationService
subAppService product.SubscriptionApplicationService
responseBuilder interfaces.ResponseBuilder
2025-07-20 20:53:26 +08:00
validator interfaces.RequestValidator
2025-07-15 13:21:34 +08:00
logger *zap.Logger
}
// NewProductHandler 创建产品HTTP处理器
func NewProductHandler(
appService product.ProductApplicationService,
2025-07-28 01:46:39 +08:00
apiConfigService product.ProductApiConfigApplicationService,
2025-07-15 13:21:34 +08:00
categoryService product.CategoryApplicationService,
subAppService product.SubscriptionApplicationService,
responseBuilder interfaces.ResponseBuilder,
2025-07-20 20:53:26 +08:00
validator interfaces.RequestValidator,
2025-07-15 13:21:34 +08:00
logger *zap.Logger,
) *ProductHandler {
return &ProductHandler{
appService: appService,
2025-07-28 01:46:39 +08:00
apiConfigService: apiConfigService,
2025-07-15 13:21:34 +08:00
categoryService: categoryService,
subAppService: subAppService,
responseBuilder: responseBuilder,
2025-07-20 20:53:26 +08:00
validator: validator,
2025-07-15 13:21:34 +08:00
logger: logger,
}
}
// ListProducts 获取产品列表(数据大厅)
// @Summary 获取产品列表
2025-07-30 00:51:22 +08:00
// @Description 分页获取可用的产品列表,支持筛选,默认只返回可见的产品
2025-07-15 13:21:34 +08:00
// @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 is_enabled query bool false "是否启用"
// @Param is_visible query bool false "是否可见"
// @Param is_package query bool false "是否组合包"
2025-07-29 00:30:32 +08:00
// @Param is_subscribed query bool false "是否已订阅(需要认证)"
2025-07-15 13:21:34 +08:00
// @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) {
2025-07-29 00:30:32 +08:00
// 获取当前用户ID可选认证
userID := h.getCurrentUserID(c)
2025-07-28 01:46:39 +08:00
// 解析查询参数
page := h.getIntQuery(c, "page", 1)
pageSize := h.getIntQuery(c, "page_size", 10)
// 构建筛选条件
filters := make(map[string]interface{})
// 搜索关键词筛选
if keyword := c.Query("keyword"); keyword != "" {
filters["keyword"] = keyword
2025-07-15 13:21:34 +08:00
}
2025-07-28 01:46:39 +08:00
// 分类ID筛选
if categoryID := c.Query("category_id"); categoryID != "" {
filters["category_id"] = categoryID
2025-07-15 13:21:34 +08:00
}
2025-07-28 01:46:39 +08:00
// 启用状态筛选
if isEnabled := c.Query("is_enabled"); isEnabled != "" {
if enabled, err := strconv.ParseBool(isEnabled); err == nil {
filters["is_enabled"] = enabled
}
2025-07-15 13:21:34 +08:00
}
2025-07-28 01:46:39 +08:00
2025-07-30 00:51:22 +08:00
// 可见状态筛选 - 用户端默认只显示可见的产品
2025-07-28 01:46:39 +08:00
if isVisible := c.Query("is_visible"); isVisible != "" {
if visible, err := strconv.ParseBool(isVisible); err == nil {
filters["is_visible"] = visible
}
2025-07-30 00:51:22 +08:00
} else {
// 如果没有指定可见状态,默认只显示可见的产品
filters["is_visible"] = true
2025-07-28 01:46:39 +08:00
}
// 产品类型筛选
if isPackage := c.Query("is_package"); isPackage != "" {
if pkg, err := strconv.ParseBool(isPackage); err == nil {
filters["is_package"] = pkg
}
}
2025-07-29 00:30:32 +08:00
// 订阅状态筛选(需要认证)
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
}
2025-07-28 01:46:39 +08:00
// 排序字段
sortBy := c.Query("sort_by")
if sortBy == "" {
sortBy = "created_at"
}
// 排序方向
sortOrder := c.Query("sort_order")
if sortOrder == "" {
sortOrder = "desc"
}
// 构建分页选项
options := interfaces.ListOptions{
Page: page,
PageSize: pageSize,
Sort: sortBy,
Order: sortOrder,
2025-07-15 13:21:34 +08:00
}
2025-07-28 01:46:39 +08:00
result, err := h.appService.ListProducts(c.Request.Context(), filters, options)
2025-07-15 13:21:34 +08:00
if err != nil {
h.logger.Error("获取产品列表失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取产品列表失败")
return
}
h.responseBuilder.Success(c, result, "获取产品列表成功")
}
2025-07-28 01:46:39 +08:00
// getIntQuery 获取整数查询参数
func (h *ProductHandler) getIntQuery(c *gin.Context, key string, defaultValue int) int {
if value := c.Query(key); value != "" {
if intValue, err := strconv.Atoi(value); err == nil && intValue > 0 {
return intValue
}
}
return defaultValue
}
2025-07-29 00:30:32 +08:00
// 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 ""
}
2025-07-15 13:21:34 +08:00
// GetProductDetail 获取产品详情
// @Summary 获取产品详情
2025-07-30 00:51:22 +08:00
// @Description 根据产品ID获取产品详细信息只能获取可见的产品
2025-07-15 13:21:34 +08:00
// @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
}
2025-07-30 00:51:22 +08:00
// 使用用户端专用的产品详情获取方法
result, err := h.appService.GetProductByIDForUser(c.Request.Context(), &query)
2025-07-15 13:21:34 +08:00
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"
// @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 == "" {
2025-07-20 20:53:26 +08:00
h.responseBuilder.Unauthorized(c, "用户未登录")
2025-07-15 13:21:34 +08:00
return
}
var cmd commands.CreateSubscriptionCommand
2025-07-20 20:53:26 +08:00
if err := h.validator.ValidateParam(c, &cmd); err != nil {
2025-07-15 13:21:34 +08:00
return
}
2025-07-20 20:53:26 +08:00
// 设置用户ID
2025-07-15 13:21:34 +08:00
cmd.UserID = userID
if err := h.subAppService.CreateSubscription(c.Request.Context(), &cmd); err != nil {
2025-07-20 20:53:26 +08:00
h.logger.Error("订阅产品失败", zap.Error(err), zap.String("user_id", userID), zap.String("product_id", cmd.ProductID))
2025-07-15 13:21:34 +08:00
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, "获取产品统计成功")
}
2025-07-28 01:46:39 +08:00
// GetProductApiConfig 获取产品API配置
// @Summary 获取产品API配置
// @Description 根据产品ID获取API配置信息
// @Tags 产品API配置
// @Accept json
// @Produce json
// @Param id path string true "产品ID"
// @Success 200 {object} responses.ProductApiConfigResponse "获取成功"
// @Failure 400 {object} interfaces.APIResponse "请求参数错误"
// @Failure 404 {object} interfaces.APIResponse "配置不存在"
// @Router /api/v1/products/{id}/api-config [get]
func (h *ProductHandler) GetProductApiConfig(c *gin.Context) {
productID := c.Param("id")
if productID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
config, err := h.apiConfigService.GetProductApiConfig(c.Request.Context(), productID)
if err != nil {
h.logger.Error("获取产品API配置失败", zap.Error(err), zap.String("product_id", productID))
h.responseBuilder.NotFound(c, "产品API配置不存在")
return
}
h.responseBuilder.Success(c, config, "获取产品API配置成功")
}
// GetProductApiConfigByCode 根据产品代码获取API配置
// @Summary 根据产品代码获取API配置
// @Description 根据产品代码获取API配置信息
// @Tags 产品API配置
// @Accept json
// @Produce json
// @Param product_code path string true "产品代码"
// @Success 200 {object} responses.ProductApiConfigResponse "获取成功"
// @Failure 400 {object} interfaces.APIResponse "请求参数错误"
// @Failure 404 {object} interfaces.APIResponse "配置不存在"
// @Router /api/v1/products/code/{product_code}/api-config [get]
func (h *ProductHandler) GetProductApiConfigByCode(c *gin.Context) {
productCode := c.Param("product_code")
if productCode == "" {
h.responseBuilder.BadRequest(c, "产品代码不能为空")
return
}
config, err := h.apiConfigService.GetProductApiConfigByCode(c.Request.Context(), productCode)
if err != nil {
h.logger.Error("根据产品代码获取API配置失败", zap.Error(err), zap.String("product_code", productCode))
h.responseBuilder.NotFound(c, "产品API配置不存在")
return
}
h.responseBuilder.Success(c, config, "获取产品API配置成功")
}
2025-07-15 13:21:34 +08:00
// ================ 分类相关方法 ================
// ListCategories 获取分类列表
// @Summary 获取分类列表
2025-07-20 20:53:26 +08:00
// @Description 获取产品分类列表,支持筛选
2025-07-15 13:21:34 +08:00
// @Tags 数据大厅
// @Accept json
// @Produce json
2025-07-20 20:53:26 +08:00
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param is_enabled query bool false "是否启用"
// @Param is_visible query bool false "是否可见"
2025-07-15 13:21:34 +08:00
// @Success 200 {object} responses.CategoryListResponse "获取分类列表成功"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/categories [get]
func (h *ProductHandler) ListCategories(c *gin.Context) {
2025-07-20 20:53:26 +08:00
var query queries.ListCategoriesQuery
if err := h.validator.ValidateQuery(c, &query); err != nil {
return
}
// 设置默认值
if query.Page <= 0 {
query.Page = 1
}
if query.PageSize <= 0 {
query.PageSize = 10
}
if query.PageSize > 100 {
query.PageSize = 100
}
2025-07-15 13:21:34 +08:00
// 调用应用服务
2025-07-20 20:53:26 +08:00
categories, err := h.categoryService.ListCategories(c.Request.Context(), &query)
2025-07-15 13:21:34 +08:00
if err != nil {
h.logger.Error("获取分类列表失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取分类列表失败")
return
}
2025-07-20 20:53:26 +08:00
2025-07-15 13:21:34 +08:00
// 返回结果
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
}
2025-07-20 20:53:26 +08:00
2025-07-15 13:21:34 +08:00
// 构建查询命令
query := &queries.GetCategoryQuery{
ID: categoryID,
}
2025-07-20 20:53:26 +08:00
2025-07-15 13:21:34 +08:00
// 调用应用服务
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
}
2025-07-20 20:53:26 +08:00
2025-07-15 13:21:34 +08:00
// 返回结果
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 == "" {
2025-07-20 20:53:26 +08:00
h.responseBuilder.Unauthorized(c, "用户未登录")
2025-07-15 13:21:34 +08:00
return
}
var query queries.ListSubscriptionsQuery
2025-07-20 20:53:26 +08:00
if err := h.validator.ValidateQuery(c, &query); err != nil {
2025-07-28 23:28:24 +08:00
return
2025-07-15 13:21:34 +08:00
}
// 设置默认值
if query.Page <= 0 {
query.Page = 1
}
if query.PageSize <= 0 {
query.PageSize = 10
}
if query.PageSize > 100 {
query.PageSize = 100
}
2025-07-28 23:28:24 +08:00
result, err := h.subAppService.ListMySubscriptions(c.Request.Context(), userID, &query)
2025-07-15 13:21:34 +08:00
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 == "" {
2025-07-20 20:53:26 +08:00
h.responseBuilder.Unauthorized(c, "用户未登录")
2025-07-15 13:21:34 +08:00
return
}
2025-07-28 23:28:24 +08:00
result, err := h.subAppService.GetMySubscriptionStats(c.Request.Context(), userID)
2025-07-15 13:21:34 +08:00
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 == "" {
2025-07-20 20:53:26 +08:00
h.responseBuilder.Unauthorized(c, "用户未登录")
2025-07-15 13:21:34 +08:00
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 == "" {
2025-07-20 20:53:26 +08:00
h.responseBuilder.Unauthorized(c, "用户未登录")
2025-07-15 13:21:34 +08:00
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, "获取我的订阅使用情况成功")
}