diff --git a/YD_Event.Application/Judge/JudgeScoringController.cs b/YD_Event.Application/Judge/JudgeScoringController.cs index 6fa4721..0c5c827 100644 --- a/YD_Event.Application/Judge/JudgeScoringController.cs +++ b/YD_Event.Application/Judge/JudgeScoringController.cs @@ -766,12 +766,32 @@ public class JudgeScoringController : IDynamicApiController var messages = await _messageRepository.AsQueryable() .Where(x => x.RefereeId == referee.Id || x.ReceiverUserId == userId) .WhereIF(onlyUnread == true, x => !x.IsRead) - .ToListAsync(); + .ToListAsync()) + .OrderByDescending(x => x.CreatedTime).ToList(); + + // 成绩修正只服务于进行中的赛事。赛事状态同步可能有延迟,因此同时结合 + // 赛事时间计算实时状态;赛事结束后不再向裁判端返回对应通知。 + var correctionEventIds = messages + .Where(IsScoreCorrectionMessage) + .Select(x => x.EventId) + .Distinct() + .ToList(); + if (correctionEventIds.Count > 0) + { + var now = DateTime.Now; + var endedEventIds = (await _eventRepository.AsQueryable() + .Where(x => correctionEventIds.Contains(x.Id)) + .ToListAsync()) + .Where(x => GetEffectiveEventStatus(x, now) == EventStatus.Ended) + .Select(x => x.Id) + .ToHashSet(); + messages = messages + .Where(x => !IsScoreCorrectionMessage(x) || !endedEventIds.Contains(x.EventId)) + .ToList(); + } - messages = messages.OrderByDescending(x => x.CreatedTime).ToList(); var correctionIds = messages - .Where(x => string.Equals(x.RelationType, nameof(EventScoreCorrection), StringComparison.OrdinalIgnoreCase) - && x.RelationId.HasValue) + .Where(x => IsScoreCorrectionMessage(x) && x.RelationId.HasValue) .Select(x => x.RelationId!.Value).Distinct().ToList(); var corrections = correctionIds.Count == 0 ? new Dictionary() @@ -1453,6 +1473,13 @@ public class JudgeScoringController : IDynamicApiController return calculatedStatus; } + /// + /// 兼容历史实体名和当前业务关系名两种成绩修正消息标识。 + /// + private static bool IsScoreCorrectionMessage(EventJudgeMessage message) => + string.Equals(message.RelationType, "ScoreCorrection", StringComparison.OrdinalIgnoreCase) || + string.Equals(message.RelationType, nameof(EventScoreCorrection), StringComparison.OrdinalIgnoreCase); + /// /// 场次状态名称 /// diff --git a/YD_Event.Application/Workbench/WorkbenchController.cs b/YD_Event.Application/Workbench/WorkbenchController.cs index d9200a4..29a6065 100644 --- a/YD_Event.Application/Workbench/WorkbenchController.cs +++ b/YD_Event.Application/Workbench/WorkbenchController.cs @@ -4,6 +4,7 @@ using YD_Event.Application.Scoring.Dtos; using YD_Event.Application.Workbench.Dtos; using YD_Event.Core.Entities; using Newtonsoft.Json.Linq; +using System.Text.RegularExpressions; namespace YD_Event.Application.Workbench; @@ -196,7 +197,10 @@ public class WorkbenchController : IDynamicApiController var eventProjectDict = eventProjects.ToDictionary(ep => ep.Id); sessions = sessions - .OrderBy(s => eventProjectDict.TryGetValue(s.EventProjectId, out var eventProject) + // 场次号末段保存了秩序册预览中的全局编排序号。先按该序号排序, + // 前端再按项目合并时,项目顺序便与它在秩序册中首次出现的顺序一致。 + .OrderBy(s => GetArrangementSequence(s.SessionNo)) + .ThenBy(s => eventProjectDict.TryGetValue(s.EventProjectId, out var eventProject) && projectDict.TryGetValue(eventProject.ProjectId, out var project) ? project.SortIndex : int.MaxValue) @@ -363,6 +367,13 @@ public class WorkbenchController : IDynamicApiController .OrderBy(s => s.JudgeRole) .ToListAsync(); + // 表头必须以场次裁判分配为准,不能等评分单生成后才出现裁判列。 + var assignments = await _assignmentRepository.AsQueryable() + .Where(a => !a.DeleteMark && a.SessionId == sessionId && a.AssignmentStatus < 9 && + a.Status == Core.Enum.AvailabilityStatus.Enable) + .OrderBy(a => a.JudgeRole) + .ToListAsync(); + // 批量查询当前评分单的动态评分项明细,按评分单分组挂到各裁判评分下 var sheetIds = sheets.Select(s => s.Id).ToList(); var scoreItems = await _scoreDetailRepository.AsQueryable() @@ -382,7 +393,8 @@ public class WorkbenchController : IDynamicApiController d.Sort }).ToListAsync(); - var refereeIds = sheets.Select(s => s.RefereeId).Distinct().ToList(); + var refereeIds = assignments.Select(a => a.RefereeId) + .Concat(sheets.Select(s => s.RefereeId)).Distinct().ToList(); var refereeMap = (await _refereeRepository.AsQueryable() .Where(r => refereeIds.Contains(r.Id)) .Select(r => new { r.Id, r.Name }) @@ -401,11 +413,62 @@ public class WorkbenchController : IDynamicApiController .ToDictionary(g => g.Key, g => g.OrderByDescending(r => r.ResultVersion) .ThenBy(r => r.Id).First()); + WorkbenchJudgeScoreDto MapSheet(EventJudgeScoreSheet sheet) => new() + { + ScoreSheetId = sheet.Id, + JudgeRole = sheet.JudgeRole, + JudgeRoleType = sheet.JudgeRoleType, + JudgeRoleName = JudgeRoleSchemaService.RoleNameOf(roleSlots, sheet.JudgeRoleType), + JudgeName = refereeMap.GetValueOrDefault(sheet.RefereeId, ""), + SheetStatus = sheet.SheetStatus, + SheetStatusName = GetSheetStatusName(sheet.SheetStatus), + DifficultyScore = sheet.DifficultyScore, + PerformanceScore = sheet.PerformanceScore, + RequiredScore = sheet.RequiredScore, + DeductionScore = sheet.DeductionScore, + TotalScore = sheet.TotalScore, + IsDisqualified = sheet.IsDisqualified, + SubmittedTime = sheet.SubmittedTime, + HasPendingCorrection = pendingCorrectionSet.Contains(sheet.Id), + ScoreItems = scoreItems.Where(d => d.ScoreSheetId == sheet.Id).Select(d => new WorkbenchScoreItemDto + { + ItemCode = d.ItemCode, + ItemName = d.ItemName, + SubjectCode = d.SubjectCode, + ItemType = d.ItemType, + ScoreDimension = d.ScoreDimension, + CountValue = d.CountValue, + InputValue = d.InputValue, + Subtotal = d.Subtotal, + Sort = d.Sort + }).ToList() + }; + return entries.Select(e => { var result = resultDict.GetValueOrDefault(e.Id); var entrySheets = sheets.Where(s => s.SessionEntryId == e.Id).ToList(); + var assignedScores = assignments.Select(assignment => + { + var sheet = entrySheets.FirstOrDefault(s => s.RefereeId == assignment.RefereeId && + s.JudgeRole == assignment.JudgeRole); + return sheet != null ? MapSheet(sheet) : new WorkbenchJudgeScoreDto + { + ScoreSheetId = 0, + JudgeRole = assignment.JudgeRole, + JudgeRoleType = assignment.JudgeRoleType ?? string.Empty, + JudgeRoleName = JudgeRoleSchemaService.RoleNameOf(roleSlots, assignment.JudgeRoleType ?? string.Empty), + JudgeName = refereeMap.GetValueOrDefault(assignment.RefereeId, ""), + SheetStatus = 0, + SheetStatusName = "待提交" + }; + }).ToList(); + var assignmentKeys = assignments.Select(a => $"{a.RefereeId}:{a.JudgeRole}").ToHashSet(); + assignedScores.AddRange(entrySheets + .Where(sheet => !assignmentKeys.Contains($"{sheet.RefereeId}:{sheet.JudgeRole}")) + .Select(MapSheet)); + return new WorkbenchEntryDetailDto { SessionEntryId = e.Id, @@ -438,36 +501,7 @@ public class WorkbenchController : IDynamicApiController ReviewTime = result.ReviewTime, Rank = result.Rank } : null, - JudgeScores = entrySheets.Select(sheet => new WorkbenchJudgeScoreDto - { - ScoreSheetId = sheet.Id, - JudgeRole = sheet.JudgeRole, - JudgeRoleType = sheet.JudgeRoleType, - JudgeRoleName = JudgeRoleSchemaService.RoleNameOf(roleSlots, sheet.JudgeRoleType), - JudgeName = refereeMap.GetValueOrDefault(sheet.RefereeId, ""), - SheetStatus = sheet.SheetStatus, - SheetStatusName = GetSheetStatusName(sheet.SheetStatus), - DifficultyScore = sheet.DifficultyScore, - PerformanceScore = sheet.PerformanceScore, - RequiredScore = sheet.RequiredScore, - DeductionScore = sheet.DeductionScore, - TotalScore = sheet.TotalScore, - IsDisqualified = sheet.IsDisqualified, - SubmittedTime = sheet.SubmittedTime, - HasPendingCorrection = pendingCorrectionSet.Contains(sheet.Id), - ScoreItems = scoreItems.Where(d => d.ScoreSheetId == sheet.Id).Select(d => new WorkbenchScoreItemDto - { - ItemCode = d.ItemCode, - ItemName = d.ItemName, - SubjectCode = d.SubjectCode, - ItemType = d.ItemType, - ScoreDimension = d.ScoreDimension, - CountValue = d.CountValue, - InputValue = d.InputValue, - Subtotal = d.Subtotal, - Sort = d.Sort - }).ToList() - }).ToList() + JudgeScores = assignedScores }; }).ToList(); } @@ -1972,6 +2006,25 @@ public class WorkbenchController : IDynamicApiController #region 辅助方法 + /// + /// 获取场次在秩序册/自动编排中的全局顺序。 + /// 秩序册导入及花样赛自动编排生成的场次号末段均保存该顺序; + /// 无全局序号的速度项目回退到项目配置及场地顺序。 + /// + private static int GetArrangementSequence(string? sessionNo) + { + if (string.IsNullOrWhiteSpace(sessionNo)) return int.MaxValue; + + var match = Regex.Match(sessionNo, @"^EP\d+-(?:V)?\d+(?:-(?\d+))?$", + RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + if (!match.Success) return int.MaxValue; + + return match.Groups["sequence"].Success && + int.TryParse(match.Groups["sequence"].Value, out var sequence) + ? sequence + : int.MaxValue; + } + private static string GetSessionStatusName(int status) { return status switch