1. 项目背景与原理解读:大学生餐厅点餐管理系统到底在解决什么问题
很多做过校园类项目的人应该都有同感:大学餐厅一到中午十二点,打饭窗口前就会排成一条长龙。刷卡、找零、喊菜名、手忙脚乱地看余额,高峰期窗口阿姨一个人要同时应付七八个学生。而另一边,后厨出餐全凭吼,哪个菜先做好哪个先上,根本顾不上订单优先级。这种场景下,学生的不满集中在“排队太久”“菜凉了”“想吃的菜卖完了才知道”,餐厅管理方的问题则是“不知道每天哪个菜卖得最好”“原材料备多备少全靠拍脑袋”“月底算账对不上”。
所以我接这个需求时,第一反应是:这不是简单做一个“网上菜单”,而是要把整个学生餐厅的业务流做一遍梳理。点餐系统真正要解决的,是三个核心矛盾:高峰时段的并发压力、订单状态的不透明、库存与销售的脱节。哪怕是一个课程设计级别的Spring Boot项目,只要能把这三个问题想清楚,系统就不会只是一个空壳。
这套基于Spring Boot的大学生餐厅点餐管理系统,面向三拨人:
- 学生用户:浏览当日菜品、加购、下单、支付(或模拟支付)、查看订单状态、取餐、评价;
- 餐厅管理员:维护菜品分类、上下架菜品、设置库存、处理订单(接单/拒单/完成)、查看营业统计;
- 系统管理员:管理用户账号、重置密码、查看全餐厅订单流水、导出报表。
很多人第一眼看到“Spring Boot”就觉得是后端技术选型,但如果只做后端接口而没有一个能演示的前端页面,项目会大打折扣。我最终采用的是 Spring Boot + MyBatis-Plus + MySQL + Redis + JWT 的后端组合,前端部分适合学生的场景,用微信小程序或Vue管理后台都可以,后面我会单独说怎么选。
提示:选技术栈不必追求“高大上”,关键是每一层都能说出“为什么用它”。面试官追问时,你如果能讲清楚“Redis在这里不是为了炫技,而是为了扛住中午11:30到12:30的下单高峰”,比背一堆框架概念要有说服力得多。
2. 功能模块拆解:三个角色、两条主链路、一张状态流转图
做管理系统的第一步永远不是写代码,而是把所有“人”和“事”理清楚。这个项目里,核心角色是三类,核心链路只有两条:学生下单链路和管理员处理链路。设计时我给自己定了几个原则:能复用的一律复用、能拆独立的模块绝不耦合、权限校验从Controller层一路做到底层Service。
2.1 三个角色与权限边界
学生端的功能看起来简单,但细节非常多:
- 注册/登录(支持手机号+密码,同时支持一键模拟登录便于演示);
- 浏览菜品,支持按分类筛选、按名称搜索;
- 菜品详情页展示图片、价格、月销量、好评率、库存状态;
- 购物车:加菜、减菜、清空、结算;
- 生成订单时可填写备注(比如“不要香菜”“微辣”);
- 订单状态实时可见:待支付、待接单、制作中、待取餐、已完成、已取消;
- 取餐后可以评价,评价会汇总到菜品评分中;
- 个人中心:历史订单、地址/取餐码、账户余额、优惠券(可选)。
餐厅管理端的功能相对较重,是运营核心:
- 菜品管理:分类增删改查、菜品上下架、图片上传、价格调整、每日库存设置;
- 订单管理:按状态筛选订单列表、接单/拒单/出餐完成操作、查看订单详情;
- 营业统计:今日订单数、营收、热销菜品TOP5、分类销售占比、近7日趋势图;
- 评价管理:查看学生对菜品的评价,回复或删除不合理评价。
系统管理端则放在整个系统之上做统一管控:
- 用户管理:冻结/解冻账号、重置密码;
- 餐厅基础设置:营业时间、公告、操作日志查询。
2.2 两条主链路的时序梳理
下单链路:学生选菜 → 加入购物车 → 确认订单 → 校验库存和价格 → 扣减库存 → 生成订单(状态=待支付) → 模拟支付 → 订单推送通知给对应餐厅管理员 → 管理员接单 → 后厨制作 → 出餐完成 → 学生收到取餐码 → 取餐 → 评价。
管理员处理链路:管理员登录后台 → 看当日订单列表 → 接单后订单进入制作中 → 输入取餐码/点击完成 → 订单变成待取餐 → 学生取餐后确认完成。
这两条链路听起来简单,但落地时最容易出问题的地方恰恰是状态判断和并发扣减,比如“两个学生同时买最后一份鸡腿饭”。我在设计时就把订单状态流转和库存扣减做成了一组事务,而不是各个接口各干各的。
2.3 前端页面怎么选:小程序还是Vue后台
如果这个项目要拿去答辩、比赛或者作为求职作品,我的建议是:
- 学生端用微信小程序,模拟真实现场感,因为大学里点餐最自然的场景就是手机上操作;
- 餐厅管理端用Vue + Element UI搭建一个运营后台,桌面端操作更高效。
如果时间有限,学生端也可以用移动端H5(Vant组件库)实现,放到同一套Vue工程里,省去小程序审核注册的麻烦。我自己做这套系统时,学生端先写的H5,后期才封装成小程序,差别主要在登录授权和路由方式,后端接口完全可以复用。
3. 数据库设计与订单状态机:厨房里那把最关键的交椅
数据库是整个系统的“定盘星”。我先给出一份我实际落地时最有价值的核心表结构设计,然后再逐张表解释每一个字段为什么这么设计。这个部分值得多花笔墨,因为很多人的项目看起来功能全,但一打开数据库全是“应付式设计”,一看就是没真正思考过业务。
3.1 核心表结构说明
用户表(user)
字段包括 id、openid/手机号、username、password(BCrypt加密)、real_name、role(0学生 1餐厅管理员 2系统管理员)、avatar、balance、status(0正常 1冻结)、create_time。这里要注意:密码绝对不能明文存储,哪怕只是一个演示项目,也要用加密。如果登录用手机号,记得给手机号建唯一索引,不然注册时并发插入能插入重复账号。
菜品分类表(category)
id、name、sort、status。分类要保留sort排序字段,因为前端菜单的展示顺序是运营每天都要调整的,这个字段一定不能省。
菜品表(dish)
id、category_id(外键索引)、name、description、image、price(用Decimal(10,2)而不是Float,否则金额计算会出现精度问题)、monthly_sales、rating、status(0下架 1上架)、stock(当天剩余份数)、total_stock(当天总备货量)。
很多初学者会忽略一个关键字段:daily_date。为什么需要它?因为餐厅的库存是按天重置的,今天卖完的鸡腿饭明天又会补货。如果stock只有一个值,第二天你就得手动批量刷数据。我用的方案是加一个 stock_date,当天的stock只在当天生效,零点后定时任务自动恢复成total_stock。这个细节在答辩时非常加分。
购物车表(cart)
id、user_id、dish_id、dish_name、price、quantity、create_time。冗余dish_name和price的原因是:即使管理员后续改了菜名或者价格,用户购物车里看到的还是加入时的快照。不过最好在进入结算页时重新从dish表拉一次最新价格,以服务端价格为准。
订单表(orders)
这是整个项目里字段最多的表,我把核心字段列出来:
- id
- order_no(唯一订单号,格式例如:20250520 + 8位随机数)
- user_id
- total_amount
- status(0待支付 1待接单 2制作中 3待取餐 4已完成 5已取消 6已拒单)
- remark(用户备注)
- pickup_code(取餐码,4位数字,用于取餐确认)
- pay_type(模拟支付方式,如余额支付/在线支付)
- create_time、pay_time、take_time、finish_time
- cancel_reason
订单状态流转是我重点盯的部分,后面单独细讲。
订单明细表(order_detail)
字段包括 id、order_id、dish_id、dish_name、price、quantity。这里必须冗余菜品名称和价格快照,因为后续菜品可能改名、改价,但历史订单里的名称和金额是用户当时实际购买的记录,必须保留。这一点是会做项目和不会做项目的分水岭。
评价表(comment)
id、order_id、dish_id、user_id、content、rating(1-5分)、create_time。每个订单可以评价多个菜品,不需要单独建评价与订单之间的复杂关系。另外建议增加一个reply字段,让餐厅管理员回复评价。
营业统计可以不用建表
每天的营业额、热销菜品等,直接从订单表聚合查询即可,量级不大的场景下没必要做报表表,反而增加数据一致性处理负担。等到数据量大了再考虑用定时任务生成汇总表。
3.2 订单状态机的设计要点
这个项目里最容易被忽略但实际又最重要的,是订单状态机。状态不是几个数字随便填的,每一步都绑定对应的业务动作:
- 待支付:用户下单成功,库存已经预占扣减。用户在15分钟内不支付,系统定时任务自动取消并回滚库存。
- 待接单:支付完成,推送给餐厅管理员等待确认。
- 制作中:管理员接单后,后厨开始制作。
- 待取餐:制作完成,学生到取餐窗口凭取餐码取餐。
- 已完成:学生确认取餐,订单闭环。
- 已取消:支付前取消,或超时未支付系统自动取消。
- 已拒单:管理员因“某菜品售罄/食材问题”拒单,退款到学生余额。
状态机设计的核心守则:禁止跳状态和逆状态。比如制作中的订单不能直接变成已完成,必须先变成待取餐。要用代码保证这一点,光靠前端按钮隐藏是不行的,后端接口里必须校验前置状态。我在Service层封装了一个changeOrderStatus(orderId, expectedStatus, targetStatus)方法,用乐观锁比较数据库里的当前状态,不一致就抛异常,这正是保证并发安全的关键。
提示:订单状态字段的注释一定要写清楚每个数字的含义,否则前后端联调时你会被猜状态的大量沟通逼疯。建议把状态常量单独定义在一个枚举类中,比如
OrderStatusEnum.PENDING_PAYMENT = 0。
4. 核心后端功能落地:Spring Boot的高并发点餐实现细节
这一章节是整套系统的技术核心。我会按照项目实际开发的先后顺序,从登录鉴权到下单事务,把每一段代码的设计意图说清楚。代码不一定给全量,但关键的逻辑必须给出可直接参考的版本。
4.1 登录鉴权:JWT + 拦截器
之前的很多教程项目还在用Session,但前后端分离的场景下我更推荐JWT。不只是因为“主流”,而是因为JWT天然适配多端场景:未来的小程序、App、Web管理后台可以用同一套登录接口。
实现思路:
- 登录成功后将 userId、role 写入 JWT,设置过期时间(我设的是12小时);
- 前端把 Token 存在本地,每次请求在请求头里带上
Authorization: Bearer xxx; - 后端写一个拦截器,在 Controller 执行前解析 Token,把 userId 和 role 塞进请求上下文。
核心JWT生成逻辑可以参考这样:
public String generateToken(Long userId, Integer role) { return Jwts.builder() .setSubject(userId.toString()) .claim("role", role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 12 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }拦截器里解析完Token后,要把用户信息存到ThreadLocal里,这样在Service层任意位置都能拿到当前操作人ID,不需要每个方法都传userId参数。这个模式叫用户上下文,在真实的系统里非常常见。
需要特别注意的是:JWT一旦签发,在有效期内无法主动吊销。所以如果做了冻结用户的功能,光靠JWT是拦截不住的。我的做法是:在拦截器里除了解析JWT,还要查一下Redis里的用户状态缓存,如果用户被冻结就从Redis标记黑名单,强制拦截。这个细节很能体现工程意识。
4.2 下单事务与并发扣库存
点餐系统最容易出问题的就是“两个用户同时买最后一份菜”。这里我直接给出正确写法,并说明为什么不能用自己的判断逻辑。
很多初学者的写法是这样:
// 错误示例 Dish dish = dishMapper.selectById(dishId); if (dish.getStock() > 0) { dish.setStock(dish.getStock() - 1); dishMapper.updateById(dish); }这段代码在并发场景下一定出问题:两个请求同时读到stock=1,都认为还有货,都执行减一,最后一个库存变成0,但有两个人都下单成功了。
正确做法是使用数据库的原子更新:
int updated = dishMapper.reduceStock(dishId, quantity); if (updated == 0) { throw new BusinessException("菜品库存不足"); }对应的SQL是:
UPDATE dish SET stock = stock - #{quantity} WHERE id = #{dishId} AND stock >= #{quantity}这条SQL为什么靠谱?因为数据库行锁会保证同一时刻只有一个事务能执行这条更新,库存够才更新成功,影响行数为1;不够则更新失败,影响行数为0。这比先查询再判断安全得多。
库存扣减成功后,还需要把订单明细和订单主表插入数据库。这里就有第二个大坑:如果插入订单明细失败了怎么办?库存已经扣了,但订单没生成,就会造成“库存凭空消失”。所以这两个操作必须放在同一个事务中,要么都成功,要么都回滚。在Spring Boot中只需要在Service方法上加@Transactional(rollbackFor = Exception.class)即可。
注意:
@Transactional默认只在抛出RuntimeException时回滚,受检异常不会触发回滚。所以一定要用rollbackFor = Exception.class,这是一个很低级但很经典的坑。
4.3 超时未支付自动取消
这个功能很多人会漏掉,但它是订单系统闭环里不可少的一环。我的做法是启动一个定时任务,每30秒扫描一次超时未支付订单:
@Scheduled(fixedDelay = 30000) public void cancelExpiredOrders() { LocalDateTime deadline = LocalDateTime.now().minusMinutes(15); List<Orders> expiredOrders = orderMapper.selectExpiredOrders(OrderStatusEnum.PENDING_PAYMENT.getCode(), deadline); for (Orders order : expiredOrders) { // 回滚库存,逐个明细恢复 rollbackStock(order.getId()); // 更新订单状态 orderMapper.updateStatus(order.getId(), OrderStatusEnum.CANCELLED.getCode()); } }注意这里的回滚库存不能简单地“把库存加回去”,因为如果在超时间隙有别的订单已经把菜品卖出去了,直接加回去会导致库存虚增。所以回滚时要重新走一次断言判断(CAS思路),更新SQL依然带上当前库存条件,比如:
UPDATE dish SET stock = stock + #{quantity} WHERE id = #{dishId}整体来看,这个自动取消逻辑虽然不算复杂,但它是系统健全性的分水岭。
4.4 取餐码与订单通知
取餐码我用的是4位纯数字,在订单进入“待取餐”时生成。生成规则很简单:在1000-9999之间取随机数,但要保证同一个餐厅同一天不重复。我通过Redis的set去重,如果生成冲突就重新生成。
至于订单通知,我没有引入消息队列,因为课程设计级别的项目引入MQ属于过度设计。我的方案是用的 WebSocket:学生下单成功时,向餐厅管理员的WebSocket通道推送一条新订单消息,管理端页面实时刷新待接单数量。Spring Boot中对WebSocket的封装已经比较成熟,集成难度不高,但效果非常直观。
4.5 接口幂等性设计
下单、支付这类接口天然需要幂等控制,否则用户双击“提交订单”按钮会生成两笔订单。我用的方案是前端生成一个requestId(UUID),下单接口接收这个参数并在Redis里设置标记,如果同一个requestId在1分钟内重复提交,直接返回上一次的操作结果。
Boolean firstRequest = redisTemplate.opsForValue() .setIfAbsent("order_request_" + requestId, "1", Duration.ofMinutes(1)); if (Boolean.FALSE.equals(firstRequest)) { return Result.error("请勿重复提交"); }这个小改动效果立竿见影,也是我在实际开发中反复遇到过的“双击出双单”问题。
5. 前端交互与对接细节:从按钮到接口的完整链路
这里分享的是前端联调过程中容易被坑到的内容。很多后端出身的同学觉得前端不重要,实际上前端联调质量直接决定了演示效果和答辩体验。
5.1 菜品列表与购物车状态同步
学生端最核心的交互是菜品列表页和购物车。这里有一个重要的体验点:列表页每个菜品右上角的“+ / -”按钮,对应的其实是购物车接口。不要为了省事在列表页维护一个本地购物车状态,否则刷新后就全丢了。
正确做法是:进入页面时先调用/cart/list获取当前用户的购物车,在内存中用Map合并菜品ID和数量,加减时调用接口并更新本地Map。为了减少请求频率,我加了一个300毫秒的防抖,用户连续点击加减时并不会每个点击都发接口,而是等稳定后再发一次。
5.2 下单前的二次校验
用户点击“去结算”时,前端看似把购物车数据都拿到了,但真正提交订单时必须以服务端重新查询的价格和库存为准。原因很简单:前端展示的价格可能已经过期,或者菜品已经下架。我在后端下单接口里做了三件必做的事:
- 校验菜品状态是已上架;
- 用数据库里的最新价格重算订单总额,而不是直接信任前端的total_price;
- 库存不足时明确返回“哪些菜品库存不足”而不是笼统地提示下单失败。
5.3 管理端订单列表的轮询优化
管理端一开始用的是短轮询,每2秒请求一次待接单数量。这个方案在并发量低的时候没问题,但每次都走HTTP请求,效率不算最优。后来我用WebSocket做了服务端主动推送,管理端只监听消息即可,刷新频率降为零。
不要小看这个改动,它在高并发场景下的价值不只是减少请求量,更是让“新订单到达”这件事变成实时,管理员处理订单的响应速度会明显变快。演示时也更有说服力。
6. 项目部署与演示环境搭建:从本地到服务器的完整过程
这个项目要不要上服务器,取决于你的场景。如果只是本地跑给老师看,MySQL和Redis在本机起服务就好;如果想要一个随时可访问的在线演示地址,那我建议按下面的方式部署。
6.1 本地快速启动
本地环境需要安装的工具:
- JDK 1.8+(推荐17但1.8更稳,很多教学环境都是1.8);
- Maven 3.6+;
- MySQL 5.7+;
- Redis 5.0+;
- Node.js 14+(如果前端用Vue)。
启动顺序有讲究:先启动Redis,再启动MySQL,最后启动Spring Boot后端。如果后端启动后报“连接不上Redis”之类的错误,先检查Redis的protected-mode,本地调试时通常要设为no或配置密码。
配置文件中我习惯把数据库密码和Redis密码放到application-dev.yml里,生产配置单独用application-prod.yml,两套配置互不干扰。
数据初始化:建库语言手动执行一次即可,MyBatis-Plus的自动填充可以处理create_time这类字段。为了项目开箱即用,我写好了init.sql,里面包含管理员账号、几个测试学生账号、十个测试菜品和一个演示分类,这样任何拿到代码的人都能直接跑起来。
6.2 服务器部署的注意事项
在云服务器上部署时,最核心的部分是数据库和缓存不能裸奔:
- MySQL 的 root 账号只能本地登录,对外单独创建一个只拥有本库权限的账号;
- Redis 必须设置密码并绑定内网IP,禁止暴露到公网;
- Spring Boot 后端打包成
jar,用nohup java -jar方式后台启动; - 前端Vue项目打包成静态文件后,用Nginx反向代理到后端接口。
Nginx中一个需要特别关注的点是:如果前端和后端在不同端口,必须配置跨域,或者在Spring Boot中加入CORS配置。我实际踩过的坑是:前端本地调试时一切正常,但打包部署后接口全部403,排查半天发现是Nginx没加proxy_set_header Host和跨域头。把这些配置提前写明白,能省去大量联调和演示时的手忙脚乱。
6.3 接口文档怎么维护
我推荐在项目中集成SpringDoc(也就是OpenAPI 3),启动后访问http://localhost:8080/swagger-ui.html就能看到所有接口的说明。一个附带的好处是,做前端时可以直接在Swagger界面里测试接口,不需要Postman反复切换环境变量。
集成起来非常简单,加一个依赖再补一个配置类就行。多说一句:接口文档不需要写得特别华丽,但每个接口的“请求参数说明”和“返回示例”一定要有,这是对外演示时最省事的助手。
7. 常见问题与排查技巧实录:我能想到的12个高频坑
做这套系统的过程中,我自己踩过、带过的同学也踩过很多次的问题,挑出12个我认为最有代表性的,集中写在这里。这些内容在常规文档里不太会有,属于真正实操中会撞墙的地方。
7.1 后端启动与配置类问题
问题1:启动时一直报Failed to configure a DataSource。排查思路:这是最常见的问题,往往是配置文件里数据源信息没配上,或者配置类没有被扫描到。检查application.yml中spring.datasource.url、username、password是否填写正确,再确认数据库连接串里的useSSL=false&characterEncoding=utf8是否加上。
问题2:启动成功,但访问接口返回404。排查思路:确认Controller类是否在启动类所在的包路径下。Spring Boot默认只扫描启动类所在包的子孙包,如果Controller放在了启动类的兄弟包里,必然404。
问题3:Redis连接正常,但缓存写入不生效。排查思路:先检查通知消息是否启用。直接在Redis中执行CONFIG SET notify-keyspace-events Ex只能临时生效,重启后就没了,要在配置文件里持久化设置。另外看序列化方式,如果用JDK默认序列化,Redis里会出现乱码,建议统一用StringRedisSerializer。
7.2 业务逻辑类问题
问题4:下单时提示库存不足,但数据库里明明有货。排查思路:大概率是库存更新SQL写成了set stock = stock - 1而没有加stock >= #{quantity}条件。也可能是库存字段类型是int,而传入的quantity是0或负数。建议在下单前做一次参数校验,禁止quantity小于等于0。
问题5:订单状态能直接从“待支付”跳到“已完成”。排查思路:前端隐藏按钮不等于后端安全。需要在Service层的方法入口调用状态校验。我用一个私有方法统一做前置状态判断,如果状态不匹配就抛出异常。
问题6:取消订单后库存没恢复。排查思路:回滚库存的业务逻辑和更新订单状态必须在同一个事务中。如果你把回滚库存写在OrderController里,而订单状态更新写在OrderService里,这是两个事务,回滚失败的概率非常大。建议在Service层的同一个方法里完成全部操作。
问题7:支付流程做成了“直接改状态”。排查思路:这种简化可以做,但一定要记录支付时间、支付流水号。建议至少在订单表增加一个pay_log字段或单独的流水表,否则运营时发现订单状态是“已完成”但支付金额对不上,就完全没法追溯。
7.3 前端与联调类问题
问题8:前端请求接口跨域报错。排查思路:后端加上CORS配置即可,在Spring Boot中实现WebMvcConfigurer并设置addCorsMappings。注意前端上线后如果和后端同域,可以去掉跨域配置,避免无谓的风险。
问题9:多端登录时效不同步。排查思路:JWT的设计决定了它在过期前无法主动失效,所以一定配合用户状态缓存。我建议登录成功后将最新token写入Redis,每次请求时比对token是否是最新版本,不一致则强制重新登录。
问题10:管理端页面刷新后列表状态不同步。排查思路:前端要在路由进入时重新拉取订单列表,不要只依赖WebSocket推送。如果WebSocket断线重连,推送消息会丢,一刷新就全回来了。这个属于前端状态管理的基础,但很容易被忽略。
问题11:菜品图片上传后无法预览。排查思路:检查上传路径是不是放在了静态资源目录之外。Spring Boot默认只暴露classpath:/static/,如果你把图片传到服务器任意路径但没有配置资源映射,一定无法访问。我常用的方式是:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceHandler("file:" + uploadDir + "/"); } }问题12:Tomcat默认并发瓶颈。排查思路:默认的Tomcat最大线程数是200,在模拟高并发测试时可能不够。可以在application.yml里调大,并且配合MySQL连接池(HikariCP)的maximum-pool-size一起调整,线程数调大但连接池上限不调就会出现新的瓶颈。
8. 这套系统还能怎么扩展:从课程设计到真实落地之间的距离
写完这套系统后,我个人最真实的感觉是:它已经能撑起一个大学餐厅的日常运转了,但离真正的商业产品还有一段距离。如果未来你想把它继续推进,我建议从这些方向做增强:
- 引入MQ(RocketMQ或RabbitMQ)做订单异步处理和解耦,把WebSocket推送改成基于消息的可靠通知;
- 加入真正的在线支付(比如对接第三方支付沙箱),把模拟支付替换成真实支付流程;
- 增加数据统计报表的可视化,比如用ECharts展示每日营收趋势和各窗口销售占比;
- 引入分布式锁或Redisson,进一步提升高并发下的库存扣减安全性;
- 增加消息推送能力,比如通过订阅消息通知用户“订单已出餐、可以取餐”。
如果这是一个求职项目的作品,我还建议补充单元测试和CI/CD流水线。哪怕只是简单的几个核心Service的单元测试,也会让面试官感受到你具备基本的质量保障意识。
我在实测中的感受是:@Transactional、JWT、Redis缓存、WebSocket实时推送这些点,看似都是基础知识,但真正把它们串到一个完整业务场景里,是会和“只会调用框架API”完全不同的境界。这套系统做完后,至少可以理直气壮地说:我理解订单系统是怎么运转的,我知道怎么处理并发下的库存扣减,我知道什么时候该引入缓存、什么时候该引入消息队列。
最后再分享一个特别实用的细节:写Swagger接口文档时,把每个接口的summary写得像一个完整的业务动作,比如“学生确认收货并评价”,而不是“订单评价接口”。答辩时你打开Swagger页面,不需要额外讲解,评委扫一眼就知道你系统具备哪些能力,这个习惯在未来的工作中也非常受益。