订单服务场景,使用MQ异步实现清空购物车以及减少商品库存和增加销量

一、订单生成

接口:/order/save

1.支付成功

2.进行订单数据保存

3.异步清空购物车数据

4.异步修改商品的库存和销量信息

5.由于商品库存和销量信息发生改变,所以使用异步通知的方式修改es数据库中的商品信息。

6.单业务涉及多DML语句,注意添加事务。

1.准备param实体类

//订单参数
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class OrderParam implements Serializable {

    public static final Long serialVersionUID = 1L;

    @JsonProperty("user_id")
    private Integer userId;
    private List<CartVo> products;

}

//调用product参数
/**
 * projectName: b2c-cloud-store
 *
 * description: 商品库存信息保存param
 */
@Data
public class ProductNumberParam {

    //商品id
    private Integer productId;
    //购买数量
    private Integer productNum;
}

2.准备订单实体类

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@TableName("orders")
public class Order implements Serializable {

    @TableId(type = IdType.AUTO)
    private Integer id;
    @JsonProperty("order_id")
    private Long    orderId; //订单编号,选择使用时间戳
    @JsonProperty("user_id")
    private Integer userId;
    @JsonProperty("product_id")
    private Integer productId;
    @JsonProperty("product_num")
    private Integer productNum;
    @JsonProperty("product_price")
    private Double  productPrice;
    @JsonProperty("order_time")
    private Long    orderTime;
 }

3.准备查询订单需要返回的结果实体类

需要返回商品名称和图片。

//查询订单需要返回结果
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class OrderVo extends Order {

    @JsonProperty("product_name")
    private String productName;
    @JsonProperty("product_picture")
    private String productPicture;

}

4.Controller实现

/product/save接口

@RequestMapping("/order")
public class OrderController {


    @Autowired
    private OrderService orderService;

    /**
     * 订单数据保存
     * @param orderParam
     * @return
     */
    @PostMapping("save")
    public Object save(@RequestBody OrderParam orderParam){


        return orderService.save(orderParam);
    }

5.RabbitMQ关系绑定

在search服务中,定义RbbitMQListener,绑定批量插入消息队列,接收商品集合参数,批量更新到es数据库。

@Component
@Slf4j
public class RabbitMQListener {

    @Autowired
    private RestHighLevelClient client;

    /**
     * 单个更新和插入数据同一个!
     */
  /*  @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "insert.queue"),
            exchange = @Exchange("topic.ex"),
            key = "insert.product"
    ))
    public void insert(Product product) throws IOException {

       IndexRequest indexRequest = new IndexRequest("product")
                .id(product.getProductId().toString());

        ProductDoc productDoc = new ProductDoc(product);

        ObjectMapper objectMapper = new ObjectMapper();
        String json  = objectMapper.writeValueAsString(productDoc);

        indexRequest.source(json, XContentType.JSON);

        client.index(indexRequest, RequestOptions.DEFAULT);


    }*/

    /**
     * 单个删除数据
     *
     * @param
     */
  /*  @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "delete.queue"),
            exchange = @Exchange("topic.ex"),
            key = "delete.product"
    ))
    public void remove(Integer productId) throws IOException {

        DeleteRequest request = new DeleteRequest("product")
                .id(productId.toString());

        client.delete(request, RequestOptions.DEFAULT);
    }*/

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "insertproductList.queue"),
            exchange = @Exchange("topic.ex"),
            key = "insert.productList"
    ))
    public void insertProductList(List<Product> productList) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        ObjectMapper objectMapper = new ObjectMapper();
       for (Product product : productList) {
            ProductDoc productDoc = new ProductDoc(product);
            //插入数据的作用
            IndexRequest indexRequest = new IndexRequest("product").id(product.getProductId().toString());
            String json = objectMapper.writeValueAsString(productDoc);
            indexRequest.source(json,XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
/*        //update方式修改前台显示成功但是es中数据修改异常
            for (Product product : productList) {
            ProductDoc productDoc = new ProductDoc(product);
            //用于插入数据的作用
            UpdateRequest updateRequest = new UpdateRequest("product",id(product.getProductId().toString()));
            //productDoc转成JSON放入
            String json = objectMapper.writeValueAsString(productDoc);
            updateRequest.doc(json,XContentType.JSON);
            bulkRequest.add(updateRequest);
        }*/
        log.info("RabbitMQListener insertProductList parameters:{}", productList);
        bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
        BulkResponse bulk =client.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(bulk.status());
    }
}

6.订单服务业务实现

实现逻辑:1.清空购物车 2.批量插入订单数据 3.商品修改库存参数

参数传OrderParam,包含userId和订单商品集合。获取user_id,然后用CartVo实体的List集合保存商品集合。根据购物车商品集合保存订单,存储商品购物车数量的参数数据,方便修改商品数据。然后批量保存订单,通过异步方式通知购物车服务清空购物车以及商品服务更新库存和销量。

public interface OrderService extends IService<Order> {

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

    @Autowired
    private ProductClient productClient;

    /**
     * 消息队列发送
     */
    @Autowired
    private RabbitTemplate rabbitTemplate;

    //@Autowired
    //private OrderMapper orderMapper;

    /**
     * 订单保存业务
     * 库存和购物车使用mq异步,避免分布式事务!
     * @param orderParam
     * @return
     */
    @Transactional //添加事务
    @Override
    public Object save(OrderParam orderParam) {

        //修改清空购物车的参数
        List<Integer> cartIds = new ArrayList<>();
        //修改批量插入数据库的参数
        List<Order>  orderList = new ArrayList<>();
        //商品修改库存参数集合
        List<ProductNumberParam>  productNumberParamList  =
                                             new ArrayList<>();

        Integer userId = orderParam.getUserId();
        List<CartVo> products = orderParam.getProducts();
        //封装order实体类集合
        //统一生成订单编号和创建时间
        //使用时间戳 + 做订单编号和事件
        long ctime = System.currentTimeMillis();

        for (CartVo cartVo : products) {
            cartIds.add(cartVo.getId()); //进行购物车订单保存
            //订单信息保存
            Order order = new Order();
            order.setOrderId(ctime);
            order.setUserId(userId);
            order.setOrderTime(ctime);
            order.setProductId(cartVo.getProductID());
            order.setProductNum(cartVo.getNum());
            order.setProductPrice(cartVo.getPrice());
            orderList.add(order); //添加用户信息

            //修改信息存储
            ProductNumberParam productNumberParam = new ProductNumberParam();
            productNumberParam.setProductId(cartVo.getProductID());
            productNumberParam.setProductNum(cartVo.getNum());
            productNumberParamList.add(productNumberParam); //添加集合
        }
        //批量数据插入//
        this.saveBatch(orderList); //批量保存

        //修改商品库存 [product-service] [异步通知]
        /**
         *  交换机: topic.ex
         *  routingkey: sub.number
         *  消息: 商品id和减库存数据集合
         */
        rabbitTemplate.convertAndSend("topic.ex","sub.number",productNumberParamList);
        //清空对应购物车数据即可 [注意: 不是清空用户所有的购物车数据] [cart-service] [异步通知]
        /**
         * 交换机:topic.ex
         * routingkey: clear.cart
         * 消息: 要清空的购物车id集合
         */
        rabbitTemplate.convertAndSend("topic.ex","clear.cart",cartIds);

        R ok = R.ok("订单生成成功!");
        log.info("OrderServiceImpl.save业务结束,结果:{}",ok);
        return ok;
    }
 
 }

7.定义productListener监听实现修改库存数据。

@Component
public class ProductRabbitMqListener {
    @Autowired
    private ProductService productService;
    @RabbitListener(
            bindings = @QueueBinding(
                    value = @Queue(name = "sub.queue"),
                    exchange = @Exchange("topic.ex"),
                    key = "sub.number"
            )
    )
    public void subNumber(List<OrderToProduct> orderToProducts){
        productService.subNumber(orderToProducts);
    }
}

1.实现类逻辑

需求点:当生成订单时,因为商品的库存和销量改变,应该异步通知mq更新es,在后台管理模块,商品展示使用的搜索的alllist方法,查询es的商品数据,所以设计该消息队列。

把传入的OrderProduct List集合转换成id做键,num做值的map集合。然后调用map.keyset获取商品id集合,然后调用baseMapper的selectBatchIds方法通过id集合批量修改查询实体。然后批量修改库存和销量,然后批量更新,最后调用Search服务中定义的批量更新es的方法,进行更新。

@Override
    public void subNumber(List<OrderToProduct> orderToProducts) {
        //集合转成map prductid做key num做值
        Map<Integer, OrderToProduct> map = orderToProducts.stream().collect(Collectors.toMap(OrderToProduct::getProductId, v -> v));
        //获取商品id集合
        Set<Integer> productIds = map.keySet();
        List<Product> productList=productMapper.selectBatchIds(productIds);
        for (Product product : productList) {
            //改库存
            Integer num = map.get(product.getProductId()).getNum();
            product.setProductNum(product.getProductNum() - num);
            //改销量
            product.setProductSales(product.getProductSales()+num);
        }
        //批量更新
        this.updateBatchById(productList);
        log.info("ProductServiceImpl.subNumber业务结束,结果:库存和销售量修改完毕");
        rabbitTemplate.convertAndSend("topic.ex", "insert.productList",productList);
        log.info("rabbitTemplate.convertAndSend  insert.productList:{}", productList);;
    }

8.Cart服务设定消息监听

主要用于订单生成调用清空购物车

@Component
public class CartRabbitMqListener {
    @Autowired
    private CartService cartService;
    @RabbitListener(bindings = {
            @QueueBinding(
                    /**
                     * 队列名
                     * 交换机名称
                     * 关键字
                    */
                    value = @Queue(name = "clear.queue"),
                    exchange = @Exchange("topic.ex"),
                    key = "clear.cart"
            )
    })
    public void clear(List<Integer> cartIds){
        cartService.clearIds(cartIds);
    }
}

1.实现类逻辑

更具购物车id集合,批量执行删除操作

  @Override
    public void clearIds(List<Integer> cartIds) {
        cartMapper.deleteBatchIds(cartIds);
        log.info("CartServiceImpl执行完毕,结果:{}",cartIds);
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值