当前位置: 首页 > article >正文

046 购物车

文章目录

  • cubemall-product
    • SkuSaleAttrValueController.java
    • SkuSaleAttrValueDao.java
    • SkuSaleAttrValueService.java
    • SkuSaleAttrValueServiceImpl.java
    • SkuSaleAttrValueDao.xml
    • SkuInfoController.java
    • SkuInfoDao.java
    • SkuInfoService.java
    • application.yml
    • pom.xml
  • cubemall-auth
    • LoginController.java
    • UserService.java
    • UserServiceImpl.java
    • application.yml
    • CubemallAuthApplication.java
    • pom.xml
  • cubemall-cart
    • config
      • SpringBeansUtils.java
      • ThreadPoolConfig.java
      • ThreadPoolProperties.java
    • dto
      • UserInfoDTO.java
    • entity
      • UserEntity.java
    • interceptor
      • CartInterceptor.java
      • InterceptorConfig.java
    • rpc
      • AuthRemoteClient.java
      • ProductRemoteClient.java
    • service
      • CartService.java
      • CartServiceImpl.java
    • utils
      • Constants.java
      • CookieUtils.java
    • vo
      • CartItemVo.java
      • CartVo.java
      • SkuInfoVo.java
    • web
      • CartController.java
    • application.yml
    • CubemallCartApplication.java
    • pom.xml

购物车添加的时候用户未登录,用户已经登录,如何添加购物车?
用户是否登录,都可以添加购物车,用户未登录,添加到cookie,Redis都可以,前提是在cookie中都需要存储一个信息(直接把购物车数据存储在cookie,在cookie存储一个用户身份信息,然后把购物车数据还是存储在服务端)

购物车对象设计
CartVo
totalAccount 商品总价
countNum 商品数量
List<CartItemVo> 商品详细

CartItemVo
skuId
title
price
image
List<String> skuAttr
count
check=false
totalPrice

购物车

SELECT CONCAT(attr_name, ": ", attr_value) FROM tb_sku_sale_attr_value WHERE sku_id = 15

sql
sql2
getter方法的优先级:当Thymeleaf解析 ${item.totalPrice} 时,它实际上是在调用 item 对象的 getTotalPrice() 方法(如果这个方法存在的话)。这是因为在Java Bean规范中,属性的访问通常是通过getter方法进行的。因此,即使 CartItemVo 类中有一个名为 totalPrice 的字段,Thymeleaf也会优先尝试调用 getTotalPrice() 方法来获取值

cubemall-product

SkuSaleAttrValueController.java

package com.xd.cubemall.product.controller;

import java.util.Arrays;
import java.util.List;
import java.util.Map;


import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.common.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.xd.cubemall.product.entity.SkuSaleAttrValueEntity;
import com.xd.cubemall.product.service.SkuSaleAttrValueService;




/**
 * sku销售属性值
 *
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 07:57:20
 */
@RestController
@RequestMapping("product/skusaleattrvalue")
public class SkuSaleAttrValueController {
    @RequestMapping("/sale/{skuId}")
    public List<String> getSaleAttrsString(@PathVariable("skuId") Long skuId){
        List<String> saleAttrsString = skuSaleAttrValueService.getSaleAttrsString(skuId);
        return saleAttrsString;
    }
}

SkuSaleAttrValueDao.java

package com.xd.cubemall.product.dao;

import com.xd.cubemall.product.entity.SkuSaleAttrValueEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xd.cubemall.product.vo.SkuItemSaleAttrVo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * sku销售属性值
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
@Mapper
public interface SkuSaleAttrValueDao extends BaseMapper<SkuSaleAttrValueEntity> {
    public List<String> getSaleAttrsString(Long skuId);	
}

SkuSaleAttrValueService.java

package com.xd.cubemall.product.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.product.entity.SkuSaleAttrValueEntity;
import com.xd.cubemall.product.vo.SkuItemSaleAttrVo;

import java.util.List;
import java.util.Map;

/**
 * sku销售属性值
 *
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
public interface SkuSaleAttrValueService extends IService<SkuSaleAttrValueEntity> {

    /**
     * 根据skuID查询销售属性
     * @param skuId
     * @return
     */
    public List<String> getSaleAttrsString(Long skuId);
}

SkuSaleAttrValueServiceImpl.java

package com.xd.cubemall.product.service.impl;

import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.common.utils.Query;
import com.xd.cubemall.product.vo.SkuItemSaleAttrVo;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;


import com.xd.cubemall.product.dao.SkuSaleAttrValueDao;
import com.xd.cubemall.product.entity.SkuSaleAttrValueEntity;
import com.xd.cubemall.product.service.SkuSaleAttrValueService;


@Service("skuSaleAttrValueService")
public class SkuSaleAttrValueServiceImpl extends ServiceImpl<SkuSaleAttrValueDao, SkuSaleAttrValueEntity> implements SkuSaleAttrValueService {
    /**
     * 根据skuID查询销售属性
     * @param skuId
     * @return
     */

    @Override
    public List<String> getSaleAttrsString(Long skuId) {

        List<String> saleAttrsString = this.baseMapper.getSaleAttrsString(skuId);

        return saleAttrsString;
    }
}

SkuSaleAttrValueDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xd.cubemall.product.dao.SkuSaleAttrValueDao">
    <select id="getSaleAttrsString" resultType="java.lang.String">
        SELECT
            CONCAT( attr_name, ": ", attr_value )
        FROM
            tb_sku_sale_attr_value
        WHERE
            sku_id = #{skuId}
    </select>

</mapper>

SkuInfoController.java

package com.xd.cubemall.product.controller;

import java.util.Arrays;
import java.util.Map;


import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.product.vo.SpuSaveVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.xd.cubemall.product.entity.SkuInfoEntity;
import com.xd.cubemall.product.service.SkuInfoService;




/**
 * sku
 *
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 07:57:20
 */
@RestController
@RequestMapping("product/skuinfo")
public class SkuInfoController {
    @Autowired
    private SkuInfoService skuInfoService;

    /**
     * 信息
     */
    @RequestMapping("/info/{id}")
    //@RequiresPermissions("product:skuinfo:info")
    public R info(@PathVariable("id") Long id){
		SkuInfoEntity skuInfo = skuInfoService.getById(id);

        return R.ok().put("skuInfo", skuInfo);
    }

}

SkuInfoDao.java

package com.xd.cubemall.product.dao;

import com.xd.cubemall.product.entity.SkuInfoEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

/**
 * sku??Ϣ
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
@Mapper
public interface SkuInfoDao extends BaseMapper<SkuInfoEntity> {
	
}

SkuInfoService.java

package com.xd.cubemall.product.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.product.entity.SkuInfoEntity;
import com.xd.cubemall.product.entity.SpuInfoEntity;
import com.xd.cubemall.product.vo.SkuItemVo;
import com.xd.cubemall.product.vo.SpuSaveVo;

import java.util.Map;

/**
 * sku??Ϣ
 *
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
public interface SkuInfoService extends IService<SkuInfoEntity> {


}

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cube_goods?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        ip: 127.0.0.1
  application:
    name: cubemall-product
server:
  port: 8081
mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      logic-delete-value: 1 #逻辑删除值(默认为1)
      logic-not-delete-value: 0 #逻辑未删除值(默认为0)

logging:
  level:
    com.xd.cubemall: debug

# 线程池相关的配置
cubemall:
  thread:
    core-pool-size: 20
    max-pool-size: 200
    keep-alive-time: 10

pom.xml

<?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.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xd.cubemall</groupId>
    <artifactId>cubemall-product</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cubemall-product</name>
    <description>cubemall-product</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>
    <dependencies>

        <!--引入common公共模块-->
        <dependency>
            <groupId>com.xd.cubemall</groupId>
            <artifactId>cubemall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>


        <!--添加模板技术渲染页面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>


        <!--阿里云OSS-->
<!--        <dependency>-->
<!--            <groupId>com.aliyun.oss</groupId>-->
<!--            <artifactId>aliyun-sdk-oss</artifactId>-->
<!--            <version>3.17.4</version>-->
<!--        </dependency>-->


    </dependencies>
    <dependencyManagement>
        <dependencies>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>



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

cubemall-auth

LoginController.java

package com.xd.cubemall.user.web;

import com.alibaba.fastjson.JSON;
import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.user.pojo.UserDTO;
import com.xd.cubemall.user.service.UserService;
import com.xd.cubemall.user.utils.Constants;
import com.xd.cubemall.user.utils.CookieUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class LoginController {

    // 注入service服务对象
    @Autowired
    private UserService userService;

    /**
     * 根据token查询用户身份信息
     * "http://localhost:8082/user/info/"+token
     */
    @ResponseBody
    @RequestMapping("/user/info/{token}")
    public String userInfo(@PathVariable String token,String callback){
        // 调用服务方法,查询用户身份信息
        R r = userService.userInfo(token);

        // 判断是否是跨域
        if(StringUtils.isBlank(callback)){
            return JSON.toJSONString(r);
        }

        // 是跨域
        return callback + "("+JSON.toJSONString(r)+")";
    }

}

UserService.java

package com.xd.cubemall.user.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.user.entity.UserEntity;
import com.xd.cubemall.user.pojo.UserDTO;

import java.util.Map;

/**
 * 会员
 *
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-11-20 14:45:16
 */
public interface UserService extends IService<UserEntity> {

    /**
     * 根据token查询用户身份信息
     * "http://localhost:8082/user/info/"+token
     */
    R userInfo(String token);

}

UserServiceImpl.java

package com.xd.cubemall.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.user.pojo.UserDTO;
import com.xd.cubemall.user.utils.Constants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.common.utils.Query;

import com.xd.cubemall.user.dao.UserDao;
import com.xd.cubemall.user.entity.UserEntity;
import com.xd.cubemall.user.service.UserService;


@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {

    // 注入redis服务
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 根据token查询用户身份信息
     * "http://localhost:8082/user/info/"+token
     */
    @Override
    public R userInfo(String token) {

        // 根据token从redis服务中查询用户身份信息
        String userJson = redisTemplate.opsForValue().get(Constants.REDIS_LOGIN_KEY + token);
        // 把json字符串转换对象
        if(StringUtils.isBlank(userJson)){
            return R.error("用户身份已失效");
        }
        // 用户身份存在
        UserEntity userEntity = JSON.parseObject(userJson,UserEntity.class);

        // 返回结果
        return R.ok(userEntity);


    }
	

}

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cube_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        ip: 127.0.0.1
  redis:
    port: 6379
    host: 127.0.0.1
  application:
    name: cubemall-auth
server:
  port: 8082
mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      logic-delete-value: 1 #逻辑删除值(默认为1)
      logic-not-delete-value: 0 #逻辑未删除值(默认为0)

logging:
  level:
    com.xd.cubemall: debug

github:
  # 客户端ID
  clientId: 
  # 客户端秘钥
  clientSecret: 
  # 用户授权地址(返回授权码)
  authorizationUrl: https://github.com/login/oauth/authorize
  # 回调地址,获取access_token
  redirectUrl: http://localhost:8082/auth2/success
  # 认证服务器生成access_token
  accessTokenUrl: https://github.com/login/oauth/access_token
  # 获取用户身份信息
  userInfoUrl: https://api.github.com/user



CubemallAuthApplication.java

package com.xd.cubemall.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CubemallAuthApplication {

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

}

pom.xml

<?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 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xd.cubemall</groupId>
    <artifactId>cubemall-auth</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>cubemall-auth</name>
    <description>cubemall-auth</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>
    <dependencies>

        <!--引入common公共模块-->
        <dependency>
            <groupId>com.xd.cubemall</groupId>
            <artifactId>cubemall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>




        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>


        <!--添加模板技术渲染页面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>


        <!--阿里云OSS-->
        <!--        <dependency>-->
        <!--            <groupId>com.aliyun.oss</groupId>-->
        <!--            <artifactId>aliyun-sdk-oss</artifactId>-->
        <!--            <version>3.17.4</version>-->
        <!--        </dependency>-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>


    </dependencies>
    <dependencyManagement>
        <dependencies>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>



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


cubemall-cart

config

SpringBeansUtils.java

package com.xd.cubemall.cart.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringBeansUtils implements ApplicationContextAware {


    //定一个spring上下文的变量
    private static ApplicationContext applicationContext;

    @Override
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringBeansUtils.applicationContext = applicationContext;

    }


    //获取上下文对象
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }


    //根据名称获取spring对象
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    //根据class类获取对象
    public static Object getBeanClass(Class clazz){
        return getApplicationContext().getBean(clazz);
    }





}

ThreadPoolConfig.java

package com.xd.cubemall.cart.config;


import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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


@EnableConfigurationProperties(ThreadPoolProperties.class)
@Configuration
public class ThreadPoolConfig {


    @Bean
    public ThreadPoolExecutor executor(ThreadPoolProperties poolProperties){
        return new ThreadPoolExecutor(
                poolProperties.getCoreSize(),
                poolProperties.getMaxSize(),
                poolProperties.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }

}

ThreadPoolProperties.java

package com.xd.cubemall.cart.config;


import lombok.Data;
import lombok.ToString;

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


@ConfigurationProperties(prefix = "cubemall.thread")
@Data
@ToString
public class ThreadPoolProperties {
    private Integer coreSize;
    private Integer maxSize;
    private Integer keepAliveTime;


}

dto

UserInfoDTO.java

package com.xd.cubemall.cart.dto;

import lombok.Data;
import lombok.ToString;

@ToString
@Data
public class UserInfoDTO {

    private Long userId; //用户登录使用的
    private String userKey; //标识临时用户的key,未登录使用这个值

    private Boolean tempUser = false;

}

entity

UserEntity.java

package com.xd.cubemall.cart.entity;


import lombok.Data;

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

/**
 * 会员
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-11-20 14:45:16
 */
@Data
public class UserEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * id
	 */

	private Long id;
	/**
	 * 会员等级id
	 */
	private Long levelId;
	/**
	 * 用户名
	 */
	private String username;
	/**
	 * 密码
	 */
	private String password;
	/**
	 * 昵称
	 */
	private String nickname;
	/**
	 * 手机号码
	 */
	private String mobile;
	/**
	 * 邮箱
	 */
	private String email;
	/**
	 * 头像
	 */
	private String header;
	/**
	 * 性别
	 */
	private Integer gender;
	/**
	 * 生日
	 */
	private Date birth;
	/**
	 * 所在城市
	 */
	private String city;
	/**
	 * 职业
	 */
	private String job;
	/**
	 * 个性签名
	 */
	private String sign;
	/**
	 * 用户来源
	 */
	private Integer sourceType;
	/**
	 * 积分
	 */
	private Integer integration;
	/**
	 * 启用状态
	 */
	private Integer status;
	/**
	 * 注册时间
	 */
	private Date createTime;

}

interceptor

CartInterceptor.java

package com.xd.cubemall.cart.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xd.cubemall.cart.config.SpringBeansUtils;
import com.xd.cubemall.cart.dto.UserInfoDTO;
import com.xd.cubemall.cart.entity.UserEntity;
import com.xd.cubemall.cart.rpc.AuthRemoteClient;
import com.xd.cubemall.cart.utils.Constants;
import com.xd.cubemall.cart.utils.CookieUtils;
import com.xd.cubemall.common.utils.R;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class CartInterceptor implements HandlerInterceptor {


    //创建一个ThreadLocal对象,用来存储用户身份信息
    //主要用来实现数据隔离,填充的数据只属于当前线程,高并发用户情况下,每一个用户只维护自己的用户信息即可
    public static ThreadLocal<UserInfoDTO> dtoThreadLocal = new ThreadLocal<>();


    // 注入远程调用rpc接口
    AuthRemoteClient remoteClient = (AuthRemoteClient) SpringBeansUtils.getBeanClass(AuthRemoteClient.class);

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

        //创建对象,判断用户处于状态
        UserInfoDTO userInfoDTO = new UserInfoDTO();


        //获取cookie中token,判断token是否存在,如果token不存在,表示用户没有登录
        String token = CookieUtils.getCookieValue(request, Constants.COOKIE_LOGIN_KEY, true);
        if(StringUtils.isNotBlank(token)){
            //根据接口远程调用认证服务,获取用户身份信息
            String jsonUser = remoteClient.userInfoByToken(token);

            if(StringUtils.isNotBlank(jsonUser)){
                //转换对象
                R r = JSON.parseObject(jsonUser, R.class);

                // 判断
                if(r.getCode() == 0){
                    //获取用户身份信息对象
                    //UserEntity result = (UserEntity) r.get("result");
                    UserEntity result = r.getData("result", new TypeReference<UserEntity>() {
                    });


                    //用户处于登录状态
                    userInfoDTO.setUserId(result.getId());

                }
            }



        }

        // 如果没有登录,标记此用户为一个临时用户
        //1.从cookie中获取购物车临时用户信息,判断此用户是否存在
        String userKey = CookieUtils.getCookieValue(request, Constants.COOKIE_TEMP_CART_KEY, true);
        //如果这个key存在,标记此用户为一个临时用户
        if(StringUtils.isNotBlank(userKey)){
            userInfoDTO.setUserKey(userKey);
            userInfoDTO.setTempUser(true);
        }

        //如果用户第一次来网站,没有登录,必须分配一个临时用户
        if(StringUtils.isBlank(userInfoDTO.getUserKey())){
            String userToken = UUID.randomUUID().toString();
            userInfoDTO.setUserKey(userToken);
        }

        //使用ThreadLocal存储用户信息
        dtoThreadLocal.set(userInfoDTO);



        //用户无论登录与否

        return true;
    }


    /**
     * 业务执行之后完成执行的方法
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //获取用户当前身份信息
        UserInfoDTO userInfoDTO = dtoThreadLocal.get();
        //如果没有临时用户,需要保存临时用户
        if(!userInfoDTO.getTempUser()){
            //把用户身份信息存储在cookie
            CookieUtils.setCookie(request,
                    response,
                    Constants.COOKIE_TEMP_CART_KEY,
                    userInfoDTO.getUserKey(),Constants.COOKIE_TEMP_CART_KEY_TIMEOUT,
                    true);

        }
    }


    /**
     * 整个任务全部完成,执行方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

InterceptorConfig.java

package com.xd.cubemall.cart.interceptor;

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 InterceptorConfig implements WebMvcConfigurer {

    //实现拦截器注册
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new CartInterceptor())
                .addPathPatterns("/**");
    }
}

rpc

AuthRemoteClient.java

package com.xd.cubemall.cart.rpc;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient("cubemall-auth")
public interface AuthRemoteClient {
    /**
     * 根据token查询用户信息
     */
    @RequestMapping("/user/info/{token}")
    public String userInfoByToken(@PathVariable("token") String token);

}

ProductRemoteClient.java

package com.xd.cubemall.cart.rpc;


import com.xd.cubemall.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient("cubemall-product")
public interface ProductRemoteClient {

    @RequestMapping("product/skuinfo/info/{skuId}")
    public R getSkuInfo(@PathVariable("skuId") Long skuId);

    // 查询商品规格属性
    @RequestMapping("/product/skusaleattrvalue/sale/{skuId}")
    public List<String> getSkuSaleAttrs(@PathVariable("skuId") Long skuId);

}

service

CartService.java

package com.xd.cubemall.cart.service;


import com.xd.cubemall.cart.vo.CartItemVo;
import com.xd.cubemall.cart.vo.CartVo;

/**
 * 购物车实现服务端接口
 */
public interface CartService {

    /**
     * 购物车添加的接口
     */
    public CartItemVo addToCart(Long skuId,Integer num);

    // 查询此次添加的购物车数据
    CartItemVo getCartItemData(Long skuId);

    /**
     * 购物车结算页面 购物车列表页面
     */
    public CartVo getCartList();

    /**
     * 购物车商品删除实现
     */
     public void deleteCart(Long skuId);

    /**
     * 购物车数量的改变
     * @param skuId
     * @param num
     */
    public void changeItemNum(Long skuId,Integer num);


}

CartServiceImpl.java

package com.xd.cubemall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xd.cubemall.cart.dto.UserInfoDTO;
import com.xd.cubemall.cart.interceptor.CartInterceptor;
import com.xd.cubemall.cart.rpc.ProductRemoteClient;
import com.xd.cubemall.cart.service.CartService;
import com.xd.cubemall.cart.utils.Constants;
import com.xd.cubemall.cart.vo.CartItemVo;
import com.xd.cubemall.cart.vo.CartVo;
import com.xd.cubemall.cart.vo.SkuInfoVo;
import com.xd.cubemall.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

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 {

    // 注入redistemplate
    @Autowired
    private RedisTemplate redisTemplate;

    // 注入线程池对象
    @Autowired
    private ThreadPoolExecutor executor;

    // 注入商品远程feign调用接口
    @Autowired
    private ProductRemoteClient productRemoteClient;


    /**
     * 购物车数量的改变
     * @param skuId
     * @param num
     */
    @Override
    public void changeItemNum(Long skuId, Integer num) {
        CartItemVo cartItemData = this.getCartItemData(skuId);
        // 添加商品数量
        cartItemData.setCount(num);
        // 获取购物车数据
        BoundHashOperations<String, Long, Object> cartFromRedis = this.getCartFromRedis();
        // 把购物车数据转换JOSN字符串
        String cartJSON = JSON.toJSONString(cartItemData);
        // 放回购物车数据
        cartFromRedis.put(skuId,cartJSON);
    }



    /**
     * 购物车添加的接口
     * 业务步骤:
     * 1.获取购物车中已经存在的数据
     * 2.判断当前添加的商品是否在购物车中已经存在,如果已经存在这个商品,此时添加商品的数量相加即可
     * 3.如果此时购物车不存在这个商品,需要新添加商品进入购物车
     */
    @Override
    public CartItemVo addToCart(Long skuId, Integer num) {

        // 1.获取购物车数据,不论用户是否登录
        BoundHashOperations<String,Long,Object> cartOps = this.getCartFromRedis();

        // 2.判断当前添加的商品是否在购物车中已经存在
        String str = (String) cartOps.get(skuId);
        if(StringUtils.isBlank(str)){
            // 此时添加的商品在购物车中并不存在
            // 新创建购物车商品对象,组装购物车数据
            CartItemVo itemVo = new CartItemVo();
            // 异步编排
            CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
                //远程调用商品服务,获取商品信息
                R r = productRemoteClient.getSkuInfo(skuId);
                //获取购物车数据
                SkuInfoVo skuInfoVo = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                });

                // 给购物车数据赋值
                itemVo.setTitle(skuInfoVo.getSkuTitle());
                itemVo.setSkuId(skuInfoVo.getId());
                itemVo.setImage(skuInfoVo.getSkuDefaultImg());
                itemVo.setPrice(skuInfoVo.getPrice());
                itemVo.setCount(num);

            }, executor);

            // 查询商品规格属性值
            CompletableFuture<Void> attrFuture = CompletableFuture.runAsync(() -> {
                // 远程调用商品服务接口,获取属性组合
                List<String> skuSaleAttrs = productRemoteClient.getSkuSaleAttrs(skuId);
                itemVo.setSkuAttr(skuSaleAttrs);
            }, executor);

            // 等到所有的异步任务全部完成
            try {
                CompletableFuture.allOf(skuInfoFuture,attrFuture).get();

                // 把组装的数据添加到购物车:redis
                String cartJson = JSON.toJSONString(itemVo);
                cartOps.put(skuId,cartJson);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }

            return itemVo;


        }else{
            // 此时添加的商品在购物车中存在
            CartItemVo itemVo = JSON.parseObject(str, CartItemVo.class);
            // 商品数量相加即可
            itemVo.setCount(itemVo.getCount() + num);

            // 更新购物车中数据即可:redis
            String cartJsonStr = JSON.toJSONString(itemVo);
            // 放入购物车即可
            cartOps.put(skuId,cartJsonStr);

            return itemVo;

        }





    }


    /**
     * 获取购物车数据
     * @return
     */
    private BoundHashOperations<String, Long, Object> getCartFromRedis() {
        //1.获取购物车中已经存在的数据
        UserInfoDTO userInfoDTO = CartInterceptor.dtoThreadLocal.get();

        String cartKey = "";

        //判断是否登录
        if(userInfoDTO.getUserId()!=null){
            cartKey = Constants.CART_PREFIX + userInfoDTO.getUserId();
        }else{
            cartKey = Constants.CART_PREFIX + userInfoDTO.getUserKey();
        }

        //从redis中获取购物车相关的数据
        BoundHashOperations<String,Long,Object> cartOps = redisTemplate.boundHashOps(cartKey);

        return cartOps;
    }


    // 查询此次添加的购物车数据

    @Override
    public CartItemVo getCartItemData(Long skuId) {

        BoundHashOperations<String, Long, Object> cartFromRedis = this.getCartFromRedis();
        // 根据skuId获取用户数据
        String itemJson = (String) cartFromRedis.get(skuId);
        
        // 把json字符串变成一个对象
        CartItemVo cartItemVo = JSON.parseObject(itemJson, CartItemVo.class);



        return cartItemVo;
    }

    /**
     * 购物车结算页面 购物车列表页面
     * @return
     */

    @Override
    public CartVo getCartList() {

        // 创建购物车数据对象
        CartVo cartVo = new CartVo();
        // 获取用户信息
        UserInfoDTO userInfoDTO = CartInterceptor.dtoThreadLocal.get();
        // 判断用户是登录还是未登录
        if(userInfoDTO.getUserId() != null){
            // 用户登录状态
            String loginCartKey = Constants.CART_PREFIX + userInfoDTO.getUserId();
            //  临时用户key,在用户登录状态,合并购物车数据
            String tempCartKey = Constants.CART_PREFIX + userInfoDTO.getUserKey();

            // 获取临时用户购物车数据
            List<CartItemVo> tempCartList = this.getCartListFromRedis(tempCartKey);
            // 判断临时购物车数据是否存在,如果存在,合并购物车
            if(tempCartList != null){
                for (CartItemVo cartItemVo : tempCartList) {
                    // 添加购物车方法,直接合并即可
                    addToCart(cartItemVo.getSkuId(), cartItemVo.getCount());
                }

                // 清除掉临时购物车
                redisTemplate.delete(tempCartKey);

            }
            // 用户处于登录状态,获取合并后购物车的所有的数据
            List<CartItemVo> cartList = getCartListFromRedis(loginCartKey);
            cartVo.setItems(cartList);

        }else{
            // 用户未登录
            String tempCartKey = Constants.CART_PREFIX + userInfoDTO.getUserKey();
            // 获取临时购物车数据
            List<CartItemVo> cartList = this.getCartListFromRedis(tempCartKey);
            cartVo.setItems(cartList);

        }


        return cartVo;
    }


    /**
     * 根据key获取购物车数据
     * @param cartKey
     * @return
     */
    private List<CartItemVo> getCartListFromRedis(String cartKey) {

        // 获取的临时购物车所有的数据
        BoundHashOperations<String,Long,Object> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        // 获取所有的值
        List<Object> values = boundHashOperations.values();
        // 判断这个值是否存在
        if(values!=null && values.size()>0){
            List<CartItemVo> collectList =  values.stream().map(obj -> {
                // 把字符串数据转换成对象
                String cartItemJson = (String)obj;
                CartItemVo cartItemVo = JSON.parseObject(cartItemJson, CartItemVo.class);
                return cartItemVo;
            }).collect(Collectors.toList());
            return collectList;
        }
        return null;

    }


    /**
     * 购物车商品删除实现
     * @param skuId
     */
    @Override
    public void deleteCart(Long skuId) {
        // 获取购物车数据对象
        BoundHashOperations<String, Long, Object> cartFromRedis = this.getCartFromRedis();
        // 清除购物车中数据
        cartFromRedis.delete(skuId);
    }
}

utils

Constants.java

package com.xd.cubemall.cart.utils;

public class Constants {
    //用户身份信息存储的key:redis存储的
    public static final String REDIS_LOGIN_KEY="CUBE_LOGIN_TOKEN:";

    // 存储cookie中token的key
    public static final String COOKIE_LOGIN_KEY="COOKIE_LOGIN_KEY";

    //存储临时用户cookie key
    public static final String COOKIE_TEMP_CART_KEY="TEMP_USER_KEY";

    //存储临时用户cookie key 超时时间
    public static final int COOKIE_TEMP_CART_KEY_TIMEOUT= 24*60*60;


    //购物车key
    public static final String CART_PREFIX="CART_KEY:";



}

CookieUtils.java

package com.xd.cubemall.cart.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * 
 * Cookie 工具类
 *
 */
public final class CookieUtils {

    protected static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);

    /**
     * 得到Cookie的值, 不编码
     * 
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        return getCookieValue(request, cookieName, false);
    }

    /**
     * 得到Cookie的值,
     * 
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    if (isDecoder) {
                        retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                    } else {
                        retValue = cookieList[i].getValue();
                    }
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            logger.error("Cookie Decode Error.", e);
        }
        return retValue;
    }

    /**
     * 得到Cookie的值,
     * 
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            logger.error("Cookie Decode Error.", e);
        }
        return retValue;
    }

    /**
     * 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue) {
        setCookie(request, response, cookieName, cookieValue, -1);
    }

    /**
     * 设置Cookie的值 在指定时间内生效,但不编码
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage) {
        setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
    }

    /**
     * 设置Cookie的值 不设置生效时间,但编码
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, boolean isEncode) {
        setCookie(request, response, cookieName, cookieValue, -1, isEncode);
    }

    /**
     * 设置Cookie的值 在指定时间内生效, 编码参数
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage, boolean isEncode) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
    }

    /**
     * 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage, String encodeString) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
    }

    /**
     * 删除Cookie带cookie域名
     */
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName) {
        doSetCookie(request, response, cookieName, "", -1, false);
    }

    /**
     * 设置Cookie的值,并使其在指定时间内生效
     * 
     * @param cookieMaxage cookie生效的最大秒数
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else if (isEncode) {
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request)// 设置域名的cookie
                cookie.setDomain(getDomainName(request));
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
            logger.error("Cookie Encode Error.", e);
        }
    }

    /**
     * 设置Cookie的值,并使其在指定时间内生效
     * 
     * @param cookieMaxage cookie生效的最大秒数
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else {
                cookieValue = URLEncoder.encode(cookieValue, encodeString);
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request)// 设置域名的cookie
                cookie.setDomain(getDomainName(request));
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
            logger.error("Cookie Encode Error.", e);
        }
    }

    /**
     * 得到cookie的域名
     */
    private static final String getDomainName(HttpServletRequest request) {
        String domainName = null;

        String serverName = request.getRequestURL().toString();
        if (serverName == null || serverName.equals("")) {
            domainName = "";
        } else {
            serverName = serverName.toLowerCase();
            serverName = serverName.substring(7);
            final int end = serverName.indexOf("/");
            serverName = serverName.substring(0, end);
            final String[] domains = serverName.split("\\.");
            int len = domains.length;
            if (len > 3) {
                // www.xxx.com.cn
                domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
            } else if (len <= 3 && len > 1) {
                // xxx.com or xxx.cn
                domainName = "." + domains[len - 2] + "." + domains[len - 1];
            } else {
                domainName = serverName;
            }
        }

        if (domainName != null && domainName.indexOf(":") > 0) {
            String[] ary = domainName.split("\\:");
            domainName = ary[0];
        }
        return domainName;
    }

}

vo

CartItemVo.java

package com.xd.cubemall.cart.vo;


import lombok.Data;
import lombok.ToString;

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

@ToString
@Data
public class CartItemVo {

    // sku商品id
    private Long skuId;

    //商品标题
    private String title;

    //商品图片
    private String image;

    //商品属性
    private List<String> skuAttr;

    //商品价格
    private BigDecimal price;

    //商品数量
    private Integer count;

    //总价格
    private BigDecimal totalPrice;

    // 是否选中
    private boolean check = true;


    //计算单价*数量的商品总价
    public BigDecimal getTotalPrice(){
        return this.price.multiply(new BigDecimal(count+""));
    }


}

CartVo.java

package com.xd.cubemall.cart.vo;


import lombok.Data;
import lombok.ToString;

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

@ToString
@Data
public class CartVo {

    //商品详细选项
    private List<CartItemVo> items;

    //商品总数量
    private Integer countNum;

    //商品总价
    private BigDecimal totalAccount;

    //属性,减免价格
    private BigDecimal reducePrice = new BigDecimal("0");


    //计算总价格
    public BigDecimal getTotalAccount() {

        // 定义变量实现总价的叠加
        BigDecimal account = new BigDecimal("0");

        // 循环获取商品详情,计算总价
        if(items!=null && items.size()>0){

            for (CartItemVo item : items) {
                //判断商品是否被选中
                if(item.isCheck()){
                    //获取单个商品总价
                    BigDecimal totalPrice = item.getTotalPrice();
                    account = account.add(totalPrice);

                }




            }
        }

        //减免优惠价格
        BigDecimal subtract = account.subtract(getReducePrice());




        return subtract;
    }



    public Integer getCountNum(){
        Integer count = 0;
        if(items!=null && items.size()>0){
            for (CartItemVo item: items) {
                //判断商品是否被选中
                if(item.isCheck()){
                    count += item.getCount();
                }


            }
        }
        return count;
    }





}

SkuInfoVo.java

package com.xd.cubemall.cart.vo;


import lombok.Data;

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

/**
 * sku??Ϣ
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
@Data
public class SkuInfoVo implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 自增ID
	 */

	private Long id;
	/**
	 * spuId
	 */
	private Long spuId;
	/**
	 * sku名称
	 */
	private String skuName;
	/**
	 * sku描述
	 */
	private String skuDesc;
	/**
	 * 分类id
	 */
	private Long categoryId;
	/**
	 * 品牌id
	 */
	private Long brandId;
	/**
	 * 默认sku图片
	 */
	private String skuDefaultImg;
	/**
	 * sku名称
	 */
	private String skuTitle;
	/**
	 * sku子名称
	 */
	private String skuSubtitle;
	/**
	 * 价格
	 */
	private BigDecimal price;
	/**
	 * 销售数量
	 */
	private Long saleCount;

}

web

CartController.java

package com.xd.cubemall.cart.web;


import com.xd.cubemall.cart.service.CartService;
import com.xd.cubemall.cart.vo.CartItemVo;
import com.xd.cubemall.cart.vo.CartVo;
import com.xd.cubemall.common.utils.R;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
public class CartController {

    // 注入购物车服务对象
    @Autowired
    private CartService cartService;

    /**
     * 购物车数量的改变
     * @param skuId
     * @param num
     * @return
     */

    @GetMapping("cart/changeItem")
    public String changeItemNum(@RequestParam("skuId") Long skuId,
                                @RequestParam("num") Integer num){

        //调用服务端更新购物车数量方法
        cartService.changeItemNum(skuId,num);

        return "redirect:http://localhost:8083/cart/list";
    }


    /**
     * 添加购物车
     * @param num
     * @param skuId
     * @param attributes
     * @return
     */
    @RequestMapping("/cart/addCart")
    public String addCart(@RequestParam("num") Integer num,
                          @RequestParam("skuId") Long skuId,
                          RedirectAttributes attributes){
        //调用添加购物车方法,实现购物车添加
        CartItemVo itemVo = cartService.addToCart(skuId, num);

        // 属性将会自动追加重定向请求后面
        attributes.addAttribute("skuId",skuId);

        return "redirect:http://localhost:8083/cart/successPage";
    }


    /**
     * 跳转到添加购物车成功的页面
     */
    @GetMapping("/cart/successPage")
    public String successPage(@RequestParam("skuId") Long skuId, Model model){

        // 查询此次添加的购物车数据
        CartItemVo cartItem = cartService.getCartItemData(skuId);

        // 放入作用域
        model.addAttribute("item", cartItem);


        return "success-cart";
    }


    /**
     * 购物车结算页面,购物车列表页面
     */
    @RequestMapping("cart/list")
    public String getCartList(Model model){

        // 调用服务层方法
        CartVo cartVo = cartService.getCartList();

        model.addAttribute("cart",cartVo);

        return "cart";
    }


    /**
     * 购物车商品删除实现
     */
    @RequestMapping("/cart/delete")
    public String deleteCart(@RequestParam("skuId") Long skuId){


        // 调用服务层接口,实现购物车数据删除即可
        cartService.deleteCart(skuId);

        return "redirect:http://localhost:8083/cart/list";

    }


}

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        ip: 127.0.0.1
  redis:
    port: 6379
    host: 127.0.0.1
  application:
    name: cubemall-cart
server:
  port: 8083

  global-config:
    db-config:
      id-type: auto
      logic-delete-value: 1 #逻辑删除值(默认为1)
      logic-not-delete-value: 0 #逻辑未删除值(默认为0)

logging:
  level:
    com.xd.cubemall: debug
# 配置连接池
cubemall:
  thread:
    coreSize: 20
    maxSize: 100
    keepAliveTime: 10




CubemallCartApplication.java

package com.xd.cubemall.cart;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CubemallCartApplication {

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

}

pom.xml

<?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 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.18.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xd.cubemall</groupId>
    <artifactId>cubemall-cart</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>cubemall-cart</name>
    <description>cubemall-cart</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>
    <dependencies>

        <!--引入common公共模块-->
        <dependency>
            <groupId>com.xd.cubemall</groupId>
            <artifactId>cubemall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.mysql</groupId>
                    <artifactId>mysql-connector-j</artifactId>
                </exclusion>
            </exclusions>


        </dependency>




        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>


        <!--添加模板技术渲染页面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>


        <!--阿里云OSS-->
        <!--        <dependency>-->
        <!--            <groupId>com.aliyun.oss</groupId>-->
        <!--            <artifactId>aliyun-sdk-oss</artifactId>-->
        <!--            <version>3.17.4</version>-->
        <!--        </dependency>-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>


    </dependencies>
    <dependencyManagement>
        <dependencies>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>



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


http://www.kler.cn/a/405791.html

相关文章:

  • node报错:Error: Cannot find module ‘express‘
  • SQL注入的那些面试题总结
  • reactflow 中 useStoreApi 模块作用
  • 链表续-8种链表(数据结构)
  • 07点积与叉积
  • java Map 遍历 详解
  • Go语言基本类型转换
  • git使用(一)
  • 金融数据中心容灾“大咖说” | 美创科技赋能“灾备一体化”建设
  • STM32 ADC 读取模拟量
  • GitLab|数据迁移
  • DICOM图像处理:深入解析DICOM彩色图像中的Planar配置及其对像素数据解析处理的实现
  • [linux]linux c实现mac地址写入文件,实现删除和增加操作
  • windows下sqlplus连接到linux oracle不成功
  • dockerdsktop修改安装路径/k8s部署wordpress和ubuntu
  • 【论文阅读】Poison Forensics: Traceback of Data Poisoning Attacks in Neural Networks
  • 【大模型-智能体】AutoGen Studio测试和导出工作流程
  • C++:设计模式-单例模式
  • 如何优化Sql的查询性能?
  • 实景三维赋能国土空间智慧治理
  • Python爬虫中的IP封禁问题及其解决方案
  • Chrome 浏览器 131 版本新特性
  • WebLogic 版本升级的注意事项与流程
  • STM32 RAM在Memory Map中被分为3个区域
  • Vscode/Code-server无网环境安装通义灵码
  • Jenkins修改LOGO