Files
zszq-trs/YLErpDAL/Modules/TradeModule/DocGenerateModule/ConfirmationGenerateService.cs
T
tengyufan e1c5f72418 #EQD-6604 国联民生-确认书编码生成环节移到簿记(支持手工录入)
feat: 国联民生收益互换簿记后按确认书规则生成交易编号

- - 国联收益互换首次入库后复用原交易确认书编号规则生成交易编号,保留手工填写的交易编号
- 覆盖手工新增、系统流水自动簿记、普通交易导入、流水导入和日终合成今日交易流水
- 自动流水簿记增加本地事务,交易编号回写与基础簿记数据失败时统一回滚
- 交易确认书生成时直接取交易编号,不再重新生成确认书编号
- 结算管理-交易确认书页面将合约编号更名为交易确认书编号
- 上传确认书时将合约编号更名为交易确认书编号
- 历史交易编号不处理
2026-07-29 16:45:29 +08:00

1201 lines
53 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using NPOI.Util;
using Org.BouncyCastle.Asn1.Ocsp;
using System.Text;
using YLErp.BLL;
using YLErp.BLL.GenerateDocument;
using YLErp.DBModels.Consts;
using YLErp.Helpers;
using YLErp.Model;
using YLErp.Model.Enum;
using YLErp.Models;
using YLErp.Modules.TradeDalModule;
using YLErp.MsOffice;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.Office;
using YLErp.Office.Converters;
using YLErp.Plugins.TradeDocGenerator.Abstracts;
namespace YLErp.Modules.TradeModule.DocGenerateModule
{
/// <summary>
/// 生成交易确认书
/// </summary>
public class ConfirmationGenerateService : YLBaseService
{
public ConfirmationGenerateService(OptUserInfo userInfo) : base(userInfo)
{
}
public ConfirmationGenerateService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 生成交易确认书
/// </summary>
public List<TradeDocGenerateResult> Generate(IEnumerable<int> tradeIds, string docType)
{
var logger = LogFactory.GetLogger("生成确认书详细日志");
logger.Info($"[Generate] 开始生成确认书 - tradeIds: {string.Join(",", tradeIds)}, docType: {docType}");
if (tradeIds == null)
{
logger.Error("[Generate] tradeIds 为 null");
throw new ArgumentNullException(nameof(tradeIds));
}
if (docType == null)
{
logger.Error("[Generate] docType 为 null");
throw new ArgumentNullException(nameof(docType));
}
//----------------------------------
// 获取当日行权 平仓 到期的有效交易
//----------------------------------
List<string> statusList = new List<string>() {
ConsTrade.确认成交
};
if (GlobalConfig.EnableGenerateOnTradeClosed)
{
statusList.Add(ConsTrade.已到期);
statusList.Add(ConsTrade.已平仓);
statusList.Add(ConsTrade.已执行);
statusList.Add(ConsTrade.审批中);
}
TradeDalService service = new TradeDalService(this);
//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;
//----------------------------------
// 交易现金交割记录
//----------------------------------
logger.Info("[Generate] 开始查询交易数据");
List<trade> allTradeList = allTradeList = DbContext.trade.Where(x => tradeIds.Contains(x.id)).ToList();
logger.Info($"[Generate] 查询到交易数: {allTradeList.Count}");
if (allTradeList.Count < 1)
{
logger.Error("[Generate] 没有找到交易记录");
throw new ServiceException("没有已确认的记录信息!");
}
var tradeDic = allTradeList.Where(O =>
statusList.Contains(O.TradeStatus)
&& O.ClientId != 0
&& O.ParentTradeId == 0)
.ToDictionary(n => n.id, n => new trade_contract_group { trade = n });
var cashQuery = from trade in allTradeList
join tradeCash in DbContext.trade_cash on trade.id equals tradeCash.TradeId
where tradeCash.ValidState != ConsGlobal.InValid && !tradeCash.IsDeleted
&& tradeCash.Action == ClientCashInCashOut.系统操作_期权费
select tradeCash;
var cashDatas = cashQuery.ToDictionary(K => K.TradeId, V => V);
//var cashSwapQuery = from trade in allTradeList
// join tradeCashSwap in DbContext.swap_event on trade.id equals tradeCashSwap.SwapTradeId
// where tradeCashSwap.EventType == 9
// select tradeCashSwap;
//var cashSwapDatas = cashSwapQuery.ToDictionary(K => K.SwapTradeId, V => V);
trade_cash cash = null;
//swap_event cashSwap = null;
foreach (var item in tradeDic.Values)
{
if (item.trade.TradeType == "结构化交易")
{
item.trade.SubTrades = allTradeList.Where(O => O.ParentTradeId == item.trade.id);
List<string> cashTig = new List<string>();
foreach (var t in item.trade.SubTrades)
{
if (cashDatas.TryGetValue(t.id, out cash))
{
t.trade_cash = cash;
item.trade.trade_cash = cash;
}
}
if (item.trade.IsGroup == 1)
{
item.trade.SubTrades = DbContext.trade.Where(O => O.ParentTradeId == item.trade.id).ToList();
foreach (var t in item.trade.SubTrades)
{
t.trade_cash = DbContext.trade_cash.FirstOrDefault(l => l.TradeId == t.id);
}
if (cashDatas.TryGetValue(item.trade.id, out cash))
{
item.trade.trade_cash = cash;
}
}
}
else
{
if (item.trade.TradeType == "累计期权")
{
new OptionTradeActionRestoreService(this).RestoreTradeDataToSpecialDay(item.trade, item.trade.TradeDate.Value);
}
if (cashDatas.TryGetValue(item.trade.id, out cash))
{
item.trade_cash = cash;
item.trade.trade_cash = cash;
}
}
}
//----------------------------------
// 生成交易确认书
//----------------------------------
var resultList = new List<TradeDocGenerateResult>();
AppManager.TryResolve(out ITradeConfirmationGenerator generatorV2);
if (generatorV2 == null && !PS.Config.UseOldGenerateBook)
{
var baseGenerate = BatchGenerateFactory.GetInstance(tradeIds, UserId, UserName, ContractTypeEnum.Trade, docType);
var returnInfo = baseGenerate.GenerateBooks();
var trad = new trade();
resultList = returnInfo.Content.Select(n => new TradeDocGenerateResult(trad) { OutputFilePath = n }).ToList();
if (!returnInfo.Success)
{
resultList.Add(new TradeDocGenerateResult(trad) { ErrorMessage = returnInfo.Msg });
}
return resultList;
}
var tradeContractGroupTradeIds = tradeDic.Values.Select(x => x.trade.id).ToList();
//var childs = DbContext.trade.Where(x => tradeContractGroupTradeIds.Contains(x.ParentTradeId ?? 0));
var childs = service.GetStructTradeFromTradeOrEodTrade(tradeContractGroupTradeIds, SystemValueDate, true);
LogFactory.GetLogger("交易确认书").Info("交易确认书1:" + string.Join(",", tradeContractGroupTradeIds));
if (PS.Config.Company == Configuration.CompanyEnum.浙商)
{
LogFactory.GetLogger("交易确认书").Info("交易确认书浙商");
if (generatorV2 != null)
{
LogFactory.GetLogger("交易确认书").Info("交易确认书插件");
var result = GenerateSingleV2(generatorV2, null, docType , allTradeList);
resultList.Add(result);
}
}
else
{
foreach (var tradeContractGroup in tradeDic.Values)
{
//判断是否为期权交易(非收益互换的交易强制走默认逻辑V1,不走插件)
var isOptionTrade = tradeContractGroup.trade.TradeType != "收益互换";
//插件版本(期权交易强制使用V1默认逻辑)
if ((tradeContractGroup.trade.TradeType != "结构化交易" && tradeContractGroup.trade.TradeType != "收益互换" && tradeContractGroup.trade_cash == null)
|| (tradeContractGroup.trade.TradeType == "结构化交易" && tradeContractGroup.trade.IsGroup == 0 && tradeContractGroup.trade.SubTrades.Any(O => O.trade_cash == null)))
{
resultList.Add(new TradeDocGenerateResult(tradeContractGroup.trade)
{
ErrorMessage = "缺少现金交割记录"
});
}
else if (generatorV2 != null && !isOptionTrade)
{
var result = GenerateSingleV2(generatorV2, tradeContractGroup, docType);
resultList.Add(result);
}
else
{
var result = GenerateSingleV1(tradeContractGroup, docType);
resultList.Add(result);
//if (!string.IsNullOrWhiteSpace(result.OutputFilePath))
//{
// tradeContractGroup.trade.HasGeneratedConfirmBook = 1;
// var childTds = childs.Where(x => x.ParentTradeId == tradeContractGroup.trade.id);
// foreach (var child in childTds)
// {
// child.HasGeneratedConfirmBook = 1;
// }
//}
}
}
}
if (generatorV2 == null)
{
DbContext.SaveChanges();
}
return resultList;
}
/// <summary>
///
/// </summary>
private TradeDocGenerateResult GenerateSingleV2(ITradeConfirmationGenerator generator, trade_contract_group tradeContractGroup, string docType = "DOCX", List<trade> allTrades = null)
{
try
{
//修改调用的构造方法
List<trade> trades = new List<trade>();
if (allTrades != null && allTrades.Count() > 0)
{
trades = allTrades;
}else
{
trades = new List<trade>() { tradeContractGroup.trade };
}
List<int> ids = new List<int>();
if (tradeContractGroup != null && tradeContractGroup.trade != null && tradeContractGroup.trade.trade_cash != null)
{
ids.Add(tradeContractGroup.trade.trade_cash.id);
}
var context = new ConfirmationGenerateContext(
ids,
trades,
ContractTypeEnum.Trade, OptUser, null, null);
generator.Initialize(docType, context);
LogFactory.GetLogger("交易确认书").Info("交易确认书init");
return generator.GenerateDocument();
}
catch (Exception ex)
{
LogFactory.GetLogger("GenerateSingleV2").Error(ex);
return new TradeDocGenerateResult(tradeContractGroup.trade)
{
ErrorMessage = ex.Message
};
}
}
/// <summary>
///
/// </summary>
private TradeDocGenerateResult GenerateSingleV1(trade_contract_group contractGroup, string docType = "DOCX")
{
var logger = LogFactory.GetLogger("生成确认书详细日志");
logger.Info($"[GenerateSingleV1] 开始处理 - docType: {docType}");
if (contractGroup == null)
{
logger.Error("[GenerateSingleV1] contractGroup 为 null");
throw new ArgumentNullException(nameof(contractGroup));
}
var trade = contractGroup.trade;
logger.Info($"[GenerateSingleV1] 交易信息 - 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 = YLErp.Helpers.PathHelper.PhysicalPathToWebPath(pdfPath, OtcAppContext.RootPath)
};
}
return new TradeDocGenerateResult(trade)
{
OutputFilePath = YLErp.Helpers.PathHelper.PhysicalPathToWebPath(confirmDoc.AbsolutePath.Replace(".pdf", ".docx"), 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<string, object>
{
["{{乙方名称}}"] = 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;
}
string strUnit = underlying.TradeUnitString;
double notional = trade.Notional;
dic["{{报价单位}}"] = strUnit;
dic["{{商品标的数量}}"] = $"{notional}{strUnit}";
dic["{{商品标的交易所}}"] = underlying.MarketName;
//价格
if (trade.ActualStrike != null)
{
if (trade.IsMoneynessOption == "是")
{
dic["{{执行价格}}"] = trade.Strike.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}期权";
if (trade.TradeType == "远期")
{
dic["{{期权费}}"] = $"{(trade.TradePrice * (trade.BuySell == "卖出" ? -1 : 1) ?? 0).OtcFormatMoney(grouping: false)}元";
}
else
{
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.OtcFormatUmPrice();
dic["{{期权费率}}"] = ((trade.PremiumRate ?? ((trade.TradePrice ?? 0) / trade.StockEqvNotional)) * 100).OtcFormatFlex(2);
string 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 = DbContextFactory.GetClientDbContext(null).bankcard.FirstOrDefault(s => s.ApprovalOrder < 1 && s.ClientId == trade.ClientId && s.ValidState == "Valid");
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 confirmLogger = LogFactory.GetLogger("生成确认书详细日志");
try
{
confirmLogger.Info($"[GenerateSingleConfirmBook] 开始生成 - TradeId: {trade?.id}, TradeNumber: {trade?.TradeNumber}, TradeType: {trade?.TradeType}");
confirmLogger.Info($"[GenerateSingleConfirmBook] 模板路径配置 - TradeType: {trade.TradeType}, IsUsePremiumRate: {trade.IsUsePremiumRate}");
var templateDir = OtcAppContext.MapPath("~/App_Docs/GTJA");
confirmLogger.Info($"[GenerateSingleConfirmBook] templateDir: {templateDir}");
var templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书模板.docx");
confirmLogger.Info($"[GenerateSingleConfirmBook] 默认模板路径: {templatePath}");
if (trade.TradeType == "亚式期权")
{
templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书(亚式期权)模板.docx");
confirmLogger.Info($"[GenerateSingleConfirmBook] 使用亚式期权模板: {templatePath}");
}
if (trade.IsUsePremiumRate != null && trade.IsUsePremiumRate.Value)
{
templatePath = OtcAppContext.MapPath("~/App_Docs/导出模板/交易确认书(名义本金)模板.docx");
confirmLogger.Info($"[GenerateSingleConfirmBook] 使用名义本金模板: {templatePath}");
}
confirmLogger.Info($"[GenerateSingleConfirmBook] 最终模板路径: {templatePath}");
confirmLogger.Info($"[GenerateSingleConfirmBook] 输出文件名: {outPutFileName}");
// 检查模板文件是否存在
if (!File.Exists(templatePath))
{
confirmLogger.Error($"[GenerateSingleConfirmBook] 模板文件不存在: {templatePath}");
return new TradeDocGenerateResult(trade) { ErrorMessage = $"模板文件不存在: {templatePath}" };
}
confirmLogger.Info($"[GenerateSingleConfirmBook] 模板文件存在,准备生成");
LogFactory.GetLogger("交易确认书").Info("交易确认书:" + outPutFileName);
string outPutFilePath = Path.Combine(templateDir, outPutFileName);
confirmLogger.Info($"[GenerateSingleConfirmBook] 输出文件路径: {outPutFilePath}");
// 确保输出目录存在
if (!Directory.Exists(templateDir))
{
confirmLogger.Info($"[GenerateSingleConfirmBook] 创建输出目录: {templateDir}");
Directory.CreateDirectory(templateDir);
}
confirmLogger.Info($"[GenerateSingleConfirmBook] 调用 MsWordTempalteHelper.GenerateFromTemplate");
MsWordTempalteHelper.GenerateFromTemplate(templatePath, dic, outPutFilePath);
confirmLogger.Info($"[GenerateSingleConfirmBook] MsWordTempalteHelper.GenerateFromTemplate 完成");
if (docType == "PDF" && !outPutFilePath.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
{
MsWordFormatConverter.ConvertToPDF(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.SourceType = 1; // 系统生成
contractRecord.Paths = $"App_Docs\\GTJA\\{outPutFileName}";
contractRecord.FileName = outPutFileName;
contractRecord.Type = ContractTypeEnum.Trade;
contractRecord.ClientId = trade.ClientId;
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)
{
confirmLogger.Error($"[GenerateSingleConfirmBook] 发生异常 - TradeId: {trade?.id}, TradeNumber: {trade?.TradeNumber}", e);
confirmLogger.Error($"[GenerateSingleConfirmBook] 异常类型: {e.GetType().FullName}");
confirmLogger.Error($"[GenerateSingleConfirmBook] 异常消息: {e.Message}");
confirmLogger.Error($"[GenerateSingleConfirmBook] 异常堆栈: {e.StackTrace}");
// 检查是否是内部异常
if (e.InnerException != null)
{
confirmLogger.Error($"[GenerateSingleConfirmBook] 内部异常类型: {e.InnerException.GetType().FullName}");
confirmLogger.Error($"[GenerateSingleConfirmBook] 内部异常消息: {e.InnerException.Message}");
confirmLogger.Error($"[GenerateSingleConfirmBook] 内部异常堆栈: {e.InnerException.StackTrace}");
}
LogFactory.GetLogger<ConfirmationGenerateService>().Error("GenerateSingleConfirmBook出错", e);
return new TradeDocGenerateResult(trade) { ErrorMessage = e.Message };
}
}
public (bool, string, DateTime, string) UploadContractFile(string encryptId, string fileDescription, string ContractCode, bool OurpartySeal, bool CounterpartySeal, UploadFileModel uploadFileModel)
{
var contractCodeName = PS.Config.Company == YLErp.Configuration.CompanyEnum.国联
? "交易确认书编号"
: "合约编号";
try
{
var msg = "";
#region 验证
if (string.IsNullOrWhiteSpace(encryptId))
{
return (false, "请传入id", DateTime.Now, "");
}
var intid = DataProtectHelper.DecryptInt(encryptId);
var db = DbContextFactory.GetYLDbContext();
var contractDoc = db.trade_contract_document.Find(intid);
var contractDocRArr = db.trade_contract_r.Where(O => O.ContractCode == contractDoc.Code && O.Type == contractDoc.Type && O.IsValid).ToArray();
var contractDocR = contractDocRArr.FirstOrDefault();
if (contractDoc == null || contractDocR == null)
{
return (false, "没有相关的确认书文档", DateTime.Now, "");
}
var isExistCode = true;
if (!string.IsNullOrWhiteSpace(ContractCode))
{
var Codes = new List<string>
{
ContractCode
};
var oldDoc = db.trade_contract_document.Where(O => O.Code == ContractCode).FirstOrDefault();
isExistCode = oldDoc != null ||
(new TradeContractGenerateService(OptUser).GetExistsContractCode(Codes).Any());
if (isExistCode)
{
if (!contractDoc.Code.Equals(ContractCode))
{
if (db.trade_contract_r.Where(O => O.ContractCode == ContractCode && O.IsValid).Any())
{
return (false, $"{contractCodeName}已存在", DateTime.Now, "");
}
else
{
if (oldDoc != null)
{
db.trade_contract_document.Remove(oldDoc);
isExistCode = false;
}
}
}
}
msg = "修改成功";
}
#endregion
//var files = Request.Form.Files;
if (uploadFileModel?.Length > 0)
{
//var file = files[0];
// var str = Encoding.UTF8.GetString(fileBytes);
#region 验证
if (PS.Config.ErpElement.SecuritiesEnvironment)
{
if (System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".xlsx"
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".xls"
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".pdf"
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".docx"
&& System.IO.Path.GetExtension(uploadFileModel.FileName).ToLower() != ".doc")
{
return (false, "请上传PDF/Word/Excel格式的文件", DateTime.Now, "");
}
}
if (PS.Config.ErpElement.SecuritiesEnvironment && uploadFileModel.Length > Modules.SuperviseReportModule.SAC.Model.ReportStatusModel.MaxAnnexLength)//小于30MB
{
return (false, "上传失败,单笔确认书文件大小不应超过30MB", DateTime.Now, "");
}
#endregion
var tradeNumber = "未知";
if (contractDocR != null)
{
tradeNumber = contractDocR.TradeNumber;
}
var filePath = Path.Combine(OtcAppContext.AppDocsPath, "contractdoc", YLErp.PS.Config.Company.ToString(), tradeNumber);
if (!Directory.Exists(filePath))
{
Directory.CreateDirectory(filePath);
}
#region 设置用印文件名
var stampFileFullName = Path.Combine(filePath, uploadFileModel.FileName);
contractDoc.StampDocumentFileName = stampFileFullName.Replace(OtcAppContext.AppDocsPath, "/App_Docs").Replace("\\", "/");
#endregion
if (File.Exists(stampFileFullName))
{
File.Delete(stampFileFullName);
}
using var fs = new FileStream(stampFileFullName, FileMode.Create, FileAccess.ReadWrite);
//using var fs = File.OpenWrite(filePath);
using var sr = uploadFileModel.OpenReadStream();
sr.Seek(0, SeekOrigin.Begin);
var tempBytes = new byte[sr.Length];
sr.Read(tempBytes, 0, tempBytes.Length);
////var tempstr = Encoding.UTF8.GetString(tempBytes);
fs.Write(tempBytes, 0, tempBytes.Length);
sr.Close();
fs.Close();
//sr.CopyTo(fs);
msg = msg.Length > 0 ? msg : "上传成功";
}
#region 更新数据
contractDoc.Comments = fileDescription;
if (OurpartySeal && CounterpartySeal)
{ contractDoc.Status = ContractStatusEnum.BothPartyStamped; }
else if (OurpartySeal) { contractDoc.Status = ContractStatusEnum.OurPartyStamped; }
else if (CounterpartySeal) { contractDoc.Status = ContractStatusEnum.CounterPartyStamped; }
else { contractDoc.Status = ContractStatusEnum.Draft; }
if (!isExistCode)//默认为存在,判断 要有修改的合约编号且修改后的编号不存在
{
//trade_meta
var meta = db.TradeMeta.Where(O => (O.MetaKey == ConsTradeMetaKey.ContractCode || O.MetaKey.Contains("确认书")) && O.MetaValue == contractDoc.Code).FirstOrDefault();
if (meta != null)
{
meta.MetaValue = ContractCode;
}
foreach (var docR in contractDocRArr)
{
docR.ContractCode = ContractCode;
docR.OptDate = DateTime.Now;
docR.OptId = OptUser.UserId;
docR.OptName = OptUser.UserName;
}
contractDocR.ContractCode = ContractCode;
contractDocR.TradeClearCode = ContractCode;
contractDoc.OptDate = DateTime.Now;
contractDoc.OptId = OptUser.UserId;
contractDoc.OptName = OptUser.UserName;
contractDoc.Code = ContractCode;
}
new TradeConfirmService(OptUser, db).EditReportStatus(contractDocR.TradeId, true);
db.SaveChanges();
#endregion
return (true, msg, contractDoc.OptDate.Value, contractDoc.StampDocumentFileName);
}
catch (Exception ex)
{
LogFactory.GetLogger("UploadContractFile").Error("UploadContractFile出错", ex);
return (false, "操作出错", DateTime.Now, "");
}
}
/// <summary>
/// 单笔交易上传确认书。
/// <para>会将该 tradeId 对应的旧 trade_contract_r 置为无效,并新增一条有效记录;同时创建/更新 trade_contract_document 并保存上传文件。</para>
/// </summary>
public UploadContractFileNewResult UploadContractFileNew(
int tradeId,
bool useContractCode,
string contractCode,
bool isCounterpartyDoc,
UploadFileModel uploadFileModel)
{
var nowDate = DateTime.Now;
var isGuolian = PS.Config.Company == YLErp.Configuration.CompanyEnum.国联;
var contractCodeName = isGuolian
? "交易确认书编号"
: "合约编号";
if (tradeId <= 0)
{
return new UploadContractFileNewResult { Success = false, Message = "请传入参数", OptDate = nowDate, DocumentPath = string.Empty };
}
if (!TryValidateUploadContractFileNew(new[] { tradeId }, uploadFileModel, out _, out var errMsg))
{
return new UploadContractFileNewResult { Success = false, Message = errMsg, OptDate = nowDate, DocumentPath = string.Empty };
}
try
{
var db = DbContextFactory.GetYLDbContext();
var trade = db.trade.AsNoTracking().FirstOrDefault(t => t.id == tradeId && t.ValidState != "InValid");
if (trade == null)
{
return new UploadContractFileNewResult { Success = false, Message = "未找到交易数据", OptDate = nowDate, DocumentPath = string.Empty };
}
var oldR = db.trade_contract_r
.Where(r => r.TradeId == tradeId && r.Type == ContractTypeEnum.Trade && r.IsValid)
.OrderByDescending(r => r.id)
.FirstOrDefault();
var oldCode = (oldR?.ContractCode ?? "").Trim();
var activeCode = "";
if (useContractCode)
{
activeCode = (contractCode ?? "").Trim();
if (string.IsNullOrWhiteSpace(activeCode))
{
return new UploadContractFileNewResult { Success = false, Message = $"{contractCodeName}必填", OptDate = nowDate, DocumentPath = string.Empty };
}
if (!string.Equals(oldCode, activeCode, StringComparison.OrdinalIgnoreCase))
{
var isExists = new TradeContractGenerateService(OptUser).GetExistsContractCode(new[] { activeCode }).Any();
if (isExists)
{
return new UploadContractFileNewResult { Success = false, Message = $"{contractCodeName}已存在", OptDate = nowDate, DocumentPath = string.Empty };
}
}
}
else
{
if (!string.IsNullOrWhiteSpace(oldCode))
{
activeCode = oldCode;
}
else
{
if (isGuolian)
{
activeCode = trade.TradeNumber;
}
else
{
var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId);
if (string.IsNullOrEmpty(client?.Code))
{
return new UploadContractFileNewResult { Success = false, Message = "对手方代码缩写缺失,请联系运营组同事维护", OptDate = nowDate, DocumentPath = string.Empty };
}
activeCode = GenerateContractCodeForTrade(db, trade, client?.Code);
}
}
}
if (string.IsNullOrWhiteSpace(activeCode))
{
return new UploadContractFileNewResult { Success = false, Message = $"{contractCodeName}生成失败", OptDate = nowDate, DocumentPath = string.Empty };
}
trade_contract_document oldDoc = null;
if (!string.IsNullOrWhiteSpace(oldCode))
{
oldDoc = db.trade_contract_document.FirstOrDefault(d => d.Code == oldCode && d.Type == ContractTypeEnum.Trade);
}
var oldValidRs = new List<trade_contract_r>();
if (oldR != null)
{
oldValidRs.Add(oldR);
oldR.IsValid = false;
oldR.OptDate = nowDate;
oldR.OptId = OptUser.UserId;
oldR.OptName = OptUser.UserName;
}
var newDoc = new trade_contract_document();
db.trade_contract_document.Add(newDoc);
if (oldDoc != null)
{
newDoc.ValueDate = oldDoc.ValueDate;
newDoc.BuyerId = oldDoc.BuyerId;
newDoc.BuyerName = oldDoc.BuyerName;
newDoc.SellerId = oldDoc.SellerId;
newDoc.SellerName = oldDoc.SellerName;
newDoc.ContractVersion = oldDoc.ContractVersion;
newDoc.FileType = oldDoc.FileType;
newDoc.DayNumber = oldDoc.DayNumber;
newDoc.ClientId = oldDoc.ClientId;
newDoc.Comments = oldDoc.Comments;
db.trade_contract_document.Remove(oldDoc);
}
else
{
var isGJBuyer = trade.BuySell == "买入";
newDoc.ValueDate = valuedateBLL.ValueDate;
newDoc.BuyerId = isGJBuyer ? 0 : trade.ClientId;
newDoc.BuyerName = isGJBuyer ? (PS.Config.CompanyName ?? "") : trade.ClientName;
newDoc.SellerId = isGJBuyer ? trade.ClientId : 0;
newDoc.SellerName = isGJBuyer ? trade.ClientName : (PS.Config.CompanyName ?? "");
newDoc.ClientId = trade.ClientId;
}
newDoc.Code = activeCode;
newDoc.Status = ContractStatusEnum.Draft;
newDoc.SourceType = isCounterpartyDoc ? 2 : 1;
newDoc.Type = ContractTypeEnum.Trade;
newDoc.OptId = OptUser.UserId;
newDoc.OptName = OptUser.UserName;
newDoc.OptDate = nowDate;
SaveUploadFile(activeCode, uploadFileModel, out var outputRelativeDir);
newDoc.Paths = outputRelativeDir;
newDoc.FileName = uploadFileModel.FileName;
var newR = new trade_contract_r();
db.trade_contract_r.Add(newR);
newR.TradeId = tradeId;
newR.TradeNumber = trade.TradeNumber;
newR.ContractCode = activeCode;
newR.TradeClearCode = activeCode;
newR.Type = ContractTypeEnum.Trade;
newR.IsValid = true;
newR.OptId = OptUser.UserId;
newR.OptName = OptUser.UserName;
newR.OptDate = nowDate;
new TradeConfirmService(OptUser, db).EditReportStatus(tradeId, true);
db.SaveChanges();
return new UploadContractFileNewResult
{
Success = true,
Message = "上传成功",
OptDate = newDoc.OptDate ?? nowDate,
DocumentPath = newDoc.RelativePath
};
}
catch (Exception ex)
{
LogFactory.GetLogger("UploadContractFileNew").Error("UploadContractFileNew出错", ex);
return new UploadContractFileNewResult { Success = false, Message = "操作出错", OptDate = nowDate, DocumentPath = string.Empty };
}
}
private static string GenerateContractCodeForTrade(YLContext db, trade trade, string clientCode)
{
if (trade.TradeType == "收益互换")
{
return GuolianContractNoGenerator.Generate(db, trade, clientCode ?? string.Empty);
}
return trade.TradeNumber;
}
private static bool TryValidateUploadContractFileNew(
IReadOnlyCollection<int> tradeIds,
UploadFileModel uploadFileModel,
out HashSet<int> tradeIdSet,
out string errMsg)
{
tradeIdSet = new HashSet<int>();
errMsg = null;
if (tradeIds == null || tradeIds.Count == 0)
{
errMsg = "请传入参数";
return false;
}
if (uploadFileModel == null || uploadFileModel.Length <= 0)
{
errMsg = "请选择需上传的文件";
return false;
}
var fileExt = System.IO.Path.GetExtension(uploadFileModel.FileName)?.ToLowerInvariant();
if (fileExt != ".xlsx" && fileExt != ".xls" && fileExt != ".pdf" && fileExt != ".docx" && fileExt != ".doc")
{
errMsg = "请上传PDF/Word/Excel格式的文件";
return false;
}
foreach (var tid in tradeIds)
{
if (tid > 0)
{
tradeIdSet.Add(tid);
}
}
if (tradeIdSet.Count == 0)
{
errMsg = "请传入参数";
return false;
}
return true;
}
/// <summary>
/// 保存上传的文件到服务器,并返回相对路径和文件名
/// </summary>
/// <param name="contractCode"></param>
/// <param name="uploadFileModel"></param>
/// <param name="outputRelativeDir"></param>
/// <param name="outputFileName"></param>
private static void SaveUploadFile(string contractCode, UploadFileModel uploadFileModel, out string outputRelativeDir)
{
var outputDir = Path.Combine(OtcAppContext.AppDocsPath, "contractdoc", "output");
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
var ext = Path.GetExtension(uploadFileModel.FileName) ?? "";
var outputFullPath = Path.Combine(outputDir, uploadFileModel.FileName);
if (File.Exists(outputFullPath))
{
File.Delete(outputFullPath);
}
using (var fs = new FileStream(outputFullPath, FileMode.Create, FileAccess.Write, FileShare.None))
using (var sr = uploadFileModel.OpenReadStream())
{
sr.CopyTo(fs);
}
outputRelativeDir = Path.Combine("App_Docs", "contractdoc", "output");
}
public string GetSealErrMsg(string code)
{
var db = DbContextFactory.GetYLDbContext();
var errMsg = db.trade_contract_document.FirstOrDefault(x => x.Code == code && x.SealResult == 1);
return errMsg == null ? "" : errMsg.SealErrMsg;
}
public bool IsSealWaiting(int tradeId)
{
var db = DbContextFactory.GetYLDbContext();
var code = db.trade_contract_r.FirstOrDefault(x => x.TradeId == tradeId)?.ContractCode;
var flag = code != null && db.trade_contract_document.Any(x => x.Code == code && x.SealResult == 3);
return flag;
}
/// <summary>
/// 判断确认书是否生成过
/// </summary>
/// <param name="ids">trade_contract_r.id</param>
/// <returns></returns>
public bool IsContractGenerated(List<int> ids,List<string> types)
{
var db = DbContextFactory.GetYLDbContext();
var flag = true;
var tradeContractList = (from tradeContractR in db.trade_contract_r
join tradeContractDoc in (from tradeContractDocTemp in db.trade_contract_document select tradeContractDocTemp)
on tradeContractR.ContractCode equals tradeContractDoc.Code into tradeContractDoc1
from tradeContractDoc2 in tradeContractDoc1.DefaultIfEmpty()
where ids.Contains(tradeContractR.id) && types.Contains(tradeContractR.Type) && tradeContractR.IsValid
select new
{
tradeContractR.id,
tradeContractR.TradeId,
tradeContractR.ContractCode,
tradeContractDoc = tradeContractDoc2
}).ToList();
if (tradeContractList.Count == 0 || tradeContractList.Any(x => x.tradeContractDoc == null || string.IsNullOrWhiteSpace(x.tradeContractDoc.Paths)))
{
flag = false;
return flag;
}
foreach (var item in tradeContractList)
{
var path = Path.ChangeExtension(item.tradeContractDoc.AbsolutePath, "pdf");
if (!File.Exists(path))
{
flag = false;
break;
}
}
return flag;
}
/// <summary>
/// 批量用印
/// </summary>
/// <param name="ids">trade_contract_r.id</param>
/// <param name="type"></param>
public void SealContracts(List<int> ids,List<string> types)
{
var db = DbContextFactory.GetYLDbContext();
var contract = (from tradeContractR in db.trade_contract_r
join tradeContractDoc in (from tradeContractDocTemp in db.trade_contract_document
select tradeContractDocTemp)
on tradeContractR.ContractCode equals tradeContractDoc.Code into tradeContractDoc1
from tradeContractDoc2 in tradeContractDoc1.DefaultIfEmpty()
where ids.Contains(tradeContractR.id) && types.Contains(tradeContractR.Type) && tradeContractR.IsValid
select new
{
tradeContractR.id,
tradeContractR.TradeId,
tradeContractR.ContractCode,
tradeContractDoc = tradeContractDoc2
}).ToList();
foreach (var item in contract)
{
if (item.tradeContractDoc != null)
{
GlobalConfig.SealHandler.RemoveQueue(item.TradeId);
GlobalConfig.SealHandler.AddQueue(item.tradeContractDoc, item.TradeId);
}
}
}
}
}