mirror of
https://github.com/kirklin/kkmall.git
synced 2024-09-20 06:46:15 +08:00
商城业务 --> 订单提交基本逻辑完成
This commit is contained in:
parent
412781ef91
commit
04d98a1cb0
|
@ -2,9 +2,12 @@ package name.lkk.kkmall.order.feign;
|
|||
|
||||
|
||||
import name.lkk.common.utils.R;
|
||||
import name.lkk.kkmall.order.vo.WareSkuLockVo;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -14,4 +17,13 @@ public interface WmsFeignService {
|
|||
@PostMapping("/ware/waresku/hasStock")
|
||||
R getSkuHasStock(@RequestBody List<Long> SkuIds);
|
||||
|
||||
}
|
||||
@GetMapping("/ware/wareinfo/fare")
|
||||
R getFare(@RequestParam("addrId") Long addrId);
|
||||
|
||||
/**
|
||||
* 锁定库存
|
||||
*/
|
||||
@PostMapping("/ware/waresku/lock/order")
|
||||
R orderLockStock(@RequestBody WareSkuLockVo vo);
|
||||
|
||||
}
|
|
@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||
import name.lkk.common.utils.PageUtils;
|
||||
import name.lkk.kkmall.order.entity.OrderEntity;
|
||||
import name.lkk.kkmall.order.vo.OrderConfirmVo;
|
||||
import name.lkk.kkmall.order.vo.OrderSubmitVo;
|
||||
import name.lkk.kkmall.order.vo.SubmitOrderResponseVo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -20,5 +22,9 @@ public interface OrderService extends IService<OrderEntity> {
|
|||
PageUtils queryPage(Map<String, Object> params);
|
||||
|
||||
OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException;
|
||||
|
||||
SubmitOrderResponseVo submitOrder(OrderSubmitVo submitVo);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,11 @@ package name.lkk.kkmall.order.service.impl;
|
|||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import name.lkk.common.enume.OrderStatusEnum;
|
||||
import name.lkk.common.exception.NotStockException;
|
||||
import name.lkk.common.utils.PageUtils;
|
||||
import name.lkk.common.utils.Query;
|
||||
import name.lkk.common.utils.R;
|
||||
|
@ -12,24 +15,27 @@ import name.lkk.common.vo.MemberRsepVo;
|
|||
import name.lkk.kkmall.order.constant.OrderConstant;
|
||||
import name.lkk.kkmall.order.dao.OrderDao;
|
||||
import name.lkk.kkmall.order.entity.OrderEntity;
|
||||
import name.lkk.kkmall.order.entity.OrderItemEntity;
|
||||
import name.lkk.kkmall.order.feign.CartFeignService;
|
||||
import name.lkk.kkmall.order.feign.MemberFeignService;
|
||||
import name.lkk.kkmall.order.feign.ProductFeignService;
|
||||
import name.lkk.kkmall.order.feign.WmsFeignService;
|
||||
import name.lkk.kkmall.order.interceptor.LoginUserInterceptor;
|
||||
import name.lkk.kkmall.order.service.OrderItemService;
|
||||
import name.lkk.kkmall.order.service.OrderService;
|
||||
import name.lkk.kkmall.order.vo.MemberAddressVo;
|
||||
import name.lkk.kkmall.order.vo.OrderConfirmVo;
|
||||
import name.lkk.kkmall.order.vo.OrderItemVo;
|
||||
import name.lkk.kkmall.order.vo.SkuStockVo;
|
||||
import name.lkk.kkmall.order.to.OrderCreateTo;
|
||||
import name.lkk.kkmall.order.vo.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
@ -40,21 +46,30 @@ import java.util.stream.Collectors;
|
|||
@Service("orderService")
|
||||
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
|
||||
|
||||
@Autowired
|
||||
private ThreadPoolExecutor executor;
|
||||
|
||||
@Autowired
|
||||
private OrderItemService orderItemService;
|
||||
|
||||
@Autowired
|
||||
private MemberFeignService memberFeignService;
|
||||
|
||||
@Autowired
|
||||
private CartFeignService cartFeignService;
|
||||
|
||||
@Autowired
|
||||
private ThreadPoolExecutor executor;
|
||||
|
||||
@Autowired
|
||||
private WmsFeignService wmsFeignService;
|
||||
|
||||
@Autowired
|
||||
private ProductFeignService productFeignService;
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();
|
||||
|
||||
|
||||
@Override
|
||||
public PageUtils queryPage(Map<String, Object> params) {
|
||||
IPage<OrderEntity> page = this.page(
|
||||
|
@ -124,4 +139,237 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> impleme
|
|||
return confirmVo;
|
||||
}
|
||||
|
||||
// @GlobalTransactional
|
||||
@Transactional
|
||||
@Override
|
||||
public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
|
||||
// 当条线程共享这个对象
|
||||
confirmVoThreadLocal.set(vo);
|
||||
SubmitOrderResponseVo submitVo = new SubmitOrderResponseVo();
|
||||
// 0:正常
|
||||
submitVo.setCode(0);
|
||||
// 去服务器创建订单,验令牌,验价格,所库存
|
||||
MemberRsepVo memberRsepVo = LoginUserInterceptor.threadLocal.get();
|
||||
// 1. 验证令牌 [必须保证原子性] 返回 0 or 1
|
||||
// 0 令牌删除失败 1删除成功
|
||||
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
|
||||
String orderToken = vo.getOrderToken();
|
||||
|
||||
// 原子验证令牌 删除令牌
|
||||
Long result = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRsepVo.getId()), orderToken);
|
||||
if (result == 0L) {
|
||||
// 令牌验证失败
|
||||
submitVo.setCode(1);
|
||||
} else {
|
||||
// 令牌验证成功
|
||||
// 1 .创建订单等信息
|
||||
OrderCreateTo order = createOrder();
|
||||
// 2. 验价
|
||||
BigDecimal payAmount = order.getOrder().getPayAmount();
|
||||
BigDecimal voPayPrice = vo.getPayPrice();
|
||||
if (Math.abs(payAmount.subtract(voPayPrice).doubleValue()) < 0.01) {
|
||||
// 金额对比成功
|
||||
// 3.保存订单
|
||||
saveOrder(order);
|
||||
// 4.库存锁定
|
||||
WareSkuLockVo lockVo = new WareSkuLockVo();
|
||||
lockVo.setOrderSn(order.getOrder().getOrderSn());
|
||||
List<OrderItemVo> locks = order.getOrderItems().stream().map(item -> {
|
||||
OrderItemVo itemVo = new OrderItemVo();
|
||||
// 锁定的skuId 这个skuId要锁定的数量
|
||||
itemVo.setSkuId(item.getSkuId());
|
||||
itemVo.setCount(item.getSkuQuantity());
|
||||
itemVo.setTitle(item.getSkuName());
|
||||
return itemVo;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
lockVo.setLocks(locks);
|
||||
// 远程锁库存
|
||||
R r = wmsFeignService.orderLockStock(lockVo);
|
||||
if (r.getCode() == 0) {
|
||||
// 库存足够 锁定成功 给MQ发送消息
|
||||
submitVo.setOrderEntity(order.getOrder());
|
||||
// rabbitTemplate.convertAndSend(this.eventExchange, this.createOrder, order.getOrder());
|
||||
// int i = 10/0;
|
||||
} else {
|
||||
// 锁定失败
|
||||
String msg = (String) r.get("msg");
|
||||
throw new NotStockException(msg);
|
||||
}
|
||||
} else {
|
||||
// 价格验证失败
|
||||
submitVo.setCode(2);
|
||||
}
|
||||
}
|
||||
return submitVo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存订单所有数据
|
||||
*/
|
||||
private void saveOrder(OrderCreateTo order) {
|
||||
OrderEntity orderEntity = order.getOrder();
|
||||
orderEntity.setModifyTime(new Date());
|
||||
this.save(orderEntity);
|
||||
|
||||
List<OrderItemEntity> orderItems = order.getOrderItems();
|
||||
orderItems = orderItems.stream().map(item -> {
|
||||
item.setOrderId(orderEntity.getId());
|
||||
item.setSpuName(item.getSpuName());
|
||||
item.setOrderSn(order.getOrder().getOrderSn());
|
||||
return item;
|
||||
}).collect(Collectors.toList());
|
||||
orderItemService.saveBatch(orderItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建订单
|
||||
*/
|
||||
private OrderCreateTo createOrder() {
|
||||
|
||||
OrderCreateTo orderCreateTo = new OrderCreateTo();
|
||||
// 1. 生成一个订单号
|
||||
String orderSn = IdWorker.getTimeId();
|
||||
OrderEntity orderEntity = buildOrderSn(orderSn);
|
||||
|
||||
// 2. 获取所有订单项
|
||||
List<OrderItemEntity> items = buildOrderItems(orderSn);
|
||||
|
||||
// 3.验价 传入订单 、订单项 计算价格、积分、成长值等相关信息
|
||||
computerPrice(orderEntity, items);
|
||||
orderCreateTo.setOrder(orderEntity);
|
||||
orderCreateTo.setOrderItems(items);
|
||||
return orderCreateTo;
|
||||
}
|
||||
|
||||
private void computerPrice(OrderEntity orderEntity, List<OrderItemEntity> items) {
|
||||
BigDecimal totalPrice = new BigDecimal("0.0");
|
||||
// 叠加每一个订单项的金额
|
||||
BigDecimal coupon = new BigDecimal("0.0");
|
||||
BigDecimal integration = new BigDecimal("0.0");
|
||||
BigDecimal promotion = new BigDecimal("0.0");
|
||||
BigDecimal gift = new BigDecimal("0.0");
|
||||
BigDecimal growth = new BigDecimal("0.0");
|
||||
for (OrderItemEntity item : items) {
|
||||
// 优惠券的金额
|
||||
coupon = coupon.add(item.getCouponAmount());
|
||||
// 积分优惠的金额
|
||||
integration = integration.add(item.getIntegrationAmount());
|
||||
// 打折的金额
|
||||
promotion = promotion.add(item.getPromotionAmount());
|
||||
BigDecimal realAmount = item.getRealAmount();
|
||||
totalPrice = totalPrice.add(realAmount);
|
||||
|
||||
// 购物获取的积分、成长值
|
||||
gift.add(new BigDecimal(item.getGiftIntegration().toString()));
|
||||
growth.add(new BigDecimal(item.getGiftGrowth().toString()));
|
||||
}
|
||||
// 1.订单价格相关 总额、应付总额
|
||||
orderEntity.setTotalAmount(totalPrice);
|
||||
orderEntity.setPayAmount(totalPrice.add(orderEntity.getFreightAmount()));
|
||||
|
||||
orderEntity.setPromotionAmount(promotion);
|
||||
orderEntity.setIntegrationAmount(integration);
|
||||
orderEntity.setCouponAmount(coupon);
|
||||
|
||||
// 设置积分、成长值
|
||||
orderEntity.setIntegration(gift.intValue());
|
||||
orderEntity.setGrowth(growth.intValue());
|
||||
|
||||
// 设置订单的删除状态
|
||||
orderEntity.setDeleteStatus(OrderStatusEnum.CREATE_NEW.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 orderSn 订单构建订单数据
|
||||
*/
|
||||
private List<OrderItemEntity> buildOrderItems(String orderSn) {
|
||||
// 这里是最后一次来确认购物项的价格 这个远程方法还会查询一次数据库
|
||||
List<OrderItemVo> cartItems = cartFeignService.getCurrentUserCartItems();
|
||||
List<OrderItemEntity> itemEntities = null;
|
||||
if (cartItems != null && cartItems.size() > 0) {
|
||||
itemEntities = cartItems.stream().map(cartItem -> {
|
||||
OrderItemEntity itemEntity = buildOrderItem(cartItem);
|
||||
itemEntity.setOrderSn(orderSn);
|
||||
return itemEntity;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
return itemEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建某一个订单项
|
||||
*/
|
||||
private OrderItemEntity buildOrderItem(OrderItemVo cartItem) {
|
||||
OrderItemEntity itemEntity = new OrderItemEntity();
|
||||
// 1.订单信息: 订单号
|
||||
|
||||
// 2.商品spu信息
|
||||
Long skuId = cartItem.getSkuId();
|
||||
R r = productFeignService.getSkuInfoBySkuId(skuId);
|
||||
SpuInfoVo spuInfo = r.getData(new TypeReference<SpuInfoVo>() {
|
||||
});
|
||||
itemEntity.setSpuId(spuInfo.getId());
|
||||
itemEntity.setSpuBrand(spuInfo.getBrandId().toString());
|
||||
itemEntity.setSpuName(spuInfo.getSpuName());
|
||||
itemEntity.setCategoryId(spuInfo.getCatalogId());
|
||||
// 3.商品的sku信息
|
||||
itemEntity.setSkuId(cartItem.getSkuId());
|
||||
itemEntity.setSkuName(cartItem.getTitle());
|
||||
itemEntity.setSkuPic(cartItem.getImage());
|
||||
itemEntity.setSkuPrice(cartItem.getPrice());
|
||||
// 把一个集合按照指定的字符串进行分割得到一个字符串
|
||||
String skuAttr = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";");
|
||||
itemEntity.setSkuAttrsVals(skuAttr);
|
||||
itemEntity.setSkuQuantity(cartItem.getCount());
|
||||
// 4.积分信息 买的数量越多积分越多 成长值越多
|
||||
itemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
|
||||
itemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
|
||||
// 5.订单项的价格信息 优惠金额
|
||||
itemEntity.setPromotionAmount(new BigDecimal("0.0"));
|
||||
itemEntity.setCouponAmount(new BigDecimal("0.0"));
|
||||
itemEntity.setIntegrationAmount(new BigDecimal("0.0"));
|
||||
// 当前订单项的实际金额
|
||||
BigDecimal orign = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity().toString()));
|
||||
// 减去各种优惠的价格
|
||||
BigDecimal subtract = orign.subtract(itemEntity.getCouponAmount()).subtract(itemEntity.getPromotionAmount()).subtract(itemEntity.getIntegrationAmount());
|
||||
itemEntity.setRealAmount(subtract);
|
||||
return itemEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个订单
|
||||
*/
|
||||
private OrderEntity buildOrderSn(String orderSn) {
|
||||
OrderEntity entity = new OrderEntity();
|
||||
entity.setOrderSn(orderSn);
|
||||
entity.setCreateTime(new Date());
|
||||
entity.setCommentTime(new Date());
|
||||
entity.setReceiveTime(new Date());
|
||||
entity.setDeliveryTime(new Date());
|
||||
MemberRsepVo rsepVo = LoginUserInterceptor.threadLocal.get();
|
||||
entity.setMemberId(rsepVo.getId());
|
||||
entity.setMemberUsername(rsepVo.getUsername());
|
||||
entity.setBillReceiverEmail(rsepVo.getEmail());
|
||||
// 2. 获取收获地址信息
|
||||
OrderSubmitVo submitVo = confirmVoThreadLocal.get();
|
||||
R fare = wmsFeignService.getFare(submitVo.getAddrId());
|
||||
FareVo resp = fare.getData(new TypeReference<FareVo>() {
|
||||
});
|
||||
entity.setFreightAmount(resp.getFare());
|
||||
entity.setReceiverCity(resp.getMemberAddressVo().getCity());
|
||||
entity.setReceiverDetailAddress(resp.getMemberAddressVo().getDetailAddress());
|
||||
entity.setDeleteStatus(OrderStatusEnum.CREATE_NEW.getCode());
|
||||
entity.setReceiverPhone(resp.getMemberAddressVo().getPhone());
|
||||
entity.setReceiverName(resp.getMemberAddressVo().getName());
|
||||
entity.setReceiverPostCode(resp.getMemberAddressVo().getPostCode());
|
||||
entity.setReceiverProvince(resp.getMemberAddressVo().getProvince());
|
||||
entity.setReceiverRegion(resp.getMemberAddressVo().getRegion());
|
||||
// 设置订单状态
|
||||
entity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
|
||||
entity.setAutoConfirmDay(7);
|
||||
return entity;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package name.lkk.kkmall.order.to;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import name.lkk.kkmall.order.entity.OrderEntity;
|
||||
import name.lkk.kkmall.order.entity.OrderItemEntity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
public class OrderCreateTo {
|
||||
|
||||
private OrderEntity order;
|
||||
|
||||
private List<OrderItemEntity> orderItems;
|
||||
|
||||
/**
|
||||
* 订单计算的应付价格
|
||||
*/
|
||||
private BigDecimal payPrice;
|
||||
|
||||
/**
|
||||
* 运费
|
||||
*/
|
||||
private BigDecimal fare;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package name.lkk.kkmall.order.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
public class PayAsyncVo {
|
||||
|
||||
private Date gmt_create;
|
||||
|
||||
private String charset;
|
||||
|
||||
private String gmt_payment;
|
||||
|
||||
private Date notify_time;
|
||||
|
||||
private String subject;
|
||||
|
||||
private String sign;
|
||||
|
||||
private String buyer_id;//支付者的id
|
||||
|
||||
private String body;//订单的信息
|
||||
|
||||
private String invoice_amount;//支付金额
|
||||
|
||||
private String version;
|
||||
|
||||
private String notify_id;//通知id
|
||||
|
||||
private String fund_bill_list;
|
||||
|
||||
private String notify_type;//通知类型; trade_status_sync
|
||||
|
||||
private String out_trade_no;//订单号
|
||||
|
||||
private String total_amount;//支付的总额
|
||||
|
||||
private String trade_status;//交易状态 TRADE_SUCCESS
|
||||
|
||||
private String trade_no;//流水号
|
||||
|
||||
private String auth_app_id;
|
||||
|
||||
private String receipt_amount;//商家收到的款
|
||||
|
||||
private String point_amount;//
|
||||
|
||||
private String app_id;//应用id
|
||||
|
||||
private String buyer_pay_amount;//最终支付的金额
|
||||
|
||||
private String sign_type;//签名类型
|
||||
|
||||
private String seller_id;//商家的id
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "gmt_create --> '" + gmt_create + '\'' +
|
||||
"\ncharset --> '" + charset + '\'' +
|
||||
"\ngmt_payment --> '" + gmt_payment + '\'' +
|
||||
"\nnotify_time --> '" + notify_time + '\'' +
|
||||
"\nsubject --> '" + subject + '\'' +
|
||||
"\nsign --> '" + sign + '\'' +
|
||||
"\nbuyer_id --> '" + buyer_id + '\'' +
|
||||
"\nbody --> '" + body + '\'' +
|
||||
"\ninvoice_amount --> '" + invoice_amount + '\'' +
|
||||
"\nversion --> '" + version + '\'' +
|
||||
"\nnotify_id --> '" + notify_id + '\'' +
|
||||
"\nfund_bill_list --> '" + fund_bill_list + '\'' +
|
||||
"\nnotify_type --> '" + notify_type + '\'' +
|
||||
"\nout_trade_no --> '" + out_trade_no + '\'' +
|
||||
"\ntotal_amount --> '" + total_amount + '\'' +
|
||||
"\ntrade_status --> '" + trade_status + '\'' +
|
||||
"\ntrade_no --> '" + trade_no + '\'' +
|
||||
"\nauth_app_id --> '" + auth_app_id + '\'' +
|
||||
"\nreceipt_amount --> '" + receipt_amount + '\'' +
|
||||
"\npoint_amount --> '" + point_amount + '\'' +
|
||||
"\napp_id --> '" + app_id + '\'' +
|
||||
"\nbuyer_pay_amount --> '" + buyer_pay_amount + '\'' +
|
||||
"\nsign_type --> '" + sign_type + '\'' +
|
||||
"\nseller_id --> '" + seller_id + '\'';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package name.lkk.kkmall.order.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PayVo {
|
||||
/**
|
||||
* 商户订单号 必填
|
||||
*/
|
||||
private String out_trade_no;
|
||||
|
||||
/**
|
||||
* 订单名称 必填
|
||||
*/
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* 付款金额 必填
|
||||
*/
|
||||
private String total_amount;
|
||||
|
||||
/**
|
||||
* 商品描述 可空
|
||||
*/
|
||||
private String body;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package name.lkk.kkmall.order.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
public class WareSkuLockVo {
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderSn;
|
||||
|
||||
/**
|
||||
* 要锁住的所有库存信息
|
||||
*/
|
||||
private List<OrderItemVo> locks;
|
||||
|
||||
}
|
|
@ -1,12 +1,17 @@
|
|||
package name.lkk.kkmall.order.web;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import name.lkk.common.exception.NotStockException;
|
||||
import name.lkk.kkmall.order.service.OrderService;
|
||||
import name.lkk.kkmall.order.vo.OrderConfirmVo;
|
||||
import name.lkk.kkmall.order.vo.OrderSubmitVo;
|
||||
import name.lkk.kkmall.order.vo.SubmitOrderResponseVo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
|
@ -31,4 +36,45 @@ public class OrderWebController {
|
|||
return "confirm";
|
||||
}
|
||||
|
||||
/**
|
||||
* 下单功能
|
||||
*/
|
||||
@PostMapping("/submitOrder")
|
||||
public String submitOrder(OrderSubmitVo submitVo, Model model, RedirectAttributes redirectAttributes) {
|
||||
|
||||
try {
|
||||
SubmitOrderResponseVo responseVo = orderService.submitOrder(submitVo);
|
||||
// 下单失败回到订单重新确认订单信息
|
||||
if (responseVo.getCode() == 0) {
|
||||
// 下单成功取支付选项
|
||||
model.addAttribute("submitOrderResp", responseVo);
|
||||
return "pay";
|
||||
} else {
|
||||
|
||||
String msg = "下单失败,";
|
||||
switch (responseVo.getCode()) {
|
||||
case 1:
|
||||
msg += "订单信息过期,请刷新在提交";
|
||||
break;
|
||||
case 2:
|
||||
msg += "订单商品价格发送变化,请确认后再次提交";
|
||||
break;
|
||||
case 3:
|
||||
msg += "商品库存不足";
|
||||
break;
|
||||
default:
|
||||
msg += "未知错误";
|
||||
}
|
||||
|
||||
redirectAttributes.addFlashAttribute("msg", msg);
|
||||
return "redirect:http://order.kkmall.com/toTrade";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof NotStockException) {
|
||||
String message = e.getMessage();
|
||||
redirectAttributes.addFlashAttribute("msg", message);
|
||||
}
|
||||
return "redirect:http://order.kkmall.com/toTrade";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package name.lkk.kkmall.ware.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import name.lkk.common.exception.BizCodeEnum;
|
||||
import name.lkk.common.exception.NotStockException;
|
||||
import name.lkk.common.to.es.SkuHasStockVo;
|
||||
import name.lkk.common.utils.PageUtils;
|
||||
import name.lkk.common.utils.R;
|
||||
import name.lkk.kkmall.ware.entity.WareSkuEntity;
|
||||
import name.lkk.kkmall.ware.service.WareSkuService;
|
||||
import name.lkk.kkmall.ware.vo.WareSkuLockVo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
@ -21,17 +25,31 @@ import java.util.Map;
|
|||
* @email linkirk@163.com
|
||||
* @date 2021-06-07 16:44:32
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("ware/waresku")
|
||||
public class WareSkuController {
|
||||
@Autowired
|
||||
private WareSkuService wareSkuService;
|
||||
|
||||
@PostMapping("/lock/order")
|
||||
public R orderLockStock(@RequestBody WareSkuLockVo vo) {
|
||||
try {
|
||||
wareSkuService.orderLockStock(vo);
|
||||
return R.ok();
|
||||
} catch (NotStockException e) {
|
||||
log.warn("\n" + e.getMessage());
|
||||
}
|
||||
return R.error(BizCodeEnum.NOT_STOCK_EXCEPTION.getCode(), BizCodeEnum.NOT_STOCK_EXCEPTION.getMsg());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询sku是否有库存
|
||||
* 返回当前id stock量
|
||||
*/
|
||||
@PostMapping("/hasStock")
|
||||
public R getSkuHasStock(@RequestBody List<Long> skuIds){
|
||||
public R getSkuHasStock(@RequestBody List<Long> skuIds) {
|
||||
List<SkuHasStockVo> vos = wareSkuService.getSkuHasStock(skuIds);
|
||||
return R.ok().setData(vos);
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@ import name.lkk.kkmall.ware.entity.WareSkuEntity;
|
|||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品库存
|
||||
*
|
||||
*
|
||||
* @author KirkLin
|
||||
* @email linkirk@163.com
|
||||
* @date 2021-06-07 16:44:32
|
||||
|
@ -15,12 +17,26 @@ import org.apache.ibatis.annotations.Param;
|
|||
@Mapper
|
||||
public interface WareSkuDao extends BaseMapper<WareSkuEntity> {
|
||||
|
||||
/**
|
||||
* 更新库存
|
||||
*
|
||||
* @param skuId
|
||||
* @param wareId
|
||||
* @param skuNum
|
||||
*/
|
||||
void addStock(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("skuNum") Integer skuNum);
|
||||
|
||||
/**
|
||||
* 查询是否有库存
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
Long getSkuStock(Long id);
|
||||
|
||||
List<Long> listWareIdHasSkuStock(@Param("skuId") Long skuId);
|
||||
|
||||
Long lockSkuStock(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("num") Integer num);
|
||||
|
||||
void unlockStock(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("num") Integer num);
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ package name.lkk.kkmall.ware.entity;
|
|||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 库存工作单
|
||||
|
@ -15,6 +16,8 @@ import lombok.Data;
|
|||
* @date 2021-06-07 16:44:32
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("wms_ware_order_task_detail")
|
||||
public class WareOrderTaskDetailEntity implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||
import name.lkk.common.to.es.SkuHasStockVo;
|
||||
import name.lkk.common.utils.PageUtils;
|
||||
import name.lkk.kkmall.ware.entity.WareSkuEntity;
|
||||
import name.lkk.kkmall.ware.vo.WareSkuLockVo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -26,9 +27,14 @@ public interface WareSkuService extends IService<WareSkuEntity> {
|
|||
|
||||
/**
|
||||
* 查询sku是否有库存
|
||||
*
|
||||
* @param skuIds
|
||||
* @return
|
||||
*/
|
||||
List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds);
|
||||
|
||||
Boolean orderLockStock(WareSkuLockVo vo);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,22 @@ package name.lkk.kkmall.ware.service.impl;
|
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import name.lkk.common.exception.NotStockException;
|
||||
import name.lkk.common.to.es.SkuHasStockVo;
|
||||
import name.lkk.common.utils.PageUtils;
|
||||
import name.lkk.common.utils.Query;
|
||||
import name.lkk.common.utils.R;
|
||||
import name.lkk.kkmall.ware.dao.WareSkuDao;
|
||||
import name.lkk.kkmall.ware.entity.WareOrderTaskDetailEntity;
|
||||
import name.lkk.kkmall.ware.entity.WareOrderTaskEntity;
|
||||
import name.lkk.kkmall.ware.entity.WareSkuEntity;
|
||||
import name.lkk.kkmall.ware.feign.ProductFeignService;
|
||||
import name.lkk.kkmall.ware.service.WareOrderTaskDetailService;
|
||||
import name.lkk.kkmall.ware.service.WareOrderTaskService;
|
||||
import name.lkk.kkmall.ware.service.WareSkuService;
|
||||
import name.lkk.kkmall.ware.vo.OrderItemVo;
|
||||
import name.lkk.kkmall.ware.vo.SkuWareHasStock;
|
||||
import name.lkk.kkmall.ware.vo.WareSkuLockVo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -28,6 +36,13 @@ public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> i
|
|||
private WareSkuDao wareSkuDao;
|
||||
@Autowired
|
||||
private ProductFeignService productFeignService;
|
||||
|
||||
@Autowired
|
||||
private WareOrderTaskService orderTaskService;
|
||||
|
||||
@Autowired
|
||||
private WareOrderTaskDetailService orderTaskDetailService;
|
||||
|
||||
/**
|
||||
* 商品库存的模糊查询
|
||||
* skuId: 1
|
||||
|
@ -101,9 +116,69 @@ public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> i
|
|||
// 查询当前sku的总库存量
|
||||
stockVo.setSkuId(id);
|
||||
// 这里库存可能为null 要避免空指针异常
|
||||
stockVo.setHasStock(baseMapper.getSkuStock(id)==null?false:true);
|
||||
stockVo.setHasStock(baseMapper.getSkuStock(id) == null ? false : true);
|
||||
return stockVo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = NotStockException.class)
|
||||
@Override
|
||||
public Boolean orderLockStock(WareSkuLockVo vo) {
|
||||
// 当定库存之前先保存订单 以便后来消息撤回
|
||||
WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
|
||||
taskEntity.setOrderSn(vo.getOrderSn());
|
||||
orderTaskService.save(taskEntity);
|
||||
// [理论上]1. 按照下单的收获地址 找到一个就近仓库, 锁定库存
|
||||
// [实际上]1. 找到每一个商品在那个一个仓库有库存
|
||||
List<OrderItemVo> locks = vo.getLocks();
|
||||
List<SkuWareHasStock> collect = locks.stream().map(item -> {
|
||||
SkuWareHasStock hasStock = new SkuWareHasStock();
|
||||
Long skuId = item.getSkuId();
|
||||
hasStock.setSkuId(skuId);
|
||||
// 查询这两个商品在哪有库存
|
||||
List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
|
||||
hasStock.setWareId(wareIds);
|
||||
hasStock.setNum(item.getCount());
|
||||
return hasStock;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
for (SkuWareHasStock hasStock : collect) {
|
||||
Boolean skuStocked = true;
|
||||
Long skuId = hasStock.getSkuId();
|
||||
List<Long> wareIds = hasStock.getWareId();
|
||||
if (wareIds == null || wareIds.size() == 0) {
|
||||
// 没有任何仓库有这个库存
|
||||
throw new NotStockException(skuId.toString());
|
||||
}
|
||||
// 如果每一个商品都锁定成功 将当前商品锁定了几件的工作单记录发送给MQ
|
||||
// 如果锁定失败 前面保存的工作单信息回滚了
|
||||
for (Long wareId : wareIds) {
|
||||
// 成功就返回 1 失败返回0
|
||||
Long count = wareSkuDao.lockSkuStock(skuId, wareId, hasStock.getNum());
|
||||
if (count == 1) {
|
||||
// TODO 告诉MQ库存锁定成功 一个订单锁定成功 消息队列就会有一个消息
|
||||
WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity(null, skuId, "", hasStock.getNum(), taskEntity.getId(), wareId, 1);
|
||||
orderTaskDetailService.save(detailEntity);
|
||||
// StockLockedTo stockLockedTo = new StockLockedTo();
|
||||
// stockLockedTo.setId(taskEntity.getId());
|
||||
// StockDetailTo detailTo = new StockDetailTo();
|
||||
// BeanUtils.copyProperties(detailEntity, detailTo);
|
||||
// // 防止回滚以后找不到数据 把详细信息页
|
||||
// stockLockedTo.setDetailTo(detailTo);
|
||||
//
|
||||
// rabbitTemplate.convertAndSend(eventExchange, routingKey ,stockLockedTo);
|
||||
skuStocked = false;
|
||||
break;
|
||||
}
|
||||
// 当前仓库锁定失败 重试下一个仓库
|
||||
}
|
||||
if (skuStocked) {
|
||||
// 当前商品在所有仓库都没锁柱
|
||||
throw new NotStockException(skuId.toString());
|
||||
}
|
||||
}
|
||||
// 3.全部锁定成功
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package name.lkk.kkmall.ware.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
public class OrderItemVo {
|
||||
|
||||
private Long skuId;
|
||||
|
||||
/**
|
||||
* 是否被选中
|
||||
*/
|
||||
private Boolean check = true;
|
||||
|
||||
private String title;
|
||||
|
||||
private String image;
|
||||
|
||||
private List<String> skuAttr;
|
||||
|
||||
/**
|
||||
* 价格
|
||||
*/
|
||||
private BigDecimal price;
|
||||
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
private Integer count;
|
||||
|
||||
private BigDecimal totalPrice;
|
||||
|
||||
/**
|
||||
* 是否有货
|
||||
*/
|
||||
// private boolean hasStock;
|
||||
|
||||
/**
|
||||
* 重量
|
||||
*/
|
||||
private BigDecimal weight;
|
||||
}
|
177
kkmall-ware/src/main/java/name/lkk/kkmall/ware/vo/OrderVo.java
Normal file
177
kkmall-ware/src/main/java/name/lkk/kkmall/ware/vo/OrderVo.java
Normal file
|
@ -0,0 +1,177 @@
|
|||
package name.lkk.kkmall.ware.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
@Data
|
||||
public class OrderVo {
|
||||
|
||||
private Long id;
|
||||
/**
|
||||
* member_id
|
||||
*/
|
||||
private Long memberId;
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderSn;
|
||||
/**
|
||||
* 使用的优惠券
|
||||
*/
|
||||
private Long couponId;
|
||||
/**
|
||||
* create_time
|
||||
*/
|
||||
private Date createTime;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String memberUsername;
|
||||
/**
|
||||
* 订单总额
|
||||
*/
|
||||
private BigDecimal totalAmount;
|
||||
/**
|
||||
* 应付总额
|
||||
*/
|
||||
private BigDecimal payAmount;
|
||||
/**
|
||||
* 运费金额
|
||||
*/
|
||||
private BigDecimal freightAmount;
|
||||
/**
|
||||
* 促销优化金额(促销价、满减、阶梯价)
|
||||
*/
|
||||
private BigDecimal promotionAmount;
|
||||
/**
|
||||
* 积分抵扣金额
|
||||
*/
|
||||
private BigDecimal integrationAmount;
|
||||
/**
|
||||
* 优惠券抵扣金额
|
||||
*/
|
||||
private BigDecimal couponAmount;
|
||||
/**
|
||||
* 后台调整订单使用的折扣金额
|
||||
*/
|
||||
private BigDecimal discountAmount;
|
||||
/**
|
||||
* 支付方式【1->支付宝;2->微信;3->银联; 4->货到付款;】
|
||||
*/
|
||||
private Integer payType;
|
||||
/**
|
||||
* 订单来源[0->PC订单;1->app订单]
|
||||
*/
|
||||
private Integer sourceType;
|
||||
/**
|
||||
* 订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 物流公司(配送方式)
|
||||
*/
|
||||
private String deliveryCompany;
|
||||
/**
|
||||
* 物流单号
|
||||
*/
|
||||
private String deliverySn;
|
||||
/**
|
||||
* 自动确认时间(天)
|
||||
*/
|
||||
private Integer autoConfirmDay;
|
||||
/**
|
||||
* 可以获得的积分
|
||||
*/
|
||||
private Integer integration;
|
||||
/**
|
||||
* 可以获得的成长值
|
||||
*/
|
||||
private Integer growth;
|
||||
/**
|
||||
* 发票类型[0->不开发票;1->电子发票;2->纸质发票]
|
||||
*/
|
||||
private Integer billType;
|
||||
/**
|
||||
* 发票抬头
|
||||
*/
|
||||
private String billHeader;
|
||||
/**
|
||||
* 发票内容
|
||||
*/
|
||||
private String billContent;
|
||||
/**
|
||||
* 收票人电话
|
||||
*/
|
||||
private String billReceiverPhone;
|
||||
/**
|
||||
* 收票人邮箱
|
||||
*/
|
||||
private String billReceiverEmail;
|
||||
/**
|
||||
* 收货人姓名
|
||||
*/
|
||||
private String receiverName;
|
||||
/**
|
||||
* 收货人电话
|
||||
*/
|
||||
private String receiverPhone;
|
||||
/**
|
||||
* 收货人邮编
|
||||
*/
|
||||
private String receiverPostCode;
|
||||
/**
|
||||
* 省份/直辖市
|
||||
*/
|
||||
private String receiverProvince;
|
||||
/**
|
||||
* 城市
|
||||
*/
|
||||
private String receiverCity;
|
||||
/**
|
||||
* 区
|
||||
*/
|
||||
private String receiverRegion;
|
||||
/**
|
||||
* 详细地址
|
||||
*/
|
||||
private String receiverDetailAddress;
|
||||
/**
|
||||
* 订单备注
|
||||
*/
|
||||
private String note;
|
||||
/**
|
||||
* 确认收货状态[0->未确认;1->已确认]
|
||||
*/
|
||||
private Integer confirmStatus;
|
||||
/**
|
||||
* 删除状态【0->未删除;1->已删除】
|
||||
*/
|
||||
private Integer deleteStatus;
|
||||
/**
|
||||
* 下单时使用的积分
|
||||
*/
|
||||
private Integer useIntegration;
|
||||
/**
|
||||
* 支付时间
|
||||
*/
|
||||
private Date paymentTime;
|
||||
/**
|
||||
* 发货时间
|
||||
*/
|
||||
private Date deliveryTime;
|
||||
/**
|
||||
* 确认收货时间
|
||||
*/
|
||||
private Date receiveTime;
|
||||
/**
|
||||
* 评价时间
|
||||
*/
|
||||
private Date commentTime;
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
private Date modifyTime;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package name.lkk.kkmall.ware.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: kirklin
|
||||
* @date: 2021/7/13 4:57 下午
|
||||
* @description:
|
||||
*/
|
||||
@Data
|
||||
public class SkuWareHasStock {
|
||||
private Long skuId;
|
||||
|
||||
private List<Long> wareId;
|
||||
|
||||
private Integer num;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package name.lkk.kkmall.ware.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
public class WareSkuLockVo {
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String orderSn;
|
||||
|
||||
/**
|
||||
* 要锁住的所有库存信息
|
||||
*/
|
||||
private List<OrderItemVo> locks;
|
||||
|
||||
}
|
|
@ -14,10 +14,33 @@
|
|||
</resultMap>
|
||||
<!-- 更新库存 -->
|
||||
<insert id="addStock">
|
||||
UPDATE `wms_ware_sku` SET stock = stock + #{skuNum} WHERE sku_id = #{skuId} AND ware_id = #{wareId}
|
||||
UPDATE `wms_ware_sku`
|
||||
SET stock = stock + #{skuNum}
|
||||
WHERE sku_id = #{skuId}
|
||||
AND ware_id = #{wareId}
|
||||
</insert>
|
||||
<select id="getSkuStock" resultType="java.lang.Long">
|
||||
SELECT SUM(stock - stock_locked) FROM `wms_ware_sku` WHERE sku_id = #{id}
|
||||
SELECT SUM(stock - stock_locked)
|
||||
FROM `wms_ware_sku`
|
||||
WHERE sku_id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="listWareIdHasSkuStock" resultType="java.lang.Long">
|
||||
SELECT ware_id
|
||||
FROM `wms_ware_sku`
|
||||
WHERE sku_id = #{skuId}
|
||||
AND stock - stock_locked > 0
|
||||
</select>
|
||||
<update id="lockSkuStock">
|
||||
UPDATE `wms_ware_sku`
|
||||
SET stock_locked = stock_locked + #{num}
|
||||
WHERE sku_id = #{skuId}
|
||||
AND ware_id = #{wareId}
|
||||
AND stock - stock_locked >= #{num}
|
||||
</update>
|
||||
<update id="unlockStock">
|
||||
UPDATE `wms_ware_sku`
|
||||
SET stock_locked = stock_locked - #{num}
|
||||
WHERE sku_id = #{skuId}
|
||||
AND ware_id = #{wareId}
|
||||
</update>
|
||||
</mapper>
|
Loading…
Reference in a new issue