diff --git a/YD_Event.Application/Points/Dtos/UserPointsDtos.cs b/YD_Event.Application/Points/Dtos/UserPointsDtos.cs
new file mode 100644
index 0000000..0c7b435
--- /dev/null
+++ b/YD_Event.Application/Points/Dtos/UserPointsDtos.cs
@@ -0,0 +1,92 @@
+using System.ComponentModel.DataAnnotations;
+using YD_Event.Core.Enum;
+
+namespace YD_Event.Application.Points.Dtos;
+
+///
+/// 用户积分查询输入
+///
+public class UserPointsQueryInput : Pagination
+{
+ ///
+ /// 积分类型(1-获取 2-消耗,不传为全部)
+ ///
+ public PointsOperationType? Type { get; set; }
+
+ ///
+ /// 动作关键字(赛事名称/商品名称)
+ ///
+ public string? ActionKeyword { get; set; }
+
+ ///
+ /// 开始时间
+ ///
+ public DateTime? StartTime { get; set; }
+
+ ///
+ /// 结束时间
+ ///
+ public DateTime? EndTime { get; set; }
+}
+
+///
+/// 积分明细列表项
+///
+public class UserPointsLogDto
+{
+ ///
+ /// 记录ID
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// 积分类型(1-获取 2-消耗)
+ ///
+ public PointsOperationType Type { get; set; }
+
+ ///
+ /// 动作(赛事名称/消费内容)
+ ///
+ public string Action { get; set; }
+
+ ///
+ /// 项目名称
+ ///
+ public string? ProjectName { get; set; }
+
+ ///
+ /// 组别名称
+ ///
+ public string? GroupName { get; set; }
+
+ ///
+ /// 参赛人(仅单位账号有效)
+ ///
+ public string? ParticipantName { get; set; }
+
+ ///
+ /// 名次
+ ///
+ public int? Rank { get; set; }
+
+ ///
+ /// 积分变动
+ ///
+ public decimal Points { get; set; }
+
+ ///
+ /// 生成时间
+ ///
+ public DateTime CreatedTime { get; set; }
+}
+
+///
+/// 用户积分概览
+///
+public class UserPointsOverviewDto
+{
+ ///
+ /// 当前总积分
+ ///
+ public decimal CurrentBalance { get; set; }
+}
diff --git a/YD_Event.Application/Points/PointsRecordService.cs b/YD_Event.Application/Points/PointsRecordService.cs
index 426899c..1a30178 100644
--- a/YD_Event.Application/Points/PointsRecordService.cs
+++ b/YD_Event.Application/Points/PointsRecordService.cs
@@ -1,4 +1,5 @@
using SqlSugar;
+using YD_Event.Application.Auth;
using YD_Event.Application.Points.Dtos;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
@@ -13,15 +14,118 @@ public class PointsRecordService : BaseService, ITransient
private readonly ISqlSugarRepository _repository;
private readonly ISqlSugarRepository _appUserRepository;
private readonly ISqlSugarRepository _merchantRepository;
+ private readonly ISqlSugarRepository _pointsRuleRepository;
+ private readonly AuthManager _authManager;
public PointsRecordService(
ISqlSugarRepository repository,
ISqlSugarRepository appUserRepository,
- ISqlSugarRepository merchantRepository) : base(repository)
+ ISqlSugarRepository merchantRepository,
+ ISqlSugarRepository pointsRuleRepository,
+ AuthManager authManager) : base(repository)
{
_repository = repository;
_appUserRepository = appUserRepository;
_merchantRepository = merchantRepository;
+ _pointsRuleRepository = pointsRuleRepository;
+ _authManager = authManager;
+ }
+
+ ///
+ /// 获取用户当前积分余额 (PC端)
+ ///
+ ///
+ [DisplayName("获取用户当前积分余额")]
+ [HttpGet("balance")]
+ public async Task 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 };
+ }
+
+ ///
+ /// 查询积分明细列表 (PC端)
+ ///
+ ///
+ ///
+ [DisplayName("查询积分明细列表")]
+ [HttpGet("logs")]
+ public async Task> GetLogs([FromQuery] UserPointsQueryInput input)
+ {
+ var userId = _authManager.UserId;
+ var merchant = await _merchantRepository.GetFirstAsync(m => m.CreatedUserId == userId);
+
+ 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 (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;
+ }
+
+ ///
+ /// 积分规则说明接口 (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();
}
///