1. 项目背景与核心需求梳理毕业设计选了一个“基于Spring Boot的高校科研管理系统”说实话一开始觉得就是个CRUD增删改查真做进去才发现科研管理业务远比想象中要琐碎而且每一块都牵扯到角色权限、审批流程和数据统计。这里把我从选题到完成设计的完整思路写出来希望能给正在做同类系统的同学一些参考。1.1 科研管理业务的真实痛点高校科研管理最常见的场景有这么几类教师申报科研项目学院先初审然后科研处终审项目立项后需要录入经费、中期检查、结题验收发表的论文、申请的专利、获得的奖项要随时登记年底还要给学院排名、统计成果数量。如果这些全靠Excel和纸质材料科研处老师基本天天在收表格、催材料、汇总数据。做系统之前我专门找某高校科研处的一位老师聊过她提到最头疼的是三个问题一是信息散老师交上来的申报书格式五花八门汇总时经常漏项二是流程慢一个项目审批要跑好几个办公室纸质签字就得一星期三是统计难年底要各种报表手动数论文数量、算经费到账率熬夜是常态。所以这套系统的核心目标很明确把申报、审批、立项、经费、成果、统计全部线上化让数据集中、流程固定、报表自动出。1.2 系统角色与功能边界科研管理系统不能只做一个普通的管理后台它至少包含四类角色普通教师科研人员填写项目申报书、登记论文和专利、查看审批进度、查询自己的经费余额。学院科研秘书二级管理员初审本院教师的申报书管理本院教师信息统计本院科研成果。科研处管理员系统核心角色终审项目、发布项目申报通知、分配经费编号、管理专家库、导出全校数据报表。系统超级管理员维护用户账号、角色权限、学院院系信息负责系统基础配置。功能模块划分上我按业务流拆成了六个子模块项目管理、成果管理、经费管理、专家评审、数据统计、系统管理。项目管理是最重的包含申报、审批、立项、中期、结题五个环节成果管理要覆盖论文、专利、软著、获奖四类经费管理要记录预算、到账、支出并且能按项目维度汇总专家评审则是从专家库随机抽取专家对重点项目进行匿名评审数据统计是给科研处用的按学院、年份、成果类型多维度汇总图表系统管理负责用户、角色、菜单、字典等配置。1.3 技术选型与理由技术选型我参考了近两年企业级Java后端的主流组合最终确定后端用Spring Boot 2.7、MyBatis-Plus、Spring Security JWT数据库用MySQL 8.0缓存用Redis前端用Vue 3 Element Plus ECharts。这套组合在实验室电脑上跑起来很顺畅而且毕业设计要演示的话前后端分离结构也比较容易说清楚。为什么选Spring Boot而不是SSH或者Servlet原生最直接的原因是生态成熟、配置少。Spring Boot内置了Tomcat干掉了一堆XML配置写个Controller就能跑。其次Spring Security和JWT的整合资料非常多权限控制部分不用从零造轮子踩坑时一搜就有答案。MyBatis-Plus主要图它的代码生成器和条件构造器单表CRUD基本不写SQL开发效率能提升一大截我后面把常用查询都封装成PageHelper式的分页查询前端传current和size就能出分页结果。前端选Vue 3是因为Composition API对复杂表单和状态管理更友好配合Pinia存用户信息和菜单权限Element Plus组件多表格、表单、弹窗、树形控件都能直接拿来用。ECharts拿来画各类统计图表比如学院成果分布图、项目经费趋势图展示效果很不错。这里插一句个人体会技术栈宁可用自己熟的也不要盲目追新。毕业设计考察的是你能否独立完成一个完整项目而不是用了多么高端的技术。把Spring Boot那一套吃透远远比堆一堆微服务中间件更实际。2. 系统架构与数据库设计系统架构我是按经典的三层模式走的Controller只接收参数和返回结果Service写业务逻辑Mapper负责SQL交互实体类用MyBatis-Plus注解映射数据库表。另外单独加了一层DTO/VO避免把数据库实体直接暴露给前端。这样分层的好处是后期改需求时不会牵一发动全身。2.1 分层架构设计我画过一张自己理解的分层图大致是前端展示层Vue页面组件 → API请求封装 → 路由拦截 → 状态管理后端控制层RESTful Controller统一返回Result对象业务层Service接口 实现类事务边界放在这里数据访问层Mapper接口MyBatis-Plus提供基础CRUD复杂查询用XML写SQL基础设施层MySQL、Redis、文件存储本地磁盘存储 静态资源映射前后端通过HTTP接口通信统一返回格式是{ code: 200, message: success, data: ... }错误码有401未登录、403无权限、500系统异常。所有接口都加了JWT鉴权前端把Token存在localStorage里请求拦截器每次带上Authorization头后端写了一个拦截器放行登录接口其余接口验签和解析用户信息。关于事务重点注意申报审批这类写操作多的流程一个步骤涉及多张表的更新必须在Service方法上加Transactional否则中途抛异常会导致审批状态和项目状态不一致。2.2 数据库表设计与ER关系数据库表我前前后后改了三版第一版想省事把所有成果类型都塞进一张表后来发现不同成果字段差异太大只好拆开。最终保留了12张核心表表名用途关键字段sys_user用户表username, password, real_name, dept_id, role_idsys_role角色表role_name, role_codesys_dept院系表dept_name, parent_idresearch_project项目申报表project_code, name, type, applicant_id, dept_id, status, budgetproject_approval审批记录表project_id, approver_id, approve_status, commentresearch_expense经费表project_id, amount, expense_type, create_timepaper_result论文成果表title, author, journal, doi, project_idpatent_result专利成果表patent_name, patent_type, status, apply_datesoftware_result软著成果表software_name, register_no, create_dateaward_result获奖成果表award_name, level, rank, yearexpert_info专家库表expert_name, field, title, departmentsys_dict字典表dict_type, dict_label, dict_value这12张表之间的关系说起来也不复杂用户属于某个院系可以申报多个项目一个项目对应一条审批链项目经费记录挂在项目下面各类成果都通过project_id和项目关联有些论文如果没有项目支撑也可以不关联所以这个字段允许为空。专家库和项目之间是评审关系单独建了一张评审任务表记录哪个专家评审哪个项目评分是多少。初次设计时很多人会把用户和项目创作者设计成多对多其实在这个场景里一个项目只能有一个申报人不需要复杂关联直接外键引用就行。审批人倒是可能多个所以才有了审批记录表每个节点写一条记录。2.3 关键表字段说明拿research_project举例我设置的字段主要是project_code项目编号前端保存时自动生成格式是XM 年份 4位序列号比如XM20250001这样线下对账时很方便。project_type项目类型包括校级项目、纵向项目、横向项目、教改项目用字典管理。status状态机0草稿、1待学院审核、2待科研处审核、3已立项、4中期检查中、5已结题、6已终止、7已驳回。这个字段是整个系统的核心流转依据。budget申报金额后面经费模块会跟它做差额对比。start_date和end_date项目起止时间用来做结题提醒的定时任务。所有表都统一加了create_time、update_time、deleted字段其中deleted是逻辑删除标志MyBatis-Plus配置全局逻辑删除后查询会自动过滤掉已删除数据。这样做比物理删除安全得多误删数据也能轻松找回。3. 后端核心模块实现解析这一部分讲具体的代码实现思路。我只挑自己觉得最有价值的五个点展开权限认证、项目审批流程、经费管理、文件上传、统计报表。这些模块组合起来基本覆盖了一套正经管理后台最常见的中台逻辑。3.1 权限认证与角色控制用户登录后后端用JWT生成一个Token里面存放userId、username、角色编码过期时间设置为24小时。关键点是每次请求进入Controller之前拦截器要从Token里解析出用户信息放入ThreadLocal这样Service层任意位置都能拿到当前登录人id。角色控制我用的是Spring Security的PreAuthorize注解在Controller方法上加PreAuthorize(hasRole(ADMIN))就可以限定只有管理员能访问。这里有个细节Spring Security默认的角色前缀是ROLE_所以数据库里角色编码存的是ROLE_ADMIN前端返回时再去掉前缀展示成“管理员”。实际使用中光靠注解不够因为很多接口需要按数据范围过滤。比如学院的科研秘书只能看到本院的数据我不能只在注解上写死角色还得在Service查询时根据当前用户所属dept_id加一层WHERE条件。我给BaseMapper封装了一个自定义方法先判断当前用户角色如果是管理员则查询全量数据如果是院系秘书则自动加上dept_id条件如果是普通教师则加上applicant_id条件。这样能避免每个Mapper都重复写权限判断。3.2 科研项目申报审批流程审批流程是最容易出bug的地方。我采用的是“状态机 审批记录表”的方案没有引入Flowable这样的工作流引擎原因是这个场景的流程是固定的只有学院初审和科研处终审两步用工作流引擎会增加学习和部署成本毕业设计里讲起来也费劲。具体实现逻辑是这样教师提交申报书时状态从0草稿变成1待学院审核同时插入一条审批记录审批人指向该教师所属学院的科研秘书。科研秘书看到待办列表选择通过或驳回。通过后状态变成2待科研处审核驳回则状态变成7已驳回教师可以修改重新提交。科研处终审同样两个操作通过后状态变成3已立项同时自动生成项目编号和经费总账户。每一步操作都要校验当前状态是否合法比如状态为2时学院秘书不能再审批。我在Service里第一步就是查项目状态和审批人然后跟当前用户匹配不匹配直接抛501业务异常。事务方面审批通过时除了更新项目表还要生成审批记录和经费初始记录这三个操作必须在一个事务里。为了计算待办数量每次查询待办列表时条件是status1且当前的科研秘书部门等于项目所属部门。如果项目被驳回还要把驳回意见存到审批记录的comment字段前端在项目详情页展示时间线用不同颜色区分通过和驳回节点。3.3 成果登记与经费管理成果登记这块论文、专利、软著、获奖四种类型我分别建了Controller和Service因为它们的表单字段完全不同。但写多了以后发现借用了模板方法模式把公共逻辑提取到一个抽象类中保存前自动带出当前用户和所属部门插入数据前判断项目是否存在如果是结题项目绑定的成果必须确认项目状态为已立项或结题否则拒绝录入。经费管理要重点关注到账和支出两条线。立项成功时系统根据budget字段生成一条到账类型的经费记录同时更新项目的“可用余额”。教师录入支出时系统先检查当前余额是否够用不够就报错“经费不足”。这个逻辑虽然简单但期末对账时特别好用避免经费超支。我还做了按月份汇总经费趋势的接口前端用ECharts画折线图科研处老师可以直观看到每个月经费到账和支出的走势汇报用很加分。3.4 文件上传与统计接口申报项目时老师要上传申报书PDF、结题报告等附件文件上传不能直接存数据库我用的是本地磁盘存储方案上传时把文件写到服务器指定目录文件元数据存数据库表file_info包括原始文件名、存储路径、上传人、上传时间。下载时通过文件ID查库再根据存储路径读取文件流输出给浏览器。这里有个隐患是文件名乱码和路径穿越我对存储文件名做了UUID重命名保存成UUID_原始文件名这样既避免重名冲突也不会暴露真实路径。另外限制文件大小不超过20MB类型只允许PDF和压缩包后端用Apache Tika检测文件真实类型不只是看后缀名。统计接口我写了一个StatisticsController提供三类聚合数据按学院统计立项数量、按年份统计论文发表数量、按项目类型统计经费占比。SQL用MyBatis-Plus的groupBy加count配合Java 8 Stream做二次聚合。由于统计涉及多表关联分开写四个Mapper查询接口在Service层组装返回前端渲染更简单。4. 前端页面设计与联调测试前端这块我用了比较省力的方案Vue 3 Vite Element Plus Pinia Vue Router。页面结构按照后台管理系统的经典布局左侧菜单栏、顶部用户信息栏、中间主内容区。为了能让答辩演示时看起来完整我不光写了管理后台还给教师端做了一套相对简洁的申报页面申报流程可以全程在线操作。4.1 页面结构、路由权限与接口对接前端按模块拆页面路由定义时做了动态路由登录后根据用户角色从后端获取该角色能访问的菜单列表动态添加到Vue Router中。比如教师角色只有“项目管理、成果登记、经费查询”菜单科研处管理员才有“审批管理、数据统计、专家库管理”。这样前端路由拦截只需判断是否有该路径的meta信息不用在每个页面手动写v-if。接口对接时我统一封装了api/request.js先定义axios实例设置baseURL和超时时间。请求拦截器读Token响应拦截器判断code如果401就跳转登录页面并清空本地缓存。每个模块的接口都按文件整理比如project.js里只放项目相关APIapproval.js里放审批相关API这样维护起来很清晰。页面表格基本都用Element Plus的el-table配合分页组件el-pagination后端返回的数据结构统一是records和total前端直接复制就能用。审批页面用el-steps展示审批进度条很直观。申报表单比较长我拆成了“基本信息”“经费预算”“附件信息”三个步骤用el-form的校验规则控制必填项。联调过程中最常见的坑是字段名对不上。比如后端Java属性createTimeJSON转换默认是createTime前端如果写成了create_time就取不到值。我后来在全局配置了Jackson驼峰命名策略但更管用的做法是接口文档先在文档平台上把字段名定死前后端照着一写根本不冲突。4.2 核心流程测试用例与问题排查系统写完以后我列了一张功能测试清单按角色和流程分级测试教师提交项目申报书后状态是否正确变为“待学院审核”学院秘书是否只能看到本院教师的申请科研处驳回后教师能否修改并重新提交经费支出时余额不足能否正确拦截导出Excel报表数据是否与数据库一致并发场景下同一项目被两个人同时审批会怎样前面几项还顺利最后并发审批的问题确实踩了个坑。两个管理员同时点“通过”后端由于没有加锁会出现两个线程同时读取到状态为“待审核”然后都更新为“已通过”数据库里审批记录也插了两条。虽然概率不高但一旦发生就很尴尬。解决办法是在审批方法上加Transactional并且在更新项目状态时使用乐观锁机制SQL语句带上WHERE status 原状态如果更新行数为0说明状态已被其他人改过直接抛出“当前项目已被处理请刷新列表”的异常。我用MyBatis-Plus的Version注解实现了这个乐观锁实测下来很稳。另一个问题是权限缓存不一致。用户角色变了以后前端动态路由没有重新生成导致管理员变成普通教师后依然能看到管理菜单。后来我改成每次刷新页面都拉一次当前用户的菜单接口不再把菜单存在本地持久化虽然多了一次请求但权限永远是最新的。4.3 部署上线与答辩演示准备毕业设计演示最怕环境出问题所以部署我测试了两种方式一种是本地直接用Idea跑后端Node启动前端适合开发调试另一种是打包部署到服务器后端用mvn package打成Jar包前端用npm run build生成dist目录然后放到Nginx里做静态资源服务同时配置反向代理转发/api请求到后端的8080端口。服务器是某云服务商的轻量应用服务器2核4G跑这个系统完全没问题。MySQL和Redis都用Docker容器部署好处是答辩换电脑时只需要把容器镜像导过去环境就恢复了。数据库初始化脚本我放在项目的sql目录里附带测试数据不然上台演示时如果是一片空白效果会大打折扣。答辩演示环节我总结了一个自己的套路先演示流程闭环再演示数据统计最后讲一两个技术亮点。流程闭环是教师申报到科研处审核通过再到录入一笔经费整个过程不要超过两分钟动作太慢会让人犯困。技术亮点可以讲JWT权限控制、状态机审批、乐观锁并发控制这几个点都容易引起提问。千万不要一上来就讲数据库需求分析那个太枯燥而且容易被老师连续追问细节。4.4 性能优化与代码规范对于一些查询接口我发现列表页在数据量到两万条以后变慢排查发现是关联查询没用索引。我给research_project表的applicant_id和status加了联合索引给paper_result表的create_time加了索引查询速度从原来的800毫秒降到100毫秒以内。另外列表页的详情弹窗不用每次请求直接复用表格行数据减少一次后端交互。代码规范方面我统一了返回码枚举业务异常和系统异常分开处理。全局异常处理器用RestControllerAdvice把校验异常、业务异常、未知异常分类格式化返回。这样前端拿到message可以直接提示用户而不是看到一长串堆栈信息。5. 常见问题与避坑指南开发这个系统历时两个月前前后后遇到了不少问题。我把最典型的问题整理成一张速查表方便后面做同类系统的朋友快速定位。问题现象原因分析解决方案登录后接口一直返回401JWT Token中角色字段缺失或过期登录时写入完整用户信息和过期时间验证时检查异常类型前端页面白屏或路由不生效动态路由添加时机不对在路由守卫中先请求菜单再调用router.addRoute完成后放行附件下载后文件名乱码浏览器对UTF-8解码问题设置响应头Content-Disposition时对文件名做URL编码查询列表数据重复表连接时没有去重使用distinct或确认连接字段是一对一关系日期字段传到前端少一天Jackson默认将Date转为UTC时间在配置类中设置时间格式和时区为GMT8并发审批出现重复数据缺少并发控制机制使用MyBatis-Plus乐观锁插件更新条件带原状态其中日期字段少一天这个问题特别坑我刚联调时发现前端显示的时间比数据库早8小时后来查出来是Jackson默认把java.util.Date转成了UTC格式只要在全局配置里指定时区Asia/Shanghai并且把格式统一成yyyy-MM-dd HH:mm:ss就好了。这里还想提醒一点不要把业务逻辑写在Controller里。有些新手图省事直接在Controller里写mapper.selectById再set属性这样写演示时没问题但后期维护会很痛苦。把所有操作封装到ServiceController只做参数接收和结果返回这是代码规范问题也是答辩时老师大概率会问的问题。我再单独分享一下常见的“逻辑删除与唯一索引冲突”问题。我给论文表设置了一个唯一索引确保同标题论文不能重复登记。但用了逻辑删除以后删除一条数据再登记同标题的新论文会由于旧数据只是打标记且唯一索引仍然存在导致新增失败。我当时通过给字段加一个deleted联合唯一索引来解决或者删除时把标题加上一个时间戳后缀也建议研发时尽量用业务唯一编号而非文本内容做唯一约束。6. 写在最后的一点经验这套系统从需求梳理、数据库设计、后端开发、前端联调到打包部署基本上走完了一个完整项目的全部生命周期。说实话刚拿到题目时我也觉得“科研管理系统”听起来很普通但真正做进去才知道任何一个管理层系统都绕不开“角色权限”“状态流转”“数据统计”这三座大山把这三块啃透了后面再去做什么订单系统、库存系统、办公OA思路其实是通用的。我个人在实际操作中最大的体会是不要一开始就急着写代码先用一周时间把表设计好、把状态机理清楚后面开发会快很多。我第一版就是因为没想清楚审批记录表的结构导致后来返工改了好几处逻辑。如果现在让我重新做一遍我会先写一份简单的接口文档把所有前后端字段名对好再动手写代码。还有一个建议就是一定要留足够时间写论文。毕业设计不只是做系统论文才是最后呈现在老师面前的核心成果。我差不多用两周做系统最后三周连续写论文、画图、整理测试数据。论文结构上重点把需求分析、系统设计、数据库设计、系统实现和测试这几个章节写扎实每个功能模块都要有截图和关键代码片段支撑查重时注意把核心代码用自己的话重构一遍避免雷同。答辩时被问得最多的是“为什么这样设计”所以每个技术选型都要能自圆其说。最后再分享一个小技巧演示时准备一份测试数据包括一位教师账号、一个学院秘书账号、一个科研处管理员账号并预置几个已经完成申报和审批的项目、几篇论文和专利、若干条经费记录。这样上台演示的时候不需要现场注册账号、填写表单只需要把核心操作流走一遍既流畅又能把时间控制在有效范围内给老师留下准备充分的印象。这里就和准备继续做这个系统的同学说一句别怕踩坑每个坑都是论文里“系统测试与问题解决”章节的素材。真正把一个原本看着不起眼的管理系统做到能上线、能演示、能响应各种边界情况这本身就是一个完整的软件工程训练。