YD_Event.Admin/YD_Event.Application/Event/EventArrangementService.cs

297 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <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;
private readonly ISqlSugarRepository<EventInfo> _eventInfoRepository;
public EventArrangementService(
ISqlSugarRepository<EventProject> eventProjectRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
ISqlSugarRepository<ProjectGroup> groupRepository,
ISqlSugarRepository<EventRegistrationPerson> personRepository,
ISqlSugarRepository<EventRegistration> registrationRepository,
ISqlSugarRepository<ProjectType> projectTypeRepository,
ISqlSugarRepository<EventInfo> eventInfoRepository) : base(projectRepository)
{
_eventProjectRepository = eventProjectRepository;
_projectRepository = projectRepository;
_groupRepository = groupRepository;
_personRepository = personRepository;
_registrationRepository = registrationRepository;
_projectTypeRepository = projectTypeRepository;
_eventInfoRepository = eventInfoRepository;
}
/// <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 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();
}
}