ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Java的大学生创新成果信息管理系统设计与实现

基于Java的大学生创新成果信息管理系统设计与实现 做毕业设计选题目最怕的就是“大而空”或者“旧而泛”。如果你拿到了“基于Java的大学生创新成果信息管理系统”这个题或者正在这个方向里选型我想先给你吃颗定心丸这是一个非常典型的、能拿高分、也能锻炼完整Java后端能力的题目。创新成果申报、审核、展示这一条线把权限管理、文件上传、流程状态流转、报表统计这些企业级开发里的高频场景全部串起来了。这篇文章我不打算给你灌什么“系统背景意义”的套话我会直接按照我自己做同类项目时的方法把这套系统的设计思路、数据库建模、核心代码写法、部署上线、答辩避坑全部拆开讲清楚。你照着这个思路走哪怕不抄我一行代码也能把自己的系统做得明明白白。项目本身不大但五脏俱全尤其适合作为Java Web方向的毕业设计选题。1. 项目整体设计方案拆解1.1 这个系统到底在解决什么问题很多同学拿到题目就开始画界面这是个误区。你首先要搞清楚高校里“创新成果”这四个字指的是什么。它不是普通课程作业而是学生的专利申请、软件著作权、学术论文、学科竞赛获奖、科研项目结题报告等一堆带着“证书”“文件”“证明材料”的正式成果。这些东西的管理在大多数高校里还停留在Excel汇总、QQ群传文件的阶段。所以这套系统的核心价值就一句话把成果申报从“线下填表人工催办汇总统计”变成“线上提交流程审批自动统计”。从这个需求出发系统必须解决四个核心问题学生能方便地填报成果信息上传佐证材料图片、PDF等指导老师或学院管理员能审核申报内容是否真实、格式是否合规成果数据能按照学院、专业、成果类型、年份做多维度的统计和展示审核过程和结论要有记录不能是“我说通过了就通过了”搞清楚了这四点你的功能模块设计就有据可依答辩时老师问你“你这个系统的创新点在哪里”你也可以把“将成果申报流程线上化、审核流程规范化、数据统计自动化”这套话讲得理直气壮。1.2 功能模块拆解三类角色三条主流程我建议你把系统用户划分为三个角色学生、审核教师含学院管理员、系统管理员。其中审核教师和管理员可以复用同一套后端逻辑只做权限区分。学生端功能相对简单直接成果申报填写成果名称、类型论文/专利/竞赛/软著/项目、成果描述、取得时间、本人排名/角色、上传佐证材料附件我的成果查看自己提交过的所有成果列表以及每条成果的审核状态成果修改与撤回在审核通过之前允许学生修改或撤回申报审核通过后只能申请变更不能直接改个人信息管理修改密码、查看个人基本资料。审核端教师/管理员功能是系统的核心待审核列表按提交时间排序展示所有待审核的申报支持按成果类型和学院筛选成果详情与在线预览查看申报详情能在网页里直接预览图片格式的附件或下载PDF文件审核操作审核通过、审核驳回。驳回时必须填写驳回原因系统将原因反馈给学生支持批量审核但同一批次只能给同一种结论数据统计看板按学院、专业、成果类型统计总数和通过数用图表展示主要用于学院层面的成果数据汇报。系统管理端则负责基础数据和全局配置用户管理学生账号的批量导入Excel导入教师账号的开通与角色分配成果类型管理动态维护“成果类型”字典表比如新增“外观设计专利”这种新类型不需要改代码学院与专业管理维护学校的组织结构树公告发布发布申报通知、获奖喜报等。三条主流程分别是“申报-审核-归档”、“账号导入-权限分配-使用”、“数据统计-导出报表”。你系统的所有代码其实都是在为这三条流程服务。1.3 技术选型不追新但要稳技术栈的选择我一般会给这样的建议不要选太冷门的东西也不要用十年前的老技术选“当前企业里真正在用的主流组合”最稳妥。我用的是前后端分离方案。后端Spring Boot 2.7.x MyBatis-Plus MySQL 8.0 Redis (可选) Spring Security (或Sa-Token) 前端Vue 3 Element Plus Axios ECharts这套组合是当前Java后端开发的事实标准。Spring Boot负责把项目跑起来MyBatis-Plus让数据库操作变得极其省事——单表CRUD基本不用写XML分页查询一个Page对象搞定。Redis用来存储登录令牌和部分热点数据能加上就加加分项。Spring Security做登录认证和接口权限控制虽然学习曲线有点陡但这是答辩时的高频考点。如果你对Spring Security实在不熟也可以用Sa-Token这个国产轻量级鉴权框架API设计对新手友好很多。我自己带过的学生里用Sa-Token完成后端权限控制的同学普遍比硬磕Spring Security的同学项目更完整。但如果你求职方向是Java后端Spring Security这关早晚得过不如趁毕设把它啃下来。前端为什么选Vue而不是纯JSP/Thymeleaf因为前后端分离后你部署的时候可以前端打包成静态文件丢给Nginx后端只跑接口分工清楚调试起来也直观。而且Vue Element Plus做后台管理界面速度飞快表格、表单、弹窗、分页这些组件都是现成的。2. 数据库设计与核心表结构2.1 数据库设计思路一切围绕“成果”这张主表这套系统的数据模型其实不复杂核心是成果表(result_info)所有其他表要么是它的从表审核记录、附件表要么是它的支撑表用户、角色、成果类型。设计表结构的时候我特别强调要遵循一个原则——不要为了“可能用到”而设计字段每个字段都要能在某个界面上找到对应的输入框或展示位置。用户表和角色表就是最常规的RBAC模型设计用户主表、角色表、用户角色关联表。我见过很多同学把“角色”直接做成用户表的一个字段像is_admin这种系统如果只给管理员和学生两类人用确实没问题但一旦要加“指导教师”“学院教务”这类角色代码就要改数据库非常被动。所以我还是建议规范一点上经典的三张表。成果类型表需要注意别写死在前端下拉框里。要设计成字典表管理员可以在后台维护毕设答辩的时候这一句话就能让你多拿两分——“我做了数据字典设计所有可扩展的枚举状态都放到了数据库里”。2.2 成果主表和审核记录表建表SQL这里我直接给你一份核心的SQL是我实际用过的简化版本字段命名清晰也符合答辩时老师的阅读习惯。-- 成果信息主表 CREATE TABLE result_info ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键ID, student_id BIGINT NOT NULL COMMENT 申报学生ID关联sys_user表, result_name VARCHAR(200) NOT NULL COMMENT 成果名称, result_type VARCHAR(50) NOT NULL COMMENT 成果类型论文/PATENT/软著/竞赛/项目, achievement_date DATE DEFAULT NULL COMMENT 成果取得日期, first_author VARCHAR(50) DEFAULT NULL COMMENT 第一作者/主持人姓名, author_rank VARCHAR(20) DEFAULT NULL COMMENT 申报人在成果中的位次如1/3, description TEXT COMMENT 成果简介与个人贡献说明, attachment_path VARCHAR(255) DEFAULT NULL COMMENT 佐证材料存储路径, status TINYINT NOT NULL DEFAULT 0 COMMENT 审核状态0草稿 1待审核 2已通过 3已驳回, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_student (student_id), KEY idx_status (status) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4 COMMENT创新成果申报信息表; -- 审核记录表 CREATE TABLE review_record ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键ID, result_id BIGINT NOT NULL COMMENT 成果ID, reviewer_id BIGINT NOT NULL COMMENT 审核人ID, review_status TINYINT NOT NULL COMMENT 审核结论1通过 2驳回, review_comment VARCHAR(500) DEFAULT NULL COMMENT 审核意见驳回时必填, review_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 审核时间, PRIMARY KEY (id), KEY idx_result (result_id) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4 COMMENT成果审核记录表;为什么要单独建一张审核记录表而不是只在成果表里更新一个status字段因为作为正式的管理系统每一次审核操作都应该有迹可查。而且这张表还有一个妙用成果列表页可以展示“最近审核日期”“审核老师是谁”这些信息在成果表里不会冗余存储而是关联查询得到的。这种“状态变化留痕”的设计思路在企业级系统里是硬性要求写在答辩里会显得你考虑得很周全。2.3 状态机设计让审核流程不混乱成果的状态我用0-3四个数字表示落地到代码里就是一个状态枚举类。很多同学做这种带审核流程的系统容易犯一个错在Service层里随便写if判断今天加一个状态明天加一个状态代码越写越乱。正确的做法是先定义清楚状态机明确“谁能在什么状态下执行什么操作”public enum ResultStatusEnum { DRAFT(0, 草稿), PENDING(1, 待审核), APPROVED(2, 已通过), REJECTED(3, 已驳回); private final int code; private final String desc; // 构造方法、getter方法省略... // 定义允许的状态迁移 public static boolean canTransit(int from, int to) { // 草稿可以提交成为待审核 if (from DRAFT.code to PENDING.code) return true; // 待审核可以被通过或驳回 if (from PENDING.code to APPROVED.code) return true; if (from PENDING.code to REJECTED.code) return true; // 被驳回的可以重新编辑后再次提交 if (from REJECTED.code to PENDING.code) return true; return false; } }这样写有一个非常直接的好处你可以在Service层做校验时这样写if (!ResultStatusEnum.canTransit(currentStatus, targetStatus)) { throw new BizException(当前状态下不允许执行该操作); }一行代码拦截所有非法操作。学生不可能绕过前端把“已通过”的成果状态改成“草稿”后端不会因为漏写判断而暴露隐藏bug。这就是数据状态机的价值所在。3. 核心功能模块实现思路3.1 登录认证与权限控制的落地方式登录这一块如果你用的是Spring Security JWT的组合很多同学会卡在配置上。我建议你不要直接去背Filter链的源码而是要理解它的核心思路登录成功后服务器签发一个token之后每次请求前端都在请求头里带上这个token后端用过滤器验证token是否有效再判断用户是否有权限访问某个接口。Controller层的权限控制我的建议是用注解直接体现在接口方法上RestController RequestMapping(/api/result) public class ResultController { PostMapping(/submit) PreAuthorize(hasRole(STUDENT)) public ApiResponse submit(RequestBody ResultSubmitDTO dto) { // 学生提交成果申报 } GetMapping(/pending) PreAuthorize(hasRole(TEACHER) or hasRole(ADMIN)) public ApiResponse pendingList(RequestParam(defaultValue 1) int pageNum, RequestParam(defaultValue 10) int pageSize) { // 教师查看待审核列表 } }注意前端界面上隐藏按钮只是用户体验层面的措施真正的安全边界一定要在后端接口上。我见过不少毕设项目只在前端做了路由守卫后端所有接口都是裸奔的谁拿到了路径都能直接调用修改数据这种问题一旦被答辩老师发现分数会很难看。3.2 成果申报与审核流程的实现细节成果申报的Service层代码里我认为最值得讲的是“事务控制”。一个提交操作既要往成果信息表插入主记录又要往附件表插入附件关联记录还要把Redis里缓存的学生成果数量统计加一任何一个环节失败数据都会对不上。所以在关键方法上加上Transactional注解是基本操作。审核逻辑更是如此。老师点击“通过”按钮后端要做三件事更新成果表状态、写入审核记录表、给学生发送一条站内消息或者只是状态变更通知。这三件事必须在一个事务里完成Transactional(rollbackFor Exception.class) public void review(Long resultId, Integer status, String comment, Long reviewerId) { ResultInfo result resultMapper.selectById(resultId); if (result null) { throw new BizException(成果不存在); } if (!ResultStatusEnum.PENDING.getCode().equals(result.getStatus())) { throw new BizException(该成果不在待审核状态); } // 1. 更新成果状态 result.setStatus(status); resultMapper.updateById(result); // 2. 写入审核记录 ReviewRecord record new ReviewRecord(); record.setResultId(resultId); record.setReviewerId(reviewerId); record.setReviewStatus(status); record.setReviewComment(StringUtils.hasText(comment) ? comment : 同意通过); reviewRecordMapper.insert(record); // 3. 更新统计缓存等额外操作 statsService.updateResultStat(result.getResultType(), status); }注意这里我先查了一遍成果的当前状态再执行更新而不是直接update where id ? and status 1。前一种写法在多个人同时操作时有并发风险但毕设项目里问题不大而且代码更直观好讲。如果你想让系统更专业一点可以在update语句里带上status条件利用数据库行锁来保证状态只被正确的人修改一次。3.3 文件上传审题时最容易忽视的硬需求创新成果申报系统里文件上传不是可选项。论文要有PDF证明竞赛要有获奖证书照片专利要有授权通知书。这块我强烈建议你直接使用本地文件存储方案而不是去接OSS对象存储。本地存储就是项目里配置一个上传目录把文件写到磁盘上数据库记录文件路径前端通过一个静态资源映射的接口访问。这样不依赖外网环境部署到学校机房或者演示用的服务器上都能正常运行。Spring Boot配置本地文件访问非常简单Configuration public class FileUploadConfig implements WebMvcConfigurer { Value(${file.upload-path}) private String uploadPath; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 把 /files/** 路径映射到本地磁盘目录 registry.addResourceHandler(/files/**) .addResourceLocations(file: uploadPath /); } }上传接口建议限制单文件大小不超过10MB后缀名白名单校验放后端做。我知道有些同学只在前端做accept.pdf后端不校验学生随手改后缀就能传一个exe上去这种上传漏洞在企业面试里是必问的。控制文件大小直接在配置里加spring.servlet.multipart.max-file-size10MB spring.servlet.multipart.max-request-size50MB3.4 数据统计与可视化展示统计看板是这个系统的门面也是很多人答辩时的演示重点。我用ECharts画了三个图表学院成果总数柱状图、成果类型饼图、近三年成果数量趋势折线图。这些数据都来自后端的一个统计接口GetMapping(/stats/summary) PreAuthorize(hasRole(TEACHER) or hasRole(ADMIN)) public ApiResponse summary() { SummaryVO vo new SummaryVO(); // 查询各类型成果数量 vo.setTypeStat(resultMapper.countByType()); // 查询各学院成果数量 vo.setCollegeStat(resultMapper.countByCollege()); // 查询近三年成果趋势 vo.setYearTrend(resultMapper.countByYear()); return ApiResponse.success(vo); }需要注意的是这里的Mapper方法你可以直接用MyBatis-Plus的selectMaps加QueryWrapper分组的写法也可以写两句简单的SQL。我推荐直接写SQL注解因为逻辑更清晰Select(SELECT result_type AS name, COUNT(*) AS value FROM result_info WHERE status 2 GROUP BY result_type) ListMapString, Object countByType();前端拿到List之后转成ECharts需要的格式就五行代码的事。这个功能做完之后你可以导出整个看板页面用来做答辩的开场演示——大屏一打开数据图表自动加载老师对你的第一印象就已经建立起来了。4. 关键难点与解决方案4.1 并发审核场景下的数据一致性毕设里一般不会真的遇到高并发但“能不能考虑到并发问题”是评分点。你要知道假如同一个学生提交的两个不同成果被两个不同的老师同时审核而代码没有做好控制就可能出现“状态覆盖”问题。前面提过的做法——canTransit状态机校验——能拦住绝大多数非法操作但还有一个更极端的场景学生A把成果提交后管理员的“撤回修改”请求和学生自己的“修改并重新提交”请求同时生效最终数据是否还能保持一致最简单的解决办法是给成果主表加一个乐观锁字段version更新的时候带上版本号UPDATE result_info SET status 2, version version 1 WHERE id #{id} AND version #{oldVersion}如果更新影响行数为0说明数据已经被其他人改过了后端返回“操作失败请刷新后重试”。这个方案代码量极小但体现的是你对并发控制的认识答辩时讲出来效果比背一堆概念好得多。4.2 大文件上传导致的内存溢出如果一个项目里没有对上传做限制学生传一个几百MB的视频作为“佐证材料”后端直接用MultipartFile.getBytes()读取内存瞬间被打满项目就挂了。我给的建议是两层控制。第一层是前面说的在Spring配置里限制单文件大小第二层是用transferTo方法将文件流直接写入磁盘而不是通过byte[]中转PostMapping(/upload) public ApiResponse upload(RequestParam(file) MultipartFile file) { if (file.isEmpty()) { throw new BizException(上传文件不能为空); } String originalFilename file.getOriginalFilename(); // 生成唯一文件名防止重名覆盖 String suffix originalFilename.substring(originalFilename.lastIndexOf(.)); String filename UUID.randomUUID().toString().replace(-, ) suffix; File dest new File(uploadPath, filename); if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } try { file.transferTo(dest); // 流式写入不驻留内存 } catch (IOException e) { throw new BizException(文件上传失败); } return ApiResponse.success(Map.of(path, filename, name, originalFilename)); }4.3 分页查询性能优化不要在列表页取全表成果列表页用MyBatis-Plus分页非常简单但有几个性能细节值得注意。一是排序字段要建立索引create_time这个字段建议加个普通索引二是列表页的字段只查询需要的列别select *把大字段description和attachment_path都带出来。对于MySQL来说查询的列越多磁盘IO开销越大尤其当description是几百字的TEXT时更是如此。MyBatis-Plus里可以只查部分字段PageResultInfo page resultMapper.selectPage( new Page(pageNum, pageSize), new LambdaQueryWrapperResultInfo() .select(ResultInfo::getId, ResultInfo::getResultName, ResultInfo::getResultType, ResultInfo::getStatus, ResultInfo::getCreateTime) .eq(StringUtils.hasText(type), ResultInfo::getResultType, type) .orderByDesc(ResultInfo::getCreateTime) );另外如果你做的是前后端分离前端表格里需要显示“学生姓名”“学院名称”而成果表里只有student_id这就需要关联查询了。可以在MyBatis-Plus的分页插件基础上写一个自定义SQL做联表查询或者简单点在Service层查出成果列表后再用student_id集合批量查用户表拼装VO返回。第二种方式虽然多一次数据库交互但代码结构非常清晰新手也不容易出错。4.4 前端联调时的跨域问题前端跑在8080端口后端跑在9090端口浏览器直接访问后端接口会被跨域拦截。这个坑每个做前后端分离项目的同学都会遇到。解决方式有三种我给你按优先级排一下第一后端配置全局CORS最省事适合开发阶段Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(http://localhost:8080) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }第二前端配置代理适合调试场景在vite.config.js里配proxy把/api开头的请求转发到后端地址浏览器看到的请求是同源的就不会报跨域错误。第三生产环境用Nginx做反向代理把前端和后端挂在同一个域名下自然也没有跨域问题。我建议你在开发时用第一种第二种都行部署时用第三种。把三种方式都掌握答辩时老师问你“跨域怎么解决”你就不慌了。5. 环境准备与项目部署指南5.1 环境变量的坑值得先说清楚这个项目用到的环境就是JDK、Maven、MySQL、Redis。很多同学卡在环境配置上尤其是Java环境变量。我不在这里重新写一遍配置教程但我说三个最容易踩的坑lombok找不到符号、ClassNotFoundException: javax.annotation.PostConstruct这些问题的八成原因是你的JDK版本和项目配置对不上。Spring Boot 2.7.x配JDK 8或11没问题配JDK 17就需要额外加依赖。如果你用的是IDEA自带的高版本JDK建议直接统一项目所有地方都用同一个版本包括pom.xml里的java.version、IDEA的Project Structure、Maven的JRE设置。第二个坑是MySQL 8.0的驱动配置。com.mysql.jdbc.Driver是老版本MySQL的写法8.0要用com.mysql.cj.jdbc.Driver同时连接串要加serverTimezoneAsia/Shanghai不然日期查询会出现8小时时差。第三个坑是Redis。如果你的系统用了Redis本地没装或者没启动Spring Boot项目启动报错是必然的。建议把Redis的配置设成非必选或者至少单独写清楚“没有Redis也能体验完整功能”的降级方案。实在不行Redis作为加分项留到答辩演示后再介绍也可以。5.2 后端打包部署从jar包到服务器部署后端我用的是最常规的打包方式mvn clean package -DskipTests打完包后会生成一个target/xxx.jar文件你把它上传到服务器运行java -jar xxx.jar --spring.profiles.activeprodprod配置文件里可以把数据库连接、文件上传路径等手动指定成服务器上的绝对路径。你还需要确保服务器防火墙开放了对应端口。如果你部署的服务器没有外网访问MySQL的权限最简单的做法是宝塔面板装数据库本地Navicat连上去导入SQL文件。很多学生第一次部署都会卡在“数据库连接被拒绝”这一步先检查三件事数据库端口是否开放、账号是否有访问权限、连接串里的IP是否正确。5.3 前端打包与Nginx配置Vue项目打包npm run build打包后得到dist目录里面是纯静态文件。我建议你用Nginx托管server { listen 80; server_name your-domain-or-ip; # 前端静态文件 root /www/result-system/dist; index index.html; # 解决vue-router的history模式刷新404问题 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; } # 本地文件上传目录映射 location /files/ { alias /www/result-system/upload/; } }这套配置里后端接口以/api/开头前端上传的附件预览以/files/开头两个路径都通过Nginx做了统一入口浏览器看到的host就是服务器IP完全没有跨域问题。try_files那行如果你没写刷新页面就会出现404这是Vue Router History模式的经典坑一定要记住。5.4 系统运行验证清单部署完成后建议按照下面的清单快速跑一遍验证学生登录提交一条带PDF附件的成果状态显示“待审核”教师登录能看到待审核列表点击预览PDF附件能正常打开教师审核通过后学生端状态变为“已通过”更换成果类型在统计看板上确认柱状图和饼图数据有变化管理员新增一个成果类型学生端申报下拉框立即出现新选项。这五步走完整个项目的主流程就通了。我一般还会额外验证一种异常情况学生提交后再点“撤回”系统应提示“允许撤回”待审核状态下学生点“编辑”系统应提示“不能编辑”。这就是状态机在起作用你可以在答辩时主动演示这个场景老师会觉得你考虑问题很细致。6. 毕设答辩常见问题与踩坑实录6.1 高频技术问题速查表这部分我整理了带学生做毕设时被问得最多的问题你可以直接拿去当复习提纲问题方向常见问法我的建议回答要点技术选型为什么选Spring Boot VueSpring Boot生态成熟、自动化配置降低开发成本Vue组件化开发提高界面复用性前后端分离便于团队协作和后续维护权限控制接口安全怎么做的Spring Security过滤器链对token校验方法级注解控制角色访问前端只做展示控制真正的权限校验在后端完成文件上传如何防止上传危险文件后缀名白名单校验限制文件大小使用UUID重命名文件将文件存储路径与项目目录分离状态设计审核状态有没有可能冲突用状态机枚举集中管理所有状态变更走统一Service方法关键更新语句带条件防止并发覆盖性能优化数据量大了怎么办列表页字段按需查询分页查询常用查询条件加索引统计数据引入Redis缓存降低数据库压力数据安全数据库连接串和密码直接暴露吗生产环境使用独立配置文件密码通过环境变量注入前端不直接访问数据库所有操作经过后端API6.2 开发期踩过的坑你是不是也遇到过这个项目代码量不算大但我在调试过程中遇到过的坑还真的挺典型的。第一个坑是MyBatis-Plus的逻辑删除配置和唯一索引冲突。我给用户表加了deleted逻辑删除字段又在student_number上建了唯一索引。删除一个用户后再重新导入同一个学号的学生插入就报唯一索引冲突。原因是逻辑删除的记录实际还在表里唯一索引仍然生效。解决方案是给唯一索引改成联合索引(student_number, deleted)或者在插入前先物理清理删除标记的数据。第二个坑是Spring Security放行路径配置错误。我当时把/api/login放在了permitAll里但静态资源的/files/**路径忘了放行导致上传的图片在页面上无法预览。排查了很久才发现是Security拦截了图片请求。解决后在Security配置类中加上antMatchers(/files/**).permitAll()就正常了。第三个坑是前端的时间格式。MySQL的datetime传到后端再通过JSON序列化传给前端默认格式是一长串数字时间戳Element Plus的表格直接显示这串数字很难看。解决方法是后端在Jackson配置里统一格式化spring.jackson.date-formatyyyy-MM-dd HH:mm:ss spring.jackson.time-zoneGMT86.3 答辩演示的加分技巧这套系统答辩演示时我有几个建议可以分享。演示开始先打开统计看板展示柱状图和饼图让数据先抓眼球。然后从“学生申报”开始走完整流程上传一个文件展示用户操作流畅度。审核成功后切回看板指出刚才新增的那条数据已经体现在图表里。整个过程不超过三分钟但把系统的完整链路全部展示出来了。另外强烈建议你准备好一个“异常演示”剧本。比如用另一个学生账号登录去访问教师接口后端返回403。这个演示直接证明权限控制是真实有效的比嘴上说“我做了权限控制”有说服力十倍。如果你做了Redis缓存可以补充一句“登录令牌通过Redis管理支持过期自动清理用户量上来后也可以横向扩展缓存节点。”这句话一出口老师就知道你对缓存不是背概念的水平。7. 个人经验总结与扩展建议我实际带着这个题目改过几个版本如果让我重新做一遍有些地方我会做得不一样这里分享给你。我建议你后面可以往这三个方向扩展。第一个方向是增加消息通知模块。目前审核状态变化是靠学生刷新页面才能看到优化方案是审核通过或驳回后给该学生生成一条站内消息甚至在系统公告栏实时滚动“某学院某同学获发明专利授权”。技术上就是审核事务里顺带往message表插一条数据一小时能做完但系统完整度能上一个台阶。第二个方向是把附件预览做得更细。目前只做了图片预览和PDF下载可以引入一个在线文档预览组件加载更多的文件格式。这属于体验优化不影响主流程但演示的时候很出效果。第三个方向是统计数据导出成Word或Excel报告。很多高校管理员每个月要向上级汇报成果情况如果系统能一键生成Excel统计报表是很接地气的功能。用EasyExcel或者POI导出半小时能搞定却能成为答辩时“落地价值”的加分项。代码层面的体会我也想说两句。我自己最初把太多的逻辑放在了Controller里后来重构时把业务逻辑下沉到了Service层。这样做的好处是Controller变得非常薄每个接口只负责接收参数、调用服务、返回结果测试和排查问题都变得容易。如果你自己写代码时发现Controller越写越胖说明该考虑拆分Service了。这套系统持续迭代的空间其实挺大的比如把成果信息直接与学校教务系统对接、将学生的创新学分自动累加、引入论文查重相似度作为审核辅助指标等。当然毕设的评审标准首先是完整、可用其次才是创新。先把主流程做扎实再考虑扩展这个是恒定的顺序。希望你做的时候不要焦虑一步一个脚印把每个模块做透最终交付出一份自己满意、老师也认可的作品。
返回列表