579 lines
19 KiB
C#
579 lines
19 KiB
C#
using AutoMapper;
|
||
using CapMachine.Core;
|
||
using CapMachine.Model;
|
||
using CapMachine.Wpf.Dtos;
|
||
using DynamicExpresso;
|
||
using FreeSql;
|
||
using FreeSql.DataAnnotations;
|
||
using Prism.Mvvm;
|
||
using Syncfusion.Windows.Shared;
|
||
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Collections.ObjectModel;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
|
||
namespace CapMachine.Wpf.Services
|
||
{
|
||
/// <summary>
|
||
/// 逻辑服务 - 用于处理数据逻辑转换
|
||
/// </summary>
|
||
public class LogicRuleService : BindableBase
|
||
{
|
||
private readonly ILogService LogService;
|
||
private readonly IFreeSql FreeSql;
|
||
|
||
/// <summary>
|
||
/// 逻辑转换规则集合
|
||
/// </summary>
|
||
public ObservableCollection<LogicRuleDto> LogicRuleDtos { get; private set; }
|
||
|
||
/// <summary>
|
||
/// DynamicExpresso解释器
|
||
/// </summary>
|
||
private Interpreter CurInterpreter { get; set; }
|
||
public IMapper Mapper { get; }
|
||
|
||
/// <summary>
|
||
/// 规则表达式缓存(高性能访问)
|
||
/// Key: 规则名称, Value: 已编译的Lambda表达式
|
||
///private readonly ConcurrentDictionary<string, Func<double, double>> _expressionCache;
|
||
/// </summary>
|
||
private readonly ConcurrentDictionary<string, Func<double, double>> _expressionCache;
|
||
|
||
///// <summary>
|
||
///// 创建包含指定值的参数数组
|
||
///// </summary>
|
||
//private Parameter[] CreateParameters(double value)
|
||
//{
|
||
// return new Parameter[]
|
||
// {
|
||
// new Parameter("value", typeof(double), value)
|
||
// };
|
||
//}
|
||
|
||
/// <summary>
|
||
/// 创建包含指定值的参数
|
||
/// </summary>
|
||
private Parameter CreateParameters(double value)
|
||
{
|
||
return new Parameter("value", typeof(double), value);
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 实例化函数
|
||
/// </summary>
|
||
public LogicRuleService(ILogService logService, IFreeSql freeSql, IMapper mapper)
|
||
{
|
||
LogService = logService;
|
||
FreeSql = freeSql;
|
||
Mapper = mapper;
|
||
|
||
// 初始化集合
|
||
LogicRuleDtos = new ObservableCollection<LogicRuleDto>();
|
||
|
||
_expressionCache = new ConcurrentDictionary<string, Func<double, double>>();
|
||
|
||
// 初始化DynamicExpresso解释器
|
||
CurInterpreter = new Interpreter();
|
||
|
||
try
|
||
{
|
||
// 从数据库加载规则
|
||
LoadRulesFromDatabase();
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error($"初始化逻辑服务失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从数据库加载规则
|
||
/// </summary>
|
||
private void LoadRulesFromDatabase()
|
||
{
|
||
try
|
||
{
|
||
// 从数据库加载规则
|
||
var dbRules = FreeSql.Select<LogicRule>().OrderBy(a => a.Id).ToList();
|
||
if (dbRules != null && dbRules.Count > 0)
|
||
{
|
||
LogicRuleDtos.Clear();
|
||
_expressionCache.Clear(); // 清空表达式缓存
|
||
|
||
foreach (var rule in dbRules)
|
||
{
|
||
LogicRuleDtos.Add(Mapper.Map<LogicRuleDto>(rule));
|
||
}
|
||
|
||
// 预编译所有表达式
|
||
WarmUpExpressionCache();
|
||
|
||
//LogService.Info($"已从数据库加载并预编译 {dbRules.Count} 条逻辑规则");
|
||
}
|
||
else
|
||
{
|
||
LogService.Warn("数据库中没有逻辑规则");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error($"从数据库加载逻辑规则失败: {ex.Message}");
|
||
throw; // 重新抛出异常以便调用者处理
|
||
}
|
||
}
|
||
|
||
|
||
#region 增删改查LogicRule
|
||
|
||
|
||
/// <summary>
|
||
/// 添加新规则
|
||
/// </summary>
|
||
/// <param name="rule">规则对象</param>
|
||
public void AddRule(LogicRule rule)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(rule.Name))
|
||
{
|
||
MessageBox.Show("规则名称不能为空");
|
||
return;
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(rule.Expression))
|
||
{
|
||
MessageBox.Show("规则表达式不能为空");
|
||
return;
|
||
}
|
||
|
||
if (LogicRuleDtos.Where(a => a.Name == rule.Name).Any())
|
||
{
|
||
MessageBox.Show("已经有另一个相同名称的规则了");
|
||
return;
|
||
}
|
||
|
||
// 验证表达式是否有效
|
||
if (!ValidateExpression(rule.Expression))
|
||
{
|
||
MessageBox.Show("规则表达式验证失败");
|
||
return;
|
||
}
|
||
|
||
if (InsertRuleToDb(rule, out LogicRule resultInsert))
|
||
{
|
||
//此时的resultInsert有新增的ID
|
||
// 添加规则
|
||
LogicRuleDtos.Add(Mapper.Map<LogicRuleDto>(resultInsert));
|
||
|
||
// 预编译并缓存表达式
|
||
CacheExpression(Mapper.Map<LogicRuleDto>(resultInsert));
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("增加数据失败!");
|
||
return;
|
||
}
|
||
//LogService.Info($"添加新规则: {rule.Name}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存规格到数据库中
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public bool InsertRuleToDb(LogicRule rule, out LogicRule ResultInsert)
|
||
{
|
||
try
|
||
{
|
||
// 插入规则到数据库
|
||
var result = FreeSql.Insert<LogicRule>(rule).ExecuteInserted();
|
||
|
||
// 检查影响的行数
|
||
bool success = result.Count > 0;
|
||
|
||
if (success)
|
||
{
|
||
ResultInsert = result.FirstOrDefault()!;
|
||
return success;
|
||
//LogService.Info($"成功插入规则: {rule.Name}");
|
||
|
||
// 刷新内存中的规则集合
|
||
//LoadRulesFromDatabase();
|
||
}
|
||
else
|
||
{
|
||
LogService.Warn($"插入规则失败: {rule.Name},没有行受影响");
|
||
ResultInsert = null;
|
||
return success;
|
||
}
|
||
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error($"插入规则到数据库时发生异常: {ex.Message}");
|
||
ResultInsert = null;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新规则
|
||
/// </summary>
|
||
/// <param name="rule">规则对象</param>
|
||
public void UpdateRule(LogicRule ruleOld, LogicRule rule)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(rule.Name))
|
||
{
|
||
MessageBox.Show("规则名称不能为空");
|
||
return;
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(rule.Expression))
|
||
{
|
||
MessageBox.Show("规则表达式不能为空");
|
||
return;
|
||
}
|
||
|
||
|
||
// 验证表达式是否有效
|
||
if (!ValidateExpression(rule.Expression))
|
||
{
|
||
MessageBox.Show("规则表达式验证失败");
|
||
return;
|
||
}
|
||
|
||
if (UpdateRuleToDb(rule))
|
||
{
|
||
// 更新缓存
|
||
var updatedDto = LogicRuleDtos.FirstOrDefault(r => r.Id == rule.Id);
|
||
var insertIndex = LogicRuleDtos.ToList().FindIndex(x => x.Id > rule.Id);
|
||
if (updatedDto != null)
|
||
{
|
||
//移除数据
|
||
LogicRuleDtos.Remove(updatedDto);
|
||
if (insertIndex == -1)
|
||
{
|
||
LogicRuleDtos.Add(Mapper.Map<LogicRuleDto>(rule));
|
||
}
|
||
else
|
||
{
|
||
// 在找到的位置插入
|
||
LogicRuleDtos.Insert(insertIndex - 1, Mapper.Map<LogicRuleDto>(rule));
|
||
}
|
||
|
||
//有可能更改的是名称,那么新名称的话在_expressionCache中肯定是找不到的,可以用之前的ruleOld
|
||
_expressionCache.TryRemove(ruleOld.Name!, out _);
|
||
CacheExpression(updatedDto);
|
||
}
|
||
}
|
||
|
||
//LogService.Info($"更新规则: {rule.Name}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新规格到数据库中
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public bool UpdateRuleToDb(LogicRule rule)
|
||
{
|
||
try
|
||
{
|
||
// 更新规则到数据库
|
||
var result = FreeSql.Update<LogicRule>()
|
||
.Set(a => a.Name, rule.Name)
|
||
.Set(a => a.Description, rule.Description)
|
||
.Set(a => a.Expression, rule.Expression)
|
||
.Set(a => a.ParameterType, rule.ParameterType)
|
||
.Where(r => r.Id == rule.Id)
|
||
.ExecuteUpdated();
|
||
|
||
// 检查影响的行数
|
||
bool success = result.Count() > 0;
|
||
|
||
if (success)
|
||
{
|
||
//LogService.Info($"成功更新规则: {rule.Name}");
|
||
|
||
}
|
||
else
|
||
{
|
||
//LogService.Warn($"更新规则失败: {rule.Name},没有行受影响");
|
||
}
|
||
|
||
return success;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
//LogService.Error($"更新规则到数据库时发生异常: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除规则
|
||
/// </summary>
|
||
/// <param name="ruleName">规则名称</param>
|
||
public void DeleteRule(string ruleName)
|
||
{
|
||
var rule = LogicRuleDtos.FirstOrDefault(r => r.Name == ruleName);
|
||
if (rule == null)
|
||
{
|
||
MessageBox.Show("找不到删除的数据");
|
||
return;
|
||
}
|
||
|
||
var Count = FreeSql.Delete<LogicRule>().Where(r => r.Id == rule.Id).ExecuteAffrows();
|
||
if (Count > 0)
|
||
{
|
||
// 删除规则
|
||
LogicRuleDtos.Remove(rule);
|
||
// 从缓存中移除表达式
|
||
_expressionCache.TryRemove(ruleName, out _);
|
||
}
|
||
|
||
//LogService.Info($"删除规则: {ruleName}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证表达式是否有效
|
||
/// </summary>
|
||
/// <param name="expression">表达式字符串</param>
|
||
/// <returns>表达式是否有效</returns>
|
||
private bool ValidateExpression(string expression)
|
||
{
|
||
try
|
||
{
|
||
// 创建测试解释器并直接设置变量
|
||
var interpreter = new Interpreter()
|
||
.Reference(typeof(Math)) // 引用Math类
|
||
.SetVariable("Math", typeof(Math))
|
||
.SetVariable("value", 100); // 直接设置值变量
|
||
|
||
// 尝试编译表达式
|
||
var lambda = interpreter.Parse(expression);
|
||
|
||
// 尝试执行表达式
|
||
var result = lambda.Invoke();
|
||
|
||
// 检查结果是否为数值类型
|
||
if (result is double doubleResult)
|
||
{
|
||
// 验证结果不是NaN或Infinity
|
||
if (double.IsNaN(doubleResult) || double.IsInfinity(doubleResult))
|
||
{
|
||
//LogService.Warn($"表达式执行结果无效 (NaN或Infinity): {expression}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Warn($"验证表达式失败: {expression}, 错误: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
|
||
/// <summary>
|
||
/// 根据参数类型获取适用的规则
|
||
/// </summary>
|
||
/// <param name="parameterType">参数类型</param>
|
||
/// <returns>适用的规则列表</returns>
|
||
public IEnumerable<LogicRuleDto> GetRulesByParameterType(string parameterType)
|
||
{
|
||
return LogicRuleDtos.Where(r => r.ParameterType == parameterType);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定名称的规则
|
||
/// </summary>
|
||
/// <param name="ruleName">规则名称</param>
|
||
/// <returns>规则对象</returns>
|
||
public LogicRuleDto GetRuleByName(string ruleName)
|
||
{
|
||
var rule = LogicRuleDtos.FirstOrDefault(r => r.Name == ruleName);
|
||
if (rule == null)
|
||
{
|
||
throw new KeyNotFoundException($"找不到名为 {ruleName} 的规则");
|
||
}
|
||
return rule;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 将规则表达式预编译并缓存到字典中
|
||
/// </summary>
|
||
/// <param name="rule">要缓存的规则</param>
|
||
private void CacheExpression(LogicRuleDto rule)
|
||
{
|
||
if (string.IsNullOrEmpty(rule.Name) || string.IsNullOrEmpty(rule.Expression))
|
||
{
|
||
return; // 防止空名称或空表达式
|
||
}
|
||
|
||
try
|
||
{
|
||
// ****如下步骤很重要,尝试了很多次才正常运行****
|
||
|
||
// 使用已有的CreateParameters方法创建参数声明
|
||
var parameter = CreateParameters(0); // 值不重要,只是为了获取参数声明
|
||
// 只解析一次,得到 Lambda 对象
|
||
var lambda = CurInterpreter.Parse(rule.Expression, parameter); // 只定义参数类型,不传值
|
||
// 编译为强类型委托
|
||
var compiledFunc = lambda.Compile<Func<double, double>>();
|
||
|
||
// 存入缓存
|
||
_expressionCache[rule.Name] = compiledFunc;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogService.Error($"缓存规则 {rule.Name} 表达式失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 预热表达式缓存 - 预编译所有规则
|
||
/// </summary>
|
||
private void WarmUpExpressionCache()
|
||
{
|
||
foreach (var rule in LogicRuleDtos)
|
||
{
|
||
if (!_expressionCache.ContainsKey(rule.Name))
|
||
{
|
||
CacheExpression(rule);
|
||
}
|
||
}
|
||
LogService.Info($"已预编译 {_expressionCache.Count} 条规则表达式");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据规则应用表达式(高性能版本)
|
||
/// </summary>
|
||
/// <param name="value">输入值</param>
|
||
/// <param name="rule">规则对象</param>
|
||
/// <returns>转换后的输出值</returns>
|
||
public double ApplyExpression(double value, LogicRuleDto rule)
|
||
{
|
||
try
|
||
{
|
||
// 尝试从缓存获取预编译的表达式
|
||
if (!_expressionCache.TryGetValue(rule.Name, out var compiledExpression))
|
||
{
|
||
// 如果缓存中没有,则编译并添加到缓存
|
||
CacheExpression(rule);
|
||
|
||
// 再次尝试获取
|
||
if (!_expressionCache.TryGetValue(rule.Name, out compiledExpression))
|
||
{
|
||
// 初始解析表达式(只需执行一次)
|
||
string expressionText = rule.Expression;
|
||
|
||
// 强类型函数
|
||
Func<double, double> compiledFunc = (v) =>
|
||
{
|
||
// 高性能版本:直接根据公式计算
|
||
try
|
||
{
|
||
if (expressionText.Contains("value"))
|
||
{
|
||
var parameters = CreateParameters(v);
|
||
var lambda = CurInterpreter.Parse(expressionText, parameters);
|
||
return (double)lambda.Invoke();
|
||
}
|
||
else
|
||
{
|
||
// 简单表达式使用直接求值
|
||
return v / 100.0; // 默认处理
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 出错时返回原值
|
||
return v;
|
||
}
|
||
};
|
||
// 再次尝试缓存
|
||
CacheExpression(rule);
|
||
|
||
return compiledFunc(value);
|
||
}
|
||
}
|
||
|
||
// 创建包含实际值的参数
|
||
var parameter = new Parameter("value", typeof(double), value);
|
||
// 使用预编译的表达式执行计算(高性能)
|
||
var convertedValue = (double)compiledExpression.Invoke(value);
|
||
|
||
return convertedValue;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
//LogService.Error($"应用规则 {rule.Name} 失败: {ex.Message}");
|
||
return value; // 出错时返回原始值
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 快速的执行数据
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
/// <param name="rule"></param>
|
||
/// <returns></returns>
|
||
public double ApplyExpressionFast(double value, LogicRuleDto rule)
|
||
{
|
||
try
|
||
{
|
||
if (!_expressionCache.TryGetValue(rule.Name, out var CurActiveFunc))
|
||
{
|
||
CacheExpression(rule);
|
||
if (!_expressionCache.TryGetValue(rule.Name, out CurActiveFunc))
|
||
return value;
|
||
}
|
||
|
||
// 直接调用委托,无反射开销
|
||
return CurActiveFunc(value);
|
||
}
|
||
catch
|
||
{
|
||
return value;
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 如果需要对多个数据应用相同规则,考虑实现批处理版本:
|
||
/// </summary>
|
||
/// <param name="values"></param>
|
||
/// <param name="rule"></param>
|
||
/// <returns></returns>
|
||
public IEnumerable<double> ApplyExpressionBatch(IEnumerable<double> values, LogicRuleDto rule)
|
||
{
|
||
if (!_expressionCache.TryGetValue(rule.Name, out var func))
|
||
{
|
||
CacheExpression(rule);
|
||
if (!_expressionCache.TryGetValue(rule.Name, out func))
|
||
return values;
|
||
}
|
||
|
||
// 使用并行处理大量数据
|
||
return values.AsParallel().Select(v =>
|
||
{
|
||
try { return func(v); }
|
||
catch { return v; }
|
||
});
|
||
}
|
||
|
||
}
|
||
}
|