From 3bf90e25efbbeaf20af0076b0272086f4d06f4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=B9=E5=B3=B0?= Date: Thu, 25 Jun 2026 20:44:30 +0800 Subject: [PATCH] =?UTF-8?q?refactor(risk-engine):=20ConditionJson=20?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20RuleCondition=20=E6=95=B0=E7=BB=84?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=EF=BC=8C=E7=B2=BE=E7=AE=80=20DTO=20=E5=91=BD?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重命名 FormulaCondition → RuleCondition,删除 FormulaDefinition - ValidateConditionJson 直接反序列化为 List,移除 LogicOperator 校验 - 新增 RiskRuleSimpleItem DTO - 同步更新建表注释与种子数据 --- .../DbUpdate/Ver-5.6.0/prod.sql | 2 +- .../DbUpdate/Ver-5.6.0/seed_rules.sql | 111 ++++++++---------- .../RiskEngine/Dto/FormulaDefinition.cs | 10 -- .../RiskEngine/Dto/RiskRuleSimpleItem.cs | 9 ++ .../{FormulaCondition.cs => RuleCondition.cs} | 6 +- .../Modules/RiskEngine/RiskRuleService.cs | 60 +++++----- YLErpWeb/Controllers/RiskRuleController.cs | 21 ++++ 7 files changed, 112 insertions(+), 107 deletions(-) delete mode 100644 YLErpDAL/Modules/RiskEngine/Dto/FormulaDefinition.cs create mode 100644 YLErpDAL/Modules/RiskEngine/Dto/RiskRuleSimpleItem.cs rename YLErpDAL/Modules/RiskEngine/Dto/{FormulaCondition.cs => RuleCondition.cs} (55%) 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 3f266be1..d9642a21 100644 --- a/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/prod.sql +++ b/Framework/YLErp.Resources/DbUpdate/Ver-5.6.0/prod.sql @@ -4,7 +4,7 @@ CREATE TABLE `glms_risk_rule` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键Id', `RuleName` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '规则名称', `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(结构化模式, FormulaCondition数组)', + `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#表达式)', `Status` tinyint NOT NULL DEFAULT 1 COMMENT '状态: 1=Active, 2=Disabled, 3=Deleted', `Version` int NOT NULL DEFAULT 1 COMMENT '版本号(乐观锁)', 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 33b72018..45a17cb9 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 @@ -21,6 +21,9 @@ -- 45=期初净价偏离度 46=期初收益率偏离度 47=期初价格偏离度 -- 48=利息端利率与FR007偏离度 49=利息端利率与借贷加权费率偏离度 -- 50=执行价偏离度 51=执行价 52=客户品种最低保证金率 53=参考价格 +-- +-- ConditionJson 精简设计:只存 VariableId/Operator/ThresholdType/Value/ThresholdVariableId +-- 展示字段(VariableName/VariableType/Unit/ThresholdVariableName)从变量接口关联获取 -- ============================================================ @@ -34,9 +37,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('挂钩标的集中度超阈值', '挂钩标的集中度超过阈值(默认30%)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 24, 'VariableName', '挂钩标的集中度', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 30, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 24, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 30 )), 'calc.UnderlyingConcentration > 30', 1, 1, 0, 'SYSTEM', NOW()); @@ -47,9 +49,9 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('挂钩标的到期日小于合约到期日', '挂钩标的到期日早于合约到期日时禁止交易', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 22, 'VariableName', '挂钩标的到期日', 'VariableType', 'Date', - 'Operator', '<', 'ThresholdType', 'variable', 'Value', JSON_TYPE(NULL), - 'ThresholdVariableId', 3, 'ThresholdVariableName', '合约到期日', 'Unit', JSON_TYPE(NULL) + 'VariableId', 22, + 'Operator', '<', 'ThresholdType', 'Variable', + 'ThresholdVariableId', 3 )), 'market.UnderlyingMaturityDate < trade.ExerciseDate', 1, 1, 0, 'SYSTEM', NOW()); @@ -60,9 +62,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('名义本金超阈值', '合约名义本金超过阈值(默认1亿元)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 1, 'VariableName', '合约名义本金', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 100000000, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '元' + 'VariableId', 1, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 100000000 )), 'trade.StockEqvNotional > 100000000', 1, 1, 0, 'SYSTEM', NOW()); @@ -73,9 +74,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('保证金支付比例超阈值', '保证金比例超过阈值(默认50%)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 13, 'VariableName', '保证金比例', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 50, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 13, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 50 )), 'trade.MarginRate > 50', 1, 1, 0, 'SYSTEM', NOW()); @@ -86,9 +86,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('保证金利率偏离', '保证金利率不在配置区间内(默认2%~5%)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 12, 'VariableName', '保证金利率', 'VariableType', 'Numeric', - 'Operator', '不介于', 'ThresholdType', 'fixed', 'Value', JSON_ARRAY(2, 5), - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 12, + 'Operator', '不介于', 'ThresholdType', 'Fixed', 'Value', JSON_ARRAY(2, 5) )), '!(client_marginrate.InitMarginRebateRate >= 2 && client_marginrate.InitMarginRebateRate <= 5)', 1, 1, 0, 'SYSTEM', NOW()); @@ -99,9 +98,9 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('保证金收取比例低于最低标准', '保证金比例低于客户品种最低保证金率时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 13, 'VariableName', '保证金比例', 'VariableType', 'Numeric', - 'Operator', '<', 'ThresholdType', 'variable', 'Value', JSON_TYPE(NULL), - 'ThresholdVariableId', 52, 'ThresholdVariableName', '客户品种最低保证金率', 'Unit', '%' + 'VariableId', 13, + 'Operator', '<', 'ThresholdType', 'Variable', + 'ThresholdVariableId', 52 )), 'trade.MarginRate < config.MinMarginRate', 1, 1, 0, 'SYSTEM', NOW()); @@ -112,9 +111,9 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('起息日早于当前日期', '合约起息日早于当前日期时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 2, 'VariableName', '合约起息日', 'VariableType', 'Date', - 'Operator', '<', 'ThresholdType', 'variable', 'Value', JSON_TYPE(NULL), - 'ThresholdVariableId', 21, 'ThresholdVariableName', '当前日期', 'Unit', JSON_TYPE(NULL) + 'VariableId', 2, + 'Operator', '<', 'ThresholdType', 'Variable', + 'ThresholdVariableId', 21 )), 'trade.StartDate < sys.CurrentDate', 1, 1, 0, 'SYSTEM', NOW()); @@ -125,9 +124,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('支付日为银行间交易日', '支付日为银行间交易日时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 38, 'VariableName', '支付日是否银行间交易日', 'VariableType', 'Boolean', - 'Operator', '是', 'ThresholdType', 'fixed', 'Value', true, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', JSON_TYPE(NULL) + 'VariableId', 38, + 'Operator', '是', 'ThresholdType', 'Fixed', 'Value', true )), 'calc.IsSettlementDateTradingDay == true', 1, 1, 0, 'SYSTEM', NOW()); @@ -138,9 +136,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('到期日为银行间交易日', '到期日为银行间交易日时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 36, 'VariableName', '到期日是否银行间交易日', 'VariableType', 'Boolean', - 'Operator', '是', 'ThresholdType', 'fixed', 'Value', true, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', JSON_TYPE(NULL) + 'VariableId', 36, + 'Operator', '是', 'ThresholdType', 'Fixed', 'Value', true )), 'calc.IsExerciseDateTradingDay == true', 1, 1, 0, 'SYSTEM', NOW()); @@ -151,9 +148,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('平仓日为银行间交易日', '平仓日为银行间交易日时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 37, 'VariableName', '平仓日是否银行间交易日', 'VariableType', 'Boolean', - 'Operator', '是', 'ThresholdType', 'fixed', 'Value', true, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', JSON_TYPE(NULL) + 'VariableId', 37, + 'Operator', '是', 'ThresholdType', 'Fixed', 'Value', true )), 'calc.IsUnwindDateTradingDay == true', 1, 1, 0, 'SYSTEM', NOW()); @@ -164,9 +160,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('合约期限超阈值', '合约期限超过阈值(默认365天)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 26, 'VariableName', '合约期限', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 365, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '天' + 'VariableId', 26, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 365 )), 'calc.MaturityDays > 365', 1, 1, 0, 'SYSTEM', NOW()); @@ -177,9 +172,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('债券类净价偏离', '期初净价与上一收盘日中债估值净价偏离度超阈值(默认5%)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 45, 'VariableName', '期初净价偏离度', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 5, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 45, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 5 )), 'Math.Abs(swap_position.PosiNetNoFeePrice - market.CBValuationNetPrice) / market.CBValuationNetPrice * 100 > 5', 1, 1, 0, 'SYSTEM', NOW()); @@ -190,9 +184,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('债券类收益率偏离', '期初收益率与上一收盘日中债估值收益率偏离度超阈值(默认5%)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 46, 'VariableName', '期初收益率偏离度', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 5, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 46, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 5 )), 'Math.Abs(trade.InitYtm - market.CBValuationYtm) / market.CBValuationYtm * 100 > 5', 1, 1, 0, 'SYSTEM', NOW()); @@ -203,9 +196,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('非债券类价格偏离', '期初价格与上一日收盘价偏离度超阈值(默认5%)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 47, 'VariableName', '期初价格偏离度', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 5, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 47, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 5 )), 'Math.Abs(trade.SpotPrice - market.LastClosePrice) / market.LastClosePrice * 100 > 5', 1, 1, 0, 'SYSTEM', NOW()); @@ -216,9 +208,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('单一交易对手累计标的数量超阈值', '对手方累计标的数量超过阈值(默认10个)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 32, 'VariableName', '对手方累计标的数量', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 10, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '个' + 'VariableId', 32, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 10 )), 'calc.CounterpartyUnderlyingCount > 10', 1, 1, 0, 'SYSTEM', NOW()); @@ -230,14 +221,12 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr '多头方向支付固定端场景下,利息端利率与FR007偏离度超阈值(默认3%)时触发审批', JSON_ARRAY( JSON_OBJECT( - 'VariableId', 41, 'VariableName', '多空方向为多头', 'VariableType', 'Boolean', - 'Operator', '是', 'ThresholdType', 'fixed', 'Value', true, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', JSON_TYPE(NULL) + 'VariableId', 41, + 'Operator', '是', 'ThresholdType', 'Fixed', 'Value', true ), JSON_OBJECT( - 'VariableId', 48, 'VariableName', '利息端利率与FR007偏离度', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 3, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 48, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 3 ) ), 'calc.IsLongDirection == true && Math.Abs(calc.InterestRate - market.FR007) / market.FR007 * 100 > 3', @@ -250,14 +239,12 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr '空头方向场景下,利息端利率与借贷加权费率偏离度超阈值(默认2%)时触发审批', JSON_ARRAY( JSON_OBJECT( - 'VariableId', 42, 'VariableName', '多空方向为空头', 'VariableType', 'Boolean', - 'Operator', '是', 'ThresholdType', 'fixed', 'Value', true, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', JSON_TYPE(NULL) + 'VariableId', 42, + 'Operator', '是', 'ThresholdType', 'Fixed', 'Value', true ), JSON_OBJECT( - 'VariableId', 49, 'VariableName', '利息端利率与借贷加权费率偏离度', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 2, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 49, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 2 ) ), 'calc.IsShortDirection == true && Math.Abs(calc.InterestRate - market.BondLendingRate) / market.BondLendingRate * 100 > 2', @@ -269,9 +256,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('账户授权收支方向不匹配', '保证金收支方向与账户授权方向不匹配时禁止交易', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 43, 'VariableName', '保证金收支方向为支付', 'VariableType', 'Boolean', - 'Operator', '是', 'ThresholdType', 'fixed', 'Value', true, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', JSON_TYPE(NULL) + 'VariableId', 43, + 'Operator', '是', 'ThresholdType', 'Fixed', 'Value', true )), 'calc.IsMarginPay == true', 1, 1, 0, 'SYSTEM', NOW()); @@ -282,9 +268,8 @@ INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr ('执行价偏离超阈值', '执行价与参考价格偏离度超阈值(默认5%)时触发审批', JSON_ARRAY(JSON_OBJECT( - 'VariableId', 50, 'VariableName', '执行价偏离度', 'VariableType', 'Numeric', - 'Operator', '>', 'ThresholdType', 'fixed', 'Value', 5, - 'ThresholdVariableId', JSON_TYPE(NULL), 'ThresholdVariableName', JSON_TYPE(NULL), 'Unit', '%' + 'VariableId', 50, + 'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 5 )), 'Math.Abs(trade.StrikePrice - market.ReferencePrice) / market.ReferencePrice * 100 > 5', 1, 1, 0, 'SYSTEM', NOW()); diff --git a/YLErpDAL/Modules/RiskEngine/Dto/FormulaDefinition.cs b/YLErpDAL/Modules/RiskEngine/Dto/FormulaDefinition.cs deleted file mode 100644 index cd85d0d7..00000000 --- a/YLErpDAL/Modules/RiskEngine/Dto/FormulaDefinition.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace YLErp.Modules.RiskEngine.Dto -{ - public class FormulaDefinition - { - public string LogicOperator { get; set; } - public List Conditions { get; set; } - } -} diff --git a/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleSimpleItem.cs b/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleSimpleItem.cs new file mode 100644 index 00000000..aea6a343 --- /dev/null +++ b/YLErpDAL/Modules/RiskEngine/Dto/RiskRuleSimpleItem.cs @@ -0,0 +1,9 @@ +namespace YLErp.Modules.RiskEngine.Dto +{ + public class RiskRuleSimpleItem + { + public long Id { get; set; } + public string RuleName { get; set; } + public string RuleText { get; set; } + } +} diff --git a/YLErpDAL/Modules/RiskEngine/Dto/FormulaCondition.cs b/YLErpDAL/Modules/RiskEngine/Dto/RuleCondition.cs similarity index 55% rename from YLErpDAL/Modules/RiskEngine/Dto/FormulaCondition.cs rename to YLErpDAL/Modules/RiskEngine/Dto/RuleCondition.cs index 5f313d70..81facae9 100644 --- a/YLErpDAL/Modules/RiskEngine/Dto/FormulaCondition.cs +++ b/YLErpDAL/Modules/RiskEngine/Dto/RuleCondition.cs @@ -1,15 +1,11 @@ namespace YLErp.Modules.RiskEngine.Dto { - public class FormulaCondition + public class RuleCondition { public long VariableId { get; set; } - public string VariableName { get; set; } - public string VariableType { get; set; } public string Operator { get; set; } public string ThresholdType { get; set; } public object Value { get; set; } public long? ThresholdVariableId { get; set; } - public string ThresholdVariableName { get; set; } - public string Unit { get; set; } } } diff --git a/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs b/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs index 58c9f5e5..8767bfc4 100644 --- a/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs +++ b/YLErpDAL/Modules/RiskEngine/RiskRuleService.cs @@ -79,6 +79,7 @@ using YLErp.Modules.RiskEngine.Dto; GetRuleVersions(long) 获取规则版本历史(从审计日志 SnapshotData 提取版本号) BatchDeleteRules(List) 批量删除规则(逐条保护检查,单事务) GetRuleApplications(long) 获取规则关联的应用配置列表 + GetAllRuleList() 获取所有启用规则(轻量字段,供应用配置下拉) ── 应用配置管理(9 个) ─────────────────────────────────────────── QueryApplicationList(QueryRiskApplicationReq) 查询应用列表(多条件筛选,内存关联规则名称) @@ -259,25 +260,22 @@ namespace YLErp.Modules.RiskEngine private void ValidateConditionJson(string conditionJson) { - FormulaDefinition formula; + List conditions; try { - formula = JsonConvert.DeserializeObject(conditionJson); + conditions = JsonConvert.DeserializeObject>(conditionJson); } catch { throw new ServiceException("公式表达式 JSON 格式不合法"); } - if (formula.Conditions == null || formula.Conditions.Count == 0) + if (conditions == null || conditions.Count == 0) throw new ServiceException("公式条件列表不能为空"); - if (formula.LogicOperator != "AND") - throw new ServiceException("当前仅支持 AND 逻辑运算符"); - - for (int i = 0; i < formula.Conditions.Count; i++) + for (int i = 0; i < conditions.Count; i++) { - var cond = formula.Conditions[i]; + var cond = conditions[i]; var condLabel = $"条件{i + 1}"; var variableDef = DbContext.glms_risk_variable @@ -289,7 +287,7 @@ namespace YLErp.Modules.RiskEngine if (!validOperators.Contains(cond.Operator)) throw new ServiceException($"{condLabel}:操作符 '{cond.Operator}' 不适用于{GetDataTypeName(variableDef.DataType)}类型变量"); - if (cond.ThresholdType == "fixed") + if (cond.ThresholdType == "Fixed") { if (cond.Value == null) throw new ServiceException($"{condLabel}:固定阈值不能为空"); @@ -304,7 +302,7 @@ namespace YLErp.Modules.RiskEngine throw new ServiceException($"{condLabel}:日期型变量的阈值必须为合法日期"); } } - else if (cond.ThresholdType == "variable") + else if (cond.ThresholdType == "Variable") { if (!cond.ThresholdVariableId.HasValue) throw new ServiceException($"{condLabel}:变量阈值引用的变量 ID 不能为空"); @@ -317,7 +315,7 @@ namespace YLErp.Modules.RiskEngine } else { - throw new ServiceException($"{condLabel}:阈值类型 '{cond.ThresholdType}' 不合法,仅支持 fixed/variable"); + throw new ServiceException($"{condLabel}:阈值类型 '{cond.ThresholdType}' 不合法,仅支持 Fixed/Variable"); } } } @@ -445,23 +443,15 @@ namespace YLErp.Modules.RiskEngine if (ruleName.Length > 200) throw new ServiceException("规则名称长度不能超过200字符"); - bool hasStructured = !string.IsNullOrWhiteSpace(conditionJson); - bool hasFreeText = !string.IsNullOrWhiteSpace(ruleExpr); + if (string.IsNullOrWhiteSpace(ruleExpr)) + throw new ServiceException("规则表达式不能为空"); - if (!hasStructured && !hasFreeText) - throw new ServiceException("公式表达式不能为空,请提供结构化条件或自由文本表达式"); - - if (hasStructured && hasFreeText) - throw new ServiceException("结构化条件和自由文本表达式不能同时提供,请选择其中一种模式"); - - if (hasStructured) + if (!string.IsNullOrWhiteSpace(conditionJson)) { ValidateConditionJson(conditionJson); } - else - { - ValidateRuleExpr(ruleExpr); - } + + ValidateRuleExpr(ruleExpr); } private List ParseRuleIds(string ruleIds) @@ -843,6 +833,20 @@ namespace YLErp.Modules.RiskEngine return result; } + public List 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 @@ -1077,7 +1081,7 @@ namespace YLErp.Modules.RiskEngine app.UpdateOptName = UserName; app.UpdateDate = DateTime.Now; - WriteAuditLog("APPLICATION_UPDATE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), + WriteAuditLog("APP_UPDATE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), $"修改应用配置:策略={req.ControlStrategy}, 触发时点={req.TriggerPoints}"); DbContext.SaveChanges(); @@ -1132,7 +1136,7 @@ namespace YLErp.Modules.RiskEngine app.UpdateOptName = UserName; app.UpdateDate = DateTime.Now; - WriteAuditLog("APPLICATION_DISABLE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "停用应用配置"); + WriteAuditLog("APP_DISABLE", "APPLICATION", applicationId, ResolveRuleNames(app.RuleIds), "停用应用配置"); DbContext.SaveChanges(); TryRefreshCache(); @@ -1159,7 +1163,7 @@ namespace YLErp.Modules.RiskEngine app.UpdateOptName = UserName; app.UpdateDate = DateTime.Now; - WriteAuditLog("APPLICATION_BATCH_ENABLE", "APPLICATION", app.id, ResolveRuleNames(app.RuleIds), "批量启用应用配置"); + WriteAuditLog("APP_BATCH_ENABLE", "APPLICATION", app.id, ResolveRuleNames(app.RuleIds), "批量启用应用配置"); } DbContext.SaveChanges(); @@ -1358,7 +1362,7 @@ namespace YLErp.Modules.RiskEngine variable.UpdateOptName = UserName; variable.UpdateDate = DateTime.Now; - WriteAuditLog("VARIABLE_UPDATE", "VARIABLE", variableId, req.VariableName, $"修改变量:{variable.VariableName}"); + WriteAuditLog("VAR_UPDATE", "VARIABLE", variableId, req.VariableName, $"修改变量:{variable.VariableName}"); DbContext.SaveChanges(); InvalidateVariableCache(); diff --git a/YLErpWeb/Controllers/RiskRuleController.cs b/YLErpWeb/Controllers/RiskRuleController.cs index d65997dc..9c97be3e 100644 --- a/YLErpWeb/Controllers/RiskRuleController.cs +++ b/YLErpWeb/Controllers/RiskRuleController.cs @@ -221,6 +221,27 @@ namespace YLErp.Web.Controllers } } + [HttpGet("risk-rules/list")] + [MyAuthorize("风控规则查看")] + public JsonResult GetAllRiskRules() + { + try + { + var service = new RiskRuleService(CurUser); + var result = service.GetAllRuleList(); + return Json(new { success = true, data = result }); + } + catch (ServiceException ex) + { + return Json(new { success = false, message = ex.Message }); + } + catch (Exception ex) + { + _logger.Error(ex, "查询规则下拉列表"); + return Json(new { success = false, message = "系统异常,请联系管理员" }); + } + } + #endregion #region Application Management