diff --git a/YD_Event.Application/Event/JudgeAssignmentService.cs b/YD_Event.Application/Event/JudgeAssignmentService.cs index 5cdf0fa..79f75c6 100644 --- a/YD_Event.Application/Event/JudgeAssignmentService.cs +++ b/YD_Event.Application/Event/JudgeAssignmentService.cs @@ -240,22 +240,74 @@ public class JudgeAssignmentService : IDynamicApiController, ITransient .ToList(); var assignmentUnitSessionIds = assignmentUnitSessions.Select(x => x.Id).ToList(); - // 赛前允许反复修改已提交的分配。保存新版本后先回到草稿态,重新提交后裁判端才可见。 - await _assignmentRepository.SoftDeleteAsync(x => assignmentUnitSessionIds.Contains(x.SessionId) && x.AssignmentStatus < 9); + // 数据库可能仍使用不带软删除过滤条件的旧唯一索引 (SessionId, JudgeRole)。 + // 因此反复保存同一席位时复用原记录,不能软删除后再插入相同键。 + var existingAssignments = await _assignmentRepository.AsQueryable().ClearFilter() + .Where(x => assignmentUnitSessionIds.Contains(x.SessionId)) + .ToListAsync(); + var existingBySeat = existingAssignments + .GroupBy(x => $"{x.SessionId}:{x.JudgeRole}", StringComparer.OrdinalIgnoreCase) + .ToDictionary(group => group.Key, + group => group.OrderBy(x => x.DeleteMark) + .ThenBy(x => x.Status != AvailabilityStatus.Enable) + .ThenByDescending(x => x.CreatedTime).First(), + StringComparer.OrdinalIgnoreCase); 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); - var newAssignments = assignmentUnitSessions.SelectMany(sessionItem => seats.Select(x => new EventJudgeAssignment + var retainedIds = new HashSet(); + var updatedAssignments = new List(); + var newAssignments = new List(); + foreach (var sessionItem in assignmentUnitSessions) { - 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(); - await _assignmentRepository.InsertRangeAsync(newAssignments); + foreach (var seat in seats) + { + var key = $"{sessionItem.Id}:{seat.SeatCode}"; + if (existingBySeat.TryGetValue(key, out var assignment)) + { + retainedIds.Add(assignment.Id); + assignment.EventRefereeId = seat.Candidate.EventRefereeId; + assignment.RefereeId = seat.Candidate.RefereeId; + assignment.JudgeUserId = refereeUsers.GetValueOrDefault(seat.Candidate.RefereeId); + assignment.JudgeRole = seat.SeatCode; + assignment.JudgeRoleType = seat.Slot.Code; + assignment.AssignmentStatus = 0; + assignment.SubmittedTime = null; + assignment.Status = AvailabilityStatus.Enable; + assignment.DeleteMark = false; + assignment.UpdatedUserId = _authManager.UserId; + assignment.UpdatedTime = now; + updatedAssignments.Add(assignment); + } + else + { + newAssignments.Add(new EventJudgeAssignment + { + Id = _idGenerator.NextId(), EventId = input.EventId, SessionId = sessionItem.Id, + EventRefereeId = seat.Candidate.EventRefereeId, RefereeId = seat.Candidate.RefereeId, + JudgeUserId = refereeUsers.GetValueOrDefault(seat.Candidate.RefereeId), + JudgeRole = seat.SeatCode, JudgeRoleType = seat.Slot.Code, AssignmentStatus = 0, + Status = AvailabilityStatus.Enable, CreatedUserId = _authManager.UserId, CreatedTime = now + }); + } + } + } + + var removedAssignments = existingAssignments + .Where(x => !x.DeleteMark && !retainedIds.Contains(x.Id) && x.AssignmentStatus < 9) + .ToList(); + removedAssignments.ForEach(assignment => + { + assignment.DeleteMark = true; + assignment.AssignmentStatus = 9; + assignment.UpdatedUserId = _authManager.UserId; + assignment.UpdatedTime = now; + }); + if (removedAssignments.Count > 0) await _assignmentRepository.UpdateRangeAsync(removedAssignments); + if (updatedAssignments.Count > 0) await _assignmentRepository.UpdateRangeAsync(updatedAssignments); + if (newAssignments.Count > 0) await _assignmentRepository.InsertRangeAsync(newAssignments); } ///