Files
2026-04-28 18:37:26 +08:00

366 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MZH 图书馆管理系统
MZH Library Management 是一个基于 Java 11、Maven WAR、JSP、Servlet、JDBC DAO 和 MySQL 的 B/S 图书馆管理系统。项目以 Tomcat 部署为目标,采用传统 Java Web 分层结构,适合用于课程设计、Java Web 实训、图书馆业务原型验证,以及学习 Servlet -> Service -> DAO -> MySQL 的完整数据流。
当前仓库已经包含登录认证、角色权限、馆藏检索、图书管理、分类管理、读者档案、借阅流通、读者借阅历史、报表中心、管理员用户管理和系统日志查看等功能切片。
## 核心功能
- 登录与会话管理:通过 `/login` 登录,认证成功后进入 `/dashboard`;会话中只保存安全的 `AuthenticatedUser` 快照、角色代码和权限代码集合。
- 角色工作台:管理员、馆员、读者分别通过 `/admin/home``/librarian/home``/reader/home` 进入对应区域。
- 馆藏检索:通过 `/catalog` 按图书编号、书名、作者和分类检索图书。
- 图书管理:通过 `/books` 维护图书信息,支持新增、编辑、删除和库存状态管理。
- 图书分类管理:通过 `/book-categories` 维护图书分类,并防止删除仍被图书引用的分类。
- 读者管理:通过 `/readers` 维护读者档案、联系方式、状态和最大借阅数量。
- 借阅流通:通过 `/borrowing` 完成借书、还书、续借、逾期筛选和库存联动更新。
- 读者借阅历史:读者通过 `/reader/loans` 查看自己的借阅记录。
- 报表中心:通过 `/reports` 查看馆藏汇总、借阅汇总、逾期列表和热门图书排行。
- 用户管理:管理员通过 `/admin/users` 维护管理员、馆员和读者登录账户。
- 系统日志:管理员通过 `/admin/system-logs` 查询关键操作、审计和异常日志。
## 技术栈
| 类别 | 当前配置 |
| --- | --- |
| Java | Java 11`maven.compiler.release=11` |
| 构建工具 | MavenWAR 项目 |
| Web 技术 | Servlet 4.0、JSP、JSTL |
| Servlet API | `javax.servlet:javax.servlet-api:4.0.1``provided` |
| JSP 标签库 | `javax.servlet:jstl:1.2` |
| 数据库 | MySQLJDBC DAO 访问 |
| MySQL 驱动 | `com.mysql:mysql-connector-j:8.0.33``runtime` |
| 部署产物 | `target/library-management.war` |
| Web 配置 | `src/main/webapp/WEB-INF/web.xml``web-app` 版本 4.0 |
| 编码 | Maven 源码编码 UTF-8Web 请求通过 `CharacterEncodingFilter` 使用 UTF-8 |
建议使用兼容 Servlet 4.0 的 Tomcat 9.x 运行该 WAR。数据库脚本使用 InnoDB、`utf8mb4` 字符集和检查约束,建议使用 MySQL 8.x。
## 项目结构
```text
.
├── pom.xml
├── README.md
├── src
│ ├── main
│ │ ├── java/com/mzh/library
│ │ │ ├── controller/ Servlet 控制器,负责路由、参数读取和 JSP 转发/重定向
│ │ │ ├── dao/ DAO 接口
│ │ │ ├── dao/impl/ JDBC DAO 实现
│ │ │ ├── entity/ JavaBean、枚举、查询条件和报表对象
│ │ │ ├── exception/ DAO 异常
│ │ │ ├── filter/ 编码、登录认证、权限过滤器
│ │ │ ├── service/ Service 接口、权限策略和通用结果对象
│ │ │ ├── service/impl/ 业务服务实现
│ │ │ └── util/ JDBC、密码哈希、Session 常量等工具
│ │ ├── resources
│ │ │ ├── db/schema.sql
│ │ │ └── db.properties.example
│ │ └── webapp
│ │ ├── WEB-INF/web.xml
│ │ ├── WEB-INF/jsp/ 受保护的 JSP 页面和 JSP 片段
│ │ ├── static/css/app.css
│ │ ├── static/images/library-login.svg
│ │ └── index.jsp
│ └── test/java/com/mzh/library/service
│ └── *Check.java 服务层和权限策略自检类
└── target/ Maven 构建输出,已被 .gitignore 忽略
```
本地数据库配置文件 `src/main/resources/db.properties` 也已被 `.gitignore` 忽略。不要提交真实数据库地址、账号或密码。
## 分层设计
项目遵循以下边界:
```text
JSP/CSS 页面 -> Servlet 控制器 -> Service 业务层 -> DAO 数据访问层 -> MySQL
```
- JSP 只负责展示、表单和用户交互,不直接访问数据库,不编写业务流程。
- Servlet 负责读取请求参数、做基础格式校验、调用 Service,并决定转发 JSP 或重定向。
- Service 负责权限检查、业务规则、事务边界和跨 DAO 工作流,例如借书、还书、续借和库存更新。
- DAO 负责 SQL、JDBC 资源访问和 MySQL CRUD,不返回 HTML 或 Servlet 对象。
- 过滤器统一处理 UTF-8 编码、登录拦截和基于权限的访问控制。
## 数据库初始化
数据库脚本位于:
```text
src/main/resources/db/schema.sql
```
脚本会创建并使用数据库 `mzh_library`,包含以下核心表:
- `roles`:角色定义。
- `permissions`:权限定义。
- `role_permissions`:角色与权限的关联。
- `users`:登录账户,密码字段保存 PBKDF2 哈希。
- `system_logs`:系统操作、审计和异常日志。
- `readers`:读者档案、联系方式、状态和借阅上限。
- `book_categories`:图书分类。
- `books`:图书基础信息、分类、总册数、可借册数和状态。
- `borrow_records`:借阅、归还、续借和逾期判断所需记录。
初始化示例:
```bash
mysql -u root -p < src/main/resources/db/schema.sql
```
脚本内包含本地验证用的演示角色、权限、用户、读者、分类和图书数据。演示账户只用于本地脚手架验证;在非本地数据库中使用前应更换或删除这些数据。
本地/demo 初始登录账号如下。这些是应用登录账号,不是 MySQL 数据库账号:
| 角色 | 用户名 | 初始密码 |
| --- | --- | --- |
| 管理员 | `admin` | `admin123` |
| 馆员 | `librarian` | `librarian123` |
| 读者 | `reader` | `reader123` |
这些明文密码只用于新部署本地环境的首次验证。非本地或生产环境上线前,必须通过系统用户管理功能改密,或删除/替换这些演示账号。
`schema.sql` 使用 `INSERT IGNORE INTO users` 写入演示账号。如果目标数据库里已经存在同名 `admin``librarian``reader` 行,重新执行脚本不会覆盖现有密码哈希。需要重置本地演示账号时,优先在系统用户管理功能中修改密码;如果无法登录,可在确认这是本地/demo 数据库后执行以下 SQL:
```sql
UPDATE users
SET password_hash = 'pbkdf2_sha256$60000$Ren1B30RDysysnApRiFVaQ==$1XwzMHaALqC7dKffwjbQkilBedfAuiMOXbR/xTMr5+Y=',
active = 1
WHERE username = 'admin';
UPDATE users
SET password_hash = 'pbkdf2_sha256$60000$PV/DJwZlMRm8vy0lKMAM4g==$+Aijfop3YoPp6HTePN5r4wG8N3qgxJE+yZHkTfzfbaw=',
active = 1
WHERE username = 'librarian';
UPDATE users
SET password_hash = 'pbkdf2_sha256$60000$wBzxTIT4ep79hgEzYDV9aQ==$w3oO5iSKRSfG4++b4558yiTHy6Tz9BB2+wuV9UOAKhs=',
active = 1
WHERE username = 'reader';
```
## 本地配置
复制配置模板:
```bash
cp src/main/resources/db.properties.example src/main/resources/db.properties
```
模板内容使用以下键:
```properties
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/mzh_library?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
db.username=library_user
db.password=change_me
```
按本机 MySQL 环境调整 `db.url``db.username``db.password`。实际配置文件不应提交到版本库。
如果你希望单独创建应用数据库用户,可以在 MySQL 中按需授权,例如:
```sql
CREATE USER 'library_user'@'localhost' IDENTIFIED BY '<your-local-password>';
GRANT ALL PRIVILEGES ON mzh_library.* TO 'library_user'@'localhost';
FLUSH PRIVILEGES;
```
请将示例中的占位密码替换为本地安全密码,不要把真实密码写入 README、提交记录或共享截图。
## 构建与部署
在项目根目录执行:
```bash
mvn clean package
```
如果当前 shell 找不到 `mvn`,本工作区规范记录的 Maven 路径为:
```bash
/home/sjy/.sdkman/candidates/maven/current/bin/mvn clean package
```
构建成功后会生成:
```text
target/library-management.war
```
部署到 Tomcat 的常见方式:
1. 确认 MySQL 已启动,`mzh_library` 已初始化。
2. 确认 `src/main/resources/db.properties` 已写入本地数据库连接信息。
3. 执行 Maven 打包。
4.`target/library-management.war` 放入 Tomcat 的 `webapps/` 目录,或通过 Tomcat Manager 上传。
5. 启动或重启 Tomcat。
6. 默认情况下,WAR 文件名会形成访问上下文 `/library-management`,实际路径以 Tomcat 配置为准。
示例访问地址:
```text
http://localhost:8080/library-management/login
```
## 主要路由
以下路由来自 `src/main/webapp/WEB-INF/web.xml`、welcome-file 配置和对应 Servlet
| 路由 | 处理者 | 用途 |
| --- | --- | --- |
| `/`welcome-file: `index.jsp` | `index.jsp` | 欢迎入口,页面会重定向到 `/login` |
| `/login` | `LoginServlet` | 登录页和登录提交 |
| `/logout` | `LogoutServlet` | 退出登录 |
| `/dashboard` | `DashboardServlet` | 登录后的总览页 |
| `/admin/home` | `RoleAreaServlet` | 管理员区域首页 |
| `/librarian/home` | `RoleAreaServlet` | 馆员工作台 |
| `/reader/home` | `RoleAreaServlet` | 读者中心 |
| `/catalog` | `BookCatalogServlet` | 馆藏检索 |
| `/books``/books/new``/books/edit``/books/update``/books/delete` | `BookManagementServlet` | 图书管理 |
| `/book-categories``/book-categories/new``/book-categories/edit``/book-categories/update``/book-categories/delete` | `BookManagementServlet` | 图书分类管理 |
| `/readers``/readers/new``/readers/edit``/readers/update``/readers/delete` | `ReaderManagementServlet` | 读者档案管理 |
| `/borrowing``/borrowing/new``/borrowing/create``/borrowing/return``/borrowing/renew` | `BorrowingManagementServlet` | 借阅、归还、续借 |
| `/reader/loans` | `ReaderLoanHistoryServlet` | 当前读者借阅历史 |
| `/reports` | `ReportServlet` | 报表中心 |
| `/admin/users``/admin/users/new``/admin/users/edit``/admin/users/update``/admin/users/deactivate` | `UserManagementServlet` | 管理员用户管理 |
| `/admin/system-logs` | `SystemLogServlet` | 系统日志查询 |
| `/unauthorized` | `UnauthorizedServlet` | 无权限提示 |
静态资源路径 `/static/` 不要求登录。除 `/``/login``/unauthorized``/favicon.ico` 和静态资源外,其他页面会经过 `AuthenticationFilter``AuthorizationFilter`
## 角色与权限
系统当前包含三类角色。运行时权限由 `PermissionPolicy` 根据 `Role` 枚举授予;`schema.sql` 同时保留 `roles``permissions``role_permissions` 表和本地种子数据,但当前登录流程不会从 `role_permissions` 动态加载权限。
| 角色代码 | 显示名称 | 权限概览 |
| --- | --- | --- |
| `administrator` | 管理员 | 运行时策略授予全部 `Permission` 枚举权限;具体入口仍受路由规则约束 |
| `librarian` | 馆员 | 可管理图书、读者、借阅流通,查看报表和馆藏 |
| `reader` | 读者 | 可查看馆藏,并访问读者自己的借阅历史 |
权限代码包括:
- `manage_users`
- `manage_books`
- `manage_readers`
- `manage_borrowing`
- `view_reports`
- `view_system_logs`
- `view_catalog`
- `borrow_books`
访问控制重点:
- `/admin/system-logs` 需要 `view_system_logs`
- `/admin/**` 用户管理入口需要 `manage_users`
- `/books/**``/book-categories/**` 需要 `manage_books`
- `/readers/**` 需要 `manage_readers`
- `/borrowing/**` 需要 `manage_borrowing`
- `/reports` 需要 `view_reports`
- `/catalog` 需要 `view_catalog`
- `/reader/loans` 需要 `borrow_books`,并且必须是 `reader` 角色。
当前代码中的读者自助入口是 `/reader/loans` 借阅历史查看;借书、还书和续借操作由管理员或馆员通过 `/borrowing` 工作台处理。
## 业务规则摘要
- 图书以 `book_identifier` 作为面向用户的唯一编号。
- 图书分类名称唯一,已被图书引用的分类不能直接删除。
- 图书状态使用 `available``unavailable``archived`
- 读者以 `reader_identifier` 作为面向用户的唯一编号。
- 读者状态使用 `active``suspended``inactive`,最大借阅数量范围为 1 到 50。
- 借阅记录状态使用 `active``returned`;逾期不是单独状态,而是由未归还且 `due_at` 早于当前时间的记录推导。
- 借书会在同一事务中创建借阅记录并减少图书可借册数。
- 还书会在同一事务中标记归还并恢复可借册数。
- 续借会延长应还时间并增加续借次数,当前 MVP 对单笔借阅限制一次续借。
- 用户管理变更会写入系统日志,审计信息不应记录密码、明文凭据或密码哈希。
## 开发约定
- 保持 Servlet -> Service -> DAO -> MySQL 的层次边界。
- JSP 页面只渲染 Servlet 设置的 request/session 属性,避免 JSP scriptlet、SQL、JDBC 或业务流程。
- 受保护操作必须经过服务层权限校验,不能只依赖页面按钮是否显示。
- 新增数据库访问时使用参数化 SQL 或 PreparedStatement 风格,避免拼接用户输入。
- 多表一致性操作放在服务层事务边界中,DAO 只处理具体 SQL。
- 用户可见的页面文案、表单标签、按钮、空状态和服务反馈消息使用简体中文。
- URL、请求参数名、Java 标识符、数据库枚举值和权限代码保持现有代码形式。
- 本地私密配置只放在 `src/main/resources/db.properties`,不要提交真实凭据。
## 测试与检查
当前仓库在 `src/test/java/com/mzh/library/service/` 下包含多个自检类:
- `AuthServiceCheck`
- `BookServiceCheck`
- `BorrowingServiceCheck`
- `PermissionPolicyCheck`
- `ReaderServiceCheck`
- `ReportServiceCheck`
- `SystemLogServiceCheck`
- `UserAccountServiceCheck`
这些自检类使用 `public static void main` 和内部断言。当前 `pom.xml` 没有引入 JUnit/TestNG,也没有配置 Surefire 执行 `*Check`,因此 `mvn test` 会编译测试源码,但不会自动运行这些自检类。
常用编译和打包检查命令:
```bash
mvn test
mvn clean package
```
如需手动运行自检类,可在 `mvn test` 编译完成后执行:
```bash
for check in AuthServiceCheck BookServiceCheck BorrowingServiceCheck PermissionPolicyCheck ReaderServiceCheck ReportServiceCheck SystemLogServiceCheck UserAccountServiceCheck; do
java -cp target/classes:target/test-classes "com.mzh.library.service.${check}"
done
```
如果 Maven 不在 `PATH` 中,可使用:
```bash
/home/sjy/.sdkman/candidates/maven/current/bin/mvn test
/home/sjy/.sdkman/candidates/maven/current/bin/mvn clean package
```
文档或页面调整后的人工自查建议:
- README 中的路径、路由和依赖版本是否与 `pom.xml``web.xml``schema.sql` 一致。
- 是否意外写入真实数据库账号、密码、连接串或生产部署信息。
- JSP 中是否出现 SQL、JDBC、密码字段展示或业务逻辑 scriptlet。
- 管理入口是否仍与 `AuthorizationFilter` 的权限规则一致。
## 常见问题
### 访问页面时跳回登录页
`/``/login``/unauthorized``/favicon.ico``/static/` 外,系统默认要求登录。请确认已经登录,并检查 Tomcat 会话是否过期。当前登录会话超时时间为 30 分钟。
### 登录或数据库操作提示服务不可用
优先检查:
- MySQL 是否启动。
- `mzh_library` 是否已经通过 `schema.sql` 初始化。
- `src/main/resources/db.properties` 是否存在。
- `db.url``db.username``db.password` 是否与本地 MySQL 一致。
- MySQL Connector/J 依赖是否已被 Maven 正确下载。
### 打包后访问路径不是 `/library-management`
Maven 当前将 WAR 产物命名为 `library-management.war`。Tomcat 通常会用 WAR 文件名作为上下文路径,但如果你在 Tomcat 中手动配置了 Context,最终访问路径以 Tomcat 配置为准。
### 可以把 `db.properties` 提交吗?
不可以。`src/main/resources/db.properties` 是本地私密配置,已经被 `.gitignore` 忽略。只应提交 `src/main/resources/db.properties.example`
### 重新执行 `schema.sql` 后演示账号密码为什么没变?
`schema.sql` 使用 `INSERT IGNORE INTO users` 写入本地/demo 账号。已有同名用户时,MySQL 会跳过插入,不会覆盖现有密码哈希。需要重置时,请参考“数据库初始化”里的本地/demo 账号说明;不要在非本地数据库中直接恢复这些演示密码。
## 维护提示
`pom.xml``web.xml`、数据库表结构、角色权限、主要 JSP 路径或测试入口变化时,应同步更新本 README,避免部署步骤和功能说明与实际代码脱节。