商城业务 --> 订单提交基本逻辑完成

This commit is contained in:
Kirk Lin 2021-07-13 17:10:03 +08:00
parent 412781ef91
commit 04d98a1cb0
18 changed files with 895 additions and 19 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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 + '\'';
}
}

View file

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

View file

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

View file

@ -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";
}
}
}

View file

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

View file

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

View file

@ -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;

View file

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

View file

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

View file

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

View 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;
}

View file

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

View file

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

View file

@ -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>