订单服务-提交订单业务&立即购买业务


在这里插入图片描述

1、提交订单 业务

在这里插入图片描述

2、在 OrderController 创建 submitOrder 方法

@RestController
@Tag(name = "订单管理模块", description = "订单管理模块")
@RequestMapping("/api/order/orderInfo")
public class OrderController {
    @Operation(summary = "提交订单")
    @PostMapping("/auth/submitOrder")
    public Result submitOrder(@RequestBody OrderSubmitDto orderSubmitDto) {
        Long orderId = orderService.submitOrder(orderSubmitDto);
        return Result.ok(orderId);
    }
}

3、 在 OrderServiceImpl 中实现 submitOrder 方法

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {
    @Resource
    private CartClient cartClient;
    @Resource
    private ProductClient productClient;
    @Resource
    private UserClient userClient;
    @Resource
    private OrderItemService orderItemService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
        //1、幂等性保证:用户订单确认页提交的相同的请求只处理一次
        //获取前端提交的token
        List<OrderItem> orderItemList = orderSubmitDto.getOrderItemList();
        if (CollectionUtils.isEmpty(orderItemList)) {
            //没有订单项
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
        Long token = orderItemList.get(0).getOrderId();
        //获取redis缓存的token,如果存在删除

        Boolean delete = stringRedisTemplate.delete("spzx:order:" + token.toString());
        if (!delete) {
            //幂等性保证:用户订单确认页提交的相同的请求只处理一次
            throw new SpzxException(ResultCodeEnum.ORDER_SUBMIT_REPEAT,null);
        }

        //2、保证订单中商品价格正确、库存足够

        //远程调用:service-product 验证前端提交的每个orderItem的价格 和skuId对应的商品当前价格是否一样 和库存是否足够
        orderItemList.forEach(orderItem -> {
            Result<ProductSku> skuResult = productClient.getById(orderItem.getSkuId());
            if (skuResult.getCode() != 200) {
                throw new SpzxException(ResultCodeEnum.FAIL,null);
            }
            ProductSku sku = skuResult.getData();
            if (sku.getSalePrice().compareTo(orderItem.getSkuPrice()) != 0) {
                //价格不一致
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_PRICE_ERROR,null);
            }
            if (sku.getStockNum() < orderItem.getSkuNum()) {
                //库存不足
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_STOCK_ERROR,null);
            }
        });
        Result<UserAddress> userAddressResult = userClient.findUserAddressById(orderSubmitDto.getUserAddressId());
        if (userAddressResult.getCode() != 200) {
            //用户收件人地址信息查询失败
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
        //3、创建订单持久化
        OrderInfo orderInfo = new OrderInfo();

        UserInfo userInfo = SpzxServiceAuthInterceptor.THREAD_LOCAL.get();
        orderInfo.setUserId(userInfo.getId());

        orderInfo.setNickName(userInfo.getNickName());

        //订单号:展示给用户的唯一订单编号
        String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
        orderInfo.setOrderNo(orderNo);

        orderInfo.setCouponId(0L);

        //计算订单总金额,并返回总金额
        BigDecimal totalAmount = orderItemList.stream().map(orderItem -> {
            return orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum()));
        }).reduce(BigDecimal::add).get();
        orderInfo.setTotalAmount(totalAmount);

        orderInfo.setCouponAmount(new BigDecimal("0"));

        orderInfo.setOriginalTotalAmount(totalAmount);

        orderInfo.setFreightFee(orderSubmitDto.getFreightFee());
        //orderInfo.setPayType(0);
        orderInfo.setOrderStatus(0);
        //收件人信息
        UserAddress userAddress = userAddressResult.getData();
        orderInfo.setReceiverName(userAddress.getName());
        orderInfo.setReceiverPhone(userAddress.getPhone());
        orderInfo.setReceiverTagName(userAddress.getTagName());
        orderInfo.setReceiverProvince(userAddress.getProvinceCode());
        orderInfo.setReceiverCity(userAddress.getCityCode());
        orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
        orderInfo.setReceiverAddress(userAddress.getAddress());

        orderInfo.setPaymentTime(new Date());
        orderInfo.setDeliveryTime(new Date());
        orderInfo.setReceiveTime(new Date());
        orderInfo.setCancelTime(new Date());
        orderInfo.setCancelReason("");
        orderInfo.setRemark(orderSubmitDto.getRemark());

        this.save(orderInfo);
        Long orderId = orderInfo.getId();


        //4、保存订单的所有的订单项列表
        //订单项关联订单 id 保存
        orderItemList.forEach(orderItem -> {
            orderItem.setOrderId(orderId);
        });
        orderItemService.saveBatch(orderItemList);

        //5、删除购物车中已创建订单的购物项
        //远程调用:service-cart 删除当前用户选中的购物项
        cartClient.delCheckedCartInfos();

        //异步:购物车清空无需阻塞订单创建的业务    解耦合:订单创建业务不需要耦合购物车的操作
        //order服务是生产者发送消息
        //cart服务是消费者消费消息:根据userId获取他的购物车 再删除选中的购物项
        //发送订单创建成功的消息到kafka中:购物车需要userId 如果还有其他的业务需要使用订单创建成功的消息,也可以携带其他的数据
        //userId  幂等性校验使用订单id    order信息
        /*HashMap<String, Object> params = new HashMap<>();
        params.put("userId",userInfo.getId());
        params.put("orderId", orderId);
        params.put("totalAmount", orderInfo.getTotalAmount());
        params.put("type", "1");//type表示订单的类型  1、创建订单  2、取消订单   3、更新订单状态
        //将对象转为json字符串
        String json = JSON.toJSONString(params);
        kafkaTemplate.send(SpzxConst.SPZX_ORDER_TOPIC, json);*/

        /*Map<String,Object> params = new HashMap<String,Object>();
        params.put("userId" , userInfo.getId());
        params.put("orderId" , orderId);
        params.put("totalAmount" , orderInfo.getTotalAmount());
        params.put("type","1");//type表示订单操作的类型  1创建订单  2取消订单  3更新订单状态
        //将对象转为json字符串
        String paramsJson = JSON.toJSONString(params);
        kafkaTemplate.send(SpzxConst.SPZX_ORDER_TOPIC ,paramsJson );*/


        //6、返回订单id
        return orderId;
    }
}

4、根据id查询sku详情(service-product")

@RestController
@Tag(name = "商品模块")
@RequestMapping(value = "/api/product")
public class ProductController {
    @Operation(summary = "根据id查询sku商品详情")
    @GetMapping("getById/{skuId}")
    public Result getById(@PathVariable("skuId") Long skuId) {
        ProductSku productSku = productSkuService.getById(skuId);
        return Result.ok(productSku);
    }
}

@FeignClient(value = "service-product")
public interface ProductClient {
    //因为远程调用时data存的是 sku对象
    @GetMapping("/api/product/getById/{skuId}")
    public Result<ProductSku> getById(@PathVariable("skuId") Long skuId);

    @PutMapping("/api/product/updateSkuSaleAndStock/{skuId}/{skuNum}")
    public Result updateSkuSaleAndStock(@PathVariable Long skuId, @PathVariable Integer skuNum);
}

5、查询用户地址保存到订单项中(service-user)

@RestController
@Tag(name = "用户地址管理模块", description = "用户地址管理模块")
@RequestMapping("/api/user/userAddress")
public class UserAddressController {
    @Operation(summary = "获取地址信息")
    @GetMapping("/getUserAddress/{id}")
    public Result findUserAddressById(@PathVariable("id") Long id) {
        UserAddress userAddress = userAddressService.getById(id);
        return Result.ok(userAddress);
    }
}

@FeignClient(value = "service-user")
public interface UserClient {
    @Operation(summary = "获取地址信息")
    @GetMapping("/api/user/userAddress/getUserAddress/{id}")
    public Result<UserAddress> findUserAddressById(@PathVariable("id") Long id);
}

6、删除购物车选中的购物项接口(service-cart)

@RestController
@RequestMapping("/api/order/cart")
@Tag(name = "购物车模块")
public class CartController {

    @Resource
    private CartService cartService;
    @Operation(summary = "删除购物车选中购物项")
    @DeleteMapping("/auth/delCheckedCartInfos")
    public Result delCheckedCartInfos()
    {
        cartService.deleteCheckedCartInfos();
        return Result.ok();
    }
}

@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();
}

7、验价验库存

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {
    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
        List<OrderItem> orderItemList = orderSubmitDto.getOrderItemList();
        if (CollectionUtils.isEmpty(orderItemList)) {
            //没有订单项
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
    	//2、保证订单中商品价格正确、库存足够
        //远程调用:service-product 验证前端提交的每个orderItem的价格 和skuId对应的商品当前价格是否一样 和库存是否足够
        orderItemList.forEach(orderItem -> {
            Result<ProductSku> skuResult = productClient.getById(orderItem.getSkuId());
            if (skuResult.getCode() != 200) {
                throw new SpzxException(ResultCodeEnum.FAIL,null);
            }
            ProductSku sku = skuResult.getData();
            if (sku.getSalePrice().compareTo(orderItem.getSkuPrice()) != 0) {
                //价格不一致
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_PRICE_ERROR,null);
            }
            if (sku.getStockNum() < orderItem.getSkuNum()) {
                //库存不足
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_STOCK_ERROR,null);
            }
        });
    }
}

8、创建订单持久化到mysql

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {

    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
   		 Result<UserAddress> userAddressResult = userClient.findUserAddressById(orderSubmitDto.getUserAddressId());
        if (userAddressResult.getCode() != 200) {
            //用户收件人地址信息查询失败
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
        //3、创建订单持久化
        OrderInfo orderInfo = new OrderInfo();

        UserInfo userInfo = SpzxServiceAuthInterceptor.THREAD_LOCAL.get();
        orderInfo.setUserId(userInfo.getId());

        orderInfo.setNickName(userInfo.getNickName());

        //订单号:展示给用户的唯一订单编号
        String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
        orderInfo.setOrderNo(orderNo);

        orderInfo.setCouponId(0L);

        //计算订单总金额,并返回总金额
        BigDecimal totalAmount = orderItemList.stream().map(orderItem -> {
            return orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum()));
        }).reduce(BigDecimal::add).get();
        orderInfo.setTotalAmount(totalAmount);

        orderInfo.setCouponAmount(new BigDecimal("0"));

        orderInfo.setOriginalTotalAmount(totalAmount);

        orderInfo.setFreightFee(orderSubmitDto.getFreightFee());
        //orderInfo.setPayType(0);
        orderInfo.setOrderStatus(0);
        //收件人信息
        UserAddress userAddress = userAddressResult.getData();
        orderInfo.setReceiverName(userAddress.getName());
        orderInfo.setReceiverPhone(userAddress.getPhone());
        orderInfo.setReceiverTagName(userAddress.getTagName());
        orderInfo.setReceiverProvince(userAddress.getProvinceCode());
        orderInfo.setReceiverCity(userAddress.getCityCode());
        orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
        orderInfo.setReceiverAddress(userAddress.getAddress());

        orderInfo.setPaymentTime(new Date());
        orderInfo.setDeliveryTime(new Date());
        orderInfo.setReceiveTime(new Date());
        orderInfo.setCancelTime(new Date());
        orderInfo.setCancelReason("");
        orderInfo.setRemark(orderSubmitDto.getRemark());

        this.save(orderInfo);
        Long orderId = orderInfo.getId();


        //4、保存订单的所有的订单项列表
        //订单项关联订单 id 保存
        orderItemList.forEach(orderItem -> {
            orderItem.setOrderId(orderId);
        });
        orderItemService.saveBatch(orderItemList);
}

9、清空选中的购物车

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
    	//5、删除购物车中已创建订单的购物项
        //远程调用:service-cart 删除当前用户选中的购物项
        cartClient.delCheckedCartInfos();
    }

}

2、立即购买,不经过购物车

在这里插入图片描述

2.1、在 OrderController 创建 buy方法

@RestController
@Tag(name = "订单管理模块", description = "订单管理模块")
@RequestMapping("/api/order/orderInfo")
public class OrderController {

    @Operation(summary = "立即购买:商品详情页立即购买按钮")
    @GetMapping("/auth/buy/{skuId}")
    public Result buy(@PathVariable("skuId") Long skuId) {
        TradeVo tradeVo = orderService.buy(skuId);
        return Result.ok(tradeVo);
    }
}

2.2、在 OrderServiceImpl 中实现 buy 方法

    @Override
    public TradeVo buy(Long skuId) {
        Result<ProductSku> skuResult = productClient.getById(skuId);
        if (skuResult.getCode() != 200) {
            //商品信息查询失败
            throw new SpzxException(ResultCodeEnum.FAIL,null);
        }
        ProductSku sku = skuResult.getData();
        Long token = IdUtil.getSnowflake(1,1).nextId();
        //将token存到redis:redis的大key问题
        stringRedisTemplate.opsForValue().set("spzx:order:"+token.toString(), "1",  30, TimeUnit.MINUTES);
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(token);
        orderItem.setSkuNum(1);
        orderItem.setSkuPrice(sku.getSalePrice());
        orderItem.setThumbImg(sku.getThumbImg());
        orderItem.setSkuId(skuId);
        orderItem.setSkuName(sku.getSkuName());

        TradeVo tradeVo = new TradeVo();
        tradeVo.setTotalAmount(orderItem.getSkuPrice());
        tradeVo.setOrderItemList(List.of(orderItem));
        return tradeVo;
    }

3、订单幂等性校验分析

幂等性如何保证:相同的请求多次提交如果处理的结果一致
解决思路:和防止表单重复提交一样

在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值