package services import ( "context" "fmt" "github.com/shopspring/decimal" "go.uber.org/zap" "tyapi-server/internal/config" "tyapi-server/internal/domains/api/entities" api_repositories "tyapi-server/internal/domains/api/repositories" user_repositories "tyapi-server/internal/domains/user/repositories" "tyapi-server/internal/infrastructure/external/sms" ) // BalanceAlertService 余额预警服务接口 type BalanceAlertService interface { CheckAndSendAlert(ctx context.Context, userID string, balance decimal.Decimal) error } // BalanceAlertServiceImpl 余额预警服务实现 type BalanceAlertServiceImpl struct { apiUserRepo api_repositories.ApiUserRepository userRepo user_repositories.UserRepository enterpriseInfoRepo user_repositories.EnterpriseInfoRepository smsService *sms.AliSMSService config *config.Config logger *zap.Logger } // NewBalanceAlertService 创建余额预警服务 func NewBalanceAlertService( apiUserRepo api_repositories.ApiUserRepository, userRepo user_repositories.UserRepository, enterpriseInfoRepo user_repositories.EnterpriseInfoRepository, smsService *sms.AliSMSService, config *config.Config, logger *zap.Logger, ) BalanceAlertService { return &BalanceAlertServiceImpl{ apiUserRepo: apiUserRepo, userRepo: userRepo, enterpriseInfoRepo: enterpriseInfoRepo, smsService: smsService, config: config, logger: logger, } } // CheckAndSendAlert 检查余额并发送预警 func (s *BalanceAlertServiceImpl) CheckAndSendAlert(ctx context.Context, userID string, balance decimal.Decimal) error { // 1. 获取API用户信息 apiUser, err := s.apiUserRepo.FindByUserId(ctx, userID) if err != nil { s.logger.Error("获取API用户信息失败", zap.String("user_id", userID), zap.Error(err)) return fmt.Errorf("获取API用户信息失败: %w", err) } if apiUser == nil { s.logger.Debug("API用户不存在,跳过余额预警检查", zap.String("user_id", userID)) return nil } // 2. 兼容性处理:如果API用户没有配置预警信息,从用户表获取并更新 needUpdate := false if apiUser.AlertPhone == "" { user, err := s.userRepo.GetByID(ctx, userID) if err != nil { s.logger.Error("获取用户信息失败", zap.String("user_id", userID), zap.Error(err)) return fmt.Errorf("获取用户信息失败: %w", err) } if user.Phone != "" { apiUser.AlertPhone = user.Phone needUpdate = true } } // 3. 兼容性处理:如果API用户没有配置预警阈值,使用默认值 if apiUser.BalanceAlertThreshold == 0 { apiUser.BalanceAlertThreshold = s.config.Wallet.BalanceAlert.DefaultThreshold needUpdate = true } // 4. 如果需要更新API用户信息,保存到数据库 if needUpdate { if err := s.apiUserRepo.Update(ctx, apiUser); err != nil { s.logger.Error("更新API用户预警配置失败", zap.String("user_id", userID), zap.Error(err)) // 不返回错误,继续执行预警检查 } } balanceFloat, _ := balance.Float64() // 5. 检查是否需要发送欠费预警(不受冷却期限制) if apiUser.ShouldSendArrearsAlert(balanceFloat) { if err := s.sendArrearsAlert(ctx, apiUser, balanceFloat); err != nil { s.logger.Error("发送欠费预警失败", zap.String("user_id", userID), zap.Error(err)) return err } // 欠费预警不受冷却期限制,不需要更新LastArrearsAlert时间 return nil } // 6. 检查是否需要发送低余额预警 if apiUser.ShouldSendLowBalanceAlert(balanceFloat) { if err := s.sendLowBalanceAlert(ctx, apiUser, balanceFloat); err != nil { s.logger.Error("发送低余额预警失败", zap.String("user_id", userID), zap.Error(err)) return err } // 标记预警已发送 apiUser.MarkLowBalanceAlertSent() if err := s.apiUserRepo.Update(ctx, apiUser); err != nil { s.logger.Error("更新API用户预警时间失败", zap.String("user_id", userID), zap.Error(err)) } } return nil } // sendArrearsAlert 发送欠费预警 func (s *BalanceAlertServiceImpl) sendArrearsAlert(ctx context.Context, apiUser *entities.ApiUser, balance float64) error { // 直接从企业信息表获取企业名称 enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, apiUser.UserId) if err != nil { s.logger.Error("获取企业信息失败", zap.String("user_id", apiUser.UserId), zap.Error(err)) // 如果获取企业信息失败,使用默认名称 return s.smsService.SendBalanceAlert(ctx, apiUser.AlertPhone, balance, 0, "arrears", "天远数据用户") } // 获取企业名称,如果没有则使用默认名称 enterpriseName := "天远数据用户" if enterpriseInfo != nil && enterpriseInfo.CompanyName != "" { enterpriseName = enterpriseInfo.CompanyName } s.logger.Info("发送欠费预警短信", zap.String("user_id", apiUser.UserId), zap.String("phone", apiUser.AlertPhone), zap.Float64("balance", balance), zap.String("enterprise_name", enterpriseName)) return s.smsService.SendBalanceAlert(ctx, apiUser.AlertPhone, balance, 0, "arrears", enterpriseName) } // sendLowBalanceAlert 发送低余额预警 func (s *BalanceAlertServiceImpl) sendLowBalanceAlert(ctx context.Context, apiUser *entities.ApiUser, balance float64) error { // 直接从企业信息表获取企业名称 enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, apiUser.UserId) if err != nil { s.logger.Error("获取企业信息失败", zap.String("user_id", apiUser.UserId), zap.Error(err)) // 如果获取企业信息失败,使用默认名称 return s.smsService.SendBalanceAlert(ctx, apiUser.AlertPhone, balance, apiUser.BalanceAlertThreshold, "low_balance", "天远数据用户") } // 获取企业名称,如果没有则使用默认名称 enterpriseName := "天远数据用户" if enterpriseInfo != nil && enterpriseInfo.CompanyName != "" { enterpriseName = enterpriseInfo.CompanyName } s.logger.Info("发送低余额预警短信", zap.String("user_id", apiUser.UserId), zap.String("phone", apiUser.AlertPhone), zap.Float64("balance", balance), zap.Float64("threshold", apiUser.BalanceAlertThreshold), zap.String("enterprise_name", enterpriseName)) return s.smsService.SendBalanceAlert(ctx, apiUser.AlertPhone, balance, apiUser.BalanceAlertThreshold, "low_balance", enterpriseName) }