文章目录
- 1、确认下单:购物车页面点击去结算
- 1.1、在OrderController类中创建 trade 方法
- 1.2、在CartController类中创建 checkedCartInfos
- 1.3、CartServiceImpl 实现 checkedCartInfos的业务功能
- 1.4、在service-cart-client模块下定义远程openFeign接口
- 1.5、在SpzxOrderApplication类上加上@EnableFeignClients
- 1.6、OrderServiceImpl 实现 trade的业务逻辑
- 1.7、此时启动 SpzxOrderApplication
- 2、 openFeign拦截器使用
- 2.1、使用feign拦截器拦截请求,获取token,重新传递token
- 2.1.1、CartClientInterceptor
- 2.1.2、@EnableCartClientConfig
- 2.1.3、SpzxOrderApplication
1、确认下单:购物车页面点击去结算
- 点击
去结算
这个按钮,会发起一个请求,这个请求是trade
,然后展示我们要购买的商品
和商品的总金额
1.1、在OrderController类中创建 trade 方法
@RestController
@Tag(name = "订单管理模块", description = "订单管理模块")
@RequestMapping("/api/order/orderInfo")
public class OrderController {
@Resource
private OrderService orderService;
//查询购物车中选中的购物项列表 转为 orderItem 列表交给前端展示
@Operation(summary = "确认下单:购物车页面点击去结算")
@GetMapping("/auth/trade")
public Result trade() {
TradeVo tradeVo = orderService.trade();
return Result.ok(tradeVo);
}
}
1.2、在CartController类中创建 checkedCartInfos
@RestController
@RequestMapping("/api/order/cart")
@Tag(name = "购物车模块")
public class CartController {
//只要请求头中携带token,不需要再传用户id
@Operation(summary = "查询用户购物车已选中购物项列表")
@GetMapping("/auth/checkedCartInfos")
public Result checkedCartInfos()
{
List<CartInfo> cartInfos = cartService.checkedCartInfos();
return Result.ok(cartInfos);
}
}
1.3、CartServiceImpl 实现 checkedCartInfos的业务功能
@Service
public class CartServiceImpl implements CartService {
@Resource
private RedisTemplate redisTemplate;
private BoundHashOperations getUserCart() {
UserInfo userInfo = SpzxServiceAuthInterceptor.THREAD_LOCAL.get();
BoundHashOperations ops = redisTemplate.boundHashOps("spzx:cart:" + userInfo.getId());
return ops;
}
@Override
public List<CartInfo> checkedCartInfos() {
//泛型1:redis键类型,泛型2:hash的key类型, 泛型3:hash的value的类型
BoundHashOperations<String,String,CartInfo> userCart = getUserCart();
return userCart.values().stream().filter(cartInfo -> cartInfo.getIsChecked()==1).toList();
}
}
1.4、在service-cart-client模块下定义远程openFeign接口
@FeignClient(value = "service-cart")
public interface CartClient {
@GetMapping("/api/order/cart/auth/checkedCartInfos")
public Result<List<CartInfo>> checkedCartInfos();
@DeleteMapping("/api/order/cart/auth/delCheckedCartInfos")
public Result<Void> delCheckedCartInfos();
}
1.5、在SpzxOrderApplication类上加上@EnableFeignClients
@SpringBootApplication
@EnableSpzxServiceAuth
@EnableFeignClients(basePackages = "com.atguigu.spzx")
@EnableCartClientConfig
@MapperScan(basePackages = "com.atguigu.spzx.order.mapper")
@EnableTransactionManagement
public class SpzxOrderApplication {
public static void main(String[] args)
{
SpringApplication.run(SpzxOrderApplication.class,args);
}
}
1.6、OrderServiceImpl 实现 trade的业务逻辑
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {
@Resource
private CartClient cartClient;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public TradeVo trade() {
//1、查询 当前用户 的购物车中已选中的购物项列表(cart服务管理购物车数据)
Result<List<CartInfo>> cartInfos = cartClient.checkedCartInfos();
if (cartInfos.getCode() != 200) {
throw new SpzxException(ResultCodeEnum.FAIL,null);
}
List<CartInfo> cartInfoList = cartInfos.getData();
if (CollectionUtils.isEmpty(cartInfoList)) {
//没有已选中的购物项
throw new SpzxException(ResultCodeEnum.FAIL,null);
}
Long token = IdUtil.getSnowflake(1,1).nextId();
//将token存到redis:redis的大key问题
stringRedisTemplate.opsForValue().set("spzx:order:"+token.toString(), "1", 30, TimeUnit.MINUTES);
//2、将购物项列表转为 OrderItem列表
List<OrderItem> orderItemList = cartInfoList.stream().map(cartInfo -> {
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(token);
orderItem.setSkuId(cartInfo.getSkuId());
orderItem.setSkuName(cartInfo.getSkuName());
orderItem.setSkuNum(cartInfo.getSkuNum());
orderItem.setSkuPrice(cartInfo.getCartPrice());
orderItem.setThumbImg(cartInfo.getImgUrl());
return orderItem;
}).toList();
TradeVo tradeVo = new TradeVo();
tradeVo.setOrderItemList(orderItemList);
//遍历每一个订单项,计算它的小计金额返回
//最后对所有小计金额累加 得到总金额
tradeVo.setTotalAmount(orderItemList.stream().map(orderItem -> {
return orderItem.getSkuPrice().multiply(new java.math.BigDecimal(orderItem.getSkuNum()));
}).reduce(BigDecimal::add).get());
return tradeVo;
}
}
1.7、此时启动 SpzxOrderApplication
-
点击 购物车页面的 去结算按钮 发现报错NullPointerException
java.lang.NullPointerException: Cannot invoke "com.atguigu.spzx.model.entity.user.UserInfo.getId()"
because the return value of "com.atguigu.spzx.common.util.AuthContextUtil.getUserInfo()" is null
at com.atguigu.spzx.cart.service.impl.CartServiceImpl.getAllCkecked(CartServiceImpl.java:147)
2、 openFeign拦截器使用
2.1、使用feign拦截器拦截请求,获取token,重新传递token
针对service-cart微服务是获取不到当前登录用户的信息。原因:service-order微服务调用service-cart微服务的时候,是通过openFeign进行调用,openFeign在调用的时候会丢失请求头
2.1.1、CartClientInterceptor
@Component
public class CartClientInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//1、获取引入cartClient模块的 项目 在使用cartClient时 请求报文中的token
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
String token = request.getHeader("token");
//2、将token设置到feign客户端的请求报文中
requestTemplate.header("token", token);
}
}
现在订单服务无法使用feign拦截器,因为这个组件类放到了com.atguigu.spzx.cart.interceptor下面,订单服务扫描不到,如果我们想启用它,可以创建注解,然后把注解加到订单服务的启动类上面
2.1.2、@EnableCartClientConfig
@Target({ElementType.TYPE})
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Documented
@Import(value = {CartClientInterceptor.class})
public @interface EnableCartClientConfig {
}
2.1.3、SpzxOrderApplication
@SpringBootApplication
@EnableSpzxServiceAuth
@EnableFeignClients(basePackages = "com.atguigu.spzx")
@EnableCartClientConfig
@MapperScan(basePackages = "com.atguigu.spzx.order.mapper")
@EnableTransactionManagement
public class SpzxOrderApplication {
public static void main(String[] args)
{
SpringApplication.run(SpzxOrderApplication.class,args);
}
}