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 delAddress, addAddress string if nft.Id == 0 { // 新增 _, err = l.svcCtx.NftHolderModel.Insert(l.ctx, nft) addAddress = nft.Address } else { // 地址变化 if nft.Address != o.OwnerAddress { delAddress = nft.Address addAddress = o.OwnerAddress } nft.Balance = balance nft.UpdateSeq = updateSeq nft.Address = o.OwnerAddress 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 addAddress != "" { _, err = l.svcCtx.NftHolderChangeLogModel.Insert(l.ctx, &model.NhNftHolderChangeLog{ Address: addAddress, 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)) } } // 原地址(删除) if delAddress != "" { _, err = l.svcCtx.NftHolderChangeLogModel.Insert(l.ctx, &model.NhNftHolderChangeLog{ Address: delAddress, TokenId: tk.TokenID, Value: -balance, Balance: 0, }) 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)) } uid, err := l.svcCtx.WalletModel.FindUidByAddress(l.ctx, delAddress) 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, tk.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)) } } } } // 删除已经不持有的地址,且添加变化日志 //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 }