zoukankan      html  css  js  c++  java
  • Web 小案例 -- 网上书城(三)

    内容有点乱,有兴趣的同伙可依照后面的案例结构结合文章进行阅读 

      和网上购买东西一样,你可以在不登录的状态下去浏览商品,但是当你想把自己中意的东西加入购物车或是收藏起来就需要你拥有自己的账号然后登录后才可以进一步操作,上次我们的翻页操作也就是可以供大家进行商品的浏览,所以这次我们先完成登录操作(具体登录所需我们直接插入数据表,不再对注册做处理)。

    关于登录操作的具体操作步骤

    • 向数据表 userinfo 中插入数据(user_id,user_name,Account_id)
    • 在翻页操作页面我们已经将登录的超链接加入,点击 Login 超链接跳转到登录页面(login.jsp)
    • 在登录页面输入我们提前插入数据库的信息完成登录并会提示你登录成功
    • 登录成功后点击“开始购物”到商品展示页面将会显示欢迎信息 “欢迎您,xxx

    关于登录操作的具体思路

    • login.jsp 页面点击登录发送请求到 Servlet 处理
    • Servlet 方法获取输入框内容,并根据用户所输入的内容新建 UserInfo 对象
    • 根据 UserInfo 对象查询数据库进行匹配(所有需要在 Servlet 中进行逻辑处理的代码我们都将之封装到 BookService 类中,本方法为 Long login(Userinfo userinfo)
    • 若匹配所得到的个数为 1,则表明用户输入的信息正确,跳转到登陆成功页面,否则提示错误信息

    关于登录操作的代码展示

    • login.jsp
     1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
     2 <html>
     3 <head>
     4     <title>Login</title>
     5 </head>
     6 <body>
     7 <form action="${pageContext.request.contextPath}/login.do" method="post">
     8     UserName: <input type="text" name="userName"/><br><br>
     9     AccountId: <input type="text" name="accountId"/><br><br>
    10 
    11     <button type="submit">Submit</button>
    12 </form>
    13 </body>
    14 </html>
    View Code

      如代码所示我们根据用户名和其所对应的 accountId 进行判别

    • Servlet 中对应的 login() 方法
     1 /*
     2     * 用户登录操作(只需用户名和账户号,没有密码)
     3     * */
     4     protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     5 //        获取 session 对象
     6         HttpSession session = getSession(request);
     7 //        获取所登录账户的用户名
     8         String userName = request.getParameter("userName");
     9 //        获得该用户所对应的 accountId
    10         String accountIdStr = request.getParameter("accountId");
    11         int accountId = -1;
    12         try {
    13             accountId = Integer.parseInt(accountIdStr);
    14         } catch (NumberFormatException e) {
    15         }
    16 //        利用登录名和 accountId 新建 UserInfo 对象
    17         Userinfo userinfo = new Userinfo(userName, accountId);
    18 //        从数据库中判断是否存在和登录账户所匹配的账户
    19         long count = bookService.login(userinfo);
    20 //        若匹配则 count 为 1
    21         if (count == 1) {
    22 //            若匹配则将用户信息存入 session,在后面购物车操作中会用到
    23             session.setAttribute("userInfo", userinfo);
    24 //            并重定向到登录成功页面
    25             response.sendRedirect(request.getContextPath() + "/success/successLogin.jsp");
    26         } else {
    27 //            否则转发到登录失败页面
    28             response.sendRedirect(request.getContextPath() + "/error/errorLogin.jsp");
    29         }
    30     }
    View Code

      和上面所表述的一样我们根据匹配数进行判断信息是否正确(我只是为了写案例练习,以简单为主)

    • BookService 的 login(UserInfo userInfo) 方法
    1     /*
    2     * 登录操作
    3     * */
    4     public long login(Userinfo userinfo) {
    5         Integer userId = userinfoDao.getUserId(userinfo);
    6         userinfo.setUserId(userId);
    7         return userinfoDao.login(userinfo);
    8     }
    View Code

      在此 login 方法中,我们调用了数据表 userInfo 对应的实现类的 login(UserInfo userInfo) 方法,并为传入的 userinfo 对象的 userId 进行了赋值操作,在后面所说的 login 方法中我们和数据库进行互动返回匹配数

    • UserInfo 接口实现类 UserInfoImpl 的 login(UserInfo userInfo) 方法
    1     /*
    2     * 登录操作,根据请求参数获取登录账户在数据库中是否存在,根据匹配数判断
    3     * */
    4     @Override
    5     public long login(Userinfo userinfo) {
    6         String sql = "SELECT COUNT(user_id) FROM userinfo WHERE user_name = ? AND account_id = ?";
    7         long count = (Long) getCount(sql, userinfo.getUserName(), userinfo.getAccountId());
    8         return count;
    9     }
    View Code

      登录操作也到此为止,接下来为有关购物车的操作(以下操作均以已登录为基础)。

    关于购物车操作的具体步骤

    • 商品展示页面上对自己中意的商品点击加入购物车,商品展示页面顶部显示提示信息 
    • 点击查看购物车根据所登录用户进入对应的购物车中
    • 如上图所示,我们进入对应的购物车后将显示我们已经添加的商品的详细信息
    • 点击删除我们可以直接将该商品从购物车中清除
    • 在 BookCount 栏中我们可以对购物车中该商品的数量进行更改,若输入 0 则等同于从购物车中清除该商品
    • 点击继续购物我们将回到商品展示页面
    • 点击清空购物车则删除所有购物车中的商品,并提示删除成功(在清空以及删除之前我们可以为之加上一个确认框以防止误点)
    • 点击上一步所示的返回继续购物将回到商品展示页面
    • 有关结账操作我们下次继续

    关于购物车操作的具体思路

    • 点击加入购物车发送请求到 Servlet 方法 addToCart,并附带参数查询条件和页码以及对应商品的 id
    • 在 Servlet 的 addToCart 方法中获取商品 id 以及 session (在登录的时候我们将用户信息保存到了 session 中,就为了现在用)
    • 从 session 中获取到 userInfo 对象,根据商品 id 获取到 Book 对象
    • 调用 Service 类的  void addToCart(Books books, Integer userId) 方法将对应的商品加入购物车,在此方法中调用购物车数据表对应的实现类(ShoppingCartImpl)的方法将对应的商品信息以及用户信息插入数据表中
    • 将 Book 对象存入 request 域对象中以便在商品展示页面显示提示信息 “你已将XXX加入购物车
    • 点击查看购物车发送请求到 Servlet 方法 showCart,并附带参数查询条件和页码
    • 在执行 showCart 方法之前我们把购物车显示页面进行封装为一个类 ShoppingCartPage,包括属性 List<ShoppingCartItem> shoppingCartItemList, Integer totalBookCount, Integer totalBookMoney,便于操作
    • 在 showCart 方法中我们首先获取到购物车页面 ShoppingCartPage 对象(在 Service 方法中填充 ShoppingCartPage 对象)以及 totalBookCount 属性
    • 根据获取到的 totalBookCount 属性判断购物车中是否有商品,若有则显示购物车页面,否则提示错误信息
    • 我们在 ShoppingCart 接口实现类中去填充 ShoppingCartPage 页面,避免在 ShoppingCartPage 中填充可以降低耦合度
    • 将获取到的 shoppingCartPage 对象添加到 session 域中,以便在购物车页面进行显示
    • 在 shoppingCart.jsp 页面利用 session 域对象中的 shoppingCartPage 对象显示购物车中所有商品信息
    • 点击删除发送请求到 Servlet 的 deleteItem 方法进行处理
    • 调用 Service 方法的 delete,从数据库中删除该条数据,删除后返回购物车页面
    • 点击清空购物车发送请求到 Servlet 的 truncated 方法进行处理,也就是执行 SQL 语句清空 shoppingCart 数据表
    • 点击后提示清空成功,并可选择继续购物
    • 点击继续购物返回商品展示页面
    • 若你在查看购物车的时候你正在浏览特定条件下的第几页那么从购物车中返回商品展示页面你依旧在你原来的页面,这个依赖于每次点击超链接都将页码以及查询条件加在 URL 后

    关于购物车操作的代码展示

    • Servlet 的方法 addTocart (加入购物车操作
     1 /*
     2     * 将商品添加至购物车
     3     * */
     4     protected void addToCart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     5 //        获取到要添加到购物车中的商品的 id
     6         String idStr = request.getParameter("id");
     7 //        获取 session 对象
     8         HttpSession session = getSession(request);
     9 //        获取到登录时存入的 UserInfo 对象
    10         Userinfo userinfo = (Userinfo) session.getAttribute("userInfo");
    11 //        如果 UserInfo 对象为空,则表明用户没有登录进行加入购物车操作
    12         if (userinfo == null) {
    13 //            若为空那么就重定向到错误页面提示用户需要登录
    14             response.sendRedirect(request.getContextPath() + "/error/emptyAccount.jsp");
    15 //            并结束本方法的执行
    16             return;
    17         }
    18 
    19         int id = -1;
    20         try {
    21             id = Integer.parseInt(idStr);
    22         } catch (NumberFormatException e) {}
    23 //        若用户登录了,则利用 id 获取到该商品所对应的对象
    24         Books books = bookService.getBook(id);
    25 //        执行加入购物车操作,即加入购物车所对应的数据表
    26         bookService.addToCart(books, userinfo.getUserId());
    27 //        并将加入购物车的商品对象存入 request 中
    28         request.setAttribute("books", books);
    29 //        转发回页面,在页面利用 request 域中的对象显示信息“你已经将 XXX 加入购物车”
    30         request.getRequestDispatcher("/query.do").forward(request, response);
    31     }
    View Code
    • Service 的方法 getBook(int id)
    1     /*
    2     * 获得 book 对象
    3     * */
    4     public Books getBook(int id) {
    5         return booksDao.getBook(id);
    6     }
    View Code
    • BookDao 实现类的 getBook(int id) 方法
     1     /*
     2     * 根据 id 获取某书籍对应的对象
     3     * */
     4     @Override
     5     public Books getBook(int id) {
     6         String sql = "SELECT id, author, title, price, publish_date publishDate, sales_count salesCount, store_number storeNumber, remark " +
     7                 "FROM books WHERE id=?";
     8         Books books = getValue(sql, id);
     9         return books;
    10     }
    View Code
    • Service 的 addTocart 方法
     1   /*
     2     * 加入购物车操作
     3     * */
     4     public void addToCart(Books books, Integer userId) {
     5 //        获取某一商品的数量
     6         Integer count = shoppingCart.getBookCount(books.getTitle());
     7         if (count != null) {
     8 //            若购物车中存在则更新数量即加 1
     9             shoppingCart.updateBookCount(books.getTitle());
    10         } else {
    11 //            若不存在则加入数据表
    12             shoppingCart.insertData(books, userId);
    13         }
    14     }
    View Code
    • ShoppingCart 实现类中相关方法
    1     /*
    2     * 根据 cart_name 获取该商品的数量
    3     * */
    4     @Override
    5     public Integer getBookCount(String cart_name) {
    6         String sql = "SELECT cart_count cartCount FROM shoppingcart WHERE cart_name=?";
    7         Integer count = (Integer) getCount(sql, cart_name);
    8         return count;
    9     }
    View Code
    1     /*
    2     * 更新购物车中某商品的数量加 1,每次点击加入购物车使其对应的数量加 1
    3     * */
    4     @Override
    5     public void updateBookCount(String cart_name) {
    6         String sql = "UPDATE shoppingcart SET cart_count = cart_count + 1 WHERE cart_name=?";
    7         update(sql, cart_name);
    8     }
    View Code
    1     /*
    2     * 将某商品加入购物车,点击加入购物车,若购物车中没有此商品则将其加入数据表并设其数量为 1
    3     * */
    4     @Override
    5     public void insertData(Books books, Integer userId) {
    6         String sql = "INSERT INTO shoppingcart (user_id, cart_count, cart_name, cart_price) VALUES(?, 1, ?, ?)";
    7         insert(sql, userId, books.getTitle(), books.getPrice());
    8     }
    View Code
    • 商品展示页面显示欢迎信息和提示将某本书加入购物车的信息 bookList.jsp
     1     <c:if test="${!empty requestScope.books}" var="books">
     2         <br>你已经把 ${requestScope.books.title} 加入购物车<br><br>
     3     </c:if>
     4     <c:choose>
     5         <c:when test="${empty sessionScope.userInfo}">
     6             <a href="${pageContext.request.contextPath}/loginPage/login.jsp?pageNo=${requestScope.page.pageNo}">LoGin</a></c:when>
     7         <c:otherwise>
     8             欢迎您,${sessionScope.userInfo.userName}&nbsp;&nbsp;&nbsp;
     9             <a href="${pageContext.request.contextPath}/showCart.do?pageNo=${requestScope.page.pageNo}">查看购物车</a>&nbsp;&nbsp;&nbsp;
    10         </c:otherwise>
    11     </c:choose>
    View Code
    • Servlet 的方法 showCart(查看购物车操作
     1  /*
     2     * 点击查看购物车所执行的方法
     3     * */
     4     protected void showCart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     5 //        从数据中获取属性以新建购物车页面对象
     6         ShoppingCartPage shoppingCartPage = bookService.getShoppingCartPage();
     7 //        获取购物车中总的商品数量
     8         int totalNum = shoppingCartPage.getTotalBookCount();
     9 //        判断总数量是否合法
    10         if (totalNum > 0) {
    11 //            将购物车页面对象存入 session中,以便在页面进行显示
    12             getSession(request).setAttribute("shoppingCartPage", shoppingCartPage);
    13 //            转发到购物车页面,在页面进行显示
    14             request.getRequestDispatcher("/showView/shoppingCart.jsp").forward(request, response);
    15 //            结束执行该方法
    16             return;
    17         }
    18 //        若购物车中没有商品则重定向到错误页面提示购物车中没有商品
    19         response.sendRedirect(request.getContextPath() + "/error/empty.jsp");
    20     }
    View Code
    • Service 的方法 getShoppingCartPage
    1     /*
    2     * 获取购物车显示页面
    3     * */
    4     public ShoppingCartPage getShoppingCartPage() {
    5         return shoppingCart.getShoppingCartPage();
    6     }
    View Code
    • ShoppingCart 实现类相关方法
     1     /*
     2     * 获取购物车页面显示的信息所构成的页面对象
     3     * */
     4     @Override
     5     public ShoppingCartPage getShoppingCartPage() {
     6         ShoppingCartPage shoppingCartPage = new ShoppingCartPage();
     7 //        设置显示页面的 list
     8         shoppingCartPage.setShoppingCartItemList(getCartItemList());
     9 //        设置购物车中所有商品的总数量
    10         shoppingCartPage.setTotalBookCount(getTotalCount());
    11 //        设置购物车中所有商品的总价钱
    12         shoppingCartPage.setTotalBookMoney(getTotalMoney());
    13         return shoppingCartPage;
    14     }
    View Code
    1     /*
    2     * 获取购物车表中所有的商品并构建为 list
    3     * */
    4     @Override
    5     public List<ShoppingCartItem> getCartItemList() {
    6         String sql = "SELECT cart_id cartId, user_id userId, cart_name cartName, cart_price cartPrice, cart_count cartCount FROM shoppingcart";
    7         List<ShoppingCartItem> shoppingCartItems = getList(sql, null);
    8         return shoppingCartItems;
    9     }
    View Code
     1    /*
     2     * 获取购物车中所有商品的总数量
     3     * */
     4     public Integer getTotalCount() {
     5         Integer totalCount = 0;
     6         List<ShoppingCartItem> shoppingCartItemList = getCartItemList();
     7         for (ShoppingCartItem shoppingCartItem : shoppingCartItemList) {
     8             totalCount = shoppingCartItem.getCartCount() + totalCount;
     9         }
    10         return totalCount;
    11     }
    View Code
     1     /*
     2     * 获取购物车中所有商品的总价钱
     3     * */
     4     public Integer getTotalMoney() {
     5         Integer totalMoney = 0;
     6         List<ShoppingCartItem> shoppingCartItemList = getCartItemList();
     7         for (ShoppingCartItem shoppingCartItem : shoppingCartItemList) {
     8             totalMoney = shoppingCartItem.getCartCount() * shoppingCartItem.getCartPrice() + totalMoney;
     9         }
    10         return totalMoney;
    11     }
    View Code
    • ShoppingCartPage
     1 package com.book.store.web;
     2 
     3 import com.book.store.domain.ShoppingCartItem;
     4 
     5 import java.util.List;
     6 
     7 /**
     8  * Created by shkstart on 2017/12/13.
     9  */
    10 public class ShoppingCartPage {
    11 
    12     private List<ShoppingCartItem> shoppingCartItemList;
    13     private Integer totalBookCount;
    14     private Integer totalBookMoney;
    15 
    16     public List<ShoppingCartItem> getShoppingCartItemList() {
    17         return shoppingCartItemList;
    18     }
    19 
    20     public void setShoppingCartItemList(List<ShoppingCartItem> shoppingCartItemList) {
    21         this.shoppingCartItemList = shoppingCartItemList;
    22     }
    23 
    24     public Integer getTotalBookCount() {
    25         return totalBookCount;
    26     }
    27 
    28     public void setTotalBookCount(Integer totalBookCount) {
    29         this.totalBookCount = totalBookCount;
    30     }
    31 
    32     public Integer getTotalBookMoney() {
    33         return totalBookMoney;
    34     }
    35 
    36     public void setTotalBookMoney(Integer totalBookMoney) {
    37         this.totalBookMoney = totalBookMoney;
    38     }
    39 
    40     @Override
    41     public String toString() {
    42         return "ShoppingCartPage{" +
    43                 "shoppingCartItemList=" + shoppingCartItemList +
    44                 ", totalBookCount=" + totalBookCount +
    45                 ", totalBookMoney=" + totalBookMoney +
    46                 '}';
    47     }
    48 }
    View Code
    • Servlet 的 deleteItem 方法 (删除购物车某商品
     1    /*
     2     * 购物车操作之删除某一商品
     3     * */
     4     protected void deleteItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     5 //        获取当前操作的商品
     6         String cartIdStr = request.getParameter("cartId");
     7 
     8         Integer cartId = -1;
     9         try {
    10             cartId = Integer.valueOf(cartIdStr);
    11         } catch (NumberFormatException e) {
    12         }
    13 //        将删除方法(操作数据库)封装在数据库中
    14         bookService.deleteItem(cartId);
    15 //        删除成功后转发到购物车页面
    16         request.getRequestDispatcher("/showCart.do").forward(request, response);
    17     }
    View Code
    • Service 的 deleteItem 方法
    1     /*
    2     * 删除购物车中的商品
    3     * */
    4     public void deleteItem(Integer cartId) {
    5         shoppingCart.delete(cartId);
    6     }
    View Code
    • ShoppingCart 实现类的 delete 方法,从数据库中删除
    1     /*
    2     * 执行删除操作,删除某一个商品
    3     * */
    4     @Override
    5     public void delete(Integer cartId) {
    6         String sql = "DELETE FROM shoppingcart WHERE cart_id = ?";
    7         update(sql, cartId);
    8     }
    View Code
    • Servlet 的 truncated 方法
    1     /*
    2     * 清空购物车
    3     * */
    4     protected void truncated(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    5 //        在 bookService 中执行清空
    6         bookService.truncated();
    7 //        转发到清空成功的页面
    8         request.getRequestDispatcher("/success/success.jsp").forward(request, response);
    9     }
    View Code
    • bookService 的 truncated 方法
    1     /*
    2     * 清空购物车
    3     * */
    4     public void truncated() {
    5         shoppingCart.deleteAll();
    6     }
    View Code
    • shoppingCart 实现类的 deleteAll 方法
    1     /*
    2     * 清空购物车
    3     * */
    4     @Override
    5     public void deleteAll() {
    6         String sql = "TRUNCATE shoppingcart";
    7         update(sql, null);
    8     }
    View Code
    • shoppingCart.jsp
     1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
     2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
     3 <html>
     4 <head>
     5     <title>ShoppingCart</title>
     6     <script type="text/javascript" src="${pageContext.request.contextPath}/jquery-1.7.2.js"></script>
     7     <%@ include file="/commons/queryCondition.jsp" %>
     8     <script type="text/javascript">
     9         $(function () {
    10             $("input").change(function () {
    11                 var count = $(this).val();
    12                 var cartId = $(this).parent().parent().find("input").val();
    13 
    14                 var url = "${pageContext.request.contextPath}/updateCount.do";
    15 
    16                 if (count == 0) {
    17                     var aTag = $(this).parent().parent().find("a");
    18 //                    触发
    19                     aTag.trigger("click");
    20                     return;
    21                 }
    22 
    23                 var args = {"time": new Date, "count": count, "cartId": cartId};
    24 
    25                 $.getJSON(url, args, function (data) {
    26                     window.location.reload();
    27                     $("#totalMoney").text(data);
    28                 })
    29             })
    30         })
    31     </script>
    32 </head>
    33 <body>
    34 <div>
    35     <c:forEach items="${requestScope.messageList}" var="message">
    36         ${message}&nbsp;&nbsp;&nbsp;&nbsp;
    37     </c:forEach>
    38     <c:if test="${!empty requestScope.messageList}">库存不足!</c:if>
    39     ${requestScope.moneyMessage }
    40     <table cellpadding="7">
    41         <tr>
    42             <th>BookName</th>
    43             <th>BookCount</th>
    44             <th>BookPrice</th>
    45             <th></th>
    46         </tr>
    47         <c:forEach items="${sessionScope.shoppingCartPage.shoppingCartItemList}" var="shoppingCart">
    48             <tr>
    49                 <input type="hidden" value="${shoppingCart.cartId}">
    50                 <td>${shoppingCart.cartName}</td>
    51                 <td><input type="text" size="1" value="${shoppingCart.cartCount }"></td>
    52                 <td>${shoppingCart.cartPrice}</td>
    53                 <td><a href="${pageContext.request.contextPath}/deleteItem.do?cartId=${shoppingCart.cartId}">删除</a></td>
    54             </tr>
    55         </c:forEach>
    56         <tr>
    57             <td>总价钱</td>
    58             <td id="totalMoney">${sessionScope.shoppingCartPage.totalBookMoney}</td>
    59         </tr>
    60         <tr>
    61             <td><a href="${pageContext.request.contextPath}/query.do?pageNo=${param.pageNo}">继续购物</a></td>
    62             <td><a href="${pageContext.request.contextPath}/truncated.do?pageNo=${param.pageNo}">清空购物车</a></td>
    63             <td>
    64                 <a href="${pageContext.request.contextPath}/check.do?pageNo=${param.pageNo}">结账</a></td>
    65         </tr>
    66     </table>
    67 </div>
    68 </body>
    69 </html>
    View Code

      其中的 JSON 处理为修改购物车中某商品的数量,当修改为 0 的时候等同于删除操作(利用 trrigger 函数)

    • 保留查询条件的操作是每次点击超链接之后将对应的查询条件加到 URL 之后便可,我们将点击超链接并添加 URL 部分提取出来为每个需要的加入(
      <%@ include file="/commons/queryCondition.jsp" %>
      ),可使代码更加简洁。
    • 共同部分(queryCondition.jsp)
     1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
     2 
     3 <script type="text/javascript">
     4     $(function () {
     5         $("a").click(function () {
     6             var hrefVal = $(":hidden").serialize();
     7             var href = this.href + "&" + hrefVal;
     8             window.location.href = href;
     9             return false;
    10         })
    11     })
    12 </script>
    13 
    14 <input type="hidden" name="minPrice" value="${param.minPrice}">
    15 <input type="hidden" name="maxPrice" value="${param.maxPrice}">
    View Code

    案例结构

     

      在此感谢您的阅读,希望你可以提出宝贵的意见,若有错的地方还望指出,谢谢!!!

  • 相关阅读:
    Go语言中通过结构体匿名字段实现方法的继承和重载
    项目组:ouc海票票 第十一周Scrum meeting博客作业
    SQL Server数据库
    微信小程序界面美化
    Linux中的fork函数
    ouc2021秋软件工程“海票票”小组 Alpha冲刺阶段博客
    SQL Server数据库的分离和附加
    微信小程序玄学bug记录
    项目组:ouc海票票 alpha阶段测试报告
    项目组:ouc海票票 第十二周Scrum meeting会议记录
  • 原文地址:https://www.cnblogs.com/bgzyy/p/8544895.html
Copyright © 2011-2022 走看看