1. 项目背景与核心需求
松江大学城作为上海重要的高校聚集区,拥有超过10万师生规模的就餐需求。我在实地调研中发现,每到用餐高峰时段,各食堂普遍存在窗口排队时间过长、特色档口难找、新开店铺无人知晓等问题。传统的纸质导览图和口碑传播方式已经无法满足当代大学生对就餐效率和信息实时性的要求。
这个毕业设计项目正是要解决三个核心痛点:
- 信息不对称:师生无法实时掌握各餐厅的排队情况、特色菜品和优惠活动
- 决策效率低:选择就餐地点时缺乏数据支撑,往往随大流排队
- 资源不均衡:热门餐厅过度拥挤,部分优质档口却因位置偏僻而客流不足
系统采用SSM(Spring+SpringMVC+MyBatis)框架组合,这是目前Java Web开发中最成熟的方案之一。选择Java 8作为开发语言,既保证了校园环境的兼容性,又能充分利用Stream API等现代特性提升开发效率。特别值得说明的是,我们没有盲目追求最新的Java 17,因为学校教学服务器通常配置较为保守,Java 8的稳定性已经过充分验证。
2. 系统架构设计
2.1 技术栈选型依据
SSM框架组合在2026年仍然是高校毕业设计的黄金选择,原因有三:
- 教学延续性:大多数高校的Java课程仍以SSM为主线,便于答辩展示
- 资源丰富性:CSDN、GitHub上有大量可参考的SSM项目案例
- 功能完备性:Spring的IoC/AOP、SpringMVC的请求处理、MyBatis的ORM足以支撑中型系统
数据库选用MySQL 5.7而非8.0版本,这是考虑到:
- 学校实验室普遍安装的是5.7版本
- 5.7的JSON支持已经足够存储餐厅的扩展属性
- 配套的Navicat等工具对5.7的兼容性更好
2.2 核心模块划分
系统采用典型的三层架构,但针对就餐推荐场景做了特殊设计:
code复制表现层
├─ 微信小程序端(主入口)
├─ 管理后台Web端
├─ 食堂大屏展示端
业务层
├─ 推荐引擎(协同过滤算法)
├─ 实时排队预测
├─ 优惠聚合服务
数据层
├─ 餐厅基础数据库
├─ 用户行为日志
├─ 第三方数据(天气/课表)
特别设计了"冷启动解决方案":对于新注册用户,优先展示距离最近的三个食堂的今日特价菜,随着点击行为积累再逐步个性化。
3. 关键功能实现细节
3.1 动态权重推荐算法
核心推荐逻辑采用改进的加权评分模型:
code复制综合评分 =
0.4*口味匹配度(基于历史订单) +
0.3*实时排队系数(当前人数/平均处理速度) +
0.2*距离衰减因子(1/ln(距离+1)) +
0.1*随机探索项(防止信息茧房)
在MyBatis中通过动态SQL实现多条件查询:
xml复制<select id="selectRestaurants" resultMap="restaurantMap">
SELECT * FROM restaurants
<where>
<if test="category != null">
AND category = #{category}
</if>
<if test="minRating != null">
AND avg_rating >= #{minRating}
</if>
<choose>
<when test="sortBy == 'distance'">
ORDER BY distance ASC
</when>
<when test="sortBy == 'waiting'">
ORDER BY estimated_waiting ASC
</when>
<otherwise>
ORDER BY hot_score DESC
</otherwise>
</choose>
</where>
</select>
3.2 排队时长预测模型
通过食堂窗口的智能POS机采集真实交易数据,建立时间序列预测:
java复制// 简化版的指数平滑算法实现
public int predictWaiting(int windowId) {
List<Record> records = recordMapper.selectLastHour(windowId);
double alpha = 0.7; // 平滑系数
double prediction = records.get(0).getDuration();
for (int i = 1; i < records.size(); i++) {
prediction = alpha * records.get(i).getDuration()
+ (1 - alpha) * prediction;
}
return (int) Math.ceil(prediction);
}
实测发现,在午餐高峰时段(11:30-12:30)的预测误差能控制在±3分钟内,显著优于简单的移动平均算法。
4. 典型业务场景实现
4.1 课表联动推荐
通过对接学校教务系统(模拟接口),实现智能时段推荐:
java复制public List<Restaurant> recommendBySchedule(String userId) {
// 获取下一节课的时间和教学楼
Course nextCourse = courseService.getNextCourse(userId);
Building building = nextCourse.getBuilding();
// 计算可用就餐时间(减去步行时间)
int availableMinutes = nextCourse.getInterval()
- getWalkingTime(building);
// 查询符合条件的餐厅
return restaurantMapper.selectByCriteria(
new Criteria()
.nearBuilding(building)
.maxWaitingTime(availableMinutes - 10) // 保留10分钟缓冲
.withDiscount(true) // 优先推荐优惠活动
);
}
这个功能特别受学生欢迎,实测使用率超过62%。
4.2 食堂档口热度预警
为后勤管理处开发的可视化监控功能,采用ECharts实现实时热力图:
javascript复制// 前端关键代码
function updateHeatmap() {
axios.get('/api/stats/heatmap').then(response => {
const option = {
tooltip: {...},
visualMap: {
min: 0,
max: 100,
calculable: true,
inRange: {
color: ['#50a3ba', '#eac736', '#d94e5d']
}
},
series: [{
type: 'heatmap',
data: response.data.map(item => {
return [item.x, item.y, item.value];
})
}]
};
heatmapChart.setOption(option);
});
}
setInterval(updateHeatmap, 30000); // 每30秒更新
5. 开发中的典型问题与解决方案
5.1 MyBatis延迟加载异常
在实现复杂餐厅详情页时,遇到经典的N+1查询问题:
java复制// 错误示范
public Restaurant getDetail(Long id) {
Restaurant restaurant = restaurantMapper.selectById(id);
// 每次访问都会触发新的SQL
List<Comment> comments = restaurant.getComments();
return restaurant;
}
解决方案是在mapper.xml中配置嵌套结果映射:
xml复制<resultMap id="detailMap" type="Restaurant">
<collection property="comments" column="id"
select="selectCommentsByRestaurant"/>
</resultMap>
<select id="selectDetail" resultMap="detailMap">
SELECT * FROM restaurants WHERE id = #{id}
</select>
5.2 高并发下的缓存穿透
在促销活动期间,发现不存在的餐厅ID查询导致数据库压力骤增。最终采用布隆过滤器+空值缓存的组合方案:
java复制public Restaurant getById(Long id) {
// 第一层:布隆过滤器
if (!bloomFilter.mightContain(id)) {
return null;
}
// 第二层:Redis缓存
String key = "restaurant:" + id;
Restaurant restaurant = redisTemplate.opsForValue().get(key);
if (restaurant != null) {
return restaurant == NULL_OBJECT ? null : restaurant;
}
// 第三层:数据库查询
restaurant = restaurantMapper.selectById(id);
if (restaurant == null) {
redisTemplate.opsForValue().set(key, NULL_OBJECT, 5, MINUTES);
} else {
redisTemplate.opsForValue().set(key, restaurant, 30, MINUTES);
}
return restaurant;
}
6. 部署与性能优化
6.1 服务器配置建议
根据实测数据,给出不同用户规模下的最低配置要求:
| 并发用户数 | CPU | 内存 | JVM参数 | 平均响应时间 |
|---|---|---|---|---|
| <500 | 2核 | 2G | -Xms1g -Xmx1g | 300ms |
| 500-2000 | 4核 | 4G | -Xms2g -Xmx2g -XX:+UseG1 | 500ms |
| >2000 | 8核 | 8G | -Xms4g -Xmx4g -XX:+UseZ | 800ms |
特别提醒:学校的测试服务器往往配置较低,建议在application.properties中关闭不必要的功能:
properties复制# 关闭Actuator端点
management.endpoints.web.exposure.include=
# 使用内嵌Tomcat的简单配置
server.tomcat.max-threads=50
server.tomcat.min-spare-threads=5
6.2 数据库索引优化
通过EXPLAIN分析发现,最频繁的复合查询需要优化:
sql复制-- 原始执行计划显示全表扫描
EXPLAIN SELECT * FROM restaurants
WHERE distance < 500 AND rating > 4
ORDER BY waiting_time LIMIT 10;
-- 优化方案
ALTER TABLE restaurants ADD INDEX idx_compound (distance, rating, waiting_time);
优化后查询时间从1200ms降至80ms。同时建议对用户行为表进行按月分表:
java复制// 动态表名拦截器示例
public class MonthTableInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) {
String tableName = invocation.getArgs()[1].toString();
if (tableName.startsWith("user_behavior_")) {
String month = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
invocation.getArgs()[1] = tableName + "_" + month;
}
return invocation.proceed();
}
}
7. 答辩常见问题与应对
根据往届答辩经验,整理出高频问题及推荐回答策略:
-
为什么不用Spring Boot?
- 承认Boot的便捷性,但强调SSM更能展示对底层原理的理解
- 举例说明手动配置DataSource的过程体现了对连接池的理解
-
推荐算法是否考虑营养均衡?
- 说明当前版本侧重效率,但已预留营养分析接口
- 展示Nutrition类设计原型,表示这是下一步工作
-
数据真实性如何保证?
- 介绍采用的三种数据源:食堂POS系统、人工抽检、用户上报
- 展示数据清洗流程,特别是异常值处理代码片段
-
系统扩展性体现在哪?
- 指出模块化设计:推荐算法可替换为深度学习模型
- 展示扩展接口:IRecommendStrategy、IDataCollector等
建议在代码中预留几处明显的"设计亮点",比如:
java复制// 设计模式应用示例:策略模式切换推荐算法
public class RecommendContext {
private IRecommendStrategy strategy;
public void setStrategy(IRecommendStrategy strategy) {
this.strategy = strategy;
}
public List<Restaurant> recommend(User user) {
return strategy.execute(user);
}
}
8. 论文写作要点
技术类毕业论文需要特别注意以下几点:
-
系统架构图绘制规范
- 使用PlantUML绘制,避免Visio的版权问题
- 分层要清晰:表现层、业务层、数据层分开
- 标注关键技术选型,如Spring版本号
-
性能测试数据呈现
- 对比表格比纯文字更有说服力
- 包含压力测试工具(JMeter)的配置参数
- 典型错误:只给出最优数据,应包含调优过程
-
核心算法描述技巧
- 伪代码+流程图结合
- 对比经典算法与你的改进
- 标注算法来源(如引用某篇论文)
-
参考文献注意事项
- 必须包含近三年的文献
- 混合引用类型:教材+论文+技术文档
- 推荐引用Spring官方文档和MyBatis源码
特别提醒:论文中的代码片段要保持风格统一,建议使用:
java复制// 标准示例
public interface RestaurantService {
/**
* 根据用户位置获取推荐餐厅
* @param userId 用户ID
* @param count 推荐数量
* @return 推荐餐厅列表,按综合评分降序
*/
List<Restaurant> recommend(Long userId, int count);
}
9. 源码管理建议
毕业设计代码库的组织方式直接影响答辩印象:
- 标准目录结构
code复制├─ docs/ # 文档
│ ├─ 需求规格说明书.md
│ └─ 数据库设计.pdf
├─ src/
│ ├─ main/
│ │ ├─ java/ # 按package组织
│ │ └─ resources/
│ └─ test/ # 测试代码
├─ sql/ # 数据库脚本
│ ├─ ddl.sql # 表结构
│ └─ dml.sql # 示例数据
└─ pom.xml # Maven配置
-
Git提交规范
- 功能开发:feat: 添加餐厅详情页
- 缺陷修复:fix: 解决推荐算法空指针问题
- 文档更新:docs: 补充接口文档
- 避免:'update'、'fix bug'等模糊信息
-
.gitignore必备项
code复制# IDE
.idea/
*.iml
# 构建输出
target/
build/
# 本地配置
application-local.properties
建议在项目根目录添加README.md,包含:
- 项目简介(200字内)
- 快速启动指南(5步以内)
- 关键依赖版本(Java/MySQL等)
- 常见问题解答(3-5个)
10. 项目扩展方向
如果时间允许,可以考虑以下增值功能:
-
社交化功能
- 好友口味偏好对比
- 多人拼单推荐
- 就餐打卡分享
-
增值服务
- 食堂档口在线预约
- 特殊饮食需求定制
- 外卖自提柜集成
-
数据分析
- 菜品流行趋势预测
- 窗口服务效率评估
- 食堂人流热力图
技术实现上,这些扩展主要涉及:
- WebSocket实时通信
- 第三方支付接口对接
- 基于PyTorch的时序预测
我在开发后期尝试实现了简单的菜品图片识别功能,通过OpenCV+TensorFlow Lite实现:
python复制# 菜品识别原型代码
def recognize_dish(image_path):
model = load_model('dish_model.h5')
img = preprocess_image(image_path)
predictions = model.predict(img)
return decode_predictions(predictions)
这个功能虽然准确率只有75%左右,但在答辩演示时非常直观,容易获得加分。建议选择1-2个这样的亮点功能深入实现,不必追求大而全。
