using BaseOUDAL; using DocumentFormat.OpenXml.Office.Word; using System.Collections.Concurrent; using System.Drawing.Printing; using YLErp.Configuration; using YLErp.DBModels.Consts; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Models; using YLErp.Modules.TradeDalModule; using YLErp.Modules.TradeModule.DocGenerateModule; namespace YLErp.Modules.TradeModule.DealModule { /// /// 交易确认书生成 /// public class TradeContractGenerateService : YLBaseService { public TradeContractGenerateService(YLBaseService baseService) : base(baseService) { } public TradeContractGenerateService(OptUserInfo userInfo) : base(userInfo) { } /// /// 确认书生成队列 /// private static readonly ConcurrentQueue<_ContractInfoModel> _ContractQueue = new ConcurrentQueue<_ContractInfoModel>(); /// /// 确认书缓存队列结构 /// private class _ContractInfoModel { public string ContractType; public List TradeIds; public _ContractInfoModel(string contractType, List tradeIds) { this.ContractType = contractType; this.TradeIds = tradeIds; } } /// /// 异步生成交易确认书 /// public void GenerateContractsAsync(List tradeIds, string contractType = "其它") { var status = false; lock (_ContractQueue) { status = _ContractQueue.IsEmpty; _ContractQueue.Enqueue(new _ContractInfoModel(contractType, tradeIds)); } if (status) { Task.Run(() => { while (!_ContractQueue.IsEmpty) { if (!_ContractQueue.TryPeek(out var obj)) { continue; } new TradeContractGenerateService(this).GenerateContracts(obj.TradeIds, obj.ContractType); lock (_ContractQueue) { _ContractQueue.TryDequeue(out _); } } }); } } /// /// 同步生成交易确认书 /// /// /// contractType为确认书的话,表示生成交易确认书;否则则生成提前终止确认书或者到期结算单 private TradeContractGenerateResultModel GenerateContracts(List tradeIds, string contractType = "其它") { ResetDbContext(); var result = new TradeContractGenerateResultModel(); try { if (contractType == "确认书") { result.ContractType = "确认书"; result.Success = GJGenerateConfirmBook(tradeIds, "PDF", out var errors, out _); result.Message = result.Success ? "生成确认书成功" : string.Join("\r\n", errors.AsEnumerable()); return result; } var clearingTradeIds = new List(); var unwindReportInputModels = new List(); var tcQuery = from tc in DbContext.trade_cash join t in DbContext.trade on tc.TradeId equals t.id where tradeIds.Contains(tc.TradeId) && !tc.IsDeleted select new { tc.id, tc.TradeId, tc.Action, tc.ExerciseWay, tc.IsLastAction, ValueDate = tc.HappenedDate ?? tc.ValueDate, t.ExerciseDate }; var lookUp = tcQuery.ToLookup(tc => tc.TradeId); var tradeCashes = lookUp.Select(l => l.OrderByDescending(m => m.id).First()).ToList(); foreach (var tc in tradeCashes) { if (tc.Action == ClientCashInCashOut.系统操作_平仓费 || (tc.Action == ClientCashInCashOut.系统操作_行权费 && tc.ExerciseWay == TradeCashExerciseWayEnum.提前终止行权)) { unwindReportInputModels.Add(new UnwindReportInputModel { TradeId = tc.TradeId, TradeCashId = tc.id }); } else if (tc.Action == ClientCashInCashOut.系统操作_行权费) { clearingTradeIds.Add(tc.TradeId); } else if (tc.Action == ClientCashInCashOut.系统操作_票息) { if (tc.ValueDate >= tc.ExerciseDate) { clearingTradeIds.Add(tc.TradeId); } else { unwindReportInputModels.Add(new UnwindReportInputModel { TradeId = tc.TradeId, TradeCashId = tc.id }); } } } var hasUnwindReport = unwindReportInputModels.Count > 0; if (hasUnwindReport) { result.ContractType = "提前终止确认书"; result.Success = GJGenerateUnwindReport(unwindReportInputModels, "PDF", out var errors, out _); result.Message = result.Success ? "生成提前终止确认书成功" : string.Join("\r\n", errors.AsEnumerable()); } if (clearingTradeIds.Count > 0) { result.ContractType = "到期结算单"; if (GJGenerateSettleBill(clearingTradeIds, "PDF", out var errors, out _)) { if (!hasUnwindReport) { result.Success = true; result.Message = "生成到期结算单成功"; } else if (result.Success) { result.Message = "生成到期结算单或提前终止确认书成功"; } } else if (result.Success) { result.Success = false; result.Message = string.Join("\r\n", errors.AsEnumerable()); } else { result.Message += (string.IsNullOrEmpty(result.Message) ? string.Empty : "\r\n") + string.Join("\r\n", errors.AsEnumerable()); } } return result; } catch (Exception ex) { result.Exception = ex; LogFactory.GetLogger("生成确认书").Error("确认书类型:" + result.ContractType, ex); } return result; } /// /// 生成交易确认书 /// public bool GJGenerateConfirmBook(List tradeIds, string docType, out IEnumerable errors, out IEnumerable files, DateTime? startDate = null, DateTime? endDate = null) { if (PS.Config.Company == CompanyEnum.国信金阳) { if (!GXJY_CorrespondingConfirmationTemplate(tradeIds, out string Msg)) { throw new ServiceException(Msg); } } Func getUnderlyingInstrumentType = new Func((underlyingId) => { return DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingId)?.UnderlyingInstrumentType; }); List results = new List(); List error = new List(); List file = new List(); if (PS.Config.Company == CompanyEnum.浙商) { LogFactory.GetLogger("生成确认书").Error("浙商生成确认书"); var trades = DbContext.trade.Where(x=>tradeIds.Contains(x.id)).AsNoTracking().ToList(); var clientIds= trades.Select(x=>x.ClientId).Distinct().ToList(); var allTrades= DbContext.trade.Where(x => clientIds.Contains(x.ClientId)&&x.ValidState!=ConsGlobal.InValid); if (startDate.HasValue) { allTrades = allTrades.Where(x => x.TradeDate >= startDate); } if (endDate.HasValue) { allTrades = allTrades.Where(x => x.TradeDate <= endDate); } var Ids = allTrades.Select(o => o.id).Distinct().ToArray(); var swapPositions = DbContext.swap_position.Where(s => Ids.Contains(s.SwapTradeId) && s.IsInitial && s.PositionType > 0 && !s.Invalid) .Distinct().ToList(); var allTradeList= allTrades.AsNoTracking().ToList(); foreach (var item in allTradeList) { item.swap_positions.Add(swapPositions.FirstOrDefault(o => o.SwapTradeId == item.id)); } allTradeList = allTradeList.Where(x => x.swap_positions.Any()).ToList(); // 按照客户ID、交易日期、多空头进行分组 var group = allTradeList.GroupBy(O => new { O.ClientId, O.TradeDate, O.swap_positions.FirstOrDefault().PositionType }) .ToDictionary(K => K.Key, V => V.Select(O => O.id).ToList()); foreach (var item in group) { LogFactory.GetLogger("生成确认书").Error("浙商生成确认书" + item.Value.Distinct().ToList().Count()); groupGenerateHandle(item.Value.Distinct().ToList(), docType, startDate, endDate, results, error, file); } } errors = results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToList(); files = results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList(); return !errors.Any(); } private void groupGenerateHandle_Date(List ids, string docType, DateTime? startDate, DateTime? endDate, List results, List error, List file) { var trades = DbContext.trade.Where(c => ids.Contains(c.id)).ToList(); var supProtocolDate = DataCacheProvider.GetClientDataSource().GetData(trades.FirstOrDefault().ClientId).SupProtocolDate; var groupDateDouble = trades.Where(x => supProtocolDate == null || x.TradeDate < supProtocolDate).Select(x => x.id).ToList(); var groupDateSingle = trades.Where(x => supProtocolDate != null && x.TradeDate >= supProtocolDate).Select(x => x.id).ToList(); // 单章确认书和双章确认书同时生成要生成两个文件 if (groupDateDouble.Any() && groupDateSingle.Any()) { groupGenerateHandle(groupDateDouble, docType, startDate, supProtocolDate.Value.AddDays(-1), results, error, file); groupGenerateHandle(groupDateSingle, docType, supProtocolDate, endDate, results, error, file); } else if (groupDateDouble.Any()) { groupGenerateHandle(groupDateDouble, docType, startDate, endDate, results, error, file); } else if (groupDateSingle.Any()) { groupGenerateHandle(groupDateSingle, docType, startDate, endDate, results, error, file); } } private void groupGenerateHandle(List ids, string docType, DateTime? startDate, DateTime? endDate, List results, List error, List file) { var trade_contract_r = DbContext.trade_contract_r.Where(c => ids.Contains(c.TradeId) && c.Type == "交易确认书" && c.IsValid).ToList(); //DbContext.trade_contract_r.RemoveRange(trade_contract_r); trade_contract_r.ForEach(O => O.IsValid = false); DbContext.SaveChanges(); var result = new BatchConfirmationGenerateService(this).Generate(ids, docType, startDate, endDate); results.AddRange(result); error.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToArray()); file.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList()); } public bool GXJY_CorrespondingConfirmationTemplate(List tradeIds, out string ErrMsg) { bool IsExist = true; ErrMsg = ""; var listTrade = DbContext.trade.Where(l => tradeIds.Contains(l.id)); foreach (var t in listTrade) { bool IsExistChild = false; var exerciseMode = t.ExerciseMode ?? ""; switch (t.TradeType) { case "亚式期权": IsExistChild = true; break; case "结构化交易": switch (t.StructureType) { case "普通亚式带保底": case "增强亚式": case "增强亚式价差": case "增强亚式价差组合标的": IsExistChild = true; break; } break; default: IsExistChild = true; break; } if (IsExistChild == false) { IsExist = false; ErrMsg += t.TradeNumber.ToString() + ","; } } if (!string.IsNullOrWhiteSpace(ErrMsg)) { ErrMsg = ErrMsg.Substring(0, ErrMsg.Length - 1); ErrMsg += "该交易没有相应确认书模板"; } return IsExist; } /// /// 生成提前中止确认书 /// public bool GJGenerateUnwindReport(List trades, string docType, out List errors, out List files) { var tdIds = trades.Select(t => t.TradeId).ToList(); if (PS.Config.Company == CompanyEnum.国信金阳) { var IsTradeContract = DbContext.trade_contract_r.Where(l => l.IsValid && tdIds.Contains(l.TradeId) && l.Type == "交易确认书").Select(l => l.TradeId).ToList(); var IsTradeIds = tdIds.Where(l => !IsTradeContract.Contains(l)).ToList(); if (IsTradeIds.Count > 0) { throw new ServiceException(String.Join(",", DbContext.trade.Where(l => IsTradeIds.Contains(l.id)).Select(l => l.TradeNumber).ToArray()) + "请生成该交易编号相应交易确认书"); } if (!GXJY_CorrespondingConfirmationTemplate(tdIds, out string Msg)) { throw new ServiceException(Msg); } } files = new List(); errors = new List(); if (PS.Config.Company == CompanyEnum.兴证) { IEnumerable results = null; results = new BatchUnwindReportGenerateService(this).Generate(trades, docType, UserId, UserName); errors = results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToList(); files = results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList(); } else if (PS.Config.Company == CompanyEnum.海通) { var tradeCashIds = trades.Select(O => O.TradeCashId); var tradeCashStatus = new string[] { ClientCashInCashOut.系统操作_行权费, ClientCashInCashOut.系统操作_平仓费 }; var query = from t in DbContext.trade join tc in DbContext.trade_cash on t.id equals tc.TradeId where tradeCashIds.Contains(tc.id) && tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted && tradeCashStatus.Contains(tc.Action) && tc.ExerciseWay == TradeCashExerciseWayEnum.提前终止行权 select new { t.id, tradeCashId = tc.id, t.ClientId }; var clientTradeDict = query.AsEnumerable().GroupBy(O => O.ClientId) .ToDictionary(K => K.Key, V => V.Select(O => new UnwindReportInputModel() { TradeId = O.id, TradeCashId = O.tradeCashId }).ToList()); foreach (var ctrades in clientTradeDict) { var results = new BatchUnwindReportGenerateService(this).Generate(ctrades.Value, "PDF", UserId, UserName); errors.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToArray()); files.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList()); if (errors.Any()) break; } } else if (PS.Config.Is方顿) //同一笔交易结构化交易在一个确认书里 { List results = new List(); List error = new List(); List file = new List(); var tradeIds = trades.Select(O => O.TradeId).ToArray(); var query = DbContext.trade.Where(O => tradeIds.Contains(O.id)).Select(O => new { O.id, O.ParentTradeId }).ToList(); var group = query.GroupBy(O => O.ParentTradeId).ToDictionary(K => K.Key, V => V.Select(O => O.id).ToList()); foreach (var item in group) { if (item.Key == 0) { foreach (var item2 in item.Value) { var tradeCashIds = trades.Where(x => x.TradeId == item2).ToList(); var CashIds = trades.Where(x => x.TradeId == item2).Select(x => x.TradeId).ToList(); var trade_contract_r = DbContext.trade_contract_r.Where(c => CashIds.Contains(c.TradeCashId.Value) && c.IsValid).ToList(); trade_contract_r.ForEach(o => o.IsValid = false); DbContext.SaveChanges(); var result = new BatchUnwindReportGenerateService(this).Generate(tradeCashIds, docType, UserId, UserName); results.AddRange(result); error.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToArray()); file.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList()); if (error.Any()) break; ; } } else { var tradeCashIds = (from t1 in item.Value join tc in trades on t1 equals tc.TradeId select tc).ToList(); var CashIds = (from t1 in item.Value join tc in trades on t1 equals tc.TradeId select tc.TradeId).ToList(); var trade_contract_r = DbContext.trade_contract_r.Where(c => CashIds.Contains(c.TradeCashId.Value) && c.IsValid).ToList(); trade_contract_r.ForEach(o => o.IsValid = false); DbContext.SaveChanges(); var result = new BatchUnwindReportGenerateService(this).Generate(tradeCashIds, docType, UserId, UserName); results.AddRange(result); error.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToArray()); file.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList()); if (error.Any()) break; ; } } errors = results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToList(); files = results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList(); } else { List results = new List(); if (PS.Config.Is国泰君安) { List error = new List(); List file = new List(); var tradeIds = trades.Select(O => O.TradeId); var tradecashId = trades.Select(o => o.TradeCashId).ToList(); Func getUnderlyingInstrumentType = new Func((underlyingId) => { return DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingId).UnderlyingInstrumentType; }); List instrumentTypeArr = new List() { ConsGlobal.InstrumentType.Stock, ConsGlobal.InstrumentType.StockIF }; var query = DbContext.trade.Where(O => tradeIds.Contains(O.id)).Select(O => new { O.id, O.UnderlyingId, O.ClientId, O.TradeType }).ToList(); if (query.Any(O => instrumentTypeArr.Contains(getUnderlyingInstrumentType(O.UnderlyingId)) || O.TradeType == "雪球期权")) { //暂时将所有子交易都作为判断依据,等拿到模板后再根据模板做调整更改; //目前是否为权益类的判断条件只是是否是股票,没有考虑IC IF等品种; int count = query.GroupBy(O => new { UnderlyingInstrumentType = (instrumentTypeArr.Contains(getUnderlyingInstrumentType(O.UnderlyingId)) || O.TradeType == "雪球期权") ? 1 : 0 }).Count(); if (count > 1) { throw new ServiceException("不应勾选多个非股票(含股指期货)标的的交易"); } var group = query.GroupBy(O => new { O.ClientId }).ToDictionary(K => K.Key, V => V.Select(O => O.id).ToList()); foreach (var item in group) { var tradeCashIds = (from t1 in item.Value join tc in trades on t1 equals tc.TradeId select tc).ToList(); var CashIds = (from t1 in item.Value join tc in trades on t1 equals tc.TradeId select tc.TradeId).ToList(); var trade_contract_r = DbContext.trade_contract_r.Where(c => CashIds.Contains(c.TradeCashId.Value) && c.IsValid).ToList(); //DbContext.trade_contract_r.RemoveRange(trade_contract_r); trade_contract_r.ForEach(o => o.IsValid = false); DbContext.SaveChanges(); var result = new BatchUnwindReportGenerateService(this).Generate(tradeCashIds, docType, UserId, UserName); results.AddRange(result); error.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToArray()); file.AddRange(results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList()); if (error.Any()) break; ; } } } if (results.Count == 0) { results = (List)new UnwindReportGenerateService(this).Generate(trades, docType); } errors = results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToList(); files = results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToList(); } return !errors.Any(); } /// /// 生成结算确认书 /// public bool GJGenerateSettleBill(List tradeIds, string docType, out IEnumerable errors, out IEnumerable files) { IEnumerable results; var settleSv = new SettlementBillGenerateService(this); results = settleSv.Generate(null, tradeIds, docType, UserId, UserName); errors = results.Where(n => !string.IsNullOrWhiteSpace(n.ErrorMessage)).Select(n => n.ErrorMessage).ToArray(); files = results.Where(n => !string.IsNullOrWhiteSpace(n.OutputFilePath)).Select(n => n.OutputFilePath).ToArray(); return !errors.Any(); } /// /// 根据传进来的合约代码过滤出已存在的合约代码 /// public IEnumerable GetExistsContractCode(IEnumerable contractCodes) { if (contractCodes == null) { throw new ArgumentNullException(nameof(contractCodes)); } if (!contractCodes.Any()) { return Enumerable.Empty(); } var query = (from a in DbContext.trade_contract_r.AsNoTracking() join b in DbContext.trade.AsNoTracking() on a.TradeId equals b.id where contractCodes.Contains(a.ContractCode) && b.ValidState != "InValid" && a.IsValid select a.ContractCode).Union (from tm in DbContext.TradeMeta.AsNoTracking() join t in DbContext.trade.AsNoTracking() on tm.TradeId equals t.id where (tm.MetaKey == ConsTradeMetaKey.ContractCode || tm.MetaKey.Contains("确认书")) && //伴星确认书编号的Key是合约编号,长江确认书编号的Key是日期+(交易确认书、结算确认书和提前终止确认书)+序号 t.ValidState != "InValid" && contractCodes.Contains(tm.MetaValue) select tm.MetaValue); //.Union //(from t in DbContext.trade.AsNoTracking() // where t.ValidState != "InValid" && contractCodes.Contains(t.TradeNumber) // select t.TradeNumber); return query.ToArray(); } public SearchListResult GetConfirmBookListByClient(ClientConfirmBookListReq req) { var result = new SearchListResult(); var contractList = new List(); // Fix for the issue: CS1525, CS0746, CS1003 // The problem lies in the incorrect syntax for defining an anonymous type. // Anonymous type members must be declared using member assignment or simple names. var query = from t in DbContext.trade.AsNoTracking().Where(x => x.TradeType == "收益互换" && x.ValidState != "InValid") join tcrs in DbContext.trade_contract_r on t.id equals tcrs.TradeId into tcrsGroup from tcr in tcrsGroup.Where(r => r.IsValid && r.Type == ContractTypeEnum.Trade).DefaultIfEmpty() join tcds in DbContext.trade_contract_document on tcr.ContractCode equals tcds.Code into tcdsGroup from tcd in tcdsGroup.DefaultIfEmpty() join sp in DbContext.swap_position.AsNoTracking().Where(s => s.IsInitial && s.PositionType > 0 && !s.Invalid) on t.id equals sp.SwapTradeId select new { t.id, t.TradeDate, tcr.ContractCode, t.ClientId, t.ClientNumber, t.ClientName, tcr.send_email_result, tcd.Status, tcd.RelativePath, tcd.SealResult, ContractId = tcr != null ? tcr.id : 0, sp.PositionType, t.SentMailCount, tcd.StampDocumentFileName, tcr.OptDate, tcr.OptName, ContractDocId = tcd != null ? tcd.id : 0, }; if (req.StartDate != null && req.StartDate != DateTime.MinValue) { query = query.Where(o => o.TradeDate >= req.StartDate); } if (req.EndDate != null && req.EndDate != DateTime.MaxValue) { query = query.Where(o => o.TradeDate <= req.EndDate); } if (req.ClientIds != null && req.ClientIds.Count > 0) { query = query.Where(o => req.ClientIds.Contains(o.ClientId)); } if (!string.IsNullOrEmpty(req.ContractCode)) { query = query.Where(o => o.ContractCode.Contains(req.ContractCode)); } if (req.PositionTypes!= null && req.PositionTypes.Count() > 0) { query = query.Where(o => req.PositionTypes.Contains(o.PositionType)); } var contractGroups = query.AsEnumerable() .GroupBy(x => new { x.TradeDate, x.ClientId, x.PositionType }) .Select(group => group.ToList()) .ToList(); // 构建返回结果 foreach (var group in contractGroups) { var sealResult = group.FirstOrDefault().SealResult; contractList.Add(new clientContractR { id = group.FirstOrDefault().id, ContractCode = group.FirstOrDefault().ContractCode, TradeDate = group.FirstOrDefault().TradeDate.Value, ClientNumber = group.FirstOrDefault().ClientNumber, ClientName = group.FirstOrDefault().ClientName, StampStatus = group.FirstOrDefault().Status, EmailResult = group.FirstOrDefault().send_email_result, ContractDocUrl = group.FirstOrDefault().RelativePath, tradeIds = group.Select(x => x.id).Distinct().ToList(), SealResult = sealResult != null ? Enum.GetName(typeof(SealResultEnum), sealResult) : "", HasGeneratedConfirmBook = group.FirstOrDefault().ContractId != 0, ContractId = group.FirstOrDefault().ContractId, tradeCount = group.Select(x => x.id).Distinct().Count(), PositionType = group.FirstOrDefault().PositionType, SentMailCount = group.FirstOrDefault().SentMailCount ?? 0, StampDocumentFileName = group.FirstOrDefault().StampDocumentFileName, OptDate = group.FirstOrDefault().OptDate, OptName = group.FirstOrDefault().OptName, ContractDocId = group.FirstOrDefault().ContractDocId }); } result = contractList.OrderByDescending(r => r.TradeDate).AsQueryable().ToSearchList(req); return result; } } /// /// 交易确认书生成结果 /// public class TradeContractGenerateResultModel { public bool Success; public string Message; public string ContractType; public Exception Exception; } public class ClientConfirmBookListReq : BaseSearchReq { //合约编号 public string ContractCode { get; set; } //开仓起始时间 public DateTime? StartDate { get; set; } //开仓结束时间 public DateTime? EndDate { get; set; } //客户 public List? ClientIds { get; set; } public List? PositionTypes { get; set; } } public class clientContractR { public int id { get; set; } public string ContractCode { get; set; } public DateTime TradeDate { get; set; } public string ClientNumber { get; set; } public string ClientName { get; set; } public string StampStatus { get; set; } public string EmailResult { get; set; } public List tradeIds { get; set;} public string ContractDocUrl { get; set; } public string SealResult { get; set; } public string ContractEncryptId { get; set; } public bool HasGeneratedConfirmBook { get; set; } public int ContractId { get; set; } public int PositionType { get; set; } public int tradeCount { get; set; } public int SentMailCount { get; set; } public string StampDocumentFileName { get; set; } public DateTime? OptDate { get; set; } public string OptName { get; set; } public int ContractDocId { get; set; } } }