chain balance query

This commit is contained in:
yangduo 2024-09-06 10:25:17 +08:00
parent 0303dd302e
commit e7da389340
11 changed files with 327 additions and 2 deletions

View File

@ -3,6 +3,8 @@ package system
import (
"f5"
"fmt"
"main/mt"
"main/service"
"main/constant"
"main/model/system"
@ -125,3 +127,68 @@ func (this *NFTApi) NFTQuery(c *gin.Context) {
return p
})
}
func (this *NFTApi) BalanceQuery(c *gin.Context) {
type BalanceQueryForm struct {
Account string `json:"account_address"`
}
reqJson := BalanceQueryForm{}
if !checkparam(&reqJson, c) {
return
}
selfaccount := false
if reqJson.Account != "" {
mt.Table.Chain.Traverse(func(item *mt.Chain) bool {
if item.GetAccountAddress() == reqJson.Account {
selfaccount = true
return false
}
return true
})
}
type retitem struct {
Account string `json:"account_address"`
TokenName string `json:"token_name"`
ChainBalance string `json:"chain_balance"`
Recharge int64 `json:"recharge"`
NetId int32 `json:"net_id"`
TokenAddress string `json:"-"`
}
result := []retitem{}
mt.Table.Chain.Traverse(func(item *mt.Chain) bool {
account := item.GetAccountAddress()
if selfaccount && account != reqJson.Account {
return true
}
if reqJson.Account != "" && !selfaccount {
account = reqJson.Account
}
p := q5.NewSliceElement(&result)
p.Account = account
p.NetId = item.GetNetId()
p.TokenName = item.GetTokenName()
p.TokenAddress = item.GetTokenAddress()
key := fmt.Sprintf("%d$%s", item.GetNetId(), item.GetTokenAddress())
if selfaccount || reqJson.Account == "" {
p.ChainBalance, p.Recharge = service.Balance.GetGlobalBalance(key)
} else {
p.ChainBalance, p.Recharge = service.Balance.GetAccoutBalance(key, account)
}
return true
})
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "",
"data": result,
})
}

View File

@ -18,6 +18,7 @@ const (
APP_MODULE_IDX = iota
ROUTER_MODULE_IDX
SESSION_MGR_MODULE_IDX
SERVICE_MGR_MODULE_IDX
MAX_MODULE_IDX
)

View File

@ -1,16 +1,17 @@
package global
import (
"q5"
"fmt"
"main/constant"
"main/common"
"main/constant"
"q5"
)
var modules [constant.MAX_MODULE_IDX]q5.Module
var initOrders = []int32 {
constant.ROUTER_MODULE_IDX,
constant.SESSION_MGR_MODULE_IDX,
constant.SERVICE_MGR_MODULE_IDX,
}
var app common.App
@ -39,6 +40,10 @@ func RegModule(idx int32, m q5.Module) {
{
sessionMgr = m.(common.SessionMgr)
}
case constant.SERVICE_MGR_MODULE_IDX:
{
}
default:
{
panic("unknow module")

View File

@ -0,0 +1,14 @@
package mt
import (
"f5"
"main/mtb"
)
type Chain struct {
mtb.Erc20
}
type ChainTable struct {
f5.RawMetaTable[Chain]
}

View File

@ -18,6 +18,7 @@ type table struct {
ConfDb *ConfDbTable
Item *ItemTable
Contract *ContractTable
Chain *ChainTable
}
var Table = f5.New(func(this *table) {
@ -78,4 +79,8 @@ var Table = f5.New(func(this *table) {
this.FileName = "../res/item@item.json"
this.PrimKey = "id"
})
this.Chain = f5.New(func(this *ChainTable) {
this.FileName = "../config/erc20.json"
})
})

View File

@ -129,6 +129,7 @@ type Item struct {
}
type Erc20 struct {
token_name string
net_id int32
rpc_url string
account_address string
@ -602,6 +603,14 @@ func (this *Item) HasSubType() bool {
return (this._flags1_ & (uint64(1) << 4)) > 0
}
func (this *Erc20) GetTokenName() string {
return this.token_name
}
func (this *Erc20) HasTokenName() bool {
return (this._flags1_ & (uint64(1) << 1)) > 0
}
func (this *Erc20) GetNetId() int32 {
return this.net_id
}
@ -727,6 +736,7 @@ func (this *Item) LoadFromKv(kv map[string]interface{}) {
}
func (this *Erc20) LoadFromKv(kv map[string]interface{}) {
f5.ReadMetaTableField(&this.token_name, "token_name", &this._flags1_, 1, kv)
f5.ReadMetaTableField(&this.net_id, "net_id", &this._flags1_, 2, kv)
f5.ReadMetaTableField(&this.rpc_url, "rpc_url", &this._flags1_, 3, kv)
f5.ReadMetaTableField(&this.account_address, "account_address", &this._flags1_, 4, kv)

View File

@ -106,6 +106,7 @@ message Item
message Erc20
{
optional string token_name = 1;
optional int32 net_id = 2;
optional string rpc_url = 3;
optional string account_address = 4;

View File

@ -16,5 +16,6 @@ func (nr *NFTRouter) InitNFTRouter(priRouter *gin.RouterGroup) {
group.POST("orderquery", middleware.Permission("api/v1/nft/orderquery", api.OrderQuery))
group.POST("salequery", middleware.Permission("api/v1/nft/salequery", api.SaleQuery))
group.POST("nftquery", middleware.Permission("api/v1/nft/nftquery", api.NFTQuery))
group.POST("balancequery", middleware.Permission("api/v1/nft/balancequery", api.BalanceQuery))
}
}

View File

@ -0,0 +1,196 @@
package service
import (
"adminserver/constant"
"f5"
"fmt"
"main/mt"
"q5"
"strings"
"time"
)
type baseinfo struct {
recharge int64
balance string
}
type addressBalance struct {
gcTime int64
infoList q5.ConcurrentMap[string, *baseinfo] // <netid$toke_addr, baseinfo>
}
type chainbalance struct {
gcTime int64
infoList q5.ConcurrentMap[string, *baseinfo] // <netid$toke_addr, baseinfo>
addressBalanceList q5.ConcurrentMap[string, *addressBalance] // <account, addressBalance>
}
func (this *chainbalance) init() {
this.infoList = q5.ConcurrentMap[string, *baseinfo]{}
this.addressBalanceList = q5.ConcurrentMap[string, *addressBalance]{}
this.GetGlobalBalance("")
go this.checkBalanceList()
}
func (this *chainbalance) unInit() {
}
func (this *chainbalance) GetAccoutBalance(netidtoken string, acc string) (string, int64) {
info, exist := this.addressBalanceList.Load(acc)
if !exist {
tmpinfo := new(addressBalance)
this.addressBalanceList.Store(acc, tmpinfo)
info = &tmpinfo
}
if nowseconds := f5.GetApp().GetRealSeconds(); (*info).gcTime+60 < nowseconds {
(*info).gcTime = nowseconds
this.getChainBalance(acc)
}
v, exist := (*info).infoList.Load(netidtoken)
if exist {
return (*v).balance, (*v).recharge
}
return "", 0
}
func (this *chainbalance) GetGlobalBalance(netidtoken string) (string, int64) {
if nowseconds := f5.GetApp().GetRealSeconds(); this.gcTime+60 < nowseconds {
this.gcTime = nowseconds
go this.getChainBalance("")
}
v, exist := this.infoList.Load(netidtoken)
if exist {
return (*v).balance, (*v).recharge
}
return "", 0
}
func (this *chainbalance) getChainBalance(acc string) {
tmpmap := q5.ConcurrentMap[string, int32]{}
url := mt.Table.Web3ServiceCluster.RandElement().GetUrl() + "/webapp/index.php"
mt.Table.Chain.Traverse(func(item *mt.Chain) bool {
var info *baseinfo
key := fmt.Sprintf("%d$%s", item.GetNetId(), item.GetTokenAddress())
if acc == "" {
tmp, exist := this.infoList.Load(key)
if !exist {
info = new(baseinfo)
this.infoList.Store(key, info)
tmp = &info
} else {
info = *tmp
}
} else {
accinfo, exist := this.addressBalanceList.Load(acc)
if !exist {
tmp := new(addressBalance)
this.addressBalanceList.Store(acc, tmp)
accinfo = &tmp
info = new(baseinfo)
tmp.infoList.Store(key, info)
} else {
keyinfo, exist := (*accinfo).infoList.Load(key)
if !exist {
info = new(baseinfo)
(*accinfo).infoList.Store(key, info)
} else {
info = *keyinfo
}
}
}
{
sql := fmt.Sprintf(`SELECT sum(diamond) FROM t_recharge_order WHERE idx > 0 AND pay_status = 1 AND net_id = %d AND currency_name = '%s'`, item.GetNetId(), strings.ToLower(item.GetTokenAddress()))
if acc != "" {
sql = fmt.Sprintf(`%s AND account_address = '%s'`, sql, strings.ToLower(acc))
}
f5.GetGoStyleDb().RawQuery(
constant.BCNFT_DB,
sql,
[]string{},
func(err error, ds *f5.DataSet) {
if err != nil {
return
}
if ds.Next() {
info.recharge = q5.SafeToInt64(ds.GetByIndex(0)) / 100
}
})
}
rpcurl := item.GetRpcUrl()
tmpmap.Store(key, 1)
doquery := func(account string, tokeaddr string, balance *string) {
f5.GetHttpCliMgr().SendGoStyleRequest(
url,
map[string]string{
"c": "Erc20",
"a": "getBalance",
"rpc_url": rpcurl,
"account_address": account,
"token_address": tokeaddr,
},
func(rsp f5.HttpCliResponse) {
tmpmap.Delete(key)
if rsp.GetErr() != nil {
return
}
rspJson := struct {
ErrCode int32 `json:"errcode"`
ErrMsg string `json:"errmsg"`
Balance string `json:"balance"`
}{}
if q5.DecodeJson(rsp.GetRawData(), &rspJson) != nil {
return
}
*balance = rspJson.Balance
})
}
queryAcc := item.GetAccountAddress()
if acc != "" {
queryAcc = acc
}
go doquery(queryAcc, item.GetTokenAddress(), &info.balance)
return true
})
for tmpmap.GetSize() > 0 {
time.Sleep(time.Second)
}
}
func (this *chainbalance) checkBalanceList() {
fmt.Println("checkBalanceList start")
for {
if time.Now().UTC().Hour() == 0 && time.Now().UTC().Minute() == 0 {
nowseconds := f5.GetApp().GetRealSeconds()
deletelist := []string{}
this.addressBalanceList.Range(func(key string, value *addressBalance) bool {
if value.gcTime+86400 < nowseconds {
deletelist = append(deletelist, key)
}
return true
})
for _, account := range deletelist {
this.addressBalanceList.Delete(account)
}
f5.GetSysLog().Info("delete chainbalance address cache count:%d", len(deletelist))
}
time.Sleep((time.Second * 60))
}
}

View File

@ -0,0 +1,13 @@
package service
import (
"main/constant"
"main/global"
)
var Balance = new(chainbalance)
var _serviceMgr = new(serviceMgr)
func init() {
global.RegModule(constant.SERVICE_MGR_MODULE_IDX, _serviceMgr)
}

View File

@ -0,0 +1,12 @@
package service
type serviceMgr struct {
}
func (this *serviceMgr) Init() {
Balance.init()
}
func (this *serviceMgr) UnInit() {
Balance.unInit()
}