ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Spring Boot + Vue 课程管理系统全栈开发实战指南

Spring Boot + Vue 课程管理系统全栈开发实战指南 1. 项目全貌与选型思路1.1 这个项目到底解决什么问题先把这个项目摆到桌面上看Spring Boot Vue 的课程管理系统交付物是源码、文档、调试支持、基础修改和答疑。说白了这就是一个典型的全栈 CRUD 项目做的是课程信息的管理、学生选课、教师开课、成绩录入这类校园业务场景。如果你是为了毕业设计、课程设计或者给自己简历上补一个完整项目那这个题目算是一个非常标准且稳妥的选择。为什么说它“标准”因为 Spring Boot Vue 是目前国内中小型系统开发里最主流的技术组合之一。后端用 Spring Boot 快速搭 RESTful API前端用 Vue 做单页应用两者通过 JSON 交互结构清晰、分工明确。这类项目最容易被面试官认可也最容易在短期内跑通并部署上线。课程管理系统又是一个业务边界非常清楚的领域——不涉及支付、不涉及高并发、不涉及复杂的权限模型核心就是“用户 - 课程 - 选课记录”这几张表之间的关系。这种业务复杂度对于学习全栈开发的人来说刚刚好不会因为业务逻辑过难而忽略技术本身的学习又不至于简单到没有东西可写。从交付内容来看源码、文档、调试、基础修改、答疑这几项其实覆盖了一个学习者的完整需求链。源码是骨架文档是地图调试是实战修改是二次开发的能力训练答疑则解决“卡住没人问”的最大痛点。如果你买到的项目缺了任何一块我都建议你补齐因为这五样东西合在一起才是一个完整的“可学习、可运行、可扩展”的项目闭环。1.2 为什么是 Spring Boot 而不是 SSM 或 Spring Cloud很多人会纠结技术栈选型这里我给一个比较实在的判断逻辑。SSMSpring Spring MVC MyBatis是老一代的组合配置繁琐XML 文件一堆初学者光搞定环境就能卡几天。Spring Cloud 是微服务架构引入注册中心、配置中心、网关、熔断等一系列组件做课程管理系统属于杀鸡用牛刀而且微服务的分布式事务、服务间调用等问题对一个新手项目来说是巨大的负担。Spring Boot 正好卡在中间它把 Spring 家族的配置简化到了极致内嵌 Tomcat打包后一个 java -jar 就能跑不需要单独装服务器。同时它保留了 Spring 的核心能力——IoC 容器、AOP、事务管理、数据校验等这些知识点面试必问学了不亏。课程管理系统这个规模单体应用完全够用模块划分清晰的话后续想拆成微服务也有重构的余地。Vue 这边同理Vue 2 和 Vue 3 的选择要看你的实际情况。如果你刚接触前端Vue 3 Element Plus 是目前的主流搭配生态完善、组件丰富。如果你的参考资料或学校要求用的是 Vue 2 Element UI那也可以核心逻辑差别不大主要注意一下 API 写法的差异比如 Vue 3 用 Composition APIVue 2 用 Options API。不管选哪个版本核心要掌握的是组件化开发思维、路由配置、Axios 封装和状态管理这些才是 Vue 的灵魂。2. 数据库设计与后端核心实现2.1 表结构设计课程管理系统的数据根基课程管理系统听起来简单但表结构设计的好坏直接决定后期开发的顺畅程度。我先给出一套经过验证的完整表设计再逐个解释为什么这样设计。第一个是用户表我建议叫sys_user核心字段包括id、username、password、real_name、role、create_time等。这里的 password 字段我用的是 BCrypt 加密后的密文绝不能存明文。角色字段role用字符串类型存ADMIN、TEACHER、STUDENT三个值虽然不够“高级”但简单直观适合这个体量的项目。如果你想用更规范的方式可以单独建一张角色表做关联但对于课程管理系统来说字段直接标识角色已经够用了反而查起来更简单。第二个是课程表course核心字段包括id、course_name、course_code、teacher_id、credit、classroom、class_time、max_students、selected_count、status、create_time。这里面有几个字段需要重点说明。teacher_id是外键关联sys_user表中角色为 TEACHER 的用户用来表示这门课由谁开设。max_students是课程容量上限selected_count是当前已选人数选课操作时必须校验selected_count max_students这是核心业务逻辑之一。status字段用来标识课程状态比如0表示未开放选课、1表示选课中、2表示选课结束这样做的好处是可以在不删除数据的情况下控制课程的可选状态。第三张表是选课记录表student_course这是整个系统的逻辑核心。字段包括id、student_id、course_id、select_time、score。为什么需要这张表因为学生和课程之间是多对多的关系一个学生可以选多门课一门课可以被多个学生选如果不加中间表数据关系根本无法建模。score字段是教师录入成绩时用的不选课就没有成绩所以成绩挂在选课记录上比挂在课程表上更合理。这张表的联合唯一索引必须加即(student_id, course_id)不能重复防止同一个学生重复选同一门课。补充一个常见的表设计误区很多人会把选课人数直接存在课程表里并认为这样查询方便。这确实方便但会带来并发问题——两个学生同时选最后一门课时两个请求都读到selected_count max_students - 1然后各自加一最终导致超卖。解决方式有两种一是用数据库行锁SELECT ... FOR UPDATE二是对student_course表加唯一索引后先插入选课记录再更新课程表的selected_count如果插入失败说明已经选过。我建议第二种方案简单有效且能防重复选课。2.2 后端分层架构与控制层实现细节Spring Boot 后端我建议严格分层Controller、Service、Mapper、Entity再加统一的返回结果类和异常处理类。这个分层不是形式主义而是为了把每个环节的职责边界划清楚出现问题能快速定位。实体类Course对应course表字段与数据库列一一对应。这里要注意teacher_id在实体里可以定义为Integer类型但在返回给前端的 JSON 数据中建议额外包含一个teacherName字段方便前端直接显示教师姓名而不是 ID。这个字段在实体类里用TableField(exist false)标注——MyBatis Plus 的注解表示它不是数据库表中的字段只是查询时手动填充的。这个做法很实用免去了前端二次请求查询教师名的麻烦。Controller 层的设计要遵循一个原则只做参数接收、格式转换和结果返回不写任何业务逻辑。以课程列表接口为例典型的代码结构如下RestController RequestMapping(/api/course) public class CourseController { Autowired private CourseService courseService; GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String courseName) { PageCourse page courseService.getCoursePage(pageNum, pageSize, courseName); return Result.success(page); } PostMapping(/select) public Result selectCourse(RequestBody SelectCourseDTO dto) { courseService.selectCourse(dto.getStudentId(), dto.getCourseId()); return Result.success(null); } }Result类是我自己封装的统一返回格式结构一般是{ code: 200, message: success, data: ... }。code用 200 表示成功400 表示参数错误500 表示服务器异常。前端 axios 统一拦截这个结构根据 code 判断逻辑走向比直接返回裸数据规范得多。Service 层是业务逻辑的核心。比如选课这个操作Service 里要做的事包括校验学生是否存在、校验课程是否存在、校验当前状态是否为选课中、校验是否已选过、校验人数是否已满。校验通过后先插入选课记录再更新课程表的selected_count加一。整个流程要加上Transactional事务注解任何一步抛异常就整体回滚数据才不会出现不一致。这是我反复强调的一点——涉及多表写入的操作必须加事务。2.3 认证授权JWT 的思路与应用方式很多课程管理系统用 Session 做登录状态管理这在单体应用里没问题但前后端分离的场景下我更推荐 JWTJSON Web Token。简单说JWT 就是后端在用户登录成功后签发一段加密的 token 字符串前端存到 localStorage每次请求时放进请求头Authorization: Bearer token后端解析 token 就知道当前是谁、有什么权限。为什么这么设计因为前后端分离后前端可能部署在一台服务器后端在另一台Session 共享需要额外配置而 JWT 无状态后端不用存 session天然适合这种场景。课程管理系统里我建议在 JWT 的 payload 里放入userId、username、role三个字段后续每个受保护的接口都从 token 中获取当前用户信息不需要再看数据库。实现上用jjwt或java-jwt库都可以。核心配置包括密钥、过期时间、加密算法。过期时间建议设置为 24 小时太短会导致用户频繁重新登录太长则存在安全风险。登录接口的伪代码逻辑是查用户表、比对密码BCrypt 的 matches 方法、生成 token、返回给前端。后续通过拦截器HandlerInterceptor或过滤器统一拦截/api/**下除登录接口以外的请求从请求头解析 token解析失败返回 401。有一个坑必须提醒JWT 一旦签发在过期前是无法主动失效的。如果用户修改密码旧 token 仍然有效。这时候你有两个选择一是把 JWT 的过期时间设短些配合前端刷新机制二是引入 Redis 黑名单。对于课程管理系统24 小时过期时间已经够用了遇到修改密码的场景最简单粗暴的处理方式是同时把前端 token 清掉重新登录别把方案搞复杂。3. 前端工程搭建与核心页面实现3.1 Vue 项目结构和路由设计前端工程我用 Vue CLI 或 Vite 初始化项目内部的结构推荐这样组织src ├── api // 所有接口请求方法按模块拆分 ├── assets // 静态资源 ├── components // 公共组件 ├── router // 路由配置 ├── store // 状态管理Vuex 或 Pinia ├── views // 页面组件 ├── utils // 工具函数如 axios 封装 ├── App.vue └── main.js这个结构最核心的两个目录是api和views。api目录下的每个文件对应后端一个模块比如course.js里放课程相关的所有请求方法。这样做的好处是页面组件里不直接写 axios而是调用封装好的方法代码看起来非常干净后期接口调整只需要改一个文件。路由设计方面我采用动态路由的思路。根据用户角色动态生成可访问的路由表而不是在静态路由表里把所有页面都暴露出来。比如管理员能看到用户管理页教师能看到成绩录入页学生只能看到选课中心。这个通过路由守卫实现用户登录后前端拿到用户角色根据角色对应的路由配置表动态添加路由同时在router.beforeEach导航守卫中做登录态检查和角色匹配。Vue Router 使用 history 模式有一个注意点部署到 Nginx 时如果前端路由是 /course/123刷新页面 Nginx 会直接 404。解决办法是在 Nginx 配置中添加try_files $uri $uri/ /index.html;。如果你把前端打包后放到 Spring Boot 的 static 目录里部署则要配置后端处理的 fallbackSpring Boot 里可以通过自定义 ErrorController 或资源映射来处理。这个小细节不算难但忘了配置就会被坑到怀疑人生。3.2 Element Plus 表格表单与弹窗的实战写法课程管理系统的前端页面核心的交互无非是表格展示数据、表单新增或编辑、弹窗确认删除这三类。Element Plus 是 Vue 3 环境下最顺手的 UI 组件库表格、表单、弹窗、消息提示全都有而且样式统一、开箱即用。以课程列表页为例核心结构是搜索栏课程名称的关键字搜索、状态筛选、工具栏新增按钮、批量删除按钮、表格课程信息列 操作列。表格数据通过onMounted生命周期调用getCourseList()接口获取这个逻辑写在下面const loadData async () { loading.value true; try { const res await getCourseList({ pageNum: pageNum.value, pageSize: pageSize.value, courseName: keyword.value }); tableData.value res.data.records; total.value res.data.total; } finally { loading.value false; } };新增和编辑功能我用同一个弹窗组件通过dialogVisible和formData两个变量控制。弹窗打开时如果是编辑模式就调用getCourseDetail(id)回填数据如果是新增模式就重置表单。提交时做表单校验Element Plus 的el-form自带rules校验机制比如课程名称必填、学分数必须是数字、最大选课人数必须大于 0这些规则在表单的rules属性里声明即可不用自己手写 if-else。删除操作一定要弹确认框这是行业惯例防止误删。用ElMessageBox.confirm实现点击确定后才发送删除请求。删除成功后还要注意当前页的数据量——如果当前页只有一条数据且不是第一页删除后应该回到上一页否则会出现“当前页无数据”的空白状态。这个小逻辑很多人会漏掉实际做的时候加上体验会很加分。3.3 Axios 封装与权限控制的前端处理Axios 封装是前端工程里的一个关键环节。我这里说的封装不是简单地把 axios 包一层而是要做到三件事请求拦截、响应拦截、统一错误处理。请求拦截器的作用是每次发请求前从 localStorage 里取出 token设置到请求头的 Authorization 字段上。如果 token 不存在且当前请求不是登录接口直接跳转到登录页。响应拦截器的作用是统一解析后端的 Result 结构如果code ! 200就弹出错误提示如果 HTTP 状态码是 401说明 token 过期或者未认证清除本地登录信息并跳转登录页。我给出一个比较精简的 axios 封装示例你直接可以抄作业import axios from axios; import { ElMessage } from element-plus; import router from ../router; const request axios.create({ baseURL: /api, timeout: 10000 }); request.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); request.interceptors.response.use( response { const res response.data; if (res.code ! 200) { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message)); } return res; }, error { if (error.response error.response.status 401) { localStorage.removeItem(token); localStorage.removeItem(userInfo); router.push(/login); } ElMessage.error(error.message || 网络异常); return Promise.reject(error); } ); export default request;baseURL我建议写/api而不是写死http://localhost:8080这样本地开发时通过 Vite 或 Vue CLI 的 proxy 代理转发到后端生产环境直接把前后端部署在同一个域名下不需要改代码。如果你写死了后端地址本地能跑打包部署后还得改很容易漏掉。前端权限控制除了动态路由还有一个细节是按钮级别的权限。比如课程删除按钮只有管理员能看教师只能看自己的课程并操作成绩录入。这个可以用自定义指令v-permissionADMIN实现指令内部比对当前用户角色不匹配就移除这个 DOM 元素。这样代码比在每个按钮上都写一次 v-if 干净得多后期角色增减也更灵活。4. 联调、调试与全流程部署4.1 环境准备与本地开发调试配置这个项目要跑起来本地环境最少需要这些JDK 8 或 11、Maven 3.6、Node.js 14、MySQL 5.7 或 8.0、Vue CLI 或 Vite。IDEA 和 VS Code 分别作为前后端 IDEVSCode 里建议装 Vue Language Features 插件和 ESLint 插件IDEA 里建议装 Lombok 插件不然实体类的Data注解会报错。很多人第一步就挂在 MySQL 版本和驱动匹配上。MySQL 8.0 的驱动类是com.mysql.cj.jdbc.Driver连接 URL 里必须加上serverTimezoneAsia/Shanghai否则会有时区报错。MySQL 5.7 用com.mysql.jdbc.Driver不需要配时区。如果你用 Spring Boot 2.x 默认的依赖管理驱动版本是 8.x数据库也是 8.0那直接复制这段配置就行spring: datasource: url: jdbc:mysql://localhost:3306/course_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver后端启动时如果遇到端口被占用排查方法很简单。用netstat -ano | findstr 8080Windows或lsof -i:8080Mac/Linux找出占用端口的进程要么关掉它要么在application.yml里把server.port改成一个不冲突的端口。前后端联调时我建议后端端口保持 8080前端用 3000 或 5173通过 proxy 代理解决跨域这样浏览器里访问的是同一个前端地址不会有跨域问题。前端代理配置在 Vue CLI 里是vue.config.js的devServer.proxy在 Vite 里是vite.config.js的server.proxy。核心配置长这样// vite.config.js server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这里把/api开头的请求全部转发到 8080 端口。注意后端接口的路径一定要统一带/api前缀否则代理规则就匹配不上。4.2 前后端分离项目的打包部署开发调试完成后部署上线是另一个关键环节。后端打包用 Maven执行mvn clean package -DskipTests会在 target 目录下生成一个 jar 包。这个 jar 包依赖内嵌的 Tomcat直接java -jar xxx.jar就能启动前提是数据库环境已经就绪且application.yml里的数据库连接地址改为生产环境的地址。为了让 jar 包在后台持续运行我建议用nohup java -jar xxx.jar app.log 21 启动日志输出到app.log文件方便排查问题。如果你的服务器有 systemd那就写一个 service 文件来管理项目的启停这个更专业一些还能设置开机自启。前端打包执行npm run build产物在dist目录。三种部署方式我给你列清楚第一种前后端完全分离部署。前端 dist 目录交给 NginxNginx 里配置一个 server 块root 指向 distlocation /api配置反向代理到后端地址。这是最推荐的方式因为前端静态资源用 Nginx 托管性能最好后端服务独立运行互不影响。第二种前端打包后放进 Spring Boot 的 static 目录。把 dist 里的文件复制到src/main/resources/static/重新打包这样只有一个 jar 包部署最简单。适合小型项目或演示用。需要注意的坑是只要后端启动依赖了 Spring MVC静态资源访问没问题但前端是 history 模式时服务端必须处理未知路径的 fallback否则直接刷新子路由就 404。我在 Spring Boot 里的处理办法是写一个简单的 Controller 或资源映射配置将所有非接口链接转发到 index.html。第三种Docker 容器化部署。写一个 Dockerfile后端用 openjdk 镜像前端用 nginx 镜像服务编排交给 docker-compose。这种方式在云服务器上部署、迁移都很方便属于进阶操作初期不用急着学项目都跑不通的时候折腾 Docker 属于给自己找麻烦。4.3 调试的核心方法与技巧这个项目交付里包含“调试”服务说明调试能力是学习者必须掌握的基本功。我给一套实际工作中一直在用的调试方法论。后端调试的第一步是启动日志。看日志不是等报错了才看而是启动时就要确认这些关键信息数据源是否初始化成功、MyBatis 的 Mapper 是否全部映射、端口是否监听成功、Tomcat 是否正常启动。Spring Boot 启动日志里有任何 ERROR 或红色信息都要第一时间处理不要等接口调不通才回头查。第二步是接口调试。我习惯用 Postman 或 Apifox 做接口测试先测登录接口拿 token再测需要鉴权的业务接口。一个接口访问不通排查顺序是确认后端启动状态 - 确认请求 URL 和方法的正确性 - 确认参数格式是否 JSON、字段名是否一致- 确认 token 是否有效 - 看后端异常堆栈。千万别跳过前面的步骤直接看代码大多数 404 和 400 都是前几步的问题。第三步是前端调试。Vue 项目里打开浏览器开发者工具重点关注 Network 面板和 Console 面板。Network 面板能看到每个请求的详细信息状态码、请求头、响应体、耗时。如果某个接口返回 500切到 Console 面板看后端返回的异常信息基本上能判断是哪一行代码出了问题。前端报错分两类一类是 JavaScript 运行时报错另一类是接口返回的数据结构不对导致渲染出错。后者很好排查F12 看一下 response 数据结构和前端代码里期望的结构是否一致。这里分享一个我踩过的坑后端接口返回的时间字段是 UTC 时间格式前端直接显示比本地时间少 8 小时。这是因为 Java 的 Date 默认序列化格式导致的。解决办法是配置 Jackson 的时间格式化spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8这个坑非常隐蔽如果你不对比后端返回的原始 JSON 和前端的显示根本发现不了。排查这类问题时用 Postman 看原始返回永远是第一步不要直接看页面效果。5. 常见问题排查与项目扩展方向5.1 高频踩坑问题速查我把课程管理系统开发中常见的问题列一个排查清单这些基本上是我在调试过程中遇到最多的你在开发和答疑过程中也大概率会碰见。第一个问题是 Maven 依赖下载慢或者下载失败。原因通常是网络问题或仓库源不是国内镜像。解决办法是在settings.xml里配置阿里云镜像这个改动对所有 Maven 项目都生效。换成阿里云镜像后依赖下载速度会快很多构建时间能缩短一半以上。第二个问题是前端 npm install 卡死或报错。解决办法是先清 npm 缓存再换淘宝镜像源。如果node_modules目录残留了坏依赖直接把整个目录删掉重新安装这是最彻底的方式。删除后执行npm install --registryhttps://registry.npmmirror.com基本能解决。第三个问题是数据库连接报错或者中文乱码。连接报错十有八九是 URL 里的serverTimezone没配置或者驱动版本不对。中文乱码要检查三处数据库表字符集是否 utf8mb4、连接 URL 里是否设置了characterEncodingutf8、后端接口返回前是否被正确编码。这三处检查完乱码问题基本不会出现。第四个问题是前端跨域报错。如果你用了我上面说的 proxy 方案本地联调不会出现跨域。如果你没有配置 proxy而是直接在 axios 里写后端地址那就必须在后端配置 CORS。Spring Boot 里用一个配置类实现WebMvcConfigurer重写addCorsMappings方法允许前端的源访问。生产环境上线时建议关闭 CORS 并把请求改为同源部署不要图省事弄得所有人都能跨域访问你的接口。第五个问题也是最经典的实体类字段映射为 null。例如数据库列名是course_nameJava 属性是courseName如果 MyBatis 的 map-underscore-to-camel-case 没开启查询结果就是 null。解决办法是在application.yml中配置mybatis.configuration.map-underscore-to-camel-case: true或者在 Mapper 的 SQL 里用Results注解手动映射字段。用 MyBatis Plus 的话默认就是驼峰映射但如果你手写了 SQL依然要小心。第六个问题是前端打包后接口地址不对。开发时接口能通打包后页面白屏或接口报 404大概率是你把 baseURL 写成了http://localhost:8080。换成相对路径/api并配置 Nginx 反向代理就能解决。还有publicPath的问题Vite 打包后静态资源默认路径是/如果你部署在子路径下就需要配置base: ./用相对路径否则静态资源全部 404。5.2 项目交付后的二次开发扩展建议课程管理系统跑通只是第一步不代表做完就完了。我强烈建议你在基础版本上做二次开发哪怕只是为了给自己增加项目经验值也至少要扩展下面几个方向中的一个。第一个方向是引入 Redis 做缓存和数据管理。比如把课程列表的查询结果缓存起来大量学生同时查询时直接命中缓存减少数据库压力。还可以用 Redis 存储验证码比存在 Session 里更灵活。这是一个很好写的技术亮点面试时比纯 CRUD 项目有说服力得多。第二个方向是扩展业务功能。比如加一个公告栏模块管理员发布系统通知加一个课程评价功能学生选完课程后可以对课程进行评分和评价给课程加上学期的概念每个学期独立维护一份课程数据。这些扩展都不用动核心架构表结构和接口增加而已但对业务完整度的提升非常明显。第三个方向是引入文件上传功能。比如教师上传课程资料PPT、PDF、视频学生在线浏览或下载。这个功能涉及文件和数据库的交互上传用 MultipartFile存储到服务器本地磁盘数据库只存文件路径。文件类型和大小限制可以在 Spring Boot 的配置里设置spring: servlet: multipart: max-file-size: 10MB max-request-size: 20MB上传文件时还要做文件类型校验不能只依赖前端后端也要校验文件的扩展名和 MIME 类型防止有人上传恶意文件。文件命名统一用 UUID 加原始文件名的后缀避免文件名冲突。第四个方向是增加数据可视化。用 ECharts 做一个统计页面展示课程选课人数的柱状图、各专业学生的选课分布饼图、课程成绩的分布直方图等。前端从后端接口拿统计数据接口里用 SQL 的 group by 或 count 聚合查询实现。这个功能视觉效果好老师看完会给高分面试介绍时也有画面感。第五个方向是代码质量的打磨。给项目补充完善的单元测试用 JUnit 写 Service 层的测试用例用 MockMvc 写 Controller 层测试引入 Lombok 简化实体类代码把 SQL 语句统一放到 Mapper XML 里管理避免注解 SQL 出现在 Java 代码中结合 SonarQube 扫描一遍代码修掉提示的问题。这些都是企业级项目的标配提前养成习惯绝对有好处。5.3 基于个人经验的学习路线建议结合我带过一些初学者的经验我给一个比较务实的参考路径。如果你是刚入门 Spring Boot 和 Vue 的新手建议先按这个顺序来理解项目先不改代码把项目跑起来用管理员身份登录后台手动点击每个功能页面的按钮感受数据的流向然后打开数据库工具看课程表、用户表、选课表之间的关系对核心数据模型形成直觉再把项目源码从 Controller 层到 Mapper 层完整读一遍在 IDEA 里用 Debug 模式打断点跟踪一次“选课”操作到底走了哪些代码最后再开始改代码先改简单的比如把课程的学分字段加一个默认值、调整表格的显示列逐步过渡到增加一个新接口、一个新功能页面。实际操作中我建议你准备好两份文档一份是数据库表结构的说明文档另一份是所有接口的调用说明文档路径、参数、返回值。不用做得非常正式但要做到“拿着这两份文档没有源码也能知道系统的数据结构是什么、接口怎么调”。这两份文档也是项目文档的核心很多人的文档写得空泛就是因为缺这种细节。最后说一个实在的建议不要拿项目源码去应付了事。拿到源码后从头敲一遍可能不太现实但至少要完整跑通一遍、理清一条数据链路、修改一个独立功能并且能讲清楚自己在里面做了哪些内容。哪怕只是给课程列表加一个“学期筛选”的功能这种颗粒度的独立实践对理解全栈开发的价值比从头到尾照抄一遍源码大得多。真正面试的时候面试官看重的不是你“做过什么项目”而是你“能解决什么问题”。带着整套源码、文档、调试笔记和修改记录去讲一个项目这就是最扎实的底气。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进