谷粒商城订单服务(四十)
261-284 订单服务
代码提交到码云:https://gitee.com/dalianpai/gulimall
感觉订单服务业务逻辑还是比较复杂的,改html改的头皮发麻。
上个星期写的,没有时间简单的记录一下,让人感觉有点意思的也就Feign的请求头丢失的问题。
ThreadLocal可以查看之前的博客:https://www.cnblogs.com/dalianpai/p/12623044.html
/**
* @author WGR
* @create 2020/7/27 -- 21:52
*/
@Component
public class LoginUserInterceptor implements HandlerInterceptor {
public static ThreadLocal<MemberRespVo> loginUser = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
MemberRespVo attribute = (MemberRespVo)request.getSession().getAttribute(AuthServerConstant.LOGIN_USER);
if(attribute !=null){
loginUser.set(attribute);
return true;
}else{
request.getSession().setAttribute("msg","请先进行登录");
response.sendRedirect("http://auth.gulimall.com/login.html");
return false;
}
}
}
Feign的请求头丢失的问题:https://www.cnblogs.com/dalianpai/p/12426425.html
/**
* @author WGR
* @create 2020/7/28 -- 16:01
*/
@Configuration
public class GuliFeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(attributes !=null){
HttpServletRequest request = attributes.getRequest();
if(request !=null){
requestTemplate.header("Cookie", request.getHeader("Cookie"));
}
}
}
};
}
}
业务逻辑:
/**
* @author WGR
* @create 2020/7/27 -- 22:05
*/
@Controller
public class OrderWenController {
@Autowired
OrderService orderService;
@GetMapping("/toTrade")
public String toTrade(Model model) throws ExecutionException, InterruptedException {
OrderConfirmVo confirmVo = orderService.confirmOrder();
model.addAttribute("orderConfirmData",confirmVo);
return "confirm";
}
@PostMapping("/submitOrder")
public String submitOrder(OrderSubmitVo vo, Model model, RedirectAttributes redirectAttributes){
System.out.println("订单提交的数据。。。"+vo);
SubmitOrderResponseVo responseVo = orderService.submitOrder(vo);
if(responseVo.getCode() ==0){
System.out.println(responseVo);
model.addAttribute("submitOrderResp",responseVo);
return "pay";
}else{
String msg="下单失败:";
switch (responseVo.getCode()){
case 1: msg += "订单信息过期,请刷新再次提交";break;
case 2: msg += "订单商品价格发生变化,请确认后再次提交";break;
case 3: msg += "库存锁定失败,商品库存不足";break;
}
redirectAttributes.addFlashAttribute("msg",msg);
return "redirect:http://order.gulimall.com/toTrade";
}
}
}
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();
@Autowired
MemberFeignService memberFeignService;
@Autowired
OrderItemService orderItemService;
@Autowired(required = false)
OrderDao orderDao;
@Autowired(required = false)
OrderItemDao orderItemDao;
@Autowired
CartFeignService cartFeignService;
@Autowired
WmsFeignService wmsFeignService;
@Autowired
StringRedisTemplate redisTemplate;
@Autowired
ThreadPoolExecutor executor;
@Autowired
ProductFeignService productFeignService;
@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);
}
@Override
public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//1.远程查询所有的收货地址列表
CompletableFuture<Void> getCompletableFuture = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(requestAttributes);
List<MemberReceiveAddressEntity> address = memberFeignService.getAddress(memberRespVo.getId());
System.out.println(address);
orderConfirmVo.setAddress(address);
}, executor);
//2.远程查询购物车所有选中的购物项
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(requestAttributes);
List<CartItem> items = cartFeignService.getCurrentUserCartItems();
orderConfirmVo.setItems(items);
}, executor).thenRunAsync( () ->{
List<CartItem> items = orderConfirmVo.getItems();
List<Long> collect = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
R hasStock = wmsFeignService.getSkusHasStock(collect);
List<SkuHasStockVo> data = hasStock.getData(new TypeReference<List<SkuHasStockVo>>() {
});
if(data !=null){
Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
orderConfirmVo.setStocks(map);
}
},executor);
//3.查询用户积分
Integer integration = memberRespVo.getIntegration();
orderConfirmVo.setIntegration(integration);
//5 防重令牌
String token = UUID.randomUUID().toString().replace("-", "");
System.out.println(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId());
redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId(),token,30, TimeUnit.MINUTES);
orderConfirmVo.setOrderToken(token);
CompletableFuture.allOf(completableFuture,getCompletableFuture).get();
return orderConfirmVo;
}
@GlobalTransactional
@Transactional
@Override
public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
confirmVoThreadLocal.set(vo);
SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
responseVo.setCode(0);
MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
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 = redisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX+memberRespVo.getId() ),orderToken);
if(result ==0L){
responseVo.setCode(1);
return responseVo;
}else{
//令牌验证成功
//下单: 去创建订单,验证令牌,验价格,锁库存
OrderCreateTo order = createOrder();
//2. 验价
BigDecimal payAmount = order.getOrder().getPayAmount();
BigDecimal payPrice = vo.getPayPrice();
if(Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){
//3 保存订单
saveOrder(order);
//4 库存锁定
WareSkuLockVo lockVo = new WareSkuLockVo();
lockVo.setOrderSn(order.getOrder().getOrderSn());
List<CartItem> locks = order.getOrderItems().stream().map(item -> {
CartItem itemVo = new CartItem();
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){
//锁定成功
responseVo.setOrder(order.getOrder());
// int i = 1/0;
return responseVo;
}else{
//锁定失败
responseVo.setCode(3);
return responseVo;
}
}else{
responseVo.setCode(2);
return responseVo;
}
}
}
@Transactional
private void saveOrder(OrderCreateTo order) {
OrderEntity orderEntity = order.getOrder();
orderEntity.setModifyTime(new Date());
this.save(orderEntity);
List<OrderItemEntity> orderItems = order.getOrderItems();
orderItems.forEach(l ->{
orderItemService.save(l);
});
// orderItemService.saveBatch(orderItems);
}
private OrderCreateTo createOrder(){
OrderCreateTo ceateTo = new OrderCreateTo();
//生成单号
String orderSn = IdWorker.getTimeId();
//创建订单号
OrderEntity entity = buildOrder(orderSn);
//2 获取到所有的订单项
List<OrderItemEntity> itemEntities = buildOrderItems();
//3.计算价格相关
computePrice(entity,itemEntities);
ceateTo.setOrder(entity);
ceateTo.setOrderItems(itemEntities);
return ceateTo;
}
private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> itemEntities) {
BigDecimal total = 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 entity:itemEntities){
coupon = coupon.add(entity.getCouponAmount());
integration = integration.add(entity.getIntegrationAmount());
promotion = promotion.add(entity.getPromotionAmount());
total = total.add(entity.getRealAmount());
gift = gift.add(new BigDecimal(entity.getGiftIntegration().toString()));
growth = growth.add(new BigDecimal(entity.getGiftGrowth().toString()));
}
orderEntity.setTotalAmount(total);
orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
orderEntity.setIntegrationAmount(integration);
orderEntity.setCouponAmount(coupon);
//设置积分等信息
orderEntity.setIntegration(gift.intValue());
orderEntity.setGrowth(growth.intValue());
orderEntity.setDeleteStatus(0);
}
private OrderEntity buildOrder(String orderSn) {
MemberRespVo respVo = LoginUserInterceptor.loginUser.get();
OrderEntity entity = new OrderEntity();
entity.setOrderSn(orderSn);
entity.setMemberId(respVo.getId());
OrderSubmitVo submitVo = confirmVoThreadLocal.get();
//获取地址的收货信息
R fare = wmsFeignService.getFare(submitVo.getAddrId());
FareVo fareResp = fare.getData(new TypeReference<FareVo>() {
});
entity.setFreightAmount(fareResp.getFare());
entity.setReceiverCity(fareResp.getAddress().getCity());
entity.setReceiverDetailAddress(fareResp.getAddress().getDetailAddress());
entity.setReceiverName(fareResp.getAddress().getName());
entity.setReceiverPhone(fareResp.getAddress().getPhone());
entity.setReceiverPostCode(fareResp.getAddress().getPostCode());
entity.setReceiverProvince(fareResp.getAddress().getProvince());
entity.setReceiverRegion(fareResp.getAddress().getRegion());
//设置订单的相关状态信息
entity.setStatus(1);
entity.setAutoConfirmDay(7);
return entity;
}
/**
* 构建所有订单项数据
*/
private List<OrderItemEntity> buildOrderItems(){
List<CartItem> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
if(currentUserCartItems !=null && currentUserCartItems.size()>0){
List<OrderItemEntity> itemEntities = currentUserCartItems.stream().map(cartItem ->{
OrderItemEntity itemEntity =buildOrderItem(cartItem);
return itemEntity;
}).collect(Collectors.toList());
return itemEntities;
}
return null ;
}
private OrderItemEntity buildOrderItem(CartItem cartItem) {
OrderItemEntity itemEntity = new OrderItemEntity();
Long skuId = cartItem.getSkuId();
R r = productFeignService.getSpuInfoBySkuId(skuId);
SpuInfoVo data = r.getData(new TypeReference<SpuInfoVo>() {
});
itemEntity.setSpuId(data.getId());
itemEntity.setSpuBrand(data.getBrandId().toString());
itemEntity.setSpuName(data.getSpuName());
itemEntity.setCategoryId(data.getCatalogId());
//3.商品的sku信息
itemEntity.setSkuId(cartItem.getSkuId());
itemEntity.setSkuName(cartItem.getTitle());
itemEntity.setSkuPic(cartItem.getImages());
itemEntity.setSkuPrice(cartItem.getPrice());
String skuAttr = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";");
itemEntity.setSkuAttrsVals(skuAttr);
itemEntity.setSkuQuantity(cartItem.getCount());
//5.积分信息
itemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount().toString())).intValue());
itemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount().toString())).intValue());
//6.订单项的价格信息
itemEntity.setPromotionAmount(new BigDecimal("0"));
itemEntity.setCouponAmount(new BigDecimal("0"));
itemEntity.setIntegrationAmount(new BigDecimal("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;
}
}
锁库存的服务:
@Override
public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {
List<SkuHasStockVo> hasStockVoList = skuIds.stream().map(id -> {
SkuHasStockVo vo = new SkuHasStockVo();
Long count = baseMapper.getSkuStock(id);
vo.setSkuId(id);
vo.setHasStock(count ==null ?false:count > 0);
return vo;
}).collect(Collectors.toList());
return hasStockVoList;
}
@Transactional
@Override
public Boolean orderLockStock(WareSkuLockVo vo) {
//1. 找到每个商品在哪个仓库都有库存
List<CartItem> locks = vo.getLocks();
List<SkuWareHasStock> collect = locks.stream().map(item -> {
SkuWareHasStock stock = new SkuWareHasStock();
Long skuId = item.getSkuId();
stock.setSkuId(skuId);
stock.setNum(item.getCount());
//查询这个商品在哪里有库存
List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
stock.setWareId(wareIds);
return stock;
}).collect(Collectors.toList());
//2. 锁定库存
for(SkuWareHasStock hasStock:collect){
Boolean skuStocked = false;
Long skuId = hasStock.getSkuId();
List<Long> wareIds = hasStock.getWareId();
if(wareIds == null || wareIds.size() ==0){
throw new RRException(skuId+"不存在");
}
for (Long wareId : wareIds){
//成功就是1,失败就是0
Long count = wareSkuDao.lockSkuStock(skuId, wareId, hasStock.getNum());
if(count == 1){
skuStocked = true;
break;
}else{
//当前仓库锁失败,重试下一个仓库
}
}
if(skuStocked == false){
throw new RRException(skuId+"不存在");
}
}
return true;
}
@Data
class SkuWareHasStock{
private Long skuId;
private List<Long> wareId;
private Integer num;
}
注意演示的时候把feign的超时时间都改大点,不然老是报错,就很烦
feign:
client:
config:
gulimall-ware:
readTimeout: 12000
connectTimeout: 12000
loggerLevel: FULL
gulimall-member:
readTimeout: 12000
connectTimeout: 12000
loggerLevel: FULL
gulimall-product:
readTimeout: 12000
connectTimeout: 12000
loggerLevel: FULL
gulimall-cart:
readTimeout: 12000
connectTimeout: 12000
loggerLevel: FULL
演示如下: