tianboguang 3 éve
commit
4b1336d64e
22 módosított fájl, 3209 hozzáadás és 0 törlés
  1. 125 0
      pom.xml
  2. 23 0
      src/main/java/com/fdkankan/modeling/ModelingApplication.java
  3. 57 0
      src/main/java/com/fdkankan/modeling/bean/BuildSceneResultBean.java
  4. 70 0
      src/main/java/com/fdkankan/modeling/constants/SysConstants.java
  5. 21 0
      src/main/java/com/fdkankan/modeling/controller/BuildLogController.java
  6. 116 0
      src/main/java/com/fdkankan/modeling/entity/BuildLog.java
  7. 19 0
      src/main/java/com/fdkankan/modeling/exception/BuildException.java
  8. 100 0
      src/main/java/com/fdkankan/modeling/generate/AutoGenerate.java
  9. 58 0
      src/main/java/com/fdkankan/modeling/job/EcsJob.java
  10. 18 0
      src/main/java/com/fdkankan/modeling/mapper/IBuildLogMapper.java
  11. 832 0
      src/main/java/com/fdkankan/modeling/receiver/BuildSceneListener.java
  12. 794 0
      src/main/java/com/fdkankan/modeling/receiver/BuildSceneMQListener.java
  13. 533 0
      src/main/java/com/fdkankan/modeling/receiver/RabbitMqListener.java
  14. 16 0
      src/main/java/com/fdkankan/modeling/service/IBuildLogService.java
  15. 20 0
      src/main/java/com/fdkankan/modeling/service/impl/BuildLogServiceImpl.java
  16. 52 0
      src/main/resources/bootstrap-dev.yml
  17. 48 0
      src/main/resources/bootstrap-pro.yml
  18. 48 0
      src/main/resources/bootstrap-test.yml
  19. 3 0
      src/main/resources/bootstrap.yml
  20. 233 0
      src/main/resources/logback-spring.xml
  21. 5 0
      src/main/resources/mapper/modeling/BuildLogMapper.xml
  22. 18 0
      src/test/java/com/fdkankan/modeling/ModelingApplicationTests.java

+ 125 - 0
pom.xml

@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.fdkankan</groupId>
+		<artifactId>4dkankan-parent</artifactId>
+		<version>2.0.0-SNAPSHOT</version>
+	</parent>
+	<artifactId>4dkankan-center-modeling</artifactId>
+	<version>2.0.0-SNAPSHOT</version>
+	<name>4dkankan-center-modeling</name>
+	<description>计算中心</description>
+
+	<properties>
+		<java.version>1.8</java.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.fdkankan</groupId>
+			<artifactId>4dkankan-center-platform-api</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fdkankan</groupId>
+			<artifactId>4dkankan-utils-fyun</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+
+<!--		<dependency>-->
+<!--			<groupId>com.fdkankan</groupId>-->
+<!--			<artifactId>4dkankan-utils-mq</artifactId>-->
+<!--			<version>2.0.0-SNAPSHOT</version>-->
+<!--		</dependency>-->
+
+		<dependency>
+			<groupId>com.fdkankan</groupId>
+			<artifactId>4dkankan-utils-rabbitmq</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.fdkankan</groupId>
+			<artifactId>4dkankan-utils-redis</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<!-- 钉钉sdk -->
+		<dependency>
+			<groupId>com.dingtalk.open</groupId>
+			<artifactId>taobao-sdk-java-auto</artifactId>
+			<version>1479188381469-20211020</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba.cloud</groupId>
+			<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>cn.hutool</groupId>
+			<artifactId>hutool-all</artifactId>
+		</dependency>
+
+		<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
+<!--		<dependency>-->
+<!--			<groupId>com.fasterxml.jackson.core</groupId>-->
+<!--			<artifactId>jackson-annotations</artifactId>-->
+<!--			<version>2.12.3</version>-->
+<!--		</dependency>-->
+
+		<dependency>
+			<artifactId>4dkankan-utils-app-push</artifactId>
+			<groupId>com.fdkankan</groupId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fdkankan</groupId>
+			<artifactId>4dkankan-utils-dingtalk</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.fdkankan</groupId>
+			<artifactId>4dkankan-utils-db</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>de.codecentric</groupId>
+			<artifactId>spring-boot-admin-client</artifactId>
+			<version>2.3.1</version>
+		</dependency>
+
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

+ 23 - 0
src/main/java/com/fdkankan/modeling/ModelingApplication.java

@@ -0,0 +1,23 @@
+package com.fdkankan.modeling;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@EnableScheduling
+@EnableDiscoveryClient
+@EnableFeignClients("com.fdkankan.*.api.feign")
+@ComponentScan(basePackages = {"com.fdkankan.*"})
+@MapperScan("com.fdkankan.**.mapper")
+public class ModelingApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(ModelingApplication.class, args);
+	}
+
+}

+ 57 - 0
src/main/java/com/fdkankan/modeling/bean/BuildSceneResultBean.java

@@ -0,0 +1,57 @@
+package com.fdkankan.modeling.bean;
+
+import com.fdkankan.common.constant.ModelingBuildStatus;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * <p>
+ * TODO
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/4/6
+ **/
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class BuildSceneResultBean {
+
+    private ModelingBuildStatus buildStatus;
+
+    private String resultQueueName;
+
+    private String cameraType;
+
+    private String fileId;
+
+    private int payStatus;
+
+    private String num;
+
+    private Map<String, String> uploadMap;
+
+    private String path;
+
+    private Integer pushChannel;
+
+    private String pushToken;
+
+    private String prefix;
+
+    private Integer videoVersion;
+
+    /**
+     * 计算耗时
+     */
+    private Long duration;
+
+
+
+
+}

+ 70 - 0
src/main/java/com/fdkankan/modeling/constants/SysConstants.java

@@ -0,0 +1,70 @@
+package com.fdkankan.modeling.constants;
+
+import cn.hutool.core.util.StrUtil;
+import com.fdkankan.common.util.FileUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+
+@Component
+public class SysConstants {
+    private static final Logger log = LoggerFactory.getLogger(SysConstants.class);
+
+    public static String hostName;
+    public static int modelTimeOut;
+
+    public static List<String> residenceServiceHostName = new ArrayList<>();
+
+    public static volatile Boolean SYSTEM_BUILDING = false;
+    public static volatile Boolean SYSTEM_OFFING = false;
+
+    public static ExecutorService executorService = Executors.newFixedThreadPool(1);
+
+    public static final String DINGTALK_MSG_PATTERN = "**环境**: %s\n\n" +
+            "**服务器名称**: %s\n\n" +
+            "**失败原因**: %s\n\n" +
+            "**num**: %s\n\n" +
+            "**server-path**: %s\n\n" +
+            "**algorithm-log**: [https://4dkk.4dage.com/build_log/%s/console.log](https://4dkk.4dage.com/build_log/%s/console.log)";
+
+    @Value("${hostName.filePath:/opt/hosts/hosts.txt}")
+    public void setHostName(String filePath){
+        try {
+            SysConstants.hostName = FileUtils.readFile(filePath);
+            //去除换行符
+            if(StrUtil.isNotEmpty(SysConstants.hostName)){
+                SysConstants.hostName = SysConstants.hostName.trim().replaceAll("\\s","");
+            }
+            log.info("从文件({})中获取服务器名称:{}", filePath,hostName);
+        } catch (Exception e) {
+            log.error("从文件中获取服务器名称失败,文件路径{}", filePath);
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 默认超时时间2天
+     * @param timeOut
+     */
+    @Value("${model.timeOut:48}")
+    public void setModelTimeOut(int timeOut){
+        SysConstants.modelTimeOut = timeOut;
+    }
+
+    /**
+     * 常驻服务实例ID集合
+     */
+    @Value("${residenceService.hostNames:#{null}}")
+    public void setResidenceServiceHostName(String[] hostNames){
+        if(!ObjectUtils.isEmpty(hostNames)){
+            SysConstants.residenceServiceHostName = Arrays.asList(hostNames);
+        }
+    }
+}

+ 21 - 0
src/main/java/com/fdkankan/modeling/controller/BuildLogController.java

@@ -0,0 +1,21 @@
+package com.fdkankan.modeling.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author 
+ * @since 2022-04-07
+ */
+@RestController
+@RequestMapping("/modeling/buildLog")
+public class BuildLogController {
+
+}
+

+ 116 - 0
src/main/java/com/fdkankan/modeling/entity/BuildLog.java

@@ -0,0 +1,116 @@
+package com.fdkankan.modeling.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author 
+ * @since 2022-04-07
+ */
+@Getter
+@Setter
+@TableName("t_build_log")
+public class BuildLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 场景码
+     */
+    @TableField("num")
+    private String num;
+
+    /**
+     * 资源路径
+     */
+    @TableField("data_source")
+    private String dataSource;
+
+    /**
+     * 主题名称
+     */
+    @TableField("queue_name")
+    private String queueName;
+
+    /**
+     * -10:其他原因失败
+-3:重复计算
+-2:计算失败
+-1:计算超时
+0:计算中
+1:计算成功
+     */
+    @TableField("build_status")
+    private Integer buildStatus;
+
+    /**
+     * 服务器名称
+     */
+    @TableField("host_name")
+    private String hostName;
+
+    @TableField("build_type")
+    private Integer buildType;
+
+    /**
+     * 相机类型
+     */
+    @TableField("camera_type")
+    private Integer cameraType;
+
+    @TableField("source")
+    private String source;
+
+    /**
+     * java程序异常详情
+     */
+    @TableField("reason")
+    private String reason;
+
+    @TableField("log_detail_path")
+    private String logDetailPath;
+
+    /**
+     * 计算耗时
+     */
+    @TableField("duration")
+    private Long duration;
+
+    /**
+     * 开始计算时间
+     */
+    @TableField("create_time")
+    private Date createTime;
+
+    /**
+     * 结束计算时间
+     */
+    @TableField("update_time")
+    private Date updateTime;
+
+    /**
+     * 0-有效,1-删除
+     */
+    @TableField("tb_status")
+    @TableLogic
+    private Integer tbStatus;
+
+
+}

+ 19 - 0
src/main/java/com/fdkankan/modeling/exception/BuildException.java

@@ -0,0 +1,19 @@
+package com.fdkankan.modeling.exception;
+
+import com.fdkankan.common.constant.ModelingBuildStatus;
+
+public class BuildException extends RuntimeException{
+
+    private static final long serialVersionUID = -1518945670203783450L;
+    private ModelingBuildStatus buildStatus;
+
+
+    public BuildException(ModelingBuildStatus buildStatus){
+        super(buildStatus.message());
+        this.buildStatus = buildStatus;
+    }
+
+    public ModelingBuildStatus getBuildStatus() {
+        return buildStatus;
+    }
+}

+ 100 - 0
src/main/java/com/fdkankan/modeling/generate/AutoGenerate.java

@@ -0,0 +1,100 @@
+package com.fdkankan.modeling.generate;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.generator.FastAutoGenerator;
+import com.baomidou.mybatisplus.generator.config.OutputFile;
+import com.baomidou.mybatisplus.generator.config.rules.DateType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class AutoGenerate {
+
+
+    public static void main(String[] args) {
+
+        System.out.println("相对路径指定到:"+ System.getProperty("user.dir"));
+
+
+        String path =System.getProperty("user.dir") + "\\4dkankan-center-modeling";
+
+        generate(path,"modeling", getTables(new String[]{
+                "t_build_log",
+        }));
+
+//        generate(path,"goods", getTables(new String[]{
+//                        "t_camera","t_camera_detail","t_camera_out","t_camera_space","t_camera_version",
+//                        "t_company","t_goods","t_goods_sku","t_cart","t_goods_spec",
+//                        "t_goods_spec_value","t_goods_spu_spec","t_sn_code"
+//        }));
+//
+//        generate(path,"order", getTables(new String[]{
+//                        "t_increment_order","t_invoice","t_order","t_order_item",
+//                        "t_pre_sale","t_space_sdk","t_trade_log","t_commerce_order","t_download_order","t_expansion_order"
+//        }));
+//        generate(path,"order", getTables(new String[]{
+//                        "t_virtual_order"
+//        }));
+//
+//        generate(path,"user", getTables(new String[]{
+//                        "t_user","t_user_increment","t_manager","t_province","t_increment_type","t_intercom_message","t_receiver_info"
+//        }));
+    }
+
+    public static List<String> getTables(String [] tableNames){
+        return new ArrayList<>(Arrays.asList(tableNames));
+    }
+
+
+    public static void  generate(String path,String moduleName,  List<String> tables){
+        FastAutoGenerator.create("jdbc:mysql://192.168.0.47:13306/4dkankan-center-modeling",
+                "root","4dkk2020cuikuan%")
+                .globalConfig(builder -> {
+                    builder.author("")               //作者
+                            .outputDir(path+"\\src\\main\\java")    //输出路径(写到java目录)
+                            //.enableSwagger()           //开启swagger
+                            .commentDate("yyyy-MM-dd")
+                            .dateType(DateType.ONLY_DATE)
+                            .fileOverride();            //开启覆盖之前生成的文件
+
+                })
+                .packageConfig(builder -> {
+                    builder.parent("com.fdkankan")
+                            .moduleName(moduleName)
+                            .entity("entity")
+                            .service("service")
+                            .serviceImpl("service.impl")
+                            .controller("controller")
+                            .mapper("mapper")
+                            .xml("test.mapper")
+                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml,path+"\\src\\main\\resources\\mapper\\"+moduleName));
+                })
+                .strategyConfig(builder -> {
+                    builder.addInclude(tables)
+                            .addTablePrefix("t_")
+
+                            .serviceBuilder()
+                            .formatServiceFileName("I%sService")
+                            .formatServiceImplFileName("%sServiceImpl")
+
+                            .entityBuilder()
+                            .enableLombok()
+                            .logicDeleteColumnName("tb_status")
+                            .enableTableFieldAnnotation()
+//                            .superClass(BaseEntity.class)
+
+                            .controllerBuilder()
+                            .formatFileName("%sController")
+                            .enableRestStyle()
+
+                            .mapperBuilder()
+                            .superClass(BaseMapper.class)
+                            .formatMapperFileName("I%sMapper")
+                            .enableMapperAnnotation()
+                            .formatXmlFileName("%sMapper");
+                })
+                // .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
+                .execute();
+    }
+}

+ 58 - 0
src/main/java/com/fdkankan/modeling/job/EcsJob.java

@@ -0,0 +1,58 @@
+package com.fdkankan.modeling.job;
+
+import com.fdkankan.common.util.RubberSheetingUtil;
+import com.fdkankan.modeling.constants.SysConstants;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.PostConstruct;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+
+@Component
+public class EcsJob {
+
+    private static final Logger log = LoggerFactory.getLogger(EcsJob.class);
+
+    @Autowired
+    private RubberSheetingUtil rubberSheetingUtil;
+
+    @PostConstruct
+    public void deleteEcs() {
+        log.info("常驻服务列表:{}", SysConstants.residenceServiceHostName);
+        log.info("当前服务名称:{}", SysConstants.hostName);
+        log.info("当前服务是否是常驻服务:{}", SysConstants.residenceServiceHostName.contains(SysConstants.hostName));
+        if (!ObjectUtils.isEmpty(SysConstants.residenceServiceHostName) && SysConstants.residenceServiceHostName.contains(SysConstants.hostName)) {
+            log.info("此服务是常驻服务");
+            return;
+        }
+        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> {
+
+                    // 判断是否有任务执行
+                    if (SysConstants.SYSTEM_BUILDING) {
+                        return;
+                    }
+                    log.info("服务未构建,准备停止服务!");
+                    if (!SysConstants.executorService.isShutdown()) {
+                        SysConstants.SYSTEM_OFFING = true;
+                        if (!SysConstants.SYSTEM_BUILDING) {
+                            SysConstants.executorService.shutdown();
+                            log.error("{} 线程池关闭,不接受新的构建请求!", SysConstants.hostName);
+                        }else{
+                            SysConstants.SYSTEM_OFFING = false;
+                            log.error("{} 服务构建中,退出停止服务请求!", SysConstants.hostName);
+                        }
+                    }
+                    // 没有任务执行时,则退出
+                    if (SysConstants.executorService.isShutdown() && SysConstants.executorService.isTerminated()) {
+                        log.error("{} 请求阿里云关闭弹性伸缩", SysConstants.hostName);
+                        rubberSheetingUtil.deleteEcs(SysConstants.hostName);
+                    } else {
+                        log.error("{} 服务构建中,退出删除程序失败!", SysConstants.hostName);
+                    }
+                },
+                50, 60, TimeUnit.MINUTES);
+    }
+}

+ 18 - 0
src/main/java/com/fdkankan/modeling/mapper/IBuildLogMapper.java

@@ -0,0 +1,18 @@
+package com.fdkankan.modeling.mapper;
+
+import com.fdkankan.modeling.entity.BuildLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author 
+ * @since 2022-04-07
+ */
+@Mapper
+public interface IBuildLogMapper extends BaseMapper<BuildLog> {
+
+}

+ 832 - 0
src/main/java/com/fdkankan/modeling/receiver/BuildSceneListener.java

@@ -0,0 +1,832 @@
+//package com.fdkankan.modeling.receiver;
+//
+//import static com.fdkankan.push.PushMessageConfig.ANDROID_KEY;
+//import static com.fdkankan.push.PushMessageConfig.ANDROID_KEY_Z;
+//import static com.fdkankan.push.PushMessageConfig.ANDROID_SECRET;
+//import static com.fdkankan.push.PushMessageConfig.ANDROID_SECRET_Z;
+//import static com.fdkankan.push.PushMessageConfig.IOS_KEY;
+//import static com.fdkankan.push.PushMessageConfig.IOS_KEY_Z;
+//import static com.fdkankan.push.PushMessageConfig.IOS_SECRET;
+//import static com.fdkankan.push.PushMessageConfig.IOS_SECRET_Z;
+//
+//import cn.hutool.core.collection.CollUtil;
+//import cn.hutool.core.date.DateUtil;
+//import cn.hutool.core.exceptions.ExceptionUtil;
+//import cn.hutool.core.util.StrUtil;
+//import com.alibaba.fastjson.JSONArray;
+//import com.alibaba.fastjson.JSONObject;
+//import com.fdkankan.common.constant.BuildStatus;
+//import com.fdkankan.common.constant.ConstantFilePath;
+//import com.fdkankan.common.constant.ConstantUrl;
+//import com.fdkankan.common.constant.ModelingBuildStatus;
+//import com.fdkankan.common.constant.ServerCode;
+//import com.fdkankan.common.constant.UploadFilePath;
+//import com.fdkankan.common.exception.BusinessException;
+//import com.fdkankan.common.util.ComputerUtil;
+//import com.fdkankan.common.util.CreateObjUtil;
+//import com.fdkankan.common.util.FileUtil;
+//import com.fdkankan.common.util.FileUtils;
+//import com.fdkankan.common.util.SceneUtil;
+//import com.fdkankan.dingtalk.DingTalkSendUtils;
+//import com.fdkankan.fyun.constant.StorageType;
+//import com.fdkankan.fyun.oss.UploadToOssUtil;
+//import com.fdkankan.modeling.bean.BuildSceneResultBean;
+//import com.fdkankan.modeling.constants.SysConstants;
+//import com.fdkankan.modeling.entity.BuildLog;
+//import com.fdkankan.modeling.exception.BuildException;
+//import com.fdkankan.modeling.service.IBuildLogService;
+//import com.fdkankan.mq.message.BuildSceneFailDTMqMessage;
+//import com.fdkankan.mq.message.BuildSceneMqMessage;
+//import com.fdkankan.mq.message.BuildSceneResultMqMessage;
+//import com.fdkankan.mq.util.RocketMQProducer;
+//import com.fdkankan.push.PushMessageConfig;
+//import com.fdkankan.push.PushMsgUtil;
+//import com.fdkankan.redis.constant.RedisKey;
+//import com.fdkankan.redis.util.RedisUtil;
+//import com.taobao.api.ApiException;
+//import java.io.File;
+//import java.io.FileWriter;
+//import java.io.IOException;
+//import java.io.PrintWriter;
+//import java.io.StringWriter;
+//import java.io.UnsupportedEncodingException;
+//import java.security.InvalidKeyException;
+//import java.security.NoSuchAlgorithmException;
+//import java.time.Duration;
+//import java.time.temporal.ChronoUnit;
+//import java.util.Date;
+//import java.util.HashMap;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.Map.Entry;
+//import java.util.Objects;
+//import java.util.concurrent.Callable;
+//import java.util.concurrent.CompletableFuture;
+//import java.util.concurrent.ExecutorService;
+//import java.util.concurrent.Executors;
+//import java.util.concurrent.Future;
+//import java.util.concurrent.TimeUnit;
+//import java.util.concurrent.TimeoutException;
+//import lombok.extern.slf4j.Slf4j;
+//import org.apache.commons.lang3.StringUtils;
+//import org.apache.commons.lang3.time.StopWatch;
+//import org.apache.rocketmq.spring.annotation.MessageModel;
+//import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+//import org.apache.rocketmq.spring.core.RocketMQListener;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.stereotype.Component;
+//import org.springframework.util.ObjectUtils;
+//import org.springframework.web.client.RestTemplate;
+//
+//
+//@Slf4j
+//@Component
+//@RocketMQMessageListener(
+//        consumerGroup = "${rocketmq.consumer.build-scene-group}",
+//        topic = "${rocketmq.build-scene.topicName.topic-modeling-a}",
+//        messageModel = MessageModel.CLUSTERING,//负载均衡模式
+//        consumeThreadMax = 1,//消费者最大线程数,每次只消费一个
+//        consumeTimeout = 172801000L
+//)
+//public class BuildSceneListener implements RocketMQListener<String> {
+//
+//    @Value("${rocketmq.consumer.build-scene-group}")
+//    private String consumerGroup;
+//
+//    @Value("${rocketmq.build-scene.topicName.topic-modeling-a}")
+//    private String topicName;
+//
+//    @Value("${rocketmq.build-scene.topicName.topic-modeling-a-dt}")
+//    private String topicNameDt;
+//
+//    @Value("${rocketmq.build-scene-result.topicName.topic-modeling-a}")
+//    private String buildSceneResultTopic;
+//
+//    @Autowired
+//    RedisUtil redisUtil;
+//
+//    private RestTemplate restTemplate = new RestTemplate();
+//
+//    @Value("${4dkk.laserService.host}")
+//    private String laserHost;
+//
+//    @Value("${upload.type}")
+//    private String ossType;
+//
+//    @Value("${oss.prefix.ali}")
+//    private String prefixAli;
+//
+//    @Autowired
+//    private UploadToOssUtil uploadToOssUtil;
+//
+//    @Autowired
+//    RocketMQProducer rocketMQProducer;
+//
+//    @Value("${environment:dev}")
+//    private String environment;
+//
+//    @Autowired
+//    private DingTalkSendUtils dingTalkSendUtils;
+//
+//    @Autowired
+//    private IBuildLogService buildLogService;
+//
+//    @Override
+//    public void onMessage(String message) {
+//        log.info("开始处理消息,消费者组:{},主题名:{}, 消息内容:{}", consumerGroup, topicName, message);
+//        if (ObjectUtils.isEmpty(message)) {
+//            SysConstants.SYSTEM_BUILDING = false;
+//            log.error("消息体为空,退出构建,当前服务器id:{}", SysConstants.hostName);
+//            return;
+//        }
+//        BuildSceneMqMessage buildSceneMqMessage = JSONObject.parseObject(message, BuildSceneMqMessage.class);
+//        if(Objects.isNull(buildSceneMqMessage) || StrUtil.isBlank(buildSceneMqMessage.getSceneNum())){
+//            log.error("消息内容错误,消费者组:{},主题名:{}, 消息内容:{}", consumerGroup, topicName, message);
+//            return;
+//        }
+//        process(buildSceneMqMessage);
+//
+//        log.info("处理消息结束,消费者组:{},主题名:{}, 消息内容:{}", consumerGroup, topicName, message);
+//    }
+//
+//    public void process(BuildSceneMqMessage message) {
+//
+//        SysConstants.SYSTEM_BUILDING = true;
+//        if (SysConstants.SYSTEM_OFFING) {
+//            SysConstants.SYSTEM_BUILDING = false;
+//            log.error("服务实例:{} 正在关闭,退出构建!", SysConstants.hostName);
+//            throw new BusinessException(ServerCode.SERVER_CLOSING);
+//        }
+//        //开始计时
+//        StopWatch watch = new StopWatch();
+//        watch.start();
+//
+//        BuildLog buildLog = new BuildLog();
+//        final BuildSceneResultBean buildSceneResult = new BuildSceneResultBean();
+//        Future<ModelingBuildStatus> future = null;
+//        try {
+//
+//            //休眠2秒等待准备数据
+//            Thread.sleep(2000L);
+//
+//            //计算前准备
+//            preBuild(message, buildLog);
+//
+//            future = SysConstants.executorService.submit(()->{
+//                return buildScene(message, buildSceneResult);
+//            });
+//            future.get(SysConstants.modelTimeOut, TimeUnit.HOURS);
+//
+//            //结束计时
+//            watch.stop();
+//            buildLog.setDuration(watch.getTime(TimeUnit.SECONDS));
+//        } catch (TimeoutException ex) {
+//            log.error("服务实例:{} 构建异常:",SysConstants.hostName,ex);
+//            buildSceneResult.setBuildStatus(ModelingBuildStatus.OVERTIME);
+//        } catch (BuildException e){
+//            buildSceneResult.setBuildStatus(e.getBuildStatus());
+//        } catch(Exception e) {
+//            log.error("服务实例:{} 构建异常:", SysConstants.hostName, e);
+//            if (e.getCause() instanceof BuildException) {
+//                buildSceneResult.setBuildStatus(((BuildException) e.getCause()).getBuildStatus());
+//            } else {
+//                buildSceneResult.setBuildStatus(ModelingBuildStatus.FAILED);
+//                //未知异常,记录异常信息
+//                buildLog.setReason(ExceptionUtil.stacktraceToString(e, 3000));
+//            }
+//        }
+//        //计算后处理
+//        afterBuild(message, buildSceneResult, buildLog);
+//
+//    }
+//
+//    private ModelingBuildStatus buildScene(BuildSceneMqMessage message, BuildSceneResultBean buildSceneResult) throws Exception{
+//        String projectNum = null;
+//        Long computeTime = null;//计算耗时
+//        //如果mq生产者在消息体中执行了计算结果mq的主题名,就发到这个主题名,否则就发送到配置的主题名
+//        String resultTopicName = StrUtil.isNotBlank(message.getResultReceiverMqName()) ?
+//                message.getResultReceiverMqName() : buildSceneResultTopic;
+//        //该场景使用的容量
+////        Long space = 0L;
+//        //支付状态
+//        int payStatus = 0;
+//        String unicode = message.getUnicode();
+//        String path = message.getPath();
+//        String prefix = message.getPrefix();
+//        projectNum = message.getSceneNum();
+//        //不同的相机不同的方法
+//        String cameraType = message.getCameraType();
+//        String algorithm = message.getAlgorithm();
+//        String fileId = message.getFileId();
+//        String cameraName = message.getCameraName();
+//        //0表示有4k图,1表示没有
+//        String resolution = message.getResolution();
+//        //判断调用V2还是V3版本的算法
+//        String buildType = ObjectUtils.isEmpty(message.getBuildType()) ? "V2" : message.getBuildType();
+//
+//        String dataPath = "data/data" + projectNum;//老版本文件路径
+//
+//        Map<String,String> map = null;
+//        Integer pushChannel = null;
+//        String pushToken = null;
+//        Integer videoVersion = null;
+//
+//        buildSceneResult.setResultTopicName(resultTopicName);
+//        buildSceneResult.setCameraType(cameraType);
+//        buildSceneResult.setNum(projectNum);
+//
+//
+//        Map<String, String> dataMap = ComputerUtil.getTypeString(cameraType, algorithm,
+//                resolution);
+//        String splitType = dataMap.get("splitType");
+//        String skyboxType = dataMap.get("skyboxType");
+//        String dataDescribe = dataMap.get("dataDescribe");
+//
+//        log.info("用的算法是:"+algorithm);
+//        log.info("用的相机是:"+ (Integer.parseInt(cameraType) < 4 ? "单球目" : "双球目(八目)"));
+//
+//        //4表示硬件部研发的双球目相机,其余为旧版本相机
+//        if(Integer.parseInt(cameraType) < 3){
+//
+//            String data = FileUtils.readFile(path + File.separator + "capture" +File.separator+"data.fdage");
+//            JSONObject dataJson = new JSONObject();
+//            if(data!=null){
+//                dataJson = JSONObject.parseObject(data);
+//                if(dataJson.containsKey("imgs")){
+//                    splitType = "SPLIT_V4";
+//                }
+//            }
+//            //生成project.json和data.json供算法部使用
+//            ComputerUtil.createJson(path, splitType, skyboxType, dataDescribe, projectNum, path);
+//            //计算模型并返回需要上传oss的文件集合
+//            map = ComputerUtil.computer(projectNum, path, buildType);
+//
+//        } else {
+//
+//            path = SceneUtil.getPath(path, cameraName, fileId, Integer.parseInt(cameraType), unicode);
+//
+//            String dataFdagePath = path + File.separator + "capture" +File.separator+"data.fdage";
+//            log.info("dataFdagePath 文件路径 :{}", dataFdagePath);
+//            String data = FileUtils.readFile(dataFdagePath);
+//            //获取data.fdage的内容
+//            JSONObject dataJson = new JSONObject();
+//            if(data!=null){
+//                dataJson = JSONObject.parseObject(data);
+//            }
+//            pushChannel = dataJson.getInteger("pushChannel");
+//            pushToken = dataJson.getString("pushToken");
+//
+//            /*
+//             1、判断是否计算过资源,若计算过删除缓存, 如果caches中存在_images文件或者目录,就删除掉,否则就删除除了images以外的所有文件和目录
+//             2、删除上一次计算出来的result目录
+//             */
+//            this.deleteCachesAndResult(path);
+//
+//            //v2版本使用4k算法
+//            if(dataJson.containsKey("videoVersion")
+//                    && StringUtils.isNotEmpty(dataJson.getString("videoVersion"))
+//                    && Integer.parseInt(dataJson.getString("videoVersion")) < 4){
+//                skyboxType = "SKYBOX_V6";
+//            }
+//
+//            //生成project.json和data.json并写到path目录下供算法部使用
+//            log.info("path:" + path);
+//            ComputerUtil.createJson(path, splitType, skyboxType, dataDescribe, projectNum, path);
+//            //计算模型并返回需要上传oss的文件集合
+//            map = ComputerUtil.computer(projectNum, path, buildType);
+//            //计算完成时间
+//            log.info("计算完成耗时:{}",computeTime);
+//
+//            if(Integer.parseInt(cameraType) == 5 || Integer.parseInt(cameraType) == 6){
+//                map.put(path + File.separator + "capture/stitch_params.txt", dataPath + "/stitch_params.txt");
+//            }
+//            map.put(path + File.separator + "capture/Up.xml", dataPath + "/Up.xml");
+//            map.put(path + File.separator + "capture/Up2.xml", dataPath + "/Up2.xml");
+//
+//            //转台相机
+//            if(Integer.parseInt(cameraType) == 13){
+//                map.put(path + File.separator + "capture/Up.txt", dataPath + "/Up.txt");
+//                map.put(path + File.separator + "capture/Up2.txt", dataPath + "/Up2.txt");
+//            }
+//
+//            //计算已使用容量
+//            payStatus = 1;
+//
+//            log.info("八目上完oss结束修改数据:"+projectNum);
+//
+//            //读取计算结果文件生成videosJson
+////                videosJson = this.getVideosJson(path, dataJson, projectNum, Integer.valueOf(cameraType));
+//            String videoVersionStr = dataJson.getString("videoVersion");
+//            videoVersion = StrUtil.isEmpty(videoVersionStr) ? null : Integer.parseInt(videoVersionStr);
+//
+//            //更新scene.json里面的video数据
+////                this.updateVideosToSceneJson(projectNum, videosJson);
+//
+//            //计算成功  激光转台相机 同步 请求
+//            this.jgSynch(message);
+//        }
+//
+//        buildSceneResult.setBuildStatus(ModelingBuildStatus.SUCCESS);
+//        buildSceneResult.setFileId(fileId);
+//        buildSceneResult.setPayStatus(payStatus);
+//        buildSceneResult.setUploadMap(map);
+//        buildSceneResult.setPath(path);
+//        buildSceneResult.setPushChannel(pushChannel);
+//        buildSceneResult.setPushToken(pushToken);
+//        buildSceneResult.setPrefix(prefix);
+//        buildSceneResult.setVideoVersion(videoVersion);
+//
+//        CreateObjUtil.deleteFile(path.replace(ConstantFilePath.BUILD_MODEL_PATH, "/") + "/capture");
+//
+//        return ModelingBuildStatus.SUCCESS;
+//
+////        return BuildSceneResultBean.builder()
+////            .buildStatus(ModelingBuildStatus.SUCCESS)
+////            .resultTopicName(resultTopicName)
+////            .cameraType(cameraType)
+////            .fileId(fileId)
+////            .payStatus(payStatus)
+////            .num(projectNum)
+////            .uploadMap(map)
+////            .path(path)
+////            .pushChannel(pushChannel)
+////            .pushToken(pushToken)
+////            .prefix(prefix)
+////            .videoVersion(videoVersion)
+////            .build();
+//    }
+//
+//    private void afterBuild(BuildSceneMqMessage message, BuildSceneResultBean buildSceneResult, BuildLog buildLog){
+//        try {
+//            ModelingBuildStatus buildStatus = buildSceneResult.getBuildStatus();
+//            String buildLogPath = String.format(UploadFilePath.BUILD_LOG_PATH, message.getSceneNum());
+//            log.info("服务{} 计算结束:{},计算状态:{}", SysConstants.hostName, buildLog.getNum(),buildStatus.message());
+//
+//            if(!buildStatus.equals(ModelingBuildStatus.REPEAT)){
+//                //释放锁
+//                redisUtil.del(String.format(RedisKey.SCENE_BUILDING, message.getSceneNum()));
+//
+//                //生成status.json文件
+//                this.upoadLog(message.getSceneStatus(), message.getWebSite(),
+//                    message.getSceneNum(),message.getThumb(),message.getPayStatus(), message.getDataSource());
+//
+//                //如果是重复计算,没有走到计算逻辑,不需要上传日志文件
+//                buildLog.setLogDetailPath(prefixAli+ buildLogPath + "console.log");
+//            }
+//            buildLog.setBuildStatus(buildStatus.code());
+//            buildLog.setCameraType(Integer.parseInt(buildSceneResult.getCameraType()));
+//            buildLogService.updateById(buildLog);
+//
+//        } catch (Exception e) {
+//            log.error("计算后业务处理出错!", e);
+//        }finally {
+//            //发送计算结果mq
+//            buildSceneResult.setDuration(buildLog.getDuration());
+//            this.sendCallResult(message, buildSceneResult);
+//
+//            //计算完毕,将当前系统构建状态改为false
+//            SysConstants.SYSTEM_BUILDING = false;
+//        }
+//    }
+//
+//    private void uploadCaches(String num, Map<String,String> map, String path){
+//
+//        String sceneNumPAth = String.format("scene/%s", num);
+//
+//        List<String> imagesList = FileUtil.getFileList(path + "/caches/images");
+//        log.info("caches/images_path:{}", path + "/caches/images");
+//        if(CollUtil.isNotEmpty(imagesList)){
+//            log.info("上传的caches/images文件列表:{}", imagesList.toString());
+//            imagesList.stream().forEach(str -> map.put(str, str.replace(path, sceneNumPAth)));
+//        }
+//
+//        List<String> videosList = FileUtil.getFileList(path + "/caches/videos");
+//        if(CollUtil.isNotEmpty(videosList)){
+//            log.info("上传的caches/videos文件列表:{}", videosList.toString());
+//            videosList.stream().forEach(str -> map.put(str, str.replace(path, sceneNumPAth)));
+//        }
+//
+//    }
+//
+//    private void preBuild(BuildSceneMqMessage message, BuildLog buildLog) throws BuildException {
+//
+//        // 初始化日志参数
+//        buildLog.setHostName(SysConstants.hostName);
+//        buildLog.setTopicName(topicName);
+//        buildLog.setNum(message.getSceneNum());
+//        buildLog.setDataSource(message.getDataSource());
+//        buildLogService.save(buildLog);
+//
+////        String key = RedisKey.SCENE_BUILDING + message.getSceneNum();
+////        Long building = redisUtil.incr(key, 1);
+////        if (building.compareTo(1L) != 0) {
+////            log.error("场景正在构建中,退出构建,当前服务器id:{},参数:{}", SysConstants.hostName, JSONObject.toJSONString(message));
+////            throw new BuildException(ModelingBuildStatus.REPEAT);
+////        } else {
+////            redisUtil.expire(key, Duration.of(SysConstants.modelTimeOut, ChronoUnit.HOURS));
+////        }
+//    }
+//
+//    private void copyToEditDir(String num) throws IOException {
+//
+//        String editImagesPath = String.format(UploadFilePath.IMG_EDIT_PATH, num);
+//        String viewImagesPath = String.format(UploadFilePath.IMG_VIEW_PATH, num);
+//
+//        String editDataPath = String.format(UploadFilePath.DATA_EDIT_PATH, num);
+//        String viewDataPath = String.format(UploadFilePath.DATA_VIEW_PATH, num);
+//
+//        Map<String, String> map = new HashMap<>();
+//        map.put(editImagesPath + "vision.modeldata", viewImagesPath + "vision.modeldata");
+//        map.put(editImagesPath + "vision2.modeldata", viewImagesPath + "vision2.modeldata");
+//        map.put(editDataPath + "floorplan_cad.json", viewDataPath + "floorplan_cad.json");
+//
+//        for (Entry<String, String> entry : map.entrySet()) {
+//            uploadToOssUtil.copyFiles(entry.getValue(), entry.getKey());
+//        }
+//    }
+//
+//    /**
+//     * 发送计算结果
+//     * @param message
+//     * @param buildSceneResult
+//     */
+//    private void sendCallResult(BuildSceneMqMessage message, BuildSceneResultBean buildSceneResult){
+//        ModelingBuildStatus buildStatus = buildSceneResult.getBuildStatus();
+//        //重复计算不需要发送mq做后置处理
+//        if(buildStatus.equals(ModelingBuildStatus.REPEAT)){
+//            return;
+//        }
+//        boolean buildSuccess = buildStatus.equals(ModelingBuildStatus.SUCCESS) ? true : false;
+//        rocketMQProducer.sendOneWay(buildSceneResult.getResultTopicName(),
+//                BuildSceneResultMqMessage.builder()
+//                    .buildSuccess(buildSuccess)
+//                    .cameraType(buildSceneResult.getCameraType())
+//                    .computeTime(buildSceneResult.getDuration())
+//                    .fileId(buildSceneResult.getFileId())
+//                    .payStatus(buildSceneResult.getPayStatus())
+//                    .sceneCode(message.getSceneNum())
+//                    .uploadFiles(buildSceneResult.getUploadMap())
+//                    .path(buildSceneResult.getPath())
+//                    .pushChannel(buildSceneResult.getPushChannel())
+//                    .pushToken(buildSceneResult.getPushToken())
+//                    .prefix(buildSceneResult.getPrefix())
+//                    .videoVersion(buildSceneResult.getVideoVersion())
+//                    .build());
+//
+//        //如果是计算失败,需要发送钉钉
+//        if(!buildSuccess){
+//            // TODO: 2022/1/25 测试阶段,先注释掉推送
+//            this.sendDingTalkMq(buildStatus.message(), message);
+//        }
+//    }
+//
+//    private void upoadLog(Integer sceneStatus, String webSite,
+//            String num, String thumb, Integer payStatus, String dataSource){
+//        try{
+//            JSONObject statusJson = new JSONObject();
+//            //临时将-2改成1,app还没完全更新
+//            if(sceneStatus == -2){
+//                sceneStatus = 1;
+//            }else if(sceneStatus == -1){
+//                // 失败状态不予显示到客户端
+//                sceneStatus = 0;
+//            }
+//            statusJson.put("status", sceneStatus);
+//            statusJson.put("webSite", webSite);
+//            statusJson.put("sceneNum", num);
+//            statusJson.put("thumb", thumb);
+//            statusJson.put("payStatus", payStatus);
+//            FileUtils.writeFile(ConstantFilePath.SCENE_PATH+"data/data"+num+File.separator+"status.json", statusJson.toString());
+//
+//            String dataPath = String.format(UploadFilePath.DATA_VIEW_PATH, num);
+//            String buildLogPath = String.format(UploadFilePath.BUILD_LOG_PATH, num);
+//
+//            //todo 待删除old_path
+//            uploadToOssUtil.upload(
+//                ConstantFilePath.SCENE_PATH + "data/data" + num + File.separator + "status.json",
+//                "data/data" + num + File.separator + "status.json");
+//
+//            uploadToOssUtil.upload(
+//                ConstantFilePath.SCENE_PATH + "data/data" + num + File.separator + "status.json",
+//                dataPath + "status.json");
+//
+//            uploadToOssUtil.upload(dataSource + File.separator + "console.log",
+//                buildLogPath + "console.log");
+//
+//        }catch (Exception e){
+//            log.error("上传日志失败!", e);
+//        }
+//    }
+//
+//    private void jgSynch(BuildSceneMqMessage message){
+//        log.info("激光转台相机 同步 请求 ");
+//        if(Integer.parseInt(message.getCameraType()) != 14)
+//            return;
+//
+//        String sceneName = message.getSceneName();
+//        String dataSource = message.getDataSource();
+//        Long userId = message.getUserId();
+//        String userName = message.getUserName();
+//        String cameraName = message.getCameraName();
+//        try {
+//            String title = "";
+//            if(StringUtils.isNotEmpty(sceneName)){
+//                title = sceneName;
+//            }
+////                        String dataSource = sceneProExt.getDataSource();
+//            String jgPath = dataSource;
+//            //创建目录
+//            if(dataSource.lastIndexOf("/")!=-1){
+//                jgPath = jgPath + "_laserData";
+//            }else{
+//                jgPath = jgPath.substring(0,jgPath.length()-1) + "_laserData";
+//            }
+//
+//            FileUtils.createDir(jgPath+"/extras");
+//
+//            log.info("生成 激光相机目录 " + jgPath);
+//            //生成data.json
+//            JSONObject jgDataJson = new JSONObject();
+//            jgDataJson.put("split_type", "SPLIT_V15");
+//            jgDataJson.put("skybox_type", "SKYBOX_V5");
+//            jgDataJson.put("extras", null);
+//            FileUtils.writeFile(jgPath + File.separator + "data.json", jgDataJson.toString());
+//
+//            CreateObjUtil.cpfile(dataSource  + "/results/laserData/cover", jgPath+"/extras/");
+//            CreateObjUtil.cplaserfile(dataSource  + "/results/laserData", jgPath+File.separator);
+//
+//            //激光相机
+//            String url = laserHost+"/indoor/{sceneCode}/service/init?path="
+//                    + jgPath + File.separator + "laserData" + "&title="+ title
+//                    + "&childName=" + cameraName + "&createTime=" +  message.getSceneProCreateTime()
+//                    + "&snCode="+ cameraName;
+//
+//            if(userId!=null){
+//                url = url + "&userId=" + userId;
+//            }
+//
+//            if(StringUtils.isNotEmpty(userName)){
+//                url = url + "&phone=" + userName;
+//            }
+//
+//            url = url.replace("{sceneCode}",message.getSceneNum());
+//            log.info("激光转台相机 同步 :" + url);
+//            JSONObject hotListJson =
+//                    JSONObject.parseObject(restTemplate.getForObject(url,String.class));
+//            log.info("激光转台相机 同步结束 :" + hotListJson);
+//        }catch (Exception e){
+//            log.error("激光转台相机同步失败", e);
+//            // TODO: 2022/1/25 测试阶段,先注释掉推送
+////            this.sendDingTalkMq("激光转台相机同步失败", message);
+//        }
+//    }
+//
+//    private void sendSms(JSONObject dataJson, int cameraType, String sceneName, String webSite){
+//        log.info("推送消息,渠道是 {}, 手机token是 {}", dataJson.get("pushChannel"), dataJson.get("pushToken"));
+//        if(dataJson.containsKey("pushChannel") && dataJson.containsKey("pushToken")){
+//            try{
+//                if(StorageType.AWS.code().equals(ossType)){
+//                    PushMsgUtil.googlePushMsg(ConstantFilePath.BASE_PATH + "/refreshToken.json",dataJson.getString("pushToken"),
+//                            sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                            webSite);
+//                    return;
+//                }
+//
+//                PushMessageConfig demo = null;
+//                if(dataJson.getIntValue("pushChannel") == 0){
+//
+//                    if(cameraType == 10 || cameraType == 13){
+//                        //ios
+//                        log.info("IOS_KEY:{}, IOS_SECRET:{}", IOS_KEY_Z, IOS_SECRET_Z);
+//                        demo = new PushMessageConfig(IOS_KEY_Z, IOS_SECRET_Z);
+//                        demo.sendIOSUnicast(dataJson.getString("pushToken"),
+//                                "四维看看Minion", sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                                webSite);
+//                    }else {
+//                        //ios
+//                        log.info("IOS_KEY:{}, IOS_SECRET:{}", IOS_KEY, IOS_SECRET);
+//                        demo = new PushMessageConfig(IOS_KEY, IOS_SECRET);
+//                        demo.sendIOSUnicast(dataJson.getString("pushToken"),
+//                                "四维看看Pro", sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                                webSite);
+//                    }
+//
+//                }else {
+//                    if(cameraType == 10 || cameraType == 13){
+//                        //ios
+//                        //安卓
+//                        log.info("ANDROID_KEY:{}, ANDROID_SECRET:{}", ANDROID_KEY_Z, ANDROID_SECRET_Z);
+//                        demo = new PushMessageConfig(ANDROID_KEY_Z, ANDROID_SECRET_Z);
+//                        demo.sendAndroidUnicast2(dataJson.getString("pushToken"),
+//                                "四维看看Minion", sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                                webSite);
+//                    }else {
+//                        //安卓
+//                        log.info("ANDROID_KEY:{}, ANDROID_SECRET:{}", ANDROID_KEY, ANDROID_SECRET);
+//                        demo = new PushMessageConfig(ANDROID_KEY, ANDROID_SECRET);
+//                        demo.sendAndroidUnicast(dataJson.getString("pushToken"),
+//                                "四维看看Pro", sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                                webSite);
+//                    }
+//                }
+//                log.info("消息推送结束!");
+//            }catch (Exception e){
+//                log.error("推送消息失败:", e);
+//            }
+//        }
+//    }
+//
+//    private void updateVideosToSceneJson(String projectNum, JSONObject videosJson) throws Exception {
+//        String sceneJsonPath = String.format(ConstantFilePath.SCENE_PATH_FORMAT, projectNum);
+//        String strsceneInfos = FileUtils.readFile(sceneJsonPath);
+//        JSONObject scenejson = new JSONObject();
+//        if(strsceneInfos!=null){
+//            scenejson = JSONObject.parseObject(strsceneInfos);
+//        }
+//        scenejson.put("videos", videosJson.toJSONString());
+//        FileUtils.writeFile(sceneJsonPath, scenejson.toString());
+//    }
+//
+//    private JSONObject getVideosJson(String path, JSONObject dataJson, String projectNum, int cameraType) throws Exception {
+//        //读取videos_hdr_param.json, 保存点位视频的value
+//        Map<String, Object> videoMap = new HashMap<>();
+//        String videosHdr = FileUtils.readFile(path + File.separator + "results/videos/videos_hdr_param.json");
+//        JSONArray videoArray = null;
+//        if(StringUtils.isNotEmpty(videosHdr)){
+//            videoArray = JSONObject.parseObject(videosHdr).getJSONArray("hdr_param");
+//        }
+//        if(videoArray != null){
+//            for(int i = 0, len = videoArray.size(); i < len; i++) {
+//                videoMap.put(videoArray.getJSONObject(i).getString("name"), videoArray.getJSONObject(i).getString("value"));
+//                if(videoArray.getJSONObject(i).containsKey("fov")){
+//                    videoMap.put(videoArray.getJSONObject(i).getString("name") + "_fov", videoArray.getJSONObject(i).getString("fov"));
+//                }
+//            }
+//        }
+//
+//        //获取upload中的video视频名称
+//        String uploadData = FileUtils.readFile(path + File.separator + "results" +File.separator+"upload.json");
+//        JSONObject uploadJson = null;
+//        JSONArray array = null;
+//        if(uploadData!=null) {
+//            uploadJson = JSONObject.parseObject(uploadData);
+//            array = uploadJson.getJSONArray("upload");
+//        }
+//        JSONObject fileJson = null;
+//        String fileName = "";
+//
+//        //计算ts文件的大小,并拼接成json格式
+//        JSONArray jsonArray = new JSONArray();
+//        JSONObject videoJson = null;
+//        JSONObject videosJson = new JSONObject();
+//        long videoSize = 0L;
+//        for(int i = 0, len = array.size(); i < len; i++) {
+//            fileJson = array.getJSONObject(i);
+//            fileName = fileJson.getString("file");
+//            if(fileJson.getIntValue("clazz") == 11 && fileName.contains(".mp4") && !fileName.contains("-ios.mp4")){
+//                videoJson = new JSONObject();
+//                videoJson.put("id", fileName.substring(
+//                        0, fileName.lastIndexOf(".")).replace("videos/", ""));
+//
+//                //如果ts文件存在,就计算ts大小
+//                if(new File(path + File.separator + "results" +File.separator+ fileName.replace(".mp4", ".ts")).exists()){
+//                    videoSize = new File(path + File.separator + "results" +File.separator+ fileName.replace(".mp4", ".ts")).length();
+//                    videoJson.put("tsSize", videoSize);
+//                }
+//                if(videoMap.containsKey(videoJson.get("id"))){
+//                    videoJson.put("value", videoMap.get(videoJson.get("id")));
+//                }
+//                if(videoMap.containsKey(videoJson.get("id") + "_fov")){
+//                    videoJson.put("blend_fov", videoMap.get(videoJson.get("id") + "_fov"));
+//                }else {
+//                    videoJson.put("blend_fov", 7);
+//                }
+//                jsonArray.add(videoJson);
+//            }
+//        }
+//
+//        videosJson.put("data", jsonArray);
+//        if(dataJson.containsKey("videoVersion") && StringUtils.isNotEmpty(dataJson.getString("videoVersion")) && Integer.parseInt(dataJson.getString("videoVersion")) >= 4){
+//            videosJson.put("version", 3);
+//            if(StorageType.OSS.code().equals(ossType)){
+//                videosJson.put("upPath", prefixAli + "data/data" + projectNum + "/Up.xml");
+//            }
+//            if(StorageType.AWS.code().equals(ossType)){
+//                videosJson.put("upPath", ConstantUrl.PREFIX_AWS + "data/data" + projectNum + "/Up.xml");
+//            }
+//            if(cameraType == 13){
+//                //转台相机
+//                videosJson.put("upPath", videosJson.getString("upPath").replace(".xml", ".txt"));
+//            }
+//        }else {
+//            videosJson.put("version", 1);
+//            if("oss".equals(ossType)){
+//                videosJson.put("upPath", prefixAli + "data/data" + projectNum + "/Up2.xml");
+//            }
+//            if("aws".equals(ossType)){
+//                videosJson.put("upPath", ConstantUrl.PREFIX_AWS + "data/data" + projectNum + "/Up2.xml");
+//            }
+//
+//            if(cameraType == 13){
+//                //转台相机
+//                videosJson.put("upPath", videosJson.getString("upPath").replace(".xml", ".txt"));
+//            }
+//        }
+//
+//        if(cameraType == 5 || cameraType == 6){
+//            videosJson.put("version", 1);
+//            if("oss".equals(ossType)){
+//                videosJson.put("upPath", prefixAli + "data/data" + projectNum + "/stitch_params.txt");
+//            }
+//            if("aws".equals(ossType)){
+//                videosJson.put("upPath", ConstantUrl.PREFIX_AWS + "data/data" + projectNum + "/stitch_params.txt");
+//            }
+//        }
+//
+//        return videosJson;
+//    }
+//
+//    private String downLoadSource(String path, int cameraType, String cameraName, String unicode, String fileId, String imgsName, String prefix) throws Exception {
+//        if(cameraType < 4){
+//            throw new Exception("cameraType 不能小于4");
+//        }
+//        //cameraType=5为新版本双目, cameraType=6为小红屋新版本双目
+//        String ossPath = ConstantFilePath.OSS_PREFIX + cameraName.replace("4DKKPRO_", "")
+//                .replace("-fdage", "").toLowerCase() + File.separator + fileId + File.separator
+//                + unicode + File.separator;
+//        String localPath = cameraName.replace("4DKKPRO_", "").replace("-fdage", "").toLowerCase() +
+//                File.separator + fileId + File.separator + unicode;
+//        if(cameraType == 5 || cameraType == 6){
+//            path = ConstantFilePath.BUILD_MODEL_PATH + unicode;
+//            //下载zip包
+//            FileUtils.downLoadFromUrl(prefix + "/" + imgsName + "?m=" + System.currentTimeMillis(), imgsName, path + File.separator + "capture");
+//            //解压
+//            FileUtils.decompress(path + File.separator + "capture" + File.separator + imgsName,
+//                    path + File.separator + "capture") ;
+//            //删除压缩包
+//            FileUtil.delFile(path + File.separator + "capture" + File.separator + imgsName);
+//        }else if(cameraType == 14) {
+////                    + File.separator + "capture"
+//            path = ConstantFilePath.BUILD_MODEL_LASER_PATH + localPath;
+//            CreateObjUtil.ossUtilCp(ossPath,path + File.separator + "capture");
+//        } else{
+//            path = ConstantFilePath.BUILD_MODEL_PATH + localPath;
+//            CreateObjUtil.ossUtilCp(ossPath,path + File.separator + "capture");
+//        }
+//        return path;
+//    }
+//
+//    private void deleteCachesAndResult(String path){
+//        //判断是否计算过资源,若计算过删除缓存, 如果caches中存在_images文件或者目录,就删除掉,否则就删除除了images以外的所有文件和目录
+//        File caches = new File(path + File.separator + "caches");
+//        if(caches.exists()){
+//            for(File deleteFile : caches.listFiles()){
+//                if(new File(path + "_images").exists()){
+//                    if(deleteFile.isDirectory()){
+//                        FileUtils.delAllFile(deleteFile.getAbsolutePath());
+//                    }else {
+//                        FileUtils.deleteFile(deleteFile.getAbsolutePath());
+//                    }
+//                }else if(!deleteFile.getAbsolutePath().contains("images")){
+//                    if(deleteFile.isDirectory()){
+//                        FileUtils.delAllFile(deleteFile.getAbsolutePath());
+//                    }else {
+//                        FileUtils.deleteFile(deleteFile.getAbsolutePath());
+//                    }
+//                }
+//            }
+//        }
+//        //删除上一次计算出来的result目录
+//        if(new File(path + File.separator + "results").exists()){
+//            FileUtils.delAllFile(path + File.separator + "results");
+//        }
+//    }
+//
+//    private void sendDingTalkMq(String reason,BuildSceneMqMessage message){
+//        String serverPath = message.getPath().substring(0,message.getPath().lastIndexOf("/")+1).concat(message.getPrefix());
+//        rocketMQProducer.sendOneWay(topicNameDt,
+//            BuildSceneFailDTMqMessage.builder()
+//                .hostName(SysConstants.hostName)
+//                .reason(reason)
+//                .num(message.getSceneNum())
+//                .serverPath(serverPath)
+//                .build());
+//    }
+//
+//    private void handFail(String reason,BuildSceneMqMessage message){
+//        String serverPath = message.getPath().substring(0,message.getPath().lastIndexOf("/")+1).concat(message.getPrefix());
+//        handFail(reason,serverPath,message.getSceneNum());
+//    }
+//
+//    private void handFail(String reason,String serverPath,String num){
+//        CompletableFuture.runAsync(() -> {
+//            try {
+//                String content = String.format(SysConstants.DINGTALK_MSG_PATTERN,environment, SysConstants.hostName,reason,num,serverPath,num,num);
+//                dingTalkSendUtils.sendActioncardMsgToDingRobot(content,"场景计算失败");
+//            } catch (ApiException | UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException apiException) {
+//                apiException.printStackTrace();
+//            }
+//        });
+//    }
+//}

+ 794 - 0
src/main/java/com/fdkankan/modeling/receiver/BuildSceneMQListener.java

@@ -0,0 +1,794 @@
+//package com.fdkankan.modeling.receiver;
+//
+//import cn.hutool.core.collection.CollUtil;
+//import cn.hutool.core.date.DateUtil;
+//import cn.hutool.core.util.StrUtil;
+//import com.alibaba.fastjson.JSONArray;
+//import com.alibaba.fastjson.JSONObject;
+//import com.amazonaws.services.devicefarm.model.UploadType;
+//import com.fdkankan.common.constant.ConstantFilePath;
+//import com.fdkankan.common.constant.ConstantUrl;
+//import com.fdkankan.common.constant.UploadFilePath;
+//import com.fdkankan.common.util.ComputerUtil;
+//import com.fdkankan.common.util.CreateObjUtil;
+//import com.fdkankan.common.util.FileUtil;
+//import com.fdkankan.common.util.FileUtils;
+//import com.fdkankan.dingtalk.DingTalkSendUtils;
+//import com.fdkankan.fyun.constant.StorageType;
+//import com.fdkankan.fyun.oss.UploadToOssUtil;
+//import com.fdkankan.modeling.constants.SysConstants;
+//import com.fdkankan.mq.message.BuildSceneMqMessage;
+//import com.fdkankan.mq.message.BuildSceneResultMqMessage;
+//import com.fdkankan.mq.util.RocketMQProducer;
+//import com.fdkankan.push.PushMessageConfig;
+//import com.fdkankan.push.PushMsgUtil;
+//import com.fdkankan.redis.constant.RedisKey;
+//import com.fdkankan.redis.util.RedisUtil;
+//import com.taobao.api.ApiException;
+//import java.util.List;
+//import java.util.Map.Entry;
+//import lombok.extern.slf4j.Slf4j;
+//import org.apache.commons.lang3.StringUtils;
+//import org.apache.rocketmq.spring.annotation.ConsumeMode;
+//import org.apache.rocketmq.spring.annotation.MessageModel;
+//import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+//import org.apache.rocketmq.spring.core.RocketMQListener;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.stereotype.Component;
+//import org.springframework.util.ObjectUtils;
+//import org.springframework.web.client.RestTemplate;
+//
+//import java.io.*;
+//import java.security.InvalidKeyException;
+//import java.security.NoSuchAlgorithmException;
+//import java.time.Duration;
+//import java.time.temporal.ChronoUnit;
+//import java.util.Date;
+//import java.util.HashMap;
+//import java.util.Map;
+//import java.util.Objects;
+//import java.util.concurrent.*;
+//
+//import static com.fdkankan.push.PushMessageConfig.*;
+//
+//
+//@Slf4j
+//@Component
+//@RocketMQMessageListener(
+//        consumerGroup = "${rocketmq.consumer.build-scene-group}",
+//        topic = "${rocketmq.build-scene.topicName.topic-modeling-a}",
+//        messageModel = MessageModel.CLUSTERING,//负载均衡模式
+//        consumeThreadMax = 1//消费者最大线程数,每次只消费一个
+//)
+//public class BuildSceneMQListener implements RocketMQListener<String> {
+//
+//    @Value("${rocketmq.consumer.build-scene-group}")
+//    private String consumerGroup;
+//
+//    @Value("${rocketmq.build-scene.topicName.topic-modeling-a}")
+//    private String topicName;
+//
+//    @Autowired
+//    RedisUtil redisUtil;
+//
+//    private RestTemplate restTemplate = new RestTemplate();
+//
+//    @Value("${4dkk.laserService.host}")
+//    private String laserHost;
+//
+//    @Value("${upload.type}")
+//    private String ossType;
+//
+//    @Value("${oss.prefix.ali}")
+//    private String prefixAli;
+//
+//    @Autowired
+//    private UploadToOssUtil uploadToOssUtil;
+//
+//    @Autowired
+//    RocketMQProducer rocketMQProducer;
+//
+//    @Value("${rocketmq.build-scene-result.topicName.topic-modeling-a}")
+//    private String buildSceneResultTopic;
+//
+//    @Value("${environment:dev}")
+//    private String environment;
+//
+//    @Autowired
+//    private DingTalkSendUtils dingTalkSendUtils;
+//
+//    @Override
+//    public synchronized void onMessage(String message) {
+//        log.info("开始处理消息,消费者组:{},主题名:{}, 消息内容:{}", consumerGroup, topicName, message);
+//        BuildSceneMqMessage buildSceneMqMessage = JSONObject.parseObject(message, BuildSceneMqMessage.class);
+//        if(StringUtils.isBlank(buildSceneMqMessage.getSceneNum())){
+//            log.error("消息结构错误,消费者组:{},主题名:{}, 消息内容:{}", consumerGroup, topicName, message);
+//            return;
+//        }
+//        process(buildSceneMqMessage);
+//    }
+//
+//    public void process(BuildSceneMqMessage message) {
+//        final ExecutorService exec = Executors.newFixedThreadPool(1);
+//        Callable<String> call = (Callable<String>) () -> {
+//            //开始执行耗时操作
+//            try {
+//                String key = RedisKey.SCENE_BUILDING + message.getSceneNum();
+//                // 获取缓存锁,防止重复消费
+//                Long building = redisUtil.incr(key, 1);
+//                if (building.compareTo(1L) != 0) {
+//                    log.error("场景正在构建中,退出构建,当前服务器id:{},参数:{}", SysConstants.hostName, JSONObject.toJSONString(message));
+//                } else {
+//                    redisUtil.expire(key, Duration.of(SysConstants.modelTimeOut, ChronoUnit.HOURS));
+//                }
+//                //休眠2秒等待数据入库
+//                Thread.sleep(2000L);
+//                try {
+//                    FileUtils.writeFile("/opt/hosts/running.txt", DateUtil.formatDateTime(new Date()));
+//                    String tomcatLog = "scenenum:" + message.getSceneNum() + "\ntime:" + DateUtil.formatDateTime(new Date()) +
+//                            " action:create hostname:" + SysConstants.hostName;
+//                    //打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
+//                    FileWriter writer = new FileWriter("/mnt/elastic_log/tomcat" + "_" + message.getSceneNum() + ".log", true);
+//                    writer.write(tomcatLog);
+//                    writer.close();
+//                } catch (Exception e) {
+//                    e.printStackTrace();
+//                }
+//
+//                this.buildScene(message);
+//                redisUtil.del(key);
+//                try {
+//                    FileUtils.deleteFile("/opt/hosts/running.txt");
+//                    String tomcatLog = "scenenum:" + message.getSceneNum() + "\ntime:" + DateUtil.formatDateTime(new Date()) +
+//                            " action:delete hostname:" + SysConstants.hostName;
+//                    //打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
+//                    FileWriter writer = new FileWriter("/mnt/elastic_log/tomcat" + "_" + message.getSceneNum() + "log", true);
+//                    writer.write(tomcatLog);
+//                    writer.close();
+//                } catch (Exception e) {
+//                    e.printStackTrace();
+//                }
+//            } catch (Exception e) {
+//                log.error("大场景计算失败",e);
+//                // TODO: 2022/1/25 测试阶段,先注释掉推送
+////                this.handFail("计算失败", message);
+//            }
+//            log.info("场景生成好了***");
+//            return "success";
+//        };
+//        Future<String> future = exec.submit(call);
+//        try {
+//            future.get(SysConstants.modelTimeOut, TimeUnit.HOURS); //任务处理超时时间设为 24个小时
+//        } catch (TimeoutException ex) {
+//            ex.printStackTrace();
+//            StringWriter trace=new StringWriter();
+//            ex.printStackTrace(new PrintWriter(trace));
+//            log.error("超时了");
+//            log.error(trace.toString());
+//            FileUtils.deleteFile("/opt/hosts/running.txt");
+//            future.cancel(true);
+//            // TODO: 2022/1/25 测试阶段,先注释掉推送
+////            handFail("计算超时",message);
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//            StringWriter trace=new StringWriter();
+//            e.printStackTrace(new PrintWriter(trace));
+//            log.error(trace.toString());
+//            FileUtils.deleteFile("/opt/hosts/running.txt");
+//            future.cancel(true);
+//            // TODO: 2022/1/25 测试阶段,先注释掉推送
+////            handFail("计算失败",message);
+//        }
+//
+//    }
+//
+//    private void buildScene(BuildSceneMqMessage message){
+//        boolean buildSuccess = Boolean.FALSE;
+//        String projectNum = null;
+//        Long computeTime = null;//计算耗时
+//        JSONObject videosJson = null;
+//        //如果mq生产者在消息体中执行了计算结果mq的主题名,就发到这个主题名,否则就发送到配置的主题名
+//        String resultTopicName = StrUtil.isNotBlank(message.getResultReceiverMqName()) ?
+//                message.getResultReceiverMqName() : buildSceneResultTopic;
+//        //该场景使用的容量
+//        Long space = 0L;
+//        //支付状态
+//        int payStatus = 0;
+//        String unicode = message.getUnicode();
+//        String path = message.getPath();
+//        String prefix = message.getPrefix();
+//        String imgsName = message.getImgsName();
+//        projectNum = message.getSceneNum();
+//        String userName = message.getUserName();
+//        //不同的相机不同的方法
+//        String cameraType = message.getCameraType();
+//        String algorithm = message.getAlgorithm();
+//        String fileId = message.getFileId();
+//        String cameraName = message.getCameraName();
+//        //0表示有4k图,1表示没有
+//        String resolution = message.getResolution();
+//        //判断调用V2还是V3版本的算法
+//        String buildType = ObjectUtils.isEmpty(message.getBuildType()) ? "V2" : message.getBuildType();
+//        String sceneName = message.getSceneName();
+//        String webSite = message.getWebSite();
+//        Date createTime = message.getSceneProCreateTime();
+//        Long userId = message.getUserId();
+//        String dataSource = message.getDataSource();
+//        Integer sceneStatus = message.getSceneStatus();
+//        Integer sceneProPayStatus = message.getPayStatus();
+//        Long sceneProId = message.getSceneProId();
+//        String thumb = message.getThumb();
+//
+//        String imgPath = String.format(UploadFilePath.IMG_VIEW_PATH, projectNum);       //新版本文件路径
+//        String dataPath = String.format(UploadFilePath.DATA_VIEW_PATH, projectNum);     //新版本文件路径
+//        String videosPath = String.format(UploadFilePath.VIDEOS_VIEW_PATH, projectNum);     //新版本文件路径
+//
+//        String oldDataPath = "data/data" + projectNum;//老版本文件路径
+//
+//        try{
+//            if(StringUtils.equals(message.getIsStandardization(),"1")){
+//                //表示标定算法
+//                ComputerUtil.computerCalibration(message.getPath());
+//                return;
+//            }
+//
+//            Map<String, String> dataMap = ComputerUtil.getTypeString(cameraType, algorithm,
+//                    resolution);
+//            String splitType = dataMap.get("splitType");
+//            String skyboxType = dataMap.get("skyboxType");
+//            String dataDescribe = dataMap.get("dataDescribe");
+//
+//            log.info("用的算法是:"+algorithm);
+//            log.info("用的相机是:"+ (Integer.parseInt(cameraType) < 4 ? "单球目" : "双球目(八目)"));
+//            Map<String,String> map = new HashMap<>();
+//
+//            //4表示硬件部研发的双球目相机,其余为旧版本相机
+//            if(Integer.parseInt(cameraType) < 3){
+//                for(int i = 0;i<5;++i){
+//                    try{
+//                        FileUtils.downLoadFromUrl(prefix+imgsName+"?m="+new Date().getTime(), imgsName,
+//                                path + File.separator + "capture");
+//                        FileUtils.decompress(path + File.separator + "capture" +File.separator+imgsName,
+//                                path + File.separator + "capture") ;
+//                        break;
+//                    }catch(Exception e){
+//                        log.error(String.format("第%d次下载并解压资源失败", i+1),  e);
+//                        if(i<4)
+//                        {
+//                            FileUtils.deleteFile(path + File.separator + "capture" +File.separator+imgsName);
+//                            FileUtils.delFolder(path + File.separator + "capture" +File.separator+"images");
+//                        }
+//                        Thread.sleep(10000);
+//                    }
+//                }
+//
+//                FileUtils.deleteFile(path + File.separator + "capture" +File.separator+"zip.Zip");
+//
+//                String data = FileUtils.readFile(path + File.separator + "capture" +File.separator+"data.fdage");
+//                JSONObject dataJson = new JSONObject();
+//                if(data!=null){
+//                    dataJson = JSONObject.parseObject(data);
+//                    if(dataJson.containsKey("imgs")){
+//                        splitType = "SPLIT_V4";
+//                    }
+//                }
+//                //生成project.json和data.json供算法部使用
+//                ComputerUtil.createJson(path, splitType, skyboxType, dataDescribe, projectNum, path);
+//                //计算模型并返回需要上传oss的文件集合
+//                map = ComputerUtil.computer(projectNum, path, buildType);
+//
+//                uploadToOssUtil.uploadMulFiles(map);
+//                log.info("双目上完oss结束修改数据:"+projectNum);
+//            } else {
+//
+//                //休眠1秒,否则可能获取不到场景资源
+//                Thread.sleep(1000);
+//                if(sceneProId == null){
+//                    log.info(projectNum + ":场景不存在");
+//                    // TODO: 2022/1/25 测试阶段,先注释掉推送
+////                    handFail("场景不存在",message);
+//                    return;
+//                }
+//
+//                long start = System.currentTimeMillis();
+//
+//                //下载资源到本地
+//                path = this.downLoadSource(path, Integer.valueOf(cameraType), cameraName, unicode, fileId, imgsName, prefix);
+//
+//                String dataFdagePath = path + File.separator + "capture" +File.separator+"data.fdage";
+//                log.info("dataFdagePath 文件路径 :{}", dataFdagePath);
+//                String data = FileUtils.readFile(dataFdagePath);
+//                //获取data.fdage的内容
+//                JSONObject dataJson = new JSONObject();
+//                if(data!=null){
+//                    dataJson = JSONObject.parseObject(data);
+//                }
+//
+//                /*
+//                 1、判断是否计算过资源,若计算过删除缓存, 如果caches中存在_images文件或者目录,就删除掉,否则就删除除了images以外的所有文件和目录
+//                 2、删除上一次计算出来的result目录
+//                 */
+//                this.deleteCachesAndResult(path);
+//
+//                //v2版本使用4k算法
+//                if(dataJson.containsKey("videoVersion")
+//                        && StringUtils.isNotEmpty(dataJson.getString("videoVersion"))
+//                        && Integer.parseInt(dataJson.getString("videoVersion")) < 4){
+//                    skyboxType = "SKYBOX_V6";
+//                }
+//
+//                //生成project.json和data.json并写到path目录下供算法部使用
+//                log.info("path:" + path);
+//                ComputerUtil.createJson(path, splitType, skyboxType, dataDescribe, projectNum, path);
+//                //计算模型并返回需要上传oss的文件集合
+//                map = ComputerUtil.computer(projectNum, path, buildType);
+//                //计算完成时间
+//                computeTime = (System.currentTimeMillis() - start) / 1000;
+//                log.info("计算完成耗时:{}",computeTime);
+//
+//                if(Integer.parseInt(cameraType) == 5 || Integer.parseInt(cameraType) == 6){
+//                    //todo 老版本old_path 待删除
+//                    map.put(path + File.separator + "capture/stitch_params.txt",
+//                            oldDataPath + "/stitch_params.txt");
+//                    //新版本文件路径
+//                    map.put(path + File.separator + "capture/stitch_params.txt",
+//                            dataPath+ "stitch_params.txt");
+//                }
+//
+//                //todo 老版本old_path 待删除
+//                map.put(path + File.separator + "capture/Up.xml", oldDataPath + "/Up.xml");
+//                map.put(path + File.separator + "capture/Up2.xml", oldDataPath + "/Up2.xml");
+//                //新版本文件路径
+//                map.put(path + File.separator + "capture/Up.xml", dataPath  + "Up.xml");
+//                map.put(path + File.separator + "capture/Up2.xml", dataPath + "Up2.xml");
+//
+//                //转台相机
+//                if(Integer.parseInt(cameraType) == 13){
+//
+//                    //todo 老版本old_path 待删除
+//                    map.put(path + File.separator + "capture/Up.txt", oldDataPath + "/Up.txt");
+//                    map.put(path + File.separator + "capture/Up2.txt", oldDataPath + "/Up2.txt");
+//
+//                    map.put(path + File.separator + "capture/Up.txt", dataPath+ "Up.txt");
+//                    map.put(path + File.separator + "capture/Up2.txt",dataPath + "Up2.txt");
+//                }
+//
+//                //上传计算结果中的caches/images和caches/videos
+//                this.uploadCaches(projectNum, map, path);
+//
+//                log.info("计算完成,批量上传文件集合:{}", map.toString());
+//                //上传计算后生成的文件
+//                uploadToOssUtil.uploadMulFiles(map);
+//
+//                //拷贝部分文件到编辑目录,用于用户编辑
+//                this.copyToEditDir(projectNum);
+//
+//                //计算已使用容量
+//                payStatus = 1;
+//                File spaceFile = null;
+//                for (String key : map.keySet()) {
+//                    spaceFile = new File(key);
+//                    if(spaceFile.exists()){
+//                        space += spaceFile.length();
+//                    }
+//                }
+//
+//                log.info("八目上完oss结束修改数据:"+projectNum);
+//
+//                //读取计算结果文件生成videosJson
+//                videosJson = this.getVideosJson(path, dataJson, projectNum, Integer.valueOf(cameraType));
+//
+//                //更新scene.json里面的video数据
+//                this.updateVideosToSceneJson(projectNum, videosJson);
+//
+//                //根据data.fdage推送计算完成的消息
+//                this.sendSms(dataJson, Integer.valueOf(cameraType), sceneName, webSite);
+//
+//                //计算成功  激光转台相机 同步 请求
+//                this.jgSynch(projectNum, Integer.valueOf(cameraType), sceneName, dataSource, createTime, userId, userName, cameraName);
+//            }
+//
+//            CreateObjUtil.deleteFile(path.replace(ConstantFilePath.BUILD_MODEL_PATH, "/") + "/capture");
+//            buildSuccess = Boolean.TRUE;
+//        } catch(Exception e){
+//            log.error("计算大场景失败"+projectNum, e);
+//            // TODO: 2022/1/25 测试阶段,先注释掉推送
+////            handFail("计算失败",message);
+//        }finally {
+//            //发送计算结果到mq,再由消费者消费并操作数据库
+//            this.sendCallResult(buildSuccess, resultTopicName, cameraType, computeTime, fileId, payStatus, projectNum, space, videosJson);
+//            //上传status.json   console.log
+//            this.uploadStatusJsonAndConsoleLog(sceneProId, sceneStatus, webSite, projectNum, thumb, payStatus, dataPath, dataSource);
+//
+//        }
+//    }
+//
+//    private void uploadCaches(String num, Map<String,String> map, String path){
+//
+//        String sceneNumPAth = String.format("scene/%s", num);
+//
+//        List<String> imagesList = FileUtil.getFileList(path + "/caches/images");
+//        log.info("caches/images_path:{}", path + "/caches/images");
+//        if(CollUtil.isNotEmpty(imagesList)){
+//            log.info("上传的caches/images文件列表:{}", imagesList.toString());
+//            imagesList.stream().forEach(str -> map.put(str, str.replace(path, sceneNumPAth)));
+//        }
+//
+//        List<String> videosList = FileUtil.getFileList(path + "/caches/videos");
+//        if(CollUtil.isNotEmpty(videosList)){
+//            log.info("上传的caches/videos文件列表:{}", videosList.toString());
+//            videosList.stream().forEach(str -> map.put(str, str.replace(path, sceneNumPAth)));
+//        }
+//
+//    }
+//
+//    private void copyToEditDir(String num) throws IOException {
+//
+//        String editImagesPath = String.format(UploadFilePath.IMG_EDIT_PATH, num);
+//        String viewImagesPath = String.format(UploadFilePath.IMG_VIEW_PATH, num);
+//
+//        String editDataPath = String.format(UploadFilePath.DATA_EDIT_PATH, num);
+//        String viewDataPath = String.format(UploadFilePath.DATA_VIEW_PATH, num);
+//
+//        Map<String, String> map = new HashMap<>();
+//        map.put(editImagesPath + "vision.modeldata", viewImagesPath + "vision.modeldata");
+//        map.put(editImagesPath + "vision2.modeldata", viewImagesPath + "vision2.modeldata");
+//        map.put(editDataPath + "floorplan_cad.json", viewDataPath + "floorplan_cad.json");
+//
+//        for (Entry<String, String> entry : map.entrySet()) {
+//            uploadToOssUtil.copyFiles(entry.getValue(), entry.getKey());
+//        }
+//    }
+//
+//    private void sendCallResult(boolean buildSuccess, String resultTopicName, String cameraType, Long computeTime, String fileId,
+//                                Integer payStatus, String projectNum, Long space, JSONObject videosJson){
+//        rocketMQProducer.sendOneWay(resultTopicName,
+//                BuildSceneResultMqMessage.builder()
+//                        .buildSuccess(buildSuccess)
+//                        .cameraType(cameraType)
+//                        .computeTime(computeTime)
+//                        .fileId(fileId)
+//                        .payStatus(payStatus)
+//                        .sceneCode(projectNum)
+//                        .space(space)
+//                        .videosJson(buildSuccess? videosJson.toJSONString() : null)
+//                        .build());
+//    }
+//
+//    private void uploadStatusJsonAndConsoleLog(Long sceneProId, Integer sceneStatus, String webSite, String projectNum,
+//                                               String thumb, Integer payStatus, String dataPath, String dataSource){
+//        try{
+//            if(Objects.nonNull(sceneProId)){
+//                com.alibaba.fastjson.JSONObject statusJson = new com.alibaba.fastjson.JSONObject();
+//                //临时将-2改成1,app还没完全更新
+//                if(sceneStatus == -2){
+//                    sceneStatus = 1;
+//                }else if(sceneStatus == -1){
+//                    // 失败状态不予显示到客户端
+//                    sceneStatus = 0;
+//                }
+//                statusJson.put("status", sceneStatus);
+//                statusJson.put("webSite", webSite);
+//                statusJson.put("sceneNum", projectNum);
+//                statusJson.put("thumb", thumb);
+//                statusJson.put("payStatus", payStatus);
+//                FileUtils.writeFile(ConstantFilePath.SCENE_PATH+"data/data"+projectNum+File.separator+"status.json", statusJson.toString());
+//
+//                //todo 待删除old_path
+//                uploadToOssUtil.upload(ConstantFilePath.SCENE_PATH+"data/data"+projectNum+File.separator+"status.json",
+//                        "data/data"+projectNum+File.separator+"status.json");
+//
+//                uploadToOssUtil.upload(ConstantFilePath.SCENE_PATH+"data/data"+projectNum+File.separator+"status.json",
+//                        dataPath+"status.json");
+//
+//                //上送日志  //todo 待删除old_path
+//                uploadToOssUtil.upload(dataSource+File.separator+"console.log",
+//                        "build_log/"+projectNum+File.separator+"console.log");
+//
+//                //上送日志
+//                uploadToOssUtil.upload(dataSource+File.separator+"console.log",
+//                        dataPath+"console.log");
+//
+//            }
+//        }catch (Exception e){
+//            log.error("上传status.json/console.log失败", e);
+//        }
+//    }
+//
+//    private void jgSynch(String projectNum, int cameraType, String sceneName, String dataSource, Date createTime, Long userId, String userName, String cameraName){
+//        log.info("激光转台相机 同步 请求 ");
+//        if(cameraType != 14)
+//            return;
+//        try {
+//            String title = "";
+//            if(StringUtils.isNotEmpty(sceneName)){
+//                title = sceneName;
+//            }
+////                        String dataSource = sceneProExt.getDataSource();
+//            String jgPath = dataSource;
+//            //创建目录
+//            if(dataSource.lastIndexOf("/")!=-1){
+//                jgPath = jgPath + "_laserData";
+//            }else{
+//                jgPath = jgPath.substring(0,jgPath.length()-1) + "_laserData";
+//            }
+//
+//            FileUtils.createDir(jgPath+"/extras");
+//
+//            log.info("生成 激光相机目录 " + jgPath);
+//            //生成data.json
+//            JSONObject jgDataJson = new JSONObject();
+//            jgDataJson.put("split_type", "SPLIT_V15");
+//            jgDataJson.put("skybox_type", "SKYBOX_V5");
+//            jgDataJson.put("extras", null);
+//            FileUtils.writeFile(jgPath + File.separator + "data.json", jgDataJson.toString());
+//
+//            CreateObjUtil.cpfile(dataSource  + "/results/laserData/cover", jgPath+"/extras/");
+//            CreateObjUtil.cplaserfile(dataSource  + "/results/laserData", jgPath+File.separator);
+//
+//            //激光相机
+//            String url = laserHost+"/indoor/{sceneCode}/service/init?path="
+//                    + jgPath + File.separator + "laserData" + "&title="+ title
+//                    + "&childName=" + cameraName + "&createTime=" +  createTime
+//                    + "&snCode="+ cameraName;
+//
+//            if(userId!=null){
+//                url = url + "&userId=" + userId;
+//            }
+//
+//            if(StringUtils.isNotEmpty(userName)){
+//                url = url + "&phone=" + userName;
+//            }
+//
+//            url = url.replace("{sceneCode}",projectNum);
+//            log.info("激光转台相机 同步 :" + url);
+//            com.alibaba.fastjson.JSONObject hotListJson =
+//                    com.alibaba.fastjson.JSONObject.parseObject(restTemplate.getForObject(url,String.class));
+//            log.info("激光转台相机 同步结束 :" + hotListJson);
+//        }catch (Exception e){
+//            log.error("激光转台相机同步失败", e);
+//            // TODO: 2022/1/25 测试阶段,先注释掉推送
+////          handFail("激光转台相机同步失败",message);
+//        }
+//    }
+//
+//    private void sendSms(JSONObject dataJson, int cameraType, String sceneName, String webSite){
+//        log.info("推送消息,渠道是 {}, 手机token是 {}", dataJson.get("pushChannel"), dataJson.get("pushToken"));
+//        if(dataJson.containsKey("pushChannel") && dataJson.containsKey("pushToken")){
+//            try{
+//                if(StorageType.AWS.code().equals(ossType)){
+//                    PushMsgUtil.googlePushMsg(ConstantFilePath.BASE_PATH + "/refreshToken.json",dataJson.getString("pushToken"),
+//                            sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                            webSite);
+//                    return;
+//                }
+//
+//                PushMessageConfig demo = null;
+//                if(dataJson.getIntValue("pushChannel") == 0){
+//
+//                    if(cameraType == 10 || cameraType == 13){
+//                        //ios
+//                        log.info("IOS_KEY:{}, IOS_SECRET:{}", IOS_KEY_Z, IOS_SECRET_Z);
+//                        demo = new PushMessageConfig(IOS_KEY_Z, IOS_SECRET_Z);
+//                        demo.sendIOSUnicast(dataJson.getString("pushToken"),
+//                                "四维看看Minion", sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                                webSite);
+//                    }else {
+//                        //ios
+//                        log.info("IOS_KEY:{}, IOS_SECRET:{}", IOS_KEY, IOS_SECRET);
+//                        demo = new PushMessageConfig(IOS_KEY, IOS_SECRET);
+//                        demo.sendIOSUnicast(dataJson.getString("pushToken"),
+//                                "四维看看Pro", sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                                webSite);
+//                    }
+//
+//                }else {
+//                    if(cameraType == 10 || cameraType == 13){
+//                        //ios
+//                        //安卓
+//                        log.info("ANDROID_KEY:{}, ANDROID_SECRET:{}", ANDROID_KEY_Z, ANDROID_SECRET_Z);
+//                        demo = new PushMessageConfig(ANDROID_KEY_Z, ANDROID_SECRET_Z);
+//                        demo.sendAndroidUnicast2(dataJson.getString("pushToken"),
+//                                "四维看看Minion", sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                                webSite);
+//                    }else {
+//                        //安卓
+//                        log.info("ANDROID_KEY:{}, ANDROID_SECRET:{}", ANDROID_KEY, ANDROID_SECRET);
+//                        demo = new PushMessageConfig(ANDROID_KEY, ANDROID_SECRET);
+//                        demo.sendAndroidUnicast(dataJson.getString("pushToken"),
+//                                "四维看看Pro", sceneName + "计算完成", "您上传的" + sceneName + "计算完成,点击查看",
+//                                webSite);
+//                    }
+//                }
+//                log.info("消息推送结束!");
+//            }catch (Exception e){
+//                log.error("推送消息失败:", e);
+//            }
+//        }
+//    }
+//
+//    private void updateVideosToSceneJson(String projectNum, JSONObject videosJson) throws Exception {
+//        String sceneJsonPath = String.format(ConstantFilePath.SCENE_PATH_FORMAT, projectNum);
+//        String strsceneInfos = FileUtils.readFile(sceneJsonPath);
+//        JSONObject scenejson = new JSONObject();
+//        if(strsceneInfos!=null){
+//            scenejson = com.alibaba.fastjson.JSONObject.parseObject(strsceneInfos);
+//        }
+//        scenejson.put("videos", videosJson.toJSONString());
+//        FileUtils.writeFile(sceneJsonPath, scenejson.toString());
+//    }
+//
+//    private JSONObject getVideosJson(String path, JSONObject dataJson, String projectNum, int cameraType) throws Exception {
+//        //读取videos_hdr_param.json, 保存点位视频的value
+//        Map<String, Object> videoMap = new HashMap<>();
+//        String videosHdr = FileUtils.readFile(path + File.separator + "results/videos/videos_hdr_param.json");
+//        JSONArray videoArray = null;
+//        if(StringUtils.isNotEmpty(videosHdr)){
+//            videoArray = com.alibaba.fastjson.JSONObject.parseObject(videosHdr).getJSONArray("hdr_param");
+//        }
+//        if(videoArray != null){
+//            for(int i = 0, len = videoArray.size(); i < len; i++) {
+//                videoMap.put(videoArray.getJSONObject(i).getString("name"), videoArray.getJSONObject(i).getString("value"));
+//                if(videoArray.getJSONObject(i).containsKey("fov")){
+//                    videoMap.put(videoArray.getJSONObject(i).getString("name") + "_fov", videoArray.getJSONObject(i).getString("fov"));
+//                }
+//            }
+//        }
+//
+//        //获取upload中的video视频名称
+//        String uploadData = FileUtils.readFile(path + File.separator + "results" +File.separator+"upload.json");
+//        com.alibaba.fastjson.JSONObject uploadJson = null;
+//        JSONArray array = null;
+//        if(uploadData!=null) {
+//            uploadJson = com.alibaba.fastjson.JSONObject.parseObject(uploadData);
+//            array = uploadJson.getJSONArray("upload");
+//        }
+//        com.alibaba.fastjson.JSONObject fileJson = null;
+//        String fileName = "";
+//
+//        //计算ts文件的大小,并拼接成json格式
+//        JSONArray jsonArray = new JSONArray();
+//        com.alibaba.fastjson.JSONObject videoJson = null;
+//        JSONObject videosJson = new JSONObject();
+//        long videoSize = 0L;
+//        for(int i = 0, len = array.size(); i < len; i++) {
+//            fileJson = array.getJSONObject(i);
+//            fileName = fileJson.getString("file");
+//            if(fileJson.getIntValue("clazz") == 11 && fileName.contains(".mp4") && !fileName.contains("-ios.mp4")){
+//                videoJson = new com.alibaba.fastjson.JSONObject();
+//                videoJson.put("id", fileName.substring(
+//                        0, fileName.lastIndexOf(".")).replace("videos/", ""));
+//
+//                //如果ts文件存在,就计算ts大小
+//                if(new File(path + File.separator + "results" +File.separator+ fileName.replace(".mp4", ".ts")).exists()){
+//                    videoSize = new File(path + File.separator + "results" +File.separator+ fileName.replace(".mp4", ".ts")).length();
+//                    videoJson.put("tsSize", videoSize);
+//                }
+//                if(videoMap.containsKey(videoJson.get("id"))){
+//                    videoJson.put("value", videoMap.get(videoJson.get("id")));
+//                }
+//                if(videoMap.containsKey(videoJson.get("id") + "_fov")){
+//                    videoJson.put("blend_fov", videoMap.get(videoJson.get("id") + "_fov"));
+//                }else {
+//                    videoJson.put("blend_fov", 7);
+//                }
+//                jsonArray.add(videoJson);
+//            }
+//        }
+//
+//        videosJson.put("data", jsonArray);
+//        if(dataJson.containsKey("videoVersion") && StringUtils.isNotEmpty(dataJson.getString("videoVersion")) && Integer.parseInt(dataJson.getString("videoVersion")) >= 4){
+//            videosJson.put("version", 3);
+//            if(StorageType.OSS.code().equals(ossType)){
+//                videosJson.put("upPath", prefixAli + "data/data" + projectNum + "/Up.xml");
+//            }
+//            if(StorageType.AWS.code().equals(ossType)){
+//                videosJson.put("upPath", ConstantUrl.PREFIX_AWS + "data/data" + projectNum + "/Up.xml");
+//            }
+//            if(cameraType == 13){
+//                //转台相机
+//                videosJson.put("upPath", videosJson.getString("upPath").replace(".xml", ".txt"));
+//            }
+//        }else {
+//            videosJson.put("version", 1);
+//            if("oss".equals(ossType)){
+//                videosJson.put("upPath", prefixAli + "data/data" + projectNum + "/Up2.xml");
+//            }
+//            if("aws".equals(ossType)){
+//                videosJson.put("upPath", ConstantUrl.PREFIX_AWS + "data/data" + projectNum + "/Up2.xml");
+//            }
+//
+//            if(cameraType == 13){
+//                //转台相机
+//                videosJson.put("upPath", videosJson.getString("upPath").replace(".xml", ".txt"));
+//            }
+//        }
+//
+//        if(cameraType == 5 || cameraType == 6){
+//            videosJson.put("version", 1);
+//            if("oss".equals(ossType)){
+//                videosJson.put("upPath", prefixAli + "data/data" + projectNum + "/stitch_params.txt");
+//            }
+//            if("aws".equals(ossType)){
+//                videosJson.put("upPath", ConstantUrl.PREFIX_AWS + "data/data" + projectNum + "/stitch_params.txt");
+//            }
+//        }
+//
+//        return videosJson;
+//    }
+//
+//    private String downLoadSource(String path, int cameraType, String cameraName, String unicode, String fileId, String imgsName, String prefix) throws Exception {
+//        if(cameraType < 4){
+//            throw new Exception("cameraType 不能小于4");
+//        }
+//        //cameraType=5为新版本双目, cameraType=6为小红屋新版本双目
+//        String ossPath = ConstantFilePath.OSS_PREFIX + cameraName.replace("4DKKPRO_", "")
+//                .replace("-fdage", "").toLowerCase() + File.separator + fileId + File.separator
+//                + unicode + File.separator;
+//        String localPath = cameraName.replace("4DKKPRO_", "").replace("-fdage", "").toLowerCase() +
+//                File.separator + fileId + File.separator + unicode;
+//        if(cameraType == 5 || cameraType == 6){
+//            path = ConstantFilePath.BUILD_MODEL_PATH + unicode;
+//            //下载zip包
+//            FileUtils.downLoadFromUrl(prefix + "/" + imgsName + "?m=" + System.currentTimeMillis(), imgsName, path + File.separator + "capture");
+//            //解压
+//            FileUtils.decompress(path + File.separator + "capture" + File.separator + imgsName,
+//                    path + File.separator + "capture") ;
+//            //删除压缩包
+//            FileUtil.delFile(path + File.separator + "capture" + File.separator + imgsName);
+//        }else if(cameraType == 14) {
+////                    + File.separator + "capture"
+//            path = ConstantFilePath.BUILD_MODEL_LASER_PATH + localPath;
+//            CreateObjUtil.ossUtilCp(ossPath,path + File.separator + "capture");
+//        } else{
+//            path = ConstantFilePath.BUILD_MODEL_PATH + localPath;
+//            CreateObjUtil.ossUtilCp(ossPath,path + File.separator + "capture");
+//        }
+//        return path;
+//    }
+//
+//    private void deleteCachesAndResult(String path){
+//        //判断是否计算过资源,若计算过删除缓存, 如果caches中存在_images文件或者目录,就删除掉,否则就删除除了images以外的所有文件和目录
+//        File caches = new File(path + File.separator + "caches");
+//        if(caches.exists()){
+//            for(File deleteFile : caches.listFiles()){
+//                if(new File(path + "_images").exists()){
+//                    if(deleteFile.isDirectory()){
+//                        FileUtils.delAllFile(deleteFile.getAbsolutePath());
+//                    }else {
+//                        FileUtils.deleteFile(deleteFile.getAbsolutePath());
+//                    }
+//                }else if(!deleteFile.getAbsolutePath().contains("images")){
+//                    if(deleteFile.isDirectory()){
+//                        FileUtils.delAllFile(deleteFile.getAbsolutePath());
+//                    }else {
+//                        FileUtils.deleteFile(deleteFile.getAbsolutePath());
+//                    }
+//                }
+//            }
+//        }
+//        //删除上一次计算出来的result目录
+//        if(new File(path + File.separator + "results").exists()){
+//            FileUtils.delAllFile(path + File.separator + "results");
+//        }
+//    }
+//
+//    private void handFail(String reason,BuildSceneMqMessage message){
+//        String serverPath = message.getPath().substring(0,message.getPath().lastIndexOf("/")+1).concat(message.getPrefix());
+//        handFail(reason,serverPath,message.getSceneNum());
+//    }
+//
+//    private void handFail(String reason,String serverPath,String num){
+//        // 释放缓存锁
+//        redisUtil.del(RedisKey.SCENE_BUILDING + num);
+//        CompletableFuture.runAsync(() -> {
+//            try {
+//                String content = String.format(SysConstants.DINGTALK_MSG_PATTERN,environment, SysConstants.hostName,reason,num,serverPath,num,num);
+//                dingTalkSendUtils.sendActioncardMsgToDingRobot(content,"场景计算失败");
+//            } catch (ApiException | UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException apiException) {
+//                apiException.printStackTrace();
+//            }
+//        });
+//    }
+//}

+ 533 - 0
src/main/java/com/fdkankan/modeling/receiver/RabbitMqListener.java

@@ -0,0 +1,533 @@
+package com.fdkankan.modeling.receiver;
+
+import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.fdkankan.common.constant.ConstantFilePath;
+import com.fdkankan.common.constant.ModelingBuildStatus;
+import com.fdkankan.common.constant.ServerCode;
+import com.fdkankan.common.constant.UploadFilePath;
+import com.fdkankan.common.exception.BusinessException;
+import com.fdkankan.common.util.ComputerUtil;
+import com.fdkankan.common.util.CreateObjUtil;
+import com.fdkankan.common.util.FileUtils;
+import com.fdkankan.common.util.SceneUtil;
+import com.fdkankan.dingtalk.DingTalkSendUtils;
+import com.fdkankan.fyun.oss.UploadToOssUtil;
+import com.fdkankan.modeling.bean.BuildSceneResultBean;
+import com.fdkankan.modeling.constants.SysConstants;
+import com.fdkankan.modeling.entity.BuildLog;
+import com.fdkankan.modeling.exception.BuildException;
+import com.fdkankan.modeling.service.IBuildLogService;
+import com.fdkankan.rabbitmq.bean.BuildSceneCallMessage;
+import com.fdkankan.rabbitmq.bean.BuildSceneFailDTMqMessage;
+import com.fdkankan.rabbitmq.bean.BuildSceneResultMqMessage;
+import com.fdkankan.rabbitmq.util.RabbitMqProducer;
+import com.fdkankan.redis.constant.RedisKey;
+import com.fdkankan.redis.util.RedisUtil;
+import com.rabbitmq.client.Channel;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+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.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * <p>
+ * TODO
+ * </p>
+ *
+ * @author dengsixing
+ * @since 2022/4/20
+ **/
+@Slf4j
+@Component
+public class RabbitMqListener {
+
+    @Value("${queue.modeling.modeling-call}")
+    private String queueModelingCall;
+    @Value("${queue.modeling.modeling-dt}")
+    private String queueModelingDt;
+    @Value("${queue.modeling.modeling-post}")
+    private String queueModelingPost;
+
+    @Autowired
+    RedisUtil redisUtil;
+
+    private RestTemplate restTemplate = new RestTemplate();
+
+    @Value("${4dkk.laserService.host}")
+    private String laserHost;
+
+    @Value("${upload.type}")
+    private String ossType;
+
+    @Value("${oss.prefix.ali}")
+    private String prefixAli;
+
+    @Autowired
+    private UploadToOssUtil uploadToOssUtil;
+
+    @Autowired
+    RabbitMqProducer rabbitMqProducer;
+
+    @Value("${spring.profiles.active}")
+    private String environment;
+
+    @Autowired
+    private DingTalkSendUtils dingTalkSendUtils;
+
+    @Autowired
+    private IBuildLogService buildLogService;
+
+    /**
+     * 场景计算
+     * @param channel
+     * @param message
+     * @throws Exception
+     */
+    @RabbitListener(
+        queuesToDeclare = @Queue("${queue.modeling.modeling-call}"),
+        concurrency = "${maxThread.modeling.modeling-call}"
+    )
+    public void buildSceneHandler(Channel channel, Message message) throws Exception {
+        Object correlation = message.getMessageProperties().getHeader("spring_returned_message_correlation");
+        String correlationId = (String) correlation;
+        if (ObjectUtils.isEmpty(message.getBody())) {
+            SysConstants.SYSTEM_BUILDING = false;
+            log.error("消息体为空,退出构建,当前服务器:{}", SysConstants.hostName);
+            return;
+        }
+        String msg = new String(message.getBody(), StandardCharsets.UTF_8);
+        BuildSceneCallMessage buildSceneMessage = JSONObject.parseObject(msg, BuildSceneCallMessage.class);
+        log.info("场景计算开始,队列名:{},id:{},消息体:{}", queueModelingCall, correlationId,msg);
+        if(Objects.isNull(buildSceneMessage) || StrUtil.isBlank(buildSceneMessage.getSceneNum())){
+            log.error("消息内容错误,id:{},消息体:{}", correlationId, msg);
+            return;
+        }
+
+        Thread.sleep(2000L);
+        this.process(buildSceneMessage);
+
+        log.info("场景计算开始,队列名:{},id:{}", queueModelingCall, correlationId);
+
+        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
+    }
+
+    public void process(BuildSceneCallMessage message) {
+
+        SysConstants.SYSTEM_BUILDING = true;
+        if (SysConstants.SYSTEM_OFFING) {
+            SysConstants.SYSTEM_BUILDING = false;
+            log.error("服务实例:{} 正在关闭,退出构建!", SysConstants.hostName);
+            throw new BusinessException(ServerCode.SERVER_CLOSING);
+        }
+        //开始计时
+        StopWatch watch = new StopWatch();
+        watch.start();
+
+        BuildLog buildLog = new BuildLog();
+        final BuildSceneResultBean buildSceneResult = new BuildSceneResultBean();
+        Future<ModelingBuildStatus> future = null;
+        try {
+
+            //休眠2秒等待准备数据
+            Thread.sleep(2000L);
+
+            //计算前准备
+            preBuild(message, buildLog);
+
+            future = SysConstants.executorService.submit(()->{
+                return buildScene(message, buildSceneResult);
+            });
+            future.get(SysConstants.modelTimeOut, TimeUnit.HOURS);
+
+            //结束计时
+            watch.stop();
+            buildLog.setDuration(watch.getTime(TimeUnit.SECONDS));
+        } catch (TimeoutException ex) {
+            log.error("服务实例:{} 构建异常:",SysConstants.hostName,ex);
+            buildSceneResult.setBuildStatus(ModelingBuildStatus.OVERTIME);
+        } catch (BuildException e){
+            buildSceneResult.setBuildStatus(e.getBuildStatus());
+        } catch(Exception e) {
+            log.error("服务实例:{} 构建异常:", SysConstants.hostName, e);
+            if (e.getCause() instanceof BuildException) {
+                buildSceneResult.setBuildStatus(((BuildException) e.getCause()).getBuildStatus());
+            } else {
+                buildSceneResult.setBuildStatus(ModelingBuildStatus.FAILED);
+                //未知异常,记录异常信息
+                buildLog.setReason(ExceptionUtil.stacktraceToString(e, 3000));
+            }
+        }
+        //计算后处理
+        afterBuild(message, buildSceneResult, buildLog);
+
+    }
+
+    private ModelingBuildStatus buildScene(BuildSceneCallMessage message, BuildSceneResultBean buildSceneResult) throws Exception{
+        Long computeTime = null;//计算耗时
+        String num = null;
+        //如果mq生产者在消息体中设置了结算结果mq队列名,就发到这个队列,否则就发送到默认队列
+        String resultQueueName = StrUtil.isNotBlank(message.getResultReceiverMqName()) ?
+            message.getResultReceiverMqName() : queueModelingPost;
+        //支付状态
+        int payStatus = 0;
+        String unicode = message.getUnicode();
+        String path = message.getPath();
+        String prefix = message.getPrefix();
+        num = message.getSceneNum();
+        //不同的相机不同的方法
+        String cameraType = message.getCameraType();
+        String algorithm = message.getAlgorithm();
+        String fileId = message.getFileId();
+        String cameraName = message.getCameraName();
+        //0表示有4k图,1表示没有
+        String resolution = message.getResolution();
+        //判断调用V2还是V3版本的算法
+        String buildType = ObjectUtils.isEmpty(message.getBuildType()) ? "V2" : message.getBuildType();
+
+        String dataPath = "data/data" + num;//老版本文件路径
+
+        Map<String,String> map = null;
+        Integer pushChannel = null;
+        String pushToken = null;
+        Integer videoVersion = null;
+
+        buildSceneResult.setResultQueueName(resultQueueName);
+        buildSceneResult.setCameraType(cameraType);
+        buildSceneResult.setNum(num);
+
+
+        Map<String, String> dataMap = ComputerUtil.getTypeString(cameraType, algorithm,
+            resolution);
+        String splitType = dataMap.get("splitType");
+        String skyboxType = dataMap.get("skyboxType");
+        String dataDescribe = dataMap.get("dataDescribe");
+
+        log.info("用的算法是:"+algorithm);
+        log.info("用的相机是:"+ (Integer.parseInt(cameraType) < 4 ? "单球目" : "双球目(八目)"));
+
+        //4表示硬件部研发的双球目相机,其余为旧版本相机
+        if(Integer.parseInt(cameraType) < 3){
+
+            String data = FileUtils.readFile(path + File.separator + "capture" +File.separator+"data.fdage");
+            JSONObject dataJson = new JSONObject();
+            if(data!=null){
+                dataJson = JSONObject.parseObject(data);
+                if(dataJson.containsKey("imgs")){
+                    splitType = "SPLIT_V4";
+                }
+            }
+            //生成project.json和data.json供算法部使用
+            ComputerUtil.createJson(path, splitType, skyboxType, dataDescribe, num, path);
+            //计算模型并返回需要上传oss的文件集合
+            map = ComputerUtil.computer(num, path, buildType);
+
+        } else {
+
+            path = SceneUtil.getPath(path, cameraName, fileId, Integer.parseInt(cameraType), unicode);
+
+            String dataFdagePath = path + File.separator + "capture" +File.separator+"data.fdage";
+            log.info("dataFdagePath 文件路径 :{}", dataFdagePath);
+            String data = FileUtils.readFile(dataFdagePath);
+            //获取data.fdage的内容
+            JSONObject dataJson = new JSONObject();
+            if(data!=null){
+                dataJson = JSONObject.parseObject(data);
+            }
+            pushChannel = dataJson.getInteger("pushChannel");
+            pushToken = dataJson.getString("pushToken");
+
+            /*
+             1、判断是否计算过资源,若计算过删除缓存, 如果caches中存在_images文件或者目录,就删除掉,否则就删除除了images以外的所有文件和目录
+             2、删除上一次计算出来的result目录
+             */
+            this.deleteCachesAndResult(path);
+
+            //v2版本使用4k算法
+            if(dataJson.containsKey("videoVersion")
+                && StringUtils.isNotEmpty(dataJson.getString("videoVersion"))
+                && Integer.parseInt(dataJson.getString("videoVersion")) < 4){
+                skyboxType = "SKYBOX_V6";
+            }
+
+            //生成project.json和data.json并写到path目录下供算法部使用
+            log.info("path:" + path);
+            ComputerUtil.createJson(path, splitType, skyboxType, dataDescribe, num, path);
+            //计算模型并返回需要上传oss的文件集合
+            map = ComputerUtil.computer(num, path, buildType);
+            //计算完成时间
+            log.info("计算完成耗时:{}",computeTime);
+
+            if(Integer.parseInt(cameraType) == 5 || Integer.parseInt(cameraType) == 6){
+                map.put(path + File.separator + "capture/stitch_params.txt", dataPath + "/stitch_params.txt");
+            }
+            map.put(path + File.separator + "capture/Up.xml", dataPath + "/Up.xml");
+            map.put(path + File.separator + "capture/Up2.xml", dataPath + "/Up2.xml");
+
+            //转台相机
+            if(Integer.parseInt(cameraType) == 13){
+                map.put(path + File.separator + "capture/Up.txt", dataPath + "/Up.txt");
+                map.put(path + File.separator + "capture/Up2.txt", dataPath + "/Up2.txt");
+            }
+
+            //计算已使用容量
+            payStatus = 1;
+
+            log.info("八目上完oss结束修改数据:"+num);
+
+            //读取计算结果文件生成videosJson
+            String videoVersionStr = dataJson.getString("videoVersion");
+            videoVersion = StrUtil.isEmpty(videoVersionStr) ? null : Integer.parseInt(videoVersionStr);
+
+            //计算成功  激光转台相机 同步 请求
+            this.jgSynch(message);
+        }
+
+        buildSceneResult.setBuildStatus(ModelingBuildStatus.SUCCESS);
+        buildSceneResult.setFileId(fileId);
+        buildSceneResult.setPayStatus(payStatus);
+        buildSceneResult.setUploadMap(map);
+        buildSceneResult.setPath(path);
+        buildSceneResult.setPushChannel(pushChannel);
+        buildSceneResult.setPushToken(pushToken);
+        buildSceneResult.setPrefix(prefix);
+        buildSceneResult.setVideoVersion(videoVersion);
+
+        CreateObjUtil.deleteFile(path.replace(ConstantFilePath.BUILD_MODEL_PATH, "/") + "/capture");
+
+        return ModelingBuildStatus.SUCCESS;
+    }
+
+    private void afterBuild(BuildSceneCallMessage message, BuildSceneResultBean buildSceneResult, BuildLog buildLog){
+        try {
+            ModelingBuildStatus buildStatus = buildSceneResult.getBuildStatus();
+            String buildLogPath = String.format(UploadFilePath.BUILD_LOG_PATH, message.getSceneNum());
+            log.info("服务{} 计算结束:{},计算状态:{}", SysConstants.hostName, buildLog.getNum(),buildStatus.message());
+
+            if(!buildStatus.equals(ModelingBuildStatus.REPEAT)){
+                //释放锁
+                redisUtil.del(String.format(RedisKey.SCENE_BUILDING, message.getSceneNum()));
+
+                //生成status.json文件
+                this.upoadLog(message.getSceneStatus(), message.getWebSite(),
+                    message.getSceneNum(),message.getThumb(),message.getPayStatus(), message.getDataSource());
+
+                //如果是重复计算,没有走到计算逻辑,不需要上传日志文件
+                buildLog.setLogDetailPath(prefixAli+ buildLogPath + "console.log");
+            }
+            buildLog.setBuildStatus(buildStatus.code());
+            buildLog.setCameraType(Integer.parseInt(buildSceneResult.getCameraType()));
+            buildLogService.updateById(buildLog);
+
+        } catch (Exception e) {
+            log.error("计算后业务处理出错!", e);
+        }finally {
+            //发送计算结果mq
+            buildSceneResult.setDuration(buildLog.getDuration());
+            this.sendCallResult(message, buildSceneResult);
+
+            //计算完毕,将当前系统构建状态改为false
+            SysConstants.SYSTEM_BUILDING = false;
+        }
+    }
+
+    private void preBuild(BuildSceneCallMessage message, BuildLog buildLog) throws BuildException {
+
+        // 初始化日志参数
+        buildLog.setHostName(SysConstants.hostName);
+        buildLog.setQueueName(queueModelingCall);
+        buildLog.setNum(message.getSceneNum());
+        buildLog.setDataSource(message.getDataSource());
+        buildLogService.save(buildLog);
+    }
+
+    /**
+     * 发送计算结果
+     * @param message
+     * @param buildSceneResult
+     */
+    private void sendCallResult(BuildSceneCallMessage message, BuildSceneResultBean buildSceneResult){
+        ModelingBuildStatus buildStatus = buildSceneResult.getBuildStatus();
+        //重复计算不需要发送mq做后置处理
+        if(buildStatus.equals(ModelingBuildStatus.REPEAT)){
+            return;
+        }
+        boolean buildSuccess = buildStatus.equals(ModelingBuildStatus.SUCCESS) ? true : false;
+        rabbitMqProducer.sendByWorkQueue(queueModelingPost,
+            BuildSceneResultMqMessage.builder()
+                .buildSuccess(buildSuccess)
+                .cameraType(buildSceneResult.getCameraType())
+                .computeTime(buildSceneResult.getDuration())
+                .fileId(buildSceneResult.getFileId())
+                .payStatus(buildSceneResult.getPayStatus())
+                .sceneCode(message.getSceneNum())
+                .uploadFiles(buildSceneResult.getUploadMap())
+                .path(buildSceneResult.getPath())
+                .pushChannel(buildSceneResult.getPushChannel())
+                .pushToken(buildSceneResult.getPushToken())
+                .prefix(buildSceneResult.getPrefix())
+                .videoVersion(buildSceneResult.getVideoVersion())
+                .build());
+        //如果是计算失败,需要发送钉钉
+        if(!buildSuccess){
+            // TODO: 2022/1/25 测试阶段,先注释掉推送
+//            this.sendDingTalkMq(buildStatus.message(), message);
+        }
+    }
+
+    private void upoadLog(Integer sceneStatus, String webSite,
+        String num, String thumb, Integer payStatus, String dataSource){
+        try{
+            JSONObject statusJson = new JSONObject();
+            //临时将-2改成1,app还没完全更新
+            if(sceneStatus == -2){
+                sceneStatus = 1;
+            }else if(sceneStatus == -1){
+                // 失败状态不予显示到客户端
+                sceneStatus = 0;
+            }
+            statusJson.put("status", sceneStatus);
+            statusJson.put("webSite", webSite);
+            statusJson.put("sceneNum", num);
+            statusJson.put("thumb", thumb);
+            statusJson.put("payStatus", payStatus);
+            FileUtils.writeFile(ConstantFilePath.SCENE_PATH+"data/data"+num+File.separator+"status.json", statusJson.toString());
+
+            String dataPath = String.format(UploadFilePath.DATA_VIEW_PATH, num);
+            String buildLogPath = String.format(UploadFilePath.BUILD_LOG_PATH, num);
+
+            //todo 待删除old_path
+            uploadToOssUtil.upload(
+                ConstantFilePath.SCENE_PATH + "data/data" + num + File.separator + "status.json",
+                "data/data" + num + File.separator + "status.json");
+
+            uploadToOssUtil.upload(
+                ConstantFilePath.SCENE_PATH + "data/data" + num + File.separator + "status.json",
+                dataPath + "status.json");
+
+            uploadToOssUtil.upload(dataSource + File.separator + "console.log",
+                buildLogPath + "console.log");
+
+        }catch (Exception e){
+            log.error("上传日志失败!", e);
+        }
+    }
+
+    private void jgSynch(BuildSceneCallMessage message){
+        log.info("激光转台相机 同步 请求 ");
+        if(Integer.parseInt(message.getCameraType()) != 14)
+            return;
+
+        String sceneName = message.getSceneName();
+        String dataSource = message.getDataSource();
+        Long userId = message.getUserId();
+        String userName = message.getUserName();
+        String cameraName = message.getCameraName();
+        try {
+            String title = "";
+            if(StringUtils.isNotEmpty(sceneName)){
+                title = sceneName;
+            }
+//                        String dataSource = sceneProExt.getDataSource();
+            String jgPath = dataSource;
+            //创建目录
+            if(dataSource.lastIndexOf("/")!=-1){
+                jgPath = jgPath + "_laserData";
+            }else{
+                jgPath = jgPath.substring(0,jgPath.length()-1) + "_laserData";
+            }
+
+            FileUtils.createDir(jgPath+"/extras");
+
+            log.info("生成 激光相机目录 " + jgPath);
+            //生成data.json
+            JSONObject jgDataJson = new JSONObject();
+            jgDataJson.put("split_type", "SPLIT_V15");
+            jgDataJson.put("skybox_type", "SKYBOX_V5");
+            jgDataJson.put("extras", null);
+            FileUtils.writeFile(jgPath + File.separator + "data.json", jgDataJson.toString());
+
+            CreateObjUtil.cpfile(dataSource  + "/results/laserData/cover", jgPath+"/extras/");
+            CreateObjUtil.cplaserfile(dataSource  + "/results/laserData", jgPath+File.separator);
+
+            //激光相机
+            String url = laserHost+"/indoor/{sceneCode}/service/init?path="
+                + jgPath + File.separator + "laserData" + "&title="+ title
+                + "&childName=" + cameraName + "&createTime=" +  message.getSceneProCreateTime()
+                + "&snCode="+ cameraName;
+
+            if(userId!=null){
+                url = url + "&userId=" + userId;
+            }
+
+            if(StringUtils.isNotEmpty(userName)){
+                url = url + "&phone=" + userName;
+            }
+
+            url = url.replace("{sceneCode}",message.getSceneNum());
+            log.info("激光转台相机 同步 :" + url);
+            JSONObject hotListJson =
+                JSONObject.parseObject(restTemplate.getForObject(url,String.class));
+            log.info("激光转台相机 同步结束 :" + hotListJson);
+        }catch (Exception e){
+            log.error("激光转台相机同步失败", e);
+            // TODO: 2022/1/25 测试阶段,先注释掉推送
+//            this.sendDingTalkMq("激光转台相机同步失败", message);
+        }
+    }
+
+    private void deleteCachesAndResult(String path){
+        //判断是否计算过资源,若计算过删除缓存, 如果caches中存在_images文件或者目录,就删除掉,否则就删除除了images以外的所有文件和目录
+        File caches = new File(path + File.separator + "caches");
+        if(caches.exists()){
+            for(File deleteFile : caches.listFiles()){
+                if(new File(path + "_images").exists()){
+                    if(deleteFile.isDirectory()){
+                        FileUtils.delAllFile(deleteFile.getAbsolutePath());
+                    }else {
+                        FileUtils.deleteFile(deleteFile.getAbsolutePath());
+                    }
+                }else if(!deleteFile.getAbsolutePath().contains("images")){
+                    if(deleteFile.isDirectory()){
+                        FileUtils.delAllFile(deleteFile.getAbsolutePath());
+                    }else {
+                        FileUtils.deleteFile(deleteFile.getAbsolutePath());
+                    }
+                }
+            }
+        }
+        //删除上一次计算出来的result目录
+        if(new File(path + File.separator + "results").exists()){
+            FileUtils.delAllFile(path + File.separator + "results");
+        }
+    }
+
+    private void sendDingTalkMq(String reason,BuildSceneCallMessage message){
+        String serverPath = message.getPath().substring(0,message.getPath().lastIndexOf("/")+1).concat(message.getPrefix());
+        rabbitMqProducer.sendByWorkQueue(
+            queueModelingDt,
+            BuildSceneFailDTMqMessage.builder()
+            .hostName(SysConstants.hostName)
+            .reason(reason)
+            .num(message.getSceneNum())
+            .serverPath(serverPath)
+            .build());
+    }
+
+}

+ 16 - 0
src/main/java/com/fdkankan/modeling/service/IBuildLogService.java

@@ -0,0 +1,16 @@
+package com.fdkankan.modeling.service;
+
+import com.fdkankan.modeling.entity.BuildLog;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author 
+ * @since 2022-04-07
+ */
+public interface IBuildLogService extends IService<BuildLog> {
+
+}

+ 20 - 0
src/main/java/com/fdkankan/modeling/service/impl/BuildLogServiceImpl.java

@@ -0,0 +1,20 @@
+package com.fdkankan.modeling.service.impl;
+
+import com.fdkankan.modeling.entity.BuildLog;
+import com.fdkankan.modeling.mapper.IBuildLogMapper;
+import com.fdkankan.modeling.service.IBuildLogService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author 
+ * @since 2022-04-07
+ */
+@Service
+public class BuildLogServiceImpl extends ServiceImpl<IBuildLogMapper, BuildLog> implements IBuildLogService {
+
+}

+ 52 - 0
src/main/resources/bootstrap-dev.yml

@@ -0,0 +1,52 @@
+spring:
+  application:
+    name: 4dkankan-center-modeling
+  cloud:
+    nacos:
+      config:
+        server-addr: 192.168.0.47:8848
+        file-extension: yaml
+        namespace: 4dkankan-dev
+        extension-configs:
+          - data-id: 4dkankan-center-modeling.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+        shared-configs:
+          - data-id: common-db-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+          - data-id: common-redis-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+#          - data-id: common-mq-config.yaml
+#            group: DEFAULT_GROUP
+#            refresh: true
+
+          - data-id: common-rabbitmq-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+          - data-id: other-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+          - data-id: common-upload-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+      discovery:
+        server-addr: 192.168.0.47:8848
+        namespace: 4dkankan-dev
+        metadata:
+          management:
+            context-path: ${server.servlet.context-path}/actuator
+
+#management:
+#  endpoints:
+#    web:
+#      exposure:
+#        include: '*'
+#  endpoint:
+#    health:
+#      show-details: ALWAYS

+ 48 - 0
src/main/resources/bootstrap-pro.yml

@@ -0,0 +1,48 @@
+spring:
+  application:
+    name: 4dkankan-center-modeling
+  cloud:
+    nacos:
+      config:
+        server-addr: 192.168.0.47:8848
+        file-extension: yaml
+        namespace: 4dkankan-pro
+        extension-configs:
+          - data-id: 4dkankan-center-modeling.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+        shared-configs:
+          - data-id: common-redis-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+#          - data-id: common-mq-config.yaml
+#            group: DEFAULT_GROUP
+#            refresh: true
+
+          - data-id: common-rabbitmq-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+          - data-id: other-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+          - data-id: common-upload-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+      discovery:
+        server-addr: 192.168.0.47:8848
+        namespace: 4dkankan-pro
+        metadata:
+          management:
+            context-path: ${server.servlet.context-path}/actuator
+
+#management:
+#  endpoints:
+#    web:
+#      exposure:
+#        include: '*'
+#  endpoint:
+#    health:
+#      show-details: ALWAYS

+ 48 - 0
src/main/resources/bootstrap-test.yml

@@ -0,0 +1,48 @@
+spring:
+  application:
+    name: 4dkankan-center-modeling
+  cloud:
+    nacos:
+      config:
+        server-addr: 120.24.144.164:8848
+        file-extension: yaml
+        namespace: 4dkankan-test
+        extension-configs:
+          - data-id: 4dkankan-center-modeling.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+        shared-configs:
+          - data-id: common-redis-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+#          - data-id: common-mq-config.yaml
+#            group: DEFAULT_GROUP
+#            refresh: true
+
+          - data-id: common-rabbitmq-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+          - data-id: other-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+
+          - data-id: common-upload-config.yaml
+            group: DEFAULT_GROUP
+            refresh: true
+      discovery:
+        server-addr: 120.24.144.164:8848
+        namespace: 4dkankan-test
+        metadata:
+          management:
+            context-path: ${server.servlet.context-path}/actuator
+
+#management:
+#  endpoints:
+#    web:
+#      exposure:
+#        include: '*'
+#  endpoint:
+#    health:
+#      show-details: ALWAYS

+ 3 - 0
src/main/resources/bootstrap.yml

@@ -0,0 +1,3 @@
+spring:
+  profiles:
+    active: dev

+ 233 - 0
src/main/resources/logback-spring.xml

@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<configuration scan="true" scanPeriod="10 seconds">
+	<springProperty scope="context" name="LOG_PATH" source="logging.path"/>
+	<define name="hostName" class = "com.fdkankan.common.config.LogPathHostNameProperty"/>
+
+	<contextName>logback</contextName>
+	<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
+	<property name="log.path" value="${LOG_PATH}/modeling/${hostName}" />
+
+	<!-- 彩色日志 -->
+	<!-- 彩色日志依赖的渲染类 -->
+	<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
+	<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
+	<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
+	<!-- 彩色日志格式 -->
+	<property name="CONSOLE_LOG_PATTERN"
+		value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
+
+	<!--输出到控制台 -->
+	<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+		<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息 -->
+		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+			<level>info</level>
+		</filter>
+		<encoder>
+			<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+			<!-- 设置字符集 -->
+			<charset>UTF-8</charset>
+		</encoder>
+	</appender>
+	<!--输出到文件 -->
+
+	<!-- 时间滚动输出 level为 DEBUG 日志 -->
+	<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/log_debug.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<!-- 日志归档 -->
+			<fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录debug级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>debug</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+
+	<!-- 时间滚动输出 level为 INFO 日志 -->
+	<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/log_info.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset>
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<!-- 每天日志归档路径以及格式 -->
+			<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录info级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>info</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+
+	<!-- 时间滚动输出 level为 WARN 日志 -->
+	<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/log_warn.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录warn级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>warn</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+
+
+	<!-- 时间滚动输出 level为 ERROR 日志 -->
+	<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/log_error.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录ERROR级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<level>ERROR</level>
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+	
+	<appender name="PROGRAM_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/program/log_program.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/program/log-program-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录ERROR级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+	<logger name="programLog" level="INFO" additivity="true">
+        <appender-ref ref="PROGRAM_FILE"/>
+    </logger>
+
+	<appender name="VISIT_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<!-- 正在记录的日志文件的路径及文件名 -->
+		<file>${log.path}/visit/log_visit.log</file>
+		<!--日志文件输出格式 -->
+		<encoder>
+			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+			<charset>UTF-8</charset> <!-- 此处设置字符集 -->
+		</encoder>
+		<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+			<fileNamePattern>${log.path}/visit/log-visit-%d{yyyy-MM-dd}.%i.log
+			</fileNamePattern>
+
+			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+				<maxFileSize>100MB</maxFileSize>
+			</timeBasedFileNamingAndTriggeringPolicy>
+			<!--日志文件保留天数 -->
+			<maxHistory>15</maxHistory>
+		</rollingPolicy>
+		<!-- 此日志文件只记录ERROR级别的 -->
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+			<onMatch>ACCEPT</onMatch>
+			<onMismatch>DENY</onMismatch>
+		</filter>
+	</appender>
+	<logger name="visitLog" level="INFO" additivity="true">
+        <appender-ref ref="VISIT_FILE"/>
+    </logger>
+
+	<!-- <logger>用来设置某一个包或者具体的某一个类的日志打印级别、 以及指定<appender>。<logger>仅有一个name属性, 一个可选的level和一个可选的addtivity属性。 name:用来指定受此logger约束的某一个包或者具体的某一个类。 level:用来设置打印级别,大小写无关:TRACE, 
+		DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前logger将会继承上级的级别。 addtivity:是否向上级logger传递打印信息。默认是true。 -->
+	<!--<logger name="org.springframework.web" level="info"/> -->
+	<!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/> -->
+	<!-- 使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作: 第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息 第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别: -->
+	<!-- root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 不能设置为INHERITED或者同义词NULL。默认是DEBUG 可以包含零个或多个元素,标识这个appender将会添加到这个logger。 -->
+
+	<root level="info">
+		<appender-ref ref="CONSOLE" />
+		<appender-ref ref="DEBUG_FILE" />
+		<appender-ref ref="INFO_FILE" />
+		<appender-ref ref="WARN_FILE" />
+		<appender-ref ref="ERROR_FILE" />
+	</root>
+
+	<!--生产环境:输出到文件 -->
+	<!--<springProfile name="pro"> -->
+	<!--<root level="info"> -->
+	<!--<appender-ref ref="CONSOLE" /> -->
+	<!--<appender-ref ref="DEBUG_FILE" /> -->
+	<!--<appender-ref ref="INFO_FILE" /> -->
+	<!--<appender-ref ref="ERROR_FILE" /> -->
+	<!--<appender-ref ref="WARN_FILE" /> -->
+	<!--</root> -->
+	<!--</springProfile> -->
+
+</configuration>
+
+

+ 5 - 0
src/main/resources/mapper/modeling/BuildLogMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fdkankan.modeling.mapper.IBuildLogMapper">
+
+</mapper>

+ 18 - 0
src/test/java/com/fdkankan/modeling/ModelingApplicationTests.java

@@ -0,0 +1,18 @@
+package com.fdkankan.modeling;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class ModelingApplicationTests {
+
+	@Test
+	void contextLoads() {
+	}
+
+	@Test
+	public String test(){
+		return "1";
+	}
+
+}