using SqlSugar; using YD_Event.Application.Auth; using YD_Event.Application.Points.Dtos; using YD_Event.Core.Entities; using YD_Event.Core.Enum; namespace YD_Event.Application.Points; /// /// 积分记录管理 /// public class PointsRecordService : BaseService, ITransient { private readonly ISqlSugarRepository _repository; private readonly ISqlSugarRepository _appUserRepository; private readonly ISqlSugarRepository _pointsRuleRepository; private readonly ISqlSugarClient _db; private readonly AuthManager _authManager; public PointsRecordService( ISqlSugarRepository repository, ISqlSugarRepository appUserRepository, ISqlSugarRepository pointsRuleRepository, ISqlSugarClient db, AuthManager authManager) : base(repository) { _repository = repository; _appUserRepository = appUserRepository; _pointsRuleRepository = pointsRuleRepository; _db = db; _authManager = authManager; } /// /// 获取用户当前积分余额 (PC用户) /// /// [DisplayName("获取用户当前积分余额")] [HttpGet("balance")] public async Task GetBalance() { var userId = _authManager.UserId; var certification = await _db.Queryable() .FirstAsync(c => c.UserId == userId && c.AuditStatus == AuditStatus.Pass && !c.DeleteMark); decimal balance; if (certification != null) { // 单位认证用户:查询单位认证ID的积分 balance = await _repository.AsQueryable() .Where(x => x.MerchantCertificationId == certification.Id) .SumAsync(x => x.Points); } else { // 个人用户:查询个人积分 balance = await _repository.AsQueryable() .Where(x => x.UserId == userId && x.MerchantCertificationId == null) .SumAsync(x => x.Points); } return new UserPointsOverviewDto { CurrentBalance = balance }; } /// /// 查询积分明细列表 (PC用户) /// /// /// [DisplayName("查询积分明细列表")] [HttpGet("logs")] public async Task> GetLogs([FromQuery] UserPointsQueryInput input) { var userId = _authManager.UserId; var certification = await _db.Queryable() .FirstAsync(c => c.UserId == userId && c.AuditStatus == AuditStatus.Pass && !c.DeleteMark); var query = _repository.AsQueryable() .LeftJoin((pr, e) => pr.EventId == e.Id) .LeftJoin((pr, e, p) => pr.ProjectId == p.Id) .LeftJoin((pr, e, p, pg) => pr.ProjectGroupId == pg.Id) .LeftJoin((pr, e, p, pg, u) => pr.UserId == u.Id) .WhereIF(input.Type.HasValue, pr => pr.Type == input.Type) .WhereIF(!string.IsNullOrWhiteSpace(input.ActionKeyword), (pr, e) => e.EventName.Contains(input.ActionKeyword) || pr.Remark.Contains(input.ActionKeyword)) .WhereIF(input.StartTime.HasValue, pr => pr.CreatedTime >= input.StartTime) .WhereIF(input.EndTime.HasValue, pr => pr.CreatedTime <= input.EndTime) .OrderByDescending(pr => pr.CreatedTime); if (certification != null) { // 单位认证用户:查询单位认证ID的积分记录 query = query.Where(pr => pr.MerchantCertificationId == certification.Id); } else { // 个人查询:按 UserId 过滤,且排除单位认证记录 query = query.Where(pr => pr.UserId == userId && pr.MerchantCertificationId == null); } var result = await query.Select((pr, e, p, pg, u) => new UserPointsLogDto { Id = pr.Id, Type = pr.Type, Action = e.EventName ?? pr.Remark ?? "未知动作", ProjectName = p.Name, GroupName = pg.GroupName, ParticipantName = u.Name, // 显示关联用户的姓名 Rank = 0, // 暂无法直接关联到名次,需业务侧在写入PointsRecord时记录 Points = pr.Points, CreatedTime = pr.CreatedTime }) .ToPagedListAsync(input); return result; } /// /// 积分规则说明接口 (PC用户) /// /// [DisplayName("积分规则说明接口")] [HttpGet("rules")] [AllowAnonymous] public async Task> GetRules() { // 返回所有启用的积分规则 return await _pointsRuleRepository.AsQueryable() .Where(x => x.Status == AvailabilityStatus.Enable) .Includes(x => x.Details) // 包含明细 .OrderBy(x => x.CreatedTime) .ToListAsync(); } /// /// 积分记录分页查询 /// [DisplayName("积分记录分页查询")] [HttpGet("list")] public async Task> List([FromQuery] PointsRecordQueryInput input) { if (input.UserType == 0) // 普通用户 { var query = _appUserRepository.AsQueryable() .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.Name.Contains(input.Keyword) || u.Mobile.Contains(input.Keyword)); // 关联查询积分 // 注意:这里需要根据时间范围筛选积分记录,还是筛选最后有积分变动的用户? // 需求:时间范围筛选。通常指筛选在该时间范围内有积分变动的记录,或者只统计该时间范围内的积分。 // 但“积分额度”通常指总积分。 // 如果时间范围筛选是指“只看某段时间的积分情况”,那么积分额度应该是该段时间内的总和。 // 如果是指“筛选某段时间有活动的用户”,积分额度可能是总积分。 // 考虑到“积分额度排序”,通常是总积分。 // 让我们假设:时间范围筛选是指筛选在该时间范围内有积分变动的用户,积分额度为当前总积分。 // 或者:积分额度 = 该时间范围内的积分总和。 // 根据管理后台惯例,如果有时间筛选,通常列表显示的是该时间段的数据统计。 // 让我们采用:积分额度 = 该时间范围内的积分总和(如果没有时间筛选,就是所有时间)。 // 使用子查询或 LeftJoin 计算积分 var result = await query .Select(u => new PointsRecordListOutput { UserId = u.Id, UserType = 0, Name = u.Name, Phone = u.Mobile, IdentityInfo = u.IdCardImageUrl, // 身份证图片 TotalPoints = SqlFunc.Subqueryable() .Where(p => p.UserId == u.Id && !p.DeleteMark) .WhereIF(input.StartTime.HasValue, p => p.CreatedTime >= input.StartTime) .WhereIF(input.EndTime.HasValue, p => p.CreatedTime <= input.EndTime) .Sum(p => p.Points), // 实时计算 LastUpdateTime = SqlFunc.Subqueryable() .Where(p => p.UserId == u.Id && !p.DeleteMark) .WhereIF(input.StartTime.HasValue, p => p.CreatedTime >= input.StartTime) .WhereIF(input.EndTime.HasValue, p => p.CreatedTime <= input.EndTime) .Max(p => p.CreatedTime) }) .MergeTable() // 将上面的 Select 结果作为临时表,以便进行后续的 Where 和 OrderBy .WhereIF(input.StartTime.HasValue || input.EndTime.HasValue, x => x.LastUpdateTime != null) // 如果有时间筛选,只显示有记录的用户 .OrderByIF(!string.IsNullOrWhiteSpace(input.SortPoints), x => x.TotalPoints, input.SortPoints == "asc" ? OrderByType.Asc : OrderByType.Desc) .OrderByDescending(x => x.LastUpdateTime) // 默认按更新时间倒序 .ToPagedListAsync(input); return result; } else // 单位用户 { // 单位用户现在应该查询 MerchantCertification 表 var query = _db.Queryable() .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), m => m.MerchantName.Contains(input.Keyword) || m.ContactPhone.Contains(input.Keyword)); var result = await query .Select(m => new PointsRecordListOutput { UserId = m.Id, // 单位认证ID UserType = 1, Name = m.MerchantName, Phone = m.ContactPhone, IdentityInfo = m.CreditCode, // 统一社会信用代码 TotalPoints = SqlFunc.Subqueryable() .Where(p => p.MerchantCertificationId == m.Id && !p.DeleteMark) .WhereIF(input.StartTime.HasValue, p => p.CreatedTime >= input.StartTime) .WhereIF(input.EndTime.HasValue, p => p.CreatedTime <= input.EndTime) .Sum(p => p.Points), LastUpdateTime = SqlFunc.Subqueryable() .Where(p => p.MerchantCertificationId == m.Id && !p.DeleteMark) .WhereIF(input.StartTime.HasValue, p => p.CreatedTime >= input.StartTime) .WhereIF(input.EndTime.HasValue, p => p.CreatedTime <= input.EndTime) .Max(p => p.CreatedTime) }) .MergeTable() .WhereIF(input.StartTime.HasValue || input.EndTime.HasValue, x => x.LastUpdateTime != null) .OrderByIF(!string.IsNullOrWhiteSpace(input.SortPoints), x => x.TotalPoints, input.SortPoints == "asc" ? OrderByType.Asc : OrderByType.Desc) .OrderByDescending(x => x.LastUpdateTime) .ToPagedListAsync(input); return result; } } /// /// 积分明细查询 /// [DisplayName("积分明细查询")] [HttpGet("detail/{userId}")] public async Task> Detail([FromQuery] PointsDetailQueryInput input) { var query = _repository.AsQueryable() .LeftJoin((p, e) => p.EventId == e.Id) .LeftJoin((p, e, pr) => p.ProjectId == pr.Id) .LeftJoin((p, e, pr, pg) => p.ProjectGroupId == pg.Id) .Where((p, e, pr, pg) => !p.DeleteMark) .WhereIF(input.Type.HasValue, (p, e, pr, pg) => p.Type == input.Type) .OrderByDescending((p, e, pr, pg) => p.CreatedTime); if (input.UserType == 0) // 普通用户 { query = query.Where((p, e, pr, pg) => p.UserId == input.UserId); return await query .Select((p, e, pr, pg) => new PointsDetailOutput { CreatedTime = p.CreatedTime, Type = p.Type, Action = e.EventName, Item = pr.Name, Group = pg.GroupName, Points = p.Points, Remark = p.Remark }).ToPagedListAsync(input); } else // 单位用户 { // 单位用户:查询 MerchantCertificationId = userId 的记录 query = query.Where((p, e, pr, pg) => p.MerchantCertificationId == input.UserId); return await query .LeftJoin((p, e, pr, pg, u) => p.UserId == u.Id) .Select((p, e, pr, pg, u) => new PointsDetailOutput { CreatedTime = p.CreatedTime, Type = p.Type, Action = e.EventName, Item = pr.Name, Group = pg.GroupName, Points = p.Points, Remark = p.Remark, ParticipantName = u.Name, // 参赛人姓名 ParticipantId = u.Id // 参赛人ID }).ToPagedListAsync(input); } } }