update:编排顺序

This commit is contained in:
ltx 2026-09-21 17:52:13 +08:00
parent 57df359e53
commit 6e19fce024
2 changed files with 101 additions and 5 deletions

View File

@ -135,6 +135,11 @@ public class JudgeSessionDto
/// 我已提交评分的出场数
/// </summary>
public int ScoredCount { get; set; }
/// <summary>
/// 场次在赛事编排中的顺序
/// </summary>
public int ArrangementOrder { get; set; }
}
/// <summary>
@ -157,6 +162,11 @@ public class JudgeEntryDto
/// </summary>
public int PlayOrder { get; set; }
/// <summary>
/// 秩序册中的全局出场顺序;非秩序册数据为0
/// </summary>
public long ScheduleOrder { get; set; }
/// <summary>
/// 参赛者/参赛队名称
/// </summary>
@ -643,6 +653,11 @@ public class SaveJudgeScoreInput
/// 各项评分录入值
/// </summary>
public List<JudgeScoreItemInput> Items { get; set; } = new();
/// <summary>
/// 主裁花样赛扣分系数(各扣分项合计值,不是最终得分系数 M)
/// </summary>
public decimal? DeductionCoefficient { get; set; }
}
/// <summary>
@ -660,6 +675,11 @@ public class SubmitJudgeScoreInput
/// </summary>
public List<JudgeScoreItemInput> Items { get; set; } = new();
/// <summary>
/// 主裁花样赛扣分系数(各扣分项合计值,不是最终得分系数 M)
/// </summary>
public decimal? DeductionCoefficient { get; set; }
/// <summary>
/// 幂等键,取评分表接口返回值。弱网重试时同一幂等键只会提交一次。
/// </summary>

View File

@ -162,14 +162,14 @@ public class JudgeScoringController : IDynamicApiController
.ToListAsync()).ToDictionary(x => x.Id);
sessions = sessions
.OrderBy(x => eventProjects.TryGetValue(x.EventProjectId, out var eventProject)
.OrderBy(x => GetArrangementSequence(x.SessionNo))
.ThenBy(x => eventProjects.TryGetValue(x.EventProjectId, out var eventProject)
&& projects.TryGetValue(eventProject.ProjectId, out var project)
? project.SortIndex
: int.MaxValue)
.ThenBy(x => eventProjects.TryGetValue(x.EventProjectId, out var eventProject)
? eventProject.ProjectId
: long.MaxValue)
.ThenBy(x => GetArrangementSequence(x.SessionNo))
.ThenBy(x => venues.TryGetValue(x.VenueId, out var venue) ? venue.Sort : int.MaxValue)
.ThenBy(x => x.SessionNo)
.ThenBy(x => x.Id)
@ -216,7 +216,11 @@ public class JudgeScoringController : IDynamicApiController
JudgeRoleType = roleType,
JudgeRoleName = slot?.Name ?? JudgeRoleSchemaService.RoleName(roleType),
EntryCount = entries.Count(x => x.SessionId == session.Id),
ScoredCount = mySheets.Count(x => x.SessionId == session.Id)
ScoredCount = mySheets.Count(x => x.SessionId == session.Id),
ArrangementOrder = GetArrangementSequence(session.SessionNo) is var order
&& order < int.MaxValue
? order
: 0
});
}
return rows;
@ -266,7 +270,10 @@ public class JudgeScoringController : IDynamicApiController
var config = ScoreRuleConfigService.Parse(
(await _ruleRepository.GetFirstAsync(x => x.Id == session.ScoreRuleId))?.RuleConfigJson);
var entries = (await _entryRepository.AsQueryable().Where(x => x.SessionId == sessionId).ToListAsync())
var entries = (await _entryRepository.AsQueryable()
.Where(x => x.SessionId == sessionId && !x.IsVoided &&
x.Status == AvailabilityStatus.Enable && !x.DeleteMark)
.ToListAsync())
.OrderBy(x => x.PlayOrder).ThenBy(x => x.Id).ToList();
if (entries.Count == 0) return new List<JudgeEntryDto>();
var entryIds = entries.Select(x => x.Id).ToList();
@ -289,6 +296,7 @@ public class JudgeScoringController : IDynamicApiController
SessionEntryId = entry.Id,
EntryNo = entry.EntryNo,
PlayOrder = entry.PlayOrder,
ScheduleOrder = entry.SourceType == 1 ? entry.Id : 0,
ParticipantName = string.IsNullOrWhiteSpace(entry.TeamName) ? entry.ParticipantName : entry.TeamName!,
GroupName = entry.GroupName,
EntryStatus = entry.EntryStatus,
@ -364,7 +372,11 @@ public class JudgeScoringController : IDynamicApiController
ProjectName = ctx.Project.Name,
RuleName = ctx.Rule.RuleName,
GroupName = entry.GroupName,
SessionVenue = $"{ctx.Session.SessionName ?? ctx.Session.SessionNo}-{venue?.VenueName ?? "-"}",
SessionVenue = BuildSessionVenue(
ctx.Session,
ctx.Project.Name,
entry.GroupName,
venue?.VenueName ?? "-"),
EntryNo = entry.EntryNo,
ParticipantName = string.IsNullOrWhiteSpace(entry.TeamName) ? entry.ParticipantName : entry.TeamName!,
MemberNames = members,
@ -440,6 +452,7 @@ public class JudgeScoringController : IDynamicApiController
throw Oops.Bah("已提交评分,如需修改请先申请成绩修正");
var calculation = ScoreSheetCalculationService.Calculate(definitions, ToInputValues(input.Items), ctx.Config, false);
ValidateDeductionCoefficient(input.DeductionCoefficient, calculation, ctx.Config, roleType);
var task = await EnsureTaskAsync(entry, ctx, assignment, roleType, submitted == null ? 0 : TaskTypeCorrection);
var draft = await _sheetRepository.GetFirstAsync(x => x.SessionEntryId == entry.Id
&& x.JudgeAssignmentId == assignment.Id && x.SheetStatus == SheetStatusDraft);
@ -497,6 +510,7 @@ public class JudgeScoringController : IDynamicApiController
// 正式提交才校验必填项与取值范围。
var calculation = ScoreSheetCalculationService.Calculate(definitions, ToInputValues(input.Items), ctx.Config, true);
ValidateDeductionCoefficient(input.DeductionCoefficient, calculation, ctx.Config, roleType);
var task = await EnsureTaskAsync(entry, ctx, assignment, roleType, correction == null ? 0 : TaskTypeCorrection);
var draft = await _sheetRepository.GetFirstAsync(x => x.SessionEntryId == entry.Id
&& x.JudgeAssignmentId == assignment.Id && x.SheetStatus == SheetStatusDraft);
@ -1145,6 +1159,31 @@ public class JudgeScoringController : IDynamicApiController
CountValue = x.CountValue
}).ToList();
/// <summary>
/// 校验主裁端上传的扣分系数。最终得分系数 M 仍由平台按 1-扣分系数计算,
/// 这里不信任客户端结果,只用于确认页面明细与上传系数属于同一评分快照。
/// </summary>
private static void ValidateDeductionCoefficient(decimal? uploaded,
ScoreSheetCalculationResult calculation, ScoreRuleConfig config, string roleType)
{
if (uploaded == null) return; // 兼容尚未升级的裁判端。
if (!config.IsFactorTotal || !string.Equals(
JudgeRoleSchemaService.NormalizeRoleType(roleType),
JudgeRoleSchemaService.RoleChief,
StringComparison.OrdinalIgnoreCase))
throw Oops.Bah("当前评分表不支持上传扣分系数");
if (uploaded < 0m || uploaded > 1m)
throw Oops.Bah("扣分系数必须在0至1之间");
// 明细行保留了递进扣分的未舍入小计,用它校验 0.125 等三位小数系数。
var expected = calculation.Lines
.Where(x => x.ScoreDimension == ScoreDimensionCode.Deduction)
.Sum(x => x.Subtotal);
if (Math.Abs(uploaded.Value - expected) > 0.0005m)
throw Oops.Bah($"扣分系数与扣分明细不一致,应为{expected:0.###}");
}
/// <summary>
/// 组装合计预览
/// </summary>
@ -1275,6 +1314,43 @@ public class JudgeScoringController : IDynamicApiController
: 0;
}
/// <summary>
/// 构建裁判端展示用的“场次-场地”,去除秩序册导入时写入场次名的项目和组别信息。
/// </summary>
private static string BuildSessionVenue(
EventSession session,
string projectName,
string groupName,
string venueName)
{
var sessionName = session.SessionName ?? string.Empty;
foreach (var redundant in new[] { projectName, groupName, venueName })
{
if (!string.IsNullOrWhiteSpace(redundant))
sessionName = sessionName.Replace(
redundant,
string.Empty,
StringComparison.OrdinalIgnoreCase);
}
sessionName = Regex.Replace(
sessionName,
@"^[\s·|/_—–-]+|[\s·|/_—–-]+$",
string.Empty).Trim();
if (string.IsNullOrWhiteSpace(sessionName))
{
var arrangementOrder = GetArrangementSequence(session.SessionNo);
sessionName = arrangementOrder > 0 && arrangementOrder < int.MaxValue
? $"第{arrangementOrder}场"
: session.SessionNo;
}
return string.IsNullOrWhiteSpace(venueName)
|| sessionName.Contains(venueName, StringComparison.OrdinalIgnoreCase)
? sessionName
: $"{sessionName}-{venueName}";
}
/// <summary>
/// 获取用于裁判场次选择的赛事实时状态,避免后台状态同步延迟导致比赛中场次不可见。
/// </summary>