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;
}
}