zxl
4 天以前 0d243e7f5dc593cdc6e0608bb52cd635f8fc6982
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
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);
 
    }
}