diff --git a/server/adminserver/api/v1/system/nft.go b/server/adminserver/api/v1/system/nft.go index a3ec2282..a41b0cf3 100644 --- a/server/adminserver/api/v1/system/nft.go +++ b/server/adminserver/api/v1/system/nft.go @@ -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, + }) +} diff --git a/server/adminserver/constant/constant.go b/server/adminserver/constant/constant.go index 68fa5994..e466e9a3 100644 --- a/server/adminserver/constant/constant.go +++ b/server/adminserver/constant/constant.go @@ -18,6 +18,7 @@ const ( APP_MODULE_IDX = iota ROUTER_MODULE_IDX SESSION_MGR_MODULE_IDX + SERVICE_MGR_MODULE_IDX MAX_MODULE_IDX ) diff --git a/server/adminserver/global/global.go b/server/adminserver/global/global.go index 7242351f..aaa76b58 100644 --- a/server/adminserver/global/global.go +++ b/server/adminserver/global/global.go @@ -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") diff --git a/server/adminserver/mt/Chain.go b/server/adminserver/mt/Chain.go new file mode 100644 index 00000000..f934b81f --- /dev/null +++ b/server/adminserver/mt/Chain.go @@ -0,0 +1,14 @@ +package mt + +import ( + "f5" + "main/mtb" +) + +type Chain struct { + mtb.Erc20 +} + +type ChainTable struct { + f5.RawMetaTable[Chain] +} diff --git a/server/adminserver/mt/export.go b/server/adminserver/mt/export.go index d8ed25ea..1d64c8c9 100644 --- a/server/adminserver/mt/export.go +++ b/server/adminserver/mt/export.go @@ -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" + }) }) diff --git a/server/adminserver/mtb/mtb.auto_gen.go b/server/adminserver/mtb/mtb.auto_gen.go index ab8913a1..185ed99c 100644 --- a/server/adminserver/mtb/mtb.auto_gen.go +++ b/server/adminserver/mtb/mtb.auto_gen.go @@ -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) diff --git a/server/adminserver/proto/mt.proto b/server/adminserver/proto/mt.proto index 91f45cfe..cd182c42 100644 --- a/server/adminserver/proto/mt.proto +++ b/server/adminserver/proto/mt.proto @@ -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; diff --git a/server/adminserver/router/system/sys_nft.go b/server/adminserver/router/system/sys_nft.go index 89ba3734..81a74f89 100644 --- a/server/adminserver/router/system/sys_nft.go +++ b/server/adminserver/router/system/sys_nft.go @@ -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)) } } diff --git a/server/adminserver/service/balance.go b/server/adminserver/service/balance.go new file mode 100644 index 00000000..4adff432 --- /dev/null +++ b/server/adminserver/service/balance.go @@ -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] // +} + +type chainbalance struct { + gcTime int64 + + infoList q5.ConcurrentMap[string, *baseinfo] // + addressBalanceList q5.ConcurrentMap[string, *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)) + } +} diff --git a/server/adminserver/service/export.go b/server/adminserver/service/export.go new file mode 100644 index 00000000..bb684d28 --- /dev/null +++ b/server/adminserver/service/export.go @@ -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) +} diff --git a/server/adminserver/service/servicemgr.go b/server/adminserver/service/servicemgr.go new file mode 100644 index 00000000..01a7e45a --- /dev/null +++ b/server/adminserver/service/servicemgr.go @@ -0,0 +1,12 @@ +package service + +type serviceMgr struct { +} + +func (this *serviceMgr) Init() { + Balance.init() +} + +func (this *serviceMgr) UnInit() { + Balance.unInit() +}