基于Spring Boot+ Vue的试听课管理系统的设计与实现

发布时间:2026/8/16 3:15:21
基于Spring Boot+ Vue的试听课管理系统的设计与实现 一、 项目背景与意义随着在线教育的蓬勃发展各类培训机构、学校及知识付费平台对试听课体验课的运营管理需求日益增长。试听课作为潜在学员了解课程质量、体验教学风格、建立信任的关键环节其管理效率直接影响招生转化率。传统的人工排课、手动记录、电话或即时通讯工具沟通的方式存在信息分散、流程繁琐、数据难以追踪分析等问题。因此设计并实现一个基于Spring Boot Vue的试听课管理系统具有重要的现实意义提升运营效率将试听课的预约、排课、提醒、签到、反馈收集等流程线上化、自动化减少人工操作降低出错率。优化用户体验为学员提供便捷的在线预约、课程提醒、在线听课入口提升试听体验和满意度。赋能数据决策系统化记录试听课的预约、出勤、转化等数据为课程优化、营销策略调整提供数据支持。技术实践价值Spring Boot Vue是当前企业级Web应用开发的主流技术栈之一该项目涵盖了前后端分离架构、RESTful API设计、数据库建模、权限控制等核心技能具有很高的学习和实践价值。二、 技术栈选型本项目采用前后端分离架构后端提供RESTful API前端负责页面渲染和用户交互。2.1 后端技术栈 (Spring Boot)核心框架Spring Boot 2.xWeb框架Spring MVC数据持久层Spring Data JPA (或 MyBatis-Plus)数据库MySQL 8.0缓存Redis (用于会话管理、验证码、热点数据缓存)安全框架Spring Security JWT (JSON Web Token)API文档Swagger2 / Knife4j构建工具Maven 或 Gradle其他Lombok (简化代码)Hutool (工具类库)Quartz (定时任务用于发送提醒)2.2 前端技术栈 (Vue)核心框架Vue 3 (Composition API) 或 Vue 2构建工具Vite 或 Vue CLIUI组件库Element Plus (Vue 3) 或 Element UI (Vue 2)状态管理Pinia (Vue 3) 或 Vuex (Vue 2)路由Vue RouterHTTP客户端Axios工具库Day.js (日期处理)Lodash (实用函数)2.3 开发与部署版本控制Git接口联调Postman / Apifox部署后端打包为Jar使用Docker容器化部署前端静态资源部署至Nginx。三、 系统核心功能模块设计系统主要分为后台管理端和学员端。3.1 后台管理端用户与权限管理管理员、课程顾问、讲师等角色管理基于RBAC的权限控制。课程管理创建、编辑、上架/下架试听课程设置课程信息标题、简介、封面、讲师、时长等。试听课排期管理为每门试听课程设置可预约的时间段日期、开始时间、结束时间、最大人数。预约管理查看所有预约记录支持按状态待确认、已确认、已取消、已完成筛选手动确认或取消预约。签到与反馈查看学员签到情况收集并查看学员试听后的反馈评价。数据统计可视化仪表盘展示预约趋势、出勤率、课程热度、转化率等关键指标。3.2 学员端 (Web/H5)课程浏览与搜索查看所有可试听的课程列表支持按分类、讲师筛选。在线预约选择心仪的课程和时间段填写个人信息姓名、手机号进行预约。我的预约查看个人预约记录及状态接收预约成功、上课提醒等通知。在线听课在预约时间通过系统提供的链接如腾讯会议、Zoom房间号进入线上教室。课后反馈试听结束后可对课程进行评分和填写文字反馈。四、 数据库核心表设计-- 用户表 (sys_user) CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, username varchar(50) NOT NULL COMMENT 用户名, password varchar(255) NOT NULL COMMENT 密码, nickname varchar(50) DEFAULT NULL COMMENT 昵称, phone varchar(20) DEFAULT NULL COMMENT 手机号, role varchar(20) NOT NULL COMMENT 角色 (ADMIN, CONSULTANT, LECTURER, STUDENT), status tinyint DEFAULT 1 COMMENT 状态 (0-禁用1-启用), create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 试听课程表 (trial_course) CREATE TABLE trial_course ( id bigint NOT NULL AUTO_INCREMENT, title varchar(200) NOT NULL COMMENT 课程标题, cover_image varchar(500) DEFAULT NULL COMMENT 封面图URL, description text COMMENT 课程描述, lecturer_id bigint DEFAULT NULL COMMENT 讲师ID, duration int DEFAULT NULL COMMENT 课程时长(分钟), category varchar(50) DEFAULT NULL COMMENT 分类, is_active tinyint DEFAULT 1 COMMENT 是否上架, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT试听课程表; -- 试听课排期表 (trial_schedule) CREATE TABLE trial_schedule ( id bigint NOT NULL AUTO_INCREMENT, course_id bigint NOT NULL COMMENT 课程ID, start_time datetime NOT NULL COMMENT 开始时间, end_time datetime NOT NULL COMMENT 结束时间, max_capacity int DEFAULT 50 COMMENT 最大预约人数, current_bookings int DEFAULT 0 COMMENT 当前已预约人数, online_room varchar(500) DEFAULT NULL COMMENT 线上会议室链接/ID, status tinyint DEFAULT 1 COMMENT 状态 (1-可预约 0-已满 -1-已取消), PRIMARY KEY (id), KEY idx_course_time (course_id, start_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT试听课排期表; -- 预约记录表 (booking_record) CREATE TABLE booking_record ( id bigint NOT NULL AUTO_INCREMENT, schedule_id bigint NOT NULL COMMENT 排期ID, user_id bigint NOT NULL COMMENT 学员用户ID, student_name varchar(50) NOT NULL COMMENT 学员姓名(预约时填写), student_phone varchar(20) NOT NULL COMMENT 学员手机号, status varchar(20) DEFAULT PENDING COMMENT 状态(PENDING, CONFIRMED, CANCELLED, COMPLETED), check_in_time datetime DEFAULT NULL COMMENT 签到时间, feedback_score tinyint DEFAULT NULL COMMENT 反馈评分(1-5), feedback_comment text COMMENT 反馈文字, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_schedule_user (schedule_id, user_id), KEY idx_user (user_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT预约记录表;五、 核心代码示例5.1 后端核心代码 (Spring Boot)1. 预约试听课接口 (BookingController.java)RestController RequestMapping(/api/booking) RequiredArgsConstructor public class BookingController { private final BookingService bookingService; /** 学员预约试听课 */ PostMapping public Resultlt;BookingRecordVOgt; createBooking(RequestBody Valid BookingRequest request) { // 获取当前登录学员ID (从JWT Token中) Long currentUserId SecurityUtil.getCurrentUserId(); BookingRecordVO booking bookingService.createBooking(currentUserId, request); return Result.success(booking); } /** 管理员确认/取消预约 */ PutMapping(/{bookingId}/status) PreAuthorize(hasRole(ADMIN) or hasRole(CONSULTANT)) public Resultlt;Voidgt; updateBookingStatus(PathVariable Long bookingId, RequestParam String status) { bookingService.updateBookingStatus(bookingId, status); return Result.success(); } }2. 预约业务逻辑层 (BookingServiceImpl.java)Service Slf4j RequiredArgsConstructor public class BookingServiceImpl implements BookingService { private final TrialScheduleRepository scheduleRepository; private final BookingRecordRepository bookingRepository; private final RedisTemplatelt;String, Stringgt; redisTemplate; Override Transactional(rollbackFor Exception.class) public BookingRecordVO createBooking(Long userId, BookingRequest request) { // 1. 校验排期是否存在且可预约 TrialSchedule schedule scheduleRepository.findById(request.getScheduleId()) .orElseThrow(() - new BusinessException(试听课排期不存在)); if (!ScheduleStatus.AVAILABLE.equals(schedule.getStatus())) { throw new BusinessException(该场次暂不可预约); } if (schedule.getCurrentBookings() schedule.getMaxCapacity()) { throw new BusinessException(该场次预约已满); } // 2. 防止重复预约 (同一用户同一场次) boolean exists bookingRepository.existsByScheduleIdAndUserId(request.getScheduleId(), userId); if (exists) { throw new BusinessException(您已预约过该场次); } // 3. 创建预约记录 BookingRecord record new BookingRecord(); record.setScheduleId(request.getScheduleId()); record.setUserId(userId); record.setStudentName(request.getStudentName()); record.setStudentPhone(request.getStudentPhone()); record.setStatus(BookingStatus.PENDING); bookingRepository.save(record); // 4. 更新排期已预约人数 (使用乐观锁) int updated scheduleRepository.incrementBookingCount(schedule.getId()); if (updated 0) { throw new ConcurrentBookingException(预约冲突请重试); } // 5. 发送预约成功通知 (异步) sendBookingNotification(record); return BookingConverter.INSTANCE.toVO(record); } private void sendBookingNotification(BookingRecord record) { // 实现短信、站内信或邮件通知逻辑可使用消息队列异步处理 log.info(预约成功通知已发送预约ID: {}, record.getId()); } }5.2 前端核心代码 (Vue 3 Element Plus)1. 试听课预约页面组件 (TrialBooking.vue)template div classtrial-booking el-card template #header div classcard-header span{{ course.title }} - 试听预约/span /div /template lt;!-- 课程基本信息 --gt; lt;div classcourse-infogt; lt;el-image :srccourse.coverImage fitcovergt;lt;/el-imagegt; lt;div classinfo-textgt; lt;h3gt;{{ course.title }}lt;/h3gt; lt;pgt;讲师{{ course.lecturerName }}lt;/pgt; lt;pgt;时长{{ course.duration }}分钟lt;/pgt; lt;pgt;{{ course.description }}lt;/pgt; lt;/divgt; lt;/divgt; lt;el-dividergt;lt;/el-dividergt; lt;!-- 可选时间表 --gt; lt;h4gt;可选时间lt;/h4gt; lt;div classschedule-listgt; lt;el-radio-group v-modelselectedScheduleIdgt; lt;el-radio v-forschedule in availableSchedules :keyschedule.id :labelschedule.id :disabledschedule.currentBookings gt; schedule.maxCapacity border gt; lt;div classschedule-itemgt; lt;divgt;{{ formatDate(schedule.startTime, MM-DD HH:mm) }} 开始lt;/divgt; lt;div classcapacitygt; 已预约{{ schedule.currentBookings }} / {{ schedule.maxCapacity }} lt;/divgt; lt;/divgt; lt;/el-radiogt; lt;/el-radio-groupgt; lt;/divgt; lt;el-dividergt;lt;/el-dividergt; lt;!-- 预约表单 --gt; lt;h4gt;填写预约信息lt;/h4gt; lt;el-form :modelform :rulesrules refformRef label-width80pxgt; lt;el-form-item label姓名 propstudentNamegt; lt;el-input v-modelform.studentName placeholder请输入您的姓名 /gt; lt;/el-form-itemgt; lt;el-form-item label手机号 propstudentPhonegt; lt;el-input v-modelform.studentPhone placeholder请输入手机号 /gt; lt;/el-form-itemgt; lt;el-form-itemgt; lt;el-button typeprimary clicksubmitBooking :loadingloadinggt; 立即预约 lt;/el-buttongt; lt;/el-form-itemgt; lt;/el-formgt; lt;/el-cardgt; /div /template script setup import { ref, onMounted } from vue import { useRoute } from vue-router import { ElMessage } from element-plus import { getCourseDetail, getSchedules, createBooking } from /api/trial import { formatDate } from /utils/date const route useRoute() const courseId route.params.id const course ref({}) const availableSchedules ref([]) const selectedScheduleId ref(null) const form ref({ studentName: , studentPhone: }) const formRef ref() const loading ref(false) const rules { studentName: [{ required: true, message: 请输入姓名, trigger: blur }], studentPhone: [ { required: true, message: 请输入手机号, trigger: blur }, { pattern: /^1[3-9]\d{9}$/, message: 手机号格式不正确, trigger: blur } ] } // 加载课程详情和可预约时间 const loadData async () { try { const [courseRes, scheduleRes] await Promise.all([ getCourseDetail(courseId), getSchedules(courseId) ]) course.value courseRes.data availableSchedules.value scheduleRes.data.filter(s s.status AVAILABLE) } catch (error) { ElMessage.error(数据加载失败) } } // 提交预约 const submitBooking () { formRef.value.validate(async (valid) { if (!valid) return if (!selectedScheduleId.value) { ElMessage.warning(请选择预约时间) return