feat(swap): 完善自动互换资金记录管理功能

- 在SwapEodPositionService中添加clientCashIds列表追踪多笔资金记录
- 为UnwindData类新增ClientCashIds属性存储自动互换生成的资金记录ID集合
- 在SwapTradeBaseService中实现GetAutoSwapClientCashIds方法解析历史数据
- 添加GetLegacyAutoSwapClientCashRecords方法处理遗留资金记录删除逻辑
- 实现IsLegacyAutoSwapClientCashRecord方法精确匹配待删除资金记录
- 优化资金记录删除策略区分自动互换与手动操作产生的记录
This commit is contained in:
张名锐
2026-07-01 09:53:44 +08:00
parent 1a9b8af512
commit 78751f0a57
3 changed files with 135 additions and 14 deletions
@@ -102,6 +102,7 @@ namespace YLErp.DBModels
public UnwindData()
{
FlowEvents = new List<swap_flow_event>();
ClientCashIds = new List<int>();
}
public int SwapTradeId { get; set; }
/// <summary>
@@ -178,6 +179,10 @@ namespace YLErp.DBModels
/// </summary>
public decimal SwapDividendPnl { get; set; }
/// <summary>
/// 自动互换生成的客户资金记录ID集合
/// </summary>
public List<int> ClientCashIds { get; set; }
/// <summary>
/// 利息腿/浮动腿 集合,不序列化存储,只做查询
/// </summary>
public List<swap_flow_event> FlowEvents { get; set; }
@@ -510,16 +510,19 @@ namespace YLErp.Modules.SwapModule
var cashHappenDate = interval?.SettlementDate ?? unwindData.ValueDate;
int clientCashId = 0;
var clientCashIds = new List<int>();
// 利息腿:插入资金记录(使用系统操作_互换)
if (unwindData.SwapCloseAmount != 0)
{
clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapCloseAmount), ClientCashInCashOut._互换, cashHappenDate);
clientCashIds.Add(clientCashId);
}
// 预付金腿:单独插入一条资金记录(系统操作_预付金返息)
if (unwindData.SwapMarginRebatePnl != 0)
{
clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut._预付金返息, unwindData.ValueDate);
clientCashIds.Add(clientCashId);
}
unwindData.SwapCloseAmount = unwindData.SwapRealizedPnL;//需要算上预付金利息 和 分红; 只是不算预付金返还
// 分红:使用派息支付日偏移记录资金记录
@@ -529,8 +532,10 @@ namespace YLErp.Modules.SwapModule
? dividendEvents.First().PayDate.Value
: unwindData.ValueDate;
clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapDividendPnl), ClientCashInCashOut._互换, dividendPayDate);
clientCashIds.Add(clientCashId);
}
unwindData.ClientCashIds = clientCashIds;
string data = JsonConvert.SerializeObject(unwindData);
var swapEvent = new SwapEventService(this).AddSwapEventDate(unwindData.ValueDate, unwindData.SwapTradeId, (int)SwapEventTypeEnum., data, clientCashId, true, "系统操作-自动互换");//将互换总额存入事件
if (flowEvents!=null)
@@ -384,28 +384,40 @@ namespace YLErp.Modules.SwapModule
DbContext.swap_flow_event.RemoveRange(swapFlowEvents);
// 删除自动互换产生的资金记录(client_cash_in_out
var swapEventIds = swapEvents.Select(s => s.id).ToList();
if (swapEventIds.Any())
var autoSwapEvents = swapEvents.ToList();
if (autoSwapEvents.Any())
{
// 通过 swap_event 的 ClientCashId 删除对应的资金记录(利息腿)
var clientCashIds = swapEvents.Where(s => s.ClientCashId > 0).Select(s => s.ClientCashId).ToList();
var clientCashIds = autoSwapEvents
.Where(s => s.ClientCashId > 0)
.Select(s => s.ClientCashId)
.ToList();
var legacyAutoEvents = new List<swap_event>();
foreach (var swapEvent in autoSwapEvents)
{
var eventCashIds = GetAutoSwapClientCashIds(swapEvent);
if (eventCashIds.Any())
{
clientCashIds.AddRange(eventCashIds);
}
else
{
legacyAutoEvents.Add(swapEvent);
}
}
clientCashIds = clientCashIds.Distinct().ToList();
if (clientCashIds.Any())
{
var clientCashRecords = DbContext.ClientCashInCashOut.Where(x => clientCashIds.Contains(x.id)).ToList();
DbContext.ClientCashInCashOut.RemoveRange(clientCashRecords);
}
// 删除预付金腿的资金记录(通过交易ID、日期和Action类型查找,一次性查询避免MySQL连接重用问题)
var swapTradeIds = swapEvents.Select(s => s.SwapTradeId).Distinct().ToList();
var actions = new List<string>() { ClientCashInCashOut._预付金返息, ClientCashInCashOut._互换 };
var premiumCashRecords = DbContext.ClientCashInCashOut
.Where(x => swapTradeIds.Contains(x.TradeId ?? 0)
&& x.HappenDate>=valueDate
&& actions.Contains(x.Action))
.ToList();
if (premiumCashRecords.Any())
var legacyCashRecords = GetLegacyAutoSwapClientCashRecords(legacyAutoEvents, clientCashIds);
if (legacyCashRecords.Any())
{
DbContext.ClientCashInCashOut.RemoveRange(premiumCashRecords);
DbContext.ClientCashInCashOut.RemoveRange(legacyCashRecords);
}
}
}
@@ -413,6 +425,105 @@ namespace YLErp.Modules.SwapModule
DbContext.eod_swap.RemoveRange(eodSwaps);
DbContext.SaveChanges();
}
private List<int> GetAutoSwapClientCashIds(swap_event swapEvent)
{
if (swapEvent == null || string.IsNullOrWhiteSpace(swapEvent.EventData))
{
return new List<int>();
}
try
{
var unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
return unwindData?.ClientCashIds?.Where(x => x > 0).Distinct().ToList() ?? new List<int>();
}
catch
{
return new List<int>();
}
}
private List<ClientCashInCashOut> GetLegacyAutoSwapClientCashRecords(List<swap_event> swapEvents, List<int> excludedClientCashIds)
{
if (swapEvents == null || !swapEvents.Any())
{
return new List<ClientCashInCashOut>();
}
var eventIds = swapEvents.Select(x => x.id).ToList();
var flowEvents = DbContext.swap_flow_event
.Where(x => x.EventId.HasValue && eventIds.Contains(x.EventId.Value))
.ToList();
var manualClientCashIds = DbContext.swap_event
.Where(x => x.SwapTradeId == swapEvents.First().SwapTradeId
&& x.ClientCashId > 0
&& x.EventType != (int)SwapEventTypeEnum.)
.Select(x => x.ClientCashId)
.ToList();
var records = new List<ClientCashInCashOut>();
foreach (var swapEvent in swapEvents)
{
UnwindData unwindData = null;
if (!string.IsNullOrWhiteSpace(swapEvent.EventData))
{
try
{
unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
}
catch
{
}
}
var eventFlowEvents = flowEvents.Where(x => x.EventId == swapEvent.id).ToList();
var candidateDates = new HashSet<DateTime> { swapEvent.ValueDate.Date };
if (unwindData?.PayDate != null)
{
candidateDates.Add(unwindData.PayDate.Value.Date);
}
eventFlowEvents.Where(x => x.PayDate.HasValue).ToList().ForEach(x => candidateDates.Add(x.PayDate.Value.Date));
var eventRecords = DbContext.ClientCashInCashOut
.Where(x => x.TradeId == swapEvent.SwapTradeId
&& !excludedClientCashIds.Contains(x.id)
&& !manualClientCashIds.Contains(x.id)
&& (x.Action == ClientCashInCashOut._预付金返息 || x.Action == ClientCashInCashOut._互换))
.ToList()
.Where(x => x.HappenDate.HasValue && candidateDates.Contains(x.HappenDate.Value.Date))
.Where(x => IsLegacyAutoSwapClientCashRecord(x, unwindData))
.ToList();
records.AddRange(eventRecords);
}
return records.GroupBy(x => x.id).Select(x => x.First()).ToList();
}
private bool IsLegacyAutoSwapClientCashRecord(ClientCashInCashOut cashRecord, UnwindData unwindData)
{
if (unwindData == null)
{
return true;
}
var amount = Convert.ToDecimal(cashRecord.Money ?? 0);
if (cashRecord.Action == ClientCashInCashOut._预付金返息)
{
return unwindData.SwapMarginRebatePnl != 0
&& amount == -unwindData.SwapMarginRebatePnl;
}
if (cashRecord.Action == ClientCashInCashOut._互换)
{
return (unwindData.SwapCloseAmount != 0 && amount == -unwindData.SwapCloseAmount)
|| (unwindData.SwapDividendPnl != 0 && amount == -unwindData.SwapDividendPnl)
|| (unwindData.SwapRealizedPnL != 0 && amount == -unwindData.SwapRealizedPnL);
}
return false;
}
/// <summary>
/// 获取上一交易日
/// </summary>