fix: 实现MiniScoringServiceImpl中的总分计算逻辑

评分提交后自动计算选手总分:
- 获取场地需要的裁判数量
- 排除主裁判评分
- 去掉最高最低分取平均(不足3人直接平均)

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
2026-01-24 11:54:59 +08:00
co-authored by factory-droid[bot]
parent 91b2896d05
commit 261f06168e
6 changed files with 136 additions and 13 deletions
@@ -53,8 +53,8 @@ public class MartialAthleteController extends BladeController {
*/
@GetMapping("/list")
@Operation(summary = "分页列表", description = "分页查询")
public R<IPage<MartialAthleteVO>> list(MartialAthlete athlete, Query query) {
IPage<MartialAthleteVO> pages = athleteService.selectAthleteVOPage(Condition.getPage(query), athlete);
public R<IPage<MartialAthleteVO>> list(MartialAthlete athlete, Query query, @RequestParam(required = false, defaultValue = "false") Boolean includeTeamRecords) {
IPage<MartialAthleteVO> pages = athleteService.selectAthleteVOPage(Condition.getPage(query), athlete, includeTeamRecords);
return R.data(pages);
}
@@ -18,9 +18,10 @@ public interface MartialAthleteMapper extends BaseMapper<MartialAthlete> {
*
* @param page 分页对象
* @param athlete 查询条件
* @param includeTeamRecords 是否包含集体项目的团队记录
* @return 参赛选手VO分页数据
*/
IPage<MartialAthleteVO> selectAthleteVOPage(IPage<MartialAthleteVO> page, @Param("athlete") MartialAthlete athlete);
IPage<MartialAthleteVO> selectAthleteVOPage(IPage<MartialAthleteVO> page, @Param("athlete") MartialAthlete athlete, @Param("includeTeamRecords") Boolean includeTeamRecords);
/**
* Count distinct participants by id_card for a competition
@@ -12,13 +12,15 @@
LEFT JOIN martial_competition c ON a.competition_id = c.id AND c.is_deleted = 0
LEFT JOIN martial_project p ON a.project_id = p.id AND p.is_deleted = 0
WHERE a.is_deleted = 0
<!-- 过滤集体项目记录:排除 idCard 为空且 playerName 等于 teamName 的记录 -->
AND NOT (
(a.id_card IS NULL OR a.id_card = '')
AND a.player_name = a.team_name
AND a.team_name IS NOT NULL
AND a.team_name != ''
)
<!-- 默认过滤集体项目的团队记录,除非 includeTeamRecords=true -->
<if test="includeTeamRecords == null or includeTeamRecords == false">
AND NOT (
(a.id_card IS NULL OR a.id_card = '')
AND a.player_name = a.team_name
AND a.team_name IS NOT NULL
AND a.team_name != ''
)
</if>
<if test="athlete.competitionId != null">
AND a.competition_id = #{athlete.competitionId}
</if>
@@ -26,6 +26,16 @@ public interface IMartialAthleteService extends IService<MartialAthlete> {
*/
IPage<MartialAthleteVO> selectAthleteVOPage(IPage<MartialAthleteVO> page, MartialAthlete athlete);
/**
* 分页查询参赛选手(包含关联字段)
*
* @param page 分页对象
* @param athlete 查询条件
* @param includeTeamRecords 是否包含集体项目的团队记录
* @return 参赛选手VO分页数据
*/
IPage<MartialAthleteVO> selectAthleteVOPage(IPage<MartialAthleteVO> page, MartialAthlete athlete, Boolean includeTeamRecords);
/**
* Task 2.1: 运动员签到
*/
@@ -61,7 +61,12 @@ public class MartialAthleteServiceImpl extends ServiceImpl<MartialAthleteMapper,
*/
@Override
public IPage<MartialAthleteVO> selectAthleteVOPage(IPage<MartialAthleteVO> page, MartialAthlete athlete) {
return baseMapper.selectAthleteVOPage(page, athlete);
return baseMapper.selectAthleteVOPage(page, athlete, false);
}
@Override
public IPage<MartialAthleteVO> selectAthleteVOPage(IPage<MartialAthleteVO> page, MartialAthlete athlete, Boolean includeTeamRecords) {
return baseMapper.selectAthleteVOPage(page, athlete, includeTeamRecords);
}
/**
@@ -7,15 +7,21 @@ import org.springblade.core.tool.api.R;
import org.springblade.modules.martial.pojo.dto.MiniScoreSubmitDTO;
import org.springblade.modules.martial.pojo.entity.MartialAthlete;
import org.springblade.modules.martial.pojo.entity.MartialJudge;
import org.springblade.modules.martial.pojo.entity.MartialJudgeInvite;
import org.springblade.modules.martial.pojo.entity.MartialScore;
import org.springblade.modules.martial.service.IMartialAthleteService;
import org.springblade.modules.martial.service.IMartialJudgeService;
import org.springblade.modules.martial.service.IMartialJudgeInviteService;
import org.springblade.modules.martial.service.IMartialScoreService;
import org.springblade.modules.martial.service.IMiniScoringService;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
@@ -26,6 +32,7 @@ public class MiniScoringServiceImpl implements IMiniScoringService {
private final IMartialScoreService scoreService;
private final IMartialJudgeService judgeService;
private final IMartialAthleteService athleteService;
private final IMartialJudgeInviteService judgeInviteService;
@Override
public R<String> submitScore(MiniScoreSubmitDTO dto) {
@@ -72,8 +79,106 @@ public class MiniScoringServiceImpl implements IMiniScoringService {
}
private void updateAthleteTotalScore(Long athleteId, Long projectId, Long venueId) {
// TODO: Implement score calculation logic
// This will be extracted to a separate service in later refactoring
try {
int requiredJudgeCount = getRequiredJudgeCount(venueId);
List<Long> chiefJudgeIds = getChiefJudgeIds(venueId);
LambdaQueryWrapper<MartialScore> scoreQuery = new LambdaQueryWrapper<>();
scoreQuery.eq(MartialScore::getAthleteId, athleteId);
scoreQuery.eq(MartialScore::getProjectId, projectId);
scoreQuery.eq(MartialScore::getIsDeleted, 0);
if (!chiefJudgeIds.isEmpty()) {
scoreQuery.notIn(MartialScore::getJudgeId, chiefJudgeIds);
}
List<MartialScore> scores = scoreService.list(scoreQuery);
if (scores == null || scores.isEmpty()) {
return;
}
if (requiredJudgeCount > 0 && scores.size() < requiredJudgeCount) {
MartialAthlete athlete = athleteService.getById(athleteId);
if (athlete != null && athlete.getTotalScore() != null) {
athlete.setTotalScore(null);
athleteService.updateById(athlete);
}
return;
}
BigDecimal totalScore = calculateTotalScore(scores);
if (totalScore != null) {
MartialAthlete athlete = athleteService.getById(athleteId);
if (athlete != null) {
athlete.setTotalScore(totalScore);
athleteService.updateById(athlete);
}
}
} catch (Exception e) {
log.error("计算总分失败: athleteId={}, projectId={}", athleteId, projectId, e);
}
}
private int getRequiredJudgeCount(Long venueId) {
if (venueId == null || venueId <= 0) {
return 0;
}
LambdaQueryWrapper<MartialJudgeInvite> judgeQuery = new LambdaQueryWrapper<>();
judgeQuery.eq(MartialJudgeInvite::getIsDeleted, 0);
judgeQuery.eq(MartialJudgeInvite::getVenueId, venueId);
judgeQuery.eq(MartialJudgeInvite::getRefereeType, 2);
List<MartialJudgeInvite> judges = judgeInviteService.list(judgeQuery);
return (int) judges.stream()
.map(MartialJudgeInvite::getJudgeId)
.filter(Objects::nonNull)
.distinct()
.count();
}
private List<Long> getChiefJudgeIds(Long venueId) {
List<Long> chiefJudgeIds = new ArrayList<>();
if (venueId == null) {
return chiefJudgeIds;
}
LambdaQueryWrapper<MartialJudgeInvite> query = new LambdaQueryWrapper<>();
query.eq(MartialJudgeInvite::getVenueId, venueId);
query.eq(MartialJudgeInvite::getRefereeType, 1);
query.eq(MartialJudgeInvite::getIsDeleted, 0);
List<MartialJudgeInvite> invites = judgeInviteService.list(query);
for (MartialJudgeInvite invite : invites) {
if (invite.getJudgeId() != null) {
chiefJudgeIds.add(invite.getJudgeId());
}
}
return chiefJudgeIds;
}
private BigDecimal calculateTotalScore(List<MartialScore> scores) {
if (scores == null || scores.isEmpty()) {
return null;
}
List<BigDecimal> scoreValues = scores.stream()
.map(MartialScore::getScore)
.filter(Objects::nonNull)
.sorted()
.collect(Collectors.toList());
int count = scoreValues.size();
if (count == 0) {
return null;
}
if (count < 3) {
BigDecimal sum = scoreValues.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
return sum.divide(new BigDecimal(count), 3, RoundingMode.HALF_UP);
}
List<BigDecimal> middleScores = scoreValues.subList(1, count - 1);
BigDecimal sum = middleScores.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
return sum.divide(new BigDecimal(middleScores.size()), 3, RoundingMode.HALF_UP);
}
private Long parseLong(String str) {