springboot篇】二十一. 基于springboot电商项目 十 购物车模块和自定义注解校验登录状况

springboot项目

中国加油,武汉加油!

篇幅较长,配合目录观看

案例准备

  1. 本案例基于springboot篇】二十一. 基于springboot电商项目 九 修改密码,登录和注销

1. 购物车模块环境搭建

1.1 新建数据库表t_cart

DROP TABLE IF EXISTS `t_cart`;
CREATE TABLE `t_cart` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `gid` int(10) DEFAULT NULL,
  `num` int(10) DEFAULT NULL,
  `uid` int(10) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `sub_total` decimal(10,0) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

1.2 shop-entity定义Cart

package com.wpj.entity;

import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_cart")
public class Cart implements Serializable{
    @TableId(type = IdType.AUTO)
    private Integer id;
    private Integer gid; // 商品id
    private Integer num; // 商品数量
    private Integer uid; // 用户id
    @TableField(value = "create_time")
    private Date createTime; // 创建
    @TableField(value = "sub_total")
    private BigDecimal subTotal; // 商品小计
    @TableField(exist = false)
    private Goods goods; // 商品信息
}

1.3 shop-mapper编写Mapper接口

package com.wpj.mapper;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.wpj.entity.Cart;

public interface ICartMapper extends BaseMapper<Cart> {
}

1.4 shop-service编写Servic接口

package com.wpj.service;

import com.wpj.entity.Cart;

public interface ICartService extends IBaseService<Cart> {
}

1.5 shop-service-iml新建cart-service(module-springboot)

1.6 cart-service导包

<dependency>
 <groupId>com.wpj</groupId>
    <artifactId>shop-service-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>com.wpj</groupId>
    <artifactId>shop-mapper</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>com.wpj</groupId>
    <artifactId>shop-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>com.alibaba.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>0.2.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.8</version>
</dependency>

1.7 编写yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/nz1904-springboot-shop
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
mybatis-plus:
  type-aliases-package: com.wpj.entity
  mapper-locations: classpath:/mapper/*.xml
dubbo:
  application:
    name: cart-service
  registry:
    address: zookeeper://192.168.59.100:2181
  protocol:
    port: -1

1.8 cart-service编写ServiceImpl实现类

package com.wpj.service.impl;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.wpj.entity.Cart;
import com.wpj.mapper.ICartMapper;
import com.wpj.service.ICartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CartServiceImpl extends BaseServiceImpl<Cart> implements ICartService {

    @Autowired
    private ICartMapper cartMapper;

    @Override
    public BaseMapper<Cart> getMapper() {
        return cartMapper;
    }
}

1.9 shop-web新建shop-cart(module-springboot)

  1. Spring Boot DevTools
  2. Spring Web
  3. Thymeleaf
  4. Spring Data Redis(Access+Driver)

1.10 shop-cart编写Controller

package com.wpj.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/cart")
public class CartController {

    @RequestMapping("/addCart")
    @ResponseBody
    public String addCart(Integer gid, Integer num) {
        System.out.println("gid = " + gid + ", num = " + num);
        return "ok";
    }
}

1.11 编写yml

server:
  port: 8085
spring:
  thymeleaf:
    cache: false
    prefix: classpath:/templates/
  redis:
    host: 192.168.59.100
    password: admin
dubbo:
  application:
    name: shop-cart
  registry:
    address: zookeeper://192.168.59.100:2181
  consumer:
    check:  false
    retries: 3
    timeout: 10000

1.12 修改程序入口

package com.wpj;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(scanBasePackages = "com.wpj", exclude = DataSourceAutoConfiguration.class)
public class ShopCartApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShopCartApplication.class, args);
    }
}

1.13 启动程序入口Test

在这里插入图片描述

2. 自定义注解校验登录状况

2.1 shop-common导包

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.2</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.2</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.24.RELEASE</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.1.8.RELEASE</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.wpj</groupId>
    <artifactId>shop-entity</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.1.10.RELEASE</version>
</dependency>

2.2 编写IsLogin注解

package com.wpj.common.aop;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface IsLogin {
    // 默认不强制注入user
    boolean mustUser() default false;
}

2.3 编写IsLoginAOP

package com.wpj.common.aop;

import com.wpj.common.constant.Constants;
import com.wpj.entity.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class IsLoginAOP {

    @Autowired
    private RedisTemplate redisTemplate;

    @Around("@annotation(IsLogin)")
    public Object handler(ProceedingJoinPoint point) {
        // 添加了两个依赖 servlet-api 和 spring-web
        // 获取HttpServletRequest对象(和Servlet容器解耦的情况下获取ServletAPI)
        ServletRequestAttributes requestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        // 根据req对象获取Cookie
        String loginToken = ""; // 登录用户的凭证
        Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for(int i=0;i<cookies.length;i++){
                Cookie cookie = cookies[i];
                if(Constants.LOGIN_TOKEN.equals(cookie.getName())){
                    loginToken = cookie.getValue();
                    break;
                }
            }
        }
        // 再从Redis中查询登录用户
        User user = null; // 当前登录用户
        if(!StringUtils.isEmpty(loginToken)){
            user = (User) redisTemplate.opsForValue().get(loginToken);
        }
        // 判断用户是否登录,如果没有登录,再判断mustUser是否为true
        if(user == null) {
            // 获取目标方法
            MethodSignature methodSignature = (MethodSignature)point.getSignature();
            // 获取方法上面的IsLogin的注解
            IsLogin annotation = methodSignature.getMethod().getAnnotation(IsLogin.class);
            // 判断user是否是必须的
            if(annotation.mustUser()){
                return "redirect:http://localhost:8084/toLogin";
            }
        }
        // 把user传递Controller中的形参
        Object[] args = point.getArgs();// 获取目标方法的参数
        for(int i =0;i<args.length;i++){
            if(args[i]!= null && args[i].getClass() == User.class){ // 判断当前的参数是否是user类型
                args[i]=user; // 替换
                break;
            }
        }
        Object proceed = null;
        try {
            // 调用Controller,把我们处理过的参数传递过去
            proceed = point.proceed(args);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }
}

2.4 shop-cart编写Contrller

package com.wpj.controller;

import com.wpj.common.aop.IsLogin;
import com.wpj.entity.Cart;
import com.wpj.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@RequestMapping("/cart")
public class CartController {

    @RequestMapping(value = "/addCart")
    // @ResponseBody
    @IsLogin(mustUser = true)
    public String addCart(String callback) {
        System.out.println("CartController.addCart");
        String result = "ok";
        return callback == null?result+"":callback+"('"+result+"')";
    }
}

2.5 启动程序入口测试

  1. 注意清楚cookie达到没有登录情况下
  2. 调用addCart就会跳转到login页面
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3. 购物车模块-加入购物车

3.1 shop-cart导包

<dependency>
    <groupId>com.wpj</groupId>
    <artifactId>shop-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>com.wpj</groupId>
    <artifactId>shop-service-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>com.alibaba.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>0.2.0</version>
</dependency>

3.2 Service添加方法

package com.wpj.service;

import com.wpj.entity.Cart;
import com.wpj.entity.User;

public interface ICartService extends IBaseService<Cart> {
    int addCart(Cart cart, User user, String cartToken);
}

3.3 ServiceImpl重写方法

package com.wpj.service.impl;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.wpj.entity.Cart;
import com.wpj.entity.User;
import com.wpj.mapper.ICartMapper;
import com.wpj.service.ICartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class CartServiceImpl extends BaseServiceImpl<Cart> implements ICartService {

    @Autowired
    private ICartMapper cartMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Override
    public BaseMapper<Cart> getMapper() {
        return cartMapper;
    }

    @Override
    public int addCart(Cart cart, User user, String cartToken) {

        cart.setCreateTime(new Date());
        // 判断用户是否登录
        if (user != null) {
            cart.setUid(user.getId());
            cartMapper.insert(cart); // 添加到数据库
            return 1;
        } else {
            redisTemplate.opsForList().rightPush(cartToken, cart);
            return 1;
        }
    }
}

3.2 shop-cart完善Controller的addCart方法

package com.wpj.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.wpj.common.aop.IsLogin;
import com.wpj.common.constant.Constants;
import com.wpj.entity.Cart;
import com.wpj.entity.User;
import com.wpj.service.ICartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Controller
@RequestMapping("/cart")
public class CartController {

    @Autowired
    private RedisTemplate redisTemplate;

    @Reference
    private ICartService cartService;

    /**
     * 登录了放到数据库中
     * 未登录放到Redis
     *      1)key放到cookie中
     * @param callback
     * @return
     */
    @RequestMapping(value = "/addCart")
    @ResponseBody
    @IsLogin
    public String addCart(String callback, Cart cart, User user,
                          @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken,
                          HttpServletResponse resp){

       // 判断用户浏览器中是否有cartToken
        if(StringUtils.isEmpty(cartToken)){
            cartToken= UUID.randomUUID().toString();
            Cookie cookie = new Cookie(Constants.CART_TOKEN,cartToken);
            cookie.setMaxAge(60*60*24*5);
            cookie.setPath("/");
            cookie.setHttpOnly(false);

            resp.addCookie(cookie);
        }
        int result = cartService.addCart(cart,user,cartToken);

        return callback == null?result+"":callback+"('"+result+"')";
    }
}

3.3 cart-service修改ServiceImpl的@Service

在这里插入图片描述

3.4 配置yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/nz1904-springboot-shop
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  redis:
    host: 192.168.59.100
    password: admin
mybatis-plus:
  type-aliases-package: com.wpj.entity
  mapper-locations: classpath:/mapper/*.xml
dubbo:
  application:
    name: cart-service
  registry:
    address: zookeeper://192.168.59.100:2181
  protocol:
    port: -1

3.4 启动程序入口Test

3.4.1 未登录状态下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.4.2 登录状态下

在这里插入图片描述在这里插入图片描述

4. 购物车模块-我的购物车

4.1 shop-service-api添加方法

List<Cart> getUserCartList(User user, String cartToken);

4.2 cart-service重写方法

@Reference
private IGoodService goodService;

@Override
public List<Cart> getUserCartList(User user, String cartToken) {
    // 查询用户购物车
    List<Cart> cartList = new ArrayList<Cart>();
    if (user != null) {
        EntityWrapper wrapper = new EntityWrapper();
        wrapper.eq("uid", user.getId());
        cartList = cartMapper.selectList(wrapper);
    } else {
        cartList = redisTemplate.opsForList().range(cartToken, 0, -1);
    }
    // 查询购物车中每个商品的信息
    for (Cart cart : cartList) {
        Goods goods = goodService.selectById(cart.getGid());
        cart.setGoods(goods);
        BigDecimal gprice = goods.getGprice();
        Integer num = cart.getNum();
        BigDecimal subTotal = gprice.multiply(BigDecimal.valueOf(num));// 价格*num
        cart.setSubTotal(subTotal); // 小计
    }
    // 去重
    Set<Integer> set = new HashSet<>(); // set目的是为了判断是否有重复
    Map<Integer, Cart> map = new HashMap<>(); // Map<商品Id,购车>
    for (Cart cart : cartList) {
        if (set.add(cart.getGoods().getId())) {
            map.put(cart.getGoods().getId(), cart);
        } else {
            Cart mapCart = map.get(cart.getGoods().getId());
            mapCart.setNum(mapCart.getNum() + cart.getNum());
            mapCart.setSubTotal(mapCart.getSubTotal().add(cart.getSubTotal()));
        }
    }
    // 清空集合
    cartList.clear();
    // 把集合中的商品放到list里面
    Set<Map.Entry<Integer, Cart>> entries = map.entrySet();
    for (Map.Entry<Integer, Cart> entry : entries) {
        cartList.add(entry.getValue());
    }
    return cartList;
}

4.3 shop-cart引入静态资源

4.4 编写cartList.html

4.5 shop-cart添加方法

@RequestMapping(value = "/getUserCartList")
@IsLogin
public String getUserCartList(User user, @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken, ModelMap map){
    List<Cart> cartList = cartService.getUserCartList(user,cartToken);
    map.put("cartList",cartList);
    return "cartList";
}

4.6 修改shop-search的searchGoodList.html

在这里插入图片描述

4.7 启动程序入口测试

在这里插入图片描述
在这里插入图片描述

5 购物车模块-设置购物车数量

5.1 修改shop-search的searchGoodsList.html

<script type="text/javascript">
    $(function(){
        $.ajax({
            url:"http://localhost:8085/cart/getUserCartCount",
            dataType:"JSONP",
            jsonpCallback:"callback1",
            success:function(data){
                $("#showCart").text(data);
            }
        })
    })
</script>
<li><a href="http://localhost:8085/cart/getUserCartList">我的购物车<span id="showCart"></span></a></li>

5.2 shop-cart的Controller编写方法

@RequestMapping(value = "/getUserCartCount")
@IsLogin
@ResponseBody
public String getUserCartCount(String callback,User user, @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken){
    Integer count = 0;
    List<Cart> cartList = cartService.getUserCartList(user,cartToken);
    for(Cart cart:cartList){
        count+=cart.getNum();
    }
    return callback == null?count+"":callback+"('"+count+"')";
}

6. 购物车模块-购物车合并

6.1 shop-service-api添加方法

int mergeCart(User user, String cartToken);

6.2 cart-service重写方法

 @Override
public int mergeCart(User user, String cartToken) {
	// 从reids中查询
	List<Cart> cartList = redisTemplate.opsForList().range(cartToken, 0, -1);
	for (Cart cart : cartList) {
	    cart.setUid(user.getId());
	    cartMapper.insert(cart);
	}
	// 清空redis
	redisTemplate.delete(cartToken);
	return 1;
}

6.3 shop-sso修改Controller的login方法

@Reference
private ICartService cartService;

@RequestMapping(value = "/login")
@ResponseBody
public ResultEntity login(String username, String password, String returnUrl,HttpServletResponse resp, @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken){
    // 根据用户名查询对象
    User user = userService.selectByUsername(username);
    if(user != null){
        // 密码的比对
        if(PasswordUtils.checkpw(password,user.getPassword())){
            // 购车的合并
            if(!StringUtils.isEmpty(cartToken)){
                int result = cartService.mergeCart(user,cartToken);
            }
            // 把登陆用户放道redis中
            redisTemplate.opsForValue().set(username,user,5,TimeUnit.DAYS);
            // 把用户的凭证放道cookie
            Cookie cookie = new Cookie(Constants.LOGIN_TOKEN,username);
            cookie.setMaxAge(60*60*24*5);
            cookie.setHttpOnly(false); // 是否允许js访问
            cookie.setPath("/"); // 同一个域名下面可以访问
//                cookie.setDomain(); // 正则表达式,解决一级域名的cookie跨域
//                cookie.setSecure(true); // 必须是加密(https)的请求才可以携带cookie
            // 写到cookie
            resp.addCookie(cookie);
            if(StringUtils.isEmpty(returnUrl)){
                System.out.println(returnUrl);
                returnUrl = "http://localhost:8081/";
            }
            return ResultEntity.SUCCESS(returnUrl);
        }else{
            return ResultEntity.FALL("用户名或密码错误");
        }

    }else{
        return ResultEntity.FALL("用户名不存在");
    }
}

7. 购物车模块-提交订单

7.1 shop-cart修改cartList.html

在这里插入图片描述

7.2 shop-web修改Controller的getUserCartList方法

 @RequestMapping(value = "/getUserCartList")
@IsLogin
public String getUserCartList(User user, @CookieValue(name = Constants.CART_TOKEN,required = false) String cartToken, ModelMap map){
    List<Cart> cartList = cartService.getUserCartList(user,cartToken);
    BigDecimal totalPrice = new BigDecimal(0);
    for(Cart cart:cartList){
        totalPrice= totalPrice.add(cart.getSubTotal());
    }
    map.put("cartList",cartList);
    map.put("totalPrice",totalPrice);
    return "cartList";
}

7.3 启动程序入口测试

在这里插入图片描述

发布了126 篇原创文章 · 获赞 18 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/TheNew_One/article/details/105319690
今日推荐