add Subscribe Discount

This commit is contained in:
2025-08-23 16:30:34 +08:00
parent 5dad8a3ccb
commit 365a2a8886
5 changed files with 99 additions and 5 deletions

View File

@@ -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:所有)"`
}

View File

@@ -80,4 +80,7 @@ type SubscriptionApplicationService interface {
// 统计
GetSubscriptionStats(ctx context.Context) (*responses.SubscriptionStatsResponse, error)
// 一键改价
BatchUpdateSubscriptionPrices(ctx context.Context, cmd *commands.BatchUpdateSubscriptionPricesCommand) error
}

View File

@@ -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,

View File

@@ -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 管理员获取订阅统计信息

View File

@@ -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调用记录管理