Forráskód Böngészése

Merge branch 'hotfix-35274-20230321-dsx' into test

# Conflicts:
#	src/main/java/com/fdkankan/contro/service/impl/SceneFileBuildServiceImpl.java
dsx 2 éve
szülő
commit
ff4102326c

+ 69 - 0
src/main/java/com/fdkankan/contro/entity/SceneCopyDistinctEnv.java

@@ -0,0 +1,69 @@
+package com.fdkankan.contro.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 2023-03-21
+ */
+@Getter
+@Setter
+@TableName("t_scene_copy_distinct_env")
+public class SceneCopyDistinctEnv implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 场景码
+     */
+    @TableField("num")
+    private String num;
+
+    /**
+     * 原资源路径
+     */
+    @TableField("src_data_source")
+    private String srcDataSource;
+
+    /**
+     * 目标资源路径
+     */
+    @TableField("target_data_source")
+    private String targetDataSource;
+
+    /**
+     * 创建时间
+     */
+    @TableField("create_time")
+    private Date createTime;
+
+    /**
+     * 修改时间
+     */
+    @TableField("update_time")
+    private Date updateTime;
+
+    /**
+     * 状态(A-有效,I-无效)
+     */
+    @TableField("rec_status")
+    @TableLogic(value = "A", delval = "I")
+    private String recStatus;
+
+
+}

+ 18 - 0
src/main/java/com/fdkankan/contro/mapper/ISceneCopyDistinctEnvMapper.java

@@ -0,0 +1,18 @@
+package com.fdkankan.contro.mapper;
+
+import com.fdkankan.contro.entity.SceneCopyDistinctEnv;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author 
+ * @since 2023-03-21
+ */
+@Mapper
+public interface ISceneCopyDistinctEnvMapper extends BaseMapper<SceneCopyDistinctEnv> {
+
+}

+ 35 - 26
src/main/java/com/fdkankan/contro/mq/service/impl/BuildObjServiceImpl.java

@@ -6,10 +6,7 @@ import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.fdkankan.common.constant.CommonOperStatus;
-import com.fdkankan.common.constant.CommonStatus;
-import com.fdkankan.common.constant.ModelKind;
-import com.fdkankan.common.constant.SceneSource;
+import com.fdkankan.common.constant.*;
 import com.fdkankan.common.util.FileUtils;
 import com.fdkankan.contro.entity.ScenePlus;
 import com.fdkankan.contro.entity.ScenePlusExt;
@@ -161,28 +158,35 @@ public class BuildObjServiceImpl implements IBuildSceneService {
     @Override
     public void downLoadSource(BuildSceneCallMessage message,String path) throws Exception{
 
-        String ossResultPath = String.format(UploadFilePath.scene_result_data_path, message.getSceneNum());
-        fYunFileService.downloadFile(ossResultPath + "caches/reconstruction/final.bin", path + "/caches/reconstruction/final.bin");
-        fYunFileService.downloadFile(ossResultPath + "caches/reconstruction/chunk.json", path + "/caches/reconstruction/chunk.json");
-        fYunFileService.downloadFile(ossResultPath + "caches/floor_group_fix.json", path + "/caches/floor_group_fix.json");
-        fYunFileService.downloadFileByCommand(path + "/caches/images/", ossResultPath + "caches/images/");
-        fYunFileService.downloadFileByCommand( path + "/caches/depthmap_csc/" , ossResultPath + "caches/depthmap_csc/");
-        fYunFileService.downloadFileByCommand( path + "/caches/depthmap_vis/", ossResultPath + "caches/depthmap_vis/");
-        fYunFileService.downloadFileByCommand( path + "/caches/depthmap/", ossResultPath + "caches/depthmap/");
-        fYunFileService.downloadFile(ossResultPath + "caches/panorama.json", path + "/caches/panorama.json");
-        fYunFileService.downloadFile(ossResultPath + "results/laserData/laser.ply", path + "/results/laserData/laser.ply");
-
-//        String prevoisPath = message.getBuildContext().get("previousPath").toString();
-//        FileUtils.copyFile(prevoisPath + "/caches/reconstruction/final.bin", path + "/caches/reconstruction/final.bin", true);
-//        FileUtils.copyFile(prevoisPath + "/caches/reconstruction/chunk.json", path + "/caches/reconstruction/chunk.json", true);
-//        FileUtils.copyFile(prevoisPath + "/caches/floor_group_fix.json", path + "/caches/floor_group_fix.json", true);
-//        FileUtils.copyDirectiory(prevoisPath + "/caches/images", path + "/caches/images");
-//
-//        FileUtils.copyDirectiory(prevoisPath + "/caches/depthmap_csc", path + "/caches/depthmap_csc");
-//        FileUtils.copyDirectiory(prevoisPath + "/caches/depthmap_vis", path + "/caches/depthmap_vis");
-//        FileUtils.copyDirectiory(prevoisPath + "/caches/depthmap", path + "/caches/depthmap");
-//        FileUtils.copyFile(prevoisPath + "/caches/panorama.json", path + "/caches/panorama.json", true);
-//        FileUtils.copyFile(prevoisPath + "/results/laserData/laser.ply", path + "/results/laserData/laser.ply", true);
+        ScenePro scenePro = sceneProService.getByNum(message.getSceneNum());
+        String version = "V4";
+        if(Objects.nonNull(scenePro) && scenePro.getIsUpgrade() != CommonStatus.YES.code().intValue()){
+            version = "V3";
+        }
+
+        if(SceneVersionType.V4.code().equals(version)){
+            String ossResultPath = String.format(UploadFilePath.scene_result_data_path, message.getSceneNum());
+            fYunFileService.downloadFile(ossResultPath + "caches/reconstruction/final.bin", path + "/caches/reconstruction/final.bin");
+            fYunFileService.downloadFile(ossResultPath + "caches/reconstruction/chunk.json", path + "/caches/reconstruction/chunk.json");
+            fYunFileService.downloadFile(ossResultPath + "caches/floor_group_fix.json", path + "/caches/floor_group_fix.json");
+            fYunFileService.downloadFileByCommand(path + "/caches/images/", ossResultPath + "caches/images/");
+            fYunFileService.downloadFileByCommand( path + "/caches/depthmap_csc/" , ossResultPath + "caches/depthmap_csc/");
+            fYunFileService.downloadFileByCommand( path + "/caches/depthmap_vis/", ossResultPath + "caches/depthmap_vis/");
+            fYunFileService.downloadFileByCommand( path + "/caches/depthmap/", ossResultPath + "caches/depthmap/");
+            fYunFileService.downloadFile(ossResultPath + "caches/panorama.json", path + "/caches/panorama.json");
+            fYunFileService.downloadFile(ossResultPath + "results/laserData/laser.ply", path + "/results/laserData/laser.ply");
+        }else{
+            String prevoisPath = message.getBuildContext().get("previousPath").toString();
+            FileUtils.copyFile(prevoisPath + "/caches/reconstruction/final.bin", path + "/caches/reconstruction/final.bin", true);
+            FileUtils.copyFile(prevoisPath + "/caches/reconstruction/chunk.json", path + "/caches/reconstruction/chunk.json", true);
+            FileUtils.copyFile(prevoisPath + "/caches/floor_group_fix.json", path + "/caches/floor_group_fix.json", true);
+            FileUtils.copyDirectiory(prevoisPath + "/caches/images", path + "/caches/images");
+            FileUtils.copyDirectiory(prevoisPath + "/caches/depthmap_csc", path + "/caches/depthmap_csc");
+            FileUtils.copyDirectiory(prevoisPath + "/caches/depthmap_vis", path + "/caches/depthmap_vis");
+            FileUtils.copyDirectiory(prevoisPath + "/caches/depthmap", path + "/caches/depthmap");
+            FileUtils.copyFile(prevoisPath + "/caches/panorama.json", path + "/caches/panorama.json", true);
+            FileUtils.copyFile(prevoisPath + "/results/laserData/laser.ply", path + "/results/laserData/laser.ply", true);
+        }
     }
 
     @Override
@@ -193,6 +197,11 @@ public class BuildObjServiceImpl implements IBuildSceneService {
         String laserObjFilePath = path;
         boolean success = false;
         try {
+
+            //上传计算日志
+            String buildLogPath = String.format(UploadFilePath.BUILD_LOG_PATH, projectNum);
+            fYunFileService.uploadFile(path + File.separator + "console.log", buildLogPath + "console.log");
+
             if (!message.getBuildSuccess()) {
                 log.error("生成OBJ场景计算失败!");
                 // 发送钉钉消息,计算失败

+ 16 - 0
src/main/java/com/fdkankan/contro/service/ISceneCopyDistinctEnvService.java

@@ -0,0 +1,16 @@
+package com.fdkankan.contro.service;
+
+import com.fdkankan.contro.entity.SceneCopyDistinctEnv;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author 
+ * @since 2023-03-21
+ */
+public interface ISceneCopyDistinctEnvService extends IService<SceneCopyDistinctEnv> {
+
+}

+ 20 - 0
src/main/java/com/fdkankan/contro/service/impl/SceneCopyDistinctEnvServiceImpl.java

@@ -0,0 +1,20 @@
+package com.fdkankan.contro.service.impl;
+
+import com.fdkankan.contro.entity.SceneCopyDistinctEnv;
+import com.fdkankan.contro.mapper.ISceneCopyDistinctEnvMapper;
+import com.fdkankan.contro.service.ISceneCopyDistinctEnvService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author 
+ * @since 2023-03-21
+ */
+@Service
+public class SceneCopyDistinctEnvServiceImpl extends ServiceImpl<ISceneCopyDistinctEnvMapper, SceneCopyDistinctEnv> implements ISceneCopyDistinctEnvService {
+
+}

+ 104 - 53
src/main/java/com/fdkankan/contro/service/impl/SceneFileBuildServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.qrcode.QrCodeUtil;
 import cn.hutool.extra.qrcode.QrConfig;
@@ -32,6 +33,7 @@ import com.fdkankan.model.constants.ConstantFilePath;
 import com.fdkankan.model.constants.UploadFilePath;
 import com.fdkankan.rabbitmq.bean.BuildSceneCallMessage;
 import com.fdkankan.rabbitmq.util.RabbitMqProducer;
+import com.fdkankan.redis.constant.RedisKey;
 import com.fdkankan.redis.util.RedisLockUtil;
 import com.fdkankan.redis.util.RedisUtil;
 import com.fdkankan.web.response.Result;
@@ -55,6 +57,8 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
 
 /**
@@ -158,6 +162,9 @@ public class SceneFileBuildServiceImpl extends ServiceImpl<ISceneFileBuildMapper
 
     private RestTemplate restTemplate = new RestTemplate();
 
+    @Autowired
+    private ISceneCopyDistinctEnvService sceneCopyDistinctEnvService;
+
     @Override
     public SceneFileBuild findByFileId(String fileId) {
 
@@ -1290,16 +1297,17 @@ public class SceneFileBuildServiceImpl extends ServiceImpl<ISceneFileBuildMapper
     }
 
     @Override
-    public ResultData copyDataAndBuild(String sourceBucet,String dataSource,String sceneVer) throws Exception {
-        if(!StringUtils.equals(sceneVer,"V3") && ! StringUtils.equals(sceneVer,"V4")){
+    public ResultData copyDataAndBuild(String sourceBucet,String dataSource, String sceneVer) throws Exception {
+        if(!SceneVersionType.V3.code().equals(sceneVer)
+                && SceneVersionType.V4.code().equals(sceneVer)){
             throw new BusinessException(ErrorCode.PARAM_FORMAT_ERROR.code(),"版本有误,请填写 V3 或者 V4");
         }
 
         String fYunPath = ConstantFilePath.OSS_PREFIX + dataSource.replace(ConstantFilePath.BUILD_MODEL_PATH, "")
                 .replace(ConstantFilePath.BUILD_MODEL_LASER_PATH, "");
-        if(!ObjectUtils.isEmpty(sourceBucet)){
-            fYunFileService.copyFileBetweenBucket(sourceBucet,fYunPath,fYunFileConfig.getBucket(),fYunPath);
-        }
+//        if(!ObjectUtils.isEmpty(sourceBucet)){
+//            fYunFileService.copyFileBetweenBucket(sourceBucet,fYunPath,fYunFileConfig.getBucket(),fYunPath);
+//        }
         // 下载data.fdage
         JSONObject fdageData = JSONObject.parseObject(fYunFileService.getFileContent(fYunPath + "/data.fdage"));
         if(ObjectUtils.isEmpty(fdageData)){
@@ -1319,6 +1327,52 @@ public class SceneFileBuildServiceImpl extends ServiceImpl<ISceneFileBuildMapper
         // 拷贝资源至新的资源路径
         String[] newDataPath = dataSource.split("/");
         newDataPath[4] = newDataPath[4] + "_" + System.currentTimeMillis();
+        String time = DateExtUtil.format(Calendar.getInstance().getTime(), DateExtUtil.dateStyle11);
+        newDataPath[5] = newDataPath[5].split("_")[0] + "_" + time;
+        String newDataSource = Arrays.stream(newDataPath).collect(Collectors.joining("/"));
+
+        String newFYunPath = ConstantFilePath.OSS_PREFIX + newDataSource.replace(ConstantFilePath.BUILD_MODEL_PATH, "")
+                .replace(ConstantFilePath.BUILD_MODEL_LASER_PATH, "");
+
+        List<ScenePro> pros = sceneProService.list(new LambdaQueryWrapper<ScenePro>().eq(ScenePro::getDataSource, newDataSource));
+        if (CollUtil.isNotEmpty(pros)) {
+            return ResultData.error(ErrorCode.PARAM_ERROR.code(),newDataSource + "已存在,请勿重复添加!");
+        }
+        List<ScenePlusExt> plusExts = scenePlusExtService.list(new LambdaQueryWrapper<ScenePlusExt>().eq(ScenePlusExt::getDataSource, newDataSource));
+        if (CollUtil.isNotEmpty(plusExts)) {
+            return ResultData.error(ErrorCode.PARAM_ERROR.code(),newDataSource + "已存在,请勿重复添加!");
+        }
+        List<String> newFyunFileList = fYunFileService.listRemoteFiles(newFYunPath);
+        if(CollUtil.isNotEmpty(newFyunFileList)){
+            return ResultData.error(ErrorCode.PARAM_ERROR.code(),newFYunPath + "已存在,请勿重复添加!");
+        }
+
+        String sceneNum = scene3dNumService.generateSceneNum(detailEntity.getType());
+
+        //写入日志表
+        SceneCopyDistinctEnv sceneCopyDistinctEnv = new SceneCopyDistinctEnv();
+        sceneCopyDistinctEnv.setNum(sceneNum);
+        sceneCopyDistinctEnv.setSrcDataSource(dataSource);
+        sceneCopyDistinctEnv.setTargetDataSource(newDataSource);
+        sceneCopyDistinctEnvService.save(sceneCopyDistinctEnv);
+
+        ExecutorService executor = ThreadUtil.newSingleExecutor();
+        try {
+            CompletableFuture.runAsync(() -> {
+                try {
+                    fYunFileService.copyFileBetweenBucket(sourceBucet, fYunPath, fYunFileConfig.getBucket(), fYunPath);
+
+                    fdageData.put("uuidtime",time);
+                    fYunFileService.uploadFile(fdageData.toJSONString().getBytes(),newFYunPath+"/data.fdage");
+
+                    Long cameraType = 11L;
+                    //判断是否转台相机
+                    if (detailEntity.getType() == 9) {
+                        cameraType = 13L;
+                    }
+        // 拷贝资源至新的资源路径
+        String[] newDataPath = dataSource.split("/");
+        newDataPath[4] = newDataPath[4] + "_" + System.currentTimeMillis();
         String time = com.fdkankan.common.util.DateUtil.date2String(new Date(), com.fdkankan.common.util.DateUtil.YYYYMMDDHHMMSSSSS_DATA_FORMAT);
         newDataPath[5] = newDataPath[5].split("_")[0] + "_" + time;
         String newDataSource = Arrays.stream(newDataPath).collect(Collectors.joining("/"));
@@ -1340,57 +1394,54 @@ public class SceneFileBuildServiceImpl extends ServiceImpl<ISceneFileBuildMapper
             cameraType = 13L;
         }
 
-        if (detailEntity.getType() == 10) {
-            cameraType = 14L;
-        }
-        String sceneNum = scene3dNumService.generateSceneNum(detailEntity.getType());
-
-        String icon = null;
-        String imgViewPath = null;
-        switch (sceneVer) {
-            case "V3":
-                List<ScenePro> pros = sceneProService.list(new LambdaQueryWrapper<ScenePro>()
-                        .like(ScenePro::getDataSource, dataSource));
-                if (pros.size() > 0) {
-                    return ResultData.error(ErrorCode.PARAM_ERROR.code(),"该场景资源已存在,请勿重复添加!");
-                }
-
-                imgViewPath = String.format(ConstantFilePath.IMAGE_PATH_FORMAT, sceneNum);
-                if(fdageData.containsKey("icon") && StringUtils.isNotEmpty(fdageData.getString("icon"))){
-                    String ossPath = ConstantFilePath.OSS_PREFIX + dataSource.replace(ConstantFilePath.BUILD_MODEL_PATH, "")
-                            .replace(ConstantFilePath.BUILD_MODEL_LASER_PATH, "");
-                    fYunFileService.copyFileInBucket(ossPath + File.separator + fdageData.getString("icon"),imgViewPath + fdageData.getString("icon"));
-                    icon = fYunFileConfig.getHost() + imgViewPath + fdageData.getString("icon");
-                    log.info("上传icon成功....");
-                }
-                buildV3Scene2(dataSource,fdageData,cameraType,sceneNum,cameraEntity,detailEntity,icon);
-                break;
-            case "V4":
-                List<ScenePlusExt> plusExts = scenePlusExtService.list(new LambdaQueryWrapper<ScenePlusExt>()
-                        .like(ScenePlusExt::getDataSource, dataSource));
-                if (plusExts.size() > 0) {
-                    return ResultData.error(ErrorCode.PARAM_ERROR.code(),"该场景资源已存在,请勿重复添加!");
-                }
+                    if (detailEntity.getType() == 10) {
+                        cameraType = 14L;
+                    }
 
-                imgViewPath = String.format(UploadFilePath.IMG_VIEW_PATH, sceneNum);
-                if(fdageData.containsKey("icon") && StringUtils.isNotEmpty(fdageData.getString("icon"))){
-                    String ossPath = ConstantFilePath.OSS_PREFIX + dataSource.replace(ConstantFilePath.BUILD_MODEL_PATH, "")
-                            .replace(ConstantFilePath.BUILD_MODEL_LASER_PATH, "");
-                    fYunFileService.copyFileInBucket(ossPath + File.separator + fdageData.getString("icon"),imgViewPath + fdageData.getString("icon"));
-                    icon = fYunFileConfig.getHost() + imgViewPath + fdageData.getString("icon");
-                    log.info("上传icon成功....");
-                }
-                buildScenePost(dataSource, fdageData, "V3", cameraType, sceneNum, detailEntity, 0, icon);
-                if (cameraType == 14) {
-                    // 通知激光系统
-                    ScenePlus scenePlus = scenePlusService.getScenePlusByNum(sceneNum);
-                    String userName = null;
-                    if (!ObjectUtils.isEmpty(detailEntity.getUserId())) {
-                        userName = userService.getSSOUserByUserId(detailEntity.getUserId()).getUserName();
+                    String icon = null;
+                    String imgViewPath = null;
+                    switch (sceneVer) {
+                        case "V3":
+                            imgViewPath = String.format(ConstantFilePath.IMAGE_PATH_FORMAT, sceneNum);
+                            if(fdageData.containsKey("icon") && StringUtils.isNotEmpty(fdageData.getString("icon"))){
+                                String ossPath = ConstantFilePath.OSS_PREFIX + newDataSource.replace(ConstantFilePath.BUILD_MODEL_PATH, "")
+                                        .replace(ConstantFilePath.BUILD_MODEL_LASER_PATH, "");
+                                fYunFileService.copyFileInBucket(ossPath + File.separator + fdageData.getString("icon"),imgViewPath + fdageData.getString("icon"));
+                                icon = fYunFileConfig.getHost() + imgViewPath + fdageData.getString("icon");
+                            }
+                            buildV3Scene2(newDataSource,fdageData,cameraType,sceneNum,cameraEntity,detailEntity,icon);
+                            break;
+                        case "V4":
+                            imgViewPath = String.format(UploadFilePath.IMG_VIEW_PATH, sceneNum);
+                            if(fdageData.containsKey("icon") && StringUtils.isNotEmpty(fdageData.getString("icon"))){
+                                String ossPath = ConstantFilePath.OSS_PREFIX + newDataSource.replace(ConstantFilePath.BUILD_MODEL_PATH, "")
+                                        .replace(ConstantFilePath.BUILD_MODEL_LASER_PATH, "");
+                                fYunFileService.copyFileInBucket(ossPath + File.separator + fdageData.getString("icon"),imgViewPath + fdageData.getString("icon"));
+                                icon = fYunFileConfig.getHost() + imgViewPath + fdageData.getString("icon");
+                            }
+                            buildScenePost(newDataSource, fdageData, "V3", cameraType, sceneNum, detailEntity, 0, icon);
+                            if (cameraType == 14) {
+                                // 通知激光系统
+                                ScenePlus scenePlus = scenePlusService.getScenePlusByNum(sceneNum);
+                                String userName = null;
+                                if (!ObjectUtils.isEmpty(detailEntity.getUserId())) {
+                                    userName = userService.getSSOUserByUserId(detailEntity.getUserId()).getUserName();
+                                }
+                                fdkkLaserService.saveScene(scenePlus, fdageData.getString("pwd"), cameraEntity, userName, false);
+                            }
+                            break;
                     }
-                    fdkkLaserService.saveScene(scenePlus, fdageData.getString("pwd"), cameraEntity, userName, false);
+
+                }catch (Exception e){
+                    log.error("复制场景异常", e);
                 }
-                break;
+            }, executor).whenComplete((reslut, e) -> {
+                log.info("复制场景souceDataSource:{},newDataSource:{}结束-{}",dataSource,newDataSource, new Date());
+            });
+        }catch (Exception e){
+            log.error("线程错误:{}",e);
+        }finally {
+            executor.shutdown();
         }
         Map<String,Object> result = new HashMap<>();
         result.put("code",sceneNum);

+ 5 - 0
src/main/resources/mapper/contro/SceneCopyDistinctEnvMapper.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.contro.mapper.ISceneCopyDistinctEnvMapper">
+
+</mapper>