基于Redis的购物车实现
1 向购物车中添加商品
1.1服务端
1.1.1 公共类-处理结果类
/**
* 结果处理类
*/
@Getter
public class Result {
private Boolean success;
private Integer code;
private String message;
private Object data;
private Result(Boolean success, Integer code, String message, Object data) {
this.success = success;
this.code = code;
this.message = message;
this.data = data;
}
public static Result success() {
return new Result(true,200,null,null);
}
public static Result success(String message) {
return new Result(true,200,message,null);
}
public static Result success(String message,Object data) {
return new Result(true,200,message,data);
}
public static Result fail(Integer code,String message,Object data) {
return new Result(false,code,message,data);
}
public static Result fail(Integer code,String message) {
return new Result(false,code,message,null);
}
public static Result fail(Integer code) {
return new Result(false,code,null,null);
}
}
1.1.2 创建CarContoller
@RestController
@RequestMapping("/car")
public class CarController {
@Resource
private CarService carService;
@PostMapping("/addCar")
public Result addCar(@RequestBody Car car) {
try {
//设置当前登录的用户编号
car.setUser_id(93);
carService.addCar(car);
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.fail(500,"商品添加失败!!!");
}
}
}
1.1.3 创建CarService
-
CarService接口
public interface CarService { void addCar(Car car) throws Exception; }
- CarServcieImpl类
- 采用hash存储添加购物车信息
@Service public class CarServiceImpl implements CarService { private RedisTemplate redisTemplate; private HashOperations hashOperations; //构造器注入RedisTemplate,并且序列化 @Autowired public CarServiceImpl(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //获得Redis对象 hashOperations = redisTemplate.opsForHash(); } @Override public void addCar(Car car) throws Exception{ //向Redis中添加一个商品 Car[] cars ={car}; //向Redis中添加购物车信息 hashOperations.put("cars","user:"+car.getUser_id(),cars); } }
- CarServcieImpl类
1.1.4 购物车版本升级-添加多个商品-Service层修改对应方法
思路:
- 购物车是否存在该商品
- 若存在,给该商品数量+1,总价进行更新,存入carList集合中
- 若不存在,添加该商品,总价进行更新,存入carList集合中
- 购物车不存在该商品
- 将该商品直接存入carList集合中
- 将carList存入redis
@Override
public void addCar(Car car) throws Exception {
List<Car> carList;
//是否存在购物车
Boolean bool = redisTemplate.hasKey("cars");
if (bool) {//购物车存在
//获取redis中的数据
carList = (List<Car>) hashOperations.get("cars", "user:" + car.getUser_id());
Boolean exists = false;//redis是否存在新要添加的商品,默认不存在
for (Car car1 : carList) {
if (car1.getBook_id().equals(car.getBook_id())) {//判断redis是否存在新添加的商品
//如果存在数量则增加
car1.setCar_count(car1.getCar_count() + car.getCar_count());
//更新总价格
car1.setTotal(car1.getCar_count()*car1.getBook_price());
exists=true;
break;
}
}
if (!exists){//如果不存在商品
car.setTotal(car.getCar_count()*car.getBook_price());
//直接添加新商品
carList.add(car);
}
}else{//不存在购物车
//创建购物车集合
carList =new ArrayList<>();
car.setTotal(car.getCar_count()*car.getBook_price());
carList.add(car);
}
hashOperations.put("cars","user:"+car.getUser_id(),carList);
}
1.2客户端
1.2.1 前端添加UI
<p class="buyCar">
<el-input-number size="medium" v-model="car.car_count" :min="1"></el-input-number>
<el-button type="warning" style="margin-left: 20px;" @click="addCar">
<span class="el-icon-shopping-cart-full">加入购物车</span>
</el-button>
</p>
1.2.2 前端向后端传递数据
export default {
data() {
return {
car:{
car_count: 1,//加入购物车数量
},
book: {},//图书对象
},
methods: {
addCar(){
//将book_id添加到car对象中
this.car.book_id=this.book.book_id;
this.$axios
.post('car/addCar',this.car)
.then(response=>{
let result=response.data;
if(result.success){//添加成功
}else{//添加失败
}
})
.catch(error=>{
alert(error);
})
},
}
1.2.3 测试Redis是否传入数据
127.0.0.1:6379> keys *
1) "bookList"
2) "cars"
成功…
2 查看购物车
2.1 客户端
2.1.1创建Show.vue
<template>
<div class="showCar-Container">
<div class="car_list">
<el-table
:data="carList"
border
style="width: 100%"
empty-text="暂无商品">
<el-table-column
align="center"
type="selection">
</el-table-column>
<el-table-column
align="center"
type="index"
label="序号">
</el-table-column>
<el-table-column
align="center"
prop="book_name"
label="商品名称">
</el-table-column>
<el-table-column
align="center"
label="商品照片">
<template slot-scope="scope">
<img :src="require('@/assets/images/books/' + scope.row.book_image)" class="image">
</template>
</el-table-column>
<el-table-column
align="center"
prop="book_price"
label="单价">
</el-table-column>
<el-table-column
align="center"
prop="car_count"
label="购买数量">
<template slot-scope="scope">
<el-input-number size="small" v-model="scope.row.car_count" :min="1" @change="updateCarCount(scope.row.book_id,scope.row.car_count)"></el-input-number>
</template>
</el-table-column>
<el-table-column
align="center"
label="总计">
<template slot-scope="scope">
{{ scope.row.car_count* scope.row.book_price }}
</template>
</el-table-column>
<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-popconfirm
confirm-button-text='确认'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="确定删除购物车内该商品吗?"
@confirm="delCarByBookId(scope.row.book_id)">
<el-button type="danger" icon="el-icon-delete" slot="reference">删除</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.image{
width: 80px;
height: 80px;
}
</style>
2.2.2 路由配置
import ShowCar from "../views/ShowCar.vue"
const routes = [
{
path: "/showCar",
name: "showCar",
component: ShowCar,
},
];
2.2.3 前端对后端传来的数据进行处理
export default {
data(){
return{
carList:[],//购物车列表
}
},methods:{
/**
* 获得购物车信息
*/
queryCars(){
this.$axios
.get('car/queryCarInfo')
.then(response=>{
this.carList=response.data;
})
.catch(error=>{
alert(error)
})
},
},created(){
this.queryCars();
}
}
2.2 服务端
2.2.1 创建前端所用数据的model-view-CarView
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CarView implements Serializable {
private Integer book_id; //图书编号
private String book_name;
private Double book_price;
private String book_message;
private Integer car_count; //加入购物车数量
private Double total; //总价
}
2.2.2 CarController添加该功能
@GetMapping("/queryCarInfo")
public List<CarView> queryCarInfo(){
//当前登陆者的编号
int userId=93;
return carService.queryCarInfo(userId);
}
2.2.3 CarService增加该功能
- CarService
List<CarView> queryCarInfo(int userId);
- CarServiceImpl
@Override
public List<CarView> queryCarInfo(int user_id) {
//从redis中获取去购物车信息
List<Car> carList = (List<Car>) hashOperations.get("cars", "user:" + user_id);
//将carList中的book_id封装到list集合中
List<Integer> bookIdList = new ArrayList<>();
for (Car car : carList) {
bookIdList.add(car.getBook_id());
}
//根据book_id获得对应商品数据
List<Book> books = bookMapper.queryBookById(bookIdList.toArray(new Integer[bookIdList.size()]));
List<CarView> carViewList = new ArrayList<>();
for (Car car : carList) {
CarView carView = new CarView();
//购买数量
carView.setCar_count(car.getCar_count());
carView.setBook_id(car.getBook_id());
//获得商品数据
for (Book book : books) {
if (book.getBook_id().equals(car.getCar_count())) {
carView.setBook_image(book.getBook_image());
carView.setBook_name(book.getBook_name());
carView.setBook_price(book.getBook_price());
carView.setTotal(car.getCar_count()*book.getBook_price());
break;
}
}
carViewList.add(carView);
}
return carViewList;
}
3 修改版-查看购物车基于redis
上文中,Redis中存储的数据只是[book_id(图书编号),user_id(用户id),car_count(购买数量)],需要用图书编号查询其他图书信息,是基于redis和mysql共同实现的。
- 该修改是把购物车所需的数据全部存储在Redis中不需要和mysql配合
3.1 客户端
3.1.1 将购物车所需的数据都添加到car对象中
addCar(){
//将数据动态添加到car对象中
this.car.book_id=this.book.book_id;
this.car.book_name=this.book.book_name;
this.car.book_image=this.book.book_image;
this.car.book_price=this.book.book_price;
this.$axios
.post('car/addCar',this.car)
.then(response=>{
let result=response.data;
if(result.success){//添加成功
this.$notify({
title: '成功',
message: '添加成功',
type: 'success'
})
}else{//添加失败
this.$notify.error({
title: '错误',
message: '添加失败'
});
}
})
.catch(error=>{
alert(error);
})
}
3.2 服务端
3.2.1 服务端实体类Car的修改
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Car implements Serializable {
private Integer book_id;
private Integer car_count;
private Integer user_id;
private String book_name;
private Double book_price;
private String book_image;
private Double total;
}
3.2.2 CarController对应方法的修改
@GetMapping("/queryCarInfo")
public List<Car> queryCarInfo() {
//当前登陆者的编号
int userId = 93;
return carService.queryCarInfo(userId);
}
3.2.3 CarService中的对应方法修改
- addCar方法
@Override
public void addCar(Car car) throws Exception {
//向Redis中添加一个商品
List<Car> cars = new ArrayList<>();
//设置合计
car.setTotal(car.getCar_count()*car.getBook_price());
cars.add(car);
//向Redis中添加购物车信息
hashOperations.put("cars", "user:" + car.getUser_id(), cars);
}
- CarService接口
List<Car> queryCarInfo(int userId);
- queryCarInfo方法
@Override
public List<Car> queryCarInfo(int user_id) {
return (List<Car>) hashOperations.get("cars","user:"+user_id);
}
4 修改购物车中商品的数量及删除商品
4.1 客户端
4.1.1 前端UI的修改
- 商品数量
<el-table-column
align="center"
prop="car_count"
label="购买数量">
<template slot-scope="scope">
<el-input-number size="small" v-model="scope.row.car_count" :min="1" @change="updateCarCount(scope.row.book_id,scope.row.car_count)"></el-input-number> </template>
</el-table-column>
- 删除商品
<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-popconfirm
confirm-button-text='确认'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="确定删除购物车内该商品吗?"
@confirm="delCarByBookId(scope.row.book_id)">
<el-button type="danger" icon="el-icon-delete" slot="reference">删除</el-button>
</el-popconfirm>
</template>
</el-table-column>
4.1.2 前端向后端传递数据
- 商品数量
/**
* 修改购物车商品数量
*/
updateCarCount(book_id,car_count){
this.$axios
.put('car/updateCarCount',
{"book_id":book_id,
"car_count": car_count})
.then(response => {
let result=response.data;
if(result.success){
this.queryCars();
}else{
this.$notify.error({
title: '错误',
message: '请输入有效数字'
});
}
})
.catch(error => {
alert(error)
})
},
- 删除商品
/**
* 根据id删除对应商品
*/
delCarByBookId(book_id){
this.$axios
.delete('car/delCarByBookId?book_id='+book_id)
.then(response => {
let result = response.data;
if (result.success) {
this.queryCars();
} else {
this.$notify.error({
title: '错误',
message: '删除失败'
});
}
})
.catch(error => {
alert(error)
})
}
4.2 服务端
4.2.1 CarController添加对应方法
- 修改数量
@PutMapping("/updateCarCount")
public Result updateCarCount(@RequestBody Car car){
try {
car.setUser_id(93);
carService.updateCarCount(car);
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.fail(500, "数字不能为0");
}
}
- 删除商品
@DeleteMapping("/delCarByBookId")
public Result delCarByBookId(Integer book_id){
try {
carService.delCarByBookId(93,book_id);
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.fail(500, "删除失败!!");
}
}
4.2.2 CarService添加对应方法
-
修改数量
- CarService接口
void updateCarCount(Car car);
- CarServiceImpl
@Override public void updateCarCount(Car car) { //获取当前用户购物车 List<Car> carList = (List<Car>) hashOperations.get("cars", "user:" +car.getUser_id()); for (Car car1 : carList) {//查找要修改的商品,并修改数量 if (car1.getBook_id().equals(car.getBook_id())) { //如果存在数量则修改 car1.setCar_count(car.getCar_count()); //更新总价格 car1.setTotal(car1.getCar_count() * car1.getBook_price()); break; } } //修改redis中数据 hashOperations.put("cars", "user:" + car.getUser_id(), carList); }
-
删除商品
- CarService接口
void delCarByBookId(int user_id, Integer book_id);
- CarServiceImpl
@Override public void delCarByBookId(int user_id, Integer book_id) { List<Car> carList = (List<Car>) hashOperations.get("cars", "user:" + user_id); for (Car car :carList){ if (car.getBook_id().equals(book_id)) { //如果存在则从carList中删除 carList.remove(car); break; } } //修改redis中数据 hashOperations.put("cars", "user:" + user_id, carList); }
5 使用Map集合重构购物车
之前存出购物车里商品用的是List列表,操作不是很容易,此修改我们用Map集合来存储购物车商品信息,Map中Key用来存储商品id,Value存储购物车对应商品信息
5.1 添加购物车
5.1.1 服务端Service层-CarService
/**
* 版本3:使用Map解存储购物车内信息
* @param car
* @throws Exception
*/
@Override
public void addCar(Car car) throws Exception {
//声明Map集合存储购物车信息
Map<Integer, Car> carMap = null;
//是否存在购物车
Boolean bool = redisTemplate.hasKey("cars");
if (bool) {//购物车存在
//获取redis中的数据
carMap = (Map<Integer, Car>) hashOperations.get("cars", "user:" + car.getUser_id());
//判断当前用户购物车是否存在
if (carMap == null) {//如果不存在创建Map对象
carMap = new HashMap<>();
} else {//当前用户购物车存在
//判断当前商品是否在购物车
if (carMap.containsKey(car.getBook_id())) {
//更新数量
Car updateCar = carMap.get(car.getBook_id());
updateCar.setCar_count(updateCar.getCar_count() + car.getCar_count());
} else {//当前商品不存在
//直接加入map集合
carMap.put(car.getBook_id(), car);
}
}
} else {//当前购物车不存在
carMap = new HashMap<>();
carMap.put(car.getBook_id(), car);
}
hashOperations.put("cars", "user:" + car.getUser_id(), carMap);
}
5.1.2 价格总计在前端处理
<el-table-column
align="center"
label="总计">
<template slot-scope="scope">
{{ scope.row.car_count* scope.row.book_price }}
</template>
</el-table-column>
5.2 查询购物车信息
- Service层-CarService
@Override
public List<Car> queryCarInfo(int user_id) {
Map<Integer, Car> carMap = (Map<Integer, Car>) hashOperations.get("cars", "user:" + user_id);
if (carMap == null) {
return null;
}
List<Car> carList =new ArrayList<>();
Collection<Car> carInfo = carMap.values();
carList.addAll(carInfo);
return carList;
}
5.3 修改购物车商品数量和删除商品
- Service层-CarService
@Override
public void updateCarCount(Car car) {
//获取当前用户购物车
Map<Integer, Car> carMap = (Map<Integer, Car>) hashOperations.get("cars", "user:" + car.getUser_id());
if (carMap.containsKey(car.getBook_id())){//存在该书
Car updateCar = carMap.get(car.getBook_id());
updateCar.setCar_count(car.getCar_count());
carMap.put(updateCar.getBook_id(),updateCar);
}
//修改redis中数据
hashOperations.put("cars", "user:" + car.getUser_id(), carMap);
}
@Override public void delCarByBookId(int user_id, Integer book_id) {
Map<Integer, Car> carMap = (Map<Integer, Car>) hashOperations.get("cars", "user:" + user_id);
if (carMap.containsKey(book_id)){//购物车是否存在该书,存在即删除
carMap.remove(book_id);
}
//修改redis中数据
hashOperations.put("cars", "user:" + user_id, carMap);
}