電商網站中添加商品到購物車功能模塊2017.12.8

前言:java

電商網站中添加商品到購物車功能模塊實現:數據庫

根據前一篇博客的介紹,咱們看到淘寶網站爲了保證購物車數據的同步,直接是強制用戶必須登陸才能夠將商品加入購物車。而京東網站是用戶在未登陸的狀態下也能夠將商品加入到購物車,此時這個是保存在了cookie中,而後用戶登陸後,根據商品的id判斷商品是否存在,將兩個購物車的商品合併,造成最終的購物車商品。json

本篇文章分兩個模塊,分別看下這兩個功能是如何實現的:瀏覽器

一、必須在用戶登陸的前提下,才能夠將商品加入到購物車列表cookie

咱們今天先看下淘寶網站的狀態下的添加商品到購物車的功能實現:app

邏輯分析:dom

入參:productId,count(商品id,商品數量)iphone

出參:ide

{
    "status": 0,
    "data": {
        "cartProductVoList": [
            {
                "id": 1,
                "userId": 13,
                "productId": 1,
                "quantity": 12,
                "productName": "iphone7",
                "productSubtitle": "雙十一促銷",
                "productMainImage": "mainimage.jpg",
                "productPrice": 7199.22,
                "productStatus": 1,
                "productTotalPrice": 86390.64,
                "productStock": 86,
                "productChecked": 1,
                "limitQuantity": "LIMIT_NUM_SUCCESS"
            },
            {
                "id": 2,
                "userId": 13,
                "productId": 2,
                "quantity": 1,
                "productName": "oppo R8",
                "productSubtitle": "oppo促銷進行中",
                "productMainImage": "mainimage.jpg",
                "productPrice": 2999.11,
                "productStatus": 1,
                "productTotalPrice": 2999.11,
                "productStock": 86,
                "productChecked": 1,
                "limitQuantity": "LIMIT_NUM_SUCCESS"
            }
        ],
        "allChecked": true,
        "cartTotalPrice": 89389.75
    }
}
fail

{
    "status": 10,
    "msg": "用戶未登陸,請登陸"
}

  根據接口文檔,咱們看到返回只是這樣子的。工具

此時咱們封裝兩個vo對象,一個是裝載購物車商品數據的list,一個是大的購物車列表

這個很簡單,直接根據接口文檔來作便可。

第一個商品購物車數據vo對象:

package com.imooc.project.cartVo;

import java.math.BigDecimal;

/**
 * 購物車商品的vo類,用於展現在前臺信息
 */
public class CartProductVoList {
    private Integer id;
    private Integer userId;
    private Integer productId;
    private Integer quantity;
    private String productName;
    private String productSubtitle;
    private String productMainImage;
    private BigDecimal productPrice;
    private Integer productStatus;
    private BigDecimal productTotalPrice;
    private Integer productStock;
    private Integer productChecked;//是否被選中
    private String limitQuantity;
    //判斷加入購物車的商品是否超過庫存中商品的數量會返回這樣的標識"limitQuantity"
    //失敗的:LIMIT_NUM_FAIL 成功的:LIMIT_NUM_SUCCESS

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getProductId() {
        return productId;
    }

    public void setProductId(Integer productId) {
        this.productId = productId;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getProductSubtitle() {
        return productSubtitle;
    }

    public void setProductSubtitle(String productSubtitle) {
        this.productSubtitle = productSubtitle;
    }

    public String getProductMainImage() {
        return productMainImage;
    }

    public void setProductMainImage(String productMainImage) {
        this.productMainImage = productMainImage;
    }

    public BigDecimal getProductPrice() {
        return productPrice;
    }

    public void setProductPrice(BigDecimal productPrice) {
        this.productPrice = productPrice;
    }

    public Integer getProductStatus() {
        return productStatus;
    }

    public void setProductStatus(Integer productStatus) {
        this.productStatus = productStatus;
    }

    public BigDecimal getProductTotalPrice() {
        return productTotalPrice;
    }

    public void setProductTotalPrice(BigDecimal productTotalPrice) {
        this.productTotalPrice = productTotalPrice;
    }

    public Integer getProductStock() {
        return productStock;
    }

    public void setProductStock(Integer productStock) {
        this.productStock = productStock;
    }

    public Integer getProductChecked() {
        return productChecked;
    }

    public void setProductChecked(Integer productChecked) {
        this.productChecked = productChecked;
    }

    public String getLimitQuantity() {
        return limitQuantity;
    }

    public void setLimitQuantity(String limitQuantity) {
        this.limitQuantity = limitQuantity;
    }
}

  第二個:大的購物車列表

package com.imooc.project.cartVo;

import java.math.BigDecimal;
import java.util.List;

/**
 * 一個大的vo對象,用於裝載購物車功能模塊的顯示信息
 */
public class CartVo {

    private List<CartProductVoList>  cartProductVoLists;
    private boolean allChecked;
    private BigDecimal cartTotalPrice;

    public List<CartProductVoList> getCartProductVoLists() {
        return cartProductVoLists;
    }

    public void setCartProductVoLists(List<CartProductVoList> cartProductVoLists) {
        this.cartProductVoLists = cartProductVoLists;
    }

    public boolean isAllChecked() {
        return allChecked;
    }

    public void setAllChecked(boolean allChecked) {
        this.allChecked = allChecked;
    }

    public BigDecimal getCartTotalPrice() {
        return cartTotalPrice;
    }

    public void setCartTotalPrice(BigDecimal cartTotalPrice) {
        this.cartTotalPrice = cartTotalPrice;
    }
}

  作完了兩個vo對象後,咱們來看下下一步的操做:

一、首先判斷用戶是否存在,若是不存在則提示用戶必須登陸
二、若是用戶存在,則進行下一步的操做
三、判斷該用戶的購物車中是否存在該商品,若是存在則更新購物車中該商品的數量
若是不存在,則將此商品加入到購物車列表中,寫入數據庫。
四、展現給用戶的購物車列表是改用戶下全部加入到購物車的商品列表:
根據userid查詢該用戶的購物車列表,遍歷列表,將對象封裝到咱們寫好的vo類中。
展現給前臺用戶。
五、注意問題:
    (1)商品的價格計算問題,防止精度丟失。
     (2)加入購物車是商品數量與該商品的總庫存量問題。
     (3)該商品的總價以及購物車中商品的總價計算問題
     (4)購物車中商品是否選中的問題,默認咱們認爲若是全選則返回true,若是不是則返回false。

 下面是咱們看下代碼實現:依次是controller,service

 

 service:

public class CartServiceImpl implements ICartService {
    @Autowired
    private mmall_cartMapper cartMapper;
    @Autowired
    private mmall_productMapper productMapper;
    //購物車功能流程:
    //當用戶未登陸的狀態下,加入購物車,此時商品是保存在cookie中的,用戶換臺電腦購物車就失效。當用戶結算的時候須要用戶的登陸,這一塊的處理也是計算價格庫存
    // 在用戶登陸的前提下,查詢用戶購物車中是否有該商品,若是沒有,則將商品添加到購物車中(這中間牽扯到庫存和商品價格的處理,該商品的總結,該用戶購物車最終的總價),若是有該商品,則增長商品的數量,更新用戶的購物車,計算價格
    //這種狀況是淘寶網站使用的,只有用戶的登陸的狀態下商品才能夠加入購物車,保證了數據的同步
    @Override
    public ServerResponse<CartVo> addProductCart(Integer userId,Integer productId,Integer count) {
        if (productId == null || count == null) {
            return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
        }
        //查詢該用戶的購物車中是否有該商品
        mmall_cart cart = cartMapper.selectProductExit(userId, productId);
        mmall_product product = productMapper.selectByPrimaryKey(productId);
        if (cart == null) {
            //若是購物車爲空,則購物車沒有此商品,須要插入到購物車
            mmall_cart cartItem = new mmall_cart();
            cartItem.setProductId(productId);
            cartItem.setUserId(userId);
            cartItem.setQuantity(count);
            cartItem.setChecked(Const.CartProperty.CARTCHECKED);
            int i = cartMapper.insertSelective(cartItem);
        } else {
            //若是購物車不爲空,則已有此商品,須要更新購物車商品的數量
            int stock = product.getStock();
                cart.setQuantity(cart.getQuantity() + count);
                cartMapper.updateByPrimaryKeySelective(cart);
        }
          return this.list(userId);
    }


    public ServerResponse<CartVo> list (Integer userId){
        CartVo cartVo = this.getCartVoLimit(userId);
        return ServerResponse.createBySuccess(cartVo);
    }
    private CartVo getCartVoLimit(Integer userId){
        //封裝vo展現給前臺,查詢用戶購物車中的商品展現
        CartVo cartVo=new CartVo();
        BigDecimal cartTotalPrice=new BigDecimal("0");
        List<mmall_cart> cartList=cartMapper.selectProductByUserId(userId);
        List<CartProductVoList> list= Lists.newArrayList();
        if (!CollectionUtils.isEmpty(cartList)) {
            for (mmall_cart cartItem : cartList) {
                //根據購物車的商品id來查詢該商品的信息

                //開始封裝這個包裝顯示類
                CartProductVoList cartProductVoList = new CartProductVoList();
                cartProductVoList.setId(cartItem.getId());
                cartProductVoList.setUserId(userId);
                cartProductVoList.setProductId(cartItem.getProductId());
                mmall_product productItem = productMapper.selectByPrimaryKey(cartItem.getProductId());
                if (productItem!=null){
                    cartProductVoList.setProductMainImage(productItem.getMainImage());
                    cartProductVoList.setProductName(productItem.getName());
                    cartProductVoList.setProductStatus(productItem.getStatus());
                    cartProductVoList.setProductStock(productItem.getStock());
                    cartProductVoList.setProductSubtitle(productItem.getSubtitle());
                    cartProductVoList.setProductPrice(productItem.getPrice());
                    //商品庫存限制這個功能
                    int buyLimitCount = 0;
                    if (cartItem.getQuantity()<= productItem.getStock()) {
                        buyLimitCount=cartItem.getQuantity();
                        cartProductVoList.setLimitQuantity(Const.CartProperty.LIMIT_NUM_SUCCESS);
                    } else {
                        //這一步須要注意,當庫存不足時,須要更新購物車庫存
                        buyLimitCount = productItem.getStock();
                        cartProductVoList.setLimitQuantity(Const.CartProperty.LIMIT_NUM_FAIL);
                        //購物車中更新有效庫存,
                        mmall_cart cartForQuantity = new mmall_cart();
                        cartForQuantity.setId(cartItem.getId());
                        cartForQuantity.setQuantity(buyLimitCount);
                        cartMapper.updateByPrimaryKeySelective(cartForQuantity);
                    }
                    cartProductVoList.setQuantity(buyLimitCount);
                    //購物車總價格的問題:一個是該產品的總價,一個是購物車中最後的商品總價
                    //這個是該商品的總價格:商品價格*商品的數量
                    cartProductVoList.setProductTotalPrice(BigDecimalUtil.mul(productItem.getPrice().doubleValue(),cartItem.getQuantity().doubleValue()));
                    cartProductVoList.setProductChecked(cartItem.getChecked());
                }
                //這裏的總價格默認爲購物車商品所有選中的狀態下計算的價格
                if (cartItem.getChecked()==Const.CartProperty.CARTCHECKED){
                    cartTotalPrice=BigDecimalUtil.add(cartTotalPrice.doubleValue(),cartProductVoList.getProductTotalPrice().doubleValue());
                }
                list.add(cartProductVoList);
            }
        }
        cartVo.setCartProductVoLists(list);
        cartVo.setAllChecked(this.getAllCheckedStatus(userId));//若是全選則返回true,非全選則返回false
        cartVo.setCartTotalPrice(cartTotalPrice);
        return cartVo;
    }
    private boolean getAllCheckedStatus(Integer userId){
        if(userId == null){
            return false;
        }
        //查詢購物車中該用戶下選中的狀態,checked=0,即未被選中狀態,若是返回0,則代表購物車中所有選中的狀態,返回true
        return cartMapper.selectCartProductCheckedStatusByUserId(userId) == 0;

    }

  上面就是這塊的功能實現。

二、用戶在未登陸狀態下將商品加入到購物車:

其實原理差很少的,只是咱們在處理cookie的時候,使用的cookie工具類,而後將cookie中的json格式數據轉爲list,此時咱們須要使用到阿里巴巴的fastjson這個工具包。

直接上代碼吧:

//用戶未登陸的狀況下購物車保存到cookie中,京東網站使用的方法
    @Override
    public ServerResponse addProductCookie(HttpServletRequest request, HttpServletResponse response, Integer productId, Integer count) {
         /*添加購物車商品,首先購物車商品是保存在cookie中的,由於咱們只要不付款是沒有什麼做用的。
         * 如何從cookie中讀取購物車列表呢,是利用request來實現的。
         * 第一步:首先判斷cookie中是否存在該商品,若是存在,則商品數量加1,
         * 若是沒有則根據商品id從rest工程中獲取該商品,將商品寫入cookie。
         */
        CartItmCookieVo cartItmCookieVo=null;
        //從cookie中讀取商品
      List<CartItmCookieVo> cookieList=this.getProductByCookie(request);
      List<CartItmCookieVo> list=Lists.newArrayList();
      //遍歷這個列表,查詢購物車中是否存在此商品,若是存在則更新,若是不存在則寫入cookie中
        for (CartItmCookieVo cartItem: cookieList) {
            if (cartItem.getProductId()==productId){
                cartItem.setQuantity(cartItem.getQuantity()+count);
                cartItmCookieVo=cartItem;
                break;
            }else{
                cartItmCookieVo=new CartItmCookieVo();
               mmall_product product=productMapper.selectByPrimaryKey(productId);
                cartItmCookieVo.setId(cartItem.getId());
                cartItmCookieVo.setProductId(productId);
                cartItmCookieVo.setProductName(product.getName());
                if (product.getStock()>=cartItem.getQuantity()) {
                    cartItmCookieVo.setQuantity(cartItem.getQuantity());
                }else{
                    cartItmCookieVo.setQuantity(product.getStock());
                }
                cartItmCookieVo.setProductPrice(product.getPrice());
                cartItmCookieVo.setProductMainImage(product.getMainImage());
                list.add(cartItmCookieVo);
                CookieUtils.setCookie(request,response,"TT_CART",JSON.toJSONString(list),true);
            }
        }
        return ServerResponse.createBySuccess(list);
    }
//從cookie中讀取商品列表
    private List<CartItmCookieVo> getProductByCookie(HttpServletRequest request) {
       String cookie=CookieUtils.getCookieValue(request,"TT_CART",true);
       //由於cookie中存放的是json格式的數據,因此若是須要轉換成list形式
        if (cookie==null){
            return new ArrayList<>();
        }else{
            //這裏用到了使用阿里巴巴的fastjson將json轉爲list集合的形式
            List<CartItmCookieVo> cartcookieList = JSON.parseArray(cookie, CartItmCookieVo.class);
            return cartcookieList;
        }
    }

  cookieUtil的工具類:

這裏一點就是在使用工具類的時候咱們通常防止工具類實例化,此時的解決方案是使用私有構造器來解決:

package com.imooc.project.util;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/*
*Cookie 工具類
 */
public final class CookieUtils {  
  
    /** 
     * 獲得Cookie的值, 不編碼 
     *  
     * @param request 
     * @param cookieName 
     * @return 
     */  
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        return getCookieValue(request, cookieName, false);  
    }  
  
    /** 
     * 獲得Cookie的值, 
     *  
     * @param request 
     * @param cookieName 
     * @return 
     */  
    public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {  
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {  
            return null;  
        }  
        String retValue = null;  
        try {  
            for (int i = 0; i < cookieList.length; i++) {  
                if (cookieList[i].getName().equals(cookieName)) {  
                    if (isDecoder) {  
                        retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                    } else {  
                        retValue = cookieList[i].getValue();  
                    }  
                    break;  
                }  
            }  
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();  
        }  
        return retValue;  
    }  
  
    /** 
     * 獲得Cookie的值, 
     *  
     * @param request 
     * @param cookieName 
     * @return 
     */  
    public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {  
        Cookie[] cookieList = request.getCookies();  
        if (cookieList == null || cookieName == null) {  
            return null;  
        }  
        String retValue = null;  
        try {  
            for (int i = 0; i < cookieList.length; i++) {  
                if (cookieList[i].getName().equals(cookieName)) {  
                    retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);  
                    break;  
                }  
            }  
        } catch (UnsupportedEncodingException e) {  
             e.printStackTrace();  
        }  
        return retValue;  
    }  
  
    /** 
     * 設置Cookie的值 不設置生效時間默認瀏覽器關閉即失效,也不編碼 
     */  
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue) {
        setCookie(request, response, cookieName, cookieValue, -1);  
    }  
  
    /** 
     * 設置Cookie的值 在指定時間內生效,但不編碼 
     */  
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,  
            String cookieValue, int cookieMaxage) {  
        setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);  
    }  
  
    /** 
     * 設置Cookie的值 不設置生效時間,但編碼 
     */  
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,  
            String cookieValue, boolean isEncode) {  
        setCookie(request, response, cookieName, cookieValue, -1, isEncode);  
    }  
  
    /** 
     * 設置Cookie的值 在指定時間內生效, 編碼參數 
     */  
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,  
            String cookieValue, int cookieMaxage, boolean isEncode) {  
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);  
    }  
  
    /** 
     * 設置Cookie的值 在指定時間內生效, 編碼參數(指定編碼) 
     */  
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,  
            String cookieValue, int cookieMaxage, String encodeString) {  
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);  
    }  
  
    /** 
     * 刪除Cookie帶cookie域名 
     */  
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,  
            String cookieName) {  
        doSetCookie(request, response, cookieName, "", -1, false);  
    }  
  
    /** 
     * 設置Cookie的值,並使其在指定時間內生效 
     *  
     * @param cookieMaxage cookie生效的最大秒數 
     */  
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,  
            String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {  
        try {  
            if (cookieValue == null) {  
                cookieValue = "";  
            } else if (isEncode) {  
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }  
            Cookie cookie = new Cookie(cookieName, cookieValue);  
            if (cookieMaxage > 0)  
                cookie.setMaxAge(cookieMaxage);  
            if (null != request) {// 設置域名的cookie  
                String domainName = getDomainName(request);  
                System.out.println(domainName);  
                if (!"localhost".equals(domainName)) {  
                    //cookie.setDomain(domainName);  
                }  
            }  
            cookie.setPath("/");  
            response.addCookie(cookie);  
        } catch (Exception e) {  
             e.printStackTrace();  
        }  
    }  
  
    /** 
     * 設置Cookie的值,並使其在指定時間內生效 
     *  
     * @param cookieMaxage cookie生效的最大秒數 
     */  
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,  
            String cookieName, String cookieValue, int cookieMaxage, String encodeString) {  
        try {  
            if (cookieValue == null) {  
                cookieValue = "";  
            } else {  
                cookieValue = URLEncoder.encode(cookieValue, encodeString);  
            }  
            Cookie cookie = new Cookie(cookieName, cookieValue);  
            if (cookieMaxage > 0)  
                cookie.setMaxAge(cookieMaxage);  
            if (null != request) {// 設置域名的cookie  
                String domainName = getDomainName(request);  
                System.out.println(domainName);  
                if (!"localhost".equals(domainName)) {  
                    //本地測試的時候不要寫.實際發佈時在打開  
                    //cookie.setDomain(domainName);  
                }  
            }  
            cookie.setPath("/");  
            response.addCookie(cookie);  
        } catch (Exception e) {  
             e.printStackTrace();  
        }  
    }  
  
    /** 
     * 獲得cookie的域名 
     */  
    private static final String getDomainName(HttpServletRequest request) {  
        String domainName = null;  
  
        String serverName = request.getRequestURL().toString();  
        if (serverName == null || serverName.equals("")) {  
            domainName = "";  
        } else {  
            final int end = serverName.lastIndexOf("/");  
            serverName = serverName.substring(0, end);  
            final String[] domains = serverName.split("\\.");  
            int len = domains.length;  
            if (len > 3) {  
                // www.xxx.com.cn  
                domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];  
            } else if (len <= 3 && len > 1) {  
                // xxx.com or xxx.cn  
                domainName = "." + domains[len - 2] + "." + domains[len - 1];  
            } else {  
                domainName = serverName;  
            }  
        }  
  
        if (domainName != null && domainName.indexOf(":") > 0) {  
            String[] ary = domainName.split("\\:");  
            domainName = ary[0];  
        }  
        return domainName;  
    }  
  
}

  BigDecimal的工具類:

package com.imooc.project.util;

import java.math.BigDecimal;

/**
 *商業計算中,如何防止精度的丟失,使用Bigdecimal來解決這個問題
 * 因爲Java的簡單類型不可以精確的對浮點數進行運算,這個工具類提供精
 * 確的浮點數運算,包括加減乘除和四捨五入。
 */
public class BigDecimalUtil {
//工具類(utility class),實例化對它們沒有意義的工具類。這時候,就要作到不讓該類被實例化.方法是使用私有構造器
private BigDecimalUtil(){}
//私有構造器這樣子就代表這個工具類不能被實例化
// 重寫BigDecimal的加減乘除
    public static BigDecimal add(Double v1,Double v2){
    BigDecimal b1=new BigDecimal(Double.toString(v1));
    BigDecimal b2=new BigDecimal(Double.toString(v2));
    return b1.add(b2);
    }
    public static BigDecimal sub(Double v1,Double v2){
        BigDecimal b1=new BigDecimal(Double.toString(v1));
        BigDecimal b2=new BigDecimal(Double.toString(v2));
        return b1.subtract(b2);
    }
    public static BigDecimal mul(Double v1,Double v2){
        BigDecimal b1=new BigDecimal(Double.toString(v1));
        BigDecimal b2=new BigDecimal(Double.toString(v2));
        return b1.multiply(b2);
    }
    public static BigDecimal div(Double v1,Double v2){
        BigDecimal b1=new BigDecimal(Double.toString(v1));
        BigDecimal b2=new BigDecimal(Double.toString(v2));
        //<p>The new {@link #divide(BigDecimal, int, RoundingMode)}
        return b1.divide(b2,2,BigDecimal.ROUND_FLOOR);
    }
    //除法的時候須要注意除不盡的狀況,重寫BigDecimal的除法保留兩位小數的方法,四捨五入。

}

  以上就是兩種功能的實現,至於合併的話,可使用消息機制也可使用判斷商品id是否相同,而後合併該商品。

測試結果:

 

 ok,總結了下,也重溫了下購物車的功能模塊,一個好的功能模塊的設計要涉及到太多的用戶體驗問題了,咱們只能儘可能完善吧。

相關文章
相關標籤/搜索