1.订单服务基本概念
1.订单中心
- 电商系统涉及到3流,分别是信息流、资金流、物流,而订单系统作为中枢将三者有机的集合起来。
- 订单模块是电商系统的枢纽,在订单这个环节上需求获取多个模块的数据和信息,同时对这些信息进行加工处理后流向下个环节,这一系列就构成了订单的信息流通。
2.订单构成
2.环境搭建
- 上传nginx资源
- html替换路径
- …
3.订单登陆拦截
- 购物单点击去结算,必须是登陆状态下才允许结算,所以在跳转到结算页时必须做登陆校验
@Configuration
public class LoginUserInterceptor implements HandlerInterceptor {
public static ThreadLocal<MemberResponseVo> loginUser = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object attribute = request.getSession().getAttribute(AuthConstant.LOGIN_USER);
if (Objects.nonNull(attribute) && attribute instanceof MemberResponseVo) {
MemberResponseVo vo = (MemberResponseVo) attribute;
loginUser.set(vo);
return true;
}
request.getSession().setAttribute("msg", "请先进行登录");
response.sendRedirect("http://auth.dreammall.com/login.html");
return false;
}
}
@Configuration
public class DreamMallOrderWebConfig implements WebMvcConfigurer {
@Autowired
private LoginUserInterceptor loginUserInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**");
}
}
4.订单确认页功能实现
-
controller层
/** * 去结算确认页 * @param model * @param request * @return * @throws ExecutionException * @throws InterruptedException */ @GetMapping(value = "/toTrade") public String toTrade(Model model, HttpServletRequest request){ OrderConfirmVo confirmVo = orderService.confirmOrder(); model.addAttribute("confirmOrderData",confirmVo); //展示订单确认的数据 return "confirm"; }
-
service层实现
@Override public OrderConfirmVo confirmOrder() { //构建OrderConfirmVo OrderConfirmVo confirmVo = new OrderConfirmVo(); //获取当前用户登录的信息 MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get(); //获取当前线程请求头信息(解决Feign异步调用丢失请求头问题) RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); //开启第一个异步任务 CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> { //每一个线程都来共享之前的请求数据 RequestContextHolder.setRequestAttributes(requestAttributes); //1、远程查询所有的收获地址列表 List<MemberAddressVo> address = memberFeignService.getAddress(memberResponseVo.getId()); confirmVo.setMemberAddressVos(address); }, threadPoolExecutor); //开启第二个异步任务 CompletableFuture<Void> cartInfoFuture = CompletableFuture.runAsync(() -> { //每一个线程都来共享之前的请求数据 RequestContextHolder.setRequestAttributes(requestAttributes); //2、远程查询购物车所有选中的购物项 List<OrderItemVo> currentCartItems = cartFeignService.getCurrentCartItems(); confirmVo.setItems(currentCartItems); //feign在远程调用之前要构造请求,调用很多的拦截器 }, threadPoolExecutor).thenRunAsync(() -> { List<OrderItemVo> items = confirmVo.getItems(); //获取全部商品的id List<Long> skuIds = items.stream() .map((OrderItemVo::getSkuId)) .collect(Collectors.toList()); //远程查询商品库存信息 R skuHasStock = wareFeignService.getSkuHasStock(skuIds); List<SkuStockVo> skuStockVos = skuHasStock.getData("data", new TypeReference<List<SkuStockVo>>() {}); if (CollectionUtil.isNotEmpty(skuStockVos)) { //将skuStockVos集合转换为map Map<Long, Boolean> skuHasStockMap = skuStockVos.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock)); confirmVo.setStocks(skuHasStockMap); } }, threadPoolExecutor); //3、查询用户积分 Integer integration = memberResponseVo.getIntegration(); confirmVo.setIntegration(integration); //4、价格数据自动计算 CompletableFuture.allOf(addressFuture, cartInfoFuture).join(); return confirmVo; } }
-
主要遇到的问题
-
feign远程调用导致request头中丢失cookie,从而cart模块从分布式session无法获取到数据
-
-
解决方式如下
// 自定义requestInterceptor 将当前线程的reuqest头中的cookie再次设置到请求头 @Configuration public class DreamMallFeignConfig { @Bean("requestInterceptor") public RequestInterceptor requestInterceptor() { return template -> { // 获取当前线程请求 ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); // 将cookie再次设置到请求头中 if (null != requestAttributes) { String cookie = requestAttributes.getRequest().getHeader("Cookie"); template.header("Cookie", cookie); } }; } }
-
第二个问题,多线程下依然存在丢失cookie的情况
通过上下文参数传递的方式解决