开发日志(四):从菜单识别到用户系统、历史记录与购物车持久化
最近这段时间我继续完善了自己的AI 菜单识别与点单翻译系统。这个项目最初的目标是让用户上传菜单图片后系统能够自动识别菜单内容并输出结构化结果例如菜品原文、中文译名、描述、价格和标签等信息。但随着开发深入我逐渐意识到如果只是做一个“识别结果展示页”这个项目更像是一个 Demo如果想让它真正接近一个完整应用就必须继续把用户系统、历史记录、菜品详情、图片生成、购物车等功能都补齐。这篇文章就记录一下这次开发过程中我到底实现了什么、踩了哪些坑又是怎么一步步把系统跑通的。项目目标从“单次识别”走向“完整应用”一开始项目已经具备了基本的菜单识别流程前端 Flutter 上传菜单图片后端 FastAPI 接收图片调用模型解析菜单内容返回结构化菜品列表但如果想进一步提升可用性一个更完整的系统至少还应该具备以下能力用户注册与登录JWT 身份认证识别历史保存与查询菜品详情分析菜品图片生成购物车持久化存储于是这一轮开发的重点就变成了把这个项目从一个识别工具逐渐扩展成一个真正有状态的全栈应用。一、先把用户系统搭起来1. 用户注册与登录这一部分后端基于FastAPI SQLAlchemy MySQL实现主要新增了两个接口POST /registerPOST /login登录成功后后端返回 JWT token前端 Flutter 将 token 存储在本地后续访问需要认证的接口时统一携带Authorization: Bearer token这样后续历史记录、上传菜单、图片生成、购物车等功能都可以和当前用户绑定起来。2. 第一个坑passlib 和 bcrypt 版本不兼容用户注册最开始并没有顺利跑通。后端一开始一直报错日志关键信息AttributeError: module bcrypt has no attribute __about__ ValueError: password cannot be longer than 72 bytes表面看是密码长度问题实际是版本兼容性问题。解决方法固定依赖版本pipinstallpasslib1.7.4bcrypt4.0.1修复后注册接口返回 200用户数据成功入库。心得后端报错不一定是业务逻辑错误很多时候是环境依赖问题。3. 第二个坑登录 422不是密码错而是请求格式不对注册成功后登录接口报错422 Unprocessable Content前端最初使用 JSON 请求体headers:{Content-Type:application/json},body:jsonEncode({username:username,password:password,}),但 FastAPI 的OAuth2PasswordRequestForm要求application/x-www-form-urlencoded格式表单字段username和password解决方法前端改为表单提交登录成功打通。心得422 错误大多是前后端接口协议不匹配而非账号密码错误。下面是运行展示二、识别结果不能只停留在页面上加入历史记录持久化用户系统打通后核心功能是识别历史保存让用户可追溯操作记录。1. 新增recognition_history表字段设计iduser_idimage_filenameocr_textparsed_items存储菜品 JSON 数据created_at2. 自动保存历史用户上传图片识别成功后后端同步将结果写入数据库识别操作不再是一次性展示。3. 新增历史查询接口GET /history接口根据用户 token 仅返回自身历史按时间倒序排列。前端 Flutter 新增历史页面展示图片名称、OCR 文本、解析结果、创建时间。至此项目从 Demo 升级为具备用户记忆的应用。三、上传接口又踩坑为什么/upload一直 401现象GET /history正常返回 200POST /upload一直 401 Unauthorized问题根源前端使用MultipartRequest上传图片未携带 Authorization 请求头。解决方法上传时手动添加 tokenrequest.headers[Authorization]Bearer$token;心得文件上传等特殊请求不会自动继承认证逻辑需单独配置请求头。四、购物车功能从内存存储到数据库持久化最初购物车是 Flutter 本地内存集合重启应用数据丢失因此改造为数据库持久化。1. 后端新增CartItem模型对应数据库表cart_items字段id、user_id外键关联用户name_original、name_zh、description、price、tagscreated_at新增购物车 CRUD 接口GET /cart # 查询购物车 POST /cart # 添加商品 DELETE /cart/{item_id} # 删除商品2. 前端同步改造menu_item.dart增加id字段auth_service.dart新增购物车接口方法页面逻辑改为与后端数据库同步数据3. 又一个坑数据库未建表测试接口报错Table menu_app.cart_items doesnt exist问题代码定义了模型但数据库未同步表结构。解决方法确认CartItem模型定义正确执行Base.metadata.create_all(bindengine)自动建表或手动执行 SQL 建表语句六、前端细节优化1. 修复 Flutter 空值问题报错Unexpected null value原因Dart 空安全下强制解包空字段。优化方案避免对空字段使用!强制解包JSON 解析增加兜底逻辑时间字段安全解析2. 统一前后端字段契约这轮开发最大的感悟80% 的bug源于前后端字段不统一。易出错场景登录请求是 JSON 还是表单上传/生成图片是否携带 token接口入参字段名、必填项购物车去重规则不统一会直接导致401/404/422/400/500 错误。购物车效果展示七、本轮开发成果总结后端能力✅ 用户注册/登录 JWT 鉴权✅ 菜单识别历史持久化 查询✅ 菜品图片生成接口联调✅ 购物车完整 CRUD 接口✅ SQLAlchemy MySQL 数据持久化前端能力✅ 登录注册页面 Token 本地存储✅ 菜单上传识别 历史记录展示✅ 菜品详情页 图片生成请求✅ 持久化购物车页面项目已从识别 Demo升级为完整全栈应用骨架。八、开发核心收获不再只关注单点功能如菜单识别而是建立系统级开发思维接口路由是否完整身份认证是否统一请求格式与字段是否匹配数据库结构是否同步前端空值保护与异常处理用户行为数据是否可追溯这些细节的衔接才是完整项目的核心难点。九、下一步计划优化购物车功能增加数量管理、总价统计、收藏夹完善异常提示前端展示友好化提示替代原始报错优化 UI/UX提升应用美观度与用户体验打造可展示的成品