|
@@ -0,0 +1,326 @@
|
|
|
|
+package com.gis.listener;
|
|
|
|
+
|
|
|
|
+import cn.hutool.core.io.FileUtil;
|
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
|
+import com.gis.constant.CmdConstant;
|
|
|
|
+import com.gis.constant.ConfigConstant;
|
|
|
|
+import com.gis.entity.ScenePanoEntity;
|
|
|
|
+import com.gis.entity.SceneQueueDTO;
|
|
|
|
+import com.gis.entity.WorkEntity;
|
|
|
|
+import com.gis.exception.BaseRuntimeException;
|
|
|
|
+import com.gis.oss.util.AliYunOssUtil;
|
|
|
|
+import com.gis.oss.util.FileAndOssUtil;
|
|
|
|
+import com.gis.service.ScenePanoService;
|
|
|
|
+import com.gis.service.WorkService;
|
|
|
|
+import com.gis.util.CmdUtils;
|
|
|
|
+import com.gis.util.FileUtils;
|
|
|
|
+import com.gis.util.QrCodeUtils;
|
|
|
|
+import com.gis.util.RandomUtils;
|
|
|
|
+import com.rabbitmq.client.Channel;
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import org.slf4j.MDC;
|
|
|
|
+import org.springframework.amqp.core.Message;
|
|
|
|
+import org.springframework.amqp.rabbit.annotation.Queue;
|
|
|
|
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
+import org.springframework.util.ObjectUtils;
|
|
|
|
+
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.io.PrintWriter;
|
|
|
|
+import java.io.StringWriter;
|
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
+import java.util.*;
|
|
|
|
+import java.util.concurrent.*;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 消息监听器
|
|
|
|
+ *
|
|
|
|
+ * @author Xiewj
|
|
|
|
+ * @version 1.0
|
|
|
|
+ * @since 2023/08/07
|
|
|
|
+ */
|
|
|
|
+@Component
|
|
|
|
+@Slf4j
|
|
|
|
+public class SceneListener {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ AliYunOssUtil aliyunOssUtil;
|
|
|
|
+ @Autowired
|
|
|
|
+ ConfigConstant configConstant;
|
|
|
|
+ @Autowired
|
|
|
|
+ FileAndOssUtil fileAndOssUtil;
|
|
|
|
+ @Autowired
|
|
|
|
+ WorkService workService;
|
|
|
|
+ @Autowired
|
|
|
|
+ QrCodeUtils qrCodeUtils;
|
|
|
|
+ @Autowired
|
|
|
|
+ ScenePanoService scenePanoService;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 全景场景初始化方法
|
|
|
|
+ *
|
|
|
|
+ * @param channel
|
|
|
|
+ * @param message
|
|
|
|
+ * @throws Exception the io exception 这里异常需要处理
|
|
|
|
+ */
|
|
|
|
+ @RabbitListener(
|
|
|
|
+ queuesToDeclare = @Queue("${queue.scene-queue}"),concurrency = "1"
|
|
|
|
+ )
|
|
|
|
+ public void sceneQueue(Channel channel, Message message) throws IOException {
|
|
|
|
+ if (ObjectUtils.isEmpty(message.getBody())) {
|
|
|
|
+ log.error("消息内容为空,退出构建,当前服务器id:{}" );
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ String traceId = System.currentTimeMillis()+"";
|
|
|
|
+ MDC.put("TRACE_ID", traceId);
|
|
|
|
+
|
|
|
|
+ long deliveryTag = message.getMessageProperties().getDeliveryTag();
|
|
|
|
+ try {
|
|
|
|
+ String msg = new String(message.getBody(), StandardCharsets.UTF_8);
|
|
|
|
+ String messageId = message.getMessageProperties().getMessageId();
|
|
|
|
+ log.info("场景sceneQueue开始,id:{},deliveryTag:{},消息体:{}", messageId,deliveryTag,msg);
|
|
|
|
+ SceneQueueDTO param = JSONObject.parseObject(msg, SceneQueueDTO.class);
|
|
|
|
+ if (ObjectUtil.isEmpty(param.getSceneCode())) {
|
|
|
|
+ log.error("参数列表错误(缺少),格式不匹配,{}",param);
|
|
|
|
+ channel.basicAck(deliveryTag, false);
|
|
|
|
+ }
|
|
|
|
+ if (ObjectUtil.isNotEmpty(param.getSceneCode())&¶m.getStatus()==0){
|
|
|
|
+ //初始化创建work表数据
|
|
|
|
+ workService.entityAdd(param.getPhoneNum(),param.getSceneCode(),param.getStatus(),param.getSceneName());
|
|
|
|
+ channel.basicAck(deliveryTag, false);
|
|
|
|
+ }else {
|
|
|
|
+ //计算完成处理
|
|
|
|
+ WorkEntity workEntity=workService.findByNum(param.getSceneCode());
|
|
|
|
+ //处理业务
|
|
|
|
+ //1,下载场景的全景图 默认 。scene_view_data/panorama/目录下的全景图图片
|
|
|
|
+ String ossPath = "/scene_view_data/"+param.getSceneCode()+"/panorama/";
|
|
|
|
+ List<String> list=aliyunOssUtil.getFileFolder("4dkankan",ossPath);
|
|
|
|
+// workEntity.setIcon(someDataJson.getString("icon"));
|
|
|
|
+// workEntity.setShare(someDataJson.getString("share"));
|
|
|
|
+// workEntity.setQrCode(someDataJson.getString("qrCode"));
|
|
|
|
+
|
|
|
|
+ //2,使用krpano工具生成全景图数据
|
|
|
|
+ List<ScenePanoEntity> scenePanoEntities=new ArrayList<>();
|
|
|
|
+ List<Map<String,Object>> scenes=new ArrayList<>();
|
|
|
|
+ list.forEach(item->{
|
|
|
|
+ log.info("key:{}",item);
|
|
|
|
+ Map<String,Object> scene=new HashMap<>();
|
|
|
|
+ String sceneCode = RandomUtils.getSceneCode("fd720_");
|
|
|
|
+ String newName = sceneCode + ".jpg";
|
|
|
|
+ String savePath = configConstant.serverBasePath + sceneCode+ "/" + newName;
|
|
|
|
+ aliyunOssUtil.downloadFile("4dkankan",item,savePath );
|
|
|
|
+ ScenePanoEntity scenePanoEntity = this.doSlice(traceId, savePath, sceneCode, param.getSceneCode(), workEntity.getId());
|
|
|
|
+ scenePanoEntities.add(scenePanoEntity);
|
|
|
|
+
|
|
|
|
+ // "icon": "https://ossxiaoan.4dage.com/720yun_fd_manage/fd720_8nRkFlzpp/vtour/panos/fd720_8nRkFlzpp.tiles/thumb.jpg",
|
|
|
|
+ // "sceneCode": "fd720_8nRkFlzpp",
|
|
|
|
+ // "sceneTitle": "0",
|
|
|
|
+ // "category": 1,
|
|
|
|
+ // "type": "pano",
|
|
|
|
+ // "id": "s_xId8vevp",
|
|
|
|
+ scene.put("icon", scenePanoEntity.getIcon());
|
|
|
|
+ scene.put("sceneCode", scenePanoEntity.getSceneCode());
|
|
|
|
+ scene.put("sceneTitle", param.getSceneName());
|
|
|
|
+ scene.put("category", 1);
|
|
|
|
+ scene.put("type", "pano");
|
|
|
|
+ //id生成规则是 s_ 拼接,随机8位字符串,字母加数字组合,有大小写
|
|
|
|
+ String customId = generateCustomId();
|
|
|
|
+ scene.put("id", customId);
|
|
|
|
+ scenes.add(scene);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ log.info("场景切图完成组装json");
|
|
|
|
+ //3,修改json文件,并创建work作品表数据,增加作品类型为相机拍摄全景图
|
|
|
|
+ this.editSomeData(workEntity.getId(),scenes);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ channel.basicAck(deliveryTag, false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }catch (Exception e){
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ channel.basicAck(deliveryTag, false);
|
|
|
|
+ log.error("场景sceneQueue报错{}",e.getMessage());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
|
|
+
|
|
|
|
+ public static String generateRandomId() {
|
|
|
|
+ Random random = new Random();
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
+ for (int i = 0; i < 8; i++) {
|
|
|
|
+ int index = random.nextInt(CHARACTERS.length());
|
|
|
|
+ sb.append(CHARACTERS.charAt(index));
|
|
|
|
+ }
|
|
|
|
+ return sb.toString();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static String generateCustomId() {
|
|
|
|
+ return "s_" + generateRandomId();
|
|
|
|
+ }
|
|
|
|
+ private ScenePanoEntity doSlice(String traceId,String filePath,String sceneCode,String num,String workId){
|
|
|
|
+ ScenePanoEntity entity = new ScenePanoEntity();
|
|
|
|
+ entity.setFilePath(filePath);
|
|
|
|
+ entity.setFileName(FileUtil.getName(filePath));
|
|
|
|
+ entity.setSceneCode(num);
|
|
|
|
+ entity.setWorkId(workId);
|
|
|
|
+ scenePanoService.save(entity);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ log.info("切图流程开始 : " + filePath);
|
|
|
|
+ long start = System.currentTimeMillis();
|
|
|
|
+
|
|
|
|
+ String cmd = CmdConstant.PANO_KRPANO + filePath;
|
|
|
|
+ // 超时处理机制
|
|
|
|
+ final ExecutorService exec = Executors.newFixedThreadPool(1);
|
|
|
|
+ Callable<String> call = new Callable<String>() {
|
|
|
|
+ @Override
|
|
|
|
+ public String call() throws Exception {
|
|
|
|
+ MDC.put("TRACE_ID", traceId);
|
|
|
|
+ CmdUtils.callLine(cmd, 200);
|
|
|
|
+ long end = System.currentTimeMillis();
|
|
|
|
+ log.info("切图完成耗时: {} s" ,(end-start)/1000);
|
|
|
|
+ return "执行完成";
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ // 超时回调
|
|
|
|
+ Future<String> future = exec.submit(call);
|
|
|
|
+ try {
|
|
|
|
+ String obj = future.get(20, TimeUnit.MINUTES); //任务处理超时时间设为 3分钟
|
|
|
|
+ entity.setStatus(3);
|
|
|
|
+ String tourXmlScene = this.getTourXmlScene(sceneCode);
|
|
|
|
+ entity.setTour(tourXmlScene);
|
|
|
|
+ // 上传切图
|
|
|
|
+ this.uploadOss(sceneCode);
|
|
|
|
+ String ossUrl = configConstant.ossDomain + configConstant.ossBasePath + sceneCode+"/vtour/panos/"+sceneCode+".tiles/thumb.jpg";
|
|
|
|
+ log.info("切图预览图ossUrl: {}",ossUrl);
|
|
|
|
+ entity.setIcon(ossUrl);
|
|
|
|
+ log.info("任务成功返回: " + obj);
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ entity.setStatus(2);
|
|
|
|
+ StringWriter trace=new StringWriter();
|
|
|
|
+ log.error(trace.toString());
|
|
|
|
+ log.error("异常了 InterruptedException");
|
|
|
|
+ } catch (ExecutionException | BaseRuntimeException e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ StringWriter trace=new StringWriter();
|
|
|
|
+ e.printStackTrace(new PrintWriter(trace));
|
|
|
|
+ log.error("超时了 1");
|
|
|
|
+ // 异常日志要打印,不然不会出现在日志文件中,只会出现在控制台
|
|
|
|
+ log.error(trace.toString());
|
|
|
|
+ entity.setStatus(2);
|
|
|
|
+ future.cancel(true);
|
|
|
|
+ } catch (TimeoutException e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ StringWriter trace=new StringWriter();
|
|
|
|
+ e.printStackTrace(new PrintWriter(trace));
|
|
|
|
+ log.error("超时了 2");
|
|
|
|
+ log.error(trace.toString());
|
|
|
|
+ entity.setStatus(2);
|
|
|
|
+ future.cancel(true);
|
|
|
|
+ } finally {
|
|
|
|
+ scenePanoService.update(entity);
|
|
|
|
+ log.info("切图流程, 更新数据库完成 : ");
|
|
|
|
+ }
|
|
|
|
+ log.info("切图流程结束 : ");
|
|
|
|
+ return entity;
|
|
|
|
+ }
|
|
|
|
+ // 获取切图后的tour.xml
|
|
|
|
+ private String getTourXmlScene(String code) {
|
|
|
|
+ log.info("处理tour.xml");
|
|
|
|
+ String tourPath = configConstant.serverBasePath + code + "/vtour/tour.xml";
|
|
|
|
+ log.info("tourPath: {}", tourPath);
|
|
|
|
+ BaseRuntimeException.isTrue(!FileUtil.isFile(tourPath), null, code + "_tour.xml文件不存在");
|
|
|
|
+
|
|
|
|
+ String tour = FileUtil.readString(tourPath, "utf-8");
|
|
|
|
+ tour = StrUtil.subAfter(tour, "</action>", true);
|
|
|
|
+ tour = StrUtil.subBefore(tour, "</krpano>", true);
|
|
|
|
+ String trim = StrUtil.trim(tour);
|
|
|
|
+ // log.info("trim: {}", trim);
|
|
|
|
+ BaseRuntimeException.isTrue(StrUtil.isAllBlank(trim), null, code + "_tour.xml文件不存在");
|
|
|
|
+
|
|
|
|
+ // 2022-09-15 加入相对路径
|
|
|
|
+ String basePath = "%CURRENTXML%../" + code + "/vtour/panos/";
|
|
|
|
+ trim = trim.replaceAll("panos/", basePath);
|
|
|
|
+
|
|
|
|
+ return trim;
|
|
|
|
+ }
|
|
|
|
+ private void uploadOss(String sceneCode) {
|
|
|
|
+ long start = System.currentTimeMillis();
|
|
|
|
+ String filePath = "/mnt/720yun_fd_manage_data/" + sceneCode;
|
|
|
|
+ String ossKey = "/720yun_fd_manage/" + sceneCode;
|
|
|
|
+ fileAndOssUtil.uploadBySh(filePath, ossKey);
|
|
|
|
+ long end = System.currentTimeMillis();
|
|
|
|
+ log.info("切图上传完成, 耗时:{} s" , (end-start)/1000 );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private void editSomeData(String id, List<Map<String, Object>> scenes){
|
|
|
|
+ // 如基someData作出修改,记得把代码里的someData 跟服务器里的someData都修改
|
|
|
|
+ String baseSomeDataPath = configConstant.serverBasePath + "baseData/someData.json";
|
|
|
|
+ String baseSomeDataSceneDataPath = configConstant.serverBasePath + "baseData/someDataSceneData.json.json";
|
|
|
|
+ log.info("服务器base someData.json path: {}", baseSomeDataPath);
|
|
|
|
+ // someData.json 内容
|
|
|
|
+ String content = null;
|
|
|
|
+ if (FileUtil.isFile(baseSomeDataPath)) {
|
|
|
|
+ log.info("使用服务器someData.json");
|
|
|
|
+ content = FileUtil.readUtf8String(baseSomeDataPath);
|
|
|
|
+ } else {
|
|
|
|
+ // 获取文件内容
|
|
|
|
+ log.info("创建someData.json");
|
|
|
|
+ content = FileUtils.getResourceContent("data/someData.json");
|
|
|
|
+ FileUtil.writeUtf8String(content, baseSomeDataPath);
|
|
|
|
+ }
|
|
|
|
+ String someDataSceneDatContent = null;
|
|
|
|
+ if (FileUtil.isFile(baseSomeDataSceneDataPath)) {
|
|
|
|
+ log.info("使用服务器SomeDataSceneData.json");
|
|
|
|
+ someDataSceneDatContent = FileUtil.readUtf8String(baseSomeDataSceneDataPath);
|
|
|
|
+ } else {
|
|
|
|
+ // 获取文件内容
|
|
|
|
+ log.info("创建SomeDataSceneData.json");
|
|
|
|
+ someDataSceneDatContent = FileUtils.getResourceContent("data/someData.json");
|
|
|
|
+ FileUtil.writeUtf8String(someDataSceneDatContent, baseSomeDataSceneDataPath);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(content);
|
|
|
|
+ jsonObject.put("id", id + "");
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // 创建二维码、二维码url
|
|
|
|
+ String shareUrl = configConstant.domain4dKK + "/panorama/showMobile.html?id=" + id;
|
|
|
|
+ String qrCode = qrCodeUtils.generateLogoQrCode(shareUrl, configConstant.serverBasePath, configConstant.ossBasePath, configConstant.ossDomain, id);
|
|
|
|
+ jsonObject.put("share", shareUrl);
|
|
|
|
+ jsonObject.put("qrCode", qrCode);
|
|
|
|
+
|
|
|
|
+ String ossKeyPath = configConstant.ossBasePath + id + "/someData.json";
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ JSONObject SomeDataSceneDataJsonObject = JSONObject.parseObject(someDataSceneDatContent);
|
|
|
|
+ JSONArray scenesJson = new JSONArray();
|
|
|
|
+ for (Map<String, Object> scene : scenes) {
|
|
|
|
+ JSONObject newData =SomeDataSceneDataJsonObject;
|
|
|
|
+ newData.putAll(scene);
|
|
|
|
+ scenesJson.add(newData);
|
|
|
|
+ }
|
|
|
|
+ jsonObject.remove("scenes");
|
|
|
|
+ jsonObject.put("scenes", scenesJson);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ fileAndOssUtil.upload(jsonObject.toJSONString().getBytes(), ossKeyPath);
|
|
|
|
+
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+ String ossUrl = configConstant.ossDomain + ossKeyPath;
|
|
|
|
+ log.info("ossSomeData:{}", ossUrl);
|
|
|
|
+ }
|
|
|
|
+}
|