SceneProServiceImpl.java 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. package com.fdkankan.scene.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.date.DateField;
  4. import cn.hutool.core.date.DateUtil;
  5. import cn.hutool.core.io.FileUtil;
  6. import cn.hutool.core.util.RuntimeUtil;
  7. import cn.hutool.core.util.StrUtil;
  8. import cn.hutool.core.util.ZipUtil;
  9. import com.alibaba.fastjson.JSON;
  10. import com.alibaba.fastjson.JSONArray;
  11. import com.alibaba.fastjson.JSONObject;
  12. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  13. import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  14. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  15. import com.fdkankan.common.constant.*;
  16. import com.fdkankan.model.constants.ConstantFileName;
  17. import com.fdkankan.model.constants.ConstantFilePath;
  18. import com.fdkankan.model.constants.UploadFilePath;
  19. import com.fdkankan.common.exception.BusinessException;
  20. import com.fdkankan.scene.bean.SceneBean;
  21. import com.fdkankan.web.response.ResultData;
  22. import com.fdkankan.model.utils.ComputerUtil;
  23. import com.fdkankan.model.utils.ConvertUtils;
  24. import com.fdkankan.model.utils.CreateObjUtil;
  25. import com.fdkankan.common.util.FileUtils;
  26. import com.fdkankan.common.util.OkHttpUtils;
  27. import com.fdkankan.fyun.constant.FYunTypeEnum;
  28. import com.fdkankan.fyun.face.FYunFileServiceInterface;
  29. import com.fdkankan.redis.constant.RedisKey;
  30. import com.fdkankan.redis.constant.RedisLockKey;
  31. import com.fdkankan.redis.util.RedisLockUtil;
  32. import com.fdkankan.redis.util.RedisUtil;
  33. import com.fdkankan.scene.bean.IconBean;
  34. import com.fdkankan.scene.bean.TagBean;
  35. import com.fdkankan.scene.entity.SceneEditInfo;
  36. import com.fdkankan.scene.entity.ScenePlus;
  37. import com.fdkankan.scene.entity.ScenePlusExt;
  38. import com.fdkankan.scene.entity.ScenePro;
  39. import com.fdkankan.scene.mapper.ISceneProMapper;
  40. import com.fdkankan.scene.service.ISceneDataDownloadService;
  41. import com.fdkankan.scene.service.ISceneEditControlsService;
  42. import com.fdkankan.scene.service.ISceneEditInfoService;
  43. import com.fdkankan.scene.service.IScenePlusExtService;
  44. import com.fdkankan.scene.service.IScenePlusService;
  45. import com.fdkankan.scene.service.ISceneProService;
  46. import com.fdkankan.scene.service.ISceneUploadService;
  47. import com.fdkankan.scene.vo.BaseDataParamVO;
  48. import com.fdkankan.scene.vo.DeleteFileParamVO;
  49. import com.fdkankan.scene.vo.DeleteHotIconParamVO;
  50. import com.fdkankan.scene.vo.DeleteHotParamVO;
  51. import com.fdkankan.scene.vo.FileNameAndDataParamVO;
  52. import com.fdkankan.scene.vo.HotParamVO;
  53. import com.fdkankan.scene.vo.SaveTagsParamVO;
  54. import com.fdkankan.scene.vo.SaveTagsVisibleParamVO;
  55. import com.google.common.collect.Lists;
  56. import com.google.common.collect.Sets;
  57. import java.io.File;
  58. import java.io.IOException;
  59. import java.nio.charset.StandardCharsets;
  60. import java.util.*;
  61. import java.util.Map.Entry;
  62. import java.util.stream.Collectors;
  63. import lombok.extern.slf4j.Slf4j;
  64. import org.redisson.Redisson;
  65. import org.redisson.RedissonLock;
  66. import org.springframework.beans.factory.annotation.Autowired;
  67. import org.springframework.beans.factory.annotation.Qualifier;
  68. import org.springframework.beans.factory.annotation.Value;
  69. import org.springframework.stereotype.Service;
  70. import org.springframework.transaction.annotation.Transactional;
  71. import org.springframework.web.multipart.MultipartFile;
  72. /**
  73. * <p>
  74. * pro场景表 服务实现类
  75. * </p>
  76. *
  77. * @author dengsixing
  78. * @since 2021-12-23
  79. */
  80. @Slf4j
  81. @Service
  82. public class SceneProServiceImpl extends ServiceImpl<ISceneProMapper, ScenePro> implements ISceneProService {
  83. @Value("${fyun.host}")
  84. private String ossUrlPrefix;
  85. @Value("${fyun.type}")
  86. private String fyunType;
  87. @Value("${main.url}")
  88. private String mainUrl;
  89. @Value("${scene.url}")
  90. private String sceneUrl;
  91. @Value("${scene.pro.url}")
  92. private String sceneProUrl;
  93. @Value("${scene.pro.new.url}")
  94. private String sceneProNewUrl;
  95. @Value("${ecs.checkFile.maxTimes:5}")
  96. private int maxCheckTimes;
  97. @Value("${ecs.checkFile.waitTime:5000}")
  98. private int waitTime;
  99. @Autowired
  100. private FYunFileServiceInterface fYunFileService;
  101. @Autowired
  102. private RedisLockUtil redisLockUtil;
  103. @Autowired
  104. private RedisUtil redisUtil;
  105. @Autowired
  106. private ISceneDataDownloadService sceneDataDownloadService;
  107. @Autowired
  108. private ISceneProService sceneProService;
  109. @Autowired
  110. private ISceneEditInfoService sceneEditInfoService;
  111. @Autowired
  112. private ISceneEditControlsService sceneEditControlsService;
  113. @Autowired
  114. private IScenePlusService scenePlusService;
  115. @Autowired
  116. private IScenePlusExtService scenePlusExtService;
  117. @Autowired
  118. private ISceneUploadService sceneUploadService;
  119. @Transactional
  120. @Override
  121. public ResultData saveInitialPage(FileNameAndDataParamVO param) throws Exception{
  122. ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
  123. if(scenePlus == null){
  124. throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
  125. }
  126. ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
  127. //更新缩略图url
  128. String thumbUrl = this.ossUrlPrefix + String.format(UploadFilePath.USER_EDIT_PATH, param.getNum()) + param.getFileName();
  129. scenePlusExt.setThumb(thumbUrl);
  130. scenePlusExtService.updateById(scenePlusExt);
  131. SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
  132. if(sceneEditInfo == null){
  133. sceneEditInfo = new SceneEditInfo();
  134. sceneEditInfo.setScenePlusId(scenePlus.getId());
  135. sceneEditInfo.setEntry(param.getData());
  136. sceneEditInfoService.save(sceneEditInfo);
  137. }else{
  138. sceneEditInfoService.update(
  139. new LambdaUpdateWrapper<SceneEditInfo>()
  140. .set(SceneEditInfo::getEntry, param.getData())
  141. .setSql("version = version + 1")
  142. .eq(SceneEditInfo::getId, sceneEditInfo.getId()));
  143. }
  144. return ResultData.ok();
  145. }
  146. @Override
  147. public ResultData addOrUpdateTag(SaveTagsParamVO param) throws Exception {
  148. // ScenePro scenePro = this.findBySceneNum(param.getNum());
  149. ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
  150. if (scenePlus == null)
  151. return ResultData.error(ErrorCode.FAILURE_CODE_5005);
  152. this.addOrUpdateHotData(param.getNum(), param.getHotDataList());
  153. this.addOrUpdateIcons(param.getNum(), param.getIcons());
  154. //写入本地文件,作为备份
  155. this.writeHotJson(param.getNum());
  156. //保存数据库
  157. SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
  158. sceneEditInfoService.saveTagsToSceneEditInfo(param.getNum(), sceneEditInfo);
  159. sceneEditInfoService.updateById(sceneEditInfo);
  160. return ResultData.ok();
  161. }
  162. private void addOrUpdateHotData(String num, List<HotParamVO> hotDataList) throws Exception{
  163. Map<String, String> addOrUpdateMap = new HashMap<>();
  164. int i = 0;
  165. for (HotParamVO hotParamVO : hotDataList) {
  166. JSONObject jsonObject = JSON.parseObject(hotParamVO.getHotData());
  167. jsonObject.put("createTime", Calendar.getInstance().getTimeInMillis() + i++);
  168. addOrUpdateMap.put(hotParamVO.getSid(), jsonObject.toJSONString());
  169. }
  170. this.syncHotFromFileToRedis(num);
  171. //处理新增和修改数据
  172. this.addOrUpdateHotDataHandler(num, addOrUpdateMap);
  173. }
  174. private void addOrUpdateIcons(String num, List<String> icons) throws Exception{
  175. if(CollUtil.isEmpty(icons)){
  176. return;
  177. }
  178. this.syncIconsFromFileToRedis(num);
  179. String key = String.format(RedisKey.SCENE_HOT_ICONS, num);
  180. redisUtil.sSet(key, icons.toArray());
  181. }
  182. @Override
  183. public ResultData deleteTag(DeleteHotParamVO param) throws Exception {
  184. // ScenePro scenePro = this.findBySceneNum(param.getNum());
  185. ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
  186. if (scenePlus == null)
  187. throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
  188. ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
  189. String bucket = scenePlusExt.getYunFileBucket();
  190. List<String> deleteSidList = param.getSidList();
  191. //处理删除状态数据
  192. this.deleteHotData(param.getNum(), deleteSidList, bucket);
  193. //删除导览中的热点数据
  194. this.deleteHotDataFromTourJson(param.getNum(), param.getSidList(), bucket);
  195. //写入本地文件,作为备份
  196. this.writeHotJson(param.getNum());
  197. //保存数据库
  198. SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
  199. sceneEditInfoService.saveTagsToSceneEditInfo(param.getNum(), sceneEditInfo);
  200. sceneEditInfoService.updateById(sceneEditInfo);
  201. return ResultData.ok();
  202. }
  203. private void deleteHotDataFromTourJson(String num, List<String> sidList, String bucket){
  204. String key = String.format(UploadFilePath.USER_EDIT_PATH, num) + "tour.json";
  205. String tourJson = fYunFileService.getFileContent(bucket, key);
  206. if(StrUtil.isEmpty(tourJson)){
  207. return;
  208. }
  209. JSONArray jsonArray = JSON.parseArray(tourJson);
  210. if(CollUtil.isEmpty(jsonArray)){
  211. return;
  212. }
  213. jsonArray.stream().forEach(tour->{
  214. JSONObject obj = (JSONObject) tour;
  215. JSONArray itemArra = obj.getJSONArray("list");
  216. itemArra.stream().forEach(item->{
  217. JSONObject itemObj = (JSONObject) item;
  218. String tagId = itemObj.getString("tagId");
  219. if(tagId != null && sidList.contains(tagId)){
  220. itemObj.remove("tagId");
  221. }
  222. });
  223. });
  224. fYunFileService.uploadFile(bucket, jsonArray.toJSONString().getBytes(StandardCharsets.UTF_8), key);
  225. }
  226. @Override
  227. public ResultData deleteIcons(DeleteHotIconParamVO param) throws Exception {
  228. ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
  229. if (scenePlus == null)
  230. throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
  231. List<String> fileNameList = param.getFileNameList();
  232. this.syncIconsFromFileToRedis(param.getNum());
  233. String key = String.format(RedisKey.SCENE_HOT_ICONS, param.getNum());
  234. redisUtil.setRemove(key, fileNameList.toArray());
  235. //写入本地文件,作为备份
  236. this.writeHotJson(param.getNum());
  237. //删除oss文件
  238. sceneUploadService.delete(
  239. DeleteFileParamVO.builder()
  240. .num(param.getNum())
  241. .fileNames(fileNameList)
  242. .bizType(FileBizType.TAG_ICON.code()).build());
  243. return ResultData.ok();
  244. }
  245. @Override
  246. public ResultData listTags(String num) throws Exception{
  247. //保证热点数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis
  248. this.syncHotFromFileToRedis(num);
  249. //保证icons数据安全性,当redis宕机导致icons数据丢失时,可以从文件中读取,恢复到redis
  250. this.syncIconsFromFileToRedis(num);
  251. JSONObject result = new JSONObject();
  252. //查询缓存是否包含热点数据
  253. String key = String.format(RedisKey.SCENE_HOT_DATA, num);
  254. Map<String, String> allTagsMap = redisUtil.hmget(key);
  255. List<JSONObject> tags = Lists.newArrayList();
  256. List<TagBean> tagBeanList = new ArrayList<>();
  257. if(CollUtil.isNotEmpty(allTagsMap)){
  258. allTagsMap.entrySet().stream().forEach(entry -> {
  259. JSONObject jsonObject = JSON.parseObject(entry.getValue());
  260. tagBeanList.add(
  261. TagBean.builder()
  262. .createTime(jsonObject.getLong("createTime"))
  263. .tag(jsonObject).build());
  264. });
  265. //按创建时间倒叙排序
  266. tagBeanList.sort(Comparator.comparingLong(TagBean::getCreateTime).reversed());
  267. //移除createTime字段
  268. tags = tagBeanList.stream().map(tagBean -> {
  269. JSONObject tag = tagBean.getTag();
  270. tag.remove("createTime");
  271. return tag;
  272. }).collect(Collectors.toList());
  273. }
  274. result.put("tags", tags);
  275. //查询缓存是否包含icons
  276. key = String.format(RedisKey.SCENE_HOT_ICONS, num);
  277. Set<String> icons = redisUtil.sGet(key);
  278. if(icons == null){
  279. icons = Sets.newHashSet();
  280. }
  281. List<String> iconList = this.sortIcons(tags, icons);
  282. result.put("icons", iconList);
  283. return ResultData.ok(result);
  284. }
  285. private List<String> sortIcons(List<JSONObject> tags, Set<String> icons){
  286. //统计使用频次
  287. List<IconBean> iconBeans = Lists.newArrayList();
  288. for (String icon : icons) {
  289. int count = 0;
  290. for (JSONObject tag : tags) {
  291. String sid = tag.getString("icon");
  292. if(StrUtil.isEmpty(sid) || !icon.equals(sid)){
  293. continue;
  294. }
  295. ++count;
  296. }
  297. iconBeans.add(IconBean.builder().icon(icon).count(count).build());
  298. }
  299. //排序
  300. List<String> iconList = iconBeans.stream().sorted(Comparator.comparing(IconBean::getCount).reversed())
  301. .map(item -> {
  302. return item.getIcon();
  303. }).collect(Collectors.toList());
  304. return iconList;
  305. }
  306. /**
  307. * <p>
  308. 保证热点数据安全性,当redis宕机导致热点数据丢失时,可以从文件中读取,恢复到redis
  309. * </p>
  310. * @author dengsixing
  311. * @date 2022/3/3
  312. **/
  313. private void syncHotFromFileToRedis(String num) throws Exception{
  314. String key = String.format(RedisKey.SCENE_HOT_DATA, num);
  315. boolean exist = redisUtil.hasKey(key);
  316. if(exist){
  317. return;
  318. }
  319. String lockKey = String.format(RedisLockKey.LOCK_HOT_DATA_SYNC, num);
  320. String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString();
  321. boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE);
  322. if(!lock){
  323. throw new BusinessException(ErrorCode.SYSTEM_BUSY);
  324. }
  325. try{
  326. exist = redisUtil.hasKey(key);
  327. if(exist){
  328. return;
  329. }
  330. String tagsFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json";
  331. String tagsData = FileUtils.readUtf8String(tagsFilePath);
  332. if(StrUtil.isEmpty(tagsData)){
  333. return;
  334. }
  335. JSONObject jsonObject = JSON.parseObject(tagsData);
  336. JSONArray tagsArr = jsonObject.getJSONArray("tags");
  337. if(CollUtil.isEmpty(tagsArr)){
  338. return;
  339. }
  340. Map<String, String> map = new HashMap<>();
  341. for (Object o : tagsArr) {
  342. JSONObject jo = (JSONObject)o;
  343. map.put(jo.getString("sid"), jo.toJSONString());
  344. }
  345. redisUtil.hmset(key, map);
  346. }finally {
  347. redisLockUtil.unlockLua(lockKey, lockVal);
  348. }
  349. }
  350. /**
  351. * <p>
  352. 保证icons数据安全性,当redis宕机导致icons数据丢失时,可以从文件中读取,恢复到redis
  353. * </p>
  354. * @author dengsixing
  355. * @date 2022/3/3
  356. **/
  357. private void syncIconsFromFileToRedis(String num) throws Exception{
  358. String key = String.format(RedisKey.SCENE_HOT_ICONS, num);
  359. boolean exist = redisUtil.hasKey(key);
  360. if(exist){
  361. return;
  362. }
  363. String lockKey = String.format(RedisLockKey.LOCK_HOT_ICONS_SYNC, num);
  364. String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString();
  365. boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE);
  366. if(!lock){
  367. throw new BusinessException(ErrorCode.SYSTEM_BUSY);
  368. }
  369. try{
  370. exist = redisUtil.hasKey(key);
  371. if(exist){
  372. return;
  373. }
  374. String tagsFilePath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json";
  375. String tagsData = FileUtils.readUtf8String(tagsFilePath);
  376. if(StrUtil.isEmpty(tagsData)){
  377. return;
  378. }
  379. JSONObject jsonObject = JSON.parseObject(tagsData);
  380. JSONArray iconArr = jsonObject.getJSONArray("icons");
  381. if(CollUtil.isEmpty(iconArr)){
  382. return;
  383. }
  384. redisUtil.sSet(key, iconArr.toJavaList(String.class).toArray());
  385. }finally {
  386. redisLockUtil.unlockLua(lockKey, lockVal);
  387. }
  388. }
  389. /**
  390. * <p>
  391. 热点数据保存
  392. * </p>
  393. * @author dengsixing
  394. * @date 2022/3/3
  395. **/
  396. private void writeHotJson(String num) throws Exception{
  397. String dataKey = String.format(RedisKey.SCENE_HOT_DATA, num);
  398. Map<String, String> tagMap = redisUtil.hmget(dataKey);
  399. List<String> tagList = Lists.newArrayList();
  400. tagMap.entrySet().stream().forEach(entry->{
  401. if(StrUtil.isNotEmpty(entry.getValue())){
  402. tagList.add(entry.getValue());
  403. }
  404. });
  405. JSONObject jsonObject = new JSONObject();
  406. JSONArray tagJsonArr = new JSONArray();
  407. if(CollUtil.isNotEmpty(tagList)){
  408. tagList.stream().forEach(hot->{
  409. tagJsonArr.add(JSONObject.parseObject(hot));
  410. });
  411. }
  412. jsonObject.put("tags", tagJsonArr);
  413. String iconsKey = String.format(RedisKey.SCENE_HOT_ICONS, num);
  414. Set<String> iconList = redisUtil.sGet(iconsKey);
  415. jsonObject.put("icons", iconList);
  416. String hotJsonPath = String.format(ConstantFilePath.SCENE_USER_PATH_V4, num) + "hot.json";
  417. String lockKey = String.format(RedisLockKey.LOCK_HOT_JSON, num);
  418. String lockVal = cn.hutool.core.lang.UUID.randomUUID().toString();
  419. boolean lock = redisLockUtil.lock(lockKey, lockVal, RedisKey.EXPIRE_TIME_1_MINUTE);
  420. if(!lock){
  421. return;
  422. }
  423. try{
  424. FileUtils.writeFile(hotJsonPath, jsonObject.toJSONString());
  425. }finally {
  426. redisLockUtil.unlockLua(lockKey, lockVal);
  427. }
  428. }
  429. private void addOrUpdateHotDataHandler(String num, Map<String, String> addOrUpdateMap){
  430. if(CollUtil.isEmpty(addOrUpdateMap))
  431. return;
  432. //数据验证,新增、修改状态,hotdata不能为空
  433. for (String sid : addOrUpdateMap.keySet()) {
  434. String hotData = addOrUpdateMap.get(sid);
  435. if(StrUtil.isEmpty(hotData)){
  436. throw new BusinessException(ErrorCode.FAILURE_CODE_7004);
  437. }
  438. }
  439. //批量写入缓存
  440. String key = String.format(RedisKey.SCENE_HOT_DATA, num);
  441. redisUtil.hmset(key, addOrUpdateMap);
  442. }
  443. private void deleteHotData(String num, List<String> deleteSidList, String bucket) throws Exception {
  444. this.syncHotFromFileToRedis(num);
  445. if(CollUtil.isEmpty(deleteSidList)){
  446. return;
  447. }
  448. //从redis中加载热点数据
  449. String key = String.format(RedisKey.SCENE_HOT_DATA, num);
  450. List<String> deletDataList = redisUtil.hMultiGet(key, deleteSidList);
  451. if(CollUtil.isEmpty(deletDataList))
  452. return;
  453. String userDataPath = String.format(UploadFilePath.USER_EDIT_PATH, num);
  454. //删除图片音频视频等资源文件
  455. for (String data : deletDataList) {
  456. if(StrUtil.isBlank(data)){
  457. continue;
  458. }
  459. JSONObject jsonObject = JSON.parseObject(data);
  460. String sid = jsonObject.getString("sid");
  461. if(jsonObject.containsKey("media")){
  462. String fileType = jsonObject.getString("media");
  463. if(fileType.contains("photo"))
  464. {
  465. fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".jpg");
  466. }
  467. if(fileType.contains("audio") || fileType.contains("voice"))
  468. {
  469. fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".mp3");
  470. }
  471. if(fileType.contains("video"))
  472. {
  473. fYunFileService.deleteFile(bucket,userDataPath + "hot"+sid+".mp4");
  474. }
  475. }
  476. }
  477. //从redis中移除热点数据
  478. redisUtil.hdel(key, deleteSidList.toArray());
  479. }
  480. @Override
  481. public ResultData saveTagsVisible(SaveTagsVisibleParamVO param) throws Exception {
  482. ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
  483. if (scenePlus == null ) {
  484. return ResultData.error(ErrorCode.FAILURE_CODE_5005);
  485. }
  486. JSONArray visiblePanos = JSONArray.parseArray(param.getData());
  487. //如果redis找不到,就从本地文件中reload
  488. this.syncHotFromFileToRedis(param.getNum());
  489. //从缓存中获取热点数据,如果为空,抛出异常
  490. String key = String.format(RedisKey.SCENE_HOT_DATA, param.getNum());
  491. Map<String, String> map = redisUtil.hmget(key);
  492. if (CollUtil.isEmpty(map)) {
  493. throw new BusinessException(ErrorCode.FAILURE_CODE_7005);
  494. }
  495. List<Entry<String, String>> allTags = map.entrySet().stream().filter(item -> {
  496. if (StrUtil.isBlank(item.getValue())) {
  497. return false;
  498. }
  499. return true;
  500. }).collect(Collectors.toList());
  501. if (CollUtil.isEmpty(allTags)) {
  502. throw new BusinessException(ErrorCode.FAILURE_CODE_7005);
  503. }
  504. allTags.stream().forEach(entry->{
  505. JSONObject hot = JSON.parseObject(entry.getValue());
  506. visiblePanos.stream().forEach(item->{
  507. if (hot.getString("sid").equals(((JSONObject) item).getString("sid"))) {
  508. hot.put("visiblePanos", ((JSONObject) item).getJSONArray("value"));
  509. hot.put("isHidden", ((JSONObject) item).getBoolean("isHidden"));
  510. entry.setValue(hot.toJSONString());
  511. }
  512. });
  513. });
  514. //更新版本号
  515. SceneEditInfo editInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
  516. sceneEditInfoService.upgradeVersionById(editInfo.getId());
  517. //放入缓存
  518. Map<String, String> finalMap = new HashMap<>();
  519. allTags.stream().forEach(entry->{
  520. finalMap.put(entry.getKey(), entry.getValue());
  521. });
  522. redisUtil.hmset(key, finalMap);
  523. //写入本地文件,作为备份,以防redis数据丢失
  524. this.writeHotJson(param.getNum());
  525. return ResultData.ok();
  526. }
  527. @Override
  528. public ResultData saveRoam(BaseDataParamVO param) throws Exception {
  529. // ScenePro scenePro = this.findBySceneNum(param.getNum());
  530. ScenePlus scenePlus = scenePlusService.getScenePlusByNum(param.getNum());
  531. if (scenePlus == null ) {
  532. return ResultData.error(ErrorCode.FAILURE_CODE_5005);
  533. }
  534. ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
  535. String bucket = scenePlusExt.getYunFileBucket();
  536. JSONArray inputData = JSONObject.parseArray(param.getData());
  537. String localDataPath = String.format(ConstantFilePath.SCENE_DATA_PATH_V4, param.getNum());
  538. File directory = new File(localDataPath);
  539. if (!directory.exists()) {
  540. directory.mkdirs();
  541. }
  542. String viewImagesPath = String.format(UploadFilePath.IMG_VIEW_PATH, param.getNum());
  543. String modeldataUrl = ossUrlPrefix + viewImagesPath + "vision.modeldata?t=" + System.currentTimeMillis();
  544. //如果是云存储,将vision.modeldata下载到本地,如果是本地存储,场景计算完就已经将这个文件拷贝到编辑目录了存在这个文件了,不需要再下载
  545. fYunFileService.downloadFile(bucket, viewImagesPath + "vision.modeldata", localDataPath + "vision.modeldata");
  546. //检查vision.modeldata本地是否存在,不存在抛出异常
  547. File file = new File(localDataPath + "vision.modeldata");
  548. if(!file.exists()) {
  549. return ResultData.error(ErrorCode.FAILURE_CODE_5012);
  550. }
  551. //将vision.modeldata解压缩至vision.json
  552. ConvertUtils.convertVisionModelDataToTxt(localDataPath + "vision.modeldata", localDataPath + "vision.json");
  553. String str = FileUtils.readFile(localDataPath + "vision.json");
  554. JSONObject json = JSONObject.parseObject(str);
  555. JSONArray panos = json.getJSONArray("sweepLocations");
  556. for (int i = 0; i < panos.size(); ++i) {
  557. JSONObject pano = panos.getJSONObject(i);
  558. for (int j = 0; j < inputData.size(); ++j) {
  559. JSONObject jo = inputData.getJSONObject(j);
  560. String currentPanoId = jo.getString("panoID");
  561. JSONArray visibles = jo.getJSONArray("visibles");
  562. JSONArray visibles3 = jo.getJSONArray("visibles3");
  563. if (pano.getString("uuid").replaceAll("-", "").equals(currentPanoId)) {
  564. pano.put("visibles", visibles);
  565. pano.put("visibles3", visibles3);
  566. }
  567. }
  568. }
  569. FileUtils.deleteFile(localDataPath + "vision.json");
  570. FileUtils.deleteFile(localDataPath + "vision.modeldata");
  571. FileUtils.writeFile(localDataPath + "vision.json", json.toString());
  572. ConvertUtils.convertTxtToVisionModelData(localDataPath + "vision.json", localDataPath + "vision.modeldata");
  573. fYunFileService.uploadFile(bucket, localDataPath + "vision.modeldata", viewImagesPath + "vision.modeldata");
  574. //更新版本号
  575. SceneEditInfo editInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
  576. if(Objects.isNull(editInfo)){
  577. editInfo = new SceneEditInfo();
  578. editInfo.setScenePlusId(scenePlus.getId());
  579. sceneEditInfoService.save(editInfo);
  580. }else{
  581. sceneEditInfoService.upgradeVersionAndImgVersionById(editInfo.getId());
  582. //更新scenejson缓存和oss文件版本号
  583. sceneEditInfoService.upgradeSceneJsonVersion(param.getNum(), editInfo.getVersion() + 1, editInfo.getImgVersion() + 1, bucket);
  584. }
  585. return ResultData.ok();
  586. }
  587. @Override
  588. public void updateUserIdByCameraId(Long userId, Long cameraId) {
  589. this.update(new LambdaUpdateWrapper<ScenePro>()
  590. .eq(ScenePro::getCameraId, cameraId)
  591. .set(ScenePro::getUserId, userId));
  592. }
  593. @Override
  594. public ResultData uploadObjAndImg(String num, MultipartFile file) throws Exception{
  595. if(StrUtil.isEmpty(num)){
  596. throw new BusinessException(ServerCode.PARAM_REQUIRED, "num");
  597. }
  598. if(!file.getOriginalFilename().endsWith(".zip")){
  599. throw new BusinessException(ErrorCode.FAILURE_CODE_7015);
  600. }
  601. ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num);
  602. if(scenePlus == null){
  603. throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
  604. }
  605. ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
  606. String bucket = scenePlusExt.getYunFileBucket();
  607. if(ModelKind.THREE_D_TILE.code().equals(scenePlusExt.getModelKind())){
  608. this.buildModel43dtiles(num, bucket, scenePlusExt.getDataSource(), file);
  609. }else{
  610. this.buildModel4Dam(num, bucket, scenePlusExt.getDataSource(), scenePlusExt.getBuildType(), file);
  611. }
  612. //更新版本信息
  613. SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
  614. if(Objects.isNull(sceneEditInfo)){
  615. sceneEditInfo = new SceneEditInfo();
  616. sceneEditInfo.setScenePlusId(scenePlus.getId());
  617. sceneEditInfo.setFloorPublishVer(1);
  618. sceneEditInfo.setFloorEditVer(1);
  619. sceneEditInfo.setIsUploadObj(CommonStatus.YES.code());
  620. sceneEditInfoService.save(sceneEditInfo);
  621. }else{
  622. sceneEditInfoService.update(
  623. new LambdaUpdateWrapper<SceneEditInfo>()
  624. .setSql("version = version + 1")
  625. .setSql("floor_edit_ver = floor_edit_ver + 1")
  626. .setSql("floor_publish_ver = floor_publish_ver + 1")
  627. .setSql("img_version = img_version + 1")
  628. .set(SceneEditInfo::getIsUploadObj, CommonStatus.YES.code())
  629. .eq(SceneEditInfo::getId, sceneEditInfo.getId()));
  630. sceneEditInfoService.upgradeSceneJsonVersion(num, sceneEditInfo.getVersion() + 1, sceneEditInfo.getImgVersion() + 1, bucket);
  631. }
  632. return ResultData.ok();
  633. }
  634. /**
  635. * 老算法(dam)上传模型逻辑
  636. * @param num
  637. * @param bucket
  638. * @param dataSource
  639. * @param buildType
  640. * @throws Exception
  641. */
  642. private void buildModel4Dam(String num, String bucket, String dataSource, String buildType, MultipartFile file) throws Exception {
  643. //文件上传的位置可以自定义
  644. String path = dataSource + "_obj2txt";
  645. String zipPath = path + "/zip/";
  646. String filePath = path + "/extras/";
  647. String resultPath = path + "/results/";
  648. //压缩文件处理:解压缩,解压缩后复制等操作
  649. this.objAndImgFileHandler(resultPath, filePath, zipPath, file);
  650. //创建data.json
  651. this.writeDataJson(path);
  652. //调用算法,不同的类型调用不同的算法
  653. if("V2".equals(buildType)){
  654. CreateObjUtil.objToTxt(path , "1");
  655. }
  656. if("V3".equals(buildType)){
  657. CreateObjUtil.build3dModel(path , "1");
  658. }
  659. //算法计算完后,生成压缩文件,上传到oss
  660. this.uploadFileofterRebuildPanoram(path, filePath, num, bucket);
  661. }
  662. /**
  663. * 新算法(3dtiles)上传模型逻辑
  664. * @param num
  665. * @param bucket
  666. * @param dataSource
  667. * @throws Exception
  668. */
  669. private void buildModel43dtiles(String num, String bucket, String dataSource, MultipartFile file) throws Exception {
  670. //文件上传的位置可以自定义
  671. String path = dataSource + "_obj2Tiles" + File.separator;
  672. String meshPath = path + "mesh";
  673. String zipPath = path + "zip" + File.separator;
  674. String zipFilePath = zipPath + file.getOriginalFilename();
  675. //压缩文件处理:解压缩,解压缩后复制等操作
  676. FileUtil.del(path);
  677. FileUtil.mkdir(zipPath);
  678. File zipFile = new File(zipFilePath);
  679. file.transferTo(zipFile);
  680. ZipUtil.unzip(zipFilePath, meshPath);
  681. //检测文件
  682. String floorsJsonPath = meshPath + File.separator + "floors.json";
  683. if(!FileUtil.exist(floorsJsonPath)){
  684. throw new BusinessException(ErrorCode.FAILURE_CODE_5068);
  685. }
  686. String floorsJsonStr = FileUtil.readUtf8String(floorsJsonPath);
  687. JSONObject floorsJsonObj = JSON.parseObject(floorsJsonStr);
  688. JSONArray floorArr = floorsJsonObj.getJSONArray("floors");
  689. if(CollUtil.isEmpty(floorArr)){
  690. throw new BusinessException(ErrorCode.FAILURE_CODE_5069);
  691. }
  692. Set<String> floorNameSet = new HashSet<>();
  693. floorArr.stream().forEach(item->{
  694. JSONObject itemObj = (JSONObject) item;
  695. //楼层目录是否存在
  696. String name = itemObj.getString("name");
  697. if(StrUtil.isEmpty(name) || !FileUtil.exist(meshPath + File.separator + name)){
  698. throw new BusinessException(ErrorCode.FAILURE_CODE_5070);
  699. }
  700. //检测obj文件是否存在
  701. String objPath = itemObj.getString("objPath");
  702. if(StrUtil.isEmpty(objPath) || !FileUtil.exist(path + objPath)){
  703. throw new BusinessException(ErrorCode.FAILURE_CODE_5070);
  704. }
  705. if(floorNameSet.contains(name)){
  706. throw new BusinessException(ErrorCode.FAILURE_CODE_5069);
  707. }
  708. floorNameSet.add(name);
  709. });
  710. //读取oss上的floors.jsoon用于校验用户上传的模型楼层数是否一一对应
  711. String ossFloorsJson = fYunFileService.getFileContent(bucket, String.format(UploadFilePath.DATA_VIEW_PATH, num) + "mesh/floors.json");
  712. JSONObject orginFloorsJsonObj = JSON.parseObject(ossFloorsJson);
  713. JSONArray orginFloorArr = orginFloorsJsonObj.getJSONArray("floors");
  714. Set<String> orginFloorNameSet = orginFloorArr.stream().map(item -> {
  715. JSONObject itemObj = (JSONObject) item;
  716. return itemObj.getString("name");
  717. }).collect(Collectors.toSet());
  718. if(floorNameSet.size() != orginFloorNameSet.size()){
  719. throw new BusinessException(ErrorCode.FAILURE_CODE_5070);
  720. }
  721. orginFloorNameSet.stream().forEach(orginName->{
  722. if(!floorNameSet.contains(orginName)){
  723. throw new BusinessException(ErrorCode.FAILURE_CODE_5070);
  724. }
  725. });
  726. //调用算法
  727. String command = "bash /home/ubuntu/bin/Obj2Tiles.sh " + path;
  728. log.info("上传3dtiles模型开始, num:{}, targetPath:{}", num, path);
  729. // RuntimeUtil.exec(command);
  730. CreateObjUtil.callshell(command);
  731. log.info("上传3dtiles模型结束, num:{}, targetPath:{}", num, path);
  732. //检测计算结果
  733. String tilesPath = path + "3dtiles";
  734. String tilesetJsonPath = tilesPath + File.separator + "tileset.json";
  735. boolean success = ComputerUtil.checkComputeCompleted(tilesetJsonPath, maxCheckTimes, waitTime);
  736. if(!success){
  737. throw new BusinessException(ErrorCode.FAILURE_CODE_7013);
  738. }
  739. //删除logs
  740. FileUtil.del(tilesPath + File.separator + "logs");
  741. //算法计算完后,生成压缩文件,上传到oss
  742. //上传3dtiles
  743. fYunFileService.deleteFolder(bucket, String.format(UploadFilePath.IMG_VIEW_PATH, num) + "3dtiles");
  744. fYunFileService.uploadFileByCommand(bucket, tilesPath, String.format(UploadFilePath.IMG_VIEW_PATH, num) + "3dtiles");
  745. //上传mesh
  746. fYunFileService.deleteFolder(bucket, String.format(UploadFilePath.DATA_VIEW_PATH, num) + "mesh");
  747. fYunFileService.uploadFileByCommand(bucket, meshPath, String.format(UploadFilePath.DATA_VIEW_PATH, num) + "mesh");
  748. }
  749. private void uploadFileofterRebuildPanoram(String path, String filePath, String sceneNum, String bucket) throws Exception {
  750. //因为共享目录有延迟,这里循环检测算法是否计算完毕3次,每次隔五秒
  751. String uploadJsonPath = path + File.separator + "results" +File.separator+"upload.json";
  752. boolean exist = ComputerUtil.checkComputeCompleted(uploadJsonPath, maxCheckTimes, waitTime);
  753. if(!exist){
  754. throw new BusinessException(ErrorCode.FAILURE_CODE_7013);
  755. }
  756. String uploadData = FileUtils.readFile(uploadJsonPath);
  757. JSONObject uploadJson = null;
  758. JSONArray array = null;
  759. if(uploadData!=null) {
  760. uploadJson = JSONObject.parseObject(uploadData);
  761. array = uploadJson.getJSONArray("upload");
  762. }
  763. Map<String,String> map = new HashMap<String,String>();
  764. JSONObject fileJson = null;
  765. String fileName = "";
  766. String imgViewPath = String.format(UploadFilePath.IMG_VIEW_PATH, sceneNum);
  767. for(int i = 0, len = array.size(); i < len; i++) {
  768. fileJson = array.getJSONObject(i);
  769. fileName = fileJson.getString("file");
  770. //文件不存在抛出异常
  771. if (!new File(path + File.separator + "results" + File.separator + fileName).exists()) {
  772. throw new Exception(path + File.separator + "results" + File.separator + fileName + "文件不存在");
  773. }
  774. //tex文件夹
  775. if (fileJson.getIntValue("clazz") == 15) {
  776. map.put(path + File.separator + "results" + File.separator + fileName,
  777. imgViewPath + ConstantFileName.modelUUID + "_50k_texture_jpg_high1/" + fileName.replace("tex/", ""));
  778. continue;
  779. }
  780. }
  781. String damPath = path + File.separator + "results" +File.separator+ ConstantFileName.modelUUID+"_50k.dam";
  782. CreateObjUtil.convertTxtToDam( path + File.separator + "results" +File.separator+"modeldata.txt", damPath);
  783. boolean existDam = ComputerUtil.checkComputeCompleted(damPath, 5, 2);
  784. if(!existDam){
  785. throw new BusinessException(ErrorCode.FAILURE_CODE_7013);
  786. }
  787. // CreateObjUtil.convertDamToLzma(path + File.separator + "results");
  788. // CreateObjUtil.convertTxtToDam( path + File.separator + "results" +File.separator+"modeldata.txt", path + File.separator + "results" + File.separator+ConstantFileName.modelUUID+"_50k.dam");
  789. // map.put(path + File.separator + "results" +File.separator+ConstantFileName.modelUUID+"_50k.dam.lzma", imgViewPath +ConstantFileName.modelUUID+"_50k.dam.lzma");
  790. map.put(path + File.separator + "results" +File.separator+ConstantFileName.modelUUID+"_50k.dam", imgViewPath+ConstantFileName.modelUUID+"_50k.dam");
  791. String ossMeshPath = String.format(UploadFilePath.DATA_VIEW_PATH, sceneNum) + "mesh";
  792. //删除oss中的mesh
  793. fYunFileService.deleteFolder(bucket, ossMeshPath);
  794. //上传obj相关文件
  795. List<String> fileNames = FileUtil.listFileNames(filePath);
  796. fileNames.stream().forEach(name->map.put(filePath + name, ossMeshPath + File.separator + name));
  797. fYunFileService.uploadMulFiles(bucket, map);
  798. }
  799. private void writeDataJson(String path) throws IOException {
  800. JSONObject dataJson = new JSONObject();
  801. dataJson.put("obj2txt", true);
  802. dataJson.put("split_type", "SPLIT_V6");
  803. dataJson.put("data_describe", "double spherical");
  804. dataJson.put("skybox_type", "SKYBOX_V5");
  805. FileUtils.writeFile(path + "/data.json", dataJson.toString());
  806. }
  807. private void objAndImgFileHandler(String resultPath, String filePath, String zipPath, MultipartFile file)
  808. throws Exception {
  809. FileUtils.delAllFile(resultPath);
  810. File targetFile = new File(filePath);
  811. if (!targetFile.exists()) {
  812. targetFile.mkdirs();
  813. }else {
  814. FileUtils.delAllFile(filePath);
  815. }
  816. targetFile = new File(zipPath);
  817. if (!targetFile.exists()) {
  818. targetFile.mkdirs();
  819. }else {
  820. FileUtils.delAllFile(zipPath);
  821. }
  822. targetFile = new File(zipPath + file.getOriginalFilename());
  823. if(!targetFile.getParentFile().exists()){
  824. targetFile.getParentFile().mkdirs();
  825. }
  826. // 保存压缩包到本地
  827. if(targetFile.exists())
  828. {
  829. FileUtils.deleteFile(zipPath + file.getOriginalFilename());
  830. }
  831. file.transferTo(targetFile);
  832. ZipUtil.unzip(zipPath + file.getOriginalFilename(), zipPath + "data/");
  833. //源文件数据,判断是否有多个文件夹,有多个就提示错误,有一个就将文件夹里数据迁移到extras目录,无直接迁移到extras目录
  834. boolean flag = false;
  835. //目录名称,如果不为空,则压缩文件第一层是目录
  836. String targetName = "";
  837. File dataFile = new File(zipPath + "data/");
  838. for(File data : dataFile.listFiles()){
  839. if(data.isDirectory() && flag){
  840. throw new BusinessException(ErrorCode.FAILURE_CODE_5018);
  841. }
  842. if(data.isDirectory() && !flag){
  843. flag = true;
  844. targetName = data.getName();
  845. }
  846. }
  847. //是否包含obj文件
  848. boolean objFlag = false;
  849. //是否包含mtl文件
  850. boolean mtlFlag = false;
  851. File[] files = null;
  852. String dataPath = null;
  853. if(StrUtil.isEmpty(targetName)){
  854. files = dataFile.listFiles();
  855. dataPath = zipPath + "data/";
  856. }else{
  857. files = new File(zipPath + "data/" + targetName).listFiles();
  858. dataPath = zipPath + "data/" + targetName + File.separator;
  859. }
  860. for(File data : files){
  861. if(data.isDirectory()){
  862. throw new BusinessException(ErrorCode.FAILURE_CODE_5018);
  863. }
  864. if(data.getName().endsWith(".jpg") || data.getName().endsWith(".png")){
  865. if(!FileUtils.checkFileSizeIsLimit(data.length(), 1.5, "M")){
  866. throw new BusinessException(ErrorCode.FAILURE_CODE_5020);
  867. }
  868. }
  869. if(data.getName().endsWith(".obj")){
  870. if(objFlag){
  871. throw new BusinessException(ErrorCode.FAILURE_CODE_5019);
  872. }
  873. if(!data.getName().equals("mesh.obj")){
  874. throw new BusinessException(ErrorCode.FAILURE_CODE_5060);
  875. }
  876. if(!FileUtils.checkFileSizeIsLimit(data.length(), 20, "M")){
  877. throw new BusinessException(ErrorCode.FAILURE_CODE_5020);
  878. }
  879. objFlag = true;
  880. FileUtils.copyFile(dataPath + data.getName(), filePath + "mesh.obj", true);
  881. continue;
  882. }
  883. if(data.getName().endsWith(".mtl")){
  884. mtlFlag = true;
  885. }
  886. FileUtils.copyFile(dataPath + data.getName(), filePath + data.getName(), true);
  887. }
  888. //压缩文件中必须有且仅有一个obj和mtl文件,否则抛出异常
  889. if(!mtlFlag || !objFlag){
  890. throw new BusinessException(ErrorCode.FAILURE_CODE_5059);
  891. }
  892. }
  893. public ResultData downloadTexData(String num) throws Exception {
  894. if(StrUtil.isEmpty(num)){
  895. throw new BusinessException(ErrorCode.PARAM_REQUIRED);
  896. }
  897. ScenePlus scenePlus = scenePlusService.getScenePlusByNum(num);
  898. if(scenePlus == null){
  899. throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
  900. }
  901. ScenePlusExt scenePlusExt = scenePlusExtService.getScenePlusExtByPlusId(scenePlus.getId());
  902. String bucket = scenePlusExt.getYunFileBucket();
  903. SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
  904. if(ModelKind.THREE_D_TILE.code().equals(scenePlusExt.getModelKind())){
  905. return this.downloadModel43dtiles(num, bucket, scenePlusExt, sceneEditInfo);
  906. }
  907. return this.downloadModel4Dam(num, bucket);
  908. }
  909. @Override
  910. public ScenePro getByNum(String num) {
  911. return this.getOne(new LambdaQueryWrapper<ScenePro>().eq(ScenePro::getNum, num));
  912. }
  913. private ResultData downloadModel43dtiles(String num, String bucket, ScenePlusExt scenePlusExt, SceneEditInfo sceneEditInfo){
  914. //下载mesh到本地
  915. String meshOssPath = String.format(UploadFilePath.DATA_VIEW_PATH, num) + "mesh/";
  916. String meshLocalPath = String.format(ConstantFilePath.SCENE_DATA_PATH_V4, num) + "mesh";
  917. String zipName = num + "_mesh.zip";
  918. String zipFilePath = String.format(ConstantFilePath.SCENE_DATA_PATH_V4, num) + zipName;
  919. //下载
  920. fYunFileService.downloadFileByCommand(bucket, meshLocalPath, meshOssPath);
  921. //打包
  922. ZipUtil.zip(meshLocalPath,zipFilePath);
  923. //上传压缩包
  924. fYunFileService.uploadFile(bucket, zipFilePath, "downloads/extras/" + zipName);
  925. //删除本地文件
  926. FileUtil.del(meshLocalPath);
  927. FileUtil.del(zipFilePath);
  928. String url = ossUrlPrefix + "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis();
  929. return ResultData.ok(url);
  930. }
  931. public static void main(String[] args) throws Exception {
  932. ConvertUtils.convertVisionModelDataToTxt( "D:\\test\\vision.modeldata", "D:\\test\\vision.json");
  933. }
  934. private ResultData downloadModel4Dam(String num, String bucket){
  935. String localImagePath = String.format(ConstantFilePath.IMAGESBUFFER_FORMAT, num);
  936. if(!new File(localImagePath).exists()){
  937. new File(localImagePath).mkdirs();
  938. }
  939. String zipName = num + "_extras.zip";
  940. String zipPath = localImagePath + zipName;
  941. String dataViewPath = String.format(UploadFilePath.DATA_VIEW_PATH, num);
  942. //V3版本去oss下载2048模型
  943. String meshPath = String.format(ConstantFilePath.DATABUFFER_FORMAT, num) + "mesh";
  944. FileUtils.deleteDirectory(meshPath);
  945. fYunFileService.downloadFileByCommand(bucket, meshPath, dataViewPath + "mesh");
  946. log.info("meshPath="+meshPath);
  947. if(!new File(meshPath).exists() || new File(meshPath).listFiles().length < 1){
  948. throw new BusinessException(ErrorCode.FAILURE_CODE_7006);
  949. }
  950. for(File file : new File(meshPath).listFiles()){
  951. if(file.isDirectory()){
  952. for (File item : file.listFiles()) {
  953. if(item.getName().endsWith(".obj") && !"output.house.obj".equals(item.getName()) &&
  954. !"mesh.obj".equals(item.getName())){
  955. item.delete();
  956. }
  957. if(item.getName().endsWith(".mtl") && !"output.house.mtl".equals(item.getName()) &&
  958. !"mesh.mtl".equals(item.getName())){
  959. item.delete();
  960. }
  961. }
  962. continue;
  963. }
  964. if(file.getName().endsWith(".obj") && !"output.house.obj".equals(file.getName()) &&
  965. !"mesh.obj".equals(file.getName())){
  966. file.delete();
  967. }
  968. if(file.getName().endsWith(".mtl") && !"output.house.mtl".equals(file.getName()) &&
  969. !"mesh.mtl".equals(file.getName())){
  970. file.delete();
  971. }
  972. }
  973. //打包
  974. ZipUtil.zip(meshPath, zipPath);
  975. //上传压缩包
  976. fYunFileService.uploadFile(bucket, zipPath, "downloads/extras/" + zipName);
  977. String url = ossUrlPrefix + "downloads/extras/" + zipName + "?t=" + Calendar.getInstance().getTimeInMillis();
  978. FileUtil.del(zipPath);
  979. return ResultData.ok(url);
  980. }
  981. @Override
  982. public List<SceneBean> listCleanOrigScene(int cleanOrigMonth) {
  983. Date time = Calendar.getInstance().getTime();
  984. time = DateUtil.beginOfDay(DateUtil.offset(time, DateField.MONTH, -cleanOrigMonth));
  985. return this.baseMapper.selectCleanOrigScene(time);
  986. }
  987. @Override
  988. public List<SceneBean> listCleanOss4DeletedScene(int month) {
  989. Date time = Calendar.getInstance().getTime();
  990. time = DateUtil.beginOfDay(DateUtil.offset(time, DateField.MONTH, -month));
  991. return this.baseMapper.listCleanOss4DeletedScene(time);
  992. }
  993. @Override
  994. public List<SceneBean> listCleanOss4TestCamera(Set<Long> cameraIds, int month) {
  995. Date time = Calendar.getInstance().getTime();
  996. time = DateUtil.beginOfDay(DateUtil.offset(time, DateField.MONTH, -month));
  997. return this.baseMapper.listCleanOss4TestCamera(cameraIds, time);
  998. }
  999. @Override
  1000. public List<SceneBean> listColdStorageScene(int cleanOrigMonth) {
  1001. Date time = Calendar.getInstance().getTime();
  1002. time = DateUtil.beginOfDay(DateUtil.offset(time, DateField.MONTH, -cleanOrigMonth));
  1003. return this.baseMapper.selectColdStorageScene(time);
  1004. }
  1005. }