using Microsoft.AspNetCore.Mvc; using YD_Event.Application.Client.Dtos; using YD_Event.Core.Entities; using YD_Event.Core.Enum; using SqlSugar; using Furion.DatabaseAccessor; using Microsoft.AspNetCore.Authorization; using System.Security.Claims; using Furion.FriendlyException; namespace YD_Event.Application.Client; /// /// 小程序-个人中心接口 /// [ApiDescriptionSettings("微信小程序接口")] [Route("api/v1/mini-program/my")] [Authorize(AuthenticationSchemes = "Bearer")] public class MyController : IDynamicApiController { private readonly ISqlSugarRepository _appUserRepository; private readonly ISqlSugarRepository _teamRepository; private readonly ISqlSugarRepository _certificateRepository; private readonly ISqlSugarRepository _pointsRecordRepository; private readonly ISqlSugarRepository _orderRepository; private readonly ISqlSugarRepository _registrationRepository; private readonly ISqlSugarRepository _eventRepository; private readonly IHttpContextAccessor _httpContextAccessor; public MyController( ISqlSugarRepository appUserRepository, ISqlSugarRepository teamRepository, ISqlSugarRepository certificateRepository, ISqlSugarRepository pointsRecordRepository, ISqlSugarRepository orderRepository, ISqlSugarRepository registrationRepository, ISqlSugarRepository eventRepository, IHttpContextAccessor httpContextAccessor) { _appUserRepository = appUserRepository; _teamRepository = teamRepository; _certificateRepository = certificateRepository; _pointsRecordRepository = pointsRecordRepository; _orderRepository = orderRepository; _registrationRepository = registrationRepository; _eventRepository = eventRepository; _httpContextAccessor = httpContextAccessor; } private long GetCurrentUserId() { var userIdStr = _httpContextAccessor.HttpContext?.User?.FindFirst(AuthClaimsConst.AuthIdKey)?.Value; if (string.IsNullOrEmpty(userIdStr) || !long.TryParse(userIdStr, out long userId)) { throw Oops.Bah("未登录或登录已失效"); } return userId; } /// /// 获取个人主页信息 /// /// [HttpGet("profile")] public async Task GetProfile() { var userId = GetCurrentUserId(); // 注意:AuthAccount.Id 和 AppUser.UserId 关联。这里假设登录的是 AuthAccount,需要查 AppUser // 实际上 AppUser 表的 UserId 字段关联的是 AuthAccount 的 Id。 // 但 AppUser 本身也有 Id。 // 业务逻辑通常是:一个 AuthAccount 对应一个 AppUser。 var user = await _appUserRepository.GetFirstAsync(u => u.UserId == userId); if (user == null) throw Oops.Bah("用户信息不存在"); // 统计证书数量 var certCount = await _certificateRepository.CountAsync(c => c.CreatedUserId == userId && c.CertificateStatus == 2); // 积分 (需计算积分总和,PointsRecord 表有 Points 字段,Type 1-加 2-减) // 简单计算:Sum(Type=1) - Sum(Type=2) var pointsIn = await _pointsRecordRepository.AsQueryable().Where(p => p.UserId == userId && p.Type == PointsOperationType.Acquire).SumAsync(p => p.Points); var pointsOut = await _pointsRecordRepository.AsQueryable().Where(p => p.UserId == userId && p.Type == PointsOperationType.Consume).SumAsync(p => p.Points); var pointsBalance = (int)(pointsIn - pointsOut); // 认证状态: AuditStatus: 0-待提交, 1-待审核, 2-通过, 3-拒绝. int authStatus = 0; if (string.IsNullOrEmpty(user.Name) || string.IsNullOrEmpty(user.IdCardNumber)) { authStatus = 0; // 未认证 } else if (user.AuditStatus == AuditStatus.Pass) { authStatus = 1; // 已认证 } else if (user.AuditStatus == AuditStatus.PendingAudit) { authStatus = 2; // 审核中 } else { authStatus = 3; // 失败/其他 } return new MyProfileDto { NickName = user.NickName ?? "微信用户", Avatar = user.Avatar ?? "", Mobile = MaskMobile(user.Mobile), Points = pointsBalance, CertificateCount = certCount, AuthStatus = authStatus }; } /// /// 获取实名认证信息 /// /// [HttpGet("auth")] public async Task GetAuthInfo() { var userId = GetCurrentUserId(); var user = await _appUserRepository.GetFirstAsync(u => u.UserId == userId); if (user == null) throw Oops.Bah("用户信息不存在"); return new MyAuthInfoDto { Name = user.Name, Mobile = user.Mobile, IdCardNumber = user.IdCardNumber, // 可以在前端脱敏 IdCardImageUrl = user.IdCardImageUrl, AuditStatus = user.AuditStatus, AuditRemark = user.AuditRemark }; } /// /// 提交实名认证 /// /// /// [HttpPost("auth")] public async Task SubmitAuth([FromBody] SubmitAuthInput input) { var userId = GetCurrentUserId(); var user = await _appUserRepository.GetFirstAsync(u => u.UserId == userId); if (user == null) throw Oops.Bah("用户信息不存在"); if (user.AuditStatus == AuditStatus.Pass) throw Oops.Bah("已通过实名认证,无需重复提交"); if (user.AuditStatus == AuditStatus.PendingAudit) throw Oops.Bah("审核中,请勿重复提交"); user.Name = input.Name; user.Mobile = input.Mobile; user.IdCardNumber = input.IdCardNumber; user.IdCardImageUrl = input.IdCardImageUrl; user.AuditStatus = AuditStatus.PendingAudit; // 提交后变更为待审核 user.UpdatedTime = DateTime.Now; return await _appUserRepository.UpdateAsync(user); } /// /// 我的团队列表 /// /// [HttpGet("teams")] public async Task> GetTeams([FromQuery] MyPageInput input) { var userId = GetCurrentUserId(); // 逻辑:查找我是创建人(队长) 或 队员的团队 // 目前 Team 表只有 CreatedUserId,假设 CreatedUserId 就是队长。 // 如果有 TeamMember 表关联队员,需要联表。目前假设只查我创建的团队(根据原型图"我的团队"通常指我管理的)。 // 如果原型包含"我加入的",需要查 TeamMember。 // 鉴于 TeamMember 实体未直接给出,这里先查 CreatedUserId = userId var query = _teamRepository.AsQueryable() .Where(t => t.CreatedUserId == userId && !t.DeleteMark) .Select(t => new MyTeamDto { Id = t.Id, Name = t.Name, EstablishedDate = t.EstablishedDate, IsLeader = true, MemberCount = 0 // 暂无 Member 表关联,置为 0 或需扩展 }); return await query.ToPagedListAsync(input); } /// /// 我的证书列表 /// /// [HttpGet("certificates")] public async Task> GetCertificates([FromQuery] MyPageInput input) { var userId = GetCurrentUserId(); // 联表查询 EventInfo 获取赛事名称 var query = _certificateRepository.AsQueryable() .LeftJoin((c, e) => c.EventId == e.Id) .Where(c => c.CreatedUserId == userId && c.CertificateStatus == 2 && !c.DeleteMark) .Select((c, e) => new MyCertificateDto { Id = c.Id, EventName = e.EventName, ProjectName = c.GroupName, // 暂时用 GroupName 代替 ProjectName,具体看 ProjectId 关联 CertificateName = "荣誉证书", // 此时可根据 CertificateType 转义 CertificateUrl = c.CertificateUrl, IssuedTime = c.IssuedTime }) .OrderByDescending(c => c.IssuedTime); return await query.ToPagedListAsync(input); } /// /// 我的积分记录 /// /// [HttpGet("points")] public async Task> GetPoints([FromQuery] MyPageInput input) { var userId = GetCurrentUserId(); var query = _pointsRecordRepository.AsQueryable() .Where(p => p.UserId == userId && !p.DeleteMark) .Select(p => new MyPointsRecordDto { Id = p.Id, Title = p.Remark ?? (p.Type == PointsOperationType.Acquire ? "获取积分" : "消耗积分"), Points = p.Points, Type = (int)p.Type, CreatedTime = p.CreatedTime }) .OrderByDescending(p => p.CreatedTime); return await query.ToPagedListAsync(input); } /// /// 我的订单列表 /// /// [HttpGet("orders")] public async Task> GetOrders([FromQuery] MyPageInput input) { var userId = GetCurrentUserId(); var query = _orderRepository.AsQueryable() .LeftJoin((o, e) => o.EventId == e.Id) .Where(o => o.CreatedUserId == userId && !o.DeleteMark) .Select((o, e) => new MyOrderDto { Id = o.Id, OrderNo = o.OrderNo, EventName = e.EventName, Amount = o.ActualAmount, Status = (int)o.OrderStatus, CreatedTime = o.CreatedTime }) .OrderByDescending(o => o.CreatedTime); return await query.ToPagedListAsync(input); } /// /// 我的参赛记录 /// /// [HttpGet("registrations")] public async Task> GetRegistrations([FromQuery] MyPageInput input) { var userId = GetCurrentUserId(); var query = _registrationRepository.AsQueryable() .LeftJoin((r, e) => r.EventId == e.Id) .Where(r => r.CreatedUserId == userId && !r.DeleteMark) .Select((r, e) => new MyRegistrationDto { Id = r.Id, EventName = e.EventName, TeamName = r.TeamName, Status = r.RegistrationStatus, RegistrationTime = r.RegistrationTime, ProjectNames = "" // 暂空,需联表 EventRegistrationItem }) .OrderByDescending(r => r.RegistrationTime); return await query.ToPagedListAsync(input); } private string MaskMobile(string mobile) { if (string.IsNullOrEmpty(mobile) || mobile.Length < 7) return mobile; return mobile.Substring(0, 3) + "****" + mobile.Substring(7); } }