046 购物车

文章目录

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

购物车对象设计

CartVo

totalAccount 商品总价

countNum 商品数量

List<CartItemVo> 商品详细

CartItemVo

skuId

title

price

image

List<String> skuAttr

count

check=false

totalPrice

sql 复制代码
SELECT CONCAT(attr_name, ": ", attr_value) FROM tb_sku_sale_attr_value WHERE sku_id = 15


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

cubemall-product

SkuSaleAttrValueController.java

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

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

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

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 复制代码
<?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

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

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

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

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 复制代码
<?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

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

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

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

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

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 复制代码
<?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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 复制代码
<?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>
相关推荐
岁月变迁呀3 小时前
Redis梳理
数据库·redis·缓存
Code apprenticeship5 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站5 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶5 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
黄名富9 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
G_whang10 小时前
centos7下docker 容器实现redis主从同步
redis·docker·容器
.生产的驴10 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
我叫啥都行13 小时前
计算机基础复习12.22
java·jvm·redis·后端·mysql
阿乾之铭14 小时前
Redis四种模式在Spring Boot框架下的配置
redis
on the way 12316 小时前
Redisson锁简单使用
redis