第一步、创建gulimall-cart服务,并进行降版本处理
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.8.RELEASEversion>
<relativePath/>
parent>
<groupId>com.atguigugroupId>
<artifactId>gulimall-cartartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>gulimall-cartname>
<description>购物车description>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR3spring-cloud.version>
properties>
第二步、修改域名
vim /etc/hosts
# Gulimall Host Start
127.0.0.1 gulimall.cn
127.0.0.1 search.gulimall.cn
127.0.0.1 item.gulimall.cn
127.0.0.1 auth.gulimall.cn
127.0.0.1 cart.gulimall.cn
# Gulimall Host End
第三步、导入依赖
<dependency>
<groupId>com.atguigu.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
因为目前不用数据库,故排除掉
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class GulimallCartApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallCartApplication.class, args);
}
}
第四步、添加配置
server.port=40000
spring.application.name=gulimall-cart
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
第五步、为启动类添加注解
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class GulimallCartApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallCartApplication.class, args);
}
}
第六步、修改网关,给购物车配置路由
- id: gulimall_cart_route
uri: lb://gulimall-cart
predicates:
- Host=cart.gulimall.cn
将资料中购物车文件夹下的所有的静态资源复制到服务器的:mydata/nginx/html/static/cart
目录下
将资料中购物车文件夹下的 两个页面复制到 gulimall-cart服务的 templates
目录下
替换掉网页中的所有资源申请路径
hgw@HGWdeAir cart % ll
total 0
drwxrwxr-x@ 5 hgw staff 160B 5 29 2019 bootstrap
drwxrwxr-x@ 8 hgw staff 256B 3 22 2020 css
drwxrwxr-x@ 16 hgw staff 512B 3 22 2020 image
drwxrwxr-x@ 74 hgw staff 2.3K 3 22 2020 img
drwxrwxr-x@ 6 hgw staff 192B 3 22 2020 js
需求:实现页面的跳转
- 当我们在商品详情页
item.html
点击加入购物车之后,跳转到加入成功页success.html
- 在成功页
success.html
点击 购物车 进入购物车列表页cartList.html
- 在成功页
success.html
点击 查看商品详情 跳转到该商品的详情页- 在 首页
index.html
中点击我的购物车也跳转到 购物车列表页cartList.html
gulimall-product 服务中的 Item.html
<div class="box-btns-two">
<a href="http://cart.gulimall.cn/addToCart">
加入购物车
a>
div>
//......
<div class="nav_top_three"><a href="http://cart.gulimall.cn/cart.html">我的购物车a><span class="glyphicon glyphicon-shopping-cart">span>
<div class="nav_top_three_1">
<img src="/static/item/img/44.png"/>购物车还没有商品,赶紧选购吧!
div>
div>
Gulimall-cart 服务中 success.html 页面
<div class="bg_shop">
<a class="btn-tobback" href="http://item.gulimall.cn/3.html">查看商品详情a>
<a class="btn-addtocart" href="http://cart.gulimall.cn/cart.html"
id="GotoShoppingCart"><b>b>去购物车结算a>
div>
Gulimall-cart 服务中 success.html 页面
<div class="one_top_left">
<a href="http://gulimall.cn" class="one_left_logo"><img src="/static/cart/img/logo1.jpg">a>
<a href="/static/cart#" class="one_left_link">购物车a>
div>
//.....
<li>
<a href="http://gulimall.cn">首页a>
li>
Gulimall-cart 服务中的 CartController类中添加映射
@Controller
public class CartController {
@GetMapping("/cart.html")
public String cartListPage(){
return "cartList";
}
/**
* 添加商品到购物车
* @return
*/
@GetMapping("/addToCart")
public String addToCart() {
return "success";
}
}
需求描述
用户可以在 登录状态 下将商品添加到购物车 [ 用户在线购物车 ]
登录之后,会将离线购物车的数据全部合并过来,并清空 离线购物车;
用户可在 未登录状态 下将商品添加到购物车 [ 用户离线临时购物车 ]
浏览器即使关闭,下次进入,临时购物车数据都在
Redis中 每个用户的购物车 都是由各个购物项组成,根据分析这里使用 Hash进行存储比较合适:
package com.atguigu.cart.vo;
/**
* Data time:2022/4/10 10:41
* StudentID:2019112118
* Author:hgw
* Description: 整个购物车
* 需要计算的属性,必须重写它的get方法,保证每次获取属性都会进行计算
*/
public class Cart {
/**
*
*/
List<CartItem> items;
/**
* 商品的总数量
*/
private Integer countNum;
/**
* 商品类型数量
*/
private Integer countType;
/**
* 商品总价
*/
private BigDecimal totalAmount;
/**
* 减免价格
*/
private BigDecimal reduce = new BigDecimal("0");
public List<CartItem> getItems() {
return items;
}
public void setItems(List<CartItem> items) {
this.items = items;
}
public Integer getCountNum() {
int count = 0;
if (items!=null && items.size()>0) {
for (CartItem item : items) {
countNum += item.getCount();
}
}
return count;
}
public Integer getCountType() {
int count = 0;
if (items!=null && items.size()>0) {
for (CartItem item : items) {
countNum += 1;
}
}
return count;
}
public BigDecimal getTotalAmount() {
BigDecimal amount = new BigDecimal("0");
// 1、计算购物项总价
if (items!=null && items.size()>0) {
for (CartItem item : items) {
BigDecimal totalPrice = item.getTotalPrice();
amount = amount.add(totalPrice);
}
}
// 2、减去优惠总价
BigDecimal subtract = amount.subtract(getReduce());
return subtract;
}
public BigDecimal getReduce() {
return reduce;
}
public void setReduce(BigDecimal reduce) {
this.reduce = reduce;
}
}
package com.atguigu.cart.vo;
/**
* Data time:2022/4/10 10:41
* StudentID:2019112118
* Author:hgw
* Description: 购物项内容
*/
public class CartItem {
/**
* 商品Id
*/
private Long skuId;
/**
* 商品是否被选中(默认被选中)
*/
private Boolean check = true;
/**
* 商品标题
*/
private String title;
/**
* 商品图片
*/
private String image;
/**
* 商品套餐信息
*/
private List<String> skuAttr;
/**
* 商品价格
*/
private BigDecimal price;
/**
* 数量
*/
private Integer count;
/**
* 小计价格
*/
private BigDecimal totalPrice;
public Long getSkuId() {
return skuId;
}
public void setSkuId(Long skuId) {
this.skuId = skuId;
}
public Boolean getCheck() {
return check;
}
public void setCheck(Boolean check) {
this.check = check;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public List<String> getSkuAttr() {
return skuAttr;
}
public void setSkuAttr(List<String> skuAttr) {
this.skuAttr = skuAttr;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
/**
* 动态计算当前的总价
* @return
*/
public BigDecimal getTotalPrice() {
return this.price.multiply(new BigDecimal("" + this.count));
}
public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}
}
1、导入redis和SpringSession的依赖
<dependency>
<groupId>org.springframework.sessiongroupId>
<artifactId>spring-session-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
2、编写配置
# 配置redis
spring.redis.host=124.222.223.222
spring.redis.port=6379
3、添加SpringSession配置类(自定义Session配置类)
将 gulimall-auth-server 服务中 /com/atguigu/gulimall/auth/config
路径下的GulimallSessionConfig.java配置类复制到 gulimall-cart服务的config包下:
package com.atguigu.cart.config;
@Configuration
public class GulimallSessionConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("gulimall.cn");
cookieSerializer.setCookieName("GULISESSION");
return cookieSerializer;
}
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
需求:参照京东
- 用户登录,访问Session中的用户信息
- 用户未登录
- Cookie中有 user-key,则表示有临时用户
- Cookie中没有 user-key,则表示没有临时用户
- 创建一个封装 并返回 user-key
ThreadLocal:同一个线程共享数据
已知:
(1)用户身份鉴别方式
(2)使用ThreadLocal 进行用户身份鉴别信息传递
第一步、编写 用户身份信息的封装的TO
package com.atguigu.cart.vo;
@ToString
@Data
public class UserInfoTo {
private Long userId;
private String userKey;
private boolean tempUser = false; // 判断是否有临时用户
}
**第二步、**编写拦截器
package com.atguigu.cart.interceptor;
/**
* Data time:2022/4/10 11:29
* StudentID:2019112118
* Author:hgw
* Description: 拦截器
* 在执行目标方法之前,判断用户的登录状态,并封装传递给controller目标请求
* 登录用户
* 临时用户
*/
public class CartInterceptor implements HandlerInterceptor {
public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();
/**
* 目标方法执行之前
* @param request
* @param response
* @param handler
* @return
* true: 放行
* false: 不放行
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
UserInfoTo userInfoTo = new UserInfoTo();
HttpSession session = request.getSession();
MemberRespVo member = (MemberRespVo) session.getAttribute(AuthServerConstant.LOGIN_USER);
if (member != null) {
// 用户登录
userInfoTo.setUserId(member.getId());
}
Cookie[] cookies = request.getCookies();
if (cookies!=null && cookies.length>0){
for (Cookie cookie : cookies) {
//user-key
String name = cookie.getName();
if (name.equals(CartConstant.TEMP_USER_COOKIE_NAME)) {
userInfoTo.setUserKey(cookie.getValue());
userInfoTo.setTempUser(true); // 标识携带user-key
}
}
}
// 如果没有临时用户,则进行分配
if (StringUtils.isEmpty(userInfoTo.getUserKey())){
String uuid = UUID.randomUUID().toString();
userInfoTo.setUserKey(uuid);
}
// 目标方法执行之前,将信息封装好放进ThreadLocal
threadLocal.set(userInfoTo);
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 {
UserInfoTo userInfoTo = threadLocal.get();
if (!userInfoTo.isTempUser()) {
// 持续的延长临时用户的过期时间
Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
cookie.setDomain("gulimall.cn");
cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);
response.addCookie(cookie);
}
}
}
第三步、添加拦截器的配置
添加拦截器的配置,不能只把拦截器加入容器中,不然拦截器不生效的
package com.atguigu.cart.config;
@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CartInterceptor()).addPathPatterns("/**");
}
}
**第四步、**编写Controller处理请求
package com.atguigu.cart.controller;
@Controller
public class CartController {
/**
* 去往用户购物车页面
* 浏览器有一个cookie:user-key 用来标识用户身份,一个月后过期
* 如果第一次使用京东的购物车功能,都会给一个临时用户身份;浏览器以后保存,每次访问都会带上这个cookie;
* 登录:Session有
* 没登录:按照cookie里面的user-key来做。
* 第一次:如果没有临时用户,帮忙创建一个临时用户。
* @return
*/
@GetMapping("/cart.html")
public String cartListPage(){
// 1、快速得到用户信息,登录:id,没登录:user-key
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
return "cartList";
}
}
添加常量:
package com.atguigu.common.constant;
public class CartConstant {
public static final String TEMP_USER_COOKIE_NAME = "user-key";
public static final int TEMP_USER_COOKIE_TIMEOUT = 60*60*24*30;
}
在gulimall-product模块,修改添加购物车按钮
第一步、修改item页面
点击 加入购物车 按钮时,发送请求:
http://cart.gulimall.cn/addToCart?skuId=?&num=?
$("#addToCartA").click(function () {
var num = $("#numInput").val();
var skuId = $(this).attr("skuId");
location.href = "http://cart.gulimall.cn/addToCart?skuId="+skuId+"&num="+num;
});
第二步、修改 success页面
业务逻辑:
user-key
1、Controller层接口 CartController类 编写添加商品到购物车方法
/**
* 添加商品到购物车
* @param skuId 商品的skuid
* @param num 添加的商品数量
* @return
*/
@GetMapping("/addToCart")
public String addToCart(@RequestParam("skuId") Long skuId,
@RequestParam("num") Integer num,
Model model) throws ExecutionException, InterruptedException {
CartItem cartItem = cartService.addToCart(skuId,num);
model.addAttribute("item",cartItem);
return "success";
}
2、Service层实现类 CartController 编写方法
@Slf4j
@Service
public class CartServiceImpl implements CartService {
@Autowired
StringRedisTemplate redisTemplate;
@Autowired
ProductFeignService productFeignService;
@Autowired
ThreadPoolExecutor executor;
// 用户标识前缀
private final String CART_PREFIX = "gulimall:cart:";
@Override
public CartItem addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
String res = (String) cartOps.get(skuId.toString());
if (StringUtils.isEmpty(res)){
// 购物车无此商品,添加新商品到购物车 (封装到购物项)
CartItem cartItem = new CartItem();
// 1、远程查询当前要添加的商品的信息 SKU信息并封装
CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {
R skuInfo = productFeignService.getSkuInfo(skuId);
SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {
});
cartItem.setSkuId(skuId);
cartItem.setCheck(true);
cartItem.setTitle(data.getSkuTitle());
cartItem.setImage(data.getSkuDefaultImg());
cartItem.setPrice(data.getPrice());
cartItem.setCount(num);
},executor);
// 2、远程查询sku的组合信息
CompletableFuture<Void> getSkuSaleAttrValues = CompletableFuture.runAsync(() -> {
List<String> values = productFeignService.getSkuSaleAttrValues(skuId);
cartItem.setSkuAttr(values);
}, executor);
// 3、等远程查询都完成之后在向Redis中放数据
CompletableFuture.allOf(getSkuInfoTask,getSkuSaleAttrValues).get();
String s = JSON.toJSONString(cartItem);
cartOps.put(skuId.toString(), s);
return cartItem;
} else {
// 购物车有此商品,增添数量
CartItem cartItem = JSON.parseObject(res, CartItem.class);
cartItem.setCount(cartItem.getCount() + num);
cartOps.put(skuId.toString(), JSON.toJSONString(cartItem));
return cartItem;
}
}
/**
* 获取到要操作的购物车
* @return
*/
private BoundHashOperations<String, Object, Object> getCartOps() {
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
// 1、判断用户有没有登录
String cartKey = "";
if (userInfoTo.getUserId() != null){
// 用户已登录,则存储在Redis中的key 是 用户的Id
cartKey = CART_PREFIX+userInfoTo.getUserId();
} else {
// 用户没有登录,则存在在Redis中的key 是 临时用户对应的 `user-key`
cartKey = CART_PREFIX+userInfoTo.getUserKey();
}
// 绑定hash
BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);
return operations;
}
}
假设 远程查询sku的组合信息 查询需要1秒,远程查询sku的组合信息有需要1.5秒,那总耗时就需要2.5秒。
若使用异步编排的话,只需要1.5秒。
1、 将gulimall-product中 com/atguigu/gulimall/product/config
路径下的 MyThreadConfig、ThreadPoolConfigProperties类复制到 gulimall-cart 服务下的 config 路径下:
package com.atguigu.cart.config;
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
return new ThreadPoolExecutor(pool.getCoreSize(),
pool.getMaxSize(),
pool.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
}
package com.atguigu.cart.config;
@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
private Integer coreSize;
private Integer maxSize;
private Integer keepAliveTime;
}
2、配置 线程池
# 配置线程池
gulimall.thread.core-size: 20
gulimall.thread.max-size: 200
gulimall.thread.keep-alive-time: 10
在gulimall-cart 服务中编写远程调用feign接口
package com.atguigu.cart.feign;
@FeignClient("gulimall-product")
public interface ProductFeignService {
@GetMapping("/product/skusaleattrvalue/stringlist/{skuId}")
List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId);
}
Gulimall-product 服务中
@RestController
@RequestMapping("product/skusaleattrvalue")
public class SkuSaleAttrValueController {
@Autowired
private SkuSaleAttrValueService skuSaleAttrValueService;
@GetMapping("/stringlist/{skuId}")
public List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId){
return skuSaleAttrValueService.getSkuSaleAttrValuesAsStringList(skuId);
}
//....
}
@Override
public List<String> getSkuSaleAttrValuesAsStringList(Long skuId) {
SkuSaleAttrValueDao dao = this.baseMapper;
return dao.getSkuSaleAttrValuesAsStringList(skuId);
}
<select id="getSkuSaleAttrValuesAsStringList" resultType="java.lang.String">
SELECT CONCAT(attr_name,":",attr_value) FROM pms_sku_sale_attr_value WHERE sku_id=#{skuId};
select>
package com.atguigu.cart.feign;
@FeignClient("gulimall-product")
public interface ProductFeignService {
@RequestMapping("/product/skuinfo/info/{skuId}")
R getSkuInfo(@PathVariable("skuId") Long skuId);
@GetMapping("/product/skusaleattrvalue/stringlist/{skuId}")
List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId);
}
上述编写的代码,只要我们刷新success页面会一直增加数量,客户说 体验感不好
这里修改逻辑:
1、Controller层 CartController 类中编写业务
/**
* 添加商品到购物车
* @param skuId 商品的skuid
* @param num 添加的商品数量
* @return
* RedirectAttributes
* ra.addFlashAttribute(, ) :将数据放在session里面可以在页面里取出,但是只能取一次
* ra.addAttribute(,); 将数据放在url后面
*/
@GetMapping("/addToCart")
public String addToCart(@RequestParam("skuId") Long skuId,
@RequestParam("num") Integer num,
RedirectAttributes ra) throws ExecutionException, InterruptedException {
cartService.addToCart(skuId,num);
ra.addAttribute("skuId", skuId);
return "redirect:http://cart.gulimall.cn/addToCartSuccess.html";
}
/**
* 跳转到成功页
* @param skuId
* @param model
* @return
*/
@GetMapping("/addToCartSuccess.html")
public String addToCartSuccessPage(@RequestParam("skuId") Long skuId,Model model) {
// 重定向到成功页面,再次查询购物车数据
CartItem cartItem = cartService.getCartItem(skuId);
model.addAttribute("item",cartItem);
return "success";
}
2、Service层 CartServiceImpl 实现类编写 获取购物车某个购物项方法
@Override
public CartItem getCartItem(Long skuId) {
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
String str = (String) cartOps.get(skuId.toString());
CartItem cartItem = JSON.parseObject(str, CartItem.class);
return cartItem;
}
3、success页面修改
<div class="success-wrap">
<div class="w" id="result">
<div class="m succeed-box">
<div th:if="${item!=null}" class="mc success-cont">
<div class="success-lcol">
<div class="success-top"><b class="succ-icon">b>
<h3 class="ftx-02">商品已成功加入购物车h3>div>
<div class="p-item">
<div class="p-img">
<a href="/static/cart/javascript:;" target="_blank"><img
style="height: 60px;width:60px;" th:src="${item.image}">a>
div>
<div class="p-info">
<div class="p-name">
<a th:href="'http://item.gulimall.cn/'+${item.skuId}+'.html'"
th:text="${item.title}">TCL 55A950C 55英寸32核人工智能 HDR曲面超薄4K电视金属机身(枪色)a>
div>
<div class="p-extra"><span class="txt" th:text="'数量:'+${item.count}"> 数量:1span>div>
div>
<div class="clr">div>
div>
div>
<div class="success-btns success-btns-new">
<div class="success-ad">
<a href="/#none">a>
div>
<div class="clr">div>
<div class="bg_shop">
<a class="btn-tobback" th:href="'http://item.gulimall.cn/'+${item.skuId}+'.html'">查看商品详情a>
<a class="btn-addtocart" href="http://cart.gulimall.cn/cart.html"
id="GotoShoppingCart"><b>b>去购物车结算a>
div>
div>
div>
<div th:if="${item==null}" class="mc success-cont">
<h2>购物车中无商品h2>
<a href="http://gulimall.cn">去购物a>
div>
div>
div>
div>
若用户未登录,则使用user-key获取Redis中购物车数据
若用户登录,则使用userId获取Redis中购物车数据,并将
合并 并删除临时购物车。
第一步、Controller层 CartController 类编写方法
@Controller
public class CartController {
@Autowired
CartService cartService;
@GetMapping("/cart.html")
public String cartListPage(Model model) throws ExecutionException, InterruptedException {
Cart cart = cartService.getCart();
model.addAttribute("cart",cart);
return "cartList";
}
第二步、编写Service层 方法
package com.atguigu.cart.service;
public interface CartService {
//....
/**
* 获取购物车某个购物项
* @param skuId
* @return
*/
CartItem getCartItem(Long skuId);
/**
* 获取整个购物车
* @return
*/
Cart getCart() throws ExecutionException, InterruptedException;
/**
* 清空购物车数据
* @param cartKey
*/
void clearCart(String cartKey);
}
实现类 CartServiceImpl 方法:
@Override
public CartItem getCartItem(Long skuId) {
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
String str = (String) cartOps.get(skuId.toString());
CartItem cartItem = JSON.parseObject(str, CartItem.class);
return cartItem;
}
@Override
public Cart getCart() throws ExecutionException, InterruptedException {
Cart cart = new Cart();
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
if (userInfoTo.getUserId()!=null){
// 1、登录状态
String cartKey = CART_PREFIX + userInfoTo.getUserId();
// 2、如果临时购物车的数据还没有合并,则合并购物车
String tempCartKey = CART_PREFIX + userInfoTo.getUserKey();
List<CartItem> tempCartItems = getCartItems(tempCartKey);
if (tempCartItems!=null) {
// 临时购物车有数据,需要合并
for (CartItem item : tempCartItems) {
addToCart(item.getSkuId(),item.getCount());
}
// 清除临时购物车的数据
clearCart(tempCartKey);
}
// 3、删除临时购物车
// 4、获取登录后的购物车数据
List<CartItem> cartItems = getCartItems(cartKey);
cart.setItems(cartItems);
} else {
// 2、没登录状态
String cartKey = CART_PREFIX + userInfoTo.getUserKey();
// 获取临时购物车的所有项
List<CartItem> cartItems = getCartItems(cartKey);
cart.setItems(cartItems);
}
return cart;
}
@Override
public void clearCart(String cartKey) {
// 直接删除该键
redisTemplate.delete(cartKey);
}
第三步、修改购物车前端页面 cartList.html
测试结果:
**第一步、**Controller层方法编写
gulimall-cart 服务com/atguigu/cart/controller/
路径下 CartController.java类中添加映射方法
@GetMapping("/checkItem")
public String checkItem(@RequestParam("skuId") Long skuId,
@RequestParam("check") Integer check) {
cartService.checkItem(skuId,check);
return "redirect:http://cart.gulimall.cn/cart.html";
}
**第二步、**Service层实现类方法中编写是否选中购物项方法
/**
* 勾选购物项
* @param skuId
* @param check
*/
void checkItem(Long skuId, Integer check);
gulimall-cart 服务中 com/atguigu/cart/service/impl/
路径下 CartServiceImpl.java 实现类:
@Override
public void checkItem(Long skuId, Integer check) {
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
CartItem cartItem = getCartItem(skuId);
cartItem.setCheck(check==1?true:false);
String s = JSON.toJSONString(cartItem);
cartOps.put(skuId.toString(),s);
}
第三步、页面修改
$(".itemCheck").click(function () {
var skuId = $(this).attr("skuId");
var check = $(this).prop("checked");
location.href = "http://cart.gulimall.cn/checkItem?skuId="+skuId+"&check="+(check?1:0);
});
前端 cartList.html 页面修改
<li>
<p style="width:80px" th:attr="skuId=${item.skuId}">
<span class="countOpsBtn">-span>
<span class="countOpsNum" th:text="${item.count}">5span>
<span class="countOpsBtn">+span>
p>
li>
$(".countOpsBtn").click(function () {
var skuId = $(this).parent().attr("skuId");
var num = $(this).parent().find(".countOpsNum").text();
location.href = "http://cart.gulimall.cn/countItem?skuId="+skuId+"&num="+num;
});
后端 接口编写
修改“com.atguigu.gulimall.cart.controller.CartController”类,代码如下:
@GetMapping("/countItem")
public String countItem(@RequestParam("skuId") Long skuId,
@RequestParam("num") Integer num) {
cartService.countItem(skuId,num);
return "redirect:http://cart.gulimall.cn/cart.html";
}
/**
* 修改购物项数量
* @param skuId
* @param num
*/
void countItem(Long skuId, Integer num);
修改“com.atguigu.gulimall.cart.service.impl.CartServiceImpl”类,代码如下:
@Override
public void countItem(Long skuId, Integer num) {
BoundHashOperations cartOps = getCartOps();
CartItem cartItem = getCartItem(skuId);
cartItem.setCount(num);
cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));
}
@GetMapping("/deleteItem")
public String deleteItem(@RequestParam("skuId") Long skuId) {
cartService.deleteItem(skuId);
return "redirect:http://cart.gulimall.cn/cart.html";
}
/**
* 删除购物项
* @param skuId
*/
@Override
public void deleteItem(Long skuId) {
BoundHashOperations<String, Object, Object> cartOps = getCartOps();
cartOps.delete(skuId.toString());
}