package com.persagy.bdtp.adm.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.persagy.bdtp.adm.client.DicClient; import com.persagy.bdtp.adm.client.RwdClient; import com.persagy.bdtp.adm.common.AdmConst; import com.persagy.bdtp.adm.dao.*; import com.persagy.bdtp.adm.datatx.ObjectMapper4Tx; import com.persagy.bdtp.adm.entity.*; import com.persagy.bdtp.adm.entity.db.*; import com.persagy.bdtp.adm.service.*; import com.persagy.bdtp.adm.util.DataExtrasUtil; import com.persagy.dmp.basic.model.QueryCriteria; import com.persagy.dmp.common.constant.ValidEnum; import com.persagy.dmp.define.entity.RelationDefine; import com.persagy.dmp.digital.client.DigitalObjectClient; import com.persagy.dmp.digital.client.DigitalRelationClient; import com.persagy.dmp.digital.entity.ObjectRelation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.sql.Timestamp; import java.util.*; @Service public class SyncAppImpl implements ISyncApp { @Autowired private RwdClient rwdClient; @Autowired private DicClient dicClient; @Autowired private DigitalObjectClient objectClient; @Autowired private DigitalRelationClient relationClient; @Autowired private AdmClientFlagMapper clientFlagMapper; @Autowired private IConfigService configService; @Autowired private AdmPipeMapper pipeMapper; @Autowired private AdmJobSpaceMapper jobSpaceMapper; @Autowired private AdmProblemArchMapper problemArchMapper; @Autowired private AdmProblemEquipMapper problemEquipMapper; @Autowired private AdmFileMapper fileMapper; @Autowired private AdmQrCodeMapper qrCodeMapper; @Autowired private AdmServeAreaMapper serveAreaMapper; @Autowired private ISyncModel syncModel; @Autowired private ObjectMapper objectMapper; @Autowired private IAdmLogService logService; @Autowired private AdmUploadJobMapper uploadJobMapper; private final ObjectMapper4Tx mapper4Tx; @Autowired public SyncAppImpl(ObjectMapper4Tx mapper4Tx) { this.mapper4Tx = mapper4Tx; } @Override public String getClientId(String userId) { AdmClientFlag flagEntity = new AdmClientFlag(); flagEntity.setUserId(userId); clientFlagMapper.insert(flagEntity); return String.valueOf(flagEntity.getId()); } @Override public Dict downloadDict(String groupCode, String projectId, String userId) { return queryDict(groupCode, projectId, userId, true, true); } @Override public Dict queryDict(String groupCode, String projectId, String userId, boolean includeRel, boolean includeInfos){ //从知识库服务获取专业分类 QueryCriteria majorCriteria = ServiceUtil.getQueryCriteria(objectMapper); majorCriteria.getCriteria().put("type", "major"); List> majorList = ServiceUtil.call (() -> dicClient.dataQuery(groupCode, projectId, AdmConst.APP_ID, userId, majorCriteria)); //查询系统和设备定义 QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, AdmConst.OBJ_TYPE_SYSTEM, AdmConst.OBJ_TYPE_EQUIPMENT); List typeList = ServiceUtil.queryAllPage(() -> rwdClient.queryObjectType(groupCode, projectId, AdmConst.APP_ID, userId, criteria), criteria, new Pagination(500)); LinkedList sysList = new LinkedList<>(); LinkedList equipList = new LinkedList<>(); for(TypeDef def : typeList){ if(def.getObjType().equals(AdmConst.OBJ_TYPE_SYSTEM)) sysList.add(def); else if(def.getObjType().equals(AdmConst.OBJ_TYPE_EQUIPMENT)) equipList.add(def); } Dict data = new Dict(); data.setMajor(majorList); data.setSystem(sysList); data.setEquipment(equipList); //查询关系定义 if(includeRel) { List relList = ServiceUtil.call(() -> rwdClient.queryRelDef(groupCode, projectId, AdmConst.APP_ID, userId, new QueryCriteria())); data.setRelation(relList); } //查询信息点定义 if(includeInfos) { QueryCriteria qc = new QueryCriteria(); qc.setCriteria(objectMapper.createObjectNode()); List funcList = ServiceUtil.call(() -> rwdClient.queryFunc(groupCode, projectId, AdmConst.APP_ID, userId, qc)); data.setInfos(funcList); } return data; } @Override public Map downloadConfig(String groupCode, String projectId, String userId) { //配置数据查询 List m2dTypes = configService.queryM2dEquip(projectId); List infosConfig = configService.queryInfosConfig(projectId); List relConfig = configService.queryRelsConfig(projectId); List pipeConfig = configService.queryPipeConfig(projectId); List config = configService.queryCommonConfig(projectId); List component = configService.queryComponent(projectId); List containerConfig = configService.queryContainerConfig(projectId); HashMap data = new HashMap<>(); data.put("m2dTypes", m2dTypes); data.put("infosConfig", infosConfig); data.put("relConfig", relConfig); data.put("pipeConfig", pipeConfig); data.put("config", config); data.put("component", component); data.put("containerConfig", containerConfig); return data; } @Override public Map downloadFrameData(String groupCode, String projectId, String userId) { //TODO 项目修改为从运维平台查询 QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, AdmConst.OBJ_TYPE_PROJECT); List prjList = ServiceUtil.call(() -> objectClient.query(groupCode, null, AdmConst.APP_ID, userId, criteria)); packInfos(prjList); QueryCriteria criteria2 = ServiceUtil.getQueryCriteria(objectMapper, AdmConst.OBJ_TYPE_BUILDING, AdmConst.OBJ_TYPE_FLOOR); List bdAndFl = ServiceUtil.call(() -> objectClient.query(groupCode, projectId, AdmConst.APP_ID, userId, criteria2)); packInfos(bdAndFl); HashMap data = new HashMap<>(); data.put("projects", prjList); data.put("buildingsAndFloors", bdAndFl); return data; } private void packInfos(List dataList){ if(dataList != null) { for(ObjectNode node : dataList) { handle4Download(node); DataExtrasUtil.packObjExtras(node); } } } /** 下载前处理数据结构 */ private void handle4Download(ObjectNode node){ JsonNode ol = node.get("outline"); if(ol != null){ try { node.set("outline", objectMapper.readValue(ol.asText(), ArrayNode.class)); }catch (Exception e) { } } JsonNode bl = node.get("bimLocation"); if(bl != null) { try { String[] arr = bl.asText().split(","); ObjectNode ln = objectMapper.createObjectNode(); ln.put("x", Double.parseDouble(arr[0])); ln.put("y", Double.parseDouble(arr[1])); ln.put("z", Double.parseDouble(arr[2])); node.set("location", ln); node.remove("bimLocation"); }catch (Exception e) { } } } @Override public BuildingData downloadBuildingData(String projectId, String buildingId, String uploadFlag, String lastDownloadTime) { throw new RuntimeException("not supported"); } @Override public BuildingData downloadBuildingData(String groupCode, String projectId, String userId, String clientId, String buildingId, Long bdtpDownloadTs, Long admDownloadTs) { String operator = getOperator(clientId, userId); // //从bdtp下载对象(空间、竖井) // QueryCriteria archCriteria = ServiceUtil.getQueryCriteria(objectMapper, AdmConst.OBJ_TYPE_SPACE, AdmConst.OBJ_TYPE_SHAFT); // buildDownloadQueryCriteria(archCriteria, operator, bdtpDownloadTs); // ObjectNode rt = archCriteria.getCriteria().putObject("relationTo"); // rt.put("graphCode", "ArchSubset"); // rt.put("objFrom", buildingId); // List archList = ServiceUtil.queryAllPage(() -> objectClient.query(groupCode, projectId, AdmConst.APP_ID, userId, archCriteria), archCriteria, new Pagination(500)); // //系统、设备 // QueryCriteria eqCriteria = ServiceUtil.getQueryCriteria(objectMapper, AdmConst.OBJ_TYPE_SYSTEM, AdmConst.OBJ_TYPE_EQUIPMENT); // buildDownloadQueryCriteria(eqCriteria, operator, bdtpDownloadTs); // ObjectNode rf = eqCriteria.getCriteria().putObject("relationFrom"); // rf.put("graphCode", "MechInArch"); // rf.put("objTo", buildingId); // List eqList = ServiceUtil.queryAllPage(() -> objectClient.query(groupCode, projectId, AdmConst.APP_ID, userId, eqCriteria), eqCriteria, new Pagination(500)); // // List objs = new ArrayList<>(); // if(archList != null) // objs.addAll(archList); // if(eqList != null) // objs.addAll(eqList); //从bdtp下载对象(空间、竖井、系统、设备) QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, AdmConst.OBJ_TYPE_SPACE, AdmConst.OBJ_TYPE_SHAFT, AdmConst.OBJ_TYPE_SYSTEM, AdmConst.OBJ_TYPE_EQUIPMENT); buildDownloadQueryCriteria(criteria, operator, bdtpDownloadTs, buildingId); List objs = ServiceUtil.queryAllPage(() -> objectClient.query(groupCode, projectId, AdmConst.APP_ID, userId, criteria), criteria, new Pagination(500)); if(objs == null) objs = new ArrayList<>(0); packInfos(objs); test4Ts(objs); //关系数据按项目下载,不在建筑数据中处理 //管道数据,实验方案 List pipes = pipeMapper.selectList(buildDownloadQueryWrapper(new QueryWrapper(), projectId, buildingId, operator, admDownloadTs)); //任务相关数据查询 List jobSpace = jobSpaceMapper.selectList(buildDownloadQueryWrapper(new QueryWrapper(), projectId, buildingId, operator, admDownloadTs)); List problemArch = problemArchMapper.selectList(buildDownloadQueryWrapper(new QueryWrapper(), projectId, buildingId, operator, admDownloadTs)); List problemEquip = problemEquipMapper.selectList(buildDownloadQueryWrapper(new QueryWrapper(), projectId, buildingId, operator, admDownloadTs)); List file = fileMapper.selectList(buildDownloadQueryWrapper(new QueryWrapper(), projectId, buildingId, operator, admDownloadTs)); List qrCode = qrCodeMapper.selectList(buildDownloadQueryWrapper(new QueryWrapper(), projectId, buildingId, operator, admDownloadTs)); List serveArea = serveAreaMapper.selectList(buildDownloadQueryWrapper(new QueryWrapper(), projectId, buildingId, operator, admDownloadTs)); BuildingData data = new BuildingData(); data.setBuildingId(buildingId); data.setObjects(objs); data.setPipes(pipes); data.setJobSpace(jobSpace); data.setProblemArch(problemArch); data.setProblemEquip(problemEquip); data.setFile(file); data.setQrCode(qrCode); data.setServeArea(serveArea); data.buildDownloadTs(); return data; } private String getOperator(String clientId, String userId){ return userId + ':' + clientId; } private QueryWrapper buildDownloadQueryWrapper(QueryWrapper wrapper, String projectId, String buildingId, String operator, Long lastDownloadTime){ wrapper.eq("project_id", projectId).eq("building_id", buildingId); if (lastDownloadTime != null && lastDownloadTime > 0) { //非初始化下载 wrapper.gt("ts", lastDownloadTime); wrapper.and( w -> w.isNotNull("modifier").ne("modifier", operator) .or() .isNull("modifier").ne("creator", operator) ); } return wrapper; } private void buildDownloadQueryCriteria(QueryCriteria criteria, String operator, Long lastDownloadTime, String buildingId){ if (lastDownloadTime != null && lastDownloadTime > 0) { criteria.getCriteria().putObject("ts").put("$gt", lastDownloadTime); criteria.getCriteria().putObject("modifier").put("$ne", operator); //TODO 暂时只能处理modifier } if(StrUtil.isNotBlank(buildingId)) criteria.getCriteria().put("buildingId", buildingId); } //TODO debug 接口未返回ts时,添加一个 private void test4Ts(List dataList){ for(Object data : dataList) { if(data instanceof ObjectNode) { if (((ObjectNode) data).get("ts") == null) ((ObjectNode) data).put("ts", System.currentTimeMillis()); } else if(data instanceof ObjectRelation){ if(((ObjectRelation) data).getTs() == null) ((ObjectRelation) data).setTs(new Date()); } } } @Override public ProjectData downloadProjectData(String groupCode, String projectId, String userId, String clientId, Long bdtpDownloadTs, Long admDownloadTs) { String operator = getOperator(clientId, userId); QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper); buildDownloadQueryCriteria(criteria, operator, bdtpDownloadTs, null); //List rels = ServiceUtil.queryAllPage(() -> relationClient.query(groupCode, projectId, AdmConst.APP_ID, userId, criteria), criteria, new Pagination(500)); //TODO debug 关系数据量过大,测试下载1000条 criteria.setPage(1L); criteria.setSize(1000L); List rels = ServiceUtil.call(() -> rwdClient.queryRelation(groupCode, projectId, AdmConst.APP_ID, userId, criteria)); test4Ts(rels); ProjectData data = new ProjectData(); data.setRelations(rels); data.buildDownloadTs(); return data; } @Override public UploadRtn uploadData(UploadData uploadData, String groupCode, String projectId, String userId, String clientId) { if(!uploadData.notEmpty()) return new UploadRtn(AdmConst.UPLOAD_FINISHED); String operator = getOperator(clientId, userId); //检查上传任务状态 String status = checkUploadStatus(uploadData.getUploadJobId(), operator, projectId); if(status != null) return new UploadRtn(status); String timestamp = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN); List logs = new ArrayList<>(); //TODO 分布式事务管理 try { //上传bdtp数据 if (CollUtil.isNotEmpty(uploadData.getObjects())) { D2mMessage d2mMessage = new D2mMessage("object"); List[] objData = prepareBdtpData(uploadData.getObjects(), operator, timestamp, uploadData.getUploadJobId(), d2mMessage, logs); uploadObjs(groupCode, projectId, userId, objData); sendD2mMessage(d2mMessage); } if (CollUtil.isNotEmpty(uploadData.getRelations())) { List[] relData = prepareBdtpData(uploadData.getRelations(), operator, timestamp, uploadData.getUploadJobId(), null, logs); uploadRels(groupCode, projectId, userId, relData); } //上传adm数据 uploadAdmData(uploadData, operator, timestamp, logs); } catch (Exception e) { markStatus(uploadData.getUploadJobId(), AdmConst.UPLOAD_ERROR); if(e instanceof RuntimeException) throw (RuntimeException)e; throw new RuntimeException(e); } //标注上传任务状态 markStatus(uploadData.getUploadJobId(), AdmConst.UPLOAD_FINISHED); //记录日志 logService.saveUploadLog(logs); return new UploadRtn(AdmConst.UPLOAD_FINISHED); } private String checkUploadStatus(String uploadJobId, String operator, String projectId){ AdmUploadJob job = uploadJobMapper.selectOne(new QueryWrapper().eq("id", uploadJobId)); if(job != null) { if(!AdmConst.UPLOAD_ERROR.equals(job.getStatus())) return job.getStatus(); } else { job = new AdmUploadJob(); job.setId(uploadJobId); job.setProjectId(projectId); job.setOperator(operator); job.setStatus(AdmConst.UPLOAD_PROCESSING); job.setStartTime(new Timestamp(new Date().getTime())); uploadJobMapper.insert(job); } return null; } private void markStatus(String uploadJobId, String status) { AdmUploadJob job = new AdmUploadJob(); job.setId(uploadJobId); job.setStatus(status); job.setFinishTime(new Timestamp(new Date().getTime())); uploadJobMapper.updateById(job); } private void uploadObjs(String groupCode, String projectId, String userId, List[] data) { if(data[0].size() > 0) ServiceUtil.call(() -> objectClient.create(groupCode, projectId, AdmConst.APP_ID, userId, data[0])); if (data[1].size() > 0) ServiceUtil.call(() -> objectClient.update(groupCode, projectId, AdmConst.APP_ID, userId, data[1])); } private void uploadRels(String groupCode, String projectId, String userId, List[] data) { if(data[0].size() > 0) ServiceUtil.call(() -> relationClient.create(groupCode, projectId, AdmConst.APP_ID, userId, data[0])); if (data[1].size() > 0) ServiceUtil.call(() -> relationClient.update(groupCode, projectId, AdmConst.APP_ID, userId, data[1])); } private List[] prepareBdtpData(List list, String operator, String time, String jobId, D2mMessage d2mMessage, List logs){ List updateList = new ArrayList<>(); Iterator iter = list.iterator(); while (iter.hasNext()) { ObjectNode entity = iter.next(); handle4Upload(entity); DataExtrasUtil.unpackObjExtras(entity); entity.remove("ts"); //调用bdtp接口前,删除ts字段 AdmUploadLog log; if (entity.get("state").asInt() == 1) { updateList.add(entity); iter.remove(); boolean remove = entity.get("valid").asInt() == ValidEnum.FALSE.getType(); //设备数据上传时向模型服务发同步消息 if(d2mMessage != null && AdmConst.OBJ_TYPE_EQUIPMENT.equals(entity.get("objType"))) { if(remove) d2mMessage.appendRemoveItem(entity); else d2mMessage.appendModifyItem(entity); } log = new AdmUploadLog(jobId, operator, remove ? AdmUploadLog.REMOVE : AdmUploadLog.MODIFY); log.setData(entity, objectMapper); entity.put("updateApp", AdmConst.CREATOR_APP); entity.put("modifier", operator); entity.put("modifiedTime", time); //修改对象信息点,轮廓不上传 if(entity.get("outline") != null) entity.remove("outline"); //TODO 可能需要进行删除信息点操作 } else { if(d2mMessage != null) d2mMessage.appendAddItem(entity); log = new AdmUploadLog(jobId, operator, AdmUploadLog.ADD); log.setData(entity, objectMapper); entity.put("createApp", AdmConst.CREATOR_APP); entity.put("creator", operator); entity.put("creationTime", time); //TODO 临时处理,方便之后的增量查询 entity.put("modifier", operator); entity.put("modifiedTime", time); } logs.add(log); } return new List[] {list, updateList}; } @Transactional(rollbackFor = Exception.class) public void uploadAdmData(UploadData uploadData, String operator, String time, List logs) { if (CollUtil.isNotEmpty(uploadData.getPipes())) { uploadAdmEntities(uploadData.getPipes(), operator, time, uploadData.getUploadJobId(), pipeMapper, new D2mMessage("pipe"), logs, true); } if (CollUtil.isNotEmpty(uploadData.getJobSpace())) { uploadAdmEntities(uploadData.getJobSpace(), operator, time, uploadData.getUploadJobId(), jobSpaceMapper, null, logs, false); } if (CollUtil.isNotEmpty(uploadData.getProblemArch())) { uploadAdmEntities(uploadData.getProblemArch(), operator, time, uploadData.getUploadJobId(), problemArchMapper, null, logs, false); } if (CollUtil.isNotEmpty(uploadData.getProblemEquip())) { uploadAdmEntities(uploadData.getProblemEquip(), operator, time, uploadData.getUploadJobId(), problemEquipMapper, null, logs, false); } if (CollUtil.isNotEmpty(uploadData.getFile())) { uploadAdmEntities(uploadData.getFile(), operator, time, uploadData.getUploadJobId(), fileMapper, null, logs, false); } if (CollUtil.isNotEmpty(uploadData.getQrCode())) { uploadAdmEntities(uploadData.getQrCode(), operator, time, uploadData.getUploadJobId(), qrCodeMapper, null, logs, false); } if (CollUtil.isNotEmpty(uploadData.getServeArea())) { uploadAdmEntities(uploadData.getServeArea(), operator, time, uploadData.getUploadJobId(), serveAreaMapper, null, logs, false); } } private void uploadAdmEntities(List list, String operator, String time, String jobId, BaseMapper mapper, D2mMessage d2mMessage, List logs, boolean merge) { List updateList = new ArrayList<>(); Iterator iter = list.iterator(); while (iter.hasNext()) { T entity = iter.next(); AdmUploadLog log; if (entity.getState() == 1) { updateList.add(entity); iter.remove(); boolean remove = entity.getValid().intValue() == ValidEnum.FALSE.getType(); if(d2mMessage != null) { if(remove) d2mMessage.appendRemoveItem(entity); else d2mMessage.appendModifyItem(entity); } log = new AdmUploadLog(jobId, operator, remove ? AdmUploadLog.REMOVE : AdmUploadLog.MODIFY); //任务空间不上传轮廓数据 if(entity instanceof AdmJobSpace) ((AdmJobSpace) entity).setOutline(null); } else { if(d2mMessage != null) d2mMessage.appendAddItem(entity); log = new AdmUploadLog(jobId, operator, AdmUploadLog.ADD); } log.setData(entity, objectMapper); logs.add(log); entity.setTs(null); //保存数据前,删除ts字段 } //insert if(list.size() > 0) { for(T entity : list) { entity.setCreator(operator); entity.setCreationTime(time); mapper.insert(entity); } } //update if (updateList.size() > 0) { Map dbObjsMap = null; if(merge) { ArrayList updateIds = new ArrayList<>(updateList.size()); updateList.forEach(entity -> updateIds.add(entity.getId())); List dbList = mapper.selectBatchIds(updateIds); dbObjsMap = new HashMap<>(dbList.size()); for(T dbObj : dbList) { dbObjsMap.put(dbObj.getId(), dbObj); } } for(T entity : updateList) { if(merge) entity = mergeEntity(entity, dbObjsMap.get(entity.getId())); entity.setModifier(operator); entity.setModifiedTime(time); mapper.updateById(entity); } } sendD2mMessage(d2mMessage); } private T mergeEntity(T obj, T dbObj) { if (dbObj != null) { if(dbObj instanceof AdmPipe) ((AdmPipe)obj).setInfos(mergeInfos(((AdmPipe) obj).getInfos(), ((AdmPipe) dbObj).getInfos())); } return obj; } private ObjectNode mergeInfos(ObjectNode infos, ObjectNode dbInfos){ if (dbInfos != null) { if(infos != null) dbInfos.setAll(infos); return dbInfos; } else return infos; } private void sendD2mMessage(D2mMessage d2mMessage){ if (d2mMessage != null && d2mMessage.isNotEmpty()) { if(d2mMessage.getAddItems() != null){ for(Object o : d2mMessage.getAddItems()) { markM2dState(o); } } if(d2mMessage.getModifyItems() != null){ for(Object o : d2mMessage.getModifyItems()) { markM2dState(o); } } if(d2mMessage.getRemoveItems() != null) { for(Object o : d2mMessage.getRemoveItems()) { markM2dState(o); } } syncModel.sendMessageToModel(d2mMessage); } } /** 上传前处理数据结构 */ private void handle4Upload(ObjectNode node){ JsonNode l = node.get("location"); if(l != null) { try { node.put("bimLocation", StrUtil.join(",", l.get("x").asDouble(), l.get("y").asDouble(), l.get("z").asDouble())); node.remove("location"); }catch (Exception e) { } } } private void markM2dState(Object item){ //TODO 标记数据同步状态 } }