package api import ( "context" "errors" "tyapi-server/internal/application/api/commands" "tyapi-server/internal/application/api/dto" "tyapi-server/internal/config" entities "tyapi-server/internal/domains/api/entities" "tyapi-server/internal/domains/api/repositories" "tyapi-server/internal/domains/api/services" "tyapi-server/internal/domains/api/services/processors" finance_services "tyapi-server/internal/domains/finance/services" product_services "tyapi-server/internal/domains/product/services" "tyapi-server/internal/shared/crypto" "tyapi-server/internal/shared/database" "tyapi-server/internal/shared/interfaces" "go.uber.org/zap" ) type ApiApplicationService interface { CallApi(ctx context.Context, cmd *commands.ApiCallCommand) (string, string, error) // 获取用户API密钥 GetUserApiKeys(ctx context.Context, userID string) (*dto.ApiKeysResponse, error) // 用户白名单管理 GetUserWhiteList(ctx context.Context, userID string) (*dto.WhiteListListResponse, error) AddWhiteListIP(ctx context.Context, userID string, ipAddress string) error DeleteWhiteListIP(ctx context.Context, userID string, ipAddress string) error // 获取用户API调用记录 GetUserApiCalls(ctx context.Context, userID string, filters map[string]interface{}, options interfaces.ListOptions) (*dto.ApiCallListResponse, error) } type ApiApplicationServiceImpl struct { apiCallService services.ApiCallAggregateService apiUserService services.ApiUserAggregateService apiRequestService *services.ApiRequestService apiCallRepository repositories.ApiCallRepository walletService finance_services.WalletAggregateService productManagementService *product_services.ProductManagementService productSubscriptionService *product_services.ProductSubscriptionService txManager *database.TransactionManager config *config.Config logger *zap.Logger } func NewApiApplicationService(apiCallService services.ApiCallAggregateService, apiUserService services.ApiUserAggregateService, apiRequestService *services.ApiRequestService, apiCallRepository repositories.ApiCallRepository, walletService finance_services.WalletAggregateService, productManagementService *product_services.ProductManagementService, productSubscriptionService *product_services.ProductSubscriptionService, txManager *database.TransactionManager, config *config.Config, logger *zap.Logger) ApiApplicationService { return &ApiApplicationServiceImpl{apiCallService: apiCallService, apiUserService: apiUserService, apiRequestService: apiRequestService, apiCallRepository: apiCallRepository, walletService: walletService, productManagementService: productManagementService, productSubscriptionService: productSubscriptionService, txManager: txManager, config: config, logger: logger} } // CallApi 应用服务层统一入口 func (s *ApiApplicationServiceImpl) CallApi(ctx context.Context, cmd *commands.ApiCallCommand) (string, string, error) { // 在事务外创建ApiCall apiCall, err := s.apiCallService.CreateApiCall(cmd.AccessId, cmd.Data, cmd.ClientIP) if err != nil { s.logger.Error("创建ApiCall失败", zap.Error(err)) return "", "", ErrSystem } transactionId := apiCall.TransactionId // 先保存初始状态 err = s.apiCallService.SaveApiCall(ctx, apiCall) if err != nil { s.logger.Error("保存ApiCall初始状态失败", zap.Error(err)) return "", "", ErrSystem } var encryptedResponse string var businessError error // 在事务中执行业务逻辑 err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error { // 1. 查ApiUser apiUser, err := s.apiUserService.LoadApiUserByAccessId(txCtx, cmd.AccessId) if err != nil { s.logger.Error("查ApiUser失败", zap.Error(err)) businessError = ErrInvalidAccessId return ErrInvalidAccessId } // 加入UserId apiCall.UserId = &apiUser.UserId if apiUser.IsFrozen() { s.logger.Error("账户已冻结", zap.String("userId", apiUser.UserId)) businessError = ErrFrozenAccount return ErrFrozenAccount } // 在开发环境下跳过IP白名单校验 if s.config.App.IsDevelopment() { s.logger.Info("开发环境跳过IP白名单校验", zap.String("userId", apiUser.UserId), zap.String("ip", cmd.ClientIP)) } else { if !apiUser.IsWhiteListed(cmd.ClientIP) { s.logger.Error("IP不在白名单内", zap.String("userId", apiUser.UserId), zap.String("ip", cmd.ClientIP)) businessError = ErrInvalidIP return ErrInvalidIP } } // 2. 查钱包 wallet, err := s.walletService.LoadWalletByUserId(txCtx, apiUser.UserId) if err != nil { s.logger.Error("查钱包失败", zap.Error(err)) businessError = ErrSystem return ErrSystem } if wallet.IsArrears() { s.logger.Error("账户已欠费", zap.String("userId", apiUser.UserId)) businessError = ErrArrears return ErrArrears } // 3. 查产品 product, err := s.productManagementService.GetProductByCode(txCtx, cmd.ApiName) if err != nil { s.logger.Error("查产品失败", zap.Error(err)) businessError = ErrProductNotFound return ErrProductNotFound } // 4. 查订阅 subscription, err := s.productSubscriptionService.GetUserSubscribedProduct(txCtx, apiUser.UserId, product.ID) if err != nil { s.logger.Error("查订阅失败", zap.Error(err)) businessError = ErrSystem return ErrSystem } if subscription == nil { s.logger.Error("用户未订阅该产品", zap.String("userId", apiUser.UserId), zap.String("productId", product.ID)) businessError = ErrNotSubscribed return ErrNotSubscribed } apiCall.ProductId = &product.ID if !product.IsValid() { s.logger.Error("产品已停用", zap.String("productId", product.ID)) businessError = ErrProductDisabled return ErrProductDisabled } // 5. 解密参数 requestParams, err := crypto.AesDecrypt(cmd.Data, apiUser.SecretKey) if err != nil { s.logger.Error("解密参数失败", zap.Error(err)) businessError = ErrDecryptFail return ErrDecryptFail } // 6. 调用API response, err := s.apiRequestService.PreprocessRequestApi(txCtx, cmd.ApiName, requestParams, &cmd.Options) if err != nil { if errors.Is(err, processors.ErrDatasource) { s.logger.Error("调用API失败", zap.Error(err)) businessError = ErrSystem return ErrSystem } else if errors.Is(err, processors.ErrInvalidParam) { s.logger.Error("调用API失败", zap.Error(err)) businessError = ErrInvalidParam return ErrInvalidParam } else if errors.Is(err, processors.ErrNotFound) { s.logger.Error("调用API失败", zap.Error(err)) businessError = ErrQueryEmpty return ErrQueryEmpty } else { s.logger.Error("调用API失败", zap.Error(err)) businessError = ErrSystem return ErrSystem } } // 7. 加密响应 encryptedResponse, err = crypto.AesEncrypt(response, apiUser.SecretKey) if err != nil { s.logger.Error("加密响应失败", zap.Error(err)) businessError = ErrSystem return ErrSystem } apiCall.ResponseData = &encryptedResponse // 8. 更新订阅使用次数 subscription.IncrementAPIUsage(1) err = s.productSubscriptionService.SaveSubscription(txCtx, subscription) if err != nil { s.logger.Error("保存订阅失败", zap.Error(err)) businessError = ErrSystem return ErrSystem } // 9. 扣钱 err = s.walletService.Deduct(txCtx, apiUser.UserId, subscription.Price, apiCall.ID, transactionId, product.ID) if err != nil { s.logger.Error("扣钱失败", zap.Error(err)) businessError = ErrSystem return ErrSystem } apiCall.Cost = &subscription.Price // 10. 标记成功 apiCall.MarkSuccess(encryptedResponse, subscription.Price) return nil }) // 根据事务结果更新ApiCall状态 if err != nil { // 事务失败,根据错误类型标记ApiCall if businessError != nil { // 使用业务错误类型 switch businessError { case ErrInvalidAccessId: apiCall.MarkFailed(entities.ApiCallErrorInvalidAccess, err.Error()) case ErrFrozenAccount: apiCall.MarkFailed(entities.ApiCallErrorFrozenAccount, "") case ErrInvalidIP: apiCall.MarkFailed(entities.ApiCallErrorInvalidIP, "") case ErrArrears: apiCall.MarkFailed(entities.ApiCallErrorArrears, "") case ErrProductNotFound: apiCall.MarkFailed(entities.ApiCallErrorProductNotFound, err.Error()) case ErrProductDisabled: apiCall.MarkFailed(entities.ApiCallErrorProductDisabled, "") case ErrNotSubscribed: apiCall.MarkFailed(entities.ApiCallErrorNotSubscribed, "") case ErrDecryptFail: apiCall.MarkFailed(entities.ApiCallErrorDecryptFail, err.Error()) case ErrInvalidParam: apiCall.MarkFailed(entities.ApiCallErrorInvalidParam, err.Error()) case ErrQueryEmpty: apiCall.MarkFailed(entities.ApiCallErrorQueryEmpty, "") default: apiCall.MarkFailed(entities.ApiCallErrorSystem, err.Error()) } } else { // 系统错误 apiCall.MarkFailed(entities.ApiCallErrorSystem, err.Error()) } } // 保存最终状态 err = s.apiCallService.SaveApiCall(ctx, apiCall) if err != nil { s.logger.Error("保存ApiCall最终状态失败", zap.Error(err)) // 即使保存失败,也返回业务结果 } if businessError != nil { return transactionId, "", businessError } return transactionId, encryptedResponse, nil } // GetUserApiKeys 获取用户API密钥 func (s *ApiApplicationServiceImpl) GetUserApiKeys(ctx context.Context, userID string) (*dto.ApiKeysResponse, error) { apiUser, err := s.apiUserService.LoadApiUserByUserId(ctx, userID) if err != nil { return nil, err } return &dto.ApiKeysResponse{ ID: apiUser.ID, UserID: apiUser.UserId, AccessID: apiUser.AccessId, SecretKey: apiUser.SecretKey, Status: apiUser.Status, CreatedAt: apiUser.CreatedAt, UpdatedAt: apiUser.UpdatedAt, }, nil } // GetUserWhiteList 获取用户白名单列表 func (s *ApiApplicationServiceImpl) GetUserWhiteList(ctx context.Context, userID string) (*dto.WhiteListListResponse, error) { apiUser, err := s.apiUserService.LoadApiUserByUserId(ctx, userID) if err != nil { return nil, err } // 确保WhiteList不为nil if apiUser.WhiteList == nil { apiUser.WhiteList = []string{} } // 将白名单字符串数组转换为响应格式 var items []dto.WhiteListResponse for _, ip := range apiUser.WhiteList { items = append(items, dto.WhiteListResponse{ ID: apiUser.ID, // 使用API用户ID作为标识 UserID: apiUser.UserId, IPAddress: ip, CreatedAt: apiUser.CreatedAt, // 使用API用户创建时间 }) } return &dto.WhiteListListResponse{ Items: items, Total: len(items), }, nil } // AddWhiteListIP 添加白名单IP func (s *ApiApplicationServiceImpl) AddWhiteListIP(ctx context.Context, userID string, ipAddress string) error { apiUser, err := s.apiUserService.LoadApiUserByUserId(ctx, userID) if err != nil { return err } // 确保WhiteList不为nil if apiUser.WhiteList == nil { apiUser.WhiteList = []string{} } // 使用实体的领域方法添加IP到白名单 err = apiUser.AddToWhiteList(ipAddress) if err != nil { return err } // 保存更新 err = s.apiUserService.SaveApiUser(ctx, apiUser) if err != nil { return err } return nil } // DeleteWhiteListIP 删除白名单IP func (s *ApiApplicationServiceImpl) DeleteWhiteListIP(ctx context.Context, userID string, ipAddress string) error { apiUser, err := s.apiUserService.LoadApiUserByUserId(ctx, userID) if err != nil { return err } // 确保WhiteList不为nil if apiUser.WhiteList == nil { apiUser.WhiteList = []string{} } // 使用实体的领域方法删除IP err = apiUser.RemoveFromWhiteList(ipAddress) if err != nil { return err } // 保存更新 err = s.apiUserService.SaveApiUser(ctx, apiUser) if err != nil { return err } return nil } // GetUserApiCalls 获取用户API调用记录 func (s *ApiApplicationServiceImpl) GetUserApiCalls(ctx context.Context, userID string, filters map[string]interface{}, options interfaces.ListOptions) (*dto.ApiCallListResponse, error) { // 查询API调用记录(包含产品名称) productNameMap, calls, total, err := s.apiCallRepository.ListByUserIdWithFiltersAndProductName(ctx, userID, filters, options) if err != nil { s.logger.Error("查询API调用记录失败", zap.Error(err), zap.String("userID", userID)) return nil, err } // 转换为响应DTO var items []dto.ApiCallRecordResponse for _, call := range calls { item := dto.ApiCallRecordResponse{ ID: call.ID, AccessId: call.AccessId, UserId: *call.UserId, TransactionId: call.TransactionId, ClientIp: call.ClientIp, Status: call.Status, StartAt: call.StartAt.Format("2006-01-02 15:04:05"), CreatedAt: call.CreatedAt.Format("2006-01-02 15:04:05"), UpdatedAt: call.UpdatedAt.Format("2006-01-02 15:04:05"), } // 处理可选字段 if call.ProductId != nil { item.ProductId = call.ProductId } // 从映射中获取产品名称 if productName, exists := productNameMap[call.ID]; exists { item.ProductName = &productName } if call.EndAt != nil { endAt := call.EndAt.Format("2006-01-02 15:04:05") item.EndAt = &endAt } if call.Cost != nil { cost := call.Cost.String() item.Cost = &cost } if call.ErrorType != nil { item.ErrorType = call.ErrorType } if call.ErrorMsg != nil { item.ErrorMsg = call.ErrorMsg } items = append(items, item) } return &dto.ApiCallListResponse{ Items: items, Total: total, Page: options.Page, Size: options.PageSize, }, nil }