package com.persagy.proxy.report.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.RowUtil;
import cn.hutool.poi.excel.WorkbookUtil;
import cn.hutool.poi.excel.cell.CellUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.common.helper.SpringHelper;
import com.persagy.dmp.define.client.DigitalDefineFacade;
import com.persagy.dmp.define.entity.ObjectInfoDefine;
import com.persagy.dmp.define.entity.ObjectTypeDefine;
import com.persagy.dmp.digital.client.DigitalObjectFacade;
import com.persagy.proxy.adm.constant.AdmCommonConstant;
import com.persagy.proxy.adm.constant.AdmRelationTypeEnum;
import com.persagy.proxy.adm.handler.RelationReportHandler;
import com.persagy.proxy.adm.request.AdmQueryCriteria;
import com.persagy.proxy.adm.request.AdmResponse;
import com.persagy.proxy.adm.strategy.RelationObjectContext;
import com.persagy.proxy.adm.utils.AdmContextUtil;
import com.persagy.proxy.adm.utils.AdmExcelUtil;
import com.persagy.proxy.adm.utils.AdmQueryCriteriaHelper;
import com.persagy.proxy.common.entity.InstanceUrlParam;
import com.persagy.proxy.report.model.AdmRelationObject;
import com.persagy.proxy.report.model.EquipmentBindPointExcel;
import com.persagy.proxy.report.model.EquipmentCountExcel;
import com.persagy.proxy.report.model.EquipmentExcel;
import com.persagy.proxy.report.service.IRelationReportService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 报表/模板下载
 * 
 * @version 1.0.0
 * @company persagy
 * @author zhangqiankun
 * @date 2021年8月30日 下午7:41:56
 */
@Slf4j
@RestController
@RequiredArgsConstructor
@SuppressWarnings("deprecation")
public class RelationReportController {

	public static final String EXPORT_SHEET_NAME = "关系维护";
	
	private final RelationReportHandler relationReportHandler;
	
	private final RelationObjectContext relationObjectContext;
	
	private final IRelationReportService relationReportService;

	/**
	 * 查询总数量
	 * 
	 * @param request
	 * @return
	 */
	@RequestMapping("/graphic/query-count")
	public AdmResponse queryCount(HttpServletRequest request) {
		InstanceUrlParam context = AdmContextUtil.toDmpContext();
		List<JSONObject> relationProjectCal = this.relationReportService.getAllRelationProjectCalTree(context.getGroupCode(), context.getProjectId());
		return AdmResponse.success(relationProjectCal);
	}
	
	/**
	 * 查询总览数量-根据类型查询关系数量
	 * 
	 * @param type	relationType
	 */
	@RequestMapping("/graphic/type/query-count")
	public AdmResponse countRelationObjects(@RequestParam String type, HttpServletRequest request) {
		InstanceUrlParam context = AdmContextUtil.toDmpContext();
		JSONObject objectNode = this.relationReportService.getRelationProjectCal(context.getGroupCode(), context.getProjectId(), type);
 		return AdmResponse.success(Lists.newArrayList(objectNode));
	}
	
	/**
	 * 查询关系总览（空表，先插入）
	 * 
	 * @param content:模糊查询卡片名称关键字
	 * @return
	 */
	@RequestMapping("/graphic/overview")
	public JSONObject overview(@RequestParam(required = false) String content) throws JsonProcessingException {
		JSONObject overview = this.relationReportService.overview(content);
		return overview;
	}
	
	/**
	 * 项目关系类型查询
	 * {"filters":"relationType in [\"bd2sp\",\"fl2sp\"];computationalState>1"}
	 * 
	 * @param request
	 * @return
	 */
	@RequestMapping("/graphic/relation_type_project")
	public AdmResponse queryRelationObjects(@RequestBody AdmQueryCriteria requestCriteria, HttpServletRequest request) {
		InstanceUrlParam context = AdmContextUtil.toDmpContext();
		String filters = requestCriteria.getFilters();
		
		
		QueryCriteria queryCriteria = new QueryCriteria();
		ObjectMapper objectMapper = SpringHelper.getBean(ObjectMapper.class);
		ObjectNode criteria = null;
		Set<String> graphs = new HashSet<String>();
		Set<String> relCodes = new HashSet<String>();
		if (StrUtil.isNotBlank(filters)) {
			criteria = AdmQueryCriteriaHelper.toCriteria(null, requestCriteria.getFilters());
			
			if (filters.contains("relationType")) {
				String condition = filters.substring(filters.indexOf("in [") + 4, filters.lastIndexOf("]"));
				String[] split = condition.split(",");
				for (String relType : split) {
					relType = relType.replaceAll("\"", "");
					AdmRelationTypeEnum typeEnum = AdmCommonConstant.RELATION_TYPE_MAP.get(relType);
					if (typeEnum != null) {
						graphs.add(typeEnum.getGraphCode());
						relCodes.add(typeEnum.getRelCode());
					}
				}
				criteria.remove("relationType");
			}
			if (criteria.has("automatic")) {
				criteria.put("automaticFlag", criteria.get("automatic"));
				criteria.remove("automatic");
			}
			if (criteria.has("source")) {
				criteria.put("sourceFlag", criteria.get("source"));
				criteria.remove("source");
			}
		}
		if (criteria == null) {
			criteria = objectMapper.createObjectNode();
		}
		if (CollectionUtil.isNotEmpty(graphs)) {
			JSONObject graphObject = new JSONObject();
			graphObject.put("$in", graphs);
			criteria.putPOJO("graphCode", graphObject);
		}
		if (CollectionUtil.isNotEmpty(relCodes)) {
			JSONObject relObject = new JSONObject();
			relObject.put("$in", relCodes);
			criteria.putPOJO("relCode", relObject);
		}
		criteria.put("projectId", context.getProjectId());
		queryCriteria.setCriteria(criteria);
		List<JSONObject> relationProjectCal = this.relationReportService.getAllRelationProjectCal(queryCriteria, context.getGroupCode(), context.getProjectId());
		return AdmResponse.success(relationProjectCal);
	}
	
	/**
	 * 下载报告-查询key值
	 * 
	 * @param queryCriteria
	 * @param request
	 * @return
	 */
	@RequestMapping("/graphic/downloads/point/key")
	public AdmResponse downloadsPointKey(@RequestBody AdmQueryCriteria queryCriteria, HttpServletRequest request) {
		//String groupCode = request.getHeader(AdmCommonConstant.GROUP_CODE_HEADER);
		//String projectId = request.getHeader(AdmCommonConstant.PROJECT_ID_HEADER);
		//List<ObjectNode> relationProjectCal = this.relationReportService.getAllRelationProjectCal(groupCode, projectId);
		return AdmResponse.success(new ArrayList<>());
	}
	
	/**
	 * 下载报告-设备-未关联空间的设备
	 * 设备.xlsx 是个空模板，所以这里未使用 
	 * 
	 * @param projectId
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("/graphic/downloads/eq/not/sp")
	public void downloadsEqNotSp(@RequestParam(required = false) String projectId, HttpServletRequest request, HttpServletResponse response) {
		InstanceUrlParam context = AdmContextUtil.toDmpContext();
		if (StrUtil.isBlank(projectId)) {
			projectId = context.getProjectId();
		}
		try {
            response.setContentType("application/vnd.ms-excel");
            String encode = StandardCharsets.UTF_8.name();
            response.setCharacterEncoding(encode);
            // 防止中文乱码
            String fileName = URLEncoder.encode("equipment", encode);
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            
            List<EquipmentExcel> equipByNotSpace = this.relationReportHandler.findEquipByNotSpace(context.getGroupCode(), projectId);
            EasyExcel.write(response.getOutputStream(), EquipmentExcel.class).sheet("设备").doWrite(equipByNotSpace);
		} catch(Exception e) {
			log.error("设备数据导出异常", e);
		}
	}
	
	/**
	 * 下载报告-统计项目下已有的设备，信息点，每个设备
	 * 设备.xlsx 是个空模板，所以这里未使用 
	 * 
	 * @param projectId
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("/graphic/downloads/project/binding/point")
	public void downloadsProjectBindingPoint(@RequestParam(required = false) String projectId, HttpServletRequest request, HttpServletResponse response) {
		InstanceUrlParam context = AdmContextUtil.toDmpContext();
		if (StrUtil.isBlank(projectId)) {
			projectId = context.getProjectId();
		}
		try {
            response.setContentType("application/vnd.ms-excel");
            String encode = StandardCharsets.UTF_8.name();
            response.setCharacterEncoding(encode);
            // 防止中文乱码
            String fileName = URLEncoder.encode("设备实例交付", encode);
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            
            List<EquipmentBindPointExcel> equipmentBindPointExcel = this.relationReportHandler.queryProjectBindPoint(context.getGroupCode(), projectId);
            EasyExcel.write(response.getOutputStream(), EquipmentBindPointExcel.class).sheet(0).doWrite(equipmentBindPointExcel);
		} catch(Exception e) {
			log.error("设备数据导出异常", e);
		}
	}
	
	/**
	 * 下载报告-统计项目下已有的设备，以及静态和iot 信息点使用情况
	 * 设备.xlsx 是个空模板，所以这里未使用 
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("/graphic/downloads/project/equip/count")
	public void downloadsProjectEquipPoint(HttpServletRequest request, HttpServletResponse response) {
		try {
            // response.setContentType("application/vnd.ms-excel");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            String encode = StandardCharsets.UTF_8.name();
            response.setCharacterEncoding(encode);
            // 防止中文乱码
            String fileName = URLEncoder.encode("equipment", encode);
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            
            List<EquipmentCountExcel> equipmentCountExcel = relationReportHandler
					.countClassCodeEquip(AppContext.getContext().getGroupCode(), AppContext.getContext().getProjectId());
            EasyExcel.write(response.getOutputStream(), EquipmentCountExcel.class).sheet(0).doWrite(equipmentCountExcel);
		} catch(Exception e) {
			log.error("设备数据导出异常", e);
		}
	}
	
	/**
	 * 下载报告-设备静态信息维护模板
	 * 
	 * @param request
	 * @param response
	 */
	@RequestMapping("/graphic/downloads/equip/template")
	public void downloadEquipTemplate(HttpServletRequest request, HttpServletResponse response) {
		ExcelWriter excelWriter = null;
		try {
			response.setContentType("application/vnd.ms-excel");
			String encode = StandardCharsets.UTF_8.name();
			response.setCharacterEncoding(encode);
			// 防止中文乱码
			String fileName = URLEncoder.encode("台账信息导入模板", encode);
			response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
			
			File templateFile = AdmExcelUtil.getTemplateFile("equip_template.xlsx");
			excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(templateFile).build();
		} catch (Exception e) {
			log.error("设备静态信息维护模板下载失败", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.finish();
			}
		}
	}

	/**
	 * 下载报告-点表
	 * 
	 * @param request
	 * @param response
	 */
	@RequestMapping("/graphic/downloads/point")
	public void downloadPoint(@RequestParam(required = false) String key, HttpServletRequest request, HttpServletResponse response) {
		ExcelWriter excelWriter = null;
		try {
			response.setContentType("application/vnd.ms-excel");
			String encode = StandardCharsets.UTF_8.name();
			response.setCharacterEncoding(encode);
			response.setHeader("Content-disposition", "attachment;filename=" + key);
			
			File file = ResourceUtils.getFile(AdmCommonConstant.SERVER_ROOT_PATH + AdmExcelUtil.TEMP_UPLOAD_PAHT + key);
			if (!file.exists()) {
				file = ResourceUtils.getFile(AdmCommonConstant.SERVER_ROOT_PATH + AdmExcelUtil.TEMP_PAHT + "relation-template.xlsx");
			}
			
			excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(file).build();
		} catch (Exception e) {
			log.error("下载报告-点表失败", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.finish();
			}
		}
	}
	
	/**
	 * 下载模板
	 * 
	 * @param request
	 * @param response
	 */
	@RequestMapping("/graphic/template-downloads")
	public void templateDownloads(HttpServletRequest request, HttpServletResponse response) {
		ExcelWriter excelWriter = null;
		try {
			response.setContentType("application/vnd.ms-excel");
			String encode = StandardCharsets.UTF_8.name();
			response.setCharacterEncoding(encode);
			response.setHeader("Content-disposition", "attachment;filename=relation-template.xlsx");
			
			File templateFile = AdmExcelUtil.getTemplateFile("relation-template.xlsx");
			excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(templateFile).build();
		} catch (Exception e) {
			log.error("模板下载失败", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.finish();
			}
		}
	}
	
	/**
	 * 下载报告
	 * 
	 * @param request
	 * @param response
	 */
	@RequestMapping("/graphic/report-downloads")
	public void reportDownloads(@RequestParam String projectId, @RequestParam String relType, @RequestParam(required = false) String zoneType, 
			HttpServletRequest request, HttpServletResponse response) {
		ExcelWriter excelWriter = null;
		try {
			response.setContentType("application/vnd.ms-excel");
			String encode = StandardCharsets.UTF_8.name();
			response.setCharacterEncoding(encode);
			
			String templateFile = projectId + AdmCommonConstant.LINE_THROUGH + relType + AdmCommonConstant.EXCEL_SUFFIX_XLSX;
			response.setHeader("Content-disposition", "attachment;filename=" + templateFile);
			
			File file = AdmExcelUtil.getTemplateOrDefaultFile(templateFile);
			excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(file).build();
		} catch (Exception e) {
			log.error("下载报告失败", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.finish();
			}
		}
	}
	
	/**
	 * 导出-点位模板
	 * 
	 * @param request
	 * @param response
	 */
	@RequestMapping("/graphic/export/point")
	public void exportPoint(@RequestParam String buildingId, @RequestParam String floorId, @RequestParam String category, 
			HttpServletRequest request, HttpServletResponse response) {
		ExcelWriter excelWriter = null;
		try {
			response.setContentType("application/vnd.ms-excel");
			String encode = StandardCharsets.UTF_8.name();
			response.setCharacterEncoding(encode);
			
			//excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(file).build();
		} catch (Exception e) {
			log.error("导出-点位模板失败", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.finish();
			}
		}
	}
	
	/**
	 * 导出关系
	 * 
	 * @param request
	 * @param response
	 */
	@RequestMapping("/graphic/export")
	public void export(@RequestParam String projectId, @RequestParam String relType, @RequestParam(required = false) String code, 
			@RequestParam(required = false) String zoneType, HttpServletRequest request, HttpServletResponse response) {
		ExcelWriter excelWriter = null;
		try {
			InstanceUrlParam context = AdmContextUtil.toDmpContext();
			if(StrUtil.isBlank(projectId)) {
				projectId = context.getProjectId();
			}
			code = StrUtil.isBlank(code) ? "对象ID" : code;
			
			//response.setContentType("application/vnd.ms-excel");
			response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
			String encode = StandardCharsets.UTF_8.name();
			response.setCharacterEncoding(encode);
			response.setHeader("Content-disposition", "attachment;filename=relation-template.xlsx");
			
			log.info("下载报表： groupCode[{}], projectId[{}], relType[{}], code[{}]", context.getGroupCode(), projectId, relType, code);
			AdmRelationTypeEnum typeEnum = AdmCommonConstant.RELATION_TYPE_MAP.get(relType);
			
			List<AdmRelationObject> relationObjects = null;
			if (typeEnum != null) {
				String graphAndRelKey = typeEnum.getGraphCode() + AdmCommonConstant.UNDERLINE + typeEnum.getRelCode();
				relationObjects = this.relationObjectContext.exportRelationObject(context.getGroupCode(), projectId, graphAndRelKey);
			}
			
			File templateFile = AdmExcelUtil.getTemplateFile("relation-template.xlsx");
//			if (CollUtil.isEmpty(relationObjects)){
//				EasyExcel.write(response.getOutputStream()).withTemplate(templateFile).sheet(EXPORT_SHEET_NAME).needHead(false);
//				return;
//			}
			EasyExcel.write(response.getOutputStream()).withTemplate(templateFile).sheet(EXPORT_SHEET_NAME).needHead(false).doWrite(relationObjects);
		} catch (Exception e) {
			log.error("深化点导入失败", e);
		} finally {
			if (excelWriter != null) {
				excelWriter.finish();
			}
		}
	}
	
	
	/************************************************** 文件上传 ***********************************************/
	
	/**
	 * 文件导入，这里由于需要回写文件，且存储到本地，采用原始POI文件解析、上传、回写、存储
	 * 
	 * @param relType
	 * @param zoneType
	 * @param request
	 * @param response
	 */
	@RequestMapping("/graphic/import")
	public AdmResponse importFile(@RequestParam("file") MultipartFile file, @RequestParam String relType, 
			@RequestParam(required = false) String zoneType, HttpServletRequest request, HttpServletResponse response) {
		String failure = null;
		Workbook workbook = null;
		FileOutputStream fileOutputStream = null;
		try {
			if (file == null) {
				return AdmResponse.failure("未选择文件");
			}
			AdmRelationTypeEnum typeEnum = AdmCommonConstant.RELATION_TYPE_MAP.get(relType);
			if (typeEnum == null) {
				return AdmResponse.failure("不支持的关系类型");
			}
			String graphAndRelKey = typeEnum.getGraphCode() + AdmCommonConstant.UNDERLINE + typeEnum.getRelCode();
			
			int failurNum = 0;
			int successNum = 0;
			String graphId = null;
			// 临时存储行号
			Set<Integer> rowNums = new HashSet<Integer>();
			// 批量更新数据
			List<ObjectNode> relationObjects = new ArrayList<ObjectNode>();
			InstanceUrlParam context = AdmContextUtil.toDmpContext();
			ExcelReader excelReader = ExcelUtil.getReader(file.getInputStream(), "关系维护");
			// 获取真实行数
			int rowCount = excelReader.getRowCount();
			if (rowCount<=5){
				// 没有数据，直接返回成功
				return AdmResponse.success(Lists.newArrayList(AdmExcelUtil.getImportResult(graphId, successNum, failurNum)));
			}
			String code = StrUtil.utf8Str(excelReader.readCellValue(1,2));
			if (StrUtil.isBlank(code)){
				return AdmResponse.failure("未选择识别编码");
			}
			for (int i = 5; i < rowCount; i=i+1000) {
				List<AdmRelationObject> admRelationObjects = new ArrayList<>();
				int index = Math.min(i + 1000, rowCount);
				for (int j = i; j < index; j++) {
					String masterCodeCell = StrUtil.utf8Str(excelReader.readCellValue(0,j));
					String slaveCodeCell = StrUtil.utf8Str(excelReader.readCellValue(8,j));
					String masterIdCell = StrUtil.utf8Str(excelReader.readCellValue(3,j));
					String slaveIdCell = StrUtil.utf8Str(excelReader.readCellValue(11,j));
					AdmRelationObject relationObject = new AdmRelationObject();
					relationObject.setMasterCode(masterCodeCell);
					relationObject.setSlaveCode(slaveCodeCell);
					relationObject.setMasterId(masterIdCell);
					relationObject.setSlaveId(slaveIdCell);
					admRelationObjects.add(relationObject);
				}
				Map<String, Object> resultMap = this.relationObjectContext.checkRelationObjects(admRelationObjects, context.getGroupCode(),
						context.getProjectId(), graphAndRelKey, code, typeEnum);
				for (int j = i; j < index; j++) {
					String masterCodeCell = StrUtil.utf8Str(excelReader.readCellValue(0, j));
					String slaveCodeCell = StrUtil.utf8Str(excelReader.readCellValue(8, j));
					String key = StrUtil.toString(masterCodeCell) + StrUtil.UNDERLINE +StrUtil.toString(slaveCodeCell);
					Object str = resultMap.getOrDefault(key, "对象不存在");
					if (str instanceof String) {
						failurNum++;
						CellUtil.setCellValue(CellUtil.getOrCreateCell(RowUtil.getOrCreateRow(excelReader.getSheet(),j),16),str,null);
						continue;
					}
					ObjectNode relation = (ObjectNode) str;
					graphId = relation.get("graphId") == null ? graphId : relation.get("graphId").asText();
					// 每一千条数据，批量新增一次
					relationObjects.add(relation);
					rowNums.add(j);
				}
				if (CollUtil.isNotEmpty(relationObjects)) {
					boolean objects = this.relationObjectContext.saveRelationObjects(relationObjects, context.getGroupCode(),
							context.getProjectId(), graphAndRelKey);
					if (objects) {
						successNum += relationObjects.size();
						for (int rowNum : rowNums) {
							CellUtil.setCellValue(CellUtil.getOrCreateCell(RowUtil.getOrCreateRow(excelReader.getSheet(),rowNum),16),"关联成功",null);
						}
					} else {
						failurNum += relationObjects.size();
						for (int rowNum : rowNums) {
							CellUtil.setCellValue(CellUtil.getOrCreateCell(RowUtil.getOrCreateRow(excelReader.getSheet(),rowNum),16),"关联失败",null);
						}
					}
					rowNums.clear();
					relationObjects.clear();
				}
			}
			try {
        		// 调用中台存储完成之后，此文件存储至本地
        		String fileName = context.getProjectId() + AdmCommonConstant.LINE_THROUGH + relType + AdmCommonConstant.EXCEL_SUFFIX_XLSX;
        		File templateFile = AdmExcelUtil.createTemplateFile(AdmCommonConstant.SERVER_ROOT_PATH + AdmExcelUtil.TEMP_PAHT, fileName);
        		fileOutputStream = new FileOutputStream(templateFile);
        		excelReader.getWorkbook().write(fileOutputStream);
			} catch (Exception e) {
				log.error("文件存储失败", e);
			}
        	return AdmResponse.success(Lists.newArrayList(AdmExcelUtil.getImportResult(graphId, successNum, failurNum)));
		} catch (Exception e) {
			log.error("文件导入失败", e);
			failure = e.getMessage();
		} finally {
			if (fileOutputStream != null) {
				try {
					fileOutputStream.flush();
					fileOutputStream.close();
				} catch (IOException e) {}
			}
			if (workbook != null) {
				try {
					workbook.close();
				} catch (IOException e) {}
			}
		}
		return AdmResponse.failure(failure);
	}
	
	/**
	 * 深化点信息导入
	 * 
	 * @param file
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("/graphic/import/point")
	public AdmResponse importFile(@RequestParam("file") MultipartFile file, HttpServletRequest request, HttpServletResponse response) {
		int failCount = 0;
		int successCount = 0;
		String failure = null;
		Workbook workbook = null;
		String groupCode = AppContext.getContext().getGroupCode();
		String projectId = AppContext.getContext().getProjectId();
		ObjectMapper objectMapper = SpringHelper.getBean(ObjectMapper.class);
		try {
			if (file == null) {
				return AdmResponse.failure("未选择文件");
			}
			workbook = ExcelUtil.getReader(file.getInputStream()).getWorkbook();
		    Sheet sheet = WorkbookUtil.getOrCreateSheet(workbook,0);
		    Row row = RowUtil.getOrCreateRow(sheet,0);
		    if (row == null) {
		    	return AdmResponse.failure("文件标题行不存在");
			}
		    int identificationCode = -1, infosCode = -1, tableCode = -1, functionCode = -1, infosFeedback = -1,
		    		classCodeIndexes = -1, bimIdIndexes = -1;
		    short lastCellNum = row.getLastCellNum();
		    for (int i = 0; i <= lastCellNum; i++) {
				String cellValue = StrUtil.str(CellUtil.getCellValue(CellUtil.getOrCreateCell(row,i),true), StandardCharsets.UTF_8);
				if (StrUtil.isNotBlank(cellValue) && "本地编码".equals(cellValue)) {
                    identificationCode = i;
                } else if (StrUtil.isNotBlank(cellValue) && "信息点编码".equals(cellValue)) {
                    infosCode = i;
                } else if (StrUtil.isNotBlank(cellValue) && "表号".equals(cellValue)) {
                    tableCode = i;
                } else if (StrUtil.isNotBlank(cellValue) && "功能号".equals(cellValue)) {
                    functionCode = i;
                } else if (StrUtil.isNotBlank(cellValue) && "反馈信息".equals(cellValue)) {
                    infosFeedback = i;
                } else if (StrUtil.isNotBlank(cellValue) && "设备类型".equals(cellValue)) {
                    classCodeIndexes = i;
                } else if (StrUtil.isNotBlank(cellValue) && "BIMID".equals(cellValue)) {
                    bimIdIndexes = i;
                }
			}
			if (identificationCode == -1) {
				failure = "缺少识别码：本地编码";
			} else if (infosCode == -1) {
				failure = "缺少信息点编码";
			} else if (tableCode == -1) {
				failure = "缺少表号";
			} else if (functionCode == -1) {
				failure = "缺少功能号";
			} else if (infosFeedback == -1) {
				failure = "缺少反馈信息";
			} else if (classCodeIndexes == -1) {
				failure = "缺少设备类型";
			} else if (bimIdIndexes == -1) {
				failure = "缺少BIMID";
			}
			if (StrUtil.isNotBlank(failure)) {
				return AdmExcelUtil.getImportFailResult(failure, "3", 0, 0);
			}
			// 表中的设备编码code集合
			Set<String> classCodes = CollUtil.newHashSet();
			// 表中的设备本地编码集合
			Set<String> equipLocalIds = CollUtil.newHashSet();
			// 业务数据获取
			int lastRowNum = sheet.getLastRowNum();
			for (int i = 1; i < lastRowNum; i++) {
				String classCode = StrUtil.str(CellUtil
						.getCellValue(CellUtil.getOrCreateCell(RowUtil.getOrCreateRow(sheet,i),classCodeIndexes),true), StandardCharsets.UTF_8);
				String localId = StrUtil.str(CellUtil
						.getCellValue(CellUtil.getOrCreateCell(RowUtil.getOrCreateRow(sheet,i),identificationCode),true), StandardCharsets.UTF_8);
				if (StrUtil.isNotBlank(classCode)){
					classCodes.add(classCode);
				}
				if (StrUtil.isNotBlank(localId)){
					equipLocalIds.add(localId);
				}
			}
			// 1.根据设备类型code集合查询定义
			Map<String,ObjectTypeDefine> defineMap = relationReportService.queryDefineTypeMapByClassCodes(classCodes);
			// 2.根据设备类型code集合查询设备类的信息点
			Map<String,List<ObjectInfoDefine>> defineInfoMap = relationReportService.queryDefineInfoMapByClassCodes(classCodes);
			// 3.根据是否根据设备类型code上传来区分处理
			Map<String,List<ObjectNode>> objectDigitalMap = relationReportService.queryEquipsByConditions("localId",equipLocalIds,classCodes);
			List<ObjectNode> updateObjs = new ArrayList<>();
			Set<String> addEquipIds = new HashSet<>();
		    for (int i = 1; i <= lastRowNum; i++) {
		    	Row xssfRow = sheet.getRow(i);
		    	if (xssfRow == null) {
		    		sheet.getRow(0).createCell(16).setCellValue("第" + i + " 行数据为空");
					continue;
				}
		    	Cell identificationCodeCell = xssfRow.getCell(identificationCode);
		    	if (identificationCodeCell == null || StrUtil.isBlank(identificationCodeCell.getStringCellValue())) {
		    		failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("本地编码不可以为空");
                    continue;
				}
		    	identificationCodeCell.setCellType(CellType.STRING);
		    	Cell infosCodeCell = xssfRow.getCell(infosCode);
		    	if (infosCodeCell == null || StrUtil.isBlank(infosCodeCell.getStringCellValue())) {
		    		failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("信息点编码不可以为空");
                    continue;
				}
		    	infosCodeCell.setCellType(CellType.STRING);
		    	Cell tableCodeCell = xssfRow.getCell(tableCode);
		    	if (tableCodeCell == null || StrUtil.isBlank(StrUtil.str(CellUtil.getCellValue(tableCodeCell,true), StandardCharsets.UTF_8))) {
		    		failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("表号不可以为空");
                    continue;
				}
		    	tableCodeCell.setCellType(CellType.STRING);
		    	Cell functionCodeCell = xssfRow.getCell(functionCode);
		    	if (functionCodeCell == null || StrUtil.isBlank(StrUtil.str(CellUtil.getCellValue(functionCodeCell,true), StandardCharsets.UTF_8))) {
		    		failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("功能号不可以为空");
                    continue;
				}
		    	functionCodeCell.setCellType(CellType.STRING);
		    	Cell classCodeIndexesCell = xssfRow.getCell(classCodeIndexes);
		    	if (classCodeIndexesCell == null || StrUtil.isBlank(classCodeIndexesCell.getStringCellValue())) {
		    		failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("设备类型不可以为空");
                    continue;
				}
		    	classCodeIndexesCell.setCellType(CellType.STRING);
		    	String classCode = classCodeIndexesCell.getStringCellValue().trim();
		    	// 验证此classCode是否存在
		    	ObjectTypeDefine typeDefine = defineMap.get(classCode);
		    	if (typeDefine == null) {
		    		failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("设备类型未在数据字典定义范围");
                    continue;
				}

		    	// 获取实际cell值
		    	String val0 = identificationCodeCell.getStringCellValue().trim();
		    	String val1 = infosCodeCell.getStringCellValue().trim();
		    	String val2 = tableCodeCell.getStringCellValue().trim();
		    	String val3 = functionCodeCell.getStringCellValue().trim();

		    	// 获取设备信息
		    	ObjectNode criteria = objectMapper.createObjectNode();
		    	criteria.put("localId", val0);
		    	criteria.put("classCode", classCode);
		    	List<ObjectNode> equipments = objectDigitalMap.getOrDefault(val0+StrUtil.UNDERLINE+classCode,new ArrayList<>());
		    	if (CollectionUtil.isEmpty(equipments)) {
		    		failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("设备类型下本地编码不存在");
                    continue;
				} else if (equipments.size() > 1) {
					String temp = null;
					for (ObjectNode objectNode : equipments) {
						String bimId = this.getObjectBimId(objectNode);
						if (StrUtil.isNotBlank(bimId)) {
							temp = (StrUtil.isBlank(temp) ? "" : (temp + ",")) + bimId;
						}
					}
					failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("本地编码重复");
		    		if (StrUtil.isNotBlank(temp)) {
		    			xssfRow.createCell(bimIdIndexes).setCellValue(temp);
					}
                    continue;
				}

		    	// 获取信息点定义
		    	List<ObjectInfoDefine> funidList = defineInfoMap.getOrDefault(classCode,new ArrayList<>())
						.stream()
						.filter(obj->val1.equals(obj.getAliasCode()))
						.collect(Collectors.toList());;
		    	if (CollectionUtil.isEmpty(funidList)) {
		    		failCount++;
		    		xssfRow.createCell(infosFeedback).setCellValue("信息点不存在");
                    continue;
				}
				ObjectInfoDefine infoDefine = funidList.get(0);
				if ("STATIC".equals(infoDefine.getCategory())) {
					failCount++;
					xssfRow.createCell(infosFeedback).setCellValue("该信息点为静态信息点");
					continue;
				}
		    	 // 数据更新，更新对应code值为，表号和功能号
		    	ObjectNode objectNode = equipments.get(0);
		    	objectNode.put(infoDefine.getCode(), val2 + "-" +val3);
				successCount++;
				if (!addEquipIds.contains(objectNode.get("id").asText())){
					addEquipIds.add(objectNode.get("id").asText());
					updateObjs.add(objectNode);
				}
			}
		    if (CollUtil.isNotEmpty(updateObjs)){
		    	DigitalObjectFacade.update(AppContext.getContext().getGroupCode(),
						AppContext.getContext().getProjectId(),
						AppContext.getContext().getAppId(),
						AppContext.getContext().getAccountId(),
						updateObjs);
			}
		    // 文件存储及返回
		    String fileName = AdmExcelUtil.writeFile2Local(workbook, projectId, AdmCommonConstant.SERVER_ROOT_PATH + AdmExcelUtil.TEMP_UPLOAD_PAHT, file.getOriginalFilename());
		    return AdmExcelUtil.getImportSuccessResult(fileName, successCount, failCount);
		} catch (Exception e) {
			log.error("文件导入失败", e);
			failure = e.getMessage();
		} finally {
			if (workbook != null) {
				try {
					workbook.close();
				} catch (IOException e) {}
			}
		}
		return AdmResponse.failure(failure);
	}
	
	
	/***************************************************************************************************************/
	
	/**
	 * 获取类定义信息
	 * 
	 * @param groupCode
	 * @param projectId
	 * @param classCode
	 * @param objType 不等于条件
	 * @return
	 */
	private ObjectTypeDefine queryClassList(String groupCode, String projectId, String classCode, String objType) {
		ObjectMapper objectMapper = SpringHelper.getBean(ObjectMapper.class);
		QueryCriteria classCriteria = new QueryCriteria();
		ObjectNode condition = objectMapper.createObjectNode();
		if (StrUtil.isNotBlank(classCode)) {
			condition.put("code", classCode);
		}
		if (StrUtil.isNotBlank(objType)) {
			JSONObject arrays = new JSONObject();
			arrays.put("$ne", objType);
			condition.putPOJO("objType", arrays);
		}
		classCriteria.setCriteria(condition);
		List<ObjectTypeDefine> objectNodes = DigitalDefineFacade.queryObjectType(groupCode, projectId, AdmCommonConstant.APP_ID, AdmCommonConstant.USER_ID, classCriteria);
		return CollectionUtil.isEmpty(objectNodes) ? null : objectNodes.get(0);
	}
	
	/**
	 * 获取BimId
	 * 
	 * @param objectNode
	 * @return
	 */
	private String getObjectBimId(ObjectNode objectNode) {
		String infos = objectNode.has("infos") ? objectNode.get("infos").asText() : null;
		if (StrUtil.isNotBlank(infos)) {
			try {
				JSONObject jsonObject = JSONObject.parseObject(infos);
				return jsonObject.getString("bimId");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
}
