budgets, decimal totalLimit, decimal totalCurrent, DateTime referenceDate, BudgetCategory category)
{
var description = new StringBuilder();
var categoryName = category == BudgetCategory.Expense ? "支出" : "收入";
description.AppendLine($"月度{categoryName}预算明细
");
description.AppendLine("""
| 名称 |
预算额度 |
实际金额 |
类型 |
""");
foreach (var budget in budgets)
{
var budgetLimit = CalculateBudgetLimit(budget, BudgetPeriodType.Month, referenceDate);
var typeLabel = budget.IsMandatoryExpense ? "硬性" : "普通";
var archiveLabel = budget.IsArchive ? $" ({budget.ArchiveMonth}月归档)" : "";
description.AppendLine($"""
| {budget.Name}{archiveLabel} |
{budgetLimit:N0} |
{budget.Current:N1} |
{typeLabel} |
""");
}
description.AppendLine("
");
// 计算公式
description.AppendLine($"计算公式
");
description.AppendLine($"预算额度合计:");
var limitParts = budgets.Select(b =>
{
var limit = CalculateBudgetLimit(b, BudgetPeriodType.Month, referenceDate);
var archiveLabel = b.IsArchive ? $"({b.ArchiveMonth}月归档)" : "";
return $"{b.Name}{archiveLabel}({limit:N0})";
});
description.AppendLine($"{string.Join(" + ", limitParts)} = {totalLimit:N0}
");
description.AppendLine($"实际{categoryName}合计:");
var currentParts = budgets.Select(b =>
{
var archiveLabel = b.IsArchive ? $"({b.ArchiveMonth}月归档)" : "";
return $"{b.Name}{archiveLabel}({b.Current:N1})";
});
description.AppendLine($"{string.Join(" + ", currentParts)} = {totalCurrent:N1}
");
var rate = totalLimit > 0 ? totalCurrent / totalLimit * 100 : 0;
description.AppendLine($"使用率:{totalCurrent:N1} ÷ {totalLimit:N0} × 100% = {rate:F2}%
");
return description.ToString();
}
private string GenerateYearlyDescription(List budgets, decimal totalLimit, decimal totalCurrent, DateTime referenceDate, BudgetCategory category)
{
var description = new StringBuilder();
var categoryName = category == BudgetCategory.Expense ? "支出" : "收入";
// 分组:归档的月度预算、归档的年度预算、当前月度预算(剩余月份)、当前年度预算
var archivedMonthlyBudgets = budgets.Where(b => b is { IsArchive: true, Type: BudgetPeriodType.Month }).ToList();
var archivedYearlyBudgets = budgets.Where(b => b is { IsArchive: true, Type: BudgetPeriodType.Year }).ToList();
var currentMonthlyBudgets = budgets.Where(b => b is { IsArchive: false, Type: BudgetPeriodType.Month }).ToList();
var currentYearlyBudgets = budgets.Where(b => b is { IsArchive: false, Type: BudgetPeriodType.Year }).ToList();
// 归档月度预算明细
if (archivedMonthlyBudgets.Any())
{
description.AppendLine($"已归档月度{categoryName}预算
");
description.AppendLine("""
| 名称 |
预算额度 |
实际金额 |
归档月份 |
""");
foreach (var budget in archivedMonthlyBudgets.GroupBy(b => b.Id))
{
var first = budget.First();
var months = budget.Select(b => b.ArchiveMonth).OrderBy(m => m).ToArray();
var monthsText = FormatMonths(months);
var groupLimit = first.Limit * budget.Count();
var groupCurrent = budget.Sum(b => b.Current);
description.AppendLine($"""
| {first.Name} |
{groupLimit:N0} |
{groupCurrent:N1} |
{monthsText} |
""");
}
description.AppendLine("
");
}
// 当前月度预算(剩余月份)
if (currentMonthlyBudgets.Any())
{
description.AppendLine($"当前月度{categoryName}预算(当前月及未来月)
");
description.AppendLine("""
| 名称 |
类型 |
计算方式 |
合计额度 |
实际金额 |
""");
foreach (var budget in currentMonthlyBudgets)
{
var budgetLimit = CalculateBudgetLimit(budget, BudgetPeriodType.Year, referenceDate);
var typeStr = budget.IsCurrentMonth ? "当前月" : "未来月";
// 修正:当前月是1个月,未来月是剩余月份数量
var calcStr = budget.IsCurrentMonth
? $"1个月×{budget.Limit:N0}"
: $"{budget.RemainingMonths}个月×{budget.Limit:N0}";
description.AppendLine($"""
| {budget.Name} |
{typeStr} |
{calcStr} |
{budgetLimit:N0} |
{(budget.IsCurrentMonth ? budget.Current.ToString("N1") : "-")} |
""");
}
description.AppendLine("
");
}
// 年度预算明细
if (archivedYearlyBudgets.Any() || currentYearlyBudgets.Any())
{
description.AppendLine($"年度{categoryName}预算
");
description.AppendLine("""
| 名称 |
预算额度 |
实际金额 |
状态 |
""");
foreach (var budget in archivedYearlyBudgets.Concat(currentYearlyBudgets))
{
var statusLabel = budget.IsArchive ? "归档" : "当前";
var typeLabel = budget.IsMandatoryExpense ? "硬性" : "普通";
description.AppendLine($"""
| {budget.Name} |
{budget.Limit:N0} |
{budget.Current:N1} |
{statusLabel}/{typeLabel} |
""");
}
description.AppendLine("
");
}
// 计算公式
description.AppendLine($"计算公式
");
description.AppendLine($"年度预算额度合计:");
var limitParts = new List();
// 归档月度预算部分
foreach (var group in archivedMonthlyBudgets.GroupBy(b => b.Id))
{
var first = group.First();
var count = group.Count();
var groupTotalLimit = first.Limit * count;
limitParts.Add($"{first.Name}(归档{count}月×{first.Limit:N0}={groupTotalLimit:N0})");
}
// 当前月度预算部分
foreach (var budget in currentMonthlyBudgets)
{
var budgetLimit = CalculateBudgetLimit(budget, BudgetPeriodType.Year, referenceDate);
if (budget.IsCurrentMonth)
{
limitParts.Add($"{budget.Name}(当前月1个月×{budget.Limit:N0}={budgetLimit:N0})");
}
else
{
limitParts.Add($"{budget.Name}(未来{budget.RemainingMonths}个月×{budget.Limit:N0}={budgetLimit:N0})");
}
}
// 年度预算部分
foreach (var budget in archivedYearlyBudgets.Concat(currentYearlyBudgets))
{
limitParts.Add($"{budget.Name}({budget.Limit:N0})");
}
description.AppendLine($"{string.Join(" + ", limitParts)} = {totalLimit:N0}
");
description.AppendLine($"实际{categoryName}合计:");
var currentParts = new List();
// 归档月度预算的实际值
foreach (var group in archivedMonthlyBudgets.GroupBy(b => b.Id))
{
var first = group.First();
var groupTotalCurrent = group.Sum(b => b.Current);
currentParts.Add($"{first.Name}(归档{groupTotalCurrent:N1})");
}
// 年度预算的实际值
foreach (var budget in archivedYearlyBudgets.Concat(currentYearlyBudgets))
{
currentParts.Add($"{budget.Name}({budget.Current:N1})");
}
description.AppendLine($"{string.Join(" + ", currentParts)} = {totalCurrent:N1}
");
var rate = totalLimit > 0 ? totalCurrent / totalLimit * 100 : 0;
description.AppendLine($"使用率:{totalCurrent:N1} ÷ {totalLimit:N0} × 100% = {rate:F2}%
");
return description.ToString();
}
private string FormatMonths(int[] months)
{
if (months.Length == 0) return "";
if (months.Length == 1) return $"{months[0]}月";
// 如果是连续的月份,简化显示为 1~3月
Array.Sort(months);
var isContinuous = true;
for (var i = 1; i < months.Length; i++)
{
if (months[i] != months[i - 1] + 1)
{
isContinuous = false;
break;
}
}
if (isContinuous)
{
return $"{months.First()}~{months.Last()}月";
}
return string.Join(", ", months) + "月";
}
}