小程序金额计算

This commit is contained in:
马梓彭 2026-05-08 10:54:08 +08:00
parent d573fb73c4
commit 532ad46ca0
4 changed files with 289 additions and 54 deletions

View File

@ -454,22 +454,23 @@ public class MyController : IDynamicApiController
.LeftJoin<EventProject>((p, e, ep) => p.ProjectId == ep.Id)
.LeftJoin<ProjectEntity>((p, e, ep, pr) => ep.ProjectId == pr.Id)
.LeftJoin<EventProjectGroup>((p, e, ep, pr, pg) => p.ProjectGroupId == pg.Id)
.LeftJoin<EventResult>((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);

View File

@ -1653,6 +1653,15 @@ public class EventScoreService : BaseService<EventResult>, 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<EventRegistrationPerson>((r, p) => r.RegistrationPersonId == p.Id)
.Where((r, p) => r.EventId == input.EventId && r.Rank > 0)
@ -1665,7 +1674,8 @@ public class EventScoreService : BaseService<EventResult>, 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<EventResult>, ITransient
int successCount = 0;
int skipCount = 0;
var pointsRecords = new List<PointsRecord>();
var processedTeams = new HashSet<string>();
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++;
}
}
}
}

View File

@ -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<UnifiedPersonInput>(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);

View File

@ -14,6 +14,7 @@ public class PointsRecordService : BaseService<PointsRecord>, ITransient
private readonly ISqlSugarRepository<PointsRecord> _repository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _appUserRepository;
private readonly ISqlSugarRepository<PointsRule> _pointsRuleRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
private readonly ISqlSugarClient _db;
private readonly AuthManager _authManager;
@ -21,12 +22,14 @@ public class PointsRecordService : BaseService<PointsRecord>, ITransient
ISqlSugarRepository<PointsRecord> repository,
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
ISqlSugarRepository<PointsRule> pointsRuleRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Project> 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<PointsRecord>, ITransient
.LeftJoin<YD_Event.Core.Entities.Project>((p, e, ep, pr) => ep.ProjectId == pr.Id)
.LeftJoin<ProjectGroup>((p, e, ep, pr, pg) => p.ProjectGroupId == pg.Id)
.LeftJoin<YD_Event.Core.Entities.AppUser>((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<EventResult>((p, e, ep, pr, pg, u, er) => p.ResultId == er.Id && !er.DeleteMark)
.LeftJoin<EventRegistrationPerson>((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<long, YD_Event.Core.Entities.Project>();
if (projectIds.Any())
{
var projectList = await _projectRepository.GetListAsync(p => projectIds.Contains(p.Id));
projects = projectList.ToDictionary(p => p.Id);
}
// 处理数据
var processedData = new List<PointsDetailOutput>();
var teamRegistrationItemIds = new List<long>();
// 先处理团队项目,收集需要查询的 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<long, string> teamPersonDict = new Dictionary<long, string>();
if (teamRegistrationItemIds.Any())
{
var distinctTeamIds = teamRegistrationItemIds.Distinct().ToList();
// 查询同一 RegistrationItemId 的所有 EventRegistrationPerson,获取姓名
var teamPersons = await _db.Queryable<EventRegistrationPerson>()
.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<PointsDetailOutput>
{
PageNo = input.PageNo,
PageSize = pageSize,
Total = total,
Pages = (int)Math.Ceiling((double)total / pageSize),
Rows = pagedData
};
}
}
}