using YD_Event.Application.Event.Dtos;
using YD_Event.Core.Entities;
using MiniExcelLibs;
using SqlSugar;
using OfficeOpenXml;
using OfficeOpenXml.Style;
using System.Drawing;
namespace YD_Event.Application.Event;
///
/// 赛事编排服务
///
public class EventArrangementService : BaseService, ITransient
{
private readonly ISqlSugarRepository _eventProjectRepository;
private readonly ISqlSugarRepository _projectRepository;
private readonly ISqlSugarRepository _groupRepository;
private readonly ISqlSugarRepository _personRepository;
private readonly ISqlSugarRepository _registrationRepository;
private readonly ISqlSugarRepository _projectTypeRepository;
private readonly ISqlSugarRepository _eventInfoRepository;
public EventArrangementService(
ISqlSugarRepository eventProjectRepository,
ISqlSugarRepository projectRepository,
ISqlSugarRepository groupRepository,
ISqlSugarRepository personRepository,
ISqlSugarRepository registrationRepository,
ISqlSugarRepository projectTypeRepository,
ISqlSugarRepository eventInfoRepository) : base(projectRepository)
{
_eventProjectRepository = eventProjectRepository;
_projectRepository = projectRepository;
_groupRepository = groupRepository;
_personRepository = personRepository;
_registrationRepository = registrationRepository;
_projectTypeRepository = projectTypeRepository;
_eventInfoRepository = eventInfoRepository;
}
///
/// 获取编排项目列表
///
[DisplayName("获取编排项目列表")]
public async Task> 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((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();
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()
};
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;
}
///
/// 更新编排设置
///
[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);
}
}
}
///
/// 编排预览
///
[DisplayName("编排预览")]
public async Task> 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();
// 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((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;
}
///
/// 导出编排
///
[DisplayName("导出编排")]
public async Task Export(long eventId)
{
var eventInfo = await _eventInfoRepository.GetFirstAsync(x => x.Id == eventId);
var data = await Preview(eventId);
// 设置 EPPlus 许可证上下文
ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
using var package = new ExcelPackage();
var worksheet = package.Workbook.Worksheets.Add("编排表");
// 第一行:赛事标题(合并单元格 A1:G1)
worksheet.Cells["A1:G1"].Merge = true;
worksheet.Cells["A1"].Value = eventInfo?.EventName ?? "赛事编排表";
// 设置标题样式
var titleStyle = worksheet.Cells["A1:G1"].Style;
titleStyle.Font.Bold = true;
titleStyle.Font.Size = 14;
titleStyle.HorizontalAlignment = ExcelHorizontalAlignment.Center;
titleStyle.VerticalAlignment = ExcelVerticalAlignment.Center;
titleStyle.Fill.PatternType = ExcelFillStyle.Solid;
titleStyle.Fill.BackgroundColor.SetColor(Color.FromArgb(255, 217, 179)); // 浅橙色
// 第二行:列标题
string[] headers = { "时间", "参赛项目", "参赛组别", "参赛队伍", "运动员姓名", "场次", "场地" };
for (int i = 0; i < headers.Length; i++)
{
worksheet.Cells[2, i + 1].Value = headers[i];
}
// 设置列标题样式
var headerStyle = worksheet.Cells["A2:G2"].Style;
headerStyle.Font.Bold = true;
headerStyle.Font.Size = 11;
headerStyle.HorizontalAlignment = ExcelHorizontalAlignment.Center;
headerStyle.VerticalAlignment = ExcelVerticalAlignment.Center;
headerStyle.Fill.PatternType = ExcelFillStyle.Solid;
headerStyle.Fill.BackgroundColor.SetColor(Color.FromArgb(204, 255, 204)); // 浅绿色
headerStyle.Border.Top.Style = ExcelBorderStyle.Thin;
headerStyle.Border.Bottom.Style = ExcelBorderStyle.Thin;
headerStyle.Border.Left.Style = ExcelBorderStyle.Thin;
headerStyle.Border.Right.Style = ExcelBorderStyle.Thin;
// 填充数据行
for (int i = 0; i < data.Count; i++)
{
var item = data[i];
int rowIndex = i + 3;
worksheet.Cells[rowIndex, 1].Value = item.TimeRange;
worksheet.Cells[rowIndex, 2].Value = item.ProjectName;
worksheet.Cells[rowIndex, 3].Value = item.GroupName;
worksheet.Cells[rowIndex, 4].Value = item.TeamName ?? "";
worksheet.Cells[rowIndex, 5].Value = item.AthleteName;
worksheet.Cells[rowIndex, 6].Value = item.SessionIndex;
worksheet.Cells[rowIndex, 7].Value = item.VenueIndex;
// 设置数据行样式
var rowStyle = worksheet.Cells[$"A{rowIndex}:G{rowIndex}"].Style;
rowStyle.HorizontalAlignment = ExcelHorizontalAlignment.Center;
rowStyle.VerticalAlignment = ExcelVerticalAlignment.Center;
rowStyle.Border.Top.Style = ExcelBorderStyle.Thin;
rowStyle.Border.Bottom.Style = ExcelBorderStyle.Thin;
rowStyle.Border.Left.Style = ExcelBorderStyle.Thin;
rowStyle.Border.Right.Style = ExcelBorderStyle.Thin;
// 交替行颜色
if (i % 2 == 1)
{
rowStyle.Fill.PatternType = ExcelFillStyle.Solid;
rowStyle.Fill.BackgroundColor.SetColor(Color.FromArgb(240, 255, 240)); // 更浅的绿色
}
}
// 设置列宽
worksheet.Column(1).Width = 12; // 时间
worksheet.Column(2).Width = 18; // 参赛项目
worksheet.Column(3).Width = 15; // 参赛组别
worksheet.Column(4).Width = 25; // 参赛队伍
worksheet.Column(5).Width = 15; // 运动员姓名
worksheet.Column(6).Width = 8; // 场次
worksheet.Column(7).Width = 8; // 场地
// 设置行高
worksheet.Row(1).Height = 30; // 标题行
worksheet.Row(2).Height = 25; // 列标题行
// 冻结首行和首列
worksheet.View.FreezePanes(3, 1);
return await package.GetAsByteArrayAsync();
}
}