购物车
1)编写前端增加减少数量,发生堆栈溢出的错误,起初以为是事件冒泡的问题,后来发现是data中把specs删除了,然后我有个方法叫specs,里面使用this.specs调用变量,结果由于JS的弱语法...它调用了自己。在非主页面登录出现路径错误的问题,是因为shortcut,js中的路径都是相对路径(不加/),所以我们访问item.html再访问login的时候,路径是/item/login.在路径前面加上/即可解决。
2)未登录添加商品至购物车,采用web本地存储,把数据存储到localStorage中,由前端完成。登陆后前台校验/auth/verify通过,执行then后面的代码,向后台发起/cart请求。我们向redis存购物车数据的时候需要知道当前操作的用户是谁,所以数据结构是Map<String,Map<String,String>>,key是用户的id,hashkey是skuId(这样对购物车操作就可以直接根据id获取)。添加购物车和修改,删除购物车等,都需要从token中解析出用户信息,那么一个方法就要访问一次cart微服务,即写一次解析的代码。所以在实现了HandlerInterceptor接口的自定义的拦截器中实现解析token的操作,然后将解析出来的用户信息保存起来,在逻辑层获取。可以保存到request,但是springmvc不推荐使用域对象,所以我们使用ThreadLocal。其key是当前线程,值是用户id。编写获取对象的静态方法,以便在service中调用来得到key。
3)编写配置类MvcConfig,使我们自定义的拦截器生效。让MvcConfig类实现WebMvcConfigurer接口,实现addInterceptor方法,并添加拦截路径。由于我们的UserInterceptor是自己new出来的,但是在UserInterceptor中使用了@Autowired注解将JwtProperties注入了进去,但是自己new的对象是无法使用Spring自动注入的。所以把JwtProperties注入到MvcConfig,然后在UserInterceptor添加带参构造方法。去掉UserInterceptor的@Autowired注解,完成Jwt的注入从UserInterceptor迁移至MvcConfig。
4)添加购物车,在service中先判断redis中是否存在这个skuId,如果有,取出这个数据,转换为cart对象,修改他的数量,然后将对象重写回redis。如果没有,直接添加数据到redis。
5)完成购物车的添加,发现浏览器network报错405,说是前台发出的是get方法,经排查发现那时已经跳转到购物车页面,是另一个请求。
6)完成查询购物车接口和合并购物车接口。完成修改购物车商品数量和删除商品接口。
7)创建订单微服务 com.leyou.service ly-order。订单微服务也需要编写拦截器解析token。
8)编写添加订单,编写OrderDTO和CartDTO来和前台的json结构匹配(data transfer object /vo)。业务逻辑要添加订单,订单详情和订单状态三张表。最后还要减库存。减库存涉及到并发问题,第一个想法是添加synchronized关键字,但是因为一个微服务是一个tomcat,锁是锁内存的,当商品微服务搭了集群的时候,synchronized之间锁不住,还会出现高并发下的线程安全问题。于是就要采用分布式锁的方案(redis有一个setNX,来一个线程创建一个锁对象,执行完毕删除del这个锁对象,当服务突然宕机,锁对象无法被删除,容易造成死锁。zookeeper利用节点的唯一性,一个线程创建一个节点,执行完毕删除这个节点。因为节点是临时节点,服务宕机会自动删除,所以没有死锁问题)。但是这样会导致效率低下,所以我们采用的是乐观锁的方式,在数据库sql语句中进行判断。
9)针对减库存这个操作,我们使用的是Feign同步调用,相比使用mq异步调用,如果操作出现异常,添加订单方法是可以回滚的。但是如果减库存操作放在前面,并且调用成功,如果下面的代码抛出异常,减库存操作不会回滚。这就是分布式系统的事务问题。解决这个问题有三种方案:2PC(两阶段提交) 3PC,TCC【try confirm cancel】(补偿事务)和异步确保。
10)启动报错,1:没有在启动类添加@MapperScan注解。2:AddressClient不是配置类,不用autowired。AddressClient中添加数据发生问题,未解决。