做Java毕设这几年,见得最多的题目类型就是各种信息管理系统。今天想聊的这个题目——《基于Spring Boot的老年人膳食营养服务网站管理系统》,名字虽然长,但它把Java Web里的增删改查、登录鉴权、文件上传、前后端交互这些东西全串起来了,而且业务场景非常清晰,从需求分析到论文撰写都有东西可写。
这个系统说到底就解决两件事:一是把老年人的饮食健康档案管起来,二是根据档案里的身体状况给出合理的膳食营养建议。目标用户虽然是老年人,但真正天天用系统的是家属、护理人员和营养师,这就决定了角色的权限设计有层次、有区分度,很适合作为计算机专业的毕业设计题目。
这篇文章我会把这个题目从选题逻辑、技术栈选型、数据库设计、核心功能实现,到前后端联调、打包部署、文档撰写的完整链路都过一遍。正在做Java毕设、尤其对Spring Boot项目感兴趣的同学,可以直接按这个思路落地,省掉不少走弯路的时间。
1. 选题前必须想清楚的事:这题目到底在做什么
1.1 业务底色:不是普通的信息管理
很多人看到“管理系统”四个字,下意识就以为是普通的增删改查套页面,随便拿一个图书管理之类的改改就交差。但这个题目不一样,老年人膳食营养服务网站管理系统,本质上是“信息档案 + 决策建议”的复合型系统,既要管好老人的饮食健康档案,又要能根据档案内容生成合理的膳食计划。
具体的使用场景可以这样描述:社区养老机构或者医院营养科里,护理人员把老年人的身高、体重、慢病情况、过敏食物等信息登记成电子档案;营养师基于这份档案,从食材库和食谱库里筛选合适的膳食方案;老人和家属则通过前端页面查看推荐食谱、获取营养科普资讯、在线向营养师咨询。整个流程里,数据是不断流动的,状态是有流转过程的,而不是几个孤立的表单堆在一起。
再梳理一下角色:
- 老年人/家属:维护个人健康档案、查看膳食计划、收藏食谱、在线咨询。
- 营养师:查看档案并评估、制定和审核膳食计划、回复咨询。
- 系统管理员:维护食材库、食谱库、营养资讯、用户账号、平台数据。
- 游客:浏览公开的资讯内容和食材科普文章。
把角色和动作画成用例图,毕业设计的架子一下就出来了。而且这个题目还自带一个业务亮点,就是“一人一策”的膳食管理,相比普通的管理系统更有差异化,写论文也更容易找切入角度。
1.2 项目里必须做好的三个隐藏考点
在答辩评委眼里,这种题目通常有三个躲不开的技术考点,选题前最好心里有数。
第一个是多表关系的掌握。一个老年人有多条健康档案记录,因为身高体重会定期更新;一个食谱由多个食材组成,食材也会出现在多个食谱中,是典型的多对多关系;一个膳食计划关联某位老人的某个餐次。这些一对多、多对多关系,必须落到数据库设计和ORM查询里,这也是Java Web课程的核心内容。
第二个是权限路由与控制。不同角色看到的菜单不一样,接口也不能越权访问。老人只能看自己的档案和计划,营养师只能处理待评估档案,管理员才能维护系统基础数据。这需要登录认证、拦截器或者权限框架的配合。
第三个是状态流转设计。档案要经过待提交、待审核、已通过等状态;膳食计划要经历草稿、待确认、已发布;资讯内容要区分已发布和已下线。状态字段加条件查询,配合前端按钮的动态渲染,就是一个完整的业务闭环,这一块老师们最容易追问。
除了这三个考点,还有一个隐藏加分点,就是系统的业务逻辑里必须要有“营养计算”。比如BMI指数计算、每日目标热量估算、食谱总热量汇总。这可不是普通管理系统里简单的加减乘除,里面涉及公式、精度处理、业务判断,写进论文的创新点也站得住脚。
2. 技术栈与整体架构:Spring Boot为主线的合理选型
2.1 为什么选Spring Boot而不是SSH/SSM
如果你去搜Java毕设相关资料,会发现很多老教程还在讲SSM甚至SSH,很多学校教材也滞后。不是说这些技术不能做项目,而是从零开始配置一堆XML实在太折腾,光是Spring、SpringMVC、MyBatis的配置文件就能劝退一大批人。
Spring Boot把内嵌Tomcat、自动配置、依赖管理都处理好了,一个Starter就能拉进大部分需要用到的依赖,非常适合毕设这种以“快速实现核心功能”为目标的场景。至于题目标注里的“Java Web”,落到项目里就是Spring MVC这一层:Controller接收前端请求,Service处理业务逻辑,Mapper访问数据库,这个三层架构学校老师最认可,论文里也好画图说明。
具体版本选择,我的建议比较明确:
| 对比项 | Spring Boot 2.7.x | Spring Boot 3.x |
|---|---|---|
| JDK版本 | JDK 8/11均可 | 需要JDK 17及以上 |
| 包命名空间 | javax.* | jakarta.* |
| 网上资料 | 最多,踩坑有参考 | 相对少 |
| MyBatis-Plus兼容 | 稳定成熟 | 需3.5.3以上并调整 |
| 毕设推荐程度 | 首选 | 除非老师强制要求 |
我实测下来最稳的组合是Spring Boot 2.7.18 + MySQL 8 + MyBatis-Plus 3.5.3 + JDK 8。这套搭配网上资料多,遇到问题基本都能搜到参考。如果学校要求必须用Spring Boot 3,也不用慌,核心区别就是包名从javax换成了jakarta,配置上注意版本对应即可,很多人说“springboot版本太高”就是栽在这个点上。
2.2 模板渲染还是前后端分离
这里有一个很常见的纠结:用Thymeleaf服务端渲染,还是用Vue做前后端分离。我的建议是走折中方案,看起来是前后端分离,实际上还是单jar部署,演示的时候最省事。
| 方案 | 优点 | 缺点 |
|---|---|---|
| Thymeleaf模板 | 开发快、无跨域、部署简单 | 交互体验一般,页面现代感弱 |
| 前后端分离部署 | 职责清晰、加分项明显 | 要处理跨域、多进程部署麻烦 |
| 折中:前端构建后放static | 有分离开发体验,又打包成单jar | 刷新404需要处理 |
我这套项目实际用的技术组合是:Spring Boot 2.7.18 + MyBatis-Plus + MySQL 8 + Vue 2 + Element UI + Axios。登录认证走JWT,权限控制用拦截器,没有过度设计,代码量也控制在合理范围内。全部完成后,执行前端构建,再把生成结果放入Spring Boot的静态资源目录,整体打包成一个jar,答辩现场启动一条命令就能跑起来。
2.3 工程目录与Maven依赖
工程结构非常重要,建议从一开始就按职责分好包,不要把所有类都堆在controller包里。我习惯的分层结构如下:
com.example.diet ├── controller // 接口层,放RESTful接口 ├── service // 业务层,核心业务逻辑 ├── mapper // 数据访问层,MyBatis-Plus的Mapper接口 ├── entity // 数据库对应的实体类 ├── config // 跨域、拦截器、MyBatis-Plus配置 ├── common // 统一返回、异常处理、常量 └── util // JWT、文件上传等工具类对应的Maven核心依赖如下:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> </dependencies>这里有一个很多人踩过的坑:jjwt 0.9.1在JDK 17下会报模块化相关错误,如果用了Spring Boot 3.x和JDK 17,需要换到jjwt 0.11.x系列,并拆成api、impl、jackson三个依赖。为了避免折腾,直接用JDK 8是最舒服的。
配置好MyBatis-Plus的分页插件和管理员端的Mapper扫描,基础骨架就算搭好了。同时记得在application.yml里开启下划线转驼峰,否则数据库字段create_time和实体属性createTime的映射会一直对不上。
3. 数据库设计与核心表结构拆解
3.1 从业务流程倒推数据实体
设计数据库的第一步不是直接建表,而是先把一次完整业务流程走一遍。以这个系统为例:老年人注册账号,填写或更新健康档案,营养师查看档案并评估,生成膳食计划,老人端查看计划并收藏食谱,管理员在后台维护食材和食谱,营养师回答咨询。流程里的关键名词就是账号、档案、食材、食谱、计划、资讯、留言,每个名词基本对应一张表。
关系上说得清楚一些:
- 一个用户可以有多条健康档案历史,因为身体数据会按周期更新,查询时取最新一条即可。
- 一个食谱由多个食材组成,一个食材可出现在多个食谱中,所以需要中间表避免字段冗余。
- 一条膳食计划针对某位老人的某个餐次,关联到一个具体食谱。
- 营养师对档案的评估独立存放在评估表中,保留完整历史。
3.2 核心表的字段设计
核心表的结构可以按下表来设计,字段不贪多,够用就好。
| 表名 | 说明 | 关键字段 |
|---|---|---|
| sys_user | 用户账号表 | id, username, password, real_name, role, phone, avatar, status, create_time |
| elder_profile | 老年人健康档案 | id, user_id, name, gender, age, height, weight, bmi, chronic_disease, allergy_foods, eating_habit, status |
| food | 食材库 | id, name, category, calorie, protein, fat, carbohydrate, fiber, unit |
| recipe | 食谱表 | id, name, type, meal_time, total_calorie, cover, description, status |
| recipe_item | 食谱与食材中间表 | id, recipe_id, food_id, quantity |
| diet_plan | 膳食计划表 | id, elder_id, plan_date, recipe_id, meal_type, total_calorie, advice, status |
| health_assessment | 健康评估表 | id, elder_id, nutritionist_id, result, suggestion, create_time |
| article | 营养资讯表 | id, title, cover, content, author_id, publish_time, status |
| consult | 在线咨询表 | id, user_id, nutritionist_id, question, answer, create_time |
| favorite | 收藏表 | id, user_id, recipe_id, create_time |
有几个字段设计是答辩时值得拿出来说的。第一,密码字段存放的是BCrypt哈希值而不是明文密码,这是很多毕设的扣分重灾区。第二,热量、体重等数值字段统一用DECIMAL类型,不用float和double,避免精度误差。第三,档案表按user_id和create_time排序取最新,保留历史版本记录,体现设计思路。
3.3 营养计算与数据精度问题
食材表里的热量字段以每100克对应的千卡数为准,因为这是营养学里最常用的口径。计算食谱总热量时,根据中间表里每种食材的克数做换算累加。
public BigDecimal calculateTotalCalorie(Long recipeId) { List<RecipeItem> items = recipeItemMapper.selectList( new LambdaQueryWrapper<RecipeItem>() .eq(RecipeItem::getRecipeId, recipeId)); BigDecimal total = BigDecimal.ZERO; for (RecipeItem item : items) { Food food = foodMapper.selectById(item.getFoodId()); BigDecimal foodCal = food.getCalorie() .multiply(BigDecimal.valueOf(item.getQuantity())) .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); total = total.add(foodCal); } return total; }这里用BigDecimal而不是double,核心原因是浮点数在连续累加时会积累误差,而营养热量结果在生活中可能被直接当作参考数值,必须稳定可靠。BigDecimal可以显式指定精度和舍入方式,这是代码层面一个很好的细节。
至于老年人每日目标热量,可以用基础代谢公式估算。男性基础代谢约等于66 + 13.7×体重公斤数 + 5×身高厘米数 - 6.8×年龄,女性约等于655 + 9.6×体重公斤数 + 1.8×身高厘米数 - 4.7×年龄,再乘以一个1.2到1.5的活动系数。这个公式不用做到临床级别的精确,但它会让整个系统看起来是真的懂业务,而不只是一个空壳页面。
4. 核心功能模块的实现细节
4.1 登录认证与角色权限实现
用户表里的role字段只有三种取值:ADMIN、DIETITIAN、ELDER。登录成功后用JWT生成token,前端保存在localStorage里,每次请求放到Authorization请求头。后端使用拦截器统一解析token,再根据路径前缀判断角色权限。
@Component public class AuthInterceptor implements HandlerInterceptor { private final JwtUtil jwtUtil; public AuthInterceptor(JwtUtil jwtUtil) { this.jwtUtil = jwtUtil; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { return true; } String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token) || !jwtUtil.validateToken(token)) { response.setStatus(401); return false; } request.setAttribute("userId", jwtUtil.getUserId(token)); request.setAttribute("role", jwtUtil.getRole(token)); return true; } }权限判断我建议直接在拦截器里写路径规则,例如/admin/**需要ADMIN角色,/nutritionist/**需要DIETITIAN或ADMIN,其他接口至少要求登录。这种方式简单直观,论文里也好描述,不需要引入过重的Spring Security配置。如果想把权限做得更细致,可以再配合自定义注解和切面,但毕设阶段拦截器已经完全够用。
需要注意注册接口里的密码加密,用BCryptPasswordEncoder生成哈希,登录时调用matches方法校验。如果项目里所有密码都是明文存取,这基本就是答辩时的一个硬伤,一定要提前修正。
4.2 老年人健康档案管理
档案管理是系统的信息底座。用户注册后,需要创建一份老年人健康档案,包含姓名、性别、年龄、身高、体重、慢病标签、过敏食物、饮食习惯等字段。保存档案时,服务端自动计算BMI体质指数。
HealthProfile profile = new HealthProfile(); profile.setUserId(userId); profile.setRealName(vo.getRealName()); profile.setGender(vo.getGender()); profile.setAge(vo.getAge()); BigDecimal heightM = vo.getHeight().divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); BigDecimal bmi = vo.getWeight().divide(heightM.pow(2), 1, RoundingMode.HALF_UP); profile.setBmi(bmi); healthProfileService.save(profile);表单校验要做两层。前端用Element UI的rule做体验拦截,比如身高范围50到250厘米、体重范围20到200公斤;后端再手动做一遍校验,防止绕过前端直接调接口。前校验收体验,后校验收安全,两者不能只做其中一个。
档案还必须设计审核状态。老人提交档案后默认为待审核,营养师在后台查看并填写评估意见后,状态变为已通过。这个设计把系统从单纯的信息录入提升到了业务处理层面,答辩老师通常会更认可。
文件上传也是档案模块里的典型功能,比如上传头像或者体检报告。毕设场景下用本地磁盘存储最省事,数据库里保存文件相对路径,再通过配置暴露一个虚拟访问路径给前端。记得在application.yml里限制上传文件大小,避免大文件拖垮接口。
4.3 膳食计划与营养搭配逻辑
营养师在后台选择一个老年人,系统自动读取最新健康档案,计算BMI和每日目标热量,然后从食谱库里筛选热量匹配的食谱作为候选,营养师可以手动调整后生成膳食计划。
DailyTarget target = nutritionCalculator.calculateDailyTarget(profile); List<Recipe> candidates = recipeMapper.selectList( new LambdaQueryWrapper<Recipe>() .le(Recipe::getTotalCalorie, target.getTotal() + 150) .ge(Recipe::getTotalCalorie, target.getTotal() - 150)); PlanSaveVO plan = new PlanSaveVO(); plan.setElderProfileId(profile.getId()); plan.setMealType("LUNCH"); plan.setRecipeId(candidates.get(0).getId()); plan.setTotalCalorie(candidates.get(0).getTotalCalorie()); plan.setAdvice("建议细嚼慢咽,少油少盐"); plan.setStatus("PENDING"); dietPlanService.save(plan);这里有一个很容易想的过于简单的点:自动推荐不能完全替代营养师的决定。计划生成后的状态设置为待确认,营养师确认后变为已发布,老人端才能看到。这样做既避免了系统直接下结论,也把状态流转的考核点刷到位了。
膳食计划按照日期和餐次展开,前端首页用卡片渲染本周膳食安排,点击某一天查看早餐、午餐、晚餐的具体食谱,并展示总热量和营养素信息。这些页面做出来,演示效果要比光秃秃的表格好很多。
4.4 前台展示与后台管理的联动
前台页面通常包括:首页轮播、本周推荐食谱、营养资讯列表、食材科普、食谱库、我的档案、我的计划、收藏、在线咨询。后台页面包括:用户管理、档案管理、营养师审核、食材管理、食谱管理、计划管理、资讯管理、咨询回复、数据统计。
数据流就是两句话:管理员发布食材和食谱后老人端可见,营养师生成计划并发布后老人端可见。老人端的收藏和提问又回流到营养师后台。前后端联动的本质,就是围绕这些状态字段做条件查询和按钮渲染。状态不同,前端显示的操作按钮就不同,接口返回的数据范围也不同。
所有接口统一返回Result结构,前端Axios拦截器统一判断code,非200时弹出错误提示。这样可以避免在每一个页面重复处理错误逻辑,也让后端接口风格保持一致。
public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> ok(T data) { Result<T> r = new Result<>(); r.code = 200; r.message = "success"; r.data = data; return r; } }5. 前后端联调、部署与调试的关键经验
5.1 接口设计与统一返回
接口路径建议这样划分:登录接口/api/auth/login,档案接口/api/profile,评估接口/api/profile/assess,食谱接口/api/recipe/list,周计划接口/api/plan/week,资讯接口/api/article/list,咨询接口/api/consult/send。登录接口不需要鉴权,其他接口通过拦截器校验token。
联调时不要一开始就开前端页面,先用Swagger或者Postman把后端接口全部测通。很多问题都是字段名对不上、返回值结构不一致,等前端开发时才发现,然后浪费大量时间排查。把后端先跑稳再做前端,效率会高很多。
如果开发时后端和前端分别跑在不同端口,跨域配置必须加好。配置时注意allowedHeaders要包含Authorization,否则携带token的请求在预检阶段就会被拦截。
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .maxAge(3600); } }5.2 按钮重复提交校验怎么做
前后端共同防重复提交,是实际项目里会真实遇到的问题,也是答辩老师喜欢深挖的点。用户连续点两次提交按钮,数据库里出现两条重复档案,这种场景在老年用户群体里尤其常见,因为很多人会以为页面卡了然后反复点。
前端方案最简单:提交按钮在点击后立即进入loading状态并禁用,等接口响应后再恢复。只做前端还不够,网络超时后用户手动重试,后端依然可能接收到重复请求。所以后端要做幂等处理。
比较实用的方案是幂等token:前端在打开表单时请求一个唯一token,后端保存起来并返回给前端;提交时前端带上这个token,后端校验存在就删除并执行业务,校验不存在就直接提示重复提交。
@Component public class IdempotentInterceptor implements HandlerInterceptor { @Autowired private StringRedisTemplate redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("idempotent-token"); if (StringUtils.isBlank(token) || Boolean.FALSE.equals(redisTemplate.delete(token))) { response.setStatus(400); response.getWriter().write("repeat submit"); return false; } return true; } }如果毕设项目没有安装Redis,用ConcurrentHashMap实现一个单机版本也能演示效果,但答辩时最好坦诚说明Redis方案更可靠。至少要把“为什么需要幂等”和“怎么实现的”这两点讲清楚。
5.3 部署策略:先本地跑通再打包
毕设交付时,单jar部署是体验最好的方案,操作流程如下:
- 在前端项目目录执行npm install,再执行npm run build。
- 构建完成后,把dist目录内的文件全部复制到Spring Boot项目的src/main/resources/static目录。
- 检查application.yml,server.port设置为8080,数据库连接指向本地MySQL,context-path留空。
- 执行mvn clean package -DskipTests,生成jar包。
- 执行java -jar target/diet-system-0.0.1-SNAPSHOT.jar启动系统。
如果要交付到其他电脑,数据库脚本和说明文档要一起带上,脚本里包含库表结构和基础测试数据。建议预设三个测试账号:admin/admin123、营养师和老年人账号各一个,这样可以快速演示不同角色的界面。
还有一个常见问题:Vue打包后某些页面刷新会白屏。原因是用history路由时,浏览器访问某个具体路径,后端没有对应的资源返回。最简单的处理方法是把Vue Router改为hash模式,演示最稳,不折腾。如果一定要用history模式,就需要在后端加一个转发规则,把非api且不是静态资源的请求转发到index.html。
6. 常见问题排查与避坑笔记
6.1 项目启动类问题
启动失败是毕设调试时遇到的第一座山,常见问题有四种。
端口被占用:Spring Boot自带Tomcat默认8080端口,如果本机有其他程序占用,启动会直接报错。用netstat命令查一下是什么程序占了端口,关掉或者改server.port都行。
数据库连接失败:看到Communications link failure一类的报错,优先检查MySQL服务是否启动、用户名密码是否正确、连接串是否带了serverTimezone参数。时区参数不写,在MySQL 8下经常会出现8小时时差或连接失败问题。
Maven依赖下载太慢:很多入门者卡在项目第一步,POM里几十个依赖一直报红。建议在Maven的settings.xml里配置阿里云镜像,下载速度会提升几个量级。
JDK版本不匹配:Spring Boot 3.x强制要求JDK 17及以上,如果本机是JDK 8却强行用高版本,编译直接失败。最舒服的方式就是统一用JDK 8加Spring Boot 2.7.x,避开一整套兼容问题。
6.2 数据访问与事务问题
MyBatis-Plus使用中比较多见的坑包括这几个。
启动时提示Invalid bound statement,通常是XML文件没被扫描到。确保resources/mapper目录下有对应XML,并且配置了mapper-locations,指向classpath*:mapper/*.xml。
字段下划线和驼峰不自动映射,需要在application.yml里开启map-underscore-to-camel-case。这个配置不打开,数据库的create_time就映射不到实体的createTime属性,查询结果全是null。
分页查询不生效,多半是MyBatis-Plus拦截器没注册。3.5.x版本需要在配置类里创建MybatisPlusInterceptor,并添加PaginationInnerInterceptor,否则分页参数会被忽略。
事务不生效的问题要重点检查两点。第一,同一个类内部调用this.save方法时,事务注解失效,因为Spring的代理对象没有介入;第二,事务方法内部如果把异常try-catch吞掉,事务自然也不会回滚。正确做法是把异常抛到外层,交给@Transactional统一处理。
6.3 前端资源与调试问题
前端集成进Spring Boot后,常见的坑也很固定。
接口404:Axios的baseURL尽量不要写到具体的http地址,而是设为空字符串,请求路径以/api开头,这样部署后天然和当前服务同源,换端口也不用改前端代码。
图片加载不出来:上传目录如果写在jar包解压目录里,重启后会丢失。建议上传路径配置为外部绝对路径,比如D:/diet-upload,然后通过资源映射把该路径暴露为静态访问地址。
跨域报错:配置了CORS还报错,先检查权限拦截器是否放过了OPTIONS预检请求。跨域场景下浏览器先发预检请求,如果被拦截器拦截,真实请求根本到不了后台。
刷新404:history路由在刷新后会请求一个不存在的路径,最简单的方案是换成hash路由,演示现场最稳。
6.4 说明文档与LW撰写的技巧
题目标注里的LW,其实就是毕业设计说明文档也就是毕设论文。很多同学习惯把文档放到最后写,等代码全部完成再动笔,结果发现当时设计时的很多决策原因已经记不清了。正确做法是边做边记录,代码只是系统实现的一部分,文档才是最终评分的大头。
论文结构基本按这个顺序不会出错:
- 摘要和关键词。
- 绪论:背景与意义、国内外现状、主要工作。
- 需求分析:功能性需求、非功能性需求、用例说明。
- 系统设计:总体架构、功能模块划分、数据库设计、接口设计。
- 系统实现:核心功能页面截图加关键代码解释。
- 系统测试:测试用例表、功能测试、性能测试简述。
- 总结与展望。
画图工具推荐ProcessOn或者Draw.io,用例图、E-R图、系统架构图都要有,这是论文的颜值担当。论文里不要大段贴代码,只贴核心逻辑片段,比如热量计算公式、权限拦截器、幂等校验,配上文字解释,让老师看到的是设计思路,而不是复制粘贴的代码。
答辩前至少把这几条准备到能流利说出来的程度:项目整体架构如何分层、数据库为什么这样设计、为什么加中间表、状态字段怎么流转、重复提交如何解决、接口越权如何拦截、一条完整业务如何走通。能把这几点讲清楚,这个题目基本就稳了。
最后说一点个人体会。做了这么多期毕设项目,我发现最容易翻车的不是功能写不完,而是写完了说不清自己的设计。答辩老师只要追问一句“这个膳食热量是怎么算的”,很多人就当场卡壳。所以代码跑通只是开始,每一个关键设计的选择理由,都要能在十几秒内讲明白。另外现场演示一定要用127.0.0.1:8080访问,提前把所有页面打开一遍,确认图片和接口都没有控制台报错再上台。细节做到位,这个项目才算真正落地。