版本260406

This commit is contained in:
2026-04-06 22:04:05 +08:00
parent 7dc5e73af7
commit 0b150470be
216 changed files with 98993 additions and 33 deletions

View File

@@ -0,0 +1,176 @@
namespace OrpaonVision.Core.Security.Contracts.Commands;
/// <summary>
/// 创建用户命令。
/// </summary>
public sealed class CreateUserCommand
{
/// <summary>
/// 用户名。
/// </summary>
public string UserName { get; init; } = string.Empty;
/// <summary>
/// 显示名称。
/// </summary>
public string DisplayName { get; init; } = string.Empty;
/// <summary>
/// 邮箱。
/// </summary>
public string Email { get; init; } = string.Empty;
/// <summary>
/// 密码。
/// </summary>
public string Password { get; init; } = string.Empty;
/// <summary>
/// 角色列表。
/// </summary>
public IReadOnlyList<string> Roles { get; init; } = [];
/// <summary>
/// 权限列表。
/// </summary>
public IReadOnlyList<string> Permissions { get; init; } = [];
/// <summary>
/// 是否激活。
/// </summary>
public bool IsActive { get; init; } = true;
/// <summary>
/// 备注。
/// </summary>
public string Remarks { get; init; } = string.Empty;
/// <summary>
/// 创建者。
/// </summary>
public string CreatedBy { get; init; } = string.Empty;
}
/// <summary>
/// 更新用户命令。
/// </summary>
public sealed class UpdateUserCommand
{
/// <summary>
/// 用户ID。
/// </summary>
public Guid UserId { get; init; }
/// <summary>
/// 显示名称。
/// </summary>
public string DisplayName { get; init; } = string.Empty;
/// <summary>
/// 邮箱。
/// </summary>
public string Email { get; init; } = string.Empty;
/// <summary>
/// 角色列表。
/// </summary>
public IReadOnlyList<string> Roles { get; init; } = [];
/// <summary>
/// 权限列表。
/// </summary>
public IReadOnlyList<string> Permissions { get; init; } = [];
/// <summary>
/// 是否激活。
/// </summary>
public bool IsActive { get; init; }
/// <summary>
/// 备注。
/// </summary>
public string Remarks { get; init; } = string.Empty;
/// <summary>
/// 更新者。
/// </summary>
public string UpdatedBy { get; init; } = string.Empty;
}
/// <summary>
/// 删除用户命令。
/// </summary>
public sealed class DeleteUserCommand
{
/// <summary>
/// 用户ID。
/// </summary>
public Guid UserId { get; init; }
/// <summary>
/// 删除原因。
/// </summary>
public string DeleteReason { get; init; } = string.Empty;
/// <summary>
/// 删除者。
/// </summary>
public string DeletedBy { get; init; } = string.Empty;
}
/// <summary>
/// 重置用户密码命令。
/// </summary>
public sealed class ResetUserPasswordCommand
{
/// <summary>
/// 用户ID。
/// </summary>
public Guid UserId { get; init; }
/// <summary>
/// 新密码。
/// </summary>
public string NewPassword { get; init; } = string.Empty;
/// <summary>
/// 是否下次登录必须修改密码。
/// </summary>
public bool ForceChangeOnNextLogin { get; init; } = true;
/// <summary>
/// 重置原因。
/// </summary>
public string ResetReason { get; init; } = string.Empty;
/// <summary>
/// 重置者。
/// </summary>
public string ResetBy { get; init; } = string.Empty;
}
/// <summary>
/// 启用/禁用用户命令。
/// </summary>
public sealed class ToggleUserStatusCommand
{
/// <summary>
/// 用户ID。
/// </summary>
public Guid UserId { get; init; }
/// <summary>
/// 是否激活。
/// </summary>
public bool IsActive { get; init; }
/// <summary>
/// 原因。
/// </summary>
public string Reason { get; init; } = string.Empty;
/// <summary>
/// 操作者。
/// </summary>
public string Operator { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,67 @@
namespace OrpaonVision.Core.Security.Contracts.Queries;
/// <summary>
/// 用户查询条件。
/// </summary>
public sealed class UserQueryDto
{
/// <summary>
/// 关键字搜索(用户名、显示名称、邮箱)。
/// </summary>
public string? Keyword { get; init; }
/// <summary>
/// 用户状态。
/// </summary>
public string? Status { get; init; }
/// <summary>
/// 角色过滤。
/// </summary>
public string? Role { get; init; }
/// <summary>
/// 创建时间范围开始UTC
/// </summary>
public DateTime? CreatedAtUtcStart { get; init; }
/// <summary>
/// 创建时间范围结束UTC
/// </summary>
public DateTime? CreatedAtUtcEnd { get; init; }
/// <summary>
/// 最后登录时间范围开始UTC
/// </summary>
public DateTime? LastLoginAtUtcStart { get; init; }
/// <summary>
/// 最后登录时间范围结束UTC
/// </summary>
public DateTime? LastLoginAtUtcEnd { get; init; }
/// <summary>
/// 是否激活。
/// </summary>
public bool? IsActive { get; init; }
/// <summary>
/// 页码从1开始
/// </summary>
public int PageIndex { get; init; } = 1;
/// <summary>
/// 每页大小。
/// </summary>
public int PageSize { get; init; } = 20;
/// <summary>
/// 排序字段。
/// </summary>
public string SortField { get; init; } = "UserName";
/// <summary>
/// 排序方向ASC/DESC
/// </summary>
public string SortDirection { get; init; } = "ASC";
}

View File

@@ -0,0 +1,67 @@
namespace OrpaonVision.Core.Security.Contracts;
/// <summary>
/// 用户详情。
/// </summary>
public sealed class UserDetailDto
{
/// <summary>
/// 用户ID。
/// </summary>
public Guid UserId { get; init; }
/// <summary>
/// 用户名。
/// </summary>
public string UserName { get; init; } = string.Empty;
/// <summary>
/// 显示名称。
/// </summary>
public string DisplayName { get; init; } = string.Empty;
/// <summary>
/// 邮箱。
/// </summary>
public string Email { get; init; } = string.Empty;
/// <summary>
/// 角色列表。
/// </summary>
public IReadOnlyList<string> Roles { get; init; } = [];
/// <summary>
/// 权限列表。
/// </summary>
public IReadOnlyList<string> Permissions { get; init; } = [];
/// <summary>
/// 用户状态。
/// </summary>
public string Status { get; init; } = string.Empty;
/// <summary>
/// 最后登录时间UTC
/// </summary>
public DateTime? LastLoginAtUtc { get; init; }
/// <summary>
/// 创建时间UTC
/// </summary>
public DateTime CreatedAtUtc { get; init; }
/// <summary>
/// 创建者。
/// </summary>
public string CreatedBy { get; init; } = string.Empty;
/// <summary>
/// 是否激活。
/// </summary>
public bool IsActive { get; init; }
/// <summary>
/// 备注。
/// </summary>
public string Remarks { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,198 @@
using OrpaonVision.Core.Results;
using OrpaonVision.Model.Security;
using OrpaonVision.Model.Configuration;
namespace OrpaonVision.Core.Security;
/// <summary>
/// 机种权限服务接口。
/// </summary>
public interface IProductPermissionService
{
/// <summary>
/// 检查用户是否具有指定机种的权限。
/// </summary>
/// <param name="userId">用户ID。</param>
/// <param name="productTypeId">机种ID。</param>
/// <param name="permissionType">权限类型。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>权限检查结果。</returns>
Task<Result<bool>> CheckPermissionAsync(Guid userId, Guid productTypeId, ProductPermissionType permissionType, CancellationToken cancellationToken = default);
/// <summary>
/// 检查用户是否具有指定机种编码的权限。
/// </summary>
/// <param name="userId">用户ID。</param>
/// <param name="productTypeCode">机种编码。</param>
/// <param name="permissionType">权限类型。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>权限检查结果。</returns>
Task<Result<bool>> CheckPermissionByCodeAsync(Guid userId, string productTypeCode, ProductPermissionType permissionType, CancellationToken cancellationToken = default);
/// <summary>
/// 获取用户在指定机种的权限列表。
/// </summary>
/// <param name="userId">用户ID。</param>
/// <param name="productTypeId">机种ID。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>权限列表。</returns>
Task<Result<IReadOnlyList<ProductPermissionModel>>> GetUserPermissionsAsync(Guid userId, Guid productTypeId, CancellationToken cancellationToken = default);
/// <summary>
/// 授予用户机种权限。
/// </summary>
/// <param name="userId">用户ID。</param>
/// <param name="productTypeId">机种ID。</param>
/// <param name="permissionType">权限类型。</param>
/// <param name="expiresAtUtc">过期时间(可选)。</param>
/// <param name="remark">备注。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>授权结果。</returns>
Task<Result<ProductPermissionModel>> GrantPermissionAsync(Guid userId, Guid productTypeId, ProductPermissionType permissionType, DateTime? expiresAtUtc = null, string? remark = null, CancellationToken cancellationToken = default);
/// <summary>
/// 撤销用户机种权限。
/// </summary>
/// <param name="permissionId">权限ID。</param>
/// <param name="reason">撤销原因。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>撤销结果。</returns>
Task<Result> RevokePermissionAsync(Guid permissionId, string reason, CancellationToken cancellationToken = default);
/// <summary>
/// 检查机种是否被锁定。
/// </summary>
/// <param name="productTypeId">机种ID。</param>
/// <param name="lockType">锁定类型。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>锁定检查结果。</returns>
Task<Result<bool>> IsProductLockedAsync(Guid productTypeId, ProductLockType lockType, CancellationToken cancellationToken = default);
/// <summary>
/// 锁定机种。
/// </summary>
/// <param name="productTypeId">机种ID。</param>
/// <param name="lockType">锁定类型。</param>
/// <param name="lockReason">锁定原因。</param>
/// <param name="expectedUnlockAtUtc">预计解锁时间(可选)。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>锁定结果。</returns>
Task<Result<ProductLockRecordModel>> LockProductAsync(Guid productTypeId, ProductLockType lockType, string lockReason, DateTime? expectedUnlockAtUtc = null, CancellationToken cancellationToken = default);
/// <summary>
/// 解锁机种。
/// </summary>
/// <param name="lockRecordId">锁定记录ID。</param>
/// <param name="unlockReason">解锁原因。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>解锁结果。</returns>
Task<Result> UnlockProductAsync(Guid lockRecordId, string unlockReason, CancellationToken cancellationToken = default);
/// <summary>
/// 验证机种切换权限。
/// </summary>
/// <param name="userId">用户ID。</param>
/// <param name="sourceProductTypeId">源机种ID。</param>
/// <param name="targetProductTypeId">目标机种ID。</param>
/// <param name="isForced">是否强制切换。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>验证结果。</returns>
Task<Result<ProductSwitchValidationResult>> ValidateSwitchPermissionAsync(Guid userId, Guid? sourceProductTypeId, Guid targetProductTypeId, bool isForced = false, CancellationToken cancellationToken = default);
/// <summary>
/// 记录机种切换。
/// </summary>
/// <param name="userId">用户ID。</param>
/// <param name="userName">用户姓名。</param>
/// <param name="userRole">用户角色。</param>
/// <param name="sourceProductTypeId">源机种ID。</param>
/// <param name="targetProductTypeId">目标机种ID。</param>
/// <param name="switchType">切换类型。</param>
/// <param name="switchReason">切换原因。</param>
/// <param name="isForced">是否强制切换。</param>
/// <param name="forcedReason">强制切换原因。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>记录结果。</returns>
Task<Result<ProductSwitchRecordModel>> RecordSwitchAsync(Guid userId, string userName, string userRole, Guid? sourceProductTypeId, Guid targetProductTypeId, ProductSwitchType switchType, string switchReason, bool isForced = false, string? forcedReason = null, CancellationToken cancellationToken = default);
/// <summary>
/// 获取机种切换历史记录。
/// </summary>
/// <param name="productTypeId">机种ID可选。</param>
/// <param name="userId">用户ID可选。</param>
/// <param name="startTime">开始时间。</param>
/// <param name="endTime">结束时间。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>切换记录列表。</returns>
Task<Result<IReadOnlyList<ProductSwitchRecordModel>>> GetSwitchHistoryAsync(Guid? productTypeId = null, Guid? userId = null, DateTime? startTime = null, DateTime? endTime = null, CancellationToken cancellationToken = default);
/// <summary>
/// 获取机种锁定记录。
/// </summary>
/// <param name="productTypeId">机种ID。</param>
/// <param name="includeUnlocked">是否包含已解锁的记录。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>锁定记录列表。</returns>
Task<Result<IReadOnlyList<ProductLockRecordModel>>> GetLockRecordsAsync(Guid productTypeId, bool includeUnlocked = false, CancellationToken cancellationToken = default);
}
/// <summary>
/// 机种切换验证结果。
/// </summary>
public sealed class ProductSwitchValidationResult
{
/// <summary>
/// 是否允许切换。
/// </summary>
public bool CanSwitch { get; init; }
/// <summary>
/// 验证消息。
/// </summary>
public string Message { get; init; } = string.Empty;
/// <summary>
/// 警告信息。
/// </summary>
public List<string> Warnings { get; init; } = new();
/// <summary>
/// 错误信息。
/// </summary>
public List<string> Errors { get; init; } = new();
/// <summary>
/// 需要审批。
/// </summary>
public bool RequiresApproval { get; init; }
/// <summary>
/// 审批人列表。
/// </summary>
public List<string> Approvers { get; init; } = new();
/// <summary>
/// 源机种信息。
/// </summary>
public ProductTypeModel? SourceProduct { get; init; }
/// <summary>
/// 目标机种信息。
/// </summary>
public ProductTypeModel? TargetProduct { get; init; }
/// <summary>
/// 当前用户权限。
/// </summary>
public List<ProductPermissionType> CurrentPermissions { get; init; } = new();
/// <summary>
/// 所需权限。
/// </summary>
public List<ProductPermissionType> RequiredPermissions { get; init; } = new();
/// <summary>
/// 验证时间UTC
/// </summary>
public DateTime ValidatedAtUtc { get; init; } = DateTime.UtcNow;
}

View File

@@ -0,0 +1,303 @@
using OrpaonVision.Core.Results;
using OrpaonVision.Model.Security;
namespace OrpaonVision.Core.Security;
/// <summary>
/// 用户仓储接口。
/// </summary>
public interface IUserStore
{
/// <summary>
/// 根据用户ID获取用户。
/// </summary>
Task<Result<UserModel?>> GetByIdAsync(Guid id);
/// <summary>
/// 根据用户名获取用户。
/// </summary>
Task<Result<UserModel?>> GetByUsernameAsync(string username);
/// <summary>
/// 创建用户。
/// </summary>
Task<Result<UserModel>> CreateAsync(UserModel user);
/// <summary>
/// 更新用户。
/// </summary>
Task<Result<UserModel>> UpdateAsync(UserModel user);
/// <summary>
/// 删除用户。
/// </summary>
Task<Result> DeleteAsync(Guid id);
/// <summary>
/// 检查用户名是否存在。
/// </summary>
Task<Result<bool>> UsernameExistsAsync(string username);
/// <summary>
/// 获取用户列表(分页)。
/// </summary>
Task<Result<(IReadOnlyList<UserModel> users, int totalCount)>> GetPagedListAsync(
int pageIndex,
int pageSize,
string? keyword = null,
UserStatus? status = null);
/// <summary>
/// 更新最后登录信息。
/// </summary>
Task<Result> UpdateLastLoginAsync(Guid userId, string ipAddress);
}
/// <summary>
/// 角色仓储接口。
/// </summary>
public interface IRoleStore
{
/// <summary>
/// 根据角色ID获取角色。
/// </summary>
Task<Result<RoleModel?>> GetByIdAsync(Guid id);
/// <summary>
/// 根据角色名称获取角色。
/// </summary>
Task<Result<RoleModel?>> GetByNameAsync(string name);
/// <summary>
/// 创建角色。
/// </summary>
Task<Result<RoleModel>> CreateAsync(RoleModel role);
/// <summary>
/// 更新角色。
/// </summary>
Task<Result<RoleModel>> UpdateAsync(RoleModel role);
/// <summary>
/// 删除角色。
/// </summary>
Task<Result> DeleteAsync(Guid id);
/// <summary>
/// 检查角色名称是否存在。
/// </summary>
Task<Result<bool>> NameExistsAsync(string name);
/// <summary>
/// 获取角色列表(分页)。
/// </summary>
Task<Result<(IReadOnlyList<RoleModel> roles, int totalCount)>> GetPagedListAsync(
int pageIndex,
int pageSize,
string? keyword = null,
RoleStatus? status = null);
/// <summary>
/// 获取所有启用的角色。
/// </summary>
Task<Result<IReadOnlyList<RoleModel>>> GetAllEnabledAsync();
}
/// <summary>
/// 权限仓储接口。
/// </summary>
public interface IPermissionStore
{
/// <summary>
/// 根据权限ID获取权限。
/// </summary>
Task<Result<PermissionModel?>> GetByIdAsync(Guid id);
/// <summary>
/// 根据权限编码获取权限。
/// </summary>
Task<Result<PermissionModel?>> GetByCodeAsync(string code);
/// <summary>
/// 创建权限。
/// </summary>
Task<Result<PermissionModel>> CreateAsync(PermissionModel permission);
/// <summary>
/// 更新权限。
/// </summary>
Task<Result<PermissionModel>> UpdateAsync(PermissionModel permission);
/// <summary>
/// 删除权限。
/// </summary>
Task<Result> DeleteAsync(Guid id);
/// <summary>
/// 检查权限编码是否存在。
/// </summary>
Task<Result<bool>> CodeExistsAsync(string code);
/// <summary>
/// 获取权限列表(分页)。
/// </summary>
Task<Result<(IReadOnlyList<PermissionModel> permissions, int totalCount)>> GetPagedListAsync(
int pageIndex,
int pageSize,
string? keyword = null,
PermissionType? type = null,
PermissionStatus? status = null);
/// <summary>
/// 根据模块获取权限列表。
/// </summary>
Task<Result<IReadOnlyList<PermissionModel>>> GetByModuleAsync(string module);
/// <summary>
/// 获取所有启用的权限。
/// </summary>
Task<Result<IReadOnlyList<PermissionModel>>> GetAllEnabledAsync();
}
/// <summary>
/// 用户角色仓储接口。
/// </summary>
public interface IUserRoleStore
{
/// <summary>
/// 分配角色给用户。
/// </summary>
Task<Result<UserRoleModel>> AssignAsync(Guid userId, Guid roleId, string grantedBy);
/// <summary>
/// 取消用户角色分配。
/// </summary>
Task<Result> UnassignAsync(Guid userId, Guid roleId);
/// <summary>
/// 获取用户的角色列表。
/// </summary>
Task<Result<IReadOnlyList<RoleModel>>> GetUserRolesAsync(Guid userId);
/// <summary>
/// 获取角色的用户列表。
/// </summary>
Task<Result<IReadOnlyList<UserModel>>> GetRoleUsersAsync(Guid roleId);
/// <summary>
/// 检查用户是否拥有指定角色。
/// </summary>
Task<Result<bool>> UserHasRoleAsync(Guid userId, Guid roleId);
/// <summary>
/// 检查用户是否拥有指定角色名称。
/// </summary>
Task<Result<bool>> UserHasRoleAsync(Guid userId, string roleName);
/// <summary>
/// 获取用户角色关联记录。
/// </summary>
Task<Result<UserRoleModel?>> GetAssignmentAsync(Guid userId, Guid roleId);
/// <summary>
/// 更新用户角色关联状态。
/// </summary>
Task<Result<UserRoleModel>> UpdateAssignmentAsync(UserRoleModel userRole);
/// <summary>
/// 获取用户角色关联列表(分页)。
/// </summary>
Task<Result<(IReadOnlyList<UserRoleModel> assignments, int totalCount)>> GetPagedListAsync(
int pageIndex,
int pageSize,
Guid? userId = null,
Guid? roleId = null);
}
/// <summary>
/// 角色权限仓储接口。
/// </summary>
public interface IRolePermissionStore
{
/// <summary>
/// 分配权限给角色。
/// </summary>
Task<Result<RolePermissionModel>> AssignAsync(Guid roleId, Guid permissionId, string grantedBy);
/// <summary>
/// 取消角色权限分配。
/// </summary>
Task<Result> UnassignAsync(Guid roleId, Guid permissionId);
/// <summary>
/// 获取角色的权限列表。
/// </summary>
Task<Result<IReadOnlyList<PermissionModel>>> GetRolePermissionsAsync(Guid roleId);
/// <summary>
/// 获取权限的角色列表。
/// </summary>
Task<Result<IReadOnlyList<RoleModel>>> GetPermissionRolesAsync(Guid permissionId);
/// <summary>
/// 检查角色是否拥有指定权限。
/// </summary>
Task<Result<bool>> RoleHasPermissionAsync(Guid roleId, Guid permissionId);
/// <summary>
/// 检查角色是否拥有指定权限编码。
/// </summary>
Task<Result<bool>> RoleHasPermissionAsync(Guid roleId, string permissionCode);
/// <summary>
/// 获取角色权限关联记录。
/// </summary>
Task<Result<RolePermissionModel?>> GetAssignmentAsync(Guid roleId, Guid permissionId);
/// <summary>
/// 更新角色权限关联状态。
/// </summary>
Task<Result<RolePermissionModel>> UpdateAssignmentAsync(RolePermissionModel rolePermission);
/// <summary>
/// 获取角色权限关联列表(分页)。
/// </summary>
Task<Result<(IReadOnlyList<RolePermissionModel> assignments, int totalCount)>> GetPagedListAsync(
int pageIndex,
int pageSize,
Guid? roleId = null,
Guid? permissionId = null);
}
/// <summary>
/// 操作日志仓储接口。
/// </summary>
public interface IOperationLogStore
{
/// <summary>
/// 记录操作日志。
/// </summary>
Result<OperationLogModel> LogAsync(OperationLogModel log);
/// <summary>
/// 获取操作日志列表(分页)。
/// </summary>
Result<(IReadOnlyList<OperationLogModel> logs, int totalCount)> GetPagedListAsync(
int pageIndex,
int pageSize,
Guid? userId = null,
OperationType? operationType = null,
string? module = null,
DateTime? startTime = null,
DateTime? endTime = null);
/// <summary>
/// 获取用户最近的操作日志。
/// </summary>
Result<IReadOnlyList<OperationLogModel>> GetUserRecentLogsAsync(Guid userId, int count = 10);
/// <summary>
/// 清理过期日志。
/// </summary>
Result<int> CleanupExpiredLogsAsync(DateTime expireBefore);
}

View File

@@ -0,0 +1,67 @@
using OrpaonVision.Core.Results;
using OrpaonVision.Core.Security.Contracts;
using OrpaonVision.Core.Security.Contracts.Queries;
namespace OrpaonVision.Core.Security;
/// <summary>
/// 用户应用服务接口。
/// </summary>
public interface IUserAppService
{
/// <summary>
/// 创建用户。
/// </summary>
/// <param name="command">创建用户命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>用户ID。</returns>
Task<Result<Guid>> CreateAsync(Contracts.Commands.CreateUserCommand command, CancellationToken cancellationToken = default);
/// <summary>
/// 更新用户。
/// </summary>
/// <param name="command">更新用户命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>操作结果。</returns>
Task<Result> UpdateAsync(Contracts.Commands.UpdateUserCommand command, CancellationToken cancellationToken = default);
/// <summary>
/// 删除用户。
/// </summary>
/// <param name="command">删除用户命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>操作结果。</returns>
Task<Result> DeleteAsync(Contracts.Commands.DeleteUserCommand command, CancellationToken cancellationToken = default);
/// <summary>
/// 获取用户详情。
/// </summary>
/// <param name="userId">用户ID。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>用户详情。</returns>
Task<Result<UserDetailDto>> GetDetailAsync(Guid userId, CancellationToken cancellationToken = default);
/// <summary>
/// 分页查询用户列表。
/// </summary>
/// <param name="query">查询条件。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>分页结果。</returns>
Task<Result<PagedResult<UserDetailDto>>> GetPagedListAsync(UserQueryDto query, CancellationToken cancellationToken = default);
/// <summary>
/// 重置用户密码。
/// </summary>
/// <param name="command">重置密码命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>操作结果。</returns>
Task<Result> ResetPasswordAsync(Contracts.Commands.ResetUserPasswordCommand command, CancellationToken cancellationToken = default);
/// <summary>
/// 启用/禁用用户。
/// </summary>
/// <param name="command">启用/禁用用户命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>操作结果。</returns>
Task<Result> ToggleUserStatusAsync(Contracts.Commands.ToggleUserStatusCommand command, CancellationToken cancellationToken = default);
}