ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Spring Boot的厨房达人美食分享平台设计与实现

基于Spring Boot的厨房达人美食分享平台设计与实现 做Java课程设计或者毕业设计的人估计这段时间都在满世界翻“基于Spring Boot”的项目。今天聊的这个项目是“厨房达人美食分享平台系统”说白了就是一个美食菜谱笔记分享社区——用户可以注册登录、发布自己的菜谱、看看别人家的拿手菜还能点赞收藏评论管理者在后台做审核和分类管理。技术栈是经典的Java Spring Boot MySQL前台和后台都在同一个Spring Boot工程里配的是Thymeleaf模板引擎。这套组合特别适合当毕设或课设也适合想快速把Spring Boot全流程跑通的开发者拿来练手。这个项目表面看就是常见的CRUD但把用户、菜谱、评论、点赞、收藏这几条业务线串起来之后你会发现它几乎覆盖了Spring Boot开发的核心内容拦截器、文件上传、分页查询、事务控制、全局异常处理、富文本、角色权限。把这些代码彻底吃透后面再写管理系统、社区类项目基本就是换个业务皮。1. 项目定位与整体设计思路1.1 为什么是“美食分享”这个业务场景选项目题目的时候很多人会在“图书管理系统”和“新闻发布系统”之间反复横跳最后答辩被老师问两句就卡壳。美食分享平台这个题好在哪儿首先业务链路足够完整用户体系有注册登录核心业务有菜谱的增删改查互动体系有评论点赞收藏后台有用户管理和菜谱审核。其次可视化效果好——美食图片天然有观赏性首页做出来视觉效果比一堆表格强得多演示观感好答辩也更好讲。而且这个场景的业务复杂度控制得很好。对比电商系统没有订单、库存、支付那一堆令人头大的状态流转对比社交系统没有好友关系、私信、消息推送这些难点。菜谱分享的逻辑非常直接发布、浏览、互动、管理正好落在一个“麻雀虽小五脏俱全”的区间。既能体现工作量又不会把你拖进深坑对时间有限的课设党非常友好。1.2 核心功能模块怎么拆从产品角度这个平台分成前台展示和后台管理两大部分。前台用户端的核心功能注册与登录用户名加密码登录基于Session会话管理登录后才能发布菜谱和互动。菜谱浏览首页展示最新推荐菜谱支持分类浏览和关键词搜索。菜谱详情展示封面图、食材清单、制作步骤、作者信息底部是评论区、点赞和收藏按钮。菜谱发布与编辑登录用户可发布菜谱上传封面、填食材、写步骤支持修改和删除自己的菜谱。个人中心维护个人信息查看自己发布的菜谱、我的收藏、我的点赞。互动功能点赞和取消点赞、收藏和取消收藏、评论和回复评论。后台管理端的核心功能管理员登录与权限校验普通用户和管理员角色分离后台入口单独控制。菜谱管理审核新发布菜谱、上下架、删除违规内容。用户管理查看用户列表、禁用或启用账号。分类管理对菜谱分类做增删改查前台导航栏同步更新。功能清单别一开始就铺满。我的建议是先把“发布菜谱 列表详情 评论点赞收藏 后台管理”这条主线跑通再考虑数据统计、个人主页、消息通知这些扩展。时间永远不够用主线不稳的时候不要追花瓣上的功能。1.3 技术栈选型经典组合的底气这个项目用的是Spring Boot 2.7.x MyBatis Plus MySQL 8.0 Thymeleaf Bootstrap。这套组合几乎是国内高校项目的默认配置为什么大家这么选Spring Boot把配置疯狂简化一个main方法就能起服务内嵌Tomcat不需要额外装服务器对新手极其友好。MyBatis Plus在MyBatis基础上把单表CRUD、分页、条件构造器都封装好了写业务代码的时间能省一大半——尤其分页查询一行代码搞定比手写XML舒服太多。Thymeleaf配合Bootstrap做前台和后台页面服务端渲染页面数据直接拼在HTML模板里上课答辩也好解释。MySQL开源免费、资料全遇到问题搜一下就有答案。有同学问为什么不前后端分离、不搞Vue。对这个体量的项目完全不必要。前后端分离意味着多维护一套Node环境、一套接口文档、一套跨域配置答辩时还会被追问JWT怎么设计、token过期怎么处理等于给自己挖坑。服务端渲染的Thymeleaf项目逻辑都在一个工程里开发调试链路短出问题排查快。做毕设讲究的是按时交付、稳过答辩不是追求技术炫技。2. 数据库设计与核心模型2.1 核心表怎么设计数据模型不复杂但如果表设计得不行后面写Mapper和业务代码会反复打补丁。我按一个稳定结构来讲。用户表user字段类型说明idbigint主键自增usernamevarchar(50)用户名唯一索引passwordvarchar(100)加密后的密码nicknamevarchar(50)昵称默认同用户名avatarvarchar(255)头像地址roletinyint1管理员 / 0普通用户statustinyint1正常 / 0禁用create_timedatetime注册时间菜谱表recipe字段类型说明idbigint主键自增user_idbigint发布者IDtitlevarchar(100)菜名加索引category_idbigint分类IDcover_imgvarchar(255)封面图ingredientstext食材清单descriptionvarchar(255)简介stepstext制作步骤difficultyvarchar(10)难度cook_timevarchar(20)烹饪时长like_countint点赞数favorite_countint收藏数view_countint浏览数statustinyint0待审核 / 1已发布 / 2已下架create_timedatetime发布时间评论表commentid、recipe_id、user_id、content、parent_id、create_time。点赞表like_recordid、user_id、recipe_id、create_time。收藏表favorite_collectid、user_id、recipe_id、create_time。另外补一张分类表category三个字段id、name、sort。2.2 为什么用逻辑外键而不是物理外键表之间有关联但实际开发中强烈建议不要加物理外键约束。很多课设教材喜欢在表上画外键看起来规范实际是给自己上枷锁。删除数据会被外键约束挡住报出莫名其妙的“Cannot delete or update a parent row”错误修改主键也要级联处理业务上毫无收益。正确做法是只在字段上体现关联比如recipe.user_id业务逻辑里自己控制完整性。查询时用关联查询或分两次查完全不存在一致性问题。面试官和老师更看重你知不知道这个取舍而不是表里有没有那个约束。如果你非要说外键就回答“毕设为了演示和修改方便采用逻辑外键生产环境可以按需补充物理约束”这个回答反而显得懂行。2.3 冗余字段和索引的处理like_count、favorite_count、view_count这些计数我建议直接冗余在recipe表上。点赞时先查有没有这条记录没有就插入记录同时update recipe表的like_countlike_count1取消点赞就删除记录、计数减一。这个思路简单可控列表页按点赞数排序时直接order by like_count不需要group by子查询性能好得多。索引方面点赞表(user_id, recipe_id)、收藏表(user_id, recipe_id)必须加联合唯一索引这是防重复点赞收藏的关键。recipe表的title加普通索引配合搜索接口category_id也建议加索引因为分类浏览是高频操作。真正等数据量大到需要优化的时候你早就答辩完了别过度设计索引够用就行。3. 核心功能实现细节3.1 登录会话怎么控制拦截器方案这个项目用的就是最经典的Session配合拦截器方案。用户登录成功后把User对象塞进Session同时写一个LoginInterceptor拦截器注册到Spring MVC的拦截器链里public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); User user (User) session.getAttribute(loginUser); if (user null) { // 判断是不是Ajax请求前端页面重定向接口直接返回401 if (isAjax(request)) { response.setStatus(401); } else { response.sendRedirect(/login); } return false; } return true; } }注册拦截器时静态资源一定要放行否则css、js、图片全被拦住页面直接裸奔Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /register, /css/**, /js/**, /images/**, /, /index, /recipe/**, /category/**); } }这里有个经验拦截器放行的路径要反复对照前端页面检查。我第一次写这个项目时忘了放行/images/**结果整个页面一张图都显示不出来排查了半天才发现图片请求全部被重定向到登录页了。管理员权限单独用AdminInterceptor判断Session里用户的role字段不是管理员就返回“无权限”页面。别把管理员权限判断混在登录拦截器里两个拦截器分开写职责清晰后面改起来也方便。3.2 图片上传本地存储加静态资源映射毕设项目上传图片最省事的方案是存本地磁盘然后在Spring Boot里配一个静态资源映射。首先在application.yml里配置上传大小限制spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB然后是自定义上传目录upload: path: /data/upload/配置资源映射让外部能访问到上传的文件Configuration public class UploadConfig implements WebMvcConfigurer { Value(${upload.path}) private String uploadPath; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: uploadPath); } }Controller里接收MultipartFile用UUID重命名文件名防止中文文件名和重名造成的乱码与覆盖String originalFilename file.getOriginalFilename(); String suffix originalFilename.substring(originalFilename.lastIndexOf(.)); String filename UUID.randomUUID().toString().replace(-, ) suffix; file.transferTo(new File(uploadDir filename));这里有个关键细节先保存文件、再写数据库记录如果数据库写入失败要把磁盘上的文件删掉否则会出现一堆孤儿文件。事务管不到文件系统这个坑必须自己在代码里补上。3.3 点赞收藏评论防重复、计数同步点赞接口的核心逻辑Transactional public Result like(Integer recipeId, Integer userId) { LambdaQueryWrapperLikeRecord wrapper new LambdaQueryWrapper(); wrapper.eq(LikeRecord::getRecipeId, recipeId) .eq(LikeRecord::getUserId, userId); // 1. 已点赞则取消 if (likeRecordMapper.selectCount(wrapper) 0) { likeRecordMapper.delete(wrapper); recipeMapper.updateLikeCount(recipeId, -1); return Result.ok(已取消点赞); } // 2. 未点赞则新增 LikeRecord record new LikeRecord(); record.setRecipeId(recipeId); record.setUserId(userId); likeRecordMapper.insert(record); recipeMapper.updateLikeCount(recipeId, 1); return Result.ok(点赞成功); }记得在Service方法上加Transactional因为“插入点赞记录”和“更新计数”必须同时成功或同时失败。很多同学忘了这个注解结果点赞记录写进去了计数没变或者计数变了记录没写进去查问题查到怀疑人生。updateLikeCount用MyBatis Plus的Update注解写自定义SQLUpdate(UPDATE recipe SET like_count like_count #{delta} WHERE id #{recipeId}) int updateLikeCount(Param(recipeId) Integer recipeId, Param(delta) int delta);评论功能分顶级评论和回复前端点击“回复”按钮时带上parent_id。展示时按create_time倒序查出来前端渲染成“主评论加回复列表”就行。毕设级别推荐平铺渲染一个循环搞定别把评论做成递归树给自己找麻烦。3.4 菜谱发布与富文本的取舍菜谱的“制作步骤”这个字段很多同学一开始就想上富文本编辑器插图片插视频做得花里胡哨。我的建议是分情况如果想让Demo页面好看可以用wangEditor这种轻量级富文本后端存储HTML展示时用Thymeleaf的th:utext输出。但如果你是新手对XSS没什么概念强烈建议用固定模板一个步骤一行文本逻辑短、代码稳还不引入安全问题。用富文本不是不行但至少要懂转义。Thymeleaf里用th:utext输出富文本内容时如果没有对HTML做白名单过滤用户提交的
返回列表