- 新建 TestableSwapEodPositionService 收敛 8/8 Stub 重复的高频 override (PersistEodSwapPosition/SaveAllChanges/GetCurrencyRate/AddClientCash) + 统一 OptUserInfo 构造 + PersistedPositions/SaveChangesCount 输出捕获 - SwapEodPositionService.DealInterests 改 protected virtual(行为零变化) - 8 个 ScenarioTest 改为继承基类,删除重复 override - 消灭 DealInterestsScenarioTest/DealInterestsGoldenReplayTest 的反射调用 (typeof().GetMethod().Invoke → 直接调用 DealInterests) 验证:dotnet build 0 错误;dotnet test SwapModule 284通过/6跳过/0失败
254 lines
12 KiB
C#
254 lines
12 KiB
C#
using Newtonsoft.Json;
|
||
using YLErp.DBModels;
|
||
using YLErp.DBModels.Enums;
|
||
|
||
namespace YLErp.Modules.SwapModule
|
||
{
|
||
/// <summary>
|
||
/// ClearSwapPositions 重收盘清理 - 合成单元测试
|
||
/// ============================================================================
|
||
/// 验证 78751f0a 的核心修复:重收盘清理自动互换资金记录时,
|
||
/// 正确排除手动互换的资金记录(manualClientCashIds)。
|
||
///
|
||
/// 核心方法:GetLegacyAutoSwapClientCashRecords(SwapTradeBaseService)
|
||
/// 它接收自动互换事件列表,返回应该删除的资金记录。
|
||
/// 关键逻辑:cs:491 !manualClientCashIds.Contains(x.id) 排除手动互换的记录。
|
||
/// ============================================================================
|
||
[TestClass]
|
||
public class ClearSwapPositionsScenarioTest
|
||
{
|
||
private const int TradeId = 300;
|
||
|
||
#region Stub
|
||
|
||
private sealed class StubService : TestableSwapEodPositionService
|
||
{
|
||
// 注入的内存数据
|
||
public List<swap_flow_event> FlowEvents { get; set; } = new();
|
||
public List<swap_event> SwapEvents { get; set; } = new();
|
||
public List<ClientCashInCashOut> CashRecords { get; set; } = new();
|
||
|
||
public List<ClientCashInCashOut> DeletedRecords { get; } = new();
|
||
|
||
public StubService() : base(nameof(ClearSwapPositionsScenarioTest))
|
||
{
|
||
}
|
||
|
||
// override GetLegacyAutoSwapClientCashRecords 的内部依赖
|
||
protected override List<swap_flow_event> FindFlowEventsByEventIds(List<long> eventIds)
|
||
{
|
||
return FlowEvents.Where(x => x.EventId.HasValue && eventIds.Contains(x.EventId.Value)).ToList();
|
||
}
|
||
|
||
protected override List<int> FindManualClientCashIds(int swapTradeId)
|
||
{
|
||
return SwapEvents
|
||
.Where(x => x.SwapTradeId == swapTradeId
|
||
&& x.ClientCashId > 0
|
||
&& x.EventType != (int)SwapEventTypeEnum.自动互换)
|
||
.Select(x => x.ClientCashId)
|
||
.ToList();
|
||
}
|
||
|
||
protected override List<ClientCashInCashOut> FindClientCashRecords(int tradeId)
|
||
{
|
||
return CashRecords.Where(x => x.TradeId == tradeId).ToList();
|
||
}
|
||
|
||
// public 包装
|
||
public List<ClientCashInCashOut> ExecuteGetLegacyAutoSwapClientCashRecords(
|
||
List<swap_event> swapEvents, List<int> excludedClientCashIds)
|
||
{
|
||
return GetLegacyAutoSwapClientCashRecords(swapEvents, excludedClientCashIds);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 数据构建
|
||
|
||
private static swap_event CreateAutoSwapEvent(long id, DateTime valueDate, params int[] cashIds)
|
||
{
|
||
// SwapRealizedPnL=-100 匹配 Money=100(IsLegacyAutoSwapClientCashRecord 按金额校验)
|
||
var data = new UnwindData
|
||
{
|
||
SwapTradeId = TradeId, ValueDate = valueDate,
|
||
ClientCashIds = cashIds.ToList(),
|
||
SwapRealizedPnL = -100m // -Money → IsLegacyAutoSwapClientCashRecord 匹配
|
||
};
|
||
return new swap_event
|
||
{
|
||
id = id, SwapTradeId = TradeId, EventType = (int)SwapEventTypeEnum.自动互换,
|
||
ValueDate = valueDate, ClientCashId = cashIds.FirstOrDefault(),
|
||
EventData = JsonConvert.SerializeObject(data)
|
||
};
|
||
}
|
||
|
||
private static swap_event CreateManualSwapEvent(long id, DateTime valueDate, int clientCashId)
|
||
{
|
||
return new swap_event
|
||
{
|
||
id = id, SwapTradeId = TradeId, EventType = (int)SwapEventTypeEnum.互换,
|
||
ValueDate = valueDate, ClientCashId = clientCashId,
|
||
EventData = JsonConvert.SerializeObject(new UnwindData { SwapTradeId = TradeId, ValueDate = valueDate })
|
||
};
|
||
}
|
||
|
||
private static ClientCashInCashOut CreateCashRecord(int id, string action, DateTime happenDate, double money = 100)
|
||
{
|
||
return new ClientCashInCashOut
|
||
{
|
||
id = id, TradeId = TradeId, Action = action,
|
||
HappenDate = happenDate, Money = money,
|
||
State = ClientCashInCashOut.已确认, ValidState = "Valid"
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
|
||
// ================================================================
|
||
// 场景1:自动互换资金记录被正确选中删除
|
||
// ================================================================
|
||
|
||
/// <summary>
|
||
/// [CSW_001] 只有一条自动互换资金记录 → 应被选中删除
|
||
/// ---------------------------------------------------------------
|
||
/// 构造1条自动互换事件(EventData含ClientCashIds),
|
||
/// 1条资金记录(Action=系统操作-互换)。
|
||
/// GetLegacyAutoSwapClientCashRecords 应返回这条资金记录。
|
||
/// </summary>
|
||
[TestMethod]
|
||
public void CSW_001_自动互换资金记录被选中删除()
|
||
{
|
||
var date = new DateTime(2026, 6, 29);
|
||
var service = new StubService();
|
||
|
||
// 不含ClientCashIds → 走legacy路径
|
||
var autoEvent = CreateAutoSwapEvent(id: 5001, valueDate: date);
|
||
service.SwapEvents.Add(autoEvent);
|
||
|
||
var cashRecord = CreateCashRecord(id: 7001, action: ClientCashInCashOut.系统操作_互换, happenDate: date);
|
||
service.CashRecords.Add(cashRecord);
|
||
|
||
var result = service.ExecuteGetLegacyAutoSwapClientCashRecords(
|
||
new List<swap_event> { autoEvent }, excludedClientCashIds: new List<int>());
|
||
|
||
Assert.AreEqual(1, result.Count, "应选中1条自动互换资金记录");
|
||
Assert.AreEqual(7001, result[0].id, "选中的应是id=7001");
|
||
Console.WriteLine($"自动互换资金记录(id=7001)被正确选中 ✅");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景2:手动互换资金记录被排除(78751f0a 核心修复)
|
||
// ================================================================
|
||
|
||
/// <summary>
|
||
/// [CSW_002] 手动互换资金记录不被选中(manualClientCashIds排除)
|
||
/// ---------------------------------------------------------------
|
||
/// 构造1条自动互换事件 + 1条手动互换事件(ClientCashId=8001)。
|
||
/// 2条资金记录都是Action=系统操作-互换,但1条属于手动(id=8001)。
|
||
/// GetLegacyAutoSwapClientCashRecords 应只返回自动的那条,排除手动的。
|
||
/// </summary>
|
||
[TestMethod]
|
||
public void CSW_002_手动互换资金记录被排除()
|
||
{
|
||
var date = new DateTime(2026, 6, 29);
|
||
var service = new StubService();
|
||
|
||
// 自动互换事件(无ClientCashIds,走legacy路径)
|
||
var autoEvent = CreateAutoSwapEvent(id: 5001, valueDate: date);
|
||
// 手动互换事件(ClientCashId=8001)
|
||
var manualEvent = CreateManualSwapEvent(id: 5002, valueDate: date, clientCashId: 8001);
|
||
service.SwapEvents.Add(autoEvent);
|
||
service.SwapEvents.Add(manualEvent);
|
||
|
||
// 两条资金记录都是系统操作-互换
|
||
var autoCash = CreateCashRecord(id: 7001, action: ClientCashInCashOut.系统操作_互换, happenDate: date);
|
||
var manualCash = CreateCashRecord(id: 8001, action: ClientCashInCashOut.系统操作_互换, happenDate: date);
|
||
service.CashRecords.Add(autoCash);
|
||
service.CashRecords.Add(manualCash);
|
||
|
||
var result = service.ExecuteGetLegacyAutoSwapClientCashRecords(
|
||
new List<swap_event> { autoEvent }, excludedClientCashIds: new List<int>());
|
||
|
||
// 应只返回自动的(7001),排除手动的(8001)
|
||
Assert.AreEqual(1, result.Count, "应只选中1条(排除手动的)");
|
||
Assert.AreEqual(7001, result[0].id, "选中的应是自动的id=7001");
|
||
Assert.IsFalse(result.Any(x => x.id == 8001), "手动互换(id=8001)不应被选中");
|
||
Console.WriteLine($"手动互换资金记录(id=8001)被正确排除 ✅");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景3:excludedClientCashIds 排除已处理的记录
|
||
// ================================================================
|
||
|
||
/// <summary>
|
||
/// [CSW_003] 已在excludedClientCashIds中的记录不被重复选中
|
||
/// ---------------------------------------------------------------
|
||
/// 资金记录id=7001已在excludedClientCashIds中(之前已处理过),
|
||
/// 不应再次被选中。
|
||
/// </summary>
|
||
[TestMethod]
|
||
public void CSW_003_已处理的记录不重复选中()
|
||
{
|
||
var date = new DateTime(2026, 6, 29);
|
||
var service = new StubService();
|
||
|
||
var autoEvent = CreateAutoSwapEvent(id: 5001, valueDate: date);
|
||
service.SwapEvents.Add(autoEvent);
|
||
|
||
var cash1 = CreateCashRecord(id: 7001, action: ClientCashInCashOut.系统操作_互换, happenDate: date);
|
||
var cash2 = CreateCashRecord(id: 7002, action: ClientCashInCashOut.系统操作_互换, happenDate: date);
|
||
service.CashRecords.Add(cash1);
|
||
service.CashRecords.Add(cash2);
|
||
|
||
// 7001 已在 excludedClientCashIds 中
|
||
var result = service.ExecuteGetLegacyAutoSwapClientCashRecords(
|
||
new List<swap_event> { autoEvent }, excludedClientCashIds: new List<int> { 7001 });
|
||
|
||
Assert.AreEqual(1, result.Count, "应只选中1条(排除已处理的7001)");
|
||
Assert.AreEqual(7002, result[0].id, "选中的应是未处理的7002");
|
||
Console.WriteLine($"已处理记录(7001)被排除,只选中7002 ✅");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景4:预付金返息记录也被正确处理
|
||
// ================================================================
|
||
|
||
/// <summary>
|
||
/// [CSW_004] 预付金返息记录(Action=系统操作-预付金返息)也参与清理
|
||
/// ---------------------------------------------------------------
|
||
/// 自动互换产生的预付金返息记录应被选中,手动的不应被选中。
|
||
/// </summary>
|
||
[TestMethod]
|
||
public void CSW_004_预付金返息记录参与清理()
|
||
{
|
||
var date = new DateTime(2026, 6, 29);
|
||
var service = new StubService();
|
||
|
||
var autoEvent = CreateAutoSwapEvent(id: 5001, valueDate: date);
|
||
// 预付金返息匹配 SwapMarginRebatePnl,覆盖默认的 SwapRealizedPnL
|
||
var autoData = JsonConvert.DeserializeObject<UnwindData>(autoEvent.EventData);
|
||
autoData.SwapMarginRebatePnl = -100m;
|
||
autoData.SwapRealizedPnL = 0m;
|
||
autoEvent.EventData = JsonConvert.SerializeObject(autoData);
|
||
var manualEvent = CreateManualSwapEvent(id: 5002, valueDate: date, clientCashId: 8001);
|
||
service.SwapEvents.Add(autoEvent);
|
||
service.SwapEvents.Add(manualEvent);
|
||
|
||
// 预付金返息记录
|
||
var autoRebate = CreateCashRecord(id: 7001, action: ClientCashInCashOut.系统操作_预付金返息, happenDate: date);
|
||
var manualRebate = CreateCashRecord(id: 8001, action: ClientCashInCashOut.系统操作_预付金返息, happenDate: date);
|
||
service.CashRecords.Add(autoRebate);
|
||
service.CashRecords.Add(manualRebate);
|
||
|
||
var result = service.ExecuteGetLegacyAutoSwapClientCashRecords(
|
||
new List<swap_event> { autoEvent }, excludedClientCashIds: new List<int>());
|
||
|
||
Assert.AreEqual(1, result.Count, "应只选中1条预付金返息(排除手动的)");
|
||
Assert.AreEqual(7001, result[0].id, "选中的应是自动的预付金返息7001");
|
||
Console.WriteLine($"预付金返息: 自动的(7001)被选中, 手动的(8001)被排除 ✅");
|
||
}
|
||
}
|
||
}
|