重构试卷相关内容
This commit is contained in:
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Entities.Contracts;
|
||||
using TechHelper.Server.Context.Configuration;
|
||||
|
||||
namespace TechHelper.Context
|
||||
{
|
||||
@@ -35,6 +36,7 @@ namespace TechHelper.Context
|
||||
builder.ApplyConfiguration(new ClassTeacherConfiguration());
|
||||
builder.ApplyConfiguration(new QuestionConfiguration());
|
||||
builder.ApplyConfiguration(new SubmissionConfiguration());
|
||||
builder.ApplyConfiguration(new QuestionGroupConfiguration());
|
||||
builder.ApplyConfiguration(new SubmissionDetailConfiguration());
|
||||
}
|
||||
}
|
||||
|
@@ -58,10 +58,33 @@ namespace TechHelper.Context
|
||||
CreateMap<Assignment, ExamDto>()
|
||||
.ForMember(dest => dest.AssignmentTitle, opt => opt.MapFrom(src => src.Title))
|
||||
.ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description))
|
||||
.ForMember(dest => dest.AssignmentId, opt => opt.MapFrom(src=> src.Id))
|
||||
.ForMember(dest => dest.AssignmentId, opt => opt.MapFrom(src => src.Id))
|
||||
.ForMember(dest => dest.QuestionGroups, opt => opt.MapFrom(src =>
|
||||
src.AssignmentGroups.FirstOrDefault(ag => ag.ParentGroup == null)))
|
||||
.ForMember(dest => dest.SubjectArea, opt => opt.MapFrom(src => src.SubjectArea.ToString()));
|
||||
|
||||
CreateMap<AssignmentGroup, QuestionGroupDto>()
|
||||
.ForMember(dest => dest.SubQuestionGroups, opt => opt.MapFrom(src => src.ChildAssignmentGroups))
|
||||
.ForMember(dest => dest.SubQuestions, opt => opt.MapFrom(src => src.AssignmentQuestions));
|
||||
|
||||
CreateMap<AssignmentQuestion, SubQuestionDto>()
|
||||
.ForMember(dest => dest.Stem, opt => opt.MapFrom(src => src.Question.QuestionText))
|
||||
.ForMember(dest => dest.SampleAnswer, opt => opt.MapFrom(src => src.Question.CorrectAnswer))
|
||||
.ForMember(dest => dest.QuestionType, opt => opt.MapFrom(src => src.Question.QuestionType.ToString()))
|
||||
.ForMember(dest => dest.DifficultyLevel, opt => opt.MapFrom(src => src.Question.DifficultyLevel.ToString()));
|
||||
|
||||
|
||||
|
||||
CreateMap<QuestionGroupDto, AssignmentGroup>()
|
||||
.ForMember(dest => dest.ChildAssignmentGroups, opt => opt.MapFrom(src => src.SubQuestionGroups))
|
||||
.ForMember(dest => dest.AssignmentQuestions, opt => opt.MapFrom(src => src.SubQuestions));
|
||||
|
||||
CreateMap<SubQuestionDto, AssignmentQuestion>()
|
||||
.ForMember(dest => dest.Question, opt => opt.MapFrom(src => src)); // 映射到嵌套的 Question 对象
|
||||
|
||||
|
||||
CreateMap<Assignment, ExamDto>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -22,8 +22,12 @@ namespace TechHelper.Context.Configuration
|
||||
|
||||
// 配置 QuestionId 列 (已修正拼写)
|
||||
builder.Property(aq => aq.QuestionId)
|
||||
.HasColumnName("question_id")
|
||||
.IsRequired();
|
||||
.HasColumnName("question_id");
|
||||
|
||||
|
||||
builder.Property(aq => aq.QuestionGroupId)
|
||||
.HasColumnName("question_group_id");
|
||||
|
||||
|
||||
// 配置 QuestionNumber 列
|
||||
builder.Property(aq => aq.QuestionNumber)
|
||||
@@ -36,7 +40,7 @@ namespace TechHelper.Context.Configuration
|
||||
.IsRequired(); // 通常创建时间字段是非空的
|
||||
|
||||
builder.Property(aq => aq.Score)
|
||||
.HasColumnName("score");
|
||||
.HasColumnName("score");
|
||||
|
||||
// 配置 AssignmentGroupId 列
|
||||
// 该列在数据库中名为 "detail_id"
|
||||
@@ -44,7 +48,11 @@ namespace TechHelper.Context.Configuration
|
||||
.HasColumnName("group_id")
|
||||
.IsRequired();
|
||||
|
||||
// 配置 IsDeleted 列
|
||||
|
||||
builder.Property(aq => aq.IsGroup)
|
||||
.HasColumnName("is_group") // 修正为一致的列名
|
||||
.IsRequired(); // IsGroup 应该是必需的
|
||||
// 配置 IsDeleted 列
|
||||
builder.Property(aq => aq.IsDeleted)
|
||||
.HasColumnName("deleted")
|
||||
.HasDefaultValue(false); // 适用于软删除策略
|
||||
@@ -61,6 +69,12 @@ namespace TechHelper.Context.Configuration
|
||||
.HasForeignKey(aq => aq.QuestionId) // 外键是 AssignmentQuestion.QuestionId
|
||||
.OnDelete(DeleteBehavior.Cascade); // 当 Question 被删除时,相关的 AssignmentQuestion 也级联删除。
|
||||
|
||||
builder.HasOne(aq => aq.QuestionGroup)
|
||||
.WithMany(qg => qg.AssignmentQuestions)
|
||||
.HasForeignKey(aq => aq.QuestionGroupId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
|
||||
// ---
|
||||
// 配置 AssignmentQuestion 到 AssignmentGroup 的关系 (多对一)
|
||||
// 一个 AssignmentQuestion 属于一个 AssignmentGroup。
|
||||
|
@@ -14,6 +14,8 @@ namespace TechHelper.Context.Configuration
|
||||
// 2. 设置主键
|
||||
builder.HasKey(q => q.Id);
|
||||
|
||||
builder.HasIndex(q => q.QuestionText);
|
||||
|
||||
// 3. 配置列名、必需性、长度及其他属性
|
||||
|
||||
// Id
|
||||
@@ -21,6 +23,11 @@ namespace TechHelper.Context.Configuration
|
||||
.HasColumnName("id");
|
||||
// 对于 Guid 类型的主键,EF Core 默认由应用程序生成值,无需 ValueGeneratedOnAdd()
|
||||
|
||||
builder.Property(q => q.QuestionGroupId)
|
||||
.HasColumnName("question_group_id")
|
||||
.IsRequired(false); // 可为空,因为题目不一定属于某个题组
|
||||
|
||||
|
||||
// QuestionText
|
||||
builder.Property(q => q.QuestionText)
|
||||
.HasColumnName("question_text")
|
||||
@@ -97,6 +104,13 @@ namespace TechHelper.Context.Configuration
|
||||
builder.HasMany(q => q.AssignmentQuestions) // 当前 Question 有多个 AssignmentQuestion
|
||||
.WithOne(aq => aq.Question); // 每一个 AssignmentQuestion 都有一个 Question
|
||||
// .HasForeignKey(aq => aq.QuestionId); // 外键的配置应在 `AssignmentQuestionConfiguration` 中进行
|
||||
|
||||
builder.HasOne(q => q.QuestionGroup) // Question 实体中的 QuestionGroup 导航属性
|
||||
.WithMany(qg => qg.Questions) // QuestionGroup 实体中的 Questions 集合
|
||||
.HasForeignKey(q => q.QuestionGroupId) // Question 实体中的 QuestionGroupId 外键
|
||||
.IsRequired(false) // QuestionGroupId 在 Question 实体中是可空的
|
||||
.OnDelete(DeleteBehavior.SetNull); // 如果 QuestionGroup 被删除,关联的 Question 的外键设置为 NULL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,111 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Entities.Contracts;
|
||||
|
||||
namespace TechHelper.Server.Context.Configuration
|
||||
{
|
||||
public class QuestionGroupConfiguration : IEntityTypeConfiguration<QuestionGroup>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<QuestionGroup> builder)
|
||||
{
|
||||
// 1. 设置表名
|
||||
builder.ToTable("question_groups");
|
||||
|
||||
// 2. 设置主键
|
||||
builder.HasKey(qg => qg.Id);
|
||||
|
||||
// 3. 配置列属性
|
||||
|
||||
// Title 标题
|
||||
builder.Property(qg => qg.Title)
|
||||
.HasColumnName("title")
|
||||
.HasMaxLength(255)
|
||||
.IsRequired(false); // 允许为空
|
||||
|
||||
// Description 描述内容 (Required)
|
||||
builder.Property(qg => qg.Description)
|
||||
.HasColumnName("description")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext"); // 对应 MySQL 的 TEXT 或 LONGTEXT
|
||||
|
||||
// Type 类型 (例如: "ReadingComprehension", "DiagramAnalysis")
|
||||
builder.Property(qg => qg.Type)
|
||||
.HasColumnName("type")
|
||||
.HasMaxLength(50)
|
||||
.IsRequired(false); // 允许为空
|
||||
|
||||
// DifficultyLevel 难度级别 (枚举映射为字符串)
|
||||
builder.Property(qg => qg.DifficultyLevel)
|
||||
.HasColumnName("difficulty_level")
|
||||
.HasConversion<string>() // 将枚举转换为字符串存储
|
||||
.HasMaxLength(10);
|
||||
|
||||
// SubjectArea 科目领域 (枚举映射为字符串)
|
||||
builder.Property(qg => qg.SubjectArea)
|
||||
.HasColumnName("subject_area")
|
||||
.HasConversion<string>(); // 将枚举转换为字符串存储
|
||||
|
||||
// TotalQuestions 包含题目总数
|
||||
builder.Property(qg => qg.TotalQuestions)
|
||||
.HasColumnName("total_questions")
|
||||
.IsRequired();
|
||||
|
||||
// ParentQG 父题组 ID (外键,自引用关系)
|
||||
builder.Property(qg => qg.ParentQG)
|
||||
.HasColumnName("parent_question_group") // 使用你定义的列名
|
||||
.IsRequired(false); // 可为空,因为根题组没有父级
|
||||
|
||||
// CreatedBy 创建者 ID (外键)
|
||||
builder.Property(qg => qg.CreatedBy)
|
||||
.HasColumnName("created_by")
|
||||
.IsRequired();
|
||||
|
||||
// CreatedAt 创建时间
|
||||
builder.Property(qg => qg.CreatedAt)
|
||||
.HasColumnName("created_at")
|
||||
.IsRequired();
|
||||
|
||||
// UpdatedAt 更新时间
|
||||
builder.Property(qg => qg.UpdatedAt)
|
||||
.HasColumnName("updated_at")
|
||||
.IsRequired();
|
||||
|
||||
// IsDeleted 是否删除 (软删除)
|
||||
builder.Property(qg => qg.IsDeleted)
|
||||
.HasColumnName("deleted")
|
||||
.IsRequired();
|
||||
|
||||
// ValidGroup 是否有效
|
||||
builder.Property(qg => qg.ValidGroup)
|
||||
.HasColumnName("valid_group")
|
||||
.IsRequired();
|
||||
|
||||
// 4. 配置关系
|
||||
|
||||
// 与 User 的关系 (创建者)
|
||||
builder.HasOne(qg => qg.Creator)
|
||||
.WithMany()
|
||||
.HasForeignKey(qg => qg.CreatedBy)
|
||||
.OnDelete(DeleteBehavior.Restrict); // 阻止删除关联的 User
|
||||
|
||||
// 与 Question 的关系 (一对多)
|
||||
// 一个 QuestionGroup 可以包含多个 Question
|
||||
builder.HasMany(qg => qg.Questions)
|
||||
.WithOne(q => q.QuestionGroup) // Question 实体中的 QuestionGroup 导航属性
|
||||
.HasForeignKey(q => q.QuestionGroupId) // Question 实体中的 QuestionGroupId 外键
|
||||
.IsRequired(false) // QuestionGroupId 在 Question 实体中是可空的
|
||||
.OnDelete(DeleteBehavior.SetNull); // 如果 QuestionGroup 被删除,关联的 Question 的外键设置为 NULL
|
||||
|
||||
// 与自身的自引用关系 (父子题组)
|
||||
// 一个 QuestionGroup 可以有多个 ChildQuestionGroups
|
||||
builder.HasMany(qg => qg.ChildQuestionGroups)
|
||||
.WithOne(childQG => childQG.ParentQuestionGroup) // 子 QuestionGroup 实体中的 ParentQuestionGroup 导航属性
|
||||
.HasForeignKey(childQG => childQG.ParentQG) // 子 QuestionGroup 实体中的 ParentQG 外键
|
||||
.IsRequired(false) // ParentQG 是可空的,因为根题组没有父级
|
||||
.OnDelete(DeleteBehavior.Restrict); // 或者 SetNull, Cascade。Restrict 更安全,避免意外删除整个分支。
|
||||
// 如果选择 SetNull,删除父组时子组的 ParentQG 会变为 NULL,它们就成了新的根组。
|
||||
// 如果选择 Cascade,删除父组会递归删除所有子组。根据业务逻辑选择。
|
||||
// 这里我选择了 Restrict 作为默认安全选项。
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user