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);
- }
-
///
/// 处理浮动腿归档
///