基于SSM框架的在线家教预约平台 - 源码深度解析

JavaJavaScriptMavenHTMLCSSSSM框架MySQL
2026-02-204 浏览

文章摘要

本项目是基于SSM(Spring+SpringMVC+MyBatis)框架开发的在线家教预约平台,旨在连接学生与家教老师,解决传统家教信息不对称、预约流程繁琐、时间安排不灵活的痛点。平台通过线上化服务,将教师资源、课程信息、学生需求进行高效整合,实现一键预约、订单管理、课程评价等核心功能,显著提升家...

在家教服务需求日益增长的背景下,传统的中介模式存在信息不透明、匹配效率低、时间安排僵化等问题。为了解决这些痛点,一个高效、便捷的线上解决方案应运而生。本系统采用成熟的SSM(Spring + SpringMVC + MyBatis)框架集,构建了一个集教师资源展示、智能预约、订单管理及教学评价于一体的综合性服务平台。

系统在技术架构上层次分明。Spring Framework作为核心控制容器,通过依赖注入(DI)和面向切面编程(AOP)管理业务对象生命周期和事务控制,确保了业务逻辑的清晰与服务的可测试性。SpringMVC模块承担Web层的职责,采用注解驱动的方式简化了控制器(Controller)的开发,高效地处理HTTP请求的路由、参数绑定和视图解析。数据持久层由MyBatis实现,它通过XML映射文件或注解将Java接口方法与SQL语句灵活关联,其强大的动态SQL能力简化了复杂查询条件的拼接。前端界面基于JSP与Bootstrap框架构建,保证了在不同设备上的响应式体验,并结合Ajax技术实现无刷新数据交互,提升了用户操作的流畅度。数据库选用MySQL,通过合理的表结构设计和索引优化来支撑平台的数据存储与高效查询需求。

数据库核心表结构设计

平台的业务逻辑建立在8张核心数据表之上,其设计直接决定了系统的数据一致性和查询性能。以下是几个关键表的详细分析。

用户表(user)是系统的基石,它统一管理学生、教师及管理员等所有角色的基础信息。

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户唯一标识',
  `username` varchar(50) NOT NULL COMMENT '用户名,用于登录',
  `password` varchar(255) NOT NULL COMMENT '加密存储的密码',
  `role` varchar(20) NOT NULL DEFAULT 'student' COMMENT '用户角色:student, teacher, admin',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
  `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
  `avatar` varchar(255) DEFAULT NULL COMMENT '头像图片路径',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),
  KEY `idx_role` (`role`),
  KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

该表设计的亮点在于:

  1. 角色管理:通过role字段实现单表多角色存储,简化了权限验证逻辑。索引idx_role加速了按角色筛选用户的操作。
  2. 安全与可追溯性password字段预留了足够的长度以支持安全的哈希加密算法(如BCrypt)。create_timeupdate_time自动维护,便于审计和数据追踪。
  3. 查询优化:在username(登录)、phone(联系方式验证)上建立唯一索引或普通索引,确保了关键业务操作的高效性。

订单表(order)是业务流转的核心,记录了完整的预约生命周期。

CREATE TABLE `order` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_number` varchar(64) NOT NULL COMMENT '订单号,业务唯一',
  `student_id` int(11) NOT NULL COMMENT '学生ID',
  `teacher_id` int(11) NOT NULL COMMENT '教师ID',
  `course_id` int(11) NOT NULL COMMENT '课程ID',
  `scheduled_time` datetime NOT NULL COMMENT '预约上课时间',
  `duration` int(11) NOT NULL COMMENT '课程时长(分钟)',
  `amount` decimal(10,2) NOT NULL COMMENT '订单金额',
  `status` varchar(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending, confirmed, completed, cancelled',
  `student_comment` text COMMENT '学生评价',
  `student_rating` int(11) DEFAULT NULL COMMENT '学生评分(1-5)',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_number` (`order_number`),
  KEY `idx_student_id` (`student_id`),
  KEY `idx_teacher_id` (`teacher_id`),
  KEY `idx_status` (`status`),
  KEY `idx_scheduled_time` (`scheduled_time`),
  CONSTRAINT `fk_order_student` FOREIGN KEY (`student_id`) REFERENCES `user` (`id`),
  CONSTRAINT `fk_order_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `user` (`id`),
  CONSTRAINT `fk_order_course` FOREIGN KEY (`course_id`) REFERENCES `course` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

该表设计的深度体现在:

  1. 业务唯一性与关联完整性order_number作为业务层面的唯一标识,避免了使用自增ID暴露业务量。通过外键约束(如fk_order_student)强制保证了与学生、教师、课程表之间的引用完整性,防止了脏数据的产生。
  2. 状态机与查询效率status字段清晰地定义了订单的生命周期状态。为其建立索引idx_status,并结合idx_teacher_ididx_student_id,可以极快地查询出特定用户处于某种状态的所有订单,这对于“我的订单”页面至关重要。
  3. 时间维度优化scheduled_time上的索引idx_scheduled_time,使得查询某天、某周的课程安排变得非常高效,为教师日历和学生预约时段选择提供了性能保障。

核心功能实现与代码解析

1. 教师资源筛选与分页查询

学生用户进入平台后,首要操作是根据学科、价格区间、教龄等条件筛选合适的家教。该功能由TeacherControllerlistTeachers方法处理,它接收前端传递的多维度查询参数,并调用服务层完成复杂查询。

Controller层接收参数并调用服务:

@Controller
@RequestMapping("/teacher")
public class TeacherController {

    @Autowired
    private TeacherService teacherService;

    @RequestMapping("/list")
    @ResponseBody
    public PageInfo<TeacherVO> listTeachers(
            @RequestParam(value = "subject", required = false) String subject,
            @RequestParam(value = "minPrice", defaultValue = "0") Integer minPrice,
            @RequestParam(value = "maxPrice", defaultValue = "1000") Integer maxPrice,
            @RequestParam(value = "teachingYears", required = false) Integer teachingYears,
            @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
            @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {

        TeacherQuery query = new TeacherQuery();
        query.setSubject(subject);
        query.setMinPrice(minPrice);
        query.setMaxPrice(maxPrice);
        query.setTeachingYears(teachingYears);

        return teacherService.getTeachersByPage(query, pageNum, pageSize);
    }
}

教师列表与筛选界面

Service层组合查询条件并分页:

@Service
public class TeacherServiceImpl implements TeacherService {

    @Autowired
    private TeacherMapper teacherMapper;

    @Override
    public PageInfo<TeacherVO> getTeachersByPage(TeacherQuery query, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<TeacherVO> teacherList = teacherMapper.selectByCondition(query);
        return new PageInfo<>(teacherList);
    }
}

MyBatis Mapper XML 动态SQL构建:

<!-- TeacherMapper.xml -->
<select id="selectByCondition" parameterType="com.example.dto.TeacherQuery" resultMap="TeacherVOResultMap">
    SELECT
        u.id, u.real_name, u.avatar,
        t.subject, t.teaching_years, t.hourly_rate, t.introduction
    FROM teacher_profile t
    INNER JOIN user u ON t.user_id = u.id
    WHERE u.role = 'teacher'
    <if test="subject != null and subject != ''">
        AND t.subject = #{subject}
    </if>
    <if test="minPrice != null">
        AND t.hourly_rate >= #{minPrice}
    </if>
    <if test="maxPrice != null">
        AND t.hourly_rate <= #{maxPrice}
    </if>
    <if test="teachingYears != null">
        AND t.teaching_years >= #{teachingYears}
    </if>
    ORDER BY t.create_time DESC
</select>

此功能的关键在于使用MyBatis的<if>标签动态生成SQL,避免了拼接SQL字符串的繁琐与安全隐患。结合PageHelper插件,轻松实现了后端分页,减轻了数据库的压力。

2. 预约时间冲突校验与订单创建

当学生选择心仪教师和具体上课时间后,系统必须确保该时段未被占用。这是一个典型的并发一致性场景,需要在服务层进行严格的校验。

Service层核心校验与订单创建逻辑:

@Service
@Transactional(rollbackFor = Exception.class)
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Override
    public String createOrder(OrderDTO orderDTO) {
        // 1. 校验时间冲突
        if (hasTimeConflict(orderDTO.getTeacherId(), orderDTO.getScheduledTime(), orderDTO.getDuration())) {
            throw new BusinessException("该时间段已被预约,请选择其他时间");
        }

        // 2. 生成唯一订单号
        String orderNumber = generateOrderNumber();

        // 3. DTO 转 Entity
        Order order = new Order();
        BeanUtils.copyProperties(orderDTO, order);
        order.setOrderNumber(orderNumber);
        order.setStatus(OrderStatus.PENDING);

        // 4. 持久化订单
        orderMapper.insert(order);
        return orderNumber;
    }

    private boolean hasTimeConflict(Integer teacherId, Date scheduledTime, Integer duration) {
        // 计算课程结束时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(scheduledTime);
        calendar.add(Calendar.MINUTE, duration);
        Date endTime = calendar.getTime();

        // 查询该教师在该时间段内是否存在已确认或进行中的订单
        int count = orderMapper.countConflictOrders(teacherId, scheduledTime, endTime);
        return count > 0;
    }

    private String generateOrderNumber() {
        // 格式: 年月日时分秒+随机数
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String timeStr = sdf.format(new Date());
        int random = (int) ((Math.random() * 9 + 1) * 1000);
        return timeStr + random;
    }
}

课程预约与时间选择界面

支持冲突校验的Mapper方法:

<!-- OrderMapper.xml -->
<select id="countConflictOrders" resultType="int">
    SELECT COUNT(1)
    FROM `order`
    WHERE teacher_id = #{teacherId}
    AND status IN ('confirmed', 'pending') -- 已确认和待确认的订单都算冲突
    AND scheduled_time < #{endTime}
    AND DATE_ADD(scheduled_time, INTERVAL duration MINUTE) > #{startTime}
</select>

该方法通过比较时间区间来判断是否存在重叠,是解决资源调度冲突的经典SQL写法。整个createOrder方法被@Transactional注解包裹,确保校验和插入操作的原子性,防止在高并发下出现超订问题。

3. 订单状态流转与教师确认

教师登录后,可以在个人中心查看待处理的预约请求,并选择接受或拒绝。此操作触发了订单状态机的流转。

Controller层处理确认请求:

@Controller
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/confirm")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> confirmOrder(@RequestParam("orderId") Integer orderId,
                                                             @RequestParam("action") String action) { // action: confirm/cancel
        try {
            if ("confirm".equals(action)) {
                orderService.confirmOrder(orderId);
            } else if ("cancel".equals(action)) {
                orderService.cancelOrder(orderId, "教师拒绝");
            }
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("message", "操作成功");
            return ResponseEntity.ok(result);
        } catch (BusinessException e) {
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", e.getMessage());
            return ResponseEntity.badRequest().body(result);
        }
    }
}

教师订单管理界面

Service层状态更新逻辑:

@Override
@Transactional(rollbackFor = Exception.class)
public void confirmOrder(Integer orderId) {
    Order order = orderMapper.selectById(orderId);
    if (order == null) {
        throw new BusinessException("订单不存在");
    }
    if (!OrderStatus.PENDING.equals(order.getStatus())) {
        throw new BusinessException("当前订单状态不可确认");
    }
    // 再次校验时间冲突(防止教师确认时时段已被其他订单占用)
    if (hasTimeConflict(order.getTeacherId(), order.getScheduledTime(), order.getDuration())) {
        throw new BusinessException("确认失败,该时间段已被其他预约占用");
    }
    // 更新订单状态
    order.setStatus(OrderStatus.CONFIRMED);
    order.setUpdateTime(new Date());
    orderMapper.updateStatus(order);
}

状态变更时,除了检查当前状态是否允许转换外,还再次执行了时间冲突校验,这是一个防御性编程的实践,确保了即使在极端并发情况下数据的最终正确性。

实体模型与业务逻辑封装

系统的核心业务逻辑通过精心设计的Java实体类(Entity)和数据传输对象(DTO)进行封装。例如,订单实体Order不仅映射数据库字段,还包含了其内在的行为。

public class Order {
    private Integer id;
    private String orderNumber;
    private Integer studentId;
    private Integer teacherId;
    private Integer courseId;
    private Date scheduledTime;
    private Integer duration;
    private BigDecimal amount;
    private String status; // PENDING, CONFIRMED, COMPLETED, CANCELLED
    private String studentComment;
    private Integer studentRating;
    private Date createTime;

    // 业务逻辑方法:判断订单是否可被评价
    public boolean canBeRated() {
        return OrderStatus.COMPLETED.equals(this.status) && this.studentRating == null;
    }

    // 业务逻辑方法:计算课程结束时间
    public Date getEndTime() {
        if (scheduledTime == null || duration == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(scheduledTime);
        calendar.add(Calendar.MINUTE, duration);
        return calendar.getTime();
    }

    // ... getters and setters
}

canBeRated()getEndTime()这样的业务规则封装在实体内部,遵循了面向对象的“数据与行为在一起”的原则,使得业务代码更加内聚、清晰和易于维护。

功能展望与系统优化方向

  1. 集成实时通信能力:引入WebSocket技术,在师生之间建立即时消息通道。教师可以发送上课提醒、资料链接,学生可以实时提问。实现上,可使用Spring WebSocket模块,定义ChatMessage实体和ChatController来处理消息的收发与存储。
  2. 引入支付网关集成:当前系统可能仅记录金额,未来可集成支付宝、微信支付等第三方支付接口。需要设计Payment表记录支付流水,并在订单服务中增加支付状态(如unpaid, paid, refunded)。支付成功后,通过异步通知回调更新订单状态。
  3. 实现智能推荐算法:基于学生以往的预约历史、搜索行为以及评价数据,构建协同过滤或基于内容的推荐模型。可以在教师列表页面增加“猜你喜欢”板块,提升教师曝光率和学生选课效率。
  4. 开发移动端应用:考虑到用户使用的便捷性,开发React Native或Flutter版本的移动App是必然趋势。这需要将现有的后端系统进行API化改造,提供一套完整的RESTful API,供移动端调用。
  5. 强化后台管理与数据分析:为平台管理员开发更强大的数据看板,可视化展示平台运营数据,如每日订单量、热门学科、教师收入排行等。使用ECharts等图表库进行数据渲染,为运营决策提供数据支持。

该家教预约平台通过SSM框架的稳健组合,实现了核心家教服务的线上化与自动化。其清晰的架构、严谨的数据库设计以及可扩展的代码结构,为后续的功能迭代和性能优化奠定了坚实的基础。

本文关键词
SSM框架在线家教预约平台源码解析SpringMyBatis

上下篇

上一篇
没有更多文章
下一篇
没有更多文章