package svc import ( "context" "errors" "fmt" ea "github.com/earn-alliance/earnalliance-go" "github.com/shopspring/decimal" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/rest" "nova_task/internal/config" "nova_task/internal/consts" "nova_task/internal/middleware" "nova_task/internal/model" "nova_task/internal/pkg/errs" "nova_task/internal/types" "time" ) type ServiceContext struct { Config config.Config *dbModel ApiKeyCheck rest.Middleware AdminSecretCheck rest.Middleware Game7ApiKeyCheck rest.Middleware KGeNApiKeyCheck rest.Middleware Earn *ea.Client DBConn sqlx.SqlConn Redis *redis.Redis } func NewServiceContext(c config.Config) *ServiceContext { dbConn := c.MySql.Conn() gameConn := c.GameDB.Conn() configModel := model.NewNhSystemConfigModel(dbConn, c.Cache) return &ServiceContext{ Config: c, dbModel: newDBModel(dbConn, gameConn, configModel), ApiKeyCheck: middleware.NewApiKeyCheckMiddleware(configModel).Handle, AdminSecretCheck: middleware.NewAdminSecretCheckMiddleware(configModel).Handle, Game7ApiKeyCheck: middleware.NewGame7ApiKeyCheckMiddleware(configModel).Handle, KGeNApiKeyCheck: middleware.NewKGeNApiKeyCheckMiddleware(configModel).Handle, DBConn: dbConn, Earn: c.Earn.BuildEarnClient(), Redis: redis.MustNewRedis(c.Redis), } } func (s *ServiceContext) Close() { err := s.Earn.Flush() if err != nil { logx.Errorw("flush earn error", logx.Field("err", err)) } s.Earn.Close() } func (s *ServiceContext) IsAmbassador(ctx context.Context, uid uint, stage int8) bool { u, err := s.UserModel.FindOne(ctx, uid) if err != nil { if !errors.Is(err, model.ErrNotFound) { logx.Errorw("find user error", logx.Field("err", err), logx.Field("uid", uid), logx.Field("stage", stage)) } return false } _, err = s.AmbassadorModel.FindOneByEmailStage(ctx, u.Email, stage) if err != nil { if !errors.Is(err, model.ErrNotFound) { logx.Errorw("find ambassador error", logx.Field("err", err), logx.Field("uid", uid), logx.Field("stage", stage)) } return false } return true } func (s *ServiceContext) HasBindTwitter(ctx context.Context, uid uint) bool { tw, err := s.TwitterModel.FindOneByUid(ctx, uid) if err != nil { if !errors.Is(err, model.ErrNotFound) { logx.Errorw("find twitter error", logx.Field("err", err), logx.Field("uid", uid)) } return false } return tw.TwitterId != "" } func (s *ServiceContext) GetUidByEmail(ctx context.Context, email string) (uint, *types.CarvResult) { u, err := s.UserModel.FindOneByEmail(ctx, email) if err != nil { if !errors.Is(err, model.ErrNotFound) { return 0, &types.CarvResult{ Error: &types.Error{ Code: int(errs.ErrDatabaseOperate), Message: "system error", }, } } return 0, &types.CarvResult{ Error: &types.Error{ Code: int(errs.ErrUserNotFound), Message: "email not exist", }, } } return u.Id, nil } func (s *ServiceContext) AddUserAssetWithSession(ctx context.Context, session sqlx.Session, uid uint, role int64, asset consts.AssetType, assetId string, amount decimal.Decimal, remark string, eventId uint64, provideUid uint, referralReward bool) error { var assetModel model.NhTaskAssetModel var recordModel model.NhTaskAssetRecordModel var gamesPropertyModel model.NhGamesPropertyLogsModel if session != nil { assetModel = s.taskAssetModel.WithSession(session) recordModel = s.taskAssetRecordModel.WithSession(session) gamesPropertyModel = s.GamesPropertyModel.WithSession(session) } else { assetModel = s.taskAssetModel recordModel = s.taskAssetRecordModel gamesPropertyModel = s.GamesPropertyModel } if amount.LessThan(decimal.Zero) { uAsset, err := assetModel.FindOneByUid(ctx, int(uid)) if err != nil { if errors.Is(err, model.ErrNotFound) { return errs.New(errs.ErrInsufficientPoints, "insufficient point") } return errs.New(errs.ErrDatabaseOperate, err) } switch asset { case consts.AssetType_Points: if uAsset.Points.LessThan(amount.Abs()) { return errs.New(errs.ErrInsufficientPoints, "insufficient point") } case consts.AssetType_Castile: if uAsset.Castile.LessThan(amount.Abs()) { return errs.New(errs.ErrInsufficientCastile, "insufficient castile") } case consts.AssetType_Elite_Points: if uAsset.ElitePoints.LessThan(amount.Abs()) { return errs.New(errs.ErrInsufficientElitePoints, "insufficient elite points") } case consts.AssetType_Keys: if uAsset.Keys < int(amount.IntPart()) { return errs.New(errs.ErrInsufficientKeys, "insufficient keys") } default: return errors.New("unknown asset type") } } var err error switch asset { case consts.AssetType_Points: err = assetModel.AddPoint(ctx, uid, amount) case consts.AssetType_Castile: err = assetModel.AddCastile(ctx, uid, amount) case consts.AssetType_Elite_Points: err = assetModel.AddElitePoints(ctx, uid, amount) case consts.AssetType_Keys: err = assetModel.AddKeys(ctx, uid, int(amount.IntPart())) case consts.AssetType_Property: _, err = gamesPropertyModel.Insert(ctx, &model.NhGamesPropertyLogs{ Uid: uid, RoleId: role, PropertyId: assetId, PropertyNum: uint(amount.IntPart()), }) return err default: return errors.New("unknown asset type") } if err != nil { return err } _, err = recordModel.Insert(ctx, &model.NhTaskAssetRecord{ Uid: int(uid), EventId: eventId, AssetField: string(asset), Count: amount.InexactFloat64(), Remark: remark, ProvideUid: provideUid, CreateTime: int(time.Now().Unix()), }) if err != nil { return err } if !referralReward { return nil } // 返利给上级 pb, err := s.PromoteBindModel.FindOneByInvitedUid(ctx, uid) if err != nil { if !errors.Is(err, model.ErrNotFound) { logx.Errorw("find promote bind error", logx.Field("err", err), logx.Field("uid", uid)) } return nil } if pb.ShareUid == 0 { return nil } var rate = 5 if s.NftHolderModel.HoldNft(ctx, pb.ShareUid) { rate = 15 } else if s.AmbassadorModel.IsAmbassador(ctx, pb.ShareUid) { rate = 10 } rated := decimal.NewFromFloat(float64(rate) / 100) rewardAmount := amount.Mul(rated) return s.AddUserAssetWithSession(ctx, session, pb.ShareUid, 0, asset, assetId, rewardAmount, fmt.Sprintf("分享返利,比例%s", rated.String()), 7, uid, false) } // AddUserAsset 增加用户资产 func (s *ServiceContext) AddUserAsset(ctx context.Context, uid uint, role int64, asset consts.AssetType, assetId string, amount decimal.Decimal, remark string, eventId uint64, provideUid uint, referralReward bool) error { return s.AddUserAssetWithSession(ctx, nil, uid, role, asset, assetId, amount, remark, eventId, provideUid, referralReward) }