diff --git a/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs b/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs
index eab45af4..e2315e10 100644
--- a/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs
+++ b/YLErpDAL/Modules/RiskModule/QuotaMonitorService.cs
@@ -4465,12 +4465,20 @@ namespace YLErp.Modules.RiskModule
return result;
}
- public bool QuotaCheck(ref TradeOpenResult res)
+ ///
+ /// 交易确认风控检查入口:执行完整风控试算(老风控 + 新风控),并把试算结果转换为交易确认结果。
+ /// 返回 true 表示确认流程可以继续;返回 false 表示需要阻断、提示或审批。
+ ///
+ 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 ignoreRiskRuleIds = null)
+ ///
+ /// 交易确认风控检查入口:执行完整风控试算(老风控 + 新风控)。
+ /// ignoreRiskWarning/ignoreRiskRuleIds 只用于新风控“需审批”规则的二次确认,不影响老风控结果。
+ ///
+ public bool CheckTradeConfirmRisk(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable 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);
+ }
+
+ ///
+ /// 交易确认新风控检查入口:只执行新风控试算。
+ /// 用于老风控已被特批放行(ignoreMoneyCheck = true)的场景,避免老风控特批误跳过新风控。
+ ///
+ public bool CheckTradeConfirmNewRisk(ref TradeOpenResult res, bool ignoreRiskWarning, IEnumerable 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);
+ }
+
+ ///
+ /// 将风控试算结果转换为交易确认结果。
+ /// 这里不重新执行风控,只负责设置 RetCode、ErrorMsg、TipMsg、TrialDataId 等返回给确认流程的字段。
+ ///
+ private bool ApplyQuotaCheckResult(ref TradeOpenResult res, QuotaTrial quotaObj, bool ignoreRiskWarning, IEnumerable ignoreRiskRuleIds = null)
+ {
if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success)//这次计算结果是通过,直接通过
{
if (!string.IsNullOrWhiteSpace(quotaObj.AvailableForClient)) // 返回用户资金情况
@@ -5026,12 +5064,13 @@ namespace YLErp.Modules.RiskModule
}
///
- /// 交易试算
+ /// 完整风控试算:先执行老风控资金/限额试算,再叠加新风控试算结果。
+ /// 后续老风控下线时,调用方可以保留该入口,内部改为直接调用 RunNewRiskTrial。
///
/// 交易Id,结构化交易请传主交易Id
/// 试算来源,0:手动点击试算按钮;1:确认成交时自动调用;
///
- 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;
+ }
+ ///
+ /// 新风控独立试算:不执行老风控资金/限额检查,只调用新风控引擎。
+ /// 用于老风控特批后的确认流程,也可作为后续老风控下线后的主试算入口。
+ ///
+ 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;
+ }
+
+ ///
+ /// 将新风控引擎结果合并到 QuotaTrial。
+ /// 如果传入的是完整试算结果,则是在老风控结果基础上叠加新风控;如果传入的是新建结果,则表示只跑新风控。
+ ///
+ private QuotaTrial ApplyNewRiskTrial(int tradeId, QuotaTrial result)
+ {
// ===== 新风控引擎接入点(设计文档 4.6.1) =====
try
{
@@ -5318,7 +5403,6 @@ namespace YLErp.Modules.RiskModule
}
// ===== 新风控引擎接入结束 =====
- SaveQuotaTrial(result);
return result;
}
///
diff --git a/YLErpDAL/Modules/TradeModule/DealModule/TradeConfirmService.cs b/YLErpDAL/Modules/TradeModule/DealModule/TradeConfirmService.cs
index 7bca1074..018cac68 100644
--- a/YLErpDAL/Modules/TradeModule/DealModule/TradeConfirmService.cs
+++ b/YLErpDAL/Modules/TradeModule/DealModule/TradeConfirmService.cs
@@ -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 = "确认交易";
diff --git a/YLErpWeb/Controllers/tradeController.cs b/YLErpWeb/Controllers/tradeController.cs
index 0a162c3c..cf12800c 100644
--- a/YLErpWeb/Controllers/tradeController.cs
+++ b/YLErpWeb/Controllers/tradeController.cs
@@ -8428,7 +8428,7 @@ namespace YLErp.Web.Controllers
///
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);
}