205 lines
8.2 KiB
C#
205 lines
8.2 KiB
C#
using YD_Event.Application.Event.Dtos;
|
||
using YD_Event.Core.Entities;
|
||
using MiniExcelLibs;
|
||
using SqlSugar;
|
||
|
||
namespace YD_Event.Application.Event;
|
||
|
||
/// <summary>
|
||
/// 赛事编排服务
|
||
/// </summary>
|
||
public class EventArrangementService : BaseService<YD_Event.Core.Entities.Project>, ITransient
|
||
{
|
||
private readonly ISqlSugarRepository<EventProject> _eventProjectRepository;
|
||
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
|
||
private readonly ISqlSugarRepository<ProjectGroup> _groupRepository;
|
||
private readonly ISqlSugarRepository<EventRegistrationPerson> _personRepository;
|
||
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
|
||
private readonly ISqlSugarRepository<ProjectType> _projectTypeRepository;
|
||
|
||
public EventArrangementService(
|
||
ISqlSugarRepository<EventProject> eventProjectRepository,
|
||
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
|
||
ISqlSugarRepository<ProjectGroup> groupRepository,
|
||
ISqlSugarRepository<EventRegistrationPerson> personRepository,
|
||
ISqlSugarRepository<EventRegistration> registrationRepository,
|
||
ISqlSugarRepository<ProjectType> projectTypeRepository) : base(projectRepository)
|
||
{
|
||
_eventProjectRepository = eventProjectRepository;
|
||
_projectRepository = projectRepository;
|
||
_groupRepository = groupRepository;
|
||
_personRepository = personRepository;
|
||
_registrationRepository = registrationRepository;
|
||
_projectTypeRepository = projectTypeRepository;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取编排项目列表
|
||
/// </summary>
|
||
[DisplayName("获取编排项目列表")]
|
||
public async Task<List<ArrangementProjectListOutput>> GetList(long eventId)
|
||
{
|
||
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == eventId);
|
||
var projectIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
|
||
var projects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
|
||
projects = projects.OrderBy(x => x.SortIndex).ToList();
|
||
|
||
var groups = await _groupRepository.GetListAsync(x => projectIds.Contains(x.ProjectId));
|
||
|
||
// 获取项目类型信息
|
||
var typeIds = projects.Select(x => x.ProjectTypeId).Distinct().ToList();
|
||
var types = await _projectTypeRepository.GetListAsync(x => typeIds.Contains(x.Id));
|
||
|
||
// 统计各组别报名人数(仅统计已支付或审核通过的)
|
||
var counts = await _personRepository.AsQueryable()
|
||
.InnerJoin<EventRegistration>((p, r) => p.RegistrationId == r.Id)
|
||
.Where((p, r) => r.EventId == eventId && (r.PaymentStatus == 1 || r.RegistrationStatus == 2))
|
||
.GroupBy(p => new { p.ProjectId, p.GroupName })
|
||
.Select(p => new { p.ProjectId, p.GroupName, Count = SqlFunc.AggregateCount(p.Id) })
|
||
.ToListAsync();
|
||
|
||
var result = new List<ArrangementProjectListOutput>();
|
||
foreach (var eventProj in eventProjects)
|
||
{
|
||
var proj = projects.FirstOrDefault(x => x.Id == eventProj.ProjectId);
|
||
if (proj == null) continue;
|
||
|
||
var type = types.FirstOrDefault(x => x.Id == proj.ProjectTypeId);
|
||
|
||
var dto = new ArrangementProjectListOutput
|
||
{
|
||
ProjectId = eventProj.Id,
|
||
ProjectName = proj.Name,
|
||
ProjectType = type?.Name ?? "未知类型",
|
||
DurationSeconds = proj.DurationSeconds,
|
||
VenueCount = proj.VenueCount,
|
||
SortIndex = proj.SortIndex,
|
||
Groups = new List<ArrangementGroupDto>()
|
||
};
|
||
|
||
var projGroups = groups.Where(g => g.ProjectId == proj.Id).ToList();
|
||
foreach (var group in projGroups)
|
||
{
|
||
var countObj = counts.FirstOrDefault(c => c.ProjectId == eventProj.Id && c.GroupName == group.GroupName);
|
||
dto.Groups.Add(new ArrangementGroupDto
|
||
{
|
||
GroupName = group.GroupName,
|
||
ParticipantCount = countObj?.Count ?? 0
|
||
});
|
||
}
|
||
|
||
// 估算总时长
|
||
int venueCount = proj.VenueCount > 0 ? proj.VenueCount : 1;
|
||
int totalParticipants = dto.Groups.Sum(g => g.ParticipantCount);
|
||
int rounds = (int)Math.Ceiling((double)totalParticipants / venueCount);
|
||
dto.TotalDurationMinutes = (rounds * proj.DurationSeconds) / 60;
|
||
|
||
result.Add(dto);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新编排设置
|
||
/// </summary>
|
||
[UnitOfWork]
|
||
[DisplayName("更新编排设置")]
|
||
public async Task Update(ArrangementUpdateInput input)
|
||
{
|
||
var eventProjectIds = input.Settings.Select(x => x.ProjectId).ToList();
|
||
var eventProjects = await _eventProjectRepository.GetListAsync(x => eventProjectIds.Contains(x.Id));
|
||
|
||
foreach (var setting in input.Settings)
|
||
{
|
||
var ep = eventProjects.FirstOrDefault(x => x.Id == setting.ProjectId);
|
||
if (ep != null)
|
||
{
|
||
await _projectRepository.UpdateAsync(x => new YD_Event.Core.Entities.Project
|
||
{
|
||
VenueCount = setting.VenueCount,
|
||
SortIndex = setting.SortIndex,
|
||
DurationSeconds = setting.DurationSeconds,
|
||
DurationMinutes = setting.DurationSeconds / 60
|
||
}, x => x.Id == ep.ProjectId);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 编排预览
|
||
/// </summary>
|
||
[DisplayName("编排预览")]
|
||
public async Task<List<ArrangementPreviewOutput>> Preview(long eventId)
|
||
{
|
||
// 1. 获取项目(按顺序)
|
||
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == eventId);
|
||
var projectIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
|
||
var projects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
|
||
projects = projects.OrderBy(x => x.SortIndex).ToList();
|
||
|
||
var result = new List<ArrangementPreviewOutput>();
|
||
|
||
// 2. 生成排期
|
||
foreach (var eventProj in eventProjects)
|
||
{
|
||
var proj = projects.FirstOrDefault(x => x.Id == eventProj.ProjectId);
|
||
if (proj == null) continue;
|
||
|
||
int venueCount = proj.VenueCount > 0 ? proj.VenueCount : 1;
|
||
|
||
// 获取参赛人员(排序:组别 -> ID)
|
||
var persons = await _personRepository.AsQueryable()
|
||
.InnerJoin<EventRegistration>((p, r) => p.RegistrationId == r.Id)
|
||
.Where((p, r) => p.ProjectId == eventProj.Id && (r.PaymentStatus == 1 || r.RegistrationStatus == 2))
|
||
.OrderBy(p => p.GroupName)
|
||
.OrderBy(p => p.Id)
|
||
.Select((p, r) => new {
|
||
p.Name, p.GroupName, r.TeamName,
|
||
Duration = proj.DurationSeconds
|
||
})
|
||
.ToListAsync();
|
||
|
||
if (!persons.Any()) continue;
|
||
|
||
// 分组逻辑
|
||
int currentRound = 1;
|
||
int currentVenue = 1;
|
||
|
||
foreach (var person in persons)
|
||
{
|
||
result.Add(new ArrangementPreviewOutput
|
||
{
|
||
TimeRange = $"{proj.DurationSeconds}''",
|
||
ProjectName = proj.Name,
|
||
GroupName = person.GroupName,
|
||
TeamName = person.TeamName,
|
||
AthleteName = person.Name,
|
||
SessionIndex = currentRound,
|
||
VenueIndex = currentVenue
|
||
});
|
||
|
||
currentVenue++;
|
||
if (currentVenue > venueCount)
|
||
{
|
||
currentVenue = 1;
|
||
currentRound++;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出编排
|
||
/// </summary>
|
||
[DisplayName("导出编排")]
|
||
public async Task<byte[]> Export(long eventId)
|
||
{
|
||
var data = await Preview(eventId);
|
||
using var stream = new MemoryStream();
|
||
stream.SaveAs(data);
|
||
return stream.ToArray();
|
||
}
|
||
}
|