SceneOfflinePackagePushServiceImpl.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. package com.fdkankan.external.service.impl;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.collection.CollUtil;
  4. import cn.hutool.core.date.DateUtil;
  5. import cn.hutool.core.date.TimeInterval;
  6. import cn.hutool.core.io.FileUtil;
  7. import cn.hutool.core.lang.UUID;
  8. import cn.hutool.core.thread.ExecutorBuilder;
  9. import cn.hutool.core.util.RuntimeUtil;
  10. import cn.hutool.core.util.StrUtil;
  11. import cn.hutool.core.util.ZipUtil;
  12. import cn.hutool.http.HttpUtil;
  13. import com.alibaba.fastjson.JSON;
  14. import com.alibaba.fastjson.JSONObject;
  15. import com.fdkankan.common.constant.CommonStatus;
  16. import com.fdkankan.common.constant.CommonSuccessStatus;
  17. import com.fdkankan.common.constant.FileSizeUnitType;
  18. import com.fdkankan.common.constant.SceneSource;
  19. import com.fdkankan.common.util.DateExtUtil;
  20. import com.fdkankan.common.util.FileSizeUtil;
  21. import com.fdkankan.external.bean.DownloadProcessBean;
  22. import com.fdkankan.external.bean.LaserDownloadBean;
  23. import com.fdkankan.external.callback.ErrorCallback;
  24. import com.fdkankan.external.callback.SuccessCallback;
  25. import com.fdkankan.external.entity.*;
  26. import com.fdkankan.external.httpclient.HttpClient;
  27. import com.fdkankan.external.mapper.SceneOfflinePackagePushMapper;
  28. import com.fdkankan.external.service.*;
  29. import com.fdkankan.external.util.CmdUtils;
  30. import com.fdkankan.fyun.face.FYunFileServiceInterface;
  31. import com.fdkankan.redis.constant.RedisKey;
  32. import com.fdkankan.redis.util.RedisUtil;
  33. import com.fdkankan.web.response.Result;
  34. import com.fdkankan.web.response.ResultData;
  35. import com.mybatisflex.core.query.QueryWrapper;
  36. import com.mybatisflex.spring.service.impl.ServiceImpl;
  37. import lombok.extern.slf4j.Slf4j;
  38. import org.apache.commons.lang3.StringUtils;
  39. import org.springframework.beans.factory.annotation.Autowired;
  40. import org.springframework.beans.factory.annotation.Value;
  41. import org.springframework.stereotype.Service;
  42. import javax.annotation.Resource;
  43. import java.io.File;
  44. import java.nio.charset.Charset;
  45. import java.util.*;
  46. import java.util.concurrent.ThreadPoolExecutor;
  47. import java.util.stream.Collectors;
  48. /**
  49. * 场景离线包推送表 服务层实现。
  50. *
  51. * @author dsx
  52. * @since 2023-12-07
  53. */
  54. @Slf4j
  55. @Service
  56. public class SceneOfflinePackagePushServiceImpl extends ServiceImpl<SceneOfflinePackagePushMapper, SceneOfflinePackagePush> implements ISceneOfflinePackagePushService {
  57. @Value("${host.4dkk.scene}")
  58. private String fdkkSceneHost;
  59. @Value("${host.laser}")
  60. private String laserHost;
  61. @Value("${oss.host.laser.old}")
  62. private String ossHostLaserOld;
  63. @Value("${oss.host.laser.new}")
  64. private String ossHostLaserNew;
  65. @Value("${oss.host.4dkk.old}")
  66. private String ossHost4dkkOld;
  67. @Value("${oss.host.4dkk.new}")
  68. private String ossHost4dkkNew;
  69. @Value("${api.4dkk.scene.getInfo}")
  70. private String getInfoUrl;
  71. @Value("${api.laser.downOfflineScene}")
  72. private String downOfflineSceneUrl;
  73. @Value("${file.offlineZip.dir}")
  74. private String offlineZipDir;
  75. private final static ThreadPoolExecutor threadPoolExecutor = ExecutorBuilder.create().setCorePoolSize(1).setMaxPoolSize(3).build();
  76. @Autowired
  77. private IDepartmentService departmentService;
  78. @Autowired
  79. private IDepartmentCameraService departmentCameraService;
  80. @Autowired
  81. private IScenePlusService scenePlusService;
  82. @Autowired
  83. private IScenePlusExtService scenePlusExtService;
  84. @Autowired
  85. private ISceneOfflinePackagePushService sceneOfflinePackagePushService;
  86. @Autowired
  87. private ISceneService sceneService;
  88. @Resource
  89. private HttpClient httpClient;
  90. @Autowired
  91. private ICameraService cameraService;
  92. @Resource
  93. private RedisUtil redisUtil;
  94. @Resource
  95. private FYunFileServiceInterface fYunFileService;
  96. @Override
  97. public void scenePushScheduleHandler(String departmentCode) {
  98. Department department = departmentService.getByCode(departmentCode);
  99. if(Objects.isNull(department)){
  100. return;
  101. }
  102. List<DepartmentCamera> departmentCameraList = departmentCameraService.listByDepartmentId(department.getId());
  103. if(CollUtil.isEmpty(departmentCameraList)){
  104. return;
  105. }
  106. List<String> snCodeList = departmentCameraList.stream().map(DepartmentCamera::getSnCode).collect(Collectors.toList());
  107. List<Camera> cameras = cameraService.listBySnCodeList(snCodeList);
  108. if(CollUtil.isEmpty(cameras)){
  109. return;
  110. }
  111. Map<Long, String> snCodeMap = cameras.stream().collect(Collectors.toMap(Camera::getId, Camera::getSnCode));
  112. List<Long> cameraIdList = cameras.stream().map(Camera::getId).collect(Collectors.toList());
  113. List<ScenePlus> scenePlusList = scenePlusService.listByCameraIdList(cameraIdList);
  114. if(CollUtil.isEmpty(scenePlusList)){
  115. return;
  116. }
  117. SceneOfflinePackagePush condition =
  118. SceneOfflinePackagePush.builder()
  119. .departmentId(department.getId())
  120. .pushStatus(CommonSuccessStatus.SUCCESS.code())
  121. .build();
  122. for (ScenePlus scenePlus : scenePlusList) {
  123. try {
  124. condition.setNum(scenePlus.getNum());
  125. SceneOfflinePackagePush commonPush =
  126. SceneOfflinePackagePush.builder()
  127. .departmentId(department.getId())
  128. .destUrl(department.getDestUrl())
  129. .storageType("oss")
  130. .snCode(snCodeMap.get(scenePlus.getCameraId()))
  131. .num(scenePlus.getNum()).build();
  132. ScenePlusExt scenePlusExt = null;
  133. //点云场景推送
  134. int isObj = CommonStatus.NO.code();
  135. if(scenePlus.getSceneSource() == SceneSource.JG.code() || scenePlus.getSceneSource() == SceneSource.SG.code()){
  136. condition.setZipType("laser");
  137. SceneOfflinePackagePush lastPush = sceneOfflinePackagePushService.getLastByCondition(condition);
  138. Scene scene = sceneService.getBySceneCode(scenePlus.getNum());
  139. if(Objects.isNull(scene)){
  140. throw new RuntimeException("未查询到激光系统场景信息,场景码:" + scenePlus.getNum());
  141. }
  142. //如果没有推送过或者推送过但是版本号不一致,就需要推送
  143. if(Objects.isNull(lastPush) || lastPush.getVersion() != scene.getOfflineVerForPush()){
  144. // threadPoolExecutor.submit(()->{
  145. SceneOfflinePackagePush push = BeanUtil.copyProperties(commonPush, SceneOfflinePackagePush.class);
  146. push.setZipType("laser");
  147. push.setVersion(scene.getOfflineVerForPush());
  148. try {
  149. sceneOfflinePackagePushService.scenePushHandler(push);
  150. }catch (Exception e){
  151. log.error("场景推送失败,num:{}",scenePlus.getNum(), e);
  152. }
  153. // });
  154. }
  155. scenePlusExt = scenePlusExtService.getByPlusId(scenePlus.getId());
  156. isObj = scenePlusExt.getIsObj();
  157. }
  158. //看看场景推送
  159. if(scenePlus.getSceneSource() == SceneSource.BM.code()
  160. || scenePlus.getSceneSource() == SceneSource.ZT.code()
  161. || isObj == CommonStatus.YES.code()){
  162. condition.setZipType("kankan");
  163. SceneOfflinePackagePush lastPush = sceneOfflinePackagePushService.getLastByCondition(condition);
  164. //查询版本号
  165. String sceneJsonStr = fYunFileService.getFileContent("scene_view_data/" + scenePlus.getNum() + "/data/scene.json");
  166. // String getInfo = fdkkSceneHost.concat(String.format(getInfoUrl, scenePlus.getNum()));
  167. // ResultData<Map<String, Object>> mapResultData = httpClient.get(getInfo, new HashMap<>(), new SuccessCallback(), new ErrorCallback());
  168. JSONObject sceneJson = JSON.parseObject(sceneJsonStr);
  169. int version = sceneJson.getIntValue("version");
  170. //如果没有推送过或者推送过但是版本号不一致,就需要推送
  171. if(Objects.isNull(lastPush) || lastPush.getVersion() != version){
  172. // threadPoolExecutor.submit(()->{
  173. SceneOfflinePackagePush push = BeanUtil.copyProperties(commonPush, SceneOfflinePackagePush.class);
  174. push.setZipType("kankan");
  175. push.setVersion(version);
  176. try {
  177. sceneOfflinePackagePushService.scenePushHandler(push);
  178. }catch (Exception e){
  179. log.error("场景推送失败,num:{}",scenePlus.getNum(), e);
  180. }
  181. // });
  182. }
  183. }
  184. }catch (Exception e){
  185. log.error("场景推送失败,num:{}",scenePlus.getNum(), e);
  186. }
  187. }
  188. }
  189. private String genZipUrl4Kankan(String num){
  190. String downloadUrl = null;
  191. String downloadTaskKey = RedisKey.SCENE_DOWNLOADS_TASK_V4;
  192. String progressKey = String.format(RedisKey.PREFIX_DOWNLOAD_PROGRESS_V4, num);
  193. TimeInterval timer = DateUtil.timer();
  194. Map<String,String> playod = new HashMap<>(2);
  195. playod.put("type","local");
  196. playod.put("num",num);
  197. redisUtil.del(progressKey);
  198. redisUtil.lRightPush(downloadTaskKey, JSONObject.toJSONString(playod));
  199. boolean exit = false;
  200. String progress = null;
  201. DownloadProcessBean downloadProcessBean = null;
  202. do {
  203. progress = redisUtil.get(progressKey);
  204. if(StringUtils.isEmpty(progress)){
  205. downloadProcessBean = new DownloadProcessBean();
  206. }else{
  207. downloadProcessBean = JSONObject.parseObject(progress, DownloadProcessBean.class);
  208. }
  209. Integer status = downloadProcessBean.getStatus();
  210. if (Objects.nonNull(status) && status == 1002) {
  211. downloadUrl = downloadProcessBean.getUrl();
  212. exit = true;
  213. }
  214. if (Objects.nonNull(status) && status == 1003) {
  215. log.error("下载失败,num:{}", num);
  216. throw new RuntimeException("下载失败,num:" + num);
  217. }
  218. if (timer.intervalMinute() > 8 * 60) {
  219. log.error("下载超时,num:{}", num);
  220. throw new RuntimeException("下载超时,num:" + num);
  221. }
  222. try {
  223. Thread.sleep(5000L);
  224. } catch (InterruptedException e) {
  225. throw new RuntimeException(e);
  226. }
  227. } while (!exit);
  228. return downloadUrl.replace(ossHost4dkkOld, ossHost4dkkNew);
  229. }
  230. private String genZipUrl4Laser(String num){
  231. String downloadUrl = null;
  232. TimeInterval timer = DateUtil.timer();
  233. boolean exit = false;
  234. //请求激光系统开始下载
  235. Map<String, Object> params = new HashMap<>();
  236. params.put("sceneCode", num);
  237. Result<LaserDownloadBean> resultData = httpClient.downOfflineScene(laserHost.concat(downOfflineSceneUrl), params, new SuccessCallback(), new ErrorCallback());
  238. LaserDownloadBean laserRes = resultData.getData();
  239. if(laserRes.getStatus() == 2){
  240. return laserRes.getUrl().replace(ossHostLaserOld, ossHostLaserNew);
  241. }
  242. do {
  243. resultData = httpClient.downOfflineScene(laserHost.concat(downOfflineSceneUrl), params, new SuccessCallback(), new ErrorCallback());
  244. laserRes = resultData.getData();
  245. Integer status = laserRes.getStatus();
  246. if (Objects.nonNull(status) && status == 2) {
  247. downloadUrl = laserRes.getUrl();
  248. exit = true;
  249. }
  250. if (Objects.nonNull(status) && status == -1) {
  251. log.error("下载失败,num:{}", num);
  252. throw new RuntimeException("下载失败,num:" + num);
  253. }
  254. if (timer.intervalMinute() > 8 * 60) {
  255. log.error("下载超时,num:{}", num);
  256. throw new RuntimeException("下载超时,num:" + num);
  257. }
  258. try {
  259. Thread.sleep(5000L);
  260. } catch (InterruptedException e) {
  261. throw new RuntimeException(e);
  262. }
  263. } while (!exit);
  264. return downloadUrl.replace(ossHostLaserOld, ossHostLaserNew);
  265. }
  266. @Override
  267. public SceneOfflinePackagePush getLastByCondition(SceneOfflinePackagePush condition) {
  268. QueryWrapper wrapper = new QueryWrapper();
  269. wrapper.eq(SceneOfflinePackagePush::getDepartmentId, condition.getDepartmentId());
  270. wrapper.eq(SceneOfflinePackagePush::getNum, condition.getNum());
  271. wrapper.orderBy(SceneOfflinePackagePush::getId, false);
  272. wrapper.limit(1);
  273. if(StrUtil.isNotEmpty(condition.getZipType())){
  274. wrapper.eq(SceneOfflinePackagePush::getZipType, condition.getZipType());
  275. }
  276. if(Objects.nonNull(condition.getPushStatus())){
  277. wrapper.eq(SceneOfflinePackagePush::getPushStatus, condition.getPushStatus());
  278. }
  279. return this.getOne(wrapper);
  280. }
  281. @Override
  282. public void scenePushHandler(SceneOfflinePackagePush push){
  283. String num = push.getNum();
  284. String zipType = push.getZipType();
  285. int version = push.getVersion();
  286. String downloadUrl = null;
  287. try {
  288. if("laser".equals(zipType)){
  289. downloadUrl = this.genZipUrl4Laser(num);
  290. }else{
  291. downloadUrl = this.genZipUrl4Kankan(num);
  292. }
  293. //开始推送到第三方服务
  294. if(StrUtil.isEmpty(downloadUrl)){
  295. throw new RuntimeException("场景下载失败,下载链接为空,场景码:" + num);
  296. }
  297. //下载到本地
  298. String zipPath = offlineZipDir.concat(num).concat(".zip");
  299. HttpUtil.downloadFile(downloadUrl, zipPath);
  300. String dirPath = null;
  301. String unzipPath = offlineZipDir.concat(num).concat("-").concat(zipType).concat("-").concat(String.valueOf(version));
  302. ZipUtil.unzip(zipPath, unzipPath, Charset.forName("GBK"));
  303. if("laser".equals(zipType)){
  304. dirPath = unzipPath.concat("/www");
  305. }else{
  306. dirPath = unzipPath.concat("/wwwroot/scene_view_data");
  307. }
  308. String zipDir = dirPath.concat("/zip/");
  309. FileUtil.del(zipDir);
  310. String volumeName = zipDir.concat(num).concat(".zip");
  311. FileUtil.mkParentDirs(volumeName);
  312. String cmd = "cd " + dirPath + " && zip -r " + volumeName + " " + num + " -s 500M";
  313. log.info("压缩命令:{}", cmd);
  314. CmdUtils.callLineSh(cmd, 200);
  315. log.info("分卷压缩完成");
  316. List<String> fileList = FileUtil.listFileNames(zipDir);
  317. if(CollUtil.isEmpty(fileList)){
  318. throw new RuntimeException("压缩包不存在");
  319. }
  320. String id = UUID.fastUUID().toString();
  321. int index = 1;
  322. for (String file : fileList) {
  323. Map<String, Object> params = new HashMap<>();
  324. params.put("id", id);
  325. params.put("action", "upload");
  326. params.put("fileName", file);
  327. params.put("file", FileUtil.file(zipDir.concat(file)));
  328. log.info("开发发送第{}个压缩包", index);
  329. String post = HttpUtil.post(push.getDestUrl(), params, 60 * 60 * 1000);
  330. log.info("第{}个场景推送成功,接收端返回结果:{}", index, post);
  331. ++index;
  332. }
  333. ScenePlus scenePlus = scenePlusService.getByNum(num);
  334. ScenePlusExt scenePlusExt = scenePlusExtService.getByPlusId(scenePlus.getId());
  335. String title = scenePlus.getTitle();
  336. if("laser".equals(zipType)){
  337. Scene scene = sceneService.getBySceneCode(scenePlus.getNum());
  338. title = scene.getTitle();
  339. }
  340. Map<String, Object> params = new HashMap<>();
  341. params.put("id", id);
  342. params.put("action", "save");
  343. params.put("fileName", num.concat(".zip"));
  344. params.put("num", num);
  345. params.put("title", title);
  346. params.put("zipType", zipType);
  347. params.put("downloadUrl", downloadUrl);
  348. params.put("version", push.getVersion());
  349. params.put("calcTime", DateExtUtil.format(scenePlusExt.getAlgorithmTime(), DateExtUtil.dateStyle8));
  350. String post = HttpUtil.post(push.getDestUrl(), params, 60 * 60 * 1000);
  351. log.info("场景推送成功,接收端返回结果:{}", post);
  352. push.setPushStatus(CommonSuccessStatus.SUCCESS.code());
  353. }catch (Exception e){
  354. log.error("场景推送失败,num:{}", num, e);
  355. push.setPushStatus(CommonSuccessStatus.FAIL.code());
  356. }
  357. this.saveOrUpdate(push);
  358. }
  359. public static void main(String[] args) throws Exception {
  360. // Map<String, Object> params = new HashMap<>();
  361. // params.put("num", "KJ-t-WypZvHxCR4X");
  362. // params.put("title", "123");
  363. // params.put("zipType", "kankan");
  364. // params.put("version", 1);
  365. // params.put("calcTime", DateExtUtil.format(new Date(), DateExtUtil.dateStyle8));
  366. // params.put("file", FileUtil.file("D:\\mnt\\external\\temp\\KJ-t-WypZvHxCR4X.zip"));
  367. // final String post = HttpUtil.post("http://localhost:8080/scene/receive2", params, 60 * 60 * 1000);
  368. // System.out.println(post);
  369. }
  370. }