这是大三上学期的软件综合课程设计。项目面向餐饮企业,完成管理后台和移动端点餐流程,并在基础业务之上实践 Redis 缓存、MySQL 读写分离和 Nginx 部署。
返回项目实践 · 课程资料(提取码:3ezu)
1、项目概览
| 项目 |
内容 |
| 学习阶段 |
大三上学期 |
| 课程名称 |
软件综合课程设计 |
| 项目名称 |
基于 Spring Boot 和 H5 的瑞吉外卖系统 |
| 项目形式 |
小组课程设计 |
| 现有材料 |
课程报告、答辩课件、可执行 JAR、演示视频 |
系统包含两类使用端:
- 管理后台:员工、分类、菜品、套餐和订单管理。
- 移动端:手机号登录、菜品浏览、规格选择、购物车、地址管理、下单和历史订单。
报告明确记录:课程版本没有接入真实支付模块,因此下单流程不能等同于完整商业外卖系统。
2、技术栈
| 层次 |
技术 |
| 后端 |
Spring Boot 2.4.5、Spring MVC、MyBatis-Plus |
| 数据 |
MySQL、Druid、Redis、Spring Cache |
| 前端 |
HTML、CSS、JavaScript、Vue、Element UI、Axios、H5 |
| 工程 |
Maven、Lombok、Fastjson、Knife4j |
| 部署与优化 |
Linux、Nginx、Sharding-JDBC |
课程资料里保留了报告、课件、演示视频和可执行 JAR。报告中有较完整的关键接口与缓存改造代码,JAR 可以用于确认控制器、实体、服务和配置等模块结构。
3、核心功能
3.1 管理后台
- 员工登录、退出、状态管理和分页查询。
- 菜品分类、套餐分类、菜品及口味管理。
- 套餐组合管理和图片上传。
- 订单查询与基础状态管理。
- 登录过滤、统一返回结构和全局异常处理。
3.2 移动端
- 手机验证码登录。
- 菜品和套餐浏览。
- 购物车增删与数量调整。
- 收货地址维护。
- 提交订单和查看历史订单。
4、数据设计
课程报告整理了 11 张主要数据表:
user、shopping_cart、setmeal_dish、setmeal、order_detail、orders、dish_flavor、dish、category、address_book、employee。
项目没有使用复杂外键关系,主要由业务层维护数据之间的关联。这让课程开发更直接,但也意味着一致性校验和删除更新逻辑需要在代码中承担。
5、性能与部署实践
5.1 Redis 与 Spring Cache
- 将短信验证码从 Session 改为 Redis 存储,并设置有效期。
- 缓存菜品和套餐等高频读取数据。
- 数据更新时主动清理相关缓存,避免数据库和缓存不一致。
- 使用 Spring Cache 注解简化套餐缓存逻辑。
5.2 MySQL 读写分离
课程材料配置了 MySQL 主从复制,并通过 Sharding-JDBC 将写操作路由到主库、读操作路由到从库。这部分主要用于理解读写分离链路,不代表系统已经完成生产级高可用设计。
5.3 Nginx
使用 Nginx 部署静态资源、反向代理后端服务,并学习负载均衡的基本配置思路。
6、关键代码记录
6.1 手机验证码缓存
移动端登录从 Session 改为 Redis 缓存验证码,核心点是:发送验证码时设置 5 分钟有效期,登录成功后删除缓存,避免验证码重复使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @PostMapping("/sendMsg") public R<String> sendMsg(@RequestBody User user, HttpSession session) { String phone = user.getPhone();
if (StringUtils.isNotEmpty(phone)) { String code = ValidateCodeUtils.generateValidateCode(4).toString(); redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES); return R.success("手机验证码短信发送成功"); }
return R.error("短信发送失败"); }
@PostMapping("/login") public R<User> login(@RequestBody Map map, HttpSession session) { String phone = map.get("phone").toString(); String code = map.get("code").toString(); Object codeInRedis = redisTemplate.opsForValue().get(phone);
if (codeInRedis != null && codeInRedis.equals(code)) { LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getPhone, phone); User user = userService.getOne(queryWrapper);
if (user == null) { user = new User(); user.setPhone(phone); user.setStatus(1); userService.save(user); }
session.setAttribute("user", user.getId()); redisTemplate.delete(phone); return R.success(user); }
return R.error("登录失败"); }
|
6.2 菜品列表缓存
菜品列表属于高频读取接口。课程代码根据分类和状态拼接 Redis key,命中缓存则直接返回;未命中时查询数据库、组装口味数据,再写回 Redis。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @GetMapping("/list") public R<List<DishDto>> list(Dish dish) { String key = "dish_" + dish.getCategoryId() + "_" + dish.getStatus(); List<DishDto> dishDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);
if (dishDtoList != null) { return R.success(dishDtoList); }
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId()); queryWrapper.eq(Dish::getStatus, 1); queryWrapper.orderByAsc(Dish::getSort) .orderByDesc(Dish::getUpdateTime);
List<Dish> list = dishService.list(queryWrapper); dishDtoList = list.stream().map(item -> { DishDto dishDto = new DishDto(); BeanUtils.copyProperties(item, dishDto);
LambdaQueryWrapper<DishFlavor> flavorWrapper = new LambdaQueryWrapper<>(); flavorWrapper.eq(DishFlavor::getDishId, item.getId()); dishDto.setFlavors(dishFlavorService.list(flavorWrapper)); return dishDto; }).collect(Collectors.toList());
redisTemplate.opsForValue().set(key, dishDtoList, 60, TimeUnit.MINUTES); return R.success(dishDtoList); }
|
7、项目边界
- 项目为课程学习与小组实践,系统整体成果不等同于个人独立开发。
- 当前资料以报告、课件、可执行 JAR 和演示材料为主,完整源码仓库没有单独归档。
- 没有真实支付、完整安全审计、生产监控和压力测试闭环。
- Redis、主从复制和 Nginx 更接近技术实践,不应直接描述成生产环境能力。
8、复盘
这个项目让我第一次把登录、权限、业务接口、数据库、缓存和部署串成一条完整链路。现在回看,我会更关注版本化源码、接口测试、权限模型、事务边界、支付模拟和容器化部署,并把“能运行”与“能稳定维护”分成两个阶段验收。