AsiignmentStruct

This commit is contained in:
SpecialX
2025-06-20 18:58:11 +08:00
parent d20c051c51
commit 681c0862b6
32 changed files with 414 additions and 752 deletions

View File

@@ -13,7 +13,6 @@ namespace TechHelper.Context
public DbSet<AssignmentClass> AssignmentClasses { get; set; }
public DbSet<Assignment> Assignments { get; set; }
public DbSet<AssignmentQuestion> AssignmentGroups { get; set; }
public DbSet<AssignmentQuestion> AssignmentQuestions { get; set; }
public DbSet<Class> Classes { get; set; }
public DbSet<ClassTeacher> ClassStudents { get; set; }
@@ -21,6 +20,7 @@ namespace TechHelper.Context
public DbSet<Question> Questions { get; set; }
public DbSet<Submission> Submissions { get; set; }
public DbSet<SubmissionDetail> SubmissionDetails { get; set; }
public DbSet<QuestionContext> QuestionContexts { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
@@ -28,7 +28,6 @@ namespace TechHelper.Context
builder.ApplyConfiguration(new RoleConfiguration());
builder.ApplyConfiguration(new AssignmentConfiguration());
builder.ApplyConfiguration(new AssignmentClassConfiguration());
builder.ApplyConfiguration(new AssignmentGroupConfiguration());
builder.ApplyConfiguration(new AssignmentQuestionConfiguration());
builder.ApplyConfiguration(new ClassConfiguration());
builder.ApplyConfiguration(new ClassStudentConfiguration());

View File

@@ -50,10 +50,11 @@ namespace TechHelper.Context
// =============================================================
// ENTITY -> DTO Mappings (用于读取/查询)
// =============================================================
CreateMap<Assignment, AssignmentDto>()
.ForMember(dest => dest.ExamStruct, opt => opt.MapFrom(src => src.ExamStruct));
CreateMap<Assignment, AssignmentDto>();
CreateMap<QuestionContext, QuestionContextDto>().ReverseMap();
CreateMap<AssignmentStruct, AssignmentStructDto>(); // 直接映射,因为成员现在对等了
// 新增!从实体到新的 DTO 的映射
CreateMap<AssignmentQuestion, AssignmentQuestionDto>();
@@ -64,7 +65,6 @@ namespace TechHelper.Context
// DTO -> ENTITY Mappings (用于创建/更新) - 现在变得极其简单!
// =================================================================
CreateMap<AssignmentDto, Assignment>();
CreateMap<AssignmentStructDto, AssignmentStruct>();
// 新增!从新的 DTO 到实体的映射
CreateMap<AssignmentQuestionDto, AssignmentQuestion>();

View File

@@ -1,83 +0,0 @@
using Entities.Contracts;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
namespace TechHelper.Context.Configuration
{
public class AssignmentGroupConfiguration : IEntityTypeConfiguration<AssignmentStruct>
{
public void Configure(EntityTypeBuilder<AssignmentStruct> builder)
{
// 1. 设置表名
// 将此实体映射到数据库中名为 "assignment_detail" 的表。
builder.ToTable("assignment_group");
// 2. 配置主键
// Id 属性作为主键。
builder.HasKey(ag => ag.Id);
// 3. 配置列名、必需性、长度和默认值
// 配置 Id 属性对应的数据库列名为 "id"。
builder.Property(ag => ag.Id)
.HasColumnName("id");
// EF Core 默认 Guid 类型主键由应用程序生成,因此无需 ValueGeneratedOnAdd()。
// 配置 AssignmentId 属性对应的数据库列名为 "assignment",并设置为必需字段。
builder.Property(ag => ag.AssignmentId)
.HasColumnName("assignment");
// 配置 Title 属性对应的数据库列名为 "title",设置为必需字段,并设置最大长度。
builder.Property(ag => ag.Title)
.HasColumnName("title")
.IsRequired()
.HasMaxLength(65535); // 对应 MaxLength(65535)
// 配置 Descript 属性对应的数据库列名为 "descript",并设置最大长度。
builder.Property(ag => ag.Description)
.HasColumnName("descript")
.HasMaxLength(65535); // 对应 MaxLength(65535)
// 配置 TotalPoints 属性对应的数据库列名为 "total_points"。
// TotalPoints 是 decimal? 类型,默认就是可选的,无需 IsRequired(false)。
builder.Property(ag => ag.Score)
.HasColumnName("total_points");
// 配置 Number 属性对应的数据库列名为 "number"。
builder.Property(ag => ag.Index)
.HasColumnName("number")
.IsRequired(); // byte 默认非空,显式 IsRequired 增加可读性。
// 配置 ParentGroup 属性对应的数据库列名为 "sub_group"。
// ParentGroup 是 Guid? 类型,默认就是可选的,无需 IsRequired(false)。
builder.Property(ag => ag.ParentStructId)
.HasColumnName("parent_group")
.IsRequired(false);
// 配置 IsDeleted 属性对应的数据库列名为 "deleted",并设置默认值为 false。
builder.Property(ag => ag.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false); // 适用于软删除策略
// 4. 配置导航属性和外键关系
// 配置 AssignmentGroup 到 Assignment 的多对一关系。
// 一个 AssignmentGroup 记录属于一个 Assignment。
builder.HasOne(ag => ag.Assignment) // 当前 AssignmentGroup 有一个 Assignment
.WithOne(a => a.ExamStruct) // 该 Assignment 可以有多个 AssignmentGroup 记录
.HasForeignKey<AssignmentStruct>(ag => ag.AssignmentId); // 通过 AssignmentId 建立外键
// 配置 AssignmentGroup 到 AssignmentGroup 的自引用关系(父子关系)。
// 一个 AssignmentGroup 可以有一个父 AssignmentGroup (SubAssignmentGroup)。
// 假设父 AssignmentGroup 实体中有一个名为 ChildAssignmentGroups 的集合属性来表示它所包含的所有子组。
builder.HasOne(ag => ag.ParentStruct) // 当前 AssignmentGroup 有一个父 AssignmentGroup
.WithMany(parentAg => parentAg.ChildrenGroups) // 该父 AssignmentGroup 可以有多个子 AssignmentGroup
.HasForeignKey(ag => ag.ParentStructId) // 通过 SubGroup 建立外键
.IsRequired(false) // SubGroup 是可空的 (Guid?),所以这个关系是可选的。
.OnDelete(DeleteBehavior.SetNull); // 当父 AssignmentGroup 被删除时,其子 AssignmentGroup 的 SubGroup 外键将被设置为 NULL。
// 如果你希望父组被删除时子组不能脱离父组(即不允许父组被删除),
// 可以使用 DeleteBehavior.Restrict 或 DeleteBehavior.NoAction。
}
}
}

View File

@@ -38,12 +38,6 @@ namespace TechHelper.Context.Configuration
builder.Property(aq => aq.Score)
.HasColumnName("score");
// 配置 AssignmentGroupId 列
// 该列在数据库中名为 "detail_id"
builder.Property(aq => aq.AssignmentStructId)
.HasColumnName("group_id")
.IsRequired();
builder.Property(aq => aq.IsDeleted)
.HasColumnName("deleted")
.HasDefaultValue(false); // 适用于软删除策略
@@ -60,17 +54,11 @@ namespace TechHelper.Context.Configuration
.HasForeignKey(aq => aq.QuestionId) // 外键是 AssignmentQuestion.QuestionId
.OnDelete(DeleteBehavior.Cascade); // 当 Question 被删除时,相关的 AssignmentQuestion 也级联删除。
// ---
// 配置 AssignmentQuestion 到 AssignmentGroup 的关系 (多对一)
// 一个 AssignmentQuestion 属于一个 AssignmentGroup。
//
// 你的 `AssignmentQuestion` 类现在有了 `public AssignmentGroup AssignmentGroup { get; set; }`
// 这是一个非常好的改进,它与 `AssignmentGroupId` 外键完美匹配。
// 假设 `AssignmentGroup` 实体中有一个名为 `AssignmentQuestions` 的 `ICollection<AssignmentQuestion>` 集合属性。
builder.HasOne(aq => aq.AssignmentStruct) // 当前 AssignmentQuestion 有一个 AssignmentGroup
.WithMany(ag => ag.AssignmentQuestions) // 那个 AssignmentGroup 可以有多个 AssignmentQuestion
.HasForeignKey(aq => aq.AssignmentStructId) // 外键是 AssignmentQuestion.AssignmentGroupId (列名 detail_id)
.OnDelete(DeleteBehavior.Cascade); // 当 AssignmentGroup 被删除时,相关的 AssignmentQuestion 也级联删除。
builder.HasOne(aq => aq.QuestionContext)
.WithMany(qc => qc.Questions)
.HasForeignKey(aq => aq.QuestionContextId)
.OnDelete(DeleteBehavior.SetNull);
// ---
// 配置 AssignmentQuestion 到 SubmissionDetail 的关系 (一对多)

View File

@@ -111,11 +111,6 @@ namespace TechHelper.Context.Configuration
.HasForeignKey(q => q.LessonId)
.OnDelete(DeleteBehavior.SetNull);
builder.HasOne(q => q.ParentQuestion)
.WithMany(pq => pq.ChildrenQuestion)
.IsRequired(false)
.HasForeignKey(q => q.ParentQuestionId)
.OnDelete(DeleteBehavior.SetNull);
}
}

View File

@@ -12,7 +12,7 @@ using TechHelper.Context;
namespace TechHelper.Server.Migrations
{
[DbContext(typeof(ApplicationContext))]
[Migration("20250619070929_init")]
[Migration("20250620104952_init")]
partial class init
{
/// <inheritdoc />
@@ -161,9 +161,9 @@ namespace TechHelper.Server.Migrations
.HasColumnType("char(36)")
.HasColumnName("id");
b.Property<Guid>("AssignmentGroupId")
b.Property<Guid?>("AssignmentId")
.HasColumnType("char(36)")
.HasColumnName("group_id");
.HasColumnName("assignment");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)")
@@ -179,7 +179,15 @@ namespace TechHelper.Server.Migrations
.HasDefaultValue(false)
.HasColumnName("deleted");
b.Property<Guid>("QuestionId")
b.Property<Guid?>("ParentAssignmentQuestionId")
.HasColumnType("char(36)")
.HasColumnName("parent_question_group_id");
b.Property<Guid?>("QuestionContextId")
.HasColumnType("char(36)")
.HasColumnName("description");
b.Property<Guid?>("QuestionId")
.HasColumnType("char(36)")
.HasColumnName("question_id");
@@ -187,58 +195,13 @@ namespace TechHelper.Server.Migrations
.HasColumnType("float")
.HasColumnName("score");
b.HasKey("Id");
b.HasIndex("AssignmentGroupId");
b.HasIndex("QuestionId");
b.ToTable("assignment_questions", (string)null);
});
modelBuilder.Entity("Entities.Contracts.AssignmentStruct", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)")
.HasColumnName("id");
b.Property<Guid?>("AssignmentId")
.HasColumnType("char(36)")
.HasColumnName("assignment");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(65535)
.HasColumnType("longtext")
.HasColumnName("descript");
b.Property<byte>("Index")
b.Property<byte>("StructType")
.HasColumnType("tinyint unsigned")
.HasColumnName("number");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false)
.HasColumnName("deleted");
b.Property<byte>("Layout")
.HasColumnType("tinyint unsigned")
.HasColumnName("layout");
b.Property<Guid?>("ParentGroupId")
.HasColumnType("char(36)")
.HasColumnName("parent_group");
b.Property<float?>("Score")
.HasColumnType("float")
.HasColumnName("total_points");
.HasColumnName("group_state");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(65535)
.HasColumnType("longtext")
.HasMaxLength(1024)
.HasColumnType("varchar(1024)")
.HasColumnName("title");
b.HasKey("Id");
@@ -246,9 +209,13 @@ namespace TechHelper.Server.Migrations
b.HasIndex("AssignmentId")
.IsUnique();
b.HasIndex("ParentGroupId");
b.HasIndex("ParentAssignmentQuestionId");
b.ToTable("assignment_group", (string)null);
b.HasIndex("QuestionContextId");
b.HasIndex("QuestionId");
b.ToTable("assignment_questions", (string)null);
});
modelBuilder.Entity("Entities.Contracts.Class", b =>
@@ -408,6 +375,7 @@ namespace TechHelper.Server.Migrations
b.Property<string>("Question")
.IsRequired()
.HasMaxLength(65535)
.HasColumnType("longtext");
b.HasKey("Id");
@@ -445,10 +413,6 @@ namespace TechHelper.Server.Migrations
.HasColumnType("tinyint unsigned")
.HasColumnName("difficulty_level");
b.Property<byte>("GroupState")
.HasColumnType("tinyint unsigned")
.HasColumnName("group_state");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
@@ -467,10 +431,6 @@ namespace TechHelper.Server.Migrations
.HasColumnType("longtext")
.HasColumnName("options");
b.Property<Guid?>("ParentQuestionId")
.HasColumnType("char(36)")
.HasColumnName("parent_question_group_id");
b.Property<byte>("SubjectArea")
.HasMaxLength(100)
.HasColumnType("tinyint unsigned")
@@ -501,14 +461,27 @@ namespace TechHelper.Server.Migrations
b.HasIndex("LessonId");
b.HasIndex("ParentQuestionId");
b.HasIndex("Title")
.HasAnnotation("MySql:IndexPrefixLength", new[] { 20 });
b.ToTable("questions", (string)null);
});
modelBuilder.Entity("Entities.Contracts.QuestionContext", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("QuestionContexts");
});
modelBuilder.Entity("Entities.Contracts.Submission", b =>
{
b.Property<Guid>("Id")
@@ -774,19 +747,19 @@ namespace TechHelper.Server.Migrations
b.HasData(
new
{
Id = new Guid("895d8f32-714e-4a14-bd97-8fa262b83172"),
Id = new Guid("577dbfe8-7b77-4ead-9386-678f02dea5f4"),
Name = "Student",
NormalizedName = "STUDENT"
},
new
{
Id = new Guid("d182c396-c656-42da-965a-d93c17a1f74f"),
Id = new Guid("04b04eed-32b9-4eb0-b5f5-a97bb4626718"),
Name = "Teacher",
NormalizedName = "TEACHER"
},
new
{
Id = new Guid("4e65fab9-3315-4474-b92c-bdab5a617e65"),
Id = new Guid("82354e4d-902d-4dd6-9790-6ef50ba9bc11"),
Name = "Administrator",
NormalizedName = "ADMINISTRATOR"
});
@@ -942,37 +915,31 @@ namespace TechHelper.Server.Migrations
modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b =>
{
b.HasOne("Entities.Contracts.AssignmentStruct", "AssignmentGroup")
.WithMany("AssignmentQuestions")
.HasForeignKey("AssignmentGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entities.Contracts.Assignment", "Assignment")
.WithOne("ExamStruct")
.HasForeignKey("Entities.Contracts.AssignmentQuestion", "AssignmentId");
b.HasOne("Entities.Contracts.AssignmentQuestion", "ParentAssignmentQuestion")
.WithMany("ChildrenAssignmentQuestion")
.HasForeignKey("ParentAssignmentQuestionId");
b.HasOne("Entities.Contracts.QuestionContext", "QuestionContext")
.WithMany("Questions")
.HasForeignKey("QuestionContextId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("Entities.Contracts.Question", "Question")
.WithMany("AssignmentQuestions")
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AssignmentGroup");
b.Navigation("Question");
});
modelBuilder.Entity("Entities.Contracts.AssignmentStruct", b =>
{
b.HasOne("Entities.Contracts.Assignment", "Assignment")
.WithOne("ExamStruct")
.HasForeignKey("Entities.Contracts.AssignmentStruct", "AssignmentId");
b.HasOne("Entities.Contracts.AssignmentStruct", "ParentGroup")
.WithMany("ChildrenGroups")
.HasForeignKey("ParentGroupId")
.OnDelete(DeleteBehavior.SetNull);
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Assignment");
b.Navigation("ParentGroup");
b.Navigation("ParentAssignmentQuestion");
b.Navigation("Question");
b.Navigation("QuestionContext");
});
modelBuilder.Entity("Entities.Contracts.Class", b =>
@@ -1075,18 +1042,11 @@ namespace TechHelper.Server.Migrations
.HasForeignKey("LessonId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("Entities.Contracts.Question", "ParentQuestion")
.WithMany("ChildrenQuestion")
.HasForeignKey("ParentQuestionId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Creator");
b.Navigation("KeyPoint");
b.Navigation("Lesson");
b.Navigation("ParentQuestion");
});
modelBuilder.Entity("Entities.Contracts.Submission", b =>
@@ -1207,16 +1167,11 @@ namespace TechHelper.Server.Migrations
modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b =>
{
b.Navigation("ChildrenAssignmentQuestion");
b.Navigation("SubmissionDetails");
});
modelBuilder.Entity("Entities.Contracts.AssignmentStruct", b =>
{
b.Navigation("AssignmentQuestions");
b.Navigation("ChildrenGroups");
});
modelBuilder.Entity("Entities.Contracts.Class", b =>
{
b.Navigation("AssignmentClasses");
@@ -1243,8 +1198,11 @@ namespace TechHelper.Server.Migrations
modelBuilder.Entity("Entities.Contracts.Question", b =>
{
b.Navigation("AssignmentQuestions");
});
b.Navigation("ChildrenQuestion");
modelBuilder.Entity("Entities.Contracts.QuestionContext", b =>
{
b.Navigation("Questions");
});
modelBuilder.Entity("Entities.Contracts.Submission", b =>

View File

@@ -77,6 +77,20 @@ namespace TechHelper.Server.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "QuestionContexts",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_QuestionContexts", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "textbook",
columns: table => new
@@ -327,39 +341,6 @@ namespace TechHelper.Server.Migrations
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "assignment_group",
columns: table => new
{
id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
assignment = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
title = table.Column<string>(type: "longtext", maxLength: 65535, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
descript = table.Column<string>(type: "longtext", maxLength: 65535, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
layout = table.Column<byte>(type: "tinyint unsigned", nullable: false),
total_points = table.Column<float>(type: "float", nullable: true),
number = table.Column<byte>(type: "tinyint unsigned", nullable: false),
parent_group = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
deleted = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValue: false)
},
constraints: table =>
{
table.PrimaryKey("PK_assignment_group", x => x.id);
table.ForeignKey(
name: "FK_assignment_group_assignment_group_parent_group",
column: x => x.parent_group,
principalTable: "assignment_group",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_assignment_group_assignments_assignment",
column: x => x.assignment,
principalTable: "assignments",
principalColumn: "id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "submissions",
columns: table => new
@@ -507,7 +488,7 @@ namespace TechHelper.Server.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Question = table.Column<string>(type: "longtext", nullable: false)
Question = table.Column<string>(type: "longtext", maxLength: 65535, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
LessonID = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
},
@@ -535,12 +516,10 @@ namespace TechHelper.Server.Migrations
question_type = table.Column<byte>(type: "tinyint unsigned", maxLength: 20, nullable: false),
difficulty_level = table.Column<byte>(type: "tinyint unsigned", maxLength: 10, nullable: false),
subject_area = table.Column<byte>(type: "tinyint unsigned", maxLength: 100, nullable: false),
group_state = table.Column<byte>(type: "tinyint unsigned", nullable: false),
options = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
key_point = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
lesson = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
parent_question_group_id = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
created_by = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
created_at = table.Column<DateTime>(type: "datetime(6)", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
@@ -568,12 +547,6 @@ namespace TechHelper.Server.Migrations
principalTable: "lesson",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_questions_questions_parent_question_group_id",
column: x => x.parent_question_group_id,
principalTable: "questions",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
})
.Annotation("MySql:CharSet", "utf8mb4");
@@ -582,9 +555,14 @@ namespace TechHelper.Server.Migrations
columns: table => new
{
id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
question_id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
group_id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
question_id = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
assignment = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
title = table.Column<string>(type: "varchar(1024)", maxLength: 1024, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
description = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
question_number = table.Column<byte>(type: "tinyint unsigned", nullable: false),
parent_question_group_id = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
group_state = table.Column<byte>(type: "tinyint unsigned", nullable: false),
created_at = table.Column<DateTime>(type: "datetime(6)", nullable: false),
score = table.Column<float>(type: "float", nullable: true),
deleted = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValue: false)
@@ -593,11 +571,21 @@ namespace TechHelper.Server.Migrations
{
table.PrimaryKey("PK_assignment_questions", x => x.id);
table.ForeignKey(
name: "FK_assignment_questions_assignment_group_group_id",
column: x => x.group_id,
principalTable: "assignment_group",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
name: "FK_assignment_questions_QuestionContexts_description",
column: x => x.description,
principalTable: "QuestionContexts",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
table.ForeignKey(
name: "FK_assignment_questions_assignment_questions_parent_question_gr~",
column: x => x.parent_question_group_id,
principalTable: "assignment_questions",
principalColumn: "id");
table.ForeignKey(
name: "FK_assignment_questions_assignments_assignment",
column: x => x.assignment,
principalTable: "assignments",
principalColumn: "id");
table.ForeignKey(
name: "FK_assignment_questions_questions_question_id",
column: x => x.question_id,
@@ -655,9 +643,9 @@ namespace TechHelper.Server.Migrations
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
values: new object[,]
{
{ new Guid("4e65fab9-3315-4474-b92c-bdab5a617e65"), null, "Administrator", "ADMINISTRATOR" },
{ new Guid("895d8f32-714e-4a14-bd97-8fa262b83172"), null, "Student", "STUDENT" },
{ new Guid("d182c396-c656-42da-965a-d93c17a1f74f"), null, "Teacher", "TEACHER" }
{ new Guid("04b04eed-32b9-4eb0-b5f5-a97bb4626718"), null, "Teacher", "TEACHER" },
{ new Guid("577dbfe8-7b77-4ead-9386-678f02dea5f4"), null, "Student", "STUDENT" },
{ new Guid("82354e4d-902d-4dd6-9790-6ef50ba9bc11"), null, "Administrator", "ADMINISTRATOR" }
});
migrationBuilder.CreateIndex(
@@ -708,20 +696,20 @@ namespace TechHelper.Server.Migrations
column: "class_id");
migrationBuilder.CreateIndex(
name: "IX_assignment_group_assignment",
table: "assignment_group",
name: "IX_assignment_questions_assignment",
table: "assignment_questions",
column: "assignment",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_assignment_group_parent_group",
table: "assignment_group",
column: "parent_group");
name: "IX_assignment_questions_description",
table: "assignment_questions",
column: "description");
migrationBuilder.CreateIndex(
name: "IX_assignment_questions_group_id",
name: "IX_assignment_questions_parent_question_group_id",
table: "assignment_questions",
column: "group_id");
column: "parent_question_group_id");
migrationBuilder.CreateIndex(
name: "IX_assignment_questions_question_id",
@@ -783,11 +771,6 @@ namespace TechHelper.Server.Migrations
table: "questions",
column: "lesson");
migrationBuilder.CreateIndex(
name: "IX_questions_parent_question_group_id",
table: "questions",
column: "parent_question_group_id");
migrationBuilder.CreateIndex(
name: "IX_questions_question_text",
table: "questions",
@@ -874,7 +857,7 @@ namespace TechHelper.Server.Migrations
name: "submissions");
migrationBuilder.DropTable(
name: "assignment_group");
name: "QuestionContexts");
migrationBuilder.DropTable(
name: "questions");

View File

@@ -158,9 +158,9 @@ namespace TechHelper.Server.Migrations
.HasColumnType("char(36)")
.HasColumnName("id");
b.Property<Guid>("AssignmentGroupId")
b.Property<Guid?>("AssignmentId")
.HasColumnType("char(36)")
.HasColumnName("group_id");
.HasColumnName("assignment");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)")
@@ -176,7 +176,15 @@ namespace TechHelper.Server.Migrations
.HasDefaultValue(false)
.HasColumnName("deleted");
b.Property<Guid>("QuestionId")
b.Property<Guid?>("ParentAssignmentQuestionId")
.HasColumnType("char(36)")
.HasColumnName("parent_question_group_id");
b.Property<Guid?>("QuestionContextId")
.HasColumnType("char(36)")
.HasColumnName("description");
b.Property<Guid?>("QuestionId")
.HasColumnType("char(36)")
.HasColumnName("question_id");
@@ -184,58 +192,13 @@ namespace TechHelper.Server.Migrations
.HasColumnType("float")
.HasColumnName("score");
b.HasKey("Id");
b.HasIndex("AssignmentGroupId");
b.HasIndex("QuestionId");
b.ToTable("assignment_questions", (string)null);
});
modelBuilder.Entity("Entities.Contracts.AssignmentStruct", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)")
.HasColumnName("id");
b.Property<Guid?>("AssignmentId")
.HasColumnType("char(36)")
.HasColumnName("assignment");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(65535)
.HasColumnType("longtext")
.HasColumnName("descript");
b.Property<byte>("Index")
b.Property<byte>("StructType")
.HasColumnType("tinyint unsigned")
.HasColumnName("number");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
.HasDefaultValue(false)
.HasColumnName("deleted");
b.Property<byte>("Layout")
.HasColumnType("tinyint unsigned")
.HasColumnName("layout");
b.Property<Guid?>("ParentGroupId")
.HasColumnType("char(36)")
.HasColumnName("parent_group");
b.Property<float?>("Score")
.HasColumnType("float")
.HasColumnName("total_points");
.HasColumnName("group_state");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(65535)
.HasColumnType("longtext")
.HasMaxLength(1024)
.HasColumnType("varchar(1024)")
.HasColumnName("title");
b.HasKey("Id");
@@ -243,9 +206,13 @@ namespace TechHelper.Server.Migrations
b.HasIndex("AssignmentId")
.IsUnique();
b.HasIndex("ParentGroupId");
b.HasIndex("ParentAssignmentQuestionId");
b.ToTable("assignment_group", (string)null);
b.HasIndex("QuestionContextId");
b.HasIndex("QuestionId");
b.ToTable("assignment_questions", (string)null);
});
modelBuilder.Entity("Entities.Contracts.Class", b =>
@@ -405,6 +372,7 @@ namespace TechHelper.Server.Migrations
b.Property<string>("Question")
.IsRequired()
.HasMaxLength(65535)
.HasColumnType("longtext");
b.HasKey("Id");
@@ -442,10 +410,6 @@ namespace TechHelper.Server.Migrations
.HasColumnType("tinyint unsigned")
.HasColumnName("difficulty_level");
b.Property<byte>("GroupState")
.HasColumnType("tinyint unsigned")
.HasColumnName("group_state");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("tinyint(1)")
@@ -464,10 +428,6 @@ namespace TechHelper.Server.Migrations
.HasColumnType("longtext")
.HasColumnName("options");
b.Property<Guid?>("ParentQuestionId")
.HasColumnType("char(36)")
.HasColumnName("parent_question_group_id");
b.Property<byte>("SubjectArea")
.HasMaxLength(100)
.HasColumnType("tinyint unsigned")
@@ -498,14 +458,27 @@ namespace TechHelper.Server.Migrations
b.HasIndex("LessonId");
b.HasIndex("ParentQuestionId");
b.HasIndex("Title")
.HasAnnotation("MySql:IndexPrefixLength", new[] { 20 });
b.ToTable("questions", (string)null);
});
modelBuilder.Entity("Entities.Contracts.QuestionContext", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("QuestionContexts");
});
modelBuilder.Entity("Entities.Contracts.Submission", b =>
{
b.Property<Guid>("Id")
@@ -771,19 +744,19 @@ namespace TechHelper.Server.Migrations
b.HasData(
new
{
Id = new Guid("895d8f32-714e-4a14-bd97-8fa262b83172"),
Id = new Guid("577dbfe8-7b77-4ead-9386-678f02dea5f4"),
Name = "Student",
NormalizedName = "STUDENT"
},
new
{
Id = new Guid("d182c396-c656-42da-965a-d93c17a1f74f"),
Id = new Guid("04b04eed-32b9-4eb0-b5f5-a97bb4626718"),
Name = "Teacher",
NormalizedName = "TEACHER"
},
new
{
Id = new Guid("4e65fab9-3315-4474-b92c-bdab5a617e65"),
Id = new Guid("82354e4d-902d-4dd6-9790-6ef50ba9bc11"),
Name = "Administrator",
NormalizedName = "ADMINISTRATOR"
});
@@ -939,37 +912,31 @@ namespace TechHelper.Server.Migrations
modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b =>
{
b.HasOne("Entities.Contracts.AssignmentStruct", "AssignmentGroup")
.WithMany("AssignmentQuestions")
.HasForeignKey("AssignmentGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entities.Contracts.Assignment", "Assignment")
.WithOne("ExamStruct")
.HasForeignKey("Entities.Contracts.AssignmentQuestion", "AssignmentId");
b.HasOne("Entities.Contracts.AssignmentQuestion", "ParentAssignmentQuestion")
.WithMany("ChildrenAssignmentQuestion")
.HasForeignKey("ParentAssignmentQuestionId");
b.HasOne("Entities.Contracts.QuestionContext", "QuestionContext")
.WithMany("Questions")
.HasForeignKey("QuestionContextId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("Entities.Contracts.Question", "Question")
.WithMany("AssignmentQuestions")
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AssignmentGroup");
b.Navigation("Question");
});
modelBuilder.Entity("Entities.Contracts.AssignmentStruct", b =>
{
b.HasOne("Entities.Contracts.Assignment", "Assignment")
.WithOne("ExamStruct")
.HasForeignKey("Entities.Contracts.AssignmentStruct", "AssignmentId");
b.HasOne("Entities.Contracts.AssignmentStruct", "ParentGroup")
.WithMany("ChildrenGroups")
.HasForeignKey("ParentGroupId")
.OnDelete(DeleteBehavior.SetNull);
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Assignment");
b.Navigation("ParentGroup");
b.Navigation("ParentAssignmentQuestion");
b.Navigation("Question");
b.Navigation("QuestionContext");
});
modelBuilder.Entity("Entities.Contracts.Class", b =>
@@ -1072,18 +1039,11 @@ namespace TechHelper.Server.Migrations
.HasForeignKey("LessonId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("Entities.Contracts.Question", "ParentQuestion")
.WithMany("ChildrenQuestion")
.HasForeignKey("ParentQuestionId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Creator");
b.Navigation("KeyPoint");
b.Navigation("Lesson");
b.Navigation("ParentQuestion");
});
modelBuilder.Entity("Entities.Contracts.Submission", b =>
@@ -1204,16 +1164,11 @@ namespace TechHelper.Server.Migrations
modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b =>
{
b.Navigation("ChildrenAssignmentQuestion");
b.Navigation("SubmissionDetails");
});
modelBuilder.Entity("Entities.Contracts.AssignmentStruct", b =>
{
b.Navigation("AssignmentQuestions");
b.Navigation("ChildrenGroups");
});
modelBuilder.Entity("Entities.Contracts.Class", b =>
{
b.Navigation("AssignmentClasses");
@@ -1240,8 +1195,11 @@ namespace TechHelper.Server.Migrations
modelBuilder.Entity("Entities.Contracts.Question", b =>
{
b.Navigation("AssignmentQuestions");
});
b.Navigation("ChildrenQuestion");
modelBuilder.Entity("Entities.Contracts.QuestionContext", b =>
{
b.Navigation("Questions");
});
modelBuilder.Entity("Entities.Contracts.Submission", b =>

View File

@@ -27,12 +27,12 @@ builder.Services.AddDbContext<ApplicationContext>(options =>
).AddUnitOfWork<ApplicationContext>()
.AddCustomRepository<Assignment, AssignmentRepository>()
.AddCustomRepository<AssignmentAttachment, AssignmentAttachmentRepository>()
.AddCustomRepository<AssignmentStruct, AssignmentGroupRepository>()
.AddCustomRepository<AssignmentQuestion, AssignmentQuestionRepository>()
.AddCustomRepository<Class, ClassRepository>()
.AddCustomRepository<ClassStudent, ClassStudentRepository>()
.AddCustomRepository<ClassTeacher, ClassTeacherRepository>()
.AddCustomRepository<Question, QuestionRepository>()
.AddCustomRepository<QuestionContext, QuestionContextRepository>()
.AddCustomRepository<Submission, SubmissionRepository>();
builder.Services.AddAutoMapper(typeof(AutoMapperProFile).Assembly);

View File

@@ -10,14 +10,12 @@ namespace TechHelper.Server.Repositories
{
private readonly IUnitOfWork _unitOfWork;
private readonly IRepository<Assignment> _assignmentRepo;
private readonly IRepository<AssignmentStruct> _assignmentGroupRepo;
private readonly IRepository<Question> _questionRepo;
public ExamRepository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_assignmentRepo = _unitOfWork.GetRepository<Assignment>();
_assignmentGroupRepo = _unitOfWork.GetRepository<AssignmentStruct>();
}
public async Task<Assignment?> GetFullExamByIdAsync(Guid assignmentId)
@@ -27,30 +25,7 @@ namespace TechHelper.Server.Repositories
}
private async Task LoadSubGroupsRecursive(AssignmentStruct group)
{
// EF Core 已经加载了下一层,我们需要确保更深层次的加载
var groupWithChildren = await _assignmentGroupRepo.GetFirstOrDefaultAsync(
predicate: g => g.Id == group.Id,
include: source => source
.Include(g => g.ChildrenGroups.Where(cg => !cg.IsDeleted))
.ThenInclude(cg => cg.AssignmentQuestions.Where(aq => !aq.IsDeleted))
.ThenInclude(aq => aq.Question)
.Include(g => g.AssignmentQuestions.Where(aq => !aq.IsDeleted))
.ThenInclude(aq => aq.Question)
);
group.ChildrenGroups = groupWithChildren.ChildrenGroups;
group.AssignmentQuestions = groupWithChildren.AssignmentQuestions;
if (group.ChildrenGroups != null)
{
foreach (var child in group.ChildrenGroups)
{
await LoadSubGroupsRecursive(child);
}
}
}
public async Task<IEnumerable<Assignment>> GetExamPreviewsByUserAsync(Guid userId)
{
@@ -72,11 +47,6 @@ namespace TechHelper.Server.Repositories
}
}
public async Task AddAsync(AssignmentStruct assignment)
{
}
public async Task AddAsync(AssignmentQuestion assignment)
{
}

View File

@@ -26,7 +26,6 @@ namespace TechHelper.Server.Repositories
Task AddAsync(AssignmentStruct assignment);
Task AddAsync(AssignmentQuestion assignment);

View File

@@ -5,10 +5,13 @@ using TechHelper.Context;
namespace TechHelper.Repository
{
public class AssignmentGroupRepository : Repository<AssignmentStruct>, IRepository<AssignmentStruct>
public class QuestionContextRepository : Repository<QuestionContext>, IRepository<QuestionContext>
{
public AssignmentGroupRepository(ApplicationContext dbContext) : base(dbContext)
public QuestionContextRepository(ApplicationContext dbContext) : base(dbContext)
{
}
}
}

View File

@@ -25,87 +25,43 @@ namespace TechHelper.Server.Services
public async Task<ApiResponse> CreateExamAsync(AssignmentDto assignmentDto)
{
Assignment newAssi = _mapper.Map<Assignment>(assignmentDto);
await _examRepository.AddAsync(newAssi);
var context = _unitOfWork.GetDbContext<ApplicationContext>();
foreach (var entry in context.ChangeTracker.Entries())
try
{
if (entry.State == Microsoft.EntityFrameworkCore.EntityState.Added)
Assignment newAssi = _mapper.Map<Assignment>(assignmentDto);
await _examRepository.AddAsync(newAssi);
var context = _unitOfWork.GetDbContext<ApplicationContext>();
foreach (var entry in context.ChangeTracker.Entries())
{
if(entry.Entity is Question newQues)
if (entry.State == Microsoft.EntityFrameworkCore.EntityState.Added)
{
newQues.CreatorId = newAssi.CreatorId;
if (entry.Entity is Question newQues)
{
newQues.CreatorId = newAssi.CreatorId;
}
}
}
if (await _unitOfWork.SaveChangesAsync() > 0)
{
return ApiResponse.Success();
}
return ApiResponse.Error("保存失败");
}
await _unitOfWork.SaveChangesAsync();
return ApiResponse.Success();
}
private async void ParseStruct(AssignmentStructDto assignmentStruct, Guid ParentID)
{
var newStruct = _mapper.Map<AssignmentStruct>(assignmentStruct);
newStruct.ParentStructId = Guid.Empty == ParentID ? null : ParentID;
await _examRepository.AddAsync(newStruct);
foreach (var item in assignmentStruct.AssignmentQuestions)
catch (Exception ex)
{
var newQuestion = _mapper.Map<Question>(item);
//newQuestion.ParentQuestionId = item.ParentQuestion == null ? null : item.ParentQuestion.Id;
await _examRepository.AddAsync(newQuestion);
//await ParseAssignmentQuestion(assignmentStruct, item, newQuestion);
}
foreach (var item in assignmentStruct.ChildrenGroups)
{
ParseStruct(item, assignmentStruct.Id);
return ApiResponse.Error(ex.Message);
}
}
private async Task ParseAssignmentQuestion(AssignmentStructDto assignmentStruct, QuestionDto item, Question newQuestion)
{
AssignmentQuestion newAssignQues = new AssignmentQuestion();
newAssignQues.QuestionId = newQuestion.Id;
newAssignQues.AssignmentStructId = assignmentStruct.Id;
newAssignQues.CreatedAt = DateTime.UtcNow;
newAssignQues.Score = item.Score;
await _examRepository.AddAsync(newAssignQues);
}
private void SetEntityIdsAndRelations(AssignmentStruct group, Guid? assignmentId, Guid creatorId)
{
group.Id = Guid.NewGuid();
group.AssignmentId = assignmentId;
foreach (var aq in group.AssignmentQuestions)
{
aq.Id = Guid.NewGuid();
aq.AssignmentStructId = group.Id;
aq.Question.Id = Guid.NewGuid();
aq.Question.CreatorId = creatorId;
aq.CreatedAt = DateTime.UtcNow;
// ... 其他默认值
}
foreach (var childGroup in group.ChildrenGroups)
{
// 子题组的 AssignmentId 为 null通过 ParentGroup 关联
SetEntityIdsAndRelations(childGroup, null, creatorId);
childGroup.ParentStructId = group.Id;
}
}
public async Task<AssignmentDto> GetExamByIdAsync(Guid id)
{

View File

@@ -1,9 +0,0 @@
using Entities.Contracts;
using TechHelper.Services;
namespace TechHelper.Server.Services
{
public interface IAssignmentGroupService : IBaseService<AssignmentStruct, Guid>
{
}
}