203 lines
8.6 KiB
C#
203 lines
8.6 KiB
C#
using YD_Event.Application.Client.Dtos;
|
||
|
||
namespace YD_Event.Application.Client;
|
||
|
||
public partial class EventClientController
|
||
{
|
||
/// <summary>
|
||
/// 获取可查询成绩的赛事。只开放比赛中、已结束两种状态。
|
||
/// </summary>
|
||
[HttpGet("scores/events")]
|
||
[AllowAnonymous]
|
||
[DisplayName("获取成绩赛事")]
|
||
public async Task<List<ScoreEventDto>> GetScoreEvents(
|
||
[FromServices] ISqlSugarRepository<EventResult> resultRepository,
|
||
[FromServices] ISqlSugarRepository<EventInfo> eventRepository)
|
||
{
|
||
var eventIds = await resultRepository.AsQueryable()
|
||
.Where(x => x.Status == AvailabilityStatus.Enable && !x.DeleteMark)
|
||
.Select(x => x.EventId)
|
||
.Distinct()
|
||
.ToListAsync();
|
||
|
||
if (eventIds.Count == 0) return new List<ScoreEventDto>();
|
||
|
||
var events = await eventRepository.AsQueryable()
|
||
.Where(x => eventIds.Contains(x.Id) && 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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定赛事成绩的项目、组别筛选项。
|
||
/// </summary>
|
||
[HttpGet("scores/filters")]
|
||
[AllowAnonymous]
|
||
[DisplayName("获取成绩筛选项")]
|
||
public async Task<ScoreFilterOptionsDto> GetScoreFilters(
|
||
[FromQuery] long eventId,
|
||
[FromServices] ISqlSugarRepository<EventResult> resultRepository,
|
||
[FromServices] ISqlSugarRepository<EventInfo> eventRepository,
|
||
[FromServices] ISqlSugarRepository<YD_Event.Core.Entities.Project> 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<YD_Event.Core.Entities.Project>()
|
||
: 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()
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按赛事、项目、组别、姓名或报名人员ID查询成绩与排名。
|
||
/// </summary>
|
||
[HttpGet("scores/query")]
|
||
[AllowAnonymous]
|
||
[DisplayName("查询赛事成绩")]
|
||
public async Task<ScoreQueryOutput> QueryScores(
|
||
[FromQuery] ScoreQueryInput input,
|
||
[FromServices] ISqlSugarRepository<EventResult> resultRepository,
|
||
[FromServices] ISqlSugarRepository<EventInfo> eventRepository,
|
||
[FromServices] ISqlSugarRepository<YD_Event.Core.Entities.Project> 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")
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 成绩分项明细。规定赛没有难度分,难度分为 0 时不展示该项。
|
||
/// </summary>
|
||
private static List<JudgeScoreDto> BuildScoreBreakdown(EventResult result)
|
||
{
|
||
var items = new List<JudgeScoreDto>();
|
||
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');
|
||
}
|
||
}
|