Files
tyapi-server/internal/application/product/subscription_application_service_impl.go
2025-07-15 13:21:34 +08:00

354 lines
11 KiB
Go

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