2026-08-07 14:59:15 +08:00
|
|
|
|
using YD_Event.Application.Client.Dtos;
|
|
|
|
|
|
|
|
|
|
|
|
namespace YD_Event.Application.Client;
|
|
|
|
|
|
|
|
|
|
|
|
public partial class EventClientController
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2026-08-24 00:07:15 +08:00
|
|
|
|
/// 获取可查询成绩的赛事。只开放比赛中、已结束两种状态。
|
2026-08-07 14:59:15 +08:00
|
|
|
|
/// </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()
|
2026-08-24 00:07:15 +08:00
|
|
|
|
.Where(x => x.Status == AvailabilityStatus.Enable && !x.DeleteMark)
|
2026-08-07 14:59:15 +08:00
|
|
|
|
.Select(x => x.EventId)
|
|
|
|
|
|
.Distinct()
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (eventIds.Count == 0) return new List<ScoreEventDto>();
|
|
|
|
|
|
|
|
|
|
|
|
var events = await eventRepository.AsQueryable()
|
2026-08-24 00:07:15 +08:00
|
|
|
|
.Where(x => eventIds.Contains(x.Id) && x.Status == AvailabilityStatus.Enable && !x.DeleteMark)
|
|
|
|
|
|
// 原型要求成绩查询只显示进行中、已结束状态的赛事。
|
|
|
|
|
|
.Where(x => x.EventStatus == EventStatus.Ongoing || x.EventStatus == EventStatus.Ended)
|
2026-08-07 14:59:15 +08:00
|
|
|
|
.OrderByDescending(x => x.MatchStartTime)
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
2026-08-24 00:07:15 +08:00
|
|
|
|
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()
|
|
|
|
|
|
};
|
2026-08-07 14:59:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-08-24 00:07:15 +08:00
|
|
|
|
/// 按赛事、项目、组别、姓名或报名人员ID查询成绩与排名。
|
2026-08-07 14:59:15 +08:00
|
|
|
|
/// </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()
|
2026-08-24 00:07:15 +08:00
|
|
|
|
.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());
|
2026-08-07 14:59:15 +08:00
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-24 00:07:15 +08:00
|
|
|
|
// 名次升序展示;未排名(Rank 为 0)的成绩排在最后。
|
|
|
|
|
|
var results = await query.OrderBy(x => x.Rank == 0 ? 1 : 0).OrderBy(x => x.Rank)
|
|
|
|
|
|
.OrderByDescending(x => x.CreatedTime).ToListAsync();
|
2026-08-07 14:59:15 +08:00
|
|
|
|
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),
|
2026-08-24 00:07:15 +08:00
|
|
|
|
ProjectId = x.ProjectId,
|
2026-08-07 14:59:15 +08:00
|
|
|
|
Category = projectMap.GetValueOrDefault(x.ProjectId, x.ProjectTypeName ?? string.Empty),
|
|
|
|
|
|
GroupName = x.GroupName,
|
|
|
|
|
|
Rank = x.Rank,
|
|
|
|
|
|
TotalScore = x.Score,
|
2026-08-24 00:07:15 +08:00
|
|
|
|
DifficultyScore = x.DifficultyScore,
|
|
|
|
|
|
PerformanceScore = x.PerformanceScore,
|
|
|
|
|
|
RequiredScore = x.RequiredScore,
|
|
|
|
|
|
DeductionScore = x.DeductionScore,
|
|
|
|
|
|
IsDisqualified = x.IsDisqualified,
|
|
|
|
|
|
Status = ResolveResultStatusText(x),
|
|
|
|
|
|
JudgeScores = BuildScoreBreakdown(x)
|
2026-08-07 14:59:15 +08:00
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
2026-08-24 00:07:15 +08:00
|
|
|
|
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")
|
|
|
|
|
|
};
|
2026-08-07 14:59:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-24 00:07:15 +08:00
|
|
|
|
/// <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.##");
|
|
|
|
|
|
|
2026-08-07 14:59:15 +08:00
|
|
|
|
private static string FormatBibNo(long personId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var value = personId.ToString();
|
|
|
|
|
|
return value.Length > 6 ? value[^6..] : value.PadLeft(6, '0');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|