package com.fdkankan.extend.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.ZipUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.symmetric.AES; import cn.hutool.http.HttpStatus; import cn.hutool.http.HttpUtil; import cn.hutool.jwt.signers.AlgorithmUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.fdkankan.common.constant.CommonSuccessStatus; import com.fdkankan.common.constant.ErrorCode; import com.fdkankan.common.constant.SceneStatus; import com.fdkankan.common.exception.BusinessException; import com.fdkankan.common.util.AesUtil; import com.fdkankan.common.util.DateExtUtil; import com.fdkankan.extend.bean.TowerSceneBean; import com.fdkankan.extend.entity.Camera; import com.fdkankan.extend.entity.CameraDetail; import com.fdkankan.extend.entity.ScenePlus; import com.fdkankan.extend.httpclient.HttpClient; import com.fdkankan.extend.service.*; import com.fdkankan.extend.util.RsaCryptTools; import com.fdkankan.fyun.face.FYunFileServiceInterface; import com.fdkankan.model.constants.UploadFilePath; import com.fdkankan.model.utils.SceneUtil; import com.fdkankan.rabbitmq.util.RabbitMqProducer; import com.fdkankan.redis.constant.RedisKey; import com.fdkankan.redis.util.RedisUtil; import com.fdkankan.web.response.ResultData; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.io.File; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @Slf4j @Service public class TowerServiceImpl implements ITowerService { private static String parentPath = "/home/backend/4dkankan_v4/modeling-extend/download/"; private static String ossParentPath = "customer-projects/china-tower/"; private static String ossScenePoolFilePath = ossParentPath.concat("scene.txt"); private static String ossCameraPoolFilePath = ossParentPath.concat("camera.txt"); private static String pushLogPath = parentPath.concat("log.txt"); public static final String TIE_TA_SCENE_PUSH_CALLBACK_QUEUE = "tie_ta_scene_push_callback_queue"; /** 场景推送回调交换机*/ public static final String TIE_TA_SCENE_PUSH_CALLBACK_EXCHANGE = "tie_ta_scene_push_callback_exchange"; /** 场景推送回调交换机路由*/ public static final String TIE_TA_SCENE_PUSH_CALLBACK_ROUTING = "tie_ta_scene_push_callback_routing"; @Value("${tower.manufact}") private String manufact; @Value("${tower.private-key}") private String privateKey; @Value("${tower.url}") private String url; @Autowired private FYunFileServiceInterface fYunFileService; @Autowired private RedisUtil redisUtil; @Autowired private ICameraDetailService cameraDetailService; @Autowired private IScenePlusService scenePlusService; @Autowired private ITowerService towerService; @Autowired private ICameraService cameraService; @Autowired private ISsoService ssoService; @Resource private HttpClient httpClient; @Autowired private RabbitMqProducer rabbitMqProducer; @Autowired private RabbitTemplate rabbitTemplate; @Override public String packSceneDataHandler(String num, String title, String logId) throws Exception { //校验场景名称是否正确 String sceneStr = fYunFileService.getFileContent(ossScenePoolFilePath); List towerSceneBeans = JSON.parseArray(sceneStr, TowerSceneBean.class); if(CollUtil.isEmpty(towerSceneBeans)){ return null; } TowerSceneBean towerSceneBean = towerSceneBeans.stream().filter(bean -> title.equals(bean.getName())).findFirst().get(); if(Objects.isNull(towerSceneBean)){ return null; } log.info("开始下载:{}", num); //场景本地数据父目录 String scenePath = parentPath.concat(num).concat("/"); String sceneSourcePath = scenePath.concat(towerSceneBean.getSceneId()).concat("_").concat(towerSceneBean.getRoomId()); String zipPath = sceneSourcePath.concat(".zip"); //data数据下载 String dataPath = sceneSourcePath + "/data/"; String ossDataPath = String.format(UploadFilePath.DATA_VIEW_PATH, num); //下载floorplan.json fYunFileService.downloadFile(ossDataPath.concat("floorplan.json"), dataPath.concat("floorplan.json")); //下载scene.json String sceneJsonPath = dataPath.concat("scene.json"); String sceneJson = redisUtil.get(String.format(RedisKey.SCENE_JSON, num)); if(StrUtil.isEmpty(sceneJson)){ sceneJson = fYunFileService.getFileContent(ossDataPath.concat("scene.json")); } JSONObject sceneJsonObj = JSON.parseObject(sceneJson); sceneJsonObj.put("sceneKind", "pano"); FileUtil.writeUtf8String(sceneJsonObj.toJSONString(), sceneJsonPath); //下载mesh FileUtil.mkdir(dataPath.concat("mesh/")); List meshList = fYunFileService.listRemoteFiles(ossDataPath.concat("mesh")); meshList.parallelStream().forEach(key -> fYunFileService.downloadFile(key, dataPath.concat("mesh/"))); //images数据下载 String imagesPath = sceneSourcePath + "/images/"; String ossImagesPath = String.format(UploadFilePath.IMG_VIEW_PATH, num); //下载high图 List highList = fYunFileService.listRemoteFiles(ossImagesPath.concat("pan/high/")); FileUtil.mkdir(imagesPath.concat("4k/")); highList.parallelStream().forEach(key -> fYunFileService.downloadFile(key, imagesPath.concat("4k/"))); //下载low图 FileUtil.mkdir(imagesPath.concat("512/")); List lowList = fYunFileService.listRemoteFiles(ossImagesPath.concat("pan/low/")); lowList.parallelStream().forEach(key -> fYunFileService.downloadFile(key, imagesPath.concat("512/"))); //下载vision.txt fYunFileService.downloadFile(ossImagesPath.concat("vision.txt"), imagesPath.concat("vision.txt")); //下载caches/images List panoramaImageList = SceneUtil.getPanoramaImageList(imagesPath.concat("vision.txt")); log.info(JSON.toJSONString(panoramaImageList)); List imageList = fYunFileService.listRemoteFiles(String.format(UploadFilePath.scene_result_data_path, num).concat("caches/images/")); if(CollUtil.isNotEmpty(imageList)){ imageList.stream().forEach(key->{ if(panoramaImageList.contains(key.substring(key.lastIndexOf("/") + 1))){ if(!FileUtil.exist(imagesPath.concat("8K"))){ FileUtil.mkdir(imagesPath.concat("8K")); } fYunFileService.downloadFile(key, imagesPath.concat("8K")); } }); // //移除非必须文件 // List fileNames = FileUtil.listFileNames(imagesPath.concat("8K/")); // fileNames.stream().forEach(name->{ // if(!panoramaImageList.contains(name)){ // FileUtil.del(imagesPath.concat("8K/") + name); // } // }); } this.convertVisable(imagesPath.concat("vision.txt")); //打包 ZipUtil.zip(sceneSourcePath, zipPath, true); log.info("结束下载,开始推送:{}", num); try { this.dataPush(num, towerSceneBean.getSceneId(), towerSceneBean.getRoomId(), zipPath); }finally { FileUtil.del(scenePath); } return zipPath; } private void convertVisable(String visionPath){ String visionStr = FileUtil.readUtf8String(visionPath); JSONObject visionObj = JSON.parseObject(visionStr); JSONArray array = visionObj.getJSONArray("sweepLocations"); Map uuidMap = new HashMap<>(); for(int i = 0; i < array.size(); i++){ JSONObject o = (JSONObject)array.get(i); uuidMap.put(i, o.getString("uuid")); } for(int i = 0; i < array.size(); i++){ JSONObject o = (JSONObject)array.get(i); JSONArray visibles = o.getJSONArray("visibles"); o.put("visibles", this.convertVisableHandler(visibles,uuidMap)); JSONArray visibles2 = o.getJSONArray("visibles2"); o.put("visibles2", this.convertVisableHandler(visibles2,uuidMap)); JSONArray visibles3 = o.getJSONArray("visibles3"); o.put("visibles3", this.convertVisableHandler(visibles3,uuidMap)); } FileUtil.writeUtf8String(JSON.toJSONString(visionObj), visionPath); } private String[] convertVisableHandler(JSONArray visibles, Map uuidMap){ int size = visibles.size(); String[] visibleArr = new String[size]; for(int j = 0; j < size; j++){ int index = (Integer)visibles.get(j); String uuid = uuidMap.get(index); visibleArr[j] = uuid; } return visibleArr; } @Override public void packSceneData(Long companyId, List nums) { String key = "tower:scene:data:download"; List scenePlusList = null; if(Objects.nonNull(companyId)){ //根据客户企业id查出所有相机 List cameraDetailList = cameraDetailService.getByCompanyId(companyId); if(CollUtil.isEmpty(cameraDetailList)){ return; } Set cameraIds = cameraDetailList.stream().map(detail -> detail.getCameraId()).collect(Collectors.toSet()); //根据相机id查出所有待下载场景 scenePlusList = scenePlusService.getByCameraIds(cameraIds); }else if(CollUtil.isNotEmpty(nums)){ scenePlusList = scenePlusService.list(new LambdaQueryWrapper().in(ScenePlus::getNum, nums)); }else{ String snCodeStr = fYunFileService.getFileContent(ossCameraPoolFilePath); List snCodeList = Arrays.asList(snCodeStr.split(",")); String sceneStr = fYunFileService.getFileContent(ossScenePoolFilePath); List towerSceneBeans = JSON.parseArray(sceneStr, TowerSceneBean.class); List titleList = towerSceneBeans.stream().map(TowerSceneBean::getName).collect(Collectors.toList()); scenePlusList = scenePlusService.list(new LambdaQueryWrapper().in(ScenePlus::getTitle, titleList)); List dbTitleList = scenePlusList.stream().map(ScenePlus::getTitle).collect(Collectors.toList()); List notFindList = titleList.stream().filter(title -> { if (dbTitleList.contains(title)) { return false; }else{ return true; } }).collect(Collectors.toList()); if(CollUtil.isNotEmpty(notFindList)){ fYunFileService.uploadFile(JSON.toJSONString(notFindList).getBytes(), ossParentPath.concat("notFind") .concat("-") .concat(DateExtUtil.format(new Date(), DateExtUtil.dateStyle7)) .concat(".txt")); } } if(CollUtil.isEmpty(scenePlusList)){ return; } String id = DateExtUtil.format(Calendar.getInstance().getTime(), "yyyyMMddHHmmss"); //遍历场景列表开始下载 scenePlusList.stream().forEach(plus -> { JSONObject jsonObject = new JSONObject(); try { jsonObject = new JSONObject(); jsonObject.put("title", plus.getTitle()); jsonObject.put("num", plus.getNum()); String zipPath = towerService.packSceneDataHandler(plus.getNum(), plus.getTitle(), id); jsonObject.put("status", CommonSuccessStatus.SUCCESS.code()); jsonObject.put("zipPath", zipPath); redisUtil.hset(key, plus.getNum(), JSON.toJSONString(jsonObject)); }catch (Exception e){ log.error("推送失败,num:{},title:{}", plus.getNum(), plus.getTitle(), e); jsonObject.put("status", CommonSuccessStatus.FAIL.code()); jsonObject.put("error", ExceptionUtil.stacktraceToString(e, 3000)); redisUtil.hset(key, plus.getNum(), JSON.toJSONString(jsonObject)); } }); } @Override public JSONObject dataPush(String num, String sceneId, String roomId, String zipPath) throws Exception { JSONObject jsonObject = null; log.info("开始组装参数"); String fileMd5 = com.fdkankan.extend.util.FileUtil.getMulFileMD5(FileUtil.getInputStream(zipPath)); String compose = sceneId.concat("+").concat(roomId).concat("+").concat(fileMd5); String encrypt = RsaCryptTools.encryptData(compose, privateKey); Map params = new HashMap<>(); params.put("stationCode", sceneId); params.put("roomEntityID", roomId); params.put("file", FileUtil.file(zipPath)); params.put("manufact", manufact); params.put("encrypt", encrypt); params.put("upUserAcct", "xxx"); log.info("private-key:{}", privateKey); log.info("manufact:{}", manufact); log.info("compose:{}", compose); log.info("encrypt:{}", encrypt); log.info("开始推送"); try { String post = HttpUtil.post(url, params, 60*60*1000); jsonObject = JSON.parseObject(post); if(!jsonObject.containsKey("resultCode")){ throw new RuntimeException("铁塔接口出参错误"); } return jsonObject; }catch (Exception e){ log.error("调用铁塔接口出错,场景:{}", num, e); jsonObject = new JSONObject(); jsonObject.put("resultCode", -2); jsonObject.put("resultMsg", "调用铁塔接口出错"); return jsonObject; } } private void dataPushLog(String num, String title, String resultCode, String resultMsg, String logId){ Map log = new HashMap<>(); log.put("num", num); log.put("title", title); log.put("resultCode", resultCode); log.put("resultMsg", resultMsg); String content = JSON.toJSONString(log).concat("\r\n"); FileUtil.appendUtf8String(content, parentPath.concat("download").concat("-").concat(logId).concat(".log")); } public static void main(String[] args) { // System.out.println(AlgorithmUtil.getId("AES")); } @Override public ResultData sceneBatchMove(MultipartFile file, String origSnCode, String snCode) throws IOException { if(file.isEmpty()){ throw new BusinessException(-1, "文件为空"); } String origPath = "/home/backend/4dkankan_v4/modeling-extend/move/"; String move_url = "https://www.4dkankan.com/service/manage/scene/move"; String orgFileId = DateUtil.format(Calendar.getInstance().getTime(), DateExtUtil.dateStyle11); String orgFilePath = origPath.concat(orgFileId).concat(".txt"); FileUtil.mkParentDirs(orgFilePath); file.transferTo(new File(orgFilePath)); List nums = FileUtil.readUtf8Lines(orgFilePath); if(CollUtil.isEmpty(nums)){ throw new BusinessException(-1, "文件为空"); } Camera camera = cameraService.findByChildName(origSnCode); if(Objects.isNull(camera)){ throw new BusinessException(ErrorCode.CAMERA_BIND_NO_EXIST); } List notExists = new ArrayList<>(); for (String num : nums) { ScenePlus byCameraIdAndNum = scenePlusService.getByCameraIdAndNum(num, camera.getId()); if(Objects.isNull(byCameraIdAndNum)){ notExists.add(num); } } if(CollUtil.isNotEmpty(notExists)){ throw new BusinessException(-1, "在原始相机下查询不到场景,请核查:".concat(JSON.toJSONString(notExists))); } //模拟登录 Map headers = new HashMap<>(); try { String token = ssoService.login("zhongwentong", "4Dage168"); headers.put("token", token); }catch (Exception e){ return ResultData.error(-1, "登录失败"); } List> result = new ArrayList<>(); for (String num : nums) { Map param = new HashMap<>(); param.put("num", num); param.put("snCode", snCode); ResultData> resultData = httpClient.post(move_url, headers, param); Map item = new HashMap<>(); item.put("num", num); item.put("code", resultData.getCode()); item.put("message", resultData.getMessage()); result.add(item); } return ResultData.ok(result); } @Override public void pushScene(TowerSceneBean towerSceneBean) throws Exception { JSONObject pushResult = null; String num = towerSceneBean.getNum(); Map noticeContent = new HashMap<>(); noticeContent.put("num", num); String scenePath = parentPath.concat(num).concat("/"); try { //场景本地数据父目录 String sceneSourcePath = scenePath.concat(towerSceneBean.getSceneId()).concat("_").concat(towerSceneBean.getRoomId()); String zipPath = sceneSourcePath.concat(".zip"); log.info("开始下载:{}", num); ScenePlus scenePlus = scenePlusService.getByNum(num); if(Objects.isNull(scenePlus)){ throw new RuntimeException("查询不到此场景"); } if(scenePlus.getSceneStatus() != SceneStatus.SUCCESS.code() && scenePlus.getSceneStatus() != SceneStatus.NO_DISPLAY.code()){ throw new RuntimeException("场景计算中"); } //data数据下载 String dataPath = sceneSourcePath + "/data/"; String ossDataPath = String.format(UploadFilePath.DATA_VIEW_PATH, num); //下载floorplan.json fYunFileService.downloadFile(ossDataPath.concat("floorplan.json"), dataPath.concat("floorplan.json")); // //下载scene.json String sceneJsonPath = dataPath.concat("scene.json"); String sceneJson = fYunFileService.getFileContent(ossDataPath.concat("scene.json")); JSONObject sceneJsonObj = JSON.parseObject(sceneJson); sceneJsonObj.put("sceneKind", "pano"); FileUtil.writeUtf8String(sceneJsonObj.toJSONString(), sceneJsonPath); //下载mesh FileUtil.mkdir(dataPath.concat("mesh/")); List meshList = fYunFileService.listRemoteFiles(ossDataPath.concat("mesh")); meshList.parallelStream().forEach(key -> fYunFileService.downloadFile(key, dataPath.concat("mesh/"))); //images数据下载 String imagesPath = sceneSourcePath + "/images/"; String ossImagesPath = String.format(UploadFilePath.IMG_VIEW_PATH, num); //下载high图 List highList = fYunFileService.listRemoteFiles(ossImagesPath.concat("pan/high/")); FileUtil.mkdir(imagesPath.concat("4k/")); highList.parallelStream().forEach(key -> fYunFileService.downloadFile(key, imagesPath.concat("4k/"))); //下载low图 FileUtil.mkdir(imagesPath.concat("512/")); List lowList = fYunFileService.listRemoteFiles(ossImagesPath.concat("pan/low/")); lowList.parallelStream().forEach(key -> fYunFileService.downloadFile(key, imagesPath.concat("512/"))); //下载vision.txt fYunFileService.downloadFile(ossImagesPath.concat("vision.txt"), imagesPath.concat("vision.txt")); //下载caches/images List panoramaImageList = SceneUtil.getPanoramaImageList(imagesPath.concat("vision.txt")); log.info(JSON.toJSONString(panoramaImageList)); List imageList = fYunFileService.listRemoteFiles(String.format(UploadFilePath.scene_result_data_path, num).concat("caches/images/")); if(CollUtil.isNotEmpty(imageList)){ imageList.stream().forEach(key->{ if(panoramaImageList.contains(key.substring(key.lastIndexOf("/") + 1))){ if(!FileUtil.exist(imagesPath.concat("8K"))){ FileUtil.mkdir(imagesPath.concat("8K")); } fYunFileService.downloadFile(key, imagesPath.concat("8K")); } }); } this.convertVisable(imagesPath.concat("vision.txt")); //打包 ZipUtil.zip(sceneSourcePath, zipPath, true); log.info("打包成功,开始推送:{}", num); pushResult = this.dataPush(num, towerSceneBean.getSceneId(), towerSceneBean.getRoomId(), zipPath); }catch (Exception e){ log.error("场景推送出错,未知异常", e); pushResult = new JSONObject(); pushResult.put("resultCode", -3); pushResult.put("resultMsg", e.getMessage()); } finally { noticeContent.put("result", pushResult); rabbitMqProducer.sendByWorkQueue(TIE_TA_SCENE_PUSH_CALLBACK_QUEUE, noticeContent); // rabbitTemplate.convertAndSend(TIE_TA_SCENE_PUSH_CALLBACK_EXCHANGE , TIE_TA_SCENE_PUSH_CALLBACK_ROUTING , noticeContent); FileUtil.del(scenePath); } } }