diff --git a/YD_Event.Application/Event/Dtos/ScoreDtos.cs b/YD_Event.Application/Event/Dtos/ScoreDtos.cs
index 5c42a7e..3b74be2 100644
--- a/YD_Event.Application/Event/Dtos/ScoreDtos.cs
+++ b/YD_Event.Application/Event/Dtos/ScoreDtos.cs
@@ -198,7 +198,7 @@ public class IssueCertificateInput
/// 页面类型:1-运动员页面,2-教练页面,3-领队页面
/// 用于明确区分不同角色页面的证书生成逻辑
///
- public CertificatePageType PageType { get; set; }
+ public int PageType { get; set; }
}
///
diff --git a/YD_Event.Application/Event/EventScoreService.cs b/YD_Event.Application/Event/EventScoreService.cs
index b796a0f..56221f2 100644
--- a/YD_Event.Application/Event/EventScoreService.cs
+++ b/YD_Event.Application/Event/EventScoreService.cs
@@ -423,25 +423,14 @@ public class EventScoreService : BaseService, ITransient
_logger.LogInformation("发放证书 - 页面类型: {PageType}, 允许的人员类型: {AllowedTypes}",
input.PageType, string.Join(", ", allowedPersonTypes));
- // 批量预加载所有需要的数据
- // 先尝试直接从 EventRegistrationPerson 表获取所有人员
- var directPersons = await _personRepository.GetListAsync(x => input.PersonIds.Contains(x.Id));
- var directPersonDict = directPersons.ToDictionary(p => p.Id);
-
- // 找出在 directPersonDict 中找不到的人员ID,这些可能是教练/领队的 AppUser ID
- var missingPersonIds = input.PersonIds.Where(id => !directPersonDict.ContainsKey(id)).ToList();
-
// 构建最终的人员字典
var personDict = new Dictionary();
- foreach (var kvp in directPersonDict)
- {
- personDict[kvp.Key] = kvp.Value;
- }
- // 对于找不到的人员ID,尝试通过团队关系查找对应的 EventRegistrationPerson 记录
- if (missingPersonIds.Any())
+ // 根据页面类型区分人员信息获取逻辑
+ if (input.PageType == 2 || input.PageType == 3)
{
- _logger.LogInformation("查找 {Count} 个缺失的人员ID,尝试通过团队关系获取", missingPersonIds.Count);
+ // 页面类型为教练(2)或领队(3)时,从团队关系数据中获取对应人员ID
+ _logger.LogInformation("页面类型为教练/领队,从团队关系数据中获取人员信息");
// 获取该赛事的所有 EventRegistrationPerson 记录
var allEventPersons = await _personRepository.AsQueryable()
@@ -456,20 +445,33 @@ public class EventScoreService : BaseService, ITransient
.GroupBy(p => p.UserId)
.ToDictionary(g => g.Key, g => g.First());
- // 尝试通过 UserId 匹配
- foreach (var missingId in missingPersonIds)
+ // 通过 UserId 查找对应的 EventRegistrationPerson
+ foreach (var userId in input.PersonIds)
{
- if (userToPersonMap.TryGetValue(missingId, out var person))
+ if (userToPersonMap.TryGetValue(userId, out var person))
{
- personDict[missingId] = person;
- _logger.LogInformation("通过 UserId {UserId} 找到对应的 EventRegistrationPerson: {PersonId}", missingId, person.Id);
+ personDict[userId] = person;
+ _logger.LogInformation("通过 UserId {UserId} 找到对应的 EventRegistrationPerson: {PersonId}", userId, person.Id);
}
else
{
- _logger.LogWarning("无法找到人员ID: {PersonId}", missingId);
+ _logger.LogWarning("无法通过 UserId {UserId} 找到对应的 EventRegistrationPerson", userId);
}
}
}
+ else
+ {
+ // 页面类型为运动员(1)或其他,直接获取人员ID
+ _logger.LogInformation("页面类型为运动员,直接获取人员信息");
+
+ // 直接从 EventRegistrationPerson 表获取所有人员
+ var directPersons = await _personRepository.GetListAsync(x => input.PersonIds.Contains(x.Id));
+ foreach (var person in directPersons)
+ {
+ personDict[person.Id] = person;
+ _logger.LogInformation("直接获取 EventRegistrationPerson: {PersonId}", person.Id);
+ }
+ }
// 使用最终找到的人员ID列表
var foundPersonIds = personDict.Values.Select(p => p.Id).ToList();
@@ -677,15 +679,15 @@ public class EventScoreService : BaseService, ITransient
///
/// 根据页面类型获取允许的人员类型列表
///
- /// 页面类型
+ /// 页面类型:1-运动员页面,2-教练页面,3-领队页面
/// 允许的人员类型列表
- private List GetAllowedPersonTypes(CertificatePageType pageType)
+ private List GetAllowedPersonTypes(int pageType)
{
return pageType switch
{
- CertificatePageType.Athlete => new List { PersonType.Athlete, PersonType.User },
- CertificatePageType.Coach => new List { PersonType.Coach },
- CertificatePageType.Leader => new List { PersonType.Leader },
+ 1 => new List { PersonType.Athlete, PersonType.User },
+ 2 => new List { PersonType.Coach },
+ 3 => new List { PersonType.Leader },
_ => new List { PersonType.Athlete, PersonType.User, PersonType.Coach, PersonType.Leader }
};
}
diff --git a/YD_Event.Application/Event/UnifiedRegistrationService.cs b/YD_Event.Application/Event/UnifiedRegistrationService.cs
index 2fc9ecd..04f5435 100644
--- a/YD_Event.Application/Event/UnifiedRegistrationService.cs
+++ b/YD_Event.Application/Event/UnifiedRegistrationService.cs
@@ -1160,7 +1160,8 @@ public class UnifiedRegistrationService : ITransient
// 2. 再检查成员项目限报 - 检查队员是否超过个人/团队项目的限报数量
// 3. 然后检查人员项目组别重复 - 检查同一人员是否在同一项目的多个组别中重复报名
// 4. 再检查人员是否已报名 - 检查人员是否已在其他报名记录中报过同一项目组别
- // 5. 最后检查项目完成人数 - 确保人数与项目的完成人数完全一致
+ // 5. 再检查团队成员冲突 - 检查同一人员是否在同一项目的多个团队中
+ // 6. 最后检查项目完成人数 - 确保人数与项目的完成人数完全一致
// 检查1:团队组别容量限制
// 验证是否有组别超过了限报的团队数量(例如:某组别限报2队,但本次报了3队)
@@ -1178,7 +1179,11 @@ public class UnifiedRegistrationService : ITransient
// 验证人员是否已在其他已支付的报名记录中报过同一项目组别(避免重复占用名额)
CheckPersonAlreadyRegisteredAsync(input, result, eventProjects, projects, eventProjectGroups, registrationPersons, allRegistrationItems, eventRegistrationIds);
- // 检查5:项目完成人数验证
+ // 检查5:团队成员冲突检测
+ // 验证同一人员是否在同一项目的多个团队中(例如:同一队员既在1队又在2队)
+ CheckTeamMemberConflictAsync(input, result, eventProjects, projects, eventProjectGroups);
+
+ // 检查6:项目完成人数验证
// 验证用户选择的参与人数必须与项目的"完成人数"完全一致(例如:项目要求完成3人,但只选了2人或选了4人)
CheckProjectCompletionAsync(input, result, eventProjects, projects, eventProjectGroups);
@@ -1595,6 +1600,112 @@ public class UnifiedRegistrationService : ITransient
}
}
+ ///
+ /// 检查团队成员冲突
+ /// 验证同一人员是否在同一项目的多个团队中(例如:同一队员既在1队又在2队)
+ ///
+ private void CheckTeamMemberConflictAsync(
+ UnifiedRegistrationInput input,
+ RegistrationLimitCheckOutput result,
+ List eventProjects,
+ List projects,
+ List eventProjectGroups)
+ {
+ 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);
+ if (project == null) continue;
+
+ // 判断是否为团队项目
+ var isTeamProject = project.IsTeamProject();
+ if (!isTeamProject) continue;
+
+ // 获取所有团队
+ var teams = GetTeamsFromItem(item);
+ if (teams.Count <= 1) continue;
+
+ // 建立用户到团队的映射,用于检测冲突
+ var userTeamMap = new Dictionary>();
+
+ foreach (var team in teams)
+ {
+ if (team.Persons == null || !team.Persons.Any()) continue;
+
+ foreach (var person in team.Persons)
+ {
+ if (!person.UserId.HasValue) continue;
+
+ var userId = person.UserId.Value;
+ var memberName = person.Name ?? "未知人员";
+
+ if (!userTeamMap.TryGetValue(userId, out var teamsList))
+ {
+ teamsList = new List<(int, string)>();
+ userTeamMap[userId] = teamsList;
+ }
+
+ teamsList.Add((team.TeamIndex, memberName));
+ }
+ }
+
+ // 检测冲突:同一用户在多个团队中
+ foreach (var kvp in userTeamMap)
+ {
+ if (kvp.Value.Count > 1)
+ {
+ var memberName = kvp.Value.First().MemberName;
+ var firstTeamIndex = kvp.Value.First().TeamIndex;
+ var projectName = project.Name ?? "未知项目";
+
+ // 提示信息格式:"XXX已在xx项目x队"
+ var errorMessage = $"{memberName}已在{projectName}{GetTeamNumber(firstTeamIndex)}";
+
+ _logger.LogWarning("团队成员冲突检测:人员{MemberName}已在项目{ProjectName}的多个团队中",
+ memberName, projectName);
+
+ result.HasLimit = true;
+ result.LimitDetails.Add(CreateLimitDetail(
+ kvp.Key,
+ memberName,
+ "TeamMemberConflict",
+ errorMessage));
+ }
+ }
+ }
+ }
+
+ ///
+ /// 获取团队序号的中文表示
+ ///
+ private static string GetTeamNumber(int teamIndex)
+ {
+ return teamIndex switch
+ {
+ 1 => "1队",
+ 2 => "2队",
+ 3 => "3队",
+ 4 => "4队",
+ 5 => "5队",
+ 6 => "6队",
+ 7 => "7队",
+ 8 => "8队",
+ 9 => "9队",
+ 10 => "10队",
+ _ => $"{teamIndex}队"
+ };
+ }
+
///
/// 检查项目完成人数
///
diff --git a/YD_Event.Application/YD_Event.Application.xml b/YD_Event.Application/YD_Event.Application.xml
index bb5cf3a..85282d1 100644
--- a/YD_Event.Application/YD_Event.Application.xml
+++ b/YD_Event.Application/YD_Event.Application.xml
@@ -5897,7 +5897,7 @@
- 页面类型:1-运动员页面,2-教练页面,3-领队页面
+ 页面类型:0-未指定,1-运动员页面,2-教练页面,3-领队页面
用于明确区分不同角色页面的证书生成逻辑
@@ -6758,7 +6758,27 @@
- 成员ID列表
+ 成员ID列表(单个团队报名时使用,向后兼容)
+
+
+
+
+ 团队列表(支持多个团队报名同一项目组别)
+
+
+
+
+ 用户团队输入DTO
+
+
+
+
+ 团队序号(用于显示,如"团队一"、"团队二")
+
+
+
+
+ 该团队的成员ID列表
@@ -7755,11 +7775,18 @@
发放证书(支持模板选择和批量发放)
-
+
+
+ 验证 PageType 参数是否有效
+
+ 页面类型值
+ 是否有效
+
+
根据页面类型获取允许的人员类型列表
- 页面类型
+ 页面类型:1-运动员页面,2-教练页面,3-领队页面
允许的人员类型列表
@@ -8143,6 +8170,17 @@
检查人员是否已在其他报名中报过同一项目组别
+
+
+ 检查团队成员冲突
+ 验证同一人员是否在同一项目的多个团队中(例如:同一队员既在1队又在2队)
+
+
+
+
+ 获取团队序号的中文表示
+
+
检查项目完成人数
diff --git a/YD_Event.Core/Enum/CertificatePageType.cs b/YD_Event.Core/Enum/CertificatePageType.cs
deleted file mode 100644
index 4242b3f..0000000
--- a/YD_Event.Core/Enum/CertificatePageType.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.ComponentModel;
-
-namespace YD_Event.Core.Enum;
-
-///
-/// 证书页面类型
-///
-public enum CertificatePageType
-{
- ///
- /// 未指定(向后兼容)
- ///
- [Description("未指定")]
- Unspecified = 0,
-
- ///
- /// 运动员页面
- ///
- [Description("运动员页面")]
- Athlete = 1,
-
- ///
- /// 教练页面
- ///
- [Description("教练页面")]
- Coach = 2,
-
- ///
- /// 领队页面
- ///
- [Description("领队页面")]
- Leader = 3
-}
diff --git a/YD_Event.Core/YD_Event.Core.xml b/YD_Event.Core/YD_Event.Core.xml
index 494f3b0..15bde8d 100644
--- a/YD_Event.Core/YD_Event.Core.xml
+++ b/YD_Event.Core/YD_Event.Core.xml
@@ -4295,31 +4295,6 @@
禁用
-
-
- 证书页面类型
-
-
-
-
- 未指定(向后兼容)
-
-
-
-
- 运动员页面
-
-
-
-
- 教练页面
-
-
-
-
- 领队页面
-
-
收费模式枚举