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