1. 项目背景与核心价值
高校食堂每天面临数千名学生的就餐需求,传统人工推荐方式存在效率低下、营养不均衡、排队时间长等问题。这套基于SpringBoot+Vue+MyBatis的企业级饮食推荐系统,通过算法分析学生历史就餐数据、体质特征和实时人流,实现了个性化餐品推荐与智能分流。
我在某高校信息化部门实施这套系统时发现,上线后食堂窗口平均排队时间减少了37%,食材浪费率下降28%。系统核心创新点在于:
- 多维度用户画像构建(消费习惯、过敏原、运动量等)
- 实时库存与客流联动算法
- 支持移动端扫码点餐与营养报告生成
2. 技术架构设计解析
2.1 后端SpringBoot实现要点
采用SpringBoot 2.7.x + JDK1.8组合,关键配置如下:
java复制// 多数据源配置示例(主库+营养分析库)
@Configuration
@MapperScan(basePackages = "com.canteen.mapper")
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.nutrition")
public DataSource nutritionDataSource() {
return DataSourceBuilder.create().build();
}
}
特别要注意的是:
食堂场景存在明显的就餐高峰时段,必须配置HikariCP连接池的maxPoolSize为CPU核心数*2+1,我们在生产环境设置的是minimumIdle=5, maximumPoolSize=25
2.2 Vue前端工程化实践
前端采用Vue3+Element Plus,通过动态路由实现权限控制:
javascript复制// 路由守卫示例
router.beforeEach((to, from, next) => {
const userRole = store.getters.role
if (to.meta.roles && !to.meta.roles.includes(userRole)) {
next('/403')
} else {
next()
}
})
实测中发现的问题:
- 移动端Chrome浏览器下,连续快速点击会导致重复提交
- 解决方案:采用lodash的debounce函数包装提交方法
2.3 MyBatis优化技巧
针对高并发查询场景,我们做了这些优化:
- 二级缓存配置(注意食堂数据实时性要求高,设置flushInterval=300000)
xml复制<cache eviction="LRU" flushInterval="300000" size="1024"/>
- 使用MyBatis-Plus的LambdaQueryWrapper避免SQL注入:
java复制LambdaQueryWrapper<Dish> wrapper = Wrappers.lambdaQuery();
wrapper.eq(Dish::getCanteenId, canteenId)
.between(Dish::getPrice, minPrice, maxPrice);
3. 数据库设计与性能调优
3.1 MySQL表结构设计
核心表关系图:
code复制用户表(user)
│
└───┬── 消费记录(consumption)
│
└───┬── 菜品评价(dish_comment)
│
└───┬── 菜品(dish)
│
└─── 食堂窗口(window)
重点表字段示例(菜品表):
sql复制CREATE TABLE `dish` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_bin NOT NULL,
`window_id` bigint NOT NULL,
`price` decimal(10,2) NOT NULL,
`calorie` int DEFAULT NULL COMMENT '千卡',
`allergens` json DEFAULT NULL COMMENT '过敏原JSON数组',
`sales_count` int DEFAULT '0',
`recommend_score` decimal(3,2) DEFAULT '0.00',
PRIMARY KEY (`id`),
KEY `idx_window` (`window_id`),
KEY `idx_recommend` (`recommend_score`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
3.2 查询性能优化案例
在推荐算法核心查询中,我们发现当并发量>500时响应时间从200ms飙升到2s。通过EXPLAIN分析发现未使用复合索引:
优化前:
sql复制SELECT * FROM dish
WHERE calorie BETWEEN 300 AND 500
AND allergens NOT LIKE '%花生%'
ORDER BY recommend_score DESC
LIMIT 10;
优化方案:
- 创建复合索引:
ALTER TABLE dish ADD INDEX idx_nutrition (calorie, recommend_score) - 改写查询:
sql复制SELECT * FROM dish FORCE INDEX(idx_nutrition)
WHERE calorie BETWEEN 300 AND 500
AND JSON_CONTAINS(allergens, '"花生"') = 0
ORDER BY recommend_score DESC
LIMIT 10;
优化后TPS从120提升到2100,这是我们在压力测试中得到的数据。
4. 典型业务场景实现
4.1 实时推荐算法流程
mermaid复制graph TD
A[获取用户特征] --> B{是否首次就餐}
B -->|是| C[按食堂热门推荐]
B -->|否| D[查询历史偏好]
D --> E[结合当前库存]
E --> F[过滤过敏原]
F --> G[计算营养均衡]
G --> H[生成TOP5推荐]
实际代码实现时需要注意:
- 使用Redis缓存用户最近3次就餐记录
- 库存数据需要本地缓存+数据库双重校验
- 过敏原检查要放在最后一步避免误判
4.2 支付对账模块设计
高校场景特有的需求:
- 支持校园卡/微信/支付宝三端支付
- 每日自动对账(处理退款、补贴等)
核心代码结构:
code复制payment/
├── strategy/
│ ├── CampusCardStrategy.java
│ ├── WechatStrategy.java
│ └── AlipayStrategy.java
├── reconciler/
│ ├── DailyReconciler.java
│ └── ExceptionHandler.java
└── service/PaymentService.java
我们在对账逻辑中特别处理了:
- 校园卡每日补贴额度计算(22:00重置)
- 微信支付异步通知的幂等处理
- 退款原路返回的渠道判断
5. 部署与运维实战
5.1 生产环境部署方案
推荐使用Docker Compose部署:
yaml复制version: '3'
services:
app:
image: openjdk:8-jdk-alpine
ports:
- "8080:8080"
volumes:
- ./config:/config
depends_on:
- redis
- mysql
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6-alpine
ports:
- "6379:6379"
关键配置经验:
- JVM参数:-Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m
- MySQL的innodb_buffer_pool_size设置为物理内存的70%
- Redis配置maxmemory-policy=allkeys-lru
5.2 监控与日志方案
我们采用的监控组合:
- SpringBoot Actuator + Prometheus + Grafana
- 关键业务日志通过Logstash接入ELK
- 自定义健康检查接口:
java复制@GetMapping("/health")
public ResponseEntity<Map<String, Object>> healthCheck() {
Map<String, Object> result = new HashMap<>();
result.put("status", "UP");
result.put("db", checkDatabase());
result.put("redis", checkRedis());
result.put("recommendQueue", queueMonitor.getQueueSize());
return ResponseEntity.ok(result);
}
在真实运维中发现:
- 就餐高峰时段需要特别监控Redis内存使用率
- MyBatis的慢查询日志要定期分析(超过500ms的SQL)
- Vue前端需要监控Chrome内存泄漏问题
6. 扩展开发建议
基于现有系统可以进一步扩展:
- 人脸识别支付模块(需对接校园统一身份认证)
- 营养健康分析报告PDF生成
- 供应链管理系统对接(自动补货预测)
- 微信小程序端开发(使用Uni-app跨平台方案)
在开发微信小程序时我们遇到的典型问题:
- 扫码点餐的二维码需要特殊编码(包含桌号信息)
- 支付成功后需要主动查询支付状态(不能依赖回调)
- 图片上传需要压缩到200KB以下
这套系统经过三个学期的运行迭代,目前日均处理订单1.2万+,高峰期QPS达到1500。最大的收获是认识到:在校园场景下,稳定性比功能丰富度更重要,所有技术选型都要服务于这个目标。
