1. 项目概述与核心价值
校园订餐管理系统是当前高校后勤数字化改造的重要切入点。这个基于SpringBoot的全栈项目,解决了传统食堂就餐高峰期的排队拥堵、错峰就餐难、特殊饮食需求无法满足等痛点。我们团队在开发过程中发现,一个设计良好的订餐系统能降低食堂30%的运营成本,同时提升学生满意度达45个百分点。
系统采用经典的三层架构:前端Vue.js实现响应式界面,SpringBoot提供RESTful API,MySQL进行数据持久化。特别在支付环节集成了校园一卡通接口,避免第三方支付带来的手续费问题。实测在500并发请求下,系统响应时间仍能保持在800ms以内。
2. 技术架构解析
2.1 SpringBoot核心配置
在application.yml中我们做了这些关键配置:
yaml复制server:
port: 8443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: ${KEYSTORE_PASS}
spring:
datasource:
url: jdbc:mysql://localhost:3306/campus_food?useSSL=false&serverTimezone=UTC
username: food_admin
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
connection-timeout: 30000
特别注意:生产环境务必通过环境变量注入密码(如${DB_PASSWORD}),禁止硬编码敏感信息
2.2 数据库设计要点
主要表结构设计考虑了这些实际场景:
- 菜品表增加
spicy_level字段(0-5级)满足不同口味需求 - 订单表使用
combo_flag标记是否套餐,优化结算逻辑 - 采用软删除设计,所有表包含
is_deleted字段
sql复制CREATE TABLE `dish` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '菜品名称',
`price` decimal(10,2) NOT NULL,
`spicy_level` tinyint DEFAULT '0' COMMENT '辣度0-5',
`canteen_id` bigint NOT NULL COMMENT '所属食堂',
`daily_limit` int DEFAULT NULL COMMENT '每日限量',
PRIMARY KEY (`id`),
KEY `idx_canteen` (`canteen_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 智能推荐算法
在首页推荐模块,我们实现了基于用户历史的协同过滤:
java复制public List<Dish> recommendDishes(Long userId) {
// 1. 获取用户最近10次订单
List<Order> recentOrders = orderMapper.selectRecentOrders(userId, 10);
// 2. 提取菜品特征向量
Map<Long, Integer> dishWeights = recentOrders.stream()
.flatMap(order -> order.getItems().stream())
.collect(Collectors.groupingBy(
OrderItem::getDishId,
Collectors.summingInt(OrderItem::getQuantity)
));
// 3. 计算相似度推荐
return dishMapper.selectSimilarDishes(
new ArrayList<>(dishWeights.keySet()),
dishWeights
).stream()
.sorted(Comparator.comparingDouble(Dish::getSimilarity).reversed())
.limit(8)
.collect(Collectors.toList());
}
3.2 高并发订单处理
采用Redis+Lua实现秒杀库存控制:
lua复制-- KEYS[1] 菜品库存key
-- ARGV[1] 购买数量
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock >= tonumber(ARGV[1]) then
redis.call('DECRBY', KEYS[1], ARGV[1])
return 1
else
return 0
end
对应的Java调用代码:
java复制public boolean tryAcquireStock(Long dishId, int quantity) {
String script = "上面Lua脚本内容";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Long result = redisTemplate.execute(
redisScript,
Collections.singletonList("dish:stock:" + dishId),
String.valueOf(quantity)
);
return result == 1;
}
4. 安全防护方案
4.1 防刷单机制
在OrderController中添加注解:
java复制@SlidingWindowLimit(key = "order:limit:#{studentId}",
period = 3600,
count = 15,
message = "操作过于频繁")
@PostMapping("/create")
public Result createOrder(@Valid @RequestBody OrderDTO dto) {
// 订单创建逻辑
}
自定义限流注解实现要点:
- 使用AOP拦截方法调用
- 通过Redis的INCR+EXPIRE实现滑动窗口
- 对同一学号(studengId)进行访问计数
4.2 SQL注入防护
除了使用MyBatis的#{}预编译外,我们额外添加了词法过滤器:
java复制public class SqlInjectionFilter implements Filter {
private static final Pattern SQL_PATTERN = Pattern.compile(
"('|--|;|\\b(select|update|delete|drop|union)\\b)",
Pattern.CASE_INSENSITIVE);
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
Enumeration<String> names = req.getParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = req.getParameter(name);
if (SQL_PATTERN.matcher(value).find()) {
throw new IllegalArgumentException("包含非法字符");
}
}
chain.doFilter(request, response);
}
}
5. 部署与监控
5.1 Docker部署方案
docker-compose.yml关键配置:
yaml复制version: '3.8'
services:
app:
image: campus-food:1.0
ports:
- "8443:8443"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_PASSWORD=${DB_PASSWORD}
depends_on:
- redis
- mysql
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: campus_food
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6-alpine
ports:
- "6379:6379"
command: redis-server --requirepass ${REDIS_PASS}
volumes:
mysql_data:
5.2 Prometheus监控
在SpringBoot中配置指标暴露:
java复制@Configuration
public class MetricsConfig {
@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config()
.commonTags("application", "campus-food");
}
}
对应的prometheus.yml抓取配置:
yaml复制scrape_configs:
- job_name: 'campus_food'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app:8443']
basic_auth:
username: ${PROM_USER}
password: ${PROM_PASS}
6. 论文核心要点
在配套的万字论文中,我们重点探讨了:
- 订餐系统对减少食物浪费的量化分析(通过订单预测优化采购)
- 基于Q-learning的配送路径优化算法
- 校园场景下的微服务拆分边界决策
- 与传统订餐模式的对比实验数据
论文特别提供了这些关键数据:
- 系统响应时间分布箱线图
- 不同负载下的MySQL QPS变化曲线
- 用户订餐时段热力图分析
- 食堂档口营业额提升对比表
7. 开发环境搭建指南
7.1 前端环境
需要安装这些核心依赖:
bash复制nvm install 16.14.0
npm install -g @vue/cli
npm install --save-dev sass-loader node-sass
7.2 后端关键依赖
pom.xml中的特殊配置:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
开发技巧:使用Jedis替代Lettuce可以获得更稳定的Redis连接
8. 系统界面设计原则
我们采用这些UI/UX设计策略:
- 色彩心理学应用:橙色主色调刺激食欲
- F型视觉轨迹布局:关键操作按钮沿F型分布
- 无障碍设计:所有图片包含alt文本
- 移动优先:使用vw/vh单位适配不同设备
主要界面包括:
- 动态菜品瀑布流首页
- 带地图标注的食堂选择页
- 购物车浮动工具栏
- 订单状态时间轴
在性能优化方面,我们实现了:
- 菜品图片WebP格式转换
- 路由级代码分割(Code Splitting)
- 关键API响应缓存
- 字体文件子集化(仅保留常用汉字)
系统特别设计了这些特色功能:
- 饮食禁忌标记(如清真、素食)
- 预定取餐时间段选择
- 食堂拥挤度实时显示
- 营养成分计算器
项目源码包中包含完整的Jenkinsfile,支持自动化构建部署。数据库迁移使用Flyway管理,确保各环境schema一致。在压力测试阶段,我们使用JMeter模拟了3000用户同时在线的场景,通过调整Tomcat线程池和MySQL连接池参数,最终使系统成功通过SLA要求