From df7b5a641e1170b30234be9290e0bc6a77ee1edc Mon Sep 17 00:00:00 2001 From: LFE <756741044@qq.com> Date: Fri, 13 Dec 2024 23:00:51 +0800 Subject: [PATCH] lfe --- src/com/yj/bean/CartItem.java | 49 ++++- src/com/yj/bean/Page.java | 36 +++- src/com/yj/dao/impl/BaseDao.java | 91 +++++++--- src/com/yj/test/CartTest.java | 20 ++ src/com/yj/test/JDBCUtilsTest.java | 23 ++- src/com/yj/utils/JDBCUtils.java | 102 +++++++---- src/com/yj/utils/PaymentUtil.java | 90 +++++---- src/com/yj/web/CartServlet.java | 121 ++++++------ web/pages/cart/cart.jsp | 121 +++++++----- web/pages/cart/checkout.jsp | 58 ++++-- web/pages/cart/pay.jsp | 60 ++++-- web/static/script/jquery-1.7.2.js | 283 ++++++++++++++++++----------- 12 files changed, 690 insertions(+), 364 deletions(-) diff --git a/src/com/yj/bean/CartItem.java b/src/com/yj/bean/CartItem.java index c9feca1..bcc656f 100644 --- a/src/com/yj/bean/CartItem.java +++ b/src/com/yj/bean/CartItem.java @@ -10,72 +10,101 @@ import java.math.BigDecimal; /** * 购物车商品项 */ +// 定义一个名为CartItem的类,代表购物车中的一个商品项 public class CartItem { + // 定义一个私有变量id,用于存储商品的唯一标识符 private Integer id; + // 定义一个私有变量name,用于存储商品的名称 private String name; + // 定义一个私有变量count,用于存储商品的数量 private Integer count; + // 定义一个私有变量price,用于存储商品的单价,使用BigDecimal类型以支持精确的小数计算 private BigDecimal price; + // 定义一个私有变量totalPrice,用于存储商品的总价(单价乘以数量),同样使用BigDecimal类型 private BigDecimal totalPrice; + // 定义一个公开的getter方法,用于获取商品的id public Integer getId() { return id; } - + // 定义一个公开的setter方法,用于设置商品的id public void setId(Integer id) { this.id = id; } - + // 定义一个公开的getter方法,用于获取商品的名称 public String getName() { return name; } - + // 定义一个公开的setter方法,用于设置商品的名称 public void setName(String name) { this.name = name; } - + // 定义一个公开的getter方法,用于获取商品的数量 public Integer getCount() { return count; } - + // 定义一个公开的setter方法,用于设置商品的数量 public void setCount(Integer count) { this.count = count; } - + // 定义一个公开的getter方法,用于获取商品的单价 public BigDecimal getPrice() { return price; } - + // 定义一个公开的setter方法,用于设置商品的单价 public void setPrice(BigDecimal price) { this.price = price; } - + // 定义一个公开的getter方法,用于获取商品的总价 public BigDecimal getTotalPrice() { return totalPrice; } - + // 定义一个公开的setter方法,用于设置商品的总价 public void setTotalPrice(BigDecimal totalPrice) { this.totalPrice = totalPrice; } + // 定义一个公开的构造方法,用于创建CartItem对象并初始化其所有属性 public CartItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal totalPrice) { + // 使用this关键字将传入的id参数赋值给当前对象的id属性 this.id = id; + // 设置商品的唯一标识符 + + // 使用this关键字将传入的name参数赋值给当前对象的name属性 this.name = name; + // 设置商品的名称 + + // 使用this关键字将传入的count参数赋值给当前对象的count属性 this.count = count; + // 设置商品的数量 + + // 使用this关键字将传入的price参数赋值给当前对象的price属性 this.price = price; + // 设置商品的单价,使用BigDecimal类型以支持精确的小数计算 + + // 使用this关键字将传入的totalPrice参数赋值给当前对象的totalPrice属性 this.totalPrice = totalPrice; + // 设置商品的总价(单价乘以数量),同样使用BigDecimal类型 } - + // 定义一个无参构造方法,用于创建CartItem对象时不初始化任何属性 public CartItem() { } + // 重写toString方法,用于返回CartItem对象的字符串表示形式 @Override public String toString() { + // 返回的字符串包含CartItem对象的所有属性信息 return "Cart{" + "id=" + id + + // 显示商品的id ", name='" + name + '\'' + + // 显示商品的名称 ", count=" + count + + // 显示商品的数量 ", price=" + price + + // 显示商品的单价 ", totalPrice=" + totalPrice + + // 显示商品的总价 '}'; } } diff --git a/src/com/yj/bean/Page.java b/src/com/yj/bean/Page.java index 5869cab..ed5f155 100644 --- a/src/com/yj/bean/Page.java +++ b/src/com/yj/bean/Page.java @@ -1,90 +1,118 @@ package com.yj.bean; +// 定义包名,用于组织相关的类和接口 import java.util.List; +// 导入List接口,用于存储数据集合 /** - * @author yj - * @create 2020-08-25 9:27 + * @author yj // 类的作者 + * @create 2020-08-25 9:27 // 类的创建时间 */ public class Page { + // 定义一个泛型类Page,T表示该Page类可以持有任何类型的对象 public static final Integer PAGE_SIZE = 4; + // 定义一个常量,表示每页显示的数据条数 //当前页 private Integer pageNo; + // 当前页码 //总页码 private Integer pageTotal; + // 总页数 //当前页显示数量 private Integer pageSize = PAGE_SIZE; + // 每页显示的数据条数,默认为常量PAGE_SIZE的值 //总的记录数 private Integer pageTotalCount; + // 数据总条数 //当前页数据 private List items; + // 存储当前页的数据 //分页条的请求地址 private String url; + // 分页条请求的URL地址 + // 获取分页条请求的URL地址 public String getUrl() { return url; } + // 设置分页条请求的URL地址 public void setUrl(String url) { this.url = url; } + // 获取当前页码 public Integer getPageNo() { return pageNo; } - + // 获取总页数 public Integer getPageTotal() { return pageTotal; } + // 设置总页数 public void setPageTotal(Integer pageTotal) { this.pageTotal = pageTotal; } + // 获取每页显示的数据条数 public Integer getPageSize() { return pageSize; } + // 设置每页显示的数据条数 public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } + // 获取数据总条数 public Integer getPageTotalCount() { return pageTotalCount; } + // 设置数据总条数 public void setPageTotalCount(Integer pageTotalCount) { this.pageTotalCount = pageTotalCount; } + // 获取当前页的数据 public List getItems() { return items; } + // 设置当前页的数据 public void setItems(List items) { this.items = items; } + // 设置当前页码 public void setPageNo(Integer pageNo) { this.pageNo = pageNo; } + // 重写toString方法,用于打印Page对象的详细信息 @Override public String toString() { return "Page{" + "pageNo=" + pageNo + + // 当前页码 ", pageTotal=" + pageTotal + + // 总页数 ", pageSize=" + pageSize + + // 每页显示的数据条数 ", pageTotalCount=" + pageTotalCount + + // 数据总条数 ", items=" + items + + // 当前页的数据 ", url='" + url + '\'' + + // 分页条请求的URL地址 '}'; } -} +} \ No newline at end of file diff --git a/src/com/yj/dao/impl/BaseDao.java b/src/com/yj/dao/impl/BaseDao.java index 60a0e28..4a215d7 100644 --- a/src/com/yj/dao/impl/BaseDao.java +++ b/src/com/yj/dao/impl/BaseDao.java @@ -1,85 +1,124 @@ package com.yj.dao.impl; +// 定义包名,组织相关的类 import com.yj.utils.JDBCUtils; +// 导入JDBC工具类,用于获取数据库连接 import org.apache.commons.dbutils.QueryRunner; +// 导入DbUtils的QueryRunner类,用于简化JDBC操作 import org.apache.commons.dbutils.handlers.BeanHandler; +// 导入BeanHandler,用于将结果集的第一行数据封装为JavaBean对象 import org.apache.commons.dbutils.handlers.BeanListHandler; +// 导入BeanListHandler,用于将结果集的每一行数据封装为JavaBean对象,并存储在List集合中 import org.apache.commons.dbutils.handlers.ScalarHandler; +// 导入ScalarHandler,用于返回结果集的一行一列数据 import java.sql.Connection; +// 导入Connection接口,用于数据库连接 import java.sql.SQLException; +// 导入SQLException类,处理SQL异常 import java.util.List; +// 导入List接口,用于存储数据集合 /** - * @author yj - * @create 2020-08-21 11:32 + * @author yj // 类的作者 + * @create 2020-08-21 11:32 // 类的创建时间 */ public abstract class BaseDao { - //使用DbUtils操作数据库 + // 定义一个抽象类BaseDao,作为数据访问层的基类 + // 使用DbUtils操作数据库 private QueryRunner queryRunner = new QueryRunner(); - /* - *update()方法用来执行Insert\Update\Delete语句 + // 初始化QueryRunner对象,用于执行SQL语句 + + /** + * update()方法用来执行Insert\Update\Delete语句 + * @param sql 要执行的SQL语句 + * @param args SQL语句中的参数值,可变参数 * @return 如果返回-1则失败,成功返回影响的行数 */ - public int update(String sql,Object ... args) { + public int update(String sql, Object ... args) { Connection connection = JDBCUtils.getConnection(); + // 调用JDBCUtils工具类获取数据库连接 try { - return queryRunner.update(connection,sql,args); + // 使用QueryRunner的update方法执行更新操作,并返回影响的行数 + return queryRunner.update(connection, sql, args); } catch (SQLException e) { e.printStackTrace(); + // 打印SQL异常堆栈信息 throw new RuntimeException(e); + // 将SQL异常转换为运行时异常抛出 + } finally { + // 注意:原代码中缺少了关闭连接的逻辑,应在此处添加(但在实际使用中,JDBCUtils的getConnection可能已负责关闭) + // JDBCUtils.close(connection); // 关闭数据库连接 } } /** - *查询返回一个Javabean的SQL语句 + * 查询返回一个Javabean的SQL语句 * @param type 返回的对象类型 - * @param sql 执行的sql语句 - * @param args sql对应的参数值 + * @param sql 执行的SQL语句 + * @param args SQL语句对应的参数值,可变参数 * @param 返回的类型的泛型 - * @return + * @return 返回查询结果的第一行数据封装成的JavaBean对象 */ - public T queryForOne(Class type,String sql,Object ... args) { + public T queryForOne(Class type, String sql, Object ... args) { Connection connection = JDBCUtils.getConnection(); + // 获取数据库连接 try { - return queryRunner.query(connection,sql,new BeanHandler(type),args); + // 使用QueryRunner的query方法执行查询操作,并将结果集的第一行数据封装为指定的JavaBean对象 + return queryRunner.query(connection, sql, new BeanHandler(type), args); } catch (SQLException e) { e.printStackTrace(); + // 打印SQL异常堆栈信息 throw new RuntimeException(e); + // 将SQL异常转换为运行时异常抛出 + } finally { + // 注意:同样缺少了关闭连接的逻辑,应在实际使用中添加 } } /** - *查询返回多个Javabean的SQL语句 + * 查询返回多个Javabean的SQL语句 * @param type 返回的对象类型 - * @param sql 执行的sql语句 - * @param args sql对应的参数值 + * @param sql 执行的SQL语句 + * @param args SQL语句对应的参数值,可变参数 * @param 返回的类型的泛型 - * @return + * @return 返回查询结果的每一行数据封装成的JavaBean对象列表 */ public List queryForList(Class type, String sql, Object ... args) { Connection connection = JDBCUtils.getConnection(); + // 获取数据库连接 try { - return queryRunner.query(connection,sql,new BeanListHandler(type),args); + // 使用QueryRunner的query方法执行查询操作,并将结果集的每一行数据封装为指定的JavaBean对象,存储在List集合中返回 + return queryRunner.query(connection, sql, new BeanListHandler(type), args); } catch (SQLException e) { e.printStackTrace(); + // 打印SQL异常堆栈信息 throw new RuntimeException(e); + // 将SQL异常转换为运行时异常抛出 + } finally { + // 注意:同样缺少了关闭连接的逻辑,应在实际使用中添加 } } /** - * 执行返回一行一列的sql语句 - * @param sql 执行的sql语句 - * @param args sql对应的参数值 - * @return + * 执行返回一行一列的SQL语句 + * @param sql 执行的SQL语句 + * @param args SQL语句对应的参数值,可变参数 + * @return 返回查询结果的一行一列数据 */ - public Object queryForSingleValue(String sql,Object ... args) { + public Object queryForSingleValue(String sql, Object ... args) { Connection connection = JDBCUtils.getConnection(); + // 获取数据库连接 try { - return queryRunner.query(connection,sql,new ScalarHandler(),args); - }catch (SQLException e) { + // 使用QueryRunner的query方法执行查询操作,并返回结果集的一行一列数据 + return queryRunner.query(connection, sql, new ScalarHandler(), args); + } catch (SQLException e) { e.printStackTrace(); + // 打印SQL异常堆栈信息 throw new RuntimeException(e); + // 将SQL异常转换为运行时异常抛出 + } finally { + // 注意:同样缺少了关闭连接的逻辑,但在实际中,JDBCUtils的getConnection可能已负责关闭连接 } } -} +} \ No newline at end of file diff --git a/src/com/yj/test/CartTest.java b/src/com/yj/test/CartTest.java index f4b6506..7562506 100644 --- a/src/com/yj/test/CartTest.java +++ b/src/com/yj/test/CartTest.java @@ -10,44 +10,64 @@ import java.math.BigDecimal; * @author yj * @create 2020-08-26 20:30 */ +// 定义一个测试类CartTest,用于测试Cart类的功能 public class CartTest { + // 使用@Test注解标记的方法表示这是一个测试方法,通常与测试框架(如JUnit)一起使用 @Test public void addItem() { + // 创建一个新的Cart对象 Cart cart = new Cart(); + // 向购物车中添加一个商品项,注意这里totalPrice应该是根据price和count计算得出的,但此处直接给出 cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); + // 再次添加相同的商品项,理论上应该更新数量而不是添加新项(但根据代码,它会添加新项) cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); + // 添加一个不同的商品项 cart.addItem(new CartItem(2,"你妹的",1,new BigDecimal(5),new BigDecimal(66))); + // 打印购物车的内容,输出将取决于Cart类的toString方法实现 System.out.println(cart); } + // 另一个测试方法,用于测试删除商品项的功能 @Test public void deleteItem() { + // 创建一个新的Cart对象并添加一些商品项 Cart cart = new Cart(); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(2,"你妹的",1,new BigDecimal(5),new BigDecimal(66))); + // 根据商品ID删除商品项,理论上应该删除所有ID匹配的项 cart.deleteItem(1); + // 打印购物车的内容,查看删除操作的结果 System.out.println(cart); } + // 测试清空购物车的方法 @Test public void clear() { + // 创建一个新的Cart对象并添加一些商品项 Cart cart = new Cart(); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(2,"你妹的",1,new BigDecimal(5),new BigDecimal(66))); + // 清空购物车中的所有商品项 cart.clear(); + // 打印购物车的内容,应该为空 System.out.println(cart); } + // 测试更新商品数量的方法 @Test public void updateCount() { + // 创建一个新的Cart对象并添加一些商品项 Cart cart = new Cart(); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(2,"你妹的",1,new BigDecimal(5),new BigDecimal(66))); + // 根据商品ID更新商品数量,注意这里可能需要根据业务逻辑决定是更新现有项还是添加新项 cart.updateCount(1,5); + // 假设意图是将ID为1的商品数量更新为5 + // 打印购物车的内容,查看更新操作的结果 System.out.println(cart); } } \ No newline at end of file diff --git a/src/com/yj/test/JDBCUtilsTest.java b/src/com/yj/test/JDBCUtilsTest.java index 3c762af..546fbd6 100644 --- a/src/com/yj/test/JDBCUtilsTest.java +++ b/src/com/yj/test/JDBCUtilsTest.java @@ -1,29 +1,44 @@ package com.yj.test; +// 声明包名,用于组织类 import com.yj.utils.JDBCUtils; +// 导入JDBCUtils工具类,用于数据库连接 import org.junit.Test; +// 导入JUnit测试注解 import java.sql.Connection; +// 导入Connection接口,用于数据库连接 import java.sql.ResultSet; +// 导入ResultSet接口,用于处理查询结果集 import java.sql.Statement; +// 导入Statement接口,用于执行SQL语句 /** * @author yj * @create 2020-08-21 11:25 */ -public class JDBCUtilsTest { - @Test +public class JDBCUtilsTest { // 定义一个测试类 + @Test // 标记此方法为一个测试方法 public void testJdbcUtils() { + // 定义一个测试方法 Connection connection = JDBCUtils.getConnection(); + // 通过JDBCUtils工具类获取数据库连接 String sql = "select * from t_book"; + // 定义SQL查询语句,查询t_book表中的所有记录 try (Statement st = connection.createStatement()) { + // 创建Statement对象用于执行SQL语句,使用try-with-resources语法自动关闭资源 ResultSet rs = st.executeQuery(sql); - while(rs.next()){ + // 执行查询语句,并将结果存储在ResultSet对象中 + while(rs.next()){ // 遍历ResultSet对象中的每一条记录 System.out.println(rs.getString("author")+" " + // 从当前记录中获取"author"字段的值,并输出 +rs.getString("name")); + // 从当前记录中获取"name"字段的值,并输出,与"author"字段的值用空格分隔 } }catch (Exception e) { + // 捕获并处理异常 e.printStackTrace(); + // 打印异常堆栈信息 } } -} +} \ No newline at end of file diff --git a/src/com/yj/utils/JDBCUtils.java b/src/com/yj/utils/JDBCUtils.java index e189e44..f991314 100644 --- a/src/com/yj/utils/JDBCUtils.java +++ b/src/com/yj/utils/JDBCUtils.java @@ -1,79 +1,107 @@ package com.yj.utils; +// 声明包名 import com.alibaba.druid.pool.DruidDataSource; +// 导入Druid连接池的数据源类 import com.alibaba.druid.pool.DruidDataSourceFactory; +// 导入Druid连接池的工厂类 import com.alibaba.druid.util.JdbcUtils; +// 导入Druid的工具类(注意:此处的JdbcUtils并非JDK自带的,而是Druid框架提供的,但在此代码中未直接使用) import java.io.InputStream; +// 导入输入流接口 import java.sql.Connection; +// 导入数据库连接接口 import java.sql.SQLException; +// 导入SQL异常类 import java.util.Properties; +// 导入属性类 /** * @author yj * @create 2020-08-21 10:48 */ public class JDBCUtils { + // 定义一个工具类 private static DruidDataSource dataSource; + // 定义一个静态的Druid数据源对象 private static ThreadLocal conns = new ThreadLocal(); + // 定义一个ThreadLocal对象,用于存储每个线程的数据库连接 static { + // 静态代码块,用于初始化静态变量 try { Properties properties = new Properties(); - //读取jdbc.properties属性配置文件 - InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties" ); - //从流中加载数据 + // 创建一个属性对象 + // 读取jdbc.properties属性配置文件 + InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); + // 从流中加载数据到属性对象中 properties.load(inputStream); - //创建数据库连接池 + // 创建数据库连接池 dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); + // 使用Druid工厂类创建数据源对象 } catch (Exception e) { + // 捕获异常 e.printStackTrace(); + // 打印异常堆栈信息 } - } - - - - - /* - 获取数据库连接池的连接 - @retrun 如果返回null则连接失败 + /** + * 获取数据库连接池的连接 + * @return 如果返回null则连接失败 */ public static Connection getConnection() { - Connection conn = conns.get(); - if(conn == null) { - try { - conn = dataSource.getConnection();//从数据库池中获取连接 - conns.set(conn);//保存到ThreadLocal对象中,供后面的JDBC操作使用 - conn.setAutoCommit(false);//设置为手动管理事务 - } catch (SQLException throwables) { - throwables.printStackTrace(); - } - } - return conn; + // 定义一个静态方法,用于获取数据库连接 + Connection conn = conns.get(); + // 从ThreadLocal对象中获取当前线程的连接 + if(conn == null) { + // 如果当前线程没有连接 + try { + conn = dataSource.getConnection(); + // 从数据库池中获取连接 + conns.set(conn); + // 将连接保存到ThreadLocal对象中,供后面的JDBC操作使用 + conn.setAutoCommit(false); + // 设置为手动管理事务 + } catch (SQLException throwables) { + // 捕获SQL异常 + throwables.printStackTrace(); + // 打印异常堆栈信息 + } + } + return conn; + // 返回连接 } /** * 提交事务并关闭释放连接 */ public static void commitAndClose() { + // 定义一个静态方法,用于提交事务并关闭连接 Connection connection = conns.get(); + // 从ThreadLocal对象中获取连接 if(connection!=null) { + // 如果连接不为空 try { - connection.commit();//提交事务 + connection.commit();// 提交事务 } catch (SQLException throwables) { + // 捕获SQL异常 throwables.printStackTrace(); + // 打印异常堆栈信息 }finally { try { - connection.close();//关闭连接释放资源 + connection.close(); + // 关闭连接释放资源 } catch (SQLException throwables) { + // 捕获SQL异常 throwables.printStackTrace(); + // 打印异常堆栈信息 } } } - //一定要执行remove操作,否则就会出错,因为tomcat底层使用了线程池技术 + // 移除ThreadLocal对象中的连接,防止内存泄漏(特别是在使用线程池时) conns.remove(); } @@ -81,35 +109,45 @@ public class JDBCUtils { * 回滚事务并关闭释放连接 */ public static void rollbackAndClose() { + // 定义一个静态方法,用于回滚事务并关闭连接 Connection connection = conns.get(); + // 从ThreadLocal对象中获取连接 if(connection!=null) { + // 如果连接不为空 try { - connection.rollback();//回滚事务 + connection.rollback(); + // 回滚事务 } catch (SQLException throwables) { + // 捕获SQL异常 throwables.printStackTrace(); + // 打印异常堆栈信息 }finally { try { - connection.close();//关闭连接释放资源 + connection.close(); + // 关闭连接释放资源 } catch (SQLException throwables) { + // 捕获SQL异常 throwables.printStackTrace(); + // 打印异常堆栈信息 } } } - //一定要执行remove操作,否则就会出错,因为tomcat底层使用了线程池技术 + // 移除ThreadLocal对象中的连接,防止内存泄漏(特别是在使用线程池时) conns.remove(); } /* - 关闭连接,放回数据库连接池 - + (注释掉的代码)关闭连接,放回数据库连接池 + (注意:由于使用了连接池,通常不需要手动关闭连接放回池中,连接池会自动管理连接的回收和重用) public static void close(Connection conn) { if(conn != null) { try { conn.close(); + // 这里实际上并不是将连接关闭,而是将连接归还给连接池(对于连接池而言) } catch (SQLException throwables) { throwables.printStackTrace(); } } }*/ -} +} \ No newline at end of file diff --git a/src/com/yj/utils/PaymentUtil.java b/src/com/yj/utils/PaymentUtil.java index fbdb457..a3ba130 100644 --- a/src/com/yj/utils/PaymentUtil.java +++ b/src/com/yj/utils/PaymentUtil.java @@ -12,10 +12,11 @@ import java.util.Arrays; public class PaymentUtil { + // 定义字符编码 private static String encodingCharset = "UTF-8"; /** - * 生成hmac方法 + * 生成hmac方法,用于构建支付请求的参数签名 * * @param p0_Cmd 业务类型 * @param p1_MerId 商户编号 @@ -31,45 +32,34 @@ public class PaymentUtil { * @param pd_FrpId 银行编码 * @param pr_NeedResponse 应答机制 * @param keyValue 商户密钥 - * @return + * @return 生成的hmac签名 */ - public static String buildHmac(String p0_Cmd,String p1_MerId, - String p2_Order, String p3_Amt, String p4_Cur,String p5_Pid, String p6_Pcat, - String p7_Pdesc,String p8_Url, String p9_SAF,String pa_MP,String pd_FrpId, - String pr_NeedResponse,String keyValue) { + public static String buildHmac(String p0_Cmd, String p1_MerId, + String p2_Order, String p3_Amt, String p4_Cur, String p5_Pid, String p6_Pcat, + String p7_Pdesc, String p8_Url, String p9_SAF, String pa_MP, String pd_FrpId, + String pr_NeedResponse, String keyValue) { StringBuilder sValue = new StringBuilder(); - // 业务类型 + // 依次追加所有参数到StringBuilder sValue.append(p0_Cmd); - // 商户编号 sValue.append(p1_MerId); - // 商户订单号 sValue.append(p2_Order); - // 支付金额 sValue.append(p3_Amt); - // 交易币种 sValue.append(p4_Cur); - // 商品名称 sValue.append(p5_Pid); - // 商品种类 sValue.append(p6_Pcat); - // 商品描述 sValue.append(p7_Pdesc); - // 商户接收支付成功数据的地址 sValue.append(p8_Url); - // 送货地址 sValue.append(p9_SAF); - // 商户扩展信息 sValue.append(pa_MP); - // 银行编码 sValue.append(pd_FrpId); - // 应答机制 sValue.append(pr_NeedResponse); + // 调用hmacSign方法生成签名 return PaymentUtil.hmacSign(sValue.toString(), keyValue); } /** - * 返回校验hmac方法 + * 验证支付网关回调的hmac签名 * * @param hmac 支付网关发来的加密验证码 * @param p1_MerId 商户编号 @@ -84,57 +74,54 @@ public class PaymentUtil { * @param r8_MP 商户扩展信息 * @param r9_BType 交易结果返回类型 * @param keyValue 密钥 - * @return + * @return 验证结果,true表示验证通过 */ public static boolean verifyCallback(String hmac, String p1_MerId, String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt, String r4_Cur, String r5_Pid, String r6_Order, String r7_Uid, String r8_MP, String r9_BType, String keyValue) { StringBuilder sValue = new StringBuilder(); - // 商户编号 + // 依次追加所有回调参数到StringBuilder sValue.append(p1_MerId); - // 业务类型 sValue.append(r0_Cmd); - // 支付结果 sValue.append(r1_Code); - // 易宝支付交易流水号 sValue.append(r2_TrxId); - // 支付金额 sValue.append(r3_Amt); - // 交易币种 sValue.append(r4_Cur); - // 商品名称 sValue.append(r5_Pid); - // 商户订单号 sValue.append(r6_Order); - // 易宝支付会员ID sValue.append(r7_Uid); - // 商户扩展信息 sValue.append(r8_MP); - // 交易结果返回类型 sValue.append(r9_BType); + // 调用hmacSign方法生成签名并与支付网关的签名比较 String sNewString = PaymentUtil.hmacSign(sValue.toString(), keyValue); return sNewString.equals(hmac); } /** - * @param aValue - * @param aKey - * @return + * 使用HMAC-MD5算法生成签名 + * + * @param aValue 待签名的字符串 + * @param aKey 密钥 + * @return 生成的签名 */ public static String hmacSign(String aValue, String aKey) { + // 初始化内部填充字节数组 byte k_ipad[] = new byte[64]; byte k_opad[] = new byte[64]; byte keyb[]; byte value[]; try { + // 使用指定的字符编码获取字节数组 keyb = aKey.getBytes(encodingCharset); value = aValue.getBytes(encodingCharset); } catch (UnsupportedEncodingException e) { + // 如果不支持指定的字符编码,则使用默认字符编码 keyb = aKey.getBytes(); value = aValue.getBytes(); } + // 填充k_ipad和k_opad数组 Arrays.fill(k_ipad, keyb.length, 64, (byte) 54); Arrays.fill(k_opad, keyb.length, 64, (byte) 92); for (int i = 0; i < keyb.length; i++) { @@ -142,23 +129,32 @@ public class PaymentUtil { k_opad[i] = (byte) (keyb[i] ^ 0x5c); } + // 获取MD5算法的MessageDigest实例 MessageDigest md = null; try { md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { - + // 如果MD5算法不可用,则返回null return null; } + // 对k_ipad和待签名数据进行更新 md.update(k_ipad); md.update(value); - byte dg[] = md.digest(); - md.reset(); + byte dg[] = md.digest(); // 进行第一次摘要 + md.reset(); // 重置MessageDigest实例 + // 对k_opad和第一次摘要结果进行更新 md.update(k_opad); md.update(dg, 0, 16); - dg = md.digest(); - return toHex(dg); + dg = md.digest(); // 进行第二次摘要,得到最终的签名 + return toHex(dg); // 将字节数组转换为十六进制字符串 } + /** + * 将字节数组转换为十六进制字符串 + * + * @param input 字节数组 + * @return 十六进制字符串 + */ public static String toHex(byte input[]) { if (input == null) return null; @@ -169,15 +165,15 @@ public class PaymentUtil { output.append("0"); output.append(Integer.toString(current, 16)); } - return output.toString(); } /** + * 根据参数数组和密钥生成HMAC签名 * - * @param args - * @param key - * @return + * @param args 参数数组 + * @param key 密钥 + * @return 生成的HMAC签名 */ public static String getHmac(String[] args, String key) { if (args == null || args.length == 0) { @@ -191,8 +187,10 @@ public class PaymentUtil { } /** - * @param aValue - * @return + * 使用SHA算法对字符串进行摘要 + * + * @param aValue 待摘要的字符串 + * @return 摘要结果的十六进制字符串 */ public static String digest(String aValue) { aValue = aValue.trim(); diff --git a/src/com/yj/web/CartServlet.java b/src/com/yj/web/CartServlet.java index cfa53cd..b6beb41 100644 --- a/src/com/yj/web/CartServlet.java +++ b/src/com/yj/web/CartServlet.java @@ -1,12 +1,20 @@ +// 导入所需的包和类 package com.yj.web; import com.google.gson.Gson; +// 用于将Java对象转换为JSON字符串 import com.yj.bean.Book; +// 书籍实体类 import com.yj.bean.Cart; +// 购物车实体类 import com.yj.bean.CartItem; +// 购物车商品项实体类 import com.yj.service.BookService; +// 书籍服务接口 import com.yj.service.impl.BookServiceImpl; +// 书籍服务接口的实现类 import com.yj.utils.WebUtils; +// 工具类,用于处理Web请求中的常见操作 import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -15,100 +23,93 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -/** - * @author yj - * @create 2020-08-27 9:47 - */ +// 定义一个Servlet类,用于处理与购物车相关的请求 public class CartServlet extends BaseServlet { + // 创建一个书籍服务实例,用于访问书籍数据 private BookService bookService = new BookServiceImpl(); + // 处理通过AJAX方式添加商品到购物车的请求 protected void ajaxAddItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - int id = WebUtils.parseInt(req.getParameter("id"),0); + // 从请求中获取书籍ID,并转换为整数类型,默认为0 + int id = WebUtils.parseInt(req.getParameter("id"), 0); + // 根据书籍ID查询书籍信息 Book book = bookService.queryBookById(id); - CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice()); + // 创建一个购物车商品项实例,数量为1,单价和总价均为书籍价格 + CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice()); + // 从会话中获取购物车对象,如果不存在则创建一个新的购物车对象 Cart cart = (Cart) req.getSession().getAttribute("cart"); - if(cart==null) { + if (cart == null) { cart = new Cart(); - req.getSession().setAttribute("cart",cart); + req.getSession().setAttribute("cart", cart); } + // 将商品项添加到购物车中 cart.addItem(cartItem); - req.getSession().setAttribute("lastName",cartItem.getName()); + // 在会话中保存最后添加的商品名称(可能用于显示或提示) + req.getSession().setAttribute("lastName", cartItem.getName()); - //返回购物车总数量和最后一个商品的名称 - Map resultMap = new HashMap(); - resultMap.put("totalCount",cart.getTotalCount()); - resultMap.put("lastName",cartItem.getName()); + // 准备返回给客户端的数据,包括购物车总数量和最后一个商品的名称 + Map resultMap = new HashMap(); + resultMap.put("totalCount", cart.getTotalCount()); + resultMap.put("lastName", cartItem.getName()); + // 使用Gson将Map对象转换为JSON字符串 Gson gson = new Gson(); String resultMapJsonString = gson.toJson(resultMap); + // 将JSON字符串写入响应中 resp.getWriter().write(resultMapJsonString); - } - /** - * 加入购物车 - * @param req - * @param resp - * @throws ServletException - * @throws IOException - */ + + // 处理添加商品到购物车的请求(非AJAX方式) protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - int id = WebUtils.parseInt(req.getParameter("id"),0); - Book book = bookService.queryBookById(id); - CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice()); + // 以下代码与ajaxAddItem方法中的代码大致相同,只是最后返回的是页面重定向 + int id = WebUtils.parseInt(req.getParameter("id"), 0); + Book book = bookService.queryBookById(id); + CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice()); Cart cart = (Cart) req.getSession().getAttribute("cart"); - if(cart==null) { + if (cart == null) { cart = new Cart(); - req.getSession().setAttribute("cart",cart); + req.getSession().setAttribute("cart", cart); } cart.addItem(cartItem); - req.getSession().setAttribute("lastName",cartItem.getName()); + req.getSession().setAttribute("lastName", cartItem.getName()); + // 重定向到请求来源页面(通常是通过Referer头部获取的) resp.sendRedirect(req.getHeader("Referer")); } - /** - * 删除商品项 - * @param req - * @param resp - * @throws ServletException - * @throws IOException - */ + // 处理删除购物车中商品项的请求 protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - int id = WebUtils.parseInt(req.getParameter("id"),0); + // 从请求中获取要删除的商品项ID + int id = WebUtils.parseInt(req.getParameter("id"), 0); + // 从会话中获取购物车对象 Cart cart = (Cart) req.getSession().getAttribute("cart"); - if(cart!=null) { + // 如果购物车对象存在,则删除指定ID的商品项 + if (cart != null) { cart.deleteItem(id); - resp.sendRedirect(req.getHeader("Referer")); } + // 重定向到请求来源页面 + resp.sendRedirect(req.getHeader("Referer")); } - /** - * 清空商品项 - * @param req - * @param resp - * @throws ServletException - * @throws IOException - */ + // 处理清空购物车中所有商品项的请求 protected void clearItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - req.getSession().removeAttribute("cart"); + // 从会话中移除购物车对象,从而清空购物车 + req.getSession().removeAttribute("cart"); + // 重定向到请求来源页面 resp.sendRedirect(req.getHeader("Referer")); } - /** - * 修改商品数量 - * @param req - * @param resp - * @throws ServletException - * @throws IOException - */ + // 处理修改购物车中商品项数量的请求 protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - int id = WebUtils.parseInt(req.getParameter("id"),0); - int count = WebUtils.parseInt(req.getParameter("count"),1); + // 从请求中获取要修改的商品项ID和新的数量 + int id = WebUtils.parseInt(req.getParameter("id"), 0); + int count = WebUtils.parseInt(req.getParameter("count"), 1); + // 从会话中获取购物车对象 Cart cart = (Cart) req.getSession().getAttribute("cart"); - if(cart!=null) { - cart.updateCount(id,count); - resp.sendRedirect(req.getHeader("Referer")); + // 如果购物车对象存在,则修改指定ID的商品项数量 + if (cart != null) { + cart.updateCount(id, count); } - + // 重定向到请求来源页面 + resp.sendRedirect(req.getHeader("Referer")); } - -} +} \ No newline at end of file diff --git a/web/pages/cart/cart.jsp b/web/pages/cart/cart.jsp index 84c5289..60c7d91 100644 --- a/web/pages/cart/cart.jsp +++ b/web/pages/cart/cart.jsp @@ -1,28 +1,45 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + <%@ page contentType="text/html;charset=UTF-8" language="java" %> + + - -购物车 + + + 购物车 + <%@include file="/pages/common/header.jsp"%> + - - - -
- - + + + +
+ + +
+ + + + + + + + + + + - - - - - + + + - + + + + - + + + + + + + + + - + + - - - - - - - - - - - - -
商品名称数量单价金额操作
商品名称数量单价金额操作亲,当前购物车为空,快去和小伙伴浏览书籍吧!
亲,当前购物车为空,快去和小伙伴浏览书籍吧! ${entry.value.name} + + + ${entry.value.price}${entry.value.totalPrice}删除
${entry.value.name} - - ${entry.value.price}${entry.value.totalPrice}删除
- + + +
+ 购物车中共有${sessionScope.cart.totalCount}本书籍 + 总金额${sessionScope.cart.totalPrice} + 清空购物车 + 去结账 +
-
+
-
+ - <%@include file="/pages/common/footer.jsp"%> +<%@include file="/pages/common/footer.jsp"%> + - + \ No newline at end of file diff --git a/web/pages/cart/checkout.jsp b/web/pages/cart/checkout.jsp index 8d4efd6..313620c 100644 --- a/web/pages/cart/checkout.jsp +++ b/web/pages/cart/checkout.jsp @@ -1,32 +1,52 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + - -结算页面 + + + + 结算页面 + <%@include file="/pages/common/header.jsp"%> - + - - - -
- -

你的订单已结算,订单号为${sessionScope.orderId},店主很快就会发货啦!

- - -
+ - <%@include file="/pages/common/footer.jsp"%> + + +
+ + +

你的订单已结算,订单号为${sessionScope.orderId},店主很快就会发货啦!

+ + + +
+ +<%@include file="/pages/common/footer.jsp"%> + - \ No newline at end of file + + \ No newline at end of file diff --git a/web/pages/cart/pay.jsp b/web/pages/cart/pay.jsp index 6db9fe5..bc4b993 100644 --- a/web/pages/cart/pay.jsp +++ b/web/pages/cart/pay.jsp @@ -1,78 +1,98 @@ <%-- + 这是一个JSP注释,不会被发送到客户端。它包含了文件的创建信息。 Created by IntelliJ IDEA. User: jhu Date: 2020/10/5 Time: 14:50 To change this template use File | Settings | File Templates. --%> + <%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + + 结算页面 + + <%@include file="/pages/common/header.jsp"%> + + + + + +
+
+ + + + + 支付金额:元 - + + + + - + - + - + - - + + - + + - + +
- 支付金额:

-

请您选择在线支付银行
招商银行 - 招商银行 工商银行 农业银行建设银行 - 建设银行
中国民生银行总行光大银行 - 光大银行 交通银行 深圳发展银行
北京银行兴业银行 - 上海浦东发展银行 - 兴业银行上海浦东发展银行 中信银行

-

-
-
<%@include file="/pages/common/footer.jsp"%> + - - + + \ No newline at end of file diff --git a/web/static/script/jquery-1.7.2.js b/web/static/script/jquery-1.7.2.js index 391f195..53ce4bb 100644 --- a/web/static/script/jquery-1.7.2.js +++ b/web/static/script/jquery-1.7.2.js @@ -4306,124 +4306,203 @@ var getText = Sizzle.getText = function( elem ) { return ret; }; -var Expr = Sizzle.selectors = { - order: [ "ID", "NAME", "TAG" ], - - match: { - ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, - ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, - TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, - PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ - }, - - leftMatch: {}, - - attrMap: { - "class": "className", - "for": "htmlFor" - }, - - attrHandle: { - href: function( elem ) { - return elem.getAttribute( "href" ); + // 定义Expr对象,它包含选择器引擎的一些核心属性和方法,Sizzle.selectors将其引用为Expr + var Expr = Sizzle.selectors = { + // 定义选择器匹配的优先级顺序 + order: [ "ID", "NAME", "TAG" ], + + // 定义一个对象,包含不同类型的选择器匹配正则表达式 + match: { + // ID选择器,匹配以#开头的字符串,后面跟随一个或多个字母、数字、连字符、Unicode字符或转义字符 + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + // 类选择器,匹配以.开头的字符串,后面跟随一个或多个字母、数字、连字符、Unicode字符或转义字符 + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + // 名称选择器,匹配[name='value']的形式,其中value可以是字母、数字、连字符、Unicode字符或转义字符 + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + // 属性选择器,匹配[attr=value]的形式,支持多种属性值和复杂的表达式 + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + // 标签选择器,匹配一个或多个字母、数字、星号、连字符、Unicode字符或转义字符 + TAG: /^((?:[\w\u00c0-\uFFFF*\-]|\\.)+)/, + // 子选择器,匹配:first-child, :last-child, :nth-child(n), :only-child等 + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + // 位置选择器,匹配:nth, :eq, :gt, :lt, :first, :last, :even, :odd等 + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + // 伪类选择器,匹配形如:hover, :focus等的伪类,支持带参数的伪类如:not(.class) + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ }, - type: function( elem ) { - return elem.getAttribute( "type" ); - } - }, - - relative: { - "+": function(checkSet, part){ - var isPartStr = typeof part === "string", - isTag = isPartStr && !rNonWord.test( part ), - isPartStrNotTag = isPartStr && !isTag; - - if ( isTag ) { - part = part.toLowerCase(); - } - for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { - if ( (elem = checkSet[i]) ) { - while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + // leftMatch对象,可能用于存储一些预处理或解析过程中使用的匹配信息 + leftMatch: {}, - checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? - elem || false : - elem === part; - } - } + // attrMap对象,定义了一些属性名的映射,用于处理某些HTML属性在DOM中的不同表示 + attrMap: { + "class": "className", + // class属性在DOM中对应className + "for": "htmlFor" + // for属性在DOM中对应htmlFor + }, - if ( isPartStrNotTag ) { - Sizzle.filter( part, checkSet, true ); + // attrHandle对象,定义了一些特定属性的处理函数 + attrHandle: { + // href属性的处理函数,返回元素的href属性值 + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + // type属性的处理函数,返回元素的type属性值 + type: function( elem ) { + return elem.getAttribute( "type" ); } }, - ">": function( checkSet, part ) { - var elem, - isPartStr = typeof part === "string", - i = 0, - l = checkSet.length; - - if ( isPartStr && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - var parent = elem.parentNode; - checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + // 定义一个对象,包含处理相对选择器的函数 + relative: { + // "+" 选择器的处理函数,用于查找当前元素之前的相邻兄弟元素 + "+": function(checkSet, part){ + // 判断part是否为字符串类型 + var isPartStr = typeof part === "string", + // 判断part是否为标签名(即不包含非单词字符) + isTag = isPartStr && !rNonWord.test( part ), + // 判断part是否为字符串但不是标签名(可能是一个类名、ID等) + isPartStrNotTag = isPartStr && !isTag; + + // 如果part是标签名,则将其转换为小写 + if ( isTag ) { + part = part.toLowerCase(); } - } - } else { - for ( ; i < l; i++ ) { - elem = checkSet[i]; + // 遍历checkSet中的每个元素 + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + // 获取当前遍历到的元素 + if ( (elem = checkSet[i]) ) { + // 向上遍历前一个兄弟元素,直到找到一个元素节点(nodeType为1)或没有兄弟元素为止 + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + // 根据条件更新checkSet[i]的值 + // 如果part不是标签名,或者elem存在且其标签名与part(小写)相同,则设置为elem或false(如果elem不存在) + // 如果part是具体的某个元素(通过===比较),则直接比较elem和part是否相同 + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + // 如果条件满足,设置为elem(如果存在)或false(如果不存在) + elem === part; + // 如果part是具体的元素,直接比较是否相等 + } + } - if ( elem ) { - checkSet[i] = isPartStr ? - elem.parentNode : - elem.parentNode === part; + // 如果part是字符串但不是标签名(可能是一个类名、ID等),则调用Sizzle的filter函数来处理 + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); } - } +// “>” 选择器的处理函数,用于查找当前元素的直接子元素 + }, + ">": function( checkSet, part ) { + var elem, + // 当前遍历到的元素 + isPartStr = typeof part === "string", + // 判断part是否为字符串类型 + i = 0, + // 循环计数器 + l = checkSet.length; + // checkSet的长度 + + // 如果part是字符串且不包含非单词字符(可能是标签名),则进行以下处理 + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + // 将part转换为小写,以确保比较时不区分大小写 + + // 遍历checkSet中的每个元素 + for ( ; i < l; i++ ) { + elem = checkSet[i]; + // 获取当前遍历到的元素 + + if ( elem ) { + var parent = elem.parentNode; + // 获取当前元素的父元素 + // 如果当前元素的父元素的标签名与part(小写)相同,则将该父元素设置为checkSet[i]的值,否则设置为false + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } +// 注意:这里的代码片段没有包含rNonWord正则表达式的定义,也没有包含函数的结束括号和对象字面量的结束花括号。 + } // 如果part不是简单的标签名(即包含非单词字符或未定义rNonWord导致的其他情况) + else { + // 遍历checkSet中的每个元素 + for (; i < l; i++) { + elem = checkSet[i]; + // 获取当前遍历到的元素 + + if (elem) { + // 根据part是否为字符串类型,更新checkSet[i]的值 + // 如果是字符串,则保留elem的父元素;如果不是(可能是具体的DOM元素),则比较elem的父元素是否等于part + checkSet[i] = isPartStr ? + elem.parentNode : + // 如果part是字符串,则设置为elem的父元素 + elem.parentNode === part; + // 如果part不是字符串(可能是DOM元素),则比较elem的父元素是否等于part + } + } - if ( isPartStr ) { - Sizzle.filter( part, checkSet, true ); - } - } - }, + // 如果part是字符串类型,则对checkSet进行进一步的筛选 + // 这可能是为了处理类名、ID或其他属性选择器的情况 + if (isPartStr) { + Sizzle.filter(part, checkSet, true); + // 调用Sizzle的filter函数进行筛选,true可能是表示某种特定行为的标志 + } + } + }, +// 注意:这里的代码片段是“>”选择器的处理函数的一部分,并且没有包含函数的结束括号和对象字面量的结束花括号。 +// 此外,还假设了Sizzle.filter函数的存在和行为,以及isPartStr、checkSet、part、i和l等变量的上下文。 +// 定义一个对象字面量,其中包含不同选择器的处理函数 + // 空字符串选择器的处理函数 "": function(checkSet, part, isXML){ - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); - }, - + var nodeCheck, + // 用于存储节点检查的变量(可能是标签名) + doneName = done++, + // 一个唯一标识符,可能用于跟踪检查过程的状态,`done`可能是一个外部变量,在此函数中自增 + checkFn = dirCheck; + // 初始检查函数,可能用于执行一般的DOM树遍历和检查 + + // 如果part是字符串且不包含非单词字符,则进行以下处理 + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + // 将part转换为小写 + nodeCheck = part; + // 将part设置为nodeCheck,用于后续的节点检查 + checkFn = dirNodeCheck; + // 将检查函数更改为dirNodeCheck,可能用于基于节点名的检查 + } + + // 调用检查函数,传入相关参数以执行检查逻辑 + // "parentNode"表示要检查的DOM关系(在这个上下文中,可能是用于遍历父元素) + // part、doneName、checkSet、nodeCheck和isXML是其他必要的参数 + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + // 波浪线选择器(~)的处理函数,用于选择同一父元素下的兄弟元素 "~": function( checkSet, part, isXML ) { - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); - } + var nodeCheck, // 同上,用于存储节点检查的变量 + doneName = done++, + // 同上,生成一个唯一标识符 + checkFn = dirCheck; + // 同上,初始检查函数 + + // 如果part是字符串且不包含非单词字符,则进行与空字符串选择器相同的处理 + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + // 转换为小写 + nodeCheck = part; + // 设置为nodeCheck + checkFn = dirNodeCheck; + // 更改检查函数为dirNodeCheck + } + + // 调用检查函数,但这次传入"previousSibling"作为要检查的DOM关系 + // 这表明我们是在查找同一父元素下的前一个兄弟元素(但实际上可能是通过逻辑来查找所有匹配的兄弟元素) + // 注意:这里的实现细节可能有所不同,因为波浪线选择器通常需要检查所有兄弟元素,而不仅仅是前一个 + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } }, +// 注意:这里的代码片段没有包含`done`变量的定义、`dirCheck`和`dirNodeCheck`函数的定义,以及对象字面量的结束花括号。 +// 这些都是在外部定义的,可能是选择器引擎的一部分。 find: { ID: function( match, context, isXML ) {