本文目录导读:

我来为你详细介绍如何实现一个PHP选课系统,我会从基础架构开始,逐步实现核心功能。
系统架构设计
数据库表结构
-- 学生表
CREATE TABLE students (
id INT PRIMARY KEY AUTO_INCREMENT,
student_no VARCHAR(20) UNIQUE NOT NULL,
name VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL,
major VARCHAR(100),
grade VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 课程表
CREATE TABLE courses (
id INT PRIMARY KEY AUTO_INCREMENT,
course_no VARCHAR(20) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
teacher VARCHAR(50),
credit DECIMAL(2,1),
max_students INT DEFAULT 50,
enrolled INT DEFAULT 0,
schedule VARCHAR(200), -- 上课时间
classroom VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 选课记录表
CREATE TABLE enrollments (
id INT PRIMARY KEY AUTO_INCREMENT,
student_id INT,
course_id INT,
enroll_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status ENUM('active', 'dropped') DEFAULT 'active',
FOREIGN KEY (student_id) REFERENCES students(id),
FOREIGN KEY (course_id) REFERENCES courses(id),
UNIQUE KEY unique_enrollment (student_id, course_id, status)
);
-- 管理员表
CREATE TABLE admins (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL
);
目录结构
course-selection-system/
├── config/
│ └── database.php
├── includes/
│ ├── auth.php
│ ├── functions.php
│ └── session.php
├── css/
│ └── style.css
├── student/
│ ├── login.php
│ ├── dashboard.php
│ ├── course-list.php
│ ├── select-course.php
│ └── my-courses.php
├── admin/
│ ├── login.php
│ ├── dashboard.php
│ ├── manage-courses.php
│ ├── add-course.php
│ └── manage-students.php
└── index.php
核心代码实现
数据库配置文件
<?php
// config/database.php
class Database {
private $host = 'localhost';
private $db_name = 'course_selection';
private $username = 'root';
private $password = '';
private $conn;
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO(
"mysql:host={$this->host};dbname={$this->db_name}",
$this->username,
$this->password
);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->conn->exec("set names utf8");
} catch(PDOException $e) {
echo "数据库连接错误: " . $e->getMessage();
}
return $this->conn;
}
}
?>
会话管理类
<?php
// includes/session.php
class Session {
public static function start() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
}
public static function set($key, $value) {
$_SESSION[$key] = $value;
}
public static function get($key) {
return $_SESSION[$key] ?? null;
}
public static function destroy() {
session_destroy();
}
public static function isLoggedIn() {
return isset($_SESSION['user_id']);
}
public static function getUserType() {
return $_SESSION['user_type'] ?? null;
}
}
?>
认证功能
<?php
// includes/auth.php
require_once 'session.php';
require_once '../config/database.php';
class Auth {
private $db;
public function __construct() {
$database = new Database();
$this->db = $database->getConnection();
Session::start();
}
// 学生登录
public function studentLogin($student_no, $password) {
$sql = "SELECT * FROM students WHERE student_no = :student_no";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':student_no', $student_no);
$stmt->execute();
$student = $stmt->fetch(PDO::FETCH_ASSOC);
if ($student && password_verify($password, $student['password'])) {
Session::set('user_id', $student['id']);
Session::set('user_type', 'student');
Session::set('user_name', $student['name']);
Session::set('student_no', $student['student_no']);
return true;
}
return false;
}
// 管理员登录
public function adminLogin($username, $password) {
$sql = "SELECT * FROM admins WHERE username = :username";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':username', $username);
$stmt->execute();
$admin = $stmt->fetch(PDO::FETCH_ASSOC);
if ($admin && password_verify($password, $admin['password'])) {
Session::set('user_id', $admin['id']);
Session::set('user_type', 'admin');
Session::set('user_name', $admin['username']);
return true;
}
return false;
}
// 登出
public function logout() {
Session::destroy();
header('Location: ../index.php');
exit();
}
// 验证登录状态
public static function requireLogin() {
Session::start();
if (!Session::isLoggedIn()) {
header('Location: ../index.php');
exit();
}
}
}
?>
学生端功能
课程列表页面
<?php
// student/course-list.php
require_once '../includes/auth.php';
Auth::requireLogin();
$database = new Database();
$db = $database->getConnection();
// 获取当前学生信息
$student_id = Session::get('user_id');
// 获取学生已选课程ID
$sql_enrolled = "SELECT course_id FROM enrollments WHERE student_id = :student_id AND status = 'active'";
$stmt_enrolled = $db->prepare($sql_enrolled);
$stmt_enrolled->bindParam(':student_id', $student_id);
$stmt_enrolled->execute();
$enrolled_courses = $stmt_enrolled->fetchAll(PDO::FETCH_COLUMN);
// 课程筛选
$where = [];
$params = [];
if (isset($_GET['keyword']) && !empty($_GET['keyword'])) {
$where[] = "(c.name LIKE :keyword OR c.teacher LIKE :keyword OR c.course_no LIKE :keyword)";
$params[':keyword'] = '%' . $_GET['keyword'] . '%';
}
$where_clause = $where ? 'WHERE ' . implode(' AND ', $where) : '';
// 获取课程列表
$sql = "SELECT c.*,
(c.max_students - c.enrolled) as available_seats
FROM courses c
{$where_clause}
ORDER BY c.course_no";
$stmt = $db->prepare($sql);
$stmt->execute($params);
$courses = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html>
<head>课程列表</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div class="header">
<h1>选课系统 - 课程列表</h1>
<div class="user-info">
欢迎, <?php echo Session::get('user_name'); ?> |
<a href="my-courses.php">我的课程</a> |
<a href="logout.php">退出</a>
</div>
</div>
<div class="search-form">
<form method="GET" action="">
<input type="text" name="keyword" placeholder="搜索课程名称/教师/课程编号"
value="<?php echo $_GET['keyword'] ?? ''; ?>">
<button type="submit">搜索</button>
</form>
</div>
<div class="course-grid">
<?php foreach ($courses as $course): ?>
<div class="course-card">
<h3><?php echo htmlspecialchars($course['name']); ?></h3>
<p class="course-no">编号:<?php echo $course['course_no']; ?></p>
<p class="teacher">教师:<?php echo htmlspecialchars($course['teacher']); ?></p>
<p class="credit">学分:<?php echo $course['credit']; ?></p>
<p class="schedule">时间:<?php echo $course['schedule']; ?></p>
<p class="capacity">
已选:<?php echo $course['enrolled']; ?>/<?php echo $course['max_students']; ?>
<span class="available">(可退:<?php echo $course['available_seats']; ?>)</span>
</p>
<?php if (in_array($course['id'], $enrolled_courses)): ?>
<button class="btn btn-warning" disabled>已选</button>
<a href="drop-course.php?id=<?php echo $course['id']; ?>"
class="btn btn-danger"
onclick="return confirm('确定退选该课程?')">退选</a>
<?php elseif ($course['available_seats'] > 0): ?>
<a href="select-course.php?id=<?php echo $course['id']; ?>"
class="btn btn-primary"
onclick="return confirm('确定选择该课程?')">选课</a>
<?php else: ?>
<button class="btn btn-secondary" disabled>已满</button>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</body>
</html>
选课功能
<?php
// student/select-course.php
require_once '../includes/auth.php';
Auth::requireLogin();
$database = new Database();
$db = $database->getConnection();
$student_id = Session::get('user_id');
$course_id = $_GET['id'] ?? 0;
try {
// 开始事务
$db->beginTransaction();
// 检查课程是否存在且有剩余名额
$sql = "SELECT * FROM courses WHERE id = :course_id FOR UPDATE";
$stmt = $db->prepare($sql);
$stmt->bindParam(':course_id', $course_id);
$stmt->execute();
$course = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$course) {
throw new Exception("课程不存在");
}
if ($course['enrolled'] >= $course['max_students']) {
throw new Exception("课程已满");
}
// 检查是否已经选过该课程
$sql = "SELECT id FROM enrollments
WHERE student_id = :student_id
AND course_id = :course_id
AND status = 'active'";
$stmt = $db->prepare($sql);
$stmt->bindParam(':student_id', $student_id);
$stmt->bindParam(':course_id', $course_id);
$stmt->execute();
if ($stmt->rowCount() > 0) {
throw new Exception("您已选择该课程");
}
// 添加选课记录
$sql = "INSERT INTO enrollments (student_id, course_id, status)
VALUES (:student_id, :course_id, 'active')";
$stmt = $db->prepare($sql);
$stmt->bindParam(':student_id', $student_id);
$stmt->bindParam(':course_id', $course_id);
$stmt->execute();
// 更新课程已选人数
$sql = "UPDATE courses SET enrolled = enrolled + 1 WHERE id = :course_id";
$stmt = $db->prepare($sql);
$stmt->bindParam(':course_id', $course_id);
$stmt->execute();
// 提交事务
$db->commit();
$_SESSION['success'] = "选课成功!";
header("Location: course-list.php");
} catch (Exception $e) {
// 回滚事务
$db->rollBack();
$_SESSION['error'] = $e->getMessage();
header("Location: course-list.php");
}
?>
我的课程页面
<?php
// student/my-courses.php
require_once '../includes/auth.php';
Auth::requireLogin();
$database = new Database();
$db = $database->getConnection();
$student_id = Session::get('user_id');
// 获取学生已选课程
$sql = "SELECT c.*, e.enroll_time, e.id as enrollment_id
FROM enrollments e
JOIN courses c ON e.course_id = c.id
WHERE e.student_id = :student_id
AND e.status = 'active'
ORDER BY e.enroll_time DESC";
$stmt = $db->prepare($sql);
$stmt->bindParam(':student_id', $student_id);
$stmt->execute();
$my_courses = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 计算总学分
$total_credits = array_sum(array_column($my_courses, 'credit'));
?>
<!DOCTYPE html>
<html>
<head>我的课程</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div class="header">
<h1>我的课程</h1>
<div class="user-info">
欢迎, <?php echo Session::get('user_name'); ?> |
<a href="course-list.php">课程列表</a> |
<a href="logout.php">退出</a>
</div>
</div>
<div class="summary">
<p>已选课程:<?php echo count($my_courses); ?> 门</p>
<p>总学分:<?php echo $total_credits; ?></p>
</div>
<?php if (count($my_courses) > 0): ?>
<table class="course-table">
<thead>
<tr>
<th>课程编号</th>
<th>课程名称</th>
<th>教师</th>
<th>学分</th>
<th>上课时间</th>
<th>选课时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($my_courses as $course): ?>
<tr>
<td><?php echo $course['course_no']; ?></td>
<td><?php echo htmlspecialchars($course['name']); ?></td>
<td><?php echo htmlspecialchars($course['teacher']); ?></td>
<td><?php echo $course['credit']; ?></td>
<td><?php echo $course['schedule']; ?></td>
<td><?php echo $course['enroll_time']; ?></td>
<td>
<a href="drop-course.php?id=<?php echo $course['id']; ?>"
class="btn btn-danger btn-sm"
onclick="return confirm('确定退选该课程?')">退选</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="empty-message">
<p>您还没有选择任何课程</p>
<a href="course-list.php" class="btn btn-primary">浏览课程</a>
</div>
<?php endif; ?>
</body>
</html>
管理端功能
课程管理
<?php
// admin/manage-courses.php
require_once '../includes/auth.php';
Auth::requireLogin();
if (Session::getUserType() !== 'admin') {
header('Location: ../index.php');
exit();
}
$database = new Database();
$db = $database->getConnection();
// 处理添加课程
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if ($_POST['action'] === 'add') {
try {
$sql = "INSERT INTO courses (course_no, name, teacher, credit, max_students, schedule, classroom)
VALUES (:course_no, :name, :teacher, :credit, :max_students, :schedule, :classroom)";
$stmt = $db->prepare($sql);
$stmt->execute([
':course_no' => $_POST['course_no'],
':name' => $_POST['name'],
':teacher' => $_POST['teacher'],
':credit' => $_POST['credit'],
':max_students' => $_POST['max_students'],
':schedule' => $_POST['schedule'],
':classroom' => $_POST['classroom']
]);
$_SESSION['success'] = "课程添加成功!";
} catch (Exception $e) {
$_SESSION['error'] = "添加失败:" . $e->getMessage();
}
}
}
// 获取所有课程
$sql = "SELECT * FROM courses ORDER BY course_no";
$stmt = $db->query($sql);
$courses = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html>
<head>课程管理</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div class="header">
<h1>课程管理</h1>
<div class="user-info">
管理员: <?php echo Session::get('user_name'); ?> |
<a href="dashboard.php">管理首页</a> |
<a href="logout.php">退出</a>
</div>
</div>
<!-- 添加课程表单 -->
<div class="add-course-form">
<h2>添加新课程</h2>
<form method="POST" action="">
<input type="hidden" name="action" value="add">
<div class="form-group">
<label>课程编号:</label>
<input type="text" name="course_no" required>
</div>
<div class="form-group">
<label>课程名称:</label>
<input type="text" name="name" required>
</div>
<div class="form-group">
<label>授课教师:</label>
<input type="text" name="teacher" required>
</div>
<div class="form-group">
<label>学分:</label>
<input type="number" name="credit" step="0.5" required>
</div>
<div class="form-group">
<label>容量:</label>
<input type="number" name="max_students" value="50" required>
</div>
<div class="form-group">
<label>上课时间:</label>
<input type="text" name="schedule" placeholder="如:周一 1-2节" required>
</div>
<div class="form-group">
<label>教室:</label>
<input type="text" name="classroom" required>
</div>
<button type="submit" class="btn btn-primary">添加课程</button>
</form>
</div>
<!-- 课程列表 -->
<div class="course-list">
<h2>已开设课程</h2>
<table class="data-table">
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>教师</th>
<th>学分</th>
<th>容量</th>
<th>已选</th>
<th>时间</th>
<th>教室</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($courses as $course): ?>
<tr>
<td><?php echo $course['course_no']; ?></td>
<td><?php echo htmlspecialchars($course['name']); ?></td>
<td><?php echo htmlspecialchars($course['teacher']); ?></td>
<td><?php echo $course['credit']; ?></td>
<td><?php echo $course['max_students']; ?></td>
<td><?php echo $course['enrolled']; ?></td>
<td><?php echo $course['schedule']; ?></td>
<td><?php echo $course['classroom']; ?></td>
<td>
<a href="edit-course.php?id=<?php echo $course['id']; ?>"
class="btn btn-sm btn-info">编辑</a>
<a href="delete-course.php?id=<?php echo $course['id']; ?>"
class="btn btn-sm btn-danger"
onclick="return confirm('确定删除该课程?')">删除</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</body>
</html>
安全考虑和优化建议
安全措施
// 防止SQL注入 - 使用预处理语句
// 防止XSS攻击 - 使用htmlspecialchars()
// 防止CSRF攻击 - 添加Token验证
// 密码加密 - 使用password_hash()和password_verify()
// CSRF Token生成和验证
function generateCSRFToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function verifyCSRFToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
性能优化
// 数据库索引优化 ALTER TABLE enrollments ADD INDEX idx_student_course (student_id, course_id, status); ALTER TABLE courses ADD INDEX idx_course_no (course_no); // 使用缓存 // 可以缓存热门课程列表,减少数据库查询
验证规则
// 选课时间限制
function isWithinSelectionPeriod() {
$start_time = strtotime('2024-09-01 08:00:00');
$end_time = strtotime('2024-09-15 18:00:00');
$now = time();
return $now >= $start_time && $now <= $end_time;
}
// 最大选课数量限制
function canSelectMoreCourses($student_id, $max_courses = 6) {
// 查询学生当前选课数量
// 比较是否超过最大限制
}
部署建议
-
环境要求
- PHP 7.4+
- MySQL 5.7+
- Apache/Nginx
-
配置文件
- 将数据库配置信息放在单独的配置文件中
- 设置错误报告级别
- 配置时区
-
初始数据
- 创建管理员账号
- 导入测试数据
这个选课系统包含了基本的选课功能,你可以根据实际需求进行扩展,比如添加课程冲突检测、选课优先级、退课审核等功能。