谷粒商城十九购物车服务

// win本地hosts
// 将cart的静态资源上传到nginx的cart文件夹下
// 将cartList.html和success.html复制到模块中,并修改静态资源路径
192.168.56.10	gulimall.com
192.168.56.10	search.gulimall.com
192.168.56.10	item.gulimall.com
192.168.56.10	auth.gulimall.com
192.168.56.10	cart.gulimall.com


// gateway添加以下路由
- id: gulimall_cart_route
  uri: lb://gulimall-cart
  predicates:
   - Host=cart.gulimall.com


// application.properties
server.port=14000
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.application.name=gulimall-cart
spring.redis.host=192.168.56.10

gulimall.thread.core-size=20
gulimall.thread.keep-alive-time=10
gulimall.thread.max-size=200

需求描述

  1. 用户可以在登录状态下将商品添加到购物车【用户购物车/在线购物车】

    • 登录以后,会将临时购物车的数据全部合并过来,并清空临时购物车;
    • 选用redis作为存储(数据结构好组织,拥有极高的读写并发性能)
      购物车是读多写多的操作,存入mysql会造成非常大的压力。

    问题: redis是一个内存数据库,一旦宕机则数据丢失。后面我们可以修改持久化策略来解决,即使这样会损失一部分吞吐量,但仍然比mysql性能高很多。

  2. 用户可以在未登录状态下将商品添加到购物车【游客购物车/离线购物车/临时购物车】

    • 浏览器即使关闭,下次进入,临时购物车数据都在。
    • 选用redis作为存储
      存cookie,后端的压力会减小,缺点是我们会根据购物车的商品在首页推荐相关的产品,提高用户的购买能力,存放在浏览器端的话我们就获取不到购物车中的数据了。

功能

  1. 用户可以使用购物车一起结算下单
  2. 给购物车添加商品
  3. 用户可以查询自己的购物车
  4. 用户可以在购物车中修改购买商品的数量
  5. 用户可以在购物车中删除商品
  6. 选中不选中商品
  7. 在购物车中展示商品优惠信息
  8. 提示购物车商品价格变化

在这里插入图片描述

ThreadLocal 同一个线程共享数据

每一个请求进来,tomcat会开一个线程给我们处理,从拦截器的执行,到controller,service,dao一直到请求结束给浏览器响应,从始至终都是同一个线程。

所以在同一个线程期间,所以下一个流程要共享上一个流程的数据,就可以使用ThreadLocal。

ThreadLocal的核心原理,其实就是一个map,map的key时thread(当前线程),值就是当前线程要共享的数据。

key不一样,值也就不一样,所以我们每一个线程是互不干扰的。

在这里插入图片描述

购物车功能

启动类

package com.atlinxi.gulimall.cart;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

@EnableRedisHttpSession
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class GulimallCartApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(GulimallCartApplication.class, args);
	}

}

config

package com.atlinxi.gulimall.cart.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;

@Configuration
public class GulimallSessionConfig {
    
    

    @Bean
    public CookieSerializer cookieSerializer(){
    
    
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        cookieSerializer.setDomainName("gulimall.com");
        cookieSerializer.setCookieName("GULISESSION");
        return cookieSerializer;
    }


    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
    
    
        return new GenericJackson2JsonRedisSerializer();
    }
}








package com.atlinxi.gulimall.cart.config;

import com.atlinxi.gulimall.cart.interceptor.CartInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {
    
    

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        // 将我们写好的拦截器添加到注册列表中,并指定拦截所有请求
        registry.addInterceptor(new CartInterceptor()).addPathPatterns("/**");
    }
}












package com.atlinxi.gulimall.cart.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


// ThreadPoolConfigProperties 已经被注入容器了,就不需要这样配置了
//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {
    
    

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool){
    
    
        return new ThreadPoolExecutor(pool.getCoreSize(),pool.getMaxSize(),
                pool.getKeepAliveTime(), TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100000),
                Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()
                );
    }

}










package com.atlinxi.gulimall.cart.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties("gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
    
    

    private Integer coreSize;
    private Integer maxSize;
    private Integer keepAliveTime;
}

intercepter

package com.atlinxi.gulimall.cart.interceptor;

import com.atlinxi.common.constant.AuthServerConstant;
import com.atlinxi.common.constant.CartConstant;
import com.atlinxi.common.vo.MemberRespVo;
import com.atlinxi.gulimall.cart.vo.UserInfoTo;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/**
 * 在执行目标方法之前(controller中的方法),判断用户的登录状态,并封装传递给controller目标请求
 */
public class CartInterceptor implements HandlerInterceptor {
    
    


    public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();

    /**
     * 目标方法执行之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    

        UserInfoTo userInfoTo = new UserInfoTo();
        HttpSession session = request.getSession();
        MemberRespVo member = (MemberRespVo) session.getAttribute(AuthServerConstant.LOGIN_USER);
        if (member != null){
    
    
            // 用户登录
            userInfoTo.setUserId(member.getId());

        }
        Cookie[] cookies = request.getCookies();
        if (cookies!=null && cookies.length > 0){
    
    
            for (Cookie cookie : cookies) {
    
    
                String name = cookie.getName();
                if (name.equals(CartConstant.TEMP_USER_COOKIE_NAME)){
    
    
                    userInfoTo.setUserKey(cookie.getValue());
                    userInfoTo.setTempUser(true);
                }
            }
        }

        // 如果没有临时用户一定分配一个临时用户
        if (StringUtils.isEmpty(userInfoTo.getUserKey())){
    
    
            String uuid = UUID.randomUUID().toString();
            userInfoTo.setUserKey(uuid);

        }

        // 目标方法执行之前
        threadLocal.set(userInfoTo);

        return true;
    }


    /**
     * 业务执行之后:分配临时用户,让浏览器保存
     *
     * 购物车无论是否登录都需要user-key的存在,响应之后可能会跳转页面,我们需要设置一下cookie
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    

        UserInfoTo userInfoTo = threadLocal.get();

        // 如果没有临时用户一定保存一个临时用户
        if (!userInfoTo.isTempUser()){
    
    
            Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
            cookie.setDomain("gulimall.com");
            // 一个月
            cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);
            response.addCookie(cookie);
        }


    }
}

product服务获取sku销售属性

SELECT CONCAT(attr_name,":",attr_value) FROM pms_sku_sale_attr_value WHERE sku_id=#{skuId}

controller

package com.atlinxi.gulimall.cart.controller;

import com.atlinxi.gulimall.cart.feign.ProductFeignService;
import com.atlinxi.gulimall.cart.service.CartService;
import com.atlinxi.gulimall.cart.vo.Cart;
import com.atlinxi.gulimall.cart.vo.CartItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.concurrent.ExecutionException;


@Controller
public class CartController {
    
    

    @Autowired
    CartService cartService;

    @Autowired
    ProductFeignService productFeignService;





    @GetMapping("/deleteItem")
    public String countItem(@RequestParam("skuId") Long skuId){
    
    

        cartService.deleteItem(skuId);

        return "redirect:http://cart.gulimall.com/cart.html";

    }



    @GetMapping("/countItem")
    public String countItem(@RequestParam("skuId") Long skuId,@RequestParam("num") Integer num){
    
    

        cartService.changeItemCount(skuId,num);

        return "redirect:http://cart.gulimall.com/cart.html";

    }



    @GetMapping("/checkItem")
    public String checkItem(@RequestParam("skuId") Long skuId,@RequestParam("check") Integer check){
    
    

        cartService.checkItem(skuId,check);

        return "redirect:http://cart.gulimall.com/cart.html";

    }


    /**
     * 浏览器有一个cookie:user-key:标识用户身份,一个月后过期
     * 如果第一次使用购物车功能,都会给一个临时的用户身份
     * 浏览器以后保存,每次访问都会带上这个cookie
     *
     * 登录:session有
     * 没登录:按照cookie里面带来user-key来做
     * 第一次:如果没有临时用户,则创建一个临时用户
     * @return
     */
    @GetMapping("/cart.html")
    public String cartListPage(Model model) throws ExecutionException, InterruptedException {
    
    

        Cart cart = cartService.getCart();
        model.addAttribute("cart",cart);


        return  "cartList";
    }


    /**
     * 添加商品到购物车
     * @return
     *
     * 使用重定向解决重复提交的问题
     * 在这儿直接返回success的话,浏览器再次刷新该页面,则会重复提交
     * 之前这个商品买了几个,刷新一遍就会又买一遍,
     * 添加成功之后重定向到其他页面就可以避免这个问题
     *
     *
     * RedirectAttributes
     *
     *  addFlashAttribute()
     *      将数据放在session里面,可以在页面取出,但是只能取一次
     *  addAttribute()
     *      将数据放在url后面
     *
     *
     *
     */
    @GetMapping("/addToCart")
    public String addToCart(@RequestParam("skuId") Long skuId,
                            @RequestParam("num") Integer num,
                            RedirectAttributes redirectAttributes) throws ExecutionException, InterruptedException {
    
    

        cartService.addToCart(skuId,num);

        redirectAttributes.addAttribute("skuId",skuId);

        return "redirect:http://cart.gulimall.com/addToCartSuccess.html";
    }


    /**
     * 跳转到成功页
     * @param skuId
     * @param model
     * @return
     */
    @GetMapping("/addToCartSuccess.html")
    public String addToCartSuccessPage(@RequestParam("skuId") Long skuId,Model model){
    
    

        // 重定向到成功页面。再次查询购物车数据即可
        CartItem cartItem = cartService.getCartItem(skuId);

        model.addAttribute("item",cartItem);

        return "success";
    }
}

service

package com.atlinxi.gulimall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atlinxi.common.utils.R;
import com.atlinxi.gulimall.cart.feign.ProductFeignService;
import com.atlinxi.gulimall.cart.interceptor.CartInterceptor;
import com.atlinxi.gulimall.cart.service.CartService;
import com.atlinxi.gulimall.cart.vo.Cart;
import com.atlinxi.gulimall.cart.vo.CartItem;
import com.atlinxi.gulimall.cart.vo.SkuInfoVo;
import com.atlinxi.gulimall.cart.vo.UserInfoTo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CartServiceImpl implements CartService {
    
    

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    ThreadPoolExecutor threadPoolExecutor;

    private final String CART_PREFIX = "gulimall:cart:";


    @Override
    public CartItem addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
    
    



        BoundHashOperations<String, Object, Object> cartOps = getCartOps();


        String res  = (String) cartOps.get(skuId.toString());

        if (StringUtils.isEmpty(res)){
    
    
            // cartItem 不能抽取到上面,异步任务可能不能让对象改变
            // 具体也不是很明白
            CartItem cartItem = new CartItem();
            // 购物车无此商品
            // 2. 新商品添加到购物车


            CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {
    
    
                // 1. 远程查询当前要添加的商品的信息
                R skuInfo = productFeignService.getSkuInfo(skuId);

                SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {
    
    
                });



                cartItem.setCheck(true);
                cartItem.setCount(num);
                cartItem.setImage(data.getSkuDefaultImg());
                cartItem.setTitle(data.getSkuTitle());
                cartItem.setPrice(data.getPrice());
                cartItem.setSkuId(data.getSkuId());
            },threadPoolExecutor);


            // 3. 远程查询sku的组合信息
            CompletableFuture<Void> getSukSaleAttrValues = CompletableFuture.runAsync(() -> {
    
    
                List<String> values = productFeignService.getSkuSaleAttrValues(skuId);
                cartItem.setSkuAttr(values);
            }, threadPoolExecutor);

            CompletableFuture.allOf(getSkuInfoTask,getSukSaleAttrValues).get();

            String s = JSON.toJSONString(cartItem);
            cartOps.put(skuId.toString(),s);

            return cartItem;
        }else {
    
    
            CartItem cartItem = new CartItem();
            // 购物车有此商品,修改数量即可
            cartItem = JSON.parseObject(res, CartItem.class);
            cartItem.setCount(cartItem.getCount()+num);

            cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));
            return cartItem;

        }



    }

    @Override
    public CartItem getCartItem(Long skuId) {
    
    
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();

        String str = (String) cartOps.get(skuId.toString());

        CartItem cartItem = JSON.parseObject(str, CartItem.class);

        return cartItem;
    }

    @Override
    public Cart getCart() throws ExecutionException, InterruptedException {
    
    

        Cart cart = new Cart();
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();

        if (userInfoTo.getUserId()!=null){
    
    

            // 1. 登录
            String cartKey = CART_PREFIX + userInfoTo.getUserId();

            String tempCartKey = CART_PREFIX + userInfoTo.getUserKey();

            // 2. 如果临时购物车的数据还没有进行合并
            List<CartItem> tempCartItems = getCartItems(tempCartKey);

            if (tempCartItems!=null){
    
    
                // 临时购物车有数据,需要合并
                for (CartItem item : tempCartItems) {
    
    
                    addToCart(item.getSkuId(), item.getCount());
                }

                // 清除临时购物车的数据
                clearCart(tempCartKey);

            }

            // 3. 获取登录后的购物车数据【包含合并过来的临时购物车的数据,和登陆后的购物车的数据】
            List<CartItem> cartItems = getCartItems(cartKey);

            cart.setItems(cartItems);

        }else {
    
    
            // 2. 没登录
            String cartKey = CART_PREFIX + userInfoTo.getUserKey();
            // 获取临时购物车的所有购物项
            List<CartItem> cartItems = getCartItems(cartKey);
            cart.setItems(cartItems);


        }

        return cart;
    }


    /**
     *
     * 获取到我们要操作的购物车(临时/已登录)
     *
     * @return
     */
    private BoundHashOperations<String, Object, Object> getCartOps() {
    
    
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();

        //
        String cartKey = "";
        if (userInfoTo.getUserId()!=null){
    
    
            // gulimall:cart:1
            cartKey = CART_PREFIX + userInfoTo.getUserId();
        }else {
    
    
            cartKey = CART_PREFIX + userInfoTo.getUserKey();
        }


        // boundHashOps,这种方式的话,以下的操作都是围绕这个key的
        BoundHashOperations<String, Object, Object> operations = stringRedisTemplate.boundHashOps(cartKey);
        return operations;
    }


    /**
     * 获取购物车中所有的购物项
     * @param cartKey
     * @return
     */
    private List<CartItem> getCartItems(String cartKey){
    
    
        BoundHashOperations<String, Object, Object> hashOps = stringRedisTemplate.boundHashOps(cartKey);
        // 获取所有值,key是skuId,val是CartItem
        List<Object> values = hashOps.values();
        if (values!=null && values.size() > 0){
    
    
            List<CartItem> collect = values.stream().map((obj) -> {
    
    

                String str = (String) obj;

                return JSON.parseObject(str, CartItem.class);

            }).collect(Collectors.toList());

            return collect;
        }
        return null;
    }







    @Override
    public void clearCart(String cartKey){
    
    

        stringRedisTemplate.delete(cartKey);

    }

    @Override
    public void checkItem(Long skuId, Integer check) {
    
    

        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItem cartItem = getCartItem(skuId);
        cartItem.setCheck(check==1?true:false);
        String s = JSON.toJSONString(cartItem);

        cartOps.put(skuId.toString(),s);

    }

    @Override
    public void changeItemCount(Long skuId, Integer num) {
    
    
        CartItem cartItem = getCartItem(skuId);

        cartItem.setCount(num);

        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));
    }

    @Override
    public void deleteItem(Long skuId) {
    
    
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();

        cartOps.delete(skuId.toString());
    }
}

vo

package com.atlinxi.gulimall.cart.vo;


import java.math.BigDecimal;
import java.util.List;


/**
 * 整个购物车
 *
 * 需要计算的属性,必须重写它的get方法,保证每次获取属性都会进行计算
 */
public class Cart {
    
    

    List<CartItem> items;
    private Integer countNum; // 商品数量
    private Integer countType; // 商品类型数量
    private BigDecimal totalAmount; // 商品总价
    private BigDecimal reduce = new BigDecimal("0"); // 减免价格

    public List<CartItem> getItems() {
    
    
        return items;
    }

    public void setItems(List<CartItem> items) {
    
    
        this.items = items;
    }

    public Integer getCountNum() {
    
    
        int count = 0;
        if (items!=null && items.size() > 0){
    
    
            for (CartItem item : items) {
    
    
                count+= item.getCount();
            }
        }
        return count;
    }

    public void setCountNum(Integer countNum) {
    
    
        this.countNum = countNum;
    }

    public Integer getCountType() {
    
    
        int count = 0;
        if (items!=null && items.size() > 0){
    
    
            for (CartItem item : items) {
    
    
                count+= 1;
            }
        }
        return count;
    }

    public void setCountType(Integer countType) {
    
    
        this.countType = countType;
    }

    public BigDecimal getTotalAmount() {
    
    

        BigDecimal amount = new BigDecimal("0");

        if (items!=null && items.size() > 0){
    
    
            for (CartItem item : items) {
    
    
                // 购物车列表页只计算勾选的商品价格
                if (item.getCheck()){
    
    
                    BigDecimal totalPrice = item.getTotalPrice();
                    amount = amount.add(totalPrice);
                }

            }
        }

        // 2. 减去优惠总价
        BigDecimal subtract = amount.subtract(getReduce());

        return subtract;
    }

    public void setTotalAmount(BigDecimal totalAmount) {
    
    

        this.totalAmount = totalAmount;
    }

    public BigDecimal getReduce() {
    
    
        return reduce;
    }

    public void setReduce(BigDecimal reduce) {
    
    
        this.reduce = reduce;
    }
}












package com.atlinxi.gulimall.cart.vo;


import java.math.BigDecimal;
import java.util.List;

/**
 * 购物项内容
 */
public class CartItem {
    
    

    private Long skuId;

    // 是否被选中
    private Boolean check = true;

    private String title;
    private String image;
    // 内存 cpu
    private List<String> skuAttr;
    private BigDecimal price;
    private Integer count;
    private BigDecimal totalPrice;

    /**
     * 计算当前总价
     * @return
     */
    public BigDecimal getTotalPrice() {
    
    
        // multiply 乘以
        return this.price.multiply(new BigDecimal(this.count));
    }

    public Long getSkuId() {
    
    
        return skuId;
    }

    public void setSkuId(Long skuId) {
    
    
        this.skuId = skuId;
    }

    public Boolean getCheck() {
    
    
        return check;
    }

    public void setCheck(Boolean check) {
    
    
        this.check = check;
    }

    public String getTitle() {
    
    
        return title;
    }

    public void setTitle(String title) {
    
    
        this.title = title;
    }

    public String getImage() {
    
    
        return image;
    }

    public void setImage(String image) {
    
    
        this.image = image;
    }

    public List<String> getSkuAttr() {
    
    
        return skuAttr;
    }

    public void setSkuAttr(List<String> skuAttr) {
    
    
        this.skuAttr = skuAttr;
    }

    public BigDecimal getPrice() {
    
    
        return price;
    }

    public void setPrice(BigDecimal price) {
    
    
        this.price = price;
    }

    public Integer getCount() {
    
    
        return count;
    }

    public void setCount(Integer count) {
    
    
        this.count = count;
    }

    public void setTotalPrice(BigDecimal totalPrice) {
    
    
        this.totalPrice = totalPrice;
    }
}









package com.atlinxi.gulimall.cart.vo;

import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class SkuInfoVo {
    
    
    /**
     * skuId
     */
    @TableId
    private Long skuId;
    /**
     * spuId
     */
    private Long spuId;
    /**
     * sku名称
     */
    private String skuName;
    /**
     * sku介绍描述
     */
    private String skuDesc;
    /**
     * 所属分类id
     */
    private Long catalogId;
    /**
     * 品牌id
     */
    private Long brandId;
    /**
     * 默认图片
     */
    private String skuDefaultImg;
    /**
     * 标题
     */
    private String skuTitle;
    /**
     * 副标题
     */
    private String skuSubtitle;
    /**
     * 价格
     */
    private BigDecimal price;
    /**
     * 销量
     */
    private Long saleCount;
}
















package com.atlinxi.gulimall.cart.vo;

import lombok.Data;


@Data
public class UserInfoTo {
    
    

    private Long userId;
    // 临时登录时存放用户的唯一标识,获取临时登录时的购物车数据
    private String userKey;

    private boolean tempUser = false;
}


common常量

package com.atlinxi.common.constant;

public class CartConstant {
    
    

    public static final String TEMP_USER_COOKIE_NAME = "user-key";
    public static final int TEMP_USER_COOKIE_TIMEOUT = 60 * 60 * 24 * 30;
}

mvn

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.atlinxi.gulimall</groupId>
	<artifactId>gulimall-cart</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>gulimall-cart</name>
	<description>购物车</description>
	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>2020.0.4</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>com.atlinxi.gulimall</groupId>
			<artifactId>gulimall-common</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>
		<!--        由于SpringCloud Feign高版本不使用Ribbon而是使用spring-cloud-loadbalancer,
            所以需要引用spring-cloud-loadbalancer或者降版本-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-loadbalancer</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

只有在淋浴间,哭声才不会走出去,说闲话。

房思琪的初恋乐园
林奕含

猜你喜欢

转载自blog.csdn.net/weixin_44431371/article/details/129649329