Files
zszq-trs/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs
T
尹峰 a259b745ce feat(风控引擎): RiskRuleService/RiskRuleController 权限控制及需求对齐
【权限控制】
  - FunctionRight.xml 注册 16 个风控引擎权限标识(规则/应用/变量/日志 四类 Tab+操作权限)
  - RiskRuleController 28 个 Action 均添加 [MyAuthorize] 校验
  - 权限管理员角色自动获得全部风控权限(UserInfo.cs)

【需求对齐】对照 v1.15 需求文档修复差异:
  - 审计日志操作类型命名对齐:APPLICATION_* → APP_*,VARIABLE_* → VAR_*
  - 规则列表新增按变量编码筛选(QueryRiskRuleReq.VariableCode → FormulaJson.Contains)
  - 变量列表新增分页支持(QueryRiskVariableReq 继承 BaseSearchReq,QueryVariableList → SearchListResult)
  - 需求文档同步至 v1.16
2026-06-23 16:01:31 +08:00

227 lines
12 KiB
C#
Raw 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 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 │
│ - 风控执行 APIEvaluateRisk / Trial
└──────────────────────────┬──────────────────────────────────────┘
┌──────────────────────────▼──────────────────────────────────────┐
│ 风控引擎层(YLErpDAL/Modules/RiskEngine/ ← 本文件夹所在层 │
│ │
│ RiskEngineService(本文件) │
│ - 内存缓存管理(规则 + 应用 + 变量 + 编译后脚本) │
│ - 构造 RiskContext → 解析 FormulaJson → 执行条件判断 → 输出结果 │
│ │
│ RiskVariableProvider(待实现) │
│ - 接收已编译委托 + RiskContext → 执行计算 → 返回变量值 │
│ │
│ RiskRuleService(待实现) │
│ - 规则 / 应用 / 变量 / 日志的 CRUD │
│ - CRUD 后触发 RiskEngineService.RefreshCache() │
│ │
│ RiskAuditLogService(待实现) │
│ - 操作日志写入 / 查询 │
└──────────────────────────┬──────────────────────────────────────┘
┌──────────────────────────▼──────────────────────────────────────┐
│ 数据访问层 │
│ - RiskRuleRepository(规则 + 应用 + 变量 + 日志) │
│ - 复用现有 DALTrade / Client / SwapPosition / Credit 等) │
└─────────────────────────────────────────────────────────────────┘
【当前进度】(截至 2026-06-22
✅ 已完成:
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.csRiskWarningDetails 赋值逻辑)
→ quotaTrial.cshtml(前端渲染,检查类别列"风险预警"写死在前端 HTML
【关键文件清单】
本模块:
- RiskEngineService.cs(本文件):引擎核心服务
- RiskContext.cs:风控上下文
- RiskResult.cs:风控结果
现有集成点:
- QuotaMonitorService.csRiskModule 下):QuotaCheck() 末尾已接入
设计文档:
- X:\尹峰\onederiv\风控引擎需求详细设计.md
【命名空间】
YLErp.Modules.RiskEngine(与 RiskModule 平级)
【注意事项】
1. 本期不对接现有审批模块,规则创建后直接可用(Active)
2. 旧 QuotaMonitorService 功能保持不变,新旧引擎独立运行
3. 变量新增仅需在变量池管理页面添加记录,无需修改引擎核心代码
4. 所有查询使用 AsNoTracking() 避免 EF 变更追踪开销
5. 数值统一使用 decimal 类型,避免浮点精度问题
================================================================================
*/
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// 风控引擎服务(第一版 — 骨架版,先跑通)
/// </summary>
public class RiskEngineService : YLBaseService
{
IYcLogger _logger = LogFactory.GetLogger("RiskEngineService");
private static readonly Lazy<RiskEngineService> _instance =
new Lazy<RiskEngineService>(() => new RiskEngineService());
public static RiskEngineService GetInstance() => _instance.Value;
private RiskEngineService() : base((OptUserInfo)null)
{
}
public RiskEngineService(OptUserInfo userInfo) : base(userInfo)
{
}
public RiskEngineService(YLBaseService baseService) : base(baseService)
{
}
public RiskEngineService(OptUserInfo optUser, YLContext dbContext) : base(optUser, dbContext)
{
}
public void RefreshCache()
{
_logger.Info("[风控引擎] RefreshCache 被调用(当前为桩实现,待缓存机制完成后替换)");
}
/// <summary>
/// 执行风控检查(第一版:一句简单的 1==1,先跑通)
/// </summary>
public bool Evaluate()
{
// 第一版占位:脚本 1==1,永远返回 true,验证服务能正常实例化和调用
return 1 == 1;
}
/// <summary>
/// 执行风控检查(按设计文档预演版)
/// </summary>
/// <param name="context">风控上下文</param>
/// <param name="triggerPoint">触发时点,如 BOOK_CONFIRM</param>
/// <returns>风控结果</returns>
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;
}
}
}