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