package com.fdkankan.scene.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.ZipUtil; 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.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fdkankan.common.constant.CommonStatus; import com.fdkankan.model.constants.ConstantFileName; import com.fdkankan.model.constants.ConstantFilePath; import com.fdkankan.common.constant.ErrorCode; import com.fdkankan.common.constant.FileBizType; import com.fdkankan.common.constant.ServerCode; import com.fdkankan.model.constants.UploadFilePath; import com.fdkankan.common.exception.BusinessException; import com.fdkankan.web.response.ResultData; import com.fdkankan.model.utils.ComputerUtil; import com.fdkankan.model.utils.ConvertUtils; import com.fdkankan.model.utils.CreateObjUtil; import com.fdkankan.common.util.FileUtils; import com.fdkankan.common.util.OkHttpUtils; import com.fdkankan.fyun.constant.FYunTypeEnum; import com.fdkankan.fyun.face.FYunFileServiceInterface; import com.fdkankan.redis.constant.RedisKey; import com.fdkankan.redis.constant.RedisLockKey; import com.fdkankan.redis.util.RedisLockUtil; import com.fdkankan.redis.util.RedisUtil; import com.fdkankan.scene.bean.IconBean; import com.fdkankan.scene.bean.TagBean; import com.fdkankan.scene.entity.SceneEditInfo; import com.fdkankan.scene.entity.ScenePlus; import com.fdkankan.scene.entity.ScenePlusExt; import com.fdkankan.scene.entity.ScenePro; import com.fdkankan.scene.mapper.ISceneProMapper; import com.fdkankan.scene.service.ISceneDataDownloadService; import com.fdkankan.scene.service.ISceneEditControlsService; import com.fdkankan.scene.service.ISceneEditInfoService; import com.fdkankan.scene.service.IScenePlusExtService; import com.fdkankan.scene.service.IScenePlusService; import com.fdkankan.scene.service.ISceneProService; import com.fdkankan.scene.service.ISceneUploadService; import com.fdkankan.scene.vo.BaseDataParamVO; import com.fdkankan.scene.vo.DeleteFileParamVO; import com.fdkankan.scene.vo.DeleteHotIconParamVO; import com.fdkankan.scene.vo.DeleteHotParamVO; import com.fdkankan.scene.vo.FileNameAndDataParamVO; import com.fdkankan.scene.vo.HotParamVO; import com.fdkankan.scene.vo.SaveTagsParamVO; import com.fdkankan.scene.vo.SaveTagsVisibleParamVO; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.redisson.Redisson; import org.redisson.RedissonLock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; /** *

* pro场景表 服务实现类 *

* * @author dengsixing * @since 2021-12-23 */ @Slf4j @Service public class SceneProServiceImpl extends ServiceImpl implements ISceneProService { @Value("${fyun.host}") private String ossUrlPrefix; @Value("${fyun.type}") private String fyunType; @Value("${ecs.checkFile.maxTimes:5}") private int maxCheckTimes; @Value("${ecs.checkFile.waitTime:5000}") private int waitTime; @Autowired private FYunFileServiceInterface fYunFileService; @Autowired private RedisLockUtil redisLockUtil; @Autowired private RedisUtil redisUtil; @Autowired private ISceneEditInfoService sceneEditInfoService; @Autowired private IScenePlusService scenePlusService; @Autowired private IScenePlusExtService scenePlusExtService; @Autowired private ISceneUploadService sceneUploadService; @Transactional @Override public ResultData saveInitialPage(FileNameAndDataParamVO param) throws Exception{ ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(scenePlus == null){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String thumbUrl = String.format(UploadFilePath.USER_EDIT_PATH, param.getNum()) + param.getFileName(); //更新缩略图url if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){ thumbUrl = this.ossUrlPrefix + thumbUrl; } scenePlusExt.setThumb(thumbUrl); scenePlusExtService.updateById(scenePlusExt); SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId()); if(sceneEditInfo == null){ sceneEditInfo = new SceneEditInfo(); sceneEditInfo.setScenePlusId(scenePlus.getId()); sceneEditInfo.setEntry(param.getData()); sceneEditInfoService.save(sceneEditInfo); }else{ sceneEditInfoService.update( new LambdaUpdateWrapper() .set(SceneEditInfo::getEntry, param.getData()) .setSql("version = version + 1") .eq(SceneEditInfo::getId, sceneEditInfo.getId())); } return ResultData.ok(); } @Override public ResultData addOrUpdateTag(SaveTagsParamVO param) throws Exception { // ScenePro scenePro = this.findBySceneNum(param.getNum()); ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if (scenePlus == null) return ResultData.error(ErrorCode.FAILURE_CODE_5005); this.addOrUpdateHotData(param.getNum(), param.getHotDataList()); this.addOrUpdateIcons(param.getNum(), param.getIcons()); //写入本地文件,作为备份 this.writeHotJson(param.getNum()); //保存数据库 SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId()); sceneEditInfoService.saveTagsToSceneEditInfo(param.getNum(), sceneEditInfo); sceneEditInfoService.updateById(sceneEditInfo); return ResultData.ok(); } private void addOrUpdateHotData(String num, List hotDataList) throws Exception{ Map addOrUpdateMap = new HashMap<>(); int i = 0; for (HotParamVO hotParamVO : hotDataList) { JSONObject jsonObject = JSON.parseObject(hotParamVO.getHotData()); jsonObject.put("createTime", Calendar.getInstance().getTimeInMillis() + i++); addOrUpdateMap.put(hotParamVO.getSid(), jsonObject.toJSONString()); } this.syncHotFromFileToRedis(num); //处理新增和修改数据 this.addOrUpdateHotDataHandler(num, addOrUpdateMap); } private void addOrUpdateIcons(String num, List icons) throws Exception{ if(CollUtil.isEmpty(icons)){ return; } this.syncIconsFromFileToRedis(num); String key = String.format(RedisKey.SCENE_HOT_ICONS, num); redisUtil.sSet(key, icons.toArray()); } @Override public ResultData deleteTag(DeleteHotParamVO param) throws Exception { // ScenePro scenePro = this.findBySceneNum(param.getNum()); ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if (scenePlus == null) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); List deleteSidList = param.getSidList(); //处理删除状态数据 this.deleteHotData(param.getNum(), deleteSidList, scenePlusExt.getYunFileBucket()); //写入本地文件,作为备份 this.writeHotJson(param.getNum()); //保存数据库 SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId()); sceneEditInfoService.saveTagsToSceneEditInfo(param.getNum(), sceneEditInfo); sceneEditInfoService.updateById(sceneEditInfo); return ResultData.ok(); } @Override public ResultData deleteIcons(DeleteHotIconParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if (scenePlus == null) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); List fileNameList = param.getFileNameList(); this.syncIconsFromFileToRedis(param.getNum()); String key = String.format(RedisKey.SCENE_HOT_ICONS, param.getNum()); redisUtil.setRemove(key, fileNameList.toArray()); //写入本地文件,作为备份 this.writeHotJson(param.getNum()); //删除oss文件 sceneUploadService.delete( DeleteFileParamVO.builder() .num(param.getNum()) .fileNames(fileNameList) .bizType(FileBizType.TAG_ICON.code()).build()); return ResultData.ok(); } @Override public ResultData listTags(String num) throws Exception{ //保证热点数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis this.syncHotFromFileToRedis(num); //保证icons数据安全性,当redis宕机导致icons数据丢失时,可以从文件中读取,恢复到redis this.syncIconsFromFileToRedis(num); JSONObject result = new JSONObject(); //查询缓存是否包含热点数据 String key = String.format(RedisKey.SCENE_HOT_DATA, num); Map allTagsMap = redisUtil.hmget(key); List tags = Lists.newArrayList(); List tagBeanList = new ArrayList<>(); if(CollUtil.isNotEmpty(allTagsMap)){ allTagsMap.entrySet().stream().forEach(entry -> { JSONObject jsonObject = JSON.parseObject(entry.getValue()); tagBeanList.add( TagBean.builder() .createTime(jsonObject.getLong("createTime")) .tag(jsonObject).build()); }); //按创建时间倒叙排序 tagBeanList.sort(Comparator.comparingLong(TagBean::getCreateTime).reversed()); //移除createTime字段 tags = tagBeanList.stream().map(tagBean -> { JSONObject tag = tagBean.getTag(); tag.remove("createTime"); return tag; }).collect(Collectors.toList()); } result.put("tags", tags); //查询缓存是否包含icons key = String.format(RedisKey.SCENE_HOT_ICONS, num); Set icons = redisUtil.sGet(key); if(icons == null){ icons = Sets.newHashSet(); } List iconList = this.sortIcons(tags, icons); result.put("icons", iconList); return ResultData.ok(result); } private List sortIcons(List tags, Set icons){ //统计使用频次 List iconBeans = Lists.newArrayList(); for (String icon : icons) { int count = 0; for (JSONObject tag : tags) { String sid = tag.getString("icon"); if(StrUtil.isEmpty(sid) || !icon.equals(sid)){ continue; } ++count; } iconBeans.add(IconBean.builder().icon(icon).count(count).build()); } //排序 List iconList = iconBeans.stream().sorted(Comparator.comparing(IconBean::getCount).reversed()) .map(item -> { return item.getIcon(); }).collect(Collectors.toList()); return iconList; } /** *

保证热点数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis *

* @author dengsixing * @date 2022/3/3 **/ private void syncHotFromFileToRedis(String num) throws Exception{ String key = String.format(RedisKey.SCENE_HOT_DATA, num); boolean exist = redisUtil.hasKey(key); if(exist){ return; } String lockKey = String.format(RedisLockKey.LOCK_HOT_DATA_SYNC, num); String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString(); boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE); if(!lock){ throw new BusinessException(ErrorCode.SYSTEM_BUSY); } try{ exist = redisUtil.hasKey(key); if(exist){ return; } String tagsFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json"; String tagsData = FileUtils.readUtf8String(tagsFilePath); if(StrUtil.isEmpty(tagsData)){ return; } JSONObject jsonObject = JSON.parseObject(tagsData); JSONArray tagsArr = jsonObject.getJSONArray("tags"); if(CollUtil.isEmpty(tagsArr)){ return; } Map map = new HashMap<>(); for (Object o : tagsArr) { JSONObject jo = (JSONObject)o; map.put(jo.getString("sid"), jo.toJSONString()); } redisUtil.hmset(key, map); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } } /** *

保证icons数据安全性,当redis宕机导致icons数据丢失时,可以从文件中读取,恢复到redis *

* @author dengsixing * @date 2022/3/3 **/ private void syncIconsFromFileToRedis(String num) throws Exception{ String key = String.format(RedisKey.SCENE_HOT_ICONS, num); boolean exist = redisUtil.hasKey(key); if(exist){ return; } String lockKey = String.format(RedisLockKey.LOCK_HOT_ICONS_SYNC, num); String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString(); boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE); if(!lock){ throw new BusinessException(ErrorCode.SYSTEM_BUSY); } try{ exist = redisUtil.hasKey(key); if(exist){ return; } String tagsFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json"; String tagsData = FileUtils.readUtf8String(tagsFilePath); if(StrUtil.isEmpty(tagsData)){ return; } JSONObject jsonObject = JSON.parseObject(tagsData); JSONArray iconArr = jsonObject.getJSONArray("icons"); if(CollUtil.isEmpty(iconArr)){ return; } redisUtil.sSet(key, iconArr.toJavaList(String.class).toArray()); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } } /** *

热点数据保存 *

* @author dengsixing * @date 2022/3/3 **/ private void writeHotJson(String num) throws Exception{ String dataKey = String.format(RedisKey.SCENE_HOT_DATA, num); Map tagMap = redisUtil.hmget(dataKey); List tagList = Lists.newArrayList(); tagMap.entrySet().stream().forEach(entry->{ if(StrUtil.isNotEmpty(entry.getValue())){ tagList.add(entry.getValue()); } }); JSONObject jsonObject = new JSONObject(); JSONArray tagJsonArr = new JSONArray(); if(CollUtil.isNotEmpty(tagList)){ tagList.stream().forEach(hot->{ tagJsonArr.add(JSONObject.parseObject(hot)); }); } jsonObject.put("tags", tagJsonArr); String iconsKey = String.format(RedisKey.SCENE_HOT_ICONS, num); Set iconList = redisUtil.sGet(iconsKey); jsonObject.put("icons", iconList); String hotJsonPath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json"; String lockKey = String.format(RedisLockKey.LOCK_HOT_JSON, num); String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString(); boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE); if(!lock){ return; } try{ FileUtils.writeFile(hotJsonPath, jsonObject.toJSONString()); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } } private void addOrUpdateHotDataHandler(String num, Map addOrUpdateMap){ if(CollUtil.isEmpty(addOrUpdateMap)) return; //数据验证,新增、修改状态,hotdata不能为空 for (String sid : addOrUpdateMap.keySet()) { String hotData = addOrUpdateMap.get(sid); if(StrUtil.isEmpty(hotData)){ throw new BusinessException(ErrorCode.FAILURE_CODE_7004); } } //批量写入缓存 String key = String.format(RedisKey.SCENE_HOT_DATA, num); redisUtil.hmset(key, addOrUpdateMap); } private void deleteHotData(String num, List deleteSidList, String bucket) throws Exception { this.syncHotFromFileToRedis(num); if(CollUtil.isEmpty(deleteSidList)){ return; } //从redis中加载热点数据 String key = String.format(RedisKey.SCENE_HOT_DATA, num); List deletDataList = redisUtil.hMultiGet(key, deleteSidList); if(CollUtil.isEmpty(deletDataList)) return; String userDataPath = String.format(UploadFilePath.USER_EDIT_PATH, num); //删除图片音频视频等资源文件 for (String data : deletDataList) { if(StrUtil.isBlank(data)){ continue; } JSONObject jsonObject = JSON.parseObject(data); String sid = jsonObject.getString("sid"); if(jsonObject.containsKey("media")){ String fileType = jsonObject.getString("media"); if(fileType.contains("photo")) { fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".jpg"); } if(fileType.contains("audio") || fileType.contains("voice")) { fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".mp3"); } if(fileType.contains("video")) { fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".mp4"); } } } //从redis中移除热点数据 redisUtil.hdel(key, deleteSidList.toArray()); } @Override public ResultData saveTagsVisible(SaveTagsVisibleParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if (scenePlus == null ) { return ResultData.error(ErrorCode.FAILURE_CODE_5005); } JSONArray visiblePanos = JSONArray.parseArray(param.getData()); //如果redis找不到,就从本地文件中reload this.syncHotFromFileToRedis(param.getNum()); //从缓存中获取热点数据,如果为空,抛出异常 String key = String.format(RedisKey.SCENE_HOT_DATA, param.getNum()); Map map = redisUtil.hmget(key); if (CollUtil.isEmpty(map)) { throw new BusinessException(ErrorCode.FAILURE_CODE_7005); } List> allTags = map.entrySet().stream().filter(item -> { if (StrUtil.isBlank(item.getValue())) { return false; } return true; }).collect(Collectors.toList()); if (CollUtil.isEmpty(allTags)) { throw new BusinessException(ErrorCode.FAILURE_CODE_7005); } allTags.stream().forEach(entry->{ JSONObject hot = JSON.parseObject(entry.getValue()); visiblePanos.stream().forEach(item->{ if (hot.getString("sid").equals(((JSONObject) item).getString("sid"))) { hot.put("visiblePanos", ((JSONObject) item).getJSONArray("value")); hot.put("isHidden", ((JSONObject) item).getBoolean("isHidden")); entry.setValue(hot.toJSONString()); } }); }); //更新版本号 SceneEditInfo editInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId()); sceneEditInfoService.upgradeVersionById(editInfo.getId()); //放入缓存 Map finalMap = new HashMap<>(); allTags.stream().forEach(entry->{ finalMap.put(entry.getKey(), entry.getValue()); }); redisUtil.hmset(key, finalMap); //写入本地文件,作为备份,以防redis数据丢失 this.writeHotJson(param.getNum()); return ResultData.ok(); } @Override public ResultData saveRoam(BaseDataParamVO param) throws Exception { // ScenePro scenePro = this.findBySceneNum(param.getNum()); ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if (scenePlus == null ) { return ResultData.error(ErrorCode.FAILURE_CODE_5005); } ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); JSONArray inputData = JSONObject.parseArray(param.getData()); String localDataPath = String.format(ConstantFilePath.SCENE_DATA_PATH_V4, param.getNum()); File directory = new File(localDataPath); if (!directory.exists()) { directory.mkdirs(); } String viewImagesPath = String.format(UploadFilePath.IMG_VIEW_PATH, param.getNum()); //如果是云存储,将vision.modeldata下载到本地,如果是本地存储,场景计算完就已经将这个文件拷贝到编辑目录了存在这个文件了,不需要再下载 fYunFileService.downloadFile(bucket, viewImagesPath + "vision.modeldata", localDataPath + "vision.modeldata"); //检查vision.modeldata本地是否存在,不存在抛出异常 File file = new File(localDataPath + "vision.modeldata"); if(!file.exists()) { return ResultData.error(ErrorCode.FAILURE_CODE_5012); } //将vision.modeldata解压缩至vision.json ConvertUtils.convertVisionModelDataToTxt(localDataPath + "vision.modeldata", localDataPath + "vision.json"); String str = FileUtils.readFile(localDataPath + "vision.json"); JSONObject json = JSONObject.parseObject(str); JSONArray panos = json.getJSONArray("sweepLocations"); for (int i = 0; i < panos.size(); ++i) { JSONObject pano = panos.getJSONObject(i); for (int j = 0; j < inputData.size(); ++j) { JSONObject jo = inputData.getJSONObject(j); String currentPanoId = jo.getString("panoID"); JSONArray visibles = jo.getJSONArray("visibles"); JSONArray visibles3 = jo.getJSONArray("visibles3"); if (pano.getString("uuid").equals(currentPanoId)) { pano.put("visibles", visibles); pano.put("visibles3", visibles3); } } } FileUtils.deleteFile(localDataPath + "vision.json"); FileUtils.deleteFile(localDataPath + "vision.modeldata"); FileUtils.writeFile(localDataPath + "vision.json", json.toString()); ConvertUtils.convertTxtToVisionModelData(localDataPath + "vision.json", localDataPath + "vision.modeldata"); fYunFileService.uploadFile(bucket, localDataPath + "vision.modeldata", viewImagesPath + "vision.modeldata"); //更新版本号 SceneEditInfo editInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId()); if(Objects.isNull(editInfo)){ editInfo = new SceneEditInfo(); editInfo.setScenePlusId(scenePlus.getId()); sceneEditInfoService.save(editInfo); }else{ sceneEditInfoService.upgradeVersionById(editInfo.getId()); //更新scenejson缓存和oss文件版本号 sceneEditInfoService.upgradeSceneJsonVersion(param.getNum(), editInfo.getVersion() + 1, null, bucket); } //更新scene.json版本号 // sceneEditInfoService.upgradeVersionToSceneJson(param.getNum()); return ResultData.ok(); } @Override public void updateUserIdByCameraId(Long userId, Long cameraId) { this.update(new LambdaUpdateWrapper() .eq(ScenePro::getCameraId, cameraId) .set(ScenePro::getUserId, userId)); } @Override public ResultData uploadObjAndImg(String num, MultipartFile file) throws Exception{ if(StrUtil.isEmpty(num)){ throw new BusinessException(ServerCode.PARAM_REQUIRED, "num"); } if(!file.getOriginalFilename().endsWith(".zip")){ throw new BusinessException(ErrorCode.FAILURE_CODE_7015); } ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num); if(scenePlus == null){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); //文件上传的位置可以自定义 String path = scenePlusExt.getDataSource() + "_obj2txt"; String zipPath = path + "/zip/"; String filePath = path + "/extras/"; String resultPath = path + "/results/"; //压缩文件处理:解压缩,解压缩后复制等操作 this.objAndImgFileHandler(resultPath, filePath, zipPath, file); //创建data.json this.writeDataJson(path); //调用算法,不同的类型调用不同的算法 if("V2".equals(scenePlusExt.getBuildType())){ CreateObjUtil.objToTxt(path , "1"); } if("V3".equals(scenePlusExt.getBuildType())){ CreateObjUtil.build3dModel(path , "1"); } //算法计算完后,生成压缩文件,上传到oss this.uploadFileofterRebuildPanoram(path, num, bucket); //更新版本信息 SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId()); if(Objects.isNull(sceneEditInfo)){ sceneEditInfo = new SceneEditInfo(); sceneEditInfo.setScenePlusId(scenePlus.getId()); sceneEditInfo.setFloorPublishVer(1); sceneEditInfo.setFloorEditVer(1); sceneEditInfo.setIsUploadObj(CommonStatus.YES.code()); sceneEditInfoService.save(sceneEditInfo); }else{ sceneEditInfoService.update( new LambdaUpdateWrapper() .setSql("version = version + 1") .setSql("floor_edit_ver = floor_edit_ver + 1") .setSql("floor_publish_ver = floor_publish_ver + 1") .setSql("img_version = img_version + 1") .set(SceneEditInfo::getIsUploadObj, CommonStatus.YES.code()) .eq(SceneEditInfo::getId, sceneEditInfo.getId())); sceneEditInfoService.upgradeSceneJsonVersion(num, sceneEditInfo.getVersion() + 1, sceneEditInfo.getImgVersion() + 1, bucket); } return ResultData.ok(); } private void uploadFileofterRebuildPanoram(String path, String sceneNum, String bucket) throws Exception { //因为共享目录有延迟,这里循环检测算法是否计算完毕3次,每次隔五秒 String uploadJsonPath = path + File.separator + "results" +File.separator+"upload.json"; boolean exist = ComputerUtil.checkComputeCompleted(uploadJsonPath, maxCheckTimes, waitTime); if(!exist){ throw new BusinessException(ErrorCode.FAILURE_CODE_7013); } String uploadData = FileUtils.readFile(uploadJsonPath); JSONObject uploadJson = null; JSONArray array = null; if(uploadData!=null) { uploadJson = JSONObject.parseObject(uploadData); array = uploadJson.getJSONArray("upload"); } Map map = new HashMap(); JSONObject fileJson = null; String fileName = ""; String imgViewPath = String.format(UploadFilePath.IMG_VIEW_PATH, sceneNum); for(int i = 0, len = array.size(); i < len; i++) { fileJson = array.getJSONObject(i); fileName = fileJson.getString("file"); //文件不存在抛出异常 if (!new File(path + File.separator + "results" + File.separator + fileName).exists()) { throw new Exception(path + File.separator + "results" + File.separator + fileName + "文件不存在"); } //tex文件夹 if (fileJson.getIntValue("clazz") == 15) { map.put(path + File.separator + "results" + File.separator + fileName, imgViewPath + ConstantFileName.modelUUID + "_50k_texture_jpg_high1/" + fileName.replace("tex/", "")); continue; } } CreateObjUtil.convertTxtToDam( path + File.separator + "results" +File.separator+"modeldata.txt", path + File.separator + "results" +File.separator+ ConstantFileName.modelUUID+"_50k.dam"); CreateObjUtil.convertDamToLzma(path + File.separator + "results"); CreateObjUtil.convertTxtToDam( path + File.separator + "results" +File.separator+"modeldata.txt", path + File.separator + "results" + File.separator+ConstantFileName.modelUUID+"_50k.dam"); map.put(path + File.separator + "results" +File.separator+ConstantFileName.modelUUID+"_50k.dam.lzma", imgViewPath +ConstantFileName.modelUUID+"_50k.dam.lzma"); map.put(path + File.separator + "results" +File.separator+ConstantFileName.modelUUID+"_50k.dam", imgViewPath+ConstantFileName.modelUUID+"_50k.dam"); fYunFileService.uploadMulFiles(bucket, map); } private void writeDataJson(String path) throws IOException { JSONObject dataJson = new JSONObject(); dataJson.put("obj2txt", true); dataJson.put("split_type", "SPLIT_V6"); dataJson.put("data_describe", "double spherical"); dataJson.put("skybox_type", "SKYBOX_V5"); FileUtils.writeFile(path + "/data.json", dataJson.toString()); } private void objAndImgFileHandler(String resultPath, String filePath, String zipPath, MultipartFile file) throws Exception { FileUtils.delAllFile(resultPath); File targetFile = new File(filePath); if (!targetFile.exists()) { targetFile.mkdirs(); }else { FileUtils.delAllFile(filePath); } targetFile = new File(zipPath); if (!targetFile.exists()) { targetFile.mkdirs(); }else { FileUtils.delAllFile(zipPath); } targetFile = new File(zipPath + file.getOriginalFilename()); if(!targetFile.getParentFile().exists()){ targetFile.getParentFile().mkdirs(); } // 保存压缩包到本地 if(targetFile.exists()) { FileUtils.deleteFile(zipPath + file.getOriginalFilename()); } file.transferTo(targetFile); ZipUtil.unzip(zipPath + file.getOriginalFilename(), zipPath + "data/"); //源文件数据,判断是否有多个文件夹,有多个就提示错误,有一个就将文件夹里数据迁移到extras目录,无直接迁移到extras目录 boolean flag = false; //目录名称,如果不为空,则压缩文件第一层是目录 String targetName = ""; File dataFile = new File(zipPath + "data/"); for(File data : dataFile.listFiles()){ if(data.isDirectory() && flag){ throw new BusinessException(ErrorCode.FAILURE_CODE_5018); } if(data.isDirectory() && !flag){ flag = true; targetName = data.getName(); } } //是否包含obj文件 boolean objFlag = false; //是否包含mtl文件 boolean mtlFlag = false; File[] files = null; String dataPath = null; if(StrUtil.isEmpty(targetName)){ files = dataFile.listFiles(); dataPath = zipPath + "data/"; }else{ files = new File(zipPath + "data/" + targetName).listFiles(); dataPath = zipPath + "data/" + targetName + File.separator; } for(File data : files){ if(data.isDirectory()){ throw new BusinessException(ErrorCode.FAILURE_CODE_5018); } if(data.getName().endsWith(".jpg") || data.getName().endsWith(".png")){ if(!FileUtils.checkFileSizeIsLimit(data.length(), 1.5, "M")){ throw new BusinessException(ErrorCode.FAILURE_CODE_5020); } } if(data.getName().endsWith(".obj")){ if(objFlag){ throw new BusinessException(ErrorCode.FAILURE_CODE_5019); } if(!data.getName().equals("mesh.obj")){ throw new BusinessException(ErrorCode.FAILURE_CODE_5060); } if(!FileUtils.checkFileSizeIsLimit(data.length(), 20, "M")){ throw new BusinessException(ErrorCode.FAILURE_CODE_5020); } objFlag = true; FileUtils.copyFile(dataPath + data.getName(), filePath + "mesh.obj", true); continue; } if(data.getName().endsWith(".mtl")){ mtlFlag = true; } FileUtils.copyFile(dataPath + data.getName(), filePath + data.getName(), true); } //压缩文件中必须有且仅有一个obj和mtl文件,否则抛出异常 if(!mtlFlag || !objFlag){ throw new BusinessException(ErrorCode.FAILURE_CODE_5059); } } public ResultData downloadTexData(String num) throws Exception { if(StrUtil.isEmpty(num)){ throw new BusinessException(ErrorCode.PARAM_REQUIRED); } ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num); if(scenePlus == null){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId()); String localImagePath = String.format(ConstantFilePath.IMAGESBUFFER_FORMAT, num); if(!new File(localImagePath).exists()){ new File(localImagePath).mkdirs(); } String newData = scenePlusExt.getDataSource() + "_obj2txt/extras"; String newResultData = scenePlusExt.getDataSource() + "_obj2txt/results/upload.json"; String zipName = num + "_extras.zip"; String zipPath = localImagePath + zipName; //如果用户上传过模型,就打包上传到oss,直接返回 if(CommonStatus.YES.code().equals(sceneEditInfo.getIsUploadObj()) && new File(newData).exists() && new File(newResultData).exists()){ //打包 ZipUtil.zip(newData, zipPath); //上传压缩包 fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName); String url = "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis(); if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){ url = ossUrlPrefix + url; } return ResultData.ok(url); } String buildType = scenePlusExt.getBuildType(); if("V3".equals(buildType)){ String dataViewPath = String.format(UploadFilePath.DATA_VIEW_PATH, num); //V3版本去oss下载2048模型 String meshPath = String.format(ConstantFilePath.DATABUFFER_FORMAT, num) + "mesh"; FileUtils.deleteDirectory(meshPath); fYunFileService.downloadFileByCommand(bucket, meshPath, dataViewPath + "mesh"); log.info("meshPath="+meshPath); if(!new File(meshPath).exists()){ throw new BusinessException(ErrorCode.FAILURE_CODE_7006); } log.info(new File(meshPath).listFiles().toString()); if(new File(meshPath).listFiles().length > 0){ for(File file : new File(meshPath).listFiles()){ if(file.isDirectory()){ for (File item : file.listFiles()) { if(item.getName().endsWith(".obj") && !"output.house.obj".equals(item.getName()) && !"mesh.obj".equals(item.getName())){ item.delete(); } if(item.getName().endsWith(".mtl") && !"output.house.mtl".equals(item.getName()) && !"mesh.mtl".equals(item.getName())){ item.delete(); } } continue; } if(file.getName().endsWith(".obj") && !"output.house.obj".equals(file.getName()) && !"mesh.obj".equals(file.getName())){ file.delete(); } if(file.getName().endsWith(".mtl") && !"output.house.mtl".equals(file.getName()) && !"mesh.mtl".equals(file.getName())){ file.delete(); } } //打包 ZipUtil.zip(meshPath, zipPath); //上传压缩包 fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName); String url = "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis(); if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){ url = ossUrlPrefix + url; } // FileUtil.del(zipPath); return ResultData.ok(url); } } //V2版本在本地获取模型资源 //修改过的资源 String editData = scenePlusExt.getDataSource() + "_edit/caches/tex"; String results = scenePlusExt.getDataSource() + "_edit/results"; if (new File(editData).exists() && new File(results).exists()){ for(File file : new File(editData).listFiles()){ if(file.getName().endsWith(".obj") && !"output.house.obj".equals(file.getName()) && !"mesh.obj".equals(file.getName())){ file.delete(); } if(file.getName().endsWith(".mtl") && !"output.house.mtl".equals(file.getName()) && !"mesh.mtl".equals(file.getName())){ file.delete(); } } ZipUtil.zip(editData, zipPath); //上传压缩包 fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName); String url = "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis(); if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){ url = ossUrlPrefix + url; } return ResultData.ok(url); } //没上传过返回源资源 String dataPath = scenePlusExt.getDataSource() + "/caches/tex"; File dataFile = new File(dataPath); if(!dataFile.exists()){ throw new BusinessException(ErrorCode.FAILURE_CODE_3018); } for(File file : dataFile.listFiles()){ if(file.getName().endsWith(".obj") && !"output.house.obj".equals(file.getName()) && !"mesh.obj".equals(file.getName())){ file.delete(); } if(file.getName().endsWith(".mtl") && !"output.house.mtl".equals(file.getName()) && !"mesh.mtl".equals(file.getName())){ file.delete(); } } ZipUtil.zip(dataPath, zipPath); //上传压缩包 fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName); String url = "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis(); if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){ url = ossUrlPrefix + url; } return ResultData.ok(url); } @Override public ScenePro getByNum(String num) { return this.getOne(new LambdaQueryWrapper().eq(ScenePro::getNum, num)); } }