From 365a2a888666dc5a22ee57769f18f6386f241faa Mon Sep 17 00:00:00 2001 From: liangzai <2440983361@qq.com> Date: Sat, 23 Aug 2025 16:30:34 +0800 Subject: [PATCH] add Subscribe Discount --- .../dto/commands/subscription_commands.go | 7 +++ .../product/product_application_service.go | 3 + .../subscription_application_service_impl.go | 59 +++++++++++++++++-- .../http/handlers/product_admin_handler.go | 34 +++++++++++ .../http/routes/product_admin_routes.go | 1 + 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/internal/application/product/dto/commands/subscription_commands.go b/internal/application/product/dto/commands/subscription_commands.go index 3e20373..cbe3faa 100644 --- a/internal/application/product/dto/commands/subscription_commands.go +++ b/internal/application/product/dto/commands/subscription_commands.go @@ -10,4 +10,11 @@ type CreateSubscriptionCommand struct { type UpdateSubscriptionPriceCommand struct { ID string `json:"-" uri:"id" binding:"required,uuid" comment:"订阅ID"` Price float64 `json:"price" binding:"price,min=0" comment:"订阅价格"` +} + +// BatchUpdateSubscriptionPricesCommand 批量更新订阅价格命令 +type BatchUpdateSubscriptionPricesCommand struct { + UserID string `json:"user_id" binding:"required,uuid" comment:"用户ID"` + Discount float64 `json:"discount" binding:"required,min=0.1,max=10" comment:"折扣比例(0.1-10折)"` + Scope string `json:"scope" binding:"required,oneof=undiscounted all" comment:"改价范围(undiscounted:仅未打折,all:所有)"` } \ No newline at end of file diff --git a/internal/application/product/product_application_service.go b/internal/application/product/product_application_service.go index 19085f7..3be8e52 100644 --- a/internal/application/product/product_application_service.go +++ b/internal/application/product/product_application_service.go @@ -80,4 +80,7 @@ type SubscriptionApplicationService interface { // 统计 GetSubscriptionStats(ctx context.Context) (*responses.SubscriptionStatsResponse, error) + + // 一键改价 + BatchUpdateSubscriptionPrices(ctx context.Context, cmd *commands.BatchUpdateSubscriptionPricesCommand) error } diff --git a/internal/application/product/subscription_application_service_impl.go b/internal/application/product/subscription_application_service_impl.go index caa108a..3ab4c08 100644 --- a/internal/application/product/subscription_application_service_impl.go +++ b/internal/application/product/subscription_application_service_impl.go @@ -3,6 +3,7 @@ package product import ( "context" + "github.com/shopspring/decimal" "go.uber.org/zap" "tyapi-server/internal/application/product/dto/commands" @@ -41,6 +42,54 @@ func (s *SubscriptionApplicationServiceImpl) UpdateSubscriptionPrice(ctx context return s.productSubscriptionService.UpdateSubscriptionPrice(ctx, cmd.ID, cmd.Price) } +// BatchUpdateSubscriptionPrices 一键改价 +// 业务流程:1. 获取用户所有订阅 2. 根据范围筛选 3. 批量更新价格 +func (s *SubscriptionApplicationServiceImpl) BatchUpdateSubscriptionPrices(ctx context.Context, cmd *commands.BatchUpdateSubscriptionPricesCommand) error { + subscriptions, _, err := s.productSubscriptionService.ListSubscriptions(ctx, &repoQueries.ListSubscriptionsQuery{ + UserID: cmd.UserID, + Page: 1, + PageSize: 1000, + }) + if err != nil { + return err + } + + // 根据范围筛选订阅 + var targetSubscriptions []*entities.Subscription + for _, sub := range subscriptions { + if cmd.Scope == "all" { + // 所有订阅都修改 + targetSubscriptions = append(targetSubscriptions, sub) + } else if cmd.Scope == "undiscounted" { + // 只修改未打折的订阅(价格等于产品原价) + if sub.Product != nil && sub.Price.Equal(sub.Product.Price) { + targetSubscriptions = append(targetSubscriptions, sub) + } + } + } + + // 批量更新价格 + for _, sub := range targetSubscriptions { + if sub.Product != nil { + // 计算折扣后的价格 + discountRatio := cmd.Discount / 10 + newPrice := sub.Product.Price.Mul(decimal.NewFromFloat(discountRatio)) + // 四舍五入到2位小数 + newPrice = newPrice.Round(2) + + err := s.productSubscriptionService.UpdateSubscriptionPrice(ctx, sub.ID, newPrice.InexactFloat64()) + if err != nil { + s.logger.Error("批量更新订阅价格失败", + zap.String("subscription_id", sub.ID), + zap.Error(err)) + // 继续处理其他订阅,不中断整个流程 + } + } + } + + return nil +} + // CreateSubscription 创建订阅 // 业务流程:1. 创建订阅 func (s *SubscriptionApplicationServiceImpl) CreateSubscription(ctx context.Context, cmd *commands.CreateSubscriptionCommand) error { @@ -161,9 +210,9 @@ func (s *SubscriptionApplicationServiceImpl) GetSubscriptionUsage(ctx context.Co } return &responses.SubscriptionUsageResponse{ - ID: subscription.ID, + ID: subscription.ID, ProductID: subscription.ProductID, - APIUsed: subscription.APIUsed, + APIUsed: subscription.APIUsed, }, nil } @@ -174,7 +223,7 @@ func (s *SubscriptionApplicationServiceImpl) GetSubscriptionStats(ctx context.Co if err != nil { return nil, err } - + return &responses.SubscriptionStatsResponse{ TotalSubscriptions: stats["total_subscriptions"].(int64), TotalRevenue: stats["total_revenue"].(float64), @@ -188,7 +237,7 @@ func (s *SubscriptionApplicationServiceImpl) GetMySubscriptionStats(ctx context. if err != nil { return nil, err } - + return &responses.SubscriptionStatsResponse{ TotalSubscriptions: stats["total_subscriptions"].(int64), TotalRevenue: stats["total_revenue"].(float64), @@ -256,7 +305,7 @@ func (s *SubscriptionApplicationServiceImpl) convertToCategorySimpleResponse(cat if category == nil { return nil } - + return &responses.CategorySimpleResponse{ ID: category.ID, Name: category.Name, diff --git a/internal/infrastructure/http/handlers/product_admin_handler.go b/internal/infrastructure/http/handlers/product_admin_handler.go index 1eb56ef..6504315 100644 --- a/internal/infrastructure/http/handlers/product_admin_handler.go +++ b/internal/infrastructure/http/handlers/product_admin_handler.go @@ -769,6 +769,40 @@ func (h *ProductAdminHandler) ListSubscriptions(c *gin.Context) { h.responseBuilder.Success(c, result, "获取订阅列表成功") } +// BatchUpdateSubscriptionPrices 一键改价 +// @Summary 一键改价 +// @Description 管理员一键调整用户所有订阅的价格 +// @Tags 订阅管理 +// @Accept json +// @Produce json +// @Security Bearer +// @Param request body commands.BatchUpdateSubscriptionPricesCommand true "批量改价请求" +// @Success 200 {object} map[string]interface{} "一键改价成功" +// @Failure 400 {object} map[string]interface{} "请求参数错误" +// @Failure 401 {object} map[string]interface{} "未认证" +// @Failure 500 {object} map[string]interface{} "服务器内部错误" +// @Router /api/v1/admin/subscriptions/batch-update-prices [post] +func (h *ProductAdminHandler) BatchUpdateSubscriptionPrices(c *gin.Context) { + var cmd commands.BatchUpdateSubscriptionPricesCommand + if err := c.ShouldBindJSON(&cmd); err != nil { + h.responseBuilder.BadRequest(c, err.Error()) + return + } + + err := h.subscriptionAppService.BatchUpdateSubscriptionPrices(c.Request.Context(), &cmd) + if err != nil { + h.logger.Error("一键改价失败", zap.Error(err), zap.String("user_id", cmd.UserID)) + h.responseBuilder.InternalError(c, "一键改价失败") + return + } + + h.responseBuilder.Success(c, map[string]interface{}{ + "user_id": cmd.UserID, + "discount": cmd.Discount, + "scope": cmd.Scope, + }, "一键改价成功") +} + // GetSubscriptionStats 获取订阅统计(管理员) // @Summary 获取订阅统计 // @Description 管理员获取订阅统计信息 diff --git a/internal/infrastructure/http/routes/product_admin_routes.go b/internal/infrastructure/http/routes/product_admin_routes.go index 359840d..abfbb40 100644 --- a/internal/infrastructure/http/routes/product_admin_routes.go +++ b/internal/infrastructure/http/routes/product_admin_routes.go @@ -78,6 +78,7 @@ func (r *ProductAdminRoutes) Register(router *sharedhttp.GinRouter) { subscriptions.GET("", r.handler.ListSubscriptions) subscriptions.GET("/stats", r.handler.GetSubscriptionStats) subscriptions.PUT("/:id/price", r.handler.UpdateSubscriptionPrice) + subscriptions.POST("/batch-update-prices", r.handler.BatchUpdateSubscriptionPrices) } // API调用记录管理