using YD_Event.Application.Event.Dtos;
using YD_Event.Application.Project.Dtos;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using Easy.Core;
using SqlSugar;
using Mapster;
namespace YD_Event.Application.Event;
///
/// 赛事管理服务
///
public class EventService : BaseService, ITransient
{
private readonly ISqlSugarRepository _repository;
private readonly ISqlSugarRepository _unitRepository;
private readonly ISqlSugarRepository _provinceRepository;
private readonly ISqlSugarRepository _cityRepository;
private readonly ISqlSugarRepository _districtRepository;
private readonly ISqlSugarRepository _projectRepository;
private readonly ISqlSugarRepository _eventProjectRepository;
private readonly ISqlSugarRepository _eventProjectGroupRepository;
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;
private readonly YD_Event.Application.Menu.SysMenuService _sysMenuService;
private readonly YD_Event.Application.Auth.AuthManager _authManager;
private readonly EventRuleSnapshotService _ruleSnapshotService;
public EventService(
ISqlSugarRepository repository,
ISqlSugarRepository unitRepository,
ISqlSugarRepository provinceRepository,
ISqlSugarRepository cityRepository,
ISqlSugarRepository districtRepository,
ISqlSugarRepository projectRepository,
ISqlSugarRepository eventProjectRepository,
ISqlSugarRepository eventProjectGroupRepository,
ISqlSugarRepository projectGroupRepository,
ISqlSugarRepository registrationLimitRepository,
ISqlSugarRepository projectLimitRepository,
ISqlSugarRepository feeConfigRepository,
ISqlSugarRepository registrationRepository,
ISqlSugarRepository merchantRepository,
IIdGenerator idGenerator,
YD_Event.Application.Menu.SysMenuService sysMenuService,
YD_Event.Application.Auth.AuthManager authManager,
EventRuleSnapshotService ruleSnapshotService) : base(repository)
{
_repository = repository;
_unitRepository = unitRepository;
_provinceRepository = provinceRepository;
_cityRepository = cityRepository;
_districtRepository = districtRepository;
_projectRepository = projectRepository;
_eventProjectRepository = eventProjectRepository;
_eventProjectGroupRepository = eventProjectGroupRepository;
_projectGroupRepository = projectGroupRepository;
_registrationLimitRepository = registrationLimitRepository;
_projectLimitRepository = projectLimitRepository;
_feeConfigRepository = feeConfigRepository;
_registrationRepository = registrationRepository;
_merchantRepository = merchantRepository;
_idGenerator = idGenerator;
_sysMenuService = sysMenuService;
_authManager = authManager;
_ruleSnapshotService = ruleSnapshotService;
}
///
/// 分页查询赛事
///
///
///
[DisplayName("分页查询赛事")]
public async Task> Page(EventQueryInput input)
{
var query = _repository.AsQueryable()
.LeftJoin((x, f) => x.Id == f.EventId)
.LeftJoin((x, f, m) => x.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();
}
}
}
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);
}
///
/// 获取赛事详情
///
///
///
[DisplayName("获取赛事详情")]
public async Task GetDetail(long id)
{
var eventInfo = await _repository.GetByIdAsync(id);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
var output = eventInfo.Adapt();
// 检查是否有报名记录,决定是否可以编辑项目(有报名记录时不能编辑项目和组别)
// 排除已取消(PaymentStatus = 4)和软删除的报名记录
var hasRegistration = await _registrationRepository.IsAnyAsync(r =>
r.EventId == id && !r.DeleteMark &&
((r.PaymentMethod == 2 && r.PaymentStatus == 1) || (r.PaymentMethod == 1 && (r.PaymentStatus == 0 || r.PaymentStatus == 1))));
output.CanEditProject = !hasRegistration;
// 1. 获取参与单位
var units = await _unitRepository.GetListAsync(x => x.EventId == id);
if (units.Any())
{
output.Units = units.OrderBy(x => x.Sort).Select(x => x.Adapt()).ToList();
}
// 3. 获取区域名称和Code
if (output.ProvinceId > 0)
{
var p = await _provinceRepository.GetFirstAsync(x => x.Id == output.ProvinceId);
output.ProvinceCode = p?.ProvinceCode;
output.ProvinceName = p?.ProvinceName;
}
if (output.CityId > 0)
{
var c = await _cityRepository.GetFirstAsync(x => x.Id == output.CityId);
output.CityCode = c?.CityCode;
output.CityName = c?.CityName;
}
if (output.DistrictId > 0)
{
var d = await _districtRepository.GetFirstAsync(x => x.Id == output.DistrictId);
output.DistrictCode = d?.DistrictCode;
output.DistrictName = d?.DistrictName;
}
// 4. 获取项目列表 (Step 3)
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == id);
if (eventProjects.Any())
{
var eventProjectIds = eventProjects.Select(x => x.Id).ToList();
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => eventProjectIds.Contains(x.EventProjectId));
var projectLimits = await _projectLimitRepository.GetListAsync(x => eventProjectIds.Contains(x.ProjectId));
// 获取关联的原始项目信息
var projectIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
var baseProjects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
var baseProjectGroups = await _projectGroupRepository.GetListAsync(x => projectIds.Contains(x.ProjectId));
output.Projects = eventProjects.Select(ep =>
{
var baseProj = baseProjects.FirstOrDefault(p => p.Id == ep.ProjectId);
var dto = new EventProjectInput
{
Id = ep.Id,
ProjectId = ep.ProjectId,
GenderType = ep.GenderType,
Fee = ep.Price,
Name = baseProj?.Name,
ProjectTypeId = (long?)baseProj?.ProjectType,
DurationMinutes = baseProj?.DurationMinutes,
DurationSeconds = baseProj?.DurationSeconds,
// 填充完成人数相关字段
CompletionCountType = baseProj?.CompletionCountType ?? 0,
CompletionCount = baseProj?.CompletionCount,
CompletionCountMin = baseProj?.CompletionCountMin,
CompletionCountMax = baseProj?.CompletionCountMax
};
if (baseProj != null)
{
dto.ProjectTypeName = baseProj.ProjectType.Description();
// 生成完成人数显示文本
if (baseProj.CompletionCountType == 0)
{
dto.CompletionCountDisplay = baseProj.CompletionCount > 0 ? $"{baseProj.CompletionCount}人" : "0人";
}
else
{
var min = baseProj.CompletionCountMin ?? 0;
var max = baseProj.CompletionCountMax ?? 0;
dto.CompletionCountDisplay = $"{min}-{max}人";
}
}
var epGroups = eventProjectGroups.Where(g => g.EventProjectId == ep.Id).ToList();
dto.Groups = epGroups.Select(eg =>
{
var baseGroup = baseProjectGroups.FirstOrDefault(bg => bg.Id == eg.ProjectGroupId);
return new EventProjectGroupInput
{
Id = eg.Id,
ProjectGroupId = eg.ProjectGroupId,
GroupName = eg.GroupName ?? baseGroup?.GroupName,
BirthDateStart = eg.BirthDateStart != DateTime.MinValue ? eg.BirthDateStart : baseGroup?.BirthDateStart,
BirthDateEnd = eg.BirthDateEnd != DateTime.MinValue ? eg.BirthDateEnd : baseGroup?.BirthDateEnd,
Gender = eg.Gender
};
}).ToList();
var limit = projectLimits.FirstOrDefault(l => l.ProjectId == ep.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 (eventInfo.MerchantId.HasValue && eventInfo.MerchantId.Value > 0)
{
var merchant = await _merchantRepository.GetByIdAsync(eventInfo.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;
output.FeeConfig.MerchantId = merchant.Id;
output.FeeConfig.BusinessLicenseUrl = merchant.BusinessLicenseUrl;
}
}
// 从 EventProject 表中读取项目价格并填充到 ProjectPrices
if (eventProjects.Any())
{
output.FeeConfig.ProjectPrices = new Dictionary();
foreach (var ep in eventProjects)
{
output.FeeConfig.ProjectPrices[ep.Id] = ep.Price;
}
}
}
// 7. 获取积分规则和退款规则快照,并赋值到原有的规则DTO中
var pointsRuleSnapshot = await _ruleSnapshotService.GetPointsRuleSnapshot(id);
var refundRuleSnapshots = await _ruleSnapshotService.GetRefundRuleSnapshot(id);
if (pointsRuleSnapshot != null)
{
output.PointsRuleDetail = new PointsRuleDto
{
RuleName = pointsRuleSnapshot.RuleName,
ParticipationPoints = pointsRuleSnapshot.ParticipationPoints,
Remark = pointsRuleSnapshot.Remark,
Details = pointsRuleSnapshot.Details.Select(d => new PointsRuleDetailDto
{
StartRank = d.StartRank,
EndRank = d.EndRank,
Points = d.Points
}).ToList()
};
}
if (refundRuleSnapshots != null && refundRuleSnapshots.Any())
{
output.RefundRules = refundRuleSnapshots.Select(r => new RefundRuleDto
{
DayStart = r.DayStart,
DayEnd = r.DayEnd,
DeductPercentage = r.DeductPercentage,
Remark = r.Remark
}).ToList();
}
return output;
}
///
/// 验证赛事时间逻辑
///
private void ValidateEventTime(DateTime registrationStartTime, DateTime registrationEndTime,
DateTime matchStartTime, DateTime matchEndTime)
{
if (registrationEndTime <= registrationStartTime)
{
throw Oops.Bah("报名结束时间必须晚于报名开始时间");
}
if (matchEndTime <= matchStartTime)
{
throw Oops.Bah("比赛结束时间必须晚于比赛开始时间");
}
if (matchStartTime <= registrationStartTime)
{
throw Oops.Bah("比赛开始时间必须晚于报名开始时间");
}
if (matchStartTime <= registrationEndTime)
{
throw Oops.Bah("比赛开始时间必须晚于报名结束时间");
}
if (matchEndTime <= registrationEndTime)
{
throw Oops.Bah("比赛结束时间必须晚于报名结束时间");
}
}
///
/// 新增赛事
///
///
///
[UnitOfWork]
[DisplayName("新增赛事")]
public async Task Add(AddEventInput input)
{
// 验证赛事时间逻辑
ValidateEventTime(
input.RegistrationStartTime,
input.RegistrationEndTime,
input.MatchStartTime,
input.MatchEndTime
);
// 从EventDetailOutput继承自AddEventInput,所以需要特殊处理一下
// 先获取EventInfo,但先复制除了Id字段
var provinceId = 0;
var cityId = 0;
var districtId = 0;
// 先把输入中的regionIds当作Code处理
if (input.ProvinceId > 0)
{
// 尝试通过Code找到对应的Id
var p = await _provinceRepository.GetFirstAsync(x => x.ProvinceCode == input.ProvinceId.ToString());
if (p != null) provinceId = (int)p.Id;
}
if (input.CityId > 0)
{
var c = await _cityRepository.GetFirstAsync(x => x.CityCode == input.CityId.ToString());
if (c != null) cityId = (int)c.Id;
}
if (input.DistrictId > 0)
{
var d = await _districtRepository.GetFirstAsync(x => x.DistrictCode == input.DistrictId.ToString());
if (d != null) districtId = (int)d.Id;
}
var eventInfo = input.Adapt();
eventInfo.ProvinceId = provinceId;
eventInfo.CityId = cityId;
eventInfo.DistrictId = districtId;
eventInfo.Id = _idGenerator.NewLong();
eventInfo.CreatedTime = DateTime.Now;
eventInfo.Status = AvailabilityStatus.Enable;
// 保存商户ID到EventInfo表
if (input.FeeConfig != null)
{
eventInfo.MerchantId = input.FeeConfig.MerchantId;
}
// 自动生成二维码URL (模拟)
eventInfo.QrCodeUrl = $"/pages/event/detail?id={eventInfo.Id}";
var now = DateTime.Now;
eventInfo.EventStatus = EventStatusUpdateService.CalculateEventStatus(eventInfo, now);
// 保存规则快照
await _ruleSnapshotService.SaveAllRuleSnapshots(eventInfo);
await _repository.InsertAsync(eventInfo);
// 1. 保存参与单位
if (input.Units != null && input.Units.Any())
{
var units = input.Units.Select(u => {
var entity = u.Adapt();
entity.Id = _idGenerator.NewLong();
entity.EventId = eventInfo.Id;
return entity;
}).ToList();
await _unitRepository.InsertRangeAsync(units);
}
// 2. 自定义字段 (JSON) - Already in eventInfo
var projectFees = new List();
// 3. 保存项目信息 (Step 3)
if (input.Projects != null && input.Projects.Any())
{
foreach (var projInput in input.Projects)
{
var eventProject = new EventProject
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
ProjectId = projInput.ProjectId,
GenderType = projInput.GenderType,
Price = projInput.Fee,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _eventProjectRepository.InsertAsync(eventProject);
projectFees.Add(new ProjectFeeInput { ProjectId = eventProject.Id, Fee = projInput.Fee });
// 保存项目分组
if (projInput.Groups != null && projInput.Groups.Any())
{
var groups = projInput.Groups.Select(g =>
{
var group = new EventProjectGroup
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
EventProjectId = eventProject.Id,
ProjectId = projInput.ProjectId,
ProjectGroupId = g.ProjectGroupId,
GroupName = g.GroupName ?? "",
BirthDateStart = g.BirthDateStart ?? DateTime.MinValue,
BirthDateEnd = g.BirthDateEnd ?? DateTime.MinValue,
Gender = g.Gender ?? Gender.All,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
return group;
}).ToList();
await _eventProjectGroupRepository.InsertRangeAsync(groups);
}
// 保存项目报名限制 (Step 3 - Project Limit)
if (projInput.LimitCount.HasValue)
{
var projLimit = new EventProjectRegistrationLimit
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
ProjectId = eventProject.Id,
LimitCount = projInput.LimitCount.Value,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
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)
{
// 满足 SaveFeeConfig 的前置状态要求
eventInfo.ProcessStatus = ProcessStatus.ProjectConfigCompleted;
await _repository.UpdateAsync(eventInfo);
var feeConfigInput = new SaveFeeConfigInput
{
EventId = eventInfo.Id,
FeeConfig = input.FeeConfig,
ProjectFees = projectFees
};
await SaveFeeConfig(feeConfigInput);
}
}
///
/// 更新赛事
///
///
///
[UnitOfWork]
[DisplayName("更新赛事")]
public async Task Update(UpdateEventInput input)
{
// 验证赛事时间逻辑
ValidateEventTime(
input.RegistrationStartTime,
input.RegistrationEndTime,
input.MatchStartTime,
input.MatchEndTime
);
var eventInfo = await _repository.GetByIdAsync(input.Id);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
// 检查是否有报名记录:如果有人报名,检查是否修改了项目和组别(软删除和已取消的不算)
var hasRegistration = await _registrationRepository.IsAnyAsync(x => x.EventId == input.Id && !x.DeleteMark && x.PaymentStatus != 4);
if (hasRegistration)
{
var hasProjectOrGroupChange = await CheckIfProjectsOrGroupsChanged(input, eventInfo.Id);
if (hasProjectOrGroupChange)
{
throw Oops.Bah("当前赛事已有人员报名,不允许修改项目信息");
}
}
// 将前端传过来的Code转换为对应的Id
var provinceId = eventInfo.ProvinceId;
var cityId = eventInfo.CityId;
var districtId = eventInfo.DistrictId;
if (input.ProvinceId > 0)
{
var p = await _provinceRepository.GetFirstAsync(x => x.ProvinceCode == input.ProvinceId.ToString());
if (p != null) provinceId = (int)p.Id;
}
if (input.CityId > 0)
{
var c = await _cityRepository.GetFirstAsync(x => x.CityCode == input.CityId.ToString());
if (c != null) cityId = (int)c.Id;
}
if (input.DistrictId > 0)
{
var d = await _districtRepository.GetFirstAsync(x => x.DistrictCode == input.DistrictId.ToString());
if (d != null) districtId = (int)d.Id;
}
input.Adapt(eventInfo);
eventInfo.ProvinceId = provinceId;
eventInfo.CityId = cityId;
eventInfo.DistrictId = districtId;
// 保存商户ID到EventInfo表
if (input.FeeConfig != null)
{
eventInfo.MerchantId = input.FeeConfig.MerchantId;
}
eventInfo.UpdatedTime = DateTime.Now;
// 更新状态逻辑
var now = DateTime.Now;
eventInfo.EventStatus = EventStatusUpdateService.CalculateEventStatus(eventInfo, now);
// 保存规则快照
await _ruleSnapshotService.SaveAllRuleSnapshots(eventInfo);
await _repository.UpdateAsync(eventInfo);
// 1. 更新参与单位 - 软删旧数据避免历史关联丢失
await _unitRepository.SoftDeleteAsync(x => x.EventId == input.Id);
if (input.Units != null && input.Units.Any())
{
var units = input.Units.Select(u => {
var entity = u.Adapt();
entity.Id = _idGenerator.NewLong();
entity.EventId = eventInfo.Id;
return entity;
}).ToList();
await _unitRepository.InsertRangeAsync(units);
}
// 2. 更新自定义字段 (JSON) - Updated in eventInfo
var projectFees = new List();
// 3. 更新项目信息
if (hasRegistration)
{
// 有报名记录的情况下,只允许修改项目价格和限报
var oldEventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == input.Id && !x.DeleteMark);
var oldProjectDict = oldEventProjects.ToDictionary(x => x.ProjectId);
if (input.Projects != null && input.Projects.Any())
{
foreach (var projInput in input.Projects)
{
if (oldProjectDict.ContainsKey(projInput.ProjectId))
{
var eventProject = oldProjectDict[projInput.ProjectId];
// 只更新项目价格
eventProject.Price = projInput.Fee;
eventProject.UpdatedTime = DateTime.Now;
await _eventProjectRepository.UpdateAsync(eventProject);
projectFees.Add(new ProjectFeeInput { ProjectId = eventProject.Id, Fee = projInput.Fee });
// 更新项目限报
var oldLimit = await _projectLimitRepository.GetFirstAsync(x => x.ProjectId == eventProject.Id && !x.DeleteMark);
if (projInput.LimitCount.HasValue)
{
if (oldLimit != null)
{
oldLimit.LimitCount = projInput.LimitCount.Value;
oldLimit.UpdatedTime = DateTime.Now;
await _projectLimitRepository.UpdateAsync(oldLimit);
}
else
{
var projLimit = new EventProjectRegistrationLimit
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
ProjectId = eventProject.Id,
LimitCount = projInput.LimitCount.Value,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _projectLimitRepository.InsertAsync(projLimit);
}
}
else if (oldLimit != null)
{
await _projectLimitRepository.SoftDeleteAsync(x => x.Id == oldLimit.Id);
}
}
}
}
}
else
{
// 没有报名记录的情况下,全量更新项目信息
var oldEventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == input.Id && !x.DeleteMark);
var oldProjectDict = oldEventProjects.ToDictionary(x => x.ProjectId);
var oldEventProjectIds = oldEventProjects.Select(x => x.Id).ToList();
if (input.Projects != null && input.Projects.Any())
{
var newProjectIds = new List();
foreach (var projInput in input.Projects)
{
EventProject eventProject;
if (oldProjectDict.ContainsKey(projInput.ProjectId))
{
// 更新现有项目
eventProject = oldProjectDict[projInput.ProjectId];
eventProject.GenderType = projInput.GenderType;
eventProject.Price = projInput.Fee;
eventProject.UpdatedTime = DateTime.Now;
await _eventProjectRepository.UpdateAsync(eventProject);
newProjectIds.Add(eventProject.Id);
}
else
{
// 新增项目
eventProject = new EventProject
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
ProjectId = projInput.ProjectId,
GenderType = projInput.GenderType,
Price = projInput.Fee,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _eventProjectRepository.InsertAsync(eventProject);
newProjectIds.Add(eventProject.Id);
}
projectFees.Add(new ProjectFeeInput { ProjectId = eventProject.Id, Fee = projInput.Fee });
// 处理项目分组
var oldGroups = await _eventProjectGroupRepository.GetListAsync(x => x.EventProjectId == eventProject.Id && !x.DeleteMark);
var oldGroupDict = BuildExistingGroupMap(oldGroups);
var newGroupIds = new List();
if (projInput.Groups != null && projInput.Groups.Any())
{
foreach (var groupInput in projInput.Groups)
{
EventProjectGroup group;
if (oldGroupDict.ContainsKey(groupInput.ProjectGroupId))
{
// 更新现有分组
group = oldGroupDict[groupInput.ProjectGroupId];
group.GroupName = groupInput.GroupName ?? "";
group.BirthDateStart = groupInput.BirthDateStart ?? DateTime.MinValue;
group.BirthDateEnd = groupInput.BirthDateEnd ?? DateTime.MinValue;
group.Gender = groupInput.Gender ?? Gender.All;
group.UpdatedTime = DateTime.Now;
await _eventProjectGroupRepository.UpdateAsync(group);
newGroupIds.Add(group.Id);
}
else
{
// 新增分组
group = new EventProjectGroup
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
EventProjectId = eventProject.Id,
ProjectId = projInput.ProjectId,
ProjectGroupId = groupInput.ProjectGroupId,
GroupName = groupInput.GroupName ?? "",
BirthDateStart = groupInput.BirthDateStart ?? DateTime.MinValue,
BirthDateEnd = groupInput.BirthDateEnd ?? DateTime.MinValue,
Gender = groupInput.Gender ?? Gender.All,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _eventProjectGroupRepository.InsertAsync(group);
newGroupIds.Add(group.Id);
}
}
}
// 软删除不再存在的分组
var groupsToDelete = oldGroups.Where(g => !newGroupIds.Contains(g.Id)).Select(g => g.Id).ToList();
if (groupsToDelete.Any())
{
await _eventProjectGroupRepository.SoftDeleteAsync(x => groupsToDelete.Contains(x.Id));
}
// 处理项目限制
var oldLimit = await _projectLimitRepository.GetFirstAsync(x => x.ProjectId == eventProject.Id && !x.DeleteMark);
if (projInput.LimitCount.HasValue)
{
if (oldLimit != null)
{
// 更新现有限制
oldLimit.LimitCount = projInput.LimitCount.Value;
oldLimit.UpdatedTime = DateTime.Now;
await _projectLimitRepository.UpdateAsync(oldLimit);
}
else
{
// 新增限制
var projLimit = new EventProjectRegistrationLimit
{
Id = _idGenerator.NewLong(),
EventId = eventInfo.Id,
ProjectId = eventProject.Id,
LimitCount = projInput.LimitCount.Value,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _projectLimitRepository.InsertAsync(projLimit);
}
}
else if (oldLimit != null)
{
// 删除限制
await _projectLimitRepository.SoftDeleteAsync(x => x.Id == oldLimit.Id);
}
}
// 软删除不再存在的项目
var projectsToDelete = oldEventProjects.Where(ep => !newProjectIds.Contains(ep.Id)).Select(ep => ep.Id).ToList();
if (projectsToDelete.Any())
{
await _eventProjectGroupRepository.SoftDeleteAsync(x => projectsToDelete.Contains(x.EventProjectId));
await _projectLimitRepository.SoftDeleteAsync(x => projectsToDelete.Contains(x.ProjectId));
await _eventProjectRepository.SoftDeleteAsync(x => projectsToDelete.Contains(x.Id));
}
}
else
{
// 删除所有项目
if (oldEventProjectIds.Any())
{
await _eventProjectGroupRepository.SoftDeleteAsync(x => oldEventProjectIds.Contains(x.EventProjectId));
await _projectLimitRepository.SoftDeleteAsync(x => oldEventProjectIds.Contains(x.ProjectId));
await _eventProjectRepository.SoftDeleteAsync(x => oldEventProjectIds.Contains(x.Id));
}
}
}
// 4. 更新报名限制 - 软删旧数据避免历史关联丢失
await _registrationLimitRepository.SoftDeleteAsync(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. 更新费用配置
if (input.FeeConfig != null)
{
// 满足 SaveFeeConfig 的前置状态要求
if (eventInfo.ProcessStatus < ProcessStatus.ProjectConfigCompleted)
{
eventInfo.ProcessStatus = ProcessStatus.ProjectConfigCompleted;
await _repository.UpdateAsync(eventInfo);
}
var feeConfigInput = new SaveFeeConfigInput
{
EventId = eventInfo.Id,
FeeConfig = input.FeeConfig,
ProjectFees = projectFees
};
await SaveFeeConfig(feeConfigInput);
}
}
///
/// 检查项目或组别是否有变化
///
private async Task CheckIfProjectsOrGroupsChanged(UpdateEventInput input, long eventId)
{
var oldEventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == eventId && !x.DeleteMark);
// 检查项目数量是否变化
if ((input.Projects == null || !input.Projects.Any()) && oldEventProjects.Any())
{
return true; // 删除了所有项目
}
if (input.Projects != null && input.Projects.Count() != oldEventProjects.Count)
{
return true; // 项目数量有变化
}
// 如果都有项目,继续比较每个项目
if (input.Projects != null && input.Projects.Any())
{
var oldProjectDict = oldEventProjects.ToDictionary(x => x.ProjectId);
foreach (var inputProj in input.Projects)
{
// 检查是否有新的项目
if (!oldProjectDict.ContainsKey(inputProj.ProjectId))
{
return true;
}
var oldProj = oldProjectDict[inputProj.ProjectId];
// 检查项目的基本信息是否变化
if (oldProj.GenderType != inputProj.GenderType)
{
return true;
}
// 检查组别是否变化
var oldGroups = await _eventProjectGroupRepository.GetListAsync(x => x.EventProjectId == oldProj.Id && !x.DeleteMark);
if ((inputProj.Groups == null || !inputProj.Groups.Any()) && oldGroups.Any())
{
return true; // 删除了所有组别
}
if (inputProj.Groups != null && inputProj.Groups.Count() != oldGroups.Count)
{
return true; // 组别数量有变化
}
if (inputProj.Groups != null && inputProj.Groups.Any())
{
var oldGroupDict = BuildExistingGroupMap(oldGroups);
foreach (var inputGroup in inputProj.Groups)
{
// 检查是否有新的组别
if (!oldGroupDict.ContainsKey(inputGroup.ProjectGroupId))
{
return true;
}
var oldGroup = oldGroupDict[inputGroup.ProjectGroupId];
// 检查组别的基本信息是否变化
if (oldGroup.GroupName != inputGroup.GroupName ||
oldGroup.BirthDateStart != (inputGroup.BirthDateStart ?? DateTime.MinValue) ||
oldGroup.BirthDateEnd != (inputGroup.BirthDateEnd ?? DateTime.MinValue) ||
oldGroup.Gender != (inputGroup.Gender ?? Gender.All))
{
return true;
}
}
}
}
}
return false;
}
///
/// 导出赛事
///
///
///
[DisplayName("导出赛事")]
public async Task Export(EventExportInput input)
{
var query = _repository.AsQueryable()
.LeftJoin((e, f) => e.Id == f.EventId)
.LeftJoin((e, f, m) => e.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 EventExportData
{
Id = e.Id,
EventName = e.EventName,
EventType = e.EventType,
ChargeMode = e.ChargeMode,
RegistrationType = e.RegistrationType,
EventStatus = e.EventStatus,
RegistrationStartTime = e.RegistrationStartTime,
RegistrationEndTime = e.RegistrationEndTime,
MaterialUploadEndTime = e.MaterialUploadEndTime,
MatchStartTime = e.MatchStartTime,
MatchEndTime = e.MatchEndTime,
DetailAddress = 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));
// 使用 EPPlus 进行导出
OfficeOpenXml.ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
using var package = new OfficeOpenXml.ExcelPackage();
var worksheet = package.Workbook.Worksheets.Add("赛事列表");
// 定义所有可用的列
var allColumns = new List<(string Header, Func GetValue)>
{
("赛事名称", item => item.EventName ?? ""),
("赛事类型", item => GetEnumDescription(item.EventType)),
("收费模式", item => GetEnumDescription(item.ChargeMode)),
("收款商户", item => item.MerchantName ?? ""),
("报名方式", item => GetEnumDescription(item.RegistrationType)),
("赛事状态", item => GetEnumDescription(item.EventStatus)),
("报名起止时间", item => $"{item.RegistrationStartTime:yyyy-MM-dd HH:mm} - {item.RegistrationEndTime:yyyy-MM-dd HH:mm}"),
("上传素材截至时间", item => item.MaterialUploadEndTime.HasValue ? item.MaterialUploadEndTime.Value.ToString("yyyy-MM-dd HH:mm") : ""),
("比赛起止时间", item => $"{item.MatchStartTime:yyyy-MM-dd HH:mm} - {item.MatchEndTime:yyyy-MM-dd HH:mm}"),
("赛事地址", item => item.DetailAddress ?? ""),
("主办单位", item => ""), // 后面填充
("承办单位", item => ""), // 后面填充
("协办单位", item => ""), // 后面填充
("赞助单位", item => "") // 后面填充
};
// 确定要导出的列
var selectedFields = input.ExportFields ?? new List();
bool exportAll = selectedFields.Count == 0;
var columnsToExport = allColumns
.Where(col => exportAll || selectedFields.Contains(col.Header))
.ToList();
if (columnsToExport.Count == 0)
{
columnsToExport = allColumns;
}
// 写入表头
for (int i = 0; i < columnsToExport.Count; i++)
{
worksheet.Cells[1, i + 1].Value = columnsToExport[i].Header;
}
// 设置表头样式
using (var headerRange = worksheet.Cells[1, 1, 1, columnsToExport.Count])
{
headerRange.Style.Font.Bold = true;
headerRange.Style.Font.Size = 11;
headerRange.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
headerRange.Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center;
headerRange.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
headerRange.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightBlue);
}
// 写入数据行
for (int rowIndex = 0; rowIndex < list.Count; rowIndex++)
{
var item = list[rowIndex];
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));
for (int colIndex = 0; colIndex < columnsToExport.Count; colIndex++)
{
var column = columnsToExport[colIndex];
string cellValue = column.GetValue(item);
// 补充参与单位数据
if (column.Header == "主办单位") cellValue = hostNames;
else if (column.Header == "承办单位") cellValue = organizerNames;
else if (column.Header == "协办单位") cellValue = coOrganizerNames;
else if (column.Header == "赞助单位") cellValue = sponsorNames;
worksheet.Cells[rowIndex + 2, colIndex + 1].Value = cellValue;
}
}
// 自动调整列宽
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
return await package.GetAsByteArrayAsync();
}
///
/// 获取枚举描述
///
private string GetEnumDescription(object enumValue)
{
if (enumValue == null) return "";
var type = enumValue.GetType();
if (!type.IsEnum) return enumValue.ToString();
var fieldInfo = type.GetField(enumValue.ToString());
if (fieldInfo == null) return enumValue.ToString();
var descriptionAttribute = (System.ComponentModel.DescriptionAttribute)
System.Attribute.GetCustomAttribute(fieldInfo, typeof(System.ComponentModel.DescriptionAttribute));
return descriptionAttribute != null ? descriptionAttribute.Description : enumValue.ToString();
}
///
/// 删除赛事
///
///
///
[DisplayName("删除赛事")]
public override async Task Delete(KeyDto dto)
{
// 检查是否有报名记录(排除已取消和软删除的)
if (await _registrationRepository.IsAnyAsync(x => x.EventId == dto.Id && !x.DeleteMark && x.PaymentStatus != 4))
{
throw new InvalidOperationException("已有人员报名此赛事,暂不支持删除");
}
// 软删除 EventInfo
await base.Delete(dto);
// 关联表物理删除或保留?通常保留。如果需要清理:
// await _unitRepository.DeleteAsync(x => x.EventId == dto.Id);
}
///
/// 阶段1:保存基本信息
///
///
/// EventId
[UnitOfWork]
[DisplayName("保存基本信息")]
public async Task SaveBasicInfo(SaveBasicInfoInput input)
{
// 验证赛事时间逻辑
ValidateEventTime(
input.RegistrationStartTime,
input.RegistrationEndTime,
input.MatchStartTime,
input.MatchEndTime
);
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;
// 保存规则快照
await _ruleSnapshotService.SaveAllRuleSnapshots(eventInfo);
}
else
{
eventInfo = input.Adapt();
eventInfo.Id = _idGenerator.NewLong();
eventInfo.CreatedTime = DateTime.Now;
eventInfo.Status = AvailabilityStatus.Enable; // 默认启用
eventInfo.ProcessStatus = ProcessStatus.BasicInfoCompleted;
// 初始状态计算
var now = DateTime.Now;
eventInfo.EventStatus = EventStatusUpdateService.CalculateEventStatus(eventInfo, now);
// 保存规则快照
await _ruleSnapshotService.SaveAllRuleSnapshots(eventInfo);
await _repository.InsertAsync(eventInfo);
}
// 更新状态
if (eventInfo.ProcessStatus == ProcessStatus.NotStarted)
{
eventInfo.ProcessStatus = ProcessStatus.BasicInfoCompleted;
}
await _repository.UpdateAsync(eventInfo);
return eventInfo.Id;
}
///
/// 阶段2:保存参与单位
///
///
[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.SoftDeleteAsync(x => x.EventId == input.EventId);
if (input.Units != null && input.Units.Any())
{
var units = input.Units.Select(u => {
var entity = u.Adapt();
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);
}
}
///
/// 阶段3:保存项目与限报配置
///
///
[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("请先完成参与单位配置");
}
// 检查是否有报名记录:如果有人报名,检查是否修改了项目和组别(软删除的不算)
var hasRegistration = await _registrationRepository.IsAnyAsync(x => x.EventId == input.EventId && !x.DeleteMark);
if (hasRegistration)
{
var oldEventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == input.EventId && !x.DeleteMark);
var oldProjectDict = oldEventProjects.ToDictionary(x => x.ProjectId);
var oldProjectIds = oldEventProjects.Select(x => x.ProjectId).ToList();
var newProjectIds = input.Projects?.Select(p => p.ProjectId).ToList() ?? new List();
// 检查是否新增或删除了项目
if (newProjectIds.Except(oldProjectIds).Any() || oldProjectIds.Except(newProjectIds).Any())
{
throw Oops.Bah("当前赛事已有人员报名,不允许修改项目信息");
}
// 检查每个项目的组别是否有变化
foreach (var projInput in input.Projects ?? new List())
{
if (oldProjectDict.ContainsKey(projInput.ProjectId))
{
var oldProj = oldProjectDict[projInput.ProjectId];
// 检查项目基本信息是否变化
if (oldProj.GenderType != projInput.GenderType)
{
throw Oops.Bah("当前赛事已有人员报名,不允许修改项目信息");
}
// 检查组别是否变化
var oldGroups = await _eventProjectGroupRepository.GetListAsync(x => x.EventProjectId == oldProj.Id && !x.DeleteMark);
var oldGroupIds = oldGroups.Select(g => g.ProjectGroupId).ToList();
var newGroupIds = projInput.Groups?.Select(g => g.ProjectGroupId).ToList() ?? new List();
if (newGroupIds.Except(oldGroupIds).Any() || oldGroupIds.Except(newGroupIds).Any())
{
throw Oops.Bah("当前赛事已有人员报名,不允许修改项目信息");
}
// 检查组别的基本信息是否变化
foreach (var groupInput in projInput.Groups ?? new List())
{
var oldGroup = oldGroups.FirstOrDefault(g => g.ProjectGroupId == groupInput.ProjectGroupId);
if (oldGroup != null)
{
if (oldGroup.GroupName != (groupInput.GroupName ?? "") ||
oldGroup.BirthDateStart != (groupInput.BirthDateStart ?? DateTime.MinValue) ||
oldGroup.BirthDateEnd != (groupInput.BirthDateEnd ?? DateTime.MinValue) ||
oldGroup.Gender != (groupInput.Gender ?? Gender.All))
{
throw Oops.Bah("当前赛事已有人员报名,不允许修改项目信息");
}
}
}
}
}
// 有报名记录的情况下,只允许修改项目价格和限报
foreach (var projInput in input.Projects ?? new List())
{
if (oldProjectDict.ContainsKey(projInput.ProjectId))
{
var eventProject = oldProjectDict[projInput.ProjectId];
// 只更新项目价格
eventProject.Price = projInput.Fee;
eventProject.UpdatedTime = DateTime.Now;
await _eventProjectRepository.UpdateAsync(eventProject);
// 更新项目限报
var oldLimit = await _projectLimitRepository.GetFirstAsync(x => x.ProjectId == eventProject.Id && !x.DeleteMark);
if (projInput.LimitCount.HasValue)
{
if (oldLimit != null)
{
oldLimit.LimitCount = projInput.LimitCount.Value;
oldLimit.UpdatedTime = DateTime.Now;
await _projectLimitRepository.UpdateAsync(oldLimit);
}
else
{
var projLimit = new EventProjectRegistrationLimit
{
Id = _idGenerator.NewLong(),
EventId = input.EventId,
ProjectId = eventProject.Id,
LimitCount = projInput.LimitCount.Value,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _projectLimitRepository.InsertAsync(projLimit);
}
}
else if (oldLimit != null)
{
await _projectLimitRepository.SoftDeleteAsync(x => x.Id == oldLimit.Id);
}
}
}
}
else
{
// 没有报名记录的情况下,全量更新项目信息
var oldEventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == input.EventId && !x.DeleteMark);
var oldProjectDict = oldEventProjects.ToDictionary(x => x.ProjectId);
var oldEventProjectIds = oldEventProjects.Select(x => x.Id).ToList();
// 处理输入的项目
if (input.Projects != null && input.Projects.Any())
{
var newProjectIds = new List();
foreach (var projInput in input.Projects)
{
EventProject eventProject;
if (oldProjectDict.ContainsKey(projInput.ProjectId))
{
// 更新现有项目
eventProject = oldProjectDict[projInput.ProjectId];
eventProject.GenderType = projInput.GenderType;
eventProject.Price = projInput.Fee;
eventProject.UpdatedTime = DateTime.Now;
await _eventProjectRepository.UpdateAsync(eventProject);
newProjectIds.Add(eventProject.Id);
}
else
{
// 新增项目
eventProject = new EventProject
{
Id = _idGenerator.NewLong(),
EventId = input.EventId,
ProjectId = projInput.ProjectId,
GenderType = projInput.GenderType,
Price = projInput.Fee,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _eventProjectRepository.InsertAsync(eventProject);
newProjectIds.Add(eventProject.Id);
}
// 处理项目分组
var oldGroups = await _eventProjectGroupRepository.GetListAsync(x => x.EventProjectId == eventProject.Id && !x.DeleteMark);
var oldGroupDict = BuildExistingGroupMap(oldGroups);
var newGroupIds = new List();
if (projInput.Groups != null && projInput.Groups.Any())
{
foreach (var groupInput in projInput.Groups)
{
EventProjectGroup group;
if (oldGroupDict.ContainsKey(groupInput.ProjectGroupId))
{
// 更新现有分组
group = oldGroupDict[groupInput.ProjectGroupId];
group.GroupName = groupInput.GroupName ?? "";
group.BirthDateStart = groupInput.BirthDateStart ?? DateTime.MinValue;
group.BirthDateEnd = groupInput.BirthDateEnd ?? DateTime.MinValue;
group.Gender = groupInput.Gender ?? Gender.All;
group.UpdatedTime = DateTime.Now;
await _eventProjectGroupRepository.UpdateAsync(group);
newGroupIds.Add(group.Id);
}
else
{
// 新增分组
group = new EventProjectGroup
{
Id = _idGenerator.NewLong(),
EventId = input.EventId,
EventProjectId = eventProject.Id,
ProjectId = projInput.ProjectId,
ProjectGroupId = groupInput.ProjectGroupId,
GroupName = groupInput.GroupName ?? "",
BirthDateStart = groupInput.BirthDateStart ?? DateTime.MinValue,
BirthDateEnd = groupInput.BirthDateEnd ?? DateTime.MinValue,
Gender = groupInput.Gender ?? Gender.All,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _eventProjectGroupRepository.InsertAsync(group);
newGroupIds.Add(group.Id);
}
}
}
// 软删除不再存在的分组
var groupsToDelete = oldGroups.Where(g => !newGroupIds.Contains(g.Id)).Select(g => g.Id).ToList();
if (groupsToDelete.Any())
{
await _eventProjectGroupRepository.SoftDeleteAsync(x => groupsToDelete.Contains(x.Id));
}
// 处理项目限制
var oldLimit = await _projectLimitRepository.GetFirstAsync(x => x.ProjectId == eventProject.Id && !x.DeleteMark);
if (projInput.LimitCount.HasValue)
{
if (oldLimit != null)
{
// 更新现有限制
oldLimit.LimitCount = projInput.LimitCount.Value;
oldLimit.UpdatedTime = DateTime.Now;
await _projectLimitRepository.UpdateAsync(oldLimit);
}
else
{
// 新增限制
var projLimit = new EventProjectRegistrationLimit
{
Id = _idGenerator.NewLong(),
EventId = input.EventId,
ProjectId = eventProject.Id,
LimitCount = projInput.LimitCount.Value,
CreatedUserId = _authManager.UserId,
CreatedTime = DateTime.Now
};
await _projectLimitRepository.InsertAsync(projLimit);
}
}
else if (oldLimit != null)
{
// 删除限制
await _projectLimitRepository.SoftDeleteAsync(x => x.Id == oldLimit.Id);
}
}
// 软删除不再存在的项目
var projectsToDelete = oldEventProjects.Where(ep => !newProjectIds.Contains(ep.Id)).Select(ep => ep.Id).ToList();
if (projectsToDelete.Any())
{
await _eventProjectGroupRepository.SoftDeleteAsync(x => projectsToDelete.Contains(x.EventProjectId));
await _projectLimitRepository.SoftDeleteAsync(x => projectsToDelete.Contains(x.ProjectId));
await _eventProjectRepository.SoftDeleteAsync(x => projectsToDelete.Contains(x.Id));
}
}
else
{
// 删除所有项目
if (oldEventProjectIds.Any())
{
await _eventProjectGroupRepository.SoftDeleteAsync(x => oldEventProjectIds.Contains(x.EventProjectId));
await _projectLimitRepository.SoftDeleteAsync(x => oldEventProjectIds.Contains(x.ProjectId));
await _eventProjectRepository.SoftDeleteAsync(x => oldEventProjectIds.Contains(x.Id));
}
}
}
// 2. 更新报名限制 - 软删旧数据避免历史关联丢失
await _registrationLimitRepository.SoftDeleteAsync(x => x.EventId == input.EventId);
if (input.RegistrationLimit != null)
{
var limit = input.RegistrationLimit.Adapt();
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);
}
}
///
/// 阶段4:保存费用与账户配置
///
///
/// 二维码URL
[UnitOfWork]
[DisplayName("保存费用与账户配置")]
public async Task SaveFeeConfig(SaveFeeConfigInput input)
{
var eventInfo = await _repository.GetByIdAsync(input.EventId);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
if (eventInfo.ProcessStatus < ProcessStatus.ProjectConfigCompleted)
{
throw new InvalidOperationException("请先完成项目配置");
}
// 验证非免费模式下的必填参数
if (input.FeeConfig != null && input.FeeConfig.ChargeMode != ChargeMode.Free)
{
// 验证收款商户
if (!input.FeeConfig.MerchantId.HasValue || input.FeeConfig.MerchantId.Value <= 0)
{
throw new InvalidOperationException("收费模式为非免费时,必须选择收款商户");
}
// 验证定价(赛事)和单价(赛事)模式下的价格
if (input.FeeConfig.ChargeMode == ChargeMode.FixedPrice || input.FeeConfig.ChargeMode == ChargeMode.UnitPriceEvent)
{
if (input.FeeConfig.Price <= 0)
{
throw new InvalidOperationException("收费模式为“定价(赛事)”或“单价(赛事)”时,价格必须大于0");
}
// 格式验证:最多保留两位小数
if (!AmountHelper.IsValidAmount(input.FeeConfig.Price))
{
throw new InvalidOperationException("价格格式不正确,最多保留两位小数");
}
}
}
// 验证单价(项目)模式下的项目单价
if (input.FeeConfig != null && input.FeeConfig.ChargeMode == ChargeMode.UnitPriceProject)
{
if (input.ProjectFees == null || !input.ProjectFees.Any())
{
throw new InvalidOperationException("收费模式为“单价(项目)”时,必须提供项目单价信息");
}
var projectIds = input.ProjectFees.Select(x => x.ProjectId).ToList();
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == input.EventId && projectIds.Contains(x.Id));
foreach (var feeInput in input.ProjectFees)
{
if (feeInput.Fee <= 0)
{
throw new InvalidOperationException("项目单价必须大于0");
}
// 格式验证:最多保留两位小数
if (!AmountHelper.IsValidAmount(feeInput.Fee))
{
throw new InvalidOperationException("项目单价格式不正确,最多保留两位小数");
}
var proj = eventProjects.FirstOrDefault(p => p.Id == feeInput.ProjectId);
if (proj != null)
{
proj.Price = feeInput.Fee;
proj.UpdatedTime = DateTime.Now;
await _eventProjectRepository.UpdateAsync(proj);
}
}
}
// 更新费用配置 - 软删旧数据避免历史关联丢失
await _feeConfigRepository.SoftDeleteAsync(x => x.EventId == input.EventId);
if (input.FeeConfig != null)
{
var feeConfig = input.FeeConfig.Adapt();
feeConfig.Id = _idGenerator.NewLong();
feeConfig.EventId = input.EventId;
await _feeConfigRepository.InsertAsync(feeConfig);
// 更新赛事的 ChargeMode
eventInfo.ChargeMode = feeConfig.ChargeMode;
}
// 生成二维码URL
var qrCodeUrl = $"/pages/event/detail?id={eventInfo.Id}";
eventInfo.QrCodeUrl = qrCodeUrl;
// 更新流程状态和赛事状态
eventInfo.ProcessStatus = ProcessStatus.FeeConfigCompleted;
// 重新计算并确认赛事状态
var now = DateTime.Now;
eventInfo.EventStatus = EventStatusUpdateService.CalculateEventStatus(eventInfo, now);
await _repository.UpdateAsync(eventInfo);
return qrCodeUrl;
}
///
/// 获取赛事填报进度
///
///
///
[DisplayName("获取赛事填报进度")]
public async Task GetProgress(long eventId)
{
var eventInfo = await _repository.GetByIdAsync(eventId);
if (eventInfo == null) throw new InvalidOperationException("赛事不存在");
var completedStages = new List();
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
};
}
private static Dictionary BuildExistingGroupMap(List groups)
{
if (groups.Any(x => x.ProjectGroupId <= 0))
throw Oops.Bah("赛事存在未关联平台组别的历史数据,请先核查修复项目组别配置");
if (groups.GroupBy(x => x.ProjectGroupId).Any(x => x.Count() > 1))
throw Oops.Bah("赛事存在重复的平台组别关联,请先核查修复项目组别配置");
return groups.ToDictionary(x => x.ProjectGroupId);
}
}