diff --git a/YD_Event.Application/Judge/Dtos/JudgeScoringDtos.cs b/YD_Event.Application/Judge/Dtos/JudgeScoringDtos.cs
index 2a8bc12..c9a271f 100644
--- a/YD_Event.Application/Judge/Dtos/JudgeScoringDtos.cs
+++ b/YD_Event.Application/Judge/Dtos/JudgeScoringDtos.cs
@@ -135,6 +135,11 @@ public class JudgeSessionDto
/// 我已提交评分的出场数
///
public int ScoredCount { get; set; }
+
+ ///
+ /// 场次在赛事编排中的顺序
+ ///
+ public int ArrangementOrder { get; set; }
}
///
@@ -157,6 +162,11 @@ public class JudgeEntryDto
///
public int PlayOrder { get; set; }
+ ///
+ /// 秩序册中的全局出场顺序;非秩序册数据为0
+ ///
+ public long ScheduleOrder { get; set; }
+
///
/// 参赛者/参赛队名称
///
@@ -643,6 +653,11 @@ public class SaveJudgeScoreInput
/// 各项评分录入值
///
public List Items { get; set; } = new();
+
+ ///
+ /// 主裁花样赛扣分系数(各扣分项合计值,不是最终得分系数 M)
+ ///
+ public decimal? DeductionCoefficient { get; set; }
}
///
@@ -660,6 +675,11 @@ public class SubmitJudgeScoreInput
///
public List Items { get; set; } = new();
+ ///
+ /// 主裁花样赛扣分系数(各扣分项合计值,不是最终得分系数 M)
+ ///
+ public decimal? DeductionCoefficient { get; set; }
+
///
/// 幂等键,取评分表接口返回值。弱网重试时同一幂等键只会提交一次。
///
diff --git a/YD_Event.Application/Judge/JudgeScoringController.cs b/YD_Event.Application/Judge/JudgeScoringController.cs
index b5bf8d1..ed96ea6 100644
--- a/YD_Event.Application/Judge/JudgeScoringController.cs
+++ b/YD_Event.Application/Judge/JudgeScoringController.cs
@@ -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();
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();
+ ///
+ /// 校验主裁端上传的扣分系数。最终得分系数 M 仍由平台按 1-扣分系数计算,
+ /// 这里不信任客户端结果,只用于确认页面明细与上传系数属于同一评分快照。
+ ///
+ 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.###}");
+ }
+
///
/// 组装合计预览
///
@@ -1275,6 +1314,43 @@ public class JudgeScoringController : IDynamicApiController
: 0;
}
+ ///
+ /// 构建裁判端展示用的“场次-场地”,去除秩序册导入时写入场次名的项目和组别信息。
+ ///
+ 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}";
+ }
+
///
/// 获取用于裁判场次选择的赛事实时状态,避免后台状态同步延迟导致比赛中场次不可见。
///