1. 项目背景与核心价值
毕业设计作为高校人才培养的关键环节,传统管理模式正面临诸多痛点。我在参与多所高校信息化建设过程中,发现教务老师最头疼的就是每年毕业季的选题协调工作。一个200人的院系,仅收集选题意向就要反复沟通3-5轮,更不用说后续的进度跟踪和文档管理。
这个基于SpringBoot+Vue的毕业设计管理系统,正是为解决这些实际问题而生。系统采用前后端分离架构,后端用SpringBoot提供RESTful API,前端用Vue.js构建交互界面,数据库选用MySQL 8.0。这种技术组合既保证了系统性能,又便于二次开发。
关键设计原则:所有功能模块都围绕"降低沟通成本、规范流程节点、留存过程痕迹"三大目标展开。比如在选题阶段,我们设计了导师发布课题→学生预选→双向确认的流程,避免了传统Excel汇总时的版本混乱问题。
2. 技术架构解析
2.1 后端技术栈设计
SpringBoot 2.7.x作为基础框架,其自动配置特性大幅减少了XML配置。我在pom.xml中特别优化了依赖管理:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
安全模块采用Spring Security + JWT方案。这里有个细节处理:将JWT的刷新令牌有效期设置为7天(主令牌2小时),既保证安全又避免频繁登录:
java复制@Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("your-secret-key"); // 实际项目应从配置读取
return converter;
}
2.2 前端工程化实践
Vue 3组合式API大幅提升了代码组织性。我在src目录下采用模块化结构:
code复制src/
├── api/ # 接口定义
├── assets/ # 静态资源
├── components/ # 公共组件
├── router/ # 路由配置
├── store/ # Vuex状态
└── views/ # 页面组件
Element Plus按需引入减少打包体积,在vite.config.js中配置:
javascript复制import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
]
})
3. 核心功能实现
3.1 选题管理模块
采用状态机模式管理选题流程,定义枚举类:
java复制public enum TopicStatus {
DRAFT("草稿"),
PUBLISHED("已发布"),
SELECTED("已选定"),
CONFIRMED("已确认"),
ARCHIVED("已归档");
private final String desc;
// constructor & getter
}
数据库设计时特别注意了关联关系:
sql复制CREATE TABLE `topic` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`teacher_id` bigint NOT NULL,
`max_students` int DEFAULT '1',
`status` varchar(20) DEFAULT 'DRAFT',
PRIMARY KEY (`id`),
CONSTRAINT `fk_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 进度监控系统
使用Spring Schedule实现定时进度检查:
java复制@Scheduled(cron = "0 0 9 * * ?") // 每天9点执行
public void checkProgress() {
List<Topic> topics = topicRepository.findByStatus(TopicStatus.CONFIRMED);
topics.forEach(topic -> {
if (topic.getLastUpdate().before(DateUtils.addDays(new Date(), -7))) {
alertService.sendReminder(topic.getStudent(), "您的毕业设计已超过7天未更新");
}
});
}
前端用Echarts可视化呈现进度:
javascript复制const initChart = () => {
const chart = echarts.init(domRef.value);
chart.setOption({
tooltip: { trigger: 'item' },
series: [{
type: 'pie',
data: [
{ value: progressData.completed, name: '已完成' },
{ value: progressData.delayed, name: '已延期' }
]
}]
});
}
4. 关键问题解决方案
4.1 文件版本控制
采用"时间戳+用户ID"命名策略避免文件覆盖:
java复制public String generateFileName(MultipartFile file, Long userId) {
String originalName = file.getOriginalFilename();
String ext = originalName.substring(originalName.lastIndexOf("."));
return System.currentTimeMillis() + "_" + userId + ext;
}
文件元数据存储到数据库,支持版本回溯:
sql复制CREATE TABLE `document` (
`id` bigint NOT NULL AUTO_INCREMENT,
`topic_id` bigint NOT NULL,
`file_path` varchar(255) NOT NULL,
`version` int DEFAULT '1',
`upload_time` datetime NOT NULL,
PRIMARY KEY (`id`)
);
4.2 实时消息通知
结合WebSocket和Redis发布订阅模式:
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*");
}
}
前端使用SockJS建立连接:
javascript复制const socket = new SockJS('/ws');
const stompClient = Stomp.over(socket);
stompClient.connect({}, () => {
stompClient.subscribe('/topic/notifications', (message) => {
showNotification(JSON.parse(message.body));
});
});
5. 部署与优化实践
5.1 生产环境配置
采用Nginx反向代理+负载均衡:
nginx复制upstream backend {
server 127.0.0.1:8080 weight=5;
server 192.168.1.2:8080 weight=3;
}
server {
listen 80;
server_name yourdomain.com;
location /api {
proxy_pass http://backend;
proxy_set_header Host $host;
}
location / {
root /var/www/html;
try_files $uri $uri/ /index.html;
}
}
5.2 性能优化技巧
- 开启MyBatis-Plus二级缓存:
yaml复制mybatis-plus:
configuration:
cache-enabled: true
- 前端路由懒加载:
javascript复制const routes = [
{
path: '/admin',
component: () => import('../views/Admin.vue')
}
]
- 使用Redis缓存热点数据:
java复制@Cacheable(value = "topics", key = "#teacherId")
public List<Topic> getTeacherTopics(Long teacherId) {
return topicMapper.selectByTeacherId(teacherId);
}
6. 开发经验总结
在三个学校的实际部署中,我们发现这些配置特别重要:
- JWT密钥轮换:每月更换签名密钥,旧密钥保留24小时用于令牌过渡
- 文件存储策略:超过100MB的文件建议使用OSS存储
- 审计日志:关键操作必须记录操作人、时间和IP
对于想二次开发的同行,建议重点关注:
- 选题流程的状态机设计(可扩展答辩环节)
- 文档对比功能集成(如调用OnlyOffice API)
- 移动端适配(使用Vant组件库)
系统目前已在某高校稳定运行2个毕业季,管理了超过800个毕业设计课题。最让我自豪的是,教务主任反馈"再也不用半夜接电话调课题了"。这或许就是技术创造价值的最好证明。