diff --git a/Framework/YLErp.Core/DBModels/glms_risk_rule.cs b/Framework/YLErp.Core/DBModels/glms_risk_rule.cs index ad756d55..8359f712 100644 --- a/Framework/YLErp.Core/DBModels/glms_risk_rule.cs +++ b/Framework/YLErp.Core/DBModels/glms_risk_rule.cs @@ -16,6 +16,8 @@ namespace YLErp.DBModels public string ConditionJson { get; set; } /// 规则执行表达式(C# 布尔表达式,引擎用 Roslyn 编译执行) public string RuleExpr { get; set; } + /// 规则说明 + public string Description { get; set; } /// 规则状态 public RiskRuleStatus Status { get; set; } = RiskRuleStatus.Active; /// 版本号(乐观锁) diff --git a/Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs b/Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs index f6cefd70..89da2889 100644 --- a/Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs +++ b/Framework/YLErp.Core/DBModels/glms_risk_rule_application.cs @@ -10,6 +10,8 @@ namespace YLErp.DBModels { /// 关联规则 ID 列表(JSON 数组) public string RuleIds { get; set; } + /// 应用说明 + public string Description { get; set; } /// 应用配置状态 public RiskRuleStatus Status { get; set; } = RiskRuleStatus.Active; /// 风控策略(预警/阻断) diff --git a/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/prod.sql b/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/prod.sql index d9642a21..448439e8 100644 --- a/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/prod.sql +++ b/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/prod.sql @@ -6,6 +6,7 @@ CREATE TABLE `glms_risk_rule` ( `RuleText` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '规则文本', `ConditionJson` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '条件JSON(结构化模式, RuleCondition数组)', `RuleExpr` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '规则表达式(自由文本模式, 类C#表达式)', + `Description` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', `Status` tinyint NOT NULL DEFAULT 1 COMMENT '状态: 1=Active, 2=Disabled, 3=Deleted', `Version` int NOT NULL DEFAULT 1 COMMENT '版本号(乐观锁)', `OptId` int NULL DEFAULT NULL COMMENT '创建人Id', @@ -21,6 +22,7 @@ CREATE TABLE `glms_risk_rule` ( CREATE TABLE `glms_risk_rule_application` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键Id', `RuleIds` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '关联规则Id列表(逗号分隔)', + `Description` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', `Status` tinyint NOT NULL DEFAULT 1 COMMENT '状态: 1=Active, 2=Disabled, 3=Deleted', `ControlStrategy` tinyint NOT NULL COMMENT '控制策略: 1=Block, 2=Approval, 3=Warning', `TriggerPoints` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '触发时点(逗号分隔)', diff --git a/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskApplicationReq.cs b/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskApplicationReq.cs index 71914328..b426ea1c 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskApplicationReq.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskApplicationReq.cs @@ -9,6 +9,8 @@ namespace YLErp.Modules.RiskEngine.Dto { /// 关联规则 ID 列表(JSON 数组) public string RuleIds { get; set; } + /// 应用说明 + public string Description { get; set; } /// 风控策略 public RiskControlStrategy ControlStrategy { get; set; } /// 触发点列表(JSON 数组) diff --git a/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskRuleReq.cs b/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskRuleReq.cs index df27186d..280c3e17 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskRuleReq.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/CreateRiskRuleReq.cs @@ -13,5 +13,7 @@ namespace YLErp.Modules.RiskEngine.Dto public string ConditionJson { get; set; } /// 规则执行表达式 public string RuleExpr { get; set; } + /// 规则说明 + public string Description { get; set; } } } diff --git a/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationDetail.cs b/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationDetail.cs index 648a08b8..11d5cf7f 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationDetail.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationDetail.cs @@ -12,6 +12,8 @@ namespace YLErp.Modules.RiskEngine.Dto public long Id { get; set; } /// 关联规则 ID 列表(JSON 数组) public string RuleIds { get; set; } + /// 应用说明 + public string Description { get; set; } /// 应用配置状态 public RiskRuleStatus Status { get; set; } /// 风控策略 diff --git a/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationListItem.cs b/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationListItem.cs index fec34ea9..f50283ed 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationListItem.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/RiskApplicationListItem.cs @@ -12,6 +12,8 @@ namespace YLErp.Modules.RiskEngine.Dto public long Id { get; set; } /// 关联规则 ID 列表(JSON 数组) public string RuleIds { get; set; } + /// 应用说明 + public string Description { get; set; } /// 应用配置状态 public int Status { get; set; } /// 风控策略 diff --git a/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleDetail.cs b/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleDetail.cs index 09364933..cdb2e9ec 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleDetail.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleDetail.cs @@ -19,6 +19,8 @@ namespace YLErp.Modules.RiskEngine.Dto public string ConditionJson { get; set; } /// 规则执行表达式 public string RuleExpr { get; set; } + /// 规则说明 + public string Description { get; set; } /// 规则状态 public RiskRuleStatus Status { get; set; } /// 版本号 diff --git a/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleListItem.cs b/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleListItem.cs index 32a462e2..b35999a2 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleListItem.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleListItem.cs @@ -18,6 +18,8 @@ namespace YLErp.Modules.RiskEngine.Dto public string ConditionJson { get; set; } /// 规则执行表达式 public string RuleExpr { get; set; } + /// 规则说明 + public string Description { get; set; } /// 规则状态 public RiskRuleStatus Status { get; set; } /// 版本号 diff --git a/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskApplicationReq.cs b/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskApplicationReq.cs index f152f276..877639d0 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskApplicationReq.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskApplicationReq.cs @@ -9,6 +9,8 @@ namespace YLErp.Modules.RiskEngine.Dto { /// 关联规则 ID 列表(JSON 数组) public string RuleIds { get; set; } + /// 应用说明 + public string Description { get; set; } /// 风控策略 public RiskControlStrategy ControlStrategy { get; set; } /// 触发点列表(JSON 数组) diff --git a/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskRuleReq.cs b/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskRuleReq.cs index 9fa13d3c..0d19a313 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskRuleReq.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/UpdateRiskRuleReq.cs @@ -13,6 +13,8 @@ namespace YLErp.Modules.RiskEngine.Dto public string ConditionJson { get; set; } /// 规则执行表达式 public string RuleExpr { get; set; } + /// 规则说明 + public string Description { get; set; } /// 版本号(乐观锁) public int Version { get; set; } } diff --git a/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs b/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs index b2162888..783a6bbc 100644 --- a/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs +++ b/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs @@ -1,10 +1,8 @@ using BaseOUDAL; using Newtonsoft.Json; -using Qdp.Foundation.Utilities; using System.Linq.Expressions; using YLErp.BLL; using YLErp.DBModels; -using YLErp.Model; using YLErp.Modules.RiskEngine.Dto; /* @@ -694,6 +692,7 @@ namespace YLErp.Modules.RiskEngine RuleText = r.RuleText, ConditionJson = r.ConditionJson, RuleExpr = r.RuleExpr, + Description = r.Description, Status = r.Status, Version = r.Version, OptName = r.OptName, @@ -732,6 +731,7 @@ namespace YLErp.Modules.RiskEngine RuleText = rule.RuleText, ConditionJson = rule.ConditionJson, RuleExpr = rule.RuleExpr, + Description = rule.Description, Status = rule.Status, Version = rule.Version, OptName = rule.OptName, @@ -749,12 +749,17 @@ namespace YLErp.Modules.RiskEngine { ValidateRuleParams(req.RuleName, req.ConditionJson, req.RuleExpr); + var compileResult = RuleCompiler.ValidateAndCompileFormula(null, req.RuleExpr); + if (!compileResult.Success) + throw new ServiceException($"规则表达式编译失败:{compileResult.ErrorMessage}"); + var entity = new glms_risk_rule { RuleName = req.RuleName, RuleText = req.RuleText, ConditionJson = req.ConditionJson, RuleExpr = req.RuleExpr, + Description = req.Description, Status = RiskRuleStatus.Active, Version = 1 }; @@ -784,7 +789,7 @@ namespace YLErp.Modules.RiskEngine } } - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshOneRuleCache(entity.id); return GetRuleDetail(entity.id); } @@ -801,10 +806,15 @@ namespace YLErp.Modules.RiskEngine ValidateRuleParams(req.RuleName, req.ConditionJson, req.RuleExpr); + var compileResult = RuleCompiler.ValidateAndCompileFormula(ruleId, req.RuleExpr); + if (!compileResult.Success) + throw new ServiceException($"规则表达式编译失败:{compileResult.ErrorMessage}"); + rule.RuleName = req.RuleName; rule.RuleText = req.RuleText; rule.ConditionJson = req.ConditionJson; rule.RuleExpr = req.RuleExpr; + rule.Description = req.Description; rule.Version = rule.Version + 1; rule.UpdateOptId = UserId; rule.UpdateOptName = UserName; @@ -813,7 +823,7 @@ namespace YLErp.Modules.RiskEngine WriteAuditLog("RULE_UPDATE", "RULE", ruleId, req.RuleName, "修改规则"); DbContext.SaveChanges(); - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshOneRuleCache(ruleId); return GetRuleDetail(ruleId); } @@ -1074,6 +1084,7 @@ namespace YLErp.Modules.RiskEngine { Id = a.id, RuleIds = a.RuleIds, + Description = a.Description, Status = (int)a.Status, ControlStrategy = a.ControlStrategy, TriggerPoints = a.TriggerPoints, @@ -1107,6 +1118,7 @@ namespace YLErp.Modules.RiskEngine { Id = app.id, RuleIds = app.RuleIds, + Description = app.Description, Status = app.Status, ControlStrategy = app.ControlStrategy, TriggerPoints = app.TriggerPoints, @@ -1135,6 +1147,7 @@ namespace YLErp.Modules.RiskEngine var entity = new glms_risk_rule_application { RuleIds = req.RuleIds, + Description = req.Description, Status = RiskRuleStatus.Active, ControlStrategy = req.ControlStrategy, TriggerPoints = req.TriggerPoints, @@ -1172,7 +1185,7 @@ namespace YLErp.Modules.RiskEngine } } - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshApplication(); return GetApplicationDetail(entity.id); } @@ -1203,6 +1216,7 @@ namespace YLErp.Modules.RiskEngine if (!string.IsNullOrWhiteSpace(req.RuleIds)) app.RuleIds = req.RuleIds; + app.Description = req.Description; app.ControlStrategy = req.ControlStrategy; app.TriggerPoints = req.TriggerPoints; app.ScopeAssetBookIds = req.ScopeAssetBookIds; @@ -1219,7 +1233,7 @@ namespace YLErp.Modules.RiskEngine $"修改应用配置:策略={GetStrategyName(req.ControlStrategy)}, 触发时点={MapTriggerPointsToChinese(req.TriggerPoints)}"); DbContext.SaveChanges(); - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshApplication(); return GetApplicationDetail(applicationId); } @@ -1240,7 +1254,7 @@ namespace YLErp.Modules.RiskEngine WriteAuditLog("APP_DELETE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "删除应用配置"); DbContext.SaveChanges(); - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshApplication(); } /// @@ -1262,7 +1276,7 @@ namespace YLErp.Modules.RiskEngine WriteAuditLog("APP_ENABLE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "启用应用配置"); DbContext.SaveChanges(); - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshApplication(); } /// @@ -1282,7 +1296,7 @@ namespace YLErp.Modules.RiskEngine WriteAuditLog("APP_DISABLE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "停用应用配置"); DbContext.SaveChanges(); - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshApplication(); } /// @@ -1313,7 +1327,7 @@ namespace YLErp.Modules.RiskEngine } DbContext.SaveChanges(); - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshApplication(); return new BatchOperationResult { @@ -1346,7 +1360,7 @@ namespace YLErp.Modules.RiskEngine } DbContext.SaveChanges(); - TryRefreshCache(); + RiskEngineService.GetInstance().RefreshApplication(); return new BatchOperationResult { @@ -1534,14 +1548,24 @@ namespace YLErp.Modules.RiskEngine if (variableExprChanged) { + // 编译校验:变量表达式变更后,需验证所有级联更新的规则表达式可编译 + var affectedRules = DbContext.glms_risk_rule + .Where(r => r.RuleExpr != null && r.RuleExpr.Contains(oldVariableExpr)) + .ToList(); + + var compileTasks = affectedRules.Select(r => + Task.Run(() => RuleCompiler.ValidateAndCompileFormula( + r.id, r.RuleExpr.Replace(oldVariableExpr, req.VariableExpr))) + ); + var compileResults = Task.WhenAll(compileTasks).GetAwaiter().GetResult(); + var failed = compileResults.FirstOrDefault(r => !r.Success); + if (failed is not null) + throw new ServiceException($"变量表达式变更导致规则编译失败:{failed.ErrorMessage}"); + using (var transaction = DbContext.Database.BeginTransaction()) { try { - var affectedRules = DbContext.glms_risk_rule - .Where(r => r.RuleExpr != null && r.RuleExpr.Contains(oldVariableExpr)) - .ToList(); - foreach (var rule in affectedRules) { rule.RuleExpr = rule.RuleExpr.Replace(oldVariableExpr, req.VariableExpr); @@ -1570,16 +1594,20 @@ namespace YLErp.Modules.RiskEngine throw; } } + + InvalidateVariableCache(); + foreach (var rule in affectedRules) + { + RiskEngineService.GetInstance().RefreshOneRuleCache(rule.id); + } } else { WriteAuditLog("VAR_UPDATE", "VARIABLE", variableId, req.VariableName, $"修改变量:{variable.VariableName}"); DbContext.SaveChanges(); + InvalidateVariableCache(); } - InvalidateVariableCache(); - TryRefreshCache(); - return GetVariableDetail(variableId); } @@ -1735,4 +1763,4 @@ namespace YLErp.Modules.RiskEngine #endregion } -} +} \ No newline at end of file