已改成后台“检查并发布”任务,原来的超时主要就是同步逐条校验导致的。

发布接口立即返回 202 Accepted,页面轮询显示检查进度。
校验改为批量读取,减少重复数据库查询。
检查失败保留草稿并显示具体原因;成功后原子完成旧课表归档和新课表发布。
同一学期禁止两个发布任务并发,服务重启后未完成任务会自动恢复。
发布期间锁定编辑、自动排课等冲突操作。
已补齐 SQLite 开发迁移及 MySQL 生产迁移。
This commit is contained in:
2026-07-25 12:07:03 +08:00 Unverified
parent fdca6a3edf
commit b015514115
12 changed files with 4116 additions and 81 deletions
@@ -1448,6 +1448,67 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
b.ToTable("SchedulePlans");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePublishJob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid>("AcademicTermId")
.HasColumnType("char(36)");
b.Property<Guid?>("ActiveAcademicTermId")
.HasColumnType("char(36)");
b.Property<DateTime?>("CompletedAt")
.HasColumnType("datetime(6)");
b.Property<int>("CompletedSteps")
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("CurrentStep")
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<string>("ErrorMessage")
.HasMaxLength(2000)
.HasColumnType("varchar(2000)");
b.Property<Guid?>("RequestedByUserId")
.HasColumnType("char(36)");
b.Property<Guid>("SchedulePlanId")
.HasColumnType("char(36)");
b.Property<DateTime?>("StartedAt")
.HasColumnType("datetime(6)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<int>("TotalSteps")
.HasColumnType("int");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("ActiveAcademicTermId")
.IsUnique();
b.HasIndex("RequestedByUserId");
b.HasIndex("SchedulePlanId", "CreatedAt");
b.HasIndex("Status", "CreatedAt");
b.ToTable("SchedulePublishJobs");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleTimeSlot", b =>
{
b.Property<Guid>("Id")
@@ -2553,6 +2614,22 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
b.Navigation("AcademicTerm");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePublishJob", b =>
{
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("RequestedByUserId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("Jiaowu.Api.Domain.Academic.SchedulePlan", "SchedulePlan")
.WithMany()
.HasForeignKey("SchedulePlanId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SchedulePlan");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleTimeSlot", b =>
{
b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm")