feat(report): 添加互换估值列配置功能

- 引入 Newtonsoft.Json 用于列配置的序列化处理
- 实现 ApplySwapValuationColumnConfig 方法应用用户自定义列配置
- 添加 ReorderSwapValuationColumns 方法重新排序可见列
- 在每日估值报告导出时同步业务字段显示状态和列顺序
- 使用临时工作表避免 EPPlus 扩列时的 ColumnMax 冲突
- 确保列配置损坏时不影响报告导出功能
This commit is contained in:
张名锐
2026-07-16 21:25:46 +08:00
parent cd9fed9913
commit 2495c01f68
@@ -1,4 +1,5 @@
using BaseOUDAL;
using Newtonsoft.Json;
using OfficeOpenXml;
using OfficeOpenXml.Style;
using Org.BouncyCastle.Ocsp;
@@ -309,6 +310,7 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
, shouldDeleteSheet: true, needToPdf: false, callback: sheets =>
{
FormatSwapValuationDisplayCells(sheets, report.EodSwapPositions);
ApplySwapValuationColumnConfig(sheets);
});
if (needToPdf)
{
@@ -384,6 +386,135 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
cell.Style.Numberformat.Format = "@";
}
/// <summary>
/// 每日估值报告的互换估值导出使用当前用户已保存的列配置,
/// 同时同步业务字段的显示状态和列顺序。
/// </summary>
private void ApplySwapValuationColumnConfig(IEnumerable<ExcelWorksheet> sheets)
{
var worksheet = sheets.FirstOrDefault(x => x.Name == "互换估值");
if (worksheet == null)
{
return;
}
var config = configcolumnBLL.GetData(UserId, configcolumn_data.V1);
if (string.IsNullOrWhiteSpace(config?.data))
{
return;
}
List<columnmodel> columns;
try
{
columns = JsonConvert.DeserializeObject<List<columnmodel>>(config.data);
}
catch (JsonException)
{
// 列配置损坏时保留完整的模板字段,不能导致每日估值报告导出失败。
return;
}
if (columns == null || !columns.Any())
{
return;
}
var columnIndexByName = new Dictionary<string, int>
{
["ConfrimNo"] = 1,
["TradeNumber"] = 2,
["position.PosiStartDate"] = 3,
["MaturitySettlementDate"] = 4,
["position.ValueDate"] = 5,
["position.UnderlyingCode"] = 6,
["InterestRate"] = 7,
["position.PosiNotionalValue"] = 8,
["position.PosiQuantity"] = 9,
["PeriodAmount"] = 10,
["DividendAmount"] = 11,
["position.PosiGrossPrice"] = 12,
["InitYtm"] = 13,
["position.UnderlyingPrice"] = 14,
["DayCount"] = 15,
["FloatRateAbs"] = 16,
["InterestAmount"] = 17,
["position.PosiProfitSum"] = 18,
["position.PosiFeePending"] = 19,
["OpenMarginRate"] = 20,
["MarginInterestAmount"] = 21,
["OpenMarginAmount"] = 22,
["AdditionalMarginAmount"] = 23,
["NetSettmentAmount"] = 24,
["TrsValue"] = 25
};
// 前端 _.uniqBy 保留首次出现的配置项,导出按相同规则过滤重复字段,
// 并保留该列表的原始顺序作为 Excel 列顺序。
var visibleColumnIndexes = new List<int>();
var configuredNames = new HashSet<string>();
foreach (var column in columns)
{
if (column == null || string.IsNullOrEmpty(column.name) || !configuredNames.Add(column.name))
{
continue;
}
if (!column.hidden && columnIndexByName.TryGetValue(column.name, out var index))
{
visibleColumnIndexes.Add(index);
}
}
// 与前端一致:旧配置缺少的新增字段默认隐藏;若没有任何业务列可见,
// 前端会回退展示全部默认列,导出保持模板默认顺序。
if (!visibleColumnIndexes.Any())
{
return;
}
ReorderSwapValuationColumns(worksheet, visibleColumnIndexes);
}
/// <summary>
/// 使用临时工作表保存可见列,再按目标顺序复制回原工作表。
/// 原工作表始终保持在模板的 25 列范围内,避免 EPPlus 扩列时触发 ColumnMax 冲突。
/// </summary>
private static void ReorderSwapValuationColumns(ExcelWorksheet worksheet, IReadOnlyList<int> visibleSourceIndexes)
{
var lastRow = worksheet.Dimension.End.Row;
var originalColumnCount = worksheet.Dimension.End.Column;
var workbook = worksheet.Workbook;
var bufferName = "__swap_cols_" + Guid.NewGuid().ToString("N").Substring(0, 12);
var buffer = workbook.Worksheets.Add(bufferName);
try
{
for (var targetIndex = 0; targetIndex < visibleSourceIndexes.Count; targetIndex++)
{
var sourceColumn = visibleSourceIndexes[targetIndex];
var bufferColumn = targetIndex + 1;
worksheet.Cells[1, sourceColumn, lastRow, sourceColumn]
.Copy(buffer.Cells[1, bufferColumn, lastRow, bufferColumn]);
}
for (var targetIndex = 0; targetIndex < visibleSourceIndexes.Count; targetIndex++)
{
var targetColumn = targetIndex + 1;
buffer.Cells[1, targetColumn, lastRow, targetColumn]
.Copy(worksheet.Cells[1, targetColumn, lastRow, targetColumn]);
}
var columnsToDelete = originalColumnCount - visibleSourceIndexes.Count;
if (columnsToDelete > 0)
{
worksheet.DeleteColumn(visibleSourceIndexes.Count + 1, columnsToDelete);
}
}
finally
{
workbook.Worksheets.Delete(buffer);
}
}
/// <summary>
/// 财务状况
/// </summary>