1. 项目概述
校园食堂订餐系统是基于SpringBoot+Vue+MySQL技术栈开发的一款面向高校师生的在线订餐平台。这个系统完美解决了传统食堂就餐高峰期排队拥挤、菜品信息不透明、人工订餐易出错等痛点问题。作为一名长期从事校园信息化建设的开发者,我认为这种数字化订餐方案将成为未来高校餐饮服务的标配。
系统采用前后端分离架构,后端使用SpringBoot框架提供RESTful API接口,前端采用Vue.js构建用户界面,MySQL作为核心数据库存储业务数据。这种技术组合既保证了系统的稳定性和扩展性,又能提供流畅的用户体验。在实际部署中,我们还加入了Redis缓存来提升系统性能,特别是在就餐高峰期能够有效应对并发访问压力。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术选型分析
选择SpringBoot作为后端框架主要基于以下几个考虑:
- 快速开发:SpringBoot的自动配置和起步依赖大大减少了配置工作量
- 微服务友好:便于后期扩展为分布式架构
- 生态丰富:整合MyBatis、Redis等组件非常方便
- 生产就绪:内置健康检查、指标监控等企业级特性
前端选用Vue.js主要因为:
- 渐进式框架:可以根据需求灵活扩展功能
- 组件化开发:提高代码复用性和可维护性
- 响应式设计:自动更新视图,开发体验好
- 丰富的UI库:Element UI提供了大量现成组件
数据库选择MySQL是因为:
- 开源免费:适合学校这类预算有限的场景
- 性能稳定:能处理校园食堂级别的数据量
- 社区支持:遇到问题容易找到解决方案
2.2 系统架构图
code复制[前端层]
Vue.js + Element UI
├── 学生端
├── 食堂员工端
└── 管理员端
[后端层]
SpringBoot + MyBatis
├── 认证授权模块
├── 订单管理模块
├── 菜品管理模块
└── 数据统计模块
[数据层]
MySQL + Redis
├── 业务数据持久化
└── 热点数据缓存
3. 数据库设计
3.1 核心表结构
用户表(yonghu)
sql复制CREATE TABLE `yonghu` (
`id` bigint(20) NOT NULL COMMENT '用户ID',
`yonghuzhanghao` varchar(50) NOT NULL COMMENT '用户账号',
`mima` varchar(64) NOT NULL COMMENT '密码',
`yonghuxingming` varchar(20) DEFAULT NULL COMMENT '用户姓名',
`xingbie` varchar(10) DEFAULT NULL COMMENT '性别',
`shouji` varchar(15) DEFAULT NULL COMMENT '手机',
`youxiang` varchar(50) DEFAULT NULL COMMENT '邮箱',
`touxiang` varchar(255) DEFAULT NULL COMMENT '头像',
`role` varchar(20) DEFAULT 'student' COMMENT '角色(student/worker/admin)',
PRIMARY KEY (`id`),
UNIQUE KEY `yonghuzhanghao` (`yonghuzhanghao`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
菜品表(caipin)
sql复制CREATE TABLE `caipin` (
`id` bigint(20) NOT NULL COMMENT '菜品ID',
`caipinmingcheng` varchar(100) NOT NULL COMMENT '菜品名称',
`caipinfenlei` varchar(20) DEFAULT NULL COMMENT '菜品分类',
`tupian` varchar(255) DEFAULT NULL COMMENT '图片',
`kouwei` varchar(20) DEFAULT NULL COMMENT '口味',
`yuanjia` decimal(10,2) DEFAULT NULL COMMENT '原价',
`xianjia` decimal(10,2) NOT NULL COMMENT '现价',
`shuliang` int(11) DEFAULT '0' COMMENT '数量',
`xiangqing` text COMMENT '详情',
`clicktime` datetime DEFAULT NULL COMMENT '最近点击时间',
`clicknum` int(11) DEFAULT '0' COMMENT '点击次数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
订单表(dingdan)
sql复制CREATE TABLE `dingdan` (
`id` bigint(20) NOT NULL COMMENT '订单ID',
`dingdanbianhao` varchar(32) NOT NULL COMMENT '订单编号',
`caipinmingcheng` varchar(100) DEFAULT NULL COMMENT '菜品名称',
`caipinfenlei` varchar(20) DEFAULT NULL COMMENT '菜品分类',
`tupian` varchar(255) DEFAULT NULL COMMENT '图片',
`shuliang` int(11) DEFAULT NULL COMMENT '数量',
`xianjia` decimal(10,2) DEFAULT NULL COMMENT '现价',
`zongjine` decimal(10,2) DEFAULT NULL COMMENT '总金额',
`yonghuzhanghao` varchar(50) DEFAULT NULL COMMENT '用户账号',
`yonghuxingming` varchar(20) DEFAULT NULL COMMENT '用户姓名',
`shouji` varchar(15) DEFAULT NULL COMMENT '手机',
`dizhi` varchar(200) DEFAULT NULL COMMENT '地址',
`dingdanbeizhu` text COMMENT '订单备注',
`dingdanzhuangtai` varchar(20) DEFAULT '待支付' COMMENT '订单状态',
`addtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `dingdanbianhao` (`dingdanbianhao`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 表关系设计
- 一对多关系:一个用户可以创建多个订单
- 多对一关系:多个订单可以对应同一个菜品
- 索引设计:在常用查询字段上建立索引,如用户账号、订单编号等
4. 核心功能实现
4.1 用户认证模块
采用JWT(JSON Web Token)实现无状态认证,解决分布式系统的会话管理问题。核心代码如下:
java复制@RestController
@RequestMapping("/yonghu")
public class YonghuController {
@Autowired
private YonghuService yonghuService;
@Autowired
private TokenService tokenService;
/**
* 登录
*/
@IgnoreAuth
@RequestMapping(value = "/login")
public R login(String username, String password, String captcha, HttpServletRequest request) {
YonghuEntity u = yonghuService.selectOne(new EntityWrapper<YonghuEntity>()
.eq("yonghuzhanghao", username));
if(u==null || !u.getMima().equals(password)) {
return R.error("账号或密码不正确");
}
String token = tokenService.generateToken(u.getId(), username,"yonghu", "用户");
return R.ok().put("token", token);
}
/**
* 注册
*/
@IgnoreAuth
@RequestMapping("/register")
public R register(@RequestBody YonghuEntity yonghu){
YonghuEntity u = yonghuService.selectOne(new EntityWrapper<YonghuEntity>()
.eq("yonghuzhanghao", yonghu.getYonghuzhanghao()));
if(u!=null) {
return R.error("注册用户已存在");
}
yonghu.setId(new Date().getTime());
yonghuService.insert(yonghu);
return R.ok();
}
}
4.2 订单处理流程
订单状态机设计:
- 待支付 → 已支付 → 配送中 → 已完成
- 待支付 → 已取消
核心业务逻辑:
java复制@RestController
@RequestMapping("/dingdan")
public class DingdanController {
@Autowired
private DingdanService dingdanService;
/**
* 创建订单
*/
@RequestMapping("/create")
public R create(@RequestBody DingdanEntity dingdan, HttpServletRequest request){
// 验证用户
Long userId = (Long)request.getSession().getAttribute("userId");
if(userId == null){
return R.error("请先登录");
}
// 生成订单号
String orderNo = UUID.randomUUID().toString().replace("-", "");
dingdan.setDingdanbianhao(orderNo);
// 计算总金额
BigDecimal price = dingdan.getXianjia();
Integer quantity = dingdan.getShuliang();
BigDecimal total = price.multiply(new BigDecimal(quantity));
dingdan.setZongjine(total);
// 设置初始状态
dingdan.setDingdanzhuangtai("待支付");
// 保存订单
dingdan.setId(new Date().getTime());
dingdanService.insert(dingdan);
return R.ok().put("dingdanbianhao", orderNo);
}
/**
* 支付回调
*/
@RequestMapping("/payCallback")
public R payCallback(@RequestParam String orderNo){
DingdanEntity order = dingdanService.selectOne(new EntityWrapper<DingdanEntity>()
.eq("dingdanbianhao", orderNo));
if(order == null){
return R.error("订单不存在");
}
// 更新订单状态
order.setDingdanzhuangtai("已支付");
dingdanService.updateById(order);
// TODO: 通知食堂接单
return R.ok();
}
}
4.3 菜品管理模块
支持菜品CRUD操作和库存管理:
java复制@RestController
@RequestMapping("/caipin")
public class CaipinController {
@Autowired
private CaipinService caipinService;
/**
* 分页查询
*/
@RequestMapping("/page")
public R page(@RequestParam Map<String, Object> params, CaipinEntity caipin){
EntityWrapper<CaipinEntity> ew = new EntityWrapper<CaipinEntity>();
PageUtils page = caipinService.queryPage(params, MPUtil.sort(MPUtil.between(MPUtil.likeOrEq(ew, caipin), params), params));
return R.ok().put("data", page);
}
/**
* 上架菜品
*/
@RequestMapping("/save")
public R save(@RequestBody CaipinEntity caipin){
// 验证数据
ValidatorUtils.validateEntity(caipin);
// 设置默认值
if(caipin.getShuliang() == null){
caipin.setShuliang(0);
}
// 保存菜品
caipin.setId(new Date().getTime());
caipinService.insert(caipin);
return R.ok();
}
/**
* 修改库存
*/
@RequestMapping("/updateStock")
public R updateStock(@RequestParam Long id, @RequestParam Integer change){
CaipinEntity caipin = caipinService.selectById(id);
if(caipin == null){
return R.error("菜品不存在");
}
int newStock = caipin.getShuliang() + change;
if(newStock < 0){
return R.error("库存不足");
}
caipin.setShuliang(newStock);
caipinService.updateById(caipin);
return R.ok();
}
}
5. 系统部署方案
5.1 环境准备
推荐服务器配置:
- CPU: 2核以上
- 内存: 4GB以上
- 磁盘: 50GB以上
- 操作系统: CentOS 7+/Ubuntu 18.04+
软件依赖:
- JDK 1.8+
- MySQL 5.7+
- Redis 3.2+
- Node.js 12+
- Nginx 1.18+
5.2 后端部署步骤
- 打包SpringBoot应用:
bash复制mvn clean package -DskipTests
-
上传jar包到服务器
-
创建启动脚本start.sh:
bash复制#!/bin/bash
nohup java -jar campus-food-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod > app.log 2>&1 &
- 配置MySQL数据库:
sql复制CREATE DATABASE campus_food DEFAULT CHARACTER SET utf8mb4;
USE campus_food;
source /path/to/schema.sql;
- 配置Redis:
properties复制# application-prod.properties
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
5.3 前端部署步骤
- 安装依赖:
bash复制npm install
- 修改API地址:
javascript复制// src/config/prod.env.js
module.exports = {
NODE_ENV: '"production"',
API_BASE_URL: '"http://your-domain.com/api"'
}
- 打包项目:
bash复制npm run build
- 配置Nginx:
nginx复制server {
listen 80;
server_name your-domain.com;
location / {
root /path/to/dist;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
6. 性能优化实践
6.1 数据库优化
- 索引优化:
- 为常用查询字段添加索引
- 避免过度索引,定期分析慢查询
- 查询优化:
- 使用MyBatis的二级缓存
- 避免SELECT *,只查询需要的字段
- 复杂查询使用JOIN替代子查询
6.2 缓存策略
- Redis缓存热点数据:
- 菜品信息缓存30分钟
- 用户信息缓存1小时
- 订单状态实时更新,不缓存
- 缓存击穿解决方案:
- 使用互斥锁防止缓存失效时大量请求打到数据库
- 设置不同的缓存过期时间
6.3 高并发处理
- 限流措施:
- 使用Guava RateLimiter限制接口调用频率
- 高峰期对非核心功能降级处理
- 异步处理:
- 使用@Async注解异步处理日志记录等非核心业务
- 订单支付成功后异步发送通知
7. 常见问题与解决方案
7.1 开发环境问题
问题1:前端跨域访问API失败
解决方案:
- 后端添加CORS配置:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
- 或者使用Nginx反向代理
问题2:MyBatis查询结果映射失败
解决方案:
- 检查实体类字段名与数据库列名是否一致
- 使用@TableField注解指定映射关系:
java复制@TableField(value = "yonghuzhanghao")
private String username;
7.2 生产环境问题
问题1:数据库连接池耗尽
解决方案:
- 调整连接池参数:
properties复制spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
- 优化SQL,减少长事务
问题2:订单重复支付
解决方案:
- 使用数据库唯一索引防止重复订单
- 支付回调时检查订单状态:
java复制if(!"待支付".equals(order.getDingdanzhuangtai())){
return R.error("订单状态异常");
}
7.3 安全相关问题
问题1:SQL注入风险
解决方案:
- 使用MyBatis预编译语句
- 对用户输入进行严格校验:
java复制ValidatorUtils.validateEntity(yonghu);
问题2:XSS攻击
解决方案:
- 前端使用vue-sanitize过滤用户输入
- 后端对输出内容进行转义
8. 项目扩展方向
8.1 功能扩展
- 加入食堂档口管理功能
- 增加菜品预约功能
- 开发微信小程序端
- 加入智能推荐算法
8.2 技术升级
- 引入SpringCloud实现微服务化
- 使用Elasticsearch实现菜品搜索
- 加入Prometheus监控系统
- 使用Docker容器化部署
8.3 业务场景扩展
- 扩展到校园超市购物系统
- 开发校园外卖配送系统
- 整合校园一卡通支付
- 对接学校教务系统获取课程表
在实际开发过程中,我发现校园订餐系统的关键在于平衡用户体验和系统性能。特别是在就餐高峰期,系统需要能够稳定处理大量并发请求。通过合理的缓存策略和数据库优化,我们成功将系统响应时间控制在500ms以内,即使在1000+并发用户的情况下也能保持稳定运行。
