Add user management domain and seed model

This commit is contained in:
2026-08-18 15:43:33 +05:30
parent 878847302e
commit ea2e5229c8
11 changed files with 267 additions and 2 deletions
+27
View File
@@ -15,10 +15,37 @@ public class AppDBContext(DbContextOptions<AppDBContext> options) : DbContext(op
public DbSet<OrderItem> OrderItems { get; set; } public DbSet<OrderItem> OrderItems { get; set; }
public DbSet<DeliveryRun> DeliveryRuns { get; set; } public DbSet<DeliveryRun> DeliveryRuns { get; set; }
public DbSet<Item> Items { get; set; } public DbSet<Item> Items { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Permission> Permissions { get; set; }
public DbSet<RolePermission> RolePermissions { get; set; }
public DbSet<UserRegionScope> UserRegionScopes { get; set; }
public DbSet<UserRouteScope> UserRouteScopes { get; set; }
public DbSet<UserStoreScope> UserStoreScopes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
modelBuilder.Entity<OrderItem>() modelBuilder.Entity<OrderItem>()
.HasKey(oi => new { oi.FK_OrderId, oi.FK_ItemId }); .HasKey(oi => new { oi.FK_OrderId, oi.FK_ItemId });
modelBuilder.Entity<RolePermission>()
.HasKey(rp => new { rp.FK_RoleId, rp.FK_PermissionId });
modelBuilder.Entity<UserRegionScope>()
.HasKey(s => new { s.FK_UserId, s.FK_RegionId });
modelBuilder.Entity<UserRouteScope>()
.HasKey(s => new { s.FK_UserId, s.FK_RouteId });
modelBuilder.Entity<UserStoreScope>()
.HasKey(s => new { s.FK_UserId, s.FK_StoreId });
modelBuilder.Entity<User>()
.HasOne(u => u.ReportsTo)
.WithMany()
.HasForeignKey(u => u.FK_ReportsToUserId)
.OnDelete(DeleteBehavior.Restrict);
Seed.RoleAndPermissionSeed.Apply(modelBuilder);
} }
} }
@@ -0,0 +1,129 @@
using Distribution.Models;
using Microsoft.EntityFrameworkCore;
namespace Distribution.Data.Seed;
public static class RoleAndPermissionSeed
{
private static readonly DateTime SeedDate = new(2026, 8, 18);
private static readonly Guid SalesRepId = new("10000000-0000-0000-0000-000000000001");
private static readonly Guid SupervisorId = new("10000000-0000-0000-0000-000000000002");
private static readonly Guid AreaManagerId = new("10000000-0000-0000-0000-000000000003");
private static readonly Guid AreaSupervisorId = new("10000000-0000-0000-0000-000000000004");
private static readonly Guid RegionalManagerId = new("10000000-0000-0000-0000-000000000005");
private static readonly Guid RegionalAgencyId = new("10000000-0000-0000-0000-000000000006");
private static readonly (string Code, string Description) [] PermissionDefs =
[
("regions.view", "View regions"),
("regions.create", "Create regions"),
("regions.update", "Update regions"),
("regions.delete", "Delete regions"),
("routes.view", "View routes"),
("routes.create", "Create routes"),
("routes.update", "Update routes"),
("routes.delete", "Delete routes"),
("vehicles.view", "View vehicles"),
("vehicles.create", "Create vehicles"),
("vehicles.update", "Update vehicles"),
("vehicles.delete", "Delete vehicles"),
("stores.view", "View stores"),
("stores.create", "Create stores"),
("stores.update", "Update stores"),
("stores.delete", "Delete stores"),
("warehouses.view", "View warehouses"),
("warehouses.create", "Create warehouses"),
("warehouses.update", "Update warehouses"),
("warehouses.delete", "Delete warehouses"),
("orders.view", "View orders"),
("orders.create", "Create orders"),
("orders.update", "Update orders"),
("orders.delete", "Delete orders"),
("users.view", "View users"),
("users.create", "Create users"),
("users.update", "Update users"),
("users.deactivate", "Deactivate users"),
("users.assign_role", "Assign a role to a user"),
("users.assign_scope", "Assign region/route/store scope to a user"),
];
private static readonly Dictionary<string, Guid> PermissionIds = PermissionDefs
.Select((p, i) => (p.Code, Id: DeterministicPermissionId(i)))
.ToDictionary(x => x.Code, x => x.Id);
private static Guid DeterministicPermissionId(int index) =>
new($"20000000-0000-0000-0000-{index + 1:D12}");
public static void Apply(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Role>().HasData(
new Role { Id = SalesRepId, Name = "SalesRep", Level = 10, CreatedAt = SeedDate, IsActive = true },
new Role { Id = SupervisorId, Name = "Supervisor", Level = 20, CreatedAt = SeedDate, IsActive = true },
new Role { Id = AreaManagerId, Name = "AreaManager", Level = 30, CreatedAt = SeedDate, IsActive = true },
new Role { Id = AreaSupervisorId, Name = "AreaSupervisor", Level = 40, CreatedAt = SeedDate, IsActive = true },
new Role { Id = RegionalManagerId, Name = "RegionalManager", Level = 50, CreatedAt = SeedDate, IsActive = true },
new Role { Id = RegionalAgencyId, Name = "RegionalAgency", Level = 60, CreatedAt = SeedDate, IsActive = true }
);
modelBuilder.Entity<Permission>().HasData(
PermissionDefs.Select(p => new Permission
{
Id = PermissionIds[p.Code],
Code = p.Code,
Description = p.Description,
CreatedAt = SeedDate,
IsActive = true
})
);
var salesRepPerms = new[] { "orders.view", "orders.create", "orders.update", "stores.view", "routes.view" };
var supervisorPerms = salesRepPerms.Concat(["orders.delete", "stores.create", "stores.update"]).ToArray();
var areaManagerPerms = supervisorPerms.Concat(["vehicles.view", "warehouses.view"]).ToArray();
var areaSupervisorPerms = areaManagerPerms.Concat(["routes.create", "routes.update"]).ToArray();
var regionalManagerPerms = new[]
{
"orders.view", "orders.create", "orders.update", "orders.delete",
"stores.view", "stores.create", "stores.update", "stores.delete",
"routes.view", "routes.create", "routes.update", "routes.delete",
"vehicles.view", "vehicles.create", "vehicles.update", "vehicles.delete",
"warehouses.view", "warehouses.create", "warehouses.update", "warehouses.delete",
"regions.view", "users.view"
};
var regionalAgencyPerms = PermissionDefs.Select(p => p.Code).ToArray();
var rolePermissionMap = new (Guid RoleId, string[] Codes)[]
{
(SalesRepId, salesRepPerms),
(SupervisorId, supervisorPerms),
(AreaManagerId, areaManagerPerms),
(AreaSupervisorId, areaSupervisorPerms),
(RegionalManagerId, regionalManagerPerms),
(RegionalAgencyId, regionalAgencyPerms),
};
modelBuilder.Entity<RolePermission>().HasData(
rolePermissionMap.SelectMany(rp => rp.Codes.Select(code => new RolePermission
{
Id = DeterministicRolePermissionId(rp.RoleId, PermissionIds[code]),
FK_RoleId = rp.RoleId,
FK_PermissionId = PermissionIds[code],
CreatedAt = SeedDate,
IsActive = true
}))
);
}
private static Guid DeterministicRolePermissionId(Guid roleId, Guid permissionId)
{
using var md5 = System.Security.Cryptography.MD5.Create();
var bytes = md5.ComputeHash(roleId.ToByteArray().Concat(permissionId.ToByteArray()).ToArray());
return new Guid(bytes);
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Distribution.Models
{
public class Permission : Template
{
public string Code { get; set; }
public string Description { get; set; }
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Distribution.Models
{
public class Role : Template
{
public string Name { get; set; }
public int Level { get; set; }
}
}
+15
View File
@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Distribution.Models
{
public class RolePermission : Template
{
public Guid FK_RoleId { get; set; }
[ForeignKey("FK_RoleId")]
public Role Role { get; set; }
public Guid FK_PermissionId { get; set; }
[ForeignKey("FK_PermissionId")]
public Permission Permission { get; set; }
}
}
+7 -1
View File
@@ -1,4 +1,6 @@
namespace Distribution.Models using System.ComponentModel.DataAnnotations.Schema;
namespace Distribution.Models
{ {
public class Route : Template public class Route : Template
{ {
@@ -6,5 +8,9 @@
public String StartPoint { get; set; } public String StartPoint { get; set; }
public String EndPoint { get; set; } public String EndPoint { get; set; }
public decimal Length { get; set; } = 0; public decimal Length { get; set; } = 0;
public Guid FK_RegionId { get; set; }
[ForeignKey("FK_RegionId")]
public Region Region { get; set; }
} }
} }
+7 -1
View File
@@ -1,4 +1,6 @@
namespace Distribution.Models using System.ComponentModel.DataAnnotations.Schema;
namespace Distribution.Models
{ {
public class Store : Template public class Store : Template
{ {
@@ -9,5 +11,9 @@
public decimal Longitude { get; set; } = 0; public decimal Longitude { get; set; } = 0;
public decimal OneTimeAmount { get; set; } = 0; public decimal OneTimeAmount { get; set; } = 0;
public decimal CreditAmount { get; set; } = 0; public decimal CreditAmount { get; set; } = 0;
public Guid FK_RouteId { get; set; }
[ForeignKey("FK_RouteId")]
public Route Route { get; set; }
} }
} }
+21
View File
@@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Distribution.Models
{
public class User : Template
{
public string Username { get; set; }
public string PasswordHash { get; set; }
public string FullName { get; set; }
public Guid FK_RoleId { get; set; }
[ForeignKey("FK_RoleId")]
public Role Role { get; set; }
public Guid? FK_ReportsToUserId { get; set; }
[ForeignKey("FK_ReportsToUserId")]
public User ReportsTo { get; set; }
public DateTime? UpdatedAt { get; set; }
}
}
+15
View File
@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Distribution.Models
{
public class UserRegionScope : Template
{
public Guid FK_UserId { get; set; }
[ForeignKey("FK_UserId")]
public User User { get; set; }
public Guid FK_RegionId { get; set; }
[ForeignKey("FK_RegionId")]
public Region Region { get; set; }
}
}
+15
View File
@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Distribution.Models
{
public class UserRouteScope : Template
{
public Guid FK_UserId { get; set; }
[ForeignKey("FK_UserId")]
public User User { get; set; }
public Guid FK_RouteId { get; set; }
[ForeignKey("FK_RouteId")]
public Route Route { get; set; }
}
}
+15
View File
@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Distribution.Models
{
public class UserStoreScope : Template
{
public Guid FK_UserId { get; set; }
[ForeignKey("FK_UserId")]
public User User { get; set; }
public Guid FK_StoreId { get; set; }
[ForeignKey("FK_StoreId")]
public Store Store { get; set; }
}
}