本文目录导读:

- 系统需求分析 (核心业务)
- 数据库设计 (MySQL)
- 核心难点与解决方案 (业务逻辑)
- 后端代码示例 (以 Python Flask 为例)
- 前端页面简要设计 (Vue/HTML)
- 系统扩展点 (如何提升项目档次)
- 关键难点问题解答(面试常问)
这是一个经典的图书馆管理系统案例,我将为你提供一个从需求分析到数据库设计,再到核心代码示例的完整案例教程。
这个案例适合用于毕业设计、课程设计或作为Java Web/Python后端学习的综合练习。
系统需求分析 (核心业务)
我们做一个B/S架构(浏览器/服务器)的简易版系统,核心用户分为管理员和普通读者。
-
读者端功能:
- 图书查询:按书名、作者、ISBN(国际标准书号)模糊查询。
- 借书:查询到可借书籍后,点击借阅(要求未登录或已登录)。
- 我的借阅:查看自己当前借了哪些书,以及借书日期、应还日期。
- 续借:对即将到期且未被预约的图书进行续期操作。
- 历史记录:查看自己借过并已归还的历史记录。
-
管理员端功能:
- 图书管理:图书信息的增、删、改、查。
- 读者管理:查看所有注册读者,禁用/启用账号,查看借阅情况。
- 借阅管理:处理借书、还书操作(扫描枪或手动输入图书ID和读者ID)。
- 统计报表:统计图书总量、借出量、热门图书Top10。
数据库设计 (MySQL)
我们设计核心的4张表。
-- 1. 用户表(区分管理员和读者)
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50) UNIQUE NOT NULL,
`password` VARCHAR(100) NOT NULL, -- 实际应用中应存储哈希值
`role` ENUM('ADMIN', 'USER') DEFAULT 'USER', -- 角色
`real_name` VARCHAR(50),
`status` TINYINT DEFAULT 1 -- 1正常,0禁用
);
-- 2. 图书分类表
CREATE TABLE `category` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`category_name` VARCHAR(100) UNIQUE NOT NULL
);
-- 3. 图书表
CREATE TABLE `book` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`isbn` VARCHAR(20) UNIQUE, -- 国际标准书号 VARCHAR(200) NOT NULL,
`author` VARCHAR(100),
`publisher` VARCHAR(100),
`category_id` INT,
`total_stock` INT DEFAULT 0, -- 总库存
`available_stock` INT DEFAULT 0, -- 可借库存(剩余)
`location` VARCHAR(50), -- 书架位置
FOREIGN KEY (`category_id`) REFERENCES `category`(`id`)
);
-- 4. 借阅记录表(核心业务表)
CREATE TABLE `borrow_record` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`user_id` INT NOT NULL, -- 借阅人
`book_id` INT NOT NULL, -- 借阅的书
`borrow_date` DATETIME DEFAULT CURRENT_TIMESTAMP, -- 借书时间
`due_date` DATETIME, -- 应还时间(通常借书日期+30天)
`return_date` DATETIME NULL, -- 实际归还时间(NULL表示未还)
`status` ENUM('BORROWED', 'RETURNED', 'OVERDUE') DEFAULT 'BORROWED',
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`),
FOREIGN KEY (`book_id`) REFERENCES `book`(`id`)
);
核心难点与解决方案 (业务逻辑)
借书流程(事务控制): 这是系统的核心,必须保证“扣减库存”和“插入借阅记录”同时成功或同时失败。
-- 伪代码逻辑
START TRANSACTION;
-- 1. 检查该书是否有库存 (available_stock > 0)
SELECT available_stock FROM book WHERE id = #{bookId} FOR UPDATE; -- 加锁防止并发
-- 2. 扣减库存
UPDATE book SET available_stock = available_stock - 1 WHERE id = #{bookId};
-- 3. 插入借阅记录
INSERT INTO borrow_record (user_id, book_id, due_date) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 30 DAY));
COMMIT; -- 提交
读者借书限制: 每个读者最多同时借阅5本书。
-- 统计该读者未归还的记录数
SELECT COUNT(*) FROM borrow_record WHERE user_id = #{userId} AND return_date IS NULL;
-- 如果数量 >= 5,则拒绝借阅。
还书流程: 还书时更新记录表,并加回库存。
UPDATE borrow_record SET return_date = NOW(), status = 'RETURNED' WHERE id = #{recordId};
UPDATE book SET available_stock = available_stock + 1 WHERE id = #{bookId};
后端代码示例 (以 Python Flask 为例)
这里展示使用 Flask + MySQL 实现的关键API。
环境准备
pip install flask flask-sqlalchemy pymysql
模型定义 (Models.py 摘录)
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(100), nullable=False) # 开发环境用明文,生产环境需加密
role = db.Column(db.Enum('ADMIN', 'USER'), default='USER')
class Book(db.Model):
__tablename__ = 'book'
id = db.Column(db.Integer, primary_key=True)= db.Column(db.String(200), nullable=False)
author = db.Column(db.String(100))
isbn = db.Column(db.String(20))
available_stock = db.Column(db.Integer, default=0)
class BorrowRecord(db.Model):
__tablename__ = 'borrow_record'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
book_id = db.Column(db.Integer, db.ForeignKey('book.id'))
borrow_date = db.Column(db.DateTime, default=datetime.now)
due_date = db.Column(db.DateTime, default=lambda: datetime.now() + timedelta(days=30))
return_date = db.Column(db.DateTime, nullable=True)
# 关系映射
user = db.relationship('User', backref='records')
book = db.relationship('Book', backref='records')
借书 API 实现 (重点注意事务和并发)
from flask import jsonify, request
from sqlalchemy import func
@app.route('/api/borrow', methods=['POST'])
def borrow_book():
data = request.get_json()
user_id = data.get('userId')
book_id = data.get('bookId')
# 假设已经做了登录验证,拿到了user_id
# 1. 检查读者是否有逾期未还或超借阅数量
active_count = BorrowRecord.query.filter(
BorrowRecord.user_id == user_id,
BorrowRecord.return_date.is_(None)
).count()
if active_count >= 5:
return jsonify({'code': 400, 'msg': '借阅数量已达上限'}), 400
# 2. 开启事务锁定图书行(防止超卖)
try:
# 使用 with_for_update 锁定该行直到事务结束
book = Book.query.filter_by(id=book_id).with_for_update().first()
if not book or book.available_stock <= 0:
db.session.rollback()
return jsonify({'code': 400, 'msg': '图书库存不足'}), 400
# 3. 扣库存 + 创建记录
book.available_stock -= 1
record = BorrowRecord(user_id=user_id, book_id=book_id)
db.session.add(record)
db.session.commit()
return jsonify({'code': 200, 'msg': '借阅成功', 'due_date': record.due_date.strftime('%Y-%m-%d')})
except Exception as e:
db.session.rollback()
return jsonify({'code': 500, 'msg': str(e)}), 500
前端页面简要设计 (Vue/HTML)
- 首页:搜索栏 + 图书列表卡片(显示书名、作者、库存)。
- 借阅按钮:点击后调用
/api/borrow,成功后提示“借阅成功,请在30天内归还”。 - 个人中心:
- 列表显示“当前借阅”(状态为未还)。
- 每个借阅条目有“续借”按钮(将
due_date加15天)。
系统扩展点 (如何提升项目档次)
如果这是一个毕业设计,仅仅增删改查是不够的,可以考虑增加以下功能:
- 数据可视化:使用 ECharts 展示每月借阅量、图书分类占比。
- 消息通知:使用定时任务,查询
due_date < NOW()且return_date IS NULL的记录,给用户发送站内信或邮件提醒还书。 - 批量上传:管理员通过 Excel 批量导入图书数据(使用 poi 或 openpyxl)。
- 用户画像:根据用户的历史借阅记录,推荐相关类型的书籍。
- Redis缓存:对于图书搜索功能,将热门搜索词或搜索结果缓存到 Redis 中,降低数据库压力。
关键难点问题解答(面试常问)
- 问:如何解决“同一个人同时借同一本书”或者“超卖”的问题?
- 答:使用 MySQL 的
SELECT ... FOR UPDATE(悲观锁)锁住该行记录,确保同一时刻只有一个请求在操作该本书的库存。
- 答:使用 MySQL 的
- 问:如何检测并处理“逾期未还”?
- 答:采用两种策略。
- 主动轮询:写一个定时任务(如每天凌晨),更新状态为 OVERDUE。
- 被动计算:查询时实时计算
IF(due_date < NOW() AND return_date IS NULL, 'OVERDUE', 'NORMAL'),不需要存储状态,展示时判断即可。
- 答:采用两种策略。
- 问:权限控制如何做?
- 答:使用 JWT(JSON Web Token)生成 Token,前端在请求头中携带 Token,后端通过装饰器或拦截器验证 Token 中的
role字段,如果是ADMIN则放行管理操作,否则拒绝。
- 答:使用 JWT(JSON Web Token)生成 Token,前端在请求头中携带 Token,后端通过装饰器或拦截器验证 Token 中的
希望这个案例对你有所帮助!如果你需要针对某个特定技术栈(如 Spring Boot 或 Django)的具体实现代码,可以随时告诉我。