test(swap): SwapFlowEventService seam改造+MergePageEventScenarioTest(6场景全绿)
借鉴 testable 分支,只取 seam(排除 PosiPnl/NetSettmentAmount 公式改动和 MergePageEvent 拆分), 填补当前分支对开平仓合成(MergePageEvent)零覆盖的空白。 SwapFlowEventService 新增11个 protected virtual seam: - FindTrade/FindTradeExtend/FindPositions/FindAndInvalidateFutureEvents - GetUnderlying/GetNextBusinessDay/ResolvePositionId - PersistEvents(替代InitEvent内联Add+SaveChanges)/BeginTransaction/CommitTransaction/RollbackTransaction MergePageEvent/InitEvent: private→protected virtual,内联DB调用替换为seam。 InitEvent 移除 DbContext.swap_flow_event.Add(转入PersistEvents,纯函数化)。 SwapTradeBaseService.UpdateDbOption: public void→public virtual(测试override用)。 新增测试(借鉴testable分支): - TestableSwapFlowEventService.cs(集中式共享包装类) - MergePageEventScenarioTest.cs(6场景全绿): Scenario1 单条无持仓开仓 / Scenario2 同向追加 / Scenario3 反向全平 Scenario4 反向部分平+开 / Scenario5 一开一平 / Scenario6 复杂组合 **未借鉴**:SearchPositionFlowEvent的PosiPnl/NetSettmentAmount公式改动(行为变更)、 MergePageEvent拆分ProcessSingleFlow等(非必需重构)。 SwapModule 169测试全绿(+6),无回归。
This commit is contained in:
@@ -31,6 +31,53 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
}
|
||||
|
||||
#region 可测试化接缝(Seams)——借鉴 refactor-swap-event-testable 分支,override 可在测试中替换 DB/外部调用,生产代码行为不变
|
||||
|
||||
protected virtual trade FindTrade(int swapTradeId)
|
||||
=> DbContext.trade.Find(swapTradeId);
|
||||
|
||||
protected virtual trade_extend FindTradeExtend(int swapTradeId)
|
||||
=> DbContext.trade_extend.First(x => x.TradeId == swapTradeId);
|
||||
|
||||
protected virtual List<swap_position> FindPositions(int swapTradeId)
|
||||
=> DbContext.swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.IsInitial && !x.Invalid).AsNoTracking().ToList();
|
||||
|
||||
protected virtual List<swap_flow_event> FindAndInvalidateFutureEvents(int swapTradeId, DateTime tradeDate)
|
||||
{
|
||||
var olds = DbContext.swap_flow_event.Where(x => x.EventDate > tradeDate && x.SwapTradeId == swapTradeId && x.DataState > 0);
|
||||
olds.ForEach(x => x.DataState = (int)SwapFlowDateStateEnum.废弃);
|
||||
return olds.ToList();
|
||||
}
|
||||
|
||||
protected virtual underlying_manager GetUnderlying(string underlyingCode)
|
||||
=> DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
||||
|
||||
protected virtual DateTime GetNextBusinessDay(DateTime date)
|
||||
=> QdpCalendarHelper.GetNonHoliday(date);
|
||||
|
||||
protected virtual long ResolvePositionId(swap_flow_merge merge, DateTime maturityDate, int direction, string tradeNumber)
|
||||
=> GetMaxPositionId(merge, maturityDate, direction, tradeNumber);
|
||||
|
||||
protected virtual void PersistEvents(List<swap_flow_event> events)
|
||||
{
|
||||
foreach (var evt in events)
|
||||
{
|
||||
DbContext.swap_flow_event.Add(evt);
|
||||
}
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
protected virtual IDisposable BeginTransaction()
|
||||
=> DbContext.Database.BeginTransaction();
|
||||
|
||||
protected virtual void CommitTransaction(IDisposable transaction)
|
||||
=> (transaction as Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction)?.Commit();
|
||||
|
||||
protected virtual void RollbackTransaction(IDisposable transaction)
|
||||
=> (transaction as Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction)?.Rollback();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 互换流水开平仓事件
|
||||
/// </summary>
|
||||
@@ -70,7 +117,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// 分页处理互换流水开平仓事件,暂时只按加权平均处理
|
||||
/// </summary>
|
||||
/// <param name="pageSize"></param>
|
||||
private List<swap_flow_event> MergePageEvent(int swapTradeId, List<swap_flow_merge> flowMergeList, DateTime tradeDate, bool needTrans = true)
|
||||
protected virtual List<swap_flow_event> MergePageEvent(int swapTradeId, List<swap_flow_merge> flowMergeList, DateTime tradeDate, bool needTrans = true)
|
||||
{
|
||||
List<swap_flow_event> flowEvents = new List<swap_flow_event>();
|
||||
//按照同一互换编码、标的、买卖方向排序,一条买,一条卖//会存在买卖不在同一页
|
||||
@@ -79,26 +126,22 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
return flowEvents;
|
||||
}
|
||||
var trade = DbContext.trade.Find(swapTradeId);
|
||||
var tradeExtend = DbContext.trade_extend.First(x => x.TradeId == swapTradeId);
|
||||
var trans = needTrans ? DbContext.Database.BeginTransaction() : null;
|
||||
var trade = FindTrade(swapTradeId);
|
||||
var tradeExtend = FindTradeExtend(swapTradeId);
|
||||
var trans = needTrans ? BeginTransaction() : null;
|
||||
try
|
||||
{
|
||||
var eodPositions = DbContext.swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.IsInitial && !x.Invalid).AsNoTracking().ToList();//上一日终持仓信息
|
||||
var swapFlowEventOlds = DbContext.swap_flow_event.Where(x => x.EventDate > tradeDate && x.SwapTradeId == swapTradeId && x.DataState > 0);//废弃当前清算日期及之后的开平仓事件
|
||||
swapFlowEventOlds.ForEach(x =>
|
||||
{
|
||||
x.DataState = (int)SwapFlowDateStateEnum.废弃;
|
||||
});
|
||||
var eodPositions = FindPositions(swapTradeId);//上一日终持仓信息
|
||||
FindAndInvalidateFutureEvents(swapTradeId, tradeDate);//废弃当前清算日期及之后的开平仓事件
|
||||
var mergeUnderlyingGroup = flowquery.GroupBy(g => g.UnderlyingCode);
|
||||
int direction = tradeExtend.ExtendObj.Direction;
|
||||
foreach (var underlyingGroup in mergeUnderlyingGroup)
|
||||
{
|
||||
var mergeList = underlyingGroup.OrderByDescending(o => o.TradingQty).ToList();//先按数量最大的排序
|
||||
var flowMerge = mergeList.First();
|
||||
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(flowMerge.UnderlyingCode);
|
||||
var underlying = GetUnderlying(flowMerge.UnderlyingCode);
|
||||
var matuirityDate = trade.ExerciseDate;
|
||||
var positionId = GetMaxPositionId(flowMerge, matuirityDate.Value, direction, trade.TradeNumber);
|
||||
var positionId = ResolvePositionId(flowMerge, matuirityDate.Value, direction, trade.TradeNumber);
|
||||
var payPosition = eodPositions.FirstOrDefault(x => x.PositionId == positionId);//浮动腿 日终持仓信息
|
||||
bool hasPayPosition = payPosition != null;//是否存在日终持仓
|
||||
if (mergeList.Count == 1)//只有一条流水
|
||||
@@ -208,12 +251,12 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
}
|
||||
}
|
||||
DbContext.SaveChanges();
|
||||
trans?.Commit();
|
||||
PersistEvents(flowEvents);
|
||||
if (trans != null) CommitTransaction(trans);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
trans?.Rollback();
|
||||
if (trans != null) RollbackTransaction(trans);
|
||||
throw new Exception(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
@@ -238,7 +281,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="PayFeeUnwindPnl">平仓浮动费用</param>
|
||||
/// <param name="allUnwind">是否完全平仓</param>
|
||||
/// <param name="settleRules">0 T+0 1 T+1</param>
|
||||
private swap_flow_event InitEvent(
|
||||
protected virtual swap_flow_event InitEvent(
|
||||
int eventType,
|
||||
swap_flow_merge flow_merge,
|
||||
int direction,
|
||||
@@ -287,7 +330,6 @@ namespace YLErp.Modules.SwapModule
|
||||
flow_Event.MarkClosePnl = PayMarkUnwindPnl;
|
||||
flow_Event.CloseFee = PayFeeUnwindPnl;
|
||||
flow_Event.DataState = (int)SwapFlowDateStateEnum.等待完成;
|
||||
DbContext.swap_flow_event.Add(flow_Event);
|
||||
return flow_Event;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user