const logger = require('./logger') var remote_config_db = require("./db/remote_config_db"); var collect_coins_db = require("./db/collect_coins_db"); var withdraw_db = require("./db/withdraw_db"); var moralis = require("./moralis_sdk"); var utils = require("./utils"); const axios = require('axios'); var { account_config } = require('../config/config.js'); const { max } = require('moment'); const redis = require('./db/redis_db'); // 拿到飞书写入的 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 feishu_delete_table_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/dimension_range' const feishu_get_table_metadata_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/metainfo' const feishu_create_table_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/dimension_range' const mTokenPriceCache = new Map() const mDecimalsCache = new Map() //########################################### 出入金数据统计 ######################################## 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' } }) }); }) } 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 findCurBalance(type, address) { var balances var price var tokenItems = [] switch (type) { case 'bsc': balances = await moralis.getAccountAllCoins({ chain: getBscEnv(), address: address }) // price = await moralis.getAllTotkenPrice({ chain: getBscEnv() }) price = await getPrice(getBscEnv()) break case 'czz': balances = await moralis.getAccountAllCoins({ chain: 'czz', address: address }) price = await getPrice('czz') // price = await moralis.getAllTotkenPrice({ chain: 'czz' }) break } logger.info('findCurBalance', type, address, balances, price) if (typeof price === 'string') { price = JSON.parse(price) } if (!balances || !price) return null priceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price) if (!balances.native.balance) { balances.native.balance = '0' } if (balances.native) { balances.native.usdPrice = parseFloat(balances.native.balance) / parseFloat(10 ** 18) * priceItem.usdPrice logger.info('findTokenPriceItem 0x0000000000000000000000000000000000000000 ', balances, priceItem, type) var bo = { address: address, token_address: '0x0000000000000000000000000000000000000000', chain: type, amount: balances.native.balance, decimals: 18, price: priceItem.usdPrice, usd: balances.native.usdPrice } tokenItems.push(bo) } if (balances.other && Array.isArray(balances.other)) { for (let index = 0; index < balances.other.length; index++) { const element = balances.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 var bo = { address: address, token_address: element.token_address, chain: type, amount: element.balance, decimals: element.decimals, price: priceItem.usdPrice, usd: element.usdPrice } tokenItems.push(bo) } } } logger.debug('tokenUsds', tokenItems, type) return { nativeUsd: balances.native.usdPrice, tokenUsds: tokenItems, totalUsd: addUsds(tokenItems, 'token_balance') } } async function getAllBalanceV2() { var company = await moralis.queryCompanyInfoFromId(0) logger.info('getAllBalance company', company) var bsc_balances = await findCurBalance('bsc', company.user_address) var czz_balances = await findCurBalance('czz', company.user_address) logger.info('findCurBalance bsc_balances', bsc_balances) logger.info('findCurBalance czz_balances', czz_balances) let lists = bsc_balances.tokenUsds.concat(czz_balances.tokenUsds); return { bsc: bsc_balances, czz: czz_balances, infos: lists, totalUsd: bsc_balances.totalUsd + czz_balances.totalUsd, totalNativeBalanceUsd: bsc_balances.nativeUsd + czz_balances.nativeUsd } } function parseGas(response, index, price) { try { logger.info('parseGas in', response, index) if (response && Array.isArray(response) && response.length > 0) { var obj if (response[index] && typeof response[index] === 'string') try { obj = JSON.parse(response[index]) } catch (error) { logger.error('JSON.parse(response[index])', error.toString) } return parseFloat(obj.gasPrice.number) * parseFloat(obj.gasLimit.number) / parseFloat(10 ** 18) * parseFloat(price) } else return 0 } catch (error) { logger.error('parseGas', error.toString(), JSON.stringify(response)) return 0 } } async function getPrice(key) { if (mTokenPriceCache.has(key)) { price = mTokenPriceCache.get(key) } else { var price = await moralis.getAllTotkenPrice({ chain: key }) if (typeof price === 'string') { price = JSON.parse(price) } mTokenPriceCache.set(key, price) } return price } async function getPriceFromCache(key, address) { console.info('getPriceFromCache>', key, address) var price = await getPrice(key) // var price = await moralis.getAllTotkenPrice({ // chain: key // }) // if (typeof price === 'string') { // price = JSON.parse(price) // } var priceItem = moralis.findTokenPriceItem(address, price) if (priceItem && priceItem.usdPrice) { return priceItem.usdPrice } else { return '0' } } function convertChain(chain) { return chain } function balance2USDPrice(amount, decimals, price) { return parseFloat(amount) / parseFloat(10 ** decimals) * parseFloat(price) } async function getDecimalsFromRedis(chain, address) { var decimals = 18 try { var newKey = redis.formatRedisKey('REDIS_ERC20_CONTRACT_DECIMALS', chain, address.toLowerCase()) if (mDecimalsCache.has(newKey)) { decimals = mDecimalsCache.get(newKey) } else { decimals = await redis.readAppendRedis('REDIS_ERC20_CONTRACT_DECIMALS', chain, address.toLowerCase()) mDecimalsCache.set(newKey, decimals) } if (!decimals) decimals = '18' } catch (error) { logger.error('getDecimalsFromRedis', chain, address, error.toString()) decimals = 18 } return decimals } function addUsds(infos, type) { var total = 0 if (infos && Array.isArray(infos) && infos.length > 0) { for (let index = 0; index < infos.length; index++) { const element = infos[index]; switch (type) { case 'gas': // logger.info('addUsds total ', type, element.gasUsd, element, total) total += element.gasUsd break case 'withdraw': total += element.withdrawUsd break case 'collect_coins': total += element.withdrawUsd break case 'slGas': total += element.slGasUsd break case 'token_balance': total += element.usd break case 'native_in_coins': if (element.token_address && element.token_address == '0x0000000000000000000000000000000000000000') total += element.inUsd break case 'native_token_in_coins': total += element.inUsd break } } } return total } async function getWithdrawOutInfoV2(startTime, endTime) { if (startTime && endTime) { startTime = new Date(startTime).getTime() endTime = new Date(endTime).getTime() } if (!startTime && endTime) { startTime = 0 endTime = new Date(endTime).getTime() } var withdraw_ret = await withdraw_db.getWidthdrawTotalFee(startTime, endTime) var withDrawInfos = [] for (let index = 0; index < withdraw_ret.length; index++) { const trs = withdraw_ret[index]; // console.log('getWithdrawOutInfoV2 element ', trs) var token_address = trs.contract_address if (!trs.contract_address) token_address = '0x0000000000000000000000000000000000000000' try { var input = { dt: utils.chinaTimeMs(trs.update_time), user_address: trs.to_address, token_address: token_address, chain: convertChain(trs.chain_id + ""), amount: trs.amount, decimals: await getDecimalsFromRedis(convertChain(trs.chain_id + ""), token_address), price: await getPriceFromCache(convertChain(trs.chain_id + ""), token_address), gasUsd: parseFloat(trs.gas_price) * parseFloat(trs.gas_limit) / parseFloat(10 ** 18) * await getPriceFromCache(convertChain(trs.chain_id + ""), '0x0000000000000000000000000000000000000000') } if (!input.gasUsd) { logger.info('withdraw_ret input ', trs, input) input.gasUsd = 0 } //换算成美元 input.withdrawUsd = balance2USDPrice(input.amount, input.decimals, input.price) console.log('getWithdrawOutInfoV2 input ', input) withDrawInfos.push(input) } catch (error) { logger.error('getWithdrawOutInfoV2 trs', error.toString()) } } // logger.log('getWithdrawOutInfoV2 totalOutGasFeeUsd addUsds', addUsds(withDrawInfos, 'gas'), withDrawInfos[0], withDrawInfos[1]) return { infos: withDrawInfos, totalOutGasFeeUsd: addUsds(withDrawInfos, 'gas'), totalWithdrawUsd: addUsds(withDrawInfos, 'withdraw'), } } async function getCollectCoinsOutInfoV2(startTime, endTime) { var collect_ret = await collect_coins_db.query_collect_total_fee(startTime, endTime); // console.log('getCollectCoinsOutInfoV2 collect_ret', collect_ret.results.length) //每笔入金的详细信息 var infos = [] //入金充值的 gas 和实际消费的 gas var inGasFeeInfo = [] if (collect_ret && collect_ret.results && Array.isArray(collect_ret.results) && collect_ret.results.length > 0) { for (let index = 0; index < collect_ret.results.length; index++) { var element = collect_ret.results[index] if (!element.chain) element.chain = 'bsc_mainnet' var update_tm = element.update_time var user_address = element.user_address // console.log('getCollectCoinsOutInfoV2 element', element.before_gas_fee, element.resposes, typeof element.resposes, JSON.parse(element.resposes)) var resposes; if (element.resposes && typeof element.resposes === 'string') { try { resposes = JSON.parse(element.resposes) } catch (error) { logger.error('element.response parse', error.toString()) } } if (element.prestore_gas_fee && typeof element.prestore_gas_fee === 'string') { try { var gasObj = JSON.parse(element.prestore_gas_fee) var before_gas_fee = element.before_gas_fee ? element.before_gas_fee : '0' if (gasObj) { // logger.log('element.prestore_gas_fee parse', before_gas_fee, gasObj, gasObj.chain) var newGasObj = { chain: convertChain(gasObj.chain), amount: gasObj.amount, useGas: before_gas_fee, price: await getPriceFromCache(convertChain(gasObj.chain), '0x0000000000000000000000000000000000000000'), } //实际充值手续费用到的 usd newGasObj.gasUsd = balance2USDPrice(newGasObj.useGas, 18, newGasObj.price) //散落 gas newGasObj.slGasUsd = balance2USDPrice(parseFloat(newGasObj.amount) - parseFloat(newGasObj.useGas), 18, newGasObj.price) inGasFeeInfo.push(newGasObj) } } catch (error) { logger.error('element.prestore_gas_fee parse', error.toString()) } } if (element.transfers && typeof element.transfers === 'string') { try { var trss = JSON.parse(element.transfers) // console.log('trss.transfers', trss.chain) for (let index = 0; index < trss.length; index++) { const trs = trss[index]; var address = trs.contractAddress == null ? '0x0000000000000000000000000000000000000000' : trs.contractAddress console.log('trss.transfers', address, trs) var input = { dt: update_tm, user_address: user_address, token_address: address, chain: convertChain(trs.chain), amount: trs.amount, decimals: trs.contractAddress == null ? 18 : await getDecimalsFromRedis(convertChain(trs.chain), trs.contractAddress), price: await getPriceFromCache(convertChain(trs.chain), address), gasUsd: parseGas(resposes, index, await getPriceFromCache(convertChain(trs.chain), '0x0000000000000000000000000000000000000000')) //入金 gas 手续费 } //换算成美元 input.inUsd = balance2USDPrice(input.amount, input.decimals, input.price) infos.push(input) } } catch (error) { logger.error('transfers handle error', error.toString()) } } } } return { infos: infos, totalNativeInFee: addUsds(infos, 'native_in_coins'), //总 native 入金 totalInFee: addUsds(infos, 'native_token_in_coins'), //总入金 totalInGasFeeUsd: addUsds(infos, 'gas') + addUsds(inGasFeeInfo, 'gas'),//总入金消耗的 gas 包含打 gas fee slTotalGasFeeUsd: addUsds(inGasFeeInfo, 'slGas') //散落 gas } } async function getServerData(startTime, endTime) { //拿到所有归集 list var collectCoinsInfos = await getCollectCoinsOutInfoV2(startTime, endTime) // console.log('getCollectCoinsOutInfoV2 collectCoinsInfos ', collectCoinsInfos) //拿到所有出金 var withdrawInfos = await getWithdrawOutInfoV2(startTime, endTime) // console.log('getWithdrawOutInfoV2 withdrawInfos ', withdrawInfos) return { collectCoinsInfos: collectCoinsInfos, withdrawInfos: withdrawInfos } } function sortList(lists) { lists.sort((a, b) => { let t1 = new Date(a.dt) let t2 = new Date(b.dt) return t2.getTime() - t1.getTime() }) return lists } function formatTableData(type, datas) { var arrs = [] for (let index = 0; index < datas.length; index++) { const element = datas[index]; switch (type) { case 'incoins': arrs.push([element.dt, element.user_address, element.token_address, element.chain, element.amount, element.decimals, element.price, element.gasUsd, element.inUsd ]) break; case 'outcoins': arrs.push([element.dt, element.user_address, element.token_address, element.chain, element.amount, element.decimals, element.price, element.gasUsd, element.withdrawUsd ]) break case 'balances': arrs.push([element.address, element.token_address, element.chain, element.amount, element.decimals, element.price, element.usd ]) break default: break; } } logger.info('formatTableData', arrs) return arrs } async function getStatisticsInfoV2(day) { // //今日 var startTime = utils.getLastDay(day, 'YYYY-MM-DD') + " 00:00:00" var endTime = utils.getLastDay(day, 'YYYY-MM-DD') + " 23:59:59" logger.info('getTotalOutGasFee', startTime, endTime) var rangeData = await getServerData(startTime, endTime) logger.info('getServerData rangeData', rangeData) var allData = await getServerData(null, endTime) // var allData = rangeData logger.info('getServerData allData', allData) var data = await http_request_get(utils.getLastDay(day, 'YYYYMMDD')) logger.info('http_request_get data', data) //获取当前账户总余额 var curBalances = await getAllBalanceV2() logger.info('getAllBalanceV2 curBalances', curBalances) return { updateTime: utils.getLastDay(day, 'YYYY-MM-DD'), todayTotalProfit: parseFloat(data.data.incomeUSDTotal) - parseFloat(rangeData.collectCoinsInfos.totalInGasFeeUsd + rangeData.withdrawInfos.totalOutGasFeeUsd),//今日收入 todayTotalOutGasFee: rangeData.collectCoinsInfos.totalInGasFeeUsd + rangeData.withdrawInfos.totalOutGasFeeUsd,//今日总支出的 gas fee canNotWithdrawUSD: parseFloat(data.data.canNotWithdrawUSD), //不可提现余额 canWithdrawUSD: parseFloat(data.data.canWithdrawUSD), //可提现余额 todayIncomeUSDTotal: parseFloat(data.data.incomeUSDTotal), //今日总收入 todayIncomeUSDFee: parseFloat(data.data.incomeUSDFee), //今日固定收入 totalOutGasFee: allData.collectCoinsInfos.totalInGasFeeUsd + allData.withdrawInfos.totalOutGasFeeUsd, //总支出 gas fee totalWithdrawGasFee: allData.withdrawInfos.totalOutGasFeeUsd, //总提币 gas fee totalCollectCoinsGasFee: allData.collectCoinsInfos.totalInGasFeeUsd, //总归集 gas fee totalInFee: allData.collectCoinsInfos.totalInFee, //总入金 totalNativeInFee: allData.collectCoinsInfos.totalNativeInFee, //总 native 入金 totalOutFee: allData.withdrawInfos.totalWithdrawUsd, //总出金 totalBalances: curBalances.totalUsd, //总余额 ylGasBalance: curBalances.totalNativeBalanceUsd - allData.collectCoinsInfos.totalNativeInFee, //预留 gas 费余额 native 总余额 - 总入金 slGasBalance: allData.collectCoinsInfos.slTotalGasFeeUsd, //散落 gas 费余额 充值 0.5 gas - 使用 0.3 gas= 散落 0.2gas todayInUsdLists: sortList(rangeData.collectCoinsInfos.infos),//入金列表 todayOutUsdLists: sortList(rangeData.withdrawInfos.infos),//出金列表 totalInUsdLists: sortList(allData.collectCoinsInfos.infos),//总入金列表 totalOutUsdLists: sortList(allData.withdrawInfos.infos),//总出金列表 totalBalanceLists: curBalances.infos //总余额 } } const request = async (url, app_token, data) => { logger.info('request', url, app_token, data) return new Promise(resolve => { axios.post(url, JSON.stringify(data), { 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)) }); }) } 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 formatTableRangle(id, size) { var newId = id + size logger.info('formatTableRangle', id, size, newId) return newId } async function writeTable(app_token, data) { logger.info('writeTable', data) var valueRanges = [] if (data.todayInUsdLists.length > 0) { await insertTableRows(app_token, 'Ji1hLG', 1, data.todayInUsdLists.length + 1) valueRanges.push({//入金汇总 'range': formatTableRangle('Ji1hLG!A2:I', data.todayInUsdLists.length + 1), 'values': formatTableData('incoins', data.todayInUsdLists) }) } if (data.todayOutUsdLists.length > 0) { await insertTableRows(app_token, 'aFCrrP', 1, data.todayOutUsdLists.length + 1) valueRanges.push({//出金汇总 'range': formatTableRangle('aFCrrP!A2:I', data.todayOutUsdLists.length + 1), 'values': formatTableData('outcoins', data.todayOutUsdLists) }) } if (data.totalBalanceLists.length > 0) { var rows = await getTableRows(app_token, 2) if (rows > 1) { logger.info('getTableRows', rows) await delTableRows(app_token, '2hNaot', 2, rows) await addTableRows(app_token, '2hNaot', rows) } valueRanges.push({ //总余额汇总 'range': formatTableRangle('2hNaot!A2:I', data.totalBalanceLists.length + 1), 'values': formatTableData('balances', data.totalBalanceLists) }) } valueRanges.push({//归集汇总 'range': formatTableRangle('0pRQpu!A2:C', 2), 'values': [ [data.totalCollectCoinsGasFee, //归集总 gas data.totalWithdrawGasFee, //提币总 gas data.totalOutGasFee], //总支出 gas ] }) valueRanges.push({//总入账 'range': formatTableRangle('1ygrMB!A2:B', 2), 'values': [ [ data.totalInFee, //总入金 data.totalOutFee,//总出金 ] ] }) valueRanges.push({//利润表单 'range': formatTableRangle('BMjMDr!A3:J', 3), 'values': [ [ data.updateTime, //更新时间 data.todayTotalProfit,//今日总利润 data.todayIncomeUSDTotal,//今日总收入 data.todayIncomeUSDFee,//今日固定手续费收入 data.todayTotalOutGasFee,//今日总 gas 支出 data.totalBalances, //总余额 data.canNotWithdrawUSD, //不可提现余额 data.canWithdrawUSD,//可提现余额 data.ylGasBalance,//预留 gas data.slGasBalance,//散落 gas ], ] }) var body = { 'valueRanges': valueRanges } 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 getTableRows(app_token, index) { return new Promise(resolve => { axios.get(feishu_get_table_metadata_url, { 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, res.data.data.sheets); resolve(res.data.data.sheets[index].rowCount) }).catch(err => { logger.error('error ', JSON.stringify(err)); resolve(0) }); }) } async function delTableRows(app_token, sheetId, startIndex, endIndex) { var body = { dimension: { sheetId: sheetId, majorDimension: 'ROWS', startIndex: startIndex, endIndex: endIndex, }, } return new Promise(resolve => { axios.delete(feishu_delete_table_url, { data: body, timeout: 1 * 60 * 1000, headers: { 'Content-Type': "application/json; charset=utf-8", 'Authorization': 'Bearer ' + app_token } }) .then(res => { console.log('delTableRows res=>', res.status, res.data); resolve(res.data) }).catch(err => { logger.error('delTableRows error ', JSON.stringify(err)); resolve(JSON.stringify(err)) }); }) } async function insertTableRows(app_token, sheetId, startIndex, endIndex) { logger.info('insertTableRows', app_token, sheetId, startIndex, endIndex) var body = { dimension: { sheetId: sheetId, majorDimension: 'ROWS', startIndex: startIndex, endIndex: endIndex, }, 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 addTableRows(app_token, sheetId, endIndex) { var body = { dimension: { sheetId: sheetId, majorDimension: 'ROWS', length: endIndex, }, } return new Promise(resolve => { axios.post(feishu_create_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, app) { await insertTableRows(app.app_access_token, 'BMjMDr', 2, 3) return await writeTable(app.app_access_token, data) } async function notify(app, chain_id) { var content = "{\"zh_cn\":{\"title\":\"出入金数据更新成功\",\"content\":[[{\"tag\":\"text\",\"text\":\"更新时间:\"},{\"tag\":\"text\",\"text\":\"" + utils.getCurrentDate() + "\"}],[{\"tag\":\"text\",\"text\":\"https://st94nif1cq.feishu.cn/sheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg?sheet=BMjMDr:\"}]]}}" var data = { "receive_id": chain_id, "content": content, "msg_type": "post" } await request('https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id', app.app_access_token, data) } async function report2FeishuTable(day) { try { logger.error('数据统计 start') logger.info('report2FeishuTable') var data = await getStatisticsInfoV2(day); // data = '' logger.info('getStatisticsInfo', data) var app = await getFeishuToken() var ret = await exec(data, app) notify(app, 'oc_9770d283cd81df887e96795ddfcd9dcd') logger.error('数据统计完成:', 'https://st94nif1cq.feishu.cn/sheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg?sheet=BMjMDr') mTokenPriceCache.clear() } catch (error) { logger.error('report2FeishuTable', error.toString()) } } async function testNotify() { var app = await getFeishuToken() notify(app, 'oc_b159b9a53dd52206c5cf75677d0cf0ca') } async function test() { // var ret = await getStatisticsInfoV2(2) // logger.debug('getStatisticsInfoV2', await getStatisticsInfoV2(2), mTokenPriceCache.size) // for (let index = 40; index >=0; index--) { await report2FeishuTable(1) // } } // test() // exec() // testNotify() module.exports = { report2FeishuTable }