nonatomic commit. check description for the list of changes

feat: add UserManagementService

feat: add CopmanyDriver relation and DriverManagementService

chore: pupulate database seeding class

fix: add review filtering by CompanyId
This commit is contained in:
cuqmbr 2023-05-02 14:57:46 +03:00
parent ceaba3eb2d
commit a75ea56f69
35 changed files with 3220 additions and 93 deletions

View File

@ -1,3 +1,4 @@
using System.Dynamic;
using AutoMapper; using AutoMapper;
using Server.Models; using Server.Models;
using SharedModels.DataTransferObjects; using SharedModels.DataTransferObjects;
@ -86,6 +87,12 @@ public class MapperInitializer : Profile
CreateMap<User, UserDto>().ReverseMap(); CreateMap<User, UserDto>().ReverseMap();
CreateMap<User, CreateUserDto>().ReverseMap(); CreateMap<User, CreateUserDto>().ReverseMap();
CreateMap<User, UpdateUserDto>().ReverseMap(); CreateMap<User, UpdateUserDto>().ReverseMap();
CreateMap<User, StrippedUserDto>().ReverseMap();
CreateMap<User, DriverDto>().ForMember(d => d.CompanyId, o => o.MapFrom(s => s.Employer.CompanyId));
CreateMap<UserDto, DriverDto>().ReverseMap();
CreateMap<CreateUserDto, CreateDriverDto>().ReverseMap();
CreateMap<CreateDriverDto, UpdateDriverDto>().ReverseMap();
CreateMap<RouteAddressDetails, RouteAddressDetailsDto>().ReverseMap(); CreateMap<RouteAddressDetails, RouteAddressDetailsDto>().ReverseMap();
CreateMap<RouteAddressDetails, CreateRouteAddressDetailsDto>().ReverseMap(); CreateMap<RouteAddressDetails, CreateRouteAddressDetailsDto>().ReverseMap();

View File

@ -3,7 +3,7 @@ namespace Server.Configurations;
public class SmtpCredentials public class SmtpCredentials
{ {
public string Host { get; set; } = null!; public string Host { get; set; } = null!;
public string Port { get; set; } public int Port { get; set; }
public string User { get; set; } = null!; public string User { get; set; } = null!;
public string Password { get; set; } = null!; public string Password { get; set; } = null!;
} }

View File

@ -0,0 +1,86 @@
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Server.Services;
using SharedModels.DataTransferObjects;
using SharedModels.QueryParameters.Objects;
namespace Server.Controllers;
[Route("api/drivers")]
[ApiController]
public class DriverController : ControllerBase
{
private readonly IDriverManagementService _driverManagementService;
public DriverController(IDriverManagementService driverManagementService)
{
_driverManagementService = driverManagementService;
}
[HttpPost]
public async Task<IActionResult> AddDriver(CreateDriverDto Driver)
{
var result = await _driverManagementService.AddDriver(Driver);
if (!result.isSucceeded)
{
return result.actionResult;
}
return CreatedAtAction(nameof(GetDriver), new {id = result.driver.Id}, result.driver);
}
[HttpGet]
public async Task<IActionResult> GetDrivers([FromQuery] CompanyDriverParameters parameters)
{
var result = await _driverManagementService.GetDrivers(parameters);
if (!result.isSucceeded)
{
return result.actionResult;
}
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
return Ok(result.drivers);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetDriver(string id, [FromQuery] string? fields)
{
var result = await _driverManagementService.GetDriver(id, fields);
if (!result.isSucceeded)
{
return result.actionResult;
}
return Ok(result.driver);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateDriver(string id, UpdateDriverDto driver)
{
var result = await _driverManagementService.UpdateDriver(id, driver);
if (!result.isSucceeded)
{
return result.actionResult;
}
return Ok(result.driver);
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteDriver(string id)
{
var result = await _driverManagementService.DeleteDriver(id);
if (!result.isSucceed)
{
return result.actionResult;
}
return NoContent();
}
}

View File

@ -0,0 +1,86 @@
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Server.Services;
using SharedModels.DataTransferObjects;
using SharedModels.QueryParameters.Objects;
namespace Server.Controllers;
[Route("api/users")]
[ApiController]
public class UserController : ControllerBase
{
private readonly IUserManagementService _userManagementService;
public UserController(IUserManagementService userManagementService)
{
_userManagementService = userManagementService;
}
[HttpPost]
public async Task<IActionResult> AddUser(CreateUserDto User)
{
var result = await _userManagementService.AddUser(User);
if (!result.isSucceeded)
{
return result.actionResult;
}
return CreatedAtAction(nameof(GetUser), new {id = result.user.Id}, result.user);
}
[HttpGet]
public async Task<IActionResult> GetUsers([FromQuery] UserParameters parameters)
{
var result = await _userManagementService.GetUsers(parameters);
if (!result.isSucceeded)
{
return result.actionResult;
}
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
return Ok(result.users);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(string id, [FromQuery] string? fields)
{
var result = await _userManagementService.GetUser(id, fields);
if (!result.isSucceeded)
{
return result.actionResult;
}
return Ok(result.user);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateUser(string id, UpdateUserDto user)
{
var result = await _userManagementService.UpdateUser(id, user);
if (!result.isSucceeded)
{
return result.actionResult;
}
return Ok(result.user);
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteUser(string id)
{
var result = await _userManagementService.DeleteUser(id);
if (!result.isSucceed)
{
return result.actionResult;
}
return NoContent();
}
}

View File

@ -12,12 +12,16 @@ public class ApplicationDbContext : IdentityDbContext<User>
{ {
Database.EnsureCreated(); Database.EnsureCreated();
} }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().Ignore(u => u.UserName).Ignore(u => u.NormalizedUserName); modelBuilder.Entity<User>().Ignore(u => u.UserName).Ignore(u => u.NormalizedUserName);
modelBuilder.Entity<CompanyDriver>().HasKey(cd => new { cd.CompanyId, cd.DriverId });
} }
public DbSet<Company> Companies { get; set; } = null!; public DbSet<Company> Companies { get; set; } = null!;
public DbSet<CompanyDriver> CompanyDrivers { get; set; } = null!;
public DbSet<Vehicle> Vehicles { get; set; } = null!; public DbSet<Vehicle> Vehicles { get; set; } = null!;
public DbSet<VehicleEnrollment> VehicleEnrollments { get; set; } = null!; public DbSet<VehicleEnrollment> VehicleEnrollments { get; set; } = null!;
public DbSet<Route> Routes { get; set; } = null!; public DbSet<Route> Routes { get; set; } = null!;

View File

@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Server.Models; using Server.Models;
using Utils;
using Route = Server.Models.Route;
namespace Server.Data; namespace Server.Data;
@ -8,25 +10,314 @@ public class SeedData
public static async Task Initialize(IServiceProvider serviceProvider) public static async Task Initialize(IServiceProvider serviceProvider)
{ {
var userManager = (UserManager<User>)serviceProvider.GetService(typeof(UserManager<User>))!; var userManager = (UserManager<User>)serviceProvider.GetService(typeof(UserManager<User>))!;
var roleManager = (RoleManager<IdentityRole>)serviceProvider.GetService(typeof(RoleManager<IdentityRole>))!; userManager.UserValidators.Clear();
//Seed Roles var roleManager = (RoleManager<IdentityRole>)serviceProvider.GetService(typeof(RoleManager<IdentityRole>))!;
foreach (var role in Enum.GetValues(typeof(Constants.Identity.Roles))) var dbContext = (ApplicationDbContext)serviceProvider.GetService(typeof(ApplicationDbContext))!;
// Seed Roles
foreach (var role in Enum.GetValues(typeof(Identity.Roles)))
{ {
await roleManager.CreateAsync(new IdentityRole(role.ToString())); await roleManager.CreateAsync(new IdentityRole(role.ToString()));
} }
//Seed Default User User? companyUser;
var defaultUser = new User User driverUser;
if (!dbContext.Users.Any())
{ {
Email = Constants.Identity.DefaultEmail, // Seed Administrator user
var adminUser = new User
{
FirstName = "user", LastName = "user", Patronymic = "user",
Email = "admin@autobus.com",
EmailConfirmed = true,
};
await userManager.CreateAsync(adminUser, Identity.DefaultPassword);
await userManager.AddToRoleAsync(adminUser, Identity.Roles.Administrator.ToString());
// Seed Company user
companyUser = new User
{
FirstName = "user", LastName = "user", Patronymic = "user",
Email = "company@autobus.com",
EmailConfirmed = true EmailConfirmed = true
}; };
if (userManager.Users.All(u => u.Id != defaultUser.Id)) await userManager.CreateAsync(companyUser, Identity.DefaultPassword);
await userManager.AddToRoleAsync(companyUser, Identity.Roles.Company.ToString());
// Seed Driver user
driverUser = new User
{ {
await userManager.CreateAsync(defaultUser, Constants.Identity.DefaultPassword); FirstName = "user", LastName = "user", Patronymic = "user",
await userManager.AddToRoleAsync(defaultUser, Constants.Identity.DefaultRole.ToString()); Email = "driver@autobus.com",
EmailConfirmed = true
};
await userManager.CreateAsync(driverUser, Identity.DefaultPassword);
await userManager.AddToRoleAsync(driverUser, Identity.Roles.Driver.ToString());
// Seed Default user
var defaultUser = new User
{
FirstName = "user", LastName = "user", Patronymic = "user",
Email = "user@autobus.com",
EmailConfirmed = true
};
await userManager.CreateAsync(defaultUser, Identity.DefaultPassword);
await userManager.AddToRoleAsync(defaultUser, Identity.Roles.User.ToString());
}
companyUser = await userManager.FindByEmailAsync("company@autobus.com");
driverUser = await userManager.FindByEmailAsync("driver@autobus.com");
if (!dbContext.Companies.Any())
{
// Seed County - State - City - Address relations
await dbContext.Countries.AddRangeAsync(new Country { Name = "Ukraine", Code = "UA" });
await dbContext.SaveChangesAsync();
await dbContext.States.AddRangeAsync(new State { CountryId = 1, Name = "Kharkiv Oblast" });
await dbContext.SaveChangesAsync();
await dbContext.Cities.AddRangeAsync(new City { StateId = 1, Name = "Osykovyi Hai" },
new City { StateId = 1, Name = "Malynivka" },
new City { StateId = 1, Name = "Chuhuiv" },
new City { StateId = 1, Name = "Kam'yana Yaruha" },
new City { StateId = 1, Name = "Rohan'" },
new City { StateId = 1, Name = "Kharkiv" },
new City { StateId = 1, Name = "Korobochkyne" },
new City { StateId = 1, Name = "Kochetok" },
new City { StateId = 1, Name = "Kytsivka" });
await dbContext.SaveChangesAsync();
await dbContext.Addresses.AddRangeAsync(new Address { CityId = 1, Name = "station near E40 road", Latitude = 36.778044, Longitude = 49.777235 },
new Address { CityId = 2, Name = "v. Malynivka", Latitude = 36.732271, Longitude = 49.813638 },
new Address { CityId = 3, Name = "Chuhuyivsʹka Avtostantsiya, Vulytsya Kharkivsʹka, 133", Latitude = 36.687534, Longitude = 49.838337 },
new Address { CityId = 4, Name = "Kulynychi", Latitude = 36.595877, Longitude = 49.880585 },
new Address { CityId = 5, Name = "v. Rohan", Latitude = 36.495910, Longitude = 49.907080 },
new Address { CityId = 6, Name = "Kharkiv Parking Near Mcdonalds", Latitude = 36.207202, Longitude = 49.987560 },
new Address { CityId = 7, Name = "Ukrposhta 63540, Chuhuivs'ka St, 2Б", Latitude = 36.812743, Longitude = 49.776519 },
new Address { CityId = 8, Name = "Pivnyk", Latitude = 36.712386, Longitude = 49.867814 },
new Address { CityId = 9, Name = "v. Kytsivka", Latitude = 36.819010, Longitude = 49.869401 });
await dbContext.SaveChangesAsync();
// Seed Route - RouteAddresses relations
await dbContext.Routes.AddRangeAsync(
new Route
{
Type = "default",
RouteAddresses = new List<RouteAddress>
{
new RouteAddress { Order = 1, AddressId = 1},
new RouteAddress { Order = 2, AddressId = 2},
new RouteAddress { Order = 3, AddressId = 3},
new RouteAddress { Order = 4, AddressId = 4},
new RouteAddress { Order = 5, AddressId = 5},
new RouteAddress { Order = 6, AddressId = 6},
}
},
new Route
{
Type = "default",
RouteAddresses = new List<RouteAddress>
{
new RouteAddress { Order = 1, AddressId = 7},
new RouteAddress { Order = 2, AddressId = 2},
new RouteAddress { Order = 3, AddressId = 3},
new RouteAddress { Order = 4, AddressId = 8},
new RouteAddress { Order = 5, AddressId = 9},
}
});
await dbContext.SaveChangesAsync();
await dbContext.Companies.AddAsync(
new Company
{
OwnerId = companyUser.Id,
Name = "Default Company",
CompanyDrivers = new []
{
new CompanyDriver
{
DriverId = driverUser.Id
}
}
});
await dbContext.SaveChangesAsync();
await dbContext.Vehicles.AddRangeAsync(
new Vehicle
{
Number = "AC8376BH",
Type = "Cruiser",
Capacity = 50,
CompanyId = 1,
HasClimateControl = true,
HasWiFi = true,
HasWC = true,
HasStewardess = true,
HasTV = false,
HasOutlet = true,
HasBelts = true,
},
new Vehicle
{
Number = "HA8934MK",
Type = "Lon Rider",
Capacity = 25,
CompanyId = 1,
HasClimateControl = false,
HasWiFi = false,
HasWC = false,
HasStewardess = false,
HasTV = false,
HasOutlet = false,
HasBelts = false,
});
await dbContext.SaveChangesAsync();
await dbContext.VehicleEnrollments.AddRangeAsync(
new VehicleEnrollment
{
RouteId = 1,
VehicleId = 1,
DepartureDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 07, 45, 00, DateTimeKind.Utc),
RouteAddressDetails = new List<RouteAddressDetails>
{
new RouteAddressDetails
{
RouteAddressId = 1,
TimeSpanToNextCity = TimeSpan.FromMinutes(20),
CostToNextCity = 25
},
new RouteAddressDetails
{
RouteAddressId = 2,
TimeSpanToNextCity = TimeSpan.FromMinutes(5),
WaitTimeSpan = TimeSpan.FromMinutes(15),
CostToNextCity = 20
},
new RouteAddressDetails
{
RouteAddressId = 3,
TimeSpanToNextCity = TimeSpan.FromMinutes(5),
WaitTimeSpan = TimeSpan.FromMinutes(25),
CostToNextCity = 30
},
new RouteAddressDetails
{
RouteAddressId = 4,
TimeSpanToNextCity = TimeSpan.FromMinutes(5),
WaitTimeSpan = TimeSpan.FromMinutes(20),
CostToNextCity = 35
},
new RouteAddressDetails
{
RouteAddressId = 5,
TimeSpanToNextCity = TimeSpan.FromMinutes(5),
WaitTimeSpan = TimeSpan.FromMinutes(30),
CostToNextCity = 40
},
new RouteAddressDetails
{
RouteAddressId = 6,
}
}
},
new VehicleEnrollment
{
RouteId = 2,
VehicleId = 2,
DepartureDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc),
RouteAddressDetails = new List<RouteAddressDetails>
{
new RouteAddressDetails
{
RouteAddressId = 7,
TimeSpanToNextCity = TimeSpan.FromMinutes(20),
CostToNextCity = 25
},
new RouteAddressDetails
{
RouteAddressId = 8,
TimeSpanToNextCity = TimeSpan.FromMinutes(5),
WaitTimeSpan = TimeSpan.FromMinutes(15),
CostToNextCity = 20
},
new RouteAddressDetails
{
RouteAddressId = 9,
TimeSpanToNextCity = TimeSpan.FromMinutes(5),
WaitTimeSpan = TimeSpan.FromMinutes(25),
CostToNextCity = 30
},
new RouteAddressDetails
{
RouteAddressId = 10,
TimeSpanToNextCity = TimeSpan.FromMinutes(5),
WaitTimeSpan = TimeSpan.FromMinutes(20),
CostToNextCity = 35
},
new RouteAddressDetails
{
RouteAddressId = 11,
}
}
});
await dbContext.SaveChangesAsync();
await dbContext.TicketGroups.AddAsync(
new TicketGroup
{
UserId = companyUser.Id,
Tickets = new List<Ticket>
{
new Ticket
{
VehicleEnrollmentId = 1,
PurchaseDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc).AddDays(-1),
FirstRouteAddressId = 1,
LastRouteAddressId = 2
},
new Ticket
{
VehicleEnrollmentId = 2,
PurchaseDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc).AddDays(-1),
FirstRouteAddressId = 2,
LastRouteAddressId = 9
}
}
});
dbContext.Reviews.AddRangeAsync(
new Review
{
UserId = companyUser.Id,
VehicleEnrollmentId = 1,
Rating = 85,
Comment = "Good overall",
PostDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc).AddDays(1),
}, new Review
{
UserId = companyUser.Id,
VehicleEnrollmentId = 2,
Rating = 100,
Comment = "Amazing",
PostDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc).AddDays(1),
});
await dbContext.SaveChangesAsync();
} }
} }
} }

View File

@ -0,0 +1,922 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Server.Data;
#nullable disable
namespace Server.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20230430115750_Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table")]
partial class Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("RoleId")
.HasColumnType("text");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CityId")
.HasColumnType("integer");
b.Property<double>("Latitude")
.HasColumnType("double precision");
b.Property<double>("Longitude")
.HasColumnType("double precision");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CityId");
b.ToTable("Addresses");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("StateId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("StateId");
b.ToTable("Cities");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("OwnerId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("OwnerId")
.IsUnique();
b.ToTable("Companies");
});
modelBuilder.Entity("Server.Models.Country", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("Server.Models.Review", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Comment")
.HasColumnType("text");
b.Property<DateTime>("PostDateTimeUtc")
.HasColumnType("timestamp with time zone");
b.Property<int>("Rating")
.HasColumnType("integer");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("VehicleEnrollmentId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("UserId");
b.HasIndex("VehicleEnrollmentId");
b.ToTable("Reviews");
});
modelBuilder.Entity("Server.Models.Route", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Routes");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("AddressId")
.HasColumnType("integer");
b.Property<int>("Order")
.HasColumnType("integer");
b.Property<int>("RouteAddressDetailsId")
.HasColumnType("integer");
b.Property<int>("RouteId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("AddressId");
b.HasIndex("RouteId");
b.ToTable("RouteAddresses");
});
modelBuilder.Entity("Server.Models.RouteAddressDetails", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double>("CostToNextCity")
.HasColumnType("double precision");
b.Property<int>("RouteAddressId")
.HasColumnType("integer");
b.Property<TimeSpan>("TimeSpanToNextCity")
.HasColumnType("interval");
b.Property<int>("VehicleEnrollmentId")
.HasColumnType("integer");
b.Property<TimeSpan>("WaitTimeSpan")
.HasColumnType("interval");
b.HasKey("Id");
b.HasIndex("RouteAddressId");
b.HasIndex("VehicleEnrollmentId");
b.ToTable("RouteAddressDetails");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CountryId")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("States");
});
modelBuilder.Entity("Server.Models.Ticket", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("FirstRouteAddressId")
.HasColumnType("integer");
b.Property<bool>("IsMissed")
.HasColumnType("boolean");
b.Property<bool>("IsReturned")
.HasColumnType("boolean");
b.Property<int>("LastRouteAddressId")
.HasColumnType("integer");
b.Property<DateTime>("PurchaseDateTimeUtc")
.HasColumnType("timestamp with time zone");
b.Property<int>("TicketGroupId")
.HasColumnType("integer");
b.Property<int>("VehicleEnrollmentId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("TicketGroupId");
b.HasIndex("VehicleEnrollmentId");
b.ToTable("Tickets");
});
modelBuilder.Entity("Server.Models.TicketGroup", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("TicketGroups");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<DateTime?>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<int?>("Document")
.HasColumnType("integer");
b.Property<string>("DocumentDetails")
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("Gender")
.HasColumnType("integer");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("Patronymic")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("Capacity")
.HasColumnType("integer");
b.Property<int>("CompanyId")
.HasColumnType("integer");
b.Property<bool>("HasBelts")
.HasColumnType("boolean");
b.Property<bool>("HasClimateControl")
.HasColumnType("boolean");
b.Property<bool>("HasOutlet")
.HasColumnType("boolean");
b.Property<bool>("HasStewardess")
.HasColumnType("boolean");
b.Property<bool>("HasTV")
.HasColumnType("boolean");
b.Property<bool>("HasWC")
.HasColumnType("boolean");
b.Property<bool>("HasWiFi")
.HasColumnType("boolean");
b.Property<string>("Number")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CompanyId");
b.ToTable("Vehicles");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("CancelationComment")
.HasColumnType("text");
b.Property<TimeSpan?>("DelayTimeSpan")
.HasColumnType("interval");
b.Property<DateTime>("DepartureDateTimeUtc")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsCanceled")
.HasColumnType("boolean");
b.Property<int>("RouteId")
.HasColumnType("integer");
b.Property<int>("VehicleId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("RouteId");
b.HasIndex("VehicleId");
b.ToTable("VehicleEnrollments");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.HasOne("Server.Models.City", "City")
.WithMany("Addresses")
.HasForeignKey("CityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("City");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.HasOne("Server.Models.State", "State")
.WithMany("Cities")
.HasForeignKey("StateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("State");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.HasOne("Server.Models.User", "Owner")
.WithOne("Company")
.HasForeignKey("Server.Models.Company", "OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
});
modelBuilder.Entity("Server.Models.Review", b =>
{
b.HasOne("Server.Models.User", "User")
.WithMany("Reviews")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
.WithMany("Reviews")
.HasForeignKey("VehicleEnrollmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
b.Navigation("VehicleEnrollment");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.HasOne("Server.Models.Address", "Address")
.WithMany("RouteAddresses")
.HasForeignKey("AddressId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.Route", "Route")
.WithMany("RouteAddresses")
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Address");
b.Navigation("Route");
});
modelBuilder.Entity("Server.Models.RouteAddressDetails", b =>
{
b.HasOne("Server.Models.RouteAddress", "RouteAddress")
.WithMany("RouteAddressDetails")
.HasForeignKey("RouteAddressId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
.WithMany("RouteAddressDetails")
.HasForeignKey("VehicleEnrollmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("RouteAddress");
b.Navigation("VehicleEnrollment");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.HasOne("Server.Models.Country", "Country")
.WithMany("States")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Country");
});
modelBuilder.Entity("Server.Models.Ticket", b =>
{
b.HasOne("Server.Models.TicketGroup", "TicketGroup")
.WithMany("Tickets")
.HasForeignKey("TicketGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
.WithMany("Tickets")
.HasForeignKey("VehicleEnrollmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("TicketGroup");
b.Navigation("VehicleEnrollment");
});
modelBuilder.Entity("Server.Models.TicketGroup", b =>
{
b.HasOne("Server.Models.User", "User")
.WithMany("TicketGroups")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 =>
{
b1.Property<string>("UserId")
.HasColumnType("text");
b1.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<int>("Id"));
b1.Property<DateTime>("CreationDateTime")
.HasColumnType("timestamp with time zone");
b1.Property<DateTime>("ExpiryDateTime")
.HasColumnType("timestamp with time zone");
b1.Property<DateTime?>("Revoked")
.HasColumnType("timestamp with time zone");
b1.Property<string>("Token")
.IsRequired()
.HasColumnType("text");
b1.HasKey("UserId", "Id");
b1.ToTable("RefreshToken");
b1.WithOwner()
.HasForeignKey("UserId");
});
b.Navigation("RefreshTokens");
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.HasOne("Server.Models.Company", "Company")
.WithMany("Vehicles")
.HasForeignKey("CompanyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Company");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.HasOne("Server.Models.Route", "Route")
.WithMany("VehicleEnrollments")
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.Vehicle", "Vehicle")
.WithMany("VehicleEnrollments")
.HasForeignKey("VehicleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Route");
b.Navigation("Vehicle");
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.Navigation("RouteAddresses");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.Navigation("Addresses");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.Navigation("Vehicles");
});
modelBuilder.Entity("Server.Models.Country", b =>
{
b.Navigation("States");
});
modelBuilder.Entity("Server.Models.Route", b =>
{
b.Navigation("RouteAddresses");
b.Navigation("VehicleEnrollments");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.Navigation("RouteAddressDetails");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.Navigation("Cities");
});
modelBuilder.Entity("Server.Models.TicketGroup", b =>
{
b.Navigation("Tickets");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.Navigation("Company")
.IsRequired();
b.Navigation("Reviews");
b.Navigation("TicketGroups");
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.Navigation("VehicleEnrollments");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.Navigation("Reviews");
b.Navigation("RouteAddressDetails");
b.Navigation("Tickets");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Server.Migrations
{
public partial class Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Companies_OwnerId",
table: "Companies");
migrationBuilder.AddColumn<DateTime>(
name: "BirthDate",
table: "AspNetUsers",
type: "timestamp with time zone",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "Document",
table: "AspNetUsers",
type: "integer",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "DocumentDetails",
table: "AspNetUsers",
type: "text",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "Gender",
table: "AspNetUsers",
type: "integer",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Companies_OwnerId",
table: "Companies",
column: "OwnerId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Companies_OwnerId",
table: "Companies");
migrationBuilder.DropColumn(
name: "BirthDate",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "Document",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "DocumentDetails",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "Gender",
table: "AspNetUsers");
migrationBuilder.CreateIndex(
name: "IX_Companies_OwnerId",
table: "Companies",
column: "OwnerId");
}
}
}

View File

@ -0,0 +1,962 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Server.Data;
#nullable disable
namespace Server.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20230501105408_Add_CompanyDriver_relation")]
partial class Add_CompanyDriver_relation
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("RoleId")
.HasColumnType("text");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CityId")
.HasColumnType("integer");
b.Property<double>("Latitude")
.HasColumnType("double precision");
b.Property<double>("Longitude")
.HasColumnType("double precision");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CityId");
b.ToTable("Addresses");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("StateId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("StateId");
b.ToTable("Cities");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("OwnerId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("OwnerId")
.IsUnique();
b.ToTable("Companies");
});
modelBuilder.Entity("Server.Models.CompanyDriver", b =>
{
b.Property<int>("CompanyId")
.HasColumnType("integer");
b.Property<string>("DriverId")
.HasColumnType("text");
b.HasKey("CompanyId", "DriverId");
b.HasIndex("DriverId")
.IsUnique();
b.ToTable("CompanyDrivers");
});
modelBuilder.Entity("Server.Models.Country", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("Server.Models.Review", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Comment")
.HasColumnType("text");
b.Property<DateTime>("PostDateTimeUtc")
.HasColumnType("timestamp with time zone");
b.Property<int>("Rating")
.HasColumnType("integer");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("VehicleEnrollmentId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("UserId");
b.HasIndex("VehicleEnrollmentId");
b.ToTable("Reviews");
});
modelBuilder.Entity("Server.Models.Route", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Routes");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("AddressId")
.HasColumnType("integer");
b.Property<int>("Order")
.HasColumnType("integer");
b.Property<int>("RouteAddressDetailsId")
.HasColumnType("integer");
b.Property<int>("RouteId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("AddressId");
b.HasIndex("RouteId");
b.ToTable("RouteAddresses");
});
modelBuilder.Entity("Server.Models.RouteAddressDetails", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<double>("CostToNextCity")
.HasColumnType("double precision");
b.Property<int>("RouteAddressId")
.HasColumnType("integer");
b.Property<TimeSpan>("TimeSpanToNextCity")
.HasColumnType("interval");
b.Property<int>("VehicleEnrollmentId")
.HasColumnType("integer");
b.Property<TimeSpan>("WaitTimeSpan")
.HasColumnType("interval");
b.HasKey("Id");
b.HasIndex("RouteAddressId");
b.HasIndex("VehicleEnrollmentId");
b.ToTable("RouteAddressDetails");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CountryId")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("States");
});
modelBuilder.Entity("Server.Models.Ticket", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("FirstRouteAddressId")
.HasColumnType("integer");
b.Property<bool>("IsMissed")
.HasColumnType("boolean");
b.Property<bool>("IsReturned")
.HasColumnType("boolean");
b.Property<int>("LastRouteAddressId")
.HasColumnType("integer");
b.Property<DateTime>("PurchaseDateTimeUtc")
.HasColumnType("timestamp with time zone");
b.Property<int>("TicketGroupId")
.HasColumnType("integer");
b.Property<int>("VehicleEnrollmentId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("TicketGroupId");
b.HasIndex("VehicleEnrollmentId");
b.ToTable("Tickets");
});
modelBuilder.Entity("Server.Models.TicketGroup", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("TicketGroups");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<DateTime?>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<int?>("Document")
.HasColumnType("integer");
b.Property<string>("DocumentDetails")
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("Gender")
.HasColumnType("integer");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("Patronymic")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("Capacity")
.HasColumnType("integer");
b.Property<int>("CompanyId")
.HasColumnType("integer");
b.Property<bool>("HasBelts")
.HasColumnType("boolean");
b.Property<bool>("HasClimateControl")
.HasColumnType("boolean");
b.Property<bool>("HasOutlet")
.HasColumnType("boolean");
b.Property<bool>("HasStewardess")
.HasColumnType("boolean");
b.Property<bool>("HasTV")
.HasColumnType("boolean");
b.Property<bool>("HasWC")
.HasColumnType("boolean");
b.Property<bool>("HasWiFi")
.HasColumnType("boolean");
b.Property<string>("Number")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CompanyId");
b.ToTable("Vehicles");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("CancelationComment")
.HasColumnType("text");
b.Property<TimeSpan?>("DelayTimeSpan")
.HasColumnType("interval");
b.Property<DateTime>("DepartureDateTimeUtc")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsCanceled")
.HasColumnType("boolean");
b.Property<int>("RouteId")
.HasColumnType("integer");
b.Property<int>("VehicleId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("RouteId");
b.HasIndex("VehicleId");
b.ToTable("VehicleEnrollments");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.HasOne("Server.Models.City", "City")
.WithMany("Addresses")
.HasForeignKey("CityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("City");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.HasOne("Server.Models.State", "State")
.WithMany("Cities")
.HasForeignKey("StateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("State");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.HasOne("Server.Models.User", "Owner")
.WithOne("Company")
.HasForeignKey("Server.Models.Company", "OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
});
modelBuilder.Entity("Server.Models.CompanyDriver", b =>
{
b.HasOne("Server.Models.Company", "Company")
.WithMany("CompanyDrivers")
.HasForeignKey("CompanyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.User", "Driver")
.WithOne("Employer")
.HasForeignKey("Server.Models.CompanyDriver", "DriverId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Company");
b.Navigation("Driver");
});
modelBuilder.Entity("Server.Models.Review", b =>
{
b.HasOne("Server.Models.User", "User")
.WithMany("Reviews")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
.WithMany("Reviews")
.HasForeignKey("VehicleEnrollmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
b.Navigation("VehicleEnrollment");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.HasOne("Server.Models.Address", "Address")
.WithMany("RouteAddresses")
.HasForeignKey("AddressId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.Route", "Route")
.WithMany("RouteAddresses")
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Address");
b.Navigation("Route");
});
modelBuilder.Entity("Server.Models.RouteAddressDetails", b =>
{
b.HasOne("Server.Models.RouteAddress", "RouteAddress")
.WithMany("RouteAddressDetails")
.HasForeignKey("RouteAddressId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
.WithMany("RouteAddressDetails")
.HasForeignKey("VehicleEnrollmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("RouteAddress");
b.Navigation("VehicleEnrollment");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.HasOne("Server.Models.Country", "Country")
.WithMany("States")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Country");
});
modelBuilder.Entity("Server.Models.Ticket", b =>
{
b.HasOne("Server.Models.TicketGroup", "TicketGroup")
.WithMany("Tickets")
.HasForeignKey("TicketGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
.WithMany("Tickets")
.HasForeignKey("VehicleEnrollmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("TicketGroup");
b.Navigation("VehicleEnrollment");
});
modelBuilder.Entity("Server.Models.TicketGroup", b =>
{
b.HasOne("Server.Models.User", "User")
.WithMany("TicketGroups")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 =>
{
b1.Property<string>("UserId")
.HasColumnType("text");
b1.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<int>("Id"));
b1.Property<DateTime>("CreationDateTime")
.HasColumnType("timestamp with time zone");
b1.Property<DateTime>("ExpiryDateTime")
.HasColumnType("timestamp with time zone");
b1.Property<DateTime?>("Revoked")
.HasColumnType("timestamp with time zone");
b1.Property<string>("Token")
.IsRequired()
.HasColumnType("text");
b1.HasKey("UserId", "Id");
b1.ToTable("RefreshToken");
b1.WithOwner()
.HasForeignKey("UserId");
});
b.Navigation("RefreshTokens");
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.HasOne("Server.Models.Company", "Company")
.WithMany("Vehicles")
.HasForeignKey("CompanyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Company");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.HasOne("Server.Models.Route", "Route")
.WithMany("VehicleEnrollments")
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.Vehicle", "Vehicle")
.WithMany("VehicleEnrollments")
.HasForeignKey("VehicleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Route");
b.Navigation("Vehicle");
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.Navigation("RouteAddresses");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.Navigation("Addresses");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.Navigation("CompanyDrivers");
b.Navigation("Vehicles");
});
modelBuilder.Entity("Server.Models.Country", b =>
{
b.Navigation("States");
});
modelBuilder.Entity("Server.Models.Route", b =>
{
b.Navigation("RouteAddresses");
b.Navigation("VehicleEnrollments");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.Navigation("RouteAddressDetails");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.Navigation("Cities");
});
modelBuilder.Entity("Server.Models.TicketGroup", b =>
{
b.Navigation("Tickets");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.Navigation("Company")
.IsRequired();
b.Navigation("Employer")
.IsRequired();
b.Navigation("Reviews");
b.Navigation("TicketGroups");
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.Navigation("VehicleEnrollments");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.Navigation("Reviews");
b.Navigation("RouteAddressDetails");
b.Navigation("Tickets");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Server.Migrations
{
public partial class Add_CompanyDriver_relation : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CompanyDrivers",
columns: table => new
{
DriverId = table.Column<string>(type: "text", nullable: false),
CompanyId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CompanyDrivers", x => new { x.CompanyId, x.DriverId });
table.ForeignKey(
name: "FK_CompanyDrivers_AspNetUsers_DriverId",
column: x => x.DriverId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_CompanyDrivers_Companies_CompanyId",
column: x => x.CompanyId,
principalTable: "Companies",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_CompanyDrivers_DriverId",
table: "CompanyDrivers",
column: "DriverId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CompanyDrivers");
}
}
}

View File

@ -179,7 +179,7 @@ namespace Server.Migrations
b.HasIndex("CityId"); b.HasIndex("CityId");
b.ToTable("Addresses"); b.ToTable("Addresses", (string)null);
}); });
modelBuilder.Entity("Server.Models.City", b => modelBuilder.Entity("Server.Models.City", b =>
@ -201,7 +201,7 @@ namespace Server.Migrations
b.HasIndex("StateId"); b.HasIndex("StateId");
b.ToTable("Cities"); b.ToTable("Cities", (string)null);
}); });
modelBuilder.Entity("Server.Models.Company", b => modelBuilder.Entity("Server.Models.Company", b =>
@ -222,9 +222,26 @@ namespace Server.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("OwnerId"); b.HasIndex("OwnerId")
.IsUnique();
b.ToTable("Companies"); b.ToTable("Companies", (string)null);
});
modelBuilder.Entity("Server.Models.CompanyDriver", b =>
{
b.Property<int>("CompanyId")
.HasColumnType("integer");
b.Property<string>("DriverId")
.HasColumnType("text");
b.HasKey("CompanyId", "DriverId");
b.HasIndex("DriverId")
.IsUnique();
b.ToTable("CompanyDrivers", (string)null);
}); });
modelBuilder.Entity("Server.Models.Country", b => modelBuilder.Entity("Server.Models.Country", b =>
@ -245,7 +262,7 @@ namespace Server.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Countries"); b.ToTable("Countries", (string)null);
}); });
modelBuilder.Entity("Server.Models.Review", b => modelBuilder.Entity("Server.Models.Review", b =>
@ -278,7 +295,7 @@ namespace Server.Migrations
b.HasIndex("VehicleEnrollmentId"); b.HasIndex("VehicleEnrollmentId");
b.ToTable("Reviews"); b.ToTable("Reviews", (string)null);
}); });
modelBuilder.Entity("Server.Models.Route", b => modelBuilder.Entity("Server.Models.Route", b =>
@ -295,7 +312,7 @@ namespace Server.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Routes"); b.ToTable("Routes", (string)null);
}); });
modelBuilder.Entity("Server.Models.RouteAddress", b => modelBuilder.Entity("Server.Models.RouteAddress", b =>
@ -324,7 +341,7 @@ namespace Server.Migrations
b.HasIndex("RouteId"); b.HasIndex("RouteId");
b.ToTable("RouteAddresses"); b.ToTable("RouteAddresses", (string)null);
}); });
modelBuilder.Entity("Server.Models.RouteAddressDetails", b => modelBuilder.Entity("Server.Models.RouteAddressDetails", b =>
@ -356,7 +373,7 @@ namespace Server.Migrations
b.HasIndex("VehicleEnrollmentId"); b.HasIndex("VehicleEnrollmentId");
b.ToTable("RouteAddressDetails"); b.ToTable("RouteAddressDetails", (string)null);
}); });
modelBuilder.Entity("Server.Models.State", b => modelBuilder.Entity("Server.Models.State", b =>
@ -378,7 +395,7 @@ namespace Server.Migrations
b.HasIndex("CountryId"); b.HasIndex("CountryId");
b.ToTable("States"); b.ToTable("States", (string)null);
}); });
modelBuilder.Entity("Server.Models.Ticket", b => modelBuilder.Entity("Server.Models.Ticket", b =>
@ -416,7 +433,7 @@ namespace Server.Migrations
b.HasIndex("VehicleEnrollmentId"); b.HasIndex("VehicleEnrollmentId");
b.ToTable("Tickets"); b.ToTable("Tickets", (string)null);
}); });
modelBuilder.Entity("Server.Models.TicketGroup", b => modelBuilder.Entity("Server.Models.TicketGroup", b =>
@ -435,7 +452,7 @@ namespace Server.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("TicketGroups"); b.ToTable("TicketGroups", (string)null);
}); });
modelBuilder.Entity("Server.Models.User", b => modelBuilder.Entity("Server.Models.User", b =>
@ -446,10 +463,19 @@ namespace Server.Migrations
b.Property<int>("AccessFailedCount") b.Property<int>("AccessFailedCount")
.HasColumnType("integer"); .HasColumnType("integer");
b.Property<DateTime?>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("ConcurrencyStamp") b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken() .IsConcurrencyToken()
.HasColumnType("text"); .HasColumnType("text");
b.Property<int?>("Document")
.HasColumnType("integer");
b.Property<string>("DocumentDetails")
.HasColumnType("text");
b.Property<string>("Email") b.Property<string>("Email")
.HasMaxLength(256) .HasMaxLength(256)
.HasColumnType("character varying(256)"); .HasColumnType("character varying(256)");
@ -461,6 +487,9 @@ namespace Server.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<int?>("Gender")
.HasColumnType("integer");
b.Property<string>("LastName") b.Property<string>("LastName")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
@ -549,7 +578,7 @@ namespace Server.Migrations
b.HasIndex("CompanyId"); b.HasIndex("CompanyId");
b.ToTable("Vehicles"); b.ToTable("Vehicles", (string)null);
}); });
modelBuilder.Entity("Server.Models.VehicleEnrollment", b => modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
@ -584,7 +613,7 @@ namespace Server.Migrations
b.HasIndex("VehicleId"); b.HasIndex("VehicleId");
b.ToTable("VehicleEnrollments"); b.ToTable("VehicleEnrollments", (string)null);
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
@ -663,14 +692,33 @@ namespace Server.Migrations
modelBuilder.Entity("Server.Models.Company", b => modelBuilder.Entity("Server.Models.Company", b =>
{ {
b.HasOne("Server.Models.User", "Owner") b.HasOne("Server.Models.User", "Owner")
.WithMany() .WithOne("Company")
.HasForeignKey("OwnerId") .HasForeignKey("Server.Models.Company", "OwnerId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.Navigation("Owner"); b.Navigation("Owner");
}); });
modelBuilder.Entity("Server.Models.CompanyDriver", b =>
{
b.HasOne("Server.Models.Company", "Company")
.WithMany("CompanyDrivers")
.HasForeignKey("CompanyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.User", "Driver")
.WithOne("Employer")
.HasForeignKey("Server.Models.CompanyDriver", "DriverId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Company");
b.Navigation("Driver");
});
modelBuilder.Entity("Server.Models.Review", b => modelBuilder.Entity("Server.Models.Review", b =>
{ {
b.HasOne("Server.Models.User", "User") b.HasOne("Server.Models.User", "User")
@ -771,7 +819,7 @@ namespace Server.Migrations
modelBuilder.Entity("Server.Models.User", b => modelBuilder.Entity("Server.Models.User", b =>
{ {
b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 => b.OwnsMany("Server.Models.User.RefreshTokens#Server.Models.RefreshToken", "RefreshTokens", b1 =>
{ {
b1.Property<string>("UserId") b1.Property<string>("UserId")
.HasColumnType("text"); .HasColumnType("text");
@ -797,7 +845,7 @@ namespace Server.Migrations
b1.HasKey("UserId", "Id"); b1.HasKey("UserId", "Id");
b1.ToTable("RefreshToken"); b1.ToTable("RefreshToken", (string)null);
b1.WithOwner() b1.WithOwner()
.HasForeignKey("UserId"); .HasForeignKey("UserId");
@ -848,6 +896,8 @@ namespace Server.Migrations
modelBuilder.Entity("Server.Models.Company", b => modelBuilder.Entity("Server.Models.Company", b =>
{ {
b.Navigation("CompanyDrivers");
b.Navigation("Vehicles"); b.Navigation("Vehicles");
}); });
@ -880,6 +930,12 @@ namespace Server.Migrations
modelBuilder.Entity("Server.Models.User", b => modelBuilder.Entity("Server.Models.User", b =>
{ {
b.Navigation("Company")
.IsRequired();
b.Navigation("Employer")
.IsRequired();
b.Navigation("Reviews"); b.Navigation("Reviews");
b.Navigation("TicketGroups"); b.Navigation("TicketGroups");

View File

@ -14,5 +14,6 @@ public class Company
public string OwnerId { get; set; } = null!; public string OwnerId { get; set; } = null!;
public User Owner { get; set; } = null!; public User Owner { get; set; } = null!;
public virtual List<Vehicle> Vehicles { get; set; } = null!; public virtual IList<Vehicle> Vehicles { get; set; } = null!;
public virtual IList<CompanyDriver> CompanyDrivers { get; set; } = null!;
} }

View File

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Server.Models;
public class CompanyDriver
{
[ForeignKey("UserId")]
public string DriverId { get; set; } = null!;
public User Driver { get; set; } = null!;
[ForeignKey("CompanyId")]
public int CompanyId { get; set; }
public Company Company { get; set; } = null!;
}

View File

@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Utils;
namespace Server.Models; namespace Server.Models;
@ -14,8 +15,21 @@ public class User : IdentityUser
[Required(ErrorMessage = "Patronymic is required")] [Required(ErrorMessage = "Patronymic is required")]
public string Patronymic { get; set; } = null!; public string Patronymic { get; set; } = null!;
public DateTime? BirthDate { get; set; }
public Identity.Gender? Gender { get; set; }
public Identity.Document? Document { get; set; }
public string? DocumentDetails { get; set; }
public IList<RefreshToken> RefreshTokens { get; set; } = null!; public IList<RefreshToken> RefreshTokens { get; set; } = null!;
public Company Company { get; set; } = null!;
public virtual IList<TicketGroup> TicketGroups { get; set; } = null!; public virtual IList<TicketGroup> TicketGroups { get; set; } = null!;
public virtual IList<Review> Reviews { get; set; } = null!; public virtual IList<Review> Reviews { get; set; } = null!;
public virtual CompanyDriver? Employer { get; set; } = null!;
public string GetFullName() => $"{LastName} {FirstName} {Patronymic}";
} }

View File

@ -8,12 +8,12 @@ using Microsoft.OpenApi.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using Server.Configurations; using Server.Configurations;
using Server.Constants;
using Server.Data; using Server.Data;
using Server.Helpers; using Server.Helpers;
using Server.Models; using Server.Models;
using Server.Services; using Server.Services;
using SharedModels.DataTransferObjects; using SharedModels.DataTransferObjects;
using Utils;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
var services = builder.Services; var services = builder.Services;
@ -121,8 +121,8 @@ services.AddScoped<IVehicleManagementService, VehicleManagementService>();
services.AddScoped<IVehicleEnrollmentManagementService, VehicleEnrollmentManagementService>(); services.AddScoped<IVehicleEnrollmentManagementService, VehicleEnrollmentManagementService>();
services.AddScoped<IRouteManagementService, RouteManagementService>(); services.AddScoped<IRouteManagementService, RouteManagementService>();
services.AddScoped<IRouteAddressManagementService, RouteAddressManagementService>(); services.AddScoped<IRouteAddressManagementService, RouteAddressManagementService>();
services.AddScoped<IUserManagementService, UserManagementService>();
services.AddScoped<ISortHelper<ExpandoObject>, SortHelper<ExpandoObject>>(); services.AddScoped<IDriverManagementService, DriverManagementService>();
services.AddScoped<IDataShaper<CountryDto>, DataShaper<CountryDto>>(); services.AddScoped<IDataShaper<CountryDto>, DataShaper<CountryDto>>();
services.AddScoped<IDataShaper<StateDto>, DataShaper<StateDto>>(); services.AddScoped<IDataShaper<StateDto>, DataShaper<StateDto>>();
@ -139,15 +139,16 @@ services.AddScoped<IDataShaper<VehicleEnrollmentWithDetailsDto>, DataShaper<Vehi
services.AddScoped<IDataShaper<RouteDto>, DataShaper<RouteDto>>(); services.AddScoped<IDataShaper<RouteDto>, DataShaper<RouteDto>>();
services.AddScoped<IDataShaper<RouteWithAddressesDto>, DataShaper<RouteWithAddressesDto>>(); services.AddScoped<IDataShaper<RouteWithAddressesDto>, DataShaper<RouteWithAddressesDto>>();
services.AddScoped<IDataShaper<RouteAddressDto>, DataShaper<RouteAddressDto>>(); services.AddScoped<IDataShaper<RouteAddressDto>, DataShaper<RouteAddressDto>>();
services.AddScoped<IDataShaper<UserDto>, DataShaper<UserDto>>();
services.AddScoped<IDataShaper<DriverDto>, DataShaper<DriverDto>>();
services.AddScoped<IDataShaper<ExpandoObject>, DataShaper<ExpandoObject>>();
services.AddScoped<ISortHelper<ExpandoObject>, SortHelper<ExpandoObject>>();
services.AddScoped<IPager<ExpandoObject>, Pager<ExpandoObject>>(); services.AddScoped<IPager<ExpandoObject>, Pager<ExpandoObject>>();
services.AddScoped<AutomationService>(); services.AddScoped<AutomationService>();
services.AddScoped<IReportService, ReportService>(); services.AddScoped<IReportService, ReportService>();
services.AddScoped<IStatisticsService, StatisticsService>(); services.AddScoped<IStatisticsService, StatisticsService>();
services.AddScoped<IDataShaper<UserDto>, DataShaper<UserDto>>();
services.AddScoped<IDataShaper<ExpandoObject>, DataShaper<ExpandoObject>>();
// Adding DB Context with PostgreSQL // Adding DB Context with PostgreSQL
var connectionString = configuration.GetConnectionString("DefaultConnection"); var connectionString = configuration.GetConnectionString("DefaultConnection");
@ -162,7 +163,7 @@ var serviceProvider = scope.ServiceProvider;
await SeedData.Initialize(serviceProvider); await SeedData.Initialize(serviceProvider);
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (Convert.ToBoolean(configuration["UseApiExplorer"]))
{ {
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();

View File

@ -37,6 +37,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\SharedModels\SharedModels.csproj" /> <ProjectReference Include="..\SharedModels\SharedModels.csproj" />
<ProjectReference Include="..\Utils\Utils.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -13,6 +13,7 @@ using Server.Constants;
using Server.Models; using Server.Models;
using SharedModels.Requests; using SharedModels.Requests;
using SharedModels.Responses; using SharedModels.Responses;
using Utils;
namespace Server.Services; namespace Server.Services;
@ -75,7 +76,7 @@ public class AuthenticationService : IAuthenticationService
return (false, $"{createUserResult.Errors?.First().Description}"); return (false, $"{createUserResult.Errors?.First().Description}");
} }
await _userManager.AddToRoleAsync(user, Constants.Identity.DefaultRole.ToString()); await _userManager.AddToRoleAsync(user, Identity.DefaultRole.ToString());
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var confirmationLink = _linkGenerator.GetUriByAction(_contextAccessor.HttpContext, var confirmationLink = _linkGenerator.GetUriByAction(_contextAccessor.HttpContext,
@ -285,10 +286,12 @@ public class AuthenticationService : IAuthenticationService
var claims = new[] var claims = new[]
{ {
new Claim(JwtStandardClaimNames.Sub, user.Id), new Claim(JwtStandardClaimNames.Sub, user.Id),
new Claim(JwtStandardClaimNames.Name, $"{user.LastName} {user.FirstName} {user.Patronymic}"), new Claim(JwtStandardClaimNames.Name, user.GetFullName()),
new Claim(JwtStandardClaimNames.GivenName, user.FirstName), new Claim(JwtStandardClaimNames.GivenName, user.FirstName),
new Claim(JwtStandardClaimNames.FamilyName, user.LastName), new Claim(JwtStandardClaimNames.FamilyName, user.LastName),
new Claim(JwtStandardClaimNames.MiddleName, user.Patronymic), new Claim(JwtStandardClaimNames.MiddleName, user.Patronymic),
new Claim(JwtStandardClaimNames.Gender, user.Gender?.ToString() ?? "Undefined"),
new Claim(JwtStandardClaimNames.BirthDate, user.BirthDate?.ToString()?? "Undefined"),
new Claim(JwtStandardClaimNames.Email, user.Email), new Claim(JwtStandardClaimNames.Email, user.Email),
new Claim(JwtStandardClaimNames.EmailVerified, user.EmailConfirmed.ToString()), new Claim(JwtStandardClaimNames.EmailVerified, user.EmailConfirmed.ToString()),
new Claim(JwtRegisteredClaimNames.Exp, DateTime.UtcNow.AddMinutes(_jwt.ValidityInMinutes).ToString(CultureInfo.InvariantCulture)) new Claim(JwtRegisteredClaimNames.Exp, DateTime.UtcNow.AddMinutes(_jwt.ValidityInMinutes).ToString(CultureInfo.InvariantCulture))

View File

@ -0,0 +1,145 @@
using System.Dynamic;
using AutoMapper;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Server.Data;
using Server.Helpers;
using Server.Models;
using SharedModels.DataTransferObjects;
using SharedModels.QueryParameters;
using SharedModels.QueryParameters.Objects;
namespace Server.Services;
public class DriverManagementService : IDriverManagementService
{
private readonly IUserManagementService _userManagementService;
private readonly UserManager<User> _userManager;
private readonly ApplicationDbContext _dbContext;
private readonly IMapper _mapper;
private readonly ISortHelper<ExpandoObject> _userSortHelper;
private readonly IDataShaper<DriverDto> _userDataShaper;
private readonly IPager<ExpandoObject> _pager;
public DriverManagementService(IUserManagementService userManagementService, IMapper mapper,
UserManager<User> userManager, ApplicationDbContext dbContext,
ISortHelper<ExpandoObject> userSortHelper, IDataShaper<DriverDto> userDataShaper,
IPager<ExpandoObject> pager)
{
_userManagementService = userManagementService;
_userManager = userManager;
_dbContext = dbContext;
_userSortHelper = userSortHelper;
_userDataShaper = userDataShaper;
_pager = pager;
_mapper = mapper;
}
public async Task<(bool isSucceeded, IActionResult? actionResult, DriverDto driver)> AddDriver(CreateDriverDto createDriverDto)
{
var createUserDto = _mapper.Map<CreateUserDto>(createDriverDto);
createUserDto.Roles = new List<string> { "Driver" };
createUserDto.Password = createDriverDto.Password;
var result = await _userManagementService.AddUser(createUserDto);
if (!result.isSucceeded)
{
return (false, result.actionResult, null);
}
var driverDto = _mapper.Map<DriverDto>(createUserDto);
_dbContext.CompanyDrivers.Add(new CompanyDriver { CompanyId = createDriverDto.CompanyId, DriverId = driverDto.Id });
await _dbContext.SaveChangesAsync();
driverDto.Roles = result.user.Roles;
return (true, null, driverDto);
}
public async Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable<ExpandoObject> drivers, PagingMetadata<ExpandoObject> pagingMetadata)> GetDrivers(CompanyDriverParameters parameters)
{
var dbUsers = _userManager.Users.Include(u => u.Employer)
.Where(u => u.Employer != null).AsQueryable();
FilterByCompanyId(ref dbUsers, parameters.CompanyId);
SearchByAllUserFields(ref dbUsers, parameters.Search);
var userDtos = _mapper.ProjectTo<DriverDto>(dbUsers);
var shapedData = _userDataShaper.ShapeData(userDtos, parameters.Fields).AsQueryable();
try
{
shapedData = _userSortHelper.ApplySort(shapedData, parameters.Sort);
}
catch (Exception)
{
return (false, new BadRequestObjectResult("Invalid sorting string"), null!, null!);
}
var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber, parameters.PageSize);
return (true, null, shapedData, pagingMetadata);
void FilterByCompanyId(ref IQueryable<User> users, int? compnayId)
{
if (!users.Any() || compnayId == null)
{
return;
}
users = users.Where(u => u.Employer.CompanyId == compnayId);
}
void SearchByAllUserFields(ref IQueryable<User> users, string? search)
{
if (!users.Any() || search == null)
{
return;
}
users = users.Where(u =>
u.FirstName.ToLower().Contains(search.ToLower()) ||
u.LastName.ToLower().Contains(search.ToLower()) ||
u.Patronymic.ToLower().Contains(search.ToLower()) ||
u.Email.ToLower().Contains(search.ToLower()) ||
u.PhoneNumber.ToLower().Contains(search.ToLower()));
}
}
public async Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject driver)> GetDriver(string id, string? fields)
{
var dbUser = await _userManager.Users.Include(u => u.Employer).
FirstOrDefaultAsync(u => u.Id == id);
if (dbUser == null)
{
return (false, new NotFoundResult(), null!);
}
if (String.IsNullOrWhiteSpace(fields))
{
fields = CompanyDriverParameters.DefaultFields;
}
var userDto = _mapper.Map<DriverDto>(dbUser);
var shapedData = _userDataShaper.ShapeData(userDto, fields);
return (true, null, shapedData);
}
public async Task<(bool isSucceeded, IActionResult? actionResult, DriverDto driver)> UpdateDriver(string id, UpdateDriverDto updateDriverDto)
{
var updateUserDto = _mapper.Map<UpdateUserDto>(updateDriverDto);
var result = await _userManagementService.UpdateUser(id, updateUserDto);
return (result.isSucceeded, result.actionResult, _mapper.Map<DriverDto>(result.user));
}
public async Task<(bool isSucceed, IActionResult? actionResult)> DeleteDriver(string id)
{
return await _userManagementService.DeleteUser(id);
}
}

View File

@ -34,7 +34,7 @@ public class EmailSenderService : IEmailSenderService
mailMessage.Subject = $"{applicationName}. {subject}"; mailMessage.Subject = $"{applicationName}. {subject}";
mailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Text) { Text = message}; mailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Text) { Text = message};
await _smtpClient.ConnectAsync(_smtpCredentials.Host, Int32.Parse(_smtpCredentials.Port), false); await _smtpClient.ConnectAsync(_smtpCredentials.Host, _smtpCredentials.Port, false);
await _smtpClient.AuthenticateAsync(Encoding.ASCII, _smtpCredentials.User, _smtpCredentials.Password); await _smtpClient.AuthenticateAsync(Encoding.ASCII, _smtpCredentials.User, _smtpCredentials.Password);
await _smtpClient.SendAsync(mailMessage); await _smtpClient.SendAsync(mailMessage);
await _smtpClient.DisconnectAsync(true); await _smtpClient.DisconnectAsync(true);

View File

@ -0,0 +1,22 @@
using System.Dynamic;
using Microsoft.AspNetCore.Mvc;
using SharedModels.DataTransferObjects;
using SharedModels.QueryParameters;
using SharedModels.QueryParameters.Objects;
namespace Server.Services;
public interface IDriverManagementService
{
Task<(bool isSucceeded, IActionResult? actionResult, DriverDto driver)> AddDriver(CreateDriverDto createDriverDto);
Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable<ExpandoObject> drivers, PagingMetadata<ExpandoObject> pagingMetadata)>
GetDrivers(CompanyDriverParameters parameters);
Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject driver)> GetDriver(string id, string? fields);
Task<(bool isSucceeded, IActionResult? actionResult, DriverDto driver)> UpdateDriver(string id, UpdateDriverDto updateDriverDto);
Task<(bool isSucceed, IActionResult? actionResult)> DeleteDriver(string id);
}

View File

@ -0,0 +1,21 @@
using System.Dynamic;
using Microsoft.AspNetCore.Mvc;
using SharedModels.DataTransferObjects;
using SharedModels.QueryParameters;
using SharedModels.QueryParameters.Objects;
namespace Server.Services;
public interface IUserManagementService
{
Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> AddUser(CreateUserDto createUserDto);
Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable<ExpandoObject> users, PagingMetadata<ExpandoObject> pagingMetadata)>
GetUsers(UserParameters parameters);
Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject user)> GetUser(string id, string? fields);
Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> UpdateUser(string id, UpdateUserDto updateUserDto);
Task<(bool isSucceed, IActionResult? actionResult)> DeleteUser(string id);
}

View File

@ -44,11 +44,14 @@ public class ReviewManagementService : IReviewManagementService
PagingMetadata<ExpandoObject> pagingMetadata)> GetReviews(ReviewParameters parameters) PagingMetadata<ExpandoObject> pagingMetadata)> GetReviews(ReviewParameters parameters)
{ {
var dbReviews = _dbContext.Reviews var dbReviews = _dbContext.Reviews
.Include(r => r.VehicleEnrollment).ThenInclude(ve => ve.Vehicle)
.ThenInclude(v => v.Company).Include(r => r.User)
.AsQueryable(); .AsQueryable();
FilterByReviewRating(ref dbReviews, parameters.FromRating, parameters.ToRating); FilterByReviewRating(ref dbReviews, parameters.FromRating, parameters.ToRating);
FilterByReviewComment(ref dbReviews, parameters.Comment); FilterByReviewComment(ref dbReviews, parameters.Comment);
FilterByReviewUserId(ref dbReviews, parameters.UserId); FilterByReviewUserId(ref dbReviews, parameters.UserId);
FilterByReviewCompanyId(ref dbReviews, parameters.CompanyId);
var reviewDtos = _mapper.ProjectTo<ReviewDto>(dbReviews); var reviewDtos = _mapper.ProjectTo<ReviewDto>(dbReviews);
var shapedData = _reviewDataShaper.ShapeData(reviewDtos, parameters.Fields).AsQueryable(); var shapedData = _reviewDataShaper.ShapeData(reviewDtos, parameters.Fields).AsQueryable();
@ -103,6 +106,16 @@ public class ReviewManagementService : IReviewManagementService
reviews = reviews.Where(r => reviews = reviews.Where(r =>
r.UserId.Contains(userId.ToLower())); r.UserId.Contains(userId.ToLower()));
} }
void FilterByReviewCompanyId(ref IQueryable<Review> reviews, int? companyId)
{
if (!reviews.Any() || companyId == null)
{
return;
}
reviews = reviews.Where(r => r.VehicleEnrollment.Vehicle.CompanyId == companyId);
}
} }
public async Task<(bool isSucceed, IActionResult? actionResult, ExpandoObject review)> GetReview(int id, string? fields) public async Task<(bool isSucceed, IActionResult? actionResult, ExpandoObject review)> GetReview(int id, string? fields)
@ -113,7 +126,8 @@ public class ReviewManagementService : IReviewManagementService
} }
var dbReview = await _dbContext.Reviews.Where(r => r.Id == id) var dbReview = await _dbContext.Reviews.Where(r => r.Id == id)
.Include(r => r.VehicleEnrollment) .Include(r => r.VehicleEnrollment).ThenInclude(ve => ve.Vehicle)
.ThenInclude(v => v.Company).Include(r => r.User)
.FirstAsync(); .FirstAsync();
if (String.IsNullOrWhiteSpace(fields)) if (String.IsNullOrWhiteSpace(fields))

View File

@ -0,0 +1,231 @@
using System.Dynamic;
using AutoMapper;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Server.Data;
using Server.Helpers;
using Server.Models;
using SharedModels.DataTransferObjects;
using SharedModels.QueryParameters;
using SharedModels.QueryParameters.Objects;
namespace Server.Services;
public class UserManagementService : IUserManagementService
{
private readonly UserManager<User> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
private readonly IMapper _mapper;
private readonly ISortHelper<ExpandoObject> _userSortHelper;
private readonly IDataShaper<UserDto> _userDataShaper;
private readonly IPager<ExpandoObject> _pager;
public UserManagementService(IMapper mapper, UserManager<User> userManager, RoleManager<IdentityRole> roleManager,
ISortHelper<ExpandoObject> userSortHelper, IDataShaper<UserDto> userDataShaper, IPager<ExpandoObject> pager,
ApplicationDbContext dbContext)
{
_mapper = mapper;
_userManager = userManager;
_roleManager = roleManager;
_userSortHelper = userSortHelper;
_userDataShaper = userDataShaper;
_pager = pager;
_userManager.UserValidators.Clear();
}
public async Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> AddUser(CreateUserDto createUserDto)
{
var user = _mapper.Map<User>(createUserDto);
user.BirthDate = user.BirthDate == null ? null : new DateTime(user.BirthDate.Value.Ticks, DateTimeKind.Utc);
var userDto = _mapper.Map<UserDto>(user);
userDto.Roles = new List<string>();
if (await _userManager.FindByEmailAsync(user.Email) != null)
{
return (false, new BadRequestObjectResult("Email already registered"), null);
}
if (user.PhoneNumber != null && await _userManager.Users.FirstOrDefaultAsync(u => u.PhoneNumber == user.PhoneNumber) != null)
{
return (false, new BadRequestObjectResult("Phone number already registered"), null);
}
if (createUserDto.Roles != null!)
{
foreach (var role in createUserDto.Roles)
{
if (!await _roleManager.RoleExistsAsync(role))
{
return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null);
}
userDto.Roles.Add(role);
}
}
await _userManager.CreateAsync(user, createUserDto.Password);
await _userManager.AddToRolesAsync(user, createUserDto.Roles);
userDto.Id = user.Id;
return (true, null, userDto);
}
public async Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable<ExpandoObject> users,
PagingMetadata<ExpandoObject> pagingMetadata)> GetUsers(UserParameters parameters)
{
var dbUsers = _userManager.Users.Include(u => u.Company)
.Include(u => u.Reviews).Include(u => u.TicketGroups)
.ThenInclude(tg => tg.Tickets).AsQueryable();
SearchByAllUserFields(ref dbUsers, parameters.Search);
var userDtos = _mapper.ProjectTo<UserDto>(dbUsers);
var shapedData = _userDataShaper.ShapeData(userDtos, parameters.Fields).AsQueryable();
try
{
shapedData = _userSortHelper.ApplySort(shapedData, parameters.Sort);
}
catch (Exception)
{
return (false, new BadRequestObjectResult("Invalid sorting string"), null!, null!);
}
var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber, parameters.PageSize);
if ((bool)parameters.Fields?.Contains("roles"))
{
foreach (var user in shapedData)
{
dynamic dynamicUser = user as IDictionary<string, object>;
var roles = await _userManager.GetRolesAsync(new User { Id = dynamicUser.Id });
dynamicUser.Roles = roles;
}
}
return (true, null, shapedData, pagingMetadata);
void SearchByAllUserFields(ref IQueryable<User> users, string? search)
{
if (!users.Any() || search == null)
{
return;
}
users = users.Where(u =>
u.FirstName.ToLower().Contains(search.ToLower()) ||
u.LastName.ToLower().Contains(search.ToLower()) ||
u.Patronymic.ToLower().Contains(search.ToLower()) ||
u.Email.ToLower().Contains(search.ToLower()) ||
u.PhoneNumber.ToLower().Contains(search.ToLower()));
}
}
public async Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject user)>
GetUser(string id, string? fields)
{
var dbUser = await _userManager.Users.Include(u => u.Employer).
FirstOrDefaultAsync(u => u.Id == id);
if (dbUser == null)
{
return (false, new NotFoundResult(), null!);
}
if (String.IsNullOrWhiteSpace(fields))
{
fields = UserParameters.DefaultFields;
}
var userDto = _mapper.Map<DriverDto>(dbUser);
var shapedData = _userDataShaper.ShapeData(userDto, fields);
return (true, null, shapedData);
}
public async Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)>
UpdateUser(string id, UpdateUserDto updateUserDto)
{
if (id != updateUserDto.Id)
{
return (false, new BadRequestObjectResult("Object and query ids don't match"), null);
}
if (!await _userManager.Users.AnyAsync(u => u.Id == id))
{
return (false, new NotFoundResult(), null);
}
var dbUser = await _userManager.FindByIdAsync(id);
var user = _mapper.Map<User>(updateUserDto);
dbUser.FirstName = user.FirstName;
dbUser.LastName = user.LastName;
dbUser.Patronymic = user.Patronymic;
dbUser.BirthDate = new DateTime(user.BirthDate.Value.Ticks, DateTimeKind.Utc);
dbUser.Gender = user.Gender;
dbUser.Document = user.Document;
dbUser.DocumentDetails = user.DocumentDetails;
dbUser.Email = user.Email;
dbUser.EmailConfirmed = user.EmailConfirmed;
dbUser.PhoneNumber = user.PhoneNumber;
dbUser.PhoneNumberConfirmed = user.PhoneNumberConfirmed;
var userDto = _mapper.Map<UserDto>(user);
userDto.Roles = new List<string>();
updateUserDto.Roles = updateUserDto.Roles == null ? new List<string>() : updateUserDto.Roles;
var roles = await _userManager.GetRolesAsync(user);
var rolesToDelete = roles.Except(updateUserDto.Roles).ToArray();
var rolesToAdd = updateUserDto.Roles.Except(roles).ToArray();
userDto.Roles = roles.Except(rolesToDelete).Union(rolesToAdd).ToList();
foreach (var role in rolesToDelete)
{
if (await _roleManager.RoleExistsAsync(role))
{
await _userManager.RemoveFromRoleAsync(dbUser, role);
}
}
foreach (var role in rolesToAdd)
{
if (!await _roleManager.RoleExistsAsync(role))
{
return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null);
}
}
await _userManager.AddToRolesAsync(dbUser, rolesToAdd);
if (updateUserDto.Password != null)
{
await _userManager.RemovePasswordAsync(dbUser);
await _userManager.AddPasswordAsync(dbUser, updateUserDto.Password);
}
await _userManager.UpdateAsync(dbUser);
return (true, null, userDto);
}
public async Task<(bool isSucceed, IActionResult? actionResult)> DeleteUser(string id)
{
var dbUser = await _userManager.FindByIdAsync(id);
if (dbUser == null)
{
return (false, new NotFoundResult());
}
await _userManager.DeleteAsync(dbUser);
return (true, null);
}
}

View File

@ -12,7 +12,7 @@
"ApplicationName": "auto.bus", "ApplicationName": "auto.bus",
"SmtpCredentials": { "SmtpCredentials": {
"Host": "", "Host": "",
"Port": "", "Port": 587,
"User": "", "User": "",
"Password": "" "Password": ""
}, },

View File

@ -9,10 +9,11 @@
"ConnectionStrings": { "ConnectionStrings": {
"DefaultConnection": "host=localhost;database=auto.bus;user id=postgres;password=postgres;Include Error Detail = true" "DefaultConnection": "host=localhost;database=auto.bus;user id=postgres;password=postgres;Include Error Detail = true"
}, },
"UseApiExplorer": true,
"ApplicationName": "auto.bus", "ApplicationName": "auto.bus",
"SmtpCredentials": { "SmtpCredentials": {
"Host": "", "Host": "",
"Port": "", "Port": 587,
"User": "", "User": "",
"Password": "" "Password": ""
}, },

View File

@ -0,0 +1,18 @@
namespace SharedModels.DataTransferObjects;
public class DriverDto : UserDto
{
public int CompanyId { get; set; }
}
public class CreateDriverDto : CreateUserDto
{
public int CompanyId { get; set; }
public override IList<string>? Roles { get; set; }
}
public class UpdateDriverDto : UpdateUserDto
{
public override IList<string>? Roles { get; set; }
}

View File

@ -9,6 +9,7 @@ public class ReviewDto : CreateReviewDto
[DataType(DataType.DateTime)] [DataType(DataType.DateTime)]
public DateTime PostDateTimeUtc { get; set; } public DateTime PostDateTimeUtc { get; set; }
public StrippedUserDto User { get; set; } = null!;
public InReviewVehicleEnrollmentDto VehicleEnrollment { get; set; } = null!; public InReviewVehicleEnrollmentDto VehicleEnrollment { get; set; } = null!;
} }

View File

@ -1,39 +1,73 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Utils;
namespace SharedModels.DataTransferObjects; namespace SharedModels.DataTransferObjects;
public class UserDto : UpdateUserDto public class UserDto
{ {
public virtual CompanyDto Company { get; set; } = null!; public string Id { get; set; } = null!;
public virtual IList<TicketDto> Tickets { get; set; } = null!; public string FirstName { get; set; } = null!;
public virtual IList<ReviewDto> Ratings { get; set; } = null!;
}
public class CreateUserDto public string LastName { get; set; } = null!;
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
[Required(ErrorMessage = "Username is required")] public string Patronymic { get; set; } = null!;
public string UserName { get; set; } = null!;
[DataType(DataType.Date)]
public DateTime? BirthDate { get; set; }
public Identity.Gender? Gender { get; set; }
public Identity.Document? Document { get; set; }
public string? DocumentDetails { get; set; }
[Required(ErrorMessage = "Email is required")]
[EmailAddress]
public string Email { get; set; } = null!; public string Email { get; set; } = null!;
public bool EmailConfirmed { get; set; } = false; public bool EmailConfirmed { get; set; } = false;
[Required(ErrorMessage = "Password is required")]
[DataType(DataType.Password)]
public string Password { get; set; } = null!;
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; } = null!;
public string? PhoneNumber { get; set; } public string? PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; } = false; public bool PhoneNumberConfirmed { get; set; } = false;
public virtual IList<string> RoleIds { get; set; } = new List<string> { "User" }; public virtual IList<string> Roles { get; set; } = null!;
public virtual IList<TicketGroupDto>? TicketGroups { get; set; } = null!;
public virtual IList<ReviewDto>? Reviews { get; set; } = null!;
}
public class CreateUserDto
{
[Required]
public string FirstName { get; set; } = null!;
[Required]
public string LastName { get; set; } = null!;
[Required]
public string Patronymic { get; set; } = null!;
[DataType(DataType.Date)]
public DateTime? BirthDate { get; set; }
public Identity.Gender? Gender { get; set; }
public Identity.Document? Document { get; set; }
public string? DocumentDetails { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; } = null!;
public bool EmailConfirmed { get; set; } = false;
[Required]
[DataType(DataType.Password)]
public string Password { get; set; } = null!;
[DataType(DataType.PhoneNumber)]
public string? PhoneNumber { get; set; }
public bool? PhoneNumberConfirmed { get; set; } = false;
public virtual IList<string> Roles { get; set; } = null!;
} }
public class UpdateUserDto public class UpdateUserDto
@ -41,23 +75,54 @@ public class UpdateUserDto
[Required] [Required]
public string Id { get; set; } = null!; public string Id { get; set; } = null!;
public string? FirstName { get; set; } [Required]
public string? LastName { get; set; } public string FirstName { get; set; } = null!;
public string UserName { get; set; } = null!; [Required]
public string LastName { get; set; } = null!;
[Required]
public string Patronymic { get; set; } = null!;
[Required]
[DataType(DataType.Date)]
public DateTime? BirthDate { get; set; }
[Required]
public Identity.Gender? Gender { get; set; }
[Required]
public Identity.Document? Document { get; set; }
[Required]
public string? DocumentDetails { get; set; }
[Required]
[EmailAddress] [EmailAddress]
public string Email { get; set; } = null!; public string Email { get; set; } = null!;
public bool EmailConfirmed { get; set; } public bool EmailConfirmed { get; set; } = false;
[DataType(DataType.Password)] [DataType(DataType.Password)]
public string Password { get; set; } = null!; public string? Password { get; set; } = null!;
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; } = null!;
[Required]
[DataType(DataType.PhoneNumber)]
public string? PhoneNumber { get; set; } public string? PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; } [Required]
public bool PhoneNumberConfirmed { get; set; } = false;
public virtual IList<string> RoleIds { get; set; } = new List<string> { "User" }; public virtual IList<string> Roles { get; set; } = null!;
}
public class StrippedUserDto
{
public string Id { get; set; } = null!;
public string FirstName { get; set; } = null!;
public string LastName { get; set; } = null!;
public string Patronymic { get; set; } = null!;
public Identity.Gender? Gender { get; set; }
} }

View File

@ -0,0 +1,16 @@
namespace SharedModels.QueryParameters.Objects;
public class CompanyDriverParameters : ParametersBase
{
public const string DefaultFields = "id,firstName,lastName,patronymic,email,emailConfirmed,phoneNumber," +
"phoneNumberConfirmed,birthDate,gender,document,documentDetails," +
"companyId";
public CompanyDriverParameters()
{
Sort = "";
Fields = DefaultFields;
}
public int? CompanyId { get; set; }
}

View File

@ -2,7 +2,7 @@ namespace SharedModels.QueryParameters.Objects;
public class ReviewParameters : ParametersBase public class ReviewParameters : ParametersBase
{ {
public const string DefaultFields = "id,userId,vehicleEnrollmentId,vehicleEnrollment,rating,comment"; public const string DefaultFields = "id,rating,comment,userId,user,vehicleEnrollmentId,vehicleEnrollment";
public ReviewParameters() public ReviewParameters()
{ {
@ -14,4 +14,5 @@ public class ReviewParameters : ParametersBase
public string? Comment { get; set; } public string? Comment { get; set; }
public string? UserId { get; set; } public string? UserId { get; set; }
public int? CompanyId { get; set; }
} }

View File

@ -2,17 +2,13 @@ namespace SharedModels.QueryParameters.Objects;
public class UserParameters : ParametersBase public class UserParameters : ParametersBase
{ {
public const string DefaultFields = ""; public const string DefaultFields = "id,firstName,lastName,patronymic,email,emailConfirmed,phoneNumber," +
"phoneNumberConfirmed,birthDate,gender,document,documentDetails," +
"roles,reviews,ticketGroups";
public UserParameters() public UserParameters()
{ {
Sort = "id"; Sort = "";
Fields = DefaultFields; Fields = DefaultFields;
} }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string? Username { get; set; }
public string? Email { get; set; }
public string? Phone { get; set; }
} }

View File

@ -6,4 +6,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Utils\Utils.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -1,4 +1,4 @@
namespace Server.Constants; namespace Utils;
public class Identity public class Identity
{ {
@ -10,7 +10,19 @@ public class Identity
Administrator Administrator
} }
public enum Gender
{
Male,
Female
}
public enum Document
{
Passport,
DriverLicence
}
public const string DefaultEmail = "admin@subdomain.domain"; public const string DefaultEmail = "admin@subdomain.domain";
public const string DefaultPassword = "123qwe!@#QWE"; public const string DefaultPassword = "123qwe!@#QWE";
public const Roles DefaultRole = Roles.Administrator; public const Roles DefaultRole = Roles.User;
} }

9
Utils/Utils.csproj Normal file
View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{0DCB2130-10E7-4C98-AB94-5F8D68032B9F}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{0DCB2130-10E7-4C98-AB94-5F8D68032B9F}"
EndProject EndProject