package api import ( "context" "encoding/json" "errors" "tyapi-server/internal/application/api/commands" "tyapi-server/internal/application/api/dto" "tyapi-server/internal/application/api/utils" "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" user_repositories "tyapi-server/internal/domains/user/repositories" "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) // 管理端API调用记录 GetAdminApiCalls(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*dto.ApiCallListResponse, error) // 加密参数接口 EncryptParams(ctx context.Context, userID string, cmd *commands.EncryptCommand) (string, error) // 解密参数接口 DecryptParams(ctx context.Context, userID string, cmd *commands.DecryptCommand) (map[string]interface{}, error) } type ApiApplicationServiceImpl struct { apiCallService services.ApiCallAggregateService apiUserService services.ApiUserAggregateService apiRequestService *services.ApiRequestService apiCallRepository repositories.ApiCallRepository walletService finance_services.WalletAggregateService contractInfoService user_repositories.ContractInfoRepository productManagementService *product_services.ProductManagementService productSubscriptionService *product_services.ProductSubscriptionService userRepo user_repositories.UserRepository 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, userRepo user_repositories.UserRepository, txManager *database.TransactionManager, config *config.Config, logger *zap.Logger, contractInfoService user_repositories.ContractInfoRepository) ApiApplicationService { return &ApiApplicationServiceImpl{apiCallService: apiCallService, apiUserService: apiUserService, apiRequestService: apiRequestService, apiCallRepository: apiCallRepository, walletService: walletService, productManagementService: productManagementService, productSubscriptionService: productSubscriptionService, userRepo: userRepo, txManager: txManager, config: config, logger: logger, contractInfoService: contractInfoService} } // 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 } // 3. 查产品 product, err := s.productManagementService.GetProductByCode(txCtx, cmd.ApiName) if err != nil { s.logger.Error("查产品失败", zap.Error(err)) businessError = ErrProductNotFound return ErrProductNotFound } apiCall.ProductId = &product.ID // 加入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() || cmd.Options.IsDebug { s.logger.Info("跳过IP白名单校验", zap.String("userId", apiUser.UserId), zap.String("ip", cmd.ClientIP), zap.Bool("isDevelopment", s.config.App.IsDevelopment()), zap.Bool("isDebug", cmd.Options.IsDebug)) } 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 } // 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 } 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 // 查询用户的合同信息获取合同编号 contractCode := "" // 默认空字符串 contractInfo, err := s.contractInfoService.FindByUserID(txCtx, apiUser.UserId) if err == nil && len(contractInfo) > 0 { contractCode = contractInfo[0].ContractCode } else { s.logger.Error("查合同信息失败", zap.Error(err)) businessError = ErrSystem return ErrSystem } // 创建CallContext,传递合同编号 callContext := &processors.CallContext{ ContractCode: contractCode, } response, err := s.apiRequestService.PreprocessRequestApi(txCtx, cmd.ApiName, requestParams, &cmd.Options, callContext) 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. 更新订阅使用次数(使用乐观锁) // err = s.productSubscriptionService.IncrementSubscriptionAPIUsage(txCtx, subscription.ID, 1) // 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(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 // 添加翻译后的错误信息 item.TranslatedErrorMsg = utils.TranslateErrorMsg(call.ErrorType, call.ErrorMsg) } items = append(items, item) } return &dto.ApiCallListResponse{ Items: items, Total: total, Page: options.Page, Size: options.PageSize, }, nil } // GetAdminApiCalls 获取管理端API调用记录 func (s *ApiApplicationServiceImpl) GetAdminApiCalls(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*dto.ApiCallListResponse, error) { // 查询API调用记录(包含产品名称) productNameMap, calls, total, err := s.apiCallRepository.ListWithFiltersAndProductName(ctx, filters, options) if err != nil { s.logger.Error("查询API调用记录失败", zap.Error(err)) 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 // 添加翻译后的错误信息 item.TranslatedErrorMsg = utils.TranslateErrorMsg(call.ErrorType, call.ErrorMsg) } // 获取用户信息和企业名称 if call.UserId != nil { user, err := s.userRepo.GetByIDWithEnterpriseInfo(ctx, *call.UserId) if err == nil { companyName := "未知企业" if user.EnterpriseInfo != nil { companyName = user.EnterpriseInfo.CompanyName } item.CompanyName = &companyName item.User = &dto.UserSimpleResponse{ ID: user.ID, CompanyName: companyName, Phone: user.Phone, } } } items = append(items, item) } return &dto.ApiCallListResponse{ Items: items, Total: total, Page: options.Page, Size: options.PageSize, }, nil } // EncryptParams 加密参数 func (s *ApiApplicationServiceImpl) EncryptParams(ctx context.Context, userID string, cmd *commands.EncryptCommand) (string, error) { // 1. 将数据转换为JSON字节数组 jsonData, err := json.Marshal(cmd.Data) if err != nil { s.logger.Error("序列化参数失败", zap.Error(err)) return "", err } // 2. 使用前端传来的SecretKey进行加密 encryptedData, err := crypto.AesEncrypt(jsonData, cmd.SecretKey) if err != nil { s.logger.Error("加密参数失败", zap.Error(err)) return "", err } return encryptedData, nil } // DecryptParams 解密参数 func (s *ApiApplicationServiceImpl) DecryptParams(ctx context.Context, userID string, cmd *commands.DecryptCommand) (map[string]interface{}, error) { // 1. 使用前端传来的SecretKey进行解密 decryptedData, err := crypto.AesDecrypt(cmd.EncryptedData, cmd.SecretKey) if err != nil { s.logger.Error("解密参数失败", zap.Error(err)) return nil, err } // 2. 将解密后的JSON字节数组转换为map var result map[string]interface{} err = json.Unmarshal(decryptedData, &result) if err != nil { s.logger.Error("反序列化解密数据失败", zap.Error(err)) return nil, err } return result, nil }