diff --git a/UnitTestProject/Modules/EodModule/EodPriceDtoTest.cs b/UnitTestProject/Modules/EodModule/EodPriceDtoTest.cs
index bbab1029..6f64b3f5 100644
--- a/UnitTestProject/Modules/EodModule/EodPriceDtoTest.cs
+++ b/UnitTestProject/Modules/EodModule/EodPriceDtoTest.cs
@@ -202,5 +202,53 @@ namespace YLErp.Modules.EodModule
}
#endregion
+
+ #region 日期窗口解析("页面始终5条"根因锁定,纯单测不连库)
+
+ [TestMethod]
+ [Description("前端未传日期(年份<=2000) → 回退到 [今天-1年, 今天+1年)")]
+ public void 未传日期_回退最近一年到明年()
+ {
+ var (start, end) = EodPriceService.ResolveValueDateWindow(DateTime.MinValue, DateTime.MinValue);
+ Assert.AreEqual(DateTime.Today.AddYears(-1).Date, start.Date, "起始应回退到今天-1年");
+ Assert.AreEqual(DateTime.Today.AddYears(1).Date, end.Date, "结束应回退到今天+1年");
+ Assert.IsTrue(end > start, "窗口应正向");
+ }
+
+ [TestMethod]
+ [Description("列表页默认起止都填今天 → 窗口=[今天, 今天+1天),仅返回当天记录(即'5条'现象成因)")]
+ public void 起止都填今天_窗口仅今天()
+ {
+ var today = DateTime.Today;
+ var (start, end) = EodPriceService.ResolveValueDateWindow(today, today);
+ Assert.AreEqual(today.Date, start.Date, "起始应为今天");
+ Assert.AreEqual(today.AddDays(1).Date, end.Date, "结束应为今天+1天(半开区间含今天)");
+ Assert.IsTrue(today >= start && today < end, "今天的记录应落入窗口");
+ Assert.IsFalse(today.AddDays(-1) >= start && today.AddDays(-1) < end, "昨天的记录不应落入仅今天窗口");
+ Assert.IsFalse(today.AddDays(1) >= start && today.AddDays(1) < end, "明天的记录不应落入仅今天窗口");
+ }
+
+ [TestMethod]
+ [Description("显式传区间(如近30天) → 原样生效,不被回退覆盖")]
+ public void 显式区间_原样生效()
+ {
+ var start0 = DateTime.Today.AddDays(-30);
+ var end0 = DateTime.Today;
+ var (start, end) = EodPriceService.ResolveValueDateWindow(start0, end0);
+ Assert.AreEqual(start0.Date, start.Date, "起始应等于传入");
+ Assert.AreEqual(end0.AddDays(1).Date, end.Date, "结束应等于传入+1天");
+ }
+
+ [TestMethod]
+ [Description("结束日=今天 → 半开区间上界=今天+1天,今天当天记录可命中")]
+ public void 结束日今天_上界为明天_当天可命中()
+ {
+ var (start, end) = EodPriceService.ResolveValueDateWindow(DateTime.Today.AddDays(-365), DateTime.Today);
+ var today = DateTime.Today;
+ Assert.IsTrue(today >= start && today < end, "今天记录应命中");
+ Assert.IsFalse(today.AddDays(1) >= start && today.AddDays(1) < end, "明天记录不应命中");
+ }
+
+ #endregion
}
}
diff --git a/UnitTestProject/Modules/EodModule/EodPriceGoldenReplayTest.cs b/UnitTestProject/Modules/EodModule/EodPriceGoldenReplayTest.cs
index cb2b19ee..bf5a0633 100644
--- a/UnitTestProject/Modules/EodModule/EodPriceGoldenReplayTest.cs
+++ b/UnitTestProject/Modules/EodModule/EodPriceGoldenReplayTest.cs
@@ -187,5 +187,78 @@ namespace YLErp.Modules.EodModule
}
#endregion
+
+ #region 回归:新增日终价格可见性(连真实库,标 [Ignore] 手动跑)
+
+ ///
+ /// 回归"新增日终价格后是否查得出",直接跑生产查询 SearchUnderlyingList。
+ /// 守护点(与之前"新增后查不出"的修复一一对应):
+ /// (a) 今天 + 已上市(LaunchState=1) 标的 → 查得出;
+ /// (b) 估值日期=0001(未填) → 落在列表默认"仅今天"窗口外 → 查不出;
+ /// (c) 标的未上市(LaunchState!=1) → 被 inner join(underlying_manager.LaunchState=="1") 过滤 → 查不出。
+ /// 复用库中已有标的(不新建 underlying_manager,避免触碰该表约束),只插入/清理临时债券估值行。
+ ///
+ [TestMethod]
+ [Ignore]
+ [TestCategory("EodVisibility")]
+ [Description("新增日终价格可见性:(a)今天+已上市可查 (b)日期0001查不出 (c)未上市查不出")]
+ public void Record_NewRecordVisibility()
+ {
+ using (var db = DbContextFactory.GetYLDbContext())
+ {
+ var svc = new EodPriceService(OptUserInfo.SystemUser);
+ var today = DateTime.Today;
+ var req = new EodCommodityFuturePriceReq { ValueDateStart = today, ValueDateEnd = today };
+
+ // 取一个已上市的债券类标的(正向用例);退而求其次取任意已上市标的
+ var listedBond = db.underlying_manager
+ .FirstOrDefault(x => x.LaunchState == "1" && x.UnderlyingInstrumentType == ConsGlobal.InstrumentType.CreditBonds)
+ ?? db.underlying_manager.FirstOrDefault(x => x.LaunchState == "1");
+ Assert.IsNotNull(listedBond, "需存在一个 LaunchState=1 的标的用于正向回归");
+
+ // 取一个未上市的标的(负向用例)
+ var unlisted = db.underlying_manager.FirstOrDefault(x => x.LaunchState != "1");
+ Assert.IsNotNull(unlisted, "需存在一个 LaunchState!=1 的标的用于负向回归");
+
+ var insertedIds = new List();
+ try
+ {
+ // (a) 今天 + 已上市 → 查得出
+ var a = new ChinaBondValuation { bond_id = listedBond.UnderlyingCode, valuation_date = today, dirty_price_close = 100, net_price = 100, yield = 3 };
+ db.china_bond_valuation.Add(a);
+ db.SaveChanges();
+ insertedIds.Add(a.id);
+ var rA = svc.SearchUnderlyingList(req);
+ Assert.IsTrue(rA.rows.Any(x => x.id == a.id), "(a) 今天+已上市债券应查得出");
+
+ // (b) 日期=0001(未填) → 落在仅今天窗口外,查不出
+ var b = new ChinaBondValuation { bond_id = listedBond.UnderlyingCode, valuation_date = DateTime.MinValue, dirty_price_close = 100, net_price = 100, yield = 3 };
+ db.china_bond_valuation.Add(b);
+ db.SaveChanges();
+ insertedIds.Add(b.id);
+ var rB = svc.SearchUnderlyingList(req);
+ Assert.IsFalse(rB.rows.Any(x => x.id == b.id), "(b) 日期0001 应查不出");
+
+ // (c) 未上市标的 → 被 inner join 过滤,查不出
+ var c = new ChinaBondValuation { bond_id = unlisted.UnderlyingCode, valuation_date = today, dirty_price_close = 100, net_price = 100, yield = 3 };
+ db.china_bond_valuation.Add(c);
+ db.SaveChanges();
+ insertedIds.Add(c.id);
+ var rC = svc.SearchUnderlyingList(req);
+ Assert.IsFalse(rC.rows.Any(x => x.id == c.id), "(c) 未上市标的应查不出");
+ }
+ finally
+ {
+ foreach (var id in insertedIds)
+ {
+ var e = db.china_bond_valuation.Find(id);
+ if (e != null) db.china_bond_valuation.Remove(e);
+ }
+ db.SaveChanges();
+ }
+ }
+ }
+
+ #endregion
}
}
diff --git a/YLErpDAL/Modules/EodModule/EodPriceService.cs b/YLErpDAL/Modules/EodModule/EodPriceService.cs
index 104847dc..7e2146dc 100644
--- a/YLErpDAL/Modules/EodModule/EodPriceService.cs
+++ b/YLErpDAL/Modules/EodModule/EodPriceService.cs
@@ -17,10 +17,24 @@ namespace YLErp.Modules.EodModule
}
+ ///
+ /// 解析日终价格列表的"估值日期"查询窗口。抽成 static 以便纯单测锁定行为(避免改坏)。
+ /// 规则:
+ /// - 起始日期年份 > 2000(前端传了有效日期)→ 用传入值;否则回退到 今天-1年。
+ /// - 结束日期年份 > 2000 → 用传入值+1天(闭区间转半开);否则回退到 今天+1年。
+ /// 注意:列表页默认把起止都设成"今天",于是窗口=[今天, 今天+1天)=仅今天 → 仅返回当天的记录
+ /// (即"页面始终5条"现象的真正成因,非分页/查询 bug)。要看历史须把起始日期调早。
+ ///
+ public static (DateTime start, DateTime end) ResolveValueDateWindow(DateTime reqStart, DateTime reqEnd)
+ {
+ var start = reqStart.Year > 2000 ? reqStart : DateTime.Today.AddYears(-1);
+ var end = reqEnd.Year > 2000 ? reqEnd.AddDays(1) : DateTime.Today.AddYears(1);
+ return (start, end);
+ }
+
public SearchListResult SearchUnderlyingList(EodCommodityFuturePriceReq req)
{
- var valueDtStart = req.ValueDateStart.Year > 2000 ? req.ValueDateStart : DateTime.Today.AddYears(-1);
- var valueDtEnd = req.ValueDateEnd.Year > 2000 ? req.ValueDateEnd.AddDays(1) : DateTime.Today.AddYears(1);
+ var (valueDtStart, valueDtEnd) = ResolveValueDateWindow(req.ValueDateStart, req.ValueDateEnd);
var predicatUn = PredicateBuilder.Create(d => d.LaunchState == "1");
var predicatEoc = PredicateBuilder.Create(source => source.ValueDate >= valueDtStart && source.ValueDate < valueDtEnd);