fix: eod_commodity_future_price 入库前以 UnderlyingCode 重派生 UnderlyingId,防止 FR007 等价格行 UnderlyingId 与 FutureContractId 失同步

- 新增 EodPriceService.SyncUnderlyingIdFromCode:入库前以 UnderlyingCode(FutureContractId) 为准反查真实 UnderlyingId,不一致则告警并自动校正
- SaveEodFuturePrice(web编辑/新增)、SettlementPriceImportService(xlsx上传)、EodSyntheticPriceSaveService(系统合成价) 写入前调用
- 根治'网页端能查到、EOD结算查不到'的失同步问题
This commit is contained in:
hjhan
2026-07-17 10:32:57 +08:00
parent f37a5b997c
commit 75988a33eb
3 changed files with 40 additions and 4 deletions
+35 -4
View File
@@ -1,8 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using BaseOUDAL;
using DocumentFormat.OpenXml.Bibliography;
using NPOI.POIFS.NIO;
using YLErp.BLL;
using YLErp.Helpers;
namespace YLErp.Modules.EodModule
@@ -32,6 +29,37 @@ namespace YLErp.Modules.EodModule
return (start, end);
}
/// <summary>
/// 校正 eod_commodity_future_price 行的 UnderlyingId,使其与 UnderlyingCode(=FutureContractId) 一致。
/// 背景:网页日终价格列表按 UnderlyingId(int) JOIN underlying_manager,而 EOD 结算(EodPriceProvider.Initialize)
/// 按 UnderlyingCode(string) JOIN。两列一旦失同步(典型如 FR007 的价格行 UnderlyingId 被错写成 511160.SH 的 id)
/// 会出现"网页能查到、结算却查不到"的错价缺失,进而 EodCheckSettlePrice 报"结算价格缺失"。
/// 这里以 UnderlyingCode 为准重新派生 UnderlyingId——该列才是上传/结算使用的自然键(FutureContractId)
/// 在入库前强制两列一致,既阻止产生新的错行,又通过告警日志把失同步暴露给运维追查上游写入来源。
/// </summary>
public static void SyncUnderlyingIdFromCode(YLContext db, eod_commodity_future_price row)
{
if (row == null || string.IsNullOrWhiteSpace(row.UnderlyingCode))
{
return;
}
var um = db.underlying_manager.FirstOrDefault(u => u.UnderlyingCode == row.UnderlyingCode);
if (um == null)
{
// 标的代码在 underlying_manager 不存在:无法校正,交由既有"标的代码不存在"等校验处理。
return;
}
if (row.UnderlyingId != um.id)
{
LogFactory.GetLogger("EodPrice").Info(
$"eod_commodity_future_price.UnderlyingId 与 UnderlyingCode 不一致,已自动校正: " +
$"FutureContractId={row.UnderlyingCode}, 原UnderlyingId={row.UnderlyingId}, 修正为={um.id}");
row.UnderlyingId = um.id;
}
}
public SearchListResult<EodUnderlyingPriceDto> SearchUnderlyingList(EodCommodityFuturePriceReq req)
{
var (valueDtStart, valueDtEnd) = ResolveValueDateWindow(req.ValueDateStart, req.ValueDateEnd);
@@ -219,6 +247,9 @@ namespace YLErp.Modules.EodModule
dbmodel.DataSource = EodPriceBase.;
// 入库前强制 UnderlyingId 与 UnderlyingCode(FutureContractId) 一致,避免网页/结算两套 JOIN 失同步。
SyncUnderlyingIdFromCode(DbContext, dbmodel);
DbContext.SaveChanges();
return dbmodel;
@@ -159,6 +159,9 @@ namespace YLErp.Modules.EodModule.SettlementModule
DataSource = EodPriceBase.
};
// 入库前强制 UnderlyingId 与 UnderlyingCode(FutureContractId) 一致,避免网页/结算两套 JOIN 失同步。
EodPriceService.SyncUnderlyingIdFromCode(DbContext, dto.CommodityPrice);
DbContext.eod_commodity_future_price.Add(dto.CommodityPrice);
}
@@ -158,6 +158,8 @@ namespace YLErp.Modules.EodModule
eodPrice.ValueDate = item.date;
eodPrice.UnderlyingCode = underlying.UnderlyingCode;
eodPrice.UnderlyingId = underlying.id;
// 入库前强制 UnderlyingId 与 UnderlyingCode(FutureContractId) 一致,避免网页/结算两套 JOIN 失同步。
EodPriceService.SyncUnderlyingIdFromCode(DbContext, eodPrice);
if (item.closePrice.HasValue)
eodPrice.ClosePrice = item.closePrice ?? 0;
if (item.settlePrice.HasValue)