做一个SpringBoot+Vue实习生管理系统,前前后后折腾了两周,期间踩了不少坑,也把整个全栈开发流程摸了一遍。这东西很适合拿来当毕设或课设,业务场景清晰,技术栈主流,做完之后对Java后端和前端工程化的理解都会上一个大台阶。这篇就把整个系统的设计思路、数据模型、核心代码和部署避坑全给捋一遍,想动手复刻或者改造成自己项目的人,可以直接照着抄。
1. 项目定位与核心需求拆解
1.1 为什么选实习生管理系统当毕设/课设项目
先说结论:实习生管理系统属于典型的“管理信息系统”,功能边界清楚,角色分明,业务流程完整,而且不会像电商系统那样规模膨胀到做不完。
从学校评审的角度看,一个合格的毕设系统至少要满足三个条件:技术栈能讲出东西、业务逻辑有复杂度、页面效果看得过去。实习生管理系统的数据流是“管理员配置基础数据 → 导师分配学生 → 学生提交日报周报 → 导师审批评分”,整条链路既有权限控制,又有状态流转,还有统计报表,答辩的时候每个模块都能拎出来讲几分钟。
具体到技术层面,前端用Vue,后端用SpringBoot,数据库用MySQL,这套组合市面上学习资料最多,遇到问题搜得到的答案也最多。对Java基础一般、没做过完整全栈项目的人来说,这是最容易上手的一条路线。
还有一个隐性好处:这个系统的数据规模天然可控,实习生几十到几百人,完全不需要引入复杂的缓存和高并发方案,但你又可以在文档里把“如果数据量大怎么优化”作为扩展点写出来,显得有思考深度。
1.2 角色权限与功能矩阵设计
实习生管理系统最忌讳的是把权限做成“管理员和学生两种角色”,那样太单薄,导师的存在感为零,业务流程也撑不起来。常规设计是三角色:管理员、导师、实习生。
管理员负责基础数据维护,包括部门管理、导师账号开通、实习生信息导入、考核指标配置、实习过程总览。导师是业务核心,能看自己名下的实习生列表,审批日报周报,给实习生打考核分,发布通知。实习生端功能相对简单:维护个人信息、提交日报和周报、发起请假申请、查看自己的考核结果和导师评语。
每个角色的功能不要贪多,但边界必须清晰。我给每张表都加了状态字段,比如日报有“待提交、待审批、已通过、已驳回”四种状态,请假有“待审核、已同意、已驳回”三种状态。状态字段是整个系统业务闭环的脉络,答辩时顺着状态机讲,逻辑链条很完整。
1.3 项目目录结构与团队协作视角
虽然是单人开发的毕设项目,但目录结构必须按多人协作的标准来。后端按controller、service、mapper、entity、common分层,前端按views、components、router、store、api分层。这样分层不是形式主义,而是你后期改 bug 的时候能快速定位问题在哪一层,写论文的时候画架构图也顺手。
我实际项目的后端包结构是这样的:
- com.example.intern.controller:接口入口,只做参数接收和返回
- com.example.intern.service:业务逻辑,事务、权限判断都在这一层
- com.example.intern.mapper:数据访问层,用MyBatis-Plus的BaseMapper接口
- com.example.intern.entity:数据库实体类
- com.example.intern.common:统一返回体、异常处理、JWT工具类
前端目录结构是标准的Vue工程:
- src/api:所有axios请求封装
- src/views:页面级组件,每个角色一个文件夹
- src/components:通用组件,比如上传头像、分页组件
- src/router:路由配置,带登录守卫
- src/store:用户状态管理
目录干净了,代码量虽然有两三千行,但维护起来完全不慌。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与工程架构实践
2.1 后端框架选型:SpringBoot 2.x + MyBatis-Plus
SpringBoot版本用2.7.x,这个版本非常成熟,网上资料多,和Vue前端联调没有任何兼容问题。不建议一上来就上SpringBoot 3,因为3是基于JDK 17的,很多教材和教程默认还是JDK 8或11,遇到奇怪的编译问题排查成本高。毕业设计求稳,工具链就用最成熟的组合。
持久层框架我选了MyBatis-Plus,而不是原生MyBatis或Spring Data JPA。原因很直接:单表CRUD不用写SQL,BaseMapper自带增删改查和分页查询,省下的时间可以拿去打磨业务逻辑。系统里有几十个接口,但真正手写SQL的地方不到十处,都是多表关联查询或统计报表。MyBatis-Plus的分页插件也很方便,PaginationInnerInterceptor一行配置搞定。
ORM框架的选择其实会影响整个项目的工作量,JPA虽然也很强大,但实体关系映射的思维门槛对初学者偏高,团队里有人不熟悉的话,改起来容易懵。MyBatis-Plus基本是“中文文档最友好的持久层框架”,按我的经验,跟着官方文档走一遍,半天就能上手。
2.2 前端工程:Vue 2 + Element UI 还是 Vite + Vue 3
这一步很多人纠结,我的建议是:如果你更熟悉Vue 2 的写法,或者你参考的资料是Vue 2,那就用Vue 2 + Element UI + Vue CLI,稳定压倒一切。如果你有余力,或者学校课程教的就是Vue 3,那就用Vue 3 + Vite + Element Plus,启动速度快,组合式API写起来也顺手。
我这次用的是Vue 2 + Element UI,不是因为它新,而是因为实习生管理系统的页面场景是典型的表格加表单,Element UI 的el-table和el-form组件开箱即用,出了任何问题都能搜到解决方案。前端页面的核心难点其实不在框架本身,而在路由守卫和axios封装,这两块做好了,权限控制的前端部分基本就稳了。
前端工程化的关键配置点有两个:一是开发环境代理,把请求转发到后端8080端口解决跨域问题;二是构建输出路径和打包配置,部署到服务器时保证静态资源能正确加载。这两个配置我后面会详细说。
2.3 数据库设计:七张核心表的结构与关联
实习生管理系统虽然功能看着多,但数据库表可以控制到七张左右,太多的表徒增维护成本,太少的表撑不起业务流程。我这边的核心表是:用户表、实习生信息表、部门表、日报表、周报表、请假表、考核表。
用户表是最基础的,字段包括用户ID、用户名、密码、角色、姓名、邮箱、手机号、头像地址、创建时间。密码这里必须存加密后的密文,我用的是BCrypt加密,原生Spring Security自带这个工具类,不需要额外引包。
实习生信息表在用户表的基础上扩展:学号、学校名称、专业、实习开始时间、实习结束时间、所属部门ID、导师ID、实习状态。这里特别注意,导师ID关联的是用户表,而不是单独建一张导师表,因为导师本质上是用户的一种角色,这样做可以减少表数量,但需要在代码里保证“被关联的用户确实是导师角色”。
日报表、周报表、请假表、考核表的结构都比较类似,包含关联的实习生ID、内容、提交时间、审批人ID、审批状态、评语。其中考核表比别的表多几个维度字段:工作态度、任务完成度、专业能力、协作沟通,最后算出一个综合分。
表设计有几个容易忽视的细节:所有表都加逻辑删除字段,状态字段用tinyint存,时间字段统一定义为datetime。逻辑删除最大的好处是数据不丢,万一误删了还能恢复,对演示系统来说很实用。
2.4 统一返回体与全局异常处理的必要性
前后端分离的项目必须定义一个统一的返回结构,否则前端每个请求都要单独处理返回格式,代码会乱成一团。我的返回体定义很简单:
json复制{
"code": 200,
"message": "操作成功",
"data": {}
}
代码如下,一个泛型类搞定:
java复制@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("操作成功");
result.setData(data);
return result;
}
public static <T> Result<T> error(Integer code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
}
全局异常处理用@RestControllerAdvice注解,把参数校验异常、业务异常、兜底异常分开处理,前端拿到结构统一的错误返回,就能根据code弹错误提示。这一步虽然代码量不大,但体验提升是立竿见影的。做判断题的时候,评审老师有可能会问“如果接口报错怎么处理”,你答出来全局异常处理,这就是加分项。
3. 核心业务模块的设计与实现
3.1 认证与权限:JWT拦截器怎么做最省事
登录认证这块,我建议不要上Spring Security的完整配置,太重了,学起来费劲。直接用JWT加自定义拦截器,是实习生管理系统最简单可控的方案。
具体流程是:用户登录时校验用户名密码,通过后生成一个JWT令牌返回给前端。前端把令牌存在localStorage,每次请求在axios拦截器里往请求头塞Authorization字段。后端写一个HandlerInterceptor,在preHandle方法里解析令牌,解析失败就返回401,解析成功就把用户ID和角色放进ThreadLocal或请求域里,后续的业务代码随时能取到当前登录用户。
JWT工具类核心就是三行逻辑:生成令牌、解析令牌、校验过期时间。用jjwt这个库实现,代码量不大:
java复制public class JwtUtils {
private static final String SECRET = "your-secret-key-your-secret-key";
private static final long EXPIRE = 1000 * 60 * 60 * 24; // 24小时
public static String generateToken(Integer userId, String username, String role) {
return Jwts.builder()
.setSubject(username)
.claim("userId", userId)
.claim("role", role)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
public static Claims parseToken(String token) {
return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
}
}
权限控制的核心在拦截器里判断角色,每个接口通过自定义注解或路径规则来控制。比如/admin/**开头的接口只有管理员能访问,/mentor/**只有导师能访问,/student/**只有实习生能访问,规则清晰,代码也好维护。虽然没有Spring Security的注解那么优雅,但足以满足系统需求。
角色权限这块有一个细节要注意:实习生和导师的功能有重叠,比如实习生要查看自己的考核结果,导师要查看名下所有实习生的考核结果,翻页和查询条件不一样,接口就得分开写。不能图省事让两个角色共用一个接口,然后前端做判断,这样前端会把后端该做的事情做掉,权限容易出漏洞。
3.2 实习生档案管理:批量导入与信息维护
管理员维护实习生信息,如果一条条手动录入,几十个人就够头疼的了。所以我加了Excel批量导入功能,用的工具是EasyExcel,阿里的开源库,比POI原生API好用太多。
批量导入的完整步骤是:管理员上传Excel文件 → 后端解析每一行数据 → 逐行校验学号是否重复、学校名是否为空 → 校验通过的数据插入数据库 → 返回导入成功的条目数和失败的条目数以及失败原因。这里有一个判断陷阱:不要前端的Excel校验做完就当作万事大吉,后端的校验才是最后的防线,因为接口是公开的,别人可以绕过前端直接调接口上传文件。
批量导入的代码套路是固定的,CustomSheetListener里重写invoke方法,逐行处理数据。核心注意点是把校验和插入放到事务里,如果中间出错了要么全部入库,要么全部回滚,避免出现半截数据。我给数据库表加了一个“导入批次号”字段,虽然功能上不是必须的,但排查问题的时候非常有用,比如可以查“某个批次导入了哪些人”。
3.3 日报周报审批:状态机的实现思路
日报周报审批是这个系统业务闭环里最有“代码感”的模块。实习生提交日报,导师登录后看到待审批的日报列表,点击进入详情页,填写评语,选择通过或驳回。实习生端能看到“已通过”和“已驳回”的反馈,被驳回的日报可以修改后重新提交。
这个流程本质上是一个状态机,状态流转图为:待提交(草稿)→ 待审批 → 已通过 / 已驳回,已驳回可以回到待提交。实现状态机不需要引入任何框架,只需在service层写状态校验逻辑,比如只有状态是“待审批”的日报才能被审批,只有“已驳回”的日报才能修改重新提交。
我最开始写这个模块时踩过一个坑:实习生编辑日报时直接提交,没有先存草稿,导致前端刷新一下编辑内容就丢了。后来改成提交按钮分成“保存草稿”和“正式提交”两个动作,体验才正常。这个交互细节虽然小,但答辩时展示出来,老师会觉得你考虑得挺周到。
周报模块和日报逻辑几乎一样,只是字段更多,多了“本周工作总结”“下周计划”两个富文本字段。富文本编辑器我用的是vue-quill-editor,轻量好用,后端存储用text类型字段,注意XSS过滤,富文本内容是最容易埋脚本注入的地方,入库前把htmL标签的script过滤掉。
3.4 考核评分与统计报表:让数据有展示价值
考核模块是系统里最有“亮点潜力”的部分。导师给实习生的考核从四个维度打分:工作态度、任务完成度、专业能力、协作沟通,每个维度10分,综合分按加权算出来,比如四者各占25%。系统自动按部门、按时间段汇总平均分,生成统计报表。
前端统计页面用ECharts实现图表展示:部门平均分柱状图、实习生考核趋势折线图、个人能力雷达图。答辩演示的时候,图表一出来视觉效果是立竿见影的,评审老师会认为你在数据可视化上花过心思。
报表的后端查询要写原生SQL,因为涉及多表关联和聚合计算。典型SQL比如“查询每个部门实习生的平均考核分”:
sql复制SELECT d.name AS deptName,
ROUND(AVG(a.total_score), 2) AS avgScore
FROM assessment a
LEFT JOIN student_info s ON a.student_id = s.id
LEFT JOIN department d ON s.dept_id = d.id
WHERE a.assess_time BETWEEN #{startTime} AND #{endTime}
GROUP BY d.id
ORDER BY avgScore DESC
这段SQL返回给前端直接能用来画柱状图。这种多表关联的统计SQL,MyBatis-Plus的lambdaQuery没法优雅实现,所以我没有硬套框架,直接用@Select注解写在Mapper里,逻辑更直观。
3.5 前端页面路由与权限菜单
前端除了写页面,还有两个配套工作必须做扎实:路由守卫和动态菜单。
路由守卫的逻辑是:用户登录后把token存在localStorage,每次路由跳转时检查是否有token,没有就强制跳转到登录页。有token还要校验当前用户角色是否有权限访问该路由,比如实习生的路由表里根本没有“导师审批”这个页面,即使他手动输入URL也会被拦下。
动态菜单的实现思路是:后端登录接口返回用户角色,前端根据角色渲染不同的菜单项。比如管理员看到“部门管理、实习生管理、导师管理”,导师看不到“导师管理”,只能看到“我的实习小组、审批记录”。前端不需要复杂的权限指令,用v-if控制菜单的显示就行,Vue Router的addRoutes动态注册路由更适合大型系统,对毕设来说基础方案完全够用。
4. 核心代码实现:从后端接口到前端联调
4.1 后端接口设计与一个完整的CRUD示例
后端接口设计遵循REST风格,我以“日报管理”模块做例子来拆解。
日报相关的接口有四个:
- POST /api/dailyReport 实习生提交日报
- PUT /api/dailyReport/{id} 实习生修改日报(草稿或已驳回状态)
- GET /api/dailyReport/my?page=1&size=10 实习生分页查看自己的日报
- GET /api/dailyReport/pending 导师查看待审批日报列表
设计接口时有一个原则:接口不要过度抽象。比如提交日报和审批日报,很多人会想合并成一个接口加类型参数,看似节省代码,实际上理解和维护成本都变高了,前端也要多传一个魔法数字。接口语义清晰一点,代码自然容易读。
提交日报的后端Service代码如下:
java复制@Override
@Transactional
public void submitReport(DailyReportDTO dto, Integer studentId) {
DailyReport report = new DailyReport();
report.setStudentId(studentId);
report.setContent(dto.getContent());
report.setReportDate(dto.getReportDate());
report.setStatus(ReportStatus.PENDING_APPROVAL.getValue());
// 保存草稿时为DRAFT,正式提交时为PENDING_APPROVAL
this.save(report);
}
关键点是所有涉及写操作的方法都加上@Transactional注解,保证数据一致性。这个注解是SpringBoot里最基础也最重要的东西。有一个很容易被忽略的细节:@Transactional默认只在发生RuntimeException时回滚,如果方法里捕获了异常并吞掉,事务是不会回滚的,代码需要特别注意异常处理顺序。
4.2 前端接口封装与状态管理
前端axios封装是每个请求都会经过的统一入口,主要做三件事:携带token、统一处理错误码、统一处理loading状态。
javascript复制import axios from 'axios'
const service = axios.create({
baseURL: '/api',
timeout: 10000
})
service.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = 'Bearer ' + token
}
return config
})
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
ElementUI.Message.error(res.message || '请求失败')
return Promise.reject(new Error(res.message))
}
return res.data
},
error => {
if (error.response && error.response.status === 401) {
localStorage.removeItem('token')
router.push('/login')
}
ElementUI.Message.error('网络异常')
return Promise.reject(error)
}
)
状态管理我用的是Vuex,主要存用户信息、角色、菜单列表,页面刷新时通过token重新请求用户信息来恢复状态。这里有一个实际经验:页面刷新时Vuex数据会清空,一定要在路由守卫里加一个“根据token拉取用户信息”的异步处理,否则一刷新页面就跳登录页,体验非常差。
4.3 前后端联调与接口文档管理
联调阶段最容易出的问题不是接口逻辑错误,而是字段名对不上,后端返回的是createTime,前端写的是createdTime,结果页面怎么都不显示数据。排查这种问题最直接的办法是看浏览器控制台的Network面板,一个个对返回的JSON字段名。
为了减少这种低级错误,我建议接口出参统一用DTO对象,不要直接把数据库实体返回给前端。数据库字段通常带下划线,比如status字段在Java里是logoutFlag之类的命名,而JSON给前端展示的时候需要的可能是布尔值。转换逻辑集中在DTO类里,用BeanUtils的copyProperties加上手动补几个字段就行。
如果有条件,接口文档用Apifox或ApiPost管理,调试和生成文档都很顺手。毕设不需要搞太重的仪式感,但接口文档写清楚是给自己省事,后面前端页面多了,你不可能记得住每个接口的每个字段含义。
5. 环境准备与部署启动全流程
5.1 本地开发环境清单
这项目需要的环境其实就四样:JDK 8或11、Maven 3.6+、Node.js 14+、MySQL 5.7或8.0。IDE我用的是IntelliJ IDEA,前端开发也在IDEA里完成,装一个Vue插件就能直接写。如果电脑配置一般,IDEA开两个项目(后端和前端)确实有点吃内存,但基本也能撑住。
Java环境这里有一个常见坑:JDK安装后mvn -v能识别,但是IDEA里新建SpringBoot项目跑不起来,大概率是Project Structure里没把JDK路径配好,IDEA的File -> Project Structure -> SDKs里删掉旧的重新添加一下即可。
Node和npm要注意版本,Node 17以上npm install某些老依赖装不上,报错内容通常和教育网、镜像源有关。我的处理方式是把npm源切到国内镜像源,装Element UI这种老牌依赖包会快很多,也稳很多。
5.2 数据库初始化与后端配置
项目里必须带一个init.sql脚本,把建表语句和初始数据都写清楚。初始数据至少包含一个管理员账号、一个导师账号、一个实习生账号,以及部门和几个测试实习生,这样项目一启动就能演示完整流程。
后端配置文件application.yml核心内容如下:
yaml复制server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/intern_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
jwt:
secret: your-secret-key
expire: 86400
连接MySQL 8的时候最容易报时区错误,红色异常信息里带有“Server returns invalid timezone”字样。最简单的解决办法就是像我上面的配置一样,在MySQL连接URL后面加上serverTimezone=Asia/Shanghai,一步到位。
启动后端前记得先在MySQL里执行init.sql脚本。 SpringBoot项目成功启动的标志是日志里出现“Started XXXApplication”,然后通过浏览器访问 http://localhost:8080/api/sys/login 能看到返回结果。
5.3 前端启动与代理配置
前端开发服务器默认跑在8080端口会和后端冲突,所以Vite或Vue CLI默认的启动端口通常改到8081或3000。前后端联调的核心配置在vue.config.js里,通过devServer的proxy把请求代理到后端地址。
Vue CLI的配置长这样:
javascript复制module.exports = {
devServer: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
}
配置代理后,前端请求 /api 开头的路径会自动转发到后端8080端口,就不会有跨域问题了,开发环境跑得最舒服的方式。pathRewrite这个配置要特别注意,如果后端接口路径本身就带/api前缀,就不能重写去掉,需要先搞清楚后端controller的RequestMapping是怎么写的。
启动前端后浏览器输入 http://localhost:3000 ,能看到登录页说明前端和服务都起来了。这里一个常见的问题:npm run serve能正常跑,但浏览器访问空白页,大概率是路由模式配了history模式但没配后端支持,本地开发环境下改回hash模式最省事。
5.4 打包部署到服务器
毕设演示或者项目验收,一般用本地演示就够了,但如果你想把项目部署到云服务器上展示,流程也不复杂。后端打包用Maven的package命令打出jar包,服务器上装JDK就能跑。前端打包用npm run build生成dist目录,然后把dist目录里的静态文件放到Nginx的html目录里,再配一个反向代理把/api的请求转发到后端8080端口。
Nginx核心配置片段:
nginx复制server {
listen 80;
server_name your-domain.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
部署前记得改数据库连接地址,不能再用localhost,要改成云服务器的内网地址。安全组和防火墙也要放行80和3306端口,否则外网访问不了。这些细节最容易在部署环节翻车,如果你之前没接过服务器环境,第一次大概率要走几趟弯路。
6. 常见问题与实战避坑记录
6.1 端口冲突与启动失败
后端8080端口被占用是最高频的启动问题,报错信息一般是“Port 8080 was already in use”。排查命令是Windows下的netstat -ano | findstr 8080,Linux或Mac下的lsof -i:8080,找到占用进程干掉就行。更省事的做法是直接改后端端口,但前端代理配置也要跟着改,所以还是清理端口比较合适。
前端端口同理,如果3000被占就换成3001或3100,这类问题本质上不是技术问题,是环境问题,解决了就过去了。
6.2 数据库连接报错的几种场景
数据库相关的报错我总结有三种高频场景:第一是MySQL服务没启动,Windows下检查系统服务;第二是密码不对,这个没什么好办法,重启确认密码;第三是时区问题,前面已经说过,URL加参数解决。还有一种情况是MySQL 8的驱动类名和旧版本不一样,SpringBoot 2.7里有默认的com.mysql.cj.jdbc.Driver,一般不用手动配,但如果报“driver-class-not-found”,记得检查pom里mysql-connector-java的依赖版本。
6.3 前后端跨域问题的处理思路
虽然开发环境用代理解决了跨域,但有些人习惯于前端直接请求后端地址,不配置代理就会遇到跨域。跨域本质是浏览器同源策略限制,不是服务器拒绝请求,后端的解决方式是在配置类里加一个CorsFilter,允许指定来源跨域访问。
我建议用代理解决开发环境的跨域,因为这样生产环境的Nginx反向代理也能复用同一套逻辑,前端代码完全不用管请求域名。如果你决定在后端加CORS配置,注意不要把允许来源设为*,因为星号无法和携带凭证的请求共存,浏览器会直接报错。
6.4 JWT令牌过期和失效的排查
JWT过期是最隐蔽的问题,因为它的表现是“一会儿能登录,一会儿请求失败”。这个问题的根源是令牌有效期设得不够长,或者登录后很久没操作令牌到期了。我的处理方式是把有效期设成24小时,前端在axios响应拦截器里捕获401状态码,提示用户“登录已过期,请重新登录”,然后跳转到登录页。
还有一种特殊情况:服务器重启时如果JWT密钥是随机生成的,重启前发放的所有令牌都会失效。解决方法是把密钥固定写在application.yml配置里,不要每次启动随机生成,这样就能保证令牌跨重启有效。
6.5 Maven依赖下载缓慢或失败
Maven首次构建要从中央仓库拉一大堆依赖,国内网络环境下慢是正常的,但慢到无法忍受就有问题了。解决方案是配置镜像源,在Maven的settings.xml里加上阿里云仓库镜像。
还有一个Maven相关的典型问题:IDEA里有些依赖显示红色报错,但命令行mvn install能正常跑。这通常是IDEA没同步Maven仓库索引,点一下IDEA右侧Maven面板的刷新按钮就能解决,不用重装依赖。
6.6 常见问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 启动报端口占用 | 8080或3000被其他进程占用 | netstat/lsof查占用进程并结束 |
| 访问接口报404 | 前端代理pathRewrite配置错误 | 确认后端接口前缀是否需要保留 |
| 登录报数据库连接失败 | MySQL没启动或时区错误 | 启动MySQL,连接URL加时区参数 |
| 页面刷新回到登录页 | Vuex状态丢失,未重新拉取用户信息 | 路由守卫里加token恢复逻辑 |
| Excel导入全是失败 | 模板字段名匹配错误 | 按EasyExcel注解字段名逐列校对 |
| 富文本内容被拦截 | 后端做了XSS过滤 | 增加白名单标签,或修改过滤策略 |
7. 扩展方向:从课设到完整项目的升级路径
实习生管理系统做完基础版本后,不要急着收工,可以挑选一个方向深入扩展,作为论文的“创新点”和答辩的加分项。
第一个方向是消息通知。目前日报审批的结果实习生只能登录系统才能看到,可以集成邮件或微信通知,在审批通过或驳回时自动推送消息。这个功能的实现思路是用Spring的事件机制,审批完成后发布一个事件,监听器负责异步发邮件,代码不复杂但很有实际意义。
第二个方向是数据看板优化。目前的统计报表是固定的几个图表,可以做成可配置的Dashboard,管理员能自由选择展示哪些指标、按什么维度聚合。这个扩展点体现了你对用户需求的分析能力,而不仅仅是堆功能。
第三个方向是导出能力。把日报周报按时间范围导出成Word或PDF,实习生实习结束的时候生成一份完整的实习档案,这是很多真实企业的需求场景。EasyExcel做Excel导出很简单,Word和PDF可以直接用POI配合模板填充,虽然工作量多一些,但生成文档的过程清晰,代码套路固定,很适合当毕设核心章节。
我个人觉得,这三个方向选一个深入做,比把基础功能无限堆叠更值得。毕竟评审老师看的是你对完整开发流程的理解,而不是页面和表格的数量。
从我的实际体会来看,做这个项目最大的收获不是学会几个框架API,而是把“数据库表设计、接口定义、权限控制、前后端联调”这一整条链路走通了。很多人在校期间写的都是单接口Demo,从来没有体会过一个完整系统怎么从0到1搭起来。做完这个实习生管理系统,你对“一个业务功能是怎么从页面表单变成数据库里的一行记录”会有非常清晰的认识,这个认识是任何教程都给不了你的。
如果你想复刻这个项目,我的建议是按章节顺序来:先把数据库表和初始数据准备好,再写后端接口,用Swagger或Apifox自测通过,然后再写前端页面联调。不要前端后端同时开工,很容易陷入“改一行后端、调一下前端”的循环里,效率很低。项目整体工作量在一周之内完成核心功能是可行的,把日报周报审批和考核评分这两个模块做扎实,这个系统就已经相当完整了。
