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. _formula.Add(item.formula_name,item);
  25. }
  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. var formulaItem = new FormulaItem() { formula_name = name, formula = formula,isConst = false };
  34. AddFormulaItem(formulaItem);
  35. }
  36. public FormulaItem GetFormulaItem(string name,bool isConst = false)
  37. {
  38. return _formula[name];
  39. }
  40. /// <summary>
  41. /// 主计算入口
  42. /// </summary>
  43. public void CalculateAll()
  44. {
  45. var graph = BuildDependencyGraph(_formula);
  46. var sorted = TopologicalSort(graph);
  47. foreach (var name in sorted)
  48. {
  49. var item = _formula[name];
  50. item.formula = ConvertToEnglishSymbols(item.formula);
  51. // 常量直接赋值
  52. if (item.isConst)
  53. {
  54. item.formula_transform = item.formula;
  55. item.value = Convert.ToDecimal(item.formula);
  56. continue;
  57. }
  58. // 变量公式
  59. string expanded = ExpandFormula(item.formula, _formula);
  60. item.formula_transform = expanded;
  61. // 计算
  62. if (item.isSql) SqlCalculate(item);
  63. else Calculate(item);
  64. }
  65. }
  66. /// <summary>
  67. /// 解析依赖
  68. /// </summary>
  69. private Dictionary<string, List<string>> BuildDependencyGraph(Dictionary<string, FormulaItem> dict)
  70. {
  71. var graph = new Dictionary<string, List<string>>();
  72. foreach (var kv in dict)
  73. {
  74. var deps = new List<string>();
  75. foreach (var name in dict.Keys)
  76. {
  77. if (name == kv.Key) continue;
  78. if (Regex.IsMatch(kv.Value.formula, name))
  79. {
  80. deps.Add(name);
  81. }
  82. }
  83. graph[kv.Key] = deps;
  84. }
  85. return graph;
  86. }
  87. /// <summary>
  88. /// 拓扑排序
  89. /// </summary>
  90. private List<string> TopologicalSort(Dictionary<string, List<string>> graph)
  91. {
  92. var result = new List<string>();
  93. var visited = new Dictionary<string, int>(); // 0=未访问,1=正在访问,2=已完成
  94. foreach (var node in graph.Keys)
  95. {
  96. if (!visited.ContainsKey(node))
  97. {
  98. Dfs(node, graph, visited, result);
  99. }
  100. }
  101. return result;
  102. }
  103. private void Dfs(string node, Dictionary<string, List<string>> graph,Dictionary<string, int> visited, List<string> result)
  104. {
  105. if (visited.ContainsKey(node) && visited[node] == 1) throw new InvalidOperationException($"检测到循环依赖: {node}");
  106. if (visited.ContainsKey(node) && visited[node] == 2) return;
  107. visited[node] = 1;
  108. foreach (var dep in graph[node])
  109. {
  110. Dfs(dep, graph, visited, result);
  111. }
  112. visited[node] = 2;
  113. result.Add(node);
  114. }
  115. /// <summary>
  116. /// 展开公式,把变量替换为已计算的值
  117. /// </summary>
  118. private string ExpandFormula(string formula, Dictionary<string, FormulaItem> dict)
  119. {
  120. foreach (var kv in dict)
  121. {
  122. formula = Regex.Replace(formula, $"【{kv.Key}】", kv.Value.value.ToString());
  123. }
  124. return formula;
  125. }
  126. private string ConvertToEnglishSymbols(string input)
  127. {
  128. input = input.Replace("(", "(")
  129. .Replace(")", ")")
  130. .Replace(",", ",")
  131. .Replace("。", ".")
  132. .Replace(":", ":")
  133. .Replace(";", ";")
  134. .Replace("“", "\"")
  135. .Replace("”", "\"")
  136. .Replace("‘", "'")
  137. .Replace("’", "'");
  138. return input;
  139. }
  140. /// <summary>
  141. /// 公式计算
  142. /// </summary>
  143. /// <param name="expression"></param>
  144. /// <param name="name"></param>
  145. /// <returns></returns>
  146. private void Calculate(FormulaItem formula)
  147. {
  148. try
  149. {
  150. formula.value = LJExprParser.Parse(formula.formula_transform).Result.DecimalValue;
  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. }