package product import ( "context" "errors" "fmt" "time" "tyapi-server/internal/application/product/dto/commands" appQueries "tyapi-server/internal/application/product/dto/queries" "tyapi-server/internal/application/product/dto/responses" "tyapi-server/internal/domains/product/entities" "tyapi-server/internal/domains/product/repositories" repoQueries "tyapi-server/internal/domains/product/repositories/queries" "tyapi-server/internal/domains/product/services" "go.uber.org/zap" ) // SubscriptionApplicationServiceImpl 订阅应用服务实现 type SubscriptionApplicationServiceImpl struct { subscriptionRepo repositories.SubscriptionRepository productRepo repositories.ProductRepository productService *services.ProductService logger *zap.Logger } // NewSubscriptionApplicationService 创建订阅应用服务 func NewSubscriptionApplicationService( subscriptionRepo repositories.SubscriptionRepository, productRepo repositories.ProductRepository, productService *services.ProductService, logger *zap.Logger, ) SubscriptionApplicationService { return &SubscriptionApplicationServiceImpl{ subscriptionRepo: subscriptionRepo, productRepo: productRepo, productService: productService, logger: logger, } } // CreateSubscription 创建订阅 func (s *SubscriptionApplicationServiceImpl) CreateSubscription(ctx context.Context, cmd *commands.CreateSubscriptionCommand) error { // 1. 参数验证 if err := s.validateCreateSubscription(cmd); err != nil { return err } // 2. 检查产品是否存在且可订阅 canSubscribe, err := s.productService.CanUserSubscribeProduct(cmd.UserID, cmd.ProductID) if err != nil { return err } if !canSubscribe { return errors.New("产品不可订阅或用户已有活跃订阅") } // 3. 检查产品是否存在 _, err = s.productRepo.GetByID(ctx, cmd.ProductID) if err != nil { return fmt.Errorf("产品不存在: %w", err) } // 5. 创建订阅实体 subscription := entities.Subscription{ UserID: cmd.UserID, ProductID: cmd.ProductID, Status: entities.SubscriptionStatusActive, Price: cmd.Price, APIUsed: 0, } // 6. 保存到仓储 createdSubscription, err := s.subscriptionRepo.Create(ctx, subscription) if err != nil { s.logger.Error("创建订阅失败", zap.Error(err)) return fmt.Errorf("创建订阅失败: %w", err) } s.logger.Info("创建订阅成功", zap.String("id", createdSubscription.ID), zap.String("user_id", cmd.UserID), zap.String("product_id", cmd.ProductID)) return nil } // UpdateSubscription 更新订阅 func (s *SubscriptionApplicationServiceImpl) UpdateSubscription(ctx context.Context, cmd *commands.UpdateSubscriptionCommand) error { // 1. 获取现有订阅 subscription, err := s.subscriptionRepo.GetByID(ctx, cmd.ID) if err != nil { return fmt.Errorf("订阅不存在: %w", err) } // 2. 更新字段 if cmd.Price >= 0 { subscription.Price = cmd.Price } // 3. 保存到仓储 if err := s.subscriptionRepo.Update(ctx, subscription); err != nil { s.logger.Error("更新订阅失败", zap.Error(err)) return fmt.Errorf("更新订阅失败: %w", err) } s.logger.Info("更新订阅成功", zap.String("id", cmd.ID)) return nil } // GetSubscriptionByID 根据ID获取订阅 func (s *SubscriptionApplicationServiceImpl) GetSubscriptionByID(ctx context.Context, query *appQueries.GetSubscriptionQuery) (*responses.SubscriptionInfoResponse, error) { subscription, err := s.subscriptionRepo.GetByID(ctx, query.ID) if err != nil { return nil, fmt.Errorf("订阅不存在: %w", err) } // 转换为响应对象 response := s.convertToSubscriptionInfoResponse(&subscription) // 加载产品信息 product, err := s.productRepo.GetByID(ctx, subscription.ProductID) if err == nil { response.Product = s.convertToProductSimpleResponse(&product) } return response, nil } // ListSubscriptions 获取订阅列表 func (s *SubscriptionApplicationServiceImpl) ListSubscriptions(ctx context.Context, query *appQueries.ListSubscriptionsQuery) (*responses.SubscriptionListResponse, error) { // 构建仓储查询 repoQuery := &repoQueries.ListSubscriptionsQuery{ Page: query.Page, PageSize: query.PageSize, UserID: query.UserID, ProductID: query.ProductID, Status: query.Status, SortBy: query.SortBy, SortOrder: query.SortOrder, } // 调用仓储 subscriptions, total, err := s.subscriptionRepo.ListSubscriptions(ctx, repoQuery) if err != nil { s.logger.Error("获取订阅列表失败", zap.Error(err)) return nil, fmt.Errorf("获取订阅列表失败: %w", err) } // 转换为响应对象 items := make([]responses.SubscriptionInfoResponse, len(subscriptions)) for i, subscription := range subscriptions { items[i] = *s.convertToSubscriptionInfoResponse(subscription) } return &responses.SubscriptionListResponse{ Total: total, Page: query.Page, Size: query.PageSize, Items: items, }, nil } // UpdateAPIUsage 更新API使用量 func (s *SubscriptionApplicationServiceImpl) UpdateAPIUsage(ctx context.Context, cmd *commands.UpdateAPIUsageCommand) error { subscription, err := s.subscriptionRepo.GetByID(ctx, cmd.ID) if err != nil { return fmt.Errorf("订阅不存在: %w", err) } subscription.IncrementAPIUsage(cmd.APIUsed) if err := s.subscriptionRepo.Update(ctx, subscription); err != nil { s.logger.Error("更新API使用量失败", zap.Error(err)) return fmt.Errorf("更新API使用量失败: %w", err) } s.logger.Info("更新API使用量成功", zap.String("id", cmd.ID), zap.Int64("api_used", cmd.APIUsed)) return nil } // ResetAPIUsage 重置API使用量 func (s *SubscriptionApplicationServiceImpl) ResetAPIUsage(ctx context.Context, cmd *commands.ResetAPIUsageCommand) error { subscription, err := s.subscriptionRepo.GetByID(ctx, cmd.ID) if err != nil { return fmt.Errorf("订阅不存在: %w", err) } subscription.ResetAPIUsage() if err := s.subscriptionRepo.Update(ctx, subscription); err != nil { s.logger.Error("重置API使用量失败", zap.Error(err)) return fmt.Errorf("重置API使用量失败: %w", err) } s.logger.Info("重置API使用量成功", zap.String("id", cmd.ID)) return nil } // GetUserSubscriptions 获取用户订阅 func (s *SubscriptionApplicationServiceImpl) GetUserSubscriptions(ctx context.Context, query *appQueries.GetUserSubscriptionsQuery) ([]*responses.SubscriptionInfoResponse, error) { subscriptions, err := s.subscriptionRepo.FindByUserID(ctx, query.UserID) if err != nil { s.logger.Error("获取用户订阅失败", zap.Error(err)) return nil, fmt.Errorf("获取用户订阅失败: %w", err) } // 过滤状态 if query.Status != nil { filtered := make([]*entities.Subscription, 0) for _, sub := range subscriptions { if sub.Status == *query.Status { filtered = append(filtered, sub) } } subscriptions = filtered } // 转换为响应对象 items := make([]*responses.SubscriptionInfoResponse, len(subscriptions)) for i, subscription := range subscriptions { items[i] = s.convertToSubscriptionInfoResponse(subscription) } return items, nil } // GetProductSubscriptions 获取产品订阅 func (s *SubscriptionApplicationServiceImpl) GetProductSubscriptions(ctx context.Context, query *appQueries.GetProductSubscriptionsQuery) ([]*responses.SubscriptionInfoResponse, error) { subscriptions, err := s.subscriptionRepo.FindByProductID(ctx, query.ProductID) if err != nil { s.logger.Error("获取产品订阅失败", zap.Error(err)) return nil, fmt.Errorf("获取产品订阅失败: %w", err) } // 过滤状态 if query.Status != nil { filtered := make([]*entities.Subscription, 0) for _, sub := range subscriptions { if sub.Status == *query.Status { filtered = append(filtered, sub) } } subscriptions = filtered } // 转换为响应对象 items := make([]*responses.SubscriptionInfoResponse, len(subscriptions)) for i, subscription := range subscriptions { items[i] = s.convertToSubscriptionInfoResponse(subscription) } return items, nil } // GetSubscriptionUsage 获取订阅使用情况 func (s *SubscriptionApplicationServiceImpl) GetSubscriptionUsage(ctx context.Context, subscriptionID string) (*responses.SubscriptionUsageResponse, error) { subscription, err := s.subscriptionRepo.GetByID(ctx, subscriptionID) if err != nil { return nil, fmt.Errorf("订阅不存在: %w", err) } return &responses.SubscriptionUsageResponse{ ID: subscription.ID, ProductID: subscription.ProductID, APIUsed: subscription.APIUsed, }, nil } // GetSubscriptionStats 获取订阅统计 func (s *SubscriptionApplicationServiceImpl) GetSubscriptionStats(ctx context.Context) (*responses.SubscriptionStatsResponse, error) { // 获取各种状态的订阅数量 total, err := s.subscriptionRepo.CountActive(ctx) if err != nil { s.logger.Error("获取订阅统计失败", zap.Error(err)) return nil, fmt.Errorf("获取订阅统计失败: %w", err) } // TODO: 计算总收入,需要从订单系统获取 totalRevenue := 0.0 return &responses.SubscriptionStatsResponse{ TotalSubscriptions: total, TotalRevenue: totalRevenue, }, nil } // 私有方法 func (s *SubscriptionApplicationServiceImpl) validateCreateSubscription(cmd *commands.CreateSubscriptionCommand) error { if cmd.UserID == "" { return errors.New("用户ID不能为空") } if cmd.ProductID == "" { return errors.New("产品ID不能为空") } if cmd.Price < 0 { return errors.New("订阅价格不能为负数") } if cmd.APILimit < 0 { return errors.New("API调用限制不能为负数") } return nil } func (s *SubscriptionApplicationServiceImpl) calculateEndDate(duration string) (*time.Time, error) { if duration == "" { return nil, nil // 永久订阅 } d, err := s.parseDuration(duration) if err != nil { return nil, err } endDate := time.Now().Add(d) return &endDate, nil } func (s *SubscriptionApplicationServiceImpl) parseDuration(duration string) (time.Duration, error) { switch duration { case "7d": return 7 * 24 * time.Hour, nil case "30d": return 30 * 24 * time.Hour, nil case "90d": return 90 * 24 * time.Hour, nil case "365d": return 365 * 24 * time.Hour, nil default: return time.ParseDuration(duration) } } func (s *SubscriptionApplicationServiceImpl) convertToSubscriptionInfoResponse(subscription *entities.Subscription) *responses.SubscriptionInfoResponse { return &responses.SubscriptionInfoResponse{ ID: subscription.ID, UserID: subscription.UserID, ProductID: subscription.ProductID, Price: subscription.Price, APIUsed: subscription.APIUsed, CreatedAt: subscription.CreatedAt, UpdatedAt: subscription.UpdatedAt, } } func (s *SubscriptionApplicationServiceImpl) convertToProductSimpleResponse(product *entities.Product) *responses.ProductSimpleResponse { return &responses.ProductSimpleResponse{ ID: product.ID, Name: product.Name, Code: product.Code, Description: product.Description, Price: product.Price, IsPackage: product.IsPackage, } }