当我们制作好了菜品展示之后,用户可以根据需求来进行点单操作,将选好的菜品加入到购物车里,方便后续进行结算,今天要完成的是实现一个加入购物车的功能
2023/11/17
1.缓存点单信息
逻辑如下:
要实现购物车的功能,我们需要在每个菜品后面绑定一个添加的操作,用户每次点击添加后,会发送菜品的一些相关信息到controller,然后我们将它存到缓存redis中,因为购物车不可能一直存在,所有我们本次第一次使用redis来缓存数据。
前端页面修改如下:
<td th:if="${session.USERTYPE == 'customer'}">
<input type="text" th:id="carinput+${cai.id}" style="display: none" value="0">
<button type="button" th:id="carbutton+${cai.id}" th:data-id="${cai.id}" onclick="addcar(this)">+1</button>
</td>
<script th:inline="javascript">
function addcar(button){
const caiId = button.getAttribute('data-id')
const input = document.getElementById("carinput"+caiId)
//value为字符串,需要转换
let cainum = parseInt(input.value,10);
cainum+=1;
const formata = new FormData
formata.append('id',caiId)
formata.append('num',cainum)
fetch('/car',{
method:'POST',
body:formata
}).then(res=>res.json() )
.then(res=>{
console.log(res.message);
alert(res.message)
})
}
</script>
如果用户为消费者,则在每个菜品后面添加一个“+1”的按钮,使用fetch异步发送请求,每次点击后,向后端发送菜品id同时num+1,菜品数量初始化为0
后端代码修改如下:
@Controller
public class CarController {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
private HashOperations<String, Object, Object> hashOperations;
@PostMapping("/car")
public ResponseEntity<Map<String,String>> addCar(@RequestParam("id")int id, @RequestParam("num")int num, HttpSession session){
String username = (String) session.getAttribute("USER");
if (username==null){
Map<String,String> response = new HashMap<>();
response.put("message","用户未登录");
return ResponseEntity.badRequest().body(response);
}
// 构建Redis中的购物车键,这里使用了哈希结构
String cartKey = "cart:" + username;
String caiid = "cai" + id;
// 获取Redis哈希操作的引用
hashOperations = redisTemplate.opsForHash();
// 更新Redis中的购物车数据
// 如果该商品已经存在于购物车中,它的数量将被累加
// 如果该商品是新添加的,它将被初始化为传入的数量
hashOperations.increment(cartKey, caiid, num);
// 更新model,用于视图显示或进一步的处理
Map<String,String> response = new HashMap<>();
response.put("message","商品已加入购物车");
// model.addAttribute("message", "商品已添加到购物车");
return ResponseEntity.ok().body(response); // 或重定向到购物车页面
}
首先自动注解redistemplate意味着我们可以使用redis的一些操作,同时注解hashOperation意味着我们可以使用哈希操作(object意味着可以处理任何数据),在方法中我们不适用String类型,这种是返回视图模板,通过model来渲染数据,需要刷新页面,不适合我们异步请求,因此使用ResopnseEntity类型。
具体的逻辑如下:
我们有三个参数分别是id,num和session,在前面的logincontroller中我们定义了当验证用户密码通过后,会为用户创建一个session存储在redis中,因此此时我们可以直接获取session的用户名,如果用户名不为空则说明已经验证过了同时处于有效期内,这时,我们将用car+username的方法为每个用户新建购物车,初始化hashOperation为redis的hash操作,然后使用hashOperation的increment方法,增加num
实现如下:
2.展示购物车
在将菜品信息加入到缓存中后,我们要想办法将它给提取出来,一开始我想要使用的是model方法,毕竟动态渲染真的很方便,但是提交表单后页面会自动刷新,弹窗会自动消失,因此我采用了fetch方法
前端代码修改如下:
<button id="carbutton" onclick="showcar()" type="submit">进入购物车</button>
<script>
function showcar(){
fetch('/getcarinfo',{
method:'POST'
}).then(res=>res.json())
.then(data=>{
updatacar(data);
document.getElementById("carModal").style.display="block";
}).catch(err=>console.error("error",err))
}
function updatacar(carinfo){
const tbody = document.querySelector("#carModal .modal-content table tbody");
tbody.innerHTML="";
for(const[key,value]of Object.entries(carinfo)){
const row = document.createElement('tr');
row.innerHTML = `<td>${key}</td><td>${value}</td>`
tbody.appendChild(row);
}
}
function closeModal3() {
document.getElementById("carModal").style.display = 'none';
}
</script>
<div id="carModal" class="modal" style="display: none">
<div class="modal-content">
<h2>购物车</h2>
<span class="close-button" id="closeModalButton3" onclick="closeModal3()">×</span>
<table class="centered-table">
<thead>
<tr>
<th>菜名</th>
<th>数量</th>
</tr>
</thead>
<tbody>
<tr></tr>
</tbody>
</table>
</div>
</div>
后端代码修改如下:
@PostMapping("/getcarinfo")
public ResponseEntity<Map<Object,Object>> getcarinfo(Model model,HttpSession session){
String username = (String)session.getAttribute("USER");
System.out.println(username);
String cartKey = "cart:" + username;
// 获取Redis哈希操作的引用
hashOperations = redisTemplate.opsForHash();
Map<Object, Object> carinfo = hashOperations.entries(cartKey);
System.out.println(carinfo);
return ResponseEntity.ok().body(carinfo);
}
在展示方面我们依然采用了模拟对话弹窗的方式,展示了菜品的id信息和添加的数量,当点击进入购物车按钮后,调用fetch请求,后端接收请求后,根据用户名来获取对应的购物车信息,返回给前端一个carinfo,前端拿到数据后,调用updata方法,更新tbody中的数据
这里我们使用了一个queryselector元素选择器,可以选择类,id,标签名等多种方式,定位到tbody后,我们使用for of 方法,将carinfo对象的键值赋值给key和value,并使用反引号的方式新加到tr元素中,最终呈现效果如下: