免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

苍穹外卖实战笔记:Spring Boot + MyBatis + Redis完整项目开发

苍穹外卖实战笔记:Spring Boot + MyBatis + Redis完整项目开发 简介面向JavaWeb初学者的苍穹外卖项目完整学习包内含作者历时一个月开发的在线订餐系统源码与配套笔记。项目实践了Servlet/JSP、MVC分层、Spring依赖注入、MyBatis持久化、数据库表设计与JOIN查询、前端Bootstrap交互、文件上传、Session权限控制、SQL注入防御、JUnit测试及Tomcat部署等关键技术能帮助学习者串联JavaWeb全栈开发流程。压缩包共2000个文件以java源码、xml配置、class编译产物和yml配置为主并有md学习笔记和xlsx数据结构表整体13.61MB目录结构符合Maven规范便于按模块阅读。目前已有16320人浏览学习。通过源码与笔记对照可快速理解外卖业务从用户点餐到订单管理、菜品图片上传等完整实现细节适合作为毕业设计或进阶练习。 第一次完整地把苍穹外卖跑通的时候我盯着控制台里“Started Application”那行日志看了很久。一个月的每天晚上和周末断断续续地写接口、调联调、改Bug终于把这套外卖系统完整落地了。刚开始学Java后端的时候我其实挺迷茫的SSM学了、Spring Boot也会写个简单CRUD但真正面对一个有用户端、管理端、订单流转、支付回调的项目时还是有点手足无措。这也正是我决定把苍穹外卖彻底写一遍的原因——它不是什么“玩具项目”而是一套接近于真实商业场景的完整系统。这篇博文就把我这一个月的实战笔记和完整代码分享出来包括我梳理清楚的技术架构、每一步的实现逻辑以及那些调试到半夜才解决的坑。如果你正在学Spring Boot MyBatis Redis这套组合或者准备做一个完整项目来充实简历这套代码和笔记应该能帮你省下不少摸索的时间。全文干货可以直接照着自己敲一遍。1. 苍穹外卖这个项目到底练了什么1.1 从“会写接口”到“会做系统”的跨越我见过很多人学Spring Boot的方式是照着教程写一个“增删改查”的Demo然后觉得自己已经会了。但要你独立实现一个带订单状态流转、购物车、地址簿、来单语音提醒的外卖系统时才会发现真正的复杂度不在某个单一接口而在不同模块之间的数据一致性和流程衔接。苍穹外卖的核心业务其实和美团外卖、饿了么很相似用户在微信小程序上下单商家在管理后台接单、派单、统计营收。作为学习者我最看重的就是它完整覆盖了下面这几个场景用户端微信登录、浏览菜品、购物车、下单支付、订单查询、历史订单管理端员工登录、菜品管理、分类管理、套餐管理、订单管理、数据统计共性支撑JWT身份认证、Redis缓存、定时任务、WebSocket即时推送、微信支付模拟每个模块单独拎出来都不算难但要把它们组装成一个能跑通的完整系统牵扯到的知识面一下子就宽了。这恰恰是苍穹外卖最有价值的地方——它逼着你去思考模块之间的关系而不是只做一个孤立的功能点。1.2 一个月的时间节奏我是怎么安排的写这个项目之前我的Java基础已经比较扎实Spring Boot基本用法也没问题但缓存、消息推送、定时任务这些都是零基础。整个开发周期大致分成了四个阶段第1周梳理需求文档和数据库设计完成员工端登录、JWT拦截器、通用返回结果封装第2周完成分类、菜品、套餐的增删改查练习MyBatis动态SQL和文件上传第3周集中攻坚用户端最核心的交易链路包括购物车、下单、支付模拟、订单状态流转第4周做数据统计报表、WebSocket来单提醒、清理Bug和优化代码结构这个节奏下来平均每天投入三到四个小时周末会多一些。如果你白天上班或者课比较多两周完成核心功能也是可行的前提是不要把时间浪费在无意义的返工上比如数据库字段没设计好导致后面疯狂改表。2. 技术选型的逻辑和整体架构思路2.1 为什么选了这套技术组合苍穹外卖的主流技术栈是Spring Boot作为基础框架、MyBatis负责持久层、MySQL存数据、Redis做缓存前端用微信小程序配合管理后台的Vue页面。这套组合并不是随便选的它基本上就是当前中小型互联网公司后端团队最常用的标配。Spring Boot的好处大家都知道自动配置、内嵌Tomcat、生态成熟但它真正让你省心的是大量约定优于配置的设计能让你把注意力集中在业务实现而不是环境搭建上。MyBatis作为持久层框架轻量灵活SQL由自己掌控面对外卖这种查询条件复杂、关联表很多的场景非常好调整。Redis的应用放在这里也很典型菜品缓存、购物车数据、微信登录Token的临时存储这些都是高并发场景下绕不开的需求。WebSocket和定时任务的引入是我觉得这个项目比较有良心的设计。实际外卖系统里用户下单后商家需要实时听到语音提醒这就要靠WebSocket推送店铺打烊后系统要自动清理过期订单这就要靠Spring自带的定时任务。这些功能往简历上一写含金量立刻不一样。2.2 项目分层与目录结构设计拿到项目需求后我没有急着去写代码而是先把目录结构定下来。我采用的是前后端分离的标准分层架构后端代码按controller - service - mapper - entity/dto/vo的层次组织。controller层负责接收请求、参数校验、调用service不关心具体业务逻辑service层核心业务逻辑都在这一层包括事务控制mapper层只负责数据库交互SQL写在XML里便于后期优化entity数据库表对应的实体类dto接收前端传入的参数对象避免直接拿实体类去接参数vo返回给前端的数据对象根据页面需要灵活组装这种分层方式在项目初期看起来很“多此一举”多写了很多VO和DTO类。但当你做到订单管理模块的时候就会发现如果没有这些层隔离一个接口可能牵扯到六七张表的数据代码会变成一团乱麻。3. 核心功能模块的实现细节3.1 员工端身份认证与拦截器设计管理员登录这里用的是JWTJSON Web Token方案。用户输入账号密码后后端校验通过会生成一个有效期数小时的Token返回给前端之后前端每次请求都在请求头带上这个Token后端通过拦截器统一校验。生成Token的逻辑主要是用jjwt库把员工ID和用户名放入claims用密钥签名生成字符串。关键点在于密钥一定要放在配置文件里统一管理不要硬编码在代码中否则项目代码一旦泄露攻击者就可以随意伪造身份。拦截器这里有个细节值得注意需要放行登录接口本身不然会出现还没登录就被拦下来要求登录的死循环。我在WebMvc配置类里做了路径白名单配置比如/admin/employee/login、/user/user/login这些接口需要跳过Token校验其余接口全部拦截。这里顺便把CORS跨域配置也一起处理了前后端分离开发时跨域问题避免不了。同时我还封装了一个BaseContext工具类用ThreadLocal存储当前登录用户ID。这样在Service层随时都能获取是谁在操作做数据审计或者逻辑判断都很方便。用完之后一定要记得调用remove()方法清理否则Tomcat线程池复用的时候会出现用户数据串号的问题。这个坑我实际遇到过排查了很久才定位到。3.2 菜品与分类管理以及文件上传处理逻辑分类管理相对简单就是一个普通的CRUD但需要注意分类被菜品关联后能不能删除的逻辑判断。我通过查询菜品表来判断当前分类下是否已有菜品如果存在就抛出业务异常提示“当前分类下有菜品不能删除”。这种业务约束必须在后端做不能只靠前端控制。菜品管理涉及多表关联一个菜品对应多个口味实际表结构是菜品表和口味表分开存储。新增菜品时需要在事务内同时插入菜品主表和口味明细表删除菜品时也要把对应的口味数据一并删除。事务注解Transactional在Service方法上一定要加否则中途失败会留下脏数据。文件上传这个功能我一开始以为要接阿里云OSS后来发现项目里用本地存储就够了。前端把图片传到后端后端用UUID生成新文件名按照日期分目录存储到磁盘上再把访问路径返回给前端。真正部署的时候还要做静态资源映射让外部能够访问到这些图片。如果你以后开发真实项目建议直接换成对象存储本地存储方式只适合学习和中小型内网项目。3.3 用户下单闭环购物车、订单与缓存联动用户端最核心的一条链路是用户加购 - 提交订单 - 支付模拟 - 商家接单 - 用户查询订单状态。购物车数据我存在了Redis里使用当前用户的ID作为Key的一部分value用Hash结构包含菜品ID、数量、口味等信息。这么做的好处是购物车天然适合用缓存来做用户随时可能修改数量写MySQL反而拖慢响应速度。但要注意Redis里的购物车数据在用户下单完成后需要清除否则会出现订单提交成功但购物车还残留数据的情况。订单表是整个系统里字段最多、逻辑最复杂的表包含订单号、用户ID、地址ID、订单金额、支付状态、订单状态、下单时间等十几个字段。我的做法是先创建订单主表记录再批量插入订单明细表两个操作放在同一个事务里。订单号我采用时间戳加上随机数的方式生成避免了多用户同时下单时的重复概率。支付功能在真实项目中要对接微信支付SDK学习项目里我们不可能真实支付所以用了一个模拟支付的接口前端支付时传一个订单号后端直接更新订单状态为“已支付”。这个设计不影响理解订单状态流转的逻辑如果你以后对接真实支付只需要替换掉这个模拟接口并在支付回调里处理状态更新即可。3.4 WebSocket来单提醒与定时任务处理这功能设计上是商家端收到新订单时WebSocket推送消息提醒商家“您有新的订单待处理”。我的实现思路是用户支付成功后在service层通过WebSocket对象向服务端推送一条新订单消息商家管理后台的前端页面通过WebSocket实时接收并弹出提示音和消息框。WebSocket的原理其实不复杂它建立了一条客户端和服务器之间的长连接通道服务端可以主动给客户端发消息。我在项目里写了一个WebSocketServer类用ServerEndpoint注解声明WebSocket端点维护一个静态的会话集合。注意这里的线程安全问题多个用户同时建立连接时用线程安全的Map来存放Session对象。定时任务用的是Spring自带的Scheduled注解我在配置类上加了EnableScheduling开启定时任务功能。实际场景是商家长时间不接单订单要自动取消并回滚库存。我对超过15分钟未支付的订单做超时处理定时任务每分钟扫描一次订单表把超时未支付订单取消释放库存。这里使用cron表达式时要注意时区问题我调试的时候发现时间相差8个小时后来给服务器指定了正确的时区才解决。4. 项目中遇到的典型问题与排查过程4.1 明明配置了跨域为什么还是报错开发用户端小程序时遇到一个很典型的报错请求发送后前端提示跨域。我在WebMvcConfigurer中配置了CORS映射但接口还是访问不通。查了很久才发现问题出在我自定义的JWT拦截器上——拦截器执行顺序在CORS处理之前请求预检阶段OPTIONS请求直接被拦截器拦住了后端根本没走到跨域处理的环节。解决办法是在拦截器中加入判断如果是OPTIONS请求就直接放行让CORS过滤器来处理跨域。这一步不加的话你配置再多的跨域规则都没有用。类似的坑在网上很多人踩过如果你也遇到跨域问题先从请求方法判断入手排查。4.2 MyBatis动态SQL一个分号引发的血案菜品分页查询时我需要根据分类ID、名称、状态这些条件动态拼接SQL。用if标签写动态条件本身没问题但我在SQL语句结尾多加了一个分号结果MyBatis直接报语法异常。排查过程其实很简单把控制台打印的SQL语句复制到Navicat里执行一遍就能发现问题。这里也提醒各位配置mybatis-plus或者原生MyBatis时建议打开SQL日志打印调试效率能翻好几倍。分页查询我用的PageHelper插件使用时有一个潜在的坑PageHelper只能生效一次而且必须在查询语句之前设置分页参数。如果代码逻辑里分页设置和查询语句中间还隔着别的SQL操作分页参数有可能会被下一次查询消费掉导致数据不对。我的经验是把分页参数设置和紧随其后的查询语句写在一起别中间插入其他数据库操作。4.3 微信登录模式下的用户身份识别用户端的登录我用的是微信登录的模拟实现前端传入一个code后端调用微信接口服务换取openid如果查不到这个用户就自动注册查询得到就直接登录。学习项目没有真实微信AppID所以用一个mock接口模拟登录换取openid的过程。这个看似简单的逻辑有个细节如果用户第一次登录后查不到记录需要自动注册那么注册时要注意唯一约束。我给用户表的openid字段加上了唯一索引防止重复注册。同时登录成功后也要生成JWT Token返回给前端和员工端登录的逻辑一样。但用户端和员工端的Token解析密钥要分开配置防止用户伪造管理员身份访问管理端接口这个安全细节很容易被忽略。4.4 MySQL时区导致的时间数据混乱数据统计模块有一个很隐蔽的Bug统计营业额时某一天的数据总是不对少了几笔订单。后来我把SQL语句单独拿出来执行才发现between的时间范围条件判断出了问题——我传入的是北京时间但MySQL连接配置的时区是UTC时间被自动转换后少了8个小时。解决办法是在数据库连接URL上显式加上serverTimezoneAsia/Shanghai参数确保Java时间和数据库时间处在同一时区。另外我建议你统一在Service层处理业务时间的传入而不是在SQL里用NOW()函数这样在代码中调试起来更加可控。5. 关于笔记和代码我怎么给你做成了可以直接复用的资料5.1 代码仓库里包含了什么我把整个项目代码做了完整的梳理和注释主要包含以下几个部分完整的后端源码按照sky-pojo、sky-server、sky-common拆分成多个模块其中sky-common放通用工具类、sky-pojo放实体和DTO/VO、sky-server放Controller和Service等业务代码数据库初始化脚本建表语句加部分测试数据一起给了拿到就能直接用接口文档说明标注了每个接口的请求方式、参数说明、返回示例前端管理后台的静态资源文件配合后端做联调测试每个核心模块的代码我都加了比较详细的注释特别是一些容易踩坑的地方比如拦截器的放行规则、事物方法调用的注意事项、WebSocket的线程安全管理在代码注释里强调了一下方便你边看边理解。5.2 怎么高效地利用这套内容而不是简单地fork代码不是拿来跑起来就算学会了我建议你按照下面这个顺序去用先花半天时间把数据库的每一张表过一遍搞明白字段之间的关联关系尤其是订单表、订单明细表、菜品表之间的关系不看写好的代码自己先试着把员工登录、菜品分页查询这两个接口写出来写不出来再看答案这时候印象最深把项目跑起来之后配合接口文档逐个测试核心接口观察Redis缓存的变化、数据库数据的变化建立起数据的“流动感”拿到代码后着重看三处拦截器与JWT的处理、下单事务的完整流程、WebSocket推送的整个过程。这三个地方能看懂这个项目的价值你就吸收了一大半最后选一个模块做重构比如把订单超时处理改成使用延迟队列理论上会更好你可以动手试试5.3 如果你想进一步挑战可以把项目扩展成什么样如果你完成苍穹外卖的基础功能后觉得还有余力可以试试这几个方向引入Spring Cloud Alibaba的Nacos注册中心和OpenFeign远程调用把项目改造成微服务架构使用RabbitMQ延迟队列替换定时任务处理超时订单更贴近真实的高并发处理模型给数据统计模块接入ECharts图表展示丰富可视化效果将本地文件存储迁移到MinIO对象存储提前熟悉私有化部署的对象存储方案我在这个项目结束之后坚持把核心模块重构了一遍才感觉到自己对Spring Boot和MyBatis的理解上了一个台阶。之前那些“学过但不会用”的知识点因为项目而真正串了起来。记住项目重要的不是“做完”的那一瞬间而是你写的过程中踩过多少坑又爬出来多少次。这些真实的经历比启动成功那一刻的兴奋有用得多面试时讲项目也能讲得更有底气。本文还有配套的精品资源点击获取
返回列表