Merge branch 'feature/p132_74-risk-engine' of https://gitee.glmszq.com/gsty/onederiv/trs into feature/p132_74-risk-engine
This commit is contained in:
@@ -4465,12 +4465,20 @@ namespace YLErp.Modules.RiskModule
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool QuotaCheck(ref TradeOpenResult res)
|
||||
/// <summary>
|
||||
/// 交易确认风控检查入口:执行完整风控试算(老风控 + 新风控),并把试算结果转换为交易确认结果。
|
||||
/// 返回 true 表示确认流程可以继续;返回 false 表示需要阻断、提示或审批。
|
||||
/// </summary>
|
||||
public bool CheckTradeConfirmRisk(ref TradeOpenResult res)
|
||||
{
|
||||
return QuotaCheck(ref res, false);
|
||||
return CheckTradeConfirmRisk(ref res, false);
|
||||
}
|
||||
|
||||
public bool QuotaCheck(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable<string> ignoreRiskRuleIds = null)
|
||||
/// <summary>
|
||||
/// 交易确认风控检查入口:执行完整风控试算(老风控 + 新风控)。
|
||||
/// ignoreRiskWarning/ignoreRiskRuleIds 只用于新风控“需审批”规则的二次确认,不影响老风控结果。
|
||||
/// </summary>
|
||||
public bool CheckTradeConfirmRisk(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable<string> ignoreRiskRuleIds = null)
|
||||
{
|
||||
if (res == null)
|
||||
{
|
||||
@@ -4483,9 +4491,39 @@ namespace YLErp.Modules.RiskModule
|
||||
}
|
||||
|
||||
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);
|
||||
var quotaObj = trialService.QuotaCheck(tradeId, 1);
|
||||
var quotaObj = trialService.RunQuotaTrial(tradeId, 1);
|
||||
return ApplyQuotaCheckResult(ref res, quotaObj, ignoreRiskWarning, ignoreRiskRuleIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 交易确认新风控检查入口:只执行新风控试算。
|
||||
/// 用于老风控已被特批放行(ignoreMoneyCheck = true)的场景,避免老风控特批误跳过新风控。
|
||||
/// </summary>
|
||||
public bool CheckTradeConfirmNewRisk(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable<string> ignoreRiskRuleIds = 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 trialService = new QuotaMonitorService(this);
|
||||
var quotaObj = trialService.RunNewRiskTrial(tradeId, 1);
|
||||
return ApplyQuotaCheckResult(ref res, quotaObj, ignoreRiskWarning, ignoreRiskRuleIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将风控试算结果转换为交易确认结果。
|
||||
/// 这里不重新执行风控,只负责设置 RetCode、ErrorMsg、TipMsg、TrialDataId 等返回给确认流程的字段。
|
||||
/// </summary>
|
||||
private bool ApplyQuotaCheckResult(ref TradeOpenResult res, QuotaTrial quotaObj, bool ignoreRiskWarning, IEnumerable<string> ignoreRiskRuleIds = null)
|
||||
{
|
||||
if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success)//这次计算结果是通过,直接通过
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(quotaObj.AvailableForClient)) // 返回用户资金情况
|
||||
@@ -5026,12 +5064,13 @@ namespace YLErp.Modules.RiskModule
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 交易试算
|
||||
/// 完整风控试算:先执行老风控资金/限额试算,再叠加新风控试算结果。
|
||||
/// 后续老风控下线时,调用方可以保留该入口,内部改为直接调用 RunNewRiskTrial。
|
||||
/// </summary>
|
||||
/// <param name="tradeId">交易Id,结构化交易请传主交易Id</param>
|
||||
/// <param name="trialSource">试算来源,0:手动点击试算按钮;1:确认成交时自动调用;</param>
|
||||
/// <returns></returns>
|
||||
public QuotaTrial QuotaCheck(int tradeId, int trialSource)
|
||||
public QuotaTrial RunQuotaTrial(int tradeId, int trialSource)
|
||||
{
|
||||
_logger.Info($"[限额试算] 开始执行 - tradeId: {tradeId}, trialSource: {trialSource}");
|
||||
var result = new QuotaTrial();
|
||||
@@ -5076,8 +5115,20 @@ namespace YLErp.Modules.RiskModule
|
||||
tradeList = tradeList.Where(O => !string.IsNullOrEmpty(O.UnderlyingCode) && O.TradeType != "结构化交易" && O.TradeType != "现金流交易").ToList();
|
||||
if (tradeList.Count == 0)
|
||||
{
|
||||
// 老风控没有可试算的标的时,老逻辑认为试算通过;这里仍需继续执行新风控,避免普通确认漏掉新风控检查。
|
||||
//("当前交易不需要试算");
|
||||
return new QuotaTrial() { TrialStatus = QuotaTrialStatusEnum.Success };
|
||||
result.TrialStatus = QuotaTrialStatusEnum.Success;
|
||||
result.TradeId = tradeObj.id;
|
||||
result.TradeNumber = tradeObj.TradeNumber ?? "";
|
||||
result.ClientName = tradeObj.ClientName ?? "";
|
||||
result.FundCheckDetails = fundMsg ?? "";
|
||||
result.RiskWarningDetails = riskWarningMsg ?? "";
|
||||
result.AvailableForClient = availableMsg ?? "";
|
||||
result.TrialSource = trialSource;
|
||||
result.OldRiskTrialStatus = result.TrialStatus;
|
||||
result = ApplyNewRiskTrial(tradeId, result);
|
||||
SaveQuotaTrial(result);
|
||||
return result;
|
||||
}
|
||||
string quotaWarningMsg = "", quotaMsg = "";
|
||||
bool quotaWarningStatus = true, quotaStatus = true;
|
||||
@@ -5243,8 +5294,42 @@ namespace YLErp.Modules.RiskModule
|
||||
result.AvailableForClient = availableMsg ?? "";
|
||||
result.TrialSource = trialSource;
|
||||
result.OldRiskTrialStatus = result.TrialStatus;
|
||||
_logger.Info($"[限额试算] 试算完成 - tradeId: {tradeId}, TrialStatus: {result.TrialStatus}, FundCheck: {fundStatus}, QuotaCheck: {quotaStatus}");
|
||||
// 保留老风控原始状态,随后叠加新风控结果;这样二次特批时可以区分是老风控失败还是新风控需审批。
|
||||
result = ApplyNewRiskTrial(tradeId, result);
|
||||
SaveQuotaTrial(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新风控独立试算:不执行老风控资金/限额检查,只调用新风控引擎。
|
||||
/// 用于老风控特批后的确认流程,也可作为后续老风控下线后的主试算入口。
|
||||
/// </summary>
|
||||
public QuotaTrial RunNewRiskTrial(int tradeId, int trialSource)
|
||||
{
|
||||
var result = new QuotaTrial
|
||||
{
|
||||
TradeId = tradeId,
|
||||
TrialSource = trialSource,
|
||||
TrialStatus = QuotaTrialStatusEnum.Success,
|
||||
OldRiskTrialStatus = QuotaTrialStatusEnum.Success
|
||||
};
|
||||
var tradeObj = DbContext.trade.AsNoTracking().Where(O => O.id == tradeId).FirstOrDefault();
|
||||
if (tradeObj != null)
|
||||
{
|
||||
result.TradeNumber = tradeObj.TradeNumber ?? "";
|
||||
result.ClientName = tradeObj.ClientName ?? "";
|
||||
}
|
||||
result = ApplyNewRiskTrial(tradeId, result);
|
||||
SaveQuotaTrial(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将新风控引擎结果合并到 QuotaTrial。
|
||||
/// 如果传入的是完整试算结果,则是在老风控结果基础上叠加新风控;如果传入的是新建结果,则表示只跑新风控。
|
||||
/// </summary>
|
||||
private QuotaTrial ApplyNewRiskTrial(int tradeId, QuotaTrial result)
|
||||
{
|
||||
// ===== 新风控引擎接入点(设计文档 4.6.1) =====
|
||||
try
|
||||
{
|
||||
@@ -5318,7 +5403,6 @@ namespace YLErp.Modules.RiskModule
|
||||
}
|
||||
// ===== 新风控引擎接入结束 =====
|
||||
|
||||
SaveQuotaTrial(result);
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
@@ -203,26 +203,38 @@ namespace YLErp.Modules.TradeModule.DealModule
|
||||
result.ErrorMsg = $"未找到客户信息:{td.ClientName}";
|
||||
return result;
|
||||
}
|
||||
if (!ignoreMoneyCheck && !continueQuotaCheck)
|
||||
if (!continueQuotaCheck)
|
||||
{
|
||||
result = CheckTradeConfirmRule(result, td, isSkipApproval);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result.ErrorMsg))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = CheckClientRating(result, client, td);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result.ErrorMsg))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var trialService = new QuotaMonitorService(this);
|
||||
if (!trialService.QuotaCheck(ref result, ignoreRiskWarning, ignoreRiskRuleIds))
|
||||
if (ignoreMoneyCheck)
|
||||
{
|
||||
return result;
|
||||
// ignoreMoneyCheck 表示老风控资金/限额检查已被特批放行,只跳过老风控;新风控仍需独立执行。
|
||||
if (!trialService.CheckTradeConfirmNewRisk(ref result, ignoreRiskWarning, ignoreRiskRuleIds))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 正常确认流程:先执行原有交易确认规则和客户评级检查,再执行完整风控(老风控 + 新风控)。
|
||||
result = CheckTradeConfirmRule(result, td, isSkipApproval);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result.ErrorMsg))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = CheckClientRating(result, client, td);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result.ErrorMsg))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!trialService.CheckTradeConfirmRisk(ref result, ignoreRiskWarning, ignoreRiskRuleIds))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
var history = "确认交易";
|
||||
|
||||
@@ -8428,7 +8428,7 @@ namespace YLErp.Web.Controllers
|
||||
/// <returns></returns>
|
||||
public ActionResult quotaTrial(int id, int trialSource = 0)
|
||||
{
|
||||
var quotaTrial = new QuotaMonitorService(CurUser).QuotaCheck(id, trialSource);
|
||||
var quotaTrial = new QuotaMonitorService(CurUser).RunQuotaTrial(id, trialSource);
|
||||
return View(quotaTrial);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user