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; /// /// 普通用户服务 /// public class AppUserService : BaseService, ITransient { private readonly ISqlSugarRepository _appUserRep; private readonly ISqlSugarClient _db; private readonly AuthManager _authManager; private readonly IEasyCachingProvider _cachingProvider; public AppUserService(ISqlSugarRepository appUserRep, ISqlSugarClient db, AuthManager authManager, IEasyCachingProvider cachingProvider) : base(appUserRep) { _appUserRep = appUserRep; _db = db; _authManager = authManager; _cachingProvider = cachingProvider; } /// /// 分页查询普通用户 /// /// /// [DisplayName("分页查询普通用户")] [HttpGet("page")] public async Task> 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().ToPagedListAsync(input); } /// /// 获取普通用户详情 /// /// /// [DisplayName("获取普通用户详情")] [HttpGet("{id}")] public async Task Detail([FromRoute] long id) { return await _appUserRep.AsQueryable() .Where(u => u.Id == id) .Select() .FirstAsync(); } /// /// 审核普通用户 /// /// /// [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 统一成员管理 (领队/教练/运动员) /// /// 统一分页查询成员 /// /// /// [HttpGet("member/page")] [DisplayName("统一分页查询成员")] public async Task> 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>(); // 设置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; } /// /// 统一新增/编辑成员 /// /// /// [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 = null; bool isUnitTransfer = false; bool isNewRecord = false; long? originalMerchantId = null; if (existingInOtherUnit != null) { // 单位更换流程 isUnitTransfer = true; entity = existingInOtherUnit; originalMerchantId = entity.MerchantCertificationId; } else { // 检查是否存在相同手机号但还未加入任何单位的记录(小程序注册的用户) var existingWithoutUnit = await _appUserRep.AsQueryable() .Where(x => x.Mobile == input.Mobile && x.PersonType == input.PersonType && x.MerchantCertificationId == null && !x.DeleteMark) .FirstAsync(); if (existingWithoutUnit != null) { // 找到已存在但未加入任何单位的记录,复用该记录 entity = existingWithoutUnit; entity.UpdatedUserId = _authManager.UserId; entity.UpdatedTime = DateTime.Now; } else { // 没有找到任何记录,标记为新增 isNewRecord = true; } } 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 if (isNewRecord) { // 新增模式:检查本单位内是否有相同手机号且相同类型的记录 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(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); } } /// /// 处理单位更换时的团队成员关系 /// /// 用户ID /// 原单位ID /// 新单位ID /// private async Task HandleTeamTransfer(long userId, long oldMerchantId, long newMerchantId) { // 从原单位的所有团队中移除该成员 var oldTeamIds = await _db.Queryable() .Where(t => t.MerchantCertificationId == oldMerchantId && !t.DeleteMark) .Select(t => t.Id) .ToListAsync(); if (oldTeamIds.Any()) { await _db.Updateable() .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() .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("无权操作该成员"); } } /// /// 删除成员(解除与单位的关联,而非删除用户) /// /// /// [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() .Where(t => t.MerchantCertificationId == originalMerchantId.Value && !t.DeleteMark) .Select(t => t.Id) .ToListAsync(); if (teamIds.Any()) { // 从所有相关团队中移除该成员 await _db.Updateable() .SetColumns(tm => new TeamMember { DeleteMark = true, UpdatedTime = DateTime.Now, UpdatedUserId = _authManager.UserId }) .Where(tm => tm.UserId == id && teamIds.Contains(tm.TeamId) && !tm.DeleteMark) .ExecuteCommandAsync(); } } } /// /// 批量删除成员(解除与单位的关联,而非删除用户) /// /// /// [HttpPost("member/batchDelete")] [DisplayName("批量删除成员")] [UnitOfWork] public async Task BatchDeleteMember([FromBody] List 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() .Where(t => t.MerchantCertificationId == merchantCertificationId.Value && !t.DeleteMark) .Select(t => t.Id) .ToListAsync(); if (teamIds.Any()) { // 从所有相关团队中移除这些成员 await _db.Updateable() .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(); } } } /// /// 变更成员状态 /// /// /// [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 }