feat: 用户管理后端与日志库迁移
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
using TakeoutSaaS.Shared.Abstractions.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Identity.Entities;
|
||||
@@ -22,6 +23,41 @@ public sealed class IdentityUser : MultiTenantEntityBase
|
||||
/// </summary>
|
||||
public string PasswordHash { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 手机号(租户内唯一)。
|
||||
/// </summary>
|
||||
public string? Phone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮箱(租户内唯一)。
|
||||
/// </summary>
|
||||
public string? Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账号状态。
|
||||
/// </summary>
|
||||
public IdentityUserStatus Status { get; set; } = IdentityUserStatus.Active;
|
||||
|
||||
/// <summary>
|
||||
/// 登录失败次数。
|
||||
/// </summary>
|
||||
public int FailedLoginCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 锁定截止时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? LockedUntil { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最近登录时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? LastLoginAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否强制修改密码。
|
||||
/// </summary>
|
||||
public bool MustChangePassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属商户(平台管理员为空)。
|
||||
/// </summary>
|
||||
@@ -31,4 +67,9 @@ public sealed class IdentityUser : MultiTenantEntityBase
|
||||
/// 头像地址。
|
||||
/// </summary>
|
||||
public string? Avatar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 并发控制字段。
|
||||
/// </summary>
|
||||
public byte[] RowVersion { get; set; } = Array.Empty<byte>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TakeoutSaaS.Domain.Identity.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 后台账号状态。
|
||||
/// </summary>
|
||||
public enum IdentityUserStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 正常启用。
|
||||
/// </summary>
|
||||
Active = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 已禁用。
|
||||
/// </summary>
|
||||
Disabled = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 已锁定。
|
||||
/// </summary>
|
||||
Locked = 3
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Enums;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Identity.Repositories;
|
||||
|
||||
@@ -23,6 +24,48 @@ public interface IIdentityUserRepository
|
||||
/// <returns>存在返回 true。</returns>
|
||||
Task<bool> ExistsByAccountAsync(string account, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 判断账号是否存在(租户内,可排除指定用户)。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="account">账号。</param>
|
||||
/// <param name="excludeUserId">排除的用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>存在返回 true。</returns>
|
||||
Task<bool> ExistsByAccountAsync(
|
||||
long tenantId,
|
||||
string account,
|
||||
long? excludeUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 判断手机号是否存在(租户内,可排除指定用户)。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="phone">手机号。</param>
|
||||
/// <param name="excludeUserId">排除的用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>存在返回 true。</returns>
|
||||
Task<bool> ExistsByPhoneAsync(
|
||||
long tenantId,
|
||||
string phone,
|
||||
long? excludeUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 判断邮箱是否存在(租户内,可排除指定用户)。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="email">邮箱。</param>
|
||||
/// <param name="excludeUserId">排除的用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>存在返回 true。</returns>
|
||||
Task<bool> ExistsByEmailAsync(
|
||||
long tenantId,
|
||||
string email,
|
||||
long? excludeUserId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取后台用户。
|
||||
/// </summary>
|
||||
@@ -31,6 +74,14 @@ public interface IIdentityUserRepository
|
||||
/// <returns>后台用户或 null。</returns>
|
||||
Task<IdentityUser?> FindByIdAsync(long userId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取后台用户(忽略租户过滤器,仅用于只读查询)。
|
||||
/// </summary>
|
||||
/// <param name="userId">用户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户或 null。</returns>
|
||||
Task<IdentityUser?> FindByIdIgnoringTenantAsync(long userId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取后台用户(用于更新,返回可跟踪实体)。
|
||||
/// </summary>
|
||||
@@ -48,6 +99,20 @@ public interface IIdentityUserRepository
|
||||
/// <returns>后台用户或 null。</returns>
|
||||
Task<IdentityUser?> GetForUpdateIgnoringTenantAsync(long userId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取后台用户(用于更新,包含已删除数据)。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="userId">用户 ID。</param>
|
||||
/// <param name="ignoreTenantFilter">是否忽略租户过滤。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户或 null。</returns>
|
||||
Task<IdentityUser?> GetForUpdateIncludingDeletedAsync(
|
||||
long tenantId,
|
||||
long userId,
|
||||
bool ignoreTenantFilter = false,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 按租户与关键字查询后台用户列表(仅读)。
|
||||
/// </summary>
|
||||
@@ -57,6 +122,18 @@ public interface IIdentityUserRepository
|
||||
/// <returns>后台用户列表。</returns>
|
||||
Task<IReadOnlyList<IdentityUser>> SearchAsync(long tenantId, string? keyword, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询后台用户列表。
|
||||
/// </summary>
|
||||
/// <param name="filter">查询过滤条件。</param>
|
||||
/// <param name="ignoreTenantFilter">是否忽略租户过滤器。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>分页结果。</returns>
|
||||
Task<(IReadOnlyList<IdentityUser> Items, int Total)> SearchPagedAsync(
|
||||
IdentityUserSearchFilter filter,
|
||||
bool ignoreTenantFilter = false,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定租户、用户集合对应的用户(只读)。
|
||||
/// </summary>
|
||||
@@ -66,6 +143,22 @@ public interface IIdentityUserRepository
|
||||
/// <returns>后台用户列表。</returns>
|
||||
Task<IReadOnlyList<IdentityUser>> GetByIdsAsync(long tenantId, IEnumerable<long> userIds, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 批量获取后台用户(可用于更新,支持包含已删除数据)。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="userIds">用户 ID 集合。</param>
|
||||
/// <param name="includeDeleted">是否包含已删除数据。</param>
|
||||
/// <param name="ignoreTenantFilter">是否忽略租户过滤器。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>后台用户列表。</returns>
|
||||
Task<IReadOnlyList<IdentityUser>> GetForUpdateByIdsAsync(
|
||||
long tenantId,
|
||||
IEnumerable<long> userIds,
|
||||
bool includeDeleted,
|
||||
bool ignoreTenantFilter = false,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 新增后台用户。
|
||||
/// </summary>
|
||||
@@ -74,6 +167,14 @@ public interface IIdentityUserRepository
|
||||
/// <returns>异步操作任务。</returns>
|
||||
Task AddAsync(IdentityUser user, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 删除后台用户(软删除)。
|
||||
/// </summary>
|
||||
/// <param name="user">后台用户实体。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>异步操作任务。</returns>
|
||||
Task RemoveAsync(IdentityUser user, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 持久化仓储变更。
|
||||
/// </summary>
|
||||
@@ -81,3 +182,74 @@ public interface IIdentityUserRepository
|
||||
/// <returns>异步操作任务。</returns>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后台用户查询过滤条件。
|
||||
/// </summary>
|
||||
public sealed record IdentityUserSearchFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// </summary>
|
||||
public long? TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 关键字(账号/姓名/手机号/邮箱)。
|
||||
/// </summary>
|
||||
public string? Keyword { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户状态。
|
||||
/// </summary>
|
||||
public IdentityUserStatus? Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 角色 ID。
|
||||
/// </summary>
|
||||
public long? RoleId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建开始时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? CreatedAtFrom { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建结束时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? CreatedAtTo { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 最近登录开始时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? LastLoginFrom { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 最近登录结束时间(UTC)。
|
||||
/// </summary>
|
||||
public DateTime? LastLoginTo { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含已删除数据。
|
||||
/// </summary>
|
||||
public bool IncludeDeleted { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 页码(从 1 开始)。
|
||||
/// </summary>
|
||||
public int Page { get; init; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 每页条数。
|
||||
/// </summary>
|
||||
public int PageSize { get; init; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// 排序字段。
|
||||
/// </summary>
|
||||
public string? SortBy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否降序。
|
||||
/// </summary>
|
||||
public bool SortDescending { get; init; } = true;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,15 @@ public interface IUserRoleRepository
|
||||
/// <returns>异步操作任务。</returns>
|
||||
Task ReplaceUserRolesAsync(long tenantId, long userId, IEnumerable<long> roleIds, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 统计指定角色下的用户数量。
|
||||
/// </summary>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="roleId">角色 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>用户数量。</returns>
|
||||
Task<int> CountUsersByRoleAsync(long tenantId, long roleId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 提交持久化变更。
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
|
||||
namespace TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 运营操作日志仓储。
|
||||
/// </summary>
|
||||
public interface IOperationLogRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// 新增操作日志。
|
||||
/// </summary>
|
||||
/// <param name="log">操作日志。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
Task AddAsync(OperationLog log, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 保存仓储变更。
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
Reference in New Issue
Block a user