diff --git a/Directory.Packages.props b/Directory.Packages.props
index dd0d188..14d249c 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -4,6 +4,7 @@
3.2.1
+
diff --git a/Fengling.Platform.Domain/AggregatesModel/RoleAggregate/ApplicationRole.cs b/Fengling.Platform.Domain/AggregatesModel/RoleAggregate/ApplicationRole.cs
new file mode 100644
index 0000000..3f6b863
--- /dev/null
+++ b/Fengling.Platform.Domain/AggregatesModel/RoleAggregate/ApplicationRole.cs
@@ -0,0 +1,13 @@
+using Microsoft.AspNetCore.Identity;
+
+namespace Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
+
+public class ApplicationRole : IdentityRole
+{
+ public string? Description { get; set; }
+ public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
+ public long? TenantId { get; set; }
+ public bool IsSystem { get; set; }
+ public string? DisplayName { get; set; }
+ public List? Permissions { get; set; }
+}
diff --git a/Fengling.Platform.Domain/AggregatesModel/TenantAggregate/Tenant.cs b/Fengling.Platform.Domain/AggregatesModel/TenantAggregate/Tenant.cs
index 43090b2..ce7634b 100644
--- a/Fengling.Platform.Domain/AggregatesModel/TenantAggregate/Tenant.cs
+++ b/Fengling.Platform.Domain/AggregatesModel/TenantAggregate/Tenant.cs
@@ -1,99 +1,21 @@
namespace Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
-using System.Threading;
-using Fengling.Platform.Domain.DomainEvents;
-
-public partial record TenantId : IInt64StronglyTypedId
+public class Tenant
{
- public static TenantId New() => new TenantId(Interlocked.Increment(ref _lastId));
-
- private static long _lastId = 0;
-
- public long Value => this;
-
- public static implicit operator long(TenantId id) => id.Value;
- public static implicit operator TenantId(long value) => new TenantId(value);
-}
-
-public class Tenant : Entity, IAggregateRoot
-{
- protected Tenant() { }
-
- public Tenant(string tenantCode, string name, string contactName, string contactEmail)
- {
- TenantCode = tenantCode;
- Name = name;
- ContactName = contactName;
- ContactEmail = contactEmail;
- Status = TenantStatus.Active;
- CreatedAt = DateTime.UtcNow;
- this.AddDomainEvent(new TenantCreatedDomainEvent(this));
- }
-
- public Tenant(string tenantCode, string name, string contactName, string contactEmail,
- string? contactPhone, int? maxUsers, string? description, DateTime? expiresAt)
- : this(tenantCode, name, contactName, contactEmail)
- {
- ContactPhone = contactPhone;
- MaxUsers = maxUsers;
- Description = description;
- ExpiresAt = expiresAt;
- }
-
- public string TenantCode { get; private set; } = string.Empty;
- public string Name { get; private set; } = string.Empty;
- public string ContactName { get; private set; } = string.Empty;
- public string ContactEmail { get; private set; } = string.Empty;
- public string? ContactPhone { get; private set; }
- public int? MaxUsers { get; private set; }
- public DateTime CreatedAt { get; private set; }
- public DateTime? UpdatedAt { get; private set; }
- public DateTime? ExpiresAt { get; private set; }
- public string? Description { get; private set; }
- public TenantStatus Status { get; private set; }
- public Deleted Deleted { get; private set; } = new();
- public RowVersion RowVersion { get; private set; } = new(0);
-
- public void UpdateInfo(string name, string contactName, string contactEmail, string? contactPhone = null)
- {
- Name = name;
- ContactName = contactName;
- ContactEmail = contactEmail;
- ContactPhone = contactPhone;
- UpdatedAt = DateTime.UtcNow;
- }
-
- public void Activate()
- {
- if (Status == TenantStatus.Active) return;
- Status = TenantStatus.Active;
- UpdatedAt = DateTime.UtcNow;
- this.AddDomainEvent(new TenantStatusChangedDomainEvent(this, TenantStatus.Inactive, TenantStatus.Active));
- }
-
- public void Deactivate()
- {
- if (Status == TenantStatus.Inactive) return;
- Status = TenantStatus.Inactive;
- UpdatedAt = DateTime.UtcNow;
- this.AddDomainEvent(new TenantStatusChangedDomainEvent(this, TenantStatus.Active, TenantStatus.Inactive));
- }
-
- public void Delete()
- {
- if (Status == TenantStatus.Inactive) return;
- Status = TenantStatus.Inactive;
- UpdatedAt = DateTime.UtcNow;
- Deleted = new Deleted(true);
- }
-
- public void Freeze()
- {
- if (Status == TenantStatus.Frozen) return;
- Status = TenantStatus.Frozen;
- UpdatedAt = DateTime.UtcNow;
- this.AddDomainEvent(new TenantStatusChangedDomainEvent(this, TenantStatus.Active, TenantStatus.Frozen));
- }
+ public long Id { get; set; }
+ public string TenantCode { get; set; } = string.Empty;
+ public string Name { get; set; } = string.Empty;
+ public string ContactName { get; set; } = string.Empty;
+ public string ContactEmail { get; set; } = string.Empty;
+ public string? ContactPhone { get; set; }
+ public int? MaxUsers { get; set; }
+ public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
+ public DateTime? UpdatedAt { get; set; }
+ public DateTime? ExpiresAt { get; set; }
+ public string? Description { get; set; }
+ public TenantStatus Status { get; set; } = TenantStatus.Active;
+ public bool IsDeleted { get; set; }
+ public long RowVersion { get; set; }
}
public enum TenantStatus
diff --git a/Fengling.Platform.Domain/AggregatesModel/UserAggregate/AccessLog.cs b/Fengling.Platform.Domain/AggregatesModel/UserAggregate/AccessLog.cs
new file mode 100644
index 0000000..994f42a
--- /dev/null
+++ b/Fengling.Platform.Domain/AggregatesModel/UserAggregate/AccessLog.cs
@@ -0,0 +1,43 @@
+namespace Fengling.Platform.Domain.AggregatesModel.UserAggregate;
+
+using System.ComponentModel.DataAnnotations;
+
+public class AccessLog
+{
+ [Key]
+ public long Id { get; set; }
+
+ [MaxLength(50)]
+ public string? UserName { get; set; }
+
+ [MaxLength(50)]
+ public string? TenantId { get; set; }
+
+ [MaxLength(20)]
+ public string Action { get; set; } = string.Empty;
+
+ [MaxLength(200)]
+ public string? Resource { get; set; }
+
+ [MaxLength(10)]
+ public string? Method { get; set; }
+
+ [MaxLength(50)]
+ public string? IpAddress { get; set; }
+
+ [MaxLength(500)]
+ public string? UserAgent { get; set; }
+
+ [MaxLength(20)]
+ public string Status { get; set; } = "success";
+
+ public int Duration { get; set; }
+
+ public string? RequestData { get; set; }
+
+ public string? ResponseData { get; set; }
+
+ public string? ErrorMessage { get; set; }
+
+ public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
+}
diff --git a/Fengling.Platform.Domain/AggregatesModel/UserAggregate/ApplicationUser.cs b/Fengling.Platform.Domain/AggregatesModel/UserAggregate/ApplicationUser.cs
new file mode 100644
index 0000000..69df260
--- /dev/null
+++ b/Fengling.Platform.Domain/AggregatesModel/UserAggregate/ApplicationUser.cs
@@ -0,0 +1,14 @@
+using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
+using Microsoft.AspNetCore.Identity;
+
+namespace Fengling.Platform.Domain.AggregatesModel.UserAggregate;
+
+public class ApplicationUser : IdentityUser
+{
+ public string? RealName { get; set; }
+ public string? Phone { get; set; }
+ public TenantInfo TenantInfo { get; set; } = null!;
+ public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
+ public DateTime? UpdatedTime { get; set; }
+ public bool IsDeleted { get; set; }
+}
diff --git a/Fengling.Platform.Domain/AggregatesModel/UserAggregate/AuditLog.cs b/Fengling.Platform.Domain/AggregatesModel/UserAggregate/AuditLog.cs
new file mode 100644
index 0000000..dd0dfca
--- /dev/null
+++ b/Fengling.Platform.Domain/AggregatesModel/UserAggregate/AuditLog.cs
@@ -0,0 +1,47 @@
+namespace Fengling.Platform.Domain.AggregatesModel.UserAggregate;
+
+using System.ComponentModel.DataAnnotations;
+
+public class AuditLog
+{
+ [Key]
+ public long Id { get; set; }
+
+ [MaxLength(50)]
+ [Required]
+ public string Operator { get; set; } = string.Empty;
+
+ [MaxLength(50)]
+ public string? TenantId { get; set; }
+
+ [MaxLength(20)]
+ public string Operation { get; set; } = string.Empty;
+
+ [MaxLength(20)]
+ public string Action { get; set; } = string.Empty;
+
+ [MaxLength(50)]
+ public string? TargetType { get; set; }
+
+ public long? TargetId { get; set; }
+
+ [MaxLength(100)]
+ public string? TargetName { get; set; }
+
+ [MaxLength(50)]
+ public string IpAddress { get; set; } = string.Empty;
+
+ [MaxLength(500)]
+ public string? Description { get; set; }
+
+ public string? OldValue { get; set; }
+
+ public string? NewValue { get; set; }
+
+ public string? ErrorMessage { get; set; }
+
+ [MaxLength(20)]
+ public string Status { get; set; } = "success";
+
+ public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
+}
diff --git a/Fengling.Platform.Domain/DomainEvents/TenantDomainEvents.cs b/Fengling.Platform.Domain/DomainEvents/TenantDomainEvents.cs
deleted file mode 100644
index 28339c5..0000000
--- a/Fengling.Platform.Domain/DomainEvents/TenantDomainEvents.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Fengling.Platform.Domain.DomainEvents;
-
-using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
-
-public record TenantCreatedDomainEvent(Tenant Tenant) : IDomainEvent;
-public record TenantStatusChangedDomainEvent(Tenant Tenant, TenantStatus OldStatus, TenantStatus NewStatus) : IDomainEvent;
diff --git a/Fengling.Platform.Domain/Fengling.Platform.Domain.csproj b/Fengling.Platform.Domain/Fengling.Platform.Domain.csproj
index dddb017..cbb48aa 100644
--- a/Fengling.Platform.Domain/Fengling.Platform.Domain.csproj
+++ b/Fengling.Platform.Domain/Fengling.Platform.Domain.csproj
@@ -9,6 +9,7 @@
+
diff --git a/Fengling.Platform.Infrastructure/Configurations/TenantConfiguration.cs b/Fengling.Platform.Infrastructure/Configurations/TenantConfiguration.cs
index cee4c4e..6aab795 100644
--- a/Fengling.Platform.Infrastructure/Configurations/TenantConfiguration.cs
+++ b/Fengling.Platform.Infrastructure/Configurations/TenantConfiguration.cs
@@ -11,7 +11,7 @@ public class TenantConfiguration : IEntityTypeConfiguration
builder.ToTable("Platform_Tenants");
builder.HasKey(t => t.Id);
- builder.Property(t => t.Id).UseSnowFlakeValueGenerator();
+ builder.Property(t => t.Id).ValueGeneratedOnAdd();
builder.Property(t => t.TenantCode)
.IsRequired()
diff --git a/Fengling.Platform.Infrastructure/Fengling.Platform.Infrastructure.csproj b/Fengling.Platform.Infrastructure/Fengling.Platform.Infrastructure.csproj
index 7bafdc3..a8c251e 100644
--- a/Fengling.Platform.Infrastructure/Fengling.Platform.Infrastructure.csproj
+++ b/Fengling.Platform.Infrastructure/Fengling.Platform.Infrastructure.csproj
@@ -9,6 +9,7 @@
+
diff --git a/Fengling.Platform.Infrastructure/ITenantStore.cs b/Fengling.Platform.Infrastructure/ITenantStore.cs
new file mode 100644
index 0000000..be69c8c
--- /dev/null
+++ b/Fengling.Platform.Infrastructure/ITenantStore.cs
@@ -0,0 +1,38 @@
+namespace Fengling.Platform.Infrastructure;
+
+using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
+
+public interface ITenantStore
+{
+ Task FindByIdAsync(long tenantId, CancellationToken cancellationToken = default);
+ Task FindByTenantCodeAsync(string tenantCode, CancellationToken cancellationToken = default);
+ Task> GetAllAsync(CancellationToken cancellationToken = default);
+ Task> GetPagedAsync(int page, int pageSize, string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
+ Task GetCountAsync(string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
+ Task GetUserCountAsync(long tenantId, CancellationToken cancellationToken = default);
+ Task CreateAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default);
+
+ Task GetTenantCodeAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetNameAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetContactNameAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetContactEmailAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetContactPhoneAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetMaxUsersAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetDescriptionAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetStatusAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetExpiresAtAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetCreatedAtAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetUpdatedAtAsync(Tenant tenant, CancellationToken cancellationToken = default);
+
+ Task SetTenantCodeAsync(Tenant tenant, string code, CancellationToken cancellationToken = default);
+ Task SetNameAsync(Tenant tenant, string name, CancellationToken cancellationToken = default);
+ Task SetContactNameAsync(Tenant tenant, string name, CancellationToken cancellationToken = default);
+ Task SetContactEmailAsync(Tenant tenant, string email, CancellationToken cancellationToken = default);
+ Task SetContactPhoneAsync(Tenant tenant, string? phone, CancellationToken cancellationToken = default);
+ Task SetMaxUsersAsync(Tenant tenant, int? maxUsers, CancellationToken cancellationToken = default);
+ Task SetDescriptionAsync(Tenant tenant, string? description, CancellationToken cancellationToken = default);
+ Task SetStatusAsync(Tenant tenant, TenantStatus status, CancellationToken cancellationToken = default);
+ Task SetExpiresAtAsync(Tenant tenant, DateTime? expiresAt, CancellationToken cancellationToken = default);
+}
diff --git a/Fengling.Platform.Infrastructure/PlatformDbContext.cs b/Fengling.Platform.Infrastructure/PlatformDbContext.cs
index fe59fe6..9dbb419 100644
--- a/Fengling.Platform.Infrastructure/PlatformDbContext.cs
+++ b/Fengling.Platform.Infrastructure/PlatformDbContext.cs
@@ -1,14 +1,17 @@
namespace Fengling.Platform.Infrastructure;
+using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
-using MediatR;
+using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
-using NetCorePal.Extensions.Repository.EntityFrameworkCore;
-public partial class PlatformDbContext(DbContextOptions options, IMediator mediator)
- : AppDbContextBase(options, mediator)
+public partial class PlatformDbContext(DbContextOptions options)
+ : IdentityDbContext(options)
{
public DbSet Tenants => Set();
+ public DbSet AccessLogs => Set();
+ public DbSet AuditLogs => Set();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -17,13 +20,64 @@ public partial class PlatformDbContext(DbContextOptions optio
throw new ArgumentNullException(nameof(modelBuilder));
}
+ modelBuilder.Entity(entity =>
+ {
+ entity.Property(e => e.RealName).HasMaxLength(100);
+ entity.Property(e => e.Phone).HasMaxLength(20);
+ entity.HasIndex(e => e.Phone).IsUnique();
+
+ entity.OwnsOne(e => e.TenantInfo, navigationBuilder =>
+ {
+ navigationBuilder.Property(t => t.TenantCode).HasColumnName("TenantCode");
+ navigationBuilder.Property(t => t.TenantId).HasColumnName("TenantId");
+ navigationBuilder.Property(t => t.TenantName).HasColumnName("TenantName");
+ navigationBuilder.WithOwner();
+ });
+ });
+
+ modelBuilder.Entity(entity =>
+ {
+ entity.Property(e => e.Description).HasMaxLength(200);
+ });
+
+ modelBuilder.Entity(entity =>
+ {
+ entity.HasKey(e => e.Id);
+ entity.HasIndex(e => e.CreatedAt);
+ entity.HasIndex(e => e.UserName);
+ entity.HasIndex(e => e.TenantId);
+ entity.HasIndex(e => e.Action);
+ entity.HasIndex(e => e.Status);
+ entity.Property(e => e.UserName).HasMaxLength(50);
+ entity.Property(e => e.TenantId).HasMaxLength(50);
+ entity.Property(e => e.Action).HasMaxLength(20);
+ entity.Property(e => e.Resource).HasMaxLength(200);
+ entity.Property(e => e.Method).HasMaxLength(10);
+ entity.Property(e => e.IpAddress).HasMaxLength(50);
+ entity.Property(e => e.UserAgent).HasMaxLength(500);
+ entity.Property(e => e.Status).HasMaxLength(20);
+ });
+
+ modelBuilder.Entity(entity =>
+ {
+ entity.HasKey(e => e.Id);
+ entity.HasIndex(e => e.CreatedAt);
+ entity.HasIndex(e => e.Operator);
+ entity.HasIndex(e => e.TenantId);
+ entity.HasIndex(e => e.Operation);
+ entity.HasIndex(e => e.Action);
+ entity.Property(e => e.Operator).HasMaxLength(50);
+ entity.Property(e => e.TenantId).HasMaxLength(50);
+ entity.Property(e => e.Operation).HasMaxLength(20);
+ entity.Property(e => e.Action).HasMaxLength(20);
+ entity.Property(e => e.TargetType).HasMaxLength(50);
+ entity.Property(e => e.TargetName).HasMaxLength(100);
+ entity.Property(e => e.IpAddress).HasMaxLength(50);
+ entity.Property(e => e.Description).HasMaxLength(500);
+ entity.Property(e => e.Status).HasMaxLength(20);
+ });
+
modelBuilder.ApplyConfigurationsFromAssembly(typeof(PlatformDbContext).Assembly);
base.OnModelCreating(modelBuilder);
}
-
- protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
- {
- ConfigureStronglyTypedIdValueConverter(configurationBuilder);
- base.ConfigureConventions(configurationBuilder);
- }
}
diff --git a/Fengling.Platform.Infrastructure/Repositories/TenantRepository.cs b/Fengling.Platform.Infrastructure/Repositories/TenantRepository.cs
deleted file mode 100644
index fd67ecd..0000000
--- a/Fengling.Platform.Infrastructure/Repositories/TenantRepository.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using NetCorePal.Extensions.Repository.EntityFrameworkCore;
-
-namespace Fengling.Platform.Infrastructure.Repositories;
-
-using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
-using NetCorePal.Extensions.Repository;
-using Microsoft.EntityFrameworkCore;
-
-public interface ITenantRepository : IRepository
-{
- Task GetByTenantIdAsync(string tenantId, CancellationToken cancellationToken = default);
- Task GetByIdIncludeH5Async(TenantId id, CancellationToken cancellationToken = default);
- Task GetByIdAsync(long id, CancellationToken cancellationToken = default);
- Task> GetPagedAsync(int page, int pageSize, string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
- Task CountAsync(string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
- Task GetUserCountAsync(TenantId tenantId, CancellationToken cancellationToken = default);
-}
-
-public class TenantRepository(PlatformDbContext context)
- : RepositoryBase(context), ITenantRepository
-{
- public async Task GetByTenantIdAsync(string tenantId, CancellationToken cancellationToken = default)
- {
- return await DbContext.Tenants.FirstOrDefaultAsync(t => t.TenantCode == tenantId, cancellationToken);
- }
-
- public async Task GetByIdIncludeH5Async(TenantId id, CancellationToken cancellationToken = default)
- {
- return await DbContext.Tenants.FirstOrDefaultAsync(t => t.Id == id, cancellationToken);
- }
-
- public async Task GetByIdAsync(long id, CancellationToken cancellationToken = default)
- {
- return await DbContext.Tenants.FindAsync(new object[] { id }, cancellationToken);
- }
-
- public async Task> GetPagedAsync(int page, int pageSize, string? name = null,
- string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default)
- {
- var query = DbContext.Tenants.AsQueryable();
-
- if (!string.IsNullOrEmpty(name))
- query = query.Where(t => t.Name.Contains(name));
-
- if (!string.IsNullOrEmpty(tenantCode))
- query = query.Where(t => t.TenantCode.Contains(tenantCode));
-
- if (status.HasValue)
- query = query.Where(t => t.Status == status);
-
- return await query
- .OrderByDescending(t => t.CreatedAt)
- .Skip((page - 1) * pageSize)
- .Take(pageSize)
- .ToListAsync(cancellationToken);
- }
-
- public async Task CountAsync(string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default)
- {
- var query = DbContext.Tenants.AsQueryable();
-
- if (!string.IsNullOrEmpty(name))
- query = query.Where(t => t.Name.Contains(name));
-
- if (!string.IsNullOrEmpty(tenantCode))
- query = query.Where(t => t.TenantCode.Contains(tenantCode));
-
- if (status.HasValue)
- query = query.Where(t => t.Status == status);
-
- return await query.CountAsync(cancellationToken);
- }
-
- public async Task GetUserCountAsync(TenantId tenantId, CancellationToken cancellationToken = default)
- {
- return 0;
- }
-}
diff --git a/Fengling.Platform.Infrastructure/SeedData.cs b/Fengling.Platform.Infrastructure/SeedData.cs
index 2876ad3..daa30c7 100644
--- a/Fengling.Platform.Infrastructure/SeedData.cs
+++ b/Fengling.Platform.Infrastructure/SeedData.cs
@@ -15,8 +15,15 @@ public static class SeedData
return adminTenant;
}
- adminTenant = new Tenant("Administrator", "超级系统",
- "", "");
+ adminTenant = new Tenant
+ {
+ TenantCode = "admin",
+ Name = "超级系统",
+ ContactName = "",
+ ContactEmail = "",
+ Status = TenantStatus.Active,
+ CreatedAt = DateTime.UtcNow
+ };
await context.Tenants.AddAsync(adminTenant);
await context.SaveChangesAsync();
return adminTenant;
diff --git a/Fengling.Platform.Infrastructure/TenantManager.cs b/Fengling.Platform.Infrastructure/TenantManager.cs
new file mode 100644
index 0000000..e5132d5
--- /dev/null
+++ b/Fengling.Platform.Infrastructure/TenantManager.cs
@@ -0,0 +1,81 @@
+namespace Fengling.Platform.Infrastructure;
+
+using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
+using Microsoft.AspNetCore.Identity;
+
+public interface ITenantManager
+{
+ Task FindByIdAsync(long tenantId, CancellationToken cancellationToken = default);
+ Task FindByTenantCodeAsync(string tenantCode, CancellationToken cancellationToken = default);
+ Task> GetAllAsync(CancellationToken cancellationToken = default);
+ Task> GetPagedAsync(int page, int pageSize, string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
+ Task GetCountAsync(string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
+ Task CreateAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default);
+ Task GetUserCountAsync(long tenantId, CancellationToken cancellationToken = default);
+ Task SetTenantCodeAsync(Tenant tenant, string code, CancellationToken cancellationToken = default);
+}
+
+public class TenantManager : ITenantManager
+{
+ private readonly ITenantStore _store;
+
+ public TenantManager(ITenantStore store)
+ {
+ _store = store;
+ }
+
+ public virtual async Task FindByIdAsync(long tenantId, CancellationToken cancellationToken = default)
+ {
+ return await _store.FindByIdAsync(tenantId, cancellationToken);
+ }
+
+ public virtual async Task FindByTenantCodeAsync(string tenantCode, CancellationToken cancellationToken = default)
+ {
+ return await _store.FindByTenantCodeAsync(tenantCode, cancellationToken);
+ }
+
+ public virtual async Task> GetAllAsync(CancellationToken cancellationToken = default)
+ {
+ return await _store.GetAllAsync(cancellationToken);
+ }
+
+ public virtual async Task> GetPagedAsync(int page, int pageSize, string? name = null,
+ string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default)
+ {
+ return await _store.GetPagedAsync(page, pageSize, name, tenantCode, status, cancellationToken);
+ }
+
+ public virtual async Task GetCountAsync(string? name = null, string? tenantCode = null,
+ TenantStatus? status = null, CancellationToken cancellationToken = default)
+ {
+ return await _store.GetCountAsync(name, tenantCode, status, cancellationToken);
+ }
+
+ public virtual async Task CreateAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ {
+ return await _store.CreateAsync(tenant, cancellationToken);
+ }
+
+ public virtual async Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ {
+ return await _store.UpdateAsync(tenant, cancellationToken);
+ }
+
+ public virtual async Task DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ {
+ return await _store.DeleteAsync(tenant, cancellationToken);
+ }
+
+ public virtual async Task GetUserCountAsync(long tenantId, CancellationToken cancellationToken = default)
+ {
+ return await _store.GetUserCountAsync(tenantId, cancellationToken);
+ }
+
+ public virtual async Task SetTenantCodeAsync(Tenant tenant, string code, CancellationToken cancellationToken = default)
+ {
+ await _store.SetTenantCodeAsync(tenant, code, cancellationToken);
+ return IdentityResult.Success;
+ }
+}
diff --git a/Fengling.Platform.Infrastructure/TenantStore.cs b/Fengling.Platform.Infrastructure/TenantStore.cs
new file mode 100644
index 0000000..4c4b979
--- /dev/null
+++ b/Fengling.Platform.Infrastructure/TenantStore.cs
@@ -0,0 +1,186 @@
+namespace Fengling.Platform.Infrastructure;
+
+using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
+using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
+
+public class TenantStore : ITenantStore
+{
+ private readonly PlatformDbContext _context;
+ private readonly DbSet _tenants;
+
+ public TenantStore(PlatformDbContext context)
+ {
+ _context = context;
+ _tenants = context.Tenants;
+ }
+
+ public void Dispose() { }
+
+ public virtual Task FindByIdAsync(long tenantId, CancellationToken cancellationToken = default)
+ {
+ return _tenants.FirstOrDefaultAsync(t => t.Id == tenantId, cancellationToken);
+ }
+
+ public virtual Task FindByTenantCodeAsync(string tenantCode, CancellationToken cancellationToken = default)
+ {
+ return _tenants.FirstOrDefaultAsync(t => t.TenantCode == tenantCode, cancellationToken);
+ }
+
+ public virtual async Task> GetAllAsync(CancellationToken cancellationToken = default)
+ {
+ return await _tenants.ToListAsync(cancellationToken);
+ }
+
+ public virtual async Task> GetPagedAsync(int page, int pageSize, string? name = null,
+ string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default)
+ {
+ var query = _tenants.AsQueryable();
+
+ if (!string.IsNullOrEmpty(name))
+ query = query.Where(t => t.Name.Contains(name));
+
+ if (!string.IsNullOrEmpty(tenantCode))
+ query = query.Where(t => t.TenantCode.Contains(tenantCode));
+
+ if (status.HasValue)
+ query = query.Where(t => t.Status == status);
+
+ return await query
+ .OrderByDescending(t => t.CreatedAt)
+ .Skip((page - 1) * pageSize)
+ .Take(pageSize)
+ .ToListAsync(cancellationToken);
+ }
+
+ public virtual async Task GetCountAsync(string? name = null, string? tenantCode = null,
+ TenantStatus? status = null, CancellationToken cancellationToken = default)
+ {
+ var query = _tenants.AsQueryable();
+
+ if (!string.IsNullOrEmpty(name))
+ query = query.Where(t => t.Name.Contains(name));
+
+ if (!string.IsNullOrEmpty(tenantCode))
+ query = query.Where(t => t.TenantCode.Contains(tenantCode));
+
+ if (status.HasValue)
+ query = query.Where(t => t.Status == status);
+
+ return await query.CountAsync(cancellationToken);
+ }
+
+ public virtual async Task CreateAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ {
+ _tenants.Add(tenant);
+ await _context.SaveChangesAsync(cancellationToken);
+ return IdentityResult.Success;
+ }
+
+ public virtual async Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ {
+ tenant.UpdatedAt = DateTime.UtcNow;
+ _tenants.Update(tenant);
+ await _context.SaveChangesAsync(cancellationToken);
+ return IdentityResult.Success;
+ }
+
+ public virtual async Task DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ {
+ _tenants.Remove(tenant);
+ await _context.SaveChangesAsync(cancellationToken);
+ return IdentityResult.Success;
+ }
+
+ public virtual async Task GetUserCountAsync(long tenantId, CancellationToken cancellationToken = default)
+ {
+ return await _context.Users.CountAsync(u => u.TenantInfo.TenantId == tenantId && !u.IsDeleted, cancellationToken);
+ }
+
+ public virtual Task GetTenantCodeAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.TenantCode);
+
+ public virtual Task GetNameAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.Name);
+
+ public virtual Task GetContactNameAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.ContactName);
+
+ public virtual Task GetContactEmailAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.ContactEmail);
+
+ public virtual Task GetContactPhoneAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.ContactPhone);
+
+ public virtual Task GetMaxUsersAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.MaxUsers);
+
+ public virtual Task GetDescriptionAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.Description);
+
+ public virtual Task GetStatusAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.Status);
+
+ public virtual Task GetExpiresAtAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.ExpiresAt);
+
+ public virtual Task GetCreatedAtAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.CreatedAt);
+
+ public virtual Task GetUpdatedAtAsync(Tenant tenant, CancellationToken cancellationToken = default)
+ => Task.FromResult(tenant.UpdatedAt);
+
+ public virtual Task SetTenantCodeAsync(Tenant tenant, string code, CancellationToken cancellationToken = default)
+ {
+ tenant.TenantCode = code;
+ return Task.CompletedTask;
+ }
+
+ public virtual Task SetNameAsync(Tenant tenant, string name, CancellationToken cancellationToken = default)
+ {
+ tenant.Name = name;
+ return Task.CompletedTask;
+ }
+
+ public virtual Task SetContactNameAsync(Tenant tenant, string name, CancellationToken cancellationToken = default)
+ {
+ tenant.ContactName = name;
+ return Task.CompletedTask;
+ }
+
+ public virtual Task SetContactEmailAsync(Tenant tenant, string email, CancellationToken cancellationToken = default)
+ {
+ tenant.ContactEmail = email;
+ return Task.CompletedTask;
+ }
+
+ public virtual Task SetContactPhoneAsync(Tenant tenant, string? phone, CancellationToken cancellationToken = default)
+ {
+ tenant.ContactPhone = phone;
+ return Task.CompletedTask;
+ }
+
+ public virtual Task SetMaxUsersAsync(Tenant tenant, int? maxUsers, CancellationToken cancellationToken = default)
+ {
+ tenant.MaxUsers = maxUsers;
+ return Task.CompletedTask;
+ }
+
+ public virtual Task SetDescriptionAsync(Tenant tenant, string? description, CancellationToken cancellationToken = default)
+ {
+ tenant.Description = description;
+ return Task.CompletedTask;
+ }
+
+ public virtual Task SetStatusAsync(Tenant tenant, TenantStatus status, CancellationToken cancellationToken = default)
+ {
+ tenant.Status = status;
+ return Task.CompletedTask;
+ }
+
+ public virtual Task SetExpiresAtAsync(Tenant tenant, DateTime? expiresAt, CancellationToken cancellationToken = default)
+ {
+ tenant.ExpiresAt = expiresAt;
+ return Task.CompletedTask;
+ }
+}