本文讲什么
可以看到,购物车这样一个功能模块,在各种购物类APP或者web应用中绝对是必不可少的东西.不论在大学中的课程设计,还是在实际的项目开发中,绝对非常重要且有些复杂的内容.
在实际操作中,身边有很多的小伙伴遇到编写购物车的代码的时候,有时候真的是一脸懵逼,总是搞不明白设计的思路,这就是本文写作的原因.
所以,本文适合搞不清楚购物车实现原理,知道原理但是实际编码不知道如何下手的小伙伴,我将给出一个思路以及实际的代码供大家参考.
在本文中,我将会用尽可能简单的句子,表达出我想表达的意思.废话不多说,开始我们的购物车实战!
购物车的几种实现方式
购物车的实现方式有很多,但是最常见的就三种:Cookie,Session,数据库.三种方法各有优劣,适合的场景各不相同.
- Cookie方法:通过把购物车中的商品数据写入Cookie中,再通过浏览器进行读取.这个方法,适合在用户没有登录的情况下使用,但是有个非常严重的缺点,即在用户禁用了Cookie的时候是无法使用的.
- Session方法:通过Session来保存商品信息,这确实是个好的方法,适合用户已经登录的情况,将数据放在Session中,用户就能读取购物车中的商品信息,而且速度十分的快.但是缺点也很明显,由于Session是建立在用户客户端和服务器之间的,在Session中保存数据,无疑会增加服务器的负担.
- 数据库(Redis):数据库无疑是一种非常棒的保存购物车中信息的有效途径,且能够持久化保存,但是问题也很明显,那就是读取速度会差强人意.
好了,下面来说一下几种实现方式的应用场景.
- 当用户没有登录的情况下,用户将商品加入购物车,此时的商品信息是写入了Cookie中,并且会设置一个保存时间,即使关闭浏览器过一段时间访问仍能看到购物车中的信息.(或者将购物数据写入Session中,但是关闭浏览器,购物车中的信息也就不见了)
- 用户登陆后,如果在Session中存储了商品信息且没有关闭浏览器(如果在Cookie中存储了商品信息且没有过期),将会读取其中的商品信息,并且将这些信息写入数据库中进行持久保存.
本文的行文方式说明
经过上面的讲解,我想你一定对购物车有所了解,为了使读者更加清晰的明白购物车的实现,我们省去了在未结算的状态下的持久化数据库.
也就是说,在文章中,我将使用Session来实现购物车,并且当用户没有登录的情况下,禁止用户将商品加入购物车.当然你不必为此担忧,即使我这样做,我的代码已经包括了整个购物操作的绝大多数步骤.请耐心向下看.
此外,本文使用SSM框架作为行文代码.
如果你是初学者也不必担心,我将为你提供一套项目的源代码,可以在我的GitHub中获取—->https://github.com/roobtyan/dinner” target=”_blank”>餐厅点餐系统,这套系统是基于servlet+jsp+mysql开发的,注释非常完善,当然最重要的模块也就是下单模块肯定是有的,而且非常完善,欢迎下载.
购物车模块的实现
数据库设计
- 用户表
字段 | 意义 |
---|---|
id | 用户id |
userName | 用户名 |
password | 用户密码 |
- 商品表
字段 | 意义 |
---|---|
id | 商品id |
commName | 商品名称 |
price | 商品价格 |
- 订单表
字段 | 意义 |
---|---|
id | 订单id |
commName | 商品名称 |
count | 商品数量 |
subtotal | 商品小计 |
total | 总价 |
用户登录
为了实现我上述的思路,肯定是要求用户先行登录.代码如下.
- LoginController
@Controller
public class LoginController {
private LoginService loginService;
private CommonService commonService;
@Autowired
public LoginController(LoginService loginService, CommonService commonService) {
this.loginService = loginService;
this.commonService = commonService;
}
@RequestMapping("/login")
public String login(User user, HttpSession session, Model model) {
//登录验证
if (loginService.isUser(user)) {
List<Common> commons = commonService.selectAllCommons();
model.addAttribute("commons", commons);
model.addAttribute("username", user.getUsername());
//把用户信息保存到session中
session.setAttribute("user", user);
return "shopping";
} else {
model.addAttribute("message", "用户名或密码错误");
return "index";
}
}
}
这是最常规的用户登录的代码,思路就是拿着用户名到数据库里面查询,如果能查到,则说明用户名无误,如果查不到则说明没有此用户,提示用户注册.如果用户名存在,再比对密码,一般密码不会像我们这样直接在数据库里面使用明文密码,都是会加盐的(MD5算法).如果比对密码的结果为true,则用户可以登录.比对过程isUser的代码如下.
@Service
public class LoginService {
private UserMapper userMapper;
@Autowired
public LoginService(UserMapper userMapper) {
this.userMapper = userMapper;
}
/**
* 判断用户名或密码是否正确
* @param user
* @return
*/
public boolean isUser(User user){
UserExample example = new UserExample();
UserExample.Criteria criteria = example.createCriteria();
criteria.andUsernameEqualTo(user.getUsername());
List<User> eqUser = userMapper.selectByExample(example);
//如果没有查询到user,则返回false
if (eqUser == null){
return false;
}
return eqUser.get(0).getPassword().equals(user.getPassword());
}
}
确认用户登录以后,需要把用户信息放在session中以便后续过程使用.
用户购物模块
当用户登录以后,展示在用户眼前的界面是这样的(页面模板来自菜鸟教程,经过改编):
左栏中的数据来自登录代码中的
List<Common> commons = commonService.selectAllCommons();
model.addAttribute("commons", commons);
当点击加入购物车以后,触发onlick事件:onclick="javascript:joinCart(${common.id})"
详细代码如下:
function joinCart(id) {
$.ajax({
url: "${pageContext.request.contextPath}/shop/joinCart",
data: "id=" + id,
type: "POST",
success: function (result) {
alert("加入购物车成功!");
//清空购物车列表
$("#shop_cart tbody").empty();
//动态构建购物车列表
var obj = result;
$.each(obj,function (index,item) {
var emptyTd = $("<td></td>").append("#");
var commnameTd = $("<td></td>").append(item.commname);
var countTd = $("<td></td>").append(item.count);
var subtotalTd = $("<td></td>").append(item.subtotal);
$("<tr></tr>")
.append(emptyTd)
.append(commnameTd)
.append(countTd)
.append(subtotalTd)
.appendTo("#shop_cart tbody");
//设置总金额
var totalSpan = document.getElementById("subtotal");
totalSpan.innerHTML = item.total;
});
}
})
}
可以看到,当触发这个方法时,实际上是使用了异步请求的方式向服务端发送数据,服务端做相应处理以后,封装购物车列表,然后把购物车商品列表以JSON格式传回,也就是封装在result中,利用js,动态构建购物车列表.于是就出现下面这种情况.
当将商品加入购物车以后:
首先提示用户已经加入购物车,然后在利用异步请求构建整个购物车,如果你对前端的了解并不是很深,不必担心,这部分内容实际上很简单,你可以随便百度一下这个知识点,记住就好了.实际上就是利用js操作json数据而已.
其实对于初学者来说,感觉上面的过程挺神奇的,其实不然.前端的代码看完了,我就来给你详细的解释一下后端的代码.
首先,可以将后段代码分成两部分,一部分是判断,各种判断,另外一部分是封装数据,相对简单.
//标识符:判断是否存在此商品
boolean flag = false;
//通过id查询商品信息
Common common = shopService.selectCommById(Integer.parseInt(id));
//从session中获取购物车信息
List<Order> shopcart = (List<Order>) session.getAttribute("shopcart");
//获取用户信息
User user = (User) session.getAttribute("user");
if (user == null) {
//如果用户为空,则直接返回,让用户登录
throw new RuntimeException("用户未登录");
}
//判断购物车列表是否为空
if (shopcart == null) {
//new 一个集合
shopcart = new ArrayList<>();
}
先判断用户是否已经登录,如果用户已经登录,则执行后续流程,如果用户没有登录,则直接抛出异常,交给异步请求的error处理,提示用户登录即可.
如果用户已经登录,则继续下面的步骤,判断购物车是否为空,为空也就是说明用户没有将任何商品加入购物车,则需要创建一个新的ArrayList集合,同时利用商品id查询出的商品信息,封装订单对象.
else {
for (Order order : shopcart) {
//判断是否存在此商品,存在则数量+1
if (order.getCommname().equals(common.getCommname())) {
flag = true;
order.setCount(order.getCount() + 1);
order.setSubtotal(order.getCount() * common.getPrice());
}
}
}
在遍历的过程中,如果遍历的数据的commName和查到的name相同的话,则证明是同一件商品,则将此商品的数量+1,然后再设置小计价格即可.同时将flag设置为true,表示遍历到了同样的数据.
如果没有遍历到名称相同的商品,则直接新建一个对象,封装数据,加入集合.
//如果购物车中没有当前商品的信息,则新增商品
if (!flag) {
Order order = new Order();
order.setCommname(common.getCommname());
order.setCount(1);
order.setSubtotal(common.getPrice());
//把商品加入集合
shopcart.add(order);
}
最后一步就是计算总价格,封装数据即可.
//计算总价格
Double total = 0d;
for (Order order : shopcart) {
total += order.getSubtotal();
}
for (Order order : shopcart) {
order.setTotal(total);
}
//设置session
session.setAttribute("shopcart", shopcart);
//返回list
return shopcart;
购物车全部代码如下:
Controller
@RequestMapping("/shop")
public class ShopController {
private ShopService shopService;
public ShopController(ShopService shopService) {
this.shopService = shopService;
}
@RequestMapping("/joinCart")
@ResponseBody
public List<Order> joinCart(String id, HttpSession session, Model model) {
//标识符:判断是否存在此商品
boolean flag = false;
//通过id查询商品信息
Common common = shopService.selectCommById(Integer.parseInt(id));
//从session中获取购物车信息
List<Order> shopcart = (List<Order>) session.getAttribute("shopcart");
//获取用户信息
User user = (User) session.getAttribute("user");
if (user == null) {
//如果用户为空,则直接返回,让用户登录
throw new RuntimeException("用户未登录");
}
//判断购物车列表是否为空
if (shopcart == null) {
//new 一个集合
shopcart = new ArrayList<>();
} else {
for (Order order : shopcart) {
//判断是否存在此商品,存在则数量+1
if (order.getCommname().equals(common.getCommname())) {
flag = true;
order.setCount(order.getCount() + 1);
order.setSubtotal(order.getCount() * common.getPrice());
}
}
}
//如果购物车中没有当前商品的信息,则新增商品
if (!flag) {
Order order = new Order();
order.setCommname(common.getCommname());
order.setCount(1);
order.setSubtotal(common.getPrice());
//把商品加入集合
shopcart.add(order);
}
//计算总价格
Double total = 0d;
for (Order order : shopcart) {
total += order.getSubtotal();
}
for (Order order : shopcart) {
order.setTotal(total);
}
//设置session
session.setAttribute("shopcart", shopcart);
//返回list
return shopcart;
}
}
关于操作购物车商品数量及结算
首先说操作购物车商品数量,既然我们能够按照通过id加商品的数量,肯定也是能够按照商品id减商品的数量,这部分无需多说,相信按照上面的代码,以你的聪明才智,肯定是能够做出来的.
至于结算操作,就更加单了.
用户点击结算按钮以后,跳转到后台,通过后台代码,先把session中保存的数据取出,然后遍历将数据写入数据库,进行持久化的操作即可.
以上.
获取文中项目代码:https://download.csdn.net/download/yanmiao0715/10570386
如果您的积分不够,欢迎关注我的微信公众号:最高权限比特流,回复”购物车源代码”进行下载.
结语
感谢您的阅读,如果您对文章有任何问题,欢迎留言,欢迎联系我:roobtyan@outlook.com.
也欢迎您关注我的微信公众号:最高权限比特流.
以及我的个人博客:www.roobtyan.cn