Files
tyapi-server/internal/infrastructure/http/handlers/product_handler.go
2025-07-29 00:30:32 +08:00

550 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
apiConfigService product.ProductApiConfigApplicationService
categoryService product.CategoryApplicationService
subAppService product.SubscriptionApplicationService
responseBuilder interfaces.ResponseBuilder
validator interfaces.RequestValidator
logger *zap.Logger
}
// NewProductHandler 创建产品HTTP处理器
func NewProductHandler(
appService product.ProductApplicationService,
apiConfigService product.ProductApiConfigApplicationService,
categoryService product.CategoryApplicationService,
subAppService product.SubscriptionApplicationService,
responseBuilder interfaces.ResponseBuilder,
validator interfaces.RequestValidator,
logger *zap.Logger,
) *ProductHandler {
return &ProductHandler{
appService: appService,
apiConfigService: apiConfigService,
categoryService: categoryService,
subAppService: subAppService,
responseBuilder: responseBuilder,
validator: validator,
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 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 "获取产品列表成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @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)
// 构建筛选条件
filters := make(map[string]interface{})
// 搜索关键词筛选
if keyword := c.Query("keyword"); keyword != "" {
filters["keyword"] = keyword
}
// 分类ID筛选
if categoryID := c.Query("category_id"); categoryID != "" {
filters["category_id"] = categoryID
}
// 启用状态筛选
if isEnabled := c.Query("is_enabled"); isEnabled != "" {
if enabled, err := strconv.ParseBool(isEnabled); err == nil {
filters["is_enabled"] = enabled
}
}
// 可见状态筛选
if isVisible := c.Query("is_visible"); isVisible != "" {
if visible, err := strconv.ParseBool(isVisible); err == nil {
filters["is_visible"] = visible
}
}
// 产品类型筛选
if isPackage := c.Query("is_package"); isPackage != "" {
if pkg, err := strconv.ParseBool(isPackage); err == nil {
filters["is_package"] = pkg
}
}
// 订阅状态筛选(需要认证)
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 == "" {
sortBy = "created_at"
}
// 排序方向
sortOrder := c.Query("sort_order")
if sortOrder == "" {
sortOrder = "desc"
}
// 构建分页选项
options := interfaces.ListOptions{
Page: page,
PageSize: pageSize,
Sort: sortBy,
Order: sortOrder,
}
result, err := h.appService.ListProducts(c.Request.Context(), filters, options)
if err != nil {
h.logger.Error("获取产品列表失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取产品列表失败")
return
}
h.responseBuilder.Success(c, result, "获取产品列表成功")
}
// 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
}
// 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获取产品详细信息
// @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"
// @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
}
var cmd commands.CreateSubscriptionCommand
if err := h.validator.ValidateParam(c, &cmd); err != nil {
return
}
// 设置用户ID
cmd.UserID = userID
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", cmd.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, "获取产品统计成功")
}
// 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配置成功")
}
// ================ 分类相关方法 ================
// ListCategories 获取分类列表
// @Summary 获取分类列表
// @Description 获取产品分类列表,支持筛选
// @Tags 数据大厅
// @Accept json
// @Produce json
// @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 "是否可见"
// @Success 200 {object} responses.CategoryListResponse "获取分类列表成功"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/categories [get]
func (h *ProductHandler) ListCategories(c *gin.Context) {
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
}
// 调用应用服务
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 := 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
}
result, err := h.subAppService.ListMySubscriptions(c.Request.Context(), userID, &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.GetMySubscriptionStats(c.Request.Context(), userID)
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, "获取我的订阅使用情况成功")
}