f
This commit is contained in:
@@ -20,6 +20,8 @@ import (
|
|||||||
finance_services "tyapi-server/internal/domains/finance/services"
|
finance_services "tyapi-server/internal/domains/finance/services"
|
||||||
product_entities "tyapi-server/internal/domains/product/entities"
|
product_entities "tyapi-server/internal/domains/product/entities"
|
||||||
product_services "tyapi-server/internal/domains/product/services"
|
product_services "tyapi-server/internal/domains/product/services"
|
||||||
|
subordinate_entities "tyapi-server/internal/domains/subordinate/entities"
|
||||||
|
subordinate_repositories "tyapi-server/internal/domains/subordinate/repositories"
|
||||||
user_repositories "tyapi-server/internal/domains/user/repositories"
|
user_repositories "tyapi-server/internal/domains/user/repositories"
|
||||||
task_entities "tyapi-server/internal/infrastructure/task/entities"
|
task_entities "tyapi-server/internal/infrastructure/task/entities"
|
||||||
"tyapi-server/internal/infrastructure/task/interfaces"
|
"tyapi-server/internal/infrastructure/task/interfaces"
|
||||||
@@ -93,6 +95,7 @@ type ApiApplicationServiceImpl struct {
|
|||||||
walletService finance_services.WalletAggregateService
|
walletService finance_services.WalletAggregateService
|
||||||
subscriptionService *product_services.ProductSubscriptionService
|
subscriptionService *product_services.ProductSubscriptionService
|
||||||
balanceAlertService finance_services.BalanceAlertService
|
balanceAlertService finance_services.BalanceAlertService
|
||||||
|
subordinateRepo subordinate_repositories.SubordinateRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApiApplicationService(
|
func NewApiApplicationService(
|
||||||
@@ -112,6 +115,7 @@ func NewApiApplicationService(
|
|||||||
subscriptionService *product_services.ProductSubscriptionService,
|
subscriptionService *product_services.ProductSubscriptionService,
|
||||||
exportManager *export.ExportManager,
|
exportManager *export.ExportManager,
|
||||||
balanceAlertService finance_services.BalanceAlertService,
|
balanceAlertService finance_services.BalanceAlertService,
|
||||||
|
subordinateRepo subordinate_repositories.SubordinateRepository,
|
||||||
) ApiApplicationService {
|
) ApiApplicationService {
|
||||||
service := &ApiApplicationServiceImpl{
|
service := &ApiApplicationServiceImpl{
|
||||||
apiCallService: apiCallService,
|
apiCallService: apiCallService,
|
||||||
@@ -130,6 +134,7 @@ func NewApiApplicationService(
|
|||||||
walletService: walletService,
|
walletService: walletService,
|
||||||
subscriptionService: subscriptionService,
|
subscriptionService: subscriptionService,
|
||||||
balanceAlertService: balanceAlertService,
|
balanceAlertService: balanceAlertService,
|
||||||
|
subordinateRepo: subordinateRepo,
|
||||||
}
|
}
|
||||||
|
|
||||||
return service
|
return service
|
||||||
@@ -1108,6 +1113,24 @@ func (s *ApiApplicationServiceImpl) ProcessDeduction(ctx context.Context, cmd *c
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 优先扣减产品额度(若存在且可用),避免子账号有额度却因钱包余额不足失败
|
||||||
|
deductedByQuota, err := s.tryDeductQuota(ctx, cmd.UserID, cmd.ProductID, cmd.ApiCallID, cmd.TransactionID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("额度扣减失败",
|
||||||
|
zap.String("transaction_id", cmd.TransactionID),
|
||||||
|
zap.String("user_id", cmd.UserID),
|
||||||
|
zap.String("product_id", cmd.ProductID),
|
||||||
|
zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if deductedByQuota {
|
||||||
|
s.logger.Info("额度扣减成功",
|
||||||
|
zap.String("transaction_id", cmd.TransactionID),
|
||||||
|
zap.String("user_id", cmd.UserID),
|
||||||
|
zap.String("product_id", cmd.ProductID))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.walletService.Deduct(ctx, cmd.UserID, amount, cmd.ApiCallID, cmd.TransactionID, cmd.ProductID); err != nil {
|
if err := s.walletService.Deduct(ctx, cmd.UserID, amount, cmd.ApiCallID, cmd.TransactionID, cmd.ProductID); err != nil {
|
||||||
s.logger.Error("扣款处理失败",
|
s.logger.Error("扣款处理失败",
|
||||||
zap.String("transaction_id", cmd.TransactionID),
|
zap.String("transaction_id", cmd.TransactionID),
|
||||||
@@ -1202,6 +1225,25 @@ func (s *ApiApplicationServiceImpl) ProcessCompensation(ctx context.Context, cmd
|
|||||||
|
|
||||||
// validateWalletStatus 验证钱包状态
|
// validateWalletStatus 验证钱包状态
|
||||||
func (s *ApiApplicationServiceImpl) validateWalletStatus(ctx context.Context, userID string, product *product_entities.Product, subscription *product_entities.Subscription) error {
|
func (s *ApiApplicationServiceImpl) validateWalletStatus(ctx context.Context, userID string, product *product_entities.Product, subscription *product_entities.Subscription) error {
|
||||||
|
// 若用户在该产品有可用额度,则本次调用将走额度扣减,不再要求钱包余额预检通过
|
||||||
|
if s.subordinateRepo != nil {
|
||||||
|
quotaAccount, err := s.subordinateRepo.FindQuotaAccount(ctx, userID, product.ID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("查询额度账户失败",
|
||||||
|
zap.String("user_id", userID),
|
||||||
|
zap.String("product_id", product.ID),
|
||||||
|
zap.Error(err))
|
||||||
|
return ErrSystem
|
||||||
|
}
|
||||||
|
if quotaAccount != nil && quotaAccount.AvailableQuota > 0 {
|
||||||
|
s.logger.Info("额度校验通过,跳过钱包余额预检",
|
||||||
|
zap.String("user_id", userID),
|
||||||
|
zap.String("product_id", product.ID),
|
||||||
|
zap.Int64("available_quota", quotaAccount.AvailableQuota))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 获取用户钱包信息
|
// 1. 获取用户钱包信息
|
||||||
wallet, err := s.walletService.LoadWalletByUserId(ctx, userID)
|
wallet, err := s.walletService.LoadWalletByUserId(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1251,6 +1293,56 @@ func (s *ApiApplicationServiceImpl) validateWalletStatus(ctx context.Context, us
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tryDeductQuota 尝试扣减产品额度;若不存在额度账户则返回 false,nil 以便回退钱包扣款
|
||||||
|
func (s *ApiApplicationServiceImpl) tryDeductQuota(ctx context.Context, userID, productID, apiCallID, transactionID string) (bool, error) {
|
||||||
|
if s.subordinateRepo == nil || productID == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var deducted bool
|
||||||
|
err := s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
|
||||||
|
account, err := s.subordinateRepo.FindQuotaAccount(txCtx, userID, productID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if account.AvailableQuota <= 0 {
|
||||||
|
return ErrInsufficientBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
before := account.AvailableQuota
|
||||||
|
account.AvailableQuota -= 1
|
||||||
|
account.UsedQuota += 1
|
||||||
|
if err := s.subordinateRepo.UpdateQuotaAccount(txCtx, account); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ledger := &subordinate_entities.UserProductQuotaLedger{
|
||||||
|
UserID: userID,
|
||||||
|
ProductID: productID,
|
||||||
|
ChangeType: subordinate_entities.QuotaLedgerChangeTypeConsumeAPI,
|
||||||
|
DeltaQuota: -1,
|
||||||
|
BeforeQuota: before,
|
||||||
|
AfterQuota: account.AvailableQuota,
|
||||||
|
SourceID: apiCallID,
|
||||||
|
OperatorID: userID,
|
||||||
|
Remark: fmt.Sprintf("API调用扣减,transaction_id=%s", transactionID),
|
||||||
|
}
|
||||||
|
if err := s.subordinateRepo.CreateQuotaLedger(txCtx, ledger); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
deducted = true
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return deducted, nil
|
||||||
|
}
|
||||||
|
|
||||||
// validateSubscriptionStatus 验证订阅状态并返回订阅信息
|
// validateSubscriptionStatus 验证订阅状态并返回订阅信息
|
||||||
func (s *ApiApplicationServiceImpl) validateSubscriptionStatus(ctx context.Context, userID string, product *product_entities.Product) (*product_entities.Subscription, error) {
|
func (s *ApiApplicationServiceImpl) validateSubscriptionStatus(ctx context.Context, userID string, product *product_entities.Product) (*product_entities.Subscription, error) {
|
||||||
// 1. 检查用户是否已订阅该产品
|
// 1. 检查用户是否已订阅该产品
|
||||||
|
|||||||
@@ -811,6 +811,7 @@ func NewContainer() *Container {
|
|||||||
subscriptionService *product_services.ProductSubscriptionService,
|
subscriptionService *product_services.ProductSubscriptionService,
|
||||||
exportManager *export.ExportManager,
|
exportManager *export.ExportManager,
|
||||||
balanceAlertService finance_services.BalanceAlertService,
|
balanceAlertService finance_services.BalanceAlertService,
|
||||||
|
subordinateRepo domain_subordinate_repo.SubordinateRepository,
|
||||||
) api_app.ApiApplicationService {
|
) api_app.ApiApplicationService {
|
||||||
return api_app.NewApiApplicationService(
|
return api_app.NewApiApplicationService(
|
||||||
apiCallService,
|
apiCallService,
|
||||||
@@ -829,6 +830,7 @@ func NewContainer() *Container {
|
|||||||
subscriptionService,
|
subscriptionService,
|
||||||
exportManager,
|
exportManager,
|
||||||
balanceAlertService,
|
balanceAlertService,
|
||||||
|
subordinateRepo,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
fx.As(new(api_app.ApiApplicationService)),
|
fx.As(new(api_app.ApiApplicationService)),
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
// QuotaLedgerChangeTypePurchaseForSub 主账号为子账号购买额度
|
// QuotaLedgerChangeTypePurchaseForSub 主账号为子账号购买额度
|
||||||
QuotaLedgerChangeTypePurchaseForSub = "purchase_for_sub"
|
QuotaLedgerChangeTypePurchaseForSub = "purchase_for_sub"
|
||||||
|
// QuotaLedgerChangeTypeConsumeAPI 用户调用API消耗额度
|
||||||
|
QuotaLedgerChangeTypeConsumeAPI = "api_consume"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SubordinateQuotaPurchase 主账号为子账号购买额度记录
|
// SubordinateQuotaPurchase 主账号为子账号购买额度记录
|
||||||
|
|||||||
Reference in New Issue
Block a user