1. 项目背景与技术选型
实习管理系统作为高校计算机专业常见的实践项目,既能满足学生实习管理的实际需求,又涵盖了企业级应用开发的典型技术栈。这个基于SpringBoot+Vue的前后端分离项目,特别适合作为毕业设计或课程设计的选题。
为什么选择这个技术组合?SpringBoot作为当下Java领域最流行的微服务框架,其自动配置、快速启动的特性可以让我们把精力集中在业务逻辑实现上。而Vue.js作为渐进式前端框架,其响应式数据绑定和组件化开发模式,能够轻松构建现代化的用户界面。MySQL作为关系型数据库的经典选择,在中小型系统中表现稳定可靠。
我在指导学生的过程中发现,这个技术组合有三大优势:首先,SpringBoot和Vue都有丰富的文档和社区支持,遇到问题容易找到解决方案;其次,前后端分离的架构符合现代Web开发趋势,学生可以学到企业实际开发中的协作模式;最后,项目规模适中,一个学期内完全可以完成从设计到部署的全过程。
2. 系统架构设计
2.1 整体架构
系统采用经典的三层架构:
- 表现层:Vue.js + Element UI构建的管理后台界面
- 业务逻辑层:SpringBoot实现RESTful API
- 数据访问层:MyBatis + MySQL数据库
前后端通过HTTP协议通信,接口遵循JSON格式。这种架构的松耦合特性使得前后端可以并行开发,也便于后期维护和扩展。
2.2 数据库设计
核心表包括:
- 用户表(user):存储系统用户信息
sql复制CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码(加密存储)',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`role` tinyint(4) NOT NULL DEFAULT '0' COMMENT '角色(0学生,1导师,2管理员)',
`college` varchar(100) DEFAULT NULL COMMENT '学院',
`major` varchar(100) DEFAULT NULL COMMENT '专业',
`phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 实习申请表(internship_apply):记录学生实习申请信息
sql复制CREATE TABLE `internship_apply` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) NOT NULL COMMENT '学生ID',
`company_name` varchar(100) NOT NULL COMMENT '公司名称',
`position` varchar(100) NOT NULL COMMENT '实习岗位',
`start_date` date NOT NULL COMMENT '开始日期',
`end_date` date NOT NULL COMMENT '结束日期',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态(0待审核,1已通过,2已拒绝)',
`approver_id` int(11) DEFAULT NULL COMMENT '审批人ID',
`approve_time` datetime DEFAULT NULL COMMENT '审批时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_student_id` (`student_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 实习报告表(internship_report):存储学生提交的实习报告
sql复制CREATE TABLE `internship_report` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`apply_id` int(11) NOT NULL COMMENT '关联的申请ID',
`title` varchar(200) NOT NULL COMMENT '报告标题',
`content` text NOT NULL COMMENT '报告内容',
`attachment` varchar(255) DEFAULT NULL COMMENT '附件路径',
`score` int(11) DEFAULT NULL COMMENT '评分',
`comment` varchar(500) DEFAULT NULL COMMENT '评语',
`reviewer_id` int(11) DEFAULT NULL COMMENT '评审人ID',
`review_time` datetime DEFAULT NULL COMMENT '评审时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_apply_id` (`apply_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 后端实现关键点
3.1 SpringBoot基础配置
首先创建SpringBoot项目时,我推荐使用Spring Initializr(https://start.spring.io/)生成项目骨架,勾选以下依赖:
- Web: Spring Web
- Template Engine: Thymeleaf (可选)
- SQL: MySQL Driver, MyBatis Framework
- Security: Spring Security (可选)
在application.yml中配置数据库连接和MyBatis:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/internship_db?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.internship.entity
3.2 用户认证与授权
使用Spring Security实现基于角色的访问控制:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/teacher/**").hasRole("TEACHER")
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
3.3 RESTful API设计
实习申请接口示例:
java复制@RestController
@RequestMapping("/api/internship")
public class InternshipController {
@Autowired
private InternshipService internshipService;
@PostMapping("/apply")
public Result applyInternship(@RequestBody InternshipApplyDTO dto,
@CurrentUser User user) {
if (!user.getRole().equals(Role.STUDENT)) {
return Result.error("只有学生可以申请实习");
}
return internshipService.apply(dto, user.getId());
}
@GetMapping("/list")
public Result listInternships(@RequestParam(required = false) Integer status,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size,
@CurrentUser User user) {
return internshipService.listByCondition(status, page, size, user);
}
@PostMapping("/approve/{id}")
public Result approveInternship(@PathVariable Integer id,
@RequestParam Boolean approved,
@CurrentUser User user) {
if (!user.getRole().equals(Role.TEACHER)) {
return Result.error("只有导师可以审批实习");
}
return internshipService.approve(id, approved, user.getId());
}
}
4. 前端实现关键点
4.1 Vue项目初始化
使用Vue CLI创建项目:
bash复制vue create internship-frontend
cd internship-frontend
vue add router
vue add vuex
npm install axios element-ui --save
在main.js中引入Element UI:
javascript复制import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
4.2 页面布局与路由
使用Element UI的Container组件构建管理后台布局:
vue复制<template>
<el-container>
<el-aside width="200px">
<el-menu
router
:default-active="$route.path">
<el-submenu index="1">
<template slot="title">
<i class="el-icon-user"></i>
<span>用户管理</span>
</template>
<el-menu-item index="/user/list">用户列表</el-menu-item>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-briefcase"></i>
<span>实习管理</span>
</template>
<el-menu-item index="/internship/apply">实习申请</el-menu-item>
<el-menu-item index="/internship/approve">实习审批</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header>
<div class="header-right">
<el-dropdown>
<span class="el-dropdown-link">
{{userInfo.username}}<i class="el-icon-arrow-down"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-header>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
4.3 实习申请表单实现
使用Element UI的表单组件:
vue复制<template>
<el-form :model="form" :rules="rules" ref="form" label-width="100px">
<el-form-item label="公司名称" prop="companyName">
<el-input v-model="form.companyName"></el-input>
</el-form-item>
<el-form-item label="实习岗位" prop="position">
<el-input v-model="form.position"></el-input>
</el-form-item>
<el-form-item label="实习时间" prop="dateRange">
<el-date-picker
v-model="form.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">提交申请</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
form: {
companyName: '',
position: '',
dateRange: []
},
rules: {
companyName: [
{ required: true, message: '请输入公司名称', trigger: 'blur' }
],
position: [
{ required: true, message: '请输入实习岗位', trigger: 'blur' }
],
dateRange: [
{ required: true, message: '请选择实习时间', trigger: 'change' }
]
}
}
},
methods: {
submitForm() {
this.$refs.form.validate(valid => {
if (valid) {
const params = {
companyName: this.form.companyName,
position: this.form.position,
startDate: this.form.dateRange[0],
endDate: this.form.dateRange[1]
}
this.$axios.post('/api/internship/apply', params)
.then(res => {
this.$message.success('申请提交成功')
})
.catch(err => {
this.$message.error(err.response.data.message)
})
}
})
}
}
}
</script>
5. 项目部署与运行
5.1 后端部署
打包SpringBoot项目:
bash复制mvn clean package
生成的jar包可以直接运行:
bash复制java -jar target/internship-system-0.0.1-SNAPSHOT.jar
对于生产环境,建议使用Docker容器化部署:
dockerfile复制FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/internship-system-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
5.2 前端部署
构建生产环境代码:
bash复制npm run build
生成的dist目录可以部署到Nginx:
nginx复制server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
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;
}
}
5.3 常见问题解决
- 跨域问题:开发环境下可以在后端添加CORS配置
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(false)
.maxAge(3600);
}
}
- 文件上传大小限制:在application.yml中配置
yaml复制spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
- 前端路由刷新404:需要配置Nginx的try_files
nginx复制location / {
try_files $uri $uri/ /index.html;
}
6. 项目扩展建议
- 添加消息通知功能:使用WebSocket实现实习申请状态变更实时通知
- 集成第三方登录:支持微信、QQ等社交账号登录
- 增加数据统计模块:使用ECharts展示实习数据统计图表
- 实现导出功能:支持将实习数据导出为Excel或PDF
- 添加多语言支持:使用vue-i18n实现国际化
这个实习管理系统项目涵盖了从需求分析、数据库设计、前后端开发到部署上线的完整流程,是学习现代Web开发的绝佳案例。我在指导学生完成这类项目时发现,最大的挑战不在于技术实现,而在于如何将业务需求转化为技术方案。建议初学者先理清业务流程,画出数据流程图,再着手编码,这样可以事半功倍。
