69.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--财务健康度

我们记账不只是要记录我们每天的收支数据,更重要的是要了解我们的财务健康状况。在本文中,沃尔玛将从四个维度来分析我们的财务健康状况。

一、评分模型设计

我们设计了一个多维加权评分模型,从以下四个维度全面评估财务健康状况:

# 维度 说明
1 收支比率 衡量收入与支出之间的关系,理想情况下收入应大于支出
2 储蓄率 衡量每月储蓄的比例,健康的储蓄率通常在 20% 以上
3 预算执行率 衡量实际支出与预算的差距,理想执行率应在 90% 以下
4 收入稳定性 衡量收入是否稳定,理想情况下收入来源应多样且稳定
1.1 核心计算公式

每个维度根据其重要性分配不同权重,可依据个人财务目标灵活调整:

总分=∑(维度得分×权重) \text{总分} = \sum (\text{维度得分} \times \text{权重}) 总分=∑(维度得分×权重)

1.2 权重分配
  1. 标准模式(存在预算数据时):
维度 权重
收支比率 30%
储蓄率 30%
预算执行率 25%
收入稳定性 15%
  1. 无预算模式
    当不存在预算数据时,权重自动调整为:收支比率 40% 、储蓄率 40% 、收入稳定性 20%,以避免对"无预算用户"产生不公平的评估结果。
1.3 健康等级划分
分数区间 等级 评价
80 ~ 100 🌟 优秀 财务状况非常健康
60 ~ 79 ✅ 良好 财务状况基本健康
40 ~ 59 ⚠️ 一般 财务状况有待改善
0 ~ 39 ❌ 较差 财务状况需重点关注

二、核心算法设计

上一小节我们简单的讲解了模型的设计,那么接下来我们讲解一下核心算法的设计。

2.1 收支比率

收支比率是衡量收入与支出之间关系的关键指标,我们采用支出总额除以收入总额来计算,公式如下:

ratio=支出总额收入总额 \text{ratio} = \frac{\text{支出总额}}{\text{收入总额}} ratio=收入总额支出总额

算出来的 ratio 越小,说明收入越多,财务状况越健康。理想情况下 ratio 应该小于 1,表示收入大于支出。评分结果映射到代码中如下:

csharp 复制代码
// ScoreCalculator.cs
public static decimal CalcIncomeExpenseRatioScore(decimal income, decimal expense)
{
    if (income <= 0) return 0;
    decimal ratio = expense / income;
    if (ratio <= 0.5m) return 100;
    if (ratio <= 0.7m) return 80;
    if (ratio <= 0.9m) return 60;
    if (ratio <= 1.0m) return 40;
    return Math.Max(0, Math.Round(20 - (ratio - 1.0m) * 100, 2));
}

更直观的理解是,如果支出占收入的比例越小,说明财务状况越健康,得分也就越高。相反,如果支出占收入的比例过高,说明财务状况较差,得分也会相应降低,因此我们希望通过这个指标来鼓励用户控制支出,增加收入,从而提升整体的财务健康状况。直观的图表如下所示:

复制代码
支出/收入   ≤50%  → 100分  非常健康,有大量结余
            ≤70%  →  80分  良好,符合"50/30/20"法则
            ≤90%  →  60分  偏紧,留存空间不足
            ≤100% →  40分  危险边缘,几乎无储蓄
            >100% →  线性衰减至0分(入不敷出)

我们看到上述代码中,在ratio超过100%时,并没有直接给出0分,而是采用了一个线性衰减的方式来计算得分,这样可以更细致地反映出不同程度的财务压力,帮助用户更好地理解自己的财务状况,并采取相应的措施来改善。它所使用的公式如下:
score=20−(ratio−1.0)×100 \text{score}=20-(\text{ratio}-1.0) \times 100 score=20−(ratio−1.0)×100

例如某月超支10%,即 ratio = 1.1,那么根据公式可得分数为:
score=20−(1.1−1.0)×100=20−0.1×100=20−10=10 \text{score}=20-(1.1-1.0) \times 100=20-0.1 \times 100=20-10=10 score=20−(1.1−1.0)×100=20−0.1×100=20−10=10

当某月超支20%,即 ratio = 1.2,那么根据公式可得分数为:
score=20−(1.2−1.0)×100=20−0.2×100=20−20=0 \text{score}=20-(1.2-1.0) \times 100=20-0.2 \times 100=20-20=0 score=20−(1.2−1.0)×100=20−0.2×100=20−20=0

到了这里,估计一定会有部分读者很疑惑,这个20是怎么来的,为什么是100的线性衰减呢?其实这个20是我们在设计评分模型时,根据实际情况和经验总结出来的一个基准分数,表示当支出刚好等于收入时,用户的财务状况已经处于一个比较危险的边缘状态了,因此我们给出了一个相对较低的基准分数。而100的线性衰减则是为了让得分能够更细致地反映出不同程度的财务压力,帮助用户更好地理解自己的财务状况,并采取相应的措施来改善。通过这种方式,我们希望能够鼓励用户控制支出,增加收入,从而提升整体的财务健康状况。

2.2 储蓄率

储蓄率是衡量每月储蓄占收入比例的关键指标,我们采用收入减去支出的差额除以收入总额来计算,公式如下:
rate=收入总额−支出总额收入总额 \text{rate} = \frac{\text{收入总额} - \text{支出总额}}{\text{收入总额}} rate=收入总额收入总额−支出总额

算出来的 rate 越大,说明储蓄越多,财务状况越健康。理想情况下 rate 应该大于 20%,表示每月有足够的储蓄来应对突发情况和未来的财务目标。评分结果映射到代码中如下:

csharp 复制代码
public static decimal CalcSavingsRateScore(decimal income, decimal expense)
{
    if (income <= 0) return 0;
    decimal savingsRate = (income - expense) / income;
    if (savingsRate >= 0.3m) return 100;
    if (savingsRate >= 0.2m) return 80;
    if (savingsRate >= 0.1m) return 60;
    if (savingsRate >= 0m)   return 40;
    return 0;
}

下面是一个直观的图表,帮助大家更好地理解储蓄率与财务健康状况之间的关系:

复制代码
储蓄率  ≥30%  → 100分  远超推荐,优秀理财习惯
        ≥20%  →  80分  达到财务自由路径目标
        ≥10%  →  60分  满足最低储蓄建议
        ≥0%   →  40分  有结余但不足
        <0%   →   0分  负储蓄(借贷消费)

在这里,有人会问有了收支比率,为什么还要有储蓄率。它们两者在数学上是高度相关的,虽然都是使用收入和支出计算出来的,但是他们的侧重点不同。收支比率的关注点是支出规模的控制力,二储蓄率的关注点是财富的积累能力。一个人可能有较高的收支比率(即支出占收入的比例较高),但如果收入足够大,仍然可以有一个不错的储蓄率。反之,一个人可能有较低的收支比率(即支出占收入的比例较低),但如果收入不足,储蓄率可能也不理想。

2.3 预算执行率

预算执行率是衡量实际支出与预算之间差距的关键指标,我们采用实际支出减去预算金额的差值,再除以预算来计算,公式如下:
rate=实际支出−预算预算 \text{rate} = \frac{\text{实际支出} - \text{预算}}{\text{预算}} rate=预算实际支出−预算

算出来的 rate 越小,说明预算执行得越好,财务状况越健康。理想情况下 rate 应该小于 0,表示实际支出低于预算,有助于控制财务风险。评分结果映射到代码中如下:

csharp 复制代码
public static decimal? CalcBudgetComplianceScore(List<(decimal Budget, decimal Actual)> budgetItems)
{
    if (budgetItems == null || budgetItems.Count == 0) return null;

    // 对每条预算单独打分,最终取平均
    decimal itemScore = overrunRate switch
    {
        <= 0m    => 100,   // 未超支
        <= 0.1m  =>  80,   // 超支 ≤10%,轻微超出
        <= 0.2m  =>  60,   // 超支 ≤20%,需注意
        <= 0.5m  =>  40,   // 超支 ≤50%,明显失控
        _        =>  20    // 超支 >50%,严重失控
    };
}

这个算法比较简单,我们就不做过多的讲解了,大家如果有什么不明白的可以私信我。

Tip:这里需要注意的是,如果没有预算数据,我们应该返回 null,而不是 0 分,因为没有预算数据就无法评估预算执行率的表现,直接给 0 分会对用户产生误导,认为他们的预算执行率非常差,而实际上他们可能根本没有设置预算,所以我们选择返回 null 来表示无法评估,这样可以更准确地反映出用户的实际情况。

2.4 收入稳定性

最后一个维度是收入稳定性,收入稳定性是衡量收入是否稳定的关键指标,我们采用收入的标准差除以平均收入来计算,公式如下:
stability=1−收入标准差平均收入 \text{stability} = 1 - \frac{\text{收入标准差}}{\text{平均收入}} stability=1−平均收入收入标准差

算出来的 stability 越大,说明收入越稳定,财务状况越健康。理想情况下 stability 应该接近 1,表示收入非常稳定,有助于财务规划和风险管理。评分结果映射到代码中如下:

csharp 复制代码
public static decimal CalcIncomeStabilityScore(List<decimal> monthlyIncomes)
{
    if (monthlyIncomes == null || monthlyIncomes.Count < 2) return 80; // 数据不足给默认分
    decimal mean = monthlyIncomes.Average();
    if (mean <= 0) return 0;
    decimal variance = monthlyIncomes.Sum(x => (x - mean) * (x - mean)) / monthlyIncomes.Count;
    decimal stdDev = (decimal)Math.Sqrt((double)variance);
    decimal cv = stdDev / mean;
    if (cv <= 0.05m) return 100;  // 极度稳定(月薪固定职工)
    if (cv <= 0.10m) return  80;  // 基本稳定
    if (cv <= 0.20m) return  60;  // 轻微波动(含奖金月份)
    if (cv <= 0.30m) return  40;  // 明显波动(兼职/自由职业)
    return 20;                    // 严重波动
}

我们再计算收入稳定性时,需要向前追溯三个月的数据,来计算收入的标准差和平均值,这样才能更准确地评估收入的稳定性。通过这个算法,我们可以帮助用户了解自己的收入是否稳定,从而更好地进行财务规划和风险管理。这部分代码如下:

csharp 复制代码
// FinancialHealthScoreServiceImpl.cs
var stabilityStart = new DateTime(periodStart.Year, periodStart.Month, 1).AddMonths(-2);
// 例:统计 7月,则取 5月1日 ~ 7月31日 的数据
// 按年月分组 → 得到 [5月收入, 6月收入, 7月收入]
var monthlyIncomes = stabilityEntries
    .Where(e => e.Type == TransactionCategoryEnmu.Income)
    .GroupBy(e => new { e.Year, e.Month })
    .Select(g => g.Sum(x => x.Amount))
    .ToList();

在这里有两个问题,一个是为什么要追溯前的三个月的数据,另一个是为什么数据不足时给80分,而不是0分。对于第一个问题,我们追溯前的三个月的数据是为了能够更全面地评估收入的稳定性,因为单个月的数据可能会受到偶然因素的影响,无法准确反映出收入的稳定性。通过追溯前的三个月的数据,我们可以更好地捕捉到收入的波动情况,从而更准确地评估收入的稳定性,当然也可以追溯更长时间的数据,这个可以根据实际情况来调整。对于第二个问题,当数据不足时,我们给80分而不是0分,是因为我们认为在数据不足的情况下,无法准确评估收入的稳定性,因此我们选择给一个相对较高的默认分数,以避免对用户产生过于负面的评估结果。当然,这个默认分数也可以根据实际情况来调整,或者直接返回null表示无法评估,这些都是设计上的权衡和选择。

2.5 综合评分计算

前面四个维度的评分都有了,接下来将这个四个维度的评分综合起来计算,我们使用加权汇总与等级映射的方式来计算最终的财务健康评分,具体代码如下:

csharp 复制代码
public static (decimal TotalScore, HealthLevelEnum Level) CalcTotalScore(
    decimal incomeExpenseScore,
    decimal savingsRateScore,
    decimal? budgetComplianceScore,   // nullable
    decimal incomeStabilityScore)
{
    decimal total = budgetComplianceScore.HasValue
        ? incomeExpenseScore   * 0.30m   // 有预算:标准权重
          + savingsRateScore   * 0.30m
          + budgetComplianceScore.Value * 0.25m
          + incomeStabilityScore * 0.15m
        : incomeExpenseScore   * 0.40m   // 无预算:权重重分配
          + savingsRateScore   * 0.40m
          + incomeStabilityScore * 0.20m;

    HealthLevelEnum level = total switch
    {
        >= 80 => HealthLevelEnum.Excellent,
        >= 60 => HealthLevelEnum.Good,
        >= 40 => HealthLevelEnum.Fair,
        _     => HealthLevelEnum.Poor
    };
}

通过这个综合评分计算,我们可以得到一个总分和对应的健康等级,帮助用户更好地了解自己的财务健康状况,并根据不同的等级提供相应的建议和指导,帮助用户改善财务状况,实现财务目标。

下面我们通过一个简单的实例来演示一下,例如某用户月收入 10000 元,月支出 7500元,预算略超支,最终计算的分数如下:

维度 计算过程 得分 加权得分
收支比率 ratio=0.75 → 0.7<0.75≤0.9 60 60 * 0.30 = 18
储蓄率 (10000-7500)/10000=25% → ≥20% 80 80 * 0.30 = 24
预算执行率 平均超支8% → ≤10% 80 80 * 0.25 = 20
收入稳定性 CV=0.08 → ≤0.10 80 80 * 0.15 = 12
总分 = 74 → 良好

三、总结

通过上述的评分模型设计和核心算法实现,我们可以全面评估用户的财务健康状况,帮助用户了解自己的财务状况,并提供相应的建议和指导,帮助用户改善财务状况,实现财务目标。这个模型不仅考虑了收入和支出的关系,还考虑了储蓄能力、预算执行情况和收入稳定性等多个维度,使得评估结果更加全面和准确。同时,通过加权汇总和等级映射的方式,我们可以更直观地展示用户的财务健康状况,帮助用户更好地理解自己的财务状况,并采取相应的措施来改善。

Tip:本文中不包含数据库实体模型、service和controller的内容,是因为这些内容并不是本文的核心内容。本文主要关注于评分模型的设计和核心算法的实现,而数据库实体模型、service和controller的内容相对来说比较基础和常规,并不涉及到复杂的逻辑和设计,因此我们选择不在本文中进行详细的讲解,以保持文章的重点和清晰度。当然,如果读者对这些内容感兴趣,可以访问专栏的GitHub仓库,那里有完整的代码实现,包括数据库实体模型、service和controller的内容,读者可以参考这些代码来更好地理解和实现自己的财务健康评分系统。

相关推荐
Will_11302 小时前
Linux运维自动化常用的Python库
linux·运维·自动化
小小unicorn2 小时前
[微服务即时通讯系统]3.服务端-环境搭建
数据库·c++·redis·微服务·云原生·架构
Mu_chidonggua2 小时前
HAProxy
运维
牛马鸡niumasi2 小时前
Linux I/O重定向
linux·运维·服务器
老一岁2 小时前
linux vim及ps到tar包的详解
linux·运维·vim
林鸿群2 小时前
Git 实战:如何将本地 .NET 项目推送到 GitLab 私有仓库
git·gitlab·.net
linux修理工2 小时前
vi/vim 基本操作指南
运维·服务器
贪嘴2 小时前
Visual Studio 2026 不支持 .net upgrade assistant 升级助手 安装失败怎么办
ide·.net·visual studio
红牛vc2 小时前
Centos 7.9openssl 升级报错和python3版本安装
linux·运维·centos