using Newtonsoft.Json; using YLErp.DBModels; using YLErp.DBModels.Enums; namespace YLErp.Modules.SwapModule { /// /// 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 FlowEvents { get; set; } = new(); public List SwapEvents { get; set; } = new(); public List CashRecords { get; set; } = new(); public List DeletedRecords { get; } = new(); public StubService() : base(nameof(ClearSwapPositionsScenarioTest)) { } // override GetLegacyAutoSwapClientCashRecords 的内部依赖 protected override List FindFlowEventsByEventIds(List eventIds) { return FlowEvents.Where(x => x.EventId.HasValue && eventIds.Contains(x.EventId.Value)).ToList(); } protected override List 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 FindClientCashRecords(int tradeId) { return CashRecords.Where(x => x.TradeId == tradeId).ToList(); } // public 包装 public List ExecuteGetLegacyAutoSwapClientCashRecords( List swapEvents, List 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:自动互换资金记录被正确选中删除 // ================================================================ /// /// [CSW_001] 只有一条自动互换资金记录 → 应被选中删除 /// --------------------------------------------------------------- /// 构造1条自动互换事件(EventData含ClientCashIds), /// 1条资金记录(Action=系统操作-互换)。 /// GetLegacyAutoSwapClientCashRecords 应返回这条资金记录。 /// [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 { autoEvent }, excludedClientCashIds: new List()); Assert.AreEqual(1, result.Count, "应选中1条自动互换资金记录"); Assert.AreEqual(7001, result[0].id, "选中的应是id=7001"); Console.WriteLine($"自动互换资金记录(id=7001)被正确选中 ✅"); } // ================================================================ // 场景2:手动互换资金记录被排除(78751f0a 核心修复) // ================================================================ /// /// [CSW_002] 手动互换资金记录不被选中(manualClientCashIds排除) /// --------------------------------------------------------------- /// 构造1条自动互换事件 + 1条手动互换事件(ClientCashId=8001)。 /// 2条资金记录都是Action=系统操作-互换,但1条属于手动(id=8001)。 /// GetLegacyAutoSwapClientCashRecords 应只返回自动的那条,排除手动的。 /// [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 { autoEvent }, excludedClientCashIds: new List()); // 应只返回自动的(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 排除已处理的记录 // ================================================================ /// /// [CSW_003] 已在excludedClientCashIds中的记录不被重复选中 /// --------------------------------------------------------------- /// 资金记录id=7001已在excludedClientCashIds中(之前已处理过), /// 不应再次被选中。 /// [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 { autoEvent }, excludedClientCashIds: new List { 7001 }); Assert.AreEqual(1, result.Count, "应只选中1条(排除已处理的7001)"); Assert.AreEqual(7002, result[0].id, "选中的应是未处理的7002"); Console.WriteLine($"已处理记录(7001)被排除,只选中7002 ✅"); } // ================================================================ // 场景4:预付金返息记录也被正确处理 // ================================================================ /// /// [CSW_004] 预付金返息记录(Action=系统操作-预付金返息)也参与清理 /// --------------------------------------------------------------- /// 自动互换产生的预付金返息记录应被选中,手动的不应被选中。 /// [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(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 { autoEvent }, excludedClientCashIds: new List()); Assert.AreEqual(1, result.Count, "应只选中1条预付金返息(排除手动的)"); Assert.AreEqual(7001, result[0].id, "选中的应是自动的预付金返息7001"); Console.WriteLine($"预付金返息: 自动的(7001)被选中, 手动的(8001)被排除 ✅"); } } }