SpringBoot+Vue前后端分离校园网站管理系统实战解析
发布时间:2026/10/1 21:42:07来源:尧图网络
接这个项目的时候对方说得很轻巧给信息学科部做个校园网站展示一下日常动态就行。结果等我梳理完需求才发现这不是一个展示型官网而是一个要支撑部里日常运转的轻量级信息系统。公告发布、课程资料下载、作业提交管理、往届优秀作品视频展示全都要塞进去。技术选型最终落在SpringBoot配合Vue这套组合上前后端完全分离这也是目前校园信息化场景里最常见的搭配。下面我尽量把完整的实现过程、选型理由和踩坑记录都讲清楚给正在做SpringBoot和Vue相关管理系统、毕业设计或者校园网站的读者一份可以直接参考的方案。1. 项目整体设计与需求拆解1.1 这个校园网站到底要解决什么问题信息学科部在学校里的定位比较特殊它既有行政职能又管理着大量教学资源还经常承办学科竞赛和展示活动。如果网站只是挂几个静态页面一周都没人打开一次那就失去了建设的意义。需求方跟我强调了几点老师要能随时发布公告学生要能查看课程资料和作业要求管理员要把往届优秀作品整理归档最好能在线播放展示。翻译成技术语言核心功能就变成了四块公告模块要支持富文本发布、置顶、按时间排序和分类查询课程资源模块要支持文件上传下载、按课程分类、控制下载权限作业管理模块要支持老师发布、学生提交附件、老师查看批改视频展示模块要支持视频上传和在线播放。除此之外还有一个贯穿全局的权限问题学生、教师、管理员三种角色看到的菜单和数据必须不一样。这个需求清单看起来不复杂但真正动手设计的时候会发现几个隐藏的技术点一堆大文件往哪存、视频怎么播放才不卡、权限怎么控制得不复杂又能覆盖三种角色。这几个点直接决定了后面技术选型的方向也决定了前期工作量的大头在哪里。1.2 功能模块划分与技术难点预判拿到需求之后我没有马上建SpringBoot工程而是先把页面原型草图画了一遍再对着页面把接口清单列了出来。前台用户端大概五六张页面后台管理端十几张页面粗算一下接口有四十多个。功能模块划分成六个大块认证与用户、公告、课程资源、作业、视频、系统管理每块都单独梳理了字段和交互流程。这个阶段我提前标记了几个可能拖慢进度的技术难点。文件上传首当其冲学生交的作业、老师的课程视频动不动几百MB必须提前规划上限和存储方案。视频在线播放是第二个难点不处理直接放给前端浏览器兼容性和拖动进度条都会出问题。第三个是权限Spring Security全量引入太重纯手写拦截器又怕有遗漏得找一个平衡。最后是部署形态前后端分离之后静态资源怎么处理API怎么转发也要提前定好。这几个预判在后面的开发中全部应验了尤其是视频播放那块前后折腾了三天才理顺。提前做难点评估的最大价值就是不会在开发中途突然被一个技术问题卡住然后回头推翻之前的设计。这种事一旦发生返工成本远比多思考两天高得多。2. 技术选型为什么是SpringBoot加Vue2.1 后端选型考量后端用SpringBoot几乎是没悬念的选择。校园网站这类系统业务逻辑不复杂但绕不开数据库操作、文件存储、权限控制这些通用能力SpringBoot的生态把这些都覆盖得明明白白。另外一个现实因素是这个项目后续大概率交给学生团队维护SpringBoot加MyBatis-Plus的组合在高校里熟悉的人最多接手成本最低。选一个社区活跃、资料多的技术栈本身就是降低长期维护风险的手段。版本选择上我用了SpringBoot 2.7.x。很多人问为什么不用最新的3.x这里有个很实际的坑SpringBoot 3.x强制要求JDK 17起步而不少校园服务器的JDK还停留在1.8强行升级意味着整个运维环境都要跟着动。对一个要稳定运行的网站来说这种升级没有必要。所以除非是从零开始且环境完全可控否则校园类项目用2.7这种长期维护的稳定版本更稳妥。ORM层面我推荐MyBatis-Plus理由很直白单表增删改查不用写SQL自带分页插件逻辑删除和字段自动填充这些功能能省下大量重复代码。这个项目里大部分表都是标准的增删改查MyBatis-Plus能把代码量压缩到三分之一左右。遇到多表关联查询的时候手写SQL也不冲突两者配合没有任何问题。2.2 前端选型考量前端选Vue 3代码组织用组合式API配合Vue Router和Pinia做路由与状态管理。选择Vue而不是React主要考虑团队的上手曲线Vue的模板语法对写后端转前端的同事非常友好看到v-for、v-if基本能猜到什么意思这种低心智负担在小型团队里能明显降低沟通成本。工程构建工具用的Vite。Vite在开发环境的启动速度和热更新都比Webpack强很多校园网站页面数量不算多但每次改点代码都要等Webpack慢吞吞地重新编译那种体验太折磨人了。Vite的热更新基本是秒级的改完代码浏览器立刻反应开发节奏完全不一样。组件库选了Element Plus。后台管理界面大量涉及表格、表单、弹窗、上传组件Element Plus跟校园管理系统这种风格匹配度很高省掉了手写UI组件的时间。前台展示页面我则是用少量自定义样式加Tailwind CSS的方式页面更轻盈不会跟后台管理界面长成一个模子。2.3 整体架构与目录规划整套架构用一句话概括SpringBoot对外提供RESTful APIVue独立开发、独立部署两者通过HTTP请求交互。开发阶段用Vite的代理转发解决跨域问题生产环境由Nginx统一转发前端静态资源与API请求。目录规划上我把前后端拆成两个独立工程而不是混在一个仓库里。后端是标准Maven工程按controller、service、mapper、entity、config分包前端用Vue官方脚手架生成router、store、api、views、components各司其职。独立拆分的最大好处是两个端可以并行开发只要提前把接口文档定好谁也不用等谁。接口文档这块我用Apifox管理定义好每个接口的路径、入参、出参之后前后端各自按文档开发。前端可以用Mock数据先把页面跑起来后端直接用Postman调接口验证逻辑两边完全不互相阻塞联调阶段的冲突也少了很多。这个项目能按计划推进接口先行功不可没。3. 后端落地SpringBoot从搭建到核心实现3.1 工程初始化与关键配置创建工程我用的是IDEA的Spring Initializr选好SpringBoot 2.7.18勾选Web、MySQL驱动、Lombok再手动补上MyBatis-Plus、MinIO客户端、JWT工具包。这里有个小建议Initializr上有的依赖直接勾没有的不要硬凑手动添加其实更可控因为你能够精确锁定版本号避免依赖传递带来的版本冲突。application.yml是整个后端配置的核心。我按环境拆成三个文件公共的application.yml、开发环境application-dev.yml、生产环境application-prod.yml用spring.profiles.active控制激活哪个环境。这个习惯强烈推荐校园项目经常要在开发机和服务器之间来回切换配置没有环境隔离的话每次部署都要改一堆配置改错一个就够查半天。数据库连接这块我用的HikariCPSpringBoot默认就带性能足够不需要额外折腾。spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/info_dept?characterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的密码 hikari: maximum-pool-size: 10 minimum-idle: 2 servlet: multipart: max-file-size: 500MB max-request-size: 500MB数据库连接URL里的characterEncodingutf8和serverTimezoneAsia/Shanghai缺一不可不加这两个参数中文乱码和时区报错迟早找上门。multipart配置也特别容易被忽略SpringBoot默认文件上传大小限制只有1MB不调大的话学生传作业视频直接报错。我直接设成500MB主要是视频文件比较大但这个上限也不是越大越好得结合服务器磁盘和带宽来定。还有两个提升体验的小细节。SpringBoot的自动装配原理说到底就是启动时扫描spring.factories里的自动配置类按条件加载所需的Bean理解了这一点再排查配置问题就从容很多。另外我顺手用在线Banner生成器做了一个学科部主题的ASCII艺术字Banner启动日志里打出来一行标识纯属锦上添花但对项目辨识度和团队士气有正向作用。3.2 数据库设计与ORM选型数据库设计我遵循一个原则先把核心业务表建好辅助表用到时候再加。核心表就是用户表、公告表、课程表、作业表、视频表这几张。表数量不多但字段设计上有些细节必须注意。用户表的角色字段我直接存字符串取值是ADMIN、TEACHER、STUDENT。有人喜欢做成标准的角色关联表但对于三种角色的场景属于过度设计一个字段就搞定了。密码字段存的是BCrypt加密后的散列值长度设置为60正好对应BCrypt的输出长度如果建表时图省事设成varchar(32)认证永远成功不了这个坑特别隐蔽。作业表相对复杂因为它要关联老师、学生、课程三个维度。我的设计是作业主表存标题、要求、截止时间提交信息单独一张表存提交人ID、作业ID、提交时间、附件路径。这样设计的好处是一个作业可以多人提交同一个人也可以多次提交系统只保留最新一次再用status字段标记批改状态。这种拆法既灵活又不会让主表字段膨胀。MyBatis-Plus使用上有几个技巧值得说。实体类统一加TableName注解明确指定表名防止类名与表名不一致时映射出错逻辑删除字段加TableLogic后所有删除操作自动变成update语句误删数据还能找回创建时间和更新时间通过MetaObjectHandler自动填充完全不用在业务代码里手动维护。这些功能看起来小多个表堆叠起来节省的代码量相当可观。3.3 文件存储方案MinIO集成文件存储是这个项目里最有讨论价值的部分。一开始我考虑过直接存服务器本地磁盘文件路径写进数据库实现最简单。但后来发现两个隐患一是视频文件越来越大磁盘空间告急就得手工迁移二是应用重新部署时如果文件落在打包目录里很容易被新包覆盖误删。所以存储方案改成了MinIO。MinIO是开源的对象存储服务兼容S3协议部署极其简单下载二进制文件一条命令就能启动。校园网服务器资源有限用MinIO比单独搭一套专用存储服务轻量得多它自带Web管理界面管理员能直接看到存储桶里的文件排查问题非常直观。后端集成MinIO只需要引入Java客户端依赖再封装一个StorageService核心操作就三个创建存储桶、上传文件、生成访问链接。Service public class MinioStorageService { Value(${minio.endpoint}) private String endpoint; Value(${minio.access-key}) private String accessKey; Value(${minio.secret-key}) private String secretKey; Value(${minio.bucket-name}) private String bucketName; private MinioClient client; PostConstruct public void init() { client MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); try { if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); } } catch (Exception e) { throw new RuntimeException(MinIO初始化失败, e); } } public String upload(MultipartFile file) throws Exception { String objectName UUID.randomUUID() _ file.getOriginalFilename(); client.putObject(PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build()); return objectName; } }这里有一个细节存储桶权限我设置的是private文件上传后返回对象名前端需要访问时再通过预签名URL获取临时链接。直接设为public虽然省事但意味着任何知道链接的人都能下载作业和课程资料这些不该公开的文件就泄露了。预签名URL可以设置有效期比如一小时对校园网站这种场景完全够用同时兼顾了方便与安全。MinIO还有一个很适合校园场景的特点Docker部署一条命令就完成。docker run -d \ --name minio \ -p 9000:9000 \ -p 9001:9001 \ -e MINIO_ROOT_USERminioadmin \ -e MINIO_ROOT_PASSWORDyourpassword \ -v /data/minio:/data \ minio/minio server /data --console-address :90019000是API端口9001是控制台端口。启动之后在控制台创建一个存储桶把Access Key和Secret Key填到后端配置里。这样整个系统的文件存储统一走MinIO应用怎么重启、目录怎么迁移文件都不会丢。3.4 登录认证与权限控制登录认证我没有引入整套Spring Security而是用JWT加自定义拦截器的轻量方案。原因是这个项目的权限模型比较简单三种角色接口权限基本可以概括为管理员和老师能写、学生只能读取自己的数据。Spring Security的过滤器链配置在这种场景下反而显得冗余维护成本也高。JWT生成用的是io.jsonwebtoken:jjwt库。登录成功后把用户ID和角色写进token的claims过期时间设为24小时。前端拿到token存到localStorage每次请求在axios拦截器里把token塞进Authorization请求头。后端用一个AuthInterceptor实现登录校验。public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { response.setStatus(401); return false; } try { Claims claims JwtUtil.parseToken(token.replace(Bearer , )); request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } catch (Exception e) { response.setStatus(401); return false; } } }权限控制我用了自定义注解RequireRole标注在需要特定角色的接口方法上。拦截器先解析注解再和token里的角色对比不匹配就返回403。这样核心逻辑很透明哪个接口什么角色能访问直接看方法上的注解就知道比配置一大段Security规则容易维护得多。这里踩过一个坑注册拦截器时一定要用addPathPatterns把不需要登录的接口排除掉比如登录接口本身、公告查询接口、视频播放接口。我第一次配的时候没排除干净导致前端首页调公告接口直接401排查了半天才发现是拦截器把公开接口也拦了。4. 前端落地Vue3从创建页面到路由细节4.1 工程创建、依赖安装与环境配置前端工程我用npm create vue3初始化选择Vue Router、Pinia、ESLint这几个选项。初始化完成后手动安装axios和Element Plus。这里要注意Element Plus的按需引入如果图省事全局引入打包体积会大很多。用官方推荐的unplugin-vue-components和unplugin-auto-import插件修改vite.config.jsimport { defineConfig } from vite import vue from vitejs/plugin-vue import AutoImport from unplugin-auto-import/vite import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }) ], resolve: { alias: { : new URL(./src, import.meta.url).pathname } }, server: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这个配置里的server.proxy非常关键。开发阶段前端跑在3000端口后端跑在8080端口直接请求必然遇到跨域。配置代理之后前端请求/api/xxx会被Vite转发到http://localhost:8080浏览器看到的是同源请求跨域问题在开发阶段就消失。生产环境由Nginx做同样的事。依赖安装方面多说一句校园网络环境下npm下载经常很慢配置淘宝镜像源一条命令就能提速。如果安装过程中碰到依赖冲突优先尝试删除node_modules和package-lock.json后重新安装比手动去改依赖版本靠谱得多这是我实际试过很多次的经验。4.2 路由设计与动态路由实现Vue Router的路由设计是前端部分的核心之一。我把路由分成两类公开路由和需要权限的路由。公开路由就是首页、公告列表、公告详情、登录页、视频展示页不需要登录就能访问。需要权限的路由按角色动态加载比如作业提交页面只有学生能访问后台管理页面只有管理员和老师能访问。动态路由的标准做法是登录成功后后端返回当前用户的角色和菜单权限列表前端根据这个列表动态添加路由。实现上依赖router.addRoute方法在路由守卫里做权限判断。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) return } if (token !store.state.userInfo) { store.dispatch(fetchUserInfo).then(() { const routes generateRoutes(store.state.userInfo.role) routes.forEach(route router.addRoute(route)) next({ ...to, replace: true }) }) return } next() })这里有个容易踩的坑动态添加路由之后如果刷新页面动态路由会全部丢失因为内存中的路由定义没了。解决方法是在刷新后重新从后端拉取用户信息再走一遍动态添加流程。我第一次没处理刷新后用户明明登录了却被弹回登录页加了持久化存储判断才解决。路由的meta字段建议统一约定比如meta: { requiresAuth: true, role: [ADMIN] }路由守卫只读meta做判断权限逻辑不要散落在各个页面组件里。路由模式我推荐使用history模式URL更干净但部署时服务器必须把所有路径重定向到index.html否则刷新就404具体配置放到后面的部署章节。4.3 公告、课程等核心页面开发公告列表页看起来简单实际写起来细节不少。我用的方案是Element Plus的el-table配合分页组件数据从后端接口拉取。公告内容用v-html渲染因为后端存的是富文本HTML。这里有个安全提醒v-html之前一定要确认内容来源可信。校园网站的公告是管理员发的风险不大但如果接入了用户评论之类的内容必须做XSS过滤。课程资源页比公告复杂一些因为有分类筛选和文件下载。顶部放分类下拉框下面是资源列表每一行显示资源名称、所属课程、上传时间还有一个下载按钮。下载操作不能直接用window.open因为带token的请求没法拼在URL里我的做法是先调后端接口获取预签名URL再拿这个URL做下载跳转。大文件下载时还要加进度提示不然用户以为页面卡死了。作业模块是这个项目里交互最多的部分。学生端有作业列表、提交作业表单表单里有上传文件组件老师端有作业发布表单和作业批改列表。上传组件用Element Plus的el-upload注意要设置http-request自定义上传方法不要用默认的action上传因为默认方式不好加token请求头。自定义方法里用axios传到后端接口同时监听上传进度const customUpload async (options) { const formData new FormData() formData.append(file, options.file) const res await axios.post(/api/upload, formData, { headers: { Content-Type: multipart/form-data }, onUploadProgress: (e) { options.onProgress({ percent: Math.round((e.loaded / e.total) * 100) }) } }) options.onSuccess(res.data) }上传进度反馈看起来是小功能实际体验价值非常大。校园网环境下上传一个100MB的文件可能要好几分钟没有进度条的话用户会反复点提交反而加重服务器负担。页面组件组织上我坚持一个页面一个组件目录页面里的弹窗、表单、表格拆成子组件。比如作业管理页面WorkList.vue负责列表展示WorkSubmit.vue负责提交弹窗WorkGrade.vue负责批改界面。组件之间通过自定义事件和插槽通信Element Plus的表格和弹窗组件大量使用插槽来扩展内容。比如在表格操作列里自定义按钮就要用#default插槽封装通用弹窗时底部按钮区也会预留插槽让调用方自由控制。Vue的插槽机制是组件设计里的核心技能把这个用好了组件复用度和代码整洁度能提升一个档次。4.4 M3U8视频播放与文件预览视频播放是这个项目里折磨我最久的功能。一开始需求方说放个视频能看就行我就直接用video标签塞了一个MP4上去。结果问题接踵而至MP4文件稍大拖动进度条就卡顿暂停再继续容易黑屏不同浏览器兼容性也参差不齐。后来查资料才意识到校园网环境下做视频在线播放最靠谱的方案是HLS协议。HLS把整个视频切成很多小的TS分片通过M3U8索引文件按顺序播放天然支持拖动、断网恢复而且浏览器兼容性好。iOS的Safari原生支持HLS但Chrome、Firefox这些主流浏览器都不直接支持需要播放器库来处理。播放器我用了hls.js它能把M3U8转成MediaSource兼容性好代码量也少不需要安装任何浏览器插件。import Hls from hls.js if (Hls.isSupported()) { const hls new Hls() hls.loadSource(videoUrl) hls.attachMedia(videoElement) hls.on(Hls.Events.MANIFEST_PARSED, () { videoElement.play() }) }但新的问题来了这个项目里的视频没有专业视频网站的转码流水线管理员上传的是手机录制的课堂实录总不能让他先转码再上传。我的解决方案分两步走管理员上传视频时文件直接传到MinIO后端同时启动一个任务用FFmpeg把视频转成HLS流。ffmpeg -i input.mp4 -codec copy -hls_time 10 -hls_list_size 0 output.m3u8-codec copy参数很关键表示不重新编码只做重新封装速度快得多几分钟的视频几秒钟就处理完。但要注意如果源视频本身不是H264编码直接copy可能导致播放端不支持。我项目里的视频基本都是手机录制的H264格式所以copy方案可行。如果来源复杂还是老老实实加-preset veryfast -c:v libx264重新编码虽然慢一些但兼容性最稳。转码生成的TS分片和M3U8索引文件再一起传到MinIO同一个目录下前端拿M3U8的链接播放。FFmpeg需要提前装在服务器上Java负责调用命令行这是目前最成熟的做法。如果不想依赖FFmpeg也可以尝试Java的jcodec这类库但社区成熟度和处理复杂编码格式的能力差距明显遇到问题很难查资料。提示视频转码任务建议用异步线程池处理不要在HTTP请求里同步执行否则管理员上传一个视频要等转码完成接口才返回体验极差。文件预览也顺带说一句。课程资料里经常有PDF前端直接用iframe打开PDF地址就行。但PDF如果存放在MinIO私有桶里直接给iframe地址是访问不了的必须通过后端接口返回预签名URL再塞给iframe。我当时在这里卡了一阵后来才意识到问题出在私有桶权限上。5. 前后端联调、构建与部署5.1 跨域配置与接口联调联调阶段遇到最多的就是跨域。开发阶段虽然Vite里已经配了代理但有些接口在调试时直接访问后端地址还是会触发CORS。为了让开发和调试都顺畅后端也加了一套CORS配置用WebMvcConfigurer重写addCorsMappingsConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .maxAge(3600); } }注意生产环境不要用allowedOriginPatterns(*)最好限制成实际部署域名。开发阶段宽放没问题上线前一定要收紧。另外POST请求带JSON时浏览器会先发一个OPTIONS预检请求如果CORS配置没放行OPTIONS方法前端会看到CORS blocked的错误实际数据根本没发出去。CORS配置里放行OPTIONS并设置maxAge(3600)浏览器一小时内不会再重复发送预检请求联调效率能提升不少。接口联调时我配合Swagger使用SpringBoot集成springdoc-openapi启动后访问/swagger-ui/index.html就能看到所有接口的入参出参。前端直接对着Swagger调接口省掉大量来回确认字段的沟通。生产环境记得把Swagger关闭用环境变量控制开关。5.2 前端打包与Maven构建前端打包命令是npm run build产物在dist目录。打包前有几个检查项必须过一遍环境变量是否正确、路由模式是否设置好、静态资源路径有没有用绝对路径。我实际遇到过打包后所有图片404的问题原因是Vite默认的资源路径是/部署到子路径下就找不到需要在vite.config.js里设置base: ./。这个细节在网站根目录部署时没有影响一旦放到二级路径就暴露。后端构建用Maven建议用本地安装的Maven或IDEA自带的不要用项目里的mvnw包装器。校园网络下载Maven依赖本来就慢包装器每次还要先下载Maven本身纯属浪费时间。IDEA里配好阿里云镜像源依赖下载速度会快很多。构建命令很简单mvn clean package -DskipTests构建完的jar包在target目录下。SpringBoot的打包插件会自动打可执行jar内置Tomcat直接java -jar就能运行。多模块项目要显式配置mainClass避免打包时找不到启动类。注意构建顺序是先构建后端、再构建前端最后一起部署。前端构建的dist目录不要强行塞进后端的src/main/resources/static里虽然能实现一个jar同时提供接口和页面但前后端就耦合了后面改前端也要重新打后端包。建议始终前后端分开部署。5.3 服务器部署与环境变量部署Linux服务器时我规划了清晰的目录结构。前端静态文件放在/var/www/infosite后端jar放在/opt/infosite/app.jarMinIO数据目录放在/data/minio三个服务互不干扰。Nginx配置是部署环节最容易出问题的地方。前后端分离后页面和接口请求都经过Nginx核心是两条location规则。前端页面请求匹配根路径返回index.html接口请求匹配/api前缀反向代理到后端服务。server { listen 80; server_name your-domain.com; root /var/www/infosite; index index.html; client_max_body_size 500m; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } }try_files $uri $uri/ /index.html;这一行必须写否则history模式的路由刷新时会404。proxy_pass结尾有没有/也很有讲究location /api/配proxy_pass http://127.0.0.1:8080;无斜杠完整的/api/xxx路径会传给后端如果proxy_pass结尾带//api前缀会被去掉再传给后端。两种写法对应不同接口前缀设计搞混了就会出现404或路径错乱。client_max_body_size 500m也别忘了不然大文件上传时Nginx直接返回413。后端jar包启动我用nohup加日志重定向nohup java -jar /opt/infosite/app.jar --spring.profiles.activeprod /opt/infosite/app.log 21 生产环境用--spring.profiles.activeprod指定配置读取application-prod.yml里的生产数据库和MinIO地址。这样开发和生产配置完全隔离不会出现开发库被测试数据污染的问题。6. 踩坑总结与实战建议6.1 典型问题排查速查表项目过程中踩过的坑不少我整理成一张速查表贴在了团队Wiki上后续维护的同学照着排查能省很多时间。现象常见原因排查步骤解决方案前端请求接口返回404路径前缀不匹配查看浏览器Network面板请求URL统一/api前缀检查Nginx的proxy_pass写法上传文件超过100MB报错SpringBoot默认限制1MB或Nginx限制查看后端日志与Nginx日志调大multipart配置和client_max_body_size刷新页面404Vue Router history模式未配置切到hash模式或检查Nginx try_fileshistory配try_files或直接改hash模式中文乱码JDBC连接串缺编码参数查看数据库连接URL加characterEncodingutf8登录成功但请求被401拦截token没有正确传递检查axios拦截器是否带Authorization头统一在axios请求拦截器里设置请求头视频播放拖动卡顿视频未转成HLS流检查是否是直接播放MP4用FFmpeg转HLS分片MinIO文件上传成功但访问404桶权限设置错误或预签名URL过期确认桶权限与URL有效期设置合适的桶策略或调整签名URL有效期Element Plus组件样式丢失按需引入配置不正确检查vite.config.js的插件配置配置unplugin-auto-import和unplugin-vue-components这张表里的8个问题我实际经历了至少6个。其中刷新404和超大文件上传报错是同事遇到最高频的问题排查路径基本都是浏览器开发者工具加Nginx日志两个方向几分钟就能定位。6.2 个人开发建议与经验体会最后聊一点开发这类校园项目的感受。第一个建议动手写代码前先把接口文档和数据库表定下来。校园网站看着功能不多但前后端并行开发若不提前定义好接口两边对字段名的理解不一致联调阶段会浪费大量时间。我吃过一个亏作业提交时间这个字段前端用了submitTime后端用了createTime联调对了一个小时才发现是同一个字段。第二个建议是控制技术复杂度。SpringBoot加Vue这套组合本身已经很成熟不要在项目里为追求新技术而引入重量级组件比如数据库读写分离、消息队列、微服务这些除非有明确的业务量支撑。我也调研过国产数据库金仓加读写分离的配置方案但按校园网站的实际并发量来看单实例MySQL完全够用。把时间花在把核心功能做稳上比堆砌技术更有价值。第三个建议是安全意识不能松。校园网站同样暴露在公网环境安全防护不能忽视。我的做法包括数据库账号用最小权限账号而不是root密码用BCrypt加密JWT过期时间设为24小时文件上传做类型和后缀白名单校验Swagger文档在生产环境关闭。这些措施花不了多少时间但对整体安全性的提升非常明显。如果后续还要扩展可以考虑在视频转码基础上增加封面截取公告模块增加审核流程作业模块接入成绩统计导出Excel。核心功能稳定之后扩展这些业务功能都是按部就班的事。整个项目做下来我最深的体会是校园网站这类项目真正的难点不在技术新颖度而在于需求梳理得是否清楚、基础功能做得是否扎实、部署运维是否顺手。把这些做好了SpringBoot加Vue这套组合依然是校园团队做信息管理系统最省心、最稳妥的选择。
网站建设高端定制企业官网