187 lines
6.3 KiB
Go
187 lines
6.3 KiB
Go
|
|
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)
|
|||
|
|
}
|