diff --git a/YD_Event.Application/Client/MyController.cs b/YD_Event.Application/Client/MyController.cs index 146c198..a9073cb 100644 --- a/YD_Event.Application/Client/MyController.cs +++ b/YD_Event.Application/Client/MyController.cs @@ -454,22 +454,23 @@ public class MyController : IDynamicApiController .LeftJoin((p, e, ep) => p.ProjectId == ep.Id) .LeftJoin((p, e, ep, pr) => ep.ProjectId == pr.Id) .LeftJoin((p, e, ep, pr, pg) => p.ProjectGroupId == pg.Id) + .LeftJoin((p, e, ep, pr, pg, r) => p.ResultId == r.Id && !r.DeleteMark) // 小程序端只查看个人积分:UserId = 当前用户且 MerchantCertificationId = null - .Where((p, e, ep, pr, pg) => p.UserId == userId && p.MerchantCertificationId == null && !p.DeleteMark); + .Where((p, e, ep, pr, pg, r) => p.UserId == userId && p.MerchantCertificationId == null && !p.DeleteMark); if (input.Type.HasValue) { var typeFilter = (PointsOperationType)input.Type.Value; - query = query.Where((p, e, ep, pr, pg) => p.Type == typeFilter); + query = query.Where((p, e, ep, pr, pg, r) => p.Type == typeFilter); } // 如果前端指定了 MerchantCertificationId,优先使用前端的(保持向后兼容) if (input.MerchantCertificationId.HasValue) { - query = query.Where((p, e, ep, pr, pg) => p.MerchantCertificationId == input.MerchantCertificationId.Value); + query = query.Where((p, e, ep, pr, pg, r) => p.MerchantCertificationId == input.MerchantCertificationId.Value); } - var resultQuery = query.Select((p, e, ep, pr, pg) => new MyPointsRecordDto + var resultQuery = query.Select((p, e, ep, pr, pg, r) => new MyPointsRecordDto { Id = p.Id, Title = p.Remark ?? (p.Type == PointsOperationType.Acquire ? "获取积分" : "消耗积分"), @@ -479,7 +480,7 @@ public class MyController : IDynamicApiController Points = p.Points, Type = (int)p.Type, CreatedTime = p.CreatedTime, - Rank = 1 // 积分记录表暂无名次字段,后续若有需要可补充 + Rank = r.Rank > 0 ? r.Rank : null // 从成绩记录中获取名次 }) .OrderByDescending(p => p.CreatedTime); diff --git a/YD_Event.Application/Event/EventScoreService.cs b/YD_Event.Application/Event/EventScoreService.cs index 93ff1f1..60ffb05 100644 --- a/YD_Event.Application/Event/EventScoreService.cs +++ b/YD_Event.Application/Event/EventScoreService.cs @@ -1653,6 +1653,15 @@ public class EventScoreService : BaseService, ITransient .OrderBy(x => x.StartRank) .ToListAsync(); + // 获取所有 EventProject 和对应的 Project 信息,用于判断项目类型 + var eventProjects = await _eventProjectRepository.AsQueryable() + .Where(ep => ep.EventId == input.EventId) + .ToListAsync(); + var projectIds = eventProjects.Select(ep => ep.ProjectId).Distinct().ToList(); + var projects = await _projectRepository.GetListAsync(p => projectIds.Contains(p.Id)); + var projectDict = projects.ToDictionary(p => p.Id); + + // 查询所有成绩记录,包含 RegistrationItemId var results = await _resultRepository.AsQueryable() .InnerJoin((r, p) => r.RegistrationPersonId == p.Id) .Where((r, p) => r.EventId == input.EventId && r.Rank > 0) @@ -1665,7 +1674,8 @@ public class EventScoreService : BaseService, ITransient Rank = r.Rank, ParticipantName = r.ParticipantName, ProjectGroupId = p.EventProjectGroupId, - RegistrationId = p.RegistrationId + RegistrationId = p.RegistrationId, + RegistrationItemId = p.RegistrationItemId }) .ToListAsync(); @@ -1688,54 +1698,156 @@ public class EventScoreService : BaseService, ITransient int successCount = 0; int skipCount = 0; var pointsRecords = new List(); + var processedTeams = new HashSet(); - foreach (var result in results) + // 先按 ProjectId 分组 + var projectGroups = results.GroupBy(r => r.ProjectId); + + foreach (var projectGroup in projectGroups) { - if (processedResultIds.Contains(result.ResultId)) + var projectId = projectGroup.Key; + var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == projectId); + if (eventProject == null || !projectDict.TryGetValue(eventProject.ProjectId, out var project)) { - skipCount++; + // 找不到项目,跳过 + foreach (var result in projectGroup) + { + skipCount++; + } continue; } - int pointsToAward = 0; + var isTeamProject = project.ProjectType == ProjectTypeEnum.TeamSpeed || + project.ProjectType == ProjectTypeEnum.TeamFreestyle; - if (pointsRule.ParticipationPoints.HasValue && pointsRule.ParticipationPoints.Value > 0) + if (isTeamProject) { - pointsToAward += pointsRule.ParticipationPoints.Value; - } - - var matchingDetail = ruleDetails.FirstOrDefault(d => result.Rank >= d.StartRank && result.Rank <= d.EndRank); - if (matchingDetail != null) - { - pointsToAward += matchingDetail.Points; - } - - if (pointsToAward > 0) - { - // 从 EventRegistration 获取归属单位 ID - long? merchantCertId = null; - if (registrationDict.TryGetValue(result.RegistrationId, out var reg)) + // 团队项目:按 RegistrationItemId 分组,每组只发放一条记录 + var teamGroups = projectGroup.GroupBy(r => new { r.RegistrationItemId, r.ProjectGroupId, r.GroupName }); + foreach (var teamGroup in teamGroups) { - merchantCertId = reg.MerchantCertificationId; + var groupKey = $"team_{teamGroup.Key.RegistrationItemId}_{teamGroup.Key.ProjectGroupId}"; + if (processedTeams.Contains(groupKey)) + { + // 这个团队已经发过了 + skipCount += teamGroup.Count(); + continue; + } + + // 检查团队中是否有结果已发放 + var hasProcessed = teamGroup.Any(r => processedResultIds.Contains(r.ResultId)); + if (hasProcessed) + { + skipCount += teamGroup.Count(); + continue; + } + + // 取团队第一个成员计算积分 + var teamResult = teamGroup.OrderBy(r => r.ResultId).First(); + + int pointsToAward = 0; + + if (pointsRule.ParticipationPoints.HasValue && pointsRule.ParticipationPoints.Value > 0) + { + pointsToAward += pointsRule.ParticipationPoints.Value; + } + + var matchingDetail = ruleDetails.FirstOrDefault(d => teamResult.Rank >= d.StartRank && teamResult.Rank <= d.EndRank); + if (matchingDetail != null) + { + pointsToAward += matchingDetail.Points; + } + + if (pointsToAward > 0) + { + // 从 EventRegistration 获取归属单位 ID + long? merchantCertId = null; + if (registrationDict.TryGetValue(teamResult.RegistrationId, out var reg)) + { + merchantCertId = reg.MerchantCertificationId; + } + + var record = new PointsRecord + { + Id = _idGenerator.NextId(), + UserId = teamResult.UserId, + Type = PointsOperationType.Acquire, + EventId = input.EventId, + ProjectId = teamResult.ProjectId, + ProjectGroupId = teamResult.ProjectGroupId, + ResultId = teamResult.ResultId, + MerchantCertificationId = merchantCertId, + Points = pointsToAward, + Remark = eventInfo.EventName, + Status = AvailabilityStatus.Enable, + CreatedTime = DateTime.Now + }; + pointsRecords.Add(record); + successCount++; + processedTeams.Add(groupKey); + } + else + { + skipCount += teamGroup.Count(); + } } - - var record = new PointsRecord + } + else + { + // 个人项目:按原来的逻辑处理 + foreach (var result in projectGroup) { - Id = _idGenerator.NextId(), - UserId = result.UserId, - Type = PointsOperationType.Acquire, - EventId = input.EventId, - ProjectId = result.ProjectId, - ProjectGroupId = result.ProjectGroupId, - ResultId = result.ResultId, - MerchantCertificationId = merchantCertId, - Points = pointsToAward, - Remark = eventInfo.EventName, - Status = AvailabilityStatus.Enable, - CreatedTime = DateTime.Now - }; - pointsRecords.Add(record); - successCount++; + if (processedResultIds.Contains(result.ResultId)) + { + skipCount++; + continue; + } + + int pointsToAward = 0; + + if (pointsRule.ParticipationPoints.HasValue && pointsRule.ParticipationPoints.Value > 0) + { + pointsToAward += pointsRule.ParticipationPoints.Value; + } + + var matchingDetail = ruleDetails.FirstOrDefault(d => result.Rank >= d.StartRank && result.Rank <= d.EndRank); + if (matchingDetail != null) + { + pointsToAward += matchingDetail.Points; + } + + if (pointsToAward > 0) + { + // 从 EventRegistration 获取归属单位 ID + long? merchantCertId = null; + if (registrationDict.TryGetValue(result.RegistrationId, out var reg)) + { + merchantCertId = reg.MerchantCertificationId; + } + + var record = new PointsRecord + { + Id = _idGenerator.NextId(), + UserId = result.UserId, + Type = PointsOperationType.Acquire, + EventId = input.EventId, + ProjectId = result.ProjectId, + ProjectGroupId = result.ProjectGroupId, + ResultId = result.ResultId, + MerchantCertificationId = merchantCertId, + Points = pointsToAward, + Remark = eventInfo.EventName, + Status = AvailabilityStatus.Enable, + CreatedTime = DateTime.Now + }; + pointsRecords.Add(record); + successCount++; + } + else + { + skipCount++; + } + } } } diff --git a/YD_Event.Application/Event/UnifiedRegistrationService.cs b/YD_Event.Application/Event/UnifiedRegistrationService.cs index c284fd3..3770638 100644 --- a/YD_Event.Application/Event/UnifiedRegistrationService.cs +++ b/YD_Event.Application/Event/UnifiedRegistrationService.cs @@ -1960,7 +1960,18 @@ public class UnifiedRegistrationService : ITransient var projectIds = eventProjects.Select(ep => ep.ProjectId).Distinct().ToList(); var baseProjects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id)); - int totalParticipantCount = items.Sum(i => i.PersonCount); + // 根据收费模式决定使用哪种人数计算金额(和 CreateRegistrationDetailsAsync 保持一致) + int countForCalculation; + if (feeConfig != null && feeConfig.ChargeMode == ChargeMode.UnitPriceEvent) + { + // 单价(赛事)模式:预览时默认按1个人计算(因为是同一个用户报名多个项目) + countForCalculation = 1; + } + else + { + // 其他模式(包括单价项目):使用实际人数 + countForCalculation = items.Sum(i => i.PersonCount); + } var unifiedItems = items.Select(i => new UnifiedRegistrationItemInput { @@ -1968,7 +1979,7 @@ public class UnifiedRegistrationService : ITransient Persons = new List(new UnifiedPersonInput[i.PersonCount]) }).ToList(); - var totalAmount = CalculateTotalAmount(feeConfig, eventProjects, unifiedItems, totalParticipantCount); + var totalAmount = CalculateTotalAmount(feeConfig, eventProjects, unifiedItems, countForCalculation); var eventProjectsDict = eventProjects.ToDictionary(p => p.Id); var baseProjectsDict = baseProjects.ToDictionary(p => p.Id); diff --git a/YD_Event.Application/Points/PointsRecordService.cs b/YD_Event.Application/Points/PointsRecordService.cs index f60b135..614620a 100644 --- a/YD_Event.Application/Points/PointsRecordService.cs +++ b/YD_Event.Application/Points/PointsRecordService.cs @@ -14,6 +14,7 @@ public class PointsRecordService : BaseService, ITransient private readonly ISqlSugarRepository _repository; private readonly ISqlSugarRepository _appUserRepository; private readonly ISqlSugarRepository _pointsRuleRepository; + private readonly ISqlSugarRepository _projectRepository; private readonly ISqlSugarClient _db; private readonly AuthManager _authManager; @@ -21,12 +22,14 @@ public class PointsRecordService : BaseService, ITransient ISqlSugarRepository repository, ISqlSugarRepository appUserRepository, ISqlSugarRepository pointsRuleRepository, + ISqlSugarRepository projectRepository, ISqlSugarClient db, AuthManager authManager) : base(repository) { _repository = repository; _appUserRepository = appUserRepository; _pointsRuleRepository = pointsRuleRepository; + _projectRepository = projectRepository; _db = db; _authManager = authManager; } @@ -269,25 +272,133 @@ public class PointsRecordService : BaseService, ITransient .LeftJoin((p, e, ep, pr) => ep.ProjectId == pr.Id) .LeftJoin((p, e, ep, pr, pg) => p.ProjectGroupId == pg.Id) .LeftJoin((p, e, ep, pr, pg, u) => p.UserId == u.Id && !u.DeleteMark) - .Where((p, e, ep, pr, pg, u) => !p.DeleteMark) - .Where((p, e, ep, pr, pg, u) => p.MerchantCertificationId == input.UserId) - .WhereIF(input.Type.HasValue, (p, e, ep, pr, pg, u) => p.Type == input.Type) - .OrderByDescending((p, e, ep, pr, pg, u) => p.CreatedTime); + .LeftJoin((p, e, ep, pr, pg, u, er) => p.ResultId == er.Id && !er.DeleteMark) + .LeftJoin((p, e, ep, pr, pg, u, er, per) => er.RegistrationPersonId == per.Id && !per.DeleteMark) + .Where((p, e, ep, pr, pg, u, er, per) => !p.DeleteMark) + .Where((p, e, ep, pr, pg, u, er, per) => p.MerchantCertificationId == input.UserId) + .WhereIF(input.Type.HasValue, (p, e, ep, pr, pg, u, er, per) => p.Type == input.Type) + .OrderByDescending((p, e, ep, pr, pg, u, er, per) => p.CreatedTime); - return await query - .Select((p, e, ep, pr, pg, u) => new PointsDetailOutput + var rawData = await query + .Select((p, e, ep, pr, pg, u, er, per) => new { + PointRecordId = p.Id, CreatedTime = p.CreatedTime, Type = p.Type, Action = e.EventName, Item = pr.Name, + ProjectId = pr.Id, + ProjectType = pr.ProjectType, Group = pg.GroupName, Points = p.Points, Remark = p.Remark, - ParticipantName = u.Name, // 参赛人姓名 - ParticipantId = u.Id // 参赛人ID - }).ToPagedListAsync(input); + ParticipantName = u.Name, + ParticipantId = u.Id, + RegistrationItemId = per.RegistrationItemId + }) + .ToListAsync(); + // 收集所有项目ID + var projectIds = rawData.Where(d => d.ProjectId > 0).Select(d => d.ProjectId).Distinct().ToList(); + // 获取项目信息,判断是否是团队项目 + var projects = new Dictionary(); + if (projectIds.Any()) + { + var projectList = await _projectRepository.GetListAsync(p => projectIds.Contains(p.Id)); + projects = projectList.ToDictionary(p => p.Id); + } + + // 处理数据 + var processedData = new List(); + var teamRegistrationItemIds = new List(); + + // 先处理团队项目,收集需要查询的 RegistrationItemId + foreach (var item in rawData) + { + var isTeamProject = false; + if (item.ProjectId > 0 && projects.TryGetValue(item.ProjectId, out var project)) + { + isTeamProject = project.ProjectType == YD_Event.Core.Enum.ProjectTypeEnum.TeamSpeed || + project.ProjectType == YD_Event.Core.Enum.ProjectTypeEnum.TeamFreestyle; + } + + if (isTeamProject) + { + teamRegistrationItemIds.Add(item.RegistrationItemId); + } + } + + // 查询团队项目的团队成员 + Dictionary teamPersonDict = new Dictionary(); + if (teamRegistrationItemIds.Any()) + { + var distinctTeamIds = teamRegistrationItemIds.Distinct().ToList(); + // 查询同一 RegistrationItemId 的所有 EventRegistrationPerson,获取姓名 + var teamPersons = await _db.Queryable() + .Where(per => distinctTeamIds.Contains(per.RegistrationItemId) && !per.DeleteMark) + .Select(per => new { per.RegistrationItemId, per.Name }) + .ToListAsync(); + + // 按 RegistrationItemId 分组 + teamPersonDict = teamPersons + .GroupBy(p => p.RegistrationItemId) + .ToDictionary( + g => g.Key, + g => string.Join(", ", g.Select(p => p.Name).Distinct().OrderBy(n => n)) + ); + } + + // 构建最终输出 + foreach (var item in rawData) + { + var isTeamProject = false; + if (item.ProjectId > 0 && projects.TryGetValue(item.ProjectId, out var project)) + { + isTeamProject = project.ProjectType == YD_Event.Core.Enum.ProjectTypeEnum.TeamSpeed || + project.ProjectType == YD_Event.Core.Enum.ProjectTypeEnum.TeamFreestyle; + } + + string participantName = item.ParticipantName; + if (isTeamProject) + { + string names; + if (teamPersonDict.TryGetValue(item.RegistrationItemId, out names)) + { + participantName = names; + } + } + + processedData.Add(new PointsDetailOutput + { + CreatedTime = item.CreatedTime, + Type = item.Type, + Action = item.Action, + Item = item.Item, + Group = item.Group, + Points = item.Points, + Remark = item.Remark, + ParticipantName = participantName, + ParticipantId = item.ParticipantId + }); + } + + // 重新排序 + processedData = processedData.OrderByDescending(x => x.CreatedTime).ToList(); + + // 手动分页 + var total = processedData.Count; + var pageSize = input.PageSize > 0 ? input.PageSize : 10; + var pageNo = input.PageNo > 0 ? input.PageNo - 1 : 0; + var pagedData = processedData.Skip(pageNo * pageSize).Take(pageSize).ToList(); + + return new PageResult + { + PageNo = input.PageNo, + PageSize = pageSize, + Total = total, + Pages = (int)Math.Ceiling((double)total / pageSize), + Rows = pagedData + }; } } }