• 畅购商城(十一):订单


    好好学习,天天向上

    本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航

    收货地址

    不论是在京东还是淘宝下单,都会去选择收货地址,那这些收货地址信息都是保存在MySQL中的,所以第一步要实现查询收货地址的功能。思路就是通过用户名去查询出收货地址,但并不是前端将用户名传到服务器,而是直接解析Token拿到用户名,然后再去查询数据库,这样做的好处就是安全。实现起来很简单,直接上代码。解析Token用的还是之前写的工具类。

    //	AddressController
    @GetMapping("/user/list")
    public Result<List<Address>> list() {
        String username = tokenDecodeUtil.getUserInfo().get("username");
        List<Address> addresses = addressService.list(username);
        return new Result<>(true,StatusCode.OK,"查询成功",addresses);
    }
    ------------------------------------------------------------------------
    //	AddressServiceImpl
    @Override
    public List<Address> list(String username) {
        return addressMapper.list(username);
    }
    -------------------------------------------------------------------------
    //	AddressMapper
    @Select("select * from tb_address where username = #{username}")
    List<Address> list(String username);
    

    下单

    下单并不是前端直接将订单的信息传给服务器,服务器直接创建订单这么简单。这里面有两个需要注意的点:

    • 价格校验

      比如现在有个商品做活动,原价99元,现在限时88元。我在点击提交订单按钮之前价格就已经恢复到99元的,但是页面上的内容没有刷新,还是88元。所以订单提到到服务器的时候,后端这边应该从数据库里面将价格再查询一遍,做个校验,以数据库查询出来的为准,这样可以防止价格变动导致的损失。

    • 库存检查

      检查库存是为了防止超卖,比如我进入到商品详情页的时候显示还剩5件,就在提交订单之前,该商品卖完了,但是由于界面未刷新导致显示还有货。所以订单提交到服务器的时候。后端先从数据库里面查询一下时候还有货,如果没有货的话订单就创建失败,反之成功。

    所以说,并不是所有的东西都是从前端传过来的,具体的可以看一下和订单相关的两张表。

    和订单有关的有两张表,订单是点击下单后生成的记录,比如我一次性下单10件商品,就是一个订单,但是生成了10个订单明细。其中,支付类型收货人收货人手机收货人地址是前端传过来的,其它的信息则是由后端去完善的。

    在介绍完了这些内容之后,就可以去编写代码了。

    @PostMapping
    public Result add(@RequestBody Order order){
        String username = tokenDecodeUtil.getUserInfo().get("username");
        order.setUsername(username);
        orderService.add(order);
        return new Result(true,StatusCode.OK,"添加成功");
    }
    ---------------------------------------------------------------------------
    @Override
    public synchronized void add(Order order) {
        order.setId(String.valueOf(idWorker.nextId()));
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps("Cart_" + order.getUsername());
        int totalNum=0,totalMoney=0;    //总数量,总金额
        LocalDateTime localDateTime = LocalDateTime.now();
        List<OrderItem> orderItems = boundHashOperations.values();   //从购物车中获取订单明细
        if (orderItems == null || orderItems.size()==0) {
            throw new RuntimeException("购物车数据异常,下单失败");
        }
        List<Sku> skuList = skuFeign.findBySkuIds(order.getSkuIds()).getData(); //数据库中对应的sku集合
        //如果数据库中查询出来的sku集合数量与前端传过来的sku数量不一致,说明数据有误,下单失败
        if (skuList.size() != order.getSkuIds().size()){
            throw new RuntimeException("sku数据库数据异常,下单失败");
        }
        Map<Long,Sku> skuMap = skuList.stream().collect(Collectors.toMap(Sku::getId,a -> a));
        //遍历购物车中的数据,判断是否是选中的,将选中的订单明细数据补充完整
        for (OrderItem orderItem : orderItems) {
            if (order.getSkuIds().contains(orderItem.getSkuId())) {     //判断当前遍历到的orderItem是否是选中的
                orderItem.setId(String.valueOf(idWorker.nextId()));
                orderItem.setOrderId(order.getId());
                orderItem.setIsReturn("0");
                Sku sku = skuMap.get(orderItem.getSkuId()); //数据库中的sku
                if (orderItem.getNum() <= sku.getNum()) {   //判断库存是否充足,不足则报异常订单提交失败
                    totalNum += orderItem.getNum();
                } else {
                    throw new RuntimeException("库存不足,下单失败");
                }
                totalMoney += sku.getPrice();
            }
        }
        //减库存,删购物车
        for (OrderItem orderItem : orderItems) {
            if (order.getSkuIds().contains(orderItem.getSkuId())) {
                Sku sku = skuMap.get(orderItem.getSkuId()); //数据库中的sku
                sku.setNum(sku.getNum() - orderItem.getNum());	//减库存
                boundHashOperations.delete(orderItem.getSkuId());	//删购物车
                orderItemMapper.insertSelective(orderItem); //添加到订单明细表
            }
        }
        skuFeign.updateMap(skuMap); //将sku信息提交到数据库中的sku表
        
        order.setCreateTime(localDateTime);
        order.setUpdateTime(localDateTime);
        order.setTotalNum(totalNum);
        order.setTotalMoney(totalMoney);
        order.setSourceType("1");   //1.web
        order.setOrderStatus("0");
        order.setPayStatus("0");
        order.setIsDelete("0");
        orderMapper.insertSelective(order); //添加到订单表
    }
    

    这段代码有点长,首先在Controller层,通过解析Token,拿到了用户名,然后到了Service层。order对象里面有个字段

    private List<Long> skuIds;  //选中的sku的id
    

    这个是在页面选中的商品的id的集合。因为下单的时候,可以选择购物车中的部分商品,并不是所有的商品,用这个就可以判断哪些是选中的。

    在Service层中,先是通过skuIds去调用Feign拿到对应的商品集合数据,因为需要进行库存判断,减库存以及价格查询,所以才去拿到这些数据,为了方便使用,将其转成Map集合。然后查询出购物车中的所有数据,因为我们需要将下单的商品从购物车中移除。

    然后就开始第一遍遍历购物车中的商品,目的是进行库存判断防止超卖,计算总数量,计算总金额。如果下单的数量大于库存,就下单失败。

    如果第一遍遍历的时候没有发生超卖的现象,就可以进行第二遍遍历购物车,目的是减库存,删除购物车中已下单的商品,将订单明细添加到订单明细表中。等遍历完了之后,就将改过库存的sku信息提交到数据库中。使用两次循环而不是一次的原因是只要有一件商品库存不足,那么此次下单就应该不成功,购物车中的数据不做修改,一次循环满足不了需求。

    最后将order信息补充完整后添加到订单表中。至此,订单就算创建完成了。

    在这段代码中,对几个可能会发生异常的情况做了处理。

    • 如果购物车中的数据为0,说明可能是前端传错了令牌,解析出来的用户名下没有商品信息,此时下单失败。
    • 拿着skuId集合去数据库中查询对应的sku集合。如果数据量不一致,说明可能前端传过来的某个skuId有误或者数据库中的数据有误,那么下单失败。
    • 库存不足,下单失败。
    • 为了确保线程安全,加了synchronized关键字。

    好了,下订单的功能就完成了。

    还有一个用户积分的功能,这个就比较简单了,就是通过Feign去调用UserController中的方法,然后根据用户名修改积分数据,没什么好说的,代码就不贴了。

    总结

    这篇文章到这里就结束了,内容比较少,就两个,一个是根据用户名去查询收获地址,还有一个是下单的功能,下单功能不算复杂,就是有些可能会出现问题的几个点需要处理。没有支付的功能怎么能叫下单呢,下篇文章就去实现一下支付的功能。让我们下期再见!

    码字不易,可以的话,给我来个点赞,收藏,关注

    如果你喜欢我的文章,欢迎关注微信公众号 R o b o d,第一时间阅读。

    代码:https://github.com/RobodLee/changgou

  • 相关阅读:
    linux设备驱动归纳总结(三):7.异步通知fasync【转】
    linux设备驱动归纳总结(三):6.poll和sellct【转】
    linux设备驱动归纳总结(三):5.阻塞型IO实现【转】
    【转】14.5.6 禁止和激活中断线
    【转】ubuntu连接android设备(附最简单方法)
    【转】Everything中文绿色版在Win7/8用不了?
    【转】使用dos2unix批量转换文件
    【转】Android开发工具--android-studio-bundle-141.2288178
    【转】Win8/8.1/Win7小技巧:揪出C盘空间占用的真凶
    【转】文件同步软件FreeFileSync
  • 原文地址:https://www.cnblogs.com/robod/p/13589157.html
Copyright © 2020-2023  润新知