day79_淘淘商城項目_12_購物車流程 + 商城購物車系統的搭建 + 商城購物車系統的實現分析(cookie+redis方案) + 購物車實現增刪改查_匠心筆記

課程計劃javascript

  • 一、購物車的流程
  • 二、購物車的實現
    • a) 登陸狀態下的購物車實現
    • b) 未登陸狀態下的購物車實現

一、購物車流程

1.一、之前的購物車流程

用戶將商品添加到購物車時,判斷用戶是否登陸,若是已經登陸將購物車放入session中。php


存在的問題:
  購物車使用了session,而session是存在於內存資源中,消耗了大量的內存資源。很是很差。
  購物車存在session當中,若是session銷燬(瀏覽器關閉),購物車就沒有了。
  session沒法共享,沒法進行水平擴展。
    解決方案:給購物車作持久化。
    持久化,須要用到數據庫(把數據存儲到服務端,較安全)。
    a、mysql數據庫(數據完整性比較好)
    b、redis數據庫(讀寫速度快)

  用戶未登陸的時候不能添加購物車。
    解決方案:未登陸的狀態下,能夠把購物車放在cookie中。
  在不登錄的狀況下添加購物車。把購物車信息寫入cookie。(數據保存在客戶端,數據完整性差)。
  優勢:
    一、不佔用服務端存儲空間。
    二、用戶體驗好。
    三、代碼實現簡單。
  缺點:
    一、cookie中保存的容量有限。最大4k
    二、把購物車信息保存在cookie中,更換設備購物車信息不能同步。
  這裏咱們不使用這種方法。css

1.二、如今的購物車流程


對於未登錄用戶,將購物車放到cookie中。對於已登錄用戶將購物車放入redis緩存中。能夠實現,用戶未登陸或者登陸情況下的添加購物車(並進行購物車的增刪查改)。

二、商城購物車系統的搭建

2.一、購物車系統的架構

購物車系統架構:
taotao-cart(pom聚合工程)
  |--taotao-cart-interface(jar)
  |--taotao-cart-Service(war)
taotao-cart-web(war)
能夠參考taotao-manager、taotao-manager-web建立。html

2.二、服務層工程搭建

2.2.一、taotao-cart

taotao-cart打包方式pom。
能夠參考taotao-manager工程的建立。
New --> Maven Project --> 不使用骨架建立工程 --> Next前端


pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
    http://maven.apache.org/xsd/maven-4.0.0.xsd"
>

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.taotao</groupId>
        <artifactId>taotao-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>taotao-cart</artifactId>
    <packaging>pom</packaging>
    <dependencies>
        <!-- 配置對taotao-common的依賴-->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- 配置Tomcat插件  -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8089</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.2.二、taotao-cart-interface

New --> Other--> Maven Module --> 不使用骨架建立
千萬不要建立成Maven Project。
taotao-cart-interface打包方式jar。
pom.xmljava

<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
    http://maven.apache.org/xsd/maven-4.0.0.xsd"
>

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.taotao</groupId>
        <artifactId>taotao-cart</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>taotao-cart-interface</artifactId>
    <dependencies>
        <!-- 配置對taotao-manager-pojo的依賴 -->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-manager-pojo</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

2.2.三、taotao-cart-service

New --> Other--> Maven Module --> 不使用骨架建立
千萬不要建立成Maven Project。
taotao-cart-service打包方式war。
pom.xmlmysql

<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
    http://maven.apache.org/xsd/maven-4.0.0.xsd"
>

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.taotao</groupId>
        <artifactId>taotao-cart</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>taotao-cart-service</artifactId>
    <packaging>war</packaging>
    <dependencies>
        <!-- 配置對taotao-manager-dao的依賴 -->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-manager-dao</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- 配置對taotao-cart-interface的依賴:服務層發佈服務要經過該接口 -->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-cart-interface</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- 配置對spring的依賴 -->
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- 配置對dubbo的依賴 -->
        <!-- dubbo相關 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <!-- 排除對低版本jar包的依賴 -->
            <exclusions>
                <exclusion>
                    <artifactId>spring</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>netty</artifactId>
                    <groupId>org.jboss.netty</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
        </dependency>
        <!-- 配置對Redis的Java客戶端jedis的依賴 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
    </dependencies>
</project>

因爲沒有web.xml,打包方式爲war的項目會報錯。
右鍵項目taotao-cart-service --> Java EE Tools --> Generate … xxx,會自動幫咱們生成web.xml,
在web.xml中配置spring的監聽器,內容以下:
web.xmlgit

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

    id="WebApp_ID" version="2.5">

    <display-name>taotao-cart-service</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <!-- 初始化spring容器:也即加載spring容器 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

2.2.四、框架整合

將taotao-manager-service的src/main/resources下的配置文件所有複製到taotao-cart-service的src/main/resources中github


刪除或清空與數據庫相關的配置文件,由於購物車主要使用redis緩存,因此須要將redis的工具類添加進來:

修改applicationContext-service.xml中spring註解包掃描的類與dubbo暴露服務的端口:

在taotao-cart-interface工程的src/main/java中建立com.taotao.cart.service包;
在taotao-cart-service工程的src/main/java中建立com.taotao.cart.service.impl包。

2.三、表現層工程搭建

2.3.一、taotao-cart-web

表現層爲一個單獨的工程,因此須要建立Maven Project。
New --> Maven Project
千萬不要建立成Maven Module。
taotao-cart-web的打包方式是war。web


pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
    http://maven.apache.org/xsd/maven-4.0.0.xsd"
>

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.taotao</groupId>
        <artifactId>taotao-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>taotao-cart-web</artifactId>
    <packaging>war</packaging>
    <dependencies>
        <!-- 配置對taotao-common的依賴 -->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- 配置對taotao-manager-interface的依賴:表現層調用服務要經過該接口 -->
        <dependency>
            <groupId>com.taotao</groupId>
            <artifactId>taotao-manager-interface</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- 配置對spring的依賴 -->
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- JSP相關 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- 文件上傳組件 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
        </dependency>
        <!-- 配置對分頁插件pagehelper的依賴 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
        </dependency>
        <!-- 配置對dubbo的依賴 -->
        <!-- dubbo相關 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <!-- 排除對低版本jar包的依賴 -->
            <exclusions>
                <exclusion>
                    <artifactId>spring</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>netty</artifactId>
                    <groupId>org.jboss.netty</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- 配置Tomcat插件  -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8090</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

    version="2.5">

    <display-name>taotao-cart-web</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <!-- 配置解決post亂碼的過濾器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 配置springmvc的前端控制器 -->
    <servlet>
        <servlet-name>taotao-cart-web</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- contextConfigLocation不是必須的, 若是不配置contextConfigLocation, springmvc的配置文件默認在:WEB-INF/servlet的name+"-servlet.xml" -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>taotao-cart-web</servlet-name>
        <!-- 攔截以(*.html)請求,jsp除外 -->
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
</web-app>

2.3.二、框架整合

將表現層taotao-manager-web工程下的src/main/resources下面的配置文件複製到taotao-cart-web下的src/main/resources中
刪除或清空相關的配置文件:


修改springmvc.xml中controller註解掃描的位置:

在taotao-manager-web工程下建立com.taotao.cart.controller包。

三、商城購物車系統的實現分析

3.一、購物車如何表示?

因爲購物車中的商品屬性與TbItem相似,因此能夠將TbItem當作購買的商品。TbItem中的num屬性原用做後臺管理系統的商品庫存數量,在這裏能夠將num屬性用做購買商品的數量,因此List<TbItem>就是一個購物車。一個用戶對應一個購物車,一個購物車對應多個商品。

3.二、怎麼將購物車存入cookie中?

對於存入cookie中,咱們能夠自定義key,而後將購物車List<TbItem>轉爲json串,存到cookie。

3.三、使用哪一種redis數據結構存放購物車?

因爲一個用戶對應一個購物車,一個購物車購買多個商品。至關於一個用戶能夠購買多個商品。
對於存入redis中,咱們有兩種實現方案:
1.方案一:
  使用String類型,將用戶userId做爲key,將購物List<TbItem>轉爲json串做爲value


2.方案二:
  使用hash類型,將 用戶userId做爲key,將 商品itemId做爲filed,將 購物車List<TbItem>轉爲json串做爲value

  推薦使用hash,能夠提升修改的效率(性能的提高),若是是String存儲大數據量的時候速度較慢。redis是單線程。
分析須要存儲的商品的信息的屬性有:
  商品的ID、商品的名稱、商品的價格、購買商品的數量、商品的圖片(取一張便可)。

3.四、建立POJO

  能夠直接使用TbItem。

四、商城購物車系統實現的準備工做

因爲購物車系統的運行,依賴不少其餘系統,爲了防止登陸註冊等其餘狀況,url錯誤報404致使很差測試,須要修改整個淘淘商城系統中的url爲正確的url。

4.一、修改全部登陸註冊的url


Ctrl+H,搜索全部的:http://localhost:8084/page,將8084改成8088

搜索結果以下:

4.二、修改全部回顯用戶名的url

將全部的$.cookie()取值的name改成本身定義的值。
將ajax跨域請求的url改成本身的。

4.三、修改全部安全退出的url

Ctrl+H,搜索全部的:<a href=\"http://www.taotao.com/user/logout.html\" class=\"link-logout\">[退出]</a>
改成:<a href=\"http://localhost:8088/user/logout.html\" class=\"link-logout\">[退出]</a>

五、實現登陸與未登陸狀態下添加商品到購物車

5.一、導入靜態資源

將參考資料中的購物車靜態頁面下的js、css、images導入webapp下,將jsp導入WEB-INF下:

5.二、修改商品詳情頁

商品詳情在taotao-item-web系統的item.jsp中,給加入購物車添加一個事件javascript:addCartItem();


在前端點擊添加購物車,就會觸發addCartItem函數,跳轉url:cart/add/itemId.html?num=123,咱們須要接收itemId與num,同步到redis或者cookie中。
添加購物車功能的分析:
  url:/cart/add/{itemId}?num=2
  參數:商品的id以及num
  另外:用戶的id(由於要保證redis中存儲數據的正確性,必需要求要存放於哪個用戶的購物車數據)
  返回值:邏輯視圖,cartSuccess.jsp 。

5.三、登陸狀態下添加購物車

5.3.一、Dao層

  直接經過JedisClient對象操做redis數據庫。

5.3.二、Service層

業務邏輯:
  一、根據商品的ID查詢商品的信息。
  二、設置商品的數量和圖片(只須要設置圖片的一張便可,數量使用num字段來表示,由於原來的num存放的是庫存,因此須要從新設置一下)。
  三、根據商品id和用戶id從redis數據庫中查詢用戶的購物車的商品的列表,看是否存在(須要將JSON轉成java對象)。
  四、若是沒有存在,則直接添加到redis中(須要將java對象轉成JSON)。
  五、若是存在,則更新商品購買的數量便可。
首先在taotao-cart-interface下建立接口包com.taotao.cart.service,在接口包下建立接口CartService.java。

/**
 * 購物車管理接口
 * @author chenmingjun
 * @date 2018年12月5日 下午11:21:43
 * @version V1.0
 */

public interface CartService {

    /**
     * 添加商品至redis購物車或者cookie購物車
     * @param userId 購物車所屬的用戶
     * @param item 添加的商品
     * @param num 添加商品的數量
     * @return
     */

    TaotaoResult addItemCart(Long userId, TbItem tbItem, Integer num);

    /**
     * 根據用戶ID和商品的ID查詢購物車是否存儲在redis中
     * @param userId
     * @param itemId
     * @return null 說明購物車不存在,若是不爲空說明購物車存在
     */

    TbItem queryTbItemByUserIdAndItemId(Long userId, Long itemId);
}

使用hash類型,能夠給key設置一個前綴用於分類。在taotao-cart-service的src/main/resources下建立resource.properties文件,文件內容以下:

#購物車的前綴
TT_CART_REDIS_PRE_KEY=TT_CART_REDIS_PRE_KEY

在taotao-cart-service下建立實現類包com.taotao.cart.service.impl,在實現類包下建立CartServiceImpl實現CartService:

/**
 * 購物車管理Service
 * @author chenmingjun
 * @date 2018年12月5日 下午11:23:46
 * @version V1.0
 */

@Service
public class CartServiceImpl implements CartService {

    // 注入jedisClient對象
    @Autowired
    private JedisClient jedisClient;

    // 獲取redis中購物車的前綴
    @Value("${TT_CART_REDIS_PRE_KEY}")
    private String TT_CART_REDIS_PRE_KEY;

    @Override
    public TaotaoResult addItemCart(Long userId, TbItem tbItem, Integer num) {
        // 一、從redis數據庫中查詢該用戶的購物車中的某一個商品
        TbItem tbItem2 = queryTbItemByUserIdAndItemId(userId, tbItem.getId());
        // 利用了redis中hash類型賦值的特性:當字段不存在時賦值
        // 二、判斷要添加的商品是否存在於列表中
        if (tbItem2 != null) {
            // 三、說明存在,則將商品的數量取出後相加再設置回去(將修改過的java對象轉爲JSON)
            tbItem2.setNum(tbItem2.getNum() + num);
            // 存入redis
            jedisClient.hset(TT_CART_REDIS_PRE_KEY + ":" + userId, tbItem.getId() + "", JsonUtils.objectToJson(tbItem2));
        } else {
            // 四、說明不存在,則設置商品數量,並設置照片(只須要設置圖片的一張便可)
            tbItem.setNum(num);
       String image = tbItem.getImage();
            if (StringUtils.isNotBlank(image)) {
                tbItem.setImage(image.split(",")[0]);
            }
            // 存入redis
            jedisClient.hset(TT_CART_REDIS_PRE_KEY + ":" + userId, tbItem.getId() + "", JsonUtils.objectToJson(tbItem));
        }
        return TaotaoResult.ok();
    }

    @Override
    public TbItem queryTbItemByUserIdAndItemId(Long userId, Long itemId) {
        // 經過用戶id和商品的id查詢redis數據庫所對應的商品的數據,若是存在則不爲空
        String string = jedisClient.hget(TT_CART_REDIS_PRE_KEY + ":" + userId, itemId + "");
        if (StringUtils.isNotBlank(string)) {
            // 將JSON轉成java對象
            TbItem tbItem = JsonUtils.jsonToPojo(string, TbItem.class);
            return tbItem;
        } else {
            return null;
        }
    }
}

5.3.三、發佈服務

先在taotao-cart-service工程中的pom.xml文件中配置對taotao-cart-interface的依賴,由於服務層發佈服務要經過該接口,
再在taotao-cart-service工程中的applicationContext-service.xml文件中發佈服務:要注意圖中標註的地方

5.3.四、引用服務

先在taotao-cart-web工程中的pom.xml文件中配置對taotao-cart-interface的依賴,由於表現層引用服務要經過該接口,
再在taotao-cart-web工程中的springmvc.xml文件中引用服務:要注意圖中標註的地方

5.3.五、Controller

url: /cart/add/{itemId}
參數:itemId, num
返回值:添加購物車成功頁面。
業務邏輯:
  一、調用SSO的服務,獲取用戶相關的信息。
  二、調用商品(manage)的服務,獲取商品的相關的信息。
  三、判斷若是是登陸的狀態,則調用登陸的添加購物車的service。
  四、若是是未登陸的狀態,則調用的是未登陸的添加購物車的方法。

一、因爲要表現層taotao-cart-web要調用taotao-sso與taotao-manager的服務,因此須要在該工程的pom.xml中添加依賴:

    <!-- 配置對taotao-cart-interface的依賴:表現層調用服務要經過該接口 -->
    <dependency>
        <groupId>com.taotao</groupId>
        <artifactId>taotao-cart-interface</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <!-- 配置對taotao-manager-interface的依賴:表現層調用服務要經過該接口 -->
    <dependency>
        <groupId>com.taotao</groupId>
        <artifactId>taotao-manager-interface</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <!-- 配置對taotao-sso-interface的依賴:表現層調用服務要經過該接口 -->
    <dependency>
        <groupId>com.taotao</groupId>
        <artifactId>taotao-sso-interface</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

二、因爲要將購物車存入cookie中,因此須要給購物車存入cookie時設置一個name,獲取token時正好也須要一個name,能夠將這兩個name放在resource.properties文件中,咱們還須要設置cookie中存放token的key的過時時間,代碼截圖以下:


代碼以下:
#cookie中存放token的key
COOKIE_TOKEN_KEY=COOKIE_TOKEN_KEY

#cookie中存放購物車的key
COOKIE_CART_KEY=COOKIE_CART_KEY

#cookie中存放token的key的過時時間,7天
COOKIE_CART_EXPIRE_TIME=604800

在taotao-cart-web下建立controller的包com.taotao.cart.controller,在包controller中建立CartController.java:

@Controller
public class CartController {
    // 引入服務 ,注入依賴 
    @Autowired
    private CartService cartService;

    @Autowired
    private ItemService itemService;

    @Autowired
    private UserLoginService userLoginService;

    // cookie中存放token的key
    @Value("${COOKIE_TOKEN_KEY}")
    private String COOKIE_TOKEN_KEY;

    // cookie中存放購物車的key
    @Value("${COOKIE_CART_KEY}")
    private String COOKIE_CART_KEY;

    @RequestMapping("/cart/add/{itemId}")   // /cart/add/149204693130763.html?num=4
    public String addItemCart(@PathVariable Long itemId, Integer num, HttpServletRequest request, HttpServletResponse response) {
        // 一、從cookie中獲取token
        String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);

        // 二、根據token調用SSO的服務,獲取用戶的信息
        TaotaoResult result = userLoginService.getUserByToken(token);

        // 三、判斷
        // 3.一、首先根據商品id調用商品(manager)服務的方法,獲取商品的數據TbItem
        TbItem tbItem = itemService.getItemById(itemId);
        if (result.getStatus() == 200) { // 若是用戶存在,說明已經登陸
            // 3.二、調用添加購物車的方法,將商品數據添加到redis中
            TbUser tbUser = (TbUser) result.getData();
            cartService.addItemCart(tbUser.getId(), tbItem, num);
        } else {
            // 四、判斷,若是用戶不存在,說明未登陸,將商品數據添加到cookie中,並設置過時時間
            // 4.一、首先根據cookie獲取購物車的列表
        }
        return "cartSuccess";
    }
}

5.3.六、訪問測試

  安裝taotao-cart。
  因爲要調用taotao-sso與taotao-manager查詢用戶與商品信息,因此須要啓動taotao-sso、taotao-manager。
  須要登陸在cookie中寫入toekn,因此要啓動taotao-sso-web。
  須要搜索商品,因此要啓動taotao-search、taotao-search-web。(若是手動輸入url進入商品詳情頁,能夠不啓動。)
  須要將商品詳情頁加入購物車,因此須要啓動taotao-item-web。
  最後購物車的taotao-cart、taotao-cart-web也要啓動。

5.四、未登陸狀態下添加購物車

5.4.一、服務層

  服務層不變,存入cookie,須要要使用servlet原生response對象,跟service沒什麼關係,因此放在controller中。

5.4.二、發佈服務

5.4.三、引用服務

5.4.五、表現層

在addItemCart判斷用戶沒登陸中添加以下,須要判斷cookie中是否已存在該商品,存在的話商品數量須要相加,不存在的話直接設置商品數量,還須要設置圖片爲第一張圖片,最後設置cookie存放時間爲一個星期(7243600)。


代碼以下:
@Controller
public class CartController {
    // 引入服務 ,注入依賴 
    @Autowired
    private CartService cartService;

    @Autowired
    private ItemService itemService;

    @Autowired
    private UserLoginService userLoginService;

    // cookie中存放token的key
    @Value("${COOKIE_TOKEN_KEY}")
    private String COOKIE_TOKEN_KEY;

    // cookie中存放購物車的key
    @Value("${COOKIE_CART_KEY}")
    private String COOKIE_CART_KEY;

    // cookie中存放token的key的過時時間
    @Value("${COOKIE_CART_EXPIRE_TIME}")
    private Integer COOKIE_CART_EXPIRE_TIME;

    @RequestMapping("/cart/add/{itemId}")   // /cart/add/149204693130763.html?num=4
    public String addItemCart(@PathVariable Long itemId, Integer num, HttpServletRequest request, HttpServletResponse response) {
        // 一、從cookie中獲取token
        String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
        // 二、根據token調用SSO的服務,獲取用戶的信息
        TaotaoResult result = userLoginService.getUserByToken(token);
        // 三、判斷
        // 3.一、首先根據商品id調用商品(manager)服務的方法,獲取商品的數據TbItem
        TbItem tbItem = itemService.getItemById(itemId);
        if (result.getStatus() == 200) { // 若是用戶存在,說明已經登陸
            // 3.二、調用添加購物車的方法,將商品數據添加到redis中
            TbUser tbUser = (TbUser) result.getData();
            cartService.addItemCart(tbUser.getId(), tbItem, num);
        } else {
            // 四、判斷,若是用戶不存在,說明未登陸,將商品數據添加到cookie中,並設置過時時間
            // 4.一、首先根據cookie獲取購物車的列表
            List<TbItem> cartList = getCartListFromCookie(request);

            boolean flag = false;
            for (TbItem tbItem2 : cartList) {
                // 4.二、說明購物車cookie上有該商品,就獲取購物車列表中的商品,更新商品數量
                if (tbItem2.getId() == itemId.longValue()) { // 兩個對象比的是內存地址值,longValue()取出的是基本類型的值
                    tbItem2.setNum(tbItem2.getNum() + num);
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                // 4.三、說明購物車cookie中沒有該商品,就設置該商品數量和圖片
                tbItem.setNum(num);
                if (tbItem.getImage() != null) {
                    tbItem.setImage(tbItem.getImage().split(",")[0]);
                }
                // 把商品添加至購物車列表
                cartList.add(tbItem);
            }
            // 五、將商品添加至購物車cookie,並設置過時時間
            CookieUtils.setCookie(request, response, COOKIE_CART_KEY, JsonUtils.objectToJson(cartList), COOKIE_CART_EXPIRE_TIME, true);
        }
        return "cartSuccess";
    }

    /**
     * 從cookie中獲取購物車列表
     * @param request
     * @return
     */

    private List<TbItem> getCartListFromCookie(HttpServletRequest request) {
        // 從cookie中獲取購物車商品列表
        String cartJson = CookieUtils.getCookieValue(request, COOKIE_CART_KEY, true);
        // 將cartJson轉換爲List<TbItem>
        if (StringUtils.isNotBlank(cartJson)) {
            List<TbItem> list = JsonUtils.jsonToList(cartJson, TbItem.class);
            return list;
        }
        return new ArrayList<>();
    }
}

注意:因爲從cookie中獲取購物車方法會常用,因此單獨抽取成一個私有方法。

5.4.三、訪問測試

  首先退出用戶登陸狀態,使用咱們已經作好的查看購物車列表的功能(在下節),查看cookie中是否存入了購物車,能夠看到未登陸的狀況下,添加購物車成功。

六、實現登陸與未登陸狀態下展現購物車列表

  在 5.三、登陸狀態下添加購物車5.四、未登陸狀態下添加購物車 準備工做已經作完的狀況下。
  若是用戶登陸狀態,展現購物車列表以redis爲準。若是未登陸,以cookie爲準。

6.一、功能分析

添加商品到購物車後,會提示【去購物車結算】,點擊【去購物車結算】,會跳轉到http://localhost:8090/cart/cart.html頁面,能夠看到購物車中商品列表。
在cart.jsp,咱們能夠看到須要準備一個cartList商品集合到model中。須要修改${cart.images[0]}${cart.image}


url: /cart/cart
參數:用戶id
返回值:購物車頁面,須要傳遞模型數據 List<Tbitem>

6.二、Dao層

  直接經過JedisClient對象操做redis數據庫。

6.三、Service層

在taotao-cart-interface建立接口

    /**
     * 根據用戶的ID查詢redis數據庫中用戶的購物車的商品列表
     * @param userId
     * @return
     */

    List<TbItem> queryCartListByUserId(Long userId);

在taotao-cart-service編寫實現類
業務邏輯:
  一、根據用戶的ID查詢redis中全部的field的值(map)。
  二、遍歷map對象,將其添加到List中。
  三、返回一個List<tbitem>

    @Override
    public List<TbItem> queryCartListByUserId(Long userId) {
        // 一、根據用戶的ID查詢redis中全部的field的值(map)
        Map<StringString> map = jedisClient.hgetAll(TT_CART_REDIS_PRE_KEY + ":" + userId);
        Set<Entry<StringString>> set = map.entrySet(); // map.entrySet() 是把Map類型的數據轉換成集合類型
        // Map.Entry是Map聲明的一個內部接口,此接口爲泛型,定義爲Entry<K,V>。它表示Map中的一個實體(一個key-value對)。接口中有getKey()、getValue()方法。
        // 二、遍歷map對象,將其添加到List中
        if (set != null) { // 判斷是否爲空
            List<TbItem> list = new ArrayList<>();
            // 迭代器只針對集合類型的數據,所以map類型的必須先轉換成集合類型才能使用迭代器去獲取元素
            for (Entry<StringString> entry : set) {
                TbItem tbItem = JsonUtils.jsonToPojo(entry.getValue(), TbItem.class);
                list.add(tbItem);
            }
            // 三、返回一個List<tbitem>
            return list;
        }
        return null;
    }

6.四、發佈服務與引用服務

  同上5.4.二、發佈服務5.4.三、引用服務,這裏再也不贅圖!

6.五、Controller

url: /cart/cart
參數:無
返回值:購物車展現列表的頁面
業務邏輯:
  一、根據token調用SSO的服務,獲取用戶的信息。
  二、判斷,若是有用戶的信息,說明用戶已登陸,調用CartService服務中查詢購物車的商品列表的方法。
  三、若是沒有用戶信息,說明用戶未登陸,調用從cookie中獲取購物車商品列表的方法。
  四、將購物車對象放入request域。
  五、返回邏輯頁面cart.jsp。


代碼以下:
    @RequestMapping("/cart/cart"// http://localhost:8090/cart/cart.html
    public String showCart(HttpServletRequest request) {
        // 一、從cookie中獲取token
        String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
        // 二、根據token調用SSO的服務,獲取用戶的信息
        TaotaoResult result = userLoginService.getUserByToken(token);
        // 三、判斷,若是用戶存在,說明已經登陸
        if (result.getStatus() == 200) {
            // 若是用戶已經登陸,從redis中查詢購物車列表
            TbUser tbUser = (TbUser) result.getData();
            List<TbItem> redisCartlist = cartService.queryCartListByUserId(tbUser.getId());
            request.setAttribute("cartList", redisCartlist);
        } else {
            // 若是用戶未登陸,從cookie中查詢購物車列表
            List<TbItem> cookieCartList = getCartListFromCookie(request);
            request.setAttribute("cartList", cookieCartList);
        }
        return "cart";
    }

6.六、訪問測試

  安裝taotao-cart。
  因爲要調用taotao-sso與taotao-manager查詢用戶與商品信息,因此須要啓動taotao-sso、taotao-manager。
  須要登陸在cookie中寫入toekn,因此要啓動taotao-sso-web。
  須要搜索商品,因此要啓動taotao-search、taotao-search-web。(若是手動輸入url進入商品詳情頁,能夠不啓動)
  須要將商品詳情頁加入購物車,因此須要啓動taotao-item-web。
  最後購物車的taotao-cart、taotao-cart-web也要啓動。

七、實現登陸與未登陸狀態下更新購物車的商品數量

7.一、更新購物車的商品數量的js分析

在taotao-cart-web的cart.js中有更新商品時js事件處理。
商品數量加1、減一時會觸發對應的事件,修改dom,從而修改前端展現的商品價格。
而後會異步請求url:/cart/update/num/" + _thisInput.attr("itemId") + "/" + _thisInput.val()
也就是url:/cart/update/num/itemId/num,修改服務端的數據。
refreshTotalPrice函數用於從新計算總價。


注意:咱們的請求是以 .action結尾的。爲何呢?
答:由於在springmvc.xml中攔截 *.html結尾的請求不能夠返回json數據。

7.二、Dao層

  直接經過JedisClient對象操做redis數據庫。

7.三、Service層

在taotao-cart-interface建立接口

    /**
     * 根據用戶ID和商品的ID更新redis購物車中商品的數量
     * @param userId
     * @param itemId
     * @param num
     * @return
     */

    TaotaoResult updateTbItemCartByUserIdAndItemId(Long userId, Long itemId, Integer num);

在taotao-cart-service建立實現類
業務邏輯:
從redis中獲取到對應的商品的對象,設置對象的商品數量,轉成JSON,存入redis中。

    @Override
    public TbItem queryTbItemByUserIdAndItemId(Long userId, Long itemId) {
        // 經過用戶id和商品的id查詢redis數據庫所對應的商品的數據,若是存在則不爲空
        String string = jedisClient.hget(TT_CART_REDIS_PRE_KEY + ":" + userId, itemId + "");
        if (StringUtils.isNotBlank(string)) {
            // 將JSON轉成java對象
            TbItem tbItem = JsonUtils.jsonToPojo(string, TbItem.class);
            return tbItem;
        } else {
            return null;
        }
    }

    @Override
    public TaotaoResult updateTbItemCartByUserIdAndItemId(Long userId, Long itemId, Integer num) {
        // 經過用戶id和商品的id查詢redis數據庫所對應的商品的數據,若是存在則不爲空
        TbItem tbItem = queryTbItemByUserIdAndItemId(userId, itemId);
        if (tbItem != null) {
            tbItem.setNum(num);
            jedisClient.hset(TT_CART_REDIS_PRE_KEY + ":" + userId, itemId + "", JsonUtils.objectToJson(tbItem));
        }
        return TaotaoResult.ok();
    }

7.四、發佈服務與引用服務

  同上5.4.二、發佈服務5.4.三、引用服務,這裏再也不贅圖!

7.五、Controller

url:/cart/update/num/{itemId}/{num}
參數:itemId、num
從cookie中獲取token,根據token查詢redis,判斷用戶是否登陸,已登陸更新購物車到redis中,未登陸更新到cookie中。
更新cookie中的購物車思路比較簡單:從cookie中獲取全部購物車,遍歷購物車找到對應商品更新數量,從新存入cookie便可。

    @RequestMapping("/cart/update/num/{itemId}/{num}")
    @ResponseBody // 響應json
    public TaotaoResult updateTbItemCartByUserIdAndItemId(@PathVariable Long itemId, @PathVariable Integer num, 
            HttpServletRequest request, HttpServletResponse response) {
        // 一、從cookie中獲取token
        String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
        // 二、根據token調用SSO的服務,獲取用戶的信息
        TaotaoResult result = userLoginService.getUserByToken(token);
        // 三、判斷用戶是否登陸
        if (result.getStatus() == 200) {
            // 用戶已登陸,在redis中更新數據
            TbUser tbUser = (TbUser) result.getData();
            cartService.updateTbItemCartByUserIdAndItemId(tbUser.getId(), itemId, num);
        } else {
            // 用戶沒有登陸,在cookie中更新數據
            updateCookieItemCart(itemId, num, request, response);
        }
        // 四、返回TaotaoResult.ok()
        return TaotaoResult.ok();
    }

    /**
     * 從cookie中獲取購物車列表的方法
     * @param request
     * @return
     */

    private List<TbItem> getCartListFromCookie(HttpServletRequest request) {
        // 從cookie中獲取購物車列表
        String cartJson = CookieUtils.getCookieValue(request, COOKIE_CART_KEY, true);
        // 將cartJson轉換爲List<TbItem>
        if (StringUtils.isNotBlank(cartJson)) {
            List<TbItem> list = JsonUtils.jsonToList(cartJson, TbItem.class);
            return list;
        }
        return new ArrayList<>();
    }

    /**
     * 更新購物車cookie中的商品數量的方法
     * @param itemId
     * @param num
     * @param request
     * @param response
     */

    private void updateCookieItemCart(Long itemId, Integer num, 
            HttpServletRequest request, HttpServletResponse response) {
        // 一、從cookie中獲取商品的列表
        List<TbItem> cartList = getCartListFromCookie(request);
        // 二、判斷,若是列表中有商品的id和傳遞過來的itemId一致 ,說明商品找到,更新商品的數量
        for (TbItem tbItem : cartList) {
            if (tbItem.getId() == itemId.longValue()) { // 兩個對象比的是內存地址值,longValue()取出的是基本類型的值
                tbItem.setNum(num); // 設置新的商品數量
                break;
            }
        }
        // 三、從新設置cookie,將商品的列表轉換成JSON設置回cookie中
        CookieUtils.setCookie(request, response, COOKIE_CART_KEY, JsonUtils.objectToJson(cartList), COOKIE_CART_EXPIRE_TIME, true);
        // 四、找不到什麼不處理
    }

7.六、訪問測試

解決請求*.html後綴沒法返回json格式的數據的問題:
問題緣由:由於在springmvc.xml中攔截*.html結尾的請求不能夠返回json數據。
解決方式:因爲咱們的請求是以.action結尾的,因此咱們修改web.xml,添加url攔截格式。

八、實現登陸與未登陸狀態下刪除購物車中的商品

8.一、功能分析

用戶點擊刪除,未登陸從cookie中刪除該商品、已登陸從redis中刪除該商品。
url:/cart/delete/${cart.id}.html
參數:cartid,實際上是就是itemId
根據商品id,從cookie或者redis中刪除商品。
返回值:展現購物車列表頁面。url須要作redirect跳轉(重定向)

8.二、Dao層

  直接經過JedisClient對象操做redis數據庫。

8.三、Service層

在taotao-cart-interface建立接口

    /**
     * 根據用戶ID和商品的ID刪除redis購物車中的商品
     * @param userId
     * @param itemId
     * @param num
     * @return
     */

    TaotaoResult deleteTbItemCartByUserIdAndItemId(Long userId, Long itemId);

在taotao-cart-service建立實現類
業務邏輯:
根據userid、itemid刪除redis中購物車列表的商品

    @Override
    public TaotaoResult deleteTbItemCartByUserIdAndItemId(Long userId, Long itemId) {
        jedisClient.hdel(TT_CART_REDIS_PRE_KEY + ":" + userId, itemId + "");
        return TaotaoResult.ok();
    }

8.四、發佈服務與引用服務

  同上5.4.二、發佈服務5.4.三、引用服務,這裏再也不贅圖!

8.五、Controller

url:/cart/delete/${cart.id}.html
參數:cartid,實際上是就是itemId
根據商品id,從cookie或者redis中刪除商品
返回值:展現購物車列表頁面。url須要作redirect跳轉到商品列表展現的controller。

    @RequestMapping("/cart/delete/{itemId}")
    public String deleteTbItemCartByUserIdAndItemId(@PathVariable Long itemId, 
            HttpServletRequest request, HttpServletResponse response) {
        // 一、從cookie中獲取token
        String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
        // 二、根據token調用SSO的服務,獲取用戶的信息
        TaotaoResult result = userLoginService.getUserByToken(token);
        if (result.getStatus() == 200) {
            // 三、若是用戶已經登陸,則刪除redis中對應商品
            TbUser tbUser = (TbUser) result.getData();
            cartService.deleteTbItemCartByUserIdAndItemId(tbUser.getId(), itemId);
        } else {
            // 4.若是用戶沒登陸,則刪除cookie中對應商品
            deleteCookieCartItem(itemId, request, response);
        }
        return "redirect:/cart/cart.html"// 重定向
    }

    /**
     * 從cookie中刪除購物車中的商品的方法
     * @param itemId
     * @param request
     * @param response
     */

    private void deleteCookieCartItem(Long itemId, 
            HttpServletRequest request, HttpServletResponse response) {
        // 一、 從cookie中獲取商品的列表
        List<TbItem> cartList = getCartListFromCookie(request);
        // 二、判斷是否商品在列表中,若是列表中有商品的id和傳遞過來的itemId一致 ,說明商品找到,刪除商品
        for (TbItem tbItem : cartList) {
            if (tbItem.getId() == itemId.longValue()) { // 兩個對象比的是內存地址值,longValue()取出的是基本類型的值
                // 刪除商品
                cartList.remove(tbItem); // 在循環中刪除,再遍歷的話會有問題,因此咱們刪除完後應當即跳出循環
                break;
            }
        }
        // 三、從新設置購物車列表到cookie中
        CookieUtils.setCookie(request, response, COOKIE_CART_KEY, JsonUtils.objectToJson(cartList), COOKIE_CART_EXPIRE_TIME, true);
        // 四、若是不在不處理
    }

8.六、訪問測試

  省略。。。

相關文章
相關標籤/搜索