购物车模块的实现
1-编辑POJO对象
@TableName("tb_cart")
@Data
@Accessors(chain = true)
public class Cart extends BasePojo{
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long itemId;
private String itemTitle;
private String itemImage;
private Long itemPrice;
private Integer num;
}
2-创建购物车项目
2.2.1 添加继承/依赖/插件
<dependencies>
<dependency>
<groupId>com.jt</groupId>
<artifactId>jt-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<!--添加插件 有main方法时 需要添加插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
2.2.2 业务结构
2.3 展现购物车列表
2.3.1 业务说明
说明: 当用户点击购物车按钮时,应该跳转到购物车展现页面cart.jsp
url如下: http://www.jt.com/cart/show.html
2.3.2 编辑CartController
package com.jt.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.Cart;
import com.jt.service.DubboCartService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/cart")
public class CartController {
@Reference(check = false)
private DubboCartService cartService;
/**
* 展现购物车列表信息 根据userId查询购物车记录
* url: http://www.jt.com/cart/show.html
* 参数: 根据userId查询购物车数据信息
* 返回值: 购物车展现页面
* 页面取值方式: ${cartList}
*/
@RequestMapping("/show")
public String show(Model model){
long userId = 7L; //暂时写死 后期维护
List<Cart> cartList = cartService.findCartListByUserId(userId);
//利用model对象将数据保存到request对象中
model.addAttribute("cartList",cartList);
return "cart";
}
}
2.3.2 编辑CartService
package com.jt.service;
import com.alibaba.dubbo.config.annotation.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.CartMapper;
import com.jt.pojo.Cart;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@Service(timeout = 3000)
public class DubboCartServiceImpl implements DubboCartService{
@Autowired
private CartMapper cartMapper;
@Override
public List<Cart> findCartListByUserId(long userId) {
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("user_id", userId);
return cartMapper.selectList(queryWrapper);
}
}
2.4 购物车新增操作,
用户如果重复加购? 只做数量的更新
2.4.1 页面分析
1.页面结构分析
2.加入购物车按钮
<a class="btn-append " id="InitCartUrl" onclick="addCart();" clstag="shangpin|keycount|product|initcarturl">加入购物车<b></b></a>
3.点击事件
//利用post传值
function addCart(){
var url = "http://www.jt.com/cart/add/${item.id}.html";
document.forms[0].action = url; //js设置提交链接
document.forms[0].submit(); //js表单提交
}
4.form表单分析
<form id="cartForm" method="post">
<input class="text" id="buy-num" name="num" value="1" onkeyup="setAmount.modify('#buy-num');"/>
<input type="hidden" class="text" name="itemTitle" value="${item.title }"/>
<input type="hidden" class="text" name="itemImage" value="${item.images[0]}"/>
<input type="hidden" class="text" name="itemPrice" value="${item.price}"/>
</form>
2.4.2 编辑CartController
/**
* 业务说明: 实现用户购物车数据新增
* url: http://www.jt.com/cart/add/1474391990.html
* 参数: 购物车表单提交 Cart对象
* 返回值: 重定向到跳转到购物车展现页面
*
* 扩展内容: 如果restFul的参数,与对象的属性名称一致,则可以直接赋值
*/
@RequestMapping("/add/{itemId}")
public String addCart(Cart cart){
long userId = 7;
cart.setUserId(userId);
cartService.addCart(cart);
return "redirect:/cart/show.html";
}
2.4.3 编辑CartService
/**
* 难点: 用户如果重复加购? 只做数量的更新
* 业务操作:
* 1.根据userId/itemId查询数据库检查是否加购.
* 有值: 已经加购 则只更新数量
* 没有值: 第一次加购 则直接入库即可.
* @param cart
*/
@Override
public void addCart(Cart cart) {
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", cart.getUserId());
queryWrapper.eq("item_id", cart.getItemId());
Cart cartDB = cartMapper.selectOne(queryWrapper);
if(cartDB == null){ //说明用户第一次加购
cartMapper.insert(cart);
}else{
//说明用户之间添加过该商品 数量的更新
int num = cart.getNum() + cartDB.getNum();
Cart temp = new Cart();
temp.setNum(num).setId(cartDB.getId());
//根据id 更新对象中不为null的数据...
cartMapper.updateById(temp);
}
}
3. 如果是已加购的,只做数量更新操作
3.1页面分析
1).页面URL分析
2).页面html分析
<div class="quantity-form" data-bind="">
<a href="javascript:void(0);" class="decrement" clstag="clickcart|keycount|xincart|diminish1" id="decrement">-</a>
<input type="text" class="quantity-text" itemPrice="${cart.itemPrice}" itemId="${cart.itemId}" value="${cart.num }" id="changeQuantity-11345721-1-1-0">
<a href="javascript:void(0);" class="increment" clstag="clickcart|keycount|xincart|add1" id="increment">+</a>
</div>
3).页面JS
$(".increment").click(function(){//+
var _thisInput = $(this).siblings("input");
_thisInput.val(eval(_thisInput.val()) + 1);
$.post("/cart/update/num/"+_thisInput.attr("itemId")+"/"+_thisInput.val(),function(data){
TTCart.refreshTotalPrice();
});
});
3.2 编辑CartController
/**
* 购物车数量更新操作
* URL地址: http://www.jt.com/cart/update/num/1474391990/16
* 参数: itemId,num
* 返回值: 没有 void
*/
@RequestMapping("/update/num/{itemId}/{num}")
@ResponseBody //将返回值转化为json 代表ajax程序结束
public void updateNum(Cart cart){
long userId = 7;
cart.setUserId(userId);
cartService.updateNum(cart);
}
3.3 编辑CartService
//sql: update tb_cart set num=#{num},updated=#{updated} where user_id=#{userId} and
// item_id = #{itemId}
@Override
public void updateNum(Cart cart) {
Cart temp = new Cart();
temp.setNum(cart.getNum());
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("user_id", cart.getUserId());
queryWrapper.eq("item_id", cart.getItemId());
cartMapper.update(temp,queryWrapper);
}
4.购物车删除操作
4.1页面分析
业务说明: 当用户点击删除按钮时,应该重定向到购物车展现页面.
4.2
4.3 编辑CartController
/**
* URL地址:http://www.jt.com/cart/delete/1474391990.html
* 返回值: String类型
*/
@RequestMapping("/delete/{itemId}")
public String deleteCart(Cart cart){
long userId = 7;
cart.setUserId(userId);
cartService.deleteCart(cart);
return "redirect:/cart/show.html";
}
4.4编辑CartService
@Override
public void deleteCart(Cart cart) {
//根据对象中不为null的属性充当where条件
cartMapper.delete(new QueryWrapper<>(cart));
}
5. 关于前台权限控制
5.1 业务说明
当用户在没有登录的条件下,不允许访问敏感业务数据例如购物车/订单业务等…
难点:
1.如何判断用户是否登录? 1.检查cookie 2.检查redis
2.如何控制权限? 拦截器!
5.1.1 拦截器业务实现原理
5.1.2 SpringMVC流程图复习
5.3 拦截器工作流程
5.4修改jt-web中的MVC的配置文件
配置拦截器
5.5jt-web中新建拦截器 UserInterceptor
编辑拦截器接口
package com.jt.interceptor;
import com.jt.pojo.User;
import com.jt.util.CookieUtil;
import com.jt.util.ObjectMapperUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import redis.clients.jedis.JedisCluster;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.function.Predicate;
@Component //将对象交给Spring容器管理
public class UserInterceptor implements HandlerInterceptor {
@Autowired
private JedisCluster jedisCluster;
/**
* 通过拦截器实现拦截,重定向系统登录页面
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*
* 返回值说明:
* boolean false 表示拦截 一般配合重定向方式使用
*
* 业务调用:
* 1.获取cookie中的数据
* 2.检查redis中是否有数据
* 3.校验用户是否登录.
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.获取cookie中的数据,检查是否登录
String ticket = CookieUtil.getCookieValue(request, "JT_TICKET");
if(StringUtils.hasLength(ticket) && jedisCluster.exists(ticket)){//判断数据是否有值
//2.动态获取user信息
String json = jedisCluster.get(ticket);
//3.将json转化为用户对象
User user = ObjectMapperUtil.toObj(json, User.class);
request.setAttribute("JT_USER", user);
return true; //表示程序放行
}
//实现用户重定向
response.sendRedirect("/user/login.html");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
request.removeAttribute("JT_USER");
}
}
5.6修改编辑CartController
ThreadLocal
6.ThreadLocal作用
名称: 本地线程变量
作用: 在同一个线程内,实现数据共享.
6.1封装工具API
package com.jt.util;
import com.jt.pojo.User;
public class UserThreadLocal {
//线程的一个属性 ThreadLocal是线程安全的
private static ThreadLocal<User> threadLocal = new ThreadLocal<>();
public static void set(User user){
threadLocal.set(user);
}
public static User get(){
return threadLocal.get();
}
public static void remove(){
threadLocal.remove(); //删除数据
}
}
6.2 基于ThreadLocal实现数据取赋值操作
1.拦截器赋值
2.用户取值
3.数据销毁
总结:
购物车模块实现步骤:
1 -建一个购物车POJO对象
2-使用Dubbo+zk进行jt-web(消费者)和jt-cart(提供者)
3-点击购物车按钮时候,跳转到购物车界面: 根据userId查询购物车数据信息,并把数据渲染到购物车页面
4-添加到购物车:
1) 商品未加购,执行新增操作入库
2) 商品已加购,获取该商品的购物信息,只需要更新数量
控制层CartController代码的实现:
(扩展内容: 如果restFul的参数,与对象的属性名称一致,则可以直接赋值)
@RequestMapping("/add/{itemId}")
public String addCart(Cart cart){
long userId = 7;//后期用localThread维护
cart.setUserId(userId);
cartService.addCart(cart);
return "redirect:/cart/show.html";
}
业务层CartServiceImpl代码的实现:
难点: 用户如果重复加购? 只做数量的更新
业务操作:
1.根据userId/itemId查询数据库检查是否加购.
有值: 已经加购 则只更新数量
没有值: 第一次加购 则直接入库即可.
把前端传过来的cart参数用构造器QueryWrapper进行eq字段比较如下
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", cart.getUserId());
queryWrapper.eq("item_id", cart.getItemId());
Cart cartDB = cartMapper.selectOne(queryWrapper);//要么是null,说明购物车内未加购过,要么是有数据,说明加购过.
然后进行判断:
if(cartDB == null){ //说明用户第一次加购
cartMapper.insert(cart);
}else{
//说明用户之间添加过该商品 数量的更新
int num = cart.getNum() + cartDB.getNum();
Cart temp = new Cart();
temp.setNum(num).setId(cartDB.getId());
//根据id 更新对象中不为null的数据...
cartMapper.updateById(temp);
}
2-已加购的话进行数量的更新就可以
实现思路:先从本地线程获取到对应的购物车,然后再更新数量就可以
控制层CartController
利用本地线程localThead 获取共享数据即可以获得用户的id,根据userId查询对应的用户购物车,然后直接更新数量
/**
* 购物车数量更新操作
* URL地址: http://www.jt.com/cart/update/num/1474391990/16
* 参数: itemId,num
* 返回值: 没有 void
*/
@RequestMapping("/update/num/{itemId}/{num}")
@ResponseBody //将返回值转化为json 代表ajax程序结束
public void updateNum(Cart cart){
long userId = 7;
cart.setUserId(userId);
cartService.updateNum(cart);
}
编辑CartService
1-重新new一个购物车,获取到数量,
根据控制层的购物车进行数量的更新即可.
//sql: update tb_cart set num=#{num},updated=#{updated} where user_id=#{userId} and
// item_id = #{itemId}
@Override
public void updateNum(Cart cart) {
Cart temp = new Cart();
temp.setNum(cart.getNum());
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("user_id", cart.getUserId());
queryWrapper.eq("item_id", cart.getItemId());
cartMapper.update(temp,queryWrapper);
}
重点难点:
1-用户如果重复加购? 只做数量的更新
2-如何进行前台的控制
当用户在没有登录的条件下,不允许访问敏感业务数据例如购物车/订单业务等…
难点:
1.如何判断用户是否登录? 1.检查cookie 2.检查redis
2.如何控制权限? MVC拦截器!