1. 學習計劃 第十二天: 1、購物車實現 2、訂單確認頁面展示 2. 購物車的實現 2.1. 功能分析 1、購物車是一個獨立的表現層工程。 2、添加購物車不要求登錄。可以指定購買商品的數量。 3、展示購物車列表頁面 4、修改購物車商品數量 5、刪除購物車商品 2.2. 工程搭建 e3-cart-w ...
1. 學習計劃
第十二天:
1、購物車實現
2、訂單確認頁面展示
2. 購物車的實現
2.1. 功能分析
1、購物車是一個獨立的表現層工程。
2、添加購物車不要求登錄。可以指定購買商品的數量。
3、展示購物車列表頁面
4、修改購物車商品數量
5、刪除購物車商品
2.2. 工程搭建
e3-cart-web打包方式war
可以參考e3-portal-web
2.2.1. Pom文件
<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>cn.e3mall</groupId> <artifactId>e3-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>cn.e3mall</groupId> <artifactId>e3-cart-web</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>cn.e3mall</groupId> <artifactId>e3-manager-interface</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- 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> <!-- dubbo相關 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <!-- 排除依賴 --> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> <exclusion> <groupId>org.jboss.netty</groupId> <artifactId>netty</artifactId> </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> <!-- 配置tomcat插件 --> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>8089</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
3. 未登錄狀態下使用購物車
3.1. 添加購物車
3.1.1. 功能分析
在不登陸的情況下也可以添加購物車。把購物車信息寫入cookie。
優點:
1、不占用服務端存儲空間
2、用戶體驗好。
3、代碼實現簡單。
缺點:
1、cookie中保存的容量有限。最大4k
2、把購物車信息保存在cookie中,更換設備購物車信息不能同步。
改造商品詳情頁面
請求的url:/cart/add/{itemId}
參數:
1)商品id: Long itemId
2)商品數量: int num
業務邏輯:
1、從cookie中查詢商品列表。
2、判斷商品在商品列表中是否存在。
3、如果存在,商品數量相加。
4、不存在,根據商品id查詢商品信息。
5、把商品添加到購車列表。
6、把購車商品列表寫入cookie。
返回值:邏輯視圖
Cookie保存購物車
1)key:TT_CART
2)Value:購物車列表轉換成json數據。需要對數據進行編碼。
3)Cookie的有效期:保存7天。
商品列表:
List<TbItem>,每個商品數據使用TbItem保存。當根據商品id查詢商品信息後,取第一張圖片保存到image屬性中即可。
讀寫cookie可以使用CookieUtils工具類實現。
3.1.2. Controller
@Controller public class CartController { @Value("${TT_CART}") private String TT_CART; @Value("${CART_EXPIRE}") private Integer CART_EXPIRE; @Autowired private ItemService itemService; @RequestMapping("/cart/add/{itemId}") public String addCartItem(@PathVariable Long itemId, Integer num, HttpServletRequest request, HttpServletResponse response) { // 1、從cookie中查詢商品列表。 List<TbItem> cartList = getCartList(request); // 2、判斷商品在商品列表中是否存在。 boolean hasItem = false; for (TbItem tbItem : cartList) { //對象比較的是地址,應該是值的比較 if (tbItem.getId() == itemId.longValue()) { // 3、如果存在,商品數量相加。 tbItem.setNum(tbItem.getNum() + num); hasItem = true; break; } } if (!hasItem) { // 4、不存在,根據商品id查詢商品信息。 TbItem tbItem = itemService.getItemById(itemId); //取一張圖片 String image = tbItem.getImage(); if (StringUtils.isNoneBlank(image)) { String[] images = image.split(","); tbItem.setImage(images[0]); } //設置購買商品數量 tbItem.setNum(num); // 5、把商品添加到購車列表。 cartList.add(tbItem); } // 6、把購車商品列表寫入cookie。 CookieUtils.setCookie(request, response, TT_CART, JsonUtils.objectToJson(cartList), CART_EXPIRE, true); return "cartSuccess"; } /** * 從cookie中取購物車列表 * <p>Title: getCartList</p> * <p>Description: </p> * @param request * @return */ private List<TbItem> getCartList(HttpServletRequest request) { //取購物車列表 String json = CookieUtils.getCookieValue(request, TT_CART, true); //判斷json是否為null if (StringUtils.isNotBlank(json)) { //把json轉換成商品列表返回 List<TbItem> list = JsonUtils.jsonToList(json, TbItem.class); return list; } return new ArrayList<>(); } }
3.2. 展示購物車商品列表
請求的url:/cart/cart
參數:無
返回值:邏輯視圖
業務邏輯:
1、從cookie中取商品列表。
2、把商品列表傳遞給頁面。
3.2.1. Controller
@RequestMapping("/cart/cart") public String showCartList(HttpServletRequest request, Model model) { //取購物車商品列表 List<TbItem> cartList = getCartList(request); //傳遞給頁面 model.addAttribute("cartList", cartList); return "cart"; }
3.3. 修改購物車商品數量
3.3.1. 功能分析
1、在頁面中可以修改商品數量
2、重新計算小計和總計。
3、修改需要寫入cookie。
4、每次修改都需要向服務端發送一個ajax請求,在服務端修改cookie中的商品數量。
請求的url:/cart/update/num/{itemId}/{num}
參數:long itemId、int num
業務邏輯:
1、接收兩個參數
2、從cookie中取商品列表
3、遍歷商品列表找到對應商品
4、更新商品數量
5、把商品列表寫入cookie。
6、響應e3Result。Json數據。
返回值:
e3Result。Json數據
3.3.2. Controller
@RequestMapping("/cart/update/num/{itemId}/{num}") @ResponseBody public e3Result updateNum(@PathVariable Long itemId, @PathVariable Integer num, HttpServletRequest request, HttpServletResponse response) { // 1、接收兩個參數 // 2、從cookie中取商品列表 List<TbItem> cartList = getCartList(request); // 3、遍歷商品列表找到對應商品 for (TbItem tbItem : cartList) { if (tbItem.getId() == itemId.longValue()) { // 4、更新商品數量 tbItem.setNum(num); } } // 5、把商品列表寫入cookie。 CookieUtils.setCookie(request, response, TT_CART, JsonUtils.objectToJson(cartList), CART_EXPIRE, true); // 6、響應e3Result。Json數據。 return e3Result.ok(); }
3.3.3. 解決請求*.html尾碼無法返回json數據的問題
在springmvc中請求*.html不可以返回json數據。
修改web.xml,添加url攔截格式。
3.4. 刪除購物車商品
3.4.1. 功能分析
請求的url:/cart/delete/{itemId}
參數:商品id
返回值:展示購物車列表頁面。Url需要做redirect跳轉。
業務邏輯:
1、從url中取商品id
2、從cookie中取購物車商品列表
3、遍歷列表找到對應的商品
4、刪除商品。
5、把商品列表寫入cookie。
6、返回邏輯視圖:在邏輯視圖中做redirect跳轉。
3.4.2. Controller
@RequestMapping("/cart/delete/{itemId}") public String deleteCartItem(@PathVariable Long itemId, HttpServletRequest request, HttpServletResponse response) { // 1、從url中取商品id // 2、從cookie中取購物車商品列表 List<TbItem> cartList = getCartList(request); // 3、遍歷列表找到對應的商品 for (TbItem tbItem : cartList) { if (tbItem.getId() == itemId.longValue()) { // 4、刪除商品。 cartList.remove(tbItem); break; } } // 5、把商品列表寫入cookie。 CookieUtils.setCookie(request, response, TT_CART, JsonUtils.objectToJson(cartList), CART_EXPIRE, true); // 6、返回邏輯視圖:在邏輯視圖中做redirect跳轉。 return "redirect:/cart/cart.html"; }
4. 登錄狀態下的購物車處理
4.1. 功能分析
1、購物車數據保存的位置:
未登錄狀態下,把購物車數據保存到cookie中。
登錄狀態下,需要把購物車數據保存到服務端。需要永久保存,可以保存到資料庫中。可以把購物車數據保存到redis中。
2、redis使用的數據類型
a) 使用hash數據類型
b) Hash的key應該是用戶id。Hash中的field是商品id,value可以把商品信息轉換成json
3、添加購物車
登錄狀態下直接包商品數據保存到redis中。
未登錄狀態保存到cookie中。
4、如何判斷是否登錄?
a) 從cookie中取token
b) 取不到未登錄
c) 取到token,到redis中查詢token是否過期。
d) 如果過期,未登錄狀態
e) 沒過期登錄狀態。
4.2. 判斷用戶是否登錄
4.2.1. 功能分析
應該使用攔截器實現。
1、實現一個HandlerInterceptor介面。
2、在執行handler方法之前做業務處理
3、從cookie中取token。使用CookieUtils工具類實現。
4、沒有取到token,用戶未登錄。放行
5、取到token,調用sso系統的服務,根據token查詢用戶信息。
6、沒有返回用戶信息。登錄已經過期,未登錄,放行。
7、返回用戶信息。用戶是登錄狀態。可以把用戶對象保存到request中,在Controller中可以通過判斷request中是否包含用戶對象,確定是否為登錄狀態。
4.2.2. LoginInterceptor
/** * 判斷用戶是否登錄的攔截器 * <p>Title: LoginInterceptor</p> * <p>Description: </p> * <p>Company: www.itcast.cn</p> * @version 1.0 */ public class LoginInterceptor implements HandlerInterceptor { @Value("${COOKIE_TOKEN_KEY}") private String COOKIE_TOKEN_KEY; @Autowired private UserService userService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 執行handler方法之前執行此方法 // 1、實現一個HandlerInterceptor介面。 // 2、在執行handler方法之前做業務處理 // 3、從cookie中取token。使用CookieUtils工具類實現。 String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY); // 4、沒有取到token,用戶未登錄。放行 if (StringUtils.isBlank(token)) { return true; } // 5、取到token,調用sso系統的服務,根據token查詢用戶信息。 E3Result e3Result = userService.getUserByToken(token); // 6、沒有返回用戶信息。登錄已經過期,未登錄,放行。 if (e3Result.getStatus() != 200) { return true; } // 7、返回用戶信息。用戶是登錄狀態。可以把用戶對象保存到request中,在Controller中可以通過判斷request中是否包含用戶對象,確定是否為登錄狀態。 TbUser user = (TbUser) e3Result.getData(); request.setAttribute("user", user); //返回true放行 //返回false攔截 return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 執行handler方法之後,並且是返回ModelAndView對象之前 } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 返回ModelAndView之後。可以捕獲異常。 } }
4.2.3. Springmvc.xml配置攔截器
<!-- 攔截器配置 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="cn.e3mall.cart.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
4.3. 添加購物車
4.3.1. 功能分析
登錄狀態下添加購物車,直接把數據保存到redis中。需要調用購物車服務,使用redis的hash來保存數據。
Key:用戶id
Field:商品id
Value:商品對象轉換成json
參數:
1、用戶id
2、商品id
3、商品數量
業務邏輯:
1、根據商品id查詢商品信息
2、把商品信息保存到redis
a) 判斷購物車中是否有此商品
b) 如果有,數量相加
c) 如果沒有,根據商品id查詢商品信息。
d) 把商品信息添加到購物車
3、返回值。E3Result
4.3.2. dao層
根據商品id查詢商品信息,單表查詢。可以使用逆向工程。
4.3.3. Service層
@Service public class CartServiceImpl implements CartService { @Value("${CART_REDIS_KEY}") private String CART_REDIS_KEY; @Autowired private TbItemMapper itemMapper; @Autowired private JedisClient jedisClient; @Override public E3Result addCart(long userId, long itemId, int num) { // a)判斷購物車中是否有此商品 Boolean flag = jedisClient.hexists(CART_REDIS_KEY + ":" + userId, itemId + ""); // b)如果有,數量相加 if (flag) { //從hash中取商品數據 String json = jedisClient.hget(CART_REDIS_KEY + ":" + userId, itemId + ""); //轉換成java對象 TbItem tbItem = JsonUtils.jsonToPojo(json, TbItem.class); //數量相加 tbItem.setNum(tbItem.getNum() + num); //寫入hash jedisClient.hset(CART_REDIS_KEY + ":" + userId, itemId + "", JsonUtils.objectToJson(tbItem)); //返回添加成功 return E3Result.ok(); } // c)如果沒有,根據商品id查詢商品信息。 TbItem tbItem = itemMapper.selectByPrimaryKey(itemId); //設置商品數量 tbItem.setNum(num); String image = tbItem.getImage(); //取一張圖片 if (StringUtils.isNotBlank(image)) { tbItem.setImage(image.split(",")[0]); } // d)把商品信息添加到購物車 jedisClient.hset(CART_REDIS_KEY + ":" + userId, itemId + "", JsonUtils.objectToJson(tbItem)); return E3Result.ok(); } }
發佈服務:
4.3.4. Controller
@RequestMapping("/cart/add/{itemId}") public String addCart(@PathVariable Long itemId, Integer num, HttpServletRequest request, HttpServletResponse response) { //判斷用戶是否為登錄狀態 Object object = request.getAttribute("user"); if (object != null) { TbUser user = (TbUser) object; //取用戶id Long userId = user.getId(); //添加到服務端 E3Result e3Result = cartService.addCart(userId, itemId, num); return "cartSuccess"; } //如果登錄直接把購物車信息添加到服務端 //如果未登錄保存到cookie中 // 1、從cookie中取購物車列表。 List<TbItem> cartList = getItemListFromCookie(request); // 2、判斷商品列表是否存在此商品。 boolean falg = false; for (TbItem tbItem : cartList) { if (tbItem.getId() == itemId.longValue()) { //數量相加 // 4、如果存在,數量相加。 tbItem.setNum(tbItem.getNum() + num); falg = true; break; } } // 3、如果不存在添加到列表 if (!falg) { //根據商品id取商品信息 TbItem tbItem = itemService.getItemById(itemId); //設置數量 tbItem.setNum(num); String image = tbItem.getImage(); //取一張圖片 if (StringUtils.isNotBlank(image)) { tbItem.setImage(image.split(",")[0]); } //添加到列表 cartList.add(tbItem); } // 5、把購車列表寫入cookie CookieUtils.setCookie(request, response, COOKIE_CART_KEY, JsonUtils.objectToJson(cartList), COOKIE_CART_EXPIRE, true); // 6、返回邏輯視圖,提示添加成功 return "cartSuccess"; }
4.4. 登錄狀態下訪問購物車列表
4.4.1. 功能分析
1、未登錄狀態下購物車列表是從cookie中取。
2、登錄狀態下購物車應該是從服務端取。
3、如果cookie中有購物車數據,應該吧cookie中的購物車和服務端合併,合併後刪除cookie中的購物車數據。
4、合併購物車時,如果商品存在,數量相加,如果不存在,添加一個新的商品。
5、從服務端取購物車列表展示
4.4.2. Dao層
不需要訪問資料庫,只需要訪問redis。
4.4.3. Service層
1、合併購物車
參數:用戶id
List<TbItem>
返回值:E3Result
業務邏輯:
1)遍歷商品列表
2)如果服務端有相同商品,數量相加
3)如果沒有相同商品,添加一個新的商品
2、取購物車列表
參數:用戶id
返回值:List<TbItem>
業務邏輯:
1)從hash中取所有商品數據
2)返回
/** * 合併購物車 * <p>Title: mergeCart</p> * <p>Description: </p> * @param userId * @param itemList * @return * @see cn.e3mall.cart.service.CartService#mergeCart(long, java.util.List) */ @Override public