|
@@ -1,8 +1,10 @@
|
|
|
using JLHHJSvr.Com.Model;
|
|
|
using JLHHJSvr.LJException;
|
|
|
using JLHHJSvr.LJFramework.Tools;
|
|
|
+using NPOI.SS.Formula;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Data;
|
|
|
using System.Linq;
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
@@ -10,8 +12,8 @@ namespace JLHHJSvr.Tools
|
|
|
{
|
|
|
public class CalculateFormula
|
|
|
{
|
|
|
- private HashSet<FormulaItem> _formula;
|
|
|
- private HashSet<FormulaItem> _const;
|
|
|
+ private HashSet<FormulaItem> _formula; // 公式
|
|
|
+ private HashSet<FormulaItem> _const; // 常量
|
|
|
|
|
|
public CalculateFormula()
|
|
|
{
|
|
@@ -43,27 +45,110 @@ namespace JLHHJSvr.Tools
|
|
|
|
|
|
public void AddFormulaItem(string name, string formula)
|
|
|
{
|
|
|
- var formulaItem = new FormulaItem() { formula_name = name, formula = formula }
|
|
|
+ var formulaItem = new FormulaItem() { formula_name = name, formula = formula };
|
|
|
AddFormulaItem(formulaItem);
|
|
|
+ }
|
|
|
|
|
|
- BuildFormulaGraph(formulaItem);
|
|
|
+ /// <summary>
|
|
|
+ /// 主计算入口
|
|
|
+ /// </summary>
|
|
|
+ public void CalculateAll()
|
|
|
+ {
|
|
|
+ var dict = _formula.ToDictionary(f => f.formula_name, f => f);
|
|
|
+ foreach (var c in _const)
|
|
|
+ {
|
|
|
+ dict[c.formula_name] = c;
|
|
|
+ }
|
|
|
+
|
|
|
+ var graph = BuildDependencyGraph(dict);
|
|
|
+ var sorted = TopologicalSort(graph);
|
|
|
+ foreach (var name in sorted)
|
|
|
+ {
|
|
|
+ var item = dict[name];
|
|
|
+ item.formula = ConvertToEnglishSymbols(item.formula);
|
|
|
+ // 展开
|
|
|
+ string expanded = ExpandFormula(item.formula, dict);
|
|
|
+ item.formula_transform = expanded;
|
|
|
+ // 计算
|
|
|
+ if (item.isSql) SqlCalculate(item);
|
|
|
+ else Calculate(item);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 构建拓扑图
|
|
|
- private void BuildFormulaGraph(FormulaItem formula)
|
|
|
+ /// <summary>
|
|
|
+ /// 解析依赖
|
|
|
+ /// </summary>
|
|
|
+ private Dictionary<string, List<string>> BuildDependencyGraph(Dictionary<string, FormulaItem> dict)
|
|
|
{
|
|
|
- formula.graph = new List<string>();
|
|
|
- // 定义正则表达式模式,匹配包含【】的内容
|
|
|
- string pattern = @"【(.*?)】";
|
|
|
- // 创建正则表达式对象
|
|
|
- Regex regex = new Regex(pattern);
|
|
|
- // 使用正则表达式匹配输入字符串
|
|
|
- MatchCollection matches = regex.Matches(formula.formula);
|
|
|
-
|
|
|
- foreach (Match match in matches)
|
|
|
+ var graph = new Dictionary<string, List<string>>();
|
|
|
+
|
|
|
+ foreach (var kv in dict)
|
|
|
{
|
|
|
- formula.graph.Add(match.Value);
|
|
|
+ var deps = new List<string>();
|
|
|
+
|
|
|
+ foreach (var name in dict.Keys)
|
|
|
+ {
|
|
|
+ if (name == kv.Key) continue;
|
|
|
+ if (Regex.IsMatch(kv.Value.formula, name))
|
|
|
+ {
|
|
|
+ deps.Add(name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ graph[kv.Key] = deps;
|
|
|
}
|
|
|
+
|
|
|
+ return graph;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 拓扑排序
|
|
|
+ /// </summary>
|
|
|
+ private List<string> TopologicalSort(Dictionary<string, List<string>> graph)
|
|
|
+ {
|
|
|
+ var result = new List<string>();
|
|
|
+ var visited = new Dictionary<string, int>(); // 0=未访问,1=正在访问,2=已完成
|
|
|
+
|
|
|
+ foreach (var node in graph.Keys)
|
|
|
+ {
|
|
|
+ if (!visited.ContainsKey(node))
|
|
|
+ {
|
|
|
+ Dfs(node, graph, visited, result);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Dfs(string node, Dictionary<string, List<string>> graph,Dictionary<string, int> visited, List<string> result)
|
|
|
+ {
|
|
|
+ if (visited.ContainsKey(node) && visited[node] == 1) throw new InvalidOperationException($"检测到循环依赖: {node}");
|
|
|
+
|
|
|
+ if (visited.ContainsKey(node) && visited[node] == 2) return;
|
|
|
+
|
|
|
+ visited[node] = 1;
|
|
|
+ foreach (var dep in graph[node])
|
|
|
+ {
|
|
|
+ Dfs(dep, graph, visited, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ visited[node] = 2;
|
|
|
+ result.Add(node);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 展开公式,把变量替换为已计算的值
|
|
|
+ /// </summary>
|
|
|
+ private string ExpandFormula(string formula, Dictionary<string, FormulaItem> dict)
|
|
|
+ {
|
|
|
+ foreach (var kv in dict)
|
|
|
+ {
|
|
|
+ if (kv.Value.value != null) // 已经算过
|
|
|
+ {
|
|
|
+ formula = Regex.Replace(formula, $"【{kv.Key}】", kv.Value.value.ToString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return formula;
|
|
|
}
|
|
|
|
|
|
private string ConvertToEnglishSymbols(string input)
|
|
@@ -81,10 +166,6 @@ namespace JLHHJSvr.Tools
|
|
|
|
|
|
return input;
|
|
|
}
|
|
|
- private void FormatExpression(FormulaItem formula)
|
|
|
- {
|
|
|
- formula.formula = ConvertToEnglishSymbols(formula.formula);
|
|
|
- }
|
|
|
/// <summary>
|
|
|
/// 公式计算
|
|
|
/// </summary>
|
|
@@ -93,8 +174,6 @@ namespace JLHHJSvr.Tools
|
|
|
/// <returns></returns>
|
|
|
private void Calculate(FormulaItem formula)
|
|
|
{
|
|
|
- FormatExpression(formula);
|
|
|
-
|
|
|
try
|
|
|
{
|
|
|
formula.value = LJExprParser.Parse(formula.formula).Result;
|
|
@@ -112,8 +191,6 @@ namespace JLHHJSvr.Tools
|
|
|
/// <returns></returns>
|
|
|
private void SqlCalculate(FormulaItem formula)
|
|
|
{
|
|
|
- FormatExpression(formula);
|
|
|
-
|
|
|
try
|
|
|
{
|
|
|
//cmd.CommandText = $@"{formula.formula}";
|