package com.fdkankan.external.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.TimeInterval; import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.thread.ExecutorBuilder; import cn.hutool.core.util.RuntimeUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.ZipUtil; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.fdkankan.common.constant.CommonStatus; import com.fdkankan.common.constant.CommonSuccessStatus; import com.fdkankan.common.constant.FileSizeUnitType; import com.fdkankan.common.constant.SceneSource; import com.fdkankan.common.util.DateExtUtil; import com.fdkankan.common.util.FileSizeUtil; import com.fdkankan.external.bean.DownloadProcessBean; import com.fdkankan.external.bean.LaserDownloadBean; import com.fdkankan.external.callback.ErrorCallback; import com.fdkankan.external.callback.SuccessCallback; import com.fdkankan.external.entity.*; import com.fdkankan.external.httpclient.HttpClient; import com.fdkankan.external.mapper.SceneOfflinePackagePushMapper; import com.fdkankan.external.service.*; import com.fdkankan.external.util.CmdUtils; import com.fdkankan.redis.constant.RedisKey; import com.fdkankan.redis.util.RedisUtil; import com.fdkankan.web.response.Result; import com.fdkankan.web.response.ResultData; import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.spring.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.io.File; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.ThreadPoolExecutor; import java.util.stream.Collectors; /** * 场景离线包推送表 服务层实现。 * * @author dsx * @since 2023-12-07 */ @Slf4j @Service public class SceneOfflinePackagePushServiceImpl extends ServiceImpl implements ISceneOfflinePackagePushService { @Value("${host.4dkk.scene}") private String fdkkSceneHost; @Value("${host.laser}") private String laserHost; @Value("${oss.host.laser.old}") private String ossHostLaserOld; @Value("${oss.host.laser.new}") private String ossHostLaserNew; @Value("${oss.host.4dkk.old}") private String ossHost4dkkOld; @Value("${oss.host.4dkk.new}") private String ossHost4dkkNew; @Value("${api.4dkk.scene.getInfo}") private String getInfoUrl; @Value("${api.laser.downOfflineScene}") private String downOfflineSceneUrl; @Value("${file.offlineZip.dir}") private String offlineZipDir; private final static ThreadPoolExecutor threadPoolExecutor = ExecutorBuilder.create().setCorePoolSize(1).setMaxPoolSize(3).build(); @Autowired private IDepartmentService departmentService; @Autowired private IDepartmentCameraService departmentCameraService; @Autowired private IScenePlusService scenePlusService; @Autowired private IScenePlusExtService scenePlusExtService; @Autowired private ISceneOfflinePackagePushService sceneOfflinePackagePushService; @Autowired private ISceneService sceneService; @Resource private HttpClient httpClient; @Autowired private ICameraService cameraService; @Resource private RedisUtil redisUtil; @Override public void scenePushScheduleHandler(String departmentCode) { Department department = departmentService.getByCode(departmentCode); if(Objects.isNull(department)){ return; } List departmentCameraList = departmentCameraService.listByDepartmentId(department.getId()); if(CollUtil.isEmpty(departmentCameraList)){ return; } List snCodeList = departmentCameraList.stream().map(DepartmentCamera::getSnCode).collect(Collectors.toList()); List cameras = cameraService.listBySnCodeList(snCodeList); if(CollUtil.isEmpty(cameras)){ return; } Map snCodeMap = cameras.stream().collect(Collectors.toMap(Camera::getId, Camera::getSnCode)); List cameraIdList = cameras.stream().map(Camera::getId).collect(Collectors.toList()); List scenePlusList = scenePlusService.listByCameraIdList(cameraIdList); if(CollUtil.isEmpty(scenePlusList)){ return; } SceneOfflinePackagePush condition = SceneOfflinePackagePush.builder() .departmentId(department.getId()) .pushStatus(CommonSuccessStatus.SUCCESS.code()) .build(); for (ScenePlus scenePlus : scenePlusList) { try { condition.setNum(scenePlus.getNum()); SceneOfflinePackagePush commonPush = SceneOfflinePackagePush.builder() .departmentId(department.getId()) .destUrl(department.getDestUrl()) .storageType("oss") .snCode(snCodeMap.get(scenePlus.getCameraId())) .num(scenePlus.getNum()).build(); ScenePlusExt scenePlusExt = null; //点云场景推送 int isObj = CommonStatus.NO.code(); if(scenePlus.getSceneSource() == SceneSource.JG.code() || scenePlus.getSceneSource() == SceneSource.SG.code()){ condition.setZipType("laser"); SceneOfflinePackagePush lastPush = sceneOfflinePackagePushService.getLastByCondition(condition); Scene scene = sceneService.getBySceneCode(scenePlus.getNum()); if(Objects.isNull(scene)){ throw new RuntimeException("未查询到激光系统场景信息,场景码:" + scenePlus.getNum()); } //如果没有推送过或者推送过但是版本号不一致,就需要推送 if(Objects.isNull(lastPush) || lastPush.getVersion() != scene.getOfflineVerForPush()){ // threadPoolExecutor.submit(()->{ SceneOfflinePackagePush push = BeanUtil.copyProperties(commonPush, SceneOfflinePackagePush.class); push.setZipType("laser"); push.setVersion(scene.getOfflineVerForPush()); try { sceneOfflinePackagePushService.scenePushHandler(push); }catch (Exception e){ log.error("场景推送失败,num:{}",scenePlus.getNum(), e); } // }); } scenePlusExt = scenePlusExtService.getByPlusId(scenePlus.getId()); isObj = scenePlusExt.getIsObj(); } //看看场景推送 if(scenePlus.getSceneSource() == SceneSource.BM.code() || scenePlus.getSceneSource() == SceneSource.ZT.code() || isObj == CommonStatus.YES.code()){ condition.setZipType("kankan"); SceneOfflinePackagePush lastPush = sceneOfflinePackagePushService.getLastByCondition(condition); //查询版本号 String getInfo = fdkkSceneHost.concat(String.format(getInfoUrl, scenePlus.getNum())); ResultData> mapResultData = httpClient.get(getInfo, new HashMap<>(), new SuccessCallback(), new ErrorCallback()); int version = (int)mapResultData.getData().get("version"); //如果没有推送过或者推送过但是版本号不一致,就需要推送 if(Objects.isNull(lastPush) || lastPush.getVersion() != version){ // threadPoolExecutor.submit(()->{ SceneOfflinePackagePush push = BeanUtil.copyProperties(commonPush, SceneOfflinePackagePush.class); push.setZipType("kankan"); push.setVersion(version); try { sceneOfflinePackagePushService.scenePushHandler(push); }catch (Exception e){ log.error("场景推送失败,num:{}",scenePlus.getNum(), e); } // }); } } }catch (Exception e){ log.error("场景推送失败,num:{}",scenePlus.getNum(), e); } } } private String genZipUrl4Kankan(String num){ String downloadUrl = null; String downloadTaskKey = RedisKey.SCENE_DOWNLOADS_TASK_V4; String progressKey = String.format(RedisKey.PREFIX_DOWNLOAD_PROGRESS_V4, num); TimeInterval timer = DateUtil.timer(); Map playod = new HashMap<>(2); playod.put("type","local"); playod.put("num",num); redisUtil.lRightPush(downloadTaskKey, JSONObject.toJSONString(playod)); boolean exit = false; String progress = null; DownloadProcessBean downloadProcessBean = null; do { progress = redisUtil.get(progressKey); if(StringUtils.isEmpty(progress)){ downloadProcessBean = new DownloadProcessBean(); }else{ downloadProcessBean = JSONObject.parseObject(progress, DownloadProcessBean.class); } Integer status = downloadProcessBean.getStatus(); if (Objects.nonNull(status) && status == 1002) { downloadUrl = downloadProcessBean.getUrl(); exit = true; } if (Objects.nonNull(status) && status == 1003) { log.error("下载失败,num:{}", num); throw new RuntimeException("下载失败,num:" + num); } if (timer.intervalMinute() > 8 * 60) { log.error("下载超时,num:{}", num); throw new RuntimeException("下载超时,num:" + num); } try { Thread.sleep(5000L); } catch (InterruptedException e) { throw new RuntimeException(e); } } while (!exit); return downloadUrl.replace(ossHost4dkkOld, ossHost4dkkNew); } private String genZipUrl4Laser(String num){ String downloadUrl = null; TimeInterval timer = DateUtil.timer(); boolean exit = false; //请求激光系统开始下载 Map params = new HashMap<>(); params.put("sceneCode", num); Result resultData = httpClient.downOfflineScene(laserHost.concat(downOfflineSceneUrl), params, new SuccessCallback(), new ErrorCallback()); LaserDownloadBean laserRes = resultData.getData(); if(laserRes.getStatus() == 2){ return laserRes.getUrl().replace(ossHostLaserOld, ossHostLaserNew); } do { resultData = httpClient.downOfflineScene(laserHost.concat(downOfflineSceneUrl), params, new SuccessCallback(), new ErrorCallback()); laserRes = resultData.getData(); Integer status = laserRes.getStatus(); if (Objects.nonNull(status) && status == 2) { downloadUrl = laserRes.getUrl(); exit = true; } if (Objects.nonNull(status) && status == -1) { log.error("下载失败,num:{}", num); throw new RuntimeException("下载失败,num:" + num); } if (timer.intervalMinute() > 8 * 60) { log.error("下载超时,num:{}", num); throw new RuntimeException("下载超时,num:" + num); } try { Thread.sleep(5000L); } catch (InterruptedException e) { throw new RuntimeException(e); } } while (!exit); return downloadUrl.replace(ossHostLaserOld, ossHostLaserNew); } @Override public SceneOfflinePackagePush getLastByCondition(SceneOfflinePackagePush condition) { QueryWrapper wrapper = new QueryWrapper(); wrapper.eq(SceneOfflinePackagePush::getDepartmentId, condition.getDepartmentId()); wrapper.eq(SceneOfflinePackagePush::getNum, condition.getNum()); wrapper.orderBy(SceneOfflinePackagePush::getId, false); wrapper.limit(1); if(StrUtil.isNotEmpty(condition.getZipType())){ wrapper.eq(SceneOfflinePackagePush::getZipType, condition.getZipType()); } if(Objects.nonNull(condition.getPushStatus())){ wrapper.eq(SceneOfflinePackagePush::getPushStatus, condition.getPushStatus()); } return this.getOne(wrapper); } @Override public void scenePushHandler(SceneOfflinePackagePush push){ String num = push.getNum(); String zipType = push.getZipType(); int version = push.getVersion(); String downloadUrl = null; try { if("laser".equals(zipType)){ downloadUrl = this.genZipUrl4Laser(num); }else{ downloadUrl = this.genZipUrl4Kankan(num); } //开始推送到第三方服务 if(StrUtil.isEmpty(downloadUrl)){ throw new RuntimeException("场景下载失败,下载链接为空,场景码:" + num); } //下载到本地 String zipPath = offlineZipDir.concat(num).concat(".zip"); HttpUtil.downloadFile(downloadUrl, zipPath); String dirPath = null; String unzipPath = offlineZipDir.concat(num).concat("-").concat(zipType).concat("-").concat(String.valueOf(version)); ZipUtil.unzip(zipPath, unzipPath, Charset.forName("GBK")); if("laser".equals(zipType)){ dirPath = unzipPath.concat("/www"); }else{ dirPath = unzipPath.concat("/wwwroot/scene_view_data"); } String zipDir = dirPath.concat("/zip/"); FileUtil.del(zipDir); String volumeName = zipDir.concat(num).concat(".zip"); FileUtil.mkParentDirs(volumeName); String cmd = "cd " + dirPath + " && zip -r " + volumeName + " " + num + " -s 100M"; log.info("压缩命令:{}", cmd); CmdUtils.callLineSh(cmd, 200); List fileList = FileUtil.listFileNames(zipDir); if(CollUtil.isEmpty(fileList)){ throw new RuntimeException("压缩包不存在"); } String id = UUID.fastUUID().toString(); for (String file : fileList) { Map params = new HashMap<>(); params.put("id", id); params.put("action", "upload"); params.put("fileName", file); params.put("file", FileUtil.file(zipDir.concat(file))); String post = HttpUtil.post(push.getDestUrl(), params, 60 * 60 * 1000); log.info("场景推送成功,接收端返回结果:{}", post); } ScenePlus scenePlus = scenePlusService.getByNum(num); ScenePlusExt scenePlusExt = scenePlusExtService.getByPlusId(scenePlus.getId()); String title = scenePlus.getTitle(); if("laser".equals(zipType)){ Scene scene = sceneService.getBySceneCode(scenePlus.getNum()); title = scene.getTitle(); } Map params = new HashMap<>(); params.put("id", id); params.put("action", "save"); params.put("fileName", num.concat(".zip")); params.put("num", num); params.put("title", title); params.put("zipType", zipType); params.put("downloadUrl", downloadUrl); params.put("version", push.getVersion()); params.put("calcTime", DateExtUtil.format(scenePlusExt.getAlgorithmTime(), DateExtUtil.dateStyle8)); String post = HttpUtil.post(push.getDestUrl(), params, 60 * 60 * 1000); log.info("场景推送成功,接收端返回结果:{}", post); push.setPushStatus(CommonSuccessStatus.SUCCESS.code()); }catch (Exception e){ log.error("场景推送失败,num:{}", num, e); push.setPushStatus(CommonSuccessStatus.FAIL.code()); } this.saveOrUpdate(push); } public static void main(String[] args) throws Exception { // Map params = new HashMap<>(); // params.put("num", "KJ-t-WypZvHxCR4X"); // params.put("title", "123"); // params.put("zipType", "kankan"); // params.put("version", 1); // params.put("calcTime", DateExtUtil.format(new Date(), DateExtUtil.dateStyle8)); // params.put("file", FileUtil.file("D:\\mnt\\external\\temp\\KJ-t-WypZvHxCR4X.zip")); // final String post = HttpUtil.post("http://localhost:8080/scene/receive2", params, 60 * 60 * 1000); // System.out.println(post); } }