Files
zszq-trs/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs
T
2026-06-26 17:14:53 +08:00

1429 lines
60 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 BaseOUDAL;
using Newtonsoft.Json;
using Qdp.Foundation.Utilities;
using YLErp.BLL;
using YLErp.DBModels;
using YLErp.Model;
using YLErp.Modules.RiskEngine.Dto;
/*
================================================================================
风控规则管理服务 — RiskRuleService 技术方案说明
================================================================================
【职责定位】
RiskRuleService 是风控引擎模块的 CRUD 管理服务层,负责规则(Rule)、应用配置
Application)、变量(Variable)和操作审计日志(AuditLog)的全生命周期管理。
与 RiskEngineService(引擎执行层)的关系:
- 本服务:配置管理(增删改查 + 审计日志)
- RiskEngineService:运行时执行(缓存 + 公式解析 + 风控判定)
- 交互方式:本服务在每次写操作后调用 RiskEngineService.RefreshCache() 通知引擎刷新
【设计文档】
详细设计文档:.trae/documents/RiskRuleService详细设计.mdv1.1
需求来源文档:.trae/documents/风控引擎需求详细设计.mdv1.17)
核心概念:
- Rule(规则):定义"什么情况下触发",支持两种公式模式(互斥):
· 结构化模式(ConditionJson 有值):变量 + 操作符 + 阈值/变量 的条件列表,前端拼接 RuleExpr
· 自由文本模式(RuleExpr 有值,ConditionJson 为空):前端直接编辑类 C# 表达式
- Application(应用配置):定义"何时、对谁、怎么处理",关联多条规则(RuleIds 逗号分隔)+ 策略/时点/范围
- Variable(变量):变量池中的可选项,用于构建规则条件(含取值表达式 VariableExpr
- AuditLog(审计日志):记录所有 CRUD 操作,支持版本追溯(SnapshotData 存储版本号快照)
【架构位置】
┌─────────────────────────────────────────────────────────────────┐
│ API 层 │
│ RiskRuleController27 个端点) │
│ - /api/risk-rules/* 规则 CRUD10 个端点) │
│ - /api/risk-applications/* 应用配置 CRUD9 个端点) │
│ - /api/risk-variables/* 变量池 CRUD6 个端点) │
│ - /api/risk-audit-logs/* 审计日志(3 个端点) │
└──────────────────────────┬──────────────────────────────────────┘
┌──────────────────────────▼──────────────────────────────────────┐
│ 服务层(本文件所在层) │
│ │
│ RiskRuleService(本文件) │
│ - 规则 / 应用 / 变量 / 审计日志的 CRUD │
│ - ConditionJson 完整校验(结构化模式:变量存在性、操作符、类型匹配)│
│ - RuleExpr 基础语法校验(自由文本模式:括号匹配、变量引用校验) │
│ - 乐观锁并发控制(Version 字段) │
│ - 删除保护(有 Active 引用时拒绝删除) │
│ - CRUD 后触发 RiskEngineService.RefreshCache() │
│ │
│ RiskEngineService(另一团队开发,单例) │
│ - 内存缓存管理 + 公式执行 + 风控判定 │
│ - RefreshCache():从数据库重新加载 Active 规则/应用/变量 │
└──────────────────────────┬──────────────────────────────────────┘
┌──────────────────────────▼──────────────────────────────────────┐
│ 数据访问层(EF Core DbContext 直接访问,无独立 Repository
│ - glms_risk_rule 规则定义表 │
│ - glms_risk_rule_application 规则应用配置表(RuleIds 逗号分隔) │
│ - glms_risk_variable 变量池定义表 │
│ - glms_risk_rule_audit_log 操作审计日志表 │
└─────────────────────────────────────────────────────────────────┘
【方法清单】(共 27 个公开方法 + 9 个私有辅助方法)
── 规则管理(10 个) ──────────────────────────────────────────────
QueryRuleList(QueryRiskRuleReq) 查询规则列表(分页,含 ApplicationCount 子查询)
GetRuleDetail(long) 获取规则详情(含关联应用摘要)
CreateRule(CreateRiskRuleReq) 新建规则(ConditionJson 完整校验)
UpdateRule(long, UpdateRiskRuleReq) 修改规则(乐观锁校验,Version+1)
DeleteRule(long) 删除规则(强保护:有 Active 应用时拒绝)
EnableRule(long) 启用规则(仅 Disabled → Active
DisableRule(long) 停用规则(仅 Active → Disabled
GetRuleVersions(long) 获取规则版本历史(从审计日志 SnapshotData 提取版本号)
BatchDeleteRules(List<long>) 批量删除规则(逐条保护检查,单事务)
GetRuleApplications(long) 获取规则关联的应用配置列表
GetAllRuleList() 获取所有启用规则(轻量字段,供应用配置下拉)
── 应用配置管理(9 个) ───────────────────────────────────────────
QueryApplicationList(QueryRiskApplicationReq) 查询应用列表(多条件筛选,内存关联规则名称)
GetApplicationDetail(long) 获取应用详情(含关联规则信息)
CreateApplication(CreateRiskApplicationReq) 新建应用(关联规则 Active 校验,触发时点/Scope 校验)
UpdateApplication(long, UpdateRiskApplicationReq) 修改应用(乐观锁校验)
DeleteApplication(long) 删除应用(软删除)
EnableApplication(long) 启用应用(校验关联规则状态)
DisableApplication(long) 停用应用
BatchEnableApplications(List<long>) 批量启用(逐条校验关联规则)
BatchDisableApplications(List<long>) 批量停用
── 变量管理(6 个) ───────────────────────────────────────────────
QueryVariableList(QueryRiskVariableReq) 查询变量列表(全量返回,Category/Keyword 筛选)
GetVariableDetail(long) 获取变量详情(含 ReferenceCount 引用统计)
CreateVariable(CreateRiskVariableReq) 新建变量(VariableExpr 长度校验 ≤10000
UpdateVariable(long, UpdateRiskVariableReq) 修改变量(乐观锁,DataType 变更保护)
DeleteVariable(long) 删除变量(引用保护:有 Active 规则引用时拒绝,硬删除)
GetAllVariableList() 获取所有已实现变量(轻量字段,供规则编辑器下拉)
── 审计日志(3 个) ───────────────────────────────────────────────
QueryAuditLogs(QueryRiskAuditLogReq) 查询审计日志(多条件筛选,分页)
ExportAuditLogs(QueryRiskAuditLogReq) 导出审计日志(占位实现,后续迭代 Excel 导出)
GetAuditLogDetail(long) 获取审计日志详情(含完整 SnapshotData
── 私有辅助方法 ───────────────────────────────────────────────────
WriteAuditLog(...) 写入审计日志(含 snapshotData 可选参数)
GetVariableExprSet() 获取变量表达式集合(5 分钟本地缓存)
InvalidateVariableCache() 清除变量缓存
ValidateConditionJson(conditionJson) ConditionJson 完整校验(结构化模式)
ValidateRuleExpr(ruleExpr) RuleExpr 基础语法校验(自由文本模式:括号匹配 + 变量引用校验)
ValidateTriggerPoints(triggerPoints) 触发时点校验(5 个合法时点)
ValidateScopeFields(req) Scope 字段校验(全局互斥 + ID 格式校验)
ValidateIdList(idList, fieldName) ID 列表格式校验
TryRefreshCache() 安全调用 RefreshCache(失败仅记日志)
GetRuleOrThrow / GetApplicationOrThrow / GetVariableOrThrow 实体获取快捷方法
【关键设计决策】
1. 数据访问:直接使用 EF Core DbContext(无独立 Repository,与项目风格一致)
2. 事务策略:Create* 方法使用 BeginTransaction 包裹双 SaveChanges(实体 + 审计日志同事务)
3. 并发控制:乐观锁(Version 字段),修改时版本不匹配抛 ServiceException
4. 删除保护:强保护模式,有 Active 引用时拒绝删除规则/变量
5. ConditionJson 校验:变量存在性(按 VariableId+ 操作符合法性(按 DataType 映射)+ 类型匹配 + 阈值变量引用
6. RuleExpr:自由文本模式下前端直接编辑,后端做基础语法校验(括号匹配 + 变量引用校验)
7. Scope 字段:逗号分隔字符串存储,全局适用时不允许指定其他维度
8. 缓存刷新:写操作后调用 TryRefreshCache(),失败仅记日志不影响操作结果
9. 批量操作:单事务批量,全部成功或全部失败
10. 变量缓存:5 分钟本地 HashSet<string> 缓存,CRUD 后 InvalidateVariableCache()
11. 审计日志:16 种操作类型,CRUD + 审计日志同一事务,SnapshotData 存储版本快照
12. 多规则支持:Application 通过 RuleIds(逗号分隔)关联多条规则,内存解析关联关系
【数据库表】(MySQL,迁移脚本位于 Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/prod.sql
glms_risk_rule 规则定义表
- 普通索引:Status, OptDate
- 关键字段:ConditionJson(结构化条件列表), RuleExpr(类 C# 表达式,引擎执行用), Version(乐观锁)
glms_risk_rule_application 规则应用配置表
- 普通索引:Status, OptDate
- 关键字段:RuleIds(逗号分隔的规则 ID 列表), ControlStrategy(禁止/审批/提示), TriggerPoints(触发时点), Scope*(适用范围)
glms_risk_variable 变量池定义表
- 关键字段:VariableExpr(取值表达式), DataType(数值/日期/布尔)
glms_risk_rule_audit_log 操作审计日志表
- 普通索引:TargetType+TargetId, OperationType, OptDate
- 关键字段:SnapshotDataJSON 快照,存储版本号等)
【关键文件清单】
本服务:
- RiskRuleService.cs(本文件):规则/应用/变量/日志 CRUD 管理
- Dto/ 目录:请求/响应 DTO 模型
关联服务:
- RiskEngineService.cs:引擎执行层(单例,本服务通过 GetInstance() 获取引用)
- RiskRuleController.csYLErpWeb/Controllers/):API 控制器(27 个端点)
实体模型:
- Framework/YLErp.Core/DBModels/glms_risk_rule.cs
- Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs
- Framework/YLErp.Core/DBModels/glms_risk_variable.cs
- Framework/YLErp.Core/DBModels/glms_risk_rule_audit_log.cs
枚举定义:
- Framework/YLErp.Core/DBModels/Enums/RiskRuleStatus.csActive/Disabled/Deleted
- Framework/YLErp.Core/DBModels/Enums/RiskControlStrategy.csBlock/Approve/Warn
- Framework/YLErp.Core/DBModels/Enums/RiskVariableCategory.cs
- Framework/YLErp.Core/DBModels/Enums/RiskVariableDataType.csNumeric/Date/Boolean
【命名空间】
YLErp.Modules.RiskEngine
【注意事项】
1. RiskEngineService 由另一个团队开发实现,本服务通过 GetInstance() 获取单例引用
2. 所有写操作后必须调用 TryRefreshCache(),确保引擎缓存与数据库一致
3. 变量删除为硬删除(物理删除),规则/应用删除为软删除(Status=Deleted
4. ExportAuditLogs 当前为占位实现,Excel 导出功能待后续迭代
5. VariableExpr 当前仅做基础长度校验(≤10000),完整编译校验待引入 Roslyn 库
6. ValidateScopeFields 的 ID 存在性校验已跳过(前端下拉选择器保证有效性)
================================================================================
*/
namespace YLErp.Modules.RiskEngine
{
public class RiskRuleService : YLBaseService
{
private readonly IYcLogger _logger = LogFactory.GetLogger("RiskRuleService");
private HashSet<string> _variableExprCache;
private DateTime _variableCacheUpdateTime;
private static readonly HashSet<string> ValidTriggerPoints = new HashSet<string>
{
"BOOK_CONFIRM",
"CLOSE_REVIEW",
"UPLOAD_CONFIRMATION",
"EVENT_TRIGGER",
"FUND_PAYMENT"
};
private static readonly Dictionary<RiskVariableDataType, HashSet<string>> ValidOperatorsByType = new Dictionary<RiskVariableDataType, HashSet<string>>
{
{ RiskVariableDataType.Numeric, new HashSet<string> { ">", "<", ">=", "<=", "=", "≠", "介于", "不介于" } },
{ RiskVariableDataType.Date, new HashSet<string> { "<", ">", "=", ">=", "<=", "介于", "不介于" } },
{ RiskVariableDataType.Boolean, new HashSet<string> { "是", "否" } }
};
public RiskRuleService(OptUserInfo userInfo) : base(userInfo)
{
}
public RiskRuleService(YLBaseService baseService) : base(baseService)
{
}
public RiskRuleService(OptUserInfo optUser, YLContext dbContext) : base(optUser, dbContext)
{
}
#region Private Helpers
private void WriteAuditLog(string operationType, string targetType, long targetId,
string targetName, string operationDetail, string result = "成功", string snapshotData = null)
{
var log = new glms_risk_rule_audit_log
{
OperationType = operationType,
TargetType = targetType,
TargetId = targetId,
TargetName = targetName,
OperationDetail = operationDetail,
Result = result,
SnapshotData = snapshotData,
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now
};
DbContext.glms_risk_rule_audit_log.Add(log);
}
private HashSet<string> GetVariableExprSet()
{
if (_variableExprCache == null ||
DateTime.Now - _variableCacheUpdateTime > TimeSpan.FromMinutes(5))
{
_variableExprCache = DbContext.glms_risk_variable
.Where(v => v.VariableExpr != null && v.VariableExpr != "")
.Select(v => v.VariableExpr)
.ToHashSet();
_variableCacheUpdateTime = DateTime.Now;
}
return _variableExprCache;
}
private void InvalidateVariableCache()
{
_variableExprCache = null;
}
private void ValidateConditionJson(string conditionJson)
{
List<RuleCondition> conditions;
try
{
conditions = JsonConvert.DeserializeObject<List<RuleCondition>>(conditionJson);
}
catch
{
throw new ServiceException("公式表达式 JSON 格式不合法");
}
if (conditions == null || conditions.Count == 0)
throw new ServiceException("公式条件列表不能为空");
for (int i = 0; i < conditions.Count; i++)
{
var cond = conditions[i];
var condLabel = $"条件{i + 1}";
var variableDef = DbContext.glms_risk_variable
.FirstOrDefault(v => v.id == cond.VariableId);
if (variableDef == null)
throw new ServiceException($"{condLabel}:变量 ID '{cond.VariableId}' 不存在");
var validOperators = ValidOperatorsByType[variableDef.DataType];
if (!validOperators.Contains(cond.Operator))
throw new ServiceException($"{condLabel}:操作符 '{cond.Operator}' 不适用于{GetDataTypeName(variableDef.DataType)}类型变量");
if (cond.ThresholdType == "Fixed")
{
if (cond.Value == null)
throw new ServiceException($"{condLabel}:固定阈值不能为空");
if (variableDef.DataType == RiskVariableDataType.Numeric)
{
if (!decimal.TryParse(cond.Value.ToString(), out _))
throw new ServiceException($"{condLabel}:数值型变量的阈值必须为数字");
}
if (variableDef.DataType == RiskVariableDataType.Date)
{
if (!DateTime.TryParse(cond.Value.ToString(), out _))
throw new ServiceException($"{condLabel}:日期型变量的阈值必须为合法日期");
}
}
else if (cond.ThresholdType == "Variable")
{
if (!cond.ThresholdVariableId.HasValue)
throw new ServiceException($"{condLabel}:变量阈值引用的变量 ID 不能为空");
var thresholdVarDef = DbContext.glms_risk_variable
.FirstOrDefault(v => v.id == cond.ThresholdVariableId.Value);
if (thresholdVarDef == null)
throw new ServiceException($"{condLabel}:阈值变量 ID '{cond.ThresholdVariableId.Value}' 不存在");
if (thresholdVarDef.DataType != variableDef.DataType)
throw new ServiceException($"{condLabel}:阈值变量与条件变量的数据类型不一致");
}
else
{
throw new ServiceException($"{condLabel}:阈值类型 '{cond.ThresholdType}' 不合法,仅支持 Fixed/Variable");
}
}
}
private void ValidateRuleExpr(string ruleExpr)
{
if (ruleExpr.Length > 10000)
throw new ServiceException("公式表达式长度不能超过 10000 字符");
int parenDepth = 0;
foreach (char c in ruleExpr)
{
if (c == '(') parenDepth++;
else if (c == ')') parenDepth--;
if (parenDepth < 0)
throw new ServiceException("公式表达式括号不匹配:多余的右括号");
}
if (parenDepth != 0)
throw new ServiceException("公式表达式括号不匹配:缺少右括号");
}
private void ValidateTriggerPoints(string triggerPoints)
{
if (string.IsNullOrWhiteSpace(triggerPoints))
throw new ServiceException("触发时点不能为空");
var points = triggerPoints.Split(',');
foreach (var point in points)
{
var trimmed = point.Trim();
if (!ValidTriggerPoints.Contains(trimmed))
throw new ServiceException($"触发时点 '{trimmed}' 不合法");
}
if (points.All(p => string.IsNullOrWhiteSpace(p)))
throw new ServiceException("至少需要指定一个触发时点");
}
private void ValidateScopeFields(CreateRiskApplicationReq req)
{
if (req.ScopeIsGlobal)
{
if (!string.IsNullOrEmpty(req.ScopeAssetBookIds) ||
!string.IsNullOrEmpty(req.ScopeClientIds) ||
!string.IsNullOrEmpty(req.ScopeUnderlyingTypes) ||
!string.IsNullOrEmpty(req.ScopeTradeTypes))
{
throw new ServiceException("全局适用时,不应指定其他适用范围维度");
}
return;
}
if (!string.IsNullOrEmpty(req.ScopeAssetBookIds))
{
ValidateIdList(req.ScopeAssetBookIds, "适用账户");
}
if (!string.IsNullOrEmpty(req.ScopeClientIds))
{
ValidateIdList(req.ScopeClientIds, "适用对手方");
}
}
private void ValidateIdList(string idList, string fieldName)
{
var parts = idList.Split(',');
foreach (var part in parts)
{
if (!int.TryParse(part.Trim(), out _))
throw new ServiceException($"{fieldName}列表格式不合法,应为逗号分隔的数字 ID");
}
}
private void TryRefreshCache()
{
try
{
RiskEngineService.GetInstance().RefreshCache();
}
catch (Exception ex)
{
_logger.Error($"缓存刷新失败:{ex.Message}", ex);
}
}
private string GetDataTypeName(RiskVariableDataType dataType)
{
return dataType switch
{
RiskVariableDataType.Numeric => "数值",
RiskVariableDataType.Date => "日期",
RiskVariableDataType.Boolean => "布尔",
_ => "未知"
};
}
private glms_risk_rule GetRuleOrThrow(long ruleId)
{
var rule = DbContext.glms_risk_rule.FirstOrDefault(r => r.id == ruleId && r.Status != RiskRuleStatus.Deleted);
if (rule == null)
throw new ServiceException("规则不存在或已删除");
return rule;
}
private glms_risk_rule_application GetApplicationOrThrow(long applicationId)
{
var app = DbContext.glms_risk_rule_application.FirstOrDefault(a => a.id == applicationId && a.Status != RiskRuleStatus.Deleted);
if (app == null)
throw new ServiceException("应用配置不存在或已删除");
return app;
}
private glms_risk_variable GetVariableOrThrow(long variableId)
{
var variable = DbContext.glms_risk_variable.FirstOrDefault(v => v.id == variableId);
if (variable == null)
throw new ServiceException("变量不存在");
return variable;
}
private void ValidateRuleParams(string ruleName, string conditionJson, string ruleExpr)
{
if (string.IsNullOrWhiteSpace(ruleName))
throw new ServiceException("规则名称不能为空");
if (ruleName.Length > 200)
throw new ServiceException("规则名称长度不能超过200字符");
if (string.IsNullOrWhiteSpace(ruleExpr))
throw new ServiceException("规则表达式不能为空");
if (!string.IsNullOrWhiteSpace(conditionJson))
{
ValidateConditionJson(conditionJson);
}
ValidateRuleExpr(ruleExpr);
}
private List<long> ParseRuleIds(string ruleIds)
{
if (string.IsNullOrWhiteSpace(ruleIds))
throw new ServiceException("关联规则 ID 不能为空");
var ids = new List<long>();
foreach (var part in ruleIds.Split(','))
{
if (long.TryParse(part.Trim(), out long id))
ids.Add(id);
else
throw new ServiceException($"规则 ID 列表格式不合法:'{part.Trim()}'");
}
if (ids.Count == 0)
throw new ServiceException("关联规则 ID 不能为空");
return ids;
}
private void ValidateRuleIdsActive(string ruleIds)
{
var ids = ParseRuleIds(ruleIds);
var rules = DbContext.glms_risk_rule
.Where(r => ids.Contains(r.id) && r.Status != RiskRuleStatus.Deleted)
.ToList();
if (rules.Count != ids.Count)
throw new ServiceException("部分关联规则不存在或已删除");
foreach (var rule in rules)
{
if (rule.Status == RiskRuleStatus.Disabled)
throw new ServiceException($"关联规则 '{rule.RuleName}' 已停用,无法创建/启用应用配置");
}
}
private string ResolveRuleNames(string ruleIds)
{
if (string.IsNullOrWhiteSpace(ruleIds))
return "";
var ids = ruleIds.Split(',')
.Select(s => long.TryParse(s.Trim(), out long id) ? id : (long?)null)
.Where(id => id.HasValue)
.Select(id => id.Value)
.ToList();
if (ids.Count == 0)
return "";
var names = DbContext.glms_risk_rule
.Where(r => ids.Contains(r.id) && r.Status != RiskRuleStatus.Deleted)
.Select(r => r.RuleName)
.ToList();
return string.Join(", ", names);
}
#endregion
#region Rule Management
public SearchListResult<RiskRuleListItem> QueryRuleList(QueryRiskRuleReq req)
{
var query = DbContext.glms_risk_rule
.Where(r => r.Status != RiskRuleStatus.Deleted);
if (!string.IsNullOrWhiteSpace(req.Keyword))
{
query = query.Where(r => r.RuleName.Contains(req.Keyword)
|| r.RuleText.Contains(req.Keyword));
}
if (req.Status.HasValue)
{
query = query.Where(r => r.Status == req.Status.Value);
}
if (req.VariableId.HasValue)
{
var varIdStr = req.VariableId.Value.ToString();
query = query.Where(r => (r.ConditionJson != null && r.ConditionJson.Contains(varIdStr))
|| (r.RuleExpr != null && r.RuleExpr.Contains(varIdStr)));
}
var result = query.OrderByDescending(r => r.UpdateDate)
.Select(r => new RiskRuleListItem
{
Id = r.id,
RuleName = r.RuleName,
RuleText = r.RuleText,
ConditionJson = r.ConditionJson,
RuleExpr = r.RuleExpr,
Status = r.Status,
Version = r.Version,
OptName = r.OptName,
OptDate = r.OptDate.GetValueOrDefault(),
UpdateOptName = r.UpdateOptName,
UpdateDate = r.UpdateDate ?? r.OptDate.GetValueOrDefault(),
ApplicationCount = DbContext.glms_risk_rule_application.Count(a => a.RuleIds.Contains(r.id.ToString()) && a.Status != RiskRuleStatus.Deleted)
});
return result.ToSearchList(req);
}
public RiskRuleDetail GetRuleDetail(long ruleId)
{
var rule = GetRuleOrThrow(ruleId);
var ruleIdStr = ruleId.ToString();
var applications = DbContext.glms_risk_rule_application
.Where(a => a.RuleIds.Contains(ruleIdStr) && a.Status != RiskRuleStatus.Deleted)
.Select(a => new RiskRuleApplicationSummary
{
Id = a.id,
Status = a.Status,
ControlStrategy = a.ControlStrategy,
TriggerPoints = a.TriggerPoints
})
.ToList();
return new RiskRuleDetail
{
Id = rule.id,
RuleName = rule.RuleName,
RuleText = rule.RuleText,
ConditionJson = rule.ConditionJson,
RuleExpr = rule.RuleExpr,
Status = rule.Status,
Version = rule.Version,
OptName = rule.OptName,
OptDate = rule.OptDate.GetValueOrDefault(),
UpdateOptName = rule.UpdateOptName,
UpdateDate = rule.UpdateDate ?? rule.OptDate.GetValueOrDefault(),
Applications = applications
};
}
public RiskRuleDetail CreateRule(CreateRiskRuleReq req)
{
ValidateRuleParams(req.RuleName, req.ConditionJson, req.RuleExpr);
var entity = new glms_risk_rule
{
RuleName = req.RuleName,
RuleText = req.RuleText,
ConditionJson = req.ConditionJson,
RuleExpr = req.RuleExpr,
Status = RiskRuleStatus.Active,
Version = 1
};
SetDBModelOpt(entity);
entity.UpdateOptId = UserId;
entity.UpdateOptName = UserName;
entity.UpdateDate = DateTime.Now;
using (var transaction = DbContext.Database.BeginTransaction())
{
try
{
DbContext.glms_risk_rule.Add(entity);
DbContext.SaveChanges();
WriteAuditLog("RULE_CREATE", "RULE", entity.id, req.RuleName, "创建规则",
snapshotData: JsonConvert.SerializeObject(new { Version = entity.Version }));
DbContext.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
TryRefreshCache();
return GetRuleDetail(entity.id);
}
public RiskRuleDetail UpdateRule(long ruleId, UpdateRiskRuleReq req)
{
var rule = GetRuleOrThrow(ruleId);
if (rule.Version != req.Version)
throw new ServiceException("规则已被其他用户修改,请重新加载后再编辑");
ValidateRuleParams(req.RuleName, req.ConditionJson, req.RuleExpr);
rule.RuleName = req.RuleName;
rule.RuleText = req.RuleText;
rule.ConditionJson = req.ConditionJson;
rule.RuleExpr = req.RuleExpr;
rule.Version = rule.Version + 1;
rule.UpdateOptId = UserId;
rule.UpdateOptName = UserName;
rule.UpdateDate = DateTime.Now;
WriteAuditLog("RULE_UPDATE", "RULE", ruleId, req.RuleName, "修改规则");
DbContext.SaveChanges();
TryRefreshCache();
return GetRuleDetail(ruleId);
}
public void DeleteRule(long ruleId)
{
var rule = GetRuleOrThrow(ruleId);
var ruleIdStr = ruleId.ToString();
var activeCount = DbContext.glms_risk_rule_application
.Count(a => a.RuleIds.Contains(ruleIdStr) && a.Status == RiskRuleStatus.Active);
if (activeCount > 0)
throw new ServiceException($"该规则仍有 {activeCount} 个生效中的应用配置,请先停用或删除相关应用");
rule.Status = RiskRuleStatus.Deleted;
rule.Version = rule.Version + 1;
rule.UpdateOptId = UserId;
rule.UpdateOptName = UserName;
rule.UpdateDate = DateTime.Now;
WriteAuditLog("RULE_DELETE", "RULE", ruleId, rule.RuleName, "删除规则");
DbContext.SaveChanges();
TryRefreshCache();
}
public void EnableRule(long ruleId)
{
var rule = DbContext.glms_risk_rule.FirstOrDefault(r => r.id == ruleId && r.Status == RiskRuleStatus.Disabled);
if (rule == null)
throw new ServiceException("仅已停用的规则可以启用");
rule.Status = RiskRuleStatus.Active;
rule.UpdateOptId = UserId;
rule.UpdateOptName = UserName;
rule.UpdateDate = DateTime.Now;
WriteAuditLog("RULE_ENABLE", "RULE", ruleId, rule.RuleName, "启用规则");
DbContext.SaveChanges();
TryRefreshCache();
}
public void DisableRule(long ruleId)
{
var rule = DbContext.glms_risk_rule.FirstOrDefault(r => r.id == ruleId && r.Status == RiskRuleStatus.Active);
if (rule == null)
throw new ServiceException("仅已生效的规则可以停用");
rule.Status = RiskRuleStatus.Disabled;
rule.UpdateOptId = UserId;
rule.UpdateOptName = UserName;
rule.UpdateDate = DateTime.Now;
WriteAuditLog("RULE_DISABLE", "RULE", ruleId, rule.RuleName, "停用规则");
DbContext.SaveChanges();
TryRefreshCache();
}
public List<RiskRuleVersionItem> GetRuleVersions(long ruleId)
{
GetRuleOrThrow(ruleId);
var logs = DbContext.glms_risk_rule_audit_log
.Where(l => l.TargetType == "RULE" && l.TargetId == ruleId)
.OrderByDescending(l => l.OptDate)
.ToList();
var result = new List<RiskRuleVersionItem>();
foreach (var l in logs)
{
int version = 0;
if (!string.IsNullOrEmpty(l.SnapshotData))
{
try
{
var snapshot = JsonConvert.DeserializeAnonymousType(l.SnapshotData, new { Version = 0 });
if (snapshot != null)
version = snapshot.Version;
}
catch { }
}
result.Add(new RiskRuleVersionItem
{
Version = version,
OperationType = l.OperationType,
OperationDetail = l.OperationDetail,
OptName = l.OptName,
OptDate = l.OptDate
});
}
return result;
}
public BatchOperationResult BatchDeleteRules(List<long> ruleIds)
{
var rules = DbContext.glms_risk_rule
.Where(r => ruleIds.Contains(r.id) && r.Status != RiskRuleStatus.Deleted)
.ToList();
if (rules.Count != ruleIds.Count)
throw new ServiceException("部分规则不存在或已删除");
foreach (var rule in rules)
{
var ruleIdStr = rule.id.ToString();
var activeCount = DbContext.glms_risk_rule_application
.Count(a => a.RuleIds.Contains(ruleIdStr) && a.Status == RiskRuleStatus.Active);
if (activeCount > 0)
throw new ServiceException($"规则 '{rule.RuleName}' 仍有 {activeCount} 个生效中的应用配置,请先停用或删除相关应用");
}
foreach (var rule in rules)
{
rule.Status = RiskRuleStatus.Deleted;
rule.Version = rule.Version + 1;
rule.UpdateOptId = UserId;
rule.UpdateOptName = UserName;
rule.UpdateDate = DateTime.Now;
WriteAuditLog("RULE_BATCH_DELETE", "RULE", rule.id, rule.RuleName, "批量删除规则");
}
DbContext.SaveChanges();
TryRefreshCache();
return new BatchOperationResult
{
Success = true,
TotalCount = ruleIds.Count,
SuccessCount = ruleIds.Count
};
}
public List<RiskApplicationListItem> GetRuleApplications(long ruleId)
{
GetRuleOrThrow(ruleId);
var ruleIdStr = ruleId.ToString();
var apps = DbContext.glms_risk_rule_application
.Where(a => a.RuleIds.Contains(ruleIdStr) && a.Status != RiskRuleStatus.Deleted)
.OrderByDescending(a => a.UpdateDate)
.ToList();
var result = new List<RiskApplicationListItem>();
foreach (var app in apps)
{
result.Add(new RiskApplicationListItem
{
Id = app.id,
RuleIds = app.RuleIds,
Status = app.Status,
ControlStrategy = app.ControlStrategy,
TriggerPoints = app.TriggerPoints,
ScopeAssetBookIds = app.ScopeAssetBookIds,
ScopeClientIds = app.ScopeClientIds,
ScopeUnderlyingTypes = app.ScopeUnderlyingTypes,
ScopeTradeTypes = app.ScopeTradeTypes,
ScopeIsGlobal = app.ScopeIsGlobal,
Version = app.Version,
OptName = app.OptName,
OptDate = app.OptDate.GetValueOrDefault(),
UpdateOptName = app.UpdateOptName,
UpdateDate = app.UpdateDate ?? app.OptDate.GetValueOrDefault()
});
}
return result;
}
public List<RiskRuleSimpleItem> GetAllRuleList()
{
return DbContext.glms_risk_rule
.Where(r => r.Status == RiskRuleStatus.Active)
.OrderBy(r => r.id)
.Select(r => new RiskRuleSimpleItem
{
Id = r.id,
RuleName = r.RuleName,
RuleText = r.RuleText
})
.ToList();
}
#endregion
#region Application Management
public SearchListResult<RiskApplicationListItem> QueryApplicationList(QueryRiskApplicationReq req)
{
var query = DbContext.glms_risk_rule_application
.Where(a => a.Status != RiskRuleStatus.Deleted);
if (req.Status.HasValue)
{
query = query.Where(a => a.Status == req.Status.Value);
}
if (req.Strategy.HasValue)
{
query = query.Where(a => a.ControlStrategy == req.Strategy.Value);
}
if (!string.IsNullOrWhiteSpace(req.TriggerPoint))
{
query = query.Where(a => a.TriggerPoints.Contains(req.TriggerPoint));
}
if (!string.IsNullOrWhiteSpace(req.RuleName))
{
query = query.Where(a => DbContext.glms_risk_rule
.Any(r => r.Status != RiskRuleStatus.Deleted
&& a.RuleIds.Contains(r.id.ToString())
&& r.RuleName.Contains(req.RuleName)));
}
var pagedResult = query.OrderByDescending(a => a.UpdateDate)
.Select(a => new RiskApplicationListItem
{
Id = a.id,
RuleIds = a.RuleIds,
Status = a.Status,
ControlStrategy = a.ControlStrategy,
TriggerPoints = a.TriggerPoints,
ScopeAssetBookIds = a.ScopeAssetBookIds,
ScopeClientIds = a.ScopeClientIds,
ScopeUnderlyingTypes = a.ScopeUnderlyingTypes,
ScopeTradeTypes = a.ScopeTradeTypes,
ScopeIsGlobal = a.ScopeIsGlobal,
Version = a.Version,
OptName = a.OptName,
OptDate = a.OptDate.GetValueOrDefault(),
UpdateOptName = a.UpdateOptName,
UpdateDate = a.UpdateDate ?? a.OptDate.GetValueOrDefault()
})
.ToSearchList(req);
return pagedResult;
}
public RiskApplicationDetail GetApplicationDetail(long applicationId)
{
var app = DbContext.glms_risk_rule_application
.FirstOrDefault(a => a.id == applicationId && a.Status != RiskRuleStatus.Deleted);
if (app == null)
throw new ServiceException("应用配置不存在或已删除");
return new RiskApplicationDetail
{
Id = app.id,
RuleIds = app.RuleIds,
Status = app.Status,
ControlStrategy = app.ControlStrategy,
TriggerPoints = app.TriggerPoints,
ScopeAssetBookIds = app.ScopeAssetBookIds,
ScopeClientIds = app.ScopeClientIds,
ScopeUnderlyingTypes = app.ScopeUnderlyingTypes,
ScopeTradeTypes = app.ScopeTradeTypes,
ScopeIsGlobal = app.ScopeIsGlobal,
Version = app.Version,
OptName = app.OptName,
OptDate = app.OptDate.GetValueOrDefault(),
UpdateOptName = app.UpdateOptName,
UpdateDate = app.UpdateDate ?? app.OptDate.GetValueOrDefault()
};
}
public RiskApplicationDetail CreateApplication(CreateRiskApplicationReq req)
{
ValidateRuleIdsActive(req.RuleIds);
ValidateTriggerPoints(req.TriggerPoints);
ValidateScopeFields(req);
var entity = new glms_risk_rule_application
{
RuleIds = req.RuleIds,
Status = RiskRuleStatus.Active,
ControlStrategy = req.ControlStrategy,
TriggerPoints = req.TriggerPoints,
ScopeAssetBookIds = req.ScopeAssetBookIds,
ScopeClientIds = req.ScopeClientIds,
ScopeUnderlyingTypes = req.ScopeUnderlyingTypes,
ScopeTradeTypes = req.ScopeTradeTypes,
ScopeIsGlobal = req.ScopeIsGlobal,
Version = 1
};
SetDBModelOpt(entity);
entity.UpdateOptId = UserId;
entity.UpdateOptName = UserName;
entity.UpdateDate = DateTime.Now;
using (var transaction = DbContext.Database.BeginTransaction())
{
try
{
DbContext.glms_risk_rule_application.Add(entity);
DbContext.SaveChanges();
WriteAuditLog("APP_CREATE", "APPLICATION", entity.id, ResolveRuleNames(req.RuleIds),
$"创建应用配置:策略={req.ControlStrategy}, 触发时点={req.TriggerPoints}",
snapshotData: JsonConvert.SerializeObject(new { Version = entity.Version }));
DbContext.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
TryRefreshCache();
return GetApplicationDetail(entity.id);
}
public RiskApplicationDetail UpdateApplication(long applicationId, UpdateRiskApplicationReq req)
{
var app = GetApplicationOrThrow(applicationId);
if (app.Version != req.Version)
throw new ServiceException("应用配置已被其他用户修改,请重新加载后再编辑");
if (!string.IsNullOrWhiteSpace(req.RuleIds))
ValidateRuleIdsActive(req.RuleIds);
ValidateTriggerPoints(req.TriggerPoints);
var scopeReq = new CreateRiskApplicationReq
{
ScopeAssetBookIds = req.ScopeAssetBookIds,
ScopeClientIds = req.ScopeClientIds,
ScopeUnderlyingTypes = req.ScopeUnderlyingTypes,
ScopeTradeTypes = req.ScopeTradeTypes,
ScopeIsGlobal = req.ScopeIsGlobal
};
ValidateScopeFields(scopeReq);
if (!string.IsNullOrWhiteSpace(req.RuleIds))
app.RuleIds = req.RuleIds;
app.ControlStrategy = req.ControlStrategy;
app.TriggerPoints = req.TriggerPoints;
app.ScopeAssetBookIds = req.ScopeAssetBookIds;
app.ScopeClientIds = req.ScopeClientIds;
app.ScopeUnderlyingTypes = req.ScopeUnderlyingTypes;
app.ScopeTradeTypes = req.ScopeTradeTypes;
app.ScopeIsGlobal = req.ScopeIsGlobal;
app.Version = app.Version + 1;
app.UpdateOptId = UserId;
app.UpdateOptName = UserName;
app.UpdateDate = DateTime.Now;
WriteAuditLog("APP_UPDATE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds),
$"修改应用配置:策略={req.ControlStrategy}, 触发时点={req.TriggerPoints}");
DbContext.SaveChanges();
TryRefreshCache();
return GetApplicationDetail(applicationId);
}
public void DeleteApplication(long applicationId)
{
var app = GetApplicationOrThrow(applicationId);
app.Status = RiskRuleStatus.Deleted;
app.Version = app.Version + 1;
app.UpdateOptId = UserId;
app.UpdateOptName = UserName;
app.UpdateDate = DateTime.Now;
WriteAuditLog("APPLICATION_DELETE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "删除应用配置");
DbContext.SaveChanges();
TryRefreshCache();
}
public void EnableApplication(long applicationId)
{
var app = DbContext.glms_risk_rule_application.FirstOrDefault(a => a.id == applicationId && a.Status == RiskRuleStatus.Disabled);
if (app == null)
throw new ServiceException("仅已停用的应用配置可以启用");
ValidateRuleIdsActive(app.RuleIds);
app.Status = RiskRuleStatus.Active;
app.UpdateOptId = UserId;
app.UpdateOptName = UserName;
app.UpdateDate = DateTime.Now;
WriteAuditLog("APP_ENABLE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "启用应用配置");
DbContext.SaveChanges();
TryRefreshCache();
}
public void DisableApplication(long applicationId)
{
var app = DbContext.glms_risk_rule_application.FirstOrDefault(a => a.id == applicationId && a.Status == RiskRuleStatus.Active);
if (app == null)
throw new ServiceException("仅已生效的应用配置可以停用");
app.Status = RiskRuleStatus.Disabled;
app.UpdateOptId = UserId;
app.UpdateOptName = UserName;
app.UpdateDate = DateTime.Now;
WriteAuditLog("APP_DISABLE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "停用应用配置");
DbContext.SaveChanges();
TryRefreshCache();
}
public BatchOperationResult BatchEnableApplications(List<long> applicationIds)
{
var apps = DbContext.glms_risk_rule_application
.Where(a => applicationIds.Contains(a.id) && a.Status == RiskRuleStatus.Disabled)
.ToList();
if (apps.Count != applicationIds.Count)
throw new ServiceException("部分应用配置不存在或当前状态不允许启用");
foreach (var app in apps)
{
ValidateRuleIdsActive(app.RuleIds);
}
foreach (var app in apps)
{
app.Status = RiskRuleStatus.Active;
app.UpdateOptId = UserId;
app.UpdateOptName = UserName;
app.UpdateDate = DateTime.Now;
WriteAuditLog("APP_BATCH_ENABLE", "APPLICATION", app.id, ResolveRuleNames(app.RuleIds), "批量启用应用配置");
}
DbContext.SaveChanges();
TryRefreshCache();
return new BatchOperationResult
{
Success = true,
TotalCount = applicationIds.Count,
SuccessCount = applicationIds.Count
};
}
public BatchOperationResult BatchDisableApplications(List<long> applicationIds)
{
var apps = DbContext.glms_risk_rule_application
.Where(a => applicationIds.Contains(a.id) && a.Status == RiskRuleStatus.Active)
.ToList();
if (apps.Count != applicationIds.Count)
throw new ServiceException("部分应用配置不存在或当前状态不允许停用");
foreach (var app in apps)
{
app.Status = RiskRuleStatus.Disabled;
app.UpdateOptId = UserId;
app.UpdateOptName = UserName;
app.UpdateDate = DateTime.Now;
WriteAuditLog("APPLICATION_BATCH_DISABLE", "APPLICATION", app.id, ResolveRuleNames(app.RuleIds), "批量停用应用配置");
}
DbContext.SaveChanges();
TryRefreshCache();
return new BatchOperationResult
{
Success = true,
TotalCount = applicationIds.Count,
SuccessCount = applicationIds.Count
};
}
#endregion
#region Variable Management
public SearchListResult<RiskVariableListItem> QueryVariableList(QueryRiskVariableReq req)
{
var query = DbContext.glms_risk_variable.AsQueryable();
if (req.Category.HasValue)
{
query = query.Where(v => v.Category == req.Category.Value);
}
if (!string.IsNullOrWhiteSpace(req.VariableName))
{
query = query.Where(v => v.VariableName.Contains(req.VariableName));
}
var result = query.OrderBy(v => v.SortOrder).ThenBy(v => v.VariableName)
.Select(v => new RiskVariableListItem
{
Id = v.id,
VariableName = v.VariableName,
Category = v.Category,
DataType = v.DataType,
Unit = v.Unit,
ValueDomain = v.ValueDomain,
Description = v.Description,
VariableExpr = v.VariableExpr,
Version = v.Version,
SortOrder = v.SortOrder
});
return result.ToSearchList(req);
}
public RiskVariableDetail GetVariableDetail(long variableId)
{
var variable = GetVariableOrThrow(variableId);
var varExpr = variable.VariableExpr;
var referenceCount = DbContext.glms_risk_rule
.Count(r => r.Status == RiskRuleStatus.Active &&
((r.ConditionJson != null && r.ConditionJson.Contains(variable.id.ToString())) ||
(r.RuleExpr != null && varExpr != null && r.RuleExpr.Contains(varExpr))));
return new RiskVariableDetail
{
Id = variable.id,
VariableName = variable.VariableName,
Category = variable.Category,
DataType = variable.DataType,
Unit = variable.Unit,
ValueDomain = variable.ValueDomain,
Description = variable.Description,
VariableExpr = variable.VariableExpr,
Version = variable.Version,
SortOrder = variable.SortOrder,
OptName = variable.OptName,
OptDate = variable.OptDate.GetValueOrDefault(),
UpdateOptName = variable.UpdateOptName,
UpdateDate = variable.UpdateDate ?? variable.OptDate.GetValueOrDefault(),
ReferenceCount = referenceCount
};
}
public RiskVariableDetail CreateVariable(CreateRiskVariableReq req)
{
if (string.IsNullOrWhiteSpace(req.VariableName))
throw new ServiceException("变量名称不能为空");
if (string.IsNullOrWhiteSpace(req.VariableExpr))
throw new ServiceException("取值表达式不能为空");
if (req.VariableExpr.Length > 10000)
throw new ServiceException("取值表达式长度不能超过 10000 字符");
var entity = new glms_risk_variable
{
VariableName = req.VariableName,
Category = req.Category,
DataType = req.DataType,
Unit = req.Unit,
ValueDomain = req.ValueDomain,
Description = req.Description,
VariableExpr = req.VariableExpr,
SortOrder = req.SortOrder,
Version = 1
};
SetDBModelOpt(entity);
entity.UpdateOptId = UserId;
entity.UpdateOptName = UserName;
entity.UpdateDate = DateTime.Now;
using (var transaction = DbContext.Database.BeginTransaction())
{
try
{
DbContext.glms_risk_variable.Add(entity);
DbContext.SaveChanges();
WriteAuditLog("VARIABLE_CREATE", "VARIABLE", entity.id, req.VariableName, $"创建变量:{req.VariableName}",
snapshotData: JsonConvert.SerializeObject(new { Version = entity.Version }));
DbContext.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
InvalidateVariableCache();
TryRefreshCache();
return GetVariableDetail(entity.id);
}
public RiskVariableDetail UpdateVariable(long variableId, UpdateRiskVariableReq req)
{
var variable = GetVariableOrThrow(variableId);
if (variable.Version != req.Version)
throw new ServiceException("变量已被其他用户修改,请重新加载后再编辑");
if (string.IsNullOrWhiteSpace(req.VariableName))
throw new ServiceException("变量名称不能为空");
if (string.IsNullOrWhiteSpace(req.VariableExpr))
throw new ServiceException("取值表达式不能为空");
if (req.DataType != variable.DataType)
{
var varExpr = variable.VariableExpr;
var activeRefCount = DbContext.glms_risk_rule
.Count(r => r.Status == RiskRuleStatus.Active &&
((r.ConditionJson != null && r.ConditionJson.Contains(variable.id.ToString())) ||
(r.RuleExpr != null && varExpr != null && r.RuleExpr.Contains(varExpr))));
if (activeRefCount > 0)
throw new ServiceException($"该变量被 {activeRefCount} 个生效中的规则引用,无法修改数据类型");
}
variable.VariableName = req.VariableName;
variable.Category = req.Category;
variable.DataType = req.DataType;
variable.Unit = req.Unit;
variable.ValueDomain = req.ValueDomain;
variable.Description = req.Description;
variable.VariableExpr = req.VariableExpr;
variable.SortOrder = req.SortOrder;
variable.Version = variable.Version + 1;
variable.UpdateOptId = UserId;
variable.UpdateOptName = UserName;
variable.UpdateDate = DateTime.Now;
WriteAuditLog("VAR_UPDATE", "VARIABLE", variableId, req.VariableName, $"修改变量:{variable.VariableName}");
DbContext.SaveChanges();
InvalidateVariableCache();
TryRefreshCache();
return GetVariableDetail(variableId);
}
public void DeleteVariable(long variableId)
{
var variable = GetVariableOrThrow(variableId);
var varExpr = variable.VariableExpr;
var activeRefCount = DbContext.glms_risk_rule
.Count(r => r.Status == RiskRuleStatus.Active &&
((r.ConditionJson != null && r.ConditionJson.Contains(variable.id.ToString())) ||
(r.RuleExpr != null && varExpr != null && r.RuleExpr.Contains(varExpr))));
if (activeRefCount > 0)
throw new ServiceException($"该变量被 {activeRefCount} 个生效中的规则引用,无法删除");
DbContext.glms_risk_variable.Remove(variable);
WriteAuditLog("VAR_DELETE", "VARIABLE", variableId, variable.VariableName, $"删除变量:{variable.VariableName}");
DbContext.SaveChanges();
InvalidateVariableCache();
TryRefreshCache();
}
public List<RiskVariableSimpleItem> GetAllVariableList()
{
var result = DbContext.glms_risk_variable
.Where(v => v.VariableExpr != null && v.VariableExpr != "")
.OrderBy(v => v.SortOrder)
.Select(v => new RiskVariableSimpleItem
{
Id = v.id,
VariableName = v.VariableName,
Category = v.Category,
DataType = v.DataType,
Unit = v.Unit,
VariableExpr = v.VariableExpr
})
.ToList();
return result;
}
#endregion
#region Audit Log
public SearchListResult<RiskAuditLogListItem> QueryAuditLogs(QueryRiskAuditLogReq req)
{
var query = DbContext.glms_risk_rule_audit_log.AsQueryable();
if (!string.IsNullOrWhiteSpace(req.OperationType))
{
query = query.Where(l => l.OperationType == req.OperationType);
}
if (!string.IsNullOrWhiteSpace(req.TargetType))
{
query = query.Where(l => l.TargetType == req.TargetType);
}
if (req.StartDate.HasValue)
{
query = query.Where(l => l.OptDate >= req.StartDate.Value);
}
if (req.EndDate.HasValue)
{
query = query.Where(l => l.OptDate <= req.EndDate.Value);
}
if (!string.IsNullOrWhiteSpace(req.Keyword))
{
query = query.Where(l => l.TargetName.Contains(req.Keyword)
|| l.OperationDetail.Contains(req.Keyword));
}
var result = query.OrderByDescending(l => l.OptDate)
.Select(l => new RiskAuditLogListItem
{
Id = l.id,
OperationType = l.OperationType,
TargetType = l.TargetType,
TargetId = l.TargetId,
TargetName = l.TargetName,
OperationDetail = l.OperationDetail,
Result = l.Result,
OptName = l.OptName,
OptDate = l.OptDate
});
return result.ToSearchList(req);
}
public byte[] ExportAuditLogs(QueryRiskAuditLogReq req)
{
throw new ServiceException("导出功能暂未实现");
}
public RiskAuditLogDetail GetAuditLogDetail(long logId)
{
var log = DbContext.glms_risk_rule_audit_log.FirstOrDefault(l => l.id == logId);
if (log == null)
throw new ServiceException("审计日志不存在");
return new RiskAuditLogDetail
{
Id = log.id,
OperationType = log.OperationType,
TargetType = log.TargetType,
TargetId = log.TargetId,
TargetName = log.TargetName,
OperationDetail = log.OperationDetail,
Result = log.Result,
SnapshotData = log.SnapshotData,
OptId = log.OptId,
OptName = log.OptName,
OptDate = log.OptDate
};
}
#endregion
}
}