From 872a2f6364587051620803a1d22b7466449df9d2 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Thu, 24 Apr 2025 04:33:23 +0300 Subject: [PATCH 1/5] =?UTF-8?q?=D0=9F=D1=80=D0=B5=D0=BF=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B8:=20=D0=B2=D1=8B=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=B8=20=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D1=83=D1=8E=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20250424012738_'CourseMentors'.Designer.cs | 350 ++++++++++++++++++ .../20250424012738_'CourseMentors'.cs | 63 ++++ .../Migrations/CourseContextModelSnapshot.cs | 23 ++ .../Models/Course.cs | 6 +- .../Models/CourseContext.cs | 4 + .../Models/CourseMentor.cs | 8 + 6 files changed, 452 insertions(+), 2 deletions(-) create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.Designer.cs create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.cs create mode 100644 HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseMentor.cs diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.Designer.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.Designer.cs new file mode 100644 index 000000000..315d4b145 --- /dev/null +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.Designer.cs @@ -0,0 +1,350 @@ +// +using System; +using HwProj.CoursesService.API.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace HwProj.CoursesService.API.Migrations +{ + [DbContext(typeof(CourseContext))] + [Migration("20250424012738_'CourseMentors'")] + partial class CourseMentors + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.Assignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CourseId"); + + b.Property("MentorId"); + + b.Property("StudentId"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.ToTable("Assignments"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.Course", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("GroupName"); + + b.Property("InviteCode"); + + b.Property("IsCompleted"); + + b.Property("IsOpen"); + + b.Property("MentorIds"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.CourseFilter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("FilterJson"); + + b.HasKey("Id"); + + b.ToTable("CourseFilters"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.CourseMate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CourseId"); + + b.Property("IsAccepted"); + + b.Property("StudentId"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.ToTable("CourseMates"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.CourseMentor", b => + { + b.Property("UserId"); + + b.Property("CourseId"); + + b.HasKey("UserId", "CourseId"); + + b.HasIndex("CourseId"); + + b.HasIndex("UserId"); + + b.ToTable("CourseMentor"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CourseId"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.GroupMate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("GroupId"); + + b.Property("StudentId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasAlternateKey("GroupId", "StudentId"); + + b.ToTable("GroupMates"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.Homework", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CourseId"); + + b.Property("DeadlineDate"); + + b.Property("Description"); + + b.Property("HasDeadline"); + + b.Property("IsDeadlineStrict"); + + b.Property("PublicationDate"); + + b.Property("Tags"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.ToTable("Homeworks"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.HomeworkTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("DeadlineDate"); + + b.Property("Description"); + + b.Property("HasDeadline"); + + b.Property("HomeworkId"); + + b.Property("IsDeadlineStrict"); + + b.Property("MaxRating"); + + b.Property("PublicationDate"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("HomeworkId"); + + b.ToTable("Tasks"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.StudentCharacteristics", b => + { + b.Property("CourseMateId"); + + b.Property("Description"); + + b.Property("Tags"); + + b.HasKey("CourseMateId"); + + b.ToTable("StudentCharacteristics"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.TaskModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("GroupId"); + + b.Property("TaskId"); + + b.HasKey("Id"); + + b.HasIndex("GroupId"); + + b.ToTable("TasksModels"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.TaskQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Answer") + .HasMaxLength(1000); + + b.Property("IsPrivate"); + + b.Property("LecturerId"); + + b.Property("StudentId"); + + b.Property("TaskId"); + + b.Property("Text") + .HasMaxLength(1000); + + b.HasKey("Id"); + + b.HasIndex("TaskId"); + + b.ToTable("Questions"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.UserToCourseFilter", b => + { + b.Property("CourseId"); + + b.Property("UserId"); + + b.Property("CourseFilterId"); + + b.HasKey("CourseId", "UserId"); + + b.HasIndex("CourseFilterId"); + + b.ToTable("UserToCourseFilters"); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.Assignment", b => + { + b.HasOne("HwProj.CoursesService.API.Models.Course") + .WithMany("Assignments") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.CourseMate", b => + { + b.HasOne("HwProj.CoursesService.API.Models.Course") + .WithMany("CourseMates") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.CourseMentor", b => + { + b.HasOne("HwProj.CoursesService.API.Models.Course") + .WithMany("Mentors") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.GroupMate", b => + { + b.HasOne("HwProj.CoursesService.API.Models.Group") + .WithMany("GroupMates") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.Homework", b => + { + b.HasOne("HwProj.CoursesService.API.Models.Course") + .WithMany("Homeworks") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.HomeworkTask", b => + { + b.HasOne("HwProj.CoursesService.API.Models.Homework", "Homework") + .WithMany("Tasks") + .HasForeignKey("HomeworkId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.StudentCharacteristics", b => + { + b.HasOne("HwProj.CoursesService.API.Models.CourseMate") + .WithOne("Characteristics") + .HasForeignKey("HwProj.CoursesService.API.Models.StudentCharacteristics", "CourseMateId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.TaskModel", b => + { + b.HasOne("HwProj.CoursesService.API.Models.Group") + .WithMany("Tasks") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("HwProj.CoursesService.API.Models.UserToCourseFilter", b => + { + b.HasOne("HwProj.CoursesService.API.Models.CourseFilter", "CourseFilter") + .WithMany() + .HasForeignKey("CourseFilterId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.cs new file mode 100644 index 000000000..974e37435 --- /dev/null +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace HwProj.CoursesService.API.Migrations +{ + public partial class CourseMentors : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CourseMentor", + columns: table => new + { + UserId = table.Column(nullable: false), + CourseId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CourseMentor", x => new { x.UserId, x.CourseId }); + table.ForeignKey( + name: "FK_CourseMentor_Courses_CourseId", + column: x => x.CourseId, + principalTable: "Courses", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_CourseMentor_CourseId", + table: "CourseMentor", + column: "CourseId"); + + migrationBuilder.CreateIndex( + name: "IX_CourseMentor_UserId", + table: "CourseMentor", + column: "UserId"); + + // Миграция данных: Перенос MentorIds в таблицу CourseMentor + // language=SQL + migrationBuilder.Sql(@" +-- Разделим MentorIds на элементы, преобразуем в строки, удалим дубликаты и вставим в таблицу CourseMentor +WITH SplitMentorIds AS ( + SELECT + c.Id AS CourseId, + LTRIM(RTRIM(value)) AS MentorId + FROM Courses c + CROSS APPLY STRING_SPLIT(c.MentorIds, '/') -- Разделяем MentorIds по запятой +) +, DistinctMentors AS ( + SELECT DISTINCT + MentorId, + CourseId + FROM SplitMentorIds + WHERE MentorId <> '' -- Исключаем пустые строки +) +INSERT INTO CourseMentor (UserId, CourseId) +SELECT + MentorId, + CourseId +FROM DistinctMentors; +"); + } + } +} diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs index 736fb0058..4a822cfea 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs @@ -93,6 +93,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("CourseMates"); }); + modelBuilder.Entity("HwProj.CoursesService.API.Models.CourseMentor", b => + { + b.Property("UserId"); + + b.Property("CourseId"); + + b.HasKey("UserId", "CourseId"); + + b.HasIndex("CourseId"); + + b.HasIndex("UserId"); + + b.ToTable("CourseMentor"); + }); + modelBuilder.Entity("HwProj.CoursesService.API.Models.Group", b => { b.Property("Id") @@ -272,6 +287,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade); }); + modelBuilder.Entity("HwProj.CoursesService.API.Models.CourseMentor", b => + { + b.HasOne("HwProj.CoursesService.API.Models.Course") + .WithMany("Mentors") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("HwProj.CoursesService.API.Models.GroupMate", b => { b.HasOne("HwProj.CoursesService.API.Models.Group") diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Models/Course.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Models/Course.cs index e5f6af8a3..3efb5b4e9 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Models/Course.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Models/Course.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using HwProj.Repositories; @@ -12,7 +13,8 @@ public class Course : IEntity public bool IsOpen { get; set; } public string InviteCode { get; set; } public bool IsCompleted { get; set; } - public string MentorIds { get; set; } + [Obsolete] public string MentorIds { get; set; } + public List Mentors { get; set; } = new List(); public List CourseMates { get; set; } = new List(); public List Homeworks { get; set; } = new List(); public List Assignments { get; set; } = new List(); diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs index ca089ff93..774be0f2e 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs @@ -27,6 +27,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().HasIndex(a => a.CourseId); modelBuilder.Entity().HasKey(u => new { u.CourseId, u.UserId }); modelBuilder.Entity().HasIndex(t => t.TaskId); + + modelBuilder.Entity().HasKey(m => new { m.UserId, m.CourseId }); + modelBuilder.Entity().HasIndex(m => m.UserId); + modelBuilder.Entity().HasIndex(m => m.CourseId); } } } diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseMentor.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseMentor.cs new file mode 100644 index 000000000..0f0b51420 --- /dev/null +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseMentor.cs @@ -0,0 +1,8 @@ +namespace HwProj.CoursesService.API.Models +{ + public class CourseMentor + { + public string UserId { get; set; } + public long CourseId { get; set; } + } +} From 986940c6ff8882db3a29effc06fcebfebe3ee91b Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Thu, 24 Apr 2025 05:20:24 +0300 Subject: [PATCH 2/5] wip --- Directory.Build.props | 1 + .../HwProj.Repositories/ReadOnlyRepository.cs | 2 +- .../Domains/MappingExtensions.cs | 4 ++-- .../Events/LecturerAcceptToCourseEvent.cs | 1 - .../Events/LecturerRejectToCourseEvent.cs | 1 - .../Events/NewCourseMateEvent.cs | 2 +- .../Models/CourseContext.cs | 1 + .../Repositories/CoursesRepository.cs | 14 ++++++++++- .../Services/CoursesService.cs | 23 ++++++------------- .../EventHandlers/NewCourseMateHandler.cs | 2 +- 10 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 134b7ea71..733e4e7e8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,7 @@ 8 + 0612 diff --git a/HwProj.Common/HwProj.Repositories/ReadOnlyRepository.cs b/HwProj.Common/HwProj.Repositories/ReadOnlyRepository.cs index 4e0b9a4ad..59c94c59e 100644 --- a/HwProj.Common/HwProj.Repositories/ReadOnlyRepository.cs +++ b/HwProj.Common/HwProj.Repositories/ReadOnlyRepository.cs @@ -32,7 +32,7 @@ public async Task GetAsync(TKey id) return await Context.FindAsync(id).ConfigureAwait(false); } - public async Task FindAsync(Expression> predicate) + public virtual async Task FindAsync(Expression> predicate) { return await Context.Set().AsNoTracking().FirstOrDefaultAsync(predicate).ConfigureAwait(false); } diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Domains/MappingExtensions.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Domains/MappingExtensions.cs index 0393b25f0..bfef52d02 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Domains/MappingExtensions.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Domains/MappingExtensions.cs @@ -85,7 +85,7 @@ public static CourseDTO ToCourseDto(this Course course) Name = course.Name, GroupName = course.GroupName, IsCompleted = course.IsCompleted, - MentorIds = course.MentorIds.Split("/"), + MentorIds = course.Mentors.Select(x => x.UserId).ToArray(), IsOpen = course.IsOpen, InviteCode = course.InviteCode, CourseMates = course.CourseMates.Select(cm => cm.ToCourseMateViewModel()).ToArray(), @@ -99,7 +99,7 @@ public static CoursePreview ToCoursePreview(this Course course) Name = course.Name, GroupName = course.GroupName, IsCompleted = course.IsCompleted, - MentorIds = course.MentorIds.Split("/"), + MentorIds = course.Mentors.Select(x => x.UserId).ToArray(), }; public static HomeworkTask ToHomeworkTask(this CreateTaskViewModel createTaskViewModel) diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Events/LecturerAcceptToCourseEvent.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Events/LecturerAcceptToCourseEvent.cs index 163af8672..4362ed80f 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Events/LecturerAcceptToCourseEvent.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Events/LecturerAcceptToCourseEvent.cs @@ -6,7 +6,6 @@ public class LecturerAcceptToCourseEvent : Event { public long CourseId { get; set; } public string CourseName { get; set; } - public string MentorIds { get; set; } public string StudentId { get; set; } } } diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Events/LecturerRejectToCourseEvent.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Events/LecturerRejectToCourseEvent.cs index 9386a6cb1..f6b6fe876 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Events/LecturerRejectToCourseEvent.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Events/LecturerRejectToCourseEvent.cs @@ -6,7 +6,6 @@ public class LecturerRejectToCourseEvent : Event { public long CourseId { get; set; } public string CourseName { get; set; } - public string MentorIds { get; set; } public string StudentId { get; set; } } } diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Events/NewCourseMateEvent.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Events/NewCourseMateEvent.cs index 747917731..99c1977c9 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Events/NewCourseMateEvent.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Events/NewCourseMateEvent.cs @@ -6,7 +6,7 @@ public class NewCourseMateEvent : Event { public long CourseId { get; set; } public string CourseName { get; set; } - public string MentorIds { get; set; } + public string[] MentorIds { get; set; } public string StudentId { get; set; } public bool IsAccepted { get; set; } } diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs index 774be0f2e..6678bbce2 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs @@ -5,6 +5,7 @@ namespace HwProj.CoursesService.API.Models public sealed class CourseContext : DbContext { public DbSet Courses { get; set; } + public DbSet Mentors { get; set; } public DbSet CourseMates { get; set; } public DbSet Groups { get; set; } public DbSet GroupMates { get; set; } diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Repositories/CoursesRepository.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Repositories/CoursesRepository.cs index 17e4bd829..09254f343 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Repositories/CoursesRepository.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Repositories/CoursesRepository.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; using HwProj.CoursesService.API.Models; using HwProj.Repositories; @@ -6,6 +8,7 @@ namespace HwProj.CoursesService.API.Repositories { + // TODO: include mentors by default public class CoursesRepository : CrudRepository, ICoursesRepository { public CoursesRepository(CourseContext context) @@ -13,8 +16,14 @@ public CoursesRepository(CourseContext context) { } + public override async Task FindAsync(Expression> predicate) + { + return await Context.Set().AsNoTracking().Include(x => x.Mentors).FirstOrDefaultAsync(predicate); + } + public Task GetWithCourseMates(long id) => Context.Set() + .Include(x => x.Mentors) .Include(c => c.CourseMates) .ThenInclude(c => c.Characteristics) .AsNoTracking() @@ -22,6 +31,7 @@ public CoursesRepository(CourseContext context) public async Task GetWithHomeworksAsync(long id) => await Context.Set() + .Include(x => x.Mentors) .Include(c => c.Homeworks) .ThenInclude(h => h.Tasks) .AsNoTracking() @@ -30,6 +40,7 @@ public CoursesRepository(CourseContext context) public async Task GetWithCourseMatesAndHomeworksAsync(long id) { var course = await Context.Set() + .Include(x => x.Mentors) .Include(c => c.CourseMates) .ThenInclude(c => c.Characteristics) .Include(c => c.Homeworks) @@ -45,6 +56,7 @@ public CoursesRepository(CourseContext context) public IQueryable GetAllWithCourseMatesAndHomeworks() { return Context.Set() + .Include(x => x.Mentors) .Include(c => c.CourseMates) .ThenInclude(c => c.Characteristics) .Include(c => c.Homeworks) diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Services/CoursesService.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Services/CoursesService.cs index 7fd03cdb0..a7b70639a 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Services/CoursesService.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Services/CoursesService.cs @@ -113,10 +113,11 @@ public async Task AddFromTemplateAsync(CourseTemplate courseTemplate, List using var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); var course = courseTemplate.ToCourse(); - course.MentorIds = mentorId; course.InviteCode = Guid.NewGuid().ToString(); var courseId = await _coursesRepository.AddAsync(course); + await _context.Mentors.AddAsync(new CourseMentor() { CourseId = courseId, UserId = mentorId }); + var homeworks = courseTemplate.Homeworks.Select(hwTemplate => hwTemplate.ToHomework(courseId)); var homeworkIds = await _homeworksRepository.AddRangeAsync(homeworks); @@ -141,7 +142,6 @@ public async Task AddFromTemplateAsync(CourseTemplate courseTemplate, List { CourseId = courseId, CourseName = course.Name, - MentorIds = course.MentorIds, StudentId = student.StudentId }); } @@ -187,7 +187,7 @@ public async Task AddStudentAsync(long courseId, string studentId) { CourseId = courseId, CourseName = course.Name, - MentorIds = course.MentorIds, + MentorIds = course.Mentors.Select(x => x.UserId).ToArray(), StudentId = studentId, IsAccepted = false }); @@ -209,7 +209,6 @@ public async Task AcceptCourseMateAsync(long courseId, string studentId) { CourseId = courseId, CourseName = course.Name, - MentorIds = course.MentorIds, StudentId = studentId }); @@ -230,7 +229,6 @@ public async Task RejectCourseMateAsync(long courseId, string studentId) { CourseId = courseId, CourseName = course.Name, - MentorIds = course.MentorIds, StudentId = studentId }); return true; @@ -241,7 +239,7 @@ public async Task GetUserCoursesAsync(string userId, string role) var isMentor = role == Roles.LecturerRole || role == Roles.ExpertRole; var courses = isMentor - ? _coursesRepository.FindAll(c => c.MentorIds.Contains(userId)) + ? _coursesRepository.FindAll(c => c.Mentors.Any(m => m.UserId == userId)) : _coursesRepository.FindAll(c => c.CourseMates.Any(cm => cm.IsAccepted && cm.StudentId == userId)); var coursesWithValues = await courses @@ -271,14 +269,9 @@ public async Task AcceptLecturerAsync(long courseId, string lecturerEmail, { var course = await _coursesRepository.GetAsync(courseId); if (course == null) return false; - if (!course.MentorIds.Contains(lecturerId)) + if (_context.Mentors.All(x => x.UserId != lecturerId)) { - var newMentors = course.MentorIds + "/" + lecturerId; - await _coursesRepository.UpdateAsync(courseId, с => new Course - { - MentorIds = newMentors, - }); - + _context.Mentors.Add(new CourseMentor() { CourseId = courseId, UserId = lecturerId }); _eventBus.Publish(new LecturerInvitedToCourseEvent { CourseId = courseId, @@ -286,8 +279,6 @@ public async Task AcceptLecturerAsync(long courseId, string lecturerEmail, MentorId = lecturerId, MentorEmail = lecturerEmail }); - //TODO: remove - await RejectCourseMateAsync(courseId, lecturerId); } return true; @@ -296,7 +287,7 @@ public async Task AcceptLecturerAsync(long courseId, string lecturerEmail, public async Task GetCourseLecturers(long courseId) { var course = await _coursesRepository.GetAsync(courseId); - return course.MentorIds.Split('/'); + return course.Mentors.Select(x => x.UserId).ToArray(); } public async Task HasStudent(long courseId, string studentId) diff --git a/HwProj.NotificationsService/HwProj.NotificationsService.API/EventHandlers/NewCourseMateHandler.cs b/HwProj.NotificationsService/HwProj.NotificationsService.API/EventHandlers/NewCourseMateHandler.cs index eab810b0f..f3f755d44 100644 --- a/HwProj.NotificationsService/HwProj.NotificationsService.API/EventHandlers/NewCourseMateHandler.cs +++ b/HwProj.NotificationsService/HwProj.NotificationsService.API/EventHandlers/NewCourseMateHandler.cs @@ -38,7 +38,7 @@ public override async Task HandleAsync(NewCourseMateEvent @event) var user = await _authServiceClient.GetAccountData(@event.StudentId); var url = _configuration["Url"]; - var mentorIds = @event.MentorIds.Split('/'); + var mentorIds = @event.MentorIds; var mentors = await _authServiceClient.GetAccountsData(mentorIds); //TODO: fix From 2d427e60975c552eea453099059138b55da38da9 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Sat, 26 Apr 2025 02:53:36 +0300 Subject: [PATCH 3/5] wip --- .../20250424012738_'CourseMentors'.Designer.cs | 2 +- .../Migrations/20250424012738_'CourseMentors'.cs | 16 ++++++++-------- .../Models/CourseContext.cs | 2 +- .../Services/CoursesService.cs | 10 ++++++---- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.Designer.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.Designer.cs index 315d4b145..16c6dba41 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.Designer.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.Designer.cs @@ -107,7 +107,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("UserId"); - b.ToTable("CourseMentor"); + b.ToTable("CourseMentors"); }); modelBuilder.Entity("HwProj.CoursesService.API.Models.Group", b => diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.cs index 974e37435..3aaade4d9 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/20250424012738_'CourseMentors'.cs @@ -7,7 +7,7 @@ public partial class CourseMentors : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "CourseMentor", + name: "CourseMentors", columns: table => new { UserId = table.Column(nullable: false), @@ -15,9 +15,9 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_CourseMentor", x => new { x.UserId, x.CourseId }); + table.PrimaryKey("PK_CourseMentors", x => new { x.UserId, x.CourseId }); table.ForeignKey( - name: "FK_CourseMentor_Courses_CourseId", + name: "FK_CourseMentors_Courses_CourseId", column: x => x.CourseId, principalTable: "Courses", principalColumn: "Id", @@ -25,13 +25,13 @@ protected override void Up(MigrationBuilder migrationBuilder) }); migrationBuilder.CreateIndex( - name: "IX_CourseMentor_CourseId", - table: "CourseMentor", + name: "IX_CourseMentors_CourseId", + table: "CourseMentors", column: "CourseId"); migrationBuilder.CreateIndex( - name: "IX_CourseMentor_UserId", - table: "CourseMentor", + name: "IX_CourseMentors_UserId", + table: "CourseMentors", column: "UserId"); // Миграция данных: Перенос MentorIds в таблицу CourseMentor @@ -52,7 +52,7 @@ SELECT DISTINCT FROM SplitMentorIds WHERE MentorId <> '' -- Исключаем пустые строки ) -INSERT INTO CourseMentor (UserId, CourseId) +INSERT INTO CourseMentors (UserId, CourseId) SELECT MentorId, CourseId diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs index 6678bbce2..ebd5e1fe5 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseContext.cs @@ -5,7 +5,7 @@ namespace HwProj.CoursesService.API.Models public sealed class CourseContext : DbContext { public DbSet Courses { get; set; } - public DbSet Mentors { get; set; } + public DbSet CourseMentors { get; set; } public DbSet CourseMates { get; set; } public DbSet Groups { get; set; } public DbSet GroupMates { get; set; } diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Services/CoursesService.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Services/CoursesService.cs index a7b70639a..141990b80 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Services/CoursesService.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Services/CoursesService.cs @@ -114,10 +114,9 @@ public async Task AddFromTemplateAsync(CourseTemplate courseTemplate, List var course = courseTemplate.ToCourse(); course.InviteCode = Guid.NewGuid().ToString(); + course.Mentors = new List { new CourseMentor { UserId = mentorId } }; var courseId = await _coursesRepository.AddAsync(course); - await _context.Mentors.AddAsync(new CourseMentor() { CourseId = courseId, UserId = mentorId }); - var homeworks = courseTemplate.Homeworks.Select(hwTemplate => hwTemplate.ToHomework(courseId)); var homeworkIds = await _homeworksRepository.AddRangeAsync(homeworks); @@ -243,6 +242,7 @@ public async Task GetUserCoursesAsync(string userId, string role) : _coursesRepository.FindAll(c => c.CourseMates.Any(cm => cm.IsAccepted && cm.StudentId == userId)); var coursesWithValues = await courses + .Include(c => c.Mentors) .Include(c => c.CourseMates) .Include(c => c.Homeworks).ThenInclude(t => t.Tasks) .ToArrayAsync(); @@ -269,9 +269,11 @@ public async Task AcceptLecturerAsync(long courseId, string lecturerEmail, { var course = await _coursesRepository.GetAsync(courseId); if (course == null) return false; - if (_context.Mentors.All(x => x.UserId != lecturerId)) + if (course.Mentors.All(x => x.UserId != lecturerId)) { - _context.Mentors.Add(new CourseMentor() { CourseId = courseId, UserId = lecturerId }); + await _context.CourseMentors.AddAsync(new CourseMentor() { CourseId = courseId, UserId = lecturerId }); + await _context.SaveChangesAsync(); + _eventBus.Publish(new LecturerInvitedToCourseEvent { CourseId = courseId, From 1bd0d55ee07e285cc096b68871e38b47ddc91be9 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Sat, 26 Apr 2025 02:56:13 +0300 Subject: [PATCH 4/5] wip --- .../HwProj.CoursesService.API/Models/CourseMentor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseMentor.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseMentor.cs index 0f0b51420..8fa0abdcf 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseMentor.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Models/CourseMentor.cs @@ -1,5 +1,8 @@ -namespace HwProj.CoursesService.API.Models +using System.ComponentModel.DataAnnotations.Schema; + +namespace HwProj.CoursesService.API.Models { + [Table("CourseMentors")] public class CourseMentor { public string UserId { get; set; } From b8ff75b074e36e872a73026a04dbef1800364ccf Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Sat, 26 Apr 2025 02:56:55 +0300 Subject: [PATCH 5/5] wip --- .../Migrations/CourseContextModelSnapshot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs index 4a822cfea..26fc00997 100644 --- a/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs +++ b/HwProj.CoursesService/HwProj.CoursesService.API/Migrations/CourseContextModelSnapshot.cs @@ -105,7 +105,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("UserId"); - b.ToTable("CourseMentor"); + b.ToTable("CourseMentors"); }); modelBuilder.Entity("HwProj.CoursesService.API.Models.Group", b =>