A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小石姐姐 于 2018-6-21 14:24 编辑

网上商城 学习笔记

准备工作
解决一个请求对应一个Servlet导致的Servlet过多的方案:
创建通用的Servlet类,利用反射重写其中的service方法,各个模块的Servelt继承此类,只需按照约定的参数及返回值编写方法即可
[Java] 纯文本查看 复制代码
public class BaseServlet extends HttpServlet{
     public void service(HttpServletRequest req,HttpServletResponse resp){
          //获取要调用的的方法名称
          String methodName = request.getParameter(“method”);
          // 获得Class,利用反射执行方法
          Class clazz = this.getClass(); 
          Method method = clazz.getMethod(methodName,HttpServletRequest.class,HttpSerlvetResponse.class);
          //接收方法的返回值,如果不为null就转发
          String path = (String)method.invoke(this,req,resp);
          if(path != null){
              req.getRequestDispatcher(path).forward(req,resp);
          }
     }
}
用户模块
  • 用户登陆

    • 验证码
      通过ajax将请求发送到负责生成验证码的Servlet,产生验证码并存到session中;
      登陆校验时首先验证session中的验证码与从前台中获得的验证是否一致,如果不一致,则登陆失败,如果一致再执行后续的代码;
    • 记住用户名
      1.前台:
      [HTML] 纯文本查看 复制代码
      <!--用户名文本框-->
      <input type="text" class="form-control" id="username" name="username" value="${cookie.username.value }" placeholder="请输入用户名">
      <!--记住用户名复选框-->
      <input type="checkbox" name="remember" value="true"> 记住用户名

      2.后台:如果从前台获得的值为true并且登陆成功时,则将用户名存入到cookie中
      [Java] 纯文本查看 复制代码
      String remember = request.getParameter("remember");
      if("true".equals(remember)){
          Cookie cookie = new Cookie("username", existUser.getUsername());
          cookie.setPath(request.getContextPath());
          cookie.setMaxAge(60*60*24);
          response.addCookie(cookie);
      }
    • 自动登陆

      • 前台

      [HTML] 纯文本查看 复制代码
      <!--自动登录复选框-->
      <input type="checkbox" name="autoLogin" value="true"> 自动登录

      • 后台:自动登陆是一个全局的功能,所以用Filter实现
        自动登陆逻辑:
        *判断session中是否有用户的信息:
                 * * session中如果有:放行.
                 * * session中没有:
                 *    * 从Cookie中获取:
                 *        * Cookie中没有:放行.
                 *        * Cookie中有:
                 *            * 获取Cookie中存的用户名和密码到数据库查询.
                 *                * 没有查询到:放行.
                 *                * 查询到:将用户信息存入到session . 放行
        前台判断session域中的existUser是否存在即可判断是否登录的状态.
      [Java] 纯文本查看 复制代码
      //仅需在session中没有用户信息,cookie中存在且正确的情况下才需要在把用户信息存到session中,其他情况直接放行
      HttpServletRequest req = (HttpServletRequest) request;//转为HttpServletRequest对象
      HttpSession session = req.getSession();//获取session
      User existUser = (User) session.getAttribute("existUser");//获取session中的用户信息
      if (existUser == null) {//如果用户信息不存在
          Cookie[] cookies = req.getCookies();
          Cookie c = CookieUtils.getCookie(cookies, "autoLogin");//获取保存登陆信息的cookie
          if (c != null) {//如果cookie不为空,也就是登陆信息存在
              String username = c.getValue().split("#")[0];//获取保存的用户信息
              String password = c.getValue().split("#")[1];
              User user = new User();
              user.setUsername(username);
              user.setPassword(password);
              try {
                  existUser = new UserServiceImpl().login(user);//查询cookie中保存的是否正确
                  if (existUser != null) {//如果能查到,将该信息保存到session中
                      session.setAttribute("existUser", existUser);
                  }
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
      }
      chain.doFilter(req, response);//其他条件直接放行
  • 用户注册

    • 异步用户名校验

      • 前台
        [JavaScript] 纯文本查看 复制代码
        $("#username").bind("blur",function(){
            if($(this).val()!=""){
                $.ajax({
                    url:"${pageContext.request.contextPath}/UserServlet",
                    data:{username:$(this).val(),"method":"checkUsername"},
                    success:function(data){
                        if(data!=""){
                            if(data==0){//如果后台返回的值为0,则表示用户名可用
                                $("#msgSpan").html("<font class='msg' color='green'>用户名可用</font>");
                                $("#submit").prop("disabled",false);
                            }else if(data==1){//如果后台返回的值为1,则表示用户名不可用
                                $("#msgSpan").html("<font class='msg' color='red'>用户名不可用</font>");
                                $("#submit").prop("disabled",true);
                            }
                        }
                    }
                });
            }else{
                $("#msgSpan").empty();
            }
        });
      • 后台
        [Java] 纯文本查看 复制代码
        public String checkUsername(HttpServletRequest request,HttpServletResponse response){
            String username = request.getParameter("username");
            try {
                User existUser = new UserServiceImpl().checkUsername(username);
                if(existUser == null){
                    response.getWriter().print(0);
                }else{
                    response.getWriter().print(1);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } 
            return null;
        }

    • 发送邮件的实现
      [Java] 纯文本查看 复制代码
      public static void sendMail(String to, String code){
          Properties props = new Properties();
          //props.setProperty("smtp", "localhost");
          //props.setProperty("pop", "localhost");
          Session session = Session.getInstance(props, new Authenticator(){
              @Override
              protected PasswordAuthentication getPasswordAuthentication() {
                  return new PasswordAuthentication("service@store.com", "111");
              }
          });
          try {
              Message message = new MimeMessage(session);
              message.setFrom(new InternetAddress("service@store.com"));
              message.addRecipient(RecipientType.TO, new InternetAddress(to));
              message.setSubject("来自商城的激活邮件");
              message.setContent("<h3>请点击激活</h3><h3><a href='http://localhost:8080/store_v2.0/UserServlet?method=active&code="+code+"'>http://localhost:8080/store_v2.0/UserServlet?method=active&code="+code+"</a></h3>","text/html;charset=UTF-8");
              Transport.send(message);
          } catch (Exception e) {
              e.printStackTrace();
          } 
      }
    • 激活账户
      点击邮件中的链接,向Servlet发送请求,将数据库中保存激活码的字段置为null;
  • 用户退出
    销毁存储在浏览器的cookie
    手动将session设置过期
    [Java] 纯文本查看 复制代码
    public String logOut(HttpServletRequest request, HttpServletResponse response){
        Cookie cookie = new Cookie("autoLogin", "");
        cookie.setPath(request.getContextPath());
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        request.getSession().invalidate();
        return "/index.jsp";
    }
  • 邮件
    邮件相关概念:

    • 邮箱服务器:如果一台电脑安装了邮箱服务器的软件,这台电脑称为是邮箱服务器.'
    • 电子邮箱:邮箱服务器上的一块空间,通过电子邮箱账号访问这块空间的数据.
    • 收发邮件的协议:

      • 发邮件
        SMTP协议:SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 默认端口号是25
      • 收邮件:
        POP3协议:POP3,全名为“Post Office Protocol - Version 3”,即“邮局协议版本3”。是TCP/IP协议族中的一员。默认端口是110
        IMAP协议:IMAP(Internet Mail Access Protocol,Internet邮件访问协议)以前称作交互邮件访问协议(Interactive Mail Access Protocol)

分类模块
  • 缓存
    当向数据库频繁查询数据,且这些数据不经常变动时,可以将这些数据存储到缓存中;另外,为了更好的用户体验,可以把数据存储到缓存的操作通过Listener放在服务器启动时进行
    [Java] 纯文本查看 复制代码
    缓存工具类
    private static CacheManager cacheManager;//创建缓存管理对象
    static{//从配置文件中读取缓存管理对象
        cacheManager = CacheManager.create(CacheUtils.class.getClassLoader().getResourceAsStream("ehcache.xml"));
    }
    public static Object getValue(String cacheName,Object key){
        try {
            Cache cache = cacheManager.getCache(cacheName);
            Element element = cache.get(key);
            if(element!=null){
                return element.getObjectValue();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    ​
    public static void putValue(String cacheName,Object key,Object value){
        try { 
            Cache cache = cacheManager.getCache(cacheName);
            Element element = new Element(key,value);
            cache.put(element);
        } catch (Exception e) {
             e.printStackTrace();
        }
    }
    ​
    public static void removeCache(String cacheName,Object key) {
        Cache cache = cacheManager.getCache(cacheName);
        cache.remove(key);
    }
  • 热门、最新商品展示
    通过is_hot和is_new字段查询List<Product> hotlist 和List<Product> newlist,将这两集合转发到商品展示页面,遍历集合展示
  • 按商品分类分页显示商品信息
    通过商品分类id查询PageBean<Product> pageBean,将pageBean对象转发到分类显示页面,遍历pageBean中的List<Product>集合展示

商品模块
  • 商品列表
    点击栏目后,往ProductServlet中传method,cid参数和当前页(因为需要分页,所以传当前页).
    难点:
    分页:封装PageBean对象.
    了解PageBean对象中属性,封装对象,设置PageBean的参数,然后返回给前台.

    • 当前页数
    • 页面大小
    • 商品总数
    • 总页数:总页数=总数/页面大小 向上取整.注意Double类型向int类型的转换.
    • 装有商品的list集合:
      查询商品集合的时候,注意传参,当前页CurrPage,begin开始,和PageSize页面大小;其中begin = (currpage-1)*pageSize;

  • 浏览记录
    步骤分析:

    • 获取所有的cookie,的到cookie[] cookies.
    • 查询是否有浏览记录的cookie.判断是否有这样的cookie
      ​        *如果没有这样的cookie,则创建一个这样的cookie,设置声明周期路径,然后response.add添加回去.
      ​        *如果有这样的cookie.判断里面是否有此商品的浏览记录,将cookie数组转换成LinkedList集合.便于对里面cookie信息的增删和移动位置
      ​                *如果有此商品1,比如原来顺序是 2-1-3,则更改list集合将1删除  然后将1放到第一个.
      ​                *如果没有此商品的浏览记录,则判断浏览记录长度是否大于指定的浏览记录长度6
      ​                        *浏览记录大于等于6,删除最后一个,将商品1添加到第一个.
      ​                        *浏览记录小于6,直接将商品1添加到第一个.
    • 遍历list集合,拼接cookie信息的字符串,再截取字符串.此处注意最后一个分隔符需要截取掉.
    • 将cookie写回即可.
    • 返回前台,在前台用java语言根据cookie信息,业务层-dao层查找到商品信息.放到域中,再从jsp页面中调用商品信息即可.
      [Java] 纯文本查看 复制代码
      <-- 商品浏览记录 -->   
      <%
          Cookie[] cookies = request.getCookies();
          Cookie cookie = CookieUtils.findCookie(cookies, "history");
          if(cookie==null){
              out.print("<h3>您还没有浏览记录!</h3>");
              return;
          }
          String value = cookie.getValue();
          String[] ids = value.split("-");
          ProductService productService = new ProductServiceImpl();
          for(String id:ids){
              Product product= productService.findByPid(id);
              request.setAttribute("p", product);
      %>
          <li><img src="${pageContext.request.contextPath}/${p.pimage}" /></li>
      <%
          }
      %>


根据商品id查询商品的、将商品对象转发到商品详情页面
购物模块
  • 创建购物车和购物项的实体类

    • 购物项CartItem:商品对象,数量,小计
      [Java] 纯文本查看 复制代码
      private Product product;
      private Integer count;
      private Double subTotal;
    • 购物车Cart:购物项集合(Map集合,泛型为String和CartItem),总计
      [Java] 纯文本查看 复制代码
      private Map<String, CartItem> map = new LinkedHashMap<>();
      private Double totalCount = 0d;

      购物车中的addItem方法:
      [Java] 纯文本查看 复制代码
      public void addItem(CartItem cartItem) {
          String pid = cartItem.getProduct().getPid();
          if(!this.map.containsKey(pid)){
              this.map.put(pid, cartItem);
          }else{
              CartItem _cartItem = this.map.get(pid);
              _cartItem.setCount(cartItem.getCount()+_cartItem.getCount());
          }
          totalCount+=cartItem.getSubTotal();
      }

  • 加入购物车
    根据商品id查询商品,将查询到的商品对象封装到购物项中,再把购物项封装到购物车对象中,最后将购物车对象存入到session中
  • 清空购物车
    调用购物车对象的清空购物车方法
    [Java] 纯文本查看 复制代码
    public void clearCart() {
        this.map.clear();
        this.totalCount = 0d;
    }
  • 移除购物项
    调用购物车对象的移除购物项方法
    [Java] 纯文本查看 复制代码
    public void removeItem(String pid) {
        CartItem cartItem = this.map.remove(pid);
        totalCount -= cartItem.getSubTotal();
    }
  • 刷新页面导致的重复添加商品问题的解决办法:

    • 方法一:
      通过重定向,跳转到购物车页面
    • 方法二:
      在页面中使用JSP定义变量token,并存入session,另外在页面的表单中添加一个隐藏元素,value的值为token的值,在表单提交时一同提交到后台;
      登陆校验时首先验证session中的token与从前台中获得的token是否一致,如果不一致,则登陆失败,如果一致再执行后续的代码.最后不管token是否一致,都需要把session中的token移除;
      [Java] 纯文本查看 复制代码
      <% 
           String token = UUIDUtils.getUUID();
           request.getSession().setAttribute("token", token);
      %>
      <input type="hidden" name="token" value="${token }">


订单模块
  • 创建订单和订单项实体

    • 订单Order:
      [Java] 纯文本查看 复制代码
      private Date ordertime;
      private Double total;
      private Integer state; // 1:未付款   2:已经付款,未发货. 3:已经发货,没有确认收货.  4:已经确认收货,订单结束.
      private String address;
      private String name;
      private String telephone;
      private User user;
      private List<OrderItem> orderItems = new ArrayList<OrderItem>();
    • 订单项OrderItem:
      [Java] 纯文本查看 复制代码
      private String itemid;
      private Integer count;
      private Double subtotal;
      private Product product;
      private Order order;

  • 生成订单
    [Java] 纯文本查看 复制代码
    //创建订单
    Order order = new Order();
    order.setOid(UUIDUtils.getUUID());
    order.setOrdertime(new Date());
    order.setTotal(cart.getTotalCount());
    order.setState(1);
    order.setUser(existUser);
    //创建订单项
    List<OrderItem> orderItems = order.getOrderItems();
    Collection<CartItem> cartItems = cart.getMap().values();
    for (CartItem cartItem : cartItems) {
        OrderItem orderItem = new OrderItem();
        orderItem.setItemid(UUIDUtils.getUUID());
        orderItem.setCount(cartItem.getCount());
        orderItem.setSubtotal(cartItem.getSubTotal());
        orderItem.setProduct(cartItem.getProduct());
        orderItem.setOrder(order);
        orderItems.add(orderItem);//将订单项添加到订单中的集合
    }
    //调用dao层的save方法将订单保存到数据库
    OrderService orderService = (OrderService) BeansFactory.getBean("OrderService");
    orderService.save(order);
  • 根据用户id查询订单并分页显示PageBean<Order> pageBean
    Dao层根据用户id查询,并返回订单集合List<Order>
    [AppleScript] 纯文本查看 复制代码
    public List<Order> findByUid(String uid, int begin, int pageSize) throws Exception {
        QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
        String sql = "select * from orders where uid = ? order by ordertime desc limit ?,?";
        List<Order> list = qr.query(sql, new BeanListHandler<Order>(Order.class), uid, begin, pageSize);//根据用户id查询订单,此时list中并没有product的信息
        for (Order order : list) {
            sql = "select * from orderitem o,product p where o.oid = ? and o.pid = p.pid";
            List<Map<String, Object>> mapList = qr.query(sql, new MapListHandler(), order.getOid());
            for (Map<String, Object> map : mapList) { // 遍历List,获取其中的每个Map
                Product product = new Product(); // 创建商品对象
                BeanUtils.populate(product, map); // 封装商品数据
                OrderItem orderItem = new OrderItem(); // 创建订单项
                BeanUtils.populate(orderItem, map); // 封装订单项数据
                orderItem.setProduct(product); // 将商品信息设置到订单项中
                order.getOrderItems().add(orderItem); // 将订单项添加到订单中
            }
        }
        return list;
    }

    将List<Order>集合封装到pageBean中,转发到页面并遍历
  • 根据订单id查询订单
    根据订单id查询,返回订单order
    [Java] 纯文本查看 复制代码
    public Order findByOid(String oid) throws Exception {
        QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
        String sql = "select * from orders where oid = ?";
        Order order = qr.query(sql, new BeanHandler<Order>(Order.class), oid);
        sql = "select * from product p,orderitem o where o.oid = ? and o.pid = p.pid";
        List<Map<String, Object>> maps = qr.query(sql, new MapListHandler(), oid);
        for (Map<String, Object> map : maps) {
            Product product = new Product();
            BeanUtils.populate(product, map);
            OrderItem orderItem = new OrderItem();
            BeanUtils.populate(orderItem, map);
            orderItem.setProduct(product);
            order.getOrderItems().add(orderItem);
        }
        return order;
    }

    转发order到页面并取值展示

订单支付
  • 根据第三方支付公司提供的API中,填充参数,向指定的地址发送请求
  • 支付完成后,接收第三方支付公司返回的信息
  • 根据返回的信息更改数据库中该订单的支付状态



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马