|
|
@@ -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
|
*/
|