1.搭建购物车服务
1.1.创建module
搭建购物车模块与之前的一样,依旧是加入引导类,编写配置文件,加入启动类
加入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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-data-redis</artifactId>
</dependency>
</dependencies>
加入配置文件
server:
port: 8088
spring:
application:
name: cart-service
redis:
host: 192.168.56.101
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
registry-fetch-interval-seconds: 10
instance:
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 15
加入引导类
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LeyouCartApplication {
public static void main(String[] args) {
SpringApplication.run(LeyouCartApplication.class, args);
}
}
2.购物车功能分析
2.1.需求
需求描述:
-
用户可以在登录状态下将商品添加到购物车
-
放入数据库
-
mongodb
-
放入redis(采用)
-
-
用户可以在未登录状态下将商品添加到购物车
-
放入localstorage
-
cookie
-
webSQL
-
-
用户可以使用购物车一起结算下单
-
用户可以查询自己的购物车
-
用户可以在购物车中修改购买商品的数量。
-
用户可以在购物车中删除商品。
-
在购物车中展示商品优惠信息
-
提示购物车商品价格变化
这幅图主要描述了两个功能:新增商品到购物车、查询购物车。
新增商品:
-
判断是否登录
-
是:则添加商品到后台Redis中
-
否:则添加商品到本地的Localstorage
-
无论哪种新增,完成后都需要查询购物车列表:
-
判断是否登录
-
否:直接查询localstorage中数据并展示
-
是:已登录,则需要先看本地是否有数据,
-
有:需要提交到后台添加到redis,合并数据,而后查询
-
否:直接去后台查询redis,而后返回
-
-
编写前端代码因为我个人的原因,JS没有代码校验,少写括号出现了
前端代码一定要写好:
methods:{
addCarts(){
},
incr(){
this.num++;
},desc(){
if (this.num>1){
this.num--;
}
}
},
添加购物车的前端代码的编写:
addCarts(){
ly.http.get("/auth/verify").then(resp=>{
//已经登录
}).catch(()=>{
//未登录
let carts=ly.store.get("LY_CART")||[];
let cart=carts.find(cart=>cart.skuId===this.sku.id);
if (cart){
//如果已经又商品,则在起基础上进行累计
cart.num+=this.num;
} else
{
//如果没有就进行新增
cart={
skuId:this.sku.id,
image:this.sku.image,
title:this.sku.titile,
price:this.sku.price,
ownSpec:this.sku.ownSpec
}
carts.push(cart);
}
ly.store.set("LY_CART",carts);
window.location.href="http://www.leyou.com/cart.html";
})
},
完成测试:
JS代码段函数编写:
created(){
ly.verify().then(resp=>{
}).catch(()=>{
this.carts= ly.store.get("LY_CART")
})
},
components: {
shortcut: () => import("/js/pages/shortcut.js")
}
加入购物车成功:
因为对前端的Vue的代码不熟悉,语法等不是很熟练,所以这一部分代码写起来比较吃力,不过大概的效果还是可以运行出来的,对购物车的增删改查操作,前端的标签进行值得渲染
decre(cart){
if (cart.num>1){
cart.num--;
ly.verify().then(resp=>{
}).catch(()=>{
ly.store.set("LY_CART",this.carts);
})
}
},incr(cart){
cart.num++;
ly.verify().then(resp=>{
}).catch(()=>{
ly.store.set("LY_CART",this.carts);
})
},deleteCart(index){
ly.verify().then(resp=>{
}).catch(()=>{
this.carts.splice(index,1);
ly.store.set("LY_CART",this.carts);
})
}
添加拦截器
@Component
@EnableConfigurationProperties(JwtProperties.class)
public class LoginInterpertor extends HandlerInterceptorAdapter {
@Autowired
private JwtProperties jwtProperties;
private static final ThreadLocal<UserInfo> THREAD_LOCAL=new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = CookieUtils.getCookieValue(request, jwtProperties.getCookieName());
UserInfo userInfo = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
if (userInfo==null){
return false;
}
THREAD_LOCAL.set(userInfo);
return true;
}
public static UserInfo get(){
return THREAD_LOCAL.get();
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//删除线程池中得线程
THREAD_LOCAL.remove();
}
}
@Component
public class LeyouMVCConfiguatrion implements WebMvcConfigurer {
@Autowired
LoginInterpertor loginInterpertor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterpertor).addPathPatterns("/**");
}
}
4.2.后台购物车设计
当用户登录时,我们需要把购物车数据保存到后台,可以选择保存在数据库。但是购物车是一个读写频率很高的数据。因此我们这里选择读写效率比较高的Redis作为购物车存储。
Redis有5种不同数据结构,这里选择哪一种比较合适呢?Map<String, List<String>>
-
首先不同用户应该有独立的购物车,因此购物车应该以用户的作为key来存储,Value是用户的所有购物车信息。这样看来基本的
k-v
结构就可以了。 -
但是,我们对购物车中的商品进行增、删、改操作,基本都需要根据商品id进行判断,为了方便后期处理,我们的购物车也应该是
k-v
结构,key是商品id,value才是这个商品的购物车信息。
综上所述,我们的购物车结构是一个双层Map:Map<String,Map<String,String>>
-
第一层Map,Key是用户id
-
第二层Map,Key是购物车中商品id,值是购物车数据
将购物车中得数据保存在Redis中
Controller:
@Controller
public class CartController {
@Autowired
private CartService cartService;
@PostMapping
public ResponseEntity<Void> addCart(@RequestBody Cart cart){
this.cartService.addCart(cart);
return ResponseEntity.ok(null);
}
}
Service:
@Service
public class CartService {
@Autowired
private GoodsClient goodsClient;
@Autowired
private StringRedisTemplate redisTemplate;
private static final String PRY_KEY="leyou:cart:uid";
public void addCart(Cart cart) {
//获取用户信息
UserInfo userInfo = LoginInterceptor.getLoginUser();
String key = cart.getSkuId().toString();
Integer num = cart.getNum();
//查询购物车中得商品记录
BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(PRY_KEY + key);
if (ops.hasKey(key)){
//如果购物车中包含此商品
String cartJson = ops.hasKey(key).toString();
cart= JsonUtils.parse(cartJson,Cart.class);
cart.setNum(cart.getNum()+num);
ops.put(key,cart);
}
else {
//如果购物车中部包含此商品
cart.setUserId(userInfo.getId());
Sku sku = goodsClient.querBySkuId(cart.getSkuId());
cart.setImage(StringUtils.isBlank(sku.getImages())?"":StringUtils.split(sku.getImages(),",")[0]);
cart.setPrice(sku.getPrice());
cart.setTitle(sku.getTitle());
cart.setOwnSpec(sku.getOwnSpec());
}
ops.put(key,JsonUtils.serialize(cart));
}
}
添加成功:则会个往Redis添加得时候可能会出现问题,一定要看清token又没有值,注意时拦截器得配置,其中得用户信息时从拦截器中获取得,如果拦截器配置部成功,token值为空,这个往redis中添加数据就一直就添加部进去,一定要注意token得值!!!
加入购物车成功:页面回显,关于购物车得查询
Controller:
@GetMapping
public ResponseEntity<List<Cart>> query(){
List<Cart> cartList =this.cartService.query();
if (CollectionUtils.isEmpty(cartList)){
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(cartList);
}
Service:
/**
* 查询购物车方法
* @return
*/
public List<Cart> query() {
UserInfo user = LoginInterceptor.getLoginUser();
if (!redisTemplate.hasKey(PRY_KEY+user.getId())){
return null;
}
BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(PRY_KEY + user.getId());
List<Object> objectList = ops.values();
return objectList.stream().map(cart->JsonUtils.parse(cart.toString(),Cart.class)).collect(Collectors.toList());
}
修改购物车中得数量
Controller:
@PutMapping
public ResponseEntity<Void> updateCart(@RequestBody Cart cart){
this.cartService.update(cart);
return ResponseEntity.noContent().build();
}
Serivce:
public void update(Cart cart) {
//获取用户信息
UserInfo userInfo = LoginInterceptor.getLoginUser();
//判断用户得购物车是否存在
if(!redisTemplate.hasKey(PRY_KEY+userInfo))
{
return;
}
//获取购物车
BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(PRY_KEY + userInfo);
//获取Redios中得购物车
Integer num = cart.getNum();
String carString = ops.get(cart.getSkuId().toString()).toString();
cart=JsonUtils.parse(carString,Cart.class);
cart.setNum(num);
//重新放入缓冲总
ops.put(cart.getSkuId().toString(),JsonUtils.serialize(cart));
}
Redis中得数量
今日总结:
1.nginx
proxy_set_header Host $host;
2.zuul网关
add-host-header: true
sensitiveHeaders: # 设置敏感头信息为null
skuId
image
skuTitle
ownSpec
price
num
<input type="checkbox" value="java" v-model="language">
<input type="checkbox" value="php" v-model="language">
<input type="checkbox" value="ios" v-model="language">
1.jwt登陆校验接口
2.解决cookie写入问题
Release SR2
3.首页用户名显示
1.jwtUtils解析token
2.刷新有效时间
4.zuul网关统一校验用户
自定义过滤器继承ZuulFilter
添加白名单
5.购物车,未登录状态购物车
加入购物车 查询购物车 修改数量 删除购物车 勾选购物车