feat:根据ai merge意见修改:dbContext取消与单例riskEngienService的绑定,反射代码修改。

支持老风控Error情况下的二次特批
This commit is contained in:
ruisu
2026-07-14 17:18:33 +08:00
parent 0e90c6f0cc
commit 8e0e779133
5 changed files with 31 additions and 19 deletions
@@ -66,5 +66,8 @@ namespace YLErp.DBModels
[NotMapped]
public List<string> ApprovalRuleIds { get; set; } = new List<string>();
[NotMapped]
public QuotaTrialStatusEnum? OldRiskTrialStatus { get; set; }
}
}
+2 -1
View File
@@ -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;
@@ -519,7 +519,9 @@ namespace YLErp.Modules.RiskEngine
/// </summary>
private List<RiskRule> 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<RiskRuleApplication> 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
/// <summary>
/// 读取交易的资产簿账户ID。
/// 当前先按 BookId 取值;如果后续真实字段不是 BookId,再统一调整这里即可。
/// </summary>
private object GetTradeAssetBookId(YLErp.DBModels.trade trade)
{
var property = trade.GetType().GetProperty("BookId");
return property?.GetValue(trade);
return trade.AssetId;
}
/// <summary>
/// 读取交易的标的类型。
/// 当前先按 UnderlyingInstrumentType 取值;如果后续真实字段名不同,再统一调整这里即可。
/// </summary>
private object GetTradeUnderlyingType(YLErp.DBModels.trade trade)
{
var property = trade.GetType().GetProperty("UnderlyingInstrumentType");
return property?.GetValue(trade);
return trade.UnderlyingInstrumentType;
}
}
}
@@ -4470,11 +4470,16 @@ namespace YLErp.Modules.RiskModule
public bool QuotaCheck(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable<string> 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<string>();
@@ -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) =====
+1 -1
View File
@@ -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 });
}