package com.ruoyi.system.service.house.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.domain.grid.GridRegion;
import com.ruoyi.system.domain.grid.GridRegionExample;
import com.ruoyi.system.domain.grid.GridRegionUser;
import com.ruoyi.system.domain.grid.GridRegionUserExample;
import com.ruoyi.system.domain.house.BusinessEntityInfo;
import com.ruoyi.system.domain.house.BusinessEntitySell;
import com.ruoyi.system.domain.house.HouseResource;
import com.ruoyi.system.domain.house.HouseResourceBusinessEntityInfoMapping;
import com.ruoyi.system.domain.house.enums.HouseEnums;
import com.ruoyi.system.domain.house.vo.*;
import com.ruoyi.system.domain.changerecord.enums.OperationType;
import com.ruoyi.system.domain.changerecord.enums.SubjectType;
import com.ruoyi.system.mapper.grid.GridRegionMapper;
import com.ruoyi.system.mapper.grid.GridRegionUserMapper;
import com.ruoyi.system.mapper.house.BusinessEntityInfoMapper;
import com.ruoyi.system.mapper.house.BusinessEntitySellMapper;
import com.ruoyi.system.mapper.house.HouseResourceBusinessEntityInfoMappingMapper;
import com.ruoyi.system.mapper.house.HouseResourceMapper;
import com.ruoyi.system.service.house.HouseResourceService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

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.function.Function;
import java.util.stream.Collectors;

@Service
public class HouseResourceServiceImpl implements HouseResourceService {

    private static final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

    private final HouseResourceMapper houseResourceMapper;

    private final BusinessEntityInfoMapper businessEntityInfoMapper;

    private final BusinessEntitySellMapper businessEntitySellMapper;

    private final HouseResourceBusinessEntityInfoMappingMapper houseResourceBusinessEntityInfoMappingMapper;

    private final GridRegionMapper gridRegionMapper;

    private final GridRegionUserMapper gridRegionUserMapper;

    public HouseResourceServiceImpl(HouseResourceMapper houseResourceMapper, BusinessEntityInfoMapper businessEntityInfoMapper, BusinessEntitySellMapper businessEntitySellMapper, HouseResourceBusinessEntityInfoMappingMapper houseResourceBusinessEntityInfoMappingMapper, GridRegionMapper gridRegionMapper, GridRegionUserMapper gridRegionUserMapper) {
        this.houseResourceMapper = houseResourceMapper;
        this.businessEntityInfoMapper = businessEntityInfoMapper;
        this.businessEntitySellMapper = businessEntitySellMapper;
        this.houseResourceBusinessEntityInfoMappingMapper = houseResourceBusinessEntityInfoMappingMapper;
        this.gridRegionMapper = gridRegionMapper;
        this.gridRegionUserMapper = gridRegionUserMapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveHouseResource(HouseResourceSaveUpdateDto dto) {
        String houseResourceId = UUID.randomUUID().toString().replaceAll("-", "");
        dto.getHouseResource().setId(houseResourceId);
        houseResourceMapper.insertHouseResource(dto.getHouseResource());

        for (BusinessEntityDto businessEntityDto : dto.getBusinessEntityDtoList()) {
            String businessEntityInfoId;
            if (StringUtils.hasText(businessEntityDto.getId())) {
                businessEntityInfoId = businessEntityDto.getId();
                BusinessEntityInfo existingInfo = businessEntityInfoMapper.selectBusinessEntityInfoById(businessEntityDto.getId());
                BeanUtils.copyProperties(businessEntityDto, existingInfo);
                businessEntityInfoMapper.updateBusinessEntityInfo(existingInfo);
            } else {
                BusinessEntityInfo businessEntityInfo = new BusinessEntityInfo();
                BeanUtils.copyProperties(businessEntityDto, businessEntityInfo);
                businessEntityInfoId = UUID.randomUUID().toString().replaceAll("-", "");
                businessEntityInfo.setId(businessEntityInfoId);
                businessEntityInfoMapper.insertBusinessEntityInfo(businessEntityInfo);
            }

            HouseResourceBusinessEntityInfoMapping houseResourceBusinessEntityInfoMapping = new HouseResourceBusinessEntityInfoMapping();
            houseResourceBusinessEntityInfoMapping.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            houseResourceBusinessEntityInfoMapping.setHouseResourceId(houseResourceId);
            houseResourceBusinessEntityInfoMapping.setBusinessEntityInfoId(businessEntityInfoId);
            houseResourceBusinessEntityInfoMappingMapper.insert(houseResourceBusinessEntityInfoMapping);

            Map<Integer, List<BusinessEntitySell>> sellGroup = businessEntityDto.getBusinessEntitySells().stream().collect(Collectors.groupingBy(BusinessEntitySell::getYear));
            for (Map.Entry<Integer, List<BusinessEntitySell>> entry : sellGroup.entrySet()) {
                if (entry.getValue().size() > 1) {
                    throw new RuntimeException("年份 " + entry.getKey() + " 只能有一条销售数据");
                }
            }

            businessEntitySellMapper.deleteBusinessEntitySellByEntityId(businessEntityInfoId);
            for (BusinessEntitySell businessEntitySell : businessEntityDto.getBusinessEntitySells()) {
                businessEntitySell.setId(UUID.randomUUID().toString().replaceAll("-", ""));
                businessEntitySell.setBusinessEntityInfoId(businessEntityInfoId);
                businessEntitySellMapper.insertBusinessEntitySell(businessEntitySell);
            }
        }
    }

    @Override
    public HouseResourceDetail detail(String id) {
        HouseResource houseResource = houseResourceMapper.selectHouseResourceById(id);
        if (houseResource == null) {
            return null;
        }
        HouseResourceDetail houseResourceDetail = new HouseResourceDetail();
        BeanUtils.copyProperties(houseResource, houseResourceDetail);
        GridRegionExample gridRegionExample = new GridRegionExample();
        gridRegionExample.createCriteria().andWgCodeEqualTo(houseResourceDetail.getTwo()).andWgTypeEqualTo(String.valueOf(houseResourceDetail.getType()));
        List<GridRegion> two = gridRegionMapper.selectByExample(gridRegionExample);
        if (!two.isEmpty()) {
            houseResourceDetail.setWgName2(two.get(0).getWgName());
        }
        GridRegionExample gridRegionExampleThree = new GridRegionExample();
        gridRegionExampleThree.createCriteria().andWgCodeEqualTo(houseResourceDetail.getThree()).andWgTypeEqualTo(String.valueOf(houseResourceDetail.getType()));
        List<GridRegion> three = gridRegionMapper.selectByExample(gridRegionExampleThree);
        if (!three.isEmpty()) {
            houseResourceDetail.setWgName3(three.get(0).getWgName());
        }
        if (StringUtils.hasText(houseResourceDetail.getHouseResourceUrl())) {
            houseResourceDetail.setHouseResourceUrlList(Arrays.asList(houseResourceDetail.getHouseResourceUrl().split(",")));
        }
        if (StringUtils.hasText(houseResourceDetail.getBusinessLicenseUrl())) {
            houseResourceDetail.setBusinessLicenseUrlList(Arrays.asList(houseResourceDetail.getBusinessLicenseUrl().split(",")));
        }
        houseResourceDetail.setHouseResourceAttributeText(HouseEnums.HouseResourceAttributeEnum.getDescByCode(houseResourceDetail.getHouseResourceAttribute()));
        houseResourceDetail.setHouseResourceTypeText(HouseEnums.HouseResourceTypeEnum.getDescByCode(houseResourceDetail.getHouseResourceType()));
        houseResourceDetail.setOrientationText(HouseEnums.OrientationEnum.getDescByCode(houseResourceDetail.getOrientation()));
        houseResourceDetail.setDecorationConditionText(HouseEnums.DecorationConditionEnum.getDescByCode(houseResourceDetail.getDecorationCondition()));
        houseResourceDetail.setRentalUnitText(HouseEnums.RentalUnitEnum.getDescByCode(houseResourceDetail.getRentalUnit()));
        houseResourceDetail.setHireStatusText(HouseEnums.hireStatusTextEnum.getDescByCode(houseResourceDetail.getHireStatus()));

        List<HouseResourceBusinessEntityInfoMapping> houseResourceMappings = houseResourceBusinessEntityInfoMappingMapper.selectByHouseResourceId(id);
        if (!CollectionUtils.isEmpty(houseResourceMappings)) {
            List<BusinessEntityDto> result = new ArrayList<>();
            List<BusinessEntityInfo> businessEntityInfos = businessEntityInfoMapper.selectByIdList(houseResourceMappings.stream().map(HouseResourceBusinessEntityInfoMapping::getBusinessEntityInfoId).collect(Collectors.toList()));
            businessEntityInfos.forEach(x -> {
                BusinessEntityDto businessEntityDto = new BusinessEntityDto();
                BeanUtils.copyProperties(x, businessEntityDto);
                List<HouseResourceBusinessEntityInfoMapping> businessEntityInfoMappings = houseResourceBusinessEntityInfoMappingMapper.selectByBusinessEntityInfoId(x.getId());
                if (!CollectionUtils.isEmpty(businessEntityInfoMappings)) {
                    List<HouseResourcePage> list = houseResourceMapper.selectByIdList(businessEntityInfoMappings.stream().map(HouseResourceBusinessEntityInfoMapping::getHouseResourceId).collect(Collectors.toList()));
                    List<HouseResourcePage> handleList = handleHouseList(list, false);
                    businessEntityDto.setHouseResources(handleList);
                }
                List<BusinessEntitySell> businessEntitySells = businessEntitySellMapper.selectBusinessEntitySellByEntityId(x.getId());
                if (!CollectionUtils.isEmpty(businessEntitySells)) {
                    businessEntityDto.setBusinessEntitySells(businessEntitySells);
                }
                result.add(businessEntityDto);
            });
            houseResourceDetail.setBusinessEntityDtoList(result);
        }
        return houseResourceDetail;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateHouseResource(HouseResourceSaveUpdateDto dto) {
        if (Objects.isNull(dto.getHouseResource().getId())) {
            throw new RuntimeException("房源id不能为空");
        }
        houseResourceMapper.updateHouseResource(dto.getHouseResource());
        String houseResourceId = dto.getHouseResource().getId();

        houseResourceBusinessEntityInfoMappingMapper.deleteByHouseResourceId(houseResourceId);

        for (BusinessEntityDto businessEntityDto : dto.getBusinessEntityDtoList()) {
            String businessEntityInfoId;
            BusinessEntityInfo businessEntityInfo;
            if (StringUtils.hasText(businessEntityDto.getId())) {
                businessEntityInfoId = businessEntityDto.getId();
                businessEntityInfo = businessEntityInfoMapper.selectBusinessEntityInfoById(businessEntityInfoId);
                if (businessEntityInfo == null) {
                    throw new RuntimeException("未找到对应的企业信息: " + businessEntityInfoId);
                }
                BeanUtils.copyProperties(businessEntityDto, businessEntityInfo);
                businessEntityInfoMapper.updateBusinessEntityInfo(businessEntityInfo);
            } else {
                businessEntityInfo = new BusinessEntityInfo();
                BeanUtils.copyProperties(businessEntityDto, businessEntityInfo);
                businessEntityInfoId = UUID.randomUUID().toString().replaceAll("-", "");
                businessEntityInfo.setId(businessEntityInfoId);
                businessEntityInfoMapper.insertBusinessEntityInfo(businessEntityInfo);
            }

            HouseResourceBusinessEntityInfoMapping mapping = new HouseResourceBusinessEntityInfoMapping();
            mapping.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            mapping.setHouseResourceId(houseResourceId);
            mapping.setBusinessEntityInfoId(businessEntityInfoId);
            houseResourceBusinessEntityInfoMappingMapper.insert(mapping);

            Map<Integer, List<BusinessEntitySell>> sellGroup = businessEntityDto.getBusinessEntitySells()
                    .stream().collect(Collectors.groupingBy(BusinessEntitySell::getYear));
            for (Map.Entry<Integer, List<BusinessEntitySell>> entry : sellGroup.entrySet()) {
                if (entry.getValue().size() > 1) {
                    throw new RuntimeException("年份 " + entry.getKey() + " 只能有一条销售数据");
                }
            }

            businessEntitySellMapper.deleteBusinessEntitySellByEntityId(businessEntityInfoId);
            for (BusinessEntitySell businessEntitySell : businessEntityDto.getBusinessEntitySells()) {
                businessEntitySell.setId(UUID.randomUUID().toString().replaceAll("-", ""));
                businessEntitySell.setBusinessEntityInfoId(businessEntityInfoId);
                businessEntitySellMapper.insertBusinessEntitySell(businessEntitySell);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteHouseResourceById(String id) {
        houseResourceMapper.deleteHouseResourceById(id);
        houseResourceBusinessEntityInfoMappingMapper.deleteByHouseResourceId(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchSaveHouseResources(List<HouseResource> houseResources) {
        houseResourceMapper.batchInsertHouseResources(houseResources);
    }

    @Override
    public IPage<HouseResourcePage> pageHouseResources(HouseResourcePageQuery query) {
        List<String> wgCodes = resolveCurrentUserWgCodes(true);
        if (wgCodes != null) {
            query.setWgCodes(wgCodes);
        }
        IPage<HouseResourcePage> page = houseResourceMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), query);
        List<HouseResourcePage> records = handleHouseList(page.getRecords(), false);
        page.setRecords(records);
        return page;
    }

    @Override
    public List<HouseResourceProfileGraph> profileGraphHouseResources(HouseResourcePageQuery query) {
        List<HouseResourcePage> list = houseResourceMapper.selectProfileGraph(query);
        List<HouseResourcePage> houseResourcePages = handleHouseList(list, true);
        if (query.isZlFlag()) {
            houseResourcePages = houseResourcePages.stream().filter(x -> x.getHouseResourceAttribute() == 0).collect(Collectors.toList());
        }
        if (query.isZyFlag()) {
            houseResourcePages = houseResourcePages.stream().filter(x -> x.getHouseResourceAttribute() == 2).collect(Collectors.toList());
        }
        if (query.isQtFlag()) {
            houseResourcePages = houseResourcePages.stream().filter(x -> x.getHouseResourceAttribute() == 1).collect(Collectors.toList());
        }
        if (query.isZaiyingFlag()) {
            houseResourcePages = houseResourcePages.stream().filter(x -> x.getHouseResourceType() == 0).collect(Collectors.toList());
        }
        if (query.isXzFlag()) {
            houseResourcePages = houseResourcePages.stream().filter(x -> x.getHouseResourceType() == 1).collect(Collectors.toList());
        }
        if (query.isGkFlag()) {
            houseResourcePages = houseResourcePages.stream().filter(x -> x.getHouseResourceType() == 2).collect(Collectors.toList());
        }
        if (query.isSbFlag()) {
            houseResourcePages = houseResourcePages.stream().filter(x -> x.getHouseResourceType() == 3).collect(Collectors.toList());
        }
        if (query.isZyzyFlag()) {
            houseResourcePages = houseResourcePages.stream().filter(x -> x.getHouseResourceType() == 4).collect(Collectors.toList());
        }
        List<HouseResourceProfileGraph> resourceProfileGraphList = new ArrayList<>();
        Map<String, List<HouseResourcePage>> collect = houseResourcePages.stream().collect(Collectors.groupingBy(HouseResourcePage::getFour));
        collect.forEach((k, v) -> {
            HouseResourceProfileGraph houseResourceProfileGraph = new HouseResourceProfileGraph();
            houseResourceProfileGraph.setId(k);
            GridRegion gridRegion = gridRegionMapper.getWgLevel(k);
            if (Objects.nonNull(gridRegion)) {
                houseResourceProfileGraph.setWgName(gridRegion.getWgName());
                if (Objects.nonNull(gridRegion.getJzArea())) {
                    houseResourceProfileGraph.setJzArea(new BigDecimal(gridRegion.getJzArea()));
                }
                if (Objects.nonNull(gridRegion.getPosition())) {
                    houseResourceProfileGraph.setPosition(gridRegion.getPosition());
                }
            }
            houseResourceProfileGraph.setKzCount(v.stream().filter(x -> x.getHouseResourceType() == HouseEnums.HouseResourceTypeEnum.KZ.getCode()).count());

            List<HouseResourcePage> distinctList = new ArrayList<>(v.stream().collect(Collectors.toMap(HouseResourcePage::getId, Function.identity(), (a, b) -> a)).values());
            Map<String, List<HouseResourcePage>> houseMap = distinctList.stream().collect(Collectors.groupingBy(HouseResourcePage::getId));
            BigDecimal kzArea = houseMap.values().stream()
                    .flatMap(List::stream)
                    .filter(house -> {
                        int type = house.getHouseResourceType();
                        return type == HouseEnums.HouseResourceTypeEnum.KZ.getCode() || type == HouseEnums.HouseResourceTypeEnum.SBC.getCode();
                    })
                    .map(HouseResourcePage::getHouseArea)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            houseResourceProfileGraph.setKzArea(kzArea);
            BigDecimal jyArea = houseMap.values().stream()
                    .flatMap(List::stream)
                    .filter(house -> {
                        int type = house.getHouseResourceType();
                        return type == HouseEnums.HouseResourceTypeEnum.ZY.getCode() || type == HouseEnums.HouseResourceTypeEnum.GK.getCode() || type == HouseEnums.HouseResourceTypeEnum.ZYZY.getCode();
                    })
                    .map(HouseResourcePage::getHouseArea)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            houseResourceProfileGraph.setJyArea(jyArea);
            BigDecimal totalArea = houseMap.values().stream()
                    .map(x -> x.stream()
                            .map(HouseResourcePage::getHouseArea)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            houseResourceProfileGraph.setAreaTotal(totalArea);
            houseResourceProfileGraph.setHouseCount(distinctList.size());
            houseResourceProfileGraph.setHouseResources(v.stream().sorted(Comparator.comparing(HouseResourcePage::getHouseNumber, Comparator.nullsLast(String::compareTo))).collect(Collectors.toList()));
            resourceProfileGraphList.add(houseResourceProfileGraph);
        });
        return resourceProfileGraphList.stream().sorted(Comparator.comparing(HouseResourceProfileGraph::getPosition, Comparator.nullsLast(Integer::compareTo))).collect(Collectors.toList());
    }

    @Override
    public List<HouseResourceProfileGraph> appProfileGraphHouseResources(HouseResourcePageQuery query) {
        GridRegionExample gridRegionExample = new GridRegionExample();
        gridRegionExample.createCriteria().andWgParentIdEqualTo(query.getThree()).andLevelEqualTo("4");
        List<GridRegion> fourList = gridRegionMapper.selectByExample(gridRegionExample);
        if (!CollectionUtils.isEmpty(fourList)) {
            List<HouseResourceProfileGraph> result = new ArrayList<>();
            List<HouseResourcePage> houseResources = houseResourceMapper.selectList(query);
            for (GridRegion gridRegion : fourList) {
                HouseResourceProfileGraph houseResourceProfileGraph = new HouseResourceProfileGraph();
                houseResourceProfileGraph.setId(gridRegion.getWgCode());
                houseResourceProfileGraph.setWgName(gridRegion.getWgName());
                houseResourceProfileGraph.setPosition(gridRegion.getPosition());
                if (Objects.nonNull(gridRegion.getJzArea())) {
                    houseResourceProfileGraph.setJzArea(new BigDecimal(gridRegion.getJzArea()));
                }
                List<HouseResourcePage> collect = houseResources.stream().filter(x -> x.getFour().equals(gridRegion.getWgCode())).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(collect)) {
                    houseResourceProfileGraph.setHouseResources(Collections.emptyList());
                    result.add(houseResourceProfileGraph);
                } else {
                    houseResourceProfileGraph.setHouseResources(handleHouseList(collect, true));
                    result.add(houseResourceProfileGraph);
                }
            }
            result.forEach(x -> {
                List<HouseResourcePage> distinctList = new ArrayList<>(x.getHouseResources().stream().collect(Collectors.toMap(HouseResourcePage::getId, Function.identity(), (a, b) -> a)).values());
                Map<String, List<HouseResourcePage>> houseMap = distinctList.stream().collect(Collectors.groupingBy(HouseResourcePage::getId));
                BigDecimal kzArea = houseMap.values().stream()
                        .flatMap(List::stream)
                        .filter(house -> {
                            int type = house.getHouseResourceType();
                            return type == HouseEnums.HouseResourceTypeEnum.KZ.getCode() || type == HouseEnums.HouseResourceTypeEnum.SBC.getCode();
                        })
                        .map(HouseResourcePage::getHouseArea)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                x.setKzArea(kzArea);
                BigDecimal jyArea = houseMap.values().stream()
                        .flatMap(List::stream)
                        .filter(house -> {
                            int type = house.getHouseResourceType();
                            return type == HouseEnums.HouseResourceTypeEnum.ZY.getCode() || type == HouseEnums.HouseResourceTypeEnum.GK.getCode() || type == HouseEnums.HouseResourceTypeEnum.ZYZY.getCode();
                        })
                        .map(HouseResourcePage::getHouseArea)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                x.setJyArea(jyArea);
                BigDecimal totalArea = houseMap.values().stream()
                        .map(y -> y.stream()
                                .map(HouseResourcePage::getHouseArea)
                                .filter(Objects::nonNull)
                                .reduce(BigDecimal.ZERO, BigDecimal::add))
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                x.setAreaTotal(totalArea);
                x.setHouseCount(distinctList.size());
                x.setKzCount(x.getHouseResources().stream().filter(y -> y.getHouseResourceType() == HouseEnums.HouseResourceTypeEnum.KZ.getCode() || y.getHouseResourceType() == HouseEnums.HouseResourceTypeEnum.SBC.getCode()).count());
            });
            return result.stream().sorted(Comparator.comparing(HouseResourceProfileGraph::getPosition, Comparator.nullsLast(Integer::compareTo))).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    @Override
    public List<HouseResourcePage> listHouseResources(HouseResourcePageQuery query) {
        List<HouseResourcePage> list = houseResourceMapper.selectList(query);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        } else {
            return handleHouseList(list, true);
        }
    }

    @Override
    public List<HouseResourcePage> exportListHouseResources(HouseResourcePageQuery query) {
        List<String> wgCodes = resolveCurrentUserWgCodes(false);
        if (wgCodes != null) {
            query.setWgCodes(wgCodes);
        }
        List<HouseResourcePage> list = houseResourceMapper.exportList(query);
        list.forEach(x -> {
            BusinessEntitySell businessEntitySell2024 = businessEntitySellMapper.selectBusinessEntitySellByBusinessEntityInfoId(x.getBusinessEntityInfoId(), 2024);
            if (Objects.nonNull(businessEntitySell2024)) {
                if (Objects.nonNull(businessEntitySell2024.getYearSell())) {
                    x.setYearSell2024(businessEntitySell2024.getYearSell());
                }
            }
            BusinessEntitySell businessEntitySell2025 = businessEntitySellMapper.selectBusinessEntitySellByBusinessEntityInfoId(x.getBusinessEntityInfoId(), 2025);
            if (Objects.nonNull(businessEntitySell2025)) {
                if (Objects.nonNull(businessEntitySell2025.getYearSell())) {
                    x.setYearSell2025(businessEntitySell2025.getYearSell());
                }
            }
            BusinessEntitySell businessEntityTax2024 = businessEntitySellMapper.selectBusinessEntityTaxByBusinessEntityInfoId(x.getBusinessEntityInfoId(), 2024);
            if (Objects.nonNull(businessEntityTax2024)) {
                if (Objects.nonNull(businessEntityTax2024.getYearTax())) {
                    x.setYearTax2024(businessEntityTax2024.getYearTax());
                }
            }
            BusinessEntitySell businessEntityTax2025 = businessEntitySellMapper.selectBusinessEntityTaxByBusinessEntityInfoId(x.getBusinessEntityInfoId(), 2025);
            if (Objects.nonNull(businessEntityTax2025)) {
                if (Objects.nonNull(businessEntityTax2025.getYearTax())) {
                    x.setYearTax2025(businessEntityTax2025.getYearTax());
                }
            }
        });
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        } else {
            return handleHouseList(list, false);
        }
    }

    /** 行业分类固定顺序，用于并发查询各行业的年销售合计。 */
    private static final List<String> INDUSTRY_CLASSIFICATIONS = Arrays.asList(
            "工业", "建筑业", "批发", "零售", "住宿", "餐饮", "服务业", "其他");

    @Override
    public BusinessEntityStatisticsDetail businessEntityStatistics(HouseResourcePageQuery query) {
        List<HouseResourcePage> list = houseResourceMapper.selectProfileGraph(query);
        BusinessEntityStatisticsDetail detail = new BusinessEntityStatisticsDetail();
        if (CollectionUtils.isEmpty(list)) {
            return detail;
        }

        // 原实现起了 20+ 个 CompletableFuture 各扫一遍 list，纯内存操作没有并发收益，
        // 反而让口径分散。改为单遍累加：type / 房源属性 / 房源类型 / 收入税收 / 规上统计全部一次走完。
        long lyCount = 0, jdCount = 0, jqCount = 0, zhtCount = 0, scCount = 0;
        Set<String> sjLy = new HashSet<>();
        Set<String> sjJd = new HashSet<>();
        Set<String> sjJq = new HashSet<>();
        Set<String> sjZht = new HashSet<>();
        Set<String> sjSc = new HashSet<>();
        long zlCount = 0, zyCount = 0, qtCount = 0;
        long zaiyingCount = 0, xzCount = 0, gkCount = 0, sbCount = 0, zyzyCount = 0;
        BigDecimal xsSum = BigDecimal.ZERO;
        BigDecimal ssSum = BigDecimal.ZERO;
        long gsCount = 0;
        BigDecimal gsSsSum = BigDecimal.ZERO;
        BigDecimal gsXsSum = BigDecimal.ZERO;

        for (HouseResourcePage x : list) {
            String three = x.getThree();
            switch (x.getType()) {
                case 1: lyCount++;  sjLy.add(three);  break;
                case 4: jdCount++;  sjJd.add(three);  break;
                case 5: jqCount++;  sjJq.add(three);  break;
                case 6: zhtCount++; sjZht.add(three); break;
                case 7: scCount++;  sjSc.add(three);  break;
                default: break;
            }

            switch (x.getHouseResourceAttribute()) {
                case 0: zlCount++; break;
                case 1: qtCount++; break;
                case 2: zyCount++; break;
                default: break;
            }

            switch (x.getHouseResourceType()) {
                case 0: zaiyingCount++; break;
                case 1: xzCount++;      break;
                case 2: gkCount++;      break;
                case 3: sbCount++;      break;
                case 4: zyzyCount++;    break;
                default: break;
            }

            if (x.getIncome() != null)   xsSum = xsSum.add(x.getIncome());
            if (x.getTaxCount() != null) ssSum = ssSum.add(x.getTaxCount());

            if (Boolean.TRUE.equals(x.getGs())) {
                gsCount++;
                if (x.getTaxCount() != null) gsSsSum = gsSsSum.add(x.getTaxCount());
                if (x.getIncome() != null)   gsXsSum = gsXsSum.add(x.getIncome());
            }
        }

        detail.setLyCount(lyCount);
        detail.setJdCount(jdCount);
        detail.setJqCount(jqCount);
        detail.setZhtCount(zhtCount);
        detail.setScCount(scCount);

        detail.setSjLyCount(sjLy.size());
        detail.setSjJdCount(sjJd.size());
        detail.setSjJqCount(sjJq.size());
        detail.setSjZhtCount(sjZht.size());
        detail.setSjScCount(sjSc.size());

        detail.setZlCount(zlCount);
        detail.setZyCount(zyCount);
        detail.setQtCount(qtCount);

        detail.setZaiyingCount(zaiyingCount);
        detail.setXzCount(xzCount);
        detail.setGkCount(gkCount);
        detail.setSbCount(sbCount);
        detail.setZyzyCount(zyzyCount);

        detail.setXsCount(xsSum);
        detail.setSsCount(ssSum);
        detail.setGsCount(gsCount);
        detail.setGsSsCount(gsSsSum);
        detail.setGsXsCount(gsXsSum);

        // 主体数量
        List<String> entityIds = list.stream()
                .map(HouseResourcePage::getBusinessEntityInfoId)
                .collect(Collectors.toList());
        List<BusinessEntityInfo> businessEntityInfos = businessEntityInfoMapper.selectByIdList(entityIds);
        detail.setZtCount(businessEntityInfos.size());

        // 行业销售：每个行业是一次独立的 DB 查询，保留并发
        Map<String, BigDecimal> industrySums = computeIndustrySells(businessEntityInfos);
        detail.setGyCount(industrySums.getOrDefault("工业", BigDecimal.ZERO));
        detail.setJzyCount(industrySums.getOrDefault("建筑业", BigDecimal.ZERO));
        detail.setPfCount(industrySums.getOrDefault("批发", BigDecimal.ZERO));
        detail.setLsCount(industrySums.getOrDefault("零售", BigDecimal.ZERO));
        detail.setZsCount(industrySums.getOrDefault("住宿", BigDecimal.ZERO));
        detail.setCyCount(industrySums.getOrDefault("餐饮", BigDecimal.ZERO));
        detail.setFwyCount(industrySums.getOrDefault("服务业", BigDecimal.ZERO));
        detail.setQtjjfzCount(industrySums.getOrDefault("其他", BigDecimal.ZERO));

        detail.setJjzl(industrySums.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add));
        return detail;
    }

    /** 按行业并发查询每个分类下的年销售合计。 */
    private Map<String, BigDecimal> computeIndustrySells(List<BusinessEntityInfo> infos) {
        Map<String, CompletableFuture<BigDecimal>> futures = INDUSTRY_CLASSIFICATIONS.stream()
                .collect(Collectors.toMap(
                        Function.identity(),
                        industry -> CompletableFuture.supplyAsync(
                                () -> getIndustrySellCount(infos, industry, businessEntitySellMapper),
                                executor)));
        Map<String, BigDecimal> result = new LinkedHashMap<>();
        futures.forEach((k, f) -> result.put(k, f.join()));
        return result;
    }

    @Override
    public PageBusinessEntityStatisticsDetail pageBusinessEntityStatistics(PageBusinessEntityStatisticsQuery query) {
        List<HouseResourcePage> list = houseResourceMapper.selectPageBusinessEntityStatistics();
        if (CollectionUtils.isEmpty(list)) {
            return new PageBusinessEntityStatisticsDetail();
        }
        List<HouseResourcePage> result = new ArrayList<>();
        if (!CollectionUtils.isEmpty(query.getTwos())) {
            for (String two : query.getTwos()) {
                for (HouseResourcePage houseResourcePage : list) {
                    if (two.equals(houseResourcePage.getTwo())) {
                        result.add(houseResourcePage);
                    }
                }
            }
        } else {
            List<String> wgCodes = resolveCurrentUserWgCodes(true);
            if (wgCodes != null) {
                for (String two : wgCodes) {
                    for (HouseResourcePage houseResourcePage : list) {
                        if (two.equals(houseResourcePage.getTwo())) {
                            result.add(houseResourcePage);
                        }
                    }
                }
            } else {
                result = list;
            }
        }
        if (CollectionUtils.isEmpty(result)) {
            return new PageBusinessEntityStatisticsDetail();
        }
        PageBusinessEntityStatisticsDetail pageBusinessEntityStatisticsDetail = new PageBusinessEntityStatisticsDetail();
        pageBusinessEntityStatisticsDetail.setLyCount(result.stream().filter(x -> x.getType() == 1).count());
        pageBusinessEntityStatisticsDetail.setDlCount(result.stream().filter(x -> x.getType() == 4).count());
        pageBusinessEntityStatisticsDetail.setJqCount(result.stream().filter(x -> x.getType() == 5).count());
        pageBusinessEntityStatisticsDetail.setZhtCount(result.stream().filter(x -> x.getType() == 6).count());
        pageBusinessEntityStatisticsDetail.setScCount(result.stream().filter(x -> x.getType() == 7).count());
        pageBusinessEntityStatisticsDetail.setTotalCount(result.size());

        pageBusinessEntityStatisticsDetail.setSjLyCount(result.stream().filter(x -> x.getType() == 1).collect(Collectors.groupingBy(HouseResourcePage::getThree)).size());
        pageBusinessEntityStatisticsDetail.setSjDlCount(result.stream().filter(x -> x.getType() == 4).collect(Collectors.groupingBy(HouseResourcePage::getThree)).size());
        pageBusinessEntityStatisticsDetail.setSjJqCount(result.stream().filter(x -> x.getType() == 5).collect(Collectors.groupingBy(HouseResourcePage::getThree)).size());
        pageBusinessEntityStatisticsDetail.setSjZhtCount(result.stream().filter(x -> x.getType() == 6).collect(Collectors.groupingBy(HouseResourcePage::getThree)).size());
        pageBusinessEntityStatisticsDetail.setSjScCount(result.stream().filter(x -> x.getType() == 7).collect(Collectors.groupingBy(HouseResourcePage::getThree)).size());
        pageBusinessEntityStatisticsDetail.setSjTotalCount(result.stream().collect(Collectors.groupingBy(HouseResourcePage::getThree)).size());

        pageBusinessEntityStatisticsDetail.setXsCount(result.stream().map(HouseResourcePage::getIncome).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
        pageBusinessEntityStatisticsDetail.setSsCount(result.stream().map(HouseResourcePage::getTaxCount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));

        pageBusinessEntityStatisticsDetail.setGsCount(result.stream().filter(x -> Objects.nonNull(x.getGs()) && x.getGs().equals(Boolean.TRUE)).count());
        pageBusinessEntityStatisticsDetail.setGsXsCount(result.stream().filter(x -> Objects.nonNull(x.getGs()) && x.getGs().equals(Boolean.TRUE)).map(HouseResourcePage::getIncome).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
        pageBusinessEntityStatisticsDetail.setGsSsCount(result.stream().filter(x -> Objects.nonNull(x.getGs()) && x.getGs().equals(Boolean.TRUE)).map(HouseResourcePage::getTaxCount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
        return pageBusinessEntityStatisticsDetail;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveYearTax(YearTaxSaveUpdateDto dto) {
        if (Objects.nonNull(dto.getId())) {
            BusinessEntitySell existingSell = businessEntitySellMapper.selectBusinessEntitySellById(dto.getId());
            if (existingSell == null) {
                throw new RuntimeException("未找到对应的记录");
            }
            BeanUtils.copyProperties(dto, existingSell);
            businessEntitySellMapper.updateBusinessEntitySell(existingSell);
        } else {
            BusinessEntitySell businessEntitySell = new BusinessEntitySell();
            BeanUtils.copyProperties(dto, businessEntitySell);
            businessEntitySell.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            businessEntitySellMapper.insertBusinessEntitySell(businessEntitySell);
        }
    }

    @Override
    public List<BusinessEntityDto> listByBusinessEntityInfoName(String name) {
        List<BusinessEntityInfo> businessEntityInfos = businessEntityInfoMapper.selectByName(name);
        if (CollectionUtils.isEmpty(businessEntityInfos)) {
            return Collections.emptyList();
        }
        List<BusinessEntityDto> result = new ArrayList<>();
        businessEntityInfos.forEach(x -> {
            BusinessEntityDto businessEntityDto = new BusinessEntityDto();
            BeanUtils.copyProperties(x, businessEntityDto);
            List<HouseResourceBusinessEntityInfoMapping> houseResourceBusinessEntityInfoMappings = houseResourceBusinessEntityInfoMappingMapper.selectByBusinessEntityInfoId(x.getId());
            if (!CollectionUtils.isEmpty(houseResourceBusinessEntityInfoMappings)) {
                List<HouseResourcePage> list = houseResourceMapper.selectByIdList(houseResourceBusinessEntityInfoMappings.stream().map(HouseResourceBusinessEntityInfoMapping::getHouseResourceId).collect(Collectors.toList()));
                List<HouseResourcePage> handleList = handleHouseList(list, false);
                businessEntityDto.setHouseResources(handleList);
            }
            List<BusinessEntitySell> businessEntitySells = businessEntitySellMapper.selectBusinessEntitySellByEntityId(x.getId());
            if (!CollectionUtils.isEmpty(businessEntitySells)) {
                businessEntityDto.setBusinessEntitySells(businessEntitySells);
            }
            result.add(businessEntityDto);
        });
        return result ;
    }

    /** HOUSE_RESOURCE 按 type 展示时固定补齐的档位，缺失档位会补一个 count = 0 的桶。 */
    private static final List<Integer> HOUSE_RESOURCE_TYPE_BUCKETS = Arrays.asList(1, 4, 5, 6, 7);

    @Override
    public HouseResourceDataCollection houseResourceDataCollection(HouseResourceDataCollectionQuery query) {
        if (query.getChangeStartTime() == null || query.getChangeEndTime() == null) {
            throw new IllegalArgumentException("changeStartTime / changeEndTime 不能为空");
        }
        if (query.getChangeStartTime().after(query.getChangeEndTime())) {
            throw new IllegalArgumentException("changeStartTime 不能晚于 changeEndTime");
        }

        applyGridPermission(query);

        HouseResourceDataCollection result = new HouseResourceDataCollection();

        // HOUSE_RESOURCE: insert / update 出详情（带 ids），delete 只出 count。
        List<HouseResourceDataCollectionDetailRow> houseDetails =
                houseResourceMapper.selectHouseResourceChangeDetails(query);
        List<HouseResourceDataCollectionRow> houseDeleteRows =
                houseResourceMapper.selectHouseResourceDeleteCount(query);

        result.setHouseResourceInsert(buildHouseTypeBuckets(houseDetails, OperationType.INSERT.getCode(), true));
        result.setHouseResourceUpdate(buildHouseTypeBuckets(houseDetails, OperationType.UPDATE.getCode(), true));
        result.setHouseResourceDelete(buildHouseTypeBucketsFromCount(houseDeleteRows));

        // BUSINESS_ENTITY_INFO：结构与房源对称，按关联房源 hr2.type 分桶（1/4/5/6/7）。
        // insert / update 出详情（带 ids，前端 drilldown 时需配合 wgType = type 使用），delete 只出 count。
        List<HouseResourceDataCollectionDetailRow> entityDetails =
                houseResourceMapper.selectBusinessEntityChangeDetails(query);
        List<HouseResourceDataCollectionRow> entityDeleteRows =
                houseResourceMapper.selectBusinessEntityDeleteCount(query);

        result.setBusinessEntityInsert(buildHouseTypeBuckets(entityDetails, OperationType.INSERT.getCode(), true));
        result.setBusinessEntityUpdate(buildHouseTypeBuckets(entityDetails, OperationType.UPDATE.getCode(), true));
        result.setBusinessEntityDelete(buildHouseTypeBucketsFromCount(entityDeleteRows));

        return result;
    }

    /**
     * 无需网格限制的登录人白名单（即便不是 admin 也可以看全量）。
     * 目前只给 {@link #pageHouseResources}、{@link #businessEntityStatistics}、{@link #houseResourceDataCollection}
     * 这类"列表/汇总"场景启用；{@link #exportListHouseResources} 保持原有行为不放开。
     */
    public static final Set<String> WG_PERMISSION_BYPASS_USER_IDS = new HashSet<>(Arrays.asList(
            "794aa2c8b5c24933a30591dd7dc439ed",
            "ca1df7d1a3f347dc9e73e8283dd134a5",
            "3cf3b0fa6c0945919504be4aa237f89e"));

    /**
     * 计算当前登录人应被限制到的 wgCodes 列表。返回 {@code null} 表示无需限制（全量可见），
     * 返回非 {@code null} 表示需要把后续查询限定在这些网格内。
     *
     * <p>短路条件（任意命中即不限制）：</p>
     * <ul>
     *     <li>登录人没有任何有效的 {@code grid_region_user} 绑定；</li>
     *     <li>登录人是 admin；</li>
     *     <li>{@code applyBypassWhitelist = true} 且登录人在 {@link #WG_PERMISSION_BYPASS_USER_IDS} 里。</li>
     * </ul>
     *
     * @param applyBypassWhitelist 是否启用白名单短路。列表/汇总类场景传 {@code true}；
     *                             导出等严格场景传 {@code false} 保持原行为。
     */
    private List<String> resolveCurrentUserWgCodes(boolean applyBypassWhitelist) {
        String userId = SecurityUtils.getLoginUser().getUser().getUserId();
        GridRegionUserExample example = new GridRegionUserExample();
        example.createCriteria().andIsValidEqualTo("1").andUserIdEqualTo(userId);
        List<GridRegionUser> gridRegionUsers = gridRegionUserMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(gridRegionUsers) || SecurityUtils.getLoginUser().getUser().isAdmin()) {
            return null;
        }
        if (applyBypassWhitelist && WG_PERMISSION_BYPASS_USER_IDS.contains(userId)) {
            return null;
        }
        return gridRegionUsers.stream().map(GridRegionUser::getWgId).collect(Collectors.toList());
    }

    /**
     * 给 dataCollection 查询注入当前登录人的网格权限（与 pageHouseResources 保持一致）。
     */
    private void applyGridPermission(HouseResourceDataCollectionQuery query) {
        List<String> wgCodes = resolveCurrentUserWgCodes(true);
        if (wgCodes != null) {
            query.setWgCodes(wgCodes);
        }
    }

    /**
     * 按 HOUSE_RESOURCE_TYPE_BUCKETS 把指定 operation 下的详情行聚合成 {type, count, ids} 桶。
     *
     * @param rows        详情行列表（可能包含多个 operation 的行）
     * @param operationType 本次要筛出的 operation
     * @param includeIds  true 填充 ids，false 只填 count（当前调用点都传 true）
     */
    private List<HouseResourceDataCollectionSimpleObject> buildHouseTypeBuckets(
            List<HouseResourceDataCollectionDetailRow> rows, int operationType, boolean includeIds) {
        Map<Integer, List<String>> idsByType = rows.stream()
                .filter(r -> r.getOperationType() == operationType)
                .collect(Collectors.groupingBy(
                        HouseResourceDataCollectionDetailRow::getType,
                        Collectors.mapping(HouseResourceDataCollectionDetailRow::getSubjectId, Collectors.toList())));
        List<HouseResourceDataCollectionSimpleObject> list = new ArrayList<>(HOUSE_RESOURCE_TYPE_BUCKETS.size());
        for (Integer type : HOUSE_RESOURCE_TYPE_BUCKETS) {
            List<String> ids = idsByType.getOrDefault(type, Collections.emptyList());
            HouseResourceDataCollectionSimpleObject item = new HouseResourceDataCollectionSimpleObject();
            item.setType(type);
            item.setCount(ids.size());
            if (includeIds) {
                item.setIds(ids);
            }
            list.add(item);
        }
        return list;
    }

    /**
     * delete 桶从聚合 count 行构造，不填 ids。同样按 HOUSE_RESOURCE_TYPE_BUCKETS 补齐。
     */
    private List<HouseResourceDataCollectionSimpleObject> buildHouseTypeBucketsFromCount(
            List<HouseResourceDataCollectionRow> rows) {
        Map<Integer, Long> byType = rows.stream()
                .collect(Collectors.toMap(HouseResourceDataCollectionRow::getType,
                        HouseResourceDataCollectionRow::getCount,
                        Long::sum));
        List<HouseResourceDataCollectionSimpleObject> list = new ArrayList<>(HOUSE_RESOURCE_TYPE_BUCKETS.size());
        for (Integer type : HOUSE_RESOURCE_TYPE_BUCKETS) {
            HouseResourceDataCollectionSimpleObject item = new HouseResourceDataCollectionSimpleObject();
            item.setType(type);
            item.setCount(byType.getOrDefault(type, 0L));
            list.add(item);
        }
        return list;
    }

    private List<HouseResourcePage> handleHouseList(List<HouseResourcePage> list, boolean houseNumberSortFlag) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        CompletableFuture<Set<String>> twoCodesFuture = CompletableFuture.supplyAsync(() -> list.stream().map(HouseResourcePage::getTwo).collect(Collectors.toSet()), executor);
        CompletableFuture<Set<String>> threeCodesFuture = CompletableFuture.supplyAsync(() -> list.stream().map(HouseResourcePage::getThree).collect(Collectors.toSet()), executor);
        CompletableFuture<Set<String>> fourCodesFuture = CompletableFuture.supplyAsync(() -> list.stream().map(HouseResourcePage::getFour).collect(Collectors.toSet()), executor);
        CompletableFuture<List<String>> idsFuture = CompletableFuture.supplyAsync(() -> list.stream().map(HouseResourcePage::getId).collect(Collectors.toList()), executor);

        Set<String> twoCodes = twoCodesFuture.join();
        Set<String> threeCodes = threeCodesFuture.join();
        Set<String> fourCodes = fourCodesFuture.join();
        List<String> ids = idsFuture.join();


        CompletableFuture<Map<String, GridRegion>> twoMapFuture = CompletableFuture.supplyAsync(() -> gridRegionMapper.selectByCodes(twoCodes).stream().collect(Collectors.toMap(GridRegion::getWgCode, gridRegion -> gridRegion, (e, r) -> e)), executor);
        CompletableFuture<Map<String, GridRegion>> threeMapFuture = CompletableFuture.supplyAsync(() -> gridRegionMapper.selectByCodes(threeCodes).stream().collect(Collectors.toMap(GridRegion::getWgCode, gridRegion -> gridRegion, (e, r) -> e)), executor);
        CompletableFuture<Map<String, GridRegion>> fourMapFuture = CompletableFuture.supplyAsync(() -> gridRegionMapper.selectByCodes(fourCodes).stream().collect(Collectors.toMap(GridRegion::getWgCode, gridRegion -> gridRegion, (e, r) -> e)), executor);
        CompletableFuture<Map<String, List<HouseResourceBusinessEntityInfoMapping>>> mappingMapFuture = CompletableFuture.supplyAsync(() -> houseResourceBusinessEntityInfoMappingMapper.selectByHouseResourceIds(ids).stream().collect(Collectors.groupingBy(HouseResourceBusinessEntityInfoMapping::getHouseResourceId)), executor);

        Map<String, GridRegion> twoMap = twoMapFuture.join();
        Map<String, GridRegion> threeMap = threeMapFuture.join();
        Map<String, GridRegion> fourMap = fourMapFuture.join();
        Map<String, List<HouseResourceBusinessEntityInfoMapping>> mappingMap = mappingMapFuture.join();

        List<String> entityInfoIds = mappingMap.values().stream()
                .flatMap(List::stream)
                .map(HouseResourceBusinessEntityInfoMapping::getBusinessEntityInfoId)
                .collect(Collectors.toList());

        Map<String, List<BusinessEntityInfo>> entityInfoMap;
        if (CollectionUtils.isEmpty(entityInfoIds)) {
            entityInfoMap = Collections.emptyMap();
        } else {
            entityInfoMap = businessEntityInfoMapper.selectByIdList(entityInfoIds).stream().collect(Collectors.groupingBy(BusinessEntityInfo::getId));
        }

        List<HouseResourcePage> result = list.parallelStream().map(x -> {
            HouseResourcePage page = new HouseResourcePage();
            BeanUtils.copyProperties(x, page);

            GridRegion two = twoMap.get(page.getTwo());
            if (two != null) page.setWgName2(two.getWgName());
            GridRegion three = threeMap.get(page.getThree());
            if (three != null) page.setWgName3(three.getWgName());
            if (!StringUtils.hasText(page.getWgName4())) {
                GridRegion four = fourMap.get(page.getFour());
                if (four != null) page.setWgName4(four.getWgName());
            }
            page.setHouseResourceAttributeText(HouseEnums.HouseResourceAttributeEnum.getDescByCode(page.getHouseResourceAttribute()));
            page.setHouseResourceTypeText(HouseEnums.HouseResourceTypeEnum.getDescByCode(page.getHouseResourceType()));
            page.setOrientationText(HouseEnums.OrientationEnum.getDescByCode(page.getOrientation()));
            page.setDecorationConditionText(HouseEnums.DecorationConditionEnum.getDescByCode(page.getDecorationCondition()));
            page.setRentalUnitText(HouseEnums.RentalUnitEnum.getDescByCode(page.getRentalUnit()));
            page.setHireStatusText(HouseEnums.hireStatusTextEnum.getDescByCode(page.getHireStatus()));
            List<HouseResourceBusinessEntityInfoMapping> mappings = mappingMap.get(page.getId());
            if (!CollectionUtils.isEmpty(mappings)) {
                List<String> entityIds = mappings.stream().map(HouseResourceBusinessEntityInfoMapping::getBusinessEntityInfoId).collect(Collectors.toList());
                List<BusinessEntityInfo> infos = entityIds.stream().map(entityInfoMap::get).filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(infos)) {
                    page.setBusinessEntityInfoNames(infos.stream().map(BusinessEntityInfo::getName).collect(Collectors.toList()));
                }
            }
            return page;
        }).collect(Collectors.toList());

        if (houseNumberSortFlag) {
            return result.stream().sorted(Comparator.comparing(HouseResourcePage::getHouseNumber, Comparator.nullsLast(String::compareTo))).collect(Collectors.toList());
        } else {
            return result;
        }
    }

    private BigDecimal getIndustrySellCount(List<BusinessEntityInfo> infos, String industry, BusinessEntitySellMapper sellMapper) {
        List<String> ids = infos.stream()
                .filter(x -> StringUtils.hasText(x.getIndustryClassification()) && x.getIndustryClassification().equals(industry))
                .map(BusinessEntityInfo::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(ids)) {
            return BigDecimal.ZERO;
        }
        List<BusinessEntitySell> sells = sellMapper.selectBusinessEntitySellByEntityIds(ids);
        if (CollectionUtils.isEmpty(sells)) {
            return BigDecimal.ZERO;
        }
        return sells.stream().map(BusinessEntitySell::getYearSell).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}
