package com.alatus.mall.ware.service.impl; import com.alatus.common.to.mq.StockDetailTo; import com.alatus.common.to.mq.StockLockedTo; import com.alatus.common.utils.R; import com.alatus.common.exception.NoStockException; import com.alatus.mall.ware.config.RabbitMQConfigProperties; import com.alatus.mall.ware.entity.WareOrderTaskDetailEntity; import com.alatus.mall.ware.entity.WareOrderTaskEntity; import com.alatus.mall.ware.feign.ProductFeignService; import com.alatus.common.vo.SkuHasStockVo; import com.alatus.mall.ware.service.WareOrderTaskDetailService; import com.alatus.mall.ware.service.WareOrderTaskService; import com.alatus.mall.ware.vo.WareSkuLockVo; import lombok.Data; import org.apache.commons.lang.StringUtils; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; 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.alatus.common.utils.PageUtils; import com.alatus.common.utils.Query; import com.alatus.mall.ware.dao.WareSkuDao; import com.alatus.mall.ware.entity.WareSkuEntity; import com.alatus.mall.ware.service.WareSkuService; import org.springframework.transaction.annotation.Transactional; @Service("wareSkuService") @RabbitListener(queues = "order.release.order.queue") public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService { @Autowired private WareSkuDao wareSkuDao; @Autowired private ProductFeignService productFeignService; @Autowired private RabbitTemplate rabbitTemplate; @Autowired private WareOrderTaskService wareOrderTaskService; @Autowired private WareOrderTaskDetailService wareOrderTaskDetailService; @Autowired private RabbitMQConfigProperties rabbitMQConfigProperties; @RabbitHandler public void handleStockLockedRelease(StockLockedTo stockLockedTo, Message message){ StockDetailTo stockDetailTo = stockLockedTo.getStockDetailTo(); WareOrderTaskDetailEntity wareOrderTaskDetailEntity = wareOrderTaskDetailService.getById(stockDetailTo.getId()); // 说明订单详情还在,也就是库存锁定肯定是成功的 if(wareOrderTaskDetailEntity!=null){ // 需要解锁订单信息,还需要检查订单状态,没有这个订单,必须解锁,有订单,但是状态为已取消,必须解锁 Long orderTaskId = stockLockedTo.getId(); // 获取库存订单任务,在里面获取订单的SN编码 WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getById(orderTaskId); String orderSn = wareOrderTaskEntity.getOrderSn(); } // 否则说明锁库存业务整体都回滚了,压根就没有必要解锁 } @Override public PageUtils queryPage(Map<String, Object> params) { QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>(); String skuId = (String) params.get("skuId"); if(!StringUtils.isEmpty(skuId)){ queryWrapper.eq("sku_id",skuId); } String wareId = (String) params.get("wareId"); if(!StringUtils.isEmpty(skuId)){ queryWrapper.eq("ware_id",wareId); } IPage<WareSkuEntity> page = this.page( new Query<WareSkuEntity>().getPage(params), queryWrapper ); return new PageUtils(page); } @Override public void addStock(Long skuId, Long wareId, Integer skuNum) { // 判断是否存在这个库存记录,没有执行新增操作 List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId)); if(wareSkuEntities == null || wareSkuEntities.size() == 0){ WareSkuEntity wareSkuEntity = new WareSkuEntity(); wareSkuEntity.setSkuId(skuId); wareSkuEntity.setWareId(wareId); wareSkuEntity.setStock(skuNum); wareSkuEntity.setStockLocked(0); // TODO 远程查询SKU的名字,如果失败,整个事务无需回滚 // 自己catch掉这个异常 try{ R info = productFeignService.info(skuId); if(info.getCode() == 0){ Map<String,Object> data = (Map<String, Object>) info.get("skuInfo"); wareSkuEntity.setSkuName((String) data.get("skuName")); } }catch (Exception e){ e.printStackTrace(); } wareSkuDao.insert(wareSkuEntity); }else{ wareSkuDao.addStock(skuId,wareId,skuNum); } } @Override public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) { List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> { SkuHasStockVo skuHasStockVo = new SkuHasStockVo(); skuHasStockVo.setSkuId(skuId); Long count = baseMapper.getSkuStock(skuId); skuHasStockVo.setHasStock(count == null ? false : count > 0); return skuHasStockVo; }).collect(Collectors.toList()); return collect; } // 为某个订单锁定库存 @Transactional(rollbackFor = NoStockException.class) // 默认只要是运行时异常都会回滚 @Override public void orderLockStock(WareSkuLockVo wareSkuLockVo) { // 保存库存工作单 WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity(); wareOrderTaskEntity.setOrderSn(wareSkuLockVo.getOrderSn()); wareOrderTaskService.save(wareOrderTaskEntity); // TODO 按照下单的收货地址,找到一个就近仓库,锁定库存 // 找到每个商品都在哪个仓库有库存 List<SkuWareHasStock> skuWareHasStockList = wareSkuLockVo.getLocks().stream().map(orderItemVo -> { SkuWareHasStock skuWareHasStock = new SkuWareHasStock(); Long skuId = orderItemVo.getSkuId(); skuWareHasStock.setSkuId(skuId); skuWareHasStock.setNum(orderItemVo.getCount()); // 查询哪个仓库有这个商品的库存 List<Long> wareIds = baseMapper.wareIdHasStock(skuId); skuWareHasStock.setWareIds(wareIds); return skuWareHasStock; }).collect(Collectors.toList()); // 锁定库存 for (SkuWareHasStock skuWareHasStock : skuWareHasStockList) { // 库存锁定 boolean skuStocked = false; Long skuId = skuWareHasStock.getSkuId(); List<Long> wareIds = skuWareHasStock.getWareIds(); if(wareIds.isEmpty()){ throw new NoStockException(skuWareHasStock.getSkuId()); } // 如果每一个商品都锁定成功,件当前商品锁定的工作单发给MQ // 锁定失败,前面保存的工作单信息就回滚了,发送出去的ID是不存在的 for (Long wareId : wareIds) { // 成功就返回1,否则返回0 Long count = baseMapper.lockSkuStock(skuId,wareId,skuWareHasStock.getNum()); if(count == 1){ // 当前的SKU锁定成功 skuStocked = true; // TODO SKU的商品名称 WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(null,skuId,"", skuWareHasStock.getNum(), wareOrderTaskEntity.getId(), wareId, 1); wareOrderTaskDetailService.save(wareOrderTaskDetailEntity); StockLockedTo stockLockedTo = new StockLockedTo(); // 记录工作单ID stockLockedTo.setId(wareOrderTaskEntity.getId()); // 保存锁了库存的Detail信息 StockDetailTo stockDetailTo = new StockDetailTo(); // 把完整的锁定库存工作信息保存下来发到消息队列 BeanUtils.copyProperties(wareOrderTaskDetailEntity,stockDetailTo); stockLockedTo.setStockDetailTo(stockDetailTo); rabbitTemplate.convertAndSend(rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getLockedKey(),stockLockedTo); break; } // 没有锁定成功的话,循环会继续,说明当前仓库没锁到,去其他仓库找 } if(!skuStocked){ // 所有仓库都没有这个库存,直接抛出异常,其他商品也不用继续了 throw new NoStockException(skuId); } } // 能走到这里,说明肯定是所有商品都锁定到库存了 } @Data class SkuWareHasStock{ private Long skuId; private Integer num; private List<Long> wareIds; } }
package com.alatus.mall.ware.service.impl; import com.alatus.common.to.mq.StockDetailTo; import com.alatus.common.to.mq.StockLockedTo; import com.alatus.common.utils.R; import com.alatus.common.exception.NoStockException; import com.alatus.mall.ware.config.RabbitMQConfigProperties; import com.alatus.mall.ware.entity.WareOrderTaskDetailEntity; import com.alatus.mall.ware.entity.WareOrderTaskEntity; import com.alatus.mall.ware.feign.ProductFeignService; import com.alatus.common.vo.SkuHasStockVo; import com.alatus.mall.ware.service.WareOrderTaskDetailService; import com.alatus.mall.ware.service.WareOrderTaskService; import com.alatus.mall.ware.vo.WareSkuLockVo; import lombok.Data; import org.apache.commons.lang.StringUtils; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; 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.alatus.common.utils.PageUtils; import com.alatus.common.utils.Query; import com.alatus.mall.ware.dao.WareSkuDao; import com.alatus.mall.ware.entity.WareSkuEntity; import com.alatus.mall.ware.service.WareSkuService; import org.springframework.transaction.annotation.Transactional; @Service("wareSkuService") @RabbitListener(queues = "order.release.order.queue") public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService { @Autowired private WareSkuDao wareSkuDao; @Autowired private ProductFeignService productFeignService; @Autowired private RabbitTemplate rabbitTemplate; @Autowired private WareOrderTaskService wareOrderTaskService; @Autowired private WareOrderTaskDetailService wareOrderTaskDetailService; @Autowired private RabbitMQConfigProperties rabbitMQConfigProperties; @RabbitHandler public void handleStockLockedRelease(StockLockedTo stockLockedTo, Message message){ StockDetailTo stockDetailTo = stockLockedTo.getStockDetailTo(); WareOrderTaskDetailEntity wareOrderTaskDetailEntity = wareOrderTaskDetailService.getById(stockDetailTo.getId()); // 说明订单详情还在,也就是库存锁定肯定是成功的 if(wareOrderTaskDetailEntity!=null){ // 需要解锁订单信息,还需要检查订单状态,没有这个订单,必须解锁,有订单,但是状态为已取消,必须解锁 Long orderTaskId = stockLockedTo.getId(); // 获取库存订单任务,在里面获取订单的SN编码 WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getById(orderTaskId); String orderSn = wareOrderTaskEntity.getOrderSn(); } // 否则说明锁库存业务整体都回滚了,压根就没有必要解锁 } @Override public PageUtils queryPage(Map<String, Object> params) { QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>(); String skuId = (String) params.get("skuId"); if(!StringUtils.isEmpty(skuId)){ queryWrapper.eq("sku_id",skuId); } String wareId = (String) params.get("wareId"); if(!StringUtils.isEmpty(skuId)){ queryWrapper.eq("ware_id",wareId); } IPage<WareSkuEntity> page = this.page( new Query<WareSkuEntity>().getPage(params), queryWrapper ); return new PageUtils(page); } @Override public void addStock(Long skuId, Long wareId, Integer skuNum) { // 判断是否存在这个库存记录,没有执行新增操作 List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId)); if(wareSkuEntities == null || wareSkuEntities.size() == 0){ WareSkuEntity wareSkuEntity = new WareSkuEntity(); wareSkuEntity.setSkuId(skuId); wareSkuEntity.setWareId(wareId); wareSkuEntity.setStock(skuNum); wareSkuEntity.setStockLocked(0); // TODO 远程查询SKU的名字,如果失败,整个事务无需回滚 // 自己catch掉这个异常 try{ R info = productFeignService.info(skuId); if(info.getCode() == 0){ Map<String,Object> data = (Map<String, Object>) info.get("skuInfo"); wareSkuEntity.setSkuName((String) data.get("skuName")); } }catch (Exception e){ e.printStackTrace(); } wareSkuDao.insert(wareSkuEntity); }else{ wareSkuDao.addStock(skuId,wareId,skuNum); } } @Override public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) { List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> { SkuHasStockVo skuHasStockVo = new SkuHasStockVo(); skuHasStockVo.setSkuId(skuId); Long count = baseMapper.getSkuStock(skuId); skuHasStockVo.setHasStock(count == null ? false : count > 0); return skuHasStockVo; }).collect(Collectors.toList()); return collect; } // 为某个订单锁定库存 @Transactional(rollbackFor = NoStockException.class) // 默认只要是运行时异常都会回滚 @Override public void orderLockStock(WareSkuLockVo wareSkuLockVo) { // 保存库存工作单 WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity(); wareOrderTaskEntity.setOrderSn(wareSkuLockVo.getOrderSn()); wareOrderTaskService.save(wareOrderTaskEntity); // TODO 按照下单的收货地址,找到一个就近仓库,锁定库存 // 找到每个商品都在哪个仓库有库存 List<SkuWareHasStock> skuWareHasStockList = wareSkuLockVo.getLocks().stream().map(orderItemVo -> { SkuWareHasStock skuWareHasStock = new SkuWareHasStock(); Long skuId = orderItemVo.getSkuId(); skuWareHasStock.setSkuId(skuId); skuWareHasStock.setNum(orderItemVo.getCount()); // 查询哪个仓库有这个商品的库存 List<Long> wareIds = baseMapper.wareIdHasStock(skuId); skuWareHasStock.setWareIds(wareIds); return skuWareHasStock; }).collect(Collectors.toList()); // 锁定库存 for (SkuWareHasStock skuWareHasStock : skuWareHasStockList) { // 库存锁定 boolean skuStocked = false; Long skuId = skuWareHasStock.getSkuId(); List<Long> wareIds = skuWareHasStock.getWareIds(); if(wareIds.isEmpty()){ throw new NoStockException(skuWareHasStock.getSkuId()); } // 如果每一个商品都锁定成功,件当前商品锁定的工作单发给MQ // 锁定失败,前面保存的工作单信息就回滚了,发送出去的ID是不存在的 for (Long wareId : wareIds) { // 成功就返回1,否则返回0 Long count = baseMapper.lockSkuStock(skuId,wareId,skuWareHasStock.getNum()); if(count == 1){ // 当前的SKU锁定成功 skuStocked = true; // TODO SKU的商品名称 WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(null,skuId,"", skuWareHasStock.getNum(), wareOrderTaskEntity.getId(), wareId, 1); wareOrderTaskDetailService.save(wareOrderTaskDetailEntity); StockLockedTo stockLockedTo = new StockLockedTo(); // 记录工作单ID stockLockedTo.setId(wareOrderTaskEntity.getId()); // 保存锁了库存的Detail信息 StockDetailTo stockDetailTo = new StockDetailTo(); // 把完整的锁定库存工作信息保存下来发到消息队列 BeanUtils.copyProperties(wareOrderTaskDetailEntity,stockDetailTo); stockLockedTo.setStockDetailTo(stockDetailTo); rabbitTemplate.convertAndSend(rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getLockedKey(),stockLockedTo); break; } // 没有锁定成功的话,循环会继续,说明当前仓库没锁到,去其他仓库找 } if(!skuStocked){ // 所有仓库都没有这个库存,直接抛出异常,其他商品也不用继续了 throw new NoStockException(skuId); } } // 能走到这里,说明肯定是所有商品都锁定到库存了 } @Data class SkuWareHasStock{ private Long skuId; private Integer num; private List<Long> wareIds; } }
package com.alatus.mall.order.app; import java.util.Arrays; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import com.alatus.mall.order.entity.OrderEntity; import com.alatus.mall.order.service.OrderService; import com.alatus.common.utils.PageUtils; import com.alatus.common.utils.R; /** * 订单 * * @author alatus * @date 2024-03-12 13:50:51 */ @RestController @RequestMapping("/order/order") public class OrderController { @Autowired private OrderService orderService; /** * 获取订单状态 */ @GetMapping("/status/{orderSn}") public R getOrderStatus(@PathVariable("orderSn")String orderSn){ OrderEntity order = orderService.getOrderByOrderSn(orderSn); return R.ok().put("data",order); } /** * 列表 */ @RequestMapping("/list") public R list(@RequestParam Map<String, Object> params){ PageUtils page = orderService.queryPage(params); return R.ok().put("page", page); } /** * 信息 */ @RequestMapping("/info/{id}") public R info(@PathVariable("id") Long id){ OrderEntity order = orderService.getById(id); return R.ok().put("order", order); } /** * 保存 */ @RequestMapping("/save") public R save(@RequestBody OrderEntity order){ orderService.save(order); return R.ok(); } /** * 修改 */ @RequestMapping("/update") public R update(@RequestBody OrderEntity order){ orderService.updateById(order); return R.ok(); } /** * 删除 */ @RequestMapping("/delete") public R delete(@RequestBody Long[] ids){ orderService.removeByIds(Arrays.asList(ids)); return R.ok(); } }
package com.alatus.mall.order.app;
import java.util.Arrays;
import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import com.alatus.mall.order.entity.OrderEntity;
import com.alatus.mall.order.service.OrderService;
import com.alatus.common.utils.PageUtils;
import com.alatus.common.utils.R;/**
* 订单
*
* @author alatus
* @date 2024-03-12 13:50:51
*/
@RestController
@RequestMapping("/order/order")
public class OrderController {
@Autowired
private OrderService orderService;/**
* 获取订单状态
*/
@GetMapping("/status/{orderSn}")
public R getOrderStatus(@PathVariable("orderSn")String orderSn){
OrderEntity order = orderService.getOrderByOrderSn(orderSn);
return R.ok().put("data",order);
}/**
* 列表
*/
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){
PageUtils page = orderService.queryPage(params);return R.ok().put("page", page);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
public R info(@PathVariable("id") Long id){
OrderEntity order = orderService.getById(id);return R.ok().put("order", order);
}/**
* 保存
*/
@RequestMapping("/save")
public R save(@RequestBody OrderEntity order){
orderService.save(order);return R.ok();
}/**
* 修改
*/
@RequestMapping("/update")
public R update(@RequestBody OrderEntity order){
orderService.updateById(order);return R.ok();
}/**
* 删除
*/
@RequestMapping("/delete")
public R delete(@RequestBody Long[] ids){
orderService.removeByIds(Arrays.asList(ids));return R.ok();
}}
package com.alatus.mall.ware.feign; import com.alatus.common.utils.R; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient("Mall-order") public interface OrderFeignService { @GetMapping("/order/order/status/{orderSn}") R getOrderStatus(@PathVariable("orderSn")String orderSn); }
package com.alatus.mall.ware.feign; import com.alatus.common.utils.R; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient("Mall-order") public interface OrderFeignService { @GetMapping("/order/order/status/{orderSn}") R getOrderStatus(@PathVariable("orderSn")String orderSn); }
package com.alatus.mall.order.service; import com.alatus.mall.order.vo.OrderConfirmVo; import com.alatus.mall.order.vo.OrderSubmitVo; import com.alatus.mall.order.vo.SubmitOrderResponseVo; import com.baomidou.mybatisplus.extension.service.IService; import com.alatus.common.utils.PageUtils; import com.alatus.mall.order.entity.OrderEntity; import java.math.BigDecimal; import java.util.Map; import java.util.concurrent.ExecutionException; /** * 订单 * * @author alatus * @date 2024-03-12 13:50:51 */ public interface OrderService extends IService<OrderEntity> { PageUtils queryPage(Map<String, Object> params); OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException; BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException; SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo); OrderEntity getOrderByOrderSn(String orderSn); }
package com.alatus.mall.order.service; import com.alatus.mall.order.vo.OrderConfirmVo; import com.alatus.mall.order.vo.OrderSubmitVo; import com.alatus.mall.order.vo.SubmitOrderResponseVo; import com.baomidou.mybatisplus.extension.service.IService; import com.alatus.common.utils.PageUtils; import com.alatus.mall.order.entity.OrderEntity; import java.math.BigDecimal; import java.util.Map; import java.util.concurrent.ExecutionException; /** * 订单 * * @author alatus * @date 2024-03-12 13:50:51 */ public interface OrderService extends IService<OrderEntity> { PageUtils queryPage(Map<String, Object> params); OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException; BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException; SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo); OrderEntity getOrderByOrderSn(String orderSn); }
spring.application.name=Mall-order spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 spring.rabbitmq.host=192.168.56.10 spring.rabbitmq.port=5672 spring.rabbitmq.virtual-host=/ spring.rabbitmq.template.mandatory=true spring.rabbitmq.publisher-confirm-type=CORRELATED spring.rabbitmq.listener.simple.acknowledge-mode=manual spring.cloud.nacos.config.namespace=becc5563-6c9b-47ff-a6b1-580982722371 spring.session.store-type=redis alatusmall.thread.keep-alive-time=10 alatusmall.thread.blocking-deque=100000 alatusmall.thread.core-size=20 alatusmall.thread.max-size=200 alatusmall.rabbitmq.order.exchange=order-event-exchange alatusmall.rabbitmq.order.releaseKey=order.release.order alatusmall.rabbitmq.order.createKey=order.create.order alatusmall.rabbitmq.order.messageTTL=60000 alatusmall.rabbitmq.order.delayQueue=order.delay.queue alatusmall.rabbitmq.order.releaseQueue=order.release.order.queue
spring.application.name=Mall-order spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 spring.rabbitmq.host=192.168.56.10 spring.rabbitmq.port=5672 spring.rabbitmq.virtual-host=/ spring.rabbitmq.template.mandatory=true spring.rabbitmq.publisher-confirm-type=CORRELATED spring.rabbitmq.listener.simple.acknowledge-mode=manual spring.cloud.nacos.config.namespace=becc5563-6c9b-47ff-a6b1-580982722371 spring.session.store-type=redis alatusmall.thread.keep-alive-time=10 alatusmall.thread.blocking-deque=100000 alatusmall.thread.core-size=20 alatusmall.thread.max-size=200 alatusmall.rabbitmq.order.exchange=order-event-exchange alatusmall.rabbitmq.order.releaseKey=order.release.order alatusmall.rabbitmq.order.createKey=order.create.order alatusmall.rabbitmq.order.messageTTL=60000 alatusmall.rabbitmq.order.delayQueue=order.delay.queue alatusmall.rabbitmq.order.releaseQueue=order.release.order.queue
package com.alatus.mall.order.service.impl; import com.alatus.common.exception.NoStockException; import com.alatus.common.to.MemberAddressTo; import com.alatus.common.utils.R; import com.alatus.common.vo.MemberRespVo; import com.alatus.common.vo.SkuHasStockVo; import com.alatus.mall.order.entity.OrderItemEntity; import com.alatus.mall.order.enums.OrderStatusEnum; import com.alatus.mall.order.feign.ProductFeignService; import com.alatus.mall.order.service.OrderItemService; import com.alatus.mall.order.to.OrderCreateTo; import com.alatus.mall.order.constant.OrderConstant; import com.alatus.mall.order.feign.CartFeignService; import com.alatus.mall.order.feign.MemberFeignService; import com.alatus.mall.order.feign.WareFeignService; import com.alatus.mall.order.interceptor.LoginUserInterceptor; import com.alatus.mall.order.vo.*; import com.alibaba.fastjson.TypeReference; import com.baomidou.mybatisplus.core.toolkit.IdWorker; 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 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.alatus.common.utils.PageUtils; import com.alatus.common.utils.Query; import com.alatus.mall.order.dao.OrderDao; import com.alatus.mall.order.entity.OrderEntity; import com.alatus.mall.order.service.OrderService; 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; @Service("orderService") public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService { @Autowired private MemberFeignService memberFeignService; @Autowired private CartFeignService cartFeignService; @Autowired private OrderItemService orderItemService; @Autowired private ThreadPoolExecutor threadPoolExecutor; @Autowired private WareFeignService wareFeignService; @Autowired private StringRedisTemplate redisTemplate; @Autowired private ProductFeignService productFeignService; private ThreadLocal<OrderSubmitVo> submitVoThreadLocal = new ThreadLocal<>(); @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 confirmVo = new OrderConfirmVo(); // 获取原请求信息,避免异步导致丢失 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); // 远程查询购物车所有选中的购物项 CompletableFuture<Void> itemVoList = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); confirmVo.setItemVoList(cartFeignService.getCurrentCartItem()); }, threadPoolExecutor).thenRunAsync(()->{ // 获取原请求信息,避免异步导致丢失 RequestContextHolder.setRequestAttributes(requestAttributes); // 查询库存 List<OrderItemVo> itemList = confirmVo.getItemVoList(); List<Long> ids = itemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList()); R skuHasStock = wareFeignService.getSkuHasStock(ids); if(skuHasStock.getCode()==0){ List<SkuHasStockVo> data = skuHasStock.getData(new TypeReference<List<SkuHasStockVo>>(){}); if(data!=null&& !data.isEmpty()){ Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock)); confirmVo.setStocks(collect); } } },threadPoolExecutor); // 远程查询所有的用户地址列表 CompletableFuture<Void> addressTask = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); confirmVo.setAddress(memberFeignService.getAddress(memberRespVo.getId())); }, threadPoolExecutor).thenRunAsync(()->{ // 获取运费信息 RequestContextHolder.setRequestAttributes(requestAttributes); List<MemberAddressTo> address = confirmVo.getAddress(); for (MemberAddressTo memberAddressTo : address) { if(memberAddressTo.getDefaultStatus()==1){ R fare = wareFeignService.getFare(memberAddressTo.getId()); if (fare.getCode()==0) { confirmVo.setFare(fare.getData(new TypeReference<BigDecimal>() {})); } } } },threadPoolExecutor); // 用户积分 CompletableFuture<Void> integrationTask = CompletableFuture.runAsync(() -> { confirmVo.setIntegration(memberRespVo.getIntegration()); }, threadPoolExecutor); // TODO 订单令牌 // 订单令牌 CompletableFuture<Void> tokenTask = CompletableFuture.runAsync(() -> { String token = UUID.randomUUID().toString().replace("-", ""); confirmVo.setOrderToken(token); redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), token, 30, TimeUnit.MINUTES); }, threadPoolExecutor); CompletableFuture.allOf(addressTask,itemVoList,integrationTask,tokenTask).get(); return confirmVo; } @Override public BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException { // 获取原请求信息,避免异步导致丢失 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); CompletableFuture<Void> changeAddr = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); R r = memberFeignService.changeAddr(memberRespVo.getId(), addrId); if (r.getCode() != 0) { throw new RuntimeException("修改失败"); } }, threadPoolExecutor); // 查询新的运费信息 CompletableFuture<BigDecimal> getFareTask = CompletableFuture.supplyAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); R fare = wareFeignService.getFare(addrId); if (fare.getCode() == 0) { BigDecimal farePay = fare.getData(new TypeReference<BigDecimal>() {}); return farePay; } else{ return new BigDecimal(0); } }, threadPoolExecutor); CompletableFuture.allOf(changeAddr,getFareTask).get(); return getFareTask.get(); } @Override @Transactional public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) { SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo(); submitVoThreadLocal.set(orderSubmitVo); // 获取当前登录用户 MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); responseVo.setCode(0); // 验证令牌是否合法,验证和删除必须保证原子性 String orderToken = orderSubmitVo.getOrderToken(); String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; // 原子删除锁和验证 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(); BigDecimal payAmount = order.getOrderEntity().getPayAmount(); BigDecimal payPrice = orderSubmitVo.getPayPrice(); if(Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){ // 金额对比成功 // 保存订单 saveOrder(order); // 锁定库存,只要没锁到,就抛异常让它回滚 WareSkuLockVo wareSkuLockVo = new WareSkuLockVo(); wareSkuLockVo.setOrderSn(order.getOrderEntity().getOrderSn()); List<OrderItemVo> orderItemVoList = order.getOrderItemEntities().stream().map(orderItemEntity -> { OrderItemVo orderItemVo = new OrderItemVo(); orderItemVo.setSkuId(orderItemEntity.getSkuId()); orderItemVo.setCount(orderItemEntity.getSkuQuantity()); orderItemVo.setTitle(orderItemEntity.getSkuName()); return orderItemVo; }).collect(Collectors.toList()); wareSkuLockVo.setLocks(orderItemVoList); R stockResult = wareFeignService.orderLockStock(wareSkuLockVo); // 订单号,skuId,skuName if (stockResult.getCode()==0) { // 锁定成功 responseVo.setOrderEntity(order.getOrderEntity()); // TODO 远程扣除积分 int i = 10/0; return responseVo; } else{ // 锁定失败 throw new NoStockException(0L); } } else{ responseVo.setCode(2); return responseVo; } } } @Override public OrderEntity getOrderByOrderSn(String orderSn) { return this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn",orderSn)); } private void saveOrder(OrderCreateTo order) { OrderEntity orderEntity = order.getOrderEntity(); List<OrderItemEntity> orderItemEntities = order.getOrderItemEntities(); orderEntity.setModifyTime(new Date()); save(orderEntity); orderItemService.saveBatch(orderItemEntities); } // 创建订单 private OrderCreateTo createOrder(){ OrderCreateTo orderCreateTo = new OrderCreateTo(); // 利用mybatisPlus的IdWorker生成一个不会重复的订单号 String orderSn = IdWorker.getTimeId(); // 构建订单 OrderEntity orderEntity = buildOrder(orderSn); orderCreateTo.setOrderEntity(orderEntity); // 设置订单项 List<OrderItemEntity> orderItemEntities = buildOrderItems(orderSn); orderCreateTo.setOrderItemEntities(orderItemEntities); // 验证价格 if(!orderItemEntities.isEmpty()){ computePrice(orderEntity,orderItemEntities); } return orderCreateTo; } // 计算价格 private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) { // 订单价格相关 BigDecimal totalAmount = new BigDecimal("0.0"); // 优惠卷 BigDecimal coupon = new BigDecimal("0.0"); // 积分 BigDecimal integration = new BigDecimal("0.0"); // 折扣 BigDecimal promotion = new BigDecimal("0.0"); Integer giftIntegration = 0; Integer giftGrowth = 0; for (OrderItemEntity orderItemEntity : orderItemEntities) { totalAmount = totalAmount.add(orderItemEntity.getRealAmount()); coupon = coupon.add(orderItemEntity.getCouponAmount()); integration = integration.add(orderItemEntity.getIntegrationAmount()); promotion = promotion.add(orderItemEntity.getPromotionAmount()); giftIntegration += orderItemEntity.getGiftIntegration(); giftGrowth += orderItemEntity.getGiftGrowth(); } // 设置总额 orderEntity.setTotalAmount(totalAmount); orderEntity.setPromotionAmount(promotion); orderEntity.setCouponAmount(coupon); orderEntity.setIntegrationAmount(integration); // 设置积分和成长值 orderEntity.setGrowth(giftGrowth); orderEntity.setIntegration(giftIntegration); // 设置订单删除状态 orderEntity.setDeleteStatus(0); // 加上运费就是最终总价 orderEntity.setPayAmount(totalAmount.add(orderEntity.getFreightAmount())); } // 构建订单项 private List<OrderItemEntity> buildOrderItems(String orderSn) { // 获取购物车订单项 List<OrderItemVo> currentCartItem = cartFeignService.getCurrentCartItem(); if(!currentCartItem.isEmpty()){ return currentCartItem.stream().map(cartItem -> { // 最后一次再确认订单购物项价格 OrderItemEntity orderItemEntity = buildOrderItem(cartItem); orderItemEntity.setOrderSn(orderSn); return orderItemEntity; }).collect(Collectors.toList()); } return null; } // 构建每一个订单项 private OrderItemEntity buildOrderItem(OrderItemVo cartItem) { OrderItemEntity orderItemEntity = new OrderItemEntity(); // 订单信息,订单号 // 商品的SPU信息 R spuInfo = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId()); SpuInfoVo spuInfoData = spuInfo.getData(new TypeReference<SpuInfoVo>(){}); orderItemEntity.setSpuPic(cartItem.getImage()); orderItemEntity.setSpuBrand(spuInfoData.getBrandId().toString()); orderItemEntity.setSpuId(spuInfoData.getId()); orderItemEntity.setSpuName(spuInfoData.getSpuName()); orderItemEntity.setCategoryId(spuInfoData.getCatalogId()); // 商品的SKU信息 orderItemEntity.setSkuId(cartItem.getSkuId()); orderItemEntity.setSkuName(cartItem.getTitle()); orderItemEntity.setSkuPic(cartItem.getImage()); orderItemEntity.setSkuPrice(cartItem.getPrice()); orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(),";")); orderItemEntity.setSkuQuantity(cartItem.getCount()); // 积分信息 orderItemEntity.setGiftGrowth(cartItem.getPrice().intValue()*cartItem.getCount()); orderItemEntity.setGiftIntegration(cartItem.getPrice().intValue()*cartItem.getCount()); // 总价 // TODO 商品的优惠信息 // 促销 orderItemEntity.setPromotionAmount(new BigDecimal("0.0")); // 优惠卷 orderItemEntity.setCouponAmount(new BigDecimal("0.0")); // 积分优惠 orderItemEntity.setIntegrationAmount(new BigDecimal("0.0")); // 最终价格 BigDecimal price = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity())); // 减去所有的优惠信息 price = price.subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getCouponAmount()).subtract(orderItemEntity.getIntegrationAmount()); orderItemEntity.setRealAmount(price); return orderItemEntity; } private OrderEntity buildOrder(String orderSn){ // 获取当前登录用户 MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); OrderEntity entity = new OrderEntity(); entity.setMemberId(memberRespVo.getId()); entity.setMemberUsername(memberRespVo.getUsername()); // 设置订单号 entity.setOrderSn(orderSn); // 获取物流收货地址信息 OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get(); // 设置运费信息 R fareData = wareFeignService.getFare(orderSubmitVo.getAddrId()); BigDecimal fare = fareData.get("data", new TypeReference<BigDecimal>(){}); entity.setFreightAmount(fare); // 获取用户当前收货默认地址 MemberAddressTo defaultAddr = memberFeignService.getDefaultAddr(memberRespVo.getId()); entity.setReceiverCity(defaultAddr.getCity()); entity.setReceiverDetailAddress(defaultAddr.getDetailAddress()); entity.setReceiverName(defaultAddr.getName()); entity.setReceiverPhone(defaultAddr.getPhone()); entity.setReceiverPostCode(defaultAddr.getPostCode()); entity.setReceiverProvince(defaultAddr.getProvince()); entity.setReceiverRegion(defaultAddr.getRegion()); // 设置订单相关状态 entity.setStatus(OrderStatusEnum.CANCLED.getCode()); entity.setAutoConfirmDay(7); return entity; } }
package com.alatus.mall.order.service.impl; import com.alatus.common.exception.NoStockException; import com.alatus.common.to.MemberAddressTo; import com.alatus.common.utils.R; import com.alatus.common.vo.MemberRespVo; import com.alatus.common.vo.SkuHasStockVo; import com.alatus.mall.order.entity.OrderItemEntity; import com.alatus.mall.order.enums.OrderStatusEnum; import com.alatus.mall.order.feign.ProductFeignService; import com.alatus.mall.order.service.OrderItemService; import com.alatus.mall.order.to.OrderCreateTo; import com.alatus.mall.order.constant.OrderConstant; import com.alatus.mall.order.feign.CartFeignService; import com.alatus.mall.order.feign.MemberFeignService; import com.alatus.mall.order.feign.WareFeignService; import com.alatus.mall.order.interceptor.LoginUserInterceptor; import com.alatus.mall.order.vo.*; import com.alibaba.fastjson.TypeReference; import com.baomidou.mybatisplus.core.toolkit.IdWorker; 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 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.alatus.common.utils.PageUtils; import com.alatus.common.utils.Query; import com.alatus.mall.order.dao.OrderDao; import com.alatus.mall.order.entity.OrderEntity; import com.alatus.mall.order.service.OrderService; 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; @Service("orderService") public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService { @Autowired private MemberFeignService memberFeignService; @Autowired private CartFeignService cartFeignService; @Autowired private OrderItemService orderItemService; @Autowired private ThreadPoolExecutor threadPoolExecutor; @Autowired private WareFeignService wareFeignService; @Autowired private StringRedisTemplate redisTemplate; @Autowired private ProductFeignService productFeignService; private ThreadLocal<OrderSubmitVo> submitVoThreadLocal = new ThreadLocal<>(); @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 confirmVo = new OrderConfirmVo(); // 获取原请求信息,避免异步导致丢失 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); // 远程查询购物车所有选中的购物项 CompletableFuture<Void> itemVoList = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); confirmVo.setItemVoList(cartFeignService.getCurrentCartItem()); }, threadPoolExecutor).thenRunAsync(()->{ // 获取原请求信息,避免异步导致丢失 RequestContextHolder.setRequestAttributes(requestAttributes); // 查询库存 List<OrderItemVo> itemList = confirmVo.getItemVoList(); List<Long> ids = itemList.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList()); R skuHasStock = wareFeignService.getSkuHasStock(ids); if(skuHasStock.getCode()==0){ List<SkuHasStockVo> data = skuHasStock.getData(new TypeReference<List<SkuHasStockVo>>(){}); if(data!=null&& !data.isEmpty()){ Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock)); confirmVo.setStocks(collect); } } },threadPoolExecutor); // 远程查询所有的用户地址列表 CompletableFuture<Void> addressTask = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); confirmVo.setAddress(memberFeignService.getAddress(memberRespVo.getId())); }, threadPoolExecutor).thenRunAsync(()->{ // 获取运费信息 RequestContextHolder.setRequestAttributes(requestAttributes); List<MemberAddressTo> address = confirmVo.getAddress(); for (MemberAddressTo memberAddressTo : address) { if(memberAddressTo.getDefaultStatus()==1){ R fare = wareFeignService.getFare(memberAddressTo.getId()); if (fare.getCode()==0) { confirmVo.setFare(fare.getData(new TypeReference<BigDecimal>() {})); } } } },threadPoolExecutor); // 用户积分 CompletableFuture<Void> integrationTask = CompletableFuture.runAsync(() -> { confirmVo.setIntegration(memberRespVo.getIntegration()); }, threadPoolExecutor); // TODO 订单令牌 // 订单令牌 CompletableFuture<Void> tokenTask = CompletableFuture.runAsync(() -> { String token = UUID.randomUUID().toString().replace("-", ""); confirmVo.setOrderToken(token); redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), token, 30, TimeUnit.MINUTES); }, threadPoolExecutor); CompletableFuture.allOf(addressTask,itemVoList,integrationTask,tokenTask).get(); return confirmVo; } @Override public BigDecimal changeAddr(Long addrId) throws ExecutionException, InterruptedException { // 获取原请求信息,避免异步导致丢失 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); CompletableFuture<Void> changeAddr = CompletableFuture.runAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); R r = memberFeignService.changeAddr(memberRespVo.getId(), addrId); if (r.getCode() != 0) { throw new RuntimeException("修改失败"); } }, threadPoolExecutor); // 查询新的运费信息 CompletableFuture<BigDecimal> getFareTask = CompletableFuture.supplyAsync(() -> { RequestContextHolder.setRequestAttributes(requestAttributes); R fare = wareFeignService.getFare(addrId); if (fare.getCode() == 0) { BigDecimal farePay = fare.getData(new TypeReference<BigDecimal>() {}); return farePay; } else{ return new BigDecimal(0); } }, threadPoolExecutor); CompletableFuture.allOf(changeAddr,getFareTask).get(); return getFareTask.get(); } @Override @Transactional public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) { SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo(); submitVoThreadLocal.set(orderSubmitVo); // 获取当前登录用户 MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); responseVo.setCode(0); // 验证令牌是否合法,验证和删除必须保证原子性 String orderToken = orderSubmitVo.getOrderToken(); String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; // 原子删除锁和验证 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(); BigDecimal payAmount = order.getOrderEntity().getPayAmount(); BigDecimal payPrice = orderSubmitVo.getPayPrice(); if(Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){ // 金额对比成功 // 保存订单 saveOrder(order); // 锁定库存,只要没锁到,就抛异常让它回滚 WareSkuLockVo wareSkuLockVo = new WareSkuLockVo(); wareSkuLockVo.setOrderSn(order.getOrderEntity().getOrderSn()); List<OrderItemVo> orderItemVoList = order.getOrderItemEntities().stream().map(orderItemEntity -> { OrderItemVo orderItemVo = new OrderItemVo(); orderItemVo.setSkuId(orderItemEntity.getSkuId()); orderItemVo.setCount(orderItemEntity.getSkuQuantity()); orderItemVo.setTitle(orderItemEntity.getSkuName()); return orderItemVo; }).collect(Collectors.toList()); wareSkuLockVo.setLocks(orderItemVoList); R stockResult = wareFeignService.orderLockStock(wareSkuLockVo); // 订单号,skuId,skuName if (stockResult.getCode()==0) { // 锁定成功 responseVo.setOrderEntity(order.getOrderEntity()); // TODO 远程扣除积分 int i = 10/0; return responseVo; } else{ // 锁定失败 throw new NoStockException(0L); } } else{ responseVo.setCode(2); return responseVo; } } } @Override public OrderEntity getOrderByOrderSn(String orderSn) { return this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn",orderSn)); } private void saveOrder(OrderCreateTo order) { OrderEntity orderEntity = order.getOrderEntity(); List<OrderItemEntity> orderItemEntities = order.getOrderItemEntities(); orderEntity.setModifyTime(new Date()); save(orderEntity); orderItemService.saveBatch(orderItemEntities); } // 创建订单 private OrderCreateTo createOrder(){ OrderCreateTo orderCreateTo = new OrderCreateTo(); // 利用mybatisPlus的IdWorker生成一个不会重复的订单号 String orderSn = IdWorker.getTimeId(); // 构建订单 OrderEntity orderEntity = buildOrder(orderSn); orderCreateTo.setOrderEntity(orderEntity); // 设置订单项 List<OrderItemEntity> orderItemEntities = buildOrderItems(orderSn); orderCreateTo.setOrderItemEntities(orderItemEntities); // 验证价格 if(!orderItemEntities.isEmpty()){ computePrice(orderEntity,orderItemEntities); } return orderCreateTo; } // 计算价格 private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) { // 订单价格相关 BigDecimal totalAmount = new BigDecimal("0.0"); // 优惠卷 BigDecimal coupon = new BigDecimal("0.0"); // 积分 BigDecimal integration = new BigDecimal("0.0"); // 折扣 BigDecimal promotion = new BigDecimal("0.0"); Integer giftIntegration = 0; Integer giftGrowth = 0; for (OrderItemEntity orderItemEntity : orderItemEntities) { totalAmount = totalAmount.add(orderItemEntity.getRealAmount()); coupon = coupon.add(orderItemEntity.getCouponAmount()); integration = integration.add(orderItemEntity.getIntegrationAmount()); promotion = promotion.add(orderItemEntity.getPromotionAmount()); giftIntegration += orderItemEntity.getGiftIntegration(); giftGrowth += orderItemEntity.getGiftGrowth(); } // 设置总额 orderEntity.setTotalAmount(totalAmount); orderEntity.setPromotionAmount(promotion); orderEntity.setCouponAmount(coupon); orderEntity.setIntegrationAmount(integration); // 设置积分和成长值 orderEntity.setGrowth(giftGrowth); orderEntity.setIntegration(giftIntegration); // 设置订单删除状态 orderEntity.setDeleteStatus(0); // 加上运费就是最终总价 orderEntity.setPayAmount(totalAmount.add(orderEntity.getFreightAmount())); } // 构建订单项 private List<OrderItemEntity> buildOrderItems(String orderSn) { // 获取购物车订单项 List<OrderItemVo> currentCartItem = cartFeignService.getCurrentCartItem(); if(!currentCartItem.isEmpty()){ return currentCartItem.stream().map(cartItem -> { // 最后一次再确认订单购物项价格 OrderItemEntity orderItemEntity = buildOrderItem(cartItem); orderItemEntity.setOrderSn(orderSn); return orderItemEntity; }).collect(Collectors.toList()); } return null; } // 构建每一个订单项 private OrderItemEntity buildOrderItem(OrderItemVo cartItem) { OrderItemEntity orderItemEntity = new OrderItemEntity(); // 订单信息,订单号 // 商品的SPU信息 R spuInfo = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId()); SpuInfoVo spuInfoData = spuInfo.getData(new TypeReference<SpuInfoVo>(){}); orderItemEntity.setSpuPic(cartItem.getImage()); orderItemEntity.setSpuBrand(spuInfoData.getBrandId().toString()); orderItemEntity.setSpuId(spuInfoData.getId()); orderItemEntity.setSpuName(spuInfoData.getSpuName()); orderItemEntity.setCategoryId(spuInfoData.getCatalogId()); // 商品的SKU信息 orderItemEntity.setSkuId(cartItem.getSkuId()); orderItemEntity.setSkuName(cartItem.getTitle()); orderItemEntity.setSkuPic(cartItem.getImage()); orderItemEntity.setSkuPrice(cartItem.getPrice()); orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(),";")); orderItemEntity.setSkuQuantity(cartItem.getCount()); // 积分信息 orderItemEntity.setGiftGrowth(cartItem.getPrice().intValue()*cartItem.getCount()); orderItemEntity.setGiftIntegration(cartItem.getPrice().intValue()*cartItem.getCount()); // 总价 // TODO 商品的优惠信息 // 促销 orderItemEntity.setPromotionAmount(new BigDecimal("0.0")); // 优惠卷 orderItemEntity.setCouponAmount(new BigDecimal("0.0")); // 积分优惠 orderItemEntity.setIntegrationAmount(new BigDecimal("0.0")); // 最终价格 BigDecimal price = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity())); // 减去所有的优惠信息 price = price.subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getCouponAmount()).subtract(orderItemEntity.getIntegrationAmount()); orderItemEntity.setRealAmount(price); return orderItemEntity; } private OrderEntity buildOrder(String orderSn){ // 获取当前登录用户 MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get(); OrderEntity entity = new OrderEntity(); entity.setMemberId(memberRespVo.getId()); entity.setMemberUsername(memberRespVo.getUsername()); // 设置订单号 entity.setOrderSn(orderSn); // 获取物流收货地址信息 OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get(); // 设置运费信息 R fareData = wareFeignService.getFare(orderSubmitVo.getAddrId()); BigDecimal fare = fareData.get("data", new TypeReference<BigDecimal>(){}); entity.setFreightAmount(fare); // 获取用户当前收货默认地址 MemberAddressTo defaultAddr = memberFeignService.getDefaultAddr(memberRespVo.getId()); entity.setReceiverCity(defaultAddr.getCity()); entity.setReceiverDetailAddress(defaultAddr.getDetailAddress()); entity.setReceiverName(defaultAddr.getName()); entity.setReceiverPhone(defaultAddr.getPhone()); entity.setReceiverPostCode(defaultAddr.getPostCode()); entity.setReceiverProvince(defaultAddr.getProvince()); entity.setReceiverRegion(defaultAddr.getRegion()); // 设置订单相关状态 entity.setStatus(OrderStatusEnum.CANCLED.getCode()); entity.setAutoConfirmDay(7); return entity; } }
package com.alatus.common.to.mq; import lombok.Data; @Data public class StockDetailTo { private Long id; /** * sku_id */ private Long skuId; /** * sku_name */ private String skuName; /** * 购买个数 */ private Integer skuNum; /** * 工作单id */ private Long taskId; /** * 仓库id */ private Long wareId; /** * 锁定状态 1-锁定 2-解锁 3-扣减 */ private Integer lockStatus; }
package com.alatus.common.to.mq; import lombok.Data; @Data public class StockDetailTo { private Long id; /** * sku_id */ private Long skuId; /** * sku_name */ private String skuName; /** * 购买个数 */ private Integer skuNum; /** * 工作单id */ private Long taskId; /** * 仓库id */ private Long wareId; /** * 锁定状态 1-锁定 2-解锁 3-扣减 */ private Integer lockStatus; }
标签:order,-----,new,import,alatus,orderItemEntity,com,TCC,商城 From: https://blog.csdn.net/2201_75960169/article/details/144488299package com.alatus.common.to.mq; import lombok.Data; @Data public class StockLockedTo { //库存工作单ID private Long id; // 工作详情映射对象 private StockDetailTo stockDetailTo; }
package com.alatus.common.to.mq; import lombok.Data; @Data public class StockLockedTo { //库存工作单ID private Long id; // 工作详情映射对象 private StockDetailTo stockDetailTo; }