【个人记账理财助手】手动新增账单功能

【个人记账理财助手】手动新增账单功能 手动新增账单功能项目背景这是一个个人消费分析和理财助手 APP主要能力包括账单记录、分类统计、图表分析、AI 对话以及微信/支付宝账单导入。前面已经把首页账单列表和数据分析页接到了后端这次补上一个很基础但使用频率很高的功能手动新增账单。一、为什么先做手动记账一开始做这个项目的时候我更关注“自动化”和“分析”这两个点比如账单导入、图表汇总、AI 问答这些功能看起来更完整也更像一个理财助手。但真正把 APP 跑起来之后会发现一个很实际的问题不是所有消费都会稳定地从第三方账单导入进来。比如现金支出、临时转账、银行卡消费或者用户只是想快速补一笔账这些场景都需要一个手动入口。所以这次选择先完成“手动新增账单”首页中间的加号不再是空按钮而是进入新增账单页用户可以选择支出或收入填写金额、交易对象、分类、支付方式、交易时间等信息保存后写入后端数据库返回首页后自动刷新本月汇总和账单列表。这个功能不算大但它决定了整个记账链路是不是闭环。二、整体实现思路这次改动分成前后端两部分。后端主要做一件事提供新增账单接口。前端主要做三件事新增表单页、首页加号跳转、保存成功后刷新首页数据。首页点击加号进入新增账单页填写金额、交易对象、分类、时间前端表单校验POST /bills后端创建 Bill 记录返回首页并刷新汇总我个人比较在意的一点是这个页面不能只是“能提交”还要尽量贴近日常记账的动作。用户打开页面时默认就是“支出 餐饮 微信支付 当前时间”这样大多数日常消费只需要填金额和商家就可以保存。三、后端新增账单接口后端原来已经有账单列表、月度汇总、删除、更新等接口但缺少一个真正用于手动创建账单的接口。新增的请求模型包含金额、交易对象、平台、分类、收支方向、交易时间、商品说明和备注classBillCreateRequest(BaseModel):amount:StrictStr counterparty:StrictStr platform_type:StrictStr transaction_type:StrictStr transaction_direction:StrictStr time:datetime product:str|NoneNoneremark:str|NoneNone这里金额用了字符串传递主要是为了避免前端浮点数传输时出现精度问题。后端真正入库时再统一转成Decimal。接口路径设计为POST /bills接口内部会从 token 中取当前用户 id把请求参数组装成Bill金额转成Decimal后写入数据库最后返回统一的账单列表项结构。billBill(amountDecimal(request.amount),counterpartyrequest.counterparty,productrequest.product,platform_typerequest.platform_type,transaction_typerequest.transaction_type,transaction_directionrequest.transaction_direction,timerequest.time,remarkrequest.remark,user_iduser_id,)这里有个小细节新增账单一定要绑定当前登录用户而不是让前端传user_id。这样可以避免用户伪造请求把账单写到其他人名下。四、前端新增账单页面前端新增了一个AddBillPage页面结构没有做得太复杂主要分成三块账单类型支出 / 收入基础信息金额、交易对象、分类、支付方式补充信息交易时间、商品说明、备注。1. 支出和收入分类分开刚开始我考虑过直接用一个分类列表但后面发现这样体验不好。比如“工资”明显应该属于收入“餐饮”明显应该属于支出如果混在一起后续做统计时也容易出现误选。所以前端维护了两组分类final_expenseTypesconst[餐饮,交通,购物,居住,娱乐,医疗,学习,其他];final_incomeTypesconst[工资,转账,奖金,退款,其他];切换“支出/收入”时会自动把分类切到对应列表的第一个值_transactionTypedirection收入?_incomeTypes.first:_expenseTypes.first;这个处理看起来很小但可以减少很多无效数据。比如用户从支出切到收入后分类还停留在“餐饮”这类数据后面做统计就会很奇怪。2. 表单校验放在前端先拦一层金额和交易对象是必填项。金额校验主要判断两点是否能转成数字以及是否大于 0。finalamountdouble.tryParse(text);if(amountnull||amount0)return请输入有效金额;交易对象也必须填写。因为账单列表里最先展示的就是交易对象如果这个字段为空用户回到首页看到一堆空白记录会很难受。当然前端校验只是为了提升体验后端也仍然要保留模型约束。3. 时间选择手动记账很常见的场景是“补记昨天的一笔账”所以交易时间不能只用当前时间。页面里用showDatePicker和showTimePicker组合选择时间finaldateawaitshowDatePicker(...);finaltimeawaitshowTimePicker(...);最终传给后端的是 ISO 格式时间time:time.toIso8601String()这样后端用datetime接收会比较自然后续做按月统计、按年筛选也方便。五、首页加号接入原来首页中间的 FAB 只是一个空方法onPressed:(){},这次改成跳转新增账单页面finalcreatedawaitGet.to(()constAddBillPage());保存成功时新增页面会Get.back(result:true);首页收到true之后做两件事切回首页 tab并调用HomeController.refresh()重新请求首页数据。这样用户保存完账单后不需要手动下拉刷新也不需要重启 APP本月支出和账单列表会立刻更新。六、联调过程中遇到的小问题1. 前后端端口要对齐这个项目后端默认跑在8080Android 模拟器访问宿主机需要用http://10.0.2.2:8080如果这里端口不一致前端页面是能打开的但保存账单会一直请求失败。这个问题不复杂但调试时很容易误以为是 token 或接口参数的问题。2. 保存后必须刷新首页一开始只做了新增接口和页面跳转但如果保存后首页不刷新用户会觉得“是不是没保存成功”。所以保存成功后的刷新非常关键。这个地方不是单纯的页面体验问题它也影响用户对数据可靠性的判断。3. 支出和收入不能混到一起统计之前处理过一个问题工资被算进支出统计里。做手动记账时我也特别注意了这个点新增账单必须明确带上transaction_direction。也就是说“分类”解决的是这笔账属于餐饮、交通、工资还是转账而“收支方向”解决的是它到底是支出还是收入。两个字段不能混用。七、测试与验证后端补了一个新增账单接口测试主要验证POST /bills能正常创建账单返回金额格式正确交易对象、支付平台、分类、收支方向都能正确返回数据确实写入数据库。这次跑了相关测试uv run pytest tests/test_bill_api.py tests/test_analysis_api.py结果23 passed前端也跑了静态检查flutter analyze目前剩下的是项目里原有的一些 info 级别提示比如命名风格和部分旧 API 提示没有新增错误。最后还在 Android 模拟器里走了一遍真实流程打开首页点击中间加号填写金额和交易对象点击保存返回首页后本月支出变化列表顶部出现新账单。这一步很重要因为接口测试只能证明后端能用模拟器验证才能证明用户真实路径是通的。八、这次开发后的感受手动新增账单这个功能看起来不像 AI 对话、图表分析那样“亮眼”但它其实是记账 APP 里最基础的一环。这次做完之后我对这个项目的理解也更清楚了一点一个理财助手不能只靠分析结果吸引用户它首先要让用户愿意把数据记进去。数据入口越顺后面的统计、图表、AI 问答才越有价值。后面如果继续优化我会优先考虑这几个方向新增账单后支持编辑和删除的快捷入口分类支持用户自定义根据交易对象自动推荐分类金额输入键盘做得更像记账软件比如支持简单计算首页加一个“最近常用商家”减少重复输入。这次功能的代码量不算小但核心逻辑并不复杂。真正需要注意的是数据字段要清晰尤其是金额、分类、收支方向、时间这几个字段。只要这些基础数据稳定后面的消费分析才不会跑偏。九、总结这次完成的是个人消费分析 APP 的手动记账闭环后端新增POST /bills接口前端新增AddBillPage首页加号接入新增页面保存成功后自动刷新首页数据补充了后端接口测试在 Android 模拟器里完成了真实流程验证。从结果来看这个功能让 APP 从“能展示账单”往“能主动记录账单”迈了一步。虽然只是一个基础功能但它对后续的数据分析、图表统计和 AI 财务问答都有直接帮助。