Day425&426.购物车服务 -谷粒商城

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day425&426.购物车服务 -谷粒商城相关的知识,希望对你有一定的参考价值。

购物车服务

一、环境搭建

  • springboot初始化

  • 新增本地域名cart.achangmall.com

  • 修改对应pom配置,与此次服务间版本对应
    • 引入公共依赖
    • 修改springboot版本
    • 修改springcloud版本

  • 将提供的静态资源导入nginx服务,做到动静分离

  • 修改页面对应的资源地址,修改只选nginx的静态地址,如:

  • 主启动类com.achang.achangmall.cart.AchangmallCartApplication
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients
@EnableDiscoveryClient
public class AchangmallCartApplication {
    public static void main(String[] args) {
        SpringApplication.run(AchangmallCartApplication.class, args);
    }
}
  • 配置文件achangmall-cart/src/main/resources/application.yaml
spring:
  cloud:
    loadbalancer:
      ribbon:
        enabled: false
    nacos:
      discovery:
        server-addr: localhost:8848
  thymeleaf:
    cache: false
  redis:
    host: 192.168.109.101
    port: 6379
  application:
    name: achangmall-cart

server:
  port: 30000
  • 网关配置路由映射achangmall-gateway/src/main/resources/application.yml
        - id: cart_route
          uri: lb://achangmall-cart
          predicates:
            - Host=cart.achangmall.com
  • 启动服务测试,访问:http://cart.achangmall.com/


二、业务

  • com.achang.achangmall.cart.vo.CartVo
/**
 * 购物车1级
 */
public class CartVo {

    /**
     * 购物车子项信息
     */
    List<CartItemVo> items;

    /**
     * 商品数量
     */
    private Integer countNum;

    /**
     * 商品类型数量
     */
    private Integer countType;

    /**
     * 商品总价
     */
    private BigDecimal totalAmount;

    /**
     * 减免价格
     */
    private BigDecimal reduce = new BigDecimal("0.00");;

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

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

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

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

    public BigDecimal getTotalAmount() {
        BigDecimal amount = new BigDecimal("0");
        // 计算购物项总价
        if (!CollectionUtils.isEmpty(items)) {
            for (CartItemVo cartItem : items) {
                if (cartItem.getCheck()) {
                    amount = amount.add(cartItem.getTotalPrice());
                }
            }
        }
        // 计算优惠后的价格
        return amount.subtract(getReduce());
    }

    public BigDecimal getReduce() {
        return reduce;
    }

    public void setReduce(BigDecimal reduce) {
        this.reduce = reduce;
    }
}
  • com.achang.achangmall.cart.vo.CartItemVo
/**
 * 购物车2级--购物项
 */
public class CartItemVo {

    private Long skuId;

    private Boolean check = true;

    private String title;

    private String image;

    /**
     * 商品套餐属性
     */
    private List<String> skuAttrValues;

    private BigDecimal price;

    private Integer count;

    private BigDecimal totalPrice;

    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> getSkuAttrValues() {
        return skuAttrValues;
    }

    public void setSkuAttrValues(List<String> skuAttrValues) {
        this.skuAttrValues = skuAttrValues;
    }

    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;
    }

    /**
     * 计算当前购物项总价
     * @return
     */
    public BigDecimal getTotalPrice() {
        return this.price.multiply(new BigDecimal("" + this.count));
    }

    public void setTotalPrice(BigDecimal totalPrice) {
        this.totalPrice = totalPrice;
    }
}
  • com.achang.achangmall.cart.vo.SkuInfoVo
@Data
public class SkuInfoVo {

    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;
}
  • 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 整合springsession -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
  • com.achang.achangmall.cart.config.AchangmallSessionConfig
@Configuration
@EnableRedisHttpSession
public class AchangmallSessionConfig {
    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        //放大作用域
        cookieSerializer.setDomainName("achangmall.com");
        cookieSerializer.setCookieName("ACHANGSESSION");
        return cookieSerializer;
    }

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        //使用json存储redis,而不是默认的序列化存储
        return new GenericJackson2JsonRedisSerializer();
    }
}
  • com.achang.achangmall.cart.config.MyThreadConfig
@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {
    @Bean
    public ThreadPoolExecutor executor(ThreadPoolConfigProperties pool) {
        return new ThreadPoolExecutor(
                pool.getCoreSize(),
                pool.getMaxSize(),
                pool.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
}
  • com.achang.achangmall.cart.config.ThreadPoolConfigProperties
@ConfigurationProperties(prefix = "achangmall.thread")
@Data
public class ThreadPoolConfigProperties {
    private Integer coreSize;
    private Integer maxSize;
    private Integer keepAliveTime;
}
  • com.achang.achangmall.cart.exception.RuntimeExceptionHandler
@ControllerAdvice
public class RuntimeExceptionHandler {
    /**
     * 全局统一异常处理
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public R handler(RuntimeException exception) {
        return R.error(exception.getMessage());
    }
    @ExceptionHandler(CartExceptionHandler.class)
    public R userHandler(CartExceptionHandler exception) {
        return R.error("购物车无此商品");
    }
}
  • com.achang.achangmall.cart.exception.CartExceptionHandler
public class CartExceptionHandler extends RuntimeException {}
  • com.achang.achangmall.cart.feign.ProductFeignService
@FeignClient("achangmall-product")
public interface ProductFeignService {
    /**
     * 根据skuId查询sku信息
     */
    @RequestMapping("/product/skuinfo/info/{skuId}")
    R getInfo(@PathVariable("skuId") Long skuId);

    /**
     * 根据skuId查询pms_sku_sale_attr_value表中的信息
     */
    @GetMapping(value = "/product/skusaleattrvalue/stringList/{skuId}")
    List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId);

    /**
     * 根据skuId查询当前商品的最新价格
     */
    @GetMapping(value = "/product/skuinfo/{skuId}/price")
    BigDecimal getPrice(@PathVariable("skuId") Long skuId);
}
  • com.achang.achangmall.cart.intercept.CartIntercept
@Data
public class UserInfoTo {
    private Long userId;
    private String userKey;//一定存在
    /**
     * 是否临时用户
     */
    private Boolean tempUser = false;
}
  • 拦截器com.achang.achangmall.cart.intercept.CartIntercept
@Component
public class CartIntercept implements HandlerInterceptor {

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

    //在目标方法执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        UserInfoTo userInfoTo = new UserInfoTo();

        HttpSession session = request.getSession();
        MemberResponseVo memberResponseVo = (MemberResponseVo) session.getAttribute(LOGIN_USER);
        if (memberResponseVo != null) {
            //用户登录了
            userInfoTo.setUserId(memberResponseVo.getId());
        }
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                //user-key
                String name = cookie.getName();
                if (name.equals(TEMP_USER_COOKIE_NAME)) {
                    userInfoTo.setUserKey(cookie.getValue());
                    //标记为已是临时用户
                    userInfoTo.setTempUser(true);
                }
            }
        }
        //如果没有临时用户一定分配一个临时用户
        if (StringUtils.isEmpty(userInfoTo.getUserKey())) {
            String uuid = UUID.randomUUID().toString();
            userInfoTo.setUserKey(uuid);
        }

        //目标方法执行之前
        toThreadLocal.set(userInfoTo);
        return true;
    }


    //业务执行之后,分配临时用户来浏览器保存
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //获取当前用户的值
        UserInfoTo userInfoTo = toThreadLocal.get();

        //如果没有临时用户一定保存一个临时用户
        if (!userInfoTo.getTempUser()) {
            //创建一个cookie
            Cookie cookie = new Cookie(TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
            //扩大作用域
            cookie.setDomain("achangmall.com");
            //设置过期时间
            cookie.setMaxAge(TEMP_USER_COOKIE_TIMEOUT);
            response.addCookie(cookie);
        }
        toThreadLocal.remove();
    }
}
  • com.achang.achangmall.cart.to.UserInfoTo
@Data
public class UserInfoTo {
    private Long userId;
    private String userKey;//一定存在
    /**
     * 是否临时用户
     */
    private Boolean tempUser = false;
}
  • com.achang.common.constant.CartConstant
public class CartConstant {
    public final static String TEMP_USER_COOKIE_NAME = "user-key";
    public final static int TEMP_USER_COOKIE_TIMEOUT = 60*60*24*30;
    public final static String CART_PREFIX = "achangmall:cart:";
}
  • com.achang.achangmall.cart.intercept.AchangmallWebConfig配置拦截器,让其生效
@Configuration
public class AchangmallWebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(以上是关于Day425&426.购物车服务 -谷粒商城的主要内容,如果未能解决你的问题,请参考以下文章

JDK 19 / Java 19 正式发布,虚拟线程来了!

熊猫将多行作为一列,添加特定列

*Leetcode 426 & 剑指 Offer 36. 二叉搜索树与双向链表

*Leetcode 426 & 剑指 Offer 36. 二叉搜索树与双向链表

web day25 web day24 小项目练习图书商城, 购物车模块,订单模块,支付(易宝支付)

红色小恐龙团队--冲刺DAY3