package com.fdkankan.scene.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fdkankan.common.constant.CommonStatus;
import com.fdkankan.model.constants.ConstantFileName;
import com.fdkankan.model.constants.ConstantFilePath;
import com.fdkankan.common.constant.ErrorCode;
import com.fdkankan.common.constant.FileBizType;
import com.fdkankan.common.constant.ServerCode;
import com.fdkankan.model.constants.UploadFilePath;
import com.fdkankan.common.exception.BusinessException;
import com.fdkankan.web.response.ResultData;
import com.fdkankan.model.utils.ComputerUtil;
import com.fdkankan.model.utils.ConvertUtils;
import com.fdkankan.model.utils.CreateObjUtil;
import com.fdkankan.common.util.FileUtils;
import com.fdkankan.common.util.OkHttpUtils;
import com.fdkankan.fyun.constant.FYunTypeEnum;
import com.fdkankan.fyun.face.FYunFileServiceInterface;
import com.fdkankan.redis.constant.RedisKey;
import com.fdkankan.redis.constant.RedisLockKey;
import com.fdkankan.redis.util.RedisLockUtil;
import com.fdkankan.redis.util.RedisUtil;
import com.fdkankan.scene.bean.IconBean;
import com.fdkankan.scene.bean.TagBean;
import com.fdkankan.scene.entity.SceneEditInfo;
import com.fdkankan.scene.entity.ScenePlus;
import com.fdkankan.scene.entity.ScenePlusExt;
import com.fdkankan.scene.entity.ScenePro;
import com.fdkankan.scene.mapper.ISceneProMapper;
import com.fdkankan.scene.service.ISceneDataDownloadService;
import com.fdkankan.scene.service.ISceneEditControlsService;
import com.fdkankan.scene.service.ISceneEditInfoService;
import com.fdkankan.scene.service.IScenePlusExtService;
import com.fdkankan.scene.service.IScenePlusService;
import com.fdkankan.scene.service.ISceneProService;
import com.fdkankan.scene.service.ISceneUploadService;
import com.fdkankan.scene.vo.BaseDataParamVO;
import com.fdkankan.scene.vo.DeleteFileParamVO;
import com.fdkankan.scene.vo.DeleteHotIconParamVO;
import com.fdkankan.scene.vo.DeleteHotParamVO;
import com.fdkankan.scene.vo.FileNameAndDataParamVO;
import com.fdkankan.scene.vo.HotParamVO;
import com.fdkankan.scene.vo.SaveTagsParamVO;
import com.fdkankan.scene.vo.SaveTagsVisibleParamVO;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.RedissonLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
/**
*
* pro场景表 服务实现类
*
*
* @author dengsixing
* @since 2021-12-23
*/
@Slf4j
@Service
public class SceneProServiceImpl extends ServiceImpl implements ISceneProService {
@Value("${fyun.host}")
private String ossUrlPrefix;
@Value("${fyun.type}")
private String fyunType;
@Value("${ecs.checkFile.maxTimes:5}")
private int maxCheckTimes;
@Value("${ecs.checkFile.waitTime:5000}")
private int waitTime;
@Autowired
private FYunFileServiceInterface fYunFileService;
@Autowired
private RedisLockUtil redisLockUtil;
@Autowired
private RedisUtil redisUtil;
@Autowired
private ISceneEditInfoService sceneEditInfoService;
@Autowired
private IScenePlusService scenePlusService;
@Autowired
private IScenePlusExtService scenePlusExtService;
@Autowired
private ISceneUploadService sceneUploadService;
@Transactional
@Override
public ResultData saveInitialPage(FileNameAndDataParamVO param) throws Exception{
ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
if(scenePlus == null){
throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
}
ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
String thumbUrl = String.format(UploadFilePath.USER_EDIT_PATH, param.getNum()) + param.getFileName();
//更新缩略图url
if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){
thumbUrl = this.ossUrlPrefix + thumbUrl;
}
scenePlusExt.setThumb(thumbUrl);
scenePlusExtService.updateById(scenePlusExt);
SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
if(sceneEditInfo == null){
sceneEditInfo = new SceneEditInfo();
sceneEditInfo.setScenePlusId(scenePlus.getId());
sceneEditInfo.setEntry(param.getData());
sceneEditInfoService.save(sceneEditInfo);
}else{
sceneEditInfoService.update(
new LambdaUpdateWrapper()
.set(SceneEditInfo::getEntry, param.getData())
.setSql("version = version + 1")
.eq(SceneEditInfo::getId, sceneEditInfo.getId()));
}
return ResultData.ok();
}
@Override
public ResultData addOrUpdateTag(SaveTagsParamVO param) throws Exception {
// ScenePro scenePro = this.findBySceneNum(param.getNum());
ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
if (scenePlus == null)
return ResultData.error(ErrorCode.FAILURE_CODE_5005);
this.addOrUpdateHotData(param.getNum(), param.getHotDataList());
this.addOrUpdateIcons(param.getNum(), param.getIcons());
//写入本地文件,作为备份
this.writeHotJson(param.getNum());
//保存数据库
SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
sceneEditInfoService.saveTagsToSceneEditInfo(param.getNum(), sceneEditInfo);
sceneEditInfoService.updateById(sceneEditInfo);
return ResultData.ok();
}
private void addOrUpdateHotData(String num, List hotDataList) throws Exception{
Map addOrUpdateMap = new HashMap<>();
int i = 0;
for (HotParamVO hotParamVO : hotDataList) {
JSONObject jsonObject = JSON.parseObject(hotParamVO.getHotData());
jsonObject.put("createTime", Calendar.getInstance().getTimeInMillis() + i++);
addOrUpdateMap.put(hotParamVO.getSid(), jsonObject.toJSONString());
}
this.syncHotFromFileToRedis(num);
//处理新增和修改数据
this.addOrUpdateHotDataHandler(num, addOrUpdateMap);
}
private void addOrUpdateIcons(String num, List icons) throws Exception{
if(CollUtil.isEmpty(icons)){
return;
}
this.syncIconsFromFileToRedis(num);
String key = String.format(RedisKey.SCENE_HOT_ICONS, num);
redisUtil.sSet(key, icons.toArray());
}
@Override
public ResultData deleteTag(DeleteHotParamVO param) throws Exception {
// ScenePro scenePro = this.findBySceneNum(param.getNum());
ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
if (scenePlus == null)
throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
List deleteSidList = param.getSidList();
//处理删除状态数据
this.deleteHotData(param.getNum(), deleteSidList, scenePlusExt.getYunFileBucket());
//写入本地文件,作为备份
this.writeHotJson(param.getNum());
//保存数据库
SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
sceneEditInfoService.saveTagsToSceneEditInfo(param.getNum(), sceneEditInfo);
sceneEditInfoService.updateById(sceneEditInfo);
return ResultData.ok();
}
@Override
public ResultData deleteIcons(DeleteHotIconParamVO param) throws Exception {
ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
if (scenePlus == null)
throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
List fileNameList = param.getFileNameList();
this.syncIconsFromFileToRedis(param.getNum());
String key = String.format(RedisKey.SCENE_HOT_ICONS, param.getNum());
redisUtil.setRemove(key, fileNameList.toArray());
//写入本地文件,作为备份
this.writeHotJson(param.getNum());
//删除oss文件
sceneUploadService.delete(
DeleteFileParamVO.builder()
.num(param.getNum())
.fileNames(fileNameList)
.bizType(FileBizType.TAG_ICON.code()).build());
return ResultData.ok();
}
@Override
public ResultData listTags(String num) throws Exception{
//保证热点数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis
this.syncHotFromFileToRedis(num);
//保证icons数据安全性,当redis宕机导致icons数据丢失时,可以从文件中读取,恢复到redis
this.syncIconsFromFileToRedis(num);
JSONObject result = new JSONObject();
//查询缓存是否包含热点数据
String key = String.format(RedisKey.SCENE_HOT_DATA, num);
Map allTagsMap = redisUtil.hmget(key);
List tags = Lists.newArrayList();
List tagBeanList = new ArrayList<>();
if(CollUtil.isNotEmpty(allTagsMap)){
allTagsMap.entrySet().stream().forEach(entry -> {
JSONObject jsonObject = JSON.parseObject(entry.getValue());
tagBeanList.add(
TagBean.builder()
.createTime(jsonObject.getLong("createTime"))
.tag(jsonObject).build());
});
//按创建时间倒叙排序
tagBeanList.sort(Comparator.comparingLong(TagBean::getCreateTime).reversed());
//移除createTime字段
tags = tagBeanList.stream().map(tagBean -> {
JSONObject tag = tagBean.getTag();
tag.remove("createTime");
return tag;
}).collect(Collectors.toList());
}
result.put("tags", tags);
//查询缓存是否包含icons
key = String.format(RedisKey.SCENE_HOT_ICONS, num);
Set icons = redisUtil.sGet(key);
if(icons == null){
icons = Sets.newHashSet();
}
List iconList = this.sortIcons(tags, icons);
result.put("icons", iconList);
return ResultData.ok(result);
}
private List sortIcons(List tags, Set icons){
//统计使用频次
List iconBeans = Lists.newArrayList();
for (String icon : icons) {
int count = 0;
for (JSONObject tag : tags) {
String sid = tag.getString("icon");
if(StrUtil.isEmpty(sid) || !icon.equals(sid)){
continue;
}
++count;
}
iconBeans.add(IconBean.builder().icon(icon).count(count).build());
}
//排序
List iconList = iconBeans.stream().sorted(Comparator.comparing(IconBean::getCount).reversed())
.map(item -> {
return item.getIcon();
}).collect(Collectors.toList());
return iconList;
}
/**
*
保证热点数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis
*
* @author dengsixing
* @date 2022/3/3
**/
private void syncHotFromFileToRedis(String num) throws Exception{
String key = String.format(RedisKey.SCENE_HOT_DATA, num);
boolean exist = redisUtil.hasKey(key);
if(exist){
return;
}
String lockKey = String.format(RedisLockKey.LOCK_HOT_DATA_SYNC, num);
String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString();
boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE);
if(!lock){
throw new BusinessException(ErrorCode.SYSTEM_BUSY);
}
try{
exist = redisUtil.hasKey(key);
if(exist){
return;
}
String tagsFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json";
String tagsData = FileUtils.readUtf8String(tagsFilePath);
if(StrUtil.isEmpty(tagsData)){
return;
}
JSONObject jsonObject = JSON.parseObject(tagsData);
JSONArray tagsArr = jsonObject.getJSONArray("tags");
if(CollUtil.isEmpty(tagsArr)){
return;
}
Map map = new HashMap<>();
for (Object o : tagsArr) {
JSONObject jo = (JSONObject)o;
map.put(jo.getString("sid"), jo.toJSONString());
}
redisUtil.hmset(key, map);
}finally {
redisLockUtil.unlockLua(lockKey, lockVal);
}
}
/**
*
保证icons数据安全性,当redis宕机导致icons数据丢失时,可以从文件中读取,恢复到redis
*
* @author dengsixing
* @date 2022/3/3
**/
private void syncIconsFromFileToRedis(String num) throws Exception{
String key = String.format(RedisKey.SCENE_HOT_ICONS, num);
boolean exist = redisUtil.hasKey(key);
if(exist){
return;
}
String lockKey = String.format(RedisLockKey.LOCK_HOT_ICONS_SYNC, num);
String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString();
boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE);
if(!lock){
throw new BusinessException(ErrorCode.SYSTEM_BUSY);
}
try{
exist = redisUtil.hasKey(key);
if(exist){
return;
}
String tagsFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json";
String tagsData = FileUtils.readUtf8String(tagsFilePath);
if(StrUtil.isEmpty(tagsData)){
return;
}
JSONObject jsonObject = JSON.parseObject(tagsData);
JSONArray iconArr = jsonObject.getJSONArray("icons");
if(CollUtil.isEmpty(iconArr)){
return;
}
redisUtil.sSet(key, iconArr.toJavaList(String.class).toArray());
}finally {
redisLockUtil.unlockLua(lockKey, lockVal);
}
}
/**
*
热点数据保存
*
* @author dengsixing
* @date 2022/3/3
**/
private void writeHotJson(String num) throws Exception{
String dataKey = String.format(RedisKey.SCENE_HOT_DATA, num);
Map tagMap = redisUtil.hmget(dataKey);
List tagList = Lists.newArrayList();
tagMap.entrySet().stream().forEach(entry->{
if(StrUtil.isNotEmpty(entry.getValue())){
tagList.add(entry.getValue());
}
});
JSONObject jsonObject = new JSONObject();
JSONArray tagJsonArr = new JSONArray();
if(CollUtil.isNotEmpty(tagList)){
tagList.stream().forEach(hot->{
tagJsonArr.add(JSONObject.parseObject(hot));
});
}
jsonObject.put("tags", tagJsonArr);
String iconsKey = String.format(RedisKey.SCENE_HOT_ICONS, num);
Set iconList = redisUtil.sGet(iconsKey);
jsonObject.put("icons", iconList);
String hotJsonPath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json";
String lockKey = String.format(RedisLockKey.LOCK_HOT_JSON, num);
String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString();
boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE);
if(!lock){
return;
}
try{
FileUtils.writeFile(hotJsonPath, jsonObject.toJSONString());
}finally {
redisLockUtil.unlockLua(lockKey, lockVal);
}
}
private void addOrUpdateHotDataHandler(String num, Map addOrUpdateMap){
if(CollUtil.isEmpty(addOrUpdateMap))
return;
//数据验证,新增、修改状态,hotdata不能为空
for (String sid : addOrUpdateMap.keySet()) {
String hotData = addOrUpdateMap.get(sid);
if(StrUtil.isEmpty(hotData)){
throw new BusinessException(ErrorCode.FAILURE_CODE_7004);
}
}
//批量写入缓存
String key = String.format(RedisKey.SCENE_HOT_DATA, num);
redisUtil.hmset(key, addOrUpdateMap);
}
private void deleteHotData(String num, List deleteSidList, String bucket) throws Exception {
this.syncHotFromFileToRedis(num);
if(CollUtil.isEmpty(deleteSidList)){
return;
}
//从redis中加载热点数据
String key = String.format(RedisKey.SCENE_HOT_DATA, num);
List deletDataList = redisUtil.hMultiGet(key, deleteSidList);
if(CollUtil.isEmpty(deletDataList))
return;
String userDataPath = String.format(UploadFilePath.USER_EDIT_PATH, num);
//删除图片音频视频等资源文件
for (String data : deletDataList) {
if(StrUtil.isBlank(data)){
continue;
}
JSONObject jsonObject = JSON.parseObject(data);
String sid = jsonObject.getString("sid");
if(jsonObject.containsKey("media")){
String fileType = jsonObject.getString("media");
if(fileType.contains("photo"))
{
fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".jpg");
}
if(fileType.contains("audio") || fileType.contains("voice"))
{
fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".mp3");
}
if(fileType.contains("video"))
{
fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".mp4");
}
}
}
//从redis中移除热点数据
redisUtil.hdel(key, deleteSidList.toArray());
}
@Override
public ResultData saveTagsVisible(SaveTagsVisibleParamVO param) throws Exception {
ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
if (scenePlus == null ) {
return ResultData.error(ErrorCode.FAILURE_CODE_5005);
}
JSONArray visiblePanos = JSONArray.parseArray(param.getData());
//如果redis找不到,就从本地文件中reload
this.syncHotFromFileToRedis(param.getNum());
//从缓存中获取热点数据,如果为空,抛出异常
String key = String.format(RedisKey.SCENE_HOT_DATA, param.getNum());
Map map = redisUtil.hmget(key);
if (CollUtil.isEmpty(map)) {
throw new BusinessException(ErrorCode.FAILURE_CODE_7005);
}
List> allTags = map.entrySet().stream().filter(item -> {
if (StrUtil.isBlank(item.getValue())) {
return false;
}
return true;
}).collect(Collectors.toList());
if (CollUtil.isEmpty(allTags)) {
throw new BusinessException(ErrorCode.FAILURE_CODE_7005);
}
allTags.stream().forEach(entry->{
JSONObject hot = JSON.parseObject(entry.getValue());
visiblePanos.stream().forEach(item->{
if (hot.getString("sid").equals(((JSONObject) item).getString("sid"))) {
hot.put("visiblePanos", ((JSONObject) item).getJSONArray("value"));
hot.put("isHidden", ((JSONObject) item).getBoolean("isHidden"));
entry.setValue(hot.toJSONString());
}
});
});
//更新版本号
SceneEditInfo editInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
sceneEditInfoService.upgradeVersionById(editInfo.getId());
//放入缓存
Map finalMap = new HashMap<>();
allTags.stream().forEach(entry->{
finalMap.put(entry.getKey(), entry.getValue());
});
redisUtil.hmset(key, finalMap);
//写入本地文件,作为备份,以防redis数据丢失
this.writeHotJson(param.getNum());
return ResultData.ok();
}
@Override
public ResultData saveRoam(BaseDataParamVO param) throws Exception {
// ScenePro scenePro = this.findBySceneNum(param.getNum());
ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
if (scenePlus == null ) {
return ResultData.error(ErrorCode.FAILURE_CODE_5005);
}
ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
String bucket = scenePlusExt.getYunFileBucket();
JSONArray inputData = JSONObject.parseArray(param.getData());
String localDataPath = String.format(ConstantFilePath.SCENE_DATA_PATH_V4, param.getNum());
File directory = new File(localDataPath);
if (!directory.exists()) {
directory.mkdirs();
}
String viewImagesPath = String.format(UploadFilePath.IMG_VIEW_PATH, param.getNum());
//如果是云存储,将vision.modeldata下载到本地,如果是本地存储,场景计算完就已经将这个文件拷贝到编辑目录了存在这个文件了,不需要再下载
fYunFileService.downloadFile(bucket, viewImagesPath + "vision.modeldata", localDataPath + "vision.modeldata");
//检查vision.modeldata本地是否存在,不存在抛出异常
File file = new File(localDataPath + "vision.modeldata");
if(!file.exists()) {
return ResultData.error(ErrorCode.FAILURE_CODE_5012);
}
//将vision.modeldata解压缩至vision.json
ConvertUtils.convertVisionModelDataToTxt(localDataPath + "vision.modeldata", localDataPath + "vision.json");
String str = FileUtils.readFile(localDataPath + "vision.json");
JSONObject json = JSONObject.parseObject(str);
JSONArray panos = json.getJSONArray("sweepLocations");
for (int i = 0; i < panos.size(); ++i) {
JSONObject pano = panos.getJSONObject(i);
for (int j = 0; j < inputData.size(); ++j) {
JSONObject jo = inputData.getJSONObject(j);
String currentPanoId = jo.getString("panoID");
JSONArray visibles = jo.getJSONArray("visibles");
JSONArray visibles3 = jo.getJSONArray("visibles3");
if (pano.getString("uuid").equals(currentPanoId)) {
pano.put("visibles", visibles);
pano.put("visibles3", visibles3);
}
}
}
FileUtils.deleteFile(localDataPath + "vision.json");
FileUtils.deleteFile(localDataPath + "vision.modeldata");
FileUtils.writeFile(localDataPath + "vision.json", json.toString());
ConvertUtils.convertTxtToVisionModelData(localDataPath + "vision.json", localDataPath + "vision.modeldata");
fYunFileService.uploadFile(bucket, localDataPath + "vision.modeldata", viewImagesPath + "vision.modeldata");
//更新版本号
SceneEditInfo editInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
if(Objects.isNull(editInfo)){
editInfo = new SceneEditInfo();
editInfo.setScenePlusId(scenePlus.getId());
sceneEditInfoService.save(editInfo);
}else{
sceneEditInfoService.upgradeVersionById(editInfo.getId());
//更新scenejson缓存和oss文件版本号
sceneEditInfoService.upgradeSceneJsonVersion(param.getNum(), editInfo.getVersion() + 1, null, bucket);
}
//更新scene.json版本号
// sceneEditInfoService.upgradeVersionToSceneJson(param.getNum());
return ResultData.ok();
}
@Override
public void updateUserIdByCameraId(Long userId, Long cameraId) {
this.update(new LambdaUpdateWrapper()
.eq(ScenePro::getCameraId, cameraId)
.set(ScenePro::getUserId, userId));
}
@Override
public ResultData uploadObjAndImg(String num, MultipartFile file) throws Exception{
if(StrUtil.isEmpty(num)){
throw new BusinessException(ServerCode.PARAM_REQUIRED, "num");
}
if(!file.getOriginalFilename().endsWith(".zip")){
throw new BusinessException(ErrorCode.FAILURE_CODE_7015);
}
ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num);
if(scenePlus == null){
throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
}
ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
String bucket = scenePlusExt.getYunFileBucket();
//文件上传的位置可以自定义
String path = scenePlusExt.getDataSource() + "_obj2txt";
String zipPath = path + "/zip/";
String filePath = path + "/extras/";
String resultPath = path + "/results/";
//压缩文件处理:解压缩,解压缩后复制等操作
this.objAndImgFileHandler(resultPath, filePath, zipPath, file);
//创建data.json
this.writeDataJson(path);
//调用算法,不同的类型调用不同的算法
if("V2".equals(scenePlusExt.getBuildType())){
CreateObjUtil.objToTxt(path , "1");
}
if("V3".equals(scenePlusExt.getBuildType())){
CreateObjUtil.build3dModel(path , "1");
}
//算法计算完后,生成压缩文件,上传到oss
this.uploadFileofterRebuildPanoram(path, num, bucket);
//更新版本信息
SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
if(Objects.isNull(sceneEditInfo)){
sceneEditInfo = new SceneEditInfo();
sceneEditInfo.setScenePlusId(scenePlus.getId());
sceneEditInfo.setFloorPublishVer(1);
sceneEditInfo.setFloorEditVer(1);
sceneEditInfo.setIsUploadObj(CommonStatus.YES.code());
sceneEditInfoService.save(sceneEditInfo);
}else{
sceneEditInfoService.update(
new LambdaUpdateWrapper()
.setSql("version = version + 1")
.setSql("floor_edit_ver = floor_edit_ver + 1")
.setSql("floor_publish_ver = floor_publish_ver + 1")
.setSql("img_version = img_version + 1")
.set(SceneEditInfo::getIsUploadObj, CommonStatus.YES.code())
.eq(SceneEditInfo::getId, sceneEditInfo.getId()));
sceneEditInfoService.upgradeSceneJsonVersion(num, sceneEditInfo.getVersion() + 1, sceneEditInfo.getImgVersion() + 1, bucket); }
return ResultData.ok();
}
private void uploadFileofterRebuildPanoram(String path, String sceneNum, String bucket) throws Exception {
//因为共享目录有延迟,这里循环检测算法是否计算完毕3次,每次隔五秒
String uploadJsonPath = path + File.separator + "results" +File.separator+"upload.json";
boolean exist = ComputerUtil.checkComputeCompleted(uploadJsonPath, maxCheckTimes, waitTime);
if(!exist){
throw new BusinessException(ErrorCode.FAILURE_CODE_7013);
}
String uploadData = FileUtils.readFile(uploadJsonPath);
JSONObject uploadJson = null;
JSONArray array = null;
if(uploadData!=null) {
uploadJson = JSONObject.parseObject(uploadData);
array = uploadJson.getJSONArray("upload");
}
Map map = new HashMap();
JSONObject fileJson = null;
String fileName = "";
String imgViewPath = String.format(UploadFilePath.IMG_VIEW_PATH, sceneNum);
for(int i = 0, len = array.size(); i < len; i++) {
fileJson = array.getJSONObject(i);
fileName = fileJson.getString("file");
//文件不存在抛出异常
if (!new File(path + File.separator + "results" + File.separator + fileName).exists()) {
throw new Exception(path + File.separator + "results" + File.separator + fileName + "文件不存在");
}
//tex文件夹
if (fileJson.getIntValue("clazz") == 15) {
map.put(path + File.separator + "results" + File.separator + fileName,
imgViewPath + ConstantFileName.modelUUID + "_50k_texture_jpg_high1/" + fileName.replace("tex/", ""));
continue;
}
}
CreateObjUtil.convertTxtToDam( path + File.separator + "results" +File.separator+"modeldata.txt", path + File.separator + "results" +File.separator+ ConstantFileName.modelUUID+"_50k.dam");
CreateObjUtil.convertDamToLzma(path + File.separator + "results");
CreateObjUtil.convertTxtToDam( path + File.separator + "results" +File.separator+"modeldata.txt", path + File.separator + "results" + File.separator+ConstantFileName.modelUUID+"_50k.dam");
map.put(path + File.separator + "results" +File.separator+ConstantFileName.modelUUID+"_50k.dam.lzma", imgViewPath +ConstantFileName.modelUUID+"_50k.dam.lzma");
map.put(path + File.separator + "results" +File.separator+ConstantFileName.modelUUID+"_50k.dam", imgViewPath+ConstantFileName.modelUUID+"_50k.dam");
fYunFileService.uploadMulFiles(bucket, map);
}
private void writeDataJson(String path) throws IOException {
JSONObject dataJson = new JSONObject();
dataJson.put("obj2txt", true);
dataJson.put("split_type", "SPLIT_V6");
dataJson.put("data_describe", "double spherical");
dataJson.put("skybox_type", "SKYBOX_V5");
FileUtils.writeFile(path + "/data.json", dataJson.toString());
}
private void objAndImgFileHandler(String resultPath, String filePath, String zipPath, MultipartFile file)
throws Exception {
FileUtils.delAllFile(resultPath);
File targetFile = new File(filePath);
if (!targetFile.exists()) {
targetFile.mkdirs();
}else {
FileUtils.delAllFile(filePath);
}
targetFile = new File(zipPath);
if (!targetFile.exists()) {
targetFile.mkdirs();
}else {
FileUtils.delAllFile(zipPath);
}
targetFile = new File(zipPath + file.getOriginalFilename());
if(!targetFile.getParentFile().exists()){
targetFile.getParentFile().mkdirs();
}
// 保存压缩包到本地
if(targetFile.exists())
{
FileUtils.deleteFile(zipPath + file.getOriginalFilename());
}
file.transferTo(targetFile);
ZipUtil.unzip(zipPath + file.getOriginalFilename(), zipPath + "data/");
//源文件数据,判断是否有多个文件夹,有多个就提示错误,有一个就将文件夹里数据迁移到extras目录,无直接迁移到extras目录
boolean flag = false;
//目录名称,如果不为空,则压缩文件第一层是目录
String targetName = "";
File dataFile = new File(zipPath + "data/");
for(File data : dataFile.listFiles()){
if(data.isDirectory() && flag){
throw new BusinessException(ErrorCode.FAILURE_CODE_5018);
}
if(data.isDirectory() && !flag){
flag = true;
targetName = data.getName();
}
}
//是否包含obj文件
boolean objFlag = false;
//是否包含mtl文件
boolean mtlFlag = false;
File[] files = null;
String dataPath = null;
if(StrUtil.isEmpty(targetName)){
files = dataFile.listFiles();
dataPath = zipPath + "data/";
}else{
files = new File(zipPath + "data/" + targetName).listFiles();
dataPath = zipPath + "data/" + targetName + File.separator;
}
for(File data : files){
if(data.isDirectory()){
throw new BusinessException(ErrorCode.FAILURE_CODE_5018);
}
if(data.getName().endsWith(".jpg") || data.getName().endsWith(".png")){
if(!FileUtils.checkFileSizeIsLimit(data.length(), 1.5, "M")){
throw new BusinessException(ErrorCode.FAILURE_CODE_5020);
}
}
if(data.getName().endsWith(".obj")){
if(objFlag){
throw new BusinessException(ErrorCode.FAILURE_CODE_5019);
}
if(!data.getName().equals("mesh.obj")){
throw new BusinessException(ErrorCode.FAILURE_CODE_5060);
}
if(!FileUtils.checkFileSizeIsLimit(data.length(), 20, "M")){
throw new BusinessException(ErrorCode.FAILURE_CODE_5020);
}
objFlag = true;
FileUtils.copyFile(dataPath + data.getName(), filePath + "mesh.obj", true);
continue;
}
if(data.getName().endsWith(".mtl")){
mtlFlag = true;
}
FileUtils.copyFile(dataPath + data.getName(), filePath + data.getName(), true);
}
//压缩文件中必须有且仅有一个obj和mtl文件,否则抛出异常
if(!mtlFlag || !objFlag){
throw new BusinessException(ErrorCode.FAILURE_CODE_5059);
}
}
public ResultData downloadTexData(String num) throws Exception {
if(StrUtil.isEmpty(num)){
throw new BusinessException(ErrorCode.PARAM_REQUIRED);
}
ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num);
if(scenePlus == null){
throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
}
ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
String bucket = scenePlusExt.getYunFileBucket();
SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
String localImagePath = String.format(ConstantFilePath.IMAGESBUFFER_FORMAT, num);
if(!new File(localImagePath).exists()){
new File(localImagePath).mkdirs();
}
String newData = scenePlusExt.getDataSource() + "_obj2txt/extras";
String newResultData = scenePlusExt.getDataSource() + "_obj2txt/results/upload.json";
String zipName = num + "_extras.zip";
String zipPath = localImagePath + zipName;
//如果用户上传过模型,就打包上传到oss,直接返回
if(CommonStatus.YES.code().equals(sceneEditInfo.getIsUploadObj())
&& new File(newData).exists()
&& new File(newResultData).exists()){
//打包
ZipUtil.zip(newData, zipPath);
//上传压缩包
fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName);
String url = "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis();
if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){
url = ossUrlPrefix + url;
}
return ResultData.ok(url);
}
String buildType = scenePlusExt.getBuildType();
if("V3".equals(buildType)){
String dataViewPath = String.format(UploadFilePath.DATA_VIEW_PATH, num);
//V3版本去oss下载2048模型
String meshPath = String.format(ConstantFilePath.DATABUFFER_FORMAT, num) + "mesh";
FileUtils.deleteDirectory(meshPath);
fYunFileService.downloadFileByCommand(bucket, meshPath, dataViewPath + "mesh");
log.info("meshPath="+meshPath);
if(!new File(meshPath).exists()){
throw new BusinessException(ErrorCode.FAILURE_CODE_7006);
}
log.info(new File(meshPath).listFiles().toString());
if(new File(meshPath).listFiles().length > 0){
for(File file : new File(meshPath).listFiles()){
if(file.isDirectory()){
for (File item : file.listFiles()) {
if(item.getName().endsWith(".obj") && !"output.house.obj".equals(item.getName()) &&
!"mesh.obj".equals(item.getName())){
item.delete();
}
if(item.getName().endsWith(".mtl") && !"output.house.mtl".equals(item.getName()) &&
!"mesh.mtl".equals(item.getName())){
item.delete();
}
}
continue;
}
if(file.getName().endsWith(".obj") && !"output.house.obj".equals(file.getName()) &&
!"mesh.obj".equals(file.getName())){
file.delete();
}
if(file.getName().endsWith(".mtl") && !"output.house.mtl".equals(file.getName()) &&
!"mesh.mtl".equals(file.getName())){
file.delete();
}
}
//打包
ZipUtil.zip(meshPath, zipPath);
//上传压缩包
fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName);
String url = "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis();
if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){
url = ossUrlPrefix + url;
}
// FileUtil.del(zipPath);
return ResultData.ok(url);
}
}
//V2版本在本地获取模型资源
//修改过的资源
String editData = scenePlusExt.getDataSource() + "_edit/caches/tex";
String results = scenePlusExt.getDataSource() + "_edit/results";
if (new File(editData).exists() && new File(results).exists()){
for(File file : new File(editData).listFiles()){
if(file.getName().endsWith(".obj") && !"output.house.obj".equals(file.getName()) &&
!"mesh.obj".equals(file.getName())){
file.delete();
}
if(file.getName().endsWith(".mtl") && !"output.house.mtl".equals(file.getName()) &&
!"mesh.mtl".equals(file.getName())){
file.delete();
}
}
ZipUtil.zip(editData, zipPath);
//上传压缩包
fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName);
String url = "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis();
if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){
url = ossUrlPrefix + url;
}
return ResultData.ok(url);
}
//没上传过返回源资源
String dataPath = scenePlusExt.getDataSource() + "/caches/tex";
File dataFile = new File(dataPath);
if(!dataFile.exists()){
throw new BusinessException(ErrorCode.FAILURE_CODE_3018);
}
for(File file : dataFile.listFiles()){
if(file.getName().endsWith(".obj") && !"output.house.obj".equals(file.getName()) &&
!"mesh.obj".equals(file.getName())){
file.delete();
}
if(file.getName().endsWith(".mtl") && !"output.house.mtl".equals(file.getName()) &&
!"mesh.mtl".equals(file.getName())){
file.delete();
}
}
ZipUtil.zip(dataPath, zipPath);
//上传压缩包
fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName);
String url = "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis();
if(!FYunTypeEnum.LOCAL.code().equals(fyunType)){
url = ossUrlPrefix + url;
}
return ResultData.ok(url);
}
@Override
public ScenePro getByNum(String num) {
return this.getOne(new LambdaQueryWrapper().eq(ScenePro::getNum, num));
}
}