课堂分配系统的核心挑战
教师课堂分配是在线教育平台的核心业务模块。一个好的分配系统需要平衡多方需求:教师的时间偏好、学生的学习需求、教室资源的利用率,以及各种业务规则的约束。
| 挑战维度 | 具体问题 | 解决思路 |
|---|---|---|
| 时间冲突 | 同一教师同时有多节课 | 冲突检测算法 |
| 资源竞争 | 多个班级需要同一教室 | 资源锁定与预约 |
| 偏好匹配 | 教师擅长科目与课程匹配 | 技能标签系统 |
| 负载均衡 | 课程分配不均 | 工作量计算与分配算法 |
| 动态调整 | 临时换课、代课 | 变更审批流程 |
数据模型设计
核心实体关系
课堂分配涉及多个实体之间的复杂关系。以下是简化的数据模型设计:
// 教师实体
interface Teacher {
id: string
name: string
subjects: string[] // 可教授科目
maxWeeklyHours: number // 每周最大课时
preferredTimeSlots: TimeSlot[] // 偏好时间段
unavailableSlots: TimeSlot[] // 不可用时间
}
// 课程实体
interface Course {
id: string
name: string
subject: string
gradeLevel: number
weeklyHours: number // 每周课时数
requiredEquipment: string[] // 需要的设备
}
// 课堂分配记录
interface ClassAssignment {
id: string
teacherId: string
courseId: string
classroomId: string
timeSlot: TimeSlot
semester: string
status: 'scheduled' | 'cancelled' | 'substituted'
}
// 时间段定义
interface TimeSlot {
dayOfWeek: 1 | 2 | 3 | 4 | 5 | 6 | 7
startTime: string // "08:00"
endTime: string // "09:40"
}
约束规则配置
系统需要支持灵活的规则配置,不同学校可能有不同的排课限制:
interface ScheduleRules {
// 每日最大连续课时
maxConsecutiveHours: number
// 午休时间不排课
lunchBreak: { start: string; end: string }
// 主课优先排在上午
prioritySubjectsInMorning: string[]
// 体育课不排在上午第一节
physicalEducationConstraints: TimeSlot[]
}
冲突检测算法
在分配课程前,必须确保不会产生时间冲突。冲突类型包括教师冲突、教室冲突和班级冲突。
核心检测逻辑
function detectConflicts(
newAssignment: ClassAssignment,
existingAssignments: ClassAssignment[]
): ConflictResult[] {
const conflicts: ConflictResult[] = []
for (const existing of existingAssignments) {
// 检查时间是否重叠
if (!isTimeOverlap(newAssignment.timeSlot, existing.timeSlot)) {
continue
}
// 教师冲突:同一教师同一时间有其他课
if (newAssignment.teacherId === existing.teacherId) {
conflicts.push({
type: 'teacher',
message: `教师在 ${formatTimeSlot(existing.timeSlot)} 已有课程`,
existingAssignment: existing
})
}
// 教室冲突:同一教室同一时间被占用
if (newAssignment.classroomId === existing.classroomId) {
conflicts.push({
type: 'classroom',
message: `教室在该时段已被占用`,
existingAssignment: existing
})
}
}
return conflicts
}
// 判断两个时间段是否重叠
function isTimeOverlap(a: TimeSlot, b: TimeSlot): boolean {
if (a.dayOfWeek !== b.dayOfWeek) return false
const aStart = parseTime(a.startTime)
const aEnd = parseTime(a.endTime)
const bStart = parseTime(b.startTime)
const bEnd = parseTime(b.endTime)
// 重叠条件:A开始时间在B结束之前,且A结束时间在B开始之后
return aStart < bEnd && aEnd > bStart
}
关键说明: 时间重叠判断是排课系统的基础。使用"两个区间重叠当且仅当 A.start < B.end 且 A.end > B.start"这个经典算法,可以准确判断任意两个时间段是否冲突。
自动排课算法
贪心算法实现
对于中小规模的排课需求,贪心算法是一个实用的选择。它按优先级依次分配课程,每次选择最优的可用时间段:
async function autoSchedule(
courses: Course[],
teachers: Teacher[],
semester: string
): Promise<ScheduleResult> {
// 1. 按优先级排序课程(主课优先)
const sortedCourses = courses.sort((a, b) =>
getPriority(a.subject) - getPriority(b.subject)
)
const assignments: ClassAssignment[] = []
const failures: ScheduleFailure[] = []
for (const course of sortedCourses) {
// 2. 找到可教授该科目的教师
const eligibleTeachers = teachers.filter(t =>
t.subjects.includes(course.subject) &&
getWeeklyHours(t.id, assignments) + course.weeklyHours <= t.maxWeeklyHours
)
if (eligibleTeachers.length === 0) {
failures.push({ course, reason: '无可用教师' })
continue
}
// 3. 按工作量从少到多排序,实现负载均衡
eligibleTeachers.sort((a, b) =>
getWeeklyHours(a.id, assignments) - getWeeklyHours(b.id, assignments)
)
// 4. 尝试为课程找到合适的时间段
let assigned = false
for (const teacher of eligibleTeachers) {
const availableSlots = findAvailableSlots(teacher, course, assignments)
if (availableSlots.length >= course.weeklyHours) {
// 选择最优时间段并创建分配
const selectedSlots = selectOptimalSlots(availableSlots, course)
for (const slot of selectedSlots) {
assignments.push(createAssignment(course, teacher, slot, semester))
}
assigned = true
break
}
}
if (!assigned) {
failures.push({ course, reason: '无可用时间段' })
}
}
return { assignments, failures }
}
优化时间段选择
选择时间段时需要考虑多个因素:教师偏好、科目特性、避免过于分散等。
function selectOptimalSlots(
availableSlots: TimeSlot[],
course: Course
): TimeSlot[] {
// 根据科目特性调整优先级
const scoredSlots = availableSlots.map(slot => ({
slot,
score: calculateSlotScore(slot, course)
}))
// 按分数排序并选择需要的数量
scoredSlots.sort((a, b) => b.score - a.score)
return scoredSlots.slice(0, course.weeklyHours).map(s => s.slot)
}
function calculateSlotScore(slot: TimeSlot, course: Course): number {
let score = 100
// 主课在上午加分
if (isPrioritySubject(course.subject) && isMorning(slot)) {
score += 20
}
// 体育课在下午加分
if (course.subject === '体育' && isAfternoon(slot)) {
score += 15
}
// 避免周一第一节(师生状态不佳)
if (slot.dayOfWeek === 1 && slot.startTime === '08:00') {
score -= 10
}
return score
}
教师工作量管理
工作量统计看板
interface TeacherWorkload {
teacherId: string
teacherName: string
weeklyHours: number
maxHours: number
utilization: number // 利用率百分比
courses: CourseAssignmentSummary[]
}
function calculateWorkload(
teacher: Teacher,
assignments: ClassAssignment[]
): TeacherWorkload {
const teacherAssignments = assignments.filter(
a => a.teacherId === teacher.id && a.status === 'scheduled'
)
const weeklyHours = teacherAssignments.reduce((sum, a) => {
const duration = getSlotDuration(a.timeSlot)
return sum + duration
}, 0)
return {
teacherId: teacher.id,
teacherName: teacher.name,
weeklyHours,
maxHours: teacher.maxWeeklyHours,
utilization: Math.round((weeklyHours / teacher.maxWeeklyHours) * 100),
courses: groupByCourse(teacherAssignments)
}
}
负载均衡可视化
展示全校教师工作量分布,帮助管理员发现不均衡问题:
| 教师 | 周课时 | 上限 | 利用率 | 状态 |
|---|---|---|---|---|
| 张老师 | 18 | 20 | 90% | 🟢 正常 |
| 李老师 | 22 | 20 | 110% | 🔴 超载 |
| 王老师 | 8 | 20 | 40% | 🟡 偏低 |
代课与换课管理
代课申请流程
当教师因故无法上课时,系统需要支持快速安排代课:
interface SubstitutionRequest {
id: string
originalTeacherId: string
assignmentId: string
reason: string
requestedDate: string
status: 'pending' | 'approved' | 'rejected'
substituteTeacherId?: string
}
async function findSubstitute(
assignment: ClassAssignment
): Promise<Teacher[]> {
const course = await getCourse(assignment.courseId)
const timeSlot = assignment.timeSlot
// 查找满足条件的代课教师
const candidates = await db.teachers.findMany({
where: {
// 能教授该科目
subjects: { has: course.subject },
// 该时段没有其他课
NOT: {
assignments: {
some: {
timeSlot: { equals: timeSlot },
status: 'scheduled'
}
}
}
},
orderBy: {
// 优先选择课时较少的教师
weeklyHours: 'asc'
}
})
return candidates
}
换课规则
换课需要确保双方时间都可用,且符合教学安排:
async function validateSwap(
assignment1: ClassAssignment,
assignment2: ClassAssignment
): Promise<ValidationResult> {
const errors: string[] = []
// 检查教师能否在对方时间上课
if (!isTeacherAvailable(assignment1.teacherId, assignment2.timeSlot)) {
errors.push('教师1在交换后的时段不可用')
}
if (!isTeacherAvailable(assignment2.teacherId, assignment1.timeSlot)) {
errors.push('教师2在交换后的时段不可用')
}
// 检查教室是否仍然适用
if (!isClassroomSuitable(assignment1.classroomId, assignment2.courseId)) {
errors.push('教室不满足交换后课程的设备需求')
}
return {
valid: errors.length === 0,
errors
}
}
权限与审批流程
角色权限设计
| 角色 | 权限 |
|---|---|
| 系统管理员 | 全部权限,包括规则配置 |
| 教务主任 | 审批换课/代课,手动调整排课 |
| 年级组长 | 查看本年级排课,提交调课申请 |
| 普通教师 | 查看个人课表,提交换课申请 |
审批工作流
// 审批状态机
const approvalStateMachine = {
pending: {
approve: 'approved',
reject: 'rejected',
requestMoreInfo: 'pending'
},
approved: {
execute: 'completed',
revoke: 'pending'
},
rejected: {
resubmit: 'pending'
}
}
实际应用建议
- 分步推进:先实现手动排课,再逐步引入自动化
- 留有余量:教师最大课时设置时预留10-15%缓冲
- 版本控制:每次排课保存快照,支持回滚
- 预警机制:工作量接近上限时自动提醒
- 数据分析:统计排课效率和资源利用率,持续优化
通过以上设计,可以构建一个功能完善、易于扩展的教师课堂分配系统,有效支撑在线教育平台的日常运营。


