package com.persagy.dmp.rwd.digital.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.basic.constant.DigitalMessageConstant;
import com.persagy.dmp.basic.dto.RequestData;
import com.persagy.dmp.basic.model.CodeTableTypeEnum;
import com.persagy.dmp.codetable.client.CodeTableDataFacade;
import com.persagy.dmp.codetable.entity.CodeTableData;
import com.persagy.dmp.common.constant.CommonConstant;
import com.persagy.dmp.common.constant.ResponseCode;
import com.persagy.dmp.common.constant.ValidEnum;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.common.model.entity.BaseEntity;
import com.persagy.dmp.common.model.response.CommonResult;
import com.persagy.dmp.common.utils.ResultHelper;
import com.persagy.dmp.define.entity.ObjectInfoDefine;
import com.persagy.dmp.digital.entity.ObjectDigital;
import com.persagy.dmp.digital.entity.ObjectRelation;
import com.persagy.dmp.rwd.basic.constant.DigitalObjectType;
import com.persagy.dmp.rwd.basic.constant.DigitalRelCode;
import com.persagy.dmp.rwd.basic.utils.DigitalCommonUtils;
import com.persagy.dmp.rwd.basic.utils.DigitalMessageHelper;
import com.persagy.dmp.rwd.define.service.IObjectInfoService;
import com.persagy.dmp.rwd.define.service.IObjectTypeService;
import com.persagy.dmp.rwd.digital.dao.ObjectDigitalMapper;
import com.persagy.dmp.rwd.digital.service.IObjectDigitalService;
import com.persagy.dmp.rwd.digital.service.IObjectRelationService;
import com.persagy.dmp.rwd.parser.service.InfoDataFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 对象数据 实现类
 * @author Charlie Yu
 * @date 2021-07-08
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
@Slf4j
public class ObjectDigitalServiceImpl extends ServiceImpl<ObjectDigitalMapper, ObjectDigital> implements IObjectDigitalService {

    @Autowired
    private ObjectDigitalMapper dao;
    @Autowired
    private DigitalMessageHelper messageSender;
    @Autowired
    private IObjectInfoService infoService;
    @Autowired
    private IObjectTypeService typeService;
    @Autowired
    private IObjectRelationService relationService;

    @Override
    public ObjectDigital load(String id) {
        return dao.selectById(id);
    }

    @Override
    public List<ObjectDigital> queryByCondition(Wrapper<ObjectDigital> queryWrapper) {
        return list(queryWrapper);
    }

    @Override
    public Integer queryCount(Wrapper<ObjectDigital> queryWrapper) {
        return count(queryWrapper);
    }

    @Override
    public Page queryByCondition(Page page, Wrapper<ObjectDigital> queryWrapper) {
        return page(page, queryWrapper);
    }

    @Override
    public List<ObjectDigital> insert(List<ObjectDigital> voList) {
        validateOnSave(voList, false);
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        //获取对象类型
        Map<String, String> typeMap = typeService.queryObjTypeMap();
        voList.forEach(vo -> {
            // 生成ID
            if (StrUtil.isBlank(vo.getId())){
                if (StrUtil.isNotBlank(vo.getObjType())){
                    vo.setId(DigitalCommonUtils.fetchObjectId(vo.getProjectId(), vo.getObjType()));
                }else {
                    vo.setId(DigitalCommonUtils.fetchObjectId(vo.getProjectId(), typeMap.get(vo.getClassCode())));
                }
            }
            //设置对象类型-objType
            vo.setObjType(typeMap.get(vo.getClassCode()));
            vo.setValid(ValidEnum.TRUE.getType());
            // 新增
            dao.insert(vo);
            // 新增后消息
            messageSender.sendMessage(DigitalMessageConstant.OPERATE_AFTER_INSERT, null, vo, false);
        });
        return voList;
    }

    @Override
    public List<ObjectDigital> update(List<ObjectDigital> voList){
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        // 获取$remove字段
        Map<String, Set<String>> removeMap = convertRemoveInfo(voList);
        //查询数据库中存在的数据
        List<String> idList = CollUtil.getFieldValues(voList, BaseEntity.PROP_ID, String.class);
        List<ObjectDigital> dbList = listByIds(idList);
        Map<String, ObjectDigital> dbMap = CollectionUtil.fieldValueMap(dbList, BaseEntity.PROP_ID);

        //填充classCode
        fillClassCodeForUpdate(voList,dbMap);
        //信息点校验
        validateOnSave(voList, true);
        if(CollUtil.isEmpty(voList)) {
            return null;
        }
        voList.forEach(vo -> {
            ObjectDigital dbVO = MapUtil.get(dbMap, vo.getId(), ObjectDigital.class);
            // 容错处理
            if(dbVO == null) {
                return;
            }
            // 更新数据的属性合并和删除
            ensureUpdateInfos(vo, dbVO, removeMap.get(vo.getId()));
            // 修改
            dao.updateById(vo);
            // 修改后消息
            messageSender.sendMessage(DigitalMessageConstant.OPERATE_AFTER_UPDATE, dbVO, vo, false);
        });
        return voList;
    }

    /**
     * 补充更新信息
     * @param vo
     * @param dbVO
     * @param removeColumns
     */
    private void ensureUpdateInfos(ObjectDigital vo, ObjectDigital dbVO, Set<String> removeColumns) {
        // 获取对象属性，不需要处理基类属性
        Field[] fields = ReflectUtil.getFields(ObjectDigital.class);
        // 按属性处理
        for(Field field:fields) {
            Object fieldValue = ReflectUtil.getFieldValue(vo, field);
            Object dbValue = ReflectUtil.getFieldValue(dbVO, field);
            if(fieldValue == null && dbValue == null) {
                // 都为空不处理
                continue;
            }
            // 如果不是扩展属性
            if(!ObjectDigital.EXTRA_COLUMN.equalsIgnoreCase(field.getName())) {
                // 如果待删除
                if(CollUtil.contains(removeColumns, field.getName())) {
                    ReflectUtil.setFieldValue(vo, field, null);
                    continue;
                }
                // 如果vo里值为空，则将数据库值赋上
                if(fieldValue == null) {
                    ReflectUtil.setFieldValue(vo, field, dbValue);
                }
                continue;
            }
            // 如果是扩展属性
            ObjectNode infos = vo.getInfos();
            ObjectNode dbInfos = dbVO.getInfos();
            // 如果数据库有值，则补充到vo的infos中
            if(dbInfos != null) {
                if(infos == null) {
                    vo.setInfos(dbInfos);
                } else {
                    // 如果都不为空，则将dbVO的infos数据拷贝到vo中
                    Iterator<String> infoFields = dbInfos.fieldNames();
                    while(infoFields.hasNext()) {
                        String infoField = infoFields.next();
                        if(infos.has(infoField)) {
                            // 如果已存在，则不需要
                            continue;
                        }
                        infos.set(infoField, dbInfos.get(infoField));
                    }
                }
            }
            // 最后，处理vo的infos
            Iterator<String> infoFields = infos.fieldNames();
            while(infoFields.hasNext()) {
                String infoField = infoFields.next();
                // 如果需要删除，则删除
                if(CollUtil.contains(removeColumns, infoField)) {
                    infoFields.remove();
                }
            }
            // 如果都删没了，则把infos置空
            if(infos == null || infos.size() <= 0) {
                vo.setInfos(null);
            }
        }
    }

    @Override
    public void delete(List<String> idList) {
        if(CollUtil.isEmpty(idList)) {
            return;
        }
        // 1.批量删除对象
        ObjectDigital vo = new ObjectDigital();
        vo.setValid(ValidEnum.FALSE.getType());
        LambdaQueryWrapper<ObjectDigital> objQueryWrapper = new LambdaQueryWrapper<>();
        objQueryWrapper.in(ObjectDigital::getId,idList);
        update(vo, objQueryWrapper);
        // 2.批量删除关系
        LambdaQueryWrapper<ObjectRelation> relWrapper = new LambdaQueryWrapper<>();
        relWrapper.in(ObjectRelation::getObjFrom, idList);
        relWrapper.or(wrapper->wrapper.in(ObjectRelation::getObjTo, idList));
        ObjectRelation objectRelation = new ObjectRelation();
        objectRelation.setValid(ValidEnum.FALSE.getType());
        relationService.delete(relWrapper);
        // 3.发送删除消息
        List<ObjectDigital> dbList = listByIds(idList);
        Map<String, ObjectDigital> dbMap = CollectionUtil.fieldValueMap(dbList, BaseEntity.PROP_ID);
        idList.forEach(id -> {
            ObjectDigital dbVO = MapUtil.get(dbMap, id, ObjectDigital.class);
            // 容错处理
            if(dbVO == null) {
                return;
            }
            // 删除后发送消息
            dbVO.setValid(ValidEnum.FALSE.getType());
            messageSender.sendMessage(DigitalMessageConstant.OPERATE_AFTER_DELETE, null, dbVO, false);
        });
    }

    /***
     * Description: 根据上级id查询下级关联对象
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/24 16:37
     * Update By lijie 2021/8/24 16:37
     */
    @Override
    public CommonResult<List<ObjectDigital>> queryObjectListSuperiorId(RequestData requestData){
        // 2.当只有项目id时不查询关系,直接根据项目id筛选所有对象
        if (StrUtil.isBlank(requestData.getBuildingId())
                && StrUtil.isBlank(requestData.getFloorId())
                && StrUtil.isBlank(requestData.getEquipmentId())){
            return queryObjectListByProjectId(requestData);
        }
        // 3.存在建筑id/楼层id/设备id
        // 3.1 获取关系code列表
        fillSubordinateRelCode(requestData);
        // 3.2 调用查询语句查询相关内容
        return analysisQueryResultList(dao
                .queryObjectListBySuperiorId(DigitalCommonUtils
                        .computePageObject(requestData),requestData),requestData,false,true);
    }
    /***
     * Description: 查询指定楼层下所有指定空间类型的空间
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/30 9:54
     * Update By lijie 2021/8/30 9:54
     */
    @Override
    public CommonResult<List<ObjectDigital>> querySpaceListByFloorIdAndRoomFuncType(RequestData requestData) {
        // 2.使用Fl2Sp关系查询楼层下的空间
        requestData.setRelCode(DigitalRelCode.FL_2_SP.getIndex());
        requestData.setObjType(DigitalObjectType.SPACE.getIndex());
        requestData.setRelId(requestData.getFloorId());
        Page<ObjectDigital> dataResult = dao
                .queryObjectListByFloorIdAndRoomFuncType(DigitalCommonUtils.computePageObject(requestData),requestData);
        return commonAnalysisPageResult(dataResult);
    }
    /***
     * Description:查询指定空间下所有指定对象类型的设备
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/30 17:03
     * Update By lijie 2021/8/30 17:03
     */
    @Override
    public CommonResult<List<ObjectDigital>> queryEquipListBySpaceIdAndClassCode(RequestData requestData) {
        // 2.使用Eq2Sp关系查询楼层下的空间
        requestData.setRelCode(DigitalRelCode.EQ_2_SP.getIndex());
        requestData.setObjType(DigitalObjectType.EQUIPMENT.getIndex());
        requestData.setRelId(requestData.getSpaceId());
        Page<ObjectDigital> dataResult = dao
                .queryObjectListByRelIdAndClassCode(DigitalCommonUtils.computePageObject(requestData),requestData);
        return ResultHelper.multi(dataResult.getRecords(),dataResult.getTotal());
    }
    /***
     * Description: 按类型编码查询所有设备
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/30 9:41
     * Update By lijie 2021/8/30 9:41
     */
    @Override
    public CommonResult<List<ObjectDigital>> queryEquipListByClassCode(RequestData requestData) {
        // 2.使用Fl2Sp关系查询楼层下的空间
        requestData.setObjType(DigitalObjectType.EQUIPMENT.getIndex());
        Page<ObjectDigital> dataResult = dao
                .queryEquipListByClassCode(DigitalCommonUtils.computePageObject(requestData),requestData);
        return commonAnalysisPageResult(dataResult);
    }
    /***
     * Description: 按系统查询系统下所有设备
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/30 9:41
     * Update By lijie 2021/8/30 9:41
     */
    @Override
    public CommonResult<List<ObjectDigital>> queryEquipListBySystemId(RequestData requestData) {
        // 2.使用Sy2Eq关系查询系统下的设备
        requestData.setRelId(requestData.getSystemId());
        requestData.setObjType(DigitalObjectType.EQUIPMENT.getIndex());
        requestData.setRelCode(DigitalRelCode.SY_2_EQ.getIndex());
        Page<ObjectDigital> dataResult = dao
                .queryObjectListByRelIdAndClassCode(DigitalCommonUtils.computePageObject(requestData),requestData);
        return ResultHelper.multi(dataResult.getRecords(),dataResult.getTotal());
    }

    /***
     * Description: 支持按指定空间功能类型查询此空间类型及其下级类型的所有空间列表
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/30 9:41
     * Update By lijie 2021/8/30 9:41
     */
    @Override
    public CommonResult<List<ObjectDigital>> querySpaceListByRoomFuncType(RequestData requestData) {
        // 1.计算分页参数
        DigitalCommonUtils.computePageParam(requestData);
        // 2.根据空间功能类型查询其下级空间功能类型
        List<CodeTableData> codeTableDatas = CodeTableDataFacade
                .queryCodeTableDatasByParentCode(AppContext.getContext().getGroupCode(),
                AppContext.getContext().getProjectId(),
                AppContext.getContext().getAppId(),
                AppContext.getContext().getAccountId(),
                RequestData.builder()
                        .typeCode(CodeTableTypeEnum.SPACE.getIndex())
                        .parentCode(requestData.getRoomFuncType())
                        .build());
        if (CollUtil.isEmpty(codeTableDatas)){
            return ResultHelper.multi(new ArrayList<>(),0L);
        }
        // 3.根据分页参数及空间功能类型code数组查询对象
        Set<String> codes = codeTableDatas.stream()
                .map(CodeTableData::getCode)
                .collect(Collectors.toSet());
        QueryWrapper<ObjectDigital> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(ObjectDigital.PROP_VALID,true);
        queryWrapper.eq(StrUtil.toUnderlineCase(CommonConstant.QUERY_GROUPCODE),AppContext.getContext().getGroupCode());
        if (StrUtil.isNotBlank(requestData.getProjectId())){
            queryWrapper.eq(StrUtil.toUnderlineCase(CommonConstant.QUERY_PROJECTID),requestData.getProjectId());
        }
        queryWrapper.in("JSON_UNQUOTE(JSON_EXTRACT(infos,'$.roomFuncType'))",codes);
        Page<ObjectDigital> page = new Page<>(requestData.getPage(), requestData.getSize(), true);
        Page<ObjectDigital> digitalPage = dao.selectPage(page, queryWrapper);
        return ResultHelper.multi(digitalPage.getRecords(),digitalPage.getTotal());
    }

    /***
     * Description: 根据图类型编码及关系类型编码查询对象及其关联对象
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/30 9:41
     * Update By lijie 2021/8/30 9:41
     */
    @Override
    public CommonResult<List<ObjectDigital>> queryObjectListByGraphCodeAndRelCode(RequestData requestData) {
        Page<ObjectDigital> objList = dao
                .queryObjectListByGraphCodeAndRelCode(DigitalCommonUtils.computePageObject(requestData),requestData);
        return analysisQueryResultList(objList,requestData,false,false);
    }
    /***
     * Description: 根据对象id批量更新对象的classCode
     * @param objectDigitals : 对象数组
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/9/27 17:46
     * Update By lijie 2021/9/27 17:46
     */
    @Override
    public CommonResult<List<ObjectDigital>> batchUpdateClassCodesByIds(List<ObjectDigital> objectDigitals) {
        // 1.校验数据必填
        for (ObjectDigital objectDigital : objectDigitals) {
            if (StrUtil.isBlank(objectDigital.getId())
                    || StrUtil.isBlank(objectDigital.getClassCode())){
                throw new BusinessException(ResponseCode.A0400.getCode(),
                        StrUtil.format("id:{}或classCode:{}不能为空",objectDigital.getId(),
                                objectDigital.getClassCode()));
            }
        }
        // 2.校验对象是否都存在以及对象要更新的classCode是否符合
        // 2.1 获得对象id集合
        Set<String> objIds = objectDigitals.stream().map(BaseEntity::getId).collect(Collectors.toSet());
        Map<String, ObjectDigital> digitalMap = objectDigitals.stream().collect(Collectors.toMap(ObjectDigital::getId, obj -> obj, (k1, k2) -> k1));
        // 2.2 根据对象id查询对象信息及对象的分类对应的classCode集合
        List<ObjectDigital> queryObjs = dao.queryObjectListByIds(objIds);
        if (CollUtil.isEmpty(queryObjs)){
            throw new BusinessException(ResponseCode.A0400.getCode(), StrUtil.format("id不存在:{}",objIds));
        }
        // 2.3 校验对象是否都存在
        Map<String, ObjectDigital> queryObjMap = queryObjs.stream().collect(Collectors.toMap(BaseEntity::getId, obj -> obj, (k1, k2) -> k1));
        List<ObjectDigital> updateObjs = new ArrayList<>();
        for (String objId : objIds) {
            if (!queryObjMap.containsKey(objId)){
                throw new BusinessException(ResponseCode.A0400.getCode(),
                        StrUtil.format("id不存在:{}",objId));
            }
            ObjectDigital queryObj = queryObjMap.get(objId);
            Set<String> validClassCodes = CollUtil.defaultIfEmpty(queryObj.getObjTypeClassCodes(), new HashSet<>());
            ObjectDigital objectDigital = digitalMap.get(objId);
            if (!validClassCodes.contains(objectDigital.getClassCode())){
                throw new BusinessException(ResponseCode.A0400.getCode(),
                        StrUtil.format("id:{}的classCode不符合:{}",objectDigital.getId(),objectDigital.getClassCode()));
            }
            queryObj.setClassCode(objectDigital.getClassCode());
            queryObj.setUpdateApp(AppContext.getContext().getAppId());
            queryObj.setModifier(AppContext.getContext().getAccountId());
            updateObjs.add(queryObj);
        }
        // 3.校验通过,直接更新
        updateBatchById(updateObjs);
        return ResultHelper.multi(new ArrayList<>());
    }


    /**
     * 查询对象类型
     * @param queryWrapper 查询条件
     * @return
     */
    @Override
    public List<String> queryObjectClassCode(QueryWrapper<ObjectDigital> queryWrapper) {
        queryWrapper.select("distinct class_code");
        List<ObjectDigital> objectDigitals = dao.selectList(queryWrapper);
        if(CollUtil.isEmpty(objectDigitals)){
            return new ArrayList<>();
        }
        return objectDigitals.stream().map(item -> item.getClassCode()).collect(Collectors.toList());
    }


    /***
     * Description: 通用处理分页数据方法
     * @param dataResult : 分页数据
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/30 14:15
     * Update By lijie 2021/8/30 14:15
     */
    private CommonResult<List<ObjectDigital>> commonAnalysisPageResult(Page<ObjectDigital> dataResult) {
        List<ObjectDigital> records = dataResult.getRecords();
        if (CollUtil.isEmpty(records)){
            return ResultHelper.multi(new ArrayList<>(),0L);
        }
        return ResultHelper.multi(records,dataResult.getTotal());
    }

    /***
     * Description: 根据对象id获取下级关系code数组
     * @param requestData : 请求参数
     * @return : void
     * @author : lijie
     * @date :2021/8/24 21:20
     * Update By lijie 2021/8/24 21:20
     */
    private void fillSubordinateRelCode(RequestData requestData) {
        if (StrUtil.isNotBlank(requestData.getEquipmentId())){
            // 设备下的部件
            requestData.setRelCodes(CollUtil.newHashSet(DigitalRelCode.EQ_2_EC.getIndex()));
            requestData.setFilterObjId(requestData.getEquipmentId());
            return;
        }
        if (StrUtil.isNotBlank(requestData.getFloorId())){
            // 楼层下的空间,设备,系统
            requestData.setRelCodes(CollUtil.newHashSet(DigitalRelCode.FL_2_SP.getIndex(),
                    DigitalRelCode.EQ_2_FL.getIndex(),DigitalRelCode.SY_2_FL.getIndex()));
            // 设置设备查询部件
            requestData.setQueryEcFlag(true);
            requestData.setEcRelCodes(CollUtil.newHashSet(DigitalRelCode.EQ_2_EC.getIndex()));
            requestData.setFilterObjId(requestData.getFloorId());
            return;
        }
        if (StrUtil.isNotBlank(requestData.getBuildingId())){
            // 建筑下的楼层,空间,设备,系统
            requestData.setRelCodes(CollUtil.newHashSet(DigitalRelCode.BD_2_FL.getIndex(),
                    DigitalRelCode.BD_2_SP.getIndex(),DigitalRelCode.SY_2_BD.getIndex(),
                    DigitalRelCode.EQ_2_BD.getIndex(),DigitalRelCode.BD_2_SH.getIndex()));
            // 设置设备查询部件
            requestData.setQueryEcFlag(true);
            requestData.setEcRelCodes(CollUtil.newHashSet(DigitalRelCode.EQ_2_EC.getIndex()));
            requestData.setFilterObjId(requestData.getBuildingId());
        }
    }

    /***
     * Description: 根据项目id查询对象列表
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/24 16:40
     * Update By lijie 2021/8/24 16:40
     */
    private CommonResult<List<ObjectDigital>> queryObjectListByProjectId(RequestData requestData) {
        // 1.查询结果集
        Page<ObjectDigital> resultList =
                dao.queryObjectDigitalsByProjectId(DigitalCommonUtils.computePageObject(requestData),requestData);
        return analysisQueryResultList(resultList,requestData,true,true);
    }
    /***
     * Description: 解析结果集
     * @param pageResult : 结果集
     * @param requestData : 请求参数
     * @return : com.persagy.dmp.common.model.response.CommonResult<java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>>
     * @author : lijie
     * @date :2021/8/25 13:16
     * Update By lijie 2021/8/25 13:16
     */
    private CommonResult<List<ObjectDigital>> analysisQueryResultList(Page<ObjectDigital> pageResult,
                                                                      RequestData requestData,
                                                                      Boolean fillRelFlag,
                                                                      Boolean fillSpaceFlag) {
        List<ObjectDigital> records = pageResult.getRecords();
        if (CollUtil.isEmpty(records)){
            return ResultHelper.multi(new ArrayList<>(),0);
        }
        if (requestData.isOnlyCount()){
            // 只返回数
            return ResultHelper.multi(new ArrayList<>(),pageResult.getTotal());
        }
        fillRelIds(records,fillRelFlag);
        // 2.过滤信息点
        retainObjInfos(records,requestData.getReturnInfos());
        // 3.处理Fl2Sp的关系,将对应的空间对象的楼层id扔进去
        fillFloorIdIntoSpaceObjFromIds(records,fillSpaceFlag);
        return ResultHelper.multi(records,pageResult.getTotal());
    }

    /***
     * Description: 当只有项目时需要填充关系id
     * @param vos : 查询的结果集
     * @param fillRelFlag : 0-不注入关系数据,1-注入关系数据
     * @return : void
     * @author : lijie
     * @date :2021/9/4 22:39
     * Update By lijie 2021/9/4 22:39
     */
    private void fillRelIds(List<ObjectDigital> vos, Boolean fillRelFlag) {
        if (null==fillRelFlag || !fillRelFlag){
            return;
        }
        // 注入关系
        // 2.1 根据楼层id查询建筑id
        // 2.2 根据空间id查询建筑id
        // 2.3 根据设备id查询建筑id
        // 2.4 根据系统id查询建筑id
        // 2.5 根据竖井id查询建筑id
        Set<String> objIds = vos.stream()
                .filter(object->!DigitalObjectType.BUILDING.getIndex().equals(object.getObjType()))
                .map(ObjectDigital::getId)
                .collect(Collectors.toSet());
        if (CollUtil.isEmpty(objIds)){
            return;
        }
        List<ObjectDigital> relVOs = relationService.queryRelatedObjects(objIds, CollUtil
                .newHashSet(DigitalRelCode.EQ_2_EC.getIndex(), DigitalRelCode.BD_2_FL.getIndex(),
                        DigitalRelCode.BD_2_SP.getIndex(), DigitalRelCode.SY_2_BD.getIndex(),
                        DigitalRelCode.EQ_2_BD.getIndex(),DigitalRelCode.BD_2_SH.getIndex()));
        if (CollUtil.isEmpty(relVOs)){
            return;
        }
        Map<String, ObjectDigital> digitalMap = relVOs.stream()
                .collect(Collectors.toMap(ObjectDigital::getId, relVO -> relVO, (k1, k2) -> k1));
        for (ObjectDigital vo : vos) {
            if (!digitalMap.containsKey(vo.getId())){
                continue;
            }
            ObjectDigital relVO = digitalMap.get(vo.getId());
            vo.setObjToIds(CollUtil.defaultIfEmpty(relVO.getObjToIds(),new HashSet<>()));
            vo.setObjFromIds(CollUtil.defaultIfEmpty(relVO.getObjFromIds(),new HashSet<>()));
        }
    }

    /***
     * Description: 填充关系id
     * @param objectDigitals : 查询结果集
     * @return : void
     * @author : lijie
     * @date :2021/8/26 12:00
     * Update By lijie 2021/8/26 12:00
     */
    private void fillFloorIdIntoSpaceObjFromIds(List<ObjectDigital> objectDigitals,Boolean fillSpaceFlag) {
        if (null==fillSpaceFlag || !fillSpaceFlag){
            return;
        }
        // 1.获得空间id
        Set<String> spaceIds = objectDigitals.stream()
                .filter(objectDigital -> DigitalObjectType.SPACE.getIndex().equals(objectDigital.getObjType()))
                .map(ObjectDigital::getId)
                .collect(Collectors.toSet());
        if (CollUtil.isEmpty(spaceIds)){
            return;
        }
        List<ObjectDigital> relResult = relationService.queryRelatedObjects(spaceIds, CollUtil.newHashSet(DigitalRelCode.FL_2_SP.getIndex()));
        if (CollUtil.isEmpty(relResult)){
            return;
        }
        Map<String, Set<String>> setMap = relResult
                .stream()
                .collect(Collectors.toMap(ObjectDigital::getId, ObjectDigital::getObjFromIds, (k1, k2) -> k1));
        for (ObjectDigital objectDigital : objectDigitals) {
            Set<String> objFromIds = CollUtil.defaultIfEmpty(objectDigital.getObjFromIds(),new HashSet<>());
            objFromIds.addAll(setMap.getOrDefault(objectDigital.getId(),new HashSet<>()));
            objectDigital.setObjFromIds(objFromIds);
        }
    }

    /***
     * Description: 过滤掉不需要的信息点
     * @param objectDigitals : 查询结果集
     * @param returnInfos : 返回的信息点key
     * @return : void
     * @author : lijie
     * @date :2021/8/25 13:13
     * Update By lijie 2021/8/25 13:13
     */
    private void retainObjInfos(List<ObjectDigital> objectDigitals, Set<String> returnInfos) {
        if (CollUtil.isEmpty(returnInfos) || CollUtil.isEmpty(objectDigitals)){
            return;
        }
        for (ObjectDigital objectDigital : objectDigitals) {
            ObjectNode infos = objectDigital.getInfos();
            if (null==infos || infos.size()<=0){
                continue;
            }
            infos.retain(returnInfos);
        }
    }

    /**
     * 保存校验
     * 编码全局唯一
     * @param voList
     * @param isUpdate
     */
    private void validateOnSave(List<ObjectDigital> voList, boolean isUpdate) {
        validateInfoCode(voList);
    }

    /**
     * 信息点值校验
     * @param voList
     */
    private void validateInfoCode(List<ObjectDigital> voList) {
        if(CollUtil.isEmpty(voList)) {
            return;
        }
        // 保存时，项目ID一定有，而且都是操作这个项目的数据。这是前提，若后续放开全集团保存，则应按项目分组后查询信息点
        String projectId = AppContext.getContext().getProjectId();
        //获取该信息点当前classCode
        List<String> classCodes = CollUtil.getFieldValues(voList, "classCode", String.class);
        // 查询信息点定义
        Map<String, Map<String, ObjectInfoDefine>> typeInfoMap = infoService.queryInfoMapByClass(projectId, classCodes);
        // 信息点校验器
        List<ObjectDigital> restList = new ArrayList<>();
        for(ObjectDigital vo:voList) {
            // 获取信息点列表
            Map<String, ObjectInfoDefine> infoMap = typeInfoMap == null ? null : typeInfoMap.get(vo.getClassCode());
            if(MapUtil.isEmpty(infoMap)) {
                log.error(StrUtil.format("没有对象类型【{}】的信息点定义信息！", vo.getClassCode()));
                continue;
            }
            // 只保留有对象类型的
            restList.add(vo);
            // 信息点值对象
            ObjectNode infos = vo.getInfos();
            if(infos == null || infos.isNull()) {
                continue;
            }
            // 按infos属性循环处理
            Iterator<String> infoKeys = infos.fieldNames();
            while(infoKeys.hasNext()) {
                String infoCode = infoKeys.next();
                // 如果信息点中不存在，移除
                if(!infoMap.containsKey(infoCode)) {
                    infoKeys.remove();
                    continue;
                }
                // 校验信息点值
                try {
                    InfoDataFactory.parse(infoMap.get(infoCode), infos.get(infoCode));
                } catch (BusinessException e) {
                    // 校验不通过的移除
                    infoKeys.remove();;
                    continue;
                }
            }
        }
        // 只保留能找到对象类型的
        voList.clear();
        if(CollUtil.isNotEmpty(restList)) {
            voList.addAll(restList);
        }
    }

    /**
     * 获取remove信息点
     * @param voList
     * @return Map<id, Set<RemoveColumn>>
     */
    private Map<String, Set<String>> convertRemoveInfo(List<ObjectDigital> voList){
        final String REMOVE_FIELD = "$remove";
        Map<String, Set<String>> removeMap = new HashMap<>(16);
        for (ObjectDigital vo : voList) {
            ObjectNode infos = vo.getInfos();
            // 没有此列时不处理
            if(infos == null || infos.size() <= 0 || infos.get(REMOVE_FIELD) == null
                || !infos.get(REMOVE_FIELD).isArray()) {
                continue;
            }
            // 收集删除列
            ArrayNode removeNode = infos.withArray(REMOVE_FIELD);
            Set<String> removeSet = new HashSet<>();
            removeMap.put(vo.getId(), removeSet);
            for(int i = 0,j = removeNode.size();i < j;i++) {
                removeSet.add(removeNode.get(i).textValue());
            }
            // 处理完后，删除此项
            infos.remove(REMOVE_FIELD);
        }
        return removeMap;
    }

    /**
     * 修改时填充classCode
     * @param voList
     * @param dbMap
     */
    private void fillClassCodeForUpdate(List<ObjectDigital> voList,Map<String, ObjectDigital> dbMap){
        if(voList == null || voList.size()<=0){
            return;
        }
        for (ObjectDigital digital : voList) {
            ObjectDigital dbDigital = dbMap.get(digital.getId());
            if(dbDigital!=null && StrUtil.isBlank(digital.getClassCode())){
                digital.setClassCode(dbDigital.getClassCode());
            }
            if(dbDigital!=null && StrUtil.isBlank(digital.getObjType())){
                digital.setObjType(dbDigital.getObjType());
            }
        }
    }

    /**
     * 查询虚拟信息点
     * @param projectId
     * @param groupCode
     * @param ids
     * @return
     */
    @Override
    public Map<String, List<String>> queryVirtualCodeMap(String projectId,String groupCode,List<String> ids) {
        LambdaQueryWrapper<ObjectDigital> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(ObjectDigital::getId, ObjectDigital::getVirtualCodes);
        wrapper.eq(ObjectDigital::getProjectId,projectId);
        wrapper.eq(ObjectDigital::getGroupCode,groupCode);
        wrapper.in(ObjectDigital::getId,ids);
        wrapper.eq(ObjectDigital::getValid, ValidEnum.TRUE.getType());
        List<ObjectDigital> vos = queryByCondition(wrapper);
        return CollUtil.fieldValueAsMap(vos, "id", "virtual_codes");
    }
}
