using YD_Event.Application.Event.Dtos;
using YD_Event.Core.Entities;
using MiniExcelLibs;
namespace YD_Event.Application.Event;
///
/// 赛事管理服务
///
public class EventService : BaseService, ITransient
{
private readonly ISqlSugarRepository _repository;
private readonly ISqlSugarRepository _participantRepository;
private readonly ISqlSugarRepository _customFieldRepository;
private readonly ISqlSugarRepository _provinceRepository;
private readonly ISqlSugarRepository _cityRepository;
private readonly ISqlSugarRepository _districtRepository;
private readonly ISqlSugarRepository _projectRepository;
private readonly ISqlSugarRepository _projectGroupRepository;
private readonly ISqlSugarRepository _registrationLimitRepository;
private readonly ISqlSugarRepository _projectLimitRepository;
private readonly ISqlSugarRepository _feeConfigRepository;
private readonly ISqlSugarRepository _registrationRepository;
private readonly ISqlSugarRepository _merchantRepository;
private readonly IIdGenerator _idGenerator;
public EventService(
ISqlSugarRepository repository,
ISqlSugarRepository participantRepository,
ISqlSugarRepository customFieldRepository,
ISqlSugarRepository provinceRepository,
ISqlSugarRepository cityRepository,
ISqlSugarRepository districtRepository,
ISqlSugarRepository projectRepository,
ISqlSugarRepository projectGroupRepository,
ISqlSugarRepository registrationLimitRepository,
ISqlSugarRepository projectLimitRepository,
ISqlSugarRepository feeConfigRepository,
ISqlSugarRepository registrationRepository,
ISqlSugarRepository merchantRepository,
IIdGenerator idGenerator) : base(repository)
{
_repository = repository;
_participantRepository = participantRepository;
_customFieldRepository = customFieldRepository;
_provinceRepository = provinceRepository;
_cityRepository = cityRepository;
_districtRepository = districtRepository;
_projectRepository = projectRepository;
_projectGroupRepository = projectGroupRepository;
_registrationLimitRepository = registrationLimitRepository;
_projectLimitRepository = projectLimitRepository;
_feeConfigRepository = feeConfigRepository;
_registrationRepository = registrationRepository;
_merchantRepository = merchantRepository;
_idGenerator = idGenerator;
}
///
/// 分页查询赛事
///
///
///
[DisplayName("分页查询赛事")]
public async Task> Page(EventQueryInput input)
{
var query = _repository.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.EventName), x => x.EventName.Contains(input.EventName))
.WhereIF(input.EventType.HasValue, x => x.EventType == input.EventType)
.WhereIF(input.ChargeMode.HasValue, x => x.ChargeMode == input.ChargeMode)
.WhereIF(input.EventStatus.HasValue, x => x.EventStatus == input.EventStatus)
.WhereIF(input.RegistrationStartTime.HasValue, x => x.RegistrationStartTime >= input.RegistrationStartTime)
.WhereIF(input.RegistrationEndTime.HasValue, x => x.RegistrationEndTime <= input.RegistrationEndTime)
.WhereIF(input.MatchStartTime.HasValue, x => x.MatchStartTime >= input.MatchStartTime)
.WhereIF(input.MatchEndTime.HasValue, x => x.MatchEndTime <= input.MatchEndTime)
.OrderByDescending(x => x.CreatedTime)
.Select(x => new EventListOutput
{
Id = x.Id,
EventName = x.EventName,
CoverImage = x.CoverImage,
EventType = x.EventType,
RegistrationType = x.RegistrationType,
ChargeMode = x.ChargeMode,
EventStatus = x.EventStatus,
RegistrationStartTime = x.RegistrationStartTime,
RegistrationEndTime = x.RegistrationEndTime,
MatchStartTime = x.MatchStartTime,
MatchEndTime = x.MatchEndTime,
// AccountName 暂未关联
});
return await query.ToPagedListAsync(input);
}
///
/// 获取赛事详情
///
///
///
[DisplayName("获取赛事详情")]
public async Task GetDetail(long id)
{
var eventInfo = await _repository.GetByIdAsync(id);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
var output = eventInfo.Adapt();
// 1. 获取参与单位
var participant = await _participantRepository.GetFirstAsync(x => x.EventId == id);
if (participant != null)
{
output.Participant = participant.Adapt();
}
// 2. 获取自定义字段
var customFields = await _customFieldRepository.GetListAsync(x => x.EventId == id);
output.CustomFields = customFields.OrderBy(x => x.Sort).Select(x => x.FieldName).ToList();
// 3. 获取区域名称
if (output.ProvinceId > 0)
{
var p = await _provinceRepository.GetFirstAsync(x => x.ProvinceId == output.ProvinceId);
output.ProvinceName = p?.ProvinceName;
}
if (output.CityId > 0)
{
var c = await _cityRepository.GetFirstAsync(x => x.CityId == output.CityId);
output.CityName = c?.CityName;
}
if (output.DistrictId > 0)
{
var d = await _districtRepository.GetFirstAsync(x => x.DistrictId == output.DistrictId);
output.DistrictName = d?.DistrictName;
}
// 4. 获取项目列表 (Step 3)
var projects = await _projectRepository.GetListAsync(x => x.EventId == id);
if (projects.Any())
{
var projectIds = projects.Select(x => x.Id).ToList();
var groups = await _projectGroupRepository.GetListAsync(x => projectIds.Contains(x.ProjectId));
var projectLimits = await _projectLimitRepository.GetListAsync(x => projectIds.Contains(x.ProjectId));
output.Projects = projects.Select(p =>
{
var dto = p.Adapt();
dto.Groups = groups.Where(g => g.ProjectId == p.Id).Select(g => g.Adapt()).ToList();
var limit = projectLimits.FirstOrDefault(l => l.ProjectId == p.Id);
if (limit != null) dto.LimitCount = limit.LimitCount;
return dto;
}).ToList();
}
// 5. 获取报名限制 (Step 3)
var registrationLimit = await _registrationLimitRepository.GetFirstAsync(x => x.EventId == id);
if (registrationLimit != null)
{
output.RegistrationLimit = registrationLimit.Adapt();
}
// 6. 获取费用配置 (Step 4)
var feeConfig = await _feeConfigRepository.GetFirstAsync(x => x.EventId == id);
if (feeConfig != null)
{
output.FeeConfig = feeConfig.Adapt();
// 获取商户详情
if (feeConfig.MerchantId > 0)
{
var merchant = await _merchantRepository.GetByIdAsync(feeConfig.MerchantId);
if (merchant != null)
{
output.FeeConfig.MerchantName = merchant.MerchantName;
output.FeeConfig.CreditCode = merchant.CreditCode;
output.FeeConfig.BankName = merchant.BankName;
output.FeeConfig.AccountNumber = merchant.AccountNumber;
}
}
}
return output;
}
///
/// 新增赛事
///
///
///
[UnitOfWork]
[DisplayName("新增赛事")]
public async Task Add(AddEventInput input)
{
var eventInfo = input.Adapt();
eventInfo.Id = _idGenerator.NewLong();
eventInfo.CreatedTime = DateTime.Now;
eventInfo.Status = AvailabilityStatus.Enable;
var now = DateTime.Now;
if (now < eventInfo.RegistrationStartTime) eventInfo.EventStatus = EventStatus.Enrolling;
else if (now > eventInfo.MatchEndTime) eventInfo.EventStatus = EventStatus.Ended;
else if (now >= eventInfo.MatchStartTime) eventInfo.EventStatus = EventStatus.Ongoing;
else eventInfo.EventStatus = EventStatus.Enrolling;
await _repository.InsertAsync(eventInfo);
// 1. 保存参与单位
if (input.Participant != null)
{
var participant = input.Participant.Adapt();
participant.Id = _idGenerator.NewLong();
participant.EventId = eventInfo.Id;
await _participantRepository.InsertAsync(participant);
}
// 2. 保存自定义字段
if (input.CustomFields != null && input.CustomFields.Any())
{
var fields = input.CustomFields.Select((name, index) => new EventCustomField
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
FieldName = name,
Sort = index
}).ToList();
await _customFieldRepository.InsertRangeAsync(fields);
}
// 3. 保存项目信息 (Step 3)
if (input.Projects != null && input.Projects.Any())
{
foreach (var projInput in input.Projects)
{
var project = projInput.Adapt();
project.Id = _idGenerator.NewLong();
project.EventId = eventInfo.Id;
project.Status = AvailabilityStatus.Enable;
project.CreatedTime = DateTime.Now;
await _projectRepository.InsertAsync(project);
// 保存项目分组
if (projInput.Groups != null && projInput.Groups.Any())
{
var groups = projInput.Groups.Select(g =>
{
var group = g.Adapt();
group.Id = _idGenerator.NewLong();
group.ProjectId = project.Id;
return group;
}).ToList();
await _projectGroupRepository.InsertRangeAsync(groups);
}
// 保存项目报名限制 (Step 3 - Project Limit)
if (projInput.LimitCount.HasValue)
{
var projLimit = new EventProjectRegistrationLimit
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
ProjectId = project.Id,
LimitCount = projInput.LimitCount.Value
};
await _projectLimitRepository.InsertAsync(projLimit);
}
}
}
// 4. 保存报名限制 (Step 3 - Individual/Team Limit)
if (input.RegistrationLimit != null)
{
var limit = input.RegistrationLimit.Adapt();
limit.Id = _idGenerator.NewLong();
limit.EventId = eventInfo.Id;
await _registrationLimitRepository.InsertAsync(limit);
}
// 5. 保存费用配置 (Step 4)
if (input.FeeConfig != null)
{
var feeConfig = input.FeeConfig.Adapt();
feeConfig.Id = _idGenerator.NewLong();
feeConfig.EventId = eventInfo.Id;
await _feeConfigRepository.InsertAsync(feeConfig);
}
}
///
/// 更新赛事
///
///
///
[UnitOfWork]
[DisplayName("更新赛事")]
public async Task Update(UpdateEventInput input)
{
var eventInfo = await _repository.GetByIdAsync(input.Id);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
var oldEventName = eventInfo.EventName;
input.Adapt(eventInfo);
eventInfo.EventName = oldEventName; // 赛事名称不允许修改
eventInfo.UpdatedTime = DateTime.Now;
// 更新状态逻辑(可选,每次更新重新计算)
var now = DateTime.Now;
if (now > eventInfo.MatchEndTime) eventInfo.EventStatus = EventStatus.Ended;
else if (now >= eventInfo.MatchStartTime) eventInfo.EventStatus = EventStatus.Ongoing;
else eventInfo.EventStatus = EventStatus.Enrolling;
await _repository.UpdateAsync(eventInfo);
// 1. 更新参与单位
await _participantRepository.DeleteAsync(x => x.EventId == input.Id);
if (input.Participant != null)
{
var participant = input.Participant.Adapt();
participant.Id = _idGenerator.NewLong();
participant.EventId = eventInfo.Id;
await _participantRepository.InsertAsync(participant);
}
// 2. 更新自定义字段
await _customFieldRepository.DeleteAsync(x => x.EventId == input.Id);
if (input.CustomFields != null && input.CustomFields.Any())
{
var fields = input.CustomFields.Select((name, index) => new EventCustomField
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
FieldName = name,
Sort = index
}).ToList();
await _customFieldRepository.InsertRangeAsync(fields);
}
// 3. 更新项目信息
// 策略:全量删除旧的,插入新的。简单可靠。
// 获取旧项目ID列表,用于级联删除分组和限制
var oldProjects = await _projectRepository.GetListAsync(x => x.EventId == input.Id);
var oldProjectIds = oldProjects.Select(x => x.Id).ToList();
if (oldProjectIds.Any())
{
await _projectGroupRepository.DeleteAsync(x => oldProjectIds.Contains(x.ProjectId));
await _projectLimitRepository.DeleteAsync(x => oldProjectIds.Contains(x.ProjectId));
await _projectRepository.DeleteAsync(x => x.EventId == input.Id);
}
if (input.Projects != null && input.Projects.Any())
{
foreach (var projInput in input.Projects)
{
var project = projInput.Adapt();
project.Id = _idGenerator.NewLong();
project.EventId = eventInfo.Id;
project.Status = AvailabilityStatus.Enable;
project.CreatedTime = DateTime.Now;
await _projectRepository.InsertAsync(project);
if (projInput.Groups != null && projInput.Groups.Any())
{
var groups = projInput.Groups.Select(g =>
{
var group = g.Adapt();
group.Id = _idGenerator.NewLong();
group.ProjectId = project.Id;
return group;
}).ToList();
await _projectGroupRepository.InsertRangeAsync(groups);
}
if (projInput.LimitCount.HasValue)
{
var projLimit = new EventProjectRegistrationLimit
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
ProjectId = project.Id,
LimitCount = projInput.LimitCount.Value
};
await _projectLimitRepository.InsertAsync(projLimit);
}
}
}
// 4. 更新报名限制
await _registrationLimitRepository.DeleteAsync(x => x.EventId == input.Id);
if (input.RegistrationLimit != null)
{
var limit = input.RegistrationLimit.Adapt();
limit.Id = _idGenerator.NewLong();
limit.EventId = eventInfo.Id;
await _registrationLimitRepository.InsertAsync(limit);
}
// 5. 更新费用配置
await _feeConfigRepository.DeleteAsync(x => x.EventId == input.Id);
if (input.FeeConfig != null)
{
var feeConfig = input.FeeConfig.Adapt();
feeConfig.Id = _idGenerator.NewLong();
feeConfig.EventId = eventInfo.Id;
await _feeConfigRepository.InsertAsync(feeConfig);
}
}
///
/// 导出赛事
///
///
///
[DisplayName("导出赛事")]
public async Task Export(EventExportInput input)
{
var query = _repository.AsQueryable()
.LeftJoin((e, p) => e.Id == p.EventId)
.LeftJoin((e, p, f) => e.Id == f.EventId)
.LeftJoin((e, p, f, m) => f.MerchantId == m.Id)
.WhereIF(!string.IsNullOrWhiteSpace(input.EventName), e => e.EventName.Contains(input.EventName))
.WhereIF(input.EventType.HasValue, e => e.EventType == input.EventType)
.WhereIF(input.ChargeMode.HasValue, e => e.ChargeMode == input.ChargeMode)
.WhereIF(input.EventStatus.HasValue, e => e.EventStatus == input.EventStatus)
.WhereIF(input.RegistrationStartTime.HasValue, e => e.RegistrationStartTime >= input.RegistrationStartTime)
.WhereIF(input.RegistrationEndTime.HasValue, e => e.RegistrationEndTime <= input.RegistrationEndTime)
.WhereIF(input.MatchStartTime.HasValue, e => e.MatchStartTime >= input.MatchStartTime)
.WhereIF(input.MatchEndTime.HasValue, e => e.MatchEndTime <= input.MatchEndTime)
.Select((e, p, f, m) => new
{
e.EventName,
e.EventType,
e.ChargeMode,
e.RegistrationType,
e.EventStatus,
e.RegistrationStartTime,
e.RegistrationEndTime,
e.MaterialUploadEndTime,
e.MatchStartTime,
e.MatchEndTime,
e.DetailAddress,
p.HostName,
p.OrganizerName,
p.CoOrganizerName,
p.SponsorName,
MerchantName = m.MerchantName
});
var list = await query.ToListAsync();
var exportData = new List>();
foreach (var item in list)
{
var row = new Dictionary();
var fields = input.ExportFields ?? new List();
bool all = fields.Count == 0;
if (all || fields.Contains("赛事名称")) row["赛事名称"] = item.EventName;
if (all || fields.Contains("赛事类型")) row["赛事类型"] = item.EventType.ToString(); // TODO: Enum Description
if (all || fields.Contains("收费模式")) row["收费模式"] = item.ChargeMode.ToString();
if (all || fields.Contains("收款商户")) row["收款商户"] = item.MerchantName;
if (all || fields.Contains("报名方式")) row["报名方式"] = item.RegistrationType.ToString();
if (all || fields.Contains("赛事状态")) row["赛事状态"] = item.EventStatus.ToString();
if (all || fields.Contains("报名起止时间")) row["报名起止时间"] = $"{item.RegistrationStartTime:yyyy-MM-dd HH:mm} - {item.RegistrationEndTime:yyyy-MM-dd HH:mm}";
if (all || fields.Contains("上传素材截至时间")) row["上传素材截至时间"] = item.MaterialUploadEndTime?.ToString("yyyy-MM-dd HH:mm");
if (all || fields.Contains("比赛起止时间")) row["比赛起止时间"] = $"{item.MatchStartTime:yyyy-MM-dd HH:mm} - {item.MatchEndTime:yyyy-MM-dd HH:mm}";
if (all || fields.Contains("赛事地址")) row["赛事地址"] = item.DetailAddress;
if (all || fields.Contains("主办单位")) row["主办单位"] = item.HostName;
if (all || fields.Contains("承办单位")) row["承办单位"] = item.OrganizerName;
if (all || fields.Contains("协办单位")) row["协办单位"] = item.CoOrganizerName;
if (all || fields.Contains("赞助单位")) row["赞助单位"] = item.SponsorName;
exportData.Add(row);
}
using var stream = new MemoryStream();
stream.SaveAs(exportData);
return stream.ToArray();
}
///
/// 删除赛事
///
///
///
[DisplayName("删除赛事")]
public override async Task Delete(KeyDto dto)
{
if (await _registrationRepository.IsAnyAsync(x => x.EventId == dto.Id))
{
throw new InvalidOperationException("已有人员报名此赛事,暂不支持删除");
}
// 软删除 EventInfo
await base.Delete(dto);
// 关联表物理删除或保留?通常保留。如果需要清理:
// await _participantRepository.DeleteAsync(x => x.EventId == dto.Id);
// await _customFieldRepository.DeleteAsync(x => x.EventId == dto.Id);
}
}