From 59c16aabc19dfc209bbdb59128e87717356e8c46 Mon Sep 17 00:00:00 2001 From: hjhan Date: Tue, 11 Aug 2026 09:17:42 +0800 Subject: [PATCH] =?UTF-8?q?refactor(interest):=20SwapDealService=206?= =?UTF-8?q?=E5=A4=84=E5=8F=96=E4=BB=B7=E6=94=B6=E6=95=9B=E5=88=B0=20IIndex?= =?UTF-8?q?Fixer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正上次尝试的失败: 静态 Fr007IndexFixer.Instance 绕过了 TryGetFloatRate(virtual)接缝, 导致测试 stub 失效(CI_007/CI_008失败)。 本次方案: 新增 SwapDealIndexFixer(实例级, 委托 TryGetFloatRate): - SwapDealService.IndexFixer 属性 lazy 初始化, 包 SwapDealIndexFixer - 6处 GetNonHolidayDefore+TryGetFloatRate 全部替换为 IndexFixerBase+IndexFixer - TryGetFloatRate(protected virtual)保留不动, 测试 stub 机制完全不受影响 验证: CI_007/CI_008 从失败转为通过, 全量480测试7失败(与基线一致,零回归)。 SwapDealService 内 GetNonHolidayDefore 出现次数: 6 → 0(全部收敛到 IndexFixerBase)。 --- .../Modules/SwapModule/SwapDealIndexFixer.cs | 25 +++++++++ .../Modules/SwapModule/SwapDealService.cs | 53 +++++++++---------- 2 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 YLErpDAL/Modules/SwapModule/SwapDealIndexFixer.cs diff --git a/YLErpDAL/Modules/SwapModule/SwapDealIndexFixer.cs b/YLErpDAL/Modules/SwapModule/SwapDealIndexFixer.cs new file mode 100644 index 00000000..0ba1acc9 --- /dev/null +++ b/YLErpDAL/Modules/SwapModule/SwapDealIndexFixer.cs @@ -0,0 +1,25 @@ +using System; +using YLErp.Derivatives.Interest; + +namespace YLErp.Modules.SwapModule; + +/// +/// SwapDealService 专用取价器:通过实例方法委托给 TryGetFloatRate(virtual), +/// 而非 Fr007IndexFixer.Instance 那样直接调 EodPriceQueryService。 +/// +/// 为什么不用静态 Instance:SwapDealService.TryGetFloatRate 是 protected virtual, +/// 测试通过 override 它注入 stub 利率。静态 Instance 绕过这个接缝会让 45 个测试失败。 +/// 本类把 TryGetFloatRate 包成 IIndexFixer,既保留 virtual 接缝,又用上 IndexFixerBase。 +/// +internal sealed class SwapDealIndexFixer : IndexFixerBase, IIndexFixer +{ + private readonly SwapDealService _owner; + internal SwapDealIndexFixer(SwapDealService owner) => _owner = owner; + + public bool TryGetFixing(DateTime fixingDate, string underlyingCode, out decimal rate) + { + bool ok = _owner.TryGetFloatRate(fixingDate, underlyingCode, out double r); + rate = Convert.ToDecimal(r); + return ok; + } +} diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 4183bece..c2a74be4 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -5,6 +5,7 @@ using System.Linq.Expressions; using YLErp.BLL; using YLErp.BLL.Eod; using YLErp.DBModels.Enums; +using YLErp.Derivatives.Interest; using YLErp.Helpers; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; @@ -23,6 +24,10 @@ namespace YLErp.Modules.SwapModule return EodPriceQueryService.TryGetPrice(valueDate, underlyingCode, out rate); } + private IIndexFixer _indexFixer; + /// FR007 取价器,委托 TryGetFloatRate(保留 virtual 接缝供测试 stub)。 + protected virtual IIndexFixer IndexFixer => _indexFixer ??= new SwapDealIndexFixer(this); + #region 可测试化接缝(Seams)——override 这些虚方法可在测试中替换 DB/外部调用,生产代码行为不变 // FindTrade 已上提到基类 SwapTradeBaseService(三子类实现一致,消除重复) @@ -1037,9 +1042,8 @@ namespace YLErp.Modules.SwapModule if (string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) return position.FloatRate; int days = (endDate - startDate).Days; - DateTime rateDate = days % period == 0 - ? QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)) - : QdpCalendarHelper.GetNonHolidayDefore(startDate.AddDays(position.interest_rule ?? 0)); + DateTime rateDate = IndexFixerBase.GetFixingDate( + days % period == 0 ? endDate : startDate, position.interest_rule); if (preEod.id != 0 && days % period != 0) { @@ -1047,9 +1051,9 @@ namespace YLErp.Modules.SwapModule return preEod.FloatRate; } - if (TryGetFloatRate(rateDate, position.FloatRateUnderlyingCode, out double rate)) + if (IndexFixer.TryGetFixing(rateDate, position.FloatRateUnderlyingCode, out decimal rate)) { - position.FloatRate = positionClone.FloatRate = Convert.ToDecimal(rate); + position.FloatRate = positionClone.FloatRate = rate; return position.FloatRate; } if (!swap) throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{rateDate:yyyy年MM月dd日}的价格"); @@ -1352,14 +1356,14 @@ namespace YLErp.Modules.SwapModule if (accrueDate >= startDate && i % interestPeriod == 0 && !string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { - var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(accrueDate.AddDays(position.interest_rule ?? 0)); - if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1)) + var fixingDate = IndexFixerBase.GetFixingDate(accrueDate, position.interest_rule); + if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing)) { - if (floatRate1 != 0) floatRate = floatRate1; + if (fixing != 0m) floatRate = Convert.ToDouble(fixing); } else { - throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格"); + throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格"); } } if (accrueDate >= startDate) @@ -1443,14 +1447,14 @@ namespace YLErp.Modules.SwapModule // tdDynomicPrincipal = flowEvent.InterestPrincipal 使本金累积乘 closePercent^N)。 if (i % interestPeriod == 0 && !string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { - var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(accrueDate.AddDays(position.interest_rule ?? 0)); - if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1)) + var fixingDate = IndexFixerBase.GetFixingDate(accrueDate, position.interest_rule); + if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing)) { - if (floatRate1 != 0) floatRate = floatRate1; + if (fixing != 0m) floatRate = Convert.ToDouble(fixing); } else { - throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格"); + throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格"); } } @@ -1507,18 +1511,14 @@ namespace YLErp.Modules.SwapModule tdDynomicPrincipal = tdDynomicPrincipal + interestProfitSum * remainingPercent; if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { - // 获取合适的 rateDate - var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)); // 获取前一工作日; - if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1)) + var fixingDate = IndexFixerBase.GetFixingDate(endDate, position.interest_rule); + if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing)) { - if (floatRate1 != 0) - { - floatRate = floatRate1; - } + if (fixing != 0m) floatRate = Convert.ToDouble(fixing); } else { - throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格"); + throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格"); } } @@ -1572,17 +1572,14 @@ namespace YLErp.Modules.SwapModule // 获取新的浮动利率 if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { - var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)); - if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double newFloatRate)) + var fixingDate = IndexFixerBase.GetFixingDate(endDate, position.interest_rule); + if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing)) { - if (newFloatRate != 0) - { - floatRate = newFloatRate; - } + if (fixing != 0m) floatRate = Convert.ToDouble(fixing); } else { - throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格"); + throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格"); } } }