package com.neusoft.smart.pos.inventory.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.neusoft.smart.pos.dto.inventory.bean.LockStorageBean;
import com.neusoft.smart.pos.dto.inventory.request.*;
import com.neusoft.smart.pos.dto.inventory.response.InventoryGoodsResponse;
import com.neusoft.smart.pos.framework.bean.PaginationExtendBean;
import com.neusoft.smart.pos.framework.exception.BusinessCommonException;
import com.neusoft.smart.pos.framework.utils.BeanUtil;
import com.neusoft.smart.pos.framework.utils.DateUtils;
import com.neusoft.smart.pos.inventory.constants.GoodsErrorCode;
import com.neusoft.smart.pos.inventory.constants.InventoryConstant;
import com.neusoft.smart.pos.inventory.constants.InventoryErrorCode;
import com.neusoft.smart.pos.inventory.persistent.domain.*;
import com.neusoft.smart.pos.inventory.persistent.mapper.*;
import com.neusoft.smart.pos.utils.RequestHeaderUtil;
import lombok.val;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class GoodsDefaultService implements GoodsBaseServiceInterface {
    
    @Resource
    private InventoryGoodsInfoMapper inventoryGoodsInfoMapper;
    
    @Resource
    private InventoryStoreBatchMapper inventoryStoreBatchMapper;
    
    @Resource
    private InventoryGoodsLockInfoMapper inventoryGoodsLockInfoMapper;
    
    @Resource
    private InventoryGoodsConfigMapper inventoryGoodsConfigMapper;
    
    @Resource
    private InventoryStoreInoutMapper inventoryStoreInoutMapper;
    
    @Resource
    private InventoryGoodsLimitMapper inventoryGoodsLimitMapper;
    
    @Resource
    private InventoryGoodsTagRefMapper inventoryGoodsTagRefMapper;
    
    @Resource
    private InventoryGoodsTagMapper inventoryGoodsTagMapper;
    
    @Resource
    private InventoryGoodsCategoryMapper inventoryGoodsCategoryMapper;
    
    @Value("${checkStorageOnceMore}")
    private Boolean checkStorageOnceMore = false;
    
    @Override
    public Object sign(JSONObject authData) {
        return null;
    }
    
    @Override
    public JSONObject getAuthData() {
        return null;
    }
    
    @Override
    public PaginationExtendBean<InventoryGoodsResponse> getPage(InventoryGoodsQueryRequest inventoryGoodsQueryRequest) {
        
        PaginationExtendBean<InventoryGoodsResponse> result = new PaginationExtendBean<>();
        result.setTotal(0);
        result.setRows(new ArrayList<>());
        
        InventoryGoodsInfoExample inventoryGoodsInfoExample = new InventoryGoodsInfoExample();
        if (inventoryGoodsQueryRequest.getIsPage() && inventoryGoodsQueryRequest.getLimit() != null && inventoryGoodsQueryRequest.getLimit() > 0) {
            inventoryGoodsInfoExample.page(inventoryGoodsQueryRequest.getOffset(), inventoryGoodsQueryRequest.getLimit());
        }
        
        if (!StringUtils.isEmpty(inventoryGoodsQueryRequest.getOrderByClause())) {
            inventoryGoodsInfoExample.setOrderByClause(inventoryGoodsQueryRequest.getOrderByClause());
        } else {
            inventoryGoodsInfoExample.setOrderByClause("id desc");
        }
        
        InventoryGoodsInfoExample.Criteria inventoryGoodsInfoExampleCriteria = inventoryGoodsInfoExample.createCriteria();
        inventoryGoodsInfoExampleCriteria.andDeletedEqualTo(0);
        if (inventoryGoodsQueryRequest.getWithoutServiceCharge()) {
            inventoryGoodsInfoExampleCriteria.andCateIdGreaterThan(0);
        }
        if (!StringUtils.isEmpty(inventoryGoodsQueryRequest.getName())) {
            inventoryGoodsInfoExampleCriteria.andNameLike("%" + inventoryGoodsQueryRequest.getName() + "%");
        }
        
        if (!StringUtils.isEmpty(inventoryGoodsQueryRequest.getBarCode())) {
            inventoryGoodsInfoExampleCriteria.andBarCodeEqualTo(inventoryGoodsQueryRequest.getBarCode());
        }
        
        if (inventoryGoodsQueryRequest.getOrgId() != null) {
            inventoryGoodsInfoExampleCriteria.andOrgIdEqualTo(inventoryGoodsQueryRequest.getOrgId());
        }
        
        if (inventoryGoodsQueryRequest.getShopId() != null) {
            inventoryGoodsInfoExampleCriteria.andShopIdEqualTo(inventoryGoodsQueryRequest.getShopId());
        }
        if (inventoryGoodsQueryRequest.getShopIds() != null) {
            inventoryGoodsInfoExampleCriteria.andShopIdIn(inventoryGoodsQueryRequest.getShopIds());
        }
        
        if (inventoryGoodsQueryRequest.getOnSale() != null)
            inventoryGoodsInfoExampleCriteria.andOnSaleEqualTo(inventoryGoodsQueryRequest.getOnSale());
        
        if (!StringUtils.isEmpty(inventoryGoodsQueryRequest.getCategories())) {
            inventoryGoodsInfoExampleCriteria.andCateIdIn(this.convertIdListString(inventoryGoodsQueryRequest.getCategories()));
        }
        
        Map<Integer, List<InventoryGoodsTag>> tagsMap = new HashMap<>();
        // 如果是按照标签查询,查询出标签商品关系,记录各自的id
        if (!StringUtils.isEmpty(inventoryGoodsQueryRequest.getTags())) {
            tagsMap = this.getTags(this.convertIdListString(inventoryGoodsQueryRequest.getTags()), null);
            if (tagsMap.keySet().size() == 0) {
                return result;
            }
            inventoryGoodsInfoExampleCriteria.andIdIn(new ArrayList<>(tagsMap.keySet()));
        }
        List<InventoryGoodsResponse> inventoryGoodsResponses = new ArrayList<>();
        List<InventoryGoodsInfo> inventoryGoodsInfos = inventoryGoodsInfoMapper.selectByExample(inventoryGoodsInfoExample);
        if (inventoryGoodsInfos.isEmpty()) {
            return result;
        }
        //查询标签是空的情况下 需要查询所有商品结果的标签对应数据
        if (StringUtils.isEmpty(inventoryGoodsQueryRequest.getTags())) {
            tagsMap = this.getTags(null, inventoryGoodsInfos.stream().map(InventoryGoodsInfo::getId).collect(Collectors.toList()));
        }
        List<InventoryGoodsCategory> inventoryGoodsCategories = this.getCategoriesByIds(inventoryGoodsInfos.stream().map(InventoryGoodsInfo::getCateId).collect(Collectors.toList()));
        for (InventoryGoodsInfo inventoryGoodsInfo : inventoryGoodsInfos) {
            InventoryGoodsResponse resp = new InventoryGoodsResponse();
            BeanUtils.copyProperties(inventoryGoodsInfo, resp);
            if (inventoryGoodsInfo.getCateId() == -1) {
                resp.setCateName("餐位费");
                resp.setPriority(0);
            } else {
                resp.setCateName(inventoryGoodsCategories.stream().filter(item -> item.getId().equals(resp.getCateId())).findAny().orElse(new InventoryGoodsCategory()).getName());
                resp.setPriority(inventoryGoodsCategories.stream().filter(item -> item.getId().equals(resp.getCateId())).findAny().orElse(new InventoryGoodsCategory()).getPriority());
            }
            resp.setTags(tagsMap.containsKey(resp.getId()) ? tagsMap.get(resp.getId()).stream().collect(Collectors.toMap(InventoryGoodsTag::getId, InventoryGoodsTag::getName)) : null);
            
            inventoryGoodsResponses.add(resp);
        }
        
        result.setRows(inventoryGoodsResponses);
        
        if (inventoryGoodsQueryRequest.getLimit() != null && inventoryGoodsQueryRequest.getLimit() > 0) {
            result.setTotal(inventoryGoodsInfoMapper.countByExample(inventoryGoodsInfoExample));
        }
        return result;
    }
    
    @Override
    @Transactional
    public Boolean createGoods(PostInventoryGoodsInfoRequest postInventoryGoodsInfoRequest) {
        if ("admin".equals(RequestHeaderUtil.getRole())) {
            postInventoryGoodsInfoRequest.setStore(0);
        } else if (postInventoryGoodsInfoRequest.getStore() == null) {
            throw new BusinessCommonException(GoodsErrorCode.MISSING_STORE_ID);
        }
        postInventoryGoodsInfoRequest.setOrg(RequestHeaderUtil.getOrgId());
        //判断一下是否同一个门店或者集团下,商品一维码是否重复
        if (!StringUtils.isEmpty(postInventoryGoodsInfoRequest.getBarCode())) {
            InventoryGoodsInfoExample example = new InventoryGoodsInfoExample();
            example.createCriteria().andBarCodeEqualTo(postInventoryGoodsInfoRequest.getBarCode())
                    .andOrgIdEqualTo(postInventoryGoodsInfoRequest.getOrg())
                    .andShopIdEqualTo(postInventoryGoodsInfoRequest.getStore())
                    .andDeletedEqualTo(0);
            if (inventoryGoodsInfoMapper.countByExample(example) > 0) {
                throw new BusinessCommonException(GoodsErrorCode.DUPLICATE_BAR_CODE);
            }
        } else {
            InventoryGoodsInfoExample example = new InventoryGoodsInfoExample();
            example.createCriteria().andNameEqualTo(postInventoryGoodsInfoRequest.getName())
                    .andOrgIdEqualTo(postInventoryGoodsInfoRequest.getOrg())
                    .andShopIdEqualTo(postInventoryGoodsInfoRequest.getStore())
                    .andDeletedEqualTo(0);
            if (inventoryGoodsInfoMapper.countByExample(example) > 0) {
                throw new BusinessCommonException(GoodsErrorCode.DUPLICATE_CATERING_MENU_NAME);
            }
        }
        
        
        InventoryGoodsInfo goods = new InventoryGoodsInfo();
        BeanUtils.copyProperties(postInventoryGoodsInfoRequest, goods);
        goods.setCateId(postInventoryGoodsInfoRequest.getCategoryId());
        goods.setShopId(postInventoryGoodsInfoRequest.getStore());
        goods.setOrgId(postInventoryGoodsInfoRequest.getOrg());
        goods.setCreatedBy(RequestHeaderUtil.getUserId());
        goods.setCateId(postInventoryGoodsInfoRequest.getCategoryId());
        inventoryGoodsInfoMapper.insertSelective(goods);
        
        if (InventoryConstant.YES.equals(goods.getIsStock())) {
            this.createLimit(goods.getId(), InventoryConstant.STOCK_LIMIT_BEAN_ID, null);
        }
        //TODO 以后可能会有标签和商品的关系 现在这个没有做
        
        return true;
    }
    
    @Override
    public Boolean batchCreateGoods(List<PostInventoryGoodsInfoRequest> postInventoryGoodsInfoRequestList) {
        List<InventoryGoodsInfo> inventoryGoodsInfos = new ArrayList<>();
        Date date = new Date();
        Integer userId = RequestHeaderUtil.getUserId();
        postInventoryGoodsInfoRequestList.forEach(item -> {
            InventoryGoodsInfo inventoryGoodsInfo = new InventoryGoodsInfo();
            BeanUtils.copyProperties(item, inventoryGoodsInfo);
            inventoryGoodsInfo.setCateId(item.getCategoryId());
            inventoryGoodsInfo.setOrgId(item.getOrg());
            inventoryGoodsInfo.setShopId(item.getStore());
            inventoryGoodsInfo.setDeleted(InventoryConstant.NO);
            inventoryGoodsInfo.setPriority(999);
            inventoryGoodsInfo.setIsThirdparty(InventoryConstant.NO);//FIXME 以后实现第三方
            inventoryGoodsInfo.setCreatedBy(userId);
            inventoryGoodsInfo.setCreatedTime(date);
            inventoryGoodsInfos.add(inventoryGoodsInfo);
        });
        inventoryGoodsInfoMapper.batchInsert(inventoryGoodsInfos);
        return true;
    }
    
    @Override
    public Boolean updateGoods(PutInventoryGoodsInfoRequest putInventoryGoodsInfoRequest) {
        InventoryGoodsInfoExample example = new InventoryGoodsInfoExample();
        InventoryGoodsInfoExample.Criteria criteria = example.createCriteria();
        criteria.andNameEqualTo(putInventoryGoodsInfoRequest.getName())
                .andShopIdEqualTo(putInventoryGoodsInfoRequest.getStore())
                .andDeletedEqualTo(0)
                .andIdNotEqualTo(putInventoryGoodsInfoRequest.getId());
        if (inventoryGoodsInfoMapper.countByExample(example) > 0) {
            throw new BusinessCommonException(GoodsErrorCode.DUPLICATE_CATERING_MENU_NAME);
        }
        
        InventoryGoodsInfo goods = new InventoryGoodsInfo();
        goods.setId(putInventoryGoodsInfoRequest.getId());
        goods.setUpdatedTime(new Date());
        
        Integer store = putInventoryGoodsInfoRequest.getStore();
        if (!"admin".equals(RequestHeaderUtil.getRole()) && store != null) {
            goods.setShopId(store);
        }
        
        if (putInventoryGoodsInfoRequest.getCategoryId() != null) {
            goods.setCateId(putInventoryGoodsInfoRequest.getCategoryId());
        }
        
        BeanUtils.copyProperties(putInventoryGoodsInfoRequest, goods);
        inventoryGoodsInfoMapper.updateByPrimaryKeySelective(goods);
        if (goods.getIsStock().equals(InventoryConstant.YES)) {
            this.createLimit(goods.getId(), InventoryConstant.STOCK_LIMIT_BEAN_ID, null);
        } else {
            this.deleteLimit(goods.getId(), InventoryConstant.STOCK_LIMIT_BEAN_ID);
        }
        return true;
    }
    
    @Override
    public Boolean deleteGoods(Integer goodsId, Boolean deleteBatch) {
        InventoryGoodsInfo inventoryGoodsInfo = inventoryGoodsInfoMapper.selectByPrimaryKey(goodsId);
        inventoryGoodsInfo.setDeleted(InventoryConstant.YES);
        int row = inventoryGoodsInfoMapper.updateByPrimaryKey(inventoryGoodsInfo);
        if (row <= 0) {
            throw new BusinessCommonException(InventoryErrorCode.UPDATE_ERROR);
        }
        if (deleteBatch) {
            InventoryStoreBatch record = new InventoryStoreBatch();
            record.setStatus(InventoryConstant.SOLDOUT_BATCH);
            InventoryStoreBatchExample example = new InventoryStoreBatchExample();
            example.createCriteria().andGoodsIdEqualTo(goodsId);
            inventoryStoreBatchMapper.updateByExampleSelective(record, example);
        }
        return true;
    }
    
    @Override
    public boolean batchDeleteGoods(List<Integer> goodsIds) {
        InventoryGoodsInfoExample goodsInfoExample = new InventoryGoodsInfoExample();
        goodsInfoExample.createCriteria().andIdIn(goodsIds);
        InventoryGoodsInfo goods = new InventoryGoodsInfo();
        goods.setDeleted(1);
        goods.setUpdatedTime(new Date());
        inventoryGoodsInfoMapper.updateByExampleSelective(goods, goodsInfoExample);
        return true;
    }
    
    @Override
    public InventoryGoodsResponse getGoods(Integer goodsId) {
        InventoryGoodsInfo inventoryGoodsInfo = inventoryGoodsInfoMapper.selectByPrimaryKey(goodsId);
        if (null == inventoryGoodsInfo) {
            return null;
        }
        InventoryGoodsResponse inventoryGoodsResponse = BeanUtil.parse(inventoryGoodsInfo, InventoryGoodsResponse.class);
        List<InventoryGoodsCategory> categories = this.getCategoriesByIds(Collections.singletonList(inventoryGoodsInfo.getCateId()));
        if (!categories.isEmpty()) {
            inventoryGoodsResponse.setCateName(categories.get(0).getName());
        }
        Map<Integer, List<InventoryGoodsTag>> tagMap = this.getTags(null, Collections.singletonList(inventoryGoodsInfo.getCateId()));
        if (tagMap.size() > 0) {
            inventoryGoodsResponse.setTags(tagMap.get(goodsId).stream().collect(Collectors.toMap(InventoryGoodsTag::getId, InventoryGoodsTag::getName)));
        }
        return inventoryGoodsResponse;
    }
    
    @Override
    public boolean warehouseIn(PostInventoryStoreBatchRequest postInventoryStoreBatchRequest) {
        //TODO 用户退货和盘盈后续逻辑待设计
        InventoryStoreBatch insertBatch = new InventoryStoreBatch();
        BeanUtils.copyProperties(postInventoryStoreBatchRequest, insertBatch);
        String operator = "";
        if (StringUtils.isEmpty(postInventoryStoreBatchRequest.getOperator()))
            operator = RequestHeaderUtil.getUsername();
        else operator = postInventoryStoreBatchRequest.getOperator();
        
        insertBatch.setStatus(InventoryConstant.USABLE_BATCH);
        insertBatch.setCreatedBy(operator);
        InventoryGoodsInfo inventoryGoodsInfo = inventoryGoodsInfoMapper.selectByPrimaryKey(postInventoryStoreBatchRequest.getGoodsId());
        Integer storageCount = postInventoryStoreBatchRequest.getStorageNum().multiply(new BigDecimal(Math.pow(10, inventoryGoodsInfo.getScale()))).intValue();
        insertBatch.setLockedStorageCount(storageCount); //入库的时候 锁库存和实际库存赋值成一样,其他库存操作,另外方法
        insertBatch.setStorageNum(storageCount);
        insertBatch.setOrgId(inventoryGoodsInfo.getOrgId());

        InventoryStoreInout inout = new InventoryStoreInout();
        inout.setBatchId(insertBatch.getId());
        inout.setGoodsId(insertBatch.getGoodsId());
        inout.setShopId(insertBatch.getShopId());

        BigDecimal totalNum = this.sumStockNumByGoodsId(insertBatch);

        if(null != totalNum) {
            totalNum = totalNum.divide(new BigDecimal(Math.pow(10, inventoryGoodsInfo.getScale())), inventoryGoodsInfo.getScale(), BigDecimal.ROUND_HALF_UP);
        } else {
            totalNum = new BigDecimal(0);
        }
        //先让上边查询总数，然后插入，否则会将新入库的数量查询出来
        inventoryStoreBatchMapper.insertSelective(insertBatch);

        inout.setResidueNum(totalNum);
        inout.setVarNum(postInventoryStoreBatchRequest.getStorageNum());
        inout.setInPrice(postInventoryStoreBatchRequest.getInPrice());
        inout.setSupplier(postInventoryStoreBatchRequest.getSupplier());
        inout.setActionDire(postInventoryStoreBatchRequest.getActionDire());
        inout.setActionType(postInventoryStoreBatchRequest.getActionType());
        inout.setCreatedBy(operator);
        inventoryStoreInoutMapper.insertSelective(inout);
        return true;
    }
    
    @Override
    @Transactional
    public boolean warehouseOut(SellAndWarehouseOutRequest sellAndWarehouseOutRequest) {
        //FIXME 无锁库存的订单重复扣减库存需要再考虑
        if (!this.getLockInfoByOrderId(sellAndWarehouseOutRequest.getOrderNum()).isEmpty()) {
            throw new BusinessCommonException(InventoryErrorCode.DUPLICATE_ORDER_ERROR);
        }
        List<String> msg = new ArrayList<>();
        //先查询出商品系统配置
        boolean canNegativeStorage = false;  //库存是否可以为负
        boolean isLock = false; //是否需要锁库存
        InventoryGoodsConfig inventoryGoodsConfig = this.getConfig(sellAndWarehouseOutRequest.getOrgId(), sellAndWarehouseOutRequest.getShopId());
        //门店库存配置
        if (null != inventoryGoodsConfig) {
            canNegativeStorage = inventoryGoodsConfig.getCanNegativeStorage() == 1;
            isLock = inventoryGoodsConfig.getIsLock() == 1;
        }
        boolean finalCanNegativeStorage = canNegativeStorage;
        boolean finalIsLock = isLock;
        sellAndWarehouseOutRequest.getOrderItems().forEach(item -> {
            //查询一下对应商品,如果没查到就忽略库存相关,主要为了兼容pos菜单id不准确的问题(使用自定义序列而不是id)
            InventoryGoodsInfoExample inventoryGoodsInfoExample = new InventoryGoodsInfoExample();
            inventoryGoodsInfoExample.createCriteria().andOrgIdEqualTo(sellAndWarehouseOutRequest.getOrgId()).andShopIdEqualTo(sellAndWarehouseOutRequest.getShopId()).andDeletedEqualTo(InventoryConstant.NO).andIdEqualTo(item.getGoodsId());
            InventoryGoodsInfo inventoryGoodsInfo = inventoryGoodsInfoMapper.selectByExample(inventoryGoodsInfoExample).stream().findAny().orElse(null);
            if (null != inventoryGoodsInfo) {
                boolean isStock = inventoryGoodsInfo.getIsStock() == 1;//是否库存管理
                if (isStock) {
                    //查询出有效库存
                    InventoryStoreBatchExample example = new InventoryStoreBatchExample();
                    example.createCriteria().andGoodsIdEqualTo(item.getGoodsId()).andStatusEqualTo(InventoryConstant.USABLE_BATCH);
                    example.setOrderByClause(" id asc");
                    List<InventoryStoreBatch> inventoryStoreBatchList = inventoryStoreBatchMapper.selectByExample(example);
                    //不可以负库存,并在配置文件中配置了需要再次计算,具体看性能,如果太慢可以取消二次检查
                    if (checkStorageOnceMore && !finalCanNegativeStorage) {
                        checkLimitOnceMore(inventoryStoreBatchList, item.getStorageNum(), item.getScale());
                    }
                    if (inventoryStoreBatchList.isEmpty()) {
                        msg.add("商品" + inventoryGoodsInfo.getName() + "没有库存信息");
                    } else {
                        try {
                            //先计算一下单个商品所需要扣减的换算后的数量
                            Integer deductionStorageCount = item.getStorageNum().multiply(new BigDecimal(Math.pow(10, item.getScale()))).intValue();
                            String batchStatus = InventoryConstant.USABLE_BATCH;
                            //循环所有有效批次
                            for (InventoryStoreBatch inventoryStoreBatch : inventoryStoreBatchList) {
                                Integer loopDeduction;
                                //扣减变为0的时候 跳出循环
                                if (deductionStorageCount <= 0) {
                                    break;
                                }
                                //如果批次锁库存剩余小于要扣减的 批次清零后 修改状态
                                if (inventoryStoreBatch.getLockedStorageCount() < deductionStorageCount) {
                                    loopDeduction = inventoryStoreBatch.getLockedStorageCount();
                                    deductionStorageCount -= loopDeduction;
                                    batchStatus = InventoryConstant.ZERO_BATCH;
                                } else {
                                    loopDeduction = deductionStorageCount;
                                    deductionStorageCount = 0;
                                }
                                deductionStorage(sellAndWarehouseOutRequest.getOrderNum(),
                                        inventoryStoreBatch.getGoodsId(),
                                        inventoryStoreBatch.getId(),
                                        inventoryStoreBatch.getLockedStorageCount(),
                                        loopDeduction, batchStatus, DateUtils.addSecond(new Date(), 1000 * 60 * 60 * 48),//TODO  因为调用该方法时订单服务未生成订单,所以超时时间没法提供,与订单服务相同手动默认2小时
                                        inventoryStoreBatch.getVersion(), finalIsLock);
                                //没有锁库存商品扣减成功后,记录商品出入库信息
                                if (!finalIsLock) {
                                    InventoryStoreInout inventoryStoreInout = new InventoryStoreInout();
                                    inventoryStoreInout.setGoodsId(item.getGoodsId());
                                    inventoryStoreInout.setBatchId(inventoryStoreBatch.getId());
                                    inventoryStoreInout.setResidueNum(new BigDecimal(inventoryStoreBatch.getStorageNum()).divide(new BigDecimal(Math.pow(10, item.getScale())), item.getScale(), BigDecimal.ROUND_HALF_UP));
                                    inventoryStoreInout.setVarNum(new BigDecimal(loopDeduction).divide(new BigDecimal(Math.pow(10, item.getScale())), item.getScale(), BigDecimal.ROUND_HALF_UP));
                                    inventoryStoreInout.setActionDire(InventoryConstant.STOCK_DEC);
                                    inventoryStoreInout.setActionType(InventoryConstant.GOODS_SALE);
                                    inventoryStoreInout.setUniqId(sellAndWarehouseOutRequest.getOrderNum());
                                    inventoryStoreInout.setShopId(item.getShopId());
                                    inventoryStoreInoutMapper.insertSelective(inventoryStoreInout);
                                }
                            }
                            if (deductionStorageCount > 0 && finalCanNegativeStorage) {//可以负库存，直接选第一个扣减
                                deductionStorage(sellAndWarehouseOutRequest.getOrderNum(),
                                        inventoryStoreBatchList.get(inventoryStoreBatchList.size() - 1).getGoodsId(),
                                        inventoryStoreBatchList.get(inventoryStoreBatchList.size() - 1).getId(),
                                        inventoryStoreBatchList.get(inventoryStoreBatchList.size() - 1).getLockedStorageCount(),
                                        deductionStorageCount,
                                        InventoryConstant.USABLE_BATCH, DateUtils.addSecond(new Date(), 1000 * 60 * 60 * 48),
                                        inventoryStoreBatchList.get(inventoryStoreBatchList.size() - 1).getVersion() + 1,
                                        finalIsLock);
                                //没有锁库存商品扣减成功后,记录商品出入库信息
                                if (!finalIsLock) {
                                    InventoryStoreInout inventoryStoreInout = new InventoryStoreInout();
                                    inventoryStoreInout.setGoodsId(inventoryStoreBatchList.get(inventoryStoreBatchList.size() - 1).getGoodsId());
                                    inventoryStoreInout.setBatchId(inventoryStoreBatchList.get(inventoryStoreBatchList.size() - 1).getId());
                                    inventoryStoreInout.setResidueNum(new BigDecimal(inventoryStoreBatchList.get(inventoryStoreBatchList.size() - 1).getLockedStorageCount()).divide(new BigDecimal(Math.pow(10, item.getScale())), item.getScale(), BigDecimal.ROUND_HALF_UP));
                                    inventoryStoreInout.setVarNum(new BigDecimal(deductionStorageCount).divide(new BigDecimal(Math.pow(10, item.getScale())), item.getScale(), BigDecimal.ROUND_HALF_UP));
                                    inventoryStoreInout.setActionType(InventoryConstant.GOODS_SALE);
                                    inventoryStoreInout.setActionDire(InventoryConstant.STOCK_DEC);
                                    inventoryStoreInout.setUniqId(sellAndWarehouseOutRequest.getOrderNum());
                                    inventoryStoreInout.setShopId(inventoryStoreBatchList.get(inventoryStoreBatchList.size() - 1).getShopId());
                                    inventoryStoreInoutMapper.insertSelective(inventoryStoreInout);
                                }
                            }
                        } catch (Exception e) {
                            msg.add(e.getMessage());
                        }
                    }
                }
            }
        });
        if (!msg.isEmpty()) {
            throw new BusinessCommonException(InventoryErrorCode.DEDUCTION_ERROR, StringUtils.collectionToDelimitedString(msg, ","));
        }
        return true;
    }
    
    @Override
    public boolean lockStorage(LockStorageBean lockStorageBean) {
        inventoryGoodsLockInfoMapper.insertSelective(BeanUtil.parse(lockStorageBean, InventoryGoodsLockInfo.class));
        return true;
    }
    
    @Override
    @Transactional
    public Boolean releaseLockStorage(String orderId, Integer orgId, Integer shopId, Integer status) {
        List<InventoryGoodsLockInfo> goodsLockInfos = getLockInfoByOrderId(orderId, InventoryConstant.LOCKER_DEFAULT);
        //没找到啥也不处理
        if (goodsLockInfos.isEmpty()) {
            return true;
        }
        InventoryGoodsConfig config = this.getConfig(orgId, shopId);
        Boolean canNegativeStorage = config.getCanNegativeStorage() == 1;
        
        goodsLockInfos.forEach(lock -> {
            InventoryStoreBatch inventoryStoreBatch = inventoryStoreBatchMapper.selectByPrimaryKey(lock.getGoodsBatchId());
            inventoryStoreBatchMapper.handleLockStorage(inventoryStoreBatch.getId(),
                    status == InventoryConstant.LOCKER_DEDUCTED ? lock.getLockCount() : null,
                    status != InventoryConstant.LOCKER_DEDUCTED ? lock.getLockCount() : null,
                    canNegativeStorage, inventoryStoreBatch.getVersion());
            lock.setStatus(status);
            inventoryGoodsLockInfoMapper.updateByPrimaryKeySelective(lock);
            if (status.equals(InventoryConstant.LOCKER_DEDUCTED)) {
                //扣减成功后,记录商品出入库信息
                InventoryStoreInout inventoryStoreInout = new InventoryStoreInout();
                inventoryStoreInout.setGoodsId(lock.getGoodsId());
                inventoryStoreInout.setBatchId(lock.getGoodsBatchId());
                inventoryStoreInout.setResidueNum(BigDecimal.valueOf(lock.getBeforeLockCount()));
                inventoryStoreInout.setVarNum(BigDecimal.valueOf(lock.getLockCount()));
                inventoryStoreInout.setActionDire(InventoryConstant.STOCK_DEC);
                inventoryStoreInout.setActionType(InventoryConstant.GOODS_SALE);
                inventoryStoreInout.setUniqId(orderId);
                inventoryStoreInoutMapper.insertSelective(inventoryStoreInout);
            }
        });
        return null;
    }
    
    /**
     * 超时释放锁库存
     *
     * @param lockInfo
     */
    public void lockTimeOut(InventoryGoodsLockInfo lockInfo) {
        InventoryStoreBatch inventoryStoreBatch = inventoryStoreBatchMapper.selectByPrimaryKey(lockInfo.getGoodsBatchId());
        inventoryStoreBatchMapper.handleLockStorage(inventoryStoreBatch.getId(), null, lockInfo.getLockCount(),
                true, inventoryStoreBatch.getVersion());
        lockInfo.setStatus(InventoryConstant.LOCKER_TIMEOUT);
        inventoryGoodsLockInfoMapper.updateByPrimaryKeySelective(lockInfo);
    }
    
    
    /**
     * 检查商品是否重复
     *
     * @param orgId
     * @param shopId
     * @param barCode
     * @param name
     * @return
     */
    public Boolean checkDuplicateGoods(Integer orgId, Integer shopId, String barCode, String name) {
        InventoryGoodsInfoExample example = new InventoryGoodsInfoExample();
        InventoryGoodsInfoExample.Criteria criteria = example.createCriteria().andOrgIdEqualTo(orgId).andShopIdEqualTo(shopId).andNameEqualTo(name).andDeletedEqualTo(InventoryConstant.NO);
        if (!StringUtils.isEmpty(barCode)) {
            criteria.andBarCodeEqualTo(barCode);
        }
        return inventoryGoodsInfoMapper.countByExample(example) > 0;
    }
    
    /**
     * 再次验证库存
     *
     * @param inventoryStoreBatchList
     * @param count
     * @param scale
     * @return 第一个是否可以负库存, 第二个是否开启库存锁功能
     */
    private void checkLimitOnceMore(List<InventoryStoreBatch> inventoryStoreBatchList, BigDecimal count, Integer scale) {
        //查询商品大于0的库存
        if (inventoryStoreBatchList.isEmpty()) {
            throw new BusinessCommonException(InventoryErrorCode.STORAGE_SHORTAGE);
        }
        List<InventoryStoreBatch> greaterThanZero = inventoryStoreBatchList.stream().filter(item -> item.getLockedStorageCount() > 0).collect(Collectors.toList());
        BigDecimal sum = new BigDecimal("0");
        BigDecimal lockCount = count.multiply(new BigDecimal(Math.pow(10, scale)));
        for (InventoryStoreBatch batch : greaterThanZero) {
            BigDecimal aBatch = new BigDecimal(batch.getLockedStorageCount());
            sum = sum.add(aBatch);
        }
        if (sum.compareTo(lockCount) < 0) {
            throw new BusinessCommonException(InventoryErrorCode.STORAGE_SHORTAGE);
        }
    }
    
    /**
     * 扣减库存
     *
     * @param orderId
     * @param goodsId
     * @param batchId
     * @param beforeLockCount
     * @param count
     * @param status
     * @param expireTime
     * @param version
     * @param isLock
     */
    private void deductionStorage(String orderId, Integer goodsId, Integer batchId, Integer beforeLockCount, Integer count, String status, Date expireTime, Integer version, Boolean isLock) {
        int result = inventoryStoreBatchMapper.deductionStorage(batchId, count, status, version, isLock);
        if (result <= 0) {
            throw new RuntimeException("更新批次失败");
        }
        if (isLock) {
            LockStorageBean lockStorageBean = new LockStorageBean();
            lockStorageBean.setOrderId(orderId);
            lockStorageBean.setGoodsId(goodsId);
            lockStorageBean.setGoodsBatchId(batchId);
            lockStorageBean.setBeforeLockCount(beforeLockCount);
            lockStorageBean.setLockCount(count);
            lockStorageBean.setLockTime(new Date());
            lockStorageBean.setExpireTime(expireTime);
            this.lockStorage(lockStorageBean);
        }
    }
    
    /**
     * 创建商品限制数据
     *
     * @param goodsId
     * @param limitBeanId
     * @param data
     */
    public void createLimit(Integer goodsId, Integer limitBeanId, Object data) {
        InventoryGoodsLimitExample inventoryGoodsLimitExample = new InventoryGoodsLimitExample();
        inventoryGoodsLimitExample.createCriteria().andGoodsIdEqualTo(goodsId).andBeanIdEqualTo(limitBeanId);
        if (inventoryGoodsLimitMapper.countByExample(inventoryGoodsLimitExample) > 0) {
            return;
        }
        InventoryGoodsLimit inventoryGoodsLimit = new InventoryGoodsLimit();
        inventoryGoodsLimit.setGoodsId(goodsId);
        inventoryGoodsLimit.setBeanId(limitBeanId);
        inventoryGoodsLimit.setLimitJson(null == data ? null : JSON.toJSONString(data));
        inventoryGoodsLimitMapper.insertSelective(inventoryGoodsLimit);
    }
    
    /**
     * 删除商品限制
     *
     * @param goodsId
     * @param limitBeanId
     */
    public void deleteLimit(Integer goodsId, Integer limitBeanId) {
        InventoryGoodsLimitExample inventoryGoodsLimitExample = new InventoryGoodsLimitExample();
        inventoryGoodsLimitExample.createCriteria().andGoodsIdEqualTo(goodsId).andBeanIdEqualTo(limitBeanId);
        inventoryGoodsLimitMapper.deleteByExample(inventoryGoodsLimitExample);
    }
    
    /**
     * 订单更改状态后库存相应适配
     *
     * @param orderStatus
     * @param sellAndWarehouseOutRequest
     */
    @Transactional
    public void orderChangeStatus(Integer orderStatus, SellAndWarehouseOutRequest sellAndWarehouseOutRequest) {
        switch (orderStatus) {
            case InventoryConstant.LOCKER_DEFAULT://订单生成
                this.warehouseOut(sellAndWarehouseOutRequest);
                break;
            case InventoryConstant.LOCKER_DEDUCTED://订单支付完成
                this.releaseLockStorage(sellAndWarehouseOutRequest.getOrderNum(), sellAndWarehouseOutRequest.getShopId(),
                        sellAndWarehouseOutRequest.getOrgId(), InventoryConstant.LOCKER_DEDUCTED);
                break;
            case InventoryConstant.LOCKER_CANCEL://订单取消
                this.releaseLockStorage(sellAndWarehouseOutRequest.getOrderNum(), sellAndWarehouseOutRequest.getShopId(),
                        sellAndWarehouseOutRequest.getOrgId(), InventoryConstant.LOCKER_CANCEL);
                break;
            case InventoryConstant.LOCKER_RETURN://订单退货
                this.returnOrder(sellAndWarehouseOutRequest.getOrderNum());
                break;
        }
    }
    
    /**
     * @param orderId
     */
    void returnOrder(String orderId) {
        //更新下锁库存状态,只更新已实际扣减过库存的数据
        InventoryGoodsLockInfo record = new InventoryGoodsLockInfo();
        record.setStatus(InventoryConstant.LOCKER_RETURN);
        InventoryGoodsLockInfoExample inventoryGoodsLockInfoExample = new InventoryGoodsLockInfoExample();
        inventoryGoodsLockInfoExample.createCriteria().andOrderIdEqualTo(orderId).andStatusEqualTo(InventoryConstant.LOCKER_DEDUCTED);
        inventoryGoodsLockInfoMapper.updateByExampleSelective(record, inventoryGoodsLockInfoExample);
        //查询出库记录
        InventoryStoreInoutExample example = new InventoryStoreInoutExample();
        example.createCriteria().andUniqIdEqualTo(orderId).andActionDireEqualTo(InventoryConstant.STOCK_DEC).andActionTypeEqualTo(InventoryConstant.GOODS_SALE);
        List<InventoryStoreInout> inventoryStoreInoutList = inventoryStoreInoutMapper.selectByExample(example);
        if (!inventoryStoreInoutList.isEmpty()) {
            inventoryStoreInoutList.forEach(item -> {
                InventoryStoreBatch inventoryStoreBatch = inventoryStoreBatchMapper.selectByPrimaryKey(item.getBatchId());
                int result = inventoryStoreBatchMapper.returnStorage(inventoryStoreBatch.getId(), item.getVarNum().intValue(), inventoryStoreBatch.getVersion());
                if (result <= 0) {
                    throw new BusinessCommonException(InventoryErrorCode.RETURN_ERROR);
                }
                //重新存一下进销存出入库数据
                item.setId(null);
                item.setCreatedBy(StringUtils.isEmpty(RequestHeaderUtil.getUsername()) ? "pos" : RequestHeaderUtil.getUsername());
                item.setCreatedTime(new Date());
                item.setResidueNum(new BigDecimal(inventoryStoreBatch.getStorageNum()));
                item.setActionDire(InventoryConstant.STOCK_ADD);
                item.setActionType(InventoryConstant.USER_RETURN);
                inventoryStoreInoutMapper.insertSelective(item);
            });
        }
    }
    
    /**
     * @param inventoryStoreBatch
     * @return
     */
    private BigDecimal sumStockNumByGoodsId(InventoryStoreBatch inventoryStoreBatch) {
        val example = new InventoryStoreBatchExample();
        example.createCriteria()
                .andGoodsIdEqualTo(inventoryStoreBatch.getGoodsId())
                .andStatusEqualTo(inventoryStoreBatch.getStatus());
        return inventoryStoreBatchMapper.sumByExample(example);
    }
    
    /**
     * 通过订单id 查询所有对应锁库存信息
     *
     * @param orderId
     * @param status
     * @return
     */
    private List<InventoryGoodsLockInfo> getLockInfoByOrderId(String orderId, Integer... status) {
        InventoryGoodsLockInfoExample inventoryGoodsLockInfoExample = new InventoryGoodsLockInfoExample();
        inventoryGoodsLockInfoExample.createCriteria().andOrderIdEqualTo(orderId);
        if (status != null && status.length > 0) {
            inventoryGoodsLockInfoExample.getOredCriteria().get(0).andStatusIn(Arrays.asList(status));
        }
        return inventoryGoodsLockInfoMapper.selectByExample(inventoryGoodsLockInfoExample);
    }
    
    /**
     * @param orgId
     * @param shopId
     * @return
     */
    public InventoryGoodsConfig getConfig(Integer orgId, Integer shopId) {
        InventoryGoodsConfigExample example1 = new InventoryGoodsConfigExample();
        example1.createCriteria().andShopIdEqualTo(shopId).
                andOrgIdEqualTo(orgId);
        List<InventoryGoodsConfig> inventoryGoodsConfigs = inventoryGoodsConfigMapper.selectByExample(example1);
        //配置
        if (!inventoryGoodsConfigs.isEmpty()) {
            return inventoryGoodsConfigs.get(0);
        }
        return null;
    }
    
    private List<Integer> convertIdListString(String ids) {
        String[] categories = StringUtils.delimitedListToStringArray(ids, "|");
        return Arrays.stream(categories).map(Integer::parseInt).collect(Collectors.toList());
    }
    
    /**
     * 通过标签id集合和商品id集合查询标签内容
     *
     * @param tagIds
     * @param goodsIds
     * @return key是商品id, value是标签内容
     */
    private Map<Integer, List<InventoryGoodsTag>> getTags(List<Integer> tagIds, List<Integer> goodsIds) {
        if (CollectionUtils.isEmpty(tagIds) && CollectionUtils.isEmpty(goodsIds)) {
            return new HashMap<>();
        }
        InventoryGoodsTagRefExample inventoryGoodsTagRefExample = new InventoryGoodsTagRefExample();
        InventoryGoodsTagRefExample.Criteria inventoryGoodsTagRefExampleCriteria = inventoryGoodsTagRefExample.createCriteria();
        if (!CollectionUtils.isEmpty(tagIds)) {
            inventoryGoodsTagRefExampleCriteria.andTagIdIn(tagIds);
        }
        if (!CollectionUtils.isEmpty(goodsIds)) {
            inventoryGoodsTagRefExampleCriteria.andGoodsIdIn(goodsIds);
        }
        List<InventoryGoodsTagRef> inventoryGoodsTagRefs = inventoryGoodsTagRefMapper.selectByExample(inventoryGoodsTagRefExample);
        tagIds = inventoryGoodsTagRefs.stream().map(InventoryGoodsTagRef::getTagId).collect(Collectors.toList());
        Map<Integer, List<InventoryGoodsTag>> result = new HashMap<>();
        if (!tagIds.isEmpty()) {
            InventoryGoodsTagExample inventoryGoodsTagExample = new InventoryGoodsTagExample();
            inventoryGoodsTagExample.createCriteria().andIdIn(tagIds);
            List<InventoryGoodsTag> inventoryGoodsTags = inventoryGoodsTagMapper.selectByExample(inventoryGoodsTagExample);
            inventoryGoodsTagRefs.forEach(ref -> {
                InventoryGoodsTag tag = inventoryGoodsTags.stream().filter(tempTag -> tempTag.getId().equals(ref.getTagId())).findAny().get();
                if (!result.containsKey(ref.getGoodsId())) {
                    List<InventoryGoodsTag> tags = new ArrayList<>();
                    tags.add(tag);
                    result.put(ref.getGoodsId(), tags);
                } else {
                    result.get(ref.getGoodsId()).add(tag);
                }
            });
        }
        return result;
    }
    
    /**
     * 根据分类id查询分类名称 TODO 以后可能会需要显示分类全名称,带父级名称
     *
     * @param ids
     * @return
     */
    private List<InventoryGoodsCategory> getCategoriesByIds(List<Integer> ids) {
        InventoryGoodsCategoryExample inventoryGoodsCategoryExample = new InventoryGoodsCategoryExample();
        inventoryGoodsCategoryExample.createCriteria().andIdIn(ids);
        return inventoryGoodsCategoryMapper.selectByExample(inventoryGoodsCategoryExample);
    }
}
