package com.ycl.platform.service.impl; import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ycl.config.PlatformConfig; import com.ycl.config.ServerConfig; import com.ycl.exception.ServiceException; import com.ycl.platform.domain.entity.*; import com.ycl.platform.domain.excel.PointExport; import com.ycl.platform.domain.form.*; import com.ycl.platform.domain.query.*; import com.ycl.platform.domain.vo.*; import com.ycl.platform.domain.vo.screen.ScreenWorkOrderVO; import com.ycl.platform.domain.vo.screen.WorkOrderRegionVO; import com.ycl.platform.domain.vo.screen.WorkOrderTotalVO; import com.ycl.platform.mapper.*; import com.ycl.platform.service.NotifyService; import com.ycl.platform.service.WorkOrderAuditingRecordService; import com.ycl.platform.service.WorkOrderService; import com.ycl.platform.service.YwPointService; import com.ycl.platform.wvp.StreamContent; import com.ycl.platform.wvp.WVPResult; import com.ycl.system.Result; import com.ycl.system.domain.SysConfig; import com.ycl.system.entity.SysDictData; import com.ycl.system.mapper.SysConfigMapper; import com.ycl.system.mapper.SysDictDataMapper; import com.ycl.system.model.LoginUser; import com.ycl.system.page.PageUtil; import com.ycl.utils.DateUtils; import com.ycl.utils.SecurityUtils; import com.ycl.utils.http.HttpUtils; import com.ycl.utils.redis.RedisCache; import com.ycl.utils.uuid.IdUtils; import constant.ApiConstants; import constant.CheckConstants; import constant.Constants; import constant.RedisConstant; import enumeration.ErrorType; import enumeration.general.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.bytedeco.javacv.*; import org.bytedeco.opencv.global.opencv_imgcodecs; import org.bytedeco.opencv.opencv_core.Mat; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import pojo.CascadeOption; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * 工单 服务实现类 * * @author xp * @since 2024-03-05 */ @Slf4j @Service @RequiredArgsConstructor public class WorkOrderServiceImpl extends ServiceImpl implements WorkOrderService { private final YwPointService ywPointService; private final WorkOrderAuditingRecordMapper workOrderAuditingRecordMapper; private final WorkOrderAuditingRecordService workOrderAuditingRecordService; private final WorkOrderYwConditionRecordMapper workOrderYwConditionRecordMapper; private final NotifyService notifyService; private final WorkOrderDistributeRecordMapper workOrderDistributeRecordMapper; private final WorkOrderErrorTypeServiceImpl workOrderErrorTypeService; private final WorkOrderErrorTypeMapper workOrderErrorTypeMapper; private final SysConfigMapper configMapper; private final ReportMapper reportMapper; private final WorkOrderCheckImgMapper workOrderCheckImgMapper; private final WorkOrderWhiteMapper workOrderWhiteMapper; private final DeviceInfoMapper deviceInfoMapper; private final ApplicationContext applicationContext; @Value("${rtsp.server:http://127.0.0.1:7788}") private String rtspServer; private final String DISTRIBUTE_LOCK_KEY = "distributeLock"; private final static String IMPORTANT = "important"; @Autowired private RedisCache redisCache; @Override public synchronized Boolean innerAddWorkOrder(List workOrderList) { //避免坑(事务加锁会失效、方法内部调用事务会失效) WorkOrderServiceImpl self = applicationContext.getBean(WorkOrderServiceImpl.class); return self.batchAddWorkOrder(workOrderList); } @Transactional(rollbackFor = Exception.class) public Boolean batchAddWorkOrder(List workOrderList){ int total = workOrderList.size(); // 查询出白名单列表 List serialNumbers = workOrderWhiteMapper.selectList().stream().map(WorkOrderWhite::getSerialNumber).collect(Collectors.toList()); // 遍历工单列表,判断是否在白名单中 workOrderList = workOrderList.stream().filter(item -> !serialNumbers.contains(item.getSerialNumber())) .collect(Collectors.toList()); workOrderList = workOrderList.stream().filter(item -> { return StringUtils.hasText(item.getSerialNumber()) && Objects.nonNull(item.getStatus()) && !CollectionUtils.isEmpty(item.getErrorTypeList()); }).collect(Collectors.toList()); if (CollectionUtils.isEmpty(workOrderList)) { return Boolean.TRUE; } // 根据国标码去重 workOrderList = workOrderList.stream() .collect(Collectors.toMap( WorkOrder::getSerialNumber, p -> p, (existing, replacement) -> existing // 冲突时保留第一个 )).values().stream().collect(Collectors.toList()); List serialNumberList = workOrderList.stream().map(WorkOrder::getSerialNumber).collect(Collectors.toList()); // 查出数据库中国标码对应的未完成的工单 List inDatabaseWorkOrderList = baseMapper.getNotFinishedWorkOrders(serialNumberList); inDatabaseWorkOrderList.stream().forEach(item -> { if (StringUtils.hasText(item.getErrorType())) { item.setErrorTypeList(List.of(item.getErrorType().split(","))); } else { item.setErrorTypeList(new ArrayList<>(1)); } }); Map mapping = inDatabaseWorkOrderList.stream().collect((Collectors.toMap(WorkOrder::getSerialNumber, workOrder -> workOrder))); List waitAddList = new ArrayList<>(48); List waitAddErrorTypeList = new ArrayList<>(48); Integer updateNum = 0; Date now = new Date(); // 因故障类型不一致而要更新状态的工单 List willUpdateStatusWorkOrderList = new ArrayList<>(48); // 更改工单类型而要增加的系统运维处理信息 List willAddMsg = new ArrayList<>(48); // 即将要添加的错误类型 List willAddErrorType = new ArrayList<>(96); for (WorkOrder workOrder : workOrderList) { WorkOrder databaseWorkOrder = mapping.get(workOrder.getSerialNumber()); if (Objects.nonNull(databaseWorkOrder)) { List errorNameList = databaseWorkOrder.getErrorTypeList(); List errorTypes = workOrder.getErrorTypeList(); if (errorNameList.containsAll(errorTypes)) { // 如果,国标码、故障类型都一样,则跳过不处理 continue; } else { for (String errorType : errorTypes) { if (!errorNameList.contains(errorType)) { // 错误类型不一样,就新增一个错误类型,并且重置工单状态为待处理 WorkOrderErrorType workOrderErrorType = new WorkOrderErrorType(); workOrderErrorType.setWorkOrderNo(databaseWorkOrder.getWorkOrderNo()); workOrderErrorType.setCreateTime(now); workOrderErrorType.setUpdateTime(now); workOrderErrorType.setErrorName(errorType); willAddErrorType.add(workOrderErrorType); } } //避免代下发的工单直接变成下发 if(!databaseWorkOrder.getStatus().equals(WorkOrderStatusEnum.WAIT_DISTRIBUTE)) { databaseWorkOrder.setStatus(WorkOrderStatusEnum.DISTRIBUTED); } databaseWorkOrder.setUpdateTime(now); willUpdateStatusWorkOrderList.add(databaseWorkOrder); updateNum++; // 同时新增一个运维处理信息,表明此工单被调整 WorkOrderYwConditionRecord ywRecord = new WorkOrderYwConditionRecord(); ywRecord.setWorkOrderNo(databaseWorkOrder.getWorkOrderNo()); ywRecord.setCommitUser(1); ywRecord.setYwCondition("故障类型更新,工单状态调整为待处理"); ywRecord.setCreateTime(new Date()); ywRecord.setSysMsg(Boolean.TRUE); willAddMsg.add(ywRecord); } } else { workOrder.setCreateTime(new Date()); workOrder.setUpdateTime(new Date()); // // 如果报备过,使用最新报备的错误类型 // Report report = reportMapper.checkPointReported(workOrder.getSerialNumber()); // if (Objects.nonNull(report)) { // workOrder.setErrorType(report.getErrorType()); // } waitAddList.add(workOrder); } } if (willAddErrorType.size() > 0) { workOrderErrorTypeService.getBaseMapper().addMany(willAddErrorType); } if (willAddMsg.size() > 0) { workOrderYwConditionRecordMapper.insertMany(willAddMsg); } log.info("将要更新的工单数:" + willUpdateStatusWorkOrderList.size()); if (willUpdateStatusWorkOrderList.size() > 0) { this.baseMapper.updateMany(willUpdateStatusWorkOrderList); } if (CollectionUtils.isEmpty(waitAddList)) { return Boolean.TRUE; } List willAddSerialNumber = waitAddList.stream().map(WorkOrder::getSerialNumber).collect(Collectors.toList()); //只生成考核设备、且有运维单位的工单 List pointList = new LambdaQueryChainWrapper<>(ywPointService.getBaseMapper()) .select(YwPoint::getUnitId, YwPoint::getSerialNumber, YwPoint::getImportantTag, YwPoint::getImportantTag, YwPoint::getProvinceTag, YwPoint::getImportantCommandImageTag) .in(YwPoint::getSerialNumber, willAddSerialNumber) .eq(YwPoint::getExamineStatus, Boolean.TRUE) .isNotNull(YwPoint::getUnitId) .list(); Map pointMapping = pointList.stream().collect(Collectors.toMap(YwPoint::getSerialNumber, point -> point)); // 查出重点点位、普通点位的处理时间 SysConfig important = configMapper.checkConfigKeyUnique("important.wordkorder.time"); SysConfig normal = configMapper.checkConfigKeyUnique("normal.wordkorder.alarm.time"); // 如果即将生成工单,但是设备国标码查不到点位,则不添加? List notAddList = new ArrayList<>(); //查redis今日工单数量 int workOrderNum = 0; for (WorkOrder workOrder : waitAddList) { YwPoint point = pointMapping.get(workOrder.getSerialNumber()); if (Objects.isNull(point)) { notAddList.add(workOrder); continue; } if (Objects.nonNull(point.getUnitId())) { workOrder.setUnitId(Math.toIntExact(point.getUnitId())); } if (point.getImportantTag() || point.getImportantCommandImageTag()) { workOrder.setProcessingPeriod(Integer.valueOf(important.getConfigValue())); } else { workOrder.setProcessingPeriod(Integer.valueOf(normal.getConfigValue())); } } waitAddList.removeAll(notAddList); if (CollectionUtils.isEmpty(waitAddList)) { return Boolean.TRUE; } //UUID作为value,保证上锁的线程自己解锁 String requestId = IdUtils.fastSimpleUUID(); try { for (int i = 0; i < 3; i++) { boolean result = redisCache.acquireLock(RedisConstant.WORKORDER_NUM_LOCK, requestId, 10000); if (result) { //查今日工单量 Object redisNum = redisCache.getCacheObject(RedisConstant.WORKORDER_NUM); workOrderNum = redisNum == null ? 0 : (Integer) redisNum; break; } else { if (i == 2) { log.error("锁被占用"); return Boolean.FALSE; } //等待一段时间后继续 Thread.sleep(5000); } } } catch (InterruptedException e) { log.error("获取锁异常"); return Boolean.FALSE; } //记录工单数,补充工单号 for (WorkOrder workOrder : waitAddList) { //数字前面补0 workOrderNum++; workOrder.setWorkOrderNo(IdUtils.workOrderNO(now, String.format("%05d", workOrderNum))); // 保存错误类型 for (String errorType : workOrder.getErrorTypeList()) { WorkOrderErrorType workOrderErrorType = new WorkOrderErrorType(); workOrderErrorType.setWorkOrderNo(workOrder.getWorkOrderNo()); workOrderErrorType.setCreateTime(now); workOrderErrorType.setUpdateTime(now); workOrderErrorType.setErrorName(errorType); waitAddErrorTypeList.add(workOrderErrorType); } log.info("即将生成的工单:{}",workOrder); } redisCache.setCacheObject(RedisConstant.WORKORDER_NUM, workOrderNum); redisCache.releaseLock(RedisConstant.WORKORDER_NUM_LOCK, requestId); // 保存工单和故障类型 baseMapper.addMany(waitAddList); if (!CollectionUtils.isEmpty(waitAddErrorTypeList)) { workOrderErrorTypeService.getBaseMapper().addMany(waitAddErrorTypeList); } // 如果是直接下发,添加下发记录 if (WorkOrderStatusEnum.DISTRIBUTED.equals(waitAddList.get(0).getStatus())) { ArrayList list = new ArrayList<>(); List distributedRecordList = waitAddList.stream().map(item -> { list.add(item.getSerialNumber()); WorkOrderDistributeRecord workOrderDistributeRecord = new WorkOrderDistributeRecord(); workOrderDistributeRecord.setWorkOrderNo(item.getWorkOrderNo()); workOrderDistributeRecord.setDistributeWay(WorkOrderDistributeWayEnum.DIRECT_DISTRIBUTE); workOrderDistributeRecord.setUserId(1L); workOrderDistributeRecord.setCreateTime(now); workOrderDistributeRecord.setUpdateTime(now); return workOrderDistributeRecord; }).collect(Collectors.toList()); workOrderDistributeRecordMapper.insertBatch(distributedRecordList); // 同步点位状态 ywPointService.updateRecovery(list, 1); } log.info("传入工单总数: {},实际添加工单数:{}, 实际修改工单数:{}", total, waitAddList.size(), updateNum); return Boolean.TRUE; } /** * 添加 * * @param form * @return */ @Override public Result add(WorkOrderForm form) { //查redis今日工单数量 int workOrderNum = 0; //UUID作为value,保证上锁的线程自己解锁 String requestId = IdUtils.fastSimpleUUID(); boolean result = redisCache.acquireLock(RedisConstant.WORKORDER_NUM_LOCK, requestId, 10000); if (result) { //查今日工单量 Object redisNum = redisCache.getCacheObject(RedisConstant.WORKORDER_NUM); workOrderNum = redisNum == null ? 0 : (Integer) redisNum; workOrderNum++; redisCache.setCacheObject(RedisConstant.WORKORDER_NUM, workOrderNum); redisCache.releaseLock(RedisConstant.WORKORDER_NUM_LOCK, requestId); } else { return Result.error("工单正在添加,请稍后再试"); } WorkOrder entity = WorkOrderForm.getEntityByForm(form, null); // 查询出白名单列表 List serialNumbers = workOrderWhiteMapper.selectList().stream().map(WorkOrderWhite::getSerialNumber).collect(Collectors.toList()); // 遍历工单列表,判断是否在白名单中 if (serialNumbers.contains(entity.getSerialNumber())){ return Result.error("该设备在白名单中,不能添加工单"); }else { entity.setCreateTime(DateUtils.getNowDate()); entity.setStatus(WorkOrderStatusEnum.WAIT_DISTRIBUTE); Date now = new Date(); entity.setCreateTime(now); entity.setUpdateTime(now); entity.setWorkOrderNo(IdUtils.workOrderNO(now, String.format("%05d", workOrderNum))); entity.setErrorType(String.join(",", form.getErrorType())); List workOrderErrorTypes = form.getErrorType().stream().map(errorType -> new WorkOrderErrorType(entity.getWorkOrderNo(), errorType)).toList(); workOrderErrorTypeService.getBaseMapper().insertWorkOrderErrorTypeList(workOrderErrorTypes); if (baseMapper.insert(entity) > 0) { return Result.ok("添加成功"); } return Result.error("添加失败"); } } /** * 修改 * * @param form * @return */ @Override public Result update(WorkOrderForm form) { WorkOrder entity = baseMapper.selectById(form.getId()); // 为空抛IllegalArgumentException,做全局异常处理 Assert.notNull(entity, "记录不存在"); BeanUtils.copyProperties(form, entity); Date now = new Date(); entity.setUpdateTime(now); if (baseMapper.updateById(entity) > 0) { return Result.ok("修改成功"); } return Result.error("修改失败"); } @Override @Transactional(rollbackFor = Exception.class) public Result auditing(WorkOrderAuditingForm form) { WorkOrder workOrder = baseMapper.selectById(form.getId()); // 工单状态 workOrder.setStatus(form.getAuditingResult()); baseMapper.updateById(workOrder); // 添加一条审核记录 WorkOrderAuditingRecord workOrderAuditingRecord = new WorkOrderAuditingRecord(); workOrderAuditingRecord.setWorkOrderNo(workOrder.getWorkOrderNo()); workOrderAuditingRecord.setAuditingUser(SecurityUtils.getLoginUser().getUserId().intValue()); workOrderAuditingRecord.setResult(form.getAuditingResult().getDesc()); workOrderAuditingRecord.setRemark(form.getAuditingRemark()); workOrderAuditingRecordMapper.insert(workOrderAuditingRecord); // 添加新通知 Notify notify = Notify.genEntityByUnit(NotifyTypeEnum.WORK_ORDER, form.getAuditingResult().getDesc(), workOrder.getUnitId(), UrgentLevelEnum.WARNING, workOrder.getWorkOrderNo()); notifyService.save(notify); // 同步点位状态 if (form.getAuditingResult() == WorkOrderStatusEnum.AUDITING_SUCCESS) { ywPointService.updateRecovery(Collections.singletonList(workOrder.getSerialNumber()), 0); } return Result.ok("操作成功"); } @Override @Transactional(rollbackFor = Exception.class) public Result batchAuditing(WorkOrderBatchAuditingForm form) { //根据工单编号获取工单 List list = baseMapper.selectByNos(form.getWorkOrderNos()); if (list.isEmpty()) { return Result.error("没有工单可以审核"); } List workOrderNoList = list.stream().map(WorkOrder::getWorkOrderNo).collect(Collectors.toList()); List serialNumbers = list.stream().map(WorkOrder::getSerialNumber).toList(); // 工单状态 LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper<>(); lambdaUpdateWrapper.in(WorkOrder::getWorkOrderNo, workOrderNoList); lambdaUpdateWrapper.set(WorkOrder::getStatus, form.getAuditingResult()); baseMapper.update(lambdaUpdateWrapper); // 添加多条审核记录 List workOrderAuditingRecords = new ArrayList<>(); for (String workOrderNo : workOrderNoList) { WorkOrderAuditingRecord workOrderAuditingRecord = new WorkOrderAuditingRecord(); workOrderAuditingRecord.setWorkOrderNo(workOrderNo); workOrderAuditingRecord.setAuditingUser(SecurityUtils.getLoginUser().getUserId().intValue()); workOrderAuditingRecord.setResult(form.getAuditingResult().getDesc()); workOrderAuditingRecord.setRemark(form.getAuditingRemark()); workOrderAuditingRecords.add(workOrderAuditingRecord); } workOrderAuditingRecordService.saveBatch(workOrderAuditingRecords); // 添加新通知 List notifies = new ArrayList<>(); for (WorkOrder workOrder : list) { Notify notify = Notify.genEntityByUnit(NotifyTypeEnum.WORK_ORDER, form.getAuditingResult().getDesc(), workOrder.getUnitId(), UrgentLevelEnum.WARNING, workOrder.getWorkOrderNo()); notifies.add(notify); } // 同步点位状态 if (form.getAuditingResult() == WorkOrderStatusEnum.AUDITING_SUCCESS) { ywPointService.updateRecovery(serialNumbers, 0); } notifyService.saveBatch(notifies); return Result.ok("操作成功"); } @Override @Transactional(rollbackFor = Exception.class) public Result ywCondition(WorkOrderYWConditionForm form) { WorkOrder workOrder = baseMapper.selectById(form.getId()); if (Objects.isNull(workOrder)) { throw new ServiceException("工单不存在"); } // 工单状态 workOrder.setStatus(WorkOrderStatusEnum.YW_HANDLE); workOrder.setYwHandleTime(LocalDateTime.now()); baseMapper.updateById(workOrder); // 添加一条运维情况记录 WorkOrderYwConditionRecord workOrderYwConditionRecord = new WorkOrderYwConditionRecord(); workOrderYwConditionRecord.setWorkOrderNo(workOrder.getWorkOrderNo()); workOrderYwConditionRecord.setCommitUser(SecurityUtils.getLoginUser().getUserId().intValue()); workOrderYwConditionRecord.setYwCondition(form.getYwCondition()); workOrderYwConditionRecord.setYwProofMaterials(form.getYwProofMaterials()); workOrderYwConditionRecord.setSysMsg(Boolean.FALSE); workOrderYwConditionRecordMapper.insert(workOrderYwConditionRecord); //异步获取图片 WorkOrderServiceImpl self = applicationContext.getBean(WorkOrderServiceImpl.class); self.getImage(workOrder); return Result.ok("操作成功"); } @Async("threadPoolTaskExecutor") public void getImage(WorkOrder workOrder) { List gbDevices = new LambdaQueryChainWrapper<>(deviceInfoMapper) .orderByDesc(DeviceInfo::getUpdateTime) .last("limit 1") .list(); if (CollectionUtils.isEmpty(gbDevices)) { return; } // 国标设备的编码就是取视频流的设备编码,国标设备就一个。国标设备的每一个通道代表一个摄像头,也就是设备id是取流的通道id String frameImg = null; try { log.info("国标平台:{},设备编码:{},工单号:{}",gbDevices.get(0).getDeviceId(), workOrder.getSerialNumber(), workOrder.getWorkOrderNo()); frameImg = this.getFrameImgByDevice(gbDevices.get(0).getDeviceId(), workOrder.getSerialNumber(), workOrder.getWorkOrderNo()); } catch (Exception e) { e.printStackTrace(); } if (StringUtils.hasText(frameImg)) { WorkOrderCheckImg img = new WorkOrderCheckImg(); img.setWorkOrderNo(workOrder.getWorkOrderNo()); img.setImgUrl(frameImg); img.setCreateTime(new Date()); workOrderCheckImgMapper.insert(img); } } @Override public List selectYwConditionByYwId(String workOrderNo) { List ywConditionList = workOrderYwConditionRecordMapper.selectYwConditionByYwId(workOrderNo); ywConditionList.stream().forEach(item -> { if(item.getUserId().equals(1)){ item.setUnitName("管理员"); } if (Objects.nonNull(item.getSysMsg()) && item.getSysMsg()) { item.setUnitName("系统消息"); } }); return ywConditionList; } @Override public List selectYwAuditingListByYwId(String workOrderNo) { return new LambdaQueryChainWrapper<>(workOrderAuditingRecordMapper) .eq(WorkOrderAuditingRecord::getWorkOrderNo, workOrderNo) .orderByAsc(WorkOrderAuditingRecord::getCreateTime) .list(); } @Override public Result ywResult(WorkOrderYWResultForm form) { return null; } @Override public Result checkResult(WorkOrderCheckResultForm form) { return null; } /** * 批量删除 * * @param ids * @return */ @Override public Result remove(List ids) { if (baseMapper.deleteBatchIds(ids) > 0) { return Result.ok("删除成功"); } return Result.error("删除失败"); } /** * id删除 * * @param id * @return */ @Override public Result removeById(String id) { WorkOrder workOrder = baseMapper.selectById(id); String workOrderNo = workOrder.getWorkOrderNo(); workOrderDistributeRecordMapper.deleteByWorkOrder(workOrderNo); if (baseMapper.deleteById(id) > 0) { return Result.ok("删除成功"); } return Result.error("删除失败"); } /** * 分页查询 * * @param query * @return */ @Override public Result page(WorkOrderQuery query) { IPage page = PageUtil.getPage(query, WorkOrderVO.class); if(query.getUnitId()==null) { query.setUnitId(SecurityUtils.getUnitId()); } if(query.getStart()!=null) query.setStart(DateUtils.getDayStart(query.getStart())); if(query.getEnd()!=null) query.setEnd(DateUtils.getDayEnd(query.getEnd())); baseMapper.page(page, query); if (!CollectionUtils.isEmpty(page.getRecords())) { page.getRecords().stream().forEach(item -> { if (StringUtils.hasText(item.getErrorType())) { List errorTypeList = new ArrayList<>(List.of(item.getErrorType().split(","))); int index = errorTypeList.indexOf(query.getErrorTypeLabel()); if (index != -1) { // 保存要移动的元素 String firstOccurrence = errorTypeList.get(index); // 从列表中移除该元素(第一次出现的位置) errorTypeList.remove(index); // 将该元素添加到列表的首位 errorTypeList.add(0, firstOccurrence); } // 设置更新后的列表 item.setErrorTypeList(errorTypeList); } if (StringUtils.hasText(item.getImgListStr())) { item.setImgList(List.of(item.getImgListStr().split(","))); } }); } return Result.ok().data(page.getRecords()).total(page.getTotal()); } @Override public Result distributePage(DistributeWorkOrderQuery query) { IPage page = PageUtil.getPage(query, WorkOrderVO.class); if(query.getStart()!=null) query.setStart(DateUtils.getDayStart(query.getStart())); if(query.getEnd()!=null) query.setEnd(DateUtils.getDayEnd(query.getEnd())); baseMapper.distributePage(page, query); return Result.ok().data(page).total(page.getTotal()); } @Override @Transactional public Result distributeFast(DistributeWorkOrderVO data) { // 获取当前时间 LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault()); data.setEnd(now); switch (data.getFastWay()) { case LAST_HOUR: data.setStart(now.minusHours(1)); break; case LAST_TWO_HOUR: data.setStart(now.minusHours(2)); break; case LAST_DAY: data.setStart(now.minusDays(1)); break; default: break; } if (Objects.isNull(data.getStart())) { throw new RuntimeException("无法生成快速下发的时间范围,请选择正确的快速下发方式"); } // 查询符合条件的工单 List list = new LambdaQueryChainWrapper<>(baseMapper) .select(WorkOrder::getSerialNumber, WorkOrder::getWorkOrderNo) .eq(WorkOrder::getStatus, WorkOrderStatusEnum.WAIT_DISTRIBUTE) .eq(Objects.nonNull(data.getUnitId()), WorkOrder::getUnitId, data.getUnitId()) .in(WorkOrder::getErrorType, data.getErrorType()) .between(WorkOrder::getCreateTime, data.getStart(), data.getEnd()) .orderByDesc(WorkOrder::getCreateTime) .last("limit " + data.getFastNumLimit()) .list(); List workOrderNoList = list.stream().map(WorkOrder::getWorkOrderNo).toList(); List serialNumberList = list.stream().map(WorkOrder::getSerialNumber).toList(); if (workOrderNoList.isEmpty()) { return Result.error("没有符合条件的工单"); } if (!getDistributeLock()) { return Result.error("此刻有人下发中,为避免冲突,请稍后重试"); } try { new LambdaUpdateChainWrapper<>(baseMapper) .set(WorkOrder::getStatus, WorkOrderStatusEnum.DISTRIBUTED) .in(WorkOrder::getWorkOrderNo, workOrderNoList) .update(); addDistributeRecord(workOrderNoList, WorkOrderDistributeWayEnum.FAST_DISTRIBUTE); // 同步点位状态 ywPointService.updateRecovery(serialNumberList, 1); return Result.ok("成功下发" + workOrderNoList.size() + "条工单"); } catch (Exception e) { return Result.error("操作失败"); } finally { distributeUnLock(); } } @Override @Transactional public Result selectedIdsDistribute(DistributeWorkOrderQuery query) { WorkOrderDistributeWayEnum distributeWayEnum = WorkOrderDistributeWayEnum.SELECTED_DISTRIBUTE; if (!getDistributeLock()) { return Result.error("此刻有人下发中,为避免冲突,请稍后重试"); } try { if (query.getWorkOrderNOList().isEmpty()) { query.setWorkOrderNOList(new LambdaQueryChainWrapper<>(baseMapper) .eq(WorkOrder::getStatus, WorkOrderStatusEnum.WAIT_DISTRIBUTE) .eq(Objects.nonNull(query.getUnitId()), WorkOrder::getUnitId, query.getUnitId()) .select(WorkOrder::getWorkOrderNo) .list() .stream() .map(WorkOrder::getWorkOrderNo) .collect(Collectors.toList())); distributeWayEnum = WorkOrderDistributeWayEnum.ALL_DISTRIBUTE; } if (query.getWorkOrderNOList().isEmpty()) { return Result.error("没有工单待下发"); } new LambdaUpdateChainWrapper<>(baseMapper) .set(WorkOrder::getStatus, WorkOrderStatusEnum.DISTRIBUTED) .in(WorkOrder::getWorkOrderNo, query.getWorkOrderNOList()) .update(); addDistributeRecord(query.getWorkOrderNOList(), distributeWayEnum); // 同步点位状态 List serialNumberList = new LambdaQueryChainWrapper<>(baseMapper).select(WorkOrder::getSerialNumber).in(WorkOrder::getWorkOrderNo, query.getWorkOrderNOList()).list().stream().map(WorkOrder::getSerialNumber).toList(); ywPointService.updateRecovery(serialNumberList, 1); return Result.ok("成功下发" + query.getWorkOrderNOList().size() + "条工单"); } catch (Exception e) { System.out.println(e.getMessage()); return Result.error("操作失败"); } finally { distributeUnLock(); } } /** * 申请工单下发锁 * * @return 工单下发锁申请结果 */ public synchronized Boolean getDistributeLock() { if (Objects.isNull(redisCache.getCacheObject(DISTRIBUTE_LOCK_KEY))) { redisCache.setCacheObject(DISTRIBUTE_LOCK_KEY, "1", 30, TimeUnit.SECONDS); return true; } else { return false; } } /** * 工单下发锁释放 */ public synchronized void distributeUnLock() { redisCache.deleteObject(DISTRIBUTE_LOCK_KEY); } /** * 添加工单下发记录 * * @param workOrderNoList 工单id */ private void addDistributeRecord(List workOrderNoList, WorkOrderDistributeWayEnum distributeWay) { LoginUser loginUser = SecurityUtils.getLoginUser(); workOrderDistributeRecordMapper.insertBatch( workOrderNoList.stream() .map(no -> new WorkOrderDistributeRecord(no, loginUser.getUserId(), distributeWay)) .toList() ); } /** * 根据id查找 * * @param id * @return */ @Override public Result detail(String id) { WorkOrder entity = baseMapper.selectById(id); Assert.notNull(entity, "记录不存在"); WorkOrderVO vo = WorkOrderVO.getVoByEntity(entity, null); return Result.ok().data(vo); } /** * 列表 * * @return */ @Override public Result all() { List entities = baseMapper.selectList(null); List vos = entities.stream() .map( entity -> WorkOrderVO.getVoByEntity(entity, null) ) .collect(Collectors.toList()); return Result.ok().data(vos); } @Override public Result screenWorkOrder(ScreenQuery query) { ScreenWorkOrderVO screen = baseMapper.screenWorkOrder(query); return Result.ok().data(screen); } @Override public Map home(HomeQuery monitorQuery) { Map dataMap = new HashMap<>(); Map data1 = new HashMap<>(); Map data2 = new HashMap<>(); Map data3 = new HashMap<>(); List> home = baseMapper.home(monitorQuery); if (ObjectUtils.isNotEmpty(home)) { for (Map map : home) { if (Objects.nonNull(map.get("dateType")) && StringUtils.hasText(map.get("dateType").toString())) { data1.put(map.get("dateType").toString(), map.get("num1")); data2.put(map.get("dateType").toString(), map.get("num2")); data3.put(map.get("dateType").toString(), map.get("num3")); } } dataMap.put("name", home.get(0).get("name")); } dataMap.put("complete", data1); dataMap.put("waiting", data2); dataMap.put("pending", data3); return dataMap; } @Override public WorkOrderTotalVO workOrderTotal(DashboardQuery dashboardQuery) { return baseMapper.workOrderTotal(dashboardQuery); } @Override public List workOrderRegion(DashboardQuery dashboardQuery) { //初始化所有区域数据 List vos = new ArrayList<>(); for (AreaDeptEnum value : AreaDeptEnum.values()) { WorkOrderRegionVO vo = new WorkOrderRegionVO(); vo.setArea(value.getName()); vo.setDoneNum(0); vo.setTodoNum(0); vos.add(vo); } List workOrderRegionVOS = baseMapper.workOrderRegion(dashboardQuery); for (WorkOrderRegionVO vo : vos) { for (WorkOrderRegionVO workOrder : workOrderRegionVOS) { //赋值 if (vo.getArea().equals(workOrder.getArea())) { BeanUtils.copyProperties(workOrder, vo); } } } return vos; } @Override public String getFrameImgByDevice(String deviceId, String channelId, String workOrderNo) throws Exception { String url = String.format(this.rtspServer + "/api/play/start/img/%s/%s", deviceId, channelId); log.info("访问路径{}",url); String result = HttpUtils.sendGet(url); log.info("拿到取流图片响应结果:" + result); WVPResult wvpResult = JSON.parseObject(result, WVPResult.class); if(wvpResult.getCode() == 0) { Object imgUrl = (Object) wvpResult.getData(); imgUrl = Objects.isNull(imgUrl) ? "" : imgUrl; log.info("拿到取流图片:" + imgUrl); return (String) imgUrl; } return null; // WVPResult wvpResult = JSON.parseObject(result, WVPResult.class); // String imgUrl = null; // if (wvpResult.getCode() == 0) { // JSONObject data = (JSONObject) wvpResult.getData(); // String rtspUrl = data.getString("fmp4"); // 取mp4地址 // if (StringUtils.hasText(rtspUrl)) { // System.out.println("目标地址:" + rtspUrl); // FFmpegFrameGrabber grabber = null; // try { // grabber = new FFmpegFrameGrabber(rtspUrl); //// grabber.setOption("rtsp_transport", "tcp"); // 使用tcp的方式,不然会丢包很严重 //// grabber.setVideoOption("probesize", "10000"); // 设置捕获分析的最大字节 // grabber.start(); // Frame frame = grabber.grabImage(); // 直接捕获一帧 // if (frame != null) { // System.out.println("成功捕获一帧"); // // 将Frame转换为Mat // OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat(); // Mat mat = converter.convertToMat(frame); // // imgUrl = workOrderNo + "_" + IdUtils.fastSimpleUUID() + ".png"; // // 生成图片路径 // String imgPath = PlatformConfig.getProfile() + "/" + imgUrl; // System.out.println("图片保存地址:" + imgPath); // imgUrl = Constants.RESOURCE_PREFIX + "/" + imgUrl; // // 保存图片 // opencv_imgcodecs.imwrite(imgPath, mat); // } else { // System.out.println("未捕获到帧"); // } // } catch (FrameGrabber.Exception e) { // e.printStackTrace(); // } finally { // if (grabber != null) { // try { // grabber.stop(); // 停止捕获 // } catch (FrameGrabber.Exception e) { // e.printStackTrace(); // } // } // } // } // } else { // System.out.println("请求失败,错误码:" + wvpResult.getCode() + "--" + wvpResult.getMsg()); // } // System.out.println("图片URL:" + imgUrl); // return imgUrl; } @Override public void saveFrameImgByDevice (String deviceId, String channelId, String workOrderNo){ String url = String.format(this.rtspServer + "/api/play/start/%s/%s", deviceId, channelId); String result = HttpUtils.sendGet(url); WVPResult wvpResult = JSON.parseObject(result, WVPResult.class); String imgUrl = null; if (wvpResult.getCode() == 0) { JSONObject data = (JSONObject) wvpResult.getData(); String rtspUrl = data.getString("rtsp"); // 取mp4地址 if (StringUtils.hasText(rtspUrl)) { System.out.println("目标地址:" + rtspUrl); FFmpegFrameGrabber grabber = null; try { grabber = FFmpegFrameGrabber.createDefault(rtspUrl); //设置10s超时 grabber.setTimeout(10000); grabber.start(); Frame frame = grabber.grabImage(); // 直接捕获一帧 if (frame != null) { System.out.println("成功捕获一帧"); // 将Frame转换为Mat OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat(); Mat mat = converter.convertToMat(frame); imgUrl = workOrderNo + "_" + IdUtils.fastSimpleUUID() + ".png"; // 生成图片路径 String imgPath = PlatformConfig.getProfile() + "/" + imgUrl; System.out.println("图片保存地址:" + imgPath); imgUrl = Constants.RESOURCE_PREFIX + "/" + imgUrl; // 保存图片 opencv_imgcodecs.imwrite(imgPath, mat); } else { System.out.println("未捕获到帧"); } } catch (FrameGrabber.Exception e) { e.printStackTrace(); } finally { if (grabber != null) { try { grabber.stop(); // 停止捕获 } catch (FrameGrabber.Exception e) { e.printStackTrace(); } // 通常不需要调用release(),因为stop()会处理资源释放 // grabber.release(); // 释放资源 } } } } else { System.out.println("请求失败,错误码:" + wvpResult.getCode()); } System.out.println("图片URL:" + imgUrl); if (StringUtils.hasText(imgUrl)) { WorkOrderCheckImg img = new WorkOrderCheckImg(); img.setWorkOrderNo(workOrderNo); img.setImgUrl(imgUrl); img.setCreateTime(new Date()); workOrderCheckImgMapper.insert(img); } } @Override public List hasErrorWorkOrderList (Date start, Date end){ List list = baseMapper.hasErrorWorkOrderList(start, end); return list; } @Override public void updateImgById (Integer workOrderId, String imgPath){ new LambdaUpdateChainWrapper<>(baseMapper) .eq(WorkOrder::getId, workOrderId) .set(WorkOrder::getYwCheckResult, imgPath) .update(); } @Override public Result processImg (String workOrderNo){ WorkOrder workOrder = new LambdaQueryChainWrapper<>(baseMapper) .eq(WorkOrder::getWorkOrderNo, workOrderNo) .one(); if (Objects.isNull(workOrder)) { throw new RuntimeException("此工单不存在"); } // 运维记录 List workOrderYwConditionRecordVOS = this.selectYwConditionByYwId(workOrderNo); // 审核记录 List workOrderAuditingRecords = this.selectYwAuditingListByYwId(workOrderNo); WorkOrderProcessVO process = new WorkOrderProcessVO(); process.setYwList(workOrderYwConditionRecordVOS); process.setAuditingList(workOrderAuditingRecords); // 查询点位事前事后最新的一条数据是否审核通过 ReportAuditingRecordVO beforeRecord = ywPointService.getReportResult(workOrder.getSerialNumber(), "事前报备"); ReportAuditingRecordVO afterRecord = ywPointService.getReportResult(workOrder.getSerialNumber(), "事后报备"); Date now = new Date(); if (Objects.nonNull(beforeRecord)) { if (now.before(beforeRecord.getBeginCreateTime())) { process.setBeforeReportMsg("事前报备已失效"); } else if (now.after(beforeRecord.getEndCreateTime())) { process.setBeforeReportMsg("事前报备未生效"); } else { process.setBeforeReportMsg("已事前报备"); } } if (Objects.nonNull(afterRecord)) { if (now.before(afterRecord.getBeginCreateTime())) { process.setAfterReportMsg("事后报备已失效"); } else if (now.after(afterRecord.getEndCreateTime())) { process.setAfterReportMsg("事后报备未生效"); } else { process.setAfterReportMsg("已事后报备"); } } return Result.ok().data(process); } @Override public Result detailByNo (String workOrderNo){ WorkOrderDetailVO workOrder = baseMapper.detailByNo(workOrderNo); // 是否报备 boolean hasReport = new LambdaQueryChainWrapper<>(reportMapper) .eq(Report::getSerialNumber, workOrder.getSerialNumber()) .exists(); workOrder.setHasReport(hasReport); // 故障类型 List errorList = workOrderErrorTypeService.getBaseMapper().getErrorList(workOrder.getWorkOrderNo()); List errList = errorList.stream().map(SysDictData::getDictLabel).collect(Collectors.toList()); workOrder.setErrorTypeList(errList); // 检测图片 List imgList = new LambdaQueryChainWrapper<>(workOrderCheckImgMapper) .eq(WorkOrderCheckImg::getWorkOrderNo, workOrderNo) .orderByDesc(WorkOrderCheckImg::getCreateTime) .last("limit 20") .list(); workOrder.setImgList(imgList); return Result.ok().data(workOrder); } @Override public List export (WorkOrderExportQuery query){ if(query.getUnitId()==null) { query.setUnitId(SecurityUtils.getUnitId()); } if (query.getStart() != null) query.setStart(query.getStart() + " 00:00:00"); if (query.getEnd() != null) query.setEnd(query.getEnd() + " 23:59:59"); return baseMapper.export(query); } /** * 工单白名单列表 * * @param query 查询 * @return {@link List }<{@link WorkOrderWhite }> * @author */ @Override public Result selectWorkOrderWhiteList(WorkOrderWhiteQuery query) { IPage page = PageUtil.getPage(query, WorkOrderWhite.class); workOrderWhiteMapper.page(page, query); return Result.ok().data(page.getRecords()).total(page.getTotal()); } /** * 添加工单白名单 * * @param workOrderWhite 白色工单 * @return {@link Result } * @author */ @Override public Result addWorkOrderWhite(WorkOrderWhite workOrderWhite) { // 通过设备编码查询设备信息 YwPoint ywPoint = ywPointService.selectBySerialNumber(workOrderWhite.getSerialNumber()); // 新增白名单设备记录 if (ywPoint == null){ return Result.error("设备不存在"); } // 检查是否已经存在该白名单 WorkOrderWhite flag = workOrderWhiteMapper.selectBySerialNumber(workOrderWhite.getSerialNumber()); if (flag != null) { return Result.error("该设备已存在白名单"); } else { workOrderWhite.setPointName(ywPoint.getPointName()); workOrderWhite.setCreateBy(SecurityUtils.getUsername()); workOrderWhiteMapper.insert(workOrderWhite); return Result.ok(); } } /** * 批量删除工单白名单 * * @param ids ids * @author */ @Override public Result batchDeleteWorkOrderWhite(List ids) { workOrderWhiteMapper.batchDelete(ids); return Result.ok(); } /** * 批量导入白名单 * * @param dataList 数据列表 * @author */ @Override public void batchImportWhite(List dataList) { // 获得所有需要新增的白名单对象 List whitelist = dataList.stream().map(pointExport -> new WorkOrderWhite(pointExport.getSerialNumber(), pointExport.getPointName(), SecurityUtils.getUsername())) .collect(Collectors.toList()); // 获得所有已存在的白名单设备编码 List serialNumbers = workOrderWhiteMapper.selectList().stream().map(WorkOrderWhite::getSerialNumber).collect(Collectors.toList()); // 筛选出新增、修改的白名单对象 List updateWhiteList = whitelist.stream().filter(white -> serialNumbers.contains(white.getSerialNumber())).collect(Collectors.toList()); List insertWhiteList = whitelist.stream().filter(white ->!serialNumbers.contains(white.getSerialNumber())).collect(Collectors.toList()); // 新增/修改白名单设备记录 updateWhiteList.stream().forEach(white -> workOrderWhiteMapper.updateBySerialNumber(white)); insertWhiteList.stream().forEach(white -> workOrderWhiteMapper.insert(white)); } /** * 检测工单按钮 * @param workOrderNo * @return */ @Override public Result checkImage(String workOrderNo,String serialNumber) { // 查出国标设备,就一条数据 List gbDevices = new LambdaQueryChainWrapper<>(deviceInfoMapper) .orderByDesc(DeviceInfo::getUpdateTime) .last("limit 1") .list(); if (!CollectionUtils.isEmpty(gbDevices)) { try { String imgUrl = this.getFrameImgByDevice(gbDevices.get(0).getDeviceId(), serialNumber,workOrderNo); if (StringUtils.hasText(imgUrl)) { WorkOrderCheckImg img = new WorkOrderCheckImg(); img.setWorkOrderNo(workOrderNo); img.setImgUrl(imgUrl); img.setCreateTime(new Date()); workOrderCheckImgMapper.insert(img); } return Result.ok().data(imgUrl); } catch (Exception e) { e.printStackTrace(); } } return Result.ok(); } @Override @Transactional(rollbackFor = Exception.class) public Result batchDeleteWorkOrder(List workOrderNos) { if(!CollectionUtils.isEmpty(workOrderNos)) { //删除工单审核记录 workOrderAuditingRecordMapper.delete(new QueryWrapper().in("work_order_no", workOrderNos)); //删除工单图片记录 workOrderCheckImgMapper.delete(new QueryWrapper().in("work_order_no", workOrderNos)); //删除工单下发记录 workOrderDistributeRecordMapper.delete(new QueryWrapper().in("work_order_no", workOrderNos)); //删除工单故障类型 workOrderErrorTypeMapper.delete(new QueryWrapper().in("work_order_no", workOrderNos)); //删除工单情况记录 workOrderYwConditionRecordMapper.delete(new QueryWrapper().in("work_order_no", workOrderNos)); //删除工单 this.baseMapper.delete(new QueryWrapper().in("work_order_no", workOrderNos)); } return Result.ok(); } @Override public Result errorAll() { List results = new ArrayList<>(); CascadeOption video = new CascadeOption(); video.setLabel("视频"); video.setValue("VIDEO"); List videoErr = ErrorType.getErrorTypesByCategory("VIDEO"); List videoErr1 = ErrorType.getErrorTypesByCategory("COMMON"); videoErr.addAll(videoErr1); List videoChildren = videoErr.stream().map(item -> CascadeOption.builder() .value(item.getValue()) .label(item.getDesc()) .build()).collect(Collectors.toList()); video.setChildren(videoChildren); CascadeOption car = new CascadeOption(); car.setLabel("车辆"); car.setValue("CAR"); List carErr = ErrorType.getErrorTypesByCategory("CAR"); List carErr1 = ErrorType.getErrorTypesByCategory("COMMON"); List carErr2 = ErrorType.getErrorTypesByCategory("CARORFACE"); carErr.addAll(carErr1); carErr.addAll(carErr2); List carChildren = carErr.stream().map(item -> CascadeOption.builder() .value(item.getValue()) .label(item.getDesc()) .build()).collect(Collectors.toList()); car.setChildren(carChildren); CascadeOption face = new CascadeOption(); face.setLabel("人脸"); face.setValue("FACE"); List faceErr = ErrorType.getErrorTypesByCategory("FACE"); List faceErr1 = ErrorType.getErrorTypesByCategory("COMMON"); List faceErr2 = ErrorType.getErrorTypesByCategory("CARORFACE"); faceErr.addAll(faceErr1); faceErr.addAll(faceErr2); List faceChildren = faceErr.stream().map(item -> CascadeOption.builder() .value(item.getValue()) .label(item.getDesc()) .build()).collect(Collectors.toList()); face.setChildren(faceChildren); results.add(video); results.add(car); results.add(face); return Result.ok().data(results); } }