1. 项目背景与技术选型
校园管理系统作为高校信息化建设的基础设施,承担着学生信息管理、课程安排、成绩统计等核心功能。传统校园管理系统往往采用单体架构,前后端耦合度高,导致系统维护困难、扩展性差。而基于SpringBoot+Vue的前后端分离架构,能够有效解决这些问题。
技术栈选择上,后端采用SpringBoot框架主要基于以下考量:
- 约定优于配置的设计理念,大幅减少XML配置
- 内嵌Tomcat服务器,简化部署流程
- 完善的Starter生态,轻松集成MyBatis、Redis等组件
- 自动化的依赖管理,通过spring-boot-starter-parent统一版本
前端选择Vue.js框架的核心优势:
- 响应式数据绑定,简化DOM操作
- 组件化开发模式,提高代码复用率
- Vue CLI提供的现代化工具链
- 丰富的生态系统(Vuex、Vue Router等)
数据库选用MySQL 8.0版本,主要考虑因素:
- 高校场景下数据关系明确,适合关系型数据库
- 事务支持完善,保证数据一致性
- 社区活跃,遇到问题容易找到解决方案
- 与MyBatis的兼容性良好
2. 系统架构设计
2.1 整体架构
系统采用经典的三层架构:
- 表现层:Vue构建的SPA应用
- 业务逻辑层:SpringBoot提供的RESTful API
- 数据访问层:MyBatis实现ORM映射
前后端通过HTTP协议通信,接口遵循REST规范。为保障安全性,所有请求都需要携带JWT令牌。
2.2 数据库设计
核心表结构设计如下:
学生表(student)
sql复制CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`student_no` varchar(20) NOT NULL COMMENT '学号',
`name` varchar(50) NOT NULL,
`gender` tinyint DEFAULT '0' COMMENT '0-男 1-女',
`id_card` varchar(18) NOT NULL COMMENT '身份证号',
`class_id` int NOT NULL COMMENT '班级ID',
`enroll_date` date NOT NULL COMMENT '入学日期',
`status` tinyint DEFAULT '1' COMMENT '1-在读 2-休学 3-退学',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_student_no` (`student_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
课程表(course)
sql复制CREATE TABLE `course` (
`id` int NOT NULL AUTO_INCREMENT,
`course_code` varchar(20) NOT NULL COMMENT '课程编号',
`name` varchar(100) NOT NULL,
`credit` decimal(3,1) NOT NULL COMMENT '学分',
`hours` int NOT NULL COMMENT '课时',
`teacher_id` int NOT NULL COMMENT '授课教师',
`classroom` varchar(50) DEFAULT NULL,
`schedule` varchar(255) DEFAULT NULL COMMENT '上课时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
成绩表(score)
sql复制CREATE TABLE `score` (
`id` int NOT NULL AUTO_INCREMENT,
`student_id` int NOT NULL,
`course_id` int NOT NULL,
`regular_score` decimal(5,2) DEFAULT NULL COMMENT '平时成绩',
`exam_score` decimal(5,2) DEFAULT NULL COMMENT '考试成绩',
`final_score` decimal(5,2) GENERATED ALWAYS AS (COALESCE(`regular_score`*0.3 + `exam_score`*0.7,0)) STORED,
`term` varchar(20) NOT NULL COMMENT '学期',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_student_course` (`student_id`,`course_id`,`term`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 后端实现细节
3.1 SpringBoot应用配置
核心配置文件application.yml示例:
yaml复制server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://localhost:3306/campus?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
3.2 权限控制实现
采用Spring Security + JWT实现认证授权:
- 创建JWT工具类
java复制public class JwtUtil {
private static final String SECRET = "campus-secret-key";
private static final long EXPIRATION = 86400000L; // 24小时
public static String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
public static String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}
- 配置Spring Security
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4. 前端实现细节
4.1 Vue项目结构
code复制src/
├── api/ # 接口定义
├── assets/ # 静态资源
├── components/ # 公共组件
├── router/ # 路由配置
├── store/ # Vuex状态管理
├── utils/ # 工具函数
├── views/ # 页面组件
│ ├── student/ # 学生管理
│ ├── course/ # 课程管理
│ ├── score/ # 成绩管理
│ └── system/ # 系统管理
├── App.vue # 根组件
└── main.js # 入口文件
4.2 典型页面实现 - 学生列表
使用Element UI表格组件实现分页查询:
vue复制<template>
<div class="student-list">
<el-table :data="tableData" border style="width: 100%">
<el-table-column prop="studentNo" label="学号" width="180"></el-table-column>
<el-table-column prop="name" label="姓名" width="120"></el-table-column>
<el-table-column prop="gender" label="性别" width="80">
<template #default="scope">
{{ scope.row.gender === 0 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column prop="className" label="班级"></el-table-column>
<el-table-column prop="enrollDate" label="入学日期" width="120"></el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="mini" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</template>
<script>
import { getStudentList } from '@/api/student'
export default {
data() {
return {
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
const params = {
page: this.currentPage,
size: this.pageSize
}
getStudentList(params).then(res => {
this.tableData = res.data.list
this.total = res.data.total
})
},
handleSizeChange(val) {
this.pageSize = val
this.fetchData()
},
handleCurrentChange(val) {
this.currentPage = val
this.fetchData()
}
}
}
</script>
5. 系统部署方案
5.1 开发环境部署
- 后端启动:
bash复制# 打包
mvn clean package
# 运行
java -jar target/campus-system-0.0.1-SNAPSHOT.jar
- 前端启动:
bash复制# 安装依赖
npm install
# 开发模式运行
npm run serve
# 生产构建
npm run build
5.2 生产环境部署
推荐使用Docker容器化部署:
- 编写Dockerfile
dockerfile复制# 后端Dockerfile
FROM openjdk:11-jre
COPY target/campus-system-0.0.1-SNAPSHOT.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
# 前端Dockerfile
FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
- Nginx配置示例
nginx复制server {
listen 80;
server_name campus.example.com;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
6. 开发经验与优化建议
在实际开发过程中,有几个关键点值得注意:
- 接口文档管理:使用Swagger或YAPI管理API文档,保持前后端协作效率。SpringBoot集成Swagger示例:
java复制@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.campus.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("校园管理系统API文档")
.description("前后端接口定义")
.version("1.0")
.build();
}
}
- 性能优化:
- 启用MyBatis二级缓存
- 高频查询接口添加Redis缓存
- 使用PageHelper实现物理分页
- 前端组件按需加载
- 常见问题排查:
- 跨域问题:确保后端配置CORS或通过Nginx代理解决
- 时区问题:统一使用UTC+8时区,数据库连接字符串添加serverTimezone=Asia/Shanghai
- 前端路由问题:配置Nginx支持HTML5 History模式
- 扩展性考虑:
- 预留微服务改造空间,后期可拆分为学生服务、课程服务等独立模块
- 设计可插拔的权限模块,方便对接不同学校的权限体系
- 数据库表设计考虑分表分库可能性
