☰
全栈开发个人记账系统:技术选型与实战经验
2026/10/14 9:09:36 网站建设 项目流程

1. 项目背景与核心价值

去年我决定彻底解决自己的财务糊涂账问题。试用了市面上十几款记账软件后,发现要么功能太复杂,要么数据隐私存疑,要么无法跨设备同步。作为一个全栈开发者,我意识到完全可以打造一个完全符合自己需求的记账系统——既能通过网页端详细管理数据,又能用手机小程序随时记录开销。

这个项目的核心价值在于:

  • 完全掌控数据所有权(所有记录保存在自己的服务器)
  • 深度定制分类体系和报表功能
  • 实现"电脑记账+手机速记"的无缝体验
  • 开发成本可控(主要技术栈都是开源方案)

经过三个月的迭代,目前系统已稳定运行半年,日均记账50+笔。下面就把从技术选型到部署上线的完整过程拆解给大家,特别会重点分享那些官方文档里不会写的实战经验。

2. 技术架构设计

2.1 整体技术栈选择

采用前后端分离架构,主要组件如下:

模块技术方案选型理由
前端Vue3 + Vant组件丰富,适合快速开发管理后台和移动端H5
小程序Uni-app一套代码同时生成微信/支付宝小程序,维护成本低
后端NestJS模块化架构适合复杂业务逻辑,TypeScript支持完善
数据库PostgreSQL财务数据需要严格的事务支持,JSONB类型便于存储动态字段
部署Docker + Nginx容器化部署方便迁移,Nginx处理静态资源和反向代理
数据分析ECharts强大的可视化库,支持自定义报表
同步方案WebSocket + 增量同步协议解决多端数据一致性问题

避坑提示:初期尝试用MongoDB存储交易记录,但在复杂聚合查询时性能较差,特别是涉及多条件统计时。财务系统强烈建议使用关系型数据库。

2.2 关键业务流程设计

记账系统的核心流程需要特别关注数据一致性和操作便捷性:

  1. 记账流程:

    • 小程序端:极简输入(金额+分类+备注)
    • 网页端:完整表单(含票据图片上传、多账户转账等)
    • 统一通过WebSocket实时同步到服务端
  2. 数据同步机制:

    // 增量同步协议示例 interface SyncPacket { clientId: string; lastSyncTime: number; operations: Array<{ type: 'CREATE' | 'UPDATE' | 'DELETE'; table: 'transactions' | 'categories'; data: Record<string, any>; }>; }
  3. 对账逻辑:

    • 每日自动生成账户余额快照
    • 提供余额差异检测功能
    • 支持手动调整记录(保留审计日志)

3. 核心功能实现细节

3.1 多端统一的账目模型

设计良好的数据模型是系统的基石。我的账目模型包含以下关键字段:

interface Transaction { id: string; // 雪花ID amount: number; // 支持正负值 currency: string; // 默认CNY type: 'expense' | 'income' | 'transfer'; category: { // 分类体系 id: string; name: string; icon: string; }; account: string; // 关联账户ID targetAccount?: string; // 转账专用 occurTime: Date; // 实际发生时间 createTime: Date; // 记录创建时间 tags?: string[]; // 自定义标签 images?: string[]; // 票据图片URL remark?: string; // 备注文本 }

分类体系设计技巧:

  • 采用三级分类(大类-中类-小类)
  • 支持用户自定义图标和颜色
  • 内置常用分类模板(可导入)
  • 通过material-design-icons库获得丰富的图标选择

3.2 实时同步的实现

多端数据同步是体验的关键,我的解决方案是:

  1. 客户端:

    • 本地维护一个SQLite数据库
    • 所有操作先在本地执行
    • 通过队列异步同步到服务端
  2. 服务端:

    // WebSocket网关实现 @WebSocketGateway() export class SyncGateway { @WebSocketServer() server: Server; @SubscribeMessage('sync') handleSync(client: Socket, payload: SyncPacket): SyncResult { // 处理冲突解决逻辑 return this.syncService.process(payload); } }
  3. 冲突解决策略:

    • 最后修改时间优先
    • 保留冲突记录供人工处理
    • 重要操作(如删除)需要二次确认

3.3 数据分析模块

报表功能采用按需计算方案:

  1. 预聚合设计:

    CREATE MATERIALIZED VIEW daily_stats AS SELECT date_trunc('day', occur_time) AS day, type, category->>'id' AS category_id, SUM(amount) AS total FROM transactions GROUP BY day, type, category_id;
  2. 动态查询接口:

    @Get('report') async getReport( @Query('start') start: string, @Query('end') end: string, @Query('dimensions') dimensions: string[] ) { return this.reportService.build({ range: [new Date(start), new Date(end)], dimensions: dimensions.split(',') }); }
  3. 可视化优化:

    • 使用ECharts的dataset特性实现动态数据绑定
    • 移动端采用简化版图表
    • 支持报表模板保存

4. 部署与运维实战

4.1 服务器配置建议

我的生产环境配置(月均5000+条记录):

资源规格说明
云服务器2核4G突发性能实例足够应对个人使用
数据库PostgreSQL 14单独实例,1核2G配置
对象存储50GB存储票据图片
带宽5Mbps主要服务API和小程序资源

4.2 Docker部署方案

完整的docker-compose.yml配置:

version: '3' services: app: build: . ports: - "3000:3000" depends_on: - db environment: DATABASE_URL: postgres://user:pass@db:5432/ledger restart: unless-stopped db: image: postgres:14 volumes: - pg_data:/var/lib/postgresql/data environment: POSTGRES_PASSWORD: pass restart: unless-stopped nginx: image: nginx:alpine ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./dist:/usr/share/nginx/html depends_on: - app volumes: pg_data:

重要安全提示:务必配置数据库定期备份!我使用pg_dump每天凌晨3点全量备份到对象存储。

4.3 性能优化技巧

  1. 数据库层面:

    • 为occur_time字段创建BRIN索引
    • 对常用查询条件建立复合索引
    • 定期执行VACUUM ANALYZE
  2. API层面:

    • 使用Redis缓存热点数据
    • 实现ETag缓存策略
    • 对大数据量查询实现分页
  3. 前端层面:

    • 小程序采用分包加载
    • 使用virtual-list优化长列表
    • 实现骨架屏提升体验

5. 典型问题排查指南

5.1 同步冲突处理

现象:同一笔记录在不同设备显示不一致

排查步骤:

  1. 检查客户端和服务端的lastSyncTime
  2. 查看操作日志中的冲突记录
  3. 比对各端的操作序列

解决方案:

// 冲突解决算法示例 function resolveConflict(client, server) { if (client.version > server.version) { return { ...client, status: 'merged' }; } else if (server.version > client.version) { return { ...server, status: 'override' }; } else { // 保留双方记录并标记冲突 return [ { ...client, status: 'conflict' }, { ...server, status: 'conflict' } ]; } }

5.2 报表数据不准

常见原因:

  • 时区设置不一致
  • 未刷新的物化视图
  • 转账记录重复计算

修复方案:

  1. 统一使用UTC时间存储
  2. 设置物化视图自动刷新
  3. 转账统计时排除内部交易

5.3 小程序审核问题

经验总结:

  • 避免使用"记账"等金融相关关键词
  • 个人版不要提供多用户功能
  • 数据导出格式需符合平台规范
  • 隐私协议必须明确数据存储位置

6. 扩展与个性化方案

6.1 自动化记账

通过以下方式减少手动输入:

  • 邮件账单解析(配置转发规则)
  • 短信消费提醒抓取(Android可用Tasker)
  • 支付宝/微信账单导入
# 账单解析示例(支付宝格式) def parse_alipay_bill(file): df = pd.read_csv(file, skiprows=3) return [{ 'amount': float(row['金额']), 'type': 'income' if row['收/支'] == '收入' else 'expense', 'occurTime': datetime.strptime(row['交易时间'], '%Y-%m-%d %H:%M:%S'), 'remark': row['商品说明'] } for _, row in df.iterrows()]

6.2 多币种支持

实现方案:

  1. 存储交易时的实时汇率
  2. 提供汇率手动维护界面
  3. 报表支持按本币/原币展示
-- 汇率表设计 CREATE TABLE exchange_rates ( from_currency CHAR(3), to_currency CHAR(3), rate DECIMAL(10,6), date DATE, PRIMARY KEY (from_currency, to_currency, date) );

6.3 预算功能

核心逻辑:

  • 按周期(周/月/年)设置预算
  • 支持分类预算和总预算
  • 提供超额预警
// 预算检查中间件 @Injectable() export class BudgetAlertMiddleware implements NestMiddleware { async use(req: Request, res: Response, next: NextFunction) { const transaction = req.body; const budgets = await this.budgetService.getActiveBudgets(); budgets.forEach(budget => { if (budget.isOver(transaction)) { this.notificationService.sendAlert(budget); } }); next(); } }

这个项目给我最大的收获是:好的个人工具必须平衡功能的完整性和使用的便捷性。现在我的记账系统已经能自动处理70%的日常记录,每月对账时间从原来的3小时缩短到20分钟。如果你也想打造自己的记账系统,建议先从最小可用版本开始,逐步迭代完善。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询