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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.auth.constant.SysTypeEnum;
import com.tmerclub.cloud.api.auth.feign.AccountFeignClient;
import com.tmerclub.cloud.api.auth.vo.AuthAccountVO;
import com.tmerclub.cloud.api.delivery.feign.StationFeignClient;
import com.tmerclub.cloud.api.delivery.vo.StationVO;
import com.tmerclub.cloud.common.order.vo.WarehouseVO;
import com.tmerclub.cloud.api.product.bo.SpuSimpleBO;
import com.tmerclub.cloud.api.product.dto.StockBillLogItemDTO;
import com.tmerclub.cloud.api.product.util.ProductLangUtil;
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.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.util.PageUtil;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.i18n.I18nMessage;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.order.bo.RefundReductionStockBO;
import com.tmerclub.cloud.common.order.bo.RefundStockOperateBO;
import com.tmerclub.cloud.common.order.constant.StockModeEnum;
import com.tmerclub.cloud.common.product.constant.*;
import com.tmerclub.cloud.common.product.dto.SkuDTO;
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.security.AuthUserContext;
import com.tmerclub.cloud.common.util.AnalysisUtil;
import com.tmerclub.cloud.common.util.Arith;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.product.bo.mongo.MongoStockBillLogBO;
import com.tmerclub.cloud.product.bo.mongo.MongoStockBillLogItemBO;
import com.tmerclub.cloud.product.constant.DistributedIdKey;
import com.tmerclub.cloud.product.constant.ProdKeyType;
import com.tmerclub.cloud.product.dto.StockBillLogDTO;
import com.tmerclub.cloud.product.dto.StockChangeReasonDTO;
import com.tmerclub.cloud.api.product.dto.WarehouseDTO;
import com.tmerclub.cloud.product.manager.StockManager;
import com.tmerclub.cloud.product.mapper.SkuMapper;
import com.tmerclub.cloud.product.mapper.SpuMapper;
import com.tmerclub.cloud.product.model.StockBillLog;
import com.tmerclub.cloud.product.model.StockBillLogItem;
import com.tmerclub.cloud.product.service.StockBillLogService;
import com.tmerclub.cloud.product.service.StockChangeReasonService;
import com.tmerclub.cloud.product.service.StockPointSkuService;
import com.tmerclub.cloud.product.service.WarehouseService;
import com.tmerclub.cloud.product.util.StockUtil;
import com.tmerclub.cloud.product.vo.StockBillLogExcelVO;
import com.tmerclub.cloud.product.vo.StockChangeReasonVO;
import com.tmerclub.cloud.product.vo.mongo.MongoStockBillLogItemVO;
import com.tmerclub.cloud.product.vo.mongo.MongoStockBillLogVO;
import com.mongodb.client.result.UpdateResult;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 出入库明细
 *
 * @author lhd
 * @date 2022-06-22 16:57:26
 */
@Service
public class StockBillLogServiceImpl implements StockBillLogService {
    private static final Logger logger = LoggerFactory.getLogger(StockBillLogServiceImpl.class);

    @Autowired
    private StockManager stockManager;
    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private SpuMapper spuMapper;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private SegmentManager segmentManager;
    @DubboReference
    private AccountFeignClient accountFeignClient;
    @Autowired
    private WarehouseService warehouseService;

    @Autowired
    private StockChangeReasonService stockChangeReasonService;

    @Autowired
    private StockPointSkuService stockPointSkuService;
    @Autowired
    private StationFeignClient stationFeignClient;

    @Value("${tmerclub.expose.operation.auth:}")
    private Boolean permission;

    @Override
    public PageVO<MongoStockBillLogBO> page(PageDTO pageDTO, StockBillLogDTO stockBillLogDTO) {
        // 查询条件
        Criteria criteria = loadStockBillLogItemQuery(stockBillLogDTO);

        // 返回字段
        ProjectionOperation projectionOperation = Aggregation.project().andExclude("stockBillItemLogs");
        // 排序
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "createTime").and(Sort.Direction.DESC, "_id");
        // 分页数据
        SkipOperation skipOperation = Aggregation.skip((long) pageDTO.getPageSize() * (pageDTO.getPageNum() - 1));
        LimitOperation limitOperation = Aggregation.limit(pageDTO.getPageSize());

        TypedAggregation<MongoStockBillLogBO> typedAggregation = Aggregation.newAggregation(
                MongoStockBillLogBO.class,
                Aggregation.match(criteria),
                projectionOperation,
                sortOperation,
                skipOperation,
                limitOperation
        );
        logger.info("pageStockLog()的mongodb查询语句： {}", typedAggregation);

        GroupOperation countGroupOperation = Aggregation.group().count().as("totalCount");
        // 执行聚合操作获取当前页数据
        AggregationResults<MongoStockBillLogBO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoStockBillLogBO.class);
        // 总条数
        TypedAggregation<MongoStockBillLogBO> totalTypedAggregation = Aggregation.newAggregation(
                MongoStockBillLogBO.class,
                Aggregation.match(criteria),
                countGroupOperation
        );
        AggregationResults<MongoStockBillLogBO> totalAggregationResults = mongoTemplate.aggregate(totalTypedAggregation, MongoStockBillLogBO.class);
        List<MongoStockBillLogBO> countStockList = totalAggregationResults.getMappedResults();
        long size = 0L;
        if (CollUtil.isNotEmpty(countStockList)) {
            size = countStockList.get(0).getTotalCount();
        }
        // 返回查询结果
        List<MongoStockBillLogBO> mappedResults = aggregationResults.getMappedResults();
        Set<Long> employeeIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        Set<Long> warehouseIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        for (MongoStockBillLogBO mongoStockBillLogBO : mappedResults) {
            employeeIds.add(mongoStockBillLogBO.getEmployeeId());
            warehouseIds.add(mongoStockBillLogBO.getStockPointId());
        }
        // 获取制单人Map
        List<AuthAccountVO> accounts = accountFeignClient.listByUserIdsAndPhoneAndType(new ArrayList<>(employeeIds), null, SysTypeEnum.MULTISHOP.value()).getData();
        Map<Long, AuthAccountVO> userMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (!CollectionUtils.isEmpty(accounts)) {
            userMap = accounts.stream().collect(Collectors.toMap(AuthAccountVO::getUserId, x -> x));
        }
        // 获取仓库Map
        WarehouseDTO warehouseDTO = new WarehouseDTO();
        warehouseDTO.setWarehouseIds(new ArrayList<>(warehouseIds));
        List<WarehouseVO> warehouses = warehouseService.listWarehouse(warehouseDTO);
        Map<Long, String> warehouseMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (!CollectionUtils.isEmpty(warehouses)) {
            warehouseMap = warehouses.stream().collect(Collectors.toMap(WarehouseVO::getWarehouseId, WarehouseVO::getWarehouseName));
        }
        //出入库原因
        StockChangeReasonDTO stockChangeReasonDTO = new StockChangeReasonDTO();
        stockChangeReasonDTO.setShopId(AuthUserContext.get().getTenantId());
        List<StockChangeReasonVO> reasonList = stockChangeReasonService.list(stockChangeReasonDTO);
        Map<Long, String> reasonMap = reasonList.stream().collect(Collectors.toMap(StockChangeReasonVO::getStockChangeReasonId, StockChangeReasonVO::getReason));
        // 数据补充
        AuthAccountVO accountVO;
        for (MongoStockBillLogBO mongoStockBillLogBO : mappedResults) {
            // 补充制单人信息
            if (!Objects.isNull(accountVO = userMap.get(mongoStockBillLogBO.getEmployeeId()))) {
                mongoStockBillLogBO.setMakerName(accountVO.getUsername());
                mongoStockBillLogBO.setMakerMobile(accountVO.getPhone());
            }
            // 补充库存点信息
            mongoStockBillLogBO.setStockPointName(warehouseMap.get(mongoStockBillLogBO.getStockPointId()));
            mongoStockBillLogBO.setStockPointType(mongoStockBillLogBO.getStockPointType() == null ? StockPointType.WAREHOUSE.getValue() : mongoStockBillLogBO.getStockPointType());
            // 补充出入库原因
            mongoStockBillLogBO.setReason(reasonMap.getOrDefault(mongoStockBillLogBO.getStockChangeReasonId(), ""));
            if (StrUtil.isNotBlank(mongoStockBillLogBO.getMakerMobile())) {
                mongoStockBillLogBO.setMakerMobile(PhoneUtil.hideBetween(mongoStockBillLogBO.getMakerMobile()).toString());
            }
        }
        // 构建分页数据
        PageVO<MongoStockBillLogBO> mongoStockBillLogPage = new PageVO<>();
        mongoStockBillLogPage.setTotal(size);
        mongoStockBillLogPage.setList(mappedResults);
        mongoStockBillLogPage.setPages(PageUtil.getPages(size, pageDTO.getPageSize()));
        return mongoStockBillLogPage;
    }

    private Criteria loadStockBillLogItemQuery(StockBillLogDTO stockBillLogDTO) {
        Criteria criteria = new Criteria();
        if (Objects.nonNull(stockBillLogDTO.getShopId())) {
            criteria.and("shopId").is(stockBillLogDTO.getShopId());
        }
        if (Objects.nonNull(stockBillLogDTO.getSupplierId())) {
            criteria.and("supplierId").is(stockBillLogDTO.getSupplierId());
        }
        SpuSimpleBO spuSimpleBO = new SpuSimpleBO();
        spuSimpleBO.setLang(I18nMessage.getLang());
        if (StrUtil.isNotBlank(stockBillLogDTO.getProdKey())) {
            if (Objects.equals(stockBillLogDTO.getProdKeyType(), ProdKeyType.SPU_NAME.value())) {
                // 商品名称
                spuSimpleBO.setSpuName(stockBillLogDTO.getProdKey());
                List<SpuSimpleBO> spuList = spuMapper.listSimple(spuSimpleBO);
                List<Long> spuIds = spuList.stream().map(SpuSimpleBO::getSpuId).collect(Collectors.toList());
                criteria.and("stockBillItemLogs.spuId").in(spuIds);
            } else if (Objects.equals(stockBillLogDTO.getProdKeyType(), ProdKeyType.PARTY_CODE.value())) {
                logger.info("通过商品编码查询库存流水，商品编码：{}", stockBillLogDTO.getProdKey());
                // 商品编码
                List<SkuVO> skuVoList = skuMapper.listSkuByPartyCodeAndShopId(stockBillLogDTO.getProdKey(), stockBillLogDTO.getShopId(), stockBillLogDTO.getSupplierId());
                // skuId
                List<Long> skuIdList = skuVoList.stream().map(SkuVO::getSkuId).collect(Collectors.toList());
                // 供应商skuId
                List<Long> supplierSkuIdList = skuVoList.stream().map(SkuVO::getSupplierSkuId).toList();
                skuIdList.addAll(supplierSkuIdList);
                criteria.and("stockBillItemLogs.skuId").in(skuIdList);
            }
        }
        if (Objects.nonNull(stockBillLogDTO.getCreateStartTime()) && Objects.nonNull(stockBillLogDTO.getCreateEndTime())) {
            criteria.and("createTime").lte(stockBillLogDTO.getCreateEndTime()).gte(stockBillLogDTO.getCreateStartTime());
        }
        if (Objects.nonNull(stockBillLogDTO.getBusinessStartTime()) && Objects.nonNull(stockBillLogDTO.getBusinessEndTime())) {
            criteria.and("businessTime").lte(stockBillLogDTO.getBusinessEndTime()).gte(stockBillLogDTO.getBusinessStartTime());
        }
        if (Objects.nonNull(stockBillLogDTO.getSourceOrderNo()) && !Objects.equals(stockBillLogDTO.getSourceOrderNo(),"")) {
            // 模糊查询只支持字段属性是字符串
            criteria.and("sourceOrderNo").regex(Pattern.compile("^.*" + stockBillLogDTO.getSourceOrderNo() + ".*$", Pattern.CASE_INSENSITIVE));
        }
        if (StrUtil.isNotBlank(stockBillLogDTO.getStockBillNo())) {
            criteria.and("stockBillNo").regex(Pattern.compile("^.*" + stockBillLogDTO.getStockBillNo() + ".*$", Pattern.CASE_INSENSITIVE));
        }
        if (Objects.nonNull(stockBillLogDTO.getStockBillType()) && !Objects.equals(stockBillLogDTO.getStockBillType(), 0)) {
            criteria.and("stockBillType").is(stockBillLogDTO.getStockBillType());
        }
        if (Objects.nonNull(stockBillLogDTO.getType())) {
            criteria.and("type").is(stockBillLogDTO.getType());
        }
        if (Objects.nonNull(stockBillLogDTO.getStatus())) {
            criteria.and("status").is(stockBillLogDTO.getStatus());
        }
        if (StrUtil.isNotBlank(stockBillLogDTO.getMakerMobile())) {
            criteria.and("makerMobile").is(stockBillLogDTO.getMakerMobile());
        }
        if (Objects.nonNull(stockBillLogDTO.getEmployeeId())) {
            criteria.and("employeeId").is(stockBillLogDTO.getEmployeeId());
        }
        if (CollUtil.isNotEmpty(stockBillLogDTO.getStockBillLogIds())) {
            criteria.and("stockBillLogId").in(stockBillLogDTO.getStockBillLogIds());
        }
        if (!ObjectUtils.isEmpty(stockBillLogDTO.getStockPointName())) {
            WarehouseDTO warehouseDTO = new WarehouseDTO();
            warehouseDTO.setWarehouseName(stockBillLogDTO.getStockPointName());
            List<WarehouseVO> warehouse = warehouseService.listWarehouse(warehouseDTO);
            if (!CollectionUtils.isEmpty(warehouse)) {
                List<Long> warehouseIds = warehouse.stream().map(WarehouseVO::getWarehouseId).toList();
                criteria.and("stockPointId").in(warehouseIds);
            }
        }
        return criteria;
    }

    @Override
    public MongoStockBillLogVO getByStockBillLogId(Long stockBillLogId) {
        MongoStockBillLogBO stockBillLogDb = mongoTemplate.findById(stockBillLogId, MongoStockBillLogBO.class);
        if (Objects.isNull(stockBillLogDb)) {
            return null;
        }
        MongoStockBillLogVO mongoStockBillLogVO = BeanUtil.map(stockBillLogDb, MongoStockBillLogVO.class);
        List<Long> spuIds = mongoStockBillLogVO.getStockBillItemLogs().stream().map(MongoStockBillLogItemVO::getSpuId).toList();
        List<SpuVO> spuList = spuMapper.listSpuBySpuIds(spuIds);
        Map<Long, SpuVO> spuMap = spuList.stream().collect(Collectors.toMap(SpuVO::getSpuId, spuVO -> spuVO));
        List<SkuVO> skuVOS = skuMapper.listBySpuIds(spuIds);
        Map<Long, String> skuMap = new HashMap<>(skuVOS.size());
        if (!CollectionUtils.isEmpty(skuVOS)) {
            skuVOS.forEach( s-> {
                skuMap.put(s.getSkuId(), Objects.isNull(s.getImgUrl()) || Objects.equals(s.getImgUrl(), "") ? s.getMainImgUrl() : s.getImgUrl());
            });
        }
        if (Objects.nonNull(stockBillLogDb.getStockPointId())) {
            // 补充库存点名称
            WarehouseVO warehouse = warehouseService.getByWarehouseId(stockBillLogDb.getStockPointId());
            if (!Objects.isNull(warehouse)) {
                mongoStockBillLogVO.setStockPointName(warehouse.getWarehouseName());
            }
        } else {
            mongoStockBillLogVO.setStockPointName("默认仓库");
            mongoStockBillLogVO.setStockPointType(StockPointType.WAREHOUSE.getValue());
        }
        // 补充出入库原因
        if (!Objects.isNull(mongoStockBillLogVO.getStockChangeReasonId())) {
            StockChangeReasonVO changeReason = stockChangeReasonService.getByStockChangeReasonId(mongoStockBillLogVO.getStockChangeReasonId());
            mongoStockBillLogVO.setReason(Objects.isNull(changeReason) ? null : changeReason.getReason());
        }
        // 处理商品信息
        for (MongoStockBillLogItemVO mongoStockBillLogItemVO : mongoStockBillLogVO.getStockBillItemLogs()) {
            SpuVO spu = spuMap.get(mongoStockBillLogItemVO.getSpuId());
            String mainImgUrl = skuMap.getOrDefault(mongoStockBillLogItemVO.getSkuId(), spu.getMainImgUrl());
            mongoStockBillLogItemVO.setMainImgUrl(mainImgUrl);
        }
        return mongoStockBillLogVO;
    }

    @Override
    public List<StockBillLogExcelVO> exportStockBillLogList(StockBillLogDTO stockBillLogDTO) {
        // 查询条件
        Criteria criteria = loadStockBillLogItemQuery(stockBillLogDTO);
        // 返回字段
        ProjectionOperation projectionOperation = Aggregation.project("stockBillLogId", "stockBillNo", "sourceOrderNo", "stockBillType",
                "createTime", "updateTime", "stockChangeReasonId", "employeeId", "status", "businessTime","stockPointId","stockPointType",
                "totalCount", "totalAmount", "qualifications", "shopId", "supplierId", "remark", "type", "makerMobile", "stockBillItemLogs");
        // 排序
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "createTime").and(Sort.Direction.DESC, "_id");

        TypedAggregation<MongoStockBillLogBO> typedAggregation = Aggregation.newAggregation(
                MongoStockBillLogBO.class,
                Aggregation.match(criteria),
                projectionOperation,
                sortOperation
        );
        logger.info("exportStockBillLogList()的mongodb查询语句： {}", Json.toJsonString(typedAggregation));
        // 执行聚合操作获取当前页数据
        AggregationResults<MongoStockBillLogVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoStockBillLogVO.class);
        List<MongoStockBillLogVO> mappedResults = aggregationResults.getMappedResults();

        for (MongoStockBillLogVO mongoStockBillLog : mappedResults) {
            if (StrUtil.isNotBlank(mongoStockBillLog.getMakerMobile())) {
                mongoStockBillLog.setMakerMobile(PhoneUtil.hideBetween(mongoStockBillLog.getMakerMobile()).toString());
            }
        }

        return handleExportStockBillLogListInfo(mappedResults, stockBillLogDTO.getType());
    }

    private List<StockBillLogExcelVO> handleExportStockBillLogListInfo(List<MongoStockBillLogVO> stockBillLogList, Integer billType) {
        List<StockBillLogExcelVO> stockBillLogExcelList = BeanUtil.mapAsList(stockBillLogList, StockBillLogExcelVO.class);
        Map<Long, MongoStockBillLogVO> stockBillLogItemMap = stockBillLogList.stream().collect(Collectors.toMap(MongoStockBillLogVO::getStockBillLogId, item -> item));
        List<Long> stationIds = stockBillLogList.stream().filter(stockBillLog -> Objects.equals(stockBillLog.getStockPointType(), StockPointType.STATION.getValue())).map(MongoStockBillLogVO::getStockPointId).collect(Collectors.toList());
        Map<Long, String> stationMap = stationFeignClient.listStationByIds(stationIds).stream().collect(Collectors.toMap(StationVO::getStationId, StationVO::getStationName));
        WarehouseDTO warehouseDTO = new WarehouseDTO();
        List<Long> warehouseIds = stockBillLogList.stream().filter(stockBillLog -> Objects.equals(stockBillLog.getStockPointType(), StockPointType.WAREHOUSE.getValue())).map(MongoStockBillLogVO::getStockPointId).collect(Collectors.toList());
        warehouseDTO.setWarehouseIds(warehouseIds);
        Map<Long, String> warehouseMap = warehouseService.listWarehouse(warehouseDTO).stream().collect(Collectors.toMap(WarehouseVO::getWarehouseId, WarehouseVO::getWarehouseName));
        for (StockBillLogExcelVO stockBillLogExcelVO : stockBillLogExcelList) {
            String statusName = stockBillLogExcelVO.getStatus() == 1
                    ? (billType == 1 ? "已出库" : "已入库")
                    : Objects.requireNonNull(StockBillStatus.instance(stockBillLogExcelVO.getStatus())).text();
            MongoStockBillLogItemVO mongoStockBillItemLogVO = stockBillLogItemMap.get(stockBillLogExcelVO.getStockBillLogId()).getStockBillItemLogs().get(0);
            stockBillLogExcelVO.setSkuName(mongoStockBillItemLogVO.getSkuName());
            stockBillLogExcelVO.setSpuName(mongoStockBillItemLogVO.getSpuName());
            stockBillLogExcelVO.setPartyCode(mongoStockBillItemLogVO.getPartyCode());
            stockBillLogExcelVO.setStockCount(mongoStockBillItemLogVO.getStockCount());
            stockBillLogExcelVO.setUnitPrice(AnalysisUtil.conversionPrices(mongoStockBillItemLogVO.getUnitPrice().toString()));
            stockBillLogExcelVO.setStockBillTypeName(Objects.requireNonNull(StockBillType.instance(stockBillLogExcelVO.getStockBillType())).text());
            stockBillLogExcelVO.setStatusName(statusName);
            stockBillLogExcelVO.setTotalAmount(AnalysisUtil.conversionPrices(stockBillLogItemMap.get(stockBillLogExcelVO.getStockBillLogId()).getTotalAmount().toString()));
            if (Objects.equals(stockBillLogItemMap.get(stockBillLogExcelVO.getStockBillLogId()).getStockPointType(), StockPointType.STATION.getValue())) {
                stockBillLogExcelVO.setStockPointType(StockPointType.STATION.getDesc());
                stockBillLogExcelVO.setStockPointName(stationMap.get(stockBillLogItemMap.get(stockBillLogExcelVO.getStockBillLogId()).getStockPointId()));
            } else {
                stockBillLogExcelVO.setStockPointType(StockPointType.WAREHOUSE.getDesc());
                stockBillLogExcelVO.setStockPointName(warehouseMap.get(stockBillLogItemMap.get(stockBillLogExcelVO.getStockBillLogId()).getStockPointId()));
            }
        }
        return stockBillLogExcelList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveInfo(StockBillLogVO stockBillLogVO) {
        StockType stockType = Objects.requireNonNull(StockType.instance(stockBillLogVO.getType()));
        // mq幂等处理
        if (Objects.nonNull(stockBillLogVO.getStockBillNo())) {
            String stockBillNoPre = Objects.equals(stockType.value(), 1) ? Constant.OB : Constant.IB;
            stockBillLogVO.setStockBillNo(stockBillNoPre + stockBillLogVO.getStockBillNo());
            Query query = new Query();
            query.addCriteria(Criteria.where("stockBillNo").is(stockBillLogVO.getStockBillNo()));
            if (Objects.nonNull(mongoTemplate.findOne(query, MongoStockBillLogBO.class))) {
                return;
            }
        }
        Date now = new Date();
        // 校验部分信息
        this.checkInfo(stockBillLogVO);
        stockBillLogVO.setCreateTime(now);
        stockBillLogVO.setUpdateTime(now);
        if (Objects.isNull(stockBillLogVO.getStockBillNo())) {
            // 没有id就生成一个
            stockBillLogVO.setStockBillNo(getStockBillNoByType(stockType));
        }
        List<SkuStockVO> changeStockList = Collections.emptyList();
        // 处理金额并对商品完成出入库操作 - 非采购入库操作
        if (!Objects.equals(stockBillLogVO.getStockBillType(), StockBillType.PURCHASE_STORAGE.value())
                && !Objects.equals(stockBillLogVO.getChangeStock(), false)) {
            changeStockList = this.processingAmountAndStock(stockBillLogVO);
        }
        MongoStockBillLogBO mongoStockBillLogBO = BeanUtil.map(stockBillLogVO, MongoStockBillLogBO.class);
        if (Objects.nonNull(stockBillLogVO.getSourceOrderNo())) {
            mongoStockBillLogBO.setSourceOrderNo(stockBillLogVO.getSourceOrderNo());
        }
        List<MongoStockBillLogItemBO> mongoStockBillLogItemList = BeanUtil.mapAsList(stockBillLogVO.getStockBillLogItems(), MongoStockBillLogItemBO.class);
        this.saveMongoStockBillLog(mongoStockBillLogBO, mongoStockBillLogItemList);
        if (CollUtil.isNotEmpty(changeStockList)) {
            stockManager.skuStock(changeStockList, null);
        }
    }

    @Override
    public void generateStockLogOfRefundOrderList(RefundStockOperateBO refundStockOperateBO) {
        List<RefundReductionStockBO> stockBillLogItems = refundStockOperateBO.getRefundReductionStockList();
        // 如果供应商商品 退款需要添加库存记录
//        if (Objects.equals(stockBillLogItems.get(0).getSupplierProd(), 1)) {
//            return;
//        }
        List<StockBillLogItemDTO> skuStockLockList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        Map<String, StockPointSkuVO> stockPointMap = new HashMap<>(stockBillLogItems.size());
        for (RefundReductionStockBO stockBillLogItem : stockBillLogItems) {
            String key = StockUtil.skuPointKey(stockBillLogItem.getSkuId(), stockBillLogItem.getStockPointId());
            if (stockPointMap.containsKey(key)) {
                continue;
            }
            StockPointSkuVO stockPointSkuVO = new StockPointSkuVO();
            stockPointSkuVO.setSkuId(stockBillLogItem.getSkuId());
            stockPointSkuVO.setStockPointId(stockBillLogItem.getStockPointId());
            stockPointMap.put(key, stockPointSkuVO);
        }
        // 获取sku区域库存
        Map<String, SkuStockVO> skuStockVOMap = stockManager.skuStockMap(new ArrayList<>(stockPointMap.values()));
        // 生成入库明细
        for (RefundReductionStockBO refundReductionStock : stockBillLogItems) {
            StockBillLogItemDTO skuStockLockDTO = new StockBillLogItemDTO(
                    refundReductionStock.getSpuId(),
                    refundReductionStock.getSkuId(),
                    refundReductionStock.getShopId(),
                    refundReductionStock.getOrderId(),
                    refundReductionStock.getSpuName(),
                    refundReductionStock.getSkuName(),
                    refundReductionStock.getCount(),
                    refundReductionStock.getPrice(),
                    refundReductionStock.getPartyCode(),
                    refundReductionStock.getPic()
            );
            skuStockLockDTO.setSupplierId(refundReductionStock.getSupplierId());
            skuStockLockDTO.setStockPointId(refundReductionStock.getStockPointId());
            SkuStockVO skuStockVO = skuStockVOMap.get(StockUtil.skuPointKey(skuStockLockDTO.getSkuId(), skuStockLockDTO.getStockPointId()));
            // 当前的sku库存减去sku退货数量
            skuStockLockDTO.setAfterStock(skuStockVO.getStock() - refundReductionStock.getCount());
            skuStockLockDTO.setStockBillLogId(refundStockOperateBO.getId());
            skuStockLockDTO.setStockBillType(StockBillType.RETURN_STORAGE.value());
            skuStockLockDTO.setStocktype(StockType.WAREHOUSING.value());
            skuStockLockList.add(skuStockLockDTO);
        }

        // 生成入库明细信息 - 操作成功才扣除库存，操作失败代表是空列表或者已经入库了不需要扣库存，如果报错会回滚
        StockBillLogService stockBillLogService = (StockBillLogService) AopContext.currentProxy();

        // 生成入库明细信息
        stockBillLogService.generateStockLogOfOrderList(skuStockLockList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateStockLogOfOrderList(List<StockBillLogItemDTO> stockBillLogItems) {
        if (CollUtil.isEmpty(stockBillLogItems)) {
            return;
        }
        Map<Long, List<StockBillLogItemDTO>> shopStockMap = stockBillLogItems.stream().collect(Collectors.groupingBy(StockBillLogItemDTO::getShopId));
        // 如果id为空，就生成新的id
        if (Objects.isNull(stockBillLogItems.get(0).getStockBillLogId())) {
            logger.info("生成sku出入库记录id， 订单号：{}， 数量：{}", stockBillLogItems.get(0).getOrderId(), shopStockMap.keySet().size());
            for (StockBillLogItemDTO stockBillLogItem : stockBillLogItems) {
                stockBillLogItem.setStockBillLogId(segmentManager.getSegmentId(DistributedIdKey.tmerclub_STOCK_BILL_LOG));
            }
        }

        Date now = new Date();
        // 根据店铺处理
        for (Long shopId : shopStockMap.keySet()) {
            // 如果没有出入库项，就不生成出入库单
            if (CollUtil.isEmpty(shopStockMap.get(shopId))) {
                continue;
            }
            List<StockBillLogItemDTO> stockBillLogItemDTOList = shopStockMap.get(shopId);
            Map<Integer, List<StockBillLogItemDTO>> stockTypeMap = stockBillLogItemDTOList.stream().collect(Collectors.groupingBy(StockBillLogItemDTO::getStocktype));
            // 分别处理出入库数据
            for (Map.Entry<Integer, List<StockBillLogItemDTO>>  typeMap :  stockTypeMap.entrySet()) {
                Integer stockType = typeMap.getKey();
                List<StockBillLogItemDTO> mapValue = typeMap.getValue();
                Map<Long, List<StockBillLogItemDTO>> pointMap = mapValue.stream().collect(Collectors.groupingBy(StockBillLogItemDTO::getStockPointId));
                // 根据库存点分别生成记录
                for (Map.Entry<Long, List<StockBillLogItemDTO>>  map :  pointMap.entrySet()) {
                    Long stockBillLogId = map.getValue().get(0).getStockBillLogId();
                    Integer stockBillType = map.getValue().get(0).getStockBillType();
                    StockBillLogItemDTO stockBillLogItem = map.getValue().get(0);
                    MongoStockBillLogBO stockBillLog = new MongoStockBillLogBO(
                            stockBillLogItem.getShopId(),
                            now,
                            stockBillType,
                            stockType,
                            getStockBillNoByType(Objects.requireNonNull(StockType.instance(stockType))),
                            StockBillStatus.SUCCESS.value(),
                            stockBillLogItem.getOrderId() != null ? stockBillLogItem.getOrderId().toString() : null
                    );
                    stockBillLog.setMakerMobile(stockBillLogItem.getMakerMobile());
                    stockBillLog.setStockPointId(map.getKey());
                    stockBillLog.setStockPointType(stockBillLogItem.getStockPointType());
                    stockBillLog.setSupplierId(stockBillLogItem.getSupplierId());
                    long totalAmount = 0L;
                    int totalCount = 0;
                    // 设置id
                    stockBillLog.setStockBillLogId(stockBillLogItem.getStockBillLogId());
                    for (StockBillLogItemDTO stockBillLogItemDTO : map.getValue()) {
                        // 计算总金额
                        totalAmount +=  stockBillLogItemDTO.getStockCount()  * stockBillLogItemDTO.getUnitPrice();
                        // 计算总数量
                        totalCount += stockBillLogItemDTO.getStockCount();
                    }
//                    if (totalCount == 0) {
//                        continue;
//                    }
                    stockBillLog.setTotalAmount(totalAmount);
                    stockBillLog.setTotalCount(totalCount);
                    List<MongoStockBillLogItemBO> mongoStockBillLogItemList = BeanUtil.mapAsList(map.getValue(), MongoStockBillLogItemBO.class);
                    try {
                        this.saveMongoStockBillLog(stockBillLog, mongoStockBillLogItemList);
                    } catch (DuplicateKeyException e) {
                        // 主键重复就说明数据已经插入了，不需要再重复操作
                        stockBillLog.setStockBillItemLogs(mongoStockBillLogItemList);
                        logger.info("generateStockLogOfOrderList-sku库存记录重复插入，stockBillLog：{}", stockBillLog);
                        return;
                    }
                }
            };
        }
    }

    @Override
    public void updateInfo(StockBillLogDTO stockBillLogDTO) {
        if (AuthUserContext.get().getSysType().equals(SysTypeEnum.MULTISHOP.value())) {
            stockBillLogDTO.setShopId(AuthUserContext.get().getTenantId());
        }
        if (AuthUserContext.get().getSysType().equals(SysTypeEnum.SUPPLIER.value())) {
            stockBillLogDTO.setSupplierId(AuthUserContext.get().getTenantId());
        }
        Date now = new Date();
        StockBillLogVO stockBillLogVO = BeanUtil.map(stockBillLogDTO, StockBillLogVO.class);
        this.checkInfo(stockBillLogVO);
        stockBillLogVO.setUpdateTime(now);

        // 库存项
        // 保存初始化商品项信息
        List<MongoStockBillLogItemBO> mongoStockBillLogItemList = Collections.emptyList();
        if (CollUtil.isNotEmpty(stockBillLogDTO.getStockBillLogItems())) {
            List<StockBillLogItem> stockBillLogItemList = stockBillLogDTO.getStockBillLogItems();
            // 更新的数据不一定完整，所以查询最新的库存信息
            List<MongoStockBillLogBO> stockBillLogList= mongoTemplate.find(new Query(Criteria.where("_id").is(stockBillLogDTO.getStockBillLogId())), MongoStockBillLogBO.class);
            if (CollUtil.isEmpty(stockBillLogList)) {
                throw new LuckException("该出入库单不存在");
            }
            MongoStockBillLogBO mongoStockBillLogBO = stockBillLogList.get(0);
            // 保存库存项信息
            mongoStockBillLogItemList = BeanUtil.mapAsList(stockBillLogItemList, MongoStockBillLogItemBO.class);
            for (MongoStockBillLogItemBO mongoStockBillLogItemBO : mongoStockBillLogItemList) {
                mongoStockBillLogItemBO.setCreateTime(now);
                mongoStockBillLogItemBO.setUpdateTime(now);
            }
            mongoStockBillLogItemList.addAll(mongoStockBillLogBO.getStockBillItemLogs());
        }


        List<SkuStockVO> changeStockList = this.processingAmountAndStock(stockBillLogVO);
        //更新出入库明细信息
        StockBillLog stockBillLog = BeanUtil.map(stockBillLogVO, StockBillLog.class);
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(stockBillLogDTO.getStockBillLogId()));
        Update update = this.getStockBillLogUpdateParam(stockBillLog, mongoStockBillLogItemList);
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, MongoStockBillLogBO.class);
        if (updateResult.getModifiedCount() < 1) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        stockManager.skuStock(changeStockList, null);
    }

    @Override
    public long countByOrderId(Long orderId, Integer type) {
        Query query = new Query(Criteria.where("sourceOrderNo").is(orderId.toString()).and("type").is(type)).limit(1);
        return mongoTemplate.count(query, MongoStockBillLogBO.class);
    }

    private Update getStockBillLogUpdateParam(StockBillLog stockBillLog, List<MongoStockBillLogItemBO> stockBillLogItemList) {
        Update update = new Update();
        if (Objects.nonNull(stockBillLog.getStockChangeReasonId())) {
            update.set("stockChangeReasonId", stockBillLog.getStockChangeReasonId());
        }
        if (Objects.nonNull(stockBillLog.getEmployeeId())) {
            update.set("employeeId", stockBillLog.getEmployeeId());
        }
        if (Objects.nonNull(stockBillLog.getStatus())) {
            update.set("status", stockBillLog.getStatus());
        }
        if (Objects.nonNull(stockBillLog.getBusinessTime())) {
            update.set("businessTime", stockBillLog.getBusinessTime());
        }
        if (Objects.nonNull(stockBillLog.getTotalCount())) {
            update.set("totalCount", stockBillLog.getTotalCount());
        }
        if (Objects.nonNull(stockBillLog.getTotalAmount())) {
            update.set("totalAmount", stockBillLog.getTotalAmount());
        }
        if (Objects.nonNull(stockBillLog.getQualifications())) {
            update.set("qualifications", stockBillLog.getQualifications());
        }
        if (Objects.nonNull(stockBillLog.getSupplierId())) {
            update.set("supplierId", stockBillLog.getSupplierId());
        }
        if (Objects.nonNull(stockBillLog.getRemark())) {
            update.set("remark", stockBillLog.getRemark());
        }
        if (Objects.nonNull(stockBillLog.getMakerMobile())) {
            update.set("makerMobile", stockBillLog.getMakerMobile());
        }
        if (CollUtil.isNotEmpty(stockBillLogItemList)) {
            update.set("stockBillLogItemList", stockBillLogItemList);
        }
        return update;
    }

    private void saveMongoStockBillLog(MongoStockBillLogBO mongoStockBillLogBO, List<MongoStockBillLogItemBO> stockBillLogItems) {
        Long stockBillLogId = mongoStockBillLogBO.getStockBillLogId();
        // 没有库存项id，需要去生成
        if (Objects.isNull(stockBillLogId)) {
            stockBillLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_STOCK_BILL_LOG);
        }

        Date now = new Date();
        mongoStockBillLogBO.setStockBillLogId(stockBillLogId);
        mongoStockBillLogBO.setStockBillItemLogs(stockBillLogItems);
        mongoStockBillLogBO.setCreateTime(now);
        mongoStockBillLogBO.setUpdateTime(now);

        for (MongoStockBillLogItemBO stockBillLogItem : stockBillLogItems) {
            stockBillLogItem.setCreateTime(now);
            stockBillLogItem.setUpdateTime(now);
        }

        // 不是供应商的库存记录，supplierId 为null
        if (Objects.equals(mongoStockBillLogBO.getSupplierId(), Constant.ZERO_LONG)) {
            mongoStockBillLogBO.setSupplierId(null);
        }

        // 保存初始化明细信息
        mongoTemplate.insert(mongoStockBillLogBO);

        // 更新es的商品库存和销量数据
        // 采购入库、退款入库、销售出库、订单取消  - 判断是否更新es商品的库存、销量
        // 库存初始化、编辑入库、其他入库、盘盈入库、编辑出库、其他出库、盘盈出库 - 直接更新es商品的库存、销量
    }


    /**
     * 校验出入库明细信息
     */
    private void checkInfo(StockBillLogVO stockBillLog) {
        if (CollUtil.isEmpty(stockBillLog.getStockBillLogItems())) {
            // 出入库商品项不能为空
            throw new LuckException("商品项不能为空");
        }
        if (Objects.nonNull(stockBillLog.getStockBillLogId())) {
            // 更新时校验
            MongoStockBillLogBO mongoStockBillLog = mongoTemplate.findById(stockBillLog.getStockBillLogId(), MongoStockBillLogBO.class);
            if (Objects.isNull(mongoStockBillLog)) {
                throw new LuckException("该出入库信息不存在");
            }
            if (AuthUserContext.get().getSysType().equals(SysTypeEnum.MULTISHOP.value())) {
                if (!Objects.equals(stockBillLog.getShopId(), mongoStockBillLog.getShopId())) {
                    throw new LuckException("当前出入库信息不属于你的店铺");
                }
            }
            if (AuthUserContext.get().getSysType().equals(SysTypeEnum.SUPPLIER.value())) {
                if (!Objects.equals(stockBillLog.getSupplierId(), mongoStockBillLog.getSupplierId())) {
                    throw new LuckException("当前出入库信息不属于你的店铺");
                }
            }
            if (!Objects.equals(mongoStockBillLog.getStatus(), StockBillStatus.AWAIT_SUBMIT.value())) {
                throw new LuckException("当前信息已完成或已作废，无法继续修改，请刷新页面");
            }
            stockBillLog.setStockBillNo(mongoStockBillLog.getStockBillNo());
            stockBillLog.setStockBillType(mongoStockBillLog.getStockBillType());
        }
        List<Long> spuIds= stockBillLog.getStockBillLogItems().stream().map(StockBillLogItemVO::getSpuId).collect(Collectors.toList());
        Map<Long, Integer> spuMap = spuMapper.listSpuBySpuIds(spuIds).stream().collect(Collectors.toMap(SpuVO::getSpuId, SpuVO::getSpuMold));
        for (StockBillLogItemVO stockBillLogItem : stockBillLog.getStockBillLogItems()) {
            // 判断非默认仓库且不是普通商品
            if (!Objects.equals(spuMap.get(stockBillLogItem.getSpuId()), SpuMold.REAL.value()) && !Objects.equals(stockBillLog.getWarehouseType(), 0)) {
                throw new LuckException("当前仓库不能添加该商品：" + stockBillLogItem.getSpuName());
            }
        }
    }

    private List<SkuStockVO> processingAmountAndStock(StockBillLogVO stockBillLog) {
        List<StockBillLogItemVO> stockBillLogItems = stockBillLog.getStockBillLogItems();
        Integer totalCount = 0;
        long totalAmount = 0L;
        List<Long> skuIds = stockBillLogItems.stream().map(StockBillLogItemVO::getSkuId).collect(Collectors.toList());
        Map<Long, List<StockBillLogItemVO>> listMap = stockBillLogItems.stream().collect(Collectors.groupingBy(StockBillLogItemVO::getSpuId));
        List<SkuStockVO> list = new ArrayList<>(stockBillLog.getStockBillLogItems().size());
        // TODO 获取sku区域库存
        Map<String, StockPointSkuVO> stockPointSkuMap = new HashMap<>(stockBillLogItems.size());
        for (StockBillLogItemVO stockBillLogItem : stockBillLogItems) {
            String key = StockUtil.skuPointKey(stockBillLogItem.getSkuId(), stockBillLogItem.getStockPointId());
            if (stockPointSkuMap.containsKey(key)) {
                continue;
            }
            stockPointSkuMap.put(key, new StockPointSkuVO(stockBillLogItem.getSkuId(), stockBillLogItem.getStockPointId()));
        }
        // 其他操作
        List<SkuVO> skuList = skuMapper.listSkuBySkuIds(skuIds);
        Map<Long, SkuVO> skuMap = skuList.stream().collect(Collectors.toMap(SkuVO::getSkuId, s -> s));
        List<StockPointSkuVO> stockPointSkuVOS = stockPointSkuService.listByPointId(stockBillLog.getStockPointId());
        Map<Long, StockPointSkuVO> stockPointSkuVOMap = stockPointSkuVOS.stream().collect(Collectors.toMap(StockPointSkuVO::getSkuId, s -> s));
        List<StockPointSkuDTO> addPointSkus = new ArrayList<>();
        // 获取sku总库存信息集合
        Map<String, SkuStockVO> skuStockVOMap = stockManager.skuStockMap(new ArrayList<>(stockPointSkuMap.values()));
        for (Long spuId : listMap.keySet()) {
            for (StockBillLogItemVO stockBillLogItem : listMap.get(spuId)) {
                // 其他出库或其他入库
                boolean isOther = (Objects.equals(stockBillLog.getStockBillType(), StockBillType.OTHER_STORAGE.value()) || Objects.equals(stockBillLog.getStockBillType(), StockBillType.OTHER_OUTBOUND.value())) && Objects.equals(stockBillLog.getStatus(), StockBillStatus.SUCCESS.value());
                int stock = 0;
                if (isOther) {
                    // 更新商品与sku的库存
                    stock = Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? -stockBillLogItem.getStockCount() : stockBillLogItem.getStockCount();
                    // skuId_采购库存数量,skuId_采购库存数量...
                    // int addOrSub = Objects.equals(stockBillLog.getStockBillType(), StockBillType.OTHER_STORAGE.value()) ? StockUtil.ADD : StockUtil.SUBTRACT;
                    //list.add(StockUtil.loadQueryArray(stockBillLogItem.getSkuId(), StockManager.WAREHOUSE_ID, stock, LuaOperateEnum.SKU_REPLACE));
                    SkuStockVO skuStockVO = new SkuStockVO(stockBillLogItem.getSkuId(), stock, stockBillLog.getStockPointId(), stockBillLog.getStockPointType());
                    // 判断该库存点是否有改商品数据
                    if (stockPointSkuVOMap.containsKey(stockBillLogItem.getSkuId())) {
                        // 有就修改
                        if (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value())) {
                            skuStockVO.setOperateType(LuaOperateEnum.SKU_SUB.value());
                        } else {
                            skuStockVO.setOperateType(LuaOperateEnum.SKU_ADD.value());
                        }
                        //list.add(StockUtil.loadQueryArray(stockBillLogItem.getSkuId(), stockBillLog.getStockPointId(), stock, LuaOperateEnum.SKU_ADD));
                    } else {
                        if (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value())) {
                            throw new LuckException("该库存点没有商品" + stockBillLogItem.getSpuName() + "的库存");
                        }
                        // 没有就添加
                        StockPointSkuDTO stockPointSkuDTO = new StockPointSkuDTO(
                                stockBillLog.getStockPointId(), stockBillLog.getStockPointType(),
                                stockBillLogItem.getSpuId(),stockBillLogItem.getSkuId(),
                                1, stockBillLogItem.getWarehouseType(), stockBillLogItem.getWarehouseType() == null ? StockModeEnum.SINGLE_STOCK.value() : null);
                        addPointSkus.add(stockPointSkuDTO);
                        skuStockVO.setOperateType(LuaOperateEnum.SKU_INSERT.value());
                        // list.add(StockUtil.loadQueryArray(stockBillLogItem.getSkuId(), stockBillLog.getStockPointId(), stock, LuaOperateEnum.SKU_INSERT));
                    }
                    list.add(skuStockVO);
                }
                SkuStockVO skuStockVO = skuStockVOMap.get(StockUtil.skuPointKey(stockBillLogItem.getSkuId(), stockBillLogItem.getStockPointId()));
                SkuVO skuDb = skuMap.get(stockBillLogItem.getSkuId());
                stockBillLogItem.setAfterStock(skuStockVO.getStock() + stock);
                // 更新计算后的库存，用于下一次的计算
                skuStockVO.setStock(stockBillLogItem.getAfterStock());
                // 设置默认单价
                stockBillLogItem.setUnitPrice(skuDb.getPriceFee());
                // 累计总金额
                totalAmount = (long) Arith.add(totalAmount, Arith.mul(stockBillLogItem.getStockCount().doubleValue(), skuDb.getPriceFee().doubleValue()));
                // 累计总数量
                totalCount += stockBillLogItem.getStockCount();
            }
        }
        stockPointSkuService.batchSave(addPointSkus);
        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setTotalAmount(totalAmount);
        return list;
    }


    /**
     * 根据类型生成出入库单号
     */
    private String getStockBillNoByType(StockType stockType) {
        // 出库
        if (Objects.equals(stockType.value(), 1)) {
            // 获取订单号
            return Constant.OB + segmentManager.getSegmentCode(DistributedIdKey.tmerclub_PRODUCT_STOCK_OUT);
        }
        // 入库
        else {
            // 获取订单号
            return Constant.IB + segmentManager.getSegmentCode(DistributedIdKey.tmerclub_PRODUCT_STOCK_IN);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void voided(Long shopId, Long stockBillLogId) {
        MongoStockBillLogBO stockBillLogBO = mongoTemplate.findById(stockBillLogId, MongoStockBillLogBO.class);
        if (Objects.isNull(stockBillLogBO) || !Objects.equals(stockBillLogBO.getStatus(), StockBillStatus.AWAIT_SUBMIT.value())) {
            throw new LuckException("当前数据不存在或状态已经发生改变，请刷新后重试");
        }
        if (!Objects.equals(shopId, Constant.PLATFORM_SHOP_ID)) {
            if (!Objects.equals(shopId, stockBillLogBO.getShopId()) && AuthUserContext.get().getSysType().equals(SysTypeEnum.MULTISHOP.value())){
                throw new LuckException("当前数据不属于你的店铺");
            }
            if (!Objects.equals(shopId, stockBillLogBO.getSupplierId()) && AuthUserContext.get().getSysType().equals(SysTypeEnum.SUPPLIER.value())){
                throw new LuckException("当前数据不属于你的店铺");
            }
        }
        StockBillLog stockBillLog = BeanUtil.map(stockBillLogBO, StockBillLog.class);
        stockBillLog.setStatus(StockBillStatus.ABOLISHED.value());
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(stockBillLog.getStockBillLogId()));
        Update update = this.getStockBillLogUpdateParam(stockBillLog, null);
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, MongoStockBillLogBO.class);
        if (updateResult.getModifiedCount() < 1) {
            throw new LuckException("操作失败，请刷新后重试");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void generateStockLogOfTakeStock(StockBillLog stockBillLog, List<SkuDTO> skuDTOList) {
        if (CollUtil.isEmpty(skuDTOList)) {
            return;
        }
        Date now = new Date();
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setBusinessTime(now);
        stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
        stockBillLog.setStockBillNo(getStockBillNoByType(Objects.requireNonNull(StockBillType.instance(stockBillLog.getStockBillType())).getStockType()));
        stockBillLog.setType(Objects.equals(stockBillLog.getStockBillType(), StockBillType.LOSS_OUTBOUND.value()) ? StockType.OUT_OF_STOCK.value() : StockType.WAREHOUSING.value());
        // 构建库存日志商品列表，并统计总数量和总金额
        List<MongoStockBillLogItemBO> stockBillLogItems = new ArrayList<>(skuDTOList.size());
        double totalAmount = 0.0;
        int totalCount = 0;
        if (CollUtil.isNotEmpty(skuDTOList)) {
            for (SkuDTO skuDTO : skuDTOList) {
                int changeStock = Math.abs(skuDTO.getChangeStock());
                MongoStockBillLogItemBO stockBillLogItem = this.getStockBillLogItem(skuDTO,changeStock);
                totalCount += changeStock;
                totalAmount = Arith.add(totalAmount, Arith.mul(skuDTO.getPriceFee(), changeStock));
                stockBillLogItems.add(stockBillLogItem);
            }
        }
        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setTotalAmount((long) totalAmount);
        // 保存初始化明细信息
        MongoStockBillLogBO mongoStockBillLogBO = new MongoStockBillLogBO();
        BeanUtils.copyProperties(stockBillLog, mongoStockBillLogBO);
        // 保存库存日志
        this.saveMongoStockBillLog(mongoStockBillLogBO, stockBillLogItems);
    }

    private MongoStockBillLogItemBO getStockBillLogItem(SkuDTO skuDTO, Integer changeStock) {
        MongoStockBillLogItemBO stockBillLogItem = new MongoStockBillLogItemBO();
        stockBillLogItem.setSpuId(skuDTO.getSpuId());
        stockBillLogItem.setSkuId(skuDTO.getSkuId());
        stockBillLogItem.setSpuName(skuDTO.getSpuName());
        stockBillLogItem.setSkuName(skuDTO.getSkuName());
        stockBillLogItem.setMainImgUrl(Objects.nonNull(skuDTO.getImgUrl()) ? skuDTO.getImgUrl() : skuDTO.getSpuMainImgUrl());
        stockBillLogItem.setUnitPrice(skuDTO.getPriceFee());
        stockBillLogItem.setStockCount(changeStock);
        stockBillLogItem.setAfterStock(skuDTO.getStock() + changeStock);
        stockBillLogItem.setPartyCode(skuDTO.getPartyCode());
        return stockBillLogItem;
    }

    @Override
    public void removeByOrderIds(List<Long> orderIds, Integer stockBillType) {
        Query query = new Query(Criteria.where("stockBillType").is(stockBillType).and("orderId").in(orderIds));
        List<MongoStockBillLogBO> mongoStockBillLogList = mongoTemplate.find(query, MongoStockBillLogBO.class);
        if (CollUtil.isEmpty(mongoStockBillLogList)) {
            return;
        }
        List<Long> ids = mongoStockBillLogList.stream().map(MongoStockBillLogBO::getStockBillLogId).collect(Collectors.toList());
        Query deleteQuery = new Query(Criteria.where("stockBillLogId").in(ids));
        mongoTemplate.remove(deleteQuery, MongoStockBillLogBO.class);
    }

    @Override
    public List<Long> listSpuIdOfStockChange(Date beginTime, Date endTime) {
        List<AggregationOperation> aggregationOperations = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (Objects.nonNull(beginTime) && Objects.nonNull(endTime)) {
            aggregationOperations.add(Aggregation.match(Criteria.where("createTime").gte(beginTime).lte(endTime)));
        }
        aggregationOperations.add(Aggregation.unwind("stockBillItemLogs"));

        aggregationOperations.add(Aggregation.group("stockBillItemLogs.spuId"));

        TypedAggregation<MongoStockBillLogBO> typedAggregation =
                Aggregation.newAggregation(
                        MongoStockBillLogBO.class,
                        aggregationOperations
                );
        logger.info("listSpuIdOfStockChange()的mongodb查询语句： {}", typedAggregation);

        AggregationResults<MongoStockBillLogBO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoStockBillLogBO.class);
        List<MongoStockBillLogBO> spuList = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(spuList)) {
            return Collections.emptyList();
        }

         return spuList.stream().map(MongoStockBillLogBO::getStockBillLogId).collect(Collectors.toList());
    }

    @Override
    public void initStockBillLog() {
        while (true) {
            int size = 10000;
            // 分页获取，每页10000条
            Criteria criteria = Criteria.where("stockBillItemLogs").exists(false);
            Query query = new Query(criteria).skip(0).limit(size);
            List<MongoStockBillLogBO> mongoStockBillLogList = mongoTemplate.find(query, MongoStockBillLogBO.class);
            if (CollUtil.isEmpty(mongoStockBillLogList)) {
                break;
            }
            // 查询库存项记录
            List<Long> stockBillLogIds = mongoStockBillLogList.stream().map(MongoStockBillLogBO::getStockBillLogId).collect(Collectors.toList());
            Criteria criteriaItem = Criteria.where("stockBillLogId").in(stockBillLogIds);
            List<MongoStockBillLogItemBO> mongoStockBillLogItemList = mongoTemplate.find(new Query(criteriaItem), MongoStockBillLogItemBO.class, "stock_bill_log_item");
            if (CollUtil.isEmpty(mongoStockBillLogItemList)) {
                break;
            }
            // 查询sku和spu信息
            Set<Long> spuIds = mongoStockBillLogItemList.stream().map(MongoStockBillLogItemBO::getSpuId).collect(Collectors.toSet());
            Set<Long> skuIds = mongoStockBillLogItemList.stream().map(MongoStockBillLogItemBO::getSkuId).collect(Collectors.toSet());
            List<SkuVO> skuList = skuMapper.listSkuBySkuIds(new ArrayList<>(skuIds));
            ProductLangUtil.skuList(skuList);
            List<SpuVO> spuList = spuMapper.listSpuBySpuIds(new ArrayList<>(spuIds));
            ProductLangUtil.spuList(spuList);
            Map<Long, SkuVO> skuMap = skuList.stream().collect(Collectors.toMap(SkuVO::getSkuId, Function.identity()));
            Map<Long, SpuVO> spuMap = spuList.stream().collect(Collectors.toMap(SpuVO::getSpuId, Function.identity()));

            // 拼接库存项数据
            BulkOperations bulkOperations = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, MongoStockBillLogBO.class);

            Map<Long, List<MongoStockBillLogItemBO>> stockBillMap = mongoStockBillLogItemList.stream().collect(Collectors.groupingBy(MongoStockBillLogItemBO::getStockBillLogId));
            for (MongoStockBillLogBO mongoStockBillLogBO : mongoStockBillLogList) {
                if (!stockBillMap.containsKey(mongoStockBillLogBO.getStockBillLogId())) {
                    continue;
                }
                List<MongoStockBillLogItemBO> mongoStockBillLogItems = stockBillMap.get(mongoStockBillLogBO.getStockBillLogId());
                for (MongoStockBillLogItemBO mongoStockBillLogItem : mongoStockBillLogItems) {
                    mongoStockBillLogItem.setStockBillLogId(null);
                    SkuVO skuVO = skuMap.get(mongoStockBillLogItem.getSkuId());
                    SpuVO spuVO = spuMap.get(mongoStockBillLogItem.getSpuId());
                    if (Objects.nonNull(spuVO)) {
                        mongoStockBillLogItem.setSpuName(spuVO.getName());
                        mongoStockBillLogItem.setMainImgUrl(spuVO.getMainImgUrl());
                    }
                    if (Objects.nonNull(skuVO)) {
                        mongoStockBillLogItem.setSkuName(skuVO.getSkuName());
                        mongoStockBillLogItem.setPartyCode(skuVO.getPartyCode());
                        if (StrUtil.isNotBlank(skuVO.getImgUrl())) {
                            mongoStockBillLogItem.setMainImgUrl(skuVO.getImgUrl());
                        }
                    }
                }
                mongoStockBillLogBO.setStockBillItemLogs(mongoStockBillLogItems);
                bulkOperations.replaceOne(new Query(Criteria.where("_id").is(mongoStockBillLogBO.getStockBillLogId())), mongoStockBillLogBO);
            }
            // 执行批量操作
            bulkOperations.execute();
            if (mongoStockBillLogList.size() < size) {
                break;
            }
        }
    }

    @Override
    public List<MongoStockBillLogItemBO> listLastSkuStock() {
        GroupOperation groupOperation = Aggregation.group("stockBillItemLogs.skuId")
                .last("stockBillItemLogs.skuId").as("skuId")
                .last("stockBillItemLogs.afterStock").as("afterStock");

        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.unwind("stockBillItemLogs"),
                groupOperation
        );

        AggregationResults<MongoStockBillLogItemBO> aggregationResults = mongoTemplate.aggregate(aggregation, "stock_bill_log", MongoStockBillLogItemBO.class);
        List<MongoStockBillLogItemBO> mongoStockBillLogItemBOList = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(mongoStockBillLogItemBOList)) {
            return Collections.emptyList();
        }
        return mongoStockBillLogItemBOList;
    }
}
