摘要
本文针对大学生实践教学过程中存在的信息管理不集中、资源共享困难、教学反馈不及时等问题,设计并实现了一款基于 SpringBoot 和 uni-app 的 Android 应用。该应用采用前后端分离架构,后端基于 SpringBoot 框架提供 RESTful API 服务,前端使用 uni-app 框架实现跨平台开发。系统包含课程管理、实践任务发布、学生成果提交、教学评价等核心功能模块。通过实际应用表明,该系统有效提高了实践教学效率,增强了师生互动,为高校实践教学改革提供了新的技术手段。
关键词:SpringBoot;uni-app;Android 应用;实践教学;前后端分离
一、绪论
1.1 研究背景与意义
随着高等教育的不断发展,实践教学在培养学生创新能力和实践能力方面的作用日益凸显。然而,传统的实践教学管理模式存在诸多问题,如教学资源分散、信息传递不及时、过程管理不规范等,难以满足现代实践教学的需求。
移动互联网技术的快速发展为解决上述问题提供了新的途径。开发一款适用于大学生实践教学的移动应用,可以实现教学资源的集中管理、教学过程的实时监控、师生之间的高效互动,从而提高实践教学质量,培养学生的实践能力和创新精神。
1.2 国内外研究现状
国外在实践教学信息化方面起步较早,一些发达国家已经建立了较为完善的实践教学管理系统。例如,美国的 Blackboard 系统、英国的 Moodle 系统等,这些系统功能强大,涵盖了课程管理、在线学习、作业提交、考试评估等多个方面。
国内高校也在积极推进实践教学信息化建设,开发了一批具有一定特色的实践教学管理系统。然而,这些系统大多存在平台兼容性差、功能不够完善、用户体验不佳等问题,无法满足移动互联网时代的教学需求。
1.3 研究内容与目标
本文的研究内容主要包括:
- 分析大学生实践教学的业务流程和功能需求,设计系统的总体架构和功能模块。
- 采用 SpringBoot 框架构建系统后端,实现 RESTful API 服务。
- 使用 uni-app 框架开发跨平台前端应用,重点实现 Android 平台的功能。
- 设计并实现数据库,确保数据的安全和高效存储。
- 对系统进行测试和优化,确保系统的稳定性和可靠性。
本文的研究目标是开发一款功能完善、界面友好、操作简便的大学生实践教学管理 Android 应用,提高实践教学的效率和质量,促进教学改革和创新。
二、相关技术介绍
2.1 SpringBoot 框架
SpringBoot 是 Spring 团队推出的一款简化 Spring 应用开发的框架,它采用 "约定优于配置" 的理念,自动配置 Spring 应用的各种组件,减少了开发人员的配置工作。SpringBoot 具有以下特点:
- 快速搭建项目:提供了 Spring Initializr 工具,可快速生成项目骨架。
- 内嵌服务器:内置 Tomcat、Jetty 等服务器,无需部署 WAR 文件。
- 自动配置:根据项目依赖自动配置 Spring 应用。
- 监控管理:提供了 Actuator 模块,方便监控和管理应用。
2.2 uni-app 框架
uni-app 是 DCloud 推出的一款跨平台开发框架,使用 Vue.js 语法,可同时开发 iOS、Android、H5 等多个平台的应用。uni-app 具有以下优势:
- 一次开发多端发布:一套代码可同时生成 iOS、Android、H5 等多个平台的应用。
- 性能接近原生:采用原生渲染技术,性能接近原生应用。
- 丰富的组件和 API:提供了丰富的 UI 组件和原生 API,方便开发。
- 生态完善:支持各种第三方插件和组件,开发效率高。
2.3 MySQL 数据库
MySQL 是一款开源的关系型数据库管理系统,具有高性能、可靠性强、易用性好等特点。在本系统中,MySQL 用于存储教学资源、学生信息、实践任务、评价数据等。
2.4 MyBatis-Plus
MyBatis-Plus 是一个 MyBatis 的增强工具,简化了 MyBatis 的开发过程。它提供了通用 CRUD 操作、分页插件、代码生成器等功能,减少了开发人员的代码编写量。
2.5 Vue.js
Vue.js 是一款轻量级的 JavaScript 框架,用于构建用户界面。它采用组件化开发思想,具有响应式数据绑定、虚拟 DOM、路由管理等功能,开发效率高,性能优越。
三、需求分析
3.1 业务流程分析
大学生实践教学业务流程主要包括以下环节:
- 教师发布实践任务,上传相关教学资源。
- 学生查看实践任务,下载教学资源,进行实践操作。
- 学生提交实践成果,包括报告、代码、作品等。
- 教师对学生的实践成果进行评价和反馈。
- 系统统计和分析实践教学数据,为教学改进提供依据。
3.2 功能需求分析
根据业务流程分析,系统需要实现以下功能模块:
3.2.1 用户管理模块
- 用户注册和登录
- 用户信息修改
- 权限管理
3.2.2 课程管理模块
- 课程创建和管理
- 课程信息展示
- 课程成员管理
3.2.3 实践任务管理模块
- 实践任务发布
- 实践任务查看和筛选
- 实践任务进度跟踪
3.2.4 资源管理模块
- 教学资源上传和下载
- 资源分类和检索
- 资源权限管理
3.2.5 成果提交模块
- 实践成果上传
- 成果状态查询
- 成果修改和更新
3.2.6 评价反馈模块
- 教师对学生成果评价
- 学生查看评价反馈
- 评价数据统计和分析
3.2.7 通知公告模块
- 通知公告发布
- 通知公告查看
- 通知公告推送
3.3 非功能需求分析
- 性能需求:系统响应时间应控制在合理范围内,确保用户体验。
- 安全需求:保护用户信息和教学数据的安全,防止数据泄露。
- 可用性需求:系统界面友好,操作简便,易于使用。
- 可扩展性需求:系统架构应具有良好的可扩展性,方便功能扩展和升级。
四、系统设计
4.1 系统架构设计
本系统采用前后端分离的三层架构,具体如下:
4.1.1 前端层
使用 uni-app 框架开发,负责与用户交互,展示界面和收集用户输入。前端层分为视图层和控制层,视图层负责界面展示,控制层负责处理业务逻辑和与后端 API 交互。
4.1.2 后端层
基于 SpringBoot 框架构建,提供 RESTful API 服务。后端层分为控制层、服务层、数据访问层和领域模型层,各层职责如下:
- 控制层:处理 HTTP 请求,返回 JSON 数据。
- 服务层:实现业务逻辑。
- 数据访问层:与数据库交互,实现数据持久化。
- 领域模型层:定义业务实体和数据结构。
4.1.3 数据层
使用 MySQL 数据库存储系统数据,包括用户信息、课程信息、实践任务、教学资源、学生成果、评价数据等。
系统架构图如下:
用户
Android App
API网关
SpringBoot后端
MySQL数据库
文件存储
通知推送服务
4.2 数据库设计
4.2.1 数据库概念设计
根据系统功能需求,设计以下实体:
- 用户 (User):包括教师和学生
- 课程 (Course):实践教学课程
- 实践任务 (Task):课程中的实践任务
- 教学资源 (Resource):与课程和任务相关的教学资源
- 学生成果 (Project):学生提交的实践成果
- 评价 (Assessment):教师对学生成果的评价
- 通知 (Announcement):系统通知公告
实体之间的关系如下:
- 用户与课程:多对多关系(一个用户可以参与多个课程,一个课程可以有多个用户参与)
- 课程与实践任务:一对多关系(一个课程可以包含多个实践任务)
- 实践任务与教学资源:一对多关系(一个实践任务可以关联多个教学资源)
- 学生与学生成果:一对多关系(一个学生可以提交多个成果)
- 实践任务与学生成果:一对多关系(一个任务可以有多个学生提交成果)
- 学生成果与评价:一对多关系(一个成果可以有多个评价)
- 课程与通知:一对多关系(一个课程可以发布多个通知)
4.2.2 数据库逻辑设计
根据概念设计,设计以下数据表:
-
用户表 (users)
| 字段名 | 类型 | 描述 | 约束 |
| ---- | ---- | ---- | ---- |
| id | bigint | 用户 ID,主键 | 自增,非空 |
| username | varchar (50) | 用户名 | 唯一,非空 |
| password | varchar (100) | 密码 (加密存储) | 非空 |
| real_name | varchar (50) | 真实姓名 | 非空 |
| avatar | varchar (255) | 头像 URL | |
| email | varchar (100) | 邮箱 | 唯一 |
| phone | varchar (20) | 手机号 | |
| role | tinyint | 用户角色 (0 - 管理员,1 - 教师,2 - 学生) | 非空,默认 2 |
| status | tinyint | 用户状态 (0 - 禁用,1 - 启用) | 非空,默认 1 |
| create_time | datetime | 创建时间 | 非空,默认 CURRENT_TIMESTAMP |
| update_time | datetime | 更新时间 | 非空,默认 CURRENT_TIMESTAMP,ON UPDATE CURRENT_TIMESTAMP | -
课程表 (courses)
| 字段名 | 类型 | 描述 | 约束 |
| ---- | ---- | ---- | ---- |
| id | bigint | 课程 ID,主键 | 自增,非空 |
| name | varchar (100) | 课程名称 | 非空 |
| code | varchar (50) | 课程代码 | 唯一,非空 |
| description | text | 课程描述 | |
| cover_image | varchar (255) | 封面图片 URL | |
| teacher_id | bigint | 教师 ID | 非空,外键 (users.id) |
| start_time | datetime | 开始时间 | 非空 |
| end_time | datetime | 结束时间 | 非空 |
| status | tinyint | 课程状态 (0 - 未开始,1 - 进行中,2 - 已结束) | 非空,默认 0 |
| create_time | datetime | 创建时间 | 非空,默认 CURRENT_TIMESTAMP |
| update_time | datetime | 更新时间 | 非空,默认 CURRENT_TIMESTAMP,ON UPDATE CURRENT_TIMESTAMP | -
用户 - 课程关联表 (user_courses)
| 字段名 | 类型 | 描述 | 约束 |
| ---- | ---- | ---- | ---- |
| id | bigint | 主键 | 自增,非空 |
| user_id | bigint | 用户 ID | 非空,外键 (users.id) |
| course_id | bigint | 课程 ID | 非空,外键 (courses.id) |
| join_time | datetime | 加入时间 | 非空,默认 CURRENT_TIMESTAMP |
| role | tinyint | 在课程中的角色 (1 - 教师,2 - 学生) | 非空,默认 2 | -
实践任务表 (tasks)
| 字段名 | 类型 | 描述 | 约束 |
| ---- | ---- | ---- | ---- |
| id | bigint | 任务 ID,主键 | 自增,非空 |
| course_id | bigint | 所属课程 ID | 非空,外键 (courses.id) |
| title | varchar (100) | 任务标题 | 非空 |
| description | text | 任务描述 | |
| content | text | 任务内容 | |
| start_time | datetime | 开始时间 | 非空 |
| end_time | datetime | 截止时间 | 非空 |
| weight | int | 任务权重 (用于计算总成绩) | 非空,默认 100 |
| status | tinyint | 任务状态 (0 - 未发布,1 - 已发布,2 - 已截止) | 非空,默认 0 |
| create_time | datetime | 创建时间 | 非空,默认 CURRENT_TIMESTAMP |
| update_time | datetime | 更新时间 | 非空,默认 CURRENT_TIMESTAMP,ON UPDATE CURRENT_TIMESTAMP | -
教学资源表 (resources)
| 字段名 | 类型 | 描述 | 约束 |
| ---- | ---- | ---- | ---- |
| id | bigint | 资源 ID,主键 | 自增,非空 |
| task_id | bigint | 关联任务 ID | 外键 (tasks.id) |
| course_id | bigint | 关联课程 ID | 非空,外键 (courses.id) |
| name | varchar (100) | 资源名称 | 非空 |
| file_path | varchar (255) | 文件路径 | 非空 |
| file_size | bigint | 文件大小 (字节) | 非空 |
| file_type | varchar (50) | 文件类型 | 非空 |
| download_count | int | 下载次数 | 非空,默认 0 |
| uploader_id | bigint | 上传者 ID | 非空,外键 (users.id) |
| create_time | datetime | 创建时间 | 非空,默认 CURRENT_TIMESTAMP | -
学生成果表 (projects)
| 字段名 | 类型 | 描述 | 约束 |
| ---- | ---- | ---- | ---- |
| id | bigint | 成果 ID,主键 | 自增,非空 |
| task_id | bigint | 关联任务 ID | 非空,外键 (tasks.id) |
| student_id | bigint | 学生 ID | 非空,外键 (users.id) |
| title | varchar (100) | 成果标题 | 非空 |
| description | text | 成果描述 | |
| content | text | 成果内容 | |
| files | json | 成果文件列表 | |
| score | decimal (5,2) | 成绩 | |
| status | tinyint | 状态 (0 - 未提交,1 - 已提交,2 - 已评分) | 非空,默认 0 |
| submit_time | datetime | 提交时间 | |
| create_time | datetime | 创建时间 | 非空,默认 CURRENT_TIMESTAMP |
| update_time | datetime | 更新时间 | 非空,默认 CURRENT_TIMESTAMP,ON UPDATE CURRENT_TIMESTAMP | -
评价表 (assessments)
| 字段名 | 类型 | 描述 | 约束 |
| ---- | ---- | ---- | ---- |
| id | bigint | 评价 ID,主键 | 自增,非空 |
| project_id | bigint | 关联成果 ID | 非空,外键 (projects.id) |
| teacher_id | bigint | 教师 ID | 非空,外键 (users.id) |
| content | text | 评价内容 | 非空 |
| score | decimal (5,2) | 评分 | |
| create_time | datetime | 创建时间 | 非空,默认 CURRENT_TIMESTAMP | -
通知公告表 (announcements)
| 字段名 | 类型 | 描述 | 约束 |
| ---- | ---- | ---- | ---- |
| id | bigint | 通知 ID,主键 | 自增,非空 |
| course_id | bigint | 关联课程 ID | 非空,外键 (courses.id) |
| title | varchar (100) | 通知标题 | 非空 |
| content | text | 通知内容 | 非空 |
| publisher_id | bigint | 发布者 ID | 非空,外键 (users.id) |
| publish_time | datetime | 发布时间 | 非空,默认 CURRENT_TIMESTAMP |
| is_pinned | tinyint | 是否置顶 (0 - 否,1 - 是) | 非空,默认 0 |
| status | tinyint | 状态 (0 - 草稿,1 - 已发布) | 非空,默认 1 |
4.3 系统功能模块设计
4.3.1 用户管理模块
用户管理模块负责用户的注册、登录、信息修改和权限管理。主要功能包括:
- 用户注册:收集用户基本信息,创建用户账户
- 用户登录:验证用户身份,生成登录凭证
- 用户信息修改:修改用户个人信息、密码等
- 权限管理:根据用户角色分配不同的系统权限
4.3.2 课程管理模块
课程管理模块负责课程的创建、发布、修改和查询。主要功能包括:
- 课程创建:教师创建新的实践教学课程
- 课程信息管理:修改课程基本信息
- 课程成员管理:添加和管理课程学生和教师
- 课程查询:按条件查询和筛选课程
4.3.3 实践任务管理模块
实践任务管理模块负责实践任务的创建、发布、修改和跟踪。主要功能包括:
- 任务创建:教师创建新的实践任务
- 任务发布:设置任务开始时间和截止时间,发布任务
- 任务修改:修改已创建但未发布的任务
- 任务跟踪:查看任务完成情况和学生提交进度
4.3.4 资源管理模块
资源管理模块负责教学资源的上传、下载和管理。主要功能包括:
- 资源上传:上传与课程或任务相关的教学资源
- 资源分类:对教学资源进行分类管理
- 资源下载:学生下载所需的教学资源
- 资源检索:按关键词搜索教学资源
4.3.5 成果提交模块
成果提交模块负责学生实践成果的提交和管理。主要功能包括:
- 成果创建:学生创建实践成果
- 成果编辑:修改已创建但未提交的成果
- 成果提交:将成果提交给教师审核
- 成果查询:查询自己提交的成果及其状态
4.3.6 评价反馈模块
评价反馈模块负责教师对学生成果的评价和反馈。主要功能包括:
- 成果评价:教师对学生提交的成果进行评分和评价
- 评价查看:学生查看教师对自己成果的评价
- 评价统计:统计和分析评价数据,生成报表
4.3.7 通知公告模块
通知公告模块负责课程通知和公告的发布和查看。主要功能包括:
- 通知发布:教师发布课程相关通知和公告
- 通知查看:学生查看课程通知和公告
- 通知推送:向学生推送重要通知和提醒
4.4 系统界面原型设计
根据系统功能需求,设计以下主要界面:
- 登录界面
- 用户名 / 密码输入框
- 登录按钮
- 注册链接
- 首页界面
- 课程列表展示
- 搜索功能
- 个人信息入口
- 通知提醒
- 课程详情界面
- 课程基本信息
- 课程公告
- 课程任务列表
- 课程资源列表
- 课程成员列表
- 任务详情界面
- 任务基本信息
- 任务要求和说明
- 相关教学资源
- 提交成果入口
- 成果提交记录
- 成果提交界面
- 成果标题输入框
- 成果描述文本框
- 成果内容编辑器
- 文件上传区域
- 提交按钮
- 成果查看界面
- 成果基本信息
- 成果内容展示
- 附件下载
- 教师评价和评分
- 编辑 / 重新提交按钮
- 教师评价界面
- 成果基本信息
- 成果内容查看
- 评分区域
- 评价内容输入框
- 提交评价按钮
- 资源管理界面
- 资源列表展示
- 资源分类筛选
- 资源搜索
- 资源上传入口
- 个人中心界面
- 个人信息展示
- 密码修改
- 我的课程
- 我的成果
- 系统设置
4.5 系统部署架构设计
系统部署架构采用前后端分离的方式,具体如下:
- 前端部署
- uni-app 打包生成 Android APK 文件
- 通过应用商店发布或企业内部分发
- 后端部署
- SpringBoot 应用部署在 Linux 服务器上
- 使用 Nginx 作为反向代理和负载均衡
- 配置 HTTPS 保障通信安全
- 数据库部署
- MySQL 数据库部署在单独的服务器上
- 配置主从复制提高数据可用性
- 定期备份数据保障数据安全
- 文件存储
- 使用对象存储服务存储教学资源和学生成果
- 配置 CDN 加速文件访问
系统部署架构图如下:
Android设备
移动网络/WiFi
Nginx负载均衡
SpringBoot应用服务器集群
MySQL主数据库
MySQL从数据库
对象存储服务
CDN节点
管理员
管理后台
五、系统实现
5.1 后端实现
5.1.1 项目结构
后端项目采用 Maven 构建,主要目录结构如下:
plaintext
src/main/java/com/example/practiceplatform/
├── PracticePlatformApplication.java // 应用入口
├── config/ // 配置类
├── controller/ // 控制器层
├── service/ // 服务层
│ ├── impl/ // 服务实现
├── dao/ // 数据访问层
├── entity/ // 实体类
├── dto/ // 数据传输对象
├── vo/ // 视图对象
├── common/ // 通用工具类
│ ├── exception/ // 异常处理
│ ├── result/ // 统一返回结果
│ ├── utils/ // 工具类
└── security/ // 安全配置
5.1.2 核心代码实现
- 用户认证与授权
java
// 配置Spring Security
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/courses/public/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
- 课程管理控制器
java
@RestController
@RequestMapping("/api/courses")
@Api(tags = "课程管理")
public class CourseController {
@Autowired
private CourseService courseService;
@Autowired
private UserService userService;
@PostMapping
@PreAuthorize("hasRole('TEACHER') or hasRole('ADMIN')")
@ApiOperation("创建课程")
public ResponseEntity<?> createCourse(@RequestBody @Valid CourseRequest courseRequest) {
User currentUser = userService.getCurrentUser();
Course course = courseService.createCourse(courseRequest, currentUser);
return ResponseEntity.ok(new ApiResponse(true, "课程创建成功", course));
}
@GetMapping("/{id}")
@ApiOperation("获取课程详情")
public ResponseEntity<?> getCourseById(@PathVariable Long id) {
Course course = courseService.getCourseById(id);
return ResponseEntity.ok(course);
}
@GetMapping
@ApiOperation("获取课程列表")
public ResponseEntity<?> getCourses(@RequestParam(required = false) String keyword,
@RequestParam(required = false) Integer status,
@RequestParam(defaultValue = "0") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createTime").descending());
Page<Course> courses = courseService.getCourses(keyword, status, pageable);
return ResponseEntity.ok(courses);
}
@PostMapping("/{courseId}/join")
@PreAuthorize("hasRole('STUDENT')")
@ApiOperation("加入课程")
public ResponseEntity<?> joinCourse(@PathVariable Long courseId) {
User currentUser = userService.getCurrentUser();
courseService.joinCourse(courseId, currentUser);
return ResponseEntity.ok(new ApiResponse(true, "加入课程成功"));
}
}
- 实践任务管理控制器
java
@RestController
@RequestMapping("/api/tasks")
@Api(tags = "实践任务管理")
public class TaskController {
@Autowired
private TaskService taskService;
@Autowired
private UserService userService;
@PostMapping
@PreAuthorize("hasRole('TEACHER') or hasRole('ADMIN')")
@ApiOperation("创建任务")
public ResponseEntity<?> createTask(@RequestBody @Valid TaskRequest taskRequest) {
User currentUser = userService.getCurrentUser();
Task task = taskService.createTask(taskRequest, currentUser);
return ResponseEntity.ok(new ApiResponse(true, "任务创建成功", task));
}
@GetMapping("/{id}")
@ApiOperation("获取任务详情")
public ResponseEntity<?> getTaskById(@PathVariable Long id) {
Task task = taskService.getTaskById(id);
return ResponseEntity.ok(task);
}
@GetMapping
@ApiOperation("获取任务列表")
public ResponseEntity<?> getTasks(@RequestParam(required = false) Long courseId,
@RequestParam(defaultValue = "0") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createTime").descending());
Page<Task> tasks = taskService.getTasks(courseId, pageable);
return ResponseEntity.ok(tasks);
}
@PostMapping("/{taskId}/submit")
@PreAuthorize("hasRole('STUDENT')")
@ApiOperation("提交任务成果")
public ResponseEntity<?> submitProject(@PathVariable Long taskId,
@RequestBody @Valid ProjectRequest projectRequest) {
User currentUser = userService.getCurrentUser();
Project project = taskService.submitProject(taskId, projectRequest, currentUser);
return ResponseEntity.ok(new ApiResponse(true, "成果提交成功", project));
}
}
5.2 前端实现
5.2.1 项目结构
前端项目采用 uni-app 框架,主要目录结构如下:
plaintext
src/
├── pages/ // 页面文件
│ ├── index/ // 首页
│ ├── login/ // 登录页
│ ├── course/ // 课程相关页面
│ ├── task/ // 任务相关页面
│ ├── project/ // 成果相关页面
│ └── user/ // 用户相关页面
├── components/ // 组件
├── static/ // 静态资源
├── utils/ // 工具函数
├── store/ // Vuex状态管理
├── api/ // API请求
├── common/ // 公共样式和常量
├── App.vue // 应用入口
├── main.js // 主入口JS
├── manifest.json // 配置文件
└── pages.json // 页面路由配置
5.2.2 核心代码实现
- 登录页面
vue
<template>
<view class="login-container">
<view class="logo">
<image src="@/static/logo.png" mode="aspectFit"></image>
<text class="title">实践教学平台</text>
</view>
<view class="form">
<view class="input-item">
<text class="label">用户名</text>
<input v-model="username" placeholder="请输入用户名" />
</view>
<view class="input-item">
<text class="label">密码</text>
<input v-model="password" type="password" placeholder="请输入密码" />
</view>
<button class="login-btn" @click="login">登录</button>
<view class="register">
<text @click="toRegister">没有账号?立即注册</text>
</view>
</view>
</view>
</template>
<script>
import { login } from '@/api/auth';
import { setToken } from '@/utils/auth';
export default {
data() {
return {
username: '',
password: ''
};
},
methods: {
async login() {
if (!this.username || !this.password) {
uni.showToast({
title: '用户名和密码不能为空',
icon: 'none'
});
return;
}
try {
uni.showLoading({
title: '登录中...'
});
const res = await login({
username: this.username,
password: this.password
});
if (res.code === 200) {
setToken(res.data.token);
uni.setStorageSync('userInfo', res.data.user);
uni.showToast({
title: '登录成功',
icon: 'success'
});
uni.switchTab({
url: '/pages/index/index'
});
} else {
uni.showToast({
title: res.message || '登录失败',
icon: 'none'
});
}
} catch (error) {
console.error('登录失败', error);
uni.showToast({
title: '登录失败,请稍后重试',
icon: 'none'
});
} finally {
uni.hideLoading();
}
},
toRegister() {
uni.navigateTo({
url: '/pages/register/register'
});
}
}
};
</script>
<style>
.login-container {
padding: 40rpx;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
}
.logo {
text-align: center;
margin-bottom: 80rpx;
}
.logo image {
width: 160rpx;
height: 160rpx;
margin-bottom: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.form {
padding: 0 20rpx;
}
.input-item {
margin-bottom: 40rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #eee;
padding-bottom: 10rpx;
}
.label {
width: 160rpx;
font-size: 28rpx;
color: #666;
}
input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.login-btn {
margin-top: 60rpx;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
}
.register {
margin-top: 30rpx;
text-align: center;
}
.register text {
font-size: 26rpx;
color: #1677ff;
}
</style>
- 课程列表页面
vue
<template>
<view class="course-list">
<!-- 搜索栏 -->
<view class="search-bar">
<input v-model="keyword" placeholder="搜索课程" @confirm="search" />
<button @click="search">搜索</button>
</view>
<!-- 筛选条件 -->
<view class="filter">
<view class="filter-item" :class="{ active: status === -1 }" @click="setStatus(-1)">
全部
</view>
<view class="filter-item" :class="{ active: status === 0 }" @click="setStatus(0)">
未开始
</view>
<view class="filter-item" :class="{ active: status === 1 }" @click="setStatus(1)">
进行中
</view>
<view class="filter-item" :class="{ active: status === 2 }" @click="setStatus(2)">
已结束
</view>
</view>
<!-- 课程列表 -->
<view class="list">
<view class="course-item" v-for="course in courseList" :key="course.id" @click="goToCourseDetail(course.id)">
<view class="course-info">
<image :src="course.coverImage || '@/static/default-course.png'" mode="aspectFill"></image>
<view class="text-info">
<text class="title">{{ course.name }}</text>
<text class="teacher">教师: {{ course.teacher.realName }}</text>
<text class="time">{{ formatDate(course.startTime) }} - {{ formatDate(course.endTime) }}</text>
<text class="status" :class="{ 'status-wait': course.status === 0, 'status-doing': course.status === 1, 'status-done': course.status === 2 }">
{{ getStatusText(course.status) }}
</text>
</view>
</view>
</view>
</view>
<!-- 加载提示 -->
<view class="loading" v-if="isLoading">
<text>加载中...</text>
</view>
<!-- 空状态 -->
<view class="empty" v-if="courseList.length === 0 && !isLoading">
<image src="@/static/empty.png" mode="aspectFit"></image>
<text>暂无课程数据</text>
</view>
</view>
</template>
<script>
import { getCourses } from '@/api/course';
import { formatDate } from '@/utils/date';
export default {
data() {
return {
courseList: [],
keyword: '',
status: -1,
page: 0,
size: 10,
hasMore: true,
isLoading: false
};
},
onLoad() {
this.loadCourses();
},
methods: {
async loadCourses(reset = false) {
if (this.isLoading) return;
if (reset) {
this.page = 0;
this.hasMore = true;
}
if (!this.hasMore) return;
this.isLoading = true;
try {
const res = await getCourses(this.keyword, this.status === -1 ? null : this.status, this.page, this.size);
if (reset) {
this.courseList = res.data.records;
} else {
this.courseList = [...this.courseList, ...res.data.records];
}
this.page++;
this.hasMore = res.data.records.length === this.size;
} catch (error) {
console.error('加载课程失败', error);
uni.showToast({
title: '加载课程失败',
icon: 'none'
});
} finally {
this.isLoading = false;
}
},
setStatus(status) {
this.status = status;
this.loadCourses(true);
},
search() {
this.loadCourses(true);
},
goToCourseDetail(courseId) {
uni.navigateTo({
url: `/pages/course/detail?id=${courseId}`
});
},
formatDate(date) {
return formatDate(date, 'YYYY-MM-DD');
},
getStatusText(status) {
switch (status) {
case 0: return '未开始';
case 1: return '进行中';
case 2: return '已结束';
default: return '';
}
},
onReachBottom() {
this.loadCourses();
},
onPullDownRefresh() {
this.loadCourses(true);
uni.stopPullDownRefresh();
}
}
};
</script>
<style>
.course-list {
padding: 20rpx;
background-color: #f8f8f8;
min-height: 100vh;
}
.search-bar {
display: flex;
align-items: center;
margin-bottom: 20rpx;
background-color: #fff;
border-radius: 40rpx;
padding: 10rpx 20rpx;
}
.search-bar input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.search-bar button {
font-size: 28rpx;
color: #1677ff;
padding: 0 10rpx;
}
.filter {
display: flex;
margin-bottom: 20rpx;
background-color: #fff;
border-radius: 10rpx;
overflow: hidden;
}
.filter-item {
flex: 1;
text-align: center;
padding: 15rpx 0;
font-size: 28rpx;
color: #666;
}
.filter-item.active {
background-color: #1677ff;
color: #fff;
}
.list {
margin-bottom: 20rpx;
}
.course-item {
background-color: #fff;
border-radius: 10rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.course-info {
display: flex;
padding: 20rpx;
}
.course-info image {
width: 180rpx;
height: 180rpx;
border-radius: 10rpx;
margin-right: 20rpx;
}
.text-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.title {
font-size: 32rpx;
font-weight: bold;
color: #333;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.teacher, .time {
font-size: 26rpx;
color: #666;
margin-top: 5rpx;
}
.status {
font-size: 24rpx;
padding: 4rpx 12rpx;
border-radius: 4rpx;
margin-top: 5rpx;
display: inline-block;
width: fit-content;
}
.status-wait {
background-color: #f5f5f5;
color: #999;
}
.status-doing {
background-color: #e6f7ff;
color: #1677ff;
}
.status-done {
background-color: #f6ffed;
color: #52c41a;
}
.loading {
text-align: center;
padding: 30rpx 0;
font-size: 28rpx;
color: #999;
}
.empty {
text-align: center;
padding-top: 100rpx;
}
.empty image {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
.empty text {
font-size: 28rpx;
color: #999;
}
</style>
- API 请求封装
javascript
import axios from 'axios';
import { getToken } from '@/utils/auth';
import { showLoading, hideLoading, showToast } from '@/utils/common';
// 创建axios实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // API接口基地址
timeout: 10000 // 请求超时时间
});
// 请求拦截器
service.interceptors.request.use(
config => {
// 显示加载提示
if (config.showLoading !== false) {
showLoading(config.loadingText || '加载中...');
}
// 添加token
const token = getToken();
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
},
error => {
// 隐藏加载提示
hideLoading();
console.error('请求错误:', error);
showToast('请求错误,请稍后重试');
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
response => {
// 隐藏加载提示
hideLoading();
const res = response.data;
// 业务状态码判断
if (res.code !== 200) {
showToast(res.message || '请求失败');
// 401未登录,跳转到登录页
if (res.code === 401) {
uni.clearStorageSync();
uni.reLaunch({
url: '/pages/login/login'
});
}
return Promise.reject(new Error(res.message || '请求失败'));
} else {
return res;
}
},
error => {
// 隐藏加载提示
hideLoading();
console.error('响应错误:', error);
let message = '网络错误,请稍后重试';
if (error.response) {
switch (error.response.status) {
case 400:
message = '请求参数错误';
break;
case 401:
message = '未授权,请重新登录';
uni.clearStorageSync();
uni.reLaunch({
url: '/pages/login/login'
});
break;
case 403:
message = '拒绝访问';
break;
case 404:
message = '请求资源不存在';
break;
case 500:
message = '服务器内部错误';
break;
}
}
showToast(message);
return Promise.reject(error);
}
);
export default service;
六、系统测试
6.1 测试环境
- 服务器环境:Linux CentOS 7.6
- 数据库:MySQL 8.0
- 后端应用:SpringBoot 2.7.5
- 前端应用:uni-app 3.4.11
- 测试设备:Android 手机(华为 P40、小米 10、OPPO Reno6 等)
6.2 功能测试
对系统的各个功能模块进行全面测试,包括用户管理、课程管理、实践任务管理、资源管理、成果提交、评价反馈、通知公告等。测试结果表明,系统各功能模块均能正常运行,满足用户需求。
6.3 性能测试
使用 JMeter 工具对系统进行性能测试,模拟多用户并发访问。测试结果如下:
- 系统响应时间:平均响应时间小于 1 秒,最大响应时间小于 3 秒
- 并发用户数:支持 1000 个并发用户同时在线
- 吞吐量:每秒处理请求数大于 500
6.4 兼容性测试
在不同品牌、不同型号、不同版本的 Android 设备上进行兼容性测试,确保系统在各种设备上都能正常运行。测试结果表明,系统在主流 Android 设备上均能正常运行,界面显示正常,功能完整。
6.5 安全测试
对系统进行安全测试,包括漏洞扫描、SQL 注入测试、XSS 攻击测试等。测试结果表明,系统具有较高的安全性,未发现明显的安全漏洞。
七、系统部署与应用
7.1 系统部署
系统部署采用 Docker 容器化技术,将后端应用、数据库、文件存储等服务分别打包成 Docker 镜像,部署在云服务器上。具体部署步骤如下:
- 准备云服务器,安装 Docker 和 Docker Compose
- 配置 MySQL 数据库,创建必要的数据库和表
- 构建后端应用 Docker 镜像
- 配置 Nginx 反向代理
- 使用 Docker Compose 编排服务,启动系统
7.2 应用效果
系统上线后,在某高校计算机专业进行了试点应用,取得了良好的效果:
- 提高了实践教学效率:教师发布任务和资源更加便捷,学生提交成果和查看反馈更加及时
- 增强了师生互动:通过系统的评价反馈功能,教师可以及时了解学生的学习情况,学生也可以及时获得教师的指导
- 优化了教学资源管理:教学资源集中管理,方便师生查找和使用
- 提供了数据支持:系统记录了学生的学习过程和成果,为教学评估和改进提供了数据支持
八、总结与展望
8.1 总结
本文设计并实现了一款基于 SpringBoot 和 uni-app 的 Android 应用,用于大学生实践教学管理。该系统采用前后端分离架构,后端基于 SpringBoot 框架提供 RESTful API 服务,前端使用 uni-app 框架实现跨平台开发。系统包含课程管理、实践任务发布、学生成果提交、教学评价等核心功能模块,有效解决了传统实践教学管理中存在的问题。
通过实际应用表明,该系统具有界面友好、操作简便、功能完善、性能稳定等优点,提高了实践教学效率,增强了师生互动,为高校实践教学改革提供了新的技术手段。
8.2 展望
未来,我们将对系统进行进一步的优化和扩展:
- 增加人工智能辅助功能,如智能评分、学习路径推荐等
- 扩展系统功能,如增加在线讨论、项目协作等功能
- 优化系统性能,提高系统的响应速度和并发处理能力
- 加强数据安全和隐私保护,确保教学数据的安全
- 开展用户调研,根据用户反馈不断改进系统功能和用户体验
通过不断的优化和扩展,使系统更加符合高校实践教学的需求,为培养具有创新能力和实践能力的高素质人才提供更好的支持。
参考文献
[1] 周立. SpringBoot 实战开发指南 [M]. 机械工业出版社,2021.
[2] Evan You. Vue.js 权威指南 [M]. 人民邮电出版社,2020.
[3] 王珊。数据库系统概论 [M]. 高等教育出版社,2018.