完善裁判评分赛事状态判断
This commit is contained in:
parent
55e6fa71f3
commit
5ce5a837dc
@ -163,15 +163,18 @@ public class JudgeAssignmentService : IDynamicApiController, ITransient
|
||||
{
|
||||
throw Oops.Bah("场次不存在或已取消");
|
||||
}
|
||||
var eventInfo = await _eventRepository.GetFirstAsync(x => x.Id == input.EventId);
|
||||
if (eventInfo == null) throw Oops.Bah("赛事不存在");
|
||||
|
||||
var eventSessions = await _sessionRepository.AsQueryable()
|
||||
.Where(x => x.EventId == input.EventId && x.SessionStatus != 90)
|
||||
.ToListAsync();
|
||||
var lockedSession = eventSessions.FirstOrDefault(
|
||||
x => !CompetitionStagePolicy.CanModifyAssignment(x.SessionStatus));
|
||||
x => !CompetitionStagePolicy.CanModifyAssignment(x.SessionStatus, eventInfo.EventStatus));
|
||||
if (lockedSession != null)
|
||||
{
|
||||
throw Oops.Bah(CompetitionStagePolicy.AssignmentLockReason(lockedSession.SessionStatus));
|
||||
throw Oops.Bah(CompetitionStagePolicy.AssignmentLockReason(
|
||||
lockedSession.SessionStatus, eventInfo.EventStatus));
|
||||
}
|
||||
|
||||
if (input.Assignments.Any(x => string.IsNullOrWhiteSpace(x.JudgeRole) || (x.EventRefereeId <= 0 && x.RefereeId <= 0)))
|
||||
@ -219,6 +222,8 @@ public class JudgeAssignmentService : IDynamicApiController, ITransient
|
||||
[HttpPost("submit"), DisplayName("提交裁判分配"), UnitOfWork]
|
||||
public async Task Submit([FromBody] SubmitJudgeAssignmentsInput input)
|
||||
{
|
||||
var eventInfo = await _eventRepository.GetFirstAsync(x => x.Id == input.EventId);
|
||||
if (eventInfo == null) throw Oops.Bah("赛事不存在");
|
||||
var sessions = await _sessionRepository.AsQueryable()
|
||||
.Where(x => x.EventId == input.EventId && x.SessionStatus != 90)
|
||||
.ToListAsync();
|
||||
@ -228,10 +233,11 @@ public class JudgeAssignmentService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
var lockedSession = sessions.FirstOrDefault(
|
||||
x => !CompetitionStagePolicy.CanModifyAssignment(x.SessionStatus));
|
||||
x => !CompetitionStagePolicy.CanModifyAssignment(x.SessionStatus, eventInfo.EventStatus));
|
||||
if (lockedSession != null)
|
||||
{
|
||||
throw Oops.Bah(CompetitionStagePolicy.AssignmentLockReason(lockedSession.SessionStatus));
|
||||
throw Oops.Bah(CompetitionStagePolicy.AssignmentLockReason(
|
||||
lockedSession.SessionStatus, eventInfo.EventStatus));
|
||||
}
|
||||
|
||||
// 重提时只有修改过的场次是草稿,其余场次仍是已提交;完整性校验必须同时考虑两者。
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
namespace YD_Event.Application.Scoring;
|
||||
|
||||
using YD_Event.Core.Enum;
|
||||
|
||||
/// <summary>
|
||||
/// 场次阶段与操作权限的统一判定。管理端分配和裁判端评分必须共用同一口径。
|
||||
/// </summary>
|
||||
@ -27,6 +29,14 @@ public static class CompetitionStagePolicy
|
||||
/// <returns>允许修改返回 <see langword="true"/>,否则返回 <see langword="false"/>。</returns>
|
||||
public static bool CanModifyAssignment(int sessionStatus) => sessionStatus is 0 or 10;
|
||||
|
||||
/// <summary>
|
||||
/// 结合赛事状态判断是否允许修改裁判分配。
|
||||
/// 赛事进入比赛中或已结束后,即使场次仍停留在草稿/就绪状态也必须锁定。
|
||||
/// </summary>
|
||||
public static bool CanModifyAssignment(int sessionStatus, EventStatus eventStatus) =>
|
||||
CanModifyAssignment(sessionStatus)
|
||||
&& eventStatus is not EventStatus.Ongoing and not EventStatus.Ended;
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定场次状态是否允许评分。
|
||||
/// </summary>
|
||||
@ -34,6 +44,16 @@ public static class CompetitionStagePolicy
|
||||
/// <returns>允许评分返回 <see langword="true"/>,否则返回 <see langword="false"/>。</returns>
|
||||
public static bool CanScore(int sessionStatus) => sessionStatus is 20 or 30;
|
||||
|
||||
/// <summary>
|
||||
/// 结合赛事状态判断指定场次是否允许评分。
|
||||
/// 已提交裁判分配的场次处于“就绪”状态;赛事进入比赛中后,该状态即可评分。
|
||||
/// </summary>
|
||||
/// <param name="sessionStatus">场次状态。</param>
|
||||
/// <param name="eventStatus">赛事状态。</param>
|
||||
/// <returns>允许评分返回 <see langword="true"/>,否则返回 <see langword="false"/>。</returns>
|
||||
public static bool CanScore(int sessionStatus, EventStatus eventStatus) =>
|
||||
eventStatus == EventStatus.Ongoing && sessionStatus is 10 or 20 or 30;
|
||||
|
||||
/// <summary>
|
||||
/// 根据场次状态获取赛事阶段编码。
|
||||
/// </summary>
|
||||
@ -46,6 +66,18 @@ public static class CompetitionStagePolicy
|
||||
_ => PHASE_POST_MATCH
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 结合赛事状态获取赛事阶段编码。
|
||||
/// </summary>
|
||||
public static int PhaseOf(int sessionStatus, EventStatus eventStatus)
|
||||
{
|
||||
if (eventStatus == EventStatus.Ended) return PHASE_POST_MATCH;
|
||||
if (eventStatus != EventStatus.Ongoing) return PHASE_PRE_MATCH;
|
||||
return sessionStatus is 0 ? PHASE_PRE_MATCH : PhaseOf(sessionStatus) == PHASE_POST_MATCH
|
||||
? PHASE_POST_MATCH
|
||||
: PHASE_IN_PROGRESS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据场次状态获取赛事阶段名称。
|
||||
/// </summary>
|
||||
@ -58,6 +90,17 @@ public static class CompetitionStagePolicy
|
||||
_ => "赛后"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 结合赛事状态获取赛事阶段名称。
|
||||
/// </summary>
|
||||
public static string PhaseName(int sessionStatus, EventStatus eventStatus) =>
|
||||
PhaseOf(sessionStatus, eventStatus) switch
|
||||
{
|
||||
PHASE_PRE_MATCH => "赛前",
|
||||
PHASE_IN_PROGRESS => "赛中",
|
||||
_ => "赛后"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定场次状态下禁止评分的提示信息。
|
||||
/// </summary>
|
||||
@ -73,6 +116,17 @@ public static class CompetitionStagePolicy
|
||||
_ => "当前赛事状态不可评分"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 结合赛事状态获取禁止评分的提示信息。
|
||||
/// </summary>
|
||||
public static string ScoreLockReason(int sessionStatus, EventStatus eventStatus)
|
||||
{
|
||||
if (eventStatus == EventStatus.Ended) return "赛事已结束,无法评分";
|
||||
if (eventStatus != EventStatus.Ongoing) return "赛事未开始";
|
||||
|
||||
return ScoreLockReason(sessionStatus);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定场次状态下禁止修改裁判分配的提示信息。
|
||||
/// </summary>
|
||||
@ -85,4 +139,14 @@ public static class CompetitionStagePolicy
|
||||
90 => "赛事已取消,不能修改裁判分配",
|
||||
_ => "当前赛事状态不能修改裁判分配"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 结合赛事状态获取禁止修改裁判分配的提示信息。
|
||||
/// </summary>
|
||||
public static string AssignmentLockReason(int sessionStatus, EventStatus eventStatus)
|
||||
{
|
||||
if (eventStatus == EventStatus.Ended) return "赛事已结束,不能修改裁判分配";
|
||||
if (eventStatus == EventStatus.Ongoing) return "赛事已开始,不能修改裁判分配";
|
||||
return AssignmentLockReason(sessionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,19 +181,20 @@ public class JudgeScoringController : IDynamicApiController
|
||||
await GetMyAssignmentAsync(sessionId, referee.Id);
|
||||
var session = await _sessionRepository.GetFirstAsync(x => x.Id == sessionId);
|
||||
if (session == null) throw Oops.Bah("比赛场次不存在");
|
||||
var eventInfo = await GetEventAsync(session.EventId);
|
||||
|
||||
var canScore = CompetitionStagePolicy.CanScore(session.SessionStatus);
|
||||
var canScore = CompetitionStagePolicy.CanScore(session.SessionStatus, eventInfo.EventStatus);
|
||||
return new JudgeEventStatusDto
|
||||
{
|
||||
EventId = session.EventId,
|
||||
SessionId = session.Id,
|
||||
SessionStatus = session.SessionStatus,
|
||||
SessionStatusName = SessionStatusName(session.SessionStatus),
|
||||
Phase = CompetitionStagePolicy.PhaseOf(session.SessionStatus),
|
||||
PhaseName = CompetitionStagePolicy.PhaseName(session.SessionStatus),
|
||||
Phase = CompetitionStagePolicy.PhaseOf(session.SessionStatus, eventInfo.EventStatus),
|
||||
PhaseName = CompetitionStagePolicy.PhaseName(session.SessionStatus, eventInfo.EventStatus),
|
||||
CanScore = canScore,
|
||||
CanModifyAssignment = CompetitionStagePolicy.CanModifyAssignment(session.SessionStatus),
|
||||
Message = canScore ? null : CompetitionStagePolicy.ScoreLockReason(session.SessionStatus)
|
||||
CanModifyAssignment = CompetitionStagePolicy.CanModifyAssignment(session.SessionStatus, eventInfo.EventStatus),
|
||||
Message = canScore ? null : CompetitionStagePolicy.ScoreLockReason(session.SessionStatus, eventInfo.EventStatus)
|
||||
};
|
||||
}
|
||||
|
||||
@ -255,7 +256,7 @@ public class JudgeScoringController : IDynamicApiController
|
||||
public async Task<JudgeScoreSheetOutput> GetSheet(long sessionEntryId)
|
||||
{
|
||||
var referee = await EnsureRefereeAsync();
|
||||
var (entry, ctx, assignment, roleType) = await LoadSheetContextAsync(sessionEntryId, referee.Id);
|
||||
var (entry, ctx, eventInfo, assignment, roleType) = await LoadSheetContextAsync(sessionEntryId, referee.Id);
|
||||
var definitions = ScoreSheetCalculationService.ItemsOf(ctx, roleType);
|
||||
if (definitions.Count == 0) throw Oops.Bah($"计分规则“{ctx.Rule.RuleName}”未配置{JudgeRoleSchemaService.RoleNameOf(ctx.RoleSlots, roleType)}的评分项");
|
||||
|
||||
@ -279,12 +280,11 @@ public class JudgeScoringController : IDynamicApiController
|
||||
// 草稿允许留空,因此按非必填口径试算,只汇总已录入的部分。
|
||||
var preview = ScoreSheetCalculationService.Calculate(definitions, inputs, ctx.Config, false);
|
||||
|
||||
var stageAllowsScoring = CompetitionStagePolicy.CanScore(ctx.Session.SessionStatus);
|
||||
var stageAllowsScoring = CompetitionStagePolicy.CanScore(ctx.Session.SessionStatus, eventInfo.EventStatus);
|
||||
var scoreSheetAllowsEditing = submitted == null || correction != null;
|
||||
var canEdit = stageAllowsScoring && scoreSheetAllowsEditing;
|
||||
var members = await LoadMemberNamesAsync(entry);
|
||||
var venue = await _venueRepository.GetFirstAsync(x => x.Id == ctx.Session.VenueId);
|
||||
var eventInfo = await _eventRepository.GetFirstAsync(x => x.Id == ctx.Session.EventId);
|
||||
var nextVersion = (submitted?.SheetVersion ?? 0) + 1;
|
||||
|
||||
return new JudgeScoreSheetOutput
|
||||
@ -297,7 +297,7 @@ public class JudgeScoringController : IDynamicApiController
|
||||
SheetStatus = source?.SheetStatus ?? SheetStatusDraft,
|
||||
CanEdit = canEdit,
|
||||
LockReason = canEdit ? null : !stageAllowsScoring
|
||||
? CompetitionStagePolicy.ScoreLockReason(ctx.Session.SessionStatus)
|
||||
? CompetitionStagePolicy.ScoreLockReason(ctx.Session.SessionStatus, eventInfo.EventStatus)
|
||||
: "已提交评分,如需修改请先申请成绩修正",
|
||||
IsCorrection = correction != null,
|
||||
// 幂等键随评分单版本递增,弱网重复提交同一版本只会落一张评分单。
|
||||
@ -372,8 +372,8 @@ public class JudgeScoringController : IDynamicApiController
|
||||
public async Task<JudgeScorePreviewDto> SaveDraft([FromBody] SaveJudgeScoreInput input)
|
||||
{
|
||||
var referee = await EnsureRefereeAsync();
|
||||
var (entry, ctx, assignment, roleType) = await LoadSheetContextAsync(input.SessionEntryId, referee.Id);
|
||||
EnsureCanScore(ctx.Session.SessionStatus);
|
||||
var (entry, ctx, eventInfo, assignment, roleType) = await LoadSheetContextAsync(input.SessionEntryId, referee.Id);
|
||||
EnsureCanScore(ctx.Session.SessionStatus, eventInfo.EventStatus);
|
||||
var definitions = ScoreSheetCalculationService.ItemsOf(ctx, roleType);
|
||||
if (definitions.Count == 0) throw Oops.Bah($"计分规则“{ctx.Rule.RuleName}”未配置{JudgeRoleSchemaService.RoleNameOf(ctx.RoleSlots, roleType)}的评分项");
|
||||
|
||||
@ -415,8 +415,8 @@ public class JudgeScoringController : IDynamicApiController
|
||||
public async Task<SubmitJudgeScoreOutput> Submit([FromBody] SubmitJudgeScoreInput input)
|
||||
{
|
||||
var referee = await EnsureRefereeAsync();
|
||||
var (entry, ctx, assignment, roleType) = await LoadSheetContextAsync(input.SessionEntryId, referee.Id);
|
||||
EnsureCanScore(ctx.Session.SessionStatus);
|
||||
var (entry, ctx, eventInfo, assignment, roleType) = await LoadSheetContextAsync(input.SessionEntryId, referee.Id);
|
||||
EnsureCanScore(ctx.Session.SessionStatus, eventInfo.EventStatus);
|
||||
if (entry.IsVoided) throw Oops.Bah("该出场成绩已作废,无法提交评分");
|
||||
if (entry.EntryStatus == EntryStatusWithdrawn) throw Oops.Bah("该运动员已退赛,无需评分");
|
||||
var definitions = ScoreSheetCalculationService.ItemsOf(ctx, roleType);
|
||||
@ -616,7 +616,8 @@ public class JudgeScoringController : IDynamicApiController
|
||||
if (sheet == null) throw Oops.Bah("评分单不存在");
|
||||
var session = await _sessionRepository.GetFirstAsync(x => x.Id == sheet.SessionId);
|
||||
if (session == null) throw Oops.Bah("比赛场次不存在");
|
||||
EnsureCanScore(session.SessionStatus);
|
||||
var eventInfo = await GetEventAsync(session.EventId);
|
||||
EnsureCanScore(session.SessionStatus, eventInfo.EventStatus);
|
||||
if (!sheet.IsCurrent || sheet.SheetStatus < EventScoreCalculationService.SheetStatusSubmitted)
|
||||
throw Oops.Bah("该评分单尚未提交,可直接修改后提交");
|
||||
if (sheet.SheetStatus == SheetStatusConfirmed) throw Oops.Bah("成绩已确认,请联系赛事管理员处理");
|
||||
@ -810,10 +811,21 @@ public class JudgeScoringController : IDynamicApiController
|
||||
/// 校验当前场次状态是否允许评分。
|
||||
/// </summary>
|
||||
/// <param name="sessionStatus">场次状态。</param>
|
||||
private static void EnsureCanScore(int sessionStatus)
|
||||
/// <param name="eventStatus">赛事状态。</param>
|
||||
private static void EnsureCanScore(int sessionStatus, EventStatus eventStatus)
|
||||
{
|
||||
if (!CompetitionStagePolicy.CanScore(sessionStatus))
|
||||
throw Oops.Bah(CompetitionStagePolicy.ScoreLockReason(sessionStatus));
|
||||
if (!CompetitionStagePolicy.CanScore(sessionStatus, eventStatus))
|
||||
throw Oops.Bah(CompetitionStagePolicy.ScoreLockReason(sessionStatus, eventStatus));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取场次所属赛事,确保评分权限校验与后台展示使用同一赛事状态。
|
||||
/// </summary>
|
||||
private async Task<EventInfo> GetEventAsync(long eventId)
|
||||
{
|
||||
var eventInfo = await _eventRepository.GetFirstAsync(x => x.Id == eventId);
|
||||
if (eventInfo == null) throw Oops.Bah("赛事不存在");
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -830,16 +842,18 @@ public class JudgeScoringController : IDynamicApiController
|
||||
/// <summary>
|
||||
/// 加载评分表所需的出场、计分上下文与我的裁判席位
|
||||
/// </summary>
|
||||
private async Task<(EventSessionEntry Entry, ScoringContext Context, EventJudgeAssignment Assignment, string RoleType)>
|
||||
private async Task<(EventSessionEntry Entry, ScoringContext Context, EventInfo Event,
|
||||
EventJudgeAssignment Assignment, string RoleType)>
|
||||
LoadSheetContextAsync(long sessionEntryId, long refereeId)
|
||||
{
|
||||
var entry = await _entryRepository.GetFirstAsync(x => x.Id == sessionEntryId);
|
||||
if (entry == null) throw Oops.Bah("出场记录不存在");
|
||||
var assignment = await GetMyAssignmentAsync(entry.SessionId, refereeId);
|
||||
var ctx = await _calculationService.LoadContextAsync(entry.SessionId);
|
||||
var eventInfo = await GetEventAsync(ctx.Session.EventId);
|
||||
var roleType = JudgeRoleSchemaService.NormalizeRoleType(
|
||||
string.IsNullOrWhiteSpace(assignment.JudgeRoleType) ? assignment.JudgeRole : assignment.JudgeRoleType);
|
||||
return (entry, ctx, assignment, roleType);
|
||||
return (entry, ctx, eventInfo, assignment, roleType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user