CalculateFormula.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. using JLHHJSvr.Com.Model;
  2. using JLHHJSvr.LJException;
  3. using JLHHJSvr.LJFramework.Tools;
  4. using NPOI.SS.Formula;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Data;
  8. using System.Linq;
  9. using System.Text.RegularExpressions;
  10. namespace JLHHJSvr.Tools
  11. {
  12. public class CalculateFormula
  13. {
  14. private Dictionary<string,FormulaItem> _formula; // 公式
  15. public CalculateFormula()
  16. {
  17. _formula = new Dictionary<string, FormulaItem>();
  18. }
  19. public void AddFormulaItem(FormulaItem item)
  20. {
  21. if(_formula.ContainsKey(item.formula_name))
  22. {
  23. _formula.Remove(item.formula_name);
  24. }
  25. _formula.Add(item.formula_name, item);
  26. }
  27. public void AddFormulaItem(string name,decimal value)
  28. {
  29. AddFormulaItem(new FormulaItem() { formula_name = name, value = value,isConst = true });
  30. }
  31. public void AddFormulaItem(string name, string formula)
  32. {
  33. AddFormulaItem(new FormulaItem() { formula_name = name, formula = formula, isConst = false });
  34. }
  35. public FormulaItem GetFormulaItem(string name,bool isConst = false)
  36. {
  37. return _formula[name];
  38. }
  39. /// <summary>
  40. /// 主计算入口
  41. /// </summary>
  42. public void CalculateAll()
  43. {
  44. var graph = BuildDependencyGraph(_formula);
  45. var sorted = TopologicalSort(graph);
  46. foreach (var name in sorted)
  47. {
  48. var item = _formula[name];
  49. // 常量直接赋值
  50. if (item.isConst)
  51. {
  52. //item.formula_transform = item.formula;
  53. //item.value = Convert.ToDecimal(item.formula);
  54. continue;
  55. }
  56. // 变量公式
  57. item.formula = ConvertToEnglishSymbols(item.formula);
  58. string expanded = ExpandFormula(item.formula, _formula);
  59. item.formula_transform = expanded;
  60. // 计算
  61. if (item.isSql) SqlCalculate(item);
  62. else Calculate(item);
  63. }
  64. }
  65. /// <summary>
  66. /// 解析依赖
  67. /// </summary>
  68. private Dictionary<string, List<string>> BuildDependencyGraph(Dictionary<string, FormulaItem> dict)
  69. {
  70. var graph = new Dictionary<string, List<string>>();
  71. foreach (var kv in dict)
  72. {
  73. var deps = new List<string>();
  74. foreach (var name in dict.Keys)
  75. {
  76. if (name == kv.Key || kv.Value.isConst) continue;
  77. if (Regex.IsMatch(kv.Value.formula, name))
  78. {
  79. deps.Add(name);
  80. }
  81. }
  82. graph[kv.Key] = deps;
  83. }
  84. return graph;
  85. }
  86. /// <summary>
  87. /// 拓扑排序
  88. /// </summary>
  89. private List<string> TopologicalSort(Dictionary<string, List<string>> graph)
  90. {
  91. var result = new List<string>();
  92. var visited = new Dictionary<string, int>(); // 0=未访问,1=正在访问,2=已完成
  93. foreach (var node in graph.Keys)
  94. {
  95. if (!visited.ContainsKey(node))
  96. {
  97. Dfs(node, graph, visited, result);
  98. }
  99. }
  100. return result;
  101. }
  102. private void Dfs(string node, Dictionary<string, List<string>> graph,Dictionary<string, int> visited, List<string> result)
  103. {
  104. if (visited.ContainsKey(node) && visited[node] == 1) throw new InvalidOperationException($"检测到循环依赖: {node}");
  105. if (visited.ContainsKey(node) && visited[node] == 2) return;
  106. visited[node] = 1;
  107. foreach (var dep in graph[node])
  108. {
  109. Dfs(dep, graph, visited, result);
  110. }
  111. visited[node] = 2;
  112. result.Add(node);
  113. }
  114. /// <summary>
  115. /// 展开公式,把变量替换为已计算的值
  116. /// </summary>
  117. private string ExpandFormula(string formula, Dictionary<string, FormulaItem> dict)
  118. {
  119. foreach (var kv in dict)
  120. {
  121. formula = Regex.Replace(formula, kv.Key, kv.Value.value.ToString());
  122. }
  123. return formula;
  124. }
  125. private string ConvertToEnglishSymbols(string input)
  126. {
  127. input = input.Replace("(", "(")
  128. .Replace(")", ")")
  129. .Replace(",", ",")
  130. .Replace("。", ".")
  131. .Replace(":", ":")
  132. .Replace(";", ";")
  133. .Replace("“", "\"")
  134. .Replace("”", "\"")
  135. .Replace("‘", "'")
  136. .Replace("’", "'");
  137. return input;
  138. }
  139. /// <summary>
  140. /// 公式计算
  141. /// </summary>
  142. /// <param name="expression"></param>
  143. /// <param name="name"></param>
  144. /// <returns></returns>
  145. private void Calculate(FormulaItem formula)
  146. {
  147. try
  148. {
  149. formula.value = LJExprParser.Parse(formula.formula_transform).Result.DecimalValue;
  150. formula.isConst = true;
  151. }
  152. catch (Exception ex)
  153. {
  154. throw new LJCommonException($"计算{formula.formula_name}公式错误: {ex.Message}");
  155. }
  156. }
  157. /// <summary>
  158. /// Sql公式计算
  159. /// </summary>
  160. /// <param name="expression"></param>
  161. /// <param name="name"></param>
  162. /// <returns></returns>
  163. private void SqlCalculate(FormulaItem formula)
  164. {
  165. try
  166. {
  167. //cmd.CommandText = $@"{formula.formula}";
  168. //cmd.Parameters.Clear();
  169. //return cmd.ExecuteScalar();
  170. }
  171. catch (Exception ex)
  172. {
  173. throw new LJCommonException($"计算{formula.formula_name}公式错误: {ex.Message}");
  174. }
  175. }
  176. }
  177. }