需求分析
當我們點擊加入購物車的時候,將商品加入到我們的購物車,頁面傳遞到後臺的信息是商品的id和數量,我們根據id和數量將商品放進購物車,購物車數據存儲到cookie或者是服務器端
購物車列表在cookie的存儲指的是我們在不登錄的情況下也能將商品添加到購物車,這時購物車中的商品信息存儲在瀏覽器端的cookie,用戶清除瀏覽器緩存或者cookie過期後,購物車中就不存在商品信息
創建購物車的單個商家對象Cart,List Cart構成了購物車,Cart對象存儲的是像京東自營、xx專賣店所包含商品的數據
public class Cart implements Serializable {
private String sellerId;//商家id
private String sellerName;//商家名稱
private List<TbOrderItem> orderItemList;//購物車明細
public Cart() {
}
public Cart(String sellerId, String sellerName, List<TbOrderItem> orderItemList) {
this.sellerId = sellerId;
this.sellerName = sellerName;
this.orderItemList = orderItemList;
}
public String getSellerId() {
return sellerId;
}
public void setSellerId(String sellerId) {
this.sellerId = sellerId;
}
public String getSellerName() {
return sellerName;
}
public void setSellerName(String sellerName) {
this.sellerName = sellerName;
}
public List<TbOrderItem> getOrderItemList() {
return orderItemList;
}
public void setOrderItemList(List<TbOrderItem> orderItemList) {
this.orderItemList = orderItemList;
}
}
存儲實現
注意
Long類型數據的比較通過longValue()方法在進行比較,而long類型可以通過==比較
/*
添加商品到購物車
cartList 老的購物車
*/
@Override
public List<Cart> addCart(List<Cart> cartList, Long itemId, Integer num) {
TbItem item = itemMapper.selectByPrimaryKey(itemId);
//做安全校驗
if (item==null){
throw new RuntimeException("該商品不存在");
}
if (!item.getStatus().equals("1")){
throw new RuntimeException("該商品暫不支持添加購物車");
}
String sellerId = item.getSellerId();
Cart cart = searchItemInCartList(cartList, sellerId);
if (cart!=null){//購物車中已經存在該商家
List<TbOrderItem> orderItems = cart.getOrderItemList();//獲取用戶在這個商家中的商品明細
//判斷新商品在不在購物車的商品明細裏
TbOrderItem orderItem = searchItemInOrderOtemList(orderItems, itemId);
if (orderItem!=null){//如果該商品已經在購物車中,改變數量和總價
orderItem.setNum(orderItem.getNum()+num);
orderItem.setTotalFee(new BigDecimal(orderItem.getPrice().doubleValue()*orderItem.getNum()));
//移除商品數量減到0的商品明細
if (orderItem.getNum()<=0){
orderItems.remove(orderItem);
}
if (orderItems.size()==0){//如果商家在購物車列表的商品數量爲0,就移除該商家
cartList.remove(orderItems);
}
}
else{//如果該商品不在購物車中,添加該商品信息到新建的商品明細中
TbOrderItem newOrderItem = createOrderItem(item, num);
orderItems.add(newOrderItem);
}
}
else{//購物車中不存在該商家,創建該商家的購物明細
TbOrderItem orderItem = createOrderItem(item, num);
List<TbOrderItem> items = new ArrayList<>();//創建集合用來保存商品信息
items.add(orderItem);
Cart newCart = new Cart(item.getSellerId(),item.getSeller(),items);
cartList.add(newCart);
}
return cartList;
}
/*
檢測商家商品明細是否存在於購物車列表
*/
private Cart searchItemInCartList(List<Cart> cartList,String sellerId){
for (Cart cart : cartList) {
if (cart.getSellerId().equals(sellerId)) {//購物車中已經存在該商家
return cart;
}
}
return null;
}
/*
判斷商品是否存在於商家商品明細中
*/
private TbOrderItem searchItemInOrderOtemList(List<TbOrderItem> itemList,Long itemId){
for (TbOrderItem orderItem : itemList) {
if (orderItem.getItemId().longValue()==itemId.longValue()) {//如果該商品已經在購物車中
return orderItem;
}
}
return null;
}
/*
新建商品明細中的單個商品信息
*/
private TbOrderItem createOrderItem(TbItem item,Integer num){
TbOrderItem orderItem = new TbOrderItem();
orderItem.setGoodsId(item.getGoodsId());
orderItem.setItemId(item.getId());
orderItem.setTitle(item.getTitle());
orderItem.setNum(num);
orderItem.setPrice(item.getPrice());
orderItem.setTotalFee(new BigDecimal(orderItem.getPrice().doubleValue()*num));
orderItem.setPicPath(item.getImage());
return orderItem;
}
未登錄保存在cookie中
在後端對cookie進行操作
注意
複雜對象是不能進行編碼的,可以先轉換成字符串,字符串是可編碼的,這樣就可以避免中文亂碼
@Reference
private CartService cartService;
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
@RequestMapping("/add")
public Result addCart(Long itemId,Integer num){
try {
List<Cart> cartList = findCartList();
List<Cart> carts = cartService.addCart(cartList, itemId, num);
//存入cookie
//複雜對象不支持編碼,轉成字符串,字符串是可編碼的,防止對象中中文亂碼
String cartsJson = JSON.toJSONString(carts);
CookieUtil.setCookie(request,response,"cartList",cartsJson,3600*24,"utf-8");
return new Result(true,"加入購物車成功!");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"加入購物車失敗!");
}
}
CookieUtil是我們自己封裝的工具類
優化(登錄存儲在redis,未登錄保存在cookie)
判斷當前用戶是否登陸,如果未登錄採用Cookie存儲,如果登錄則採用Redis存儲。登錄後要進行Cookie購物車與Redis購物車的合併操作,並清除Cookie購物車。
修改spring-security.xml配置文件
之前我們是直接放行有關購物車操作的請求,這樣的話spring-security就不會驗證是否登錄,也自然就沒有登錄相關信息
現在我們需要,spring-security裏有登錄相關信息,不登錄也能執行有關購物車的操作,登錄也可以操作
解決辦法就是,修改攔截的權限
刪除<http pattern="/cart/*.do" security="none"></http>
access=“IS_AUTHENTICATED_ANONYMOUSLY” 用於設置資源可以在不登陸時訪問。
此配置與 security="none"的區別在於當用戶未登陸時獲取登陸人賬號的值爲anonymousUser,而security="none"的話,無論是否登陸都不能獲取登錄人賬號的值。
否則:"/cart/*.do" security="none"表示不攔截,繞過spring security,此時SecurityContextHolder.getContext()上下文爲空。
service層代碼同上,修改controller代碼如下
@Reference
private CartService cartService;
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping("/addGoodsToCart")
public Result addCart(Long itemId,Integer num){
try {
List<Cart> cartList = findCartList();
List<Cart> carts = cartService.addCart(cartList, itemId, num);
String username = SecurityContextHolder.getContext().getAuthentication().getName();
if ("anonymousUser".equals(username)){//沒有登錄,將信息存入cookie
//存入cookie
//複雜對象不支持編碼,轉成字符串,字符串是可編碼的,防止對象中中文亂碼
String cartsJson = JSON.toJSONString(carts);
CookieUtil.setCookie(request,response,"cartList",cartsJson,3600*24,"utf-8");
System.out.println(">>>>>save cartList to cookie" );
}
else{//已登錄,存入redis
redisTemplate.boundHashOps("cartList").put(username,carts);
System.out.println(">>>>>save cartList to Redis" );
}
return new Result(true,"加入購物車成功!");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"加入購物車失敗!");
}
}
/*
獲取到原來的購物車對象
*/
@RequestMapping("/findCartList")
public List<Cart> findCartList(){
String username = SecurityContextHolder.getContext().getAuthentication().getName();
if ("anonymousUser".equals(username)) {//沒有登錄,從cookie取出信息
String cartListStr = CookieUtil.getCookieValue(request, "cartList","utf-8");
if (cartListStr==null || cartListStr.equals("")){
cartListStr = "[]";
}
List<Cart> cartList = JSON.parseArray(cartListStr, Cart.class);
System.out.println(">>>>>get cartList from Cookie" );
return cartList;
}else{//已登錄,從redis中取出信息
List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(username);
if (cartList==null){
cartList = new ArrayList<>();
}
System.out.println(">>>>>get cartList from Redis" );
return cartList;
}
}
@RequestMapping("/test")
public List<Cart> testCartList(){
String cartListStr = CookieUtil.getCookieValue(request, "cartList","utf-8");
if (cartListStr==null || cartListStr.equals("")){
cartListStr = "[]";
}
List<Cart> cartList = JSON.parseArray(cartListStr, Cart.class);
return cartList;
}
這樣我們就做到了,未登錄時,購物車信息存儲在本地瀏覽器的cookie中,已登錄時購物車信息存儲在redis服務器中
購物車的合併(將cookie中的購物車信息同步到redis中)
service層新添合併方法,會調用之前的添加方法
/**
* 將cookieList合併到redisList
* @param redisList
* @param cookieList
* @return
*/
@Override
public List<Cart> joinCartList(List<Cart> redisList, List<Cart> cookieList) {
for (Cart cart : cookieList) {
List<TbOrderItem> orderItemList = cart.getOrderItemList();
for (TbOrderItem orderItem : orderItemList) {
addCart(redisList,orderItem.getItemId(),orderItem.getNum());
}
}
return redisList;
}
修改controller層的獲取方法
@RequestMapping("/findCartList")
public List<Cart> findCartList(){
String username = SecurityContextHolder.getContext().getAuthentication().getName();
// 從cookie中獲取購物車列表,如果考慮將cookie同步到redis的情況
// 那麼,不管登錄與否,都需要獲取本地cookie數據
String cartListStr = CookieUtil.getCookieValue(request, "cartList","utf-8");
if (cartListStr==null || cartListStr.equals("")){
cartListStr = "[]";
}
List<Cart> cookieList = JSON.parseArray(cartListStr, Cart.class);
//沒有登錄,從cookie取出信息
if ("anonymousUser".equals(username)) {
System.out.println(">>>>>get cartList from Cookie" );
return cookieList;
}
//已登錄
else{
List<Cart> redisList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(username);
if (redisList==null){
redisList = new ArrayList<>();
}
if (cookieList.size()>0){//如果cookie的購物車裏有數據就進行合併,沒有就直接返回redis中的數據
//合併購物車
redisList = cartService.joinCartList(redisList, cookieList);
// 將合併後的數據重新存回redis
redisTemplate.boundHashOps("cartList").put(username,redisList);
// 合併完成後,刪除本地的cookie
CookieUtil.deleteCookie(request, response, "cartList");
System.out.println(">>>>>import cookie to redis");
}
return redisList;
}
}