- EodCheckSettlePrice 抽出 public static QueryPositionUnderlyingCodes, 增加 clienIds 过滤:client-scoped 收盘不再把其他客户/系统级(ClientId=0) 持仓标的纳入"检查标的结算价格缺失"。 - clienIds==null(系统级全结算)时短路为 true,零回归。 - class 改 public 便于单测。 - 新增 EodCheckSettlePriceClientScopeTest 确定性回归测试(红绿双段验证)。 - 代码补充 EQD-6967 防御行为详细注释(仅聚焦客户作用域收敛)。 - 防御范围说明:场内 exchange_trade 无 ClientId 列,场内分支噪声需靠结算模式选 '场外交易'(IsSettleExchangeTrades=false)规避,本提交不覆盖该分支。
117 lines
5.2 KiB
C#
117 lines
5.2 KiB
C#
using YLErp.Modules.EodModule.SettlementModule;
|
|
|
|
namespace YLErp.Modules.EodModule
|
|
{
|
|
/// <summary>
|
|
/// 验证 EodCheckSettlePrice 的持仓分支按 ClientIds 收敛:
|
|
/// 给定收盘客户时,不应再把“仅属于其他客户”的上一交易日持仓标的纳入结算价缺失检查。
|
|
///
|
|
/// 采用确定性夹具:插入两条合成持仓(客户A持标的A、客户B持标的B),直接调用抽出的
|
|
/// static 查询方法断言过滤语义,finally 中清理,避免依赖测试库现有数据形状。
|
|
/// 若 underlying_manager 无足够的对冲类型标的,则 Assert.Inconclusive 跳过。
|
|
/// </summary>
|
|
[TestClass]
|
|
public class EodCheckSettlePriceClientScopeTest : UnitTestBase
|
|
{
|
|
[TestMethod]
|
|
public void PositionUnderlyingQuery_ExcludesOtherClients_WhenClientIdsGiven()
|
|
{
|
|
using var db = DbContextFactory.GetYLDbContext();
|
|
|
|
// 选两个存在的、非期货的标的(避开 GetFutureTypes,保证通过方法内部的期货到期过滤);
|
|
// 持仓 TradeType 固定为"股票"(属于 TradeTypesForHedge),才能进入结算价检查。
|
|
var futureTypes = ConsGlobal.InstrumentType.GetFutureTypes();
|
|
var underlyings = db.underlying_manager
|
|
.Where(u => u.UnderlyingCode != null && !futureTypes.Contains(u.UnderlyingInstrumentType))
|
|
.Take(5)
|
|
.ToList();
|
|
if (underlyings.Count < 2)
|
|
{
|
|
Assert.Inconclusive("underlying_manager 无足够的非期货标的,跳过");
|
|
return;
|
|
}
|
|
var uA = underlyings[0];
|
|
var uB = underlyings[1];
|
|
|
|
// 复用一条现有持仓的 BookId/TradeId,确保外键合法(若存在)
|
|
var sample = db.eod_trade_position.FirstOrDefault(p => p.BookId != 0);
|
|
int bookId = sample?.BookId ?? 1;
|
|
int tradeId = sample?.TradeId ?? 0;
|
|
|
|
// 合成日期与客户,避免与测试库真实数据冲突
|
|
var preSettleDate = new DateTime(2026, 5, 1);
|
|
var settleDate = new DateTime(2026, 5, 2);
|
|
int clientA = 900001;
|
|
int clientB = 900002;
|
|
|
|
var rows = new List<eod_trade_position>
|
|
{
|
|
new eod_trade_position
|
|
{
|
|
ValueDate = preSettleDate,
|
|
ClientId = clientA,
|
|
UnderlyingCode = uA.UnderlyingCode,
|
|
UnderlyingId = uA.id,
|
|
TradeType = "股票",
|
|
BookId = bookId,
|
|
TradeId = tradeId,
|
|
Amount = 1,
|
|
HedgeUniqueCode = "UT_CLIENTSCOPE_A"
|
|
},
|
|
new eod_trade_position
|
|
{
|
|
ValueDate = preSettleDate,
|
|
ClientId = clientB,
|
|
UnderlyingCode = uB.UnderlyingCode,
|
|
UnderlyingId = uB.id,
|
|
TradeType = "股票",
|
|
BookId = bookId,
|
|
TradeId = tradeId,
|
|
Amount = 1,
|
|
HedgeUniqueCode = "UT_CLIENTSCOPE_B"
|
|
}
|
|
};
|
|
|
|
foreach (var r in rows)
|
|
{
|
|
r.OptId = 0;
|
|
r.OptName = "UT_CLIENTSCOPE";
|
|
r.OptDate = DateTime.Now;
|
|
}
|
|
|
|
try
|
|
{
|
|
db.eod_trade_position.AddRange(rows);
|
|
db.SaveChanges();
|
|
|
|
var fullSet = EodCheckSettlePrice.QueryPositionUnderlyingCodes(db, preSettleDate, settleDate, null)
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
var filteredA = EodCheckSettlePrice.QueryPositionUnderlyingCodes(db, preSettleDate, settleDate, new List<int> { clientA })
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
|
|
Assert.IsTrue(fullSet.Contains(uA.UnderlyingCode), "全客户结果应包含客户A的标的");
|
|
Assert.IsTrue(fullSet.Contains(uB.UnderlyingCode), "全客户结果应包含客户B的标的");
|
|
Assert.IsTrue(filteredA.Contains(uA.UnderlyingCode), "按客户A收敛后仍应包含客户A的标的");
|
|
// 关键断言:修复点——按客户A收敛后不应再包含“仅属客户B”的标的
|
|
Assert.IsFalse(filteredA.Contains(uB.UnderlyingCode),
|
|
"修复验证失败:按客户A收敛后仍包含仅属客户B的持仓标的(ClientId 过滤未生效)");
|
|
}
|
|
finally
|
|
{
|
|
// 清理合成数据,使测试库状态不变
|
|
foreach (var r in rows)
|
|
{
|
|
var exist = db.eod_trade_position.FirstOrDefault(x =>
|
|
x.ValueDate == preSettleDate && x.ClientId == r.ClientId &&
|
|
x.UnderlyingCode == r.UnderlyingCode && x.HedgeUniqueCode == r.HedgeUniqueCode);
|
|
if (exist != null)
|
|
{
|
|
db.eod_trade_position.Remove(exist);
|
|
}
|
|
}
|
|
db.SaveChanges();
|
|
}
|
|
}
|
|
}
|
|
}
|