ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JavaWeb图书管理系统实战:环境配置、数据库设计到高分答辩全攻略

JavaWeb图书管理系统实战:环境配置、数据库设计到高分答辩全攻略 简介基于JavaWeb的图书管理系统设计与实现完整源码包适合正在准备Java方向毕业设计、期末大作业或课程设计的学生也适合希望快速理解Web项目结构的初学者。系统覆盖图书信息管理、读者信息管理、图书借还、Excel批量导入导出等常用业务场景界面布局清晰交互流程完整。代码中保留大量中文注释关键业务逻辑逐段说明文档附带数据库脚本与部署说明简单配置即可在本地跑通。压缩包共352个文件体积64.79MB核心文件包括37个Java源文件、91个编译后的class文件、5个SQL数据库脚本、14个HTML页面、12个JS脚本与13个CSS样式另有xls导出模板、jar依赖库及docx设计文档结构完整便于按模块查阅和二次开发。已有544人学习下载个人手打98分项目功能完善、管理便捷可直接作为毕业设计或期末大作业的高分基础也可在现有模块上继续扩展预约、统计等功能。1. 基于JavaWeb的图书管理系统为什么说这是一道绕不过去的经典题课设题目列表翻到底几乎都能看到「基于javaweb图书管理系统」这道题一份完整源码、一份设计文档数据库锁定 MySQL部署环境是本地 Tomcat。它的本质不是让你从零发明新框架而是把 Servlet JSP JDBC 这套三层架构完整走一遍——登录、图书增删改查、借书还书、模糊查询和分页JavaWeb 的典型考点全在这一个项目里。对刚学完 JavaWeb 的人这套源码值得亲手跑通一次请求怎么进 Servlet、数据库连接怎么管、会话怎么保持跑一遍就全通了对想省时间交差的人最值得研究的反而是文档写法因为环境搭不起来、数据库连不上、答辩讲不清才是这类项目最常见的翻车点。这篇文章就按「环境 → 建库 → 代码 → 排错 → 加分」的顺序把整个链路拆给你看。2. 先把环境立住JDK 1.8 Tomcat 8.5 MySQL 5.7 的最小可跑配置任何一份 javaweb 源码落地第一步不是急着打开 IDEA而是确认三个软件的版本能互相兼容。版本不匹配是这套流程里最大的玄学来源同一个源码在你机器上报 ClassNotFoundException换台机器就正常了背后基本都是驱动和数据库版本的问题。这一章把选型理由和部署步骤一次说清后面写代码和排错才不虚。2.1 为什么是 JSP Servlet JDBC而不是直接上 Spring Boot很多人在拿到题目后第一反应是网上找一份 Spring Boot 图书管理系统来交差。我一般不建议这么干题目已经明确写了 javaweb多数课程设计和毕业设计的验收点就是 JSP 页面、Servlet 生命周期、JDBC 数据库交互Spring Boot 把 Tomcat 和配置全部内嵌了答辩时老师问一句「请求从浏览器到数据库经过了哪几步」没有 Servlet 骨架意识的人很容易冷场。从学习角度Servlet 理解的是 HTTP 本质请求对象、响应对象、转发与重定向。后来换到 SSM、Spring Boot你会发现它们只是在 Servlet 之上包了一层路由基础没打牢整个框架对你来说就是个黑匣子报错都不知道去哪找。当然纯 JDBC 的代价也要提前知道每次查询都要写 Connection、PreparedStatement、ResultSet 三件套代码很啰嗦。这个痛点会在第 4 章用连接工具类和 Service 层缓解。如果你拿到的源码是 JSP Servlet MyBatis也没问题分层更清晰但排错时要多关注 Mapper XML 的 namespace 是否写对——这类错误 IDE 不报红运行起来才炸新手容易一脸懵。2.2 版本搭配与本地部署步骤我自己的固定搭配是 JDK 1.8 Tomcat 8.5 MySQL 5.7。这套组合下驱动、字符集、SQL 语法的兼容性最省心。用 MySQL 8.0 不是不行但连接驱动类名和时区参数都得跟着改具体差别在第 5 章单独讲。组件推荐版本说明JDK1.8稳定兼容绝大多数旧代码不建议直接上 17Tomcat8.5.x支持 Servlet 3.1JavaWeb 课设最常见容器MySQL5.7 或 8.05.7 最省心8.0 需要改驱动和时区参数IDEA2021 之后的任意版本社区版够用专业版多出来的 Spring 插件用不上确认环境的命令很简单按顺序执行即可# 1. 确认 JDK 版本输出里必须能看到 1.8 字样 java -version # 2. 确认 MySQL 服务在运行能返回版本号即可 mysql --version # 3. 启动 TomcatWindows 下运行 bin/startup.batLinux 用脚本 /opt/tomcat/bin/startup.sh # 4. 验证容器本身没问题能看到 Tomcat 默认欢迎页 curl http://localhost:8080逻辑说明java -version 确认编译环境mysql --version 确认数据库可用curl 确认 Tomcat 独立服务是否通。这三步任一失败都不要急着导项目先把本机环境修好。参数说明8080 是 Tomcat 默认端口如果你自己改过curl 的地址要跟着改。在 IDEA 里给这个 JavaWeb 项目配置 Tomcat是容易卡住的环节正确顺序如下先打开 Run/Debug Configurations点加号选 Tomcat Server → Local在 Server 页指定 Tomcat 安装目录然后切到 Deployment 页点加号选 Artifact把项目的 war exploded 添加进去最后改 Application context 为 /book。启动后访问 http://localhost:8080/book/login.jsp能看到登录页就算部署成功。2.3 拿到一份源码后的导入顺序网上流传的 javaweb 项目完整案例大多是带 MySQL 脚本和 web 目录的传统结构少量走 Maven。拿到手先看根目录这一步能省半小时根目录有 pom.xml 的用 IDEA 以 Maven 项目方式打开等依赖下载完再配置 Tomcat没有 pom.xml 的直接新建 Web Application 项目把 src 和 web 目录拷进去再把 WEB-INF/lib 下的 jar 加进 Library。数据库导入永远排在改代码之前。先建库再把编码统一成 utf8mb4mysql -u root -p library.sql导完库之后改连接配置。传统 JavaWeb 项目一般有一个 db.properties 或 jdbc.properties常见内容如下jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingUTF-8 jdbc.usernameroot jdbc.password123456参数说明driver 写 com.mysql.jdbc.Driver 是 MySQL 5.7 时代的经典写法配合 5.7 最稳如果你本地是 MySQL 8.0driver 要换成 com.mysql.cj.jdbc.DriverURL 末尾还要追加 serverTimezoneAsia/Shanghai否则连接直接报时区错误。username 和 password 改成你本地的实际账号。顺序非常重要先导库、改配置再启动项目。反过来的后果是 Tomcat 起来了登录页一提交就 HTTP 500你还得回头猜是库没导、密码错了还是驱动不对白白浪费时间。3. 数据库设计决定上线前的心情四张表定下借还书业务闭环图书管理系统这种课设表不用多但每张表之间的关系必须闭环。常见做法是四张表用户表、图书表、分类表、借阅表。设计顺序也很讲究先想业务链路再反推表结构而不是上来就抄别人的建表语句。这一章给出可以直接用的建表 SQL并说清楚每个字段为什么这么设计。3.1 从业务链路反推表结构典型业务链路是读者/管理员登录 → 查询图书 → 借书 → 还书 → 查看历史记录。管理员额外需要维护图书和分类。顺着这条链路可以推出四张核心表用户表 t_user 存登录信息和角色分类表 t_category 存图书分类图书表 t_book 存书的基本信息和库存借阅表 t_borrow 存每一次借还流水。业务环节涉及表关键字段登录t_userusername, password, role查询图书t_book, t_categorytitle, author, category_id借书t_borrow, t_bookuser_id, book_id, remain还书t_borrow, t_bookreturn_time, status, remain查看记录t_borrow, t_user, t_book三个表关联查询角色字段 role 我建议直接放用户表里用 0 表示管理员、1 表示读者而不是单独建一张管理员表。原因很简单登录功能只需要一张表、一条 SQL根据 role 的值决定跳转到读者首页还是后台管理页逻辑最简单答辩也最好讲。你要是单独建管理员表登录时得先判断用户表再查管理员表纯属给自己加戏。3.2 建表 SQL类型、默认值和外键怎么选下面是完整的建表脚本按依赖顺序执行先建无外键依赖的用户表和分类表再建图书表和借阅表CREATE DATABASE IF NOT EXISTS library DEFAULT CHARSET utf8mb4; USE library; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL COMMENT 存储加密后的密码, role TINYINT NOT NULL DEFAULT 1 COMMENT 0管理员, 1读者, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_book ( id INT PRIMARY KEY AUTO_INCREMENT, isbn VARCHAR(20) NOT NULL, title VARCHAR(100) NOT NULL, author VARCHAR(50), category_id INT, stock INT NOT NULL DEFAULT 0 COMMENT 总库存, remain INT NOT NULL DEFAULT 0 COMMENT 当前可借数量, CONSTRAINT fk_book_category FOREIGN KEY (category_id) REFERENCES t_category(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_borrow ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, book_id INT NOT NULL, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, due_time DATETIME COMMENT 应还时间, return_time DATETIME COMMENT 实际还书时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0借出, 1已还, CONSTRAINT fk_borrow_user FOREIGN KEY (user_id) REFERENCES t_user(id), CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES t_book(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明数据库和所有表统一用 utf8mb4它比 utf8 多覆盖了生僻字和表情符号配合后面第 5 章的编码配置中文乱码概率会低很多。password 字段长度留到 100因为 MD5 加密后是 32 位加盐后可能更长留足余量。t_book 里 stock 和 remain 分开存是有讲究的stock 表示总库存remain 表示当前还能借出几本报表统计时两个口径都拿得到很多简版源码只有一个 stock 字段借书时减一、还书时加一也能跑但想查总共进过多少本就废了。外键我建议加但不建议设 ON DELETE CASCADE。借阅记录是历史流水用户删了记录还在才有意义外键约束能防止你手滑把还在借阅关联的书删掉。due_time 由借书时间加 30 天算出可以在 Service 层用 Calendar 算好再写进 SQL不在建表语句里硬编码。3.3 借书和还书的 SQL 事务边界借书不是一条 INSERT 就完事。完整逻辑有三步先查 remain 是否大于 0再插入一条借阅记录最后把 remain 减一。这三条 SQL 要么全部成功要么全部失败。如果插入借阅记录成功、扣减库存失败用户拿到一本没被借出的书库存数据就废了。Connection conn JdbcUtil.getConnection(); try { conn.setAutoCommit(false); // 第一步查询库存 // 第二步INSERT 借阅记录 // 第三步UPDATE t_book SET remain remain - 1 WHERE id ? AND remain 0 conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); JdbcUtil.close(conn); }逻辑说明先关掉自动提交是因为 JDBC 默认每条 SQL 各自提交不关掉的话第二步 INSERT 成功、第三步 UPDATE 失败记录就悬空事务形同虚设。还书逻辑完全对称更新借阅记录的状态、把 remain 加一同样要包在同一个事务里。写这一步时记住一句话凡是涉及两条以上 SQL 的业务方法事务边界一定在 Service 层而不是在 DAO 层。提示JdbcUtil 是手写 JDBC 时的核心工具类负责获取连接、关闭连接。DAO 方法里不要自己 new Connection要由 Service 传入或用 ThreadLocal 传递否则事务根本串不起来。4. 从登录到借阅Servlet JSP 把核心流程跑起来表结构定稿代码就好写了。这一章给出最常见的包结构和四段关键代码登录 Servlet、登录拦截 Filter、分页查询、借书事务。JSP 只负责展示Servlet 收参数Service 管事务DAO 碰数据库四层职责分清后面扩展功能才不会拆东墙补西墙。4.1 项目分包与请求流转路径一个典型的 JavaWeb 课设源码包结构大致如下src/ ├── entity/ # 对应四张表的实体类 │ ├── User.java │ ├── Book.java │ └── Borrow.java ├── dao/ # 数据访问层只写 SQL ├── service/ # 业务层负责事务和业务判断 ├── servlet/ # 请求入口接收参数、调 service、决定跳转 ├── filter/ # 登录拦截、统一编码 └── util/ # JdbcUtil、PageBean 等工具 web/ ├── login.jsp ├── bookList.jsp └── WEB-INF/ └── lib/ # mysql-connector.jar、jstl.jar 等请求流转路径就是一个环JSP 表单的 action 指向 Servlet 映射地址Servlet 收到请求后调 serviceservice 调 daodao 返回结果后servlet 决定是 forward 转发还是 redirect 重定向。比如登录页的表单 action 写${pageContext.request.contextPath}/login请求就会进到 LoginServlet 的 doPost。保留 service 层是我特别想强调的有些简版项目把事务写在 servlet 里也能跑但借书、还书这种多步操作会让 servlet 越来越臃肿答辩时也不好讲。4.2 登录模块与 Session 会话保持登录逻辑的 Servlet 代码如下WebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password req.getParameter(password); UserService service new UserService(); User user service.login(username, password); if (user ! null) { // 登录成功用户信息放进 Session req.getSession().setAttribute(user, user); String home user.getRole() 0 ? admin/index.jsp : index.jsp; resp.sendRedirect(req.getContextPath() / home); } else { // 登录失败转发回登录页并携带错误提示 req.setAttribute(msg, 用户名或密码错误); req.getRequestDispatcher(login.jsp).forward(req, resp); } } }参数说明doPost 第一行必须设置请求编码否则 POST 提交的中文用户名在对比时会乱码。session.setAttribute 把整个 user 对象放进去后续 JSP 里直接用${sessionScope.user.username}就能显示当前登录人。登录成功用 sendRedirect作用是让浏览器地址栏变成首页地址用户刷新页面不会重复提交表单登录失败用 forward因为要保留 request 里的 msg 属性转发出去才能显示错误提示。如果你拿到的是老式 web.xml 配置的源码把WebServlet(/login)对应成 web.xml 里的servlet-mapping即可行为完全一致。4.3 图书查询分页LIMIT 的 offset 是新手重灾区图书列表不分页的话几十本书还好加进几百条测试数据后页面直接卡成 PPT。分页查询的 DAO 方法public ListBook findByPage(int page, int pageSize, String keyword) { String sql SELECT * FROM t_book WHERE title LIKE ? ORDER BY id LIMIT ?, ?; // 第 1 个参数模糊匹配关键词 // 第 2 个参数offset等于 (page - 1) * pageSize // 第 3 个参数pageSize每页条数 }Servlet 里接收分页参数的常规写法int page 1; try { page Integer.parseInt(req.getParameter(page)); } catch (NumberFormatException e) { // 用户手改 URL 为 ?pageabc 时保持第 1 页 } int pageSize 5; String keyword req.getParameter(keyword); if (keyword null) { keyword ; } PageBean pb bookService.findByPage(page, pageSize, keyword); req.setAttribute(pb, pb); req.getRequestDispatcher(bookList.jsp).forward(req, resp);参数说明page 参数从 URL 里取取不到就默认 1NumberFormatException 要主动吃掉不然用户手滑把 URL 改成?pageabc程序会直接 500这在答辩演示时非常难看。LIMIT 的 offset 是(page - 1) * pageSize而不是page * pageSize第一页从第 0 条开始第二页从第 5 条开始写错的话第二页会漏掉第一页最后一条数据。JSP 里的翻页链接要写成?page2keyword${keyword}keyword 必须原样带上否则翻到第二页模糊搜索条件就丢了。这一条做对了项目质感立刻不一样因为半数课设都挂在这里。4.4 借书事务一个 Service 方法里完成两条 SQL有了第 3.3 节的铺垫完整借书方法的代码就顺理成章了public boolean borrowBook(int userId, int bookId) { Connection conn JdbcUtil.getConnection(); try { conn.setAutoCommit(false); BookDao bookDao new BookDao(); Book book bookDao.findById(conn, bookId); if (book null || book.getRemain() 0) { return false; // 库存不足直接失败 } BorrowDao borrowDao new BorrowDao(); borrowDao.insert(conn, userId, bookId); // 带条件的 UPDATE防止并发下超借 int rows bookDao.decreaseRemain(conn, bookId); if (rows 0) { conn.rollback(); return false; } conn.commit(); return true; } catch (Exception e) { try { conn.rollback(); } catch (Exception ex) { /* 记录日志 */ } return false; } finally { JdbcUtil.close(conn); } }逻辑说明先查库存再插借阅记录最后扣库存三步都在同一个 Connection 里保证要么全部成功、要么全部失败。特别是最后一步的UPDATE t_book SET remain remain - 1 WHERE id ? AND remain 0返回受影响行数为 0 说明并发下库存被抢光了直接回滚。DAO 方法的 Connection 都是从 Service 传入的而不是自己新开——这是事务能否生效的核心。如果你见到网上某份源码里每个 DAO 都自己 getConnection那它的事务一定是假的借书方法跑十次就乱十次。5. 图书管理系统源码排查手册5 个高频翻车点的现象、原因、解决JavaWeb 课设百分之八十的时间花在排错上这是血泪经验不用慌。这一章列出我见过最多的 5 个坑按「现象 → 原因 → 解决」的顺序写照着一项项核对就能定位。5.1 中文乱码三连问页面、请求、数据库各管一段现象登录页输入中文数据库里存成??列表页显示乱码。原因要拆成三层看JSP 页面本身没声明 UTF-8POST 请求体被容器按默认编码解析数据库连接 URL 没带字符集参数还有可能是表建成了 latin1。解决方式是四个地方一起改JSP 顶部加% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %每个 Servlet 的 doPost 和 doGet 第一行写request.setCharacterEncoding(UTF-8)JDBC URL 追加useUnicodetruecharacterEncodingUTF-8建库语句统一DEFAULT CHARSETutf8mb4。只改其中一处是不够的这套流程必须成套出现。排查时还有一个技巧直接在 Navicat 里手写一条中文 INSERT如果库里能正常显示问题就在 Java 请求链路上如果库里就是问号先改 MySQL 字符集。5.2 登录页一提交就 HTTP 500现象表单提交后白屏Tomcat 日志刷出一堆 Exception。原因集中在四类SQL 语句写错列名或表名对不上数据库没导入或库名不对驱动 jar 不在 WEB-INF/lib 下Java 代码里某个对象为 null最常见是conn没拿到。解决方式永远是先看日志第一行ClassNotFoundException就是缺 jarSQLSyntaxErrorException就把 SQL 复制到 Navicat 里执行一遍空指针就打印完整堆栈定位到第几行 return null。不要一上来就改业务代码日志才是后悔药IDE 控制台里红色堆栈第一行能告诉你 80% 的真相。5.3 Tomcat 8080 端口被占用现象Tomcat 启动到一半失败日志出现Address already in use: JVM_Bind。原因基本是上一个 Tomcat 没关干净或者 IDE 内置服务占用了 8080。解决有两种一是改 Tomcat 的conf/server.xml把Connector port8080改成 8081 之类的端口同时把同文件里的 8005 和 8443 也改成不常见端口三个端口要一致改只改 8080 会造成后面 shutdown 端口冲突二是在 Windows 下执行netstat -ano | findstr 8080找到 PID然后taskkill /PID 进程号 /F强制结束进程Linux 下用lsof -i:8080定位再 kill。如果是在 IDEA 里跑的还要检查是不是重复启动了多个 Tomcat 实例。5.4 MySQL 8 与 5.7 驱动配置互不兼容现象同一份源码在 MySQL 5.7 上能跑换到 8.0 就报ClassNotFoundException: com.mysql.jdbc.Driver或Communications link failure反过来也可能。原因是 MySQL 8 开始官方驱动主类改成了com.mysql.cj.jdbc.Driver并且 URL 必须声明时区参数老版本的驱动 jar 无法建立连接。解决方式是二选一如果你必须用 MySQL 8把 db.properties 里的 driver 改成com.mysql.cj.jdbc.DriverURL 末尾加serverTimezoneAsia/Shanghai并把mysql-connector-java的 jar 换成 8.x 版本如果你的源码只配套老驱动那就老老实实装 MySQL 5.7全流程最省心。我现在的习惯是统一按 MySQL 8 处理因为新装环境基本都是 8.0早改早适应。5.5 连点两次借书库存变负现象同一本书快速点两次借书remain 从 1 变成 -1列表页出现负数。原因是没有在 Service 层判断库存或者判断了但两条并发请求同时通过了检查。解决的关键在最后一条 SQL更新库存时必须带条件WHERE id ? AND remain 0根据受影响行数是否为 0 来判断是否真的借出同时整个流程用事务包住任何一步失败都回滚。前端把借书按钮点击后置灰只是体验优化不是并发保障后端这条带条件的 UPDATE 才是兜底。我自己写完借书方法后的固定动作是开两个浏览器标签页同一本书连点借书刷新列表看 remain 有没有变成负数这个验证方法比什么单元测试都直观。6. 从「能跑」到「高分」三个加分项和一个文档习惯标着「高分项目」的源码到处都是但真正决定你得分的不是源码本身而是架构能不能讲清楚、文档能不能对齐代码。这一章给三个性价比最高的加分功能和文档写作习惯每一块都不大但对课程设计和毕业设计很管用。6.1 加分项一模糊搜索和分页放进同一个列表列表页加一个搜索框查询时把关键词传给findByPage方法SQL 写成WHERE title LIKE CONCAT(%, ?, %)。重点是翻页链接必须把 keyword 带回来否则第二页搜索条件就丢了。这个功能做完之后答辩时可以顺势讲一句「搜索和分页是共用一个 PageBean 的」这比多做一张表更让人印象深刻。6.2 加分项二用 Filter 统一做登录拦截第 4.2 节那段 LoginFilter 就是全部代码。注意放行规则必须包含 login.jsp、登录接口和静态资源否则登录页自己都会被拦下来形成死循环。功能本身不难但它体现的是「横切关注点」的思路面试和答辩都爱问。6.3 加分项三密码加盐后 MD5 存储不要在数据库里明文存密码。常见做法是用 MD5 加固定盐代码一行级别DigestUtils.md5Hex(password library_salt)没有工具类就用MessageDigest.getInstance(MD5)手工算。答辩时被问「为什么要加盐」答得上「防止用户密码撞库后被直接还原」这一句就够。注意别自己造加密算法MD5 虽然是老方案但课设层面足够重点是「不存明文」这个意识。6.4 文档比代码更值钱设计文档怎么写得像样高分文档的标准不是字数多而是每句话都对应到代码。需求分析写清楚「谁在用、有几个角色、每个角色能做什么」对应第 3.1 节的用户表和 role 字段ER 图直接按第 3.2 节四张表画实体和关联测试用例用表格写「输入、预期输出、实际结果、是否通过」能表格化就不要写段落部署步骤写 JDK/Tomcat/MySQL 版本、导入 SQL 的命令、db.properties 要改哪几个参数。文档里出现一个名词代码里就要能找到对应的类或表这是文档最值钱的地方。我当年做 JavaWeb 课设的习惯是拿一张 A4 纸把每个功能点对应到代码文件画成树比如 login.jsp → LoginServlet → UserService → UserDao答辩时老师点到任何一环都能顺着往下讲。这个习惯后来面试时也帮了我不少。项目能不能跑是底线能不能讲清楚才是高分和及格的分界线。希望帮到你。本文还有配套的精品资源点击获取
返回列表