2026-02-28 20:59:18 +08:00

530 lines
23 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;
namespace YD_Event.Application.Event;
/// <summary>
/// 赛事管理服务
/// </summary>
public class EventService : BaseService<EventInfo>, ITransient
{
private readonly ISqlSugarRepository<EventInfo> _repository;
private readonly ISqlSugarRepository<EventParticipant> _participantRepository;
private readonly ISqlSugarRepository<EventCustomField> _customFieldRepository;
private readonly ISqlSugarRepository<Sys_Province> _provinceRepository;
private readonly ISqlSugarRepository<Sys_City> _cityRepository;
private readonly ISqlSugarRepository<Sys_District> _districtRepository;
private readonly ISqlSugarRepository<EventProject> _projectRepository;
private readonly ISqlSugarRepository<EventProjectGroup> _projectGroupRepository;
private readonly ISqlSugarRepository<EventRegistrationLimit> _registrationLimitRepository;
private readonly ISqlSugarRepository<EventProjectRegistrationLimit> _projectLimitRepository;
private readonly ISqlSugarRepository<EventFeeConfig> _feeConfigRepository;
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Merchant> _merchantRepository;
private readonly IIdGenerator _idGenerator;
private readonly YD_Event.Application.Menu.SysMenuService _sysMenuService;
private readonly YD_Event.Application.Auth.AuthManager _authManager;
public EventService(
ISqlSugarRepository<EventInfo> repository,
ISqlSugarRepository<EventParticipant> participantRepository,
ISqlSugarRepository<EventCustomField> customFieldRepository,
ISqlSugarRepository<Sys_Province> provinceRepository,
ISqlSugarRepository<Sys_City> cityRepository,
ISqlSugarRepository<Sys_District> districtRepository,
ISqlSugarRepository<EventProject> projectRepository,
ISqlSugarRepository<EventProjectGroup> projectGroupRepository,
ISqlSugarRepository<EventRegistrationLimit> registrationLimitRepository,
ISqlSugarRepository<EventProjectRegistrationLimit> projectLimitRepository,
ISqlSugarRepository<EventFeeConfig> feeConfigRepository,
ISqlSugarRepository<EventRegistration> registrationRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Merchant> 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;
_registrationLimitRepository = registrationLimitRepository;
_projectLimitRepository = projectLimitRepository;
_feeConfigRepository = feeConfigRepository;
_registrationRepository = registrationRepository;
_merchantRepository = merchantRepository;
_idGenerator = idGenerator;
_sysMenuService = sysMenuService;
_authManager = authManager;
}
/// <summary>
/// 分页查询赛事
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询赛事")]
public async Task<PageResult<EventListOutput>> 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<EventListOutput>();
}
}
}
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);
}
/// <summary>
/// 获取赛事详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[DisplayName("获取赛事详情")]
public async Task<EventDetailOutput> GetDetail(long id)
{
var eventInfo = await _repository.GetByIdAsync(id);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
var output = eventInfo.Adapt<EventDetailOutput>();
// 1. 获取参与单位
var participant = await _participantRepository.GetFirstAsync(x => x.EventId == id);
if (participant != null)
{
output.Participant = participant.Adapt<EventParticipantInput>();
}
// 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<EventProjectInput>();
dto.Groups = groups.Where(g => g.ProjectId == p.Id).Select(g => g.Adapt<EventProjectGroupInput>()).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<EventRegistrationLimitInput>();
}
// 6. 获取费用配置 (Step 4)
var feeConfig = await _feeConfigRepository.GetFirstAsync(x => x.EventId == id);
if (feeConfig != null)
{
output.FeeConfig = feeConfig.Adapt<EventFeeConfigInput>();
// 获取商户详情
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;
}
/// <summary>
/// 新增赛事
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[UnitOfWork]
[DisplayName("新增赛事")]
public async Task Add(AddEventInput input)
{
var eventInfo = input.Adapt<EventInfo>();
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<EventParticipant>();
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<EventProject>();
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<EventProjectGroup>();
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<EventRegistrationLimit>();
limit.Id = _idGenerator.NewLong();
limit.EventId = eventInfo.Id;
await _registrationLimitRepository.InsertAsync(limit);
}
// 5. 保存费用配置 (Step 4)
if (input.FeeConfig != null)
{
var feeConfig = input.FeeConfig.Adapt<EventFeeConfig>();
feeConfig.Id = _idGenerator.NewLong();
feeConfig.EventId = eventInfo.Id;
await _feeConfigRepository.InsertAsync(feeConfig);
}
}
/// <summary>
/// 更新赛事
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[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<EventParticipant>();
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<EventProject>();
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<EventProjectGroup>();
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<EventRegistrationLimit>();
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<EventFeeConfig>();
feeConfig.Id = _idGenerator.NewLong();
feeConfig.EventId = eventInfo.Id;
await _feeConfigRepository.InsertAsync(feeConfig);
}
}
/// <summary>
/// 导出赛事
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("导出赛事")]
public async Task<byte[]> Export(EventExportInput input)
{
var query = _repository.AsQueryable()
.LeftJoin<EventParticipant>((e, p) => e.Id == p.EventId)
.LeftJoin<EventFeeConfig>((e, p, f) => e.Id == f.EventId)
.LeftJoin<YD_Event.Core.Entities.Merchant>((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<Dictionary<string, object>>();
foreach (var item in list)
{
var row = new Dictionary<string, object>();
var fields = input.ExportFields ?? new List<string>();
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();
}
/// <summary>
/// 删除赛事
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[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);
}
}