package com.czg.service.product.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.czg.account.entity.ShopConfig;
import com.czg.account.entity.ShopInfo;
import com.czg.account.enums.BranchDataSyncMethodEnum;
import com.czg.account.service.ShopConfigService;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.SyncNoticeService;
import com.czg.constant.CacheConstant;
import com.czg.exception.CzgException;
import com.czg.market.entity.ShopCoupon;
import com.czg.market.service.ShopCouponService;
import com.czg.product.entity.*;
import com.czg.product.service.*;
import com.czg.product.vo.ProductGroupVo;
import com.czg.utils.AssertUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.DbChain;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static com.mybatisflex.core.query.QueryMethods.column;

/**
 * @author ww
 * @description
 */
@Slf4j
@DubboService
public class ShopSyncServiceImpl implements ShopSyncService {
    @DubboReference
    private ShopInfoService shopInfoService;
    @DubboReference
    private ShopConfigService shopConfigService;
    @Resource
    private ShopProdUnitService unitService;
    @Resource
    private ShopProdSpecService specService;
    @Resource
    private ShopProdCategoryService categoryService;
    @Resource
    private ProductService productService;
    @Resource
    private ProdSkuService skuService;
    @Resource
    private ProdGroupService groupService;
    @Resource
    private ProdGroupRelationService prodGroupRelationService;
    @Resource
    private ConsInfoService consInfoService;
    @Resource
    private ConsGroupService consGroupService;
    @Resource
    private ProdConsRelationService prodConsRelationService;
    @Resource
    private ProductRpcService productRpcService;
    @Resource
    private ShopVendorService vendorService;
    @DubboReference
    private ShopCouponService couponService;
    @DubboReference
    private SyncNoticeService syncNoticeService;

    private void checkShopInfo(Long sourceShopId, Long targetShopId) {
        AssertUtil.isNull(sourceShopId, "{}不能为空", "源店铺ID");
        AssertUtil.isNull(targetShopId, "{}不能为空", "目标店铺ID");
        ShopInfo sourceShop = shopInfoService.getById(sourceShopId);
        if (StrUtil.isBlank(sourceShop.getShopType()) || "only".equals(sourceShop.getShopType())
                || sourceShop.getIsHeadShop() == null || sourceShop.getIsHeadShop() != 1) {
            throw new CzgException("同步失败,源店铺不是主店铺或源店铺是单店");
        }
        ShopInfo targetShop = shopInfoService.getById(targetShopId);
        if (StrUtil.isBlank(targetShop.getShopType()) || "only".equals(targetShop.getShopType())
                || targetShop.getIsHeadShop() == null || targetShop.getIsHeadShop().equals(1)) {
            throw new CzgException("同步失败,目标店铺是主店铺或目标店铺是单店");
        }
    }

    /**
     * 校验主店
     *
     * @param sourceShopId 主店铺ID
     * @param type         1-同步商品内容 2-内部耗材内容
     * @return 开启该开关的子店集合
     */
    private Set<Long> checkSourceShopInfo(Long sourceShopId, int type) {
        AssertUtil.isNull(sourceShopId, "{}不能为空", "源店铺ID");
        ShopInfo sourceShop = shopInfoService.getById(sourceShopId);
        if (StrUtil.isBlank(sourceShop.getShopType()) || "only".equals(sourceShop.getShopType())
                || sourceShop.getIsHeadShop() == null || sourceShop.getIsHeadShop() != 1) {
            log.error("同步失败,源店铺不是主店铺或源店铺是单店");
            return null;
        }
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.select(column(ShopConfig::getId));
        queryWrapper.eq(ShopConfig::getMainId, sourceShopId);
        switch (type) {
            case 1:
                queryWrapper.eq(ShopConfig::getIsEnableProdSync, 1);
                break;
            case 2:
                queryWrapper.eq(ShopConfig::getIsEnableConsSync, 1);
                break;
        }
        List<Long> ids = shopConfigService.listAs(queryWrapper, Long.class);
        return new HashSet<>(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, key = "#targetShopId")
    public void sync(Long sourceShopId, Long targetShopId, Long sysUserId) {
        log.info("同步开始,源店铺ID:{},目标店铺ID:{}", sourceShopId, targetShopId);
        try {
            Map<Long, Long> unitMap;
            Map<Long, Long> specMap;
            Map<Long, Long> categoryMap;
            Map<Long, Long> proMap = new HashMap<>();
            Map<Long, Long> skuMap = new HashMap<>();
            checkShopInfo(sourceShopId, targetShopId);
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            //商品
            try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
                CompletableFuture<Map<Long, Long>> futureUnit = CompletableFuture.supplyAsync(() -> syncUnit(sourceShopId, targetShopId, mainMapList), executor);
                CompletableFuture<Map<Long, Long>> futureSpec = CompletableFuture.supplyAsync(() -> syncSpec(sourceShopId, targetShopId, mainMapList), executor);
                CompletableFuture<Map<Long, Long>> futureCategory = CompletableFuture.supplyAsync(() -> syncCategory(sourceShopId, targetShopId, mainMapList), executor);

                CompletableFuture<Void> allFutures = CompletableFuture.allOf(futureUnit, futureSpec, futureCategory);
                allFutures.join();

                unitMap = futureUnit.join();
                specMap = futureSpec.join();
                categoryMap = futureCategory.join();
            } catch (Exception e) {
                log.error("同步异常", e);
                throw new CzgException("同步失败");
            }
            proMap = syncProduct(sourceShopId, targetShopId, unitMap, specMap, categoryMap, mainMapList);
            skuMap = syncSku(sourceShopId, targetShopId, proMap);
            syncGroup(sourceShopId, targetShopId, proMap, mainMapList);
            syncProductPackage(targetShopId, proMap, skuMap);


            //耗材
            Map<Long, Long> conGroupMap = syncConsGroup(sourceShopId, targetShopId, mainMapList);
            Map<Long, Long> consInfoMap = syncConsInfo(sourceShopId, targetShopId, conGroupMap, mainMapList);
            syncConsPro(sourceShopId, targetShopId, consInfoMap, proMap);
            syncVendor(sourceShopId, targetShopId, mainMapList);
            syncNoticeService.addNotice(targetShopId, sysUserId, "数据同步", JSON.toJSONString(mainMapList), null);
            categoryMap.forEach((k, v) -> {
                productRpcService.cleanCategoryProduct(targetShopId, v);
            });
        } catch (Exception e) {
            log.error("同步异常", e);
            throw new CzgException("同步失败");
        }
        log.info("同步结束,源店铺ID:{},目标店铺ID:{}", sourceShopId, targetShopId);

    }

    /**
     * 构建消息
     *
     * @param mainMapList 主消息集合
     * @param title       内容title  "XX同步/变动",//商品 单位 规格 分组 耗材 耗材分组
     * @param size        变动数量
     * @param dataMap     变动数据集合<Id,名称>
     */
    private void buildNotice(List<Map<String, Object>> mainMapList, String title, int size, String type, Map<Long, String> dataMap) {
        Map<String, Object> mainJsonMap = new HashMap<>();
        mainJsonMap.put("title", title);
        mainJsonMap.put("number", size + "个");
        if (StrUtil.isNotBlank(type)) {
            mainJsonMap.put("type", type);
        }
        if (CollUtil.isNotEmpty(dataMap)) {
            List<Map<String, Object>> mapList = new ArrayList<>();
            dataMap.forEach((k, v) -> {
                Map<String, Object> innerMap = new HashMap<>();
                innerMap.put("id", k);
                innerMap.put("name", v);
                mapList.add(innerMap);
            });
            mainJsonMap.put("map", mapList);
        }
        mainMapList.add(mainJsonMap);
    }

    public Map<Long, Long> syncUnit(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> unitMap = new HashMap<>();
        Map<Long, ShopProdUnit> unitEntityMap = new HashMap<>();
        List<Long> pointShopUnits = new ArrayList<>();
        List<ShopProdUnit> pointUnits = unitService.queryChain()
                .eq(ShopProdUnit::getShopId, pointShopId)
                .isNotNull(ShopProdUnit::getSyncId)
                .list();
        pointUnits.stream().forEach(tbShopUnit -> {
            pointShopUnits.add(tbShopUnit.getSyncId());
            unitEntityMap.put(tbShopUnit.getSyncId(), tbShopUnit);
        });
        List<ShopProdUnit> list = unitService.queryChain().eq(ShopProdUnit::getShopId, sourceShopId).list();
        for (ShopProdUnit tbShopUnit : list) {
            if (CollUtil.isEmpty(pointShopUnits) || !pointShopUnits.contains(tbShopUnit.getId())) {
                ShopProdUnit unitNew = BeanUtil.copyProperties(tbShopUnit, ShopProdUnit.class);
                unitNew.setId(null);
                unitNew.setSyncId(tbShopUnit.getId());
                unitNew.setShopId(pointShopId);
                unitService.save(unitNew);
                unitMap.put(tbShopUnit.getId(), unitNew.getId());
            } else {
                ShopProdUnit unit = unitEntityMap.get(tbShopUnit.getId());
                if (unit == null) {
                    continue;
                }
                unit.setUnitType(tbShopUnit.getUnitType());
                unit.setName(tbShopUnit.getName());
                unit.setStatus(tbShopUnit.getStatus());
                unitService.updateById(unit, false);
                unitMap.put(unit.getSyncId(), unit.getId());
            }
        }

        buildNotice(mainMapList, "单位同步", pointShopUnits.size(), null, null);
        buildNotice(mainMapList, "单位新增", unitMap.size() - pointShopUnits.size(), null, null);
        log.info("单位同步,源{}个,已有{}个,同步{}个", list.size(), pointShopUnits.size(), unitMap.size() - pointShopUnits.size());
        return unitMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, allEntries = true)
    public void syncUnitBySourceShop(Long sourceShopId, Long unitId, Long sysUserId) {
        assertAutoSync(sourceShopId);
        AssertUtil.isNull(unitId, "{}不能为空", "单位ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ShopProdUnit mainUnit = unitService.getById(unitId);
        AssertUtil.isNull(mainUnit, "需要同步的{}不存在", "单位");
        List<ShopProdUnit> list = unitService.queryChain().eq(ShopProdUnit::getSyncId, unitId).in(ShopProdUnit::getShopId, shopIds).list();
        Map<Long, ShopProdUnit> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ShopProdUnit::getShopId, t -> t));
        }
        Map<Long, ShopProdUnit> finalMap = map;
        shopIds.forEach(shopId -> {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> unitDataMap = new HashMap<>();
            ShopProdUnit newUnit = BeanUtil.copyProperties(mainUnit, ShopProdUnit.class);

            newUnit.setSyncId(mainUnit.getId());
            newUnit.setShopId(shopId);
            if (finalMap.containsKey(shopId)) {
                ShopProdUnit unit = finalMap.get(shopId);
                newUnit.setId(unit.getId());
                unitService.updateById(newUnit);
            } else {
                newUnit.setId(null);
                unitService.save(newUnit);
            }
            unitDataMap.put(newUnit.getId(), newUnit.getName());
            buildNotice(mainMapList, "单位变动", 1, "unit", unitDataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newUnit));
        });
    }

    //规格
    public Map<Long, Long> syncSpec(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> specMap = new HashMap<>();
        Map<Long, ShopProdSpec> specEntityMap = new HashMap<>();
        List<Long> pointShopSpecs = new ArrayList<>();
        List<ShopProdSpec> newPointList = new ArrayList<>();
        List<ShopProdSpec> pointList = specService.queryChain()
                .eq(ShopProdSpec::getShopId, pointShopId)
                .isNotNull(ShopProdSpec::getSyncId)
                .list();
        for (ShopProdSpec shopProdSpec : pointList) {
            pointShopSpecs.add(shopProdSpec.getSyncId());
            specEntityMap.put(shopProdSpec.getSyncId(), shopProdSpec);
        }
        List<ShopProdSpec> list = specService.queryChain().eq(ShopProdSpec::getShopId, sourceShopId.toString()).list();
        for (ShopProdSpec spec : list) {
            if (CollUtil.isEmpty(pointShopSpecs) || !pointShopSpecs.contains(spec.getId())) {
                ShopProdSpec newSpec = BeanUtil.copyProperties(spec, ShopProdSpec.class);
                newSpec.setId(null);
                newSpec.setSyncId(spec.getId());
                newSpec.setShopId(pointShopId);
                specService.save(newSpec);
                newPointList.add(newSpec);
                specMap.put(spec.getId(), newSpec.getId());
            } else {
                ShopProdSpec newSpec = specEntityMap.get(spec.getId());
                if (newSpec == null) {
                    continue;
                }
                newSpec.setPid(spec.getPid());
                newSpec.setPids(spec.getPids());
                newSpec.setName(spec.getName());
                newSpec.setFullName(spec.getFullName());
                newSpec.setLevel(spec.getLevel());
                newSpec.setSort(spec.getSort());
                newSpec.setStatus(spec.getStatus());
                specService.updateById(newSpec, false);
                specMap.put(spec.getId(), newSpec.getId());
                newPointList.add(newSpec);
            }
        }
        for (ShopProdSpec shopProdSpec : newPointList) {
            if (shopProdSpec.getPid() != null && !shopProdSpec.getPid().equals(0L)) {
                shopProdSpec.setPid(specMap.get(shopProdSpec.getPid()));
                String[] split = shopProdSpec.getPids().split(",");
                for (String pid : split) {
                    if ("0".equals(pid)) {
                        shopProdSpec.setPids("0,");
                    } else {
                        if (!"null".equals(pid) && specMap.containsKey(Long.valueOf(pid))) {
                            shopProdSpec.setPids(shopProdSpec.getPids() + specMap.get(Long.valueOf(pid)) + ",");
                        }
                    }
                }
                specService.updateById(shopProdSpec);
            }
        }
        log.info("规格同步,源{}个,已有{}个,同步{}个", list.size(), pointShopSpecs.size(), specMap.size() - pointShopSpecs.size());
        buildNotice(mainMapList, "规格同步", pointShopSpecs.size(), null, null);
        buildNotice(mainMapList, "规格新增", specMap.size() - pointShopSpecs.size(), null, null);
        return specMap;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, allEntries = true)
    public void syncSpecBySourceShop(Long sourceShopId, Long specId, Long sysUserId) {
        assertAutoSync(sourceShopId);
        AssertUtil.isNull(specId, "{}不能为空", "规格ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ShopProdSpec mainEntity = specService.getById(specId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "规格");
        List<ShopProdSpec> list = specService.queryChain().eq(ShopProdSpec::getSyncId, specId).in(ShopProdSpec::getShopId, shopIds).list();
        Map<Long, ShopProdSpec> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ShopProdSpec::getShopId, t -> t));
        }
        Map<Long, ShopProdSpec> finalMap = map;
        shopIds.forEach(shopId -> {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ShopProdSpec newEntity = BeanUtil.copyProperties(mainEntity, ShopProdSpec.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            if (finalMap.containsKey(shopId)) {
                ShopProdSpec childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                specService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                specService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "规格变动", 1, "spec", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        });
    }

    // 分类
    public Map<Long, Long> syncCategory(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> categoryMap = new HashMap<>();
        Map<Long, ShopProdCategory> categoryEntityMap = new HashMap<>();
        List<Long> pointShopCateGory = new ArrayList<>();
        List<ShopProdCategory> newPointShopCateGory = new ArrayList<>();
        List<ShopProdCategory> pointList = categoryService.queryChain()
                .eq(ShopProdCategory::getShopId, pointShopId)
                .isNotNull(ShopProdCategory::getSyncId)
                .list();
        for (ShopProdCategory shopProdCategory : pointList) {
            pointShopCateGory.add(shopProdCategory.getSyncId());
            categoryEntityMap.put(shopProdCategory.getSyncId(), shopProdCategory);
        }
        List<ShopProdCategory> tbShopCategories = categoryService.queryChain()
                .eq(ShopProdCategory::getShopId, sourceShopId)
                .eq(ShopProdCategory::getPid, 0)
                .list();
        List<Long> treeIds = new ArrayList<>();
        for (ShopProdCategory shopProdCategory : tbShopCategories) {
            treeIds.add(shopProdCategory.getId());
            if (CollUtil.isEmpty(pointShopCateGory) || !pointShopCateGory.contains(shopProdCategory.getId())) {
                ShopProdCategory tbShopCategoryNew = BeanUtil.copyProperties(shopProdCategory, ShopProdCategory.class);
                tbShopCategoryNew.setId(null);
                tbShopCategoryNew.setShopId(pointShopId);
                tbShopCategoryNew.setSyncId(shopProdCategory.getId());
                categoryService.save(tbShopCategoryNew);
                categoryMap.put(shopProdCategory.getId(), tbShopCategoryNew.getId());
                newPointShopCateGory.add(tbShopCategoryNew);
            } else {
                ShopProdCategory tbShopCategoryNew = categoryEntityMap.get(shopProdCategory.getId());
                if (tbShopCategoryNew == null) {
                    continue;
                }
                tbShopCategoryNew.setName(shopProdCategory.getName());
                tbShopCategoryNew.setShortName(shopProdCategory.getShortName());
                tbShopCategoryNew.setPic(shopProdCategory.getPic());
                tbShopCategoryNew.setDetail(shopProdCategory.getDetail());
                tbShopCategoryNew.setSort(shopProdCategory.getSort());
                tbShopCategoryNew.setKeyWord(shopProdCategory.getKeyWord());
                tbShopCategoryNew.setStatus(shopProdCategory.getStatus());
                categoryService.updateById(tbShopCategoryNew, false);
                categoryMap.put(shopProdCategory.getId(), tbShopCategoryNew.getId());
                newPointShopCateGory.add(tbShopCategoryNew);
            }
        }
        int childrenSize = 0;
        if (CollectionUtil.isNotEmpty(treeIds)) {
            List<ShopProdCategory> children = categoryService.queryChain()
                    .eq(ShopProdCategory::getShopId, sourceShopId)
                    .in(ShopProdCategory::getPid, treeIds)
                    .list();
            childrenSize = children.size();
            for (ShopProdCategory child : children) {
                if (CollUtil.isEmpty(pointShopCateGory) || !pointShopCateGory.contains(child.getId())) {
                    ShopProdCategory tbShopCategoryNew = BeanUtil.copyProperties(child, ShopProdCategory.class);
                    tbShopCategoryNew.setId(null);
                    tbShopCategoryNew.setShopId(pointShopId);
                    tbShopCategoryNew.setSyncId(child.getId());
                    tbShopCategoryNew.setPid(categoryMap.get(child.getPid()));
                    categoryService.save(tbShopCategoryNew);
                    categoryMap.put(child.getId(), tbShopCategoryNew.getId());
                    newPointShopCateGory.add(tbShopCategoryNew);
                } else {
                    ShopProdCategory tbShopCategoryNew = categoryEntityMap.get(child.getId());
                    tbShopCategoryNew.setName(child.getName());
                    tbShopCategoryNew.setShortName(child.getShortName());
                    tbShopCategoryNew.setPic(child.getPic());
                    tbShopCategoryNew.setDetail(child.getDetail());
                    tbShopCategoryNew.setSort(child.getSort());
                    tbShopCategoryNew.setKeyWord(child.getKeyWord());
                    tbShopCategoryNew.setStatus(child.getStatus());
                    categoryService.updateById(tbShopCategoryNew, false);
                    categoryMap.put(child.getId(), tbShopCategoryNew.getId());
                    newPointShopCateGory.add(tbShopCategoryNew);
                }
            }
        }
//        for (ShopProdCategory shopProdCategory : newPointShopCateGory) {
//            shopProdCategory.getPid()
//        }
        log.info("分类同步,同步{}个", categoryMap.size());
        buildNotice(mainMapList, "分类同步", tbShopCategories.size() + childrenSize - pointShopCateGory.size(), null, null);
        buildNotice(mainMapList, "分类新增", categoryMap.size() - pointShopCateGory.size(), null, null);
        return categoryMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, allEntries = true)
    public void syncCategoryBySourceShop(Long sourceShopId, Long categoryId, Long sysUserId) {
        assertAutoSync(sourceShopId);
        AssertUtil.isNull(categoryId, "{}不能为空", "分类ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ShopProdCategory mainEntity = categoryService.getById(categoryId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "分类");
        List<ShopProdCategory> list = categoryService.queryChain().eq(ShopProdCategory::getSyncId, categoryId).in(ShopProdCategory::getShopId, shopIds).list();
        Map<Long, ShopProdCategory> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ShopProdCategory::getShopId, t -> t));
        }
        Map<Long, Long> categoryMap = new HashMap<>();
        if (mainEntity.getPid() != null) {
            List<ShopProdCategory> categoryList = categoryService.queryChain().eq(ShopProdCategory::getSyncId, mainEntity.getPid()).in(ShopProdCategory::getShopId, shopIds).list();
            if (CollUtil.isNotEmpty(categoryList)) {
                categoryMap = categoryList.stream().collect(Collectors.toMap(ShopProdCategory::getShopId, ShopProdCategory::getId));
            }
        }
        Map<Long, ShopProdCategory> finalMap = map;
        for (Long shopId : shopIds) {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ShopProdCategory newEntity = BeanUtil.copyProperties(mainEntity, ShopProdCategory.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            newEntity.setPid(newEntity.getPid() != null ? categoryMap.get(shopId) : null);
            if (finalMap.containsKey(shopId)) {
                ShopProdCategory childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                categoryService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                categoryService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "分类变动", 1, "category", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        }
    }

    public Map<Long, Long> syncProduct(Long sourceShopId, Long pointShopId, Map<Long, Long> unitMap,
                                       Map<Long, Long> specMap, Map<Long, Long> cateGoryMap, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> proMap = new HashMap<>();
        Map<Long, Product> pointMap = new HashMap<>();
        List<Long> pointProducts = new ArrayList<>();
        List<Product> pointList = productService.queryChain()
                .eq(Product::getShopId, pointShopId)
                .isNotNull(Product::getSyncId)
                .list();
        for (Product product : pointList) {
            pointProducts.add(product.getSyncId());
            pointMap.put(product.getSyncId(), product);
        }
        List<Product> products = productService.queryChain().eq(Product::getShopId, sourceShopId).list();
        if (CollectionUtil.isNotEmpty(products)) {
            unitService.queryChain().eq(ShopProdUnit::getIsSystem, 1).list().forEach(tbShopUnit -> {
                unitMap.put(tbShopUnit.getId(), tbShopUnit.getId());
            });
            for (Product tbProduct : products) {
                if (CollUtil.isEmpty(pointProducts) || !pointProducts.contains(tbProduct.getId())) {
                    Product tbProductNew = BeanUtil.copyProperties(tbProduct, Product.class);
                    tbProductNew.setId(null);
                    tbProductNew.setSyncId(tbProduct.getId());
                    tbProductNew.setShopId(pointShopId);
                    tbProductNew.setCategoryId(tbProduct.getCategoryId() != null ? cateGoryMap.get(tbProduct.getCategoryId()) : null);
                    tbProductNew.setSpecId(tbProduct.getSpecId() != null ? specMap.get(tbProduct.getSpecId()) : null);
                    tbProductNew.setUnitId(tbProduct.getUnitId() != null ? unitMap.get(tbProduct.getUnitId()) : null);
                    if (CollUtil.isNotEmpty(pointProducts)) {
                        tbProductNew.setIsSale(0);
                    }
                    productService.save(tbProductNew);
                    proMap.put(tbProduct.getId(), tbProductNew.getId());
                } else {
                    Product tbProductNew = pointMap.get(tbProduct.getId());
                    if (tbProductNew == null) {
                        continue;
                    }
                    tbProductNew.setCategoryId(tbProduct.getCategoryId() != null ? cateGoryMap.get(tbProduct.getCategoryId()) : null);
                    tbProductNew.setSpecId(tbProduct.getSpecId() != null ? specMap.get(tbProduct.getSpecId()) : null);
                    tbProductNew.setUnitId(tbProduct.getUnitId() != null ? unitMap.get(tbProduct.getUnitId()) : null);
                    tbProductNew.setName(tbProduct.getName());
                    tbProductNew.setShortTitle(tbProduct.getShortTitle());
                    tbProductNew.setType(tbProduct.getType());
                    tbProductNew.setGroupType(tbProduct.getGroupType());
                    tbProductNew.setPackFee(tbProduct.getPackFee());
                    tbProductNew.setIsAutoSoldStock(tbProduct.getIsAutoSoldStock());
                    tbProductNew.setRefundMode(tbProduct.getRefundMode());
                    tbProductNew.setCoverImg(tbProduct.getCoverImg());
                    tbProductNew.setImages(tbProduct.getImages());
                    tbProductNew.setWeight(tbProduct.getWeight());
                    tbProductNew.setSelectSpecInfo(tbProduct.getSelectSpecInfo());
                    tbProductNew.setSort(tbProduct.getSort());
                    tbProductNew.setIsDel(tbProduct.getIsDel());
                    tbProductNew.setGroupSnap(tbProduct.getGroupSnap());
                    productService.updateById(tbProductNew, false);
                    proMap.put(tbProductNew.getSyncId(), tbProductNew.getId());
                }
            }
        }
        log.info("商品同步,源{}个,已有{}个,同步{}个", products.size(), pointProducts.size(), proMap.size() - pointProducts.size());
        buildNotice(mainMapList, "商品同步", pointProducts.size(), null, null);
        buildNotice(mainMapList, "商品新增", proMap.size() - pointProducts.size(), null, null);
        return proMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, allEntries = true)
    public void syncProductBySourceShop(Long sourceShopId, Long productId, Long sysUserId) {
        try {
            log.info("商品同步开始,源店铺ID:{},商品ID:{}", sourceShopId, productId);
            assertAutoSync(sourceShopId);
            AssertUtil.isNull(productId, "{}不能为空", "商品ID");
            Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
            if (CollUtil.isEmpty(shopIds)) {
                return;
            }
            Product mainEntity = productService.getById(productId);
            AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "商品");
            List<Product> list = productService.queryChain().eq(Product::getSyncId, productId).in(Product::getShopId, shopIds).list();
            Map<Long, Product> map = new HashMap<>();
            if (CollUtil.isNotEmpty(list)) {
                map = list.stream().collect(Collectors.toMap(Product::getShopId, t -> t));
            }
            Map<Long, Long> specMap = new HashMap<>();
            if (mainEntity.getSpecId() != null) {
                List<ShopProdSpec> specList = specService.queryChain().eq(ShopProdSpec::getSyncId, mainEntity.getSpecId())
                        .in(ShopProdSpec::getShopId, shopIds).list();
                if (CollUtil.isNotEmpty(specList)) {
                    specMap = specList.stream().collect(Collectors.toMap(ShopProdSpec::getShopId, ShopProdSpec::getId));
                }
            }
            Map<Long, Long> unitMap = new HashMap<>();
            Set<Long> sysUnitMap = new HashSet<>();
            if (mainEntity.getUnitId() != null) {
                List<ShopProdUnit> unitList = unitService.queryChain().eq(ShopProdUnit::getSyncId, mainEntity.getUnitId())
                        .in(ShopProdUnit::getShopId, shopIds).list();
                if (CollUtil.isNotEmpty(unitList)) {
                    unitMap = unitList.stream().collect(Collectors.toMap(ShopProdUnit::getShopId, ShopProdUnit::getId));
                }
                for (ShopProdUnit shopProdUnit : unitService.queryChain().eq(ShopProdUnit::getIsSystem, 1).list()) {
                    sysUnitMap.add(shopProdUnit.getId());
                }
            }
            Map<Long, Long> categoryMap = new HashMap<>();
            if (mainEntity.getCategoryId() != null) {
                List<ShopProdCategory> categoryList = categoryService.queryChain().eq(ShopProdCategory::getSyncId, mainEntity.getCategoryId()).in(ShopProdCategory::getShopId, shopIds).list();
                if (CollUtil.isNotEmpty(categoryList)) {
                    categoryMap = categoryList.stream().collect(Collectors.toMap(ShopProdCategory::getShopId, ShopProdCategory::getId));
                }
            }
            Map<Long, Product> finalMap = map;
            for (Long shopId : shopIds) {
                List<Long> cateGoryIds = new ArrayList<>();
                List<Map<String, Object>> mainMapList = new ArrayList<>();
                Map<Long, String> dataMap = new HashMap<>();
                Product newEntity = BeanUtil.copyProperties(mainEntity, Product.class);
                newEntity.setSyncId(mainEntity.getId());
                newEntity.setShopId(shopId);
                newEntity.setCategoryId(newEntity.getCategoryId() != null ? categoryMap.get(shopId) : null);
                newEntity.setSpecId(newEntity.getSpecId() != null ? specMap.get(shopId) : null);
                if (newEntity.getUnitId() != null) {
                    if (!sysUnitMap.contains(newEntity.getUnitId())) {
                        newEntity.setUnitId(unitMap.get(shopId));
                    }
                }
                if (finalMap.containsKey(shopId)) {
                    Product childEntity = finalMap.get(shopId);
                    if (childEntity.getCategoryId() != null) {
                        cateGoryIds.add(childEntity.getCategoryId());
                    }
                    newEntity.setId(childEntity.getId());
                    newEntity.setStartTime(null);
                    newEntity.setEndTime(null);
                    newEntity.setDays(null);
                    newEntity.setIsHot(null);
                    newEntity.setIsSoldStock(null);
                    newEntity.setIsSale(null);
                    productService.updateById(newEntity);
                } else {
                    newEntity.setId(null);
                    newEntity.setIsSale(0);
                    productService.save(newEntity);
                }
                if (newEntity.getCategoryId() != null) {
                    cateGoryIds.add(newEntity.getCategoryId());
                }
                syncSkuBySourceShop(sourceShopId, shopId, productId, newEntity.getId());
                if ("package".equals(newEntity.getType()) || "coupon".equals(newEntity.getType())) {
                    syncProductPackageBySourceShop(newEntity, shopId);
                }
                dataMap.put(newEntity.getId(), newEntity.getName());
                buildNotice(mainMapList, "商品变动", 1, "product", dataMap);
                syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
                if (CollUtil.isNotEmpty(cateGoryIds)) {
                    for (Long cateGoryId : cateGoryIds) {
                        productRpcService.cleanCategoryProduct(shopId, cateGoryId);
                    }
                }
            }
        } catch (Exception e) {
            log.error("商品同步异常", e);
            throw new CzgException("商品同步失败");
        }
    }

    public Map<Long, Long> syncGroup(Long sourceShopId, Long pointShopId, Map<Long, Long> pros, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> groupMap = new HashMap<>();
        List<Long> pointGroup = new ArrayList<>();
        List<ProdGroup> pointList = groupService.queryChain().eq(ProdGroup::getShopId, pointShopId)
                .isNotNull(ProdGroup::getSyncId).list();
        for (ProdGroup prodGroup : pointList) {
            pointGroup.add(prodGroup.getSyncId());
            groupMap.put(prodGroup.getSyncId(), prodGroup.getId());
        }
        List<ProdGroup> list = groupService.queryChain().eq(ProdGroup::getShopId, sourceShopId).list();
        if (CollUtil.isEmpty(list)) {
            log.info("分组同步,分组数据为空");
            return new HashMap<>();

        }
        for (ProdGroup prodGroup : list) {
            if (CollUtil.isEmpty(pointGroup) || !pointGroup.contains(prodGroup.getId())) {
                ProdGroup tbProductGroupNew = BeanUtil.copyProperties(prodGroup, ProdGroup.class);
                tbProductGroupNew.setId(null);
                tbProductGroupNew.setSyncId(prodGroup.getId());
                tbProductGroupNew.setShopId(pointShopId);
                groupService.save(tbProductGroupNew);
                groupMap.put(prodGroup.getId(), tbProductGroupNew.getId());
            }
        }
        if (CollUtil.isNotEmpty(groupMap)) {
            List<ProdGroupRelation> groupRelations = prodGroupRelationService.queryChain().in(ProdGroupRelation::getProdGroupId, groupMap.keySet()).list();
            List<ProdGroupRelation> upList = new ArrayList<>();
            List<ProdGroupRelation> addList = new ArrayList<>();
            for (ProdGroupRelation prodGroupRelation : groupRelations) {
                prodGroupRelation.setProdGroupId(groupMap.get(prodGroupRelation.getProdGroupId()));
                prodGroupRelation.setProductId(pros.get(prodGroupRelation.getProductId()));
                ProdGroupRelation oldRelation = prodGroupRelationService.queryChain()
                        .eq(ProdGroupRelation::getProdGroupId, prodGroupRelation.getProdGroupId())
                        .eq(ProdGroupRelation::getProductId, prodGroupRelation.getProductId()).one();
                if (oldRelation == null) {
                    addList.add(prodGroupRelation);
                } else {
                    if (oldRelation.getSort().equals(prodGroupRelation.getSort())) {
                        upList.add(prodGroupRelation);
                    }
                }
            }
            if (CollUtil.isNotEmpty(addList)) {
                prodGroupRelationService.getMapper().insertBatchSelective(addList, 100);
            }
            if (CollUtil.isNotEmpty(upList)) {
                prodGroupRelationService.updateBatch(addList, 100);
            }
        }
        log.info("分组同步,源{}个,已有{}个,同步{}个", list.size(), pointGroup.size(), groupMap.size() - pointGroup.size());
        buildNotice(mainMapList, "分组同步", groupMap.size() - pointGroup.size(), null, null);
        return groupMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = {CacheConstant.USER_CLIENT_HOTS_PRODUCT, CacheConstant.USER_CLIENT_GROUPS_PRODUCT}, allEntries = true)
    public void syncGroupBySourceShop(Long sourceShopId, Long groupId, Long sysUserId) {
        assertAutoSync(sourceShopId);
        AssertUtil.isNull(groupId, "{}不能为空", "分组ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 1);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ProdGroup mainEntity = groupService.getById(groupId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "分组");
        List<ProdGroup> list = groupService.queryChain().eq(ProdGroup::getSyncId, groupId).in(ProdGroup::getShopId, shopIds).list();
        Map<Long, ProdGroup> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ProdGroup::getShopId, t -> t));
        }
        List<ProdGroupRelation> groupRelations = prodGroupRelationService.queryChain().eq(ProdGroupRelation::getProdGroupId, mainEntity.getId()).list();
        Map<Long, Integer> groupRelationMap = new HashMap<>();
        if (CollUtil.isNotEmpty(groupRelations)) {
            groupRelationMap = groupRelations.stream().collect(Collectors.toMap(ProdGroupRelation::getProductId, ProdGroupRelation::getSort));
        }
        List<ProdGroupRelation> groupRelationList = new ArrayList<>();
        for (Long shopId : shopIds) {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ProdGroup newEntity = BeanUtil.copyProperties(mainEntity, ProdGroup.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            if (map.containsKey(shopId)) {
                ProdGroup childEntity = map.get(shopId);
                newEntity.setId(childEntity.getId());
                groupService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                groupService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "分组变动", 1, "group", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
            List<ProdGroupRelation> oldProdGroup = prodGroupRelationService.queryChain().eq(ProdGroupRelation::getProdGroupId, newEntity.getId()).list();
            if (CollUtil.isEmpty(oldProdGroup)) {
                continue;
            }
            Set<Long> oldProds = oldProdGroup.stream().map(ProdGroupRelation::getProductId).collect(Collectors.toSet());
            List<Long> rmIds = productService.queryChain().select(Product::getId).eq(Product::getShopId, shopId)
                    .in(Product::getId, oldProds).isNotNull(Product::getSyncId).listAs(Long.class);
            if (CollUtil.isEmpty(rmIds)) {
                continue;
            }
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.eq(ProdGroupRelation::getProdGroupId, newEntity.getId());
            queryWrapper.in(ProdGroupRelation::getProductId, rmIds);
            prodGroupRelationService.remove(queryWrapper);

            if (CollUtil.isNotEmpty(groupRelationMap)) {
                List<Product> prods = productService.queryChain().eq(Product::getShopId, shopId).in(Product::getSyncId, groupRelationMap.keySet()).list();
                for (Product prod : prods) {
                    ProdGroupRelation newProdGroupRelation = new ProdGroupRelation();
                    newProdGroupRelation.setProdGroupId(newEntity.getId());
                    newProdGroupRelation.setProductId(prod.getId());
                    newProdGroupRelation.setSort(groupRelationMap.get(prod.getSyncId()));
                    groupRelationList.add(newProdGroupRelation);
                }
            }
        }
        if (CollUtil.isNotEmpty(groupRelations)) {
            prodGroupRelationService.saveBatch(groupRelationList, 100);
        }
    }

    //套餐
    public void syncProductPackage(Long shopId, Map<Long, Long> pros, Map<Long, Long> skus) {
        List<Product> list = productService.queryChain()
                .eq(Product::getShopId, shopId)
                .and(column(Product::getType).eq("package").or(column(Product::getType).eq("coupon")))
                .list();
        for (Product product : list) {
            if (StrUtil.isNotBlank(product.getGroupSnap()) && product.getGroupSnap().length() > 5) {
                List<ProductGroupVo> proGroupVos = JSON.parseArray(product.getGroupSnap(), ProductGroupVo.class);
                List<ProductGroupVo.Food> goodList = new ArrayList<>();
                for (ProductGroupVo proGroupVo : proGroupVos) {
                    for (ProductGroupVo.Food goods : proGroupVo.getGoods()) {
                        if (pros.containsKey(goods.getProId())) {
                            if (goods.getSkuId() != null && skus.containsKey(goods.getSkuId())) {
                                goods.setProId(pros.get(goods.getProId()));
                                goods.setSkuId(skus.get(goods.getSkuId()));
                            } else {
                                goods.setProId(pros.get(goods.getProId()));
                            }
                            goodList.add(goods);
                        }
                    }
                    proGroupVo.setCount(goodList.size());
                    proGroupVo.setGoods(goodList);
                    if (proGroupVo.getNumber() != null && proGroupVo.getNumber() > 0) {
                        if (proGroupVo.getNumber() > proGroupVo.getCount()) {
                            proGroupVo.setNumber(proGroupVo.getCount());
                        }
                    }
                }
                product.setGroupSnap(JSON.toJSONString(proGroupVos));
            }
        }
        productService.saveOrUpdateBatch(list, 100);
    }

    public void syncProductPackageBySourceShop(Product product, Long pointShopId) {
        if (StrUtil.isNotBlank(product.getGroupSnap()) && product.getGroupSnap().length() > 5) {
            List<ProductGroupVo> proGroupVos = JSON.parseArray(product.getGroupSnap(), ProductGroupVo.class);
            for (ProductGroupVo proGroupVo : proGroupVos) {
                List<ProductGroupVo.Food> goodList = new ArrayList<>();
                for (ProductGroupVo.Food goods : proGroupVo.getGoods()) {
                    if (goods.getProId() != null) {
                        Product sourceProduct = productService.queryChain().eq(Product::getShopId, pointShopId).eq(Product::getSyncId, goods.getProId()).one();
                        if (sourceProduct != null) {
                            goods.setProId(sourceProduct.getId());
                            goods.setProName(sourceProduct.getName());
                        }
                    }
                    if (goods.getSkuId() != null) {
                        ProdSku sourceSku = skuService.queryChain().eq(ProdSku::getShopId, pointShopId).eq(ProdSku::getSyncId, goods.getSkuId()).one();
                        goods.setSkuId(sourceSku.getId());
                        goods.setSkuName(sourceSku.getSpecInfo());
                    }
                    goodList.add(goods);
                }
                proGroupVo.setCount(goodList.size());
                proGroupVo.setGoods(goodList);
                if (proGroupVo.getNumber() != null && proGroupVo.getNumber() > 0) {
                    if (proGroupVo.getNumber() > proGroupVo.getCount()) {
                        proGroupVo.setNumber(proGroupVo.getCount());
                    }
                }
            }
            product.setGroupSnap(JSON.toJSONString(proGroupVos, JSONWriter.Feature.WriteMapNullValue));
            productService.saveOrUpdate(product);
        }
    }

    //分组
    public Map<Long, Long> syncSku(Long sourceShopId, Long pointShopId, Map<Long, Long> prods) {
        Map<Long, Long> skuMap = new HashMap<>();
        Map<Long, ProdSku> pointSkuMap = new HashMap<>();
        List<Long> pointSkus = new ArrayList<>();
        List<ProdSku> skus = skuService.queryChain()
                .eq(ProdSku::getShopId, pointShopId)
                .isNotNull(ProdSku::getSyncId)
                .list();
        skus.forEach(tbShopSku -> {
            pointSkus.add(tbShopSku.getSyncId());
            pointSkuMap.put(tbShopSku.getSyncId(), tbShopSku);
        });
        List<ProdSku> list = skuService.queryChain().eq(ProdSku::getShopId, sourceShopId).list();
        for (ProdSku prodSku : list) {
            if (prods.containsKey(prodSku.getProductId()) && (CollUtil.isEmpty(pointSkus) || !pointSkus.contains(prodSku.getId()))) {
                ProdSku newSku = BeanUtil.copyProperties(prodSku, ProdSku.class);
                newSku.setId(null);
                newSku.setShopId(pointShopId);
                newSku.setProductId(prods.get(prodSku.getProductId()));
                newSku.setSyncId(prodSku.getId());
                skuService.save(newSku);
                skuMap.put(prodSku.getId(), newSku.getId());
            } else {
                ProdSku newSku = pointSkuMap.get(prodSku.getId());
                if (newSku == null) {
                    continue;
                }
                newSku.setSpecInfo(prodSku.getSpecInfo());
                newSku.setCoverImg(prodSku.getCoverImg());
                newSku.setIsDel(prodSku.getIsDel());
                newSku.setBarCode(prodSku.getBarCode());

                newSku.setOriginPrice(prodSku.getOriginPrice());
                newSku.setCostPrice(prodSku.getCostPrice());
                newSku.setMemberPrice(prodSku.getMemberPrice());
                newSku.setSalePrice(prodSku.getSalePrice());
                newSku.setSuitNum(prodSku.getSuitNum());
                newSku.setWeight(prodSku.getWeight());
                skuService.updateById(newSku, false);
                skuMap.put(prodSku.getId(), newSku.getId());
            }
        }
        log.info("商品SKU同步,源{}个,已有{}个,同步{}个", list.size(), pointSkus.size(), skuMap.size());
        return skuMap;
    }

    public void syncSkuBySourceShop(Long sourceShopId, Long pointShopId, Long sourceProductId, Long pointProductId) {
        List<ProdSku> sourceSkus = skuService.queryChain()
                .eq(ProdSku::getShopId, sourceShopId)
                .eq(ProdSku::getProductId, sourceProductId)
                .list();
        if (CollUtil.isEmpty(sourceSkus)) {
            return;
        }
        List<ProdSku> newSaveSkus = new ArrayList<>();
        Set<Long> sourceIds = sourceSkus.stream().map(ProdSku::getId).collect(Collectors.toSet());

        List<ProdSku> childSkus = skuService.queryChain().eq(ProdSku::getShopId, pointShopId).in(ProdSku::getSyncId, sourceIds).list();
        Map<Long, ProdSku> childSkuMap = childSkus.stream().collect(Collectors.toMap(ProdSku::getSyncId, t -> t));
        for (ProdSku skus : sourceSkus) {
            ProdSku newSku = BeanUtil.copyProperties(skus, ProdSku.class);
            newSku.setSyncId(skus.getId());
            newSku.setShopId(pointShopId);
            newSku.setProductId(pointProductId);
            if (childSkuMap.containsKey(skus.getId())) {
                ProdSku childSku = childSkuMap.get(skus.getId());
                newSku.setId(childSku.getId());
                newSku.setIsGrounding(null);
                newSku.setIsPauseSale(null);
                newSku.setRealSalesNumber(null);
            } else {
                newSku.setId(null);
            }
            newSaveSkus.add(newSku);
        }
        skuService.saveOrUpdateBatch(newSaveSkus, 100);
    }

    public Map<Long, Long> syncConsGroup(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> consGroupMap = new HashMap<>();
        Map<Long, ConsGroup> pointConsGroupMap = new HashMap<>();
        List<Long> pointConsGroup = new ArrayList<>();
        List<ConsGroup> consGroupList = consGroupService.queryChain()
                .eq(ConsGroup::getShopId, pointShopId)
                .isNotNull(ConsGroup::getSyncId)
                .list();
        for (ConsGroup consGroup : consGroupList) {
            pointConsGroup.add(consGroup.getSyncId());
            pointConsGroupMap.put(consGroup.getSyncId(), consGroup);
        }
        List<ConsGroup> list = consGroupService.queryChain().eq(ConsGroup::getShopId, sourceShopId).list();
        for (ConsGroup consGroup : list) {
            if (CollUtil.isEmpty(pointConsGroup) || !pointConsGroup.contains(consGroup.getId())) {
                ConsGroup newConsGroup = BeanUtil.copyProperties(consGroup, ConsGroup.class);
                newConsGroup.setId(null);
                newConsGroup.setSyncId(consGroup.getId());
                newConsGroup.setShopId(pointShopId);
                consGroupService.save(newConsGroup);
                consGroupMap.put(consGroup.getId(), newConsGroup.getId());
            } else {
                ConsGroup newConsGroup = pointConsGroupMap.get(consGroup.getId());
                if (newConsGroup == null) {
                    continue;
                }
                newConsGroup.setName(consGroup.getName());
                newConsGroup.setStatus(consGroup.getStatus());
                consGroupService.updateById(newConsGroup, false);
                consGroupMap.put(consGroup.getId(), newConsGroup.getId());
            }
        }
        log.info("耗材分组同步,源{}个,已有{}个,同步{}个", list.size(), pointConsGroup.size(), consGroupMap.size() - pointConsGroup.size());
        buildNotice(mainMapList, "耗材分组同步", pointConsGroup.size(), null, null);
        buildNotice(mainMapList, "耗材分组新增", consGroupMap.size() - pointConsGroup.size(), null, null);
        return consGroupMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncConsGroupBySourceShop(Long sourceShopId, Long consGroupId, Long sysUserId) {
        assertAutoSync(sourceShopId);
        AssertUtil.isNull(consGroupId, "{}不能为空", "耗材分组ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 2);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ConsGroup mainEntity = consGroupService.getById(consGroupId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "耗材分组");
        List<ConsGroup> list = consGroupService.queryChain().eq(ConsGroup::getSyncId, consGroupId).in(ConsGroup::getShopId, shopIds).list();
        Map<Long, ConsGroup> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ConsGroup::getShopId, t -> t));
        }
        Map<Long, ConsGroup> finalMap = map;
        shopIds.forEach(shopId -> {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ConsGroup newEntity = BeanUtil.copyProperties(mainEntity, ConsGroup.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            if (finalMap.containsKey(shopId)) {
                ConsGroup childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                consGroupService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                consGroupService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getName());
            buildNotice(mainMapList, "耗材分组变动", 1, "consGroup", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        });
    }

    // 耗材
    public Map<Long, Long> syncConsInfo(Long sourceShopId, Long pointShopId, Map<Long, Long> consGroupMap, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> consMap = new HashMap<>();
        Map<Long, ConsInfo> pointConsMap = new HashMap<>();
        List<Long> pointConsInfo = new ArrayList<>();
        List<ConsInfo> pointList = consInfoService.queryChain()
                .eq(ConsInfo::getShopId, pointShopId)
                .isNotNull(ConsInfo::getSyncId)
                .list();
        for (ConsInfo consInfo : pointList) {
            pointConsInfo.add(consInfo.getSyncId());
            pointConsMap.put(consInfo.getSyncId(), consInfo);
        }
        List<ConsInfo> list = consInfoService.queryChain().eq(ConsInfo::getShopId, sourceShopId).list();
        for (ConsInfo cons : list) {
            if (CollUtil.isEmpty(pointConsInfo) || !pointConsInfo.contains(cons.getId())) {
                ConsInfo conInfo = BeanUtil.copyProperties(cons, ConsInfo.class);
                conInfo.setId(null);
                conInfo.setShopId(pointShopId);
                conInfo.setSyncId(cons.getId());
                conInfo.setConsGroupId(consGroupMap.get(conInfo.getConsGroupId()));
                conInfo.setStockNumber(BigDecimal.ZERO);
                consInfoService.save(conInfo);
                consMap.put(cons.getId(), conInfo.getId());
            } else {
                ConsInfo conInfo = pointConsMap.get(cons.getId());
                if (conInfo == null) {
                    continue;
                }
                conInfo.setConsGroupId(consGroupMap.get(conInfo.getConsGroupId()));
                conInfo.setConName(cons.getConName());
                conInfo.setPrice(cons.getPrice());
                conInfo.setStatus(cons.getStatus());
                conInfo.setConUnit(cons.getConUnit());
                conInfo.setConWarning(cons.getConWarning());
                conInfo.setIsStock(cons.getIsStock());
                conInfo.setConUnitTwo(cons.getConUnitTwo());
                conInfo.setConUnitTwoConvert(cons.getConUnitTwoConvert());
                conInfo.setDefaultUnit(cons.getDefaultUnit());
                consInfoService.updateById(conInfo, false);
                consMap.put(cons.getId(), conInfo.getId());
            }
        }
        log.info("耗材同步,源{}个,已有{}个,同步{}个", list.size(), pointConsInfo.size(), consMap.size() - pointConsInfo.size());
        buildNotice(mainMapList, "耗材信息同步", pointConsInfo.size(), null, null);
        buildNotice(mainMapList, "耗材信息新增", consMap.size() - pointConsInfo.size(), null, null);
        return consMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncConsInfoBySourceShop(Long sourceShopId, Long consInfoId, Long sysUserId) {
        assertAutoSync(sourceShopId);
        AssertUtil.isNull(consInfoId, "{}不能为空", "耗材ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 2);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ConsInfo mainEntity = consInfoService.getById(consInfoId);
        AssertUtil.isNull(mainEntity, "需要同步的{}不存在", "耗材");
        List<ConsInfo> list = consInfoService.queryChain().eq(ConsInfo::getSyncId, consInfoId).in(ConsInfo::getShopId, shopIds).list();
        Map<Long, ConsInfo> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ConsInfo::getShopId, t -> t));
        }
        Map<Long, ConsInfo> finalMap = map;
        for (Long shopId : shopIds) {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> dataMap = new HashMap<>();
            ConsInfo newEntity = BeanUtil.copyProperties(mainEntity, ConsInfo.class);
            newEntity.setSyncId(mainEntity.getId());
            newEntity.setShopId(shopId);
            if (newEntity.getConsGroupId() != null) {
                Long consGroupId = consGroupService.queryChain().select(ConsGroup::getId)
                        .eq(ConsGroup::getSyncId, newEntity.getConsGroupId()).eq(ConsGroup::getShopId, shopId).oneAs(Long.class);
                newEntity.setConsGroupId(consGroupId);
            }
            if (finalMap.containsKey(shopId)) {
                ConsInfo childEntity = finalMap.get(shopId);
                newEntity.setId(childEntity.getId());
                newEntity.setStockNumber(null);
                consInfoService.updateById(newEntity);
            } else {
                newEntity.setId(null);
                newEntity.setStockNumber(BigDecimal.ZERO);
                consInfoService.save(newEntity);
            }
            dataMap.put(newEntity.getId(), newEntity.getConName());
            buildNotice(mainMapList, "耗材信息变动", 1, "consInfo", dataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newEntity));
        }
    }

    public void syncConsPro(Long sourceShopId, Long pointShopId, Map<Long, Long> consMap, Map<Long, Long> proMap) {
        prodConsRelationService.remove(QueryWrapper.create().eq(ProdConsRelation::getShopId, pointShopId));
        List<ProdConsRelation> list = prodConsRelationService.queryChain().eq(ProdConsRelation::getShopId, sourceShopId).list();
        List<ProdConsRelation> addList = new ArrayList<>();
        for (ProdConsRelation prodConsRelation : list) {
            prodConsRelation.setShopId(pointShopId);
            prodConsRelation.setProductId(proMap.get(prodConsRelation.getProductId()));
            prodConsRelation.setConsInfoId(consMap.get(prodConsRelation.getConsInfoId()));
            if (prodConsRelation.getProductId() == null || prodConsRelation.getConsInfoId() == null) {
                log.info("关联关系同步失败 商品ID:{}或耗材ID:{}不存在", prodConsRelation.getProductId(), prodConsRelation.getConsInfoId());
                continue;
            }
            addList.add(prodConsRelation);
        }
        if (CollUtil.isNotEmpty(addList)) {
            prodConsRelationService.saveBatch(addList, 100);
        }
        log.info("耗材与商品关联关系,同步{}个", list.size());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncConsProBySourceShop(Long sourceShopId, Long sourceProdId, Long sysUserId) {
        try {
            log.info("同步商品耗材关系开始");
            assertAutoSync(sourceShopId);
            AssertUtil.isNull(sourceProdId, "{}不能为空", "商品ID");
            Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 2);
            if (CollUtil.isEmpty(shopIds)) {
                return;
            }
            List<ProdConsRelation> newSaveList = new ArrayList<>();
            List<ProdConsRelation> list = prodConsRelationService.queryChain()
                    .eq(ProdConsRelation::getShopId, sourceShopId).eq(ProdConsRelation::getProductId, sourceProdId).list();
            for (Long shopId : shopIds) {
                Product product = productService.queryChain().eq(Product::getSyncId, sourceProdId).eq(Product::getShopId, shopId).one();
                if (product != null) {
                    prodConsRelationService.remove(QueryWrapper.create()
                            .eq(ProdConsRelation::getShopId, shopId)
                            .eq(ProdConsRelation::getProductId, product.getId()));
                    if (CollUtil.isNotEmpty(list)) {
                        Map<Long, BigDecimal> map = list.stream().collect(Collectors.toMap(ProdConsRelation::getConsInfoId, ProdConsRelation::getSurplusStock));
                        List<ConsInfo> newCons = consInfoService.queryChain()
                                .in(ConsInfo::getSyncId, map.keySet()).eq(ConsInfo::getShopId, shopId).list();
                        if (CollUtil.isNotEmpty(newCons)) {
                            for (ConsInfo newCon : newCons) {
                                ProdConsRelation prodConsRelation = new ProdConsRelation();
                                prodConsRelation.setShopId(shopId);
                                prodConsRelation.setProductId(product.getId());
                                prodConsRelation.setConsInfoId(newCon.getId());
                                prodConsRelation.setSurplusStock(map.get(newCon.getSyncId()));
                                newSaveList.add(prodConsRelation);
                            }
                        }
                    }
                }
            }
            if (CollUtil.isNotEmpty(newSaveList)) {
                prodConsRelationService.saveBatch(newSaveList, 100);
            }
            log.info("同步商品耗材关系结束");
        } catch (Exception e) {
            log.error("同步商品耗材关系异常", e);
        }
    }

    public Map<Long, Long> syncVendor(Long sourceShopId, Long pointShopId, List<Map<String, Object>> mainMapList) {
        Map<Long, Long> vendorMap = new HashMap<>();
        Map<Long, ShopVendor> pointVendorMap = new HashMap<>();
        List<Long> pointShopVendor = new ArrayList<>();
        List<ShopVendor> pointShopVendorList = vendorService.queryChain()
                .eq(ShopVendor::getShopId, pointShopId)
                .isNotNull(ShopVendor::getSyncId)
                .list();
        for (ShopVendor shopVendor : pointShopVendorList) {
            pointShopVendor.add(shopVendor.getSyncId());
            pointVendorMap.put(shopVendor.getSyncId(), shopVendor);
        }
        List<ShopVendor> list = vendorService.queryChain().eq(ShopVendor::getShopId, sourceShopId).list();
        for (ShopVendor vendor : list) {
            if (CollUtil.isEmpty(pointShopVendor) || !pointShopVendor.contains(vendor.getId())) {
                ShopVendor vendorNew = BeanUtil.copyProperties(vendor, ShopVendor.class);
                vendorNew.setId(null);
                vendorNew.setSyncId(vendor.getId());
                vendorNew.setShopId(pointShopId);
                vendorService.save(vendorNew);
                vendorMap.put(vendor.getId(), vendorNew.getId());
            } else {
                ShopVendor vendorNew = pointVendorMap.get(vendor.getId());
                if (vendorNew == null) {
                    continue;
                }
                vendorNew.setSort(vendor.getSort());
                vendorNew.setName(vendor.getName());
                vendorNew.setContactName(vendor.getContactName());
                vendorNew.setTelephone(vendor.getTelephone());
                vendorNew.setAddress(vendor.getAddress());
                vendorNew.setPeriod(vendor.getPeriod());
                vendorNew.setTip(vendor.getTip());
                vendorNew.setRemark(vendor.getRemark());
                vendorNew.setIsDel(vendor.getIsDel());
                vendorService.updateById(vendorNew, false);
                vendorMap.put(vendor.getId(), vendorNew.getId());
            }
        }
        buildNotice(mainMapList, "供应商同步", pointShopVendor.size(), null, null);
        buildNotice(mainMapList, "供应商新增", vendorMap.size() - pointShopVendor.size(), null, null);
        log.info("供应商同步,源{}个,已有{}个,同步{}个", list.size(), pointShopVendor.size(), vendorMap.size());
        return vendorMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncVendorBySourceShop(Long sourceShopId, Long vendorId, Long sysUserId) {
        assertAutoSync(sourceShopId);
        AssertUtil.isNull(vendorId, "{}不能为空", "供应商ID");
        Set<Long> shopIds = checkSourceShopInfo(sourceShopId, 2);
        if (CollUtil.isEmpty(shopIds)) {
            return;
        }
        ShopVendor mainVendor = vendorService.getById(vendorId);
        AssertUtil.isNull(mainVendor, "需要同步的{}不存在", "单位");
        List<ShopVendor> list = vendorService.queryChain().eq(ShopVendor::getSyncId, vendorId).in(ShopVendor::getShopId, shopIds).list();
        Map<Long, ShopVendor> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)) {
            map = list.stream().collect(Collectors.toMap(ShopVendor::getShopId, t -> t));
        }
        Map<Long, ShopVendor> finalMap = map;
        shopIds.forEach(shopId -> {
            List<Map<String, Object>> mainMapList = new ArrayList<>();
            Map<Long, String> vendorDataMap = new HashMap<>();
            ShopVendor newVendor = BeanUtil.copyProperties(mainVendor, ShopVendor.class);

            newVendor.setSyncId(mainVendor.getId());
            newVendor.setShopId(shopId);
            if (finalMap.containsKey(shopId)) {
                ShopVendor vendor = finalMap.get(shopId);
                newVendor.setId(vendor.getId());
                vendorService.updateById(newVendor);
            } else {
                newVendor.setId(null);
                vendorService.save(newVendor);
            }
            vendorDataMap.put(newVendor.getId(), newVendor.getName());
            buildNotice(mainMapList, "供应商变动", 1, "vendor", vendorDataMap);
            syncNoticeService.addNotice(shopId, sysUserId, "数据变动", JSON.toJSONString(mainMapList), JSON.toJSONString(newVendor));
        });
    }

    /**
     * 断言是否支持自动同步
     *
     * @param sourceShopId 源店铺ID
     */
    private void assertAutoSync(Long sourceShopId) {
        String branchDataSyncMethod = DbChain.table("tb_shop_config").select("branch_data_sync_method").where("id = ?", sourceShopId).objAs(String.class);
        if (branchDataSyncMethod == null) {
            throw new CzgException("主店未配置数据同步方式");
        }
        if (!BranchDataSyncMethodEnum.AUTO.getValue().equals(branchDataSyncMethod)) {
            throw new CzgException("主店数据同步方式不是自动同步");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncCouponBySourceShop(Long sourceShopId, Long couponId, Integer type) {
        AssertUtil.isNull(sourceShopId, "{}不能为空", "源店铺ID");
        AssertUtil.isNull(couponId, "{}不能为空", "优惠券ID");
        AssertUtil.isNull(type, "{}不能为空", "操作类型");
        ShopInfo sourceShop = shopInfoService.getById(sourceShopId);
        if (StrUtil.isBlank(sourceShop.getShopType()) || "only".equals(sourceShop.getShopType())
                || sourceShop.getIsHeadShop() == null || sourceShop.getIsHeadShop() != 1) {
            // 主店不是主店铺或主店是单店,不进行优惠券同步
            return;
        }
        ShopCoupon couponSource = couponService.getById(couponId);
        if (couponSource == null) {
            return;
        }
        if ("only".equals(couponSource.getUseShopType())) {
            upIsDel(couponId);
            return;
        }
        List<Long> ids = new ArrayList<>();
        if ("all".equals(couponSource.getUseShopType())) {
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.select(column(ShopConfig::getId));
            queryWrapper.eq(ShopConfig::getMainId, sourceShopId);
            ids = shopConfigService.listAs(queryWrapper, Long.class);
        } else {
            if (StrUtil.isBlank(couponSource.getUseShops())) {
                return;
            }
            ids = Arrays.stream(couponSource.getUseShops().split(","))
                    .map(Long::parseLong)
                    .collect(Collectors.toList());
        }
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        couponSource.setId(null);
        switch (type) {
            case 1://新增
                saveCouponsForShops(null, ids, couponSource, couponId);
                break;
            case 2://先删除再新增
                List<ShopCoupon> couponList = couponService.list(new QueryWrapper().eq(ShopCoupon::getSyncId, couponId));
                Map<Long, Long> map = couponList.stream().collect(Collectors.toMap(ShopCoupon::getShopId, ShopCoupon::getId));
                deleteCouponsBySyncId(couponId);//假性删除
                saveCouponsForShops(map, ids, couponSource, couponId);
                break;
            case 3:// 删除
                upIsDel(couponId);
                break;
            default:
                // 处理未知类型，可根据业务需求抛出异常或忽略
                throw new IllegalArgumentException("不支持的操作类型: " + type);
        }
    }

    private void saveCouponsForShops(Map<Long, Long> map, List<Long> ids, ShopCoupon coupon, Long couponId) {
        List<Long> couponFoodIds = new ArrayList<>();
        if (StrUtil.isNotBlank(coupon.getFoods()) && !",".equals(coupon.getFoods())) {
            couponFoodIds = Arrays.stream(coupon.getFoods().split(",")).map(Long::parseLong).toList();
        }
        for (Long id : ids) {
            if (CollUtil.isNotEmpty(couponFoodIds)) {
                List<Long> foodIds = productService.listAs(new QueryWrapper().select(Product::getId)
                        .in(Product::getSyncId, couponFoodIds).eq(Product::getShopId, id), Long.class);
                if (CollUtil.isEmpty(foodIds)) {
                    continue;
                }
                coupon.setFoods(foodIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
            }
            coupon.setShopId(id);
            coupon.setSyncId(couponId);
            coupon.setId(null);
            if (CollUtil.isNotEmpty(map) && map.containsKey(id)) {
                coupon.setId(map.get(id));
            }
            couponService.saveOrUpdate(coupon);
        }
    }

    private void upIsDel(Long couponId) {
        couponService.update(new ShopCoupon().setIsDel(1), new QueryWrapper()
                .eq(ShopCoupon::getSyncId, couponId));
    }

    private void deleteCouponsBySyncId(Long couponId) {
        couponService.update(new ShopCoupon().setIsDel(1), new QueryWrapper()
                .eq(ShopCoupon::getSyncId, couponId));
//        couponService.remove(new QueryWrapper().eq(ShopCoupon::getSyncId, couponId));
    }
}
