JT_购物车系列

  1. 各系统核心业务说明

整个工程分为8个工程,细分为26个子工程。

序号

项目名称

各子系统

业务子系统

1

jt-web

前台商城系统:用户可以访问商城首界面,查看不同分类下的商品,浏览商品的详细信息,并可以查询商品,将商品加入购物车,最终提交订单,还包括用户注册和登录。

2

jt-manage

后台管理系统:商品分类管理、商品信息管理、商品规格属性、注册用户管理以及CMS内容发布管理等功能。

包括:jt-manage-mapper/pojo/service/web四个子项目

3

jt-cart

购物车系统:未登录商品选择,登录商品选择,修改商品数量,计算支付金额,下单提交到订单系统。

4

jt-order

订单系统:提供下单、查询订单、修改订单状态、定时处理订单。

5

jt-search

搜索系统:提供商品的搜索功能。

支撑子系统

6

jt-parent

jar包依赖管理

7

jt-common

公用工具类

8

jt-sso

单点登录系统:为多个系统之间提供用户登录凭证以及查询登录用户的信息。

 

  1. 购物车接口

思考:

序号

知识点

类型

 

 

  1.  

购物车表设计,创建复合索引

设计

 

 

  1.  

商品库存,超卖现象如何解决?虚拟库存。

论述

 

 

知识点:

序号

知识点

类型

 

 

  1.  

购物车业务接口实现

技术

 

 

  1.  

购物车表设计

设计

 

 

  1.  

商品点击”加入购物车“,将页面的buy_num数量也同时提交

技术

 

 

  1.  

我的购物车列表

技术

 

 

  1.  

点击+增加商品数量,点击-减少商品数量

技术

 

 

  1.  

购物车拦截器,获取用户信息

技术

 

 

 

  1. 购物车

购物车的特点:访问压力非常大,查询量非常大,并发量比较高,数据必须做持久化。

  1. 流程图

问题:当用户登录后购买了一本书,退出登录。然后我们又买了本书,这时由于没有登录,信息是保存到cookie中的,那当登录后,cookie中的信息会怎么处理?

答:可以操作下京东网站,可以看到众多网站都是将cookie中的内容和持久化的购物车内容合并。如果买的书存在则数量加1,如果买的书是新的,就新增。

问题:当用户购买一本书时是100元,存放到购物车,但还未支付。这时搞优惠,这本书价格改为60元。那用户该支付多少?100还是60?

答:购物车时必须保存当时的价格,否则如果不保存,支付时再去获取价格就变了。如果价格低了用户当然高兴,但要高了。用户肯定会投诉的。

  1. 需求分析

1、登录逻辑:

1)数据库表保存,user_id,item_id,num,item_price,item_title等。

2)登录逻辑。

3)购物车系统中实现。

问题:如果不单独创建购物车系统,那购物车功能应该放在哪个系统中?前台?后台?单点?订单?

答:如果不做成单独的系统就应该放在后台系统中。因为在大型系统开发时,维护数据的地方应该尽量是一个地方。否则数据管理容易混乱。比如,商品添加、修改维护、删除都在后台,其他系统要数据时,只需要调用接口就行了。这样就达到一个数据的共享。这是现在主流架构,API化。就是说它把整个系统的数据操作全部提供API,其他系统就调用这些API。我们后台接口都是些API,不仅实现数据共享,还实现数据一致。

购物车系统提供API。

2、未登录逻辑:

1)将商品数据写入cookie(cookie有效期、域、数据安全性)

2)基于cookie实现提供API。

  1. 购物车系统结构图

  1. 搭建环境
  2. 创建Maven工程

  1. 增加依赖

/jt-cart/pom.xml

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    4.0.0

    com.jt.cart

    jt-cart

    war

    0.0.1-SNAPSHOT

    jt-cart Maven Webapp

    http://maven.apache.org

   

      

           com.jt.common

           jt-common

           1.0.0-SNAPSHOT

      

      

      

           junit

           junit

           test

      

      

           org.springframework

           spring-webmvc

      

      

           org.springframework

           spring-jdbc

      

      

           org.springframework

           spring-aspects

      

      

      

           org.mybatis

           mybatis

      

      

           org.mybatis

           mybatis-spring

      

      

      

           mysql

           mysql-connector-java

      

      

           org.slf4j

           slf4j-log4j12

      

      

      

           com.fasterxml.jackson.core

           jackson-databind

      

      

      

           org.apache.httpcomponents

           httpclient

      

 

      

      

           jstl

           jstl

      

      

           javax.servlet

           servlet-api

           provided

      

      

           javax.servlet

           jsp-api

           provided

      

      

      

           org.apache.commons

           commons-lang3

      

      

           org.apache.commons

           commons-io

      

      

      

           com.jolbox

           bonecp-spring

           0.8.0.RELEASE

      

      

      

           com.github.pagehelper

           pagehelper

           3.4.2

      

      

           com.github.jsqlparser

           jsqlparser

           0.9.1

      

   

 

   

      

          

          

              org.apache.tomcat.maven

              tomcat7-maven-plugin

             

                  8086

                  /

             

          

      

   

   

       com.jt.parent

       jt-parent

       1.0.0-SNAPSHOT

   

  1. 修改配置文件

与其他工程类似,这里就不一一概述。

  1. 创建运行菜单

  1. Nginx配置

    #购物车服务器

    server {

       listen       80;

       server_name  cart.jt.com;

       #charset koi8-r;

       #access_log  logs/host.access.log  main;

          

       proxy_set_header X-Forwarded-Host $host;

       proxy_set_header X-Forwarded-Server $host;

       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      

       location / {

              proxy_pass http://127.0.0.1:8086;

              proxy_connect_timeout 600;

              proxy_read_timeout 600;

       }

    }

  1. HOSTS配置

  1. 数据库表设计优化

创建数据库表

create table tb_cart

(

   id                   bigint(20) not null auto_increment,

   user_id              bigint(20),

   item_id              bigint(20),

   item_title           varchar(100),

   item_image           varchar(200),

   item_price           bigint(20) comment '单位:分',

   num                  int(10),

   created              datetime,

   updated              datetime,

   primary key (id),

   key AK_user_itemId (user_id, item_id)

);

这里建立了一个复合关键字索引。购物车一般的查询条件是查询某个用户的所有购物,或者查询某个用户某个商品是否加入到购物车。这两种情况是否用到了这个索引?支持,联合索引支持单查用户id,但不支持按商品id。也支持用户id+商品id。如果按商品id搜索,这个复合索引是失效的。

这就是数据库表设计的一个优化-索引左侧前缀特性。

  1. MySQL验证索引的使用-最左前缀特性

EXPLAIN SELECT * FROM tb_cart WHERE user_id=1 AND item_id=1

EXPLAIN SELECT * FROM tb_cart WHERE user_id=1

EXPLAIN SELECT * FROM tb_cart WHERE item_id=1

查看SQL的执行计划,可以看出第一句、第二句SQL使用了索引,第三句 SQL未使用索引。很好的证明了索引左侧前缀特性。

注意:

1)的语句违反了左侧前缀的特性,为何仍然可以使用索引呢?因为MYSQL对SQL语句有优化,它会重新组合where条件。

EXPLAIN SELECT * FROM tb_cart WHERE item_id=1 AND user_id=1

2)没有where条件的查询是不会使用索引的。

  1. 编写购物车API
  2. CartMapper.xml

/jt-cart/src/main/resources/mybatis/mappers/CartMapper.xml

  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

 

   

   

   

       INSERT INTO tb_cart (

           id,

           user_id,

           item_id,

           item_title,

           item_image,

           item_price,

           num,

           created,

           updated

       )

       VALUES

           (

              NULL,

              #{userId},

              #{itemId},

              #{itemTitle},

              #{itemImage},

              #{itemPrice},

              #{num},

              NOW(),

              NOW()

           );

   

   

   

       UPDATE tb_cart

           SET

            num = #{num},

            updated = NOW()

           WHERE

              id = #{id}

   

   

   

   

   

       UPDATE tb_cart   

           SET

            num = #{num},

            updated = NOW()

           WHERE

              user_id = #{userId} AND item_id = #{itemId}

    1

   

   

       DELETE FROM tb_cart WHERE user_id = #{userId} AND item_id = #{itemId}

   

  1. CartMapper接口

package com.jt.cart.mapper;

 

import java.util.List;

 

import org.apache.ibatis.annotations.Param;

 

import com.jt.cart.pojo.Cart;

 

public interface CartMapper {

 

    /**

     * 根据用户名id和商品id查询购物车数据

     *

     * @param userId

     * @param itemId

     * @return

     */

    Cart queryCartByUserIdAndItemId(@Param("userId") Long userId, @Param("itemId") Long itemId);

 

    /**

     * 新增购物车数据

     *

     * @param cart

     */

    void save(Cart cart);

 

    /**

     * 修改商品数量

     *

     * @param cart

     */

    void updateCartNum(Cart cart);

 

    /**

     * 根据用户id查询

     *

     * @param userId

     * @return

     */

    List queryCartList(Long userId);

 

    /**

     * 根据用户id、商品id修改数量

     *

     * @param userId

     * @param itemId

     * @param num

     * @return

     */

    Integer updateCartNumByUserIdAndItemId(@Param("userId") Long userId, @Param("itemId") Long itemId,@Param("num") Integer num);

 

    /**

     * 删除

     *

     * @param userId

     * @param itemId

     * @return

     */

    Integer delete(@Param("userId") Long userId, @Param("itemId") Long itemId);

 

}

  1. CartService类

package com.jt.cart.service;

 

import java.util.List;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

 

import com.jt.cart.mapper.CartMapper;

import com.jt.cart.pojo.Cart;

import com.jt.common.vo.SysResult;

 

@Service

public class CartService {

 

    @Autowired

    private CartMapper cartMapper;

 

    public SysResult saveItem(Cart newCart) {

        // 判断商品是否存在购物车中,如果存在,数量+1

        Cart cart = this.cartMapper.queryCartByUserIdAndItemId(newCart.getUserId(), newCart.getItemId());

        if (cart == null) {

            // 不存在

            this.cartMapper.save(newCart);

            return SysResult.ok(newCart.getId());

        } else {

            // 存在

            cart.setNum(cart.getNum() + 1);

            this.cartMapper.updateCartNum(cart);

            return SysResult.build(202, "该商品已经存在购物车中!商品数量+1", null);

        }

    }

 

    public SysResult queryCartList(Long userId) {

        List carts = this.cartMapper.queryCartList(userId);

        return SysResult.ok(carts);

    }

 

    /**

     * 修改购物车数量

     *

     * @param userId

     * @param itemId

     * @param num

     * @return

     */

    public SysResult updateNum(Long userId, Long itemId, Integer num) {

        Integer count = this.cartMapper.updateCartNumByUserIdAndItemId(userId, itemId, num);

        if (count == null || count.intValue() == 0) {

            return SysResult.build(201, "该商品不存在购物车中!");

        }

        return SysResult.ok();

    }

 

    /**

     * 删除商品

     *

     * @param userId

     * @param itemId

     * @return

     */

    public SysResult delete(Long userId, Long itemId) {

        Integer count = this.cartMapper.delete(userId, itemId);

        if (count == null || count.intValue() == 0) {

            return SysResult.build(201, "该商品不存在购物车中!");

        }

        return SysResult.ok();

    }

 

}

  1. CartController类

package com.jt.cart.controller;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.ResponseBody;

 

import com.jt.cart.pojo.Cart;

import com.jt.cart.service.CartService;

import com.jt.common.vo.SysResult;

 

/**

 * 购物车相关API

 *

 */

@RequestMapping(value = "cart")

@Controller

public class CartController {

 

    @Autowired

    private CartService cartService;

 

    /**

     * 添加商品到购物车

     *

     * @param cart

     * @return

     */

    @RequestMapping(value = "save", method = RequestMethod.POST)

    @ResponseBody

    public SysResult saveItem(Cart cart) {

        return this.cartService.saveItem(cart);

    }

 

    /**

     * 根据用户ID查询购物车数据

     *

     * @return

     */

    @RequestMapping(value = "query/{userId}", method = RequestMethod.GET)

    @ResponseBody

    public SysResult queryCartList(@PathVariable("userId") Long userId) {

        return this.cartService.queryCartList(userId);

    }

 

    /**

     * 更新商品数量

     *

     * @param userId

     * @param itemId

     * @param num

     * @return

     */

    @RequestMapping(value = "update/num/{userId}/{itemId}/{num}", method = RequestMethod.POST)

    @ResponseBody

    public SysResult updateNum(@PathVariable("userId") Long userId, @PathVariable("itemId") Long itemId,

            @PathVariable("num") Integer num) {

        return this.cartService.updateNum(userId, itemId, num);

    }

 

    /**

     * 删除购物车中的商品数据

     *

     * @param userId

     * @param itemId

     * @return

     */

    @RequestMapping(value = "delete/{userId}/{itemId}", method = RequestMethod.POST)

    @ResponseBody

    public SysResult delete(@PathVariable("userId") Long userId, @PathVariable("itemId") Long itemId) {

        return this.cartService.delete(userId, itemId);

    }

 

}

启动后台、sso、前台、搜索、RabbitMQ

  1. 配置UserThreadLocal

com.jt.web.threadlocal.UserThreadLocal

package com.jt.web.threadlocal;

 

import com.jt.web.pojo.User;

 

public class UserThreadLocal {

 

    private static final ThreadLocal USER = new ThreadLocal();

 

    public static void set(User user) {

        USER.set(user);

    }

 

    public static User get() {

        return USER.get();

    }

 

    public static Long getUserId(){

       if(null!=USER){

           return USER.get().getId();

       }else{

           return null;

       }

    }

}

  1. 前台系统CartInterceptor

package com.jt.web.interceptor;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

 

import com.jt.common.util.CookieUtils;

import com.jt.web.controller.UserController;

import com.jt.web.pojo.User;

import com.jt.web.service.UserService;

import com.jt.web.threadlocal.UserThreadLocal;

 

public class CartInterceptor implements HandlerInterceptor {

 

    @Autowired

    private UserService userService;

 

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

            throws Exception {

        String ticket = CookieUtils.getCookieValue(request, UserController.JT_TICKET);

        if (null == ticket) {

            UserThreadLocal.set(null);

            return true;

        }

 

        User user = this.userService.queryUserByTicket(ticket);

        if (user == null) {

            UserThreadLocal.set(null);

            return true;

        }

       

        // 成功

        // 将user对象放到本地线程中,方便后续使用

        UserThreadLocal.set(user);

        return true;

    }

 

    @Override

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,

            ModelAndView modelAndView) throws Exception {

 

    }

 

    @Override

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,

            Exception ex) throws Exception {

 

    }

 

}

  1. 配置拦截器springmvc-config.xml

   

   

      

      

          

          

          

      

   

  1. 前台系统CartController

package com.jt.web.controller;

 

import java.util.List;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.servlet.ModelAndView;

 

import com.jt.web.pojo.Cart;

import com.jt.web.pojo.User;

import com.jt.web.threadlocal.UserThreadLocal;

 

@RequestMapping(value = "cart")

@Controller

public class CartController {

 

    @Autowired

    private CartService cartService;

   

    @Autowired

    private CartCookieService cartCookieService;

 

    @RequestMapping("add/{itemId}")

    public String addItemToCart(@PathVariable("itemId") Long itemId,HttpServletRequest request,HttpServletResponse response) {

        // 判断用户是否登录

        User user = UserThreadLocal.get();

        if (user == null) {

            // 未登录,将商品数据保存到cookie中

            try {

                this.cartCookieService.addItemToCart(itemId, request, response);

            } catch (Exception e) {

                // TODO 出现异常的跳转?

                e.printStackTrace();

            }

        } else {

            // 登录,将商品数据保存到数据库中

            this.cartService.addItemToCart(user, itemId);

        }

        return "redirect:/cart/show.html";

    }

 

    @RequestMapping("show")

    public ModelAndView showCart(HttpServletRequest request,HttpServletResponse response) {

        ModelAndView mv = new ModelAndView("cart");

        User user = UserThreadLocal.get();

        List carts = null;

        if (user == null) {

            // 未登录,从cookie中查询商品

            try {

                carts =  this.cartCookieService.queryCartByUser(request,response,true);

            } catch (Exception e) {

                // TODO

                e.printStackTrace();

            }

        } else {

            // 登录,从api查询商品

            carts = this.cartService.queryCartByUser(user);

        }

        mv.addObject("cartList", carts);

        return mv;

    }

 

    @RequestMapping("delete/{itemId}")

    public String deleteCart(@PathVariable("itemId") Long itemId,HttpServletRequest request,HttpServletResponse response) {

        User user = UserThreadLocal.get();

        if (user == null) {

            // 未登录,从cookie中查询商品

            try {

                this.cartCookieService.deleteCart(itemId, request, response);

            } catch (Exception e) {

                // TODO

                e.printStackTrace();

            }

        } else {

            // 登录,从api查询商品

            this.cartService.deleteCart(user, itemId);

        }

        return "redirect:/cart/show.html";

    }

 

    /**

     * 更新数量

     * @param itemId

     * @param num

     * @return

     */

    @RequestMapping(value = "update/num/{itemId}/{num}")

    @ResponseBody

    public SysResult updateCart(@PathVariable("itemId") Long itemId, @PathVariable("num") Integer num,HttpServletRequest request,HttpServletResponse response) {

        User user = UserThreadLocal.get();

        if (user == null) {

            // 未登录,从cookie中查询商品

            try {

                this.cartCookieService.updateCart(request,response,itemId, num);

            } catch (Exception e) {

                e.printStackTrace();

                return SysResult.build(201, "更新数量失败!");

            }

        } else {

            // 登录,从api查询商品

            this.cartService.updateCart(user, itemId, num);

        }

        return SysResult.ok();

    }

 

}

  1. 前台系统CartService

/jt-web/src/main/java/com/jt/web/service/CartService.java

package com.jt.web.service;

 

import java.util.HashMap;

import java.util.List;

import java.util.Map;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

 

import com.fasterxml.jackson.databind.JsonNode;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.jt.common.service.HttpClientService;

import com.jt.common.spring.exetend.PropertyConfig;

import com.jt.common.vo.SysResult;

import com.jt.web.pojo.Cart;

import com.jt.web.pojo.Item;

import com.jt.web.pojo.User;

 

@Service

public class CartService {

 

    @Autowired

    private ItemService itemService;

 

    @Autowired

    private HttpClientService httpClientService;

 

    @PropertyConfig

    private String CART_URL;

 

    private static final ObjectMapper MAPPER = new ObjectMapper();

 

    public Boolean addItemToCart(User user, Long itemId) {

        // 通过商品id查询商品数据

        Item item = this.itemService.queryItemById(itemId);

        // 调用购物车系统的API添加商品

        String url = CART_URL + "/cart/save";

        Map params = new HashMap();

        params.put("userId", String.valueOf(user.getId()));

        params.put("itemId", String.valueOf(itemId));

        params.put("itemTitle", item.getTitle());

        String[] images = item.getImages();

        if (null == images) {

            params.put("itemImage", "");

        } else {

            params.put("itemImage", images[0]);

        }

        params.put("itemPrice", String.valueOf(item.getPrice()));

        params.put("num", "1");// 默认为:1

        try {

            String jsonData =httpClientService.doPost(url, params, "UTF-8");

            JsonNode jsonNode = MAPPER.readTree(jsonData);

            Integer status = jsonNode.get("status").intValue();

            if (status == 200 || status == 202) {

                return true;

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return false;

    }

 

    @SuppressWarnings("unchecked")

    public List queryCartByUser(User user) {

        String url = CART_URL + "/cart/query/" + user.getId();

        try {

            String jsonData = this.httpClientService.doGet(url);

            SysResult sysResult = SysResult.formatToList(jsonData, Cart.class);

            return (List) sysResult.getData();

        } catch (Exception e) {

            e.printStackTrace();

        }

        return null;

    }

 

    /**

     * 删除

     *

     * @param user

     * @param itemId

     * @return

     */

    public Boolean deleteCart(User user, Long itemId) {

        String url = CART_URL + "/cart/delete/" + user.getId() + "/" + itemId;

        try {

            String jsonData = this.httpClientService.doPost(url, null);

            JsonNode jsonNode = MAPPER.readTree(jsonData);

            Integer status = jsonNode.get("status").intValue();

            if (status == 200) {

                return true;

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return false;

    }

 

    /**

     * 更新数量

     * @param user

     * @param itemId

     * @param num

     * @return

     */

    public Boolean updateCart(User user, Long itemId, Integer num) {

        String url = CART_URL + "/cart/update/num/" + user.getId() + "/" + itemId + "/" + num;

        try {

            String jsonData = httpClientService.doPost(url, null);

            JsonNode jsonNode = MAPPER.readTree(jsonData);

            Integer status = jsonNode.get("status").intValue();

            if (status == 200) {

                return true;

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return false;

    }

 

}

  1. js实现金额格式化插件

当点击货物,增加或者减少数量时,ajax调用完,页面的价格跟着变化,并按上面有人民币前缀、千位符、保留两位小数等格式,可以通过js插件完成。

cart.js实现业务调用链接,jquery.price_format.2.0.min.js实现格式化。

  1. 购物车写入cookie
  2. 实现新增、修改、删除

前台系统CartCookieService

/jt-web/src/main/java/com/jt/web/service/CartCookieService.java

package com.jt.web.service;

 

import java.net.URLDecoder;

import java.net.URLEncoder;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

 

import com.fasterxml.jackson.databind.ObjectMapper;

import com.jt.common.util.CookieUtils;

import com.jt.web.pojo.Cart;

import com.jt.web.pojo.Item;

 

@Service

public class CartCookieService {

 

    private static final String JT_CART = "JT_CART";

 

    private static final ObjectMapper MAPPER = new ObjectMapper();

 

    private static final Integer COOKIE_TIME = 60 * 60 * 24 * 30;

 

    @Autowired

    private ItemService itemService;

 

    public void addItemToCart(Long itemId, HttpServletRequest request, HttpServletResponse response)

            throws Exception {

        // 读取cookie中购物车的数据

        String cookieData = CookieUtils.getCookieValue(request, JT_CART);

        List carts = null;

        if (StringUtils.isEmpty(cookieData)) {

            // 将商品数据保存到cookie中

            carts = new ArrayList(1);

            carts.add(itemToCart(itemId));

        } else {

            carts = queryCartByUser(request, response, false);

            // 判断,该商品是否存在购物车中

            boolean bool = false;

            Cart cart = null;

            for (Cart c : carts) {

                if (c.getItemId().intValue() == itemId.intValue()) {

                    bool = true;

                    cart = c;

                    break;

                }

            }

 

            if (bool) {

                // 存在

                cart.setNum(cart.getNum() + 1);

            } else {

                carts.add(itemToCart(itemId));

            }

        }

        CookieUtils.setCookie(request, response, JT_CART, MAPPER.writeValueAsString(carts), COOKIE_TIME);

    }

 

    private Cart itemToCart(Long itemId) throws Exception {

        Item item = this.itemService.queryItemById(itemId);

        Cart cart = new Cart();

        cart.setItemId(itemId);

        String[] images = item.getImages();

        if (images == null) {

            cart.setItemImage("");

        } else {

            cart.setItemImage(images[0]);   //显示第一张图片

        }

        cart.setItemPrice(item.getPrice());

        // 编码

        String title = URLEncoder.encode(item.getTitle(), "UTF-8");

        cart.setItemTitle(title);

        cart.setNum(1);

        cart.setCreated(new Date());

        return cart;

    }

 

    public List queryCartByUser(HttpServletRequest request, HttpServletResponse response,

            boolean isDecode) throws Exception {

        // 读取cookie中购物车的数据

        String cookieData = CookieUtils.getCookieValue(request, JT_CART);

        if (null == cookieData) {

            return null;

        }

        List carts = MAPPER.readValue(cookieData,

                MAPPER.getTypeFactory().constructCollectionType(List.class, Cart.class));

        if (isDecode) {

            for (Cart cart : carts) {

                String title = URLDecoder.decode(cart.getItemTitle(), "UTF-8");

                cart.setItemTitle(title);

            }

        }

        return carts;

    }

 

    public void deleteCart(Long itemId, HttpServletRequest request, HttpServletResponse response)

            throws Exception {

        List carts = queryCartByUser(request, response, false);

        for (int i = 0; i < carts.size(); i++) {

            Cart cart = carts.get(i);

            if (cart.getItemId().intValue() == itemId.intValue()) {

                carts.remove(i);

                break;

            }

        }

        // 写入到cookie中

        CookieUtils.setCookie(request, response, JT_CART, MAPPER.writeValueAsString(carts), COOKIE_TIME);

    }

 

    public void updateCart(HttpServletRequest request, HttpServletResponse response, Long itemId, Integer num)throws Exception {

        List carts = queryCartByUser(request, response, false);

        for (int i = 0; i < carts.size(); i++) {

            Cart cart = carts.get(i);

            if (cart.getItemId().intValue() == itemId.intValue()) {

                cart.setNum(num);

                break;

            }

        }

        // 写入到cookie中

        CookieUtils.setCookie(request, response, JT_CART, MAPPER.writeValueAsString(carts), COOKIE_TIME);

    }

 

}

  1. Cookie中不能保存中文,对中文字段编码

在写入数据之前编码

String title = URLEncoder.encode(item.getTitle(), "UTF-8");

cart.setItemTitle(title);

从数据库读出,展示前解码

String title = URLDecoder.decode(cart.getItemTitle(), "UTF-8");

实现有什么问题?

1)不安全,明文。

2)清空浏览器(缓存)数据,购物车数据丢失(不登录情况下)。

3)如果用户禁用cookie,功能失效。

4)Cookie是有长度限制。如下图所示:

解决方案:

1)只保存商品ID和购买数量。(问题得不到根本解决)

2)生成key,根据key去后台接口中查询数据。

    A)保存数据库中(性能问题)

    B)保存redis中(不能持久化)

    C)两者结合使用(选定)

  1. 购物车下单

/jt-web/src/main/java/com/jt/web/controller/OrderController.java

    //创建订单

    @RequestMapping("create")

    public ModelAndView createOrder() {

        User user = UserThreadLocal.get();

        // 查询该用户的购物车

        List carts = this.cartService.queryCartByUser(user);

        ModelAndView mv = new ModelAndView("order-cart");

        mv.addObject("carts", carts);

        return mv;

    }

  1. 结算

启动订单系统,点击“结算”。

    //结算

    @RequestMapping("submit")

    @ResponseBody

    public SysResult create(Order order) {

        User user = UserThreadLocal.get();

        order.setUserId(user.getId());

        order.setBuyerNick(user.getUsername());

        String orderNo = this.orderService.createOrder(order);

        return SysResult.ok(orderNo);

    }

  1. 购物车遗留问题
  1. 用户登录后cookie中的数据和数据库中的数据合并问题?
  2. 下单成功后清空购物车数据
  3. 下单成功后,商品库存要相应减少
  4. Cookie存储优化
  5. 商品详情页,购买商品数量功能实现
  6. 购物车中选择某些商品下单

你可能感兴趣的:(JT,SSM)