7 changed files with 201 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||||
|
package com.project.statistics.application; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.statistics.domain.dto.ExamTaskStatisticsDTO; |
||||
|
|
||||
|
public interface StatisticsApplicationService { |
||||
|
|
||||
|
Result<ExamTaskStatisticsDTO> examTaskStatistics(Long taskId); |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package com.project.statistics.application.impl; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.statistics.application.StatisticsApplicationService; |
||||
|
import com.project.statistics.domain.dto.ExamTaskStatisticsDTO; |
||||
|
import com.project.statistics.domain.service.ExamTaskStatisticsDomainService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
@Service |
||||
|
public class StatisticsApplicationServiceImpl implements StatisticsApplicationService { |
||||
|
|
||||
|
@Autowired |
||||
|
private ExamTaskStatisticsDomainService examTaskStatisticsDomainService; |
||||
|
|
||||
|
/** |
||||
|
* 统计考试任务数据 |
||||
|
*/ |
||||
|
@Override |
||||
|
public Result<ExamTaskStatisticsDTO> examTaskStatistics(Long taskId) { |
||||
|
return examTaskStatisticsDomainService.calculateExamTaskStatistics(taskId); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
package com.project.statistics.controller; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.statistics.application.StatisticsApplicationService; |
||||
|
import com.project.statistics.domain.dto.ExamTaskStatisticsDTO; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
|
||||
|
@RestController |
||||
|
@RequestMapping("/api/admin/statistics") |
||||
|
public class StatisticsController { |
||||
|
|
||||
|
@Autowired |
||||
|
private StatisticsApplicationService statisticsApplicationService; |
||||
|
|
||||
|
/** |
||||
|
* 统计考试任务数据 |
||||
|
*/ |
||||
|
@GetMapping("/examTask") |
||||
|
public Result<ExamTaskStatisticsDTO> examTask(Long taskId) { |
||||
|
return statisticsApplicationService.examTaskStatistics(taskId); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.project.statistics.domain.dto; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class ExamTaskStatisticsDTO { |
||||
|
|
||||
|
/** |
||||
|
* 参与率(%) |
||||
|
*/ |
||||
|
private String participationRate; |
||||
|
|
||||
|
/** |
||||
|
* 平均得分 |
||||
|
*/ |
||||
|
private String averageScore; |
||||
|
|
||||
|
/** |
||||
|
* 通过率(%) |
||||
|
*/ |
||||
|
private String passRate; |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
package com.project.statistics.domain.service; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.statistics.domain.dto.ExamTaskStatisticsDTO; |
||||
|
|
||||
|
public interface ExamTaskStatisticsDomainService { |
||||
|
|
||||
|
Result<ExamTaskStatisticsDTO> calculateExamTaskStatistics(Long taskId); |
||||
|
} |
||||
@ -0,0 +1,112 @@ |
|||||
|
package com.project.statistics.domain.service.impl; |
||||
|
|
||||
|
import com.project.base.domain.result.Result; |
||||
|
import com.project.exam.domain.entity.ExamRecordEntity; |
||||
|
import com.project.exam.domain.service.ExamRecordBaseService; |
||||
|
import com.project.statistics.domain.dto.ExamTaskStatisticsDTO; |
||||
|
import com.project.statistics.domain.service.ExamTaskStatisticsDomainService; |
||||
|
import com.project.task.domain.entity.TaskUserEntity; |
||||
|
import com.project.task.domain.service.TaskUserBaseService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.math.RoundingMode; |
||||
|
import java.util.List; |
||||
|
import java.util.Objects; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@Service |
||||
|
public class ExamTaskStatisticsDomainServiceImpl implements ExamTaskStatisticsDomainService { |
||||
|
|
||||
|
@Autowired |
||||
|
private TaskUserBaseService taskUserBaseService; |
||||
|
|
||||
|
@Autowired |
||||
|
private ExamRecordBaseService examRecordBaseService; |
||||
|
|
||||
|
@Override |
||||
|
public Result<ExamTaskStatisticsDTO> calculateExamTaskStatistics(Long taskId) { |
||||
|
ExamTaskStatisticsDTO dto = new ExamTaskStatisticsDTO(); |
||||
|
|
||||
|
// 1. 查询该考试任务下的所有任务用户
|
||||
|
List<TaskUserEntity> taskUserList = taskUserBaseService.lambdaQuery() |
||||
|
.eq(TaskUserEntity::getTaskId, taskId) |
||||
|
.list(); |
||||
|
|
||||
|
int totalUserCount = taskUserList.size(); |
||||
|
if (totalUserCount == 0) { |
||||
|
dto.setParticipationRate("0"); |
||||
|
dto.setAverageScore("0"); |
||||
|
dto.setPassRate("0"); |
||||
|
return Result.success(dto); |
||||
|
} |
||||
|
|
||||
|
// 2. 获取任务用户ID列表
|
||||
|
List<Long> taskUserIdList = taskUserList.stream() |
||||
|
.map(TaskUserEntity::getId) |
||||
|
.filter(Objects::nonNull) |
||||
|
.collect(Collectors.toList()); |
||||
|
|
||||
|
// 3. 查询关联的考试记录
|
||||
|
List<ExamRecordEntity> examRecordList = examRecordBaseService.lambdaQuery() |
||||
|
.in(ExamRecordEntity::getTaskUserId, taskUserIdList) |
||||
|
.list(); |
||||
|
|
||||
|
// 4. 筛选已提交的考试记录(提交时间不为空)
|
||||
|
List<ExamRecordEntity> submittedRecordList = examRecordList.stream() |
||||
|
.filter(record -> record.getSubmitTime() != null) |
||||
|
.toList(); |
||||
|
|
||||
|
if (submittedRecordList.isEmpty()) { |
||||
|
dto.setParticipationRate("0"); |
||||
|
dto.setAverageScore("0"); |
||||
|
dto.setPassRate("0"); |
||||
|
return Result.success(dto); |
||||
|
} |
||||
|
|
||||
|
int submittedCount = (int) submittedRecordList.stream() |
||||
|
.map(ExamRecordEntity::getTaskUserId) |
||||
|
.distinct() // 对 userId 本身去重
|
||||
|
.count(); |
||||
|
// 5. 计算参与率 = 已提交人数 / 总人数 * 100
|
||||
|
BigDecimal participationRate = BigDecimal.valueOf(submittedCount) |
||||
|
.multiply(BigDecimal.valueOf(100)) |
||||
|
.divide(BigDecimal.valueOf(totalUserCount), 2, RoundingMode.HALF_UP); |
||||
|
dto.setParticipationRate(formatValue(participationRate.doubleValue())); |
||||
|
|
||||
|
// 6. 计算平均得分 = 累加考试得分 / 已提交记录数
|
||||
|
double totalScore = submittedRecordList.stream() |
||||
|
.mapToDouble(record -> record.getScore() != null ? record.getScore() : 0.0) |
||||
|
.sum(); |
||||
|
BigDecimal averageScore = BigDecimal.valueOf(totalScore) |
||||
|
.divide(BigDecimal.valueOf(submittedRecordList.size()), 2, RoundingMode.HALF_UP); |
||||
|
dto.setAverageScore(formatValue(averageScore.doubleValue())); |
||||
|
|
||||
|
// 7. 计算通过率 = 通过的记录数 / 已提交记录数 * 100
|
||||
|
long passCount = submittedRecordList.stream() |
||||
|
.filter(record -> Boolean.TRUE.equals(record.getPass())) |
||||
|
.count(); |
||||
|
BigDecimal passRate = BigDecimal.valueOf(passCount) |
||||
|
.multiply(BigDecimal.valueOf(100)) |
||||
|
.divide(BigDecimal.valueOf(submittedRecordList.size()), 2, RoundingMode.HALF_UP); |
||||
|
dto.setPassRate(formatValue(passRate.doubleValue())); |
||||
|
|
||||
|
return Result.success(dto); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 格式化数值:整数保留整数,有小数保留两位 |
||||
|
*/ |
||||
|
private String formatValue(Double value) { |
||||
|
if (value == null) { |
||||
|
return "0"; |
||||
|
} |
||||
|
BigDecimal bd = BigDecimal.valueOf(value); |
||||
|
BigDecimal rounded = bd.setScale(0, RoundingMode.HALF_UP); |
||||
|
if (bd.compareTo(rounded) == 0) { |
||||
|
return rounded.toPlainString(); |
||||
|
} |
||||
|
return bd.setScale(2, RoundingMode.HALF_UP).toPlainString(); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue