YD_Event.Admin/YD_Event.Application/Points/PointsRecordService.cs
2026-03-12 10:30:13 +08:00

276 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
/// 积分记录管理
/// </summary>
public class PointsRecordService : BaseService<PointsRecord>, ITransient
{
private readonly ISqlSugarRepository<PointsRecord> _repository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _appUserRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Merchant> _merchantRepository;
private readonly ISqlSugarRepository<PointsRule> _pointsRuleRepository;
private readonly AuthManager _authManager;
public PointsRecordService(
ISqlSugarRepository<PointsRecord> repository,
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Merchant> merchantRepository,
ISqlSugarRepository<PointsRule> pointsRuleRepository,
AuthManager authManager) : base(repository)
{
_repository = repository;
_appUserRepository = appUserRepository;
_merchantRepository = merchantRepository;
_pointsRuleRepository = pointsRuleRepository;
_authManager = authManager;
}
/// <summary>
/// 获取用户当前积分余额 (PC用户)
/// </summary>
/// <returns></returns>
[DisplayName("获取用户当前积分余额")]
[HttpGet("balance")]
public async Task<UserPointsOverviewDto> GetBalance()
{
var userId = _authManager.UserId;
var merchant = await _merchantRepository.GetFirstAsync(m => m.CreatedUserId == userId);
// 如果是商户/团队账号,查询商户积分;否则查询个人积分
decimal balance;
if (merchant != null)
{
balance = await _repository.AsQueryable()
.Where(x => x.MerchantId == merchant.Id)
.SumAsync(x => x.Points);
}
else
{
balance = await _repository.AsQueryable()
.Where(x => x.UserId == userId && x.MerchantId == null) // 个人积分需排除作为商户成员的记录(如果有)
.SumAsync(x => x.Points);
}
return new UserPointsOverviewDto { CurrentBalance = balance };
}
/// <summary>
/// 查询积分明细列表 (PC用户)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("查询积分明细列表")]
[HttpGet("logs")]
public async Task<PageResult<UserPointsLogDto>> GetLogs([FromQuery] UserPointsQueryInput input)
{
var userId = _authManager.UserId;
var merchant = await _merchantRepository.GetFirstAsync(m => m.CreatedUserId == userId);
var query = _repository.AsQueryable()
.LeftJoin<EventInfo>((pr, e) => pr.EventId == e.Id)
.LeftJoin<YD_Event.Core.Entities.Project>((pr, e, p) => pr.ProjectId == p.Id)
.LeftJoin<ProjectGroup>((pr, e, p, pg) => pr.ProjectGroupId == pg.Id)
.LeftJoin<YD_Event.Core.Entities.AppUser>((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 (merchant != null)
{
// 商户查询:按 MerchantId 过滤
query = query.Where(pr => pr.MerchantId == merchant.Id);
}
else
{
// 个人查询:按 UserId 过滤,且排除商户记录
query = query.Where(pr => pr.UserId == userId && pr.MerchantId == 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;
}
/// <summary>
/// 积分规则说明接口 (PC用户)
/// </summary>
/// <returns></returns>
[DisplayName("积分规则说明接口")]
[HttpGet("rules")]
[AllowAnonymous]
public async Task<List<PointsRule>> GetRules()
{
// 返回所有启用的积分规则
return await _pointsRuleRepository.AsQueryable()
.Where(x => x.Status == AvailabilityStatus.Enable)
.Includes(x => x.Details) // 包含明细
.OrderBy(x => x.CreatedTime)
.ToListAsync();
}
/// <summary>
/// 积分记录分页查询
/// </summary>
[DisplayName("积分记录分页查询")]
[HttpGet("list")]
public async Task<PageResult<PointsRecordListOutput>> 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<PointsRecord>()
.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<PointsRecord>()
.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 // 单位用户
{
var query = _merchantRepository.AsQueryable()
.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<PointsRecord>()
.Where(p => p.MerchantId == 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<PointsRecord>()
.Where(p => p.MerchantId == 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;
}
}
/// <summary>
/// 积分明细查询
/// </summary>
[DisplayName("积分明细查询")]
[HttpGet("detail/{userId}")]
public async Task<PageResult<PointsDetailOutput>> Detail([FromQuery] PointsDetailQueryInput input)
{
var query = _repository.AsQueryable()
.LeftJoin<EventInfo>((p, e) => p.EventId == e.Id)
.LeftJoin<YD_Event.Core.Entities.Project>((p, e, pr) => p.ProjectId == pr.Id)
.LeftJoin<ProjectGroup>((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 // 单位用户
{
// 单位用户:查询 MerchantId = userId 的记录
query = query.Where((p, e, pr, pg) => p.MerchantId == input.UserId);
return await query
.LeftJoin<YD_Event.Core.Entities.AppUser>((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);
}
}
}