using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace YLErp.Modules.RiskEngine
{
///
/// Roslyn 脚本全局变量容器
/// CSharpScript 执行时通过此对象传递 DataMap
///
public class ScriptGlobals
{
///
/// 数据字典:key=表名/前缀,value=对应数据对象
///
public Dictionary DataMap { get; set; }
}
///
/// 规则编译器(Roslyn 版)
///
/// 编译时机:规则定义时(零运行时解析开销)
/// 执行方式:判风控时直接调用 rule.CompiledScript(context) → bool
///
/// 相比表达式树方案的优势:
/// - 代码简洁:生成 C# 字符串即可,无需手动拼接 Expression 节点
/// - 灵活性高:天然支持多条件 AND/OR、复杂运算、未来可扩展任意 C# 语法
/// - 可调试:生成的脚本代码可直接阅读和理解
///
/// 示例:
/// RuleExpr: Convert.ToDecimal(((YLErp.DBModels.trade)DataMap["trade"]).StockEqvNotional) > 100000000m
/// 编译结果:Func<RiskContext, bool>(内部通过 Roslyn 编译缓存)
///
public static class RuleCompiler
{
///
/// 变量编码 → 类型全名的映射
/// 用于 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",
["swap_position"] = "YLErp.DBModels.swap_position",
["client"] = "YLErp.DBModels.client",
["credit"] = "YLErp.DBModels.credit",
["client_marginrate"] = "YLErp.DBModels.client_marginrate",
["market"] = "YLErp.DBModels.market",
["calc"] = "YLErp.DBModels.calc",
["sys"] = "YLErp.DBModels.sys",
["eod_swap_position"] = "YLErp.DBModels.eod_swap_position",
["realtime_trade_risk"] = "YLErp.DBModels.realtime_trade_risk",
};
///
/// 将规则编译为可执行委托,并写入 rule.CompiledScript
/// 调用时机:规则创建时 / 规则加载时 / 缓存刷新时
///
public static void Compile(this RiskRule rule)
{
var result = ValidateAndCompileRule(rule);
if (!result.Success)
{
throw new InvalidOperationException(result.ErrorMessage);
}
}
///
/// 校验并编译规则表达式。
/// 当前 RuleExpr 要求是 Roslyn 可直接执行的 bool 表达式。
///
public static RuleCompileResult ValidateAndCompileFormula(string formulaExp)
{
if (string.IsNullOrWhiteSpace(formulaExp))
{
return RuleCompileResult.Fail("规则 RuleExpr 不能为空");
}
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 (rule.Id <= 0)
{
return RuleCompileResult.Fail("规则 Id 不能为空");
}
var result = ValidateAndCompileFormula(rule.RuleExpr);
if (!result.Success)
{
return RuleCompileResult.Fail($"规则[{rule.Id}]编译失败:{result.ErrorMessage}");
}
rule.CompiledScript = result.CompiledScript;
RuleCompiledCache.Set(rule.Id.ToString(), result.CompiledScript);
return result;
}
///
/// 解析 ConditionJson,提取条件列表。
/// 要求 expression 明确描述实际取值路径。
/// 当前主执行链不再使用该方法,仅作为早期方案保留。
///
// private static List<(string variableCode, string expression, string op, object value, string variableType)> ParseConditionJson(string conditionJson)
// {
// var result = new List<(string, string, string, object, string)>();
// var jObj = JObject.Parse(formulaJson);
// var conditions = jObj["conditions"] as JArray;
// if (conditions == null || !conditions.Any())
// {
// throw new ArgumentException("ConditionJson 中缺少 conditions");
// }
// 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