package com.tmerclub.cloud.order.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.tmerclub.cloud.api.delivery.vo.StationVO;
import com.tmerclub.cloud.common.order.vo.WarehouseVO;
import com.tmerclub.cloud.api.order.bo.EsAllotOrderBO;
import com.tmerclub.cloud.api.order.bo.EsAllotOrderItemBO;
import com.tmerclub.cloud.api.product.dto.SkuStockLockDTO;
import com.tmerclub.cloud.api.product.feign.*;
import com.tmerclub.cloud.api.product.vo.SkuStockVO;
import com.tmerclub.cloud.common.cache.constant.LuaOperateEnum;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.constant.SysTypeEnum;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.product.constant.StockBillStatus;
import com.tmerclub.cloud.common.product.constant.StockBillType;
import com.tmerclub.cloud.common.product.constant.StockPointType;
import com.tmerclub.cloud.common.product.constant.StockType;
import com.tmerclub.cloud.common.product.dto.StockPointSkuDTO;
import com.tmerclub.cloud.common.product.vo.*;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.rocketmq.config.RocketMqConstant;
import com.tmerclub.cloud.common.util.ExcelUtil;
import com.tmerclub.cloud.constant.DistributedIdKey;
import com.tmerclub.cloud.delivery.mapper.StationMapper;
import com.tmerclub.cloud.delivery.service.DeliveryCompanyService;
import com.tmerclub.cloud.delivery.vo.DeliveryCompanyVO;
import com.tmerclub.cloud.order.bo.mongo.MongoAllotOrderBO;
import com.tmerclub.cloud.order.bo.mongo.MongoAllotOrderItemBO;
import com.tmerclub.cloud.order.constant.AllotOrderStatusEnum;
import com.tmerclub.cloud.order.dto.AllotOrderDTO;
import com.tmerclub.cloud.order.dto.AllotOrderItemDTO;
import com.tmerclub.cloud.order.listener.AllotSpuExcelListener;
import com.tmerclub.cloud.order.listener.AllotSpuInboundExcelListener;
import com.tmerclub.cloud.order.manager.MongoAllotOrderManager;
import com.tmerclub.cloud.order.mapper.AllotOrderMapper;
import com.tmerclub.cloud.order.model.AllotOrder;
import com.tmerclub.cloud.order.service.AllotOrderItemService;
import com.tmerclub.cloud.order.service.AllotOrderService;
import com.tmerclub.cloud.order.vo.*;
import com.tmerclub.cloud.order.vo.*;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author gaozijie
 * @since  2023-11-20
 */
@Service
public class AllotOrderServiceImpl implements AllotOrderService {

    private static final Logger log = LoggerFactory.getLogger(AllotOrderServiceImpl.class);

    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private MongoAllotOrderManager mongoAllotOrderManager;
    @Autowired
    private RocketMQTemplate stockBillLogPurchaseStorageTemplate;

    @Autowired
    private AllotOrderMapper allotOrderMapper;

    @Autowired
    private AllotOrderItemService allotOrderItemService;
    @DubboReference
    private WarehouseFeignClient warehouseFeignClient;
    @Autowired
    private DeliveryCompanyService deliveryCompanyService;
    @Autowired
    private StationMapper stationMapper;

    @DubboReference
    private SpuFeignClient spuFeignClient;
    @DubboReference
    private SkuFeignClient skuFeignClient;
    @DubboReference
    private SkuStockFeignClient skuStockFeignClient;
    @DubboReference
    private SkuStockChangeFeignClient skuStockChangeFeignClient;
    @DubboReference
    private StockPointFeignClient stockPointFeignClient;

    @Override
    public AllotOrderVO getAllotOrder(AllotOrderDTO allotOrderDTO) {
        AllotOrderVO allotOrderVO = allotOrderMapper.getAllotOrder(allotOrderDTO);
        // 补充物流公司名称
        DeliveryCompanyVO deliveryCompany = deliveryCompanyService.getByDeliveryCompanyId(allotOrderVO.getDvyCompanyId());
        if (!Objects.isNull(deliveryCompany)) {
            allotOrderVO.setDvyCompanyName(deliveryCompany.getName());
        }
        // 补充仓库名称
        WarehouseVO warehouseVO = warehouseFeignClient.getWarehouseById(allotOrderVO.getOutWarehouseId());
        if (!Objects.isNull(warehouseVO)) {
            allotOrderVO.setOutStockPointName(warehouseVO.getWarehouseName());
        }
        warehouseVO = warehouseFeignClient.getWarehouseById(allotOrderVO.getInWarehouseId());
        if (!Objects.isNull(warehouseVO)) {
            allotOrderVO.setInStockPointName(warehouseVO.getWarehouseName());
        }
        // 补充商品名称
        this.fillAllotItemInfo(allotOrderVO.getAllotOrderItemVOList());
        return allotOrderVO;
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void addAllotOrder(AllotOrderDTO allotOrderDTO) {
        this.checkParam(allotOrderDTO);
        AllotOrder allotOrder = this.buildAllotOrder(allotOrderDTO);
        allotOrderDTO.setAllotOrderId(allotOrder.getAllotOrderId());
        // 库存锁定
        List<SkuStockLockDTO> skuStockLocks = allotOrderDTO.getAllotOrderItemDTOList().stream()
                .map(x -> new SkuStockLockDTO(allotOrder.getAllotOrderId(), allotOrderDTO.getOutWarehouseId(), x.getSkuId(), x.getAllotCount())).toList();
        this.lockStock(skuStockLocks);
        // 新增调拨订单
        allotOrderMapper.addAllotOrder(allotOrder);
        // 新增调拨订单商品
        List<Long> skuIds = new ArrayList<>(allotOrderDTO.getAllotOrderItemDTOList().size());
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderDTO.getAllotOrderItemDTOList()) {
            allotOrderItemDTO.setAllotOrderId(allotOrder.getAllotOrderId());
            allotOrderItemDTO.setShopId(allotOrderDTO.getShopId());
            allotOrderItemDTO.setInboundCount(allotOrderItemDTO.getAllotCount());
            skuIds.add(allotOrderItemDTO.getSkuId());
        }
        allotOrderItemService.addBatchAllotOrderItem(allotOrderDTO.getAllotOrderItemDTOList());
        // 解锁库存（调拨订单不存在支付，创建成功后直接解锁）
        skuStockChangeFeignClient.reduceLockAllotOrder(skuStockLocks);
        // 更新仓库和商品关联
        stockPointFeignClient.updateStockPointSkuRelate(Collections.singletonList(allotOrderDTO.getOutWarehouseId()), skuIds);
        // 新增出库库存流水
        StockBillLogVO stockBillLog = this.buildStockBillLog(allotOrderDTO, allotOrder.getOutWarehouseId(), allotOrder.getOutStockPointType(), StockBillType.ALLOT_OUT);
        TransactionSendResult transactionSendResult = stockBillLogPurchaseStorageTemplate.sendMessageInTransaction(RocketMqConstant.STOCK_BILL_LOG_PURCHASE_STORAGE_TOPIC, new GenericMessage<>(stockBillLog), stockBillLog);
        if (!Objects.equals(transactionSendResult.getLocalTransactionState(), LocalTransactionState.COMMIT_MESSAGE)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public void updateAllotOrder(AllotOrderDTO allotOrderDTO) {
        if (Objects.isNull(allotOrderDTO)) {
            return;
        }
        AllotOrder allotOrder = new AllotOrder();
        BeanUtils.copyProperties(allotOrderDTO, allotOrder);
        allotOrder.setUpdateTime(LocalDateTime.now());
        allotOrderMapper.updateAllotOrder(allotOrder);
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void inbound(AllotOrderDTO allotOrderDTO) {
        AllotOrderVO dbAllotOrder = this.checkOrderStatus(allotOrderDTO);
        // 过滤掉入库数量为0的数据
        List<AllotOrderItemDTO> allotOrderItems = allotOrderDTO.getAllotOrderItemDTOList().stream().filter(x -> x.getInboundCount() != null && x.getInboundCount() > 0).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(allotOrderItems)) {
            throw new LuckException("入库数量不能为0");
        }
        allotOrderDTO.setAllotOrderItemDTOList(allotOrderItems);
        allotOrderDTO.setOutStockPointType(dbAllotOrder.getOutStockPointType());
        allotOrderDTO.setInStockPointType(dbAllotOrder.getInStockPointType());
        // 修改调拨商品入库数量
        Map<Long, AllotOrderItemVO> dbItemMap = dbAllotOrder.getAllotOrderItemVOList().stream().collect(Collectors.toMap(AllotOrderItemVO::getSkuId, x -> x));
        List<AllotOrderItemDTO> updateItems = new ArrayList<>(Constant.INITIAL_CAPACITY);
        AllotOrderItemDTO updateItem;
        AllotOrderItemVO dbItem;
        for (AllotOrderItemDTO inboundItem : allotOrderItems) {
            dbItem = dbItemMap.get(inboundItem.getSkuId());
            if (Objects.isNull(dbItem)) {
                throw new LuckException("入库商品不是该订单的商品");
            }
            if (inboundItem.getInboundCount() > dbItem.getAllotCount() - dbItem.getAllotInCount()) {
                throw new LuckException("入库数量超出可入库数量");
            }
            dbItem.setAllotInCount(dbItem.getAllotInCount() + inboundItem.getInboundCount());
            updateItem = new AllotOrderItemDTO();
            updateItem.setAllotOrderId(dbAllotOrder.getAllotOrderId());
            updateItem.setAllotOrderItemId(dbItem.getAllotOrderItemId());
            updateItem.setAllotInCount(dbItem.getAllotInCount());
            updateItems.add(updateItem);
        }
        allotOrderItemService.updateBatchAllotOrderItem(updateItems);
        // 修改调拨订单状态
        boolean isCompete = true;
        for (AllotOrderItemVO item : dbAllotOrder.getAllotOrderItemVOList()) {
            isCompete = isCompete && (Objects.equals(item.getAllotCount(), item.getAllotInCount()));
        }
        AllotOrderDTO updateOrder = new AllotOrderDTO();
        updateOrder.setAllotOrderId(allotOrderDTO.getAllotOrderId());
        updateOrder.setRemark(allotOrderDTO.getRemark());
        updateOrder.setStatus(isCompete ? AllotOrderStatusEnum.COMPLETION.getNum() : AllotOrderStatusEnum.PARTIALLY_STOCK.getNum());
        this.updateAllotOrder(updateOrder);
        // 修改库存(入库仓增加库存)
        List<SkuStockVO> skuStockVOList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderItems) {
            skuStockVOList.add(this.buildSkuStockVO(allotOrderItemDTO, allotOrderDTO.getInWarehouseId(), LuaOperateEnum.SKU_ADD));
        }
        skuStockFeignClient.changeSkuStock(skuStockVOList);
        // 新增入库库存流水
        StockBillLogVO stockBillLog = this.buildStockBillLog(allotOrderDTO, allotOrderDTO.getInWarehouseId(), allotOrderDTO.getInStockPointType(), StockBillType.ALLOT_IN);
        TransactionSendResult transactionSendResult = stockBillLogPurchaseStorageTemplate.sendMessageInTransaction(RocketMqConstant.STOCK_BILL_LOG_PURCHASE_STORAGE_TOPIC, new GenericMessage<>(stockBillLog), stockBillLog);
        if (!Objects.equals(transactionSendResult.getLocalTransactionState(), LocalTransactionState.COMMIT_MESSAGE)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public void completeOrder(AllotOrderDTO allotOrderDTO) {
        this.checkOrderStatus(allotOrderDTO);
        allotOrderDTO.setStatus(AllotOrderStatusEnum.COMPLETION.getNum());
        this.updateAllotOrder(allotOrderDTO);
    }

    @Override
    public void nullifyOrder(AllotOrderDTO allotOrderDTO) {
        AllotOrderVO dbAllotOrder = this.checkOrderStatus(allotOrderDTO);
        // 过滤出需要退还库存的数据，并转成dto
        List<AllotOrderItemDTO> allotOrderItems = dbAllotOrder.getAllotOrderItemVOList().stream()
                .filter(x -> x.getAllotCount() > x.getAllotInCount())
                .map(x -> {
                    AllotOrderItemDTO allotOrderItemDTO = new AllotOrderItemDTO();
                    BeanUtils.copyProperties(x, allotOrderItemDTO);
                    return allotOrderItemDTO;
                }).toList();
        if (CollectionUtils.isEmpty(allotOrderItems)) {
            return;
        }
        // 作废
        AllotOrderDTO allotOrder = new AllotOrderDTO();
        BeanUtils.copyProperties(dbAllotOrder, allotOrder);
        allotOrder.setAllotOrderItemDTOList(allotOrderItems);
        this.nullify(allotOrder);
    }

    @Override
    public void nullifyOrderByWarehouseId(Long warehouseId) {
        // 查找未完成/作废的调拨订单
        AllotOrderDTO allotOrderDTO = new AllotOrderDTO();
        allotOrderDTO.setWarehouseId(warehouseId);
        allotOrderDTO.setStatuses(Arrays.asList(AllotOrderStatusEnum.WAIT_STOCK.getNum(), AllotOrderStatusEnum.PARTIALLY_STOCK.getNum()));
        List<MongoAllotOrderBO> allotOrderBOS = mongoAllotOrderManager.listAllotOrder(allotOrderDTO);
        if (CollectionUtils.isEmpty(allotOrderBOS)) {
            return;
        }
        // 循环作废
        for (MongoAllotOrderBO allotOrderBO : allotOrderBOS) {
            List<AllotOrderItemDTO> allotOrderItems = allotOrderBO.getAllotOrderItemBOList().stream()
                    .filter(x -> x.getAllotCount() > x.getAllotInCount())
                    .map(x -> {
                        AllotOrderItemDTO allotOrderItemDTO = new AllotOrderItemDTO();
                        BeanUtils.copyProperties(x, allotOrderItemDTO);
                        return allotOrderItemDTO;
                    }).toList();
            if (CollectionUtils.isEmpty(allotOrderItems)) {
                continue;
            }
            // 作废
            AllotOrderDTO allotOrder = new AllotOrderDTO();
            BeanUtils.copyProperties(allotOrderBO, allotOrder);
            allotOrder.setAllotOrderItemDTOList(allotOrderItems);
            this.nullify(allotOrder);
        }
    }

    @Override
    public EsAllotOrderBO saveMongoAndGetEsInfo(Long allotOrderId) {
        MongoAllotOrderBO mongoAllotOrderBO = mongoAllotOrderManager.saveAllotOrder(allotOrderId);
        // 转成es数据
        EsAllotOrderBO esAllotOrderBO = new EsAllotOrderBO();
        BeanUtils.copyProperties(mongoAllotOrderBO, esAllotOrderBO);
        List<EsAllotOrderItemBO> esItems = new ArrayList<>(mongoAllotOrderBO.getAllotOrderItemBOList().size());
        for (MongoAllotOrderItemBO itemBO : mongoAllotOrderBO.getAllotOrderItemBOList()) {
            EsAllotOrderItemBO esItem = new EsAllotOrderItemBO();
            BeanUtils.copyProperties(itemBO, esItem);
            esItems.add(esItem);
        }
        esAllotOrderBO.setAllotOrderItems(esItems);
        return esAllotOrderBO;
    }

    @Override
    public void downloadModel(HttpServletResponse response) {
        Map<Integer, String[]> map = new HashMap<>(8);
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            ExcelWriterBuilder excelWriterMerge = ExcelUtil.getExcelWriterMerge(response, AllotSpuExcelVO.EXCEL_NAME, AllotSpuExcelVO.MERGE_ROW_INDEX, AllotSpuExcelVO.MERGE_COLUMN_INDEX);
            // useDefaultStyle false 不需要默认的头部加粗/自动换行样式
            excelWriter = ExcelUtil.getExcelModel(excelWriterMerge, map, 1).useDefaultStyle(false).build();
            // 进行写入操作
            WriteSheet sheetWriter = EasyExcel.writerSheet(AllotSpuExcelVO.SHEET_NAME).head(AllotSpuExcelVO.class).build();
            excelWriter.write(Collections.emptyList(), sheetWriter);
        } catch (Exception e) {
            log.error("导出调拨商品excel模板失败", e);
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    @Override
    public AllotSpuImportVO parseFile(MultipartFile excelFile, AllotOrderDTO allotOrderDTO) {
        // 参数检查
        if (excelFile == null) {
            throw new LuckException("网络繁忙，请稍后重试");
        }
        this.checkWarehouse(allotOrderDTO.getOutWarehouseId(), allotOrderDTO.getShopId(), allotOrderDTO.getSysType());
        this.checkWarehouse(allotOrderDTO.getInWarehouseId(), allotOrderDTO.getShopId(), allotOrderDTO.getSysType());
        // 读取excel文件数据
        List<AllotSpuExcelVO> allotSpuExcels = new ArrayList<>(Constant.INITIAL_CAPACITY);
        AllotSpuExcelListener excelListener = new AllotSpuExcelListener(allotSpuExcels);
        try {
            EasyExcel.read(excelFile.getInputStream(), AllotSpuExcelVO.class, excelListener).sheet().doRead();
        } catch (IOException e) {
            throw new LuckException(e.getMessage());
        }
        if (CollectionUtil.isEmpty(allotSpuExcels)) {
            throw new LuckException("导入数据为空，请检查下文件");
        }
        // 查询sku数据
        Map<String, AllotSpuExcelVO> partyCodeMap = allotSpuExcels.stream().collect(Collectors.toMap(AllotSpuExcelVO::getPartyCode, x -> x));
        List<SkuVO> skuVOList = skuFeignClient.listByPartyCodes(new ArrayList<>(partyCodeMap.keySet()));
        Set<Long> spuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        List<Long> skuIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
        Map<Long, SkuVO> skuMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        for (SkuVO skuVO : skuVOList) {
            spuIds.add(skuVO.getSpuId());
            skuIds.add(skuVO.getSkuId());
            skuMap.put(skuVO.getSkuId(), skuVO);
        }
        // 检查sku
        StockPointSkuDTO stockPointSkuDTO = new StockPointSkuDTO();
        stockPointSkuDTO.setSkuIds(skuIds);
        stockPointSkuDTO.setStockPointIds(Collections.singletonList(allotOrderDTO.getOutWarehouseId()));
        List<StockPointSkuVO> stockPointSkus = stockPointFeignClient.listStockPointSku(stockPointSkuDTO);
        if (CollectionUtils.isEmpty(stockPointSkus)) {
            throw new LuckException("调拨商品非该调出点的商品");
        }
        // 查询spu数据
        List<SpuVO> spuVOList = spuFeignClient.listSpuBySpuIds(new ArrayList<>(spuIds)).getData();
        Map<Long, SpuVO> spuMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (!CollectionUtils.isEmpty(spuVOList)) {
            spuMap = spuVOList.stream().collect(Collectors.toMap(SpuVO::getSpuId, x -> x));
        }
        // 查询sku对应调出/入仓库存
        List<SkuStockVO> skuStockVOList = new ArrayList<>(skuIds.size() * 2);
        for (Long skuId : skuIds) {
            SkuStockVO outSkuStockVO = new SkuStockVO(skuId);
            outSkuStockVO.setStockPointId(allotOrderDTO.getOutWarehouseId());
            skuStockVOList.add(outSkuStockVO);
            SkuStockVO inSkuStockVO = new SkuStockVO(skuId);
            inSkuStockVO.setStockPointId(allotOrderDTO.getInWarehouseId());
            skuStockVOList.add(inSkuStockVO);
        }
        Map<String, SkuStockVO> skuStockMap = skuStockFeignClient.listSkuStock(skuStockVOList);
        // 补充商品信息
        List<AllotSpuVO> allotSpuVOList = new ArrayList<>(allotSpuExcels.size());
        AllotSpuVO allotSpuVO;
        for (StockPointSkuVO stockPointSku : stockPointSkus) {
            SkuVO skuVO = skuMap.get(stockPointSku.getSkuId());
            allotSpuVO = this.buildAllotSpuVO(partyCodeMap.get(skuVO.getPartyCode()), spuMap.get(skuVO.getSpuId()), skuVO, skuStockMap, stockPointSku.getStockPointId(), allotOrderDTO.getInWarehouseId());
            if (Objects.isNull(allotSpuVO)) {
                continue;
            }
            allotSpuVOList.add(allotSpuVO);
        }
        // 构建返回信息
        AllotSpuImportVO allotSpuImportVO = new AllotSpuImportVO();
        allotSpuImportVO.setAllotSpuVOList(allotSpuVOList);
        allotSpuImportVO.setTips(String.format("检查到%d条商品信息，成功导入%d条商品信息！错误%d条商品信息!", allotSpuExcels.size(), allotSpuVOList.size(), allotSpuExcels.size()-allotSpuVOList.size()));
        return allotSpuImportVO;
    }

    @Override
    public void downloadInboundModel(HttpServletResponse response, AllotOrderDTO allotOrderDTO) {
        // 获取订单导入项数据
        AllotOrderVO allotOrder = allotOrderMapper.getAllotOrder(allotOrderDTO);
        if (Objects.isNull(allotOrder)) {
            throw new LuckException("调拨订单不存在");
        }
        // 补充商品信息
        this.fillAllotItemInfo(allotOrder.getAllotOrderItemVOList());
        // 转成excel数据集合
        int seq = 1;
        List<AllotSpuInboundExcelVO> inboundExcels = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderItemVO allotOrderItemVO : allotOrder.getAllotOrderItemVOList()) {
            inboundExcels.add(this.buildInboundExcelVO(allotOrderItemVO, seq++));
        }
        Map<Integer, String[]> map = new HashMap<>(8);
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            ExcelWriterBuilder excelWriterMerge = ExcelUtil.getExcelWriterMerge(response, AllotSpuInboundExcelVO.EXCEL_NAME, AllotSpuInboundExcelVO.MERGE_ROW_INDEX, AllotSpuInboundExcelVO.MERGE_COLUMN_INDEX);
            // useDefaultStyle false 不需要默认的头部加粗/自动换行样式
            excelWriter = ExcelUtil.getExcelModel(excelWriterMerge, map, 1).useDefaultStyle(false).build();
            // 进行写入操作
            WriteSheet sheetWriter = EasyExcel.writerSheet(AllotSpuInboundExcelVO.SHEET_NAME).head(AllotSpuInboundExcelVO.class).build();
            excelWriter.write(inboundExcels, sheetWriter);
        } catch (Exception e) {
            log.error("导出调拨商品入库excel模板失败", e);
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    @Override
    public AllotSpuInboundImportVO parseInboundFile(MultipartFile excelFile, AllotOrderDTO allotOrderDTO) {
        AllotOrderVO allotOrder = allotOrderMapper.getAllotOrder(allotOrderDTO);
        if (Objects.isNull(allotOrder)) {
            throw new LuckException("调拨订单不存在");
        }
        // 补充商品信息
        this.fillAllotItemInfo(allotOrder.getAllotOrderItemVOList());
        // 解析excel文件
        List<AllotSpuInboundExcelVO> inboundExcels = new ArrayList<>(Constant.INITIAL_CAPACITY);
        AllotSpuInboundExcelListener excelListener = new AllotSpuInboundExcelListener(inboundExcels);
        try {
            EasyExcel.read(excelFile.getInputStream(), AllotSpuInboundExcelVO.class, excelListener).sheet().doRead();
        } catch (IOException e) {
            throw new LuckException(e.getMessage());
        }
        if (CollectionUtil.isEmpty(inboundExcels)) {
            throw new LuckException("导入数据为空，请检查下文件");
        }
        Map<String, AllotSpuInboundExcelVO> inboundExcelMap = inboundExcels.stream().collect(Collectors.toMap(AllotSpuInboundExcelVO::getPartyCode, x -> x));
        // 补充入库数量
        int successCount = 0;
        AllotSpuInboundExcelVO inboundExcel;
        for (AllotOrderItemVO allotOrderItemVO : allotOrder.getAllotOrderItemVOList()) {
            inboundExcel = inboundExcelMap.get(allotOrderItemVO.getPartyCode());
            if (Objects.isNull(inboundExcel)) {
                allotOrderItemVO.setInboundCount(0);
                continue;
            }
            allotOrderItemVO.setInboundCount(inboundExcel.getInboundCount());
            successCount ++;
        }
        // 构建返回信息
        AllotSpuInboundImportVO importVO = new AllotSpuInboundImportVO();
        importVO.setAllotOrderItemVOList(allotOrder.getAllotOrderItemVOList());
        importVO.setTips(String.format("检查到%d条商品信息，成功导入%d条商品信息！错误%d条商品信息!", inboundExcels.size(), successCount, inboundExcels.size()-successCount));
        return importVO;
    }

    @Override
    public Long countUnFinish(Long warehouseId) {
        if (Objects.isNull(warehouseId)) {
            return 0L;
        }
        return mongoAllotOrderManager.countUnFinishAllot(warehouseId);
    }

    /**
     * 校验参数
     * @param allotOrderDTO 调拨订单dto
     */
    private void checkParam(AllotOrderDTO allotOrderDTO) {
        // 校验库存点
        WarehouseVO outWarehouse = this.checkWarehouse(allotOrderDTO.getOutWarehouseId(), allotOrderDTO.getShopId(), allotOrderDTO.getSysType());
        allotOrderDTO.setOutStockPointType(this.getStockPointType(outWarehouse.getSysType()));
        WarehouseVO inWarehouse = this.checkWarehouse(allotOrderDTO.getInWarehouseId(), allotOrderDTO.getShopId(), allotOrderDTO.getSysType());
        allotOrderDTO.setInStockPointType(this.getStockPointType(inWarehouse.getSysType()));
        // 校验商品库存
        List<AllotOrderItemDTO> allotOrderItemDTOList = allotOrderDTO.getAllotOrderItemDTOList();
        if (CollectionUtils.isEmpty(allotOrderItemDTOList)) {
            throw new LuckException("调拨订单商品不能为空");
        }
        List<Long> skuIds = new ArrayList<>(allotOrderItemDTOList.size());
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderItemDTOList) {
            if (Objects.isNull(allotOrderItemDTO.getAllotCount())
                    || Objects.equals(allotOrderItemDTO.getAllotCount(), 0)) {
                throw new LuckException("调拨数量不能为0");
            }
            skuIds.add(allotOrderItemDTO.getSkuId());
        }
        List<Long> containSkuIds = skuStockFeignClient.checkStockPointContainSku(Collections.singletonList(allotOrderDTO.getOutWarehouseId()), skuIds);
        if (!Objects.equals(skuIds.size(), containSkuIds.size())) {
            throw new LuckException("调拨商品非该调出点的商品");
        }
    }

    /**
     * 检查仓库
     * @param warehouseId 仓库id
     * @param shopId 店铺id
     * @param sysType 系统类型
     */
    private WarehouseVO checkWarehouse(Long warehouseId,
                                Long shopId,
                                Integer sysType) {
        WarehouseVO warehouse = warehouseFeignClient.getWarehouseById(warehouseId);
        if (Objects.isNull(warehouse)) {
            throw new LuckException(String.format("仓库[%s]不存在", warehouseId));
        }
        if (Objects.equals(warehouse.getSysType(), SysTypeEnum.STATION.value())) {
            // 检查门店仓库
            if(Objects.equals(sysType, SysTypeEnum.SUPPLIER.value())) {
                throw new LuckException("供应商不存在门店");
            }
            StationVO station = stationMapper.getByStationId(warehouse.getShopId());
            if (!Objects.equals(station.getShopId(), shopId))  {
                throw new LuckException(String.format("门店[%s]非该店铺的门店", station.getStationId()));
            }
        } else if (!Objects.equals(shopId, warehouse.getShopId())
                || !Objects.equals(sysType, warehouse.getSysType())) {
            // 检查商家/供应商仓库
            throw new LuckException(String.format("仓库[%s]非该店铺的仓库", warehouseId));
        }
        return warehouse;
    }

    /**
     * 构建调拨订单实体类
     * @param allotOrderDTO 调拨订单dto
     * @return 调拨订单
     */
    private AllotOrder buildAllotOrder(AllotOrderDTO allotOrderDTO) {
        AllotOrder allotOrder = new AllotOrder();
        BeanUtils.copyProperties(allotOrderDTO, allotOrder);
        allotOrder.setAllotOrderId(segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_ALLOT_ORDER, allotOrderDTO.getShopId()));
        allotOrder.setStatus(AllotOrderStatusEnum.WAIT_STOCK.getNum());
        allotOrder.setTotalAllotCount(allotOrderDTO.getAllotOrderItemDTOList().stream().mapToInt(AllotOrderItemDTO::getAllotCount).sum());
        allotOrder.setCreateTime(LocalDateTime.now());
        allotOrder.setUpdateTime(LocalDateTime.now());
        return allotOrder;
    }

    /**
     * 检查订单状态
     * @param allotOrderDTO 调拨订单dto
     */
    private AllotOrderVO checkOrderStatus(AllotOrderDTO allotOrderDTO) {
        AllotOrderVO dbAllotOrder = allotOrderMapper.getAllotOrder(allotOrderDTO);
        if (Objects.isNull(dbAllotOrder)) {
            throw new LuckException("调拨订单不存在");
        }
        if (Objects.equals(dbAllotOrder.getStatus(), AllotOrderStatusEnum.VOIDED.getNum())) {
            throw new LuckException("调拨订单已作废");
        }
        if (Objects.equals(dbAllotOrder.getStatus(), AllotOrderStatusEnum.COMPLETION.getNum())) {
            throw new LuckException("调拨订单已完成");
        }
        return dbAllotOrder;
    }

    /**
     * 获取库存点类型
     * @param warehouseSysType 库存点系统类型
     * @return 库存点类型
     */
    private Integer getStockPointType(Integer warehouseSysType) {
        // 系统类型为门店，库存点则为门店，其余都是仓库
        return Objects.equals(warehouseSysType, SysTypeEnum.STATION.value()) ? StockPointType.STATION.getValue() : StockPointType.WAREHOUSE.getValue();
    }

    /**
     * 补充调拨商品信息
     * @param allotOrderItemVOList 调拨商品集合
     */
    private void fillAllotItemInfo(List<AllotOrderItemVO> allotOrderItemVOList) {
        if (CollectionUtils.isEmpty(allotOrderItemVOList)) {
            return;
        }
        // 补充spu和sku名称
        Set<Long> spuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        Set<Long> skuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderItemVO allotOrderItemVO : allotOrderItemVOList) {
            spuIds.add(allotOrderItemVO.getSpuId());
            skuIds.add(allotOrderItemVO.getSkuId());
        }
        List<SpuVO> spuVOList = spuFeignClient.listSpuBySpuIds(new ArrayList<>(spuIds)).getData();
        List<SkuVO> skuVOList = skuFeignClient.listBySkuIds(new ArrayList<>(skuIds));
        Map<Long, SpuVO> spuMap = spuVOList.stream().collect(Collectors.toMap(SpuVO::getSpuId, x -> x));
        Map<Long, SkuVO> skuMap = skuVOList.stream().collect(Collectors.toMap(SkuVO::getSkuId, x -> x));
        for (AllotOrderItemVO allotOrderItemVO : allotOrderItemVOList) {
            SpuVO spuVO = spuMap.get(allotOrderItemVO.getSpuId());
            if (!Objects.isNull(spuVO)) {
                allotOrderItemVO.setSpuLangVOList(spuVO.getSpuLangList());
                allotOrderItemVO.setMainImgUrl(spuVO.getMainImgUrl());
            }
            SkuVO skuVO = skuMap.get(allotOrderItemVO.getSkuId());
            if (!Objects.isNull(skuVO)) {
                allotOrderItemVO.setPartyCode(skuVO.getPartyCode());
                allotOrderItemVO.setSkuLangVOList(skuVO.getSkuLangList());
                allotOrderItemVO.setImgUrl(skuVO.getImgUrl());
            }
        }
    }

    /**
     * 构建sku库存VO
     * @param allotOrderItemDTO 调拨订单项
     * @param warehouseId 仓库id
     * @param luaOperateEnum lua库存脚本操作
     * @return sku库存VO
     */
    private SkuStockVO buildSkuStockVO(AllotOrderItemDTO allotOrderItemDTO,
                                       Long warehouseId,
                                       LuaOperateEnum luaOperateEnum) {
        SkuStockVO skuStockVO = new SkuStockVO();
        BeanUtils.copyProperties(allotOrderItemDTO, skuStockVO);
        skuStockVO.setStockPointId(warehouseId);
        skuStockVO.setStock(allotOrderItemDTO.getInboundCount());
        skuStockVO.setOperateType(luaOperateEnum.value());
        return skuStockVO;
    }

    /**
     * 构建库存流水VO集合
     * @param allotOrderDTO 调拨订单dto
     * @param warehouseId 仓库id
     * @param stockPointType 库存点类型
     * @param stockBillType 库存流水类型
     * @return 库存流水VO集合
     */
    private StockBillLogVO buildStockBillLog(AllotOrderDTO allotOrderDTO,
                                              Long warehouseId,
                                              Integer stockPointType,
                                              StockBillType stockBillType) {
        // 商品数据准备
        Set<Long> spuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        Set<Long> skuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderDTO.getAllotOrderItemDTOList()) {
            spuIds.add(allotOrderItemDTO.getSpuId());
            skuIds.add(allotOrderItemDTO.getSkuId());
        }
        List<SpuVO> spuVOList = spuFeignClient.listSpuBySpuIds(new ArrayList<>(spuIds)).getData();
        List<SkuVO> skuVOList = skuFeignClient.listBySkuIds(new ArrayList<>(skuIds));
        Map<Long, SpuVO> spuMap = spuVOList.stream().collect(Collectors.toMap(SpuVO::getSpuId, x -> x));
        Map<Long, SkuVO> skuMap = skuVOList.stream().collect(Collectors.toMap(SkuVO::getSkuId, x -> x));
        // 获取库存入库单号
        boolean isOut = Objects.equals(stockBillType, StockBillType.ALLOT_OUT);
        StockBillLogVO stockBillLog = new StockBillLogVO();
        stockBillLog.setSourceOrderNo(allotOrderDTO.getAllotOrderId().toString());
        stockBillLog.setStockBillType(stockBillType.value());
        stockBillLog.setBusinessTime(new Date());
        stockBillLog.setChangeStock(false);
        stockBillLog.setRemark(allotOrderDTO.getRemark());
        if (Objects.equals(allotOrderDTO.getSysType(), SysTypeEnum.MULTISHOP.value())) {
            stockBillLog.setShopId(allotOrderDTO.getShopId());
        } else {
            stockBillLog.setSupplierId(allotOrderDTO.getShopId());
        }
        stockBillLog.setEmployeeId(allotOrderDTO.getUserId());
        stockBillLog.setType(isOut ? StockType.OUT_OF_STOCK.value() : StockType.WAREHOUSING.value());
        stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
        stockBillLog.setStockPointId(warehouseId);
        stockBillLog.setStockPointType(stockPointType);
        long totalAmount = 0L;
        int totalCount = 0;
        List<StockBillLogItemVO> stockBillLogItems = new ArrayList<>(Constant.INITIAL_CAPACITY);
        StockBillLogItemVO stockBillLogItem;
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderDTO.getAllotOrderItemDTOList()) {
            stockBillLogItem = this.buildStockBillLogItem(allotOrderItemDTO, spuMap, skuMap);
            stockBillLogItems.add(stockBillLogItem);
            totalCount += stockBillLogItem.getStockCount();
            totalAmount += stockBillLogItem.getStockCount() * stockBillLogItem.getUnitPrice();
        }
        stockBillLog.setTotalAmount(totalAmount);
        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setStockBillLogItems(stockBillLogItems);
        return stockBillLog;
    }

    /**
     * 构建库存流水项VO
     * @param allotOrderItemDTO 调拨订单dto
     * @param spuMap 商品spuMap
     * @param skuMap 商品skuMap
     * @return 库存流水项VO
     */
    private StockBillLogItemVO buildStockBillLogItem(AllotOrderItemDTO allotOrderItemDTO,
                                                     Map<Long, SpuVO> spuMap,
                                                     Map<Long, SkuVO> skuMap) {
        SkuVO skuVO = skuMap.get(allotOrderItemDTO.getSkuId());
        SpuVO spuVO = spuMap.get(allotOrderItemDTO.getSpuId());
        StockBillLogItemVO stockBillLogItem = new StockBillLogItemVO();
        stockBillLogItem.setStockCount(allotOrderItemDTO.getInboundCount());
        stockBillLogItem.setSpuId(spuVO.getSpuId());
        stockBillLogItem.setSkuId(skuVO.getSkuId());
        stockBillLogItem.setSpuName(spuVO.getName());
        stockBillLogItem.setSkuName(skuVO.getSkuName());
        stockBillLogItem.setPartyCode(skuVO.getPartyCode());
        stockBillLogItem.setMainImgUrl(spuVO.getMainImgUrl());
        stockBillLogItem.setImgUrl(skuVO.getImgUrl());
        stockBillLogItem.setUnitPrice(skuVO.getPriceFee());
        return stockBillLogItem;
    }

    /**
     * 构建调拨商品数据
     * @param allotSpuExcelVO 调拨商品excel数据
     * @param spuVO 商品spuVO
     * @param skuVO 商品skuVO
     * @param skuStockMap 商品库存映射
     * @param outWarehouseId 出库仓id
     * @param inWarehouseId 入库仓id
     * @return 调拨商品
     */
    private AllotSpuVO buildAllotSpuVO(AllotSpuExcelVO allotSpuExcelVO,
                                       SpuVO spuVO,
                                       SkuVO skuVO,
                                       Map<String, SkuStockVO> skuStockMap,
                                       Long outWarehouseId,
                                       Long inWarehouseId) {
        if (Objects.isNull(spuVO)
                || Objects.isNull(skuVO)
                || Objects.isNull(allotSpuExcelVO)) {
            return null;
        }
        AllotSpuVO allotSpuVO = new AllotSpuVO();
        allotSpuVO.setPartyCode(allotSpuExcelVO.getPartyCode());
        allotSpuVO.setAllotCount(allotSpuExcelVO.getAllotCount());
        allotSpuVO.setSkuId(skuVO.getSkuId());
        allotSpuVO.setImgUrl(skuVO.getImgUrl());
        allotSpuVO.setSkuLangVOList(skuVO.getSkuLangList());
        allotSpuVO.setSpuId(spuVO.getSpuId());
        allotSpuVO.setMainImgUrl(spuVO.getMainImgUrl());
        allotSpuVO.setSpuLangVOList(spuVO.getSpuLangList());
        SkuStockVO skuStockVO;
        allotSpuVO.setOutWarehouseStock(Objects.isNull((skuStockVO = skuStockMap.get(allotSpuVO.getSkuId() + "_" + outWarehouseId))) ? 0 : skuStockVO.getStock());
        allotSpuVO.setInWarehouseStock(Objects.isNull((skuStockVO = skuStockMap.get(allotSpuVO.getSkuId() + "_" + inWarehouseId))) ? 0 : skuStockVO.getStock());
        // 更新调拨数量（负数改为0，超出库存改为当前库存）
        if (allotSpuVO.getAllotCount() < 0) {
            allotSpuVO.setAllotCount(0);
        } else if (allotSpuVO.getAllotCount() > allotSpuVO.getOutWarehouseStock()) {
            allotSpuVO.setAllotCount(allotSpuVO.getOutWarehouseStock());
        }
        return allotSpuVO;
    }

    /**
     * 构建商品入库excelVO
     * @param allotOrderItem 调拨订单商品
     * @param seq 序号
     * @return 商品入库excelVO
     */
    private AllotSpuInboundExcelVO buildInboundExcelVO(AllotOrderItemVO allotOrderItem, Integer seq) {
        AllotSpuInboundExcelVO inboundExcel = new AllotSpuInboundExcelVO();
        BeanUtils.copyProperties(allotOrderItem, inboundExcel);
        inboundExcel.setSeq(seq);
        inboundExcel.setSpuName(allotOrderItem.getSpuLangVOList().get(0).getSpuName());
        inboundExcel.setSkuName(CollectionUtils.isEmpty(allotOrderItem.getSkuLangVOList()) ? "" : allotOrderItem.getSkuLangVOList().get(0).getSkuName());
        inboundExcel.setCanInboundCount(allotOrderItem.getAllotCount() - allotOrderItem.getAllotInCount());
        inboundExcel.setInboundCount(inboundExcel.getCanInboundCount());
        return inboundExcel;
    }

    /**
     * 库存锁定
     * @param skuStockLocks sku库存锁定类
     */
    private void lockStock(List<SkuStockLockDTO> skuStockLocks) {
        String result = skuStockChangeFeignClient.lockAllotOrder(skuStockLocks);
        if (result.contains(Constant.UNDERLINE)) {
            return;
        }
        // 提示商品库存不足
        long skuId = Long.parseLong(result);
        SkuVO skuVO = skuFeignClient.getSkuAndNameById(skuId);
        throw new LuckException(String.format("sku：【%s】 的库存不足", skuVO.getSkuName()));
    }

    /**
     * 作废操作
     * @param allotOrderDTO 调拨订单dto
     */
    private void nullify(AllotOrderDTO allotOrderDTO) {
        // 退还库存
        List<SkuStockVO> skuStockVOList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderDTO.getAllotOrderItemDTOList()) {
            allotOrderItemDTO.setInboundCount(allotOrderItemDTO.getAllotCount() - allotOrderItemDTO.getAllotInCount());
            skuStockVOList.add(this.buildSkuStockVO(allotOrderItemDTO, allotOrderDTO.getOutWarehouseId(), LuaOperateEnum.SKU_ADD));
        }
        skuStockFeignClient.changeSkuStock(skuStockVOList);
        // 新增入库库存流水
        StockBillLogVO stockBillLog = this.buildStockBillLog(allotOrderDTO, allotOrderDTO.getOutWarehouseId(), allotOrderDTO.getOutStockPointType(), StockBillType.ORDER_CANCEL);
        TransactionSendResult transactionSendResult = stockBillLogPurchaseStorageTemplate.sendMessageInTransaction(RocketMqConstant.STOCK_BILL_LOG_PURCHASE_STORAGE_TOPIC, new GenericMessage<>(stockBillLog), stockBillLog);
        if (!Objects.equals(transactionSendResult.getLocalTransactionState(), LocalTransactionState.COMMIT_MESSAGE)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        // 更新订单状态
        allotOrderDTO.setStatus(AllotOrderStatusEnum.VOIDED.getNum());
        this.updateAllotOrder(allotOrderDTO);
    }
}
