初始提交:图书管理系统源码
Flask + SQLite 分层架构的图书馆借阅管理系统 规格驱动开发(Spec → Models → Services → Routes → UI)
系统覆盖图书馆业务全流程:图书管理 / 读者管理 / 借阅(借书 · 还书 · 续借)/ 逾期管理 / 数据概览,同时提供 REST API 与管理端 Web 界面。
项目采用 规格驱动 的组织方式:先由 case/spec/API_SPEC.md 明确实体、接口与业务规则(Spec 层),再按 models(实体与仓储)→ services(业务规则)→ routes(REST 接口)→ templates + static(管理端界面)逐层实现,使业务约束集中在 Service 层、可被 pytest 直接验证。
case/spec/API_SPEC.md
models
services
routes
templates + static
data-view="dashboard"
data-view="books"
data-view="users"
data-view="loans"
data-view="history"
create_app
sqlite3
Row
dataclass
依赖清单(case/requirements.txt):
case/requirements.txt
flask>=3.0,<4 pytest>=8.0,<9
图书管理系统/ ├── case/ # 案例实现(主程序) │ ├── app.py # 应用入口:create_app / 路由 / 启动 │ ├── config.py # 配置:SQLite 数据库路径 │ ├── requirements.txt │ ├── README.md # 案例目录内的精简说明 │ ├── spec/ │ │ └── API_SPEC.md # 规格文档(实体 / API / 业务规则) │ ├── models/ # 数据层 │ │ ├── __init__.py # 导出 init_db 与实体 │ │ ├── database.py # 连接管理、建表、增量迁移、初始化 │ │ ├── book.py # Book 实体 + BookRepository │ │ ├── user.py # User 实体 + UserRepository │ │ ├── loan.py # Loan 实体 + LoanRepository │ │ └── seed.py # 首次启动写入演示数据 │ ├── services/ # 业务层 │ │ ├── loan_service.py # 借书 / 还书 / 续借规则校验 │ │ └── stats_service.py # 概览统计聚合 │ ├── routes/ # 接口层(Blueprint) │ │ ├── __init__.py # register_blueprints 统一注册 │ │ ├── books.py # /api/books │ │ ├── users.py # /api/users │ │ ├── loans.py # /api/loans │ │ └── stats.py # /api/stats │ ├── tests/ │ │ └── test_loan_service.py # 借阅业务单元测试 │ ├── templates/index.html # 管理端单页界面 │ ├── static/ │ │ ├── css/main.css │ │ └── js/main.js # 视图切换、表格渲染、弹窗与 API 调用 │ └── data/library.db # SQLite 数据库文件(运行时生成) ├── start_case.bat # 一键:装依赖 → 跑测试 → 起服务 ├── export_word.py # 研究报告 Markdown → Word 转换脚本 ├── 研究报告.docx # 课程研究报告 └── 安装部署说明.pdf # 安装部署说明
数据库文件:case/data/library.db(可通过环境变量 BOOK_DB_PATH 覆盖)。
case/data/library.db
BOOK_DB_PATH
id
isbn
title
author
category
publisher
shelf
copies_total
copies_available
created_at
card_no
name
email
phone
department
book_id
user_id
loan_date
due_date
return_date
NULL
索引:idx_loans_active(return_date)、idx_books_title(title)。
idx_loans_active(return_date)
idx_books_title(title)
增量迁移:models/database.py 中的 _migrate() 会通过 PRAGMA table_info 检查字段是否存在,为旧版本数据库自动补齐 category、publisher、shelf、phone、department 等新增字段,保证历史数据文件可直接升级。
models/database.py
_migrate()
PRAGMA table_info
规则集中在 services/loan_service.py,常量与 spec/API_SPEC.md 第 4 节严格一致:
services/loan_service.py
spec/API_SPEC.md
MIN_LOAN_DAYS = 1
MAX_LOAN_DAYS = 90
LoanServiceError
MAX_ACTIVE_LOANS_PER_USER = 5
UPDATE ... WHERE copies_available > 0
return_date IS NULL
extra_days
copies_available == copies_total
ValueError("总册数不能小于已借出数量")
借阅状态(Loan.to_dict() 计算得出,非存储字段):
Loan.to_dict()
returned
overdue
due_date < 今天
active
days_left
完整规格见 case/spec/API_SPEC.md。服务基础地址:http://127.0.0.1:5005
http://127.0.0.1:5005
/
/health
{"status": "ok"}
/api/stats
/api/stats/dashboard
返回字段:book_titles(书目数)、total_copies(总册数)、available_copies、borrowed_copies、reader_count、active_loans、overdue_loans、returned_total、by_category(分类分布数组)。
book_titles
total_copies
available_copies
borrowed_copies
reader_count
active_loans
overdue_loans
returned_total
by_category
/api/books
/api/books?q=&category=
/api/books/categories
/api/books/<id>
/api/users
/api/users?q=
/api/loans
/api/loans?overdue=1
overdue=1
/api/loans/history?limit=50
limit
/api/loans/borrow
{"book_id":1,"user_id":1,"days":30}
/api/loans/<id>/return
/api/loans/<id>/renew
{"days":14}
业务校验失败统一返回 400 与 {"error": "..."};资源不存在返回 404;创建成功返回 201。
400
{"error": "..."}
404
201
无需安装数据库服务——使用 Python 内置 SQLite。
双击根目录的 start_case.bat,脚本会依次:
start_case.bat
pip install -r requirements.txt -q
python -m pytest tests -q
python app.py
cd case # 1. 安装依赖(建议先创建虚拟环境) python -m venv venv venv\Scripts\activate # Windows # source venv/bin/activate # macOS / Linux pip install -r requirements.txt # 2. 运行测试 python -m pytest tests -q # 3. 启动服务 python app.py
启动后访问 http://127.0.0.1:5005/
首次启动时 init_db() 会自动检测 books 表是否为空,为空则写入演示数据(models/seed.py):
init_db()
books
models/seed.py
如需重新加载演示数据,删除 case/data/library.db 后重启即可。
测试文件:case/tests/test_loan_service.py,共 7 个用例,通过 monkeypatch 将数据库指向 pytest 的临时目录,每个用例使用独立数据库,互不干扰。
case/tests/test_loan_service.py
monkeypatch
test_borrow_and_return
test_borrow_no_stock
test_renew_extends_due_date
test_borrow_days_out_of_range
test_borrow_days_boundary_ok
test_max_active_loans
test_can_borrow_after_return_when_at_limit
运行:
cd case python -m pytest tests -q
其他运行参数(case/app.py):
case/app.py
127.0.0.1
5005
debug=True
# 健康检查 curl http://127.0.0.1:5005/health # 查看概览统计 curl http://127.0.0.1:5005/api/stats/dashboard # 新增图书 curl -X POST http://127.0.0.1:5005/api/books \ -H "Content-Type: application/json" \ -d "{\"isbn\":\"978-7-111-99999-9\",\"title\":\"软件工程\",\"author\":\"Sommerville\",\"copies_total\":3,\"category\":\"计算机\"}" # 搜索计算机类图书 curl "http://127.0.0.1:5005/api/books?q=软件&category=计算机" # 注册读者 curl -X POST http://127.0.0.1:5005/api/users \ -H "Content-Type: application/json" \ -d "{\"card_no\":\"2024099\",\"name\":\"赵六\",\"department\":\"计算机学院\"}" # 借书 curl -X POST http://127.0.0.1:5005/api/loans/borrow \ -H "Content-Type: application/json" \ -d "{\"book_id\":1,\"user_id\":1,\"days\":30}" # 归还 / 续借 curl -X POST http://127.0.0.1:5005/api/loans/1/return curl -X POST http://127.0.0.1:5005/api/loans/1/renew \ -H "Content-Type: application/json" -d "{\"days\":14}" # 查看逾期记录 curl "http://127.0.0.1:5005/api/loans?overdue=1"
1. 端口 5005 被占用 修改 case/app.py 末尾 create_app().run(...) 中的 port 参数,或释放该端口。
create_app().run(...)
port
2. 演示数据没有出现 seed_if_empty() 仅在 books 表记录数为 0 时写入。若数据库已有数据,可删除 case/data/library.db 后重启。
seed_if_empty()
3. 新增图书提示「ISBN 可能重复」 books.isbn 有唯一约束,请更换 ISBN。
books.isbn
4. 删除图书失败 业务规则限制:仅当该书无在借记录(copies_available == copies_total)时才可删除,请先办理归还。
5. 修改图书总册数报错「总册数不能小于已借出数量」 新总册数不得低于当前已借出数量(copies_total - copies_available)。
copies_total - copies_available
6. 运行 export_word.py 提示未找到 研究报告.md 该脚本期望读取根目录的 研究报告.md 并生成 研究报告.docx。当前仓库只保留了导出后的 研究报告.docx,如需重新生成,请先补回 研究报告.md,并安装依赖:
export_word.py
研究报告.md
研究报告.docx
pip install python-docx python export_word.py
版权所有:中国计算机学会技术支持:开源发展技术委员会 京ICP备13000930号-9 京公网安备 11010802047560号
智慧图书馆管理系统
Flask + SQLite 分层架构的图书馆借阅管理系统
规格驱动开发(Spec → Models → Services → Routes → UI)
一、项目简介
系统覆盖图书馆业务全流程:图书管理 / 读者管理 / 借阅(借书 · 还书 · 续借)/ 逾期管理 / 数据概览,同时提供 REST API 与管理端 Web 界面。
项目采用 规格驱动 的组织方式:先由
case/spec/API_SPEC.md明确实体、接口与业务规则(Spec 层),再按models(实体与仓储)→services(业务规则)→routes(REST 接口)→templates + static(管理端界面)逐层实现,使业务约束集中在 Service 层、可被 pytest 直接验证。二、功能模块
data-view="dashboard"data-view="books"data-view="users"data-view="loans"data-view="history"三、技术栈
create_app+ Blueprint 蓝图)sqlite3,Row行工厂 + 上下文管理器管理事务)dataclass实体 + 静态方法 Repository(无 ORM 依赖)依赖清单(
case/requirements.txt):四、目录结构
五、数据库设计
数据库文件:
case/data/library.db(可通过环境变量BOOK_DB_PATH覆盖)。books(图书)
idisbntitleauthorcategorypublishershelfcopies_totalcopies_availablecreated_atusers(读者)
idcard_nonameemailphonedepartmentcreated_atloans(借阅记录)
idbook_iduser_idloan_datedue_datereturn_dateNULL表示在借索引:
idx_loans_active(return_date)、idx_books_title(title)。六、业务规则
规则集中在
services/loan_service.py,常量与spec/API_SPEC.md第 4 节严格一致:MIN_LOAN_DAYS = 1~MAX_LOAN_DAYS = 90,超出抛出LoanServiceErrorMAX_ACTIVE_LOANS_PER_USER = 5,每位读者同时最多在借 5 册UPDATE ... WHERE copies_available > 0原子扣减,失败即「无可借册数」return_date IS NULL时生效,成功后库存 +1extra_days可调)copies_available == copies_total(无在借)时允许删除ValueError("总册数不能小于已借出数量")借阅状态(
Loan.to_dict()计算得出,非存储字段):returned:已归还overdue:未归还且due_date < 今天active:未归还且未到期days_left(剩余天数,可为负数)七、API 接口
完整规格见
case/spec/API_SPEC.md。服务基础地址:http://127.0.0.1:5005系统
//health{"status": "ok"}统计
/api/stats/api/stats/dashboard返回字段:
book_titles(书目数)、total_copies(总册数)、available_copies、borrowed_copies、reader_count、active_loans、overdue_loans、returned_total、by_category(分类分布数组)。图书
/api/books/api/books?q=&category=/api/books/categories/api/books/<id>/api/booksisbn/title/author,copies_total至少为 1;ISBN 重复返回 409)/api/books/<id>title/author/category/publisher/shelf/copies_total)/api/books/<id>读者
/api/users/api/users?q=active_loans在借册数/api/userscard_no/name;证号重复返回 409)借阅
/api/loans/api/loans?overdue=1overdue=1时仅返回逾期记录/api/loans/history?limit=50limit上限 200)/api/loans/borrow{"book_id":1,"user_id":1,"days":30}/api/loans/<id>/return/api/loans/<id>/renew{"days":14}(默认 14 天)业务校验失败统一返回
400与{"error": "..."};资源不存在返回404;创建成功返回201。八、快速开始
8.1 环境要求
无需安装数据库服务——使用 Python 内置 SQLite。
8.2 一键启动(Windows)
双击根目录的
start_case.bat,脚本会依次:pip install -r requirements.txt -qpython -m pytest tests -qpython app.py8.3 手动启动
启动后访问 http://127.0.0.1:5005/
8.4 演示数据
首次启动时
init_db()会自动检测books表是否为空,为空则写入演示数据(models/seed.py):如需重新加载演示数据,删除
case/data/library.db后重启即可。九、测试
测试文件:
case/tests/test_loan_service.py,共 7 个用例,通过monkeypatch将数据库指向 pytest 的临时目录,每个用例使用独立数据库,互不干扰。test_borrow_and_returntest_borrow_no_stocktest_renew_extends_due_datetest_borrow_days_out_of_rangetest_borrow_days_boundary_oktest_max_active_loanstest_can_borrow_after_return_when_at_limit运行:
十、配置项
BOOK_DB_PATHcase/data/library.db其他运行参数(
case/app.py):127.0.0.15005debug=True十一、使用示例
十二、常见问题
1. 端口 5005 被占用 修改
case/app.py末尾create_app().run(...)中的port参数,或释放该端口。2. 演示数据没有出现
seed_if_empty()仅在books表记录数为 0 时写入。若数据库已有数据,可删除case/data/library.db后重启。3. 新增图书提示「ISBN 可能重复」
books.isbn有唯一约束,请更换 ISBN。4. 删除图书失败 业务规则限制:仅当该书无在借记录(
copies_available == copies_total)时才可删除,请先办理归还。5. 修改图书总册数报错「总册数不能小于已借出数量」 新总册数不得低于当前已借出数量(
copies_total - copies_available)。6. 运行
export_word.py提示未找到研究报告.md该脚本期望读取根目录的研究报告.md并生成研究报告.docx。当前仓库只保留了导出后的研究报告.docx,如需重新生成,请先补回研究报告.md,并安装依赖:十三、说明
models层以dataclass承载实体、以静态方法类充当 Repository。spec/API_SPEC.md保持同步,修改规则时需同时更新规格文档与测试用例。