fix(risk): 修复限额试算服务中的空值检查和异常处理
- 添加了交易对象存在性检查,避免空引用异常 - 增加了TradeType、TradeDate等关键字段的空值验证 - 添加了标的对象为空时的处理逻辑 - 实现了ExJson反序列化的异常捕获和错误处理 - 增加了CheckFund方法中交易列表的空值检查 - 添加了ExerciseDate为空的验证逻辑 - 增加了详细的日志记录用于调试追踪
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using BaseOUDAL;
|
||||
using BaseOUDAL;
|
||||
using ClosedXML.Report.Utils;
|
||||
using Confluent.Kafka;
|
||||
using CsvHelper;
|
||||
@@ -4721,8 +4721,26 @@ namespace YLErp.Modules.RiskModule
|
||||
/// <returns></returns>
|
||||
public QuotaTrial QuotaCheck(int tradeId, int trialSource)
|
||||
{
|
||||
_logger.Info($"[限额试算] 开始执行 - tradeId: {tradeId}, trialSource: {trialSource}");
|
||||
var result = new QuotaTrial();
|
||||
var tradeObj = DbContext.trade.AsNoTracking().Where(O => O.id == tradeId).FirstOrDefault();
|
||||
|
||||
// 检查交易对象是否存在
|
||||
if (tradeObj == null)
|
||||
{
|
||||
_logger.Error($"[限额试算] 错误: 找不到交易记录 - tradeId: {tradeId}");
|
||||
throw new ArgumentNullException(nameof(tradeObj), $"找不到ID为 {tradeId} 的交易记录");
|
||||
}
|
||||
|
||||
_logger.Info($"[限额试算] 交易信息 - tradeId: {tradeObj.id}, TradeNumber: {tradeObj.TradeNumber}, TradeType: {tradeObj.TradeType}, ClientId: {tradeObj.ClientId}");
|
||||
|
||||
// 检查关键字段是否为null
|
||||
if (string.IsNullOrEmpty(tradeObj.TradeType))
|
||||
{
|
||||
_logger.Error($"[限额试算] 错误: TradeType 为 null 或空 - tradeId: {tradeId}");
|
||||
throw new ArgumentNullException(nameof(tradeObj.TradeType), "交易类型不能为空");
|
||||
}
|
||||
|
||||
var tradeList = new List<trade>();
|
||||
if (tradeObj.TradeType == "结构化交易")
|
||||
{
|
||||
@@ -4732,6 +4750,15 @@ namespace YLErp.Modules.RiskModule
|
||||
{
|
||||
tradeList.Add(tradeObj);
|
||||
}
|
||||
// 检查关键字段
|
||||
if (!tradeObj.TradeDate.HasValue)
|
||||
{
|
||||
_logger.Error($"[限额试算] 错误: TradeDate 为 null - tradeId: {tradeId}");
|
||||
throw new ArgumentNullException(nameof(tradeObj.TradeDate), "交易日期不能为空");
|
||||
}
|
||||
|
||||
_logger.Info($"[限额试算] 检查持仓 - positions.Count: {positions?.Count ?? 0}");
|
||||
|
||||
var fundStatus = CheckFund(tradeList, tradeObj.StockEqvNotional, tradeObj.ClientId, out var fundMsg, out var availableMsg);
|
||||
//var riskWarningStatus = CheckRiskWarning(tradeList, out var riskWarningMsg);
|
||||
string riskWarningMsg = string.Empty;
|
||||
@@ -4763,6 +4790,8 @@ namespace YLErp.Modules.RiskModule
|
||||
}
|
||||
List<ClientRiskCheckItem> clientRiskCheckResps = new List<ClientRiskCheckItem>();
|
||||
var floatPosi = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.PosiQuantity > 0 && x.IsInitial && !x.Invalid).FirstOrDefault();
|
||||
_logger.Info($"[限额试算] floatPosi: {(floatPosi == null ? "null" : $"id={floatPosi.id}, UnderlyingCode={floatPosi.UnderlyingCode}")}");
|
||||
|
||||
if (floatPosi != null)
|
||||
{
|
||||
using var bondDb = new BondOmsDBContext();
|
||||
@@ -4781,15 +4810,28 @@ namespace YLErp.Modules.RiskModule
|
||||
var umCodes = clientPositions.Select(s => s.security_id).Distinct().ToList();
|
||||
umCodes.Add(floatPosi.UnderlyingCode);
|
||||
umCodes = umCodes.Distinct().ToList();
|
||||
_logger.Info($"[限额试算] umCodes.Count: {umCodes?.Count ?? 0}");
|
||||
|
||||
var ums = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().Where(x => umCodes.Contains(x.UnderlyingCode));
|
||||
_logger.Info($"[限额试算] ums.Count: {ums?.Count() ?? 0}");
|
||||
|
||||
List<CheckQuotaMoitorModel> checkPoisiList = new List<CheckQuotaMoitorModel>();
|
||||
var posiList = GetPosiQuotaMoitors();
|
||||
_logger.Info($"[限额试算] posiList.Count: {posiList?.Count ?? 0}");
|
||||
foreach (var item in clientPositions)
|
||||
{
|
||||
var dealDate = tradeObj.TradeDate.Value;
|
||||
decimal vobp = 0;
|
||||
double lastPrice = 0;
|
||||
var um = ums.FirstOrDefault(x => x.UnderlyingCode == item.security_id);
|
||||
|
||||
_logger.Info($"[限额试算] 处理持仓 - security_id: {item.security_id}, um: {(um == null ? "null" : $"id={um.id}, UnderlyingCode={um.UnderlyingCode}")}");
|
||||
|
||||
if (um == null)
|
||||
{
|
||||
_logger.Error($"[限额试算] 错误: 找不到标的 - security_id: {item.security_id}");
|
||||
continue;
|
||||
}
|
||||
CheckQuotaMoitorModel checkQuotaMoitorModel = new CheckQuotaMoitorModel();
|
||||
checkQuotaMoitorModel.Qty = (item.position_qty ?? 0) * 10000;
|
||||
checkQuotaMoitorModel.id = item.id;
|
||||
@@ -4818,8 +4860,34 @@ namespace YLErp.Modules.RiskModule
|
||||
var bondPrice = EodPriceQueryService.GetBondPrice(dealDate, item.security_id);
|
||||
lastPrice = bondPrice != null ? bondPrice.ClosePrice : (um.Price ?? 0) * Convert.ToDouble(ConsGlobal.bondPriceMultiple);
|
||||
vobp = bondPrice != null ? bondPrice.Vobp ?? 0 : 0;
|
||||
var bond = JsonHelper.Deserialize<UnderlyingBond>(um.ExJson);
|
||||
checkQuotaMoitorModel.Circulation = (bond.IssueSize * 100000000m) ?? 0;
|
||||
|
||||
// 检查 ExJson 是否为 null 或空
|
||||
if (string.IsNullOrEmpty(um.ExJson))
|
||||
{
|
||||
_logger.Error($"[限额试算] 错误: um.ExJson 为 null 或空 - um.id: {um.id}, UnderlyingCode: {um.UnderlyingCode}");
|
||||
checkQuotaMoitorModel.Circulation = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var bond = JsonHelper.Deserialize<UnderlyingBond>(um.ExJson);
|
||||
if (bond == null)
|
||||
{
|
||||
_logger.Error($"[限额试算] 错误: 反序列化 bond 失败 - um.id: {um.id}, ExJson: {um.ExJson.Substring(0, Math.Min(100, um.ExJson.Length))}");
|
||||
checkQuotaMoitorModel.Circulation = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkQuotaMoitorModel.Circulation = (bond.IssueSize * 100000000m) ?? 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"[限额试算] 错误: 反序列化 bond 异常 - um.id: {um.id}", ex);
|
||||
checkQuotaMoitorModel.Circulation = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var ratio = checkQuotaMoitorModel.Side == 0 ? 1 : -1;
|
||||
@@ -4859,6 +4927,7 @@ namespace YLErp.Modules.RiskModule
|
||||
result.QuotaWarningDetails = quotaWarningMsg;
|
||||
result.AvailableForClient = availableMsg;
|
||||
result.TrialSource = trialSource;
|
||||
_logger.Info($"[限额试算] 试算完成 - tradeId: {tradeId}, TrialStatus: {result.TrialStatus}, FundCheck: {fundStatus}, QuotaCheck: {quotaStatus}");
|
||||
SaveQuotaTrial(result);
|
||||
return result;
|
||||
}
|
||||
@@ -4960,11 +5029,20 @@ namespace YLErp.Modules.RiskModule
|
||||
|
||||
private bool CheckFund(List<trade> trades, double stockEqvNotional, int clientId, out string msg, out string avmsg)
|
||||
{
|
||||
|
||||
_logger.Info($"[CheckFund] 开始资金检查 - clientId: {clientId}, stockEqvNotional: {stockEqvNotional}, trades.Count: {trades?.Count ?? 0}");
|
||||
|
||||
msg = "";
|
||||
avmsg = "";
|
||||
var totalCredit = 0.0;
|
||||
var availableStockEqvNotional = 0.0;
|
||||
|
||||
if (trades == null || trades.Count == 0)
|
||||
{
|
||||
_logger.Error("[CheckFund] 错误: trades 为 null 或空列表");
|
||||
msg = "交易列表为空";
|
||||
return false;
|
||||
}
|
||||
|
||||
var client = DataCacheProvider.GetClientDataSource().GetData(clientId);
|
||||
if (client == null)
|
||||
{
|
||||
@@ -5020,6 +5098,14 @@ namespace YLErp.Modules.RiskModule
|
||||
var clientBalance = clientBalances[0];
|
||||
if (tradePrice <= 0)
|
||||
{
|
||||
// 检查 ExerciseDate 是否为 null
|
||||
if (!trades[0].ExerciseDate.HasValue)
|
||||
{
|
||||
_logger.Error($"[CheckFund] 错误: trades[0].ExerciseDate 为 null - tradeId: {trades[0].id}");
|
||||
msg = "交易行权日期不能为空";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trades[0].ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date)
|
||||
{
|
||||
var margins = margin + tradePrice;
|
||||
|
||||
Reference in New Issue
Block a user