福瑞记账(Campus Ledger)—— 面向大学生的个人记账系统
面向在校大学生的记账系统:手动记账、微信 / 支付宝账单文件导入(自动解析、去重、分类推荐)、收支统计、预算管理,以及 8 项基于本地规则的智能分析。
角色 独立开发(需求 / 设计文档 / 后端 / 前端 / 测试 / 部署)
面向在校大学生的个人记账系统:支持手动记账、微信 / 支付宝官方账单文件导入(自动解析、去重、分类推荐)、收支统计、预算管理,以及 Android 通知栏常驻看板(今日支出 / 本月预算 / 结余)。
技术栈为 Spring Boot 3.2.5 + MyBatis-Plus + MySQL 8 + Flutter(Web / Android),已用 Docker Compose 部署上线。所有智能能力均为本地规则 + 统计实现,不接入大模型、不产生调用费用。
大学生的生活费来源固定(家庭转账、奖助学金、兼职),支出集中在餐饮、交通、购物、娱乐、学习几类,特点是单笔金额小、笔数多,且几乎全部通过微信 / 支付宝支付。
手工记账难坚持;而移动支付账单虽然记录完整,却看不出「钱花在哪了」,也没有预算执行的概念。
第三方记账 App 需要上传账号或导入数据,隐私与合规风险高。
记账省事:把用户自己导出的官方账单文件导入系统,自动解析成记账记录(自动判断收支、识别金额与交易号、推荐分类、过滤重复)。
看清去向:月度收支结余、分类占比、每日趋势、支付来源构成,以及月度总预算与分类预算的执行情况。
合规与自主:不登录用户微信 / 支付宝账号,不使用私有接口、Hook、逆向或模拟登录,只解析用户自己导出的账单文件;智能分析全部本地实现,不依赖外部服务。
导入环节做成可容错的解析器:先探测文件编码(UTF-8 与 GBK / GB18030),再按表头列名映射字段而不是依赖固定列号,脏数据与异常文件返回中文提示而不是 500。
去重做三层:优先使用原始交易号,无交易号时用组合指纹,最后用数据库唯一键 (user_id, dedup_key) 兜底;预览页默认不勾选重复记录。
智能分析全部用本地规则 + 用户历史 + 统计阈值实现,每条结论都带依据与置信度;数据不足时返回明确状态(NO_DATA / INSUFFICIENT_DATA / NOT_APPLICABLE),不编造结论。
Flutter Web 客户端(登录 / 注册 · 首页 · 账单 · 导入 · 统计 · 预算 · 我的)→ HTTP + JSON(金额为字符串)与 multipart/form-data(账单文件)→ Spring Boot 后端(单模块,Controller → Service → Mapper 分层,MyBatis-Plus 数据访问);鉴权由 AuthInterceptor 解析 JWT 得到 CurrentUser,所有查询按当前用户隔离;导入由 BillImportService + 文件解析器 + 分类匹配器组成;MySQL 8.0 共 4 张表(user · bill · budget · import_batch)。
编码自动探测、按表头列名映射(不依赖固定列号)、脏数据容错;异常文件(空文件 / 错误表头 / 损坏 / 商户版账单)全部返回中文提示而不是 500。
优先原始交易号,无交易号时用组合指纹,数据库唯一键 (user_id, dedup_key) 兜底,重复导入不会产生重复账单。
全部基于规则 + 用户历史 + 统计阈值,结论可解释、零调用成本、无隐私外发;每条结论都带依据与置信度。
每个智能能力都有明确状态(OK / NO_DATA / INSUFFICIENT_DATA / NOT_APPLICABLE),样本不足或覆盖率不足时如实返回。
用测试断言「整个预测 / 分析只执行一次聚合查询」(verify(times(1)))并校验取数窗口,主动防止 N+1 与全表扫描。
type=3(转账 / 提现 / 还款等不计收支)在所有统计、预算与智能分析中统一排除,避免口径不一致导致的结论矛盾。
明确不登录用户支付账号、不用私有接口 / Hook / 逆向,只解析用户自己导出的官方账单文件。
通知栏看板用 Flutter ↔ Kotlin MethodChannel 打通,刷新由原生侧独立完成(自己持有 token 与接口地址),不依赖 Flutter 引擎存活;登录后自动恢复、退出登录自动收起、记账后立即刷新。
智能分析实现方式
选了 本地规则 + 统计阈值,而不是 调用大模型分析消费数据 —— 结论可解释、零调用成本、无隐私外发。
代价:分析能力受规则覆盖范围限制,复杂语义场景无法处理。
账单获取方式
选了 解析用户导出的官方账单文件,而不是 登录支付账号或用私有接口抓取 —— 不接触账号凭据、不使用逆向手段,合规风险最低。
代价:用户需要自己先导出一份账单文件。
Android 后台刷新机制
选了 AlarmManager 定时触发,而不是 前台服务常驻刷新 —— 前台服务在较新系统上有每日时长限制,长期常驻不可靠。
代价:重启后闹钟会丢失,需要额外处理开机恢复。
后端测试:mvn clean test 397 个用例,Failures 0 / Errors 0 / Skipped 0(本次直接解析 surefire-reports 下 22 个测试类报告核对一致)。
前端测试:flutter analyze → No issues found(本次实测);flutter test → 302 passed / 88 skipped / 0 failed(后端未启动时联调用例自动跳过,属正常降级)。
构建产物:后端 jar 45.2 MB、Flutter Web 构建产物、Android release APK。
代码规模:后端 128 个 Java 文件(含 22 个测试类)、前端 65 个 Dart 文件(含 31 个测试文件)+ 8 个 Kotlin 原生文件(通知栏看板)、数据库 4 张表。
线上部署(本次实测):https://ledger.myiskg.com/ 的 /api/health 返回 200、首页 200(自 2026-09-24 起启用 HTTPS);Docker Compose 编排 MySQL + 后端 + Nginx 三个容器。部署沿革:原阿里云实例 8.138.161.154:8090 已于 2026-09-20 停用,现全部指向腾讯云实例。
验收记录:外网登录、账单 / 统计 / 预算 / 导入历史读取、multipart 上传导入、越权 404 均通过。
合规要求应该当成设计约束写进需求:只解析用户导出的官方文件,让整个导入方案在实现前就避开了账号与私有接口的风险。
数据不足时给状态比给结论更专业:把 NO_DATA / INSUFFICIENT_DATA 做成明确返回值,比强行给出分析结果更可信。
平台限制会推动更好的技术选择:因为前台服务有时长限制而改用系统闹钟,反而减少了一项敏感权限。





