IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> JavaWEB二十一:BookShop项目 - 各模块功能 -> 正文阅读

[开发测试]JavaWEB二十一:BookShop项目 - 各模块功能

BookShop项目 - 各模块功能

导读:各模块的开发逻辑架构

  1. 业务逻辑

    对项目的各部分进行开发时,首要任务是明确该部分的业务逻辑。明确任务逻辑后,才可能对html文件、dao层、service层、controller层有正确的操作。

  2. html文件

    明确session作用域中有哪些参数可以调用,部分参数对应的pojo类中的属性情况如何,然后对html文件使用thymeleaf进行渲染

  3. dao层

    根据业务逻辑,首先对涉及到的数据表进行必要的CRUD工作,使用的参数为对应的pojo类

  4. service层

    实现主要的业务逻辑,并为pojo类中的自建类属性赋值。

  5. controller层

    获取session中的对应参数或form表单中提交的参数,并创建service层实现业务时所需的参数,

    后调用service层内的业务方法,实现该部分的业务逻辑,

    在session中保存需要的变量

    并跳转至相应的组件或html文件上

  6. 对应的参数表
    序号项目功能复杂时,可详细罗列各参数及对应的属性情况
    session参数
    form表单提交的参数

一、登录页面

  1. 业务逻辑

    tomcat的URL对应跳转到的时登陆页面,输入用户名和密码后,提交后,在t_user内查询是否有对应的数据:

    如果用户名或密码错误,就重新返回登录页面,

    如果用户名和密码正确,则跳转至book.do,加载图书列表

    在这里插入图片描述

  2. 页面显示效果

    在这里插入图片描述

  3. 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>
    
  4. 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);
        }
    }
    
  5. 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);
        }
    }
    
  6. 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";
        }
    }
    

二、主页面

  1. 业务逻辑

    主页面的图书列列表部分,需要读取数据库中t_book表内的数据进行实时渲染

  2. 页面显示效果

    在这里插入图片描述

  3. 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>
    
  4. 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);
        }
    }
    
  5. service
    public class BookServiceImpl implements BookService {
        private BookDAO bookDAO;
        @Override
        public List<Book> getBookList(Connection conn) {
            return bookDAO.getBookList(conn);
        }
    }
    
  6. 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. 业务逻辑

    点击图书后的"加入购物车"按钮,跳转到购物车页面

    如果该图书已经在购物车中,就数量加1,

    如果该图书不在购物车中,则在购物车中新增一列,并生成一条新的购物项cartItem

  2. 页面显示效果

    在这里插入图片描述

    在这里插入图片描述

  3. 业务分析,新增一个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;
        }
    }
    
  4. 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>
    
  5. 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());
        }
    }
    
  6. 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);
            }
        }
    }
    
  7. 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";
        }
    }
    

四、去结算至我的订单

  1. 业务逻辑

    点击购物车页面的去结算按钮,直接生成一个新的订单,且生成多个对应的订单详情,插入数据库中对应的表中,并跳转至我的订单页面,展示最新的订单

    一个购物项cartItem对应一条订单详情orderItem

    一个购物车cart结算一次对应一个订单order

    一个订单order对应多个订单详情orderItem

  2. 页面效果

    在这里插入图片描述

  3. 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>
    
  4. 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());
        }
    }
    
  5. 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);
        }
    }
    
  6. 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过滤器

  1. 业务逻辑

    在多个html页面中,都会使用到thymeleaf进行渲染,如果session中的值不存在,则会引发页面显示错误,故需要添加session过滤器,保证session在没有值的情况下可靠跳转至登录页面

  2. 新建一个过滤器
    // 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)

  1. 业务逻辑

    注册页面涉及到验证码(参考kaptcha的相关知识点),如果验证码不正确,就要重新回到注册页面。如果验证码正确,就将注册好的信息插入数据库中的t_user表,并跳转至登录页面

  2. 页面效果

    在这里插入图片描述

  3. 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>
    
  4. 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());
        }
    }
    
  5. service
    public class UserServiceImpl implements UserService {
        private UserDAO userDAO;
        
        @Override
        public void addUser(Connection conn, User user) {
            userDAO.addUser(conn,user);
        }
    }
    
  6. 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";
        }
    }
    

七、注册页面输入规范检测(正则表达式)

  1. 业务逻辑

    利用js中的正则表达式(Regular Express)对用户名、密码、邮箱的输入格式进行规范,不符合规范的输入会被提示,且不能提交至数据库

  2. 页面效果

    在这里插入图片描述

  3. 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>
    
  4. 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异步请求)

  1. 业务逻辑

    使用Ajax异步请求技术,对页面进行局部刷新,监测用户名是否可以注册,如果在t_user内由该名称,则不可以注册。

  2. 页面效果

    在这里插入图片描述

  3. 使用浏览器调试程序

    在这里插入图片描述

  4. html
     <div>
         <label>用户名称:</label>
         <input id="unameTxt" type="text" placeholder="请输入用户名" name="uname"           
                value="h2022" onblur="ckUname(this.value)"/>
    </div>
    
  5. 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("用户已存在");
            }
        }
    }
    
  6. 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);
        }
    }
    
  7. service
    public class UserServiceImpl implements UserService {
        private UserDAO userDAO;
        @Override
        public User getUser(Connection conn, String uname) {
            return userDAO.getUser(conn,uname);
        }
    }
    
  8. 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'}";
            }
        }
    }
    
  9. 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)

  1. 业务逻辑

    不再通过session来传递数据,改用Vue+Axios在前后端之间传输JSON数据

  2. 注意

    thymeleaf会自动解析pojo类中的get方法

    Axios进行的时前后端分离,value表达式获取的只是服务端响应的JSON数据

  3. 前端
    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();
            }
        });
    }
    
  4. 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;
        }
    }
    
    
  5. 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;
        }
    }
    
  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 19:07:17  更:2022-04-22 19:07:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 8:18:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码