diff --git a/YD_Event.Application/Client/CheckinAdminController.cs b/YD_Event.Application/Client/CheckinAdminController.cs
index a9953f2..0c3777e 100644
--- a/YD_Event.Application/Client/CheckinAdminController.cs
+++ b/YD_Event.Application/Client/CheckinAdminController.cs
@@ -1,6 +1,7 @@
using System.Text.RegularExpressions;
using YD_Event.Application.Auth;
using YD_Event.Application.Client.Dtos;
+using YD_Event.Application.Event;
namespace YD_Event.Application.Client;
@@ -194,13 +195,21 @@ public class CheckinAdminController : IDynamicApiController
if (task == null)
{
- task = new CheckinTask
+ // 兼容历史解绑数据:旧逻辑只禁用了任务,但唯一索引仍将其视为占用。
+ // 同一赛事、同一检次存在已停用任务时直接复用该任务槽位,避免再次插入触发唯一索引冲突。
+ task = await _taskRepository.GetFirstAsync(x =>
+ x.EventId == input.EventId && x.CheckinNo == input.CheckinNo && !x.DeleteMark);
+ if (task == null)
{
- EventId = input.EventId,
- CheckerUserId = checker.Id,
- CreatedUserId = managerUserId,
- CreatedTime = DateTime.Now
- };
+ task = new CheckinTask
+ {
+ EventId = input.EventId,
+ CreatedUserId = managerUserId,
+ CreatedTime = DateTime.Now
+ };
+ }
+
+ task.CheckerUserId = checker.Id;
}
ApplyCheckinAssignment(task, input.CheckinNo, assignedNumbers.Count, input.Venue);
@@ -230,6 +239,11 @@ public class CheckinAdminController : IDynamicApiController
EnsureManager();
var task = await _taskRepository.GetFirstAsync(x => x.Id == input.TaskId && !x.DeleteMark);
if (task == null) throw Oops.Bah("检录任务不存在");
+ var eventInfo = await _eventRepository.GetFirstAsync(x => x.Id == task.EventId && !x.DeleteMark);
+ if (eventInfo == null) throw Oops.Bah("赛事不存在");
+ var eventStatus = GetEffectiveEventStatus(eventInfo, DateTime.Now);
+ if (eventStatus == EventStatus.Ongoing) throw Oops.Bah("赛事比赛中,不支持解除检录员绑定");
+
var laterExists = await _taskRepository.IsAnyAsync(x =>
x.EventId == task.EventId && x.CheckinNo > task.CheckinNo && x.TaskStatus == 1 &&
x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
@@ -237,6 +251,7 @@ public class CheckinAdminController : IDynamicApiController
task.TaskStatus = 0;
task.Status = AvailabilityStatus.Disable;
+ task.DeleteMark = true;
task.UpdatedUserId = _authManager.UserId;
task.UpdatedTime = DateTime.Now;
await _taskRepository.UpdateAsync(task);
@@ -320,10 +335,13 @@ public class CheckinAdminController : IDynamicApiController
.ToList();
if (occupiedEventIds.Count > 0)
{
- var occupiedEvent = await _eventRepository.AsQueryable()
- .Where(x => occupiedEventIds.Contains(x.Id) && !x.DeleteMark &&
- x.EventStatus != EventStatus.Ended)
- .FirstAsync();
+ var occupiedEvents = await _eventRepository.AsQueryable()
+ .Where(x => occupiedEventIds.Contains(x.Id) &&
+ x.Status == AvailabilityStatus.Enable && !x.DeleteMark)
+ .ToListAsync();
+ var now = DateTime.Now;
+ var occupiedEvent = occupiedEvents.FirstOrDefault(x =>
+ GetEffectiveEventStatus(x, now) != EventStatus.Ended);
if (occupiedEvent != null)
{
return new CheckerAccountAvailabilityDto
@@ -378,6 +396,21 @@ public class CheckinAdminController : IDynamicApiController
task.Venue = venue?.Trim();
}
+ ///
+ /// 统一检录管理使用的赛事状态口径。后台状态与比赛时间不一致时按生命周期靠后的状态处理,
+ /// 避免状态同步延迟导致比赛中仍可解绑,或赛事结束后账号未及时释放。
+ ///
+ private static EventStatus GetEffectiveEventStatus(EventInfo eventInfo, DateTime now)
+ {
+ var calculatedStatus = EventStatusUpdateService.CalculateEventStatus(eventInfo, now);
+ if (calculatedStatus == EventStatus.Ended || eventInfo.EventStatus == EventStatus.Ended)
+ return EventStatus.Ended;
+ if (calculatedStatus == EventStatus.Ongoing || eventInfo.EventStatus == EventStatus.Ongoing)
+ return EventStatus.Ongoing;
+
+ return calculatedStatus;
+ }
+
private static string ToCheckinName(int checkinNo) => checkinNo switch
{
1 => "一检",
diff --git a/YD_Event.Application/Client/RefereeController.cs b/YD_Event.Application/Client/RefereeController.cs
index 1d89785..1ae4aa6 100644
--- a/YD_Event.Application/Client/RefereeController.cs
+++ b/YD_Event.Application/Client/RefereeController.cs
@@ -1,6 +1,7 @@
using EasyCaching.Core;
using YD_Event.Application.Auth;
using YD_Event.Application.Client.Dtos;
+using YD_Event.Application.Event;
namespace YD_Event.Application.Client;
@@ -190,7 +191,7 @@ public class RefereeController : IDynamicApiController
{
var userId = await EnsureRefereeAsync();
var applications = await _applicationRepository.AsQueryable()
- .Where(x => x.RefereeUserId == userId && x.Status == AvailabilityStatus.Enable)
+ .Where(x => x.RefereeUserId == userId && x.Status == AvailabilityStatus.Enable && !x.DeleteMark)
.OrderByDescending(x => x.CreatedTime)
.ToListAsync();
@@ -229,20 +230,29 @@ public class RefereeController : IDynamicApiController
public async Task