2026-04-30 09:28:50 +08:00

482 lines
18 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 YD_Event.Application.AppUser.Dtos;
using YD_Event.Application.Auth;
using YD_Event.Core.Entities;
using AppUserEntity = YD_Event.Core.Entities.AppUser;
using YD_Event.Core.Enum;
using Mapster;
using EasyCaching.Core;
namespace YD_Event.Application.AppUser;
/// <summary>
/// 普通用户服务
/// </summary>
public class AppUserService : BaseService<AppUserEntity>, ITransient
{
private readonly ISqlSugarRepository<AppUserEntity> _appUserRep;
private readonly ISqlSugarClient _db;
private readonly AuthManager _authManager;
private readonly IEasyCachingProvider _cachingProvider;
public AppUserService(ISqlSugarRepository<AppUserEntity> appUserRep,
ISqlSugarClient db,
AuthManager authManager,
IEasyCachingProvider cachingProvider) : base(appUserRep)
{
_appUserRep = appUserRep;
_db = db;
_authManager = authManager;
_cachingProvider = cachingProvider;
}
/// <summary>
/// 分页查询普通用户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询普通用户")]
[HttpGet("page")]
public async Task<PageResult<AppUserOutput>> Page([FromQuery] AppUserPageInput input)
{
var query = _appUserRep.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name))
.WhereIF(input.Gender.HasValue, u => u.Gender == input.Gender)
.WhereIF(!string.IsNullOrWhiteSpace(input.Mobile), u => u.Mobile.Contains(input.Mobile))
.WhereIF(input.AuditStatus.HasValue, u => u.AuditStatus == input.AuditStatus)
.WhereIF(input.StartTime.HasValue, u => u.CreatedTime >= input.StartTime)
.WhereIF(input.EndTime.HasValue, u => u.CreatedTime <= input.EndTime)
.OrderByDescending(u => u.CreatedTime);
return await query.Select<AppUserOutput>().ToPagedListAsync(input);
}
/// <summary>
/// 获取普通用户详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[DisplayName("获取普通用户详情")]
[HttpGet("{id}")]
public async Task<AppUserOutput> Detail([FromRoute] long id)
{
return await _appUserRep.AsQueryable()
.Where(u => u.Id == id)
.Select<AppUserOutput>()
.FirstAsync();
}
/// <summary>
/// 审核普通用户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("审核普通用户")]
[HttpPost("audit")]
[UnitOfWork]
public async Task Audit([FromBody] AuditInput input)
{
if (input.Ids == null || !input.Ids.Any()) return;
// 更新状态
await _appUserRep.UpdateAsync(u => new AppUserEntity
{
AuditStatus = input.Status,
AuditRemark = input.Remark,
UpdatedTime = DateTime.Now,
UpdatedUserId = _authManager.UserId
}, u => input.Ids.Contains(u.Id));
}
#region 统一成员管理 (领队/教练/运动员)
/// <summary>
/// 统一分页查询成员
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("member/page")]
[DisplayName("统一分页查询成员")]
public async Task<PageResult<MemberOutput>> MemberPage([FromQuery] MemberQueryInput input)
{
var (userId, merchantCertificationId) = await GetCurrentUserAndMerchantCertificationId();
var query = _appUserRep.AsQueryable()
.Where(x => x.PersonType == input.PersonType && !x.DeleteMark)
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), x => x.Name.Contains(input.Name))
.WhereIF(input.Gender.HasValue, x => x.Gender == input.Gender)
.WhereIF(!string.IsNullOrWhiteSpace(input.Mobile), x => x.Mobile.Contains(input.Mobile))
.WhereIF(!string.IsNullOrWhiteSpace(input.IdCardNumber), u => u.IdCardNumber.Contains(input.IdCardNumber))
.WhereIF(input.PersonType == PersonType.Coach && !string.IsNullOrWhiteSpace(input.SkillLevel), x => x.SkillLevel == input.SkillLevel)
.WhereIF(input.PersonType == PersonType.Coach && input.CertificationStatus.HasValue, x => x.CertificationStatus == input.CertificationStatus)
.WhereIF(input.StartTime.HasValue, u => u.CreatedTime >= input.StartTime)
.WhereIF(input.EndTime.HasValue, u => u.CreatedTime <= input.EndTime)
.OrderByDescending(x => x.CreatedTime);
// 权限过滤 (管理员可看全部,这里简化的商户隔离逻辑)
if (merchantCertificationId.HasValue)
{
query = query.Where(x => x.MerchantCertificationId == merchantCertificationId.Value);
}
else
{
// 这里根据实际业务,如果普通用户只能看自己创建的,需要这个过滤
query = query.Where(x => x.CreatedUserId == userId && x.MerchantCertificationId == null);
}
var list = await query.ToPagedListAsync(input);
var result = list.Adapt<PageResult<MemberOutput>>();
// 设置Age字段:优先使用数据库中的Age,如果没有则根据BirthDate计算
foreach (var item in result.Rows)
{
if (item.Age.HasValue)
{
// 数据库中已有Age,直接使用
continue;
}
// 数据库中没有Age,根据BirthDate动态计算
if (item.BirthDate.HasValue)
{
item.Age = MemberOutput.CalculateAgeFromBirthDate(item.BirthDate);
}
}
return result;
}
/// <summary>
/// 统一新增/编辑成员
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("member/save")]
[DisplayName("统一新增/编辑成员")]
[UnitOfWork]
public async Task SaveUnifiedMember(CreateOrUpdateMemberInput input)
{
var (userId, merchantCertificationId) = await GetCurrentUserAndMerchantCertificationId();
// 检查是否有跨单位操作权限
if (!merchantCertificationId.HasValue)
{
throw Oops.Bah("当前用户未关联单位,无法进行操作");
}
// 先检查是否存在相同手机号且相同类型的运动员在其他单位
var existingInOtherUnit = await _appUserRep.AsQueryable()
.Where(x => x.Mobile == input.Mobile
&& x.PersonType == input.PersonType
&& x.MerchantCertificationId != merchantCertificationId
&& x.MerchantCertificationId != null
&& !x.DeleteMark)
.FirstAsync();
AppUserEntity entity;
bool isUnitTransfer = false;
long? originalMerchantId = null;
if (existingInOtherUnit != null)
{
// 单位更换流程
isUnitTransfer = true;
entity = existingInOtherUnit;
originalMerchantId = entity.MerchantCertificationId;
}
if (input.Id.HasValue)
{
// 编辑模式
entity = await _appUserRep.GetByIdAsync(input.Id.Value);
if (entity == null || entity.DeleteMark) throw Oops.Bah("成员不存在");
// 检查手机号是否被本单位内同类型的其他记录占用
var duplicateInSameUnit = await _appUserRep.AsQueryable()
.Where(x => x.Mobile == input.Mobile
&& x.PersonType == input.PersonType
&& x.MerchantCertificationId == merchantCertificationId
&& !x.DeleteMark
&& x.Id != input.Id.Value)
.FirstAsync();
if (duplicateInSameUnit != null)
{
throw Oops.Bah($"该手机号在本单位已存在相同类型的成员");
}
entity.UpdatedUserId = _authManager.UserId;
entity.UpdatedTime = DateTime.Now;
}
else
{
// 新增模式:检查本单位内是否有相同手机号且相同类型的记录
var existingInSameUnit = await _appUserRep.AsQueryable()
.Where(x => x.Mobile == input.Mobile
&& x.PersonType == input.PersonType
&& x.MerchantCertificationId == merchantCertificationId
&& !x.DeleteMark)
.FirstAsync();
if (existingInSameUnit != null)
{
// 本单位内已有相同手机号且相同类型的记录,不允许重复添加
throw Oops.Bah($"该手机号在本单位已存在相同类型的成员");
}
// 新增记录
entity = new AppUserEntity
{
Id = YitIdHelper.NextId(),
PersonType = input.PersonType,
MerchantCertificationId = merchantCertificationId,
CreatedUserId = userId,
CreatedTime = DateTime.Now,
Status = AvailabilityStatus.Enable,
AuditStatus = AuditStatus.PendingSubmission
};
// 仅在真正新增运动员时验证验证码
if (input.PersonType == PersonType.Athlete)
{
if (string.IsNullOrWhiteSpace(input.VerificationCode))
{
throw Oops.Bah("验证码不能为空");
}
var cacheKey = $"sms:code:{input.Mobile}";
var cachedCodeResult = _cachingProvider.Get<string>(cacheKey);
string? cachedCode = cachedCodeResult.Value;
if (!cachedCodeResult.HasValue || cachedCode != input.VerificationCode)
{
throw Oops.Bah("验证码错误或已过期");
}
}
if (isUnitTransfer)
{
existingInOtherUnit.DeleteMark = true;
await _appUserRep.UpdateAsync(existingInOtherUnit);
}
}
// 更新用户信息字段
entity.Name = input.Name;
entity.Gender = input.Gender;
entity.IdCardNumber = input.IdCardNumber;
entity.IdCardImageUrl = input.IdCardImageUrl;
entity.PassportPhotoUrl = input.PassportPhotoUrl;
entity.Mobile = input.Mobile;
entity.Remark = input.Remark;
entity.AuditRemark = isUnitTransfer ? "单位更换" : "";
entity.BirthDate = input.BirthDate;
entity.Age = input.Age;
// 更新认证单位信息
var oldMerchantId = entity.MerchantCertificationId;
if (entity.MerchantCertificationId != merchantCertificationId)
{
entity.MerchantCertificationId = merchantCertificationId;
entity.UpdatedUserId = _authManager.UserId;
entity.UpdatedTime = DateTime.Now;
}
if (input.PersonType == PersonType.Coach)
{
entity.SkillLevel = input.SkillLevel;
entity.CertificationStatus = input.CertificationStatus;
}
// 保存用户信息
await _appUserRep.InsertOrUpdateAsync(entity);
// 如果是单位更换,处理团队关系
if (isUnitTransfer && oldMerchantId.HasValue)
{
await HandleTeamTransfer(entity.Id, oldMerchantId.Value, merchantCertificationId.Value);
}
}
/// <summary>
/// 处理单位更换时的团队成员关系
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="oldMerchantId">原单位ID</param>
/// <param name="newMerchantId">新单位ID</param>
/// <returns></returns>
private async Task HandleTeamTransfer(long userId, long oldMerchantId, long newMerchantId)
{
// 从原单位的所有团队中移除该成员
var oldTeamIds = await _db.Queryable<YD_Event.Core.Entities.Team>()
.Where(t => t.MerchantCertificationId == oldMerchantId && !t.DeleteMark)
.Select(t => t.Id)
.ToListAsync();
if (oldTeamIds.Any())
{
await _db.Updateable<TeamMember>()
.SetColumns(tm => new TeamMember
{
DeleteMark = true,
UpdatedTime = DateTime.Now,
UpdatedUserId = _authManager.UserId
})
.Where(tm => tm.UserId == userId
&& oldTeamIds.Contains(tm.TeamId)
&& !tm.DeleteMark)
.ExecuteCommandAsync();
}
}
#endregion
#region 领队/教练管理 (向下兼容)
private async Task<(long userId, long? merchantCertificationId)> GetCurrentUserAndMerchantCertificationId()
{
var userId = _authManager.UserId;
// 直接查询单位认证表,检查是否有审核通过的认证记录
var certification = await _db.Queryable<MerchantCertification>()
.FirstAsync(c => c.UserId == userId && !c.DeleteMark);
// 有审核通过的认证就返回认证ID,否则返回null
return (userId, certification?.Id);
}
private async Task CheckPermission(AppUserEntity entity)
{
var (userId, merchantCertificationId) = await GetCurrentUserAndMerchantCertificationId();
if (merchantCertificationId.HasValue)
{
if (entity.MerchantCertificationId != merchantCertificationId.Value) throw Oops.Bah("无权操作该成员");
}
else
{
if (entity.CreatedUserId != userId || entity.MerchantCertificationId != null) throw Oops.Bah("无权操作该成员");
}
}
/// <summary>
/// 删除成员(解除与单位的关联,而非删除用户)
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[DisplayName("删除成员")]
[HttpPost("member/delete")]
[UnitOfWork]
public async Task DeleteMember([FromBody] long id)
{
var entity = await _appUserRep.GetByIdAsync(id);
if (entity == null || entity.DeleteMark) throw Oops.Bah("成员不存在");
await CheckPermission(entity);
// 1. 先保存原来的 MerchantCertificationId 用于后续处理
var originalMerchantId = entity.MerchantCertificationId;
// 2. 解除用户与单位的关联
entity.MerchantCertificationId = null;
entity.UpdatedUserId = _authManager.UserId;
entity.UpdatedTime = DateTime.Now;
await _appUserRep.UpdateAsync(entity);
// 3. 如果原来有关联单位,需要从该单位的所有团队中移除该成员
if (originalMerchantId.HasValue)
{
// 查找该单位的所有团队
var teamIds = await _db.Queryable<YD_Event.Core.Entities.Team>()
.Where(t => t.MerchantCertificationId == originalMerchantId.Value && !t.DeleteMark)
.Select(t => t.Id)
.ToListAsync();
if (teamIds.Any())
{
// 从所有相关团队中移除该成员
await _db.Updateable<TeamMember>()
.SetColumns(tm => new TeamMember
{
DeleteMark = true,
UpdatedTime = DateTime.Now,
UpdatedUserId = _authManager.UserId
})
.Where(tm => tm.UserId == id
&& teamIds.Contains(tm.TeamId)
&& !tm.DeleteMark)
.ExecuteCommandAsync();
}
}
}
/// <summary>
/// 批量删除成员(解除与单位的关联,而非删除用户)
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[HttpPost("member/batchDelete")]
[DisplayName("批量删除成员")]
[UnitOfWork]
public async Task BatchDeleteMember([FromBody] List<long> ids)
{
if (ids == null || !ids.Any()) throw Oops.Bah("请选择要删除的记录");
// 获取当前用户的单位ID
var (_, merchantCertificationId) = await GetCurrentUserAndMerchantCertificationId();
// 1. 解除所有选中用户与当前单位的关联
await _appUserRep.UpdateAsync(x => new AppUserEntity
{
MerchantCertificationId = null,
UpdatedUserId = _authManager.UserId,
UpdatedTime = DateTime.Now
}, x => ids.Contains(x.Id));
// 2. 如果有单位ID,从该单位的所有团队中移除这些成员
if (merchantCertificationId.HasValue)
{
// 查找该单位的所有团队
var teamIds = await _db.Queryable<YD_Event.Core.Entities.Team>()
.Where(t => t.MerchantCertificationId == merchantCertificationId.Value && !t.DeleteMark)
.Select(t => t.Id)
.ToListAsync();
if (teamIds.Any())
{
// 从所有相关团队中移除这些成员
await _db.Updateable<TeamMember>()
.SetColumns(tm => new TeamMember
{
DeleteMark = true,
UpdatedTime = DateTime.Now,
UpdatedUserId = _authManager.UserId
})
.Where(tm => ids.Contains(tm.UserId)
&& teamIds.Contains(tm.TeamId)
&& !tm.DeleteMark)
.ExecuteCommandAsync();
}
}
}
/// <summary>
/// 变更成员状态
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("变更成员状态")]
[HttpPost("member/status")]
public async Task ChangeMemberStatus(ChangeMemberStatusInput input)
{
var entity = await _appUserRep.GetByIdAsync(input.Id);
if (entity == null || entity.DeleteMark) throw Oops.Bah("成员不存在");
await CheckPermission(entity);
entity.Status = input.Status;
entity.UpdatedUserId = _authManager.UserId;
entity.UpdatedTime = DateTime.Now;
await _appUserRep.UpdateAsync(entity);
}
#endregion
}