
每到毕业季后台就会收到一大批关于Java毕设的私信问得最多的就是“Spring Boot Vue能做什么题目”。今天这篇就写个被问烂但永远有人需要的选题基于Spring Boot Vue的酒店预订系统。这套东西是标准的全栈入门结构前端Vue负责页面交互后端Spring Boot提供接口MySQL存业务数据增删改查、列表分页、权限区分、订单流转全都有作为毕业设计不仅容易过审而且面试时能讲的东西非常多。这套系统解决的是传统酒店电话预约、前台人工登记的痛点把房源展示、在线下单、订单管理搬到网页上。如果你想找一套能真正跑起来、能看懂、能回答答辩老师追问的Java毕设项目这个题材很合适。下面我把整个项目的设计思路、表结构、后端写法、前端联调、打包部署以及我在实际开发中踩过的一堆坑全部拆开讲一遍。1. 项目概述与整体方案设计1.1 为什么选酒店预订这个题目毕设选题有个原则业务要完整但不能复杂到一个人做不完。酒店预订系统恰恰卡在这个平衡点上。它不像电商系统那样要处理购物车、优惠券、库存、支付回调一堆逻辑也不像管理系统那么单薄只有一张表来回增删改查。酒店预订的核心业务链是用户注册登录 → 浏览房型 → 提交预订 → 支付/确认 → 入住/离店 → 评价。这条链路覆盖了用户端和管理端两个视角涉及用户管理、房间管理、订单管理、评论管理四个主要模块数据表数量控制在6到8张一个人完全搞得定。从技术角度说这个题目的知识点也够全。登录注册涉及参数校验和密码加密房型列表涉及分页查询提交订单涉及事务和并发控制订单状态涉及枚举和状态流转前后端联调涉及跨域处理。这些点既是毕设答辩时老师最爱问的也是实习面试时面试官最爱聊的。1.2 技术选型为什么是 Spring Boot Vue先看主流Java毕设的几个选项SSHStruts2 Spring Hibernate已经基本被淘汰SSMSpring Spring MVC MyBatis尚有一部分旧项目在用但配置繁琐Spring Boot MyBatis Plus已经成为绝大多数学校的主流选择。Spring Boot赢在“少配置”。内嵌Tomcat、自动装配、Starter机制这三板斧让人不用再写一堆XML配置文件。Vue作为前端框架学习曲线比React平缓模板语法直观而且Element UI这类组件库可以直接拖出后台管理界面。前后端分离是现在企业开发的标准模式毕设做成这个架构在答辩和面试时的说服力比单体JSP项目高出一个档次。这套系统的技术栈清单如下层级技术说明前端框架Vue 2.x Vue Router Vuex组件化开发前端路由跳转UI 组件库Element UI表格、表单、弹窗、消息提示HTTP 请求Axios统一封装请求处理Token后端框架Spring Boot 2.x提供RESTful接口ORM 框架MyBatis Plus单表CRUD不用写SQL数据库MySQL 5.7存储业务数据权限方案JWTJSON Web Token无状态登录认证项目管理Maven依赖管理、项目打包这套组合在Github上的开源项目数量极多遇到问题基本都能搜到现成方案不会卡在某个冷门Bug上走投无路。1.3 功能模块边界怎么划分做毕设最忌讳一上来就写代码。先把功能边界想清楚后面的开发效率会快很多。我把这个系统划成用户端和管理端两个大模块功能上完全隔离。用户端前台用户注册、登录、个人资料查看与修改房型列表浏览、房型详情查看、按条件筛选提交预订订单、查看我的订单、取消订单对已入住的订单进行评价管理端后台管理员登录房型管理新增房型、修改价格与库存、上下架订单管理查看全部订单、确认订单、办理入住、办理退房用户管理查看用户列表、禁用账号评论管理查看评论、删除违规评论这里有一个细节值得注意用户端和管理端不要做成两个系统用一个项目、两个角色就够了。实现方式是在后端的用户表里加一个role字段前端根据角色动态渲染菜单和路由。这样做的好处是省掉重复的用户系统开发也方便答辩时演示权限控制。2. 核心功能拆解与数据库设计2.1 数据表结构规划数据库是整个系统的地基。我见过很多毕设代码写得还行但打开数据库一看表结构一塌糊涂字段命名乱七八糟连主键都不规范。酒店预订系统的表设计要遵循一个原则一个业务实体一张表业务关系通过外键逻辑关联。我设计的核心表有6张t_user用户表、t_admin管理员表、t_room_type房型表、t_room房间表、t_order订单表、t_comment评论表。以房间表为例字段设计如下CREATE TABLE t_room_type ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键ID, type_name VARCHAR(50) NOT NULL COMMENT 房型名称大床房、双床房、套房, price DECIMAL(10, 2) NOT NULL COMMENT 每晚价格, area VARCHAR(20) DEFAULT NULL COMMENT 房间面积, bed_type VARCHAR(20) DEFAULT NULL COMMENT 床型1.8m大床/1.2m双床, max_people INT DEFAULT 2 COMMENT 最多入住人数, room_num INT DEFAULT 0 COMMENT 该房型总数, stock INT DEFAULT 0 COMMENT 当前可订数量, image VARCHAR(255) DEFAULT NULL COMMENT 房型图片路径, description TEXT COMMENT 房型描述, status TINYINT DEFAULT 1 COMMENT 状态1上架 0下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT房型表;这里有一个我在实际开发中反复调整的字段room_num和stock的区别。room_num是房型下的物理房间总数比如“大床房”一共10间stock是当前还能预订的数量。用户下单时减少stock退房时恢复stock。如果只用一张房间表去记录每个独立房间的状态订单和房间的关联会更复杂毕设阶段没必要做这么细用库存数量的方式简单又能讲清楚。2.2 订单状态怎么设计不踩坑订单表是整个系统里逻辑最重的一张表状态字段设计得好不好直接决定业务代码好不好写。我把订单状态定义为整数枚举对应关系如下状态值含义说明0待支付用户提交订单后模拟支付前1已支付/待入住支付成功等待到店办理入住2已入住管理员确认入住3已退房用户离店订单完成4已取消用户取消或超时未支付为什么用整数而不是字符串因为整数在数据库里占空间小、比较效率高而且代码里可以用枚举类统一管理不会出现已支付和已付款这种同一含义不同写法的问题。前端拿到数字状态值通过一个全局字典映射成对应的中文标签显示。订单表核心字段还包括订单编号、用户ID、房型ID、入住日期、离店日期、总天数、总金额、下单时间、支付时间、入住人姓名、入住人手机号。注意订单编号尽量不要用MySQL自增ID直接展示给用户用时间戳加随机数生成一个业务编号比如20250520103000123456这样显得正规也避免用户猜到系统每天的订单量。2.3 数据库设计的三个经验教训这块内容是我自己改了三版表结构之后总结出来的写在这里给后来人避坑。第一金额字段一律用DECIMAL(10,2)绝不能用DOUBLE或FLOAT。浮点数在计算机里是近似存储0.1加0.2可能等于0.30000000000000004涉及金额一旦出现精度问题对账对不上答辩被问就很尴尬。DECIMAL是定点数按整数方式存储不会丢精度。第二所有表都要加create_time和update_time两个时间字段。表面上看是冗余实际上排查数据问题时非常有价值。比如用户说“我明明下单了但订单不见了”通过创建时间就能定位是数据没写入还是写入后被删了。MyBatis Plus自带TableField(fill FieldFill.INSERT)自动填充功能代码上几乎零成本。第三删除操作尽量用逻辑删除而不是物理删除。用户取消订单后订单记录不应该直接从表里消失。加上deleted字段默认0删除时执行UPDATE置为1查询时自动过滤。MyBatis Plus的TableLogic注解一行代码就能实现既保证数据可追溯又避免误删。3. 后端关键实现细节与业务逻辑3.1 项目分层与统一返回体Spring Boot项目推荐严格分层Controller接收请求和参数校验Service处理业务逻辑Mapper负责数据库操作。实体类放在entity包公共类放在common包。很多同学喜欢把业务逻辑全写在Controller里一个接口几百行这种做法前期快后期改一个功能牵一发动全身相当痛苦。后端接口必须返回统一的数据结构。我在项目中定义了一个Result类public class ResultT { private Integer code; // 状态码200成功500失败 private String message; // 提示信息 private T data; // 数据负载 public static T ResultT success(T data) { return new Result(200, 操作成功, data); } public static T ResultT error(String message) { return new Result(500, message, null); } }前端Axios拦截器统一处理这个结构code为200时直接取data渲染页面code为500时弹出错误提示。这么做的好处是前端逻辑简单不用每个接口都写一遍错误分支。同时配合一个全局异常处理器RestControllerAdvice把业务异常、参数校验异常、系统异常分别捕获统一转成Result结构返回绝不让控制台堆栈直接暴露给前端。3.2 登录认证与权限控制酒店预订系统涉及两个角色普通用户、管理员。后端接口要做权限控制不然任何人拿个接口地址就能操作后台这在答辩演示时是重大扣分项。我用JWT实现无状态认证。用户登录成功后后端用密钥签发一个Token里面包含用户ID、用户名、角色等信息设置过期时间我设的是2小时。前端拿到Token后存到localStorage每次请求都在请求头里带上token: xxx。后端写一个拦截器拦截所有除登录和注册以外的/api/**请求校验Token是否有效有效则放行并把用户信息存入请求上下文无效则返回401。关键代码逻辑如下Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行跨域预检请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(token); if (StringUtils.isEmpty(token)) { throw new BusinessException(401, 未登录或登录已过期); } // 解析Token不合法会抛异常 Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } }这里要特别注意放行接口列表要配置好。注册、登录、房型列表、房型详情这些接口属于游客也能访问的必须加入白名单。我最初把所有接口都拦截了结果前端页面一打开就提示未登录排查了半天才发现是拦截器配置问题。密码存储用的是BCryptPasswordEncoder加密。BCrypt每次加密同一个密码生成的哈希值都不一样因为里面混入了随机盐比MD5更安全。我见过有些项目直接用MD5加密密码存储到数据库里一旦泄露攻击者用彩虹表就能反查明文这在毕设里也只能算勉强及格。3.3 预订业务与库存扣减提交预订是整个系统最核心的接口逻辑链路长必须放在Service层实现并且加事务注解。我一开始没加事务测试时发现一个诡异问题订单表插入了数据但房型库存没减少。原因就是两个数据库操作中间抛了个异常前面的insert已经提交后面的update没执行。事务解决这个问题的方式很简单Transactional(rollbackFor Exception.class) public Result? createOrder(OrderDTO dto) { // 参数校验 RoomType roomType roomTypeMapper.selectById(dto.getRoomTypeId()); if (roomType null || roomType.getStatus() 0) { return Result.error(该房型已下架); } if (roomType.getStock() 1) { return Result.error(该房型暂无余房); } // 计算实际天数 long days ChronoUnit.DAYS.between(dto.getStartDate(), dto.getEndDate()); if (days 1) { return Result.error(入住日期必须早于离店日期); } BigDecimal totalPrice roomType.getPrice().multiply(BigDecimal.valueOf(days)); // 扣减库存 roomType.setStock(roomType.getStock() - 1); roomTypeMapper.updateById(roomType); // 创建订单 Order order new Order(); order.setUserId(userId); order.setRoomTypeId(roomType.getId()); order.setStartDate(dto.getStartDate()); order.setEndDate(dto.getEndDate()); order.setDays((int) days); order.setTotalPrice(totalPrice); order.setStatus(OrderStatus.WAIT_PAY.getCode()); orderMapper.insert(order); return Result.success(order); }关于并发问题有一个点可以提前想好如果两个用户同时预订同一房型的最后一间房库存会不会变成负数这个系统是毕设通常不需要引入Redis分布式锁但可以在SQL层面做一个条件更新兜底// 带条件的原子更新只有库存大于0时才扣减 UPDATE t_room_type SET stock stock - 1 WHERE id ? AND stock 0MyBatis Plus的update方法支持构造UpdateWrapper传入SQL条件返回影响行数如果影响行数为0说明库存被抢完直接提示用户。这个设计思路虽然简单但在答辩时回答“如何避免超卖”这个问题上是非常漂亮的加分回答。3.4 配置文件的几个关键参数Spring Boot的application.yml是启动的入口配置文件里面有几个参数我建议每个做这个项目的同学都要手写一遍不要直接复制模板不思考。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/hotel_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 hikari: maximum-pool-size: 10 minimum-idle: 5 mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImplserverTimezoneAsia/Shanghai这个参数千万别去掉否则数据库连接会报时区相关的异常提示。map-underscore-to-camel-case会把数据库下划线字段自动映射成Java驼峰属性比如create_time映射成createTime少写一堆TableField注解。生产环境记得把SQL日志关闭但在开发阶段开着能直接在控制台看每次请求执行了什么SQL排查问题效率翻倍。MyBatis Plus的分页插件需要单独配置一个配置类新建一个MybatisPlusConfig类Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }没配置这个插件就调selectPage方法分页不会生效而是把全表数据查出来这个问题非常隐蔽新手很容易踩。4. 前端Vue实现与接口联调4.1 前端工程结构设计前端我用Vue CLI初始化的项目版本选的Vue 2.x。虽然Vue 3已经是主流但Element UI对Vue 2的支持更成熟网上资料多遇到组件问题搜索速度更快毕设求稳不建议在这个时间点折腾新特性。前端目录结构如下src/ ├── api/ # 所有接口请求的封装一个页面一个文件 ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── router/ # 路由配置 ├── store/ # Vuex状态管理 ├── utils/ # 工具函数如request.js封装axios ├── views/ │ ├── home/ # 前台首页 │ ├── room/ # 房型列表和详情 │ ├── order/ # 订单相关页面 │ ├── user/ # 个人中心 │ └── admin/ # 后台管理页面 └── App.vue核心文件是utils/request.js。这个文件封装了所有Axios请求的公共逻辑包括基准地址、请求头携带Token、响应拦截处理错误状态import axios from axios import { Message } from element-ui import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器带上token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.token token } return config }) // 响应拦截器统一处理错误 request.interceptors.response.use( response { const res response.data if (res.code 200) { return res } if (res.code 401) { Message.error(登录已过期请重新登录) localStorage.removeItem(token) router.push(/login) return Promise.reject(new Error(未登录)) } Message.error(res.message) return Promise.reject(new Error(res.message)) }, error { Message.error(网络异常请稍后重试) return Promise.reject(error) } ) export default request这个封装做好之后每个页面的接口调用代码非常简洁只需三行导入api模块、调用方法、渲染返回值。我曾经见过一些项目在每个页面里单独写axios请求重复代码一大堆这种代码风格在答辩时被看到会被老师扣印象分的。4.2 路由守卫与权限控制前端路由分成三块逻辑游客可访问的公开页面首页、房型列表、详情、需要登录的用户页面我的订单、个人中心、只允许管理员的页面后台管理。这块用Vue Router的全局前置守卫实现最方便router.beforeEach((to, from, next) { const token localStorage.getItem(token) const role localStorage.getItem(role) if (to.meta.requiresAuth !token) { next(/login) return } if (to.meta.requiresAdmin role ! 1) { next(/) return } next() })这个方案比每个页面都写一遍登录判断要优雅得多页面级权限控制集中在一个文件里维护。这里要注意一个安全细节前端路由守卫只能控制页面显示不能让用户绕过接口校验。真正安全的边界在后端接口的JWT拦截器和权限校验。很多初学者以为前端加了路由守卫就安全了实际前端的一切代码和数据都可以被绕过后端校验才是底线。4.3 前后端联调与跨域处理前后端分离开发时前端跑在localhost:8080后端跑在localhost:9090我故意把后端端口改到9090避免冲突不同端口之间请求会触发浏览器同源策略的跨域问题。解决跨域有三种主流方案我在不同阶段都试过方案一后端CORS配置。Spring Boot里写一个WebMvcConfigurer允许所有来源跨域。实现简单但生产环境安全性稍差所有域名都可以调你的接口。方案二前端开发环境代理。在Vue CLI项目的vue.config.js里配置devServer.proxy开发时请求发到/api代理转发到后端地址module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:9090, changeOrigin: true } } } }这个方案是我最推荐的。优点非常明显前端代码里写请求路径只写/api/xxx不写完整域名打包到生产环境后用Nginx反代把同一个域名下的/api转发到后端前端代码一行不用改就能直接上线。开发和生产的行为保持一致省掉大量环境相关的问题。方案三生产环境Nginx配置location /api/ { proxy_pass http://localhost:9090/api/; }这样做的好处是用户的浏览器始终只访问同一个域名不存在跨域问题也方便统一配置SSL证书。我在部署到云服务器时用的就是这个方案。4.4 前端页面开发顺序建议开发前端页面不要按美观程度优先要按数据流顺序来。我建议的顺序是先做登录注册页把Token拿到手。第二步做房型列表页通过公开接口拿到房型数据并渲染卡片列表。第三步做房型详情页把路由参数$route.query.id传给接口查详情。第四步做订单提交页这是前后端交互最复杂的环节要同时处理日期选择、金额计算、订单提交三个交互。第五步做订单列表页和后台管理页。这个顺序的好处是每个页面都依赖前面页面的成果每一步做完都能看到实际效果不会有那种“写了一大堆代码但页面白屏”的挫败感。Element UI的el-table、el-form、el-dialog三个组件要重点掌握后台管理页面80%的界面都是这三个组件组合出来的。5. 部署运行与常见问题排查实录5.1 从零到一跑起来很多人拿到一套源码第一步就卡在环境上。我把从零启动这个项目的完整步骤写一遍按顺序操作基本不会出问题。第一步安装环境。JDK用1.8Maven用3.6以上MySQL用5.7或8.0Node.js用14以上。这些版本兼容性最好不需要刻意追求新版。第二步准备数据库。用Navicat或命令行执行项目自带的hotel.sql脚本执行完检查一下数据库里应该自动创建好所有表。手动插入几条测试数据方便页面展示比如三个房型、一个管理员账号、一个测试用户账号。第三步修改后端数据库配置。打开application.yml确认账号密码和本地一致。大多数人报Access denied for user就是这一步没改密码。第四步启动后端。在项目根目录执行mvn spring-boot:run看到控制台输出Started Application就说明成功。第五步启动前端。在vue-hotel-ui目录执行npm install安装依赖然后npm run serve启动开发服务器浏览器访问localhost:8081。第六步联调测试。注册一个用户账号走一遍预订流程再到后台管理界面确认订单状态能更新。整个链路通了项目就算跑起来了。关于npm install有个常见问题下载超时或报错。把镜像源切到国内镜像能解决大部分问题npm config set registry https://registry.npmmirror.com5.2 常见问题排查对照表我把这个项目的开发过程中最容易碰到的问题整理成一个表格每个问题都是我自己或身边同学真实遇到过的。问题现象根本原因解决方案后端启动报端口被占用8080端口被其他程序占用杀掉占用进程或改server.port数据库连接失败报时区错误JDBC连接串没加serverTimezone连接串末尾加上serverTimezoneAsia/Shanghai接口返回中文乱码字符集编码不一致数据库连接串加characterEncodingutf8确认库表是utf8mb4请求返回401Token缺失或过期检查前端是否在请求头携带了token检查拦截器白名单配置前端页面404Vue Router使用history模式刷新时服务器没有对应路由改用hash模式或配置Nginxtry_files分页查询返回全部数据没配置分页插件在MybatisPlusConfig中加PaginationInnerInterceptornpm install报错依赖版本冲突或网络问题清缓存npm cache clean --force切换镜像源打jar包后运行报找不到主类打包时跳过main方法检查pom.xml中的start-class配置重新mvn clean package这里重点说下前端路由的history模式问题。开发模式下npm run serve不会出现这个情况因为devServer有内置的historyApiFallback。但打包后部署到Nginx如果你访问/admin页面刷新了一下Nginx会去磁盘上找/admin这个文件找不到就返回404。解决方案要么用hash模式URL带#号要么在Nginx配location / { try_files $uri $uri/ /index.html; }后端部署用mvn clean package打成jar包然后nohup java -jar hotel-system-1.0.0.jar log.txt 21 这句命令的意思是把jar包在后台运行日志输出到log.txt。如果启动报错打开log.txt查看堆栈信息这是定位后端问题的第一入口。5.3 文档怎么写才能过盲审毕设文档通常是导师和评阅老师最先看的东西。有些同学系统做得不错但文档写成一锅粥最后分数不高。写文档时不要按代码行文要按业务逻辑行文重点讲清楚三件事需求是什么、系统怎么设计、关键技术怎么实现。需求分析章节要写清楚用户角色和使用场景比如“用户张三出差需要预订酒店希望通过网页查看房型和价格在线下单到店直接办理入住”。场景描述比罗列功能点更有说服力。数据库设计章节要附上ER图和每张表的字段说明表。ER图不要用手画用PowerDesigner或简单的在线工具导出显得专业。技术实现章节不要贴大段代码贴核心代码片段并配文字解释重点讲设计思路。比如JWT认证为什么比Session更适合作前后端分离分页查询底层是怎么执行的。答辩老师看到学生能讲清楚“为什么这么写”比看到“我把代码堆出来了”印象好很多。6. 答辩准备与项目扩展建议6.1 答辩时老师最爱问的问题答辩时间通常只有10到15分钟老师不可能把项目每行代码都过一遍他们问的问题集中在几个固定方向。把下面这几个问题提前准备好答辩基本稳了。项目亮点为什么选Spring Boot和Vue回答思路从开发效率、前后端分离趋势、生态成熟度三个角度切入不要只说“大家都用这个”。数据库设计表的关联关系、为什么订单表不直接存房间ID而存房型ID这里要能说清楚存房型ID是为了简化库存管理用户预订的是房型而不是具体的某一间房具体的房间分配由酒店前台线下处理。安全设计密码怎么加密的JWT相比Session的优势Token过期怎么处理这三个问题是安全方向的常客提前背熟答案。并发库存扣减会不会超卖即使你没做高并发处理也要能说出“用数据库条件更新加事务兜底”的思路并承认毕设场景下没有引入Redis分布式锁是合理的简化。6.2 后续可以从这几个方向扩展这套系统做完之后如果你想在简历上多写两行或者在学有余力的情况下提升项目含金量,有四个扩展方向性价比很高。引入Redis做房型热门榜和Token存储。把房型列表的查询热点数据缓存到Redis减少数据库压力。Token从无状态JWT改为Redis存储后可以实现服务端强制踢人下线。这个改动代码量不大但在面试时讲“缓存穿透、缓存击穿、缓存雪崩”三条理论就有了实战场地。引入Elasticsearch做全文搜索。当房型数据量增大时MySQL的模糊查询LIKE %关键词%不走索引性能很差。用Elasticsearch建立索引搜索房型名称和描述响应速度毫秒级。这个方向适合想走后端搜索方向的同学。后台增加数据统计模块。统计每个月的订单量、营业额用ECharts画折线图和柱状图。报表功能是几乎所有企业系统的刚需简历项目里带一个数据可视化模块匹配度高。对接真实支付网关。把“模拟支付”换成支付宝沙箱或微信支付沙箱环境。支付回调处理涉及签名验证、幂等处理、状态机流转是含金量很高的实战经验。如果能力允许这个方向最推荐。6.3 一点个人的实操体会我从头到尾做过好几套类似的毕设项目最大的感悟是毕设项目和技术能力的提升未必成正比但认认真真走完整个流程和糊弄复制粘贴差距会在答辩时暴露得非常彻底。自己动手写过一遍的代码老师问任何一个细节都能对答如流而完全靠网上找源码改改应付的往往连项目里某个类的作用都说不清楚。如果时间有限至少要把核心的两条业务链路亲手写一遍一条是用户从注册到下订单的前台链路一条是管理员到后台管理订单的状态流转链路。这两条链路内的所有代码都吃透整个系统就算真正掌握了。至于页面美化和一些边缘功能可以基于组件库快速完成不影响核心评分。最后再分享一个实用的小技巧做项目时顺手把接口文档按模块整理成一个Markdown文件包含接口路径、请求参数、返回值示例。后期写毕设文档、画时序图、准备答辩PPT都能直接从里面拖素材省下来的时间不是一点半点。