From d7c09c793a488cec9699746fa0ce7d17ff040a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=B9=E5=B3=B0?= Date: Mon, 29 Jun 2026 18:09:19 +0800 Subject: [PATCH] =?UTF-8?q?fix(risk-engine):=20=E4=BF=AE=E5=A4=8D=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E9=A3=8E=E6=8E=A7=E5=BC=95=E6=93=8E=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ScopeIsGlobal 类型由 bool 改为 int 以兼容前端传输 0/1 - 修复 QueryApplicationList 中 Status 投影 EF Core enum 转换失败 - 新增 Scope 字段查询支持(逗号分隔集合精确匹配) - 新增变量列表 DataType 筛选 - 修复审计日志日期查询范围(EndDate 纳入整天数据) - 统一 Application 审计日志 OperationType 前缀(APP_xxx) - 审计日志 OperationDetail 枚举值映射为中文 --- .../DBModels/glms_risk_rule_application.cs | 2 +- .../DbUpdate/Ver-5.6.0/seed_rules.sql | 7 +- .../RiskEngine/Compile/RuleCompiler.cs | 7 + .../Dto/CreateRiskApplicationReq.cs | 2 +- .../RiskEngine/Dto/QueryRiskApplicationReq.cs | 4 + .../RiskEngine/Dto/QueryRiskVariableReq.cs | 1 + .../RiskEngine/Dto/RiskApplicationDetail.cs | 2 +- .../RiskEngine/Dto/RiskApplicationListItem.cs | 4 +- .../Modules/RiskEngine/Dto/RuleCondition.cs | 3 + .../Dto/UpdateRiskApplicationReq.cs | 2 +- .../Modules/RiskEngine/RiskEngineService.cs | 2 +- .../Modules/RiskEngine/RiskRuleApplication.cs | 2 +- .../Modules/RiskEngine/RiskRuleService.cs | 161 ++++++++++++++++-- YLErpWeb/App_Data/Menus.txt | 1 + 14 files changed, 173 insertions(+), 27 deletions(-) diff --git a/Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs b/Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs index aa5dbb6a..a1d577ac 100644 --- a/Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs +++ b/Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs @@ -13,7 +13,7 @@ namespace YLErp.DBModels public string ScopeClientIds { get; set; } public string ScopeUnderlyingTypes { get; set; } public string ScopeTradeTypes { get; set; } - public bool ScopeIsGlobal { get; set; } + public int ScopeIsGlobal { get; set; } public int Version { get; set; } = 1; public int? UpdateOptId { get; set; } public string UpdateOptName { get; set; } diff --git a/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/seed_rules.sql b/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/seed_rules.sql index 45a17cb9..9751a1d9 100644 --- a/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/seed_rules.sql +++ b/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/seed_rules.sql @@ -50,7 +50,7 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr '挂钩标的到期日早于合约到期日时禁止交易', JSON_ARRAY(JSON_OBJECT( 'VariableId', 22, - 'Operator', '<', 'ThresholdType', 'Variable', + 'Operator', '早于', 'ThresholdType', 'Variable', 'ThresholdVariableId', 3 )), 'market.UnderlyingMaturityDate < trade.ExerciseDate', @@ -87,7 +87,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr '保证金利率不在配置区间内(默认2%~5%)时触发审批', JSON_ARRAY(JSON_OBJECT( 'VariableId', 12, - 'Operator', '不介于', 'ThresholdType', 'Fixed', 'Value', JSON_ARRAY(2, 5) + 'Operator', '不介于', 'ThresholdType', 'Fixed', 'Value', JSON_ARRAY(2, 5), + 'IncludeLowerBound', true, 'IncludeUpperBound', true )), '!(client_marginrate.InitMarginRebateRate >= 2 && client_marginrate.InitMarginRebateRate <= 5)', 1, 1, 0, 'SYSTEM', NOW()); @@ -112,7 +113,7 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr '合约起息日早于当前日期时触发审批', JSON_ARRAY(JSON_OBJECT( 'VariableId', 2, - 'Operator', '<', 'ThresholdType', 'Variable', + 'Operator', '早于', 'ThresholdType', 'Variable', 'ThresholdVariableId', 21 )), 'trade.StartDate < sys.CurrentDate', diff --git a/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs index 0d2f778f..85875f15 100644 --- a/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs +++ b/YLErpDAL/Modules/RiskEngine/Compile/RuleCompiler.cs @@ -304,12 +304,19 @@ namespace YLErp.Modules.RiskEngine { return op?.Trim() switch { + // 数值型 C# 符号 "=" or "==" => "==", "!=" or "≠" => "!=", ">" => ">", "<" => "<", ">=" => ">=", "<=" => "<=", + // 日期型中文语义 → C# 符号 + "早于" => "<", + "晚于" => ">", + "等于" => "==", + "不早于" => ">=", + "不晚于" => "<=", _ => throw new ArgumentException($"不支持的操作符:{op}") }; } diff --git a/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskApplicationReq.cs b/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskApplicationReq.cs index f50d4f36..65e939c4 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskApplicationReq.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskApplicationReq.cs @@ -11,6 +11,6 @@ namespace YLErp.Modules.RiskEngine.Dto public string ScopeClientIds { get; set; } public string ScopeUnderlyingTypes { get; set; } public string ScopeTradeTypes { get; set; } - public bool ScopeIsGlobal { get; set; } + public int ScopeIsGlobal { get; set; } } } diff --git a/YLErpDAL/Modules/RiskEngine/Dto/QueryRiskApplicationReq.cs b/YLErpDAL/Modules/RiskEngine/Dto/QueryRiskApplicationReq.cs index bd494ed8..eec45ca7 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/QueryRiskApplicationReq.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/QueryRiskApplicationReq.cs @@ -9,5 +9,9 @@ namespace YLErp.Modules.RiskEngine.Dto public RiskRuleStatus? Status { get; set; } public RiskControlStrategy? Strategy { get; set; } public string TriggerPoint { get; set; } + public string ScopeClientIds { get; set; } + public string ScopeUnderlyingTypes { get; set; } + public string ScopeTradeTypes { get; set; } + public string ScopeAssetBookIds { get; set; } } } diff --git a/YLErpDAL/Modules/RiskEngine/Dto/QueryRiskVariableReq.cs b/YLErpDAL/Modules/RiskEngine/Dto/QueryRiskVariableReq.cs index 639d9b3d..9b8f39bd 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/QueryRiskVariableReq.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/QueryRiskVariableReq.cs @@ -6,6 +6,7 @@ namespace YLErp.Modules.RiskEngine.Dto public class QueryRiskVariableReq : BaseSearchReq { public RiskVariableCategory? Category { get; set; } + public RiskVariableDataType? DataType { get; set; } public string VariableName { get; set; } } } diff --git a/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationDetail.cs b/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationDetail.cs index 52824a0d..cb76b20c 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationDetail.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationDetail.cs @@ -14,7 +14,7 @@ namespace YLErp.Modules.RiskEngine.Dto public string ScopeClientIds { get; set; } public string ScopeUnderlyingTypes { get; set; } public string ScopeTradeTypes { get; set; } - public bool ScopeIsGlobal { get; set; } + public int ScopeIsGlobal { get; set; } public int Version { get; set; } public string OptName { get; set; } public DateTime OptDate { get; set; } diff --git a/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationListItem.cs b/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationListItem.cs index fcaaf7df..466b1d94 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationListItem.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationListItem.cs @@ -7,14 +7,14 @@ namespace YLErp.Modules.RiskEngine.Dto { public long Id { get; set; } public string RuleIds { get; set; } - public RiskRuleStatus Status { get; set; } + public int Status { get; set; } public RiskControlStrategy ControlStrategy { get; set; } public string TriggerPoints { get; set; } public string ScopeAssetBookIds { get; set; } public string ScopeClientIds { get; set; } public string ScopeUnderlyingTypes { get; set; } public string ScopeTradeTypes { get; set; } - public bool ScopeIsGlobal { get; set; } + public int ScopeIsGlobal { get; set; } public int Version { get; set; } public string OptName { get; set; } public DateTime OptDate { get; set; } diff --git a/YLErpDAL/Modules/RiskEngine/Dto/RuleCondition.cs b/YLErpDAL/Modules/RiskEngine/Dto/RuleCondition.cs index 81facae9..d1e459ff 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/RuleCondition.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/RuleCondition.cs @@ -7,5 +7,8 @@ namespace YLErp.Modules.RiskEngine.Dto public string ThresholdType { get; set; } public object Value { get; set; } public long? ThresholdVariableId { get; set; } + // 介于/不介于 的边界包含控制,默认 true + public bool IncludeLowerBound { get; set; } = true; + public bool IncludeUpperBound { get; set; } = true; } } diff --git a/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskApplicationReq.cs b/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskApplicationReq.cs index b9af55a9..aa290529 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskApplicationReq.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskApplicationReq.cs @@ -11,7 +11,7 @@ namespace YLErp.Modules.RiskEngine.Dto public string ScopeClientIds { get; set; } public string ScopeUnderlyingTypes { get; set; } public string ScopeTradeTypes { get; set; } - public bool ScopeIsGlobal { get; set; } + public int ScopeIsGlobal { get; set; } public int Version { get; set; } } } diff --git a/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs b/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs index b357f444..70561bd5 100644 --- a/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs +++ b/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs @@ -436,7 +436,7 @@ namespace YLErp.Modules.RiskEngine return false; } - if (application.ScopeIsGlobal) + if (application.ScopeIsGlobal == 1) { return true; } diff --git a/YLErpDAL/Modules/RiskEngine/RiskRuleApplication.cs b/YLErpDAL/Modules/RiskEngine/RiskRuleApplication.cs index 7b741252..9e186dbf 100644 --- a/YLErpDAL/Modules/RiskEngine/RiskRuleApplication.cs +++ b/YLErpDAL/Modules/RiskEngine/RiskRuleApplication.cs @@ -67,7 +67,7 @@ namespace YLErp.Modules.RiskEngine /// true 表示对所有交易都适用,此时通常忽略其他 Scope 字段; /// false 表示只在指定范围内适用,需要结合下方各 Scope 字段进行过滤。 /// - public bool ScopeIsGlobal { get; set; } + public int ScopeIsGlobal { get; set; } /// /// 适用的资产簿记账户范围。 diff --git a/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs b/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs index 4e082af5..d8f1316f 100644 --- a/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs +++ b/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs @@ -1,6 +1,7 @@ using BaseOUDAL; using Newtonsoft.Json; using Qdp.Foundation.Utilities; +using System.Linq.Expressions; using YLErp.BLL; using YLErp.DBModels; using YLErp.Model; @@ -202,7 +203,7 @@ namespace YLErp.Modules.RiskEngine private static readonly Dictionary> ValidOperatorsByType = new Dictionary> { { RiskVariableDataType.Numeric, new HashSet { ">", "<", ">=", "<=", "=", "≠", "介于", "不介于" } }, - { RiskVariableDataType.Date, new HashSet { "<", ">", "=", ">=", "<=", "介于", "不介于" } }, + { RiskVariableDataType.Date, new HashSet { "早于", "晚于", "等于", "不早于", "不晚于", "介于", "不介于" } }, { RiskVariableDataType.Boolean, new HashSet { "是", "否" } } }; @@ -291,15 +292,35 @@ namespace YLErp.Modules.RiskEngine { if (cond.Value == null) throw new ServiceException($"{condLabel}:固定阈值不能为空"); - if (variableDef.DataType == RiskVariableDataType.Numeric) + + if (cond.Operator == "介于" || cond.Operator == "不介于") { - if (!decimal.TryParse(cond.Value.ToString(), out _)) - throw new ServiceException($"{condLabel}:数值型变量的阈值必须为数字"); + // 介于/不介于:Value 必须为双元素数组 [下限, 上限] + if (!(cond.Value is Newtonsoft.Json.Linq.JArray arr && arr.Count == 2)) + throw new ServiceException($"{condLabel}:操作符 '{cond.Operator}' 的阈值必须为双元素数组 [下限, 上限]"); + if (variableDef.DataType == RiskVariableDataType.Numeric) + { + if (!decimal.TryParse(arr[0].ToString(), out _) || !decimal.TryParse(arr[1].ToString(), out _)) + throw new ServiceException($"{condLabel}:数值型介于/不介于的下限和上限必须为数字"); + } + if (variableDef.DataType == RiskVariableDataType.Date) + { + if (!DateTime.TryParse(arr[0].ToString(), out _) || !DateTime.TryParse(arr[1].ToString(), out _)) + throw new ServiceException($"{condLabel}:日期型介于/不介于的起始和结束日期必须为合法日期"); + } } - if (variableDef.DataType == RiskVariableDataType.Date) + else { - if (!DateTime.TryParse(cond.Value.ToString(), out _)) - 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") @@ -356,7 +377,7 @@ namespace YLErp.Modules.RiskEngine private void ValidateScopeFields(CreateRiskApplicationReq req) { - if (req.ScopeIsGlobal) + if (req.ScopeIsGlobal == 1) { if (!string.IsNullOrEmpty(req.ScopeAssetBookIds) || !string.IsNullOrEmpty(req.ScopeClientIds) || @@ -513,6 +534,36 @@ namespace YLErp.Modules.RiskEngine return string.Join(", ", names); } + private static string GetStrategyName(RiskControlStrategy strategy) + { + return strategy switch + { + RiskControlStrategy.Block => "阻断", + RiskControlStrategy.Approval => "审批", + RiskControlStrategy.ShowTip => "提醒", + _ => strategy.ToString() + }; + } + + private static readonly Dictionary TriggerPointCnMap = new Dictionary + { + ["BOOK_CONFIRM"] = "交易录入确认", + ["CLOSE_REVIEW"] = "平仓审核", + ["UPLOAD_CONFIRMATION"] = "上传确认书", + ["EVENT_TRIGGER"] = "事件触发", + ["FUND_PAYMENT"] = "资金支付" + }; + + private static string MapTriggerPointsToChinese(string triggerPoints) + { + if (string.IsNullOrWhiteSpace(triggerPoints)) + return ""; + + var parts = triggerPoints.Split(','); + var translated = parts.Select(p => TriggerPointCnMap.TryGetValue(p.Trim(), out var cn) ? cn : p.Trim()); + return string.Join(", ", translated); + } + #endregion #region Rule Management @@ -813,7 +864,7 @@ namespace YLErp.Modules.RiskEngine { Id = app.id, RuleIds = app.RuleIds, - Status = app.Status, + Status = (int)app.Status, ControlStrategy = app.ControlStrategy, TriggerPoints = app.TriggerPoints, ScopeAssetBookIds = app.ScopeAssetBookIds, @@ -878,12 +929,84 @@ namespace YLErp.Modules.RiskEngine && r.RuleName.Contains(req.RuleName))); } + if (!string.IsNullOrWhiteSpace(req.ScopeClientIds)) + { + var ids = req.ScopeClientIds.Split(',', StringSplitOptions.RemoveEmptyEntries); + Expression> scopePredicate = null; + foreach (var id in ids) + { + var idCopy = id; + Expression> expr = a => + a.ScopeClientIds == idCopy || + a.ScopeClientIds.StartsWith(idCopy + ",") || + a.ScopeClientIds.EndsWith("," + idCopy) || + a.ScopeClientIds.Contains("," + idCopy + ","); + scopePredicate = scopePredicate == null ? expr : scopePredicate.Or(expr); + } + if (scopePredicate != null) + query = query.Where(scopePredicate); + } + + if (!string.IsNullOrWhiteSpace(req.ScopeUnderlyingTypes)) + { + var types = req.ScopeUnderlyingTypes.Split(',', StringSplitOptions.RemoveEmptyEntries); + Expression> scopePredicate = null; + foreach (var type in types) + { + var typeCopy = type; + Expression> expr = a => + a.ScopeUnderlyingTypes == typeCopy || + a.ScopeUnderlyingTypes.StartsWith(typeCopy + ",") || + a.ScopeUnderlyingTypes.EndsWith("," + typeCopy) || + a.ScopeUnderlyingTypes.Contains("," + typeCopy + ","); + scopePredicate = scopePredicate == null ? expr : scopePredicate.Or(expr); + } + if (scopePredicate != null) + query = query.Where(scopePredicate); + } + + if (!string.IsNullOrWhiteSpace(req.ScopeTradeTypes)) + { + var types = req.ScopeTradeTypes.Split(',', StringSplitOptions.RemoveEmptyEntries); + Expression> scopePredicate = null; + foreach (var type in types) + { + var typeCopy = type; + Expression> expr = a => + a.ScopeTradeTypes == typeCopy || + a.ScopeTradeTypes.StartsWith(typeCopy + ",") || + a.ScopeTradeTypes.EndsWith("," + typeCopy) || + a.ScopeTradeTypes.Contains("," + typeCopy + ","); + scopePredicate = scopePredicate == null ? expr : scopePredicate.Or(expr); + } + if (scopePredicate != null) + query = query.Where(scopePredicate); + } + + if (!string.IsNullOrWhiteSpace(req.ScopeAssetBookIds)) + { + var ids = req.ScopeAssetBookIds.Split(',', StringSplitOptions.RemoveEmptyEntries); + Expression> scopePredicate = null; + foreach (var id in ids) + { + var idCopy = id; + Expression> expr = a => + a.ScopeAssetBookIds == idCopy || + a.ScopeAssetBookIds.StartsWith(idCopy + ",") || + a.ScopeAssetBookIds.EndsWith("," + idCopy) || + a.ScopeAssetBookIds.Contains("," + idCopy + ","); + scopePredicate = scopePredicate == null ? expr : scopePredicate.Or(expr); + } + if (scopePredicate != null) + query = query.Where(scopePredicate); + } + var pagedResult = query.OrderByDescending(a => a.UpdateDate) .Select(a => new RiskApplicationListItem { Id = a.id, RuleIds = a.RuleIds, - Status = a.Status, + Status = (int)a.Status, ControlStrategy = a.ControlStrategy, TriggerPoints = a.TriggerPoints, ScopeAssetBookIds = a.ScopeAssetBookIds, @@ -962,7 +1085,7 @@ namespace YLErp.Modules.RiskEngine DbContext.SaveChanges(); WriteAuditLog("APP_CREATE", "APPLICATION", entity.id, ResolveRuleNames(req.RuleIds), - $"创建应用配置:策略={req.ControlStrategy}, 触发时点={req.TriggerPoints}", + $"创建应用配置:策略={GetStrategyName(req.ControlStrategy)}, 触发时点={MapTriggerPointsToChinese(req.TriggerPoints)}", snapshotData: JsonConvert.SerializeObject(new { Version = entity.Version })); DbContext.SaveChanges(); @@ -1016,7 +1139,7 @@ namespace YLErp.Modules.RiskEngine app.UpdateDate = DateTime.Now; WriteAuditLog("APP_UPDATE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), - $"修改应用配置:策略={req.ControlStrategy}, 触发时点={req.TriggerPoints}"); + $"修改应用配置:策略={GetStrategyName(req.ControlStrategy)}, 触发时点={MapTriggerPointsToChinese(req.TriggerPoints)}"); DbContext.SaveChanges(); TryRefreshCache(); @@ -1034,7 +1157,7 @@ namespace YLErp.Modules.RiskEngine app.UpdateOptName = UserName; app.UpdateDate = DateTime.Now; - WriteAuditLog("APPLICATION_DELETE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "删除应用配置"); + WriteAuditLog("APP_DELETE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "删除应用配置"); DbContext.SaveChanges(); TryRefreshCache(); @@ -1127,7 +1250,7 @@ namespace YLErp.Modules.RiskEngine app.UpdateOptName = UserName; app.UpdateDate = DateTime.Now; - WriteAuditLog("APPLICATION_BATCH_DISABLE", "APPLICATION", app.id, ResolveRuleNames(app.RuleIds), "批量停用应用配置"); + WriteAuditLog("APP_BATCH_DISABLE", "APPLICATION", app.id, ResolveRuleNames(app.RuleIds), "批量停用应用配置"); } DbContext.SaveChanges(); @@ -1154,6 +1277,11 @@ namespace YLErp.Modules.RiskEngine query = query.Where(v => v.Category == req.Category.Value); } + if (req.DataType.HasValue) + { + query = query.Where(v => v.DataType == req.DataType.Value); + } + if (!string.IsNullOrWhiteSpace(req.VariableName)) { query = query.Where(v => v.VariableName.Contains(req.VariableName)); @@ -1365,12 +1493,13 @@ namespace YLErp.Modules.RiskEngine if (req.StartDate.HasValue) { - query = query.Where(l => l.OptDate >= req.StartDate.Value); + query = query.Where(l => l.OptDate >= req.StartDate.Value.Date); } if (req.EndDate.HasValue) { - query = query.Where(l => l.OptDate <= req.EndDate.Value); + var end = req.EndDate.Value.Date.AddDays(1); + query = query.Where(l => l.OptDate < end); } if (!string.IsNullOrWhiteSpace(req.Keyword)) diff --git a/YLErpWeb/App_Data/Menus.txt b/YLErpWeb/App_Data/Menus.txt index a8aea562..7b460406 100644 --- a/YLErpWeb/App_Data/Menus.txt +++ b/YLErpWeb/App_Data/Menus.txt @@ -25,6 +25,7 @@ {Name:"资金监控",Rights:["风险控制-资金监控"],Url:"client/clientRiskMonitor"}, {Name:"市场风险",Rights:["风险控制-市场风险"],Url:"risk/RiskExposureReport"}, {Name:"限额监控",Rights:["风险控制-限额监控"],Url:"risk/quotaMonitor"}, + {Name:"异常交易监控",Rights:["风险控制-异常交易监控"],Url:"v3//risk/risk-engine-config"}, {Name:"白名单券池",Rights:["风险控制-白名单券池"],Url:"v3/data/underlying-pool"}, {Name:"日终持仓风险",Rights:["风险控制-日终持仓风险"],Url:"trade/EodPositionRisks"}, {Name:"日终持仓风险_互换",Rights:["风险控制-日终持仓风险_互换"],Url:"swaptrade2/EodPositionRisks"},