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

校园网站建设考核网站建设标书模板下载

校园网站建设考核,网站建设标书模板下载,北京 网站设计 公司,wordpress 锚目录 一、环境搭建 1.1、购物车模块初始化 1.2、动静资源处理 1.3、页面跳转配置 二、数据模型分析 2.1、购物车需求 2.1.1、离线购物车和在线购物车需求、数据库选择redis 2.1.2、购物车数据结构 2.2、模型类抽取,Cart和CartItem 2.3、Redis依赖和配置、…

目录

一、环境搭建

1.1、购物车模块初始化

1.2、动静资源处理

1.3、页面跳转配置

二、数据模型分析

2.1、购物车需求

2.1.1、离线购物车和在线购物车需求、数据库选择redis

2.1.2、购物车数据结构

2.2、模型类抽取,Cart和CartItem

2.3、Redis依赖和配置、SpringSession配置类

三、ThreadLocal 用户身份鉴别

3.1、需求分析 

3.2、传输对象封装临时用户id,userKey,是否有临时用户

3.3、创建购物车常量类

3.4、自定义拦截器,临时用户信息放到ThreadLocal<>

3.5、把拦截器添加到WebMvcConfigurer配置类

3.6、Controller处理购物车请求

四、添加商品到购物车

4.1、前端页面修改

4.2、Controller

4.3、service 

4.3.1、远程查询sku的组合信息

4.3.2、远程查询sku的组合信息

4.3.3、异步编排,自定义线程池配置类

4.3.4、添加购物车业务实现

4.4、刷新页面不断发请求问题,RedirectAttribute

4.4.0、分析

4.4.1、改成重定向到添加成功页面并查询购物车数据

4.4.2、Service层 CartServiceImpl 实现类编写 获取购物车某个购物项方法

4.4.3、success页面修改

五、获取购物车

六、选中购物项[是否选中]

七、修改购物项数量

7.1、前端 cartList.html 页面修改

7.2、后端 接口编写

八、删除购物项

8.1、前端修改

8.2、后端接口


一、环境搭建

1.1、购物车模块初始化

第一步、创建gulimall-cart服务,并进行降版本处理

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.8.RELEASE</version><relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu</groupId>
<artifactId>gulimall-cart</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gulimall-cart</name>
<description>购物车</description>
<properties><java.version>1.8</java.version><spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>

在这里插入图片描述
在这里插入图片描述

第二步、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);}}

第四步、bootstrap.yml添加nacos配置

server.port=40000spring.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_routeuri: lb://gulimall-cartpredicates:- Host=cart.gulimall.cn

1.2、动静资源处理


  1. 静:将资料中购物车文件夹下的所有的静态资源复制到服务器的:mydata/nginx/html/static/cart 目录下

  2. 动:将资料中购物车文件夹下的 两个页面复制到 gulimall-cart服务的 templates 目录下

  3. 替换掉网页中的所有资源申请路径

1.3、页面跳转配置


需求:实现页面的跳转

  1. 当我们在商品详情页item.html点击加入购物车之后,跳转到加入成功页success.html
  2. 在成功页success.html 点击 购物车 进入购物车列表页 cartList.html
  3. 在成功页success.html 点击 查看商品详情 跳转到该商品的详情页
  4. 在 首页 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";}
}


 

二、数据模型分析

2.1、购物车需求


2.1.1、离线购物车和在线购物车需求、数据库选择redis

需求描述

离线购物车:

  • 用户可在 未登录状态 下将商品添加到购物车 [ 用户离线临时购物车 ]。浏览器即使关闭,下次进入,临时购物车数据都在

在线购物车: 

  • 用户可以在 登录状态 下将商品添加到购物车 [ 用户在线购物车 ]。登录之后,会将离线购物车的数据全部合并过来,并清空离线购物车


  • 购物功能
    • 用户可以使用购物车一起结算下单
    • 添加商品到购物车
    • 用户可以查询自己的购物车
    • 用户可以在购物车中修改购买商品的数量
    • 用户可以在购物车中删除商品
    • 在购物车中展示商品优惠信息
    • 提示购物车商品价格变化

数据存储:

购物车是一个读多写多的场景,因此放入数据库并不合适,但购物车又需要持久化,因此这里我们选用Redis的持久化机制存储购物车数据。

redis默认是内存数据库,所有数据存在内存,

2.1.2、购物车数据结构


在这里插入图片描述

购物车Redis的Hash进行存储,key是用户标识码例如gulimall:cart:1,值是一个个CartItem

Redis中 每个用户的购物车 都是由各个购物项组成,根据分析这里使用 Hash进行存储比较合适:

  • Map<String k1,Map<String k2,CartltemInfo>>
    • K1:用户标识
    • Map<String k2,CartltemInfo>
      • K2 :商品Id
      • CartltemInfo :购物项详情

2.2、模型类抽取,Cart和CartItem


在这里插入图片描述

  • Cart
    需要计算的属性,必须重写它的get方法,保证每次获取属性都会进行计算
    • 计算商品的总数量
    • 计算商品类型数量
    • 计算商品的总价

注意:

  • 这里不用@Data,自己生成getter和setter方法,主要为了数量、金额等属性自定义计算方法。例如Cart里的商品数量通过CartItem列表计算总数量。
  • 金额相关数据必须用BigDecimal类型,进行精确的运算
package com.atguigu.cart.vo;
/*** Description: 整体购物车*  这里不用@Data,自己生成getter和setter方法,主要为了数量、金额等属性自定义计算方法。*  例如Cart里的商品数量通过CartItem列表计算总数量。*/
public class Cart {/*** 购物车子项信息*/List<CartItem> items;/*** 商品的总数量*/private Integer countNum;/*** 商品类型数量*/private Integer countType;/*** 商品总价*/private BigDecimal totalAmount;/*** 减免价格*/private BigDecimal reduce = new BigDecimal("0");//需要计算的属性,必须重写它的get方法,保证每次获取属性都会进行计算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;}
}
  • CartItem
    • 计算小计价格
package com.atguigu.cart.vo;
/*** Description: 购物项内容。*  这里不用@Data,自己生成getter和setter方法,主要为了数量、金额等属性自定义计算方法。*  例如Cart里的商品数量通过CartItem列表计算总数量。*/
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;}
}

2.3、Redis依赖和配置、SpringSession配置类


1、导入redis和SpringSession的依赖

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

2、编写配置

# 配置redis
spring.redis.host=124.222.223.222
spring.redis.port=6379

3、添加SpringSession配置类(自定义Session配置类)

作用:配置类设置session使用json序列化,并放大作用域(自定义)。

将 gulimall-auth-server 服务中 /com/atguigu/gulimall/auth/config路径下的GulimallSessionConfig.java配置类复制到 gulimall-cart服务的config包下:

package com.atguigu.cart.config;@Configuration
public class GulimallSessionConfig {@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();cookieSerializer.setDomainName("gulimall.cn");cookieSerializer.setCookieName("GULISESSION");return cookieSerializer;}@Beanpublic RedisSerializer<Object> springSessionDefaultRedisSerializer() {return new GenericJackson2JsonRedisSerializer();}
}


 

三、ThreadLocal 用户身份鉴别

3.1、需求分析 

需求:

  • 用户登录,访问Session中的用户信息
  • 用户未登录
    • Cookie中有 user-key,则表示有临时用户
    • Cookie中没有 user-key,则表示没有临时用户
      • 创建一个封装 并返回 user-key

ThreadLocal:同一个线程共享数据

  • 核心原理是:Map<Thread,Object> threadLocal
  • 在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。线程之间互不干扰

已知:

  • 一次请求进来: 拦截器 ==>> Controller ==>> Service ==>> dao 用的都是同一个线程

在这里插入图片描述


(1)用户身份鉴别方式

  • 当用户登录之后点击购物车,则进行用户登录
  • 用户未登录的时候点击购物车,会为临时用户生成一个name为user-key的cookie临时标识,过期时间为一个月,以后每次浏览器进行访问购物车的时候都会携带user-key。user-key 是用来标识和存储临时购物车数据的

2)使用ThreadLocal 进行用户身份鉴别信息传递

  • 在调用购物车的接口前,先通过session信息判断是否登录,并分别进行用户身份信息的封装
    • session有用户信息则进行用户登录 userInfoTo.setUserId(member.getId());
    • session中没有用户信息
      • cookie中携带 user-key,则表示有临时用户,把user-key进行用户身份信息的封装: userInfoTo.setUserKey(cookie.getValue());
        userInfoTo.setTempUser(true); 并标识携带user-key
      • cookie中未携带 user-key,则表示没有临时用户,进行分配
  • 将信息封装好放进ThreadLocal
  • 在调用购物车的接口后,若cookie中未携带 user-key,则分配临时用户,让浏览器保存

user-key在cookie里,标识用户身份,第一次使用购物车,都会给一个临时用户信息,浏览器保存cookie后,每次访问都会从cookie中取到user-key。

3.2、传输对象封装临时用户id,userKey,是否有临时用户

传输对象,起名to。 

package com.atguigu.cart.vo;@ToString
@Data
public class UserInfoTo {private Long userId;private String userKey; private boolean tempUser = false;   // 判断是否有临时用户
}

3.3、创建购物车常量类

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

3.4、自定义拦截器,临时用户信息放到ThreadLocal<>

业务流程: 

  1. 在执行目标方法之前,检测cookie里的userKey,如果没有则新建用户传输对象,userKey设为随机uuid
  2. 将用户传输对象封装进ThreadLocal。
  3. 在执行目标方法之后,创建cookie并,设置作用域和过期时间,让浏览器保存

购物车模块 

package com.xx.gulimall.cart.interceptor;/*** @Description: 在执行目标方法之前,判断用户的登录状态.并封装传递给controller目标请求**/public class CartInterceptor implements HandlerInterceptor {//创建ThreadLocal<>对象,同一个线程共享数据public static ThreadLocal<UserInfoTo> toThreadLocal = new ThreadLocal<>();/**** 目标方法执行之前*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {UserInfoTo userInfoTo = new UserInfoTo();HttpSession session = request.getSession();//1.从session获得当前登录用户的信息MemberResponseVo memberResponseVo = (MemberResponseVo) session.getAttribute(LOGIN_USER);if (memberResponseVo != null) {//2.1 如果用户登录了,给用户传输对象添加iduserInfoTo.setUserId(memberResponseVo.getId());}
//        3.获取cookieCookie[] cookies = request.getCookies();
//        如果cookie不为空,找到和"user-key"同名的cookie,设置userKey,标记临时用户if (cookies != null && cookies.length > 0) {for (Cookie cookie : cookies) {//user-keyString name = cookie.getName();if (name.equals(TEMP_USER_COOKIE_NAME)) {userInfoTo.setUserKey(cookie.getValue());//标记为已是临时用户userInfoTo.setTempUser(true);}}}//如果没有临时用户一定分配一个临时用户,userKey是临时id。if (StringUtils.isEmpty(userInfoTo.getUserKey())) {String uuid = UUID.randomUUID().toString();userInfoTo.setUserKey(uuid);}//目标方法执行之前,将用户传输信息放到ThreadLocal里,同一个线程共享数据。toThreadLocal.set(userInfoTo);return true;}/*** 业务执行之后,分配临时用户来浏览器保存*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {//获取当前用户的值UserInfoTo userInfoTo = toThreadLocal.get();//如果没有临时用户则保存一个临时用户,并延长cookie过期时间,扩大cookie域,实现子域名共享cookie。if (!userInfoTo.getTempUser()) {//创建一个cookieCookie cookie = new Cookie(TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());//扩大作用域cookie.setDomain("gulimall.com");//设置过期时间cookie.setMaxAge(TEMP_USER_COOKIE_TIMEOUT);response.addCookie(cookie);}}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

3.5、把拦截器添加到WebMvcConfigurer配置类

添加拦截器的配置,不能只把拦截器加入容器中,不然拦截器不生效的

package com.atguigu.cart.config;@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CartInterceptor()).addPathPatterns("/**");}
}

3.6、Controller处理购物车请求

package com.atguigu.cart.controller;@Controller
public class CartController {/*** 去往用户购物车页面*  浏览器有一个cookie:user-key 用来标识用户身份,一个月后过期*  如果第一次使用京东的购物车功能,都会给一个临时用户身份;浏览器以后保存,每次访问都会带上这个cookie;* 登录:Session有* 没登录:按照cookie里面的user-key来做。*  第一次:如果没有临时用户,帮忙创建一个临时用户。* @return*/@GetMapping(value = "/cart.html")public String cartListPage(Model model) throws ExecutionException, InterruptedException {//快速得到用户信息:id,user-key// UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();CartVo cartVo = cartService.getCart();model.addAttribute("cart",cartVo);return "cartList";}
}


 

四、添加商品到购物车

在gulimall-product模块,修改添加购物车按钮

4.1、前端页面修改

第一步、修改item页面

点击 加入购物车 按钮时,发送请求:

http://cart.gulimall.cn/addToCart?skuId=?&num=?

  • skuId:当前商品的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页面

在这里插入图片描述

业务逻辑:

  1. 保存在Redis中的key
    • 如果用户已经登录,则存储在Redis中的key,则是用户的Id
    • 如果用户没有登录,则存在在Redis中的key,是临时用户对应的 user-key
  2. 购物车保存
    • 若当前商品已经存在购物车,只需增添数量
    • 否则需要查询商品购物项所需信息,并添加新商品至购物车

4.2、Controller

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

4.3、service 

4.3.1、远程查询sku的组合信息


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

4.3.2、远程查询sku的组合信息

Gulimall-product 服务中

  1. Controller层编写查询sku的组合信息
@RestController
@RequestMapping("product/skusaleattrvalue")
public class SkuSaleAttrValueController {@Autowiredprivate SkuSaleAttrValueService skuSaleAttrValueService;@GetMapping("/stringlist/{skuId}")public List<String> getSkuSaleAttrValues(@PathVariable("skuId") Long skuId){return  skuSaleAttrValueService.getSkuSaleAttrValuesAsStringList(skuId);}//....
}

Service层实现类 SkuSaleAttrValueServiceImpl 中编写方法

@Override
public List<String> getSkuSaleAttrValuesAsStringList(Long skuId) {SkuSaleAttrValueDao dao = this.baseMapper;return dao.getSkuSaleAttrValuesAsStringList(skuId);
}

Dao层xml的SQL语句 SkuSaleAttrValueDao.xml

<select id="getSkuSaleAttrValuesAsStringList" resultType="java.lang.String">SELECT CONCAT(attr_name,":",attr_value) FROM pms_sku_sale_attr_value WHERE sku_id=#{skuId};
</select>

在gulimall-cart 服务中编写远程调用feign接口

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

4.3.3、异步编排,自定义线程池配置类

假设 远程查询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 {@Beanpublic 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

4.3.4、添加购物车业务实现

CartServiceImpl  

@Slf4j
@Service
public class CartServiceImpl implements CartService {@AutowiredStringRedisTemplate redisTemplate;@AutowiredProductFeignService productFeignService;@AutowiredThreadPoolExecutor executor;// 用户标识前缀private final String CART_PREFIX = "gulimall:cart:";@Slf4j
@Service("cartService")
public class CartServiceImpl implements CartService {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate ProductFeignService productFeignService;@Autowiredprivate ThreadPoolExecutor executor;@Overridepublic CartItemVo addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {//1.拿到要操作的购物车redis操作器信息BoundHashOperations<String, Object, Object> cartOps = getCartOps();//2.判断Redis是否有该商品的信息String productRedisValue = (String) cartOps.get(skuId.toString());if (StringUtils.isEmpty(productRedisValue)) {
//2.1如果没有就添加数据//添加新的商品到购物车(redis)CartItemVo cartItemVo = new CartItemVo();//开启第一个异步任务,存商品基本信息CompletableFuture<Void> getSkuInfoFuture = CompletableFuture.runAsync(() -> {//远程调用商品模块查询当前要添加商品的sku信息R productSkuInfo = productFeignService.getInfo(skuId);SkuInfoVo skuInfo = productSkuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {});//sku信息数据赋值给单个CartItemcartItemVo.setSkuId(skuInfo.getSkuId());cartItemVo.setTitle(skuInfo.getSkuTitle());cartItemVo.setImage(skuInfo.getSkuDefaultImg());cartItemVo.setPrice(skuInfo.getPrice());cartItemVo.setCount(num);}, executor);//开启第二个异步任务,存商品属性信息CompletableFuture<Void> getSkuAttrValuesFuture = CompletableFuture.runAsync(() -> {//2、远程查询skuAttrValues组合信息List<String> skuSaleAttrValues = productFeignService.getSkuSaleAttrValues(skuId);cartItemVo.setSkuAttrValues(skuSaleAttrValues);}, executor);//等待所有的异步任务全部完成CompletableFuture.allOf(getSkuInfoFuture, getSkuAttrValuesFuture).get();String cartItemJson = JSON.toJSONString(cartItemVo);cartOps.put(skuId.toString(), cartItemJson);return cartItemVo;} else {
//2.2 购物车有此商品,修改数量即可CartItemVo cartItemVo = JSON.parseObject(productRedisValue, CartItemVo.class);cartItemVo.setCount(cartItemVo.getCount() + num);//修改redis的数据String cartItemJson = JSON.toJSONString(cartItemVo);cartOps.put(skuId.toString(),cartItemJson);return cartItemVo;}}/*** 获取到要操作的购物车* @return*/private  BoundHashOperations<String, Object, Object> getCartOps() {UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();// 1、判断用户有没有登录String cartKey = "";   if (userInfoTo.getUserId() != null){// 用户已登录,则存储在Redis中的key 是 用户的IdcartKey = CART_PREFIX+userInfoTo.getUserId();} else {// 用户没有登录,则存在在Redis中的key 是 临时用户对应的 `user-key`cartKey = CART_PREFIX+userInfoTo.getUserKey();}// 绑定hashBoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);return operations;}
}

在这里插入图片描述


 

4.4、刷新页面不断发请求问题,RedirectAttribute

4.4.0、分析

目前问题:

不断刷新“添加成功” 页面,会不断发请求,数量会不断增长:

在这里插入图片描述

解决办法,这里修改逻辑:

  • 在controller的addToCart方法里添加商品
  • 商品添加完跳转到成功页面我们改为改成重定向另一个方法,专门查询数据跳转到成功页面

4.4.1、改成重定向到添加成功页面并查询购物车数据

/*** 添加商品到购物车* @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";
}

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

4.4.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"><imgstyle="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}">  数量:1</span></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中购物车数据,并将

    • user-key 对应的临时购物车数据
    • 用户购物车数据

    合并 并删除临时购物车。

第一步、Controller层 CartController 类编写方法

@Controller
public class CartController {@AutowiredCartService 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;
}/*** 获取购物车里面的数据* @param cartKey* @return*/private List<CartItemVo> getCartItems(String cartKey) {//获取购物车里面的所有商品BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);List<Object> values = operations.values();if (values != null && values.size() > 0) {List<CartItemVo> cartItemVoStream = values.stream().map((obj) -> {String str = (String) obj;CartItemVo cartItem = JSON.parseObject(str, CartItemVo.class);return cartItem;}).collect(Collectors.toList());return cartItemVoStream;}return null;}
@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);
});

在这里插入图片描述


 

七、修改购物项数量

7.1、前端 cartList.html 页面修改

前端 cartList.html 页面修改

<li><p style="width:80px" th:attr="skuId=${item.skuId}"><span class="countOpsBtn">-</span><span class="countOpsNum" th:text="${item.count}">5</span><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; 
});

7.2、后端 接口编写

后端 接口编写

  1. Controller 层 接口编写

修改“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";
}

Service 层编写

/*** 修改购物项数量* @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<String, Object, Object> cartOps = getCartOps();CartItem cartItem = getCartItem(skuId);cartItem.setCount(num);cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));
}


 

八、删除购物项

8.1、前端修改

在这里插入图片描述
在这里插入图片描述

8.2、后端接口

CartController

@GetMapping("/deleteItem")
public String deleteItem(@RequestParam("skuId") Long skuId) {cartService.deleteItem(skuId);return "redirect:http://cart.gulimall.cn/cart.html";
}

CartServiceImpl.java

/*** 删除购物项* @param skuId*/
@Override
public void deleteItem(Long skuId) {BoundHashOperations<String, Object, Object> cartOps = getCartOps();cartOps.delete(skuId.toString());
}
http://www.15wanjia.com/news/178927.html

相关文章:

  • o2o网站建设报价福田庆三案例照
  • asp课程设计企业网站设计招聘网站可以做两份简历吗
  • 郴州制作网站设计较好的公司中国建设银行快捷付授权网站
  • 网站建设方案与报价国内电商企业有哪些
  • 虚拟机wordpress广州seo优化排名推广
  • 肉部网站建设包括哪些网站建设找实体还是淘宝
  • 大良营销网站建设教程上海企业建站 免费
  • 网站常用字体大小官网首页
  • 做网站怎么购买主机杨浦网站建设 网站外包
  • 银川做网站哪家公司好如何用c语言做网站
  • 云主机如何上传网站贵德县公司网站建设
  • 汕头市专注网站建设深圳市九号公告
  • 中山做网站价格上海开本建设工程有限公司
  • 赣州本地网站织梦模板网站好吗
  • 成都网站建设有名的知乎 wordpress 响应
  • 免费样机素材网站谷歌seo排名技巧
  • 免费网站代码下载wordpress gzip
  • 网站做自适应好不好科技创新可以被分成三种类型
  • wordpress 网站加速wordpress子菜单不显示
  • 网站模板中心做网站电脑配置
  • 中国教学网站手机新手学做网站
  • 拍卖网站怎么做杭州响应式网站开发
  • 网站建设 ader无锡网站建设标准
  • 做微商网站的软文下关网站建设
  • 搭建一个购物网站郑州网站建设氵汉狮网络
  • 做网站维护需要学什么不限流量网站空间
  • 北京网站建设团队网站本地环境搭建
  • 怎么让公司建设网站网站建设设计流程
  • 空调维修网站建设海米云网站建设
  • 合肥专业网站优化费用360建站的应用场景