2026-04-04 22:09:43 +08:00

587 lines
22 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 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;
using ProjectEntity = YD_Event.Core.Entities.Project;
namespace YD_Event.Application.Client;
/// <summary>
/// 小程序-个人中心接口
/// </summary>
[ApiDescriptionSettings("微信小程序接口")]
[Authorize]
public class MyController : IDynamicApiController
{
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _appUserRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Team> _teamRepository;
private readonly ISqlSugarRepository<TeamMember> _teamMemberRepository;
private readonly ISqlSugarRepository<EventCertificate> _certificateRepository;
private readonly ISqlSugarRepository<PointsRecord> _pointsRecordRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
private readonly ISqlSugarRepository<EventRegistrationItem> _registrationItemRepository;
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
private readonly IHttpContextAccessor _httpContextAccessor;
public MyController(
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Team> teamRepository,
ISqlSugarRepository<TeamMember> teamMemberRepository,
ISqlSugarRepository<EventCertificate> certificateRepository,
ISqlSugarRepository<PointsRecord> pointsRecordRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
ISqlSugarRepository<EventRegistration> registrationRepository,
ISqlSugarRepository<EventRegistrationItem> registrationItemRepository,
ISqlSugarRepository<EventInfo> eventRepository,
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
IHttpContextAccessor httpContextAccessor)
{
_appUserRepository = appUserRepository;
_teamRepository = teamRepository;
_teamMemberRepository = teamMemberRepository;
_certificateRepository = certificateRepository;
_pointsRecordRepository = pointsRecordRepository;
_orderRepository = orderRepository;
_registrationRepository = registrationRepository;
_registrationItemRepository = registrationItemRepository;
_eventRepository = eventRepository;
_merchantCertificationRepository = merchantCertificationRepository;
_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;
}
/// <summary>
/// 获取个人主页信息
/// </summary>
/// <returns></returns>
[HttpGet("profile")]
public async Task<MyProfileDto> GetProfile()
{
var userId = GetCurrentUserId();
var user = await _appUserRepository.GetFirstAsync(u => u.Id == 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);
// 认证状态: 0-待提交, 1-已认证, 2-审核中, 3-失败
int authStatus = 0;
bool isAuthInfoSubmitted = !string.IsNullOrEmpty(user.Name) && !string.IsNullOrEmpty(user.IdCardNumber);
if (!isAuthInfoSubmitted)
{
authStatus = 0; // 未认证
}
else if (user.AuditStatus == AuditStatus.Pass)
{
authStatus = 1; // 已认证
}
else if (user.AuditStatus == AuditStatus.PendingAudit)
{
authStatus = 2; // 审核中
}
else
{
authStatus = 3; // 失败/其他
}
// 获取单位名称
string? merchantName = null;
if (user.MerchantCertificationId.HasValue)
{
var merchantCert = await _merchantCertificationRepository.GetFirstAsync(m => m.Id == user.MerchantCertificationId.Value);
merchantName = merchantCert?.MerchantName;
}
return new MyProfileDto
{
NickName = user.NickName ?? "微信用户",
Avatar = user.Avatar ?? "",
Mobile = MaskMobile(user.Mobile),
Points = pointsBalance,
CertificateCount = certCount,
AuthStatus = authStatus,
MerchantName = merchantName,
Gender = (int?)user.Gender,
BirthDate = user.BirthDate
};
}
/// <summary>
/// 获取实名认证信息
/// </summary>
/// <returns></returns>
[HttpGet("auth")]
public async Task<MyAuthInfoDto> GetAuthInfo()
{
var userId = GetCurrentUserId();
var user = await _appUserRepository.GetFirstAsync(u => u.Id == userId);
if (user == null) throw Oops.Bah("用户信息不存在");
int authType = 1; // 默认身份证认证
if (!string.IsNullOrEmpty(user.PassportPhotoUrl))
{
authType = 2;
}
return new MyAuthInfoDto
{
AuthType = authType,
Name = user.Name,
Mobile = user.Mobile,
Gender = (int?)user.Gender,
BirthDate = user.BirthDate,
IdCardNumber = user.IdCardNumber,
IdCardImageUrl = user.IdCardImageUrl,
PassportPhotoUrl = user.PassportPhotoUrl,
AuditStatus = user.AuditStatus,
AuditRemark = user.AuditRemark
};
}
/// <summary>
/// 提交实名认证
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("auth")]
public async Task<bool> SubmitAuth([FromBody] SubmitAuthInput input)
{
var userId = GetCurrentUserId();
var user = await _appUserRepository.GetFirstAsync(u => u.Id == userId);
if (user == null) throw Oops.Bah("用户信息不存在");
if (user.AuditStatus == AuditStatus.Pass) throw Oops.Bah("已通过实名认证,无需重复提交");
if (user.AuditStatus == AuditStatus.PendingAudit) throw Oops.Bah("审核中,请勿重复提交");
// 验证必填字段
if (input.AuthType == 1)
{
if (string.IsNullOrEmpty(input.IdCardNumber)) throw Oops.Bah("身份证号不能为空");
if (string.IsNullOrEmpty(input.IdCardImageUrl)) throw Oops.Bah("身份证正面图片不能为空");
}
else if (input.AuthType == 2)
{
if (string.IsNullOrEmpty(input.PassportPhotoUrl)) throw Oops.Bah("护照图片不能为空");
if (!input.Gender.HasValue) throw Oops.Bah("性别不能为空");
if (!input.BirthDate.HasValue) throw Oops.Bah("出生日期不能为空");
}
else
{
throw Oops.Bah("不支持的认证类型");
}
// 验证手机号验证码(此处仅占位,实际业务中需要验证短信验证码)
if (string.IsNullOrEmpty(input.SmsCode)) throw Oops.Bah("手机号验证码不能为空");
user.Name = input.Name;
user.Mobile = input.Mobile;
if (input.AuthType == 1)
{
user.IdCardNumber = input.IdCardNumber;
user.IdCardImageUrl = input.IdCardImageUrl;
user.PassportPhotoUrl = null; // 清空护照图片
}
else
{
user.PassportPhotoUrl = input.PassportPhotoUrl;
user.IdCardImageUrl = null; // 清空身份证图片
user.Gender = (Gender)input.Gender.Value;
user.BirthDate = input.BirthDate.Value;
}
user.AuditStatus = AuditStatus.PendingAudit;
user.UpdatedTime = DateTime.Now;
return await _appUserRepository.UpdateAsync(user);
}
/// <summary>
/// 我的团队列表
/// </summary>
/// <returns></returns>
[HttpGet("teams")]
public async Task<PageResult<MyTeamDto>> GetTeams([FromQuery] MyPageInput input)
{
var userId = GetCurrentUserId();
var myTeamIds = await _teamMemberRepository.AsQueryable()
.Where(tm => tm.UserId == userId && !tm.DeleteMark)
.Select(tm => tm.TeamId)
.ToListAsync();
var query = _teamRepository.AsQueryable()
.Where(t => (t.CreatedUserId == userId || myTeamIds.Contains(t.Id)) && !t.DeleteMark);
var teams = await query.ToPagedListAsync(input);
var teamIds = teams.Rows.Select(t => t.Id).ToList();
var teamMembers = await _teamMemberRepository.AsQueryable()
.Where(tm => teamIds.Contains(tm.TeamId) && !tm.DeleteMark)
.ToListAsync();
var memberCounts = teamMembers
.GroupBy(tm => tm.TeamId)
.ToDictionary(g => g.Key, g => g.Count());
var result = new PageResult<MyTeamDto>
{
PageNo = teams.PageNo,
PageSize = teams.PageSize,
Total = teams.Total,
Pages = teams.Pages,
Rows = teams.Rows.Select(t => new MyTeamDto
{
Id = t.Id,
Name = t.Name,
IsLeader = t.CreatedUserId == userId,
MemberCount = memberCounts.TryGetValue(t.Id, out var count) ? count : 0
}).ToList()
};
return result;
}
/// <summary>
/// 团队详情
/// </summary>
/// <param name="id">团队ID</param>
/// <returns></returns>
[HttpGet("teams/{id}")]
public async Task<MyTeamDetailDto> GetTeamDetail(long id)
{
var userId = GetCurrentUserId();
var team = await _teamRepository.GetFirstAsync(t => t.Id == id && !t.DeleteMark);
if (team == null) throw Oops.Bah("团队不存在");
var teamMembers = await _teamMemberRepository.AsQueryable()
.Where(tm => tm.TeamId == id && !tm.DeleteMark)
.ToListAsync();
if (!teamMembers.Any(tm => tm.UserId == userId) && team.CreatedUserId != userId)
{
throw Oops.Bah("无权访问该团队");
}
var userIds = teamMembers.Select(tm => tm.UserId).ToList();
var users = await _appUserRepository.AsQueryable()
.Where(u => userIds.Contains(u.Id))
.ToListAsync();
var userDict = users.ToDictionary(u => u.Id);
var members = teamMembers.Select(tm =>
{
userDict.TryGetValue(tm.UserId, out var user);
return new MyTeamMemberDto
{
Name = user?.Name ?? "未知",
Gender = (int)(user?.Gender ?? 0),
Age = user?.Age,
PersonType = (int)tm.PersonType,
Mobile = user?.Mobile != null ? MaskMobile(user.Mobile) : null
};
}).ToList();
var leader = members.FirstOrDefault(m => m.PersonType == 3);
var coach = members.FirstOrDefault(m => m.PersonType == 2);
return new MyTeamDetailDto
{
Id = team.Id,
Name = team.Name,
Leader = leader,
Coach = coach,
CreatedTime = team.CreatedTime,
MemberCount = members.Count,
Members = members
};
}
/// <summary>
/// 我的证书总览
/// </summary>
/// <returns></returns>
[HttpGet("certificates/overview")]
public async Task<MyCertificateOverviewDto> GetCertificatesOverview()
{
var userId = GetCurrentUserId();
var count = await _certificateRepository.CountAsync(c => c.CreatedUserId == userId && c.CertificateStatus == 2 && !c.DeleteMark);
return new MyCertificateOverviewDto
{
TotalCount = count
};
}
/// <summary>
/// 我的证书列表
/// </summary>
/// <returns></returns>
[HttpGet("certificates")]
public async Task<PageResult<MyCertificateDto>> GetCertificates([FromQuery] MyCertificatePageInput input)
{
var userId = GetCurrentUserId();
// 先查询原始数据,然后在内存中转换证书名称
var query = _certificateRepository.AsQueryable()
.LeftJoin<EventInfo>((c, e) => c.EventId == e.Id)
.LeftJoin<ProjectEntity>((c, e, p) => c.ProjectId == p.Id)
.Where((c, e, p) => c.CreatedUserId == userId && c.CertificateStatus == 2 && !c.DeleteMark)
.Select((c, e, p) => new
{
Id = c.Id,
EventName = e.EventName,
ProjectName = p.Name,
GroupName = c.GroupName,
CertificateType = c.CertificateType,
CertificateUrl = c.CertificateUrl,
IssuedTime = c.IssuedTime
})
.OrderByDescending(c => c.IssuedTime);
var pagedResult = await query.ToPagedListAsync(input);
// 在内存中转换证书名称
var result = new PageResult<MyCertificateDto>
{
PageNo = pagedResult.PageNo,
PageSize = pagedResult.PageSize,
Total = pagedResult.Total,
Pages = pagedResult.Pages,
Rows = pagedResult.Rows.Select(x => new MyCertificateDto
{
Id = x.Id,
EventName = x.EventName,
ProjectName = x.ProjectName,
GroupName = x.GroupName,
CertificateName = GetCertificateName(x.CertificateType),
CertificateUrl = x.CertificateUrl,
IssuedTime = x.IssuedTime
}).ToList()
};
return result;
}
private string GetCertificateName(int certificateType)
{
return certificateType switch
{
1 => "冠军证书",
2 => "亚军证书",
3 => "季军证书",
4 => "优胜证书",
5 => "参与证书",
_ => "荣誉证书"
};
}
/// <summary>
/// 我的积分总览
/// </summary>
/// <returns></returns>
[HttpGet("points/overview")]
public async Task<MyPointsOverviewDto> GetPointsOverview()
{
var userId = GetCurrentUserId();
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);
return new MyPointsOverviewDto
{
TotalPoints = pointsBalance
};
}
/// <summary>
/// 我的积分记录
/// </summary>
/// <returns></returns>
[HttpGet("points")]
public async Task<PageResult<MyPointsRecordDto>> GetPoints([FromQuery] MyPointsPageInput input)
{
var userId = GetCurrentUserId();
var query = _pointsRecordRepository.AsQueryable()
.LeftJoin<EventInfo>((p, e) => p.EventId == e.Id)
.LeftJoin<ProjectEntity>((p, e, pr) => p.ProjectId == pr.Id)
.LeftJoin<ProjectGroup>((p, e, pr, pg) => p.ProjectGroupId == pg.Id)
.Where((p, e, pr, pg) => p.UserId == userId && !p.DeleteMark);
if (input.Type.HasValue)
{
var typeFilter = (PointsOperationType)input.Type.Value;
query = query.Where((p, e, pr, pg) => p.Type == typeFilter);
}
if (input.MerchantCertificationId.HasValue)
{
query = query.Where((p, e, pr, pg) => p.MerchantCertificationId == input.MerchantCertificationId.Value);
}
var resultQuery = query.Select((p, e, pr, pg) => new MyPointsRecordDto
{
Id = p.Id,
Title = p.Remark ?? (p.Type == PointsOperationType.Acquire ? "获取积分" : "消耗积分"),
EventName = e.EventName,
ProjectName = pr.Name,
GroupName = pg.GroupName,
Points = p.Points,
Type = (int)p.Type,
CreatedTime = p.CreatedTime,
Rank = 1 // 积分记录表暂无名次字段,后续若有需要可补充
})
.OrderByDescending(p => p.CreatedTime);
return await resultQuery.ToPagedListAsync(input);
}
/// <summary>
/// 我的订单列表
/// </summary>
/// <returns></returns>
[HttpGet("orders")]
public async Task<PageResult<MyOrderDto>> GetOrders([FromQuery] MyPageInput input)
{
var userId = GetCurrentUserId();
var query = _orderRepository.AsQueryable()
.LeftJoin<EventInfo>((o, e) => o.EventId == e.Id)
.Where((o, e) => o.CreatedUserId == userId && !o.DeleteMark)
.Select((o, e) => new MyOrderDto
{
Id = o.Id,
OrderNo = o.OrderNo,
EventName = e.EventName,
ActualAmount = o.ActualAmount,
RefundAmount = o.OrderType == 2 ? o.ActualAmount : null,
OrderStatus = o.OrderStatus,
PaymentTime = o.PaymentTime,
CreatedTime = o.CreatedTime
})
.OrderByDescending(o => o.CreatedTime);
return await query.ToPagedListAsync(input);
}
/// <summary>
/// 订单详情
/// </summary>
/// <param name="id">订单ID</param>
/// <returns></returns>
[HttpGet("orders/{id}")]
public async Task<MyOrderDetailDto> GetOrderDetail(long id)
{
var userId = GetCurrentUserId();
var order = await _orderRepository.GetFirstAsync(o => o.Id == id && o.CreatedUserId == userId && !o.DeleteMark);
if (order == null) throw Oops.Bah("订单不存在");
var eventName = await _eventRepository.AsQueryable()
.Where(e => e.Id == order.EventId)
.Select(e => e.EventName)
.FirstAsync();
var items = await _registrationItemRepository.AsQueryable()
.Where(i => i.RegistrationId == order.RegistrationId && !i.DeleteMark)
.Select(i => new MyOrderItemDto
{
ProjectName = i.ProjectName,
GroupName = i.GroupName,
Fee = i.TotalPrice
})
.ToListAsync();
return new MyOrderDetailDto
{
Id = order.Id,
OrderNo = order.OrderNo,
EventName = eventName,
ActualAmount = order.ActualAmount,
RefundAmount = order.OrderType == 2 ? order.ActualAmount : null,
RefundTime = order.OrderType == 2 ? order.PaymentTime : null,
OrderStatus = order.OrderStatus,
PaymentTime = order.PaymentTime,
PaymentMethod = order.PaymentMethod,
Items = items
};
}
/// <summary>
/// 我的参赛记录(暂不对外提供)
/// </summary>
/// <returns></returns>
[ApiDescriptionSettings(false)]
[NonAction]
public async Task<PageResult<MyRegistrationDto>> GetRegistrations([FromQuery] MyPageInput input)
{
var userId = GetCurrentUserId();
var query = _registrationRepository.AsQueryable()
.LeftJoin<EventInfo>((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 = ""
})
.OrderByDescending(r => r.RegistrationTime);
var result = await query.ToPagedListAsync(input);
if (result.Rows.Any())
{
var registrationIds = result.Rows.Select(r => r.Id).ToList();
var items = await _registrationItemRepository.AsQueryable()
.Where(i => registrationIds.Contains(i.RegistrationId) && !i.DeleteMark)
.ToListAsync();
var itemsByRegistrationId = items.GroupBy(i => i.RegistrationId)
.ToDictionary(g => g.Key, g => string.Join("、", g.Select(i => i.ProjectName)));
foreach (var row in result.Rows)
{
if (itemsByRegistrationId.TryGetValue(row.Id, out var projectNames))
{
row.ProjectNames = projectNames;
}
}
}
return result;
}
private string MaskMobile(string mobile)
{
if (string.IsNullOrEmpty(mobile) || mobile.Length < 7) return mobile;
return mobile.Substring(0, 3) + "****" + mobile.Substring(7);
}
}