Files
tyapi-server/internal/application/product/subscription_application_service_impl.go
2025-08-23 16:30:34 +08:00

314 lines
11 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 product
import (
"context"
"github.com/shopspring/decimal"
"go.uber.org/zap"
"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"
repoQueries "tyapi-server/internal/domains/product/repositories/queries"
product_service "tyapi-server/internal/domains/product/services"
user_repositories "tyapi-server/internal/domains/user/repositories"
)
// SubscriptionApplicationServiceImpl 订阅应用服务实现
// 负责业务流程编排、事务管理、数据转换,不直接操作仓库
type SubscriptionApplicationServiceImpl struct {
productSubscriptionService *product_service.ProductSubscriptionService
userRepo user_repositories.UserRepository
logger *zap.Logger
}
// NewSubscriptionApplicationService 创建订阅应用服务
func NewSubscriptionApplicationService(
productSubscriptionService *product_service.ProductSubscriptionService,
userRepo user_repositories.UserRepository,
logger *zap.Logger,
) SubscriptionApplicationService {
return &SubscriptionApplicationServiceImpl{
productSubscriptionService: productSubscriptionService,
userRepo: userRepo,
logger: logger,
}
}
// UpdateSubscriptionPrice 更新订阅价格
// 业务流程1. 获取订阅 2. 更新价格 3. 保存订阅
func (s *SubscriptionApplicationServiceImpl) UpdateSubscriptionPrice(ctx context.Context, cmd *commands.UpdateSubscriptionPriceCommand) error {
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 {
_, err := s.productSubscriptionService.CreateSubscription(ctx, cmd.UserID, cmd.ProductID)
return err
}
// GetSubscriptionByID 根据ID获取订阅
// 业务流程1. 获取订阅信息 2. 构建响应数据
func (s *SubscriptionApplicationServiceImpl) GetSubscriptionByID(ctx context.Context, query *appQueries.GetSubscriptionQuery) (*responses.SubscriptionInfoResponse, error) {
subscription, err := s.productSubscriptionService.GetSubscriptionByID(ctx, query.ID)
if err != nil {
return nil, err
}
return s.convertToSubscriptionInfoResponse(subscription), nil
}
// ListSubscriptions 获取订阅列表(管理员用)
// 业务流程1. 获取订阅列表 2. 构建响应数据
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, // 管理员可以按用户筛选
Keyword: query.Keyword,
SortBy: query.SortBy,
SortOrder: query.SortOrder,
CompanyName: query.CompanyName,
ProductName: query.ProductName,
StartTime: query.StartTime,
EndTime: query.EndTime,
}
subscriptions, total, err := s.productSubscriptionService.ListSubscriptions(ctx, repoQuery)
if err != nil {
return nil, err
}
items := make([]responses.SubscriptionInfoResponse, len(subscriptions))
for i := range subscriptions {
resp := s.convertToSubscriptionInfoResponse(subscriptions[i])
if resp != nil {
items[i] = *resp // 解引用指针
}
}
return &responses.SubscriptionListResponse{
Total: total,
Page: query.Page,
Size: query.PageSize,
Items: items,
}, nil
}
// ListMySubscriptions 获取我的订阅列表(用户用)
// 业务流程1. 获取用户订阅列表 2. 构建响应数据
func (s *SubscriptionApplicationServiceImpl) ListMySubscriptions(ctx context.Context, userID string, query *appQueries.ListSubscriptionsQuery) (*responses.SubscriptionListResponse, error) {
repoQuery := &repoQueries.ListSubscriptionsQuery{
Page: query.Page,
PageSize: query.PageSize,
UserID: userID, // 强制设置为当前用户ID
Keyword: query.Keyword,
SortBy: query.SortBy,
SortOrder: query.SortOrder,
CompanyName: query.CompanyName,
ProductName: query.ProductName,
StartTime: query.StartTime,
EndTime: query.EndTime,
}
subscriptions, total, err := s.productSubscriptionService.ListSubscriptions(ctx, repoQuery)
if err != nil {
return nil, err
}
items := make([]responses.SubscriptionInfoResponse, len(subscriptions))
for i := range subscriptions {
resp := s.convertToSubscriptionInfoResponse(subscriptions[i])
if resp != nil {
items[i] = *resp // 解引用指针
}
}
return &responses.SubscriptionListResponse{
Total: total,
Page: query.Page,
Size: query.PageSize,
Items: items,
}, nil
}
// GetUserSubscriptions 获取用户订阅
// 业务流程1. 获取用户订阅 2. 构建响应数据
func (s *SubscriptionApplicationServiceImpl) GetUserSubscriptions(ctx context.Context, query *appQueries.GetUserSubscriptionsQuery) ([]*responses.SubscriptionInfoResponse, error) {
subscriptions, err := s.productSubscriptionService.GetUserSubscriptions(ctx, query.UserID)
if err != nil {
return nil, err
}
// 转换为响应对象
items := make([]*responses.SubscriptionInfoResponse, len(subscriptions))
for i := range subscriptions {
items[i] = s.convertToSubscriptionInfoResponse(subscriptions[i])
}
return items, nil
}
// GetProductSubscriptions 获取产品订阅
// 业务流程1. 获取产品订阅 2. 构建响应数据
func (s *SubscriptionApplicationServiceImpl) GetProductSubscriptions(ctx context.Context, query *appQueries.GetProductSubscriptionsQuery) ([]*responses.SubscriptionInfoResponse, error) {
// 这里需要扩展领域服务来支持按产品查询订阅
// 暂时返回空列表
return []*responses.SubscriptionInfoResponse{}, nil
}
// GetSubscriptionUsage 获取订阅使用情况
// 业务流程1. 获取订阅使用情况 2. 构建响应数据
func (s *SubscriptionApplicationServiceImpl) GetSubscriptionUsage(ctx context.Context, subscriptionID string) (*responses.SubscriptionUsageResponse, error) {
subscription, err := s.productSubscriptionService.GetSubscriptionByID(ctx, subscriptionID)
if err != nil {
return nil, err
}
return &responses.SubscriptionUsageResponse{
ID: subscription.ID,
ProductID: subscription.ProductID,
APIUsed: subscription.APIUsed,
}, nil
}
// GetSubscriptionStats 获取订阅统计信息
// 业务流程1. 获取订阅统计 2. 构建响应数据
func (s *SubscriptionApplicationServiceImpl) GetSubscriptionStats(ctx context.Context) (*responses.SubscriptionStatsResponse, error) {
stats, err := s.productSubscriptionService.GetSubscriptionStats(ctx)
if err != nil {
return nil, err
}
return &responses.SubscriptionStatsResponse{
TotalSubscriptions: stats["total_subscriptions"].(int64),
TotalRevenue: stats["total_revenue"].(float64),
}, nil
}
// GetMySubscriptionStats 获取我的订阅统计信息
// 业务流程1. 获取用户订阅统计 2. 构建响应数据
func (s *SubscriptionApplicationServiceImpl) GetMySubscriptionStats(ctx context.Context, userID string) (*responses.SubscriptionStatsResponse, error) {
stats, err := s.productSubscriptionService.GetUserSubscriptionStats(ctx, userID)
if err != nil {
return nil, err
}
return &responses.SubscriptionStatsResponse{
TotalSubscriptions: stats["total_subscriptions"].(int64),
TotalRevenue: stats["total_revenue"].(float64),
}, nil
}
// convertToSubscriptionInfoResponse 转换为订阅信息响应
func (s *SubscriptionApplicationServiceImpl) convertToSubscriptionInfoResponse(subscription *entities.Subscription) *responses.SubscriptionInfoResponse {
// 查询用户信息
var userInfo *responses.UserSimpleResponse
if subscription.UserID != "" {
user, err := s.userRepo.GetByIDWithEnterpriseInfo(context.Background(), subscription.UserID)
if err == nil {
companyName := "未知公司"
if user.EnterpriseInfo != nil {
companyName = user.EnterpriseInfo.CompanyName
}
userInfo = &responses.UserSimpleResponse{
ID: user.ID,
CompanyName: companyName,
Phone: user.Phone,
}
}
}
var productResponse *responses.ProductSimpleResponse
if subscription.Product != nil {
productResponse = s.convertToProductSimpleResponse(subscription.Product)
}
return &responses.SubscriptionInfoResponse{
ID: subscription.ID,
UserID: subscription.UserID,
ProductID: subscription.ProductID,
Price: subscription.Price.InexactFloat64(),
User: userInfo,
Product: productResponse,
APIUsed: subscription.APIUsed,
CreatedAt: subscription.CreatedAt,
UpdatedAt: subscription.UpdatedAt,
}
}
// convertToProductSimpleResponse 转换为产品简单信息响应
func (s *SubscriptionApplicationServiceImpl) convertToProductSimpleResponse(product *entities.Product) *responses.ProductSimpleResponse {
var categoryResponse *responses.CategorySimpleResponse
if product.Category != nil {
categoryResponse = s.convertToCategorySimpleResponse(product.Category)
}
return &responses.ProductSimpleResponse{
ID: product.ID,
OldID: product.OldID,
Name: product.Name,
Code: product.Code,
Description: product.Description,
Price: product.Price.InexactFloat64(),
Category: categoryResponse,
IsPackage: product.IsPackage,
}
}
// convertToCategorySimpleResponse 转换为分类简单信息响应
func (s *SubscriptionApplicationServiceImpl) convertToCategorySimpleResponse(category *entities.ProductCategory) *responses.CategorySimpleResponse {
if category == nil {
return nil
}
return &responses.CategorySimpleResponse{
ID: category.ID,
Name: category.Name,
}
}