using YD_Event.Application.Client.Dtos; namespace YD_Event.Application.Client; public partial class EventClientController { /// /// 获取可查询成绩的赛事。只开放比赛中、已结束两种状态。 /// [HttpGet("scores/events")] [AllowAnonymous] [DisplayName("获取成绩赛事")] public async Task> GetScoreEvents( [FromServices] ISqlSugarRepository eventRepository) { var events = await eventRepository.AsQueryable() .Where(x => x.Status == AvailabilityStatus.Enable && !x.DeleteMark) // 成绩查询按赛事状态开放,未产生成绩时也应允许选择比赛中、已结束的赛事。 .Where(x => x.EventStatus == EventStatus.Ongoing || x.EventStatus == EventStatus.Ended) .OrderByDescending(x => x.MatchStartTime) .ToListAsync(); return events.Select(x => new ScoreEventDto { Id = x.Id, Name = x.EventName, EventStatus = (int)x.EventStatus, EventStatusText = x.EventStatus.Description() }).ToList(); } /// /// 获取指定赛事成绩的项目、组别筛选项。 /// [HttpGet("scores/filters")] [AllowAnonymous] [DisplayName("获取成绩筛选项")] public async Task GetScoreFilters( [FromQuery] long eventId, [FromServices] ISqlSugarRepository resultRepository, [FromServices] ISqlSugarRepository eventRepository, [FromServices] ISqlSugarRepository projectRepository) { var evt = await eventRepository.GetFirstAsync(x => x.Id == eventId && x.Status == AvailabilityStatus.Enable && !x.DeleteMark); if (evt == null) throw Oops.Bah("赛事不存在"); var results = await resultRepository.AsQueryable() .Where(x => x.EventId == eventId && x.Status == AvailabilityStatus.Enable && !x.DeleteMark) .ToListAsync(); var projectIds = results.Select(x => x.ProjectId).Distinct().ToList(); var projects = projectIds.Count == 0 ? new List() : await projectRepository.AsQueryable().Where(x => projectIds.Contains(x.Id)).ToListAsync(); var projectMap = projects.ToDictionary(x => x.Id, x => x.Name); var projectFilters = results .GroupBy(x => x.ProjectId) .Select(group => new ScoreProjectFilterDto { ProjectId = group.Key, ProjectName = projectMap.GetValueOrDefault(group.Key) ?? group.Select(x => x.ProjectTypeName).FirstOrDefault(x => !string.IsNullOrWhiteSpace(x)) ?? string.Empty, GroupNames = group.Select(x => x.GroupName) .Where(x => !string.IsNullOrWhiteSpace(x)) .Distinct().OrderBy(x => x).ToList() }) .OrderBy(x => x.ProjectName) .ToList(); return new ScoreFilterOptionsDto { EventId = evt.Id, EventName = evt.EventName, Projects = projectFilters, GroupNames = projectFilters.SelectMany(x => x.GroupNames).Distinct().OrderBy(x => x).ToList() }; } /// /// 按赛事、项目、组别、姓名或报名人员ID查询成绩与排名。 /// [HttpGet("scores/query")] [AllowAnonymous] [DisplayName("查询赛事成绩")] public async Task QueryScores( [FromQuery] ScoreQueryInput input, [FromServices] ISqlSugarRepository resultRepository, [FromServices] ISqlSugarRepository eventRepository, [FromServices] ISqlSugarRepository projectRepository) { var query = resultRepository.AsQueryable() .Where(x => x.Status == AvailabilityStatus.Enable && !x.DeleteMark) .WhereIF(input.EventId.HasValue, x => x.EventId == input.EventId!.Value) .WhereIF(input.ProjectId.HasValue, x => x.ProjectId == input.ProjectId!.Value) .WhereIF(!string.IsNullOrWhiteSpace(input.GroupName), x => x.GroupName == input.GroupName!.Trim()); if (!string.IsNullOrWhiteSpace(input.Keyword)) { var keyword = input.Keyword.Trim(); if (long.TryParse(keyword, out var personId)) { query = query.Where(x => x.ParticipantName.Contains(keyword) || x.RegistrationPersonId == personId); } else { query = query.Where(x => x.ParticipantName.Contains(keyword) || x.GroupName.Contains(keyword)); } } // 名次升序展示;未排名(Rank 为 0)的成绩排在最后。 var results = await query.OrderBy(x => x.Rank == 0 ? 1 : 0).OrderBy(x => x.Rank) .OrderByDescending(x => x.CreatedTime).ToListAsync(); if (results.Count == 0) return new ScoreQueryOutput(); var eventIds = results.Select(x => x.EventId).Distinct().ToList(); var projectIds = results.Select(x => x.ProjectId).Distinct().ToList(); var events = await eventRepository.AsQueryable().Where(x => eventIds.Contains(x.Id)).ToListAsync(); var projects = await projectRepository.AsQueryable().Where(x => projectIds.Contains(x.Id)).ToListAsync(); var eventMap = events.ToDictionary(x => x.Id, x => x.EventName); var projectMap = projects.ToDictionary(x => x.Id, x => x.Name); var rows = results.Select(x => new ClientScoreDto { Id = x.Id, EventId = x.EventId, EventName = eventMap.GetValueOrDefault(x.EventId, string.Empty), AthleteName = x.ParticipantName, AthleteNo = FormatBibNo(x.RegistrationPersonId), ProjectId = x.ProjectId, Category = projectMap.GetValueOrDefault(x.ProjectId, x.ProjectTypeName ?? string.Empty), GroupName = x.GroupName, Rank = x.Rank, TotalScore = x.Score, DifficultyScore = x.DifficultyScore, PerformanceScore = x.PerformanceScore, RequiredScore = x.RequiredScore, DeductionScore = x.DeductionScore, IsDisqualified = x.IsDisqualified, Status = ResolveResultStatusText(x), JudgeScores = BuildScoreBreakdown(x) }).ToList(); var updatedTime = results.Max(x => x.UpdatedTime ?? x.CreatedTime); return new ScoreQueryOutput { Rows = rows, Total = rows.Count, DataUpdatedTime = updatedTime, DataUpdatedText = updatedTime.ToString("yyyy-MM-dd HH:mm:ss") }; } /// /// 成绩分项明细。规定赛没有难度分,难度分为 0 时不展示该项。 /// private static List BuildScoreBreakdown(EventResult result) { var items = new List(); if (result.DifficultyScore != 0) items.Add(new JudgeScoreDto { Label = "难度分", Score = FormatScore(result.DifficultyScore) }); items.Add(new JudgeScoreDto { Label = "表现分", Score = FormatScore(result.PerformanceScore) }); items.Add(new JudgeScoreDto { Label = "规定分", Score = FormatScore(result.RequiredScore) }); items.Add(new JudgeScoreDto { Label = "扣分", Score = FormatScore(result.DeductionScore) }); items.Add(new JudgeScoreDto { Label = "最终成绩", Score = result.Score }); return items; } private static string ResolveResultStatusText(EventResult result) { if (result.IsDisqualified) return "取消比赛资格"; return result.ResultStatus switch { 0 => "计算中", 10 => "待复核", 30 => "已投屏", 40 => "已更正", 90 => "成绩已作废", _ => "成绩有效" }; } private static string FormatScore(decimal value) => Math.Round(value, 2, MidpointRounding.AwayFromZero).ToString("0.##"); private static string FormatBibNo(long personId) { var value = personId.ToString(); return value.Length > 6 ? value[^6..] : value.PadLeft(6, '0'); } }