2026-08-24 00:07:15 +08:00
|
|
|
|
using YD_Event.Application.Client.Dtos;
|
|
|
|
|
|
|
|
|
|
|
|
namespace YD_Event.Application.Client;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>面向运动员、教练的检录进度查询与检录投屏。</summary>
|
|
|
|
|
|
public partial class CheckinController
|
|
|
|
|
|
{
|
|
|
|
|
|
[HttpGet("progress")]
|
|
|
|
|
|
[DisplayName("查看赛事检录进度")]
|
|
|
|
|
|
public async Task<CheckinProgressOutput> GetProgress([FromQuery] long eventId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var userId = _authManager.UserId;
|
|
|
|
|
|
var options = await _scheduleService.GetOptionsAsync(eventId);
|
|
|
|
|
|
var activeTasks = await GetActiveEventTasksAsync(eventId);
|
|
|
|
|
|
var context = await BuildProgressContextAsync(options, activeTasks);
|
|
|
|
|
|
|
|
|
|
|
|
// 报名人员与当前登录用户按 UserId 关联;教练可看到同一报名单下的运动员。
|
|
|
|
|
|
var myPersonIds = await ResolveMyPersonIdsAsync(eventId, userId);
|
|
|
|
|
|
var mine = options.Units
|
|
|
|
|
|
.Where(x => x.RegistrationPersonIds.Any(myPersonIds.Contains))
|
|
|
|
|
|
.OrderBy(x => x.Order)
|
|
|
|
|
|
.Select(unit => BuildMineDto(unit, context, activeTasks, myPersonIds))
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
|
return new CheckinProgressOutput
|
|
|
|
|
|
{
|
|
|
|
|
|
EventId = options.EventId,
|
|
|
|
|
|
EventName = options.EventName,
|
|
|
|
|
|
ServerTime = now,
|
|
|
|
|
|
ServerTimeText = now.ToString("yyyy-MM-dd HH:mm:ss"),
|
|
|
|
|
|
WeekDayText = ToWeekDayText(now),
|
|
|
|
|
|
CurrentCheckin = context.CurrentCheckin,
|
|
|
|
|
|
CurrentCompeting = context.CurrentCompeting,
|
|
|
|
|
|
Stages = context.Stages,
|
|
|
|
|
|
Mine = mine
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[HttpGet("screen-cast")]
|
|
|
|
|
|
[AllowAnonymous]
|
|
|
|
|
|
[DisplayName("检录投屏")]
|
|
|
|
|
|
public async Task<CheckinScreenCastOutput> GetScreenCast(
|
|
|
|
|
|
[FromQuery] long eventId,
|
|
|
|
|
|
[FromQuery] long? eventProjectId = null,
|
|
|
|
|
|
[FromQuery] long? groupId = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var options = await _scheduleService.GetOptionsAsync(eventId);
|
|
|
|
|
|
var activeTasks = await GetActiveEventTasksAsync(eventId);
|
|
|
|
|
|
var context = await BuildProgressContextAsync(options, activeTasks);
|
|
|
|
|
|
|
|
|
|
|
|
// 未指定项目组别时投屏当前检录中的项目组别。
|
|
|
|
|
|
var targetProjectId = eventProjectId ?? context.CurrentCheckin?.EventProjectId;
|
|
|
|
|
|
var targetGroupId = groupId ?? context.CurrentCheckin?.GroupId;
|
|
|
|
|
|
var units = options.Units
|
|
|
|
|
|
.Where(x => (!targetProjectId.HasValue || x.EventProjectId == targetProjectId.Value) &&
|
|
|
|
|
|
(!targetGroupId.HasValue || x.GroupId == targetGroupId.Value))
|
|
|
|
|
|
.OrderBy(x => x.Order)
|
|
|
|
|
|
.Select(unit =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var status = ResolveUnitStatus(unit, context, activeTasks);
|
|
|
|
|
|
return new CheckinScreenCastUnitDto
|
|
|
|
|
|
{
|
|
|
|
|
|
UnitKey = unit.UnitKey,
|
|
|
|
|
|
Order = unit.Order,
|
|
|
|
|
|
SessionIndex = unit.SessionIndex,
|
|
|
|
|
|
VenueName = unit.VenueName,
|
|
|
|
|
|
ProjectName = unit.ProjectName,
|
|
|
|
|
|
GroupName = unit.GroupName,
|
|
|
|
|
|
TeamName = ResolveTeamName(unit),
|
|
|
|
|
|
UnitName = unit.UnitName,
|
|
|
|
|
|
AthleteNames = unit.Athletes.Select(x => x.Name).ToList(),
|
|
|
|
|
|
CheckinStatus = status,
|
|
|
|
|
|
CheckinStatusText = ToCheckinStatusText(status)
|
|
|
|
|
|
};
|
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
|
return new CheckinScreenCastOutput
|
|
|
|
|
|
{
|
|
|
|
|
|
EventId = options.EventId,
|
|
|
|
|
|
EventName = options.EventName,
|
|
|
|
|
|
ServerTime = now,
|
|
|
|
|
|
ServerTimeText = now.ToString("yyyy-MM-dd HH:mm:ss"),
|
|
|
|
|
|
WeekDayText = ToWeekDayText(now),
|
|
|
|
|
|
CurrentCheckin = context.CurrentCheckin,
|
|
|
|
|
|
CurrentCompeting = context.CurrentCompeting,
|
|
|
|
|
|
Stages = context.Stages,
|
|
|
|
|
|
Units = units
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>汇总各检次的检录记录,生成项目组别三态与当前检录、当前比赛信息。</summary>
|
|
|
|
|
|
private async Task<CheckinProgressContext> BuildProgressContextAsync(
|
|
|
|
|
|
CheckinEventOptionsDto options, List<CheckinTask> activeTasks)
|
|
|
|
|
|
{
|
|
|
|
|
|
var validUnitKeys = options.Units.Select(x => x.UnitKey).ToHashSet();
|
|
|
|
|
|
var taskIds = activeTasks.Select(x => x.Id).ToList();
|
|
|
|
|
|
var records = taskIds.Count == 0
|
|
|
|
|
|
? new List<CheckinRecord>()
|
|
|
|
|
|
: await _recordRepository.AsQueryable()
|
|
|
|
|
|
.Where(x => taskIds.Contains(x.TaskId) && x.SessionKey != null && x.Checked && !x.DeleteMark)
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
2026-09-01 14:16:11 +08:00
|
|
|
|
// 检录员姓名按 CheckerUserId 批量查一次,避免逐条检录记录反查用户;
|
|
|
|
|
|
// AppUser.Name 为实名认证姓名,未认证的检录员退回昵称展示。
|
|
|
|
|
|
var checkerIds = records.Select(x => x.CheckerUserId).Distinct().ToList();
|
|
|
|
|
|
var checkers = checkerIds.Count == 0
|
|
|
|
|
|
? new List<YD_Event.Core.Entities.AppUser>()
|
|
|
|
|
|
: await _userRepository.AsQueryable()
|
|
|
|
|
|
.Where(x => checkerIds.Contains(x.Id) && !x.DeleteMark)
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
var checkerNameMap = checkers.ToDictionary(
|
|
|
|
|
|
x => x.Id,
|
|
|
|
|
|
x => !string.IsNullOrWhiteSpace(x.Name) ? x.Name! : x.NickName ?? string.Empty);
|
|
|
|
|
|
|
2026-08-24 00:07:15 +08:00
|
|
|
|
var taskCheckinNoMap = activeTasks.ToDictionary(x => x.Id, x => x.CheckinNo);
|
|
|
|
|
|
var passedByKey = new Dictionary<string, Dictionary<int, DateTime?>>();
|
2026-09-01 14:16:11 +08:00
|
|
|
|
var checkerNameByKey = new Dictionary<string, Dictionary<int, string>>();
|
2026-08-24 00:07:15 +08:00
|
|
|
|
foreach (var record in records.Where(x => validUnitKeys.Contains(x.SessionKey!)))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!taskCheckinNoMap.TryGetValue(record.TaskId, out var checkinNo)) continue;
|
|
|
|
|
|
if (!passedByKey.TryGetValue(record.SessionKey!, out var rounds))
|
|
|
|
|
|
{
|
|
|
|
|
|
rounds = new Dictionary<int, DateTime?>();
|
|
|
|
|
|
passedByKey[record.SessionKey!] = rounds;
|
|
|
|
|
|
}
|
|
|
|
|
|
rounds[checkinNo] = record.CheckedTime;
|
2026-09-01 14:16:11 +08:00
|
|
|
|
|
|
|
|
|
|
if (!checkerNameByKey.TryGetValue(record.SessionKey!, out var checkerNames))
|
|
|
|
|
|
{
|
|
|
|
|
|
checkerNames = new Dictionary<int, string>();
|
|
|
|
|
|
checkerNameByKey[record.SessionKey!] = checkerNames;
|
|
|
|
|
|
}
|
|
|
|
|
|
checkerNames[checkinNo] = checkerNameMap.GetValueOrDefault(record.CheckerUserId) ?? string.Empty;
|
2026-08-24 00:07:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var firstCheckinNo = activeTasks.Count == 0 ? 1 : activeTasks.Min(x => x.CheckinNo);
|
|
|
|
|
|
var lastCheckinNo = activeTasks.Count == 0 ? 1 : activeTasks.Max(x => x.CheckinNo);
|
|
|
|
|
|
var context = new CheckinProgressContext
|
|
|
|
|
|
{
|
|
|
|
|
|
PassedByKey = passedByKey,
|
2026-09-01 14:16:11 +08:00
|
|
|
|
CheckerNameByKey = checkerNameByKey,
|
2026-08-24 00:07:15 +08:00
|
|
|
|
FirstCheckinNo = firstCheckinNo,
|
|
|
|
|
|
LastCheckinNo = lastCheckinNo,
|
|
|
|
|
|
ActiveCheckinNumbers = activeTasks.Select(x => x.CheckinNo).OrderBy(x => x).ToList()
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 按秩序册顺序汇总项目组别阶段;一检开始即为检录中,三检全部通过即为已完成。
|
|
|
|
|
|
var stages = options.Units
|
|
|
|
|
|
.GroupBy(x => new { x.EventProjectId, x.GroupId, x.ProjectName, x.GroupName })
|
|
|
|
|
|
.Select(group =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var groupUnits = group.OrderBy(x => x.Order).ToList();
|
|
|
|
|
|
var total = groupUnits.Count;
|
|
|
|
|
|
var checkedCount = groupUnits.Count(x =>
|
|
|
|
|
|
context.IsPassed(x.UnitKey, firstCheckinNo));
|
|
|
|
|
|
var finishedCount = groupUnits.Count(x =>
|
|
|
|
|
|
context.IsPassed(x.UnitKey, lastCheckinNo));
|
|
|
|
|
|
return new CheckinProgressStageDto
|
|
|
|
|
|
{
|
|
|
|
|
|
EventProjectId = group.Key.EventProjectId,
|
|
|
|
|
|
GroupId = group.Key.GroupId,
|
|
|
|
|
|
ProjectName = group.Key.ProjectName,
|
|
|
|
|
|
GroupName = group.Key.GroupName,
|
|
|
|
|
|
DisplayName = $"{group.Key.ProjectName} - {group.Key.GroupName}",
|
|
|
|
|
|
Order = groupUnits[0].Order,
|
|
|
|
|
|
TotalUnits = total,
|
|
|
|
|
|
CheckedUnits = checkedCount,
|
|
|
|
|
|
ProgressPercent = total == 0 ? 0 : (int)Math.Round(checkedCount * 100d / total),
|
|
|
|
|
|
StageStatus = ResolveStageStatus(total, checkedCount, finishedCount)
|
|
|
|
|
|
};
|
|
|
|
|
|
})
|
|
|
|
|
|
.OrderBy(x => x.Order)
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// 未开始的阶段中最靠前的一个显示"即将开始",其余保持"等待中"。
|
|
|
|
|
|
var firstWaiting = stages.FirstOrDefault(x => x.StageStatus == 3);
|
|
|
|
|
|
if (firstWaiting != null) firstWaiting.StageStatus = 2;
|
|
|
|
|
|
foreach (var stage in stages) stage.StageStatusText = ToStageStatusText(stage.StageStatus);
|
|
|
|
|
|
|
|
|
|
|
|
context.Stages = stages;
|
|
|
|
|
|
context.CurrentCheckin = stages.FirstOrDefault(x => x.StageStatus == 1);
|
|
|
|
|
|
|
|
|
|
|
|
// 当前正在比赛:末检次已通过且顺序最靠后的一列。
|
|
|
|
|
|
var competingUnit = options.Units
|
|
|
|
|
|
.Where(x => context.IsPassed(x.UnitKey, lastCheckinNo))
|
|
|
|
|
|
.OrderByDescending(x => x.Order)
|
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
|
if (competingUnit != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
context.CurrentCompeting = new CheckinProgressStageDto
|
|
|
|
|
|
{
|
|
|
|
|
|
EventProjectId = competingUnit.EventProjectId,
|
|
|
|
|
|
GroupId = competingUnit.GroupId,
|
|
|
|
|
|
ProjectName = competingUnit.ProjectName,
|
|
|
|
|
|
GroupName = competingUnit.GroupName,
|
|
|
|
|
|
DisplayName = $"{competingUnit.ProjectName} - {competingUnit.GroupName}",
|
|
|
|
|
|
Order = competingUnit.Order,
|
|
|
|
|
|
StageStatus = 1,
|
|
|
|
|
|
StageStatusText = "比赛中",
|
|
|
|
|
|
TeamName = ResolveTeamName(competingUnit),
|
2026-09-01 14:16:11 +08:00
|
|
|
|
UnitName = string.IsNullOrWhiteSpace(competingUnit.UnitName) ? "/" : competingUnit.UnitName,
|
|
|
|
|
|
// 秩序册列没有独立的报名类型字段,以 TeamName 是否为空区分个人与团队报名。
|
|
|
|
|
|
RegistrationTypeText = string.IsNullOrWhiteSpace(competingUnit.TeamName) ? "个人报名" : "团队报名",
|
2026-08-24 00:07:15 +08:00
|
|
|
|
AthleteNames = competingUnit.Athletes.Select(x => x.Name).ToList()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (context.CurrentCheckin != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var checkingUnit = options.Units
|
|
|
|
|
|
.Where(x => x.EventProjectId == context.CurrentCheckin.EventProjectId &&
|
|
|
|
|
|
x.GroupId == context.CurrentCheckin.GroupId)
|
|
|
|
|
|
.OrderBy(x => x.Order)
|
|
|
|
|
|
.FirstOrDefault(x => !context.IsPassed(x.UnitKey, lastCheckinNo));
|
|
|
|
|
|
if (checkingUnit != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
context.CurrentCheckin.TeamName = ResolveTeamName(checkingUnit);
|
|
|
|
|
|
context.CurrentCheckin.AthleteNames = checkingUnit.Athletes.Select(x => x.Name).ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return context;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private CheckinProgressMineDto BuildMineDto(
|
|
|
|
|
|
CheckinOrderBookUnitDto unit,
|
|
|
|
|
|
CheckinProgressContext context,
|
|
|
|
|
|
List<CheckinTask> activeTasks,
|
|
|
|
|
|
HashSet<long> myPersonIds)
|
|
|
|
|
|
{
|
|
|
|
|
|
var status = ResolveUnitStatus(unit, context, activeTasks);
|
|
|
|
|
|
var athleteNames = unit.Athletes.Where(x => myPersonIds.Contains(x.Id)).Select(x => x.Name).ToList();
|
|
|
|
|
|
if (athleteNames.Count == 0) athleteNames = unit.Athletes.Select(x => x.Name).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
return new CheckinProgressMineDto
|
|
|
|
|
|
{
|
|
|
|
|
|
UnitKey = unit.UnitKey,
|
|
|
|
|
|
EventProjectId = unit.EventProjectId,
|
|
|
|
|
|
GroupId = unit.GroupId,
|
|
|
|
|
|
ProjectName = unit.ProjectName,
|
|
|
|
|
|
GroupName = unit.GroupName,
|
|
|
|
|
|
DisplayName = $"{unit.ProjectName} - {unit.GroupName}",
|
|
|
|
|
|
AthleteNames = athleteNames,
|
|
|
|
|
|
TeamName = ResolveTeamName(unit),
|
|
|
|
|
|
UnitName = string.IsNullOrWhiteSpace(unit.UnitName) ? "/" : unit.UnitName,
|
|
|
|
|
|
SessionIndex = unit.SessionIndex,
|
|
|
|
|
|
VenueName = unit.VenueName,
|
|
|
|
|
|
CheckinStatus = status,
|
|
|
|
|
|
CheckinStatusText = ToCheckinStatusText(status),
|
|
|
|
|
|
Rounds = context.ActiveCheckinNumbers.Select(checkinNo => new CheckinProgressRoundDto
|
|
|
|
|
|
{
|
|
|
|
|
|
CheckinNo = checkinNo,
|
|
|
|
|
|
CheckinName = ToCheckinName(checkinNo),
|
|
|
|
|
|
Passed = context.IsPassed(unit.UnitKey, checkinNo),
|
2026-09-01 14:16:11 +08:00
|
|
|
|
CheckedTime = context.GetCheckedTime(unit.UnitKey, checkinNo),
|
|
|
|
|
|
CheckerName = context.GetCheckerName(unit.UnitKey, checkinNo)
|
2026-08-24 00:07:15 +08:00
|
|
|
|
}).ToList(),
|
|
|
|
|
|
Notice = status switch
|
|
|
|
|
|
{
|
|
|
|
|
|
1 => "请前往检录口",
|
|
|
|
|
|
2 => "已完成检录,请等待上场",
|
|
|
|
|
|
3 => "已上场",
|
|
|
|
|
|
_ => "请留意检录通知"
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>我的报名人员:本人报名记录 + 我作为联系人提交的报名单下全部人员。</summary>
|
|
|
|
|
|
private async Task<HashSet<long>> ResolveMyPersonIdsAsync(long eventId, long userId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var mine = await _personRepository.AsQueryable()
|
|
|
|
|
|
.Where(x => x.UserId == userId && !x.DeleteMark)
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
var registrationIds = await _registrationRepository.AsQueryable()
|
|
|
|
|
|
.Where(x => x.EventId == eventId && x.CreatedUserId == userId && !x.DeleteMark)
|
|
|
|
|
|
.Select(x => x.Id).ToListAsync();
|
|
|
|
|
|
var teamPersons = registrationIds.Count == 0
|
|
|
|
|
|
? new List<EventRegistrationPerson>()
|
|
|
|
|
|
: await _personRepository.AsQueryable()
|
|
|
|
|
|
.Where(x => registrationIds.Contains(x.RegistrationId) && !x.DeleteMark)
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
return mine.Select(x => x.Id).Concat(teamPersons.Select(x => x.Id)).ToHashSet();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>秩序册列状态:0-等待中,1-检录中,2-已检录,3-已上场。</summary>
|
|
|
|
|
|
private static int ResolveUnitStatus(
|
|
|
|
|
|
CheckinOrderBookUnitDto unit, CheckinProgressContext context, List<CheckinTask> activeTasks)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (context.IsPassed(unit.UnitKey, context.LastCheckinNo)) return 3;
|
|
|
|
|
|
if (context.IsPassed(unit.UnitKey, context.FirstCheckinNo))
|
|
|
|
|
|
return activeTasks.Count > 1 ? 2 : 3;
|
|
|
|
|
|
|
|
|
|
|
|
var stage = context.CurrentCheckin;
|
|
|
|
|
|
return stage != null && stage.EventProjectId == unit.EventProjectId && stage.GroupId == unit.GroupId
|
|
|
|
|
|
? 1
|
|
|
|
|
|
: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static int ResolveStageStatus(int total, int checkedCount, int finishedCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (total > 0 && finishedCount == total) return 4;
|
|
|
|
|
|
if (checkedCount > 0) return 1;
|
|
|
|
|
|
return 3;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string ResolveTeamName(CheckinOrderBookUnitDto unit)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(unit.TeamName)) return unit.TeamName;
|
|
|
|
|
|
return string.IsNullOrWhiteSpace(unit.UnitName) ? "/" : unit.UnitName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string ToStageStatusText(int stageStatus) => stageStatus switch
|
|
|
|
|
|
{
|
|
|
|
|
|
1 => "检录中", 2 => "即将开始", 4 => "已完成", _ => "等待中"
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
private static string ToCheckinStatusText(int status) => status switch
|
|
|
|
|
|
{
|
|
|
|
|
|
1 => "检录中", 2 => "已检录", 3 => "已上场", _ => "等待中"
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
private static string ToWeekDayText(DateTime time) => time.DayOfWeek switch
|
|
|
|
|
|
{
|
|
|
|
|
|
DayOfWeek.Monday => "星期一",
|
|
|
|
|
|
DayOfWeek.Tuesday => "星期二",
|
|
|
|
|
|
DayOfWeek.Wednesday => "星期三",
|
|
|
|
|
|
DayOfWeek.Thursday => "星期四",
|
|
|
|
|
|
DayOfWeek.Friday => "星期五",
|
|
|
|
|
|
DayOfWeek.Saturday => "星期六",
|
|
|
|
|
|
_ => "星期日"
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>检录进度计算的中间上下文,避免重复查询检录记录。</summary>
|
|
|
|
|
|
private sealed class CheckinProgressContext
|
|
|
|
|
|
{
|
2026-09-01 14:16:11 +08:00
|
|
|
|
// 两个字典的键结构一致:外层为秩序册列 UnitKey,内层为检次号。
|
2026-08-24 00:07:15 +08:00
|
|
|
|
public Dictionary<string, Dictionary<int, DateTime?>> PassedByKey { get; init; } = new();
|
2026-09-01 14:16:11 +08:00
|
|
|
|
public Dictionary<string, Dictionary<int, string>> CheckerNameByKey { get; init; } = new();
|
2026-08-24 00:07:15 +08:00
|
|
|
|
public int FirstCheckinNo { get; init; } = 1;
|
|
|
|
|
|
public int LastCheckinNo { get; init; } = 1;
|
|
|
|
|
|
public List<int> ActiveCheckinNumbers { get; init; } = new();
|
|
|
|
|
|
public List<CheckinProgressStageDto> Stages { get; set; } = new();
|
|
|
|
|
|
public CheckinProgressStageDto? CurrentCheckin { get; set; }
|
|
|
|
|
|
public CheckinProgressStageDto? CurrentCompeting { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsPassed(string unitKey, int checkinNo) =>
|
|
|
|
|
|
PassedByKey.TryGetValue(unitKey, out var rounds) && rounds.ContainsKey(checkinNo);
|
|
|
|
|
|
|
|
|
|
|
|
public DateTime? GetCheckedTime(string unitKey, int checkinNo) =>
|
|
|
|
|
|
PassedByKey.TryGetValue(unitKey, out var rounds) && rounds.TryGetValue(checkinNo, out var time)
|
|
|
|
|
|
? time
|
|
|
|
|
|
: null;
|
2026-09-01 14:16:11 +08:00
|
|
|
|
|
|
|
|
|
|
public string GetCheckerName(string unitKey, int checkinNo) =>
|
|
|
|
|
|
CheckerNameByKey.TryGetValue(unitKey, out var rounds) && rounds.TryGetValue(checkinNo, out var name)
|
|
|
|
|
|
? name
|
|
|
|
|
|
: string.Empty;
|
2026-08-24 00:07:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|