这个系统将包含用户和管理员两种角色,涵盖图书的增、删、改、查等核心功能。

php网页图书管理系统
(图片来源网络,侵删)
  1. 技术栈:

    • 前端: HTML, CSS, JavaScript (用于表单验证和动态交互)
    • 后端: PHP (面向对象)
    • 数据库: MySQL (使用PDO进行数据库操作,更安全、更灵活)
    • 服务器: Apache/Nginx + PHP
  2. 功能模块:

    • 用户模块:
      • 用户注册
      • 用户登录
      • 注销
      • 图书列表展示 (分页)
      • 图书搜索 (按书名、作者)
      • 图书详情查看
      • 借阅图书
      • 查看我的借阅记录
    • 管理员模块:
      • 管理员登录
      • 图书管理 (增、删、改、查)
      • 用户管理 (查看、禁用/启用用户)
      • 借阅管理 (查看所有借阅记录、处理归还)
  3. 数据库设计: 我们需要几张核心的数据表来存储信息。

    • users 表 (用户表):

      php网页图书管理系统
      (图片来源网络,侵删)
      • id (INT, 主键, 自增)
      • username (VARCHAR, 唯一)
      • password (VARCHAR, 存储哈希后的密码)
      • email (VARCHAR, 唯一)
      • role (ENUM('user', 'admin'), 默认 'user')
      • is_active (BOOLEAN, 默认 TRUE)
      • created_at (DATETIME)
    • books 表 (图书表):

      • id (INT, 主键, 自增)
      • title (VARCHAR, 图书标题)
      • author (VARCHAR, 作者)
      • isbn (VARCHAR, ISBN号)
      • description (TEXT, 图书简介)
      • total_copies (INT, 总数量)
      • available_copies (INT, 可借数量)
      • created_at (DATETIME)
    • borrows 表 (借阅记录表):

      • id (INT, 主键, 自增)
      • user_id (INT, 外键关联 users.id)
      • book_id (INT, 外键关联 books.id)
      • borrow_date (DATE, 借出日期)
      • due_date (DATE, 应还日期)
      • return_date (DATE, 实际归还日期, NULL表示未归还)
      • status (ENUM('borrowed', 'returned'), 默认 'borrowed')

第一步:项目结构

创建一个项目目录,library_management,并在其中创建以下文件和文件夹:

library_management/
├── assets/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── main.js
├── config/
│   └── database.php
├── includes/
│   ├── header.php
│   ├── footer.php
│   ├── navbar.php
│   └── functions.php
├── models/
│   ├── User.php
│   ├── Book.php
│   └── Borrow.php
├── controllers/
│   ├── auth_controller.php
│   ├── book_controller.php
│   └── user_controller.php
├── admin/
│   ├── dashboard.php
│   ├── books.php
│   ├── users.php
│   └── borrows.php
├── index.php
├── login.php
├── register.php
├── logout.php
├── search.php
├── book_details.php
├── my_borrows.php
└── .htaccess

第二步:数据库配置与初始化

config/database.php

这个文件负责连接数据库。

<?php
// config/database.php
class Database {
    private $host = 'localhost';
    private $db_name = 'library_db'; // 你的数据库名
    private $username = 'root';     // 你的数据库用户名
    private $password = '';         // 你的数据库密码
    public $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->exec("set names utf8");
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $exception) {
            echo "Connection error: " . $exception->getMessage();
        }
        return $this->conn;
    }
}

创建数据库和表

在你的MySQL管理工具(如phpMyAdmin)中执行以下SQL语句来创建数据库和表:

-- 创建数据库
CREATE DATABASE IF NOT EXISTS library_db;
USE library_db;
-- 创建用户表
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    role ENUM('user', 'admin') DEFAULT 'user',
    is_active BOOLEAN DEFAULT TRUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 创建图书表
CREATE TABLE books (
    id INT AUTO_INCREMENT PRIMARY KEY,VARCHAR(255) NOT NULL,
    author VARCHAR(100) NOT NULL,
    isbn VARCHAR(20) UNIQUE,
    description TEXT,
    total_copies INT NOT NULL DEFAULT 1,
    available_copies INT NOT NULL DEFAULT 1,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 创建借阅记录表
CREATE TABLE borrows (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    book_id INT NOT NULL,
    borrow_date DATE NOT NULL,
    due_date DATE NOT NULL,
    return_date DATE DEFAULT NULL,
    status ENUM('borrowed', 'returned') DEFAULT 'borrowed',
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    FOREIGN KEY (book_id) REFERENCES books(id) ON DELETE CASCADE
);
-- 插入一个默认管理员用户 (密码为 'admin123')
INSERT INTO users (username, password, email, role) VALUES ('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'admin@example.com', 'admin');

注意: 密码 'admin123' 使用了 password_hash() 哈希,你可以用一小段PHP代码来生成这个哈希值。


第三步:核心模型层

模型负责与数据库交互。

models/User.php

<?php
// models/User.php
require_once 'config/database.php';
class User {
    private $conn;
    private $table_name = "users";
    public $id;
    public $username;
    public $password;
    public $email;
    public $role;
    public $is_active;
    public function __construct($db) {
        $this->conn = $db;
    }
    // 用户登录
    public function login() {
        $query = "SELECT id, username, password, role FROM " . $this->table_name . " WHERE username = :username AND is_active = 1 LIMIT 1";
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(":username", $this->username);
        $stmt->execute();
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($row) {
            if (password_verify($this->password, $row['password'])) {
                $this->id = $row['id'];
                $this->username = $row['username'];
                $this->role = $row['role'];
                return true;
            }
        }
        return false;
    }
    // 检查用户名是否已存在
    public function isUsernameExists() {
        $query = "SELECT id FROM " . $this->table_name . " WHERE username = :username";
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(":username", $this->username);
        $stmt->execute();
        return $stmt->rowCount() > 0;
    }
    // 检查邮箱是否已存在
    public function isEmailExists() {
        $query = "SELECT id FROM " . $this->table_name . " WHERE email = :email";
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(":email", $this->email);
        $stmt->execute();
        return $stmt->rowCount() > 0;
    }
    // 创建新用户
    public function create() {
        $query = "INSERT INTO " . $this->table_name . " (username, password, email) VALUES (:username, :password, :email)";
        $stmt = $this->conn->prepare($query);
        // 清理输入
        $this->username = htmlspecialchars(strip_tags($this->username));
        $this->email = htmlspecialchars(strip_tags($this->email));
        // 密码哈希
        $this->password = password_hash($this->password, PASSWORD_DEFAULT);
        $stmt->bindParam(":username", $this->username);
        $stmt->bindParam(":password", $this->password);
        $stmt->bindParam(":email", $this->email);
        if($stmt->execute()) {
            return true;
        }
        return false;
    }
}

models/Book.php

<?php
// models/Book.php
require_once 'config/database.php';
class Book {
    private $conn;
    private $table_name = "books";
    public $id;
    public $title;
    public $author;
    public $isbn;
    public $description;
    public $total_copies;
    public $available_copies;
    public function __construct($db) {
        $this->conn = $db;
    }
    // 获取所有图书
    public function getAll() {
        $query = "SELECT * FROM " . $this->table_name . " ORDER BY created_at DESC";
        $stmt = $this->conn->prepare($query);
        $stmt->execute();
        return $stmt;
    }
    // 根据ID获取图书
    public function getById() {
        $query = "SELECT * FROM " . $this->table_name . " WHERE id = :id LIMIT 1";
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(":id", $this->id);
        $stmt->execute();
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if($row) {
            $this->title = $row['title'];
            $this->author = $row['author'];
            $this->isbn = $row['isbn'];
            $this->description = $row['description'];
            $this->total_copies = $row['total_copies'];
            $this->available_copies = $row['available_copies'];
            return true;
        }
        return false;
    }
    // 添加新书
    public function create() {
        $query = "INSERT INTO " . $this->table_name . " (title, author, isbn, description, total_copies, available_copies) VALUES (:title, :author, :isbn, :description, :total_copies, :available_copies)";
        $stmt = $this->conn->prepare($query);
        $this->title = htmlspecialchars(strip_tags($this->title));
        $this->author = htmlspecialchars(strip_tags($this->author));
        $this->isbn = htmlspecialchars(strip_tags($this->isbn));
        $this->description = htmlspecialchars(strip_tags($this->description));
        $this->total_copies = htmlspecialchars(strip_tags($this->total_copies));
        $this->available_copies = htmlspecialchars(strip_tags($this->available_copies));
        $stmt->bindParam(":title", $this->title);
        $stmt->bindParam(":author", $this->author);
        $stmt->bindParam(":isbn", $this->isbn);
        $stmt->bindParam(":description", $this->description);
        $stmt->bindParam(":total_copies", $this->total_copies);
        $stmt->bindParam(":available_copies", $this->available_copies);
        if($stmt->execute()) {
            return true;
        }
        return false;
    }
    // 更新图书信息
    public function update() {
        $query = "UPDATE " . $this->table_name . " SET title = :title, author = :author, isbn = :isbn, description = :description, total_copies = :total_copies, available_copies = :available_copies WHERE id = :id";
        $stmt = $this->conn->prepare($query);
        $this->title = htmlspecialchars(strip_tags($this->title));
        $this->author = htmlspecialchars(strip_tags($this->author));
        $this->isbn = htmlspecialchars(strip_tags($this->isbn));
        $this->description = htmlspecialchars(strip_tags($this->description));
        $this->total_copies = htmlspecialchars(strip_tags($this->total_copies));
        $this->available_copies = htmlspecialchars(strip_tags($this->available_copies));
        $this->id = htmlspecialchars(strip_tags($this->id));
        $stmt->bindParam(":title", $this->title);
        $stmt->bindParam(":author", $this->author);
        $stmt->bindParam(":isbn", $this->isbn);
        $stmt->bindParam(":description", $this->description);
        $stmt->bindParam(":total_copies", $this->total_copies);
        $stmt->bindParam(":available_copies", $this->available_copies);
        $stmt->bindParam(":id", $this->id);
        if($stmt->execute()) {
            return true;
        }
        return false;
    }
    // 删除图书
    public function delete() {
        $query = "DELETE FROM " . $this->table_name . " WHERE id = :id";
        $stmt = $this->conn->prepare($query);
        $this->id = htmlspecialchars(strip_tags($this->id));
        $stmt->bindParam(":id", $this->id);
        if($stmt->execute()) {
            return true;
        }
        return false;
    }
    // 搜索图书
    public function search($keyword) {
        $query = "SELECT * FROM " . $this->table_name . " WHERE title LIKE :keyword OR author LIKE :keyword ORDER BY title ASC";
        $stmt = $this->conn->prepare($query);
        $keyword = "%{$keyword}%";
        $stmt->bindParam(":keyword", $keyword);
        $stmt->execute();
        return $stmt;
    }
}

第四步:控制器与页面实现

控制器负责处理业务逻辑,将数据传递给视图(页面)。

includes/functions.php (通用函数)

<?php
// includes/functions.php
// 启动会话
session_start();
// 检查用户是否登录
function isLoggedIn() {
    return isset($_SESSION['user_id']);
}
// 检查是否是管理员
function isAdmin() {
    return isset($_SESSION['role']) && $_SESSION['role'] === 'admin';
}
// 重定向用户
function redirect($url) {
    header("Location: " . BASE_URL . $url);
    exit();
}
// 显示错误信息
function flash($name = '', $message = '', $class = 'alert alert-success') {
    if(!empty($name)) {
        if(!empty($message) && empty($_SESSION[$name])) {
            if(!empty($_SESSION[$name])) {
                unset($_SESSION[$name]);
            }
            if(!empty($_SESSION[$name . '_class'])) {
                unset($_SESSION[$name . '_class']);
            }
            $_SESSION[$name] = $message;
            $_SESSION[$name . '_class'] = $class;
        } elseif(empty($message) && !empty($_SESSION[$name])) {
            $class = !empty($_SESSION[$name . '_class']) ? $_SESSION[$name . '_class'] : '';
            echo '<div class="' . $class . '" id="msg-flash">' . $_SESSION[$name] . '</div>';
            unset($_SESSION[$name]);
            unset($_SESSION[$name . '_class']);
        }
    }
}

includes/header.phpincludes/navbar.php

includes/header.php:

<?php
// includes/header.php
require_once 'functions.php';
define('BASE_URL', 'http://localhost/library_management/'); // 修改为你的项目URL
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">图书管理系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="index.php">📚 图书管理系统</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav ms-auto">
                    <li class="nav-item">
                        <a class="nav-link" href="index.php">首页</a>
                    </li>
                    <?php if(isLoggedIn()): ?>
                        <?php if(isAdmin()): ?>
                            <li class="nav-item">
                                <a class="nav-link" href="admin/dashboard.php">管理后台</a>
                            </li>
                        <?php endif; ?>
                        <li class="nav-item">
                            <a class="nav-link" href="my_borrows.php">我的借阅</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="logout.php">注销</a>
                        </li>
                    <?php else: ?>
                        <li class="nav-item">
                            <a class="nav-link" href="login.php">登录</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="register.php">注册</a>
                        </li>
                    <?php endif; ?>
                </ul>
            </div>
        </div>
    </nav>
    <div class="container mt-4">
        <?php flash('message'); ?>

includes/navbar.php:

<!-- 这个文件可以和header.php合并,或者用于其他需要导航的地方 -->

index.php (首页,展示图书列表)

<?php
// index.php
require_once 'config/database.php';
require_once 'models/Book.php';
require_once 'includes/header.php';
// 分页逻辑
$perPage = 8;
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$offset = ($page - 1) * $perPage;
$database = new Database();
$db = $database->getConnection();
$book = new Book($db);
// 获取图书总数
$totalQuery = "SELECT COUNT(*) FROM books";
$totalStmt = $db->prepare($totalQuery);
$totalStmt->execute();
$totalBooks = $totalStmt->fetchColumn();
$totalPages = ceil($totalBooks / $perPage);
// 获取当前页的图书
$book->title = ''; // 为了复用getAll,但这里不需要
$stmt = $book->getAll();
$stmt->bindValue(1, $perPage, PDO::PARAM_INT);
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
// 注意: 上述PDO绑定对于getAll方法中的简单SQL无效,需要修改getAll方法以支持分页
// 修改 Book.php 的 getAll 方法:
// public function getAll($limit = null, $offset = null) {
//     $query = "SELECT * FROM " . $this->table_name;
//     if ($limit !== null && $offset !== null) {
//         $query .= " LIMIT :limit OFFSET :offset";
//     }
//     $query .= " ORDER BY created_at DESC";
//     $stmt = $this->conn->prepare($query);
//     if ($limit !== null && $offset !== null) {
//         $stmt->bindParam(":limit", $limit, PDO::PARAM_INT);
//         $stmt->bindParam(":offset", $offset, PDO::PARAM_INT);
//     }
//     $stmt->execute();
//     return $stmt;
// }
// 由于时间关系,我们这里简化,直接使用一个支持分页的查询
$query = "SELECT * FROM books ORDER BY created_at DESC LIMIT :limit OFFSET :offset";
$stmt = $db->prepare($query);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
?>
<h2 class="mb-4">图书列表</h2>
<div class="row">
    <?php while($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>
    <div class="col-md-3 mb-4">
        <div class="card h-100">
            <div class="card-body">
                <h5 class="card-title"><?php echo htmlspecialchars($row['title']); ?></h5>
                <p class="card-text">作者: <?php echo htmlspecialchars($row['author']); ?></p>
                <p class="card-text">可借: <span class="badge bg-<?php echo $row['available_copies'] > 0 ? 'success' : 'danger'; ?>"><?php echo $row['available_copies']; ?></span></p>
                <a href="book_details.php?id=<?php echo $row['id']; ?>" class="btn btn-primary">查看详情</a>
            </div>
        </div>
    </div>
    <?php endwhile; ?>
</div>
<!-- 分页链接 -->
<nav aria-label="Page navigation">
    <ul class="pagination justify-content-center">
        <li class="page-item <?php echo $page <= 1 ? 'disabled' : ''; ?>">
            <a class="page-link" href="index.php?page=<?php echo $page - 1; ?>" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
        </li>
        <?php for($i = 1; $i <= $totalPages; $i++): ?>
            <li class="page-item <?php echo $page == $i ? 'active' : ''; ?>"><a class="page-link" href="index.php?page=<?php echo $i; ?>"><?php echo $i; ?></a></li>
        <?php endfor; ?>
        <li class="page-item <?php echo $page >= $totalPages ? 'disabled' : ''; ?>">
            <a class="page-link" href="index.php?page=<?php echo $page + 1; ?>" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
            </a>
        </li>
    </ul>
</nav>
<?php require_once 'includes/footer.php'; ?>

login.php (登录页面)

<?php
// login.php
require_once 'config/database.php';
require_once 'models/User.php';
require_once 'includes/header.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $database = new Database();
    $db = $database->getConnection();
    $user = new User($db);
    $user->username = $_POST['username'];
    $user->password = $_POST['password'];
    if ($user->login()) {
        $_SESSION['user_id'] = $user->id;
        $_SESSION['username'] = $user->username;
        $_SESSION['role'] = $user->role;
        flash('message', '登录成功!', 'alert alert-success');
        redirect('index.php');
    } else {
        flash('message', '用户名或密码错误!', 'alert alert-danger');
    }
}
?>
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card">
            <div class="card-header">用户登录</div>
            <div class="card-body">
                <form action="login.php" method="POST">
                    <div class="mb-3">
                        <label for="username" class="form-label">用户名</label>
                        <input type="text" class="form-control" id="username" name="username" required>
                    </div>
                    <div class="mb-3">
                        <label for="password" class="form-label">密码</label>
                        <input type="password" class="form-control" id="password" name="password" required>
                    </div>
                    <button type="submit" class="btn btn-primary w-100">登录</button>
                </form>
                <p class="mt-3 text-center">还没有账号? <a href="register.php">立即注册</a></p>
            </div>
        </div>
    </div>
</div>
<?php require_once 'includes/footer.php'; ?>

admin/dashboard.php (管理员后台)

<?php
// admin/dashboard.php
require_once '../includes/header.php';
require_once '../includes/functions.php';
if (!isAdmin()) {
    flash('message', '您没有权限访问此页面!', 'alert alert-danger');
    redirect('index.php');
}
?>
<h2 class="mb-4">管理后台</h2>
<div class="row">
    <div class="col-md-4 mb-3">
        <div class="card text-center">
            <div class="card-body">
                <h5 class="card-title">图书管理</h5>
                <p class="card-text">添加、编辑或删除图书。</p>
                <a href="books.php" class="btn btn-primary">管理图书</a>
            </div>
        </div>
    </div>
    <div class="col-md-4 mb-3">
        <div class="card text-center">
            <div class="card-body">
                <h5 class="card-title">用户管理</h5>
                <p class="card-text">查看和管理所有用户。</p>
                <a href="users.php" class="btn btn-primary">管理用户</a>
            </div>
        </div>
    </div>
    <div class="col-md-4 mb-3">
        <div class="card text-center">
            <div class="card-body">
                <h5 class="card-title">借阅管理</h5>
                <p class="card-text">查看所有借阅记录并处理归还。</p>
                <a href="borrows.php" class="btn btn-primary">管理借阅</a>
            </div>
        </div>
    </div>
</div>
<?php require_once '../includes/footer.php'; ?>

第五步:功能扩展与完善

  1. 借阅功能:

    • book_details.php 页面,如果用户已登录且图书可借,显示一个“借阅”按钮。
    • 点击后,调用一个 Borrow 模型的方法,在 borrows 表中插入一条记录,并更新 books 表中的 available_copies 数量(减1)。
    • 需要创建一个 Borrow.php 模型和一个处理借阅请求的控制器逻辑。
  2. 归还功能:

    • my_borrows.php(用户)和 admin/borrows.php(管理员)页面,对于未归还的图书,显示一个“归还”按钮。
    • 点击后,更新 borrows 表中对应记录的 return_datestatus,并将 books 表中的 available_copies 数量加1。
  3. 搜索功能:

    • 在首页添加一个搜索框。
    • 表单提交到 search.php
    • search.php 调用 Book 模型的 search() 方法,并显示搜索结果。
  4. 表单验证:

    • 使用JavaScript在前端进行初步验证(如非空、格式等)。
    • 在PHP后端再次进行验证,防止恶意请求。
  5. 错误处理:

    • 使用 try...catch 块处理数据库操作中可能出现的异常。
    • 为用户提供友好的错误提示。
  6. 安全性:

    • SQL注入: 已通过PDO预处理语句解决。
    • XSS攻击: 已通过 htmlspecialchars() 函数解决。
    • CSRF攻击: 为表单添加CSRF令牌。
    • 密码安全: 使用 password_hash()password_verify()

这个项目为你提供了一个完整的PHP图书管理系统框架,你可以基于此进行扩展和美化,例如添加图书封面图片、实现更复杂的借阅规则(如预约、续借)、发送邮件通知等,关键在于将 MVC(模型-视图-控制器) 的思想贯彻始终,使代码结构清晰、易于维护。