首页  /  预约专享  /  购物车的原理以及实现

购物车的原理以及实现

预约专享 1618

今天模拟京东的购物车实现原理完成了购物车模块的开发, 给大家分享下。

京东的购物车实现原理:在用户登录和不登录的状态下对购物车存入cookie还是持久化到redis中的实现。下面就来具体说次购物车的实现过程

两种情况:

用户登录,购物车存入redis中

用户未登录,购物车存入cookie中

比较两种方式的优缺点:

cookie:优点:数据保存在用户浏览器中,不占用服务端内存;用户体检效果好;代码实现简单

缺点:cookie的存储空间只有4k;更换设备时,购物车信息不能同步;cookie禁用,不提供保存

redis:优点:数据能够持久化;实现了购物车同步

缺点:增加了数据库的压力,速度慢

先介绍使用cookie存储购物车的实现思路

1、用户未登录状态下,用户添加购物车,首先从cookie中查询购物车中的商品列表

2、 判断cookie的商品列表中是否有要添加的商品信息

3、如果cookie中有该商品信息,将商品的数量相加

4、如果没有,根据商品的id值查询商品信息

5、将商品添加到购物车列表中

6、将购物车列表写入cookie中,设置cookie的过期时间

7、将cookie返回给客户端。

购物车的实现:

这里直接使用商品作为购物项对象,在页面中计算购物项的小计和购物车的总金额

代码语言:javascript复制package nyist.e3.pojo;

import java.io.Serializable;

import java.util.Date;

public class TbItem implements Serializable{

private Long id;

private String title;

private String sellPoint;

private Long price;

private Integer num;//作为购物项购买的商品数量

private String barcode;

private String image;//展示购物项中的图片

private Long cid;

private Byte status;

private Date created;

private Date updated;

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title == null ? null : title.trim();

}

public String getSellPoint() {

return sellPoint;

}

public void setSellPoint(String sellPoint) {

this.sellPoint = sellPoint == null ? null : sellPoint.trim();

}

public Long getPrice() {

return price;

}

public void setPrice(Long price) {

this.price = price;

}

public Integer getNum() {

return num;

}

public void setNum(Integer num) {

this.num = num;

}

public String getBarcode() {

return barcode;

}

public void setBarcode(String barcode) {

this.barcode = barcode == null ? null : barcode.trim();

}

public String getImage() {

return image;

}

public void setImage(String image) {

this.image = image == null ? null : image.trim();

}

public Long getCid() {

return cid;

}

public void setCid(Long cid) {

this.cid = cid;

}

public Byte getStatus() {

return status;

}

public void setStatus(Byte status) {

this.status = status;

}

public Date getCreated() {

return created;

}

public void setCreated(Date created) {

this.created = created;

}

public Date getUpdated() {

return updated;

}

public void setUpdated(Date updated) {

this.updated = updated;

}

}cookie中实现添加购物车的代码:

代码语言:javascript复制@Controller

public class ShopCartController {

@Autowired

private TbItemService tbItemService;

@Autowired

private ShopCartService shopCartService;

// 获取过期时间

@Value("${EXPIRE_KEY}")

private Integer EXPIRE_KEY;

@Value("${CART_COOKIE}")

private String CART_COOKIE;

/**

* 需求:将商品加入购物车中未登录状态下,将购物超过添加到cookie中

*

* 分析:1、从cookie中获取购物车信息

* 2、判断购物车中的商品,如果添加的商品存在,数量相加,不存在,根据商品id查询商品信息,添加到cookie中

* 3、将购物车列表信息写入cookie中

*

*

* @param itemId

* @param num

* @return

*/

@RequestMapping("/cart/add/{itemId}")

public String addCart(@PathVariable Long itemId, @RequestParam(defaultValue = "1") Integer num,

HttpServletRequest request, HttpServletResponse response) {

// 1.获得购物车列表

List itemList = getCartItemList(request);

// 用来判断商品是否存在的标志

boolean flag = false;

// 2、循环遍列表中的商品信息

for (TbItem tbItem : itemList) {

// 3、判断添加的商品是否存在

if (tbItem.getId() == itemId.longValue()) {

// 4、添加的商品在cookie中存在,将数量相加

tbItem.setNum(tbItem.getNum() + num);

// 重置标签

flag = true;

// 跳出循环

break;

}

}

if (!flag) {

// cookie中没有添加的商品信息

// 通过商品id查询商品信息

TbItem item = tbItemService.getItemById(itemId);

item.setNum(num);

if (StringUtils.isNotBlank(item.getImage())) {

// 取一张图片用于展示使用

item.setImage(item.getImage().split(",")[0]);

}

// 将商品添加购物车

itemList.add(item);

}

//将购物车写入cookie中

CookieUtils.setCookie(request, response, CART_COOKIE, JsonUtils.objectToJson(itemList),EXPIRE_KEY,true);

return "cartSuccess";

}

}cookie中查询购物车列表:

思路:

1、根据cookie的name值直接取出商品列表信息

2、将购物车列表添加到model中,返回逻辑视图

代码语言:javascript复制private List getCartItemList(HttpServletRequest request) {

// 使用utf-8,需要设置第三个参数为true

String json = CookieUtils.getCookieValue(request, CART_COOKIE, true);

if (StringUtils.isNotBlank(json)) {

// 返回cookie中取出的数据集合

return JsonUtils.jsonToList(json, TbItem.class);

}

// 返回空集合对象

return new ArrayList<>();

}

@RequestMapping("/cart/cart")

public String getCartList(HttpServletRequest request, HttpServletResponse response, Model model) {

// 从cookie中取出商品信息,

List itemList = getCartItemList(request);

// 将购物车信息返回给页面中

model.addAttribute("cartList", itemList);

// 跳转逻辑视图

return "cart";

}cookie中实现删除购物车中商品的功能:

1、接收页面传递的善品id值

2、从cookie中取出购物车列表,进行循环遍历,然后遍历的每一个商品信息和要删除的商品进行对比

3、如果存在就从购物车列表中将该商品移除

4、重新将购物车列表写入cookie中

5、将cookie信息响应给客户端

代码语言:javascript复制@RequestMapping("/cart/delete/{itemId}")

public String deleteCartItem(@PathVariable Long itemId, HttpServletRequest request, HttpServletResponse response) {

List list = getCartItemList(request);

for (TbItem tbItem : list) {

if (tbItem.getId() == itemId.longValue()) {

list.remove(tbItem);

break;

}

}

// 删除成功后,将购物车列表写入cookie中

CookieUtils.setCookie(request, response, CART_COOKIE, JsonUtils.objectToJson(list), EXPIRE_KEY, true);

// 删除成功后,重定向到购物车列表页面

return "redirect:/cart/cart.html";

}cookie购物车的添加,查询,删除已经实现实现,更改方法和删除方法实现过程基本一样

登录状态下redis购物车的实现

实现redis购物车添加功能

思路:

1、从request域中取出登录用户的信息

2、使用redis存储购物车列表 使用redis中的hash数据类型 hash的key 使用登录用户id值,field的key使用商品的id值,将商品的信息作为field的value值

3、完成cookie存储购物车列表的功能

实现的代码:

代码语言:javascript复制@Override

public E3Result addCart(Long userId, Long itemId, Integer num) {

try {

// 从redis中取出购物车,判断是否已经有购物项

Boolean hexists = jedisClient.hexists(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "");

if (hexists) {

// 表示购物车中已经有该商品,只需要将该商品的数量相加即可

String hget = jedisClient.hget(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "");

// 将数量相加

TbItem item = JsonUtils.jsonToPojo(hget, TbItem.class);

item.setNum(item.getNum() + num);

// 将商品重新放入购物车中

jedisClient.hset(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "", JsonUtils.objectToJson(item));

return E3Result.ok();

}

// 表示购物车中没有要添加的商品信息

// 根据商品的id查询商品的信息

TbItem item = itemMapper.selectByPrimaryKey(itemId);

item.setNum(num);

if (StringUtils.isNotBlank(item.getImage())) {

item.setImage(item.getImage().split(",")[0]);

}

// 将商品信息存入购物车中

jedisClient.hset(CART_REDIS_KEY_PRE + ":" + userId + "", itemId + "", JsonUtils.objectToJson(item));

return E3Result.ok();

} catch (Exception e) {

e.printStackTrace();

}

return E3Result.build(400, "商品添加购物车失败");

}展示登录状态下的购物车列表:需要将cookie中的购物车和redis中的购物车整合

1、从cookie中取出购物车列表对象

2、从redis中取出购物车对象

3、将cookie中的购物车列表和redis中的购物车列表整合(取出cookie中的购物车列表,然后添加到redis购物车中即可)

5、最终展示的结果以redis中的购物车为主

代码语言:javascript复制/**

* cookie中的购物车和redis中的购物车进行整合

*/

@Override

public E3Result mergeCart(Long userId, List itemList) {

for (TbItem tbItem : itemList) {

// 只需要调用登录状态下添加购物车业务处理逻辑即可

addCart(userId, tbItem.getId(), tbItem.getNum());

}

return E3Result.ok();

}redis购物车中删除购物项

将用户的id值和商品的id值分别作为hahs的key和field的key,调用redis中的hdel(String key,String...field)即可完成删除功能

代码语言:javascript复制/**

* 删除购物车

*

* @return

*

*/

@Override

public E3Result deleteCartItem(Long id, Long itemId) {

Long hdel = jedisClient.hdel(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "");

System.out.println("删除购物车购物项为"+hdel);

return E3Result.ok();

}redis购物车中更新购买商品的数量

代码语言:javascript复制 /**

* 更新购物车中商品的数量

*/

@Override

public E3Result updateRedisNum(Long id, Long itemId, Integer num) {

// 取出需要更改数量的商品信息

String hget = jedisClient.hget(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "");

// 将取出的json数据转换为商品对象,然后更新数量

TbItem item = JsonUtils.jsonToPojo(hget, TbItem.class);

item.setNum(num);

// 更新成功后,将数据写到redis购物车中

jedisClient.hset(CART_REDIS_KEY_PRE + ":" + id + "", itemId + "", JsonUtils.objectToJson(item));

return E3Result.ok();

}当用户点击去结算时:跳转到订单确认页面

1、生成订单详情

2、配送地址信息

3、选择支付方式

在确认订单之前, 应该判断用户是否是登录装态,可以使用拦截器实现

1、自定义拦截器实现HandlerInteceptor接口

2、从cookie中去token消息(登录认证的令牌)

3、判断token的值是否为空,如果为空,就跳转到用户登录页面完成登录,同时需要将当前地址栏的url作为参数传递(在登录的业务逻辑中,接收该url,完成登录后,跳转会该页面)

4、如果token不为空,根据token查询用户信息,然后将用户信息写入request域中,拦截器执行放行操作

5、此时获取到的购物车列表是从redis中读出的和cookie整合的最新的购物车。

拦截器的实现过程:

代码语言:javascript复制public class LoginInterceptor implements HandlerInterceptor {

@Value("${TT_TOKEN}")

private String TT_TOKEN;

@Value("${SSO_LOGIN_URL}")

private String SSO_LOGIN_URL;

@Autowired

private UserService userService;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

//执行Handler之前执行此方法

// a)从cookie中取token。

String token = CookieUtils.getCookieValue(request, TT_TOKEN);

if (StringUtils.isBlank(token)) {

//取当前请求的url

String url = request.getRequestURL().toString();

// b)没有token,需要跳转到登录页面。

response.sendRedirect(SSO_LOGIN_URL + "?redirectUrl=" + url);

//拦截

return false;

}

// c)有token。调用sso系统的服务,根据token查询用户信息。

e3Result result = userService.getUserByToken(token);

if (result.getStatus() != 200) {

// d)如果查不到用户信息。用户登录已经过期。需要跳转到登录页面。

//取当前请求的url

String url = request.getRequestURL().toString();

// b)没有token,需要跳转到登录页面。

response.sendRedirect(SSO_LOGIN_URL + "?redirectUrl=" + url);

//拦截

return false;

}

// e)查询到用户信息。放行。

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之后,执行。异常处理。

}

}

拦截器定义好之后,需要在springmvc中配置

代码语言:javascript复制

在登录页面接收url,实现sso系统的回调 接收的redirectUrl即为拦截中请求登录页面传递的参数

至此:购物车模块的功能基本实现,错误的地方希望大家多多指正。