按“项目 + 场地”显示裁判分配记录

This commit is contained in:
马梓彭 2026-09-20 14:43:01 +08:00
parent f5218fd6d7
commit 725d7b4a46
2 changed files with 56 additions and 28 deletions

View File

@ -160,7 +160,7 @@ public class JudgeAssignmentProjectOutput
public List<JudgeRoleSectionOutput> JudgeRoleSections { get; set; } = new();
/// <summary>
/// 项目下的场次分配列表
/// 项目下按场地归并后的裁判分配列表
/// </summary>
public List<JudgeAssignmentSessionOutput> Sessions { get; set; } = new();
}
@ -213,17 +213,17 @@ public class JudgeRoleSlotOutput
}
/// <summary>
/// 场次裁判分配情况
/// 项目场地裁判分配情况
/// </summary>
public class JudgeAssignmentSessionOutput
{
/// <summary>
/// 比赛场次ID
/// 代表场次ID,用于定位同一项目、同一场地的裁判分配单元
/// </summary>
public long SessionId { get; set; }
/// <summary>
/// 场次名称
/// 分配单元名称(项目名称-场地名称)
/// </summary>
public string SessionName { get; set; } = null!;
@ -238,7 +238,7 @@ public class JudgeAssignmentSessionOutput
public bool Assigned { get; set; }
/// <summary>
/// 场次内的裁判席位列表
/// 该项目场地下共用的裁判席位列表
/// </summary>
public List<JudgeAssignmentItemOutput> Assignments { get; set; } = new();
}
@ -285,7 +285,7 @@ public class JudgeAssignmentItemOutput
}
/// <summary>
/// 保存场次裁判分配输入
/// 保存项目场地裁判分配输入
/// </summary>
public class SaveJudgeAssignmentsInput
{
@ -295,12 +295,12 @@ public class SaveJudgeAssignmentsInput
public long EventId { get; set; }
/// <summary>
/// 比赛场次ID
/// 代表场次ID,用于定位同一项目、同一场地的裁判分配单元
/// </summary>
public long SessionId { get; set; }
/// <summary>
/// 该场次的裁判分配列表
/// 该项目场地下共用的裁判分配列表
/// </summary>
public List<SaveJudgeAssignmentItem> Assignments { get; set; } = new();
}

View File

@ -95,19 +95,30 @@ public class JudgeAssignmentService : IDynamicApiController, ITransient
EventProjectId = ep.Id, ProjectName = project.Name,
RuleName = rule?.RuleName ?? "未配置计分规则", JudgeRoleSections = sections
};
foreach (var session in sessions.Where(x => x.EventProjectId == ep.Id)
.OrderBy(x => venueMap.GetValueOrDefault(x.VenueId)?.Sort)
.ThenBy(x => x.SessionNo))
// 裁判按“项目 + 场地”分配;同一场地因不同组别拆出的多个实际场次共用一套裁判。
var projectSessions = sessions.Where(x => x.EventProjectId == ep.Id)
.GroupBy(x => x.VenueId)
.OrderBy(x => venueMap.GetValueOrDefault(x.Key)?.Sort)
.ThenBy(x => x.Key);
foreach (var venueSessions in projectSessions)
{
var items = assignments.Where(x => x.SessionId == session.Id)
var orderedSessions = venueSessions.OrderBy(x => x.SessionNo).ThenBy(x => x.Id).ToList();
var sessionIds = orderedSessions.Select(x => x.Id).ToHashSet();
var assignmentSessionId = assignments.Where(x => sessionIds.Contains(x.SessionId))
.Select(x => x.SessionId)
.FirstOrDefault();
var representativeSession = orderedSessions.FirstOrDefault(x => x.Id == assignmentSessionId)
?? orderedSessions[0];
var items = assignments.Where(x => x.SessionId == representativeSession.Id)
.Select(x => ToAssignmentItemOutput(x, referees.FirstOrDefault(r => r.Id == x.RefereeId), slotMap))
// 先按规则中的裁判类型排序(规则里没有的历史类型排到最后),同类型内再按席位编码排列。
.OrderBy(x => slotOrder.GetValueOrDefault(x.JudgeRoleType, int.MaxValue))
.ThenBy(x => x.JudgeRole, StringComparer.OrdinalIgnoreCase).ToList();
group.Sessions.Add(new JudgeAssignmentSessionOutput
{
SessionId = session.Id, SessionName = session.SessionName ?? session.SessionNo,
VenueName = venueMap.GetValueOrDefault(session.VenueId)?.VenueName ?? "-",
SessionId = representativeSession.Id,
SessionName = $"{project.Name}-{venueMap.GetValueOrDefault(representativeSession.VenueId)?.VenueName ?? "-"}",
VenueName = venueMap.GetValueOrDefault(representativeSession.VenueId)?.VenueName ?? "-",
Assigned = items.Count > 0, Assignments = items
});
}
@ -127,18 +138,27 @@ public class JudgeAssignmentService : IDynamicApiController, ITransient
var allReferees = await GetEventReferees(eventId);
// 如果场次没有设置时间,则不进行冲突检测
if (!session.StartTime.HasValue || !session.EndTime.HasValue)
var eventSessions = await _sessionRepository.AsQueryable()
.Where(x => x.EventId == eventId && x.SessionStatus != 90)
.ToListAsync();
var assignmentUnitSessions = eventSessions
.Where(x => x.EventProjectId == session.EventProjectId && x.VenueId == session.VenueId)
.ToList();
var timedAssignmentUnitSessions = assignmentUnitSessions
.Where(x => x.StartTime.HasValue && x.EndTime.HasValue)
.ToList();
if (timedAssignmentUnitSessions.Count == 0)
{
return allReferees;
}
// 查找同时段已分配的裁判ID(场次时间重叠且已分配)
var conflictSessions = await _sessionRepository.AsQueryable()
.Where(x => x.EventId == eventId && x.Id != sessionId && x.SessionStatus != 90)
.Where(x => x.StartTime.HasValue && x.EndTime.HasValue)
.Where(x => (x.StartTime.Value <= session.EndTime.Value && x.EndTime.Value >= session.StartTime.Value))
.ToListAsync();
// 查找与分配单元内任一实际场次时间重叠的裁判;兄弟场次共用裁判,不互相判定为冲突。
var assignmentUnitSessionIds = assignmentUnitSessions.Select(x => x.Id).ToHashSet();
var conflictSessions = eventSessions
.Where(x => !assignmentUnitSessionIds.Contains(x.Id) && x.StartTime.HasValue && x.EndTime.HasValue)
.Where(x => timedAssignmentUnitSessions.Any(unit =>
x.StartTime!.Value <= unit.EndTime!.Value && x.EndTime!.Value >= unit.StartTime!.Value))
.ToList();
if (conflictSessions.Count == 0)
{
@ -157,7 +177,8 @@ public class JudgeAssignmentService : IDynamicApiController, ITransient
}
/// <summary>
/// 保存场次裁判分配。
/// 保存场地裁判分配。
/// 传入的场次仅用于定位“项目 + 场地”,分配结果会同步到该分配单元下的全部实际场次。
/// 同一裁判类型可分配多名裁判,人数上下限由计分规则的裁判席位配置决定。
/// </summary>
/// <param name="input">场次裁判分配信息。</param>
@ -200,21 +221,28 @@ public class JudgeAssignmentService : IDynamicApiController, ITransient
var candidates = await GetEventReferees(input.EventId);
var seats = BuildSeats(slots, grouped, candidates);
// 评分、任务和成绩仍按实际场次关联,因此为每个兄弟场次各保存一份相同的裁判席位。
var assignmentUnitSessions = eventSessions
.Where(x => x.EventProjectId == session.EventProjectId && x.VenueId == session.VenueId)
.ToList();
var assignmentUnitSessionIds = assignmentUnitSessions.Select(x => x.Id).ToList();
// 赛前允许反复修改已提交的分配。保存新版本后先回到草稿态,重新提交后裁判端才可见。
await _assignmentRepository.SoftDeleteAsync(x => x.SessionId == input.SessionId && x.AssignmentStatus < 9);
await _assignmentRepository.SoftDeleteAsync(x => assignmentUnitSessionIds.Contains(x.SessionId) && x.AssignmentStatus < 9);
var now = DateTime.Now;
var refereeIds = seats.Select(x => x.Candidate.RefereeId).Distinct().ToList();
var refereeUsers = (await _refereeRepository.AsQueryable()
.Where(x => refereeIds.Contains(x.Id)).ToListAsync())
.ToDictionary(x => x.Id, x => x.AppUserId);
await _assignmentRepository.InsertRangeAsync(seats.Select(x => new EventJudgeAssignment
var newAssignments = assignmentUnitSessions.SelectMany(sessionItem => seats.Select(x => new EventJudgeAssignment
{
Id = _idGenerator.NextId(), EventId = input.EventId, SessionId = input.SessionId,
Id = _idGenerator.NextId(), EventId = input.EventId, SessionId = sessionItem.Id,
EventRefereeId = x.Candidate.EventRefereeId, RefereeId = x.Candidate.RefereeId,
JudgeUserId = refereeUsers.GetValueOrDefault(x.Candidate.RefereeId),
JudgeRole = x.SeatCode, JudgeRoleType = x.Slot.Code, AssignmentStatus = 0,
Status = AvailabilityStatus.Enable, CreatedUserId = _authManager.UserId, CreatedTime = now
}).ToList());
})).ToList();
await _assignmentRepository.InsertRangeAsync(newAssignments);
}
/// <summary>