Files
zszq-trs/YLErpDAL/Modules/SwapModule/SwapEventEmailService.cs
T

815 lines
43 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 BaseOUDAL;
using CsvHelper;
using MathNet.Numerics.Providers.LinearAlgebra;
using Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.Word;
using Newtonsoft.Json.Linq;
using NPOI.SS.Formula.Functions;
using Org.BouncyCastle.Ocsp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using YLErp.BLL;
using YLErp.Core.Helpers;
using YLErp.DBModels;
using YLErp.DBModels.Consts;
using YLErp.Helpers;
using YLErp.MailKit;
using YLErp.Model;
using YLErp.Models;
using YLErp.Modules.EodModule.QueryModule;
using YLErp.Modules.SwapModule.Margin;
using YLErp.Office.Converters;
using YLErp.Office.Helpers;
using static YLErp.ConsGlobal;
namespace YLErp.Modules.SwapModule
{
public class SwapEventEmailService : YLBaseService
{
private List<int> eventTypes = new List<int>() { (int)SwapFlowEventTypeEnum.开仓, (int)SwapFlowEventTypeEnum.平仓 };
private List<int> marginTypes = MarginModes.All.ToList();
private decimal wan = 10000m;
public SwapEventEmailService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
/// 查询资金提示列表
/// </summary>
public SearchListResult<SwapEventEmailResponse> SearchList(SwapEventEmailRequest req)
{
if (!req.Valuedate.HasValue)
{
throw new Exception("缺少日期参数");
}
var predicate = PredicateBuilder.Create<SwapEventEmail>(n => n.event_date == req.Valuedate);
var flowEventPredicate = PredicateBuilder.Create<swap_flow_event>(n => n.PositionType > 0 && eventTypes.Contains(n.EventType) && n.UnwindDate == req.Valuedate.Value && n.DataState == (int)SwapFlowDateStateEnum.完成);
var clientSpanPredicate = PredicateBuilder.Create<ClientSpan>(x => x.ValueDate == req.Valuedate.Value && x.WorstCastClientPayable < 0 && x.SpanType == ClientSpan.SpanType_Eod);
var valueDate = valuedateBLL.ValueDate;
if (req.ClientIds != null && req.ClientIds.Any())
{
flowEventPredicate = flowEventPredicate.And(d => req.ClientIds.Contains(d.ClientId ?? 0));
clientSpanPredicate = clientSpanPredicate.And(d => req.ClientIds.Contains(d.ClientId));
}
var emailquery = DbContext.swap_event_email.Where(predicate);
var dmaEmailQuery = emailquery.Where(x => x.event_id == 0);
var flowQuery = DbContext.swap_flow_event.Where(flowEventPredicate);
var clientSpanQuery = DbContext.client_span.Where(clientSpanPredicate);
var noDmaquery = from f in flowQuery
join t in DbContext.trade on f.SwapTradeId equals t.id
join se in DbContext.swap_event.Where(x => !x.Invalid) on f.EventId equals se.id into setemp
from se in setemp.DefaultIfEmpty()
join em in emailquery on f.id equals em.event_id into emTmp
from em in emTmp.DefaultIfEmpty()
where t.ValidState == "Valid" && t.TradeType == "收益互换"
select new SwapEventEmailResponse
{
event_id = f.id,
client_name = t.ClientName,
client_id = t.ClientId,
event_type = f.EventType,
send_email = em == null ? false : em.send_email,
send_remark = em == null ? "" : em.send_remark,
transpond = em == null ? 0 : em.transpond,
trade_number = t.TradeNumber,
single = em == null ? null : em.single,
};
var dmaquery = from f in clientSpanQuery
join em in dmaEmailQuery on f.ClientId equals em.client_id into emTmp
from em in emTmp.DefaultIfEmpty()
select new SwapEventEmailResponse
{
event_id = 0,
client_id = f.ClientId,
event_type = 3,
send_email = em == null ? false : em.send_email,
send_remark = em == null ? "" : em.send_remark,
transpond = em == null ? 0 : em.transpond,
trade_number = ""
};
var noDmaResults = noDmaquery.ToList();
var dmaResults = dmaquery.ToList();
var allReulst = dmaResults.Concat(noDmaResults);
foreach (var item in allReulst)
{
if (item.event_id > 0)
{
if (!item.single.HasValue)
{
var singleEvent = allReulst.Count(x => x.client_id == item.client_id && x.single == null) == 1;
if (singleEvent)
{
item.single = true;
}
}
}
item.transpondStr = item.transpond == 0 ? "未转发" : "已转发";
}
SearchListResult<SwapEventEmailResponse> searchList = new SearchListResult<SwapEventEmailResponse>();
var retListResult = allReulst.OrderBy(o => o.client_name).ThenBy(t => t.trade_number).AsQueryable().ToSearchList(req);
foreach (var item in retListResult.rows)
{
var client = DataCacheProvider.GetClientDataSource().GetData(item.client_id);
item.client_type = client?.SwapTradeTypeStr;
item.client_name = client?.Name;
item.event_date = req.Valuedate.Value;
}
return retListResult;
}
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="request"></param>
/// <exception cref="Exception"></exception>
public void SendEmail(SendSwapEventEmailRequest request)
{
if (request.EventEmailEmails == null || !request.EventEmailEmails.Any())
{
throw new Exception($"没有需要发邮件的数据");
}
var dmaEmailDatas = request.EventEmailEmails.Where(x => x.event_id == 0).ToList();
var noDmaEmailDatas = request.EventEmailEmails.Where(x => x.event_id > 0).ToList();
var clientDuitys = GetClientDuitys();
DealNoDmaEmail(noDmaEmailDatas, request.ValueDate, clientDuitys);
DealDmaEmail(dmaEmailDatas, request.ValueDate, clientDuitys);
}
/// <summary>
/// 处理非dma数据邮件
/// </summary>
/// <param name="EventEmailEmails"></param>
/// <param name="valueDate"></param>
private void DealNoDmaEmail(List<EventEmail> EventEmailEmails, DateTime valueDate, List<ClientDuty> clientDuitys)
{
if (!EventEmailEmails.Any())
{
return;
}
var flowEventIds = EventEmailEmails.Select(s => s.event_id).ToList();
var flowEvents = DbContext.swap_flow_event.Where(x => flowEventIds.Contains(x.id));
var clientIds = flowEvents.Select(x => x.ClientId).ToList();
var flowQuery = from f in DbContext.swap_flow_event
join t in DbContext.trade on f.SwapTradeId equals t.id
join se in DbContext.swap_event.Where(x => !x.Invalid) on f.EventId equals se.id into setemp
from se in setemp.DefaultIfEmpty()
join em in DbContext.swap_event_email on f.id equals em.event_id into emTmp
from em in emTmp.DefaultIfEmpty()
where t.ValidState == "Valid" && t.TradeType=="收益互换" && (em == null || em.send_email == false)
&& clientIds.Contains(f.ClientId)
&& f.UnwindDate == valueDate && f.PayDirection > 0 && eventTypes.Contains(f.EventType) && f.DataState == (int)SwapFlowDateStateEnum.完成
select f;
var flowEventList = flowQuery.ToList();
// 定位要点:发送范围按【客户+日期】整组扩张——用户勾选 A 事件,同客户同日的 B/C 事件会被一并拉入,
// 后续校验报错常是被拉入的事件缺确认书(用户以为"刚生成了还报错")。此日志把勾选与扩张结果对齐打出来。
LogFactory.GetLogger("SwapEventEmail").Info(
$"资金提示邮件-发送范围: 用户勾选事件[{string.Join(",", EventEmailEmails.Select(s => s.event_id))}] " +
$"客户[{string.Join(",", clientIds.Distinct())}] 日期[{valueDate:yyyy-MM-dd}] " +
$"扩张后实际处理事件[{string.Join(",", flowEventList.Select(f => $"{f.id}:{f.SwapTradeNo}:" + (f.EventType == (int)SwapFlowEventTypeEnum.开仓 ? "开仓" : f.EventType == (int)SwapFlowEventTypeEnum.平仓 ? "平仓" : "T" + f.EventType)))}]");
var docs = new List<SwapTradeContractDto>();
var openFlowEventList = flowEventList.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓).ToList();
var closeFlowEventList = flowEventList.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.平仓).ToList();
docs.AddRange(CheckConfirmDoc(openFlowEventList));
docs.AddRange(CheckSettlementDoc(closeFlowEventList));
SendNoDmaEmail(docs, valueDate, clientDuitys);
}
/// <summary>
/// 校验交易确认书是否存在
/// </summary>
/// <param name="flowEvents"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private List<SwapTradeContractDto> CheckConfirmDoc(List<swap_flow_event> flowEvents)
{
var tradeIds = flowEvents.Select(s => s.SwapTradeId).ToList();
var query = from doc in DbContext.trade_contract_document
join r in DbContext.trade_contract_r
on doc.Code equals r.ContractCode
where doc.Type == r.Type && r.IsValid && r.Type == ContractTypeEnum.Trade && tradeIds.Contains(r.TradeId)
select new SwapTradeContractDto
{
Paths = doc.Paths,
Type = r.Type,
TradeId = r.TradeId,
SwapFlowEventId = r.SwapFlowEventId,
FileName = doc.FileName,
TradeNumber = r.TradeNumber,
};
var docLsit = query.ToList();
List<SwapTradeContractDto> list = new List<SwapTradeContractDto>();
List<string> tradeNumbers = new List<string>();
foreach (var item in flowEvents)
{
SwapTradeContractDto doc = docLsit.FirstOrDefault(f => f.TradeId == item.SwapTradeId);
if (doc == null)
{
tradeNumbers.Add(item.SwapTradeNo);
}
else
{
doc.ClientId = item.ClientId ?? 0;
doc.SwapFlowEventType = item.EventType;
var client = DataCacheProvider.GetClientDataSource().GetData(doc.ClientId);
doc.ClientName = client.Name;
doc.SwapFlowEventId = item.id;
list.Add(doc);
}
}
if (tradeNumbers.Any())
{
// 定位要点:报错只带交易编号不够定位。补打每笔缺失交易的 trade_contract_r 实际行状态
//(无行=从未生成 / IsValid=false=被重生成作废 / document 行缺失=确认书文档被清理),命中哪种一眼可辨。
var missTradeIds = flowEvents.Where(w => tradeNumbers.Contains(w.SwapTradeNo)).Select(s => s.SwapTradeId).Distinct().ToList();
var missRows = DbContext.trade_contract_r.Where(x => missTradeIds.Contains(x.TradeId)).ToList();
var missCodes = missRows.Select(r => r.ContractCode).Distinct().ToList();
var docCodes = DbContext.trade_contract_document.Where(d => missCodes.Contains(d.Code)).Select(d => d.Code).Distinct().ToList();
var detail = string.Join("", missTradeIds.Select(tid =>
{
var tradeNo = flowEvents.First(f => f.SwapTradeId == tid).SwapTradeNo;
var rows = missRows.Where(x => x.TradeId == tid).ToList();
if (!rows.Any()) return $"{tradeNo}: trade_contract_r 无任何行(从未生成交易确认书)";
return $"{tradeNo}: " + string.Join(",", rows.Select(r =>
$"[Type={r.Type},IsValid={r.IsValid},Code={r.ContractCode},doc存在={(docCodes.Contains(r.ContractCode) ? "" : "")}]"));
}));
LogFactory.GetLogger("SwapEventEmail").Error(
$"交易确认书校验失败: 交易编号[{string.Join(",", tradeNumbers.Distinct())}]找不到有效交易确认书; 库内明细: {detail}");
throw new Exception($"交易编号为{string.Join(",", tradeNumbers.Distinct())}找不到有效的交易确认书附件,请检查或生成后再发送邮件");
}
return list;
}
/// <summary>
/// 校验结算确认书
/// </summary>
/// <param name="flowEvents"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private List<SwapTradeContractDto> CheckSettlementDoc(List<swap_flow_event> flowEvents)
{
var flowEventIds = flowEvents.Select(s => s.id).ToList();
var query = from doc in DbContext.trade_contract_document
join r in DbContext.trade_contract_r
on doc.Code equals r.ContractCode
where doc.Type == r.Type && r.IsValid && r.Type == ContractTypeEnum.Clearing && flowEventIds.Contains(r.SwapFlowEventId ?? 0)
select new SwapTradeContractDto
{
Paths = doc.Paths,
Type = r.Type,
TradeId = r.TradeId,
SwapFlowEventId = r.SwapFlowEventId,
FileName = doc.FileName,
TradeNumber = r.TradeNumber,
};
var docLsit = query.ToList();
List<SwapTradeContractDto> list = new List<SwapTradeContractDto>();
List<string> tradeNumbers = new List<string>();
foreach (var item in flowEvents)
{
SwapTradeContractDto doc = docLsit.FirstOrDefault(f => f.SwapFlowEventId == item.id);
if (doc == null)
{
tradeNumbers.Add(item.SwapTradeNo);
}
else
{
doc.ClientId = item.ClientId ?? 0;
doc.SwapFlowEventType = item.EventType;
var client = DataCacheProvider.GetClientDataSource().GetData(doc.ClientId);
doc.ClientName = client.Name;
doc.SwapFlowEventId = item.id;
list.Add(doc);
}
}
if (tradeNumbers.Any())
{
// 定位要点:结算确认书按 SwapFlowEventId 精确匹配。补打每笔缺失平仓事件的合同行状态——
// 常见原因是生成时的扩张查询覆盖了本事件但用户实际生成的是另一批,或重生成后 IsValid 被作废。
var missEventIds = flowEvents.Where(w => tradeNumbers.Contains(w.SwapTradeNo)).Select(s => s.id).Distinct().ToList();
var missRows = DbContext.trade_contract_r.Where(x => missEventIds.Contains(x.SwapFlowEventId ?? 0)).ToList();
var missTradeIds = flowEvents.Where(w => tradeNumbers.Contains(w.SwapTradeNo)).Select(s => s.SwapTradeId).Distinct().ToList();
var tradeTypeRows = DbContext.trade_contract_r.Where(x => missTradeIds.Contains(x.TradeId) && x.Type == ContractTypeEnum.Clearing).ToList();
var detail = string.Join("", flowEvents.Where(w => tradeNumbers.Contains(w.SwapTradeNo)).GroupBy(g => g.SwapTradeNo).Select(g =>
{
var rows = missRows.Where(x => x.SwapFlowEventId == g.First().id).ToList();
var byTrade = tradeTypeRows.Where(x => x.TradeId == g.First().SwapTradeId).ToList();
return $"{g.Key}(事件{g.First().id}): " + (rows.Any()
? string.Join(",", rows.Select(r => $"[Type={r.Type},IsValid={r.IsValid},Code={r.ContractCode}]"))
: $"按事件无行; 按交易的Clearing行=" + (byTrade.Any()
? string.Join(",", byTrade.Select(r => $"[SwapFlowEventId={r.SwapFlowEventId},IsValid={r.IsValid},Code={r.ContractCode}]"))
: "无"));
}));
LogFactory.GetLogger("SwapEventEmail").Error(
$"结算确认书校验失败: 交易编号[{string.Join(",", tradeNumbers.Distinct())}]找不到有效结算确认书(SwapFlowEventId匹配); 库内明细: {detail}");
throw new Exception($"交易编号为{string.Join(",", tradeNumbers.Distinct())}找不到有效的结算确认书附件,请检查或生成后再发送邮件");
}
return list;
}
/// <summary>
/// 发送非dma数据邮件
/// </summary>
/// <param name="swapTradeContracts"></param>
/// <param name="valueDate"></param>
private void SendNoDmaEmail(List<SwapTradeContractDto> swapTradeContracts, DateTime valueDate, List<ClientDuty> clientDuitys)
{
var flowEventContractGroup = swapTradeContracts.Where(x => x.SwapFlowEventId > 0).GroupBy(g => g.ClientId);
foreach (var item in flowEventContractGroup)
{
var list = item.ToList();
if (list.Count == 1)//单开单平
{
SendNodmaEmail(list, valueDate, clientDuitys);
}
else //轧差支付
{
SendNodmaEmailComplex(list, valueDate, clientDuitys);
}
}
}
/// <summary>
/// 发送单开邮件
/// </summary>
/// <param name="swapTradeContract"></param>
/// <param name="swapTradeContracts"></param>
/// <param name="valueDate"></param>
/// <param name="clientDuitys"></param>
private void SendNodmaEmail(List<SwapTradeContractDto> swapTradeContracts, DateTime valueDate, List<ClientDuty> clientDuitys)
{
SwapTradeContractDto swapTradeContract = swapTradeContracts.First();
var eventEmail = SaveEventEmail(swapTradeContract.SwapFlowEventId, swapTradeContract.ClientId, valueDate, true);
var emails = GetEmailTo(clientDuitys, swapTradeContracts);
List<string> filePaths = new List<string>();
string title = $"【{valueDate.ToString("yyyy.MM.dd")}结算说明】{swapTradeContract.ClientName}";
string content = "";
if (swapTradeContract.SwapFlowEventType == (int)SwapFlowEventTypeEnum.开仓)
{
content = GetSingleEventOpenContent(swapTradeContract, valueDate);
}
else
{
content = GetSingleEventCloseContent(swapTradeContract, valueDate);
}
var file = GetFileName(swapTradeContract.Paths, ".xlsx");
filePaths.Add(file);
var emailMsgId = SendEmailApi(title, emails, content, true, filePaths);
eventEmail.email_msg_id = emailMsgId;
eventEmail.send_email = true;
DbContext.SaveChanges();
}
/// <summary>
/// 发送轧差支付邮件
/// </summary>
/// <param name="swapTradeContracts"></param>
/// <param name="valueDate"></param>
/// <param name="clientDuitys"></param>
/// <exception cref="Exception"></exception>
private void SendNodmaEmailComplex(List<SwapTradeContractDto> swapTradeContracts, DateTime valueDate, List<ClientDuty> clientDuitys)
{
List<SwapEventEmail> swapEventEmails = new List<SwapEventEmail>();
var emails = GetEmailTo(clientDuitys, swapTradeContracts);
List<string> filePaths = new List<string>();
string title = $"【{valueDate.ToString("yyyy.MM.dd")}净额结算说明】{swapTradeContracts.First().ClientName}";
string content = "";
foreach (var item in swapTradeContracts)
{
var file = GetFileName(item.Paths, ".xlsx");
if (!filePaths.Contains(file))
{
filePaths.Add(file);
}
var eventEmail = SaveEventEmail(item.SwapFlowEventId, item.ClientId, valueDate, false);
swapEventEmails.Add(eventEmail);
}
Dictionary<string, JToken> dic = new Dictionary<string, JToken>();
var clientName = swapTradeContracts.First().ClientName;
dic["clientName"] = clientName;
dic["settlementDate"] = valueDate.ToString("yyyy.MM.dd");
var openEvents = swapTradeContracts.Where(x => x.SwapFlowEventType == (int)SwapFlowEventTypeEnum.开仓).ToList();
var closeEvents = swapTradeContracts.Where(x => x.SwapFlowEventType == (int)SwapFlowEventTypeEnum.平仓).ToList();
var openAmount = DealOpenEventData(openEvents, dic);
var closeAmount = DealCloseEventData(closeEvents, dic);
var totalAmount = openAmount + closeAmount;
dic["payAmount"] = Math.Abs(totalAmount).ToString("0.######");
dic["payDirect"] = totalAmount >= 0 ? "我方" : "客户";
content = GetComplexEventContent(dic, valueDate, clientName);
var swapEventEmailIds = swapEventEmails.Select(s => s.id).ToList();
var emailMsgId = SendEmailApi(title, emails, content, true, filePaths);
swapEventEmails.ForEach(x =>
{
x.send_email = true;
x.email_msg_id = emailMsgId;
});
DbContext.SaveChanges();
}
/// <summary>
/// 处理轧差支付 开仓数据
/// </summary>
/// <param name="swapTradeContracts"></param>
/// <param name="dic"></param>
private decimal DealOpenEventData(List<SwapTradeContractDto> swapTradeContracts, Dictionary<string, JToken> dic)
{
var tradeIds = swapTradeContracts.Select(s => s.TradeId).Distinct().ToList();
var trades = DbContext.trade.Where(x => tradeIds.Contains(x.id)).ToList();
var swapPositions = DbContext.swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.IsInitial && !x.Invalid).ToList();
var posiList = swapPositions.Where(x => x.PosiDirection > 0);
var stockNotional = posiList.Sum(s => s.PosiNotionalValue) / wan;
var notionalStock = stockNotional;
if (stockNotional >= wan)
{
notionalStock /= wan;
}
dic["openTotal"] = trades.Count;
dic["openNotionalStock"] = (notionalStock).ToString("0.######") + (stockNotional >= wan ? "亿" : "万");
string tradeNumbers = string.Empty;
decimal marginAmountTotal = 0;
foreach (var t in trades)
{
var marginAmount = swapPositions.Where(x => marginTypes.Contains(x.InterestMode) && x.PosiStartDate == t.StartDate && x.SwapTradeId == t.id).Sum(s => s.InterestPrincipalFix * (s.InterestDirection == 1 ? -1m : 1m));
marginAmountTotal += marginAmount;
tradeNumbers += $"<p>合约编号 {t.TradeNumber}</p>";
}
marginAmountTotal /= wan;
dic["openTradeNumber"] = tradeNumbers;
dic["openPaySide"] = marginAmountTotal > 0 ? "我方" : "";
dic["openMarginAmount"] = Math.Abs(marginAmountTotal).ToString("0.######");
return marginAmountTotal;
}
/// <summary>
/// 处理轧差支付 平仓数据
/// </summary>
/// <param name="swapTradeContracts"></param>
/// <param name="dic"></param>
private decimal DealCloseEventData(List<SwapTradeContractDto> swapTradeContracts, Dictionary<string, JToken> dic)
{
var flowEventIds = swapTradeContracts.Select(s => s.SwapFlowEventId).ToList();
var swapFlowEvents = DbContext.swap_flow_event.Where(x => flowEventIds.Contains(x.id)).ToList();
var eventIds = swapFlowEvents.Select(s => s.EventId).Distinct().ToList();
var swapEvents = DbContext.swap_event.Where(x => eventIds.Contains(x.id)).ToList();
decimal stockNotionalTotal = 0;
decimal swapCloseAmountTotal = 0;
string tradeNumbers = string.Empty;
foreach (var swapEvent in swapEvents)
{
var unwindData = JsonHelper.Deserialize<UnwindData>(swapEvent.EventData);
var flowEvent = swapFlowEvents.Where(x => x.EventId == swapEvent.id).First();
var stockNotional = unwindData.CloseNotionalValue / wan;
var marginAmount = unwindData.SwapMarginAmount / wan;
stockNotionalTotal += stockNotional;
swapCloseAmountTotal += -(unwindData.SwapCloseAmount + unwindData.SwapMarginRebatePnl - unwindData.SwapMarginAmount) / wan;
tradeNumbers += $"<p>合约编号 {flowEvent.SwapTradeNo}</p>";
}
var notionalStock = stockNotionalTotal;
if (stockNotionalTotal >= wan)
{
notionalStock /= wan;
}
dic["closeTradeNumber"] = tradeNumbers;
dic["closeNotionalStock"] = (notionalStock).ToString("0.######") + (stockNotionalTotal >= wan ? "亿" : "万");
dic["closePaySide"] = swapCloseAmountTotal > 0 ? "我方" : "";
dic["closeMarginAmount"] = Math.Abs(swapCloseAmountTotal).ToString("0.######");
return swapCloseAmountTotal;
}
/// <summary>
/// 获取轧差支付邮件内容
/// </summary>
/// <param name="dic"></param>
/// <param name="valueDate"></param>
/// <param name="clientName"></param>
/// <returns></returns>
private string GetComplexEventContent(Dictionary<string, JToken> dic, DateTime valueDate, string clientName)
{
var sourcePath = OtcAppContext.MapPath("~/App_Docs/导出模板");
string templatePath = Path.Combine(sourcePath, "资金提示-轧差支付模板.docx");
var tempFolder = OtcAppContext.MapPath("~/App_Docs/Temp/结算报告");
tempFolder = MosPathHelper.Combine(tempFolder, "");
var targetPath = Path.Combine(tempFolder, valueDate.ToString("yyyyMMdd"));
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
var fileName = $"资金提示-轧差支付{valueDate:yyyyMMdd}_{clientName}";
var targetFileName = Path.Combine(targetPath, $"{fileName}.docx");
return GetEmailContent(templatePath, dic, targetFileName);
}
/// <summary>
/// 保存记录
/// </summary>
/// <param name="eventId"></param>
/// <param name="tradeId"></param>
/// <param name="valueDate"></param>
/// <param name="single"></param>
/// <returns></returns>
private SwapEventEmail SaveEventEmail(long? eventId, int clientId, DateTime valueDate, bool single)
{
var swapEventEmail = DbContext.swap_event_email.FirstOrDefault(x => x.event_id == eventId && x.client_id == clientId && x.event_date == valueDate);
if (swapEventEmail == null)
{
swapEventEmail = new SwapEventEmail()
{
event_date = valueDate,
event_id = eventId,
client_id = clientId,
single = single,
send_remark = single ? "结算提示-单开单平" : "结算提示-轧差支付"
};
swapEventEmail.SetCreator(UserId, UserName);
swapEventEmail.SetOpt(UserId, UserName);
DbContext.swap_event_email.Add(swapEventEmail);
DbContext.SaveChanges();
}
return swapEventEmail;
}
/// <summary>
/// 保存记录
/// </summary>
/// <param name="eventId"></param>
/// <param name="tradeId"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
private SwapEventEmail SaveEventEmail(long? eventId, int clientId, DateTime valueDate)
{
var swapEventEmail = DbContext.swap_event_email.FirstOrDefault(x => x.event_id == eventId && x.client_id == clientId && x.event_date == valueDate);
if (swapEventEmail == null)
{
swapEventEmail = new SwapEventEmail()
{
event_date = valueDate,
event_id = eventId,
client_id = clientId,
single = false,
send_remark = "追缴预付金Email"
};
swapEventEmail.SetCreator(UserId, UserName);
swapEventEmail.SetOpt(UserId, UserName);
DbContext.swap_event_email.Add(swapEventEmail);
DbContext.SaveChanges();
}
return swapEventEmail;
}
/// <summary>
/// 处理dma数据邮件
/// </summary>
/// <param name="EventEmailEmails"></param>
/// <param name="valueDate"></param>
private void DealDmaEmail(List<EventEmail> EventEmailEmails, DateTime valueDate, List<ClientDuty> clientDuitys)
{
var clientIds = EventEmailEmails.Select(x => x.client_id).ToList();
var clientEmailDic = GetEmailTo(clientDuitys, clientIds);
var clientSpans = DbContext.client_span.Where(x => clientIds.Contains(x.ClientId) && x.ValueDate == valueDate && x.WorstCastClientPayable < 0 && x.SpanType == ClientSpan.SpanType_Eod).ToList();
foreach (var clientSpan in clientSpans)
{
var client = DataCacheProvider.GetClientDataSource().GetData(clientSpan.ClientId);
if (client == null)
{
continue;
}
var clientNumber = client.Name;
var eventEmail = SaveEventEmail(0, client.id, valueDate);
string title = $"【{valueDate.ToString("yyyy.MM.dd")}追缴预付金通知】{client.Name}";
var content = GetDmaContent(client.Name, valueDate, Math.Abs(clientSpan.WorstCastClientPayable ?? 0));
content += $"<p><div id=\"swapeventemail\" style=\"display:none;\">{eventEmail.id}</div><p>";
var emailTo = clientEmailDic[client.id];
var emailMsgId = SendEmailApi(title, emailTo, content, true, new List<string>());
eventEmail.send_email = true;
eventEmail.email_msg_id = emailMsgId;
DbContext.SaveChanges();
}
}
/// <summary>
/// 获取客户联系人邮箱
/// </summary>
/// <returns></returns>
private List<ClientDuty> GetClientDuitys()
{
using var db = new ClientDBContext();
var clientContacts = db.clientduty.Where(x => x.ApprovalOrder < 1
&& (x.DeadLine == null || x.DeadLine > DateTime.Now)
&& x.IsReceiveEmail == 1 && x.Email != null
&& x.ContactTypeId.Contains("4"))
.ToList();
return clientContacts;
}
/// <summary>
/// 获取收件人联系邮箱
/// </summary>
/// <param name="clientDuitys"></param>
/// <param name="swapTradeContracts"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private string GetEmailTo(List<ClientDuty> clientDuitys, List<SwapTradeContractDto> swapTradeContracts)
{
var clientIds = swapTradeContracts.Select(s => s.ClientId).Distinct().ToList();
var clientDuityQuery = clientDuitys.Where(x => clientIds.Contains(x.ClientId ?? 0));
List<string> clientNumbers = new List<string>();
foreach (var swapTradeContract in swapTradeContracts.GroupBy(g => g.ClientId))
{
var clientNumber = swapTradeContract.ToList().First().ClientName;
if (!clientDuityQuery.Any(x => x.ClientId == swapTradeContract.Key))
{
clientNumbers.Add(clientNumber);
}
}
if (clientNumbers.Any())
{
throw new Exception($"客户{string.Join(",", clientNumbers)}未维护职责类型为联系人且接收相关邮件选项为是");
}
var emails = clientDuityQuery.Select(s => s.Email).Distinct().ToList();
return string.Join(";", emails);
}
/// <summary>
/// 获取收件人联系邮箱
/// </summary>
/// <param name="clientDuitys"></param>
/// <param name="trades"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private Dictionary<int, string> GetEmailTo(List<ClientDuty> clientDuitys, List<int> clientIds)
{
var clientDuityQuery = clientDuitys.Where(x => clientIds.Contains(x.ClientId ?? 0));
List<string> clientNumbers = new List<string>();
Dictionary<int, string> dic = new Dictionary<int, string>();
foreach (var t in clientIds)
{
var client = DataCacheProvider.GetClientDataSource().GetData(t);
var clientNumber = client?.Name;
if (!clientDuityQuery.Any(x => x.ClientId == t))
{
clientNumbers.Add(clientNumber);
}
var emails = clientDuityQuery.Where(x => x.ClientId == t).Select(s => s.Email).Distinct().ToList();
dic.Add(t, string.Join(";", emails));
}
if (clientNumbers.Any())
{
throw new Exception($"客户{string.Join(",", clientNumbers)}未维护职责类型为联系人且接收相关邮件选项为是");
}
return dic;
}
/// <summary>
/// 获取单开邮件内容
/// </summary>
/// <param name="swapTradeContract"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
private string GetSingleEventOpenContent(SwapTradeContractDto swapTradeContract, DateTime valueDate)
{
var trade = DbContext.trade.Find(swapTradeContract.TradeId);
var swapPositions = DbContext.swap_position.Where(x => x.SwapTradeId == swapTradeContract.TradeId && x.IsInitial && !x.Invalid);
var marginAmount = swapPositions.Where(x => marginTypes.Contains(x.InterestMode) && x.PosiStartDate == trade.StartDate).Sum(s => s.InterestPrincipalFix * (s.InterestDirection == 1 ? -1m : 1m));
var posi = swapPositions.FirstOrDefault(x => x.PosiDirection > 0);
Dictionary<string, JToken> dic = new Dictionary<string, JToken>();
var stockNotional = posi.PosiNotionalValue / wan;
marginAmount /= wan;
var notionalStock = stockNotional;
if (stockNotional >= wan)
{
notionalStock /= wan;
}
dic["clientName"] = swapTradeContract.ClientName;
dic["settlementDate"] = valueDate.ToString("yyyy.MM.dd");
dic["tradeNumber"] = swapTradeContract.TradeNumber;
dic["notionalStock"] = notionalStock.ToString("0.######") + (stockNotional >= wan ? "亿" : "万");
dic["paySide"] = marginAmount >= 0 ? "我方" : "";
dic["marginAmount"] = Math.Abs(marginAmount).ToString("0.######");
dic["payAmount"] = Math.Abs(marginAmount).ToString("0.######");
dic["payDirect"] = marginAmount >= 0 ? "我方" : "客户";
var sourcePath = OtcAppContext.MapPath("~/App_Docs/导出模板");
string templatePath = Path.Combine(sourcePath, "资金提示-单开模板.docx");
var tempFolder = OtcAppContext.MapPath("~/App_Docs/Temp/结算报告");
tempFolder = MosPathHelper.Combine(tempFolder, "");
var targetPath = Path.Combine(tempFolder, valueDate.ToString("yyyyMMdd"));
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
var clientName = swapTradeContract.ClientName;
var fileName = $"资金提示-单开{valueDate:yyyyMMdd}_{clientName}";
var targetFileName = Path.Combine(targetPath, $"{fileName}.docx");
return GetEmailContent(templatePath, dic, targetFileName);
}
/// <summary>
/// 获取单平邮件内容
/// </summary>
/// <param name="swapTradeContract"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
private string GetSingleEventCloseContent(SwapTradeContractDto swapTradeContract, DateTime valueDate)
{
var swapFlowEvent = DbContext.swap_flow_event.Find(swapTradeContract.SwapFlowEventId);
var swapEvent = DbContext.swap_event.Find(swapFlowEvent.EventId);
var unwindData = JsonHelper.Deserialize<UnwindData>(swapEvent.EventData);
Dictionary<string, JToken> dic = new Dictionary<string, JToken>();
var stockNotional = unwindData.CloseNotionalValue / wan;
var payAmount = -(unwindData.SwapCloseAmount + unwindData.SwapMarginRebatePnl - unwindData.SwapMarginAmount) / wan;
var notionalStock = stockNotional;
if (stockNotional >= wan)
{
notionalStock /= wan;
}
dic["clientName"] = swapTradeContract.ClientName;
dic["settlementDate"] = valueDate.ToString("yyyy.MM.dd");
dic["tradeNumber"] = swapTradeContract.TradeNumber;
dic["notionalStock"] = notionalStock.ToString("0.######") + (stockNotional >= wan ? "亿" : "万");
dic["paySide"] = payAmount > 0 ? "我方" : "";
dic["marginAmount"] = Math.Abs(payAmount).ToString("0.######"); ;
dic["payAmount"] = Math.Abs(payAmount).ToString("0.######");
dic["payDirect"] = payAmount > 0 ? "我方" : "客户";
var sourcePath = OtcAppContext.MapPath("~/App_Docs/导出模板");
string templatePath = Path.Combine(sourcePath, "资金提示-单平模板.docx");
var tempFolder = OtcAppContext.MapPath("~/App_Docs/Temp/结算报告");
tempFolder = MosPathHelper.Combine(tempFolder, "");
var targetPath = Path.Combine(tempFolder, valueDate.ToString("yyyyMMdd"));
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
var clientName = swapTradeContract.ClientName;
var fileName = $"资金提示-单平{valueDate:yyyyMMdd}_{clientName}";
var targetFileName = Path.Combine(targetPath, $"{fileName}.docx");
return GetEmailContent(templatePath, dic, targetFileName);
}
/// <summary>
/// 获取dma邮件数据
/// </summary>
/// <param name="clientName"></param>
/// <param name="valueDate"></param>
/// <param name="marginAmount"></param>
/// <returns></returns>
private string GetDmaContent(string clientName, DateTime valueDate, double marginAmount)
{
Dictionary<string, JToken> dic = new Dictionary<string, JToken>();
dic["clientName"] = clientName;
dic["settlementDate"] = valueDate.ToString("yyyy.MM.dd");
dic["marginAmount"] = marginAmount;
var sourcePath = OtcAppContext.MapPath("~/App_Docs/导出模板");
string templatePath = Path.Combine(sourcePath, "资金提示-追加预付金模板.docx");
var tempFolder = OtcAppContext.MapPath("~/App_Docs/Temp/结算报告");
tempFolder = MosPathHelper.Combine(tempFolder, "");
var targetPath = Path.Combine(tempFolder, valueDate.ToString("yyyyMMdd"));
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
var fileName = $"资金提示-追加预付金{valueDate:yyyyMMdd}_{clientName}";
var targetFileName = Path.Combine(targetPath, $"{fileName}.docx");
return GetEmailContent(templatePath, dic, targetFileName);
}
private string GetEmailContent(string templatePath, Dictionary<string, JToken> modelDic, string outputFilePath)
{
var varDic = new JsonVarDic(modelDic);
OfficeFileConverter.ConvertByUsingDocTemplate(templatePath, outputFilePath, varDic, false);
string content = DocHelper.GetContent(outputFilePath);
File.Delete(outputFilePath);
return content;
}
/// <summary>
/// 发送单个邮件内容
/// </summary>
/// <param name="subject"></param>
/// <param name="mailTo"></param>
/// <param name="body"></param>
/// <param name="isBodyHtml"></param>
/// <param name="filesToAttach"></param>
private string SendEmailApi(string subject, string mailTo, string body, bool isBodyHtml, List<string> filesToAttach)
{
return EmailHelper.SendMail(mailTo, subject, body, isBodyHtml, filesToAttach);
}
private string GetFileName(string baseName, string sufferFix)
{
var fName = baseName;
if (fName.EndsWith(sufferFix))
{
fName = Path.ChangeExtension(fName, sufferFix);
}
if (!string.IsNullOrWhiteSpace(fName) && (fName[0] != '/' || fName[0] != '\\'))
{
fName = fName.Insert(0, "/");
}
fName = OtcAppContext.MapPath(fName);
if (!System.IO.File.Exists(fName))
{ fName = Path.ChangeExtension(fName, "docx"); }//解决数据库中存的后缀名是doc但实际文件是docx的问题;
if (System.IO.File.Exists(fName))
{ return fName; }
else
{
// 定位要点:物理文件缺失历史上静默返回 null(附件列表混入 null,发送结果不可预期),补 warn 便于发现"库里行在、盘上文件丢"。
LogFactory.GetLogger("SwapEventEmail").Error($"邮件附件物理文件缺失: trade_contract_document.Paths={baseName} 映射后={fName} 不存在");
return null;
}
}
}
}