template_balance.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. use std::collections::BTreeMap;
  2. use std::fs::File;
  3. use std::io::Write;
  4. use std::str::FromStr;
  5. use chrono::NaiveDateTime;
  6. use handlebars::Handlebars;
  7. use rust_decimal::Decimal;
  8. use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
  9. use rust_decimal_macros::dec;
  10. use serde_json::json;
  11. use tracing::info;
  12. use crate::utils::utils::{BalanceConfigInfo};
  13. pub fn export_html(config: BalanceConfigInfo, acc_name_all: &Vec<String>, x_time: &Vec<i64>, data_list: &Vec<Vec<String>>, statistic_result: String) {
  14. info!("正在生成网页,请稍后!");
  15. let export_path = if config.export_path == "" { "./" } else { config.export_path.as_str() };
  16. let export_name = if config.export_name == "" { "export_balance" } else { config.export_name.as_str() };
  17. let path = format!("{}/{}.html", export_path, export_name).replace("//", "/");
  18. // 创建 Handlebars 实例
  19. let mut handlebars = Handlebars::new();
  20. //时间片数据组装
  21. let mut time_str_x = vec![];
  22. for time in x_time {
  23. let time_str = NaiveDateTime::from_timestamp_millis((time + 8 * 3600) * 1000).unwrap().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
  24. time_str_x.push(time_str);
  25. }
  26. //每个账号的数据
  27. let mut map: BTreeMap<String, Vec<f64>> = BTreeMap::new();
  28. let mut capital_info: BTreeMap<String, Decimal> = Default::default();
  29. for acc_name in acc_name_all.clone() {
  30. capital_info.insert(acc_name.parse().unwrap(), dec!(0));
  31. }
  32. for acc_name in acc_name_all.clone() {
  33. let mut pr_data: Vec<f64> = vec![];
  34. let z = acc_name.clone();
  35. let key = z.as_str();
  36. if map.contains_key(key) {
  37. pr_data = map.get(key).unwrap().clone();
  38. }
  39. let mut income = dec!(0);
  40. let mut earnings = dec!(0);
  41. for data in data_list.clone() {
  42. if data[0].clone().as_str() == key {
  43. let pr = Decimal::from_str(&data[4]).unwrap();
  44. if capital_info[key] == dec!(0) || data[5] == "transfer" {
  45. earnings = income;
  46. capital_info.insert(key.parse().unwrap(), pr);
  47. }
  48. income = if capital_info[key] == dec!(0) { dec!(0) } else { ((pr - capital_info[key]) / capital_info[key] * dec!(100.0) + earnings).round_dp(2) };
  49. pr_data.push(f64::try_from(income).unwrap());
  50. }
  51. }
  52. map.insert(key.to_string(), pr_data);
  53. }
  54. let mut series_all = Vec::new();
  55. for (key, val) in map {
  56. let series = json!({"name": key.to_string(), "type": "line", "smooth": true, "data": val});
  57. series_all.push(series);
  58. }
  59. let series_array = if config.is_total { [series_all[series_all.len() - 1].clone()].to_vec() } else { series_all };
  60. let json = json!({
  61. "data":series_array
  62. });
  63. let series_str = format!("{}", json["data"]);
  64. let data = serde_json::json!({
  65. "chart_title": format!("账户合约余额变更历史"),
  66. "acc_name_all": acc_name_all.clone(),
  67. "statistic_result": statistic_result.clone()
  68. });
  69. // HTML 模板
  70. let template = r#"
  71. <!DOCTYPE html>
  72. <html>
  73. <head>
  74. <meta charset="UTF-8">
  75. <title>{{chart_title}}</title>
  76. <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js"></script>
  77. <script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.10/dayjs.min.js"></script>
  78. <style>
  79. * {
  80. margin: 0;
  81. padding: 0;
  82. }
  83. #main {
  84. margin: 50px auto 0;
  85. width: calc(100vw - 100px);
  86. height: calc(100vh - 150px);
  87. }
  88. .info_wp{
  89. padding: 0 40px;
  90. }
  91. </style>
  92. </head>
  93. <body>
  94. <div id="main"></div>
  95. <div class="info_wp">{{{statistic_result}}}</div>
  96. </body>
  97. <script>
  98. var exchangeColor = {binance: '#F4BC0C', gate: '#0068FF', okx: '#171F30'};
  99. var chartDom = document.getElementById('main');
  100. var myChart = echarts.init(chartDom);
  101. var option;
  102. option = {
  103. title: {
  104. text: '{{chart_title}}'
  105. },
  106. tooltip: {
  107. trigger: 'axis'
  108. },
  109. toolbox: {
  110. feature: {
  111. dataZoom: {},
  112. brush: {
  113. type: ['rect', 'clear']
  114. }
  115. }
  116. },
  117. legend: {
  118. data: "#.to_owned() + format!("{:?}", acc_name_all).as_ref() + r#"
  119. },
  120. xAxis: {
  121. type: 'category',
  122. data: "# + format!("{:?}", time_str_x).as_ref() + r#"
  123. },
  124. yAxis: {
  125. type: 'value',
  126. axisLabel: {
  127. formatter: (value, index) => {
  128. return `${value}%`;
  129. }
  130. },
  131. scale: true
  132. },
  133. series: "# + format!("{}", series_str).as_ref() + r#"
  134. };
  135. option && myChart.setOption(option);
  136. </script>
  137. </html>
  138. "#;
  139. // 编译模板
  140. handlebars
  141. .register_template_string("page", template)
  142. .expect("编译模版失败!");
  143. // 渲染模板
  144. let output = handlebars
  145. .render("page", &data)
  146. .expect("渲染模版失败!");
  147. // 将 HTML 写入文件
  148. let mut file = File::create(&path).expect("创建文件失败!");
  149. file.write_all(output.as_bytes()).expect("写入文件到本地失败!");
  150. info!("Balance信息网页生成成功!路径:{:?}\n\n", path);
  151. }