SpringBoot+Vue校园资料分享平台:权限控制与分片上传实践

📅 发布时间:2026/9/9 21:08:15
SpringBoot+Vue校园资料分享平台:权限控制与分片上传实践
校园里找资料有多痛苦估计每个经历过期末的人都懂课程PPT散落在十几个群聊里学长学姐的笔记存在个人网盘链接且经常失效想找一份往年试卷得靠人品。做这个前后端分离的校园资料分享平台初衷就是把这堆烂摊子收拢成一个可检索、有权限、能追溯的系统技术栈选了SpringBootVueMyBatisMySQL这套经典组合。这篇文章把这套系统的完整设计和实现思路写透了从数据库怎么设计、权限怎么控制到文件上传怎么处理大文件、Nginx怎么配置全部基于可运行的源码展开。适合正在做课程设计、毕业设计的在校生也适合想拿一个完整前后端分离项目练手的初中级开发者。你跟着文章的思路走一遍不仅能把这个平台跑起来还能搞清楚每个模块为什么这么设计。1. 需求梳理这个平台要解决的四个核心问题很多人一上来就建表写接口做到一半发现权限乱了、文件传不上去、搜索查不到东西本质上是动手前没把需求想清楚。这个校园资料分享平台的目标用户是学生、教师和管理员三类角色对应的核心诉求各不相同。学生要能快速找到资料、下载资料教师需要能上传教学资料并管理自己发布的内容管理员则要负责用户审核、内容合规检查、分类维护这些事情。围绕这三类角色平台需要解决的四个核心问题是。问题一资料的组织方式。海量课件、试卷、笔记必须要有清晰的多级分类体系。单一层级不够用比如“计算机学院”下面还有“数据结构”“操作系统”这样的课程维度。设计上采用父分类子分类两级结构必要时可以扩展到三级。问题二访问权限的控制。校园平台不同于公开网盘有些资料只对校内用户开放有些资料是教师指定班级可见。权限控制的粒度至少要达到“未登录用户不可下载、普通用户受每日下载次数限制、管理员可管理所有内容”这个级别。这里涉及典型的RBAC基于角色的访问控制模型。问题三大文件传输的可靠性。教学视频动辄几百MB甚至上GB常规的HTTP上传会面临超时、中断后重传成本高等问题。前端直传后端接收这种简单方案在校园网环境下根本扛不住必须设计分片上传和断点续传机制。问题四内容的可检索性。资料存进去之后要能被找到标题搜索是底线如果能按课程名、教师名、资料类型组合筛选会更实用。搜索功能看似简单实际做起来有不少细节后面会专门展开。这四个问题对应到系统模块上就是用户认证、分类管理、资料管理、文件上传下载、搜索、数据统计等几大板块。下面这张功能清单表格可以作为设计阶段的参考。模块功能点服务对象用户认证注册、登录、JWT鉴权学生、教师用户管理用户列表、状态禁启用、角色分配管理员分类管理多级分类增删改查管理员资料管理资料上传、编辑、审核上下架教师、管理员文件传输分片上传、断点续传、进度显示学生、教师搜索筛选关键字标题搜索、多条件组合筛选所有用户下载管理权限校验、每日次数限制、记录留痕学生数据看板上传量、下载量、活跃用户统计管理员回到前后端分离这个架构选择上。校园项目的场景是学生用户数据量不大但访问时间段集中比如期末周突然有很多人同时上线下资料。前后端分离的好处在于前端静态资源可以走CDN或Nginx缓存后端只提供API在高并发下载场景下能有效分担服务器压力。同时Vue前端可以单独部署调试不需要每次改样式都重启Java进程开发体验好很多。2. 技术选型的底层逻辑为什么是这套组合而非其他这套技术组合在社区里讨论度一直很高总有人问为什么不选更新潮的框架。这里把我实际对比后的思考过程完整梳理一遍免得你在同样的问题上反复纠结。2.1 SpringBoot项目落地的效率关键SpringBoot的核心价值是约定大于配置。做这个校园平台时我只需要引入spring-boot-starter-web就能得到内嵌Tomcat的Web环境引入spring-boot-starter-jdbc配合MyBatis就能完成数据访问层的搭建。没有繁琐的XML配置文件需要维护这对小团队、课程设计、毕业设计这类人力有限的场景特别友好。它内置的自动配置机制在处理常规需求时优势明显。比如数据库连接池引入spring-boot-starter-jdbc后默认的HikariCP就是性能很好的连接池选型不需要额外调优就能扛住校园场景的并发量。再比如统一异常处理配合RestControllerAdvice注解可以非常优雅地封装返回结构避免每个Controller里都写try-catch。值得注意的是版本选择。SpringBoot 2.x和3.x在底层上有较大差异3.x基于Jakarta EE规范包名从javax迁移到jakarta。对于这个校园项目我选择了2.7.x这个相对成熟的版本生态兼容性好网上资料也多遇到问题好排查。如果你是非科班或者刚接触SpringBoot建议也先用2.7.x跑通再考虑升级。2.2 MyBatis手动控制SQL是有意为之选MyBatis而不选MyBatis-Plus是在这个项目里反复权衡的结果。很多快速开发框架会推荐直接用MyBatis-Plus因为它提供了BaseMapper的CRUD方法写代码确实快。但校园资料分享平台涉及大量自定义的查询逻辑——多表关联查资料与分类、统计下载次数排行、按时间区间聚合数据——这些场景用MyBatis-Plus的LambdaQueryWrapper反而绕不如直接写SQL直观。MyBatis的另一个不可替代的价值在于SQL的可控性。当你需要优化一条大表查询时直接在XML里改SQL即可不用去理解框架底层如何拼接条件。比如搜索功能里的动态SQL使用 标签根据前端传参拼装查询条件语义非常清晰。这里要提一个容易踩的性能坑MyBatis的一级缓存默认开启且作用域是SqlSession这在Spring结合MyBatis使用时通常意味着一次请求一个SqlSession问题不大。二级缓存则需要谨慎如果开启了二级缓存并且缓存了查询结果数据更新后如果清理时机不对就会出现脏读。通常在校园项目里数据量不大没必要开二级缓存减少一个隐患。2.3 Vue Element-UI快速搭建后台管理系统前端选型其实在Vue2Element-UI和Vue3Element-Plus之间纠结过一阵。考虑到这套系统面向教务资料管理场景核心页面是表格、表单、上传组件这类中后台界面用Element系列组件库效率最高。抱着稳妥的态度我用了Vue2 Element-UI组合。Vue的核心优势是数据驱动视图配合Vuex做全局状态管理、Vue Router做页面路由前端工程结构可以组织得相当清晰。比如用户登录后的token存储、路由守卫里的权限验证、axios实例的统一封装这些跨页面的逻辑在Vue的生态里都有成熟的解决方案。Vue里有一个常用但容易出问题的点 组件缓存。在这个项目里资料列表组件和数据看板组件需要缓存否则切tab再切回来要重新加载数据但分类管理组件不能缓存因为每次进入都需要拉取最新分类。这需要在路由配置的meta字段里控制是否需要缓存实际开发中很多人忽略这个细节导致列表状态错乱。2.4 MySQL版本选择与字符集配置MySQL选用8.0版本。8.0在窗口函数、CTE、默认字符集utf8mb4等特性上比5.7好不少而且8.0社区版对开发者免费学校服务器部署也顺利。如果非要用5.7注意默认字符集是latin1建库时要手动指定utf8mb4否则中文存储会出问题。MySQL 8.0里一个需要注意的配置是数据库连接串的时区参数。如果连接串里不加serverTimezoneAsia/Shanghai会因为时区差异报错。更隐蔽的一个坑是MySQL 8.0的caching_sha2_password认证插件某些版本的客户端驱动不兼容需要在创建用户时指定mysql_native_password。我在项目中用到的连接串配置如下。spring.datasource.urljdbc:mysql://localhost:3306/campus_share?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiuseSSLfalse spring.datasource.usernameroot spring.datasource.passwordyourpassword spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver3. 数据库设计表结构如何支撑权限、分类与文件元数据数据库是这套系统的地基。表结构设计得合理后面写业务代码会非常顺手设计不合理后面每个模块的查询都会变得别扭。这里把核心表结构的设计思路拆开说清楚。3.1 五张核心表的关系梳理这套系统的核心表可以概括为用户表、分类表、资料表、文件表、下载记录表。这五张表之间的关系是整个业务逻辑的骨架。用户表存储账号信息包括用户名、密码BCrypt加密存储、真实姓名、角色字段、状态字段。角色用简单的字符串区分比如ROLE_STUDENT、ROLE_TEACHER、ROLE_ADMIN没有引入复杂的权限表——因为这个平台的角色数量少且权限固定用admin/user两个核心值加状态控制就够了。认证采用JWT方案登录成功后服务端签发token后续请求在拦截器中校验。分类表设计成自关联结构parent_id字段指向自身主键实现无限级分类。例如计算机学院下面挂数据结构操作系统两个子节点。前端渲染多级分类菜单时通过递归组件处理。资料表是整个平台的核心表字段包括标题、描述、分类、上传者、文件地址、文件大小、下载次数、审核状态等。这里要特别处理的是文件地址不直接存物理路径而是存业务路径——即文件的相对URL例如/uploads/2024/05/12/uuid_数据结构.pdf。这样便于后续搬迁存储位置只要保持相对路径不变数据库不需要改动。文件表则细化到文件层记录文件名、存储名、MD5值、大小、上传者等。这里为什么要单独拆一张文件表而不是和资料表合并因为同一个MD5的文件如果被操作人员重复上传可以通过MD5值去重避免存储浪费。文件表的MD5字段加上唯一索引上传时先查MD5如果已存在直接复用节省服务器磁盘空间。下载记录表负责记录每次下载行为包括下载者ID、资料ID、下载时间、IP地址等。这个表有两个作用限量控制的基础数据来源以及平台运营的审计日志。关联查询时用资料ID和时间范围就能算出某份资料的热度趋势。3.2 索引设计高频查询路径的优化选择资料列表页最常见的查询是按分类查最新资料和按关键字查标题类目中心是资料表。这两类查询的索引设计非常直接category_id字段加普通索引因为分类筛选是高频且选择性较好的查询条件download_count字段也值得加索引用于排序下载排行榜。下载记录表是一个增长很快的表索引设计更要谨慎。resource_id和user_id各加普通索引用于快速验证某人是否下载过某资料download_time加索引用于时间范围统计。有必要提醒一个容易忽视的点不要在一张表上加超过5-6个索引。索引不是越多越好因为每次INSERT、UPDATE都要维护索引树写入会变慢。校园项目数据量撑死几十万条保证高频查询路径有覆盖就够了不需要为了理论上的完美把所有字段都塞进索引。3.3 文件存储与数据库的配合文件本身不存数据库存服务器磁盘或对象存储数据库只记录元数据。这套设计的核心思想是数据与文件分离。文件上传接口接收前端分片后先按日期分目录存储文件名重命名为UUID保证不重名然后记录到文件表。资料表里的file_url字段则指向这个存储的相对路径。文件上传成功后响应体返回文件ID和URL前端把这两个值随资料表单一起提交。后端创建资料记录时校验文件存在性与归属完成业务闭环。这个流程看似简单但顺序很重要先传文件后创建资料避免资料记录指向一个不存在的文件。中途如果资料创建失败文件会变成孤儿文件需要定时任务清理上传目录中超时未关联的记录我在项目中加了一个每天凌晨执行的清理任务删除队龄超过24小时且未关联资料的文件记录。4. 后端核心实现权限、分片上传与检索排序后端代码是整套系统的工作核心。这一节把三个最核心的实现详细展开分别是JWT权限认证流程、多线程分片上传、以及搜索排序的优化方案。4.1 JWT认证与权限拦截器链JWT方案的选择很明确无状态认证适合前后端分离服务端不存储会话信息扩展性好。依赖引入jjwt库工具类封装生成与解析方法。生成token时把用户ID和角色放到claims里然后设置过期时间常规设为7天校园场景比较适合一周免登录过期后需要重新登录。拦截器的实现共分三步。第一步自定义一个HandlerInterceptor实现类重写preHandle方法第二步从请求头Authorization中获取token并进行解析第三步将用户信息放入ThreadLocal供后续业务使用。这里有一个细节白名单路径要放行——登录、注册、验证码、公开资源的路径不应该进入拦截器否则用户没登录连登录页面都调不了接口。权限校验方面业务接口上加自定义注解RequireRole(ROLE_ADMIN)配合拦截器中的逻辑判断。实现方式是在拦截器中读出用户角色再检查当前请求路径对应的注解要求。这样做比在每个Controller方法里写if判断要简洁得多。上传资料和删除资料都是需要校验身份的操作前者要求登录用户后者仅限管理员。这里有一个非常值得注意的Java层问题ThreadLocal保存用户信息一次请求一个线程接口结束后必须remove掉否则在Tomcat线程池复用的场景下线程复用时ThreadLocal里还残留上一个用户的信息可能造成用户A的操作被记录到用户B头上。我实际踩过这个坑排查了半个晚上才发现是ThreadLocal没清理导致的。4.2 大文件分片上传前后端联动的完整链路教学视频通常有几百MB这是Web上传最难受的场景。大部分同学做项目时只处理几MB的PDF和PPT不会意识到大文件上传的复杂度。这里把完整实现梳理一遍。前端使用Vue结合vue-simple-uploader组件它封装了分片上传、秒传、断点续传等能力。组件将文件切片后依次请求后端接口上传每个分片所有分片传完后通知后端合并成完整文件。核心参数是chunkSize我设置为5MB——太大会导致单个分片传输仍容易超时太小时分片数量过多请求频繁效率低下5MB是实践中比较均衡的值。后端提供三个核心接口初始化上传接收文件名、文件MD5判断文件是否已存在。已存在直接返回秒传标识前端不再传任何分片。上传分片接收当前分片索引和分片数据写入临时临时目录。合并分片校验分片完整性将分片按顺序合并成新文件计算完整文件MD5并记录到文件表。分片上传接口有一个高频难题分片顺序。由于前端是并发上传分片的接口收到的分片顺序不一定是按索引递增的因此合并接口必须按分片索引排序后再拼接字节流。同时每个分片在临时目录中的命名要包含分片索引比如{uploadId}_{chunkIndex}.part。合并完成后清理临时目录。上传过程中的并发安全问题也不能忽视。同一文件同时发起分片上传请求后端需要保证创建的上传上下文是唯一的。解决方式是用ConcurrentHashMap维护一个uploadId到上传上下文的映射下次请求直接复用上下文。分片上传期间的跨域问题也值得提。前后端分离项目前端在8080端口、后端在8081端口就必然有跨域请求。生产环境用Nginx反向代理能解决跨域但开发阶段最简单的办法是在后端增加CORS配置允许指定来源访问。开发环境可以宽松一点允许http://localhost:8080即可。4.3 全文检索优化从LIKE到中文分词的渐进方案搜索模块一开始用的MySQL LIKE模糊匹配资料量几百条时体验还能接受但当数据量上万条LIKE %关键字%的查询性能会急剧下降因为这种写法无法利用索引。校园项目虽然数据量通常不大但搜索体验是用户感知最强的模块值得做得更好。简单的优化是先加全文索引MySQL 8.0的全文索引支持中文但默认分词对中文支持并不理想。更实用的方案是引入ElasticSearch做独立的搜索服务但这对校园级项目又显得过重了——要额外维护一套ElasticSearch服务部署复杂度会上升不少。折中的方案是构建一个简单的倒排索引表。在资料表里增加keywords字段存标题和描述里提取的关键词用空格分隔。搜索时改查keywords LIKE %keyword%因为keywords字段比较短扫描成本远低于直接查完整标题和描述。在实际项目中我用HanLP这个轻量大分词工具在后台定时提取关键词并同步到keywords字段里。HanLP支持多个版本如果不想引入较大的依赖和模型用最简单的StandardTokenizer静态调用也能达到不错的提取效果。搜索结果排序上设计了一个很基础的排序算法score 搜索引擎字段匹配权重 时间衰减系数。把标题命中记3分、描述命中记1分关键词命中再额外加分最后按计算分做ORDER BY。虽然简单但实测下来比单纯按时间排序的体验要好不少——相关度高的资料能浮到前面。4.4 高并发下载时的计数处理下载次数计数看似简单实则很容易错。如果直接UPDATE resource SET download_count download_count 1 WHERE id ?在高并发下确实能保证原子性但每条下载都要2次SQL查询验证权限更新计数热点数据的行锁竞争会让数据库压力剧增。一个实用方案是把计数操作异步化。下载接口先用Redis的INCR命令做自增然后返回文件流定期将Redis中的计数同步到数据库。这样数据库的写压力被削峰填谷Redis的高性能支撑了频繁的计数更新。如果不想引入Redis也可以用一个小的定时任务缓冲——把下载事件记录在内存队列中每30秒批量更新一次数据库但进程重启可能丢失缓冲区数据Redis方案更可控。并发下载时还需要限制用户的重复操作。在Redis里用set命令记录一个“下载中”标识过期时间设为30秒防止用户狂点下载按钮产生大量重复请求。这个操作成本极低但对系统资源保护和用户体验的改善都非常明显。5. 前端关键模块路由守卫、文件上传组件与m3u8视频预览前端工程的核心不只是页面展示还涉及状态的传递、权限的控制、交互的流畅性。这一节挑出三个关键模块详细讲讲。5.1 路由守卫与动态侧边栏的权限玩法Vue Router前置守卫是权限控制的第二道门第一道是后端接口鉴权。具体逻辑是请求路由前先检查本地缓存是否有token。没有token并且目标路由不在白名单内则强制跳转到登录页。有token但用户信息尚未加载就自动发起一次/user/info请求获取用户角色和基本信息。侧边栏菜单的渲染也是根据角色动态生成的。具体思路是在路由配置里给每个需要权限的页面写上meta.role字段例如管理员的路由meta.role为ROLE_ADMIN然后根据用户角色过滤路由表生成侧边栏菜单。这样管理员登录后能看到用户管理和分类管理入口普通学生登录后只能看到资料列表和个人中心。有一类很隐蔽的权限漏洞值得注意不能只做路由守卫因为路由守卫只是前端层面的隐性控制用户手动请求后端的接口地址时后端如果没做校验一样会泄露数据。所以角色的权限校验必须前后端共同完成前端隐藏入口、后端拦截非法请求。5.2 文件上传组件的封装与进度展示文件上传在多个页面都要用到教师端上传资料、个人中心传头像。所以封装成单独的upload组件是很有必要的。基于vue-simple-uploader封装后对外暴露的props包括文件类型限制、上传地址、分片大小、最大文件数等。大文件上传不能只给一个上传中的菊花转要给出百分比进度。vue-simple-uploader的进度事件处理起来很自然监听file对象的progress事件实时更新进度条数值。设计了一个上传队列面板显示所有待上传文件和各自的上传速度、进度、状态能直观看到哪个文件传完了、哪个失败了、失败原因是什么。断点续传的实现机制值得展开前端在调用初始化接口时如果后端返回文件已存在部分分片响应中会包含已上传完成的索引列表。前端根据这个列表自动跳过已提交过的分片只上传缺失的部分这就是续传的本质。实测在校园网环境下从普通上传升级为分片上传后一份700MB的视频从经常失败变成稳定成功这个改造的核心价值说得非常清楚。5.3 在线预览与m3u8视频点播资料平台不只是下载在线预览能极大提升学生的使用体验。PDF和图片的预览较易实现直接用浏览器内置能力或者iframe嵌入即可。比较麻烦的是视频预览尤其是某些教师上传的格式为m3u8的流媒体视频。Vue播放m3u8在社区里有一个很成熟的做法使用video.js配合videojs-contrib-hls插件或者用hls.js。我在项目中选了hls.js原因是它对hls的支持非常标准。需要特别注意的是hls.js需要通过Hls.isSupported()判断当前浏览器是否支持不支持的降级为video标签的native播放处理代码时把这个兼容逻辑考虑进去。// m3u8播放的核心逻辑简化示例 if (Hls.isSupported()) { const hls new Hls(); hls.loadSource(videoUrl); hls.attachMedia(videoElement); hls.on(Hls.Events.MANIFEST_PARSED, () { videoElement.play(); }); } else if (videoElement.canPlayType(application/vnd.apple.mpegurl)) { // Safari原生支持 videoElement.src videoUrl; }只有图片、PDF在线预览够用但视频这个高频需求做好之后平台的完整性提升非常明显。6. 部署上线的完整流程与踩坑记录源码能跑通只是走了一半真正把一个项目送到服务器供几十个学生同时使用会遇到一系列部署层面的问题。这里把从零到上线的完整流程写清楚包括一些折腾过的大坑。6.1 本地开发环境的搭建在本地跑通项目建议先安装这几样东西JDK 8或11、Maven 3.6、Node.js 14、MySQL 8.0。IDE个人推荐IDEA社区版也能胜任前后端两套代码的开发。如果IDEA创建SpringBoot项目时下载依赖超时解决方式是更换Maven镜像源为阿里云镜像在settings.xml里修改mirror节点配置即可改动很小但效果立竿见影。MySQL安装完成后需要新建数据库并导入项目中的sql脚本。脚本文件里包含了建表语句和初始数据包括默认管理员账号admin/admin123密码经过BCrypt加密不需要手动改数据库登录后可在个人中心修改。如果执行建表脚本时报错通常是字符集和时区的问题连接时指定下characterEncodingutf8和serverTimezoneAsia/Shanghai就能解决。后端的application.yml配置需要根据自己的环境改数据库账号密码。前端工程启动前先执行npm install安装依赖过程中如果依赖安装很慢用npm config set registry https://registry.npmmirror.com切换到国内镜像。然后启动后端再执行npm run serve启动前端开发服务器浏览器访问http://localhost:8080即可看到首页。6.2 前后端分离部署方案对比部署到云服务器时有两种方案可以选择。第一种是传统部署后端打包成jar包用java -jar运行前端npm run build之后把dist目录放到Nginx的html目录。Nginx既负责静态资源的托管也负责接口反向代理。这是最常见的方案简单直观。第二种是Docker容器化写一个docker-compose.yml编排MySQL、后端服务、前端Nginx三个容器。好处是环境隔离换服务器迁移部署很方便。坏处是增加了学习成本还要处理容器与宿主机之间的数据卷挂载问题比如MySQL的数据目录如果不挂载到宿主机容器删了数据就全丢了。校园项目如果没有特殊的快速扩缩容要求用第一种传统部署方案就够了运维成本最低。Docker容器化的价值在集群场景下才会充分体现出来作为个人项目简单跑起来第一优先。6.3 阿里云服务器的完整配置阿里云是很多学生项目第一台服务器的首选。服务器选配时校园项目2核4G配置够用带宽选3Mbps以上系统镜像选Ubuntu 20.04或CentOS 7.9。一个很多人一开始就卡住的地方安全组规则。云服务器默认只开放22端口其他端口全封闭。需要在安全组里手动放行3306端口如果不用远程连接可以不开放减少暴露面、8080端口或80端口。更安全的做法是3306端口完全不开数据库只在服务器本地使用远程管理用SSH终端来执行SQL操作这样能减少不少被攻击的风险。后端服务启动时用nohup java -jar campus-share-0.0.1-SNAPSHOT.jar app.log 21 日志输出到app.log方便排查。如果服务器一重启服务就没了可以写一个systemd服务脚本把jar包托管成系统服务设置Restartalways开机自启。Nginx的配置是整个部署环节中最关键的部分。前端静态资源交给Nginx托管接口路径/api/反向代理到后端端口核心配置如下。server { listen 80; server_name your-domain.com; # 前端静态资源 root /var/www/campus-share/dist; index index.html; # 前端路由history模式需要重定向到index.html location / { try_files $uri $uri/ /index.html; } # 后端接口反向代理 location /api/ { proxy_pass http://127.0.0.1:8081; 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 /uploads/ { alias /var/www/campus-share/uploads/; } }这个配置中有两个容易踩的大坑。第一个是Vue Router如果使用history模式刷新非首页页面时Nginx会报404必须用try_files配置兜底重定向到index.html。第二个是反向代理时如果没设client_max_body_size,大文件上传会被Nginx拒绝返回413错误需要在server块里加client_max_body_size 1024m;放开这个限制。6.4 上线后观察容量和稳定性线上运行后要重点观察两个指标磁盘空间和内存占用。上传文件越来越多时磁盘告急是必然发生的事。建议在云服务器控制台配置磁盘告警空闲率低于20%就提醒及时做清理或扩容。另一个稳定性的隐患是文件上传目录的权限问题。Nginx对/uploads/目录的访问需要正确的读写权限后端创建目录时如果使用的是mkdir -p那么目录的所有者和权限取决于执行用户的umask。推荐在部署文档里明确指定uploads目录的创建命令sudo mkdir -p /var/www/campus-share/uploads sudo chown -R www-data:www-data /var/www/campus-share/避免因权限问题导致文件写入失败。7. 从课程设计到生产级系统还需要做哪些升级如果你不满足于能跑起来想让这套系统更有竞争力可以从下面几个方向继续扩展。数据可视化看板是一个值得投入的方向。当前系统的管理后台只提供了最简单的数据列表如果加上ECharts图表展示每日上传量、下载量TOP10、活跃用户分布等整个管理端立刻会显得专业很多。实现上可以通过定时任务在每天凌晨统计数据存到一张summary表中前端直接查汇总数据渲染图表避免实时聚合大表。消息通知机制也是一个常见的需求。学生上传资料后管理员审核通过与否需要通知上传者管理员操作封禁账号后需要通知被处理的用户。实现不一定要引入RabbitMQ这些消息队列最简方案是做一个通知表业务操作时往通知表插入一条记录用户登录时前端轮询接口拉取未读消息即可。数据量大了之后再考虑WebSocket实时推送。文件块的合并和上传目前是同步处理的合并大文件时会长时间占用HTTP连接。生产级方案是用线程池异步合并接口返回正在合并的状态前端轮询查询合并结果。这需要在前端配合设计上传状态机复杂度会增加不少但系统健壮性会有显著提升。全文搜索如果资料量真的超过了十万条就值得认真考虑引入ElasticSearch了。当MySQL的keywords LIKE查询超过200ms时搜索体验就会有明显卡顿此时可以设计一个同步任务把资料表的数据同步到ES索引中搜索接口改成先查ES再取详情。这个升级可以以后按需做前期能用MySQL先顶一阵。8. 疑难杂症排查实录整个开发部署过程中最费心力的往往不是写代码而是排错。这里整理几个真实遇到且极具代表性的问题如果你不幸踩中可以直接对照排查。现象一接口报401但登录接口明明正常排查链路先看浏览器控制台请求头确认请求带了Authorization。如果带了看是token过期、格式错误还是后端解析失败。一个容易忽略的点是JWT工具类解析时如果签名密钥与生成时不一致会直接抛出异常需要在捕获异常时返回401而不是500。现象二前端页面能打开但所有接口都404排查链路先确认后端是否启动了再确认Nginx的proxy_pass地址是否正确。如果后端地址是localhost:8081而Nginx在宿主机上那么proxy_pass必须写http://127.0.0.1:8081不能写localhost:8081——在某些系统上Nginx解析localhost可能会优先解析到IPv6地址导致连接不到应用。另一个原因可能是跨域问题Vue开发环境访问8081接口时如果后端没配CORS浏览器会拦截控制台会明确提示跨域错误。现象三上传大文件到99%后失败这个问题的典型原因就是Nginx默认允许的请求体太小。client_max_body_size默认值为1m上传几MB的图片没问题遇到几百MB的视频就必然会失败。解决方式就是前面提到的在server块里调大该参数。现象四生产环境刷新页面404这个在部署章节出现过Vue Router的history模式需要Nginx配合try_files $uri $uri/ /index.html;。如果你的前端路由用的是hash模式则不存在这个问题但URL会多一个#号不够美观。推荐用history模式并正确配置Nginx。这些排查经验其实不掌握也很正常都是靠翻日志、搜报错、对比官方文档一步步试出来的。部署一个前后端分离项目最大的价值就在于能让你真正理解客户端、服务端、数据库、代理这几层之间的协作关系出错时有一种全链路视角的排错能力。我个人在实际操作中的体会是不要一开始就追求用上最新最强的框架先把这套基础的校园资料分享平台跑通然后把一个个模块吃透比徒有大量依赖却说不清原理要有价值得多。遇到报错先看日志日志里没有线索就去确认环境配置一步步缩小范围绝大多数问题都能定位到具体哪一层。最后再分享一个小技巧在本地写代码时养成用IDEA的MyBatis Log插件输出完整可执行SQL的习惯排查MyBatis相关的数据问题会省很多时间。