怎样在PHP项目中实现选课系统?

wen java案例 2

本文目录导读:

怎样在PHP项目中实现选课系统?

  1. 系统架构设计
  2. 核心代码实现
  3. 安全考虑和优化建议
  4. 部署建议

我来为你详细介绍如何实现一个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) {
    // 查询学生当前选课数量
    // 比较是否超过最大限制
}

部署建议

  1. 环境要求

    • PHP 7.4+
    • MySQL 5.7+
    • Apache/Nginx
  2. 配置文件

    • 将数据库配置信息放在单独的配置文件中
    • 设置错误报告级别
    • 配置时区
  3. 初始数据

    • 创建管理员账号
    • 导入测试数据

这个选课系统包含了基本的选课功能,你可以根据实际需求进行扩展,比如添加课程冲突检测、选课优先级、退课审核等功能。

抱歉,评论功能暂时关闭!