From 8e0e7791338c736a055f3791a89a2fc5c26c4f25 Mon Sep 17 00:00:00 2001 From: ruisu Date: Tue, 14 Jul 2026 17:18:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=A0=B9=E6=8D=AEai=20merge=E6=84=8F?= =?UTF-8?q?=E8=A7=81=E4=BF=AE=E6=94=B9=EF=BC=9AdbContext=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E4=B8=8E=E5=8D=95=E4=BE=8BriskEngienService=E7=9A=84=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=EF=BC=8C=E5=8F=8D=E5=B0=84=E4=BB=A3=E7=A0=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E3=80=82=20=E6=94=AF=E6=8C=81=E8=80=81=E9=A3=8E?= =?UTF-8?q?=E6=8E=A7Error=E6=83=85=E5=86=B5=E4=B8=8B=E7=9A=84=E4=BA=8C?= =?UTF-8?q?=E6=AC=A1=E7=89=B9=E6=89=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Framework/YLErp.Core/DBModels/QuotaTrial.cs | 3 ++ YLErpDAL/Model/QuotaTrial.cs | 3 +- .../Modules/RiskEngine/RiskEngineService.cs | 28 +++++++++---------- .../Modules/RiskModule/QuotaMonitorService.cs | 14 ++++++++-- YLErpWeb/Controllers/tradeController.cs | 2 +- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/Framework/YLErp.Core/DBModels/QuotaTrial.cs b/Framework/YLErp.Core/DBModels/QuotaTrial.cs index c4e95df1..650af814 100644 --- a/Framework/YLErp.Core/DBModels/QuotaTrial.cs +++ b/Framework/YLErp.Core/DBModels/QuotaTrial.cs @@ -66,5 +66,8 @@ namespace YLErp.DBModels [NotMapped] public List ApprovalRuleIds { get; set; } = new List(); + + [NotMapped] + public QuotaTrialStatusEnum? OldRiskTrialStatus { get; set; } } } diff --git a/YLErpDAL/Model/QuotaTrial.cs b/YLErpDAL/Model/QuotaTrial.cs index 04d16d2b..60d8e7dd 100644 --- a/YLErpDAL/Model/QuotaTrial.cs +++ b/YLErpDAL/Model/QuotaTrial.cs @@ -1,4 +1,4 @@ -using BaseOUDAL; +using BaseOUDAL; using System.ComponentModel.DataAnnotations.Schema; using YLErp.Enums; @@ -33,6 +33,7 @@ namespace YLErp.Model this.AvailableForClient = obj.AvailableForClient; this.Remark = obj.Remark; this.TrialSource = obj.TrialSource; + this.OldRiskTrialStatus = obj.OldRiskTrialStatus; this.OptId = obj.OptId; this.OptName = obj.OptName; this.OptDate = obj.OptDate; diff --git a/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs b/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs index efd8b91d..3de1394c 100644 --- a/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs +++ b/YLErpDAL/Modules/RiskEngine/RiskEngineService.cs @@ -519,7 +519,9 @@ namespace YLErp.Modules.RiskEngine /// private List LoadRulesFromDb() { - var rules = DbContext.glms_risk_rule + using var dbContext = DbContextFactory.GetYLDbContext(); + + var rules = dbContext.glms_risk_rule .AsNoTracking() .OrderByDescending(r => r.UpdateDate ?? r.OptDate) .Select(r => new RiskRule @@ -736,8 +738,8 @@ namespace YLErp.Modules.RiskEngine //{ // Id = 1000014, // RuleName = "非债券类价格偏离(本地)", - // RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取 PosiGrossPrice,对应普通收益互换页面填写的期初标的交割全价%,库内为 1 左右原值;通过 DbContext.eod_stock_price / eod_commodity_future_price 按当前交易标的和交易日前日期取上一日收盘价 ClosePrice。计算逻辑:按 ABS(PosiGrossPrice×100-ClosePrice) 计算绝对价差,价差大于 5 时触发审批。", - // RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId).PosiGrossPrice * 100m - Convert.ToDecimal((DbContext.eod_stock_price.Where(e => e.UnderlyingCode == DbContext.trade.First(t => t.id == TradeId).UnderlyingCode && e.ValueDate < DbContext.trade.First(t => t.id == TradeId).TradeDate.Value.Date).OrderByDescending(e => e.ValueDate).Select(e => (double?)e.ClosePrice).FirstOrDefault() ?? DbContext.eod_commodity_future_price.Where(e => e.UnderlyingCode == DbContext.trade.First(t => t.id == TradeId).UnderlyingCode && e.ValueDate < DbContext.trade.First(t => t.id == TradeId).TradeDate.Value.Date).OrderByDescending(e => e.ValueDate).Select(e => (double?)e.ClosePrice).FirstOrDefault()).Value)) > 5m", + // RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取 PosiGrossPrice,对应普通收益互换页面填写的期初标的价格,库内为 1 左右原值;通过 DbContext.eod_commodity_future_price 按当前交易标的和交易日前日期取上一日收盘价 ClosePrice。计算逻辑:按 ABS(PosiGrossPrice×100-ClosePrice) 计算绝对价差,价差大于 5 时触发审批。", + // RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId).PosiGrossPrice * 100m - Convert.ToDecimal(DbContext.eod_commodity_future_price.Where(e => e.UnderlyingCode == DbContext.trade.First(t => t.id == TradeId).UnderlyingCode && e.ValueDate < DbContext.trade.First(t => t.id == TradeId).TradeDate.Value.Date).OrderByDescending(e => e.ValueDate).First().ClosePrice)) > 5m", // Version = 1, // Status = RiskRuleStatus.Active, // OptId = 0, @@ -752,8 +754,8 @@ namespace YLErp.Modules.RiskEngine //{ // Id = 1000015, // RuleName = "单一交易对手累计标的数量超阈值(本地)", - // RuleText = "取值字段:通过 DbContext.trade 按 TradeId 取当前交易对手 ClientId,再查询同一交易对手存续/审批中交易的去重标的数量。计算逻辑:同一交易对手累计标的数量超过 10 个时触发审批。", - // RuleExpr = "DbContext.trade.Where(t => t.ValidState != \"InValid\" && t.ClientId == DbContext.trade.First(x => x.id == TradeId).ClientId && t.ParentTradeId == 0 && (ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus) || t.TradeStatus == \"审批中\")).Select(t => t.UnderlyingId).Distinct().Count() > 10", + // RuleText = "取值字段:通过 DbContext.trade 按 TradeId 取当前交易对手 ClientId,再查询同一交易对手存续/审批中交易对应的 swap_position.UnderlyingCode 去重数量。计算逻辑:同一交易对手累计标的数量超过 10 个时触发审批。", + // RuleExpr= DbContext.swap_position.Where(p => !string.IsNullOrEmpty(p.UnderlyingCode) && p.IsInitial && !p.Invalid && DbContext.trade.Any(t => t.id == p.SwapTradeId && t.ValidState != "InValid" && t.ClientId == DbContext.trade.First(x => x.id == TradeId).ClientId && t.ParentTradeId == 0 && (ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus) || t.TradeStatus == "审批中"))).Select(p => p.UnderlyingCode).Distinct().Count() > 10 // Version = 1, // Status = RiskRuleStatus.Active, // OptId = 0, @@ -768,8 +770,8 @@ namespace YLErp.Modules.RiskEngine //{ // Id = 1000016, // RuleName = "多头支付固定端利率偏离(本地)", - // RuleText = "取值字段:通过 DbContext.trade 按 TradeId 取当前交易的 FixedRate,并结合 BuySell 判断多头方向。计算逻辑:当 BuySell 表示多头且 FixedRate 有值时,先以 2.5% 作为本地测试基准,若 ABS(FixedRate-0.025)/0.025×100% 大于 5%,则触发审批。", - // RuleExpr = "DbContext.trade.First(t => t.id == TradeId).BuySell == \"Buy\" && DbContext.trade.First(t => t.id == TradeId).FixedRate.HasValue && Math.Abs((DbContext.trade.First(t => t.id == TradeId).FixedRate.Value - 0.025d) / 0.025d) * 100d > 5d", + // RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取利息端支付方向记录的 InterestRateDefault,对应互换交易-利息端-利率文本框数值;通过 DbContext.eod_commodity_future_price 按 FutureContractId=FR007(模型属性 UnderlyingCode)和交易日前日期取最近一条 ClosePrice。计算逻辑:按 ABS(InterestRateDefault-FR007) 计算利率差,差值小于 5% 时触发审批。", + // RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId && p.InterestDirection == 2).InterestRateDefault - Convert.ToDecimal(DbContext.eod_commodity_future_price.Where(e => e.UnderlyingCode == "FR007" && e.ValueDate < DbContext.trade.First(t => t.id == TradeId).TradeDate.Value.Date).OrderByDescending(e => e.ValueDate).First().ClosePrice)) < 0.05m", // Version = 1, // Status = RiskRuleStatus.Active, // OptId = 0, @@ -851,7 +853,9 @@ namespace YLErp.Modules.RiskEngine private List LoadApplicationsFromDb() { - var applications = DbContext.glms_risk_rule_application + using var dbContext = DbContextFactory.GetYLDbContext(); + + var applications = dbContext.glms_risk_rule_application .AsNoTracking() .OrderByDescending(a => a.UpdateDate ?? a.OptDate) .Select(a => new RiskRuleApplication @@ -992,22 +996,18 @@ namespace YLErp.Modules.RiskEngine /// /// 读取交易的资产簿账户ID。 - /// 当前先按 BookId 取值;如果后续真实字段不是 BookId,再统一调整这里即可。 /// private object GetTradeAssetBookId(YLErp.DBModels.trade trade) { - var property = trade.GetType().GetProperty("BookId"); - return property?.GetValue(trade); + return trade.AssetId; } /// /// 读取交易的标的类型。 - /// 当前先按 UnderlyingInstrumentType 取值;如果后续真实字段名不同,再统一调整这里即可。 /// private object GetTradeUnderlyingType(YLErp.DBModels.trade trade) { - var property = trade.GetType().GetProperty("UnderlyingInstrumentType"); - return property?.GetValue(trade); + return trade.UnderlyingInstrumentType; } } } diff --git a/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs b/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs index a0817047..f2449805 100644 --- a/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs +++ b/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs @@ -4470,11 +4470,16 @@ namespace YLErp.Modules.RiskModule public bool QuotaCheck(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable ignoreRiskRuleIds = null) { - if (res == null || res.Trade == null) + if (res == null) { throw new ArgumentNullException(nameof(res)); } + if (res.Trade == null) + { + throw new ArgumentNullException(nameof(res.Trade)); + } + var tradeId = (res.Trade.ParentTradeId == 0 || res.Trade.IsGroup != 0) ? res.Trade.id : res.Trade.ParentTradeId; var tradeNumber = DbContext.trade.Where(O => O.id == tradeId).Select(O => O.TradeNumber).FirstOrDefault(); var trialService = new QuotaMonitorService(this); @@ -4518,8 +4523,9 @@ namespace YLErp.Modules.RiskModule var isRiskApprovalWarning = quotaObj.TrialStatus == QuotaTrialStatusEnum.RiskWarning && !string.IsNullOrWhiteSpace(quotaObj.RiskWarningDetails) && (quotaObj.ApprovalRuleIds?.Any() ?? false); - //当前为老风控特批,不是新风控需审批 - res.OldRiskNeedSpecialApproval = quotaObj.TrialStatus == QuotaTrialStatusEnum.Warning; + //老风控试算不通过时,延用master逻辑进入交易特批/二次处理;新风控需审批由RiskWarning单独处理 + res.OldRiskNeedSpecialApproval = quotaObj.OldRiskTrialStatus.HasValue + && quotaObj.OldRiskTrialStatus.Value != QuotaTrialStatusEnum.Success; if (isRiskApprovalWarning) { var currentApprovalRuleIds = quotaObj.ApprovalRuleIds ?? new List(); @@ -5148,6 +5154,7 @@ namespace YLErp.Modules.RiskModule } } } + if (quotaWarningStatus && riskWarningStatus && fundStatus && quotaStatus) { result.TrialStatus = QuotaTrialStatusEnum.Success; @@ -5172,6 +5179,7 @@ namespace YLErp.Modules.RiskModule result.QuotaWarningDetails = quotaWarningMsg ?? ""; result.AvailableForClient = availableMsg ?? ""; result.TrialSource = trialSource; + result.OldRiskTrialStatus = result.TrialStatus; _logger.Info($"[限额试算] 试算完成 - tradeId: {tradeId}, TrialStatus: {result.TrialStatus}, FundCheck: {fundStatus}, QuotaCheck: {quotaStatus}"); // ===== 新风控引擎接入点(设计文档 4.6.1) ===== diff --git a/YLErpWeb/Controllers/tradeController.cs b/YLErpWeb/Controllers/tradeController.cs index 34c01daa..7a380c0c 100644 --- a/YLErpWeb/Controllers/tradeController.cs +++ b/YLErpWeb/Controllers/tradeController.cs @@ -2513,7 +2513,7 @@ namespace YLErp.Web.Controllers { if (result.OldRiskNeedSpecialApproval) { - return JsonSuccessData(new { proccessType = "AdditionalProcessing", source = "OldRiskQuotaTrialError", type = tradeBLL.LackOfMoney, TrialDataId = result.TrialDataId, message = result.errorMsg, typecode = result.type }); + return JsonSuccessData(new { proccessType = "AdditionalProcessing", source = "OldRiskQuotaTrialError", type = PS.Config.ErpElement.Company == Configuration.CompanyEnum.天风 || config.SpecialOperateForTrade == 1 ? tradeBLL.LackOfMoney : "", TrialDataId = result.TrialDataId, message = result.errorMsg, typecode = result.type }); } return JsonSuccessData(new { proccessType = "QuotaTrialError", TrialDataId = result.TrialDataId, message = result.errorMsg, typecode = result.type }); }