基于大模型的个人消费分析和理财助手:开发日志 7
基于大模型的个人消费分析和理财助手:开发日志 7
月度报告收入统计:收支双维度分析的设计
背景与问题
APP 的月度报告最初只统计支出。这对记账 APP 来说是常见选择——人们记账主要是想控制支出。但事实上,不理解自己的收入结构,就无法评估财务健康状况:"月光的 3000 元花销意味着什么?"如果不对比收入,这句话毫无意义。
改进的目标是让月度报告同时展示收入从哪里来和钱花到哪里去,形成完整的财务画像。
后端:拆分为两套独立的统计逻辑
原始代码将所有账单的金额合在一起,没有区分支出和收入:
# 旧方案:按收支方向拆分前
base_conds = [
Bill.user_id == user.id,
func.year(Bill.time) == year,
func.month(Bill.time) == month,
]
改造的关键在于抽象出带方向的查询条件工厂:
def _base_conds(direction: str) -> list:
return [
Bill.user_id == user.id,
func.year(Bill.time) == year,
func.month(Bill.time) == month,
Bill.transaction_direction == direction, # 新增维度筛选
]
# 支出统计
exp_conds = _base_conds("支出")
# 收入统计
inc_conds = _base_conds("收入")
核心设计意图: 将原本平行的查询逻辑,按 transaction_direction 拆分为两套独立的统计链路。每条链路都包含:汇总金额、交易笔数、平均每笔金额、分类聚合、每日趋势、最大类别,共计 6 个统计指标。每个指标都同时计算 “outcome_” 和 “income_” 两个版本。
这种设计有意识地和"全量统计再过滤"的方案区分——如果先查全部账单再用 Python 过滤,随着数据量增长会越来越慢。SQLAlchemy 层面就分流,数据库索引能发挥最大作用。
字段命名策略:
class MonthlyReportResponse(BaseModel):
# 支出
total_outcome: Decimal
outcome_count: int
outcome_avg_per_transaction: Decimal
outcome_category_breakdown: list[MonthlyReportCategory]
outcome_daily_trend: list[DailyTrendItem]
top_outcome_category: MonthlyReportCategory | None
# 收入
total_income: Decimal
income_count: int
income_avg_per_transaction: Decimal
income_category_breakdown: list[MonthlyReportCategory]
income_daily_trend: list[DailyTrendItem]
top_income_category: MonthlyReportCategory | None
新字段统一加 income_ / outcome_ 前缀,避免与旧字段冲突。旧 API 的 total_outcome 等字段保持不动,保证了前端向后兼容。
前端 UI:双折线趋势图与收入饼图
趋势图的升级:
// chart_view.dart —— 从单一折线改为收支双折线
// 使用两组独立的 LineChartSeries,配合不同颜色和标签
LineChart(
seriesData: [
LineChartSeries(
data: outcomeTrend, // 红色折线 - 支出
color: Colors.red.withValues(alpha: 0.6),
),
LineChartSeries(
data: incomeTrend, // 绿色折线 - 收入
color: Colors.green.withValues(alpha: 0.6),
),
],
// 图例区分
legends: [
LegendEntry(label: "支出", color: Colors.red),
LegendEntry(label: "收入", color: Colors.green),
],
)
双折线放在同一坐标系中,用户可以直观对比某天的收支是否平衡。如果某天支出折线远高于收入折线,可能出现了大额开支。
收入分类饼图:
饼图复用已有的图表组件,但数据源切换为 MonthlyReportCategory(而非原来的 CategorySummaryItem),因为报表 API 返回的格式与首页分类不同。颜色方案选用绿色系,与支出的红色系形成心理上的对比——绿色代表"好的"、红色代表"需要关注的"。
总结
| 组件 | 变更内容 |
|---|---|
analysis_api.py |
_base_conds(direction) 工厂 + 两套 SQL 查询 |
back_end/models.py |
6 个收入相关新字段 |
| 测试 | 新增 3 笔收入账单(工资/还款/劳务) |
front_end/models.dart |
Dart 反序列化对齐新字段 |
chart_view.dart |
双折线趋势图 + 绿色收入饼图 |
这次变更的设计哲学是:一个维度的数据不足以做判断。只有支出没有收入,用户看到的是一串"花多少钱"的数字,但无法感知这些钱在自己收入中的占比。当收入饼图出现"工资收入"占比 90% 时,用户意识到自己对单一收入来源的依赖——这是数据驱动的理财意识的第一步。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)