Merge branch 'glms/feature/1.4.2' of http://git.yiliantech.com/gitlab/otc-dev/zszq-trs into glms/feature/1.4.2

This commit is contained in:
锦麟 王
2026-07-02 17:25:26 +08:00
61 changed files with 151 additions and 5034 deletions
-14
View File
@@ -160,17 +160,3 @@ csharp_preserve_single_line_statements = true
# CA1819: Properties should not return arrays
dotnet_diagnostic.CA1819.severity = silent
# JavaScript 和 TypeScript 文件
[*.{js,jsx,ts,tsx}]
# 缩进和间距
indent_size = 4
indent_style = space
tab_width = 4
# 新行首选项
end_of_line = crlf
insert_final_newline = false
# 拖尾逗号不添加
trailing_comma = none
@@ -1,9 +0,0 @@
namespace YLErp.DBModels
{
public enum RiskControlStrategy
{
Block = 1,
Approval = 2,
ShowTip = 3
}
}
@@ -1,9 +0,0 @@
namespace YLErp.DBModels
{
public enum RiskRuleStatus
{
Active = 1,
Disabled = 2,
Deleted = 3
}
}
@@ -1,10 +0,0 @@
namespace YLErp.DBModels
{
public enum RiskVariableCategory
{
BookingElement = 1,
MarketData = 2,
SystemCalc = 3,
BooleanCheck = 4
}
}
@@ -1,9 +0,0 @@
namespace YLErp.DBModels
{
public enum RiskVariableDataType
{
Numeric = 1,
Date = 2,
Boolean = 3
}
}
@@ -1,18 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace YLErp.DBModels
{
[Table("glms_risk_rule")]
public class glms_risk_rule : DBModelWithOperator
{
public string RuleName { get; set; }
public string RuleText { get; set; }
public string ConditionJson { get; set; }
public string RuleExpr { get; set; }
public RiskRuleStatus Status { get; set; } = RiskRuleStatus.Active;
public int Version { get; set; } = 1;
public int? UpdateOptId { get; set; }
public string UpdateOptName { get; set; }
public DateTime? UpdateDate { get; set; }
}
}
@@ -1,22 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace YLErp.DBModels
{
[Table("glms_risk_rule_application")]
public class glms_risk_rule_application : DBModelWithOperator
{
public string RuleIds { get; set; }
public RiskRuleStatus Status { get; set; } = RiskRuleStatus.Active;
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 int ScopeIsGlobal { get; set; }
public int Version { get; set; } = 1;
public int? UpdateOptId { get; set; }
public string UpdateOptName { get; set; }
public DateTime? UpdateDate { get; set; }
}
}
@@ -1,19 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace YLErp.DBModels
{
[Table("glms_risk_rule_audit_log")]
public class glms_risk_rule_audit_log : DBModelBase
{
public string OperationType { get; set; }
public string TargetType { get; set; }
public long TargetId { get; set; }
public string TargetName { get; set; }
public string OperationDetail { get; set; }
public string Result { get; set; }
public string SnapshotData { get; set; }
public long OptId { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
}
}
@@ -1,21 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace YLErp.DBModels
{
[Table("glms_risk_variable")]
public class glms_risk_variable : DBModelWithOperator
{
public string VariableName { get; set; }
public RiskVariableCategory Category { get; set; }
public RiskVariableDataType DataType { get; set; }
public string Unit { get; set; }
public string ValueDomain { get; set; }
public string Description { get; set; }
public string VariableExpr { get; set; }
public int Version { get; set; } = 1;
public int SortOrder { get; set; }
public int? UpdateOptId { get; set; }
public string UpdateOptName { get; set; }
public DateTime? UpdateDate { get; set; }
}
}
@@ -1,82 +0,0 @@
SET FOREIGN_KEY_CHECKS=0;
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(结构化模式, 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 '版本号(乐观锁)',
`OptId` int NULL DEFAULT NULL COMMENT '创建人Id',
`OptName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人名称',
`OptDate` datetime NULL DEFAULT NULL COMMENT '创建时间',
`UpdateOptId` int NULL DEFAULT NULL COMMENT '最后修改人Id',
`UpdateOptName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后修改人名称',
`UpdateDate` datetime NULL DEFAULT NULL COMMENT '最后修改时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_status`(`Status` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '风控规则定义表' ROW_FORMAT = Dynamic;
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列表(逗号分隔)',
`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 '触发时点(逗号分隔)',
`ScopeAssetBookIds` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '适用账户Id列表',
`ScopeClientIds` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '适用对手方Id列表',
`ScopeUnderlyingTypes` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '适用标的类型',
`ScopeTradeTypes` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '适用合约类型',
`ScopeIsGlobal` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否全局适用: 0=否, 1=是',
`Version` int NOT NULL DEFAULT 1 COMMENT '版本号(乐观锁)',
`OptId` int NULL DEFAULT NULL COMMENT '创建人Id',
`OptName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人名称',
`OptDate` datetime NULL DEFAULT NULL COMMENT '创建时间',
`UpdateOptId` int NULL DEFAULT NULL COMMENT '最后修改人Id',
`UpdateOptName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后修改人名称',
`UpdateDate` datetime NULL DEFAULT NULL COMMENT '最后修改时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_status`(`Status` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '风控规则应用配置表' ROW_FORMAT = Dynamic;
CREATE TABLE `glms_risk_rule_audit_log` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`OperationType` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '操作类型(RULE_CREATE/RULE_UPDATE等)',
`TargetType` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '目标类型(RULE/APPLICATION/VARIABLE)',
`TargetId` bigint NOT NULL COMMENT '目标记录Id',
`TargetName` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '目标名称',
`OperationDetail` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '操作详情',
`Result` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作结果',
`SnapshotData` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '快照数据(JSON)',
`OptId` bigint NOT NULL COMMENT '操作人Id',
`OptName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作人名称',
`OptDate` datetime NOT NULL COMMENT '操作时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_opt_date`(`OptDate` ASC) USING BTREE,
INDEX `idx_operation_type`(`OperationType` ASC) USING BTREE,
INDEX `idx_target_type`(`TargetType` ASC) USING BTREE,
INDEX `idx_target_id`(`TargetId` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '风控规则操作审计日志表' ROW_FORMAT = Dynamic;
CREATE TABLE `glms_risk_variable` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`VariableName` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '变量名称',
`Category` tinyint NOT NULL COMMENT '分类: 1=BookingElement, 2=MarketData, 3=SystemCalc, 4=BooleanCheck',
`DataType` tinyint NOT NULL COMMENT '数据类型: 1=Numeric, 2=Date, 3=Boolean',
`Unit` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '单位',
`ValueDomain` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '值域描述',
`Description` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述',
`VariableExpr` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '变量表达式(C#表达式)',
`Version` int NOT NULL DEFAULT 1 COMMENT '版本号(乐观锁)',
`SortOrder` int NOT NULL DEFAULT 0 COMMENT '排序',
`OptId` int NULL DEFAULT NULL COMMENT '创建人Id',
`OptName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人名称',
`OptDate` datetime NULL DEFAULT NULL COMMENT '创建时间',
`UpdateOptId` int NULL DEFAULT NULL COMMENT '最后修改人Id',
`UpdateOptName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后修改人名称',
`UpdateDate` datetime NULL DEFAULT NULL COMMENT '最后修改时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '风控变量池定义表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS=1;
@@ -1,135 +0,0 @@
-- ============================================================
-- 规则应用初始数据(对应设计文档 §4.9.1 通用规则预置参考)
-- 依赖:seed_rules.sql(规则数据需先插入)
--
-- 每条规则对应一条应用配置(1:1),定义"何时、对谁、怎么处理"
-- ControlStrategy: 1=Block(禁止), 2=Approval(审批), 3=Warning(提示)
-- TriggerPoints: BOOK_CONFIRM=簿记交易确认
-- ScopeIsGlobal: 1=全局适用, 0=按维度配置(Scope字段为NULL表示"全部"
-- ============================================================
-- 规则1:挂钩标的集中度超阈值(审批,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '挂钩标的集中度超阈值';
-- 规则2:挂钩标的到期日小于合约到期日(禁止,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 1, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '挂钩标的到期日小于合约到期日';
-- 规则3:名义本金超阈值(审批,账户/合约类型)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '名义本金超阈值';
-- 规则4:保证金支付比例超阈值(审批,账户/标的类型)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '保证金支付比例超阈值';
-- 规则5:保证金利率偏离(审批,账户)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '保证金利率偏离';
-- 规则6:保证金收取比例低于最低标准(审批,账户/标的类型)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '保证金收取比例低于最低标准';
-- 规则7:起息日早于当前日期(审批,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '起息日早于当前日期';
-- 规则8:支付日为银行间交易日(审批,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '支付日为银行间交易日';
-- 规则9:到期日为银行间交易日(审批,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '到期日为银行间交易日';
-- 规则10:平仓日为银行间交易日(审批,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '平仓日为银行间交易日';
-- 规则11:合约期限超阈值(审批,账户)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '合约期限超阈值';
-- 规则12:债券类净价偏离(审批,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '债券类净价偏离';
-- 规则13:债券类收益率偏离(审批,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '债券类收益率偏离';
-- 规则14:非债券类价格偏离(审批,全局)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '非债券类价格偏离';
-- 规则15:单一交易对手累计标的数量超阈值(审批,对手方)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '单一交易对手累计标的数量超阈值';
-- 规则16:多头支付固定端利率偏离(审批,账户)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '多头支付固定端利率偏离';
-- 规则17:空头利率减点借贷加权偏离(审批,账户)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '空头利率减点借贷加权偏离';
-- 规则18:账户授权收支方向不匹配(禁止,账户)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 1, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '账户授权收支方向不匹配';
-- 规则19:执行价偏离超阈值(审批,账户)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 2, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '执行价偏离超阈值';
-- 规则20:希腊字母限额超阈值(提示,账户/标的,预留接口)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 3, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 0, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '希腊字母限额超阈值';
-- 规则21:接近/触发敲入敲出价(提示,全局,预留接口)
INSERT INTO `glms_risk_rule_application` (`RuleIds`, `Status`, `ControlStrategy`, `TriggerPoints`, `ScopeAssetBookIds`, `ScopeClientIds`, `ScopeUnderlyingTypes`, `ScopeTradeTypes`, `ScopeIsGlobal`, `Version`, `OptId`, `OptName`, `OptDate`)
SELECT
CAST(r.id AS CHAR), 1, 3, 'BOOK_CONFIRM', NULL, NULL, NULL, NULL, 1, 1, 0, 'SYSTEM', NOW()
FROM `glms_risk_rule` r WHERE r.RuleName = '接近/触发敲入敲出价';
@@ -1,50 +0,0 @@
-- ============================================================
-- 异常交易监控页面 - 操作权限(按钮)数据
-- 依赖:异常交易监控菜单 id=1077 (type=1) 需先插入
-- 对应 FunctionRight.xml 风险控制下风控相关操作权限(共16个按钮)
-- type: 0=目录 1=菜单 2=按钮
-- ============================================================
-- ----------------------------
-- 1. sys_menu 页面+按钮(type=12
-- ----------------------------
INSERT INTO `yltrs_admin`.`sys_menu` (`id`, `pid`, `pids`, `name`, `code`, `type`, `icon`, `router`, `component`, `permission`, `application`, `open_type`, `visible`, `link`, `redirect`, `weight`, `sort`, `remark`, `status`, `create_time`, `create_user`, `update_time`, `update_user`) VALUES
(1077, 99, NULL, '异常交易监控', NULL, 1, NULL, NULL, NULL, '异常交易监控', NULL, NULL, NULL, NULL, NULL, NULL, 0, '', 0, NULL, NULL, NULL, NULL),
(1078, 1077, NULL, '风控规则查看', NULL, 2, NULL, NULL, NULL, '风控规则查看', NULL, NULL, NULL, NULL, NULL, NULL, 1, '', 0, NULL, NULL, NULL, NULL),
(1079, 1077, NULL, '风控规则新增', NULL, 2, NULL, NULL, NULL, '风控规则新增', NULL, NULL, NULL, NULL, NULL, NULL, 2, '', 0, NULL, NULL, NULL, NULL),
(1080, 1077, NULL, '风控规则编辑', NULL, 2, NULL, NULL, NULL, '风控规则编辑', NULL, NULL, NULL, NULL, NULL, NULL, 3, '', 0, NULL, NULL, NULL, NULL),
(1081, 1077, NULL, '风控规则删除', NULL, 2, NULL, NULL, NULL, '风控规则删除', NULL, NULL, NULL, NULL, NULL, NULL, 4, '', 0, NULL, NULL, NULL, NULL),
(1082, 1077, NULL, '风控规则启停', NULL, 2, NULL, NULL, NULL, '风控规则启停', NULL, NULL, NULL, NULL, NULL, NULL, 5, '', 0, NULL, NULL, NULL, NULL),
(1083, 1077, NULL, '风控应用查看', NULL, 2, NULL, NULL, NULL, '风控应用查看', NULL, NULL, NULL, NULL, NULL, NULL, 6, '', 0, NULL, NULL, NULL, NULL),
(1084, 1077, NULL, '风控应用新增', NULL, 2, NULL, NULL, NULL, '风控应用新增', NULL, NULL, NULL, NULL, NULL, NULL, 7, '', 0, NULL, NULL, NULL, NULL),
(1085, 1077, NULL, '风控应用编辑', NULL, 2, NULL, NULL, NULL, '风控应用编辑', NULL, NULL, NULL, NULL, NULL, NULL, 8, '', 0, NULL, NULL, NULL, NULL),
(1086, 1077, NULL, '风控应用删除', NULL, 2, NULL, NULL, NULL, '风控应用删除', NULL, NULL, NULL, NULL, NULL, NULL, 9, '', 0, NULL, NULL, NULL, NULL),
(1087, 1077, NULL, '风控应用启停', NULL, 2, NULL, NULL, NULL, '风控应用启停', NULL, NULL, NULL, NULL, NULL, NULL, 10, '', 0, NULL, NULL, NULL, NULL),
(1088, 1077, NULL, '风控变量查看', NULL, 2, NULL, NULL, NULL, '风控变量查看', NULL, NULL, NULL, NULL, NULL, NULL, 11, '', 0, NULL, NULL, NULL, NULL),
(1089, 1077, NULL, '风控变量新增', NULL, 2, NULL, NULL, NULL, '风控变量新增', NULL, NULL, NULL, NULL, NULL, NULL, 12, '', 0, NULL, NULL, NULL, NULL),
(1090, 1077, NULL, '风控变量编辑', NULL, 2, NULL, NULL, NULL, '风控变量编辑', NULL, NULL, NULL, NULL, NULL, NULL, 13, '', 0, NULL, NULL, NULL, NULL),
(1091, 1077, NULL, '风控变量删除', NULL, 2, NULL, NULL, NULL, '风控变量删除', NULL, NULL, NULL, NULL, NULL, NULL, 14, '', 0, NULL, NULL, NULL, NULL),
(1092, 1077, NULL, '风控日志查看', NULL, 2, NULL, NULL, NULL, '风控日志查看', NULL, NULL, NULL, NULL, NULL, NULL, 15, '', 0, NULL, NULL, NULL, NULL),
(1093, 1077, NULL, '风控日志导出', NULL, 2, NULL, NULL, NULL, '风控日志导出', NULL, NULL, NULL, NULL, NULL, NULL, 16, '', 0, NULL, NULL, NULL, NULL);
-- ----------------------------
-- 2. sys_role_menu 角色1的按钮权限
-- ----------------------------
INSERT INTO `yltrs_admin`.`sys_role_menu` (`id`, `role_id`, `menu_id`) VALUES
(1097, 1, 1077),
(1098, 1, 1078),
(1099, 1, 1079),
(1100, 1, 1080),
(1101, 1, 1081),
(1102, 1, 1082),
(1103, 1, 1083),
(1104, 1, 1084),
(1105, 1, 1085),
(1106, 1, 1086),
(1107, 1, 1087),
(1108, 1, 1088),
(1109, 1, 1089),
(1110, 1, 1090),
(1111, 1, 1091),
(1112, 1, 1092),
(1113, 1, 1093);
@@ -1,292 +0,0 @@
-- ============================================================
-- 规则初始数据(对应设计文档 §4.9.1 通用规则预置参考)
-- 依赖:seed_variables.sql(变量池数据需先插入,ID 1~44)
--
-- 变量 ID 映射(seed_variables.sql 自增):
-- 1=合约名义本金 2=合约起息日 3=合约到期日 4=合约平仓日
-- 5=合约支付日 6=期初净价 7=期初全价 8=期初收益率
-- 9=期初价格 10=期末全价 11=期末价格 12=保证金利率
-- 13=保证金比例 14=客户授信额度
-- 15=上一收盘日中债估值净价 16=上一收盘日中债估值全价 17=上一收盘日中债估值收益率
-- 18=上一日收盘价 19=借贷加权费率 20=FR007 21=当前日期
-- 22=挂钩标的到期日 23=标的发行余额
-- 24=挂钩标的集中度 25=授信占用率 26=合约期限 27=Delta
-- 28=Gamma 29=Vega 30=Theta 31=利息端利率
-- 32=对手方累计标的数量 33=同一标的累计名义本金 34=同一客户累计名义本金
-- 35=总持仓名义本金
-- 36=到期日是否银行间交易日 37=平仓日是否银行间交易日 38=支付日是否银行间交易日
-- 39=利息端/浮动端方向是否同向 40=关键业务要素是否一致
-- 41=多空方向为多头 42=多空方向为空头
-- 43=保证金收支方向为支付 44=保证金收支方向为收取
-- 45=期初净价偏离度 46=期初收益率偏离度 47=期初价格偏离度
-- 48=利息端利率与FR007偏离度 49=利息端利率与借贷加权费率偏离度
-- 50=执行价偏离度 51=执行价 52=客户品种最低保证金率 53=参考价格
--
-- ConditionJson 精简设计:只存 VariableId/Operator/ThresholdType/Value/ThresholdVariableId
-- 展示字段(VariableName/VariableType/Unit/ThresholdVariableName)从变量接口关联获取
-- ============================================================
-- ============================================================
-- 插入 21 条通用规则
-- ============================================================
-- 规则1:挂钩标的集中度超阈值(审批,全局)
-- 条件:挂钩标的集中度(ID=24) > 30%
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('挂钩标的集中度超阈值',
'挂钩标的集中度超过阈值(默认30%)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 24,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 30
)),
'calc.UnderlyingConcentration > 30',
1, 1, 0, 'SYSTEM', NOW());
-- 规则2:挂钩标的到期日小于合约到期日(禁止,全局)
-- 条件:挂钩标的到期日(ID=22) < 合约到期日(ID=3)
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('挂钩标的到期日小于合约到期日',
'挂钩标的到期日早于合约到期日时禁止交易',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 22,
'Operator', '早于', 'ThresholdType', 'Variable',
'ThresholdVariableId', 3
)),
'market.UnderlyingMaturityDate < trade.ExerciseDate',
1, 1, 0, 'SYSTEM', NOW());
-- 规则3:名义本金超阈值(审批,账户/合约类型)
-- 条件:合约名义本金(ID=1) > 1000000001亿元)
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('名义本金超阈值',
'合约名义本金超过阈值(默认1亿元)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 1,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 100000000
)),
'trade.StockEqvNotional > 100000000',
1, 1, 0, 'SYSTEM', NOW());
-- 规则4:保证金支付比例超阈值(审批,账户/标的类型)
-- 条件:保证金比例(ID=13) > 50%
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('保证金支付比例超阈值',
'保证金比例超过阈值(默认50%)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 13,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 50
)),
'trade.MarginRate > 50',
1, 1, 0, 'SYSTEM', NOW());
-- 规则5:保证金利率偏离(审批,账户)
-- 条件:保证金利率(ID=12) 不介于 [2%, 5%]
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('保证金利率偏离',
'保证金利率不在配置区间内(默认2%~5%)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 12,
'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());
-- 规则6:保证金收取比例低于最低标准(审批,账户/标的类型)
-- 条件:保证金比例(ID=13) < 客户品种最低保证金率(ID=52)
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('保证金收取比例低于最低标准',
'保证金比例低于客户品种最低保证金率时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 13,
'Operator', '<', 'ThresholdType', 'Variable',
'ThresholdVariableId', 52
)),
'trade.MarginRate < config.MinMarginRate',
1, 1, 0, 'SYSTEM', NOW());
-- 规则7:起息日早于当前日期(审批,全局)
-- 条件:合约起息日(ID=2) < 当前日期(ID=21)
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('起息日早于当前日期',
'合约起息日早于当前日期时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 2,
'Operator', '早于', 'ThresholdType', 'Variable',
'ThresholdVariableId', 21
)),
'trade.StartDate < sys.CurrentDate',
1, 1, 0, 'SYSTEM', NOW());
-- 规则8:支付日为银行间交易日(审批,全局)
-- 条件:支付日是否银行间交易日(ID=38) = 是
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('支付日为银行间交易日',
'支付日为银行间交易日时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 38,
'Operator', '', 'ThresholdType', 'Fixed', 'Value', true
)),
'calc.IsSettlementDateTradingDay == true',
1, 1, 0, 'SYSTEM', NOW());
-- 规则9:到期日为银行间交易日(审批,全局)
-- 条件:到期日是否银行间交易日(ID=36) = 是
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('到期日为银行间交易日',
'到期日为银行间交易日时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 36,
'Operator', '', 'ThresholdType', 'Fixed', 'Value', true
)),
'calc.IsExerciseDateTradingDay == true',
1, 1, 0, 'SYSTEM', NOW());
-- 规则10:平仓日为银行间交易日(审批,全局)
-- 条件:平仓日是否银行间交易日(ID=37) = 是
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('平仓日为银行间交易日',
'平仓日为银行间交易日时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 37,
'Operator', '', 'ThresholdType', 'Fixed', 'Value', true
)),
'calc.IsUnwindDateTradingDay == true',
1, 1, 0, 'SYSTEM', NOW());
-- 规则11:合约期限超阈值(审批,账户)
-- 条件:合约期限(ID=26) > 365天
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('合约期限超阈值',
'合约期限超过阈值(默认365天)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 26,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 365
)),
'calc.MaturityDays > 365',
1, 1, 0, 'SYSTEM', NOW());
-- 规则12:债券类净价偏离(审批,全局)
-- 条件:期初净价偏离度(ID=45) > 5%
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('债券类净价偏离',
'期初净价与上一收盘日中债估值净价偏离度超阈值(默认5%)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 45,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 5
)),
'Math.Abs(swap_position.PosiNetNoFeePrice - market.CBValuationNetPrice) / market.CBValuationNetPrice * 100 > 5',
1, 1, 0, 'SYSTEM', NOW());
-- 规则13:债券类收益率偏离(审批,全局)
-- 条件:期初收益率偏离度(ID=46) > 5%
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('债券类收益率偏离',
'期初收益率与上一收盘日中债估值收益率偏离度超阈值(默认5%)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 46,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 5
)),
'Math.Abs(trade.InitYtm - market.CBValuationYtm) / market.CBValuationYtm * 100 > 5',
1, 1, 0, 'SYSTEM', NOW());
-- 规则14:非债券类价格偏离(审批,全局)
-- 条件:期初价格偏离度(ID=47) > 5%
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('非债券类价格偏离',
'期初价格与上一日收盘价偏离度超阈值(默认5%)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 47,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 5
)),
'Math.Abs(trade.SpotPrice - market.LastClosePrice) / market.LastClosePrice * 100 > 5',
1, 1, 0, 'SYSTEM', NOW());
-- 规则15:单一交易对手累计标的数量超阈值(审批,对手方)
-- 条件:对手方累计标的数量(ID=32) > 10个
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('单一交易对手累计标的数量超阈值',
'对手方累计标的数量超过阈值(默认10个)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 32,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 10
)),
'calc.CounterpartyUnderlyingCount > 10',
1, 1, 0, 'SYSTEM', NOW());
-- 规则16:多头支付固定端利率偏离(审批,账户)
-- 条件:多空方向为多头(ID=41) AND 利息端利率与FR007偏离度(ID=48) > 3%
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('多头支付固定端利率偏离',
'多头方向支付固定端场景下,利息端利率与FR007偏离度超阈值(默认3%)时触发审批',
JSON_ARRAY(
JSON_OBJECT(
'VariableId', 41,
'Operator', '', 'ThresholdType', 'Fixed', 'Value', true
),
JSON_OBJECT(
'VariableId', 48,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 3
)
),
'calc.IsLongDirection == true && Math.Abs(calc.InterestRate - market.FR007) / market.FR007 * 100 > 3',
1, 1, 0, 'SYSTEM', NOW());
-- 规则17:空头利率减点借贷加权偏离(审批,账户)
-- 条件:多空方向为空头(ID=42) AND 利息端利率与借贷加权费率偏离度(ID=49) > 2%
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('空头利率减点借贷加权偏离',
'空头方向场景下,利息端利率与借贷加权费率偏离度超阈值(默认2%)时触发审批',
JSON_ARRAY(
JSON_OBJECT(
'VariableId', 42,
'Operator', '', 'ThresholdType', 'Fixed', 'Value', true
),
JSON_OBJECT(
'VariableId', 49,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 2
)
),
'calc.IsShortDirection == true && Math.Abs(calc.InterestRate - market.BondLendingRate) / market.BondLendingRate * 100 > 2',
1, 1, 0, 'SYSTEM', NOW());
-- 规则18:账户授权收支方向不匹配(禁止,账户)
-- 条件:保证金收支方向为支付(ID=43)(实际需结合账户授权方向判断,此处简化)
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('账户授权收支方向不匹配',
'保证金收支方向与账户授权方向不匹配时禁止交易',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 43,
'Operator', '', 'ThresholdType', 'Fixed', 'Value', true
)),
'calc.IsMarginPay == true',
1, 1, 0, 'SYSTEM', NOW());
-- 规则19:执行价偏离超阈值(审批,账户)
-- 条件:执行价偏离度(ID=50) > 5%
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('执行价偏离超阈值',
'执行价与参考价格偏离度超阈值(默认5%)时触发审批',
JSON_ARRAY(JSON_OBJECT(
'VariableId', 50,
'Operator', '>', 'ThresholdType', 'Fixed', 'Value', 5
)),
'Math.Abs(trade.StrikePrice - market.ReferencePrice) / market.ReferencePrice * 100 > 5',
1, 1, 0, 'SYSTEM', NOW());
-- 规则20:希腊字母限额超阈值(提示,预留接口,一期不纳入)
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('希腊字母限额超阈值',
'Delta/Gamma/Vega/Theta任一超阈值时提示(预留接口,一期不纳入)',
NULL,
NULL,
1, 1, 0, 'SYSTEM', NOW());
-- 规则21:接近/触发敲入敲出价(提示,预留接口,需确认具体判断逻辑)
INSERT INTO `glms_risk_rule` (`RuleName`, `RuleText`, `ConditionJson`, `RuleExpr`, `Status`, `Version`, `OptId`, `OptName`, `OptDate`) VALUES
('接近/触发敲入敲出价',
'标的价格接近敲入/敲出价时提示(预留接口,需确认具体判断逻辑)',
NULL,
NULL,
1, 1, 0, 'SYSTEM', NOW());
@@ -1,73 +0,0 @@
-- ============================================================
-- 变量池初始数据(对应设计文档 §4.4 变量池完整清单)
-- ============================================================
-- 4.4.1 簿记要素类(Category=1
INSERT INTO `glms_risk_variable` (`VariableName`, `Category`, `DataType`, `Unit`, `ValueDomain`, `Description`, `VariableExpr`, `SortOrder`, `OptId`, `OptName`, `OptDate`) VALUES
('合约名义本金', 1, 1, '', '≥ 0', 'trade.StockEqvNotional 或 swap_position.PosiNotionalValue', 'trade.StockEqvNotional', 101, 0, 'SYSTEM', NOW()),
('合约起息日', 1, 2, NULL, NULL, 'trade.StartDate 或 swap_position.PosiStartDate', 'trade.StartDate', 102, 0, 'SYSTEM', NOW()),
('合约到期日', 1, 2, NULL, NULL, 'trade.ExerciseDate 或 swap_position.PosiMatuirityDate', 'trade.ExerciseDate', 103, 0, 'SYSTEM', NOW()),
('合约平仓日', 1, 2, NULL, NULL, 'trade.UnWindDate', 'trade.UnWindDate', 104, 0, 'SYSTEM', NOW()),
('合约支付日', 1, 2, NULL, NULL, 'trade.SettlementDate', 'trade.SettlementDate', 105, 0, 'SYSTEM', NOW()),
('期初净价', 1, 1, '', '≥ 0', 'swap_position.PosiNetNoFeePrice(债券 TRS', 'swap_position.PosiNetNoFeePrice', 106, 0, 'SYSTEM', NOW()),
('期初全价', 1, 1, '', '≥ 0', 'swap_position.PosiNetFeePrice(债券 TRS/ trade.SpotPrice(其他)', 'swap_position.PosiNetFeePrice', 107, 0, 'SYSTEM', NOW()),
('期初收益率', 1, 1, '%', NULL, 'trade.InitYtm 或 swap_position.InitYtm', 'trade.InitYtm', 108, 0, 'SYSTEM', NOW()),
('期初价格', 1, 1, '', '≥ 0', 'trade.SpotPrice(非债券类)', 'trade.SpotPrice', 109, 0, 'SYSTEM', NOW()),
('期末全价', 1, 1, '', '≥ 0', 'eod_swap_position.UnderlyingPrice(债券 TRS', 'eod_swap_position.UnderlyingPrice', 110, 0, 'SYSTEM', NOW()),
('期末价格', 1, 1, '', '≥ 0', 'trade.FinalPrice', 'trade.FinalPrice', 111, 0, 'SYSTEM', NOW()),
('保证金利率', 1, 1, '%', NULL, 'client_marginrate.InitMarginRebateRate', 'client_marginrate.InitMarginRebateRate', 112, 0, 'SYSTEM', NOW()),
('保证金比例', 1, 1, '%', '0~100', 'trade.MarginRate', 'trade.MarginRate', 113, 0, 'SYSTEM', NOW()),
('客户授信额度', 1, 1, '', '≥ 0', 'credit.Credit', 'credit.Credit', 114, 0, 'SYSTEM', NOW());
-- 4.4.2 行情类(Category=2,统一取上一交易日收盘价)
INSERT INTO `glms_risk_variable` (`VariableName`, `Category`, `DataType`, `Unit`, `ValueDomain`, `Description`, `VariableExpr`, `SortOrder`, `OptId`, `OptName`, `OptDate`) VALUES
('上一收盘日中债估值净价', 2, 1, '', '≥ 0', '资讯数据', 'market.CBValuationNetPrice', 201, 0, 'SYSTEM', NOW()),
('上一收盘日中债估值全价', 2, 1, '', '≥ 0', '资讯数据', 'market.CBValuationFullPrice', 202, 0, 'SYSTEM', NOW()),
('上一收盘日中债估值收益率', 2, 1, '%', NULL, '资讯数据', 'market.CBValuationYtm', 203, 0, 'SYSTEM', NOW()),
('上一日收盘价', 2, 1, '', '≥ 0', '行情数据,按标的区分', 'market.LastClosePrice', 204, 0, 'SYSTEM', NOW()),
('借贷加权费率', 2, 1, '%', NULL, 'CMDM 标的债券借贷费率行情表', 'market.BondLendingRate', 205, 0, 'SYSTEM', NOW()),
('FR007', 2, 1, '%', NULL, '上一交易日收盘价', 'market.FR007', 206, 0, 'SYSTEM', NOW()),
('当前日期', 2, 2, NULL, NULL, 'DateTime.Today', 'sys.CurrentDate', 207, 0, 'SYSTEM', NOW()),
('挂钩标的到期日', 2, 2, NULL, NULL, '资讯数据', 'market.UnderlyingMaturityDate', 208, 0, 'SYSTEM', NOW()),
('标的发行余额', 2, 1, '', '≥ 0', '资讯数据', 'market.UnderlyingIssueBalance', 209, 0, 'SYSTEM', NOW());
-- 4.4.3 系统计算值类(Category=3
INSERT INTO `glms_risk_variable` (`VariableName`, `Category`, `DataType`, `Unit`, `ValueDomain`, `Description`, `VariableExpr`, `SortOrder`, `OptId`, `OptName`, `OptDate`) VALUES
('挂钩标的集中度', 3, 1, '%', '0~100', '同一标的存续交易总名义本金 ÷ 标的发行余额 × 100', 'calc.UnderlyingConcentration', 301, 0, 'SYSTEM', NOW()),
('授信占用率', 3, 1, '%', '0~100', '(已占用授信 + 本笔授信占用) ÷ 授信总额 × 100', 'calc.CreditUsageRate', 302, 0, 'SYSTEM', NOW()),
('合约期限', 3, 1, '', '≥ 0', '(ExerciseDate - StartDate).Days', 'calc.MaturityDays', 303, 0, 'SYSTEM', NOW()),
('Delta', 3, 1, NULL, NULL, 'realtime_trade_risk.Delta(预留接口,一期不纳入)', 'realtime_trade_risk.Delta', 304, 0, 'SYSTEM', NOW()),
('Gamma', 3, 1, NULL, NULL, 'realtime_trade_risk.Gamma(预留接口,一期不纳入)', 'realtime_trade_risk.Gamma', 305, 0, 'SYSTEM', NOW()),
('Vega', 3, 1, NULL, NULL, 'realtime_trade_risk.Vega(预留接口,一期不纳入)', 'realtime_trade_risk.Vega', 306, 0, 'SYSTEM', NOW()),
('Theta', 3, 1, NULL, NULL, 'realtime_trade_risk.Theta(预留接口,一期不纳入)', 'realtime_trade_risk.Theta', 307, 0, 'SYSTEM', NOW()),
('利息端利率', 3, 1, '%', NULL, '固定利率 或 FR007 ± 加点', 'calc.InterestRate', 308, 0, 'SYSTEM', NOW()),
('对手方累计标的数量', 3, 1, '', '≥ 0', 'COUNT(DISTINCT UnderlyingId) 该对手方所有存续交易,含本笔', 'calc.CounterpartyUnderlyingCount', 309, 0, 'SYSTEM', NOW()),
('同一标的累计名义本金', 3, 1, '', '≥ 0', 'SUM(该标的所有存续交易的 StockEqvNotional),含本笔', 'calc.SameUnderlyingTotalNotional', 310, 0, 'SYSTEM', NOW()),
('同一客户累计名义本金', 3, 1, '', '≥ 0', 'SUM(该客户所有存续交易的 StockEqvNotional),含本笔', 'calc.SameClientTotalNotional', 311, 0, 'SYSTEM', NOW()),
('总持仓名义本金', 3, 1, '', '≥ 0', 'SUM(所有存续交易的 StockEqvNotional)', 'calc.TotalPositionNotional', 312, 0, 'SYSTEM', NOW());
-- 新增变量(ID 45~53):
-- 45=期初净价偏离度 46=期初收益率偏离度 47=期初价格偏离度
-- 48=利息端利率与FR007偏离度 49=利息端利率与借贷加权费率偏离度
-- 50=执行价偏离度 51=执行价 52=客户品种最低保证金率 53=参考价格
INSERT INTO `glms_risk_variable` (`VariableName`, `Category`, `DataType`, `Unit`, `ValueDomain`, `Description`, `VariableExpr`, `SortOrder`, `OptId`, `OptName`, `OptDate`) VALUES
('期初净价偏离度', 3, 1, '%', '≥ 0', 'ABS(期初净价-中债估值净价)/中债估值净价*100', 'Math.Abs(swap_position.PosiNetNoFeePrice - market.CBValuationNetPrice) / market.CBValuationNetPrice * 100', 313, 0, 'SYSTEM', NOW()),
('期初收益率偏离度', 3, 1, '%', '≥ 0', 'ABS(期初收益率-中债估值收益率)/中债估值收益率*100', 'Math.Abs(trade.InitYtm - market.CBValuationYtm) / market.CBValuationYtm * 100', 314, 0, 'SYSTEM', NOW()),
('期初价格偏离度', 3, 1, '%', '≥ 0', 'ABS(期初价格-上一日收盘价)/上一日收盘价*100', 'Math.Abs(trade.SpotPrice - market.LastClosePrice) / market.LastClosePrice * 100', 315, 0, 'SYSTEM', NOW()),
('利息端利率与FR007偏离度', 3, 1, '%', '≥ 0', 'ABS(利息端利率-FR007)/FR007*100', 'Math.Abs(calc.InterestRate - market.FR007) / market.FR007 * 100', 316, 0, 'SYSTEM', NOW()),
('利息端利率与借贷加权费率偏离度', 3, 1, '%', '≥ 0', 'ABS(利息端利率-借贷加权费率)/借贷加权费率*100', 'Math.Abs(calc.InterestRate - market.BondLendingRate) / market.BondLendingRate * 100', 317, 0, 'SYSTEM', NOW()),
('执行价偏离度', 3, 1, '%', '≥ 0', 'ABS(执行价-参考价格)/参考价格*100', 'Math.Abs(trade.StrikePrice - market.ReferencePrice) / market.ReferencePrice * 100', 318, 0, 'SYSTEM', NOW()),
('执行价', 1, 1, '', '≥ 0', 'trade.StrikePrice', 'trade.StrikePrice', 115, 0, 'SYSTEM', NOW()),
('客户品种最低保证金率', 1, 1, '%', '0~100', '客户品种最低保证金率(由账户/标的配置决定)', 'config.MinMarginRate', 116, 0, 'SYSTEM', NOW()),
('参考价格', 2, 1, '', '≥ 0', '执行价参考价格(行情数据)', 'market.ReferencePrice', 210, 0, 'SYSTEM', NOW());
-- 4.4.4 布尔判断类(Category=4
INSERT INTO `glms_risk_variable` (`VariableName`, `Category`, `DataType`, `Unit`, `ValueDomain`, `Description`, `VariableExpr`, `SortOrder`, `OptId`, `OptName`, `OptDate`) VALUES
('到期日是否银行间交易日', 4, 3, NULL, NULL, '查询银行间交易日历', 'calc.IsExerciseDateTradingDay', 401, 0, 'SYSTEM', NOW()),
('平仓日是否银行间交易日', 4, 3, NULL, NULL, '查询银行间交易日历', 'calc.IsUnwindDateTradingDay', 402, 0, 'SYSTEM', NOW()),
('支付日是否银行间交易日', 4, 3, NULL, NULL, '查询银行间交易日历', 'calc.IsSettlementDateTradingDay', 403, 0, 'SYSTEM', NOW()),
('利息端/浮动端方向是否同向', 4, 3, NULL, NULL, '利息端"收取"↔浮动端"多头",利息端"支付"↔浮动端"空头"', 'calc.IsInterestFloatSameDirection', 404, 0, 'SYSTEM', NOW()),
('关键业务要素是否一致', 4, 3, NULL, NULL, '交易确认书 vs 簿记要素(大模型方案)', 'calc.IsKeyElementsConsistent', 405, 0, 'SYSTEM', NOW()),
('多空方向为多头', 4, 3, NULL, NULL, 'trade.BuySell == "买入" 或浮动端为多头', 'calc.IsLongDirection', 406, 0, 'SYSTEM', NOW()),
('多空方向为空头', 4, 3, NULL, NULL, '与多头互斥', 'calc.IsShortDirection', 407, 0, 'SYSTEM', NOW()),
('保证金收支方向为支付', 4, 3, NULL, NULL, '保证金方向为支付', 'calc.IsMarginPay', 408, 0, 'SYSTEM', NOW()),
('保证金收支方向为收取', 4, 3, NULL, NULL, '与支付互斥', 'calc.IsMarginReceive', 409, 0, 'SYSTEM', NOW());
+1 -6
View File
@@ -1,4 +1,4 @@
using BaseOUDAL;
using BaseOUDAL;
using YLErp.Core.DBModels;
using YLErp.Model;
@@ -408,10 +408,5 @@ namespace YLErp.BLL
public DbSet<trade_contract_oa_result> tradeContractOaResult { get; set; }
public DbSet<BondPayment> bondPayment { get; set; }
public DbSet<glms_risk_rule> glms_risk_rule { get; set; }
public DbSet<glms_risk_rule_application> glms_risk_rule_application { get; set; }
public DbSet<glms_risk_rule_audit_log> glms_risk_rule_audit_log { get; set; }
public DbSet<glms_risk_variable> glms_risk_variable { get; set; }
}
}
+7 -1
View File
@@ -199,8 +199,14 @@ namespace YLErp.DBModels
}
public bool SwapCanEdit()
{
var hasSwapDealEvent = swap_Events.Any(x =>
!x.Invalid
&& (x.EventType == (int)SwapEventTypeEnum.
|| x.EventType == (int)SwapEventTypeEnum.
|| x.EventType == (int)SwapEventTypeEnum.));
//确认成交
return !(ConsTrade.TradeStatusAfterAndWithOutConfirmed.Contains(TradeStatus) || ConsTrade..Equals(TradeStatus) || UnWindDate.HasValue || (!PS.Config.TradeElement.IsAmendableAfterConfirm && TradeDate < valuedateBLL.ValueDate && ConsTrade..Equals(TradeStatus)));
return !(ConsTrade.TradeStatusAfterAndWithOutConfirmed.Contains(TradeStatus) || ConsTrade..Equals(TradeStatus) || UnWindDate.HasValue || hasSwapDealEvent || (!PS.Config.TradeElement.IsAmendableAfterConfirm && TradeDate < valuedateBLL.ValueDate && ConsTrade..Equals(TradeStatus)));
}
public bool CanDelete()
{
@@ -1,50 +0,0 @@
using System;
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// 规则编译结果。
/// 用于承载公式校验、Roslyn 编译结果和错误信息。
/// </summary>
public class RuleCompileResult
{
/// <summary>
/// 是否编译成功。
/// </summary>
public bool Success { get; set; }
/// <summary>
/// 编译失败时的错误信息。
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// 编译成功后的可执行委托。
/// </summary>
public Func<RiskContext, bool> CompiledScript { get; set; }
/// <summary>
/// 创建成功结果。
/// </summary>
public static RuleCompileResult Ok(Func<RiskContext, bool> compiledScript)
{
return new RuleCompileResult
{
Success = true,
CompiledScript = compiledScript
};
}
/// <summary>
/// 创建失败结果。
/// </summary>
public static RuleCompileResult Fail(string errorMessage)
{
return new RuleCompileResult
{
Success = false,
ErrorMessage = errorMessage
};
}
}
}
@@ -1,67 +0,0 @@
using System;
using System.Collections.Concurrent;
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// 规则编译结果的进程内缓存。
/// 当前第一版按规则 Id 缓存 Roslyn 编译后的可执行委托。
/// </summary>
public static class RuleCompiledCache
{
/// <summary>
/// 规则编译结果的进程内缓存。ConcurrentDictionary为线程安全的字典,用于缓存编译后的规则委托,性能略低。
/// </summary>
private static readonly ConcurrentDictionary<string, Func<RiskContext, bool>> Cache
= new ConcurrentDictionary<string, Func<RiskContext, bool>>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// 写入或更新规则编译结果。
/// </summary>
public static void Set(string ruleCode, Func<RiskContext, bool> compiledScript)
{
if (string.IsNullOrWhiteSpace(ruleCode) || compiledScript == null)
{
return;
}
Cache[ruleCode] = compiledScript;
}
/// <summary>
/// 尝试获取规则编译结果。
/// </summary>
public static bool TryGet(string ruleCode, out Func<RiskContext, bool> compiledScript)
{
compiledScript = null;
if (string.IsNullOrWhiteSpace(ruleCode))
{
return false;
}
return Cache.TryGetValue(ruleCode, out compiledScript);
}
/// <summary>
/// 删除指定规则的编译结果。
/// </summary>
public static void Remove(string ruleCode)
{
if (string.IsNullOrWhiteSpace(ruleCode))
{
return;
}
Cache.TryRemove(ruleCode, out _);
}
/// <summary>
/// 清空全部规则编译结果。
/// </summary>
public static void Clear()
{
Cache.Clear();
}
}
}
@@ -1,324 +0,0 @@
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// Roslyn 脚本全局变量容器
/// CSharpScript 执行时通过此对象传递 DataMap
/// </summary>
public class ScriptGlobals
{
/// <summary>
/// 数据字典:key=表名/前缀,value=对应数据对象
/// </summary>
public Dictionary<string, object> DataMap { get; set; }
}
/// <summary>
/// 规则编译器(Roslyn 版)
///
/// 编译时机:规则定义时(零运行时解析开销)
/// 执行方式:判风控时直接调用 rule.CompiledScript(context) → bool
///
/// 相比表达式树方案的优势:
/// - 代码简洁:生成 C# 字符串即可,无需手动拼接 Expression 节点
/// - 灵活性高:天然支持多条件 AND/OR、复杂运算、未来可扩展任意 C# 语法
/// - 可调试:生成的脚本代码可直接阅读和理解
///
/// 示例:
/// RuleExpr: Convert.ToDecimal(((YLErp.DBModels.trade)DataMap["trade"]).StockEqvNotional) > 100000000m
/// 编译结果:Func&lt;RiskContext, bool&gt;(内部通过 Roslyn 编译缓存)
/// </summary>
public static class RuleCompiler
{
/// <summary>
/// 变量编码 → 类型全名的映射
/// 用于 Roslyn 脚本中的类型强转,如 ((YLErp.DBModels.trade)DataMap["trade"])
/// </summary>
static IYcLogger _logger =LogFactory.GetLogger("RuleCompiler");
private static readonly Dictionary<string, string> VariableTypeMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["trade"] = "YLErp.DBModels.trade",
["swap_position"] = "YLErp.DBModels.swap_position",
["client"] = "YLErp.DBModels.client",
["credit"] = "YLErp.DBModels.credit",
["client_marginrate"] = "YLErp.DBModels.client_marginrate",
["market"] = "YLErp.DBModels.market",
["calc"] = "YLErp.DBModels.calc",
["sys"] = "YLErp.DBModels.sys",
["eod_swap_position"] = "YLErp.DBModels.eod_swap_position",
["realtime_trade_risk"] = "YLErp.DBModels.realtime_trade_risk",
};
/// <summary>
/// 将规则编译为可执行委托,并写入 rule.CompiledScript
/// 调用时机:规则创建时 / 规则加载时 / 缓存刷新时
/// </summary>
public static void Compile(this RiskRule rule)
{
var result = ValidateAndCompileRule(rule);
if (!result.Success)
{
throw new InvalidOperationException(result.ErrorMessage);
}
}
/// <summary>
/// 校验并编译规则表达式。
/// 当前 RuleExpr 要求是 Roslyn 可直接执行的 bool 表达式。
/// </summary>
public static RuleCompileResult ValidateAndCompileFormula(string formulaExp)
{
if (string.IsNullOrWhiteSpace(formulaExp))
{
return RuleCompileResult.Fail("规则 RuleExpr 不能为空");
}
try
{
var compiled = CompileScript(formulaExp);
return RuleCompileResult.Ok(compiled);
}
catch (Exception ex)
{
return RuleCompileResult.Fail(ex.Message);
}
}
/// <summary>
/// 校验并编译规则,编译成功后同时写入规则对象和内存缓存。
/// </summary>
public static RuleCompileResult ValidateAndCompileRule(RiskRule rule)
{
if (rule == null)
{
return RuleCompileResult.Fail("规则不能为空");
}
if (rule.Id <= 0)
{
return RuleCompileResult.Fail("规则 Id 不能为空");
}
var result = ValidateAndCompileFormula(rule.RuleExpr);
if (!result.Success)
{
return RuleCompileResult.Fail($"规则[{rule.Id}]编译失败:{result.ErrorMessage}");
}
rule.CompiledScript = result.CompiledScript;
RuleCompiledCache.Set(rule.Id.ToString(), result.CompiledScript);
return result;
}
/// <summary>
/// 解析 ConditionJson,提取条件列表。
/// 要求 expression 明确描述实际取值路径。
/// 当前主执行链不再使用该方法,仅作为早期方案保留。
/// </summary>
// private static List<(string variableCode, string expression, string op, object value, string variableType)> ParseConditionJson(string conditionJson)
// {
// var result = new List<(string, string, string, object, string)>();
// var jObj = JObject.Parse(formulaJson);
// var conditions = jObj["conditions"] as JArray;
// if (conditions == null || !conditions.Any())
// {
// throw new ArgumentException("ConditionJson 中缺少 conditions");
// }
// foreach (var cond in conditions)
// {
// string variableCode = cond["variableCode"]?.Value<string>();
// string expression = cond["expression"]?.Value<string>();
// string op = cond["operator"]?.Value<string>();
// object value = cond["value"]?.Value<object>();
// string variableType = cond["variableType"]?.Value<string>() ?? "numeric";
// if (string.IsNullOrWhiteSpace(expression) || string.IsNullOrWhiteSpace(op))
// {
// throw new ArgumentException("条件中缺少 expression 或 operator");
// }
// result.Add((variableCode, expression, op, value, variableType));
// }
// return result;
// }
/// <summary>
/// 根据条件列表生成 Roslyn C# 脚本代码。
/// 多条件自动用 &&AND)连接。
/// 左值表达式统一基于 expression 构建。
/// </summary>
private static string BuildScriptCode(List<(string variableCode, string expression, string op, object value, string variableType)> conditions)
{
var exprParts = new List<string>();
foreach (var (_, expression, op, value, variableType) in conditions)
{
// 先根据 expression 构建左值访问表达式,再拼接比较符和右值字面量
string leftExpr = BuildMemberAccessExpression(expression, variableType);
// 格式化阈值
string valueLiteral = FormatValueLiteral(value, variableType);
// 格式化操作符
string csharpOp = FormatOperator(op);
// 生成单条件表达式
string expr = $"{leftExpr} {csharpOp} {valueLiteral}";
exprParts.Add(expr);
}
// 多条件用 && 连接
return string.Join(" && ", exprParts);
}
/// <summary>
/// 将前端传入的 expression 转成 Roslyn 可执行的成员访问表达式。
/// 例如 trade.StockEqvNotional 会转成 ((YLErp.DBModels.trade)DataMap["trade"]).StockEqvNotional。
/// 数值类型会自动包一层 Convert.ToDecimal,便于与 decimal 阈值比较。
/// </summary>
private static string BuildMemberAccessExpression(string expression, string variableType)
{
var parts = expression.Split('.');
if (parts.Length < 2)
{
throw new ArgumentException($"expression 格式不正确:{expression}");
}
string prefix = parts[0].Trim().ToLower();
if (!VariableTypeMap.TryGetValue(prefix, out string typeFullName))
{
throw new ArgumentException($"未知前缀:{prefix}");
}
// 除前缀外,其余部分都视为成员访问路径,便于后续扩展多级属性访问
string memberAccess = string.Join(".", parts.Skip(1).Select(p => p.Trim()));
string objectExpr = $"(({typeFullName})DataMap[\"{prefix}\"]).{memberAccess}";
if (variableType == "numeric" || variableType == "number")
{
return $"Convert.ToDecimal({objectExpr})";
}
return objectExpr;
}
/// <summary>
/// 用 Roslyn 编译 C# 脚本代码为可执行委托
/// </summary>
private static Func<RiskContext, bool> CompileScript(string scriptCode)
{
// 配置编译选项:引用必要的程序集
var options = ScriptOptions.Default
.WithReferences(
typeof(RiskContext).Assembly, // YLErpDAL
typeof(YLErp.DBModels.trade).Assembly // Model 所在程序集
)
.WithImports("System");
// 创建脚本(尚未执行,仅编译)
var script = CSharpScript.Create<bool>(scriptCode, options, globalsType: typeof(ScriptGlobals));
// 编译(提前发现语法错误)
//后续编译需提供接口,返回前端编译信息,包含编译错误列表
var compilation = script.GetCompilation();
var diagnostics = compilation.GetDiagnostics();
var errors = diagnostics.Where(d => d.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error).ToList();
if (errors.Any())
{
string errorMsg = string.Join("; ", errors.Select(e => e.GetMessage()));
_logger.Error($"规则编译失败:{errorMsg}\n脚本代码:{scriptCode}");
throw new InvalidOperationException($"脚本编译失败:{errorMsg}");
}
// 生成可调用委托
var runner = script.CreateDelegate();
// 包装为同步的 Func<RiskContext, bool>
return ctx =>
{
var globals = new ScriptGlobals { DataMap = ctx.DataMap };
try
{
return runner(globals).GetAwaiter().GetResult();
}
catch (Exception ex)
{
// 脚本执行异常(如空引用、类型转换失败)视为规则不触发
_logger.Error($"规则执行异常:{ex.Message}\n脚本代码:{scriptCode}");
return false;
}
};
}
/// <summary>
/// 格式化阈值为 C# 字面量
/// </summary>
private static string FormatValueLiteral(object value, string variableType)
{
if (value == null) return "null";
string strValue = value.ToString();
// 数值类型统一加 m 后缀(decimal)
if (variableType == "numeric" || variableType == "number")
{
if (decimal.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal d))
{
return d.ToString(CultureInfo.InvariantCulture) + "m";
}
}
// 布尔
if (variableType == "boolean" || variableType == "bool")
{
if (bool.TryParse(strValue, out bool b))
{
return b ? "true" : "false";
}
}
// 日期
if (variableType == "date" && DateTime.TryParse(strValue, out DateTime dt))
{
return $"DateTime.Parse(\"{dt:yyyy-MM-dd}\")";
}
// 字符串(兜底)
return $"\"{strValue.Replace("\\", "\\\\").Replace("\"", "\\\"")}\"";
}
/// <summary>
/// 将操作符统一为 C# 操作符
/// </summary>
private static string FormatOperator(string op)
{
return op?.Trim() switch
{
// 数值型 C# 符号
"=" or "==" => "==",
"!=" or "≠" => "!=",
">" => ">",
"<" => "<",
">=" => ">=",
"<=" => "<=",
// 日期型中文语义 → C# 符号
"早于" => "<",
"晚于" => ">",
"等于" => "==",
"不早于" => ">=",
"不晚于" => "<=",
_ => throw new ArgumentException($"不支持的操作符:{op}")
};
}
}
}
@@ -1,10 +0,0 @@
namespace YLErp.Modules.RiskEngine.Dto
{
public class BatchOperationResult
{
public bool Success { get; set; }
public int TotalCount { get; set; }
public int SuccessCount { get; set; }
public string ErrorMessage { get; set; }
}
}
@@ -1,16 +0,0 @@
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class CreateRiskApplicationReq
{
public string RuleIds { 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 int ScopeIsGlobal { get; set; }
}
}
@@ -1,10 +0,0 @@
namespace YLErp.Modules.RiskEngine.Dto
{
public class CreateRiskRuleReq
{
public string RuleName { get; set; }
public string RuleText { get; set; }
public string ConditionJson { get; set; }
public string RuleExpr { get; set; }
}
}
@@ -1,16 +0,0 @@
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class CreateRiskVariableReq
{
public string VariableName { get; set; }
public RiskVariableCategory Category { get; set; }
public RiskVariableDataType DataType { get; set; }
public string Unit { get; set; }
public string ValueDomain { get; set; }
public string Description { get; set; }
public string VariableExpr { get; set; }
public int SortOrder { get; set; }
}
}
@@ -1,17 +0,0 @@
using BaseOUDAL;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class QueryRiskApplicationReq : BaseSearchReq
{
public string RuleName { get; set; }
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; }
}
}
@@ -1,14 +0,0 @@
using System;
using BaseOUDAL;
namespace YLErp.Modules.RiskEngine.Dto
{
public class QueryRiskAuditLogReq : BaseSearchReq
{
public string OperationType { get; set; }
public string TargetType { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public string Keyword { get; set; }
}
}
@@ -1,12 +0,0 @@
using BaseOUDAL;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class QueryRiskRuleReq : BaseSearchReq
{
public string Keyword { get; set; }
public RiskRuleStatus? Status { get; set; }
public long? VariableId { get; set; }
}
}
@@ -1,12 +0,0 @@
using BaseOUDAL;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class QueryRiskVariableReq : BaseSearchReq
{
public RiskVariableCategory? Category { get; set; }
public RiskVariableDataType? DataType { get; set; }
public string VariableName { get; set; }
}
}
@@ -1,24 +0,0 @@
using System;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskApplicationDetail
{
public long Id { get; set; }
public string RuleIds { get; set; }
public RiskRuleStatus 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 int ScopeIsGlobal { get; set; }
public int Version { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
public string UpdateOptName { get; set; }
public DateTime UpdateDate { get; set; }
}
}
@@ -1,24 +0,0 @@
using System;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskApplicationListItem
{
public long Id { get; set; }
public string RuleIds { 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 int ScopeIsGlobal { get; set; }
public int Version { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
public string UpdateOptName { get; set; }
public DateTime UpdateDate { get; set; }
}
}
@@ -1,19 +0,0 @@
using System;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskAuditLogDetail
{
public long Id { get; set; }
public string OperationType { get; set; }
public string TargetType { get; set; }
public long TargetId { get; set; }
public string TargetName { get; set; }
public string OperationDetail { get; set; }
public string Result { get; set; }
public string SnapshotData { get; set; }
public long OptId { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
}
}
@@ -1,17 +0,0 @@
using System;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskAuditLogListItem
{
public long Id { get; set; }
public string OperationType { get; set; }
public string TargetType { get; set; }
public long TargetId { get; set; }
public string TargetName { get; set; }
public string OperationDetail { get; set; }
public string Result { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
}
}
@@ -1,12 +0,0 @@
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskRuleApplicationSummary
{
public long Id { get; set; }
public RiskRuleStatus Status { get; set; }
public RiskControlStrategy ControlStrategy { get; set; }
public string TriggerPoints { get; set; }
}
}
@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskRuleDetail
{
public long Id { get; set; }
public string RuleName { get; set; }
public string RuleText { get; set; }
public string ConditionJson { get; set; }
public string RuleExpr { get; set; }
public RiskRuleStatus Status { get; set; }
public int Version { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
public string UpdateOptName { get; set; }
public DateTime UpdateDate { get; set; }
public List<RiskRuleApplicationSummary> Applications { get; set; }
}
}
@@ -1,21 +0,0 @@
using System;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskRuleListItem
{
public long Id { get; set; }
public string RuleName { get; set; }
public string RuleText { get; set; }
public string ConditionJson { get; set; }
public string RuleExpr { get; set; }
public RiskRuleStatus Status { get; set; }
public int Version { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
public string UpdateOptName { get; set; }
public DateTime UpdateDate { get; set; }
public int ApplicationCount { get; set; }
}
}
@@ -1,9 +0,0 @@
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskRuleSimpleItem
{
public long Id { get; set; }
public string RuleName { get; set; }
public string RuleText { get; set; }
}
}
@@ -1,13 +0,0 @@
using System;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskRuleVersionItem
{
public int Version { get; set; }
public string OperationType { get; set; }
public string OperationDetail { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
}
}
@@ -1,24 +0,0 @@
using System;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskVariableDetail
{
public long Id { get; set; }
public string VariableName { get; set; }
public RiskVariableCategory Category { get; set; }
public RiskVariableDataType DataType { get; set; }
public string Unit { get; set; }
public string ValueDomain { get; set; }
public string Description { get; set; }
public string VariableExpr { get; set; }
public int Version { get; set; }
public int SortOrder { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
public string UpdateOptName { get; set; }
public DateTime UpdateDate { get; set; }
public int ReferenceCount { get; set; }
}
}
@@ -1,18 +0,0 @@
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskVariableListItem
{
public long Id { get; set; }
public string VariableName { get; set; }
public RiskVariableCategory Category { get; set; }
public RiskVariableDataType DataType { get; set; }
public string Unit { get; set; }
public string ValueDomain { get; set; }
public string Description { get; set; }
public string VariableExpr { get; set; }
public int Version { get; set; }
public int SortOrder { get; set; }
}
}
@@ -1,14 +0,0 @@
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class RiskVariableSimpleItem
{
public long Id { get; set; }
public string VariableName { get; set; }
public RiskVariableCategory Category { get; set; }
public RiskVariableDataType DataType { get; set; }
public string Unit { get; set; }
public string VariableExpr { get; set; }
}
}
@@ -1,14 +0,0 @@
namespace YLErp.Modules.RiskEngine.Dto
{
public class RuleCondition
{
public long VariableId { get; set; }
public string Operator { get; set; }
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;
}
}
@@ -1,17 +0,0 @@
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class UpdateRiskApplicationReq
{
public string RuleIds { 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 int ScopeIsGlobal { get; set; }
public int Version { get; set; }
}
}
@@ -1,11 +0,0 @@
namespace YLErp.Modules.RiskEngine.Dto
{
public class UpdateRiskRuleReq
{
public string RuleName { get; set; }
public string RuleText { get; set; }
public string ConditionJson { get; set; }
public string RuleExpr { get; set; }
public int Version { get; set; }
}
}
@@ -1,17 +0,0 @@
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine.Dto
{
public class UpdateRiskVariableReq
{
public string VariableName { get; set; }
public RiskVariableCategory Category { get; set; }
public RiskVariableDataType DataType { get; set; }
public string Unit { get; set; }
public string ValueDomain { get; set; }
public string Description { get; set; }
public string VariableExpr { get; set; }
public int SortOrder { get; set; }
public int Version { get; set; }
}
}
@@ -1,25 +0,0 @@
using System.Collections.Generic;
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// 风控上下文:封装一次风控检查所需的全部数据上下文
/// </summary>
public class RiskContext
{
/// <summary>
/// 当前交易ID
/// </summary>
public int TradeId { get; set; }
/// <summary>
/// 当前触发时点
/// </summary>
public string TriggerPoint { get; set; }
/// <summary>
/// 数据字典:key=表名/前缀,value=对应数据对象
/// </summary>
public Dictionary<string, object> DataMap { get; set; } = new Dictionary<string, object>();
}
}
@@ -1,638 +0,0 @@
using Newtonsoft.Json;
using Qdp.Foundation.Utilities;
using System.Reflection;
using YLErp.BLL;
using YLErp.Model;
/*
================================================================================
RiskEngineService
================================================================================
TRS QuotaMonitorService
- RiskRule IdRuleName
RuleTextRuleExprVersionCompiledScript
- RiskRuleApplication
/ / / /
- RuleExpr Roslyn C# bool
访 DataMap["trade"]
- ConditionJson
- RiskContext TradeIdTriggerPoint DataMap
- RuleCompiledCache Id Func<RiskContext, bool>
1. RuleExpr
2. RuleCompiler.ValidateAndCompileRule(rule)
3. 使 Roslyn RuleExpr Func<RiskContext, bool>
4. rule.CompiledScript RuleCompiledCache
5. RuleCompiledCache
EvaluateRisk
1.
2. TriggerPoints
3. / / / /
4. RuleIds Id
5. RuleCompiledCache
6. ControlStrategy Blocked / NeedApproval / Warnings
7. RiskResult
- ScopeIsGlobal=true
- OR
- AND
-
- Content RuleExpr
- RiskRule RiskRuleApplication
- RiskEngine/Compile
- RuleCompileResultRuleCompiledCache
- Rule2 RuleExpr
YLErpDAL/Modules/RiskEngine/
- RiskEngineService.cs
- RiskRule.cs CompiledScript
- RiskRuleApplication.cs
- RiskContext.cs / RiskResult.cs
YLErpDAL/Modules/RiskEngine/Compile/
- RuleCompiler.csRoslyn ValidateAndCompileFormula / ValidateAndCompileRule
- RuleCompileResult.cs
- RuleCompiledCache.cs
- QuotaMonitorService.cs RiskContext trade DataMap["trade"]
2026-06-24
1. RiskEngine QuotaMonitorService -> RiskEngineService -> RuleCompiler
2. DataMap trade 访
3. RuleExpr
4. Application / / / /
5. OR AND
6. RuleCompiler + +
7. RuleCompiledCache / RuleCompileResult Compile
8. RiskEngineService RuleCompiledCache
/ TODO
1.
- LoadRules / LoadApplications
2.
- RuleExpr
- /
3.
-
- /
4.
-
-
5.
-
6. RuleExpr
-
-
1. RuleExpr Roslyn C# bool
2. DataMap 宿
3. Func<RiskContext, bool>
4.
================================================================================
*/
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// 风控引擎服务(第一版 — 骨架版,先跑通)
/// </summary>
public class RiskEngineService : YLBaseService
{
IYcLogger _logger = LogFactory.GetLogger("RiskEngineService");
private static readonly Lazy<RiskEngineService> _instance =
new Lazy<RiskEngineService>(() => new RiskEngineService());
public static RiskEngineService GetInstance() => _instance.Value;
private RiskEngineService() : base(OptUserInfo.SystemUser)
{
}
public RiskEngineService(OptUserInfo userInfo) : base(userInfo)
{
}
public RiskEngineService(YLBaseService baseService) : base(baseService)
{
}
public RiskEngineService(OptUserInfo optUser, YLContext dbContext) : base(optUser, dbContext)
{
}
/// <summary>
/// 规则内存缓存(启动预热写入,EvaluateRisk 读取)
/// </summary>
private static volatile List<RiskRule> _cachedRules;
private static volatile List<RiskRuleApplication> _cachedApplications;
private static readonly object _cacheLock = new object();
/// <summary>
/// 预热:加载规则与应用到内存,并预编译所有规则到 RuleCompiledCache。
/// 项目启动时调用一次;规则/应用更新后调用 RefreshCache 刷新。
/// </summary>
public void Preload()
{
lock (_cacheLock)
{
_logger.Info("[风控引擎] Preload 开始 - 加载规则与应用并预编译");
var rules = LoadRulesFromDb();
var applications = LoadApplicationsFromDb();
List<long> res = new List<long>();
foreach(var i in applications)
{
if(i.Status!= RiskRuleStatus.Active)
{
res.Add(i.Id);
}
}
// 预编译所有规则到 RuleCompiledCache
foreach (var rule in rules)
{
var ruleId = rule.Id.ToString();
if (RuleCompiledCache.TryGet(ruleId, out _))
{
continue;
}
var compileResult = RuleCompiler.ValidateAndCompileRule(rule);
if (!compileResult.Success)
{
_logger.Info($"[风控引擎] 规则预编译失败 - RuleId: {rule.Id}, Error: {compileResult.ErrorMessage}");
}
}
_cachedRules = rules;
_cachedApplications = applications;
_logger.Info($"[风控引擎] Preload 完成 - 规则数: {rules.Count}, 应用数: {applications.Count}");
}
}
/// <summary>
/// 刷新缓存:清空已编译委托与内存缓存后重新预加载。
/// 规则/应用配置更新后调用。
/// </summary>
public void RefreshCache()
{
_logger.Info("[风控引擎] RefreshCache 被调用 - 清空缓存并重新预加载");
lock (_cacheLock)
{
_cachedRules = null;
_cachedApplications = null;
RuleCompiledCache.Clear();
}
Preload();
}
/// <summary>
/// 从内存缓存获取规则列表;缓存为空时兜底加载并填充缓存。
/// </summary>
private List<RiskRule> GetRules()
{
var rules = _cachedRules;
if (rules != null)
{
return rules;
}
lock (_cacheLock)
{
if (_cachedRules != null)
{
return _cachedRules;
}
var loaded = LoadRulesFromDb();
_cachedRules = loaded;
return loaded;
}
}
/// <summary>
/// 从内存缓存获取应用列表;缓存为空时兜底加载并填充缓存。
/// </summary>
private List<RiskRuleApplication> GetApplications()
{
var applications = _cachedApplications;
if (applications != null)
{
return applications;
}
lock (_cacheLock)
{
if (_cachedApplications != null)
{
return _cachedApplications;
}
var loaded = LoadApplicationsFromDb();
_cachedApplications = loaded;
return loaded;
}
}
/// <summary>
/// 执行风控检查(最终设计版:预编译委托 + 规则筛选 + 策略判定)
/// </summary>
/// <param name="context">风控上下文</param>
/// <param name="triggerPoint">触发时点,如 BOOK_CONFIRM</param>
/// <returns>风控结果</returns>
public RiskResult EvaluateRisk(RiskContext context, string triggerPoint)
{
var result = new RiskResult();
try
{
_logger.Info($"[风控引擎] EvaluateRisk 开始 - TradeId: {context?.TradeId}, TriggerPoint: {triggerPoint}");
// ============================================================
// Step 1: 从内存缓存读取规则定义和规则应用(启动时已预热)
// ============================================================
var rules = GetRules();
var applications = GetApplications();
_logger.Info($"[风控引擎] 加载规则数: {rules.Count}, 应用数: {applications.Count}");
// ============================================================
// Step 2: 先按 Application 过滤 Active 状态和 TriggerPoint 匹配的应用
// ============================================================
var activeApps = applications
.Where(a => a.Status == RiskRuleStatus.Active)
.ToList();
var apps = activeApps
.Where(a => !string.IsNullOrEmpty(a.TriggerPoints))
.ToList();
var triggerMatchedApps = apps
.Where(a => a.TriggerPoints.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.Contains(triggerPoint))
.ToList();
// 再按应用范围过滤。
// 统一走通用维度匹配:
// 1. 全局命中时直接通过;
// 2. 同一维度内多选按并集处理;
// 3. 不同维度之间按交集处理;
// 4. 某维度留空表示该维度不限制。
var trade = context?.DataMap != null && context.DataMap.ContainsKey("trade")
? context.DataMap["trade"] as YLErp.DBModels.trade
: null;
var matchedApplications = triggerMatchedApps
.Where(a => IsApplicationMatched(a, trade))
.ToList();
_logger.Info($"[风控引擎] 匹配 TriggerPoint 的应用数: {matchedApplications.Count}");
// ============================================================
// Step 3: 遍历匹配的应用,通过 RuleIds 关联规则并执行预编译委托
// ============================================================
foreach (var application in matchedApplications)
{
// ------------------------------------------------------------
// 3.1 通过 RuleIds 关联规则定义
// ------------------------------------------------------------
var applicationRuleIds = ParseRuleIds(application.RuleIds);
if (!applicationRuleIds.Any())
{
_logger.Info($"[风控引擎] 应用未配置有效规则 - RuleIds: {application.RuleIds}");
continue;
}
var applicationRules = rules
.Where(r => applicationRuleIds.Contains(r.Id) && r.Id == 3)
.ToList();
var missingRuleIds = applicationRuleIds
.Where(ruleId => applicationRules.All(r => r.Id != ruleId))
.ToList();
foreach (var missingRuleId in missingRuleIds)
{
_logger.Info($"[风控引擎] 未找到对应规则定义 - RuleId: {missingRuleId}, ApplicationRuleIds: {application.RuleIds}");
}
foreach (var rule in applicationRules)
{
var ruleId = rule.Id.ToString();
// ------------------------------------------------------------
// 3.2 优先从编译缓存读取规则委托
// ------------------------------------------------------------
if (!RuleCompiledCache.TryGet(ruleId, out var compiledScript))
{
_logger.Info($"[风控引擎] 缓存中未命中已编译规则,执行兜底编译 - RuleId: {rule.Id}");
var compileResult = RuleCompiler.ValidateAndCompileRule(rule);
if (!compileResult.Success)
{
_logger.Info($"[风控引擎] 规则编译失败 - RuleId: {rule.Id}, Error: {compileResult.ErrorMessage}");
continue;
}
compiledScript = compileResult.CompiledScript;
}
// ------------------------------------------------------------
// 3.3 执行预编译委托
// ------------------------------------------------------------
bool triggered = false;
try
{
triggered = compiledScript(context);
_logger.Info($"[风控引擎] 规则执行 - RuleId: {rule.Id}, Triggered: {triggered}");
}
catch (Exception ex)
{
_logger.Info($"[风控引擎] 规则执行异常 - RuleId: {rule.Id}, Error: {ex.Message}");
continue;
}
// ------------------------------------------------------------
// 3.4 命中后按 Application 的控制策略聚合结果
// ------------------------------------------------------------
if (triggered)
{
_logger.Info($"[风控引擎] 规则触发 - RuleId: {rule.Id}, Strategy: {application.ControlStrategy}");
switch (application.ControlStrategy)
{
case RiskControlStrategy.Block:
break;
result.Blocked = true;
result.Passed = false;
result.TriggeredRules.Add(new TriggeredRuleInfo
{
RuleId = rule.Id.ToString(),
RuleName = rule.RuleName,
ControlStrategy = RiskControlStrategy.Block,
RuleText = rule.RuleText,
Message = $"规则[{rule.RuleName}]触发:禁止"
});
break;
case RiskControlStrategy.Approval:
result.NeedApproval = true;
result.Passed = false;
result.TriggeredRules.Add(new TriggeredRuleInfo
{
RuleId = ruleId,
RuleName = rule.RuleName,
ControlStrategy = RiskControlStrategy.Approval,
RuleText = rule.RuleText,
Message = $"规则[{rule.RuleName}]触发:需审批"
});
break;
case RiskControlStrategy.ShowTip:
result.ShowTip = true;
result.TriggeredRules.Add(new TriggeredRuleInfo
{
RuleId = ruleId,
RuleName = rule.RuleName,
ControlStrategy = RiskControlStrategy.ShowTip,
RuleText = rule.RuleText,
Message = $"规则[{rule.RuleName}]触发:提示"
});
break;
default:
_logger.Info($"[风控引擎] 未知策略类型 - ControlStrategy: {application.ControlStrategy}");
break;
}
}
else
{
_logger.Info($"[风控引擎] 规则未触发 - RuleId: {rule.Id}, TriggerPoint: {triggerPoint}");
}
}
}
// ============================================================
// Step 4: 聚合最终结果
// ============================================================
if (!result.Blocked && !result.NeedApproval)
{
result.Passed = true;
}
else
{
result.Passed = false;
}
_logger.Info($"[风控引擎] EvaluateRisk 完成 - TradeId: {context?.TradeId}, Passed: {result.Passed}, Blocked: {result.Blocked}, NeedApproval: {result.NeedApproval}, TriggeredRules: {result.TriggeredRules.Count}");
}
catch (Exception ex)
{
result.NeedApproval = true;
result.Passed = false;
result.TriggeredRules.Add(new TriggeredRuleInfo
{
RuleId = "ENGINE_ERROR",
RuleName = "风控引擎执行异常",
RuleText = ex.Message,
Message = $"风控引擎异常:{ex.Message}"
});
_logger.Error($"[风控引擎] EvaluateRisk 异常 - TradeId: {context?.TradeId}, Error: {ex.Message}");
}
return result;
}
/// <summary>
/// 从数据库加载规则列表
/// </summary>
private List<RiskRule> LoadRulesFromDb()
{
return DbContext.glms_risk_rule
.AsNoTracking()
.Where(r => r.Status != RiskRuleStatus.Deleted)
.OrderByDescending(r => r.UpdateDate ?? r.OptDate)
.Select(r => new RiskRule
{
Id = r.id,
RuleName = r.RuleName,
RuleText = r.RuleText,
ConditionJson = r.ConditionJson,
RuleExpr = r.RuleExpr,
Version = r.Version,
IsDeleted = r.Status == RiskRuleStatus.Deleted,
OptId = r.OptId ?? 0,
OptName = r.OptName,
OptDate = r.OptDate ?? DateTime.MinValue,
UpdateOptId = r.UpdateOptId ?? 0,
UpdateOptName = r.UpdateOptName,
UpdateDate = r.UpdateDate ?? r.OptDate ?? DateTime.MinValue
})
.ToList();
}
/// <summary>
/// 从数据库加载应用配置
/// </summary>
private List<RiskRuleApplication> LoadApplicationsFromDb()
{
return DbContext.glms_risk_rule_application
.AsNoTracking()
.Where(a => a.Status != RiskRuleStatus.Deleted)
.OrderByDescending(a => a.UpdateDate ?? a.OptDate)
.Select(a => new RiskRuleApplication
{
Id = a.id,
RuleIds = a.RuleIds,
Status = a.Status,
ControlStrategy = a.ControlStrategy,
TriggerPoints = a.TriggerPoints,
ScopeIsGlobal = a.ScopeIsGlobal,
ScopeAssetBookIds = a.ScopeAssetBookIds,
ScopeClientIds = a.ScopeClientIds,
ScopeUnderlyingTypes = a.ScopeUnderlyingTypes,
ScopeTradeTypes = a.ScopeTradeTypes,
Version = a.Version,
OptId = a.OptId ?? 0,
OptName = a.OptName,
OptDate = a.OptDate ?? DateTime.MinValue,
UpdateOptId = a.UpdateOptId ?? 0,
UpdateOptName = a.UpdateOptName,
UpdateDate = a.UpdateDate ?? a.OptDate ?? DateTime.MinValue
})
.ToList();
}
/// <summary>
/// 判断应用配置是否命中当前交易。
/// 匹配规则遵循设计文档:
/// 1. 全局命中时直接返回 true;
/// 2. 同一维度内多选按并集处理;
/// 3. 不同维度之间按交集处理;
/// 4. 某维度留空表示该维度不限制。
/// </summary>
private bool IsApplicationMatched(RiskRuleApplication application, YLErp.DBModels.trade trade)
{
if (application == null)
{
return false;
}
if (application.ScopeIsGlobal == 1)
{
return true;
}
if (trade == null)
{
return false;
}
var accountMatched = IsScopeEmpty(application.ScopeAssetBookIds) || IsValueMatched(application.ScopeAssetBookIds, GetTradeAssetBookId(trade));
var clientMatched = IsScopeEmpty(application.ScopeClientIds) || IsValueMatched(application.ScopeClientIds, trade.ClientId);
var underlyingTypeMatched = IsScopeEmpty(application.ScopeUnderlyingTypes) || IsValueMatched(application.ScopeUnderlyingTypes, GetTradeUnderlyingType(trade));
var tradeTypeMatched = IsScopeEmpty(application.ScopeTradeTypes) || IsValueMatched(application.ScopeTradeTypes, trade.TradeType);
return accountMatched
&& clientMatched
&& underlyingTypeMatched
&& tradeTypeMatched;
}
/// <summary>
/// 解析应用配置中的规则ID列表。
/// 多个规则ID使用逗号分隔,返回去空格后的 long 集合。
/// </summary>
private List<long> ParseRuleIds(string ruleIds)
{
if (string.IsNullOrWhiteSpace(ruleIds))
{
return new List<long>();
}
var ids = new List<long>();
foreach (var part in ruleIds.Split(',', StringSplitOptions.RemoveEmptyEntries))
{
if (long.TryParse(part.Trim(), out long id))
{
ids.Add(id);
}
}
return ids;
}
/// <summary>
/// 判断某个范围字段是否为空。
/// 为空表示该维度不限制。
/// </summary>
private bool IsScopeEmpty(string scopeValue)
{
return string.IsNullOrWhiteSpace(scopeValue);
}
/// <summary>
/// 判断单个值是否命中逗号分隔的范围配置。
/// 同一维度内多选按并集处理,只要命中任一值即返回 true。
/// </summary>
private bool IsValueMatched(string scopeValue, object currentValue)
{
if (string.IsNullOrWhiteSpace(scopeValue))
{
return true;
}
if (currentValue == null)
{
return false;
}
var currentText = currentValue.ToString()?.Trim();
if (string.IsNullOrWhiteSpace(currentText))
{
return false;
}
return scopeValue
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.Any(s => string.Equals(s, currentText, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// 读取交易的资产簿账户ID。
/// 当前先按 BookId 取值;如果后续真实字段不是 BookId,再统一调整这里即可。
/// </summary>
private object GetTradeAssetBookId(YLErp.DBModels.trade trade)
{
var property = trade.GetType().GetProperty("BookId");
return property?.GetValue(trade);
}
/// <summary>
/// 读取交易的标的类型。
/// 当前先按 UnderlyingInstrumentType 取值;如果后续真实字段名不同,再统一调整这里即可。
/// </summary>
private object GetTradeUnderlyingType(YLErp.DBModels.trade trade)
{
var property = trade.GetType().GetProperty("UnderlyingInstrumentType");
return property?.GetValue(trade);
}
}
}
-47
View File
@@ -1,47 +0,0 @@
using System.Collections.Generic;
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// 风控结果
/// </summary>
public class RiskResult
{
/// <summary>
/// 是否全部通过
/// </summary>
public bool Passed { get; set; } = true;
/// <summary>
/// 是否被禁止
/// </summary>
public bool Blocked { get; set; }
/// <summary>
/// 是否需要审批
/// </summary>
public bool NeedApproval { get; set; }
/// <summary>
/// 是否存在提示
/// </summary>
public bool ShowTip { get; set; }
/// <summary>
/// 触发的规则列表
/// </summary>
public List<TriggeredRuleInfo> TriggeredRules { get; set; } = new List<TriggeredRuleInfo>();
}
/// <summary>
/// 触发规则详情
/// </summary>
public class TriggeredRuleInfo
{
public string RuleId { get; set; }
public string RuleName { get; set; }
public RiskControlStrategy ControlStrategy { get; set; }
public string RuleText { get; set; }
public string Message { get; set; }
}
}
-50
View File
@@ -1,50 +0,0 @@
using System;
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// 风控规则定义。
/// 只描述“规则是什么”,不包含状态、策略、触发时点、适用范围等应用配置。
/// 这些执行维度由 RiskRuleApplication 承载。
/// </summary>
public class RiskRule
{
// === 基础信息 ===
public long Id { get; set; }
public string RuleName { get; set; }
public string RuleText { get; set; }
/// <summary>
/// 【核心】条件JSON(结构化模式的条件列表存储),如
/// {"conditions":[{"variableId":1,"operator":">","value":100000000}]}
/// 由 RuleCompiler 解析并编译为 CompiledScript
/// </summary>
public string ConditionJson { get; set; }
/// <summary>
/// 【核心】规则表达式(自由文本模式,类C#表达式)
/// 由 RuleCompiler 编译为 CompiledScript
/// </summary>
public string RuleExpr { get; set; }
// === 生命周期 ===
public int Version { get; set; }
public bool IsDeleted { get; set; }
// === 审计字段 ===
public int OptId { get; set; }
public string OptName { get; set; }
public DateTime OptDate { get; set; }
public int UpdateOptId { get; set; }
public string UpdateOptName { get; set; }
public DateTime UpdateDate { get; set; }
// === 运行时内存字段(不持久化到数据库)===
/// <summary>
/// 【核心】预编译的可执行委托
/// 在规则创建/加载时由 RuleCompiler.Compile(this) 生成
/// 判风控时直接调用:rule.CompiledScript(context) → bool
/// </summary>
public Func<RiskContext, bool> CompiledScript { get; set; }
}
}
@@ -1,142 +0,0 @@
using System;
using YLErp.DBModels;
namespace YLErp.Modules.RiskEngine
{
/// <summary>
/// 风控规则应用配置。
///
/// 设计原因:
/// 依据设计文档,风控规则需要区分“规则定义”和“规则应用”两个层面。
/// RiskRule 负责定义“规则是什么”,例如规则名称、规则编码、公式表达式、描述、版本等;
/// RiskRuleApplication 负责定义“规则在什么场景下如何执行”,例如启用状态、校验策略、触发时点、适用范围。
///
/// 拆分后的好处是:
/// 1. 同一条规则可以被多个应用配置复用,避免重复维护相同的公式定义。
/// 2. 不同业务场景可以对同一条规则配置不同的执行策略,例如在簿记确认时禁止,在平仓审核时提示。
/// 3. 调整触发时点、适用范围、控制策略时,不需要修改规则本身,降低配置耦合。
/// 4. 后续如果要做规则管理页面、规则启停、按场景灰度投放,也更容易扩展。
///
/// 当前文件先定义第一版最小模型,用于支撑基础流程跑通和示例数据加载。
/// </summary>
public class RiskRuleApplication
{
/// <summary>
/// 应用配置主键ID。
/// 用于唯一标识一条规则应用记录。
/// </summary>
public long Id { get; set; }
/// <summary>
/// 关联的规则ID列表。
/// 通过该字段与 RiskRule.Id 关联,多个规则ID使用逗号分隔。
/// </summary>
public string RuleIds { get; set; }
/// <summary>
/// 应用状态。
/// 约定:
/// 1 = Active(已生效)
/// 2 = Disabled(已停用)
/// 3 = Deleted(已删除)
/// 在实际执行时,通常只加载 Active 状态的应用配置。
/// </summary>
public RiskRuleStatus Status { get; set; }
/// <summary>
/// 校验策略。
/// 约定:
/// 1 = 禁止
/// 2 = 审批
/// 3 = 提示
/// 当规则命中时,由该字段决定最终处理方式。
/// </summary>
public RiskControlStrategy ControlStrategy { get; set; }
/// <summary>
/// 触发时点。
/// 多个时点使用逗号分隔,例如:
/// BOOK_CONFIRM,CLOSE_REVIEW
/// 表示该应用配置会在这些业务时点参与风控判断。
/// </summary>
public string TriggerPoints { get; set; }
/// <summary>
/// 是否全局适用。
/// true 表示对所有交易都适用,此时通常忽略其他 Scope 字段;
/// false 表示只在指定范围内适用,需要结合下方各 Scope 字段进行过滤。
/// </summary>
public int ScopeIsGlobal { get; set; }
/// <summary>
/// 适用的资产簿记账户范围。
/// 第一版使用逗号分隔的字符串保存,例如:
/// 123,456,789
/// 后续执行时可按约定解析为账户ID集合。
/// </summary>
public string ScopeAssetBookIds { get; set; }
/// <summary>
/// 适用的客户范围。
/// 第一版使用逗号分隔的字符串保存客户ID,例如:
/// 10001,10002
/// 用于在执行前筛选该规则是否适用于当前客户。
/// </summary>
public string ScopeClientIds { get; set; }
/// <summary>
/// 适用的标的类型范围。
/// 例如:
/// 股票,ETF,债券
/// 用于控制规则仅对特定标的类型生效。
/// </summary>
public string ScopeUnderlyingTypes { get; set; }
/// <summary>
/// 适用的交易类型范围。
/// 例如:
/// 簿记交易,平仓交易
/// 用于控制规则仅在指定交易类型下生效。
/// </summary>
public string ScopeTradeTypes { get; set; }
public int Version { get; set; }
/// <summary>
/// 创建人ID。
/// 记录是谁创建了这条应用配置。
/// </summary>
public int OptId { get; set; }
/// <summary>
/// 创建人名称。
/// 便于界面展示和审计追踪。
/// </summary>
public string OptName { get; set; }
/// <summary>
/// 创建时间。
/// 记录应用配置的创建时刻。
/// </summary>
public DateTime OptDate { get; set; }
/// <summary>
/// 最后更新人ID。
/// 记录最近一次修改该应用配置的操作人。
/// </summary>
public int UpdateOptId { get; set; }
/// <summary>
/// 最后更新人名称。
/// 便于界面展示和审计追踪。
/// </summary>
public string UpdateOptName { get; set; }
/// <summary>
/// 最后更新时间。
/// 记录最近一次修改该应用配置的时间。
/// </summary>
public DateTime UpdateDate { get; set; }
}
}
File diff suppressed because it is too large Load Diff
@@ -50,7 +50,6 @@ using YLErp.Modules.TradeDalModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
using YLErp.Modules.RiskEngine;
using static YLErp.ConsGlobal;
namespace YLErp.Modules.RiskModule
@@ -5140,64 +5139,6 @@ namespace YLErp.Modules.RiskModule
result.QuotaWarningDetails = quotaWarningMsg ?? "";
result.AvailableForClient = availableMsg ?? "";
result.TrialSource = trialSource;
// ===== 新风控引擎接入点(设计文档 4.6.1) =====
try
{
var riskEngine = new RiskEngineService(this);
var riskContext = new RiskContext
{
TradeId = tradeId,
TriggerPoint = "BOOK_CONFIRM"
};
// 构造 DataMap(第一版只塞 trade 对象,后续按需扩展)
riskContext.DataMap["trade"] = tradeObj;
var riskResult = riskEngine.EvaluateRisk(riskContext, "BOOK_CONFIRM");
_logger.Info($"[风控引擎] 簿记交易确认 - TradeId: {tradeId}, Passed: {riskResult.Passed}, Blocked: {riskResult.Blocked}, NeedApproval: {riskResult.NeedApproval}, ShowTip: {riskResult.ShowTip}");
// 按策略映射回 QuotaTrialStatusEnum
if (riskResult.Blocked)
{
result.TrialStatus = QuotaTrialStatusEnum.Error;
result.RiskWarningDetails += "[风控引擎] 规则触发:禁止\n";
foreach (var triggeredRule in riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.Block))
{
result.RiskWarningDetails += $"规则ID:{triggeredRule.RuleId};规则名称:{triggeredRule.RuleName};规则说明:{triggeredRule.RuleText}\n";
}
}
if (riskResult.NeedApproval)
{
if (result.TrialStatus != QuotaTrialStatusEnum.Error)
{
result.TrialStatus = QuotaTrialStatusEnum.Warning;
}
result.RiskWarningDetails += "[风控引擎] 规则触发:需审批\n";
foreach (var triggeredRule in riskResult.TriggeredRules.Where(r => r.ControlStrategy == RiskControlStrategy.Approval))
{
result.RiskWarningDetails += $"规则ID:{triggeredRule.RuleId};规则名称:{triggeredRule.RuleName};规则说明:{triggeredRule.RuleText}\n";
}
}
if (riskResult.ShowTip)
{
var warningMessages = riskResult.TriggeredRules
.Where(r => r.ControlStrategy == RiskControlStrategy.ShowTip)
.Select(r => r.Message)
.Where(r => !string.IsNullOrWhiteSpace(r))
.ToList();
if (warningMessages.Count > 0)
{
result.RiskWarningDetails += "[风控引擎] 提示:" + string.Join("", warningMessages) + "\n";
}
}
}
catch (Exception ex)
{
_logger.Error($"[风控引擎] 执行异常 - TradeId: {tradeId}, Error: {ex.Message}");
// 第一版:异常不阻断原有流程,只记录日志
}
// ===== 新风控引擎接入结束 =====
_logger.Info($"[限额试算] 试算完成 - tradeId: {tradeId}, TrialStatus: {result.TrialStatus}, FundCheck: {fundStatus}, QuotaCheck: {quotaStatus}");
SaveQuotaTrial(result);
return result;
+26 -3
View File
@@ -254,6 +254,9 @@ namespace YLErp.Modules.SwapModule
unwindData.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules));
floatEvent.PayDate = unwindData.PayDate;
floatEvent.SwapTradeId = tradeId;
floatEvent.SwapTradeNo = td.TradeNumber;
floatEvent.EventType = (int)SwapFlowEventTypeEnum.;
floatEvent.EventReason = "交易";
unwindData.SwapTradeId = tradeId;
unwindData.StructureType = td.StructureType;
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
@@ -265,8 +268,6 @@ namespace YLErp.Modules.SwapModule
unwindData.CloseNotionalValue = unwindData.PosiNotionalValue;
if (position != null)
{
floatEvent.EventType = (int)SwapEventTypeEnum.;
floatEvent.EventReason = "交易";
floatEvent.PositionId = position.PositionId;
floatEvent.DividendIn = 0;
floatEvent.UnderlyingCode = position.UnderlyingCode;
@@ -1619,6 +1620,11 @@ namespace YLErp.Modules.SwapModule
/// <param name="eventType"></param>
private long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
{
var td = DbContext.trade.Find(unwindData.SwapTradeId);
if (td == null)
{
throw new ServiceException("未找到交易信息");
}
var flowList = new List<swap_flow_event>(unwindData.FlowEvents);
unwindData.FlowEvents.Clear();
string data = JsonConvert.SerializeObject(unwindData);
@@ -1633,7 +1639,7 @@ namespace YLErp.Modules.SwapModule
item.PayDate = unwindData.PayDate;
item.UnwindDate = unwindData.UnwindDate;
item.EventDate = unwindData.ValueDate;
item.EventId = swapEvent.id;
FillSwapFlowEvent(item, unwindData, td, eventType, swapEvent.id);
DbContext.swap_flow_event.Add(item);
}
if (!approve)
@@ -1641,8 +1647,25 @@ namespace YLErp.Modules.SwapModule
UpdateInitalPosition(flowList, unwindData, eventType);
}
DbContext.SaveChanges();
var savedFlowEvents = DbContext.swap_flow_event.Where(x => x.EventId == swapEvent.id).ToList();
foreach (var item in savedFlowEvents)
{
FillSwapFlowEvent(item, unwindData, td, eventType, swapEvent.id);
}
DbContext.SaveChanges();
return swapEvent.id;
}
private void FillSwapFlowEvent(swap_flow_event item, UnwindData unwindData, trade td, int eventType, long eventId)
{
item.EventId = eventId;
item.SwapTradeId = unwindData.SwapTradeId;
item.SwapTradeNo = td.TradeNumber;
item.EventType = eventType;
item.EventReason = string.IsNullOrWhiteSpace(item.EventReason) ? "交易" : item.EventReason;
UpdateDbOption(item);
}
/// <summary>
/// 计算平仓数据
/// </summary>
@@ -8,9 +8,9 @@ namespace YLErp.Modules.TradeModule.DocGenerateModule
/// <summary>
/// 国贸交易确认书编号生成器
/// 规则:
/// - 对客交易(OpponentRole为"方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-{当日第N笔对客交易(数字递增)}-{标的代码},从01开始
/// - 对客交易(OpponentRole为"方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-FICC-{当日第N笔对客交易(数字递增)}-{标的代码},从01开始
/// 例:GLMS-IS-20260424-01-250210IB
/// - 非对客交易(OpponentRole为"方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-{当日第N笔非对客交易使用字母递增}-{标的代码},从A开始
/// - 非对客交易(OpponentRole为"方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-FICC-{当日第N笔非对客交易使用字母递增}-{标的代码},从A开始
/// 例:GLMS-IS-20260424-A-250210IB
/// </summary>
public class GuolianContractNoGenerator
@@ -29,8 +29,8 @@ namespace YLErp.Modules.TradeModule.DocGenerateModule
trade trade,
string clientCode)
{
// 根据trade的OpponentRole判断是否为对客交易(方=对客)
var isClientTrade = trade.OpponentRole == "方";
// 根据trade的OpponentRole判断是否为对客交易(方=对客)。glms客户规定甲方=对客
var isClientTrade = trade.OpponentRole == "方";
var underlyingCode = (trade.UnderlyingCode ?? "").Replace(".", ""); // 去掉标的代码中的点号
var tradeDate = trade.TradeDate ?? DateTime.MinValue;
var tradeDateStr = tradeDate.ToString("yyyyMMdd");
+1 -3
View File
@@ -30,8 +30,6 @@
<PackageReference Include="Dapper" Version="2.0.123" />
<PackageReference Include="FluentFTP" Version="45.2.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.12.0" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.10.0" />
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
<PackageReference Include="Oracle.EntityFrameworkCore" Version="6.21.90" />
@@ -42,7 +40,7 @@
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.10.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.0" />
<PackageReference Include="Qdp.Pricing.Library.Options" Version="1.0.5" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="YLErp.Office" Version="1.1.2" />
</ItemGroup>
-13
View File
@@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Hosting.Server.Features;
using YLErp.DBModels.Consts;
using YLErp.Modules.AppModule;
using YLErp.Modules.EodModule.SettlementModule;
using YLErp.Modules.RiskEngine;
using YLErp.Modules.RiskModule;
using YLErp.Modules.SuperviseReportModule.SAC.Service;
using YLErp.Modules.TradeRiskCalcModule;
@@ -125,18 +124,6 @@ namespace YLErp.Web.App
PS.SetConfig(ConsAppConfig.YLErpWebUrlConfig, address);
}
// 预热风控引擎:加载规则与应用到内存,并预编译所有规则
Task.Run(() =>
{
try
{
RiskEngineService.GetInstance().Preload();
}
catch (Exception ex)
{
LogFactory.GetLogger("HostedTaskService").Error("[风控引擎] 启动预热失败", ex);
}
});
});
return Task.CompletedTask;
+1 -124
View File
@@ -1,125 +1,2 @@
var main = main || {};
main.formatOptions={
"trading": {
"umprice": {
"precision": 9,
"grouping": true,
"rounded": true,
"percent": false,
"minDecimals": 9,
"maxDecimals": 0
},
"umpriceP": {
"percent": true,
"precision": 2,
"grouping": false,
"rounded": true,
"minDecimals": 2,
"maxDecimals": 0
},
"umpricePR": {
"precision": 4,
"grouping": false,
"rounded": true,
"percent": false,
"minDecimals": 4,
"maxDecimals": 2
},
"tradeSinglePrice": {
"precision": 2,
"grouping": true,
"rounded": true,
"percent": false,
"minDecimals": 2,
"maxDecimals": 0
},
"premiumRateP": {
"percent": true,
"precision": 2,
"grouping": false,
"rounded": true,
"minDecimals": 2,
"maxDecimals": 0
},
"premiumRate": {
"precision": 4,
"grouping": false,
"rounded": true,
"percent": false,
"minDecimals": 4,
"maxDecimals": 2
},
"tradePrice": {
"precision": 2,
"grouping": false,
"rounded": true,
"percent": false,
"minDecimals": 2,
"maxDecimals": 0
},
"StockEqvNotional": {
"precision": 2,
"grouping": true,
"rounded": true,
"percent": false,
"minDecimals": 2,
"maxDecimals": 0
},
"notional": {
"precision": 2,
"grouping": true,
"rounded": true,
"percent": false,
"minDecimals": 2,
"maxDecimals": 0
},
"notionalP": {
"percent": true,
"precision": 2,
"grouping": false,
"rounded": true,
"minDecimals": 2,
"maxDecimals": 0
},
"volatility": {
"precision": 4,
"grouping": false,
"rounded": true,
"percent": false,
"minDecimals": 4,
"maxDecimals": 2
},
"volatilityP": {
"percent": true,
"precision": 2,
"grouping": false,
"rounded": true,
"minDecimals": 2,
"maxDecimals": 0
},
"greek": {
"precision": 2,
"grouping": false,
"rounded": true,
"percent": false,
"minDecimals": 2,
"maxDecimals": 0
},
"marginRateP": {
"percent": true,
"precision": 2,
"grouping": false,
"rounded": true,
"minDecimals": 2,
"maxDecimals": 0
},
"marginRate": {
"precision": 4,
"grouping": false,
"rounded": true,
"percent": false,
"minDecimals": 4,
"maxDecimals": 2
}
}
};
main.formatOptions = { "trading": { "umprice": { "trimTailZeros": true, "precision": 9, "grouping": true, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "umpriceP": { "trimTailZeros": true, "percent": true, "precision": 9, "grouping": false, "rounded": true, "minDecimals": 2, "maxDecimals": 9 }, "umpricePR": { "trimTailZeros": true, "precision": 9, "grouping": false, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "tradeSinglePrice": { "trimTailZeros": true, "precision": 9, "grouping": true, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "premiumRateP": { "trimTailZeros": true, "percent": true, "precision": 9, "grouping": false, "rounded": true, "minDecimals": 2, "maxDecimals": 9 }, "premiumRate": { "trimTailZeros": true, "precision": 9, "grouping": false, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "tradePrice": { "trimTailZeros": true, "precision": 9, "grouping": false, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "StockEqvNotional": { "trimTailZeros": true, "precision": 9, "grouping": true, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "notional": { "trimTailZeros": true, "precision": 9, "grouping": true, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "notionalP": { "trimTailZeros": true, "percent": true, "precision": 9, "grouping": false, "rounded": true, "minDecimals": 2, "maxDecimals": 9 }, "volatility": { "trimTailZeros": true, "precision": 9, "grouping": false, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "volatilityP": { "trimTailZeros": true, "percent": true, "precision": 9, "grouping": false, "rounded": true, "minDecimals": 2, "maxDecimals": 9 }, "greek": { "trimTailZeros": true, "precision": 9, "grouping": false, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 }, "marginRateP": { "trimTailZeros": true, "percent": true, "precision": 9, "grouping": false, "rounded": true, "minDecimals": 2, "maxDecimals": 9 }, "marginRate": { "trimTailZeros": true, "precision": 9, "grouping": false, "rounded": true, "percent": false, "minDecimals": 2, "maxDecimals": 9 } } };
+1 -19
View File
@@ -10,8 +10,7 @@
</FunctionParent>
<FunctionParent Name="交易管理" Title="交易管理">
<FunctionSub Name="交易搜索" Title="期权交易"></FunctionSub>
<FunctionSub Name="查看所有交易" Title="查看所有交易" Type="Operate"
Note="是否可以查看所有交易,没有这个权限在交易列表中只能看到自己所在的簿记账户的交易"></FunctionSub>
<FunctionSub Name="查看所有交易" Title="查看所有交易" Type="Operate" Note="是否可以查看所有交易,没有这个权限在交易列表中只能看到自己所在的簿记账户的交易" ></FunctionSub>
<FunctionSub Name="交易新增" Title="交易新增" Type="Operate" Note="是否有新增交易权限" ></FunctionSub>
<FunctionSub Name="交易编辑" Title="交易编辑" Type="Operate" Note="是否有交易编辑权限" ></FunctionSub>
<FunctionSub Name="交易删除" Title="交易删除" Type="Operate" Note="是否有交易删除权限" ></FunctionSub>
@@ -61,23 +60,6 @@
<FunctionSub Name="VaR计算" Title="VaR计算"></FunctionSub>
<FunctionSub Name="观察事件" Title="观察事件"></FunctionSub>
<FunctionSub Name="风险预警/禁止查询" Title="风险预警/禁止查询"></FunctionSub>
<FunctionSub Name="异常交易监控" Title="异常交易监控"></FunctionSub>
<FunctionSub Name="风控规则查看" Type="Operate" Note="是否有权限查询风控规则"></FunctionSub>
<FunctionSub Name="风控规则新增" Type="Operate" Note="是否有权限新增风控规则"></FunctionSub>
<FunctionSub Name="风控规则编辑" Type="Operate" Note="是否有权限编辑风控规则"></FunctionSub>
<FunctionSub Name="风控规则删除" Type="Operate" Note="是否有权限删除/批量删除风控规则"></FunctionSub>
<FunctionSub Name="风控规则启停" Type="Operate" Note="是否有权限启用/停用风控规则"></FunctionSub>
<FunctionSub Name="风控应用查看" Type="Operate" Note="是否有权限查询风控应用"></FunctionSub>
<FunctionSub Name="风控应用新增" Type="Operate" Note="是否有权限新增应用配置"></FunctionSub>
<FunctionSub Name="风控应用编辑" Type="Operate" Note="是否有权限编辑应用配置"></FunctionSub>
<FunctionSub Name="风控应用删除" Type="Operate" Note="是否有权限删除应用配置"></FunctionSub>
<FunctionSub Name="风控应用启停" Type="Operate" Note="是否有权限启用/停用/批量操作应用配置"></FunctionSub>
<FunctionSub Name="风控变量查看" Type="Operate" Note="是否有权限查询风控变量"></FunctionSub>
<FunctionSub Name="风控变量新增" Type="Operate" Note="是否有权限新增变量"></FunctionSub>
<FunctionSub Name="风控变量编辑" Type="Operate" Note="是否有权限编辑变量"></FunctionSub>
<FunctionSub Name="风控变量删除" Type="Operate" Note="是否有权限删除变量"></FunctionSub>
<FunctionSub Name="风控日志查看" Type="Operate" Note="是否有权限查询风控日志"></FunctionSub>
<FunctionSub Name="风控日志导出" Type="Operate" Note="是否有权限导出日志Excel"></FunctionSub>
</FunctionParent>
<FunctionParent Name="结算管理" Title="结算财务">
<FunctionSub Name="成交簿记" Title="成交簿记"></FunctionSub>
-1
View File
@@ -25,7 +25,6 @@
{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"},
+2 -2
View File
@@ -1,4 +1,4 @@
using YLErp.DBModels.Consts;
using YLErp.DBModels.Consts;
using YLErp.Model.Enum;
using YLErp.Modules.ClientModule;
using YLErp.Modules.ClientModule.Models;
@@ -280,7 +280,7 @@ namespace YLErp.Web
if (user.Roles.Any(n => n.Name == "权限管理员"))
{
var rightStrs = new string[] { "部门管理", "系统管理", "角色管理", "角色新增", "角色修改", "用户管理", "用户新增", "用户修改", "用户禁用", "基础缓存", "风控规则查看", "风控规则新增", "风控规则编辑", "风控规则删除", "风控规则启停", "风控应用查看", "风控应用新增", "风控应用编辑", "风控应用删除", "风控应用启停", "风控变量查看", "风控变量新增", "风控变量编辑", "风控变量删除", "风控日志查看", "风控日志导出" };
var rightStrs = new string[] { "部门管理", "系统管理", "角色管理", "角色新增", "角色修改", "用户管理", "用户新增", "用户修改", "用户禁用", "基础缓存" };
var query1 = db.Functions.Where(o => rightStrs.Contains(o.Name)).Select(n => n.Id);
var query2 = from roleFunc in db.RoleFunctions
join roleUsr in db.RoleUsers on roleFunc.RoleId equals roleUsr.RoleId
-667
View File
@@ -1,667 +0,0 @@
using Qdp.Foundation.Utilities;
using YLErp.DBModels;
using YLErp.Modules.RiskEngine;
using YLErp.Modules.RiskEngine.Dto;
namespace YLErp.Web.Controllers
{
[Route("risk-engine")]
public class RiskRuleController : BaseController
{
private readonly IYcLogger _logger = LogFactory.GetLogger("RiskRuleController");
#region Rule Management
[HttpGet("risk-rules")]
[MyAuthorize("风险控制-风控规则查看")]
public JsonResult QueryRiskRules(QueryRiskRuleReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.QueryRuleList(req);
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 = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-rules/{id}")]
[MyAuthorize("风险控制-风控规则查看")]
public JsonResult GetRiskRule(long id)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.GetRuleDetail(id);
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 = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-rules/{id}/versions")]
[MyAuthorize("风险控制-风控规则查看")]
public JsonResult GetRiskRuleVersions(long id)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.GetRuleVersions(id);
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 = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-rules")]
[MyAuthorize("风险控制-风控规则新增")]
public JsonResult CreateRiskRule([FromBody] CreateRiskRuleReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.CreateRule(req);
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 = "系统异常,请联系管理员" });
}
}
[HttpPut("risk-rules/{id}")]
[MyAuthorize("风险控制-风控规则编辑")]
public JsonResult UpdateRiskRule(long id, [FromBody] UpdateRiskRuleReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.UpdateRule(id, req);
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 = "系统异常,请联系管理员" });
}
}
[HttpDelete("risk-rules/{id}")]
[MyAuthorize("风险控制-风控规则删除")]
public JsonResult DeleteRiskRule(long id)
{
try
{
var service = new RiskRuleService(CurUser);
service.DeleteRule(id);
return Json(new { success = true });
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "删除风控规则");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
[HttpDelete("risk-rules/batch")]
[MyAuthorize("风险控制-风控规则删除")]
public JsonResult BatchDeleteRiskRules([FromBody] List<long> ids)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.BatchDeleteRules(ids);
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 = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-rules/{id}/enable")]
[MyAuthorize("风险控制-风控规则启停")]
public JsonResult EnableRiskRule(long id)
{
try
{
var service = new RiskRuleService(CurUser);
service.EnableRule(id);
return Json(new { success = true });
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "启用风控规则");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-rules/{id}/disable")]
[MyAuthorize("风险控制-风控规则启停")]
public JsonResult DisableRiskRule(long id)
{
try
{
var service = new RiskRuleService(CurUser);
service.DisableRule(id);
return Json(new { success = true });
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "停用风控规则");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-rules/{id}/applications")]
[MyAuthorize("风险控制-风控规则查看")]
public JsonResult GetRuleApplications(long id)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.GetRuleApplications(id);
return Json(new { success = true, data = new { rows = result, total = result.Count, records = result.Count, page = 1 } });
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "查询规则关联应用列表");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-rules/list")]
[MyAuthorize("风险控制-风控规则查看")]
public JsonResult GetAllRiskRules()
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.GetAllRuleList();
return Json(new { success = true, data = new { rows = result, total = result.Count, records = result.Count, page = 1 } });
}
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
[HttpGet("risk-applications")]
[MyAuthorize("风险控制-风控应用查看")]
public JsonResult QueryRiskApplications(QueryRiskApplicationReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.QueryApplicationList(req);
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 = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-applications/{id}")]
[MyAuthorize("风险控制-风控应用查看")]
public JsonResult GetRiskApplication(long id)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.GetApplicationDetail(id);
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 = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-applications")]
[MyAuthorize("风险控制-风控应用新增")]
public JsonResult CreateRiskApplication([FromBody] CreateRiskApplicationReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.CreateApplication(req);
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 = "系统异常,请联系管理员" });
}
}
[HttpPut("risk-applications/{id}")]
[MyAuthorize("风险控制-风控应用编辑")]
public JsonResult UpdateRiskApplication(long id, [FromBody] UpdateRiskApplicationReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.UpdateApplication(id, req);
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 = "系统异常,请联系管理员" });
}
}
[HttpDelete("risk-applications/{id}")]
[MyAuthorize("风险控制-风控应用删除")]
public JsonResult DeleteRiskApplication(long id)
{
try
{
var service = new RiskRuleService(CurUser);
service.DeleteApplication(id);
return Json(new { success = true });
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "删除应用配置");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-applications/{id}/enable")]
[MyAuthorize("风险控制-风控应用启停")]
public JsonResult EnableRiskApplication(long id)
{
try
{
var service = new RiskRuleService(CurUser);
service.EnableApplication(id);
return Json(new { success = true });
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "启用应用配置");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-applications/{id}/disable")]
[MyAuthorize("风险控制-风控应用启停")]
public JsonResult DisableRiskApplication(long id)
{
try
{
var service = new RiskRuleService(CurUser);
service.DisableApplication(id);
return Json(new { success = true });
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "停用应用配置");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-applications/batch-enable")]
[MyAuthorize("风险控制-风控应用启停")]
public JsonResult BatchEnableRiskApplications([FromBody] List<long> ids)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.BatchEnableApplications(ids);
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 = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-applications/batch-disable")]
[MyAuthorize("风险控制-风控应用启停")]
public JsonResult BatchDisableRiskApplications([FromBody] List<long> ids)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.BatchDisableApplications(ids);
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 Variable Management
[HttpGet("risk-variables")]
[MyAuthorize("风险控制-风控变量查看")]
public JsonResult QueryRiskVariables(QueryRiskVariableReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.QueryVariableList(req);
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 = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-variables/{id}")]
[MyAuthorize("风险控制-风控变量查看")]
public JsonResult GetRiskVariable(long id)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.GetVariableDetail(id);
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 = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-variables/list")]
[MyAuthorize("风险控制-风控变量查看")]
public JsonResult GetAllRiskVariables()
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.GetAllVariableList();
return Json(new { success = true, data = new { rows = result, total = result.Count, records = result.Count, page = 1 } });
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "查询全部变量列表");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
[HttpPost("risk-variables")]
[MyAuthorize("风险控制-风控变量新增")]
public JsonResult CreateRiskVariable([FromBody] CreateRiskVariableReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.CreateVariable(req);
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 = "系统异常,请联系管理员" });
}
}
[HttpPut("risk-variables/{id}")]
[MyAuthorize("风险控制-风控变量编辑")]
public JsonResult UpdateRiskVariable(long id, [FromBody] UpdateRiskVariableReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.UpdateVariable(id, req);
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 = "系统异常,请联系管理员" });
}
}
[HttpDelete("risk-variables/{id}")]
[MyAuthorize("风险控制-风控变量删除")]
public JsonResult DeleteRiskVariable(long id)
{
try
{
var service = new RiskRuleService(CurUser);
service.DeleteVariable(id);
return Json(new { success = true });
}
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 Trade Types
[HttpGet("trade-types")]
[MyAuthorize("风险控制-风控应用查看")]
public JsonResult GetTradeTypes()
{
try
{
var rows = ConsTrade.AllTradeTypes.Select(t => new { label = t, value = t }).ToList();
var total = rows.Count;
return Json(new {
success = true,
data = new {
page = 1,
records = total,
rows = rows,
total = total
}
});
}
catch (Exception ex)
{
_logger.Error(ex, "查询合约类型列表");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
#endregion
#region Audit Log
[HttpGet("risk-audit-logs")]
[MyAuthorize("风险控制-风控日志查看")]
public JsonResult QueryRiskAuditLogs(QueryRiskAuditLogReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.QueryAuditLogs(req);
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 = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-audit-logs/{id}")]
[MyAuthorize("风险控制-风控日志查看")]
public JsonResult GetRiskAuditLogDetail(long id)
{
try
{
var service = new RiskRuleService(CurUser);
var result = service.GetAuditLogDetail(id);
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 = "系统异常,请联系管理员" });
}
}
[HttpGet("risk-audit-logs/export")]
[MyAuthorize("风险控制-风控日志导出")]
public IActionResult ExportRiskAuditLogs(QueryRiskAuditLogReq req)
{
try
{
var service = new RiskRuleService(CurUser);
var bytes = service.ExportAuditLogs(req);
return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "风控操作日志.xlsx");
}
catch (ServiceException ex)
{
return Json(new { success = false, message = ex.Message });
}
catch (Exception ex)
{
_logger.Error(ex, "导出风控操作日志");
return Json(new { success = false, message = "系统异常,请联系管理员" });
}
}
#endregion
}
}
+1 -1
View File
@@ -204,7 +204,7 @@
<span title="使用系统标的价格" class="glyphicon glyphicon-refresh"></span>
</a>
</td>
<td>{{floatPosition.Quantity}}</td>
<td>{{deal.CloseQty}}</td>
<td>
<vue-number-input v-model="floatPosition.TradingFee" v-bind:format="inputFormatEqvNotional" v-on:input="changeTradingFee"></vue-number-input>
<div class="bubble-box">我方{{floatPosition.PayDirection==1?"支付":"收取"}}交易费用</div>
+11 -14
View File
@@ -7,15 +7,15 @@
},
"AllowedHosts": "*",
"ConnectionStrings": {
"ylcms": "server=10.250.202.35;uid=onederiv@OneDeriv#wx_dtpp_test;pooling=true;port=2883;pwd=Onederiv@2026;database=yltrs_ylcms;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
"yladmin": "server=10.250.202.35;uid=onederiv@OneDeriv#wx_dtpp_test;pooling=true;port=2883;pwd=Onederiv@2026;database=yltrs_admin;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
"ylclient": "server=10.250.202.35;uid=onederiv@OneDeriv#wx_dtpp_test;pooling=true;port=2883;pwd=Onederiv@2026;database=yltrs_client;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
"bondoms": "server=10.250.202.35;uid=onederiv@OneDeriv#wx_dtpp_test;pooling=true;port=2883;pwd=Onederiv@2026;database=bond_oms;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;"
"ylcms": "server=192.168.2.96;uid=DBAdmin;pooling=true;port=3306;pwd=YieldChain$$2025;database=glms_yltrs_ylcms;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
"yladmin": "server=192.168.2.96;uid=DBAdmin;pooling=true;port=3306;pwd=YieldChain$$2025;database=glms_yltrs_admin;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
"ylclient": "server=192.168.2.96;uid=DBAdmin;pooling=true;port=3306;pwd=YieldChain$$2025;database=glms_yltrs_client;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
"bondoms": "server=192.168.2.96;uid=DBAdmin;pooling=true;port=3306;pwd=YieldChain$$2025;database=glms_bond_oms;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;"
},
"AppSettings": {
"VirtualPathRoot": "",
"UseRightAligned": "",
"PluginFolder": "D:\\workspace\\onederiv\\trs\\Plugins\\YLErp.Plugins.GuoLian\\obj\\Debug\\net6.0" //
"PluginFolder": "D:\\workspace\\glms\\zszq-trs\\Plugins\\YLErp.Plugins.GuoLian\\obj\\Debug\\net6.0"
},
"LibreOffice": {
"ExePath": "",
@@ -75,15 +75,12 @@
"SendKafakaCron": "10 0 * * *",
"MaxThreadPercent": 0.6, //60%线
"oa_confg": {
"BaseUrl": "http://10.250.202.40:13590",
"SubUrl": "/gateway/oaflow/createOaFlow",
"QueryUrl": "/gateway/oaflow/getRequestData",
"DownloadFileUrl": "/gateway/oaflow/downloadSingleFile",
"SystemToken": "QIkox8sv3rVLtiT1wmI4Dp8wSjX9D84Glkp121vRino9fLobWnDiy8ppmqJcv1676TYuxZsGrYelx0whRCMaPyums2VnzIYrpqCFf5tyc3/x22Elf25xgRmd5pH8tLcnPuVOjMrUf0GsoytQCGFkhnrPajhsKq/mSwYQ4OA=", //OAkey
"SystemName": "onederiv",
"IsNextFlow": 1, // 0
"FlowId": 779115,
"UploadUrl": "http://10.250.202.40:13590/gateway/oaflow/uploadDoc"
"BaseUrl": "http://ip:port",
"SubUrl": "",
"UploadUrl": "/api/oa/upload", // OA
"SystemToken": "your-system-token", // OAToken
"SystemName": "OTC_TRADE", //
"IsNextFlow": 0
},
"GeneralSSO": {
"enable": true, //SSO