using Qdp.Foundation.Utilities; using YLErp.BLL; using YLErp.Model; /* ================================================================================ 风控引擎服务 — RiskEngineService 技术方案与当前实现说明 ================================================================================ 【项目背景】 当前 TRS 系统已在 QuotaMonitorService 中接入第一版风控引擎,用于在交易关键 时点执行可配置规则判断。当前目标不是一次性做成最终版,而是在最小可运行链路 跑通后,逐步演进为可由前端配置、后端预编译、执行期直接命中的正式版本。 本模块旨在将现有限额监控升级为完整的风控引擎,实现规则的灵活配置、公式化执行、 策略判定与结果输出。 【设计文档】 详细设计文档路径: X:\尹峰\onederiv\风控引擎需求详细设计.md 核心概念: - Rule(规则):定义"什么情况下触发",由变量 + 操作符 + 阈值组成的条件列表 - Application(应用):定义"何时、对谁、怎么处理",关联规则 + 策略/时点/范围 - Variable(变量):变量池中的可选项,用于构建规则条件 - RiskContext:一次风控检查的数据上下文(DataMap 机制) - RiskResult:风控检查结果(Passed / Blocked / NeedApproval / Warnings) 【架构设计】 ┌─────────────────────────────────────────────────────────────────┐ │ API 层(RiskRuleController / RiskEngineController) │ │ - 规则 CRUD API、应用配置 CRUD API、变量池 CRUD API │ │ - 风控执行 API(EvaluateRisk / Trial) │ └──────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────────────────┐ │ 风控引擎层(YLErpDAL/Modules/RiskEngine/) ← 本文件夹所在层 │ │ │ │ RiskEngineService(本文件) │ │ - 内存缓存管理(规则 + 应用 + 变量 + 编译后脚本) │ │ - 构造 RiskContext → 解析 FormulaJson → 执行条件判断 → 输出结果 │ │ │ │ RiskVariableProvider(待实现) │ │ - 接收已编译委托 + RiskContext → 执行计算 → 返回变量值 │ │ │ │ RiskRuleService(待实现) │ │ - 规则 / 应用 / 变量 / 日志的 CRUD │ │ - CRUD 后触发 RiskEngineService.RefreshCache() │ │ │ │ RiskAuditLogService(待实现) │ │ - 操作日志写入 / 查询 │ └──────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────────────────┐ │ 数据访问层 │ │ - RiskRuleRepository(规则 + 应用 + 变量 + 日志) │ │ - 复用现有 DAL(Trade / Client / SwapPosition / Credit 等) │ └─────────────────────────────────────────────────────────────────┘ 【当前代码结构】 风控引擎层:YLErpDAL/Modules/RiskEngine/ - RiskEngineService.cs:执行入口,负责规则筛选、应用过滤、委托执行、结果聚合 - RiskRule.cs:规则定义模型,含 CompiledScript 运行时字段 - RiskRuleApplication.cs:规则应用模型,承载策略、触发点和适用范围 - RiskContext.cs / RiskResult.cs:执行上下文与结果模型 编译相关:YLErpDAL/Modules/RiskEngine/Compile/ - RuleCompiler.cs:Roslyn 编译入口,提供 ValidateAndCompileFormula / ValidateAndCompileRule - RuleCompileResult.cs:编译结果模型 - RuleCompiledCache.cs:编译结果缓存 当前集成点: - QuotaMonitorService.cs:构造 RiskContext,并将 trade 对象放入 DataMap["trade"] 【当前进度】(截至 2026-06-24) ✅ 已完成: 1. 创建 RiskEngine 文件夹,与 RiskModule 平级,实现代码隔离 2. 创建核心模型: - RiskContext.cs:风控上下文(TradeId, TriggerPoint, DataMap) - RiskResult.cs:风控结果(Passed, Blocked, NeedApproval, Warnings, TriggeredRules) 3. RiskEngineService 骨架: - 继承 YLBaseService,保留三个构造函数 - Evaluate():1==1 占位方法,验证服务可实例化 - EvaluateRisk(RiskContext, triggerPoint):按设计文档预演的入口方法 4. 集成到现有流程: - QuotaMonitorService.QuotaCheck() 末尾已插入风控引擎调用 - 触发时点:BOOK_CONFIRM(簿记交易确认) - 构造 RiskContext,传入 trade 对象到 DataMap - 按策略(Blocked/NeedApproval/Warnings)映射回 QuotaTrialStatusEnum 5. 编译验证通过(YLErpDAL.csproj 0 error, 80 warnings 为旧代码) 【待办事项 / TODO】(按优先级排序) ⬜ 1. 内存缓存机制 - 添加 _cachedRules / _cachedApplications / _cachedVariables / _compiledScripts - 实现 RefreshCache() 方法,从数据库加载 Active 规则/应用/变量 - CRUD 操作后即时刷新(由 RiskRuleService 调用) ⬜ 2. 数据库表创建(SQL) - glms_risk_rule(规则定义表) - glms_risk_rule_application(规则应用表) - glms_risk_variable(变量池定义表) - glms_risk_rule_audit_log(操作日志表) ⬜ 3. 规则筛选逻辑 - 按 TriggerPoint 过滤应用配置 - 按 Scope(全局/账户/对手方/标的类型/合约类型)过滤适用范围 - 去重(同一规则可能被多个应用引用) ⬜ 4. 公式解析与执行 - 解析 FormulaJson(条件列表,AND 关系) - 数值型 / 日期型 / 布尔型 的条件判断 - 变量取值:通过 RiskVariableProvider 执行已编译委托 ⬜ 5. RiskVariableProvider 实现 - 接收已编译委托 + RiskContext,执行计算并返回变量值 - 异常捕获与详细错误信息(如"变量 XXX 取值失败:空引用") ⬜ 6. RiskContext 数据加载(按需加载) - trade 对象(必须) - swap_position / client / credit / market / calc / client_marginrate(按需) - 参考 QuotaMonitorService 中现有查询模式(AsNoTracking, DataCacheProvider) ⬜ 7. 脚本编译引擎 - 将变量 ImplementationScript 编译为可执行委托(Expression.Lambda) - 支持安全表达式求值,杜绝注入风险 - 按 (id, version) 缓存编译结果 ⬜ 8. 更多集成点 - 平仓审核提交(CLOSE_REVIEW):SwapTrade2Controller / unwindSwapTrade.js - 上传确认书(UPLOAD_CONFIRMATION):ConfirmationGenerateService - 事件发生时(EVENT_TRIGGER):二期实现 ⬜ 9. 单元测试 - 变量计算器取值逻辑 - 公式解析与执行 - 策略判定逻辑 - 完整风控检查流程(API → 结果返回) ⬜ 10. 前端规则配置 SPA(独立前端仓库) - 路由:/risk-config(单页面多 Tab:规则管理 / 规则应用 / 变量池 / 操作日志) ⬜ 11. 前端"确认交易"弹窗展示优化(当前混在"风险预警"栏目中,待独立) - 问题:风控引擎返回的 Blocked/NeedApproval/Warnings 目前通过 RiskWarningDetails 映射到前端 quotaTrial.cshtml,统一显示在"风险预警"栏目(橙色表头) - 方案A(最小改动):前端判断 RiskWarningDetails 是否包含"[风控引擎]"标签, 单独渲染"风控检查"栏目(如紫色表头),与原有"风险预警"并列展示 - 方案B(干净做法):QuotaTrial 模型新增 RiskEngineDetails 字段,后端往独立字段写, 前端新增独立渲染逻辑,彻底分离"风险预警"(旧限额)和"风控检查"(新引擎) - 相关文件: → QuotaMonitorService.cs(RiskWarningDetails 赋值逻辑) → quotaTrial.cshtml(前端渲染,检查类别列"风险预警"写死在前端 HTML) 【关键文件清单】 本模块: - RiskEngineService.cs(本文件):引擎核心服务 - RiskContext.cs:风控上下文 - RiskResult.cs:风控结果 现有集成点: - QuotaMonitorService.cs(RiskModule 下):QuotaCheck() 末尾已接入 设计文档: - X:\尹峰\onederiv\风控引擎需求详细设计.md 【命名空间】 YLErp.Modules.RiskEngine(与 RiskModule 平级) 【注意事项】 1. 本期不对接现有审批模块,规则创建后直接可用(Active) 2. 旧 QuotaMonitorService 功能保持不变,新旧引擎独立运行 3. 变量新增仅需在变量池管理页面添加记录,无需修改引擎核心代码 4. 所有查询使用 AsNoTracking() 避免 EF 变更追踪开销 5. 数值统一使用 decimal 类型,避免浮点精度问题 ================================================================================ */ namespace YLErp.Modules.RiskEngine { /// /// 风控引擎服务(第一版 — 骨架版,先跑通) /// public class RiskEngineService : YLBaseService { IYcLogger _logger = LogFactory.GetLogger("RiskEngineService"); public RiskEngineService(OptUserInfo userInfo) : base(userInfo) { } public RiskEngineService(YLBaseService baseService) : base(baseService) { } public RiskEngineService(OptUserInfo optUser, YLContext dbContext) : base(optUser, dbContext) { } /// /// 执行风控检查(第一版:一句简单的 1==1,先跑通) /// public bool Evaluate() { // 第一版占位:脚本 1==1,永远返回 true,验证服务能正常实例化和调用 return 1 == 1; } /// /// 执行风控检查(按设计文档预演版) /// /// 风控上下文 /// 触发时点,如 BOOK_CONFIRM /// 风控结果 public RiskResult EvaluateRisk(RiskContext context, string triggerPoint) { var result = new RiskResult(); try { // 调试模式:模拟触发审批(NeedApproval),验证前端界面展示 // TODO: 后续接入真实规则缓存和公式执行逻辑后删除此行 result.NeedApproval = true; result.Passed = false; result.Warnings.Add("【风控引擎】规则触发:需审批 - 模拟审批触发(调试模式)"); _logger.Info($"[风控引擎] EvaluateRisk 被调用 - TradeId: {context?.TradeId}, TriggerPoint: {triggerPoint}, NeedApproval: {result.NeedApproval}"); } catch (Exception ex) { result.HasError = true; result.ErrorMessage = $"风控引擎执行异常: {ex.Message}"; _logger.Error($"[风控引擎] EvaluateRisk 异常 - TradeId: {context?.TradeId}, Error: {ex.Message}"); } return result; } } }