1. 项目概述与整体设计思路
1.1 为什么要做科创项目管理系统
每年大学生创新创业训练计划(简称"大创")、各类学科竞赛项目的申报、中期检查和结题验收,很多高校还在用Excel表格加微信群的方式管理。材料散落在各个老师的电脑里,学生不知道项目审核到哪一步了,指导老师要反复催交材料,教务管理员在汇总时面对几十个格式不统一的Excel表头欲哭无泪。这个痛点我体验过不止一次,所以当朋友提出要做一套科创项目管理系统时,我几乎没有犹豫就接了下来。
这套系统的核心定位很明确:解决大学生科创项目从申报、审核、中期检查到结题验收的全流程线上化管理问题。技术栈选择了当下最主流的组合——SpringBoot + Vue + MyBatis + MySQL,前后端完全分离。系统上线后,学生可以在线提交项目申报书,指导老师在线审批,教务管理员可以实时查看全校项目的整体进度,所有流程节点都有记录可查,工作效率提升非常明显。
我把这套系统的完整源码和部署过程整理出来,适合三类人参考:一是正在做毕业设计、需要一套完整前后端分离项目的计算机专业学生;二是高校里负责科创项目管理、想推动信息化建设的老师或管理人员;三是想系统学习SpringBoot和Vue前后端分离开发实战的开发者。对于前两类人,这份资料可以直接拿来用或者二次开发;对于第三类人,整个项目涵盖了权限认证、文件上传、流程审核、数据统计这些高频业务场景,学完能少走很多弯路。
1.2 系统核心业务流程拆解
在动手写代码之前,我先梳理了科创项目管理的完整生命周期。一个典型的科创项目从无到有,大致经历以下阶段:学生申报、指导老师初审、学院审核、学校教务处终审、立项公布、中期检查、结题验收。不同学校的管理流程会有细微差别,但主体的骨架基本一致。
这个流程拆解下来,系统的核心角色自然就清晰了:学生(项目负责人和成员)、指导老师、学院管理员、学校管理员。每种角色在系统里能看到的内容和能执行的操作完全不同,这直接决定了权限模块的设计思路。我采用的方案是基于角色的访问控制模型(RBAC,即Role-Based Access Control),把权限配置到角色上,再把角色分配给用户,这样新增用户时只要指定角色就能自动获得对应权限,管理成本非常低。
流程设计上,我参考了实际管理中的两个关键场景。第一个场景是校级管理员需要能够灵活配置审核流程。有的学院是两级审核,有的是三级审核,如果我把流程硬编码在代码里,后期调整会非常痛苦。第二个场景是每个项目的状态必须一清二楚,学生提交后随时能看到"待审核"还是"已通过",避免反复打电话问管理员。基于这两个考虑,我设计了一张"项目状态机"表来维护项目的生命周期状态,前端根据状态值渲染不同的标签样式,后端通过状态流转规则校验操作的合法性,双端配合,整个流程就非常顺畅了。
2. 技术栈选型与工程结构解析
2.1 为什么选SpringBoot而不是SSH
早些年做Java Web项目用SSH(Spring + Struts + Hibernate)是标配,但Struts的配置复杂度让人痛不欲生,光是一个XML配置文件就能写几百行。SpringBoot的出现彻底改变了这个局面,它把"约定大于配置"的理念发挥到极致,内置了Tomcat,很多场景下只需要一个注解就能跑起来一个Web服务。我选择SpringBoot,核心原因有三个:一是开发效率高,省去了大量模板化配置;二是生态成熟,和MyBatis、Spring Security、JWT这些组件对接都非常顺畅;三是招人容易,现在Java后端开发几乎都要求SpringBoot经验,这套项目学完直接对口就业需求。
版本选择上我用了SpringBoot 2.7.x,而不是最新的3.x。这里有一个很重要的考量:SpringBoot 3.x要求JDK 17及以上,JDK版本升级会带来很多兼容性问题,而且部分老版本的MyBatis、连接池等依赖在新版本下可能出现奇怪的报错。对于毕业设计和企业实际项目来说,稳定压倒一切,SpringBoot 2.7 + JDK 8是目前兼容性最好的黄金组合。如果读者本地装的是JDK 17甚至21,我也不推荐直接换新,更稳妥的做法是同时安装多个JDK版本,具体切换方法后面部署章节会讲到。
2.2 Vue 2还是Vue 3,我做了个保守选择
前端选型上,我最终用了Vue 2 + Element UI,而不是Vue 3 + Element Plus。这个选择可能让一些追求新技术的读者意外,但回过头看,这个保守决策在实际开发中省了非常多麻烦。
原因有几个方面。首先是Element UI对Vue 2的适配非常成熟,几乎所有组件都能直接开箱即用,网上的踩坑经验也极多,遇到问题基本一搜就有答案。而Element Plus在早期版本中存在不少细节问题,表格性能、表单校验方面都需要额外处理。其次是若依、vue-element-admin这些成熟的后台管理脚手架都基于Vue 2,可以快速借鉴优秀代码写法。第三,这套系统面向的最终用户是高校师生,浏览器环境多种多样,Vue 2在浏览器兼容性上明显优于Vue 3。
当然,如果读者是纯学习目的,或者打算长期维护这套系统,完全可以在此基础上迁移到Vue 3 + Vite + Element Plus,整体架构不用大变,只需要重写部分组件的生命周期和响应式逻辑。前端框架的选型永远没有"绝对正确",只有"适合当前场景"。
2.3 MyBatis还是MyBatis-Plus
MyBatis作为持久层框架,核心价值是让开发者可以用XML或注解的方式灵活编写SQL,避免了JDBC的样板代码。它的学习曲线平缓,对SQL的控制力很强,特别适合业务逻辑复杂、SQL需要手工优化调优的场景。在项目开发中我大量使用了动态SQL(if标签、foreach标签),配合MyBatis的缓存机制,查询性能相当可观。
不过,如果现在从头开发一个新项目,我可能会优先考虑MyBatis-Plus。MyBatis-Plus在MyBatis基础上做了增强,提供了通用的Mapper方法,单表CRUD不需要手写XML,还有分页插件、代码生成器,开发效率能再提升30%。在后续的业务迭代里,我逐步引入了MyBatis-Plus的Wrapper构造器来实现条件查询,确实省去了一堆XML文件的编写。两者并不冲突,很多生产项目是混用的,最关键的是自己清楚哪些场景用两个框架的哪个能力。
MySQL作为底层存储是最稳妥的选择,它的事务机制、存储引擎、主从复制能力足以支撑学校级别的并发访问量。这里提醒一句:表结构设置utf8mb4字符集、InnoDB存储引擎,是所有项目的默认前提,千万不要用默认的MyISAM,不然事务回滚和行级锁这些能力都用不上。具体设计细节下一节展开。
2.4 工程目录结构与代码分层
一个好的项目结构是后续可维护性的基础。我沿用了主流的前后端分离开发包管理方式:前端和后端分成两个独立目录,前端代码放一个仓库,后端代码放另一个仓库。本地的运行方式是前端启动Node服务代理请求,后端启动SpringBoot服务,通过Nginx配置的/api前缀做反向代理,这样可以完美绕过开发环境下的跨域问题。
后端代码采用标准的Controller-Service-Mapper三层架构。Controller层只负责接收请求参数和返回统一响应结果,不写任何业务逻辑;Service层处理核心业务逻辑,事务控制也在这层;Mapper层负责数据库操作。此外,config包放各种配置类,common包放统一返回结果、异常处理、工具类,entity包对应数据库表结构,dto包用于接收前端传来的视图对象,vo包用于封装返回给前端的数据结构。
前端部分则按照vue-element-admin的组织方式,src目录下分为api、views、components、router、store、utils、styles等模块。api目录统一管理请求接口,每个模块对应一个JS文件;views目录是页面级组件,按功能模块划分子目录;router集中配置路由和路由守卫;store用来管理全局共享状态,比如用户信息和权限列表。这样的分层设计,好处是新人接手时能快速定位功能的代码位置,上下文切换成本极低。
3. 数据库设计与权限模型
3.1 核心数据表设计
数据库设计直接决定了系统能够承载的业务边界,这套系统我一共设计了11张核心业务表,先看最关键的前5张表。为了便于理解,我用表格把重点字段列出来。
| 表名 | 字段示例 | 核心用途 | 关键说明 |
|---|---|---|---|
| user | id, username, password, real_name, role_id, college_id, phone, email | 存储所有系统用户 | 密码经过MD5加盐加密存储,不能明文 |
| role | id, role_name, role_code, description | 系统角色定义 | 支持超管、校管理员、学院管理员、指导老师、学生等 |
| project | id, project_name, category, leader_id, college_id, status, detail, budget, file_url | 项目申报主表 | status字段对应项目状态机 |
| project_member | id, project_id, user_id, is_leader, join_time | 项目成员表 | 一个项目对应多个成员 |
| approval_record | id, project_id, approver_id, action, comment, create_time | 审核记录表 | 记录每一步审核人、动作和意见 |
除此之外,还有几张辅助表:college表存学院信息,notice表存系统公告,score表存结题评分,file表统一管理上传附件,operation_log表记录用户关键操作日志。对于科创管理这种业务,日志表非常重要,一旦出现项目状态被误改或审核意见丢失的情况,日志表就是排查问题的唯一线索。
3.2 项目状态机与审核流程设计
我强烈建议所有做管理系统的开发者,把业务中的状态流转设计成一张独立的状态机表,而不是在代码里用魔法值散落着写。这个系统里项目的状态流转我做成了八个节点:草稿(0)、待指导老师审核(1)、待学院审核(2)、待学校审核(3)、已立项(4)、待中期检查(5)、待结题验收(6)、已结题(7),中间还有被驳回(-1)和已终止(-2)两个异常状态。这个状态定义写在哪呢?写在Redis缓存里可以,写在MySQL配置表里也可以,甚至直接在前端定义好枚举值都行,但一定要让全系统的状态流转逻辑有一处统一的地方可以查阅和修改。
状态流转的规则是:指导老师可以驳回状态1的项目,学院管理员可以审核状态2的项目,学校管理员处理状态3,已立项的项目才可以提交中期报告,中期检查通过才能提交结题材料。在Service层我封装了一个checkStateTransition(currentState, targetState)方法,每次状态变更前都校验一下是否合法,非法操作直接抛出业务异常。这个方法看起来不起眼,但在实际使用中帮我拦截了大量误操作。
3.3 权限模型设计细节
权限模型我采用RBAC规范,但做了轻量处理:用户表直接关联角色表,没有引入菜单权限表和按钮权限表,因为科创管理系统不需要太细粒度的操作权限控制。如果硬要加菜单权限,反而会让前端动态路由的实现复杂很多。
角色划分上,我用了固定五种角色:ADMIN(系统管理员)、SCHOOL(校级管理员)、COLLEGE(院级管理员)、TEACHER(指导老师)、STUDENT(学生)。每种角色对项目的操作权限在代码里通过注解控制,比如学生端接口统一加上@PreAuthorize("hasRole('STUDENT')"),保证学生只能看到自己创建的项目;指导老师接口校验当前用户是否为项目指导老师,防止越权操作。前端这边,路由守卫会根据本地存储的角色信息动态渲染菜单,未登录的用户跳转到登录页。两端的权限控制互为补充,前端控制的是显示内容,后端才是真正的安全防线。
4. 后端核心功能实现
4.1 统一响应结果与全局异常处理
后端接口设计的第一件事,是定义一个统一的返回格式。我采用的是比较标准的RESTful风格,所有接口返回Result对象,包含三个字段:code(状态码)、message(提示信息)、data(业务数据)。200表示成功,400表示参数校验失败,401表示未登录或token过期,403表示无权限,500表示服务器内部错误。前端拿到这个格式后统一处理,尤其是401和403,需要在响应拦截器里做全局跳转,否则每个页面都要单独写判断,代码冗余度会非常高。
全局异常处理是后端开发中极具性价比的一个模块。我用@RestControllerAdvice注解配合@ExceptionHandler写了一个全局异常处理器,捕获自定义的业务异常、参数绑定异常未知异常。这样一来,Controller层就不需要写大量的try-catch,写业务代码的时候直接抛出异常,统一由异常处理器处理,代码非常清爽。实际开发中,我遇到过印象最深的坑是文件上传时抛出的MaxUploadSizeExceededException,如果不做处理,前端接收到的错误信息是一大段英文堆栈,对用户极不友好,加上全局异常处理之后,就能转换成"文件大小不能超过20MB"这样通俗的提示。
4.2 JWT认证与登录流程
登录认证这块,我选了JWT(JSON Web Token)方案而不是传统的Session方案。前后端分离架构下,Session的模式需要处理跨域携带Cookie的问题,而JWT把用户信息编码在Token里,前端每次请求在请求头加Authorization: Bearer token,后端直接解析验证,无需在服务端保存会话状态,天然适合分布式部署。
登录流程的具体实现是:用户提交用户名和密码,后端用BCryptPasswordEncoder校验密码哈希,校验通过后生成JWT Token,Token的有效期设置为24小时,里面包含了用户的id、用户名和角色信息。后端写了一个JwtInterceptor拦截器,在请求到达Controller之前验证Token的有效性,验证通过后把用户信息放入ThreadLocal,后续的业务逻辑直接从ThreadLocal取当前用户即可。这里需要注意,JWT的密钥要写配置在application.yml里,不能硬编码在代码中,一旦密钥泄露,任何人都可以伪造Token。
一个朋友在复用这套系统时问了句:JWT过期之后怎么办?我给出的方案很简单,前端在拦截器里捕获到401响应后,跳转到登录页并清理本地存储的用户信息即可。单点登录和Token自动续期属于更高阶的需求,科创管理系统并发量不大,不需要引入复杂的方案,保持简单反而是最大优势。
4.3 项目申报与审核的完整实现
项目申报是整个系统最核心的交互场景。学生在前端填写项目名称、类型(创新训练项目、创业训练项目、创业实践项目)、所属学院、指导老师、项目成员、经费预算,上传申报书附件,点击提交后,后端进行字段校验和附件存储,然后生成一条状态为1的项目记录。
指导老师审核的场景比较有意思。审核列表页需要展示学生提交的项目详情,还要展示同一位指导老师名下的所有待审核项目。我用了MyBatis的动态SQL来写这个列表查询,通过<if>标签做条件拼接,当传入的参数不同时生成不同的查询语句。在ServiceImpl里,我把审核操作设计成了一系列不可分割的操作:更新项目状态、记录审核日志、可能发送站内通知。这三步必须放在同一个事务里,我用@Transactional注解搞定,任何一步失败都会整体回滚。
学校管理员审核通过之后,项目进入"已立项"状态,系统自动给项目负责人的账号发送一条站内消息提醒。到中期检查和结题验收阶段,流程逻辑类似。这套审核逻辑写完后,我发现代码复用度非常高,所有的审核操作都可以抽象成一个统一的处理入口,区别仅在于操作类型和前置状态校验不同。
4.4 文件上传与存储
科创项目申报中免不了要上传申报书、中期报告、结题报告、成果证明等附件。文件存储方案我并没有引入OSS之类的对象存储服务,而采用了本地磁盘存储加数据库记录的方式。服务器上建一个/data/upload目录,按日期划分子文件夹,上传成功后把文件路径保存到file表,下载时根据表里的文件路径去服务器读取。
这里有几个细节要处理到位。第一,上传的接口需要限制文件大小,我设置了20MB的上限,超过会抛出异常;第二,文件名要重命名,不能直接用用户上传的文件名,避免中文文件名和重复文件名带来的问题;第三,下载接口需要设置Content-Disposition响应头,确保浏览器能正确识别文件类型并触发下载行为。我在上传之前用UUID生成文件名,把原始文件名存在数据库里,下载时再从数据库取出来拼接到响应头中,这样既避免了文件名冲突,又能保证用户下载到文件名和上传时一致。如果以后文件量特别大,可以考虑迁移到MinIO或者阿里云OSS,核心业务代码不用改动,只需要替换文件存储的服务类实现。
4.5 SpringBoot核心配置解析
最后把后端配置文件里几个值得注意的点单拎出来说一下。数据源配置上,我用了Druid连接池,不仅性能可靠,还自带了监控页面,开发环境可以实时查看SQL执行情况和慢查询统计,定位性能问题非常方便。为了便于排查问题,我在配置里打开了MyBatis的SQL日志打印,这样每一条执行的SQL都会在控制台输出,配合id-idea-plugin这样的IDEA插件格式化输出,调试SQL时非常舒服。
再一个重要的配置是CORS跨域处理。虽然在正式环境用Nginx反向代理之后不存在跨域问题,但本地开发时前端项目跑在8080端口,后端跑在9090端口,两者端口不同必然产生跨域请求。我在后端配置里明确了allowedOrigins为http://localhost:8080,allowedMethods为GET、POST、PUT、DELETE、OPTIONS,这里有一点常被忽视:如果是生产环境线上前端域名变了,记得同步修改这个配置,否则前端用户会遇到所有请求都报错的情况。
5. 前端Vue核心功能实现
5.1 前端工程与开发环境搭建
前端部分是一个基于Vue 2 + Element UI + Vuex + Vue Router的单页应用。开发环境需要先安装Node.js,我建议安装14或16 LTS版本,因为Vue 2的依赖和Webpack 4在高版本Node上偶尔会报OpenSSL错误。具体的解决方案我遇到过一次,在打包时提示digital envelope routines::unsupported,这是因为Node 17以上的版本改用OpenSSL 3导致的,解决办法是在package.json的scripts里加一句set NODE_OPTIONS=--openssl-legacy-provider,不同系统的写法不一样。这一段代码很多人在网上搜过,当时我找到这个解决方案时,心情堪比救火成功。
项目创建用的是Vue CLI 4.x,创建命令是vue create frontend,手动选择Vue Router、Vuex、ESLint。入口文件main.js中引入Element UI、路由实例、状态管理实例。为了接口调用的统一,我把axios封装成了一个request工具模块,设置了baseURL、超时时间和请求拦截器(自动加上Token请求头)、响应拦截器(统一处理错误码),所有API模块都从这个工具中导出请求方法,这是保证前端代码整洁的关键一步。
5.2 路由与权限控制的配合
前端路由设计核心思路是"根据角色生成动态菜单"。我把路由分为两部分:一部分是静态路由,包含登录页、404页、首页等所有角色都能访问的页面;另一部分是动态路由,包含项目管理、审核管理、系统管理等功能页面,这些路由不直接注册,而是等用户登录后,根据后端返回的角色信息,动态添加路由并生成侧边栏菜单。
这一块落地的时候有点绕,建议第一次写前端路由守卫的同学们先理解一下router.beforeEach的执行时机。我的实现逻辑是:用户登录成功后,系统把用户信息存到Vuex和localStorage,每次路由跳转前,路由守卫判断用户是否有登录态。如果有登录态,再判断动态路由是否已经添加过(用Vuex里的一个标记字段控制)。如果是第一次进入系统,就根据用户的角色信息,调用一个generateRoutes方法,动态添加对应权限的路由表并调用router.addRoutes,至此完成整个权限路由的装配过程。整个过程看起来长,但是一气呵成,实际使用体验非常流畅。
5.3 核心页面组件拆解
项目申报页是学生端最常操作的页面。我把它设计成了一个大表单组件,用el-form配合rules做字段校验,项目成员编辑用了动态增减的表格行,用户点击"添加成员"按钮时新增一行输入框,填写完成员姓名和学号后,点击保存,前端把整个表单的数据组装成一个JSON对象提交给后端。这里有一个坑是Element UI的日期选择器默认返回格式是Date对象,直接提交给后端会报格式不对,需要在提交前用qs或者dayjs格式化一遍。我在前端工具模块里写了一个formatDate工具函数,统一处理时间格式。
项目管理页面是一个包含搜索、筛选、列表、分页的复合页面。顶部是搜索栏,支持项目名称、当前状态、学院三个维度的条件组合查询,底部是分页表格。表格每一行都有"详情"、"编辑"、"提交审核"、"删除"等操作按钮,根据当前项目的状态动态渲染。比如草稿状态的项目才允许编辑和删除,已提交审核的项目只允许查看。这些逻辑通过一个computed属性动态计算当前行的操作权限,代码可读性非常好。
审核页面面向指导老师和各级管理员,核心功能是待办列表加详情弹窗。点击一个待审核项目,右侧弹出项目的完整信息面板,底部是审核按钮区域。审核时需要填写审核意见,必填校验做好。我额外加了一个"历史审核记录"的时间线组件,展示该项目之前所有审核节点的时间和意见,审核人与被审核人沟通不畅的问题在这里得到了极大的缓解。
5.4 前端性能与打包优化
前端性能优化这块我做了三个动作。第一个是按需引入Element UI组件,通过babel-plugin-component插件,大幅减小打包体积,首屏加载速度改善明显。第二个是路由懒加载,所有页面组件都用import()动态导入,Vue Router会把这些组件自动拆分成单独的JS文件,只有访问对应路由时才加载对应资源。第三个是打包配置里设置了productionSourceMap: false,防止源代码暴露在浏览器的调试面板中,这既是安全要求,也减少了部署包体积。
构建出来的dist目录是纯静态文件,放在Nginx的/usr/share/nginx/html目录下即可。Nginx还需要配置几个关键点:单页应用需要配置try_files指令,让所有前端路由都回退到index.html,避免刷新页面时出现404;/api开头的请求反向代理到后端SpringBoot服务端口。这两段配置我写在了Nginx的server块中,部署部分会给出完整示例。
6. 部署上线全流程
6.1 本地运行环境准备
本地把项目跑起来,环境准备是第一关。需要安装JDK 8、Maven 3.6+、MySQL 8.0、Node.js 14+。MySQL安装后,要手动创建数据库并导入项目的sql/init.sql文件,里面包含了建表语句和初始数据。初始数据里有一个超级管理员账号admin/admin123和几个测试账号,登录不同的账号可以体验不同的角色权限。
后端项目修改application.yml里的数据库连接配置,把用户名密码改成自己本地的,然后在项目根目录执行mvn spring-boot:run,看到SpringBoot启动成功的日志就说明后端OK了。前端项目进入目录执行npm install安装依赖,再执行npm run serve启动开发服务器。默认情况下前端跑在8080端口,后端跑在9090端口,本地开发环境通过vue.config.js里的代理配置解决跨域问题。前后端都启动后,浏览器打开http://localhost:8080即可登录系统。
这里提醒一下,npm install首次执行通常会比较慢,如果网络不佳,可以配置淘宝镜像源,速度会快很多。执行命令是npm config set registry https://registry.npmmirror.com,装完依赖之后记得改回来,避免后续发布的包版本和官方源不一致。
6.2 阿里云服务器部署详细步骤
线上部署我选了一台阿里云轻量应用服务器,配置是2核4G,安装的是CentOS 7.9系统。如果是新买的服务器,先要把安全组的入方向规则放开80端口和443端口。系统装好后,依次安装JDK、MySQL、Nginx。这几个服务用systemctl命令管理,全部设置成开机自启,避免服务器重启后服务不自动恢复的情况。
部署的核心流程分四步:第一步,本地把后端项目用Maven打成JAR包,生成的文件在target目录下,通过scp或者宝塔面板上传到服务器的/app/springboot目录;第二步,前端项目本地执行npm run build生成dist目录,也上传到服务器,放在Nginx的网站根目录下;第三步,配置Nginx,核心配置如下:
server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; # 单页应用路由回退 location / { try_files $uri $uri/ /index.html; } # 后端接口反向代理 location /api/ { proxy_pass http://127.0.0.1:9090; 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 /files/ { alias /data/upload/; } }第四步,启动后端JAR包。后端服务我用了nohup java -jar xxx.jar > app.log 2>&1 &的方式启动,方便把日志输出到文件里排查问题。也可以把Java服务注册成系统服务,用systemd管理,支持开机自启和崩溃重启,生产环境更推荐后者。
安全加固方面有两个细节必须做。第一个是MySQL一定要设置强密码,千万不要用默认密码,部署完服务器之后可以执行mysql_secure_installation脚本一键完成基础安全设置。第二个是后端服务不要用root用户直接跑,单独创建一个普通用户运行Java进程,即便Java应用被攻破,攻击者拿到的权限也非常有限。
6.3 HTTPS证书配置
现在的网站如果还是纯HTTP,浏览器会直接提示"不安全",对于学校类的信息管理系统,安全形象很重要,而且很多功能(比如密码修改、文件上传)都需要加密传输。我在阿里云上申请了免费的SSL证书,下载Nginx版证书后,把证书文件传到服务器,修改Nginx配置增加443端口的监听,核心配置如下:
server { listen 443 ssl; server_name your-domain.com; ssl_certificate /etc/nginx/cert/your-domain.pem; ssl_certificate_key /etc/nginx/cert/your-domain.key; ssl_protocols TLSv1.2 TLSv1.3; # 同80端口的核心配置 # ... }配置完成后记得把80端口的HTTP请求重定向到HTTPS,这样用户不管输入哪个地址都会被引导到安全的访问地址。执行nginx -t检查配置语法,确认无误后nginx -s reload让配置生效。如果配置过程中遇到证书加载失败,通常是证书文件权限问题,执行chmod 400权限设置即可。
7. 常见问题与排查技巧
7.1 高频问题速查表
我把开发过程中遇到过、以及读者反馈过的高频问题整理成一个速查表,遇到问题先对照排查,大概率能解决。
| 问题现象 | 根本原因 | 解决方案 |
|---|---|---|
| 前端请求后端报CORS错误 | 后端未允许前端域名跨域 | 检查CORS配置中的allowedOrigins是否包含前端地址 |
| 登录成功但访问接口返回401 | JWT Token未传递或已过期 | 检查请求拦截器是否在请求头加入了Authorization字段 |
| 数据库中文乱码 | 连接URL未指定字符集 | 在JDBC连接地址中增加characterEncoding=utf8&useSSL=false |
| 上传文件报超出大小限制 | 默认限制了上传大小 | SpringBoot配置中设置spring.servlet.multipart.max-file-size=20MB |
| 打包时提示Node版本不支持 | Node版本过高与Webpack冲突 | 降级Node 14,或设置NODE_OPTIONS=--openssl-legacy-provider |
| 刷新页面出现404 | 未配置单页应用路由回退 | Nginx配置try_files $uri $uri/ /index.html |
| MyBatis控制台不打印SQL | 日志级别设置未生效 | 配置mybatis.configuration.log-impl或设置mapper包日志级别为DEBUG |
| 日期参数绑定失败 | 前端传了字符串,后端没加格式化注解 | 后端字段加@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss"),前端统一格式化 |
7.2 我踩过的三个最深坑
第一个坑和MyBatis动态SQL有关。当时写项目列表的分页查询,用了<if>标签拼接查询条件,当项目名称条件传入null或者空字符串时,拼接出来的SQL会变成WHERE project_name =这种语法错误。排查了很久,最后发现是<if>标签的判断条件写法不够严谨,没有排除空字符串的情况。正确写法是<if test="projectName != null and projectName != ''">。这个细节,所有用过MyBatis动态SQL的人应该都体会过,看似简单,实际报错的时候让人抓狂。
第二个坑是跨库联表查询的性能问题。系统初期把学生信息和项目信息放一个库里,数据量小没感觉,后来导入了一批历史数据,查询项目的列表接口响应突然从300毫秒涨到了3秒。用Druid监控一查,问题出在联表查询没有走索引,加上项目表的学生外键、学院字段缺少索引,全表扫描导致慢查询。解决方法是在外键字段、状态字段、类型字段上一一加上组合索引,并把列表页默认只加载最近一年的数据。从那以后,所有上线项目我都会提前检查索引设计,而不是等慢查询了再去救火。
第三个坑和前端部署有关。第一次打包部署后,发现用户刷新页面就出404,当时第一反应是Nginx配置错了,检查了好几遍也没发现问题。后来搜了半天才恍然大悟,是单页应用的路由机制导致的:前端访问/project/detail时,后端Nginx找不到这个路径对应的物理文件,自然返回404。解决方案就是刚才部署部分提到的try_files指令。这种问题不实际部署一次,光看教程很难理解,很多同学本地开发的时候天天点路由,从来没有发现过这类问题,直到部署上线才突然遇到,所以建议所有人在开发阶段就要用npm run build加本地Nginx模拟真实的部署环境。
7.3 二次开发与业务扩展建议
这套系统目前的业务边界已经覆盖了科创项目管理的核心流程,但它仍然有非常大的扩展空间。顺着实际业务往下想,首当其冲的是增加校级优秀项目评审模块。目前结题验收只能提交材料和评分,但没有实现专家在线打分、答辩现场评分、优秀项目自动推送展示这些场景。如果加上这一块,系统的价值会大幅提升。
数据统计可视化的维度也值得深化。目前的统计报表只是简单的柱状图,如果能把历年项目的立项数量、各学院占比、项目成果转化情况做成多维度的可视化看板,对学校管理层的决策会非常有帮助。技术实现上可以用ECharts,数据源直接复用现有接口,工作量不大。
通知模块目前用的是站内消息,实际使用中师生更习惯微信和邮箱。可以考虑接入邮件服务和企业微信的Webhook,在关键节点(审核通过、驳回、中期提醒)自动推送通知,这样师生就不用总是登录系统查看状态了。技术难度不高,价值提升却非常明显,这也是我个人认为值得优先做的扩展功能。
8. 总结与实际使用体验
整个项目的开发周期我用了大概三周,从数据库设计、接口开发到前端页面完成,再到部署上线,每一步都有各自的难点。回过头来盘点,这个项目最大的收获并不是某个框架的使用技巧,而是对"全栈"这个词的分量有了真正清晰的认知。后端设计得再好,前端对接不顺畅,系统就是难用的;前端做得再漂亮,后端接口响应缓慢,用户照样吐槽。
让我比较安心的是这套系统的稳定性。上线运行至今,没有出现过一次因并发问题导致的服务中断,数据库CPU正常情况下占用率不超过10%。每天的访问高峰出现在项目申报截止前三天,负载也完全扛得住。这说明对于学校这类事务管理系统,2核4G的服务器配置已经绰绰有余,真正需要关注的是数据备份和账号安全。
在带新手使用这套系统的过程中,我发现一个规律:能顺利跑通本地环境的人,往往能顺利学会整个项目;而连环境都搭建不起来的人,通常卡在了JDK环境变量配置或者Maven依赖下载上。所以如果是初学者,建议先把环境搭建作为第一个目标,然后把代码读一遍,再动手改一两个小功能,最后才算真正掌握了这套系统。这个过程走下来,SpringBoot和Vue的实战能力会有一个质的变化。
最后分享一个我在实际使用中养成的小习惯:每次上线新功能之前,都会先在测试环境完整走一遍核心流程,包括学生申报、老师审核、管理员通过、财务填报这几个环节,全部跑通之后再上生产环境。这个习惯帮我挡掉了至少五次低级错误,推荐给所有开发者。如果你在这套系统的基础上做了二次开发,遇到了有意思的问题或者做出了好用的新功能,欢迎交流,我随时愿意和同行探讨。