package com.fjhx.item.service.product.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.common.constant.SourceConstant;
import com.fjhx.common.entity.AvailableStockBo;
import com.fjhx.common.service.file.FtpFileService;
import com.fjhx.common.service.file.impl.FtpFileServiceImpl;
import com.fjhx.file.entity.FileInfoVo;
import com.fjhx.file.utils.ObsFileUtil;
import com.fjhx.item.entity.product.ProcessesBo;
import com.fjhx.item.entity.product.bo.ProductAnalysisBo;
import com.fjhx.item.entity.product.bo.ProductExcelExportBo;
import com.fjhx.item.entity.product.dto.ProductInfoDto;
import com.fjhx.item.entity.product.dto.ProductInfoSelectDto;
import com.fjhx.item.entity.product.po.ProductClassify;
import com.fjhx.item.entity.product.po.ProductInfo;
import com.fjhx.item.entity.product.po.ProductStockInfo;
import com.fjhx.item.entity.product.vo.ProductInfoVo;
import com.fjhx.item.enums.ProductAvailableRecordType;
import com.fjhx.item.mapper.product.ProductInfoMapper;
import com.fjhx.item.service.product.ProductClassifyService;
import com.fjhx.item.service.product.ProductInfoService;
import com.fjhx.item.service.product.ProductStockInfoService;
import com.fjhx.item.util.CodeEnum;
import com.fjhx.item.util.excel.ExcelImgUtils;
import com.fjhx.item.util.excel.util.ExcelUtil;
import com.fjhx.tenant.entity.dict.dto.DictTenantDataSelectDto;
import com.fjhx.tenant.entity.dict.vo.DictTenantDataVo;
import com.fjhx.tenant.service.dict.DictTenantDataService;
import com.fjhx.tenant.utils.DictUtils;
import com.ruoyi.common.core.domain.BaseIdPo;
import com.ruoyi.common.core.domain.BasePo;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.wrapper.IWrapper;
import com.ruoyi.common.utils.wrapper.SqlField;
import com.ruoyi.system.service.ISysDeptService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URL;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * <p>
 * 产品库 服务实现类
 * </p>
 *
 * @author zlj
 * @since 2023-03-17
 */
//@DS(SourceConstant.ITEM)
@Service
public class ProductInfoServiceImpl extends ServiceImpl<ProductInfoMapper, ProductInfo> implements ProductInfoService {

    @Autowired
    private ProductClassifyService productClassifyService;
    @Autowired
    private DictTenantDataService dictTenantDataService;
    @Autowired
    private FtpFileService ftpFileService;
    @Autowired
    private ISysDeptService sysDeptService;
    @Autowired
    private ProductStockInfoService productStockInfoService;


    @Override
    public Page<ProductInfoVo> getPage(ProductInfoSelectDto dto) {
        IWrapper<ProductInfo> wrapper = getPageWrapper(dto);
        Page<ProductInfoVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);

        List<ProductInfoVo> records = page.getRecords();

        if (records.size() == 0) {
            return page;
        }

        List<ProductClassify> productClassifyList = productClassifyService.list();
        Map<Long, ProductClassify> productClassifyMap = productClassifyList.stream().collect(Collectors.toMap(BaseIdPo::getId, Function.identity()));

        //赋值原材料信息
        List<Long> rawMaterialIds = records.stream().map(ProductInfo::getRawMaterialId).collect(Collectors.toList());
        Map<Long, ProductInfo> rawMaterialMap = new HashMap<>();
        if (ObjectUtil.isNotEmpty(rawMaterialIds)) {
            rawMaterialMap = this.mapKEntity(ProductInfo::getId, q -> q.in(ProductInfo::getId, rawMaterialIds));
        }

        //获取归属公司信息
        Map<Long, String> sysDeptMap = new HashMap<>();
        List<Long> companyIds = records.stream().map(ProductInfo::getCompanyId).collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(companyIds)) {
            List<SysDept> deptList = sysDeptService.list(Wrappers.<SysDept>query().in("dept_id", companyIds));
            sysDeptMap = deptList.stream().collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName));
        }

        for (ProductInfoVo record : records) {
            Long productClassifyId = record.getProductClassifyId();
            ProductClassify productClassify = productClassifyMap.get(productClassifyId);
            if (productClassify == null) {
                continue;
            }

            record.setClassifyName(productClassify.getName());

            List<String> classifyNameGroup = new ArrayList<>();

            while (productClassify != null) {
                classifyNameGroup.add(0, productClassify.getName());
                productClassify = productClassifyMap.get(productClassify.getParentId());
            }
            record.setClassifyNameGroup(classifyNameGroup);


            //赋值原材料信息
            ProductInfo productInfo = rawMaterialMap.get(record.getRawMaterialId());
            if (ObjectUtil.isNotEmpty(productInfo)) {
                record.setRawMaterialName(productInfo.getName());
                record.setRawMaterialCode(productInfo.getCustomCode());
            }

            //赋值归属公司名称
            record.setCompanyName(sysDeptMap.get(record.getCompanyId()));

        }

        return page;
    }

    private IWrapper<ProductInfo> getPageWrapper(ProductInfoSelectDto dto) {
        IWrapper<ProductInfo> wrapper = getWrapper();
        wrapper.eq("pi", ProductInfo::getType, dto.getType());
        wrapper.eq("pi", ProductInfo::getDefinition, dto.getDefinition());

        if (dto.getProductClassifyId() != null) {
            List<Long> childIdList = productClassifyService.getChildIdList(dto.getProductClassifyId());
            wrapper.in("pi", ProductInfo::getProductClassifyId, childIdList);
        }

        wrapper.like("pi", ProductInfo::getName, dto.getName());
        wrapper.like("pi", ProductInfo::getCustomCode, dto.getCustomCode());
        wrapper.like("pi", ProductInfo::getBarCode, dto.getBarCode());
        wrapper.keyword(dto.getKeyword(),
                new SqlField("pi", ProductInfo::getName),
                new SqlField("pi", ProductInfo::getCode),
                new SqlField("pi", ProductInfo::getCustomCode),
                new SqlField("pi", ProductInfo::getSpec)
        );

        //过滤产品是否配置原材料
        Integer isRawMaterial = dto.getIsRawMaterial();
        if (ObjectUtil.isNotEmpty(isRawMaterial)) {
            if (isRawMaterial.equals(1)) {
                //过滤有配置原材料的产品
                wrapper.isNotNull("pi.raw_material_id");
                //同时过滤未配置工艺的的产品
                wrapper.isNotNull("pi.technology_id");
            } else {
                //过滤没原材料的产品
                wrapper.isNull("pi.raw_material_id");
            }
        }

        //选择物料时不显示原材料分类的数据
        Integer isNeRawMaterial = dto.getIsNeRawMaterial();
        if (Objects.equals(isNeRawMaterial, 1)) {
            List<Long> pcIds = productClassifyService.listObject(ProductClassify::getId, q -> q
                    .eq(ProductClassify::getId, 100)
                    .or().apply("FIND_IN_SET(parent_id_set, 100)")
            );
            if (ObjectUtil.isNotEmpty(pcIds)) {
                wrapper.notIn("pi", ProductInfo::getProductClassifyId, pcIds);
            }
        }

        //权限过滤：产品-子公司看自己的产品，总公司看全部，物料没有限制
        Long companyId = SecurityUtils.getCompanyId();
        if (!Objects.equals(companyId, 100L)) {
            dto.setCompanyId(companyId);
        }
        wrapper.and(q1 -> q1.
                and(q -> q.eq("pi", ProductInfo::getDefinition, 1).eq("pi", ProductInfo::getCompanyId, dto.getCompanyId()))
                .or().eq("pi", ProductInfo::getDefinition, 2)
        );

        wrapper.orderByDesc("pi", ProductInfo::getId);
        return wrapper;
    }


    @Override
    public ProductInfoVo detail(Long id) {
        ProductInfo productInfo = this.getById(id);
        ProductInfoVo result = BeanUtil.toBean(productInfo, ProductInfoVo.class);

        //赋值分类名称
        ProductClassify productClassify = productClassifyService.getById(result.getProductClassifyId());
        if (ObjectUtil.isNotEmpty(productClassify)) {
            result.setClassifyName(productClassify.getName());
        }
        return result;
    }

    @DSTransactional
    @Override
    public void add(ProductInfoDto productInfoDto) {
        //赋值产品归属公司
        if (ObjectUtil.isEmpty(productInfoDto.getCompanyId())) {
            productInfoDto.setCompanyId(SecurityUtils.getCompanyId());
        }
        Long companyId = productInfoDto.getCompanyId();

        // 赋值产品编号
        productInfoDto.setCode(CodeEnum.PRODUCT.getCode());
        //检查【产品名称】和【规格型号】不能同时重复，但可以单项重复。
        long nameAndSpecCount = this.count(q -> q
                .eq(ProductInfo::getName, productInfoDto.getName())
                .eq(ProductInfo::getSpec, productInfoDto.getSpec())
                .eq(ProductInfo::getCompanyId, companyId)
        );
        if (nameAndSpecCount != 0) {
            throw new ServiceException("同名称同规格的产品已存在");
        }
        // 排除不为空的自定义编码重复
        long count = this.count(q -> q.eq(ProductInfo::getCustomCode, productInfoDto.getCustomCode())
                .ne(ProductInfo::getCustomCode, "")
                .eq(ProductInfo::getCompanyId, companyId)
        );
        if (count != 0) {
            throw new ServiceException("产品自定义编码重复");
        }
        // 排除不为空的条码编号重复
        long count1 = this.count(q -> q.eq(ProductInfo::getBarCode, productInfoDto.getBarCode())
                .ne(ProductInfo::getBarCode, "")
                .eq(ProductInfo::getCompanyId, companyId)
        );
        if (count1 != 0) {
            throw new ServiceException("产品条码编码重复");
        }

        //操作ftp文件
        editFtpFile(productInfoDto);

        this.save(productInfoDto);
        ObsFileUtil.saveFile(productInfoDto.getFileList(), productInfoDto.getId());
        ObsFileUtil.saveFile(productInfoDto.getProdFileList(), productInfoDto.getId(), 2);
    }

    @DSTransactional
    @Override
    public void edit(ProductInfoDto productInfoDto) {
        // 禁止产品编号修改
        productInfoDto.setCode(null);
        //检查【产品名称】和【规格型号】不能同时重复，但可以单项重复。
        long nameAndSpecCount = this.count(q -> q
                .ne(ProductInfo::getId, productInfoDto.getId())
                .eq(ProductInfo::getName, productInfoDto.getName())
                .eq(ProductInfo::getSpec, productInfoDto.getSpec())
        );
        if (nameAndSpecCount != 0) {
            throw new ServiceException("同名称同规格的产品已存在");
        }
        // 排除不为空的自定义编码重复
        long count = this.count(q -> q
                .ne(ProductInfo::getId, productInfoDto.getId())
                .eq(ProductInfo::getCustomCode, productInfoDto.getCustomCode())
                .ne(ProductInfo::getCustomCode, ""));
        if (count != 0) {
            throw new ServiceException("产品自定义编码重复");
        }

        //操作ftp文件
        editFtpFile(productInfoDto);

        //赋值产品归属公司
        Long companyId = productInfoDto.getCompanyId();
        if (ObjectUtil.isEmpty(companyId)) {
            productInfoDto.setCompanyId(SecurityUtils.getCompanyId());
        }

        this.updateById(productInfoDto);
        ObsFileUtil.editFile(productInfoDto.getFileList(), productInfoDto.getId());
        ObsFileUtil.editFile(productInfoDto.getProdFileList(), productInfoDto.getId(), 2);
    }

    @DSTransactional
    @Override
    public void delete(Long id) {
        this.removeById(id);
        ObsFileUtil.removeFile(id);
    }

    /**
     * 操作ftp文件
     */
    private void editFtpFile(ProductInfoDto productInfoDto) {
        //操作ftp文件
        String prodImgTempPath = productInfoDto.getProdImgPath();
//        String prodFileTempPath = productInfoDto.getProdFilePath();
        //生产图片
        if (ObjectUtil.isNotEmpty(prodImgTempPath) && prodImgTempPath.startsWith("/temp")) {
            String prodImgPath = String.format("/product/prodImg/%s", productInfoDto.getId());
            JSONObject prodImgJson = ftpFileService.moveFolder(prodImgTempPath, prodImgPath);
            productInfoDto.setProdImgPath(prodImgJson.getString("path"));
        }
//        //生产文件
//        if (ObjectUtil.isNotEmpty(prodFileTempPath) && prodFileTempPath.startsWith("/temp")) {
//            File prodFileTemp = new File(prodFileTempPath);
//            String prodFilePath = String.format("/product/prodFile/%s/%s", productInfoDto.getCustomCode(), prodFileTemp.getName());
//            JSONObject prodFileJson = ftpFileService.moveFolder(prodFileTempPath, prodFilePath);
//            productInfoDto.setProdFilePath(prodFileJson.getString("path"));
//        }
    }

    /**
     * 修改可用库存
     *
     * @param inOutList 产品列表
     */
    @DSTransactional
    @Override
    public synchronized void editAvailableQuantity(List<AvailableStockBo> inOutList, Long businessId, ProductAvailableRecordType businessType, Long companyId) {

        if (ObjectUtil.isEmpty(inOutList)) {
            return;
        }

        List<Long> materialIds = inOutList.stream().map(AvailableStockBo::getProductId).collect(Collectors.toList());
        Map<Long, ProductInfo> materialMap = this.mapKEntity(ProductInfo::getId, q -> q.in(ProductInfo::getId, materialIds));

        for (AvailableStockBo inOutBo : inOutList) {
            Long materialId = inOutBo.getProductId();
            BigDecimal quantity = inOutBo.getQuantity();

            //获取产品安全库存
            ProductInfo materialInfo = materialMap.get(materialId);
            BigDecimal stockThreshold = materialInfo.getStockThreshold();

            //获取可用库存
            ProductStockInfo productStockInfo = productStockInfoService.getOne(q -> q
                    .eq(ProductStockInfo::getProductId, inOutBo.getProductId())
                    .eq(ProductStockInfo::getCompanyId, companyId)
            );
            if (ObjectUtil.isEmpty(productStockInfo)) {
                productStockInfo = new ProductStockInfo();
                productStockInfo.setAvailableQuantity(BigDecimal.ZERO);
                productStockInfo.setCompanyId(companyId);
                productStockInfo.setProductId(inOutBo.getProductId());
                productStockInfoService.save(productStockInfo);
            }
            Long productStockInfoId = productStockInfo.getId();
            BigDecimal availableQuantity = productStockInfo.getAvailableQuantity();


            //下发订单 创建待采购
            if (ProductAvailableRecordType.SALE_PASS.equals(businessType)) {
                //计数需要采购的数量(需采购量 = 安全库存 - （可用库存 - 合同量）若 需采购量<0，则按0算不采购)
                BigDecimal subtract = availableQuantity.subtract(quantity);
                BigDecimal requiredQuantity = stockThreshold.subtract(subtract);

                //返回需采购量
                inOutBo.setQuantity(requiredQuantity);

                //计算可用库存
                if (requiredQuantity.compareTo(BigDecimal.ZERO) < 0) {
                    //需要采购的数量<0 可用库存 = 可用库存 - 合同量
                    availableQuantity = availableQuantity.subtract(quantity);
                } else {
                    //需要采购的数量>=0 可用库存 = 安全库存
                    availableQuantity = stockThreshold;
                }
            }
            //销售合同作废          可用库存=可用库存-((待采购量-已采购量)-合同量)
            //申购的-采购合同作废    可用库存 = 当前可用库存 - 采购明细数量
            //手动出库             可用库存 = 当前可用库存 - 出库数量
            //采购退货             可用库存 = 当前可用库存 - 退货数量
            else if (ProductAvailableRecordType.SALE_CANCEL.equals(businessType) || ProductAvailableRecordType.PURCHASE_CANCEL.equals(businessType) || ProductAvailableRecordType.HAND_OUT.equals(businessType) || ProductAvailableRecordType.PURCHASE_BACK.equals(businessType)) {
                availableQuantity = availableQuantity.subtract(quantity);
            }
            //申购的-采购合同通过    可用库存 = 当前可用库存 + 采购明细数量
            //手动入库             可用库存 = 当前可用库存 + 入库数量
            else if (ProductAvailableRecordType.PURCHASE_PASS.equals(businessType) || ProductAvailableRecordType.HAND_IN.equals(businessType)) {
                availableQuantity = availableQuantity.add(quantity);
            }
            //采购变更
            else if (ProductAvailableRecordType.PURCHASE_UPDATE.equals(businessType)) {
                BigDecimal newQuantity = inOutBo.getNewQuantity();

                if (ObjectUtils.isEmpty(quantity)) {
                    //新数据直接添加
                    availableQuantity = availableQuantity.add(newQuantity);
                } else if (ObjectUtils.isEmpty(newQuantity)) {
                    //原数据被删，已入库数量大于0
                    if (inOutBo.getInStockQuantity().compareTo(BigDecimal.ZERO) > 0) {
                        throw new ServiceException("该采购存在已入库数据禁止变更！");
                    }
                    //直接回滚
                    availableQuantity = availableQuantity.subtract(quantity);
                } else if (quantity.compareTo(newQuantity) < 0) {
                    //新数量比原数量多直接添加 差额
                    BigDecimal subtract = newQuantity.subtract(quantity);

                    availableQuantity = availableQuantity.add(subtract);
                } else if (quantity.compareTo(newQuantity) > 0) {
                    //新数量比原数量少检查已入库数量，如果比新数量大直接报错
                    if (newQuantity.compareTo(inOutBo.getInStockQuantity()) >= 0) {
                        BigDecimal subtract = quantity.subtract(newQuantity);
                        //新数量大等于已入库数量
                        availableQuantity = availableQuantity.subtract(subtract);
                    } else {
                        //新数量<已入库数量
                        throw new ServiceException("新采购量小于已入库数据，禁止变更！");
                    }
                }
            }
            //合同变更
            else if (ProductAvailableRecordType.SALE_UPDATE.equals(businessType)) {
                BigDecimal newQuantity = inOutBo.getNewQuantity();

                //后>前 可用=可用-（后-前）
                if (newQuantity.compareTo(inOutBo.getQuantity()) > 0) {
                    BigDecimal subtract = newQuantity.subtract(inOutBo.getQuantity());

                    //计数需要采购的数量(需采购量 = 安全库存 - （可用库存 - 合同量）若 需采购量<0，则按0算不采购)
                    BigDecimal subtract1 = availableQuantity.subtract(subtract);
                    BigDecimal requiredQuantity = stockThreshold.subtract(subtract1);
                    inOutBo.setNewQuantity(requiredQuantity);


                    //修改可用库存
                    availableQuantity = availableQuantity.subtract(subtract);


                }
                //后<前
                else if (newQuantity.compareTo(inOutBo.getQuantity()) < 0) {

                    //后>已采购 可用=可用-（后-前-已采+原需采购（数据库中的））
                    if (newQuantity.compareTo(inOutBo.getInStockQuantity()) > 0) {
                        BigDecimal subtract = newQuantity.subtract(inOutBo.getQuantity()).subtract(inOutBo.getInStockQuantity()).add(inOutBo.getDemandPurchaseCount());

                        //计算可用库存
                        availableQuantity = availableQuantity.subtract(subtract);
                    }
                    //后<已采购
                    else if (newQuantity.compareTo(inOutBo.getInStockQuantity()) < 0) {
                        //前待采购数量=0 可用=可用+（前-后）
                        if (inOutBo.getDemandPurchaseCount().compareTo(BigDecimal.ZERO) == 0) {
                            BigDecimal subtract1 = inOutBo.getQuantity().subtract(newQuantity);//前-后
                            availableQuantity = availableQuantity.add(subtract1);
                        }
                        //前待采购数量>0 可用=可用+（前-后）+(已采购 - 前待采购数量)
                        else if (inOutBo.getDemandPurchaseCount().compareTo(BigDecimal.ZERO) > 0) {
                            BigDecimal subtract1 = inOutBo.getQuantity().subtract(newQuantity);//前-后
                            BigDecimal subtract2 = inOutBo.getInStockQuantity().subtract(inOutBo.getDemandPurchaseCount());//已采购 - 前待采购数量
                            availableQuantity = availableQuantity.add(subtract1).add(subtract2);
                        }
                        //-------
                    }


                    //需采购量直接为0
                    inOutBo.setNewQuantity(BigDecimal.ZERO);

                }
            } else {
                throw new ServiceException("未知操作类型,请联系管理员！");
            }

            //可用库存
            if (availableQuantity.compareTo(BigDecimal.ZERO) < 0) {
                availableQuantity = stockThreshold;
            }

            //保存可用库存
            BigDecimal finalAvailableQuantity = availableQuantity;
            productStockInfoService.update(q -> q
                    .eq(ProductStockInfo::getId, productStockInfoId)
                    .set(ProductStockInfo::getAvailableQuantity, finalAvailableQuantity)
                    .set(BasePo::getUpdateTime, new Date())
                    .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
            );

        }


        //-------------------------------------

//        List<ProductAvailableRecord> availableRecordList = new ArrayList<>();

//            BigDecimal availableQuantityNew;
//            BigDecimal edQuantity = inOutBo.getQuantity();


//            if (inOutType.equals(InOutType.IN)) {
//                availableQuantityNew = availableQuantity.add(inOutBo.getQuantity());
//            } else if (inOutType.equals(InOutType.OUT)) {
//                availableQuantityNew = availableQuantity.subtract(inOutBo.getQuantity());
//            } else if (inOutType.equals(InOutType.EQ)) {
//                availableQuantityNew = inOutBo.getQuantity();
//
//                edQuantity = availableQuantityNew.subtract(availableQuantity);
//            } else {
//                throw new ServiceException("未知类型");
//            }

//            productStockInfoService.update(q -> q
//                    .eq(ProductStockInfo::getId, productStockInfoId)
//                    .set(ProductStockInfo::getAvailableQuantity, availableQuantityNew)
//                    .set(BasePo::getUpdateTime, new Date())
//                    .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
//            );

//            //保存操作记录
//            ProductAvailableRecord productAvailableRecord = new ProductAvailableRecord();
//            productAvailableRecord.setBeforeQuantity(productStockInfo.getAvailableQuantity());
//            productAvailableRecord.setAfterQuantity(availableQuantityNew);
//            productAvailableRecord.setEditQuantity(edQuantity);
//            productAvailableRecord.setBusinessId(businessId);
//            productAvailableRecord.setBusinessType(businessType.getType());
//            productAvailableRecord.setProductId(inOutBo.getProductId());
//            productAvailableRecord.setCompanyId(companyId);
//
//            availableRecordList.add(productAvailableRecord);
//        productAvailableRecordService.saveBatch(availableRecordList);
    }

    /**
     * 根据产品IDS获取产品
     *
     * @param productIds
     * @return
     */
    @Override
    public List<ProductInfoVo> getListByProductIds(List<Long> productIds) {
        return baseMapper.getListByProductIds(productIds);
    }

    /**
     * 根据产品类型获取产品
     *
     * @param productType
     * @return
     */
    @Override
    public List<ProductInfoVo> getListByProductType(Integer productType, Integer definition, String productName, String productCode) {
        return baseMapper.getListByProductType(productType, definition, productName, productCode);
    }


    /**
     * 产品统计（数据看板-产品分析页面）
     *
     * @param productInfoDto
     */
    @Override
    public Map<String, Object> productStatistics(ProductInfoDto productInfoDto) {
        if (ObjectUtil.isEmpty(productInfoDto) && ObjectUtil.isEmpty(productInfoDto.getBeginTime())
                && StringUtils.isNotEmpty(productInfoDto.getEndTime())) {
            throw new ServiceException("参数缺失：开始时间，结束时间不能为null");
        }
        //存放产品统计数据
        Map map = new HashMap();

        //存放产品类型数据
        List<Map<String, Object>> typeList = new ArrayList<>();
        QueryWrapper<ProductInfo> query = Wrappers.<ProductInfo>query();
        query.select("count(*) count,ifNull(type,999) type");
        query.groupBy("type");
        //查询每个类型的总计
        List<ProductInfo> productInfos = baseMapper.selectList(query);
        Map<String, List<ProductInfo>> productInfoTotalMap = productInfos.stream().collect(Collectors.groupingBy(ProductInfo::getType));
        query.ge("DATE_FORMAT(create_time,'%Y-%m-%d')", productInfoDto.getBeginTime());
        query.le("DATE_FORMAT(create_time,'%Y-%m-%d')", productInfoDto.getEndTime());
        //查询每个类型的新增数量
        List<ProductInfo> productInfoList = baseMapper.selectList(query);
        Map<String, List<ProductInfo>> productInfoTypeMap = productInfoList.stream().collect(Collectors.groupingBy(ProductInfo::getType));
        //计算新增总计
        Integer newTotal = 0;// productInfoList.stream().map(ProductInfo::getCount).reduce(Integer::sum).orElse(0);
        //总计
        Integer total = 0;//productInfos.stream().map(ProductInfo::getCount).reduce(Integer::sum).orElse(0);

        map.put("newTotal", newTotal);
        map.put("total", total);

        //获取产品类型的字典数据
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        List<DictTenantDataVo> dictTenantDataVoList = getDict("product_type");
        DynamicDataSourceContextHolder.poll();

        if (ObjectUtil.isEmpty(dictTenantDataVoList)) {
            return map;
        }

        for (DictTenantDataVo dictTenantDataVo : dictTenantDataVoList) {
            //存放每个类型的新增与总计
            Map<String, Object> map1 = new HashMap<>();
            //设置初使值
            map1.put("type", dictTenantDataVo.getDictKey());
            map1.put("typeNewTotal", 0);
            map1.put("typeTotal", 0);

            //赋值新增的款数
            List<ProductInfo> productInfoNewList = productInfoTypeMap.get(dictTenantDataVo.getDictKey());
            if (ObjectUtil.isNotEmpty(productInfoNewList)) {
                map1.put("typeNewTotal", 0);//productInfoNewList.get(0).getCount());
            }

            //赋值类型总计
            List<ProductInfo> productInfoTotalList = productInfoTotalMap.get(dictTenantDataVo.getDictKey());
            if (ObjectUtil.isNotEmpty(productInfoTotalList)) {
                map1.put("typeTotal", 0);//productInfoTotalList.get(0).getCount());
            }
            typeList.add(map1);
        }
        map.put("typeList", typeList);

        return map;
    }

    /**
     * 产品分布（数据看板-产品分析页面）
     *
     * @param productInfoDto
     */
    @Override
    public List<Map<String, Object>> productDistribution(ProductInfoDto productInfoDto) {
        if (ObjectUtil.isEmpty(productInfoDto) && ObjectUtil.isEmpty(productInfoDto.getBeginTime())
                && StringUtils.isNotEmpty(productInfoDto.getEndTime())) {
            throw new ServiceException("参数缺失：开始时间，结束时间不能为null");
        }
        //存放产品分布列表
        List<Map<String, Object>> list = new ArrayList<>();

        QueryWrapper<ProductInfo> query = Wrappers.<ProductInfo>query();
        query.select("count(*) count,ifnull(type,999) type");
        query.groupBy("type");
        query.ge("DATE_FORMAT(create_time,'%Y-%m-%d')", productInfoDto.getBeginTime());
        query.le("DATE_FORMAT(create_time,'%Y-%m-%d')", productInfoDto.getEndTime());
        //查询每个类型的总计
        List<ProductInfo> productInfos = baseMapper.selectList(query);
        if (productInfos.size() == 0) {
            new ArrayList<>();
        }
        Map<String, List<ProductInfo>> productInfoMap = productInfos.stream().collect(Collectors.groupingBy(ProductInfo::getType));
        //获取产品类型的字典数据
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        List<DictTenantDataVo> dictTenantDataVoList = getDict("product_type");
        DynamicDataSourceContextHolder.poll();

        if (dictTenantDataVoList.size() == 0) {
            new ArrayList<>();
        }

        //赋值
        for (DictTenantDataVo dictTenantDataVo : dictTenantDataVoList) {
            Map<String, Object> map = new HashMap<>();
            //初始化
            map.put("type", dictTenantDataVo.getDictKey());
            map.put("name", dictTenantDataVo.getDictValue());
            map.put("count", 0);

            List<ProductInfo> productInfoList = productInfoMap.get(dictTenantDataVo.getDictKey());
            if (ObjectUtil.isNotEmpty(productInfoList)) {
                map.put("count", 0);//productInfoList.get(0).getCount());
            }
            list.add(map);
        }
        return list;
    }

    /**
     * 产品类型排行（数据看板-产品分析页面）
     *
     * @param productInfoDto
     */
    @Override
    public List<Map<String, Object>> productTypeRanking(ProductInfoSelectDto productInfoDto) {
        //存放产品类型排行数据
        List<Map<String, Object>> list = new ArrayList<>();

        QueryWrapper<Object> query = Wrappers.query();
        String beginTime = DateUtil.format(productInfoDto.getBeginTime(), "yyyy-MM-dd");
        String endTime = DateUtil.format(productInfoDto.getEndTime(), "yyyy-MM-dd");
        query.ge("DATE_FORMAT(pc.create_time,'%Y-%m-%d')", beginTime);
        query.le("DATE_FORMAT(pc.create_time,'%Y-%m-%d')", endTime);
        query.eq(ObjectUtil.isNotEmpty(productInfoDto.getCountryId()), "bc.buy_country_id", productInfoDto.getCountryId());
//        sort(query,productInfoDto);
        query.groupBy("pi.type");
        //查询产品类型排行数据
        List<ProductInfoVo> productInfoVos = baseMapper.productTypeRanking(query);
        if (productInfoVos.size() == 0) {
            return list;
        }
        Map<String, List<ProductInfoVo>> productInfoVoMap = productInfoVos.stream().collect(Collectors.groupingBy(ProductInfoVo::getType));
        //获取产品类型的字典数据
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        List<DictTenantDataVo> dictTenantDataVoList = getDict("product_type");
        DynamicDataSourceContextHolder.poll();

        //赋值
        for (DictTenantDataVo dictTenantDataVo : dictTenantDataVoList) {
            //初始化
            Map<String, Object> map = new HashMap<>();
            map.put("type", dictTenantDataVo.getDictKey());
            map.put("name", dictTenantDataVo.getDictValue());
            map.put("contractAmount", new BigDecimal(0));
            map.put("contractQuantity", new BigDecimal(0));
            map.put("purchaseAmount", new BigDecimal(0));
            map.put("purchaseQuantity", new BigDecimal(0));
            //赋值
            List<ProductInfoVo> productInfoVos1 = productInfoVoMap.get(dictTenantDataVo.getDictKey());
            if (ObjectUtil.isNotEmpty(productInfoVos1)) {
                map.put("contractAmount", productInfoVos1.get(0).getContractAmount());
                map.put("contractQuantity", productInfoVos1.get(0).getContractQuantity());
                map.put("purchaseAmount", productInfoVos1.get(0).getPurchaseAmount());
                map.put("purchaseQuantity", productInfoVos1.get(0).getPurchaseQuantity());
            }
            list.add(map);
        }
        if (ObjectUtil.isNotEmpty(list)) {
            //排序
            list = typeSort(list, productInfoDto);
        }
        return list;
    }


    /**
     * 产品排行（数据看板-产品分析页面）
     *
     * @param productInfoDto
     */
    @Override
    public Page<ProductInfoVo> productRanking(ProductInfoSelectDto productInfoDto) {
        QueryWrapper<Object> query = Wrappers.query();
        String beginTime = DateUtil.format(productInfoDto.getBeginTime(), "yyyy-MM-dd");
        String endTime = DateUtil.format(productInfoDto.getEndTime(), "yyyy-MM-dd");
        query.ge("DATE_FORMAT(pc.create_time,'%Y-%m-%d')", beginTime);
        query.le("DATE_FORMAT(pc.create_time,'%Y-%m-%d')", endTime);
        query.eq(ObjectUtil.isNotEmpty(productInfoDto.getCountryId()), "bc.buy_country_id", productInfoDto.getCountryId());
        sort(query, productInfoDto);
        query.groupBy("pi.name");
        Page<ProductInfoVo> productInfoVos = baseMapper.productRanking(productInfoDto.getPage(), query);
        return productInfoVos;
    }

    /**
     * 产品统计
     *
     * @return
     */
    @Override
    public Map<String, Object> statisticsProduct() {
        return baseMapper.statisticsProduct();
    }

    /**
     * 产品库统计(根据产品库类型进行分类统计)
     */
    @Override
    public Map<String, Object> productInfoStatistics(ProductInfoSelectDto dto) {
        //存放产品库统计数据
        Map<String, Object> map = new HashMap<>();

        //存放产品分类统计数据
        List<Map<String, Object>> list = new ArrayList<>();
        QueryWrapper<ProductInfo> wrapper = Wrappers.query();
        wrapper.groupBy("type");
        wrapper.select("count(*) count, ifNull(type,-1) type");
        List<ProductInfo> productInfos = baseMapper.selectList(wrapper);
        Map<String, List<ProductInfo>> productInfoMap = productInfos.stream().collect(Collectors.groupingBy(ProductInfo::getType));

        //计算统计合计
        Integer amount = 0;//productInfos.stream().map(productInfo -> productInfo.getCount()).reduce(Integer::sum).orElse(0);

        map.put("amount", amount);
        //获取产品类型统计数据
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        List<DictTenantDataVo> dictTenantDataVoList = getDict("product_type");
        DynamicDataSourceContextHolder.poll();
        if (dictTenantDataVoList.size() == 0) {
            throw new ServiceException("数据异常：产品没有设置产品类型字典，请先添加");
        }

        for (DictTenantDataVo dictTenantDataVo : dictTenantDataVoList) {
            Map typeMap = new HashMap();
            //设置初始值
            typeMap.put("type", dictTenantDataVo.getDictValue());
            typeMap.put("count", 0);

            //赋值
            List<ProductInfo> productInfoList = productInfoMap.get(dictTenantDataVo.getDictKey());
            if (CollectionUtils.isNotEmpty(productInfoList)) {
                typeMap.put("count", 0);//productInfoList.get(0).getCount());
            }
            list.add(typeMap);

        }
        map.put("typeList", list);
        return map;
    }


    //根据字典编码获取字典的数据
    private List<DictTenantDataVo> getDict(String code) {
        DictTenantDataSelectDto dto = new DictTenantDataSelectDto();
        dto.setDictCode(code);
        List<DictTenantDataVo> dictTenantDataServiceList = dictTenantDataService.getList(dto);
        return dictTenantDataServiceList;
    }

    //排序(添加排序条件)
    private void sort(QueryWrapper<Object> query, ProductInfoSelectDto productInfoDto) {
        if (productInfoDto.getOrderBy() == 10) {//正序
            query.orderByAsc(productInfoDto.getSort() == 10, "contractQuantity");
            query.orderByAsc(productInfoDto.getSort() == 20, "contractAmount");
            query.orderByAsc(productInfoDto.getSort() == 30, "purchaseQuantity");
            query.orderByAsc(productInfoDto.getSort() == 40, "purchaseAmount");
        } else if (productInfoDto.getOrderBy() == 20) {//倒序
            query.orderByDesc(productInfoDto.getSort() == 10, "contractQuantity");
            query.orderByDesc(productInfoDto.getSort() == 20, "contractAmount");
            query.orderByDesc(productInfoDto.getSort() == 30, "purchaseQuantity");
            query.orderByDesc(productInfoDto.getSort() == 40, "purchaseAmount");
        }
    }

    //产品类型排序
    private List<Map<String, Object>> typeSort(List<Map<String, Object>> list, ProductInfoSelectDto productInfoSelectDto) {
        if (productInfoSelectDto.getOrderBy() == 10) {
            if (productInfoSelectDto.getSort() == 10) {
                list = list.stream()
                        .sorted(Comparator.comparing(m -> new BigDecimal(m.get("contractQuantity").toString())))
                        .collect(Collectors.toList());
                return list;
            } else if (productInfoSelectDto.getSort() == 20) {
                list = list.stream()
                        .sorted(Comparator.comparing(m -> new BigDecimal(m.get("contractAmount").toString())))
                        .collect(Collectors.toList());
                return list;
            } else if (productInfoSelectDto.getSort() == 30) {
                list = list.stream()
                        .sorted(Comparator.comparing(m -> new BigDecimal(m.get("purchaseQuantity").toString())))
                        .collect(Collectors.toList());
                return list;
            } else if (productInfoSelectDto.getSort() == 40) {
                list = list.stream()
                        .sorted(Comparator.comparing(m -> new BigDecimal(m.get("purchaseAmount").toString())))
                        .collect(Collectors.toList());
                return list;
            }
        } else {
            if (productInfoSelectDto.getSort() == 10) {
                list = list.stream()
                        .sorted(Comparator.comparing(m -> new BigDecimal(m.get("contractQuantity").toString())))
                        .collect(Collectors.toList());
                Collections.reverse(list);
                return list;
            } else if (productInfoSelectDto.getSort() == 20) {
                list = list.stream()
                        .sorted(Comparator.comparing(m -> new BigDecimal(m.get("contractAmount").toString())))
                        .collect(Collectors.toList());
                Collections.reverse(list);
                return list;
            } else if (productInfoSelectDto.getSort() == 30) {
                list = list.stream()
                        .sorted(Comparator.comparing(m -> new BigDecimal(m.get("purchaseQuantity").toString())))
                        .collect(Collectors.toList());
                Collections.reverse(list);
                return list;
            } else if (productInfoSelectDto.getSort() == 40) {
                list = list.stream()
                        .sorted(Comparator.comparing(m -> new BigDecimal(m.get("purchaseAmount").toString())))
                        .collect(Collectors.toList());
                Collections.reverse(list);
                return list;
            }
        }
        return list;
    }

    @Override
    public Page<ProductAnalysisBo> getProductAnalysisPage(Page<Object> page, IWrapper<ProductInfo> wrapper) {
        return baseMapper.getProductAnalysisPage(page, wrapper);
    }

    @Override
    public void excelExport(HttpServletResponse httpServletResponse, ProductInfoSelectDto dto) {
        IWrapper<ProductInfo> wrapper = getPageWrapper(dto);
        List<ProductInfoVo> productInfoList = baseMapper.getList(wrapper);
        List<Long> pIds = productInfoList.stream().map(ProductInfo::getId).collect(Collectors.toList());

        //处理产品图片
        Map<Long, List<FileInfoVo>> fileMap = ObsFileUtil.getFileMap(pIds, 0);

        List<ProductExcelExportBo> productExcelExportBoList = new ArrayList<>();

        Map<String, String> frontLinesMap = DictUtils.getDictMap("front_lines");
        Map<String, String> backLinesMap = DictUtils.getDictMap("back_lines");

        for (ProductInfo productInfo : productInfoList) {
            //处理长宽高
            BigDecimal length = productInfo.getLength();
            BigDecimal width = productInfo.getWidth();
            BigDecimal height = productInfo.getHeight();
            length = ObjectUtil.isEmpty(length) ? BigDecimal.ZERO : length;
            width = ObjectUtil.isEmpty(width) ? BigDecimal.ZERO : width;
            height = ObjectUtil.isEmpty(height) ? BigDecimal.ZERO : height;

            ProductExcelExportBo bo = new ProductExcelExportBo();
            //获取产品图片信息
            List<FileInfoVo> fileInfoVoList = fileMap.get(productInfo.getId());
            if (ObjectUtil.isNotEmpty(fileInfoVoList)) {
                try {
                    String fileUrl = fileInfoVoList.get(0).getFileUrl();

                    //解析图片后缀
                    int lastIndex = fileUrl.lastIndexOf(".");
                    String suffix = fileUrl.substring(lastIndex + 1, fileUrl.length());

                    BufferedImage image = ImageIO.read(new URL(fileUrl));
                    if (ObjectUtil.isNotEmpty(image)) {
                        //获取图片宽高，用以计算比例
                        Double imgWidth = Double.valueOf(image.getWidth());
                        Double imgHeight = Double.valueOf(image.getHeight());
                        //------------------
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        ImageIO.write(image, suffix, baos);
                        WriteCellData<Void> voidWriteCellData = ExcelImgUtils.imageCells(baos.toByteArray(), imgWidth, imgHeight, 10.0 * 5.8, 50.0);
                        bo.setProductImg(voidWriteCellData);
                    }
                } catch (IOException e) {
                    throw new ServiceException("导出失败" + e.getMessage());
                }
            }

            bo.setProductCode(productInfo.getCustomCode());
            bo.setProductName(productInfo.getName());
            bo.setProductSize(String.format("%s * %s * %s", length, width, height));
            bo.setProductColor(productInfo.getColor());


            bo.setFrontalTexture(frontLinesMap.getOrDefault(productInfo.getFrontalTexture(), productInfo.getFrontalTexture()));
            bo.setReverseTexture(backLinesMap.getOrDefault(productInfo.getReverseTexture(), productInfo.getReverseTexture()));

            //赋值生产图片
            List<String> prodImgList = ftpFileService.getFileList(productInfo.getProdImgPath());
            if (ObjectUtil.isNotEmpty(prodImgList)) {
                bo.setProdImgPath(FtpFileServiceImpl.fileServiceUrl + prodImgList.get(0));
            }

            productExcelExportBoList.add(bo);
        }

        ExcelUtil.export(httpServletResponse, productExcelExportBoList, ProductExcelExportBo.class);
    }

    @Override
    public Page<ProductInfoVo> getProductByMaterialId(ProductInfoSelectDto dto) {
        IWrapper<Object> wrapper = IWrapper.getWrapper();
        wrapper.eq("pbd.material_id", dto.getMaterialId());

        //权限过滤：产品-子公司看自己的产品，总公司看全部，物料没有限制
        Long companyId = SecurityUtils.getCompanyId();
        if (Objects.equals(companyId, 100L)) {
            companyId = null;
        }
        wrapper.eq("pi.company_id", companyId);
        return baseMapper.getProductByMaterialId(dto.getPage(), wrapper);
    }

    @Override
    public List<ProcessesBo> getProcessesByTechnologyId(Long technologyId) {
        return baseMapper.getProcessesByTechnologyId(technologyId);
    }

}
