课程库 Excel 导入导出已完成:

支持下载标准导入模板。
导出严格沿用当前关键词、学院、分类、课程性质和状态筛选。
以课程编码为唯一键:存在则更新,不存在则新增。
校验学院编码、课程分类、课程性质、学分学时及考核方式。
整批原子导入,任一行错误则全部不写入,并提示具体行号。
学院管理员仍只能维护本学院的专业课和实践课,无法借 Excel 越权。
界面入口沿用现有课程库工具栏样式。
This commit is contained in:
2026-07-24 18:56:25 +08:00 Unverified
parent 5a7a8d530b
commit d7cf3f9e76
24 changed files with 3710 additions and 32 deletions
@@ -5,6 +5,61 @@ namespace Jiaowu.Api.Tests;
public sealed class GraduationAuditRulesTests
{
[Fact]
public void Curriculum_groups_Accept_Any_Options_That_Fulfill_Required_Credits()
{
var football = new Course { Code = "PE101", Name = "足球", Credits = 1 };
var basketball = new Course { Code = "PE102", Name = "篮球", Credits = 1 };
var swimming = new Course { Code = "PE103", Name = "游泳", Credits = 1 };
var module = new CurriculumModule
{
Code = "SPORTS",
Name = "大学体育",
RequiredCredits = 2,
Courses = [Option(football), Option(basketball), Option(swimming)]
};
var completed = CurriculumCompletionRules.Evaluate(
[module],
new HashSet<Guid>([football.Id, swimming.Id]));
var incomplete = CurriculumCompletionRules.Evaluate(
[module],
new HashSet<Guid>([football.Id]));
Assert.Equal(1, completed.RequirementCount);
Assert.Equal(1, completed.PassedRequirementCount);
Assert.Empty(completed.MissingRequirements);
Assert.Equal(0, incomplete.PassedRequirementCount);
Assert.Contains("大学体育(还差 1 学分)", incomplete.MissingRequirements);
}
[Fact]
public void Curriculum_groups_Still_Require_Every_Fixed_Course()
{
var ideology = new Course { Code = "MORAL101", Name = "思想道德与法治", Credits = 3 };
var module = new CurriculumModule
{
Code = "MORAL",
Name = "德育课程",
RequiredCredits = 3,
Courses =
[
new CurriculumCourse
{
CourseId = ideology.Id,
Course = ideology,
Type = CurriculumCourseType.Required
}
]
};
var result = CurriculumCompletionRules.Evaluate([module], new HashSet<Guid>());
Assert.Equal(1, result.RequirementCount);
Assert.Equal(0, result.PassedRequirementCount);
Assert.Contains("思想道德与法治", result.MissingRequirements);
}
[Fact]
public void Active_student_with_full_credits_and_courses_is_eligible()
{
@@ -35,4 +90,12 @@ public sealed class GraduationAuditRulesTests
Assert.Equal(GraduationAuditConclusion.Ineligible, conclusion);
}
private static CurriculumCourse Option(Course course) =>
new()
{
CourseId = course.Id,
Course = course,
Type = CurriculumCourseType.Elective
};
}