The principle of shopping cart and Java implementation (imitation of Jingdong implementation principle)

To start writing about shopping carts today, here are four questions:

1) The user does not log in with the user name and password, add products, close the browser and open it again without logging in the user name and password. Q: Are the items in the shopping cart still available? 

2) The user logs in with the username and password, adds products, closes the browser and reopens it, but does not log in with the username and password. Q: Is the shopping cart still available?   

3) The user logs in with the user name and password, adds products, closes the browser, then opens it again, and logs in the user name and password. Q: Are the items in the shopping cart still available?

4) The user logs in with the user name and password, adds products, closes the browser, and opens the browser in the hometown.

The above four questions are all based on JD.com, so guess what the result is?

1) in

2) is gone

3) in

4) in

If you can guess the answer, it means that you are really good, so how did you achieve these four points? (If you don’t agree, you can use JD.com to experiment)

Next, let's explain the principle of the shopping cart, and finally let's talk about the specific code implementation.

1) The user does not log in and adds a product. At this time, the product is added to the browser's cookie, so when the user visits again (without logging in), the product is still in the cookie, so the product in the shopping cart still exists.

2) The user logs in and adds products. At this time, the products in the cookie and the products selected by the user will be added to the shopping cart, and then the products in the cookie will be deleted. So when the user visits again (without logging in), the shopping in the cookie will be The cart item has been deleted, so the item in the shopping cart is no longer there.

3) The user logs in and adds a product. At this time, the product is added to the database for persistent storage. After opening the login user name and password again, the product selected by the user must still exist, so the product in the shopping cart still exists.

4) Reason 3)

Here, let’s talk about the advantages of not logging in to save products to Cookie and the comparison between saving to Session and database:

1: Cookies: Advantages: Save the user's browser (without wasting our company's server) Disadvantages: Cookies are disabled, no saving is provided

2: Session: (Redis: wastes a lot of server memory: implement, disable cookies) very fast

3: The database (Mysql, Redis, SOlr) can be persistent, but the speed of the database is too slow

So what I'm going to talk about today is:

  • User not logged in: cart added to cookie

  • User login: save the shopping cart to Redis (no database required)

Overall idea diagram:

Next is the code example to implement the function of the shopping cart:

First, let's look at the design of the two JavaBeans for the shopping cart and shopping items:

Cart: buyerCart.java

1 public class BuyerCart implements Serializable{
3     /**
4      * 购物车
5      */
6     private static final long serialVersionUID = 1L;
7     
8     //商品结果集
9     private List<BuyerItem> items = new ArrayList<BuyerItem>();
10     
11     //添加购物项到购物车
12     public void addItem(BuyerItem item){
13         //判断是否包含同款
14         if (items.contains(item)) {
15             //追加数量
16             for (BuyerItem buyerItem : items) {
17                 if (buyerItem.equals(item)) {
18                     buyerItem.setAmount(item.getAmount() + buyerItem.getAmount());
19                 }
20             }
21         }else {
22             items.add(item);
23         }
24         
25     }
26 
27     public List<BuyerItem> getItems() {
28         return items;
29     }
30 
31     public void setItems(List<BuyerItem> items) {
32         this.items = items;
33     }
34     
35     
36     //小计
37     //商品数量
38     @JsonIgnore
39     public Integer getProductAmount(){
40         Integer result = 0;
41         //计算
42         for (BuyerItem buyerItem : items) {
43             result += buyerItem.getAmount();
44         }
45         return result;
46     }
47     
48     //商品金额
49     @JsonIgnore
50     public Float getProductPrice(){
51         Float result = 0f;
52         //计算
53         for (BuyerItem buyerItem : items) {
54             result += buyerItem.getAmount()*buyerItem.getSku().getPrice();
55         }
56         return result;
57     }
58     
59     //运费
60     @JsonIgnore
61     public Float getFee(){
62         Float result = 0f;
63         //计算
64         if (getProductPrice() < 79) {
65             result = 5f;
66         }
67         
68         return result;
69     }
70     
71     //总价
72     @JsonIgnore
73     public Float getTotalPrice(){
74         return getProductPrice() + getFee();
75     }
76     
77 }

The @JsonIgonre annotation is used here because the following needs to convert BuyerCart into Json format, and these fields only have the get method, so they cannot be converted, you need to use ignore Json.

Here is the shopping item: buyerItem.java

1 public class BuyerItem implements Serializable{
3     private static final long serialVersionUID = 1L;
5     //SKu对象
6     private Sku sku;
7     
8     //是否有货
9     private Boolean isHave = true;
10     
11     //购买的数量
12     private Integer amount = 1;
13 
14     public Sku getSku() {
15         return sku;
16     }
17 
18     public void setSku(Sku sku) {
19         this.sku = sku;
20     }
21 
22     public Boolean getIsHave() {
23         return isHave;
24     }
25 
26     public void setIsHave(Boolean isHave) {
27         this.isHave = isHave;
28     }
29 
30     public Integer getAmount() {
31         return amount;
32     }
33 
34     public void setAmount(Integer amount) {
35         this.amount = amount;
36     }
37 
38     @Override
39     public int hashCode() {
40         final int prime = 31;
41         int result = 1;
42         result = prime * result + ((sku == null) ? 0 : sku.hashCode());
43         return result;
44     }
45 
46     @Override
47     public boolean equals(Object obj) {
48         if (this == obj) //比较地址
49             return true;
50         if (obj == null)
51             return false;
52         if (getClass() != obj.getClass())
53             return false;
54         BuyerItem other = (BuyerItem) obj;
55         if (sku == null) {
56             if (other.sku != null)
57                 return false;
58         } else if (!sku.getId().equals(other.sku.getId()))
59             return false;
60         return true;
61     }
62 }

1. Add the product to the shopping cart

1 //加入购物车
2 function  addCart(){
3       //  + skuId
4       window.location.href="/shopping/buyerCart?skuId="+skuId+"&amount="+$("#buy-num").val();
5 }

The parameters passed in here are skuId (the primary key of the inventory table, the product id, color, size, inventory and other information saved in the inventory table), the purchase quantity amount.

Then let's see how the Controller handles it:

1 //加入购物车
2     @RequestMapping(value="/shopping/buyerCart")
3     public <T> String buyerCart(Long skuId, Integer amount, HttpServletRequest request,
4             HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException{
5         //将对象转换成json字符串/json字符串转成对象
6         ObjectMapper om = new ObjectMapper();
7         om.setSerializationInclusion(Include.NON_NULL);
8         BuyerCart buyerCart = null;
9         //1,获取Cookie中的购物车
10         Cookie[] cookies = request.getCookies();
11         if (null != cookies && cookies.length > 0) {
12             for (Cookie cookie : cookies) {
13                 //
14                 if (Constants.BUYER_CART.equals(cookie.getName())) {
15                     //购物车 对象 与json字符串互转
16                     buyerCart = om.readValue(cookie.getValue(), BuyerCart.class);
17                     break;
18                 }
19             }
20         }
21         
22         //2,Cookie中没有购物车, 创建购物车对象
23         if (null == buyerCart) {
24             buyerCart = new BuyerCart();
25         }
26         
27         //3, 将当前款商品追加到购物车
28         if (null != skuId && null != amount) {
29             Sku sku = new Sku();
30             sku.setId(skuId);
31             BuyerItem buyerItem = new BuyerItem();
32             buyerItem.setSku(sku);
33             //设置数量
34             buyerItem.setAmount(amount);
35             //添加购物项到购物车
36             buyerCart.addItem(buyerItem);
37         }
38         
39         //排序  倒序
40         List<BuyerItem> items = buyerCart.getItems();
41         Collections.sort(items, new Comparator<BuyerItem>() {
42 
43             @Override
44             public int compare(BuyerItem o1, BuyerItem o2) {
45                 return -1;
46             }
47             
48         });
49         
50         //前三点 登录和非登录做的是一样的操作, 在第四点需要判断
51         String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));
52         if (null != username) {
53             //登录了
54             //4, 将购物车追加到Redis中
55             cartService.insertBuyerCartToRedis(buyerCart, username);
56             //5, 清空Cookie 设置存活时间为0, 立马销毁
57             Cookie cookie = new Cookie(Constants.BUYER_CART, null);
58             cookie.setPath("/");
59             cookie.setMaxAge(-0);
60             response.addCookie(cookie);
61         }else {
62             //未登录
63             //4, 保存购物车到Cookie中
64             //将对象转换成json格式
65             Writer w = new StringWriter();
66             om.writeValue(w, buyerCart);
67             Cookie cookie = new Cookie(Constants.BUYER_CART, w.toString());
68             //设置path是可以共享cookie
69             cookie.setPath("/");
70             //设置Cookie过期时间: -1 表示关闭浏览器失效  0: 立即失效  >0: 单位是秒, 多少秒后失效
71             cookie.setMaxAge(24*60*60);
72             //5,Cookie写会浏览器
73             response.addCookie(cookie);
74         }
75         
76         //6, 重定向
77         return "redirect:/shopping/toCart";
78     }

Here is a knowledge point designed: convert objects into json strings/json strings into objects

We first write a small Demo here to demonstrate the mutual conversion between json and objects, using the ObjectMapper class in springmvc.

1 public class TestJson {
3     @Test
4     public void testAdd() throws Exception {
5         TestTb testTb = new TestTb();
6         testTb.setName("范冰冰");
7         ObjectMapper om = new ObjectMapper();
8         om.setSerializationInclusion(Include.NON_NULL);
9         //将对象转换成json字符串
10         Writer wr = new StringWriter();
11         om.writeValue(wr, testTb);
12         System.out.println(wr.toString());
13         
14         //转回对象
15         TestTb r = om.readValue(wr.toString(), TestTb.class);
16         System.out.println(r.toString());
17     }
18     
19 }

Results of the: 

Here we use Include.NON_NULL. If the attribute in TestTb is null, it will not be converted to Json. From object --> Json string, objectMapper.writeValue( is used). From Json string --> object is used objectMapper.readValue().
Returning to the code in our project above, this item will be added to the cookie only when the item is not logged in to add it.

1 //未登录
2             //4, 保存购物车到Cookie中
3             //将对象转换成json格式
4             Writer w = new StringWriter();
5             om.writeValue(w, buyerCart);
6             Cookie cookie = new Cookie(Constants.BUYER_CART, w.toString());
7             //设置path是可以共享cookie
8             cookie.setPath("/");
9             //设置Cookie过期时间: -1 表示关闭浏览器失效  0: 立即失效  >0: 单位是秒, 多少秒后失效
10             cookie.setMaxAge(24*60*60);
11             //5,Cookie写会浏览器
12             response.addCookie(cookie);

We debug can see:

Here, the object shopping cart object buyerCart has been converted into Json format.

To add a product to the shopping cart, whether you are logged in or not, you must first remove the shopping cart in the cookie, and then add the currently selected product to the shopping cart.

Then if you log in, clear the shopping cart in the cookie, and add the content of the shopping cart to Redis for persistent storage.

If not logged in, append the selected item to the cookie.

The code to append the shopping cart to Redis: insertBuyerCartToRedis (this includes judging whether the added item is the same)

1 //保存购物车到Redis中
2     public void insertBuyerCartToRedis(BuyerCart buyerCart, String username){
3         List<BuyerItem> items = buyerCart.getItems();
4         if (items.size() > 0) {
5             //redis中保存的是skuId 为key , amount 为value的Map集合
6             Map<String, String> hash = new HashMap<String, String>();
7             for (BuyerItem item : items) {
8                 //判断是否有同款
9                 if (jedis.hexists("buyerCart:"+username, String.valueOf(item.getSku().getId()))) {
10                     jedis.hincrBy("buyerCart:"+username, String.valueOf(item.getSku().getId()), item.getAmount());
11                 }else {
12                     hash.put(String.valueOf(item.getSku().getId()), String.valueOf(item.getAmount()));
13                 }
14             }
15             if (hash.size() > 0) {
16                 jedis.hmset("buyerCart:"+username, hash);
17             }
18         }
19         
20     }

Determine whether the user is logged in: String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));

1 public class RequestUtils {
3     //获取CSessionID
4     public static String getCSessionId(HttpServletRequest request, HttpServletResponse response){
5         //1, 从Request中取Cookie
6         Cookie[] cookies = request.getCookies();
7         //2, 从Cookie数据中遍历查找, 并取CSessionID
8         if (null != cookies && cookies.length > 0) {
9             for (Cookie cookie : cookies) {
10                 if ("CSESSIONID".equals(cookie.getName())) {
11                     //有, 直接返回
12                     return cookie.getValue();
13                 }
14             }
15         }
16         //没有, 创建一个CSessionId, 并且放到Cookie再返回浏览器.返回新的CSessionID
17         String csessionid = UUID.randomUUID().toString().replaceAll("-", "");
18         //并且放到Cookie中
19         Cookie cookie = new Cookie("CSESSIONID", csessionid);
20         //cookie  每次都带来, 设置路径
21         cookie.setPath("/");
22         //0:关闭浏览器  销毁cookie. 0:立即消失.  >0 存活时间,秒
23         cookie.setMaxAge(-1);
24         
25         return csessionid;
26     }
27 }

 

1 //获取
2     public String getAttributterForUsername(String jessionId){
3         String value = jedis.get(jessionId + ":USER_NAME");
4         if(null != value){
5             //计算session过期时间是 用户最后一次请求开始计时.
6             jedis.expire(jessionId + ":USER_NAME", 60*exp);
7             return value;
8         }
9         return null;
10     }

2. Shopping cart display page

Finally redirect to the shopping cart display page: return "redirect:/shopping/toCart"; There are two ways to enter the checkout page here:

1) Click Add to Cart on the product details page.

2) Click the shopping cart button directly to enter the shopping cart checkout page.

Here is the code for the checkout page:

1 @Autowired
2     private CartService cartService;
3     //去购物车结算, 这里有两个地方可以直达: 1,在商品详情页 中点击加入购物车按钮  2, 直接点击购物车按钮
4     @RequestMapping(value="/shopping/toCart")
5     public String toCart(Model model, HttpServletRequest request,
6             HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException{ 
7         //将对象转换成json字符串/json字符串转成对象
8         ObjectMapper om = new ObjectMapper();
9         om.setSerializationInclusion(Include.NON_NULL);
10         BuyerCart buyerCart = null;
11         //1,获取Cookie中的购物车
12         Cookie[] cookies = request.getCookies();
13         if (null != cookies && cookies.length > 0) {
14             for (Cookie cookie : cookies) {
15                 //
16                 if (Constants.BUYER_CART.equals(cookie.getName())) {
17                     //购物车 对象 与json字符串互转
18                     buyerCart = om.readValue(cookie.getValue(), BuyerCart.class);
19                     break;
20                 }
21             }
22         }
23         
24         //判断是否登录
25         String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));
26         if (null != username) {
27             //登录了
28             //2, 购物车 有东西, 则将购物车的东西保存到Redis中
29             if (null == buyerCart) {
30                 cartService.insertBuyerCartToRedis(buyerCart, username);
31                 //清空Cookie 设置存活时间为0, 立马销毁
32                 Cookie cookie = new Cookie(Constants.BUYER_CART, null);
33                 cookie.setPath("/");
34                 cookie.setMaxAge(-0);
35                 response.addCookie(cookie);
36             }
37             //3, 取出Redis中的购物车
38             buyerCart = cartService.selectBuyerCartFromRedis(username);
39         }
40         
41         
42         //4, 没有 则创建购物车
43         if (null == buyerCart) {
44             buyerCart = new BuyerCart();
45         }
46         
47         //5, 将购物车装满, 前面只是将skuId装进购物车, 这里还需要查出sku详情
48         List<BuyerItem> items = buyerCart.getItems();
49         if(items.size() > 0){
50             //只有购物车中有购物项, 才可以将sku相关信息加入到购物项中
51             for (BuyerItem buyerItem : items) {
52                 buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId()));
53             }
54         }
55         
56         //5,上面已经将购物车装满了, 这里直接回显页面
57         model.addAttribute("buyerCart", buyerCart);
58         
59         //跳转购物页面
60         return "cart";
61     }

This is the shopping cart details display page. It should be noted here that if the same product is added continuously, it needs to be merged.

The shopping cart details display page includes two parts, 1) product details 2) total (total product, shipping cost)

1) The product details also include product size, product color, product purchase quantity, and whether it is in stock.

Take out the shopping cart in Redis: buyerCart = cartService.selectBuyerCartFromRedis(username);

1     //取出Redis中购物车
2     public BuyerCart selectBuyerCartFromRedis(String username){
3         BuyerCart buyerCart = new BuyerCart();
4         //获取所有商品, redis中保存的是skuId 为key , amount 为value的Map集合
5         Map<String, String> hgetAll = jedis.hgetAll("buyerCart:"+username);
6         Set<Entry<String, String>> entrySet = hgetAll.entrySet();
7         for (Entry<String, String> entry : entrySet) {
8             //entry.getKey(): skuId
9             Sku sku = new Sku();
10             sku.setId(Long.parseLong(entry.getKey()));
11             BuyerItem buyerItem = new BuyerItem();
12             buyerItem.setSku(sku);
13             //entry.getValue(): amount
14             buyerItem.setAmount(Integer.parseInt(entry.getValue()));
15             //添加到购物车中
16             buyerCart.addItem(buyerItem);
17         }
18         
19         return buyerCart;
20     }

Fill the shopping cart, just put the skuId into the shopping cart in front, and you need to find out the sku details here: List<BuyerItem> items = buyerCart.getItems();
buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId ()));

1 //向购物车中的购物项 添加相应的数据, 通过skuId 查询sku对象, 颜色对象, 商品对象
2     public Sku selectSkuById(Long skuId){
3         Sku sku = skuDao.selectByPrimaryKey(skuId);
4         //颜色
5         sku.setColor(colorDao.selectByPrimaryKey(sku.getColorId()));
6         //添加商品信息
7         sku.setProduct(productDao.selectByPrimaryKey(sku.getProductId()));
8         return sku;
9     }

Then return to "cart.jsp", which is the shopping cart details display page.

3. Go to the checkout page

At this point, it means that the user must be logged in, and there must be products in the shopping cart.

So here you need to use the filtering function of springmvc. When users click settlement, they must log in first. If they do not log in, they will be prompted to log in.

1 //去结算
2     @RequestMapping(value="/buyer/trueBuy")
3     public String trueBuy(String[] skuIds, Model model, HttpServletRequest request, HttpServletResponse response){
4         //1, 购物车必须有商品, 
5         //取出用户名  再取出购物车
6         String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));
7         //取出所有购物车
8         BuyerCart buyerCart = cartService.selectBuyerCartFromRedisBySkuIds(skuIds, username);
9         List<BuyerItem> items = buyerCart.getItems();
10         if (items.size() > 0) {
11             //购物车中有商品
12             //判断所勾选的商品是否都有货, 如果有一件无货, 那么就刷新页面.
13             Boolean flag = true;
14             //2, 购物车中商品必须有库存 且购买大于库存数量时视为无货. 提示: 购物车原页面不动. 有货改为无货, 加红提醒.
15             for (BuyerItem buyerItem : items) {
16                 //装满购物车的购物项, 当前购物项只有skuId这一个东西, 我们还需要购物项的数量去判断是否有货
17                 buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId()));
18                 //校验库存
19                 if (buyerItem.getAmount() > buyerItem.getSku().getStock()) {
20                     //无货
21                     buyerItem.setIsHave(false);
22                     flag = false;
23                 }
24                 if (!flag) {
25                     //无货, 原页面不动, 有货改成无货, 刷新页面.
26                     model.addAttribute("buyerCart", buyerCart);
27                     return "cart";
28                 }
29             }
30         }else {
31             //购物车没有商品
32             //没有商品: 1>原购物车页面刷新(购物车页面提示没有商品)
33             return "redirect:/shopping/toCart";
34         }
35         
36         
37         //3, 正常进入下一个页面
38         return "order";
39     }

Take out the specified shopping cart, because we will check the products we need to buy on the shopping cart details page before settlement, so here is the settlement based on the checked products.
BuyerCart buyerCart = cartService.selectBuyerCartFromRedisBySkuIds(skuIds, username);

Remove the specified item from the shopping cart:

1 //从购物车中取出指定商品
2     public BuyerCart selectBuyerCartFromRedisBySkuIds(String[] skuIds, String username){
3         BuyerCart buyerCart = new BuyerCart();
4         //获取所有商品, redis中保存的是skuId 为key , amount 为value的Map集合
5         Map<String, String> hgetAll = jedis.hgetAll("buyerCart:"+username);
6         if (null != hgetAll && hgetAll.size() > 0) {
7             Set<Entry<String, String>> entrySet = hgetAll.entrySet();
8             for (Entry<String, String> entry : entrySet) {
9                 for (String skuId : skuIds) {
10                     if (skuId.equals(entry.getKey())) {
11                         //entry.getKey(): skuId
12                         Sku sku = new Sku();
13                         sku.setId(Long.parseLong(entry.getKey()));
14                         BuyerItem buyerItem = new BuyerItem();
15                         buyerItem.setSku(sku);
16                         //entry.getValue(): amount
17                         buyerItem.setAmount(Integer.parseInt(entry.getValue()));
18                         //添加到购物车中
19                         buyerCart.addItem(buyerItem);
20                     }
21                 }
22             }
23         }
24         
25         return buyerCart;
26     }

1) When only one of the products we purchased is out of stock, then refresh the shopping cart details page and echo the out-of-stock product status. 

2) When the shopping cart is filled at noon, refresh the current page.

There are only so many things in the shopping cart. There may be some places that cannot be explained or wrong. You are welcome to point them out. If it is helpful to you, please like and support, thank you~

I have a WeChat public account, and I often share some dry goods related to Java technology; if you like my sharing, you can use WeChat to search for "Java Head" or "javatuanzhang" to follow.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324939197&siteId=291194637