Переглянути джерело

给抽问抽答统计加缓存

simonlll 2 місяців тому
батько
коміт
0ea353f342

+ 28 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysHomeReportController.java

@@ -358,6 +358,8 @@ public class SysHomeReportController extends BaseController {
358 358
         result.setSeizureList(getSeizureList(dtoList));
359 359
         // 在岗时长
360 360
         result.setWorkingList(getWorkingList(dtoList));
361
+        // 抽问抽答正确率
362
+        result.setAnswerList(getAnswerList(dtoList));
361 363
         return result;
362 364
     }
363 365
 
@@ -443,6 +445,32 @@ public class SysHomeReportController extends BaseController {
443 445
         return seizureReportService.seizureData(dto);
444 446
     }
445 447
 
448
+    /**
449
+     * 获取首页报表-明细-抽问抽答正确率
450
+     */
451
+    private List<SysHomeReportDetailDto> getAnswerList(List<SysHomePageDetailQueryParamDto> dtoList) {
452
+        List<SysHomeReportDetailDto> result = new ArrayList<>();
453
+        for (SysHomePageDetailQueryParamDto item : dtoList) {
454
+            result.add(getAnswer(item));
455
+        }
456
+        return result;
457
+    }
458
+
459
+    /**
460
+     * 获取抽问抽答正确率数据
461
+     */
462
+    private SysHomeReportDetailDto getAnswer(SysHomePageDetailQueryParamDto item) {
463
+        BaseLargeScreenQueryParamDto dto = new BaseLargeScreenQueryParamDto();
464
+        if (StrUtil.equals(HomePageQueryEnum.USER.getCode(), item.getType())) {
465
+            dto.setUserId(item.getId());
466
+        } else {
467
+            dto.setDeptId(item.getId());
468
+        }
469
+        dto.setSpecifiedDate(item.getSpecifiedDate());
470
+        dto.setStartDate(item.getStartDate());
471
+        dto.setEndDate(item.getEndDate());
472
+        return accuracyStatisticsService.answerData(dto);
473
+    }
446 474
 
447 475
     private List<SysHomeReportDetailDto> getWorkingList(List<SysHomePageDetailQueryParamDto> dtoList) {
448 476
         List<SysHomeReportDetailDto> result = new ArrayList<>();

+ 8 - 0
airport-exam/src/main/java/com/sundot/airport/exam/service/IAccuracyStatisticsService.java

@@ -61,4 +61,12 @@ public interface IAccuracyStatisticsService {
61 61
      * @return 正确率(0-1之间的小数)
62 62
      */
63 63
     BigDecimal getAccuracyRate(Long userId, Long deptId, java.util.Date startDate, java.util.Date endDate);
64
+
65
+    /**
66
+     * 首页报表-抽问抽答正确率数据
67
+     *
68
+     * @param dto 大屏查询参数
69
+     * @return 首页报表-抽问抽答正确率数据
70
+     */
71
+    com.sundot.airport.common.core.domain.SysHomeReportDetailDto answerData(BaseLargeScreenQueryParamDto dto);
64 72
 }

+ 132 - 0
airport-exam/src/main/java/com/sundot/airport/exam/service/impl/AccuracyStatisticsServiceImpl.java

@@ -6,8 +6,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
6 6
 import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
7 7
 import com.sundot.airport.common.core.domain.DailyTaskAccuracyRankingDto;
8 8
 import com.sundot.airport.common.core.domain.DailyTaskAccuracyRankingItemDto;
9
+import com.sundot.airport.common.core.domain.SysHomeReportDetailDto;
9 10
 import com.sundot.airport.common.core.domain.entity.SysDept;
10 11
 import com.sundot.airport.common.core.domain.entity.SysUser;
12
+import com.sundot.airport.common.enums.DeptTypeEnum;
13
+import com.sundot.airport.common.enums.HomePageQueryEnum;
14
+import com.sundot.airport.common.exception.ServiceException;
11 15
 import com.sundot.airport.common.enums.DeptType;
12 16
 import com.sundot.airport.common.enums.MedalTypeEnum;
13 17
 import com.sundot.airport.common.enums.TimeRangeEnum;
@@ -300,6 +304,134 @@ public class AccuracyStatisticsServiceImpl implements IAccuracyStatisticsService
300 304
         return BigDecimal.ZERO;
301 305
     }
302 306
 
307
+    @Override
308
+    public SysHomeReportDetailDto answerData(BaseLargeScreenQueryParamDto dto) {
309
+        if (ObjectUtil.isNull(dto.getUserId()) && ObjectUtil.isNull(dto.getDeptId())) {
310
+            throw new ServiceException("用户ID和部门ID不能同时为空");
311
+        }
312
+        // 默认筛选条件:最近一年
313
+        if (ObjectUtil.isNull(dto.getStartDate()) && ObjectUtil.isNull(dto.getEndDate()) && ObjectUtil.isNull(dto.getSpecifiedDate())) {
314
+            Calendar cal = Calendar.getInstance();
315
+            dto.setEndDate(cal.getTime());
316
+            cal.add(Calendar.YEAR, -1);
317
+            dto.setStartDate(cal.getTime());
318
+        }
319
+
320
+        SysHomeReportDetailDto result = new SysHomeReportDetailDto();
321
+
322
+        // 设置基本信息
323
+        if (ObjectUtil.isNotNull(dto.getUserId())) {
324
+            SysUser sysUser = userService.selectUserById(dto.getUserId());
325
+            if (ObjectUtil.isNull(sysUser)) {
326
+                throw new ServiceException("【" + dto.getUserId() + "】用户不存在");
327
+            }
328
+            result.setId(sysUser.getUserId());
329
+            result.setName(sysUser.getNickName());
330
+            result.setType(HomePageQueryEnum.USER.getCode());
331
+        } else {
332
+            SysDept sysDept = deptService.selectDeptById(dto.getDeptId());
333
+            if (ObjectUtil.isNull(sysDept)) {
334
+                throw new ServiceException("【" + dto.getDeptId() + "】部门不存在");
335
+            }
336
+            result.setId(sysDept.getDeptId());
337
+            result.setName(sysDept.getDeptName());
338
+            result.setType(HomePageQueryEnum.DEPT.getCode());
339
+        }
340
+
341
+        // 获取正确率列表
342
+        List<BigDecimal> accuracyRates = getAccuracyRatesForReport(dto);
343
+
344
+        if (accuracyRates.isEmpty()) {
345
+            result.setMaxNumber(BigDecimal.ZERO);
346
+            result.setMinNumber(BigDecimal.ZERO);
347
+            result.setAverageNumber(BigDecimal.ZERO);
348
+            result.setMedianNumber(BigDecimal.ZERO);
349
+            return result;
350
+        }
351
+
352
+        // 排序
353
+        accuracyRates.sort(BigDecimal::compareTo);
354
+
355
+        // 最大值(转换为百分比)
356
+        result.setMaxNumber(accuracyRates.get(accuracyRates.size() - 1).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
357
+        // 最小值(转换为百分比)
358
+        result.setMinNumber(accuracyRates.get(0).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
359
+        // 平均值
360
+        BigDecimal sum = accuracyRates.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
361
+        result.setAverageNumber(sum.multiply(BigDecimal.valueOf(100)).divide(BigDecimal.valueOf(accuracyRates.size()), 2, RoundingMode.HALF_UP));
362
+        // 中位数
363
+        int size = accuracyRates.size();
364
+        int middle = size / 2;
365
+        BigDecimal median;
366
+        if (size % 2 == 1) {
367
+            median = accuracyRates.get(middle);
368
+        } else {
369
+            median = accuracyRates.get(middle - 1).add(accuracyRates.get(middle)).divide(BigDecimal.valueOf(2), 4, RoundingMode.HALF_UP);
370
+        }
371
+        result.setMedianNumber(median.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP));
372
+
373
+        return result;
374
+    }
375
+
376
+    /**
377
+     * 获取用于报表统计的正确率列表
378
+     */
379
+    private List<BigDecimal> getAccuracyRatesForReport(BaseLargeScreenQueryParamDto dto) {
380
+        List<BigDecimal> result = new ArrayList<>();
381
+
382
+        if (ObjectUtil.isNotNull(dto.getUserId())) {
383
+            // 用户维度:获取用户在时间范围内每次答题的正确率
384
+            LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
385
+            taskWrapper.eq(DailyTask::getDtUserId, dto.getUserId());
386
+            taskWrapper.eq(DailyTask::getDtStatus, DailyTaskStatusEnum.COMPLETED.getCode());
387
+            taskWrapper.eq(DailyTask::getDelStatus, 0);
388
+            if (dto.getStartDate() != null) {
389
+                taskWrapper.ge(DailyTask::getDtBusinessDate, dto.getStartDate());
390
+            }
391
+            if (dto.getEndDate() != null) {
392
+                taskWrapper.le(DailyTask::getDtBusinessDate, dto.getEndDate());
393
+            }
394
+            List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
395
+
396
+            for (DailyTask task : tasks) {
397
+                LambdaQueryWrapper<DailyTaskDetail> detailWrapper = new LambdaQueryWrapper<>();
398
+                detailWrapper.eq(DailyTaskDetail::getDtdTaskId, task.getDtId());
399
+                detailWrapper.eq(DailyTaskDetail::getDelStatus, 0);
400
+                detailWrapper.isNotNull(DailyTaskDetail::getDtdIsCorrect);
401
+                List<DailyTaskDetail> details = dailyTaskDetailMapper.selectList(detailWrapper);
402
+
403
+                if (!details.isEmpty()) {
404
+                    long correctCount = details.stream().filter(d -> Boolean.TRUE.equals(d.getDtdIsCorrect())).count();
405
+                    BigDecimal accuracy = BigDecimal.valueOf(correctCount).divide(BigDecimal.valueOf(details.size()), 4, RoundingMode.HALF_UP);
406
+                    result.add(accuracy);
407
+                }
408
+            }
409
+        } else if (ObjectUtil.isNotNull(dto.getDeptId())) {
410
+            // 部门维度:获取部门下所有用户的正确率
411
+            SysDept sysDept = deptService.selectDeptById(dto.getDeptId());
412
+            if (sysDept == null) {
413
+                return result;
414
+            }
415
+
416
+            DeptHierarchy hierarchy = getDeptHierarchy(dto.getDeptId());
417
+            List<Long> teamIds = getTeamIdsUnderDept(dto.getDeptId(), hierarchy);
418
+
419
+            if (teamIds.isEmpty()) {
420
+                return result;
421
+            }
422
+
423
+            // 获取这些班组下所有用户的正确率
424
+            List<UserAccuracyInfo> userAccuracies = calculateUserAccuraciesInTeams(teamIds, dto.getStartDate(), dto.getEndDate());
425
+
426
+            for (UserAccuracyInfo info : userAccuracies) {
427
+                // userAccuracies 中的 accuracy 已经是百分比形式,需要转回小数
428
+                result.add(info.accuracy.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP));
429
+            }
430
+        }
431
+
432
+        return result;
433
+    }
434
+
303 435
     /**
304 436
      * 检查是否有待完成任务
305 437
      */