From 3524e0d0af0e786bb8796da02a1db474d4fe4a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B0=D0=BB=D0=B8=D0=BC=D0=BE=D0=B2=20=D0=A0=D1=83?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BC?= Date: Mon, 16 Feb 2026 00:06:10 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=9D=D0=9D=20=D0=B8=20=D0=BE=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/RegisterUserCommand.cs | 30 +++- .../Commands/UpdateProfileCommand.cs | 6 +- .../Queries/GetProfile/GetProfileQuery.cs | 12 +- .../RegisterUserCommandValidator.cs | 7 + .../UpdateProfileCommandValidator.cs | 3 + .../Identity/Domain/Aggregates/UserProfile.cs | 23 ++- .../Configurations/AccountConfiguration.cs | 7 + .../UserProfileConfiguration.cs | 6 + ...54_AddDescriptionToUserProfile.Designer.cs | 158 +++++++++++++++++ ...60215191454_AddDescriptionToUserProfile.cs | 31 ++++ ...0215200327_AddInnToUserProfile.Designer.cs | 162 ++++++++++++++++++ .../20260215200327_AddInnToUserProfile.cs | 31 ++++ ...2659_FixAccountProfileRelation.Designer.cs | 162 ++++++++++++++++++ ...0260215202659_FixAccountProfileRelation.cs | 22 +++ .../IdentityDbContextModelSnapshot.cs | 8 + .../Repositories/AccountRepository.cs | 10 ++ 16 files changed, 659 insertions(+), 19 deletions(-) create mode 100644 src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215191454_AddDescriptionToUserProfile.Designer.cs create mode 100644 src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215191454_AddDescriptionToUserProfile.cs create mode 100644 src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215200327_AddInnToUserProfile.Designer.cs create mode 100644 src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215200327_AddInnToUserProfile.cs create mode 100644 src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215202659_FixAccountProfileRelation.Designer.cs create mode 100644 src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215202659_FixAccountProfileRelation.cs diff --git a/src/Modules/Identity/Application/Commands/RegisterUserCommand.cs b/src/Modules/Identity/Application/Commands/RegisterUserCommand.cs index 7cf1df0..ccebf72 100644 --- a/src/Modules/Identity/Application/Commands/RegisterUserCommand.cs +++ b/src/Modules/Identity/Application/Commands/RegisterUserCommand.cs @@ -41,6 +41,16 @@ public record RegisterUserCommand : IRequest /// public string? CompanyName { get; init; } + /// + /// ИНН (необязательно). + /// + public string? Inn { get; init; } + + /// + /// Описание исполнителя или компании (необязательно, максимум 2048 символов). + /// + public string? Description { get; init; } + /// /// Роль (по умолчанию User). /// @@ -68,26 +78,28 @@ public class RegisterUserCommandHandler : IRequestHandler @@ -46,7 +48,9 @@ public class UpdateProfileCommandHandler : IRequestHandler request.FirstName, request.LastName, request.Patronymic, - request.CompanyName); + request.CompanyName, + request.Inn, + request.Description); await _accountRepository.UpdateAsync(account, cancellationToken); } diff --git a/src/Modules/Identity/Application/Queries/GetProfile/GetProfileQuery.cs b/src/Modules/Identity/Application/Queries/GetProfile/GetProfileQuery.cs index c5a9d1a..297f64b 100644 --- a/src/Modules/Identity/Application/Queries/GetProfile/GetProfileQuery.cs +++ b/src/Modules/Identity/Application/Queries/GetProfile/GetProfileQuery.cs @@ -7,13 +7,15 @@ namespace Nashel.Modules.Identity.Application.Queries.GetProfile; public record GetProfileQuery : IRequest; public record ProfileResponse( - Guid Id, - string Phone, + Guid Id, + string Phone, List Roles, string FirstName, string LastName, string? Patronymic, string? CompanyName, + string? Inn, + string? Description, string FullName, string? AvatarUrl, List Competencies); @@ -50,13 +52,15 @@ public class GetProfileQueryHandler : IRequestHandler r.ToString()).ToList(), account.Profile.FirstName, account.Profile.LastName, account.Profile.Patronymic, account.Profile.CompanyName, + account.Profile.Inn, + account.Profile.Description, fullName, account.Profile.AvatarUrl, account.Profile.Competencies.Select(c => c.Name).ToList()); diff --git a/src/Modules/Identity/Application/Validators/RegisterUserCommandValidator.cs b/src/Modules/Identity/Application/Validators/RegisterUserCommandValidator.cs index 5bfc320..0a6c891 100644 --- a/src/Modules/Identity/Application/Validators/RegisterUserCommandValidator.cs +++ b/src/Modules/Identity/Application/Validators/RegisterUserCommandValidator.cs @@ -27,5 +27,12 @@ public class RegisterUserCommandValidator : AbstractValidator x.CompanyName) .NotEmpty().WithMessage("Название компании обязательно для юридических лиц") .When(x => x.Role == Role.Company); + + RuleFor(x => x.Inn) + .Length(10).WithMessage("ИНН должен содержать 10 цифр") + .When(x => x.Role == Role.Company); + + RuleFor(x => x.Description) + .MaximumLength(2048).WithMessage("Описание не может превышать 2048 символов"); } } diff --git a/src/Modules/Identity/Application/Validators/UpdateProfileCommandValidator.cs b/src/Modules/Identity/Application/Validators/UpdateProfileCommandValidator.cs index 2f195e4..e42426d 100644 --- a/src/Modules/Identity/Application/Validators/UpdateProfileCommandValidator.cs +++ b/src/Modules/Identity/Application/Validators/UpdateProfileCommandValidator.cs @@ -14,5 +14,8 @@ public class UpdateProfileCommandValidator : AbstractValidator x.LastName) .NotEmpty().WithMessage("Фамилия обязательна") .MinimumLength(2).WithMessage("Фамилия должна содержать минимум 2 символа"); + + RuleFor(x => x.Description) + .MaximumLength(2048).WithMessage("Описание не может превышать 2048 символов"); } } diff --git a/src/Modules/Identity/Domain/Aggregates/UserProfile.cs b/src/Modules/Identity/Domain/Aggregates/UserProfile.cs index ad47bd8..99f96ea 100644 --- a/src/Modules/Identity/Domain/Aggregates/UserProfile.cs +++ b/src/Modules/Identity/Domain/Aggregates/UserProfile.cs @@ -6,38 +6,51 @@ namespace Nashel.Modules.Identity.Domain.Aggregates; public class UserProfile : Entity { private const int MaxCompetencies = 50; + private const int MaxDescriptionLength = 2048; private readonly List _competencies = new(); public string FirstName { get; private set; } public string LastName { get; private set; } public string? Patronymic { get; private set; } public string? CompanyName { get; private set; } + public string? Inn { get; private set; } + public string? Description { get; private set; } public string? AvatarUrl { get; private set; } public IReadOnlyCollection Competencies => _competencies.AsReadOnly(); // EF Core constructor private UserProfile() { } - private UserProfile(Guid id, string firstName, string lastName, string? patronymic, string? companyName) + private UserProfile(Guid id, string firstName, string lastName, string? patronymic, string? companyName, string? inn, string? description) { Id = id; FirstName = firstName; LastName = lastName; Patronymic = patronymic; CompanyName = companyName; + Inn = inn; + Description = description; } - public static UserProfile Create(Guid id, string firstName, string lastName, string? patronymic, string? companyName) + public static UserProfile Create(Guid id, string firstName, string lastName, string? patronymic, string? companyName, string? inn, string? description) { - return new UserProfile(id, firstName, lastName, patronymic, companyName); + if (!string.IsNullOrEmpty(description) && description.Length > MaxDescriptionLength) + throw new ArgumentException($"Описание не может превышать {MaxDescriptionLength} символов", nameof(description)); + + return new UserProfile(id, firstName, lastName, patronymic, companyName, inn, description); } - public void Update(string firstName, string lastName, string? patronymic, string? companyName) + public void Update(string firstName, string lastName, string? patronymic, string? companyName, string? inn, string? description) { + if (!string.IsNullOrEmpty(description) && description.Length > MaxDescriptionLength) + throw new ArgumentException($"Описание не может превышать {MaxDescriptionLength} символов", nameof(description)); + FirstName = firstName; LastName = lastName; Patronymic = patronymic; CompanyName = companyName; + Inn = inn; + Description = description; } public void UpdateAvatar(string? avatarUrl) @@ -48,7 +61,7 @@ public class UserProfile : Entity public void UpdateCompetencies(IEnumerable competencies) { var competencyList = competencies.ToList(); - + if (competencyList.Count > MaxCompetencies) throw new InvalidOperationException($"Нельзя добавить больше {MaxCompetencies} компетенций"); diff --git a/src/Modules/Identity/Infrastructure/Persistence/Configurations/AccountConfiguration.cs b/src/Modules/Identity/Infrastructure/Persistence/Configurations/AccountConfiguration.cs index d14b9aa..5ae3151 100644 --- a/src/Modules/Identity/Infrastructure/Persistence/Configurations/AccountConfiguration.cs +++ b/src/Modules/Identity/Infrastructure/Persistence/Configurations/AccountConfiguration.cs @@ -2,6 +2,7 @@ using System.Text.Json; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Nashel.Modules.Identity.Domain.Aggregates; +using Nashel.Modules.Identity.Domain.Entities; using Nashel.Modules.Identity.Domain.Enums; namespace Nashel.Modules.Identity.Infrastructure.Persistence.Configurations; @@ -30,5 +31,11 @@ public class AccountConfiguration : IEntityTypeConfiguration v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), v => JsonSerializer.Deserialize>(v, (JsonSerializerOptions?)null) ?? new List()) .HasColumnType("jsonb"); + + // Настройка связи с UserProfile (один-к-одному) + builder.HasOne(x => x.Profile) + .WithOne() + .HasForeignKey(p => p.Id) + .OnDelete(DeleteBehavior.Cascade); } } diff --git a/src/Modules/Identity/Infrastructure/Persistence/Configurations/UserProfileConfiguration.cs b/src/Modules/Identity/Infrastructure/Persistence/Configurations/UserProfileConfiguration.cs index 03c8b7c..480950d 100644 --- a/src/Modules/Identity/Infrastructure/Persistence/Configurations/UserProfileConfiguration.cs +++ b/src/Modules/Identity/Infrastructure/Persistence/Configurations/UserProfileConfiguration.cs @@ -28,6 +28,12 @@ public class UserProfileConfiguration : IEntityTypeConfiguration builder.Property(x => x.CompanyName) .HasMaxLength(200); + builder.Property(x => x.Inn) + .HasMaxLength(10); + + builder.Property(x => x.Description) + .HasMaxLength(2048); + builder.Property(x => x.AvatarUrl) .HasMaxLength(500); diff --git a/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215191454_AddDescriptionToUserProfile.Designer.cs b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215191454_AddDescriptionToUserProfile.Designer.cs new file mode 100644 index 0000000..deb9d33 --- /dev/null +++ b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215191454_AddDescriptionToUserProfile.Designer.cs @@ -0,0 +1,158 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Nashel.Modules.Identity.Infrastructure.Persistence; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(IdentityDbContext))] + [Migration("20260215191454_AddDescriptionToUserProfile")] + partial class AddDescriptionToUserProfile + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Roles") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("Phone") + .IsUnique(); + + b.ToTable("Accounts", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AvatarUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CompanyName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Description") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Patronymic") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.ToTable("UserProfiles", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Entities.Competency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_Competencies_Name"); + + b.ToTable("Competencies", "identity"); + }); + + modelBuilder.Entity("UserProfileCompetencies", b => + { + b.Property("UserProfileId") + .HasColumnType("uuid"); + + b.Property("CompetencyId") + .HasColumnType("uuid"); + + b.HasKey("UserProfileId", "CompetencyId"); + + b.HasIndex("CompetencyId"); + + b.ToTable("UserProfileCompetencies", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b => + { + b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.Account", null) + .WithOne("Profile") + .HasForeignKey("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("UserProfileCompetencies", b => + { + b.HasOne("Nashel.Modules.Identity.Domain.Entities.Competency", null) + .WithMany() + .HasForeignKey("CompetencyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", null) + .WithMany() + .HasForeignKey("UserProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.Account", b => + { + b.Navigation("Profile") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215191454_AddDescriptionToUserProfile.cs b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215191454_AddDescriptionToUserProfile.cs new file mode 100644 index 0000000..133c376 --- /dev/null +++ b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215191454_AddDescriptionToUserProfile.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddDescriptionToUserProfile : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Description", + schema: "identity", + table: "UserProfiles", + type: "character varying(2048)", + maxLength: 2048, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Description", + schema: "identity", + table: "UserProfiles"); + } + } +} diff --git a/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215200327_AddInnToUserProfile.Designer.cs b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215200327_AddInnToUserProfile.Designer.cs new file mode 100644 index 0000000..7ed3e89 --- /dev/null +++ b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215200327_AddInnToUserProfile.Designer.cs @@ -0,0 +1,162 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Nashel.Modules.Identity.Infrastructure.Persistence; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(IdentityDbContext))] + [Migration("20260215200327_AddInnToUserProfile")] + partial class AddInnToUserProfile + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Roles") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("Phone") + .IsUnique(); + + b.ToTable("Accounts", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AvatarUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CompanyName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Description") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Inn") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Patronymic") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.ToTable("UserProfiles", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Entities.Competency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_Competencies_Name"); + + b.ToTable("Competencies", "identity"); + }); + + modelBuilder.Entity("UserProfileCompetencies", b => + { + b.Property("UserProfileId") + .HasColumnType("uuid"); + + b.Property("CompetencyId") + .HasColumnType("uuid"); + + b.HasKey("UserProfileId", "CompetencyId"); + + b.HasIndex("CompetencyId"); + + b.ToTable("UserProfileCompetencies", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b => + { + b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.Account", null) + .WithOne("Profile") + .HasForeignKey("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("UserProfileCompetencies", b => + { + b.HasOne("Nashel.Modules.Identity.Domain.Entities.Competency", null) + .WithMany() + .HasForeignKey("CompetencyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", null) + .WithMany() + .HasForeignKey("UserProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.Account", b => + { + b.Navigation("Profile") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215200327_AddInnToUserProfile.cs b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215200327_AddInnToUserProfile.cs new file mode 100644 index 0000000..0a2f6a0 --- /dev/null +++ b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215200327_AddInnToUserProfile.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddInnToUserProfile : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Inn", + schema: "identity", + table: "UserProfiles", + type: "character varying(10)", + maxLength: 10, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Inn", + schema: "identity", + table: "UserProfiles"); + } + } +} diff --git a/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215202659_FixAccountProfileRelation.Designer.cs b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215202659_FixAccountProfileRelation.Designer.cs new file mode 100644 index 0000000..37786ff --- /dev/null +++ b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215202659_FixAccountProfileRelation.Designer.cs @@ -0,0 +1,162 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Nashel.Modules.Identity.Infrastructure.Persistence; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(IdentityDbContext))] + [Migration("20260215202659_FixAccountProfileRelation")] + partial class FixAccountProfileRelation + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Roles") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.HasIndex("Phone") + .IsUnique(); + + b.ToTable("Accounts", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AvatarUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CompanyName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Description") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Inn") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Patronymic") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.ToTable("UserProfiles", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Entities.Competency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_Competencies_Name"); + + b.ToTable("Competencies", "identity"); + }); + + modelBuilder.Entity("UserProfileCompetencies", b => + { + b.Property("UserProfileId") + .HasColumnType("uuid"); + + b.Property("CompetencyId") + .HasColumnType("uuid"); + + b.HasKey("UserProfileId", "CompetencyId"); + + b.HasIndex("CompetencyId"); + + b.ToTable("UserProfileCompetencies", "identity"); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", b => + { + b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.Account", null) + .WithOne("Profile") + .HasForeignKey("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("UserProfileCompetencies", b => + { + b.HasOne("Nashel.Modules.Identity.Domain.Entities.Competency", null) + .WithMany() + .HasForeignKey("CompetencyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nashel.Modules.Identity.Domain.Aggregates.UserProfile", null) + .WithMany() + .HasForeignKey("UserProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nashel.Modules.Identity.Domain.Aggregates.Account", b => + { + b.Navigation("Profile") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215202659_FixAccountProfileRelation.cs b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215202659_FixAccountProfileRelation.cs new file mode 100644 index 0000000..89ec680 --- /dev/null +++ b/src/Modules/Identity/Infrastructure/Persistence/Migrations/20260215202659_FixAccountProfileRelation.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations +{ + /// + public partial class FixAccountProfileRelation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Modules/Identity/Infrastructure/Persistence/Migrations/IdentityDbContextModelSnapshot.cs b/src/Modules/Identity/Infrastructure/Persistence/Migrations/IdentityDbContextModelSnapshot.cs index 6d4e3b3..041a5a4 100644 --- a/src/Modules/Identity/Infrastructure/Persistence/Migrations/IdentityDbContextModelSnapshot.cs +++ b/src/Modules/Identity/Infrastructure/Persistence/Migrations/IdentityDbContextModelSnapshot.cs @@ -62,11 +62,19 @@ namespace Nashel.Modules.Identity.Infrastructure.Persistence.Migrations .HasMaxLength(200) .HasColumnType("character varying(200)"); + b.Property("Description") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + b.Property("FirstName") .IsRequired() .HasMaxLength(100) .HasColumnType("character varying(100)"); + b.Property("Inn") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + b.Property("LastName") .IsRequired() .HasMaxLength(100) diff --git a/src/Modules/Identity/Infrastructure/Repositories/AccountRepository.cs b/src/Modules/Identity/Infrastructure/Repositories/AccountRepository.cs index 572eb92..41bbf39 100644 --- a/src/Modules/Identity/Infrastructure/Repositories/AccountRepository.cs +++ b/src/Modules/Identity/Infrastructure/Repositories/AccountRepository.cs @@ -16,6 +16,11 @@ public class AccountRepository : IAccountRepository public async Task AddAsync(Account account, CancellationToken cancellationToken) { + // Явно добавляем профиль, чтобы EF Core сохранил его в таблицу UserProfiles + if (account.Profile != null) + { + await _context.UserProfiles.AddAsync(account.Profile, cancellationToken); + } await _context.Accounts.AddAsync(account, cancellationToken); await _context.SaveChangesAsync(cancellationToken); } @@ -37,6 +42,11 @@ public class AccountRepository : IAccountRepository public async Task UpdateAsync(Account account, CancellationToken cancellationToken) { + // Явно обновляем профиль, чтобы EF Core сохранил изменения в таблицу UserProfiles + if (account.Profile != null) + { + _context.UserProfiles.Update(account.Profile); + } _context.Accounts.Update(account); await _context.SaveChangesAsync(cancellationToken); }