1. 项目背景与需求分析
记账这件事,每个家庭都逃不掉。从菜市场的几块钱到房贷车贷的大额支出,钱就像沙子一样从指缝间溜走。我见过太多朋友月初信誓旦旦要记账,结果月底发现Excel里只记了三天的开销。这就是为什么我们需要一个专门的家庭记账本——不是冷冰冰的财务软件,而是能真正融入日常生活的工具。
Python作为这个项目的技术选择再合适不过。它就像瑞士军刀,从数据处理到界面开发都能搞定。更重要的是,Python有大量现成的库可以直接调用,这对毕业设计来说意味着不用重复造轮子。我在大三时就用Python+Django做过一个简易记账本,当时最大的教训就是太注重功能堆砌,反而忽略了核心的记账体验。
家庭记账本的核心需求其实很明确:
- 记录收支(这是基本功)
- 分类统计(知道钱花哪儿了)
- 数据可视化(直观看到消费趋势)
- 多用户支持(全家一起记账)
- 数据备份(别辛辛苦苦记了半年,电脑一坏全没了)
但毕业设计不能只做这些基础功能。根据我带过5届毕业生的经验,评委最看重的是:
- 有没有解决真实痛点(比如自动识别消费类别)
- 技术方案是否合理(别用大炮打蚊子)
- 代码质量如何(能不能经得起答辩拷问)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么选择这些技术栈
后端用Flask而不是Django是有讲究的。Django确实更全面,但家庭记账本不需要那么重的框架。Flask轻量灵活,正好符合"够用就好"的原则。数据库选SQLite也是同理——单文件、零配置,特别适合这种小型应用。
前端我建议用Bootstrap+ECharts的组合。Bootstrap能让界面快速成型,ECharts则是数据可视化的利器。记住,毕业设计的前端不需要多么炫酷,关键是清晰展示数据关系。
技术栈清单:
- 后端:Python 3.8+Flask 2.0
- 数据库:SQLite(开发环境)+MySQL(生产环境方案)
- 前端:HTML5+Bootstrap 5+ECharts 5
- 辅助工具:Pandas(数据分析)、Faker(测试数据生成)
2.2 数据库设计要点
家庭记账的数据库模型看似简单,但有几个坑我当年就踩过:
python复制class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), unique=True) # 比如"餐饮""交通"
type = db.Column(db.String(4)) # "收入"或"支出"
class Record(db.Model):
id = db.Column(db.Integer, primary_key=True)
amount = db.Column(db.Float, nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
timestamp = db.Column(db.DateTime, default=datetime.now)
remark = db.Column(db.Text) # 备注信息
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
特别注意:
- 金额字段一定要用Decimal类型,Float会丢失精度(我吃过亏)
- 时间戳要存时区信息,否则跨时区用户会出问题
- 类别表要设计成树形结构,支持二级分类(如"餐饮-外卖")
2.3 项目结构规范
好的项目结构能让代码维护性提升十倍:
code复制/family_finance
/app
/static # 静态文件
/templates # Jinja2模板
/models.py # 数据模型
/routes.py # 路由控制
/forms.py # 表单处理
/migrations # 数据库迁移脚本
/tests # 单元测试
config.py # 配置文件
requirements.txt # 依赖清单
这个结构参考了Flask官方推荐的最佳实践。特别提醒:一定要写单元测试!这是很多毕业设计的扣分项。至少覆盖核心业务逻辑的70%。
3. 核心功能实现细节
3.1 记账功能实现
记账的核心逻辑在routes.py里:
python复制@app.route('/add_record', methods=['POST'])
@login_required
def add_record():
form = RecordForm()
if form.validate_on_submit():
try:
record = Record(
amount=float(form.amount.data),
category_id=int(form.category.data),
remark=form.remark.data.strip(),
user_id=current_user.id
)
db.session.add(record)
db.session.commit()
flash('记录添加成功', 'success')
except ValueError:
flash('金额格式错误', 'danger')
return redirect(url_for('index'))
几个关键点:
- 一定要做表单验证(Flask-WTF很方便)
- 数据库操作要用try-catch包裹
- 金额转换要处理异常情况
- 记得加@login_required装饰器保护路由
3.2 智能分类功能
这是能拿高分的亮点功能。基本原理是利用文本分析自动匹配类别:
python复制import jieba
from collections import defaultdict
category_keywords = {
"餐饮": ["饭店", "外卖", "奶茶", "餐厅"],
"交通": ["地铁", "打车", "公交", "油费"]
}
def auto_category(remark):
words = jieba.lcut(remark)
scores = defaultdict(int)
for word in words:
for cat, keywords in category_keywords.items():
if word in keywords:
scores[cat] += 1
return max(scores.items(), key=lambda x: x[1])[0] if scores else None
进阶建议:
- 可以加入机器学习(比如朴素贝叶斯)
- 让系统学习用户的手动调整
- 对高频错误匹配进行人工干预
3.3 数据可视化实现
用ECharts生成消费趋势图:
javascript复制// 在模板中这样调用
<script>
var chart = echarts.init(document.getElementById('chart'));
chart.setOption({
tooltip: {},
xAxis: {
data: {{ dates|tojson }}
},
yAxis: {},
series: [{
name: '支出',
type: 'line',
data: {{ amounts|tojson }}
}]
});
</script>
后端需要准备数据:
python复制@app.route('/get_chart_data')
@login_required
def get_chart_data():
last_30_days = db.session.query(
func.date(Record.timestamp).label('date'),
func.sum(Record.amount).label('total')
).filter(
Record.user_id == current_user.id,
Record.timestamp >= datetime.now() - timedelta(days=30)
).group_by('date').all()
dates = [d.date.strftime('%m-%d') for d in last_30_days]
amounts = [float(d.total) for d in last_30_days]
return jsonify(dates=dates, amounts=amounts)
4. 毕业设计加分项实现
4.1 多用户家庭账本
实现家庭成员共享账本:
python复制class Family(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
members = db.relationship('User', secondary='family_members')
family_members = db.Table('family_members',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('family_id', db.Integer, db.ForeignKey('family.id'))
)
功能要点:
- 邀请链接生成(用itsdangerous生成token)
- 权限控制(父母vs孩子的记账权限不同)
- 家庭消费报表(按成员统计)
4.2 预算预警功能
当某类消费接近预算时发送提醒:
python复制def check_budget(user_id):
budgets = Budget.query.filter_by(user_id=user_id).all()
for budget in budgets:
current = db.session.query(func.sum(Record.amount)).filter(
Record.user_id == user_id,
Record.category_id == budget.category_id,
Record.timestamp >= datetime.now().replace(day=1)
).scalar() or 0
if current >= budget.amount * 0.9:
send_alert_email(
to=current_user.email,
category=budget.category.name,
used=current,
budget=budget.amount
)
4.3 数据导出与备份
实现Excel导出功能:
python复制from openpyxl import Workbook
from io import BytesIO
@app.route('/export')
@login_required
def export():
records = Record.query.filter_by(user_id=current_user.id).all()
wb = Workbook()
ws = wb.active
ws.append(['日期', '类别', '金额', '备注'])
for r in records:
ws.append([
r.timestamp.strftime('%Y-%m-%d'),
r.category.name,
r.amount,
r.remark
])
buffer = BytesIO()
wb.save(buffer)
buffer.seek(0)
return send_file(
buffer,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
download_name='家庭账本导出.xlsx'
)
5. 答辩准备与代码优化
5.1 性能优化技巧
数据库查询是最容易出性能问题的地方:
python复制# 错误示范 - N+1查询问题
records = Record.query.filter_by(user_id=current_user.id).all()
categories = [r.category.name for r in records] # 每次循环都查数据库
# 正确做法 - 使用join预加载
records = Record.query.options(db.joinedload(Record.category))\
.filter_by(user_id=current_user.id).all()
其他优化点:
- 分页查询(不要一次性加载全部记录)
- 缓存常用数据(如类别列表)
- 定期归档历史数据
5.2 安全防护措施
毕业设计常被忽视的安全问题:
python复制# 密码存储
from werkzeug.security import generate_password_hash
user.password = generate_password_hash(password)
# CSRF防护
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)
# SQL注入防护
# 永远不要这样做:
query = f"SELECT * FROM user WHERE name = '{name}'"
# 应该用ORM或者参数化查询
User.query.filter_by(name=name).first()
5.3 答辩常见问题准备
根据我参与答辩的经验,评委最爱问:
- 为什么选择这个技术栈?(要能说出比较优势)
- 数据库设计是如何考虑的?(范式与反范式的权衡)
- 如果用户量增加到10万,系统要怎么扩展?
- 项目中遇到的最大挑战是什么?
建议准备:
- 架构图(用draw.io画)
- 性能测试数据(用locust压测)
- 代码覆盖率报告(pytest-cov)
