package services import ( "context" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "time" "github.com/shopspring/decimal" "go.uber.org/zap" "tyapi-server/internal/domains/finance/dto" "tyapi-server/internal/domains/finance/entities" "tyapi-server/internal/domains/finance/repositories" "tyapi-server/internal/shared/interfaces" ) // FinanceService 财务服务 type FinanceService struct { walletRepo repositories.WalletRepository userSecretsRepo repositories.UserSecretsRepository responseBuilder interfaces.ResponseBuilder logger *zap.Logger } // NewFinanceService 创建财务服务 func NewFinanceService( walletRepo repositories.WalletRepository, userSecretsRepo repositories.UserSecretsRepository, responseBuilder interfaces.ResponseBuilder, logger *zap.Logger, ) *FinanceService { return &FinanceService{ walletRepo: walletRepo, userSecretsRepo: userSecretsRepo, responseBuilder: responseBuilder, logger: logger, } } // CreateWallet 创建钱包 func (s *FinanceService) CreateWallet(ctx context.Context, req *dto.CreateWalletRequest) (*dto.CreateWalletResponse, error) { s.logger.Info("创建钱包", zap.String("user_id", req.UserID)) // 检查用户是否已有钱包 exists, err := s.walletRepo.ExistsByUserID(ctx, req.UserID) if err != nil { return nil, fmt.Errorf("检查钱包存在性失败: %w", err) } if exists { return nil, fmt.Errorf("用户已存在钱包") } // 创建钱包 wallet := entities.Wallet{ ID: s.generateID(), UserID: req.UserID, IsActive: true, Balance: decimal.Zero, } if err := s.walletRepo.Create(ctx, wallet); err != nil { return nil, fmt.Errorf("创建钱包失败: %w", err) } // 构建响应 walletInfo := dto.WalletInfo{ ID: wallet.ID, UserID: wallet.UserID, IsActive: wallet.IsActive, Balance: wallet.Balance, CreatedAt: wallet.CreatedAt, UpdatedAt: wallet.UpdatedAt, } s.logger.Info("钱包创建成功", zap.String("wallet_id", wallet.ID)) return &dto.CreateWalletResponse{Wallet: walletInfo}, nil } // GetWallet 获取钱包信息 func (s *FinanceService) GetWallet(ctx context.Context, userID string) (*dto.WalletInfo, error) { s.logger.Info("获取钱包信息", zap.String("user_id", userID)) wallet, err := s.walletRepo.FindByUserID(ctx, userID) if err != nil { return nil, fmt.Errorf("钱包不存在") } walletInfo := dto.WalletInfo{ ID: wallet.ID, UserID: wallet.UserID, IsActive: wallet.IsActive, Balance: wallet.Balance, CreatedAt: wallet.CreatedAt, UpdatedAt: wallet.UpdatedAt, } return &walletInfo, nil } // UpdateWallet 更新钱包 func (s *FinanceService) UpdateWallet(ctx context.Context, req *dto.UpdateWalletRequest) error { s.logger.Info("更新钱包", zap.String("user_id", req.UserID)) wallet, err := s.walletRepo.FindByUserID(ctx, req.UserID) if err != nil { return fmt.Errorf("钱包不存在") } // 更新字段 if !req.Balance.IsZero() { wallet.Balance = req.Balance } if req.IsActive != nil { wallet.IsActive = *req.IsActive } if err := s.walletRepo.Update(ctx, *wallet); err != nil { return fmt.Errorf("更新钱包失败: %w", err) } s.logger.Info("钱包更新成功", zap.String("user_id", req.UserID)) return nil } // Recharge 充值 func (s *FinanceService) Recharge(ctx context.Context, req *dto.RechargeRequest) (*dto.RechargeResponse, error) { s.logger.Info("钱包充值", zap.String("user_id", req.UserID), zap.String("amount", req.Amount.String())) // 验证金额 if req.Amount.LessThanOrEqual(decimal.Zero) { return nil, fmt.Errorf("充值金额必须大于0") } // 获取钱包 wallet, err := s.walletRepo.FindByUserID(ctx, req.UserID) if err != nil { return nil, fmt.Errorf("钱包不存在") } // 检查钱包状态 if !wallet.IsActive { return nil, fmt.Errorf("钱包已被禁用") } // 增加余额 if err := s.walletRepo.AddBalance(ctx, req.UserID, req.Amount); err != nil { return nil, fmt.Errorf("充值失败: %w", err) } // 获取更新后的余额 updatedWallet, err := s.walletRepo.FindByUserID(ctx, req.UserID) if err != nil { return nil, fmt.Errorf("获取更新后余额失败: %w", err) } s.logger.Info("充值成功", zap.String("user_id", req.UserID), zap.String("amount", req.Amount.String())) return &dto.RechargeResponse{ WalletID: updatedWallet.ID, Amount: req.Amount, Balance: updatedWallet.Balance, }, nil } // Withdraw 提现 func (s *FinanceService) Withdraw(ctx context.Context, req *dto.WithdrawRequest) (*dto.WithdrawResponse, error) { s.logger.Info("钱包提现", zap.String("user_id", req.UserID), zap.String("amount", req.Amount.String())) // 验证金额 if req.Amount.LessThanOrEqual(decimal.Zero) { return nil, fmt.Errorf("提现金额必须大于0") } // 获取钱包 wallet, err := s.walletRepo.FindByUserID(ctx, req.UserID) if err != nil { return nil, fmt.Errorf("钱包不存在") } // 检查钱包状态 if !wallet.IsActive { return nil, fmt.Errorf("钱包已被禁用") } // 检查余额是否足够 if wallet.Balance.LessThan(req.Amount) { return nil, fmt.Errorf("余额不足") } // 减少余额 if err := s.walletRepo.SubtractBalance(ctx, req.UserID, req.Amount); err != nil { return nil, fmt.Errorf("提现失败: %w", err) } // 获取更新后的余额 updatedWallet, err := s.walletRepo.FindByUserID(ctx, req.UserID) if err != nil { return nil, fmt.Errorf("获取更新后余额失败: %w", err) } s.logger.Info("提现成功", zap.String("user_id", req.UserID), zap.String("amount", req.Amount.String())) return &dto.WithdrawResponse{ WalletID: updatedWallet.ID, Amount: req.Amount, Balance: updatedWallet.Balance, }, nil } // CreateUserSecrets 创建用户密钥 func (s *FinanceService) CreateUserSecrets(ctx context.Context, req *dto.CreateUserSecretsRequest) (*dto.CreateUserSecretsResponse, error) { s.logger.Info("创建用户密钥", zap.String("user_id", req.UserID)) // 检查用户是否已有密钥 exists, err := s.userSecretsRepo.ExistsByUserID(ctx, req.UserID) if err != nil { return nil, fmt.Errorf("检查密钥存在性失败: %w", err) } if exists { return nil, fmt.Errorf("用户已存在密钥") } // 生成访问ID和密钥 accessID := s.generateAccessID() accessKey := s.generateAccessKey() // 创建密钥 secrets := entities.UserSecrets{ ID: s.generateID(), UserID: req.UserID, AccessID: accessID, AccessKey: accessKey, IsActive: true, ExpiresAt: req.ExpiresAt, } if err := s.userSecretsRepo.Create(ctx, secrets); err != nil { return nil, fmt.Errorf("创建密钥失败: %w", err) } // 构建响应 secretsInfo := dto.UserSecretsInfo{ ID: secrets.ID, UserID: secrets.UserID, AccessID: secrets.AccessID, AccessKey: secrets.AccessKey, IsActive: secrets.IsActive, LastUsedAt: secrets.LastUsedAt, ExpiresAt: secrets.ExpiresAt, CreatedAt: secrets.CreatedAt, UpdatedAt: secrets.UpdatedAt, } s.logger.Info("用户密钥创建成功", zap.String("user_id", req.UserID)) return &dto.CreateUserSecretsResponse{Secrets: secretsInfo}, nil } // GetUserSecrets 获取用户密钥 func (s *FinanceService) GetUserSecrets(ctx context.Context, userID string) (*dto.UserSecretsInfo, error) { s.logger.Info("获取用户密钥", zap.String("user_id", userID)) secrets, err := s.userSecretsRepo.FindByUserID(ctx, userID) if err != nil { return nil, fmt.Errorf("密钥不存在") } secretsInfo := dto.UserSecretsInfo{ ID: secrets.ID, UserID: secrets.UserID, AccessID: secrets.AccessID, AccessKey: secrets.AccessKey, IsActive: secrets.IsActive, LastUsedAt: secrets.LastUsedAt, ExpiresAt: secrets.ExpiresAt, CreatedAt: secrets.CreatedAt, UpdatedAt: secrets.UpdatedAt, } return &secretsInfo, nil } // RegenerateAccessKey 重新生成访问密钥 func (s *FinanceService) RegenerateAccessKey(ctx context.Context, req *dto.RegenerateAccessKeyRequest) (*dto.RegenerateAccessKeyResponse, error) { s.logger.Info("重新生成访问密钥", zap.String("user_id", req.UserID)) // 检查密钥是否存在 secrets, err := s.userSecretsRepo.FindByUserID(ctx, req.UserID) if err != nil { return nil, fmt.Errorf("密钥不存在") } // 生成新的访问ID和密钥 newAccessID := s.generateAccessID() newAccessKey := s.generateAccessKey() // 更新密钥 if err := s.userSecretsRepo.RegenerateAccessKey(ctx, req.UserID, newAccessID, newAccessKey); err != nil { return nil, fmt.Errorf("重新生成密钥失败: %w", err) } // 更新过期时间 if req.ExpiresAt != nil { secrets.ExpiresAt = req.ExpiresAt if err := s.userSecretsRepo.Update(ctx, *secrets); err != nil { s.logger.Error("更新密钥过期时间失败", zap.Error(err)) } } s.logger.Info("访问密钥重新生成成功", zap.String("user_id", req.UserID)) return &dto.RegenerateAccessKeyResponse{ AccessID: newAccessID, AccessKey: newAccessKey, }, nil } // DeactivateUserSecrets 停用用户密钥 func (s *FinanceService) DeactivateUserSecrets(ctx context.Context, req *dto.DeactivateUserSecretsRequest) error { s.logger.Info("停用用户密钥", zap.String("user_id", req.UserID)) // 检查密钥是否存在 if _, err := s.userSecretsRepo.FindByUserID(ctx, req.UserID); err != nil { return fmt.Errorf("密钥不存在") } // 停用密钥 if err := s.userSecretsRepo.DeactivateByUserID(ctx, req.UserID); err != nil { return fmt.Errorf("停用密钥失败: %w", err) } s.logger.Info("用户密钥停用成功", zap.String("user_id", req.UserID)) return nil } // WalletTransaction 钱包交易 func (s *FinanceService) WalletTransaction(ctx context.Context, req *dto.WalletTransactionRequest) (*dto.WalletTransactionResponse, error) { s.logger.Info("钱包交易", zap.String("from_user_id", req.FromUserID), zap.String("to_user_id", req.ToUserID), zap.String("amount", req.Amount.String())) // 验证金额 if req.Amount.LessThanOrEqual(decimal.Zero) { return nil, fmt.Errorf("交易金额必须大于0") } // 验证用户不能给自己转账 if req.FromUserID == req.ToUserID { return nil, fmt.Errorf("不能给自己转账") } // 获取转出钱包 fromWallet, err := s.walletRepo.FindByUserID(ctx, req.FromUserID) if err != nil { return nil, fmt.Errorf("转出钱包不存在") } // 获取转入钱包 toWallet, err := s.walletRepo.FindByUserID(ctx, req.ToUserID) if err != nil { return nil, fmt.Errorf("转入钱包不存在") } // 检查钱包状态 if !fromWallet.IsActive { return nil, fmt.Errorf("转出钱包已被禁用") } if !toWallet.IsActive { return nil, fmt.Errorf("转入钱包已被禁用") } // 检查余额是否足够 if fromWallet.Balance.LessThan(req.Amount) { return nil, fmt.Errorf("余额不足") } // 执行交易(使用事务) // 这里简化处理,实际应该使用数据库事务 if err := s.walletRepo.SubtractBalance(ctx, req.FromUserID, req.Amount); err != nil { return nil, fmt.Errorf("扣款失败: %w", err) } if err := s.walletRepo.AddBalance(ctx, req.ToUserID, req.Amount); err != nil { return nil, fmt.Errorf("入账失败: %w", err) } // 获取更新后的余额 updatedFromWallet, err := s.walletRepo.FindByUserID(ctx, req.FromUserID) if err != nil { return nil, fmt.Errorf("获取转出后余额失败: %w", err) } updatedToWallet, err := s.walletRepo.FindByUserID(ctx, req.ToUserID) if err != nil { return nil, fmt.Errorf("获取转入后余额失败: %w", err) } s.logger.Info("钱包交易成功", zap.String("from_user_id", req.FromUserID), zap.String("to_user_id", req.ToUserID), zap.String("amount", req.Amount.String())) return &dto.WalletTransactionResponse{ TransactionID: s.generateID(), FromUserID: req.FromUserID, ToUserID: req.ToUserID, Amount: req.Amount, FromBalance: updatedFromWallet.Balance, ToBalance: updatedToWallet.Balance, Notes: req.Notes, CreatedAt: time.Now(), }, nil } // GetWalletStats 获取钱包统计 func (s *FinanceService) GetWalletStats(ctx context.Context) (*dto.WalletStatsResponse, error) { s.logger.Info("获取钱包统计") // 获取总钱包数 totalWallets, err := s.walletRepo.Count(ctx, interfaces.CountOptions{}) if err != nil { return nil, fmt.Errorf("获取总钱包数失败: %w", err) } // 获取激活钱包数 activeWallets, err := s.walletRepo.GetActiveWalletCount(ctx) if err != nil { return nil, fmt.Errorf("获取激活钱包数失败: %w", err) } // 获取总余额 totalBalance, err := s.walletRepo.GetTotalBalance(ctx) if err != nil { return nil, fmt.Errorf("获取总余额失败: %w", err) } // 这里简化处理,实际应该查询交易记录表 todayTransactions := int64(0) todayVolume := decimal.Zero return &dto.WalletStatsResponse{ TotalWallets: totalWallets, ActiveWallets: activeWallets, TotalBalance: totalBalance.(decimal.Decimal), TodayTransactions: todayTransactions, TodayVolume: todayVolume, }, nil } // generateID 生成ID func (s *FinanceService) generateID() string { bytes := make([]byte, 16) rand.Read(bytes) return hex.EncodeToString(bytes) } // generateAccessID 生成访问ID func (s *FinanceService) generateAccessID() string { bytes := make([]byte, 20) rand.Read(bytes) return hex.EncodeToString(bytes) } // generateAccessKey 生成访问密钥 func (s *FinanceService) generateAccessKey() string { bytes := make([]byte, 32) rand.Read(bytes) hash := sha256.Sum256(bytes) return hex.EncodeToString(hash[:]) }