366 lines
17 KiB
Markdown
366 lines
17 KiB
Markdown
# 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` |
|
||
| 构建工具 | Maven,WAR 项目 |
|
||
| Web 技术 | Servlet 4.0、JSP、JSTL |
|
||
| Servlet API | `javax.servlet:javax.servlet-api:4.0.1`,`provided` |
|
||
| JSP 标签库 | `javax.servlet:jstl:1.2` |
|
||
| 数据库 | MySQL,JDBC 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-8;Web 请求通过 `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,避免部署步骤和功能说明与实际代码脱节。
|