优化报名查询逻辑

This commit is contained in:
马梓彭 2026-07-13 19:34:42 +08:00
parent 86d2d349a9
commit 8876a7e086
2 changed files with 297 additions and 189 deletions

View File

@ -12,6 +12,7 @@ using YD_Event.Core.Extensions;
using YD_Event.Core.Infrastructure.WeChat;
using YD_Event.Core.Infrastructure.WeChat.Dto;
using YD_Event.Core.Options;
using EasyCaching.Core;
namespace YD_Event.Application.Event;
@ -51,6 +52,15 @@ public class UnifiedRegistrationService : ITransient
private readonly RefundConfigService _refundConfigService;
private readonly ISqlSugarRepository<RefundRecord> _refundRecordRepository;
private readonly EventRuleSnapshotService _ruleSnapshotService;
private readonly IEasyCachingProvider _easyCachingProvider;
// 缓存键前缀
private const string CacheKey_EventConfig = "EventConfig:";
private const string CacheKey_EventLimit = "EventLimit:";
private const string CacheKey_ProjectLimit = "EventProjectLimit:";
private const string CacheKey_EventProjects = "EventProjects:";
private const string CacheKey_ProjectGroups = "EventProjectGroups:";
private const string CacheKey_BaseProjects = "BaseProjects:";
public UnifiedRegistrationService(
ISqlSugarRepository<EventInfo> eventRepository,
@ -83,7 +93,8 @@ public class UnifiedRegistrationService : ITransient
OrderNoGeneratorService orderNoGeneratorService,
RefundConfigService refundConfigService,
ISqlSugarRepository<RefundRecord> refundRecordRepository,
EventRuleSnapshotService ruleSnapshotService)
EventRuleSnapshotService ruleSnapshotService,
IEasyCachingProvider easyCachingProvider)
{
_eventRepository = eventRepository;
_unitRepository = unitRepository;
@ -116,6 +127,7 @@ public class UnifiedRegistrationService : ITransient
_refundConfigService = refundConfigService;
_refundRecordRepository = refundRecordRepository;
_ruleSnapshotService = ruleSnapshotService;
_easyCachingProvider = easyCachingProvider;
}
/// <summary>
@ -1387,10 +1399,84 @@ public class UnifiedRegistrationService : ITransient
_logger.LogInformation("已删除预报名记录,报名ID:{RegistrationId}", registration.Id);
}
/// <summary>
/// 获取赛事报名限制配置(带缓存,3分钟过期)
/// </summary>
private async Task<EventRegistrationLimit?> GetEventLimitCacheAsync(long eventId)
{
var cacheKey = $"{CacheKey_EventLimit}{eventId}";
var cacheValue = await _easyCachingProvider.GetAsync<EventRegistrationLimit?>(cacheKey);
if (cacheValue.HasValue) return cacheValue.Value;
var limit = await _limitRepository.GetFirstAsync(x => x.EventId == eventId && !x.DeleteMark);
await _easyCachingProvider.SetAsync(cacheKey, limit, TimeSpan.FromMinutes(3));
return limit;
}
/// <summary>
/// 获取赛事项目限制配置列表(带缓存,3分钟过期)
/// </summary>
private async Task<List<EventProjectRegistrationLimit>> GetProjectLimitsCacheAsync(long eventId)
{
var cacheKey = $"{CacheKey_ProjectLimit}{eventId}";
var cacheValue = await _easyCachingProvider.GetAsync<List<EventProjectRegistrationLimit>>(cacheKey);
if (cacheValue.HasValue) return cacheValue.Value;
var projectLimits = await _projectLimitRepository.GetListAsync(x => x.EventId == eventId && !x.DeleteMark);
await _easyCachingProvider.SetAsync(cacheKey, projectLimits, TimeSpan.FromMinutes(3));
return projectLimits;
}
/// <summary>
/// 获取赛事项目列表(带缓存,3分钟过期)
/// </summary>
private async Task<List<EventProject>> GetEventProjectsCacheAsync(long eventId)
{
var cacheKey = $"{CacheKey_EventProjects}{eventId}";
var cacheValue = await _easyCachingProvider.GetAsync<List<EventProject>>(cacheKey);
if (cacheValue.HasValue) return cacheValue.Value;
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == eventId);
await _easyCachingProvider.SetAsync(cacheKey, eventProjects, TimeSpan.FromMinutes(3));
return eventProjects;
}
/// <summary>
/// 获取赛事项目组别列表(带缓存,3分钟过期)
/// </summary>
private async Task<List<EventProjectGroup>> GetEventProjectGroupsCacheAsync(long eventId)
{
var cacheKey = $"{CacheKey_ProjectGroups}{eventId}";
var cacheValue = await _easyCachingProvider.GetAsync<List<EventProjectGroup>>(cacheKey);
if (cacheValue.HasValue) return cacheValue.Value;
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => x.EventId == eventId && !x.DeleteMark);
await _easyCachingProvider.SetAsync(cacheKey, eventProjectGroups, TimeSpan.FromMinutes(3));
return eventProjectGroups;
}
/// <summary>
/// 获取基础项目列表(带缓存,3分钟过期)
/// </summary>
private async Task<List<YD_Event.Core.Entities.Project>> GetBaseProjectsCacheAsync(List<long> projectIds, long eventId)
{
var cacheKey = $"{CacheKey_BaseProjects}{eventId}";
var cacheValue = await _easyCachingProvider.GetAsync<List<YD_Event.Core.Entities.Project>>(cacheKey);
if (cacheValue.HasValue) return cacheValue.Value;
var projects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id) && !x.DeleteMark);
await _easyCachingProvider.SetAsync(cacheKey, projects, TimeSpan.FromMinutes(3));
return projects;
}
/// <summary>
/// 检查报名限制
/// </summary>
public async Task<RegistrationLimitCheckOutput> CheckRegistrationLimitAsync(UnifiedRegistrationInput input, long? excludeRegistrationId = null)
public async Task<RegistrationLimitCheckOutput> CheckRegistrationLimitAsync(
UnifiedRegistrationInput input,
long? excludeRegistrationId = null,
List<EventProject>? eventProjects = null,
List<EventProjectGroup>? eventProjectGroups = null)
{
var result = new RegistrationLimitCheckOutput
{
@ -1400,50 +1486,14 @@ public class UnifiedRegistrationService : ITransient
_logger.LogInformation("开始检查报名限制,赛事ID: {EventId}", input.EventId);
var limit = await _limitRepository.GetFirstAsync(x => x.EventId == input.EventId && !x.DeleteMark);
var projectLimits = await _projectLimitRepository.GetListAsync(x => x.EventId == input.EventId && !x.DeleteMark);
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == input.EventId);
var projects = await _projectRepository.GetListAsync(x => eventProjects.Select(ep => ep.ProjectId).Contains(x.Id) && !x.DeleteMark);
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => x.EventId == input.EventId && !x.DeleteMark);
// 先获取所有报名信息,供后续检查使用
// 线上支付类型的报名记录,仅统计PaymentStatus == 1(已支付)的记录
// 线下支付类型的报名记录:
// - 银行转账方式:仅统计PaymentStatus == 1(已支付)和PaymentStatus == 0(待审核)的记录,
// 不统计PaymentStatus == 4(待确认转账)和PaymentStatus == 5(待支付确认)的记录
// - 其他线下方式:统计PaymentStatus == 1(已支付)和PaymentStatus == 0(待审核)的记录
var eventRegistrationsQuery = _registrationRepository.AsQueryable().Where(x =>
x.EventId == input.EventId && !x.DeleteMark &&
(
// 线上支付:仅已支付
(x.PaymentMethod == 2 && x.PaymentStatus == 1) ||
// 线下支付:
(x.PaymentMethod == 1 && (
// 已支付的都统计
x.PaymentStatus == 1 ||
// 待审核的都统计
x.PaymentStatus == 0
))
) &&
// 排除待支付确认的记录(状态5)、待确认转账的记录(状态4)、支付成功但名额已满的记录(状态6)
x.PaymentStatus != 4 && x.PaymentStatus != 5 && x.PaymentStatus != 6
);
// 如果指定了要排除的报名ID,则排除该记录
if (excludeRegistrationId.HasValue)
{
eventRegistrationsQuery = eventRegistrationsQuery.Where(x => x.Id != excludeRegistrationId.Value);
}
var eventRegistrations = await eventRegistrationsQuery.ToListAsync();
var eventRegistrationIds = eventRegistrations.Select(r => r.Id).ToList();
var registrationPersons = await _registrationPersonRepository.GetListAsync(x =>
eventRegistrationIds.Contains(x.RegistrationId) && !x.DeleteMark);
var allRegistrationItems = await _registrationItemRepository.GetListAsync(x =>
eventRegistrationIds.Contains(x.RegistrationId) && !x.DeleteMark);
var limit = await GetEventLimitCacheAsync(input.EventId);
var projectLimits = await GetProjectLimitsCacheAsync(input.EventId);
eventProjects ??= await GetEventProjectsCacheAsync(input.EventId);
var projects = await GetBaseProjectsCacheAsync(eventProjects.Select(ep => ep.ProjectId).Distinct().ToList(), input.EventId);
eventProjectGroups ??= await GetEventProjectGroupsCacheAsync(input.EventId);
_logger.LogInformation("获取到赛事报名记录: {RegistrationCount},报名人员: {PersonCount},报名项目: {ItemCount}",
eventRegistrations.Count, registrationPersons.Count, allRegistrationItems.Count);
_logger.LogInformation("获取赛事配置完成,项目数: {ProjectCount}, 组別数: {GroupCount}",
eventProjects.Count, eventProjectGroups.Count);
// ==================== 报名限制检查流程 ====================
// 检查顺序说明:
@ -1455,27 +1505,26 @@ public class UnifiedRegistrationService : ITransient
// 6. 最后检查项目完成人数 - 确保人数与项目的完成人数完全一致
// 检查1:团队组别容量限制
// 验证是否有组别超过了限报的团队数量(例如:某组别限报2队,但本次报了3队)
CheckTeamGroupCapacityLimitAsync(input, result, projectLimits, eventProjects, projects, eventProjectGroups);
if (result.HasLimit) return result;
// 检查2:成员项目限报
// 验证队员是否超过了个人项目或团队项目的限报数量(例如:某队员已报2个个人项目,限报2个,又报第3个)
await CheckMemberProjectLimitAsync(input, result, limit, eventProjects, projects, registrationPersons, allRegistrationItems);
// 检查2:成员项目限报(优化:使用数据库聚合查询,避免全量加载)
await CheckMemberProjectLimitAsync(input, result, limit, eventProjects, projects, excludeRegistrationId);
if (result.HasLimit) return result;
// 检查3:人员项目组别重复
// 验证同一人员是否在本次报名中重复选择了同一项目的多个组别(例如:同一队员在同一项目的男子组和女子组都报了名)
// 检查3:人员项目组别重复(纯内存操作,输入数据验证)
CheckPersonProjectDuplicateAsync(input, result, eventProjects, projects, eventProjectGroups);
if (result.HasLimit) return result;
// 检查4:人员是否已在其他报名中报名
// 验证人员是否已在其他已支付的报名记录中报过同一项目组别(避免重复占用名额)
CheckPersonAlreadyRegisteredAsync(input, result, eventProjects, projects, eventProjectGroups, registrationPersons, allRegistrationItems, eventRegistrationIds);
// 检查4:人员是否已在其他报名中报名(优化:使用数据库查询,避免全量加载)
await CheckPersonAlreadyRegisteredAsync(input, result, eventProjects, projects, eventProjectGroups, excludeRegistrationId);
if (result.HasLimit) return result;
// 检查5:团队成员冲突检测
// 验证同一人员是否在同一项目的多个团队中(例如:同一队员既在1队又在2队)
// 检查5:团队成员冲突检测(纯内存操作,输入数据验证)
CheckTeamMemberConflictAsync(input, result, eventProjects, projects, eventProjectGroups);
if (result.HasLimit) return result;
// 检查6:项目完成人数验证
// 验证用户选择的参与人数必须与项目的"完成人数"完全一致(例如:项目要求完成3人,但只选了2人或选了4人)
// 检查6:项目完成人数验证(纯内存操作,输入数据验证)
CheckProjectCompletionAsync(input, result, eventProjects, projects, eventProjectGroups);
_logger.LogInformation("报名限制检查完成,是否存在限制: {HasLimit}, 限制详情数: {LimitCount}",
@ -1590,7 +1639,7 @@ public class UnifiedRegistrationService : ITransient
}
/// <summary>
/// 检查成员项目限报
/// 检查成员项目限报(优化版:使用数据库查询,仅查询涉及的成员数据,避免全量加载)
/// </summary>
private async Task CheckMemberProjectLimitAsync(
UnifiedRegistrationInput input,
@ -1598,8 +1647,7 @@ public class UnifiedRegistrationService : ITransient
EventRegistrationLimit? limit,
List<EventProject> eventProjects,
List<YD_Event.Core.Entities.Project> projects,
List<EventRegistrationPerson> registrationPersons,
List<EventRegistrationItem> allRegistrationItems)
long? excludeRegistrationId = null)
{
var memberIdsList = new List<long>();
foreach (var item in input.Items)
@ -1617,101 +1665,103 @@ public class UnifiedRegistrationService : ITransient
var members = await _userRepository.GetListAsync(x => memberIds.Contains(x.Id) && !x.DeleteMark);
_logger.LogInformation("检查报名限制的成员数量: {MemberCount}", members.Count);
// 构建有效报名记录的子查询(与原逻辑一致的过滤条件)
var validRegistrationQuery = _registrationRepository.AsQueryable()
.Where(x => x.EventId == input.EventId && !x.DeleteMark &&
(
(x.PaymentMethod == 2 && x.PaymentStatus == 1) ||
(x.PaymentMethod == 1 && (x.PaymentStatus == 1 || x.PaymentStatus == 0))
) &&
x.PaymentStatus != 4 && x.PaymentStatus != 5 && x.PaymentStatus != 6);
if (excludeRegistrationId.HasValue)
{
validRegistrationQuery = validRegistrationQuery.Where(x => x.Id != excludeRegistrationId.Value);
}
// 查询这些成员在该赛事中已报名的项目(按用户+项目去重)
var existingMemberProjects = await _db.Queryable<EventRegistrationPerson>()
.InnerJoin<EventRegistrationItem>((p, i) => p.RegistrationItemId == i.Id && !i.DeleteMark)
.InnerJoin(validRegistrationQuery, (p, i, r) => p.RegistrationId == r.Id)
.Where((p, i, r) => memberIds.Contains(p.UserId) && !p.DeleteMark)
.Select((p, i, r) => new
{
UserId = p.UserId,
ProjectId = i.ProjectId
})
.Distinct()
.ToListAsync();
// 按用户分组统计已报名项目
var memberProjectMap = existingMemberProjects
.GroupBy(x => x.UserId)
.ToDictionary(
g => g.Key,
g => g.Select(x => x.ProjectId).ToHashSet());
// 构建项目类型映射(赛事项目ID -> 基础项目类型)
var projectTypeMap = new Dictionary<long, bool>(); // key: eventProjectId, value: isIndividual
foreach (var ep in eventProjects)
{
var baseProject = projects.FirstOrDefault(p => p.Id == ep.ProjectId);
if (baseProject != null)
{
projectTypeMap[ep.Id] = baseProject.IsIndividualProject();
}
}
// 统计每个成员的个人/团队项目数
foreach (var member in members)
{
_logger.LogDebug("检查成员报名限制,成员ID: {MemberId}, 姓名: {MemberName}", member.Id, member.Name);
var (individualProjectCount, teamProjectCount) = CalculateMemberProjectCounts(
member.Id, eventProjects, projects, registrationPersons, allRegistrationItems, input);
var countedProjectIds = new HashSet<long>();
int individualProjectCount = 0;
int teamProjectCount = 0;
// 统计已存在的报名项目
if (memberProjectMap.TryGetValue(member.Id, out var existingProjectIds))
{
foreach (var projectId in existingProjectIds)
{
if (!countedProjectIds.Add(projectId)) continue;
if (projectTypeMap.TryGetValue(projectId, out var isIndividual))
{
if (isIndividual)
individualProjectCount++;
else
teamProjectCount++;
}
}
}
// 统计本次提交的项目,确保不重复统计
foreach (var item in input.Items)
{
var allPersonsInItem = GetAllPersonsFromItem(item);
if (!allPersonsInItem.Any(p => p.UserId.HasValue && p.UserId.Value == member.Id))
continue;
if (!countedProjectIds.Add(item.ProjectId)) continue;
if (projectTypeMap.TryGetValue(item.ProjectId, out var isIndividual))
{
if (isIndividual)
individualProjectCount++;
else
teamProjectCount++;
}
}
_logger.LogDebug("成员 {MemberName} 个人项目报名数: {IndividualCount}, 团队项目报名数: {TeamCount}",
member.Name, individualProjectCount, teamProjectCount);
CheckIndividualProjectLimit(result, limit, member, individualProjectCount);
if (result.HasLimit) return;
CheckTeamProjectLimit(result, limit, member, teamProjectCount);
if (result.HasLimit) return;
}
}
/// <summary>
/// 计算成员项目报名数量(包含本次提交的项目)
/// </summary>
private (int individualCount, int teamCount) CalculateMemberProjectCounts(
long memberId,
List<EventProject> eventProjects,
List<YD_Event.Core.Entities.Project> projects,
List<EventRegistrationPerson> registrationPersons,
List<EventRegistrationItem> allRegistrationItems,
UnifiedRegistrationInput input)
{
var matchedPersons = registrationPersons.Where(x => x.UserId == memberId).ToList();
var personRegistrationIds = matchedPersons.Select(x => x.RegistrationId).Distinct().ToList();
var personRegistrationItems = allRegistrationItems.Where(ri => personRegistrationIds.Contains(ri.RegistrationId)).ToList();
int individualProjectCount = 0;
int teamProjectCount = 0;
// 已统计过的项目ID(同一项目下的多个组别不会重复统计)
var countedProjectIds = new HashSet<long>();
// 统计已存在的报名项目(去重:同一项目下的多个组别仅统计一次)
foreach (var item in personRegistrationItems)
{
if (!countedProjectIds.Add(item.ProjectId))
{
continue;
}
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == item.ProjectId);
if (eventProject == null) continue;
var baseProject = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
if (baseProject == null) continue;
if (baseProject.IsIndividualProject())
{
individualProjectCount++;
}
else if (baseProject.IsTeamProject())
{
teamProjectCount++;
}
}
// 统计本次提交的项目,确保不重复统计
foreach (var item in input.Items)
{
// 检查该成员是否在本次提交的项目中
var allPersonsInItem = GetAllPersonsFromItem(item);
if (!allPersonsInItem.Any(p => p.UserId.HasValue && p.UserId.Value == memberId))
{
continue;
}
// 检查是否已经统计过该项目(包含已有报名或本次提交中已统计过的项目)
if (!countedProjectIds.Add(item.ProjectId))
{
continue;
}
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == item.ProjectId);
if (eventProject == null) continue;
var baseProject = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
if (baseProject == null) continue;
if (baseProject.IsIndividualProject())
{
individualProjectCount++;
}
else if (baseProject.IsTeamProject())
{
teamProjectCount++;
}
}
return (individualProjectCount, teamProjectCount);
}
/// <summary>
/// 检查个人项目限报
/// </summary>
@ -1838,60 +1888,96 @@ public class UnifiedRegistrationService : ITransient
}
/// <summary>
/// 检查人员是否已在其他报名中报过同一项目组别
/// 检查人员是否已在其他报名中报过同一项目组别(优化版:使用数据库查询,避免全量加载)
/// </summary>
private void CheckPersonAlreadyRegisteredAsync(
private async Task CheckPersonAlreadyRegisteredAsync(
UnifiedRegistrationInput input,
RegistrationLimitCheckOutput result,
List<EventProject> eventProjects,
List<YD_Event.Core.Entities.Project> projects,
List<EventProjectGroup> eventProjectGroups,
List<EventRegistrationPerson> registrationPersons,
List<EventRegistrationItem> allRegistrationItems,
List<long> eventRegistrationIds)
long? excludeRegistrationId = null)
{
if (!eventRegistrationIds.Any())
{
return;
}
// 收集所有待检查的 (groupId, userId) 组合
var checkPairs = new List<(long GroupId, long UserId, string UserName)>();
foreach (var item in input.Items)
{
if (!item.GroupId.HasValue) continue;
var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == item.GroupId.Value);
if (eventProjectGroup == null) continue;
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);
if (eventProject == null) continue;
var project = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
var projectName = project?.Name ?? "未知项目";
var existingItems = allRegistrationItems.Where(ri => ri.EventProjectGroupId == item.GroupId.Value).ToList();
if (!existingItems.Any()) continue;
var existingItemIds = existingItems.Select(ri => ri.Id).ToList();
var existingPersons = registrationPersons.Where(rp => existingItemIds.Contains(rp.RegistrationItemId)).ToList();
var allPersonsInItem = GetAllPersonsFromItem(item);
foreach (var person in allPersonsInItem)
{
if (!person.UserId.HasValue) continue;
var existingPerson = existingPersons.FirstOrDefault(ep => ep.UserId == person.UserId.Value);
if (existingPerson == null) continue;
checkPairs.Add((item.GroupId.Value, person.UserId.Value, person.Name ?? "未知人员"));
}
}
if (!checkPairs.Any()) return;
var groupIds = checkPairs.Select(p => p.GroupId).Distinct().ToList();
var userIds = checkPairs.Select(p => p.UserId).Distinct().ToList();
// 构建有效报名记录的子查询
var validRegistrationQuery = _registrationRepository.AsQueryable()
.Where(x => x.EventId == input.EventId && !x.DeleteMark &&
(
(x.PaymentMethod == 2 && x.PaymentStatus == 1) ||
(x.PaymentMethod == 1 && (x.PaymentStatus == 1 || x.PaymentStatus == 0))
) &&
x.PaymentStatus != 4 && x.PaymentStatus != 5 && x.PaymentStatus != 6);
if (excludeRegistrationId.HasValue)
{
validRegistrationQuery = validRegistrationQuery.Where(x => x.Id != excludeRegistrationId.Value);
}
// 查询这些用户在这些组别中是否已有报名记录
var existingRegistrations = await _db.Queryable<EventRegistrationPerson>()
.InnerJoin<EventRegistrationItem>((p, i) => p.RegistrationItemId == i.Id && !i.DeleteMark)
.InnerJoin(validRegistrationQuery, (p, i, r) => p.RegistrationId == r.Id)
.Where((p, i, r) =>
userIds.Contains(p.UserId) &&
groupIds.Contains(i.EventProjectGroupId.Value) &&
!p.DeleteMark)
.Select((p, i, r) => new
{
UserId = p.UserId,
GroupId = i.EventProjectGroupId
})
.Distinct()
.ToListAsync();
// 构建已报名的哈希集合,用于快速查找
var registeredSet = new HashSet<(long UserId, long GroupId)>();
foreach (var reg in existingRegistrations)
{
if (reg.GroupId.HasValue)
{
registeredSet.Add((reg.UserId, reg.GroupId.Value));
}
}
// 检查是否有重复
foreach (var pair in checkPairs)
{
if (registeredSet.Contains((pair.UserId, pair.GroupId)))
{
var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == pair.GroupId);
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup?.EventProjectId);
var project = projects.FirstOrDefault(p => p.Id == eventProject?.ProjectId);
var projectName = project?.Name ?? "未知项目";
var groupName = eventProjectGroup?.GroupName ?? "未知组别";
_logger.LogWarning("人员已报名该项目组别,人员: {PersonName}, 项目: {ProjectName}, 组别: {GroupName}",
person.Name, projectName, eventProjectGroup.GroupName);
pair.UserName, projectName, groupName);
result.HasLimit = true;
result.LimitDetails.Add(CreateLimitDetail(
0,
person.Name,
pair.UserName,
"PersonAlreadyRegistered",
$"人员已报名检测:人员\"{person.Name}\"已在项目\"{projectName}\"的组别\"{eventProjectGroup.GroupName}\"中报名过,不能重复报名,避免占用多个名额"));
$"人员已报名检测:人员\"{pair.UserName}\"已在项目\"{projectName}\"的组别\"{groupName}\"中报名过,不能重复报名,避免占用多个名额"));
return;
}
}
}

View File

@ -1096,18 +1096,26 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
return result;
}
var eventProjectGroup = await _eventProjectGroupRepository.GetByIdAsync(input.GroupId);
if (eventProjectGroup != null)
// 优化:合并 eventProjectGroup → eventProject → project 三次查询为一次 JOIN 查询
var groupWithProject = await _db.Queryable<EventProjectGroup>()
.LeftJoin<EventProject>((g, ep) => g.EventProjectId == ep.Id)
.LeftJoin<YD_Event.Core.Entities.Project>((g, ep, p) => ep.ProjectId == p.Id)
.Where((g, ep, p) => g.Id == input.GroupId && !g.DeleteMark)
.Select((g, ep, p) => new
{
Group = g,
ProjectType = p.ProjectType
})
.FirstAsync();
if (groupWithProject?.Group != null)
{
// 获取项目类型信息
var eventProject = await _eventProjectRepository.GetByIdAsync(eventProjectGroup.EventProjectId);
var baseProject = eventProject != null ? await _projectRepository.GetByIdAsync(eventProject.ProjectId) : null;
var isTeamProject = baseProject?.IsTeamProject() ?? false;
var isTeamProject = groupWithProject.ProjectType.IsTeamProject();
var persons = BuildValidationPersons(members, input.MemberId);
// 使用统一的验证方法进行验证
var validationResult = _unifiedRegistrationService.ValidatePersonQualifications(persons, eventProjectGroup, isTeamProject);
var validationResult = _unifiedRegistrationService.ValidatePersonQualifications(persons, groupWithProject.Group, isTeamProject);
if (!validationResult.IsValid)
{
@ -1352,13 +1360,27 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
// 获取所有赛事项目,用于关联 ProjectId
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == eventId);
// 优化:一次查询统计所有组别的已报名队伍数,替代循环查询
// 注意:与原逻辑保持一致,统计的是 registrationItem 的数量(不去重 RegistrationId)
var registrationItems = await _registrationItemRepository.AsQueryable()
.Where(x => x.EventProjectGroupId.HasValue && !x.DeleteMark)
.InnerJoin<EventRegistration>((i, r) => i.RegistrationId == r.Id && !r.DeleteMark && r.EventId == eventId)
.Select((i, r) => new
{
GroupId = i.EventProjectGroupId
})
.ToListAsync();
// 构建组别 -> 已报名队伍数 的映射(与原逻辑一致,直接统计 item 数量)
var groupTeamCountMap = registrationItems
.Where(x => x.GroupId.HasValue)
.GroupBy(x => x.GroupId!.Value)
.ToDictionary(g => g.Key, g => g.Count());
foreach (var eventProjectGroup in eventProjectGroups)
{
var currentTeams = await _registrationItemRepository.AsQueryable()
.Where(x => x.EventProjectGroupId == eventProjectGroup.Id && !x.DeleteMark)
.Select(x => x.RegistrationId)
.Distinct()
.CountAsync();
// 从映射中获取已报名队伍数
var currentTeams = groupTeamCountMap.TryGetValue(eventProjectGroup.Id, out var count) ? count : 0;
// 查找该组别的项目限报配置
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);