37 changed files with 2451 additions and 73 deletions
@ -0,0 +1,25 @@ |
|||
# 设置错误时停止执行 |
|||
$ErrorActionPreference = "Stop" |
|||
|
|||
# 设置项目路径 |
|||
$projectPath = "src/CellularManagement.Infrastructure" |
|||
$startupProjectPath = "src/CellularManagement.WebAPI" |
|||
|
|||
# 设置迁移名称 |
|||
$migrationName = "AddLoginLogs" |
|||
|
|||
# 检查是否已存在迁移 |
|||
$migrations = dotnet ef migrations list --project $projectPath --startup-project $startupProjectPath |
|||
if ($migrations -match $migrationName) { |
|||
Write-Host "迁移 '$migrationName' 已存在,跳过创建迁移步骤" |
|||
} else { |
|||
# 创建迁移 |
|||
Write-Host "正在创建迁移 '$migrationName'..." |
|||
dotnet ef migrations add $migrationName --project $projectPath --startup-project $startupProjectPath |
|||
} |
|||
|
|||
# 更新数据库 |
|||
Write-Host "正在更新数据库..." |
|||
dotnet ef database update --project $projectPath --startup-project $startupProjectPath |
|||
|
|||
Write-Host "数据库更新完成!" |
@ -0,0 +1,59 @@ |
|||
using System; |
|||
|
|||
namespace CellularManagement.Domain.Entities; |
|||
|
|||
/// <summary>
|
|||
/// 用户登录日志
|
|||
/// </summary>
|
|||
public class LoginLog |
|||
{ |
|||
/// <summary>
|
|||
/// 日志ID
|
|||
/// </summary>
|
|||
public Guid Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 用户ID
|
|||
/// </summary>
|
|||
public string UserId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 登录时间
|
|||
/// </summary>
|
|||
public DateTime LoginTime { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 登录IP
|
|||
/// </summary>
|
|||
public string IpAddress { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 设备信息
|
|||
/// </summary>
|
|||
public string UserAgent { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 登录状态(成功/失败)
|
|||
/// </summary>
|
|||
public bool IsSuccess { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 失败原因
|
|||
/// </summary>
|
|||
public string? FailureReason { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 登录位置
|
|||
/// </summary>
|
|||
public string? Location { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 浏览器信息
|
|||
/// </summary>
|
|||
public string? Browser { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 操作系统信息
|
|||
/// </summary>
|
|||
public string? OperatingSystem { get; set; } |
|||
} |
@ -0,0 +1,31 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using CellularManagement.Domain.Entities; |
|||
|
|||
namespace CellularManagement.Domain.Repositories; |
|||
|
|||
/// <summary>
|
|||
/// 登录日志仓储接口
|
|||
/// </summary>
|
|||
public interface ILoginLogRepository |
|||
{ |
|||
/// <summary>
|
|||
/// 添加登录日志
|
|||
/// </summary>
|
|||
Task AddAsync(LoginLog log, CancellationToken cancellationToken = default); |
|||
|
|||
/// <summary>
|
|||
/// 获取用户最近的登录日志
|
|||
/// </summary>
|
|||
Task<LoginLog[]> GetRecentLogsAsync(string userId, int count, CancellationToken cancellationToken = default); |
|||
|
|||
/// <summary>
|
|||
/// 检查IP是否被限制
|
|||
/// </summary>
|
|||
Task<bool> IsIpRestrictedAsync(string ipAddress, CancellationToken cancellationToken = default); |
|||
|
|||
/// <summary>
|
|||
/// 获取IP的失败登录次数
|
|||
/// </summary>
|
|||
Task<int> GetIpFailureCountAsync(string ipAddress, CancellationToken cancellationToken = default); |
|||
} |
@ -0,0 +1,80 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
using CellularManagement.Domain.Entities; |
|||
|
|||
namespace CellularManagement.Infrastructure.Configurations; |
|||
|
|||
/// <summary>
|
|||
/// LoginLog 实体配置类
|
|||
/// 用于配置登录日志实体在数据库中的映射关系
|
|||
/// </summary>
|
|||
public sealed class LoginLogConfiguration : IEntityTypeConfiguration<LoginLog> |
|||
{ |
|||
/// <summary>
|
|||
/// 配置 LoginLog 实体
|
|||
/// </summary>
|
|||
/// <param name="builder">实体类型构建器</param>
|
|||
public void Configure(EntityTypeBuilder<LoginLog> builder) |
|||
{ |
|||
// 配置表名
|
|||
builder.ToTable("LoginLogs", t => t.HasComment("用户登录日志表")); |
|||
|
|||
// 配置主键
|
|||
builder.HasKey(l => l.Id); |
|||
|
|||
// 配置索引
|
|||
builder.HasIndex(l => l.UserId).HasDatabaseName("IX_LoginLogs_UserId"); |
|||
builder.HasIndex(l => l.LoginTime).HasDatabaseName("IX_LoginLogs_LoginTime"); |
|||
builder.HasIndex(l => l.IpAddress).HasDatabaseName("IX_LoginLogs_IpAddress"); |
|||
builder.HasIndex(l => new { l.UserId, l.LoginTime }).HasDatabaseName("IX_LoginLogs_UserId_LoginTime"); |
|||
|
|||
// 配置属性
|
|||
builder.Property(l => l.Id) |
|||
.HasComment("日志ID"); |
|||
|
|||
builder.Property(l => l.UserId) |
|||
.IsRequired() |
|||
.HasMaxLength(450) |
|||
.HasComment("用户ID"); |
|||
|
|||
builder.Property(l => l.LoginTime) |
|||
.IsRequired() |
|||
.HasComment("登录时间"); |
|||
|
|||
builder.Property(l => l.IpAddress) |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasComment("登录IP"); |
|||
|
|||
builder.Property(l => l.UserAgent) |
|||
.IsRequired() |
|||
.HasMaxLength(500) |
|||
.HasComment("设备信息"); |
|||
|
|||
builder.Property(l => l.IsSuccess) |
|||
.IsRequired() |
|||
.HasComment("登录状态(成功/失败)"); |
|||
|
|||
builder.Property(l => l.FailureReason) |
|||
.HasMaxLength(200) |
|||
.HasComment("失败原因"); |
|||
|
|||
builder.Property(l => l.Location) |
|||
.HasMaxLength(200) |
|||
.HasComment("登录位置"); |
|||
|
|||
builder.Property(l => l.Browser) |
|||
.HasMaxLength(100) |
|||
.HasComment("浏览器信息"); |
|||
|
|||
builder.Property(l => l.OperatingSystem) |
|||
.HasMaxLength(100) |
|||
.HasComment("操作系统信息"); |
|||
|
|||
// 配置外键关系
|
|||
builder.HasOne<AppUser>() |
|||
.WithMany() |
|||
.HasForeignKey(l => l.UserId) |
|||
.OnDelete(DeleteBehavior.Restrict); |
|||
} |
|||
} |
@ -0,0 +1,314 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using CellularManagement.Infrastructure.Context; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace CellularManagement.Infrastructure.Migrations |
|||
{ |
|||
[DbContext(typeof(AppDbContext))] |
|||
[Migration("20250520034505_AddLastLoginTime")] |
|||
partial class AddLastLoginTime |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "8.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 63); |
|||
|
|||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppRole", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("角色ID,主键"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("角色描述"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("角色名称"); |
|||
|
|||
b.Property<string>("NormalizedName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化角色名称(大写)"); |
|||
|
|||
b.Property<DateTime>("UpdatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Name") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Roles_Name"); |
|||
|
|||
b.HasIndex("NormalizedName") |
|||
.IsUnique() |
|||
.HasDatabaseName("RoleNameIndex"); |
|||
|
|||
b.ToTable("Roles", null, t => |
|||
{ |
|||
t.HasComment("角色表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppUser", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("用户ID,主键"); |
|||
|
|||
b.Property<int>("AccessFailedCount") |
|||
.HasColumnType("integer") |
|||
.HasComment("登录失败次数"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Email") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("电子邮箱"); |
|||
|
|||
b.Property<bool>("EmailConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("邮箱是否已验证"); |
|||
|
|||
b.Property<bool>("IsActive") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(true) |
|||
.HasComment("用户状态(true: 启用, false: 禁用)"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(false) |
|||
.HasComment("是否已删除"); |
|||
|
|||
b.Property<DateTime?>("LastLoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("最后登录时间"); |
|||
|
|||
b.Property<bool>("LockoutEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用账户锁定"); |
|||
|
|||
b.Property<DateTimeOffset?>("LockoutEnd") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("账户锁定结束时间"); |
|||
|
|||
b.Property<DateTime?>("ModifiedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("修改时间"); |
|||
|
|||
b.Property<string>("NormalizedEmail") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化电子邮箱(大写)"); |
|||
|
|||
b.Property<string>("NormalizedUserName") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化用户名(大写)"); |
|||
|
|||
b.Property<string>("PasswordHash") |
|||
.HasColumnType("text") |
|||
.HasComment("密码哈希值"); |
|||
|
|||
b.Property<string>("PhoneNumber") |
|||
.IsRequired() |
|||
.HasColumnType("text") |
|||
.HasComment("电话号码"); |
|||
|
|||
b.Property<bool>("PhoneNumberConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("电话号码是否已验证"); |
|||
|
|||
b.Property<string>("SecurityStamp") |
|||
.HasColumnType("text") |
|||
.HasComment("安全戳,用于并发控制"); |
|||
|
|||
b.Property<bool>("TwoFactorEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用双因素认证"); |
|||
|
|||
b.Property<string>("UserName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("用户名"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Email") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_Email"); |
|||
|
|||
b.HasIndex("NormalizedEmail") |
|||
.HasDatabaseName("EmailIndex"); |
|||
|
|||
b.HasIndex("NormalizedUserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("UserNameIndex"); |
|||
|
|||
b.HasIndex("PhoneNumber") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_PhoneNumber"); |
|||
|
|||
b.HasIndex("UserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_UserName"); |
|||
|
|||
b.ToTable("Users", null, t => |
|||
{ |
|||
t.HasComment("用户表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Code") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("Type") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("Permissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("PermissionId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.HasKey("RoleId", "PermissionId"); |
|||
|
|||
b.HasIndex("PermissionId"); |
|||
|
|||
b.ToTable("RolePermissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.Property<string>("UserId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.HasKey("UserId", "RoleId"); |
|||
|
|||
b.HasIndex("RoleId"); |
|||
|
|||
b.ToTable("UserRoles", null, t => |
|||
{ |
|||
t.HasComment("用户角色关系表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") |
|||
.WithMany("RolePermissions") |
|||
.HasForeignKey("PermissionId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Permission"); |
|||
|
|||
b.Navigation("Role"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Role"); |
|||
|
|||
b.Navigation("User"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Navigation("RolePermissions"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace CellularManagement.Infrastructure.Migrations |
|||
{ |
|||
/// <inheritdoc />
|
|||
public partial class AddLastLoginTime : Migration |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.AddColumn<DateTime>( |
|||
name: "LastLoginTime", |
|||
table: "Users", |
|||
type: "timestamp with time zone", |
|||
nullable: true, |
|||
comment: "最后登录时间"); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropColumn( |
|||
name: "LastLoginTime", |
|||
table: "Users"); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,396 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using CellularManagement.Infrastructure.Context; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace CellularManagement.Infrastructure.Migrations |
|||
{ |
|||
[DbContext(typeof(AppDbContext))] |
|||
[Migration("20250520044439_AddLoginLog")] |
|||
partial class AddLoginLog |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "8.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 63); |
|||
|
|||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppRole", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("角色ID,主键"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("角色描述"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("角色名称"); |
|||
|
|||
b.Property<string>("NormalizedName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化角色名称(大写)"); |
|||
|
|||
b.Property<DateTime>("UpdatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Name") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Roles_Name"); |
|||
|
|||
b.HasIndex("NormalizedName") |
|||
.IsUnique() |
|||
.HasDatabaseName("RoleNameIndex"); |
|||
|
|||
b.ToTable("Roles", null, t => |
|||
{ |
|||
t.HasComment("角色表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppUser", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("用户ID,主键"); |
|||
|
|||
b.Property<int>("AccessFailedCount") |
|||
.HasColumnType("integer") |
|||
.HasComment("登录失败次数"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Email") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("电子邮箱"); |
|||
|
|||
b.Property<bool>("EmailConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("邮箱是否已验证"); |
|||
|
|||
b.Property<bool>("IsActive") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(true) |
|||
.HasComment("用户状态(true: 启用, false: 禁用)"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(false) |
|||
.HasComment("是否已删除"); |
|||
|
|||
b.Property<DateTime?>("LastLoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("最后登录时间"); |
|||
|
|||
b.Property<bool>("LockoutEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用账户锁定"); |
|||
|
|||
b.Property<DateTimeOffset?>("LockoutEnd") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("账户锁定结束时间"); |
|||
|
|||
b.Property<DateTime?>("ModifiedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("修改时间"); |
|||
|
|||
b.Property<string>("NormalizedEmail") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化电子邮箱(大写)"); |
|||
|
|||
b.Property<string>("NormalizedUserName") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化用户名(大写)"); |
|||
|
|||
b.Property<string>("PasswordHash") |
|||
.HasColumnType("text") |
|||
.HasComment("密码哈希值"); |
|||
|
|||
b.Property<string>("PhoneNumber") |
|||
.IsRequired() |
|||
.HasColumnType("text") |
|||
.HasComment("电话号码"); |
|||
|
|||
b.Property<bool>("PhoneNumberConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("电话号码是否已验证"); |
|||
|
|||
b.Property<string>("SecurityStamp") |
|||
.HasColumnType("text") |
|||
.HasComment("安全戳,用于并发控制"); |
|||
|
|||
b.Property<bool>("TwoFactorEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用双因素认证"); |
|||
|
|||
b.Property<string>("UserName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("用户名"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Email") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_Email"); |
|||
|
|||
b.HasIndex("NormalizedEmail") |
|||
.HasDatabaseName("EmailIndex"); |
|||
|
|||
b.HasIndex("NormalizedUserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("UserNameIndex"); |
|||
|
|||
b.HasIndex("PhoneNumber") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_PhoneNumber"); |
|||
|
|||
b.HasIndex("UserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_UserName"); |
|||
|
|||
b.ToTable("Users", null, t => |
|||
{ |
|||
t.HasComment("用户表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.LoginLog", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("uuid") |
|||
.HasComment("日志ID"); |
|||
|
|||
b.Property<string>("Browser") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("浏览器信息"); |
|||
|
|||
b.Property<string>("FailureReason") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("失败原因"); |
|||
|
|||
b.Property<string>("IpAddress") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("登录IP"); |
|||
|
|||
b.Property<bool>("IsSuccess") |
|||
.HasColumnType("boolean") |
|||
.HasComment("登录状态(成功/失败)"); |
|||
|
|||
b.Property<string>("Location") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("登录位置"); |
|||
|
|||
b.Property<DateTime>("LoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("登录时间"); |
|||
|
|||
b.Property<string>("OperatingSystem") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("操作系统信息"); |
|||
|
|||
b.Property<string>("UserAgent") |
|||
.IsRequired() |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("设备信息"); |
|||
|
|||
b.Property<string>("UserId") |
|||
.IsRequired() |
|||
.HasMaxLength(450) |
|||
.HasColumnType("character varying(450)") |
|||
.HasComment("用户ID"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("IpAddress") |
|||
.HasDatabaseName("IX_LoginLogs_IpAddress"); |
|||
|
|||
b.HasIndex("LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_LoginTime"); |
|||
|
|||
b.HasIndex("UserId") |
|||
.HasDatabaseName("IX_LoginLogs_UserId"); |
|||
|
|||
b.HasIndex("UserId", "LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_UserId_LoginTime"); |
|||
|
|||
b.ToTable("LoginLogs", null, t => |
|||
{ |
|||
t.HasComment("用户登录日志表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Code") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("Type") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("Permissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("PermissionId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.HasKey("RoleId", "PermissionId"); |
|||
|
|||
b.HasIndex("PermissionId"); |
|||
|
|||
b.ToTable("RolePermissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.Property<string>("UserId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.HasKey("UserId", "RoleId"); |
|||
|
|||
b.HasIndex("RoleId"); |
|||
|
|||
b.ToTable("UserRoles", null, t => |
|||
{ |
|||
t.HasComment("用户角色关系表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.LoginLog", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", null) |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Restrict) |
|||
.IsRequired(); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") |
|||
.WithMany("RolePermissions") |
|||
.HasForeignKey("PermissionId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Permission"); |
|||
|
|||
b.Navigation("Role"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Role"); |
|||
|
|||
b.Navigation("User"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Navigation("RolePermissions"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,69 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace CellularManagement.Infrastructure.Migrations |
|||
{ |
|||
/// <inheritdoc />
|
|||
public partial class AddLoginLog : Migration |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.CreateTable( |
|||
name: "LoginLogs", |
|||
columns: table => new |
|||
{ |
|||
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "日志ID"), |
|||
UserId = table.Column<string>(type: "character varying(450)", maxLength: 450, nullable: false, comment: "用户ID"), |
|||
LoginTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false, comment: "登录时间"), |
|||
IpAddress = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false, comment: "登录IP"), |
|||
UserAgent = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false, comment: "设备信息"), |
|||
IsSuccess = table.Column<bool>(type: "boolean", nullable: false, comment: "登录状态(成功/失败)"), |
|||
FailureReason = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true, comment: "失败原因"), |
|||
Location = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true, comment: "登录位置"), |
|||
Browser = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true, comment: "浏览器信息"), |
|||
OperatingSystem = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true, comment: "操作系统信息") |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_LoginLogs", x => x.Id); |
|||
table.ForeignKey( |
|||
name: "FK_LoginLogs_Users_UserId", |
|||
column: x => x.UserId, |
|||
principalTable: "Users", |
|||
principalColumn: "Id", |
|||
onDelete: ReferentialAction.Restrict); |
|||
}, |
|||
comment: "用户登录日志表"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_LoginLogs_IpAddress", |
|||
table: "LoginLogs", |
|||
column: "IpAddress"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_LoginLogs_LoginTime", |
|||
table: "LoginLogs", |
|||
column: "LoginTime"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_LoginLogs_UserId", |
|||
table: "LoginLogs", |
|||
column: "UserId"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_LoginLogs_UserId_LoginTime", |
|||
table: "LoginLogs", |
|||
columns: new[] { "UserId", "LoginTime" }); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropTable( |
|||
name: "LoginLogs"); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,401 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using CellularManagement.Infrastructure.Context; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace CellularManagement.Infrastructure.Migrations |
|||
{ |
|||
[DbContext(typeof(AppDbContext))] |
|||
[Migration("20250520060054_AddRealNameToAppUser")] |
|||
partial class AddRealNameToAppUser |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "8.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 63); |
|||
|
|||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppRole", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("角色ID,主键"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("角色描述"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("角色名称"); |
|||
|
|||
b.Property<string>("NormalizedName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化角色名称(大写)"); |
|||
|
|||
b.Property<DateTime>("UpdatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Name") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Roles_Name"); |
|||
|
|||
b.HasIndex("NormalizedName") |
|||
.IsUnique() |
|||
.HasDatabaseName("RoleNameIndex"); |
|||
|
|||
b.ToTable("Roles", null, t => |
|||
{ |
|||
t.HasComment("角色表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppUser", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("用户ID,主键"); |
|||
|
|||
b.Property<int>("AccessFailedCount") |
|||
.HasColumnType("integer") |
|||
.HasComment("登录失败次数"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Email") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("电子邮箱"); |
|||
|
|||
b.Property<bool>("EmailConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("邮箱是否已验证"); |
|||
|
|||
b.Property<bool>("IsActive") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(true) |
|||
.HasComment("用户状态(true: 启用, false: 禁用)"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(false) |
|||
.HasComment("是否已删除"); |
|||
|
|||
b.Property<DateTime?>("LastLoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("最后登录时间"); |
|||
|
|||
b.Property<bool>("LockoutEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用账户锁定"); |
|||
|
|||
b.Property<DateTimeOffset?>("LockoutEnd") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("账户锁定结束时间"); |
|||
|
|||
b.Property<DateTime?>("ModifiedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("修改时间"); |
|||
|
|||
b.Property<string>("NormalizedEmail") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化电子邮箱(大写)"); |
|||
|
|||
b.Property<string>("NormalizedUserName") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化用户名(大写)"); |
|||
|
|||
b.Property<string>("PasswordHash") |
|||
.HasColumnType("text") |
|||
.HasComment("密码哈希值"); |
|||
|
|||
b.Property<string>("PhoneNumber") |
|||
.IsRequired() |
|||
.HasColumnType("text") |
|||
.HasComment("电话号码"); |
|||
|
|||
b.Property<bool>("PhoneNumberConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("电话号码是否已验证"); |
|||
|
|||
b.Property<string>("RealName") |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("真实姓名"); |
|||
|
|||
b.Property<string>("SecurityStamp") |
|||
.HasColumnType("text") |
|||
.HasComment("安全戳,用于并发控制"); |
|||
|
|||
b.Property<bool>("TwoFactorEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用双因素认证"); |
|||
|
|||
b.Property<string>("UserName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("用户名"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Email") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_Email"); |
|||
|
|||
b.HasIndex("NormalizedEmail") |
|||
.HasDatabaseName("EmailIndex"); |
|||
|
|||
b.HasIndex("NormalizedUserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("UserNameIndex"); |
|||
|
|||
b.HasIndex("PhoneNumber") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_PhoneNumber"); |
|||
|
|||
b.HasIndex("UserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_UserName"); |
|||
|
|||
b.ToTable("Users", null, t => |
|||
{ |
|||
t.HasComment("用户表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.LoginLog", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("uuid") |
|||
.HasComment("日志ID"); |
|||
|
|||
b.Property<string>("Browser") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("浏览器信息"); |
|||
|
|||
b.Property<string>("FailureReason") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("失败原因"); |
|||
|
|||
b.Property<string>("IpAddress") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("登录IP"); |
|||
|
|||
b.Property<bool>("IsSuccess") |
|||
.HasColumnType("boolean") |
|||
.HasComment("登录状态(成功/失败)"); |
|||
|
|||
b.Property<string>("Location") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("登录位置"); |
|||
|
|||
b.Property<DateTime>("LoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("登录时间"); |
|||
|
|||
b.Property<string>("OperatingSystem") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("操作系统信息"); |
|||
|
|||
b.Property<string>("UserAgent") |
|||
.IsRequired() |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("设备信息"); |
|||
|
|||
b.Property<string>("UserId") |
|||
.IsRequired() |
|||
.HasMaxLength(450) |
|||
.HasColumnType("character varying(450)") |
|||
.HasComment("用户ID"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("IpAddress") |
|||
.HasDatabaseName("IX_LoginLogs_IpAddress"); |
|||
|
|||
b.HasIndex("LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_LoginTime"); |
|||
|
|||
b.HasIndex("UserId") |
|||
.HasDatabaseName("IX_LoginLogs_UserId"); |
|||
|
|||
b.HasIndex("UserId", "LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_UserId_LoginTime"); |
|||
|
|||
b.ToTable("LoginLogs", null, t => |
|||
{ |
|||
t.HasComment("用户登录日志表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Code") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("Type") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("Permissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("PermissionId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.HasKey("RoleId", "PermissionId"); |
|||
|
|||
b.HasIndex("PermissionId"); |
|||
|
|||
b.ToTable("RolePermissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.Property<string>("UserId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.HasKey("UserId", "RoleId"); |
|||
|
|||
b.HasIndex("RoleId"); |
|||
|
|||
b.ToTable("UserRoles", null, t => |
|||
{ |
|||
t.HasComment("用户角色关系表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.LoginLog", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", null) |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Restrict) |
|||
.IsRequired(); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") |
|||
.WithMany("RolePermissions") |
|||
.HasForeignKey("PermissionId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Permission"); |
|||
|
|||
b.Navigation("Role"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Role"); |
|||
|
|||
b.Navigation("User"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Navigation("RolePermissions"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace CellularManagement.Infrastructure.Migrations |
|||
{ |
|||
/// <inheritdoc />
|
|||
public partial class AddRealNameToAppUser : Migration |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.AddColumn<string>( |
|||
name: "RealName", |
|||
table: "Users", |
|||
type: "character varying(50)", |
|||
maxLength: 50, |
|||
nullable: true, |
|||
comment: "真实姓名"); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropColumn( |
|||
name: "RealName", |
|||
table: "Users"); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,401 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using CellularManagement.Infrastructure.Context; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace CellularManagement.Infrastructure.Migrations |
|||
{ |
|||
[DbContext(typeof(AppDbContext))] |
|||
[Migration("20250520060351_UpdateUserComments")] |
|||
partial class UpdateUserComments |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "8.0.0") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 63); |
|||
|
|||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppRole", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("角色ID,主键"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("角色描述"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("角色名称"); |
|||
|
|||
b.Property<string>("NormalizedName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化角色名称(大写)"); |
|||
|
|||
b.Property<DateTime>("UpdatedAt") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("更新时间"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Name") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Roles_Name"); |
|||
|
|||
b.HasIndex("NormalizedName") |
|||
.IsUnique() |
|||
.HasDatabaseName("RoleNameIndex"); |
|||
|
|||
b.ToTable("Roles", null, t => |
|||
{ |
|||
t.HasComment("角色表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.AppUser", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text") |
|||
.HasComment("用户ID,主键"); |
|||
|
|||
b.Property<int>("AccessFailedCount") |
|||
.HasColumnType("integer") |
|||
.HasComment("登录失败次数"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnType("text") |
|||
.HasComment("并发控制戳"); |
|||
|
|||
b.Property<DateTime>("CreatedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("创建时间"); |
|||
|
|||
b.Property<string>("Email") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("电子邮箱"); |
|||
|
|||
b.Property<bool>("EmailConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("邮箱是否已验证"); |
|||
|
|||
b.Property<bool>("IsActive") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(true) |
|||
.HasComment("用户状态(true: 启用, false: 禁用)"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("boolean") |
|||
.HasDefaultValue(false) |
|||
.HasComment("是否已删除"); |
|||
|
|||
b.Property<DateTime?>("LastLoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("最后登录时间"); |
|||
|
|||
b.Property<bool>("LockoutEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用账户锁定"); |
|||
|
|||
b.Property<DateTimeOffset?>("LockoutEnd") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("账户锁定结束时间"); |
|||
|
|||
b.Property<DateTime?>("ModifiedTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("修改时间"); |
|||
|
|||
b.Property<string>("NormalizedEmail") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化电子邮箱(大写)"); |
|||
|
|||
b.Property<string>("NormalizedUserName") |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("标准化账号(大写)"); |
|||
|
|||
b.Property<string>("PasswordHash") |
|||
.HasColumnType("text") |
|||
.HasComment("密码哈希值"); |
|||
|
|||
b.Property<string>("PhoneNumber") |
|||
.IsRequired() |
|||
.HasColumnType("text") |
|||
.HasComment("电话号码"); |
|||
|
|||
b.Property<bool>("PhoneNumberConfirmed") |
|||
.HasColumnType("boolean") |
|||
.HasComment("电话号码是否已验证"); |
|||
|
|||
b.Property<string>("RealName") |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("用户名"); |
|||
|
|||
b.Property<string>("SecurityStamp") |
|||
.HasColumnType("text") |
|||
.HasComment("安全戳,用于并发控制"); |
|||
|
|||
b.Property<bool>("TwoFactorEnabled") |
|||
.HasColumnType("boolean") |
|||
.HasComment("是否启用双因素认证"); |
|||
|
|||
b.Property<string>("UserName") |
|||
.IsRequired() |
|||
.HasMaxLength(256) |
|||
.HasColumnType("character varying(256)") |
|||
.HasComment("账号"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Email") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_Email"); |
|||
|
|||
b.HasIndex("NormalizedEmail") |
|||
.HasDatabaseName("EmailIndex"); |
|||
|
|||
b.HasIndex("NormalizedUserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("UserNameIndex"); |
|||
|
|||
b.HasIndex("PhoneNumber") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_PhoneNumber"); |
|||
|
|||
b.HasIndex("UserName") |
|||
.IsUnique() |
|||
.HasDatabaseName("IX_Users_UserName"); |
|||
|
|||
b.ToTable("Users", null, t => |
|||
{ |
|||
t.HasComment("用户表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.LoginLog", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("uuid") |
|||
.HasComment("日志ID"); |
|||
|
|||
b.Property<string>("Browser") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("浏览器信息"); |
|||
|
|||
b.Property<string>("FailureReason") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("失败原因"); |
|||
|
|||
b.Property<string>("IpAddress") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)") |
|||
.HasComment("登录IP"); |
|||
|
|||
b.Property<bool>("IsSuccess") |
|||
.HasColumnType("boolean") |
|||
.HasComment("登录状态(成功/失败)"); |
|||
|
|||
b.Property<string>("Location") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)") |
|||
.HasComment("登录位置"); |
|||
|
|||
b.Property<DateTime>("LoginTime") |
|||
.HasColumnType("timestamp with time zone") |
|||
.HasComment("登录时间"); |
|||
|
|||
b.Property<string>("OperatingSystem") |
|||
.HasMaxLength(100) |
|||
.HasColumnType("character varying(100)") |
|||
.HasComment("操作系统信息"); |
|||
|
|||
b.Property<string>("UserAgent") |
|||
.IsRequired() |
|||
.HasMaxLength(500) |
|||
.HasColumnType("character varying(500)") |
|||
.HasComment("设备信息"); |
|||
|
|||
b.Property<string>("UserId") |
|||
.IsRequired() |
|||
.HasMaxLength(450) |
|||
.HasColumnType("character varying(450)") |
|||
.HasComment("用户ID"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("IpAddress") |
|||
.HasDatabaseName("IX_LoginLogs_IpAddress"); |
|||
|
|||
b.HasIndex("LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_LoginTime"); |
|||
|
|||
b.HasIndex("UserId") |
|||
.HasDatabaseName("IX_LoginLogs_UserId"); |
|||
|
|||
b.HasIndex("UserId", "LoginTime") |
|||
.HasDatabaseName("IX_LoginLogs_UserId_LoginTime"); |
|||
|
|||
b.ToTable("LoginLogs", null, t => |
|||
{ |
|||
t.HasComment("用户登录日志表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Property<string>("Id") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("Code") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(200) |
|||
.HasColumnType("character varying(200)"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.Property<string>("Type") |
|||
.IsRequired() |
|||
.HasMaxLength(50) |
|||
.HasColumnType("character varying(50)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("Permissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("PermissionId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<DateTime>("CreatedAt") |
|||
.HasColumnType("timestamp with time zone"); |
|||
|
|||
b.HasKey("RoleId", "PermissionId"); |
|||
|
|||
b.HasIndex("PermissionId"); |
|||
|
|||
b.ToTable("RolePermissions", (string)null); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.Property<string>("UserId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.Property<string>("RoleId") |
|||
.HasColumnType("text"); |
|||
|
|||
b.HasKey("UserId", "RoleId"); |
|||
|
|||
b.HasIndex("RoleId"); |
|||
|
|||
b.ToTable("UserRoles", null, t => |
|||
{ |
|||
t.HasComment("用户角色关系表"); |
|||
}); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.LoginLog", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", null) |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Restrict) |
|||
.IsRequired(); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") |
|||
.WithMany("RolePermissions") |
|||
.HasForeignKey("PermissionId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Permission"); |
|||
|
|||
b.Navigation("Role"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => |
|||
{ |
|||
b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") |
|||
.WithMany() |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade) |
|||
.IsRequired(); |
|||
|
|||
b.Navigation("Role"); |
|||
|
|||
b.Navigation("User"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => |
|||
{ |
|||
b.Navigation("RolePermissions"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,94 @@ |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace CellularManagement.Infrastructure.Migrations |
|||
{ |
|||
/// <inheritdoc />
|
|||
public partial class UpdateUserComments : Migration |
|||
{ |
|||
/// <inheritdoc />
|
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "UserName", |
|||
table: "Users", |
|||
type: "character varying(256)", |
|||
maxLength: 256, |
|||
nullable: false, |
|||
comment: "账号", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(256)", |
|||
oldMaxLength: 256, |
|||
oldComment: "用户名"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "RealName", |
|||
table: "Users", |
|||
type: "character varying(50)", |
|||
maxLength: 50, |
|||
nullable: true, |
|||
comment: "用户名", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(50)", |
|||
oldMaxLength: 50, |
|||
oldNullable: true, |
|||
oldComment: "真实姓名"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "NormalizedUserName", |
|||
table: "Users", |
|||
type: "character varying(256)", |
|||
maxLength: 256, |
|||
nullable: true, |
|||
comment: "标准化账号(大写)", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(256)", |
|||
oldMaxLength: 256, |
|||
oldNullable: true, |
|||
oldComment: "标准化用户名(大写)"); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "UserName", |
|||
table: "Users", |
|||
type: "character varying(256)", |
|||
maxLength: 256, |
|||
nullable: false, |
|||
comment: "用户名", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(256)", |
|||
oldMaxLength: 256, |
|||
oldComment: "账号"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "RealName", |
|||
table: "Users", |
|||
type: "character varying(50)", |
|||
maxLength: 50, |
|||
nullable: true, |
|||
comment: "真实姓名", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(50)", |
|||
oldMaxLength: 50, |
|||
oldNullable: true, |
|||
oldComment: "用户名"); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "NormalizedUserName", |
|||
table: "Users", |
|||
type: "character varying(256)", |
|||
maxLength: 256, |
|||
nullable: true, |
|||
comment: "标准化用户名(大写)", |
|||
oldClrType: typeof(string), |
|||
oldType: "character varying(256)", |
|||
oldMaxLength: 256, |
|||
oldNullable: true, |
|||
oldComment: "标准化账号(大写)"); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,84 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using CellularManagement.Domain.Entities; |
|||
using CellularManagement.Domain.Repositories; |
|||
using CellularManagement.Infrastructure.Context; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace CellularManagement.Infrastructure.Repositories; |
|||
|
|||
/// <summary>
|
|||
/// 登录日志仓储实现
|
|||
/// </summary>
|
|||
public class LoginLogRepository : CommandRepository<LoginLog>, ILoginLogRepository |
|||
{ |
|||
private readonly AppDbContext _context; |
|||
private readonly ILogger<LoginLogRepository> _logger; |
|||
|
|||
/// <summary>
|
|||
/// 初始化仓储
|
|||
/// </summary>
|
|||
/// <param name="context">数据库上下文</param>
|
|||
/// <param name="unitOfWork">工作单元</param>
|
|||
/// <param name="logger">日志记录器</param>
|
|||
public LoginLogRepository( |
|||
AppDbContext context, |
|||
IUnitOfWork unitOfWork, |
|||
ILogger<LoginLogRepository> logger) |
|||
: base(context, unitOfWork, logger) |
|||
{ |
|||
_context = context; |
|||
_logger = logger; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 添加登录日志
|
|||
/// </summary>
|
|||
public async Task AddAsync(LoginLog log, CancellationToken cancellationToken = default) |
|||
{ |
|||
await _context.LoginLogs.AddAsync(log, cancellationToken); |
|||
// 不在这里 SaveChanges,由工作单元统一提交
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取用户最近的登录日志
|
|||
/// </summary>
|
|||
public async Task<LoginLog[]> GetRecentLogsAsync(string userId, int count, CancellationToken cancellationToken = default) |
|||
{ |
|||
return await _context.LoginLogs |
|||
.Where(l => l.UserId == userId) |
|||
.OrderByDescending(l => l.LoginTime) |
|||
.Take(count) |
|||
.ToArrayAsync(cancellationToken); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 检查IP是否被限制
|
|||
/// </summary>
|
|||
public async Task<bool> IsIpRestrictedAsync(string ipAddress, CancellationToken cancellationToken = default) |
|||
{ |
|||
var recentFailures = await _context.LoginLogs |
|||
.Where(l => l.IpAddress == ipAddress && !l.IsSuccess) |
|||
.OrderByDescending(l => l.LoginTime) |
|||
.Take(5) |
|||
.ToArrayAsync(cancellationToken); |
|||
|
|||
return recentFailures.Length >= 5 && |
|||
(DateTime.UtcNow - recentFailures[0].LoginTime).TotalMinutes < 30; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 获取IP的失败登录次数
|
|||
/// </summary>
|
|||
public async Task<int> GetIpFailureCountAsync(string ipAddress, CancellationToken cancellationToken = default) |
|||
{ |
|||
return await _context.LoginLogs |
|||
.CountAsync(l => l.IpAddress == ipAddress && |
|||
!l.IsSuccess && |
|||
l.LoginTime > DateTime.UtcNow.AddMinutes(-30), |
|||
cancellationToken); |
|||
} |
|||
} |
@ -1,34 +1,57 @@ |
|||
# 数据库迁移脚本 |
|||
Write-Host "开始执行数据库迁移..." -ForegroundColor Green |
|||
# Database Migration Script |
|||
$ErrorActionPreference = "Stop" |
|||
|
|||
function Write-Status { |
|||
param( |
|||
[string]$Message, |
|||
[string]$Status = "Info" |
|||
) |
|||
|
|||
$color = switch ($Status) { |
|||
"Success" { "Green" } |
|||
"Error" { "Red" } |
|||
"Warning" { "Yellow" } |
|||
default { "White" } |
|||
} |
|||
|
|||
Write-Host "[$Status] $Message" -ForegroundColor $color |
|||
} |
|||
|
|||
try { |
|||
# 添加迁移 |
|||
Write-Host "正在添加迁移..." -ForegroundColor Yellow |
|||
dotnet ef migrations add UpdateAppUserAuditFields --project src/CellularManagement.Infrastructure --startup-project src/CellularManagement.WebAPI |
|||
# Check if projects exist |
|||
if (-not (Test-Path "src/CellularManagement.Infrastructure")) { |
|||
throw "Infrastructure project not found" |
|||
} |
|||
if (-not (Test-Path "src/CellularManagement.WebAPI")) { |
|||
throw "WebAPI project not found" |
|||
} |
|||
|
|||
if ($LASTEXITCODE -eq 0) { |
|||
Write-Host "迁移添加成功!" -ForegroundColor Green |
|||
|
|||
# 更新数据库 |
|||
Write-Host "正在更新数据库..." -ForegroundColor Yellow |
|||
dotnet ef database update --project src/CellularManagement.Infrastructure --startup-project src/CellularManagement.WebAPI |
|||
|
|||
if ($LASTEXITCODE -eq 0) { |
|||
Write-Host "数据库更新成功!" -ForegroundColor Green |
|||
} else { |
|||
Write-Host "数据库更新失败!错误代码: $LASTEXITCODE" -ForegroundColor Red |
|||
exit 1 |
|||
} |
|||
} else { |
|||
Write-Host "迁移添加失败!错误代码: $LASTEXITCODE" -ForegroundColor Red |
|||
exit 1 |
|||
# Build project |
|||
Write-Status "Building project..." "Info" |
|||
dotnet build "src/CellularManagement.Infrastructure" -c Release |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "Project build failed" |
|||
} |
|||
} |
|||
catch { |
|||
Write-Host "执行过程中发生错误:" -ForegroundColor Red |
|||
Write-Host $_.Exception.Message -ForegroundColor Red |
|||
exit 1 |
|||
} |
|||
Write-Status "Project built successfully" "Success" |
|||
|
|||
# Add migration |
|||
Write-Status "Adding database migration..." "Info" |
|||
$migrationName = "AddRealNameToAppUser" |
|||
dotnet ef migrations add $migrationName --project src/CellularManagement.Infrastructure --startup-project src/CellularManagement.WebAPI |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "Failed to add migration" |
|||
} |
|||
Write-Status "Migration added successfully" "Success" |
|||
|
|||
Write-Host "按任意键退出..." |
|||
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") |
|||
# Update database |
|||
Write-Status "Updating database..." "Info" |
|||
dotnet ef database update --project src/CellularManagement.Infrastructure --startup-project src/CellularManagement.WebAPI |
|||
if ($LASTEXITCODE -ne 0) { |
|||
throw "Database update failed" |
|||
} |
|||
Write-Status "Database updated successfully" "Success" |
|||
|
|||
} catch { |
|||
Write-Status "Error occurred: $_" "Error" |
|||
exit 1 |
|||
} |
Loading…
Reference in new issue