server_data_statisticsv2.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. const logger = require('./logger')
  2. var remote_config_db = require("./db/remote_config_db");
  3. var collect_coins_db = require("./db/collect_coins_db");
  4. var withdraw_db = require("./db/withdraw_db");
  5. var moralis = require("./moralis_sdk");
  6. var utils = require("./utils");
  7. const axios = require('axios');
  8. var { account_config } = require('../config/config.js');
  9. const { max } = require('moment');
  10. const redis = require('./db/redis_db');
  11. // 拿到飞书写入的 token
  12. const feishu_write_table_token_url = 'https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal'
  13. const feishu_write_table_data_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/values_batch_update'
  14. const feishu_insert_table_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/insert_dimension_range'
  15. const feishu_delete_table_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/dimension_range'
  16. const feishu_get_table_metadata_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/metainfo'
  17. const feishu_create_table_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/dimension_range'
  18. const mTokenPriceCache = new Map()
  19. const mDecimalsCache = new Map()
  20. //########################################### 出入金数据统计 ########################################
  21. const http_request_get = async (data) => {
  22. var host = account_config.STATISTICS_URL
  23. // host = 'https://api.denetme.net/denet/wallet/stat/getMoneyStat?date='
  24. var path = data
  25. var url = host + path
  26. logger.log('http_request_get', url)
  27. return new Promise(response => {
  28. axios.get(url)
  29. .then(res => {
  30. logger.log('res=>', res.status, res.data);
  31. if (res.data.code == 0) {
  32. response(res.data)
  33. } else {
  34. response({
  35. code: 0,
  36. msg: err.toString(),
  37. data: {
  38. canNotWithdrawUSD: '0',
  39. canWithdrawUSD: '0',
  40. incomeUSDTotal: '0',
  41. incomeUSDFee: '0'
  42. }
  43. })
  44. }
  45. }).catch(err => {
  46. logger.error('http_request_get', err.toString(), url.toString());
  47. response({
  48. code: -1,
  49. msg: err.toString(),
  50. data: {
  51. canNotWithdrawUSD: '0',
  52. canWithdrawUSD: '0',
  53. incomeUSDTotal: '0',
  54. incomeUSDFee: '0'
  55. }
  56. })
  57. });
  58. })
  59. }
  60. function getBscEnv() {
  61. var bsc_env
  62. switch (process.env.NODE_ENV) {
  63. case 'dev':
  64. case 'test':
  65. bsc_env = 'bsc_testnet'
  66. bsc_env = 'bsc_mainnet'
  67. break
  68. case 'prd':
  69. bsc_env = 'bsc_mainnet'
  70. break
  71. default:
  72. bsc_env = 'bsc_mainnet'
  73. break
  74. }
  75. return bsc_env;
  76. }
  77. async function findCurBalance(type, address) {
  78. var balances
  79. var price
  80. var tokenItems = []
  81. switch (type) {
  82. case 'bsc':
  83. balances = await moralis.getAccountAllCoins({
  84. chain: getBscEnv(),
  85. address: address
  86. })
  87. // price = await moralis.getAllTotkenPrice({ chain: getBscEnv() })
  88. price = await getPrice(getBscEnv())
  89. break
  90. case 'czz':
  91. balances = await moralis.getAccountAllCoins({
  92. chain: 'czz',
  93. address: address
  94. })
  95. price = await getPrice('czz')
  96. // price = await moralis.getAllTotkenPrice({ chain: 'czz' })
  97. break
  98. default:
  99. balances = await moralis.getAccountAllCoins({
  100. chain: type,
  101. address: address
  102. })
  103. price = await getPrice(type)
  104. break
  105. }
  106. logger.info('findCurBalance', type, address, balances, price)
  107. if (typeof price === 'string') {
  108. price = JSON.parse(price)
  109. }
  110. if (!balances || !price) return {
  111. nativeUsd: 0,
  112. tokenUsds: [],
  113. totalUsd: 0
  114. }
  115. priceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price)
  116. if (!balances.native.balance) {
  117. balances.native.balance = '0'
  118. }
  119. if (balances.native) {
  120. balances.native.usdPrice = parseFloat(balances.native.balance) / parseFloat(10 ** 18) * priceItem.usdPrice
  121. logger.info('findTokenPriceItem 0x0000000000000000000000000000000000000000 ', balances, priceItem, type)
  122. var bo = {
  123. address: address,
  124. token_address: '0x0000000000000000000000000000000000000000',
  125. chain: type,
  126. amount: balances.native.balance,
  127. decimals: 18,
  128. price: priceItem.usdPrice,
  129. usd: balances.native.usdPrice
  130. }
  131. tokenItems.push(bo)
  132. }
  133. if (balances.other && Array.isArray(balances.other)) {
  134. for (let index = 0; index < balances.other.length; index++) {
  135. const element = balances.other[index];
  136. priceItem = moralis.findTokenPriceItem(element.token_address, price)
  137. logger.info('findTokenPriceItem element ', priceItem, element.token_address)
  138. if (priceItem) {
  139. if (!element.decimals || element.decimals == 0)
  140. element.decimals = 18
  141. element.usdPrice = parseFloat(element.balance) / parseFloat(10 ** element.decimals) * priceItem.usdPrice
  142. var bo = {
  143. address: address,
  144. token_address: element.token_address,
  145. chain: type,
  146. amount: element.balance,
  147. decimals: element.decimals,
  148. price: priceItem.usdPrice,
  149. usd: element.usdPrice
  150. }
  151. tokenItems.push(bo)
  152. }
  153. }
  154. }
  155. logger.debug('tokenUsds', tokenItems, type)
  156. return {
  157. nativeUsd: balances.native.usdPrice,
  158. tokenUsds: tokenItems,
  159. totalUsd: addUsds(tokenItems, 'token_balance')
  160. }
  161. }
  162. function createArrays(arrs) {
  163. return arrs ? arrs : []
  164. }
  165. async function getAllBalanceV2() {
  166. var company = await moralis.queryCompanyInfoFromId(0)
  167. logger.info('getAllBalance company', company)
  168. var bsc_balances = await findCurBalance('bsc', company.user_address)
  169. var czz_balances = await findCurBalance('czz', company.user_address)
  170. var kcc_balances = await findCurBalance('kcc_mainnet', company.user_address)
  171. var okc_balances = await findCurBalance('okc_mainnet', company.user_address)
  172. logger.info('findCurBalance bsc_balances', bsc_balances)
  173. logger.info('findCurBalance czz_balances', czz_balances)
  174. logger.info('findCurBalance kcc_balances', kcc_balances)
  175. logger.info('findCurBalance okc_balances', okc_balances)
  176. bsc_balances.tokenUsds = createArrays(bsc_balances.tokenUsds)
  177. czz_balances.tokenUsds = createArrays(czz_balances.tokenUsds)
  178. kcc_balances.tokenUsds = createArrays(kcc_balances.tokenUsds)
  179. okc_balances.tokenUsds = createArrays(okc_balances.tokenUsds)
  180. var lists = bsc_balances.tokenUsds.concat(czz_balances.tokenUsds)
  181. .concat(kcc_balances.tokenUsds).concat(okc_balances.tokenUsds);
  182. return {
  183. bsc: bsc_balances,
  184. czz: czz_balances,
  185. infos: lists,
  186. totalUsd: bsc_balances.totalUsd + czz_balances.totalUsd + kcc_balances.totalUsd + okc_balances.totalUsd,
  187. totalNativeBalanceUsd: bsc_balances.nativeUsd + czz_balances.nativeUsd + kcc_balances.nativeUsd + okc_balances.nativeUsd
  188. }
  189. }
  190. function parseGas(response, index, price) {
  191. try {
  192. logger.info('parseGas in', response, index)
  193. if (response && Array.isArray(response) && response.length > 0) {
  194. var obj
  195. if (response[index] && typeof response[index] === 'string')
  196. try {
  197. obj = JSON.parse(response[index])
  198. } catch (error) {
  199. logger.error('JSON.parse(response[index])', error.toString)
  200. }
  201. return parseFloat(obj.gasPrice.number) * parseFloat(obj.gasLimit.number) / parseFloat(10 ** 18) * parseFloat(price)
  202. }
  203. else return 0
  204. } catch (error) {
  205. logger.error('parseGas', error.toString(), JSON.stringify(response))
  206. return 0
  207. }
  208. }
  209. async function getPrice(key) {
  210. if (mTokenPriceCache.has(key)) {
  211. price = mTokenPriceCache.get(key)
  212. } else {
  213. var price = await moralis.getAllTotkenPrice({
  214. chain: key
  215. })
  216. if (typeof price === 'string') {
  217. price = JSON.parse(price)
  218. }
  219. mTokenPriceCache.set(key, price)
  220. }
  221. return price
  222. }
  223. async function getPriceFromCache(key, address) {
  224. console.info('getPriceFromCache>', key, address)
  225. var price = await getPrice(key)
  226. // var price = await moralis.getAllTotkenPrice({
  227. // chain: key
  228. // })
  229. // if (typeof price === 'string') {
  230. // price = JSON.parse(price)
  231. // }
  232. var priceItem = moralis.findTokenPriceItem(address, price)
  233. if (priceItem && priceItem.usdPrice) {
  234. return priceItem.usdPrice
  235. } else {
  236. return '0'
  237. }
  238. }
  239. function convertChain(chain) {
  240. return chain
  241. }
  242. function balance2USDPrice(amount, decimals, price) {
  243. return parseFloat(amount) / parseFloat(10 ** decimals) * parseFloat(price)
  244. }
  245. async function getDecimalsFromRedis(chain, address) {
  246. var decimals = 18
  247. try {
  248. var newKey = redis.formatRedisKey('REDIS_ERC20_CONTRACT_DECIMALS', chain, address.toLowerCase())
  249. if (mDecimalsCache.has(newKey)) {
  250. decimals = mDecimalsCache.get(newKey)
  251. } else {
  252. decimals = await redis.readAppendRedis('REDIS_ERC20_CONTRACT_DECIMALS', chain, address.toLowerCase())
  253. mDecimalsCache.set(newKey, decimals)
  254. }
  255. if (!decimals) decimals = '18'
  256. } catch (error) {
  257. logger.error('getDecimalsFromRedis', chain, address, error.toString())
  258. decimals = 18
  259. }
  260. return decimals
  261. }
  262. function addUsds(infos, type) {
  263. var total = 0
  264. if (infos && Array.isArray(infos) && infos.length > 0) {
  265. for (let index = 0; index < infos.length; index++) {
  266. const element = infos[index];
  267. switch (type) {
  268. case 'gas':
  269. // logger.info('addUsds total ', type, element.gasUsd, element, total)
  270. total += element.gasUsd
  271. break
  272. case 'withdraw':
  273. total += element.withdrawUsd
  274. break
  275. case 'collect_coins':
  276. total += element.withdrawUsd
  277. break
  278. case 'slGas':
  279. total += element.slGasUsd
  280. break
  281. case 'token_balance':
  282. total += element.usd
  283. break
  284. case 'native_in_coins':
  285. if (element.token_address && element.token_address == '0x0000000000000000000000000000000000000000')
  286. total += element.inUsd
  287. break
  288. case 'native_token_in_coins':
  289. total += element.inUsd
  290. break
  291. }
  292. }
  293. }
  294. return total
  295. }
  296. async function getWithdrawOutInfoV2(startTime, endTime) {
  297. if (startTime && endTime) {
  298. startTime = new Date(startTime).getTime()
  299. endTime = new Date(endTime).getTime()
  300. }
  301. if (!startTime && endTime) {
  302. startTime = 0
  303. endTime = new Date(endTime).getTime()
  304. }
  305. var withdraw_ret = await withdraw_db.getWidthdrawTotalFee(startTime, endTime)
  306. var withDrawInfos = []
  307. for (let index = 0; index < withdraw_ret.length; index++) {
  308. const trs = withdraw_ret[index];
  309. // console.log('getWithdrawOutInfoV2 element ', trs)
  310. var token_address = trs.contract_address
  311. if (!trs.contract_address)
  312. token_address = '0x0000000000000000000000000000000000000000'
  313. try {
  314. var input = {
  315. dt: utils.chinaTimeMs(trs.update_time),
  316. user_address: trs.to_address,
  317. token_address: token_address,
  318. chain: convertChain(trs.chain_id + ""),
  319. amount: trs.amount,
  320. decimals: await getDecimalsFromRedis(convertChain(trs.chain_id + ""), token_address),
  321. price: await getPriceFromCache(convertChain(trs.chain_id + ""), token_address),
  322. gasUsd: parseFloat(trs.gas_price) * parseFloat(trs.gas_limit) / parseFloat(10 ** 18) * await getPriceFromCache(convertChain(trs.chain_id + ""), '0x0000000000000000000000000000000000000000')
  323. }
  324. if (!input.gasUsd) {
  325. logger.info('withdraw_ret input ', trs, input)
  326. input.gasUsd = 0
  327. }
  328. //换算成美元
  329. input.withdrawUsd = balance2USDPrice(input.amount, input.decimals, input.price)
  330. console.log('getWithdrawOutInfoV2 input ', input)
  331. withDrawInfos.push(input)
  332. } catch (error) {
  333. logger.error('getWithdrawOutInfoV2 trs', error.toString())
  334. }
  335. }
  336. // logger.log('getWithdrawOutInfoV2 totalOutGasFeeUsd addUsds', addUsds(withDrawInfos, 'gas'), withDrawInfos[0], withDrawInfos[1])
  337. return {
  338. infos: withDrawInfos,
  339. totalOutGasFeeUsd: addUsds(withDrawInfos, 'gas'),
  340. totalWithdrawUsd: addUsds(withDrawInfos, 'withdraw'),
  341. }
  342. }
  343. async function getCollectCoinsOutInfoV2(startTime, endTime) {
  344. var collect_ret = await collect_coins_db.query_collect_total_fee(startTime, endTime);
  345. // console.log('getCollectCoinsOutInfoV2 collect_ret', collect_ret.results.length)
  346. //每笔入金的详细信息
  347. var infos = []
  348. //入金充值的 gas 和实际消费的 gas
  349. var inGasFeeInfo = []
  350. if (collect_ret && collect_ret.results && Array.isArray(collect_ret.results) && collect_ret.results.length > 0) {
  351. for (let index = 0; index < collect_ret.results.length; index++) {
  352. var element = collect_ret.results[index]
  353. if (!element.chain)
  354. element.chain = 'bsc_mainnet'
  355. var update_tm = element.update_time
  356. var user_address = element.user_address
  357. // console.log('getCollectCoinsOutInfoV2 element', element.before_gas_fee, element.resposes, typeof element.resposes, JSON.parse(element.resposes))
  358. var resposes;
  359. if (element.resposes && typeof element.resposes === 'string') {
  360. try {
  361. resposes = JSON.parse(element.resposes)
  362. } catch (error) {
  363. logger.error('element.response parse', error.toString())
  364. }
  365. }
  366. if (element.prestore_gas_fee && typeof element.prestore_gas_fee === 'string') {
  367. try {
  368. var gasObj = JSON.parse(element.prestore_gas_fee)
  369. var before_gas_fee = element.before_gas_fee ? element.before_gas_fee : '0'
  370. if (gasObj) {
  371. // logger.log('element.prestore_gas_fee parse', before_gas_fee, gasObj, gasObj.chain)
  372. var newGasObj = {
  373. chain: convertChain(gasObj.chain),
  374. amount: gasObj.amount,
  375. useGas: before_gas_fee,
  376. price: await getPriceFromCache(convertChain(gasObj.chain), '0x0000000000000000000000000000000000000000'),
  377. }
  378. //实际充值手续费用到的 usd
  379. newGasObj.gasUsd = balance2USDPrice(newGasObj.useGas, 18, newGasObj.price)
  380. //散落 gas
  381. newGasObj.slGasUsd = balance2USDPrice(parseFloat(newGasObj.amount) - parseFloat(newGasObj.useGas), 18, newGasObj.price)
  382. inGasFeeInfo.push(newGasObj)
  383. }
  384. } catch (error) {
  385. logger.error('element.prestore_gas_fee parse', error.toString())
  386. }
  387. }
  388. if (element.transfers && typeof element.transfers === 'string') {
  389. try {
  390. var trss = JSON.parse(element.transfers)
  391. // console.log('trss.transfers', trss.chain)
  392. for (let index = 0; index < trss.length; index++) {
  393. const trs = trss[index];
  394. var address = trs.contractAddress == null ? '0x0000000000000000000000000000000000000000' : trs.contractAddress
  395. console.log('trss.transfers', address, trs)
  396. var input = {
  397. dt: update_tm,
  398. user_address: user_address,
  399. token_address: address,
  400. chain: convertChain(trs.chain),
  401. amount: trs.amount,
  402. decimals: trs.contractAddress == null ? 18 : await getDecimalsFromRedis(convertChain(trs.chain), trs.contractAddress),
  403. price: await getPriceFromCache(convertChain(trs.chain), address),
  404. gasUsd: parseGas(resposes, index, await getPriceFromCache(convertChain(trs.chain), '0x0000000000000000000000000000000000000000')) //入金 gas 手续费
  405. }
  406. //换算成美元
  407. input.inUsd = balance2USDPrice(input.amount, input.decimals, input.price)
  408. infos.push(input)
  409. }
  410. } catch (error) {
  411. logger.error('transfers handle error', error.toString())
  412. }
  413. }
  414. }
  415. }
  416. return {
  417. infos: infos,
  418. totalNativeInFee: addUsds(infos, 'native_in_coins'), //总 native 入金
  419. totalInFee: addUsds(infos, 'native_token_in_coins'), //总入金
  420. totalInGasFeeUsd: addUsds(infos, 'gas') + addUsds(inGasFeeInfo, 'gas'),//总入金消耗的 gas 包含打 gas fee
  421. slTotalGasFeeUsd: addUsds(inGasFeeInfo, 'slGas') //散落 gas
  422. }
  423. }
  424. async function getServerData(startTime, endTime) {
  425. //拿到所有归集 list
  426. var collectCoinsInfos = await getCollectCoinsOutInfoV2(startTime, endTime)
  427. // console.log('getCollectCoinsOutInfoV2 collectCoinsInfos ', collectCoinsInfos)
  428. //拿到所有出金
  429. var withdrawInfos = await getWithdrawOutInfoV2(startTime, endTime)
  430. // console.log('getWithdrawOutInfoV2 withdrawInfos ', withdrawInfos)
  431. return {
  432. collectCoinsInfos: collectCoinsInfos,
  433. withdrawInfos: withdrawInfos
  434. }
  435. }
  436. function sortList(lists) {
  437. lists.sort((a, b) => {
  438. let t1 = new Date(a.dt)
  439. let t2 = new Date(b.dt)
  440. return t2.getTime() - t1.getTime()
  441. })
  442. return lists
  443. }
  444. function formatTableData(type, datas) {
  445. var arrs = []
  446. for (let index = 0; index < datas.length; index++) {
  447. const element = datas[index];
  448. switch (type) {
  449. case 'incoins':
  450. arrs.push([element.dt,
  451. element.user_address,
  452. element.token_address,
  453. element.chain,
  454. element.amount,
  455. element.decimals,
  456. element.price,
  457. element.gasUsd,
  458. element.inUsd
  459. ])
  460. break;
  461. case 'outcoins':
  462. arrs.push([element.dt,
  463. element.user_address,
  464. element.token_address,
  465. element.chain,
  466. element.amount,
  467. element.decimals,
  468. element.price,
  469. element.gasUsd,
  470. element.withdrawUsd
  471. ])
  472. break
  473. case 'balances':
  474. arrs.push([element.address,
  475. element.token_address,
  476. element.chain,
  477. element.amount,
  478. element.decimals,
  479. element.price,
  480. element.usd
  481. ])
  482. break
  483. default:
  484. break;
  485. }
  486. }
  487. logger.info('formatTableData', arrs)
  488. return arrs
  489. }
  490. async function getStatisticsInfoV2(day) {
  491. // //今日
  492. var startTime = utils.getLastDay(day, 'YYYY-MM-DD') + " 00:00:00"
  493. var endTime = utils.getLastDay(day, 'YYYY-MM-DD') + " 23:59:59"
  494. logger.info('getTotalOutGasFee', startTime, endTime)
  495. var rangeData = await getServerData(startTime, endTime)
  496. logger.info('getServerData rangeData', rangeData)
  497. var allData = await getServerData(null, endTime)
  498. // var allData = rangeData
  499. logger.info('getServerData allData', allData)
  500. var data = await http_request_get(utils.getLastDay(day, 'YYYYMMDD'))
  501. logger.info('http_request_get data', data)
  502. //获取当前账户总余额
  503. var curBalances = await getAllBalanceV2()
  504. logger.info('getAllBalanceV2 curBalances', curBalances)
  505. return {
  506. updateTime: utils.getLastDay(day, 'YYYY-MM-DD'),
  507. todayTotalProfit: parseFloat(data.data.incomeUSDTotal) - parseFloat(rangeData.collectCoinsInfos.totalInGasFeeUsd + rangeData.withdrawInfos.totalOutGasFeeUsd),//今日收入
  508. todayTotalOutGasFee: rangeData.collectCoinsInfos.totalInGasFeeUsd + rangeData.withdrawInfos.totalOutGasFeeUsd,//今日总支出的 gas fee
  509. canNotWithdrawUSD: parseFloat(data.data.canNotWithdrawUSD), //不可提现余额
  510. canWithdrawUSD: parseFloat(data.data.canWithdrawUSD), //可提现余额
  511. todayIncomeUSDTotal: parseFloat(data.data.incomeUSDTotal), //今日总收入
  512. todayIncomeUSDFee: parseFloat(data.data.incomeUSDFee), //今日固定收入
  513. totalOutGasFee: allData.collectCoinsInfos.totalInGasFeeUsd + allData.withdrawInfos.totalOutGasFeeUsd, //总支出 gas fee
  514. totalWithdrawGasFee: allData.withdrawInfos.totalOutGasFeeUsd, //总提币 gas fee
  515. totalCollectCoinsGasFee: allData.collectCoinsInfos.totalInGasFeeUsd, //总归集 gas fee
  516. totalInFee: allData.collectCoinsInfos.totalInFee, //总入金
  517. totalNativeInFee: allData.collectCoinsInfos.totalNativeInFee, //总 native 入金
  518. totalOutFee: allData.withdrawInfos.totalWithdrawUsd, //总出金
  519. totalBalances: curBalances.totalUsd, //总余额
  520. ylGasBalance: curBalances.totalNativeBalanceUsd - allData.collectCoinsInfos.totalNativeInFee, //预留 gas 费余额 native 总余额 - 总入金
  521. slGasBalance: allData.collectCoinsInfos.slTotalGasFeeUsd, //散落 gas 费余额 充值 0.5 gas - 使用 0.3 gas= 散落 0.2gas
  522. todayInUsdLists: sortList(rangeData.collectCoinsInfos.infos),//入金列表
  523. todayOutUsdLists: sortList(rangeData.withdrawInfos.infos),//出金列表
  524. totalInUsdLists: sortList(allData.collectCoinsInfos.infos),//总入金列表
  525. totalOutUsdLists: sortList(allData.withdrawInfos.infos),//总出金列表
  526. totalBalanceLists: curBalances.infos //总余额
  527. }
  528. }
  529. const request = async (url, app_token, data) => {
  530. logger.info('request', url, app_token, data)
  531. return new Promise(resolve => {
  532. axios.post(url,
  533. JSON.stringify(data),
  534. {
  535. timeout: 1 * 60 * 1000,
  536. headers: {
  537. 'Content-Type': "application/json; charset=utf-8",
  538. 'Authorization': 'Bearer ' + app_token
  539. }
  540. })
  541. .then(res => {
  542. console.log('res=>', res.status, res.data);
  543. resolve(res.data)
  544. }).catch(err => {
  545. logger.error('error ', JSON.stringify(err));
  546. resolve(JSON.stringify(err))
  547. });
  548. })
  549. }
  550. const getFeishuToken = async (params) => {
  551. return new Promise(resolve => {
  552. axios.post(feishu_write_table_token_url,
  553. {
  554. app_id: "cli_a223f015abbad00e",
  555. app_secret: "DMCF6tBwIpeOQPnWrFUMYd6tmjb53C4n"
  556. },
  557. {
  558. timeout: 1 * 60 * 1000,
  559. headers: {
  560. 'Content-Type': "application/json; charset=utf-8"
  561. }
  562. })
  563. .then(res => {
  564. logger.log('getFeishuToken res=>', res.status, res.data);
  565. resolve(res.data)
  566. }).catch(err => {
  567. logger.error('getFeishuToken error ', JSON.stringify(err));
  568. resolve(JSON.stringify(err))
  569. });
  570. })
  571. }
  572. function formatTableRangle(id, size) {
  573. var newId = id + size
  574. logger.info('formatTableRangle', id, size, newId)
  575. return newId
  576. }
  577. async function writeTable(app_token, data) {
  578. logger.info('writeTable', data)
  579. var valueRanges = []
  580. if (data.todayInUsdLists.length > 0) {
  581. await insertTableRows(app_token, 'Ji1hLG', 1, data.todayInUsdLists.length + 1)
  582. valueRanges.push({//入金汇总
  583. 'range': formatTableRangle('Ji1hLG!A2:I', data.todayInUsdLists.length + 1),
  584. 'values': formatTableData('incoins', data.todayInUsdLists)
  585. })
  586. }
  587. if (data.todayOutUsdLists.length > 0) {
  588. await insertTableRows(app_token, 'aFCrrP', 1, data.todayOutUsdLists.length + 1)
  589. valueRanges.push({//出金汇总
  590. 'range': formatTableRangle('aFCrrP!A2:I', data.todayOutUsdLists.length + 1),
  591. 'values': formatTableData('outcoins', data.todayOutUsdLists)
  592. })
  593. }
  594. if (data.totalBalanceLists.length > 0) {
  595. var rows = await getTableRows(app_token, 2)
  596. if (rows > 1) {
  597. logger.info('getTableRows', rows)
  598. await delTableRows(app_token, '2hNaot', 2, rows)
  599. await addTableRows(app_token, '2hNaot', rows)
  600. }
  601. valueRanges.push({ //总余额汇总
  602. 'range': formatTableRangle('2hNaot!A2:I', data.totalBalanceLists.length + 1),
  603. 'values': formatTableData('balances', data.totalBalanceLists)
  604. })
  605. }
  606. valueRanges.push({//归集汇总
  607. 'range': formatTableRangle('0pRQpu!A2:C', 2),
  608. 'values': [
  609. [data.totalCollectCoinsGasFee, //归集总 gas
  610. data.totalWithdrawGasFee, //提币总 gas
  611. data.totalOutGasFee], //总支出 gas
  612. ]
  613. })
  614. valueRanges.push({//总入账
  615. 'range': formatTableRangle('1ygrMB!A2:B', 2),
  616. 'values': [
  617. [
  618. data.totalInFee, //总入金
  619. data.totalOutFee,//总出金
  620. ]
  621. ]
  622. })
  623. valueRanges.push({//利润表单
  624. 'range': formatTableRangle('BMjMDr!A3:J', 3),
  625. 'values': [
  626. [
  627. data.updateTime, //更新时间
  628. data.todayTotalProfit,//今日总利润
  629. data.todayIncomeUSDTotal,//今日总收入
  630. data.todayIncomeUSDFee,//今日固定手续费收入
  631. data.todayTotalOutGasFee,//今日总 gas 支出
  632. data.totalBalances, //总余额
  633. data.canNotWithdrawUSD, //不可提现余额
  634. data.canWithdrawUSD,//可提现余额
  635. data.ylGasBalance,//预留 gas
  636. data.slGasBalance,//散落 gas
  637. ],
  638. ]
  639. })
  640. var body = {
  641. 'valueRanges': valueRanges
  642. }
  643. return new Promise(resolve => {
  644. axios.post(feishu_write_table_data_url,
  645. body,
  646. {
  647. timeout: 1 * 60 * 1000,
  648. headers: {
  649. 'Content-Type': "application/json; charset=utf-8",
  650. 'Authorization': 'Bearer ' + app_token
  651. }
  652. })
  653. .then(res => {
  654. logger.log('writeTable res=>', res.status, res.data);
  655. resolve(res.data)
  656. }).catch(err => {
  657. logger.error('writeTable error ', JSON.stringify(err));
  658. resolve(JSON.stringify(err))
  659. });
  660. })
  661. }
  662. async function getTableRows(app_token, index) {
  663. return new Promise(resolve => {
  664. axios.get(feishu_get_table_metadata_url,
  665. {
  666. timeout: 1 * 60 * 1000,
  667. headers: {
  668. 'Content-Type': "application/json; charset=utf-8",
  669. 'Authorization': 'Bearer ' + app_token
  670. }
  671. })
  672. .then(res => {
  673. console.log('res=>', res.status, res.data, res.data.data.sheets);
  674. resolve(res.data.data.sheets[index].rowCount)
  675. }).catch(err => {
  676. logger.error('error ', JSON.stringify(err));
  677. resolve(0)
  678. });
  679. })
  680. }
  681. async function delTableRows(app_token, sheetId, startIndex, endIndex) {
  682. var body = {
  683. dimension: {
  684. sheetId: sheetId,
  685. majorDimension: 'ROWS',
  686. startIndex: startIndex,
  687. endIndex: endIndex,
  688. },
  689. }
  690. return new Promise(resolve => {
  691. axios.delete(feishu_delete_table_url,
  692. {
  693. data: body,
  694. timeout: 1 * 60 * 1000,
  695. headers: {
  696. 'Content-Type': "application/json; charset=utf-8",
  697. 'Authorization': 'Bearer ' + app_token
  698. }
  699. })
  700. .then(res => {
  701. console.log('delTableRows res=>', res.status, res.data);
  702. resolve(res.data)
  703. }).catch(err => {
  704. logger.error('delTableRows error ', JSON.stringify(err));
  705. resolve(JSON.stringify(err))
  706. });
  707. })
  708. }
  709. async function insertTableRows(app_token, sheetId, startIndex, endIndex) {
  710. logger.info('insertTableRows', app_token, sheetId, startIndex, endIndex)
  711. var body = {
  712. dimension: {
  713. sheetId: sheetId,
  714. majorDimension: 'ROWS',
  715. startIndex: startIndex,
  716. endIndex: endIndex,
  717. },
  718. inheritStyle: 'AFTER'
  719. }
  720. return new Promise(resolve => {
  721. axios.post(feishu_insert_table_url,
  722. JSON.stringify(body),
  723. {
  724. timeout: 1 * 60 * 1000,
  725. headers: {
  726. 'Content-Type': "application/json; charset=utf-8",
  727. 'Authorization': 'Bearer ' + app_token
  728. }
  729. })
  730. .then(res => {
  731. console.log('res=>', res.status, res.data);
  732. resolve(res.data)
  733. }).catch(err => {
  734. logger.error('error ', JSON.stringify(err));
  735. resolve(JSON.stringify(err))
  736. });
  737. })
  738. }
  739. async function addTableRows(app_token, sheetId, endIndex) {
  740. var body = {
  741. dimension: {
  742. sheetId: sheetId,
  743. majorDimension: 'ROWS',
  744. length: endIndex,
  745. },
  746. }
  747. return new Promise(resolve => {
  748. axios.post(feishu_create_table_url,
  749. JSON.stringify(body),
  750. {
  751. timeout: 1 * 60 * 1000,
  752. headers: {
  753. 'Content-Type': "application/json; charset=utf-8",
  754. 'Authorization': 'Bearer ' + app_token
  755. }
  756. })
  757. .then(res => {
  758. console.log('res=>', res.status, res.data);
  759. resolve(res.data)
  760. }).catch(err => {
  761. logger.error('error ', JSON.stringify(err));
  762. resolve(JSON.stringify(err))
  763. });
  764. })
  765. }
  766. async function exec(data, app) {
  767. await insertTableRows(app.app_access_token, 'BMjMDr', 2, 3)
  768. return await writeTable(app.app_access_token, data)
  769. }
  770. async function notify(app, chain_id) {
  771. 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:\"}]]}}"
  772. var data = {
  773. "receive_id": chain_id,
  774. "content": content,
  775. "msg_type": "post"
  776. }
  777. await request('https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id', app.app_access_token, data)
  778. }
  779. async function report2FeishuTable(day) {
  780. try {
  781. logger.error('数据统计 start')
  782. logger.info('report2FeishuTable')
  783. var data = await getStatisticsInfoV2(day);
  784. // data = ''
  785. logger.info('getStatisticsInfo', data)
  786. var app = await getFeishuToken()
  787. var ret = await exec(data, app)
  788. notify(app, 'oc_9770d283cd81df887e96795ddfcd9dcd')
  789. logger.error('数据统计完成:', 'https://st94nif1cq.feishu.cn/sheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg?sheet=BMjMDr')
  790. mTokenPriceCache.clear()
  791. } catch (error) {
  792. logger.error('report2FeishuTable', error.toString())
  793. }
  794. }
  795. async function testNotify() {
  796. // var app = await getFeishuToken()
  797. // notify(app, 'oc_b159b9a53dd52206c5cf75677d0cf0ca')
  798. //获取当前账户总余额
  799. var curBalances = await getAllBalanceV2()
  800. logger.info('getAllBalanceV2 curBalances', curBalances)
  801. }
  802. async function test() {
  803. // var ret = await getStatisticsInfoV2(2)
  804. // logger.debug('getStatisticsInfoV2', await getStatisticsInfoV2(2), mTokenPriceCache.size)
  805. // for (let index = 40; index >=0; index--) {
  806. await report2FeishuTable(1)
  807. // }
  808. }
  809. // test()
  810. // exec()
  811. // testNotify()
  812. module.exports = {
  813. report2FeishuTable
  814. }