ycl-server/src/main/java/com/ycl/platform/service/impl/WorkOrderServiceImpl.java
@@ -1,19 +1,20 @@
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.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.github.pagehelper.util.StringUtil;
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.form.*;
import com.ycl.platform.domain.query.*;
import com.ycl.platform.domain.result.SYS.TMonitorResult;
import com.ycl.platform.domain.vo.*;
import com.ycl.platform.domain.vo.screen.ScreenWorkOrderVO;
import com.ycl.platform.domain.vo.screen.WorkOrderRegionVO;
@@ -23,44 +24,54 @@
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.SysDept;
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.system.service.ISysDeptService;
import com.ycl.task.DeleteWorkOrderImgTask;
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.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.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.OpenCVFrameConverter;
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.data.redis.core.RedisTemplate;
import org.springframework.context.ApplicationContext;
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 utils.file.FileUtils;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -75,15 +86,22 @@
public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements WorkOrderService {
    private final YwPointService ywPointService;
    private final YwPointMapper ywPointMapper;
    private final TMonitorMapper monitorMapper;
    private final DynamicColumnMapper dynamicColumnMapper;
    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 WorkOrderWhiteMapper workOrderWhiteMapper;
    private final SysConfigMapper configMapper;
    private final ReportMapper reportMapper;
    private final WorkOrderCheckImgMapper workOrderCheckImgMapper;
    private final DeviceInfoMapper deviceInfoMapper;
    private final ApplicationContext applicationContext;
    @Value("${rtsp.server:http://127.0.0.1:7788}")
    private String rtspServer;
@@ -92,17 +110,23 @@
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private DeleteWorkOrderImgTask deleteWorkOrderImgTask;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized Boolean innerAddWorkOrder(List<WorkOrder> workOrderList) {
        //避免坑(事务加锁会失效、方法内部调用事务会失效)
        WorkOrderServiceImpl self = applicationContext.getBean(WorkOrderServiceImpl.class);
        return self.batchAddWorkOrder(workOrderList);
    }
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchAddWorkOrder(List<WorkOrder> workOrderList) {
        int total = workOrderList.size();
        workOrderList.stream().filter(item -> {
        //清理数据
        workOrderList = workOrderList.stream().filter(item -> {
            return StringUtils.hasText(item.getSerialNumber()) && Objects.nonNull(item.getStatus()) && !CollectionUtils.isEmpty(item.getErrorTypeList());
        });
        if (CollectionUtils.isEmpty(workOrderList)) {
            return Boolean.TRUE;
        }
        }).collect(Collectors.toList());
        // 根据国标码去重
        workOrderList = workOrderList.stream()
                .collect(Collectors.toMap(
@@ -110,6 +134,12 @@
                        p -> p,
                        (existing, replacement) -> existing // 冲突时保留第一个
                )).values().stream().collect(Collectors.toList());
        //检查白名单
        workOrderList = checkWhite(workOrderList);
        if (CollectionUtils.isEmpty(workOrderList)) {
            return Boolean.TRUE;
        }
        List<String> serialNumberList = workOrderList.stream().map(WorkOrder::getSerialNumber).collect(Collectors.toList());
        // 查出数据库中国标码对应的未完成的工单
        List<WorkOrder> inDatabaseWorkOrderList = baseMapper.getNotFinishedWorkOrders(serialNumberList);
@@ -151,18 +181,21 @@
                            willAddErrorType.add(workOrderErrorType);
                        }
                    }
                    databaseWorkOrder.setStatus(WorkOrderStatusEnum.DISTRIBUTED);
                    //避免代下发的工单直接变成下发
                    if (!databaseWorkOrder.getStatus().equals(WorkOrderStatusEnum.WAIT_DISTRIBUTE)) {
                        databaseWorkOrder.setStatus(WorkOrderStatusEnum.DISTRIBUTED);
                        // 同时新增一个运维处理信息,表明此工单被调整
                        WorkOrderYwConditionRecord ywRecord = new WorkOrderYwConditionRecord();
                        ywRecord.setWorkOrderNo(databaseWorkOrder.getWorkOrderNo());
                        ywRecord.setCommitUser(1);
                        ywRecord.setYwCondition("故障类型更新,工单状态调整为待处理");
                        ywRecord.setCreateTime(new Date());
                        ywRecord.setSysMsg(Boolean.TRUE);
                        willAddMsg.add(ywRecord);
                    }
                    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());
@@ -189,9 +222,13 @@
            return Boolean.TRUE;
        }
        List<String> willAddSerialNumber = waitAddList.stream().map(WorkOrder::getSerialNumber).collect(Collectors.toList());
        //只生成考核设备、且有运维单位的工单
        List<YwPoint> pointList = new LambdaQueryChainWrapper<>(ywPointService.getBaseMapper())
                .select(YwPoint::getUnitId, YwPoint::getSerialNumber, YwPoint::getImportantTag, YwPoint::getImportantTag, YwPoint::getProvinceTag, YwPoint::getImportantCommandImageTag)
                .select(YwPoint::getUnitId, YwPoint::getSerialNumber, YwPoint::getImportantTag, YwPoint::getImportantTag, YwPoint::getImportantCommandImageTag)
                .in(YwPoint::getSerialNumber, willAddSerialNumber)
                .eq(YwPoint::getExamineStatus, Boolean.TRUE)
                .isNotNull(YwPoint::getUnitId)
                .list();
        Map<String, YwPoint> pointMapping = pointList.stream().collect(Collectors.toMap(YwPoint::getSerialNumber, point -> point));
        // 查出重点点位、普通点位的处理时间
@@ -201,6 +238,25 @@
        List<WorkOrder> 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 {
@@ -224,23 +280,11 @@
            log.error("获取锁异常");
            return Boolean.FALSE;
        }
        //记录工单数,补充工单号
        for (WorkOrder workOrder : waitAddList) {
            YwPoint point = pointMapping.get(workOrder.getSerialNumber());
            if (Objects.isNull(point)) {
                notAddList.add(workOrder);
                continue;
            }
            //数字前面补0
            workOrderNum++;
            workOrder.setWorkOrderNo(IdUtils.workOrderNO(now, String.format("%05d", workOrderNum)));
            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()));
            }
            // 保存错误类型
            for (String errorType : workOrder.getErrorTypeList()) {
                WorkOrderErrorType workOrderErrorType = new WorkOrderErrorType();
@@ -250,12 +294,9 @@
                workOrderErrorType.setErrorName(errorType);
                waitAddErrorTypeList.add(workOrderErrorType);
            }
            log.info("即将生成的工单:{}", workOrder);
        }
        waitAddList.removeAll(notAddList);
        if (CollectionUtils.isEmpty(waitAddList)) {
            return Boolean.TRUE;
        }
        //记录工单数
        redisCache.setCacheObject(RedisConstant.WORKORDER_NUM, workOrderNum);
        redisCache.releaseLock(RedisConstant.WORKORDER_NUM_LOCK, requestId);
        // 保存工单和故障类型
@@ -284,6 +325,7 @@
        return Boolean.TRUE;
    }
    /**
     * 添加
     *
@@ -292,36 +334,111 @@
     */
    @Override
    public Result add(WorkOrderForm form) {
        //查redis今日工单数量
        int workOrderNum = 1;
        //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 ? 1 : (Integer) redisNum;
            workOrderNum++;
            redisCache.setCacheObject(RedisConstant.WORKORDER_NUM, workOrderNum);
            redisCache.releaseLock(RedisConstant.WORKORDER_NUM_LOCK, requestId);
        } else {
            return Result.error("工单正在添加,请稍后再试");
        //检查白名单
        List<WorkOrder> data = new ArrayList<>();
        WorkOrder workOrder = WorkOrderForm.getEntityByForm(form, null);
        data.add(workOrder);
        //校验白名单并排除在白名单的故障类型
        data = checkWhite(data);
        if (CollectionUtils.isEmpty(data)) {
            return Result.error("该设备的故障类型在白名单中,不能添加工单");
        }
        WorkOrder entity = WorkOrderForm.getEntityByForm(form, null);
        entity.setCreateTime(DateUtils.getNowDate());
        entity.setStatus(WorkOrderStatusEnum.WAIT_DISTRIBUTE);
        WorkOrder entity = data.get(0);
        String serialNumber = entity.getSerialNumber();
        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<WorkOrderErrorType> workOrderErrorTypes = form.getErrorType().stream().map(errorType -> new WorkOrderErrorType(entity.getWorkOrderNo(), errorType)).toList();
        workOrderErrorTypeService.getBaseMapper().insertWorkOrderErrorTypeList(workOrderErrorTypes);
        if (baseMapper.insert(entity) > 0) {
        List<WorkOrder> inDatabaseWorkOrderList = baseMapper.getNotFinishedWorkOrders(Arrays.asList(serialNumber));
        inDatabaseWorkOrderList.stream().forEach(item -> {
            if (StringUtils.hasText(item.getErrorType())) {
                item.setErrorTypeList(List.of(item.getErrorType().split(",")));
            } else {
                item.setErrorTypeList(new ArrayList<>(1));
            }
        });
        // 因故障类型不一致而要更新状态的工单
        List<WorkOrder> willUpdateStatusWorkOrderList = new ArrayList<>(48);
        // 更改工单类型而要增加的系统运维处理信息
        List<WorkOrderYwConditionRecord> willAddMsg = new ArrayList<>(48);
        // 即将要添加的错误类型
        List<WorkOrderErrorType> willAddErrorType = new ArrayList<>(96);
        if (!CollectionUtils.isEmpty(inDatabaseWorkOrderList)) {
            WorkOrder workOrderInDB = inDatabaseWorkOrderList.get(0);
            List<String> errorNameList = workOrderInDB.getErrorTypeList();
            List<String> errorTypes = entity.getErrorTypeList();
            if (errorNameList.containsAll(errorTypes)) {
                return Result.error("已有同种故障类型工单");
            } else {
                for (String errorType : errorTypes) {
                    if (!errorNameList.contains(errorType)) {
                        // 错误类型不一样,就新增一个错误类型,并且重置工单状态为待处理
                        WorkOrderErrorType workOrderErrorType = new WorkOrderErrorType();
                        workOrderErrorType.setWorkOrderNo(workOrderInDB.getWorkOrderNo());
                        workOrderErrorType.setCreateTime(now);
                        workOrderErrorType.setUpdateTime(now);
                        workOrderErrorType.setErrorName(errorType);
                        willAddErrorType.add(workOrderErrorType);
                    }
                }
                //避免代下发的工单直接变成下发
                if (!workOrderInDB.getStatus().equals(WorkOrderStatusEnum.WAIT_DISTRIBUTE)) {
                    workOrderInDB.setStatus(WorkOrderStatusEnum.DISTRIBUTED);
                    // 同时新增一个运维处理信息,表明此工单被调整
                    WorkOrderYwConditionRecord ywRecord = new WorkOrderYwConditionRecord();
                    ywRecord.setWorkOrderNo(workOrderInDB.getWorkOrderNo());
                    ywRecord.setCommitUser(1);
                    ywRecord.setYwCondition("故障类型更新,工单状态调整为待处理");
                    ywRecord.setCreateTime(new Date());
                    ywRecord.setSysMsg(Boolean.TRUE);
                    willAddMsg.add(ywRecord);
                }
                workOrderInDB.setUpdateTime(now);
                willUpdateStatusWorkOrderList.add(workOrderInDB);
                //修改故障类型
                if (willAddErrorType.size() > 0) {
                    workOrderErrorTypeService.getBaseMapper().addMany(willAddErrorType);
                }
                //增加运维情况
                if (willAddMsg.size() > 0) {
                    workOrderYwConditionRecordMapper.insertMany(willAddMsg);
                }
                //更改工单状态
                if (willUpdateStatusWorkOrderList.size() > 0) {
                    this.baseMapper.updateMany(willUpdateStatusWorkOrderList);
                }
            }
            return Result.ok("添加成功");
        } else {
            //数据库没有正在进行的工单
            //查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("工单正在添加,请稍后再试");
            }
            entity.setCreateTime(DateUtils.getNowDate());
            entity.setStatus(WorkOrderStatusEnum.WAIT_DISTRIBUTE);
            entity.setCreateTime(now);
            entity.setUpdateTime(now);
            entity.setWorkOrderNo(IdUtils.workOrderNO(now, String.format("%05d", workOrderNum)));
            entity.setErrorType(String.join(",", form.getErrorType()));
            List<WorkOrderErrorType> workOrderErrorTypes = form.getErrorType().stream().map(errorType -> new WorkOrderErrorType(entity.getWorkOrderNo(), errorType)).toList();
            workOrderErrorTypeService.getBaseMapper().insertWorkOrderErrorTypeList(workOrderErrorTypes);
            if (baseMapper.insert(entity) > 0) {
                return Result.ok("添加成功");
            } else {
                return Result.error("添加失败");
            }
        }
        return Result.error("添加失败");
    }
    /**
     * 修改
@@ -365,24 +482,35 @@
                UrgentLevelEnum.WARNING,
                workOrder.getWorkOrderNo());
        notifyService.save(notify);
        List<WorkOrderVO> workOrderVOS = workOrderErrorTypeMapper.getErrorListByNumbers(Arrays.asList(workOrder.getWorkOrderNo()));
        // 同步点位状态
        if (form.getAuditingResult() == WorkOrderStatusEnum.AUDITING_SUCCESS) {
            ywPointService.updateRecovery(Collections.singletonList(workOrder.getSerialNumber()), 0);
            //如果工单为离线工单那么修改点位pingOnline为在线
            if (!CollectionUtils.isEmpty(workOrderVOS)) {
                List<String> ips = new ArrayList<>();
                for (WorkOrderVO workOrderVO : workOrderVOS) {
                    if(ErrorType.DEVICE_OFFLINE.getValue().equals(workOrderVO.getErrorType())){
                        ips.add(workOrderVO.getIp());
                    }
                }
                if(!CollectionUtils.isEmpty(ips)) {
                    monitorMapper.batchUpdateOnline(ips, new Date(), ApiConstants.UY_OnlineSite_Online);
                }
            }
        }
        return Result.ok("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result batchAuditing(WorkOrderBatchAuditingForm form) {
        // 根据故障类型获取列表
        List<WorkOrder> list = new LambdaQueryChainWrapper<>(baseMapper)
                .in(WorkOrder::getStatus, WorkOrderStatusEnum.YW_HANDLE.getValue())
                .in(WorkOrder::getErrorType, form.getErrorTypes())
                .select(WorkOrder::getId, WorkOrder::getUnitId, WorkOrder::getWorkOrderNo, WorkOrder::getSerialNumber)
                .list();
        //根据工单编号获取工单
        List<WorkOrder> list = baseMapper.selectByNos(form.getWorkOrderNumbers());
        if (list.isEmpty()) {
            return Result.error("没有工单可以审核");
            return Result.error("没有待审核工单");
        }
        List<String> workOrderNoList = list.stream().map(WorkOrder::getWorkOrderNo).collect(Collectors.toList());
        List<String> serialNumbers = list.stream().map(WorkOrder::getSerialNumber).toList();
@@ -412,17 +540,37 @@
                    workOrder.getWorkOrderNo());
            notifies.add(notify);
        }
        notifyService.saveBatch(notifies);
        // 同步点位状态
        if (form.getAuditingResult() == WorkOrderStatusEnum.AUDITING_SUCCESS) {
            ywPointService.updateRecovery(serialNumbers, 0);
            //筛选出离线工单ip并修改在线状态
            if (!CollectionUtils.isEmpty(serialNumbers)) {
                Map<String, List<String>> errorTypes = workOrderErrorTypeMapper.getErrorListByNumbers(workOrderNoList).stream().collect(Collectors.groupingBy(
                        WorkOrderVO::getIp,
                        Collectors.mapping(
                                WorkOrderVO::getErrorType,
                                Collectors.toList()
                        )
                ));
                List<String> ips = new ArrayList<>();
                errorTypes.forEach((key, value) -> {
                    if (!CollectionUtils.isEmpty(value) && value.contains(ErrorType.DEVICE_OFFLINE.getValue())) {
                        ips.add(key);
                    }
                });
                if(!CollectionUtils.isEmpty(ips)) {
                    monitorMapper.batchUpdateOnline(ips, new Date(), ApiConstants.UY_OnlineSite_Online);
                }
            }
        }
        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("工单不存在");
@@ -439,13 +587,108 @@
        workOrderYwConditionRecord.setYwProofMaterials(form.getYwProofMaterials());
        workOrderYwConditionRecord.setSysMsg(Boolean.FALSE);
        workOrderYwConditionRecordMapper.insert(workOrderYwConditionRecord);
        //异步获取图片
        WorkOrderServiceImpl self = applicationContext.getBean(WorkOrderServiceImpl.class);
        self.getImage(workOrder);
        return Result.ok("操作成功");
    }
    @Override
    public Result ywConditionList(WorkOrderYWConditionsForm form) {
        List<WorkOrder> workOrderList = new LambdaQueryChainWrapper<>(baseMapper)
                .in(WorkOrder::getWorkOrderNo,form.getWorkOrderNumbers())
                .list();
        System.out.println("--------------");
        System.out.println(workOrderList);
        if (CollectionUtils.isEmpty(workOrderList)) {
            throw new ServiceException("工单不存在");
        }
        // 工单状态
        for (WorkOrder workOrder : workOrderList) {
            workOrder.setStatus(WorkOrderStatusEnum.YW_HANDLE);
            workOrder.setYwHandleTime(LocalDateTime.now());
        }
        baseMapper.updateMany(workOrderList);
        // 添加一条运维情况记录
        for (WorkOrder workOrder : workOrderList) {
            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<DeviceInfo> 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());
            //保留最新的5张
            workOrderCheckImgMapper.insert(img);
        }
    }
    /**
     * 清理工单图片
     * */
    public void clearWorkOrderImg(String workOrderNo) {
        //判断该工单的图片是否已经大于5张了
        //按照创建日期降序排列
        if (StringUtil.isEmpty(workOrderNo)) {
            return;
        }
        List<WorkOrderCheckImg> workOrderList = new LambdaQueryChainWrapper<WorkOrderCheckImg>(workOrderCheckImgMapper)
                .select()
                .eq(WorkOrderCheckImg::getWorkOrderNo, workOrderNo)
                .orderByDesc(WorkOrderCheckImg::getCreateTime)
                .list();
        if (workOrderList.size()>=5){
            List<String> imgList = workOrderList.subList(4,workOrderList.size()).stream().map(WorkOrderCheckImg::getImgUrl).collect(Collectors.toList());
            List<Integer> idList = workOrderList.subList(4, workOrderList.size()).stream().map(WorkOrderCheckImg::getId).collect(Collectors.toList());
            //删除照片 ,并且物理删除服务器内对应照片的名字
//                /profile/2024-11-18_1d8e6d37-a4d0-4bba-8473-ff4e50eda83d.png
            log.error("删除工单照片集合url:{}",imgList);
            log.error("删除工单存储数据集合id:{}",idList);
            for (String filePath: imgList){
                delImgFormClient(filePath);
            }
            workOrderCheckImgMapper.deleteBatchIds(idList);
        }
    }
    @Override
    public List<WorkOrderYwConditionRecordVO> selectYwConditionByYwId(String workOrderNo) {
        List<WorkOrderYwConditionRecordVO> ywConditionList = workOrderYwConditionRecordMapper.selectYwConditionByYwId(workOrderNo);
        ywConditionList.stream().forEach(item -> {
            if (item.getUserId().equals(1)) {
                item.setUnitName("管理员");
            }
            if (Objects.nonNull(item.getSysMsg()) && item.getSysMsg()) {
                item.setUnitName("系统消息");
            }
@@ -480,9 +723,29 @@
    @Override
    public Result remove(List<String> ids) {
        if (baseMapper.deleteBatchIds(ids) > 0) {
            //批量删除是否被用到?
            return Result.ok("删除成功");
        }
        return Result.error("删除失败");
    }
    /***
     * 通过url删除服务器内的图片
     * @param url
     */
    public void delImgFormClient(String url){
        //提取文件名  /profile/2024-11-17_79c02666-da1e-45ba-9f29-7523c848fa4a.png
        // 这部分2024-11-17_79c02666-da1e-45ba-9f29-7523c848fa4a.png
        String imgPath = FileUtils.getImgName(url);
        if (imgPath.isEmpty()){
            log.error("url错误:{}",imgPath);
        }else{
            //提取日期部分 组装目录 : /opt/zgyw/uploadPath  + /  + yyyy-MM-dd +
            String pathName = DeleteWorkOrderImgTask.imgSaveBasePath + "/" + FileUtils.extractDateFromString(url);
            log.error("删除工单图片集合{}",pathName+"/" + imgPath);
            FileUtils.deleteFile(pathName+"/" + imgPath);
        }
    }
    /**
@@ -493,12 +756,40 @@
     */
    @Override
    public Result removeById(String id) {
        WorkOrder workOrder = baseMapper.selectById(id);
        String workOrderNo = workOrder.getWorkOrderNo();
        //删除工单审核记录
        workOrderAuditingRecordMapper.delete(new QueryWrapper<WorkOrderAuditingRecord>().eq("work_order_no", workOrderNo));
        //需要物理删除服务器中的图片
        //查询到工单照片集合
        List<String> workOrderImg = workOrderCheckImgMapper
                .selectList(new QueryWrapper<WorkOrderCheckImg>().eq("work_order_no", workOrderNo))
                .stream().map(WorkOrderCheckImg::getImgUrl).collect(Collectors.toList());
        log.error("删除工单图片集合:{}",workOrderImg);
        log.error("删除路径:{}",PlatformConfig.getUploadPath());
        for (String filePath: workOrderImg){
            delImgFormClient(filePath);
        }
        //删除工单图片记录
        workOrderCheckImgMapper.delete(new QueryWrapper<WorkOrderCheckImg>().eq("work_order_no", workOrderNo));
        //删除工单下发记录
        workOrderDistributeRecordMapper.delete(new QueryWrapper<WorkOrderDistributeRecord>().eq("work_order_no", workOrderNo));
        //删除工单故障类型
        workOrderErrorTypeMapper.delete(new QueryWrapper<WorkOrderErrorType>().eq("work_order_no", workOrderNo));
        //删除工单情况记录
        workOrderYwConditionRecordMapper.delete(new QueryWrapper<WorkOrderYwConditionRecord>().eq("work_order_no", workOrderNo));
        if (baseMapper.deleteById(id) > 0) {
            return Result.ok("删除成功");
        }
        return Result.error("删除失败");
    }
    private final ISysDeptService deptService;
    /**
     * 分页查询
     *
@@ -507,15 +798,60 @@
     */
    @Override
    public Result page(WorkOrderQuery query) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysDept sysDept = null;
        Long deptId =null;
        if (loginUser.getDeptId() != null){
            deptId = loginUser.getDeptId();
            sysDept = deptService.selectDeptById(deptId);
        }
        //查询指定区数据
        // 登录账号的存在部门 不在部门内就不传值,默认查询全部
        if (sysDept !=null){
            //更具部门id获得对应枚举 部门code
            AreaDeptEnum areaDeptEnum = AreaDeptEnum.fromDept(Math.toIntExact(sysDept.getDeptId()));
            if (areaDeptEnum != null){
                query.setDept(areaDeptEnum.getCode());
            }
        }
        IPage<WorkOrderVO> page = PageUtil.getPage(query, WorkOrderVO.class);
        query.setUnitId(SecurityUtils.getUnitId());
        query.setStart(DateUtils.getDayStart(query.getStart()));
        query.setEnd(DateUtils.getDayEnd(query.getEnd()));
        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()));
        if (query.getErrorType() != null) {
            List<String> errorTypeList = new ArrayList<>();
            errorTypeList.add(query.getErrorType());
            if (ErrorType.ABNORMAL_PIC.getValue().equals(query.getErrorType())) {
                //List添加之前几种工单类型
                errorTypeList.add(ErrorType.SIGNAL_LOSS.getValue());
                errorTypeList.add(ErrorType.SCREEN_COLOR_DEVIATION.getValue());
                errorTypeList.add(ErrorType.SNOW_STORM.getValue());
                errorTypeList.add(ErrorType.STRIPE_INTERFERENCE.getValue());
                errorTypeList.add(ErrorType.SCREEN_OCCLUSION.getValue());
                errorTypeList.add(ErrorType.ABNORMAL_CLARITY.getValue());
            }
            query.setErrorTypeList(errorTypeList);
        }
        baseMapper.page(page, query);
        if (!CollectionUtils.isEmpty(page.getRecords())) {
            page.getRecords().stream().forEach(item -> {
                if (StringUtils.hasText(item.getErrorType())) {
                    item.setErrorTypeList(List.of(item.getErrorType().split(",")));
                    List<String> 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(",")));
@@ -528,8 +864,15 @@
    @Override
    public Result distributePage(DistributeWorkOrderQuery query) {
        IPage<WorkOrderVO> 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
    public List<WorkOrderVO> distributeList(){
        return  baseMapper.distributeList();
    }
    @Override
@@ -559,7 +902,7 @@
                .select(WorkOrder::getSerialNumber, WorkOrder::getWorkOrderNo)
                .eq(WorkOrder::getStatus, WorkOrderStatusEnum.WAIT_DISTRIBUTE)
                .eq(Objects.nonNull(data.getUnitId()), WorkOrder::getUnitId, data.getUnitId())
                .eq(WorkOrder::getErrorType, data.getErrorType())
                .in(WorkOrder::getErrorType, data.getErrorType())
                .between(WorkOrder::getCreateTime, data.getStart(), data.getEnd())
                .orderByDesc(WorkOrder::getCreateTime)
                .last("limit " + data.getFastNumLimit())
@@ -589,6 +932,9 @@
        }
    }
    @Override
    @Transactional
    public Result selectedIdsDistribute(DistributeWorkOrderQuery query) {
@@ -597,6 +943,7 @@
            return Result.error("此刻有人下发中,为避免冲突,请稍后重试");
        }
        try {
            //这是全部下发
            if (query.getWorkOrderNOList().isEmpty()) {
                query.setWorkOrderNOList(new LambdaQueryChainWrapper<>(baseMapper)
                        .eq(WorkOrder::getStatus, WorkOrderStatusEnum.WAIT_DISTRIBUTE)
@@ -621,6 +968,7 @@
            ywPointService.updateRecovery(serialNumberList, 1);
            return Result.ok("成功下发" + query.getWorkOrderNOList().size() + "条工单");
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return Result.error("操作失败");
        } finally {
            distributeUnLock();
@@ -752,58 +1100,66 @@
    @Override
    public synchronized String getFrameImgByDevice(String deviceId, String channelId, String workOrderNo) {
        String url = String.format(this.rtspServer + "/api/play/start/%s/%s", deviceId, channelId);
    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);
        String imgUrl = null;
        if (wvpResult.getCode() == 0) {
            JSONObject data = (JSONObject) wvpResult.getData();
            String rtspUrl = data.getString("mp4"); // 取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"); // 设置捕获分析的最大字节
                    //设置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();
                        }
                    }
                }
            }
        } else {
            System.out.println("请求失败,错误码:" + wvpResult.getCode() + "--" + wvpResult.getMsg());
            Object imgUrl = (Object) wvpResult.getData();
            imgUrl = Objects.isNull(imgUrl) ? "" : imgUrl;
            log.info("拿到取流图片:" + imgUrl);
            return (String) imgUrl;
        }
        System.out.println("图片URL:" + imgUrl);
        return 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
@@ -814,7 +1170,7 @@
        String imgUrl = null;
        if (wvpResult.getCode() == 0) {
            JSONObject data = (JSONObject) wvpResult.getData();
            String rtspUrl = data.getString("fmp4"); // 取mp4地址
            String rtspUrl = data.getString("rtsp"); // 取mp4地址
            if (StringUtils.hasText(rtspUrl)) {
                System.out.println("目标地址:" + rtspUrl);
                FFmpegFrameGrabber grabber = null;
@@ -925,6 +1281,11 @@
    @Override
    public Result detailByNo(String workOrderNo) {
        WorkOrderDetailVO workOrder = baseMapper.detailByNo(workOrderNo);
        //报空原因1. sql 中 inner join 表 t_work_order_distribute_record 中 无与该表关联的记录 wo.work_order_no = dr.work_order_no
        if(workOrder == null){
            workOrder = baseMapper.detailByNoButNoDistributeRecord(workOrderNo);
        }
        // 是否报备
        boolean hasReport = new LambdaQueryChainWrapper<>(reportMapper)
                .eq(Report::getSerialNumber, workOrder.getSerialNumber())
@@ -934,6 +1295,8 @@
        List<SysDictData> errorList = workOrderErrorTypeService.getBaseMapper().getErrorList(workOrder.getWorkOrderNo());
        List<String> errList = errorList.stream().map(SysDictData::getDictLabel).collect(Collectors.toList());
        workOrder.setErrorTypeList(errList);
        //详情时再次清理
        clearWorkOrderImg(workOrderNo);
        // 检测图片
        List<WorkOrderCheckImg> imgList = new LambdaQueryChainWrapper<>(workOrderCheckImgMapper)
                .eq(WorkOrderCheckImg::getWorkOrderNo, workOrderNo)
@@ -944,24 +1307,241 @@
        return Result.ok().data(workOrder);
    }
    @Autowired
    private WorkOrderMapper workOrderMapper;
    /**
     * 工单导出的逻辑
     * 标签:同一个ip标签合在一起,针对离线工单(ip相同就不会生成工单),方便运维处理
     *
     * @param query
     * @return
     */
    @Override
    public List<WorkOrderVO> export(WorkOrderExportQuery query) {
        query.setUnitId(SecurityUtils.getUnitId());
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (query.getStart() == null) {
            query.setStart(format.format(DateUtils.getDayStart(new Date())));
        } else {
            query.setStart(query.getStart() + " 00:00:00");
        if (query.getUnitId() == null) {
            query.setUnitId(SecurityUtils.getUnitId());
        }
        if (query.getEnd() == null) {
            query.setStart(format.format(DateUtils.getDayEnd(new Date())));
        } else {
            query.setEnd(query.getEnd() + " 23:59:59");
        if (query.getStart() != null) query.setStart(query.getStart() + " 00:00:00");
        if (query.getEnd() != null) query.setEnd(query.getEnd() + " 23:59:59");
        if (query.getErrorType() != null) {
            List<String> errorTypeList = new ArrayList<>();
            errorTypeList.add(query.getErrorType());
            if (ErrorType.ABNORMAL_PIC.getValue().equals(query.getErrorType())) {
                //List添加之前几种工单类型
                errorTypeList.add(ErrorType.SIGNAL_LOSS.getValue());
                errorTypeList.add(ErrorType.SCREEN_COLOR_DEVIATION.getValue());
                errorTypeList.add(ErrorType.SNOW_STORM.getValue());
                errorTypeList.add(ErrorType.STRIPE_INTERFERENCE.getValue());
                errorTypeList.add(ErrorType.SCREEN_OCCLUSION.getValue());
                errorTypeList.add(ErrorType.ABNORMAL_CLARITY.getValue());
            }
            query.setErrorTypeList(errorTypeList);
        }
        List<WorkOrderVO> export = baseMapper.export(query);
        System.out.println(export);
        List<String> workOrderNos = export.stream().map(WorkOrderVO::getWorkOrderNo).collect(Collectors.toList());
        List<WorkOrderVO> auditTimeList = workOrderMapper.getAuditTimeList(workOrderNos);
        Map<String, List<Date>> auditTimeMap = auditTimeList.stream()
                .collect(Collectors.groupingBy(
                        WorkOrderVO::getWorkOrderNo, // 分组的键
                        Collectors.mapping(WorkOrderVO::getAuditTime, Collectors.toList())));
        if (!CollectionUtils.isEmpty(workOrderNos)){
           export.forEach(workOrderVO -> {
               List<Date> auditTimes = auditTimeMap.get(workOrderVO.getWorkOrderNo());
               if (!CollectionUtils.isEmpty(auditTimes)){
                   //审核通过时间
                   Date passTime = Collections.max(auditTimes);
                   ZoneId zoneId = ZoneId.systemDefault();
                   workOrderVO.setPassTime(passTime.toInstant().atZone(zoneId).toLocalDateTime());
               }
           });
        }
        if (CollectionUtils.isEmpty(export)) return new ArrayList<WorkOrderVO>();
        List<String> ips = export.stream().map(WorkOrderVO::getIp).collect(Collectors.toList());
        //查出相同ip的设备、并查出标签
        List<TMonitorResult> monitorVOS = monitorMapper.getByIp(ips);
        //获取动态列数据
        List<Integer> pointIds = monitorVOS.stream().map(TMonitorResult::getPointId).collect(Collectors.toList());
        List<DynamicColumnVO> dynamics = dynamicColumnMapper.getDynamicsByIds("t_yw_point", pointIds);
        //补充动态列数据
        if (!CollectionUtils.isEmpty(dynamics)) {
            Map<Integer, List<DynamicColumnVO>> map = dynamics.stream().collect(Collectors.groupingBy(DynamicColumnVO::getRefId));
            for (TMonitorResult tMonitorResult : monitorVOS) {
                Integer pointId = tMonitorResult.getPointId();
                tMonitorResult.setDynamicColumnList(map.get(pointId));
            }
        }
        export.forEach(result -> {
            StringBuilder dynamicTag = new StringBuilder("");
            Boolean provinceTagVideo = false;
            Boolean provinceTagCar = false;
            Boolean provinceTagFace = false;
            Boolean importantTag = false;
            Boolean importantCommandImageTag = false;
            Boolean deptTag = false;
            List<String> dynamicTags = new ArrayList<>();
            for (TMonitorResult tagVO : monitorVOS) {
                //省厅标签通过ip判断 (运维要求)
                if (tagVO.getIp().equals(result.getIp())) {
                    if (tagVO.getProvinceTagVideo()) provinceTagVideo = true;
                    if (tagVO.getProvinceTagCar()) provinceTagCar = true;
                    if (tagVO.getProvinceTagFace()) provinceTagFace = true;
                    if (tagVO.getImportantTag()) importantTag = true;
                    if (tagVO.getImportantCommandImageTag()) importantCommandImageTag = true;
                    if (tagVO.getDeptTag()) deptTag = true;
                }
                //动态列根据国标判断
                if (tagVO.getNo().equals(result.getSerialNumber())) {
                    //动态列处理加在标签里
                    if (!CollectionUtils.isEmpty(tagVO.getDynamicColumnList())) {
                        List<DynamicColumnVO> dynamicColumnList = tagVO.getDynamicColumnList();
                        for (DynamicColumnVO dynamicColumnVO : dynamicColumnList) {
                            if (!dynamicTags.contains(dynamicColumnVO.getColumnValue()))
                                dynamicTags.add(dynamicColumnVO.getColumnValue());
                        }
                    }
                }
            }
            for (String tag : dynamicTags) {
                dynamicTag.append(tag).append("、");
            }
            StringBuilder tag = new StringBuilder("" + (provinceTagVideo ? "省厅视频、" : "") + (provinceTagCar ? "省厅车辆、" : "") + (provinceTagFace ? "省厅人脸、" : "") + (importantTag ? "重点点位、" : "") + (importantCommandImageTag ? "重点指挥图像、" : "") + (deptTag ? "部级、" : ""));
            if (tag.toString().endsWith("、")) {
                tag = new StringBuilder(tag.substring(0, tag.length() - 1));
            }
            if (dynamicTag.toString().endsWith("、")) {
                dynamicTag = new StringBuilder(dynamicTag.substring(0, dynamicTag.length() - 1));
            }
            result.setTag(tag.toString());
            result.setDynamicTag(dynamicTag.toString());
        });
        return export;
    }
    /**
     * 检测工单按钮
     *
     * @param workOrderNo
     * @return
     */
    @Override
    public Result checkImage(String workOrderNo, String serialNumber) {
        // 查出国标设备,就一条数据
        List<DeviceInfo> 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<String> workOrderNos) {
        if (!CollectionUtils.isEmpty(workOrderNos)) {
            //删除工单审核记录
            workOrderAuditingRecordMapper.delete(new QueryWrapper<WorkOrderAuditingRecord>().in("work_order_no", workOrderNos));
            //删除工单图片记录
            workOrderCheckImgMapper.delete(new QueryWrapper<WorkOrderCheckImg>().in("work_order_no", workOrderNos));
            //删除工单下发记录
            workOrderDistributeRecordMapper.delete(new QueryWrapper<WorkOrderDistributeRecord>().in("work_order_no", workOrderNos));
            //删除工单故障类型
            workOrderErrorTypeMapper.delete(new QueryWrapper<WorkOrderErrorType>().in("work_order_no", workOrderNos));
            //删除工单情况记录
            workOrderYwConditionRecordMapper.delete(new QueryWrapper<WorkOrderYwConditionRecord>().in("work_order_no", workOrderNos));
            //删除工单
            this.baseMapper.delete(new QueryWrapper<WorkOrder>().in("work_order_no", workOrderNos));
        }
        return Result.ok();
    }
    @Override
    public Result errorAll() {
        List<CascadeOption> results = new ArrayList<>();
        CascadeOption video = new CascadeOption();
        video.setLabel("视频");
        video.setValue("VIDEO");
        List<ErrorType> videoErr = ErrorType.getErrorTypesByCategory("VIDEO");
        List<ErrorType> videoErr1 = ErrorType.getErrorTypesByCategory("COMMON");
        videoErr.addAll(videoErr1);
        List<CascadeOption> 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<ErrorType> carErr = ErrorType.getErrorTypesByCategory("CAR");
        List<ErrorType> carErr1 = ErrorType.getErrorTypesByCategory("COMMON");
        List<ErrorType> carErr2 = ErrorType.getErrorTypesByCategory("CARORFACE");
        carErr.addAll(carErr1);
        carErr.addAll(carErr2);
        List<CascadeOption> 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<ErrorType> faceErr = ErrorType.getErrorTypesByCategory("FACE");
        List<ErrorType> faceErr1 = ErrorType.getErrorTypesByCategory("COMMON");
        List<ErrorType> faceErr2 = ErrorType.getErrorTypesByCategory("CARORFACE");
        faceErr.addAll(faceErr1);
        faceErr.addAll(faceErr2);
        List<CascadeOption> 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);
    }
    //白名单校验
    private List<WorkOrder> checkWhite(List<WorkOrder> workOrderList) {
        // 查询出白名单列表
        Map<String, WorkOrderWhite> whiteMap = workOrderWhiteMapper.selectList().stream().collect(Collectors.toMap(WorkOrderWhite::getSerialNumber, Function.identity()));
        workOrderList = workOrderList.stream()
                .filter(workOrder -> {
                    List<String> errorTypeList = workOrder.getErrorTypeList();
                    WorkOrderWhite white = whiteMap.get(workOrder.getSerialNumber());
                    if (white != null) {
                        List<String> whiteErrorListSet = Arrays.asList(white.getErrorType().split(","));
                        errorTypeList.removeIf(whiteErrorListSet::contains);
                    }
                    return !errorTypeList.isEmpty(); // 只保留 errorTypeList 非空的 WorkOrder
                })
                .collect(Collectors.toList());
        return workOrderList;
    }
}