using System.Collections.Generic;
using System.Linq;
using BaseOUDAL;
using DocumentFormat.OpenXml.Bibliography;
using NPOI.POIFS.NIO;
using YLErp.Helpers;
namespace YLErp.Modules.EodModule
{
///
/// 日终价格服务
///
public class EodPriceService : YLBaseService
{
public EodPriceService(OptUserInfo userInfo) : base(userInfo)
{
}
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 predicatUn = PredicateBuilder.Create(d => d.LaunchState == "1");
var predicatEoc = PredicateBuilder.Create(source => source.ValueDate >= valueDtStart && source.ValueDate < valueDtEnd);
var predicatEot = PredicateBuilder.Create(source => source.ValueDate >= valueDtStart && source.ValueDate < valueDtEnd);
var predicatEob = PredicateBuilder.Create(source => source.valuation_date >= valueDtStart && source.valuation_date < valueDtEnd);
if (!string.IsNullOrEmpty(req.DataSource))
{
predicatEoc = predicatEoc.And(d => d.DataSource.Contains(req.DataSource));
predicatEot = predicatEot.And(d => d.DataSource.Contains(req.DataSource));
// 债券来源恒为"中债估值"(聚源仅转发,无人手工维护),
// 只有按"中债估值"筛选时才命中债券;人工/系统筛选不命中债券。
if (req.DataSource != EodPriceBase.中债估值)
{
predicatEob = predicatEob.And(d => false);
}
}
if (!string.IsNullOrEmpty(req.MarketName))
{
predicatUn = predicatUn.And(d => d.MarketName == req.MarketName);
}
if (!string.IsNullOrEmpty(req.UnderlyingCode))
{
predicatUn = predicatUn.And(d => d.UnderlyingCode.Contains(req.UnderlyingCode));
}
var queryUn = DbContext.underlying_manager.Where(predicatUn).Select(n => new { n.id, n.LaunchState, n.MarketName, n.UnderlyingState, n.UnderlyingType, n.UnderlyingCode, n.UnderlyingName,n.UnderlyingInstrumentType });
var query1 = from un in queryUn
join source in DbContext.eod_commodity_future_price.Where(predicatEoc) on un.id equals source.UnderlyingId
select new EodUnderlyingPriceDto
{
IsBond=false,
id = source.id,
DataSource = source.DataSource,
LaunchState = un.LaunchState,
MarketName = un.MarketName,
UnderlyingId = un.id,
UnderlyingCode = un.UnderlyingCode,
UnderlyingName = un.UnderlyingName,
UnderlyingState = un.UnderlyingState,
UnderlyingType = un.UnderlyingType,
UnderlyingInstrumentType = "CommodityFutures",
RealInstrumentType = un.UnderlyingInstrumentType,
ValueDate = source.ValueDate,
SettlePrice = source.SettlePrice,
ClosePrice = source.ClosePrice,
UpdateTime = source.OptDate,
ReferencePrice = source.ReferencePrice,
SourceTime = source.SourceTime,
DeciClosePrice=0,
DeciSettlePrice = 0,
DeciReferencePrice=0
};
var query2 = from un in queryUn
join stockClose in DbContext.eod_stock_price.Where(predicatEot) on un.UnderlyingCode equals stockClose.UnderlyingCode
select new EodUnderlyingPriceDto
{
IsBond = false,
id = stockClose.id,
DataSource = stockClose.DataSource,
LaunchState = un.LaunchState,
MarketName = un.MarketName,
UnderlyingId = un.id,
UnderlyingCode = un.UnderlyingCode,
UnderlyingName = un.UnderlyingName,
UnderlyingState = un.UnderlyingState,
UnderlyingType = un.UnderlyingType,
UnderlyingInstrumentType = "Stock",
RealInstrumentType = un.UnderlyingInstrumentType,
ValueDate = stockClose.ValueDate,
SettlePrice = stockClose.ClosePrice,
ClosePrice = stockClose.ClosePrice,
UpdateTime = stockClose.OptDate,
ReferencePrice = stockClose.ReferencePrice,
SourceTime = stockClose.SourceTime,
DeciClosePrice = 0,
DeciSettlePrice = 0,
DeciReferencePrice = 0
};
var query3 = from un in queryUn
join bondClose in DbContext.china_bond_valuation.Where(predicatEob) on un.UnderlyingCode equals bondClose.bond_id
select new EodUnderlyingPriceDto
{
IsBond = true,
id = bondClose.id,
UpdateUser = bondClose.update_user,
// 债券 DataSource 在后处理统一置为固定值"中债估值"(见下方 foreach)。
LaunchState = un.LaunchState,
MarketName = un.MarketName,
UnderlyingId = un.id,
UnderlyingCode = un.UnderlyingCode,
UnderlyingName = un.UnderlyingName,
UnderlyingState = un.UnderlyingState,
UnderlyingType = un.UnderlyingType,
UnderlyingInstrumentType = un.UnderlyingInstrumentType,
RealInstrumentType = un.UnderlyingInstrumentType,
ValueDate = bondClose.valuation_date,
SettlePrice=0,
DeciSettlePrice =bondClose.net_price,
ClosePrice=0,
DeciClosePrice = bondClose.dirty_price_close,
UpdateTime = bondClose.update_time,
ReferencePrice=0,
DeciReferencePrice = bondClose.yield,
SourceTime=""
};
var unionQuery = query1.Concat(query2);
var finalQuery = unionQuery.Concat(query3);
if (string.IsNullOrEmpty(req.sidx))
{
req.sidx = "ValueDate";
req.sord = "desc";
}
var result = finalQuery.ToSearchList(req);
// 解析手工改过估值的债券操作人姓名:update_user 非空 = 被人手工改过(聚源同步为 NULL)。
// 此时来源列显示改这个人名,而非"中债估值",以便溯源到具体操作人。
var manualBondRows = result.rows.Where(r => r.IsBond && r.UpdateUser.HasValue).ToList();
Dictionary operatorNameMap = null;
if (manualBondRows.Any())
{
var operatorIds = manualBondRows.Select(r => r.UpdateUser.Value).Distinct().ToList();
using (var erpCtx = DbContextFactory.GetErpBaseContext())
{
operatorNameMap = erpCtx.SystemUsers.AsNoTracking()
.Where(u => operatorIds.Contains((long)u.Id))
.ToDictionary(u => (long)u.Id, u => u.Name);
}
}
foreach (var item in result.rows)
{
if (item.IsBond)
{
// 人工改动过则显示改这个人名,否则显示"中债估值"。
item.DataSource = ResolveBondDisplaySource(item.UpdateUser, operatorNameMap);
item.SourceTime = item.UpdateTime.HasValue? item.UpdateTime.Value.ToString("yyyy-MM-dd HH:mm:ss"):"";
item.SettlePrice=Convert.ToDouble(item.DeciSettlePrice);
item.ClosePrice = Convert.ToDouble(item.DeciClosePrice);
item.ReferencePrice = Convert.ToDouble(item.DeciReferencePrice);
}
}
return result;
}
///
/// 保存日终期货价格
///
public eod_commodity_future_price SaveEodFuturePrice(eod_commodity_future_price req)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req));
}
if (DbContext.eod_commodity_future_price.Any(n => n.id != req.id && n.UnderlyingCode == req.UnderlyingCode && n.ValueDate == req.ValueDate))
{
throw new ServiceException("已存在相同估值日期,相同合约的数据");
}
eod_commodity_future_price dbmodel;
if (req.id == 0)
{
DbContext.eod_commodity_future_price.Add(dbmodel = req);
}
else
{
dbmodel = DbContext.eod_commodity_future_price.Find(req.id);
if (dbmodel == null)
{
throw new ServiceException("数据不存在");
}
UpdateChanges(dbmodel, req);
}
SetDBModelOpt(dbmodel);
dbmodel.DataSource = EodPriceBase.人工;
DbContext.SaveChanges();
return dbmodel;
}
public ChinaBondValuation SaveBondPrice(ChinaBondValuation req)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req));
}
ChinaBondValuation dbmodel;
if (req.id == 0)
{
DbContext.china_bond_valuation.Add(dbmodel = req);
}
else
{
dbmodel = DbContext.china_bond_valuation.Find(req.id);
if (dbmodel == null)
{
throw new ServiceException("数据不存在");
}
UpdateChanges(dbmodel, req);
}
// 记录手工编辑人:写入登录用户ID到已有列(create_user/update_user),
// 不新增字段。聚源同步路径(SettlementPriceImportService)不写这两列,故 NULL 即"自动同步"。
StampBondOperator(dbmodel, UserId, req.id == 0);
dbmodel.update_time = DateTime.Now;
DbContext.SaveChanges();
return dbmodel;
}
///
/// 标记债券估值(china_bond_valuation)的操作人。
/// 该表已有 create_user/update_user 两列(bigint),但聚源同步路径不写入,
/// 因此:NULL = 聚源/中债自动同步;有值 = 被人手工编辑(记录登录用户ID)。
/// 抽出为纯静态函数,供 SaveBondPrice 与单元测试共用。
///
/// 债券估值实体
/// 当前登录用户ID
/// 是否为新增(true 时同时写 create_user)
public static void StampBondOperator(ChinaBondValuation model, int userId, bool isNew)
{
model.update_user = userId;
if (isNew)
{
model.create_user = userId;
}
}
///
/// 债券来源列该显示什么:
/// - update_user 有值(被人手工改过)且能解析到操作人姓名 → 显示改这个人名,便于溯源;
/// - 否则(聚源/中债自动同步,update_user 为 NULL)→ 固定显示"中债估值"。
/// 抽为纯静态函数,与解析姓名所需的 sys_user 查询解耦,便于无库单元测试。
///
/// china_bond_valuation.update_user(NULL = 自动同步)
/// update_user(ID) → 操作人姓名 的字典(由调用方按需从 sys_user 解析)
public static string ResolveBondDisplaySource(long? updateUser, IDictionary operatorNameMap)
{
if (updateUser.HasValue && operatorNameMap != null
&& operatorNameMap.TryGetValue(updateUser.Value, out var opName) && !string.IsNullOrEmpty(opName))
{
return opName;
}
return EodPriceBase.中债估值;
}
///
/// 保存日终股票价格
///
public eod_stock_price SaveEodStockPrice(eod_stock_price req)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req));
}
if (DbContext.eod_stock_price.Any(n => n.id != req.id && n.UnderlyingCode == req.UnderlyingCode && n.ValueDate == req.ValueDate))
{
throw new ServiceException("已存在相同估值日期,相同合约的数据");
}
eod_stock_price dbmodel;
if (req.id == 0)
{
DbContext.eod_stock_price.Add(dbmodel = req);
}
else
{
dbmodel = DbContext.eod_stock_price.Find(req.id);
if (dbmodel == null)
{
throw new ServiceException("数据不存在");
}
UpdateChanges(dbmodel, req);
}
SetDBModelOpt(dbmodel);
dbmodel.DataSource = EodPriceBase.人工;
DbContext.SaveChanges();
return dbmodel;
}
}
///
///
///
public class EodCommodityFuturePriceReq : BaseSearchReq
{
///
/// 数据来源
///
public string DataSource { get; set; }
public string MarketName { get; set; }
public string LaunchState { get; set; }
///
/// 标的代码
///
public string UnderlyingCode { get; set; }
public DateTime ValueDateStart { get; set; }
public DateTime ValueDateEnd { get; set; }
}
public class EodUnderlyingPriceDto
{
public string EncryptId
{
get
{
return DataProtectHelper.Encrypt(id.ToString());
}
}
public long id { get; set; }
public DateTime ValueDate { get; set; }
public double SettlePrice { get; set; }
public double ClosePrice { get; set; }
public double? ReferencePrice { get; set; }
public string DataSource { get; set; }
public string UnderlyingType { get; set; }
public string UnderlyingInstrumentType { get; set; }
///
/// 真实标的种类(取自 underlying_manager),仅供列表"标的种类"列显示。
/// UnderlyingInstrumentType 仍作为"存储表路由键"使用,二者解耦,避免改动历史路由逻辑。
///
public string RealInstrumentType { get; set; }
public string UnderlyingInstrumentTypeCn => ConsGlobal.InstrumentType.GetDesc(RealInstrumentType ?? UnderlyingInstrumentType);
public string UnderlyingState { get; set; }
public string MarketName { get; set; }
public string LaunchState { get; set; }
public int UnderlyingId { get; set; }
public string UnderlyingCode { get; set; }
public string UnderlyingName { get; set; }
public DateTime? UpdateTime { get; set; }
public string SourceTime { get; set; }
public decimal? DeciSettlePrice { get; set; }
public decimal? DeciClosePrice { get; set; }
public decimal? DeciReferencePrice { get; set; }
public bool IsBond { get; set; }
///
/// 手工改过估值时的操作人ID(china_bond_valuation.update_user)。
/// NULL = 聚源/中债自动同步(无人手工维护);有值 = 被人手工改过、可溯源。
/// 仅债券行可能非空,用于列表来源列显示改这个人名而非"中债估值"。
///
public long? UpdateUser { get; set; }
}
}