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:
@@ -1,4 +1,4 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Qdp.Foundation.Utilities;
|
||||
using System.Reflection;
|
||||
using YLErp.BLL;
|
||||
@@ -309,11 +309,14 @@ namespace YLErp.Modules.RiskEngine
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Info($"[风控引擎] EvaluateRisk 开始 - TradeId: {context?.TradeId}, TriggerPoint: {triggerPoint}");
|
||||
if (context != null && context.DbContext == null)
|
||||
{
|
||||
context.DbContext = DbContext;
|
||||
}
|
||||
using var ruleDbContext = DbContextFactory.GetYLDbContext();
|
||||
ruleDbContext.ChangeTracker.QueryTrackingBehavior = Microsoft.EntityFrameworkCore.QueryTrackingBehavior.NoTracking;
|
||||
|
||||
context ??= new RiskContext();
|
||||
context.TriggerPoint = triggerPoint;
|
||||
context.DbContext = ruleDbContext;
|
||||
|
||||
_logger.Info($"[风控引擎] EvaluateRisk 开始 - TradeId: {context.TradeId}, TriggerPoint: {triggerPoint}");
|
||||
|
||||
// ============================================================
|
||||
// Step 1: 从内存缓存读取规则定义和规则应用(启动时已预热)
|
||||
@@ -343,8 +346,8 @@ namespace YLErp.Modules.RiskEngine
|
||||
// 2. 同一维度内多选按并集处理;
|
||||
// 3. 不同维度之间按交集处理;
|
||||
// 4. 某维度留空表示该维度不限制。
|
||||
var trade = context != null && context.TradeId > 0
|
||||
? DbContext.trade.FirstOrDefault(t => t.id == context.TradeId)
|
||||
var trade = context.TradeId > 0
|
||||
? context.DbContext.trade.AsNoTracking().FirstOrDefault(t => t.id == context.TradeId)
|
||||
: null;
|
||||
|
||||
var matchedApplications = triggerMatchedApps
|
||||
@@ -706,8 +709,8 @@ namespace YLErp.Modules.RiskEngine
|
||||
//{
|
||||
// Id = 1000012,
|
||||
// RuleName = "债券类净价偏离(本地)",
|
||||
// RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取 PosiNetNoFeePrice,对应债券类标的期初交割净价,库内为 1 左右原值;通过 DbContext.china_bond_valuation 按当前交易标的和交易日前日期取上一收盘日 net_price,库内为 100 左右报价。计算逻辑:按 ABS(PosiNetNoFeePrice×100-net_price) 计算绝对价差,价差大于 5 元时触发审批。",
|
||||
// RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId).PosiNetNoFeePrice.Value * 100m - DbContext.china_bond_valuation.Where(v => v.bond_id == DbContext.trade.First(t => t.id == TradeId).UnderlyingCode && v.valuation_date < DbContext.trade.First(t => t.id == TradeId).TradeDate.Value.Date).OrderByDescending(v => v.credibility).ThenByDescending(v => v.valuation_date).First().net_price.Value) > 5m",
|
||||
// RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取 IsInitial=true、Invalid=false、PosiDirection=2 且有标的代码的浮动支付端 PosiNetNoFeePrice 和 UnderlyingCode,PosiNetNoFeePrice 对应债券类标的期初交割净价,库内为 1 左右原值;通过 DbContext.china_bond_valuation 按该浮动支付端标的和交易日前日期优先取 credibility=1 的上一收盘日 net_price,库内为 100 左右报价。计算逻辑:按 ABS(PosiNetNoFeePrice×100-net_price) 计算绝对价差,价差大于 5 元时触发审批。",
|
||||
// RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId && p.IsInitial && !p.Invalid && p.PosiDirection == 2 && !string.IsNullOrEmpty(p.UnderlyingCode)).PosiNetNoFeePrice.Value * 100m - DbContext.china_bond_valuation.Where(v => v.bond_id == DbContext.swap_position.First(p => p.SwapTradeId == TradeId && p.IsInitial && !p.Invalid && p.PosiDirection == 2 && !string.IsNullOrEmpty(p.UnderlyingCode)).UnderlyingCode && v.valuation_date < DbContext.trade.First(t => t.id == TradeId).TradeDate.Value.Date).OrderBy(v => v.credibility).ThenByDescending(v => v.valuation_date).First().net_price.Value) > 5m",
|
||||
// Version = 1,
|
||||
// Status = RiskRuleStatus.Active,
|
||||
// OptId = 0,
|
||||
@@ -722,8 +725,8 @@ namespace YLErp.Modules.RiskEngine
|
||||
//{
|
||||
// Id = 1000013,
|
||||
// RuleName = "债券类收益率偏离(本地)",
|
||||
// RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取 InitYtm,对应债券类标的期初成交收益率,库内为原值;通过 DbContext.china_bond_valuation 按当前交易标的和交易日前日期取上一收盘日 yield,库内为 1.5 到 2.2 左右百分数。计算逻辑:按 ABS(InitYtm×100-yield) 计算收益率绝对差,差值大于 1 时触发审批。",
|
||||
// RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId).InitYtm.Value * 100m - DbContext.china_bond_valuation.Where(v => v.bond_id == DbContext.trade.First(t => t.id == TradeId).UnderlyingCode && v.valuation_date < DbContext.trade.First(t => t.id == TradeId).TradeDate.Value.Date).OrderByDescending(v => v.credibility).ThenByDescending(v => v.valuation_date).First().yield.Value) > 1m",
|
||||
// RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取 IsInitial=true、Invalid=false、PosiDirection=2 且有标的代码的浮动支付端 InitYtm 和 UnderlyingCode,InitYtm 对应债券类标的期初成交收益率,库内为原值;通过 DbContext.china_bond_valuation 按该浮动支付端标的和交易日前日期优先取 credibility=1 的上一收盘日 yield,库内为 1.5 到 2.2 左右百分数。计算逻辑:按 ABS(InitYtm×100-yield) 计算收益率绝对差,差值大于 1 时触发审批。",
|
||||
// RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId && p.IsInitial && !p.Invalid && p.PosiDirection == 2 && !string.IsNullOrEmpty(p.UnderlyingCode)).InitYtm.Value * 100m - DbContext.china_bond_valuation.Where(v => v.bond_id == DbContext.swap_position.First(p => p.SwapTradeId == TradeId && p.IsInitial && !p.Invalid && p.PosiDirection == 2 && !string.IsNullOrEmpty(p.UnderlyingCode)).UnderlyingCode && v.valuation_date < DbContext.trade.First(t => t.id == TradeId).TradeDate.Value.Date).OrderBy(v => v.credibility).ThenByDescending(v => v.valuation_date).First().yield.Value) > 1m",
|
||||
// Version = 1,
|
||||
// Status = RiskRuleStatus.Active,
|
||||
// OptId = 0,
|
||||
@@ -738,8 +741,8 @@ namespace YLErp.Modules.RiskEngine
|
||||
//{
|
||||
// Id = 1000014,
|
||||
// RuleName = "非债券类价格偏离(本地)",
|
||||
// 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",
|
||||
// RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取 IsInitial=true、Invalid=false、PosiDirection=2 且有标的代码的浮动支付端 PosiGrossPrice 和 UnderlyingCode,PosiGrossPrice 对应普通收益互换页面填写的期初标的价格,库内为 1 左右原值;通过 DbContext.eod_commodity_future_price 按该浮动支付端标的和交易日前日期取上一日收盘价 ClosePrice。注意:eod_commodity_future_price 模型属性 UnderlyingCode 实际映射数据库列 FutureContractId,数据库排查时应使用 FutureContractId 与 swap_position.UnderlyingCode 关联。计算逻辑:按 ABS(PosiGrossPrice×100-ClosePrice) 计算绝对价差,价差大于 5 时触发审批。",
|
||||
// RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId && p.IsInitial && !p.Invalid && p.PosiDirection == 2 && !string.IsNullOrEmpty(p.UnderlyingCode)).PosiGrossPrice * 100m - Convert.ToDecimal(DbContext.eod_commodity_future_price.Where(e => e.UnderlyingCode == DbContext.swap_position.First(p => p.SwapTradeId == TradeId && p.IsInitial && !p.Invalid && p.PosiDirection == 2 && !string.IsNullOrEmpty(p.UnderlyingCode)).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,
|
||||
@@ -754,8 +757,8 @@ namespace YLErp.Modules.RiskEngine
|
||||
//{
|
||||
// Id = 1000015,
|
||||
// RuleName = "单一交易对手累计标的数量超阈值(本地)",
|
||||
// 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
|
||||
// RuleText = "取值字段:通过 DbContext.trade 按 TradeId 取当前交易对手 ClientId,再查询同一交易对手有效交易对应的实时存续持仓 swap_position.UnderlyingCode 去重数量。实时存续持仓口径:IsInitial=false、PosiQuantity>0、Invalid=false、PosiDirection>0 且 UnderlyingCode 非空。计算逻辑:同一交易对手累计标的数量超过 10 个时触发审批。",
|
||||
// RuleExpr= DbContext.swap_position.Where(p => !string.IsNullOrEmpty(p.UnderlyingCode) && !p.IsInitial && p.PosiQuantity > 0 && !p.Invalid && p.PosiDirection > 0 && DbContext.trade.Any(t => t.id == p.SwapTradeId && t.ValidState != "InValid" && t.ClientId == DbContext.trade.First(x => x.id == TradeId).ClientId)).Select(p => p.UnderlyingCode).Distinct().Count() > 10
|
||||
// Version = 1,
|
||||
// Status = RiskRuleStatus.Active,
|
||||
// OptId = 0,
|
||||
@@ -770,8 +773,8 @@ namespace YLErp.Modules.RiskEngine
|
||||
//{
|
||||
// Id = 1000016,
|
||||
// RuleName = "多头支付固定端利率偏离(本地)",
|
||||
// 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",
|
||||
// RuleText = "取值字段:通过 DbContext.swap_position 按 TradeId 取利息端收入固定利息方向记录的 InterestRateDefault。InterestRateDefault 只代表利率文本框中 + 号后的点差,不包含 FR007 基准利率,库内为小数原值,界面按百分比显示。计算逻辑:按 ABS(InterestRateDefault×100) 计算点差百分比绝对值,绝对值小于 5 时触发审批。",
|
||||
// RuleExpr = "Math.Abs(DbContext.swap_position.First(p => p.SwapTradeId == TradeId && p.InterestDirection == 1).InterestRateDefault * 100m) < 5m",
|
||||
// Version = 1,
|
||||
// Status = RiskRuleStatus.Active,
|
||||
// OptId = 0,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ using Qdp.Foundation.Utilities;
|
||||
using Qdp.Pricing.Ecosystem.Trade.FixedIncome;
|
||||
using Qdp.Pricing.Library.Base.Utilities;
|
||||
using Qdp.Pricing.Library.Common.Products.Rates;
|
||||
using Snowflake.Core;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
@@ -57,6 +58,7 @@ namespace YLErp.Modules.RiskModule
|
||||
{
|
||||
public class QuotaMonitorService : YLBaseService
|
||||
{
|
||||
private const string RiskCheckTriggerRemark = "触发风控";
|
||||
IYcLogger _logger = LogFactory.GetLogger("QuotaMonitorService");
|
||||
static QuotaMonitorService()
|
||||
{
|
||||
@@ -4500,11 +4502,6 @@ namespace YLErp.Modules.RiskModule
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var lastTrial = trialService.QueryLastQuotaTrial(tradeId, true);
|
||||
if (quotaObj.TrialStatus == QuotaTrialStatusEnum.Success)
|
||||
{
|
||||
new TradeRiskCheckLogService(UserInfo).AddLog(quotaObj);
|
||||
}
|
||||
//否则的情况是上次没算,这次是预警,或上次算了,结果是不通过\通过或预警,这次是预警或不通过,提示用户;
|
||||
res.RetCode = TradeOpenRetCode.QuotaTrialError;
|
||||
res.TrialDataId = quotaObj.id;
|
||||
@@ -4544,6 +4541,10 @@ namespace YLErp.Modules.RiskModule
|
||||
}
|
||||
}
|
||||
res.RetCode = isRiskApprovalWarning ? TradeOpenRetCode.RiskWarning : TradeOpenRetCode.QuotaTrialError;
|
||||
var isOldRiskErrorSpecialApproval = !isRiskApprovalWarning
|
||||
&& quotaObj.OldRiskTrialStatus == QuotaTrialStatusEnum.Error
|
||||
&& res.OldRiskNeedSpecialApproval;
|
||||
AddRiskCheckLog(quotaObj, isOldRiskErrorSpecialApproval);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4935,8 +4936,64 @@ namespace YLErp.Modules.RiskModule
|
||||
return result;
|
||||
}
|
||||
|
||||
private DateTime? QueryLatestRiskCheckTriggerTime(int tradeId)
|
||||
{
|
||||
var tradeNumber = DbContext.trade.Where(x => x.id == tradeId).Select(x => x.TradeNumber).FirstOrDefault();
|
||||
if (string.IsNullOrWhiteSpace(tradeNumber))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return DbContext.trade_risk_check_log
|
||||
.Where(x => x.trade_number == tradeNumber && x.remark.StartsWith(RiskCheckTriggerRemark))
|
||||
.OrderByDescending(x => x.create_time)
|
||||
.Select(x => (DateTime?)x.create_time)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private void AddRiskCheckLog(QuotaTrial quotaTrial, bool isOldRiskErrorSpecialApproval = false)
|
||||
{
|
||||
var td = DbContext.trade.FirstOrDefault(x => x.id == quotaTrial.TradeId);
|
||||
var log = new trade_risk_check_log();
|
||||
if (td != null)
|
||||
{
|
||||
var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId);
|
||||
log.client_number = client?.Number;
|
||||
}
|
||||
var worker = new IdWorker(1, 1);
|
||||
log.id = worker.NextId();
|
||||
log.trial_result = (int)quotaTrial.TrialStatus;
|
||||
log.client_name = quotaTrial.ClientName;
|
||||
log.trader = td?.TraderName;
|
||||
log.trade_number = quotaTrial.TradeNumber;
|
||||
var riskWarning = string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(quotaTrial.QuotaWarningDetails))
|
||||
{
|
||||
riskWarning = "限额预警: " + quotaTrial.QuotaWarningDetails;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(quotaTrial.RiskWarningDetails))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(riskWarning))
|
||||
{
|
||||
riskWarning += Environment.NewLine;
|
||||
}
|
||||
riskWarning += "风险预警: " + quotaTrial.RiskWarningDetails;
|
||||
}
|
||||
log.risk_warning = riskWarning;
|
||||
log.limit_warning = quotaTrial.QuotaCheckDetails;
|
||||
log.remark = RiskCheckTriggerRemark;
|
||||
if (isOldRiskErrorSpecialApproval)
|
||||
{
|
||||
log.remark += ";老风控Error允许交易特批";
|
||||
}
|
||||
log.create_user = UserId;
|
||||
log.create_time = DateTime.Now;
|
||||
DbContext.trade_risk_check_log.Add(log);
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按 quotaTrial.id 校验试算结果是否已超过配置时效
|
||||
/// 按 quotaTrial.id 校验最近一次触发风控的记录是否已超过配置时效
|
||||
/// </summary>
|
||||
/// <param name="trialDataId">quotaTrial.id</param>
|
||||
/// <param name="expireSeconds">有效时长(秒)</param>
|
||||
@@ -4944,11 +5001,17 @@ namespace YLErp.Modules.RiskModule
|
||||
public bool IsQuotaTrialExpired(int trialDataId, int expireSeconds)
|
||||
{
|
||||
var quotaTrial = QueryQuotaTrial(trialDataId);
|
||||
if (quotaTrial == null || !quotaTrial.OptDate.HasValue)
|
||||
if (quotaTrial == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return DateTime.Now - quotaTrial.OptDate.Value > TimeSpan.FromSeconds(expireSeconds);
|
||||
var latestRiskCheckTriggerTime = QueryLatestRiskCheckTriggerTime(quotaTrial.TradeId);
|
||||
var riskCheckTime = latestRiskCheckTriggerTime ?? quotaTrial.OptDate;
|
||||
if (!riskCheckTime.HasValue)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return DateTime.Now - riskCheckTime.Value > TimeSpan.FromSeconds(expireSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -95,13 +95,13 @@ namespace YLErp.Modules.RiskModule
|
||||
switch ((QuotaTrialStatusEnum)item.trial_result)
|
||||
{
|
||||
case QuotaTrialStatusEnum.Warning:
|
||||
exportModel.trial_result = "预警";
|
||||
exportModel.trial_result = "老风控触发特批";
|
||||
break;
|
||||
case QuotaTrialStatusEnum.RiskWarning:
|
||||
exportModel.trial_result = "风控需审批";
|
||||
exportModel.trial_result = "新风控需审批";
|
||||
break;
|
||||
case QuotaTrialStatusEnum.Error:
|
||||
exportModel.trial_result = "限额";
|
||||
exportModel.trial_result = "禁止";
|
||||
break;
|
||||
default:
|
||||
exportModel.trial_result = "通过";
|
||||
@@ -140,7 +140,20 @@ namespace YLErp.Modules.RiskModule
|
||||
log.client_name = quotaTrial.ClientName;
|
||||
log.trader = td?.TraderName;
|
||||
log.trade_number = quotaTrial.TradeNumber;
|
||||
log.risk_warning = quotaTrial.QuotaWarningDetails;
|
||||
var riskWarning = string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(quotaTrial.QuotaWarningDetails))
|
||||
{
|
||||
riskWarning = "限额预警: " + quotaTrial.QuotaWarningDetails;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(quotaTrial.RiskWarningDetails))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(riskWarning))
|
||||
{
|
||||
riskWarning += Environment.NewLine;
|
||||
}
|
||||
riskWarning += "风控预警: " + quotaTrial.RiskWarningDetails;
|
||||
}
|
||||
log.risk_warning = riskWarning;
|
||||
log.limit_warning = quotaTrial.QuotaCheckDetails;
|
||||
log.remark = quotaTrial.Remark;
|
||||
log.create_user = UserId;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using YLErp.Modules.RiskModule;
|
||||
using YLErp.Modules.RiskModule;
|
||||
using YLErp.Modules.RiskModule.Dto;
|
||||
using YLErp.Modules.SwapModule;
|
||||
|
||||
@@ -41,8 +41,9 @@ namespace YLErp.Web.Controllers
|
||||
{
|
||||
new SelectListItem() {Value="-1",Text="全部",Selected=true},
|
||||
new SelectListItem() {Value="0",Text="通过"},
|
||||
new SelectListItem() {Value="1",Text="预警"},
|
||||
new SelectListItem() {Value="2",Text="限额"}
|
||||
new SelectListItem() {Value="1",Text="老风控触发特批"},
|
||||
new SelectListItem() {Value="2",Text="新风控需审批"},
|
||||
new SelectListItem() {Value="3",Text="禁止"}
|
||||
};
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -2467,8 +2467,8 @@ namespace YLErp.Web.Controllers
|
||||
var ignoreMoneyCheck = tradeidArr.Count() == 1 && additionalProcessing == tradeBLL.LackOfMoney;
|
||||
var ignoreRiskWarning = tradeidArr.Count() == 1 && additionalProcessing == tradeBLL.RiskWarningConfirm;
|
||||
var ignoreRiskRuleIdArr = StringHelper.ConvertCommaValuesToStringArray(ignoreRiskRuleIds);
|
||||
// 老风控交易特批与新风控二次审批统一基于 quotaTrial 做超时校验,超时后清空放行标记并重新校验。
|
||||
// 仅 swapTradeView.js 会回传 trialDataId 并触发 RiskWarningConfirm,按 quotaTrial.OptDate 精确校验;
|
||||
// 老风控交易特批与新风控二次审批统一基于 trialDataId 做超时校验,超时后清空放行标记并重新校验。
|
||||
// 当前仅 swapTradeView.js 会回传 trialDataId,按最近一次“触发风控”日志时间校验;查不到日志时回退 quotaTrial.OptDate。
|
||||
// tradeview.js / tradeConfirmList.js / swapTradeConfirmList.js / quotaMonitor.js 只发 LackOfMoney,不传 trialDataId,不进入超时校验。
|
||||
if (ignoreMoneyCheck || ignoreRiskWarning)
|
||||
{
|
||||
@@ -2535,6 +2535,27 @@ namespace YLErp.Web.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
// 特批放行只有在交易确认最终成功后才记录,避免前端点击特批但后端超时重检失败时误记通过。
|
||||
if (trialDataId.HasValue)
|
||||
{
|
||||
var decision = string.Empty;
|
||||
if (ignoreRiskWarning && ignoreMoneyCheck)
|
||||
{
|
||||
decision = "新风控特批、老风控交易特批通过";
|
||||
}
|
||||
else if (ignoreRiskWarning)
|
||||
{
|
||||
decision = "新风控特批通过";
|
||||
}
|
||||
else if (ignoreMoneyCheck)
|
||||
{
|
||||
decision = "老风控交易特批通过";
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(decision))
|
||||
{
|
||||
new TradeRiskCheckLogService(CurUser).AddWarningDecisionLog(trialDataId.Value, decision);
|
||||
}
|
||||
}
|
||||
var successMsg = string.IsNullOrWhiteSpace(result.tipMsg) ? "操作完成" : result.tipMsg;
|
||||
if (result.changeConfirmPaths?.Count > 0)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var query_data = {};
|
||||
var query_data = {};
|
||||
const colModelGrid = function () {
|
||||
|
||||
function dateFmt(cellValue) {
|
||||
@@ -15,8 +15,9 @@ const colModelGrid = function () {
|
||||
name: 'trial_result', label: '预警结果', index: 'trial_result', width: 80,
|
||||
formatter(cellValue) {
|
||||
if (cellValue === 0) return '通过';
|
||||
if (cellValue === 1) return '预警';
|
||||
if (cellValue === 2) return '限额';
|
||||
if (cellValue === 1) return '老风控触发特批';
|
||||
if (cellValue === 2) return '新风控需审批';
|
||||
if (cellValue === 3) return '禁止';
|
||||
return cellValue;
|
||||
}
|
||||
},
|
||||
@@ -45,7 +46,7 @@ var g_grid = {};
|
||||
$(function () {
|
||||
|
||||
$(".datepicker").datepicker({ changeMonth: true, changeYear: true, showButtonPanel: true, showOtherMonths: true, selectOtherMonths: true });
|
||||
$("#TrialResult").val([1, 2]);
|
||||
$("#TrialResult").val([1, 2, 3]);
|
||||
$("#TrialResult").selectpicker('refresh');
|
||||
PostData = {
|
||||
TrialResults: $("#TrialResult").val(),
|
||||
|
||||
@@ -119,16 +119,6 @@ var confirmFunc = function (id, additionalProcessing, ignoreRiskRuleIds, trialDa
|
||||
}
|
||||
return saveSuccess;
|
||||
};
|
||||
var saveRiskWarningDecisionLog = function (quotaTrialId, decision) {
|
||||
var saveSuccess = true;
|
||||
main.post("/trade/SaveRiskWarningDecisionLog", { quotaTrialId: quotaTrialId, decision: decision }, { async: false }).done(function (res) {
|
||||
if (!res || !res.success) {
|
||||
saveSuccess = false;
|
||||
main.message(res.msg);
|
||||
}
|
||||
});
|
||||
return saveSuccess;
|
||||
};
|
||||
var layerSetting = {
|
||||
type: 2,
|
||||
title: "提示",
|
||||
@@ -145,11 +135,6 @@ var confirmFunc = function (id, additionalProcessing, ignoreRiskRuleIds, trialDa
|
||||
if (!saveQuotaTrial(obj)) {
|
||||
return;
|
||||
}
|
||||
if (isRiskWarningConfirm) {
|
||||
if (!saveRiskWarningDecisionLog(obj.Data.id, "确认通过")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isOldRiskSpecialApproval) {
|
||||
obj.Data.Remark = (obj.Data.Remark || "").trim();
|
||||
}
|
||||
@@ -157,11 +142,6 @@ var confirmFunc = function (id, additionalProcessing, ignoreRiskRuleIds, trialDa
|
||||
confirmFunc(id, additionalProcessingType, currentIgnoreRiskRuleIds, currentTrialDataId);
|
||||
},
|
||||
cancel: function () {
|
||||
var iframeWindow = window["layui-layer-iframe" + this.index];
|
||||
var pageObj = iframeWindow && iframeWindow.page;
|
||||
if (isRiskWarningConfirm && pageObj && pageObj.Data && pageObj.Data.id) {
|
||||
saveRiskWarningDecisionLog(pageObj.Data.id, "取消不通过");
|
||||
}
|
||||
if (window.parent && window.parent.reloadtrade) {
|
||||
window.parent.reloadtrade();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user