diff --git a/YLErpDAL/Modules/RiskEngine/Compile/RuleCompileResult.cs b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompileResult.cs
new file mode 100644
index 00000000..65a88901
--- /dev/null
+++ b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompileResult.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace YLErp.Modules.RiskEngine
+{
+ ///
+ /// 规则编译结果。
+ /// 用于承载公式校验、Roslyn 编译结果和错误信息。
+ ///
+ public class RuleCompileResult
+ {
+ ///
+ /// 是否编译成功。
+ ///
+ public bool Success { get; set; }
+
+ ///
+ /// 编译失败时的错误信息。
+ ///
+ public string ErrorMessage { get; set; }
+
+ ///
+ /// 编译成功后的可执行委托。
+ ///
+ public Func CompiledScript { get; set; }
+
+ ///
+ /// 创建成功结果。
+ ///
+ public static RuleCompileResult Ok(Func compiledScript)
+ {
+ return new RuleCompileResult
+ {
+ Success = true,
+ CompiledScript = compiledScript
+ };
+ }
+
+ ///
+ /// 创建失败结果。
+ ///
+ public static RuleCompileResult Fail(string errorMessage)
+ {
+ return new RuleCompileResult
+ {
+ Success = false,
+ ErrorMessage = errorMessage
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiledCache.cs b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiledCache.cs
new file mode 100644
index 00000000..59fe9099
--- /dev/null
+++ b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiledCache.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Concurrent;
+
+namespace YLErp.Modules.RiskEngine
+{
+ ///
+ /// 规则编译结果的进程内缓存。
+ /// 当前第一版按 RuleCode 缓存 Roslyn 编译后的可执行委托。
+ ///
+ public static class RuleCompiledCache
+ {
+ ///
+ /// 规则编译结果的进程内缓存。ConcurrentDictionary为线程安全的字典,用于缓存编译后的规则委托,性能略低。
+ ///
+ private static readonly ConcurrentDictionary> Cache
+ = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase);
+
+ ///
+ /// 写入或更新规则编译结果。
+ ///
+ public static void Set(string ruleCode, Func compiledScript)
+ {
+ if (string.IsNullOrWhiteSpace(ruleCode) || compiledScript == null)
+ {
+ return;
+ }
+
+ Cache[ruleCode] = compiledScript;
+ }
+
+ ///
+ /// 尝试获取规则编译结果。
+ ///
+ public static bool TryGet(string ruleCode, out Func compiledScript)
+ {
+ compiledScript = null;
+
+ if (string.IsNullOrWhiteSpace(ruleCode))
+ {
+ return false;
+ }
+
+ return Cache.TryGetValue(ruleCode, out compiledScript);
+ }
+
+ ///
+ /// 删除指定规则的编译结果。
+ ///
+ public static void Remove(string ruleCode)
+ {
+ if (string.IsNullOrWhiteSpace(ruleCode))
+ {
+ return;
+ }
+
+ Cache.TryRemove(ruleCode, out _);
+ }
+
+ ///
+ /// 清空全部规则编译结果。
+ ///
+ public static void Clear()
+ {
+ Cache.Clear();
+ }
+ }
+}
\ No newline at end of file
diff --git a/YLErpDAL/Modules/RiskEngine/RuleCompiler.cs b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs
similarity index 63%
rename from YLErpDAL/Modules/RiskEngine/RuleCompiler.cs
rename to YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs
index ad21674d..b2e2a8ce 100644
--- a/YLErpDAL/Modules/RiskEngine/RuleCompiler.cs
+++ b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs
@@ -42,6 +42,7 @@ namespace YLErp.Modules.RiskEngine
/// 变量编码 → 类型全名的映射
/// 用于 Roslyn 脚本中的类型强转,如 ((YLErp.DBModels.trade)DataMap["trade"])
///
+ static IYcLogger _logger =LogFactory.GetLogger("RuleCompiler");
private static readonly Dictionary VariableTypeMap = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
["trade"] = "YLErp.DBModels.trade",
@@ -62,30 +63,68 @@ namespace YLErp.Modules.RiskEngine
///
public static void Compile(this RiskRule rule)
{
- if (string.IsNullOrWhiteSpace(rule.FormulaJson))
+ var result = ValidateAndCompileRule(rule);
+ if (!result.Success)
{
- throw new ArgumentException("规则 FormulaJson 不能为空", nameof(rule.FormulaJson));
+ throw new InvalidOperationException(result.ErrorMessage);
}
-
- // 1. 解析 FormulaJson
- var conditions = ParseFormulaJson(rule.FormulaJson);
-
- // 2. 生成 Roslyn C# 脚本代码
- string scriptCode = BuildScriptCode(conditions);
-
- // 3. 编译脚本(只编译一次,后续直接执行)
- var compiled = CompileScript(scriptCode);
-
- // 4. 写入规则(不持久化,仅内存缓存)
- rule.CompiledScript = compiled;
}
///
- /// 解析 FormulaJson,提取条件列表
+ /// 校验并编译公式表达式。
+ /// 当前 FormulaExp 要求是 Roslyn 可直接执行的 bool 表达式。
///
- private static List<(string variableCode, string op, object value, string variableType)> ParseFormulaJson(string formulaJson)
+ public static RuleCompileResult ValidateAndCompileFormula(string formulaExp)
{
- var result = new List<(string, string, object, string)>();
+ if (string.IsNullOrWhiteSpace(formulaExp))
+ {
+ return RuleCompileResult.Fail("规则 FormulaExp 不能为空");
+ }
+
+ try
+ {
+ var compiled = CompileScript(formulaExp);
+ return RuleCompileResult.Ok(compiled);
+ }
+ catch (Exception ex)
+ {
+ return RuleCompileResult.Fail(ex.Message);
+ }
+ }
+
+ ///
+ /// 校验并编译规则,编译成功后同时写入规则对象和内存缓存。
+ ///
+ public static RuleCompileResult ValidateAndCompileRule(RiskRule rule)
+ {
+ if (rule == null)
+ {
+ return RuleCompileResult.Fail("规则不能为空");
+ }
+
+ if (string.IsNullOrWhiteSpace(rule.RuleCode))
+ {
+ return RuleCompileResult.Fail("规则 RuleCode 不能为空");
+ }
+
+ var result = ValidateAndCompileFormula(rule.FormulaExp);
+ if (!result.Success)
+ {
+ return RuleCompileResult.Fail($"规则[{rule.RuleCode}]编译失败:{result.ErrorMessage}");
+ }
+
+ rule.CompiledScript = result.CompiledScript;
+ RuleCompiledCache.Set(rule.RuleCode, result.CompiledScript);
+ return result;
+ }
+
+ ///
+ /// 解析 FormulaJson,提取条件列表。
+ /// 要求 expression 明确描述实际取值路径
+ ///
+ private static List<(string variableCode, string expression, string op, object value, string variableType)> ParseFormulaJson(string formulaJson)
+ {
+ var result = new List<(string, string, string, object, string)>();
var jObj = JObject.Parse(formulaJson);
var conditions = jObj["conditions"] as JArray;
@@ -97,45 +136,35 @@ namespace YLErp.Modules.RiskEngine
foreach (var cond in conditions)
{
string variableCode = cond["variableCode"]?.Value();
+ string expression = cond["expression"]?.Value();
string op = cond["operator"]?.Value();
object value = cond["value"]?.Value