using YD_Event.Application.Event.Dtos;
using YD_Event.Application.Project.Dtos;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using MiniExcelLibs;
using SqlSugar;
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 _projectTypeRepository;
private readonly ISqlSugarRepository _registrationLimitRepository;
private readonly ISqlSugarRepository _projectLimitRepository;
private readonly ISqlSugarRepository _feeConfigRepository;
private readonly ISqlSugarRepository _registrationRepository;
private readonly ISqlSugarRepository _merchantRepository;
private readonly IIdGenerator _idGenerator;
private readonly YD_Event.Application.Menu.SysMenuService _sysMenuService;
private readonly YD_Event.Application.Auth.AuthManager _authManager;
public EventService(
ISqlSugarRepository repository,
ISqlSugarRepository participantRepository,
ISqlSugarRepository customFieldRepository,
ISqlSugarRepository provinceRepository,
ISqlSugarRepository cityRepository,
ISqlSugarRepository districtRepository,
ISqlSugarRepository projectRepository,
ISqlSugarRepository projectGroupRepository,
ISqlSugarRepository projectTypeRepository,
ISqlSugarRepository registrationLimitRepository,
ISqlSugarRepository projectLimitRepository,
ISqlSugarRepository feeConfigRepository,
ISqlSugarRepository registrationRepository,
ISqlSugarRepository merchantRepository,
IIdGenerator idGenerator,
YD_Event.Application.Menu.SysMenuService sysMenuService,
YD_Event.Application.Auth.AuthManager authManager) : base(repository)
{
_repository = repository;
_participantRepository = participantRepository;
_customFieldRepository = customFieldRepository;
_provinceRepository = provinceRepository;
_cityRepository = cityRepository;
_districtRepository = districtRepository;
_projectRepository = projectRepository;
_projectGroupRepository = projectGroupRepository;
_projectTypeRepository = projectTypeRepository;
_registrationLimitRepository = registrationLimitRepository;
_projectLimitRepository = projectLimitRepository;
_feeConfigRepository = feeConfigRepository;
_registrationRepository = registrationRepository;
_merchantRepository = merchantRepository;
_idGenerator = idGenerator;
_sysMenuService = sysMenuService;
_authManager = authManager;
}
///
/// 分页查询赛事
///
///
///
[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);
// 数据权限过滤
if (!_authManager.IsSuperAdmin)
{
// 检查全部数据权限
var hasAllData = await _sysMenuService.CheckPermission("event:data:all");
if (!hasAllData)
{
// 检查个人数据权限
var hasPersonalData = await _sysMenuService.CheckPermission("event:data:personal");
if (hasPersonalData)
{
var userId = _authManager.UserId;
query = query.Where(x => x.CreatedUserId == userId);
}
else
{
// 无权限,返回空
return new PageResult();
}
}
}
var resultQuery = query.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 resultQuery.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));
// 获取项目类型信息
var typeIds = projects.Select(x => x.ProjectTypeId).Distinct().ToList();
var types = await _projectTypeRepository.GetListAsync(x => typeIds.Contains(x.Id));
output.Projects = projects.Select(p =>
{
var dto = p.Adapt();
var type = types.FirstOrDefault(x => x.Id == p.ProjectTypeId);
if (type != null)
{
dto.ProjectTypeName = type.Name;
}
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);
}
}