package com.ycl.platform.service.impl; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.fastjson2.JSONArray; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ycl.dataListener.CurrencyDataListener; import com.ycl.platform.domain.entity.WorkOrder; import com.ycl.platform.domain.entity.WorkOrderWhite; import com.ycl.platform.domain.entity.YwPoint; import com.ycl.platform.domain.entity.YwThreshold; import com.ycl.platform.domain.excel.ErrorExport; import com.ycl.platform.domain.excel.PointExport; import com.ycl.platform.domain.excel.WorkOrderWhiteExport; import com.ycl.platform.domain.query.WorkOrderWhiteQuery; import com.ycl.platform.domain.result.HK.FaceDeviceInspectionResult; import com.ycl.platform.domain.result.HK.VehicleDeviceInspectionResult; import com.ycl.platform.domain.vo.DynamicColumnVO; import com.ycl.platform.mapper.WorkOrderWhiteMapper; import com.ycl.platform.mapper.YwThresholdMapper; import com.ycl.platform.service.IYwThresholdService; import com.ycl.platform.service.WorkOrderService; import com.ycl.system.Result; import com.ycl.system.page.PageUtil; import com.ycl.utils.SecurityUtils; import com.ycl.utils.StringUtils; import com.ycl.utils.ip.PingUtil; import com.ycl.utils.poi.EasyExcelImportUtils; import com.ycl.utils.uuid.IdUtils; import constant.PointHeaderConstant; import constant.YwThreadConstants; import enumeration.CompareType; import enumeration.ErrorType; import enumeration.general.BusinessTypeEnum; import enumeration.general.WorkOrderStatusEnum; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import utils.DateUtils; import java.io.IOException; import java.io.OutputStream; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; /** * 运维阈值Service业务层处理 * * @author gonghl * @date 2024-07-19 */ @Service @Slf4j public class YwThresholdServiceImpl extends ServiceImpl implements IYwThresholdService { @Autowired private YwThresholdMapper ywThresholdMapper; @Autowired private WorkOrderService workOrderService; @Autowired private WorkOrderWhiteMapper workOrderWhiteMapper; /** * 查询运维阈值 * * @param id 运维阈值主键 * @return 运维阈值 */ @Override public YwThreshold selectYwThresholdById(Long id) { return ywThresholdMapper.selectYwThresholdById(id); } /** * 查询运维阈值列表 * * @param ywThreshold 运维阈值 * @return 运维阈值 */ @Override public Map> selectYwThresholdList(YwThreshold ywThreshold) { List ywThresholds = ywThresholdMapper.selectYwThresholdList(ywThreshold); Map> map = ywThresholds.stream().collect(Collectors.groupingBy(YwThreshold::getMonitorType)); return map; } /** * 新增运维阈值 * * @param ywThreshold 运维阈值 * @return 结果 */ @Override public int insertYwThreshold(YwThreshold ywThreshold) { ywThreshold.setCreateTime(DateUtils.getNowDate()); return ywThresholdMapper.insertYwThreshold(ywThreshold); } /** * 修改运维阈值 * * @param list 运维阈值 * @return 结果 */ @Override public Boolean updateYwThreshold(List list) { list.forEach(item -> item.setCreateTime(new Date())); return updateBatchById(list); } /** * 批量删除运维阈值 * * @param ids 需要删除的运维阈值主键 * @return 结果 */ @Override public int deleteYwThresholdByIds(Long[] ids) { return ywThresholdMapper.deleteYwThresholdByIds(ids); } /** * 删除运维阈值信息 * * @param id 运维阈值主键 * @return 结果 */ @Override public int deleteYwThresholdById(Long id) { return ywThresholdMapper.deleteYwThresholdById(id); } /** * 判断视频阈值是否满足下发条件 * * @param list */ @Override public void videoCheck(List list) { } @Override public void errorTypeCheckUY(List list) { // 平台离线故障检测 if (CollectionUtils.isEmpty(list)) { Boolean ping = PingUtil.ping("", 5); if (!ping) { } } // 设备离线故障检测 // 信号缺失故障检测 // 画面偏色故障检测 // 雪花干扰故障检测 // 条纹干扰故障检测 // 画面遮挡故障检测 // 清晰度异常检测 // 亮度异常故障检测 } /** * 判断人脸阈值是否满足下发条件 * * @param list */ @Override public void faceCheck(List list) { Map thresholdMap = getYwThresholdMap(BusinessTypeEnum.FACE.name()); //准备待下发工单集合 List distributeList = new ArrayList<>(); //准备直接下发工单集合 List workOrderList = new ArrayList<>(); //处理接口数据 for (FaceDeviceInspectionResult result : list) { if (result == null) { log.error("人脸对象数据为空"); continue; } WorkOrder workOrder = new WorkOrder(); //检查时钟准确率 if (result.getSnapClock() != null) { Float clockPercent = result.getSnapClock().getClockPercent(); check(YwThreadConstants.Face_ClockPercent, clockPercent, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.LESS_THAN_EQ, ErrorType.CLOCK_RIGHT.getValue()); } //检查数据及时率 if (result.getSnapTimely() != null) { Float timelyPercent = result.getSnapTimely().getTimelyPercent(); check(YwThreadConstants.Face_TimelyPercent, timelyPercent, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.LESS_THAN_EQ, ErrorType.DATA_TIMELY_ERROR.getValue()); } //检查持续无数据天数 if (result.getContinueNoDataCount() != null) { Integer continueNoDataCount = result.getContinueNoDataCount(); check(YwThreadConstants.Face_ContinueNoDataCount, continueNoDataCount, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.MORE_THAN_EQ, ErrorType.LONG_DAY_NO_DATA.getValue()); } //检查不唯一数据量 if (result.getSnapUnique() != null) { Integer nouniqueCount = result.getSnapUnique().getNouniqueCount(); check(YwThreadConstants.Face_NouniqueCount, nouniqueCount, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.MORE_THAN_EQ, ErrorType.NOT_UNIQUE_DATA_VOLUME.getValue()); } //检查人脸低评分率 if (result.getSnapValidity() != null) { Float lowScorePercent = result.getSnapValidity().getLowScorePercent(); check(YwThreadConstants.Face_LowScorePercent, lowScorePercent, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.MORE_THAN_EQ, ErrorType.FACE_LOW.getValue()); } //检查建模失败率 if (result.getSnapValidity() != null) { Float failPercent = result.getSnapValidity().getFailPercent(); check(YwThreadConstants.Face_FailPercent, failPercent, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.MORE_THAN_EQ, ErrorType.MODELING_FAIL.getValue()); } // 点位在线率 if (2 == result.getSnapResult()) { workOrder.setSerialNumber(result.getExternalIndexCode()); workOrder.setStatus(WorkOrderStatusEnum.DISTRIBUTED); if (CollectionUtils.isEmpty(workOrder.getErrorTypeList())) { workOrder.setErrorTypeList(new ArrayList<>()); workOrder.getErrorTypeList().add(ErrorType.DEVICE_OFFLINE.getValue()); } else { workOrder.getErrorTypeList().add(ErrorType.DEVICE_OFFLINE.getValue()); } } if (WorkOrderStatusEnum.WAIT_DISTRIBUTE.equals(workOrder.getStatus())) { workOrderList.add(workOrder); } else if (WorkOrderStatusEnum.DISTRIBUTED.equals(workOrder.getStatus())) { distributeList.add(workOrder); } } /** 添加工单 */ workOrderService.innerAddWorkOrder(workOrderList); workOrderService.innerAddWorkOrder(distributeList); } /** * 判断车辆阈值是否满足下发条件 * * @param list */ @Override public void carCheck(List list) { Map thresholdMap = getYwThresholdMap(BusinessTypeEnum.CAR.name()); //准备下发工单集合 List distributeList = new ArrayList<>(); //准备自动生成工单集合 List workOrderList = new ArrayList<>(); //处理接口数据 for (VehicleDeviceInspectionResult result : list) { if (result == null) { log.error("车辆对象数据为空"); continue; } WorkOrder workOrder = new WorkOrder(); //检查持续无数据天数 Integer continueNoDataCount = result.getContinueNoDataCount(); check(YwThreadConstants.Car_ContinueNoDataCount, continueNoDataCount, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.MORE_THAN_EQ, ErrorType.LONG_DAY_NO_DATA.getValue()); //检查时钟准确率 if (result.getSnapClock() != null) { Float clockPercent = result.getSnapClock().getClockPercent(); check(YwThreadConstants.Car_ClockPercent, clockPercent, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.LESS_THAN_EQ, ErrorType.CLOCK_RIGHT.getValue()); } //检查数据及时率 if (result.getSnapTimely() != null) { Float timelyPercentResult = result.getSnapTimely().getTimelyPercent(); check(YwThreadConstants.Car_TimelyPercent, timelyPercentResult, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.LESS_THAN_EQ, ErrorType.DATA_TIMELY_ERROR.getValue()); } //检查不唯一数据量 if (result.getSnapUnique() != null) { Integer nouniqueCountResult = result.getSnapUnique().getNouniqueCount(); check(YwThreadConstants.Car_NouniqueCount, nouniqueCountResult, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.MORE_THAN_EQ, ErrorType.NOT_UNIQUE_DATA_VOLUME.getValue()); } //检查白天未识别量 if (result.getSnapPlate() != null) { Integer dayNoNumberCountResult = result.getSnapPlate().getDayNoNumberCount(); check(YwThreadConstants.Car_DayNoNumberCount, dayNoNumberCountResult, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.MORE_THAN_EQ, ErrorType.UNRECOGNIZED_DAY_VOLUME.getValue()); } //车辆主要属性一致率 if (result.getIntegrity() != null) { Integer noIntegrityCountResult = result.getIntegrity().getMainNoIntegrityCount(); Integer dataCount = result.getDataCount(); Double integrityRate = ((double) dataCount - noIntegrityCountResult) / dataCount; check(YwThreadConstants.Car_NoIntegrityCount, integrityRate, result.getExternalIndexCode(), thresholdMap, workOrder, CompareType.LESS_THAN_EQ, ErrorType.CAR_SIX.getValue()); } // 点位在线率 if (2 == result.getSnapResult()) { workOrder.setSerialNumber(result.getExternalIndexCode()); workOrder.setStatus(WorkOrderStatusEnum.DISTRIBUTED); if (CollectionUtils.isEmpty(workOrder.getErrorTypeList())) { workOrder.setErrorTypeList(new ArrayList<>()); workOrder.getErrorTypeList().add(ErrorType.DEVICE_OFFLINE.getValue()); } else { workOrder.getErrorTypeList().add(ErrorType.DEVICE_OFFLINE.getValue()); } } if (WorkOrderStatusEnum.WAIT_DISTRIBUTE.equals(workOrder.getStatus())) { workOrderList.add(workOrder); } else if (WorkOrderStatusEnum.DISTRIBUTED.equals(workOrder.getStatus())) { distributeList.add(workOrder); } } /** 添加工单 */ workOrderService.innerAddWorkOrder(workOrderList); workOrderService.innerAddWorkOrder(distributeList); } /** * 把阈值条件查出来,转成map * * @param name * @return */ @Override public Map getYwThresholdMap(String name) { return ywThresholdMapper.selectList( new QueryWrapper().eq("monitor_type", name) ).stream().collect(Collectors.toMap( YwThreshold::getKey, Function.identity() )); } /** * 白名单详情 * * @param id * @return */ @Override public Result selectWorkOrderWhiteDetail(Integer id) { WorkOrderWhite workOrderWhite = workOrderWhiteMapper.getById(id); List errorTypeList = Arrays.asList(workOrderWhite.getErrorType().split(",")); workOrderWhite.setErrorTypeList(errorTypeList); return Result.ok().data(workOrderWhite); } /** * 工单白名单列表 * * @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); List records = page.getRecords(); records.forEach(white -> { List errorTextList = new ArrayList<>(); List errorTypeList = Arrays.asList(white.getErrorType().split(",")); errorTypeList.forEach(error -> { String errorText = ErrorType.getDescriptionByValue(error); errorTextList.add(errorText); }); white.setErrorType(String.join(",", errorTextList)); }); return Result.ok().data(records).total(page.getTotal()); } /** * 添加工单白名单 * * @param workOrderWhite 白色工单 * @return {@link Result } * @author */ @Override public Result addWorkOrderWhite(WorkOrderWhite workOrderWhite) { // 检查是否已经存在该白名单 WorkOrderWhite flag = workOrderWhiteMapper.selectBySerialNumber(workOrderWhite.getSerialNumber()); if (flag != null) { return Result.error("该设备已存在白名单"); } else { List errorTypeList = workOrderWhite.getErrorTypeList(); workOrderWhite.setErrorType(String.join(",", errorTypeList)); workOrderWhite.setCreateBy(SecurityUtils.getUsername()); workOrderWhiteMapper.insert(workOrderWhite); return Result.ok(); } } /** * 修改工单白名单 * * @param workOrderWhite 白色工单 * @return {@link Result } * @author */ @Override public Result updateWorkOrderWhite(WorkOrderWhite workOrderWhite) { WorkOrderWhite white = workOrderWhiteMapper.selectBySerialNumber(workOrderWhite.getSerialNumber()); workOrderWhite.setId(white.getId()); List errorTypeList = workOrderWhite.getErrorTypeList(); workOrderWhite.setErrorType(String.join(",", errorTypeList)); workOrderWhiteMapper.updateById(workOrderWhite); return Result.ok(); } /** * 批量删除工单白名单 * * @param ids ids * @author */ @Override public Result batchDeleteWorkOrderWhite(List ids) { workOrderWhiteMapper.batchDelete(ids); return Result.ok(); } /** * 白名单导出 * * @param response * @throws IOException */ @Override public void whiteExport(HttpServletResponse response) throws IOException { //白名单数据 List data = workOrderWhiteMapper.whiteExport(); data.forEach(white -> { List errorTextList = new ArrayList<>(); List errorTypeList = Arrays.asList(white.getErrorType().split(",")); errorTypeList.forEach(error -> { String errorText = ErrorType.getDescriptionByValue(error); errorTextList.add(errorText); }); white.setErrorType(String.join(",", errorTextList)); }); //故障类型数据 List errorTextList = ErrorType.getDescriptionList(); List errorExports = new ArrayList<>(); errorTextList.forEach(error -> { ErrorExport errorExport = new ErrorExport(); errorExport.setError(error); errorExports.add(errorExport); }); ExcelWriter excelWriter = null; try (OutputStream outputStream = response.getOutputStream()) { excelWriter = EasyExcel.write(outputStream).build(); WriteSheet whiteSheet = EasyExcel.writerSheet(0, "工单白名单清单").head(WorkOrderWhiteExport.class).build(); WriteSheet errorSheet = EasyExcel.writerSheet(1, "故障类型").head(ErrorExport.class).build(); excelWriter.write(data, whiteSheet); excelWriter.write(errorExports, errorSheet); excelWriter.finish(); outputStream.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { excelWriter.finish(); response.getOutputStream().close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 导入工单白名单 * * @param file * @return */ @Override @Transactional(rollbackFor = Exception.class) public Result importWhite(MultipartFile file) throws IOException { // Consumer> consumer = (dataList) -> { // try { // this.updateWhite(dataList); // } catch (ExecutionException e) { // e.printStackTrace(); // } catch (InterruptedException e) { // e.printStackTrace(); // } // }; // EasyExcel.read(file.getInputStream(), WorkOrderWhiteExport.class, new CurrencyDataListener(consumer)) // .sheet() // .headRowNumber(1) // .doRead(); List dataList = new ArrayList<>(); EasyExcel.read(file.getInputStream(), WorkOrderWhiteExport.class, new AnalysisEventListener() { @Override public void invoke(WorkOrderWhiteExport excel, AnalysisContext analysisContext) { // 将读取到的每一行存入reportDetails集合中 dataList.add(excel); } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } }).sheet().doRead(); if (CollectionUtils.isEmpty(dataList)) { throw new RuntimeException("导入数据不能为空"); } boolean duplic = checkDuplic(dataList); if (duplic) throw new RuntimeException("存在重复国标设备"); List addList = new ArrayList<>(); dataList.stream().forEach(item -> { WorkOrderWhite white = new WorkOrderWhite(); //国标码 white.setSerialNumber(item.getSerialNumber()); //备注 white.setRemark(item.getRemark()); String errorType = item.getErrorType(); if (StringUtils.isEmpty(errorType)) { throw new RuntimeException("国标码为" + item.getSerialNumber() + "的设备故障不能为空"); } try { List errorDataList = new ArrayList<>(); List errorExcelList = Arrays.asList(item.getErrorType().split(",")); errorExcelList.forEach(desc -> { //把中文转换为数据库存储格式 String errorText = ErrorType.getValueByDescription(desc); //找不到抛出异常 if (errorText == null) throw new RuntimeException("国标码为" + item.getSerialNumber() + "的设备故障类型有误"); errorDataList.add(errorText); }); white.setErrorType(String.join(",", errorDataList)); addList.add(white); } catch (Exception e) { throw new RuntimeException("国标码为" + item.getSerialNumber() + "的设备故障类型有误"); } }); if (!CollectionUtils.isEmpty(addList)) { workOrderWhiteMapper.deleteAll(); workOrderWhiteMapper.insertBatch(addList); } return Result.ok(); } /** * 修改白名单 * * @param dataList * @param unitId */ public void updateWhite(List dataList) throws ExecutionException, InterruptedException { if (CollectionUtils.isEmpty(dataList)) { throw new RuntimeException("导入数据不能为空"); } //已存在的白名单 List serialNumbers = workOrderWhiteMapper.selectList().stream().map(WorkOrderWhite::getSerialNumber).collect(Collectors.toList()); List addList = new ArrayList<>(); List updateList = new ArrayList<>(); dataList.stream().forEach(item -> { WorkOrderWhite white = new WorkOrderWhite(); //国标码 white.setSerialNumber(item.getSerialNumber()); //备注 white.setRemark(item.getRemark()); String errorType = item.getErrorType(); if (StringUtils.isEmpty(errorType)) { throw new RuntimeException("国标码为" + item.getSerialNumber() + "的设备故障不能为空"); } try { List errorDataList = new ArrayList<>(); List errorExcelList = Arrays.asList(item.getErrorType().split(",")); errorExcelList.forEach(desc -> { //把中文转换为数据库存储格式 String errorText = ErrorType.getValueByDescription(desc); //找不到抛出异常 if (errorText == null) throw new RuntimeException("国标码为" + item.getSerialNumber() + "的设备故障类型有误"); errorDataList.add(errorText); }); white.setErrorType(String.join(",", errorDataList)); if (!CollectionUtils.isEmpty(serialNumbers) && serialNumbers.contains(white.getSerialNumber())) { updateList.add(white); } else { addList.add(white); } } catch (Exception e) { throw new RuntimeException("国标码为" + item.getSerialNumber() + "的设备故障类型有误"); } }); if (!CollectionUtils.isEmpty(addList)) workOrderWhiteMapper.insertBatch(addList); if (!CollectionUtils.isEmpty(updateList)) workOrderWhiteMapper.updateBatch(updateList); } /** * 检查阈值 * * @param key 某阈值标识 * @param value 接口获取到的值 * @param serialNumber 国标码 * @param thresholds 阈值条件map * @param compareType 比较方式:>= <= * @param errorType 故障类型 * @param */ @Override public > void check(String key, T value, String serialNumber, Map thresholds, WorkOrder workOrder, CompareType compareType, String errorType) { Optional.ofNullable(value).ifPresentOrElse( v -> { YwThreshold ywThreshold = thresholds.get(key); //转换类型 //待工单阈值 T thresholdAutoValue = parseThreshold(ywThreshold.getValueAuto(), value.getClass()); //直接下发工单阈值 T thresholdValue = parseThreshold(ywThreshold.getValue(), value.getClass()); if ("percent".equals(ywThreshold.getCountType())) { if (thresholdAutoValue instanceof Float) { thresholdAutoValue = (T) Float.valueOf(((Float) thresholdAutoValue) / 100f); } if (thresholdValue instanceof Float) { thresholdValue = (T) Float.valueOf(((Float) thresholdValue) / 100f); } } //比较大小,加入到对应待处理集合 if (compareType.compare(v, thresholdValue)) { //进入工单直接下发 workOrder.setSerialNumber(serialNumber); workOrder.setStatus(WorkOrderStatusEnum.DISTRIBUTED); if (CollectionUtils.isEmpty(workOrder.getErrorTypeList())) { workOrder.setErrorTypeList(new ArrayList<>()); workOrder.getErrorTypeList().add(errorType); } else { workOrder.getErrorTypeList().add(errorType); } } else if (compareType.compare(v, thresholdAutoValue)) { //进入工单代下发 workOrder.setSerialNumber(serialNumber); workOrder.setStatus(WorkOrderStatusEnum.WAIT_DISTRIBUTE); if (CollectionUtils.isEmpty(workOrder.getErrorTypeList())) { workOrder.setErrorTypeList(new ArrayList<>()); workOrder.getErrorTypeList().add(errorType); } else { workOrder.getErrorTypeList().add(errorType); } } }, () -> log.error("{} 为空: {}", thresholds.get(key).getName(), serialNumber) ); } /** * 所有白名单添加故障类型 * * @param workOrderWhite */ @Override @Transactional(rollbackFor = Exception.class) public Result addBatch(WorkOrderWhite workOrderWhite) { List errorTypeListAdd = workOrderWhite.getErrorTypeList(); List whites = workOrderWhiteMapper.selectList(); for (WorkOrderWhite white : whites) { List errorTypeList = new ArrayList<>(Arrays.asList(white.getErrorType().split(","))); for (String newError : errorTypeListAdd) { if (errorTypeList.contains(newError)) { continue; } else { errorTypeList.add(newError); } } white.setErrorType(String.join(",", errorTypeList)); } if (!CollectionUtils.isEmpty(whites)) { workOrderWhiteMapper.deleteAll(); workOrderWhiteMapper.insertBatch(whites); } return Result.ok(); } private > T parseThreshold(String thresholdStr, Class type) { if (Integer.class.equals(type)) { return (T) Integer.valueOf(thresholdStr); } else if (Float.class.equals(type)) { return (T) Float.valueOf(thresholdStr); } else if (Double.class.equals(type)) { return (T) Double.valueOf(thresholdStr); } else if (Long.class.equals(type)) { return (T) Long.valueOf(thresholdStr); } else { throw new IllegalArgumentException("不支持转换类型" + type); } } public static boolean checkDuplic(List dataList) { Set serialNumbers = new HashSet<>(); for (WorkOrderWhiteExport white : dataList) { String serialNumber = white.getSerialNumber(); if (!serialNumbers.add(serialNumber)) { return true; } } return false; } }