Python Flask图书馆管理系统:生产级借还书完整源代码 简介这是一套面向高校计算机专业学生、Web开发初学者及课程设计实践者的图书馆管理系统完整源代码解决图书借阅、读者管理、库存统计等典型业务场景的信息化实现问题。资源包含175个文件以23个ASPX页面如bookBorrow.aspx、bookReturn.aspx等核心业务页、26个C#后端逻辑文件、17个JS交互脚本、90个GIF界面素材为主辅以HTML模板、CSS样式、SQL Server数据库文件mdf/ldf及ASCX用户控件整体压缩包仅482KB轻量易部署。已有5713人学习下载热度持续走高。开发者可直接运行调试深入理解B/S架构下用户权限、图书 CRUD、借阅流程闭环、预约与续借逻辑等模块的前后端协同实现代码结构清晰Controller-View分离明确适合作为.NET Web Forms项目范例用于教学、毕设参考或二次定制开发。1. 图书馆管理系统完整源代码不是Demo是能跑通借还、权限分级、数据持久化的生产级最小闭环你搜“图书馆管理系统 完整源代码”大概率会点开十几个 GitHub 仓库——结果发现80% 是 Java Swing 写的单机桌面程序数据库用 HSQLDB 或 Derby连个用户登录都靠硬编码字符串剩下 20% 声称“Web 版”但只有 HTMLJS 静态页面后端空着README 里写着“待开发”。这不是系统是教学积木。真正能部署到某高校二级学院机房、让管理员每天录入新书、学生扫码查余量、期末导出借阅TOP10报表的“完整源代码”必须同时满足四个硬条件有真实数据库建模不是三张表硬凑、有角色权限隔离管理员/教师/学生行为不可越界、有业务状态机借书≠插入记录要校验可借数、超期、黑名单、有可复现的本地运行路径不依赖云服务或未公开API。本文讲的就是这样一个方案基于 Python Flask SQLite3 构建的轻量级但边界清晰的图书馆管理系统所有代码开源、无第三方 SaaS 依赖、支持 Windows/macOS/Linux 三端本地启动重点不是炫技而是把“借书-还书-续借-预约-统计”这条主链路上每个状态跳转、每条 SQL 约束、每个前端按钮背后的校验逻辑掰开揉碎写进代码注释和本篇笔记。适合想快速验证管理类系统落地逻辑的开发者、需要交课程设计但拒绝“假大空”的学生、以及正在为小型单位搭建内部资源台账的技术支持人员。2. 从零初始化用 Flask SQLite3 搭建可运行的最小骨架一个“完整”的系统起点不是写界面而是定义清楚“什么数据必须存、谁有权改、改了之后触发什么”。我们跳过所有 UI 框架选型争论直接锁定 Flask轻量、调试友好、路由语义清晰 SQLite3单文件、免安装、ACID 保障这是中小型内部系统最稳妥的组合。关键不在技术多新而在每行代码都能解释清楚“为什么非它不可”。2.1 创建项目结构与依赖管理先建立干净目录避免后续因路径混乱导致静态文件 404 或数据库找不到mkdir library-system cd library-system python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install flask flask-sqlalchemy flask-login python-dotenv提示flask-sqlalchemy封装了 ORM 映射但不掩盖原生 SQL 的必要性——比如“查询某学生最近3次借阅且未归还的图书”ORM 写法易出 N1 查询而手写 JOIN LIMIT 更可控。本项目在核心业务处保留 raw SQL 调用入口ORM 仅用于基础 CRUD。创建基础文件结构library-system/ ├── app.py # 主应用入口 ├── models.py # 数据库模型定义 ├── database.py # 数据库初始化与连接管理 ├── requirements.txt ├── .env # 环境变量如 SECRET_KEY └── templates/ # Jinja2 模板 ├── base.html ├── login.html └── dashboard.html2.2 设计符合业务约束的数据库模型图书馆系统最常翻车的点是把“图书”“读者”“借阅”三张表做成纯关系却忽略现实规则。例如同一 ISBN 可能对应多本实体书馆藏副本每本有独立条码和状态在馆/借出/遗失学生借书上限是 5 本教师是 10 本这个限制不能只靠前端按钮禁用必须在数据库层用 CHECK 约束或应用层事务控制“还书”操作不是简单更新return_date还要同步将该副本状态设为“在馆”并检查是否触发预约队列。因此models.py中的关键设计如下精简核心字段省略__repr__等辅助方法# models.py from flask_sqlalchemy import SQLAlchemy from datetime import datetime db SQLAlchemy() class User(db.Model): __tablename__ users id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(50), uniqueTrue, nullableFalse) # 登录名 password_hash db.Column(db.String(120), nullableFalse) # bcrypt 加密 role db.Column(db.String(20), nullableFalse) # admin, teacher, student created_at db.Column(db.DateTime, defaultdatetime.utcnow) class Book(db.Model): __tablename__ books id db.Column(db.Integer, primary_keyTrue) isbn db.Column(db.String(17), nullableFalse) # 标准 ISBN-13 title db.Column(db.String(200), nullableFalse) author db.Column(db.String(100)) publisher db.Column(db.String(100)) class Copy(db.Model): # 实体书副本关键 __tablename__ copies id db.Column(db.Integer, primary_keyTrue) book_id db.Column(db.Integer, db.ForeignKey(books.id), nullableFalse) barcode db.Column(db.String(20), uniqueTrue, nullableFalse) # 每本实体书唯一条码 status db.Column(db.String(20), nullableFalse) # available, borrowed, lost class BorrowRecord(db.Model): __tablename__ borrow_records id db.Column(db.Integer, primary_keyTrue) user_id db.Column(db.Integer, db.ForeignKey(users.id), nullableFalse) copy_id db.Column(db.Integer, db.ForeignKey(copies.id), nullableFalse) borrow_date db.Column(db.DateTime, defaultdatetime.utcnow) due_date db.Column(db.DateTime, nullableFalse) # 计算得出borrow_date 30天 return_date db.Column(db.DateTime, nullableTrue) # NULL 表示未归还 is_renewed db.Column(db.Boolean, defaultFalse) # 是否已续借一次为什么这样设计Copy表解耦了“书目信息”和“物理实体”避免BorrowRecord直接关联Book导致无法追踪具体哪本被借走status字段在Copy表而非BorrowRecord因为同一本书可能有 3 本副本其中 2 本在馆、1 本借出——状态属于副本不属于借阅行为due_date在创建借阅记录时即计算好borrow_date timedelta(days30)而非运行时动态算防止因系统时间篡改导致逻辑错误。2.3 初始化数据库并预置测试数据SQLite 不需要单独安装服务但首次运行需自动建表并填入基础数据如管理员账号、几本测试图书。database.py封装此逻辑# database.py from flask import current_app from models import db, User, Book, Copy def init_db(): with current_app.app_context(): db.create_all() # 创建所有表 # 预置管理员密码明文仅用于演示实际必须 bcrypt if not User.query.filter_by(usernameadmin).first(): admin User( usernameadmin, password_hashpbkdf2:sha256:260000$...$..., # 此处应为 bcrypt 加密结果 roleadmin ) db.session.add(admin) # 预置测试图书和副本 if not Book.query.filter_by(isbn978-7-02-012345-6).first(): book Book( isbn978-7-02-012345-6, title深入理解计算机系统, authorRandal E. Bryant, publisher机械工业出版社 ) db.session.add(book) db.session.flush() # 获取 book.id # 创建 3 本副本初始状态均为 available for i in range(1, 4): copy Copy( book_idbook.id, barcodefLIB-COPY-{book.id}-{i}, statusavailable ) db.session.add(copy) db.session.commit()在app.py中调用# app.py from flask import Flask from database import init_db from models import db app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] sqlite:///library.db app.config[SQLALCHEMY_TRACK_MODIFICATIONS] False app.config[SECRET_KEY] dev-key-change-in-production db.init_app(app) app.before_first_request def create_tables(): init_db() # 首次请求前初始化数据库 if __name__ __main__: app.run(debugTrue)参数说明SQLALCHEMY_DATABASE_URI sqlite:///library.db///表示相对路径生成的library.db就在项目根目录方便打包分发before_first_request替代init_app的手动调用确保每次重启服务后自动建库仅开发环境生产环境应分离初始化步骤SECRET_KEY必须在生产环境替换为随机密钥否则 session 可被伪造。3. 核心业务实现借书、还书、续借的原子化事务控制UI 可以粗糙但借还逻辑必须像银行转账一样严谨。用户点击“借书”按钮背后是跨三张表的写操作Copy.status改为borrowed、BorrowRecord插入新记录、User的当前借阅数需实时校验。任何一步失败全部回滚。本节聚焦这三条主干链路的代码实现与事务边界。3.1 借书四步校验 事务包裹借书不是“插入一条记录”而是包含前置检查的原子操作。app.py中/borrow路由实现如下# app.py from flask import request, redirect, url_for, flash from sqlalchemy.exc import IntegrityError from datetime import timedelta app.route(/borrow/int:copy_id, methods[POST]) def borrow_book(copy_id): if not current_user.is_authenticated: return redirect(url_for(login)) # Step 1: 检查副本是否存在且可借 copy Copy.query.get_or_404(copy_id) if copy.status ! available: flash(该书副本当前不可借已被借出或遗失, error) return redirect(url_for(dashboard)) # Step 2: 检查用户借阅上限按角色 borrowed_count BorrowRecord.query.filter( BorrowRecord.user_id current_user.id, BorrowRecord.return_date.is_(None) ).count() max_allowed {student: 5, teacher: 10, admin: 20}.get(current_user.role, 5) if borrowed_count max_allowed: flash(f已达借阅上限{max_allowed} 本请先归还部分图书, error) return redirect(url_for(dashboard)) # Step 3: 检查用户是否在黑名单此处简化为检查是否有超期未还记录 overdue_exists BorrowRecord.query.filter( BorrowRecord.user_id current_user.id, BorrowRecord.return_date.is_(None), BorrowRecord.due_date datetime.utcnow() ).first() if overdue_exists: flash(您有超期未还图书请先处理后再借阅, error) return redirect(url_for(dashboard)) # Step 4: 执行借阅事务内 try: # 开启事务SQLAlchemy 默认开启 copy.status borrowed record BorrowRecord( user_idcurrent_user.id, copy_idcopy.id, due_datedatetime.utcnow() timedelta(days30) ) db.session.add(record) db.session.commit() # 成功则提交 flash(f借阅成功副本 {copy.barcode} 已登记应还日期{record.due_date.strftime(%Y-%m-%d)}, success) except IntegrityError as e: db.session.rollback() flash(借阅失败系统异常请重试, error) except Exception as e: db.session.rollback() flash(借阅失败未知错误, error) return redirect(url_for(dashboard))关键细节说明get_or_404防止 URL 篡改如/borrow/999999return_date.is_(None)是 SQLAlchemy 的 NULL 判断语法比 None更安全timedelta(days30)硬编码为 30 天实际项目应从配置表读取不同角色的借期IntegrityError捕获外键冲突、唯一约束等数据库层错误rollback()确保copy.status不被错误修改。3.2 还书状态联动与预约触发还书逻辑更复杂不仅要更新return_date和Copy.status还要检查是否有读者预约了这本书并自动通知首位预约者。此处用原始 SQL 实现高效更新避免 ORM 多次查询# app.py app.route(/return/int:record_id, methods[POST]) def return_book(record_id): record BorrowRecord.query.get_or_404(record_id) # 检查是否已归还 if record.return_date is not None: flash(该记录已归还无需重复操作, info) return redirect(url_for(my_borrows)) try: # 原子化更新设置归还时间 副本状态 db.session.execute( UPDATE copies SET status available WHERE id :copy_id, {copy_id: record.copy_id} ) record.return_date datetime.utcnow() # 检查该书是否有预约假设预约表 reservation_requests 存在 # 此处简化只查最早预约实际需加锁防止并发 pending_reservation db.session.execute( SELECT id, user_id FROM reservation_requests WHERE book_id (SELECT book_id FROM copies WHERE id :copy_id) AND status pending ORDER BY created_at LIMIT 1, {copy_id: record.copy_id} ).fetchone() if pending_reservation: # 更新预约状态为 fulfilled并通知用户此处仅打印日志 db.session.execute( UPDATE reservation_requests SET status fulfilled WHERE id :rid, {rid: pending_reservation[0]} ) flash(f已归还且触发预约通知用户 {pending_reservation[1]} 可来领取, success) else: flash(归还成功副本已恢复可借状态, success) db.session.commit() except Exception as e: db.session.rollback() flash(归还失败请重试, error) return redirect(url_for(my_borrows))为什么用 raw SQL预约队列查询涉及copies → books → reservation_requests三级关联ORM 需多次.join()而此处只需一行SELECT ... LIMIT 1UPDATE copies和UPDATE reservation_requests是两个独立语句但共享同一个db.session仍受事务保护。3.3 续借防重复与期限叠加规则续借不是无限延长规则是每人每本书仅可续借 1 次且续借后归还日期 当前due_date 15 天非原始借阅日。BorrowRecord.is_renewed字段即为此而设app.route(/renew/int:record_id, methods[POST]) def renew_book(record_id): record BorrowRecord.query.get_or_404(record_id) if record.return_date is not None: flash(该记录已归还无法续借, error) return redirect(url_for(my_borrows)) if record.is_renewed: flash(该图书已续借过一次不可再次续借, error) return redirect(url_for(my_borrows)) try: record.due_date record.due_date timedelta(days15) record.is_renewed True db.session.commit() flash(f续借成功新应还日期{record.due_date.strftime(%Y-%m-%d)}, success) except Exception as e: db.session.rollback() flash(续借失败请重试, error) return redirect(url_for(my_borrows))玄学注意点续借日期必须基于record.due_date当前到期日计算而非record.borrow_date原始借阅日否则用户第一次借阅 30 天续借又得 30 天变成 60 天违背“续借是延期”而非“重置借期”的本意is_renewed字段必须在commit()前设为True否则并发请求可能同时通过if not record.is_renewed判断。4. 权限与安全角色隔离、密码加固、防越权访问一个“完整”的系统安全性不是附加功能而是架构基底。本系统采用三层防护路由级角色拦截、数据级行级权限、密码存储强制加密。没有“超级管理员能看所有数据”的后门也没有明文密码。4.1 基于 Flask-Login 的角色路由守卫Flask-Login提供login_required但默认不区分角色。我们扩展User模型添加角色检查方法并在关键路由上装饰# models.py class User(UserMixin, db.Model): # ... 字段定义同上 ... def can_manage_books(self): return self.role in [admin, teacher] def can_view_reports(self): return self.role admin # app.py from functools import wraps def role_required(*roles): def decorator(f): wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: return redirect(url_for(login)) if current_user.role not in roles: flash(权限不足无法访问此页面, error) return redirect(url_for(dashboard)) return f(*args, **kwargs) return decorated_function return decorator # 使用示例 app.route(/admin/books) role_required(admin, teacher) def manage_books(): books Book.query.all() return render_template(manage_books.html, booksbooks) app.route(/admin/reports) role_required(admin) def view_reports(): # 只有 admin 能进 return render_template(reports.html)参数说明role_required(admin, teacher)比if role ! admin更易维护权限变更只需改装饰器can_manage_books()方法封装业务语义未来若增加“高级教师”角色只需修改此方法无需遍历所有路由。4.2 行级数据权限用户只能操作自己的借阅记录即使 URL 被猜中如/return/123也必须校验record.user_id current_user.id。在/return/int:record_id路由开头加入# app.py app.route(/return/int:record_id, methods[POST]) def return_book(record_id): record BorrowRecord.query.get_or_404(record_id) # 行级权限检查用户只能归还自己借的书 if record.user_id ! current_user.id: flash(无权操作他人借阅记录, error) return redirect(url_for(my_borrows)) # ... 后续逻辑为什么必须做前端隐藏“还书”按钮不等于安全攻击者可直接构造 POST 请求get_or_404只校验记录存在不校验归属这是最常见的越权漏洞Insecure Direct Object Reference, IDOR。4.3 密码安全bcrypt 加密与登录风控明文存密码是致命错误。使用bcrypt通过flask-bcrypt或passlibpip install flask-bcrypt# app.py from flask_bcrypt import Bcrypt bcrypt Bcrypt(app) # 用户注册时加密 app.route(/register, methods[POST]) def register(): password_hash bcrypt.generate_password_hash(request.form[password]).decode(utf-8) user User( usernamerequest.form[username], password_hashpassword_hash, rolestudent ) db.session.add(user) db.session.commit() # 登录时校验 app.route(/login, methods[POST]) def login(): user User.query.filter_by(usernamerequest.form[username]).first() if user and bcrypt.check_password_hash(user.password_hash, request.form[password]): login_user(user) return redirect(url_for(dashboard)) else: flash(用户名或密码错误, error)避坑 / 常见问题 / 排查现象注册后登录失败bcrypt.check_password_hash总返回False原因generate_password_hash返回的是bytes类型若直接存入String字段如password_hash db.Column(db.String(120))会隐式调用str()导致 hash 被破坏。解决.decode(utf-8)转为字符串再存或数据库字段改为LargeBinary但String更通用。现象flask-bcrypt报错AttributeError: Bcrypt object has no attribute generate_password_hash原因未正确初始化Bcrypt(app)或app对象未传入。解决确认bcrypt Bcrypt()在app创建后调用bcrypt.init_app(app)或直接Bcrypt(app)。现象登录成功后current_user为AnonymousUserMixin无法访问current_user.username原因User模型未继承UserMixin或load_user回调函数未正确定义。解决在models.py中class User(UserMixin, db.Model):在app.py中添加from flask_login import LoginManager login_manager LoginManager() login_manager.init_app(app) login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id))现象管理员能查看所有借阅记录但学生也能通过修改 URL 参数看到他人记录原因/user/borrows路由只做了login_required未加role_required或行级校验。解决在my_borrows视图中查询BorrowRecord时强制filter(BorrowRecord.user_id current_user.id)而非查全部再前端过滤。现象SQLite 数据库文件被意外删除所有数据丢失原因开发时习惯性rm library.db但未备份。解决在database.py的init_db()中增加判断若library.db不存在则执行初始化若存在跳过建表但可提供--force-reinitCLI 参数。生产环境必须定期sqlite3 library.db .dump backup.sql。5. 部署与验证从本地运行到 Docker 容器化的一键脚本“完整源代码”的终极检验是脱离开发机能在任意一台干净机器上一键拉起。本节提供两种方案纯 Python 本地部署适合教学演示和Docker 容器化适合真实部署并给出验证清单确保每项功能可测。5.1 本地一键启动requirements.txt 启动脚本requirements.txt必须锁定版本避免pip install时拉取不兼容新版# requirements.txt Flask2.3.3 Flask-SQLAlchemy3.0.5 Flask-Login0.6.3 Flask-Bcrypt1.0.1 Werkzeug2.3.7 click8.1.7 itsdangerous2.1.2 Jinja23.1.2 MarkupSafe2.1.3创建run.shLinux/macOS或run.batWindows#!/bin/bash # run.sh python -m venv venv source venv/bin/activate pip install -r requirements.txt export FLASK_APPapp.py export FLASK_ENVdevelopment flask run --host0.0.0.0:5000Windowsrun.batecho off python -m venv venv venv\Scripts\activate.bat pip install -r requirements.txt set FLASK_APPapp.py set FLASK_ENVdevelopment flask run --host0.0.0.0:5000启动后验证清单逐项手动测试功能验证步骤预期结果数据库初始化启动后检查项目目录是否生成library.db文件文件存在大小 0KB管理员登录浏览器访问http://localhost:5000/login输入admin/admin密码为预置 bcrypt 值跳转至dashboard右上角显示“欢迎admin”学生借书用学生账号登录 → 进入图书列表 → 点击“借阅” → 查看copies表status是否变为borrowedlibrary.db中copies.status字段更新成功越权访问拦截学生登录后手动修改浏览器地址栏为/admin/books页面跳转至dashboard提示“权限不足”并发借阅控制用两个浏览器标签页同一学生账号快速连续点击同一本书的“借阅”按钮仅第一条请求成功第二条提示“该书副本当前不可借”5.2 Docker 容器化标准化运行环境对于需要交付给某实验室或某公司 IT 部门的场景Docker 是最佳选择。Dockerfile如下# Dockerfile FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . # 暴露端口 EXPOSE 5000 # 创建非 root 用户安全最佳实践 RUN adduser --disabled-password --gecos appuser USER appuser CMD [gunicorn, --bind, 0.0.0.0:5000, --workers, 2, app:app]构建与运行docker build -t library-system . docker run -p 5000:5000 -v $(pwd)/data:/app/data library-system注意-v $(pwd)/data:/app/data将宿主机data/目录挂载到容器内确保library.db持久化。容器重启后数据不丢失。5.3 关键参数与可调配置项系统中所有可配置项集中到.env文件避免硬编码# .env FLASK_APPapp.py FLASK_ENVdevelopment SECRET_KEYyour-secret-key-change-me-in-production DATABASE_PATHlibrary.db MAX_BORROW_STUDENT5 MAX_BORROW_TEACHER10 BORROW_DAYS30 RENEW_DAYS15在app.py中加载from flask import Flask from flask_sqlalchemy import SQLAlchemy from dotenv import load_dotenv import os load_dotenv() # 加载 .env app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] fsqlite:///{os.getenv(DATABASE_PATH, library.db)} app.config[SECRET_KEY] os.getenv(SECRET_KEY, dev-key) # 业务参数 MAX_BORROW { student: int(os.getenv(MAX_BORROW_STUDENT, 5)), teacher: int(os.getenv(MAX_BORROW_TEACHER, 10)), } BORROW_PERIOD int(os.getenv(BORROW_DAYS, 30)) RENEW_PERIOD int(os.getenv(RENEW_DAYS, 15))为什么参数化某高校要求学生借阅上限为 3 本教师为 8 本只需改.env无需动代码BORROW_PERIOD从 30 天改为 21 天影响所有新借阅记录的due_date计算一处修改全局生效。6. 生产就绪技巧日志审计、备份策略与性能压测临界点写完代码只是开始让系统真正“可用”得解决三个现实问题谁在什么时候干了什么审计、数据丢了怎么办备份、并发高了会不会崩压测。这些不是锦上添花而是决定系统能否被信任交付的核心能力。6.1 借阅/归还操作全链路日志审计SQLite 本身不支持细粒度审计日志但我们可以在应用层补全。创建audit_log.py# audit_log.py from datetime import datetime from models import db class AuditLog(db.Model): __tablename__ audit_logs id db.Column(db.Integer, primary_keyTrue) user_id db.Column(db.Integer, nullableFalse) action db.Column(db.String(50), nullableFalse) # borrow, return, renew target_type db.Column(db.String(20), nullableFalse) # book, copy, user target_id db.Column(db.Integer, nullableFalse) details db.Column(db.Text) # JSON 字符串记录关键字段 ip_address db.Column(db.String(45)) created_at db.Column(db.DateTime, defaultdatetime.utcnow) def log_action(user_id, action, target_type, target_id, detailsNone, ip_addressNone): log AuditLog( user_iduser_id, actionaction, target_typetarget_type, target_idtarget_id, detailsstr(details) if details else None, ip_addressip_address ) db.session.add(log) # 注意不在此 commit由调用方统一 commit在借书逻辑末尾插入日志# app.py from audit_log import log_action app.route(/borrow/int:copy_id, methods[POST]) def borrow_book(copy_id): # ... 前置校验 ... try: # ... 执行借阅 ... db.session.commit() # 记录审计日志 log_action( user_idcurrent_user.id, actionborrow, target_typecopy, target_idcopy.id, details{barcode: copy.barcode, isbn: copy.book.isbn}, ip_addressrequest.remote_addr ) db.session.commit() # 提交日志 except Exception as e: db.session.rollback() # ... 错误处理 ...价值点当管理员发现某本书被异常借出可查audit_logs表精确到 IP、时间、操作人details字段存 JSON 字符串如{barcode: LIB-COPY-1-1, isbn: 978-7-02-012345-6}便于后期用json_extract查询。6.2 数据库自动备份策略每日快照 保留7天SQLite 单文件优势在此刻体现备份 复制文件。编写backup.py# backup.py import shutil import os from datetime import datetime def backup_database(db_pathlibrary.db, backup_dirbackups): if not os.path.exists(backup_dir): os.makedirs(backup_dir) timestamp datetime.now().strftime(%Y%m%d_%H%M%S) backup_name flibrary_{timestamp}.db backup_path os.path.join(backup_dir, backup_name) try: shutil.copy2(db_path, backup_path) print(f备份成功{backup_path}) # 清理7天前的备份 cutoff datetime.now().timestamp() - 7 * 24 * 3600 for f in os.listdir(backup_dir): if f.startswith(library_) and f.endswith(.db): fpath os.path.join(backup_dir, f) if os.path.getctime(fpath) cutoff: os.remove(fpath) print(f已清理过期备份{fpath}) except Exception as e: print(f备份失败{e}) if __name__ __main__: backup_database()加入 crontabLinux/macOS或 Windows 任务计划程序每天凌晨 2 点执行# Linux crontab -e 0 2 * * * cd /path/to/library-system python backup.py /var/log/library-backup.log 216.3 性能压测找出并发瓶颈与优化临界点用locust模拟真实压力测试系统承载力pip install locustlocustfile.py# locustfile.py from locust import HttpUser, task, between import random class LibraryUser(HttpUser): wait_time between(1, 5) task( p a hrefhttps://download.csdn.net/download/huangsong_265/3182007 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p