using Microsoft.EntityFrameworkCore; namespace IM.Admin.Data; public sealed class AdminAccount { public Guid Id { get; set; } = Guid.NewGuid(); public string Account { get; set; } = ""; public string Name { get; set; } = ""; public string Email { get; set; } = ""; public string PasswordHash { get; set; } = ""; public string Role { get; set; } = "reviewer"; public bool Enabled { get; set; } = true; public string Stamp { get; set; } = Guid.NewGuid().ToString("N"); public int FailedAttempts { get; set; } public DateTime? LockedUntil { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; } public sealed class SystemSetting { public string Id { get; set; } = ""; public long Version { get; set; } public string Value { get; set; } = "{}"; public string? Draft { get; set; } public long? TestedVersion { get; set; } public string Secret { get; set; } = ""; public string? DraftSecret { get; set; } public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; } public sealed class Report { public Guid Id { get; set; } = Guid.NewGuid(); public Guid ReporterId { get; set; } public Guid TargetId { get; set; } public string TargetName { get; set; } = ""; public string Type { get; set; } = "user"; public string Reason { get; set; } = ""; public string Description { get; set; } = ""; public string Evidence { get; set; } = "[]"; public string Status { get; set; } = "待处理"; public Guid? AssigneeId { get; set; } public string? Result { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? ClosedAt { get; set; } public long Version { get; set; } } // A durable outbox command: both the request and the pending report update commit together. public sealed class Operation { public Guid Id { get; set; } public Guid ActorId { get; set; } public string ActorName { get; set; } = ""; public Guid TargetId { get; set; } public string Type { get; set; } = ""; public string Action { get; set; } = ""; public string Reason { get; set; } = ""; public Guid? ReportId { get; set; } public string Status { get; set; } = "pending"; public int Attempts { get; set; } public string? Error { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime NextAttemptAt { get; set; } = DateTime.UtcNow; public DateTime? CompletedAt { get; set; } } public sealed class AuditRecord { public Guid Id { get; set; } = Guid.NewGuid(); public Guid ActorId { get; set; } public string ActorName { get; set; } = ""; public string Action { get; set; } = ""; public string TargetId { get; set; } = ""; public string TargetName { get; set; } = ""; public string Before { get; set; } = ""; public string After { get; set; } = ""; public string Reason { get; set; } = ""; public Guid? ReportId { get; set; } public Guid? OperationId { get; set; } public string Result { get; set; } = "成功"; public DateTime CreatedAt { get; set; } = DateTime.UtcNow; } public sealed class PasswordReset { public string Id { get; set; } = ""; public Guid AccountId { get; set; } public DateTime ExpiresAt { get; set; } } public sealed class AdminDb(DbContextOptions options) : DbContext(options) { public DbSet Accounts => Set(); public DbSet Settings => Set(); public DbSet Reports => Set(); public DbSet Operations => Set(); public DbSet Audit => Set(); public DbSet Resets => Set(); protected override void OnModelCreating(ModelBuilder b) { b.Entity().ToTable("admin_accounts").HasIndex(x => x.Account).IsUnique(); b.Entity().Property(x => x.Account).HasMaxLength(100); b.Entity().Property(x => x.Stamp).IsConcurrencyToken(); b.Entity().ToTable("admin_settings").Property(x => x.Id).HasMaxLength(64); b.Entity().Property(x => x.Version).IsConcurrencyToken(); b.Entity().ToTable("admin_reports").HasIndex(x => new { x.ReporterId, x.CreatedAt }); b.Entity().HasIndex(x => new { x.Status, x.CreatedAt }); b.Entity().Property(x => x.Status).HasMaxLength(30); b.Entity().Property(x => x.Version).IsConcurrencyToken(); b.Entity().ToTable("admin_operations").HasIndex(x => new { x.Status, x.NextAttemptAt }); b.Entity().Property(x => x.Status).HasMaxLength(30); b.Entity().ToTable("admin_audit").HasIndex(x => x.CreatedAt); b.Entity().HasIndex(x => x.OperationId).IsUnique(); b.Entity().ToTable("admin_password_resets").Property(x => x.Id).HasMaxLength(64); } }