BookShop项目 - 各模块功能
导读:各模块的开发逻辑架构
-
业务逻辑
对项目的各部分进行开发时,首要任务是明确该部分的业务逻辑。明确任务逻辑后,才可能对html文件、dao层、service层、controller层有正确的操作。
-
html文件
明确session作用域中有哪些参数可以调用,部分参数对应的pojo类中的属性情况如何,然后对html文件使用thymeleaf进行渲染
-
dao层
根据业务逻辑,首先对涉及到的数据表进行必要的CRUD工作,使用的参数为对应的pojo类
-
service层
实现主要的业务逻辑,并为pojo类中的自建类属性赋值。
-
controller层
① 获取session中的对应参数或form表单中提交的参数,并创建service层实现业务时所需的参数,
② 后调用service层内的业务方法,实现该部分的业务逻辑,
③ 在session中保存需要的变量
④ 并跳转至相应的组件或html文件上
-
对应的参数表
序号 项目功能复杂时,可详细罗列各参数及对应的属性情况 session参数 form表单提交的参数
一、登录页面
-
业务逻辑
tomcat的URL对应跳转到的时登陆页面,输入用户名和密码后,提交后,在t_user内查询是否有对应的数据:
如果用户名或密码错误,就重新返回登录页面,
如果用户名和密码正确,则跳转至book.do,加载图书列表
-
页面显示效果
-
html
<body> <div class="msg_cont"> <b></b> <span class="errorMsg">请输入用户名和密码</span> </div> <div class="form"> <form th:action="@{/user.do}" method="post"> <input type="hidden" name="operate" value="login"/> <label>用户名称:</label> <!-- name名称要和数据库中的字段名一样 --> <input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1" name="uname" value="lina" id="username" /> <label>用户密码:</label> <input class="itxt" type="password" placeholder="请输入密码" autocomplete="off" tabindex="1" name="pwd" value="ok" id="password" /> <input type="submit" value="登录" id="sub_btn" /> </form> </div> </body>
-
dao
// 根据提交的用户名和密码,在t_user数据表内查询是否有对应的数据,如果有,则user不为空 public class UserDAOImpl extends BaseDAO<User> implements UserDAO { @Override public User getUser(Connection conn,String uname, String password) { String sql = "select * from t_user where uname = ? and pwd = ?"; return super.singleCommonRetrieve(conn,sql,uname,password); } }
-
service
public class UserServiceImpl implements UserService { private UserDAO userDAO; @Override public User getUser(Connection conn, String uname, String password) { return userDAO.getUser(conn,uname,password); } }
-
controller
public class UserController { private Connection conn = JdbcUtils.getConnection(); private UserService userService; private CartItemService cartItemService; public String login(String uname, String pwd, HttpSession session) { User user = userService.getUser(conn,uname,pwd); // 如果在t_user数据表中查询到对应的数据,就将其保存在session作用域内 if (user != null) { session.setAttribute("currUser",user); return "redirect:book.do"; } // 如果返回数据为空,说明用户名或密码不存在,返回登录页面 return "user/login"; } }
二、主页面
-
业务逻辑
主页面的图书列列表部分,需要读取数据库中t_book表内的数据进行实时渲染
-
页面显示效果
-
html
<!-- 主页面右上角的渲染设置 --> <div class="topbar-right" th:if="${session.currUser==null}"> <a href="user/login.html" class="login">登录</a> <a href="user/regist.html" class="register">注册</a> <a href="cart/cart.html" class="cart iconfont icon-gouwuche">购物车 <div class="cart-num" >3</div></a> <a href="manager/book_manager.html" class="admin">后台管理</a> </div> <!-- 登录后风格 --> <div class="topbar-right" th:unless="${session.currUser==null}"> <span>欢迎你<b th:text="*{session.currUser.getUname()}">张总</b></span> <a href="#" class="register">注销</a> <!-- 跳转到cartController,执行其中的默认index方法,展示购物车的最新数据 --> <a th:href="@{/cart.do}" class="cart iconfont icon-gouwuche" >购物车<div class="cart-num" th:text="${session.currUser.cart.totalCount}">3</div></a> <a href="./pages/manager/book_manager.html" class="admin">后台管理</a> </div> <!-- 图书列表的渲染设置 --> <div class="list-content"> <!-- th:object="${book} 后面但凡需要book的引用,直接用*即可"--> <div class="list-item" th:each="book : ${session.bookList}" th:object="${book}"> <img th:src="@{|/static/uploads/*{bookImg}|}" alt=""> <p th:text="|书名:*{bookName}|">书名:活着</p> <p th:text="|作者:*{author}|">作者:余华</p> <p th:text="|价格:¥*{price}|">价格:¥66.6</p> <p th:text="|销量:*{saleCount}|">销量:230</p> <p th:text="|库存:*{bookCount}|">库存:1000</p> <button th:onclick="|addCart(*{id})|">加入购物车</button> </div> </div>
-
dao
public class BookDAOImpl extends BaseDAO<Book> implements BookDAO { @Override public List<Book> getBookList(Connection conn) { String sql = "select * from t_book where bookStatus = 0"; List<Book> bookList = super.CommonRetrieve(conn, sql); return super.CommonRetrieve(conn,sql); } }
-
service
public class BookServiceImpl implements BookService { private BookDAO bookDAO; @Override public List<Book> getBookList(Connection conn) { return bookDAO.getBookList(conn); } }
-
controller
public class BookController { private Connection conn = JdbcUtils.getConnection(); private BookService bookService; // dispatcher的默认operate是index public String index(HttpSession session) { List<Book> bookList = bookService.getBookList(conn); session.setAttribute("bookList",bookList); // index.html在/WEB-INF/pages/文件夹下,故不需要加多余路径 return "index"; } }
三、加入购物车(图书下侧的按钮)
-
业务逻辑
点击图书后的"加入购物车"按钮,跳转到购物车页面
如果该图书已经在购物车中,就数量加1,
如果该图书不在购物车中,则在购物车中新增一列,并生成一条新的购物项cartItem
-
页面显示效果
-
业务分析,新增一个pojo
由于购物车页面是由多条购物记录cart_item组成的,而且有“金额”、“商品总数量”、“总金额”等其它属性,且要分别罗列各条购物记录的详细信息,所以要新建一个pojo类,对应购物车页面。
对应购物车中每条购物记录后的金额,应该在CartItem类中新加一个count属性
public class Cart { // Map中存的是bookId和对应的CartItem private Map<Integer,CartItem> cartItemMap; // 所有图书的数量 private Integer totalBookCount; // 所有图书的总价格 private Double totalMoney; public Cart() {} public Map<Integer, CartItem> getCartItemMap() { return cartItemMap; } public void setCartItemMap(Map<Integer, CartItem> cartItemMap) { this.cartItemMap = cartItemMap; } // 图书总数量,只需要get方法即可 public Integer getTotalBookCount() { totalBookCount = 0 ; if(cartItemMap!=null && cartItemMap.size()>0){ for (CartItem cartItem : cartItemMap.values()){ totalBookCount = totalBookCount + cartItem.getBuyCount() ; } } return totalBookCount; } // 图书的总金额,只需要get方法即可 public Double getTotalMoney() { totalMoney = 0.0; if(cartItemMap!=null && cartItemMap.size()>0){ Set<Map.Entry<Integer, CartItem>> entries = cartItemMap.entrySet(); for(Map.Entry<Integer,CartItem> cartItemEntry : entries){ CartItem cartItem = cartItemEntry.getValue(); BigDecimal bigDecimalPrice = new BigDecimal("" + cartItem.getBook().getPrice()); BigDecimal bigDecimalBuyCount = new BigDecimal("" + cartItem.getBuyCount()); BigDecimal bigDecimalTotalMoney = bigDecimalBuyCount.multiply(bigDecimalPrice); totalMoney = totalMoney + bigDecimalTotalMoney.doubleValue() ; } } return totalMoney; } }
public class CartItem { private Integer id; private Book book; private Integer buyCount; private User userBean; private Double bookMoney; private Double count; public CartItem() {} public CartItem(Integer id) { this.id = id; } // 对于金额的计算,要将Double类型的数据转换为BigDecimal类 public Double getCount() { BigDecimal bigDecimalPrice = new BigDecimal("" + getBook().getPrice()); BigDecimal bigDecimalBuyCount = new BigDecimal("" + buyCount); BigDecimal bigDecimalCount = bigDecimalBuyCount.multiply(bigDecimalPrice); count = bigDecimalCount.doubleValue(); return count; } }
-
html
<div class="w"> <tbody> <tr th:each="cart:${session.currUser.cart.cartItemMap.values()}"> <td> <img th:src="@{|/static/uploads/${cart.book.bookImg}|}" alt="" /> </td> <td th:text="${cart.book.bookName}">活着</td> <td> <span class="count" th:onclick="|editCart(${cart.id},${cart.buyCount-1})|">-</span> <input class="count-num" type="text" th:value="${cart.buyCount}" /> <span class="count" th:onclick="|editCart(${cart.id},${cart.buyCount+1})|">+</span> </td> <td th:text="${cart.book.price}">36.8</td> <td th:text="${cart.count}">36.8</td> <td><a href="">删除</a></td> </tr> </tbody> </div> <div class="footer-right"> <div>共<span th:text="${session.currUser.cart.totalBookCount}">3</span>件商品</div> <div class="total-price">总金额<span th:text="${session.currUser.cart.totalMoney}">99.9</span>元</div> <a class="pay" th:href="@{/order.do?operate=checkout}">去结账</a> </div>
-
dao
public class CartItemImpl extends BaseDAO<CartItem> implements CartItemDAO { @Override public List<CartItem> getCartList(Connection conn, User user) { String sql = "select * from t_cart_item where userBean = ?"; List<CartItem> cartItems = super.CommonRetrieve(conn, sql, user.getId()); return super.CommonRetrieve(conn,sql,user.getId()); } @Override public void addCartByBook(Connection conn, Book book,User user) { String sql = "insert into t_cart_item values(0,?,1,?)"; super.update(conn,sql,book.getId(),user.getId()); } @Override public void updateCartByBook(Connection conn, Book book,CartItem cartItem) { String sql = "update t_cart_item set buyCount = ? where book = ?"; super.update(conn,sql,cartItem.getBuyCount()+1,book.getId()); } }
-
service
public class CartItemServiceImpl implements CartItemService { private CartItemDAO cartItemDAO; private BookService bookService; // 方法1:调用dao,获取当前用户的CartItem列表,并将其中的book属性全部赋值 @Override public List<CartItem> getCartItemList(Connection conn,User user) { List<CartItem> cartList = cartItemDAO.getCartList(conn, user); for (CartItem cartItem : cartList) { // 在bookDAO中,获取该购物项对应的book所有属性 Book book = bookService.getBook(conn, cartItem.getBook().getId()); // 给CartItem中的book属性赋值,保证其不为空 cartItem.setBook(book); } return cartList; } // 方法2:给购物车对应的cart类中的map属性赋值,并给User类中的cart属性赋值 @Override public Cart getCart(Connection conn, User user) { List<CartItem> cartItemList = getCartItemList(conn,user); HashMap<Integer, CartItem> cartMap = new HashMap<>(); for (CartItem cartItem : cartItemList) { cartMap.put(cartItem.getBook().getId(),cartItem); } Cart cart = new Cart(); cart.setCartItemMap(cartMap); user.setCart(cart); return cart; } // 业务逻辑实现:根据购物车中是否有该书目,而调用不同的数据库处理方法 @Override public void addOrUpdateCartItem(Connection conn, Book book,User user) { Cart cart = getCart(conn,user); if (cart != null) { Map<Integer, CartItem> cartItemMap = cart.getCartItemMap(); if (cartItemMap == null) { cartItemMap = new HashMap<Integer,CartItem>(); } // 通过Map集合中是否包含给书的id,如果有,就执行修改操作 if (cartItemMap.containsKey(book.getId())) { CartItem cartItem = cartItemMap.get(book.getId()); cartItemDAO.updateCartByBook(conn,book,cartItem); } else { // 如果没有,就执行新增操作 cartItemDAO.addCartByBook(conn,book,user); } } else { cartItemDAO.addCartByBook(conn,book,user); } } }
-
controller
public class CartController { private Connection conn = JdbcUtils.getConnection(); private CartItemService cartItemService; // 加载当前用户的购物车的最新信息,因为将数据库中的数据修改后需要重新读取一下 public String index(HttpSession session){ User user = (User)session.getAttribute("currUser"); // 获取此时的当前用户的购物车 Cart cart = cartItemService.getCart(conn, user); // 将购物车信息赋值给user user.setCart(cart); // 将最新的user保存到作用域中,在html页面上,通过currUser渲染出来的数据就是最新的 session.setAttribute("currUser",user); return "/cart/cart"; } // 图书下侧的“加入购物车按钮”:向当前用户的购物车中添加或修改该图书 public String addCart(Integer bookId, HttpSession session) { User user = (User)session.getAttribute("currUser"); cartItemService.addOrUpdateCartItem(conn,new Book(bookId),user); // 加载完成后,返回CartController,要重新加载一下cart页面 return "redirect:cart.do"; } }
四、去结算至我的订单
-
业务逻辑
点击购物车页面的去结算按钮,直接生成一个新的订单,且生成多个对应的订单详情,插入数据库中对应的表中,并跳转至我的订单页面,展示最新的订单
一个购物项cartItem对应一条订单详情orderItem
一个购物车cart结算一次对应一个订单order
一个订单order对应多个订单详情orderItem
-
页面效果
-
html
<tbody> <tr th:each="order : ${session.orderList}"> <td th:text="${order.orderNo}">12354456895</td> <td th:text="${order.orderDate}">2015.04.23</td> <td th:text="${order.orderMoney}">90.00</td> <td th:text="${order.totalBookCount}">88</td> <td><a href="" class="send">等待发货</a></td> <td><a href="">查看详情</a></td> </tr> </tbody>
-
dao
// 订单Order public class OrderDAOImpl extends BaseDAO<Order> implements OrderDAO { @Override public Integer addOrder(Connection conn, Order order) { String sql = "insert into t_order values(0,?,?,?,?,0,?)"; return super.update(conn,sql,order.getOrderNo(),order.getOrderDate(),order.getOrderUser().getId(), order.getOrderMoney(),order.getTotalBookCount()); } @Override public List<Order> getOrder(Connection conn, User user) { String sql = "select * from t_order where orderUser = ?"; return super.CommonRetrieve(conn,sql,user.getId()); } } // 订单详情orderItem public class OrderItemDAOImpl extends BaseDAO<OrderItem> implements OrderItemDAO { @Override public void addOrderItem(Connection conn, OrderItem orderItem) { String sql = "insert into t_order_item values(0,?,?,?)"; super.CommonRetrieve(conn,sql,orderItem.getBook().getId(),orderItem.getBuyCount(), orderItem.getOrderBean().getId()); } }
-
service
public class OrderServiceImpl implements OrderService { private OrderDAO orderDAO; private OrderItemDAO orderItemDAO; private CartItemDAO cartItemDAO; // 在购物车中,点击去结算,在order中生成一条新的订单,同时要生成对应的多条订单详情OrderItem @Override public void addOrder(Connection conn, Order order) { Integer orderId = orderDAO.addOrder(conn, order); /* 每个orderItem对应的是每个cartItem,由于每次提交后,购物车会清空, 所以t_cart_item中存着的只是当前购物车内的购物项。 根据购物项中的数据,生成对应的订单详情 */ User user = order.getOrderUser(); for (CartItem cartItem : user.getCart().getCartItemMap().values()) { OrderItem orderItem = new OrderItem(); orderItem.setBook(cartItem.getBook()); orderItem.setBuyCount(cartItem.getBuyCount()); orderItem.setOrderBean(new Order(orderId)); orderItemDAO.addOrderItem(conn,orderItem); } // 结算完成后,要清除购物车中的购物项 for (CartItem cartItem : user.getCart().getCartItemMap().values()) { cartItemDAO.deleteCartItem(conn,cartItem); } } @Override public List<Order> getOrder(Connection conn,User user) { return orderDAO.getOrder(conn,user); } }
-
controller
public class OrderController { private Connection conn = JdbcUtils.getConnection(); private OrderService orderService; // 获取最新的所有订单,并将其存入session作用域 public String index(HttpSession session) { User user = (User)session.getAttribute("currUser"); // 获取所有的订单详情 List<Order> orderList = orderService.getOrder(conn, user); session.setAttribute("orderList",orderList); return "/order/order"; } // 去结算后,将当前的结算的数据传入到order的各个属性中,生成新的order,并存入到数据库中 public String checkout(HttpSession session) { // 创建order对象参数,保证service层的方法可以正常运行 Order order = new Order(); Date date = new Date(); LocalDateTime now = LocalDateTime.now(); int year = now.getYear(); int month = now.getMonthValue(); int day = now.getDayOfYear(); int hour = now.getHour(); int minute = now.getMinute(); int second = now.getSecond(); order.setOrderNo(UUID.randomUUID()+"_"+year+month+day+hour+minute+second); order.setOrderDate(date); User user = (User)session.getAttribute("currUser"); order.setOrderUser(user); order.setOrderMoney(user.getCart().getTotalMoney()); order.setTotalBookCount(user.getCart().getTotalBookCount()); order.setOrderStatus(0); // 将参数传入service层 orderService.addOrder(conn,order); return "redirect:order.do"; } }
五、session过滤器
-
业务逻辑
在多个html页面中,都会使用到thymeleaf进行渲染,如果session中的值不存在,则会引发页面显示错误,故需要添加session过滤器,保证session在没有值的情况下可靠跳转至登录页面
-
新建一个过滤器
// 1.在注解中,存入可以在session不存在都可以通行的路径 @WebFilter(urlPatterns = {"*.do","*.html"}, initParams = { // 里面的value值中的逗号用来分割不同的路径,第二个是点击登录时会出现的路径 @WebInitParam(name = "bai", value = "/bookshop/page.do?operate=page&page=user/login,/bookshop/user.do?null") } ) public class SessionFilter implements Filter { List<String> baiList = null; // 2.在init方法中提取在注解中存入的通行路径 @Override public void init(FilterConfig config) throws ServletException { // 得到注解中的bai的数据 String baiStr = config.getInitParameter("bai"); // 将@WegInitParam中的value内用逗号隔开的值变为数组 String[] baiArr = baiStr.split(","); baiList = Arrays.asList(baiArr); } // 3.解析请求头中的路径信息,并进行逻辑判断是否要通行 @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 3.1 转换成HeepServlet类型的 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 3.2 URI/URL/Query分别对应的值 // request.getRequestURI = /bookshop/page.do System.out.println("request.getRequestURI = " + request.getRequestURI()); //request.getRequestURL = http://localhost:8080/bookshop/page.do System.out.println("request.getRequestURL = " + request.getRequestURL()); //request.getQueryString = operate=page&page=user/login System.out.println("request.getQueryString = " + request.getQueryString()); // 3.3 提取请求头中的URI/Query,并进行组装 String uri = request.getRequestURI(); String query = request.getQueryString(); String str = uri + "?" + query; // 3.4 如果此次请求路径在bai中出现过,则说明该路径可以被执行 if (baiList.contains(str)) { filterChain.doFilter(request,response); // 3.5 如果没有在bai中出现过,获取session中的currUser值 } else { HttpSession session = request.getSession(); Object currUserObj = session.getAttribute("currUser"); // 3.5.1 如果session中的值为空,则跳转到登录页面 if (currUserObj == null) { response.sendRedirect("page.do?operate=page&page=user/login"); } else { // 3.5.2 如果session保存作用域中的currUser有值,则放行 filterChain.doFilter(request,response); } } } @Override public void destroy() { } }
六、注册页面(验证码技术:kaptcha.jar)
-
业务逻辑
注册页面涉及到验证码(参考kaptcha的相关知识点),如果验证码不正确,就要重新回到注册页面。如果验证码正确,就将注册好的信息插入数据库中的t_user表,并跳转至登录页面
-
页面效果
-
regist.html文件
在input标签中要有name,否则提交之后获取到的是null值
<form th:action="@{/user.do}" method="post"> <input type="hidden" name="operate" value="regist"> <div class="form-item"> <div> <label>用户名称:</label> <input type="text" placeholder="请输入用户名" name="uname" value="宝2022" /> </div> <span class="errMess" >用户名应为6~16位数组和字母组成</span> </div> <div class="form-item"> <div> <label>用户密码:</label> <input type="password" placeholder="请输入密码" name="pwd" value="ok"/> </div> <span class="errMess">密码的长度至少为8位</span> </div> <div class="form-item"> <div> <label>确认密码:</label> <input type="password" placeholder="请输入确认密码" /> </div> <span class="errMess">密码两次输入不一致</span> </div> <div class="form-item"> <div> <label>用户邮箱:</label> <input type="text" placeholder="请输入邮箱" name="email" value="bao@163.com"/> </div> <span class="errMess">请输入正确的邮箱格式</span> </div> <div class="form-item"> <div> <label>验证码:</label> <div class="verify"> <input type="text" placeholder="" name="verifyCode"/> <img th:src="@{/kaptcha.jpg}" alt="" /> </div> </div> <span class="errMess">请输入正确的验证码</span> </div> <button class="btn">注册</button> </form>
-
dao
public class UserDAOImpl extends BaseDAO<User> implements UserDAO { @Override public void addUser(Connection conn, User user) { // t_user中的第一个字段为自增量,需要设置成0,但是不能为1,会与原本的数据冲突 String sql = "insert into t_user values(0,?,?,?,0)"; super.update(conn,sql,user.getUname(),user.getPwd(), user.getEmail()); } }
-
service
public class UserServiceImpl implements UserService { private UserDAO userDAO; @Override public void addUser(Connection conn, User user) { userDAO.addUser(conn,user); } }
-
controller
// 对应 user.do public class UserController { private Connection conn = JdbcUtils.getConnection(); private UserService userService; // 方法头的参数是form表单中的name,必须保证完全一样 public String regist(String uname,String pwd,String email,String verifyCode, HttpSession session) { // 1.获取kaptcha已经保存在session作用域中的验证码的值 Object verifyCodeObj = (String)session.getAttribute("KAPTCHA_SESSION_KEY"); // 2.如果该值为空,获知同表单中输入的验证码不一样,则重新返回到注册页面 if (verifyCodeObj == null || !verifyCode.equals(verifyCodeObj)) { return "user/regist"; } else { /* 3.如果表单中输入的验证码与session作用域中的验证码值一样, 则将其增加到t_user表中,并进入登录页面 */ if (verifyCode.equals(verifyCodeObj)) { userService.addUser(conn,new User(uname,pwd,email)); return "user/login"; } } return "user/login"; } }
七、注册页面输入规范检测(正则表达式)
-
业务逻辑
利用js中的正则表达式(Regular Express)对用户名、密码、邮箱的输入格式进行规范,不符合规范的输入会被提示,且不能提交至数据库
-
页面效果
-
html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <script language="JavaScript" th:src="@{/static/script/regist.js}"></script> </head> <body> <h1>注册尚硅谷会员</h1> <form th:action="@{/user.do}" method="post" onsubmit="return preRegist() ;"> <input type="hidden" name="operate" value="regist"/> <div class="form-item"> <div> <label>用户名称:</label> <input id="unameTxt" type="text" placeholder="请输入用户名" name="uname" value="h2022" /> </div> <span id="unameSpan" class="errMess" >用户名应为6~16位数组和字母组成</span> </div> <div class="form-item"> <div> <label>用户密码:</label> <input id="pwdTxt" type="password" placeholder="请输入密码" name="pwd" value="ok"/> </div> <span id="pwdSpan" class="errMess">密码的长度至少为8位</span> </div> <div class="form-item"> <div> <label>确认密码:</label> <input id="pwdTxt1" type="password" placeholder="请输入确认密码" name="pwd1" value="ok"/> </div> <span id="pwdSpan1" class="errMess">密码两次输入不一致</span> </div> <div class="form-item"> <div> <label>用户邮箱:</label> <input id="emailTxt" type="text" placeholder="请输入邮箱" name="email" value="bao@163.com"/> </div> <span id="emailSpan" class="errMess">请输入正确的邮箱格式</span> </div> <div class="form-item"> <div> <label>验证码:</label> <div class="verify"> <input type="text" placeholder="" name="verifyCode"/> <img th:src="@{/kaptcha.jpg}" alt="" /> </div> </div> <span class="errMess">请输入正确的验证码</span> </div> <button class="btn">注册</button> </form> </body> </html>
-
js
function $(id){ return document.getElementById(id); } function preRegist(){ //用户名不能为空,而且是6~16位数字和字母组成 var unameReg = /[0-9a-zA-Z]{6,16}/; var unameTxt = $("unameTxt"); var uname = unameTxt.value ; var unameSpan = $("unameSpan"); if(!unameReg.test(uname)){ unameSpan.style.visibility="visible"; return false ; }else{ unameSpan.style.visibility="hidden"; } // 密码的长度至少为8位 var pwdReg = /\w{8,}/g; var pwdTxt = $("pwdTxt"); var pwdSpan = $("pwdSpan"); var pwd = pwdTxt.value; if (!pwdReg.test(pwd)) { pwdSpan.style.visibility="visible"; return false; } else { pwdSpan.style.visibility="hidden"; } // 密码两次输入不一致 if ($("pwdTxt1").value!=$("pwdTxt").value) { $("pwdSpan1").style.visibility="visible"; return false; } else { $("pwdSpan1").style.visibility="hidden"; } // 请输入正确的邮箱格式 var emailReg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; var emailTxt = $("emailTxt"); var emailSpan = $("emailSpan"); var email = emailTxt.value; if (!emailReg.test(email)) { emailSpan.style.visibility="visible"; return false; } else { emailSpan.style.visibility="hidden"; } return true; }
八、注册页面用户名的监测(Ajax异步请求)
-
业务逻辑
使用Ajax异步请求技术,对页面进行局部刷新,监测用户名是否可以注册,如果在t_user内由该名称,则不可以注册。
-
页面效果
-
使用浏览器调试程序
-
html
<div> <label>用户名称:</label> <input id="unameTxt" type="text" placeholder="请输入用户名" name="uname" value="h2022" onblur="ckUname(this.value)"/> </div>
-
js
// 如果我们想要发送一个异步请求,我们需要一个关键的对象 XMLHttpRequest var xmlHttpRequest; // 该方法用于针对不同的浏览器,创建正确的XMLHttpRequest对象。该方法会被封装在Axios框架内,此处了解即可 function createXMLHttpRequest() { // 针对符合DOM2标准的浏览器 if (window.XMLHttpRequest) { xmlHttpRequest = new XMLHttpRequest(); // 针对不符合DOM2标准的且不同版本的IE浏览器 } else if (window.ActiveXObject) { try { xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP"); } } } function ckUname(uname) { createXMLHttpRequest(); var url = "user.do?operate=ckUname&uname="+uname; // 第一个参数:以什么样的方式发送;第二个参数:给谁发请求;第三个参数:是否为异步方式 xmlHttpRequest.open("GET",url,true); // 设置回调函数,并创建。 readyState有0-4这几种状态 xmlHttpRequest.onreadystatechange = ckUnameCB; // 发送请求 xmlHttpRequest.send(); } function ckUnameCB() { if (xmlHttpRequest.readyState==4 && xmlHttpRequest.status==200) { // xmlHttpRequest.responseText:服务器端响应给我的文本内容 alert(xmlHttpRequest.responseText); if (xmlHttpRequest.responseText == "{'uname':'1'}") { alert("用户可以注册"); } else { alert("用户已存在"); } } }
-
dao
public class UserDAOImpl extends BaseDAO<User> implements UserDAO { @Override public User getUser(Connection conn, String uname) { String sql = "select * from t_user where uname = ?"; return super.singleCommonRetrieve(conn,sql,uname); } }
-
service
public class UserServiceImpl implements UserService { private UserDAO userDAO; @Override public User getUser(Connection conn, String uname) { return userDAO.getUser(conn,uname); } }
-
controller
public class UserController { private Connection conn = JdbcUtils.getConnection(); private UserService userService; public String ckUname(String uname) { User user = userService.getUser(conn,uname); if (user == null) { // 用户名可以注册 return "json:{'uname':'1'}"; } else { return "json:{'uname':'0'}"; } } }
-
servlet
// 在dispatchServlet类内的返回值判断部分的修改 if (retrunStr.startsWith("redirect:")) { String redirectStr = retrunStr.substring("redirect:".length()); resp.sendRedirect(redirectStr); } else if (retrunStr.startsWith("json:")) { String jsonStr = retrunStr.substring("json:".length()); // 将json后的内容发给客户端 PrintWriter out = resp.getWriter(); out.print(jsonStr); out.flush(); }else { // 5.3 将controller内返回的模板传入父类的模板处理方法 super.processTemplate(retrunStr,req,resp); }
九、购物车功能改造(Vue+Axios)
-
业务逻辑
不再通过session来传递数据,改用Vue+Axios在前后端之间传输JSON数据
-
注意
thymeleaf会自动解析pojo类中的get方法
Axios进行的时前后端分离,value表达式获取的只是服务端响应的JSON数据
-
前端
html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="stylesheet" th:href="@{/static/css/minireset.css}" /> <link rel="stylesheet" th:href="@{/static/css/common.css}" /> <link rel="stylesheet" th:href="@{/static/css/cart.css}" /> <script language="JavaScript" th:src="@{/static/script/vue.js}"></script> <script language="JavaScript" th:src="@{/static/script/axios.min.js}"></script> <script language="JavaScript" th:src="@{/static/script/edit.js}"></script> <base th:href="@{/}"/> </head> <body> <div class="list" id="cart_div"> <div class="w"> <table> <thead> <tr> <th>图片</th> <th>商品名称</th> <th>数量</th> <th>单价</th> <th>金额</th> <th>操作</th> </tr> </thead> <tbody> <!-- cart:在vue中通过value在响应中获得了 --> <tr v-for="cartItem in cart.cartItemMap" > <td> <img v-bind:src="'static/uploads/'+cartItem.book.bookImg" alt="" /> </td> <td >{{cartItem.book.bookName}}</td> <td> <span class="count" v-on:click="editCart(cartItem.id,cartItem.buyCount-1)">-</span> <input class="count-num" type="text" v-bind:value="cartItem.buyCount" /> <span class="count" v-on:click="editCart(cartItem.id,cartItem.buyCount+1)">+</span> </td> <td>{{cartItem.book.price}}</td> <td>{{cartItem.count}}</td> <td><a href="">删除</a></td> </tr> </tbody> </table> <div class="footer"> <div class="footer-left"> <a href="#" class="clear-cart">清空购物车</a> <a href="#">继续购物</a> </div> <div class="footer-right"> <div>共<span>{{cart.totalBookCount}}</span>件商品</div> <div class="total-price">总金额<span>{{cart.totalMoney}}</span>元</div> <a class="pay" th:href="@{/order.do?operate=checkout}">去结账</a> </div> </div> </div> </div> </body> </html>
js
// 当页面加载的时候 window.onload=function (){ var vue = new Vue({ el:"#cart_div", data:{ cart:{} }, methods:{ // 在methods中创建了一个getCart方法 getCart:function (){ // 方法内发了一个axios请求 axios({ method:"POST", url:"cart.do", params:{ operate:'cartConfig' } }) .then(function (value){ // value是接收服务端响应回的内容,data指其中的数据 var cart = value.data; vue.cart = cart; }) .catch(function (reason){ }); }, editCart:function (cartId1,buyCount1){ axios({ method:"POST", url:"cart.do", // 普通传参 params:{ operate:'editCart', cartId:cartId1, buyCount:buyCount1 } }) .then(function (value){ // 修改后重新执行一次getCart,加载最新的购物车情况 vue.getCart(); }) .catch(function (reason){ }); } }, // 在数据渲染的时候 mounted:function () { this.getCart(); } }); }
-
service
public class CartItemServiceImpl implements CartItemService { private CartItemDAO cartItemDAO; private BookService bookService; // 获取当前用户的CartItem列表,并将其中的book属性全部赋值 @Override public List<CartItem> getCartItemList(Connection conn,User user) { List<CartItem> cartList = cartItemDAO.getCartList(conn, user); for (CartItem cartItem : cartList) { Book book = bookService.getBook(conn, cartItem.getBook().getId()); cartItem.setBook(book); // 此处调用getCount方法执行内部的代码,因为axios框架不会在解析返回值时自动加载get方法 cartItem.getCount(); } return cartList; } // 给购物车对应的cart类中的map属性赋值,并将所有的cart赋值到User类中的cart属性中 @Override public Cart getCart(Connection conn, User user) { List<CartItem> cartItemList = getCartItemList(conn,user); HashMap<Integer, CartItem> cartMap = new HashMap<>(); for (CartItem cartItem : cartItemList) { cartMap.put(cartItem.getBook().getId(),cartItem); } Cart cart = new Cart(); cart.setCartItemMap(cartMap); // cart内的get方法也要全部执行一次,因为Axios不会自动调用get方法的 cart.getTotalCount(); cart.getTotalBookCount(); cart.getTotalMoney(); user.setCart(cart); return cart; } }
-
controller
public class CartController { private Connection conn = JdbcUtils.getConnection(); private CartItemService cartItemService; public String editCart(Integer cartId,Integer buyCount) { cartItemService.updateCartById(conn,buyCount,cartId); return "json:"; } public String cartConfig(HttpSession session) { User user = (User)session.getAttribute("currUser"); // 获取此时的当前用户的购物车 Cart cart = cartItemService.getCart(conn, user); Gson gson = new Gson(); String cartJsonStr = gson.toJson(cart); return "json:"+cartJsonStr; } }