package com.fdkankan.scene.service.impl; import cn.hutool.core.bean.BeanUtil; 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.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fdkankan.common.constant.CommonStatus; import com.fdkankan.common.constant.ErrorCode; import com.fdkankan.common.constant.FileBizType; import com.fdkankan.common.constant.OperationType; import com.fdkankan.common.constant.SceneFrom; import com.fdkankan.common.constant.SceneInfoReqType; import com.fdkankan.common.constant.ServerCode; import com.fdkankan.common.exception.BusinessException; import com.fdkankan.common.util.FileMd5Util; import com.fdkankan.common.util.FileUtils; import com.fdkankan.fyun.constant.FYunTypeEnum; import com.fdkankan.fyun.face.FYunFileServiceInterface; import com.fdkankan.model.constants.ConstantFilePath; import com.fdkankan.model.constants.UploadFilePath; import com.fdkankan.model.utils.ComputerUtil; import com.fdkankan.model.utils.CreateHouseJsonUtil; import com.fdkankan.model.utils.CreateObjUtil; import com.fdkankan.model.utils.SceneUtil; 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.BoxPhotoBean; import com.fdkankan.scene.bean.PointBean; import com.fdkankan.scene.bean.SceneJsonBean; import com.fdkankan.scene.bean.SegmentBean; import com.fdkankan.scene.bean.StyleBean; import com.fdkankan.scene.bean.TagBean; import com.fdkankan.scene.bean.VertexBean; import com.fdkankan.scene.bean.WallBean; import com.fdkankan.scene.entity.CameraDetail; import com.fdkankan.scene.entity.Company; import com.fdkankan.scene.entity.SceneDataDownload; import com.fdkankan.scene.entity.SceneEditControls; import com.fdkankan.scene.entity.SceneEditInfo; import com.fdkankan.scene.entity.SceneEditInfoExt; import com.fdkankan.scene.entity.ScenePlus; import com.fdkankan.scene.entity.ScenePlusExt; import com.fdkankan.scene.mapper.ISceneEditInfoMapper; import com.fdkankan.scene.service.ICameraDetailService; import com.fdkankan.scene.service.ICompanyService; import com.fdkankan.scene.service.ISceneDataDownloadService; import com.fdkankan.scene.service.ISceneEditControlsService; import com.fdkankan.scene.service.ISceneEditInfoExtService; 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.service.ISurveillanceService; import com.fdkankan.scene.vo.BallScreenVideoParamVO; import com.fdkankan.scene.vo.BaseDataParamVO; import com.fdkankan.scene.vo.BaseFileParamVO; import com.fdkankan.scene.vo.BaseSceneParamVO; import com.fdkankan.scene.vo.DeleteFileParamVO; import com.fdkankan.scene.vo.DeleteLinkPanParamVO; import com.fdkankan.scene.vo.DeleteLinkSceneStylesParamVO; import com.fdkankan.scene.vo.DeleteMosaicParamVO; import com.fdkankan.scene.vo.DeleteSidParamVO; import com.fdkankan.scene.vo.DownloadVO; import com.fdkankan.scene.vo.FileNameAndDataParamVO; import com.fdkankan.scene.vo.FileParamVO; import com.fdkankan.scene.vo.RenameCadParamVO; import com.fdkankan.scene.vo.SaveLinkPanParamVO; import com.fdkankan.scene.vo.SceneCheckKeyParamVO; import com.fdkankan.scene.vo.SceneEditControlsVO; import com.fdkankan.scene.vo.SceneEditInfoParamVO; import com.fdkankan.scene.vo.SceneEditInfoVO; import com.fdkankan.scene.vo.SceneInfoParamVO; import com.fdkankan.scene.vo.SceneInfoVO; import com.fdkankan.scene.vo.SurveillanceVO; import com.fdkankan.scene.vo.UploadPanoramaVO; import com.fdkankan.web.response.ResultData; import com.google.common.collect.Lists; import com.google.errorprone.annotations.Var; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; /** *

* 服务实现类 *

* * @author * @since 2022-01-18 */ @Slf4j @RefreshScope @Service public class SceneEditInfoServiceImpl extends ServiceImpl implements ISceneEditInfoService { @Value("${fyun.host}") private String ossUrlPrefix; @Value("${fyun.type}") private String fyunType; @Value("${ecs.checkFile.maxTimes:20}") private int maxCheckTimes; @Value("${ecs.checkFile.waitTime:1000}") private int waitTime; @Autowired private ISceneEditControlsService sceneEditControlsService; @Autowired private ISceneProService sceneProService; @Resource private FYunFileServiceInterface fYunFileService; @Autowired RedisUtil redisUtil; @Autowired private RedisLockUtil redisLockUtil; @Autowired ISceneDataDownloadService sceneDataDownloadService; @Autowired private ISceneEditInfoExtService sceneEditInfoExtService; @Autowired IScenePlusService scenePlusService; @Autowired IScenePlusExtService scenePlusExtService; @Autowired private ISceneUploadService sceneUploadService; @Autowired private ICameraDetailService cameraDetailService; @Autowired private ICompanyService companyService; @Autowired private ISurveillanceService surveillanceService; @Transactional @Override public SceneEditInfoVO saveScene(SceneEditInfoParamVO param) { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } SceneEditInfo sceneEditInfoDb = this.getByScenePlusId(scenePlus.getId()); SceneEditControls sceneEditControlsDb = null; if(Objects.nonNull(sceneEditInfoDb)){ sceneEditControlsDb = sceneEditControlsService.getBySceneEditId(sceneEditInfoDb.getId()); } //用户上传的文件后缀名转小写 this.lowercaseExtName(param); SceneEditInfo sceneEditInfo = BeanUtil.copyProperties(param, SceneEditInfo.class); sceneEditInfo.setScenePlusId(scenePlus.getId()); if(Objects.isNull(sceneEditInfoDb)){ this.save(sceneEditInfo); }else{ sceneEditInfo.setId(sceneEditInfoDb.getId()); sceneEditInfo.setVersion(sceneEditInfoDb.getVersion() + 1); this.updateById(sceneEditInfo); } if(Objects.nonNull(param.getControls())){ SceneEditControls sceneEditControls = BeanUtil.copyProperties(param.getControls(), SceneEditControls.class); sceneEditControls.setEditInfoId(sceneEditInfo.getId()); if(Objects.isNull(sceneEditControlsDb)){ sceneEditControlsService.save(sceneEditControls); }else{ sceneEditControls.setId(sceneEditControlsDb.getId()); sceneEditControlsService.updateById(sceneEditControls); } } SceneEditInfoVO result = BeanUtil.copyProperties(param, SceneEditInfoVO.class); result.setCreateTime(sceneEditInfo.getCreateTime()); return result; } private void lowercaseExtName(SceneEditInfoParamVO sceneEditInfo){ if(StrUtil.isNotEmpty(sceneEditInfo.getMusic())){ String extName = cn.hutool.core.io.FileUtil.extName(sceneEditInfo.getMusic()); if(StrUtil.isNotEmpty(extName)){ sceneEditInfo.setMusic(sceneEditInfo.getMusic().replace(extName, extName.toLowerCase())); } } if(StrUtil.isNotEmpty(sceneEditInfo.getMusicFile())){ String extName = cn.hutool.core.io.FileUtil.extName(sceneEditInfo.getMusicFile()); if(StrUtil.isNotEmpty(extName)){ sceneEditInfo.setMusicFile(sceneEditInfo.getMusicFile().replace(extName, extName.toLowerCase())); } } } @Override public SceneEditInfo getBySceneProId(long sceneProId) { return this.getOne(new LambdaQueryWrapper() .eq(SceneEditInfo::getSceneProId, sceneProId)); } @Override public SceneEditInfo getByScenePlusId(long scenePlusId) { return this.getOne(new LambdaQueryWrapper() .eq(SceneEditInfo::getScenePlusId, scenePlusId)); } @Transactional @Override public ResultData publicScene(SceneEditInfoParamVO param) throws Exception{ String num = param.getNum(); //根据场景码查询数据库 ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num); ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); SceneEditInfoExt sceneEditInfoExt = null; SceneEditControls sceneEditControls = null; if(sceneEditInfo == null){ sceneEditInfo = new SceneEditInfo(); sceneEditInfo.setSceneProId(scenePlus.getId()); }else{ sceneEditControls = sceneEditControlsService.getBySceneEditId(sceneEditInfo.getId()); sceneEditInfoExt = sceneEditInfoExtService.getByEditInfoId(sceneEditInfo.getId()); } if(sceneEditInfoExt == null){ sceneEditInfoExt = new SceneEditInfoExt(); sceneEditInfoExt.setFloorPlanAngle(0f); sceneEditInfoExt.setFloorPlanCompass(0f); } String bucket = scenePlusExt.getYunFileBucket(); //生成sceneJson SceneJsonBean sceneJson = new SceneJsonBean(); BeanUtil.copyProperties(sceneEditInfoExt, sceneJson); BeanUtil.copyProperties(sceneEditInfo, sceneJson); sceneJson.setFloorPlanUpload(JSON.parseArray(sceneEditInfo.getFloorPlanUpload())); SceneEditControlsVO sceneEditControlsVO = BeanUtil.copyProperties(sceneEditControls, SceneEditControlsVO.class); sceneJson.setControls(sceneEditControlsVO); sceneJson.setNum(num); sceneJson.setCreateTime(scenePlus.getCreateTime()); sceneJson.setSceneResolution(scenePlusExt.getSceneResolution()); sceneJson.setSceneFrom(scenePlusExt.getSceneFrom()); sceneJson.setSceneKind(scenePlusExt.getSceneKind()); if(StrUtil.isNotEmpty(scenePlusExt.getVideos())){ sceneJson.setVideos(scenePlusExt.getVideos()); } //发布马赛克列表 sceneJson.setMosaicList(this.getMosaicList(num)); //处理热点数据,生成hot.json this.publicHotData(num, sceneEditInfo, bucket); //发布场景关联相关数据 this.publicLinkSceneData(num, bucket); //发布滤镜数据 this.publicFilterData(num, sceneEditInfoExt.getFilters(), bucket); //发布摄像头数据 this.publicSurveillance(num, sceneEditInfoExt.getSurveillances(), bucket); //本地写sceneJson文件 String localSceneJsonPath = String.format(ConstantFilePath.SCENE_DATA_PATH_V4, num) + "scene.json"; FileUtils.writeFile(localSceneJsonPath, JSON.toJSONString(sceneJson)); //上传sceneJson文件 String sceneJsonPath = String.format(UploadFilePath.DATA_VIEW_PATH+"scene.json", num); fYunFileService.uploadFile(bucket, JSON.toJSONBytes(sceneJson), sceneJsonPath); //sceneJson放入缓存 String key = String.format(RedisKey.SCENE_JSON, num); redisUtil.set(key, JSON.toJSONString(sceneJson)); //删除发布数据中的user目录 String publicUserPath = String.format(UploadFilePath.USER_VIEW_PATH, num); fYunFileService.deleteFile(bucket, publicUserPath); //复制编辑目录到发布目录 String editPath = String.format(UploadFilePath.USER_EDIT_PATH, param.getNum()); String viewPath = String.format(UploadFilePath.USER_VIEW_PATH, param.getNum()); fYunFileService.copyFileBetweenBucket(bucket, editPath, bucket, viewPath); //入库 if(sceneEditInfo.getId() == null){ this.save(sceneEditInfo); }else{ this.updateById(sceneEditInfo); } sceneEditInfoExt.setScenePlusId(scenePlus.getId()); sceneEditInfoExt.setEditInfoId(sceneEditInfo.getId()); sceneEditInfoExtService.saveOrUpdate(sceneEditInfoExt); scenePlus.setTitle(sceneEditInfo.getTitle()); scenePlus.setDescription(sceneEditInfo.getDescription()); scenePlusService.updateById(scenePlus); return ResultData.ok(); } private void publicSurveillance(String num, Integer surveillances, String bucket) throws IOException { String surveillanceJsonPath = String.format(UploadFilePath.USER_EDIT_PATH, num) + "surveillance.json"; if(surveillances == CommonStatus.NO.code().intValue()){ fYunFileService.deleteFile(bucket, surveillanceJsonPath); return; } List surveillanceList = surveillanceService.listSurveillance(num); fYunFileService.uploadFile(bucket, JSON.toJSONString(surveillanceList).getBytes(StandardCharsets.UTF_8), surveillanceJsonPath); } private void publicFilterData(String num, int filters, String bucket) throws IOException { String userEditPath = String.format(UploadFilePath.USER_EDIT_PATH, num); if(filters == CommonStatus.NO.code()){ fYunFileService.deleteFile(bucket, userEditPath + "filter.json"); return; } String key = String.format(RedisKey.SCENE_filter_DATA, num); List list = redisUtil.lGet(key, 0, -1); JSONArray jsonArray = new JSONArray(); list.stream().forEach(str->jsonArray.add(JSON.parseObject(str))); fYunFileService.uploadFile(bucket, JSON.toJSONBytes(jsonArray), userEditPath + "filter.json"); } public void publicLinkSceneData(String num, String bucket) throws IOException { String imgEditPath = String.format(UploadFilePath.IMG_EDIT_PATH, num); String userEditPath = String.format(UploadFilePath.USER_EDIT_PATH, num); String imgViewPath = String.format(UploadFilePath.IMG_VIEW_PATH, num); //生成links.json并上传到发布目录 String linkPanKey = String.format(RedisKey.SCENE_LINKPAN_DATA, num); Map linkPanMap = redisUtil.hmget(linkPanKey); if(CollUtil.isEmpty(linkPanMap)){ return; } JSONArray linkPanArr = new JSONArray(); linkPanMap.values().stream().forEach(linkPan->{ linkPanArr.add(JSON.parseObject(linkPan)); }); String linkScenePath = userEditPath + "links.json"; fYunFileService.uploadFile(bucket, linkPanArr.toString().getBytes(), linkScenePath); //拷贝编辑目录到发布目录 fYunFileService.deleteFile(bucket,imgViewPath + "panorama"); fYunFileService.copyFileBetweenBucket(bucket, imgEditPath + "panorama", bucket, imgViewPath + "panorama"); } private void publicHotData(String sceneNum, SceneEditInfo sceneEditInfo, String bucket) throws IOException { String hotDataKey = String.format(RedisKey.SCENE_HOT_DATA, sceneNum); Map hotMap = redisUtil.hmget(hotDataKey); JSONArray tags = new JSONArray(); if(CollUtil.isNotEmpty(hotMap)){ List tagBeanList = hotMap.entrySet().stream().map(entry -> { JSONObject jsonObject = JSON.parseObject(entry.getValue()); return TagBean.builder() .createTime(jsonObject.getLong("createTime")) .tag(jsonObject).build(); }).collect(Collectors.toList()); //按创建时间倒叙排序 tagBeanList.sort(Comparator.comparingLong(TagBean::getCreateTime).reversed()); //移除createTime字段 tagBeanList.stream().forEach(tagBean -> { tags.add(tagBean.getTag()); }); } String hotJsonPath = String.format(UploadFilePath.USER_EDIT_PATH, sceneNum) + "hot.json"; fYunFileService.uploadFile(bucket, tags.toString().getBytes(), hotJsonPath); this.saveTagsToSceneEditInfo(sceneNum, sceneEditInfo); } @Override public SceneInfoVO getSceneInfo(@Var SceneInfoParamVO param) throws Exception{ SceneInfoReqType sceneInfoReqType = SceneInfoReqType.get(param.getReqType()); switch (sceneInfoReqType){ //如果是编辑页面请求,查数据库 case EDIT: return this.getSceneInfo4Edit(param.getNum()); //如果是查看页面请求,查redis case VIEW: return this.getSceneInfo4View(param.getNum()); } return null; } /** *

编辑页面获取场景详情信息 *

* @author dengsixing * @date 2022/1/21 * @param num * @return com.fdkankan.scene.vo.SceneInfoVO **/ private SceneInfoVO getSceneInfo4Edit(String num) throws Exception{ ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); if(Objects.isNull(sceneEditInfo)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByEditInfoId(sceneEditInfo.getId()); SceneEditControls sceneEditControls = sceneEditControlsService.getBySceneEditId(sceneEditInfo.getId()); SceneInfoVO sceneInfoVO = new SceneInfoVO(); BeanUtil.copyProperties(sceneEditInfoExt, sceneInfoVO); BeanUtil.copyProperties(sceneEditInfo, sceneInfoVO); sceneInfoVO.setFloorPlanUpload(JSON.parseArray(sceneEditInfo.getFloorPlanUpload())); if(Objects.isNull(sceneInfoVO.getFloorPlanAngle())){ sceneInfoVO.setFloorPlanAngle(0f); } if(Objects.isNull(sceneInfoVO.getFloorPlanCompass())){ sceneInfoVO.setFloorPlanCompass(0f); } sceneInfoVO.setControls(BeanUtil.copyProperties(sceneEditControls, SceneEditControlsVO.class)); sceneInfoVO.setNum(num); sceneInfoVO.setCreateTime(scenePlus.getCreateTime()); sceneInfoVO.setSceneResolution(scenePlusExt.getSceneResolution()); sceneInfoVO.setSceneFrom(scenePlusExt.getSceneFrom()); sceneInfoVO.setSceneKind(scenePlusExt.getSceneKind()); sceneInfoVO.setVideos(scenePlusExt.getVideos()); if(CommonStatus.YES.code().equals(sceneEditInfoExt.getMosaic())){ sceneInfoVO.setMosaicList(this.getMosaicList(num)); } this.SortBoxVideos(sceneInfoVO); this.setExtData(sceneInfoVO, scenePlus.getCameraId()); return sceneInfoVO; } private void SortBoxVideos(SceneInfoVO sceneInfoVO){ String boxVideos = sceneInfoVO.getBoxVideos(); if(StrUtil.isEmpty(boxVideos)){ return; } JSONArray boxVideoArr = JSON.parseArray(boxVideos); if(CollUtil.isEmpty(boxVideoArr)){ return; } List tagBeanList = boxVideoArr.stream().map(o -> { JSONObject item = (JSONObject) o; return TagBean.builder() .createTime(item.getLong("createTime")) .tag(item).build(); }).collect(Collectors.toList()); //按创建时间倒叙排序 tagBeanList.sort(Comparator.comparingLong(TagBean::getCreateTime).reversed()); //移除createTime字段 List boxVideoList = tagBeanList.stream().map(tagBean -> { JSONObject tag = tagBean.getTag(); tag.remove("createTime"); return tag; }).collect(Collectors.toList()); sceneInfoVO.setBoxVideos(JSON.toJSONString(boxVideoList)); } /** *

getInfo接口返回字段扩展 *

* @author dengsixing * @date 2022/3/9 **/ private void setExtData(SceneInfoVO sceneInfoVO, Long cameraId) throws Exception{ if(Objects.isNull(cameraId)){ return; } CameraDetail cameraDetail = cameraDetailService.findByCameraId(cameraId); if(Objects.isNull(cameraDetail)){ throw new BusinessException(ErrorCode.FAILURE_CODE_7010); } if(Objects.isNull(cameraDetail.getCompanyId())){ return; } Company company = companyService.getById(cameraDetail.getCompanyId()); if(Objects.isNull(company)){ return; } sceneInfoVO.setDataSync(company.getDataSync()); } /** *

查看页面获取场景详情信息 *

* @author dengsixing * @date 2022/1/21 * @param num * @return com.fdkankan.scene.vo.SceneInfoVO **/ private SceneInfoVO getSceneInfo4View(String num) throws Exception{ //校验场景是否为空、封存、计算中 ScenePlus scenePlus = scenePlusService.checkSceneAvail(num); ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); //访问量+1 scenePlusService.updateViewCount(num); String key = String.format(RedisKey.SCENE_JSON, num); String sceneJson = redisUtil.get(key); SceneInfoVO sceneInfoVO = null; //先查询redis if(StrUtil.isNotEmpty(sceneJson)) { sceneInfoVO = JSON.parseObject(sceneJson, SceneInfoVO.class); sceneInfoVO.setScenePassword(null); if(Objects.isNull(sceneInfoVO.getFloorPlanAngle())){ sceneInfoVO.setFloorPlanAngle(0f); } if(Objects.isNull(sceneInfoVO.getFloorPlanCompass())){ sceneInfoVO.setFloorPlanCompass(0f); } return sceneInfoVO; } //如果redis没找到,从scene.json中获取 String objectName = String.format(ConstantFilePath.SCENE_VIEW_DATA_DATA_SCENEJSON, num); String objectContent = fYunFileService.getFileContent(scenePlusExt.getYunFileBucket(), objectName); log.info("获取scene.json内容,path:{}, content:{}", objectName, objectContent); if(StrUtil.isEmpty(objectContent)) return null; sceneInfoVO = JSON.parseObject(objectContent, SceneInfoVO.class); if(Objects.isNull(sceneInfoVO.getFloorPlanAngle())){ sceneInfoVO.setFloorPlanAngle(0f); } if(Objects.isNull(sceneInfoVO.getFloorPlanCompass())){ sceneInfoVO.setFloorPlanCompass(0f); } redisUtil.set(key, JSON.toJSONString(sceneInfoVO)); sceneInfoVO.setScenePassword(null); return sceneInfoVO; } @Override public ResultData saveCad(BaseDataParamVO param) throws Exception { String num = param.getNum(); ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num); if(Objects.isNull(scenePlus)) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); String editUserPath = String.format(UploadFilePath.USER_EDIT_PATH, num); String localDataPath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num); JSONObject fileInfoJson = JSON.parseObject(param.getData()); Float angel = null; Float compass = null; String floorJsonData = fileInfoJson.getString("floorJsonData"); if(StrUtil.isNotBlank(floorJsonData)){ JSONObject jsonObject = JSON.parseObject(floorJsonData); angel = jsonObject.getFloat("angle"); compass = jsonObject.getFloat("compass"); } //处理户型图数据 //上传floorplan_user.json文件 FileUtils.writeFile(localDataPath + "floorplan_user.json", floorJsonData); fYunFileService.uploadFile(bucket, localDataPath + "floorplan_user.json", editUserPath + "floorplan_user.json"); //写入数据库 Byte floorPlanUser = null; if(StrUtil.isNotEmpty(floorJsonData)){ JSONObject houseTypeJson = CreateHouseJsonUtil .createHouseTypeJsonByUser(localDataPath + "floorplan_user.json"); if(Objects.nonNull(houseTypeJson)) { fYunFileService.uploadFile(bucket, houseTypeJson.toJSONString().getBytes(), editUserPath + "houseType.json"); } floorPlanUser = CommonStatus.YES.code(); } SceneEditInfo sceneEditInfoDb = this.getByScenePlusId(scenePlus.getId()); if(Objects.nonNull(sceneEditInfoDb)){ LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() .setSql("version=version+" + 1) .eq(SceneEditInfo::getId, sceneEditInfoDb.getId()); if(floorPlanUser != null){ updateWrapper.set(SceneEditInfo::getFloorPlanUser, floorPlanUser); } this.update(updateWrapper); }else{ sceneEditInfoDb = new SceneEditInfo(); sceneEditInfoDb.setScenePlusId(scenePlus.getId()); sceneEditInfoDb.setFloorPlanUser(floorPlanUser); this.save(sceneEditInfoDb); } SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByEditInfoId(sceneEditInfoDb.getId()); if(Objects.isNull(sceneEditInfoExt)){ sceneEditInfoExt = new SceneEditInfoExt(); sceneEditInfoExt.setEditInfoId(sceneEditInfoDb.getId()); } if(Objects.nonNull(angel)){ sceneEditInfoExt.setFloorPlanAngle(angel); } if(Objects.nonNull(compass)){ sceneEditInfoExt.setFloorPlanCompass(compass); } sceneEditInfoExtService.saveOrUpdate(sceneEditInfoExt); return ResultData.ok(); } @Override public ResultData resetCad(String num) throws IOException { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num); if(Objects.isNull(scenePlus)) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); ScenePlusExt scenePlusExt = scenePlusExtService .getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); String path = scenePlusExt.getDataSource(); //户型图上传 String userEditPath = UploadFilePath.USER_EDIT_PATH + "floor-cad-%s.%s"; String floorCadPath = path + "/results/floorplan_cad"; List floorCadList = FileUtils.getFileList(floorCadPath); if(CollUtil.isNotEmpty(floorCadList)){ floorCadList.stream().forEach(str->{ String substring = str.substring(str.lastIndexOf(File.separator) + 1); String[] arr = substring.split("floor"); String[] arr2 = arr[1].split("\\."); fYunFileService.uploadFile(bucket, str, String.format(userEditPath, num, arr2[0], arr2[1])); }); } //根据floorplan_cad.json生成houseType.json this.uploadHouseTypeJson(num, bucket); SceneEditInfo sceneEditInfoDb = this.getByScenePlusId(scenePlus.getId()); SceneEditInfoExt sceneEditInfoExt = null; if(Objects.nonNull(sceneEditInfoDb)){ LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() .setSql("version=version+" + 1) .set(SceneEditInfo::getFloorPlanUser, CommonStatus.NO.code()) .eq(SceneEditInfo::getId, sceneEditInfoDb.getId()); this.update(updateWrapper); sceneEditInfoExt = sceneEditInfoExtService.getByEditInfoId(sceneEditInfoDb.getId()); }else{ sceneEditInfoDb = new SceneEditInfo(); sceneEditInfoDb.setScenePlusId(scenePlus.getId()); sceneEditInfoDb.setFloorPlanUser(CommonStatus.NO.code()); this.save(sceneEditInfoDb); } if(Objects.isNull(sceneEditInfoExt)){ sceneEditInfoExt = new SceneEditInfoExt(); sceneEditInfoDb.setScenePlusId(scenePlus.getId()); sceneEditInfoExt.setEditInfoId(sceneEditInfoDb.getId()); } sceneEditInfoExt.setFloorPlanAngle(0f); sceneEditInfoExt.setFloorPlanCompass(0f); sceneEditInfoExtService.saveOrUpdate(sceneEditInfoExt); return ResultData.ok(); } private void uploadHouseTypeJson(String num, String bucket) throws IOException { JSONObject result = new JSONObject(); result.put("name", "houseType.json"); result.put("version", "2.1"); String floorplanCadPath = String.format(UploadFilePath.DATA_VIEW_PATH, num) + "floorplan_cad.json"; String floorcadStr = fYunFileService.getFileContent(bucket, floorplanCadPath); JSONObject floorcadObj = JSON.parseObject(floorcadStr); JSONArray floors = floorcadObj.getJSONArray("floors"); JSONArray targetFloors = new JSONArray(); result.put("floors", targetFloors); for(int i = 0; i < floors.size(); i++){ JSONObject floor = (JSONObject)floors.get(i); JSONArray[] pointsAndWalls = this.createHouseTypeJsonHandler(floor); JSONArray points = pointsAndWalls[0]; JSONArray walls = pointsAndWalls[1]; JSONObject targetFloor = new JSONObject(); targetFloor.put("points", points); targetFloor.put("walls", walls); targetFloors.add(targetFloor); } String hourseTypeJsonPath = String.format(UploadFilePath.USER_EDIT_PATH, num) + "houseType.json"; fYunFileService.uploadFile(bucket, result.toJSONString().getBytes(), hourseTypeJsonPath); } private JSONArray[] createHouseTypeJsonHandler(JSONObject floor){ JSONArray[] result = new JSONArray[2]; //处理点 Map vertexMap = new HashMap<>(); Map pointMap = new HashMap<>(); Map vpMap = new HashMap<>(); JSONArray vertexArr = floor.getJSONArray("vertex-xy"); for(int i = 0; i < vertexArr.size(); i++){ Object o = vertexArr.get(i); VertexBean vertexBean = JSON.parseObject(JSON.toJSONString(o), VertexBean.class); Integer vertexId = vertexBean.getId(); vertexMap.put(vertexId, vertexBean); String pointId = "Point" + i; pointMap.put(pointId, PointBean.builder().vectorId(pointId).x(vertexBean.getX()).y(vertexBean.getY()).build()); vpMap.put(vertexId, pointId); } //处理墙 Map segmentMap = new HashMap<>(); Map wallMap = new HashMap<>(); Map swMap = new HashMap<>(); JSONArray segmentArr = floor.getJSONArray("segment"); Map startMap = new HashMap<>(); Map endMap = new HashMap<>(); for(int i = 0; i < segmentArr.size(); i++){ Object o = segmentArr.get(i); SegmentBean segmentBean = JSON.parseObject(JSON.toJSONString(o), SegmentBean.class); String startPointId = vpMap.get(segmentBean.getA()); String endPointId = vpMap.get(segmentBean.getB()); segmentBean.setStartPointId(startPointId); segmentBean.setEndPointId(endPointId); Integer segmentId = segmentBean.getId(); segmentMap.put(segmentId, segmentBean); String wallId = "Wall" + i; WallBean wallBean = WallBean.builder() .vectorId(wallId) .start(segmentBean.getStartPointId()) .end(segmentBean.getEndPointId()) .children(new String[]{}) .width(0.2d) .build(); wallMap.put(wallId, wallBean); startMap.put(wallBean.getStart(), wallBean.getVectorId()); endMap.put(wallBean.getEnd(), wallBean.getVectorId()); swMap.put(segmentId, wallId); } Collection pointBeans = pointMap.values(); for (PointBean pointBean : pointBeans) { Map parent = new HashMap<>(); String startParent = startMap.get(pointBean.getVectorId()); String endParent = endMap.get(pointBean.getVectorId()); parent.put(startParent, "start"); parent.put(endParent, "end"); pointBean.setParent(parent); } JSONArray pointArr = JSON.parseArray(JSON.toJSONString(pointBeans)); result[0] = pointArr; Collection wallBeans = wallMap.values(); JSONArray wallArr = JSON.parseArray(JSON.toJSONString(wallBeans)); result[1] = wallArr; return result; } @Override public ResultData renameCad(RenameCadParamVO param) throws IOException { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); String key = String.format(UploadFilePath.DATA_VIEW_PATH, param.getNum()) + "floorplan_cad.json"; String floorplanCadJson = fYunFileService.getFileContent(bucket, key); if(StrUtil.isEmpty(floorplanCadJson)){ return ResultData.ok(); } Map renameMap = new HashMap<>(); param.getFloors().stream().forEach(floor->{ renameMap.put(floor.getSubgroup(), floor.getName()); }); JSONObject jsonObject = JSON.parseObject(floorplanCadJson); if(Objects.isNull(jsonObject)){ return ResultData.ok(); } JSONArray floorArr = jsonObject.getJSONArray("floors"); if(CollUtil.isEmpty(floorArr)){ return ResultData.ok(); } for (Object o : floorArr) { JSONObject item = (JSONObject)o; int subgroup = item.getIntValue("subgroup"); String name = renameMap.get(subgroup); if(StrUtil.isEmpty(name)){ continue; } item.put("name", name); } fYunFileService.uploadFile(bucket, jsonObject.toJSONString().getBytes(), key); return ResultData.ok(); } private JSONObject updateFloorName(String sourceFloors, String targeFloors){ JSONArray sourceFloorsJson = JSON.parseArray(sourceFloors); JSONObject targeFloorsJson = JSONObject.parseObject(targeFloors); JSONArray array = targeFloorsJson.getJSONArray("floors"); for (int i = 0; i < array.size(); ++i) { JSONObject targetFloor = array.getJSONObject(i); int targetId = targetFloor.getIntValue("id"); for (int j = 0; j < sourceFloorsJson.size(); ++j) { JSONObject floor = sourceFloorsJson.getJSONObject(j); int id = floor.getIntValue("id"); String name = floor.getString("name"); if (targetId != id) continue; targetFloor.put("name", name); } } return targeFloorsJson; } @Override public void upgradeVersionById(Long id) { this.update(new LambdaUpdateWrapper() .setSql("version=version+" + 1) .eq(SceneEditInfo::getId, id)); } @Override public void upgradeVersionAndImgVersionById(Long id) { this.update(new LambdaUpdateWrapper() .setSql("version=version + " + 1) .setSql("img_version=img_version + " + 1) .eq(SceneEditInfo::getId, id)); } @Override public void upgradeSceneJsonVersion(String num, int version, Integer imgVersion, String bucket) throws IOException { //更新redis缓存版本号 String key = String.format(RedisKey.SCENE_JSON, num); String sceneJson = redisUtil.get(key); if(StrUtil.isNotEmpty(sceneJson)){ SceneJsonBean sceneJsonBean = JSON.parseObject(sceneJson, SceneJsonBean.class); sceneJsonBean.setVersion(version); if(Objects.nonNull(imgVersion)){ sceneJsonBean.setImgVersion(imgVersion + 1); } redisUtil.set(key, JSON.toJSONString(sceneJsonBean)); } //更新oss scene.json版本号 String sceneJsonPath = String.format(UploadFilePath.DATA_VIEW_PATH, num) + "scene.json"; sceneJson= fYunFileService.getFileContent(bucket, sceneJsonPath); if(StrUtil.isNotEmpty(sceneJson)){ SceneJsonBean sceneJsonBean = JSON.parseObject(sceneJson, SceneJsonBean.class); sceneJsonBean.setVersion(version); if(Objects.nonNull(imgVersion)){ sceneJsonBean.setImgVersion(imgVersion + 1); } fYunFileService.uploadFile(bucket, JSON.toJSONString(sceneJsonBean).getBytes(StandardCharsets.UTF_8), sceneJsonPath); } } @Override public ResultData uploadPanorama(String num, MultipartFile file) throws Exception { if(!file.getOriginalFilename().endsWith(".zip") && !file.getOriginalFilename().endsWith(".jpg")){ throw new BusinessException(ErrorCode.FAILURE_CODE_7007, "jpg或者zip"); } 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 = "F:\\test"; String path = scenePlusExt.getDataSource(); //全景图计算根目录 String target = path + "_images"; //解压缩文件存放目录 String targetImagesPath = target + "/extras/images/"; //压缩文件保存目录 String zipTargetFilePath = targetImagesPath + file.getOriginalFilename(); //判断全景图缓存是否存在,如果不存在,从计算目录中拷贝到缓存目录 this.cachePanorama(path, num); //先删除本地文件 FileUtils.deleteDirectory(targetImagesPath); File targetFile = new File(zipTargetFilePath); if(!targetFile.getParentFile().exists()){ targetFile.getParentFile().mkdirs(); } file.transferTo(targetFile); //如果是压缩包上传,需要解压缩 if(file.getOriginalFilename().endsWith(".zip")){ //解压zip包 ZipUtil.unzip(zipTargetFilePath,targetImagesPath); //删除压缩包 FileUtils.delFile(zipTargetFilePath); } //判断文件夹目录结构,图片必须放在压缩包根目录下,不支持空文件夹或其他格式文件上传 File[] files = new File(targetImagesPath).listFiles(); Arrays.stream(files).forEach(item->{ if(item.isDirectory()){ throw new BusinessException(ErrorCode.FAILURE_CODE_7018); } }); //获取解压后的文件列表 List uploadFileList = FileUtils.getFileList(targetImagesPath); if(CollUtil.isEmpty(uploadFileList)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5062); } //判断是否有可用的jpg文件 boolean existJpg = false; if(CollUtil.isNotEmpty(uploadFileList)){ existJpg = uploadFileList.stream().anyMatch(str -> { if(str.endsWith(".jpg")){ return true; } return false; }); } if(!existJpg){ throw new BusinessException(ErrorCode.FAILURE_CODE_5062); } //比对图片列表,不存在的要返回名称集合 String visionPath = path + "/results/vision.txt"; List panoramaImageList = SceneUtil.getPanoramaImageList(visionPath); List notExistFileList = uploadFileList.stream().filter(filePath -> { filePath = filePath.substring(filePath.lastIndexOf(File.separator) + 1); if(panoramaImageList.contains(filePath)){ return false; } return true; }).collect(Collectors.toList()); if(CollUtil.isNotEmpty(notExistFileList)){ //删除错误文件 notExistFileList.parallelStream().forEach(filePath->{ FileUtils.deleteFile(filePath); }); } //判断成功的图片,如果成功图片为0,就直接返回,不需要执行算法 uploadFileList = FileUtils.getFileList(targetImagesPath); if(CollUtil.isEmpty(uploadFileList)){ if(CollUtil.isNotEmpty(notExistFileList)){ notExistFileList = notExistFileList.stream().map(filePath -> { return filePath.substring(filePath.lastIndexOf(File.separator) + 1); }).collect(Collectors.toList()); } return ResultData.ok(new UploadPanoramaVO(0, notExistFileList)); } //上传 Map map = new HashMap<>(); String imgViewPath = String.format(UploadFilePath.IMG_VIEW_PATH, num); fYunFileService.downloadFile(bucket, imgViewPath + "vision.modeldata", target + File.separator + "extras" + File.separator + "vision.modeldata"); CreateObjUtil.convertVisionmodeldataToTxt(target + File.separator + "extras" + File.separator + "vision.modeldata", target + File.separator + "extras" + File.separator + "vision.txt"); FileUtils.copyFile(path + File.separator + "data.json", target + File.separator+"data.json", true); FileUtils.copyFile(path + File.separator + "project.json", target + File.separator+"project.json", true); //data.json增加extras为执行重建算法 String data = FileUtils.readFile(target + File.separator+"data.json"); if(data != null){ JSONObject floorplanJson = new JSONObject(); floorplanJson.put("has_source_images", true); floorplanJson.put("has_vision_txt", true); JSONObject dataJson = JSONObject.parseObject(data); dataJson.put("extras", floorplanJson); dataJson.put("split_type", "SPLIT_V8"); //V5表示不需要生成high,low文件 String skyboxType = "SKYBOX_V6"; if(scenePlusExt.getSceneScheme() == 11){ skyboxType = "SKYBOX_V7"; } //sceneScheme为3切成瓦片图 if(scenePlusExt.getSceneScheme() == 3){ if("4k".equals(scenePlusExt.getSceneResolution())){ skyboxType = "SKYBOX_V14"; }else{ skyboxType = "SKYBOX_V13"; } } dataJson.put("skybox_type", skyboxType); FileUtils.writeFile(target + File.separator+"data.json", new String(dataJson.toString().getBytes(), "UTF-8")); } if(new File(target + File.separator + "capture").exists()){ new File(target + File.separator + "capture").delete(); } if(new File(target + File.separator + "results").exists()){ FileUtils.delAllFile(target + File.separator + "results"); } if(FYunTypeEnum.AWS.code().equals(fyunType)){ //亚马逊保持旧方式,超链接capture CreateObjUtil.createSoftConnection(path + File.separator + "capture", target + File.separator + "capture"); } fYunFileService.downloadFile(ConstantFilePath.OSS_PREFIX + path.replace(ConstantFilePath.BUILD_MODEL_PATH, "") + "/data.fdage", target + File.separator + "capture/data.fdage"); CreateObjUtil.build3dModel(target , "1"); String uploadJsonPath= target + File.separator + "results" +File.separator+"upload.json"; Thread.sleep(2000); 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"); } if(array == null){ throw new BusinessException(ErrorCode.FAILURE_CODE_7013); } JSONObject fileJson = null; String fileName = ""; for(int i = 0, len = array.size(); i < len; i++) { fileJson = array.getJSONObject(i); fileName = fileJson.getString("file"); //文件不存在抛出异常 if (!new File(target + File.separator + "results" + File.separator + fileName) .exists()) { throw new Exception( target + File.separator + "results" + File.separator + fileName + "文件不存在"); } //high文件夹 if (fileJson.getIntValue("clazz") == 3) { map.put(target + File.separator + "results" + File.separator + fileName, imgViewPath + "pan/high/" + fileName.replace("high/", "")); continue; } //low文件夹 if (fileJson.getIntValue("clazz") == 4) { map.put(target + File.separator + "results" + File.separator + fileName, imgViewPath + "pan/low/" + fileName.replace("low/", "")); continue; } //tiles文件夹,亚马逊没有裁剪图片api,不需要上传4k图 if (fileJson.getIntValue("clazz") == 5) { map.put(target + File.separator + "results" + File.separator + fileName, imgViewPath + fileName); continue; } //tiles文件夹,亚马逊瓦片图 if (fileJson.getIntValue("clazz") == 7) { map.put(target + File.separator + "results" + File.separator + fileName, imgViewPath + fileName); continue; } } if(map.size()>0) { fYunFileService.uploadMulFiles(bucket, map); } //拷贝修改后的全景图到缓存目录 String cachedImagesPath = String.format(ConstantFilePath.SCENE_CACHE_IMAGES, num); uploadFileList.stream().forEach(srcPath->{ cn.hutool.core.io.FileUtil.copy(srcPath, srcPath.replace(targetImagesPath, cachedImagesPath), true); }); //更新数据库版本号 SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); this.upgradeVersionAndImgVersionById(sceneEditInfo.getId()); //更新scenejson缓存和oss文件版本号 this.upgradeSceneJsonVersion(num, sceneEditInfo.getVersion() + 1, sceneEditInfo.getImgVersion() + 1, bucket); //如果部分成功,则需要返回成功数量和失败列表 if(CollUtil.isNotEmpty(notExistFileList)){ notExistFileList = notExistFileList.stream().map(filePath -> { return filePath.substring(filePath.lastIndexOf(File.separator) + 1); }).collect(Collectors.toList()); return ResultData.ok(new UploadPanoramaVO(uploadFileList.size(), notExistFileList)); } return ResultData.ok(); } @Override public ResultData downloadPanorama(FileParamVO param) throws Exception { String num = param.getNum(); String fileName = param.getFileName(); ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); String path = scenePlusExt.getDataSource(); String cachePath = String.format(ConstantFilePath.SCENE_CACHE, num); String imgCachePath = String.format(UploadFilePath.IMG_CACHES_PATH, num); String localImagesPath = String.format(ConstantFilePath.SCENE_CACHE_IMAGES, num); String cacheFormat = "downloads/scene/%s/caches/"; String cacheImageFormat = "downloads/scene/%s/caches/images/"; //判断全景图缓存是否存在,如果不存在,从计算目录中拷贝到缓存目录 this.cachePanorama(scenePlusExt.getDataSource(), num); String url = null; String downloadName = null; //如果入参文件名不为空,则是单个文件下载,不需要打包 if(StrUtil.isNotEmpty(fileName)){ //如果是单张图片,直接提供oss url String localFilePath = localImagesPath + fileName; String ossFilePath = imgCachePath + fileName; if(!cn.hutool.core.io.FileUtil.exist(localFilePath)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5063); } fYunFileService.uploadFile(bucket, localFilePath, ossFilePath); url = ossFilePath; if(!FYunTypeEnum.LOCAL.code().equals(this.fyunType)){ url = ossUrlPrefix + url; } downloadName = fileName; }else{ if(!cn.hutool.core.io.FileUtil.exist(localImagesPath) || cn.hutool.core.io.FileUtil.isDirEmpty(new File(localImagesPath))){ throw new BusinessException(ErrorCode.FAILURE_CODE_5063); } downloadName = num + "_images.zip"; long start = Calendar.getInstance().getTimeInMillis(); //打包 String zipPath = cachePath + downloadName; ZipUtil.zip(localImagesPath, zipPath); long end1 = Calendar.getInstance().getTimeInMillis(); log.info("打包耗时:" + (end1 - start)); //上传压缩包 fYunFileService.uploadFileByCommand(bucket, zipPath, String.format(cacheFormat, num) + downloadName); url = String.format(cacheFormat, num) + downloadName; if(!FYunTypeEnum.LOCAL.code().equals(this.fyunType)){ url = ossUrlPrefix + url; } long end2 = Calendar.getInstance().getTimeInMillis(); log.info("上传耗时:" + (end2 - end1)); //删除本地压缩包 // FileUtils.deleteFile(zipPath); } Map map = new HashMap<>(); map.put("fileUrl", url + "?t=" + System.currentTimeMillis()); map.put("fileName", downloadName); return ResultData.ok(map); } private void cachePanorama(String dataSource, String num){ String cachedImagesPath = String.format(ConstantFilePath.SCENE_CACHE_IMAGES, num); if(FileUtil.exist(cachedImagesPath) && CollUtil.isNotEmpty(FileUtil.listFileNames(cachedImagesPath))){ return; } //将全景图缓存到缓存目录 List imagesList = FileUtil.listFileNames(dataSource + "/caches/images"); //先清除旧的全景图 cn.hutool.core.io.FileUtil.del(cachedImagesPath); String visionPath = dataSource + "/results/vision.txt"; List panoramaImageList = SceneUtil.getPanoramaImageList(visionPath); imagesList.stream().forEach(fileName -> { if (panoramaImageList.contains(fileName)) { String srcPath = dataSource + "/caches/images/" + fileName; String targetPath = cachedImagesPath + fileName; log.info("源文件:{}, 目标文件:{}", srcPath, targetPath); cn.hutool.core.io.FileUtil.copy(srcPath, targetPath, true); } }); } @Override public void saveTagsToSceneEditInfo(String num, SceneEditInfo sceneEditInfo){ //查询缓存是否包含热点数据 String key = String.format(RedisKey.SCENE_HOT_DATA, num); Map allTagsMap = redisUtil.hmget(key); boolean hashTags = false; for (Entry tagMap : allTagsMap.entrySet()) { if(StrUtil.isEmpty(tagMap.getValue())){ continue; } hashTags = true; break; } //更改热点状态 sceneEditInfo.setTags(hashTags ? CommonStatus.YES.code() : CommonStatus.NO.code()); //version 是空的代表 sceneEditInfo记录集不存在 if(sceneEditInfo.getVersion() != null){ sceneEditInfo.setVersion(sceneEditInfo.getVersion() + 1); } } @Override public ResultData saveVideoBox(FileNameAndDataParamVO param) throws Exception { JSONObject boxVideo = JSONObject.parseObject(param.getData()); String sid = boxVideo.getString("sid"); if(StrUtil.isEmpty(sid)){ throw new BusinessException(ErrorCode.PARAM_REQUIRED); } ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); //转换视频格式 this.transferToFlv(param.getNum(), param.getFileName(), bucket); //生成boxVideos数据 String boxVideos = this.createBoxVideos(param.getNum(), sid, boxVideo, sceneEditInfo, OperationType.ADDORUPDATE.code()); //更新数据库 this.updateBoxVideos(sceneEditInfo, scenePlus.getId(), boxVideos); return ResultData.ok(); } @Override public ResultData deleteVideoBox(DeleteSidParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); //根据sid移除json String boxVideos = this.createBoxVideos(param.getNum(), param.getSid(), null, sceneEditInfo, OperationType.DELETE.code()); //写数据库 this.updateBoxVideos(sceneEditInfo,scenePlus.getId(),boxVideos); return ResultData.ok(); } @Override public ResultData saveBoxPhoto(BaseDataParamVO param) throws Exception { JSONObject boxPhoto = JSONObject.parseObject(param.getData()); String sid = boxPhoto.getString("sid"); if(StrUtil.isEmpty(sid)){ throw new BusinessException(ServerCode.PARAM_REQUIRED, sid); } ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); //生成boxVideos数据 String boxPhotos = this.createBoxPhotos(param.getNum(), sid, boxPhoto, sceneEditInfo, OperationType.ADDORUPDATE.code()); //更新数据库 this.updateBoxPhotos(sceneEditInfo, boxPhotos); return ResultData.ok(); } @Override public ResultData deleteBoxPhoto(DeleteSidParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); //根据sid移除json String boxPhotos = this.createBoxPhotos(param.getNum(), param.getSid(), null, sceneEditInfo, OperationType.DELETE.code()); //写数据库 this.updateBoxPhotos(sceneEditInfo, boxPhotos); return ResultData.ok(); } @Override public DownloadVO downloadBallScreenVideo(BallScreenVideoParamVO param) { String videoPath = String.format(UploadFilePath.VIDEOS_VIEW_PATH, param.getNum()) + param.getFileName(); String url = videoPath + "?t=" + System.currentTimeMillis(); if(!FYunTypeEnum.LOCAL.code().equals(this.fyunType)){ url = ossUrlPrefix + url; } return DownloadVO.builder() .fileName(param.getFileName()) .url(url) .build(); } @Override public ResultData uploadBallScreenVideo(String num, String fileName, MultipartFile file) throws Exception { //校验格式 if(!fileName.endsWith(".mp4")){ throw new BusinessException(ErrorCode.FAILURE_CODE_7016); } String videosViewPath = String.format(UploadFilePath.VIDEOS_VIEW_PATH, num); 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(); //校验文件名 String videos = scenePlusExt.getVideos(); if(StrUtil.isEmpty(videos)){ throw new BusinessException(ErrorCode.FAILURE_CODE_7012); } JSONObject videosJson = JSON.parseObject(videos); JSONArray dataArr = videosJson.getJSONArray("data"); boolean exists = false; for (Object o : dataArr) { JSONObject jsonObject = (JSONObject)o; if(jsonObject.getString("id").equals(fileName.replace(".mp4", ""))){ exists = true; break; } } if(!exists){ throw new BusinessException(ErrorCode.FAILURE_CODE_7012); } if(path != null && !"".equals(path) && path.startsWith("http")){ path = ConstantFilePath.BUILD_MODEL_PATH + File.separator + path.split("/")[path.split("/").length - 2]; } String target = path + "_images"; String filePath = target + File.separator + "extras/video" + File.separator + fileName; File targetFile = new File(filePath); if(!targetFile.getParentFile().exists()){ targetFile.getParentFile().mkdirs(); } //保存视频到本地 file.transferTo(targetFile); File video = new File(target + File.separator + "extras/video"); //如果文件夹或者文件不存在,跳出 if(!video.exists() || video.listFiles() == null || video.listFiles().length == 0){ throw new BusinessException(ErrorCode.FAILURE_CODE_7009); } for(String videoName : video.list()){ log.info("球幕视频名称:{}", videoName); fYunFileService.uploadFile(bucket, target + File.separator + "extras/video/" + videoName,videosViewPath + videoName); CreateObjUtil.mp4ToFlv(target + File.separator + "extras/video/" + videoName, target + File.separator + "extras/video/" + videoName.replace("mp4", "flv")); fYunFileService.uploadFile(bucket, target + File.separator + "extras/video/" + videoName.replace("mp4", "flv"),videosViewPath + videoName.replace("mp4", "flv")); //覆盖原始视频资源 FileUtils.copyFile(target + File.separator + "extras/video/" + videoName, path + File.separator + "caches/videos/" + videoName, true); } FileUtils.deleteDirectory(target + File.separator + "extras/video/"); SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); this.upgradeVersionById(sceneEditInfo.getId()); //更新scenejson缓存和oss文件版本号 this.upgradeSceneJsonVersion(num, sceneEditInfo.getVersion() + 1, null, bucket); return ResultData.ok(); } @Override public void upgradeVersionToSceneJson(String num, String bucket) throws Exception{ String key = String.format(RedisKey.SCENE_JSON, num); String objectName = String.format(ConstantFilePath.SCENE_VIEW_DATA_DATA_SCENEJSON, num); String objectContent = fYunFileService.getFileContent(bucket, objectName); SceneJsonBean sceneJsonBean = JSON.parseObject(objectContent, SceneJsonBean.class); sceneJsonBean.setVersion(sceneJsonBean.getVersion() +1); fYunFileService.uploadFile(bucket, JSON.toJSONString(sceneJsonBean).getBytes(), objectName); redisUtil.set(key, JSON.toJSONString(sceneJsonBean)); } @Override public ResultData sceneSync(String num, String type, String floorPlanJson, String ajkJson, String cameraJson, MultipartFile[] files) throws Exception { String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString(); String lockKey = String.format(RedisLockKey.LOCK_SCENE_SYNC, num); Boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_2_HOUR); if(!lock){ throw new BusinessException(ErrorCode.SYSTEM_BUSY); } try { 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(); //更新scene.json文件 String strsceneInfos = FileUtils.readFile(ConstantFilePath.SCENE_PATH + "data" + File.separator + "data" + num + File.separator + "scene.json"); if(strsceneInfos == null) new File(ConstantFilePath.SCENE_PATH + "data" + File.separator + "data" + num + File.separator + "scene.json").createNewFile(); String path = scenePlusExt.getDataSource();// /mnt/data/0662c5389/831989883441512448/4898cab04f8c_202104141602356060/ if(path != null && !"".equals(path) && path.startsWith("http")){ path = ConstantFilePath.BUILD_MODEL_PATH + File.separator + path.split("/")[path.split("/").length - 2]; } String target = path + "_ajk"; // /mnt/data/0662c5389/831989883441512448/4898cab04f8c_202104141602356060_ajk File editPath = new File(target); if(!editPath.exists()){ editPath.mkdirs(); } //创建文件夹软连接并且复制data.json和project.json if(new File(target + File.separator + "capture").exists()){ new File(target + File.separator + "capture").delete(); } if(new File(target + File.separator + "caches").exists()){ //删除link new File(target + File.separator + "caches" + File.separator + "images").delete(); //删除所有文件 FileUtils.delAllFile(target + File.separator + "caches"); } if(new File(target + File.separator + "results").exists()){ FileUtils.delAllFile(target + File.separator + "results"); } //创建文件夹,并link文件夹 new File(target + File.separator + "caches").mkdirs(); CreateObjUtil.createSoftConnection(path + File.separator + "capture", target + File.separator + "capture"); if(new File(path + File.separator + "caches" + File.separator + "images").exists()){ CreateObjUtil.createSoftConnection(path + File.separator + "caches" + File.separator + "images", target + File.separator + "caches" + File.separator + "images"); } FileUtils.copyFile(path + File.separator + "data.json", target + File.separator+"data.json", true); FileUtils.copyFile(path + File.separator + "project.json", target + File.separator+"project.json", true); //data.json增加extras为执行重建算法 String project = FileUtils.readFile(target + File.separator+"project.json"); if(project != null){ JSONObject projectJson = JSONObject.parseObject(project); projectJson.put("parent", projectJson.get("uuid")); projectJson.put("uuid", UUID.randomUUID().toString()); projectJson.put("time", System.currentTimeMillis()); FileUtils.writeFile(path + File.separator + "project.json", projectJson.toString()); } String data = FileUtils.readFile(target + File.separator+"data.json"); if(data != null){ JSONObject floorplanJson = new JSONObject(); floorplanJson.put("has_floor_ajk_json", true); floorplanJson.put("has_vision_txt", true); floorplanJson.put("has_floorplan_json", true); JSONObject dataJson = JSONObject.parseObject(data); dataJson.put("extras", floorplanJson); //V5表示不需要生成high,low文件 dataJson.put("skybox_type", "SKYBOX_V8"); dataJson.put("split_type", "SPLIT_V10"); FileUtils.writeFile(target + File.separator+"data.json", new String(dataJson.toString().getBytes(), "UTF-8")); } //文件上传的位置可以自定义 log.info("画墙重建模型开始"); File targetFile = new File(target + File.separator + "extras" + File.separator + "floor_ajk.json"); if(!targetFile.getParentFile().exists()){ targetFile.getParentFile().mkdirs(); } if(targetFile.exists()){ FileUtils.deleteFile(target + File.separator + "extras" + File.separator + "floor_ajk.json"); } // 保存 FileUtils.writeFile(target + File.separator + "extras" + File.separator + "floorplan.json", new String(floorPlanJson.getBytes(), "UTF-8")); FileUtils.writeFile(target + File.separator + "extras" + File.separator + "floor_ajk.json", new String(ajkJson.getBytes(), "UTF-8")); FileUtils.writeFile(ConstantFilePath.SCENE_PATH+"data"+File.separator+"data"+num + File.separator + "floor_ajk.json", new String(ajkJson.getBytes(), "UTF-8")); FileUtils.writeFile(target + File.separator + "extras" + File.separator + "vision.txt", new String(cameraJson.getBytes(), "UTF-8")); FileUtils.writeFile(ConstantFilePath.SCENE_PATH+"data"+File.separator+"data"+num + File.separator + "camera.json", new String(cameraJson.getBytes(), "UTF-8")); for(int i = 0; i < files.length; i ++){ File cadImg = new File(target + File.separator + "extras" + File.separator + "Floorplans/" + files[i].getOriginalFilename()); if(!cadImg.getParentFile().exists()){ cadImg.getParentFile().mkdirs(); } if(cadImg.exists()) { cadImg.delete(); } files[i].transferTo(cadImg); } //下载封面图 FileUtils.downLoadFromUrl(scenePlusExt.getThumb() + "?t=" + System.currentTimeMillis(), "Cover.png", target + File.separator + "extras" + File.separator + "CoverImage"); //转换成jpg FileUtils.pngToJpg(target + File.separator + "extras" + File.separator + "CoverImage/Cover.png", target + File.separator + "extras" + File.separator + "CoverImage/Cover.jpg"); //安居客算法运行 log.info("安居客算法:开始建模——"+num); CreateObjUtil.build3dModelOld(target , "1"); String uploadJsonPath = target + File.separator + "results" + File.separator + "upload.json"; log.info("uploadJsonPath=" + uploadJsonPath); //因为共享目录有延迟,这里循环检测算法是否计算完毕3次,每次隔五秒 boolean exist = ComputerUtil.checkComputeCompleted(uploadJsonPath, maxCheckTimes, waitTime); if(!exist){ throw new BusinessException(ErrorCode.FAILURE_CODE_7017); } String zipPath = target + File.separator + "results/" + num + ".zip"; new File(zipPath).delete(); ZipUtil.zip(target + File.separator + "results/ajk/", zipPath); // String command = "bash /opt/ossutil/gzip.sh " + zipPath.replace(".zip", "") + " " + target + File.separator + "results/ajk/"; // log.info("压缩文件:" + command); // CreateObjUtil.callshell(command); if(!new File(zipPath).exists()){ return ResultData.error(ErrorCode.FAILURE_CODE_5043); } String fileMD5 = FileMd5Util.getFileMD5(new File(zipPath)); fYunFileService.uploadFileByCommand(bucket, zipPath, "data_download/" + num + ".zip"); SceneDataDownload sceneDataDownload = sceneDataDownloadService.findBySceneNum(num); if(sceneDataDownload == null){ String downloadPath = "data_download/" + num + ".zip"; if(!FYunTypeEnum.LOCAL.code().equals(this.fyunType)){ downloadPath = ossUrlPrefix + downloadPath; } sceneDataDownload = new SceneDataDownload(); sceneDataDownload.setSceneNum(num); sceneDataDownload.setDownloadPath(downloadPath); sceneDataDownload.setFileMd5(fileMD5); sceneDataDownloadService.save(sceneDataDownload); return ResultData.ok(); } sceneDataDownload.setFileMd5(fileMD5); sceneDataDownload.setUpdateTime(Calendar.getInstance().getTime()); sceneDataDownloadService.updateById(sceneDataDownload); }catch (Exception e){ log.error("画墙重建模型失败...", e); throw new BusinessException(ErrorCode.FAILURE_CODE_5039); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } return ResultData.ok(); } @Override public ResultData checkKey(SceneCheckKeyParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId()); String bucket = scenePlusExt.getYunFileBucket(); SceneJsonBean sceneJsonBean = null; String sceneJson = redisUtil.get(String.format(RedisKey.SCENE_JSON, param.getNum())); if(StrUtil.isEmpty(sceneJson)){ sceneJson = fYunFileService.getFileContent(bucket, String.format(UploadFilePath.DATA_VIEW_PATH, param.getNum()) + "scene.json"); } sceneJsonBean = JSON.parseObject(sceneJson, SceneJsonBean.class); if(!param.getPassword().equals(sceneJsonBean.getScenePassword())){ throw new BusinessException(ErrorCode.FAILURE_CODE_5021); } return ResultData.ok(); } @Override public ResultData addMosaics(BaseDataParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); //如果redis数据丢失,从本地文件中同步马赛克数据到redis this.syncMosaicFromFileToRedis(param.getNum()); Map map = new HashMap<>(); JSONArray jsonArray = JSON.parseArray(param.getData()); for (Object o : jsonArray) { JSONObject mosaic = (JSONObject) o; String panoId = mosaic.getString("panoId"); if(StrUtil.isEmpty(panoId)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5012); } map.put(panoId, JSON.toJSONString(mosaic)); } String key = String.format(RedisKey.SCENE_MOSAIC_DATA, param.getNum()); redisUtil.hmset(key, map); //写入本地文件,作为备份 this.writeMosaic(param.getNum()); //更新数据库 this.updateMosaicFlag(param.getNum()); //更新版本号 this.upgradeVersionById(sceneEditInfo.getId()); return ResultData.ok(); } private void updateMosaicFlag(String num){ ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num); SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByScenePlusId(scenePlus.getId()); String key = String.format(RedisKey.SCENE_MOSAIC_DATA, num); boolean flag = redisUtil.hasKey(key); if(flag){ sceneEditInfoExt.setMosaic(Integer.valueOf(CommonStatus.YES.code())); }else{ sceneEditInfoExt.setMosaic(Integer.valueOf(CommonStatus.NO.code())); } sceneEditInfoExtService.updateById(sceneEditInfoExt); } /** *

保证马赛克数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis **/ private void syncMosaicFromFileToRedis(String num) throws Exception{ String key = String.format(RedisKey.SCENE_MOSAIC_DATA, num); boolean exist = redisUtil.hasKey(key); if(exist){ return; } // String uuid = cn.hutool.core.lang.UUID.randomUUID().toString(); String lockKey = String.format(RedisLockKey.LOCK_MOSAIC_DATA_SYNC, num); boolean lock = redisLockUtil.lock(lockKey, RedisKey.EXPIRE_TIME_1_MINUTE); if(!lock){ throw new BusinessException(ErrorCode.SYSTEM_BUSY); } try{ exist = redisUtil.hasKey(key); if(exist){ return; } String filePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "mosaic.json"; String mosaicData = FileUtils.readUtf8String(filePath); if(StrUtil.isEmpty(mosaicData)){ return; } JSONArray jsonArray = JSON.parseArray(mosaicData); if(CollUtil.isEmpty(jsonArray)){ return; } Map map = new HashMap<>(); for (Object o : jsonArray) { JSONObject jo = (JSONObject)o; map.put(jo.getString("panoId"), jo.toJSONString()); } redisUtil.hmset(key, map); }finally { redisLockUtil.unlockLua(lockKey); } } /** *

保证马赛克数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis **/ private void writeMosaic(String num) throws Exception{ String mosaicPath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "mosaic.json"; String key = String.format(RedisKey.SCENE_MOSAIC_DATA, num); Map mosaicMap = redisUtil.hmget(key); if(CollUtil.isEmpty(mosaicMap)){ FileUtils.deleteFile(mosaicPath); return; } List mosaicList = Lists.newArrayList(mosaicMap.values()); JSONArray jsonArr = new JSONArray(); mosaicList.stream().forEach(mosaic->{ jsonArr.add(JSONObject.parseObject(mosaic)); }); String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString(); String lockKey = String.format(RedisLockKey.LOCK_MOSAIC_JSON, num); boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE); if(!lock){ return; } try{ FileUtils.writeFile(mosaicPath, jsonArr.toJSONString()); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } } @Override public ResultData uploadLinkPan(String num, String sid, String fileName, MultipartFile file) throws Exception { 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 localDataPath = String.format(ConstantFilePath.DATABUFFER_FORMAT, num); String localImagesPath = String.format(ConstantFilePath.IMAGESBUFFER_FORMAT, num); String path = scenePlusExt.getDataSource(); String target = localImagesPath + "panorama/" + sid; FileUtils.deleteDirectory(target); //文件写如本地磁盘 String filePath = target + File.separator + "extras/images" + File.separator + fileName; File targetFile = new File(filePath); if(!targetFile.getParentFile().exists()){ targetFile.getParentFile().mkdirs(); } file.transferTo(targetFile); //调用算法切全景图 FileUtils.copyFile(path + File.separator + "data.json", target + File.separator+"data.json", true); FileUtils.copyFile(path + File.separator + "project.json", target + File.separator+"project.json", true); JSONObject visionJson = new JSONObject(); JSONArray visionArray = new JSONArray(); visionJson.put("uuid", sid); visionJson.put("group", 1); visionJson.put("subgroup", 0); visionArray.add(visionJson); JSONObject vision = new JSONObject(); vision.put("sweepLocations", visionArray); cn.hutool.core.io.FileUtil.writeString(vision.toString(), target + "/extras" + File.separator + "vision.txt", StandardCharsets.UTF_8); //data.json增加extras为执行重建算法 String type = "4k"; String data = FileUtils.readFile(target + File.separator + "data.json"); if(data != null){ JSONObject floorplanJson = new JSONObject(); floorplanJson.put("has_source_images", true); floorplanJson.put("has_vision_txt", true); JSONObject dataJson = JSONObject.parseObject(data); dataJson.put("extras", floorplanJson); dataJson.put("split_type", "SPLIT_V8");//替换全景图算法 String skyboxType = "SKYBOX_V6";//默认4k minion if(SceneFrom.PRO.code().equals(scenePlusExt.getSceneFrom())){ skyboxType = "SKYBOX_V7"; type = "2k"; } if(scenePlusExt.getSceneScheme() == 3){ if("4k".equals(scenePlusExt.getSceneResolution())){ skyboxType = "SKYBOX_V14"; }else{ skyboxType = "SKYBOX_V13"; } } dataJson.put("skybox_type", skyboxType); cn.hutool.core.io.FileUtil.writeString(dataJson.toString(), target + File.separator+"data.json", StandardCharsets.UTF_8); } //创建文件夹软连接并且复制data.json和project.json String capturePath = target + File.separator + "capture"; String resultPath = target + File.separator + "results"; log.info("场景关联上传全景图:capturePath={}", capturePath); log.info("场景关联上传全景图:resultPath={}", resultPath); if(cn.hutool.core.io.FileUtil.exist(capturePath)){ cn.hutool.core.io.FileUtil.del(capturePath); } if(cn.hutool.core.io.FileUtil.exist(resultPath)){ cn.hutool.core.io.FileUtil.del(resultPath); } //下载data.fdage if(FYunTypeEnum.AWS.code().equals(this.fyunType)){ //亚马逊保持旧方式,超链接capture CreateObjUtil.createSoftConnection(path + File.separator + "capture", capturePath); } fYunFileService.downloadFile(ConstantFilePath.OSS_PREFIX + path.replace(ConstantFilePath.BUILD_MODEL_PATH, "") + "/data.fdage", capturePath + "/data.fdage"); CreateObjUtil.build3dModel(target , "1"); //读取upload文件,获取需要上传的文件 JSONArray array = ComputerUtil.getUploadArray(resultPath + "/upload.json", this.maxCheckTimes, this.waitTime); Map map = new HashMap<>(); JSONObject fileJson; String uploadFile, uploadFilePath; String imgEditPath = String.format(UploadFilePath.IMG_EDIT_PATH, num); for(int i = 0, len = array.size(); i < len; i++){ fileJson = array.getJSONObject(i); uploadFile = fileJson.getString("file"); uploadFilePath = resultPath +File.separator + uploadFile; //文件不存在抛出异常 if(!cn.hutool.core.io.FileUtil.exist(uploadFilePath)){ throw new Exception(uploadFilePath + "文件不存在"); } Integer clazz = fileJson.getIntValue("clazz"); if(Objects.isNull(clazz)){ continue; } if(clazz == 4 || clazz == 5 || clazz == 7){ map.put(uploadFilePath, imgEditPath + "panorama/" + sid + File.separator + uploadFile); } } //上传全景图 map.put(filePath, imgEditPath + "panorama/" + sid + "/high/" + fileName); fYunFileService.uploadMulFiles(bucket, map); Map result = new HashMap<>(); result.put("type", type); return ResultData.ok(result); } @Override public ResultData saveLinkPan(SaveLinkPanParamVO param) throws Exception { String num = param.getNum(); ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } //添加场景关联数据 this.addOrUpdateLinPan(num, param.getLinkPans()); //添加场景关联图标 this.addOrUpdateLinkPanStyles(num, param.getStyles()); //场景关联数据备份到本地 this.writeLinkScene(num); //更新场景关联标识、升级版本号 this.setLinkScenesAndUpgradeVersion(scenePlus.getId(), num); return ResultData.ok(); } @Override public ResultData deleteStyles(DeleteLinkSceneStylesParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if (scenePlus == null) throw new BusinessException(ErrorCode.FAILURE_CODE_5005); List sidList = param.getSidList(); this.syncLinkPanStylesFromFileToRedis(param.getNum()); String key = String.format(RedisKey.SCENE_LINKPAN_STYLES, param.getNum()); List deleteList = redisUtil.hMultiGet(key, sidList); redisUtil.hdel(key, sidList.toArray()); //写入本地文件,作为备份 this.writeLinkScene(param.getNum()); //删除oss文件 List deleteFileList = deleteList.stream().map(str -> { JSONObject parse = JSON.parseObject(str); return parse.getString("url"); }).collect(Collectors.toList()); sceneUploadService.delete( DeleteFileParamVO.builder() .num(param.getNum()) .fileNames(deleteFileList) .bizType(FileBizType.LINK_STYLE.code()).build()); return ResultData.ok(); } @Override public ResultData deleteLinkPan(DeleteLinkPanParamVO param) throws Exception { 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(); List deleteSidList = param.getSidList(); //处理删除状态数据 this.deletelinkPanData(param.getNum(), deleteSidList, bucket); //写入本地文件,作为备份 this.writeLinkScene(param.getNum()); //更新场景关联标识、升级版本号 this.setLinkScenesAndUpgradeVersion(scenePlus.getId(), param.getNum()); return ResultData.ok(); } private void deletelinkPanData(String num, List deleteSidList, String bucket) throws Exception { if(CollUtil.isEmpty(deleteSidList)){ return; } this.syncLinPanFromFileToRedis(num); //从redis中加载热点数据 String key = String.format(RedisKey.SCENE_LINKPAN_DATA, num); List deletDataList = redisUtil.hMultiGet(key, deleteSidList); if(CollUtil.isEmpty(deletDataList)) return; //从redis中移除热点数据 redisUtil.hdel(key, deleteSidList.toArray()); //删除oss文件 String imgEditPath = String.format(UploadFilePath.IMG_EDIT_PATH, num); deleteSidList.stream().forEach(sid->{ fYunFileService.deleteFolder(bucket, imgEditPath + "panorama_edit/" + sid); }); } @Override public ResultData listLinkPan(String num) throws Exception { this.syncLinPanFromFileToRedis(num); this.syncLinkPanStylesFromFileToRedis(num); JSONObject result = new JSONObject(); //查询场景关联数据 String key = String.format(RedisKey.SCENE_LINKPAN_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); //封装styles数据 List styles = Lists.newArrayList(); key = String.format(RedisKey.SCENE_LINKPAN_STYLES, num); Map styleMap = redisUtil.hmget(key); if(CollUtil.isNotEmpty(styleMap)) { for (String style : styleMap.values()) { styles.add(JSON.parseObject(style)); } } //图标按写入时间排序 styles = this.sortStyles(styles); result.put("styles", styles); return ResultData.ok(result); } private List sortStyles(List styles){ if(CollUtil.isEmpty(styles)){ return null; } //统计使用频次 List styleBeans = Lists.newArrayList(); for (JSONObject style : styles) { Long createTime = style.getLong("createTime"); createTime = Objects.isNull(createTime) ? Calendar.getInstance().getTimeInMillis() : createTime; style.remove("createTime"); styleBeans.add( StyleBean.builder().style(style) .createTime(createTime).build()); } //排序 List styleList = Lists.newArrayList(); if(CollUtil.isNotEmpty(styleBeans)){ styleList = styleBeans.stream().sorted(Comparator.comparing(StyleBean::getCreateTime).reversed()) .map(item -> { return item.getStyle(); }).collect(Collectors.toList()); } return styleList; } private void setLinkScenesAndUpgradeVersion(Long scenePlusId, String num){ SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlusId); //查询缓存是否有场景关联数据 String key = String.format(RedisKey.SCENE_LINKPAN_DATA, num); Map allTagsMap = redisUtil.hmget(key); boolean hashTags = false; for (Entry tagMap : allTagsMap.entrySet()) { if(StrUtil.isEmpty(tagMap.getValue())){ continue; } hashTags = true; break; } //更新linkscenes字段 sceneEditInfoExtService.update( new LambdaUpdateWrapper() .set(SceneEditInfoExt::getLinks, hashTags ? CommonStatus.YES.code() : CommonStatus.NO.code()) .eq(SceneEditInfoExt::getEditInfoId, sceneEditInfo.getId())); //更新场景版本 this.update(new LambdaUpdateWrapper() .setSql("version=version+" + 1) .setSql("link_version=link_version+" + 1) .eq(SceneEditInfo::getId, sceneEditInfo.getId())); } /** *

热点数据保存 *

* @author dengsixing * @date 2022/3/3 **/ private void writeLinkScene(String num) throws Exception{ String dataKey = String.format(RedisKey.SCENE_LINKPAN_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(linkPan->{ tagJsonArr.add(JSONObject.parseObject(linkPan)); }); } jsonObject.put("tags", tagJsonArr); String stylesKey = String.format(RedisKey.SCENE_LINKPAN_STYLES, num); Map styleMap = redisUtil.hmget(stylesKey); List styleList = Lists.newArrayList(); if(CollUtil.isNotEmpty(styleMap)){ styleMap.values().stream().forEach(style->{ styleList.add(JSONObject.parseObject(style)); }); } jsonObject.put("styles", styleList); String linkScenePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "links.json"; String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString(); String lockKey = String.format(RedisLockKey.LOCK_LINK_SCENE_JSON, num); boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE); if(!lock){ return; } try{ cn.hutool.core.io.FileUtil.writeUtf8String(jsonObject.toJSONString(), linkScenePath); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } } private void addOrUpdateLinkPanStyles(String num, List styles) throws Exception{ this.syncLinkPanStylesFromFileToRedis(num); if(CollUtil.isEmpty(styles)){ return; } long time = Calendar.getInstance().getTimeInMillis(); Map styleMap = new HashMap<>(); AtomicInteger index = new AtomicInteger(); styles.stream().forEach(style->{ String id = style.getString("sid"); style.put("createTime", time + index.getAndIncrement()); styleMap.put(id, style.toJSONString()); }); String key = String.format(RedisKey.SCENE_LINKPAN_STYLES, num); redisUtil.hmset(key, styleMap); } /** *

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

* @author dengsixing * @date 2022/3/3 **/ private void syncLinkPanStylesFromFileToRedis(String num) throws Exception{ String key = String.format(RedisKey.SCENE_LINKPAN_STYLES, num); boolean exist = redisUtil.hasKey(key); if(exist){ return; } String lockKey = String.format(RedisLockKey.LOCK_LINKPAN_STYLES_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 linkSceneFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num); String linkSceneData = FileUtils.readUtf8String(linkSceneFilePath + "links.json"); if(StrUtil.isEmpty(linkSceneData)){ return; } JSONObject jsonObject = JSON.parseObject(linkSceneData); JSONArray stylesArr = jsonObject.getJSONArray("styles"); if(CollUtil.isEmpty(stylesArr)){ return; } Map styleMap = new HashMap<>(); for (Object style : stylesArr) { JSONObject styleObj = (JSONObject)style; String id = styleObj.getString("sid"); styleMap.put(id, styleObj.toJSONString()); } redisUtil.hmset(key, styleMap); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } } private void addOrUpdateLinPan(String num, List linkPanList) throws Exception{ Map addOrUpdateMap = new HashMap<>(); int i = 0; for (JSONObject jsonObject : linkPanList) { jsonObject.put("createTime", Calendar.getInstance().getTimeInMillis() + i++); addOrUpdateMap.put(jsonObject.getString("sid"), jsonObject.toJSONString()); } this.syncLinPanFromFileToRedis(num); //处理新增和修改数据 this.addOrUpdateLinkPanHandler(num, addOrUpdateMap); } /** *

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

* @author dengsixing * @date 2022/3/3 **/ private void syncLinPanFromFileToRedis(String num) throws Exception{ String key = String.format(RedisKey.SCENE_LINKPAN_DATA, num); boolean exist = redisUtil.hasKey(key); if(exist){ return; } String lockKey = String.format(RedisLockKey.LOCK_LINKPAN_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 linkSceneFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num); String linkSceneData = FileUtils.readUtf8String(linkSceneFilePath + "links.json"); if(StrUtil.isEmpty(linkSceneData)){ return; } JSONObject jsonObject = JSON.parseObject(linkSceneData); 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); } } private void addOrUpdateLinkPanHandler(String num, Map addOrUpdateMap){ if(CollUtil.isEmpty(addOrUpdateMap)) return; //数据验证,新增、修改状态,linkPan不能为空 for (String sid : addOrUpdateMap.keySet()) { String linkPan = addOrUpdateMap.get(sid); if(StrUtil.isEmpty(linkPan)){ throw new BusinessException(ErrorCode.FAILURE_CODE_7022); } } //批量写入缓存 String key = String.format(RedisKey.SCENE_LINKPAN_DATA, num); redisUtil.hmset(key, addOrUpdateMap); } private void updateBoxVideos(SceneEditInfo sceneEditInfo, Long scenePlusId, String boxVideos){ if(Objects.isNull(sceneEditInfo)){ sceneEditInfo = new SceneEditInfo(); sceneEditInfo.setScenePlusId(scenePlusId); sceneEditInfo.setBoxVideos(boxVideos); this.save(sceneEditInfo); }else{ this.update(new UpdateWrapper() .setSql("version = version + 1") .set("box_videos", boxVideos) .eq("id", sceneEditInfo.getId())); } } private void updateBoxPhotos(SceneEditInfo sceneEditInfo, String boxPhotos){ this.update(new LambdaUpdateWrapper() .set(SceneEditInfo::getBoxPhotos, boxPhotos) .setSql("version = version + 1") .eq(SceneEditInfo::getId, sceneEditInfo.getId())); } private String createBoxVideos( String num, String sid, JSONObject boxVideo, SceneEditInfo sceneEditInfo, int type) throws Exception{ String boxVideos = null; if(sceneEditInfo != null){ boxVideos = sceneEditInfo.getBoxVideos(); } JSONArray boxVideosJson = null; if (StrUtil.isNotEmpty(boxVideos)) { boxVideosJson = JSONArray.parseArray(boxVideos); }else { boxVideosJson = new JSONArray(); } if(boxVideosJson.size() > 0){ int i = 1; long timeInMillis = Calendar.getInstance().getTimeInMillis(); for (Object o : boxVideosJson) { JSONObject item = (JSONObject)o; if(Objects.nonNull(item.getLong("createTime"))){ continue; } item.put("createTime", timeInMillis - (i++)); } } String result = null; //删除 if(type == OperationType.DELETE.code()){ Set deleteVidoeFile = new HashSet<>(); Set deletePicFile = new HashSet<>(); if(boxVideosJson.size() == 0) return null; for(int i=0;i(deleteVidoeFile)).build()); //删除资源文件 if(CollUtil.isNotEmpty(deleteVidoeFile)) sceneUploadService.delete( DeleteFileParamVO.builder().num(num) .bizType(FileBizType.BOX_POSTER.code()) .fileNames(new ArrayList<>(deletePicFile)).build()); }else{ boxVideo.put("createTime", Calendar.getInstance().getTimeInMillis()); //更新 boolean exist = false; for(int i=0;i deleteFile = new HashSet<>(); if(boxPhotosJson.size() == 0) return null; for(int i=0;i(deleteFile)).build()); }else{ //更新 boolean exist = false; for(int i=0;i list = Lists.newArrayList(); for (Object o : boxPhotosJson) { JSONObject jsonObject = (JSONObject)o; list.add(BoxPhotoBean.builder().createTime(jsonObject.getLong("createTime")).boxPhoto(jsonObject).build()); } //按创建时间倒叙排序 list.sort(Comparator.comparingLong(BoxPhotoBean::getCreateTime).reversed()); // list转JSONArray JSONArray array = new JSONArray(); list.stream().forEach(bean->{ array.add(bean.getBoxPhoto()); }); result = array.toJSONString(); } return result; } private void transferToFlv(String num, String fileName, String bucket) throws Exception { String userEditPath = String.format(UploadFilePath.USER_EDIT_PATH, num); String localImagesPath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num); String localFilePath = localImagesPath + fileName; File targetFile = new File(localImagesPath); if (!targetFile.exists()){ targetFile.mkdirs(); } targetFile = new File(localFilePath); if (targetFile.exists()){ FileUtils.deleteFile(localFilePath); } //从用户编辑目录中下载视频到本地 String filePath = userEditPath + fileName; fYunFileService.downloadFile(bucket, filePath, localImagesPath + fileName); //视频格式转换 CreateObjUtil.mp4ToFlv(localFilePath, localFilePath.replace("mp4", "flv")); //上传 String flvFileName = fileName.replace("mp4", "flv"); fYunFileService.uploadFile(bucket, localFilePath.replace("mp4", "flv"), userEditPath+flvFileName); } @Override public ResultData deleteMosaics(DeleteMosaicParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } SceneEditInfo sceneEditInfo = this.getByScenePlusId(scenePlus.getId()); //如果redis数据丢失,从本地文件中同步马赛克数据到redis this.syncMosaicFromFileToRedis(param.getNum()); String key = String.format(RedisKey.SCENE_MOSAIC_DATA, param.getNum()); redisUtil.hdel(key, param.getPanoIdList().toArray()); //写入本地文件,作为备份 this.writeMosaic(param.getNum()); //更新数据库 this.updateMosaicFlag(param.getNum()); //更新版本号 this.upgradeVersionById(sceneEditInfo.getId()); return ResultData.ok(); } @Override public List getMosaicList(String num) throws Exception { //如果redis数据丢失,从本地文件中同步马赛克数据到redis this.syncMosaicFromFileToRedis(num); String key = String.format(RedisKey.SCENE_MOSAIC_DATA, num); Map map = redisUtil.hmget(key); if(CollUtil.isEmpty(map)){ ResultData.ok(new String[0]); } return map.values().stream() .map(mosaic-> JSON.parseObject(mosaic)) .collect(Collectors.toList()); } @Override public ResultData addWaterMark(BaseFileParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService .getByScenePlusId(scenePlus.getId()); sceneEditInfoExt.setWaterMark(param.getFileName()); sceneEditInfoExtService.updateById(sceneEditInfoExt); return ResultData.ok(); } @Override public ResultData deleteWaterMark(BaseFileParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } sceneUploadService.delete( DeleteFileParamVO.builder() .num(param.getNum()) .fileNames(Lists.newArrayList(param.getFileName())) .bizType(FileBizType.WATERMARK.code()).build()); SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByScenePlusId(scenePlus.getId()); sceneEditInfoExt.setWaterMark(""); sceneEditInfoExtService.updateById(sceneEditInfoExt); return ResultData.ok(); } @Override public ResultData saveFilter(BaseDataParamVO param) throws Exception { ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum()); if(Objects.isNull(scenePlus)){ throw new BusinessException(ErrorCode.FAILURE_CODE_5005); } String key = String.format(RedisKey.SCENE_filter_DATA, param.getNum()); JSONArray filterArr = JSON.parseArray(param.getData()); int filters = CommonStatus.YES.code(); if(CollUtil.isEmpty(filterArr)){ filters = CommonStatus.NO.code(); redisUtil.del(key); }else{ List filterList = filterArr.stream().map(item->JSON.toJSONString(item)).collect(Collectors.toList()); redisUtil.lRightPushAll(key, filterList); } //写本地文件,作为备份 this.writeFilter(param.getNum()); //更新数据库 SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByScenePlusId(scenePlus.getId()); sceneEditInfoExt.setFilters(filters); sceneEditInfoExtService.updateById(sceneEditInfoExt); return ResultData.ok(); } private void writeFilter(String num) throws Exception{ String filterPath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "filter.json"; String key = String.format(RedisKey.SCENE_filter_DATA, num); List filters = redisUtil.lGet(key, 0, -1); if(CollUtil.isEmpty(filters)){ FileUtils.deleteFile(filterPath); return; } String lockKey = String.format(RedisLockKey.LOCK_filter_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(filterPath, JSON.toJSONString(filters)); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } } private void syncFiltersFromFileToRedis(String num) throws Exception{ String key = String.format(RedisKey.SCENE_filter_DATA, num); boolean exist = redisUtil.hasKey(key); if(exist){ return; } String lockKey = String.format(RedisLockKey.LOCK_FILTER_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 filePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num); String filterData = FileUtils.readUtf8String(filePath + "filter.json"); if(StrUtil.isEmpty(filterData)){ return; } JSONArray jsonArray = JSON.parseArray(filterData); if(CollUtil.isEmpty(jsonArray)){ return; } redisUtil.lRightPushAll(key, jsonArray.stream().collect(Collectors.toList())); }finally { redisLockUtil.unlockLua(lockKey, lockVal); } } @Override public ResultData listFilter(BaseSceneParamVO param) throws Exception { //同步数据 this.syncFiltersFromFileToRedis(param.getNum()); //查询redis String key = String.format(RedisKey.SCENE_filter_DATA, param.getNum()); List list = redisUtil.lGet(key, 0, -1); List collect = list.stream().map(str -> JSON.parseObject(str)).collect(Collectors.toList()); return ResultData.ok(collect); } }