Добавил слой Infrastructure

This commit is contained in:
2026-04-28 15:52:19 +03:00
parent 25d617639c
commit df0e30a1ae
32 changed files with 4139 additions and 0 deletions
@@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class AchievementConfiguration : IEntityTypeConfiguration<Achievement>
{
public void Configure(EntityTypeBuilder<Achievement> builder)
{
builder.ToTable("achievements");
builder.HasKey(a => a.Id);
builder.Property(a => a.Id).HasColumnName("id");
builder.Property(a => a.Name).HasColumnName("name").HasMaxLength(255).IsRequired();
builder.Property(a => a.Description).HasColumnName("description");
builder.Property(a => a.IconUrl).HasColumnName("icon_url").HasMaxLength(500);
builder.Property(a => a.XpReward).HasColumnName("xp_reward").HasDefaultValue(0);
builder.Property(a => a.CoinReward).HasColumnName("coin_reward").HasDefaultValue(0);
builder.Property(a => a.Condition).HasColumnName("condition");
builder.Property(a => a.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
}
}
@@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class CoinTransactionConfiguration : IEntityTypeConfiguration<CoinTransaction>
{
public void Configure(EntityTypeBuilder<CoinTransaction> builder)
{
builder.ToTable("coin_transactions");
builder.HasKey(ct => ct.Id);
builder.Property(ct => ct.Id).HasColumnName("id");
builder.Property(ct => ct.UserId).HasColumnName("user_id");
builder.Property(ct => ct.Amount).HasColumnName("amount");
builder.Property(ct => ct.Type).HasColumnName("type");
builder.Property(ct => ct.ReviewId).HasColumnName("review_id");
builder.Property(ct => ct.AchievementId).HasColumnName("achievement_id");
builder.Property(ct => ct.Description).HasColumnName("description").HasMaxLength(500);
builder.Property(ct => ct.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.HasOne(ct => ct.User)
.WithMany(u => u.CoinTransactions)
.HasForeignKey(ct => ct.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(ct => ct.Review)
.WithMany(r => r.CoinTransactions)
.HasForeignKey(ct => ct.ReviewId)
.OnDelete(DeleteBehavior.SetNull);
builder.HasOne(ct => ct.Achievement)
.WithMany()
.HasForeignKey(ct => ct.AchievementId)
.OnDelete(DeleteBehavior.SetNull);
builder.HasIndex(ct => ct.UserId);
}
}
@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class CourseConfiguration : IEntityTypeConfiguration<Course>
{
public void Configure(EntityTypeBuilder<Course> builder)
{
builder.ToTable("courses");
builder.HasKey(c => c.Id);
builder.Property(c => c.Id).HasColumnName("id");
builder.Property(c => c.Name).HasColumnName("name").HasMaxLength(500).IsRequired();
builder.Property(c => c.Description).HasColumnName("description");
builder.Property(c => c.ExternalId).HasColumnName("external_id").HasMaxLength(255);
builder.Property(c => c.IsSynced).HasColumnName("is_synced").HasDefaultValue(false);
builder.Property(c => c.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.Property(c => c.UpdatedAt).HasColumnName("updated_at").HasDefaultValueSql("NOW()");
builder.HasIndex(c => c.ExternalId).IsUnique().HasFilter("external_id IS NOT NULL");
}
}
@@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class CourseTagConfiguration : IEntityTypeConfiguration<CourseTag>
{
public void Configure(EntityTypeBuilder<CourseTag> builder)
{
builder.ToTable("course_tags");
builder.HasKey(ct => ct.Id);
builder.Property(ct => ct.Id).HasColumnName("id");
builder.Property(ct => ct.CourseId).HasColumnName("course_id");
builder.Property(ct => ct.TagId).HasColumnName("tag_id");
builder.HasOne(ct => ct.Course)
.WithMany(c => c.CourseTags)
.HasForeignKey(ct => ct.CourseId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(ct => ct.Tag)
.WithMany(t => t.CourseTags)
.HasForeignKey(ct => ct.TagId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(ct => new { ct.CourseId, ct.TagId }).IsUnique();
}
}
@@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class LectureConfiguration : IEntityTypeConfiguration<Lecture>
{
public void Configure(EntityTypeBuilder<Lecture> builder)
{
builder.ToTable("lectures");
builder.HasKey(l => l.Id);
builder.Property(l => l.Id).HasColumnName("id");
builder.Property(l => l.CourseId).HasColumnName("course_id");
builder.Property(l => l.TeacherId).HasColumnName("teacher_id");
builder.Property(l => l.LocationId).HasColumnName("location_id");
builder.Property(l => l.Title).HasColumnName("title").HasMaxLength(500).IsRequired();
builder.Property(l => l.Description).HasColumnName("description");
builder.Property(l => l.Format).HasColumnName("format");
builder.Property(l => l.StartsAt).HasColumnName("starts_at");
builder.Property(l => l.EndsAt).HasColumnName("ends_at");
builder.Property(l => l.IsOpen).HasColumnName("is_open").HasDefaultValue(true);
builder.Property(l => l.MaxEnrollments).HasColumnName("max_enrollments").HasDefaultValue(0);
builder.Property(l => l.ExternalId).HasColumnName("external_id").HasMaxLength(255);
builder.Property(l => l.OnlineUrl).HasColumnName("online_url").HasMaxLength(500);
builder.Property(l => l.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.Property(l => l.UpdatedAt).HasColumnName("updated_at").HasDefaultValueSql("NOW()");
builder.HasOne(l => l.Course)
.WithMany(c => c.Lectures)
.HasForeignKey(l => l.CourseId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(l => l.Teacher)
.WithMany()
.HasForeignKey(l => l.TeacherId)
.OnDelete(DeleteBehavior.SetNull);
builder.HasOne(l => l.Location)
.WithMany(loc => loc.Lectures)
.HasForeignKey(l => l.LocationId)
.OnDelete(DeleteBehavior.SetNull);
builder.HasIndex(l => l.ExternalId).IsUnique().HasFilter("external_id IS NOT NULL");
builder.HasIndex(l => l.StartsAt);
builder.HasIndex(l => l.CourseId);
}
}
@@ -0,0 +1,32 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class LectureEnrollmentConfiguration : IEntityTypeConfiguration<LectureEnrollment>
{
public void Configure(EntityTypeBuilder<LectureEnrollment> builder)
{
builder.ToTable("lecture_enrollments");
builder.HasKey(le => le.Id);
builder.Property(le => le.Id).HasColumnName("id");
builder.Property(le => le.LectureId).HasColumnName("lecture_id");
builder.Property(le => le.UserId).HasColumnName("user_id");
builder.Property(le => le.Attended).HasColumnName("attended").HasDefaultValue(false);
builder.Property(le => le.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.HasOne(le => le.Lecture)
.WithMany(l => l.Enrollments)
.HasForeignKey(le => le.LectureId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(le => le.User)
.WithMany(u => u.Enrollments)
.HasForeignKey(le => le.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(le => new { le.LectureId, le.UserId }).IsUnique();
}
}
@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class LocationConfiguration : IEntityTypeConfiguration<Location>
{
public void Configure(EntityTypeBuilder<Location> builder)
{
builder.ToTable("locations");
builder.HasKey(l => l.Id);
builder.Property(l => l.Id).HasColumnName("id");
builder.Property(l => l.Name).HasColumnName("name").HasMaxLength(255).IsRequired();
builder.Property(l => l.Building).HasColumnName("building").HasMaxLength(255);
builder.Property(l => l.Room).HasColumnName("room").HasMaxLength(100);
builder.Property(l => l.Address).HasColumnName("address").HasMaxLength(500);
builder.Property(l => l.ExternalId).HasColumnName("external_id").HasMaxLength(255);
builder.Property(l => l.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.HasIndex(l => l.ExternalId).IsUnique().HasFilter("external_id IS NOT NULL");
}
}
@@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class RefreshTokenConfiguration : IEntityTypeConfiguration<RefreshToken>
{
public void Configure(EntityTypeBuilder<RefreshToken> builder)
{
builder.ToTable("refresh_tokens");
builder.HasKey(rt => rt.Id);
builder.Property(rt => rt.Id).HasColumnName("id");
builder.Property(rt => rt.UserId).HasColumnName("user_id");
builder.Property(rt => rt.Token).HasColumnName("token").HasMaxLength(500).IsRequired();
builder.Property(rt => rt.ExpiresAt).HasColumnName("expires_at");
builder.Property(rt => rt.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.Property(rt => rt.RevokedAt).HasColumnName("revoked_at");
builder.HasOne(rt => rt.User)
.WithMany(u => u.RefreshTokens)
.HasForeignKey(rt => rt.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(rt => rt.Token).IsUnique();
builder.HasIndex(rt => rt.UserId);
// Computed properties should be ignored by EF
builder.Ignore(rt => rt.IsExpired);
builder.Ignore(rt => rt.IsRevoked);
builder.Ignore(rt => rt.IsActive);
}
}
@@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class ReviewConfiguration : IEntityTypeConfiguration<Review>
{
public void Configure(EntityTypeBuilder<Review> builder)
{
builder.ToTable("reviews");
builder.HasKey(r => r.Id);
builder.Property(r => r.Id).HasColumnName("id");
builder.Property(r => r.LectureId).HasColumnName("lecture_id");
builder.Property(r => r.UserId).HasColumnName("user_id");
builder.Property(r => r.Rating).HasColumnName("rating");
builder.Property(r => r.Text).HasColumnName("text");
builder.Property(r => r.LlmStatus).HasColumnName("llm_status").HasDefaultValue(Domain.Enums.ReviewLlmStatus.Pending);
builder.Property(r => r.Sentiment).HasColumnName("sentiment");
builder.Property(r => r.QualityScore).HasColumnName("quality_score");
builder.Property(r => r.IsInformative).HasColumnName("is_informative");
builder.Property(r => r.LlmTags).HasColumnName("llm_tags");
builder.Property(r => r.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.Property(r => r.UpdatedAt).HasColumnName("updated_at").HasDefaultValueSql("NOW()");
builder.HasOne(r => r.Lecture)
.WithMany(l => l.Reviews)
.HasForeignKey(r => r.LectureId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(r => r.User)
.WithMany(u => u.Reviews)
.HasForeignKey(r => r.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(r => new { r.LectureId, r.UserId }).IsUnique();
builder.HasIndex(r => r.LlmStatus);
}
}
@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class StudentProfileConfiguration : IEntityTypeConfiguration<StudentProfile>
{
public void Configure(EntityTypeBuilder<StudentProfile> builder)
{
builder.ToTable("student_profiles");
builder.HasKey(s => s.Id);
builder.Property(s => s.Id).HasColumnName("id");
builder.Property(s => s.UserId).HasColumnName("user_id");
builder.Property(s => s.StudentId).HasColumnName("student_id").HasMaxLength(50);
builder.Property(s => s.GroupName).HasColumnName("group_name").HasMaxLength(100);
builder.Property(s => s.EnrollmentYear).HasColumnName("enrollment_year");
builder.Property(s => s.Faculty).HasColumnName("faculty").HasMaxLength(255);
builder.Property(s => s.Specialty).HasColumnName("specialty").HasMaxLength(255);
builder.HasOne(s => s.User)
.WithOne(u => u.StudentProfile)
.HasForeignKey<StudentProfile>(s => s.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(s => s.UserId).IsUnique();
}
}
@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class TagConfiguration : IEntityTypeConfiguration<Tag>
{
public void Configure(EntityTypeBuilder<Tag> builder)
{
builder.ToTable("tags");
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).HasColumnName("id");
builder.Property(t => t.Name).HasColumnName("name").HasMaxLength(255).IsRequired();
builder.Property(t => t.Type).HasColumnName("type");
builder.Property(t => t.ParentId).HasColumnName("parent_id");
builder.Property(t => t.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.HasOne(t => t.Parent)
.WithMany(t => t.Children)
.HasForeignKey(t => t.ParentId)
.OnDelete(DeleteBehavior.SetNull);
}
}
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class TeacherProfileConfiguration : IEntityTypeConfiguration<TeacherProfile>
{
public void Configure(EntityTypeBuilder<TeacherProfile> builder)
{
builder.ToTable("teacher_profiles");
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).HasColumnName("id");
builder.Property(t => t.UserId).HasColumnName("user_id");
builder.Property(t => t.Department).HasColumnName("department").HasMaxLength(255);
builder.Property(t => t.Title).HasColumnName("title").HasMaxLength(255);
builder.Property(t => t.Bio).HasColumnName("bio");
builder.Property(t => t.ModeusId).HasColumnName("modeus_id").HasMaxLength(255);
builder.HasOne(t => t.User)
.WithOne(u => u.TeacherProfile)
.HasForeignKey<TeacherProfile>(t => t.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(t => t.UserId).IsUnique();
}
}
@@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class UserAchievementConfiguration : IEntityTypeConfiguration<UserAchievement>
{
public void Configure(EntityTypeBuilder<UserAchievement> builder)
{
builder.ToTable("user_achievements");
builder.HasKey(ua => ua.Id);
builder.Property(ua => ua.Id).HasColumnName("id");
builder.Property(ua => ua.UserId).HasColumnName("user_id");
builder.Property(ua => ua.AchievementId).HasColumnName("achievement_id");
builder.Property(ua => ua.AwardedAt).HasColumnName("awarded_at").HasDefaultValueSql("NOW()");
builder.HasOne(ua => ua.User)
.WithMany(u => u.UserAchievements)
.HasForeignKey(ua => ua.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasOne(ua => ua.Achievement)
.WithMany(a => a.UserAchievements)
.HasForeignKey(ua => ua.AchievementId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(ua => new { ua.UserId, ua.AchievementId }).IsUnique();
}
}
@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using UniVerse.Domain.Entities;
namespace UniVerse.Infrastructure.Data.Configurations;
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("users");
builder.HasKey(u => u.Id);
builder.Property(u => u.Id).HasColumnName("id");
builder.Property(u => u.Email).HasColumnName("email").HasMaxLength(255).IsRequired();
builder.Property(u => u.DisplayName).HasColumnName("display_name").HasMaxLength(255);
builder.Property(u => u.AvatarUrl).HasColumnName("avatar_url").HasMaxLength(500);
builder.Property(u => u.Role).HasColumnName("role");
builder.Property(u => u.IsActive).HasColumnName("is_active").HasDefaultValue(true);
builder.Property(u => u.MicrosoftId).HasColumnName("microsoft_id").HasMaxLength(255);
builder.Property(u => u.Xp).HasColumnName("xp").HasDefaultValue(0);
builder.Property(u => u.Coins).HasColumnName("coins").HasDefaultValue(0);
builder.Property(u => u.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("NOW()");
builder.Property(u => u.UpdatedAt).HasColumnName("updated_at").HasDefaultValueSql("NOW()");
builder.HasIndex(u => u.Email).IsUnique();
builder.HasIndex(u => u.MicrosoftId).IsUnique().HasFilter("microsoft_id IS NOT NULL");
}
}