diff --git a/internal/app/api/v1/account.go b/internal/app/api/v1/account.go index 2cecad9..e8742de 100644 --- a/internal/app/api/v1/account.go +++ b/internal/app/api/v1/account.go @@ -72,11 +72,13 @@ func AddCollection(c *gin.Context) { var req request.AddCollectionReq _ = c.ShouldBindJSON(&req) address := c.GetString("address") - if len(req.IDs) == 0 || address == "" { + req.Chain = global.ChainName[req.ChainID] + if address == "" || req.Chain == "" { response.FailWithMessage("Error", c) return } - if err := service.AddCollection(req.IDs, address, req.Flag); err != nil { + + if err := service.AddCollection(address, req); err != nil { global.LOG.Error("Error!", zap.Error(err)) response.FailWithMessage("Error", c) } else { @@ -104,6 +106,7 @@ func UpdatedCollection(c *gin.Context) { func RefreshUserData(c *gin.Context) { var req request.RefreshUserDataReq _ = c.ShouldBindJSON(&req) + req.Address = strings.ToLower(req.Address) if err := service.RefreshUserData(req.Address); err != nil { global.LOG.Error("Error!", zap.Error(err)) response.FailWithMessage("Error", c) diff --git a/internal/app/global/global.go b/internal/app/global/global.go index e899534..480d539 100644 --- a/internal/app/global/global.go +++ b/internal/app/global/global.go @@ -4,12 +4,11 @@ import ( "go.uber.org/zap" "gorm.io/gorm" "nft-collect/internal/app/config" - "nft-collect/pkg/cache" ) var ( - DB *gorm.DB // 数据库链接 - LOG *zap.Logger // 日志框架 - CONFIG config.Server // 配置信息 - Cache *cache.BigCacheStore // 缓存 + DB *gorm.DB // 数据库链接 + LOG *zap.Logger // 日志框架 + CONFIG config.Server // 配置信息 + ChainName map[uint]string // 链名称 ) diff --git a/internal/app/initialize/cache.go b/internal/app/initialize/cache.go deleted file mode 100644 index 5ece679..0000000 --- a/internal/app/initialize/cache.go +++ /dev/null @@ -1,14 +0,0 @@ -package initialize - -import ( - "nft-collect/internal/app/global" - "nft-collect/pkg/cache" - "time" -) - -func InitCache() { - if global.CONFIG.NFT.CacheTime < 3 { - panic("Less than 3 minutes") - } - global.Cache = cache.NewBigCacheStore(time.Duration(global.CONFIG.NFT.CacheTime)*time.Minute, global.LOG) -} diff --git a/internal/app/initialize/contract.go b/internal/app/initialize/contract.go index 6bd63e1..ea7a78c 100644 --- a/internal/app/initialize/contract.go +++ b/internal/app/initialize/contract.go @@ -6,7 +6,7 @@ import ( "strings" ) -// 获取默认NFT合约信息 +// InitNFTContract 获取默认NFT合约信息 func InitNFTContract() { for _, api := range global.CONFIG.NFT.APIConfig { for _, v := range global.CONFIG.NFT.DefContract { @@ -19,3 +19,10 @@ func InitNFTContract() { } } } + +func InitChainName() { + global.ChainName = make(map[uint]string) + for _, api := range global.CONFIG.NFT.APIConfig { + global.ChainName[api.ChainID] = api.Chain + } +} diff --git a/internal/app/middleware/cache.go b/internal/app/middleware/cache.go deleted file mode 100644 index 30c4bba..0000000 --- a/internal/app/middleware/cache.go +++ /dev/null @@ -1,11 +0,0 @@ -package middleware - -import ( - cache "github.com/chenyahui/gin-cache" - "github.com/gin-gonic/gin" - "nft-collect/internal/app/global" -) - -func Cache() gin.HandlerFunc { - return cache.CacheByRequestURI(global.Cache, 0) -} diff --git a/internal/app/model/collection.go b/internal/app/model/collection.go index 3fc7535..18dea0f 100644 --- a/internal/app/model/collection.go +++ b/internal/app/model/collection.go @@ -8,8 +8,7 @@ type Collection struct { global.MODEL Chain string `gorm:"column:chain;index:chain_address_contract_token,unique" json:"chain" form:"chain"` // 区块链的简称(eth, bnb, polygon, moonbeam, arbitrum, optimism, platon, avalanche, cronos) AccountAddress string `gorm:"column:account_address;type:char(42);index:chain_address_contract_token,unique" json:"account_address" form:"account_address"` // 资产持有者的地址 - Status uint8 `gorm:"default:1;" json:"status" form:"status"` // 显示状态(1:隐藏 2:显示) - Flag uint8 `gorm:"default:1;" json:"flag" form:"flag"` // 添加状态(1:未添加 2:已添加) + Status uint8 `gorm:"default:0;" json:"status" form:"status"` // 显示状态(0:初始状态 1:隐藏 2:显示) NFTScanOwn } @@ -18,7 +17,6 @@ type CollectionUpdate struct { Chain string `gorm:"column:chain;index:chain_address_contract_token,unique" json:"chain" form:"chain"` // 区块链的简称(eth, bnb, polygon, moonbeam, arbitrum, optimism, platon, avalanche, cronos) AccountAddress string `gorm:"column:account_address;type:char(42);index:chain_address_contract_token,unique" json:"account_address" form:"account_address"` // 资产持有者的地址 Status uint8 `gorm:"-" json:"status" form:"status"` // 显示状态(1:隐藏 2:显示) - Flag uint8 `gorm:"-" json:"flag" form:"flag"` // 添加状态(1:未添加 2:已添加) NFTScanOwn } diff --git a/internal/app/model/request/account.go b/internal/app/model/request/account.go index 7b90c4d..325be20 100644 --- a/internal/app/model/request/account.go +++ b/internal/app/model/request/account.go @@ -7,8 +7,11 @@ type AddContractReq struct { } type AddCollectionReq struct { - Flag int64 `json:"flag"` - IDs []string `json:"ids" form:"ids"` + ChainID uint `json:"chain_id" form:"chain_id"` + Chain string + ContractAddress string `json:"contract_address" form:"contract_address"` // 合约地址 + HideIDs []string `json:"hide_ids" form:"hide_ids"` + ShowIDs []string `json:"show_ids" form:"show_ids"` } type UpdatedCollectionReq struct { diff --git a/internal/app/router/account.go b/internal/app/router/account.go index dbea82d..1c4eadc 100644 --- a/internal/app/router/account.go +++ b/internal/app/router/account.go @@ -7,11 +7,12 @@ import ( ) func InitAccountRouter(Router *gin.RouterGroup) { - accountRouterWithCache := Router.Group("account").Use(middleware.Addr()) + accountRouterWithAddr := Router.Group("account").Use(middleware.Addr()) accountRouterWithAuth := Router.Group("account").Use(middleware.Auth()) // auth { - accountRouterWithCache.GET("/own/:address/contract", v1.GetContract) // Get the list of user NFT contracts - accountRouterWithCache.GET("/own/:address", v1.GetCollection) // Get the NFT data by the user + accountRouterWithAddr.GET("/own/:address/contract", v1.GetContract) // Get the list of user NFT contracts + accountRouterWithAddr.GET("/own/:address", v1.GetCollection) // Get the NFT data by the user + accountRouterWithAddr.POST("/own/refreshUserData", v1.RefreshUserData) // refresh user data } { accountRouterWithAuth.GET("/contract/:address", v1.GetCollectionByContract) // Get the NFT data by the Contract @@ -19,7 +20,5 @@ func InitAccountRouter(Router *gin.RouterGroup) { { accountRouterWithAuth.POST("/own/collection", v1.AddCollection) // add collection accountRouterWithAuth.PUT("/own/collection/:id", v1.UpdatedCollection) // update collection status - accountRouterWithAuth.POST("/own/refreshUserData", v1.RefreshUserData) // add collection - } } diff --git a/internal/app/service/account.go b/internal/app/service/account.go index a26a8f7..4ae1c82 100644 --- a/internal/app/service/account.go +++ b/internal/app/service/account.go @@ -23,6 +23,7 @@ func GetContract(address, account string) (res []response.GetContractRes, err er if errFirst != nil && errFirst != gorm.ErrRecordNotFound { return res, errFirst } + // 获取默认合约 var contractDefault []string err = db.Model(&model.ContractDefault{}).Raw("SELECT contract_id FROM contract_default").Scan(&contractDefault).Error if errFirst == gorm.ErrRecordNotFound { @@ -33,7 +34,7 @@ func GetContract(address, account string) (res []response.GetContractRes, err er dealList = contractDefault } else if address != common.HexToAddress("0").String() { dealList = slice.DiffSlice[string](contractDefault, user.ContractIDs) - go updateAllCollection(address, dealList, false) // update all collection + go updateAllCollection(address, dealList, false, false) // update all collection } if len(user.ContractIDs) != len(user.Counts) { @@ -46,7 +47,7 @@ func GetContract(address, account string) (res []response.GetContractRes, err er // TODO 优化 var count int64 err = db.Model(&model.Collection{}). - Raw("SElECT COUNT(1) FROM collection a JOIN contract b ON a.contract_address=b.contract_address AND a.chain=b.chain WHERE b.id = ? AND account_address= ? AND a.flag=2", id, address). + Raw("SElECT COUNT(1) FROM collection a JOIN contract b ON a.contract_address=b.contract_address AND a.chain=b.chain WHERE b.id = ? AND account_address= ? AND a.status=2", id, address). Scan(&count).Error contractMap[id] = count } @@ -121,7 +122,7 @@ func initAccount(address string) (err error) { if err != nil { return err } - updateAllCollection(address, uuidList, true) + updateAllCollection(address, uuidList, true, false) return err } @@ -148,13 +149,13 @@ func updateContractCount(address string) (err error) { // TODO: 添加状态 需要确定是否过滤 err = db.Model(&model.Collection{}).Where("chain", nftContract.Chain). - Where("contract_address", nftContract.ContractAddress).Where("account_address", address).Where("flag", 2). + Where("contract_address", nftContract.ContractAddress).Where("account_address", address). Count(&count).Error if err != nil { return err } err = db.Model(&model.Collection{}).Where("chain", nftContract.Chain). - Where("contract_address", nftContract.ContractAddress).Where("account_address", address).Where("flag", 2).Where("status", 2). + Where("contract_address", nftContract.ContractAddress).Where("account_address", address).Where("status", 2). Count(&countShow).Error if err != nil { return err diff --git a/internal/app/service/cahce.go b/internal/app/service/cahce.go deleted file mode 100644 index 35ecf4a..0000000 --- a/internal/app/service/cahce.go +++ /dev/null @@ -1,22 +0,0 @@ -package service - -import ( - "fmt" - "nft-collect/internal/app/global" - "strings" -) - -func DeleteCache(address string) { - iterator := global.Cache.Cache.Iterator() - url := fmt.Sprintf("/account/own/%s", address) - for iterator.SetNext() { - entryInfo, err := iterator.Value() - if err == nil { - // 处理key和value - if strings.Contains(strings.ToLower(entryInfo.Key()), url) { - fmt.Println("delete:", entryInfo.Key()) - global.Cache.Cache.Delete(entryInfo.Key()) - } - } - } -} diff --git a/internal/app/service/collection.go b/internal/app/service/collection.go index 244c37f..ee3d3ec 100644 --- a/internal/app/service/collection.go +++ b/internal/app/service/collection.go @@ -51,7 +51,7 @@ func GetCollection(req request.GetCollectionReq, account string) (total, totalPu db := global.DB.Model(&model.Collection{}).Select("collection.*,contract.contract_logo"). Joins("left join contract ON contract.chain=collection.chain AND contract.contract_address=collection.contract_address"). - Where("collection.flag", 2).Where("collection.account_address", req.AccountAddress) + Where("collection.account_address", req.AccountAddress) if req.Search != "" { db.Where("token_id ILIKE ? OR name ILIKE ?", "%"+req.Search+"%", "%"+req.Search+"%") } @@ -124,11 +124,11 @@ func GetCollectionByContract(req request.GetCollectionReq) (total int64, res []m wg.Wait() } else { if firstCollection.UpdatedAt.Before(time.Now().Add(-time.Duration(global.CONFIG.NFT.CacheTime) * time.Minute)) { - fmt.Println("缓存") wg := new(sync.WaitGroup) wg.Add(2) go addCollectionByContract(wg, req.AccountAddress, "erc721", api, req.ContractAddress) go addCollectionByContract(wg, req.AccountAddress, "erc1155", api, req.ContractAddress) + wg.Wait() } } } @@ -151,60 +151,63 @@ func GetCollectionByContract(req request.GetCollectionReq) (total int64, res []m // @description: add collection by ids // @param: ids []string, address string // @return: err error -func AddCollection(ids []string, address string, flag int64) (err error) { +func AddCollection(address string, req request.AddCollectionReq) (err error) { tx := global.DB.Begin() - if flag == 2 { - idsMap := make(map[string]struct{}) - for _, id := range ids { - if id == "" { - continue - } - raw := tx.Model(&model.Collection{}).Where("id", id).Updates(map[string]interface{}{"flag": 2, "status": 2}) - if raw.RowsAffected > 0 { - var nft model.Collection - errNft := tx.Model(&model.Collection{}).Where("id", id).First(&nft).Error - if errNft != nil { - continue - } - var idContract string - errNFTContract := tx.Model(&model.Contract{}).Select("id").Where("chain", nft.Chain). - Where("contract_address", nft.ContractAddress).First(&idContract).Error - if errNFTContract != nil { - continue - } - idsMap[idContract] = struct{}{} - } + // 添加NFT + err = tx.Model(&model.Collection{}). + Where("chain = ? AND contract_address = ? AND status = 0", req.Chain, req.ContractAddress). + Updates(map[string]interface{}{"status": 2}).Error + if err != nil { + tx.Rollback() + return err + } + // 隐藏NFT + for _, id := range req.HideIDs { + if id == "" { + continue } - if tx.Commit().Error != nil { + err = tx.Model(&model.Collection{}).Where("id", id).Updates(map[string]interface{}{"status": 1}).Error + if err != nil { + tx.Rollback() return err } - - for k, _ := range idsMap { - _ = addContractToUser(k, address) - } - } else if flag == 1 { - for _, id := range ids { - if id == "" { - continue - } - _ = tx.Model(&model.Collection{}).Where("id", id).Updates(map[string]interface{}{"flag": 1, "status": 1}) + } + // 显示NFT + for _, id := range req.ShowIDs { + if id == "" { + continue } - if tx.Commit().Error != nil { + err = tx.Model(&model.Collection{}).Where("id", id).Updates(map[string]interface{}{"status": 2}).Error + if err != nil { + tx.Rollback() return err } } + // 添加合约到用户 + var idContract string + errNFTContract := tx.Model(&model.Contract{}).Select("id").Where("chain", req.Chain). + Where("contract_address", req.ContractAddress).First(&idContract).Error + if errNFTContract != nil { + tx.Rollback() + return err + } + _ = addContractToUser(idContract, address) + // 更新合约数量 updateContractCount(address) - - return nil + return tx.Commit().Error } // updateAllCollection // @description: update account all collection // @param: address string -func updateAllCollection(address string, uuidList []string, init bool) { +func updateAllCollection(address string, uuidList []string, init bool, refresh bool) { var count int64 db := global.DB.Model(&model.Account{}) - db.Where("(updated_at < ?) AND total < 100 AND address = ? ", time.Now().Add(-time.Duration(global.CONFIG.NFT.CacheTime)*time.Minute), address) + limitTime := time.Now().Add(-time.Duration(global.CONFIG.NFT.CacheTime) * time.Minute) + if refresh { + limitTime = time.Now().Add(-time.Duration(1) * time.Minute) + } + db.Where("(updated_at < ?) AND total < 100 AND address = ? ", limitTime, address) err := db.Count(&count).Error if err != nil { global.LOG.Error("error getting", zap.Error(err)) @@ -236,7 +239,10 @@ func updateAllCollection(address string, uuidList []string, init bool) { if err = global.DB.Model(&model.Contract{}).Select("contract_address").Where("id", v).First(&contracts).Error; err != nil { return } - if err = global.DB.Model(&model.Collection{}).Where("account_address", address).Where("contract_address", contracts).Updates(map[string]interface{}{"status": 2, "flag": 2}).Error; err != nil { + if err = global.DB.Model(&model.Collection{}).Where("account_address", address). + Where("contract_address", contracts). + Where("status=1"). + Updates(map[string]interface{}{"status": 2}).Error; err != nil { return } } @@ -325,7 +331,7 @@ func addCollectionByContract(wg *sync.WaitGroup, address string, erc_type string return nil } // 保存数据 - if err = global.DB.Model(&model.Collection{}).Omit("status", "flag").Clauses(clause.OnConflict{ + if err = global.DB.Model(&model.Collection{}).Omit("status").Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "chain"}, {Name: "account_address"}, {Name: "contract_address"}, {Name: "token_id"}}, UpdateAll: true, }).Create(&nft).Error; err != nil { @@ -422,22 +428,24 @@ func addAllCollection(address string, api config.APIConfig, contract string) (to if _, ok := contractList[common.HexToAddress(v.ContractAddress)]; !ok { continue } - nft = append(nft, model.Collection{Chain: api.Chain, AccountAddress: address, NFTScanOwn: v}) + nft = append(nft, model.Collection{Chain: api.Chain, AccountAddress: address, NFTScanOwn: v, Status: 2}) } if len(nft) == 0 { return total, nil } // 保存数据 - if err = global.DB.Model(&model.Collection{}).Omit("status", "flag").Clauses(clause.OnConflict{ + if err = global.DB.Model(&model.Collection{}).Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "chain"}, {Name: "account_address"}, {Name: "contract_address"}, {Name: "token_id"}}, - UpdateAll: true, + DoNothing: true, }).Create(&nft).Error; err != nil { return total, err } - if !errFlag { - go filtrateNFT(address, &nft, api) - } + _ = errFlag + // 删除非本人NFT + //if !errFlag { + // go filtrateNFT(address, &nft, api) + //} // get item details temp := make(map[string]struct{}) for _, v := range nft { @@ -496,7 +504,7 @@ func RefreshUserData(address string) (err error) { dealList = contractDefault } else if address != common.HexToAddress("0").String() { dealList = slice.DiffSlice[string](contractDefault, user.ContractIDs) - go updateAllCollection(address, dealList, false) // update all collection + updateAllCollection(address, dealList, false, true) // update all collection } if len(user.ContractIDs) != len(user.Counts) { diff --git a/internal/app/service/contract.go b/internal/app/service/contract.go index 340aebb..378c82f 100644 --- a/internal/app/service/contract.go +++ b/internal/app/service/contract.go @@ -28,7 +28,7 @@ func ItemFiltrateAndDown(contractMap map[string]struct{}, api config.APIConfig) for key, _ := range contractMap { addressList = append(addressList, key) } - if err = global.DB.Model(&model.Contract{}).Select("contract_address").Where("contract_address IN ? AND Status = 2", addressList).Find(&addressExist).Error; err != nil { + if err = global.DB.Model(&model.Contract{}).Select("contract_address").Where("contract_address IN ? AND Status = 2 AND chain = ?", addressList, api.Chain).Find(&addressExist).Error; err != nil { return err } // 待处理 slice diff --git a/main.go b/main.go index 4219f2b..183e500 100644 --- a/main.go +++ b/main.go @@ -16,9 +16,10 @@ func main() { zap.ReplaceGlobals(global.LOG) // 初始化数据库 initialize.InitCommonDB() - // 初始化缓存 - initialize.InitCache() // 初始化默认合约 initialize.InitNFTContract() + // 初始化链名称 + initialize.InitChainName() + core.RunWindowsServer() } diff --git a/pkg/cache/bigcache.go b/pkg/cache/bigcache.go deleted file mode 100644 index 1287595..0000000 --- a/pkg/cache/bigcache.go +++ /dev/null @@ -1,65 +0,0 @@ -package cache - -import ( - "context" - "github.com/allegro/bigcache/v3" - "github.com/chenyahui/gin-cache/persist" - "go.uber.org/zap" - "time" -) - -// BigCacheStore local memory cache store -type BigCacheStore struct { - Cache *bigcache.BigCache -} - -// NewBigCacheStore allocate a local memory store with default expiration -func NewBigCacheStore(defaultExpiration time.Duration, logger *zap.Logger) *BigCacheStore { - config := bigcache.Config{ - Shards: 1024, - LifeWindow: defaultExpiration, - CleanWindow: 3 * time.Second, - MaxEntriesInWindow: 10000, - MaxEntrySize: 1500, - StatsEnabled: false, - Verbose: true, - HardMaxCacheSize: 256, // memory limit, value in MB - Logger: NewZapLogger(logger), - } - cache, err := bigcache.New(context.Background(), config) - if err != nil { - panic(err) - } - return &BigCacheStore{ - Cache: cache, - } -} - -// Set put key value pair to memory store, and expire after expireDuration -func (c *BigCacheStore) Set(key string, value interface{}, expireDuration time.Duration) error { - _ = expireDuration - payload, err := persist.Serialize(value) - if err != nil { - return err - } - return c.Cache.Set(key, payload) -} - -// Delete remove key in memory store, do nothing if key doesn't exist -func (c *BigCacheStore) Delete(key string) error { - return c.Cache.Delete(key) -} - -// Get get key in memory store, if key doesn't exist, return ErrCacheMiss -func (c *BigCacheStore) Get(key string, value interface{}) error { - payload, err := c.Cache.Get(key) - - if err == bigcache.ErrEntryNotFound { - return persist.ErrCacheMiss - } - - if err != nil { - return err - } - return persist.Deserialize(payload, value) -} diff --git a/pkg/cache/logger.go b/pkg/cache/logger.go deleted file mode 100644 index ebf53c6..0000000 --- a/pkg/cache/logger.go +++ /dev/null @@ -1,18 +0,0 @@ -package cache - -import ( - "fmt" - "go.uber.org/zap" -) - -type ZapLogger struct { - Zap *zap.Logger -} - -func NewZapLogger(zap *zap.Logger) *ZapLogger { - return &ZapLogger{zap} -} - -func (c *ZapLogger) Printf(format string, v ...interface{}) { - c.Zap.Log(2, fmt.Sprintf(format, v...)) -}