恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
SpringBoot+Vue全栈毕设实战:从数据库设计到JWT认证与答辩准备
首页
资讯中心
/
SpringBoot+Vue全栈毕设实战:从数据库设计到JWT认证与答辩准备
SpringBoot+Vue全栈毕设实战:从数据库设计到JWT认证与答辩准备
发布时间:2026/10/16 10:02:53
1. 项目全貌与定位选题理由和技术栈考量做毕设的时候最容易被问的一句话是“你为什么要选这个题目”如果答不上来后面答辩基本就是被牵着鼻子走。所以我在定“光影平台”这个题目之前认真盘过几件事。“光影平台”这个名字听起来有点文艺但落到技术上就是一个典型的前后端分离项目后端用SpringBoot提供RESTful接口前端用Vue做单页应用数据库用MySQL存数据整个项目涵盖用户登录注册、内容管理、文件上传、分类检索、数据统计这些常规模块。它既没有复杂到毕设周期做不完又比“学生管理系统”那种烂大街题目多了一点辨识度。答辩的时候老师看到的是你对完整业务链路的把控能力而不是一个概念演示。技术栈选型上我坚持了一套“稳中带新”的组合后端SpringBoot 2.x MyBatis-Plus Spring Security JWT前端Vue 2.x Element UI Axios Vue Router Vuex数据库MySQL 5.7/8.0都可以建议8.0中间件Redis可选用来做验证码缓存和Token黑名单为什么这么选因为毕设的核心目标是“能跑、能讲、能扩展”。SpringBoot降低配置成本MyBatis-Plus省去大量手写SQL的工作量JWT解决前后端分离下的登录态问题Vue2配合Element UI是当时最成熟的中后台开发组合网上资料多踩坑成本低。不是说Vue3不好而是毕设周期就几个月用最熟练的一套组合能让你把精力花在业务逻辑上而不是花在踩新框架的坑上。项目交付物除了源码之外我还整理了完整的SQL脚本和接口文档。这一点很多同学容易忽略——源码只是代码SQL脚本和接口文档才是让别人尤其是评阅老师快速看懂你项目的钥匙。后面我会详细说这两块怎么做才算“合格交付”。2. 数据库设计与SQL脚本的坑2.1 表结构划分思路不要一上来就写SQL光影平台的功能我拆成了几个模块用户模块、光影内容模块即影视/视频条目、分类模块、收藏点赞模块、评论模块、操作日志模块。对应的核心表有这些t_user用户表t_content光影内容表影视条目t_category分类表t_collect用户收藏表t_comment评论表t_operation_log操作日志表可选表设计以“够用、能演示完整流程”为原则不追求过度设计。比如权限方面我做的是用户和管理员两种角色没有引入Spring Security的RBAC完整模型而是在user表里加一个role字段。不是我不会RBAC而是毕设里用户-角色-权限三张表加关联关系会让账号系统的演示链路变长答辩时容易在细节上被追问。角色字段 前端路由守卫 后端注解拦截已经足够表达“权限控制”这个能力。下面是我实际使用的建表脚本中比较核心的几张表CREATE TABLE t_user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 加密后密码, nickname varchar(50) DEFAULT NULL, avatar varchar(255) DEFAULT NULL COMMENT 头像URL, role tinyint(1) DEFAULT 0 COMMENT 0-普通用户 1-管理员, status tinyint(1) DEFAULT 1 COMMENT 1-正常 0-禁用, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4 COMMENT用户表;CREATE TABLE t_content ( id bigint(20) NOT NULL AUTO_INCREMENT, title varchar(200) NOT NULL COMMENT 标题, cover varchar(255) DEFAULT NULL COMMENT 封面图URL, video_url varchar(255) DEFAULT NULL COMMENT 播放地址, summary text COMMENT 简介, category_id bigint(20) DEFAULT NULL, view_count int(11) DEFAULT 0 COMMENT 浏览量, collect_count int(11) DEFAULT 0 COMMENT 收藏数, status tinyint(1) DEFAULT 1 COMMENT 1-上架 0-下架, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4 COMMENT光影内容表;补充一点字符串字段一律用utf8mb4而不是utf8因为utf8在MySQL里存不了emoji和部分生僻字。亲身经历之前一个项目里用户昵称带了个特殊符号直接导致入库报错排查半天发现是字符集问题。数据库初始化的时候就把字符集定好能省掉很多莫名其妙的坑。2.2 SQL脚本交付不是扔一个dump文件就完事很多同学写毕设说明文档时SQL脚本就是MySQL Workbench导出的一个大文件里面全是CREATE TABLE和INSERT语句。这样不是不行但给评阅老师的体验不好。我的做法是准备三个脚本sql/init.sql建库、建表。里面包含完整的表结构、字段注释、索引定义。sql/data.sql基础数据。包括管理员账号、分类数据、几条内容示例数据。sql/demo_data.sql演示数据。批量插入几十条内容记录方便前端页面有东西可以展示。分别拆开的好处是评阅老师先看init.sql了解你表设计得怎么样再看data.sql知道系统能不能跑起来最后用demo_data.sql灌数据做演示。逻辑清晰也显得你专业。这里有一个容易忽略的点SQL脚本里的注释。我自己给每个字段都加了COMMENT说明。字段命名也尽量自解释username就是用户名create_time就是创建时间。千万别用a、b、c这种缩写或者au、ct这种半猜式命名。评阅老师看表结构的时间可能只有几分钟字段名和注释写清楚了他就知道你的设计是规范的。数据初始化脚本里密码一定不能存明文。我测试环境下的管理员密码也是用BCrypt加密后的字符串。初始化脚本里写清楚“默认密码是什么、加密规则是什么”方便评阅老师拿这个账号去登录系统。接口文档里我也专门留了一页说明默认账号信息。最新热词是什么其实就是平时我们做项目时说的那些接地气的东西如这样写才专业、测试环境、分分钟搞定之类。写SQL脚本时也要有这种意识——每个细节都是为了减少沟通成本。再提醒一个坑如果你用的旧版本MySQL客户端导出SQL会带出一些类似SET FOREIGN_KEY_CHECKS、ENGINEInnoDB AUTO_INCREMENTxxx的语句这些没问题但可能包含DEFINER这类指定用户名权限的语句在别人电脑上导入时会报权限错误。我的处理方式是所有交付的SQL脚本都自己手写或重新整理过确保任何人在任何MySQL环境上都能直接执行成功。3. SpringBoot后端核心模块实现3.1 项目分层用标准结构换来低犯错率后端代码我用的是经典的四层结构Controller → Service → Mapper → Entity。网上很多项目会加一个DTO/VO层我这里也加了但定位很克制——Entity对应数据库表VO是返回给前端的视图对象DTO是接收前端参数的传输对象。不强行加层但必要的隔离要做。一个典型的分层示例com.guangying.platform ├── controller // 接口层只做参数接收和返回 ├── service // 业务逻辑层核心处理 │ ├── impl ├── mapper // MyBatis-Plus的Mapper接口 ├── entity // 数据库实体 ├── dto // 入参对象 ├── vo // 返回视图对象 ├── config // 配置类JWT、跨域、静态资源映射等 ├── utils // 工具类 ├── common // 统一返回结果、异常处理等 └── PlatformApplication.java为什么这个结构能“换来低犯错率”因为它把职责边界划清楚了。Controller不写业务SQLService里不出现HttpServletRequestEntity不放注解以外的逻辑。一旦同学之间互相看代码、评阅老师抽查代码发现你的代码职责清晰印象分就上去了。这里说一下统一返回结果的设计。我用了一个ResultT类Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }所有接口都返回这个结构前端Axios响应拦截器里统一判断code。有人可能觉得这样多此一举但实际项目里没有统一返回结构前后端联调就是灾难有的接口返回字符串、有的返回JSON、有的直接返回200状态码。统一之后前端写代码非常放松只要关心data里是什么就行。3.2 认证与权限JWT的完整落地流程光影平台的用户认证我用的JWT方案。整体流程是用户提交用户名和密码到/api/auth/login后端校验账号密码成功则生成JWT返回前端把Token存到localStorage后续请求在请求头加Authorization: Bearer token后端用JWT过滤器解析Token从Token里拿到用户ID和角色JWT的生成我用的jjwt库。核心代码大致是这样public String generateToken(User user) { Date now new Date(); Date expiryDate new Date(now.getTime() 7 * 24 * 60 * 60 * 1000); return Jwts.builder() .setSubject(user.getUsername()) .claim(userId, user.getId()) .claim(role, user.getRole()) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }Token过期时间我设置为7天因为这是毕设项目用户不会频繁使用太短的过期时间会导致演示过程中经常需要重新登录体验不好。实际生产项目中通常会用短Token Refresh Token的机制但毕设不用做到那个程度。后端拦截权限我用了Spring Security做全局过滤但是配置非常简单关闭CSRF、关闭Session、放行登录注册接口和获取公开内容的接口其余接口全部走JWT过滤器验证。管理员专属接口上加PreAuthorize(hasRole(ADMIN))注解。这里有个容易踩的坑Spring Security默认会拦截所有请求包括静态资源。如果你在Controller里写了文件上传接口但前端上传的请求没有带Token就会被拦截。我自己的处理方式是上传接口也放在需要认证的路由下前端上传组件在请求头中带上Token。或者干脆把上传接口放行等业务需要时再校验。生产上建议还是校验毕设阶段看你怎么讲能自圆其说就行。3.3 文件上传与静态资源映射一个常见的演示翻车点光影平台的内容需要封面图这就涉及到文件上传。我后端用的上传方案是文件保存到服务器的本地目录比如/uploads同时把文件的访问URL存到数据库。Controller里的上传逻辑PostMapping(/api/upload) public ResultString upload(RequestParam(file) MultipartFile file) { if (file.isEmpty()) { return Result.error(400, 文件不能为空); } String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); String newFilename UUID.randomUUID().toString().replace(-, ) ext; String datePath new SimpleDateFormat(yyyyMMdd).format(new Date()); String filePath uploadDir / datePath; File dir new File(filePath); if (!dir.exists()) { dir.mkdirs(); } file.transferTo(new File(filePath, newFilename)); String url /uploads/ datePath / newFilename; return Result.success(url); }注意一个问题这里返回的是相对路径/uploads/...不是完整的HTTP地址。前端拿到这个相对路径后配合配置的接口前缀就能拼出完整可访问的URL。但如果前后端部署在不同的域名/端口下这个相对路径就会失效。所以我的做法是前端在拿到返回结果后判断如果URL以/开头就拼上VUE_APP_API_BASE_URL如果已经是http开头就直接用。这个细节很多同学想不到联调时就会出现“上传成功但图片加载不出来”的尴尬。SpringBoot里要映射/uploads/**到本地磁盘目录需要加一个配置类Configuration public class WebMvcConfig implements WebMvcConfigurer { Value(${file.upload-dir}) private String uploadDir; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/uploads/**) .addResourceLocations(file: uploadDir /); } }这里的file.upload-dir要设置一个本地绝对路径比如D:/project/uploads或者/home/user/uploads。我最初的坑就是忘了加这个映射上传后的文件存在服务器本地但通过URL访问400排查了很久才发现是资源映射没配置。上传文件的类型和大小也要限制一下。我在application.yml里设置了spring: servlet: multipart: max-file-size: 20MB max-request-size: 50MB如果要限制图片类型就在Controller里判断file.getContentType()只允许image/jpeg、image/png这类常见的图片格式返回错误。3.4 接口文档别用Word写用Swagger/在线文档接口文档是我这次项目交付中投入产出比最高的组成部分。我用的是Swaggerspringfox自动生成API文档再加上一份手动整理的Markdown版本。Swagger的集成很简单加依赖、写配置类、在Controller或接口上加注解就行。效果是启动项目后访问/swagger-ui/index.html就能看到所有接口的测试面板。评阅老师可以直接在页面上测试接口比自己用Postman调方便得多。不过Swagger有个缺点默认生成的描述信息比较简陋接口含义不清晰。所以我给每个接口都加了详细的ApiOperation注释和ApiImplicitParam参数说明。ApiOperation(value 分页查询内容列表, notes 支持按分类、关键词、状态筛选) ApiImplicitParams({ ApiImplicitParam(name page, value 页码从1开始, defaultValue 1, dataType int), ApiImplicitParam(name size, value 每页条数, defaultValue 10, dataType int), ApiImplicitParam(name categoryId, value 分类ID不传则查询全部分类, dataType long), ApiImplicitParam(name keyword, value 标题关键词模糊查询, dataType string) }) GetMapping(/api/content/list) public ResultPageResultContentVO list(RequestParam(required false, defaultValue 1) Integer page, RequestParam(required false, defaultValue 10) Integer size, RequestParam(required false) Long categoryId, RequestParam(required false) String keyword) { // 业务代码 }接口文档里面我额外加了三个板块默认账号说明、部署步骤、常见启动报错解决办法。不要小看这三个板块它能让使用你文档的评阅老师或同学十分钟内把项目跑起来对你的好感度直线上升。我的Markdown接口文档结构大致是项目简介与技术栈环境要求与部署步骤配置文件说明默认账号列表接口明细按模块分组登录注册、内容管理、收藏评论、数据统计等常见问题FAQ这份文档放到项目仓库的docs目录下。我强烈建议每个毕设项目都有一份这样的文档因为将来找工作时这份文档是证明你工程化能力的重要材料。4. Vue前端页面开发与联调4.1 前端项目结构与路由设计前端我用Vue CLI脚手架创建项目目录结构按页面模块划分src ├── api // 接口请求封装 │ ├── auth.js │ ├── content.js │ └── user.js ├── assets // 静态资源 ├── components // 公共组件 ├── layout // 布局组件头部、侧边栏、内容区 ├── router // 路由配置 │ └── index.js ├── store // Vuex状态管理 ├── views // 页面组件 │ ├── Login.vue │ ├── Home.vue │ ├── ContentList.vue │ ├── ContentDetail.vue │ └── Admin │ ├── Dashboard.vue │ ├── ContentManage.vue │ └── UserManage.vue ├── utils // 工具函数request封装、token管理等 └── main.js路由设计上我用Vue Router的守卫做了权限控制。具体逻辑是在router.beforeEach里判断用户是否已登录如果没登录且访问的是需要认证的页面就跳转到登录页如果登录了且访问的是管理端页面还要判断角色是否为管理员不是则跳回首页。router.beforeEach((to, from, next) { const token localStorage.getItem(token) const userInfo JSON.parse(localStorage.getItem(userInfo) || {}) if (to.meta.requiresAuth !token) { next(/login) } else if (to.meta.requiresAdmin userInfo.role ! 1) { next(/) } else { next() } })这套逻辑实际上是在前端做了一层导航守卫后端接口还有第二层校验双层防护。答辩时能分两层讲清楚会显得你考虑问题比较全面。4.2 组件拆分与页面实现以内容列表页为例前端页面的核心场景是内容列表页和内容详情页。内容列表页我拆了以下几个组件SearchBar.vue搜索和筛选区域ContentCard.vue单个内容卡片展示封面、标题、分类、浏览数PaginationBar.vue分页组件CategoryFilter.vue分类筛选标签组件拆分的原则是“一个组件干一件事”。ContentCard只负责展示一条内容的数据SearchBar只负责收集搜索条件并触发搜索事件父组件ContentList.vue负责组合这些组件、管理页面状态、调用接口。用Element UI提供的组件能少写很多CSS。比如分页直接用的el-pagination但要注意它的事件绑定current-change不要和click混淆。实际开发中我第一次没注意就把current-change写成了click结果点击页码根本没反应。Axios请求封装是前端联调的关键。我在utils/request.js里封装了一个axios实例统一配置了baseURL、请求拦截器、响应拦截器import axios from axios const request axios.create({ baseURL: process.env.VUE_APP_API_BASE_URL || http://localhost:8080, 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.clear() router.push(/login) } ElMessage.error(error.message || 服务器错误) return Promise.reject(error) } )这个封装同时处理了Token附带和错误提示以及401未授权的跳转。项目里所有接口请求都通过这个封装实例发出保证统一的异常处理逻辑。如果每个页面自己写axios请求很容易出现有的页面报错没提示、有的重复写Token逻辑的情况。4.3 前后端联调跨域与非规范接口的实战前后端联调是毕设过程中最容易“掉链子”的阶段。我碰到的问题主要有三类。第一类是跨域问题。前端跑在localhost:8080后端跑在localhost:9090前端请求后端就属于跨域了。解决办法有两个一是在后端加跨域配置二是在前端开发环境配代理。我的做法是两个都做了但推荐大家主要以后端的配置为准因为生产环境部署到同一个服务器时跨域就不存在了。后端的跨域配置也很简单Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }allowedOriginPatterns用*可以匹配任意来源不要用allowedOrigins(*)因为带Cookie的请求不允许*通配。加上allowedMethods里必须有OPTIONS否则浏览器预检请求会直接失败。第二类是时间格式问题。后端返回的LocalDateTime默认是2024-12-26T10:23:45这种带T的格式前端直接展示看起来很怪。我处理方式是在application.yml里配置JSON序列化格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8但注意这个配置只对java.util.Date生效对LocalDateTime还要加Jackson的JavaTimeModule配置。我直接在配置里自定义了一个Jackson2ObjectMapperBuilderCustomizer的Bean。第三类是后端返回的JSON字段命名问题。MyBatis-Plus默认下划线转驼峰通常是对的但如果前端传参是createTime后端接收实体用create_time就需要在全局配置里设置map-underscore-to-camel-case: trueMyBatis-Plus默认就是true或者用JsonProperty注解。我统一采用的策略是Java实体里用驼峰命名数据库字段用下划线命名MyBatis-Plus这个配置从SpringBoot的application.yml里开启mybatis-plus: configuration: map-underscore-to-camel-case: true这样前后端传输的JSON都是驼峰字段数据库层转换交给框架。这个思路和SQL脚本中的字段命名保持了严格一致不容易出现歧义。5. 答辩准备与常见问题排查实录5.1 评审老师最常问的几个问题别等被问到再想毕设答辩的提问环节是有规律可循的提前准备可以大幅降低翻车概率。根据我的经验光影平台这类SpringBootVue全栈项目老师的问题集中在几个方向。第一个方向是“为什么”。为什么用JWT而不是Session为什么选MyBatis-Plus而不是原生MyBatis为什么前端用Vue2而不是Vue3这类问题的核心是考察你有没有真正理解自己用的技术。回答的套路是先说出自己的选型理由开发效率、团队生态、资料丰富、符合项目体量再对比一下别的方案有什么劣势比如Session在分布式环境下不好扩展、原生MyBatis需要写大量XML。这里不用说得太深但至少要让老师知道你不是随便选的。第二个方向是“怎么做”。比如用户密码是怎么加密的文件上传后怎么访问的评论功能如何防止重复提交你要能对着代码说出关键实现。我的建议是把自己做过的每个模块的核心代码都回顾一遍至少能讲出大概流程。第三个方向是“有什么问题”。这个方向其实最容易被忽视但老师特别爱问。比如项目有什么不足如果数据量大了怎么办你打算怎么优化我准备了两个诚实的改进点一是文件上传目前存在本地磁盘后续可以迁移到对象存储二是内容检索目前是简单SQL模糊查询学有余力可以接Elasticsearch。这些真实的不足点反而比“我做的项目完美无缺”这种回答更可信也更能体现你的成长意识。5.2 部署与启动从零到一跑通项目的完整步骤一个项目交付出去如果别人按文档跑不起来那这个项目的价值要大打折扣。我整理了从零开始部署的完整链路。前置环境准备JDK 1.8、Maven 3.6、Node.js 14、MySQL 8.0。版本一定要在文档里写清楚不然别人用的JDK版本不同可能启动报错。第一步创建数据库。在MySQL执行init.sql和data.sqlmysql -u root -p init.sql mysql -u root -p data.sql第二步修改后端配置。打开application.yml把数据库地址、用户名、密码改成自己的server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/guangying_platform?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword redis: host: localhost port: 6379第三步启动后端。我用的IDE是IDEA直接运行PlatformApplication.java就行。如果用命令行mvn clean package -DskipTests java -jar target/guangying-platform.jar这里有个经验第一次打包时间会比较长Maven要下载依赖建议提前把仓库配置成国内镜像否则可能卡很久。第四步启动前端npm install npm run serve注意npm install在依赖较多的时候也容易报错常见的原因是网络或者node-sass的版本问题。一个省心的方法是使用cnpm或者pnpm但要注意锁文件差异。我在文档里提示如果npm install失败尝试删除node_modules和package-lock.json后重新安装。第五步访问前端地址http://localhost:8080用管理员账号登录看数据是否正常。如果页面白屏按F12看控制台报错。我在开发环境下遇到的多数白屏都是跨域或接口地址配置问题把VUE_APP_API_BASE_URL和前端请求路径仔细对照一遍基本能解决。5.3 常见问题与排查技巧速查表下面这几条是我在实际调试和帮朋友看代码时经常遇到的坑整理成了一张速查表碰到相同问题可以直接按这个思路排查现象排查思路解决建议前端能打开但接口全部401确认Token是否带上了确认JWT过滤器是否拦截了登录接口查看request.js的Authorization头在后端日志看JWT过滤器有没有报错上传图片后前端显示404检查静态资源映射是否配置确认文件实际保存路径是否以/vuploads/开始核对addResourceHandlers里的磁盘路径以及file.upload-dir是否为绝对路径数据库导入SQL时报错“Unknown collation”字符集排序规则问题统一使用utf8mb4_general_ci或utf8mb4_unicode_ci确保MySQL版本支持后端启动报端口被占用8080端口被其他服务占用netstat -ano前端npm install报node-sass找不到Node版本和node-sass不匹配升级到新版sass或将Node切换到项目要求的版本页面能加载但图片用相对路径无法访问前后端分离时路径拼接错误前端对以/开头的URL拼上VUE_APP_API_BASE_URL登录成功后刷新页面登录态丢失没有做用户信息持久化在store初始化时从localStorage恢复userInfo和token此外还有一个经验类技巧在本地调试时打开浏览器的Network面板看每个接口的请求URL、请求方法、响应体中的code和message大多数联调问题都能通过这一步快速定位。很多人习惯只看页面显示效果然后猜问题出在哪其实直接从接口层的响应判断会更高效。最后的几点个人体会做完这个项目最深的感受是毕设不是为了写代码而是为了让你完整地经历一次“从需求到交付”的过程。源码能跑只是及格线真正拉开差距的是SQL脚本是否严谨、接口文档是否清晰、答辨时能不能讲清每个链路的取舍理由。光影平台是我用来兜底的一个演进版本它的代码质量和文档完整度让我在找工作时能自信地把它放进作品集里。如果你也选了SpringBootVue的组合建议不要只盯着“写代码”花几天时间把文档、验证、演示视频都整理一遍你会发现自己对这个项目的理解深了很多。未来如果学了更多技术栈不妨把这个项目再过一遍加个微服务模块或者换一版移动端小程序能玩的扩展点还挺多。