SSM商城專案(十二)
1. 學習計劃
1、購物車實現
2、未登入狀態下使用購物車
3、登入狀態下使用購物車
2. 購物車的實現
2.1. 功能分析
1、購物車是一個獨立的表現層工程。
2、新增購物車不要求登入。可以指定購買商品的數量。
3、展示購物車列表頁面
4、修改購物車商品數量
5、刪除購物車商品
2.2. 工程搭建
可以參考e3-content建立。
e3-car(聚合工程pom)
|--e3-car-interface(jar)
|--e3-car-Service(war)
e3-car-web(war)
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 CarController { @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, "car", 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, "car", 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、把商品列表傳遞給頁面。
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}") public E3Result updateNum(@PathVariable Long itemId,@PathVariable Integer num,HttpServletRequest request,HttpServletResponse response){ //從cookie中查詢商品列表。 List<TbItem> cartList = getCartList(request); for (TbItem tbItem : cartList) { if(tbItem.getId()==itemId.longValue()){ tbItem.setNum(num); } } CookieUtils.setCookie(request, response, "car", JsonUtils.objectToJson(cartList), CART_EXPIRE, true); return E3Result.ok(); }
3.3.3. 解決請求*.html字尾無法返回json資料的問題
在springmvc中請求*.html不可以返回json資料。
修改web.xml,新增url攔截格式。
</servlet-mapping> <servlet-mapping> <servlet-name>e3-car-web</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping>
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 deleteCarItem(@PathVariable Long itemId,HttpServletRequest request,HttpServletResponse response){ //從cookie中查詢商品列表。 List<TbItem> cartList = getCartList(request); for (TbItem tbItem : cartList) { if(tbItem.getId()==itemId.longValue()){ cartList.remove(tbItem); break; } } CookieUtils.setCookie(request, response, "car", JsonUtils.objectToJson(cartList), CART_EXPIRE, true); return "redirect:/cart/cart.html"; }
3.5. 小結
使用cookie實現購物車:
優點:
1、實現簡單
2、不需要佔用服務端儲存空間。
缺點:
1、儲存容量有限
2、更換裝置購車資訊不能同步。
實現購車商品資料同步:
1、要求使用者登入。
2、把購物車商品列表儲存到資料庫中。推薦使用redis。
3、Key:使用者id,value:購車商品列表。推薦使用hash,hash的field:商品id,value:商品資訊。
4、在使用者未登入情況下寫cookie。當用戶登入後,訪問購物車列表時,
a) 把cookie中的資料同步到redis。
b) 把cookie中的資料刪除
c) 展示購物車列表時以redis為準。
d) 如果redis中有資料cookie中也有資料,需要做資料合併。相同商品數量相加,不同商品新增一個新商品。
5、如果使用者登入狀態,展示購物車列表以redis為準。如果未登入,以cookie為準。
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. 攔截器
pom.xml
<dependency> <groupId>cn.e3mall</groupId> <artifactId>e3-sso-interface</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
引用服務
<dubbo:reference interface="cn.e3mall.sso.service.TokenService" id="tokenService" />
public class LoginInterceptor implements HandlerInterceptor{ @Autowired private TokenService tokenService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 前處理,執行handler之前執行此方法 //返回true,放行 false:攔截 //從cookie中取token String token=CookieUtils.getCookieValue(request, "token"); //如果沒有token,未登入,直接放行 if(StringUtils.isBlank(token)){ return true; } //取到token,需要呼叫sso的服務,根據token取使用者資訊 E3Result e3Result = tokenService.getUserByToken(token); //沒有取到,登入過期,放行。 if(e3Result.getStatus()!=200){ return true; } //取到使用者資訊,登入狀態。 TbUser user = (TbUser) e3Result.getData(); //把使用者資訊放到request中。只需要在Controller中判斷request中是否包含user資訊,放行 request.setAttribute("user", user); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub } }
4.2.3. 配置攔截器
<!-- 攔截器配置 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="cn.e3mall.car.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
4.3. 新增購物車
4.3.1. Service
@Service
public class CartServiceImpl implements CartService{ @Autowired private JedisClient jedisClient; @Autowired private TbItemMapper itemMapper; @Override public E3Result addCart(long userId, long itemId, int num) { //判斷商品是否存在 Boolean hexists = jedisClient.hexists("Cart:" + userId, itemId + ""); //如果存在數量相加 if (hexists) { String json = jedisClient.hget("Cart:" + userId, itemId + ""); //把json轉換成TbItem TbItem item = JsonUtils.jsonToPojo(json, TbItem.class); item.setNum(item.getNum() + num); //寫回redis jedisClient.hset("Cart:" + userId, itemId + "", JsonUtils.objectToJson(item)); return E3Result.ok(); } //如果不存在,根據商品id取商品資訊 TbItem item = itemMapper.selectByPrimaryKey(itemId); //設定購物車資料量 item.setNum(num); //取一張圖片 String image = item.getImage(); if (StringUtils.isNotBlank(image)) { item.setImage(image.split(",")[0]); } //新增到購物車列表 jedisClient.hset("Cart:" + userId, itemId + "", JsonUtils.objectToJson(item)); return E3Result.ok(); } }
釋出服務
<dubbo:service interface="cn.e3mall.car.service.CartService" ref="cartServiceImpl" timeout="600000"/>
4.3.2. Controller
引用服務
<dubbo:reference interface="cn.e3mall.car.service.CartService" id="cartService" />
4.4. 展示購物車
4.4.1. Service
@Override public E3Result mergeCart(long userId, List<TbItem> itemList) { //遍歷商品列表 //把列表新增到購物車 //判斷購物車是否有此商品 //如果有, 數量相加 //如果沒有,新增新的商品 for (TbItem tbItem : itemList) { addCart(userId, tbItem.getId(), tbItem.getNum()); } return E3Result.ok(); } //獲得購物車列表 @Override public List<TbItem> getCartList(long userId) { //根據使用者id查詢購物車列表 List<String> jsonList = jedisClient.hvals("Cart:" + userId); ArrayList<TbItem> itemList = new ArrayList<>(); for (String string : jsonList) { TbItem item = JsonUtils.jsonToPojo(string, TbItem.class); itemList.add(item); } return itemList; }
4.4.2. Controller
@RequestMapping("/cart/cart") public String showCartList(HttpServletRequest request, HttpServletResponse response,Model model) { //從cookie中取購物車列表 List<TbItem> cartList = getCartList(request); //判斷使用者是否為登入狀態 TbUser user = (TbUser) request.getAttribute("user"); //如果是登入狀態 if (user != null) { //從cookie中取購物車列表 //如果不為空,把cookie中的購物車商品和服務端的購物車商品合併。 cartService.mergeCart(user.getId(), cartList); //把cookie中的購物車刪除 CookieUtils.deleteCookie(request, response, "cart"); //從服務端取購物車列表 cartList = cartService.getCartList(user.getId()); } //傳遞給頁面 model.addAttribute("cartList", cartList); return "cart"; }
4.5. 更新購物車數量
4.5.1. Service
@Override public E3Result updateCart(long userId, long itemId, int num) { //從redis中取商品資訊 String json = jedisClient.hget("Cart:" + userId, itemId + ""); //更新商品數量 TbItem tbItem = JsonUtils.jsonToPojo(json, TbItem.class); tbItem.setNum(num); //寫入redis jedisClient.hset("Cart:" + userId, itemId + "",JsonUtils.objectToJson(tbItem)); return E3Result.ok(); }
4.5.2. Controller
4.6. 刪除購物車
4.6.1. Service
@Override public E3Result deleteCartItem(long userId, long itemId) { //刪除購物車商品 jedisClient.hdel("Cart:" + userId, itemId + ""); return E3Result.ok(); }