
简介这是一套面向计算机专业本科生的高分毕业设计级在线厨艺平台实战项目适用于课程设计、期末大作业及毕设参考解决高校学生缺乏完整全栈开发案例的痛点。资源包共1043个文件涵盖88个Java后端核心类、145个JS与115个Vue前端组件、60个WXML与62个WXSS小程序页面样式、227个PNG与162个SVG界面资源以及2个SQL数据库脚本和3个BAT一键部署脚本完整支撑SSM微信小程序双端协同运行压缩包仅17.71MB轻量易部署。已有43人学习下载体现其在教学实践中的初步认可度。用户可直接获取经导师验收通过的可运行系统含前后端源码、MySQL5.7建库脚本、Navicat可视化配置说明、IDEA与微信开发者工具适配配置以及后台管理、菜谱浏览、视频教学、食谱分享、评论收藏等全部功能模块代码结构清晰、注释规范便于理解MVC分层逻辑与小程序与SpringBoot接口交互机制。1. 这不是又一个“微信小程序SSM”套壳项目它真能跑通厨艺教学闭环从菜谱发布、视频上传、用户跟练到学分认证你搜“Java SSM 微信小程序 毕业设计”满屏是带“高分”“源码数据库论文”的压缩包——但90%解压后连登录都卡在wx.login返回errCode: -1或者 MySQL 启动就报Unknown column create_time in field list。这个标题里的“在线厨艺平台”不一样它把「教」和「学」真正拆成了可验证的动作链——老师上传一道「宫保鸡丁」必须填刀工时长、火候等级、失败预警点比如“花生炒糊了”学生跟练时小程序端调用手机摄像头做3秒动作捕捉切配/翻炒/装盘后端用 OpenCV 简单帧差法比对关键帧节奏再结合手动提交的成品图打分。整套流程不依赖AI大模型纯靠 Java 业务逻辑MySQL 约束小程序原生能力落地。适合计算机专业本科生做毕设——代码量可控核心模块8000行、部署门槛低Windows Server JDK8 MySQL5.7 微信开发者工具、答辩时能现场演示「学生上传跟练视频→教师批注→生成学分证书」全链路。如果你正被导师催进度、被答辩组质疑“业务太假”这篇就是你打开压缩包前该先读的实操地图。2. 搭建环境避开 JDK8 与 MySQL5.7 的经典兼容雷区用最小配置跑通登录主流程2.1 JDK8 与 Tomcat8.5 的版本锁死策略很多同学用 JDK11 或 JDK17 解压即报错java.lang.UnsupportedClassVersionError因为 SSM 框架Spring 4.3.x SpringMVC 4.3.x MyBatis 3.4.x编译目标字节码是52.0JDK8。必须确认java -version # 输出必须是类似java version 1.8.0_291 # 若为 11 或 17请卸载并重装 JDK8u333推荐Tomcat 必须选 8.5.x非 9.x 或 10.x否则web.xml中servlet标签会因 schema 版本不匹配而启动失败。下载地址https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.99/bin/apache-tomcat-8.5.99.zip注意用 8.5.99 而非最新 8.5.100后者已移除对web.xmlDTD 的宽松解析。解压后修改conf/server.xml将默认端口8080改为8081避免与微信开发者工具调试端口冲突Connector port8081 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /提示不要用 IDE 内置 Tomcat 插件——它常自动升级 servlet-api 版本。务必用独立解压的 Tomcat 目录将webapps/ROOT清空后放入项目target/online-cooking.warMaven 打包后生成。2.2 MySQL5.7 安装与字符集强制统一微信小程序传中文菜名、步骤描述时若出现乱码90%是 MySQL 字符集没锁死。安装 MySQL5.7.44Windows 版后必须修改my.ini位于C:\ProgramData\MySQL\MySQL Server 5.7\[client] default-character-set utf8mb4 [mysql] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci init_connectSET NAMES utf8mb4 skip-character-set-client-handshake true重启 MySQL 服务后执行-- 验证全局设置 SHOW VARIABLES LIKE character_set%; SHOW VARIABLES LIKE collation%; -- 创建数据库时显式指定 CREATE DATABASE online_cooking DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;注意utf8mb4是必须项MySQL 的utf8实际只支持 3 字节 UTF-8不支持 emoji 和部分生僻汉字而小程序用户昵称、评论常含 emojiutf8mb4才是安全底线。2.3 微信小程序开发工具配置与 AppID 绑定下载「微信开发者工具 Stable 版v1.06.2312150」不要用 Nightly 版——其wx.getPhoneNumberAPI 在模拟器中返回undefined是已知 bug。新建项目时选择「小程序」→ 填写AppID需在微信公众平台注册小程序获取模板选「Empty Project」。关键配置在project.config.json{ description: online-cooking-miniprogram, packOptions: { ignore: [node_modules/**, dist/**] }, setting: { urlCheck: false, es6: true, enhance: true, postcss: true, minified: true, newFeature: true, coverView: true, scopeData: true, compileHotReLoad: false, // 关闭热重载避免 WebSocket 冲突 useCompiler: true } }提示urlCheck: false是必须项否则本地调试时wx.request会因未备案域名被拦截。此开关仅影响开发阶段上线前需在「小程序后台→开发管理→开发版本→服务器域名」中配置合法域名。3. 数据库设计用 7 张表撑起厨艺平台核心业务拒绝过度范式化3.1 核心表结构与字段约束逻辑本平台放弃“用户-角色-权限”三级RBAC改用user_type ENUM(teacher,student,admin)直接区分身份——毕设场景下简化权限控制且避免user_role表引发 N1 查询。关键表设计如下SQL 已适配 MySQL5.7表名主要字段设计意图索引建议t_userid BIGINT PK,open_id VARCHAR(64) NOT NULL,nick_name VARCHAR(32),avatar_url VARCHAR(255),user_type ENUM(teacher,student,admin),status TINYINT DEFAULT 1open_id为微信唯一标识不可为空且不设 UNIQUE同一用户可能换设备重新授权status0表示禁用账号KEY idx_openid (open_id),KEY idx_type_status (user_type,status)t_recipeid BIGINT PK,title VARCHAR(100) NOT NULL,teacher_id BIGINT NOT NULL,difficulty TINYINT CHECK(difficulty BETWEEN 1 AND 5),cooking_time INT COMMENT 分钟,cover_img VARCHAR(255)difficulty用 CHECK 约束而非外键避免冗余表cooking_time存整数分钟前端转为“X小时Y分钟”KEY idx_teacher (teacher_id),KEY idx_difficulty (difficulty)t_stepid BIGINT PK,recipe_id BIGINT NOT NULL,step_order TINYINT NOT NULL,content TEXT,video_url VARCHAR(255),duration_sec INT COMMENT 视频时长秒step_order保证步骤顺序不设 AUTO_INCREMENT避免插入中间步骤时序错乱video_url存 COS 或七牛云直链UNIQUE KEY uk_recipe_order (recipe_id,step_order),KEY idx_recipe (recipe_id)t_practice_recordid BIGINT PK,student_id BIGINT NOT NULL,recipe_id BIGINT NOT NULL,submit_time DATETIME,video_url VARCHAR(255),status ENUM(pending,graded,rejected) DEFAULT pendingstatus控制流程状态机pending→graded教师批阅→rejected退回重练无外键关联t_user防级联删除导致记录丢失KEY idx_student (student_id),KEY idx_recipe_status (recipe_id,status)注意所有VARCHAR字段长度按实际需求设定如nick_name最长32字微信昵称限制避免盲目设VARCHAR(255)浪费索引空间TEXT类型仅用于content等可能超长字段不用于搜索字段。3.2 初始化数据脚本的关键补丁解压包中的init.sql常遗漏两个致命补丁管理员账号初始化t_user表必须有一条user_typeadmin记录否则后台登录页login.jsp会因SELECT * FROM t_user WHERE user_typeadmin返回空而跳转失败。补丁 SQLINSERT INTO t_user (id, open_id, nick_name, avatar_url, user_type, status, create_time) VALUES (1, admin_openid_123, 系统管理员, https://example.com/avatar.png, admin, 1, NOW());微信登录临时码校验表小程序端wx.login()获取code后Java 后端需调用微信接口换取session_key。此过程需临时存储code防重放但原包常缺表。补丁 SQLCREATE TABLE t_login_code ( id BIGINT PRIMARY KEY AUTO_INCREMENT, code VARCHAR(32) NOT NULL COMMENT 微信登录code, expire_time DATETIME NOT NULL COMMENT 过期时间设为10分钟, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_code (code), KEY idx_expire (expire_time) );提示t_login_code表需配合定时任务清理过期记录每5分钟执行DELETE FROM t_login_code WHERE expire_time NOW()否则表体积膨胀。4. SSM 层关键业务实现用 MyBatis 动态 SQL 处理菜谱多条件检索绕开 PageHelper 分页陷阱4.1 菜谱检索接口动态 SQL 构建真实业务查询用户搜索“川菜”“30分钟内”“难度≤3”的菜谱传统WHERE拼接易出 SQL 注入而 PageHelper 的startPage在复杂条件组合下常漏查。本方案用 MyBatisif标签构建动态 SQL!-- RecipeMapper.xml -- select idselectByCondition resultTypecom.example.entity.Recipe SELECT r.*, u.nick_name AS teacher_name FROM t_recipe r LEFT JOIN t_user u ON r.teacher_id u.id WHERE 11 if testkeyword ! null and keyword ! AND (r.title LIKE CONCAT(%, #{keyword}, %) OR r.description LIKE CONCAT(%, #{keyword}, %)) /if if testdifficulty ! null and difficulty 0 AND r.difficulty lt; #{difficulty} /if if testmaxTime ! null and maxTime 0 AND r.cooking_time lt; #{maxTime} /if if testcategory ! null and category ! AND r.category #{category} /if ORDER BY r.create_time DESC /selectController 层接收参数并调用GetMapping(/recipes) public ResultListRecipe searchRecipes( RequestParam(required false) String keyword, RequestParam(required false, defaultValue 0) Integer difficulty, RequestParam(required false, defaultValue 0) Integer maxTime, RequestParam(required false) String category) { RecipeQuery query new RecipeQuery(); query.setKeyword(keyword); query.setDifficulty(difficulty); query.setMaxTime(maxTime); query.setCategory(category); ListRecipe recipes recipeService.selectByCondition(query); return Result.success(recipes); }逻辑说明RecipeQuery是专门封装查询条件的 DTO避免 Controller 参数过多lt;是 XML 实体转义防止解析错误LEFT JOIN关联教师昵称减少前端额外请求。4.2 微信手机号获取getPhoneNumber接口的安全校验链小程序端调用wx.getPhoneNumber获取加密数据后Java 后端必须完成三步校验解密 session_key用AESUtil.decrypt解密encryptedData需引入commons-codec依赖校验 signature将rawDatasession_keySHA1 加密与signature比对绑定手机号到用户更新t_user表的phone字段必须加乐观锁防止并发覆盖Update(UPDATE t_user SET phone #{phone}, update_time NOW() WHERE id #{userId} AND version #{version}) int updatePhone(Param(phone) String phone, Param(userId) Long userId, Param(version) Integer version);对应t_user表需增加version INT DEFAULT 0字段每次更新version version 1。若updatePhone返回 0说明版本冲突需重试或提示用户刷新页面。4.3 跟练记录状态机用数据库事务保证状态流转原子性学生提交跟练视频后状态从pending变为graded教师批阅时需同时更新t_practice_record和生成t_grade记录。MyBatis 无法跨表事务必须用 SpringTransactionalService public class PracticeService { Transactional(rollbackFor Exception.class) public void gradePractice(Long recordId, Integer score, String comment) { // 1. 更新跟练记录状态 PracticeRecord record new PracticeRecord(); record.setId(recordId); record.setStatus(graded); record.setScore(score); record.setComment(comment); record.setGradeTime(new Date()); practiceRecordMapper.updateByPrimaryKeySelective(record); // 2. 插入评分记录关联教师ID Grade grade new Grade(); grade.setRecordId(recordId); grade.setTeacherId(getCurrentTeacherId()); // 从 ThreadLocal 获取当前教师 grade.setScore(score); grade.setComment(comment); gradeMapper.insert(grade); // 3. 更新学生学分触发器或此处直接更新 Student student studentMapper.selectByPrimaryKey(record.getStudentId()); student.setCredit(student.getCredit() score * 10); // 每分折算10学分 studentMapper.updateByPrimaryKeySelective(student); } }注意Transactional必须作用于 public 方法且调用方不能是本类内部方法否则 AOP 失效getCurrentTeacherId()通过SecurityContextHolder或自定义TeacherContext获取避免硬编码。5. 小程序端避坑指南解决视频上传失败、顶部导航栏错位、登录态丢失三大高频问题5.1 视频上传失败wx.uploadFile的 size 与 duration 双重校验小程序上传跟练视频时常报错uploadFile:fail network error表面是网络问题实则因服务端未配置文件上传大小Tomcatconf/web.xml中max-file-size默认为 0禁用上传需改为multipart-config max-file-size104857600/max-file-size !-- 100MB -- max-request-size104857600/max-request-size file-size-threshold0/file-size-threshold /multipart-config前端未校验视频时长与大小用户上传 5 分钟 4K 视频必然失败。小程序端需预检// 上传前获取视频信息 wx.chooseVideo({ sourceType: [album, camera], compressed: true, // 强制压缩 maxDuration: 60, // 限制最长60秒 success: (res) { const { tempFilePath, duration, size } res; if (duration 60) { wx.showToast({ title: 视频不能超过60秒, icon: none }); return; } if (size 50 * 1024 * 1024) { // 50MB wx.showToast({ title: 视频不能超过50MB, icon: none }); return; } // 执行上传 wx.uploadFile({ url: https://your-domain.com/api/practice/upload, filePath: tempFilePath, name: video, formData: { recordId: this.data.recordId }, success: (uploadRes) { console.log(上传成功); } }); } });5.2 顶部导航栏高度异常statusBarHeight与navigationBarHeight的精准计算iPhone X 及以上机型状态栏高度为 44px但wx.getSystemInfoSync().statusBarHeight返回 44navigationBarHeight却是 88含状态栏导致自定义导航栏遮挡内容。正确做法// app.js 中全局注入 App({ onLaunch() { const systemInfo wx.getSystemInfoSync(); this.globalData.navBarHeight systemInfo.statusBarHeight 44; // 44 是导航栏固定高度 } }); // 页面 WXML 中使用 view classnav-bar styleheight: {{appData.navBarHeight}}px; view classstatus-bar styleheight: {{appData.statusBarHeight}}px;/view view classnav-title我的跟练/view /viewCSS 配置.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 999; } .status-bar { background-color: #f8f8f8; } .nav-title { height: 44px; line-height: 44px; text-align: center; font-weight: bold; }5.3 登录态丢失wx.checkSession失效后的优雅降级小程序wx.login获取的code5分钟过期session_key2小时过期但用户长时间停留后wx.checkSession()常返回fail。此时不应直接跳转登录页而应静默刷新调用wx.login()获取新code后端用新code换取新session_key并续期用户 tokenToken 续期机制后端LoginController增加/refresh-token接口接收旧 token 和新code验证open_id一致后颁发新 token前端拦截器在app.js的onShow中检查 token 有效期存于wx.setStorageSync剩余 30 分钟则主动刷新// app.js onShow() { const token wx.getStorageSync(token); const expireTime wx.getStorageSync(token_expire); if (token expireTime Date.now() expireTime - 30 * 60 * 1000) { // token 有效无需操作 } else { // 刷新 token wx.login({ success: (res) { wx.request({ url: https://your-domain.com/api/auth/refresh-token, method: POST, data: { code: res.code, token: token }, success: (r) { wx.setStorageSync(token, r.data.token); wx.setStorageSync(token_expire, r.data.expireTime); } }); } }); } }6. 毕设答辩实战技巧用 3 个可演示细节证明你真做过而不是套壳搬运6.1 演示「教师批阅跟练」时故意触发一次「退回重练」答辩时别只演示“提交→通过→发证书”的理想流。现场操作让学生账号提交一段明显切配不匀的视频用手机拍3秒土豆丝教师端批阅时输入评语“刀工不均匀请重练第2步”点击“退回”。此时学生端立即收到模板消息需提前在微信公众平台开通模板消息功能并在首页“待重练”Tab 出现新条目。这个细节证明你实现了状态机闭环pending→rejected→pending你配置了微信模板消息t_template_msg表及发送逻辑你处理了前端 Tab 切换的数据隔离tabbar页面用onShow重新拉取statusrejected记录。6.2 展示「MySQL 数据一致性」用SELECT ... FOR UPDATE模拟并发抢课厨艺平台有“热门课程限10人报名”需求原包常简单用UPDATE t_course SET enrolled_count enrolled_count 1 WHERE id ? AND enrolled_count 10这在并发下会超限。答辩时打开两个微信开发者工具实例同时点击同一门课的“报名”然后执行-- 查看锁等待 SELECT * FROM information_schema.INNODB_TRX; SELECT * FROM information_schema.INNODB_LOCK_WAITS;展示trx_stateLOCK WAIT的事务并说明你用SELECT * FROM t_course WHERE id 123 FOR UPDATE加行锁再判断enrolled_count确保绝对不超限。这比讲“我用了事务”有力十倍。6.3 用EXPLAIN分析慢查询现场优化一条 SQL找到t_recipe表上未走索引的查询如按category和difficulty组合查询执行EXPLAIN SELECT * FROM t_recipe WHERE category 川菜 AND difficulty 3;若typeALL全表扫描当场添加复合索引ALTER TABLE t_recipe ADD INDEX idx_category_diff (category, difficulty);再执行EXPLAIN展示typeref和keyidx_category_diff。评委立刻明白你懂索引原理不是只会复制粘贴。我带过三届毕设最常看到学生答辩时被问“你这个分页怎么做的”一说“用 PageHelper”评委就摇头——因为 PageHelper 的count查询在复杂条件组合下常不准。后来我要求学生必须手写COUNT(*)子查询哪怕多写20行代码。技术深度不在框架多炫而在你敢不敢掀开它的底裤看一眼。希望帮到你。本文还有配套的精品资源点击获取