using System.Globalization; using Microsoft.EntityFrameworkCore; using YLErp.BLL; using YLErp.Modules.AppModule; namespace YLErp.Modules.ReportModule { /// /// 为结算确认书邮件准备交易编号和平仓总额模板变量。 /// public static class SettlementMailTemplateHelper { /// /// 根据当前客户的确认书反查对应交易和平仓记录。 /// 文档编号可能在不同文档类型中重复,因此必须同时匹配编号和类型。 /// public static List GetSettlementRows(YLContext dbContext, IEnumerable documents, int clientId) { var documentKeys = documents .Where(document => !string.IsNullOrWhiteSpace(document.Code)) .Select(document => BuildDocumentKey(document.Code, document.Type)) .Distinct() .ToHashSet(); if (documentKeys.Count == 0) { return new List(); } var contractCodes = documents.Select(document => document.Code).Distinct().ToList(); var rows = (from relation in dbContext.trade_contract_r join tradeCash in dbContext.trade_cash on relation.TradeCashId equals tradeCash.id join tradeData in dbContext.trade on tradeCash.TradeId equals tradeData.id where contractCodes.Contains(relation.ContractCode) && relation.IsValid && !tradeCash.IsDeleted && tradeData.ClientId == clientId select new { relation.ContractCode, relation.Type, Trade = tradeData, TradeCash = tradeCash }).AsNoTracking().ToList(); return rows // 数据库先按编号缩小范围,再在内存中用“编号+类型”精确匹配附件。 .Where(row => documentKeys.Contains(BuildDocumentKey(row.ContractCode, row.Type))) .Select(row => new Datas { trade = row.Trade, tradecash = row.TradeCash }).ToList(); } /// /// 将批量加载的数据限制到当前结算日,避免跨日发送时变量汇总串日。 /// public static List ForValueDate(IEnumerable rows, DateTime valueDate) { return (rows ?? Enumerable.Empty()) .Where(row => row?.tradecash != null && row.tradecash.ValueDate.Date == valueDate.Date) .ToList(); } /// /// 将去重后的交易编号及客户视角平仓总额写入邮件模板参数。 /// public static void PopulateSettlementFields(MailInfoRequestModel request, IEnumerable rows) { ArgumentNullException.ThrowIfNull(request); // 同一平仓记录可能因关联多份文档重复出现,按 trade_cash.id 去重以避免重复计入金额。 var distinctRows = (rows ?? Enumerable.Empty()) .Where(row => row?.trade != null && row.tradecash != null) .GroupBy(row => row.tradecash.id) .Select(group => group.First()) .ToList(); request.TradeNumber = string.Join(",", distinctRows .Select(row => row.trade.TradeNumber) .Where(number => !string.IsNullOrWhiteSpace(number)) .Distinct() .OrderBy(number => number)); if (distinctRows.Count == 0) { request.UnwindTotalAmount = string.Empty; return; } // trade_cash.Amount 为交易员视角,邮件展示需取反转换为客户视角。 var customerAmount = -distinctRows.Sum(row => row.tradecash.Amount); if (customerAmount == 0) { // 消除浮点求和可能产生的 -0.00。 customerAmount = 0; } request.UnwindTotalAmount = customerAmount.ToString("F2", CultureInfo.InvariantCulture); } private static string BuildDocumentKey(string code, string type) { return $"{code}\u001f{type}"; } } }