package com.ycl.task;
|
|
|
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.DemeritRecord;
|
import com.ycl.platform.domain.entity.Report;
|
import com.ycl.platform.domain.result.UY.RecordMetaDSumResult;
|
import com.ycl.platform.mapper.DemeritRecordMapper;
|
import com.ycl.platform.mapper.ReportMapper;
|
import com.ycl.platform.service.IDemeritRecordService;
|
import com.ycl.utils.DateUtils;
|
import enumeration.ConstructionTypeEnum;
|
import enumeration.general.AreaDeptEnum;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.collections.CollectionUtils;
|
import org.springframework.data.mongodb.core.MongoTemplate;
|
import org.springframework.data.mongodb.core.query.Criteria;
|
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.stereotype.Component;
|
import utils.StringUtils;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.util.*;
|
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.function.Function;
|
import java.util.function.Predicate;
|
import java.util.stream.Collectors;
|
|
/**
|
* zgyw
|
*
|
* @author : zxl
|
* @date : 2025-09-15 17:32
|
**/
|
|
@Slf4j
|
@RequiredArgsConstructor
|
@Component("demeritRecordTask")
|
public class DemeritRecordTask {
|
|
private final MongoTemplate mongoTemplate;
|
|
private final ReportMapper reportMapper;
|
|
private static final ExecutorService executorService = new ThreadPoolExecutor(16,
|
128,
|
5000,
|
TimeUnit.SECONDS,
|
new ArrayBlockingQueue<>(1000),
|
new ThreadPoolExecutor.CallerRunsPolicy()
|
);
|
private final IDemeritRecordService demeritRecordService;
|
|
// 提取公共过滤方法
|
private Predicate<RecordMetaDSumResult> deviceNameStartsWith(String prefix) {
|
return result -> result.getDeviceName() != null && result.getDeviceName().startsWith(prefix);
|
}
|
|
private Predicate<RecordMetaDSumResult> deviceNameStartsWithAny(String... prefixes) {
|
return result -> result.getDeviceName() != null &&
|
Arrays.stream(prefixes).anyMatch(prefix -> result.getDeviceName().startsWith(prefix));
|
}
|
|
private DemeritRecord buildDemeritRecord(String constructionType,BigDecimal demerit,Integer deptId) {
|
DemeritRecord record = new DemeritRecord();
|
record.setConstructionType(constructionType);
|
record.setDemerit(demerit);
|
record.setDeptId(deptId);
|
return record;
|
}
|
private BigDecimal calculateTotalDeduction(List<RecordMetaDSumResult> records) {
|
BigDecimal total = BigDecimal.ZERO; // 单线程循环,无需AtomicReference
|
|
for (RecordMetaDSumResult record : records) {
|
// 安全处理空值:若missDuration为null,默认按0分钟处理
|
Double missDurationHours = record.getMissDuration();
|
double missDurationMinutes = Optional.ofNullable(missDurationHours)
|
.map(hours -> hours * 60.0) // 小时转分钟
|
.orElse(0.0); // 空值默认0分钟
|
|
// 计算单条记录的扣分
|
BigDecimal deduction;
|
if (missDurationMinutes > 720) {
|
deduction = new BigDecimal("2.0");
|
} else if (missDurationMinutes > 240) {
|
deduction = new BigDecimal("1.5");
|
} else if (missDurationMinutes > 60) {
|
deduction = new BigDecimal("1.0");
|
} else if (missDurationMinutes > 30) {
|
deduction = new BigDecimal("0.5");
|
} else {
|
deduction = BigDecimal.ZERO;
|
}
|
|
// 累加总扣分(最后统一处理精度,避免中间四舍五入误差)
|
total = total.add(deduction);
|
}
|
|
// 最终统一保留1位小数,四舍五入
|
return total.setScale(1, RoundingMode.HALF_UP);
|
}
|
|
public void run(){
|
log.info("开始执行计算每日扣分记录情况");
|
//获得mongodb中所有全景的设备
|
//mongodb查询条件
|
Date today =new Date();
|
// Calendar calendar = Calendar.getInstance();
|
// calendar.setTime(today);
|
// calendar.add(Calendar.DAY_OF_YEAR, -2); // 减去2天 测试用
|
// Date twoDaysAgo = calendar.getTime();
|
|
//计算录像可用率和重点录像可用率
|
Query query = new Query();
|
query.addCriteria(Criteria
|
.where("mongoCreateTime").gte(DateUtils.getDayStart(today)).lt(DateUtils.getDayEnd(today)));
|
List<RecordMetaDSumResult> results = mongoTemplate.find(query, RecordMetaDSumResult.class);
|
|
log.info("日期:{},查询出的设备录像记录数{}",today,results.size());
|
//过滤掉非全景的设备 且 1则为细节,如果是0则为全景
|
results.stream().filter(obj ->
|
{
|
String deviceId = obj.getDeviceId();
|
if (deviceId == null || deviceId.length() < 7) {
|
return false; // 去除元素
|
}
|
// 获取倒数第七位的字符
|
char seventhFromEnd = deviceId.charAt(deviceId.length() - 7);
|
return seventhFromEnd != '1'; //为1 则会 false 去除掉
|
});
|
|
log.info("过滤后剩余全景设备数{}",results.size());
|
//只考核LT_、(三期)
|
// DX_ 、(一二期)
|
// DX_R、(四区人脸)
|
// DX_RS、(四区人脸)
|
// (需要排除DX_R2、DX_RD、J_、T1、T3以及没有前缀的设备)
|
List<String> prefixes = Arrays.asList("LT_", "DX_", "DX_R", "DX_RS");
|
|
results.stream()
|
.filter(result -> {
|
String deviceName = result.getDeviceName();
|
if (deviceName == null) {
|
return false;
|
}
|
return prefixes.stream().anyMatch(deviceName::startsWith);
|
})
|
.collect(Collectors.toList());
|
|
log.info("剩余考核设备过滤后设备数{}",results.size());
|
|
//过滤掉报备的设备
|
//查询在当前时间有报备的所有设备,
|
//因为录像数据的时间
|
List<String> deviceIds = new LambdaQueryChainWrapper<>(reportMapper)
|
.eq(Report::getStatus, 1)
|
.ge(Report::getBeginCreateTime, today)
|
.le(Report::getEndCreateTime, today)
|
.list().stream()
|
.collect(Collectors.toMap(
|
Report::getSerialNumber, // key: serialNumber
|
Function.identity(), // value: Report对象本身
|
(existing, replacement) ->
|
// 当key冲突时,保留createTime更大的(更新的)记录
|
existing.getCreateTime().after(replacement.getCreateTime()) ? existing : replacement
|
))
|
.values().stream()
|
.map(Report::getSerialNumber).collect(Collectors.toList());
|
|
Set<String> deviceIdSet = new HashSet<>(deviceIds);
|
log.info("报备设备数{}",deviceIdSet.size());
|
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
|
results.forEach(item -> {
|
String areaCode = item.getArealayername().substring(0, 6);
|
AreaDeptEnum areaDeptEnum = AreaDeptEnum.fromCode(areaCode);
|
if (areaDeptEnum != null) {
|
item.setArealayerno(areaDeptEnum.getCode());
|
}
|
});
|
//需要添加数据库的数据集合
|
List<DemeritRecord> demeritRecords = new ArrayList<>();
|
|
Map<String,List<RecordMetaDSumResult>> groupByArealayerno = results.stream()
|
.collect(Collectors.groupingBy(RecordMetaDSumResult::getArealayerno));
|
|
//循环分组后的map
|
for (Map.Entry<String, List<RecordMetaDSumResult>> entry : groupByArealayerno.entrySet()) {
|
String arealayerno = entry.getKey();
|
log.info("循环区域{}",arealayerno);
|
//获得区
|
AreaDeptEnum areaDeptEnum = AreaDeptEnum.fromCode(arealayerno);
|
if (areaDeptEnum == null) {
|
log.info("区域数据异常,异常区域键值:{}",arealayerno);
|
return;
|
}
|
|
List<RecordMetaDSumResult> resultList = entry.getValue();
|
log.info("resultList:{}",resultList.size());
|
if (CollectionUtils.isNotEmpty(resultList)) {
|
// 对每个List进行处理 分建类型处理集合
|
List<RecordMetaDSumResult> phase_one_two = resultList.stream()
|
.filter(deviceNameStartsWith("DX_"))
|
.collect(Collectors.toList());
|
log.info("一二期考核记录数{}",phase_one_two.size());
|
List<RecordMetaDSumResult> phase_three = resultList.stream()
|
.filter(deviceNameStartsWith("LT_"))
|
.collect(Collectors.toList());
|
log.info("三期考核记录数{}",phase_three.size());
|
List<RecordMetaDSumResult> phase_fourth = resultList.stream()
|
.filter(deviceNameStartsWithAny("DX_R", "DX_RS"))
|
.collect(Collectors.toList());
|
log.info("四期考核记录数{}",phase_fourth.size());
|
if (CollectionUtils.isNotEmpty(phase_one_two)){
|
BigDecimal phaseOneTwoDeduction = calculateTotalDeduction(phase_one_two);
|
DemeritRecord demeritRecordPhaseOneTwo = buildDemeritRecord(
|
ConstructionTypeEnum.PHASE_ONE_TWO.name(),
|
phaseOneTwoDeduction,
|
areaDeptEnum.getDeptId());
|
demeritRecords.add(demeritRecordPhaseOneTwo);
|
}else{
|
DemeritRecord phaseOneTwoDeduction = buildDemeritRecord(
|
ConstructionTypeEnum.PHASE_ONE_TWO.name(),
|
BigDecimal.ZERO,
|
areaDeptEnum.getDeptId());
|
demeritRecords.add(phaseOneTwoDeduction);
|
}
|
|
|
if (CollectionUtils.isNotEmpty(phase_three)){
|
BigDecimal phaseThreeDeduction = calculateTotalDeduction(phase_three);
|
DemeritRecord demeritRecordPhaseThree = buildDemeritRecord(
|
ConstructionTypeEnum.PHASE_THREE.name(),
|
phaseThreeDeduction,
|
areaDeptEnum.getDeptId());
|
demeritRecords.add(demeritRecordPhaseThree);
|
}else {
|
DemeritRecord phaseThreeDeduction = buildDemeritRecord(
|
ConstructionTypeEnum.PHASE_THREE.name(),
|
BigDecimal.ZERO,
|
areaDeptEnum.getDeptId());
|
demeritRecords.add(phaseThreeDeduction);
|
}
|
if (CollectionUtils.isNotEmpty(phase_fourth)){
|
BigDecimal phaseFourthDeduction = calculateTotalDeduction(phase_fourth);
|
DemeritRecord demeritRecordPhaseFourth = buildDemeritRecord(
|
ConstructionTypeEnum.PHASE_FOURTH.name(),
|
phaseFourthDeduction,
|
areaDeptEnum.getDeptId());
|
demeritRecords.add(demeritRecordPhaseFourth);
|
}else{
|
DemeritRecord phaseFourthDeduction = buildDemeritRecord(
|
ConstructionTypeEnum.PHASE_FOURTH.name(),
|
BigDecimal.ZERO,
|
areaDeptEnum.getDeptId());
|
demeritRecords.add(phaseFourthDeduction);
|
}
|
|
}
|
|
|
|
}
|
|
//处理完数据插入数据库中
|
//先删除需要插入时间是否存在数据
|
|
LambdaQueryWrapper<DemeritRecord> queryWrapper = new LambdaQueryWrapper<>();
|
queryWrapper.ge(DemeritRecord::getCreateTime,DateUtils.getDayStart(today))
|
.le(DemeritRecord::getCreateTime,DateUtils.getDayEnd(today));
|
demeritRecordService.remove(queryWrapper);
|
demeritRecordService.saveBatch(demeritRecords);
|
|
log.info("结束计算每日扣分记录情况:插入数据量{},数据信息:{}",demeritRecords.size(),demeritRecords);
|
|
}
|
}
|