毕业论文选题管理系统是高校教学管理中的重要工具,旨在解决传统纸质选题流程效率低下、信息不透明的问题。作为一名参与过多个高校信息化项目的开发者,我将分享基于SpringBoot+Vue技术栈实现该系统的完整方案。
这个系统主要解决三个核心痛点:一是选题流程繁琐,师生需要多次线下沟通;二是选题状态不透明,学生无法实时了解选题审核进度;三是缺乏数据分析,院系难以掌握选题分布情况。我们采用前后端分离架构,后端使用SpringBoot提供RESTful API,前端使用Vue.js构建交互界面,实现了选题全流程的数字化管理。
选择SpringBoot+Vue的组合主要基于以下考虑:
提示:在高校环境中,系统通常在学期初面临高并发访问,建议在SpringBoot中配置HikariCP连接池,并合理设置MySQL的max_connections参数。
系统采用经典的三层架构:
code复制表示层(Vue) ←HTTP→ 业务逻辑层(SpringBoot) ←JDBC→ 数据访问层(MySQL)
特别在权限控制层,我们采用JWT+Spring Security的方案,相比传统的Session管理更适应前后端分离场景。JWT token中携带用户角色信息,前端根据角色动态渲染菜单,后端通过注解进行方法级权限控制。
后端采用jjwt库生成token,关键配置如下:
java复制@Configuration
public class JwtConfig {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.claim("roles", userDetails.getAuthorities())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
}
前端在axios拦截器中添加token:
javascript复制service.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = 'Bearer ' + token
}
return config
})
设计RBAC模型,包含以下数据库表:
sys_user: 用户基本信息sys_role: 角色定义(admin/teacher/student)sys_menu: 菜单权限sys_user_role: 用户-角色关联sys_role_menu: 角色-菜单关联在SpringBoot中通过注解控制权限:
java复制@PreAuthorize("hasRole('teacher') or hasRole('admin')")
@PostMapping("/topics")
public Result createTopic(@RequestBody Topic topic) {
// 创建选题逻辑
}
选题状态流转采用状态模式实现:
java复制public interface TopicState {
void handleSubmit(TopicContext context);
void handleApprove(TopicContext context);
void handleReject(TopicContext context);
}
// 具体状态类
public class DraftState implements TopicState {
@Override
public void handleSubmit(TopicContext context) {
context.setState(new UnderReviewState());
// 通知教研室主任审核...
}
}
状态枚举定义:
java复制public enum TopicStatus {
DRAFT("草稿", 0),
UNDER_REVIEW("审核中", 1),
APPROVED("已通过", 2),
REJECTED("已驳回", 3),
PUBLISHED("已发布", 4),
SELECTED("已被选", 5);
private final String desc;
private final int code;
// 构造方法、getter省略
}
为避免多个学生同时选择热门题目,采用乐观锁机制:
sql复制UPDATE topic
SET selected_count = selected_count + 1
WHERE id = #{id} AND selected_count < max_students
在Service层检查更新结果:
java复制int rows = topicMapper.updateWithOptimisticLock(topic);
if (rows == 0) {
throw new BusinessException("该选题已被选满");
}
sql复制CREATE TABLE `topic` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL COMMENT '选题标题',
`description` text COMMENT '详细描述',
`teacher_id` bigint NOT NULL COMMENT '指导教师ID',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '状态(0-草稿,1-审核中...)',
`max_students` int NOT NULL DEFAULT '1' COMMENT '最大可选人数',
`selected_count` int DEFAULT '0' COMMENT '已选人数',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_teacher` (`teacher_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
索引策略:
分页优化:
sql复制-- 避免使用LIMIT offset, size
SELECT * FROM topic
WHERE status = 1 AND id > #{lastId}
ORDER BY id ASC
LIMIT #{size}
java复制@Cacheable(value = "topic", key = "'list_' + #status")
public List<Topic> listByStatus(Integer status) {
return topicMapper.selectByStatus(status);
}
使用Vuex管理全局状态:
javascript复制const store = new Vuex.Store({
state: {
topicList: [],
filters: {
status: null,
keyword: ''
}
},
mutations: {
updateTopics(state, payload) {
state.topicList = payload
}
},
actions: {
async fetchTopics({commit}, filters) {
const res = await api.getTopics(filters)
commit('updateTopics', res.data)
}
}
})
根据选题状态动态渲染操作按钮:
vue复制<template>
<el-button
v-if="hasPermission('topic:submit') && topic.status === 0"
@click="handleSubmit"
>
提交审核
</el-button>
<el-button
v-if="hasPermission('topic:approve') && topic.status === 1"
type="success"
@click="handleApprove"
>
通过审核
</el-button>
</template>
使用Echarts实现选题统计:
javascript复制initChart() {
const chart = echarts.init(this.$refs.chart)
chart.setOption({
tooltip: {},
xAxis: {
data: ['计算机', '电子', '机械', '经管']
},
yAxis: {},
series: [{
name: '选题数量',
type: 'bar',
data: [120, 80, 60, 40]
}]
})
}
Docker-compose配置示例:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- ./mysql/data:/var/lib/mysql
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
frontend:
build: ./frontend
ports:
- "80:80"
SpringBoot调优:
server.tomcat.max-threads=200server.compression.enabled=truespring.datasource.hikari.maximum-pool-size=20前端优化:
缓存策略:
JWT续期问题:
初始设计未考虑token续期,导致用户频繁重新登录。最终采用双token方案:
文件导出内存溢出:
直接使用POI导出大数据量时出现OOM。解决方案:
-XX:+UseG1GC -Xmx512mVue响应式数据丢失:
直接给数组赋值导致视图不更新。正确做法:
javascript复制// 错误
this.list = newList
// 正确
this.list.splice(0, this.list.length, ...newList)
MySQL时区问题:
数据库时间与Java应用不一致。解决方法:
serverTimezone=Asia/Shanghai这个系统在实际部署后,选题流程从原来的平均5天缩短到2天内完成,教师审核效率提升60%,学生满意度调查显示94%的用户认为系统操作便捷。后续计划增加智能选题推荐功能,基于学生的历史成绩和研究方向进行匹配推荐。