Files
CapMachine/CapMachine.Wpf/Services/LogicRuleService.cs

579 lines
19 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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; }
});
}
}
}