diff --git a/UnitTestProject/Modules/SwapModule/InterestEodScenarioDispatchTest.cs b/UnitTestProject/Modules/SwapModule/InterestEodScenarioDispatchTest.cs index b0485614..8b4844ab 100644 --- a/UnitTestProject/Modules/SwapModule/InterestEodScenarioDispatchTest.cs +++ b/UnitTestProject/Modules/SwapModule/InterestEodScenarioDispatchTest.cs @@ -4,15 +4,12 @@ namespace YLErp.Modules.SwapModule { /// /// DealInterests 分派优先级(TEST-MATRIX §6 空洞补盖)。 - /// 纯函数 ResolveInterestScenario 的 8 组合表驱动单测,不连库、无 DB。 + /// 纯函数 InterestEodScenarioDispatch.ResolveInterestScenario 的 8 组合表驱动单测,不连库、无 DB。 /// 守卫:手工互换压制观察日自动结息;观察日±平仓区分 autoSwap 真/假;纯平仓与普通日分流。 - /// 测试类直接继承 TestableSwapEodPositionService 以访问 protected 的枚举与方法。 /// [TestClass] - public class InterestEodScenarioDispatchTest : TestableSwapEodPositionService + public class InterestEodScenarioDispatchTest { - public InterestEodScenarioDispatchTest() : base(nameof(InterestEodScenarioDispatchTest)) { } - [DataTestMethod] [DataRow(false, false, false, InterestEodScenario.RollForward)] // 普通日 [DataRow(false, false, true, InterestEodScenario.CloseOnly)] // 纯平仓(非观察日) @@ -25,7 +22,7 @@ namespace YLErp.Modules.SwapModule public void ResolveInterestScenario_CoversAllEightCombinations( bool hasInterval, bool hasSwap, bool hasClose, InterestEodScenario expected) { - var actual = ResolveInterestScenario(hasInterval, hasSwap, hasClose); + var actual = InterestEodScenarioDispatch.ResolveInterestScenario(hasInterval, hasSwap, hasClose); Assert.AreEqual(expected, actual, $"hasInterval={hasInterval}, hasSwap={hasSwap}, hasClose={hasClose}"); } diff --git a/YLErpDAL/Modules/SwapModule/InterestEodScenarioDispatch.cs b/YLErpDAL/Modules/SwapModule/InterestEodScenarioDispatch.cs new file mode 100644 index 00000000..3df6e857 --- /dev/null +++ b/YLErpDAL/Modules/SwapModule/InterestEodScenarioDispatch.cs @@ -0,0 +1,47 @@ +using YLErp.Models; + +namespace YLErp.Modules.SwapModule; + +/// +/// 利息腿 EOD 归档场景分派规格(纯函数集;表驱动单测见 InterestEodScenarioDispatchTest)。 +/// 优先级链(承重业务语义,不能乱序): +/// ① hasSwap(手工互换) → 按事件流水重新生成,压制观察日自动结息 +/// ② observationInterval(观察日) 存在 → 自动结息;同日有平仓 → autoSwap=true +/// ③ hasClose(纯平仓, 非观察日) → autoSwap=false +/// ④ 普通日 → 复制上一日终并计提当日新增 +/// 注意:hasSwap/hasClose 是交易级标志(整笔合约当天有无事件), +/// observationInterval 是腿级(本条利息腿当天是否观察日)——粒度不同,分派依赖此区分。 +/// 生产分派点:SwapEodPositionService.DealInterests(分派结构接线前为影子规格,见其分派处注释)。 +/// +public enum InterestEodScenario +{ + ManualSwap, // ① 手工互换:压制观察日自动结息 + AutoSettleWithClose, // ② 观察日 + 当日平仓 (autoSwap=true) + AutoSettle, // ② 观察日 + 当日无平仓 + CloseOnly, // ③ 非观察日 + 当日平仓 (autoSwap=false) + RollForward, // ④ 普通日滚动 +} + +public static class InterestEodScenarioDispatch +{ + /// 三分量布尔 → 场景(8 组合表驱动见 InterestEodScenarioDispatchTest)。 + public static InterestEodScenario ResolveInterestScenario(bool hasInterval, bool hasSwap, bool hasClose) + { + if (hasSwap) + return InterestEodScenario.ManualSwap; + if (hasInterval) + return hasClose ? InterestEodScenario.AutoSettleWithClose : InterestEodScenario.AutoSettle; + if (hasClose) + return InterestEodScenario.CloseOnly; + return InterestEodScenario.RollForward; + } + + /// + /// 查找利息腿在指定结算日的观察日信息(SwapIntervalList 中 Date==settleDate 且 Settlement==1 的记录)。 + /// 观察日即自动结息触发日;返回 null 表示当日非观察日。分派见 ResolveInterestScenario。 + /// + public static IntervalModel FindObservationInterval(swap_position interest, DateTime settleDate) + { + return interest.SwapIntervalList.FirstOrDefault(x => x.Date == settleDate && x.Settlement == 1); + } +} diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs index 5a3ac28b..a7992ee2 100644 --- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs @@ -375,7 +375,7 @@ namespace YLErp.Modules.SwapModule IntervalModel observationInterval = null; foreach (var interest in interestList) { - observationInterval = FindObservationInterval(interest, settleDate); + observationInterval = InterestEodScenarioDispatch.FindObservationInterval(interest, settleDate); if (observationInterval != null) break; } @@ -490,7 +490,7 @@ namespace YLErp.Modules.SwapModule } var eodPosition = eodPositions.FirstOrDefault(x => x.PositionId == interest.id);//上一日日终利息信息 可能不存在 var tdEodPosition = todyEodPositions.FirstOrDefault(x => x.PositionId == interest.id);//当前结算日日终利息信息 - var observationInterval = FindObservationInterval(interest, settleDate);//自动互换观察日信息 + var observationInterval = InterestEodScenarioDispatch.FindObservationInterval(interest, settleDate);//自动互换观察日信息 List dealInterests = new List(); dealInterests.AddRange(flowEvents); var dealInterest = dealInterests.FirstOrDefault(n => n.PositionId == interest.id);//当日是否做过互换或平仓 @@ -533,45 +533,6 @@ namespace YLErp.Modules.SwapModule } } - /// - /// 利息腿 EOD 归档场景分派(纯函数;表驱动单测见 InterestEodScenarioDispatchTest)。 - /// 优先级链(承重业务语义,不能乱序): - /// ① hasSwap(手工互换) → 按事件流水重新生成,压制观察日自动结息 - /// ② observationInterval(观察日) 存在 → 自动结息;同日有平仓 → autoSwap=true - /// ③ hasClose(纯平仓, 非观察日) → autoSwap=false - /// ④ 普通日 → 复制上一日终并计提当日新增 - /// 注意:hasSwap/hasClose 是交易级标志(整笔合约当天有无事件), - /// observationInterval 是腿级(本条利息腿当天是否观察日)——粒度不同,分派依赖此区分。 - /// - public enum InterestEodScenario - { - ManualSwap, // ① 手工互换:压制观察日自动结息 - AutoSettleWithClose, // ② 观察日 + 当日平仓 (autoSwap=true) - AutoSettle, // ② 观察日 + 当日无平仓 - CloseOnly, // ③ 非观察日 + 当日平仓 (autoSwap=false) - RollForward, // ④ 普通日滚动 - } - - protected static InterestEodScenario ResolveInterestScenario(bool hasInterval, bool hasSwap, bool hasClose) - { - if (hasSwap) - return InterestEodScenario.ManualSwap; - if (hasInterval) - return hasClose ? InterestEodScenario.AutoSettleWithClose : InterestEodScenario.AutoSettle; - if (hasClose) - return InterestEodScenario.CloseOnly; - return InterestEodScenario.RollForward; - } - - /// - /// 查找利息腿在指定结算日的观察日信息(SwapIntervalList 中 Date==settleDate 且 Settlement==1 的记录)。 - /// 观察日即自动结息触发日;返回 null 表示当日非观察日。分派见 ResolveInterestScenario。 - /// - protected static IntervalModel FindObservationInterval(swap_position interest, DateTime settleDate) - { - return interest.SwapIntervalList.FirstOrDefault(x => x.Date == settleDate && x.Settlement == 1); - } - /// /// 处理浮动腿归档 ///