新增不记额收支
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 34s
Docker Build & Deploy / Deploy to Production (push) Successful in 8s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 0s
Docker Build & Deploy / WeChat Notification (push) Successful in 3s

This commit is contained in:
孙诚
2026-01-15 10:53:05 +08:00
parent 12cf1b6323
commit 65f7316c82
9 changed files with 456 additions and 123 deletions

View File

@@ -54,6 +54,7 @@ public class BudgetService(
Current = c.Actual,
Category = c.Category,
SelectedCategories = c.SelectedCategories,
NoLimit = c.NoLimit,
Description = c.Description,
PeriodStart = periodRange.start,
PeriodEnd = periodRange.end,
@@ -172,9 +173,9 @@ public class BudgetService(
Count = 0
};
// 获取当前分类下所有预算
// 获取当前分类下所有预算,排除不记额预算
var relevant = budgets
.Where(b => b.Category == category)
.Where(b => b.Category == category && !b.NoLimit)
.ToList();
if (relevant.Count == 0)
@@ -249,6 +250,7 @@ public class BudgetService(
Actual = b.Current,
Category = b.Category,
SelectedCategories = b.SelectedCategories,
NoLimit = b.NoLimit,
Description = b.Description
}).ToArray();
@@ -471,9 +473,13 @@ public class BudgetService(
decimal incomeLimitAtPeriod = 0;
decimal expenseLimitAtPeriod = 0;
decimal noLimitIncomeAtPeriod = 0; // 新增:不记额收入汇总
decimal noLimitExpenseAtPeriod = 0; // 新增:不记额支出汇总
var incomeItems = new List<(string Name, decimal Limit, decimal Factor, decimal Total)>();
var expenseItems = new List<(string Name, decimal Limit, decimal Factor, decimal Total)>();
var noLimitIncomeItems = new List<(string Name, decimal Amount)>(); // 新增
var noLimitExpenseItems = new List<(string Name, decimal Amount)>(); // 新增
foreach (var b in allBudgets)
{
@@ -507,16 +513,36 @@ public class BudgetService(
if (factor <= 0) continue;
var subtotal = b.Limit * factor;
if (b.Category == BudgetCategory.Income)
// 新增:处理不记额预算
if (b.NoLimit)
{
incomeLimitAtPeriod += subtotal;
incomeItems.Add((b.Name, b.Limit, factor, subtotal));
// 不记额预算:计算实际发生的金额
var actualAmount = await CalculateCurrentAmountAsync(b, date);
if (b.Category == BudgetCategory.Income)
{
noLimitIncomeAtPeriod += actualAmount;
noLimitIncomeItems.Add((b.Name, actualAmount));
}
else if (b.Category == BudgetCategory.Expense)
{
noLimitExpenseAtPeriod += actualAmount;
noLimitExpenseItems.Add((b.Name, actualAmount));
}
}
else if (b.Category == BudgetCategory.Expense)
else
{
expenseLimitAtPeriod += subtotal;
expenseItems.Add((b.Name, b.Limit, factor, subtotal));
// 普通预算:按限额计算
var subtotal = b.Limit * factor;
if (b.Category == BudgetCategory.Income)
{
incomeLimitAtPeriod += subtotal;
incomeItems.Add((b.Name, b.Limit, factor, subtotal));
}
else if (b.Category == BudgetCategory.Expense)
{
expenseLimitAtPeriod += subtotal;
expenseItems.Add((b.Name, b.Limit, factor, subtotal));
}
}
}
@@ -552,6 +578,34 @@ public class BudgetService(
}
description.Append($"<p>收入合计: <span class='income-value'><strong>{incomeLimitAtPeriod:N0}</strong></span></p>");
// 新增:显示不记额收入明细
description.Append("<h3>不记额收入明细</h3>");
if (noLimitIncomeItems.Count == 0) description.Append("<p>无不记额收入</p>");
else
{
description.Append("""
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
""");
foreach (var item in noLimitIncomeItems)
{
description.Append($"""
<tr>
<td>{item.Name}</td>
<td><span class='income-value'>{item.Amount:N0}</span></td>
</tr>
""");
}
description.Append("</tbody></table>");
}
description.Append($"<p>不记额收入合计: <span class='income-value'><strong>{noLimitIncomeAtPeriod:N0}</strong></span></p>");
description.Append("<h3>预算支出明细</h3>");
if (expenseItems.Count == 0) description.Append("<p>无支出预算</p>");
else
@@ -583,14 +637,46 @@ public class BudgetService(
}
description.Append($"<p>支出合计: <span class='expense-value'><strong>{expenseLimitAtPeriod:N0}</strong></span></p>");
// 新增:显示不记额支出明细
description.Append("<h3>不记额支出明细</h3>");
if (noLimitExpenseItems.Count == 0) description.Append("<p>无不记额支出</p>");
else
{
description.Append("""
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
""");
foreach (var item in noLimitExpenseItems)
{
description.Append($"""
<tr>
<td>{item.Name}</td>
<td><span class='expense-value'>{item.Amount:N0}</span></td>
</tr>
""");
}
description.Append("</tbody></table>");
}
description.Append($"<p>不记额支出合计: <span class='expense-value'><strong>{noLimitExpenseAtPeriod:N0}</strong></span></p>");
description.Append("<h3>存款计划结论</h3>");
description.Append($"<p>计划存款 = 收入 <span class='income-value'>{incomeLimitAtPeriod:N0}</span> - 支出 <span class='expense-value'>{expenseLimitAtPeriod:N0}</span></p>");
description.Append($"<p>最终目标:<span class='highlight'><strong>{incomeLimitAtPeriod - expenseLimitAtPeriod:N0}</strong></span></p>");
// 修改计算公式:包含不记额收入和支出
var totalIncome = incomeLimitAtPeriod + noLimitIncomeAtPeriod;
var totalExpense = expenseLimitAtPeriod + noLimitExpenseAtPeriod;
description.Append($"<p>计划收入 = 预算 <span class='income-value'>{incomeLimitAtPeriod:N0}</span> + 不记额 <span class='income-value'>{noLimitIncomeAtPeriod:N0}</span> = <span class='income-value'><strong>{totalIncome:N0}</strong></span></p>");
description.Append($"<p>计划支出 = 预算 <span class='expense-value'>{expenseLimitAtPeriod:N0}</span> + 不记额 <span class='expense-value'>{noLimitExpenseAtPeriod:N0}</span> = <span class='expense-value'><strong>{totalExpense:N0}</strong></span></p>");
description.Append($"<p>最终目标:<span class='highlight'><strong>{totalIncome - totalExpense:N0}</strong></span></p>");
var virtualBudget = await BuildVirtualSavingsBudgetRecordAsync(
periodType == BudgetPeriodType.Year ? -1 : -2,
date,
incomeLimitAtPeriod - expenseLimitAtPeriod);
totalIncome - totalExpense); // 修改:使用总金额
// 计算实际发生的 收入 - 支出
var current = await CalculateCurrentAmountAsync(new BudgetRecord
@@ -638,7 +724,7 @@ public record BudgetResult
public string Period { get; set; } = string.Empty;
public DateTime? PeriodStart { get; set; }
public DateTime? PeriodEnd { get; set; }
public bool NoLimit { get; set; } = false;
public string Description { get; set; } = string.Empty;
public static BudgetResult FromEntity(
@@ -670,6 +756,7 @@ public record BudgetResult
},
PeriodStart = start,
PeriodEnd = end,
NoLimit = entity.NoLimit,
Description = description
};
}