当前位置: 首页 > article >正文

048 下单锁库存

文章目录

  • cubemall-order
    • enume
      • OrderStatusEnum.java
    • feign
      • AuthRemoteClient.java
      • ProductRemoteClient.java
      • StockRemoteClient.java
    • dto
      • OrderDTO.java
      • SubmitOrderDTO.java
    • service
      • OrderServiceImpl.java
    • OrderService.java
    • vo
      • OrderItemVo.java
      • OrderResultVo.java
      • SpuInfoVo.java
      • StockSkuLockVo.java
    • controller
      • OrderWebController.java
  • cubemall-product
    • controller
      • SpuInfoController.java
    • service
      • SpuInfoServiceImpl.java
    • vo
      • SpuInfoVo.java
  • cubemall-stock
    • dao
      • StockSkuDao.java
    • service
      • StockSkuServiceImpl.java
    • vo
      • OrderItemVo.java
      • SkuHasStockVo.java
      • StockSkuLockVo.java
    • controller
      • StockSkuWebController.java
    • mapper
      • StockSkuDao.xml

1 页面传递参数
地址id:addrId
支付方式:payType
防重令牌:防止订单重复提交

package com.xd.cubemall.order.service.dto;


import lombok.Data;
import lombok.ToString;

@ToString
@Data
public class SubmitOrderDTO {

    /*收货地址*/
    private Long addrId;


    /*支付方式*/
    private Integer payType;


    /*防重令牌*/
    private String orderToken;

}

2 返回值是什么
订单对象
异常码

package com.xd.cubemall.order.web.vo;


import com.xd.cubemall.order.entity.OrderEntity;
import lombok.Data;
import lombok.ToString;

@ToString
@Data
public class OrderResultVo {

    /*订单*/
    private OrderEntity order;

    /*订单异常状态*/
    private Integer code;


}

3 订单提交构建订单数据
订单数据
订单明细
价格数据

package com.xd.cubemall.order.service.dto;

import com.xd.cubemall.order.entity.OrderEntity;
import com.xd.cubemall.order.entity.OrderItemEntity;
import lombok.Data;
import lombok.ToString;

import java.math.BigDecimal;
import java.util.List;

@ToString
@Data
public class OrderDTO {

    private OrderEntity orderEntity;

    private List<OrderItemEntity> orderItemList;


    /*实际支付价格*/
    private BigDecimal payPrice;



}

cubemall-order

enume

OrderStatusEnum.java

package com.xd.cubemall.order.enume;

public enum OrderStatusEnum {

    ORDER_NEW(0,"待付款"),
    ORDER_PAYED(1,"已付款"),
    ORDER_SEND(2,"已发货"),
    ORDER_REC(3,"已完成"),
    ORDER_CANCEL(4,"已取消"),
    ORDER_SRVING(5,"售后中"),
    ORDER_SVRED(6,"售后完");



    private Integer code;
    private String msg;

    OrderStatusEnum(Integer code,String msg){
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode(){
        return code;
    }

    public String getMsg(){
        return msg;
    }




}

feign

AuthRemoteClient.java

package com.xd.cubemall.order.feign;

import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.order.web.vo.UserReceiveAddressVo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@FeignClient("cubemall-auth")
public interface AuthRemoteClient {
    /**
     * 根据token查询用户信息
     */
    @RequestMapping("/user/info/{token}")
    public String userInfoByToken(@PathVariable("token") String token);



    /*
查询用户地址信息:根据用户id
 */
    @RequestMapping("/user/userreceiveaddress/address/{userId}")
    public List<UserReceiveAddressVo> addressList(@PathVariable("userId") Long userId);


    @RequestMapping("/user/userreceiveaddress/info/{addrId}")
    R getAddressById(@PathVariable("addrId") Long addrId);
}

ProductRemoteClient.java

package com.xd.cubemall.order.feign;


import com.xd.cubemall.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient("cubemall-product")
public interface ProductRemoteClient {

    /**
     * 根据skuId查询商品spu信息
     * @param skuId
     * @return
     */
    @RequestMapping("/product/spuinfo/spu/{spuId}")
    public R getSpuInfoBySkuId(@PathVariable("spuId") Long skuId);


}

StockRemoteClient.java

package com.xd.cubemall.order.feign;


import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.order.web.vo.StockSkuLockVo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@FeignClient("cubemall-stock")
public interface StockRemoteClient {


    /**
     * 根据skuid查询库存信息
     * @param skuIds
     * @return
     */
    @RequestMapping("/stock/sku/hasStock")
    public R getSkuStock(@RequestBody List<Long> skuIds);


    /**
     * 远程调用库存服务,锁定库存
     * @param stockSkuLockVo
     * @return
     */
    @RequestMapping("/stock/sku/lock")
    R lockOrderStock(@RequestBody StockSkuLockVo stockSkuLockVo);
}

dto

OrderDTO.java

package com.xd.cubemall.order.service.dto;

import com.xd.cubemall.order.entity.OrderEntity;
import com.xd.cubemall.order.entity.OrderItemEntity;
import lombok.Data;
import lombok.ToString;

import java.math.BigDecimal;
import java.util.List;

@ToString
@Data
public class OrderDTO {

    private OrderEntity orderEntity;

    private List<OrderItemEntity> orderItemList;


    /*实际支付价格*/
    private BigDecimal payPrice;



}

SubmitOrderDTO.java

package com.xd.cubemall.order.service.dto;


import lombok.Data;
import lombok.ToString;

@ToString
@Data
public class SubmitOrderDTO {

    /*收货地址*/
    private Long addrId;


    /*支付方式*/
    private Integer payType;


    /*防重令牌*/
    private String orderToken;

}

service

OrderServiceImpl.java

package com.xd.cubemall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.order.entity.OrderItemEntity;
import com.xd.cubemall.order.entity.UserEntity;
import com.xd.cubemall.order.enume.OrderStatusEnum;
import com.xd.cubemall.order.feign.AuthRemoteClient;
import com.xd.cubemall.order.feign.CartRemoteClient;
import com.xd.cubemall.order.feign.ProductRemoteClient;
import com.xd.cubemall.order.feign.StockRemoteClient;
import com.xd.cubemall.order.interceptor.OrderInterceptor;
import com.xd.cubemall.order.service.OrderItemService;
import com.xd.cubemall.order.service.dto.OrderDTO;
import com.xd.cubemall.order.service.dto.SubmitOrderDTO;
import com.xd.cubemall.order.utils.Constants;
import com.xd.cubemall.order.web.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.common.utils.Query;

import com.xd.cubemall.order.dao.OrderDao;
import com.xd.cubemall.order.entity.OrderEntity;
import com.xd.cubemall.order.service.OrderService;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {


    //定义全局threadLocal
    private ThreadLocal<SubmitOrderDTO> submitOrderDTOThreadLocal = new ThreadLocal<>();

    // 注入线程池对象
    @Autowired
    private ThreadPoolExecutor poolExecutor;


    // 注入远程调用地址接口
    @Autowired
    private AuthRemoteClient authRemoteClient;


    // 注入购物车远程调用接口
    @Autowired
    private CartRemoteClient cartRemoteClient;

    // 注入库存服务
    @Autowired
    private StockRemoteClient stockRemoteClient;

    // 注入redis模板服务
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    // 注入商品服务
    @Autowired
    private ProductRemoteClient productRemoteClient;



    // 注入订单明细服务对象
    @Autowired
    private OrderItemService orderItemService;





    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }


    /*查询订单结算页相关数据*/
    // 1.地址收货信息
    // 2.购物清单
    // 3.支付方式(x)
    @Override
    public OrderConfirmVo confirmOrder() {





        // 创建订单结算页视图对象
        OrderConfirmVo confirmVo = new OrderConfirmVo();


        //获取请求上下文信息
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();



        // 1.查询用户地址信息
        // 获取当前用户信息
        UserEntity userInfo = OrderInterceptor.dtoThreadLocal.get();
        // 根据用户id查询用户信息
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {

            // 异步请求之前,共享请求数据
            RequestContextHolder.setRequestAttributes(requestAttributes);

            // 远程调用用户授权认证,用户中心服务,查询用户数据
            List<UserReceiveAddressVo> userReceiveAddressVos = authRemoteClient.addressList(userInfo.getId());
            // 把用户地址信息添加到订单结算实体对象
            confirmVo.setUserReceiveAddressList(userReceiveAddressVos);



        }, poolExecutor);

        // 2.查询购物车清单数据
        CompletableFuture<Void> cartItemsFuture = CompletableFuture.runAsync(() -> {

            // 异步请求之前,共享请求数据
            RequestContextHolder.setRequestAttributes(requestAttributes);

            // 远程调用购物车服务方法,查询购物车清单
            List<CartItemVo> cartItems = cartRemoteClient.getCartItems();
            confirmVo.setItems(cartItems);


        }, poolExecutor).thenRunAsync(()->{
            // 获取购物车清单数据
            List<CartItemVo> items = confirmVo.getItems();

            // 获取所有的skuIds
            List<Long> skuIds = items.stream().map(itemVo -> itemVo.getSkuId()).collect(Collectors.toList());


            // 根据商品skuID查询每一个商品的库存信息
            // 调用库存服务
            R skuStock = stockRemoteClient.getSkuStock(skuIds);

            // 获取是否具有库存数据
            List<StockSkuVo> skuStockDataList = skuStock.getData("data", new TypeReference<List<StockSkuVo>>() {
            });

            // 把是否具有库存的数据转换为map结构数据
            if(skuStockDataList!=null && skuStockDataList.size()>0){
                Map<Long, Boolean> hasStock = skuStockDataList.stream().collect(Collectors.toMap(StockSkuVo::getSkuId, StockSkuVo::getHasStock));
                // 添加对象
                confirmVo.setStocks(hasStock);
            }



        },poolExecutor);

        // 3.获取用户优惠券信息
        Integer integration = userInfo.getIntegration();
        confirmVo.setIntegration(integration);


        //4.获取商品库存信息,判断商品是否具有库存


        // 防重令牌
        // 由于网络延迟(重试),订单提交按钮可能多次提交
        // 防重令牌,提交订单时携带此令牌
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(Constants.ORDER_TOKEN_PREFIX+userInfo.getId(),token,30, TimeUnit.MINUTES);
        // 放入令牌对象属性
        confirmVo.setOrderToken(token);


        //同步调用
        try {
            CompletableFuture.allOf(addressFuture,cartItemsFuture).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        return confirmVo;
    }


    /**
     * 提交订单的方法
     * @param submitOrderDTO
     * @return
     */
    @Override
    public OrderResultVo submitOrder(SubmitOrderDTO submitOrderDTO) {

        //放入threadLocal对象
        submitOrderDTOThreadLocal.set(submitOrderDTO);


        // 构建一个返回值对象
        OrderResultVo resultVo = new OrderResultVo();


        // 获取登录用户信息
        UserEntity userInfo = OrderInterceptor.dtoThreadLocal.get();

        // 根据参数令牌,验证令牌信息,防止订单重复提交
        // 获取,验证令牌整个操作必须是一个原子性的操作
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return 1 else return 0 end";

        // 获取传递的令牌
        String orderToken = submitOrderDTO.getOrderToken();
        // 执行lua脚本,返回执行结果:1,0
        Long res = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
                Arrays.asList(Constants.ORDER_TOKEN_PREFIX + userInfo.getId()),
                orderToken);
        // 判断验证令牌是否成功
        if(res == 0L){
            // 令牌验证失败
            // 1表示验证令牌失败
            resultVo.setCode(1);
            return resultVo;
        }



        // 抽取一个方法,构造订单数据
        OrderDTO orderDTO = this.createOrderDTO();
        // 保存订单数据
        this.saveOrder(orderDTO);

        // 库存
        // 定义锁定库存对象
        StockSkuLockVo stockSkuLockVo = new StockSkuLockVo();
        // 设置属性值
        stockSkuLockVo.setOrderId(orderDTO.getOrderEntity().getOrderSn());


        // 设定锁定商品数据信息
        List<OrderItemVo> itemVos = orderDTO.getOrderItemList().stream().map(item -> {

            OrderItemVo itemVo = new OrderItemVo();
            itemVo.setSkuId(item.getSkuId());
            itemVo.setCount(item.getSkuQuantity());
            itemVo.setTitle(item.getSkuName());

            return itemVo;


        }).collect(Collectors.toList());

        // 把需要锁定库存商品信息放入锁定对象
        stockSkuLockVo.setLockList(itemVos);


        // 开始锁定库存
        R r = stockRemoteClient.lockOrderStock(stockSkuLockVo);


        // 判断库存是否锁定成功
        if(r.getCode() == 0){
            // 返回值对象
            resultVo.setOrder(orderDTO.getOrderEntity());
            resultVo.setCode(0);

            // 清除购物车数据
            redisTemplate.delete(Constants.CART_PREFIX+userInfo.getId());

            return resultVo;
        }else{
            // 库存锁定失败
            resultVo.setCode(2);
            return resultVo;
        }


















    }


    /**
     * 保存订单数据
     * @param orderDTO
     */

    private void saveOrder(OrderDTO orderDTO) {

        // 获取订单对象
        OrderEntity orderEntity = orderDTO.getOrderEntity();
        orderEntity.setCreateTime(new Date());
        orderEntity.setModifyTime(new Date());

        // 保存订单
        this.baseMapper.insert(orderEntity);

        // 保存订单明细
        List<OrderItemEntity> orderItemList = orderDTO.getOrderItemList();
        // 保存订单明细的数据
        orderItemService.saveBatch(orderItemList);


    }


    /**
     * 构造订单数据
     * @return
     */
    private OrderDTO createOrderDTO() {

        // 验证令牌成功,可以开始下单,开始构建订单数据
        // 创建一个订单数据
        OrderDTO orderDTO = new OrderDTO();
        // 构造订单数据
        // 1.生成一个订单号
        String orderId = IdWorker.getTimeId();


        // 构造订单数据
        OrderEntity orderEntity = this.builderOrder(orderId);

        // 把订单数据添加到对象
        orderDTO.setOrderEntity(orderEntity);


        // 构造订单明细数据
        List<OrderItemEntity> orderItemEntityList = this.builderOrderItems(orderId);
        orderDTO.setOrderItemList(orderItemEntityList);

        // 计算整个订单总价格
        this.computePrice(orderEntity,orderItemEntityList);


        return orderDTO;



    }

    /**
     * 计算订单商品总价格
     * @param orderEntity
     * @param orderItemEntityList
     */
    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntityList) {

        // 订单变量,赋值价格
        BigDecimal totalPrice = new BigDecimal("0.0");

        // 优惠价
        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal intergration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");
        BigDecimal freight = new BigDecimal("0.0");

        // 循环订单明细
        for (OrderItemEntity orderItemEntity : orderItemEntityList) {
            coupon = coupon.add(orderItemEntity.getCouponAmount());
            promotion = promotion.add(orderItemEntity.getPromotionAmount());
            intergration = intergration.add(orderItemEntity.getIntegrationAmount());

            // 总价
            totalPrice = totalPrice.add(orderItemEntity.getRealAmount());


        }



        // 订单价格相关数据
        orderEntity.setTotalAmount(totalPrice);

        // 设置应付总金额
        orderEntity.setPayAmount(totalPrice.add(freight));
        orderEntity.setCouponAmount(coupon);
        orderEntity.setPromotionAmount(promotion);
        orderEntity.setIntegrationAmount(intergration);

        // 删除
        orderEntity.setDeleteStatus(0);



    }


    /**
     * 构造订单明细
     * @param orderId
     * @return
     */
    private List<OrderItemEntity> builderOrderItems(String orderId) {

        // 获取登录用户信息
        UserEntity userInfo = OrderInterceptor.dtoThreadLocal.get();

        // 构造订单明细数据
        List<OrderItemEntity> orderItemEntityList = new ArrayList<>();
        // 从购物车中查询订单明细数据
        List<CartItemVo> cartItems = cartRemoteClient.getCartItems();
        if(cartItems!=null && cartItems.size()>0){
            orderItemEntityList = cartItems.stream().map(item->{
                // 获取购物车明细数据,变成订单明细
                // 创建对象
                OrderItemEntity orderItemEntity = new OrderItemEntity();
                // 订单号
                orderItemEntity.setOrderSn(orderId);
                // sku信息
                orderItemEntity.setSkuId(item.getSkuId());
                orderItemEntity.setSkuName(item.getTitle());
                orderItemEntity.setSkuPic(item.getImage());
                orderItemEntity.setSkuPrice(item.getPrice());
                orderItemEntity.setSkuQuantity(item.getCount());
                orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(item.getSkuAttr(),";"));

                // 获取spu数据,根据skuId查询spu数据
                R r1 = productRemoteClient.getSpuInfoBySkuId(item.getSkuId());
                // 获取spu数据
                SpuInfoVo spuInfoVo = r1.getData("data", new TypeReference<SpuInfoVo>() {
                });

                // 构造spu相关订单明细数据
                orderItemEntity.setSpuId(spuInfoVo.getId());
                orderItemEntity.setSpuName(spuInfoVo.getSpuName());
                orderItemEntity.setCategoryId(spuInfoVo.getCategoryId());
                orderItemEntity.setSpuBrand(spuInfoVo.getBrandName());

                // 用户名
                orderItemEntity.setMemberUsername(userInfo.getUsername());
                // 运费
                orderItemEntity.setFreightAmount(BigDecimal.ZERO);

                // 订单项优惠金额
                orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
                orderItemEntity.setCouponAmount(BigDecimal.ZERO);
                orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);

                // 原价
                BigDecimal originPrice = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
                // 实际金额:原价-订单优惠价格
                BigDecimal subtract = originPrice.subtract(orderItemEntity.getCouponAmount())
                        .subtract(orderItemEntity.getIntegrationAmount())
                        .subtract(orderItemEntity.getPromotionAmount());

                // 设置真实价格
                orderItemEntity.setRealAmount(subtract);


                return orderItemEntity;

            }).collect(Collectors.toList());
        }

        return orderItemEntityList;



    }


    /**
     * 构造订单数据
     * @param orderId
     * @return
     */
    private OrderEntity builderOrder(String orderId) {



        // 获取登录用户信息
        UserEntity userInfo = OrderInterceptor.dtoThreadLocal.get();


        // 2.创建一个订单实体对象,添加数据
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setMemberId(userInfo.getId());
        orderEntity.setOrderSn(orderId);
        orderEntity.setMemberUsername(userInfo.getUsername());


        // 获取对应的dto对象
        SubmitOrderDTO submitOrderDTO = submitOrderDTOThreadLocal.get();


        // 获取订单地址信息
        R r = authRemoteClient.getAddressById(submitOrderDTO.getAddrId());
        UserReceiveAddressVo userReceiveAddressVo = r.getData("userReceiveAddress", new TypeReference<UserReceiveAddressVo>() {
        });

        // 构造订单地址数据信息
        orderEntity.setReceiverName(userReceiveAddressVo.getName());
        orderEntity.setReceiverPhone(userReceiveAddressVo.getPhone());
        orderEntity.setReceiverPostCode(userReceiveAddressVo.getPostCode());
        orderEntity.setReceiverProvince(userReceiveAddressVo.getProvince());
        orderEntity.setReceiverCity(userReceiveAddressVo.getCity());
        orderEntity.setReceiverRegion(userReceiveAddressVo.getRegion());
        orderEntity.setReceiverDetailAddress(userReceiveAddressVo.getDetailAddress());


        // 构造订单状态数据
        orderEntity.setStatus(OrderStatusEnum.ORDER_NEW.getCode());
        orderEntity.setAutoConfirmDay(7);
        orderEntity.setConfirmStatus(0);

        return orderEntity;


    }
}

OrderService.java

package com.xd.cubemall.order.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.order.entity.OrderEntity;
import com.xd.cubemall.order.service.dto.SubmitOrderDTO;
import com.xd.cubemall.order.web.vo.OrderConfirmVo;
import com.xd.cubemall.order.web.vo.OrderResultVo;

import java.util.Map;

/**
 * 订单
 *
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-11-23 16:42:02
 */
public interface OrderService extends IService<OrderEntity> {

    PageUtils queryPage(Map<String, Object> params);


    /*查询订单结算页相关数据*/
    // 1.地址收货信息
    // 2.购物清单
    // 3.支付方式(x)
    OrderConfirmVo confirmOrder();


    /**
     * 提交订单的方法
     * @param submitOrderDTO
     * @return
     */
    OrderResultVo submitOrder(SubmitOrderDTO submitOrderDTO);



}


vo

OrderItemVo.java

package com.xd.cubemall.order.web.vo;


import lombok.Data;
import lombok.ToString;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;

@ToString
@Data
public class OrderItemVo  {



    // sku商品id
    private Long skuId;

    //商品标题
    private String title;

    //商品图片
    private String image;

    //商品属性
    private List<String> skuAttr;

    //商品价格
    private BigDecimal price;

    //商品数量
    private Integer count;

    //总价格
    private BigDecimal totalPrice;

    // 是否选中
    private boolean check = true;


    //计算单价*数量的商品总价
//    public BigDecimal getTotalPrice(){
//        return this.price.multiply(new BigDecimal(count+""));
//    }


}

OrderResultVo.java

package com.xd.cubemall.order.web.vo;


import com.xd.cubemall.order.entity.OrderEntity;
import lombok.Data;
import lombok.ToString;

@ToString
@Data
public class OrderResultVo {

    /*订单*/
    private OrderEntity order;

    /*订单异常状态*/
    private Integer code;


}

SpuInfoVo.java

package com.xd.cubemall.order.web.vo;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

/**
 * spu信息
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
@Data
public class SpuInfoVo implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 自增ID
	 */
	private Long id;
	/**
	 * spu名称
	 */
	private String spuName;
	/**
	 * spu描述
	 */
	private String spuDescription;
	/**
	 * 分类ID
	 */
	private Long categoryId;
	/**
	 * 品牌名称
	 */
	private String brandName;
	/**
	 * 权重
	 */
	private BigDecimal weight;
	/**
	 * 发布状态
	 */
	private Integer publishStatus;
	/**
	 * 
	 */
	private Date createTime;
	/**
	 * 
	 */
	private Date updateTime;

}

StockSkuLockVo.java

package com.xd.cubemall.order.web.vo;


import lombok.Data;
import lombok.ToString;

import java.io.Serializable;
import java.util.List;

@ToString
@Data
public class StockSkuLockVo {


    private String orderId;

    /*锁定商品库存信息*/
    private List<OrderItemVo> lockList;

}

controller

OrderWebController.java

package com.xd.cubemall.order.web;


import com.xd.cubemall.order.service.OrderService;
import com.xd.cubemall.order.service.dto.SubmitOrderDTO;
import com.xd.cubemall.order.web.vo.OrderConfirmVo;
import com.xd.cubemall.order.web.vo.OrderResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
@Slf4j
public class OrderWebController {


    // 注入订单服务对象
    @Autowired
    private OrderService orderService;







    /**
     * 去到订单结算页
     */
    @RequestMapping("/order/confirm")
    public String confirmOrder(Model model){

        // 查询结算页相关数据
        OrderConfirmVo confirmVo = orderService.confirmOrder();

        model.addAttribute("confirmVo",confirmVo);

        // 1.地址收货信息
        // 2.购物清单
        // 3.支付方式

        return "order";
    }


    /**
     * 下单功能实现
     */
    @RequestMapping("/order/createOrder")
    public String createOrder(SubmitOrderDTO orderDTO, Model model, RedirectAttributes redirectAttributes){
        // 调用服务层方法,实现下单操作
        OrderResultVo resultVo = orderService.submitOrder(orderDTO);
        // 判断下单是否成功
        if(resultVo.getCode() == 0){
            // 下单成功
            model.addAttribute("submitOrder",resultVo);

            return "pay";
        }

        String msg = "下单失败";

        // 下单失败
        switch (resultVo.getCode()){
            case 1: msg += "令牌信息过期";break;
            case 2: msg += "订单数据发生变化,请确认后再提交";break;
            case 3: msg += "库存锁定失败";break;
        }

        redirectAttributes.addFlashAttribute("msg",msg);
        return "redirect:http://localhost:8084/order/confirm";
    }



}

cubemall-product

controller

SpuInfoController.java



    /**
     * 根据skuId查询商品spu信息
     * @param skuId
     * @return
     */
    @RequestMapping("/spu/{spuId}")
    public R getSpuInfoBySkuId(@PathVariable("spuId") Long skuId){
        R r = spuInfoService.getSpuInfoBySkuId(skuId);
        return r;
    }

service

SpuInfoServiceImpl.java

    /**
     * 根据skuId查询商品spu信息
     * @param skuId
     * @return
     */
    @Override
    public R getSpuInfoBySkuId(Long skuId) {

        // 根据skuID查询sku实体
        SkuInfoEntity skuInfoEntity = skuInfoService.getById(skuId);
        // 获取spuID
        Long spuId = skuInfoEntity.getSpuId();
        // 根据spuID查询spu数据信息
        SpuInfoEntity spuInfoEntity = this.baseMapper.selectById(spuId);

        // 查询品牌名称
        BrandEntity brandEntity = brandService.getById(spuInfoEntity.getBrandId());

        // 创建对象
        SpuInfoVo spuInfoVo = new SpuInfoVo();
        spuInfoVo.setBrandName(brandEntity.getName());
        spuInfoVo.setCategoryId(spuInfoEntity.getCategoryId());

        spuInfoVo.setId(spuInfoEntity.getId());
        spuInfoVo.setCreateTime(spuInfoEntity.getCreateTime());
        spuInfoVo.setPublishStatus(spuInfoEntity.getPublishStatus());
        spuInfoVo.setSpuDescription(spuInfoEntity.getSpuDescription());
        spuInfoVo.setSpuName(spuInfoEntity.getSpuName());
        spuInfoVo.setUpdateTime(spuInfoEntity.getUpdateTime());
        spuInfoVo.setWeight(spuInfoEntity.getWeight());





        return R.ok().setData(spuInfoVo);
    }

vo

SpuInfoVo.java

package com.xd.cubemall.product.vo;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

/**
 * spu信息
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-08-13 01:36:04
 */
@Data
public class SpuInfoVo implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 自增ID
	 */
	private Long id;
	/**
	 * spu名称
	 */
	private String spuName;
	/**
	 * spu描述
	 */
	private String spuDescription;
	/**
	 * 分类ID
	 */
	private Long categoryId;
	/**
	 * 品牌名称
	 */
	private String brandName;
	/**
	 * 权重
	 */
	private BigDecimal weight;
	/**
	 * 发布状态
	 */
	private Integer publishStatus;
	/**
	 * 
	 */
	private Date createTime;
	/**
	 * 
	 */
	private Date updateTime;

}

cubemall-stock

dao

StockSkuDao.java

package com.xd.cubemall.stock.dao;

import com.xd.cubemall.stock.entity.StockSkuEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 商品库存
 * 
 * @author xuedong
 * @email email@gmail.com
 * @date 2024-11-25 00:49:57
 */
@Mapper
public interface StockSkuDao extends BaseMapper<StockSkuEntity> {

    List<Long> selectHasStockListStockIds(@Param("skuId") Long skuId);

    Long lockSkuStock(@Param("skuId") Long skuId, @Param("stockId") Long stockId, @Param("num") Integer num);
}

service

StockSkuServiceImpl.java

package com.xd.cubemall.stock.service.impl;

import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.stock.entity.StockOrderTaskDetailEntity;
import com.xd.cubemall.stock.entity.StockOrderTaskEntity;
import com.xd.cubemall.stock.service.StockOrderTaskDetailService;
import com.xd.cubemall.stock.service.StockOrderTaskService;
import com.xd.cubemall.stock.web.vo.OrderItemVo;
import com.xd.cubemall.stock.web.vo.SkuHasStockVo;
import com.xd.cubemall.stock.web.vo.StockSkuLockVo;
import com.xd.cubemall.stock.web.vo.StockSkuVo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xd.cubemall.common.utils.PageUtils;
import com.xd.cubemall.common.utils.Query;

import com.xd.cubemall.stock.dao.StockSkuDao;
import com.xd.cubemall.stock.entity.StockSkuEntity;
import com.xd.cubemall.stock.service.StockSkuService;
import org.springframework.util.StringUtils;


@Service("stockSkuService")
public class StockSkuServiceImpl extends ServiceImpl<StockSkuDao, StockSkuEntity> implements StockSkuService {

    @Autowired
    private StockOrderTaskService orderTaskService;

    // 注入dao
    @Autowired
    private StockSkuDao stockSkuDao;

    @Autowired
    private StockOrderTaskDetailService orderTaskDetailService;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<StockSkuEntity> page = this.page(
                new Query<StockSkuEntity>().getPage(params),
                new QueryWrapper<StockSkuEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 根据skuID查询数据信息
     * @param skuIds
     * @return
     */
    @Override
    public R getSkuStock(List<Long> skuIds) {




        //List<StockSkuEntity> stockSkuList = this.baseMapper.selectBatchIds(skuIds);
        //1个skuid只有1个对应的库存数据


//        //循环skuids,根据skuId查询
//        for (Long skuId : skuIds) {
//            StockSkuEntity stockSkuEntity = this.baseMapper.selectOne(new QueryWrapper<StockSkuEntity>().eq("sku_id", skuId));
//            stockSkuList.add(stockSkuEntity);
//        }


        // 创建一个集合,封装对象
        // stream
        List<StockSkuVo> stockSkuList =  skuIds.stream().map(skuId -> {
            // 根据skuID查询库存信息
            StockSkuEntity stockSkuEntity = this.baseMapper.selectOne(new QueryWrapper<StockSkuEntity>().eq("sku_id", skuId));
            // 获取库存数量
            Integer stock = stockSkuEntity.getStock();

            // 创建封装数据对象
            StockSkuVo stockSkuVo = new StockSkuVo();
            stockSkuVo.setSkuId(skuId);
            // 是否有库存
            stockSkuVo.setHasStock(stock == null?false:stock>0);
            return stockSkuVo;
        }).collect(Collectors.toList());



        return R.ok().setData(stockSkuList);
    }


    /**
     * 锁定库存
     * @param stockSkuLockVo
     * @return
     */
    @Override
    public boolean lockOrderStock(StockSkuLockVo stockSkuLockVo) {


        // 定义字段,保存锁定状态
        boolean skuStockLocked = false;



        // 保存库存订单工作单信息
        StockOrderTaskEntity orderTaskEntity = new StockOrderTaskEntity();
        orderTaskEntity.setOrderSn(stockSkuLockVo.getOrderId());
        orderTaskEntity.setCreateTime(new Date());

        // 保存
        orderTaskService.save(orderTaskEntity);

        // 按照收货地址,找到一个最近的仓库,锁定库存
        // 找到每一个商品属于哪个库存,且库存是否充足,如果条件满足,就锁定库存
        // 获取订单商品明细
        List<OrderItemVo> lockList = stockSkuLockVo.getLockList();

        List<SkuHasStockVo> hasStockVoList = lockList.stream().map(item->{
            // 创建对象
            SkuHasStockVo hasStockVo = new SkuHasStockVo();
            hasStockVo.setSkuId(item.getSkuId());
            hasStockVo.setNum(item.getCount());

            //查询这个商品在哪个仓库有库存
            //总库存减去购买商品数量,如果大于0,那么表示可以锁定库存
            List<Long> stockIds = stockSkuDao.selectHasStockListStockIds(item.getSkuId());
            hasStockVo.setStockId(stockIds);
            return hasStockVo;


        }).collect(Collectors.toList());


        // 锁定库存
        for (SkuHasStockVo hasStockVo : hasStockVoList) {

            //  获取仓库数据
            List<Long> stockIds = hasStockVo.getStockId();
            // 判断仓库是否存在
            if(!StringUtils.isEmpty(stockIds)){

                // 锁定每一个商品的库存
                for (Long stockId : stockIds) {

                    // 开始锁定,锁定成功返回1,否则返回0
                    Long count = stockSkuDao.lockSkuStock(hasStockVo.getSkuId(),stockId,hasStockVo.getNum());

                    // 判断锁定库存是否成功
                    if(count == 1){
                        skuStockLocked = true;

                        // 锁定库存成功,保存工作单详细信息
                        StockOrderTaskDetailEntity orderTaskDetailEntity = new StockOrderTaskDetailEntity();
                        orderTaskDetailEntity.setSkuId(hasStockVo.getSkuId());
                        orderTaskDetailEntity.setSkuName("");
                        orderTaskDetailEntity.setSkuNum(hasStockVo.getNum());
                        orderTaskDetailEntity.setTaskId(orderTaskEntity.getId());

                        // 保存实体对象
                        orderTaskDetailService.save(orderTaskDetailEntity);

                    }




                }



            }

        }


        return skuStockLocked;
    }
}

vo

OrderItemVo.java

package com.xd.cubemall.stock.web.vo;


import lombok.Data;
import lombok.ToString;

import java.math.BigDecimal;
import java.util.List;

@ToString
@Data
public class OrderItemVo {

    // sku商品id
    private Long skuId;

    //商品标题
    private String title;

    //商品图片
    private String image;

    //商品属性
    private List<String> skuAttr;

    //商品价格
    private BigDecimal price;

    //商品数量
    private Integer count;

    //总价格
    private BigDecimal totalPrice;

    // 是否选中
    private boolean check = true;


    //计算单价*数量的商品总价
    public BigDecimal getTotalPrice(){
        return this.price.multiply(new BigDecimal(count+""));
    }


}

SkuHasStockVo.java

package com.xd.cubemall.stock.web.vo;


import lombok.Data;
import lombok.ToString;

import java.util.List;

@ToString
@Data
public class SkuHasStockVo {

    private Long skuId;
    private Integer num;
    private List<Long> stockId;



}

StockSkuLockVo.java

package com.xd.cubemall.stock.web.vo;


import lombok.Data;
import lombok.ToString;

import java.util.List;

@ToString
@Data
public class StockSkuLockVo {

    private String orderId;

    /*锁定商品库存信息*/
    private List<OrderItemVo> lockList;

}

controller

StockSkuWebController.java

package com.xd.cubemall.stock.web;


import com.xd.cubemall.common.utils.R;
import com.xd.cubemall.stock.service.StockSkuService;
import com.xd.cubemall.stock.web.vo.StockSkuLockVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class StockSkuWebController {

    // 注入service服务对象
    @Autowired
    private StockSkuService stockSkuService;


    /**
     * 根据skuID查询数据信息
     * @param skuIds
     * @return
     */
    @RequestMapping("/stock/sku/hasStock")
    public R getSkuStock(@RequestBody List<Long> skuIds){
        R r = stockSkuService.getSkuStock(skuIds);
        return r;
    }

    /**
     * 锁定库存
     * @param stockSkuLockVo
     * @return
     */
    @RequestMapping("/stock/sku/lock")
    public R lockOrderStock(@RequestBody StockSkuLockVo stockSkuLockVo){
        boolean isLock = stockSkuService.lockOrderStock(stockSkuLockVo);

        if(isLock){
            return R.ok().put("code", 0);
        }else{
            return R.ok().put("code", 99);
        }


    }


}

mapper

StockSkuDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xd.cubemall.stock.dao.StockSkuDao">

	<!-- 可根据自己的需求,是否要使用 -->
    <resultMap type="com.xd.cubemall.stock.entity.StockSkuEntity" id="stockSkuMap">
        <result property="id" column="id"/>
        <result property="skuId" column="sku_id"/>
        <result property="wareId" column="ware_id"/>
        <result property="stock" column="stock"/>
        <result property="skuName" column="sku_name"/>
        <result property="stockLocked" column="stock_locked"/>
    </resultMap>


    <!--根据skuId查询仓库信息-->
    <select id="selectHasStockListStockIds" resultType="java.lang.Long">
        SELECT
            ware_id
        FROM
            tb_stock_sku
        WHERE
            sku_id = #{skuId}
          AND stock - stock_locked > 0
    </select>
    
    
    <update id="lockSkuStock">

        UPDATE tb_stock_sku
        SET stock_locked = stock_locked + #{num}
        WHERE
            sku_id = #{skuId}
          AND ware_id = #{stockId}
          AND stock - stock_locked > 0

    </update>


</mapper>


http://www.kler.cn/a/412131.html

相关文章:

  • 【人工智能】Python常用库-Scikit-learn常用方法教程
  • 【机器学习】如何使用Python的Scikit-learn库实现机器学习模型,并对数据进行预处理和特征缩放以提高模型性能?
  • 【VUE3】VUE组合式(响应式)API常见语法
  • 英伟达发布 Edify 3D 生成模型,可以在两分钟内生成详细的、可用于生产的 3D 资源、生成有组织的 UV 贴图、4K 纹理和 PBR 材质。
  • 验证视图状态 MAC 失败,配置machineKey
  • IDEA无法创建java8、11项目创建出的pom.xml为空
  • TCP(Transmission Control Protocol,传输控制协议)报文段的首部格式
  • 【系统设计】图书管理系统设计-2-数据库创建
  • Acunetix v24.10.241106172web漏洞扫描工具安装教程+分享(linux+Windows)
  • TCP socket api详解 续
  • Android 常用命令和工具解析之GPU相关
  • 如何制作项目网页
  • netconf 代码示例-客户端
  • 2023.11 Graph-Enriched Biomedical Language Models: A Research Proposal
  • 斐波那契数列 相关问题 详解
  • 算法篇:贪心算法
  • vue3 属性透传
  • Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No “exports“ main defined
  • 本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
  • 详解Qt 中使用虚拟键盘(软键盘qtvirtualkeyboard)
  • 【面试分享】主流编程语言的内存回收机制及其优缺点
  • fastjson不出网打法—BCEL链
  • Leetcode 290 word Pattern
  • 【Qt】Qt 在main.cpp中使用tr()函数报错
  • 【设计模式】【结构型模式(Structural Patterns)】之装饰模式(Decorator Pattern)
  • WordPress文章目录插件,LuckyWP Table of Contents自动生成文章插件