新增国联证券专用业务逻辑及文档生成插件

This commit is contained in:
锦麟 王
2026-04-10 09:22:34 +08:00
parent eccbab4b2a
commit 44c3c87304
12 changed files with 821 additions and 0 deletions
@@ -0,0 +1,392 @@
using Newtonsoft.Json.Linq;
using YLErp.Core.Helpers;
using YLErp.DBModels;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.Modules.UnderlyingModule;
using YLErp.Plugins.TradeDocGenerator;
using YLErp.Plugins.TradeDocGenerator.Abstracts;
namespace YLErp.Plugins.GuoLian.DocumentGenerator
{
/// <summary>
/// 国联证券交易确认书生成器(单个生成)
/// </summary>
internal class TradeConfirmationGenerator : BaseConfirmationGenerator, ITradeConfirmationGenerator
{
public TradeConfirmationGenerator()
: base(ContractTypeEnum.Trade)
{
}
/// <summary>
/// 获取模板文件路径
/// 收益互换模板选择逻辑:
/// 1. 客户看多/看空:通过持仓方向判断(PositionType: 1=多头/看多,2=空头/看空)
/// 2. 标的类型:
/// - 债券ETF.SH后缀且511开头,或.SZ后缀且59开头
/// - 现券:其他
/// </summary>
protected override string GetTemplateFilePath()
{
var trade = Context.Trade;
if (trade.TradeType == "收益互换")
{
return GetSwapTemplatePath();
}
else if (trade.TradeType.Contains("期权"))
{
// 期权使用期权模板
return Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\contract_template\\option_01.docx");
}
else
{
throw new Exception($"不支持的贸易类型: {trade.TradeType}");
}
}
/// <summary>
/// 获取收益互换模板路径
/// </summary>
private string GetSwapTemplatePath()
{
var trade = Context.Trade;
var underlying = Context.GetTradeUnderlying();
// 判断看多/看空方向
bool isLong = IsLongPosition(trade);
// 判断标的类型(债券ETF vs 现券)
bool isEtf = IsBondEtf(underlying?.UnderlyingCode ?? string.Empty);
// 选择对应模板(使用客户提供的原始文件名)
string templateName;
if (isLong && !isEtf)
templateName = "国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.docx";
else if (isLong && isEtf)
templateName = "国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx";
else if (!isLong && !isEtf)
templateName = "国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx";
else
templateName = "国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx";
return Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\contract_template", templateName);
}
/// <summary>
/// 判断是否为多头/看多方向
/// </summary>
private bool IsLongPosition(OtcTradeBase trade)
{
// 通过BuySell判断:买入=看多,卖出=看空
return trade.BuySell == "买入";
}
/// <summary>
/// 判断是否为债券ETF
/// - .SH后缀且511开头 -> 债券ETF
/// - .SZ后缀且59开头 -> 债券ETF
/// - 其他 -> 现券
/// </summary>
private bool IsBondEtf(string underlyingCode)
{
if (string.IsNullOrWhiteSpace(underlyingCode))
return false;
underlyingCode = underlyingCode.Trim().ToUpper();
// 上交所债券ETF511开头.SH后缀
if (underlyingCode.EndsWith(".SH") && underlyingCode.StartsWith("511"))
return true;
// 深交所债券ETF:59开头.SZ后缀
if (underlyingCode.EndsWith(".SZ") && underlyingCode.StartsWith("59"))
return true;
return false;
}
/// <summary>
/// 获取输出文件名
/// </summary>
protected override string GetOutputFileName(string contractNo, string contractIndex)
{
var trade = Context.Trade;
var rule = $"{trade.TradeType}交易确认书_{contractNo}";
return $"{rule}.{DocType.ToLower()}";
}
/// <summary>
/// 获取合同编号
/// </summary>
protected override string GetContractNo(out string contractIndex)
{
var contractNo = Context.GenerateContractNo(out contractIndex);
return contractNo;
}
/// <summary>
/// 准备视图数据(Word模板数据)
/// </summary>
protected override void PrepareViewData(Dictionary<string, JToken> dic)
{
var trade = Context.Trade;
var client = Context.GetClient();
// 基础信息
// 注意:合同编号由基类统一设置,此处不再重复生成
dic["交易编号"] = trade.TradeNumber;
dic["客户名称"] = client.Name;
dic["乙方名称"] = client.Name;
// 交易日期相关
dic["成交日期"] = trade.TradeDate?.ToString("yyyy年M月d日");
dic["开始日期"] = trade.StartDate?.ToString("yyyy年M月d日");
dic["到期日期"] = trade.ExerciseDate?.ToString("yyyy年M月d日");
// 根据交易类型填充不同数据
switch (trade.TradeType)
{
case "收益互换":
SetSwapData(dic);
break;
case "香草期权":
case "亚式期权":
case "障碍期权":
SetOptionData(dic);
break;
default:
// 默认基础数据
SetBaseData(dic);
break;
}
}
/// <summary>
/// 设置收益互换数据
/// </summary>
private void SetSwapData(Dictionary<string, JToken> dic)
{
var trade = Context.Trade;
var client = Context.GetClient();
var bank = Context.GetClientBankCard(false);
dic["交易对手方全称"] = client.ClientType != "产品"
? client.Name
: $"{client.Manager}作为管理人代表{client.Name}";
dic["主协议编号"] = client.MainProtocolCode ?? "";
dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
// 银行账户信息
dic["户名"] = bank?.ClientName ?? "";
dic["银行账号"] = bank?.Card ?? "";
dic["开户行"] = bank?.Bank ?? "";
dic["大额行号"] = bank?.Payment ?? "";
// 标的相关的数据可以通过 Context 获取
var underlying = Context.GetTradeUnderlying();
if (underlying != null)
{
dic["标的代码"] = underlying.UnderlyingCode;
dic["标的名称"] = underlying.UnderlyingName;
// 标的发行人(从债券信息中获取)
var bond = JsonHelper.Deserialize<UnderlyingBond>(underlying.ExJson) ?? new UnderlyingBond();
dic["参考标的发行人"] = bond.UnderlyingIssuer ?? "";
dic["票面利率"] = (bond.CouponRate ?? 0).ToString("N4");
dic["参考标的到期日"] = underlying.MaturityDate?.ToString("yyyy年M月d日") ?? "";
}
// 从swap_position获取期初价格、保证金率等信息
var swapPositions = Context.GetSwapPositions(trade.id, true);
var swapPosition = swapPositions
.Where(x => x.PositionType == (int)PositionTypeFlag.Long || x.PositionType == (int)PositionTypeFlag.Short)
.FirstOrDefault();
if (swapPosition != null)
{
// 期初全价和净价(转换为百分比格式)
dic["参考标的期初全价"] = ((double)swapPosition.PosiGrossPrice * 100).ToString("N4");
dic["参考标的期初净价"] = ((double)(swapPosition.PosiNetNoFeePrice ?? 0m) * 100).ToString("N4");
// 固定收益率(年化)- 债券期初到期收益率
dic["固定收益率(年化)"] = swapPosition.InitYtm.HasValue
? ((double)swapPosition.InitYtm.Value * 100).ToString("N4")
: "0.0000";
// 获取客户适用的保证金率
var clientMarginRate = UnderlyingHelper.GetApplicableMarginRate(
client.id, swapPosition.UnderlyingCode, trade.TradeDate ?? DateTime.Now);
if (clientMarginRate != null)
{
dic["初始保障金率"] = ((double)clientMarginRate.init_rate * 100).ToString("N4");
dic["维持保障金率"] = ((double)clientMarginRate.maintain_rate * 100).ToString("N4");
// 期初预付比例和金额
dic["期初预付比例"] = ((double)clientMarginRate.init_rate * 100).ToString("N4");
dic["期初预付金额"] = ((trade.OriginalStockEqvNotional ?? 0) * (double)clientMarginRate.init_rate).ToString("N2");
}
else
{
dic["初始保障金率"] = "0.0000";
dic["维持保障金率"] = "0.0000";
dic["期初预付比例"] = "0.0000";
dic["期初预付金额"] = "0.00";
}
// 期初预付金利率(InterestMode == 初始预付金)
var initialMarginPosition = swapPositions
.Where(x => x.InterestMode == (int)InterestModeEnum.)
.FirstOrDefault();
dic["期初预付金利率"] = initialMarginPosition != null
? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4")
: "0.0000";
// 利率类型判断(固定/浮动)
var interestMargin = swapPositions
.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && x.interest_rest_days != null)
.FirstOrDefault();
if (interestMargin == null)
interestMargin = swapPositions
.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && string.IsNullOrWhiteSpace(x.FloatRateUnderlyingCode))
.FirstOrDefault();
if (interestMargin == null)
interestMargin = swapPositions
.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode))
.FirstOrDefault();
dic["IsFixed"] = "□";
dic["IsFloat"] = "□";
if (interestMargin != null)
{
if (string.IsNullOrEmpty(interestMargin.FloatRateUnderlyingCode))
{
dic["利率类型"] = "固定利率";
dic["IsFixed"] = "☑"; //☑
dic["固定利率"] = ((double)interestMargin.InterestRateDefault * 100).ToString("N4");
dic["利差"] = "";
}
else
{
dic["利率类型"] = "浮动利率";
dic["IsFloat"] = "☑";
dic["固定利率"] = "";
dic["利差"] = ((double)interestMargin.InterestRateDefault * 10000).ToString("N2");
dic["重置频率"] = (interestMargin.interest_rest_days ?? 0) + "天";
}
}
else
{
dic["利率类型"] = "固定利率";
dic["固定利率"] = "0.0000";
dic["利差"] = "";
}
// 基本费率 = PosiTradingFeePending / 名义本金 * 100
var notional = trade.OriginalStockEqvNotional ?? 0;
var tradingFee = (double)swapPosition.PosiTradingFeePending;
var basicFeeRate = notional == 0 ? 0 : tradingFee / notional * 100;
dic["基本费率"] = basicFeeRate.ToString("N4");
// 期初现金交换比例和金额(使用初始预付金数据)
dic["期初现金交换比例"] = initialMarginPosition != null
? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4")
: "0.0000";
dic["期初现金交换金额"] = initialMarginPosition != null
? ((double)initialMarginPosition.InterestPrincipalFix).ToString("N2")
: "0.00";
}
else
{
dic["参考标的期初全价"] = "0.0000";
dic["参考标的期初净价"] = "0.0000";
dic["初始保障金率"] = "0.0000";
dic["维持保障金率"] = "0.0000";
dic["期初预付比例"] = "0.0000";
dic["期初预付金额"] = "0.00";
dic["期初预付金利率"] = "0.0000";
dic["利率类型"] = "固定利率";
dic["固定利率"] = "0.0000";
dic["利差"] = "";
dic["基本费率"] = "0.0000";
dic["期初现金交换比例"] = "0.0000";
dic["期初现金交换金额"] = "0.00";
}
// 参考标的券面总额(名义本金)
dic["参考标的券面总额"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
// 参考标的证券全称和参考标的名义份额(复用上方已声明的bond)
dic["参考标的证券全称"] = underlying != null
? (JsonHelper.Deserialize<UnderlyingBond>(underlying.ExJson)?.UnderlyingFullName ?? underlying.UnderlyingName)
: "";
dic["参考标的名义份额"] = swapPosition != null
? ((double)swapPosition.PosiQuantity).ToString("N2")
: "0.00";
// 乙方联系人信息
var clientDuties = Context.GetClientDuties();
var contact = clientDuties.FirstOrDefault();
dic["联系人"] = contact?.ContactName ?? "";
dic["电子邮件"] = contact?.Email ?? "";
dic["电话"] = contact?.PhoneNumber ?? "";
// 表格数据示例
var table1 = new JArray();
var row = new JObject
{
["序号"] = 1,
["标的代码"] = underlying?.UnderlyingCode ?? "",
["标的名称"] = underlying?.UnderlyingName ?? "",
["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00",
["成交日期"] = trade.TradeDate?.ToString("yyyy年M月d日"),
["到期日期"] = trade.ExerciseDate?.ToString("yyyy年M月d日")
};
table1.Add(row);
dic["table1"] = table1;
}
/// <summary>
/// 设置期权数据
/// </summary>
private void SetOptionData(Dictionary<string, JToken> dic)
{
var trade = Context.Trade;
var client = Context.GetClient();
var underlying = Context.GetTradeUnderlying();
dic["期权类型"] = trade.OptionType;
dic["买卖方向"] = trade.BuySell;
dic["行权价格"] = trade.Strike?.ToString("N4") ?? "0.0000";
dic["名义本金"] = trade.StockEqvNotional.ToString("N2");
dic["期权费"] = trade.TradePrice?.ToString("N2") ?? "0.00";
dic["期初价格"] = trade.SpotPrice?.ToString("N4") ?? "0.0000";
if (underlying != null)
{
dic["标的代码"] = underlying.UnderlyingCode;
dic["标的名称"] = underlying.UnderlyingName;
dic["报价单位"] = underlying.QuoteUnitString ?? "元";
}
// 美式/欧式
dic["行权方式"] = trade.ExerciseMode == "American" ? "美式" : "欧式";
}
/// <summary>
/// 设置基础数据
/// </summary>
private void SetBaseData(Dictionary<string, JToken> dic)
{
var trade = Context.Trade;
var client = Context.GetClient();
dic["交易类型"] = trade.TradeType;
dic["客户编号"] = client.Number;
dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
}
}
}
@@ -0,0 +1,122 @@
using Newtonsoft.Json.Linq;
using YLErp.DBModels;
using YLErp.Models;
using YLErp.Plugins.TradeDocGenerator;
using YLErp.Plugins.TradeDocGenerator.Abstracts;
namespace YLErp.Plugins.GuoLian.DocumentGenerator
{
/// <summary>
/// 国联证券结算单生成器
/// </summary>
internal class TradeSettleBillGenerator : BaseSettleBillGenerator, ITradeSettleBillGenerator
{
protected override string GetContractNo(out string contractIndex)
{
var contractNo = Context.GenerateContractNo(out contractIndex);
return contractNo;
}
/// <summary>
/// 获取模板文件路径
/// </summary>
protected override string GetTemplateFilePath()
{
var templatePath = Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\settlement_template\\settle_01.docx");
return templatePath;
}
/// <summary>
/// 获取输出文件名
/// </summary>
protected override string GetOutputFileName(string contractNo, string contractIndex)
{
var client = Context.GetClient();
var valueDate = Context.SystemValueDate;
var fileName = $"结算单_{client.Name}_{valueDate:yyyyMMdd}_{contractIndex}";
return $"{fileName}.{DocType.ToLower()}";
}
/// <summary>
/// 准备Word模板数据
/// </summary>
protected override void PrepareViewData(Dictionary<string, JToken> dic)
{
var client = Context.GetClient();
var trade = Context.Trade;
var tradeCash = Context.GetTradeCash();
// 基础信息
// 注意:合同编号由基类统一设置,此处不再重复生成
dic["客户名称"] = client.Name;
dic["乙方名称"] = client.Name;
dic["结算日期"] = tradeCash?.ValueDate.ToString("yyyy年M月d日") ?? DateTime.Now.ToString("yyyy年M月d日");
// 交易信息
dic["交易编号"] = trade.TradeNumber;
dic["交易类型"] = trade.TradeType;
dic["标的代码"] = trade.UnderlyingCode;
var underlying = Context.GetTradeUnderlying();
if (underlying != null)
{
dic["标的名称"] = underlying.UnderlyingName;
}
// 结算金额相关
if (tradeCash != null)
{
dic["结算金额"] = tradeCash.Amount.ToString("N2");
dic["结算金额大写"] = Context.CmycurD(Math.Abs(tradeCash.Amount));
dic["结算价格"] = tradeCash.FinalPrice?.ToString("N4") ?? "0.0000";
dic["结算比例"] = tradeCash.UnwindPercentRate.HasValue
? (tradeCash.UnwindPercentRate.Value * 100).ToString("N2") + "%"
: "0.00%";
}
// 名义本金
dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00";
// 表格数据
var table1 = new JArray();
var row = new JObject
{
["交易编号"] = trade.TradeNumber,
["交易类型"] = trade.TradeType,
["标的代码"] = trade.UnderlyingCode,
["标的名称"] = underlying?.UnderlyingName ?? "",
["结算金额"] = tradeCash?.Amount.ToString("N2") ?? "0.00"
};
table1.Add(row);
dic["table1"] = table1;
}
/// <summary>
/// 准备Excel模板数据(如需要)
/// </summary>
protected override void PrepareViewData(Dictionary<string, object> dic)
{
var client = Context.GetClient();
var trade = Context.Trade;
var tradeCash = Context.GetTradeCash();
dic["合同编号"] = GetContractNo(out string contractIndex);
dic["客户名称"] = client.Name;
dic["结算日期"] = tradeCash?.ValueDate.ToString("yyyy-MM-dd") ?? DateTime.Now.ToString("yyyy-MM-dd");
dic["交易编号"] = trade.TradeNumber;
dic["交易类型"] = trade.TradeType;
dic["标的代码"] = trade.UnderlyingCode;
var underlying = Context.GetTradeUnderlying();
dic["标的名称"] = underlying?.UnderlyingName ?? "";
if (tradeCash != null)
{
dic["结算金额"] = tradeCash.Amount;
dic["结算价格"] = tradeCash.FinalPrice ?? 0;
}
dic["名义本金"] = trade.OriginalStockEqvNotional ?? 0;
}
}
}
@@ -0,0 +1,142 @@
using Newtonsoft.Json.Linq;
using YLErp.DBModels;
using YLErp.Plugins.TradeDocGenerator;
using YLErp.Plugins.TradeDocGenerator.Abstracts;
namespace YLErp.Plugins.GuoLian.DocumentGenerator
{
/// <summary>
/// 国联证券了结报告生成器
/// </summary>
internal class TradeUnwindReportGenerator : BaseUnwindReportGenerator, ITradeUnwindReportGenerator
{
protected override string GetContractNo(out string contractIndex)
{
var contractNo = Context.GenerateContractNo(out contractIndex);
return contractNo;
}
/// <summary>
/// 准备Word模板数据
/// </summary>
protected override void PrepareViewData(Dictionary<string, JToken> dic)
{
var table1 = new JArray();
var trade = Context.Trade;
var client = Context.GetClient();
var valueTimeList = new List<DateTime>();
var underlying = Context.GetTradeUnderlying();
var tradeCash = Context.GetTradeCash();
var tradeList = Context.Trades.Union(Context.SubTrades).ToList();
foreach (var item in Context.Trades)
{
foreach (var tradeCashItem in Context.TradeCashes[item.id])
{
var settlementPrice = (trade.IsUsePremiumRate != null && trade.IsUsePremiumRate.Value
? tradeCashItem.UnwindPricePercentRate.OtcFormatPercent()
: tradeCashItem.UnwindPrice.OtcFormatMoney());
var row = new JObject();
var currentTrade = tradeList.Find(o => o.id == tradeCashItem.TradeId);
row["交易编号"] = currentTrade?.TradeNumber ?? "";
// 买卖方向处理
switch (item.BuySell)
{
case "买入":
row["买方"] = (trade.OpponentRole == null || trade.OpponentRole == "乙方") ? "甲方" : "乙方";
break;
case "卖出":
row["买方"] = (trade.OpponentRole == null || trade.OpponentRole == "乙方") ? "乙方" : "甲方";
break;
default:
row["买方"] = "";
break;
}
row["交易日期"] = currentTrade?.TradeDate?.ToString("yyyy/MM/dd") ?? "";
row["开始日期"] = $"{currentTrade?.StartDate:yyyy/MM/dd}-{currentTrade?.ExerciseDate:yyyy/MM/dd}";
row["名义本金"] = currentTrade?.OriginalStockEqvNotional.OtcFormatMoney() ?? "0.00";
row["标的代码"] = currentTrade?.UnderlyingCode ?? "";
row["期权类型"] = currentTrade?.TradeType ?? "";
row["期初价格"] = currentTrade?.TradePrice.OtcFormatMoney() ?? "0.00";
row["结算日期"] = tradeCashItem.ValueDate.ToString("yyyy/MM/dd");
row["结算价格"] = tradeCashItem.FinalPrice.OtcFormatMoney();
row["结算模块"] = (tradeCashItem.UnwindPercentRate * currentTrade?.OriginalNotional * underlying?.CountRatio).OtcFormatMoney() ?? "0.00";
row["结算单价"] = settlementPrice;
var amountQrs = -tradeCashItem.Amount;
var amountQrs2 = -(tradeCashItem.Amount + (currentTrade?.TradePrice ?? 0) * (tradeCashItem.UnwindPercentRate ?? 0) * ((currentTrade?.BuySell == "卖出") ? 1 : -1));
row["结算期权费"] = amountQrs.OtcFormatMoney();
row["结算金额"] = amountQrs2.OtcFormatMoney();
table1.Add(row);
valueTimeList.Add(tradeCashItem.ValueDate);
}
}
// 合同编号由基类统一设置,此处不再重复生成
dic["table1"] = table1;
dic["合同日期"] = DateTime.Now.ToString("yyyy/MM/dd");
dic["了结日期1"] = valueTimeList.Min().ToString("yyyy/MM/dd");
dic["了结日期2"] = valueTimeList.Max().ToString("yyyy/MM/dd");
// 甲乙方信息
var ownerInfo = Context.GetOwner_info("默认");
dic["客户名称"] = client?.Name ?? "";
dic["公司名称"] = ownerInfo?.FullName ?? "";
if (trade.OpponentRole == null || trade.OpponentRole == "乙方")
{
dic["甲方"] = ownerInfo?.FullName ?? "";
dic["乙方"] = client?.Name ?? "";
}
else
{
dic["甲方"] = client?.Name ?? "";
dic["乙方"] = ownerInfo?.FullName ?? "";
}
}
/// <summary>
/// 获取模板文件路径
/// </summary>
protected override string GetTemplateFilePath()
{
var templatePath = Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\settlement_template\\unwind_01.docx");
return templatePath;
}
/// <summary>
/// 获取输出文件名
/// </summary>
protected override string GetOutputFileName(string contractNo, string contractIndex)
{
var client = Context.GetClient();
var trade = Context.Trade;
var tradeList = Context.Trades.Union(Context.SubTrades).ToList();
var ownerInfo = Context.GetOwner_info("默认") ?? new Owner_info();
var maxTradeDate = tradeList.Max(m => m.TradeDate);
var minTradeDate = tradeList.Min(m => m.TradeDate);
string date;
if (maxTradeDate == minTradeDate)
{
date = trade.TradeDate?.ToString("yyyyMMdd") ?? DateTime.Now.ToString("yyyyMMdd");
}
else
{
date = $"{minTradeDate:yyyyMMdd}-{maxTradeDate:yyyyMMdd}";
}
var fullName = ownerInfo?.FullName ?? "";
var rule = $"{fullName} {trade.TradeType}结算确认书 {date}";
Context.GetcontractIndex(rule, out var contractIndex2);
return $"{rule}-{contractIndex2}.{DocType.ToLower()}";
}
}
}
@@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<BaseOutputPath>..\build\GuoLian\</BaseOutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Framework\YLErp.Core\YLErp.Core.csproj" />
<ProjectReference Include="..\..\YLErpDAL\YLErpDAL.csproj" />
</ItemGroup>
<ItemGroup>
<!-- 收益互换交易确认书模板:4种组合(客户提供的原始文件名) -->
<None Update="App_Docs\contract_template\国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="App_Docs\contract_template\国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="App_Docs\contract_template\国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="App_Docs\contract_template\国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<!-- 结算单模板 -->
<None Update="App_Docs\settlement_template\settle_01.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<!-- 了结报告模板 -->
<None Update="App_Docs\settlement_template\unwind_01.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,124 @@
using YieldChain.Helpers;
using YLErp.BLL;
using YLErp.DBModels;
using YLErp.Modules;
namespace YLErp.CustomizedBizLogic
{
/// <summary>
/// 国联证券业务逻辑
/// </summary>
public class BizLogicGuoLian : BizLogicDefault
{
/// <summary>
/// 在交易确认前生成交易编号
/// 格式:GL-{yyyyMMdd}-{序号}
/// </summary>
public override string GenerateTradeNumberBeforeConfirm(TradeNumberReqModel reqModel, YLContext db)
{
if (reqModel is null)
{
throw new ArgumentNullException(nameof(reqModel));
}
// 对于对冲交易、场内交易等没有设置客户的
if (reqModel.clientId == 0)
{
return DateTime.Now.ToString("yyyyMMddHHmmssfff");
}
// 国联格式:GL-{yyyyMMdd}-{4位序号}
var leftPart = $"GL-{reqModel.trade.TradeDate:yyyyMMdd}-";
var fullSize = leftPart.Length + 4;
// 获取数据库该用户最大的交易编号记录
var tradeNumber = db.trade.Where(x =>
x.TradeNumber.StartsWith(leftPart) &&
x.TradeNumber.Length == fullSize &&
x.ValidState != ConsGlobal.InValid)
.OrderByDescending(x => x.TradeNumber)
.Select(n => n.TradeNumber)
.FirstOrDefault() ?? string.Empty;
if (reqModel.importTrades != null && reqModel.importTrades.Any())
{
// 获取导入交易中该客户最大的交易编号记录
var importTradeNumber = reqModel.importTrades
.Where(x => x.TradeNumber.StartsWith(leftPart) && x.TradeNumber.Length == fullSize)
.OrderByDescending(x => x.TradeNumber)
.Select(n => n.TradeNumber)
.FirstOrDefault() ?? string.Empty;
tradeNumber = tradeNumber.CompareTo(importTradeNumber) > 0 ? tradeNumber : importTradeNumber;
}
// 获取总的该用户最大的交易编号记录
int startNumber = string.IsNullOrWhiteSpace(tradeNumber)
? 1
: (int.Parse(tradeNumber.Substring(leftPart.Length)) + 1);
return GetTradeNumber(startNumber, leftPart, db);
}
/// <summary>
/// 获取可用的交易编号(循环实现,避免递归栈溢出)
/// </summary>
private string GetTradeNumber(int startNumber, string leftPart, YLContext db)
{
for (int newNumber = startNumber; newNumber < 10000; newNumber++)
{
var candidate = leftPart + newNumber.ToString("D4");
var childTradeNumberPart = candidate + "-";
// 检查是否存在以该编号开头的子交易
bool exists = db.trade.Any(x =>
EF.Functions.Like(x.TradeNumber, $"{childTradeNumberPart}%") &&
x.ValidState != ConsGlobal.InValid);
if (!exists)
{
return candidate;
}
}
throw new Exception("该客户交易编号已达到最大限制9999,生成交易编号失败");
}
/// <summary>
/// 获取交易确认书前缀
/// </summary>
public override string GetTradeContractPrefix(string tradeNumber)
{
// 国联证券确认书前缀
return "GL-";
}
/// <summary>
/// 生成客户编号
/// 格式:{年份后两位}{3位序号}{拼音首字母}
/// </summary>
public override void GenerateClientCode(Client clientObj, IQueryable<Client> clientDbQuery)
{
if (string.IsNullOrWhiteSpace(clientObj.Number))
{
var firstPY = PingYinHelper.GetFirstPinYin_Enhance(clientObj.Name);
var year = DateTime.Now.ToString("yy");
var result = "";
lock (_clientCodeLock)
{
var existingClientCodes = clientDbQuery
.Where(x => x.Number.StartsWith(year))
.Select(x => x.Number)
.ToList();
var number = existingClientCodes.Count();
do
{
number++;
result = year + number.ToString("000");
} while (existingClientCodes.Any(O => O.StartsWith(result)));
clientObj.Number = result + firstPY;
}
}
}
}
}
@@ -95,6 +95,8 @@ namespace YLErp.CustomizedBizLogic
return new BizLogicShanXiGuShou();
case CompanyEnum.:
return new BizLogicZheShang();
case CompanyEnum.:
return new BizLogicGuoLian();
case CompanyEnum.: //暂时:确认书模式 是国君的
default:
return new BizLogicGTJA();