增加软质押手动结算测试接口

This commit is contained in:
lianghuanjie
2025-01-08 11:41:11 +08:00
parent 9aae8bd4dd
commit e7a802efdb
24 changed files with 634 additions and 330 deletions

View File

@@ -7,4 +7,5 @@ const (
NftStakeTaskConf = "nft_stake_task_conf"
CarvApiKey = "carv_api_key"
AdminSecret = "admin_secret"
NftHolderApiConf = "nft_holder_api_conf"
)

View File

@@ -0,0 +1,22 @@
package admin
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"nova_task/internal/logic/admin"
"nova_task/internal/svc"
)
// NFT持有者更新
func NftHolderUpdateHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := admin.NewNftHolderUpdateLogic(r.Context(), svcCtx)
err := l.NftHolderUpdate()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -0,0 +1,22 @@
package admin
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"nova_task/internal/logic/admin"
"nova_task/internal/svc"
)
// 软质压手动结算
func StakeSettleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := admin.NewStakeSettleLogic(r.Context(), svcCtx)
err := l.StakeSettle()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@@ -31,6 +31,18 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/email_reward",
Handler: admin.SendEmailRewardHandler(serverCtx),
},
{
// NFT持有者更新
Method: http.MethodGet,
Path: "/nft_holder_update",
Handler: admin.NftHolderUpdateHandler(serverCtx),
},
{
// 软质压手动结算
Method: http.MethodGet,
Path: "/stake_settle",
Handler: admin.StakeSettleHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/gapi/admin"),

View File

@@ -2,11 +2,11 @@ package earn
import (
"context"
ea "github.com/earn-alliance/earnalliance-go"
"github.com/robfig/cron/v3"
"github.com/spf13/cast"
"github.com/zeromicro/go-zero/core/logx"
"nova_task/internal/consts"
"nova_task/internal/logic/earn"
"nova_task/internal/svc"
)
@@ -35,56 +35,8 @@ func (c *Cron) Run() {
logx.Errorw("find earn alliance inviter id failed", logx.Field("err", err))
return
}
c.pushUserInfo(cast.ToUint(conf.Value))
c.pushUserBind(cast.ToUint(conf.Value))
}
func (c *Cron) pushUserInfo(shareId uint) {
us, err := c.svcCtx.PromoteBindModel.FindRequirePushUser(c.ctx, shareId)
if err != nil {
logx.Errorw("find require push user failed", logx.Field("err", err), logx.Field("share_id", shareId))
return
}
logx.Debugw("find require push user", logx.Field("count", len(us)))
for _, u := range us {
ui, err := c.svcCtx.UserModel.FindOne(c.ctx, u.InvitedUid)
if err != nil {
logx.Errorw("find user failed", logx.Field("err", err), logx.Field("uid", u.InvitedUid))
continue
}
var twitterId string
ut, err := c.svcCtx.TwitterModel.FindOne(c.ctx, u.InvitedUid)
if err == nil {
twitterId = ut.TwitterId
}
err = c.svcCtx.PromoteBindModel.UpdatePushUser(c.ctx, u.Id)
if err != nil {
logx.Errorw("update push user failed", logx.Field("err", err), logx.Field("uid", u.InvitedUid), logx.Field("twitter_id", twitterId), logx.Field("email", ui.Email))
} else {
c.svcCtx.Earn.SetIdentifiers(cast.ToString(ui.Id), &ea.Identifiers{
Email: ea.IdentifierFrom(ui.Email),
TwitterId: ea.IdentifierFrom(twitterId),
})
logx.Infow("push user info success", logx.Field("uid", u.InvitedUid), logx.Field("twitter_id", twitterId), logx.Field("email", ui.Email))
}
}
}
func (c *Cron) pushUserBind(shareId uint) {
us, err := c.svcCtx.TouristBindModel.FindRequirePushUser(c.ctx, shareId)
if err != nil {
logx.Errorw("find require push bind role user failed", logx.Field("err", err), logx.Field("share_id", shareId))
return
}
logx.Debugw("find require push bind role user", logx.Field("count", len(us)))
for _, u := range us {
err = c.svcCtx.PromoteBindModel.UpdatePushRole(c.ctx, u.Id)
if err != nil {
logx.Errorw("update push user failed", logx.Field("err", err), logx.Field("uid", u.Uid))
} else {
c.svcCtx.Earn.Track(cast.ToString(u.Uid), "BIND_ROLE", nil, nil)
logx.Infow("push user info success", logx.Field("uid", u.Uid))
}
}
shareId := cast.ToUint(conf.Value)
lg := earn.NewDataReportLogic(c.ctx, c.svcCtx)
lg.PushUserInfo(shareId)
lg.PushUserBind(shareId)
}

View File

@@ -2,13 +2,10 @@ package holder
import (
"context"
"errors"
"github.com/robfig/cron/v3"
"github.com/spf13/cast"
"github.com/zeromicro/go-zero/core/logx"
"nova_task/internal/model"
"nova_task/internal/logic/nft"
"nova_task/internal/svc"
"time"
)
type Cron struct {
@@ -33,93 +30,6 @@ func (c *Cron) Spec() string {
func (c *Cron) Run() {
logx.Debugw("run Cron cron task")
ols, err := GetOwnerList()
if err != nil {
logx.Errorw("get owner list error", logx.Field("error", err))
return
}
updateSeq := int(time.Now().Unix())
for _, o := range ols.Owners {
for _, tk := range o.TokenBalances {
balance := cast.ToInt(tk.Balance)
logx.Debugw("owner token", logx.Field("address", o.OwnerAddress), logx.Field("token", tk.TokenID), logx.Field("balance", tk.Balance))
nft, err := c.svcCtx.NftHolderModel.FindOneByTokenId(c.ctx, tk.TokenID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
nft = &model.NhNftHolder{
Address: o.OwnerAddress,
TokenId: tk.TokenID,
Balance: balance,
UpdateSeq: updateSeq,
}
} else {
logx.Errorw("find nft holder error", logx.Field("error", err), logx.Field("address", o.OwnerAddress), logx.Field("token", tk.TokenID))
continue
}
}
var value int
if nft.Id == 0 {
// 新增
_, err = c.svcCtx.NftHolderModel.Insert(c.ctx, nft)
value = balance
} else {
// 持有数量变化
value = balance - nft.Balance
if value != 0 {
nft.Balance = balance
nft.UpdateSeq = updateSeq
err = c.svcCtx.NftHolderModel.Update(c.ctx, nft)
}
}
if err != nil {
logx.Errorw("insert or update nft holder error", logx.Field("error", err), logx.Field("address", o.OwnerAddress), logx.Field("token", tk.TokenID), logx.Field("balance", balance))
}
// 余额有变化,记录变化日志
if value != 0 {
_, err = c.svcCtx.NftHolderChangeLogModel.Insert(c.ctx, &model.NhNftHolderChangeLog{
Address: o.OwnerAddress,
TokenId: tk.TokenID,
Value: balance,
Balance: balance,
})
if err != nil {
logx.Errorw("insert nft holder change log error", logx.Field("error", err), logx.Field("address", o.OwnerAddress), logx.Field("token", tk.TokenID))
}
}
}
}
// 删除已经不持有的地址,且添加变化日志
nfts, err := c.svcCtx.NftHolderModel.FindOtherUpdateSeq(c.ctx, updateSeq)
if err != nil {
logx.Errorw("find other update seq error", logx.Field("error", err))
return
}
for _, nft := range nfts {
_, err = c.svcCtx.NftHolderChangeLogModel.Insert(c.ctx, &model.NhNftHolderChangeLog{
Address: nft.Address,
TokenId: nft.TokenId,
Value: -nft.Balance,
Balance: 0,
})
if err != nil {
logx.Errorw("delete nft holder error", logx.Field("error", err), logx.Field("address", nft.Address), logx.Field("token", nft.TokenId))
}
uid, err := c.svcCtx.WalletModel.FindUidByAddress(c.ctx, nft.Address)
if err != nil {
logx.Errorw("find uid by address error", logx.Field("error", err), logx.Field("address", nft.Address))
continue
}
err = c.svcCtx.StakeNftModel.UnStakeNft(c.ctx, uid, nft.TokenId, true)
if err != nil {
logx.Errorw("un stake nft error", logx.Field("error", err), logx.Field("address", nft.Address), logx.Field("token", nft.TokenId))
}
}
err = c.svcCtx.NftHolderModel.DeleteOtherUpdateSeq(c.ctx, updateSeq)
if err != nil {
logx.Errorw("delete other update seq error", logx.Field("error", err), logx.Field("update_seq", updateSeq))
}
lg := nft.NewHolderUpdateLogic(c.ctx, c.svcCtx)
lg.HolderUpdate()
}

View File

@@ -1,31 +0,0 @@
package holder
import (
"encoding/json"
"fmt"
"net/http"
)
type OwnerList struct {
Owners []struct {
OwnerAddress string `json:"ownerAddress"`
TokenBalances []struct {
TokenID string `json:"tokenId"`
Balance string `json:"balance"`
} `json:"tokenBalances"`
} `json:"owners"`
PageKey string `json:"pageKey"`
}
func GetOwnerList() (*OwnerList, error) {
contractAddress := "0x89B8D549feA2eBd2aA0b375ce0DCaBba79e7e636"
url := fmt.Sprintf("https://eth-mainnet.g.alchemy.com/nft/v3/alcht_1a183fAsPqF9upfTfp3AC1l0iedGLo/getOwnersForContract?contractAddress=%s&withTokenBalances=true", contractAddress)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
result := new(OwnerList)
err = json.NewDecoder(resp.Body).Decode(result)
return result, err
}

View File

@@ -3,13 +3,9 @@ package stake_settle
import (
"context"
"github.com/robfig/cron/v3"
"github.com/shopspring/decimal"
"github.com/spf13/cast"
"github.com/zeromicro/go-zero/core/logx"
"nova_task/internal/model"
"nova_task/internal/pkg/utils"
"nova_task/internal/logic/nft"
"nova_task/internal/svc"
"time"
)
type Cron struct {
@@ -30,91 +26,6 @@ func (c *Cron) Spec() string {
func (c *Cron) Run() {
logx.Debugw("run settle cron task")
start, end, err := c.svcCtx.ConfigModel.GetNftStakeTaskOpenDate(c.ctx)
if err != nil {
logx.Errorw("get nft stake task open date failed", logx.Field("err", err))
return
}
end = end.AddDate(0, 0, 1).Add(-time.Second)
now := time.Now()
if now.Before(start) || now.After(end) {
logx.Debugw("now is not in the date range", logx.Field("now", now), logx.Field("start", start), logx.Field("end", end))
return
}
taskConf, err := c.svcCtx.ConfigModel.GetNftStakeTaskConf(c.ctx)
if err != nil {
logx.Errorw("get nft stake task conf failed", logx.Field("err", err))
return
}
stakes, err := c.svcCtx.StakeNftModel.AllStakeNft(c.ctx)
if err != nil {
logx.Errorw("get all stake nft failed", logx.Field("err", err))
return
}
uid2tokens := map[uint]float64{}
for _, s := range stakes {
if s.State != 1 {
continue
}
nftHolder, err := c.svcCtx.NftHolderModel.FindOneByTokenId(c.ctx, s.TokenId)
if err != nil {
logx.Errorw("find nft holder failed", logx.Field("err", err), logx.Field("tokenId", s.TokenId))
continue
}
uid, err := c.svcCtx.WalletModel.FindUidByAddress(c.ctx, nftHolder.Address)
if err != nil {
logx.Errorw("find uid by address failed", logx.Field("err", err), logx.Field("address", nftHolder.Address))
continue
}
if uid != s.Uid {
logx.Errorw("uid not match", logx.Field("uid", s.Uid), logx.Field("nftHolderUid", uid))
continue
}
if utils.IsBigTarot(s.TokenId) {
uid2tokens[s.Uid] += float64(taskConf.GreatTarot)
} else {
uid2tokens[s.Uid] += float64(taskConf.LittleTarot)
}
}
awardSeq := cast.ToInt(time.Now().AddDate(0, 0, -1).Format("20060102"))
for uid, tokens := range uid2tokens {
coefficient, err := c.svcCtx.StakeRewardModel.GetRandomCoefficientByUid(c.ctx, uid, awardSeq, taskConf.MinCoefficient, taskConf.MaxCoefficient)
if err != nil {
logx.Errorw("get random coefficient failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("awardSeq", awardSeq))
continue
}
tokensDecimal := decimal.NewFromFloat(tokens)
reward := tokensDecimal.Mul(decimal.NewFromFloat(coefficient))
err = c.svcCtx.StakeRewardModel.SetReward(c.ctx, uid, awardSeq, taskConf.OccupyPercent, tokensDecimal, reward)
if err != nil {
logx.Errorw("set reward failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("awardSeq", awardSeq), logx.Field("coefficient", coefficient), logx.Field("tokens", tokens), logx.Field("reward", reward))
continue
}
// 加资产
err = c.svcCtx.TaskAssetModel.AddCastile(c.ctx, uid, reward)
if err != nil {
logx.Errorw("add castile failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("awardSeq", awardSeq), logx.Field("reward", reward))
continue
}
// 加资产记录表
_, err = c.svcCtx.TaskAssetRecordModel.Insert(c.ctx, &model.NhTaskAssetRecord{
Uid: int(uid),
EventId: 0,
AssetField: "castile",
Count: reward.InexactFloat64(),
Remark: "nft软质押奖励",
ProvideUid: 0,
CreateTime: int(now.Unix()),
})
if err != nil {
logx.Errorw("insert task asset record failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("awardSeq", awardSeq), logx.Field("reward", reward))
}
}
lg := nft.NewStakeSettleLogic(c.ctx, c.svcCtx)
lg.StakeSettle()
}

View File

@@ -0,0 +1,31 @@
package admin
import (
"context"
"nova_task/internal/logic/nft"
"nova_task/internal/pkg/errs"
"github.com/zeromicro/go-zero/core/logx"
"nova_task/internal/svc"
)
type NftHolderUpdateLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// NFT持有者更新
func NewNftHolderUpdateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NftHolderUpdateLogic {
return &NftHolderUpdateLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *NftHolderUpdateLogic) NftHolderUpdate() error {
lg := nft.NewHolderUpdateLogic(l.ctx, l.svcCtx)
lg.HolderUpdate()
return errs.Success()
}

View File

@@ -0,0 +1,30 @@
package admin
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"nova_task/internal/logic/nft"
"nova_task/internal/pkg/errs"
"nova_task/internal/svc"
)
type StakeSettleLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 软质压手动结算
func NewStakeSettleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StakeSettleLogic {
return &StakeSettleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *StakeSettleLogic) StakeSettle() error {
lg := nft.NewStakeSettleLogic(l.ctx, l.svcCtx)
lg.StakeSettle()
return errs.Success()
}

View File

@@ -2,6 +2,9 @@ package carv
import (
"context"
"errors"
"nova_task/internal/model"
"nova_task/internal/pkg/errs"
"nova_task/internal/svc"
"nova_task/internal/types"
@@ -24,8 +27,40 @@ func NewBindWalletLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BindWa
}
}
func (l *BindWalletLogic) BindWallet(req *types.EmailKey) (resp *types.CarvResult, err error) {
l.svcCtx.UserModel.FindOneByEmail(l.ctx, req.Email)
func (l *BindWalletLogic) BindWallet(req *types.EmailKey) (*types.CarvResult, error) {
u, err := l.svcCtx.UserModel.FindOneByEmail(l.ctx, req.Email)
if err != nil {
if !errors.Is(err, model.ErrNotFound) {
return &types.CarvResult{
Error: &types.Error{
Code: int(errs.ErrDatabaseOperate),
Message: "system error",
},
}, nil
}
return &types.CarvResult{
Error: &types.Error{
Code: int(errs.ErrUserNotFound),
Message: "email not exist",
},
}, nil
}
return
_, err = l.svcCtx.WalletModel.FindAddressByUid(l.ctx, u.Id)
if err != nil {
if !errors.Is(err, model.ErrNotFound) {
return &types.CarvResult{
Error: &types.Error{
Code: int(errs.ErrDatabaseOperate),
Message: "system error",
},
}, nil
}
return &types.CarvResult{
Result: &types.Result{IsValid: false},
}, nil
}
return &types.CarvResult{
Result: &types.Result{IsValid: true},
}, nil
}

View File

@@ -0,0 +1,71 @@
package earn
import (
"context"
ea "github.com/earn-alliance/earnalliance-go"
"github.com/spf13/cast"
"github.com/zeromicro/go-zero/core/logx"
"nova_task/internal/svc"
)
type DataReportLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDataReportLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DataReportLogic {
return &DataReportLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DataReportLogic) PushUserInfo(shareId uint) {
us, err := l.svcCtx.PromoteBindModel.FindRequirePushUser(l.ctx, shareId)
if err != nil {
logx.Errorw("find require push user failed", logx.Field("err", err), logx.Field("share_id", shareId))
return
}
logx.Debugw("find require push user", logx.Field("count", len(us)))
for _, u := range us {
ui, err := l.svcCtx.UserModel.FindOne(l.ctx, u.InvitedUid)
if err != nil {
logx.Errorw("find user failed", logx.Field("err", err), logx.Field("uid", u.InvitedUid))
continue
}
var twitterId string
ut, err := l.svcCtx.TwitterModel.FindOne(l.ctx, u.InvitedUid)
if err == nil {
twitterId = ut.TwitterId
}
err = l.svcCtx.PromoteBindModel.UpdatePushUser(l.ctx, u.Id)
if err != nil {
logx.Errorw("update push user failed", logx.Field("err", err), logx.Field("uid", u.InvitedUid), logx.Field("twitter_id", twitterId), logx.Field("email", ui.Email))
} else {
l.svcCtx.Earn.SetIdentifiers(cast.ToString(ui.Id), &ea.Identifiers{
Email: ea.IdentifierFrom(ui.Email),
TwitterId: ea.IdentifierFrom(twitterId),
})
logx.Infow("push user info success", logx.Field("uid", u.InvitedUid), logx.Field("twitter_id", twitterId), logx.Field("email", ui.Email))
}
}
}
func (l *DataReportLogic) PushUserBind(shareId uint) {
us, err := l.svcCtx.TouristBindModel.FindRequirePushUser(l.ctx, shareId)
if err != nil {
logx.Errorw("find require push bind role user failed", logx.Field("err", err), logx.Field("share_id", shareId))
return
}
logx.Debugw("find require push bind role user", logx.Field("count", len(us)))
for _, u := range us {
err = l.svcCtx.PromoteBindModel.UpdatePushRole(l.ctx, u.Id)
if err != nil {
logx.Errorw("update push user failed", logx.Field("err", err), logx.Field("uid", u.Uid))
} else {
l.svcCtx.Earn.Track(cast.ToString(u.Uid), "BIND_ROLE", nil, nil)
logx.Infow("push user info success", logx.Field("uid", u.Uid))
}
}
}

View File

@@ -0,0 +1,158 @@
package nft
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/spf13/cast"
"github.com/zeromicro/go-zero/core/logx"
"net/http"
"nova_task/internal/consts"
"nova_task/internal/model"
"nova_task/internal/svc"
"time"
)
type HolderUpdateLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewHolderUpdateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HolderUpdateLogic {
return &HolderUpdateLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *HolderUpdateLogic) HolderUpdate() {
conf := struct {
Network string `json:"network"`
Contract string `json:"contract"`
}{}
cf, err := l.svcCtx.ConfigModel.FindOneByName(l.ctx, consts.NftHolderApiConf)
if err != nil {
logx.Errorw("find nft holder api conf error", logx.Field("error", err))
return
}
err = json.Unmarshal([]byte(cf.Value), &conf)
if err != nil {
logx.Errorw("unmarshal nft holder api conf error", logx.Field("error", err), logx.Field("value", cf.Value))
return
}
ols, err := l.GetOwnerList(conf.Network, conf.Contract)
if err != nil {
logx.Errorw("get owner list error", logx.Field("error", err))
return
}
updateSeq := int(time.Now().Unix())
for _, o := range ols.Owners {
for _, tk := range o.TokenBalances {
balance := cast.ToInt(tk.Balance)
logx.Debugw("owner token", logx.Field("address", o.OwnerAddress), logx.Field("token", tk.TokenID), logx.Field("balance", tk.Balance))
nft, err := l.svcCtx.NftHolderModel.FindOneByTokenId(l.ctx, tk.TokenID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
nft = &model.NhNftHolder{
Address: o.OwnerAddress,
TokenId: tk.TokenID,
Balance: balance,
UpdateSeq: updateSeq,
}
} else {
logx.Errorw("find nft holder error", logx.Field("error", err), logx.Field("address", o.OwnerAddress), logx.Field("token", tk.TokenID))
continue
}
}
var value int
if nft.Id == 0 {
// 新增
_, err = l.svcCtx.NftHolderModel.Insert(l.ctx, nft)
value = balance
} else {
// 持有数量变化
value = balance - nft.Balance
if value != 0 {
nft.Balance = balance
nft.UpdateSeq = updateSeq
err = l.svcCtx.NftHolderModel.Update(l.ctx, nft)
}
}
if err != nil {
logx.Errorw("insert or update nft holder error", logx.Field("error", err), logx.Field("address", o.OwnerAddress), logx.Field("token", tk.TokenID), logx.Field("balance", balance))
}
// 余额有变化,记录变化日志
if value != 0 {
_, err = l.svcCtx.NftHolderChangeLogModel.Insert(l.ctx, &model.NhNftHolderChangeLog{
Address: o.OwnerAddress,
TokenId: tk.TokenID,
Value: balance,
Balance: balance,
})
if err != nil {
logx.Errorw("insert nft holder change log error", logx.Field("error", err), logx.Field("address", o.OwnerAddress), logx.Field("token", tk.TokenID))
}
}
}
}
// 删除已经不持有的地址,且添加变化日志
nfts, err := l.svcCtx.NftHolderModel.FindOtherUpdateSeq(l.ctx, updateSeq)
if err != nil {
logx.Errorw("find other update seq error", logx.Field("error", err))
return
}
for _, nft := range nfts {
_, err = l.svcCtx.NftHolderChangeLogModel.Insert(l.ctx, &model.NhNftHolderChangeLog{
Address: nft.Address,
TokenId: nft.TokenId,
Value: -nft.Balance,
Balance: 0,
})
if err != nil {
logx.Errorw("delete nft holder error", logx.Field("error", err), logx.Field("address", nft.Address), logx.Field("token", nft.TokenId))
}
uid, err := l.svcCtx.WalletModel.FindUidByAddress(l.ctx, nft.Address)
if err != nil {
logx.Errorw("find uid by address error", logx.Field("error", err), logx.Field("address", nft.Address))
continue
}
err = l.svcCtx.StakeNftModel.UnStakeNft(l.ctx, uid, nft.TokenId, true)
if err != nil {
logx.Errorw("un stake nft error", logx.Field("error", err), logx.Field("address", nft.Address), logx.Field("token", nft.TokenId))
}
}
err = l.svcCtx.NftHolderModel.DeleteOtherUpdateSeq(l.ctx, updateSeq)
if err != nil {
logx.Errorw("delete other update seq error", logx.Field("error", err), logx.Field("update_seq", updateSeq))
}
}
type OwnerList struct {
Owners []struct {
OwnerAddress string `json:"ownerAddress"`
TokenBalances []struct {
TokenID string `json:"tokenId"`
Balance string `json:"balance"`
} `json:"tokenBalances"`
} `json:"owners"`
PageKey string `json:"pageKey"`
}
func (l *HolderUpdateLogic) GetOwnerList(network, contractAddress string) (*OwnerList, error) {
url := fmt.Sprintf("https://eth-%s.g.alchemy.com/nft/v3/alcht_1a183fAsPqF9upfTfp3AC1l0iedGLo/getOwnersForContract?contractAddress=%s&withTokenBalances=true", network, contractAddress)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
result := new(OwnerList)
err = json.NewDecoder(resp.Body).Decode(result)
return result, err
}

View File

@@ -0,0 +1,114 @@
package nft
import (
"context"
"github.com/shopspring/decimal"
"github.com/spf13/cast"
"github.com/zeromicro/go-zero/core/logx"
"nova_task/internal/model"
"nova_task/internal/pkg/utils"
"nova_task/internal/svc"
"time"
)
type StakeSettleLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewStakeSettleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StakeSettleLogic {
return &StakeSettleLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *StakeSettleLogic) StakeSettle() {
start, end, err := l.svcCtx.ConfigModel.GetNftStakeTaskOpenDate(l.ctx)
if err != nil {
logx.Errorw("get nft stake task open date failed", logx.Field("err", err))
return
}
end = end.AddDate(0, 0, 1).Add(-time.Second)
now := time.Now()
if now.Before(start) || now.After(end) {
logx.Infow("now is not in the date range", logx.Field("now", now), logx.Field("start", start), logx.Field("end", end))
return
}
taskConf, err := l.svcCtx.ConfigModel.GetNftStakeTaskConf(l.ctx)
if err != nil {
logx.Errorw("get nft stake task conf failed", logx.Field("err", err))
return
}
stakes, err := l.svcCtx.StakeNftModel.AllStakeNft(l.ctx)
if err != nil {
logx.Errorw("get all stake nft failed", logx.Field("err", err))
return
}
uid2tokens := map[uint]float64{}
for _, s := range stakes {
if s.State != 1 {
continue
}
nftHolder, err := l.svcCtx.NftHolderModel.FindOneByTokenId(l.ctx, s.TokenId)
if err != nil {
logx.Errorw("find nft holder failed", logx.Field("err", err), logx.Field("tokenId", s.TokenId))
continue
}
uid, err := l.svcCtx.WalletModel.FindUidByAddress(l.ctx, nftHolder.Address)
if err != nil {
logx.Errorw("find uid by address failed", logx.Field("err", err), logx.Field("address", nftHolder.Address))
continue
}
if uid != s.Uid {
logx.Errorw("uid not match", logx.Field("uid", s.Uid), logx.Field("nftHolderUid", uid))
continue
}
if utils.IsBigTarot(s.TokenId) {
uid2tokens[s.Uid] += float64(taskConf.GreatTarot)
} else {
uid2tokens[s.Uid] += float64(taskConf.LittleTarot)
}
}
awardSeq := cast.ToInt(time.Now().AddDate(0, 0, -1).Format("20060102"))
for uid, tokens := range uid2tokens {
coefficient, err := l.svcCtx.StakeRewardModel.GetRandomCoefficientByUid(l.ctx, uid, awardSeq, taskConf.MinCoefficient, taskConf.MaxCoefficient)
if err != nil {
logx.Errorw("get random coefficient failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("awardSeq", awardSeq))
continue
}
tokensDecimal := decimal.NewFromFloat(tokens)
reward := tokensDecimal.Mul(decimal.NewFromFloat(coefficient))
err = l.svcCtx.StakeRewardModel.SetReward(l.ctx, uid, awardSeq, taskConf.OccupyPercent, tokensDecimal, reward)
if err != nil {
logx.Errorw("set reward failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("awardSeq", awardSeq), logx.Field("coefficient", coefficient), logx.Field("tokens", tokens), logx.Field("reward", reward))
continue
}
// 加资产
err = l.svcCtx.TaskAssetModel.AddCastile(l.ctx, uid, reward)
if err != nil {
logx.Errorw("add castile failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("awardSeq", awardSeq), logx.Field("reward", reward))
continue
}
// 加资产记录表
_, err = l.svcCtx.TaskAssetRecordModel.Insert(l.ctx, &model.NhTaskAssetRecord{
Uid: int(uid),
EventId: 0,
AssetField: "castile",
Count: reward.InexactFloat64(),
Remark: "nft软质押奖励",
ProvideUid: 0,
CreateTime: int(now.Unix()),
})
if err != nil {
logx.Errorw("insert task asset record failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("awardSeq", awardSeq), logx.Field("reward", reward))
}
}
}

View File

@@ -48,7 +48,7 @@ func (l *GetNftListLogic) GetNftList(req *types.GetNftListReq) (*types.UserNftLi
var hasStake bool
var stakeAt string
var roleId uint64
nft, err := l.svcCtx.StakeNftModel.FindOneByUidTokenId(l.ctx, uid, token)
nft, err := l.svcCtx.StakeNftModel.FindOneByTokenId(l.ctx, token)
if err == nil {
hasStake = nft.State == 1
if hasStake {

View File

@@ -40,37 +40,52 @@ func (l *StakeNftLogic) StakeNft(req *types.StakeNftList) error {
l.Errorw("find nft holder failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("tokenId", tokenId))
return errs.New(errs.ErrDatabaseOperate, err)
}
l.svcCtx.WalletModel.FindUidByAddress(l.ctx, tkHolder.Address)
err = l.svcCtx.StakeNftModel.StakeNft(l.ctx, uid, req.RoleId, tokenId)
wUid, err := l.svcCtx.WalletModel.FindUidByAddress(l.ctx, tkHolder.Address)
if err != nil {
if !errors.Is(err, model.ErrNotFound) {
l.Errorw("find wallet by address failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("address", tkHolder.Address))
return errs.New(errs.ErrDatabaseOperate, err)
}
return errs.New(errs.ErrNftNotBelongToUser, "nft not belong to user")
}
if wUid != uid {
l.Errorw("nft not belong to user", logx.Field("uid", uid), logx.Field("address", tkHolder.Address))
return errs.New(errs.ErrNftNotBelongToUser, "nft not belong to user")
}
var sns []int8
var propertyId string
var tarotType int8
if utils.IsBigTarot(tokenId) {
sns = []int8{1, 2}
propertyId = "402505"
tarotType = 1
} else {
sns = []int8{1}
propertyId = "402605"
}
err = l.svcCtx.StakeNftModel.StakeNft(l.ctx, uid, req.RoleId, tokenId, tarotType)
if err != nil {
l.Errorw("stake nft failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("tokenIds", req.TokenIds))
return errs.New(errs.ErrDatabaseOperate, err)
}
var sns []int8
var propertyId string
if utils.IsBigTarot(tokenId) {
sns = []int8{1, 2}
propertyId = "402505"
} else {
sns = []int8{1}
propertyId = "402605"
}
for _, sn := range sns {
_, err = l.svcCtx.StakePropertyModel.Insert(l.ctx, &model.NhNftStakeProperty{
Uid: uid,
RoleId: int64(req.RoleId),
TokenId: cast.ToUint(tokenId),
PropertyId: propertyId,
Sn: sn,
})
if err != nil {
errMySQL := new(mysql.MySQLError)
if errors.As(err, &errMySQL) {
switch errMySQL.Number {
case 1062:
default:
l.Errorw("insert stake property failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("tokenId", tokenId), logx.Field("propertyId", propertyId))
if req.RoleId > 0 {
for _, sn := range sns {
_, err = l.svcCtx.StakePropertyModel.Insert(l.ctx, &model.NhNftStakeProperty{
Uid: uid,
RoleId: int64(req.RoleId),
TokenId: cast.ToUint(tokenId),
PropertyId: propertyId,
Sn: sn,
})
if err != nil {
errMySQL := new(mysql.MySQLError)
if errors.As(err, &errMySQL) {
switch errMySQL.Number {
case 1062:
default:
l.Errorw("insert stake property failed", logx.Field("err", err), logx.Field("uid", uid), logx.Field("tokenId", tokenId), logx.Field("propertyId", propertyId))
}
}
}
}

View File

@@ -37,13 +37,17 @@ type (
}
NhTaskNftStakeLog struct {
Id uint `db:"id"`
Uid uint `db:"uid"` // 用户钱包
RoleId uint64 `db:"role_id"` // 角色id
TokenId string `db:"token_id"` // token id
Operate int8 `db:"operate"` // 状态1质押 2取消质押, 3转出
CreatedAt time.Time `db:"created_at"` // 创建时间
UpdatedAt time.Time `db:"updated_at"` // 修改时间
Id uint `db:"id"`
Uid uint `db:"uid"` // 用户钱包
RoleId uint64 `db:"role_id"` // 角色id
TokenId string `db:"token_id"` // token id
Operate int8 `db:"operate"` // 状态1质押 2取消质押, 3转出
CallbackStatus int8 `db:"callback_status"` // 下发通知状态:0未通知,1已通知,2通知异常
CallbackNum int `db:"callback_num"` // 发送通知次数
CallbackAt sql.NullTime `db:"callback_at"` // 发送通知最新时间
CallbackRemark string `db:"callback_remark"` // 通知回调备注
CreatedAt time.Time `db:"created_at"` // 创建时间
UpdatedAt time.Time `db:"updated_at"` // 修改时间
}
)
@@ -75,14 +79,14 @@ func (m *defaultNhTaskNftStakeLogModel) FindOne(ctx context.Context, id uint) (*
}
func (m *defaultNhTaskNftStakeLogModel) Insert(ctx context.Context, data *NhTaskNftStakeLog) (sql.Result, error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?)", m.table, nhTaskNftStakeLogRowsExpectAutoSet)
ret, err := m.conn.ExecCtx(ctx, query, data.Uid, data.RoleId, data.TokenId, data.Operate)
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?)", m.table, nhTaskNftStakeLogRowsExpectAutoSet)
ret, err := m.conn.ExecCtx(ctx, query, data.Uid, data.RoleId, data.TokenId, data.Operate, data.CallbackStatus, data.CallbackNum, data.CallbackAt, data.CallbackRemark)
return ret, err
}
func (m *defaultNhTaskNftStakeLogModel) Update(ctx context.Context, data *NhTaskNftStakeLog) error {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, nhTaskNftStakeLogRowsWithPlaceHolder)
_, err := m.conn.ExecCtx(ctx, query, data.Uid, data.RoleId, data.TokenId, data.Operate, data.Id)
_, err := m.conn.ExecCtx(ctx, query, data.Uid, data.RoleId, data.TokenId, data.Operate, data.CallbackStatus, data.CallbackNum, data.CallbackAt, data.CallbackRemark, data.Id)
return err
}

View File

@@ -16,7 +16,7 @@ type (
NhTaskNftStakeModel interface {
nhTaskNftStakeModel
withSession(session sqlx.Session) NhTaskNftStakeModel
StakeNft(ctx context.Context, uid uint, roleId uint64, tokenId string) error
StakeNft(ctx context.Context, uid uint, roleId uint64, tokenId string, tarotType int8) error
UnStakeNft(ctx context.Context, uid uint, token string, isTransferOut bool) error
FindByUid(ctx context.Context, uid uint) ([]NhTaskNftStake, error)
AllStakeNft(ctx context.Context) ([]NhTaskNftStake, error)
@@ -49,7 +49,7 @@ func (m *customNhTaskNftStakeModel) FindByUid(ctx context.Context, uid uint) ([]
}
func (m *customNhTaskNftStakeModel) UnStakeNft(ctx context.Context, uid uint, token string, isTransferOut bool) error {
update := fmt.Sprintf("UPDATE %s SET `state` = 0 WHERE `uid` = ? AND `token_id` = ?", m.table)
update := fmt.Sprintf("UPDATE %s SET `state` = 0 WHERE `token_id` = ?", m.table)
result, err := m.conn.ExecCtx(ctx, update, uid, token)
if err != nil {
return err
@@ -74,9 +74,9 @@ func (m *customNhTaskNftStakeModel) UnStakeNft(ctx context.Context, uid uint, to
return err
}
func (m *customNhTaskNftStakeModel) StakeNft(ctx context.Context, uid uint, roleId uint64, tokenId string) error {
insertOrUpdate := fmt.Sprintf("INSERT INTO %s (`uid`, `token_id`, `role_id`, `state`) VALUES (?, ?, ?, 1) ON DUPLICATE KEY UPDATE `role_id` = ?, `state` = 1", m.table)
_, err := m.conn.ExecCtx(ctx, insertOrUpdate, uid, tokenId, roleId, roleId)
func (m *customNhTaskNftStakeModel) StakeNft(ctx context.Context, uid uint, roleId uint64, tokenId string, tarotType int8) error {
insertOrUpdate := fmt.Sprintf("INSERT INTO %s (`uid`, `token_id`, `role_id`, `type`, `state`) VALUES (?, ?, ?, ?, 1) ON DUPLICATE KEY UPDATE `uid` = ?, `role_id` = ?, `state` = 1", m.table)
_, err := m.conn.ExecCtx(ctx, insertOrUpdate, uid, tokenId, roleId, tarotType, uid, roleId)
if err != nil {
return err
}

View File

@@ -27,7 +27,7 @@ type (
nhTaskNftStakeModel interface {
Insert(ctx context.Context, data *NhTaskNftStake) (sql.Result, error)
FindOne(ctx context.Context, id uint) (*NhTaskNftStake, error)
FindOneByUidTokenId(ctx context.Context, uid uint, tokenId string) (*NhTaskNftStake, error)
FindOneByTokenId(ctx context.Context, tokenId string) (*NhTaskNftStake, error)
Update(ctx context.Context, data *NhTaskNftStake) error
Delete(ctx context.Context, id uint) error
}
@@ -41,6 +41,7 @@ type (
Id uint `db:"id"`
Uid uint `db:"uid"` // 用户钱包
RoleId uint64 `db:"role_id"` // 角色id
Type int8 `db:"type"` // 类型0=小塔罗1=大塔罗
TokenId string `db:"token_id"` // token id
State int8 `db:"state"` // 状态1质押中 0已取消质押
CreatedAt time.Time `db:"created_at"` // 创建时间
@@ -75,10 +76,10 @@ func (m *defaultNhTaskNftStakeModel) FindOne(ctx context.Context, id uint) (*NhT
}
}
func (m *defaultNhTaskNftStakeModel) FindOneByUidTokenId(ctx context.Context, uid uint, tokenId string) (*NhTaskNftStake, error) {
func (m *defaultNhTaskNftStakeModel) FindOneByTokenId(ctx context.Context, tokenId string) (*NhTaskNftStake, error) {
var resp NhTaskNftStake
query := fmt.Sprintf("select %s from %s where `uid` = ? and `token_id` = ? limit 1", nhTaskNftStakeRows, m.table)
err := m.conn.QueryRowCtx(ctx, &resp, query, uid, tokenId)
query := fmt.Sprintf("select %s from %s where `token_id` = ? limit 1", nhTaskNftStakeRows, m.table)
err := m.conn.QueryRowCtx(ctx, &resp, query, tokenId)
switch err {
case nil:
return &resp, nil
@@ -90,14 +91,14 @@ func (m *defaultNhTaskNftStakeModel) FindOneByUidTokenId(ctx context.Context, ui
}
func (m *defaultNhTaskNftStakeModel) Insert(ctx context.Context, data *NhTaskNftStake) (sql.Result, error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?)", m.table, nhTaskNftStakeRowsExpectAutoSet)
ret, err := m.conn.ExecCtx(ctx, query, data.Uid, data.RoleId, data.TokenId, data.State)
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, nhTaskNftStakeRowsExpectAutoSet)
ret, err := m.conn.ExecCtx(ctx, query, data.Uid, data.RoleId, data.Type, data.TokenId, data.State)
return ret, err
}
func (m *defaultNhTaskNftStakeModel) Update(ctx context.Context, newData *NhTaskNftStake) error {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, nhTaskNftStakeRowsWithPlaceHolder)
_, err := m.conn.ExecCtx(ctx, query, newData.Uid, newData.RoleId, newData.TokenId, newData.State, newData.Id)
_, err := m.conn.ExecCtx(ctx, query, newData.Uid, newData.RoleId, newData.Type, newData.TokenId, newData.State, newData.Id)
return err
}

View File

@@ -28,7 +28,7 @@ type (
// SetReward 对未发送奖励的用户发送奖励,支持并发,且不会重发
func (m *customNhTaskNftStakeRewardModel) SetReward(ctx context.Context, uid uint, awardSeq, occupyPercent int, pledgeOutput, reward decimal.Decimal) error {
update := fmt.Sprintf("UPDATE %s SET `occupy_percent` = ?, `pledge_output` = ?, `reward` = ?, `sent` = 1 WHERE `uid` = ? AND `award_seq` = ?, `sent` = 0", m.table)
update := fmt.Sprintf("UPDATE %s SET `occupy_percent` = ?, `pledge_output` = ?, `reward` = ?, `sent` = 1 WHERE `uid` = ? AND `award_seq` = ? AND `sent` = 0", m.table)
result, err := m.conn.ExecCtx(ctx, update, occupyPercent, pledgeOutput, reward, uid, awardSeq)
if err != nil {
return err

View File

@@ -25,4 +25,6 @@ const (
ErrNotBindWallet Reason = 20004 // 未绑定钱包
ErrTaskOpenDateNotSet Reason = 20005 // 任务开放时间未设置
ErrTaskConfNotSet Reason = 20006 // 任务配置未设置
ErrUserNotFound Reason = 20007 // 用户不存在
ErrNftNotBelongToUser Reason = 20008 // NFT不属于用户
)