首页 > 其他分享 >商城项目基于消息队列的TCC分布式事务控制改造-----商城项目

商城项目基于消息队列的TCC分布式事务控制改造-----商城项目

时间:2024-12-15 23:31:06浏览次数:5  
标签:order ----- new import alatus orderItemEntity com TCC 商城

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

标签:order,-----,new,import,alatus,orderItemEntity,com,TCC,商城
From: https://blog.csdn.net/2201_75960169/article/details/144488299

相关文章

  • Ansible基础使用-多主机批量执行脚本和一些特性介绍
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录1.基础用法1.1定义清单1.2静态清单指定受控主机1.3验证清单1.4自定义清单文件2管理配置文件2.1配置ansible2.2配置文件优先级2.3管理配置文件中的设置2.4配置连接2.4.1清单位置2.4.2......
  • 电子应用设计方案-51:智能镜子系统方案设计
    智能镜子系统方案设计 一、引言智能镜子作为一种新兴的智能家居设备,将传统镜子与现代科技相结合,为用户提供了丰富的功能和便捷的体验。本方案旨在设计一款功能强大、用户友好、外观美观的智能镜子系统。 二、系统概述1.系统目标   -提供清晰的镜面反射效果,同时......
  • 《Django 5 By Example》阅读笔记:p551-p560
    《Django5ByExample》学习第20天,p551-p560总结,总计10页。一、技术总结1.custommodelfield(1)示例courses/fields.pyfromdjango.core.exceptionsimportObjectDoesNotExistfromdjango.dbimportmodelsclassOrderField(models.PositiveIntegerField):d......
  • 大模型日报 2024-12-15
    大模型日报2024-12-15大模型资讯标题:KDD2025|多标签节点分类场景下,阿里安全&浙大对图神经网络增强发起挑战摘要:本文介绍了阿里安全与浙江大学在多标签节点分类场景下的图神经网络增强技术的研究成果,论文《Correlation-AwareGraphConvolutio......
  • 超强 !顶会创新融合!基于 2D-SWinTransformer 的并行分类网络
    往期精彩内容:Python-凯斯西储大学(CWRU)轴承数据解读与分类处理基于FFT+CNN-BiGRU-Attention时域、频域特征注意力融合的轴承故障识别模型-CSDN博客基于FFT+CNN-Transformer时域、频域特征融合的轴承故障识别模型-CSDN博客Python轴承故障诊断(11)基于VMD+CNN-B......
  • 独家原创 | CEEMDAN-Transformer-BiLSTM并行 + XGBoost组合预测
    往期精彩内容:时序预测:LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较全是干货|数据集、学习资料、建模资源分享!EMD变体分解效果最好算法——CEEMDAN(五)-CSDN博客拒绝信息泄露!VMD滚动分解+Informer-BiLSTM并行预测模型-CSDN博客单步预测-风速预测模型代码全家桶-......
  • docker启动ES增加elasticsearch-header访问,解决跨域问题
    在做一件什么事情:docker部署ES服务,希望增加可视化工具。于是选择了一种简单的方式,增加elasticsearch-header组件访问。这样只需要在浏览器上输入地址可以直接访问。遇到了什么问题:提示跨域访问问题分析:服务器端放开访问限制解决方案:增加跨域访问配置脚本如下:点击查看代......
  • CTF知识集-PHP特性
    title:CTF知识集-PHP特性写在开头可能会用到的提示call_user_func调用的函数可以不区分大小写preg_match过滤存在长度溢出,长度超过100w检测失效。str_repeat(‘show’,250000);生成100w个字符preg_match是无法处理数组的,例如:preg_match(......
  • ARM嵌入式学习--第七天(GPT)
    GPT-介绍  GPT有一个32位向上计数器,定时计数器值可以使用外部引脚上的事件捕获到寄存器中,捕获触发器可以被编程为上升沿和下降沿。GPT还可以在输出比较引脚上生成事件,并在定时器达到编程值时产生中断。GPT有一个12位预分频器,它提供从多个时钟源获得的可编程时钟频率 ......
  • 数据集-目标检测系列- 步行街 人 检测数据集 person >> DataBall
    数据集-目标检测系列-步行街人检测数据集person>>DataBallDataBall助力快速掌握数据集的信息和使用方式,会员享有百种数据集,持续增加中。 需要更多数据资源和技术解决方案,知识星球:“DataBall-X数据球(free)”贵在坚持!数据样例项目地址:*相关项目1)数据集......