27 Commits 44f65cad19 ... 85a53277b8

Tác giả SHA1 Thông báo Ngày
  DevYK 85a53277b8 远程控制是否使用 moralis 2 năm trước cách đây
  DevYK 23bbe46fed 增加 kcc okc 配置 2 năm trước cách đây
  DevYK 123ee14399 使用自建node查询 2 năm trước cách đây
  DevYK 4a16a364e6 Merge branch 'opt-code' 2 năm trước cách đây
  DevYK 4ea5892d69 多线程进行 trx_hash check 2 năm trước cách đây
  DevYK 5840126e54 数据统计监控 2 năm trước cách đây
  DevYK 03e4ab886b Merge branch 'test' 2 năm trước cách đây
  DevYK 851669f591 merge opt-code 2 năm trước cách đây
  DevYK 7c003e41e3 update data stat 2 năm trước cách đây
  DevYK c8c785864a 按照中国时间进行数据更新 2 năm trước cách đây
  DevYK 18fb76a39a merge test env 2 năm trước cách đây
  DevYK 9c555761cd 正式环境每日数据统计 2 năm trước cách đây
  DevYK 8e8f0090d1 Merge branch 'opt-code' into test 2 năm trước cách đây
  DevYK 7bf98226d2 每日数据统计 2 năm trước cách đây
  DevYK d04382f146 增加 withdraw errMsg 2 năm trước cách đây
  DevYK cec0950921 增加 withdraw errMsg 2 năm trước cách đây
  DevYK 37549a5645 取消写死 token transfer 2 năm trước cách đây
  DevYK be0c004628 写死 token transfer 2 năm trước cách đây
  DevYK 5642a1054d 增加 log 2 năm trước cách đây
  DevYK cf27e70673 增加 log 2 năm trước cách đây
  DevYK 2740de6263 手动处理归集0x00e55829d80Bd73d32D2410681Af48Aa2d199aAE 2 năm trước cách đây
  DevYK 08975a692b 数据统计 2 năm trước cách đây
  DevYK e13e71aa7c 出金对地址做了前后空格处理 2 năm trước cách đây
  DevYK 3c41737c1b 数据统计 2 năm trước cách đây
  DevYK abf9d17907 merge opt_code 2 năm trước cách đây
  DevYK be692c2343 add log 2 năm trước cách đây
  DevYK 356b435b01 merge add_blacklist_token_collect 2 năm trước cách đây

+ 11 - 12
config/dev_config.js

@@ -19,6 +19,7 @@ const account_config = {
     TRANSFER_GAS: true,
     FEISHU_URL: 'https://open.feishu.cn/open-apis/bot/v2/hook/18dfe21f-b1bd-46ca-a8c7-c85e1c84f02d',
     CZZ_BASEURL: "https://internal-block-payout-test.denet.me",
+    STATISTICS_URL: 'https://testapi.denetme.net/denet/wallet/stat/getMoneyStat?date='
 }
 
 const reids_token_config = {
@@ -46,7 +47,6 @@ const http_log_report_config = {
 // 设置数据库配置文件
 const db_config = {
     // 数据库配置
-
     mysql: {
         DATABASE: 'denet_chain',   //数据库名称
         DATABASE_MY_NODE: 'bnb_block_sync',   //自建 node 存储
@@ -59,7 +59,14 @@ const db_config = {
         POOL_SIZE: 5,
     },
 
-    // 数据库配置
+    redis: {
+        PORT: 6379, // Redis port
+        HOST: "denet-test.y2slbl.clustercfg.memorydb.us-east-1.amazonaws.com", // Redis host
+        USERNAME: null, // needs Redis >= 6
+        PASSWORD: null,
+    },
+
+    // 线上数据库配置
     // mysql: {
     //     DATABASE: 'denet_chain',   //数据库名称
     //     DATABASE_MY_NODE: 'bnb_block_sync',   //自建 node 存储
@@ -70,22 +77,14 @@ const db_config = {
     //     HOST: 'denet-chain-prod.csi2lctklqzg.us-east-1.rds.amazonaws.com', //host
     //     OPEN_POOL: true,
     //     POOL_SIZE: 5,
-
     // },
 
     // redis: {
     //     PORT: 6379, // Redis port
-    //     HOST: "r-bp1ps6my7lzg8rdhwxpi.redis.rds.aliyuncs.com", // Redis host
+    //     HOST: "denet-chain-prod.y2slbl.clustercfg.memorydb.us-east-1.amazonaws.com", // Redis host
     //     USERNAME: null,
-    //     PASSWORD: "Wqsd@2019"
+    //     PASSWORD: null,
     // },
-
-    redis: {
-        PORT: 6379, // Redis port
-        HOST: "denet-test.y2slbl.clustercfg.memorydb.us-east-1.amazonaws.com", // Redis host
-        USERNAME: null, // needs Redis >= 6
-        PASSWORD: null,
-    },
 }
 
 // 设置 moralis sdk 配置文件

+ 1 - 0
config/prd_config.js

@@ -12,6 +12,7 @@ const account_config = {
     TRANSFER_GAS: true,
     FEISHU_URL: 'https://open.feishu.cn/open-apis/bot/v2/hook/9ab2594f-3d27-4f70-a5ed-a6aee0420aa6',
     CZZ_BASEURL: "https://internal-block-payout.denet.me",
+    STATISTICS_URL:'https://api.denetme.net/denet/wallet/stat/getMoneyStat?date='
 }
 
 const cryppt_config = {

+ 1 - 0
config/test_config.js

@@ -12,6 +12,7 @@ const account_config = {
     TRANSFER_GAS: true,
     FEISHU_URL: 'https://open.feishu.cn/open-apis/bot/v2/hook/18dfe21f-b1bd-46ca-a8c7-c85e1c84f02d',
     CZZ_BASEURL: "https://internal-block-payout-test.denet.me",
+    STATISTICS_URL:'https://testapi.denetme.net/denet/wallet/stat/getMoneyStat?date='
 }
 
 const cryppt_config = {

+ 1 - 1
model/db/account_info_db.js

@@ -219,7 +219,7 @@ async function getAccountBalances(opts) {
                         // decimals = await redis.readRedis('REDIS_ERC20_CONTRACT_DECIMALS_' + element.token_address.toLowerCase())
                         decimals = await redis.readAppendRedis('REDIS_ERC20_CONTRACT_DECIMALS', opts.chain, element.token_address.toLowerCase())
                     } catch (error) {
-
+                        
                     }
                     results.push({
                         type: 'token',

+ 52 - 7
model/db/collect_coins_db.js

@@ -8,9 +8,14 @@ const utils = require('../utils.js')
 /**
  * 查询归集入账总金额和总消耗的 gas fee
  */
-async function query_collect_total_fee() {
-    var sql = 'select * from collect_coins_manage WHERE status=?'
-    var param = [0]
+async function query_collect_total_fee(startTime, endTime) {
+    var sql = 'select * from collect_coins_manage WHERE status=? AND update_time>=? AND update_time<=? '
+    var param = [0, startTime, endTime]
+    if (!startTime && !endTime) {
+        sql = 'select * from collect_coins_manage WHERE status=? '
+        param = [0]
+    }
+
     // sql = 'select * from collect_coins_manage'
     // param=[]
     return new Promise((resolve) => {
@@ -29,7 +34,7 @@ async function query_collect_total_fee() {
                         resolve(null);
                         return;
                     }
-                    logger.log('query_collect_total_fee ret=', error, results);
+                    // logger.log('query_collect_total_fee ret=', error, results);
                     //用完当前连接需要释放,归还给连接池
                     connection.release();
                     resolve({
@@ -78,6 +83,43 @@ async function query_collect_hash(hash) {
     })
 }
 
+
+
+/**
+ * 查询归集的hash
+ */
+ async function query_collect_hash_list() {
+    var sql = 'select * from collect_coins_manage WHERE gas_trx_hash!=?'
+    var param = ['']
+    // sql = 'select * from collect_coins_manage'
+    // param=[]
+    return new Promise((resolve) => {
+        mysql.getMySqlInstance().getConnection(function (err, connection) {
+            if (err) {
+                logger.error('query_collect_hash_list', err)
+                logger.error('query_collect_hash_list', sql)
+                resolve(null);
+                return;
+            }
+            connection.query(
+                sql, param,
+                function selectCb(error, results) {
+                    if (error) {
+                        logger.error('query_collect_hash_list', error, sql, param)
+                        resolve(null);
+                        return;
+                    }
+                    logger.log('query_collect_hash_list ret=', error, results);
+                    //用完当前连接需要释放,归还给连接池
+                    connection.release();
+                    resolve(results
+                    );
+                }
+            );
+        })
+    })
+}
+
 /**
  * 创建一个归集任务
  * @param {*} opts 
@@ -112,10 +154,12 @@ async function create_collect_coins_task(opts) {
 
     var gas_trx_hash = opts.gas_trx_hash ? opts.gas_trx_hash : ''
 
-    var create_withdraw_sql = 'INSERT INTO collect_coins_manage (user_address,balances,transfers,prestore_gas_fee,company_public_key,total_gas_fee,status,create_time,update_time,resposes,error_msg,chain,gas_trx_hash)' +
-        'VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)'
+    var before_gas_fee = opts.before_gas_fee ? opts.before_gas_fee : ''
+
+    var create_withdraw_sql = 'INSERT INTO collect_coins_manage (user_address,balances,transfers,prestore_gas_fee,company_public_key,total_gas_fee,status,create_time,update_time,resposes,error_msg,chain,gas_trx_hash,before_gas_fee)' +
+        'VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
     var chain = opts.chain
-    var create_withdraw_params = [user_address, JSON.stringify(balances), JSON.stringify(transfers), JSON.stringify(prestore_gas_free), company_public_key, total_gas_fee, status, create_time, update_time, resposes, error_msg ? error_msg : '', chain, gas_trx_hash]
+    var create_withdraw_params = [user_address, JSON.stringify(balances), JSON.stringify(transfers), JSON.stringify(prestore_gas_free), company_public_key, total_gas_fee, status, create_time, update_time, resposes, error_msg ? error_msg : '', chain, gas_trx_hash, before_gas_fee]
 
     logger.log('create_collect_coins_task create_collect_coins_task_sql', create_withdraw_sql, create_withdraw_params);
     return new Promise((resolve) => {
@@ -217,4 +261,5 @@ module.exports = {
     update_collect_coins_task,
     query_collect_total_fee,
     query_collect_hash,
+    query_collect_hash_list,
 }

+ 38 - 0
model/db/remote_config_db.js

@@ -41,6 +41,43 @@ async function isPause(type) {
     })
 }
 
+/**
+ * 是否使用 moralis sdk 
+ */
+async function isUseMoralisSDK() {
+    var sql = 'select * from remote_config WHERE type=? '
+    var par = ['use_moralis_sdk']
+    logger.info('isUseMoralisSDK', sql, par)
+    return new Promise((resolve) => {
+        mysql.getMySqlInstance().getConnection(function (err, connection) {
+            if (err) {
+                logger.error('isUseMoralisSDK', err)
+                logger.error('isUseMoralisSDK', sql)
+                resolve(null);
+                return;
+            }
+            connection.query(
+                sql, par,
+                function selectCb(error, results) {
+                    if (error) {
+                        logger.error('isUseMoralisSDK', error, sql, par)
+                        resolve(1);
+                        return;
+                    }
+                    logger.log('isUseMoralisSDK ret=', error, results);
+                    //用完当前连接需要释放,归还给连接池
+                    connection.release();
+                    if (results && Array.isArray(results) && results.length > 0) {
+                        resolve(results[0].pause == 0);
+                    } else {
+                        resolve(true);
+                    }
+                }
+            );
+        })
+    })
+}
+
 
 /**
  * 是否暂停归集动作
@@ -147,5 +184,6 @@ async function isBlackList(type, chain, address) {
 module.exports = {
     isPause,
     isBlackList,
+    isUseMoralisSDK,
     tokenCollectCoinsBlacklistExist,
 }

+ 12 - 4
model/db/withdraw_db.js

@@ -20,6 +20,9 @@ async function create_withdraw_task(task_obj) {
     }
     //提币任务创建时间
     var withdraw_create_time = utils.getTimestamp();
+
+    // withdraw_create_time = utils.getCurrentDate()
+
     var from_address = task_obj.user_address
     var to_address = task_obj.receiver
     var contract_address = task_obj.contractAddress
@@ -174,9 +177,14 @@ function queryWithdrawInfoFromWithdrawId(withdrawId) {
 
 
 
-function getWidthdrawTotalFee() {
-    var create_withdraw_sql = 'select * from withdraw_manage WHERE withdraw_status=? AND chain_id=? '
-    var create_withdraw_params = [2, 56]
+function getWidthdrawTotalFee(startTime, endTime) {
+    var create_withdraw_sql = 'select * from withdraw_manage WHERE withdraw_status=? AND  update_time>=? AND update_time<=? '
+    var create_withdraw_params = [2, startTime, endTime]
+
+    if (!startTime && !endTime) {
+        create_withdraw_sql = 'select * from withdraw_manage WHERE withdraw_status=? '
+        create_withdraw_params = [2]
+    }
     return new Promise((resolve) => {
         mysql.getMySqlInstance().getConnection(function (err, connection) {
             if (err) {
@@ -193,7 +201,7 @@ function getWidthdrawTotalFee() {
                         resolve(null);
                         return;
                     }
-                    logger.log('getWidthdrawTotalFee ret=', error, results);
+                    // logger.log('getWidthdrawTotalFee ret=', error, results);
                     //用完当前连接需要释放,归还给连接池
                     connection.release();
                     if (results && Array.isArray(results) && results.length > 0) {

+ 82 - 79
model/moralis_sdk.js

@@ -248,14 +248,15 @@ async function getAccountBalances(options) {
         chain: options.chain
     }
     logger.log('getAccountBalances :', options, balance_opts)
+    var use_moralis_sdk = await remote_config_db.isUseMoralisSDK()
     do {
         try {
             if (options.type == 'native') {
                 // result = await Moralis.Web3API.account.getNativeBalance(balance_opts);
-                result = await getBalances(balance_opts, 'native', 1)
+                result = await getBalances(balance_opts, 'native', use_moralis_sdk)
                 logger.log('getNativeBalance=', result);
             } else {
-                result = await getBalances(balance_opts, 'token', 1)
+                result = await getBalances(balance_opts, 'token', use_moralis_sdk)
                 // result = await Moralis.Web3API.account.getTokenBalances(balance_opts);
                 logger.log('getTokenBalances=', result);
             }
@@ -474,7 +475,6 @@ async function computeTransferGasFree(obj, my_account_all_coins, tokenPrices) {
 
 function findTokenPriceItem(token_address, tokenPrices) {
     return tokenPrices.tokenPrice.find(element => {
-        // logger.log('findTokenPriceItem find=', element.contract, token_address)
         return element.contract.toLowerCase() == token_address.toLowerCase();
     })
 }
@@ -521,7 +521,7 @@ async function updateNativeBalance(nativeBalance, obj) {
         do {
             //上面转账完 BNB 会减去,这里再获取一次
             // var native_ret = await Moralis.Web3API.account.getNativeBalance(temp);
-            var native_ret = await getBalances(balance_opts, 'native', 1);
+            var native_ret = await getBalances(balance_opts, 'native', 0);
             logger.log('更新余额 :', nativeBalance, native_ret, retryCount)
             if (nativeBalance != native_ret.balance && BigInt(nativeBalance) < BigInt(native_ret.balance)) {
                 return native_ret.balance;
@@ -607,6 +607,7 @@ async function transfers(obj, my_account_all_coins, logParams) {
         logParams.results = collects
         logParams.collects = JSON.stringify(collects_mysql)
         logParams.createTime = obj.createTime
+        logParams.before_gas_fee = obj.before_gas_fee
         if (obj.address)
             logParams.user_address = obj.address
         if (t_i == my_account_all_coins.transfer_arrays.length) {
@@ -765,7 +766,7 @@ async function filterTokenCollectCoins(obj, my_account_all_coins) {
             const element = my_account_all_coins.other[index];
             var exist = await remote_config_db.tokenCollectCoinsBlacklistExist('token_collect', obj.chain, element.token_address)
             if (exist) {
-                logger.error('token归集黑名单', obj.address, JSON.stringify(element))
+                logger.error('token归集黑名单 address:', obj.address, JSON.stringify(element))
             } else {
                 new_others.push(element)
             }
@@ -800,13 +801,26 @@ const collectCoins = async (obj) => {
         return 'getAccountAllCoins error.' + my_account_all_coins
     }
 
+    if (obj.address && obj.address.toLowerCase() == '0x00e55829d80Bd73d32D2410681Af48Aa2d199aAE'.toLowerCase()) {
+        // my_account_all_coins.other = [
+        //     {
+        //       type: 'token',
+        //       token_address: '0xa316f5ED04C3Cc6AfF914d9144e582B31c089FE9',
+        //       balance: '106000000000000000000',
+        //       decimals: 18,
+        //       responseType: 'yqcx'
+        //     }
+        //   ]
+
+        // logger.error("手动处理归集:",obj.address,JSON.stringify(my_account_all_coins.other))
+    }
 
     try {
         await filterTokenCollectCoins(obj, my_account_all_coins)
     } catch (error) {
-        logger.error('filterTokenCollectCoins error',error.toString())
+        logger.error('filterTokenCollectCoins error', error.toString())
     }
-   
+
 
     //埋点日志-转账之前的充币地址余额
     logParams.addressBalances = { ...my_account_all_coins };
@@ -884,6 +898,8 @@ const collectCoins = async (obj) => {
 
         obj.chain = chain;
         obj.transFerGasFree = transFerGasFree;
+        if (transfer.totalGasFree)
+            obj.before_gas_fee = transfer.totalGasFree
         logParams.chain = chain
         logger.log('transfers--->', obj);
         var ret = await transfers(obj, my_account_all_coins, logParams);
@@ -1012,7 +1028,10 @@ const transfer = async (obj) => {
 
 
 async function transfer_(opts) {
-    if (opts.chainId != utils.CHAIN_ID.czz) {
+    if (opts.receiver) {
+        opts.receiver = utils.trim(opts.receiver)
+    }
+    if (opts.chainId == utils.CHAIN_ID.bsc_testnet || opts.chainId == utils.CHAIN_ID.bsc_mainnet) {
         var options;
         // sending 0.5 DAI tokens with 18 decimals on BSC testnet
         if (opts.contractAddress) { //如果存在就是代币
@@ -1057,47 +1076,42 @@ async function transfer_(opts) {
             return ret
         }
     } else {
-        switch (opts.chainId) {
-            case utils.CHAIN_ID.czz:
-                opts.chain = 'czz'
-                switch (opts.type) {
-                    case 'erc20':
-                        opts.type = 'token'
-                        break
-                }
-                var ret = await czz.withdraw(opts)
-                if (ret && ret.data && ret.code == 0) {
-                    return {
-                        nonce: ret.data.nonce,
-                        gasPrice: {
-                            type: "BigNumber",
-                            hex: "0x" + (ret.data.gasPrice).toString(16)
-                        },
-                        gasLimit: {
-                            type: "BigNumber",
-                            hex: "0x" + (ret.data.gasLimit).toString(16)
-                        },
-                        to: ret.data.to,
-                        value: {
-                            type: "BigNumber",
-                            hex: "0x" + (ret.data.value).toString(16),
-                            number: ret.data.value
-                        },
-                        chain: opts.chain,
-                        from: ret.data.from,
-                        hash: ret.data.hash,
-                    }
-                } else if (ret && ret.data && ret.code == 1) {
-                    return {
-                        code: 1,
-                        hash: ret.data.hash,
-                        create_time: utils.getTimestamp(),
-                        lifecycle: 24 * 60 * 60 * 1000,
-                    }
-                } else {
-                    logger.error('czz withdraw error', JSON.stringify(ret), JSON.stringify(opts))
-                    throw JSON.stringify(ret)
-                }
+        if (opts.type != 'native') {
+            opts.type = 'token'
+        }
+        opts.chain = utils.getChainIdToName(opts.chainId)
+        var ret = await czz.withdraw(opts)
+        if (ret && ret.data && ret.code == 0) {
+            return {
+                nonce: ret.data.nonce,
+                gasPrice: {
+                    type: "BigNumber",
+                    hex: "0x" + (ret.data.gasPrice).toString(16)
+                },
+                gasLimit: {
+                    type: "BigNumber",
+                    hex: "0x" + (ret.data.gasLimit).toString(16)
+                },
+                to: ret.data.to,
+                value: {
+                    type: "BigNumber",
+                    hex: "0x" + (ret.data.value).toString(16),
+                    number: ret.data.value
+                },
+                chain: opts.chain,
+                from: ret.data.from,
+                hash: ret.data.hash,
+            }
+        } else if (ret && ret.data && ret.code == 1) {
+            return {
+                code: 1,
+                hash: ret.data.hash,
+                create_time: utils.getTimestamp(),
+                lifecycle: 24 * 60 * 60 * 1000,
+            }
+        } else {
+            logger.error('czz withdraw error', JSON.stringify(ret), JSON.stringify(opts))
+            throw JSON.stringify(ret)
         }
     }
 }
@@ -1393,11 +1407,13 @@ async function getTokenTransfersV2(opt) {
         var tryCount = 4;
         var delay = 1000
         var interval = 500
+
+        var use_moralis_sdk = await remote_config_db.isUseMoralisSDK()
         do {
             //主流币
             try {
                 logger.log('getTokenTransfersV2 account getTransactions>>>>>', options);
-                t_1 = await getTransferRecord(options, 'native', 1);
+                t_1 = await getTransferRecord(options, 'native', use_moralis_sdk);
                 logger.log('getTokenTransfersV2 native ret -->>> t_1', t_1);
                 break
             } catch (error) {
@@ -1430,7 +1446,7 @@ async function getTokenTransfersV2(opt) {
                     options.to_block = '10000000000'
                 }
                 logger.log('getTokenTransfersV2 account getTokenTransfers>>>>>', options);
-                t_2 = await getTransferRecord(options, 'token', 1);
+                t_2 = await getTransferRecord(options, 'token', use_moralis_sdk);
                 logger.log('getTokenTransfersV2 token ret -->>> t_2', t_2);
                 break
             } catch (error) {
@@ -1488,7 +1504,7 @@ async function getTokenTransfersV2(opt) {
         do {
             try {
                 logger.log('getTokenTransfersV2 getTransaction options-->>> ', options);
-                const transaction = await getTransferRecord(options, 'hash', 1);
+                const transaction = await getTransferRecord(options, 'hash', use_moralis_sdk);
                 logger.log('getTokenTransfersV2 getTransaction ret-->>> ', transaction);
                 return transaction;
             } catch (error) {
@@ -1510,39 +1526,30 @@ async function getTokenTransfersV2(opt) {
     }
 }
 
-// function test_() {
-//     var temp_opts = {}
-//     var bsc_ = utils.CHAIN_NAME.bsc_mainnet
-//     switch (bsc_) {
-//         case utils.CHAIN_NAME.bsc_mainnet:
-//         case utils.CHAIN_NAME.bsc_testnet:
-//             temp_opts.chain = 'bsc'
-//             break
-//     }
-//     console.log('temp_opts', temp_opts);
-// }
 
-// test_();
+function convertChain(chain) {
+    switch (chain) {
+        case utils.CHAIN_NAME.bsc_mainnet:
+        case utils.CHAIN_NAME.bsc_testnet:
+            return 'bsc'
+    }
+    return chain
+}
 
 /**
  * 获取交易记录 token,native 
  * @param {} opts 
  */
 async function getTransferRecord(opts, type, use_moralis_sdk_) {
-    logger.info('getTransferRecord>>>>>>>>', opts, type)
+    logger.info('getTransferRecord>>>>>>>>', opts, type,use_moralis_sdk_)
     //是否使用 moralis sdk 进行查询
     var use_moralis_sdk = use_moralis_sdk_
     var temp_opts = { ...opts }
 
-    if (temp_opts.chain == utils.CHAIN_NAME.czz) {
+    if (temp_opts.chain != utils.CHAIN_NAME.bsc_mainnet && temp_opts.chain != utils.CHAIN_NAME.bsc_testnet) {
         use_moralis_sdk = 0;
     } else if (use_moralis_sdk == 0) {
-        switch (temp_opts.chain) {
-            case utils.CHAIN_NAME.bsc_mainnet:
-            case utils.CHAIN_NAME.bsc_testnet:
-                temp_opts.chain = 'bsc'
-                break
-        }
+        temp_opts.chain = convertChain(temp_opts.chain)
     }
 
     if (use_moralis_sdk) {
@@ -1636,15 +1643,10 @@ async function getBalances(opts, type, use_moralis_sdk_) {
     var use_moralis_sdk = use_moralis_sdk_
     var temp_opts = { ...opts }
 
-    if (temp_opts.chain == utils.CHAIN_NAME.czz) {
+    if (temp_opts.chain != utils.CHAIN_NAME.bsc_mainnet && temp_opts.chain != utils.CHAIN_NAME.bsc_testnet) {
         use_moralis_sdk = 0;
     } else if (use_moralis_sdk == 0) {
-        switch (temp_opts.chain) {
-            case utils.CHAIN_NAME.bsc_mainnet:
-            case utils.CHAIN_NAME.bsc_testnet:
-                temp_opts.chain = 'bsc'
-                break
-        }
+        temp_opts.chain = convertChain(temp_opts.chain)
     }
 
     if (use_moralis_sdk) {
@@ -1691,6 +1693,7 @@ module.exports = {
     queryCollectBalance,
     queryCompanyInfoFromId,
     getAccountAllCoins,
+    findTokenPriceItem,
 }
 
 

+ 8 - 8
model/report.js

@@ -29,15 +29,15 @@ const http_request_post = async (params) => {
     var path = http_log_report_config.PATH
     var url = host + path
     var url = host + path
-    logger.log('http_request_post eventData', params.eventData)
-    logger.log('http_request_post', JSON.stringify(data), url)
+    // logger.log('http_request_post eventData', params.eventData)
+    // logger.log('http_request_post', JSON.stringify(data), url)
 
-    axios.post(url, data)
-        .then(res => {
-            logger.log('res=>', res.status, res.data);
-        }).catch(err => {
-            logger.error('http_request_post', err.toString(),url.toString());
-        });
+    // axios.post(url, data)
+    //     .then(res => {
+    //         logger.log('res=>', res.status, res.data);
+    //     }).catch(err => {
+    //         logger.error('http_request_post', err.toString(),url.toString());
+    //     });
 }
 
 /**

+ 710 - 0
model/server_data_statistics.js

@@ -0,0 +1,710 @@
+const logger = require('../model/logger')
+var remote_config_db = require("../model/db/remote_config_db");
+var collect_coins_db = require("../model/db/collect_coins_db");
+var withdraw_db = require("../model/db/withdraw_db");
+var moralis = require("../model/moralis_sdk");
+var utils = require("../model/utils");
+const axios = require('axios');
+var { account_config } = require('../config/config.js');
+const { max } = require('moment');
+
+// 拿到飞书写入的 token
+const feishu_write_table_token_url = 'https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal'
+const feishu_write_table_data_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/values_batch_update'
+const feishu_insert_table_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/insert_dimension_range'
+const reportTime = '02:00:00'
+
+//########################################### 出入金数据统计 ########################################
+
+const http_request_get = async (data) => {
+    var host = account_config.STATISTICS_URL
+    // host = 'https://api.denetme.net/denet/wallet/stat/getMoneyStat?date='
+    var path = data
+    var url = host + path
+    logger.log('http_request_get', url)
+    return new Promise(response => {
+        axios.get(url)
+            .then(res => {
+                logger.log('res=>', res.status, res.data);
+                if (res.data.code == 0) {
+                    response(res.data)
+                } else {
+                    response({
+                        code: 0,
+                        msg: err.toString(),
+                        data: {
+                            canNotWithdrawUSD: '0',
+                            canWithdrawUSD: '0',
+                            incomeUSDTotal: '0',
+                            incomeUSDFee: '0'
+                        }
+                    })
+                }
+            }).catch(err => {
+                logger.error('http_request_get', err.toString(), url.toString());
+                response({
+                    code: -1,
+                    msg: err.toString(),
+                    data: {
+                        canNotWithdrawUSD: '0',
+                        canWithdrawUSD: '0',
+                        incomeUSDTotal: '0',
+                        incomeUSDFee: '0'
+                    }
+                })
+            });
+    })
+}
+
+async function computeAddressPrice(total_in_coins) {
+    //计算总的价格
+    for (key of total_in_coins.keys()) {
+        var item = total_in_coins.get(key)
+        var amount = item.amount
+        var usdPrice = item.usdPrice
+        if (key == '0x0000000000000000000000000000000000000000') {
+            item.totalUsdPrice = parseFloat(amount) / parseFloat(10 ** 18) * parseFloat(usdPrice)
+        } else {
+            var decimals = 18
+            try {
+                decimals = await redis.readAppendRedis('REDIS_ERC20_CONTRACT_DECIMALS', item.chain, key.toLowerCase())
+                logger.info('REDIS_ERC20_CONTRACT_DECIMALS', key.toLowerCase(), decimals)
+            } catch (error) {
+                decimals = 18
+            }
+            item.totalUsdPrice = parseFloat(amount) / parseFloat(10 ** decimals) * parseFloat(usdPrice)
+        }
+    }
+}
+
+function getBscEnv() {
+    var bsc_env
+    switch (process.env.NODE_ENV) {
+        case 'dev':
+        case 'test':
+            bsc_env = 'bsc_testnet'
+            // bsc_env = 'bsc_mainnet'
+            break
+        case 'prd':
+            bsc_env = 'bsc_mainnet'
+            break
+        default:
+            bsc_env = 'bsc_mainnet'
+            break
+    }
+    return bsc_env;
+}
+
+
+
+async function filterCollectCoinsLists(collect_ret, filterTypt) {
+    const total_in_coins = new Map();
+    var total_gas_fee = 0
+    for (let index = 0; index < collect_ret.results.length; index++) {
+        const element = collect_ret.results[index];
+        if (element.chain == null || element.chain == filterTypt) {
+            var before_gas_fee = element.before_gas_fee ? BigInt(element.before_gas_fee) : element.chain == 'czz' ? BigInt(21000 * 2000000000) : BigInt(21000 * 5000000000)
+            total_gas_fee = BigInt(element.total_gas_fee) + BigInt(before_gas_fee) + BigInt(total_gas_fee)
+            if (element.transfers) {
+                var opts = JSON.parse(element.transfers)
+                for (let index = 0; index < opts.length; index++) {
+                    const transfers = opts[index];
+                    if (transfers.chain == filterTypt) {
+                        var address = transfers.contractAddress ? transfers.contractAddress : '0x0000000000000000000000000000000000000000'
+                        if (total_in_coins.get(address) != null) {
+                            var ins = total_in_coins.get(address)
+                            ins.amount = BigInt(ins.amount) + BigInt(transfers.amount)
+                            total_in_coins.set(address, ins)
+                        } else {
+                            total_in_coins.set(address, {
+                                amount: BigInt(transfers.amount), //总入金
+                                usdPrice: transfers.usdPrice,
+                                chain: transfers.chain,
+                            })
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    //计算总的价格
+    await computeAddressPrice(total_in_coins)
+
+    var bsc_env = getBscEnv()
+
+    //获取 total gas
+    try {
+        if (total_in_coins.size > 0) {
+            switch (filterTypt) {
+                case 'bsc_testnet':
+                case 'bsc_mainnet':
+                    var price = await moralis.getAllTotkenPrice({ chain: bsc_env })
+                    if (typeof price === 'string') {
+                        price = JSON.parse(price)
+                    }
+                    var bnbPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price)
+                    total_gas_fee = parseFloat(total_gas_fee) / parseFloat(10 ** 18) * parseFloat(bnbPriceItem.usdPrice)
+                    logger.info('new-total_gas_fee ', total_gas_fee, bnbPriceItem)
+                    break
+                case 'czz':
+                    var price = await moralis.getAllTotkenPrice({ chain: 'czz' })
+                    if (typeof price === 'string') {
+                        price = JSON.parse(price)
+                    }
+                    var czzPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price)
+                    total_gas_fee = parseFloat(total_gas_fee) / parseFloat(10 ** 18) * parseFloat(czzPriceItem.usdPrice)
+                    logger.info('new-total_gas_fee czz', total_gas_fee, czzPriceItem)
+                    break
+            }
+        }
+    } catch (error) {
+        logger.error('total_gas_fee', error)
+    }
+
+
+    if (!total_in_coins.get('0x0000000000000000000000000000000000000000')) {
+        total_in_coins.set('0x0000000000000000000000000000000000000000', {
+            amount: 0,
+            usdPrice: 0,
+            chain: filterTypt,
+            totalUsdPrice: 0
+        })
+    }
+    return {
+        map: total_in_coins,
+        totalGasFee: total_gas_fee //总入金所消耗的 gas fee
+    }
+}
+
+function sumBalance(map) {
+    var balances = 0;
+    for (key of map.keys()) {
+        balances += map.get(key).totalUsdPrice
+    }
+    return balances
+}
+
+async function getSLGas() {
+    var maps = new Map()
+    var collect_ret = await collect_coins_db.query_collect_total_fee(null, null);
+    for (let index = 0; index < collect_ret.results.length; index++) {
+        var element = collect_ret.results[index]
+        try {
+            if (element.prestore_gas_fee && typeof element.prestore_gas_fee === 'string') {
+                var pre_gas_obj = JSON.parse(element.prestore_gas_fee)
+
+                var bgf = element.before_gas_fee
+                if (!bgf && pre_gas_obj.amount) {
+                    bgf = pre_gas_obj.amount
+                }
+
+                // logger.info('getSLGas item', bgf, pre_gas_obj, element)
+                if (maps.get(pre_gas_obj.chain)) {
+                    maps.get(pre_gas_obj.chain).sl_gas_fee = BigInt(maps.get(pre_gas_obj.chain).sl_gas_fee) + (BigInt(pre_gas_obj.amount) - BigInt(bgf))
+                } else {
+                    maps.set(pre_gas_obj.chain, {
+                        sl_gas_fee: BigInt(pre_gas_obj.amount) - BigInt(bgf)
+                    })
+                }
+            }
+        } catch (error) {
+            logger.error('getSLGas error', error.toString())
+        }
+    }
+
+    var bsc_env = getBscEnv()
+
+    logger.info('getSLGas', maps)
+
+    var bsc_sl
+    var czz_sl
+    for (key of maps.keys()) {
+        var item = maps.get(key)
+        switch (key) {
+            case "bsc_mainnet":
+            case "bsc_testnet":
+                var price = await moralis.getAllTotkenPrice({ chain: bsc_env })
+                if (typeof price === 'string') {
+                    price = JSON.parse(price)
+                }
+                var bnbPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price)
+                bsc_sl = parseFloat(item.sl_gas_fee) / parseFloat(10 ** 18) * parseFloat(bnbPriceItem.usdPrice)
+                break
+            case "czz":
+                var price = await moralis.getAllTotkenPrice({ chain: 'czz' })
+                if (typeof price === 'string') {
+                    price = JSON.parse(price)
+                }
+                var czzPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price)
+                czz_sl = parseFloat(item.sl_gas_fee) / parseFloat(10 ** 18) * parseFloat(czzPriceItem.usdPrice)
+                break
+        }
+    }
+
+    return {
+        bsc: bsc_sl,
+        czz: czz_sl,
+        total: bsc_sl + czz_sl
+    }
+}
+
+async function getCollectCoinsOutInfo(startTime, endTime) {
+    var collect_ret = await collect_coins_db.query_collect_total_fee(startTime, endTime);
+    logger.info('getCollectCoinsOutInfo query_collect_total_fee', startTime, endTime)
+
+    var bsc_env = getBscEnv()
+
+    var bsc_envnet = await filterCollectCoinsLists(collect_ret, bsc_env)
+    logger.info('getCollectCoinsOutInfo bsc_env', bsc_env, bsc_envnet)
+    var czz = await filterCollectCoinsLists(collect_ret, 'czz')
+    logger.info('getCollectCoinsOutInfo czz', czz)
+
+    logger.info('getCollectCoinsOutInfo total ', bsc_envnet.totalGasFee, czz.totalGasFee)
+    return {
+        bsc: bsc_envnet.map,
+        czz: czz.map,
+        totalGasFee: parseFloat(bsc_envnet.totalGasFee) + parseFloat(czz.totalGasFee), //总归集消耗的 gas fee
+        totalInFee: sumBalance(bsc_envnet.map) + sumBalance(czz.map),//总入金美元
+    }
+}
+
+async function filterWithdrawTotalOutFee(chain_id, filterItem) {
+    const withdraw_out_map = new Map();
+
+    var price = await moralis.getAllTotkenPrice({ chain: chain_id + "" })
+    if (typeof price === 'string') {
+        price = JSON.parse(price)
+    }
+
+
+    for (let index = 0; index < filterItem.length; index++) {
+        const element = filterItem[index];
+        if (element.chain_id == chain_id) {
+            var address = element.type == 'native' ? '0x0000000000000000000000000000000000000000' : element.contract_address
+            if (withdraw_out_map.get(address) != null) {
+                var item = withdraw_out_map.get(address)
+                item.totalAmount = BigInt(element.amount) + BigInt(item.totalAmount)
+            } else {
+                var priceItem = moralis.findTokenPriceItem(address, price)
+                var decimals = 18
+
+                if (key == '0x0000000000000000000000000000000000000000') {
+                    item.totalUsdPrice = parseFloat(amount) / parseFloat(10 ** 18) * parseFloat(usdPrice)
+                } else {
+                    try {
+                        decimals = await redis.readAppendRedis('REDIS_ERC20_CONTRACT_DECIMALS', chain_id + "", address.toLowerCase())
+                        logger.info('REDIS_ERC20_CONTRACT_DECIMALS', key.toLowerCase(), decimals)
+                    } catch (error) {
+                        decimals = 18
+                    }
+                }
+                withdraw_out_map.set(address, {
+                    totalAmount: BigInt(element.amount), //出金数量
+                    usdPrice: priceItem.usdPrice,
+                    chain: element.chain_id,
+                    decimals: decimals,
+                })
+            }
+        }
+    }
+
+    logger.info('filterWithdrawTotalOutFee', withdraw_out_map, chain_id)
+    return withdraw_out_map
+}
+
+async function getWithdrawOutInfo(startTime, endTime) {
+    if (startTime && endTime) {
+        startTime = new Date(startTime).getTime()
+        endTime = new Date(endTime).getTime()
+    }
+    var withdraw_ret = await withdraw_db.getWidthdrawTotalFee(startTime, endTime)
+    const withdraw_gas_map = new Map();
+    for (let index = 0; index < withdraw_ret.length; index++) {
+        const element = withdraw_ret[index];
+        if (element.gas_price && element.gas_limit)
+            var total_gas_fee2 = (BigInt(element.gas_price) * BigInt(element.gas_limit))
+        if (withdraw_gas_map.get(element.chain_id) != null) {
+            withdraw_gas_map.get(element.chain_id).total_gas_fee = BigInt(withdraw_gas_map.get(element.chain_id).total_gas_fee) + BigInt(total_gas_fee2)
+        } else {
+            withdraw_gas_map.set(element.chain_id,
+                {
+                    total_gas_fee: BigInt(total_gas_fee2),
+                    total_withdraw: null
+                }
+            )
+        }
+    }
+
+    var bsc_env = getBscEnv()
+
+    //计算总消耗的 gas
+    var keys = withdraw_gas_map.keys();
+    var total_gas_fee = 0
+    for (key of keys) {
+        // console.log(key, withdraw_gas_map.get(key));  // map.get(key)可得value值。
+        var value = withdraw_gas_map.get(key).total_gas_fee
+        //获取币价
+        try {
+            if (key == 2019) {
+                var price = await moralis.getAllTotkenPrice({ chain: 'czz' })
+                if (typeof price === 'string') {
+                    price = JSON.parse(price)
+                }
+                var czzPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price)
+                total_gas_fee += parseFloat(value) / parseFloat(10 ** 18) * parseFloat(czzPriceItem.usdPrice)
+                logger.info('new-total_gas_fee czz', total_gas_fee, czzPriceItem)
+            } else {
+                var price = await moralis.getAllTotkenPrice({ chain: bsc_env })
+                if (typeof price === 'string') {
+                    price = JSON.parse(price)
+                }
+                var bnbPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price)
+                total_gas_fee += parseFloat(value) / parseFloat(10 ** 18) * parseFloat(bnbPriceItem.usdPrice)
+                logger.info('new-total_gas_fee bsc', total_gas_fee, bnbPriceItem)
+            }
+        } catch (error) {
+            logger.error('total_gas_fee', error)
+        }
+    }
+    var withdrawout = 0
+    if (!startTime && !endTime) {
+        for (key of withdraw_gas_map.keys()) {
+            try {
+                var outs = await filterWithdrawTotalOutFee(key, withdraw_ret)
+                withdraw_gas_map.get(key).total_withdraw = outs
+                // logger.error('withdraw_gas_map.get(key).total_withdraw outs', outs)
+                if (outs) {
+                    for (ckey of outs.keys()) {
+                        var item = outs.get(ckey);
+                        withdrawout = parseFloat(withdrawout) + (parseFloat(item.totalAmount) / parseFloat(10 ** item.decimals) * parseFloat(item.usdPrice))
+                    }
+                }
+            } catch (error) {
+                logger.error('filterWithdrawTotalOutFee error', error.toString())
+            }
+        }
+    }
+    return {
+        outmap: withdraw_gas_map,
+        totalGasFee: total_gas_fee,
+        totalWithdraw: withdrawout
+    };
+}
+
+async function updateBalance(obj, type) {
+    var price
+    if (type == 'bsc') {
+        var bsc_env = getBscEnv()
+        price = await moralis.getAllTotkenPrice({ chain: bsc_env })
+    } else if (type == 'czz') {
+        price = await moralis.getAllTotkenPrice({ chain: 'czz' })
+    }
+
+    if (typeof price === 'string') {
+        price = JSON.parse(price)
+    }
+    var priceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price, price)
+
+    if (!obj.native.balance) {
+        obj.native.balance = '0'
+    }
+    if (obj.native) {
+        obj.native.usdPrice = parseFloat(obj.native.balance) / parseFloat(10 ** 18) * priceItem.usdPrice
+        logger.info('findTokenPriceItem 0x0000000000000000000000000000000000000000 ', obj, priceItem, type)
+    }
+
+    var tokenTotal = 0
+    if (obj.other && Array.isArray(obj.other)) {
+        for (let index = 0; index < obj.other.length; index++) {
+            const element = obj.other[index];
+            priceItem = moralis.findTokenPriceItem(element.token_address, price)
+            logger.info('findTokenPriceItem element ', priceItem, element.token_address)
+            if (priceItem) {
+                if (!element.decimals || element.decimals == 0)
+                    element.decimals = 18
+                element.usdPrice = parseFloat(element.balance) / parseFloat(10 ** element.decimals) * priceItem.usdPrice
+                tokenTotal += element.usdPrice
+            }
+        }
+    }
+    return {
+        nativeTotal: obj.native.usdPrice,
+        tokenTotal: tokenTotal,
+        total: parseFloat(obj.native.usdPrice) + parseFloat(tokenTotal)
+    }
+}
+
+async function getAllBalance() {
+    var bsc_env = getBscEnv()
+    var company = await moralis.queryCompanyInfoFromId(0)
+    logger.info('getAllBalance company', company)
+    var bsc_balance = await moralis.getAccountAllCoins({
+        chain: bsc_env,
+        address: company.user_address
+    })
+    logger.info('getAccountAllCoins bsc_balance', bsc_balance)
+    var bscb = await updateBalance(bsc_balance, 'bsc')
+    logger.info('getAccountAllCoins updateBalance bscb', bscb)
+    var czz_balance = await moralis.getAccountAllCoins({
+        chain: 'czz',
+        address: company.user_address
+    })
+    logger.info('getAccountAllCoins czz_balance', czz_balance)
+    var czzb = await updateBalance(czz_balance, 'czz')
+
+    logger.info('getAccountAllCoins updateBalance czzb', czzb)
+
+    return {
+        bsc: bscb,
+        czz: czzb,
+        totalBalance: bscb.total + czzb.total
+    }
+}
+
+/**
+ * 获取时间段总支出的 gas fee
+ * @param {*} startTime 
+ * @param {*} endTime 
+ */
+async function getStatisticsInfo() {
+    // //今日
+    var startTime = utils.getLastDay('YYYY-MM-DD') + " 00:00:00"
+    var endTime = utils.getLastDay('YYYY-MM-DD') + " 23:59:59"
+    logger.info('getTotalOutGasFee', startTime, endTime)
+    //归集
+    var collectCoinsOut = await getCollectCoinsOutInfo(startTime, endTime)
+    logger.info('getCollectCoinsOutInfo  collectCoinsOut', collectCoinsOut)
+    //提币
+    var withdrawOut = await getWithdrawOutInfo(startTime, endTime)
+    logger.info('getWithdrawOutInfo withdrawOut ', withdrawOut)
+
+    var data = await http_request_get(utils.getLastDay('YYYYMMDD'))
+
+    //历史,总的
+    //归集
+    var totalCollectCoinsOut = await getCollectCoinsOutInfo(null, null)
+    logger.info('totalCollectCoinsOut  ', totalCollectCoinsOut)
+    //提币
+    var totalWithdrawOut = await getWithdrawOutInfo(null, null)
+    logger.info('totalWithdrawOut  ', totalWithdrawOut)
+
+    //获取当前账户总余额
+    var curBalances = await getAllBalance()
+    logger.info('getAllBalance  ', curBalances)
+
+    //获取散落 gas 
+    var totalSLGas = await getSLGas()
+    logger.info('getSLGas ret', totalSLGas)
+
+    return {
+        updateTime: utils.getLastDay('YYYY-MM-DD'),
+        todayTotalProfit: parseFloat(data.data.incomeUSDTotal) - parseFloat(collectCoinsOut.totalGasFee + withdrawOut.totalGasFee),
+        todayTotalOutGasFee: collectCoinsOut.totalGasFee + withdrawOut.totalGasFee,             //今日总支出的 gas fee
+        canNotWithdrawUSD: parseFloat(data.data.canNotWithdrawUSD),                                    //不可提现余额
+        canWithdrawUSD: parseFloat(data.data.canWithdrawUSD),                                          //可提现余额
+        todayIncomeUSDTotal: parseFloat(data.data.incomeUSDTotal),                                          //今日总收入
+        todayIncomeUSDFee: parseFloat(data.data.incomeUSDFee),                                              //今日固定收入
+        totalOutGasFee: totalCollectCoinsOut.totalGasFee + totalWithdrawOut.totalGasFee,        //总支出 gas fee
+        totalWithdrawGasFee: totalWithdrawOut.totalGasFee,                                      //总提币 gas fee
+        totalCollectCoinsGasFee: totalCollectCoinsOut.totalGasFee,                              //总归集 gas fee
+        totalInFee: totalCollectCoinsOut.totalInFee,                                            //总入金
+        totalNativeInFee: {
+            bsc: totalCollectCoinsOut.bsc.get('0x0000000000000000000000000000000000000000').totalUsdPrice,
+            czz: totalCollectCoinsOut.czz.get('0x0000000000000000000000000000000000000000').totalUsdPrice
+        },                                                                                      //总 native 入金
+        totalOutFee: totalWithdrawOut.totalWithdraw,                                            //总出金
+        totalBalances: curBalances.totalBalance,                                                             //总余额
+        ylGasBalance: {                                                                         //预留 gas 费余额  native 总余额 - 总入金
+            bsc: curBalances.bsc.nativeTotal - totalCollectCoinsOut.bsc.get('0x0000000000000000000000000000000000000000').totalUsdPrice,
+            czz: curBalances.czz.nativeTotal - totalCollectCoinsOut.czz.get('0x0000000000000000000000000000000000000000').totalUsdPrice,
+            total: (curBalances.bsc.nativeTotal - totalCollectCoinsOut.bsc.get('0x0000000000000000000000000000000000000000').totalUsdPrice) + (curBalances.czz.nativeTotal - totalCollectCoinsOut.czz.get('0x0000000000000000000000000000000000000000').totalUsdPrice)
+        },
+        slGasBalance: totalSLGas,                                                            //散落 gas 费余额  充值 0.5 gas - 使用 0.3 gas= 散落 0.2gas
+    }
+}
+
+
+
+function timeoutFunc(config, func) {
+
+    config.runNow && func()
+
+    let nowTime = new Date(utils.getCurrentDate()).getTime()
+    // nowTime = new Date().getTime()
+
+    let timePoints = config.time.split(':').map(i => parseInt(i))
+
+    let recent = new Date(utils.getCurrentDate()).setHours(...timePoints)
+
+    recent >= nowTime || (recent += 24 * 3600000)
+
+    console.log('timeoutFunc-------------------', nowTime, timePoints, recent)
+    setTimeout(() => {
+
+        func()
+
+        setInterval(func, config.interval * 3600000)
+
+    }, recent - nowTime)
+}
+
+
+
+
+
+const getFeishuToken = async (params) => {
+    return new Promise(resolve => {
+        axios.post(feishu_write_table_token_url,
+            {
+                app_id: "cli_a223f015abbad00e",
+                app_secret: "DMCF6tBwIpeOQPnWrFUMYd6tmjb53C4n"
+            },
+            {
+                timeout: 1 * 60 * 1000,
+                headers: {
+                    'Content-Type': "application/json; charset=utf-8"
+                }
+            })
+            .then(res => {
+                logger.log('getFeishuToken res=>', res.status, res.data);
+                resolve(res.data)
+            }).catch(err => {
+                logger.error('getFeishuToken error ', JSON.stringify(err));
+                resolve(JSON.stringify(err))
+            });
+    })
+}
+
+
+function writeTable(app_token, data) {
+    logger.info('writeTable', data)
+    var body = {
+        'valueRanges': [
+            {
+                'range': '0pRQpu!A2:C2',
+                'values': [
+                    [data.totalCollectCoinsGasFee, //归集总 gas
+                    data.totalWithdrawGasFee, //提币总 gas
+                    data.totalOutGasFee], //总支出 gas
+                ]
+            },
+            {
+                'range': '1ygrMB!A2:B2',
+                'values': [
+                    [
+                        data.totalInFee, //总入金
+                        data.totalOutFee,//总出金
+                    ],
+                ]
+            },
+            {
+                'range': 'BMjMDr!A3:J3',
+                'values': [
+                    [
+                        data.updateTime,      //更新时间
+                        data.todayTotalProfit,//今日总利润
+                        data.todayIncomeUSDTotal,//今日总收入
+                        data.todayIncomeUSDFee,//今日固定手续费收入
+                        data.todayTotalOutGasFee,//今日总 gas 支出
+                        data.totalBalances, //总余额
+                        data.canNotWithdrawUSD, //不可提现余额
+                        data.canWithdrawUSD,//可提现余额
+                        data.ylGasBalance.total,//预留 gas
+                        data.slGasBalance.total,//散落 gas
+                    ],
+                ]
+            }
+        ]
+    }
+    return new Promise(resolve => {
+        axios.post(feishu_write_table_data_url,
+            body,
+            {
+                timeout: 1 * 60 * 1000,
+                headers: {
+                    'Content-Type': "application/json; charset=utf-8",
+                    'Authorization': 'Bearer ' + app_token
+                }
+            })
+            .then(res => {
+                logger.log('writeTable res=>', res.status, res.data);
+                resolve(res.data)
+            }).catch(err => {
+                logger.error('writeTable error ', JSON.stringify(err));
+                resolve(JSON.stringify(err))
+            });
+    })
+}
+
+async function insertTableRows(app_token) {
+    var body = {
+        dimension: {
+            sheetId: 'BMjMDr',
+            majorDimension: 'ROWS',
+            startIndex: 2,
+            endIndex: 3,
+        },
+        inheritStyle: 'AFTER'
+    }
+    return new Promise(resolve => {
+        axios.post(feishu_insert_table_url,
+            JSON.stringify(body),
+            {
+                timeout: 1 * 60 * 1000,
+                headers: {
+                    'Content-Type': "application/json; charset=utf-8",
+                    'Authorization': 'Bearer ' + app_token
+                }
+            })
+            .then(res => {
+                console.log('res=>', res.status, res.data);
+                resolve(res.data)
+            }).catch(err => {
+                logger.error('error ', JSON.stringify(err));
+                resolve(JSON.stringify(err))
+            });
+    })
+}
+
+async function exec(data) {
+    var app = await getFeishuToken()
+    await insertTableRows(app.app_access_token)
+    await writeTable(app.app_access_token, data)
+}
+
+async function report2FeishuTable() {
+    try {
+        logger.error('数据统计 start')
+        logger.info('report2FeishuTable')
+        var data = await getStatisticsInfo();
+        logger.info('getStatisticsInfo', data)
+        var ret = await exec(data)
+        logger.error('数据统计 end','https://st94nif1cq.feishu.cn/sheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg?sheet=BMjMDr',JSON.stringify(data),JSON.stringify(ret))
+    } catch (error) {
+        logger.error('report2FeishuTable', error.toString())
+    }
+
+}
+
+timeoutFunc({
+
+    interval: 1, //间隔天数,间隔为整数
+
+    runNow: false, //是否立即运行
+
+    time: reportTime //执行的时间点 时在0~23之间
+
+}, func => {
+    if (process.env.NODE_ENV == 'prd') {
+        report2FeishuTable()
+    }
+})
+// logger.info('getAllBalance  ', getAllBalance())
+module.exports = {
+    getStatisticsInfo
+}
+
+
+

+ 52 - 5
model/utils.js

@@ -2,10 +2,10 @@
 var CryptoJS = require("crypto-js");
 require('dotenv').config()
 const logger = require('./logger')
-var moment = require('moment');
 const chinaTime = require('china-time');
 
 var { cryppt_config } = require('../config/config.js')
+const moment = require('moment-timezone');
 
 function toJson(code_, obj_, errMsg_) {
     var code = code_
@@ -31,7 +31,7 @@ function decryptPrivityKey(message) {
     // var iv = 'Zh4A7bOY2ksp9oIn'
 
     var encrypted = message; //python is base64 ECB
-    var key = process.env.DENET_CRYPT_KEY 
+    var key = process.env.DENET_CRYPT_KEY
     var iv = process.env.DENET_CRYPT_IV
     if (!key || !iv) {
         logger.error('decryptPrivityKey key or iv is empty?');
@@ -95,7 +95,11 @@ const CHAIN_NAME = {
     eth: 'eth',
     bsc_testnet: 'bsc testnet',
     bsc_mainnet: 'bsc',
-    czz: 'czz'
+    czz: 'czz',
+    kcc_testnet: 'kcc_testnet',
+    kcc_mainnet: 'kcc_mainnet',
+    okc_testnet: 'okc_testnet',
+    okc_mainnet: 'okc_mainnet',
 }
 
 const CHAIN_ID = {
@@ -103,11 +107,20 @@ const CHAIN_ID = {
     bsc_testnet: '0x61',
     bsc_mainnet: '0x38',
     czz: '2019',
+    kcc_testnet: '322',
+    kcc_mainnet: '321',
+    okc_testnet: '65',
+    okc_mainnet: '66',
 }
 
 const CHAIN_ID_NAME = {
     97: 'bsc_testnet',
     56: 'bsc_mainnet',
+    2019: 'czz',
+    65: 'okc_testnet',
+    66: 'okc_mainnet',
+    321: 'kcc_mainnet',
+    322: 'kcc_testnet',
 }
 
 const USE_SDK = {
@@ -121,14 +134,36 @@ function getRedisKeyFromChain(chain) {
         case CHAIN_NAME.bsc_testnet:
         case 'bsc_testnet':
         case 'bsc_mainnet':
+        case '97':
+        case '56':
             return '_BSC'
         case CHAIN_NAME.czz:
+        case CHAIN_ID.czz :
             return '_CZZ'
+        case CHAIN_NAME.kcc_mainnet:
+        case CHAIN_NAME.kcc_testnet:
+        case CHAIN_ID.kcc_mainnet:
+        case CHAIN_ID.kcc_testnet:
+            return '_KCC'
+        case CHAIN_NAME.okc_mainnet:
+        case CHAIN_NAME.okc_testnet:
+        case CHAIN_ID.okc_mainnet:
+        case CHAIN_ID.okc_testnet:
+            return '_OKC'
         default:
             return null
     }
 }
 
+function trim(str) {
+    try {
+        return str.replace(/(^\s*)|(\s*$)/g, "");
+    } catch (error) {
+        logger.error('trim error', error.toString())
+        return str
+    }
+}
+
 function getChainName(key) {
     return CHAIN_NAME[key];
 }
@@ -158,6 +193,16 @@ function getCurrentDateFormat(format) {
     return chinaTime(format);
 }
 
+function chinaTimeMs(format, ms) {
+    return format
+        ? moment(ms).tz('Asia/Shanghai').format(format)
+        : new Date(moment(ms).tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm'));
+}
+
+function getLastDay(format) {
+    return moment().subtract(1, 'day').tz('Asia/Shanghai').format(format)
+}
+
 function getTimestampToDate(tm) {
     var now = new Date(tm),
         y = now.getFullYear(),
@@ -175,7 +220,7 @@ function scientificNotationToString(param) {
         let strParam = String(param)
         let flag = /e/.test(strParam)
         if (!flag) return param
-    
+
         // 指数符号 true: 正,false: 负
         let sysbol = true
         if (/e-/.test(strParam)) {
@@ -185,7 +230,7 @@ function scientificNotationToString(param) {
         let index = Number(strParam.match(/\d+$/)[0])
         // 基数
         let basis = strParam.match(/^[\d\.]+/)[0].replace(/\./, '')
-    
+
         if (sysbol) {
             return basis.padEnd(index + 1, 0)
         } else {
@@ -214,4 +259,6 @@ module.exports = {
     scientificNotationToString,
     getTimestampToDate,
     getRedisKeyFromChain,
+    trim,
+    getLastDay,
 }

+ 57 - 41
routes/sdk.js

@@ -100,6 +100,18 @@ async function getCollectCoinsHash(trx_hash) {
     return ' '
 }
 
+function isExistHash(trx_hash, arrs) {
+    if (arrs && Array.isArray(arrs) && arrs.length > 0) {
+        var ret = arrs.filter(element => {
+           return element.gas_trx_hash == trx_hash
+        })
+        logger.info('isExistHash',trx_hash,ret)
+        return ret == null ? false : ret.length > 0
+    } else {
+        return false
+    }
+}
+
 
 async function filterTransfers(result) {
     try {
@@ -107,12 +119,15 @@ async function filterTransfers(result) {
             var ret = JSON.parse(result)
             if (ret.code == 0 && ret.data.total > 0) {
                 var new_ret = []
+                var filter_list = await collect_coins_db.query_collect_hash_list()
                 for (let index = 0; index < ret.data.results.length; index++) {
                     const element = ret.data.results[index];
-                    if (element.trx_hash == await getCollectCoinsHash(element.trx_hash)) {
+                    // if (element.trx_hash == await getCollectCoinsHash(element.trx_hash)) {
+                    if (isExistHash(element.trx_hash,filter_list)) {
                         logger.debug('element.trx_hash == await getCollectCoinsHash(element.trx_hash)', element)
                     } else {
                         new_ret.push(element)
+                        logger.debug('element.trx_hash != await getCollectCoinsHash(element.trx_hash)', new_ret.length)
                     }
                 }
                 return {
@@ -404,7 +419,7 @@ async function withdraw_task() {
                         logger.error('withdraw_task exectransaction err', error.toString());
                     }
                     if (tryCount < 0) {
-                        logger.error('withdraw_task getTokenTransfersV2 警告交易未更新:', JSON.stringify(options));
+                        logger.error('withdraw_task getTokenTransfersV2 40s内 警告交易未更新:', JSON.stringify(options));
                     }
                 } while (tryCount >= 0);
             }
@@ -686,7 +701,7 @@ async function getWithdrawStatus(ctx) {
                 chainId: info.chain_id,
                 transferTimestamp: info.update_time,
                 errorMsg: info.errorMsg
-            }, null)
+            }, info.errorMsg)
         }
     } else {
         ctx.body = utils.toJson(-1, null, obj.withdraw_id + ' id does not exist.')
@@ -766,9 +781,38 @@ async function timer_transfer_czz_task() {
     }
 }
 
+async function checkout_taxhash(exec_obj) {
+    logger.info('bsc_log_monitoring exec start:', exec_obj)
+    var delay = 60 * 1000
+    try {
+        if (typeof exec_obj === 'string')
+            exec_obj = JSON.parse(exec_obj)
+    } catch (error) {
+        logger.error('bsc_log_monitoring:', error)
+    }
+
+    var tryCount = 5
+    do {
+        if (tryCount == 0) {
+            logger.error('数据在5分钟未更新', JSON.stringify(exec_obj))
+            break
+        }
+        if (exec_obj.transactionHash) {
+            var ret = await account_mysql.getAccountTransactions({
+                type: 'only_hash',
+                transaction_hash: exec_obj.transactionHash
+            })
+            if (ret && ret.code == 0 && ret.data.total > 0) {
+                break
+            }
+        }
+        --tryCount
+        logger.debug('getAccountTransactions', tryCount, exec_obj)
+        await utils.sleep(delay)
+    } while (tryCount >= 0);
+}
 
 async function bsc_log_monitoring() {
-    var delay = 60 * 1000
     while (1) {
         var exec_obj = await redis.redis_pop(reids_token_config.BSC_LOG_MONITORING_KEY)
         if (!exec_obj) {
@@ -776,35 +820,7 @@ async function bsc_log_monitoring() {
             logger.log("no new check tasks")
             continue
         }
-
-        logger.info('bsc_log_monitoring exec start:', exec_obj)
-
-        try {
-            if (typeof exec_obj === 'string')
-                exec_obj = JSON.parse(exec_obj)
-        } catch (error) {
-            logger.error('bsc_log_monitoring:', error)
-        }
-
-        var tryCount = 5
-        do {
-            if (tryCount == 0) {
-                logger.error('数据在5分钟未更新', JSON.stringify(exec_obj))
-                break
-            }
-            if (exec_obj.transactionHash) {
-                var ret = await account_mysql.getAccountTransactions({
-                    type: 'only_hash',
-                    transaction_hash: exec_obj.transactionHash
-                })
-                if (ret && ret.code == 0 && ret.data.total > 0) {
-                    break
-                }
-            }
-            --tryCount
-            logger.debug('getAccountTransactions', tryCount, exec_obj)
-            await utils.sleep(delay)
-        } while (tryCount >= 0);
+        checkout_taxhash(exec_obj)
     }
 }
 
@@ -834,16 +850,16 @@ router.post('/getAllTokenWithdrawInfoLists', getAllTokenWithdrawInfoLists)
 // 定时任务 提币+归集
 withdraw_task();
 collect_conis_task();
-//czz 504 检查
-check_czz_withdraw_task();
-// bsc 监控
-bsc_log_monitoring()
+// //czz 504 检查
+// check_czz_withdraw_task();
+// // bsc 监控
+// bsc_log_monitoring()
 
 if (process.env.NODE_ENV == 'dev' || process.env.NODE_ENV == 'test') {
-    timer_transfer_bsc_task()
-    timer_transfer_czz_task()
-    timer_collect_conis_bsc_task()
-    timer_collect_conis_czz_task()
+    // timer_transfer_bsc_task()
+    // timer_transfer_czz_task()
+    // timer_collect_conis_bsc_task()
+    // timer_collect_conis_czz_task()
 }
 
 module.exports = router

+ 10 - 3
test/db_test.js

@@ -8,6 +8,8 @@ var remote_config_db = require("../model/db/remote_config_db");
 var collect_coins_db = require("../model/db/collect_coins_db");
 var withdraw_db = require("../model/db/withdraw_db");
 const BigNumber = require('bignumber.js')
+var utils = require('../model/utils')
+var statistics = require('../model/server_data_statistics')
 router.prefix('/test');
 router.post('/set', async (ctx) => {
     // const obj = ctx.request.body;
@@ -66,6 +68,7 @@ async function query() {
     console.log('getTokenCollectCoinsBlacklist',await remote_config_db.tokenCollectCoinsBlacklistExist('token_collect', 'bsc', '0x8860313deeb10a2863bc05b04b37897044edbda1'))
 }
 
+
 async function getInfo() {
     var collect_ret = await collect_coins_db.query_collect_total_fee();
     var withdraw_ret = await withdraw_db.getWidthdrawTotalFee()
@@ -142,20 +145,24 @@ async function getInfo2() {
     //     console.log('total', total_gas_fee2 + total_gas_fee)
 }
 
-function getInfo3() {
+async function getInfo3() {
     // console.log('1018087452840284941787', 1018087452840284941787 / (10**18))
 
     // console.log('101000000000000000000', 101000000000000000000 / (10**18) * 0.6476)
 
     // console.log('64585000000000000000000', 64585000000000000000000 / (10**18) * 0.0005)
 
-    
+
     // console.log('1000000000000000', 1000000000000000 / (10**18) * 0.0482)
     // console.log('2410418338592426908426', 2410418338592426908426 / (10**18) * 0.2074)
     // console.log('2501561242336020120158', 2501561242336020120158 / (10**18) * 0.2074)
 
+    console.log("YYYY-MM-dd", utils.getCurrentDateFormat('YYYY-MM-DD'))
+   
+    console.error("getStatisticsInfo", await statistics.getStatisticsInfo())
+
 }
-getInfo3()
+// getInfo3()
 // getInfo2()
 // query()
 module.exports = router

+ 109 - 5
test/sample.js

@@ -1,8 +1,9 @@
 var CryptoJS = require("crypto-js");
+const axios = require('axios');
 
 function decryptPrivityKey(message) {
-    var encrypted = message; 
-    var key = process.env.DENET_CRYPT_KEY 
+    var encrypted = message;
+    var key = process.env.DENET_CRYPT_KEY
     var iv = process.env.DENET_CRYPT_IV
     if (!key || !iv) {
         console.error('decryptPrivityKey key or iv is empty?');
@@ -13,14 +14,117 @@ function decryptPrivityKey(message) {
     iv = CryptoJS.enc.Utf8.parse(iv)
     var decrypted = CryptoJS.AES.decrypt(encrypted
         , key
-        , { iv: iv, mode: CryptoJS.mode.CBC}
-        );
+        , { iv: iv, mode: CryptoJS.mode.CBC }
+    );
     console.log('decrypted decrypt ==', decrypted);
     var de_pk = decrypted.toString(CryptoJS.enc.Utf8);
     console.log('decrypted==', de_pk);
     return de_pk
 }
 
-decryptPrivityKey('bnUvrSKmU3l7zONP+hIH1BOMhNVfIBUgEziWb2tGiUgn63FZVdzXC9E1DT+Mx3B+nqrzUAqd60I/8rLobYYl+0TSR3Ri1o0iX/hcxqs/0yg=')
+// decryptPrivityKey('bnUvrSKmU3l7zONP+hIH1BOMhNVfIBUgEziWb2tGiUgn63FZVdzXC9E1DT+Mx3B+nqrzUAqd60I/8rLobYYl+0TSR3Ri1o0iX/hcxqs/0yg=')
+
+
+const getFeishuToken = async (params) => {
+    return new Promise(resolve => {
+        axios.post('https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal',
+            {
+                app_id: "cli_a223f015abbad00e",
+                app_secret: "DMCF6tBwIpeOQPnWrFUMYd6tmjb53C4n"
+            },
+            {
+                timeout: 1 * 60 * 1000,
+                headers: {
+                    'Content-Type': "application/json; charset=utf-8"
+                }
+            })
+            .then(res => {
+                console.log('res=>', res.status, res.data);
+                resolve(res.data)
+            }).catch(err => {
+                logger.error('http_request_post withdraw_czz error ', JSON.stringify(err));
+                resolve(JSON.stringify(err))
+            });
+    })
+}
+
+
+function writeTable(app_token) {
+    var body = {
+        'valueRanges': [
+            {
+                'range': '072d4e!A1:B1',
+                'values': [
+                    ['2022/06/15 11:43:23', 1],
+                ]
+            }
+        ]
+    }
+    return new Promise(resolve => {
+        axios.post('https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnsS7N9KS6pVUvR9qvj8mYpb/values_batch_update',
+            body,
+            {
+                timeout: 1 * 60 * 1000,
+                headers: {
+                    'Content-Type': "application/json; charset=utf-8",
+                    'Authorization': 'Bearer ' + app_token
+                }
+            })
+            .then(res => {
+                console.log('res=>', res.status, res.data);
+                resolve(res.data)
+            }).catch(err => {
+                console.error('error ', JSON.stringify(err));
+                resolve(JSON.stringify(err))
+            });
+    })
+}
+
+async function insertTableRows(app_token) {
+    var body = {
+        dimension: {
+            sheetId: '072d4e',
+            majorDimension: 'ROWS',
+            startIndex: 0,
+            endIndex: 1,
+        },
+        inheritStyle: 'AFTER'
+    }
+    return new Promise(resolve => {
+        axios.post('https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnsS7N9KS6pVUvR9qvj8mYpb/insert_dimension_range',
+            JSON.stringify(body),
+            {
+                timeout: 1 * 60 * 1000,
+                headers: {
+                    'Content-Type': "application/json; charset=utf-8",
+                    'Authorization': 'Bearer ' + app_token
+                }
+            })
+            .then(res => {
+                console.log('res=>', res.status, res.data);
+                resolve(res.data)
+            }).catch(err => {
+                console.error('error ', JSON.stringify(err));
+                resolve(JSON.stringify(err))
+            });
+    })
+}
+
+async function exec() {
+    var app = await getFeishuToken()
+    await insertTableRows(app.app_access_token)
+    await writeTable(app.app_access_token)
+}
+
+
+exec()
+
+
+
+
+
+
+
+