// 声明该类所在的包名为 javabean package javabean; // 导入// 声明该类所在的包名为 javabean package javabean; // 导入 java.sql 包中的 Connection 类,用于建立与数据库的连接 import java.sql.Connection; // 导入 java.sql 包中的 PreparedStatement 类,用于执行预编译的 SQL 语句 import java.sql.PreparedStatement; // 导入 java.sql 包中的 ResultSet 类,用于存储数据库查询结果 import java.sql.ResultSet; // 导入 java.sql 包中的 SQLException 类,用于处理 SQL 操作中可能出现的异常 import java.sql.SQLException; // 定义一个名为 Reader 的公共类,用于处理读者登录相关操作 public class Reader { // 抑制“可能存在空指针引用”的警告 @SuppressWarnings("null") /** * 该方法用于处理读者的登录逻辑 * @param user 读者的账号 * @param psw 读者的密码 * @return 返回登录结果的提示信息 * @throws ClassNotFoundException 如果在加载数据库驱动类时出现问题,抛出该异常 * @throws SQLException 如果在执行 SQL 操作时出现问题,抛出该异常 */ public String login(String user, String psw) throws ClassNotFoundException, SQLException { // 检查账号是否为空或仅包含空白字符 if (user == null || user.trim().equals("")) { // 如果账号为空,返回提示信息 return "账号不能为空"; } // 检查密码是否为空或仅包含空白字符 else if (psw == null || psw.trim().equals("")) { // 如果密码为空,返回提示信息 return "密码不能为空"; } // 声明一个 Connection 对象,用于存储与数据库的连接,初始值为 null Connection connection = null; // 声明一个 PreparedStatement 对象,用于执行预编译的 SQL 语句,初始值为 null PreparedStatement pstmt = null; // 声明一个 ResultSet 对象,用于存储数据库查询结果,初始值为 null ResultSet resultSet = null; // 定义一个 SQL 查询语句,使用占位符 ? 来防止 SQL 注入 String sql = "select * from borrow_card where ID=? and PASSWORD=?"; // 调用 Base 类的 getConnection 方法获取数据库连接,并将其赋值给 connection 对象 connection = Base.getConnection(); // 使用 connection 对象创建一个 PreparedStatement 对象,用于执行预编译的 SQL 语句 pstmt = (PreparedStatement) connection.prepareStatement(sql); // 为 SQL 语句中的第一个占位符(即 ID)设置值,值为传入的 user 参数 pstmt.setString(1, user); // 为 SQL 语句中的第二个占位符(即 PASSWORD)设置值,值为传入的 psw 参数 pstmt.setString(2, psw); // 执行 SQL 查询语句,并将查询结果存储在 resultSet 对象中 resultSet = pstmt.executeQuery(); // 检查结果集中是否有下一行记录 if (resultSet.next()) { // 如果有记录,说明账号和密码匹配,返回 "1" 表示登录成功 return "1"; } // 如果结果集中没有记录,说明账号或密码错误,返回提示信息 return "账号或密码错误"; } } // 声明该 Servlet 类所在的包名为 servlet.reader package servlet.reader; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javabean.Base; import net.sf.json.JSONArray; import net.sf.json.JSONObject; /** * Servlet implementation class Book * 该 Servlet 用于处理读者相关的书籍查询请求,返回符合条件的书籍信息的 JSON 数据 */ @WebServlet("/reader/book") public class Book extends HttpServlet { // 重写 doGet 方法,处理 HTTP GET 请求 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置响应的内容类型为 JSON 格式,并指定字符编码为 UTF-8 resp.setContentType("application/json; charset=utf8"); // 接收客户端传递的参数 // limit 表示每页显示的记录数 String limit = req.getParameter("limit"); // page 表示当前页码 String page = req.getParameter("page"); // condition 表示查询的条件字段 String condition = (String) req.getParameter("condition"); // conditionValue 表示查询条件字段的值 String conditionValue = (String) req.getParameter("conditionValue"); // 初始化 SQL 查询的 WHERE 子句为空,表示无限制条件 String where = ""; // 如果 page 参数为空,默认设置为第 1 页 if (page == null) { page = "1"; } // 如果 limit 参数为空,默认设置每页显示 10 条记录 if (limit == null) { limit = "10"; } // 准备数据库操作所需的对象 // 数据库连接对象 Connection connection = null; // 用于执行查询书籍信息的预编译 SQL 语句对象 PreparedStatement pstmt = null; // 用于执行查询书籍总数的预编译 SQL 语句对象 PreparedStatement countPstmt = null; // 存储查询书籍信息的结果集 ResultSet resultSet = null; // 存储查询书籍总数的结果集 ResultSet countSet = null; // 存储查询书籍信息的 SQL 语句 String sql = ""; // 存储查询书籍总数的 SQL 语句 String countSql = ""; // 准备返回给客户端的参数 // 状态码,1 表示无数据,0 表示查询成功 int code = 1; // 提示信息,默认提示无数据 String msg = "无数据"; // 符合条件的书籍总数,初始化为 0 int count = 0; // 用于存储单条书籍信息的 JSON 对象 JSONObject jsonData = new JSONObject(); // 用于存储所有书籍信息的 JSON 数组 JSONArray jsonArray = new JSONArray(); // 用于存储最终返回给客户端的 JSON 数据,包含状态码、总数、提示信息和书籍信息数组 JSONObject jsonResult = new JSONObject(); // 进行数据库查询操作 try { // 通过 Base 类的 getConnection 方法获取数据库连接 connection = (Connection) Base.getConnection(); // 初始化查询书籍信息的 SQL 语句,从 books 表中查询所有字段 sql = "select * from books "; // 如果查询条件字段和条件值都不为空,则添加 WHERE 子句到 SQL 语句中 if (condition != null && conditionValue != null && !condition.equals("") && !conditionValue.equals("")) { where = " where " + condition + " like '%" + conditionValue + "%' "; sql += where; } // 添加分页查询的 LIMIT 子句到 SQL 语句中 sql += " limit ?,?"; // 预编译 SQL 语句 pstmt = connection.prepareStatement(sql); // 设置 LIMIT 子句的第一个参数,计算当前页的起始记录索引 pstmt.setInt(1, (Integer.parseInt(page) - 1) * Integer.parseInt(limit)); // 设置 LIMIT 子句的第二个参数,即每页显示的记录数 pstmt.setInt(2, Integer.parseInt(limit)); // 执行查询操作,获取结果集 resultSet = pstmt.executeQuery(); // 遍历结果集 while (resultSet.next()) { // 获取书籍所在图书馆的 ID String library = resultSet.getString("library_id"); // 构建查询图书馆名称的 SQL 语句 String sql1 = "select * from library where ID =" + library; // 预编译该 SQL 语句 PreparedStatement pstmt1 = connection.prepareStatement(sql1); // 执行查询操作,获取结果集 ResultSet rs1 = pstmt1.executeQuery(); // 存储图书馆名称,初始为空 String lib = ""; // 遍历结果集,获取图书馆名称 while (rs1.next()) { lib = rs1.getString("name"); } // 获取书籍的分类 ID String sortid = resultSet.getString("sort_id"); // 构建查询书籍分类名称的 SQL 语句 String sql2 = "select * from book_sort where ID =" + sortid; // 预编译该 SQL 语句 PreparedStatement pstmt2 = connection.prepareStatement(sql2); // 执行查询操作,获取结果集 ResultSet rs2 = pstmt2.executeQuery(); // 存储书籍分类名称,初始为空 String sort = ""; // 遍历结果集,获取书籍分类名称 while (rs2.next()) { sort = rs2.getString("name"); } // 将书籍信息添加到 JSON 对象中 jsonData.put("id", resultSet.getString("id")); jsonData.put("name", resultSet.getString("name")); jsonData.put("author", resultSet.getString("author")); jsonData.put("library_id", lib); jsonData.put("sort_id", sort); jsonData.put("position", resultSet.getString("position")); jsonData.put("status", resultSet.getString("status")); jsonData.put("description", resultSet.getString("description")); // 将单条书籍信息的 JSON 对象添加到 JSON 数组中 jsonArray.add(jsonData); // 清空 JSON 对象,以便存储下一条书籍信息 jsonData = new JSONObject(); } // 构建查询书籍总数的 SQL 语句 countSql = "select count(*) as count from books "; // 添加 WHERE 子句到总数查询的 SQL 语句中 countSql += where; // 预编译该 SQL 语句 countPstmt = connection.prepareStatement(countSql); // 执行查询操作,获取结果集 countSet = countPstmt.executeQuery(); // 从结果集中获取书籍总数 if (countSet.next()) { count = countSet.getInt("count"); } // 如果 JSON 数组不为空,说明查询到了数据,将状态码设置为 0,提示信息设置为查询成功 if (!jsonArray.isEmpty()) { code = 0; msg = "查询成功"; } } catch (ClassNotFoundException e) { // 如果在加载数据库驱动类时出现异常,将提示信息设置为 class 没找到 msg = "class没找到"; } catch (SQLException e) { // 如果在执行 SQL 语句时出现异常,将提示信息设置为 sql 错误 msg = "sql错误"; } finally { try { // 关闭数据库资源,包括 PreparedStatement 和 ResultSet Base.closeResource(null, pstmt, resultSet); Base.closeResource(connection, countPstmt, countSet); } catch (SQLException e) { // 如果在关闭资源时出现异常,将提示信息设置为关闭资源失败 msg = "关闭资源失败"; } } // 将状态码、总数、提示信息和书籍信息数组添加到最终的 JSON 对象中 jsonResult.put("code", code); jsonResult.put("count", count); jsonResult.put("msg", msg); jsonResult.put("data", jsonArray.toArray()); // 获取响应的输出流 PrintWriter out = resp.getWriter(); // 将最终的 JSON 对象转换为字符串并输出到客户端 out.print(jsonResult.toString()); } } // 包声明,表明该类属于servlet.reader包 package servlet.reader; // 导入处理输入输出异常的类 import java.io.IOException; // 导入用于向客户端发送字符文本的类 import java.io.PrintWriter; // 导入表示数据库连接的类 import java.sql.Connection; // 导入用于执行预编译SQL语句的类 import java.sql.PreparedStatement; // 导入用于存储数据库查询结果的类 import java.sql.ResultSet; // 导入处理SQL操作异常的类 import java.sql.SQLException; // 导入处理Servlet异常的类 import javax.servlet.ServletException; // 导入用于注解Servlet映射路径的类 import javax.servlet.annotation.WebServlet; // 导入HttpServlet类,用于创建Servlet import javax.servlet.http.HttpServlet; // 导入表示HTTP请求的类 import javax.servlet.http.HttpServletRequest; // 导入表示HTTP响应的类 import javax.servlet.http.HttpServletResponse; // 导入用于管理用户会话的类 import javax.servlet.http.HttpSession; // 导入自定义的数据库连接管理类 import javabean.Base; // 导入用于处理JSON数组的类 import net.sf.json.JSONArray; // 导入用于处理JSON对象的类 import net.sf.json.JSONObject; /** * Servlet实现类Borrow * 该Servlet用于处理读者借阅记录的查询请求,返回符合条件的借阅记录的JSON数据 */ @WebServlet("/reader/borrow") public class Borrow extends HttpServlet { // 重写doGet方法,用于处理HTTP GET请求 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置响应的内容类型为JSON格式,并指定字符编码为UTF-8 resp.setContentType("application/json; charset=utf8"); // 接收客户端传递的参数 // limit表示每页显示的记录数 String limit = req.getParameter("limit"); // page表示当前页码 String page = req.getParameter("page"); // condition表示查询的条件字段 String condition = (String) req.getParameter("condition"); // conditionValue表示查询条件字段的值 String conditionValue = (String) req.getParameter("conditionValue"); // 初始化SQL查询的WHERE子句为空,表示无限制条件 String where = ""; // 如果page参数为空,默认设置为第1页 if (page == null) { page = "1"; } // 如果limit参数为空,默认设置每页显示10条记录 if (limit == null) { limit = "10"; } // 准备数据库操作所需的对象 // 数据库连接对象 Connection connection = null; // 用于执行查询借阅记录的预编译SQL语句对象 PreparedStatement pstmt = null; // 用于执行查询借阅记录总数的预编译SQL语句对象 PreparedStatement countPstmt = null; // 存储查询借阅记录的结果集 ResultSet resultSet = null; // 存储查询借阅记录总数的结果集 ResultSet countSet = null; // 存储查询借阅记录的SQL语句 String sql = ""; // 存储查询借阅记录总数的SQL语句 String countSql = ""; // 准备返回给客户端的参数 // 状态码,1表示无数据,0表示查询成功 int code = 1; // 提示信息,默认提示无数据 String msg = "无数据"; // 符合条件的借阅记录总数,初始化为0 int count = 0; // 获取当前请求的会话对象 HttpSession session = req.getSession(); // 用于存储单条借阅记录信息的JSON对象 JSONObject jsonData = new JSONObject(); // 用于存储所有借阅记录信息的JSON数组 JSONArray jsonArray = new JSONArray(); // 用于存储最终返回给客户端的JSON数据,包含状态码、总数、提示信息和借阅记录信息数组 JSONObject jsonResult = new JSONObject(); // 进行数据库查询操作 try { // 通过Base类的getConnection方法获取数据库连接 connection = (Connection) Base.getConnection(); // 初始化查询借阅记录的SQL语句,从borrow_books表中查询所有字段,并且只查询当前读者的记录 sql = "select * from borrow_books where card_id = " + session.getAttribute("reader"); // 如果查询条件字段和条件值都不为空,则添加额外的WHERE子句到SQL语句中 if (condition != null && conditionValue != null && !condition.equals("") && !conditionValue.equals("")) { where = " and " + condition + " like '%" + conditionValue + "%' "; sql += where; } // 添加分页查询的LIMIT子句到SQL语句中 sql += " limit ?,?"; // 打印生成的SQL语句,方便调试 System.out.println("???" + sql); // 预编译SQL语句 pstmt = connection.prepareStatement(sql); // 设置LIMIT子句的第一个参数,计算当前页的起始记录索引 pstmt.setInt(1, (Integer.parseInt(page) - 1) * Integer.parseInt(limit)); // 设置LIMIT子句的第二个参数,即每页显示的记录数 pstmt.setInt(2, Integer.parseInt(limit)); // 执行查询操作,获取结果集 resultSet = pstmt.executeQuery(); // 遍历结果集 while (resultSet.next()) { // 将借阅记录的各个字段信息添加到JSON对象中 jsonData.put("id", resultSet.getString("id")); jsonData.put("card_id", resultSet.getString("card_id")); jsonData.put("book_id", resultSet.getString("book_id")); jsonData.put("borrow_date", resultSet.getString("borrow_date")); jsonData.put("end_date", resultSet.getString("end_date")); jsonData.put("return_date", resultSet.getString("return_date")); // 将单条借阅记录信息的JSON对象添加到JSON数组中 jsonArray.add(jsonData); // 清空JSON对象,以便存储下一条借阅记录信息 jsonData = new JSONObject(); } // 构建查询借阅记录总数的SQL语句,同样只查询当前读者的记录 countSql = "select count(*) as count from borrow_books where card_id = " + req.getSession().getAttribute("reader"); // 添加额外的WHERE子句到总数查询的SQL语句中 countSql += where; // 预编译该SQL语句 countPstmt = connection.prepareStatement(countSql); // 执行查询操作,获取结果集 countSet = countPstmt.executeQuery(); // 从结果集中获取借阅记录总数 if (countSet.next()) { count = countSet.getInt("count"); } // 如果JSON数组不为空,说明查询到了数据,将状态码设置为0,提示信息设置为查询成功 if (!jsonArray.isEmpty()) { code = 0; msg = "查询成功"; } } catch (ClassNotFoundException e) { // 如果在加载数据库驱动类时出现异常,将提示信息设置为class没找到 msg = "class没找到"; } catch (SQLException e) { // 如果在执行SQL语句时出现异常,将提示信息设置为sql错误 msg = "sql错误"; } finally { try { // 关闭数据库资源,包括PreparedStatement和ResultSet Base.closeResource(null, pstmt, resultSet); Base.closeResource(connection, countPstmt, countSet); } catch (SQLException e) { // 如果在关闭资源时出现异常,将提示信息设置为关闭资源失败 msg = "关闭资源失败"; } } // 将状态码、总数、提示信息和借阅记录信息数组添加到最终的JSON对象中 jsonResult.put("code", code); jsonResult.put("count", count); jsonResult.put("msg", msg); jsonResult.put("data", jsonArray.toArray()); // 获取响应的输出流 PrintWriter out = resp.getWriter(); // 将最终的JSON对象转换为字符串并输出到客户端 out.print(jsonResult.toString()); } } // 包声明:该Servlet属于servlet.reader包(读者相关Servlet) package servlet.reader; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet实现类:退出登录处理 * 功能:处理读者退出登录请求,销毁会话信息并跳转回登录页面 */ @WebServlet("/reader/exit") // 声明Servlet映射路径:/reader/exit public class Exit extends HttpServlet { // 序列化版本号(IDE自动生成,用于保证反序列化兼容性) private static final long serialVersionUID = 1L; // 处理HTTP GET请求(退出操作一般通过GET发起) @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取当前用户会话 HttpSession session = req.getSession(); // 检查会话中是否存在读者登录信息("reader"为登录时存储的会话属性) if (session.getAttribute("reader") != null) { // 移除会话中的读者登录信息,实现退出 session.removeAttribute("reader"); } // 重定向回读者登录页面(使用动态上下文路径保证部署灵活性) // req.getContextPath() 获取当前应用的上下文路径(如:/LibrarySystem) // 跳转目标:/reader/04readerFrame.jsp(读者登录页面) resp.sendRedirect(req.getContextPath() + "/reader/04readerFrame.jsp"); } } // 声明该Servlet类所在的包,用于处理读者相关的Servlet操作 package servlet.reader; // 导入处理输入输出异常的类 import java.io.IOException; // 导入用于向客户端发送字符文本的类 import java.io.PrintWriter; // 导入表示数据库连接的类 import java.sql.Connection; // 导入用于执行预编译SQL语句的类 import java.sql.PreparedStatement; // 导入用于存储数据库查询结果的类 import java.sql.ResultSet; // 导入处理SQL操作异常的类 import java.sql.SQLException; // 导入处理Servlet异常的类 import javax.servlet.ServletException; // 导入用于注解Servlet映射路径的类 import javax.servlet.annotation.WebServlet; // 导入HttpServlet类,用于创建Servlet import javax.servlet.http.HttpServlet; // 导入表示HTTP请求的类 import javax.servlet.http.HttpServletRequest; // 导入表示HTTP响应的类 import javax.servlet.http.HttpServletResponse; // 导入用于管理用户会话的类 import javax.servlet.http.HttpSession; // 导入自定义的数据库连接管理类 import javabean.Base; // 导入用于处理JSON数组的类 import net.sf.json.JSONArray; // 导入用于处理JSON对象的类 import net.sf.json.JSONObject; /** * Servlet实现类Illegal * 该Servlet用于处理读者违规借阅记录的查询请求,返回符合条件的违规借阅记录的JSON数据 */ @WebServlet("/reader/illegal") public class Illegal extends HttpServlet { // 重写doGet方法,用于处理HTTP GET请求 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置响应的内容类型为JSON格式,并指定字符编码为UTF-8 resp.setContentType("application/json; charset=utf8"); // 接收客户端传递的参数 // limit表示每页显示的记录数 String limit = req.getParameter("limit"); // page表示当前页码 String page = req.getParameter("page"); // condition表示查询的条件字段 String condition = (String) req.getParameter("condition"); // conditionValue表示查询条件字段的值 String conditionValue = (String) req.getParameter("conditionValue"); // 初始化SQL查询的WHERE子句为空,表示无限制条件 String where = ""; // 如果page参数为空,默认设置为第1页 if (page == null) { page = "1"; } // 如果limit参数为空,默认设置每页显示10条记录 if (limit == null) { limit = "10"; } // 准备数据库操作所需的对象 // 数据库连接对象 Connection connection = null; // 用于执行查询违规借阅记录的预编译SQL语句对象 PreparedStatement pstmt = null; // 用于执行查询违规借阅记录总数的预编译SQL语句对象 PreparedStatement countPstmt = null; // 存储查询违规借阅记录的结果集 ResultSet resultSet = null; // 存储查询违规借阅记录总数的结果集 ResultSet countSet = null; // 存储查询违规借阅记录的SQL语句 String sql = ""; // 存储查询违规借阅记录总数的SQL语句 String countSql = ""; // 准备返回给客户端的参数 // 状态码,1表示无数据,0表示查询成功 int code = 1; // 提示信息,默认提示无数据 String msg = "无数据"; // 符合条件的违规借阅记录总数,初始化为0 int count = 0; // 获取当前请求的会话对象 HttpSession session = req.getSession(); // 用于存储单条违规借阅记录信息的JSON对象 JSONObject jsonData = new JSONObject(); // 用于存储所有违规借阅记录信息的JSON数组 JSONArray jsonArray = new JSONArray(); // 用于存储最终返回给客户端的JSON数据,包含状态码、总数、提示信息和违规借阅记录信息数组 JSONObject jsonResult = new JSONObject(); // 进行数据库查询操作 try { // 通过Base类的getConnection方法获取数据库连接 connection = (Connection) Base.getConnection(); // 初始化查询违规借阅记录的SQL语句,从borrow_books表中查询所有字段, // 筛选出违规信息不为空且长度大于0的记录,并且只查询当前读者的记录 sql = "select * from borrow_books where ILLEGAL is not null and length(trim(illegal))>0 AND CARD_ID = " + session.getAttribute("reader"); // 如果查询条件字段和条件值都不为空,则添加额外的WHERE子句到SQL语句中 if (condition != null && conditionValue != null && !condition.equals("") && !conditionValue.equals("")) { where = " and " + condition + " like '%" + conditionValue + "%' "; sql += where; } // 添加分页查询的LIMIT子句到SQL语句中 sql += " limit ?,?"; // 打印生成的SQL语句,方便调试 System.out.println("???" + sql); // 预编译SQL语句 pstmt = connection.prepareStatement(sql); // 设置LIMIT子句的第一个参数,计算当前页的起始记录索引 pstmt.setInt(1, (Integer.parseInt(page) - 1) * Integer.parseInt(limit)); // 设置LIMIT子句的第二个参数,即每页显示的记录数 pstmt.setInt(2, Integer.parseInt(limit)); // 执行查询操作,获取结果集 resultSet = pstmt.executeQuery(); // 遍历结果集 while (resultSet.next()) { // 将违规借阅记录的各个字段信息添加到JSON对象中 jsonData.put("id", resultSet.getString("id")); jsonData.put("card_id", resultSet.getString("card_id")); jsonData.put("book_id", resultSet.getString("book_id")); jsonData.put("borrow_date", resultSet.getString("borrow_date")); jsonData.put("end_date", resultSet.getString("end_date")); jsonData.put("return_date", resultSet.getString("return_date")); jsonData.put("illegal", resultSet.getString("illegal")); jsonData.put("manager_id", resultSet.getString("manager_id")); // 将单条违规借阅记录信息的JSON对象添加到JSON数组中 jsonArray.add(jsonData); // 清空JSON对象,以便存储下一条违规借阅记录信息 jsonData = new JSONObject(); } // 构建查询违规借阅记录总数的SQL语句,同样筛选出违规信息不为空且长度大于0的记录, // 并且只查询当前读者的记录 countSql = "select count(*) as count from borrow_books where ILLEGAL is not null and length(trim(illegal))>0 AND CARD_ID = " + session.getAttribute("reader"); // 添加额外的WHERE子句到总数查询的SQL语句中 countSql += where; // 预编译该SQL语句 countPstmt = connection.prepareStatement(countSql); // 执行查询操作,获取结果集 countSet = countPstmt.executeQuery(); // 从结果集中获取违规借阅记录总数 if (countSet.next()) { count = countSet.getInt("count"); } // 如果JSON数组不为空,说明查询到了数据,将状态码设置为0,提示信息设置为查询成功 if (!jsonArray.isEmpty()) { code = 0; msg = "查询成功"; } } catch (ClassNotFoundException e) { // 如果在加载数据库驱动类时出现异常,将提示信息设置为class没找到 msg = "class没找到"; } catch (SQLException e) { // 如果在执行SQL语句时出现异常,将提示信息设置为sql错误 msg = "sql错误"; } finally { try { // 关闭数据库资源,包括PreparedStatement和ResultSet Base.closeResource(null, pstmt, resultSet); Base.closeResource(connection, countPstmt, countSet); } catch (SQLException e) { // 如果在关闭资源时出现异常,将提示信息设置为关闭资源失败 msg = "关闭资源失败"; } } // 将状态码、总数、提示信息和违规借阅记录信息数组添加到最终的JSON对象中 jsonResult.put("code", code); jsonResult.put("count", count); jsonResult.put("msg", msg); jsonResult.put("data", jsonArray.toArray()); // 获取响应的输出流 PrintWriter out = resp.getWriter(); // 将最终的JSON对象转换为字符串并输出到客户端 out.print(jsonResult.toString()); } } // 声明该Servlet类所在的包,用于处理读者相关的Servlet操作 package servlet.reader; // 导入处理输入输出异常的类 import java.io.IOException; // 导入用于向客户端发送字符文本的类 import java.io.PrintWriter; // 导入处理SQL操作异常的类 import java.sql.SQLException; // 导入HashMap类,用于存储键值对,方便组织响应数据 import java.util.HashMap; // 导入处理Servlet异常的类 import javax.servlet.ServletException; // 导入用于注解Servlet映射路径的类 import javax.servlet.annotation.WebServlet; // 导入HttpServlet类,用于创建Servlet import javax.servlet.http.HttpServlet; // 导入表示HTTP请求的类 import javax.servlet.http.HttpServletRequest; // 导入表示HTTP响应的类 import javax.servlet.http.HttpServletResponse; // 导入用于管理用户会话的类 import javax.servlet.http.HttpSession; // 导入自定义的Reader类,用于处理读者登录逻辑 import javabean.Reader; // 导入用于处理JSON对象的类 import net.sf.json.JSONObject; /** * 该Servlet用于处理读者的登录请求。 * 当客户端发送POST请求到 /readerLogin 路径时,会验证读者的账号和密码, * 并根据验证结果返回相应的JSON数据给客户端。 */ @WebServlet("/readerLogin") public class ReaderLogin extends HttpServlet { /** * 处理HTTP GET请求。 * 此方法在该Servlet中只是简单地将当前应用的上下文路径返回给客户端, * 一般登录操作不使用GET请求,这里只是一个默认的处理。 * * @param request 客户端的HTTP请求对象 * @param response 服务器的HTTP响应对象 * @throws ServletException 如果在处理请求过程中出现Servlet相关的错误 * @throws IOException 如果在输入输出过程中出现错误 */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().append("Served at: ").append(request.getContextPath()); } /** * 处理HTTP POST请求,实现读者登录功能。 * * @param request 客户端的HTTP请求对象,包含读者输入的账号和密码 * @param response 服务器的HTTP响应对象,用于返回登录结果的JSON数据 * @throws ServletException 如果在处理请求过程中出现Servlet相关的错误 * @throws IOException 如果在输入输出过程中出现错误 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应的内容类型为JSON格式,并指定字符编码为UTF-8 response.setContentType("application/json; charset=utf8"); // 获取用于向客户端输出响应内容的PrintWriter对象 PrintWriter out = response.getWriter(); // 从HTTP请求中获取读者输入的账号和密码 String user = request.getParameter("user"); String psw = request.getParameter("psw"); // 创建一个HashMap对象,用于存储要返回给客户端的响应信息, // 键为字符串类型,值为对象类型 HashMap hashMap = new HashMap(); // 创建Reader类的实例,用于调用登录验证方法 Reader reader = new Reader(); // 用于存储登录验证结果的变量 String result = null; try { // 调用Reader类的login方法进行登录验证,传入账号和密码 result = reader.login(user, psw); } catch (ClassNotFoundException | SQLException e) { // 捕获可能出现的类未找到异常或SQL异常,并打印异常堆栈信息 e.printStackTrace(); } // 判断登录验证结果是否为 "1","1" 表示登录成功 if (result != null && result.equals("1")) { // 获取当前请求的会话对象,如果不存在则创建一个新的会话 HttpSession session = request.getSession(); // 将登录的读者账号存储到会话中,方便后续使用 session.setAttribute("reader", user); // 设置一个标志,表示读者是首次登录(这里假设 "1" 表示登录状态) session.setAttribute("reader_first", "1"); // 向HashMap中添加登录成功的状态码 hashMap.put("code", 0); // 向HashMap中添加登录成功的提示信息 hashMap.put("msg", "登录成功"); // 向HashMap中添加登录成功后要跳转的页面URL hashMap.put("url", request.getContextPath() + "/reader/01main.jsp"); } else { // 登录失败,向HashMap中添加登录失败的状态码 hashMap.put("code", 1); // 向HashMap中添加登录失败的提示信息,即登录验证结果中的错误信息 hashMap.put("msg", result); } // 将HashMap对象转换为JSON对象 JSONObject json = JSONObject.fromObject(hashMap); // 将JSON对象转换为字符串并通过PrintWriter输出到客户端 out.write(json.toString()); } } <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 图书馆欢迎页 <%-- 会话校验与自动跳转逻辑 --%> <% // 检查是否已登录且为首次访问(通过reader_first标记判断) if (session.getAttribute("reader") != null && session.getAttribute("reader_first") != null && session.getAttribute("reader_first").equals("1")) { // 更新标记为已访问(防止重复跳转) session.setAttribute("reader_first", "2"); // 首次登录时,通过JS跳转至框架页(使用parent.location实现嵌套页面跳转) %> <% } %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 读者导航栏 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 借阅者页面 <% // 检查会话中是否存在"reader"属性,即判断用户是否已登录 if(session.getAttribute("reader") == null){ %> <% } else { %> <% } %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 图书查询管理
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Insert title here <% // 检查会话中是否存在"reader"属性,若不存在则表示用户未登录 if(session.getAttribute("reader")==null){%> <% } %>
<%@ page import="java.sql.*" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
❤图书馆公告栏,记得查收公告呀!❤

近期公告

<% // 定义SQL查询语句,从announcement表中查询所有记录 String sql="select*from announcement"; // 调用JavaBean的executeQuery方法执行SQL查询,并将结果存储在ResultSet对象中 ResultSet rs = check.executeQuery(sql); // 遍历ResultSet对象,处理每一条查询结果 while (rs.next()) { %>
<%=rs.getString("TITLE") %> <%=rs.getString("PUBLISH_DATE") %>

<%=rs.getString("DETAIL") %>

<% } %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Insert title here <% // 检查会话中是否存在 "reader" 属性,如果不存在则表示用户未登录 if(session.getAttribute("reader") == null){ %> <% } %>
<%@ page import="java.sql.*"%> <%@ page import="java.util.*"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 修改密码 <% // 从请求中获取用户输入的新密码(第一次输入) String psw1 = request.getParameter("psw1"); // 从请求中获取用户输入的新密码(第二次输入) String psw2 = request.getParameter("psw2"); // 输出两次输入的密码,用于调试,此处注释掉了 //out.println(psw1 + " " + psw2); // 从会话中获取当前登录读者的ID,并将其转换为字符串类型 String id = session.getAttribute("reader").toString(); // 检查两次输入的密码是否相同,且不为空或仅包含空格 if (psw1.equals(psw2) && psw1 != null && psw2 != null && !psw1.trim().equals("") && !psw2.trim().equals("")) { // 构建SQL更新语句,将borrow_card表中对应ID的用户密码更新为新密码 String sql = "update borrow_card set PASSWORD ='" + psw1 + "' where ID=" + id; try { // 调用JavaBean的executeUpdate方法执行SQL更新语句,并获取受影响的行数 int i = check.executeUpdate(sql); // 判断受影响的行数是否为1,即是否成功更新了一条记录 if (i == 1) { %> <% } else { %> <% } } catch (Exception e) { %> <% } } else { %> <% } %> <%@ page import="java.sql.*" %> <%@ page import="java.util.*" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

读者规则信息查看

<% // 定义SQL查询语句,从rules表中查询所有记录 String sql = "select * from rules"; // 调用JavaBean的executeQuery方法执行SQL查询,并将结果存储在ResultSet对象中 ResultSet rs = msg.executeQuery(sql); // 遍历ResultSet对象,处理每一条查询结果 while (rs.next()) { // 判断借阅证规则编号是否为奇数 if(Integer.parseInt(rs.getString("ID")) % 2== 1){ %>

<% } else { %>

<% } } %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>


       
<%@ page import="java.sql.*" %> <%@ page import="java.util.*" %> <%@ page import="javabean.DateTime"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% // 创建 DateTime 类的实例,用于获取日期时间 DateTime date = new DateTime(); // 获取当前日期时间 String time = date.show(); // 从请求中获取用户输入的留言内容 String mes = request.getParameter("msg"); try { // 从会话中获取读者的借阅证编号,并转换为字符串 String card = session.getAttribute("reader").toString(); // 构建 SQL 插入语句,将读者的借阅证编号、留言内容和留言时间插入到 message 表中 String sql = "insert into message(card_id,detail,public_date)values('" + card + "','" + mes + "','" + time + "');"; // 检查借阅证编号和留言内容是否不为空,且留言内容不是仅包含空白字符 if (card != null && mes != null && !mes.trim().equals("")) { // 调用 JDBCBean 实例的 executeUpdate 方法执行 SQL 插入语句,并获取受影响的行数 int i = msg.executeUpdate(sql); // 如果受影响的行数为 1,说明插入成功 if (i == 1) { %> <% } else { %> <% } } else { %> <% } %> <% // 捕获异常,当出现异常时执行以下操作 } catch (Exception e) { %> <% } %> <%@ page import="java.sql.*" %> <%@ page import="java.util.*" %> <%@ page import="javabean.DateTime"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

☆★留言板★☆

<% // 定义 SQL 查询语句,从 message 表中查询 CARD_ID、DETAIL 和 PUBLIC_DATE 字段,并按 PUBLIC_DATE 降序排列 String sql = "select CARD_ID,DETAIL,PUBLIC_DATE from message order by PUBLIC_DATE desc"; // 调用 JavaBean 的 executeQuery 方法执行 SQL 查询,并将结果存储在 ResultSet 对象中 ResultSet rs = msg.executeQuery(sql); // 遍历 ResultSet 对象,处理每一条查询结果 while (rs.next()) { %>

<%=rs.getString("CARD_ID")%>

<%=rs.getString("DETAIL")%>

<%=rs.getString("PUBLIC_DATE")%>


<% } %> <%@ page import="java.sql.*"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Nifty Modal Window Effects <% // 检查会话中是否存在 "reader" 属性,如果不存在则表示用户未登录 if (session.getAttribute("reader") == null) { %> <% } %>

个人信息

<% try { // 从会话中获取当前登录读者的借阅证编号 String cardId = session.getAttribute("reader").toString(); // 构建 SQL 查询语句,从 borrow_card 表中查询当前读者的信息 String sql = "select*from borrow_card where ID = '" + cardId + "';"; // 调用 JavaBean 的 executeQuery 方法执行 SQL 查询,并将结果存储在 ResultSet 对象中 ResultSet rs = check.executeQuery(sql); // 遍历查询结果 while (rs.next()) { // 获取借阅证编号 int id = rs.getInt(1); %>

 借阅证编号:<%=rs.getString("ID")%>


 借阅证姓名:<%=rs.getString("READER")%>


 规则编号:<%=rs.getString("RULE_ID")%>


 状态: <% // 根据 STATUS 字段的值判断借阅证状态 if (rs.getString("STATUS").equals("1")) { out.println("可用"); } else { out.println("挂失"); } %>

<% } } catch (Exception e) { // 异常处理,可根据实际需求添加日志记录等操作 } %>
package javabean; // 导入 java.sql 包中的相关类,用于与数据库进行交互 import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 该类提供了与数据库交互的基础方法,包括获取数据库连接、执行查询、执行更新以及释放资源等操作。 */ public class Base { // 从 DBConstants 类中获取数据库驱动信息,并将其设置为常量,后续不可修改 private static final String driver = DBConstants.driver; // 从 DBConstants 类中获取数据库连接的 URL 信息,并将其设置为常量,后续不可修改 private static final String url = DBConstants.url; // 从 DBConstants 类中获取数据库用户名信息,并将其设置为常量,后续不可修改 private static final String username = DBConstants.username; // 从 DBConstants 类中获取数据库密码信息,并将其设置为常量,后续不可修改 private static final String password = DBConstants.password; /** * 获取数据库连接 * * @return 返回一个数据库连接对象,如果连接失败则返回 null * @throws ClassNotFoundException 如果找不到数据库驱动类,抛出该异常 */ public static Connection getConnection() throws ClassNotFoundException { // 初始化数据库连接对象为 null Connection connection = null; try { // 加载数据库驱动类 Class.forName(driver); // 通过 DriverManager 获取数据库连接 connection = (Connection) DriverManager.getConnection(url, username, password); } catch (SQLException e) { // 若发生 SQL 异常,打印异常堆栈信息 e.printStackTrace(); } // 返回数据库连接对象 return connection; } /** * 公共查询方法,用于执行 SQL 查询语句 * * @param connection 数据库连接对象,用于与数据库建立连接 * @param preparedStatement 预编译的 SQL 语句对象,用于执行带参数的 SQL 语句 * @param resultSet 结果集对象,用于存储查询结果 * @param sql 要执行的 SQL 查询语句 * @param params SQL 语句中的参数数组 * @return 返回包含查询结果的结果集对象 * @throws SQLException 如果执行 SQL 语句时发生错误,抛出该异常 */ public static ResultSet executequery(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet, String sql, Object[] params) throws SQLException { // 如果预编译的 SQL 语句对象为空,则创建一个新的预编译对象 if (preparedStatement == null) { preparedStatement = (PreparedStatement) connection.prepareStatement(sql); } // 遍历参数数组,将参数设置到预编译的 SQL 语句中 for (int i = 0; params != null && i < params.length; i++) { preparedStatement.setObject(i + 1, params[i]); } // 执行查询语句,并将结果存储在结果集对象中 resultSet = preparedStatement.executeQuery(); // 返回结果集对象 return resultSet; } /** * 公共修改方法,用于执行 SQL 更新、插入、删除等语句 * * @param connection 数据库连接对象,用于与数据库建立连接 * @param preparedStatement 预编译的 SQL 语句对象,用于执行带参数的 SQL 语句 * @param sql 要执行的 SQL 修改语句 * @param params SQL 语句中的参数数组 * @return 返回受影响的行数 * @throws SQLException 如果执行 SQL 语句时发生错误,抛出该异常 */ public static int executeUpdate(Connection connection, PreparedStatement preparedStatement, String sql, Object[] params) throws SQLException { // 如果预编译的 SQL 语句对象为空,则创建一个新的预编译对象 if (preparedStatement == null) { preparedStatement = (PreparedStatement) connection.prepareStatement(sql); } // 遍历参数数组,将参数设置到预编译的 SQL 语句中 for (int i = 0; params != null && i < params.length; i++) { preparedStatement.setObject(i + 1, params[i]); } // 执行更新语句,并返回受影响的行数 int updateRows = preparedStatement.executeUpdate(); // 返回受影响的行数 return updateRows; } /** * 释放数据库资源,包括结果集、预编译语句和数据库连接 * * @param connection 数据库连接对象 * @param preparedStatement 预编译的 SQL 语句对象 * @param resultSet 结果集对象 * @return 如果所有资源都成功释放,返回 true;否则返回 false * @throws SQLException 如果关闭资源时发生错误,抛出该异常 */ public static boolean closeResource(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) throws SQLException { // 初始化标志位为 true,表示资源释放成功 boolean flag = true; // 如果结果集对象不为空,则尝试关闭结果集 if (resultSet != null) { try { resultSet.close(); // 关闭后将结果集对象置为 null,便于垃圾回收 resultSet = null; } catch (SQLException e) { // 若关闭结果集时发生异常,打印异常堆栈信息,并将标志位设为 false e.printStackTrace(); flag = false; } } // 如果预编译语句对象不为空,则尝试关闭预编译语句 if (preparedStatement != null) { try { preparedStatement.close(); // 关闭后将预编译语句对象置为 null,便于垃圾回收 preparedStatement = null; } catch (SQLException e) { // 若关闭预编译语句时发生异常,打印异常堆栈信息,并将标志位设为 false e.printStackTrace(); flag = false; } } // 如果数据库连接对象不为空,则尝试关闭数据库连接 if (connection != null) { try { connection.close(); // 关闭后将数据库连接对象置为 null,便于垃圾回收 connection = null; } catch (SQLException e) { // 若关闭数据库连接时发生异常,打印异常堆栈信息,并将标志位设为 false e.printStackTrace(); flag = false; } } // 返回标志位 return flag; } } package javabean; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.TreeMap; /** * Common 类提供了一些通用的数据库操作方法,例如获取指定表的行数和获取图书馆信息的映射。 */ public class Common { /** * 获取指定表的行总数。 * * @param table 要查询的表名 * @return 表中的行数,如果表名为空或者查询过程中出现异常则返回 0 * @throws SQLException 如果在执行 SQL 语句时发生数据库访问错误 * @throws ClassNotFoundException 如果无法找到数据库驱动类 */ public static int getCount(String table) throws SQLException, ClassNotFoundException { // 检查传入的表名是否为空或 null,如果是则直接返回 0 if (table == null || table.equals("")) { return 0; } // 声明数据库连接对象 Connection connection = null; // 声明预编译 SQL 语句对象 PreparedStatement pstmt = null; // 声明结果集对象 ResultSet resultSet = null; // 通过 Base 类的 getConnection 方法获取数据库连接 connection = Base.getConnection(); try { // 准备 SQL 查询语句,用于统计指定表的行数 pstmt = connection.prepareStatement("select count(*) as count from " + table); // 执行查询操作,将结果存储在结果集对象中 resultSet = pstmt.executeQuery(); // 将结果集指针移动到第一行 resultSet.next(); // 从结果集中获取名为 "count" 的列的值,并返回该值 return resultSet.getInt("count"); } catch (Exception e) { // 如果查询过程中出现异常,返回 0 return 0; } finally { // 无论查询是否成功,都调用 Base 类的 closeResource 方法关闭数据库连接、预编译语句和结果集 Base.closeResource(connection, pstmt, resultSet); } } /** * 获取图书馆信息的映射,键为图书馆的 ID,值为图书馆的名称。 * * @return 包含图书馆信息的 TreeMap,如果出现异常则返回 null * @throws SQLException 如果在执行 SQL 语句时发生数据库访问错误 */ public static TreeMap getLibraryMap() throws SQLException { // 声明数据库连接对象 Connection connection = null; // 声明用于查询图书馆信息的预编译 SQL 语句对象 PreparedStatement libraryPstmt = null; // 声明用于存储图书馆信息查询结果的结果集对象 ResultSet librarySet = null; // 定义查询图书馆信息的 SQL 语句 String librarySql = "select id,name from library"; // 创建一个 TreeMap 用于存储图书馆信息,TreeMap 会根据键的自然顺序进行排序 TreeMap map = new TreeMap(); // 获取图书馆信息 try { // 通过 Base 类的 getConnection 方法获取数据库连接 connection = (Connection) Base.getConnection(); // 准备查询图书馆信息的 SQL 语句 libraryPstmt = connection.prepareStatement(librarySql); // 执行查询操作,将结果存储在结果集对象中 librarySet = libraryPstmt.executeQuery(); // 遍历结果集 while (librarySet.next()) { // 将图书馆的 ID 作为键,图书馆的名称作为值,存入 TreeMap 中 map.put(librarySet.getString("id"), librarySet.getString("name")); } } catch (ClassNotFoundException e) { // 如果找不到数据库驱动类,返回 null return null; } catch (SQLException e) { // 如果执行 SQL 语句时发生异常,返回 null return null; } finally { // 无论查询是否成功,都调用 Base 类的 closeResource 方法关闭数据库连接、预编译语句和结果集 Base.closeResource(connection, libraryPstmt, librarySet); } // 返回存储图书馆信息的 TreeMap return map; } /** * 主方法,用于测试 getLibraryMap 方法。 * * @param args 命令行参数 * @throws SQLException 如果在执行 SQL 语句时发生数据库访问错误 */ public static void main(String[] args) throws SQLException { // 调用 getLibraryMap 方法并打印结果 System.out.println(Common.getLibraryMap()); } } package javabean; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * CompareDate 类用于比较两个日期字符串所表示的日期之间的天数差。 */ public class CompareDate { /** * 该方法用于计算两个日期字符串所表示的日期之间相差的天数。 * * @param Str1 第一个日期字符串,格式必须为 "yyyy-MM-dd HH:mm:ss" * @param Str2 第二个日期字符串,格式必须为 "yyyy-MM-dd HH:mm:ss" * @return 两个日期之间相差的天数,如果解析日期字符串时发生异常则返回 0 */ public static long show(String Str1, String Str2) { // 用于存储两个日期之间的毫秒差值 long between = 0; // 创建一个 SimpleDateFormat 对象,指定日期字符串的格式为 "yyyy-MM-dd HH:mm:ss" SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { // 将第一个日期字符串解析为 Date 对象 Date date1 = format.parse(Str1); // 将第二个日期字符串解析为 Date 对象 Date date2 = format.parse(Str2); // 计算两个日期对象之间的毫秒差值 between = (date2.getTime() - date1.getTime()); // 打印第一个日期对象,方便调试查看 System.out.println(date1); // 打印第二个日期对象,方便调试查看 System.out.println(date2); } catch (ParseException e) { // 如果在解析日期字符串时发生异常,打印异常堆栈信息 e.printStackTrace(); } // 将毫秒差值转换为天数,一天有 24 小时,每小时 60 分钟,每分钟 60 秒,每秒 1000 毫秒 long days = between / (24 * 60 * 60 * 1000); // 返回计算得到的天数差值 return days; } } package javabean; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * DateTime 类提供了一系列用于日期和时间处理的静态方法, * 可以获取当前日期时间、计算指定天数偏移后的日期时间等。 */ public class DateTime { /** * 获取当前日期和时间,格式为 "yyyy-MM-dd HH:mm:ss"。 * * @return 格式化后的当前日期和时间字符串 */ public static String show() { // 创建一个 SimpleDateFormat 对象,指定日期时间的格式为 "yyyy-MM-dd HH:mm:ss" SimpleDateFormat myFmt2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 获取当前日期和时间 Date now = new Date(); // 使用指定格式将当前日期和时间转换为字符串并返回 return myFmt2.format(now); } /** * 获取指定天数偏移后的日期和时间,格式为 "yyyy-MM-dd HH:mm:ss"。 * * @param n 要偏移的天数,正数表示未来的日期,负数表示过去的日期 * @return 格式化后的偏移日期和时间字符串 */ public static String show(int n) { // 获取当前日期和时间 Date d = new Date(); // 创建一个 SimpleDateFormat 对象,指定日期时间的格式为 "yyyy-MM-dd HH:mm:ss" SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 将当前日期和时间按照指定格式转换为字符串 String currdate = format.format(d); // 获取一个 Calendar 实例,用于日期计算 Calendar ca = Calendar.getInstance(); // 在当前日期的基础上增加或减少指定的天数 ca.add(Calendar.DATE, n); // 获取计算后的日期 d = ca.getTime(); // 将计算后的日期按照指定格式转换为字符串 String enddate = format.format(d); // 返回计算后的日期和时间字符串 return enddate; } /** * 获取指定天数偏移后的日期,只包含年、月、日,格式为 "yyyy-MM-dd"。 * * @param n 要偏移的天数,正数表示未来的日期,负数表示过去的日期 * @return 格式化后的偏移日期字符串 */ public static String showDate(int n) { // 获取当前日期和时间 Date d = new Date(); // 创建一个 SimpleDateFormat 对象,指定日期的格式为 "yyyy-MM-dd" SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); // 将当前日期按照指定格式转换为字符串 String currdate = format.format(d); // 获取一个 Calendar 实例,用于日期计算 Calendar ca = Calendar.getInstance(); // 在当前日期的基础上增加或减少指定的天数 ca.add(Calendar.DATE, n); // 获取计算后的日期 d = ca.getTime(); // 将计算后的日期按照指定格式转换为字符串 String enddate = format.format(d); // 返回计算后的日期字符串 return enddate; } /** * 获取指定天数偏移后的日期,只包含月和日,格式为 "MM-dd"。 * * @param n 要偏移的天数,正数表示未来的日期,负数表示过去的日期 * @return 格式化后的偏移日期字符串 */ public static String showMD(int n) { // 获取当前日期和时间 Date d = new Date(); // 创建一个 SimpleDateFormat 对象,指定日期的格式为 "MM-dd" SimpleDateFormat format = new SimpleDateFormat("MM-dd"); // 将当前日期按照指定格式转换为字符串 String currdate = format.format(d); // 获取一个 Calendar 实例,用于日期计算 Calendar ca = Calendar.getInstance(); // 在当前日期的基础上增加或减少指定的天数 ca.add(Calendar.DATE, n); // 获取计算后的日期 d = ca.getTime(); // 将计算后的日期按照指定格式转换为字符串 String enddate = format.format(d); // 返回计算后的日期字符串 return enddate; } } package javabean; /** * DBConstants 类用于存储数据库连接所需的常量信息, * 这些常量包含了数据库驱动、连接 URL、用户名和密码, * 方便在项目中统一管理和使用数据库连接相关的配置。 */ public class DBConstants { /** * 数据库驱动类的全限定名。这里使用的是 MySQL 8.x 及以上版本的 JDBC 驱动, * 如果使用的是 MySQL 5.x 版本,驱动类名应为 "com.mysql.jdbc.Driver"。 */ public static final String driver = "com.mysql.cj.jdbc.Driver"; /** * 数据库连接的 URL。该 URL 指定了要连接的数据库的地址、端口、数据库名以及一些连接参数。 * - "jdbc:mysql://":表示使用 JDBC 连接 MySQL 数据库的协议。 * - "localhost:3306":表示数据库服务器的地址为本地主机(localhost),端口号为 3306。 * - "library":表示要连接的数据库名。 * - "&useSSL=false":表示不使用 SSL 加密连接。 * - "serverTimezone=UTC":指定数据库服务器的时区为协调世界时(UTC),避免时间处理上的问题。 * - "useUnicode=true&characterEncoding=UTF-8":表示使用 Unicode 字符集,并将字符编码设置为 UTF-8, * 确保能够正确处理各种语言的字符。 */ public static final String url = "jdbc:mysql://localhost:3306/library?&useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8"; /** * 连接数据库时使用的用户名。这里使用的是 MySQL 的默认超级用户 "root", * 在实际项目中,建议使用具有适当权限的普通用户来连接数据库,以提高安全性。 */ public static final String username = "root"; /** * 连接数据库时使用的密码。这里的密码为 "123456", * 在实际项目中,应使用更复杂、安全的密码,并妥善保管,避免泄露。 */ public static final String password = "123456"; } package javabean; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * EndTime 类提供了一个静态方法,用于计算从当前日期开始经过指定天数后的日期和时间。 */ public class EndTime { /** * 计算从当前日期开始经过指定天数后的日期和时间。 * * @param n 要增加的天数,可以是正数(表示未来的日期)或负数(表示过去的日期)。 * @return 经过指定天数后的日期和时间,格式为 "yyyy-MM-dd HH:mm:ss"。 */ public static String show(int n) { // 获取当前日期和时间 Date d = new Date(); // 创建一个 SimpleDateFormat 对象,用于格式化日期和时间,格式为 "yyyy-MM-dd HH:mm:ss" SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 将当前日期和时间格式化为字符串 String currdate = format.format(d); // 打印当前日期和时间 System.out.println("现在的日期是:" + currdate); // 获取一个 Calendar 实例,用于进行日期计算 Calendar ca = Calendar.getInstance(); // 在当前日期的基础上增加指定的天数 ca.add(Calendar.DATE, n); // 获取增加天数后的日期 d = ca.getTime(); // 将增加天数后的日期格式化为字符串 String enddate = format.format(d); // 返回增加天数后的日期和时间字符串 return enddate; } } package javabean; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; /** * JDBCBean 类封装了与数据库交互的基本操作,包括建立连接、执行更新、执行查询以及关闭连接等功能。 */ public class JDBCBean { // 从 DBConstants 类中获取数据库驱动信息,使用 static final 修饰确保其不可变 private static final String driver = DBConstants.driver; // 从 DBConstants 类中获取数据库连接的 URL 信息,使用 static final 修饰确保其不可变 private static final String url = DBConstants.url; // 从 DBConstants 类中获取数据库用户名信息,使用 static final 修饰确保其不可变 private static final String username = DBConstants.username; // 从 DBConstants 类中获取数据库密码信息,使用 static final 修饰确保其不可变 private static final String password = DBConstants.password; // 数据库连接对象,用于与数据库建立连接 private Connection conn = null; // 用于执行 SQL 语句的 Statement 对象 private Statement stmt = null; /** * 构造函数,在创建 JDBCBean 对象时自动尝试与数据库建立连接。 */ public JDBCBean() { try { // 加载数据库驱动类 Class.forName(driver); // 通过 DriverManager 获取数据库连接 conn = DriverManager.getConnection(url, username, password); // 创建一个 Statement 对象,用于执行 SQL 语句 stmt = conn.createStatement(); // 若连接成功,打印提示信息 System.out.println("同数据库建立连接!"); } catch (Exception ex) { // 若连接过程中出现异常,打印错误提示信息 System.out.println("无法同数据库建立连接!"); } } /** * 执行 SQL 更新语句(如 INSERT、UPDATE、DELETE)。 * * @param s 要执行的 SQL 更新语句 * @return 执行更新操作后受影响的行数,若执行出错则返回 0 */ public int executeUpdate(String s) { // 用于存储执行更新操作后受影响的行数 int result = 0; try { // 打印要执行的 SQL 语句和 Statement 对象信息,方便调试 System.out.println(s + "------" + stmt + "-----"); // 执行 SQL 更新语句,并将受影响的行数赋值给 result result = stmt.executeUpdate(s); } catch (Exception e) { // 若执行更新操作时出现异常,打印错误提示信息 System.out.println("执行更新错误!"); } // 返回受影响的行数 return result; } /** * 执行 SQL 查询语句(如 SELECT)。 * * @param s 要执行的 SQL 查询语句 * @return 包含查询结果的 ResultSet 对象,若执行出错则返回 null */ public ResultSet executeQuery(String s) { // 用于存储查询结果的 ResultSet 对象 ResultSet rs = null; try { // 执行 SQL 查询语句,并将结果存储在 rs 中 rs = stmt.executeQuery(s); } catch (Exception e) { // 若执行查询操作时出现异常,打印错误提示信息和异常信息 System.out.println("执行查询错误! " + e.getMessage()); } // 返回包含查询结果的 ResultSet 对象 return rs; } /** * 关闭数据库连接和 Statement 对象,释放资源。 */ public void close() { try { // 关闭 Statement 对象 stmt.close(); // 关闭数据库连接 conn.close(); } catch (Exception e) { // 若关闭过程中出现异常,不做处理 } } } package javabean; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Date; import net.sf.json.JSONObject; /** * Util 类提供了一系列实用工具方法,涵盖字符串处理、日期格式化、JSON 数据生成和 MD5 加密等功能。 */ public class Util { /** * 计算字符串中指定子字符串的出现次数。 * 此方法可用于计算 JSON 字符串中特定对象标识的数量。 * * @param str 要进行检查的原始字符串 * @param contain 要查找的子字符串 * @return 子字符串在原始字符串中出现的次数 */ public static int getCountString(String str, String contain) { // 通过替换子字符串后计算长度差,再除以子字符串长度得到出现次数 int count = (str.length() - str.replace(contain, "").length()) / contain.length(); return count; } /** * 格式化从数据库取出的日期时间字符串,去除可能存在的 ".0" 后缀。 * * @param dateTime 从数据库取出的日期时间字符串 * @return 去除 ".0" 后缀后的日期时间字符串,如果原字符串无 ".0" 或为空则返回原字符串或 null */ public static String getFormatDateTime(String dateTime) { // 若日期时间字符串不为空且包含 ".0" if (dateTime != null && dateTime.indexOf(".0") != -1) { // 截取去除 ".0" 后的部分并返回 return dateTime.substring(0, dateTime.length() - 2); } else if (dateTime != null) { // 若不包含 ".0" 且不为空,直接返回原字符串 return dateTime; } // 若为空则返回 null return null; } /** * 获取当前时间的字符串表示,格式为 "yyyy-MM-dd hh:mm:ss"。 * * @return 当前时间的格式化字符串 */ public static String getCurrentTimeString() { // 获取当前日期对象 Date date = new Date(); // 定义日期时间格式化模式 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // 将日期对象按指定格式转换为字符串并返回 return dateFormat.format(date); } /** * 生成包含状态码、消息和数据的 JSON 格式字符串响应。 * * @param code 状态码 * @param msg 消息内容 * @param data 附带的数据,如果为 null 则不包含在 JSON 中 * @return 生成的 JSON 格式字符串 */ public static String jsonResponse(int code, String msg, String data) { // 创建一个 JSONObject 对象 JSONObject jsonObject = new JSONObject(); // 向 JSON 对象中添加状态码 jsonObject.put("code", code); // 向 JSON 对象中添加消息内容 jsonObject.put("msg", msg); // 若数据不为空,则添加到 JSON 对象中 if (data != null) { jsonObject.put("data", data); } // 将 JSON 对象转换为字符串并返回 return jsonObject.toString(); } /** * 对输入的字符串进行 MD5 加密。 * * @param plainText 待加密的明文字符串 * @return 加密后的 32 位 MD5 字符串 */ public static String stringToMD5(String plainText) { byte[] secretBytes = null; try { // 获取 MD5 算法的 MessageDigest 实例 MessageDigest md = MessageDigest.getInstance("md5"); // 对输入字符串的字节数组进行加密处理 secretBytes = md.digest(plainText.getBytes()); } catch (NoSuchAlgorithmException e) { // 若指定的 MD5 算法不存在,抛出运行时异常 throw new RuntimeException("没有这个md5算法!"); } // 将加密后的字节数组转换为 16 进制字符串 String md5code = new BigInteger(1, secretBytes).toString(16); // 补齐到 32 位 for (int i = 0; i < 32 - md5code.length(); i++) { md5code = "0" + md5code; } return md5code; } /** * 对密码进行加盐后的 MD5 加密。 * * @param password 原始密码 * @return 加盐加密后的 MD5 字符串 */ public static String passMd5(String password) { // 定义盐值 String salt = "ew!.E"; // 将密码和盐值拼接后进行 MD5 加密并返回结果 return Util.stringToMD5(password + salt); } /** * 主方法,用于测试 passMd5 方法。 * * @param args 命令行参数 */ public static void main(String[] args) { // 打印对 "admin" 密码加盐加密后的 MD5 结果 System.out.println(Util.passMd5("admin")); // 以下代码为注释掉的测试获取当前时间功能的代码 // Date date = new Date(); // SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // System.out.println(dateFormat.format(date)); } } /*! * classie - class helper functions * from bonzo https://github.com/ded/bonzo * * classie.has( elem, 'my-class' ) -> true/false * classie.add( elem, 'my-new-class' ) * classie.remove( elem, 'my-unwanted-class' ) * classie.toggle( elem, 'my-class' ) */ /*jshint browser: true, strict: true, undef: true */ /*global define: false */ // 立即执行函数表达式 (IIFE),将 window 对象作为参数传入,避免全局作用域污染 ( function( window ) { // 使用严格模式,开启更严格的语法检查 'use strict'; // 从 bonzo 库借鉴的类操作辅助函数 // 该函数用于生成一个正则表达式,用于匹配元素类名中的指定类名 function classReg( className ) { // 正则表达式解释: // (^|\\s+) 匹配字符串的开头或者一个或多个空白字符 // className 是要匹配的类名 // (\\s+|$) 匹配一个或多个空白字符或者字符串的结尾 return new RegExp("(^|\\s+)" + className + "(\\s+|$)"); } // 用于管理元素类名的函数声明 // 这些函数将根据浏览器是否支持 classList 属性来实现不同的逻辑 var hasClass, addClass, removeClass; // 检查浏览器是否支持 classList 属性 if ( 'classList' in document.documentElement ) { // 如果支持 classList 属性 // hasClass 函数用于检查元素是否包含指定的类名 hasClass = function( elem, c ) { // 使用 classList 的 contains 方法检查元素是否包含指定类名 return elem.classList.contains( c ); }; // addClass 函数用于给元素添加指定的类名 addClass = function( elem, c ) { // 使用 classList 的 add 方法添加指定类名 elem.classList.add( c ); }; // removeClass 函数用于从元素中移除指定的类名 removeClass = function( elem, c ) { // 使用 classList 的 remove 方法移除指定类名 elem.classList.remove( c ); }; } else { // 如果浏览器不支持 classList 属性 // hasClass 函数使用正则表达式来检查元素是否包含指定的类名 hasClass = function( elem, c ) { // 使用 classReg 函数生成的正则表达式来测试元素的 className 属性 return classReg( c ).test( elem.className ); }; // addClass 函数用于给元素添加指定的类名 addClass = function( elem, c ) { // 先检查元素是否已经包含指定的类名 if ( !hasClass( elem, c ) ) { // 如果不包含,则在元素的 className 属性后面添加该类名 elem.className = elem.className + ' ' + c; } }; // removeClass 函数用于从元素中移除指定的类名 removeClass = function( elem, c ) { // 使用 classReg 函数生成的正则表达式来替换元素 className 属性中的指定类名 elem.className = elem.className.replace( classReg( c ), ' ' ); }; } // toggleClass 函数用于切换元素的指定类名 function toggleClass( elem, c ) { // 根据元素是否包含指定类名来选择调用 removeClass 或 addClass 函数 var fn = hasClass( elem, c ) ? removeClass : addClass; // 调用选择的函数来切换类名 fn( elem, c ); } // 创建一个 classie 对象,包含所有的类操作函数 var classie = { // 完整的函数名 hasClass: hasClass, addClass: addClass, removeClass: removeClass, toggleClass: toggleClass, // 简短的函数名,方便使用 has: hasClass, add: addClass, remove: removeClass, toggle: toggleClass }; // 模块加载机制的处理 if ( typeof define === 'function' && define.amd ) { // 如果使用 AMD 模块加载器(如 RequireJS),则使用 define 函数定义模块 define( classie ); } else { // 如果没有使用 AMD 模块加载器,则将 classie 对象添加到 window 对象上,作为全局变量使用 window.classie = classie; } })( window ); /*! * css-filters-polyfill.js * * Author: Christian Schepp Schaefer * Summary: A polyfill for CSS filter effects * License: MIT * Version: 0.22 * * URL: * https://github.com/Schepp/ * */ // 立即执行函数表达式,将 window 对象作为参数传入,避免全局变量污染 ;(function(window){ // 创建一个名为 polyfilter 的对象,用于封装所有与 CSS 滤镜填充相关的功能和属性 var polyfilter = { // Detect if we are dealing with IE <= 9 // http://james.padolsey.com/javascript/detect-_ie-in-js-using-conditional-comments/ // 检测当前浏览器是否为 IE 9 及以下版本 _ie: (function(){ var undef; // 初始化版本号为 3 var v = 3; // 创建一个 div 元素 var div = document.createElement('div'); // 获取 div 元素内的所有 i 元素 var all = div.getElementsByTagName('i'); // 通过条件注释来检测 IE 版本 // 不断增加版本号 v,直到条件注释不满足(即当前 IE 版本不大于 v) while( // 设置 div 的 innerHTML 为条件注释 div.innerHTML = '', // 如果条件注释生效,会创建一个 i 元素,此时 all[0] 存在 all[0] ); // 如果 v 大于 4,说明检测到了 IE 版本,返回该版本号;否则返回 undefined return v > 4 ? v : undef; })(), // 用于缓存创建的 SVG 元素,避免重复创建 _svg_cache: {}, // 创建 SVG 元素的辅助函数 _create_svg_element: function(tagname,attributes){ // SVG 的命名空间 var xmlns = 'http://www.w3.org/2000/svg'; // 使用 createElementNS 方法创建 SVG 元素 var elem = document.createElementNS(xmlns,tagname); // 遍历传入的属性对象 for(var key in attributes){ // 为 SVG 元素设置属性 elem.setAttributeNS(null,key,attributes[key]); } return elem; }, // 创建完整 SVG 滤镜定义的函数 _create_svg: function(id,filterelements){ // SVG 的命名空间 var xmlns = 'http://www.w3.org/2000/svg'; // 创建一个 SVG 元素 var svg = document.createElementNS(xmlns,'svg'); // 设置 SVG 元素的宽度为 0 svg.setAttributeNS(null,'width','0'); // 设置 SVG 元素的高度为 0 svg.setAttributeNS(null,'height','0'); // 设置 SVG 元素的样式为绝对定位,避免影响页面布局 svg.setAttributeNS(null,'style','position:absolute'); // 创建一个 filter 元素 var svg_filter = document.createElementNS(xmlns,'filter'); // 为 filter 元素设置 id svg_filter.setAttributeNS(null,'id',id); // 将 filter 元素添加到 SVG 元素中 svg.appendChild(svg_filter); // 遍历传入的滤镜元素数组 for(var i = 0; i < filterelements.length; i++){ // 将每个滤镜元素添加到 filter 元素中 svg_filter.appendChild(filterelements[i]); } return svg; }, // 记录待处理的外部样式表数量 _pending_stylesheets: 0, // 存储所有样式表(内联和外部)的信息 _stylesheets: [], // 判断是否处于开发模式 _development_mode: (function(){ // 检查当前页面的主机名是否为 localhost 或者以 .local 结尾,或者是 IP 地址 if(location.hostname === 'localhost' || location.hostname.search(/.local$/) !== -1 || location.hostname.search(/\d+\.\d+\.\d+\.\d+/) !== -1){ // 如果处于开发模式,在控制台输出提示信息 if(window.console) console.log('Detected localhost or IP address. Assuming you are a developer. Caching of stylesheets is disabled.'); return true; } // 如果不是开发模式,在控制台输出提示信息 if(window.console) console.log('Caching of stylesheets is enabled. You need to refresh twice to see any changes.'); return false; })(), // 处理页面上所有样式表的函数 process_stylesheets: function(){ var xmlHttp = []; // 延迟 2 秒后检查库文件路径是否正确,避免干扰初始处理 window.setTimeout(function(){ var xmlHttpCheck; // 根据浏览器支持情况创建 XMLHttpRequest 对象 if (window.XMLHttpRequest) { xmlHttpCheck = new XMLHttpRequest(); } else if (window.ActiveXObject) { xmlHttpCheck = new ActiveXObject("Microsoft.XMLHTTP"); } // 打开一个 GET 请求,检查 sepia.htc 文件是否存在 xmlHttpCheck.open('GET', window.polyfilter_scriptpath + 'htc/sepia.htc', true); // 监听请求状态变化 xmlHttpCheck.onreadystatechange = function(){ // 当请求完成且状态码不是 200 时,说明路径可能有误 if(xmlHttpCheck.readyState == 4 && xmlHttpCheck.status != 200){ // 弹出提示框,提示用户配置正确的绝对路径 alert('The configured path \r\rvar polyfilter_scriptpath = "' + window.polyfilter_scriptpath + '"\r\rseems wrong!\r\rConfigure the polyfill\'s correct absolute(!) script path before referencing the css-filters-polyfill.js, like so:\r\rvar polyfilter_scriptpath = "/js/css-filters-polyfill/";\r\rLeaving IE dead in the water is no option. You damn Mac user... ;)'); } }; try{ // 发送请求 xmlHttpCheck.send(null); } catch(e){} },2000); // 获取页面上所有的样式表元素 var stylesheets = document.querySelectorAll ? document.querySelectorAll('style,link[rel="stylesheet"]') : document.getElementsByTagName('*'); // 遍历所有样式表元素 for(var i = 0; i < stylesheets.length; i++){ // 使用立即执行函数避免闭包问题 (function(i){ // 根据元素的节点名进行不同处理 switch(stylesheets[i].nodeName){ default: // 如果节点名不是 STYLE 或 LINK,不做处理,直接跳出 break; case 'STYLE': // 当节点名是 STYLE 时,说明是内联样式表 polyfilter._stylesheets.push({ // 获取样式表的媒体查询属性,如果没有则默认为 'all' media: stylesheets[i].media || 'all', // 获取样式表的内容 content: stylesheets[i].innerHTML }); break; case 'LINK': // 当节点名是 LINK 时 if(stylesheets[i].rel === 'stylesheet'){ // 且该 LINK 元素的 rel 属性为 'stylesheet',说明是外部样式表 var index = polyfilter._stylesheets.length; // 将该外部样式表的信息添加到 _stylesheets 数组中 polyfilter._stylesheets.push({ // 获取样式表的媒体查询属性,如果没有则默认为 'all' media: stylesheets[i].media || 'all' }); // 待处理的外部样式表数量加 1 polyfilter._pending_stylesheets++; // 获取外部样式表的链接地址 var href = stylesheets[i].href; // 如果不是开发模式,且浏览器支持 localStorage,并且 localStorage 中已经缓存了该样式表 if(!polyfilter._development_mode && window.localStorage && window.localStorage.getItem('polyfilter_' + href)){ // 待处理的外部样式表数量减 1 polyfilter._pending_stylesheets--; // 从 localStorage 中获取缓存的样式表内容,并赋值给 _stylesheets 数组中对应的元素 polyfilter._stylesheets[index].content = localStorage.getItem('polyfilter_' + href); // 如果所有待处理的外部样式表都已处理完毕 if(polyfilter._pending_stylesheets === 0){ // 调用 process 方法进行后续处理 polyfilter.process(); } } // 无论是否有缓存,都要发起请求获取最新的样式表内容 try{ // 根据浏览器支持情况创建 XMLHttpRequest 对象 if(window.XMLHttpRequest) { var xmlHttp = new XMLHttpRequest(); } else if(window.ActiveXObject) { var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } // 打开一个 GET 请求,请求外部样式表 xmlHttp.open('GET', href, true); // 监听请求状态变化 xmlHttp.onreadystatechange = function(){ // 当请求完成时 if(xmlHttp.readyState === 4){ // 如果状态码为 0,可能是跨域问题导致请求失败 if(xmlHttp.status === 0){ if(window.console) console.log('Could not fetch external CSS via HTTP-Request ' + href + '. Probably because of cross origin.'); // 如果 _stylesheets 数组中对应的元素还没有内容 if(!polyfilter._stylesheets[index].content){ // 待处理的外部样式表数量减 1 polyfilter._pending_stylesheets--; // 将响应内容赋值给 _stylesheets 数组中对应的元素 polyfilter._stylesheets[index].content = xmlHttp.responseText; // 如果所有待处理的外部样式表都已处理完毕 if(polyfilter._pending_stylesheets === 0){ // 调用 process 方法进行后续处理 polyfilter.process(); } } } else { // 如果状态码正常 if(!polyfilter._stylesheets[index].content){ // 待处理的外部样式表数量减 1 polyfilter._pending_stylesheets--; // 将响应内容赋值给 _stylesheets 数组中对应的元素 polyfilter._stylesheets[index].content = xmlHttp.responseText; // 如果所有待处理的外部样式表都已处理完毕 if(polyfilter._pending_stylesheets === 0){ // 调用 process 方法进行后续处理 polyfilter.process(); } } // 如果不是开发模式,且浏览器支持 localStorage if(!polyfilter._development_mode && window.localStorage){ try{ // 将样式表内容缓存到 localStorage 中 window.localStorage.setItem('polyfilter_' + href, polyfilter._stylesheets[index].content); } catch(e){ // 如果 localStorage 空间不足,输出提示信息 if(window.console) console.log('Local storage quota have been exceeded. Caching of stylesheet ' + href + ' is not possible'); } } } } }; try{ // 发送请求 xmlHttp.send(null); } catch(e){ // 如果请求发送失败,可能是使用 file:// 协议进行测试导致的 if(window.console) console.log('Could not fetch external CSS via HTTP-Request ' + href + '. Are you maybe testing using the file://-protocol?'); if(!polyfilter._stylesheets[index].content){ // 待处理的外部样式表数量减 1 polyfilter._pending_stylesheets--; if(polyfilter._pending_stylesheets === 0){ // 调用 process 方法进行后续处理 polyfilter.process(); } } } } catch(e){} } break; } })(i); } // 如果所有待处理的外部样式表都已处理完毕 if(this._pending_stylesheets === 0){ // 调用 process 方法进行后续处理 this.process(); } }, // 处理 CSS 规则声明的函数 _processDeclarations: function(rule){ var newstyles = ''; // 遍历规则中的所有声明 for(var k in rule.declarations){ var declaration = rule.declarations[k]; // 如果声明的属性是 'filter' if(declaration.property === 'filter'){ if(document.querySelectorAll){ // 检查浏览器是否支持 document.querySelectorAll 方法 // 使用选择器 rule.mSelectorText 获取匹配的元素集合 var elems = document.querySelectorAll(rule.mSelectorText); for(var k = 0; k < elems.length; k++){ // 遍历匹配的元素集合,将当前 filter 声明的值存储到元素的 style.polyfilterStore 属性中 // 方便后续可能的使用 elems[k].style.polyfilterStore = declaration.valueText; } } // 获取 filter 属性声明的原始值 var gluedvalues = declaration.valueText; // 将原始值按 ")" 后面跟着一个或多个空白字符进行分割,得到每个单独的滤镜值 var values = gluedvalues.split(/\)\s+/), // 初始化一个对象,用于存储不同浏览器支持的滤镜属性值 properties = { filtersW3C: [], // 存储 W3C 标准的滤镜属性值 filtersWebKit: [], // 存储 WebKit 内核浏览器(如 Safari、旧版 Chrome)的滤镜属性值 filtersSVG: [], // 存储 SVG 滤镜属性值 filtersIE: [], // 存储 Internet Explorer 浏览器的滤镜属性值 behaviorsIE: [] // 存储 Internet Explorer 浏览器的行为属性值(用于模拟滤镜效果) }; // 遍历分割后的每个滤镜值 for(var idx in values){ var value = values[idx] + ')'; // 恢复完整的滤镜值 // 调用 polyfilter 对象的 convert 方法,将单个滤镜值转换为不同浏览器支持的属性值 var currentproperties = polyfilter.convert(value); // 遍历转换后的属性值对象 for(var key in currentproperties){ if(typeof properties[key] !== 'undefined'){ // 如果当前属性在 properties 对象中存在,则将转换后的属性值追加到对应数组中 properties[key] = properties[key].concat(currentproperties[key]); } } } // 开始构建新的样式规则字符串,添加选择器和左花括号 newstyles += rule.mSelectorText + '{'; if(properties['filtersW3C'].length > 0){ // 如果存在 W3C 标准的滤镜属性值 var filter = webkitFilter = mozFilter = oFilter = msFilter = properties['filtersW3C'].join(' '); // 将 W3C 标准的滤镜属性值用空格连接成一个字符串 if(properties['filtersWebKit'] && properties['filtersWebKit'].length > 0){ // 如果存在 WebKit 内核浏览器的滤镜属性值,则使用这些值替换 webkitFilter webkitFilter = properties['filtersWebKit'].join(' '); } if(typeof this._ie === 'undefined'){ // 如果不是 Internet Explorer 浏览器,则添加 -ms-filter 属性 newstyles += '-ms-filter:' + msFilter + ';'; } // 添加不同浏览器前缀的 filter 属性 newstyles += '-webkit-filter:' + webkitFilter + ';'; newstyles += '-moz-filter:' + mozFilter + ';'; newstyles += '-o-filter:' + oFilter + ';'; } if(properties['filtersSVG'].length > 0){ if(properties['filtersSVG'][0] != 'none'){ // 如果 SVG 滤镜属性值不为 'none' // 生成一个唯一的 ID,用于标识 SVG 滤镜 var id = gluedvalues.replace(/[^a-z0-9]/g,''); if(typeof this._svg_cache[id] === 'undefined'){ // 如果 SVG 滤镜缓存中不存在该 ID 对应的 SVG 元素 // 创建一个新的 SVG 元素,并将其存储到缓存中 this._svg_cache[id] = this._create_svg(id,properties['filtersSVG']); if(typeof XMLSerializer === 'undefined'){ // 如果浏览器不支持 XMLSerializer,则直接将 SVG 元素添加到 body 中 document.body.appendChild(this._svg_cache[id]); } else { // 如果浏览器支持 XMLSerializer var s = new XMLSerializer(); // 将 SVG 元素序列化为字符串 var svgString = s.serializeToString(this._svg_cache[id]); if(svgString.search('SourceGraphic') != -1){ // 如果 SVG 字符串中包含 'SourceGraphic',则将 SVG 元素添加到 body 中 document.body.appendChild(this._svg_cache[id]); } } } if(typeof XMLSerializer === 'undefined'){ // 如果浏览器不支持 XMLSerializer,则使用 url(#id) 形式引用 SVG 滤镜 newstyles += 'filter: url(#' + id + ')'; } else { var s = new XMLSerializer(); // 将 SVG 元素序列化为字符串 var svgString = s.serializeToString(this._svg_cache[id]); if(svgString.search('SourceGraphic') != -1){ // 如果 SVG 字符串中包含 'SourceGraphic',则使用 url(#id) 形式引用 SVG 滤镜 newstyles += 'filter: url(#' + id + ')'; } else { // 否则,使用 data URI 形式引用 SVG 滤镜 newstyles += 'filter: url(\'data:image/svg+xml;utf8,' + svgString + '#' + id + '\')'; } } } else { // 如果 SVG 滤镜属性值为 'none',则添加 filter: none; newstyles += 'filter: none;'; } } if(typeof this._ie !== 'undefined'){ // 如果是 Internet Explorer 浏览器 if(properties['filtersIE'].length > 0){ // 如果存在 Internet Explorer 浏览器的滤镜属性值 var filtersIE = properties['filtersIE'].join(' '); // 添加 filter 属性 newstyles += 'filter:' + filtersIE + ';'; } if(properties['behaviorsIE'].length > 0){ // 如果存在 Internet Explorer 浏览器的行为属性值 var behaviorsIE = properties['behaviorsIE'].join(' '); // 添加 behavior 属性 newstyles += 'behavior:' + behaviorsIE + ';'; } } // 完成当前选择器样式规则的构建,添加右花括号和换行符 newstyles += '}\r\n'; } } // 返回新生成的样式规则字符串 return newstyles; }, // 存储 .htc 文件的绝对路径 scriptpath: window.polyfilter_scriptpath ? window.polyfilter_scriptpath : (function(){ // 如果没有在 window 对象中配置 polyfilter_scriptpath // 弹出提示框,提醒用户在引用 css - filters - polyfill.js 之前配置正确的绝对路径 alert('Please configure the polyfill\'s absolute(!) script path before referencing the css-filters-polyfill.js, like so:\r\nvar polyfilter_scriptpath = "/js/css-filters-polyfill/";'); // 默认返回当前目录 return './' })(), // 处理样式表的主函数 process: function(){ // 创建一个 CSS 解析器实例 var parser = new CSSParser(); // 遍历存储的所有样式表 for(var i = 0; i < this._stylesheets.length; i++){ // 初始化新样式字符串 var newstyles = ''; // 使用解析器解析当前样式表的内容 var sheet = parser.parse(this._stylesheets[i].content, false, true); // 如果解析结果不为空 if(sheet !== null) for(var j in sheet.cssRules){ var rule = sheet.cssRules[j]; // 根据规则类型进行不同处理 switch(rule.type){ default: // 默认情况不做处理 break; case 1: // 类型 1 通常表示普通的样式规则 // 调用 _processDeclarations 方法处理该规则,并将结果添加到新样式字符串中 newstyles += this._processDeclarations(rule); break; case 4: // 类型 4 通常表示 @media 媒体查询规则 // 构建媒体查询的开头部分 newstyles += '@media ' + rule.media.join(',') + '{'; // 遍历媒体查询内的子规则 for(var k in rule.cssRules){ var mediarule = rule.cssRules[k]; // 调用 _processDeclarations 方法处理子规则,并将结果添加到新样式字符串中 newstyles += this._processDeclarations(mediarule); } // 构建媒体查询的结尾部分 newstyles += '}'; break; } } // 创建一个新的