zxl
2025-11-10 cb415813de667096290d6bd0f924f5b523104117
报备工单bug修改,定时任务在线问题修改新增导出扣分明细功能
20个文件已修改
6个文件已添加
822 ■■■■ 已修改文件
sql/zgyw0506.sql 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-common/src/main/java/enumeration/ConstructionTypeEnum.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-pojo/src/main/java/com/ycl/platform/domain/entity/DailyMonitorDemeritRecord.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-pojo/src/main/java/com/ycl/platform/domain/excel/DailyMonitorDemeritRecordExp.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-pojo/src/main/java/com/ycl/platform/domain/form/DailyMonitorDemeritRecordForm.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-pojo/src/main/java/com/ycl/platform/domain/query/HomeQuery.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-pojo/src/main/java/com/ycl/platform/domain/result/UY/RecordMetaDSumResult.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-pojo/src/main/java/com/ycl/platform/domain/vo/WorkOrderVO.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/controller/DemeritRecordController.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/controller/MonitorConstructionController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/mapper/TMonitorMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/service/IDailyMonitorDemeritRecordService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/service/IMonitorConstructionService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/service/impl/DailyMonitorDemeritRecordServiceImpl.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/service/impl/DataCenterServiceImpl.java 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/service/impl/IMonitorConstructionServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/service/impl/ReportServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/service/impl/TMonitorServiceImpl.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/platform/service/impl/WorkOrderServiceImpl.java 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/system/mapper/DailyMonitorDemeritRecordMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/task/DemeritRecordTask.java 138 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/task/UYTask.java 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/java/com/ycl/thread/OnlineCheckThread.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/resources/mapper/zgyw/ReportMapper.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/resources/mapper/zgyw/TMonitorMapper.xml 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ycl-server/src/main/resources/mapper/zgyw/WorkOrderMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/zgyw0506.sql
@@ -1386,7 +1386,7 @@
INSERT INTO `sys_oper_log` VALUES (133, '删除考核模板', 3, 'com.ycl.platform.controller.CheckTemplateController.remove()', 'DELETE', 1, 'admin', '省厅', '/check/template/35', '127.0.0.1', '内网IP', '{}', '{\"msg\":\"操作成功\",\"code\":200}', 0, NULL, '2024-04-30 08:40:38', 151);
INSERT INTO `sys_oper_log` VALUES (134, '修改考核模板', 2, 'com.ycl.platform.controller.CheckTemplateController.edit()', 'PUT', 1, 'admin', '省厅', '/check/template', '127.0.0.1', '内网IP', '{\"adjustCoefficient\":\"8.0000\",\"adjustWay\":\"/\",\"createTime\":\"2024-04-19 11:45:15\",\"createUser\":1,\"createUserName\":\"admin\",\"deleted\":\"0\",\"deptId\":[101,102,201,202,203,210,211],\"description\":\"天网考核每月底最后四个工作日进行整改,前两工作日进行点位更新,后两天进行资产库更新,从1号开始考核。\\n车辆运行率:(视图库对接稳定性×1+点位在线率×1+联网卡口设备目录一致率×0.5+车辆卡口信息采集准确率×0.5+车辆卡口设备抓拍数据完整性×1+车辆卡口设备抓拍数据准确性×1+车辆卡口设备时钟准确性×1+车辆卡口设备抓拍数据上传及时性×1+车辆卡口设备url可用性×0.5+车辆卡口设备抓拍数据大图可用性×0.5)/8\",\"examineCategory\":2,\"examineTag\":1,\"frequency\":0,\"id\":29,\"pageNum\":1,\"pageSize\":10,\"status\":\"1\",\"templateName\":\"车辆考核模板\",\"updateTime\":\"2024-04-19 15:06:21\",\"updateUser\":1,\"updateUserName\":\"admin\"}', '{\"msg\":\"操作成功\",\"code\":200}', 0, NULL, '2024-04-30 08:41:49', 433);
INSERT INTO `sys_oper_log` VALUES (135, '修改考核模板', 2, 'com.ycl.platform.controller.CheckTemplateController.edit()', 'PUT', 1, 'admin', '省厅', '/check/template', '127.0.0.1', '内网IP', '{\"adjustCoefficient\":\"8.0000\",\"adjustWay\":\"/\",\"createTime\":\"2024-04-19 11:45:15\",\"createUser\":1,\"createUserName\":\"admin\",\"deleted\":\"0\",\"deptId\":[101,102,201,202,203,210,211],\"description\":\"天网考核每月底最后四个工作日进行整改,前两工作日进行点位更新,后两天进行资产库更新,从1号开始考核。\\n车辆运行率:(视图库对接稳定性×1+点位在线率×1+联网卡口设备目录一致率×0.5+车辆卡口信息采集准确率×0.5+车辆卡口设备抓拍数据完整性×1+车辆卡口设备抓拍数据准确性×1+车辆卡口设备时钟准确性×1+车辆卡口设备抓拍数据上传及时性×1+车辆卡口设备url可用性×0.5+车辆卡口设备抓拍数据大图可用性×0.5)/8\",\"examineCategory\":2,\"examineTag\":1,\"frequency\":0,\"id\":29,\"pageNum\":1,\"pageSize\":10,\"status\":\"0\",\"templateName\":\"车辆考核模板\",\"updateTime\":\"2024-04-30 16:41:49\",\"updateUser\":1,\"updateUserName\":\"admin\"}', '{\"msg\":\"操作成功\",\"code\":200}', 0, NULL, '2024-04-30 08:41:52', 348);
INSERT INTO `sys_oper_log` VALUES (136, '修改考核模板', 2, 'com.ycl.platform.controller.CheckTemplateController.edit()', 'PUT', 1, 'admin', '省厅', '/check/template', '127.0.0.1', '内网IP', '{\"adjustCoefficient\":\"8.5000\",\"adjustWay\":\"/\",\"alarmScore\":\"5.0000\",\"createTime\":\"2024-04-22 11:06:43\",\"createUser\":1,\"createUserName\":\"admin\",\"deleted\":\"0\",\"deptId\":[101,102,201,202,203,210,211],\"description\":\"天网考核每月底最后四个工作日进行整改,前两个工作日进行点位更新,后两个工作日进行资产库更新,从1号开始考核。\\n每月运行率=(平台在线率×0.5+一机一档合格率×0.5+一机一档注册率×0.5+档案考核比×0.5+点位在线率×1+录像可用率×1+标注正确率×0.5+校时正确率×0.5+重点点位在线率×0.5+重点点位录像可用率×0.5+重点点位标注正确率×0.5+重点点位校时正确率×0.5+重点指挥图像在线率×1+视频图像资源安全管理×0.5)/8.5\",\"examineCategory\":1,\"examineTag\":1,\"frequency\":0,\"id\":31,\"pageNum\":1,\"pageSize\":10,\"ruleFormList\":[{\"ruleId\":1,\"weight\":1},{\"ruleId\":2,\"weight\":0.5},{\"ruleId\":3,\"weight\":0.5},{\"ruleId\":4,\"weight\":0.5},{\"ruleId\":5,\"weight\":0.5},{\"ruleId\":6,\"weight\":1},{\"ruleId\":7,\"weight\":0.5},{\"ruleId\":8,\"weight\":0.5},{\"ruleId\":9,\"weight\":0.5},{\"ruleId\":10,\"weight\":0.5},{\"ruleId\":11,\"weight\":0.5},{\"ruleId\":12,\"weight\":0.5},{\"ruleId\":13,\"weight\":1}],\"status\":\"0\",\"templateName\":\"视频考核模板\",\"updateTime\":\"2024-04-30 14:10:52\",\"updateUser\":1,\"updateUserName\":\"admin\"}', '{\"msg\":\"操作成功\",\"code\":200}', 0, NULL, '2024-04-30 09:00:49', 1289);
INSERT INTO `sys_oper_log` VALUES (136, '修改考核模板', 2, 'com.ycl.platform.controller.CheckTemplateController.edit()', 'PUT', 1, 'admin', '省厅', '/check/template', '127.0.0.1', '内网IP', '{\"adjustCoefficient\":\"8.5000\",\"adjustWay\":\"/\",\"alarmScore\":\"5.0000\",\"createTime\":\"2024-04-22 11:06:43\",\"createUser\":1,\"createUserName\":\"admin\",\"deleted\":\"0\",\"deptId\":[101,102,201,202,203,210,211],\"description\":\"天网考核每月底最后四个工作日进行整改,前两个工作日进行点位更新,后两个工作日进行资产库更新,从1号开始考核。\\n=(平台在线率×0.5+一机一档合格率×0.5+一机一档注册率×0.5+档案考核比×0.5+点位在线率×1+录像可用率×1+标注正确率×0.5+校时正确率×0.5+重点点位在线率×0.5+重点点位录像可用率×0.5+重点点位标注正确率×0.5+重点点位校时正确率×0.5+重点指挥图像在线率×1+视频图像资源安全管理×0.5)/8.5\",\"examineCategory\":1,\"examineTag\":1,\"frequency\":0,\"id\":31,\"pageNum\":1,\"pageSize\":10,\"ruleFormList\":[{\"ruleId\":1,\"weight\":1},{\"ruleId\":2,\"weight\":0.5},{\"ruleId\":3,\"weight\":0.5},{\"ruleId\":4,\"weight\":0.5},{\"ruleId\":5,\"weight\":0.5},{\"ruleId\":6,\"weight\":1},{\"ruleId\":7,\"weight\":0.5},{\"ruleId\":8,\"weight\":0.5},{\"ruleId\":9,\"weight\":0.5},{\"ruleId\":10,\"weight\":0.5},{\"ruleId\":11,\"weight\":0.5},{\"ruleId\":12,\"weight\":0.5},{\"ruleId\":13,\"weight\":1}],\"status\":\"0\",\"templateName\":\"视频考核模板\",\"updateTime\":\"2024-04-30 14:10:52\",\"updateUser\":1,\"updateUserName\":\"admin\"}', '{\"msg\":\"操作成功\",\"code\":200}', 0, NULL, '2024-04-30 09:00:49', 1289);
-- ----------------------------
-- Table structure for sys_post
ycl-common/src/main/java/enumeration/ConstructionTypeEnum.java
@@ -23,4 +23,10 @@
    ConstructionTypeEnum(String desc) {
        this.desc = desc;
    }
    public static ConstructionTypeEnum getEnumByName(String enumName) {
        // 使用Enum的valueOf方法根据名称获取枚举实例
        return ConstructionTypeEnum.valueOf(enumName);
    }
}
ycl-pojo/src/main/java/com/ycl/platform/domain/entity/DailyMonitorDemeritRecord.java
New file
@@ -0,0 +1,49 @@
package com.ycl.platform.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ycl.platform.base.AbsEntity;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
 * zgyw
 * 设备每日扣分记录实体
 * @author : zxl
 * @date : 2025-11-04 15:13
 **/
@Data
@TableName("t_daily_monitor_demerit_record")
public class DailyMonitorDemeritRecord extends AbsEntity {
    //设备编号
    @TableField("serial_number")
    private String serialNumber;
    //扣除分数
    @TableField("demerit")
    private BigDecimal demerit;
    //计算时间
    @TableField("record_time")
    private Date recordTime;
    //报备状态
    @TableField("is_report")
    private Boolean isReport;
    @TableField("dept_id")
    private Integer deptId;
    @TableField("construction_type")
    private String constructionType;
    @TableField("device_name")
    private String deviceName;
}
ycl-pojo/src/main/java/com/ycl/platform/domain/excel/DailyMonitorDemeritRecordExp.java
New file
@@ -0,0 +1,34 @@
package com.ycl.platform.domain.excel;
import annotation.Excel;
import lombok.Data;
import java.math.BigDecimal;
/**
 * zgyw
 *
 * @author : zxl
 * @date : 2025-11-05 10:31
 **/
@Data
public class DailyMonitorDemeritRecordExp {
    @Excel(name="区域",width = 20)
    private String dept;
    @Excel(name="设备编号",width = 25)
    private String serialNumber;
    @Excel(name="设备名",width = 25)
    private String deviceName;
    @Excel(name="是否报备",width = 25)
    private String isReport;
    @Excel(name="分建类型",width = 25)
    private String constructionType;
    @Excel(name="扣分",width = 25)
    private BigDecimal demerit;
    @Excel(name="日期",width = 25)
    private String recordTime;
}
ycl-pojo/src/main/java/com/ycl/platform/domain/form/DailyMonitorDemeritRecordForm.java
New file
@@ -0,0 +1,16 @@
package com.ycl.platform.domain.form;
import com.ycl.platform.base.AbsForm;
import lombok.Data;
/**
 * zgyw
 *
 * @author : zxl
 * @date : 2025-11-04 17:57
 **/
@Data
public class DailyMonitorDemeritRecordForm extends AbsForm {
}
ycl-pojo/src/main/java/com/ycl/platform/domain/query/HomeQuery.java
@@ -47,4 +47,9 @@
     */
    private String area;
    /**
     * 建设类型
     */
    private String constructionType;
}
ycl-pojo/src/main/java/com/ycl/platform/domain/result/UY/RecordMetaDSumResult.java
@@ -112,4 +112,6 @@
        }
        return recordStatusText;
    }
    private String constructionType;
}
ycl-pojo/src/main/java/com/ycl/platform/domain/vo/WorkOrderVO.java
@@ -139,6 +139,7 @@
    private List<Date> auditTimeList;
    private Date auditTime;
    private Boolean hasReport;
    public static WorkOrderVO getVoByEntity(@NonNull WorkOrder entity, WorkOrderVO vo) {
        if(vo == null) {
ycl-server/src/main/java/com/ycl/platform/controller/DemeritRecordController.java
@@ -2,12 +2,14 @@
import com.ycl.platform.domain.query.DemeritRecordQuery;
import com.ycl.platform.service.IDailyMonitorDemeritRecordService;
import com.ycl.platform.service.IDemeritRecordService;
import com.ycl.system.Result;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
/**
 * zgyw
@@ -22,9 +24,13 @@
public class DemeritRecordController {
    public final IDemeritRecordService demeritRecordService;
    private final IDailyMonitorDemeritRecordService dailyMonitorDemeritRecordService;
    @GetMapping("/getPage")
    public Result getPage(DemeritRecordQuery query){
        return demeritRecordService.getDemeritRecordPage(query);
    }
    @PostMapping("/exportInfo/{id}")
    public void exportInfo(HttpServletResponse httpServletResponse, @PathVariable("id")Integer id) throws IOException {
        dailyMonitorDemeritRecordService.getExportInfo(httpServletResponse,id);
    }
}
ycl-server/src/main/java/com/ycl/platform/controller/MonitorConstructionController.java
@@ -1,15 +1,16 @@
package com.ycl.platform.controller;
import com.ycl.platform.domain.form.DailyMonitorDemeritRecordForm;
import com.ycl.platform.service.IDailyMonitorDemeritRecordService;
import com.ycl.platform.service.IMonitorConstructionService;
import com.ycl.system.Result;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
 * zgyw
@@ -32,4 +33,6 @@
    public void importTemplate(HttpServletResponse httpServletResponse) {
        monitorConstructionService.getImportTemplate(httpServletResponse);
    }
}
ycl-server/src/main/java/com/ycl/platform/mapper/TMonitorMapper.java
@@ -188,6 +188,8 @@
    void batchUpdateOnline(@Param("ipList")List<String> ipList,@Param("date") Date date,@Param("online")Integer online);
    void batchUpdatePingOnline(@Param("ipList")List<String> ipList,@Param("date") Date date,@Param("online")Integer online);
    List<TMonitor> selectCarOrFace();
    List<TMonitorExp> exportTMonitorList(TMonitorVO tMonitor);
ycl-server/src/main/java/com/ycl/platform/service/IDailyMonitorDemeritRecordService.java
New file
@@ -0,0 +1,14 @@
package com.ycl.platform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ycl.platform.domain.entity.DailyMonitorDemeritRecord;
import com.ycl.platform.domain.form.DailyMonitorDemeritRecordForm;
import io.swagger.models.auth.In;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface IDailyMonitorDemeritRecordService extends IService<DailyMonitorDemeritRecord> {
    void getExportInfo(HttpServletResponse httpServletResponse, Integer id) throws IOException;
}
ycl-server/src/main/java/com/ycl/platform/service/IMonitorConstructionService.java
@@ -26,4 +26,5 @@
    void getImportTemplate(HttpServletResponse httpServletResponse);
    List<MonitorConstruction> getSerialNumberListByConstructionType(String constructionType);
}
ycl-server/src/main/java/com/ycl/platform/service/impl/DailyMonitorDemeritRecordServiceImpl.java
New file
@@ -0,0 +1,94 @@
package com.ycl.platform.service.impl;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ycl.platform.domain.entity.DailyMonitorDemeritRecord;
import com.ycl.platform.domain.entity.DemeritRecord;
import com.ycl.platform.domain.entity.MonitorConstruction;
import com.ycl.platform.domain.excel.DailyMonitorDemeritRecordExp;
import com.ycl.platform.service.IDailyMonitorDemeritRecordService;
import com.ycl.platform.service.IDemeritRecordService;
import com.ycl.platform.service.IMonitorConstructionService;
import com.ycl.system.mapper.DailyMonitorDemeritRecordMapper;
import com.ycl.utils.DateUtils;
import enumeration.ConstructionTypeEnum;
import enumeration.general.AreaDeptEnum;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import pojo.ExcelExp;
import utils.poi.ExcelUtilManySheet;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
 * zgyw
 *
 * @author : zxl
 * @date : 2025-11-04 17:47
 **/
@Service
@RequiredArgsConstructor
public class DailyMonitorDemeritRecordServiceImpl extends ServiceImpl<DailyMonitorDemeritRecordMapper,DailyMonitorDemeritRecord>
        implements IDailyMonitorDemeritRecordService {
    private final DailyMonitorDemeritRecordMapper dailyMonitorDemeritRecordMapper;
    private final IDemeritRecordService demeritRecordService;
    @Override
    public void getExportInfo(HttpServletResponse httpServletResponse, Integer id) throws IOException {
        DemeritRecord demeritRecord = demeritRecordService.getBaseMapper().selectById(id);
        if (demeritRecord != null) {
            //获得详情
            Date recordTime = demeritRecord.getRecordTime();
            LocalDateTime dateTime = LocalDateTime.ofInstant(recordTime.toInstant(), ZoneId.systemDefault());
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            String formatted = dateTime.format(formatter);
            Integer deptId = demeritRecord.getDeptId();
            ConstructionTypeEnum constructionTypeEnum = ConstructionTypeEnum.getEnumByName(demeritRecord.getConstructionType());
            List<DailyMonitorDemeritRecord> list = new LambdaQueryChainWrapper<>(baseMapper)
                    .ge(DailyMonitorDemeritRecord::getRecordTime, DateUtils.getDayStart(recordTime))
                    .le(DailyMonitorDemeritRecord::getRecordTime, DateUtils.getDayEnd(recordTime))
                    .eq(DailyMonitorDemeritRecord::getDeptId, deptId)
                    .eq(DailyMonitorDemeritRecord::getConstructionType,constructionTypeEnum.getDesc())
                    .eq(DailyMonitorDemeritRecord::getDeleted, false)
                    .list();
            AreaDeptEnum areaDeptEnum = AreaDeptEnum.fromDept(deptId);
            List<DailyMonitorDemeritRecordExp> exps = new ArrayList<>();
            List<ExcelExp> mySheet = new ArrayList<>();
            for (DailyMonitorDemeritRecord dm : list) {
                DailyMonitorDemeritRecordExp dailyMonitorDemeritRecordExp = new DailyMonitorDemeritRecordExp();
                dailyMonitorDemeritRecordExp.setSerialNumber(dm.getSerialNumber());
                dailyMonitorDemeritRecordExp.setConstructionType(constructionTypeEnum.getDesc());
                if (areaDeptEnum != null) {
                    dailyMonitorDemeritRecordExp.setDept(areaDeptEnum.getName());
                }else{
                    dailyMonitorDemeritRecordExp.setDept("未知区域");
                }
                dailyMonitorDemeritRecordExp.setDemerit(dm.getDemerit());
                dailyMonitorDemeritRecordExp.setIsReport(dm.getIsReport() ? "已报备" :"未报备");
                dailyMonitorDemeritRecordExp.setRecordTime(formatted);
                dailyMonitorDemeritRecordExp.setDeviceName(dm.getDeviceName());
                exps.add(dailyMonitorDemeritRecordExp);
            }
            ExcelExp excelExp = new ExcelExp(areaDeptEnum == null ? "未知" : areaDeptEnum.getName(),exps,DailyMonitorDemeritRecordExp.class);
            mySheet.add(excelExp);
            ExcelUtilManySheet<List<ExcelExp>> util = new ExcelUtilManySheet<>(mySheet);
            util.exportExcelManySheet(httpServletResponse, mySheet);
        }
    }
}
ycl-server/src/main/java/com/ycl/platform/service/impl/DataCenterServiceImpl.java
@@ -1723,6 +1723,7 @@
     */
    @Override
    public Result videoOneMachineDocumentQualified(DataCenterQuery params) {
        long startTime = System.currentTimeMillis();
        List<String> likeFileds = Arrays.asList("ip.showValue", "name.showValue", "serialNumber.showValue");
        SysDept sysDept = getSysDeptByLoginUser();
        List<Criteria> andCriteria;
@@ -1781,99 +1782,131 @@
        long total = mongoTemplate.count(query, MonitorQualifyResult.class);
        MongoUtil.setPage(query, params, TIME_FIELD);
        List<MonitorQualifyResult> resultList = mongoTemplate.find(query, MonitorQualifyResult.class);
        List<MonitorQualifyResultVO> resultVOS = new ArrayList<>();
        for (MonitorQualifyResult result : resultList) {
            MonitorQualifyResultVO vo = MonitorQualifyResult.getVO(result);
            resultVOS.add(vo);
        }
        // 统计数量
        long endTime =  System.currentTimeMillis();
        // 统计数量(改造后:一次聚合返回所有结果)
        long startTime2 = System.currentTimeMillis();
        MongoDatabase database = mongoTemplate.getDb();
        MongoCollection<Document> collection = database.getCollection("uy_monitor_qualify");
        Document areDocument = null;
// 1. 构建公共过滤条件(deviceNo前缀 + tag过滤)
        List<Document> commonFilters = new ArrayList<>(2);
        if (areaDeptEnum != null) {
            String areaCodePrefix = areaDeptEnum.getCode();
            areDocument = new Document("deviceNo", new Document("$regex", "^" + areaCodePrefix));
            commonFilters.add(new Document("deviceNo", new Document("$regex", "^" + areaCodePrefix)));
        }
        //总数
        List<Document> dList1 = new ArrayList<>(2);
        if (areDocument != null){
            dList1.add(areDocument);
        }
        setTag(params, dList1);
        Document totalFilter = new Document("$and", dList1);
        //list1
        setTag(params, commonFilters); // 添加tag过滤条件
        Document commonFilter = commonFilters.isEmpty() ? new Document() : new Document("$and", commonFilters);
        //合格数
        List<Document> dList2 = new ArrayList<>(2);
        if (areDocument != null){
            dList2.add(areDocument);
        }
        dList2.add(new Document("serialNumber.error", Boolean.FALSE));
        dList2.add(new Document("name.error", Boolean.FALSE));
        dList2.add(new Document("civilCode.error", Boolean.FALSE));
        dList2.add(new Document("integrated_device.error", Boolean.FALSE));
        dList2.add(new Document("jkdwlx.error", Boolean.FALSE));
        dList2.add(new Document("latitude.error", Boolean.FALSE));
        dList2.add(new Document("longitude.error", Boolean.FALSE));
        dList2.add(new Document("macdz.error", Boolean.FALSE));
        dList2.add(new Document("name.error", Boolean.FALSE));
        dList2.add(new Document("sbzt.error", Boolean.FALSE));
        dList2.add(new Document("sxjcjqy.error", Boolean.FALSE));
        dList2.add(new Document("sxjgnlx.error", Boolean.FALSE));
        setTag(params, dList2);
        Document qualifyFilter = new Document("$and", dList2);
        //不合格数
        List<Document> dList3 = new ArrayList<>(2);
        if (areDocument != null){
            dList3.add(areDocument);
        }
        setTag(params, dList3);
        List<Document> errorConditions = new ArrayList<>();
        errorConditions.add(new Document("serialNumber.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("name.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("civilCode.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("integrated_device.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("jkdwlx.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("latitude.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("longitude.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("macdz.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("name.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("sbzt.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("sxjcjqy.error", new Document("$eq", Boolean.TRUE)));
        errorConditions.add(new Document("sxjgnlx.error", new Document("$eq", Boolean.TRUE)));
        Document errorFilter = new Document("$or", errorConditions);
        dList3.add(errorFilter);
        Document unQualifyFilter = new Document("$and", dList3);
// 2. 构建合格/不合格的error条件(复用原逻辑,去重name.error)
// 合格条件:所有error为false
        List<Document> qualifyErrorConditions = new ArrayList<>();
        qualifyErrorConditions.add(new Document("serialNumber.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("name.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("civilCode.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("integrated_device.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("jkdwlx.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("latitude.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("longitude.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("macdz.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("sbzt.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("sxjcjqy.error", Boolean.FALSE));
        qualifyErrorConditions.add(new Document("sxjgnlx.error", Boolean.FALSE));
        Document qualifyFilter = new Document("$and", qualifyErrorConditions);
        List<Document> lists = Arrays.asList(totalFilter, qualifyFilter, unQualifyFilter);
        List<String> rList = lists.stream().map(filter -> {
            // 构建聚合管道
            List<Document> pipeline = Arrays.asList(
                    new Document("$match", filter),
                    // $group 去重
                    new Document("$group", new Document("_id", "$serialNumber.showValue")),
                    new Document("$count", "uniqueDeviceIds")
            );
            // 执行聚合查询并获取结果
            AggregateIterable<Document> result = collection.aggregate(pipeline);
            Integer uniqueDeviceIdCount = 0;
            for (Document doc : result) {
                uniqueDeviceIdCount = doc.getInteger("uniqueDeviceIds");
                break; // 不需要继续遍历,因为 $count 只会产生一个结果
            }
            return uniqueDeviceIdCount + "";
        }).collect(Collectors.toList());
// 不合格条件:至少一个error为true
        List<Document> unQualifyErrorConditions = new ArrayList<>();
        unQualifyErrorConditions.add(new Document("serialNumber.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("name.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("civilCode.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("integrated_device.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("jkdwlx.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("latitude.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("longitude.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("macdz.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("sbzt.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("sxjcjqy.error", Boolean.TRUE));
        unQualifyErrorConditions.add(new Document("sxjgnlx.error", Boolean.TRUE));
        Document unQualifyFilter = new Document("$or", unQualifyErrorConditions);
// 3. 构建一次聚合管道(用$facet合并三个统计维度)
        List<Document> pipeline = new ArrayList<>();
// 步骤1:先应用公共过滤条件(deviceNo + tag),减少后续处理数据量
        if (!commonFilters.isEmpty()) {
            pipeline.add(new Document("$match", commonFilter));
        }
// 步骤2:用$facet分面查询,同时计算总数、合格数、不合格数
        pipeline.add(new Document("$facet", new Document()
                // 总数:按serialNumber.showValue去重后计数
                .append("total", Arrays.asList(
                        new Document("$group", new Document("_id", "$serialNumber.showValue")),
                        new Document("$count", "uniqueDeviceIds")
                ))
                // 合格数:先过滤合格条件,再去重计数
                .append("qualify", Arrays.asList(
                        new Document("$match", qualifyFilter),
                        new Document("$group", new Document("_id", "$serialNumber.showValue")),
                        new Document("$count", "uniqueDeviceIds")
                ))
                // 不合格数:先过滤不合格条件,再去重计数
                .append("unQualify", Arrays.asList(
                        new Document("$match", unQualifyFilter),
                        new Document("$group", new Document("_id", "$serialNumber.showValue")),
                        new Document("$count", "uniqueDeviceIds")
                ))
        ));
//        Document explainResult = collection.aggregate(pipeline).explain();
//
//// 打印执行计划(日志或控制台输出)
//        log.info("聚合查询执行计划:{}", explainResult.toJson());
// 4. 执行聚合查询(仅一次数据库交互)
        AggregateIterable<Document> facetResult = collection.aggregate(pipeline).allowDiskUse(true);
        Document resultDoc = facetResult.iterator().next(); // $facet仅返回一个文档
// 5. 解析结果(处理空结果场景,默认计数为0)
        int totalCount = parseFacetCount(resultDoc, "total");
        int qualifyCount = parseFacetCount(resultDoc, "qualify");
        int unQualifyCount = parseFacetCount(resultDoc, "unQualify");
// 6. 组装rList(保持原格式不变,后续逻辑无需修改)
        List<String> rList = new ArrayList<>(Arrays.asList(
                String.valueOf(totalCount),
                String.valueOf(qualifyCount),
                String.valueOf(unQualifyCount)
        ));
// 原合格率计算逻辑不变
        BigDecimal onlineRate = BigDecimal.ZERO;
        if (!StringUtils.isEmpty(rList.get(0)) && !"0".equals(rList.get(0))) {
            onlineRate = new BigDecimal(rList.get(1)).divide(new BigDecimal(rList.get(0)), 3,RoundingMode.DOWN).multiply(new BigDecimal("100"));
        if (totalCount != 0) {
            onlineRate = new BigDecimal(qualifyCount)
                    .divide(new BigDecimal(totalCount), 3, RoundingMode.DOWN)
                    .multiply(new BigDecimal("100"));
        }
        rList.add(this.remove0(onlineRate));
        long endTime2 = System.currentTimeMillis();
        log.info("统计耗时:{} ms", (endTime2 - startTime2));
        rList.add(this.remove0(onlineRate));
        HashMap<String, Object> map = new HashMap<>();
        map.put("count", rList);
        map.put("list", resultVOS);
        return Result.ok().data(map).total(total);
    }
    private int parseFacetCount(Document resultDoc, String facetName) {
        List<Document> facetList = (List<Document>) resultDoc.get(facetName);
        if (facetList == null || facetList.isEmpty()) {
            return 0;
        }
        Document countDoc = facetList.get(0);
        return countDoc.getInteger("uniqueDeviceIds", 0);
    }
    /**
     * 视频:档案考核比
     * 档案留存总量:mongo存的所有去重后的档案
ycl-server/src/main/java/com/ycl/platform/service/impl/IMonitorConstructionServiceImpl.java
@@ -9,6 +9,7 @@
import com.ycl.platform.domain.dto.ReportImportDTO;
import com.ycl.platform.domain.entity.MonitorConstruction;
import com.ycl.platform.mapper.IMonitorConstructionMapper;
import com.ycl.platform.service.IDailyMonitorDemeritRecordService;
import com.ycl.platform.service.IMonitorConstructionService;
import com.ycl.system.Result;
import com.ycl.utils.StringUtils;
@@ -41,6 +42,7 @@
public class IMonitorConstructionServiceImpl extends ServiceImpl<IMonitorConstructionMapper, MonitorConstruction>
        implements IMonitorConstructionService {
    private final IMonitorConstructionMapper monitorConstructionMapper;
    private final IDailyMonitorDemeritRecordService dailyMonitorDemeritRecordService;
    @Override
    @Transactional(rollbackFor = Exception.class)
ycl-server/src/main/java/com/ycl/platform/service/impl/ReportServiceImpl.java
@@ -1,5 +1,6 @@
package com.ycl.platform.service.impl;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
@@ -33,6 +34,7 @@
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -50,6 +52,7 @@
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> implements ReportService {
    private final YwUnitMapper unitMapper;
@@ -266,9 +269,15 @@
        if (query.getEffectTimeEnd() != null) {
            query.setEffectTimeEnd(DateUtils.getDayEnd(query.getEffectTimeEnd()));
        }
        long dbStartTime = System.currentTimeMillis();
        baseMapper.page(page, query);
        long dbCostTime = System.currentTimeMillis() - dbStartTime;
        // 5. 打印耗时日志(含请求参数,方便后续排查慢查询)
        log.info("报表数据库查询耗时:{}ms,查询参数:{}", dbCostTime, JSON.toJSONString(query));
        long dictStartTime = System.currentTimeMillis();
        List<SysDictData> errorTypeList = dictTypeService.selectDictDataByType("report_error_type");
        long dictEndTime = System.currentTimeMillis() - dictStartTime;
        Map<String, String> dictMap = errorTypeList.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
        page.getRecords().stream().forEach(item -> {
            if (StringUtils.hasText(item.getErrorType())) {
@@ -283,6 +292,7 @@
                item.setErrorType(sb.substring(0, sb.length() - 1));
            }
        });
        log.info("组装信息耗时:{}ms", dictEndTime);
        return Result.ok().data(page).total(page.getTotal());
    }
ycl-server/src/main/java/com/ycl/platform/service/impl/TMonitorServiceImpl.java
@@ -28,10 +28,7 @@
import com.ycl.platform.domain.vo.home.HomeVideoVO;
import com.ycl.platform.domain.vo.screen.MonitorRateVO;
import com.ycl.platform.domain.vo.screen.MonitorTotalVO;
import com.ycl.platform.mapper.DynamicColumnMapper;
import com.ycl.platform.mapper.TMonitorMapper;
import com.ycl.platform.mapper.WorkOrderMapper;
import com.ycl.platform.mapper.YwPointMapper;
import com.ycl.platform.mapper.*;
import com.ycl.platform.service.IMonitorConstructionService;
import com.ycl.platform.service.ITMonitorService;
import com.ycl.system.Result;
@@ -1366,18 +1363,38 @@
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        // 获取月份第一天的Date
        Date startDate = calendar.getTime();
        log.info("首页查询开始日期:{}",startDate);
        // 设置Calendar为月份的最后一天(通过增加一个月份然后减去一天)
        calendar.add(Calendar.MONTH, 1);
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        // 获取月份最后一天的Date
        Date endDate = calendar.getTime();
        log.info("首页查询结束日期:{}",endDate);
        String constructionType = monitorQuery.getConstructionType();
        List<String> deviceNoList = new ArrayList<>();
        if (StringUtils.isNotBlank(constructionType)){
            List<MonitorConstruction> serialNumberListByConstructionType = monitorConstructionService.getSerialNumberListByConstructionType(constructionType);
            if (!CollectionUtils.isEmpty(serialNumberListByConstructionType)){
                deviceNoList = serialNumberListByConstructionType.stream().map(MonitorConstruction::getSerialNumber).collect(Collectors.toList());
            }
        }
        if (StringUtils.isNotBlank(constructionType) && deviceNoList.isEmpty()) {
            // 当constructionType有值但无对应设备时,返回包含空列表的Map
            Map<String, Object> emptyResult = new HashMap<>();
            emptyResult.put("list", results); // results此时为空列表
            emptyResult.put("baseLine", 0); // 可根据业务默认基准线值调整
            return emptyResult;
        }
        //mongo查录像状态
        MongoDatabase database = mongoTemplate.getDb();
        MongoCollection<Document> collection = database.getCollection("uy_record_meta_d_sum");
        Integer examineTag = monitorQuery.getExamineTag();
        String arealayerno = monitorQuery.getArea();
        Document matchConditions = new Document("statTime", new Document("$gte", startDate).append("$lte", endDate));
        if (!deviceNoList.isEmpty()) {
            log.info("打印分建类型no集合:{}",deviceNoList);
            matchConditions.append("no", new Document("$in", deviceNoList));
        }
        // 根据examineTag的值动态添加额外的条件
        if (examineTag != null && examineTag.equals(1)) {
            matchConditions.append("provinceTag", true);
@@ -1445,6 +1462,9 @@
        List<Document> onlineMatch = new ArrayList<>();
        onlineMatch.add(new Document("mongoCreateTime", new Document("$gte", startDate).append("$lte", endDate)));
        onlineMatch.add(new Document("monitorType", new Document("$regex", "1")));
        if (!deviceNoList.isEmpty()) {
            onlineMatch.add(new Document("no", new Document("$in", deviceNoList)));
        }
        if (examineTag != null && examineTag.equals(1)) {
            onlineMatch.add(new Document("provinceTag", true));
        } else if (examineTag != null && examineTag.equals(2)) {
ycl-server/src/main/java/com/ycl/platform/service/impl/WorkOrderServiceImpl.java
@@ -874,6 +874,43 @@
                }
            });
        }
        //加上报备信息
        if (!CollectionUtils.isEmpty(page.getRecords())) {
            List<String> serialNumberList = page.getRecords().stream()
                    .map(WorkOrderVO::getSerialNumber).collect(Collectors.toList());
            List<Report> reports = new LambdaQueryChainWrapper<>(reportMapper)
                    .in(Report::getSerialNumber, serialNumberList)
                    .eq(Report::getStatus,1)
                    .eq(Report::getDeleted,Boolean.FALSE).list();
            Map<String, List<Report>> reportMap = reports.stream()
                    .collect(Collectors.groupingBy(Report::getSerialNumber));
            for (WorkOrderVO workOrder : page.getRecords()) {
                workOrder.setHasReport(false);
                List<Report> reportsByWorkOrder = reportMap.get(workOrder.getSerialNumber());
                if (CollectionUtils.isEmpty(reportsByWorkOrder)) {
                    continue; // 无报告,直接跳过
                }
                Date distributeTime = workOrder.getDistributeTime();
                for (Report report : reportsByWorkOrder) {
                    Date beginTime = report.getBeginCreateTime();
                    Date endTime = report.getEndCreateTime();
                    // 空指针防护
                    if (distributeTime == null || beginTime == null || endTime == null) {
                        continue;
                    }
                    // 时间区间判断:distributeTime 在 [beginTime, endTime] 内
                    boolean isInRange = distributeTime.compareTo(beginTime) >= 0
                            && distributeTime.compareTo(endTime) <= 0;
                    if (isInRange) {
                        workOrder.setHasReport(true);
                        break; // 找到一个即退出循环
                    }
                }
            }
        }
        return Result.ok().data(page.getRecords()).total(page.getTotal());
    }
@@ -1088,8 +1125,10 @@
    @Override
    public WorkOrderTotalVO workOrderTotal(DashboardQuery dashboardQuery) {
        //todo大屏工单未处理工单数量有点问题
        return baseMapper.workOrderTotal(dashboardQuery);
    }
    @Override
    public List<WorkOrderRegionVO> workOrderRegion(DashboardQuery dashboardQuery) {
@@ -1303,23 +1342,30 @@
        }
        // 是否报备
        Date target = workOrder.getCreateTime();
        Report reports = new LambdaQueryChainWrapper<>(reportMapper)
                .eq(Report::getSerialNumber,workOrder.getWorkOrderNo())
        List<Report> reports = new LambdaQueryChainWrapper<>(reportMapper)
                .eq(Report::getSerialNumber,workOrder.getSerialNumber())
                .eq(Report::getStatus,1)
                .orderByDesc(Report::getCreateTime)
                .last("LIMIT 1")
                .one();
        boolean isInRange = false;
        if (reports != null){
            Date start = reports.getBeginCreateTime();
            Date end = reports.getEndCreateTime();
            isInRange = target.after(start) || target.equals(start)  // target >= start
                    && target.before(end) || target.equals(end);
                .list();
        log.info("报备记录:{}",reports);
        Date distributeTime = workOrder.getDistributeTime();
        workOrder.setHasReport(false);
        for (Report report : reports) {
            Date beginTime = report.getBeginCreateTime();
            Date endTime = report.getEndCreateTime();
            // 空指针防护
            if (distributeTime == null || beginTime == null || endTime == null) {
                continue;
            }
            // 时间区间判断:distributeTime 在 [beginTime, endTime] 内
            boolean isInRange = distributeTime.compareTo(beginTime) >= 0
                    && distributeTime.compareTo(endTime) <= 0;
            if (isInRange) {
                workOrder.setHasReport(true);
                break; // 找到一个即退出循环
            }
        }
        workOrder.setHasReport(isInRange);
        // 故障类型
        List<SysDictData> errorList = workOrderErrorTypeService.getBaseMapper().getErrorList(workOrder.getWorkOrderNo());
        List<String> errList = errorList.stream().map(SysDictData::getDictLabel).collect(Collectors.toList());
ycl-server/src/main/java/com/ycl/system/mapper/DailyMonitorDemeritRecordMapper.java
New file
@@ -0,0 +1,21 @@
package com.ycl.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ycl.platform.domain.entity.DailyMonitorDemeritRecord;
import com.ycl.platform.domain.form.DailyMonitorDemeritRecordForm;
import com.ycl.system.Result;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.ibatis.annotations.Mapper;
/**
 * zgyw
 *
 * @author : zxl
 * @date : 2025-11-04 17:48
 **/
@Mapper
public interface DailyMonitorDemeritRecordMapper extends BaseMapper<DailyMonitorDemeritRecord> {
}
ycl-server/src/main/java/com/ycl/task/DemeritRecordTask.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.google.common.util.concurrent.AtomicDouble;
import com.ycl.platform.domain.entity.DailyMonitorDemeritRecord;
import com.ycl.platform.domain.entity.DemeritRecord;
import com.ycl.platform.domain.entity.MonitorConstruction;
import com.ycl.platform.domain.entity.Report;
@@ -11,6 +12,7 @@
import com.ycl.platform.mapper.DemeritRecordMapper;
import com.ycl.platform.mapper.IMonitorConstructionMapper;
import com.ycl.platform.mapper.ReportMapper;
import com.ycl.platform.service.IDailyMonitorDemeritRecordService;
import com.ycl.platform.service.IDemeritRecordService;
import com.ycl.utils.DateUtils;
import enumeration.ConstructionTypeEnum;
@@ -54,6 +56,8 @@
    private final IMonitorConstructionMapper monitorConstructionMapper;
    private final IDailyMonitorDemeritRecordService dailyMonitorDemeritRecordService;
    private static final ExecutorService executorService = new ThreadPoolExecutor(16,
            128,
            5000,
@@ -63,6 +67,8 @@
    );
    private final IDemeritRecordService demeritRecordService;
    private final IDailyMonitorDemeritRecordService iDailyMonitorDemeritRecordService;
    private DemeritRecord buildDemeritRecord(String constructionType,BigDecimal demerit,Integer deptId,Date recordTime) {
        DemeritRecord record = new DemeritRecord();
        record.setConstructionType(constructionType);
@@ -71,7 +77,19 @@
        record.setRecordTime(recordTime);
        return record;
    }
    private BigDecimal calculateTotalDeduction(List<RecordMetaDSumResult> records) {
    private DailyMonitorDemeritRecord buildDailyMonitorDemeritRecord(Date recordTime,String deviceId,String constructionType,BigDecimal demerit,Integer deptId,Boolean isReport,String deviceName) {
        DailyMonitorDemeritRecord demeritRecord  = new DailyMonitorDemeritRecord();
        demeritRecord.setRecordTime(recordTime);
        demeritRecord.setDemerit(demerit);
        demeritRecord.setConstructionType(constructionType);
        demeritRecord.setSerialNumber(deviceId);
        demeritRecord.setIsReport(isReport);
        demeritRecord.setDeptId(deptId);
        demeritRecord.setDeviceName(deviceName);
        return demeritRecord;
    }
    private BigDecimal calculateTotalDeduction(List<RecordMetaDSumResult> records,Date recordTime,Integer deptId,List<DailyMonitorDemeritRecord> dailyMonitorDemeritRecords) {
        BigDecimal total = BigDecimal.ZERO; // 单线程循环,无需AtomicReference
        for (RecordMetaDSumResult record : records) {
@@ -94,6 +112,8 @@
            } else {
                deduction = BigDecimal.ZERO;
            }
            dailyMonitorDemeritRecords.add(buildDailyMonitorDemeritRecord(recordTime,record.getDeviceId()
                    ,record.getConstructionType(),deduction,deptId,false,record.getDeviceName()));
            // 累加总扣分(最后统一处理精度,避免中间四舍五入误差)
            total = total.add(deduction);
@@ -143,19 +163,34 @@
        List<MonitorConstruction> monitorConstructionList = new LambdaQueryChainWrapper<>(monitorConstructionMapper)
                .eq(MonitorConstruction::getDeleted, Boolean.FALSE)
                .list();
        List<String> serialNumberList = monitorConstructionList.stream()
                .map(MonitorConstruction::getSerialNumber).collect(Collectors.toList());
        //过滤获得包含了这些标签的设备录像情况集合
        //按编号分组,值为设备对应标签
        Map<String, String> serialTagMap = monitorConstructionList.stream()
                .filter(mc -> mc.getSerialNumber() != null) // 过滤序列号为空的无效数据
                .collect(Collectors.toMap(
                        MonitorConstruction::getSerialNumber,
                        MonitorConstruction::getTag,
                        (oldVal, newVal) -> newVal
                ));
        //过滤掉没有标签的集合 并将标签赋值给录像情况集合
        results = results.stream()
                .filter(result -> {
                    String sn = result.getNo();
                    // 任一字段非空且在集合中即可
                    return (sn != null && serialNumberList.contains(sn));
                    // 过滤条件:序列号非空 + 在标签Map中存在(即有对应标签)
                    boolean isMatch = sn != null && serialTagMap.containsKey(sn);
                    if (isMatch) {
                        // 匹配成功,将标签赋值给result(需确保RecordMetaDSumResult有setTag()方法)
                        String tag = serialTagMap.get(sn);
                        result.setConstructionType(tag); // 关键:赋值标签
                    }
                    return isMatch; // 只保留有标签的result
                })
                .collect(Collectors.toList());
        log.info("剩余考核设备过滤后设备数{}",results.size());
        log.info("有标签的设备记录集合大小:{}",results.size());//这是需要计算扣分的录像数据
        //过滤掉报备的设备
        //查询在当前时间有报备的所有设备,
@@ -186,19 +221,14 @@
                .map(Report::getSerialNumber).collect(Collectors.toList());
        Set<String> deviceIdSet = new HashSet<>(deviceIds);
        log.info("报备设备数{}",deviceIdSet.size());
        results = results.stream()
                .filter(result -> {
                    // 获取当前对象的deviceId
                    String resultDeviceId = result.getDeviceId();
                    // 过滤条件:deviceId不在集合中(注意处理null值,避免NPE)
                    return resultDeviceId != null && !deviceIdSet.contains(resultDeviceId);
                })
                .collect(Collectors.toList());
        log.info("剩余过滤报备后设备数{}",results.size());
        // 按区域划分 组装成map
        List<DailyMonitorDemeritRecord> dailyMonitorDemeritRecords = new ArrayList<>();
        Date yesterdayBegin = DateUtils.getDayStart(yesterday);
        // 遍历区分对象的报备状态
        // 因为下面会过滤覆盖掉考核设备,需要已报备的设备录像情况信息,
        // 所以此处应该将已报备的设备录像情况信息添加到每日扣分详情记录中并初始化好 后续不在处理直接添加数据库中
        // 将区域信息 放入集合中
        results.forEach(item -> {
            String areaCode = item.getArealayername().substring(0, 6);
            AreaDeptEnum areaDeptEnum = AreaDeptEnum.fromCode(areaCode);
@@ -206,9 +236,42 @@
                item.setArealayerno(areaDeptEnum.getCode());
            }
        });
        results.forEach(result ->{
            String deviceId = result.getDeviceId();
            if (StringUtils.isNotBlank(deviceId)) {
                if (deviceIdSet.contains(deviceId)) {
                    //已报备设备记录
                    AreaDeptEnum areaDeptEnum = AreaDeptEnum.fromCode(result.getArealayerno());
                    if (areaDeptEnum == null) {
                        log.info("区域数据异常,异常区域键值:{}",result.getArealayerno());
                        return;
                    }
                    DailyMonitorDemeritRecord demeritRecord  = buildDailyMonitorDemeritRecord(
                            yesterdayBegin,deviceId,
                            result.getConstructionType(),
                            BigDecimal.ZERO,
                            areaDeptEnum.getDeptId(),
                            true,
                            result.getDeviceName()
                    );
                    dailyMonitorDemeritRecords.add(demeritRecord);
                }
            }
        });
        // 过滤获得未报备集合
        results = results.stream()
                .filter(result -> {
                    String resultDeviceId = result.getDeviceId();
                    return resultDeviceId != null && !deviceIdSet.contains(resultDeviceId);
                })
                .collect(Collectors.toList());
        log.info("剩余过滤报备后设备数{}",results.size());
        //需要添加数据库的数据集合
        List<DemeritRecord> demeritRecords = new ArrayList<>();
        // 按区域划分 组装成map
        Map<String,List<RecordMetaDSumResult>> groupByArealayerno = results.stream()
                .collect(Collectors.groupingBy(RecordMetaDSumResult::getArealayerno));
@@ -290,17 +353,17 @@
                        .collect(Collectors.toList());
                log.info("沿滩二期人脸{}", yan_tan_phase_two_face.size());
                //一二期
                buildAndAddDemeritRecords(phase_one_two, ConstructionTypeEnum.PHASE_ONE_TWO.name(), areaDeptEnum.getDeptId(),yesterday,demeritRecords);
                buildAndAddDemeritRecords(phase_one_two, ConstructionTypeEnum.PHASE_ONE_TWO.name(), areaDeptEnum.getDeptId(),yesterdayBegin,demeritRecords,dailyMonitorDemeritRecords);
                //三期
                buildAndAddDemeritRecords(phase_three, ConstructionTypeEnum.PHASE_THREE.name(), areaDeptEnum.getDeptId(),yesterday,demeritRecords);
                buildAndAddDemeritRecords(phase_three, ConstructionTypeEnum.PHASE_THREE.name(), areaDeptEnum.getDeptId(),yesterdayBegin,demeritRecords,dailyMonitorDemeritRecords);
                //四期
                buildAndAddDemeritRecords(phase_fourth, ConstructionTypeEnum.PHASE_FOURTH.name(), areaDeptEnum.getDeptId(),yesterday,demeritRecords);
                buildAndAddDemeritRecords(phase_fourth, ConstructionTypeEnum.PHASE_FOURTH.name(), areaDeptEnum.getDeptId(),yesterdayBegin,demeritRecords,dailyMonitorDemeritRecords);
                //入川即检
                buildAndAddDemeritRecords(check_enter_sichuan, ConstructionTypeEnum.CHECK_ENTER_SICHUAN.name(), areaDeptEnum.getDeptId(),yesterday,demeritRecords);
                buildAndAddDemeritRecords(check_enter_sichuan, ConstructionTypeEnum.CHECK_ENTER_SICHUAN.name(), areaDeptEnum.getDeptId(),yesterdayBegin,demeritRecords,dailyMonitorDemeritRecords);
                //东部新城
                buildAndAddDemeritRecords(eastern_new_city, ConstructionTypeEnum.EASTERN_NEW_CITY.name(), areaDeptEnum.getDeptId(),yesterday,demeritRecords);
                buildAndAddDemeritRecords(eastern_new_city, ConstructionTypeEnum.EASTERN_NEW_CITY.name(), areaDeptEnum.getDeptId(),yesterdayBegin,demeritRecords,dailyMonitorDemeritRecords);
                //沿滩二期人脸
                buildAndAddDemeritRecords(yan_tan_phase_two_face, ConstructionTypeEnum.YAN_TAN_PHASE_TWO_FACE.name(), areaDeptEnum.getDeptId(),yesterday,demeritRecords);
                buildAndAddDemeritRecords(yan_tan_phase_two_face, ConstructionTypeEnum.YAN_TAN_PHASE_TWO_FACE.name(), areaDeptEnum.getDeptId(),yesterdayBegin,demeritRecords,dailyMonitorDemeritRecords);
            }
        }
@@ -308,32 +371,39 @@
        //处理完数据插入数据库中
        //先删除需要插入时间是否存在数据
        LambdaQueryWrapper<DemeritRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ge(DemeritRecord::getCreateTime,DateUtils.getDayStart(today))
        LambdaQueryWrapper<DemeritRecord> demeritRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        demeritRecordLambdaQueryWrapper.ge(DemeritRecord::getCreateTime,DateUtils.getDayStart(today))
                        .le(DemeritRecord::getCreateTime,DateUtils.getDayEnd(today));
        demeritRecordService.remove(queryWrapper);
        demeritRecordService.remove(demeritRecordLambdaQueryWrapper);
        demeritRecordService.saveBatch(demeritRecords);
        log.info("结束计算每日扣分记录情况:插入数据量{},数据信息:{}",demeritRecords.size(),demeritRecords);
        //填充设备录像情况扣分详情结果
        LambdaQueryWrapper<DailyMonitorDemeritRecord> dailyMonitorDemeritRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        dailyMonitorDemeritRecordLambdaQueryWrapper.ge(DailyMonitorDemeritRecord::getCreateTime,DateUtils.getDayStart(today))
                .le(DailyMonitorDemeritRecord::getCreateTime,DateUtils.getDayEnd(today));
        iDailyMonitorDemeritRecordService.remove(dailyMonitorDemeritRecordLambdaQueryWrapper);
        iDailyMonitorDemeritRecordService.saveBatch(dailyMonitorDemeritRecords);
        log.info("结束计算每日扣分记录详情情况:插入数据量{},数据信息:{}",dailyMonitorDemeritRecords.size(),dailyMonitorDemeritRecords);
    }
    public void buildAndAddDemeritRecords(List<RecordMetaDSumResult> constructionByRecordMetaList,
                                          String constructionType,Integer areaDeptId,Date recordTime,
                                          List<DemeritRecord> demeritRecords) {
                                          List<DemeritRecord> demeritRecords,
                                          List<DailyMonitorDemeritRecord> dailyMonitorDemeritRecords) {
        if (CollectionUtils.isNotEmpty(constructionByRecordMetaList)) {
                    BigDecimal deduction = calculateTotalDeduction(constructionByRecordMetaList);
                    BigDecimal deduction = calculateTotalDeduction(constructionByRecordMetaList,recordTime,areaDeptId,dailyMonitorDemeritRecords);
                    DemeritRecord demeritRecord = buildDemeritRecord(
                            constructionType,
                            deduction,
                            areaDeptId,
                            DateUtils.getDayStart(recordTime));
                            recordTime);
                    demeritRecords.add(demeritRecord);
                }else{
                    DemeritRecord demeritRecord = buildDemeritRecord(
                            constructionType,
                            BigDecimal.ZERO,
                            areaDeptId,
                            DateUtils.getDayStart(recordTime));
                            recordTime);
                    demeritRecords.add(demeritRecord);
        }
    }
ycl-server/src/main/java/com/ycl/task/UYTask.java
@@ -182,8 +182,6 @@
     * online字段来自于优云,pingOnline为主动ping检测的。存入mongo给数据中心查阅
     */
    public void pointOnline() throws ExecutionException, InterruptedException {
        log.info("开始检测点位在线");
        Integer times = 2;
        SysConfig config = new SysConfig();
@@ -194,7 +192,6 @@
        } else {
            log.error("请配置离线次数,此次设置为默认值2");
        }
        // 先查出设备IP集合,剔除掉在线情况是未知的,并且只检测正在考核的设备避免多余工单
        List<TMonitorResult> monitorList = monitorMapper.getDistinctIP();
@@ -254,6 +251,10 @@
        //查出数据库纯车辆或纯人脸设备
//        List<String> serialNumbers = monitorMapper.selectCarOrFace().stream().map(TMonitor::getSerialNumber).collect(Collectors.toList());
        dataList.forEach(item->{
            if ("DX_长征大道贡兴路路口东南方向_枪机_细节".equals(item.getName())){
                log.info("2.数据流入:{}",item);
                log.info("打印状态pingOnline:{}",item.getPingOnline());
            }
            if(item.getPingOnline()) {
                onLineList.add(item.getIp());
            } else if(!item.getPingOnline()) {
@@ -264,18 +265,16 @@
            }
        });
        if(!CollectionUtils.isEmpty(onLineList)){
            log.error("在线点位集合:{}",onLineList);
        }
        log.error("unKnownList:{}",unKnownList);
        log.error("通过pingOnline未知点位的集合大小:{}",unKnownList.size());
        log.error("通过pingOnline修改离线线的点位集合大小:{}",offLineList.size());
        log.error("通过pingOnline修改在线的点位集合大小:{}",onLineList.size());
        if(!CollectionUtils.isEmpty(offLineList)) {
            log.error("修改离线的点位集合:{}",offLineList);
            monitorMapper.batchUpdateOnline(offLineList, now, ApiConstants.UY_OnlineSite_Offline);
            monitorMapper.batchUpdatePingOnline(offLineList, now, ApiConstants.UY_OnlineSite_Offline);
        }
        if(!CollectionUtils.isEmpty(onLineList)) {
            log.error("修改在线的点位集合:{}",onLineList);
            monitorMapper.batchUpdateOnline(onLineList, now, ApiConstants.UY_OnlineSite_Online);
            monitorMapper.batchUpdatePingOnline(onLineList, now, ApiConstants.UY_OnlineSite_Online);
        }
        //存放到mongo
        if (!CollectionUtils.isEmpty(dataList)) {
@@ -374,6 +373,31 @@
                            vo.setUpdateTime(now);
                            return vo;
                        }).collect(Collectors.toList());
                        log.info("打印点位在线(优云)需要修改的状态:{}",willUpdateList);
                        log.info("打印点位在线(优云)需要修改的大小:{}",willUpdateList.size());
                        Map<Integer, Long> statusCountMap = willUpdateList.stream()
                                .collect(Collectors.groupingBy(
                                        item -> {
                                            int online = item.getOnline();
                                            // 自定义分组规则:-1、1 单独分组,其他归为 0(或其他标识)
                                            if (online == -1) {
                                                return -1;
                                            } else if (online == 1) {
                                                return 1;
                                            } else {
                                                return 0; // 用 0 代表其他状态
                                            }
                                        },
                                        Collectors.counting() // 统计每个分组的数量
                                ));
                        // 从map中获取各状态的数量(默认值为0,避免空指针)
                        long offlineCount = statusCountMap.getOrDefault(-1, 0L);   // -1状态的数量
                        long onlineCount = statusCountMap.getOrDefault(1, 0L);     // 1状态的数量
                        long otherCount = statusCountMap.getOrDefault(0, 0L);      // 其他状态的数量
                        log.info("打印点位在线(优云)需要修改离线大小:{}",offlineCount);
                        log.info("打印点位在线(优云)需要修改在线大小:{}",onlineCount);
                        log.info("打印点位在线(优云)需要修改其他状态大小:{}",otherCount);
                        monitorMapper.updateOnlineFromUyOrHk(willUpdateList);
                        //离线生成工单,一个ip只生成一个工单
                        List<VideoOnlineResult> workOrders = new ArrayList<>(records.stream()
ycl-server/src/main/java/com/ycl/thread/OnlineCheckThread.java
@@ -69,6 +69,9 @@
        }else {
            result.setPingOnlineStr("-1");
        }
        if ("DX_长征大道贡兴路路口东南方向_枪机_细节".equals(result.getName())){
            log.info("1.打印查出设备信息:{}",result);
        }
        // 现在需要查出状态为未知的设备
        if (result.getCreateWorkOrder()!=null && result.getCreateWorkOrder()) {
            WorkOrder workOrder = new WorkOrder();
ycl-server/src/main/resources/mapper/zgyw/ReportMapper.xml
@@ -32,9 +32,9 @@
               GROUP_CONCAT(DISTINCT ret.error_type SEPARATOR ',') AS errorType
        FROM
        t_report r
        LEFT JOIN t_yw_unit u ON r.unit_id = u.id and u.deleted = 0
        LEFT JOIN (SELECT id,unit_name FROM t_yw_unit WHERE deleted =0) u ON r.unit_id = u.id
        LEFT JOIN t_yw_people p ON r.people_id = p.user_id and p.deleted = 0
        LEFT JOIN t_yw_point pt ON r.serial_number = pt.serial_number and pt.deleted = 0
        LEFT JOIN (SELECT serial_number,point_name FROM t_yw_point WHERE deleted =0) pt ON r.serial_number = pt.serial_number
        INNER JOIN t_report_error_type ret ON ret.report_id = r.id and ret.deleted = 0 <if test="query.errorTypeList != null and query.errorTypeList.size() > 0">
        AND ret.error_type in <foreach collection="query.errorTypeList" open="(" separator="," close=")" item="errorType">#{errorType}</foreach>
        </if>
@@ -62,13 +62,15 @@
            AND r.end_create_time &lt;= #{query.effectTimeEnd}
        </if>
        GROUP BY
            r.id, r.report_materials, r.create_time, r.report_type, r.report_content, r.status, r.serial_number,
            r.begin_create_time,
            r.end_create_time,
            r.import_batch_number,
            u.unit_name,
            p.yw_person_name,
            pt.point_name
            r.id
--                ,
--             r.report_materials, r.create_time, r.report_type, r.report_content, r.status, r.serial_number,
--             r.begin_create_time,
--             r.end_create_time,
--             r.import_batch_number,
--             u.unit_name,
--             p.yw_person_name,
--             pt.point_name
        ORDER BY r.update_time DESC
    </select>
ycl-server/src/main/resources/mapper/zgyw/TMonitorMapper.xml
@@ -97,6 +97,8 @@
            #{number}
        </foreach>
    </select>
    <!-- TODO:异常恢复监控和是否产生工单字段-->
    <select id="selectTMonitorList" resultType="com.ycl.platform.domain.vo.TMonitorVO">
        select m.id, m.serial_number, name, site_type, mac_addr, ip, camera_fun_type, longitude, latitude,
@@ -256,6 +258,7 @@
    </select>
<!--  TODO  -->
    <select id="exportTMonitorList" resultType="com.ycl.platform.domain.excel.TMonitorExp">
        select m.id, m.serial_number, name, ip, camera_fun_type,
        CASE
@@ -288,6 +291,7 @@
            <if test="onState != null and onState == -1">and (p.online = #{onState} or p.ping_online = #{onState})</if>
            <if test="onState != null and onState == 0">and (p.online = #{onState} and p.ping_online = #{onState})</if>
            <if test="civilCode != null  and civilCode != ''">and civil_code = #{civilCode}</if>
            <if test="address != null and address !=''">and d.dept_id = #{address}</if>
        </where>
    </select>
    <select id="selectMonitorVOList" resultType="com.ycl.platform.domain.vo.TMonitorVO">
@@ -663,9 +667,18 @@
            COUNT(p2.id) AS errorNum,
            COUNT(p3.id)  AS normalNum
        FROM t_monitor
        LEFT JOIN t_yw_point p1 ON t_monitor.serial_number = p1.serial_number AND p1.examine_status = 1<if test="dataScope == 1"> AND p1.province_tag_video = 1 </if><if test="dataScope == 3"> AND p1.dept_tag = 1 </if><if test="deptId != null"> AND p1.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p2 ON t_monitor.serial_number = p2.serial_number  AND p2.examine_status = 1 AND (p2.online = -1 OR p2.ping_online = -1) AND p1.id = p2.id <if test="dataScope == 1"> AND p2.province_tag_video = 1 </if><if test="dataScope == 3"> AND p2.dept_tag = 1 </if><if test="deptId != null"> AND p2.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p3 ON t_monitor.serial_number = p3.serial_number  AND p3.examine_status = 1 AND (p3.online = 1 AND p3.ping_online = 1 ) AND p1.id = p3.id<if test="dataScope == 1"> AND p3.province_tag_video = 1 </if><if test="dataScope == 3"> AND p3.dept_tag = 1 </if><if test="deptId != null"> AND p3.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p1 ON t_monitor.serial_number = p1.serial_number AND p1.examine_status = 1
        <if test="dataScope == 1"> AND p1.province_tag_video = 1 </if>
        <if test="dataScope == 3"> AND p1.dept_tag = 1 </if>
        <if test="deptId != null"> AND p1.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p2 ON t_monitor.serial_number = p2.serial_number  AND p2.examine_status = 1 AND (p2.online = -1 OR p2.ping_online = -1) AND p1.id = p2.id
        <if test="dataScope == 1"> AND p2.province_tag_video = 1 </if>
        <if test="dataScope == 3"> AND p2.dept_tag = 1 </if>
        <if test="deptId != null"> AND p2.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p3 ON t_monitor.serial_number = p3.serial_number  AND p3.examine_status = 1 AND (p3.online = 1 AND p3.ping_online = 1 ) AND p1.id = p3.id
        <if test="dataScope == 1"> AND p3.province_tag_video = 1 </if>
        <if test="dataScope == 3"> AND p3.dept_tag = 1 </if>
        <if test="deptId != null"> AND p3.dept_id = #{deptId} </if>
        WHERE INSTR(camera_fun_type, 1)
        UNION ALL
        SELECT
@@ -685,9 +698,18 @@
            COUNT(p2.id) AS errorNum,
            COUNT(p3.id) AS normalNum
        FROM t_monitor
        LEFT JOIN t_yw_point p1 ON t_monitor.serial_number = p1.serial_number  AND p1.examine_status = 1 <if test="dataScope == 1"> AND p1.province_tag_face = 1 </if><if test="dataScope == 3"> AND p1.dept_tag = 1 </if><if test="deptId != null"> AND p1.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p2 ON t_monitor.serial_number = p2.serial_number  AND p2.examine_status = 1 AND (p2.online = -1 OR p2.ping_online = -1) AND p1.id = p2.id<if test="dataScope == 1"> AND p2.province_tag_face = 1 </if><if test="dataScope == 3"> AND p2.dept_tag = 1 </if><if test="deptId != null"> AND p2.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p3 ON t_monitor.serial_number = p3.serial_number  AND p3.examine_status = 1 AND (p3.online = 1 AND p3.ping_online = 1 ) AND p1.id = p3.id<if test="dataScope == 1"> AND p3.province_tag_face = 1 </if><if test="dataScope == 3"> AND p3.dept_tag = 1 </if><if test="deptId != null"> AND p3.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p1 ON t_monitor.serial_number = p1.serial_number  AND p1.examine_status = 1
        <if test="dataScope == 1"> AND p1.province_tag_face = 1 </if>
        <if test="dataScope == 3"> AND p1.dept_tag = 1 </if>
        <if test="deptId != null"> AND p1.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p2 ON t_monitor.serial_number = p2.serial_number  AND p2.examine_status = 1 AND (p2.online = -1 OR p2.ping_online = -1) AND p1.id = p2.id
        <if test="dataScope == 1"> AND p2.province_tag_face = 1 </if>
        <if test="dataScope == 3"> AND p2.dept_tag = 1 </if>
        <if test="deptId != null"> AND p2.dept_id = #{deptId} </if>
        LEFT JOIN t_yw_point p3 ON t_monitor.serial_number = p3.serial_number  AND p3.examine_status = 1 AND (p3.online = 1 AND p3.ping_online = 1 ) AND p1.id = p3.id
        <if test="dataScope == 1"> AND p3.province_tag_face = 1 </if>
        <if test="dataScope == 3"> AND p3.dept_tag = 1 </if>
        <if test="deptId != null"> AND p3.dept_id = #{deptId} </if>
        WHERE INSTR(camera_fun_type, 3)
    </select>
    <select id="monitorRate" resultType="com.ycl.platform.domain.vo.screen.MonitorRateVO">
@@ -840,7 +862,19 @@
            AND t_monitor.serial_number = t_yw_point.serial_number )
        </foreach>
    </update>
    <update id="batchUpdatePingOnline">
        <foreach collection="ipList" item="ip" separator=";">
            UPDATE
            t_yw_point
            SET
            online = #{online},
            update_time = #{date}
            WHERE
            EXISTS (SELECT 1 FROM t_monitor
            WHERE ip = #{ip}
            AND t_monitor.serial_number = t_yw_point.serial_number )
        </foreach>
    </update>
    <update id="updateOnlineFromHk">
        <foreach collection="onlineList" item="online" separator=";">
            UPDATE
ycl-server/src/main/resources/mapper/zgyw/WorkOrderMapper.xml
@@ -356,6 +356,8 @@
            COUNT(IF(w.status != 'AUDITING_SUCCESS' and w.status != 'WAIT_DISTRIBUTE', 1, NULL)) AS todoNum,
            COUNT(IF(w.status = 'AUDITING_SUCCESS', 1, NULL)) AS doneNum
        FROM t_work_order w
        INNER JOIN t_monitor tm ON w.serial_number = tm.serial_number
        INNER JOIN t_yw_unit u ON w.unit_id = u.id AND u.deleted = 0
        LEFT JOIN t_yw_point p ON w.serial_number = p.serial_number AND p.deleted = 0
        WHERE w.deleted = 0
        <if test="dataScope == 1"> AND (p.province_tag_video = 1 or p.province_tag_car = 1 or p.province_tag_face = 1)</if>