2026-03-14 09:30:01 +08:00

767 lines
32 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.Application.Project.Dtos;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using MiniExcelLibs;
using SqlSugar;
using Mapster;
namespace YD_Event.Application.Event;
/// <summary>
/// 赛事管理服务
/// </summary>
public class EventService : BaseService<EventInfo>, ITransient
{
private readonly ISqlSugarRepository<EventInfo> _repository;
private readonly ISqlSugarRepository<EventUnit> _unitRepository;
private readonly ISqlSugarRepository<SysProvince> _provinceRepository;
private readonly ISqlSugarRepository<SysCity> _cityRepository;
private readonly ISqlSugarRepository<SysDistrict> _districtRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
private readonly ISqlSugarRepository<ProjectGroup> _projectGroupRepository;
private readonly ISqlSugarRepository<ProjectType> _projectTypeRepository;
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<EventUnit> unitRepository,
ISqlSugarRepository<SysProvince> provinceRepository,
ISqlSugarRepository<SysCity> cityRepository,
ISqlSugarRepository<SysDistrict> districtRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
ISqlSugarRepository<ProjectGroup> projectGroupRepository,
ISqlSugarRepository<ProjectType> projectTypeRepository,
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;
_unitRepository = unitRepository;
_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;
}
/// <summary>
/// 分页查询赛事
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询赛事")]
public async Task<PageResult<EventListOutput>> Page(EventQueryInput input)
{
var query = _repository.AsQueryable()
.LeftJoin<EventFeeConfig>((x, f) => x.Id == f.EventId)
.LeftJoin<YD_Event.Core.Entities.Merchant>((x, f, m) => f.MerchantId == m.Id)
.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, f, m) => new EventListOutput
{
Id = x.Id,
EventName = x.EventName,
CoverImage = x.CoverImage,
EventType = x.EventType,
RegistrationType = x.RegistrationType,
ChargeMode = x.ChargeMode,
MerchantName = m.MerchantName,
EventStatus = x.EventStatus,
RegistrationStartTime = x.RegistrationStartTime,
RegistrationEndTime = x.RegistrationEndTime,
MatchStartTime = x.MatchStartTime,
MatchEndTime = x.MatchEndTime,
QrCodeUrl = x.QrCodeUrl
});
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 units = await _unitRepository.GetListAsync(x => x.EventId == id);
if (units.Any())
{
output.Units = units.OrderBy(x => x.Sort).Select(x => x.Adapt<EventUnitDto>()).ToList();
}
// 2. 自定义字段配置 (JSON)
// output.CustomConfigJson 已自动映射
// 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<EventProjectInput>();
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<ProjectGroupInput>()).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.HasValue && feeConfig.MerchantId.Value > 0)
{
var merchant = await _merchantRepository.GetByIdAsync(feeConfig.MerchantId.Value);
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;
// 自动生成二维码URL (模拟)
eventInfo.QrCodeUrl = $"/pages/event/detail?id={eventInfo.Id}";
var now = DateTime.Now;
if (now < eventInfo.RegistrationStartTime) eventInfo.EventStatus = EventStatus.Enrolling; // Logic might need adjustment
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.Units != null && input.Units.Any())
{
var units = input.Units.Select(u => {
var entity = u.Adapt<EventUnit>();
entity.Id = _idGenerator.NewLong();
entity.EventId = eventInfo.Id;
return entity;
}).ToList();
await _unitRepository.InsertRangeAsync(units);
}
// 2. 自定义字段 (JSON) - Already in eventInfo
// 3. 保存项目信息 (Step 3)
if (input.Projects != null && input.Projects.Any())
{
foreach (var projInput in input.Projects)
{
var project = projInput.Adapt<YD_Event.Core.Entities.Project>();
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<ProjectGroup>();
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 _unitRepository.DeleteAsync(x => x.EventId == input.Id);
if (input.Units != null && input.Units.Any())
{
var units = input.Units.Select(u => {
var entity = u.Adapt<EventUnit>();
entity.Id = _idGenerator.NewLong();
entity.EventId = eventInfo.Id;
return entity;
}).ToList();
await _unitRepository.InsertRangeAsync(units);
}
// 2. 更新自定义字段 (JSON) - Updated in eventInfo
// 3. 更新项目信息
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<YD_Event.Core.Entities.Project>();
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<ProjectGroup>();
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<EventFeeConfig>((e, f) => e.Id == f.EventId)
.LeftJoin<YD_Event.Core.Entities.Merchant>((e, 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, f, m) => new
{
e.Id,
e.EventName,
e.EventType,
e.ChargeMode,
e.RegistrationType,
e.EventStatus,
e.RegistrationStartTime,
e.RegistrationEndTime,
e.MaterialUploadEndTime,
e.MatchStartTime,
e.MatchEndTime,
e.DetailAddress,
MerchantName = m.MerchantName
});
var list = await query.ToListAsync();
var eventIds = list.Select(x => x.Id).ToList();
var units = await _unitRepository.GetListAsync(x => eventIds.Contains(x.EventId));
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;
var eventUnits = units.Where(u => u.EventId == item.Id).ToList();
var hostNames = string.Join(",", eventUnits.Where(u => u.UnitType == UnitType.Host).Select(u => u.UnitName));
var organizerNames = string.Join(",", eventUnits.Where(u => u.UnitType == UnitType.Organizer).Select(u => u.UnitName));
var coOrganizerNames = string.Join(",", eventUnits.Where(u => u.UnitType == UnitType.CoOrganizer).Select(u => u.UnitName));
var sponsorNames = string.Join(",", eventUnits.Where(u => u.UnitType == UnitType.Sponsor).Select(u => u.UnitName));
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["主办单位"] = hostNames;
if (all || fields.Contains("承办单位")) row["承办单位"] = organizerNames;
if (all || fields.Contains("协办单位")) row["协办单位"] = coOrganizerNames;
if (all || fields.Contains("赞助单位")) row["赞助单位"] = sponsorNames;
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 _unitRepository.DeleteAsync(x => x.EventId == dto.Id);
}
/// <summary>
/// 阶段1:保存基本信息
/// </summary>
/// <param name="input"></param>
/// <returns>EventId</returns>
[UnitOfWork]
[DisplayName("保存基本信息")]
public async Task<long> SaveBasicInfo(SaveBasicInfoInput input)
{
EventInfo eventInfo;
if (input.EventId.HasValue && input.EventId.Value > 0)
{
eventInfo = await _repository.GetByIdAsync(input.EventId.Value);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
input.Adapt(eventInfo);
eventInfo.UpdatedTime = DateTime.Now;
}
else
{
eventInfo = input.Adapt<EventInfo>();
eventInfo.Id = _idGenerator.NewLong();
eventInfo.CreatedTime = DateTime.Now;
eventInfo.Status = AvailabilityStatus.Enable; // 默认启用
eventInfo.ProcessStatus = ProcessStatus.BasicInfoCompleted;
// 初始状态计算
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.InsertAsync(eventInfo);
}
// 更新状态
if (eventInfo.ProcessStatus == ProcessStatus.NotStarted)
{
eventInfo.ProcessStatus = ProcessStatus.BasicInfoCompleted;
}
await _repository.UpdateAsync(eventInfo);
return eventInfo.Id;
}
/// <summary>
/// 阶段2:保存参与单位
/// </summary>
/// <param name="input"></param>
[UnitOfWork]
[DisplayName("保存参与单位")]
public async Task SaveUnits(SaveUnitsInput input)
{
var eventInfo = await _repository.GetByIdAsync(input.EventId);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
if (eventInfo.ProcessStatus < ProcessStatus.BasicInfoCompleted)
{
throw new InvalidOperationException("请先完成基本信息配置");
}
// 全量更新单位
await _unitRepository.DeleteAsync(x => x.EventId == input.EventId);
if (input.Units != null && input.Units.Any())
{
var units = input.Units.Select(u => {
var entity = u.Adapt<EventUnit>();
entity.Id = _idGenerator.NewLong();
entity.EventId = input.EventId;
return entity;
}).ToList();
await _unitRepository.InsertRangeAsync(units);
}
// 更新流程状态
if (eventInfo.ProcessStatus < ProcessStatus.ParticipantsCompleted)
{
eventInfo.ProcessStatus = ProcessStatus.ParticipantsCompleted;
await _repository.UpdateAsync(eventInfo);
}
}
/// <summary>
/// 阶段3:保存项目与限报配置
/// </summary>
/// <param name="input"></param>
[UnitOfWork]
[DisplayName("保存项目与限报配置")]
public async Task SaveProjects(SaveProjectsInput input)
{
var eventInfo = await _repository.GetByIdAsync(input.EventId);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
if (eventInfo.ProcessStatus < ProcessStatus.ParticipantsCompleted)
{
throw new InvalidOperationException("请先完成参与单位配置");
}
// 1. 更新项目信息 (全量替换)
var oldProjects = await _projectRepository.GetListAsync(x => x.EventId == input.EventId);
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.EventId);
}
if (input.Projects != null && input.Projects.Any())
{
foreach (var projInput in input.Projects)
{
var project = projInput.Adapt<YD_Event.Core.Entities.Project>();
project.Id = _idGenerator.NewLong();
project.EventId = input.EventId;
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<ProjectGroup>();
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 = input.EventId,
ProjectId = project.Id,
LimitCount = projInput.LimitCount.Value
};
await _projectLimitRepository.InsertAsync(projLimit);
}
}
}
// 2. 更新报名限制
await _registrationLimitRepository.DeleteAsync(x => x.EventId == input.EventId);
if (input.RegistrationLimit != null)
{
var limit = input.RegistrationLimit.Adapt<EventRegistrationLimit>();
limit.Id = _idGenerator.NewLong();
limit.EventId = input.EventId;
await _registrationLimitRepository.InsertAsync(limit);
}
// 更新流程状态
if (eventInfo.ProcessStatus < ProcessStatus.ProjectConfigCompleted)
{
eventInfo.ProcessStatus = ProcessStatus.ProjectConfigCompleted;
await _repository.UpdateAsync(eventInfo);
}
}
/// <summary>
/// 阶段4:保存费用与账户配置
/// </summary>
/// <param name="input"></param>
/// <returns>二维码URL</returns>
[UnitOfWork]
[DisplayName("保存费用与账户配置")]
public async Task<string> SaveFeeConfig(SaveFeeConfigInput input)
{
var eventInfo = await _repository.GetByIdAsync(input.EventId);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
if (eventInfo.ProcessStatus < ProcessStatus.ProjectConfigCompleted)
{
throw new InvalidOperationException("请先完成项目配置");
}
// 更新费用配置
await _feeConfigRepository.DeleteAsync(x => x.EventId == input.EventId);
if (input.FeeConfig != null)
{
var feeConfig = input.FeeConfig.Adapt<EventFeeConfig>();
feeConfig.Id = _idGenerator.NewLong();
feeConfig.EventId = input.EventId;
await _feeConfigRepository.InsertAsync(feeConfig);
}
// 生成二维码URL
var qrCodeUrl = $"/pages/event/detail?id={eventInfo.Id}";
eventInfo.QrCodeUrl = qrCodeUrl;
// 更新流程状态和赛事状态
eventInfo.ProcessStatus = ProcessStatus.FeeConfigCompleted;
// 重新计算并确认赛事状态
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);
return qrCodeUrl;
}
/// <summary>
/// 获取赛事填报进度
/// </summary>
/// <param name="eventId"></param>
/// <returns></returns>
[DisplayName("获取赛事填报进度")]
public async Task<EventProgressOutput> GetProgress(long eventId)
{
var eventInfo = await _repository.GetByIdAsync(eventId);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
var completedStages = new List<string>();
if (eventInfo.ProcessStatus >= ProcessStatus.BasicInfoCompleted) completedStages.Add("basic");
if (eventInfo.ProcessStatus >= ProcessStatus.ParticipantsCompleted) completedStages.Add("units");
if (eventInfo.ProcessStatus >= ProcessStatus.ProjectConfigCompleted) completedStages.Add("projects");
if (eventInfo.ProcessStatus >= ProcessStatus.FeeConfigCompleted) completedStages.Add("fee");
return new EventProgressOutput
{
EventId = eventInfo.Id,
ProcessStatus = eventInfo.ProcessStatus,
CompletedStages = completedStages
};
}
}