浏览代码

Merge branch 'release' into feature-算法指定切图方式-下载改造-20220629

# Conflicts:
#	4dkankan-center-scene/src/main/java/com/fdkankan/scene/schedule/ScheduleJob.java
dengsixing 3 年之前
父节点
当前提交
3fe2cf906b
共有 23 个文件被更改,包括 998 次插入281 次删除
  1. 5 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/bean/SceneJsonBean.java
  2. 27 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/bean/StyleBean.java
  3. 26 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/bean/UpgradeBean.java
  4. 31 7
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/controller/SceneEditController.java
  5. 1 1
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/controller/TestController.java
  6. 23 8
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/controller/V3Controller.java
  7. 6 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/entity/SceneEditInfoExt.java
  8. 10 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/ISceneEditInfoService.java
  9. 0 4
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/ISceneProService.java
  10. 2 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/ISceneService.java
  11. 552 23
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/impl/SceneEditInfoServiceImpl.java
  12. 12 222
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/impl/SceneProServiceImpl.java
  13. 98 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/impl/SceneServiceImpl.java
  14. 43 8
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/impl/SceneUpgradeToV4Service.java
  15. 32 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/CopySceneParamVO.java
  16. 27 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/DeleteLinkPanParamVO.java
  17. 27 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/DeleteLinkSceneStylesParamVO.java
  18. 25 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/LinkPanParamVO.java
  19. 34 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/SaveLinkPanParamVO.java
  20. 5 0
      4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/SceneInfoVO.java
  21. 5 5
      4dkankan-center-scene/src/main/resources/bootstrap-dev-eur.yml
  22. 3 3
      4dkankan-center-scene/src/main/resources/bootstrap-dev.yml
  23. 4 0
      4dkankan-common/src/main/java/com/fdkankan/common/controller/BaseController.java

+ 5 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/bean/SceneJsonBean.java

@@ -186,5 +186,10 @@ public class SceneJsonBean {
      */
     private Integer tours;
 
+    /**
+     * 是否有场景关联(0-否,1-是)
+     */
+    private Integer links;
+
 
 }

+ 27 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/bean/StyleBean.java

@@ -0,0 +1,27 @@
+package com.fdkankan.scene.bean;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * <p>
+ * TODO
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/3/24
+ **/
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class StyleBean {
+
+    private Long createTime;
+
+    private JSONObject style;
+
+}

+ 26 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/bean/UpgradeBean.java

@@ -0,0 +1,26 @@
+package com.fdkankan.scene.bean;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * <p>
+ * TODO
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/6/28
+ **/
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class UpgradeBean {
+
+    private Long sceneProId;
+
+    private boolean reUpgrade;
+
+}

+ 31 - 7
4dkankan-center-scene/src/main/java/com/fdkankan/scene/controller/SceneEditController.java

@@ -616,17 +616,41 @@ public class SceneEditController extends BaseController {
     /**
      * 保存关联全景图
      */
-    @PostMapping(value = "/savePanorama")
-    public ResultData savePanorama(FileParamVO param, @RequestParam("file") MultipartFile file) throws Exception{
-        return sceneProService.savePanorama(param, file);
+    @CheckCooperationPermit
+    @PostMapping(value = "/linkPan/upload")
+    public ResultData uploadLinkPan(
+        @RequestParam(value = "num") String num,
+        @RequestParam(value = "sid") String sid,
+        @RequestParam(value = "fileName") String fileName,
+        @RequestParam("file") MultipartFile file) throws Exception{
+        return sceneEditInfoService.uploadLinkPan(num, sid, fileName, file);
     }
 
     /**
      * 保存关联全景图
      */
-    @PostMapping(value = "/savePanoramaJson")
-    public ResultData savePanoramaJson(@RequestBody FileParamVO param) throws Exception{
-        return sceneProService.savePanoramaJson(param);
+    @CheckCooperationPermit
+    @PostMapping(value = "/linkPan/save")
+    public ResultData saveLinkPan(@RequestBody @Validated SaveLinkPanParamVO param) throws Exception{
+        return sceneEditInfoService.saveLinkPan(param);
+    }
+
+    @CheckCooperationPermit
+    @PostMapping(value = "/linkPan/delete")
+    public ResultData deleteLinkPan(@RequestBody @Validated DeleteLinkPanParamVO param) throws Exception {
+        return sceneEditInfoService.deleteLinkPan(param);
+    }
+
+    @CheckCooperationPermit
+    @PostMapping(value = "/styles/delete")
+    public ResultData deleteStyles(@RequestBody @Validated DeleteLinkSceneStylesParamVO param) throws Exception {
+        return sceneEditInfoService.deleteStyles(param);
+    }
+
+    @CheckCooperationPermit
+    @PostMapping(value = "/linkPan/list")
+    public ResultData listLinkPan(@RequestParam(value = "num") String num) throws Exception {
+        return sceneEditInfoService.listLinkPan(num);
     }
 
     /**
@@ -920,7 +944,7 @@ public class SceneEditController extends BaseController {
 
     /**
      * 获取场景权限
-     * @param param
+     * @param num 场景码
      * @return
      * @throws Exception
      */

+ 1 - 1
4dkankan-center-scene/src/main/java/com/fdkankan/scene/controller/TestController.java

@@ -88,7 +88,7 @@ public class TestController extends BaseController {
     @GetMapping("/test")
     public ResultData test() throws Exception {
 
-//        uploadToOssUtil.uploadBySh(path, "test_dsx/bin.tar");
+        uploadToOssUtil.deleteFile("test_dsx");
 
         return ResultData.ok();
 

+ 23 - 8
4dkankan-center-scene/src/main/java/com/fdkankan/scene/controller/V3Controller.java

@@ -1,23 +1,20 @@
 package com.fdkankan.scene.controller;
 
-import com.dtflys.forest.annotation.Post;
 import com.fdkankan.common.controller.BaseController;
 import com.fdkankan.common.response.ResultData;
 import com.fdkankan.scene.service.ISceneEditInfoService;
 import com.fdkankan.scene.service.IScenePlusService;
+import com.fdkankan.scene.service.ISceneService;
 import com.fdkankan.scene.service.ISceneUpgradeToV4Service;
+import com.fdkankan.scene.vo.CopySceneParamVO;
 import com.fdkankan.scene.vo.MoveSceneParamVO;
 import com.fdkankan.scene.vo.UnbindOrBindCameraParamVO;
 import com.fdkankan.scene.vo.UpgradeToV4ParamVO;
-import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
 
 /**
  * <p>
@@ -37,6 +34,9 @@ public class V3Controller extends BaseController {
     private ISceneEditInfoService sceneEditInfoService;
     @Autowired
     private IScenePlusService scenePlusService;
+    @Autowired
+    private ISceneService sceneService;
+
 
     /**
      * <p>
@@ -109,5 +109,20 @@ public class V3Controller extends BaseController {
         return scenePlusService.moveScene(param.getNumList(), param.getCameraId(), param.getUserId());
     }
 
+    /**
+     * <p>
+         场景拷贝
+     * </p>
+     * @author tianboguang
+     * @date 2022/6/29
+     * @return com.fdkankan.common.response.ResultData
+     **/
+    @PostMapping(value = "/copyScene")
+    public ResultData copyScene(
+            @RequestBody @Validated CopySceneParamVO param) throws IOException {
+        sceneService.copyScene(param);
+        return ResultData.ok();
+    }
+
 
 }

+ 6 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/entity/SceneEditInfoExt.java

@@ -62,6 +62,12 @@ public class SceneEditInfoExt {
     private String mosaics;
 
     /**
+     * 是否有场景关联(0-否,1-是)
+     */
+    @TableField("links")
+    private Integer links;
+
+    /**
      * 创建时间
      */
     @TableField("create_time")

+ 10 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/ISceneEditInfoService.java

@@ -70,6 +70,16 @@ public interface ISceneEditInfoService extends IService<SceneEditInfo> {
 
     ResultData addMosaics(@RequestBody @Validated BaseDataParamVO param) throws Exception;
 
+    ResultData uploadLinkPan(String num, String sid, String fileName, MultipartFile file) throws Exception;
+
+    ResultData saveLinkPan(SaveLinkPanParamVO param) throws Exception;
+
+    ResultData listLinkPan(String num) throws Exception;
+
+    ResultData deleteLinkPan(DeleteLinkPanParamVO param) throws Exception;
+
+    ResultData deleteStyles(DeleteLinkSceneStylesParamVO param) throws Exception;
+
 
 
 

+ 0 - 4
4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/ISceneProService.java

@@ -148,10 +148,6 @@ public interface ISceneProService extends IService<ScenePro> {
 
     ResultData deleteUploadBgMusic(FileParamVO param) throws Exception;
 
-    ResultData savePanorama(FileParamVO param, MultipartFile file) throws Exception;
-
-    ResultData savePanoramaJson(FileParamVO param) throws Exception;
-
     ResultData copyAndUpdateFloorJson(FileParamVO param) throws Exception;
 
     ResultData updateFloorJson(FileParamVO param) throws Exception;

+ 2 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/ISceneService.java

@@ -103,4 +103,6 @@ public interface ISceneService extends IService<Scene> {
     ResultData deleteScene(String sceneNum) throws IOException;
 
     Long getSceneCount(Long cameraId, Long userId);
+
+    void copyScene(CopySceneParamVO copySceneParamVO) throws IOException;
 }

+ 552 - 23
4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/impl/SceneEditInfoServiceImpl.java

@@ -2,7 +2,6 @@ package com.fdkankan.scene.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.net.multipart.UploadFile;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.ZipUtil;
 import com.alibaba.fastjson.JSON;
@@ -13,9 +12,6 @@ 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.dtflys.forest.exceptions.ForestRuntimeException;
-import com.dtflys.forest.http.ForestRequest;
-import com.dtflys.forest.http.ForestResponse;
 import com.fdkankan.common.constant.*;
 import com.fdkankan.common.exception.BusinessException;
 import com.fdkankan.common.response.Result;
@@ -38,8 +34,10 @@ import com.fdkankan.redis.util.RedisLockUtil;
 import com.fdkankan.redis.util.RedisUtil;
 import com.fdkankan.scene.api.dto.SceneInfoDTO;
 import com.fdkankan.scene.bean.BoxPhotoBean;
+import com.fdkankan.scene.bean.IconBean;
 import com.fdkankan.scene.bean.RequestSceneProV4;
 import com.fdkankan.scene.bean.SceneJsonBean;
+import com.fdkankan.scene.bean.StyleBean;
 import com.fdkankan.scene.bean.TagBean;
 import com.fdkankan.scene.callback.FdkkMiniReqErrorCallback;
 import com.fdkankan.scene.callback.FdkkMiniReqSuccessCallback;
@@ -69,7 +67,6 @@ import com.fdkankan.scene.vo.*;
 import com.google.common.collect.Lists;
 import com.google.errorprone.annotations.Var;
 import java.io.File;
-import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -82,6 +79,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -94,7 +92,6 @@ import java.io.IOException;
 import java.util.Calendar;
 import java.util.Objects;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.ObjectUtils;
 import org.springframework.web.multipart.MultipartFile;
 
 /**
@@ -241,10 +238,6 @@ public class SceneEditInfoServiceImpl extends ServiceImpl<ISceneEditInfoMapper,
             sceneEditInfoExt.setFloorPlanCompass(0f);
         }
 
-//        if(sceneEditControls == null){
-//            sceneEditControls = new SceneEditControls();
-//        }
-
         //生成sceneJson
         SceneJsonBean sceneJson = new SceneJsonBean();
         BeanUtil.copyProperties(sceneEditInfoExt, sceneJson);
@@ -252,9 +245,6 @@ public class SceneEditInfoServiceImpl extends ServiceImpl<ISceneEditInfoMapper,
         SceneEditControlsVO sceneEditControlsVO = BeanUtil.copyProperties(sceneEditControls, SceneEditControlsVO.class);
         sceneJson.setControls(sceneEditControlsVO);
         sceneJson.setNum(num);
-//        if(StrUtil.isNotEmpty(sceneEditInfo.getFloorPlanPath())){
-//            sceneJson.setFloorPlanPaths(sceneEditInfo.getFloorPlanPath().split(","));
-//        }
         sceneJson.setCreateTime(scenePlus.getCreateTime());
 
         sceneJson.setSceneResolution(scenePlusExt.getSceneResolution());
@@ -267,9 +257,8 @@ public class SceneEditInfoServiceImpl extends ServiceImpl<ISceneEditInfoMapper,
         //处理热点数据,生成hot.json
         this.publicHotData(num, sceneEditInfo);
 
-        // TODO: 2022/3/2 这里的逻辑放在上传球幕视频接口中做了,这里先暂时注释掉,以后要删除
-        //处理球幕视频
-//        this.buildVideo(sceneEditInfo, sceneProExt.getDataSource(), sceneNum);
+        //发布场景关联相关数据
+        this.publicLinkSceneData(num);
 
         //本地写sceneJson文件
         String localSceneJsonPath = String.format(ConstantFilePath.SCENE_DATA_PATH_V4, num) + "scene.json";
@@ -303,13 +292,6 @@ public class SceneEditInfoServiceImpl extends ServiceImpl<ISceneEditInfoMapper,
         sceneEditInfoExt.setEditInfoId(sceneEditInfo.getId());
         sceneEditInfoExtService.saveOrUpdate(sceneEditInfoExt);
 
-//        sceneEditControls.setEditInfoId(sceneEditInfo.getId());
-//        if(sceneEditControls.getId() == null){
-//            sceneEditControlsService.save(sceneEditControls);
-//        }else{
-//            sceneEditControlsService.updateById(sceneEditControls);
-//        }
-
         // todo 调用v3接口同步场景缩略图url---------------------------------start
         String url = fkankanMiniHost + URL_UPGRADE_TO_V4_RESULT_SYNC;
         fdkankanMiniClient.upgradeToV4ResultSync(url,
@@ -326,6 +308,31 @@ public class SceneEditInfoServiceImpl extends ServiceImpl<ISceneEditInfoMapper,
         return ResultData.ok();
     }
 
+    public void publicLinkSceneData(String num) 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<String, String> 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";
+        uploadToOssUtil.upload(linkPanArr.toString().getBytes(), linkScenePath);
+
+        //拷贝编辑目录到发布目录
+        uploadToOssUtil.deleteFile(imgViewPath + "panorama");
+        uploadToOssUtil.copyFiles(imgEditPath + "panorama",imgViewPath + "panorama");
+
+    }
+
     private void buildVideo(SceneEditInfo sceneEditInfo, String path, String num) throws Exception{
         if(CommonStatus.NO.equals(sceneEditInfo.getBuildVideoStatus())){
             return;
@@ -1718,6 +1725,528 @@ public class SceneEditInfoServiceImpl extends ServiceImpl<ISceneEditInfoMapper,
         return ResultData.ok(result);
     }
 
+    @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 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");//替换全景图算法
+            //V5表示不需要生成high,low文件
+            dataJson.put("skybox_type", "SKYBOX_V6");//默认4k minion
+            if(SceneFrom.PRO.code().equals(scenePlusExt.getSceneFrom())){
+                dataJson.put("skybox_type", "SKYBOX_V7");//pro 2k
+                type = "2k";
+            }
+            // TODO: 2022/6/21 这里暂时不清楚含义,可能是国际版需要,先注释---start
+//            if(scenePlusExt.getSceneScheme() == 3){
+//                dataJson.put("skybox_type", "SKYBOX_V4");
+//            }
+            // TODO: 2022/6/21 这里暂时不清楚含义,可能是国际版需要,先注释---end
+            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(StorageType.AWS.code().equals(this.type)){
+            //亚马逊保持旧方式,超链接capture
+            CreateObjUtil.createSoftConnection(path + File.separator + "capture", capturePath);
+        }
+        CreateObjUtil.ossUtilCp(ConstantFilePath.OSS_PREFIX + path.replace(ConstantFilePath.BUILD_MODEL_PATH, "") + "/data.fdage", capturePath);
+        CreateObjUtil.build3dModel(target , "1");
+
+        //读取upload文件,获取需要上传的文件
+        JSONArray array = ComputerUtil.getUploadArray(resultPath + "/upload.json", this.maxCheckTimes, this.waitTime);
+        Map<String, String> 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);
+
+        uploadToOssUtil.uploadMulFiles(map);
+
+        Map<String, String> 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<String> sidList = param.getSidList();
+
+        this.syncLinkPanStylesFromFileToRedis(param.getNum());
+
+        String key = String.format(RedisKey.SCENE_LINKPAN_STYLES, param.getNum());
+        List<String> deleteList = redisUtil.hMultiGet(key, sidList);
+        redisUtil.hdel(key, sidList.toArray());
+
+        //写入本地文件,作为备份
+        this.writeLinkScene(param.getNum());
+
+        //删除oss文件
+        List<String> 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);
+
+        List<String> deleteSidList = param.getSidList();
+
+        //处理删除状态数据
+        this.deletelinkPanData(param.getNum(), deleteSidList);
+
+        //写入本地文件,作为备份
+        this.writeLinkScene(param.getNum());
+
+        //更新场景关联标识、升级版本号
+        this.setLinkScenesAndUpgradeVersion(scenePlus.getId(), param.getNum());
+
+        return ResultData.ok();
+    }
+
+    private void deletelinkPanData(String num, List<String> deleteSidList) throws Exception {
+
+        if(CollUtil.isEmpty(deleteSidList)){
+            return;
+        }
+
+        this.syncLinPanFromFileToRedis(num);
+
+        //从redis中加载热点数据
+        String key = String.format(RedisKey.SCENE_LINKPAN_DATA, num);
+        List<String> 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->{
+            uploadToOssUtil.deleteFile(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<String, String> allTagsMap = redisUtil.hmget(key);
+        List<JSONObject> tags = Lists.newArrayList();
+        List<TagBean> 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<JSONObject> styles = Lists.newArrayList();
+        key = String.format(RedisKey.SCENE_LINKPAN_STYLES, num);
+        Map<String, String> 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<JSONObject> sortStyles(List<JSONObject> styles){
+
+        if(CollUtil.isEmpty(styles)){
+            return null;
+        }
+
+        //统计使用频次
+        List<StyleBean> 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<JSONObject> 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<String, String> allTagsMap = redisUtil.hmget(key);
+        boolean hashTags = false;
+        for (Entry<String, String> tagMap : allTagsMap.entrySet()) {
+            if(StrUtil.isEmpty(tagMap.getValue())){
+                continue;
+            }
+            hashTags = true;
+            break;
+        }
+
+        //更新linkscenes字段
+        sceneEditInfoExtService.update(
+            new LambdaUpdateWrapper<SceneEditInfoExt>()
+                .set(SceneEditInfoExt::getLinks, hashTags ? CommonStatus.YES.code() : CommonStatus.NO.code())
+                .eq(SceneEditInfoExt::getEditInfoId, sceneEditInfo.getId()));
+
+        //更新场景版本
+        this.upgradeVersionById(sceneEditInfo.getId());
+    }
+
+    /**
+     * <p>
+     热点数据保存
+
+     * </p>
+     * @author dengsixing
+     * @date 2022/3/3
+     **/
+    private void writeLinkScene(String num) throws Exception{
+
+        String dataKey = String.format(RedisKey.SCENE_LINKPAN_DATA, num);
+        Map<String, String> tagMap = redisUtil.hmget(dataKey);
+        List<String> 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<String, String> styleMap = redisUtil.hmget(stylesKey);
+        List<JSONObject> 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 lockKey = String.format(RedisLockKey.LOCK_LINK_SCENE_JSON, num);
+        boolean lock = redisLockUtil.lock(lockKey, RedisKey.EXPIRE_TIME_1_MINUTE);
+        if(!lock){
+            return;
+        }
+        try{
+            cn.hutool.core.io.FileUtil.writeUtf8String(jsonObject.toJSONString(), linkScenePath);
+        }finally {
+            redisLockUtil.unlockLua(lockKey);
+        }
+    }
+
+
+    private void addOrUpdateLinkPanStyles(String num, List<JSONObject> styles) throws Exception{
+
+        this.syncLinkPanStylesFromFileToRedis(num);
+
+        if(CollUtil.isEmpty(styles)){
+            return;
+        }
+
+        long time = Calendar.getInstance().getTimeInMillis();
+        Map<String, String> 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);
+    }
+
+    /**
+     * <p>
+     保证icons数据安全性,当redis宕机导致icons数据丢失时,可以从文件中读取,恢复到redis
+     * </p>
+     * @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);
+        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 linkSceneFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num);
+            String linkSceneData = FileUtils.readFile(linkSceneFilePath + "links.json");
+            if(StrUtil.isEmpty(linkSceneData)){
+                return;
+            }
+            JSONObject jsonObject = JSON.parseObject(linkSceneData);
+            JSONArray stylesArr = jsonObject.getJSONArray("styles");
+            if(CollUtil.isEmpty(stylesArr)){
+                return;
+            }
+            Map<String, String> 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);
+        }
+
+    }
+
+    private void addOrUpdateLinPan(String num, List<JSONObject> linkPanList) throws Exception{
+        Map<String, String> 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);
+    }
+
+    /**
+     * <p>
+     保证热点数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis
+     * </p>
+     * @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);
+        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 linkSceneFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num);
+            String linkSceneData = FileUtils.readFile(linkSceneFilePath + "links.json");
+            if(StrUtil.isEmpty(linkSceneData)){
+                return;
+            }
+            JSONObject jsonObject = JSON.parseObject(linkSceneData);
+            JSONArray tagsArr = jsonObject.getJSONArray("tags");
+            if(CollUtil.isEmpty(tagsArr)){
+                return;
+            }
+            Map<String, String> 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);
+        }
+    }
+
+    private void addOrUpdateLinkPanHandler(String num, Map<String, String> 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();

+ 12 - 222
4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/impl/SceneProServiceImpl.java

@@ -20,6 +20,7 @@ import com.fdkankan.common.constant.ConstantFileName;
 import com.fdkankan.common.constant.ConstantFilePath;
 import com.fdkankan.common.constant.ConstantUrl;
 import com.fdkankan.common.constant.ErrorCode;
+import com.fdkankan.common.constant.FileBizType;
 import com.fdkankan.common.constant.OperationType;
 import com.fdkankan.common.constant.PayStatus;
 import com.fdkankan.common.constant.RecStatus;
@@ -75,7 +76,9 @@ import com.fdkankan.scene.service.ISceneProEditService;
 import com.fdkankan.scene.service.ISceneProExtService;
 import com.fdkankan.scene.service.ISceneProService;
 import com.fdkankan.scene.service.ISceneService;
+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;
@@ -90,6 +93,7 @@ import com.fdkankan.scene.vo.SceneProVO;
 import com.fdkankan.scene.vo.SceneVO;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import java.nio.charset.StandardCharsets;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
@@ -197,6 +201,8 @@ public class SceneProServiceImpl extends ServiceImpl<ISceneProMapper, ScenePro>
     IFdkkLaserService fdkkLaserService;
     @Resource
     ISceneProMapper sceneProMapper;
+    @Autowired
+    ISceneUploadService sceneUploadService;
 
     @Override
     public List<SceneVO> convert(List<ScenePro> list){
@@ -2394,6 +2400,12 @@ public class SceneProServiceImpl extends ServiceImpl<ISceneProMapper, ScenePro>
         //写入本地文件,作为备份
         this.writeHotJson(param.getNum());
 
+        //删除oss文件
+        sceneUploadService.delete(
+            DeleteFileParamVO.builder()
+                .num(param.getNum())
+                .fileNames(fileNameList)
+                .bizType(FileBizType.TAG_ICON.code()).build());
 
         return ResultData.ok();
     }
@@ -2599,11 +2611,6 @@ public class SceneProServiceImpl extends ServiceImpl<ISceneProMapper, ScenePro>
         }finally {
             redisLockUtil.unlockLua(lockKey);
         }
-
-
-
-
-
     }
 
 
@@ -4226,223 +4233,6 @@ public class SceneProServiceImpl extends ServiceImpl<ISceneProMapper, ScenePro>
     }
 
     @Override
-    public ResultData savePanorama(FileParamVO param, MultipartFile file) throws Exception {
-        String sceneNum = param.getNum();
-        String sid = param.getSid();
-        String imagesName = param.getImagesName();
-        if(StrUtil.isEmpty(sceneNum) || StrUtil.isEmpty(sid)){
-            throw new BusinessException(ErrorCode.PARAM_REQUIRED);
-        }
-
-        ScenePro scenePro = baseMapper.findByNum(sceneNum);
-        if(scenePro == null){
-            throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
-        }
-        SceneProExt sceneProExt = sceneProExtService.findBySceneProId(scenePro.getId());
-
-        StringBuffer dataBuf = new StringBuffer()
-                .append("data").append(File.separator)
-                .append("data").append(scenePro.getNum())
-                .append(File.separator);
-
-        StringBuffer imagesBuf = new StringBuffer()
-                .append("images").append(File.separator)
-                .append("images").append(scenePro.getNum())
-                .append(File.separator);
-
-        StringBuffer dataBuffer = new StringBuffer(ConstantFilePath.SCENE_PATH).append(dataBuf.toString());
-        StringBuffer imagesBuffer = new StringBuffer(ConstantFilePath.SCENE_PATH).append(imagesBuf.toString());
-
-        String path = sceneProExt.getDataSource();
-        String target = imagesBuffer.toString() + "panorama/" + sid;
-        FileUtils.deleteDirectory(target);
-
-        //文件上传的位置可以自定义
-        String filePath = target + File.separator + "extras/images" + File.separator + imagesName;
-
-        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", imagesName.substring(0, imagesName.lastIndexOf(".")));
-        visionJson.put("group", 1);
-        visionJson.put("subgroup", 0);
-        visionArray.add(visionJson);
-
-        JSONObject vision = new JSONObject();
-        vision.put("sweepLocations", visionArray);
-        FileUtils.writeFile(target + "/extras" + File.separator + "vision.txt", new String(vision.toString().getBytes(), "UTF-8"));
-
-        //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);
-            //V5表示不需要生成high,low文件
-            dataJson.put("skybox_type", "SKYBOX_V6");
-            if(scenePro.getSceneScheme() == 11){
-                dataJson.put("skybox_type", "SKYBOX_V7");
-            }
-            dataJson.put("split_type", "SPLIT_V8");
-            if(scenePro.getSceneScheme() == 3){
-                dataJson.put("skybox_type", "SKYBOX_V4");
-            }
-            FileUtils.writeFile(target + File.separator+"data.json", new String(dataJson.toString().getBytes(), "UTF-8"));
-        }
-
-        //创建文件夹软连接并且复制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 + "results").exists()){
-            FileUtils.delAllFile(target + File.separator + "results");
-        }
-        //下载data.fdage
-        if("aws".equals(this.type)){
-            //亚马逊保持旧方式,超链接capture
-            CreateObjUtil.createSoftConnection(path + File.separator + "capture", target + File.separator + "capture");
-        }
-        CreateObjUtil.ossUtilCp(ConstantFilePath.OSS_PREFIX + path.replace(ConstantFilePath.BUILD_MODEL_PATH, "") + "/data.fdage", target + File.separator + "capture/");
-
-        CreateObjUtil.build3dModel(target , "1");
-
-        //读取upload文件,检验需要上传的文件是否存在
-        String uploadData = FileUtils.readFile(target + File.separator + "results" +File.separator+"upload.json");
-        JSONObject uploadJson = null;
-        JSONArray array = null;
-        if(uploadData!=null) {
-            uploadJson = JSONObject.parseObject(uploadData);
-            array = uploadJson.getJSONArray("upload");
-        }
-        if(array == null){
-            log.info("计算全景图失败,没有upload.json");
-        }
-        Map<String, String> map = new HashMap<>();
-        JSONObject fileJson = null;
-        String fileName = "";
-        String img = "";
-        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+"文件不存在");
-            }
-
-            //low文件夹
-            if(fileJson.getIntValue("clazz") == 4){
-                img = "/results" + File.separator + fileName;
-
-                map.put(target + File.separator + "results" +File.separator+ fileName,"images/images"+
-                        scenePro.getNum()+"/pan/low/"+ fileName.replace("low/", ""));
-                continue;
-            }
-
-            //tiles文件夹,亚马逊没有裁剪图片api,不需要上传4k图
-            if(fileJson.getIntValue("clazz") == 5){
-                map.put(target + File.separator + "results" + File.separator+ fileName,"images/images"+
-                        scenePro.getNum()+ "/panorama_edit/" + sid + File.separator + fileName);
-            }
-
-            //tiles文件夹,亚马逊瓦片图
-            if(fileJson.getIntValue("clazz") == 7 ){
-                map.put(target + File.separator + "results" + File.separator+ fileName,"images/images"+
-                        scenePro.getNum()+ File.separator + fileName);
-            }
-
-        }
-
-        //上传全景图
-        map.put(filePath, "images/images"+ scenePro.getNum()+ File.separator + "pan/high/" + imagesName);
-
-        uploadToOssUtil.uploadMulFiles(map);
-
-        String strsceneInfos = FileUtils.readFile(dataBuffer.toString() + "scene.json");
-        JSONObject scenejson = new JSONObject();
-        if(strsceneInfos!=null) {
-            scenejson = JSONObject.parseObject(strsceneInfos);
-        }
-
-        scenejson.put("uploadPanoramaImg", 1);
-        scenejson.put("version", scenejson.getIntValue("version") + 1);
-        FileUtils.writeFile(dataBuffer.toString() + "scene.json", scenejson.toString());
-
-        Map<String, String> result = new HashMap<>();
-        result.put("type", scenePro.getSceneScheme() == 11? "2k" : "4k");
-        result.put("img", "scene/" + imagesBuf.toString() + "panorama/" + sid + img);
-        result.put("path", "images/images"+ scenePro.getNum()+ "/panorama_edit/" + sid);
-        return ResultData.ok(result);
-    }
-
-    @Override
-    public ResultData savePanoramaJson(FileParamVO param) throws Exception {
-        String sceneNum = param.getNum();
-        String fileName = param.getFileName();
-        String fileData = param.getFileData();
-        String sid = param.getSid();
-        if(StrUtil.isEmpty(sceneNum) || StrUtil.isEmpty(fileName)){
-            throw new BusinessException(ErrorCode.PARAM_REQUIRED);
-        }
-
-        ScenePro scenePro = baseMapper.findByNum(sceneNum);
-        if(scenePro == null){
-            throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
-        }
-
-        StringBuffer dataBuf = new StringBuffer()
-                .append("data").append(File.separator)
-                .append("data").append(scenePro.getNum())
-                .append(File.separator);
-
-        StringBuffer imagesBuf = new StringBuffer()
-                .append("images").append(File.separator)
-                .append("images").append(scenePro.getNum())
-                .append(File.separator);
-
-        StringBuffer dataBuffer = new StringBuffer(ConstantFilePath.SCENE_PATH).append(dataBuf.toString());
-        StringBuffer imagesBuffer = new StringBuffer(ConstantFilePath.SCENE_PATH).append(imagesBuf.toString());
-
-        FileUtils.writeFile(dataBuffer.toString() + fileName, fileData);
-
-        String strsceneInfos = FileUtils.readFile(dataBuffer.toString() + "scene.json");
-        JSONObject scenejson = new JSONObject();
-        if(strsceneInfos!=null) {
-            scenejson = JSONObject.parseObject(strsceneInfos);
-        }
-
-        scenejson.put("uploadPanoramaJson", 1);
-        scenejson.put("version", scenejson.getIntValue("version") + 1);
-        if(StrUtil.isNotEmpty(fileData)){
-            scenejson.put("jumpScene", true);
-        }else {
-            scenejson.put("jumpScene", false);
-        }
-        FileUtils.writeFile(dataBuffer.toString() + "scene.json", scenejson.toString());
-
-        if(StrUtil.isNotEmpty(sid)){
-            String target = imagesBuffer.toString() + "panorama/" + sid;
-            FileUtils.deleteDirectory(target);
-            uploadToOssUtil.deleteFile(imagesBuf.toString() + "panorama_edit/" + sid);
-        }
-
-        return ResultData.ok();
-    }
-
-    @Override
     public ResultData copyAndUpdateFloorJson(FileParamVO param) throws Exception{
         String sceneNum = param.getNum();
         String floorJsonData = param.getFloorJsonData();

+ 98 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/impl/SceneServiceImpl.java

@@ -33,6 +33,7 @@ import com.fdkankan.scene.mapper.ISceneProMapper;
 import com.fdkankan.scene.service.*;
 import com.fdkankan.scene.vo.*;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.joda.time.DateTime;
 import org.springframework.beans.BeanUtils;
@@ -86,6 +87,22 @@ public class SceneServiceImpl extends ServiceImpl<ISceneMapper, Scene> implement
     PlatformUserClient platformUserClient;
     @Autowired
     ISceneService sceneService;
+
+    @Autowired
+    private IScenePlusService scenePlusService;
+
+    @Autowired
+    private IScenePlusExtService scenePlusExtService;
+
+    @Autowired
+    private ISceneEditInfoService sceneEditInfoService;
+
+    @Autowired
+    private ISceneEditInfoExtService sceneEditInfoExtService;
+
+    @Autowired
+    private ISceneEditControlsService sceneEditControlsService;
+
     @Autowired
     private ISceneDataDownloadService sceneDataDownloadService;
 
@@ -1319,4 +1336,85 @@ public class SceneServiceImpl extends ServiceImpl<ISceneMapper, Scene> implement
         }
         return this.count(queryWrapper);
     }
+
+    @Override
+    public void copyScene(CopySceneParamVO paramVO) throws IOException {
+        String num = paramVO.getOldNum();
+        String newNum = paramVO.getNewNum();
+
+        ScenePro scenePro = sceneProService.findBySceneNum(num);
+        if (ObjectUtils.isEmpty(scenePro)) {
+            return;
+        }
+
+        // 拷贝场景编辑资源
+        String oldEditPath = String.format(UploadFilePath.EDIT_PATH, num);
+
+        String newEditPath = String.format(UploadFilePath.EDIT_PATH, newNum);
+        uploadToOssUtil.copyFiles(oldEditPath,newEditPath);
+
+        // 拷贝场景展示资源
+        String oldViewPath = String.format(UploadFilePath.VIEW_PATH, num);
+        String newViewPath = String.format(UploadFilePath.VIEW_PATH, newNum);
+        uploadToOssUtil.copyFiles(oldViewPath,newViewPath);
+
+        // 拷贝本地资源
+        String oldPath = String.format("/mnt/4Dkankan/scene/%s/caches/images/", num);
+        String newPath = String.format("/mnt/4Dkankan/scene/%s/caches/images/", newNum);
+        FileUtils.copyDirectiory(oldPath,newPath);
+
+        // 拷贝数据
+        Long proId = scenePro.getId();
+        scenePro.setId(paramVO.getNewSceneProId());
+        scenePro.setWebSite(scenePro.getWebSite().replace(num,newNum));
+        scenePro.setSceneName(paramVO.getNewSceneName());
+        scenePro.setThumb(scenePro.getThumb().replace(num,newNum));
+        scenePro.setVideos(scenePro.getVideos().replaceAll("https://4dkk.4dage.com/data/data"+num,"https://4dkk.4dage.com/scene_view_data/"+newNum+"/data"));
+        scenePro.setNum(newNum);
+        sceneProService.saveOrUpdate(scenePro);
+
+        SceneProExt proExt = sceneProExtService.findBySceneProId(proId);
+        proExt.setDataSource(paramVO.getDatasource());
+        proExt.setId(null);
+        proExt.setSceneProId(scenePro.getId());
+        sceneProExtService.save(proExt);
+
+        ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num);
+        Long plusId = scenePlus.getId();
+        scenePlus.setNum(newNum);
+        scenePlus.setId(paramVO.getNewSceneProId());
+        scenePlus.setTitle(scenePro.getSceneName());
+        scenePlusService.saveOrUpdate(scenePlus);
+
+        ScenePlusExt plusExt = scenePlusExtService.getScenePlusExtByPlusId(plusId);
+        plusExt.setId(null);
+        plusExt.setPlusId(scenePlus.getId());
+        plusExt.setDataSource(paramVO.getDatasource());
+        plusExt.setWebSite(plusExt.getWebSite().replace(num,newNum));
+        plusExt.setThumb(plusExt.getThumb().replace(num,newNum));
+        plusExt.setVideos(plusExt.getVideos().replace(num,newNum));
+        scenePlusExtService.save(plusExt);
+
+        SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(plusId);
+        Long sceneEditInfoId = sceneEditInfo.getId();
+
+        sceneEditInfo.setId(null);
+        sceneEditInfo.setScenePlusId(scenePlus.getId());
+        sceneEditInfo.setSceneProId(scenePro.getId());
+        sceneEditInfo.setTitle(paramVO.getNewSceneName());
+        sceneEditInfoService.save(sceneEditInfo);
+
+        SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByEditInfoId(sceneEditInfoId);
+        sceneEditInfoExt.setId(null);
+        sceneEditInfoExt.setEditInfoId(sceneEditInfo.getId());
+        sceneEditInfoExt.setScenePlusId(scenePlus.getId());
+        sceneEditInfoExt.setSceneProId(scenePro.getId());
+        sceneEditInfoExtService.save(sceneEditInfoExt);
+
+        SceneEditControls sceneEditControls = sceneEditControlsService.getBySceneEditId(sceneEditInfoId);
+        sceneEditControls.setId(null);
+        sceneEditControls.setEditInfoId(sceneEditInfo.getId());
+        sceneEditControlsService.save(sceneEditControls);
+
+    }
 }

+ 43 - 8
4dkankan-center-scene/src/main/java/com/fdkankan/scene/service/impl/SceneUpgradeToV4Service.java

@@ -1,5 +1,6 @@
 package com.fdkankan.scene.service.impl;
 
+import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -16,6 +17,7 @@ import com.fdkankan.redis.constant.RedisLockKey;
 import com.fdkankan.redis.util.RedisLockUtil;
 import com.fdkankan.redis.util.RedisUtil;
 import com.fdkankan.scene.bean.SceneUpgradeProgressBean;
+import com.fdkankan.scene.bean.UpgradeBean;
 import com.fdkankan.scene.entity.ScenePlus;
 import com.fdkankan.scene.entity.ScenePlusExt;
 import com.fdkankan.scene.entity.ScenePro;
@@ -38,6 +40,7 @@ import com.fdkankan.scene.service.ISceneRepairLogService;
 import com.fdkankan.scene.service.ISceneUpgradeToV4Service;
 import com.fdkankan.scene.vo.SceneInfoVO;
 import com.fdkankan.scene.vo.UpgradeToV4ParamVO;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -105,24 +108,28 @@ public class SceneUpgradeToV4Service implements ISceneUpgradeToV4Service {
             return ResultData.error(ErrorCode.FAILURE_CODE_7019);
         }
 
+        //查询升级日志,如果已经升级成功,不允许重复升级
+        SceneRepairLog sceneRepairLog = null;
         try {
-            //查询升级日志,如果已经升级成功,不允许重复升级
-            SceneRepairLog sceneRepairLog = sceneRepairLogService.getOne(
+            sceneRepairLog = sceneRepairLogService.getOne(
                 new LambdaQueryWrapper<SceneRepairLog>()
-                    .eq(SceneRepairLog::getNum, sceneProV3.getNum()));
+                    .eq(SceneRepairLog::getNum, sceneProV3.getNum())
+                    .orderByDesc(SceneRepairLog::getId)
+                    .last("limit 1"));
             if(Objects.nonNull(sceneRepairLog)){
                 if(sceneRepairLog.getState() == 0){
-                    redisLockUtil.unlockLua(lockKey);
                     return ResultData.error(ErrorCode.FAILURE_CODE_7019);
                 }
                 if(!param.isReUpgrade()){
                     if(sceneRepairLog.getState() == 1){
-                        redisLockUtil.unlockLua(lockKey);
                         return ResultData.error(ErrorCode.FAILURE_CODE_7020);
                     }
                 }
             }
 
+            //写入升级日志
+            this.upgradeLog(null, sceneProV3.getNum(), 0, null);
+
             //写入sceneProV3
             sceneProV3Service.saveOrUpdate(sceneProV3);
 
@@ -142,14 +149,23 @@ public class SceneUpgradeToV4Service implements ISceneUpgradeToV4Service {
             sceneUpgradeMapper.transferSceneProExt(sceneProV3.getId(),
                 sceneProV3.getSceneScheme() == 3 ? SceneKind.FACE.code():SceneKind.TILES.code());
 
+            String thumb = null;
+            if(param.isReUpgrade()){
+                ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(sceneProV3.getId());
+                thumb = scenePlusExt.getThumb();
+            }
+
             //同步到scenePlus、scenePlus
             sceneUpgradeMapper.deleteScenePlus(sceneProV3.getId());
             sceneUpgradeMapper.transferScenePlus(sceneProV3.getId());
             sceneUpgradeMapper.deleteScenePlusExt(sceneProV3.getId());
             sceneUpgradeMapper.transferScenePlusExt(sceneProV3.getId());
-
-            //发送mq
-            rabbitMqProducer.sendByWorkQueue(upgradeToV4, sceneProV3.getId());
+            //重算自动升级的,需要回复原初始画面图
+            if(param.isReUpgrade()){
+                ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(sceneProV3.getId());
+                scenePlusExt.setThumb(thumb);
+                scenePlusExtService.updateById(scenePlusExt);
+            }
 
             //写入进度条
             Map<String, Integer> progress = new HashMap<>();
@@ -157,14 +173,33 @@ public class SceneUpgradeToV4Service implements ISceneUpgradeToV4Service {
             progress.put("progress", 0);
             redisUtil.set(RedisKey.scene_upgrade_progress_num, JSON.toJSONString(progress));
 
+            //发送mq
+            HashMap<String, Object> msg = new HashMap<>();
+            msg.put("sceneProId", sceneProV3.getId());
+            msg.put("reUpgrade", param.isReUpgrade());
+            rabbitMqProducer.sendByWorkQueue(upgradeToV4, msg);
+
         }catch (Exception e){
             log.error("场景升级失败", e);
+            this.upgradeLog(sceneRepairLog, sceneProV3.getNum(), 2, ExceptionUtil.stacktraceToString(e, 3000));
             return ResultData.error(ErrorCode.FAILURE_CODE_7021);
         }
 
         return ResultData.ok();
     }
 
+    private void upgradeLog(SceneRepairLog sceneRepairLog, String num, int status, String reason){
+        //写入升级日志
+        if(Objects.isNull(sceneRepairLog)){
+            sceneRepairLog = new SceneRepairLog();
+            sceneRepairLog.setNum(num);
+        }
+        sceneRepairLog.setState(status);
+        sceneRepairLog.setReason(reason);
+        sceneRepairLog.setCreateTime(Calendar.getInstance().getTime());
+        sceneRepairLogService.saveOrUpdate(sceneRepairLog);
+    }
+
     @Override
     public ResultData getUpgradeToV4Progress(String num) {
 

+ 32 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/CopySceneParamVO.java

@@ -0,0 +1,32 @@
+package com.fdkankan.scene.vo;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * <p>
+ * TODO
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/4/21
+ **/
+@Data
+public class CopySceneParamVO {
+
+    @NotNull(message = "oldNum不能为空")
+    private String oldNum;
+
+    @NotNull(message = "newSceneProId不能为空")
+    private Long newSceneProId;
+
+    @NotNull(message = "newNum不能为空")
+    private String newNum;
+
+    @NotNull(message = "datasource不能为空")
+    private String datasource;
+
+    @NotNull(message = "newSceneName不能为空")
+    private String newSceneName;
+}

+ 27 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/DeleteLinkPanParamVO.java

@@ -0,0 +1,27 @@
+package com.fdkankan.scene.vo;
+
+import java.util.List;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * <p>
+ * 删除场景关联数据参数类
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/2/8
+ **/
+@Data
+public class DeleteLinkPanParamVO {
+
+    @NotBlank(message = "场景码不能为空")
+    private String num;
+
+    @NotNull(message = "sid不能为空")
+    private List<String> sidList;
+
+
+
+}

+ 27 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/DeleteLinkSceneStylesParamVO.java

@@ -0,0 +1,27 @@
+package com.fdkankan.scene.vo;
+
+import java.util.List;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * <p>
+ * 删除场景关联图标参数类
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/2/8
+ **/
+@Data
+public class DeleteLinkSceneStylesParamVO {
+
+    @NotBlank(message = "场景码不能为空")
+    private String num;
+
+    @NotNull(message = "sidList不能为空")
+    private List<String> sidList;
+
+
+
+}

+ 25 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/LinkPanParamVO.java

@@ -0,0 +1,25 @@
+package com.fdkankan.scene.vo;
+
+import com.alibaba.fastjson.JSONObject;
+import javax.validation.constraints.NotBlank;
+import lombok.Data;
+
+/**
+ * <p>
+ * 场景关联点参数
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/2/8
+ **/
+@Data
+public class LinkPanParamVO {
+
+    @NotBlank(message = "sid不能为空")
+    private String sid;
+
+    private JSONObject linkPanData;
+
+
+
+}

+ 34 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/SaveLinkPanParamVO.java

@@ -0,0 +1,34 @@
+package com.fdkankan.scene.vo;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import java.util.List;
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * <p>
+ * 保存场景关联参数
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/2/8
+ **/
+@Data
+public class SaveLinkPanParamVO {
+
+    @NotBlank(message = "场景码不能为空")
+    private String num;
+
+//    @Valid
+    @NotNull(message = "linkPans不能为空")
+//    private List<LinkPanParamVO> linkPans;
+    private List<JSONObject> linkPans;
+
+    private List<JSONObject> styles;
+
+
+
+}

+ 5 - 0
4dkankan-center-scene/src/main/java/com/fdkankan/scene/vo/SceneInfoVO.java

@@ -177,6 +177,11 @@ public class SceneInfoVO {
      */
     private String mosaics;
 
+    /**
+     * 是否有场景关联(0-否,1-是)
+     */
+    private Integer links;
+
 
 
 

+ 5 - 5
4dkankan-center-scene/src/main/resources/bootstrap-dev-eur.yml

@@ -4,9 +4,9 @@ spring:
   cloud:
     nacos:
       config:
-        server-addr: 192.168.0.47:8848
+        server-addr: 120.24.144.164:8848
         file-extension: yaml
-        namespace: 4dkankan-dev
+        namespace: 4dkankan-dev-eur
         extension-configs:
           - data-id: 4dkankan-center-scene.yaml
             group: DEFAULT_GROUP
@@ -32,11 +32,11 @@ spring:
             group: DEFAULT_GROUP
             refresh: true
       discovery:
-        server-addr: 192.168.0.47:8848
-        namespace: 4dkankan-dev
+        server-addr: 120.24.144.164:8848
+        namespace: 4dkankan-dev-eur
     sentinel:
       transport:
-        dashboard: 192.168.0.47:8888
+        dashboard: 120.24.144.164:8888
         heartbeat-interval-ms: 500
         port: 8719
       eager: true #取消sentinel控制台懒加载

+ 3 - 3
4dkankan-center-scene/src/main/resources/bootstrap-dev.yml

@@ -4,7 +4,7 @@ spring:
   cloud:
     nacos:
       config:
-        server-addr: 192.168.0.47:8848
+        server-addr: 120.24.144.164:8848
         file-extension: yaml
         namespace: 4dkankan-dev
         extension-configs:
@@ -32,11 +32,11 @@ spring:
             group: DEFAULT_GROUP
             refresh: true
       discovery:
-        server-addr: 192.168.0.47:8848
+        server-addr: 120.24.144.164:8848
         namespace: 4dkankan-dev
     sentinel:
       transport:
-        dashboard: 192.168.0.47:8888
+        dashboard: 120.24.144.164:8888
         heartbeat-interval-ms: 500
         port: 8719
       eager: true #取消sentinel控制台懒加载

+ 4 - 0
4dkankan-common/src/main/java/com/fdkankan/common/controller/BaseController.java

@@ -46,6 +46,10 @@ public class BaseController {
         return null;
     }
 
+    protected com.fdkankan.common.model.SSOUser getSsoUserV3(){
+        return ssoLoginHelper.loginCheckV3(request.getHeader("token"));
+    }
+
     public static void output(HttpServletResponse resp, File file) {
         OutputStream os = null;
         BufferedInputStream bis = null;