using Fake.Application; using Fake.Domain.Exceptions; using Fake.Rbac.Application.Dtos.Common; using Fake.Rbac.Application.Dtos.Role; using Fake.Rbac.Application.Dtos.User; using Fake.Rbac.Application.Services; using Fake.Rbac.Domain.Managers; using Fake.Rbac.Infrastructure; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Fake.ObjectStorage; using Oms.Application.Dtos.Rbac; using Oms.Application.Rbac; using Oms.Application.Shared; using Oms.Application.Storage; using Oms.Domain.SystemAggregate.Entities; using Oms.Domain.SystemAggregate.Enums; using Oms.Domain.SystemAggregate.Repositories; namespace Oms.Application.Services.Rbac; /// /// 接管 RBAC 用户接口,扩展电话、工号、微信号、微信二维码字段。 /// [Authorize] [ApiExplorerSettings(GroupName = "RBAC-User")] public class OmsUserService( UserService userService, AccountManager accountManager, UserProfileRepository profileRepository, IEmployeeRepository employeeRepository, FakeRbacDbContext rbacDbContext, IObjectStorage fileStorage) : ApplicationService { /// /// 获取用户详情(含扩展资料)。 /// [HttpGet("/rbac/user/get")] public async Task GetAsync([FromQuery] Guid id, CancellationToken cancellationToken = default) { var user = await userService.GetAsync(id, cancellationToken); var profile = await profileRepository.GetAsync(id, cancellationToken); return MapUserDetail(user, profile); } /// /// 分页查询用户列表(含扩展资料)。 /// [HttpGet("/rbac/user/get-list")] public async Task> GetListAsync( [FromQuery] UserPagedRequestDto input, CancellationToken cancellationToken = default) { var result = await userService.GetListAsync(input, cancellationToken); if (result.Items.Count == 0) { return new PagedResultDto { Total = result.Total, Items = [], }; } var userIds = result.Items .Select(x => x.Id) .Where(x => x != Guid.Empty) .Distinct() .ToList(); var profileMap = await profileRepository.GetManyAsync(userIds, cancellationToken); return new PagedResultDto { Total = result.Total, Items = result.Items .Select(user => { profileMap.TryGetValue(user.Id, out var profile); return MapUserDetail(user, profile); }) .ToList(), }; } /// /// 按角色查询用户简要列表。 /// [HttpGet("/rbac/user/get-users-by-role")] public Task> GetUsersByRoleAsync( [FromQuery] Guid roleId, CancellationToken cancellationToken = default) => userService.GetUsersByRoleAsync(roleId, cancellationToken); /// /// 创建用户并保存扩展资料。 /// [HttpPost("/rbac/user/create")] public async Task CreateAsync( [FromBody] OmsUserCreateInput input, CancellationToken cancellationToken = default) { var account = input.Account?.Trim() ?? string.Empty; OmsRbacRegistrationGuard.ValidateAdminUserFields( input.Name, account, input.Password, input.Email, input.Phone, input.WechatId, requirePassword: true); var employeeNo = await ResolveEmployeeNoForCreateAsync(input.EmployeeNo, cancellationToken); await OmsRbacRegistrationGuard.EnsureLoginIdentityAvailableAsync( rbacDbContext, account, input.Email, forAdminUserCreate: true, excludeUserId: null, cancellationToken); await OmsRbacRegistrationGuard.EnsurePhoneAvailableAsync( rbacDbContext, input.Phone, excludeUserId: null, cancellationToken); await OmsRbacRegistrationGuard.EnsureWechatIdAvailableAsync( rbacDbContext, input.WechatId, excludeUserId: null, cancellationToken); await OmsRbacRegistrationGuard.EnsureOrganizationExistsAsync( rbacDbContext, input.OrganizationId, cancellationToken); await OmsRbacRegistrationGuard.EnsureRolesExistAsync( rbacDbContext, input.RoleIds, cancellationToken); try { var user = await userService.CreateAsync(new UserCreateDto { Name = input.Name!.Trim(), Account = account, Password = input.Password, // 禁止 Email='':uk_user_email 会把空串当成占用 Email = string.IsNullOrWhiteSpace(input.Email) ? null : input.Email.Trim(), Avatar = OmsObjectStorageExtensions.ToStorageKey(input.Avatar), OrganizationId = input.OrganizationId, RoleIds = input.RoleIds, }, cancellationToken); await profileRepository.SaveAsync( user.Id, new UserProfileData { Phone = input.Phone, EmployeeNo = employeeNo, WechatId = input.WechatId, WechatQrCode = input.WechatQrCode, WecomKfUrl = input.WecomKfUrl, }, cancellationToken); await EnsureEmployeeProfileAsync(user.Id, input, cancellationToken); var profile = await profileRepository.GetAsync(user.Id, cancellationToken); return MapUserDetail(user, profile); } catch (Exception ex) when (ex is not BusinessException) { throw OmsRbacRegistrationGuard.ToSaveBusinessException(ex, forAdminUserCreate: true); } } /// /// 更新用户及扩展资料。 /// [HttpPut("/rbac/user/update")] public async Task UpdateAsync( [FromQuery] Guid id, [FromBody] OmsUserUpdateInput input, CancellationToken cancellationToken = default) { if (id == Guid.Empty) { throw new BusinessException("用户 Id 无效"); } // 更新不改登录账号/密码 OmsRbacRegistrationGuard.ValidateAdminUserFields( input.Name, account: "placeholder", password: null, input.Email, input.Phone, input.WechatId, requirePassword: false); var email = string.IsNullOrWhiteSpace(input.Email) ? null : input.Email.Trim(); await OmsRbacRegistrationGuard.EnsureEmailAvailableAsync( rbacDbContext, email, excludeUserId: id, forAdminUserCreate: true, cancellationToken); await OmsRbacRegistrationGuard.EnsurePhoneAvailableAsync( rbacDbContext, input.Phone, excludeUserId: id, cancellationToken); await OmsRbacRegistrationGuard.EnsureWechatIdAvailableAsync( rbacDbContext, input.WechatId, excludeUserId: id, cancellationToken); await OmsRbacRegistrationGuard.EnsureOrganizationExistsAsync( rbacDbContext, input.OrganizationId, cancellationToken); try { var user = await userService.UpdateAsync( id, new UserUpdateDto { Name = input.Name!.Trim(), Email = email, Avatar = OmsObjectStorageExtensions.ToStorageKey(input.Avatar), OrganizationId = input.OrganizationId, }, cancellationToken); await profileRepository.UpdateContactAsync( id, new UserProfileData { Phone = input.Phone, WechatId = input.WechatId, WechatQrCode = input.WechatQrCode, WecomKfUrl = input.WecomKfUrl, }, cancellationToken); var profile = await profileRepository.GetAsync(id, cancellationToken); return MapUserDetail(user, profile); } catch (Exception ex) when (ex is not BusinessException) { throw OmsRbacRegistrationGuard.ToSaveBusinessException(ex, forAdminUserCreate: true); } } /// /// 修改用户密码。 /// [HttpPut("/rbac/user/update-password")] public Task UpdatePasswordAsync( [FromQuery] Guid id, [FromBody] UpdatePasswordDto input, CancellationToken cancellationToken = default) => userService.UpdatePasswordAsync(id, input, cancellationToken); /// /// 管理员重置用户密码(无需旧密码)。 /// [HttpPost("/rbac/user/reset-password")] public async Task ResetPasswordAsync( [FromQuery] Guid id, [FromBody] ResetUserPasswordInput input, CancellationToken cancellationToken = default) { if (id == Guid.Empty) { throw new BusinessException("用户 Id 无效"); } var newPassword = input?.NewPassword?.Trim(); if (string.IsNullOrWhiteSpace(newPassword)) { throw new BusinessException("新密码不能为空"); } if (newPassword.Length < 6) { throw new BusinessException("密码长度不能少于6位"); } await accountManager.ResetPasswordAsync(id, newPassword, cancellationToken); } /// /// 更新用户头像。 /// [HttpPut("/rbac/user/update-avatar")] public Task UpdateAvatarAsync( [FromQuery] Guid id, [FromQuery] string avatarUrl, CancellationToken cancellationToken = default) => userService.UpdateAvatarAsync(id, OmsObjectStorageExtensions.ToStorageKey(avatarUrl) ?? avatarUrl, cancellationToken); /// /// 删除用户。 /// [HttpDelete("/rbac/user/delete")] public Task DeleteAsync([FromQuery] Guid id, CancellationToken cancellationToken = default) => userService.DeleteAsync(id, cancellationToken); /// /// 批量删除用户。 /// [HttpDelete("/rbac/user/delete-batch")] public Task DeleteBatchAsync([FromQuery] List ids, CancellationToken cancellationToken = default) => userService.DeleteBatchAsync(ids, cancellationToken); /// /// 为用户分配角色。 /// [HttpPost("/rbac/user/assign-roles")] public Task AssignRolesAsync( [FromQuery] Guid userId, [FromBody] List roleIds, CancellationToken cancellationToken = default) => userService.AssignRolesAsync(userId, roleIds, cancellationToken); /// /// 获取用户已分配角色。 /// [HttpGet("/rbac/user/get-user-roles")] public Task> GetUserRolesAsync( [FromQuery] Guid userId, CancellationToken cancellationToken = default) => userService.GetUserRolesAsync(userId, cancellationToken); /// /// 获取用户权限码列表。 /// [HttpGet("/rbac/user/get-user-permissions")] public Task> GetUserPermissionsAsync( [FromQuery] Guid userId, CancellationToken cancellationToken = default) => userService.GetUserPermissionsAsync(userId, cancellationToken); private async Task ResolveEmployeeNoForCreateAsync( string? requestedEmployeeNo, CancellationToken cancellationToken) { var employeeNo = string.IsNullOrWhiteSpace(requestedEmployeeNo) ? await profileRepository.GenerateNextEmployeeNoAsync(cancellationToken) : requestedEmployeeNo.Trim().ToUpperInvariant(); if (!UserProfileRepository.IsValidEmployeeNo(employeeNo)) { throw new BusinessException($"工号「{employeeNo}」格式无效,应为 IDE + 至少 3 位数字,如 IDE001"); } if (await profileRepository.IsEmployeeNoTakenAsync(employeeNo, null, cancellationToken)) { throw new BusinessException($"工号「{employeeNo}」已存在,请更换工号后再创建"); } return employeeNo; } private async Task EnsureEmployeeProfileAsync( Guid userId, OmsUserCreateInput input, CancellationToken cancellationToken) { var userIdText = userId.ToString(); if (await employeeRepository.ExistsByUserIdAsync(userIdText, null, cancellationToken)) { return; } await employeeRepository.InsertAsync( new Employee { UserId = userIdText, RealName = string.IsNullOrWhiteSpace(input.Name) ? string.Empty : input.Name.Trim(), HireDate = DateTime.Today, EmploymentStatus = EmploymentStatus.Probation, EmploymentType = EmploymentType.FullTime, }, cancellationToken); } private OmsUserDetailDto MapUserDetail(UserDto user, UserProfileData? profile) { var detail = OmsUserProfileMapper.ToDetailDto(user, profile); detail.Avatar = fileStorage.GetPublicUrl(detail.Avatar); return detail; } }