using YLErp.BLL; using YLErp.DBModels.Consts; using YLErp.Model; using YLErp.Models; using YLErp.Modules.ClientModule; using YLErp.Modules.TradeDalModule; using YLErp.MsOffice; using YLErp.Office.Converters; using YLErp.Plugins.TradeDocGenerator.Abstracts; namespace YLErp.Modules.TradeModule.DocGenerateModule { /// /// 生成交易确认书 /// public class BatchConfirmationGenerateService : YLBaseService { public BatchConfirmationGenerateService(OptUserInfo userInfo) : base(userInfo) { } public BatchConfirmationGenerateService(YLBaseService baseService) : base(baseService) { } /// /// 生成交易确认书 /// public IEnumerable Generate(IEnumerable tradeIds, string docType, DateTime? startDate = null, DateTime? endDate = null) { if (tradeIds == null) { throw new ArgumentNullException(nameof(tradeIds)); } if (docType == null) { throw new ArgumentNullException(nameof(docType)); } //---------------------------------- // 获取当日行权 平仓 到期的有效交易 //---------------------------------- var statusList = new List() { ConsTrade.确认成交 }; if (GlobalConfig.EnableGenerateOnTradeClosed) { statusList.Add(ConsTrade.已到期); statusList.Add(ConsTrade.已平仓); statusList.Add(ConsTrade.已执行); statusList.Add(ConsTrade.审批中); } var service = new TradeDalService(this); //var tradeQuery = service // .GetTradeOrEodTrade(tradeIds, ValueDate, true) // .Where(O => // statusList.Contains(O.TradeStatus) // && (O.ParentTradeId == null || O.ParentTradeId < 1) // && O.ClientId != null); //var tradeQuery = from trad in DbContext.trade // where tradeIds.Contains(trad.id) // && statusList.Contains(trad.TradeStatus) // && trad.ValidState != "InValid" && trad.ClientId != null // && (trad.ParentTradeId == null || trad.ParentTradeId < 1) // select trad; //---------------------------------- // 交易现金交割记录 //---------------------------------- List allTrades = DbContext.trade.Where(x => tradeIds.Contains(x.id)).ToList(); if (allTrades.Count < 1) { throw new ServiceException("没有已确认的记录信息!"); } var tradeDic = allTrades.Where(O => statusList.Contains(O.TradeStatus) && O.ClientId != 0 && O.ParentTradeId == 0) .ToDictionary(n => n.id, n => new trade_contract_group { trade = n }); tradeIds = allTrades.Select(O => O.id); //---------------------------------- // 生成交易确认书 //---------------------------------- var resultList = new List(); AppManager.TryResolve(out ITradeConfirmationGenerator generatorV2); var tradeContractGroupTradeIds = tradeDic.Values.Select(x => x.trade.id).ToList(); // 如果包含互换交易,强制走V1默认逻辑(不走插件) var hasSwapTrade = tradeDic.Values.Any(x => x.trade.TradeType == "收益互换"); if (generatorV2 != null && hasSwapTrade) { var result = GenerateSingleV2( generatorV2, tradeDic.Values.Select(O => O.trade).ToList(), tradeDic.Values.Select(O => (O.trade.trade_cash?.id) ?? 0).ToList(), docType, startDate, endDate); resultList.Add(result); } else { foreach (var item in tradeDic) { resultList.Add(GenerateSingleV1(item.Value, docType)); } } if (generatorV2 == null || hasSwapTrade) { DbContext.SaveChanges(); } return resultList; } /// /// /// private TradeDocGenerateResult GenerateSingleV2(ITradeConfirmationGenerator generator, List trades, List tradeCashIds, string docType = "DOCX", DateTime? startDate = null, DateTime? endDate = null) { try { var context = new ConfirmationGenerateContext(tradeCashIds, trades, ContractTypeEnum.Trade, OptUser, startDate, endDate); generator.Initialize(docType, context); return generator.GenerateDocument(); } catch (Exception ex) { LogFactory.GetLogger("GenerateSingleV2").Error(ex); return new TradeDocGenerateResult(trades) { ErrorMessage = ex.Message }; } } /// /// /// private TradeDocGenerateResult GenerateSingleV1(trade_contract_group contractGroup, string docType = "DOCX") { var logger = LogFactory.GetLogger("生成确认书详细日志"); logger.Info($"[BatchGenerateSingleV1] 开始处理 - docType: {docType}"); if (contractGroup == null) { logger.Error("[BatchGenerateSingleV1] contractGroup 为 null"); throw new ArgumentNullException(nameof(contractGroup)); } var trade = contractGroup.trade; logger.Info($"[BatchGenerateSingleV1] 交易信息 - TradeId: {trade?.id}, TradeNumber: {trade?.TradeNumber}, TradeType: {trade?.TradeType}"); var contractBLL = new TradeContractBLL(this); tradeBLL.SetFieldsByTradeType(trade); var needAddData = true; #region 如果之前交易确认书已经生成过,则直接返回 var confirmDoc = contractBLL.GetContractDoc(trade.id, ContractTypeEnum.Trade); if (confirmDoc != null) { if (File.Exists(confirmDoc.AbsolutePath)) { if (docType == "PDF") { var pdfPath = confirmDoc.AbsolutePath.Replace(".docx", ".pdf"); if (!File.Exists(pdfPath)) { OfficeFileConverter.ConvertDocxToPDF(confirmDoc.AbsolutePath, pdfPath); } return new TradeDocGenerateResult(trade) { //OutputFilePath = $"/App_Docs/GTJA/{confirmDoc.FileName.Replace(".doc", ".pdf")}" OutputFilePath = YLErp.Helpers.PathHelper.PhysicalPathToWebPath(pdfPath, OtcAppContext.RootPath) }; } return new TradeDocGenerateResult(trade) { //OutputFilePath = $"/App_Docs/GTJA/{confirmDoc.FileName}" OutputFilePath = YLErp.Helpers.PathHelper.PhysicalPathToWebPath(confirmDoc.AbsolutePath, OtcAppContext.RootPath) }; } else { needAddData = false; } } #endregion var isGJBuyer = trade.BuySell == "买入"; var underlying = underlying_managerBLL.GetByCode(trade.UnderlyingCode); if (underlying == null) { return new TradeDocGenerateResult(trade) { ErrorMessage = $"标的资产{trade.UnderlyingCode}信息未找到" }; } var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId); if (client == null) { return new TradeDocGenerateResult(trade) { ErrorMessage = $"客户{trade.ClientId}信息未找到" }; } var dic = new Dictionary { ["{{乙方名称}}"] = client.Name }; if (isGJBuyer) { dic["{{甲方方向}}"] = "买方"; dic["{{乙方方向}}"] = "卖方"; } else { dic["{{甲方方向}}"] = "卖方"; dic["{{乙方方向}}"] = "买方"; } dic["{{交易日}}"] = trade.TradeDate?.ToString("yyyy年M月d日"); if (trade.TradeType == "香草期权" || trade.TradeType == "亚式期权") { dic["{{生效日}}"] = trade.TradeDate?.ToString("yyyy年M月d日"); } else { dic["{{生效日}}"] = trade.StartDate?.ToString("yyyy年M月d日"); } if (trade.TradeType == "亚式期权") { dic["{{均价起算日}}"] = trade.trade_asian_option.AveragingPeriodStartDate?.ToString("yyyy年M月d日"); dic["{{奇异结构}}"] = $"{trade.trade_asian_option.StrikeTypeCn}的{trade.trade_asian_option.PayoffTypeCn}亚式"; } dic["{{到期日}}"] = trade.ExerciseDate?.ToString("yyyy年M月d日"); dic["{{行权日}}"] = "到期日"; dic["{{自动行权}}"] = "到期实值自动行权"; //商品标的 if (trade.TradeType == "合成价差期权") { dic["{{商品标的名称}}"] = synthetic_underlyingBLL.GetUnderlyingTipsInfo(trade.UnderlyingCode); } else { dic["{{商品标的名称}}"] = trade.UnderlyingCode; } var strUnit = underlying.TradeUnitString; var notional = trade.Notional; dic["{{报价单位}}"] = strUnit; dic["{{商品标的数量}}"] = $"{notional}{strUnit}"; dic["{{商品标的交易所}}"] = underlying.MarketName; //价格 if (trade.ActualStrike != null) { if (trade.IsMoneynessOption == "是") { dic["{{执行价格}}"] = (trade.Strike ?? 0).OtcFormatUmPrice(true); } else { dic["{{执行价格}}"] = $"{trade.ActualStrike?.OtcFormatUmPrice()}元/{strUnit}"; } } else { dic["{{执行价格}}"] = "不适用"; } var tradeType = trade.TradeType == "香草期权" ? "普通" : (trade.TradeType == "结构化交易" ? trade.StructureType : trade.TradeType.Replace("期权", "")); dic["{{期权类型}}"] = $"{trade.ExerciseModeCn}{tradeType}{trade.OptionType}期权"; dic["{{期权费}}"] = $"{(trade.TradePrice ?? 0).OtcFormatMoney(grouping: false)}元"; dic["{{协议编号}}"] = client.Remark; dic["{{期权价格}}"] = $"{(trade.TradeSinglePrice ?? 0).OtcFormat(OtcFormatFlag.tradeSinglePrice)}元/{strUnit}"; dic["{{支付时间}}"] = trade.PremiumPayDate != null ? "提前终止/到期时支付" : "已支付"; dic["{{期权方向}}"] = $"甲方{trade.BuySell}"; dic["{{名义本金}}"] = trade.StockEqvNotional.OtcFormat(OtcFormatFlag.StockEqvNotional); dic["{{期初价格}}"] = (trade.SpotPrice ?? 0).OtcFormatUmPrice(); dic["{{期权费率}}"] = ((trade.PremiumRate ?? ((trade.TradePrice ?? 0) / trade.OriginalStockEqvNotional.Value)) * 100).OtcFormatFlex(2); var settlementType = "价格"; if (trade.SettlementType == SettlementTypeEnum.SettlePrice) { settlementType = "结算价"; } else if (trade.SettlementType == SettlementTypeEnum.ClosePrice) { settlementType = "收盘价"; } if (trade.ExerciseMode == "American") { dic["{{美式期权结算价}}"] = $"所属交易所公布的标的合约可成交的最新价"; dic["{{欧式期权结算价}}"] = $"美式期权不适用"; } else { dic["{{美式期权结算价}}"] = $"欧式期权不适用"; dic["{{欧式期权结算价}}"] = $"所属交易所在到期日收盘后公布的标的合约{settlementType}"; } if (trade.UnderlyingInstrumentType != "CommodityFutures") { dic["{{证券名称}}"] = underlying.UnderlyingName; dic["{{证券代码}}"] = underlying.UnderlyingCode; dic["{{账户信息乙方}}"] = client.Name; dic["{{联系方式乙方}}"] = client.Name; dic["{{联系方式乙方联系人}}"] = client.Name; dic["{{联系方式乙方电话}}"] = client.Phone; dic["{{联系方式乙方电子邮件}}"] = client.Email; var bankcard = ClientDataQueryService.GetBankCard(trade.ClientId); if (bankcard != null) { dic["{{账户信息乙方户名}}"] = bankcard.ClientName; dic["{{账户信息乙方账号}}"] = bankcard.Card; dic["{{账户信息乙方开户行}}"] = bankcard.Bank; } dic["{{期权方向}}"] = $"甲方{trade.BuySell}"; dic["{{名义本金}}"] = trade.StockEqvNotional.OtcFormat(OtcFormatFlag.StockEqvNotional); } if (trade.OptionType == "看涨") { if (trade.ExerciseMode == "American") { dic["{{美式期权结算金额}}"] = $"(行权时{trade.UnderlyingCode}合约可成交最新价 - {trade.ActualStrike.OtcFormatUmPrice()})× {notional.OtcFormatNotional()}"; dic["{{欧式期权结算金额}}"] = $"美式期权不适用"; } else { dic["{{美式期权结算金额}}"] = $"欧式期权不适用"; dic["{{欧式期权结算金额}}"] = $"({trade.ExerciseDate?.ToString("yyyy年M月d日")}{trade.UnderlyingCode}合约{settlementType} - {trade.ActualStrike.OtcFormatUmPrice()})× {notional.OtcFormatNotional()}"; } } else if (trade.OptionType == "看跌") { if (trade.ExerciseMode == "American") { dic["{{美式期权结算金额}}"] = $"({trade.ActualStrike.OtcFormatUmPrice()} - 行权时{trade.UnderlyingCode}合约可成交最新价)× {notional.OtcFormatNotional()}"; dic["{{欧式期权结算金额}}"] = $"美式期权不适用"; } else { dic["{{美式期权结算金额}}"] = $"欧式期权不适用"; dic["{{欧式期权结算金额}}"] = $"({trade.ActualStrike.OtcFormatUmPrice()} - {trade.ExerciseDate?.ToString("yyyy年M月d日")}{trade.UnderlyingCode}合约{settlementType})× {notional.OtcFormatNotional()}"; } } //合同编号 string contractNo; string outPutFileName; if (confirmDoc != null) { contractNo = confirmDoc.Code; outPutFileName = confirmDoc.FileName; } else { var contractIndex = contractBLL.GetContractNo("GTJA"); if (PS.Config.CompanyID == "中财资本") { contractNo = $"{trade.TradeNumber}"; } else { contractNo = $"{client.Number}C{contractIndex}"; } outPutFileName = $"{client.Name}交易确认书C{contractIndex}.docx"; } dic["{{合同编号}}"] = contractNo; dic["{{结算方式}}"] = "人民币现金结算"; dic["{{补充}}"] = "补充"; var batchLogger = LogFactory.GetLogger("生成确认书详细日志"); try { batchLogger.Info($"[BatchGenerateSingleV1] 开始生成文档 - TradeId: {trade?.id}"); batchLogger.Info($"[BatchGenerateSingleV1] 模板类型: {trade.TradeType}, IsUsePremiumRate: {trade.IsUsePremiumRate}"); var templateDir = OtcAppContext.MapPath("~/App_Docs/GTJA"); batchLogger.Info($"[BatchGenerateSingleV1] templateDir: {templateDir}"); var templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书模板.docx"); if (trade.TradeType == "亚式期权") { templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书(亚式期权)模板.docx"); batchLogger.Info($"[BatchGenerateSingleV1] 使用亚式期权模板"); } if (trade.IsUsePremiumRate != null && trade.IsUsePremiumRate.Value) { templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书(名义本金)模板.docx"); batchLogger.Info($"[BatchGenerateSingleV1] 使用名义本金模板"); } batchLogger.Info($"[BatchGenerateSingleV1] 最终模板路径: {templatePath}"); batchLogger.Info($"[BatchGenerateSingleV1] 输出文件名: {outPutFileName}"); // 检查模板文件是否存在 if (!File.Exists(templatePath)) { batchLogger.Error($"[BatchGenerateSingleV1] 模板文件不存在: {templatePath}"); return new TradeDocGenerateResult(trade) { ErrorMessage = $"模板文件不存在: {templatePath}" }; } batchLogger.Info($"[BatchGenerateSingleV1] 模板文件存在,准备生成"); LogFactory.GetLogger("交易确认书").Info("交易确认书:" + outPutFileName); var outPutFilePath = Path.Combine(templateDir, outPutFileName); batchLogger.Info($"[BatchGenerateSingleV1] 输出文件路径: {outPutFilePath}"); // 确保输出目录存在 if (!Directory.Exists(templateDir)) { batchLogger.Info($"[BatchGenerateSingleV1] 创建输出目录: {templateDir}"); Directory.CreateDirectory(templateDir); } batchLogger.Info($"[BatchGenerateSingleV1] 调用 MsWordTempalteHelper.GenerateFromTemplate"); MsWordTempalteHelper.GenerateFromTemplate(templatePath, dic, outPutFilePath); batchLogger.Info($"[BatchGenerateSingleV1] MsWordTempalteHelper.GenerateFromTemplate 完成"); if (docType == "PDF" && !outPutFilePath.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) { OfficeFileConverter.ConvertDocxToPDF(outPutFilePath, outPutFilePath.Replace(".docx", ".pdf")); } //更新操作时间 var now = DateTime.Now; File.SetLastAccessTime(outPutFilePath, now); #region --- Add trade_contract_document and trade_contract_r records if (needAddData) { contractBLL.DeleteConfirmBook(trade.id, null); var nowDate = DateTime.Now; var contractRecord = new trade_contract_document(); DbContext.trade_contract_document.Add(contractRecord); contractRecord.Code = dic["{{合同编号}}"].ToString(); contractRecord.ValueDate = valuedateBLL.ValueDate; contractRecord.BuyerId = isGJBuyer ? 0 : trade.ClientId; contractRecord.BuyerName = isGJBuyer ? PS.Config.CompanyFullName : trade.ClientName; contractRecord.SellerId = isGJBuyer ? trade.ClientId : 0; contractRecord.SellerName = isGJBuyer ? trade.ClientName : PS.Config.CompanyFullName; contractRecord.FileType = FileTypeEnum.Word; contractRecord.Status = ContractStatusEnum.Draft; contractRecord.Paths = $"App_Docs\\GTJA\\{outPutFileName}"; contractRecord.FileName = outPutFileName; contractRecord.Type = ContractTypeEnum.Trade; contractRecord.OptId = UserId; contractRecord.OptName = UserName; contractRecord.OptDate = nowDate; var contractTradeRelation = new trade_contract_r(); DbContext.trade_contract_r.Add(contractTradeRelation); contractTradeRelation.TradeId = trade.id; contractTradeRelation.TradeNumber = trade.TradeNumber; contractTradeRelation.ContractCode = contractRecord.Code; contractTradeRelation.Type = ContractTypeEnum.Trade; contractTradeRelation.OptId = UserId; contractTradeRelation.OptName = UserName; contractTradeRelation.OptDate = nowDate; contractTradeRelation.TradeCashId = contractGroup.trade_cash == null ? 0 : contractGroup.trade_cash.id; contractTradeRelation.IsValid = true; DbContext.SaveChanges(); } #endregion --- Add trade_contract_document and trade_contract_r records return new TradeDocGenerateResult(trade) { OutputFilePath = $"/App_Docs/GTJA/{(docType == "PDF" ? outPutFileName.Replace(".docx", ".pdf") : outPutFileName)}" }; } catch (Exception e) { batchLogger.Error($"[BatchGenerateSingleV1] 发生异常 - TradeId: {trade?.id}, TradeNumber: {trade?.TradeNumber}", e); batchLogger.Error($"[BatchGenerateSingleV1] 异常类型: {e.GetType().FullName}"); batchLogger.Error($"[BatchGenerateSingleV1] 异常消息: {e.Message}"); batchLogger.Error($"[BatchGenerateSingleV1] 异常堆栈: {e.StackTrace}"); if (e.InnerException != null) { batchLogger.Error($"[BatchGenerateSingleV1] 内部异常类型: {e.InnerException.GetType().FullName}"); batchLogger.Error($"[BatchGenerateSingleV1] 内部异常消息: {e.InnerException.Message}"); batchLogger.Error($"[BatchGenerateSingleV1] 内部异常堆栈: {e.InnerException.StackTrace}"); } LogFactory.GetLogger().Error("GenerateSingleConfirmBook出错", e); return new TradeDocGenerateResult(trade) { ErrorMessage = e.Message }; } } } }